@friggframework/core 2.0.0--canary.548.c8ae0ca.0 → 2.0.0--canary.545.c40eca4.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 (237) 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 +29 -23
  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 +22 -14
  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/generated/prisma-mongodb/edge.js +59 -4
  74. package/generated/prisma-mongodb/index-browser.js +49 -1
  75. package/generated/prisma-mongodb/index.d.ts +4023 -61
  76. package/generated/prisma-mongodb/index.js +59 -4
  77. package/generated/prisma-mongodb/package.json +1 -1
  78. package/generated/prisma-mongodb/schema.prisma +87 -0
  79. package/generated/prisma-mongodb/wasm.js +59 -4
  80. package/generated/prisma-postgresql/edge.js +58 -4
  81. package/generated/prisma-postgresql/index-browser.js +48 -1
  82. package/generated/prisma-postgresql/index.d.ts +4738 -54
  83. package/generated/prisma-postgresql/index.js +58 -4
  84. package/generated/prisma-postgresql/package.json +1 -1
  85. package/generated/prisma-postgresql/schema.prisma +85 -0
  86. package/generated/prisma-postgresql/wasm.js +58 -4
  87. package/handlers/WEBHOOKS.md +104 -81
  88. package/handlers/app-definition-loader.js +53 -10
  89. package/handlers/app-handler-helpers.js +14 -3
  90. package/handlers/backend-utils.js +0 -1
  91. package/handlers/database-migration-handler.js +64 -45
  92. package/handlers/middleware/admin-auth.js +53 -0
  93. package/handlers/routers/HEALTHCHECK.md +170 -135
  94. package/handlers/routers/admin.js +474 -0
  95. package/handlers/routers/auth.js +51 -9
  96. package/handlers/routers/db-migration.handler.js +2 -3
  97. package/handlers/routers/db-migration.js +102 -66
  98. package/handlers/routers/docs.js +180 -0
  99. package/handlers/routers/health.js +84 -283
  100. package/handlers/routers/integration-defined-routers.js +44 -33
  101. package/handlers/routers/integration-webhook-routers.js +73 -52
  102. package/handlers/routers/middleware/loadUser.js +39 -0
  103. package/handlers/routers/middleware/requireAdmin.js +43 -0
  104. package/handlers/routers/middleware/requireLoggedInUser.js +19 -0
  105. package/handlers/routers/user.js +30 -14
  106. package/handlers/routers/websocket.js +18 -4
  107. package/handlers/use-cases/check-integrations-health-use-case.js +13 -6
  108. package/handlers/workers/db-migration.js +74 -35
  109. package/handlers/workers/integration-defined-workers.js +31 -20
  110. package/index.js +50 -16
  111. package/infrastructure/scheduler/index.js +15 -11
  112. package/infrastructure/scheduler/netlify-scheduler-adapter.js +185 -0
  113. package/infrastructure/scheduler/scheduler-service-factory.js +38 -31
  114. package/integrations/WEBHOOK-QUICKSTART.md +21 -14
  115. package/integrations/index.js +4 -0
  116. package/integrations/integration-base.js +9 -6
  117. package/integrations/integration-router.js +1828 -157
  118. package/integrations/options.js +47 -20
  119. package/integrations/repositories/integration-mapping-repository-factory.js +4 -8
  120. package/integrations/repositories/integration-repository-documentdb.js +53 -24
  121. package/integrations/repositories/integration-repository-factory.js +6 -6
  122. package/integrations/repositories/integration-repository-interface.js +27 -9
  123. package/integrations/repositories/integration-repository-postgres.js +3 -3
  124. package/integrations/repositories/process-repository-factory.js +4 -7
  125. package/integrations/repositories/process-repository-interface.js +9 -5
  126. package/integrations/repositories/process-repository-mongo.js +7 -3
  127. package/integrations/repositories/process-repository-postgres.js +2 -2
  128. package/integrations/tests/doubles/config-capturing-integration.js +9 -8
  129. package/integrations/tests/doubles/dummy-integration-class.js +81 -8
  130. package/integrations/tests/doubles/test-integration-repository.js +51 -11
  131. package/integrations/use-cases/create-integration.js +36 -32
  132. package/integrations/use-cases/create-process.js +20 -9
  133. package/integrations/use-cases/delete-integration-for-user.js +5 -4
  134. package/integrations/use-cases/execute-proxy-request.js +564 -0
  135. package/integrations/use-cases/get-integration-for-user.js +25 -10
  136. package/integrations/use-cases/get-integration-instance-by-definition.js +13 -7
  137. package/integrations/use-cases/get-integrations-for-user.js +4 -1
  138. package/integrations/use-cases/get-possible-integrations.js +1 -1
  139. package/integrations/use-cases/get-process.js +5 -6
  140. package/integrations/use-cases/index.js +3 -1
  141. package/integrations/use-cases/load-integration-context.js +1 -5
  142. package/integrations/use-cases/update-process-state.js +10 -8
  143. package/integrations/utils/map-integration-dto.js +9 -3
  144. package/jest-setup.js +1 -1
  145. package/jest-teardown.js +1 -1
  146. package/lambda/TimeoutCatcher.js +32 -30
  147. package/lambda/index.js +1 -1
  148. package/logs/index.js +3 -3
  149. package/modules/domain/entities/AuthorizationSession.js +131 -0
  150. package/modules/domain/entities/index.js +10 -0
  151. package/modules/module.js +4 -9
  152. package/modules/repositories/authorization-session-repository-factory.js +53 -0
  153. package/modules/repositories/authorization-session-repository-interface.js +75 -0
  154. package/modules/repositories/authorization-session-repository-mongo.js +169 -0
  155. package/modules/repositories/authorization-session-repository-postgres.js +170 -0
  156. package/modules/repositories/module-repository-documentdb.js +89 -41
  157. package/modules/repositories/module-repository-factory.js +4 -6
  158. package/modules/repositories/module-repository-interface.js +13 -0
  159. package/modules/repositories/module-repository-mongo.js +40 -5
  160. package/modules/repositories/module-repository-postgres.js +42 -5
  161. package/modules/repositories/module-repository.js +28 -0
  162. package/modules/requester/api-key.js +0 -2
  163. package/modules/requester/basic.js +0 -1
  164. package/modules/requester/oauth-2.js +3 -14
  165. package/modules/test/mock-api/definition.js +15 -10
  166. package/modules/test/mock-api/mocks/hubspot.js +36 -36
  167. package/modules/tests/doubles/test-module-factory.js +10 -4
  168. package/modules/tests/doubles/test-module-repository.js +1 -1
  169. package/modules/use-cases/delete-module-entity.js +23 -0
  170. package/modules/use-cases/get-authorization-requirements.js +110 -0
  171. package/modules/use-cases/get-entities-for-user.js +1 -1
  172. package/modules/use-cases/get-entity-options-by-id.js +8 -6
  173. package/modules/use-cases/get-module-entity-by-id.js +16 -0
  174. package/modules/use-cases/get-module.js +12 -9
  175. package/modules/use-cases/process-authorization-callback.js +12 -6
  176. package/modules/use-cases/process-authorization-step.js +160 -0
  177. package/modules/use-cases/refresh-entity-options.js +8 -6
  178. package/modules/use-cases/start-authorization-session.js +86 -0
  179. package/modules/use-cases/test-module-auth.js +8 -6
  180. package/modules/use-cases/update-module-entity.js +27 -0
  181. package/modules/utils/map-module-dto.js +2 -2
  182. package/openapi/openapi-spec-generator.js +370 -0
  183. package/openapi/openapi-v1.yaml +1360 -0
  184. package/openapi/openapi-v2.yaml +1535 -0
  185. package/package.json +7 -9
  186. package/prisma-mongodb/schema.prisma +87 -0
  187. package/prisma-postgresql/migrations/20260222000000_add_admin_process_and_script_schedule/migration.sql +55 -0
  188. package/prisma-postgresql/schema.prisma +85 -0
  189. package/providers/index.js +13 -0
  190. package/providers/resolve-provider.js +93 -0
  191. package/queues/index.js +19 -0
  192. package/queues/providers/index.js +7 -0
  193. package/queues/providers/netlify-background-provider.js +76 -0
  194. package/queues/providers/qstash-queue-provider.js +122 -0
  195. package/queues/queue-client-interface.js +55 -0
  196. package/queues/queue-provider-factory.js +93 -0
  197. package/queues/queue-provider.js +46 -0
  198. package/queues/queuer-util.js +33 -25
  199. package/syncs/repositories/sync-repository-documentdb.js +39 -19
  200. package/syncs/repositories/sync-repository-factory.js +4 -4
  201. package/syncs/repositories/sync-repository-interface.js +6 -2
  202. package/syncs/repositories/sync-repository-postgres.js +19 -13
  203. package/syncs/sync.js +104 -100
  204. package/token/repositories/token-repository-factory.js +4 -6
  205. package/types/assertions/index.d.ts +72 -72
  206. package/types/core/index.d.ts +98 -53
  207. package/types/errors/index.d.ts +61 -54
  208. package/types/eslint-config/index.d.ts +40 -40
  209. package/types/lambda/index.d.ts +26 -26
  210. package/types/logs/index.d.ts +4 -4
  211. package/types/prettier-config/index.d.ts +5 -5
  212. package/types/test-environment/index.d.ts +14 -14
  213. package/types/tsconfig.json +95 -93
  214. package/user/repositories/user-repository-factory.js +4 -4
  215. package/user/tests/doubles/test-user-repository.js +5 -2
  216. package/user/use-cases/authenticate-user.js +2 -4
  217. package/user/use-cases/authenticate-with-shared-secret.js +1 -1
  218. package/user/use-cases/create-individual-user.js +8 -7
  219. package/user/use-cases/create-organization-user.js +1 -1
  220. package/user/use-cases/create-token-for-user-id.js +1 -1
  221. package/user/use-cases/delete-user.js +47 -0
  222. package/user/use-cases/get-user-from-adopter-jwt.js +0 -2
  223. package/user/use-cases/get-user-from-bearer-token.js +7 -3
  224. package/user/use-cases/get-user-from-x-frigg-headers.js +14 -11
  225. package/user/use-cases/login-user.js +8 -8
  226. package/user/user.js +18 -4
  227. package/utils/backend-path.js +2 -2
  228. package/utils/index.js +4 -1
  229. package/websocket/repositories/websocket-connection-repository-documentdb.js +46 -21
  230. package/websocket/repositories/websocket-connection-repository-factory.js +4 -8
  231. package/websocket/repositories/websocket-connection-repository-mongo.js +26 -16
  232. package/websocket/repositories/websocket-connection-repository-postgres.js +26 -15
  233. package/websocket/repositories/websocket-connection-repository.js +26 -20
  234. package/websocket/websocket-message-sender-interface.js +38 -0
  235. package/database/adapters/lambda-invoker.js +0 -97
  236. package/database/repositories/migration-status-repository-s3.js +0 -137
  237. package/infrastructure/scheduler/eventbridge-scheduler-adapter.js +0 -184
