@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
package/core/CLAUDE.md CHANGED
@@ -4,12 +4,12 @@ This file provides guidance to Claude Code when working with the Frigg Framework
4
4
 
5
5
  ## Critical Context (Read First)
6
6
 
7
- - **Package Purpose**: Core runtime system and foundational classes for Frigg Lambda execution
8
- - **Main Components**: Handler factory, Worker base class, Delegate pattern, Module loading
9
- - **Core Architecture**: Lambda-optimized runtime with connection pooling, error handling, secrets management
10
- - **Key Integration**: AWS Lambda, SQS job processing, MongoDB connections, AWS Secrets Manager
11
- - **Security Model**: Automatic secrets injection, database connection management, user-facing error sanitization
12
- - **DO NOT**: Expose internal errors to users, bypass connection pooling, skip database initialization
7
+ - **Package Purpose**: Core runtime system and foundational classes for Frigg Lambda execution
8
+ - **Main Components**: Handler factory, Worker base class, Delegate pattern, Module loading
9
+ - **Core Architecture**: Lambda-optimized runtime with connection pooling, error handling, secrets management
10
+ - **Key Integration**: AWS Lambda, SQS job processing, MongoDB connections, AWS Secrets Manager
11
+ - **Security Model**: Automatic secrets injection, database connection management, user-facing error sanitization
12
+ - **DO NOT**: Expose internal errors to users, bypass connection pooling, skip database initialization
13
13
 
14
14
  ## Core Components Architecture
15
15
 
@@ -18,45 +18,50 @@ This file provides guidance to Claude Code when working with the Frigg Framework
18
18
  **Purpose**: Factory for creating Lambda handlers with consistent infrastructure setup
19
19
 
20
20
  **Key Features**:
21
- - **Database Connection Management**: Automatic MongoDB connection with pooling
22
- - **Secrets Management**: AWS Secrets Manager integration via `SECRET_ARN` env var
23
- - **Error Sanitization**: Prevents internal details from leaking to end users
24
- - **Debug Logging**: Request/response logging with structured debug info
25
- - **Connection Optimization**: `context.callbackWaitsForEmptyEventLoop = false` for reuse
21
+
22
+ - **Database Connection Management**: Automatic MongoDB connection with pooling
23
+ - **Secrets Management**: AWS Secrets Manager integration via `SECRET_ARN` env var
24
+ - **Error Sanitization**: Prevents internal details from leaking to end users
25
+ - **Debug Logging**: Request/response logging with structured debug info
26
+ - **Connection Optimization**: `context.callbackWaitsForEmptyEventLoop = false` for reuse
26
27
 
27
28
  **Handler Configuration Options**:
29
+
28
30
  ```javascript
29
31
  const handler = createHandler({
30
- eventName: 'MyIntegration', // For logging/debugging
31
- isUserFacingResponse: true, // true = sanitize errors, false = pass through
32
+ eventName: 'MyIntegration', // For logging/debugging
33
+ isUserFacingResponse: true, // true = sanitize errors, false = pass through
32
34
  method: async (event, context) => {}, // Your Lambda function logic
33
- shouldUseDatabase: true // false = skip MongoDB connection
35
+ shouldUseDatabase: true, // false = skip MongoDB connection
34
36
  });
35
37
  ```
36
38
 
37
39
  **Error Handling Patterns**:
38
- - **User-Facing**: Returns 500 with generic "Internal Error Occurred" message
39
- - **Server-to-Server**: Re-throws errors for AWS to handle
40
- - **Halt Errors**: `error.isHaltError = true` logs but returns success (no retry)
40
+
41
+ - **User-Facing**: Returns 500 with generic "Internal Error Occurred" message
42
+ - **Server-to-Server**: Re-throws errors for AWS to handle
43
+ - **Halt Errors**: `error.isHaltError = true` logs but returns success (no retry)
41
44
 
42
45
  ### Worker Base Class (`Worker.js:9-83`)
43
46
 
44
47
  **Purpose**: Base class for SQS job processing with standardized patterns
45
48
 
46
49
  **Core Responsibilities**:
47
- - **Queue Management**: Get SQS queue URLs and send messages
48
- - **Batch Processing**: Process multiple SQS records in sequence
49
- - **Message Validation**: Extensible parameter validation system
50
- - **Error Handling**: Structured error handling for async job processing
50
+
51
+ - **Queue Management**: Get SQS queue URLs and send messages
52
+ - **Batch Processing**: Process multiple SQS records in sequence
53
+ - **Message Validation**: Extensible parameter validation system
54
+ - **Error Handling**: Structured error handling for async job processing
51
55
 
