@friggframework/core 2.0.0-next.5 → 2.0.0-next.51

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 (267) hide show
  1. package/CLAUDE.md +693 -0
  2. package/README.md +959 -50
  3. package/application/commands/README.md +421 -0
  4. package/application/commands/credential-commands.js +224 -0
  5. package/application/commands/entity-commands.js +315 -0
  6. package/application/commands/integration-commands.js +179 -0
  7. package/application/commands/user-commands.js +213 -0
  8. package/application/index.js +69 -0
  9. package/core/CLAUDE.md +690 -0
  10. package/core/Worker.js +8 -21
  11. package/core/create-handler.js +2 -7
  12. package/credential/repositories/credential-repository-factory.js +47 -0
  13. package/credential/repositories/credential-repository-interface.js +98 -0
  14. package/credential/repositories/credential-repository-mongo.js +307 -0
  15. package/credential/repositories/credential-repository-postgres.js +313 -0
  16. package/credential/repositories/credential-repository.js +302 -0
  17. package/credential/use-cases/get-credential-for-user.js +21 -0
  18. package/credential/use-cases/update-authentication-status.js +15 -0
  19. package/database/MONGODB_TRANSACTION_FIX.md +198 -0
  20. package/database/adapters/lambda-invoker.js +97 -0
  21. package/database/config.js +154 -0
  22. package/database/encryption/README.md +684 -0
  23. package/database/encryption/encryption-schema-registry.js +141 -0
  24. package/database/encryption/field-encryption-service.js +226 -0
  25. package/database/encryption/logger.js +79 -0
  26. package/database/encryption/prisma-encryption-extension.js +222 -0
  27. package/database/index.js +25 -12
  28. package/database/models/WebsocketConnection.js +16 -10
  29. package/database/models/readme.md +1 -0
  30. package/database/prisma.js +222 -0
  31. package/database/repositories/health-check-repository-factory.js +43 -0
  32. package/database/repositories/health-check-repository-interface.js +87 -0
  33. package/database/repositories/health-check-repository-mongodb.js +91 -0
  34. package/database/repositories/health-check-repository-postgres.js +82 -0
  35. package/database/repositories/health-check-repository.js +108 -0
  36. package/database/repositories/migration-status-repository-s3.js +137 -0
  37. package/database/use-cases/check-database-health-use-case.js +29 -0
  38. package/database/use-cases/check-database-state-use-case.js +81 -0
  39. package/database/use-cases/check-encryption-health-use-case.js +83 -0
  40. package/database/use-cases/get-database-state-via-worker-use-case.js +61 -0
  41. package/database/use-cases/get-migration-status-use-case.js +93 -0
  42. package/database/use-cases/run-database-migration-use-case.js +137 -0
  43. package/database/use-cases/test-encryption-use-case.js +253 -0
  44. package/database/use-cases/trigger-database-migration-use-case.js +157 -0
  45. package/database/utils/mongodb-collection-utils.js +91 -0
  46. package/database/utils/mongodb-schema-init.js +106 -0
  47. package/database/utils/prisma-runner.js +400 -0
  48. package/database/utils/prisma-schema-parser.js +182 -0
  49. package/docs/PROCESS_MANAGEMENT_QUEUE_SPEC.md +517 -0
  50. package/encrypt/Cryptor.js +34 -168
  51. package/encrypt/index.js +1 -2
  52. package/encrypt/test-encrypt.js +0 -2
  53. package/generated/prisma-mongodb/client.d.ts +1 -0
  54. package/generated/prisma-mongodb/client.js +4 -0
  55. package/generated/prisma-mongodb/default.d.ts +1 -0
  56. package/generated/prisma-mongodb/default.js +4 -0
  57. package/generated/prisma-mongodb/edge.d.ts +1 -0
  58. package/generated/prisma-mongodb/edge.js +334 -0
  59. package/generated/prisma-mongodb/index-browser.js +316 -0
  60. package/generated/prisma-mongodb/index.d.ts +22898 -0
  61. package/generated/prisma-mongodb/index.js +359 -0
  62. package/generated/prisma-mongodb/package.json +183 -0
  63. package/generated/prisma-mongodb/query-engine-debian-openssl-3.0.x +0 -0
  64. package/generated/prisma-mongodb/query-engine-rhel-openssl-3.0.x +0 -0
  65. package/generated/prisma-mongodb/runtime/binary.d.ts +1 -0
  66. package/generated/prisma-mongodb/runtime/binary.js +289 -0
  67. package/generated/prisma-mongodb/runtime/edge-esm.js +34 -0
  68. package/generated/prisma-mongodb/runtime/edge.js +34 -0
  69. package/generated/prisma-mongodb/runtime/index-browser.d.ts +370 -0
  70. package/generated/prisma-mongodb/runtime/index-browser.js +16 -0
  71. package/generated/prisma-mongodb/runtime/library.d.ts +3982 -0
  72. package/generated/prisma-mongodb/runtime/react-native.js +83 -0
  73. package/generated/prisma-mongodb/runtime/wasm-compiler-edge.js +84 -0
  74. package/generated/prisma-mongodb/runtime/wasm-engine-edge.js +36 -0
  75. package/generated/prisma-mongodb/schema.prisma +362 -0
  76. package/generated/prisma-mongodb/wasm-edge-light-loader.mjs +4 -0
  77. package/generated/prisma-mongodb/wasm-worker-loader.mjs +4 -0
  78. package/generated/prisma-mongodb/wasm.d.ts +1 -0
  79. package/generated/prisma-mongodb/wasm.js +341 -0
  80. package/generated/prisma-postgresql/client.d.ts +1 -0
  81. package/generated/prisma-postgresql/client.js +4 -0
  82. package/generated/prisma-postgresql/default.d.ts +1 -0
  83. package/generated/prisma-postgresql/default.js +4 -0
  84. package/generated/prisma-postgresql/edge.d.ts +1 -0
  85. package/generated/prisma-postgresql/edge.js +356 -0
  86. package/generated/prisma-postgresql/index-browser.js +338 -0
  87. package/generated/prisma-postgresql/index.d.ts +25072 -0
  88. package/generated/prisma-postgresql/index.js +381 -0
  89. package/generated/prisma-postgresql/package.json +183 -0
  90. package/generated/prisma-postgresql/query-engine-debian-openssl-3.0.x +0 -0
  91. package/generated/prisma-postgresql/query-engine-rhel-openssl-3.0.x +0 -0
  92. package/generated/prisma-postgresql/query_engine_bg.js +2 -0
  93. package/generated/prisma-postgresql/query_engine_bg.wasm +0 -0
  94. package/generated/prisma-postgresql/runtime/binary.d.ts +1 -0
  95. package/generated/prisma-postgresql/runtime/binary.js +289 -0
  96. package/generated/prisma-postgresql/runtime/edge-esm.js +34 -0
  97. package/generated/prisma-postgresql/runtime/edge.js +34 -0
  98. package/generated/prisma-postgresql/runtime/index-browser.d.ts +370 -0
  99. package/generated/prisma-postgresql/runtime/index-browser.js +16 -0
  100. package/generated/prisma-postgresql/runtime/library.d.ts +3982 -0
  101. package/generated/prisma-postgresql/runtime/react-native.js +83 -0
  102. package/generated/prisma-postgresql/runtime/wasm-compiler-edge.js +84 -0
  103. package/generated/prisma-postgresql/runtime/wasm-engine-edge.js +36 -0
  104. package/generated/prisma-postgresql/schema.prisma +345 -0
  105. package/generated/prisma-postgresql/wasm-edge-light-loader.mjs +4 -0
  106. package/generated/prisma-postgresql/wasm-worker-loader.mjs +4 -0
  107. package/generated/prisma-postgresql/wasm.d.ts +1 -0
  108. package/generated/prisma-postgresql/wasm.js +363 -0
  109. package/handlers/WEBHOOKS.md +653 -0
  110. package/handlers/app-definition-loader.js +38 -0
  111. package/handlers/app-handler-helpers.js +56 -0
  112. package/handlers/backend-utils.js +180 -0
  113. package/handlers/database-migration-handler.js +227 -0
  114. package/handlers/integration-event-dispatcher.js +54 -0
  115. package/handlers/routers/HEALTHCHECK.md +342 -0
  116. package/handlers/routers/auth.js +15 -0
  117. package/handlers/routers/db-migration.handler.js +29 -0
  118. package/handlers/routers/db-migration.js +256 -0
  119. package/handlers/routers/health.js +519 -0
  120. package/handlers/routers/integration-defined-routers.js +45 -0
  121. package/handlers/routers/integration-webhook-routers.js +67 -0
  122. package/handlers/routers/user.js +63 -0
  123. package/handlers/routers/websocket.js +57 -0
  124. package/handlers/use-cases/check-external-apis-health-use-case.js +81 -0
  125. package/handlers/use-cases/check-integrations-health-use-case.js +44 -0
  126. package/handlers/workers/db-migration.js +352 -0
  127. package/handlers/workers/integration-defined-workers.js +27 -0
  128. package/index.js +77 -22
  129. package/integrations/WEBHOOK-QUICKSTART.md +151 -0
  130. package/integrations/index.js +12 -10
  131. package/integrations/integration-base.js +296 -54
  132. package/integrations/integration-router.js +381 -182
  133. package/integrations/options.js +1 -1
  134. package/integrations/repositories/integration-mapping-repository-factory.js +50 -0
  135. package/integrations/repositories/integration-mapping-repository-interface.js +106 -0
  136. package/integrations/repositories/integration-mapping-repository-mongo.js +161 -0
  137. package/integrations/repositories/integration-mapping-repository-postgres.js +227 -0
  138. package/integrations/repositories/integration-mapping-repository.js +156 -0
  139. package/integrations/repositories/integration-repository-factory.js +44 -0
  140. package/integrations/repositories/integration-repository-interface.js +127 -0
  141. package/integrations/repositories/integration-repository-mongo.js +303 -0
  142. package/integrations/repositories/integration-repository-postgres.js +352 -0
  143. package/integrations/repositories/process-repository-factory.js +46 -0
  144. package/integrations/repositories/process-repository-interface.js +90 -0
  145. package/integrations/repositories/process-repository-mongo.js +190 -0
  146. package/integrations/repositories/process-repository-postgres.js +217 -0
  147. package/integrations/tests/doubles/dummy-integration-class.js +83 -0
  148. package/integrations/tests/doubles/test-integration-repository.js +99 -0
  149. package/integrations/use-cases/create-integration.js +83 -0
  150. package/integrations/use-cases/create-process.js +128 -0
  151. package/integrations/use-cases/delete-integration-for-user.js +101 -0
  152. package/integrations/use-cases/find-integration-context-by-external-entity-id.js +72 -0
  153. package/integrations/use-cases/get-integration-for-user.js +78 -0
  154. package/integrations/use-cases/get-integration-instance-by-definition.js +67 -0
  155. package/integrations/use-cases/get-integration-instance.js +83 -0
  156. package/integrations/use-cases/get-integrations-for-user.js +88 -0
  157. package/integrations/use-cases/get-possible-integrations.js +27 -0
  158. package/integrations/use-cases/get-process.js +87 -0
  159. package/integrations/use-cases/index.js +19 -0
  160. package/integrations/use-cases/load-integration-context.js +71 -0
  161. package/integrations/use-cases/update-integration-messages.js +44 -0
  162. package/integrations/use-cases/update-integration-status.js +32 -0
  163. package/integrations/use-cases/update-integration.js +93 -0
  164. package/integrations/use-cases/update-process-metrics.js +201 -0
  165. package/integrations/use-cases/update-process-state.js +119 -0
  166. package/integrations/utils/map-integration-dto.js +37 -0
  167. package/jest-global-setup-noop.js +3 -0
  168. package/jest-global-teardown-noop.js +3 -0
  169. package/logs/logger.js +0 -4
  170. package/{module-plugin → modules}/entity.js +1 -1
  171. package/{module-plugin → modules}/index.js +0 -8
  172. package/modules/module-factory.js +56 -0
  173. package/modules/module.js +221 -0
  174. package/modules/repositories/module-repository-factory.js +33 -0
  175. package/modules/repositories/module-repository-interface.js +129 -0
  176. package/modules/repositories/module-repository-mongo.js +377 -0
  177. package/modules/repositories/module-repository-postgres.js +426 -0
  178. package/modules/repositories/module-repository.js +316 -0
  179. package/{module-plugin → modules}/requester/requester.js +1 -0
  180. package/{module-plugin → modules}/test/mock-api/api.js +8 -3
  181. package/{module-plugin → modules}/test/mock-api/definition.js +12 -8
  182. package/modules/tests/doubles/test-module-factory.js +16 -0
  183. package/modules/tests/doubles/test-module-repository.js +39 -0
  184. package/modules/use-cases/get-entities-for-user.js +32 -0
  185. package/modules/use-cases/get-entity-options-by-id.js +59 -0
  186. package/modules/use-cases/get-entity-options-by-type.js +34 -0
  187. package/modules/use-cases/get-module-instance-from-type.js +31 -0
  188. package/modules/use-cases/get-module.js +55 -0
  189. package/modules/use-cases/process-authorization-callback.js +122 -0
  190. package/modules/use-cases/refresh-entity-options.js +59 -0
  191. package/modules/use-cases/test-module-auth.js +55 -0
  192. package/modules/utils/map-module-dto.js +18 -0
  193. package/package.json +82 -50
  194. package/prisma-mongodb/schema.prisma +362 -0
  195. package/prisma-postgresql/migrations/20250930193005_init/migration.sql +315 -0
  196. package/prisma-postgresql/migrations/20251006135218_init/migration.sql +9 -0
  197. package/prisma-postgresql/migrations/20251010000000_remove_unused_entity_reference_map/migration.sql +3 -0
  198. package/prisma-postgresql/migrations/migration_lock.toml +3 -0
  199. package/prisma-postgresql/schema.prisma +345 -0
  200. package/queues/queuer-util.js +28 -15
  201. package/syncs/manager.js +468 -443
  202. package/syncs/repositories/sync-repository-factory.js +38 -0
  203. package/syncs/repositories/sync-repository-interface.js +109 -0
  204. package/syncs/repositories/sync-repository-mongo.js +239 -0
  205. package/syncs/repositories/sync-repository-postgres.js +319 -0
  206. package/syncs/sync.js +0 -1
  207. package/token/repositories/token-repository-factory.js +33 -0
  208. package/token/repositories/token-repository-interface.js +131 -0
  209. package/token/repositories/token-repository-mongo.js +212 -0
  210. package/token/repositories/token-repository-postgres.js +257 -0
  211. package/token/repositories/token-repository.js +219 -0
  212. package/types/core/index.d.ts +2 -2
  213. package/types/integrations/index.d.ts +2 -6
  214. package/types/module-plugin/index.d.ts +5 -59
  215. package/types/syncs/index.d.ts +0 -2
  216. package/user/repositories/user-repository-factory.js +46 -0
  217. package/user/repositories/user-repository-interface.js +198 -0
  218. package/user/repositories/user-repository-mongo.js +291 -0
  219. package/user/repositories/user-repository-postgres.js +350 -0
  220. package/user/tests/doubles/test-user-repository.js +72 -0
  221. package/user/use-cases/authenticate-user.js +127 -0
  222. package/user/use-cases/authenticate-with-shared-secret.js +48 -0
  223. package/user/use-cases/create-individual-user.js +61 -0
  224. package/user/use-cases/create-organization-user.js +47 -0
  225. package/user/use-cases/create-token-for-user-id.js +30 -0
  226. package/user/use-cases/get-user-from-adopter-jwt.js +149 -0
  227. package/user/use-cases/get-user-from-bearer-token.js +77 -0
  228. package/user/use-cases/get-user-from-x-frigg-headers.js +106 -0
  229. package/user/use-cases/login-user.js +122 -0
  230. package/user/user.js +93 -0
  231. package/utils/backend-path.js +38 -0
  232. package/utils/index.js +6 -0
  233. package/websocket/repositories/websocket-connection-repository-factory.js +37 -0
  234. package/websocket/repositories/websocket-connection-repository-interface.js +106 -0
  235. package/websocket/repositories/websocket-connection-repository-mongo.js +156 -0
  236. package/websocket/repositories/websocket-connection-repository-postgres.js +196 -0
  237. package/websocket/repositories/websocket-connection-repository.js +161 -0
  238. package/database/models/State.js +0 -9
  239. package/database/models/Token.js +0 -70
  240. package/database/mongo.js +0 -45
  241. package/encrypt/Cryptor.test.js +0 -32
  242. package/encrypt/encrypt.js +0 -132
  243. package/encrypt/encrypt.test.js +0 -1069
  244. package/errors/base-error.test.js +0 -32
  245. package/errors/fetch-error.test.js +0 -79
  246. package/errors/halt-error.test.js +0 -11
  247. package/errors/validation-errors.test.js +0 -120
  248. package/integrations/create-frigg-backend.js +0 -31
  249. package/integrations/integration-factory.js +0 -251
  250. package/integrations/integration-mapping.js +0 -43
  251. package/integrations/integration-model.js +0 -46
  252. package/integrations/integration-user.js +0 -144
  253. package/integrations/test/integration-base.test.js +0 -144
  254. package/lambda/TimeoutCatcher.test.js +0 -68
  255. package/logs/logger.test.js +0 -76
  256. package/module-plugin/auther.js +0 -393
  257. package/module-plugin/credential.js +0 -22
  258. package/module-plugin/entity-manager.js +0 -70
  259. package/module-plugin/manager.js +0 -169
  260. package/module-plugin/module-factory.js +0 -61
  261. package/module-plugin/requester/requester.test.js +0 -28
  262. package/module-plugin/test/auther.test.js +0 -97
  263. /package/{module-plugin → modules}/ModuleConstants.js +0 -0
  264. /package/{module-plugin → modules}/requester/api-key.js +0 -0
  265. /package/{module-plugin → modules}/requester/basic.js +0 -0
  266. /package/{module-plugin → modules}/requester/oauth-2.js +0 -0
  267. /package/{module-plugin → modules}/test/mock-api/mocks/hubspot.js +0 -0