@@ -0,0 +1,39 @@
1
+ const catchAsyncError = require('express-async-handler');
2
+ const {
3
+ GetUserFromBearerToken,
4
+ } = require('../../../user/use-cases/get-user-from-bearer-token');
5
+ const {
6
+ createUserRepository,
7
+ } = require('../../../user/repositories/user-repository-factory');
8
+ const { loadAppDefinition } = require('../../app-definition-loader');
9
+
10
+ /**
11
+ * Load user from bearer token middleware
12
+ * Uses DDD pattern: Handler → Use Case → Repository
13
+ */
14
+ module.exports = catchAsyncError(async (req, res, next) => {
15
+ const authorizationHeader = req.headers.authorization;
16
+
17
+ if (authorizationHeader) {
18
+ // Initialize dependencies following DDD pattern
19
+ const { userConfig } = loadAppDefinition();
20
+ const userRepository = createUserRepository({ userConfig });
21
+ const getUserFromBearerToken = new GetUserFromBearerToken({
22
+ userRepository,
23
+ userConfig,
24
+ });
25
+
26
+ try {
27
+ // Execute use case to load user
28
+ req.user = await getUserFromBearerToken.execute(
29
+ authorizationHeader
30
+ );
31
+ } catch (error) {
32
+ // Don't fail - just leave req.user undefined
33
+ // Let requireLoggedInUser middleware handle auth failures
34
+ console.debug('Failed to load user from token:', error.message);
35
+ }
36
+ }
37
+
38
+ return next();
39
+ });
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Middleware to require admin API key authentication.
3
+ * Checks for x-frigg-admin-api-key header matching ADMIN_API_KEY environment variable.
4
+ * In non-production environments, allows all requests through for easier development.
5
+ *
6
+ * Uses the same header convention as validateAdminApiKey (handlers/middleware/admin-auth.js).
7
+ *
8
+ * @param {import('express').Request} req - Express request object
9
+ * @param {import('express').Response} res - Express response object
10
+ * @param {import('express').NextFunction} next - Express next middleware function
11
+ */
12
+ const requireAdmin = (req, res, next) => {
13
+ // Allow access in local development (when NODE_ENV is not production)
14
+ if (process.env.NODE_ENV !== 'production') {
15
+ console.log('[requireAdmin] Development mode - bypassing admin auth');
16
+ return next();
17
+ }
18
+
19
+ const apiKey = req.headers['x-frigg-admin-api-key'];
20
+
21
+ if (!apiKey) {
22
+ console.error('[requireAdmin] Missing x-frigg-admin-api-key header');
23
+ return res.status(401).json({
24
+ status: 'error',
25
+ message: 'Unauthorized - Admin API key required',
26
+ code: 'MISSING_API_KEY',
27
+ });
28
+ }
29
+
30
+ if (apiKey !== process.env.ADMIN_API_KEY) {
31
+ console.error('[requireAdmin] Invalid API key provided');
32
+ return res.status(401).json({
33
+ status: 'error',
34
+ message: 'Unauthorized - Invalid admin API key',
35
+ code: 'INVALID_API_KEY',
36
+ });
37
+ }
38
+
39
+ console.log('[requireAdmin] Admin authentication successful');
40
+ next();
41
+ };
42
+
43
+ module.exports = { requireAdmin };
@@ -0,0 +1,19 @@
1
+ const Boom = require('@hapi/boom');
2
+
3
+ /**
4
+ * Require logged in user middleware
5
+ * Ensures req.user was successfully loaded by loadUser middleware
6
+ *
7
+ * Uses DDD pattern: Middleware checks domain entity existence
8
+ * req.user is populated by loadUser middleware using GetUserFromBearerToken use case
9
+ */
10
+ const requireLoggedInUser = (req, res, next) => {
11
+ // Check if user was successfully loaded by loadUser middleware
12
+ if (!req.user || !req.user.getId()) {
13
+ throw Boom.unauthorized('Invalid Token');
14
+ }
15
+
16
+ next();
17
+ };
18
+
19
+ module.exports = { requireLoggedInUser };
@@ -15,17 +15,33 @@ const catchAsyncError = require('express-async-handler');
15
15
  const { loadAppDefinition } = require('../app-definition-loader');
