@friggframework/core 2.0.0-next.41 → 2.0.0-next.43

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 (197) hide show
  1. package/CLAUDE.md +693 -0
  2. package/README.md +931 -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 +160 -0
  7. package/application/commands/integration-commands.test.js +123 -0
  8. package/application/commands/user-commands.js +213 -0
  9. package/application/index.js +69 -0
  10. package/core/CLAUDE.md +690 -0
  11. package/core/create-handler.js +0 -6
  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 +301 -0
  15. package/credential/repositories/credential-repository-postgres.js +307 -0
  16. package/credential/repositories/credential-repository.js +307 -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/config.js +117 -0
  20. package/database/encryption/README.md +683 -0
  21. package/database/encryption/encryption-integration.test.js +553 -0
  22. package/database/encryption/encryption-schema-registry.js +141 -0
  23. package/database/encryption/encryption-schema-registry.test.js +392 -0
  24. package/database/encryption/field-encryption-service.js +226 -0
  25. package/database/encryption/field-encryption-service.test.js +525 -0
  26. package/database/encryption/logger.js +79 -0
  27. package/database/encryption/mongo-decryption-fix-verification.test.js +348 -0
  28. package/database/encryption/postgres-decryption-fix-verification.test.js +371 -0
  29. package/database/encryption/postgres-relation-decryption.test.js +245 -0
  30. package/database/encryption/prisma-encryption-extension.js +222 -0
  31. package/database/encryption/prisma-encryption-extension.test.js +439 -0
  32. package/database/index.js +25 -12
  33. package/database/models/readme.md +1 -0
  34. package/database/prisma.js +162 -0
  35. package/database/repositories/health-check-repository-factory.js +38 -0
  36. package/database/repositories/health-check-repository-interface.js +86 -0
  37. package/database/repositories/health-check-repository-mongodb.js +72 -0
  38. package/database/repositories/health-check-repository-postgres.js +75 -0
  39. package/database/repositories/health-check-repository.js +108 -0
  40. package/database/use-cases/check-database-health-use-case.js +34 -0
  41. package/database/use-cases/check-encryption-health-use-case.js +82 -0
  42. package/database/use-cases/test-encryption-use-case.js +252 -0
  43. package/encrypt/Cryptor.js +20 -152
  44. package/encrypt/index.js +1 -2
  45. package/encrypt/test-encrypt.js +0 -2
  46. package/handlers/app-definition-loader.js +38 -0
  47. package/handlers/app-handler-helpers.js +0 -3
  48. package/handlers/auth-flow.integration.test.js +147 -0
  49. package/handlers/backend-utils.js +25 -45
  50. package/handlers/integration-event-dispatcher.js +54 -0
  51. package/handlers/integration-event-dispatcher.test.js +141 -0
  52. package/handlers/routers/HEALTHCHECK.md +103 -1
  53. package/handlers/routers/auth.js +3 -14
  54. package/handlers/routers/health.js +63 -424
  55. package/handlers/routers/health.test.js +7 -0
  56. package/handlers/routers/integration-defined-routers.js +8 -5
  57. package/handlers/routers/user.js +27 -5
  58. package/handlers/routers/websocket.js +5 -3
  59. package/handlers/use-cases/check-external-apis-health-use-case.js +81 -0
  60. package/handlers/use-cases/check-integrations-health-use-case.js +32 -0
  61. package/handlers/workers/integration-defined-workers.js +6 -3
  62. package/index.js +45 -22
  63. package/integrations/index.js +12 -10
  64. package/integrations/integration-base.js +224 -53
  65. package/integrations/integration-router.js +386 -178
  66. package/integrations/options.js +1 -1
  67. package/integrations/repositories/integration-mapping-repository-factory.js +50 -0
  68. package/integrations/repositories/integration-mapping-repository-interface.js +106 -0
  69. package/integrations/repositories/integration-mapping-repository-mongo.js +161 -0
  70. package/integrations/repositories/integration-mapping-repository-postgres.js +227 -0
  71. package/integrations/repositories/integration-mapping-repository.js +156 -0
  72. package/integrations/repositories/integration-repository-factory.js +44 -0
  73. package/integrations/repositories/integration-repository-interface.js +115 -0
  74. package/integrations/repositories/integration-repository-mongo.js +271 -0
  75. package/integrations/repositories/integration-repository-postgres.js +319 -0
  76. package/integrations/tests/doubles/dummy-integration-class.js +90 -0
  77. package/integrations/tests/doubles/test-integration-repository.js +99 -0
  78. package/integrations/tests/use-cases/create-integration.test.js +131 -0
  79. package/integrations/tests/use-cases/delete-integration-for-user.test.js +150 -0
  80. package/integrations/tests/use-cases/find-integration-context-by-external-entity-id.test.js +92 -0
  81. package/integrations/tests/use-cases/get-integration-for-user.test.js +150 -0
  82. package/integrations/tests/use-cases/get-integration-instance.test.js +176 -0
  83. package/integrations/tests/use-cases/get-integrations-for-user.test.js +176 -0
  84. package/integrations/tests/use-cases/get-possible-integrations.test.js +188 -0
  85. package/integrations/tests/use-cases/update-integration-messages.test.js +142 -0
  86. package/integrations/tests/use-cases/update-integration-status.test.js +103 -0
  87. package/integrations/tests/use-cases/update-integration.test.js +141 -0
  88. package/integrations/use-cases/create-integration.js +83 -0
  89. package/integrations/use-cases/delete-integration-for-user.js +73 -0
  90. package/integrations/use-cases/find-integration-context-by-external-entity-id.js +72 -0
  91. package/integrations/use-cases/get-integration-for-user.js +78 -0
  92. package/integrations/use-cases/get-integration-instance-by-definition.js +67 -0
  93. package/integrations/use-cases/get-integration-instance.js +83 -0
  94. package/integrations/use-cases/get-integrations-for-user.js +87 -0
  95. package/integrations/use-cases/get-possible-integrations.js +27 -0
  96. package/integrations/use-cases/index.js +11 -0
  97. package/integrations/use-cases/load-integration-context-full.test.js +329 -0
  98. package/integrations/use-cases/load-integration-context.js +71 -0
  99. package/integrations/use-cases/load-integration-context.test.js +114 -0
  100. package/integrations/use-cases/update-integration-messages.js +44 -0
  101. package/integrations/use-cases/update-integration-status.js +32 -0
  102. package/integrations/use-cases/update-integration.js +93 -0
  103. package/integrations/utils/map-integration-dto.js +36 -0
  104. package/jest-global-setup-noop.js +3 -0
  105. package/jest-global-teardown-noop.js +3 -0
  106. package/{module-plugin → modules}/entity.js +1 -0
  107. package/{module-plugin → modules}/index.js +0 -8
  108. package/modules/module-factory.js +56 -0
  109. package/modules/module-hydration.test.js +205 -0
  110. package/modules/module.js +221 -0
  111. package/modules/repositories/module-repository-factory.js +33 -0
  112. package/modules/repositories/module-repository-interface.js +129 -0
  113. package/modules/repositories/module-repository-mongo.js +386 -0
  114. package/modules/repositories/module-repository-postgres.js +437 -0
  115. package/modules/repositories/module-repository.js +327 -0
  116. package/{module-plugin → modules}/test/mock-api/api.js +8 -3
  117. package/{module-plugin → modules}/test/mock-api/definition.js +12 -8
  118. package/modules/tests/doubles/test-module-factory.js +16 -0
  119. package/modules/tests/doubles/test-module-repository.js +39 -0
  120. package/modules/use-cases/get-entities-for-user.js +32 -0
  121. package/modules/use-cases/get-entity-options-by-id.js +59 -0
  122. package/modules/use-cases/get-entity-options-by-type.js +34 -0
  123. package/modules/use-cases/get-module-instance-from-type.js +31 -0
  124. package/modules/use-cases/get-module.js +56 -0
  125. package/modules/use-cases/process-authorization-callback.js +122 -0
  126. package/modules/use-cases/refresh-entity-options.js +59 -0
  127. package/modules/use-cases/test-module-auth.js +55 -0
  128. package/modules/utils/map-module-dto.js +18 -0
  129. package/package.json +14 -6
  130. package/prisma-mongodb/schema.prisma +318 -0
  131. package/prisma-postgresql/migrations/20250930193005_init/migration.sql +315 -0
  132. package/prisma-postgresql/migrations/20251006135218_init/migration.sql +9 -0
  133. package/prisma-postgresql/migrations/20251010000000_remove_unused_entity_reference_map/migration.sql +3 -0
  134. package/prisma-postgresql/migrations/migration_lock.toml +3 -0
  135. package/prisma-postgresql/schema.prisma +300 -0
  136. package/syncs/manager.js +468 -443
  137. package/syncs/repositories/sync-repository-factory.js +38 -0
  138. package/syncs/repositories/sync-repository-interface.js +109 -0
  139. package/syncs/repositories/sync-repository-mongo.js +239 -0
  140. package/syncs/repositories/sync-repository-postgres.js +319 -0
  141. package/syncs/sync.js +0 -1
  142. package/token/repositories/token-repository-factory.js +33 -0
  143. package/token/repositories/token-repository-interface.js +131 -0
  144. package/token/repositories/token-repository-mongo.js +212 -0
  145. package/token/repositories/token-repository-postgres.js +257 -0
  146. package/token/repositories/token-repository.js +219 -0
  147. package/types/integrations/index.d.ts +2 -6
  148. package/types/module-plugin/index.d.ts +5 -57
  149. package/types/syncs/index.d.ts +0 -2
  150. package/user/repositories/user-repository-factory.js +46 -0
  151. package/user/repositories/user-repository-interface.js +198 -0
  152. package/user/repositories/user-repository-mongo.js +250 -0
  153. package/user/repositories/user-repository-postgres.js +311 -0
  154. package/user/tests/doubles/test-user-repository.js +72 -0
  155. package/user/tests/use-cases/create-individual-user.test.js +24 -0
  156. package/user/tests/use-cases/create-organization-user.test.js +28 -0
  157. package/user/tests/use-cases/create-token-for-user-id.test.js +19 -0
  158. package/user/tests/use-cases/get-user-from-bearer-token.test.js +64 -0
  159. package/user/tests/use-cases/login-user.test.js +140 -0
  160. package/user/use-cases/create-individual-user.js +61 -0
  161. package/user/use-cases/create-organization-user.js +47 -0
  162. package/user/use-cases/create-token-for-user-id.js +30 -0
  163. package/user/use-cases/get-user-from-bearer-token.js +77 -0
  164. package/user/use-cases/login-user.js +122 -0
  165. package/user/user.js +77 -0
  166. package/websocket/repositories/websocket-connection-repository-factory.js +37 -0
  167. package/websocket/repositories/websocket-connection-repository-interface.js +106 -0
  168. package/websocket/repositories/websocket-connection-repository-mongo.js +155 -0
  169. package/websocket/repositories/websocket-connection-repository-postgres.js +195 -0
  170. package/websocket/repositories/websocket-connection-repository.js +160 -0
  171. package/database/models/State.js +0 -9
  172. package/database/models/Token.js +0 -70
  173. package/database/mongo.js +0 -171
  174. package/encrypt/Cryptor.test.js +0 -32
  175. package/encrypt/encrypt.js +0 -104
  176. package/encrypt/encrypt.test.js +0 -1069
  177. package/handlers/routers/middleware/loadUser.js +0 -15
  178. package/handlers/routers/middleware/requireLoggedInUser.js +0 -12
  179. package/integrations/create-frigg-backend.js +0 -31
  180. package/integrations/integration-factory.js +0 -251
  181. package/integrations/integration-mapping.js +0 -43
  182. package/integrations/integration-model.js +0 -46
  183. package/integrations/integration-user.js +0 -144
  184. package/integrations/test/integration-base.test.js +0 -144
  185. package/module-plugin/auther.js +0 -393
  186. package/module-plugin/credential.js +0 -22
  187. package/module-plugin/entity-manager.js +0 -70
  188. package/module-plugin/manager.js +0 -169
  189. package/module-plugin/module-factory.js +0 -61
  190. package/module-plugin/test/auther.test.js +0 -97
  191. /package/{module-plugin → modules}/ModuleConstants.js +0 -0
  192. /package/{module-plugin → modules}/requester/api-key.js +0 -0
  193. /package/{module-plugin → modules}/requester/basic.js +0 -0
  194. /package/{module-plugin → modules}/requester/oauth-2.js +0 -0
  195. /package/{module-plugin → modules}/requester/requester.js +0 -0
  196. /package/{module-plugin → modules}/requester/requester.test.js +0 -0
  197. /package/{module-plugin → modules}/test/mock-api/mocks/hubspot.js +0 -0