52
56
  **Usage Pattern**:
57
+
53
58
  ```javascript
54
59
  class MyWorker extends Worker {
55
60
  async _run(params, context = {}) {
56
61
  // Your job processing logic here
57
62
  // params are already JSON.parsed from SQS message body
58
63
  }
59
-
64
+
60
65
  _validateParams(params) {
61
66
  // Validate required parameters
62
67
  this._verifyParamExists(params, 'requiredField');
@@ -69,13 +74,17 @@ await worker.run(event, context); // Process SQS Records
69
74
  ```
70
75
 
71
76
  **Message Sending**:
77
+
72
78
  ```javascript
73
- await worker.send({
74
- QueueUrl: 'https://sqs.region.amazonaws.com/account/queue',
75
- jobType: 'processAttachment',
76
- integrationId: 'abc123',
77
- // ... other job parameters
78
- }, delaySeconds);
79
+ await worker.send(
80
+ {
81
+ QueueUrl: 'https://sqs.region.amazonaws.com/account/queue',
82
+ jobType: 'processAttachment',
83
+ integrationId: 'abc123',
84
+ // ... other job parameters
85
+ },
86
+ delaySeconds
87
+ );
79
88
  ```
80
89
 
81
90
  ### Delegate Pattern System (`Delegate.js:3-27`)
