@friggframework/core 2.0.0--canary.550.b8b9a2b.0 → 2.0.0--canary.545.e256e95.0

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 (254) hide show
  1. package/CHANGELOG.md +66 -67
  2. package/CLAUDE.md +148 -124
  3. package/README.md +165 -130
  4. package/admin-scripts/index.js +52 -0
  5. package/admin-scripts/repositories/admin-api-key-repository-documentdb.js +21 -0
  6. package/admin-scripts/repositories/admin-api-key-repository-factory.js +51 -0
  7. package/admin-scripts/repositories/admin-api-key-repository-interface.js +121 -0
  8. package/admin-scripts/repositories/admin-api-key-repository-mongo.js +155 -0
  9. package/admin-scripts/repositories/admin-api-key-repository-postgres.js +189 -0
  10. package/admin-scripts/repositories/admin-process-repository-documentdb.js +21 -0
  11. package/admin-scripts/repositories/admin-process-repository-factory.js +49 -0
  12. package/admin-scripts/repositories/admin-process-repository-interface.js +150 -0
  13. package/admin-scripts/repositories/admin-process-repository-mongo.js +213 -0
  14. package/admin-scripts/repositories/admin-process-repository-postgres.js +251 -0
  15. package/admin-scripts/repositories/script-execution-repository-documentdb.js +21 -0
  16. package/admin-scripts/repositories/script-execution-repository-factory.js +51 -0
  17. package/admin-scripts/repositories/script-execution-repository-interface.js +193 -0
  18. package/admin-scripts/repositories/script-execution-repository-mongo.js +278 -0
  19. package/admin-scripts/repositories/script-execution-repository-postgres.js +320 -0
  20. package/admin-scripts/repositories/script-schedule-repository-documentdb.js +21 -0
  21. package/admin-scripts/repositories/script-schedule-repository-factory.js +49 -0
  22. package/admin-scripts/repositories/script-schedule-repository-interface.js +108 -0
  23. package/admin-scripts/repositories/script-schedule-repository-mongo.js +179 -0
  24. package/admin-scripts/repositories/script-schedule-repository-postgres.js +210 -0
  25. package/application/commands/admin-script-commands.js +348 -0
  26. package/application/commands/integration-commands.js +12 -6
  27. package/application/index.js +5 -3
  28. package/assertions/get.js +17 -7
  29. package/associations/association.js +59 -57
  30. package/core/CLAUDE.md +222 -156
  31. package/core/Worker.js +33 -12
  32. package/credential/credential-router.js +262 -0
  33. package/credential/repositories/credential-repository-documentdb.js +71 -1
  34. package/credential/repositories/credential-repository-factory.js +4 -6
  35. package/credential/repositories/credential-repository-interface.js +8 -2
  36. package/credential/repositories/credential-repository-mongo.js +45 -4
  37. package/credential/repositories/credential-repository-postgres.js +44 -3
  38. package/credential/repositories/credential-repository.js +3 -1
  39. package/credential/use-cases/delete-credential-for-user.js +50 -0
  40. package/credential/use-cases/get-credential-for-user.js +21 -3
  41. package/credential/use-cases/list-credentials-for-user.js +31 -0
  42. package/credential/use-cases/reauthorize-credential.js +103 -0
  43. package/credential/use-cases/update-authentication-status.js +5 -2
  44. package/database/MONGODB_TRANSACTION_FIX.md +72 -52
  45. package/database/config.js +13 -59
  46. package/database/documentdb-encryption-service.js +29 -13
  47. package/database/documentdb-utils.js +21 -12
  48. package/database/encryption/README.md +53 -36
  49. package/database/encryption/encryption-schema-registry.js +67 -21
  50. package/database/encryption/field-encryption-service.js +15 -4
  51. package/database/encryption/logger.js +3 -2
  52. package/database/encryption/prisma-encryption-extension.js +5 -8
  53. package/database/prisma.js +27 -16
  54. package/database/repositories/health-check-repository-factory.js +7 -5
  55. package/database/repositories/health-check-repository-interface.js +15 -5
  56. package/database/repositories/health-check-repository-postgres.js +1 -1
  57. package/database/use-cases/check-database-health-use-case.js +3 -2
  58. package/database/use-cases/check-database-state-use-case.js +8 -7
  59. package/database/use-cases/get-database-state-via-worker-use-case.js +15 -12
  60. package/database/use-cases/get-migration-status-use-case.js +4 -2
  61. package/database/use-cases/run-database-migration-use-case.js +41 -9
  62. package/database/use-cases/test-encryption-use-case.js +4 -5
  63. package/database/use-cases/trigger-database-migration-use-case.js +9 -8
  64. package/database/utils/mongodb-collection-utils.js +11 -3
  65. package/database/utils/mongodb-schema-init.js +10 -4
  66. package/database/utils/prisma-runner.js +144 -74
  67. package/database/utils/prisma-schema-parser.js +2 -2
  68. package/docs/PROCESS_MANAGEMENT_QUEUE_SPEC.md +135 -80
  69. package/encrypt/Cryptor.js +54 -49
  70. package/encrypt/aes-encryption-key-provider.js +82 -0
  71. package/encrypt/encryption-key-provider-interface.js +50 -0
  72. package/errors/fetch-error.js +1 -1
  73. package/extensions/bootstrap-runner.js +43 -0
  74. package/extensions/extension-loader.js +167 -0
  75. package/extensions/index.js +37 -0
  76. package/extensions/initialize-app.js +123 -0
  77. package/extensions/route-mounter.js +59 -0
  78. package/extensions/schema-composer.js +188 -0
  79. package/generated/prisma-mongodb/edge.js +60 -5
  80. package/generated/prisma-mongodb/index-browser.js +49 -1
  81. package/generated/prisma-mongodb/index.d.ts +4025 -63
  82. package/generated/prisma-mongodb/index.js +66 -11
  83. package/generated/{prisma-postgresql/query-engine-debian-openssl-3.0.x → prisma-mongodb/libquery_engine-debian-openssl-3.0.x.so.node} +0 -0
  84. package/generated/{prisma-postgresql/query-engine-rhel-openssl-3.0.x → prisma-mongodb/libquery_engine-rhel-openssl-3.0.x.so.node} +0 -0
  85. package/generated/prisma-mongodb/package.json +1 -1
  86. package/generated/prisma-mongodb/runtime/library.js +146 -0
  87. package/generated/prisma-mongodb/schema.prisma +89 -2
  88. package/generated/prisma-mongodb/wasm.js +60 -5
  89. package/generated/prisma-postgresql/edge.js +59 -5
  90. package/generated/prisma-postgresql/index-browser.js +48 -1
  91. package/generated/prisma-postgresql/index.d.ts +4742 -54
  92. package/generated/prisma-postgresql/index.js +65 -11
  93. package/generated/{prisma-mongodb/query-engine-debian-openssl-3.0.x → prisma-postgresql/libquery_engine-debian-openssl-3.0.x.so.node} +0 -0
  94. package/generated/{prisma-mongodb/query-engine-rhel-openssl-3.0.x → prisma-postgresql/libquery_engine-rhel-openssl-3.0.x.so.node} +0 -0
  95. package/generated/prisma-postgresql/package.json +1 -1
  96. package/generated/prisma-postgresql/runtime/library.js +146 -0
  97. package/generated/prisma-postgresql/schema.prisma +87 -2
  98. package/generated/prisma-postgresql/wasm.js +59 -5
  99. package/handlers/WEBHOOKS.md +104 -81
  100. package/handlers/app-definition-loader.js +53 -10
  101. package/handlers/app-handler-helpers.js +14 -3
  102. package/handlers/backend-utils.js +3 -17
  103. package/handlers/database-migration-handler.js +64 -45
  104. package/handlers/middleware/admin-auth.js +53 -0
  105. package/handlers/routers/HEALTHCHECK.md +170 -135
  106. package/handlers/routers/admin.js +474 -0
  107. package/handlers/routers/auth.js +51 -9
  108. package/handlers/routers/db-migration.handler.js +2 -3
  109. package/handlers/routers/db-migration.js +102 -66
  110. package/handlers/routers/docs.js +180 -0
  111. package/handlers/routers/health.js +84 -283
  112. package/handlers/routers/integration-defined-routers.js +44 -33
  113. package/handlers/routers/integration-webhook-routers.js +73 -52
  114. package/handlers/routers/middleware/loadUser.js +39 -0
  115. package/handlers/routers/middleware/requireAdmin.js +43 -0
  116. package/handlers/routers/middleware/requireLoggedInUser.js +19 -0
  117. package/handlers/routers/user.js +30 -14
  118. package/handlers/routers/websocket.js +18 -4
  119. package/handlers/use-cases/check-integrations-health-use-case.js +13 -6
  120. package/handlers/workers/db-migration.js +74 -35
  121. package/handlers/workers/integration-defined-workers.js +31 -20
  122. package/index.js +60 -16
  123. package/infrastructure/scheduler/index.js +15 -11
  124. package/infrastructure/scheduler/netlify-scheduler-adapter.js +185 -0
  125. package/infrastructure/scheduler/scheduler-service-factory.js +38 -31
  126. package/integrations/WEBHOOK-QUICKSTART.md +21 -14
  127. package/integrations/index.js +4 -0
  128. package/integrations/integration-base.js +9 -6
  129. package/integrations/integration-router.js +1841 -153
  130. package/integrations/options.js +47 -20
  131. package/integrations/repositories/integration-mapping-repository-factory.js +4 -8
  132. package/integrations/repositories/integration-repository-documentdb.js +53 -24
  133. package/integrations/repositories/integration-repository-factory.js +6 -6
  134. package/integrations/repositories/integration-repository-interface.js +27 -9
  135. package/integrations/repositories/integration-repository-postgres.js +3 -3
  136. package/integrations/repositories/process-repository-factory.js +4 -7
  137. package/integrations/repositories/process-repository-interface.js +9 -5
  138. package/integrations/repositories/process-repository-mongo.js +7 -3
  139. package/integrations/repositories/process-repository-postgres.js +2 -2
  140. package/integrations/tests/doubles/config-capturing-integration.js +9 -8
  141. package/integrations/tests/doubles/dummy-integration-class.js +81 -8
  142. package/integrations/tests/doubles/test-integration-repository.js +51 -11
  143. package/integrations/use-cases/create-integration.js +36 -32
  144. package/integrations/use-cases/create-process.js +20 -9
  145. package/integrations/use-cases/delete-integration-for-user.js +5 -4
  146. package/integrations/use-cases/execute-proxy-request.js +564 -0
  147. package/integrations/use-cases/get-integration-for-user.js +25 -10
  148. package/integrations/use-cases/get-integration-instance-by-definition.js +13 -7
  149. package/integrations/use-cases/get-integrations-for-user.js +4 -1
  150. package/integrations/use-cases/get-possible-integrations.js +1 -1
  151. package/integrations/use-cases/get-process.js +5 -6
  152. package/integrations/use-cases/index.js +3 -1
  153. package/integrations/use-cases/load-integration-context.js +1 -5
  154. package/integrations/use-cases/update-process-state.js +10 -8
  155. package/integrations/utils/map-integration-dto.js +9 -3
  156. package/jest-setup.js +1 -1
  157. package/jest-teardown.js +1 -1
  158. package/lambda/TimeoutCatcher.js +32 -30
  159. package/lambda/index.js +1 -1
  160. package/logs/index.js +3 -3
  161. package/modules/domain/entities/AuthorizationSession.js +131 -0
  162. package/modules/domain/entities/index.js +10 -0
  163. package/modules/module-factory.js +1 -1
  164. package/modules/module.js +44 -11
  165. package/modules/repositories/authorization-session-repository-factory.js +53 -0
  166. package/modules/repositories/authorization-session-repository-interface.js +75 -0
  167. package/modules/repositories/authorization-session-repository-mongo.js +169 -0
  168. package/modules/repositories/authorization-session-repository-postgres.js +170 -0
  169. package/modules/repositories/module-repository-documentdb.js +89 -41
  170. package/modules/repositories/module-repository-factory.js +4 -6
  171. package/modules/repositories/module-repository-interface.js +13 -0
  172. package/modules/repositories/module-repository-mongo.js +40 -5
  173. package/modules/repositories/module-repository-postgres.js +42 -5
  174. package/modules/repositories/module-repository.js +28 -0
  175. package/modules/requester/api-key.js +0 -2
  176. package/modules/requester/basic.js +0 -1
  177. package/modules/requester/oauth-2.js +3 -14
  178. package/modules/test/mock-api/definition.js +15 -10
  179. package/modules/test/mock-api/mocks/hubspot.js +36 -36
  180. package/modules/tests/doubles/test-module-factory.js +10 -4
  181. package/modules/tests/doubles/test-module-repository.js +1 -1
  182. package/modules/use-cases/delete-module-entity.js +23 -0
  183. package/modules/use-cases/get-authorization-requirements.js +110 -0
  184. package/modules/use-cases/get-entities-for-user.js +1 -1
  185. package/modules/use-cases/get-entity-options-by-id.js +8 -6
  186. package/modules/use-cases/get-module-entity-by-id.js +16 -0
  187. package/modules/use-cases/get-module.js +12 -9
  188. package/modules/use-cases/process-authorization-callback.js +12 -6
  189. package/modules/use-cases/process-authorization-step.js +160 -0
  190. package/modules/use-cases/refresh-entity-options.js +8 -6
  191. package/modules/use-cases/start-authorization-session.js +86 -0
  192. package/modules/use-cases/test-module-auth.js +8 -6
  193. package/modules/use-cases/update-module-entity.js +27 -0
  194. package/modules/utils/map-module-dto.js +2 -2
  195. package/openapi/openapi-spec-generator.js +370 -0
  196. package/openapi/openapi-v1.yaml +1360 -0
  197. package/openapi/openapi-v2.yaml +1535 -0
  198. package/package.json +7 -9
  199. package/prisma-mongodb/schema.prisma +89 -2
  200. package/prisma-postgresql/migrations/20260222000000_add_admin_process_and_script_schedule/migration.sql +55 -0
  201. package/prisma-postgresql/schema.prisma +87 -2
  202. package/providers/index.js +13 -0
  203. package/providers/resolve-provider.js +93 -0
  204. package/queues/index.js +19 -0
  205. package/queues/providers/index.js +7 -0
  206. package/queues/providers/netlify-background-provider.js +76 -0
  207. package/queues/providers/qstash-queue-provider.js +122 -0
  208. package/queues/queue-client-interface.js +55 -0
  209. package/queues/queue-provider-factory.js +93 -0
  210. package/queues/queue-provider.js +46 -0
  211. package/queues/queuer-util.js +33 -25
  212. package/syncs/repositories/sync-repository-documentdb.js +39 -19
  213. package/syncs/repositories/sync-repository-factory.js +4 -4
  214. package/syncs/repositories/sync-repository-interface.js +6 -2
  215. package/syncs/repositories/sync-repository-postgres.js +19 -13
  216. package/syncs/sync.js +104 -100
  217. package/token/repositories/token-repository-factory.js +4 -6
  218. package/types/assertions/index.d.ts +72 -72
  219. package/types/core/index.d.ts +98 -53
  220. package/types/errors/index.d.ts +61 -54
  221. package/types/eslint-config/index.d.ts +40 -40
  222. package/types/lambda/index.d.ts +26 -26
  223. package/types/logs/index.d.ts +4 -4
  224. package/types/prettier-config/index.d.ts +5 -5
  225. package/types/test-environment/index.d.ts +14 -14
  226. package/types/tsconfig.json +95 -93
  227. package/user/repositories/user-repository-factory.js +4 -4
  228. package/user/tests/doubles/test-user-repository.js +5 -2
  229. package/user/use-cases/authenticate-user.js +2 -4
  230. package/user/use-cases/authenticate-with-shared-secret.js +1 -1
  231. package/user/use-cases/create-individual-user.js +8 -7
  232. package/user/use-cases/create-organization-user.js +1 -1
  233. package/user/use-cases/create-token-for-user-id.js +1 -1
  234. package/user/use-cases/delete-user.js +47 -0
  235. package/user/use-cases/get-user-from-adopter-jwt.js +0 -2
  236. package/user/use-cases/get-user-from-bearer-token.js +7 -3
  237. package/user/use-cases/get-user-from-x-frigg-headers.js +14 -11
  238. package/user/use-cases/login-user.js +8 -8
  239. package/user/user.js +18 -4
  240. package/utils/backend-path.js +2 -2
  241. package/utils/index.js +4 -1
  242. package/websocket/repositories/websocket-connection-repository-documentdb.js +46 -21
  243. package/websocket/repositories/websocket-connection-repository-factory.js +4 -8
  244. package/websocket/repositories/websocket-connection-repository-mongo.js +26 -16
  245. package/websocket/repositories/websocket-connection-repository-postgres.js +26 -15
  246. package/websocket/repositories/websocket-connection-repository.js +26 -20
  247. package/websocket/websocket-message-sender-interface.js +38 -0
  248. package/database/adapters/lambda-invoker.js +0 -97
  249. package/database/repositories/migration-status-repository-s3.js +0 -137
  250. package/generated/prisma-mongodb/runtime/binary.d.ts +0 -1
  251. package/generated/prisma-mongodb/runtime/binary.js +0 -289
  252. package/generated/prisma-postgresql/runtime/binary.d.ts +0 -1
  253. package/generated/prisma-postgresql/runtime/binary.js +0 -289
  254. package/infrastructure/scheduler/eventbridge-scheduler-adapter.js +0 -184