package/README.md CHANGED
@@ -1,83 +1,992 @@
1
1
  # Frigg Core
2
2
 
3
- The `frigg-core` package is the heart of the Frigg Framework. It contains the core functionality and essential modules required to build and maintain integrations at scale.
4
-
3
+ The `@friggframework/core` package is the foundational layer of the Frigg Framework, implementing a hexagonal architecture pattern for building scalable, maintainable enterprise integrations. It provides the essential building blocks, domain logic, and infrastructure components that power the entire Frigg ecosystem.
5
4
 
6
5
  ## Table of Contents
7
6
 
8
- - [Introduction](#introduction)
9
- - [Features](#features)
7
+ - [Architecture Overview](#architecture-overview)
10
8
  - [Installation](#installation)
11
- - [Usage](#usage)
12
- - [Modules](#modules)
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)
13
16
  - [Contributing](#contributing)
14
- - [License](#license)
15
-
16
- ## Introduction
17
17
 
18
- The Frigg Core package provides the foundational components and utilities for the Frigg Framework. It is designed to be modular, extensible, and easy to integrate with other packages in the Frigg ecosystem.
18
+ ## Architecture Overview
19
19
 
20
- ## Features
20
+ Frigg Core implements a **hexagonal architecture** (also known as ports and adapters) that separates business logic from external concerns:
21
21
 
22
- - **Associations**: Manage relationships between different entities.
23
- - **Database**: Database utilities and connectors.
24
- - **Encryption**: Secure data encryption and decryption.
25
- - **Error Handling**: Standardized error handling mechanisms.
26
- - **Integrations**: Tools for building and managing integrations.
27
- - **Lambda**: Utilities for AWS Lambda functions.
28
- - **Logging**: Structured logging utilities.
29
- - **Module Plugin**: Plugin system for extending core functionality.
30
- - **Syncs**: Synchronization utilities for data consistency.
31
- - **Infrastructure**: Frigg reads through your integration definitions and auto-generates the infrastructure your code needs to run smoothly.
22
+ ```
23
+ ┌─────────────────────────────────────────────────────────────┐
24
+ │ Inbound Adapters │
25
+ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
26
+ │ │ Express │ │ Lambda │ │ WebSocket │ │
27
+ │ │ Routes │ │ Handlers │ │ Handlers │ │
28
+ │ └─────────────┘ └─────────────┘ └─────────────┘ │
29
+ └─────────────────────────────────────────────────────────────┘
30
+
31
+ ┌─────────────────────────────────────────────────────────────┐
32
+ │ Application Layer │
33
+ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
34
+ │ │ Use Cases │ │ Services │ │ Coordinators│ │
35
+ │ │ (Business │ │ │ │ │ │
36
+ │ │ Logic) │ │ │ │ │ │
37
+ │ └─────────────┘ └─────────────┘ └─────────────┘ │
38
+ └─────────────────────────────────────────────────────────────┘
39
+
40
+ ┌─────────────────────────────────────────────────────────────┐
41
+ │ Domain Layer │
42
+ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
43
+ │ │ Integration │ │ Entities │ │ Value │ │
44
+ │ │ Aggregates │ │ │ │ Objects │ │
45
+ │ └─────────────┘ └─────────────┘ └─────────────┘ │
46
+ └─────────────────────────────────────────────────────────────┘
47
+
48
+ ┌─────────────────────────────────────────────────────────────┐
49
+ │ Outbound Adapters │
50
+ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
51
+ │ │ Database │ │ API Modules │ │ Event │ │
52
+ │ │ Repositories│ │ │ │ Publishers │ │
53
+ │ └─────────────┘ └─────────────┘ └─────────────┘ │
54
+ └─────────────────────────────────────────────────────────────┘
55
+ ```
32
56
 
33
57
  ## Installation
34
58
 
35
- To install the `frigg-core` package, use npm or yarn:
36
-
37
- ```sh
59
+ ```bash
38
60
  npm install @friggframework/core
39
61
  # or
40
62
  yarn add @friggframework/core
41
63
  ```
42
- ## Usage
43
- Here's a basic example of how to use the frigg-core package:
64
+
65
+ ### Prisma Support (Optional)
66
+
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
+
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`)
73
+
74
+ **Installation:**
75
+ ```bash
76
+ # Install Prisma CLI and Client as dev dependencies
77
+ npm install --save-dev prisma @prisma/client
78
+
79
+ # Or with yarn
80
+ yarn add -D prisma @prisma/client
81
+ ```
82
+
83
+ **Generate Prisma Clients:**
84
+ ```bash
85
+ # From @friggframework/core directory
86
+ npm run prisma:generate:mongo # MongoDB only
87
+ npm run prisma:generate:postgres # PostgreSQL only
88
+ npm run prisma:generate # Both databases
89
+ ```
90
+
91
+ **Note:** The published npm package includes pre-generated Prisma clients, so you don't need to install Prisma just to use `@friggframework/core` in production. Prisma is only required if you're actively developing migrations or running the migration Lambda function.
92
+
93
+ ### Prerequisites
94
+
95
+ - Node.js 16+
96
+ - MongoDB 4.4+ (for data persistence)
97
+ - AWS credentials (for SQS, KMS, Lambda deployment)
98
+
99
+ ### Environment Variables
100
+
101
+ ```bash
102
+ # Database
103
+ MONGO_URI=mongodb://localhost:27017/frigg
104
+ FRIGG_ENCRYPTION_KEY=your-256-bit-encryption-key
105
+
106
+ # AWS (Optional - for production deployments)
107
+ AWS_REGION=us-east-1
108
+ AWS_ACCESS_KEY_ID=your-access-key
109
+ AWS_SECRET_ACCESS_KEY=your-secret-key
110
+
111
+ # Logging
112
+ DEBUG=frigg:*
113
+ LOG_LEVEL=info
114
+ ```
115
+
116
+ ## Core Components
117
+
118
+ ### 1. Integrations (`/integrations`)
119
+
120
+ The heart of the framework - manages integration lifecycle and business logic.
121
+
122
+ **Key Classes:**
123
+ - `IntegrationBase` - Base class for all integrations
124
+ - `Integration` - Domain aggregate using Proxy pattern
125
+ - Use cases: `CreateIntegration`, `UpdateIntegration`, `DeleteIntegration`
126
+
127
+ **Usage:**
128
+ ```javascript
129
+ const { IntegrationBase } = require('@friggframework/core');
130
+
131
+ class SlackHubSpotSync extends IntegrationBase {
132
+ static Definition = {
133
+ name: 'slack-hubspot-sync',
134
+ version: '2.1.0',
135
+ modules: {
136
+ slack: 'slack',
137
+ hubspot: 'hubspot'
138
+ }
139
+ };
140
+
141
+ async onCreate({ integrationId }) {
142
+ // Setup webhooks, initial sync, etc.
143
+ await this.slack.createWebhook(process.env.WEBHOOK_URL);
144
+ await this.hubspot.setupContactSync();
145
+ await super.onCreate({ integrationId });
146
+ }
147
+ }
148
+ ```
149
+
150
+ ### 3. Database (`/database`)
151
+
152
+ MongoDB integration with Mongoose ODM.
153
+
154
+ **Key Components:**
155
+ - Connection management
156
+ - Pre-built models (User, Integration, Credential, etc.)
157
+ - Schema definitions
158
+
159
+ **Usage:**
160
+ ```javascript
161
+ const {
162
+ connectToDatabase,
163
+ IntegrationModel,
164
+ UserModel
165
+ } = require('@friggframework/core');
166
+
167
+ await connectToDatabase();
168
+
169
+ // Query integrations
170
+ const userIntegrations = await IntegrationModel.find({
171
+ userId: 'user-123',
172
+ status: 'ENABLED'
173
+ });
174
+
175
+ // Create user
176
+ const user = new UserModel({
177
+ email: 'user@example.com',
178
+ name: 'John Doe'
179
+ });
180
+ await user.save();
181
+ ```
182
+
183
+ ### 4. Encryption (`/encrypt`)
184
+
185
+ AES-256-GCM encryption for sensitive data.
186
+
187
+ **Usage:**
188
+ ```javascript
189
+ const { Encrypt, Cryptor } = require('@friggframework/core');
190
+
191
+ // Simple encryption
192
+ const encrypted = Encrypt.encrypt('sensitive-data');
193
+ const decrypted = Encrypt.decrypt(encrypted);
194
+
195
+ // Advanced encryption with custom key
196
+ const cryptor = new Cryptor(process.env.CUSTOM_KEY);
197
+ const secureData = cryptor.encrypt(JSON.stringify({
198
+ accessToken: 'oauth-token',
199
+ refreshToken: 'refresh-token'
200
+ }));
201
+ ```
202
+
203
+ ### 5. Error Handling (`/errors`)
204
+
205
+ Standardized error types with proper HTTP status codes.
206
+
207
+ **Usage:**
208
+ ```javascript
209
+ const {
210
+ BaseError,
211
+ RequiredPropertyError,
212
+ FetchError
213
+ } = require('@friggframework/core');
214
+
215
+ // Custom business logic error
216
+ throw new RequiredPropertyError('userId is required');
217
+
218
+ // API communication error
219
+ throw new FetchError('Failed to fetch data from external API', {
220
+ statusCode: 404,
221
+ response: errorResponse
222
+ });
223
+
224
+ // Base error with custom properties
225
+ throw new BaseError('Integration failed', {
226
+ integrationId: 'int-123',
227
+ errorCode: 'SYNC_FAILED'
228
+ });
229
+ ```
230
+
231
+ ### 6. Logging (`/logs`)
232
+
233
+ Structured logging with debug capabilities.
234
+
235
+ **Usage:**
236
+ ```javascript
237
+ const { debug, initDebugLog, flushDebugLog } = require('@friggframework/core');
238
+
239
+ // Initialize debug logging
240
+ initDebugLog('integration:slack');
241
+
242
+ // Log debug information
243
+ debug('Processing webhook payload', {
244
+ eventType: 'contact.created',
245
+ payload: webhookData
246
+ });
247
+
248
+ // Flush logs (useful in serverless environments)
249
+ await flushDebugLog();
250
+ ```
251
+
252
+ ### 7. User Management (`/user`)
253
+
254
+ Comprehensive user authentication and authorization system supporting both individual and organizational users.
255
+
256
+ **Key Classes:**
257
+ - `User` - Domain aggregate for user entities
258
+ - `UserRepository` - Data access for user operations
259
+ - Use cases: `LoginUser`, `CreateIndividualUser`, `CreateOrganizationUser`, `GetUserFromBearerToken`
260
+
261
+ **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
265
+
266
+ **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)
270
+
271
+ **Usage:**
272
+ ```javascript
273
+ const {
274
+ LoginUser,
275
+ CreateIndividualUser,
276
+ GetUserFromBearerToken,
277
+ UserRepository
278
+ } = require('@friggframework/core');
279
+
280
+ // Configure user behavior in app definition
281
+ const userConfig = {
282
+ usePassword: true,
283
+ primary: 'individual', // or 'organization'
284
+ individualUserRequired: true,
285
+ organizationUserRequired: false
286
+ };
287
+
288
+ const userRepository = new UserRepository({ userConfig });
289
+
290
+ // Create individual user
291
+ const createUser = new CreateIndividualUser({ userRepository, userConfig });
292
+ const user = await createUser.execute({
293
+ email: 'user@example.com',
294
+ username: 'john_doe',
295
+ password: 'secure_password',
296
+ appUserId: 'external_user_123' // Optional external reference
297
+ });
298
+
299
+ // Login user
300
+ const loginUser = new LoginUser({ userRepository, userConfig });
301
+ const authenticatedUser = await loginUser.execute({
302
+ username: 'john_doe',
303
+ password: 'secure_password'
304
+ });
305
+
306
+ // Token-based authentication
307
+ const getUserFromToken = new GetUserFromBearerToken({ userRepository, userConfig });
308
+ const user = await getUserFromToken.execute('Bearer eyJhbGciOiJIUzI1NiIs...');
309
+
310
+ // Access user properties
311
+ console.log('User ID:', user.getId());
312
+ console.log('Primary user:', user.getPrimaryUser());
313
+ console.log('Individual user:', user.getIndividualUser());
314
+ console.log('Organization user:', user.getOrganizationUser());
315
+ ```
316
+
317
+ ### 8. Lambda Utilities (`/lambda`)
318
+
319
+ AWS Lambda-specific utilities and helpers.
320
+
321
+ **Usage:**
322
+ ```javascript
323
+ const { TimeoutCatcher } = require('@friggframework/core');
324
+
325
+ exports.handler = async (event, context) => {
326
+ const timeoutCatcher = new TimeoutCatcher(context);
327
+
328
+ try {
329
+ // Long-running integration process
330
+ const result = await processIntegrationSync(event);
331
+ return { statusCode: 200, body: JSON.stringify(result) };
332
+ } catch (error) {
333
+ if (timeoutCatcher.isNearTimeout()) {
334
+ // Handle graceful shutdown
335
+ await saveProgressState(event);
336
+ return { statusCode: 202, body: 'Processing continues...' };
337
+ }
338
+ throw error;
339
+ }
340
+ };
341
+ ```
342
+
343
+ ## User Management & Behavior
344
+
345
+ Frigg Core provides a flexible user management system that supports various authentication patterns and user types. The system is designed around the concept of **Individual Users** (personal accounts) and **Organization Users** (business accounts), with configurable authentication methods.
346
+
347
+ ### User Configuration
348
+
349
+ User behavior is configured in the app definition, allowing you to customize authentication requirements:
350
+
351
+ ```javascript
352
+ // App Definition with User Configuration
353
+ const appDefinition = {
354
+ integrations: [HubSpotIntegration],
355
+ 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
+ }
361
+ };
362
+ ```
363
+
364
+ ### User Domain Model
365
+
366
+ The `User` class provides a rich domain model with behavior:
367
+
368
+ ```javascript
369
+ const { User } = require('@friggframework/core');
370
+
371
+ // User instance methods
372
+ const user = new User(individualUser, organizationUser, usePassword, primary);
373
+
374
+ // 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
379
+
380
+ // 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
385
+
386
+ // Configuration methods
387
+ user.setIndividualUser(individualUser)
388
+ user.setOrganizationUser(organizationUser)
389
+ ```
390
+
391
+ ### Database Models
392
+
393
+ The user system uses MongoDB with Mongoose for data persistence:
394
+
44
395
  ```javascript
45
- const { encrypt, decrypt } = require('@friggframework/core/encrypt');
46
- const { logInfo } = require('@friggframework/core/logs');
396
+ // Individual User Schema
397
+ {
398
+ email: String,
399
+ username: { type: String, unique: true },
400
+ hashword: String, // Encrypted password
401
+ appUserId: String, // External app reference
402
+ organizationUser: ObjectId // Reference to organization
403
+ }
47
404
 
48
- const secret = 'mySecret';
49
- const encrypted = encrypt(secret);
50
- const decrypted = decrypt(encrypted);
405
+ // Organization User Schema
406
+ {
407
+ name: String,
408
+ appOrgId: String, // External organization reference
409
+ domain: String,
410
+ settings: Object
411
+ }
51
412
 
52
- logInfo(`Encrypted: ${encrypted}`);
53
- logInfo(`Decrypted: ${decrypted}`);
413
+ // Session Token Schema
414
+ {
415
+ user: ObjectId, // Reference to user
416
+ token: String, // Encrypted token
417
+ expires: Date,
418
+ created: Date
419
+ }
54
420
  ```
55
421
 
56
- ## Modules
422
+ ### Security Features
423
+
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
429
+
430
+ ## Hexagonal Architecture
431
+
432
+ ### Use Case Pattern
57
433
 
58
- The frigg-core package is organized into several modules:
434
+ Each business operation is encapsulated in a use case class:
435
+
436
+ ```javascript
437
+ class UpdateIntegrationStatus {
438
+ constructor({ integrationRepository }) {
439
+ this.integrationRepository = integrationRepository;
440
+ }
441
+
442
+ async execute(integrationId, newStatus) {
443
+ // Business logic validation
444
+ if (!['ENABLED', 'DISABLED', 'ERROR'].includes(newStatus)) {
445
+ throw new Error('Invalid status');
446
+ }
447
+
448
+ // Domain operation
449
+ const integration = await this.integrationRepository.findById(integrationId);
450
+ if (!integration) {
451
+ throw new Error('Integration not found');
452
+ }
453
+
454
+ // Update and persist
455
+ integration.status = newStatus;
456
+ integration.updatedAt = new Date();
457
+
458
+ return await this.integrationRepository.save(integration);
459
+ }
460
+ }
461
+ ```
59
462
 
60
- - **Associations**: @friggframework/core/associations
61
- - **Database**: @friggframework/core/database
62
- - **Encryption**: @friggframework/core/encrypt
63
- - **Errors**: @friggframework/core/errors
64
- - **Integrations**: @friggframework/core/integrations
65
- - **Lambda**: @friggframework/core/lambda
66
- - **Logs**: @friggframework/core/logs
67
- - **Module Plugin**: @friggframework/core/module-plugin
68
- - **Syncs**: @friggframework/core/syncs
69
- - **Infrastructure**: @friggframework/core/infrastructure
463
+ ### Repository Pattern
70
464
 
465
+ Data access is abstracted through repositories:
466
+
467
+ ```javascript
468
+ class IntegrationRepository {
469
+ async findById(id) {
470
+ return await IntegrationModel.findById(id);
471
+ }
472
+
473
+ async findByUserId(userId) {
474
+ return await IntegrationModel.find({ userId, deletedAt: null });
475
+ }
476
+
477
+ async save(integration) {
478
+ return await integration.save();
479
+ }
480
+
481
+ async createIntegration(entities, userId, config) {
482
+ const integration = new IntegrationModel({
483
+ entitiesIds: entities,
484
+ userId,
485
+ config,
486
+ status: 'NEW',
487
+ createdAt: new Date()
488
+ });
489
+ return await integration.save();
490
+ }
491
+ }
492
+ ```
493
+
494
+ ### Domain Aggregates
495
+
496
+ Complex business objects with behavior:
497
+
498
+ ```javascript
499
+ const Integration = new Proxy(class {}, {
500
+ construct(target, args) {
501
+ const [params] = args;
502
+ const instance = new params.integrationClass(params);
503
+
504
+ // Attach domain properties
505
+ Object.assign(instance, {
506
+ id: params.id,
507
+ userId: params.userId,
508
+ entities: params.entities,
509
+ config: params.config,
510
+ status: params.status,
511
+ modules: params.modules
512
+ });
513
+
514
+ return instance;
515
+ }
516
+ });
517
+ ```
518
+
519
+ ## Usage Examples
520
+
521
+ ### Real-World HubSpot Integration Example
522
+
523
+ Here's a complete, production-ready HubSpot integration that demonstrates advanced Frigg features:
524
+
525
+ ```javascript
526
+ const {
527
+ get,
528
+ IntegrationBase,
529
+ WebsocketConnection,
530
+ } = require('@friggframework/core');
531
+ const FriggConstants = require('../utils/constants');
532
+ const hubspot = require('@friggframework/api-module-hubspot');
533
+ const testRouter = require('../testRouter');
534
+ const extensions = require('../extensions');
535
+
536
+ class HubSpotIntegration extends IntegrationBase {
537
+ static Definition = {
538
+ name: 'hubspot',
539
+ version: '1.0.0',
540
+ supportedVersions: ['1.0.0'],
541
+ hasUserConfig: true,
542
+
543
+ display: {
544
+ label: 'HubSpot',
545
+ description: hubspot.Config.description,
546
+ category: 'Sales & CRM, Marketing',
547
+ detailsUrl: 'https://hubspot.com',
548
+ icon: hubspot.Config.logoUrl,
549
+ },
550
+ modules: {
551
+ hubspot: {
552
+ definition: hubspot.Definition,
553
+ },
554
+ },
555
+ // Express routes for webhook endpoints and custom APIs
556
+ routes: [
557
+ {
558
+ path: '/hubspot/webhooks',
559
+ method: 'POST',
560
+ event: 'HUBSPOT_WEBHOOK',
561
+ },
562
+ testRouter,
563
+ ],
564
+ };
565
+
566
+ constructor() {
567
+ super();
568
+
569
+ // Define event handlers for various integration actions
570
+ this.events = {
571
+ // Webhook handler with real-time WebSocket broadcasting
572
+ HUBSPOT_WEBHOOK: {
573
+ handler: async ({ data, context }) => {
574
+ console.log('Received HubSpot webhook:', data);
575
+
576
+ // Broadcast to all connected WebSocket clients
577
+ const activeConnections = await WebsocketConnection.getActiveConnections();
578
+ const message = JSON.stringify({
579
+ type: 'HUBSPOT_WEBHOOK',
580
+ data,
581
+ });
582
+
583
+ activeConnections.forEach((connection) => {
584
+ connection.send(message);
585
+ });
586
+ },
587
+ },
588
+
589
+ // User action: Get sample data with formatted table output
590
+ [FriggConstants.defaultEvents.GET_SAMPLE_DATA]: {
591
+ type: FriggConstants.eventTypes.USER_ACTION,
592
+ handler: this.getSampleData,
593
+ title: 'Get Sample Data',
594
+ description: 'Get sample data from HubSpot and display in a formatted table',
595
+ userActionType: 'QUICK_ACTION',
596
+ },
597
+
598
+ // User action: List available objects
599
+ GET_OBJECT_LIST: {
600
+ type: FriggConstants.eventTypes.USER_ACTION,
601
+ handler: this.getObjectList,
602
+ title: 'Get Object List',
603
+ description: 'Get list of available HubSpot objects',
604
+ userActionType: 'DATA',
605
+ },
606
+
607
+ // User action: Create records with dynamic forms
608
+ CREATE_RECORD: {
609
+ type: FriggConstants.eventTypes.USER_ACTION,
610
+ handler: this.createRecord,
611
+ title: 'Create Record',
612
+ description: 'Create a new record in HubSpot',
613
+ userActionType: 'DATA',
614
+ },
615
+ };
616
+
617
+ // Extension system for modular functionality
618
+ this.extensions = {
619
+ hubspotWebhooks: {
620
+ extension: extensions.hubspotWebhooks,
621
+ handlers: {
622
+ WEBHOOK_EVENT: this.handleWebhookEvent,
623
+ },
624
+ },
625
+ };
626
+ }
627
+
628
+ // Business logic: Fetch and format sample data
629
+ async getSampleData({ objectName }) {
630
+ let res;
631
+ switch (objectName) {
632
+ case 'deals':
633
+ res = await this.hubspot.api.searchDeals({
634
+ properties: ['dealname,amount,closedate'],
635
+ });
636
+ break;
637
+ case 'contacts':
638
+ res = await this.hubspot.api.listContacts({
639
+ after: 0,
640
+ properties: 'firstname,lastname,email',
641
+ });
642
+ break;
643
+ case 'companies':
644
+ res = await this.hubspot.api.searchCompanies({
645
+ properties: ['name,website,email'],
646
+ limit: 100,
647
+ });
648
+ break;
649
+ default:
650
+ throw new Error(`Unsupported object type: ${objectName}`);
651
+ }
652
+
653
+ const portalId = this.hubspot.entity.externalId;
654
+
655
+ // Format data with HubSpot record links
656
+ const formatted = res.results.map((item) => {
657
+ const formattedItem = {
658
+ linkToRecord: `https://app.hubspot.com/contacts/${portalId}/${objectName}/${item.id}/`,
659
+ id: item.id,
660
+ };
661
+
662
+ // Clean and format properties
663
+ for (const [key, value] of Object.entries(item.properties)) {
664
+ if (value !== null && value !== undefined && value !== '') {
665
+ formattedItem[key] = value;
666
+ }
667
+ }
668
+ delete formattedItem.hs_object_id;
669
+
670
+ return formattedItem;
671
+ });
672
+
673
+ return { label: objectName, data: formatted };
674
+ }
675
+
676
+ // Return available HubSpot object types
677
+ async getObjectList() {
678
+ return [
679
+ { key: 'deals', label: 'Deals' },
680
+ { key: 'contacts', label: 'Contacts' },
681
+ { key: 'companies', label: 'Companies' },
682
+ ];
683
+ }
684
+
685
+ // Create records based on object type
686
+ async createRecord(args) {
687
+ let res;
688
+ const objectType = args.objectType;
689
+ delete args.objectType;
690
+
691
+ switch (objectType.toLowerCase()) {
692
+ case 'deal':
693
+ res = await this.hubspot.api.createDeal({ ...args });
694
+ break;
695
+ case 'company':
696
+ res = await this.hubspot.api.createCompany({ ...args });
697
+ break;
698
+ case 'contact':
699
+ res = await this.hubspot.api.createContact({ ...args });
700
+ break;
701
+ default:
702
+ throw new Error(`Unsupported object type: ${objectType}`);
703
+ }
704
+ return { data: res };
705
+ }
706
+
707
+ // Dynamic form generation based on action and context
708
+ async getActionOptions({ actionId, data }) {
709
+ switch (actionId) {
710
+ case 'CREATE_RECORD':
711
+ let jsonSchema = {
712
+ type: 'object',
713
+ properties: {
714
+ objectType: {
715
+ type: 'string',
716
+ title: 'Object Type',
717
+ },
718
+ },
719
+ required: [],
720
+ };
721
+
722
+ let uiSchema = {
723
+ type: 'HorizontalLayout',
724
+ elements: [
725
+ {
726
+ type: 'Control',
727
+ scope: '#/properties/objectType',
728
+ rule: { effect: 'HIDE', condition: {} },
729
+ },
730
+ ],
731
+ };
732
+
733
+ // Generate form fields based on object type
734
+ switch (data.name.toLowerCase()) {
735
+ case 'deal':
736
+ jsonSchema.properties = {
737
+ ...jsonSchema.properties,
738
+ dealname: { type: 'string', title: 'Deal Name' },
739
+ amount: { type: 'number', title: 'Amount' },
740
+ };
741
+ jsonSchema.required = ['dealname', 'amount'];
742
+ uiSchema.elements.push(
743
+ { type: 'Control', scope: '#/properties/dealname' },
744
+ { type: 'Control', scope: '#/properties/amount' }
745
+ );
746
+ break;
747
+
748
+ case 'company':
749
+ jsonSchema.properties = {
750
+ ...jsonSchema.properties,
751
+ name: { type: 'string', title: 'Company Name' },
752
+ website: { type: 'string', title: 'Website URL' },
753
+ };
754
+ jsonSchema.required = ['name', 'website'];
755
+ uiSchema.elements.push(
756
+ { type: 'Control', scope: '#/properties/name' },
757
+ { type: 'Control', scope: '#/properties/website' }
758
+ );
759
+ break;
760
+
761
+ case 'contact':
762
+ jsonSchema.properties = {
763
+ ...jsonSchema.properties,
764
+ firstname: { type: 'string', title: 'First Name' },
765
+ lastname: { type: 'string', title: 'Last Name' },
766
+ email: { type: 'string', title: 'Email Address' },
767
+ };
768
+ jsonSchema.required = ['firstname', 'lastname', 'email'];
769
+ uiSchema.elements.push(
770
+ { type: 'Control', scope: '#/properties/firstname' },
771
+ { type: 'Control', scope: '#/properties/lastname' },
772
+ { type: 'Control', scope: '#/properties/email' }
773
+ );
774
+ break;
775
+
776
+ default:
777
+ throw new Error(`Unsupported object type: ${data.name}`);
778
+ }
779
+
780
+ return {
781
+ jsonSchema,
782
+ uiSchema,
783
+ data: { objectType: data.name },
784
+ };
785
+ }
786
+ return null;
787
+ }
788
+
789
+ async getConfigOptions() {
790
+ // Return configuration options for the integration
791
+ return {};
792
+ }
793
+ }
794
+
795
+ module.exports = HubSpotIntegration;
796
+ ```
71
797
 
72
- Each module provides specific functionality and can be imported individually as needed.
798
+ index.js
799
+ ```js
800
+ const HubSpotIntegration = require('./src/integrations/HubSpotIntegration');
73
801
 