16
16
 
17
17
  const router = express();
18
- const { userConfig } = loadAppDefinition();
19
- const userRepository = createUserRepository();
20
- const createIndividualUser = new CreateIndividualUser({
21
- userRepository,
22
- userConfig,
23
- });
24
- const loginUser = new LoginUser({
25
- userRepository,
26
- userConfig,
18
+
19
+ // Lazy-initialized repositories and use cases (deferred until first request)
20
+ let _userRepository, _createIndividualUser, _loginUser, _createTokenForUserId;
21
+
22
+ function ensureInitialized() {
23
+ if (!_userRepository) {
24
+ const { userConfig } = loadAppDefinition();
25
+ _userRepository = createUserRepository();
26
+ _createIndividualUser = new CreateIndividualUser({
27
+ userRepository: _userRepository,
28
+ userConfig,
29
+ });
30
+ _loginUser = new LoginUser({
31
+ userRepository: _userRepository,
32
+ userConfig,
33
+ });
34
+ _createTokenForUserId = new CreateTokenForUserId({
35
+ userRepository: _userRepository,
36
+ });
37
+ }
38
+ }
39
+
40
+ // Lazy initialization middleware — runs once on first request
41
+ router.use((req, res, next) => {
42
+ ensureInitialized();
43
+ next();
27
44
  });
28
- const createTokenForUserId = new CreateTokenForUserId({ userRepository });
29
45
 
30
46
  // define the login endpoint
31
47
  router.route('/user/login').post(
@@ -34,8 +50,8 @@ router.route('/user/login').post(
34
50
  'username',
35
51
  'password',
36
52
  ]);
37
- const user = await loginUser.execute({ username, password });
38
- const token = await createTokenForUserId.execute(user.getId(), 120);
53
+ const user = await _loginUser.execute({ username, password });
54
+ const token = await _createTokenForUserId.execute(user.getId(), 120);
39
55
  res.status(201);
40
56
  res.json({ token });
41
57
  })