@@ -5,31 +5,36 @@ This document explains how to implement webhook handling for your Frigg integrat
5
5
  ## Overview
6
6
 
7
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
8
+
9
+ - **Receives webhooks without database connections** for fast response times
10
+ - **Queues webhooks to SQS** for async processing
11
+ - **Processes webhooks with fully hydrated integrations** (with DB and API modules loaded)
12
+ - **Supports custom signature verification** for security
13
+ - **Throttles database connections** using SQS to handle webhook bursts
13
14
 
14
15
  ## Architecture
15
16
 
16
17
  The webhook flow consists of two stages:
17
18
 
18
19
  ### Stage 1: HTTP Webhook Receiver (No DB)
20
+
19
21
  ```
20
22
  Webhook → Lambda → WEBHOOK_RECEIVED event → Queue to SQS → 200 OK Response
21
23
  ```
22
- - Fast response (no database query)
23
- - Optional signature verification
24
- - Messages queued for processing
24
+
25
+ - Fast response (no database query)
26
+ - Optional signature verification
27
+ - Messages queued for processing
25
28
 
26
29
  ### Stage 2: Queue Worker (DB-Connected)
30
+
27
31
  ```
28
32
  SQS Queue → Lambda Worker → ON_WEBHOOK event → Process with hydrated integration
29
33
  ```
