@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
@@ -1,8 +1,7 @@
1
1
  const { prisma } = require('../../database/prisma');
2
2
  const {
3
- ApiGatewayManagementApiClient,
4
- PostToConnectionCommand,
5
- } = require('@aws-sdk/client-apigatewaymanagementapi');
3
+ StaleConnectionError,
4
+ } = require('../websocket-message-sender-interface');
6
5
  const {
7
6
  WebsocketConnectionRepositoryInterface,
8
7
  } = require('./websocket-connection-repository-interface');
@@ -16,15 +15,16 @@ const {
16
15
  * - PostgreSQL: Integer IDs with auto-increment
17
16
  * - Both use same query patterns (no many-to-many differences)
18
17
  *
19
- * Migration from Mongoose:
20
- * - Constructor injection of Prisma client
21
- * - Static method getActiveConnections() → Instance method
22
- * - AWS API Gateway Management API integration preserved
18
+ * BREAKING CHANGE (v3): A messageSender must be explicitly provided for
19
+ * WebSocket send functionality. For AWS API Gateway, pass
20
+ * `new ApiGatewayMessageSender()` from @friggframework/provider-aws.
21
+ * See docs/architecture-decisions/010-decouple-aws-from-core.md for migration guide.
23
22
  */
24
23
  class WebsocketConnectionRepository extends WebsocketConnectionRepositoryInterface {
25
- constructor(prismaClient = prisma) {
24
+ constructor(prismaClient = prisma, messageSender = null) {
26
25
  super();
27
- this.prisma = prismaClient; // Allow injection for testing
26
+ this.prisma = prismaClient;
27
+ this._messageSender = messageSender;
28
28
  }
29
29
 
30
30
  /**
@@ -79,25 +79,31 @@ class WebsocketConnectionRepository extends WebsocketConnectionRepositoryInterfa
79
79
  select: { connectionId: true },
80
80
  });
81
81
 
82
+ if (!this._messageSender) {
83
+ throw new Error(
84
+ 'WebsocketConnectionRepository requires a messageSender for send functionality.\n' +
85
+ 'Pass one via constructor, e.g.:\n' +
86
+ ' const { ApiGatewayMessageSender } = require("@friggframework/provider-aws");\n' +
87
+ ' new WebsocketConnectionRepository(prisma, new ApiGatewayMessageSender())\n' +
88
+ 'See docs/architecture-decisions/010-decouple-aws-from-core.md for migration guide.'
89
+ );
90
+ }
91
+ const sender = this._messageSender;
92
+
82
93
  return connections.map((conn) => ({
83
94
  connectionId: conn.connectionId,
84
95
  send: async (data) => {
85
- const apigwManagementApi = new ApiGatewayManagementApiClient({
86
- endpoint: process.env.WEBSOCKET_API_ENDPOINT,
87
- });
88
-
89
96
  try {
90
- const command = new PostToConnectionCommand({
91
- ConnectionId: conn.connectionId,
92
- Data: JSON.stringify(data),
93
- });
94
- await apigwManagementApi.send(command);
97
+ await sender.send(
98
+ conn.connectionId,
99
+ data,
100
+ process.env.WEBSOCKET_API_ENDPOINT
101
+ );
95
102
  } catch (error) {
96
- if (error.statusCode === 410 || error.$metadata?.httpStatusCode === 410) {
103
+ if (error instanceof StaleConnectionError) {
97
104
  console.log(
98
105
  `Stale connection ${conn.connectionId}`
99
106
  );
100
- // Delete stale connection
101
107
  await this.prisma.websocketConnection.deleteMany({
102
108
  where: { connectionId: conn.connectionId },
103
109
  });
@@ -0,0 +1,38 @@
1
+ /**
2
+ * WebSocket Message Sender Interface (Port)
3
+ *
4
+ * Defines the contract for sending messages to active WebSocket connections.
5
+ * Used by WebSocket connection repositories to push data to connected clients.
6
+ *
7
+ * Following Frigg's hexagonal architecture pattern:
8
+ * - Port defines WHAT the service does (contract)
9
+ * - Adapters implement HOW (AWS API Gateway, Netlify, etc.)
10
+ */
11
+ class WebSocketMessageSenderInterface {
12
+ /**
13
+ * Send data to a specific WebSocket connection
14
+ *
15
+ * @param {string} connectionId - The WebSocket connection identifier
16
+ * @param {*} data - Data to send (will be JSON-serialized by the adapter)
17
+ * @param {string} endpoint - The WebSocket API endpoint URL
18
+ * @returns {Promise<void>}
19
+ * @throws {StaleConnectionError} If the connection is no longer active (410 Gone)
20
+ */
21
+ async send(connectionId, data, endpoint) {
22
+ throw new Error('Method send must be implemented by subclass');
23
+ }
24
+ }
25
+
26
+ /**
27
+ * Error thrown when a WebSocket connection is stale (disconnected).
28
+ * Repositories should catch this and clean up the stale connection record.
29
+ */
30
+ class StaleConnectionError extends Error {
31
+ constructor(connectionId) {
32
+ super(`Stale WebSocket connection: ${connectionId}`);
33
+ this.name = 'StaleConnectionError';
34
+ this.connectionId = connectionId;
35
+ }
36
+ }
37
+
38
+ module.exports = { WebSocketMessageSenderInterface, StaleConnectionError };
@@ -1,97 +0,0 @@
1
- /**
2
- * Lambda Invoker Adapter
3
- * Infrastructure layer - handles AWS Lambda function invocations
4
- *
5
- * Part of Hexagonal Architecture:
6
- * - Infrastructure Layer adapter for AWS SDK
7
- * - Used by Domain Layer use cases
8
- * - Isolates AWS-specific logic from business logic
9
- */
10
-
11
- const { LambdaClient, InvokeCommand } = require('@aws-sdk/client-lambda');
12
-
13
- /**
14
- * Custom error for Lambda invocation failures
15
- * Provides structured error information for debugging
16
- */
17
- class LambdaInvocationError extends Error {
18
- constructor(message, functionName, statusCode) {
19
- super(message);
20
- this.name = 'LambdaInvocationError';
21
- this.functionName = functionName;
22
- this.statusCode = statusCode;
23
- }
24
- }
25
-
26
- /**
27
- * Adapter for invoking AWS Lambda functions
28
- *
29
- * Infrastructure layer - handles AWS SDK communication
30
- * Converts AWS SDK responses to domain-friendly formats
31
- */
32
- class LambdaInvoker {
33
- /**
34
- * @param {LambdaClient} lambdaClient - AWS Lambda client (injected for testability)
35
- */
36
- constructor(lambdaClient = new LambdaClient({})) {
37
- this.client = lambdaClient;
38
- }
39
-
40
- /**
41
- * Invoke Lambda function synchronously
42
- *
43
- * @param {string} functionName - Lambda function name or ARN
44
- * @param {Object} payload - Event payload to send to Lambda
45
- * @returns {Promise<Object>} Parsed response body
46
- * @throws {LambdaInvocationError} If Lambda returns error status
47
- * @throws {Error} If AWS SDK call fails
48
- */
49
- async invoke(functionName, payload) {
50
- try {
51
- const command = new InvokeCommand({
52
- FunctionName: functionName,
53
- InvocationType: 'RequestResponse', // Synchronous
54
- Payload: JSON.stringify(payload),
55
- });
56
-
57
- const response = await this.client.send(command);
58
-
59
- // Parse response payload
60
- let result;
61
- try {
62
- result = JSON.parse(Buffer.from(response.Payload).toString());
63
- } catch (parseError) {
64
- throw new LambdaInvocationError(
65
- `Failed to parse Lambda response: ${parseError.message}`,
66
- functionName,
67
- null
68
- );
69
- }
70
-
71
- // Check status code
72
- if (result.statusCode === 200) {
73
- return result.body;
74
- }
75
-
76
- // Lambda returned error status
77
- const errorMessage = result.body?.error || 'Lambda invocation failed';
78
- throw new LambdaInvocationError(
79
- `Lambda ${functionName} returned error: ${errorMessage}`,
80
- functionName,
81
- result.statusCode
82
- );
83
- } catch (error) {
84
- // Re-throw LambdaInvocationError as-is
85
- if (error instanceof LambdaInvocationError) {
86
- throw error;
87
- }
88
-
89
- // Wrap AWS SDK errors
90
- throw new Error(`Failed to invoke Lambda ${functionName}: ${error.message}`);
91
- }
92
- }
93
- }
94
-
95
- module.exports = { LambdaInvoker, LambdaInvocationError };
96
-
97
-
@@ -1,137 +0,0 @@
1
- /**
2
- * Migration Status Repository - S3 Storage
3
- *
4
- * Infrastructure Layer - Hexagonal Architecture
5
- *
6
- * Stores migration status in S3 to avoid chicken-and-egg dependency on User/Process tables.
7
- * Initial database migrations can't use Process table (requires User FK which doesn't exist yet).
8
- */
9
-
10
- const { S3Client, PutObjectCommand, GetObjectCommand } = require('@aws-sdk/client-s3');
11
- const { randomUUID } = require('crypto');
12
-
13
- class MigrationStatusRepositoryS3 {
14
- /**
15
- * @param {string} bucketName - S3 bucket name for migration status storage
16
- * @param {S3Client} s3Client - Optional S3 client (for testing)
17
- */
18
- constructor(bucketName, s3Client = null) {
19
- this.bucketName = bucketName;
20
- this.s3Client = s3Client || new S3Client({ region: process.env.AWS_REGION || 'us-east-1' });
21
- }
22
-
23
- /**
24
- * Build S3 key for migration status
25
- * @param {string} migrationId - Migration identifier
26
- * @param {string} stage - Deployment stage
27
- * @returns {string} S3 key
28
- */
29
- _buildS3Key(migrationId, stage) {
30
- return `migrations/${stage}/${migrationId}.json`;
31
- }
32
-
33
- /**
34
- * Create new migration status record
35
- * @param {Object} data - Migration data
36
- * @param {string} [data.migrationId] - Migration ID (generates UUID if not provided)
37
- * @param {string} data.stage - Deployment stage
38
- * @param {string} [data.triggeredBy] - User or system that triggered migration
39
- * @param {string} [data.triggeredAt] - ISO timestamp
40
- * @returns {Promise<Object>} Created migration status
41
- */
42
- async create(data) {
43
- const migrationId = data.migrationId || randomUUID();
44
- const timestamp = data.triggeredAt || new Date().toISOString();
45
-
46
- const status = {
47
- migrationId,
48
- stage: data.stage,
49
- state: 'INITIALIZING',
50
- progress: 0,
51
- triggeredBy: data.triggeredBy || 'system',
52
- triggeredAt: timestamp,
53
- createdAt: timestamp,
54
- updatedAt: timestamp,
55
- };
56
-
57
- const key = this._buildS3Key(migrationId, data.stage);
58
-
59
- await this.s3Client.send(
60
- new PutObjectCommand({
61
- Bucket: this.bucketName,
62
- Key: key,
63
- Body: JSON.stringify(status, null, 2),
64
- ContentType: 'application/json',
65
- })
66
- );
67
-
68
- return status;
69
- }
70
-
71
- /**
72
- * Update existing migration status
73
- * @param {Object} data - Update data
74
- * @param {string} data.migrationId - Migration ID
75
- * @param {string} data.stage - Deployment stage
76
- * @param {string} [data.state] - New state
77
- * @param {number} [data.progress] - Progress percentage (0-100)
78
- * @param {string} [data.error] - Error message if failed
79
- * @param {string} [data.completedAt] - Completion timestamp
80
- * @returns {Promise<Object>} Updated migration status
81
- */
82
- async update(data) {
83
- const key = this._buildS3Key(data.migrationId, data.stage);
84
-
85
- // Get existing status
86
- const existing = await this.get(data.migrationId, data.stage);
87
-
88
- // Merge updates
89
- const updated = {
90
- ...existing,
91
- ...data,
92
- updatedAt: new Date().toISOString(),
93
- };
94
-
95
- await this.s3Client.send(
96
- new PutObjectCommand({
97
- Bucket: this.bucketName,
98
- Key: key,
99
- Body: JSON.stringify(updated, null, 2),
100
- ContentType: 'application/json',
101
- })
102
- );
103
-
104
- return updated;
105
- }
106
-
107
- /**
108
- * Get migration status by ID
109
- * @param {string} migrationId - Migration ID
110
- * @param {string} stage - Deployment stage
111
- * @returns {Promise<Object>} Migration status
112
- * @throws {Error} If migration not found
113
- */
114
- async get(migrationId, stage) {
115
- const key = this._buildS3Key(migrationId, stage);
116
-
117
- try {
118
- const response = await this.s3Client.send(
119
- new GetObjectCommand({
120
- Bucket: this.bucketName,
121
- Key: key,
122
- })
123
- );
124
-
125
- const body = await response.Body.transformToString();
126
- return JSON.parse(body);
127
- } catch (error) {
128
- if (error.name === 'NoSuchKey') {
129
- throw new Error(`Migration not found: ${migrationId}`);
130
- }
131
- throw error;
132
- }
133
- }
134
- }
135
-
136
- module.exports = { MigrationStatusRepositoryS3 };
137
-
@@ -1 +0,0 @@
1
- export * from "./library"