@@ -48,11 +64,11 @@ router.route('/user/create').post(
48
64
  'password',
49
65
  ]);
50
66
 
51
- const user = await createIndividualUser.execute({
67
+ const user = await _createIndividualUser.execute({
52
68
  username,
53
69
  password,
54
70
  });
55
- const token = await createTokenForUserId.execute(user.getId(), 120);
71
+ const token = await _createTokenForUserId.execute(user.getId(), 120);
56
72
  res.status(201);
57
73
  res.json({ token });
58
74
  })
@@ -1,7 +1,17 @@
1
1
  const { createHandler } = require('@friggframework/core');
2
- const { createWebsocketConnectionRepository } = require('../../database/websocket-connection-repository-factory');
2
+ const {
3
+ createWebsocketConnectionRepository,
4
+ } = require('../../database/websocket-connection-repository-factory');
3
5
 
4
- const websocketConnectionRepository = createWebsocketConnectionRepository();
6
+ let _websocketConnectionRepository;
7
+
8
+ function getRepository() {
9
+ if (!_websocketConnectionRepository) {
10
+ _websocketConnectionRepository =
11
+ createWebsocketConnectionRepository();
12
+ }
13
+ return _websocketConnectionRepository;
14
+ }
5
15
 
6
16
  const handleWebSocketConnection = async (event, context) => {
7
17
  // Handle different WebSocket events
@@ -10,7 +20,9 @@ const handleWebSocketConnection = async (event, context) => {
10
20
  // Handle new connection
11
21
  try {
12
22
  const connectionId = event.requestContext.connectionId;
13
- await websocketConnectionRepository.createConnection(connectionId);
23
+ await getRepository().createConnection(
24
+ connectionId
25
+ );
14
26
  console.log(`Stored new connection: ${connectionId}`);
15
27
  return { statusCode: 200, body: 'Connected.' };
16
28
  } catch (error) {
@@ -22,7 +34,9 @@ const handleWebSocketConnection = async (event, context) => {
22
34
  // Handle disconnection
23
35
  try {
24
36
  const connectionId = event.requestContext.connectionId;
25
- await websocketConnectionRepository.deleteConnection(connectionId);
37
+ await getRepository().deleteConnection(
38
+ connectionId
39
+ );
26
40
  console.log(`Removed connection: ${connectionId}`);
27
41
  return { statusCode: 200, body: 'Disconnected.' };
28
42
  } catch (error) {
@@ -5,9 +5,10 @@ class CheckIntegrationsHealthUseCase {
5
5
  }
6
6
 
7
7
  execute() {
8
- const moduleDefinitions = (this.moduleFactory && this.moduleFactory.moduleDefinitions)
9
- ? this.moduleFactory.moduleDefinitions
10
- : [];
8
+ const moduleDefinitions =
9
+ this.moduleFactory && this.moduleFactory.moduleDefinitions
10
+ ? this.moduleFactory.moduleDefinitions
11
+ : [];
11
12
 
12
13
  const integrationClasses = Array.isArray(this.integrationClasses)
13
14
  ? this.integrationClasses
@@ -15,13 +16,19 @@ class CheckIntegrationsHealthUseCase {
15
16
 
16
17
  // Extract module names from definitions
17
18
  const moduleTypes = Array.isArray(moduleDefinitions)
18
- ? moduleDefinitions.map(def => def.moduleName || def.name || def.label || 'Unknown')
19
+ ? moduleDefinitions.map(
20
+ (def) => def.moduleName || def.name || def.label || 'Unknown'
21
+ )
19
22
  : [];
20
23
 
21
24
  // Extract integration names from classes
22
- const integrationNames = integrationClasses.map(IntegrationClass => {
25
+ const integrationNames = integrationClasses.map((IntegrationClass) => {
23
26
  try {
24
- return IntegrationClass.Definition?.name || IntegrationClass.name || 'Unknown';
27
+ return (
28
+ IntegrationClass.Definition?.name ||
29
+ IntegrationClass.name ||
30
+ 'Unknown'
31
+ );
25
32
  } catch {
26
33
  return 'Unknown';
27
34
  }
@@ -49,16 +49,28 @@ const {
49
49
  const {
50
50
  CheckDatabaseStateUseCase,
51
51
  } = require('../../database/use-cases/check-database-state-use-case');
52
- const {
53
- MigrationStatusRepositoryS3,
54
- } = require('../../database/repositories/migration-status-repository-s3');
55
-
56
52
  // Inject prisma-runner as dependency
57
53
  const prismaRunner = require('../../database/utils/prisma-runner');
58
54
 
59
- // Use S3 repository for migration status tracking (no User table dependency)
60
- const bucketName = process.env.S3_BUCKET_NAME || process.env.MIGRATION_STATUS_BUCKET;
61
- const migrationStatusRepository = new MigrationStatusRepositoryS3(bucketName);
55
+ // Migration status repository is loaded from the resolved provider.
56
+ const { resolveProvider } = require('../../providers/resolve-provider');
57
+
58
+ let _migrationStatusRepository = null;
59
+ function getMigrationStatusRepository() {
60
+ if (!_migrationStatusRepository) {
61
+ const provider = resolveProvider();
62
+ const MigrationStatusRepository = provider.MigrationStatusRepositoryS3;
63
+ if (!MigrationStatusRepository) {
64
+ throw new Error(
65
+ `Provider '${provider.name}' does not export a MigrationStatusRepository`
66
+ );
67
+ }
68
+ const bucketName =
69
+ process.env.S3_BUCKET_NAME || process.env.MIGRATION_STATUS_BUCKET;
70
+ _migrationStatusRepository = new MigrationStatusRepository(bucketName);
71
+ }
72
+ return _migrationStatusRepository;
73
+ }
62
74
 
63
75
  /**
64
76
  * Sanitizes error messages to prevent credential leaks
@@ -68,19 +80,27 @@ const migrationStatusRepository = new MigrationStatusRepositoryS3(bucketName);
68
80
  function sanitizeError(errorMessage) {
69
81
  if (!errorMessage) return 'Unknown error';
70
82
 
71
- return String(errorMessage)
72
- // Remove PostgreSQL connection strings
73
- .replace(/postgresql:\/\/[^@\s]+@[^\s/]+/gi, 'postgresql://***:***@***')
74
- // Remove MongoDB connection strings
75
- .replace(/mongodb(\+srv)?:\/\/[^@\s]+@[^\s/]+/gi, 'mongodb$1://***:***@***')
76
- // Remove password parameters
77
- .replace(/password[=:]\s*[^\s,;)]+/gi, 'password=***')
78
- // Remove API keys
79
- .replace(/apikey[=:]\s*[^\s,;)]+/gi, 'apikey=***')
80
- .replace(/api[_-]?key[=:]\s*[^\s,;)]+/gi, 'api_key=***')
81
- // Remove tokens
82
- .replace(/token[=:]\s*[^\s,;)]+/gi, 'token=***')
83
- .replace(/bearer\s+[^\s,;)]+/gi, 'bearer ***');
83
+ return (
84
+ String(errorMessage)
85
+ // Remove PostgreSQL connection strings
86
+ .replace(
87
+ /postgresql:\/\/[^@\s]+@[^\s/]+/gi,
88
+ 'postgresql://***:***@***'
89
+ )
90
+ // Remove MongoDB connection strings
91
+ .replace(
92
+ /mongodb(\+srv)?:\/\/[^@\s]+@[^\s/]+/gi,
93
+ 'mongodb$1://***:***@***'
94
+ )
95
+ // Remove password parameters
96
+ .replace(/password[=:]\s*[^\s,;)]+/gi, 'password=***')
97
+ // Remove API keys
98
+ .replace(/apikey[=:]\s*[^\s,;)]+/gi, 'apikey=***')
99
+ .replace(/api[_-]?key[=:]\s*[^\s,;)]+/gi, 'api_key=***')
100
+ // Remove tokens
101
+ .replace(/token[=:]\s*[^\s,;)]+/gi, 'token=***')
102
+ .replace(/bearer\s+[^\s,;)]+/gi, 'bearer ***')
103
+ );
84
104
  }
85
105
 
86
106
  /**
@@ -145,11 +165,18 @@ exports.handler = async (event, context) => {
145
165
  console.log('Database Migration Lambda Started');
146
166
  console.log('========================================');
147
167
  console.log('Event:', JSON.stringify(event, null, 2));
148
- console.log('Context:', JSON.stringify({
149
- requestId: context.requestId,
150
- functionName: context.functionName,
151
- remainingTimeInMillis: context.getRemainingTimeInMillis(),
152
- }, null, 2));
168
+ console.log(
169
+ 'Context:',
170
+ JSON.stringify(
171
+ {
172
+ requestId: context.requestId,
173
+ functionName: context.functionName,
174
+ remainingTimeInMillis: context.getRemainingTimeInMillis(),
175
+ },
176
+ null,
177
+ 2
178
+ )
179
+ );
153
180
 
154
181
  // Extract migration parameters from event
155
182
  const { migrationId, dbType, stage } = extractMigrationParams(event);
@@ -164,7 +191,9 @@ exports.handler = async (event, context) => {
164
191
  console.log(`========================================`);
165
192
 
166
193
  try {
167
- const checkDbStateUseCase = new CheckDatabaseStateUseCase({ prismaRunner });
194
+ const checkDbStateUseCase = new CheckDatabaseStateUseCase({
195
+ prismaRunner,
196
+ });
168
197
  const status = await checkDbStateUseCase.execute(dbType, stage);
169
198
 
170
199
  console.log('✓ Database state check completed');
@@ -217,8 +246,10 @@ exports.handler = async (event, context) => {
217
246
 
218
247
  // Update migration status to RUNNING (if migrationId provided)
219
248
  if (migrationId) {
220
- console.log(`\n✓ Updating migration status to RUNNING: ${migrationId}`);
221
- await migrationStatusRepository.update({
249
+ console.log(
250
+ `\n✓ Updating migration status to RUNNING: ${migrationId}`
251
+ );
252
+ await getMigrationStatusRepository().update({
222
253
  migrationId,
223
254
  stage,
224
255
  state: 'RUNNING',
@@ -255,8 +286,10 @@ exports.handler = async (event, context) => {
255
286
 
256
287
  // Update migration status to COMPLETED (if migrationId provided)
257
288
  if (migrationId) {
258
- console.log(`\n✓ Updating migration status to COMPLETED: ${migrationId}`);
259
- await migrationStatusRepository.update({
289
+ console.log(
290
+ `\n✓ Updating migration status to COMPLETED: ${migrationId}`
291
+ );
292
+ await getMigrationStatusRepository().update({
260
293
  migrationId,
261
294
  stage,
262
295
  state: 'COMPLETED',
@@ -284,7 +317,6 @@ exports.handler = async (event, context) => {
284
317
  statusCode: 200,
285
318
  body: JSON.stringify(responseBody),
286
319
  };
287
-
288
320
  } catch (error) {
289
321
  console.error('\n========================================');
290
322
  console.error('Migration Failed');
@@ -317,8 +349,10 @@ exports.handler = async (event, context) => {
317
349
  // Update migration status to FAILED (if migrationId provided)
318
350
  if (migrationId) {
319
351
  try {
320
- console.log(`\n✓ Updating migration status to FAILED: ${migrationId}`);
321
- await migrationStatusRepository.update({
352
+ console.log(
353
+ `\n✓ Updating migration status to FAILED: ${migrationId}`
354
+ );
355
+ await getMigrationStatusRepository().update({
322
356
  migrationId,
323
357
  stage,
324
358
  state: 'FAILED',
@@ -327,7 +361,10 @@ exports.handler = async (event, context) => {
327
361
  failedAt: new Date().toISOString(),
328
362
  });
329
363
  } catch (updateError) {
330
- console.error('Failed to update migration status:', updateError.message);
364
+ console.error(
365
+ 'Failed to update migration status:',
366
+ updateError.message
367
+ );
331
368
  // Continue - don't let status update failure block error response
332
369
  }
333
370
  }
@@ -337,7 +374,9 @@ exports.handler = async (event, context) => {
337
374
  error: sanitizedError,
338
375
  errorType: error.name || 'Error',
339
376
  // Only include stack traces in development environments
340
- ...(stage === 'dev' || stage === 'local' || stage === 'test' ? { stack: error.stack } : {}),
377
+ ...(stage === 'dev' || stage === 'local' || stage === 'test'
378
+ ? { stack: error.stack }
379
+ : {}),
341
380
  };
342
381
 
343
382
  if (migrationId) {
@@ -2,26 +2,37 @@ const { createHandler } = require('@friggframework/core');
2
2
  const { loadAppDefinition } = require('../app-definition-loader');
3
3
  const { createQueueWorker } = require('../backend-utils');
4
4
 
5
- const handlers = {};
6
- const { integrations: integrationClasses } = loadAppDefinition();
5
+ let _handlers;
7
6
 
8
- integrationClasses.forEach((IntegrationClass) => {
9
- const defaultQueueWorker = createQueueWorker(IntegrationClass);
7
+ function ensureHandlers() {
8
+ if (!_handlers) {
9
+ _handlers = {};
10
+ const { integrations: integrationClasses } = loadAppDefinition();
10
11
 
11
- handlers[`${IntegrationClass.Definition.name}`] = {
12
- queueWorker: createHandler({
13
- eventName: `Queue Worker for ${IntegrationClass.Definition.name}`,
14
- isUserFacingResponse: false,
15
- method: async (event, context) => {
16
- const worker = new defaultQueueWorker();
17
- await worker.run(event, context);
18
- return {
19
- message: 'Successfully processed the Generic Queue Worker',
20
- input: event,
21
- };
22
- },
23
- }),
24
- };
25
- });
12
+ integrationClasses.forEach((IntegrationClass) => {
13
+ const defaultQueueWorker = createQueueWorker(IntegrationClass);
26
14
 
27
- module.exports = { handlers };
15
+ _handlers[`${IntegrationClass.Definition.name}`] = {
16
+ queueWorker: createHandler({
17
+ eventName: `Queue Worker for ${IntegrationClass.Definition.name}`,
18
+ isUserFacingResponse: false,
19
+ method: async (event, context) => {
20
+ const worker = new defaultQueueWorker();
21
+ await worker.run(event, context);
22
+ return {
23
+ message: 'Successfully processed the Generic Queue Worker',
24
+ input: event,
25
+ };
26
+ },
27
+ }),
28
+ };
29
+ });
30
+ }
31
+ return _handlers;
32
+ }
33
+
34
+ module.exports = {
35
+ get handlers() {
36
+ return ensureHandlers();
37
+ },
38
+ };
package/index.js CHANGED
@@ -21,7 +21,6 @@ const {
21
21
  } = require('./database/index');
22
22
  const {
23
23
  createUserRepository,
24
- UserRepositoryMongo,
25
24
  UserRepositoryPostgres,
26
25
  } = require('./user/repositories/user-repository-factory');
27
26
  const {
@@ -30,9 +29,7 @@ const {
30
29
  const {
31
30
  GetUserFromAdopterJwt,
32
31
  } = require('./user/use-cases/get-user-from-adopter-jwt');
33
- const {
34
- AuthenticateUser,
35
- } = require('./user/use-cases/authenticate-user');
32
+ const { AuthenticateUser } = require('./user/use-cases/authenticate-user');
36
33
 
37
34
  const {
38
35
  CredentialRepository,
@@ -43,18 +40,14 @@ const {
43
40
  const {
44
41
  IntegrationMappingRepository,
45
42
  } = require('./integrations/repositories/integration-mapping-repository');
46
- const {
47
- CreateProcess,
48
- } = require('./integrations/use-cases/create-process');
43
+ const { CreateProcess } = require('./integrations/use-cases/create-process');
49
44
  const {
50
45
  UpdateProcessState,
51
46
  } = require('./integrations/use-cases/update-process-state');
52
47
  const {
53
48
  UpdateProcessMetrics,
54
49
  } = require('./integrations/use-cases/update-process-metrics');
55
- const {
56
- GetProcess,
57
- } = require('./integrations/use-cases/get-process');
50
+ const { GetProcess } = require('./integrations/use-cases/get-process');
58
51
  const { Cryptor } = require('./encrypt');
59
52
  const {
60
53
  BaseError,
@@ -70,12 +63,11 @@ const {
70
63
  checkRequiredParams,
71
64
  getModulesDefinitionFromIntegrationClasses,
72
65
  LoadIntegrationContextUseCase,
66
+ createProcessRepository,
73
67
  } = require('./integrations/index');
74
68
  const { TimeoutCatcher } = require('./lambda/index');
75
69
  const { debug, initDebugLog, flushDebugLog } = require('./logs/index');
76
70
  const {
77
- Credential,
78
- Entity,
79
71
  ApiKeyRequester,
80
72
  BasicAuthRequester,
81
73
  OAuth2Requester,
@@ -86,7 +78,31 @@ const {
86
78
  const application = require('./application');
87
79
  const utils = require('./utils');
88
80
 
89
- const { QueuerUtil } = require('./queues');
81
+ const {
82
+ QueuerUtil,
83
+ QueueProvider,
84
+ QueueClientInterface,
85
+ createQueueProvider,
86
+ QUEUE_PROVIDERS,
87
+ } = require('./queues');
88
+
89
+ const {
90
+ EncryptionKeyProviderInterface,
91
+ } = require('./encrypt/encryption-key-provider-interface');
92
+ const {
93
+ AesEncryptionKeyProvider,
94
+ } = require('./encrypt/aes-encryption-key-provider');
95
+ const {
96
+ WebSocketMessageSenderInterface,
97
+ StaleConnectionError,
98
+ } = require('./websocket/websocket-message-sender-interface');
99
+
100
+ const {
101
+ resolveProvider,
102
+ determineProviderName,
103
+ providerPackageName,
104
+ KNOWN_PROVIDERS,
105
+ } = require('./providers');
90
106
 
91
107
  module.exports = {
92
108
  // assertions
@@ -110,7 +126,7 @@ module.exports = {
110
126
  TokenRepository,
111
127
  WebsocketConnectionRepository,
112
128
  createUserRepository,
113
- UserRepositoryMongo,
129
+ get UserRepositoryMongo() { return require('./user/repositories/user-repository-factory').UserRepositoryMongo; },
114
130
  UserRepositoryPostgres,
115
131
  GetUserFromXFriggHeaders,
116
132
  GetUserFromAdopterJwt,
@@ -138,6 +154,7 @@ module.exports = {
138
154
  UpdateProcessState,
139
155
  UpdateProcessMetrics,
140
156
  GetProcess,
157
+ createProcessRepository,
141
158
 
142
159
  // application - Command factories for integration developers
143
160
  application,
@@ -147,6 +164,7 @@ module.exports = {
147
164
  createEntityCommands: application.createEntityCommands,
148
165
  createCredentialCommands: application.createCredentialCommands,
149
166
  createSchedulerCommands: application.createSchedulerCommands,
167
+ createAdminScriptCommands: application.createAdminScriptCommands,
150
168
  findIntegrationContextByExternalEntityId:
151
169
  application.findIntegrationContextByExternalEntityId,
152
170
  integrationCommands: application.integrationCommands,
@@ -160,8 +178,6 @@ module.exports = {
160
178
  flushDebugLog,
161
179
 
162
180
  // module plugin
163
- Credential,
164
- Entity,
165
181
  ApiKeyRequester,
166
182
  BasicAuthRequester,
167
183
  OAuth2Requester,
@@ -170,6 +186,24 @@ module.exports = {
170
186
  ModuleFactory,
171
187
  // queues
172
188
  QueuerUtil,
189
+ QueueProvider,
190
+ QueueClientInterface,
191
+ createQueueProvider,
192
+ QUEUE_PROVIDERS,
193
+
194
+ // encryption interfaces
195
+ EncryptionKeyProviderInterface,
196
+ AesEncryptionKeyProvider,
197
+
198
+ // websocket interfaces
199
+ WebSocketMessageSenderInterface,
200
+ StaleConnectionError,
201
+
202
+ // providers
203
+ resolveProvider,
204
+ determineProviderName,
205
+ providerPackageName,
206
+ KNOWN_PROVIDERS,
173
207
 
174
208
  // utils
175
209
  ...utils,