package/database/mongo.js DELETED
@@ -1,171 +0,0 @@
1
- // Best Practices Connecting from AWS Lambda:
2
- // https://dev.to/adnanrahic/building-a-serverless-rest-api-with-nodejs-and-mongodb-43db
3
- // https://mongoosejs.com/docs/lambda.html
4
- // https://www.mongodb.com/blog/post/optimizing-aws-lambda-performance-with-mongodb-atlas-and-nodejs
5
- const { Encrypt } = require('../encrypt');
6
- const { mongoose } = require('./mongoose');
7
- const { debug, flushDebugLog } = require('../logs');
8
- const { findNearestBackendPackageJson } = require('../utils');
9
- const path = require('path');
10
- const fs = require('fs');
11
-
12
- mongoose.plugin(Encrypt);
13
- mongoose.set('applyPluginsToDiscriminators', true); // Needed for LHEncrypt
14
-
15
- // Load app definition to check for DocumentDB configuration
16
- let appDefinition = {};
17
- let mongoConfig = {
18
- useNewUrlParser: true,
19
- bufferCommands: false, // Disable mongoose buffering
20
- autoCreate: false, // Disable because auto creation does not work without buffering
21
- useUnifiedTopology: true,
22
- serverSelectionTimeoutMS: 5000,
23
- };
24
-
25
- const checkIsConnected = () => mongoose.connection?.readyState > 0;
26
-
27
- const connectToDatabase = async () => {
28
- if (checkIsConnected()) {
29
- debug('=> using existing database connection');
30
- return;
31
- }
32
-
33
- console.log('🔗 Connecting to database...');
34
-
35
- // Load appDefinition inside the function
36
- try {
37
- console.log(
38
- '🔍 Loading app definition for DocumentDB configuration...'
39
- );
40
-
41
- const backendPath = findNearestBackendPackageJson();
42
- if (!backendPath) {
43
- throw new Error('Could not find backend package.json');
44
- }
45
-
46
- const backendDir = path.dirname(backendPath);
47
- const backendFilePath = path.join(backendDir, 'index.js');
48
- if (!fs.existsSync(backendFilePath)) {
49
- throw new Error('Could not find index.js');
50
- }
51
-
52
- const backend = require(backendFilePath);
53
- appDefinition = backend.Definition;
54
-
55
- console.log('📁 AppDefinition content:', JSON.stringify(appDefinition));
56
-
57
- // Add DocumentDB TLS configuration if enabled
58
- if (appDefinition.database?.documentDB?.enable === true) {
59
- console.log('📄 DocumentDB configuration detected, enabling TLS');
60
- console.log('📁 Current working directory:', process.cwd());
61
- console.log(
62
- '📋 App definition database config:',
63
- JSON.stringify(appDefinition.database, null, 2)
64
- );
65
-
66
- mongoConfig.tls = true;
67
-
68
- // Set TLS CA file path if specified
69
- if (appDefinition.database.documentDB.tlsCAFile) {
70
- const tlsCAFile = appDefinition.database.documentDB.tlsCAFile;
71
-
72
- // Basic safety: reject obviously dangerous paths
73
- if (tlsCAFile.includes('..') || path.isAbsolute(tlsCAFile)) {
74
- console.warn(
75
- '⚠️ Rejecting potentially unsafe tlsCAFile path:',
76
- tlsCAFile
77
- );
78
- } else {
79
- const tlsCAFilePath = path.resolve(
80
- process.cwd(),
81
- tlsCAFile
82
- );
83
-
84
- console.log('📄 DocumentDB TLS CA file configured:');
85
- console.log(' 📎 Original path:', tlsCAFile);
86
- console.log(' 📎 Resolved path:', tlsCAFilePath);
87
- console.log(
88
- ' 📄 File exists:',
89
- fs.existsSync(tlsCAFilePath)
90
- );
91
-
92
- // Only set tlsCAFile if the file actually exists
93
- if (fs.existsSync(tlsCAFilePath)) {
94
- mongoConfig.tlsCAFile = tlsCAFilePath;
95
- console.log('✅ TLS CA file configured successfully');
96
- } else {
97
- throw new Error(
98
- `TLS CA file not found at ${tlsCAFilePath}`
99
- );
100
- }
101
-
102
- // Debug directory listing (only in development)
103
- if (process.env.NODE_ENV !== 'production') {
104
- try {
105
- console.log('📁 Current directory contents:');
106
- fs.readdirSync(process.cwd()).forEach((item) => {
107
- const stats = fs.statSync(
108
- path.join(process.cwd(), item)
109
- );
110
- console.log(
111
- ` ${
112
- stats.isDirectory() ? '📁' : '📄'
113
- } ${item}`
114
- );
115
- });
116
-
117
- const securityDir = path.join(
118
- process.cwd(),
119
- 'security'
120
- );
121
- if (fs.existsSync(securityDir)) {
122
- console.log('📁 Security directory contents:');
123
- fs.readdirSync(securityDir).forEach((item) => {
124
- console.log(` 📄 ${item}`);
125
- });
126
- } else {
127
- console.log(
128
- '❌ Security directory does not exist at:',
129
- securityDir
130
- );
131
- }
132
- } catch (error) {
133
- console.log(
134
- '❌ Error listing directory contents:',
135
- error.message
136
- );
137
- }
138
- }
139
- }
140
- }
141
- } else {
142
- console.log(
143
- '📄 DocumentDB not enabled, using standard MongoDB configuration'
144
- );
145
- }
146
- } catch (error) {
147
- console.error('❌ Error loading app definition:', error.message);
148
- debug(
149
- 'Could not load app definition for DocumentDB configuration:',
150
- error.message
151
- );
152
- }
153
-
154
- console.log('🔗 MongoDB URI:', process.env.MONGO_URI ? 'SET' : 'NOT SET');
155
- console.log('🔧 Final mongoConfig:', JSON.stringify(mongoConfig, null, 2));
156
-
157
- debug('=> using new database connection');
158
- await mongoose.connect(process.env.MONGO_URI, mongoConfig);
159
- debug('Connection state:', mongoose.STATES[mongoose.connection.readyState]);
160
- mongoose.connection.on('error', (error) => flushDebugLog(error));
161
- };
162
-
163
- const disconnectFromDatabase = async () => mongoose.disconnect();
164
-
165
- const createObjectId = () => new mongoose.Types.ObjectId();
166
-
167
- module.exports = {
168
- connectToDatabase,
169
- disconnectFromDatabase,
170
- createObjectId,
171
- };
@@ -1,32 +0,0 @@
1
- const { Cryptor } = require('./Cryptor');
2
-
3
- describe('Cryptor', () => {
4
- describe('Permutations', () => {
5
- it('calculates permutations correctly', async () => {
6
- // Given a nested field, we want all possible paths that could access it.
7
- const cryptor = new Cryptor({ fields: ['a.b.c.d', 'e'] });
8
- expect(cryptor.permutationsByField).toEqual({
9
- 'a.b.c.d': [
10
- ['a', 'b', 'c', 'd'],
11
- ['a', 'b', 'c.d'],
12
- ['a', 'b.c', 'd'],
13
- ['a', 'b.c.d'],
14
- ['a.b', 'c', 'd'],
15
- ['a.b', 'c.d'],
16
- ['a.b.c', 'd'],
17
- ['a.b.c.d'],
18
- ],
19
- e: [['e']],
20
- });
21
- });
22
- });
23
-
24
- describe('Keys', () => {
25
- it('raises error on missing environment', () => {
26
- const cryptor = new Cryptor({ fields: ['a.b.c.d', 'e'] });
27
- expect(cryptor.getKeyFromEnvironment).toThrow(
28
- 'No encryption key found with ID "undefined"'
29
- );
30
- });
31
- });
32
- });
@@ -1,104 +0,0 @@
1
- const { Cryptor } = require('./Cryptor');
2
-
3
- const updateOneEvents = [
4
- 'updateOne',
5
- 'replaceOne',
6
- 'findOneAndUpdate',
7
- 'findOneAndReplace',
8
- ];
9
- const findOneEvents = [
10
- 'findOne',
11
- 'findOneAndDelete',
12
- 'findOneAndRemove',
13
- 'findOneAndUpdate',
14
- 'findOneAndReplace',
15
- ];
16
-
17
- const shouldBypassEncryption = (STAGE) => {
18
- const defaultBypassStages = ['dev', 'test', 'local'];
19
- return defaultBypassStages.includes(STAGE);
20
- };
21
-
22
- function Encrypt(schema) {
23
- const { STAGE, KMS_KEY_ARN, AES_KEY_ID } = process.env;
24
-
25
- if (shouldBypassEncryption(STAGE)) {
26
- return;
27
- }
28
-
29
- const hasAES = AES_KEY_ID && AES_KEY_ID.trim() !== '';
30
- const hasKMS = KMS_KEY_ARN && KMS_KEY_ARN.trim() !== '' && !hasAES;
31
-
32
- const fields = Object.values(schema.paths)
33
- .map(({ path, options }) => (options.lhEncrypt === true ? path : ''))
34
- .filter(Boolean);
35
-
36
- if (!fields.length) {
37
- return;
38
- }
39
-
40
- const cryptor = new Cryptor({
41
- shouldUseAws: hasKMS,
42
- fields: fields,
43
- });
44
-
45
- schema.pre('save', async function encryptionPreSave() {
46
- await cryptor.encryptFieldsInDocuments([this]);
47
- });
48
-
49
- schema.pre(
50
- 'insertMany',
51
- async function encryptionPreInsertMany(_, docs, options) {
52
- if (options?.rawResult) {
53
- throw new Error(
54
- 'Raw result not supported for insertMany with Encrypt plugin'
55
- );
56
- }
57
-
58
- await cryptor.encryptFieldsInDocuments(docs);
59
- }
60
- );
61
-
62
- schema.pre(updateOneEvents, async function encryptionPreUpdateOne() {
63
- await cryptor.encryptFieldsInQuery(this);
64
- });
65
-
66
- schema.pre('updateMany', async function encryptionPreUpdateMany() {
67
- cryptor.expectNotToUpdateManyEncrypted(this.getUpdate());
68
- });
69
-
70
- schema.pre('update', async function encryptionPreUpdate() {
71
- const { multiple } = this.getOptions();
72
-
73
- if (multiple) {
74
- cryptor.expectNotToUpdateManyEncrypted(this.getUpdate());
75
- return;
76
- }
77
-
78
- await cryptor.encryptFieldsInQuery(this);
79
- });
80
-
81
- schema.post('save', async function encryptionPreSave() {
82
- await cryptor.decryptFieldsInDocuments([this]);
83
- });
84
-
85
- schema.post(findOneEvents, async function encryptionPostFindOne(doc) {
86
- const { rawResult } = this.getOptions();
87
-
88
- if (rawResult) {
89
- return;
90
- }
91
-
92
- await cryptor.decryptFieldsInDocuments([doc]);
93
- });
94
-
95
- schema.post('find', async function encryptionPostFind(docs) {
96
- await cryptor.decryptFieldsInDocuments(docs);
97
- });
98
-
99
- schema.post('insertMany', async function encryptionPostInsertMany(docs) {
100
- await cryptor.decryptFieldsInDocuments(docs);
101
- });
102
- }
103
-
104
- module.exports = { Encrypt };