@friggframework/core 2.0.0--canary.395.e3c5038.0 → 2.0.0--canary.395.f03dc2b.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 (52) hide show
  1. package/application/commands/integration-commands.test.js +7 -0
  2. package/application/commands/user-commands.js +1 -1
  3. package/credential/repositories/credential-repository-factory.js +6 -7
  4. package/credential/repositories/credential-repository-mongo.js +2 -2
  5. package/credential/repositories/credential-repository-postgres.js +2 -2
  6. package/database/config.js +93 -6
  7. package/database/encryption/README.md +97 -5
  8. package/database/encryption/encryption-integration.test.js +149 -109
  9. package/database/encryption/encryption-schema-registry.js +11 -52
  10. package/database/encryption/field-encryption-service.js +39 -29
  11. package/database/encryption/logger.js +79 -0
  12. package/database/prisma.js +15 -17
  13. package/database/repositories/health-check-repository-factory.js +10 -16
  14. package/database/repositories/health-check-repository-mongodb.js +72 -0
  15. package/database/repositories/health-check-repository-postgres.js +75 -0
  16. package/handlers/auth-flow.integration.test.js +7 -0
  17. package/handlers/integration-event-dispatcher.test.js +7 -0
  18. package/handlers/routers/health.test.js +7 -0
  19. package/handlers/routers/user.js +1 -1
  20. package/integrations/integration-router.js +1 -1
  21. package/integrations/repositories/integration-mapping-repository-factory.js +7 -7
  22. package/integrations/repositories/integration-mapping-repository-mongo.js +2 -2
  23. package/integrations/repositories/integration-mapping-repository-postgres.js +2 -2
  24. package/integrations/repositories/integration-repository-factory.js +8 -10
  25. package/integrations/repositories/integration-repository-mongo.js +2 -2
  26. package/integrations/repositories/integration-repository-postgres.js +2 -2
  27. package/integrations/tests/use-cases/create-integration.test.js +7 -0
  28. package/integrations/tests/use-cases/delete-integration-for-user.test.js +7 -0
  29. package/integrations/tests/use-cases/get-integration-for-user.test.js +7 -0
  30. package/integrations/tests/use-cases/get-integration-instance.test.js +7 -0
  31. package/integrations/tests/use-cases/get-integrations-for-user.test.js +7 -0
  32. package/integrations/tests/use-cases/update-integration.test.js +7 -0
  33. package/integrations/use-cases/load-integration-context-full.test.js +9 -2
  34. package/modules/module-hydration.test.js +7 -0
  35. package/modules/repositories/module-repository-factory.js +6 -19
  36. package/modules/repositories/module-repository-mongo.js +2 -2
  37. package/modules/repositories/module-repository-postgres.js +2 -2
  38. package/package.json +5 -5
  39. package/syncs/repositories/sync-repository-factory.js +6 -14
  40. package/syncs/repositories/sync-repository-mongo.js +2 -2
  41. package/syncs/repositories/sync-repository-postgres.js +2 -2
  42. package/token/repositories/token-repository-factory.js +6 -19
  43. package/token/repositories/token-repository-mongo.js +2 -2
  44. package/token/repositories/token-repository-postgres.js +2 -2
  45. package/user/repositories/user-repository-factory.js +6 -9
  46. package/user/repositories/user-repository-mongo.js +4 -11
  47. package/user/repositories/user-repository-postgres.js +4 -11
  48. package/websocket/repositories/websocket-connection-repository-factory.js +6 -20
  49. package/websocket/repositories/websocket-connection-repository-mongo.js +2 -2
  50. package/websocket/repositories/websocket-connection-repository-postgres.js +2 -2
  51. package/modules/test/auther.test.js +0 -97
  52. package/user/repositories/user-repository.js +0 -263
@@ -1,3 +1,10 @@
1
+ jest.mock('../../database/config', () => ({
2
+ DB_TYPE: 'mongodb',
3
+ getDatabaseType: jest.fn(() => 'mongodb'),
4
+ PRISMA_LOG_LEVEL: 'error,warn',
5
+ PRISMA_QUERY_LOGGING: false,
6
+ }));
7
+
1
8
  const mockFindExecute = jest.fn();