@@ -83,29 +92,35 @@ await worker.send({
83
92
  **Purpose**: Observer/delegation pattern for decoupled component communication
84
93
 
85
94
  **Core Concepts**:
86
- - **Notification System**: Components notify delegates of events/state changes
87
- - **Type Safety**: `delegateTypes` array defines valid notification strings
88
- - **Bidirectional**: Supports both sending and receiving notifications
89
- - **Null Safety**: Gracefully handles missing delegates
95
+
96
+ - **Notification System**: Components notify delegates of events/state changes
97
+ - **Type Safety**: `delegateTypes` array defines valid notification strings
98
+ - **Bidirectional**: Supports both sending and receiving notifications
99
+ - **Null Safety**: Gracefully handles missing delegates
90
100
 
91
101
  **Implementation Pattern**:
102
+
92
103
  ```javascript
93
104
  class MyIntegration extends Delegate {
94
105
  constructor(params) {
95
106
  super(params);
96
- this.delegateTypes = ['processComplete', 'errorOccurred', 'statusUpdate'];
107
+ this.delegateTypes = [
108
+ 'processComplete',
109
+ 'errorOccurred',
110
+ 'statusUpdate',
111
+ ];
97
112
  }
98
-
113
+
99
114
  async processData(data) {
100
115
  // Do work
101
116
  await this.notify('statusUpdate', { progress: 50 });
102
117
  // More work
103
118
  await this.notify('processComplete', { result: data });
104
119
  }
105
-
120
+
106
121
  async receiveNotification(notifier, delegateString, object) {
107
122
  // Handle notifications from other components
108
- switch(delegateString) {
123
+ switch (delegateString) {
109
124
  case 'dataReady':
110
125
  await this.processData(object);
111
126
  break;
@@ -119,55 +134,63 @@ class MyIntegration extends Delegate {
119
134
  **Purpose**: Dynamic loading and registration of integration modules
120
135
 
121
136
  **Key Features**:
122
- - **Package Discovery**: Automatically find `@friggframework/api-module-*` packages
123
- - **Module Registration**: Load and register integration classes
124
- - **Configuration Management**: Handle module-specific configuration
125
- - **Dependency Resolution**: Manage inter-module dependencies
137
+
138
+ - **Package Discovery**: Automatically find `@friggframework/api-module-*` packages
139
+ - **Module Registration**: Load and register integration classes
140
+ - **Configuration Management**: Handle module-specific configuration
141
+ - **Dependency Resolution**: Manage inter-module dependencies
126
142
 
127
143
  ## Runtime Lifecycle & Patterns
128
144
 
129
145
  ### Lambda Handler Lifecycle
146
+
130
147
  1. **Pre-Execution Setup**:
131
- ```javascript
132
- initDebugLog(eventName, event); // Debug logging setup
133
- await secretsToEnv(); // Secrets Manager injection
134
- context.callbackWaitsForEmptyEventLoop = false; // Connection pooling
135
- ```
148
+
149
+ ```javascript
150
+ initDebugLog(eventName, event); // Debug logging setup
151
+ await secretsToEnv(); // Secrets Manager injection
152
+ context.callbackWaitsForEmptyEventLoop = false; // Connection pooling
153
+ ```
136
154
 
137
155
  2. **Database Connection**:
138
- ```javascript
139
- if (shouldUseDatabase) {
140
- await connectToDatabase(); // MongoDB connection with pooling
141
- }
142
- ```
156
+
157
+ ```javascript
158
+ if (shouldUseDatabase) {
159
+ await connectToDatabase(); // MongoDB connection with pooling
160
+ }
161
+ ```
143
162
 
144
163
  3. **Method Execution**:
145
- ```javascript
146
- return await method(event, context); // Your integration logic
147
- ```
164
+
165
+ ```javascript
166
+ return await method(event, context); // Your integration logic
167
+ ```
148
168
 
149
169
  4. **Error Handling & Cleanup**:
150
- ```javascript
151
- flushDebugLog(error); // Debug info flush on error
152
- // Sanitized error response for user-facing endpoints
153
- ```
170
+ ```javascript
171
+ flushDebugLog(error); // Debug info flush on error
172
+ // Sanitized error response for user-facing endpoints
173
+ ```
154
174
 
155
175
  ### SQS Job Processing Lifecycle
176
+
156
177
  1. **Batch Processing**: Process all records in `event.Records` sequentially
157
- 2. **Message Parsing**: JSON.parse message body for parameters
178
+ 2. **Message Parsing**: JSON.parse message body for parameters
158
179
  3. **Validation**: Run custom validation on parsed parameters
159
180
  4. **Execution**: Call `_run()` method with validated parameters
160
181
  5. **Error Propagation**: Let AWS handle retries/DLQ for failed jobs
161
182
 
162
183
  ### Secrets Management Integration
163
- - **Automatic Injection**: If `SECRET_ARN` environment variable is set
164
- - **Environment Variables**: Secrets automatically set as `process.env` variables
165
- - **Security**: No secrets logging or exposure in error messages
166
- - **Caching**: Secrets cached for Lambda container lifetime
184
+
185
+ - **Automatic Injection**: If `SECRET_ARN` environment variable is set
186
+ - **Environment Variables**: Secrets automatically set as `process.env` variables
187
+ - **Security**: No secrets logging or exposure in error messages
188
+ - **Caching**: Secrets cached for Lambda container lifetime
167
189
 
168
190
  ## Database Connection Patterns
169
191
 
170
192
  ### Connection Pooling Strategy
193
+
171
194
  ```javascript
172
195
  // Mongoose connection reuse across Lambda invocations
173
196
  context.callbackWaitsForEmptyEventLoop = false;
@@ -175,45 +198,51 @@ await connectToDatabase(); // Reuses existing connection if available
175
198
  ```
176
199
 
177
200
  ### Database Usage Patterns
201
+
178
202
  ```javascript
179
203
  // Conditional database connection
180
204
  const handler = createHandler({
181
- shouldUseDatabase: false, // Skip for database-free operations
205
+ shouldUseDatabase: false, // Skip for database-free operations
182
206
  method: async (event) => {
183
207
  // No DB operations needed
184
208
  return { statusCode: 200, body: 'OK' };
185
- }
209
+ },
186
210
  });
187
211
  ```
188
212
 
189
213
  ## Error Handling Architecture
190
214
 
191
215
  ### Error Classification
216
+
192
217
  1. **User-Facing Errors**: `isUserFacingResponse: true`
193
- - Returns generic 500 error message
194
- - Prevents information disclosure
195
- - Logs full error details internally
218
+
219
+ - Returns generic 500 error message
220
+ - Prevents information disclosure
221
+ - Logs full error details internally
196
222
 
197
223
  2. **Server-to-Server Errors**: `isUserFacingResponse: false`
198
- - Re-throws original error for AWS handling
199
- - Used for SQS, SNS, and internal API calls
200
- - Enables proper retry mechanisms
224
+
225
+ - Re-throws original error for AWS handling
226
+ - Used for SQS, SNS, and internal API calls
227
+ - Enables proper retry mechanisms
201
228
 
202
229
  3. **Halt Errors**: `error.isHaltError = true`
203
- - Logs error but returns success
204
- - Prevents infinite retries for known issues
205
- - Used for graceful degradation scenarios
230
+ - Logs error but returns success
231
+ - Prevents infinite retries for known issues
232
+ - Used for graceful degradation scenarios
206
233
 
207
234
  ### Debug Logging Strategy
235
+
208
236
  ```javascript
209
- initDebugLog(eventName, event); // Start logging context
237
+ initDebugLog(eventName, event); // Start logging context
210
238
  // ... your code ...
211
- flushDebugLog(error); // Flush on error (includes full context)
239
+ flushDebugLog(error); // Flush on error (includes full context)
212
240
  ```
213
241
 
214
242
  ## Integration Development Patterns
215
243
 
216
244
  ### Extending Worker for Job Processing
245
+
217
246
  ```javascript
218
247
  class AttachmentWorker extends Worker {
219
248
  _validateParams(params) {
@@ -221,7 +250,7 @@ class AttachmentWorker extends Worker {
221
250
  this._verifyParamExists(params, 'attachmentUrl');
222
251
  this._verifyParamExists(params, 'destination');
223
252
  }
224
-
253
+
225
254
  async _run(params, context) {
226
255
  const { integrationId, attachmentUrl, destination } = params;
227
256
  // Process attachment upload/download
@@ -232,37 +261,39 @@ class AttachmentWorker extends Worker {
232
261
  ```
233
262
 
234
263
  ### Creating Custom Handlers
264
+
235
265
  ```javascript
236
266
  const myIntegrationHandler = createHandler({
237
267
  eventName: 'MyIntegration',
238
- isUserFacingResponse: true, // Sanitize errors for users
239
- shouldUseDatabase: true, // Need database access
268
+ isUserFacingResponse: true, // Sanitize errors for users
269
+ shouldUseDatabase: true, // Need database access
240
270
  method: async (event, context) => {
241
271
  // Your integration logic here
242
272
  // Database is already connected
243
273
  // Secrets are in process.env
244
-
274
+
245
275
  return {
246
276
  statusCode: 200,
247
- body: JSON.stringify({ success: true })
277
+ body: JSON.stringify({ success: true }),
248
278
  };
249
- }
279
+ },
250
280
  });
251
281
  ```
252
282
 
253
283
  ### Delegate Pattern for Integration Communication
284
+
254
285
  ```javascript
255
286
  class IntegrationManager extends Delegate {
256
287
  constructor() {
257
288
  super();
258
289
  this.delegateTypes = [
259
290
  'authenticationComplete',
260
- 'syncStarted',
291
+ 'syncStarted',
261
292
  'syncComplete',
262
- 'errorOccurred'
293
+ 'errorOccurred',
263
294
  ];
264
295
  }
265
-
296
+
266
297
  async startSync(integrationId) {
267
298
  await this.notify('syncStarted', { integrationId });
268
299
  // ... sync logic ...
@@ -274,21 +305,24 @@ class IntegrationManager extends Delegate {
274
305
  ## Performance Optimization Patterns
275
306
 
276
307
  ### Connection Reuse
308
+
277
309
  ```javascript
278
310
  // ALWAYS set this in handlers for performance
279
311
  context.callbackWaitsForEmptyEventLoop = false;
280
312
  ```
281
313
 
282
314
  ### Conditional Database Usage
315
+
283
316
  ```javascript
284
317
  // Skip database for lightweight operations
285
318
  const handler = createHandler({
286
- shouldUseDatabase: false, // Faster cold starts
287
- method: healthCheckMethod
319
+ shouldUseDatabase: false, // Faster cold starts
320
+ method: healthCheckMethod,
288
321
  });
289
322
  ```
290
323
 
291
324
  ### SQS Batch Processing Optimization
325
+
292
326
  ```javascript
293
327
  // Process records sequentially (not parallel) for resource control
294
328
  for (const record of records) {
@@ -305,6 +339,7 @@ The Frigg Framework follows DDD/Hexagonal Architecture with clear separation bet
305
339
  **Purpose**: Abstract database and external system access into dedicated repository classes.
306
340
 
307
341
  **Structure**:
342
+
308
343
  ```javascript
309
344
  // Example: packages/core/database/websocket-connection-repository.js
310
345
  class WebsocketConnectionRepository {
@@ -335,25 +370,28 @@ class WebsocketConnectionRepository {
335
370
  ```
336
371
 
337
372
  **Repository Responsibilities**:
338
- - ✅ **CRUD operations** - Create, Read, Update, Delete database records
339
- - ✅ **Query execution** - Run database queries and return results
340
- - ✅ **Data access only** - No interpretation or decision-making
341
- - ✅ **Atomic operations** - Each method performs one database operation
342
- - ❌ **NO business logic** - Don't decide what data means or what to do with it
343
- - ❌ **NO orchestration** - Don't coordinate multiple operations
373
+
374
+ - ✅ **CRUD operations** - Create, Read, Update, Delete database records
375
+ - ✅ **Query execution** - Run database queries and return results
376
+ - ✅ **Data access only** - No interpretation or decision-making
377
+ - ✅ **Atomic operations** - Each method performs one database operation
378
+ - ❌ **NO business logic** - Don't decide what data means or what to do with it
379
+ - ❌ **NO orchestration** - Don't coordinate multiple operations
344
380
 
345
381
  **Real Repository Examples**:
346
- - `WebsocketConnectionRepository` - WebSocket persistence (packages/core/database/websocket-connection-repository.js)
347
- - `SyncRepository` - Sync object management (packages/core/syncs/sync-repository.js)
348
- - `IntegrationMappingRepository` - Integration mappings (packages/core/integrations/integration-mapping-repository.js)
349
- - `TokenRepository` - Token operations (packages/core/database/token-repository.js)
350
- - `HealthCheckRepository` - Health check data access (packages/core/database/health-check-repository.js)
382
+
383
+ - `WebsocketConnectionRepository` - WebSocket persistence (packages/core/database/websocket-connection-repository.js)
384
+ - `SyncRepository` - Sync object management (packages/core/syncs/sync-repository.js)
385
+ - `IntegrationMappingRepository` - Integration mappings (packages/core/integrations/integration-mapping-repository.js)
386
+ - `TokenRepository` - Token operations (packages/core/database/token-repository.js)
387
+ - `HealthCheckRepository` - Health check data access (packages/core/database/health-check-repository.js)
351
388
 
352
389
  ### Use Case Pattern in Core
353
390
 
354
391
  **Purpose**: Contain business logic, orchestration, and workflow coordination.
355
392
 
356
393
  **Structure**:
394
+
357
395
  ```javascript
358
396
  // Example: packages/core/database/use-cases/check-database-health-use-case.js
359
397
  class CheckDatabaseHealthUseCase {
@@ -364,7 +402,8 @@ class CheckDatabaseHealthUseCase {
364
402
 
365
403
  async execute() {
366
404
  // 1. Get raw data from repository
367
- const { stateName, isConnected } = this.repository.getDatabaseConnectionState();
405
+ const { stateName, isConnected } =
406
+ this.repository.getDatabaseConnectionState();
368
407
 
369
408
  // 2. Apply business logic - determine health status
370
409
  const result = {
@@ -383,38 +422,43 @@ class CheckDatabaseHealthUseCase {
383
422
  ```
384
423
 
385
424
  **Use Case Responsibilities**:
386
- - ✅ **Business logic** - Make decisions based on data
387
- - ✅ **Orchestration** - Coordinate multiple repository calls
388
- - ✅ **Validation** - Enforce business rules
389
- - ✅ **Workflow** - Determine what happens next
390
- - ✅ **Error handling** - Handle domain-specific errors
391
- - ❌ **NO direct database access** - Always use repositories
392
- - ❌ **NO HTTP concerns** - Don't know about status codes or headers
425
+
426
+ - ✅ **Business logic** - Make decisions based on data
427
+ - ✅ **Orchestration** - Coordinate multiple repository calls
428
+ - ✅ **Validation** - Enforce business rules
429
+ - ✅ **Workflow** - Determine what happens next
430
+ - ✅ **Error handling** - Handle domain-specific errors
431
+ - ❌ **NO direct database access** - Always use repositories
432
+ - ❌ **NO HTTP concerns** - Don't know about status codes or headers
393
433
 
394
434
  **Real Use Case Examples**:
395
- - `CheckDatabaseHealthUseCase` - Database health business logic (packages/core/database/use-cases/check-database-health-use-case.js)
396
- - `TestEncryptionUseCase` - Encryption testing workflow (packages/core/database/use-cases/test-encryption-use-case.js)
435
+
436
+ - `CheckDatabaseHealthUseCase` - Database health business logic (packages/core/database/use-cases/check-database-health-use-case.js)
437
+ - `TestEncryptionUseCase` - Encryption testing workflow (packages/core/database/use-cases/test-encryption-use-case.js)
397
438
 
398
439
  ### Handler Pattern in Core
399
440
 
400
441
  **Purpose**: Translate Lambda/HTTP/SQS events into use case calls.
401
442
 
402
443
  **Handler Should ONLY**:
403
- - Define routes and event handlers
404
- - Call use cases (NOT repositories)
405
- - Map use case results to HTTP/Lambda responses
406
- - Handle protocol-specific concerns (status codes, headers)
444
+
445
+ - Define routes and event handlers
446
+ - Call use cases (NOT repositories)
447
+ - Map use case results to HTTP/Lambda responses
448
+ - Handle protocol-specific concerns (status codes, headers)
407
449
 
408
450
  **❌ WRONG - Handler contains business logic**:
451
+
409
452
  ```javascript
410
453
  // BAD: Business logic in handler
411
454
  router.get('/health', async (req, res) => {
412
455
  const state = mongoose.connection.readyState;
413
- const isHealthy = state === 1; // ❌ Business logic in handler
456
+ const isHealthy = state === 1; // ❌ Business logic in handler
414
457
 
415
- if (isHealthy) { // ❌ Orchestration in handler
458
+ if (isHealthy) {
459
+ // ❌ Orchestration in handler
416
460
  const pingStart = Date.now();
417
- await mongoose.connection.db.admin().ping(); // ❌ Direct DB access
461
+ await mongoose.connection.db.admin().ping(); // ❌ Direct DB access
418
462
  const responseTime = Date.now() - pingStart;
419
463
  res.json({ status: 'healthy', responseTime });
420
464
  }
@@ -422,11 +466,12 @@ router.get('/health', async (req, res) => {
422
466
  ```
423
467
 
424
468
  **✅ CORRECT - Handler delegates to use case**:
469
+
425
470
  ```javascript
426
471
  // GOOD: Handler calls use case
427
472
  const healthCheckRepository = new HealthCheckRepository();
428
473
  const checkDatabaseHealthUseCase = new CheckDatabaseHealthUseCase({
429
- healthCheckRepository
474
+ healthCheckRepository,
430
475
  });
431
476
 
432
477
  router.get('/health', async (req, res) => {
@@ -442,9 +487,11 @@ router.get('/health', async (req, res) => {
442
487
  ### Dependency Direction
443
488
 
444
489
  **The Golden Rule**:
490
+
445
491
  > "Handlers ONLY call Use Cases, NEVER Repositories or Business Logic directly"
446
492
 
447
493
  **Correct Flow**:
494
+
448
495
  ```
449
496
  Handler/Router (createHandler)
450
497
  ↓ calls
@@ -456,28 +503,32 @@ Database/External System
456
503
  ```
457
504
 
458
505
  **Why This Matters**:
459
- - **Testability**: Use cases can be tested with mocked repositories
460
- - **Reusability**: Use cases can be called from handlers, CLI, background jobs
461
- - **Maintainability**: Business logic is centralized, not scattered across handlers
462
- - **Flexibility**: Swap repository implementations without changing use cases
506
+
507
+ - **Testability**: Use cases can be tested with mocked repositories
508
+ - **Reusability**: Use cases can be called from handlers, CLI, background jobs
509
+ - **Maintainability**: Business logic is centralized, not scattered across handlers
510
+ - **Flexibility**: Swap repository implementations without changing use cases
463
511
 
464
512
  ### Migration from Old Patterns
465
513
 
466
514
  **Old Pattern (Mongoose models everywhere)**:
515
+
467
516
  ```javascript
468
517
  // BAD: Direct model access in handlers
469
518
  const handler = createHandler({
470
519
  method: async (event) => {
471
- const user = await User.findById(event.userId); // ❌ Direct model access
472
- if (!user.isActive) { // ❌ Business logic in handler
520
+ const user = await User.findById(event.userId); // ❌ Direct model access
521
+ if (!user.isActive) {
522
+ // ❌ Business logic in handler
473
523
  throw new Error('User not active');
474
524
  }
475
- await Sync.create({ userId: user.id }); // ❌ Direct model access
476
- }
525
+ await Sync.create({ userId: user.id }); // ❌ Direct model access
526
+ },
477
527
  });
478
528
  ```
479
529
 
480
530
  **New Pattern (Repository + Use Case)**:
531
+
481
532
  ```javascript
482
533
  // GOOD: Repository abstracts data access
483
534
  class UserRepository {
@@ -502,7 +553,8 @@ class ActivateUserSyncUseCase {
502
553
  async execute(userId) {
503
554
  const user = await this.userRepo.findById(userId);
504
555
 
505
- if (!user.isActive) { // ✅ Business logic in use case
556
+ if (!user.isActive) {
557
+ // ✅ Business logic in use case
506
558
  throw new Error('User not active');
507
559
  }
508
560
 
@@ -515,10 +567,10 @@ const handler = createHandler({
515
567
  method: async (event) => {
516
568
  const useCase = new ActivateUserSyncUseCase({
517
569
  userRepository: new UserRepository(),
518
- syncRepository: new SyncRepository()
570
+ syncRepository: new SyncRepository(),
519
571
  });
520
572
  return await useCase.execute(event.userId);
521
- }
573
+ },
522
574
  });
523
575
  ```
524
576
 
@@ -533,7 +585,7 @@ class ProcessAttachmentWorker extends Worker {
533
585
  // Inject repositories into use case
534
586
  this.useCase = new ProcessAttachmentUseCase({
535
587
  asanaRepository: new AsanaRepository(),
536
- frontifyRepository: new FrontifyRepository()
588
+ frontifyRepository: new FrontifyRepository(),
537
589
  });
538
590
  }
539
591
 
@@ -551,20 +603,23 @@ class ProcessAttachmentWorker extends Worker {
551
603
  ### When to Extract to Repository/Use Case
552
604
 
553
605
  **Extract to Repository when you see**:
554
- - Direct Mongoose model calls (`User.findById()`, `Sync.create()`)
555
- - Database queries in handlers or business logic
556
- - External API calls scattered across codebase
557
- - File system or AWS SDK operations in handlers
606
+
607
+ - Direct Mongoose model calls (`User.findById()`, `Sync.create()`)
608
+ - Database queries in handlers or business logic
609
+ - External API calls scattered across codebase
610
+ - File system or AWS SDK operations in handlers
558
611
 
559
612
  **Extract to Use Case when you see**:
560
- - Business logic in handlers (if/else based on data)
561
- - Orchestration of multiple operations
562
- - Validation and error handling logic
563
- - Workflow coordination
613
+
614
+ - Business logic in handlers (if/else based on data)
615
+ - Orchestration of multiple operations
616
+ - Validation and error handling logic
617
+ - Workflow coordination
564
618
 
565
619
  ### Testing with Repository/Use Case Pattern
566
620
 
567
621
  **Repository Tests** (Integration tests with real DB):
622
+
568
623
  ```javascript
569
624
  describe('WebsocketConnectionRepository', () => {
570
625
  it('creates connection record', async () => {
@@ -576,17 +631,18 @@ describe('WebsocketConnectionRepository', () => {
576
631
  ```
577
632
 
578
633
  **Use Case Tests** (Unit tests with mocked repositories):
634
+
579
635
  ```javascript
580
636
  describe('CheckDatabaseHealthUseCase', () => {
581
637
  it('returns unhealthy when disconnected', async () => {
582
638
  const mockRepo = {
583
639
  getDatabaseConnectionState: () => ({
584
640
  stateName: 'disconnected',
585
- isConnected: false
586
- })
641
+ isConnected: false,
642
+ }),
587
643
  };
588
644
  const useCase = new CheckDatabaseHealthUseCase({
589
- healthCheckRepository: mockRepo
645
+ healthCheckRepository: mockRepo,
590
646
  });
591
647
  const result = await useCase.execute();
592
648
  expect(result.status).toBe('unhealthy');
@@ -595,12 +651,13 @@ describe('CheckDatabaseHealthUseCase', () => {
595
651
  ```
596
652
 
597
653
  **Handler Tests** (HTTP/Lambda response tests):
654
+
598
655
  ```javascript
599
656
  describe('Health Handler', () => {
600
657
  it('returns 503 when unhealthy', async () => {
601
658
  // Mock use case
602
659
  const mockUseCase = {
603
- execute: async () => ({ status: 'unhealthy' })
660
+ execute: async () => ({ status: 'unhealthy' }),
604
661
  };
605
662
  // Test HTTP response
606
663
  const response = await handler(mockEvent, mockContext);
@@ -612,6 +669,7 @@ describe('Health Handler', () => {
612
669
  ## Anti-Patterns to Avoid
613
670
 
614
671
  ### Core Runtime Anti-Patterns
672
+
615
673
  ❌ **Don't expose internal errors** to user-facing endpoints - use `isUserFacingResponse: true`
616
674
  ❌ **Don't skip connection optimization** - always set `callbackWaitsForEmptyEventLoop = false`
617
675
  ❌ **Don't parallel process SQS records** - sequential processing prevents resource exhaustion
@@ -621,6 +679,7 @@ describe('Health Handler', () => {
621
679
  ❌ **Don't ignore delegate types** - define valid `delegateTypes` array for type safety
622
680
 
623
681
  ### DDD/Hexagonal Architecture Anti-Patterns
682
+
624
683
  ❌ **Don't access models directly in handlers** - create repositories to abstract data access
625
684
  ❌ **Don't put business logic in handlers** - extract to use cases
626
685
  ❌ **Don't call repositories from handlers** - always go through use cases
@@ -632,13 +691,14 @@ describe('Health Handler', () => {
632
691
  ## Testing Patterns
633
692
 
634
693
  ### Handler Testing
694
+
635
695
  ```javascript
636
696
  const { createHandler } = require('@friggframework/core/core');
637
697
 
638
698
  const testHandler = createHandler({
639
699
  isUserFacingResponse: false, // Get full errors in tests
640
- shouldUseDatabase: false, // Mock/skip DB in tests
641
- method: yourTestMethod
700
+ shouldUseDatabase: false, // Mock/skip DB in tests
701
+ method: yourTestMethod,
642
702
  });
643
703
 
644
704
  // Test with mock event/context
@@ -646,12 +706,13 @@ const result = await testHandler(mockEvent, mockContext);
646
706
  ```
647
707
 
648
708
  ### Worker Testing
709
+
649
710
  ```javascript
650
711
  class TestWorker extends Worker {
651
712
  _validateParams(params) {
652
713
  this._verifyParamExists(params, 'testField');
653
714
  }
654
-
715
+
655
716
  async _run(params, context) {
656
717
  // Your test logic
657
718
  return { processed: true };
@@ -661,30 +722,35 @@ class TestWorker extends Worker {
661
722
  // Test SQS record processing
662
723
  const worker = new TestWorker();
663
724
  await worker.run({
664
- Records: [{
665
- body: JSON.stringify({ testField: 'value' })
666
- }]
725
+ Records: [
726
+ {
727
+ body: JSON.stringify({ testField: 'value' }),
728
+ },
729
+ ],
667
730
  });
668
731
  ```
669
732
 
670
733
  ## Environment Variables
671
734
 
672
735
  ### Required Variables
673
- - `AWS_REGION`: AWS region for SQS operations
674
- - `SECRET_ARN`: (Optional) AWS Secrets Manager secret ARN for automatic injection
736
+
737
+ - `AWS_REGION`: AWS region for SQS operations
738
+ - `SECRET_ARN`: (Optional) AWS Secrets Manager secret ARN for automatic injection
675
739
 
676
740
  ### Database Variables
677
- - MongoDB connection variables (handled by `../database/mongo`)
678
- - See database module documentation for complete list
741
+
742
+ - MongoDB connection variables (handled by `../database/mongo`)
743
+ - See database module documentation for complete list
679
744
 
680
745
  ### Queue Variables
681
- - Queue URLs typically passed as parameters, not environment variables
682
- - Use Worker's `getQueueURL()` method for dynamic queue discovery
746
+
747
+ - Queue URLs typically passed as parameters, not environment variables
748
+ - Use Worker's `getQueueURL()` method for dynamic queue discovery
683
749
 
684
750
  ## Security Considerations
685
751
 
686
- - **Secrets**: Never log or expose secrets in error messages
687
- - **Error Messages**: Always sanitize errors for user-facing responses
688
- - **Database**: Connection pooling reuses connections securely
689
- - **SQS**: Message validation prevents injection attacks
690
- - **Logging**: Debug logs include sensitive data - handle carefully in production
752
+ - **Secrets**: Never log or expose secrets in error messages
753
+ - **Error Messages**: Always sanitize errors for user-facing responses
754
+ - **Database**: Connection pooling reuses connections securely
755
+ - **SQS**: Message validation prevents injection attacks
756
+ - **Logging**: Debug logs include sensitive data - handle carefully in production