@friggframework/core 2.0.0--canary.548.c8ae0ca.0 → 2.0.0--canary.545.dba001a.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 (235) 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 +450 -0
  95. package/handlers/routers/auth.js +23 -2
  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 +39 -249
  100. package/handlers/routers/integration-defined-routers.js +4 -4
  101. package/handlers/routers/integration-webhook-routers.js +17 -7
  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/websocket.js +9 -3
  106. package/handlers/use-cases/check-integrations-health-use-case.js +13 -6
  107. package/handlers/workers/db-migration.js +74 -35
  108. package/index.js +50 -16
  109. package/infrastructure/scheduler/index.js +15 -11
  110. package/infrastructure/scheduler/netlify-scheduler-adapter.js +185 -0
  111. package/infrastructure/scheduler/scheduler-service-factory.js +38 -31
  112. package/integrations/WEBHOOK-QUICKSTART.md +21 -14
  113. package/integrations/index.js +4 -0
  114. package/integrations/integration-base.js +9 -6
  115. package/integrations/integration-router.js +1828 -157
  116. package/integrations/options.js +47 -20
  117. package/integrations/repositories/integration-mapping-repository-factory.js +4 -8
  118. package/integrations/repositories/integration-repository-documentdb.js +53 -24
  119. package/integrations/repositories/integration-repository-factory.js +6 -6
  120. package/integrations/repositories/integration-repository-interface.js +27 -9
  121. package/integrations/repositories/integration-repository-postgres.js +3 -3
  122. package/integrations/repositories/process-repository-factory.js +4 -7
  123. package/integrations/repositories/process-repository-interface.js +9 -5
  124. package/integrations/repositories/process-repository-mongo.js +7 -3
  125. package/integrations/repositories/process-repository-postgres.js +2 -2
  126. package/integrations/tests/doubles/config-capturing-integration.js +9 -8
  127. package/integrations/tests/doubles/dummy-integration-class.js +81 -8
  128. package/integrations/tests/doubles/test-integration-repository.js +51 -11
  129. package/integrations/use-cases/create-integration.js +36 -32
  130. package/integrations/use-cases/create-process.js +20 -9
  131. package/integrations/use-cases/delete-integration-for-user.js +5 -4
  132. package/integrations/use-cases/execute-proxy-request.js +564 -0
  133. package/integrations/use-cases/get-integration-for-user.js +25 -10
  134. package/integrations/use-cases/get-integration-instance-by-definition.js +13 -7
  135. package/integrations/use-cases/get-integrations-for-user.js +4 -1
  136. package/integrations/use-cases/get-possible-integrations.js +1 -1
  137. package/integrations/use-cases/get-process.js +5 -6
  138. package/integrations/use-cases/index.js +3 -1
  139. package/integrations/use-cases/load-integration-context.js +1 -5
  140. package/integrations/use-cases/update-process-state.js +10 -8
  141. package/integrations/utils/map-integration-dto.js +9 -3
  142. package/jest-setup.js +1 -1
  143. package/jest-teardown.js +1 -1
  144. package/lambda/TimeoutCatcher.js +32 -30
  145. package/lambda/index.js +1 -1
  146. package/logs/index.js +3 -3
  147. package/modules/domain/entities/AuthorizationSession.js +131 -0
  148. package/modules/domain/entities/index.js +10 -0
  149. package/modules/module.js +4 -9
  150. package/modules/repositories/authorization-session-repository-factory.js +53 -0
  151. package/modules/repositories/authorization-session-repository-interface.js +75 -0
  152. package/modules/repositories/authorization-session-repository-mongo.js +169 -0
  153. package/modules/repositories/authorization-session-repository-postgres.js +170 -0
  154. package/modules/repositories/module-repository-documentdb.js +89 -41
  155. package/modules/repositories/module-repository-factory.js +4 -6
  156. package/modules/repositories/module-repository-interface.js +13 -0
  157. package/modules/repositories/module-repository-mongo.js +40 -5
  158. package/modules/repositories/module-repository-postgres.js +42 -5
  159. package/modules/repositories/module-repository.js +28 -0
  160. package/modules/requester/api-key.js +0 -2
  161. package/modules/requester/basic.js +0 -1
  162. package/modules/requester/oauth-2.js +3 -14
  163. package/modules/test/mock-api/definition.js +15 -10
  164. package/modules/test/mock-api/mocks/hubspot.js +36 -36
  165. package/modules/tests/doubles/test-module-factory.js +10 -4
  166. package/modules/tests/doubles/test-module-repository.js +1 -1
  167. package/modules/use-cases/delete-module-entity.js +23 -0
  168. package/modules/use-cases/get-authorization-requirements.js +110 -0
  169. package/modules/use-cases/get-entities-for-user.js +1 -1
  170. package/modules/use-cases/get-entity-options-by-id.js +8 -6
  171. package/modules/use-cases/get-module-entity-by-id.js +16 -0
  172. package/modules/use-cases/get-module.js +12 -9
  173. package/modules/use-cases/process-authorization-callback.js +12 -6
  174. package/modules/use-cases/process-authorization-step.js +160 -0
  175. package/modules/use-cases/refresh-entity-options.js +8 -6
  176. package/modules/use-cases/start-authorization-session.js +86 -0
  177. package/modules/use-cases/test-module-auth.js +8 -6
  178. package/modules/use-cases/update-module-entity.js +27 -0
  179. package/modules/utils/map-module-dto.js +2 -2
  180. package/openapi/openapi-spec-generator.js +370 -0
  181. package/openapi/openapi-v1.yaml +1360 -0
  182. package/openapi/openapi-v2.yaml +1535 -0
  183. package/package.json +7 -9
  184. package/prisma-mongodb/schema.prisma +87 -0
  185. package/prisma-postgresql/migrations/20260222000000_add_admin_process_and_script_schedule/migration.sql +55 -0
  186. package/prisma-postgresql/schema.prisma +85 -0
  187. package/providers/index.js +13 -0
  188. package/providers/resolve-provider.js +93 -0
  189. package/queues/index.js +19 -0
  190. package/queues/providers/index.js +7 -0
  191. package/queues/providers/netlify-background-provider.js +76 -0
  192. package/queues/providers/qstash-queue-provider.js +122 -0
  193. package/queues/queue-client-interface.js +55 -0
  194. package/queues/queue-provider-factory.js +93 -0
  195. package/queues/queue-provider.js +46 -0
  196. package/queues/queuer-util.js +33 -25
  197. package/syncs/repositories/sync-repository-documentdb.js +39 -19
  198. package/syncs/repositories/sync-repository-factory.js +4 -4
  199. package/syncs/repositories/sync-repository-interface.js +6 -2
  200. package/syncs/repositories/sync-repository-postgres.js +19 -13
  201. package/syncs/sync.js +104 -100
  202. package/token/repositories/token-repository-factory.js +4 -6
  203. package/types/assertions/index.d.ts +72 -72
  204. package/types/core/index.d.ts +98 -53
  205. package/types/errors/index.d.ts +61 -54
  206. package/types/eslint-config/index.d.ts +40 -40
  207. package/types/lambda/index.d.ts +26 -26
  208. package/types/logs/index.d.ts +4 -4
  209. package/types/prettier-config/index.d.ts +5 -5
  210. package/types/test-environment/index.d.ts +14 -14
  211. package/types/tsconfig.json +95 -93
  212. package/user/repositories/user-repository-factory.js +4 -4
  213. package/user/tests/doubles/test-user-repository.js +5 -2
  214. package/user/use-cases/authenticate-user.js +2 -4
  215. package/user/use-cases/authenticate-with-shared-secret.js +1 -1
  216. package/user/use-cases/create-individual-user.js +8 -7
  217. package/user/use-cases/create-organization-user.js +1 -1
  218. package/user/use-cases/create-token-for-user-id.js +1 -1
  219. package/user/use-cases/delete-user.js +47 -0
  220. package/user/use-cases/get-user-from-adopter-jwt.js +0 -2
  221. package/user/use-cases/get-user-from-bearer-token.js +7 -3
  222. package/user/use-cases/get-user-from-x-frigg-headers.js +14 -11
  223. package/user/use-cases/login-user.js +8 -8
  224. package/user/user.js +18 -4
  225. package/utils/backend-path.js +2 -2
  226. package/utils/index.js +4 -1
  227. package/websocket/repositories/websocket-connection-repository-documentdb.js +46 -21
  228. package/websocket/repositories/websocket-connection-repository-factory.js +4 -8
  229. package/websocket/repositories/websocket-connection-repository-mongo.js +26 -16
  230. package/websocket/repositories/websocket-connection-repository-postgres.js +26 -15
  231. package/websocket/repositories/websocket-connection-repository.js +26 -20
  232. package/websocket/websocket-message-sender-interface.js +38 -0
  233. package/database/adapters/lambda-invoker.js +0 -97
  234. package/database/repositories/migration-status-repository-s3.js +0 -137
  235. package/infrastructure/scheduler/eventbridge-scheduler-adapter.js +0 -184