2
9
 
3
10
  jest.mock('../../integrations/use-cases/find-integration-context-by-external-entity-id', () => {
@@ -25,7 +25,7 @@ function mapErrorToResponse(error) {
25
25
  * @returns {Object} User command object with CRUD operations
26
26
  */
27
27
  function createUserCommands() {
28
- const userRepository = createUserRepository({ userConfig: {} });
28
+ const userRepository = createUserRepository();
29
29
 
30
30
  return {
31
31
  /**
@@ -2,6 +2,7 @@ const { CredentialRepositoryMongo } = require('./credential-repository-mongo');
2
2
  const {
3
3
  CredentialRepositoryPostgres,
4
4
  } = require('./credential-repository-postgres');
5
+ const config = require('../../database/config');
5
6
 
6
7
  /**
7
8
  * Credential Repository Factory
@@ -17,25 +18,23 @@ const {
17
18
  * Usage:
18
19
  * ```javascript
19
20
  * const repository = createCredentialRepository();
20
- * const credential = await repository.findCredentialById(id); // ID is string
21
21
  * ```
22
22
  *
23
- * @param {Object} [prismaClient] - Optional Prisma client for testing
24
23
  * @returns {CredentialRepositoryInterface} Configured repository adapter
25
24
  */
26
- function createCredentialRepository(prismaClient) {
27
- const dbType = process.env.DB_TYPE || 'mongodb';
25
+ function createCredentialRepository() {
26
+ const dbType = config.DB_TYPE;
28
27
 
29
28
  switch (dbType) {
30
29
  case 'mongodb':
31
- return new CredentialRepositoryMongo(prismaClient);
30
+ return new CredentialRepositoryMongo();
32
31
 
33
32
  case 'postgresql':
34
- return new CredentialRepositoryPostgres(prismaClient);
33
+ return new CredentialRepositoryPostgres();
35
34
 
36
35
  default:
37
36
  throw new Error(
38
- `Unsupported DB_TYPE: ${dbType}. Supported values: 'mongodb', 'postgresql'`
37
+ `Unsupported database type: ${dbType}. Supported values: 'mongodb', 'postgresql'`
39
38
  );
40
39
  }
41
40
  }
@@ -13,9 +13,9 @@ const {
13
13
  * - Dynamic schema support via JSON field
14
14
  */
15
15
  class CredentialRepositoryMongo extends CredentialRepositoryInterface {
16
- constructor(prismaClient = prisma) {
16
+ constructor() {
17
17
  super();
18
- this.prisma = prismaClient; // Allow injection for testing
18
+ this.prisma = prisma;
19
19
  }
20
20
 
21
21
  /**
@@ -13,9 +13,9 @@ const {
13
13
  * - All returned IDs are converted to strings for application layer consistency
14
14
  */
15
15
  class CredentialRepositoryPostgres extends CredentialRepositoryInterface {
16
- constructor(prismaClient = prisma) {
16
+ constructor() {
17
17
  super();
18
- this.prisma = prismaClient; // Allow injection for testing
18
+ this.prisma = prisma;
19
19
  }
20
20
 
21
21
  /**
@@ -4,11 +4,82 @@
4
4
  */
5
5
 
6
6
  /**
7
- * Database type selection
8
- * Currently only 'mongodb' is supported, but this will enable multi-database support
9
- * @type {'mongodb'|'postgresql'|'mysql'}
7
+ * Determines database type from app definition
8
+ * Reads backend/index.js Definition.database configuration
9
+ *
10
+ * @returns {'mongodb'|'postgresql'} Database type
11
+ * @throws {Error} If database type cannot be determined or app definition missing
10
12
  */
11
- const DB_TYPE = process.env.DB_TYPE || 'mongodb';
13
+ function getDatabaseType() {
14
+ try {
15
+ const path = require('node:path');
16
+ const fs = require('node:fs');
17
+ const { findNearestBackendPackageJson } = require('../utils');
18
+
19
+ let backendIndexPath;
20
+ let database;
21
+ const backendPackagePath = findNearestBackendPackageJson();
22
+
23
+ if (!backendPackagePath) {
24
+ throw new Error(
25
+ '[Frigg] Cannot find backend package.json. ' +
26
+ 'Ensure backend/package.json exists in your project.'
27
+ );
28
+ }
29
+
30
+ const backendDir = path.dirname(backendPackagePath);
31
+ backendIndexPath = path.join(backendDir, 'index.js');
32
+
33
+ if (!fs.existsSync(backendIndexPath)) {
34
+ throw new Error(
35
+ `[Frigg] Backend index.js not found at ${backendIndexPath}. ` +
36
+ 'Ensure backend/index.js exists with a Definition export.'
37
+ );
38
+ }
39
+
40
+ const backendModule = require(backendIndexPath);
41
+ database = backendModule?.Definition?.database;
42
+
43
+ if (!database) {
44
+ throw new Error(
45
+ '[Frigg] App definition missing database configuration. ' +
46
+ `Add database: { postgres: { enable: true } } (or mongoDB/documentDB) to ${backendIndexPath}`
47
+ );
48
+ }
49
+
50
+ // Determine database type from enabled database
51
+ // Priority order: postgres > mongoDB > documentDB
52
+ if (database.postgres?.enable === true) {
53
+ return 'postgresql';
54
+ }
55
+ if (database.mongoDB?.enable === true) {
56
+ return 'mongodb';
57
+ }
58
+ if (database.documentDB?.enable === true) {
59
+ return 'mongodb'; // DocumentDB is MongoDB-compatible
60
+ }
61
+
62
+ throw new Error(
63
+ '[Frigg] No database enabled in app definition. ' +
64
+ 'Set one of: database.postgres.enable, database.mongoDB.enable, or database.documentDB.enable to true'
65
+ );
66
+ } catch (error) {
67
+ // Re-throw with context if it's our error
68
+ if (error.message.includes('[Frigg]')) {
69
+ throw error;
70
+ }
71
+ // Wrap unexpected errors
72
+ throw new Error(
73
+ `[Frigg] Failed to determine database type: ${error.message}`
74
+ );
75
+ }
76
+ }
77
+
78
+ /**
79
+ * Cached database type (lazy evaluation)
80
+ * @type {'mongodb'|'postgresql'|null}
81
+ */
82
+ let cachedDbType = null;
12
83
 
13
84
  /**
14
85
  * Enable Prisma debug logging
@@ -24,7 +95,23 @@ const PRISMA_LOG_LEVEL = process.env.PRISMA_LOG_LEVEL || 'error,warn';
24
95
  const PRISMA_QUERY_LOGGING = process.env.PRISMA_QUERY_LOGGING === 'true';
25
96
 
26
97
  module.exports = {
27
- DB_TYPE,
98
+ getDatabaseType, // Export for testing and direct use
28
99
  PRISMA_LOG_LEVEL,
29
100
  PRISMA_QUERY_LOGGING,
30
- };
101
+ };
102
+
103
+ /**
104
+ * Lazy-evaluated database type determined from app definition
105
+ * Only evaluates when accessed, preventing module load failures in test environments
106
+ * @type {'mongodb'|'postgresql'}
107
+ */
108
+ Object.defineProperty(module.exports, 'DB_TYPE', {
109
+ get() {
110
+ if (cachedDbType === null) {
111
+ cachedDbType = getDatabaseType();
112
+ }
113
+ return cachedDbType;
114
+ },
115
+ enumerable: true,
116
+ configurable: true
117
+ });
@@ -41,7 +41,34 @@ External Systems (AWS KMS, Database)
41
41
 
42
42
  ## Configuration
43
43
 
44
- ### App Definition
44
+ ### Database Selection
45
+
46
+ Database type is configured in `backend/index.js` app definition:
47
+
48
+ ```javascript
49
+ const appDefinition = {
50
+ database: {
51
+ mongoDB: {
52
+ enable: true, // Use MongoDB
53
+ },
54
+ documentDB: {
55
+ enable: false, // Use DocumentDB (MongoDB-compatible)
56
+ tlsCAFile: './security/global-bundle.pem',
57
+ },
58
+ postgres: {
59
+ enable: false, // Use PostgreSQL
60
+ },
61
+ },
62
+ // ... other config
63
+ };
64
+ ```
65
+
66
+ **Important**: Only enable ONE database at a time. The framework will use the first enabled database in this priority order:
67
+ 1. PostgreSQL (`postgres.enable = true`)
68
+ 2. MongoDB (`mongoDB.enable = true`)
69
+ 3. DocumentDB (`documentDB.enable = true`)
70
+
71
+ ### Encryption Configuration
45
72
 
46
73
  In `backend/index.js`:
47
74
 
@@ -371,14 +398,79 @@ npm test -- database/encryption/
371
398
 
372
399
  ### Integration Tests
373
400
 
401
+ Database type is determined from your app definition in `backend/index.js`:
402
+
403
+ ```javascript
404
+ // backend/index.js
405
+ database: {
406
+ mongoDB: { enable: true }, // For MongoDB tests
407
+ postgres: { enable: false }
408
+ }
409
+ ```
410
+
374
411
  ```bash
375
- # Test with MongoDB
376
- DB_TYPE=mongodb npm test -- database/encryption/
412
+ # Run encryption tests
413
+ npm test -- database/encryption/
414
+
415
+ # Tests use explicit database type parameter for testing:
416
+ # createHealthCheckRepository('mongodb')
417
+ ```
418
+
419
+ ## Error Handling & Logging
420
+
421
+ ### Error Handling Strategy
422
+
423
+ The encryption system uses **fail-fast error handling**:
424
+
425
+ - **Encryption failures**: Throw errors immediately (don't save corrupted/unencrypted sensitive data)
426
+ - **Decryption failures**: Throw errors immediately (prevents exposing invalid data)
427
+ - **Configuration errors**: Warn and disable encryption (graceful degradation for development)
428
+ - **Validation errors**: Throw errors on startup (catch issues before production)
377
429
 
378
- # Test with PostgreSQL
379
- DB_TYPE=postgresql npm test -- database/encryption/
430
+ **Why fail-fast?**
431
+ - Security-critical operations must not silently fail
432
+ - Better to expose issues during development than risk data breaches
433
+ - Prevents inconsistent database state (partially encrypted data)
434
+
435
+ ### Logging Configuration
436
+
437
+ Configure log verbosity with `FRIGG_LOG_LEVEL`:
438
+
439
+ ```bash
440
+ # Production (minimal logging)
441
+ FRIGG_LOG_LEVEL=WARN
442
+
443
+ # Development (detailed logging)
444
+ FRIGG_LOG_LEVEL=DEBUG
445
+
446
+ # Default
447
+ FRIGG_LOG_LEVEL=INFO
380
448
  ```
381
449
 
450
+ **Log Levels:**
451
+ - `DEBUG`: Detailed encryption operations (includes schema loading, key checks)
452
+ - `INFO`: High-level status (encryption enabled/disabled, custom schema registration)
453
+ - `WARN`: Configuration issues (missing keys, bypassed encryption)
454
+ - `ERROR`: Operation failures (encryption/decryption errors)
455
+
456
+ **Production Safety:**
457
+ - Sensitive data automatically sanitized in logs
458
+ - Long base64 strings truncated (prevents key leakage)
459
+ - Stack traces omitted in production (`STAGE=production`)
460
+ - Key IDs never logged
461
+
462
+ ### Performance Optimizations
463
+
464
+ **Parallel field encryption:**
465
+ - Multiple fields encrypted concurrently using `Promise.all()`
466
+ - Significantly faster for models with many encrypted fields
467
+ - Example: 3 fields encrypted in ~30ms vs ~90ms (3x speedup)
468
+
469
+ **Deep cloning:**
470
+ - Uses native `structuredClone()` on Node.js 17+ (2-5x faster)
471
+ - Falls back to custom implementation for compatibility
472
+ - No external dependencies required
473
+
382
474
  ## Troubleshooting
383
475
 
384
476
  ### Encryption Not Working