74
- ## Contributing
802
+ const appDefinition = {
803
+ integrations: [
804
+ HubSpotIntegration,
805
+ ],
806
+ user: {
807
+ usePassword: true,
808
+ primary: 'individual',
809
+ organizationUserRequired: true,
810
+ individualUserRequired: true,
811
+ }
812
+ }
75
813
 
76
- We welcome contributions from the community! Please read our contributing guide to get started. Make sure to follow our code of conduct and use the provided pull request template.
814
+ module.exports = {
815
+ Definition: appDefinition,
816
+ }
817
+
818
+ ```
819
+
820
+
821
+ ### Key Features Demonstrated
822
+
823
+ This real-world example showcases:
824
+
825
+ **🔄 Webhook Integration**: Real-time event processing with WebSocket broadcasting
826
+ **📊 User Actions**: Interactive data operations with dynamic form generation
827
+ **🎯 API Module Integration**: Direct use of `@friggframework/api-module-hubspot`
828
+ **🛠 Extension System**: Modular functionality through extensions
829
+ **📝 Dynamic Forms**: JSON Schema-based form generation for different object types
830
+ **🔗 Deep Linking**: Direct links to HubSpot records in formatted data
831
+ **⚡ Real-time Updates**: WebSocket connections for live data streaming
832
+
833
+
834
+ ## Testing
835
+
836
+ ### Running Tests
837
+
838
+ ```bash
839
+ # Run all tests
840
+ npm test
841
+
842
+ # Run specific test file
843
+ npm test -- --testPathPattern="integration.test.js"
844
+ ```
845
+
846
+ ### Test Structure
847
+
848
+ The core package uses a comprehensive testing approach:
849
+
850
+ ```javascript
851
+ // Example test structure
852
+ describe('CreateIntegration Use-Case', () => {
853
+ let integrationRepository;
854
+ let moduleFactory;
855
+ let useCase;
856
+
857
+ beforeEach(() => {
858
+ integrationRepository = new TestIntegrationRepository();
859
+ moduleFactory = new TestModuleFactory();
860
+ useCase = new CreateIntegration({
861
+ integrationRepository,
862
+ integrationClasses: [TestIntegration],
863
+ moduleFactory
864
+ });
865
+ });
866
+
867
+ describe('happy path', () => {
868
+ it('creates an integration and returns DTO', async () => {
869
+ const result = await useCase.execute(['entity-1'], 'user-1', { type: 'test' });
870
+ expect(result.id).toBeDefined();
871
+ expect(result.status).toBe('NEW');
872
+ });
873
+ });
874
+
875
+ describe('error cases', () => {
876
+ 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');
879
+ });
880
+ });
881
+ });
882
+ ```
883
+
884
+ ### Test Doubles
885
+
886
+ The framework provides test doubles for external dependencies:
887
+
888
+ ```javascript
889
+ const { TestIntegrationRepository, TestModuleFactory } = require('@friggframework/core/test');
890
+
891
+ // Mock repository for testing
892
+ const testRepo = new TestIntegrationRepository();
893
+ testRepo.addMockIntegration({ id: 'test-123', userId: 'user-1' });
894
+
895
+ // Mock module factory
896
+ const testFactory = new TestModuleFactory();
897
+ testFactory.addMockModule('hubspot', mockHubSpotModule);
898
+ ```
899
+
900
+ ## Development
901
+
902
+ ### Project Structure
903
+
904
+ ```
905
+ packages/core/
906
+ ├── integrations/ # Integration domain logic
907
+ │ ├── use-cases/ # Business use cases
908
+ │ ├── tests/ # Integration tests
909
+ │ └── integration-base.js # Base integration class
910
+ ├── modules/ # API module system
911
+ │ ├── requester/ # HTTP clients
912
+ │ └── use-cases/ # Module management
913
+ ├── database/ # Data persistence
914
+ ├── encrypt/ # Encryption utilities
915
+ ├── errors/ # Error definitions
916
+ ├── logs/ # Logging system
917
+ └── lambda/ # Serverless utilities
918
+ ```
919
+
920
+ ### Adding New Components
921
+
922
+ 1. **Create the component**: Follow the established patterns
923
+ 2. **Add tests**: Comprehensive test coverage required
924
+ 3. **Export from index.js**: Make it available to consumers
925
+ 4. **Update documentation**: Keep README current
926
+
927
+ ### Code Style
928
+
929
+ ```bash
930
+ # Format code
931
+ npm run lint:fix
932
+
933
+ # Check linting
934
+ npm run lint
935
+ ```
936
+
937
+ ## API Reference
938
+
939
+ ### Core Exports
940
+
941
+ ```javascript
942
+ const {
943
+ // Integrations
944
+ IntegrationBase,
945
+ IntegrationModel,
946
+ CreateIntegration,
947
+ UpdateIntegration,
948
+ DeleteIntegration,
949
+
950
+ // Modules
951
+ OAuth2Requester,
952
+ ApiKeyRequester,
953
+ Credential,
954
+ Entity,
955
+ // Database
956
+ connectToDatabase,
957
+ mongoose,
958
+ UserModel,
959
+
960
+ // Utilities
961
+ Encrypt,
962
+ Cryptor,
963
+ BaseError,
964
+ debug,
965
+ TimeoutCatcher
966
+ } = require('@friggframework/core');
967
+ ```
968
+
969
+ ### Environment Configuration
970
+
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) |
77
978
 
78
979
  ## License
79
980
 
80
- This project is licensed under the MIT License. See the LICENSE.md file for details.
981
+ This project is licensed under the MIT License - see the [LICENSE.md](../../LICENSE.md) file for details.
81
982
 
82
983
  ---
83
- Thank you for using Frigg Core! If you have any questions or need further assistance, feel free to reach out to our community on Slack or check out our GitHub issues page.
984
+
985
+ ## Support
986
+
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)
991
+
992
+ Built with ❤️ by the Frigg Framework team.