30
- - Full database access
31
- - API modules loaded
32
- - Can use integration context
34
+
35
+ - Full database access
36
+ - API modules loaded
37
+ - Can use integration context
33
38
 
34
39
  ## Enabling Webhooks
35
40
 
@@ -59,7 +64,9 @@ class MyIntegration extends IntegrationBase {
59
64
  static Definition = {
60
65
  name: 'my-integration',
61
66
  version: '1.0.0',
62
- modules: { /* ... */ },
67
+ modules: {
68
+ /* ... */
69
+ },
63
70
  webhooks: {
64
71
  enabled: true,
65
72
  // Future options will be added here
@@ -73,20 +80,24 @@ class MyIntegration extends IntegrationBase {
73
80
  When webhooks are enabled, two routes are automatically created:
74
81
 
75
82
  ### General Webhook
83
+
76
84
  ```
77
85
  POST /api/{integrationName}-integration/webhooks
78
86
  ```
79
- - No integration ID required
80
- - Useful for system-wide events
81
- - Creates unhydrated integration instance
87
+
88
+ - No integration ID required
89
+ - Useful for system-wide events
90
+ - Creates unhydrated integration instance
82
91
 
83
92
  ### Integration-Specific Webhook
93
+
84
94
  ```
85
95
  POST /api/{integrationName}-integration/webhooks/:integrationId
86
96
  ```
87
- - Includes integration ID in URL
88
- - Worker loads full integration with DB and modules
89
- - Recommended for most use cases
97
+
98
+ - Includes integration ID in URL
99
+ - Worker loads full integration with DB and modules
100
+ - Recommended for most use cases
90
101
 
91
102
  ## Event Handlers
92
103
 
@@ -95,6 +106,7 @@ POST /api/{integrationName}-integration/webhooks/:integrationId
95
106
  Triggered when a webhook HTTP request is received (no database connection).
96
107
 
97
108
  #### Default Behavior
109
+
98
110
  Queues the webhook to SQS and responds with `200 OK`:
99
111
 
100
112
  ```javascript
@@ -125,7 +137,7 @@ class MyIntegration extends IntegrationBase {
125
137
  // Verify webhook signature
126
138
  const signature = req.headers['x-webhook-signature'];
127
139
  const expectedSignature = this.calculateSignature(req.body);
128
-
140
+
129
141
  if (signature !== expectedSignature) {
130
142
  return res.status(401).json({ error: 'Invalid signature' });
131
143
  }
@@ -156,6 +168,7 @@ class MyIntegration extends IntegrationBase {
156
168
  Triggered by the queue worker (with database connection and hydrated integration).
157
169
 
158
170
  #### Default Behavior
171
+
159
172
  Logs the webhook data (override this!):
160
173
 
161
174
  ```javascript
@@ -262,27 +275,26 @@ class SlackIntegration extends IntegrationBase {
262
275
  const crypto = require('crypto');
263
276
  const signingSecret = process.env.SLACK_SIGNING_SECRET;
264
277
  const timestamp = req.headers['x-slack-request-timestamp'];
265
-
278
+
266
279
  // Validate timestamp is recent (within 5 minutes)
267
280
  const currentTime = Math.floor(Date.now() / 1000);
268
281
  if (Math.abs(currentTime - parseInt(timestamp)) > 300) {
269
282
  return false; // Request is older than 5 minutes
270
283
  }
271
-
284
+
272
285
  const hmac = crypto.createHmac('sha256', signingSecret);
273
286
  hmac.update(`v0:${timestamp}:${JSON.stringify(req.body)}`);
274
287
  const expected = `v0=${hmac.digest('hex')}`;
275
-
288
+
276
289
  // Check lengths first to avoid errors in timingSafeEqual
277
- const expectedBuffer = Buffer.from(expected)
278
- const signatureBuffer = Buffer.from(signature)
279
-
290
+ const expectedBuffer = Buffer.from(expected);
291
+ const signatureBuffer = Buffer.from(signature);
292
+
280
293
  if (expectedBuffer.length !== signatureBuffer.length) {
281
- return false
294
+ return false;
282
295
  }
283
-
284
- return crypto.timingSafeEqual(expectedBuffer, signatureBuffer)
285
296
 
297
+ return crypto.timingSafeEqual(expectedBuffer, signatureBuffer);
286
298
  }
287
299
  }
288
300
  ```
@@ -445,6 +457,7 @@ describe('MyIntegration Webhooks', () => {
445
457
  ## Best Practices
446
458
 
447
459
  ### 1. Always Verify Signatures
460
+
448
461
  ```javascript
449
462
  async onWebhookReceived({ req, res }) {
450
463
  // Verify before queueing
@@ -457,14 +470,17 @@ async onWebhookReceived({ req, res }) {
457
470
  ```
458
471
 
459
472
  ### 2. Respond Quickly
473
+
460
474
  The `WEBHOOK_RECEIVED` handler should complete in < 3 seconds:
461
- - Verify signature
462
- - Queue message
463
- - Return 200 OK
475
+
476
+ - Verify signature
477
+ - Queue message
478
+ - Return 200 OK
464
479
 
465
480
  Heavy processing goes in `ON_WEBHOOK`.
466
481
 
467
482
  ### 3. Handle Idempotency
483
+
468
484
  ```javascript
469
485
  async onWebhook({ data }) {
470
486
  const { body } = data;
@@ -484,6 +500,7 @@ async onWebhook({ data }) {
484
500
  ```
485
501
 
486
502
  ### 4. Error Handling
503
+
487
504
  ```javascript
488
505
  async onWebhook({ data }) {
489
506
  try {
@@ -491,7 +508,7 @@ async onWebhook({ data }) {
491
508
  } catch (error) {
492
509
  // Log error - message will go to DLQ after retries
493
510
  console.error('Webhook processing failed:', error);
494
-
511
+
495
512
  // Update integration status if needed
496
513
  await this.updateIntegrationMessages.execute(
497
514
  this.id,
@@ -500,7 +517,7 @@ async onWebhook({ data }) {
500
517
  error.message,
501
518
  Date.now()
502
519
  );
503
-
520
+
504
521
  throw error; // Re-throw for retry/DLQ
505
522
  }
506
523
  }
@@ -513,18 +530,20 @@ async onWebhook({ data }) {
513
530
  When `webhooks: true` is set, the Frigg infrastructure automatically creates:
514
531
 
515
532
  1. **HTTP Lambda Function**
516
- - Handler: `integration-webhook-routers.js`
517
- - No database connection
518
- - Fast cold start
533
+
534
+ - Handler: `integration-webhook-routers.js`
535
+ - No database connection
536
+ - Fast cold start
519
537
 
520
538
  2. **Webhook Routes**
521
- - `POST /api/{name}-integration/webhooks`
522
- - `POST /api/{name}-integration/webhooks/:integrationId`
523
539
 
524
- 3. **Queue Worker**
525
- - Processes from existing integration queue
526
- - Handles `ON_WEBHOOK` events
527
- - Full database access
540
+ - `POST /api/{name}-integration/webhooks`
541
+ - `POST /api/{name}-integration/webhooks/:integrationId`
542
+
543
+ 3. **Queue Worker**
544
+ - Processes from existing integration queue
545
+ - Handles `ON_WEBHOOK` events
546
+ - Full database access
528
547
 
529
548
  ### Serverless Configuration (Automatic)
530
549
 
@@ -532,22 +551,22 @@ The following is generated automatically in `serverless.yml`:
532
551
 
533
552
  ```yaml
534
553
  functions:
535
- myintegrationWebhook:
536
- handler: node_modules/@friggframework/core/handlers/routers/integration-webhook-routers.handlers.myintegrationWebhook.handler
537
- events:
538
- - httpApi:
539
- path: /api/myintegration-integration/webhooks
540
- method: POST
541
- - httpApi:
542
- path: /api/myintegration-integration/webhooks/{integrationId}
543
- method: POST
544
-
545
- myintegrationQueueWorker:
546
- handler: node_modules/@friggframework/core/handlers/workers/integration-defined-workers.handlers.myintegration.queueWorker
547
- events:
548
- - sqs:
549
- arn: !GetAtt MyintegrationQueue.Arn
550
- batchSize: 1
554
+ myintegrationWebhook:
555
+ handler: node_modules/@friggframework/core/handlers/routers/integration-webhook-routers.handlers.myintegrationWebhook.handler
556
+ events:
557
+ - httpApi:
558
+ path: /api/myintegration-integration/webhooks
559
+ method: POST
560
+ - httpApi:
561
+ path: /api/myintegration-integration/webhooks/{integrationId}
562
+ method: POST
563
+
564
+ myintegrationQueueWorker:
565
+ handler: node_modules/@friggframework/core/handlers/workers/integration-defined-workers.handlers.myintegration.queueWorker
566
+ events:
567
+ - sqs:
568
+ arn: !GetAtt MyintegrationQueue.Arn
569
+ batchSize: 1
551
570
  ```
552
571
 
553
572
  ## Event Handler Reference
@@ -560,13 +579,14 @@ functions:
560
579
  **Must:** Respond to `res` with status code
561
580
 
562
581
  **Parameters:**
563
- - `req` - Express request object
564
- - `req.body` - Webhook payload
565
- - `req.params.integrationId` - Integration ID (if in URL)
566
- - `req.headers` - HTTP headers
567
- - `req.query` - Query parameters
568
- - `res` - Express response object
569
- - Call `res.status(code).json(data)` to respond
582
+
583
+ - `req` - Express request object
584
+ - `req.body` - Webhook payload
585
+ - `req.params.integrationId` - Integration ID (if in URL)
586
+ - `req.headers` - HTTP headers
587
+ - `req.query` - Query parameters
588
+ - `res` - Express response object
589
+ - Call `res.status(code).json(data)` to respond
570
590
 
571
591
  ### onWebhook({ data, context })
572
592
 
@@ -576,12 +596,13 @@ functions:
576
596
  **Can:** Use `this.modules`, `this.config`, DB operations
577
597
 
578
598
  **Parameters:**
579
- - `data` - Queued webhook data
580
- - `data.integrationId` - Integration ID (if provided)
581
- - `data.body` - Original webhook payload
582
- - `data.headers` - Original HTTP headers
583
- - `data.query` - Original query parameters
584
- - `context` - Lambda context object
599
+
600
+ - `data` - Queued webhook data
601
+ - `data.integrationId` - Integration ID (if provided)
602
+ - `data.body` - Original webhook payload
603
+ - `data.headers` - Original HTTP headers
604
+ - `data.query` - Original query parameters
605
+ - `context` - Lambda context object
585
606
 
586
607
  ## Queue Helper
587
608
 
@@ -608,6 +629,7 @@ Automatically uses the correct SQS queue URL based on integration name.
608
629
  **Error:** `Queue URL not found for {NAME}_QUEUE_URL`
609
630
 
610
631
  **Solution:** Ensure environment variable is set:
632
+
611
633
  ```bash
612
634
  export MY_INTEGRATION_QUEUE_URL=https://sqs.us-east-1.amazonaws.com/...
613
635
  ```
@@ -615,6 +637,7 @@ export MY_INTEGRATION_QUEUE_URL=https://sqs.us-east-1.amazonaws.com/...
615
637
  ### Webhook Not Responding
616
638
 
617
639
  **Check:**
640
+
618
641
  1. Is `webhooks: true` in Definition?
619
642
  2. Is webhook endpoint deployed?
620
643
  3. Are you sending POST requests?
@@ -623,6 +646,7 @@ export MY_INTEGRATION_QUEUE_URL=https://sqs.us-east-1.amazonaws.com/...
623
646
  ### Worker Not Processing
624
647
 
625
648
  **Check:**
649
+
626
650
  1. Is SQS queue receiving messages?
627
651
  2. Is queue worker Lambda function deployed?
628
652
  3. Check CloudWatch logs for worker errors
@@ -638,16 +662,15 @@ export MY_INTEGRATION_QUEUE_URL=https://sqs.us-east-1.amazonaws.com/...
638
662
 
639
663
  ## Performance
640
664
 
641
- - **HTTP Response:** < 100ms (signature check + queue)
642
- - **Worker Processing:** Based on your logic
643
- - **Concurrency:** Controlled by SQS worker `reservedConcurrency: 5`
644
- - **Burst Handling:** Unlimited HTTP, throttled processing
665
+ - **HTTP Response:** < 100ms (signature check + queue)
666
+ - **Worker Processing:** Based on your logic
667
+ - **Concurrency:** Controlled by SQS worker `reservedConcurrency: 5`
668
+ - **Burst Handling:** Unlimited HTTP, throttled processing
645
669
 
646
670
  ## Related Files
647
671
 
648
- - `packages/core/integrations/integration-base.js` - Event definitions and default handlers
649
- - `packages/core/handlers/routers/integration-webhook-routers.js` - HTTP webhook routes
650
- - `packages/core/handlers/backend-utils.js` - Queue worker with hydration logic
651
- - `packages/core/handlers/integration-event-dispatcher.js` - Event dispatching
652
- - `packages/devtools/infrastructure/serverless-template.js` - Automatic infrastructure generation
653
-
672
+ - `packages/core/integrations/integration-base.js` - Event definitions and default handlers
673
+ - `packages/core/handlers/routers/integration-webhook-routers.js` - HTTP webhook routes
674
+ - `packages/core/handlers/backend-utils.js` - Queue worker with hydration logic
675
+ - `packages/core/handlers/integration-event-dispatcher.js` - Event dispatching
676
+ - `packages/devtools/infrastructure/serverless-template.js` - Automatic infrastructure generation
@@ -3,18 +3,60 @@ const path = require('node:path');
3
3
  const fs = require('fs-extra');
4
4
 
5
5
  /**
6
- * Loads the App definition from the nearest backend package
6
+ * Cached app definition, set via setAppDefinition().
7
+ * When set, loadAppDefinition() returns this instead of discovering
8
+ * the backend via process.cwd().
9
+ */
10
+ let cachedAppDefinition = null;
11
+
12
+ /**
13
+ * Pre-set the app definition so loadAppDefinition() can return it
14
+ * without needing process.cwd()-based discovery.
15
+ *
16
+ * This is critical for platforms like Netlify where:
17
+ * - nft/esbuild traces static require() calls to determine bundle contents
18
+ * - process.cwd() at runtime points to /var/task/, not the backend directory
19
+ * - The caller can use a static require('../../backend/index.js') to make
20
+ * the backend traceable, then pass the definition here
21
+ *
22
+ * @param {Object} definition - The app definition object (backend's Definition export)
23
+ */
24
+ function setAppDefinition(definition) {
25
+ cachedAppDefinition = definition;
26
+ }
27
+
28
+ /**
29
+ * Loads the App definition from the nearest backend package.
30
+ *
31
+ * If setAppDefinition() was called, returns the cached definition
32
+ * immediately (no filesystem discovery needed).
33
+ *
34
+ * Otherwise, discovers the backend via process.cwd() traversal.
35
+ *
36
+ * Returns the full appDefinition object plus convenience destructured fields
37
+ * for backward compatibility (integrations, userConfig).
38
+ *
7
39
  * @function loadAppDefinition
8
- * @description Searches for the nearest backend package.json, loads the corresponding index.js file,
9
- * and extracts the application definition containing integrations and user configuration.
10
- * @returns {{integrations: Array<object>, userConfig: object | null}} An object containing the application definition.
11
- * @throws {Error} Throws error if backend package.json cannot be found.
12
- * @throws {Error} Throws error if index.js file cannot be found in the backend directory.
40
+ * @returns {{
41
+ * integrations: Array<object>,
42
+ * userConfig: object | null,
43
+ * appDefinition: object
44
+ * }}
45
+ * @throws {Error} If backend package.json or index.js cannot be found.
13
46
  * @example
14
- * const { integrations, userConfig } = loadAppDefinition();
15
- * console.log(`Found ${integrations.length} integrations`);
47
+ * // Existing callers still work:
48
+ * const { integrations, userConfig } = loadAppDefinition();
49
+ *
50
+ * // New callers can access the full definition:
51
+ * const { appDefinition } = loadAppDefinition();
52
+ * console.log(appDefinition.provider); // 'aws' | 'netlify'
16
53
  */
17
54
  function loadAppDefinition() {
55
+ if (cachedAppDefinition) {
56
+ const { integrations = [], user: userConfig = null } = cachedAppDefinition;
57
+ return { integrations, userConfig, appDefinition: cachedAppDefinition };
58
+ }
59
+
18
60
  const backendPath = findNearestBackendPackageJson();
19
61
  if (!backendPath) {
20
62
  throw new Error('Could not find backend package.json');
@@ -30,9 +72,10 @@ function loadAppDefinition() {
30
72
  const appDefinition = backendJsFile.Definition;
31
73
 
32
74
  const { integrations = [], user: userConfig = null } = appDefinition;
33
- return { integrations, userConfig };
75
+ return { integrations, userConfig, appDefinition };
34
76
  }
35
77
 
36
78
  module.exports = {
37
79
  loadAppDefinition,
38
- };
80
+ setAppDefinition,
81
+ };
@@ -3,6 +3,7 @@ const express = require('express');
3
3
  const bodyParser = require('body-parser');
4
4
  const cors = require('cors');
5
5
  const Boom = require('@hapi/boom');
6
+ const loadUserManager = require('./routers/middleware/loadUser');
6
7
  const serverlessHttp = require('serverless-http');
7
8
 
8
9
  const createApp = (applyMiddleware) => {
@@ -19,6 +20,8 @@ const createApp = (applyMiddleware) => {
19
20
  })
20
21
  );
21
22
 
23
+ app.use(loadUserManager);
24
+
22
25
  if (applyMiddleware) applyMiddleware(app);
23
26
 
24
27
  // Handle sending error response and logging server errors to console
@@ -32,7 +35,6 @@ const createApp = (applyMiddleware) => {
32
35
  flushDebugLog(boomError);
33
36
  res.status(statusCode).json({ error: 'Internal Server Error' });
34
37
  } else {
35
- console.warn(`[Frigg] ${req.method} ${req.path} -> ${statusCode}: ${err.message}`);
36
38
  res.status(statusCode).json({ error: err.message });
37
39
  }
38
40
  });
@@ -40,9 +42,18 @@ const createApp = (applyMiddleware) => {
40
42
  return app;
41
43
  };
42
44
 
43
- function createAppHandler(eventName, router, shouldUseDatabase = true) {
45
+ function createAppHandler(
46
+ eventName,
47
+ router,
48
+ shouldUseDatabase = true,
49
+ basePath = null
50
+ ) {
44
51
  const app = createApp((app) => {
45
- app.use(router);
52
+ if (basePath) {
53
+ app.use(basePath, router);
54
+ } else {
55
+ app.use(router);
56
+ }
46
57
  });
47
58
  return createHandler({
48
59
  eventName,
@@ -88,16 +88,9 @@ const loadIntegrationForWebhook = async (integrationId) => {
88
88
  moduleFactory,
89
89
  });
90
90
 
91
- let integrationRecord;
92
- try {
93
- integrationRecord =
94
- await integrationRepository.findIntegrationById(integrationId);
95
- } catch (error) {
96
- if (error.message?.includes('not found')) {
97
- return null;
98
- }
99
- throw error;
100
- }
91
+ const integrationRecord = await integrationRepository.findIntegrationById(
92
+ integrationId
93
+ );
101
94
 
102
95
  const instance = await getIntegrationInstance.execute(
103
96
  integrationId,
@@ -108,7 +101,6 @@ const loadIntegrationForWebhook = async (integrationId) => {
108
101
  };
109
102
 
110
103
  const loadIntegrationForProcess = async (processId, integrationClass) => {
111
-
112
104
  const { processRepository, integrationRepository, moduleRepository } =
113
105
  initializeRepositories();
114
106
 
@@ -158,12 +150,6 @@ const createQueueWorker = (integrationClass) => {
158
150
  integrationInstance = await loadIntegrationForWebhook(
159
151
  params.data.integrationId
160
152
  );
161
- if (!integrationInstance) {
162
- console.warn(
163
- `[${integrationClass.Definition.name}] Integration ${params.data.integrationId} no longer exists. Discarding ${params.event} message.`
164
- );
165
- return;
166
- }
167
153
  } else {
168
154
  // Instantiates a DRY integration class without database records.
169
155
  // There will be cases where we need to use helpers that the api modules can export.