package/README.md CHANGED
@@ -4,16 +4,16 @@ The `@friggframework/core` package is the foundational layer of the Frigg Framew
4
4
 
5
5
  ## Table of Contents
6
6
 
7
- - [Architecture Overview](#architecture-overview)
8
- - [Installation](#installation)
9
- - [Quick Start](#quick-start)
10
- - [Core Components](#core-components)
11
- - [Hexagonal Architecture](#hexagonal-architecture)
12
- - [Usage Examples](#usage-examples)
13
- - [Testing](#testing)
14
- - [Development](#development)
15
- - [API Reference](#api-reference)
16
- - [Contributing](#contributing)
7
+ - [Architecture Overview](#architecture-overview)
8
+ - [Installation](#installation)
9
+ - [Quick Start](#quick-start)
10
+ - [Core Components](#core-components)
11
+ - [Hexagonal Architecture](#hexagonal-architecture)
12
+ - [Usage Examples](#usage-examples)
13
+ - [Testing](#testing)
14
+ - [Development](#development)
15
+ - [API Reference](#api-reference)
16
+ - [Contributing](#contributing)
17
17
 
18
18
  ## Architecture Overview
19
19
 
@@ -67,11 +67,13 @@ yarn add @friggframework/core
67
67
  `@friggframework/core` supports both MongoDB and PostgreSQL via Prisma ORM. **Prisma is an optional peer dependency** - you only need to install it if you're using database features that require migrations or schema generation.
68
68
 
69
69
  **When you need Prisma:**
70
- - Running database migrations (`prisma migrate`, `prisma db push`)
71
- - Generating Prisma clients for your application
72
- - Using the migration Lambda function (`dbMigrate`)
70
+
71
+ - Running database migrations (`prisma migrate`, `prisma db push`)
72
+ - Generating Prisma clients for your application
73
+ - Using the migration Lambda function (`dbMigrate`)
73
74
 
74
75
  **Installation:**
76
+
75
77
  ```bash
76
78
  # Install Prisma CLI and Client as dev dependencies
77
79
  npm install --save-dev prisma @prisma/client
@@ -81,6 +83,7 @@ yarn add -D prisma @prisma/client
81
83
  ```
82
84
 
83
85
  **Generate Prisma Clients:**
86
+
84
87
  ```bash
85
88
  # From @friggframework/core directory
86
89
  npm run prisma:generate:mongo # MongoDB only
@@ -92,9 +95,9 @@ npm run prisma:generate # Both databases
92
95
 
93
96
  ### Prerequisites
94
97
 
95
- - Node.js 16+
96
- - MongoDB 4.4+ (for data persistence)
97
- - AWS credentials (for SQS, KMS, Lambda deployment)
98
+ - Node.js 16+
99
+ - MongoDB 4.4+ (for data persistence)
100
+ - AWS credentials (for SQS, KMS, Lambda deployment)
98
101
 
99
102
  ### Environment Variables
100
103
 
@@ -120,11 +123,13 @@ LOG_LEVEL=info
120
123
  The heart of the framework - manages integration lifecycle and business logic.
121
124
 
122
125
  **Key Classes:**
123
- - `IntegrationBase` - Base class for all integrations
124
- - `Integration` - Domain aggregate using Proxy pattern
125
- - Use cases: `CreateIntegration`, `UpdateIntegration`, `DeleteIntegration`
126
+
127
+ - `IntegrationBase` - Base class for all integrations
128
+ - `Integration` - Domain aggregate using Proxy pattern
129
+ - Use cases: `CreateIntegration`, `UpdateIntegration`, `DeleteIntegration`
126
130
 
127
131
  **Usage:**
132
+
128
133
  ```javascript
129
134
  const { IntegrationBase } = require('@friggframework/core');
130
135
 
@@ -134,8 +139,8 @@ class SlackHubSpotSync extends IntegrationBase {
134
139
  version: '2.1.0',
135
140
  modules: {
136
141
  slack: 'slack',
137
- hubspot: 'hubspot'
138
- }
142
+ hubspot: 'hubspot',
143
+ },
139
144
  };
140
145
 
141
146
  async onCreate({ integrationId }) {
@@ -152,30 +157,32 @@ class SlackHubSpotSync extends IntegrationBase {
152
157
  MongoDB integration with Mongoose ODM.
153
158
 
154
159
  **Key Components:**
155
- - Connection management
156
- - Pre-built models (User, Integration, Credential, etc.)
157
- - Schema definitions
160
+
161
+ - Connection management
162
+ - Pre-built models (User, Integration, Credential, etc.)
163
+ - Schema definitions
158
164
 
159
165
  **Usage:**
166
+
160
167
  ```javascript
161
- const {
162
- connectToDatabase,
163
- IntegrationModel,
164
- UserModel
168
+ const {
169
+ connectToDatabase,
170
+ IntegrationModel,
171
+ UserModel,
165
172
  } = require('@friggframework/core');
166
173
 
167
174
  await connectToDatabase();
168
175
 
169
176
  // Query integrations
170
- const userIntegrations = await IntegrationModel.find({
177
+ const userIntegrations = await IntegrationModel.find({
171
178
  userId: 'user-123',
172
- status: 'ENABLED'
179
+ status: 'ENABLED',
173
180
  });
174
181
 
175
182
  // Create user
176
183
  const user = new UserModel({
177
184
  email: 'user@example.com',
178
- name: 'John Doe'
185
+ name: 'John Doe',
179
186
  });
180
187
  await user.save();
181
188
  ```
@@ -185,6 +192,7 @@ await user.save();
185
192
  AES-256-GCM encryption for sensitive data.
186
193
 
187
194
  **Usage:**
195
+
188
196
  ```javascript
189
197
  const { Encrypt, Cryptor } = require('@friggframework/core');
190
198
 
@@ -194,10 +202,12 @@ const decrypted = Encrypt.decrypt(encrypted);
194
202
 
195
203
  // Advanced encryption with custom key
196
204
  const cryptor = new Cryptor(process.env.CUSTOM_KEY);
197
- const secureData = cryptor.encrypt(JSON.stringify({
198
- accessToken: 'oauth-token',
199
- refreshToken: 'refresh-token'
200
- }));
205
+ const secureData = cryptor.encrypt(
206
+ JSON.stringify({
207
+ accessToken: 'oauth-token',
208
+ refreshToken: 'refresh-token',
209
+ })
210
+ );
201
211
  ```
202
212
 
203
213
  ### 5. Error Handling (`/errors`)
@@ -205,11 +215,12 @@ const secureData = cryptor.encrypt(JSON.stringify({
205
215
  Standardized error types with proper HTTP status codes.
206
216
 
207
217
  **Usage:**
218
+
208
219
  ```javascript
209
- const {
210
- BaseError,
211
- RequiredPropertyError,
212
- FetchError
220
+ const {
221
+ BaseError,
222
+ RequiredPropertyError,
223
+ FetchError,
213
224
  } = require('@friggframework/core');
214
225
 
215
226
  // Custom business logic error
@@ -218,13 +229,13 @@ throw new RequiredPropertyError('userId is required');
218
229
  // API communication error
219
230
  throw new FetchError('Failed to fetch data from external API', {
220
231
  statusCode: 404,
221
- response: errorResponse
232
+ response: errorResponse,
222
233
  });
223
234
 
224
235
  // Base error with custom properties
225
236
  throw new BaseError('Integration failed', {
226
237
  integrationId: 'int-123',
227
- errorCode: 'SYNC_FAILED'
238
+ errorCode: 'SYNC_FAILED',
228
239
  });
229
240
  ```
230
241
 
@@ -233,6 +244,7 @@ throw new BaseError('Integration failed', {
233
244
  Structured logging with debug capabilities.
234
245
 
235
246
  **Usage:**
247
+
236
248
  ```javascript
237
249
  const { debug, initDebugLog, flushDebugLog } = require('@friggframework/core');
238
250
 
@@ -240,9 +252,9 @@ const { debug, initDebugLog, flushDebugLog } = require('@friggframework/core');
240
252
  initDebugLog('integration:slack');
241
253
 
242
254
  // Log debug information
243
- debug('Processing webhook payload', {
255
+ debug('Processing webhook payload', {
244
256
  eventType: 'contact.created',
245
- payload: webhookData
257
+ payload: webhookData,
246
258
  });
247
259
 
248
260
  // Flush logs (useful in serverless environments)
@@ -254,27 +266,31 @@ await flushDebugLog();
254
266
  Comprehensive user authentication and authorization system supporting both individual and organizational users.
255
267
 
256
268
  **Key Classes:**
257
- - `User` - Domain aggregate for user entities
258
- - `UserRepository` - Data access for user operations
259
- - Use cases: `LoginUser`, `CreateIndividualUser`, `CreateOrganizationUser`, `GetUserFromBearerToken`
269
+
270
+ - `User` - Domain aggregate for user entities
271
+ - `UserRepository` - Data access for user operations
272
+ - Use cases: `LoginUser`, `CreateIndividualUser`, `CreateOrganizationUser`, `GetUserFromBearerToken`
260
273
 
261
274
  **User Types:**
262
- - **Individual Users**: Personal accounts with email/username authentication
263
- - **Organization Users**: Business accounts with organization-level access
264
- - **Hybrid Mode**: Support for both user types simultaneously
275
+
276
+ - **Individual Users**: Personal accounts with email/username authentication
277
+ - **Organization Users**: Business accounts with organization-level access
278
+ - **Hybrid Mode**: Support for both user types simultaneously
265
279
 
266
280
  **Authentication Methods:**
267
- - **Password-based**: Traditional username/password authentication
268
- - **Token-based**: Bearer token authentication with session management
269
- - **App-based**: External app user ID authentication (passwordless)
281
+
282
+ - **Password-based**: Traditional username/password authentication
283
+ - **Token-based**: Bearer token authentication with session management
284
+ - **App-based**: External app user ID authentication (passwordless)
270
285
 
271
286
  **Usage:**
287
+
272
288
  ```javascript
273
- const {
274
- LoginUser,
275
- CreateIndividualUser,
289
+ const {
290
+ LoginUser,
291
+ CreateIndividualUser,
276
292
  GetUserFromBearerToken,
277
- UserRepository
293
+ UserRepository,
278
294
  } = require('@friggframework/core');
279
295
 
280
296
  // Configure user behavior in app definition
@@ -282,7 +298,7 @@ const userConfig = {
282
298
  usePassword: true,
283
299
  primary: 'individual', // or 'organization'
284
300
  individualUserRequired: true,
285
- organizationUserRequired: false
301
+ organizationUserRequired: false,
286
302
  };
287
303
 
288
304
  const userRepository = new UserRepository({ userConfig });
@@ -293,18 +309,21 @@ const user = await createUser.execute({
293
309
  email: 'user@example.com',
294
310
  username: 'john_doe',
295
311
  password: 'secure_password',
296
- appUserId: 'external_user_123' // Optional external reference
312
+ appUserId: 'external_user_123', // Optional external reference
297
313
  });
298
314
 
299
315
  // Login user
300
316
  const loginUser = new LoginUser({ userRepository, userConfig });
301
317
  const authenticatedUser = await loginUser.execute({
302
318
  username: 'john_doe',
303
- password: 'secure_password'
319
+ password: 'secure_password',
304
320
  });
305
321
 
306
322
  // Token-based authentication
307
- const getUserFromToken = new GetUserFromBearerToken({ userRepository, userConfig });
323
+ const getUserFromToken = new GetUserFromBearerToken({
324
+ userRepository,
325
+ userConfig,
326
+ });
308
327
  const user = await getUserFromToken.execute('Bearer eyJhbGciOiJIUzI1NiIs...');
309
328
 
310
329
  // Access user properties
@@ -319,12 +338,13 @@ console.log('Organization user:', user.getOrganizationUser());
319
338
  AWS Lambda-specific utilities and helpers.
320
339
 
321
340
  **Usage:**
341
+
322
342
  ```javascript
323
343
  const { TimeoutCatcher } = require('@friggframework/core');
324
344
 
325
345
  exports.handler = async (event, context) => {
326
346
  const timeoutCatcher = new TimeoutCatcher(context);
327
-
347
+
328
348
  try {
329
349
  // Long-running integration process
330
350
  const result = await processIntegrationSync(event);
@@ -353,11 +373,11 @@ User behavior is configured in the app definition, allowing you to customize aut
353
373
  const appDefinition = {
354
374
  integrations: [HubSpotIntegration],
355
375
  user: {
356
- usePassword: true, // Enable password authentication
357
- primary: 'individual', // Primary user type: 'individual' or 'organization'
358
- organizationUserRequired: true, // Require organization user
359
- individualUserRequired: true, // Require individual user
360
- }
376
+ usePassword: true, // Enable password authentication
377
+ primary: 'individual', // Primary user type: 'individual' or 'organization'
378
+ organizationUserRequired: true, // Require organization user
379
+ individualUserRequired: true, // Require individual user
380
+ },
361
381
  };
362
382
  ```
363
383
 
@@ -372,20 +392,20 @@ const { User } = require('@friggframework/core');
372
392
  const user = new User(individualUser, organizationUser, usePassword, primary);
373
393
 
374
394
  // Access methods
375
- user.getId() // Get primary user ID
376
- user.getPrimaryUser() // Get primary user based on config
377
- user.getIndividualUser() // Get individual user
378
- user.getOrganizationUser() // Get organization user
395
+ user.getId(); // Get primary user ID
396
+ user.getPrimaryUser(); // Get primary user based on config
397
+ user.getIndividualUser(); // Get individual user
398
+ user.getOrganizationUser(); // Get organization user
379
399
 
380
400
  // Validation methods
381
- user.isPasswordRequired() // Check if password is required
382
- user.isPasswordValid(password) // Validate password
383
- user.isIndividualUserRequired() // Check individual user requirement
384
- user.isOrganizationUserRequired() // Check organization user requirement
401
+ user.isPasswordRequired(); // Check if password is required
402
+ user.isPasswordValid(password); // Validate password
403
+ user.isIndividualUserRequired(); // Check individual user requirement
404
+ user.isOrganizationUserRequired(); // Check organization user requirement
385
405
 
386
406
  // Configuration methods
387
- user.setIndividualUser(individualUser)
388
- user.setOrganizationUser(organizationUser)
407
+ user.setIndividualUser(individualUser);
408
+ user.setOrganizationUser(organizationUser);
389
409
  ```
390
410
 
391
411
  ### Database Models
@@ -421,11 +441,11 @@ The user system uses MongoDB with Mongoose for data persistence:
421
441
 
422
442
  ### Security Features
423
443
 
424
- - **Password Hashing**: Uses bcrypt with configurable salt rounds
425
- - **Token Management**: Secure session tokens with expiration
426
- - **Unique Constraints**: Enforced username and email uniqueness
427
- - **External References**: Support for external app user/org IDs
428
- - **Flexible Authentication**: Multiple authentication methods
444
+ - **Password Hashing**: Uses bcrypt with configurable salt rounds
445
+ - **Token Management**: Secure session tokens with expiration
446
+ - **Unique Constraints**: Enforced username and email uniqueness
447
+ - **External References**: Support for external app user/org IDs
448
+ - **Flexible Authentication**: Multiple authentication methods
429
449
 
430
450
  ## Hexagonal Architecture
431
451
 
@@ -446,7 +466,9 @@ class UpdateIntegrationStatus {
446
466
  }
447
467
 
448
468
  // Domain operation
449
- const integration = await this.integrationRepository.findById(integrationId);
469
+ const integration = await this.integrationRepository.findById(
470
+ integrationId
471
+ );
450
472
  if (!integration) {
451
473
  throw new Error('Integration not found');
452
474
  }
@@ -454,7 +476,7 @@ class UpdateIntegrationStatus {
454
476
  // Update and persist
455
477
  integration.status = newStatus;
456
478
  integration.updatedAt = new Date();
457
-
479
+
458
480
  return await this.integrationRepository.save(integration);
459
481
  }
460
482
  }
@@ -484,7 +506,7 @@ class IntegrationRepository {
484
506
  userId,
485
507
  config,
486
508
  status: 'NEW',
487
- createdAt: new Date()
509
+ createdAt: new Date(),
488
510
  });
489
511
  return await integration.save();
490
512
  }
@@ -500,7 +522,7 @@ const Integration = new Proxy(class {}, {
500
522
  construct(target, args) {
501
523
  const [params] = args;
502
524
  const instance = new params.integrationClass(params);
503
-
525
+
504
526
  // Attach domain properties
505
527
  Object.assign(instance, {
506
528
  id: params.id,
@@ -508,11 +530,11 @@ const Integration = new Proxy(class {}, {
508
530
  entities: params.entities,
509
531
  config: params.config,
510
532
  status: params.status,
511
- modules: params.modules
533
+ modules: params.modules,
512
534
  });
513
535
 
514
536
  return instance;
515
- }
537
+ },
516
538
  });
517
539
  ```
518
540
 
@@ -565,7 +587,7 @@ class HubSpotIntegration extends IntegrationBase {
565
587
 
566
588
  constructor() {
567
589
  super();
568
-
590
+
569
591
  // Define event handlers for various integration actions
570
592
  this.events = {
571
593
  // Webhook handler with real-time WebSocket broadcasting
@@ -574,7 +596,8 @@ class HubSpotIntegration extends IntegrationBase {
574
596
  console.log('Received HubSpot webhook:', data);
575
597
 
576
598
  // Broadcast to all connected WebSocket clients
577
- const activeConnections = await WebsocketConnection.getActiveConnections();
599
+ const activeConnections =
600
+ await WebsocketConnection.getActiveConnections();
578
601
  const message = JSON.stringify({
579
602
  type: 'HUBSPOT_WEBHOOK',
580
603
  data,
@@ -585,16 +608,17 @@ class HubSpotIntegration extends IntegrationBase {
585
608
  });
586
609
  },
587
610
  },
588
-
611
+
589
612
  // User action: Get sample data with formatted table output
590
613
  [FriggConstants.defaultEvents.GET_SAMPLE_DATA]: {
591
614
  type: FriggConstants.eventTypes.USER_ACTION,
592
615
  handler: this.getSampleData,
593
616
  title: 'Get Sample Data',
594
- description: 'Get sample data from HubSpot and display in a formatted table',
617
+ description:
618
+ 'Get sample data from HubSpot and display in a formatted table',
595
619
  userActionType: 'QUICK_ACTION',
596
620
  },
597
-
621
+
598
622
  // User action: List available objects
599
623
  GET_OBJECT_LIST: {
600
624
  type: FriggConstants.eventTypes.USER_ACTION,
@@ -603,7 +627,7 @@ class HubSpotIntegration extends IntegrationBase {
603
627
  description: 'Get list of available HubSpot objects',
604
628
  userActionType: 'DATA',
605
629
  },
606
-
630
+
607
631
  // User action: Create records with dynamic forms
608
632
  CREATE_RECORD: {
609
633
  type: FriggConstants.eventTypes.USER_ACTION,
@@ -613,7 +637,7 @@ class HubSpotIntegration extends IntegrationBase {
613
637
  userActionType: 'DATA',
614
638
  },
615
639
  };
616
-
640
+
617
641
  // Extension system for modular functionality
618
642
  this.extensions = {
619
643
  hubspotWebhooks: {
@@ -687,7 +711,7 @@ class HubSpotIntegration extends IntegrationBase {
687
711
  let res;
688
712
  const objectType = args.objectType;
689
713
  delete args.objectType;
690
-
714
+
691
715
  switch (objectType.toLowerCase()) {
692
716
  case 'deal':
693
717
  res = await this.hubspot.api.createDeal({ ...args });
@@ -718,7 +742,7 @@ class HubSpotIntegration extends IntegrationBase {
718
742
  },
719
743
  required: [],
720
744
  };
721
-
745
+
722
746
  let uiSchema = {
723
747
  type: 'HorizontalLayout',
724
748
  elements: [
@@ -744,7 +768,7 @@ class HubSpotIntegration extends IntegrationBase {
744
768
  { type: 'Control', scope: '#/properties/amount' }
745
769
  );
746
770
  break;
747
-
771
+
748
772
  case 'company':
749
773
  jsonSchema.properties = {
750
774
  ...jsonSchema.properties,
@@ -757,7 +781,7 @@ class HubSpotIntegration extends IntegrationBase {
757
781
  { type: 'Control', scope: '#/properties/website' }
758
782
  );
759
783
  break;
760
-
784
+
761
785
  case 'contact':
762
786
  jsonSchema.properties = {
763
787
  ...jsonSchema.properties,
@@ -765,16 +789,25 @@ class HubSpotIntegration extends IntegrationBase {
765
789
  lastname: { type: 'string', title: 'Last Name' },
766
790
  email: { type: 'string', title: 'Email Address' },
767
791
  };
768
- jsonSchema.required = ['firstname', 'lastname', 'email'];
792
+ jsonSchema.required = [
793
+ 'firstname',
794
+ 'lastname',
795
+ 'email',
796
+ ];
769
797
  uiSchema.elements.push(
770
- { type: 'Control', scope: '#/properties/firstname' },
798
+ {
799
+ type: 'Control',
800
+ scope: '#/properties/firstname',
801
+ },
771
802
  { type: 'Control', scope: '#/properties/lastname' },
772
803
  { type: 'Control', scope: '#/properties/email' }
773
804
  );
774
805
  break;
775
-
806
+
776
807
  default:
777
- throw new Error(`Unsupported object type: ${data.name}`);
808
+ throw new Error(
809
+ `Unsupported object type: ${data.name}`
810
+ );
778
811
  }
779
812
 
780
813
  return {
@@ -796,28 +829,25 @@ module.exports = HubSpotIntegration;
796
829
  ```
797
830
 
798
831
  index.js
832
+
799
833
  ```js
800
834
  const HubSpotIntegration = require('./src/integrations/HubSpotIntegration');
801
835
 
802
836
  const appDefinition = {
803
- integrations: [
804
- HubSpotIntegration,
805
- ],
837
+ integrations: [HubSpotIntegration],
806
838
  user: {
807
839
  usePassword: true,
808
840
  primary: 'individual',
809
841
  organizationUserRequired: true,
810
842
  individualUserRequired: true,
811
- }
812
- }
843
+ },
844
+ };
813
845
 
814
846
  module.exports = {
815
847
  Definition: appDefinition,
816
- }
817
-
848
+ };
818
849
  ```
819
850
 
820
-
821
851
  ### Key Features Demonstrated
822
852
 
823
853
  This real-world example showcases:
@@ -830,7 +860,6 @@ This real-world example showcases:
830
860
  **🔗 Deep Linking**: Direct links to HubSpot records in formatted data
831
861
  **⚡ Real-time Updates**: WebSocket connections for live data streaming
832
862
 
833
-
834
863
  ## Testing
835
864
 
836
865
  ### Running Tests
@@ -860,13 +889,15 @@ describe('CreateIntegration Use-Case', () => {
860
889
  useCase = new CreateIntegration({
861
890
  integrationRepository,
862
891
  integrationClasses: [TestIntegration],
863
- moduleFactory
892
+ moduleFactory,
864
893
  });
865
894
  });
866
895
 
867
896
  describe('happy path', () => {
868
897
  it('creates an integration and returns DTO', async () => {
869
- const result = await useCase.execute(['entity-1'], 'user-1', { type: 'test' });
898
+ const result = await useCase.execute(['entity-1'], 'user-1', {
899
+ type: 'test',
900
+ });
870
901
  expect(result.id).toBeDefined();
871
902
  expect(result.status).toBe('NEW');
872
903
  });
@@ -874,8 +905,9 @@ describe('CreateIntegration Use-Case', () => {
874
905
 
875
906
  describe('error cases', () => {
876
907
  it('throws error for unknown integration type', async () => {
877
- await expect(useCase.execute(['entity-1'], 'user-1', { type: 'unknown' }))
878
- .rejects.toThrow('No integration class found for type: unknown');
908
+ await expect(
909
+ useCase.execute(['entity-1'], 'user-1', { type: 'unknown' })
910
+ ).rejects.toThrow('No integration class found for type: unknown');
879
911
  });
880
912
  });
881
913
  });
@@ -886,7 +918,10 @@ describe('CreateIntegration Use-Case', () => {
886
918
  The framework provides test doubles for external dependencies:
887
919
 
888
920
  ```javascript
889
- const { TestIntegrationRepository, TestModuleFactory } = require('@friggframework/core/test');
921
+ const {
922
+ TestIntegrationRepository,
923
+ TestModuleFactory,
924
+ } = require('@friggframework/core/test');
890
925
 
891
926
  // Mock repository for testing
892
927
  const testRepo = new TestIntegrationRepository();
@@ -946,7 +981,7 @@ const {
946
981
  CreateIntegration,
947
982
  UpdateIntegration,
948
983
  DeleteIntegration,
949
-
984
+
950
985
  // Modules
951
986
  OAuth2Requester,
952
987
  ApiKeyRequester,
@@ -956,25 +991,25 @@ const {
956
991
  connectToDatabase,
957
992
  mongoose,
958
993
  UserModel,
959
-
994
+
960
995
  // Utilities
961
996
  Encrypt,
962
997
  Cryptor,
963
998
  BaseError,
964
999
  debug,
965
- TimeoutCatcher
1000
+ TimeoutCatcher,
966
1001
  } = require('@friggframework/core');
967
1002
  ```
968
1003
 
969
1004
  ### Environment Configuration
970
1005
 
971
- | Variable | Required | Description |
972
- |----------|----------|-------------|
973
- | `MONGO_URI` | Yes | MongoDB connection string |
974
- | `FRIGG_ENCRYPTION_KEY` | Yes | 256-bit encryption key |
975
- | `AWS_REGION` | No | AWS region for services |
976
- | `DEBUG` | No | Debug logging pattern |
977
- | `LOG_LEVEL` | No | Logging level (debug, info, warn, error) |
1006
+ | Variable | Required | Description |
1007
+ | ---------------------- | -------- | ---------------------------------------- |
1008
+ | `MONGO_URI` | Yes | MongoDB connection string |
1009
+ | `FRIGG_ENCRYPTION_KEY` | Yes | 256-bit encryption key |
1010
+ | `AWS_REGION` | No | AWS region for services |
1011
+ | `DEBUG` | No | Debug logging pattern |
1012
+ | `LOG_LEVEL` | No | Logging level (debug, info, warn, error) |
978
1013
 
979
1014
  ## License
980
1015
 
@@ -984,9 +1019,9 @@ This project is licensed under the MIT License - see the [LICENSE.md](../../LICE
984
1019
 
985
1020
  ## Support
986
1021
 
987
- - 📖 [Documentation](https://docs.friggframework.org)
988
- - 💬 [Community Slack](https://friggframework.slack.com)
989
- - 🐛 [Issue Tracker](https://github.com/friggframework/frigg/issues)
990
- - 📧 [Email Support](mailto:support@friggframework.org)
1022
+ - 📖 [Documentation](https://docs.friggframework.org)
1023
+ - 💬 [Community Slack](https://friggframework.slack.com)
1024
+ - 🐛 [Issue Tracker](https://github.com/friggframework/frigg/issues)
1025
+ - 📧 [Email Support](mailto:support@friggframework.org)
991
1026
 
992
1027
  Built with ❤️ by the Frigg Framework team.