@friggframework/devtools 2.0.0--canary.546.74db90f.0 → 2.0.0--canary.545.e7becd9.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 (128) hide show
  1. package/frigg-cli/README.md +1 -1
  2. package/frigg-cli/__tests__/application/use-cases/AddApiModuleToIntegrationUseCase.test.js +326 -0
  3. package/frigg-cli/__tests__/application/use-cases/CreateApiModuleUseCase.test.js +337 -0
  4. package/frigg-cli/__tests__/domain/entities/ApiModule.test.js +373 -0
  5. package/frigg-cli/__tests__/domain/entities/AppDefinition.test.js +313 -0
  6. package/frigg-cli/__tests__/domain/services/IntegrationValidator.test.js +269 -0
  7. package/frigg-cli/__tests__/domain/value-objects/IntegrationName.test.js +82 -0
  8. package/frigg-cli/__tests__/infrastructure/adapters/IntegrationJsUpdater.test.js +408 -0
  9. package/frigg-cli/__tests__/infrastructure/repositories/FileSystemApiModuleRepository.test.js +583 -0
  10. package/frigg-cli/__tests__/infrastructure/repositories/FileSystemAppDefinitionRepository.test.js +314 -0
  11. package/frigg-cli/__tests__/infrastructure/repositories/FileSystemIntegrationRepository.test.js +383 -0
  12. package/frigg-cli/__tests__/unit/commands/build.test.js +1 -1
  13. package/frigg-cli/__tests__/unit/commands/doctor.test.js +0 -2
  14. package/frigg-cli/__tests__/unit/commands/init.test.js +406 -0
  15. package/frigg-cli/__tests__/unit/commands/install.test.js +23 -19
  16. package/frigg-cli/__tests__/unit/commands/provider-dispatch.test.js +383 -0
  17. package/frigg-cli/__tests__/unit/commands/repair.test.js +275 -0
  18. package/frigg-cli/__tests__/unit/dependencies.test.js +2 -2
  19. package/frigg-cli/__tests__/unit/start-command/application/RunPreflightChecksUseCase.test.js +411 -0
  20. package/frigg-cli/__tests__/unit/start-command/infrastructure/DatabaseAdapter.test.js +405 -0
  21. package/frigg-cli/__tests__/unit/start-command/infrastructure/DockerAdapter.test.js +496 -0
  22. package/frigg-cli/__tests__/unit/start-command/presentation/InteractivePromptAdapter.test.js +474 -0
  23. package/frigg-cli/__tests__/unit/utils/output.test.js +196 -0
  24. package/frigg-cli/application/use-cases/AddApiModuleToIntegrationUseCase.js +93 -0
  25. package/frigg-cli/application/use-cases/CreateApiModuleUseCase.js +93 -0
  26. package/frigg-cli/application/use-cases/CreateIntegrationUseCase.js +103 -0
  27. package/frigg-cli/build-command/index.js +123 -11
  28. package/frigg-cli/container.js +172 -0
  29. package/frigg-cli/deploy-command/index.js +83 -1
  30. package/frigg-cli/docs/OUTPUT_MIGRATION_GUIDE.md +286 -0
  31. package/frigg-cli/doctor-command/index.js +37 -16
  32. package/frigg-cli/domain/entities/ApiModule.js +272 -0
  33. package/frigg-cli/domain/entities/AppDefinition.js +227 -0
  34. package/frigg-cli/domain/entities/Integration.js +198 -0
  35. package/frigg-cli/domain/exceptions/DomainException.js +24 -0
  36. package/frigg-cli/domain/ports/IApiModuleRepository.js +53 -0
  37. package/frigg-cli/domain/ports/IAppDefinitionRepository.js +43 -0
  38. package/frigg-cli/domain/ports/IIntegrationRepository.js +61 -0
  39. package/frigg-cli/domain/services/IntegrationValidator.js +185 -0
  40. package/frigg-cli/domain/value-objects/IntegrationId.js +42 -0
  41. package/frigg-cli/domain/value-objects/IntegrationName.js +60 -0
  42. package/frigg-cli/domain/value-objects/SemanticVersion.js +70 -0
  43. package/frigg-cli/generate-iam-command.js +21 -1
  44. package/frigg-cli/index.js +21 -6
  45. package/frigg-cli/index.test.js +7 -2
  46. package/frigg-cli/infrastructure/UnitOfWork.js +46 -0
  47. package/frigg-cli/infrastructure/adapters/BackendJsUpdater.js +197 -0
  48. package/frigg-cli/infrastructure/adapters/FileSystemAdapter.js +224 -0
  49. package/frigg-cli/infrastructure/adapters/IntegrationJsUpdater.js +249 -0
  50. package/frigg-cli/infrastructure/adapters/SchemaValidator.js +92 -0
  51. package/frigg-cli/infrastructure/repositories/FileSystemApiModuleRepository.js +373 -0
  52. package/frigg-cli/infrastructure/repositories/FileSystemAppDefinitionRepository.js +116 -0
  53. package/frigg-cli/infrastructure/repositories/FileSystemIntegrationRepository.js +277 -0
  54. package/frigg-cli/init-command/backend-first-handler.js +124 -42
  55. package/frigg-cli/init-command/index.js +2 -1
  56. package/frigg-cli/init-command/template-handler.js +13 -3
  57. package/frigg-cli/install-command/backend-js.js +3 -3
  58. package/frigg-cli/install-command/environment-variables.js +16 -19
  59. package/frigg-cli/install-command/environment-variables.test.js +12 -13
  60. package/frigg-cli/install-command/index.js +14 -9
  61. package/frigg-cli/install-command/integration-file.js +3 -3
  62. package/frigg-cli/install-command/validate-package.js +5 -9
  63. package/frigg-cli/jest.config.js +4 -1
  64. package/frigg-cli/package-lock.json +16226 -0
  65. package/frigg-cli/repair-command/index.js +121 -128
  66. package/frigg-cli/start-command/application/RunPreflightChecksUseCase.js +376 -0
  67. package/frigg-cli/start-command/index.js +324 -2
  68. package/frigg-cli/start-command/infrastructure/DatabaseAdapter.js +591 -0
  69. package/frigg-cli/start-command/infrastructure/DockerAdapter.js +306 -0
  70. package/frigg-cli/start-command/presentation/InteractivePromptAdapter.js +329 -0
  71. package/frigg-cli/templates/backend/.env.example +62 -0
  72. package/frigg-cli/templates/backend/.eslintrc.json +12 -0
  73. package/frigg-cli/templates/backend/.prettierrc +6 -0
  74. package/frigg-cli/templates/backend/docker-compose.yml +22 -0
  75. package/frigg-cli/templates/backend/index.js +96 -0
  76. package/frigg-cli/templates/backend/infrastructure.js +12 -0
  77. package/frigg-cli/templates/backend/jest.config.js +17 -0
  78. package/frigg-cli/templates/backend/package.json +50 -0
  79. package/frigg-cli/templates/backend/src/api-modules/.gitkeep +10 -0
  80. package/frigg-cli/templates/backend/src/base/.gitkeep +7 -0
  81. package/frigg-cli/templates/backend/src/integrations/.gitkeep +10 -0
  82. package/frigg-cli/templates/backend/src/integrations/ExampleIntegration.js +65 -0
  83. package/frigg-cli/templates/backend/src/utils/.gitkeep +7 -0
  84. package/frigg-cli/templates/backend/test/setup.js +30 -0
  85. package/frigg-cli/templates/backend/ui-extensions/.gitkeep +0 -0
  86. package/frigg-cli/templates/backend/ui-extensions/README.md +77 -0
  87. package/frigg-cli/ui-command/index.js +58 -36
  88. package/frigg-cli/utils/__tests__/provider-helper.test.js +55 -0
  89. package/frigg-cli/utils/__tests__/repo-detection.test.js +436 -0
  90. package/frigg-cli/utils/output.js +382 -0
  91. package/frigg-cli/utils/provider-helper.js +75 -0
  92. package/frigg-cli/utils/repo-detection.js +85 -37
  93. package/frigg-cli/validate-command/__tests__/adapters/validate-command.test.js +205 -0
  94. package/frigg-cli/validate-command/__tests__/application/validate-app-use-case.test.js +104 -0
  95. package/frigg-cli/validate-command/__tests__/domain/fix-suggestion.test.js +153 -0
  96. package/frigg-cli/validate-command/__tests__/domain/validation-error.test.js +162 -0
  97. package/frigg-cli/validate-command/__tests__/domain/validation-result.test.js +152 -0
  98. package/frigg-cli/validate-command/__tests__/infrastructure/api-module-validator.test.js +332 -0
  99. package/frigg-cli/validate-command/__tests__/infrastructure/app-definition-validator.test.js +191 -0
  100. package/frigg-cli/validate-command/__tests__/infrastructure/integration-class-validator.test.js +146 -0
  101. package/frigg-cli/validate-command/__tests__/infrastructure/template-validation.test.js +155 -0
  102. package/frigg-cli/validate-command/adapters/cli/validate-command.js +199 -0
  103. package/frigg-cli/validate-command/application/use-cases/validate-app-use-case.js +35 -0
  104. package/frigg-cli/validate-command/domain/entities/validation-result.js +74 -0
  105. package/frigg-cli/validate-command/domain/value-objects/fix-suggestion.js +74 -0
  106. package/frigg-cli/validate-command/domain/value-objects/validation-error.js +68 -0
  107. package/frigg-cli/validate-command/infrastructure/validators/api-module-validator.js +181 -0
  108. package/frigg-cli/validate-command/infrastructure/validators/app-definition-validator.js +128 -0
  109. package/frigg-cli/validate-command/infrastructure/validators/integration-class-validator.js +113 -0
  110. package/infrastructure/create-frigg-infrastructure.js +93 -0
  111. package/infrastructure/docs/iam-policy-templates.md +1 -1
  112. package/infrastructure/domains/admin-scripts/admin-script-builder.js +200 -0
  113. package/infrastructure/domains/admin-scripts/admin-script-builder.test.js +499 -0
  114. package/infrastructure/domains/admin-scripts/index.js +5 -0
  115. package/infrastructure/domains/networking/vpc-builder.test.js +2 -4
  116. package/infrastructure/domains/networking/vpc-resolver.test.js +1 -1
  117. package/infrastructure/domains/shared/cloudformation-discovery.test.js +4 -7
  118. package/infrastructure/domains/shared/resource-discovery.js +5 -5
  119. package/infrastructure/domains/shared/types/app-definition.js +21 -0
  120. package/infrastructure/domains/shared/types/discovery-result.test.js +1 -1
  121. package/infrastructure/domains/shared/utilities/base-definition-factory.js +10 -1
  122. package/infrastructure/domains/shared/utilities/base-definition-factory.test.js +2 -2
  123. package/infrastructure/infrastructure-composer.js +2 -0
  124. package/infrastructure/infrastructure-composer.test.js +2 -2
  125. package/infrastructure/jest.config.js +16 -0
  126. package/management-ui/README.md +245 -109
  127. package/package.json +8 -7
  128. package/frigg-cli/install-command/logger.js +0 -12
@@ -0,0 +1,6 @@
1
+ {
2
+ "singleQuote": true,
3
+ "tabWidth": 4,
4
+ "printWidth": 100,
5
+ "trailingComma": "es5"
6
+ }
@@ -0,0 +1,22 @@
1
+ version: '3.8'
2
+
3
+ services:
4
+ postgres:
5
+ image: postgres:15-alpine
6
+ container_name: frigg-postgres
7
+ environment:
8
+ POSTGRES_USER: frigg
9
+ POSTGRES_PASSWORD: frigg
10
+ POSTGRES_DB: frigg
11
+ ports:
12
+ - "5432:5432"
13
+ volumes:
14
+ - postgres_data:/var/lib/postgresql/data
15
+ healthcheck:
16
+ test: ["CMD-SHELL", "pg_isready -U frigg"]
17
+ interval: 5s
18
+ timeout: 5s
19
+ retries: 5
20
+
21
+ volumes:
22
+ postgres_data:
@@ -0,0 +1,96 @@
1
+ /**
2
+ * Frigg Application Definition
3
+ *
4
+ * This file defines your Frigg integration application.
5
+ * Configure integrations, authentication, and infrastructure settings here.
6
+ */
7
+
8
+ // Import your integrations here
9
+ // const ExampleIntegration = require('./src/integrations/ExampleIntegration');
10
+
11
+ const appDefinition = {
12
+ // Application metadata
13
+ label: 'My Frigg Application',
14
+ name: 'my-frigg-app',
15
+
16
+ // Deployment mode: 'managed' (recommended) or 'custom'
17
+ managementMode: 'managed',
18
+
19
+ // VPC isolation strategy: 'isolated' (each stage separate) or 'shared'
20
+ vpcIsolation: 'isolated',
21
+
22
+ // Your integrations - add Integration classes here
23
+ integrations: [
24
+ // Add your integrations here as you install them
25
+ // Example:
26
+ // ExampleIntegration,
27
+ ],
28
+
29
+ // User authentication configuration
30
+ user: {
31
+ usePassword: true,
32
+ primary: 'organization',
33
+ individualUserRequired: true,
34
+ organizationUserRequired: true,
35
+ authModes: {
36
+ friggToken: true, // Support web UI login
37
+ sharedSecret: true, // Enable backend-to-backend API communication
38
+ adopterJwt: false, // Set true to use your own JWT tokens
39
+ },
40
+ },
41
+
42
+ // Encryption settings
43
+ encryption: {
44
+ fieldLevelEncryptionMethod: 'kms', // KMS encryption for production
45
+ },
46
+
47
+ // VPC configuration
48
+ vpc: {
49
+ enable: true,
50
+ enableVPCEndpoints: true,
51
+ selfHeal: true,
52
+ },
53
+
54
+ // Database configuration (PostgreSQL via Aurora Serverless)
55
+ database: {
56
+ postgres: {
57
+ enable: true,
58
+ publiclyAccessible: false,
59
+ database: 'postgres',
60
+ minCapacity: 0.5,
61
+ maxCapacity: 1,
62
+ },
63
+ },
64
+
65
+ // SSM Parameter Store (optional)
66
+ ssm: {
67
+ enable: false,
68
+ },
69
+
70
+ // Environment variables required by your application
71
+ // Set these in your .env file or deployment environment
72
+ environment: {
73
+ // Core Configuration
74
+ BASE_URL: true,
75
+ DATABASE_URL: true,
76
+ DATABASE_USER: true,
77
+ DATABASE_PASSWORD: true,
78
+ REDIRECT_URI: true,
79
+ HEALTH_API_KEY: true,
80
+ ADMIN_API_KEY: true,
81
+ FRIGG_API_KEY: true,
82
+ FRIGG_APP_USER_ID: true,
83
+
84
+ // AWS Configuration
85
+ AWS_REGION: true,
86
+ S3_BUCKET_NAME: true,
87
+
88
+ // Add your integration-specific environment variables here
89
+ // EXAMPLE_CLIENT_ID: true,
90
+ // EXAMPLE_CLIENT_SECRET: true,
91
+ },
92
+ };
93
+
94
+ module.exports = {
95
+ Definition: appDefinition,
96
+ };
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Frigg Infrastructure Configuration
3
+ *
4
+ * This file exports the serverless configuration for your Frigg application.
5
+ * The createFriggInfrastructure function reads your app definition and
6
+ * generates the appropriate serverless.yml configuration.
7
+ */
8
+
9
+ const { createFriggInfrastructure } = require('@friggframework/devtools');
10
+
11
+ // Serverless supports async configuration by exporting a promise
12
+ module.exports = createFriggInfrastructure();
@@ -0,0 +1,17 @@
1
+ module.exports = {
2
+ testEnvironment: 'node',
3
+ testMatch: ['**/test/**/*.test.js', '**/test/**/*.spec.js'],
4
+ testPathIgnorePatterns: ['/node_modules/'],
5
+ collectCoverageFrom: [
6
+ 'src/**/*.js',
7
+ '!src/**/*.test.js',
8
+ '!src/**/*.spec.js',
9
+ ],
10
+ coverageDirectory: 'coverage',
11
+ coverageReporters: ['text', 'lcov', 'html'],
12
+ verbose: true,
13
+ testTimeout: 30000,
14
+ setupFilesAfterEnv: ['./test/setup.js'],
15
+ // Jest groups for selective testing
16
+ runner: 'groups',
17
+ };
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "frigg-backend",
3
+ "version": "0.1.0",
4
+ "private": true,
5
+ "prettier": "@friggframework/prettier-config",
6
+ "scripts": {
7
+ "test": "jest --group=-interactive --group=-live-api",
8
+ "test:full": "jest",
9
+ "test:unit": "jest --config jest.unit.config.js",
10
+ "test:watch": "jest --watch",
11
+ "test:coverage": "jest --coverage",
12
+ "lint": "eslint .",
13
+ "lint:fix": "prettier --write --loglevel error . && eslint . --fix",
14
+ "format": "prettier --write --loglevel error .",
15
+ "backend-start": "sls offline --stage dev --config serverless.js",
16
+ "docker:start": "docker compose up -d",
17
+ "docker:stop": "docker compose down",
18
+ "frigg:start": "frigg start",
19
+ "frigg:deploy": "frigg deploy",
20
+ "frigg:db:setup": "frigg db:setup"
21
+ },
22
+ "dependencies": {
23
+ "@friggframework/core": "2.0.0-next.58",
24
+ "axios": "^1.6.0",
25
+ "body-parser": "^1.20.3",
26
+ "cors": "^2.8.5",
27
+ "express": "^4.21.2",
28
+ "express-async-handler": "^1.2.0",
29
+ "node-cache": "^5.1.2",
30
+ "serverless-http": "^2.7.0"
31
+ },
32
+ "devDependencies": {
33
+ "@friggframework/devtools": "2.0.0-next.58",
34
+ "@friggframework/eslint-config": "2.0.0-next.58",
35
+ "@friggframework/prettier-config": "2.0.0-next.58",
36
+ "@friggframework/serverless-plugin": "2.0.0-next.58",
37
+ "@friggframework/test": "2.0.0-next.58",
38
+ "@prisma/client": "^6.0.0",
39
+ "dotenv": "^16.4.5",
40
+ "esbuild": "^0.25.0",
41
+ "jest": "^29.7.0",
42
+ "nock": "^13.5.4",
43
+ "osls": "^3.40.1",
44
+ "prettier": "3.5.3",
45
+ "prisma": "^6.0.0",
46
+ "serverless-dotenv-plugin": "^6.0.0",
47
+ "serverless-esbuild": "^1.55.1",
48
+ "serverless-offline": "^13.8.0"
49
+ }
50
+ }
@@ -0,0 +1,10 @@
1
+ # This directory is for custom API modules
2
+ #
3
+ # API modules define how to connect with external APIs.
4
+ # Most integrations use published @friggframework/api-module-* packages.
5
+ #
6
+ # Only create custom API modules here if:
7
+ # - You're integrating with a proprietary API
8
+ # - No published module exists for your service
9
+ #
10
+ # Use 'frigg generate:api-module' to create new API modules
@@ -0,0 +1,7 @@
1
+ # This directory contains base classes and shared infrastructure
2
+ #
3
+ # Common patterns:
4
+ # - Custom entity extensions
5
+ # - Shared middleware
6
+ # - Base integration classes
7
+ # - Custom authentication handlers
@@ -0,0 +1,10 @@
1
+ # This directory contains your Integration classes
2
+ # Each integration connects your application with a third-party API module
3
+ #
4
+ # Example structure:
5
+ # src/integrations/
6
+ # SalesforceIntegration.js
7
+ # HubSpotIntegration.js
8
+ # SlackIntegration.js
9
+ #
10
+ # Use 'frigg generate:integration' to create new integrations
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Example Integration
3
+ *
4
+ * This is a template showing how to create an Integration class.
5
+ * An Integration connects your application with a third-party API module.
6
+ *
7
+ * To create your own integration:
8
+ * 1. Install the API module: npm install @friggframework/api-module-<name>
9
+ * 2. Copy this file and rename it (e.g., SalesforceIntegration.js)
10
+ * 3. Update the imports and class to use your API module
11
+ * 4. Register it in index.js
12
+ */
13
+
14
+ const { Integration } = require('@friggframework/core');
15
+ // const { Api, Entity } = require('@friggframework/api-module-example');
16
+
17
+ class ExampleIntegration extends Integration {
18
+ static Definition = {
19
+ name: 'example',
20
+ version: '1.0.0',
21
+ supportedVersions: ['1.0.0'],
22
+
23
+ // Events this integration can emit
24
+ events: ['SYNC_COMPLETED', 'DATA_UPDATED', 'ERROR_OCCURRED'],
25
+ };
26
+
27
+ /**
28
+ * Create API instance for this integration
29
+ * @param {Object} params - Parameters from the credential
30
+ */
31
+ static async getInstance(params) {
32
+ // Return your API module instance
33
+ // return new Api(params);
34
+ throw new Error('ExampleIntegration.getInstance() not implemented');
35
+ }
36
+
37
+ /**
38
+ * Process incoming webhook events
39
+ * @param {Object} event - The webhook event data
40
+ */
41
+ async processWebhook(event) {
42
+ // Handle webhook events from the third-party service
43
+ console.log('Processing webhook:', event);
44
+ }
45
+
46
+ /**
47
+ * Perform initial sync after user connects
48
+ * @param {Object} options - Sync options
49
+ */
50
+ async initialSync(options = {}) {
51
+ // Sync initial data from the third-party service
52
+ console.log('Performing initial sync');
53
+ }
54
+
55
+ /**
56
+ * Handle OAuth callback
57
+ * @param {Object} callbackParams - OAuth callback parameters
58
+ */
59
+ async handleCallback(callbackParams) {
60
+ // Process OAuth callback and store credentials
61
+ console.log('Handling OAuth callback');
62
+ }
63
+ }
64
+
65
+ module.exports = ExampleIntegration;
@@ -0,0 +1,7 @@
1
+ # This directory contains utility functions and helpers
2
+ #
3
+ # Common utilities:
4
+ # - Data transformation helpers
5
+ # - Validation utilities
6
+ # - Custom error classes
7
+ # - Logging helpers
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Jest Test Setup
3
+ *
4
+ * This file runs before each test file and sets up the testing environment.
5
+ */
6
+
7
+ const dotenv = require('dotenv');
8
+ const path = require('path');
9
+
10
+ // Load test environment variables
11
+ dotenv.config({ path: path.join(__dirname, '..', '.env.test') });
12
+
13
+ // Set test environment
14
+ process.env.NODE_ENV = 'test';
15
+
16
+ // Global test timeout
17
+ jest.setTimeout(30000);
18
+
19
+ // Silence console logs during tests (optional - comment out for debugging)
20
+ // global.console = {
21
+ // ...console,
22
+ // log: jest.fn(),
23
+ // info: jest.fn(),
24
+ // warn: jest.fn(),
25
+ // };
26
+
27
+ // Clean up after all tests
28
+ afterAll(async () => {
29
+ // Add any global cleanup here
30
+ });
@@ -0,0 +1,77 @@
1
+ # UI Extensions
2
+
3
+ This directory contains custom UI extensions for external platform integrations. Each subdirectory represents a separate integration-specific package.
4
+
5
+ ## Purpose
6
+
7
+ When integrating with platforms that provide their own UI extension frameworks (like Attio, HubSpot, Salesforce, etc.), you can build and manage those extension packages here.
8
+
9
+ ## Directory Structure
10
+
11
+ ```
12
+ ui-extensions/
13
+ ├── README.md # This file
14
+ ├── attio/ # Attio UI extension (if using Attio)
15
+ ├── hubspot/ # HubSpot UI extension (if using HubSpot)
16
+ ├── salesforce/ # Salesforce Lightning component (if using Salesforce)
17
+ └── <platform>/ # Other platform-specific extensions
18
+ ```
19
+
20
+ ## Getting Started
21
+
22
+ Each platform extension is a separate npm package. To create one:
23
+
24
+ 1. **Create the extension directory:**
25
+ ```bash
26
+ mkdir ui-extensions/<platform-name>
27
+ cd ui-extensions/<platform-name>
28
+ ```
29
+
30
+ 2. **Initialize the package:**
31
+ Follow the platform's SDK documentation to set up the extension package.
32
+
33
+ 3. **Develop and test:**
34
+ Each extension has its own build and development scripts.
35
+
36
+ ## Example: Attio Extension
37
+
38
+ ```bash
39
+ cd ui-extensions/attio
40
+ npm install
41
+ npm run dev # Start development server
42
+ npm run build # Build for production
43
+ ```
44
+
45
+ ## Example: HubSpot Extension
46
+
47
+ ```bash
48
+ cd ui-extensions/hubspot
49
+ npm install
50
+ npx hs project create --template=react-app
51
+ npm run dev
52
+ ```
53
+
54
+ ## Workspace Integration
55
+
56
+ Add extensions as workspaces in the root `package.json`:
57
+
58
+ ```json
59
+ {
60
+ "workspaces": [
61
+ "ui-extensions/*"
62
+ ]
63
+ }
64
+ ```
65
+
66
+ ## Platform Documentation
67
+
68
+ - [Attio Apps](https://developers.attio.com)
69
+ - [HubSpot UI Extensions](https://developers.hubspot.com/docs/platform/ui-extensions-overview)
70
+ - [Salesforce Lightning Components](https://developer.salesforce.com/docs/atlas.en-us.lightning.meta)
71
+ - [Monday.com Apps](https://developer.monday.com/apps)
72
+
73
+ ## Notes
74
+
75
+ - Each extension is independent and can be deployed separately
76
+ - Extensions typically have their own build tools and test suites
77
+ - Keep platform-specific code isolated to its respective directory
@@ -2,16 +2,19 @@ const open = require('open');
2
2
  const chalk = require('chalk');
3
3
  const path = require('path');
4
4
  const ProcessManager = require('../utils/process-manager');
5
- const {
6
- getCurrentRepositoryInfo,
7
- discoverFriggRepositories,
5
+ const {
6
+ getCurrentRepositoryInfo,
7
+ discoverFriggRepositories,
8
8
  promptRepositorySelection,
9
- formatRepositoryInfo
9
+ formatRepositoryInfo
10
10
  } = require('../utils/repo-detection');
11
11
 
12
12
  async function uiCommand(options) {
13
- const { port = 3001, open: shouldOpen = true, repo: specifiedRepo, dev = false } = options;
14
-
13
+ const { port = 3210, open: shouldOpen = true, repo: specifiedRepo, dev = false } = options;
14
+
15
+ // Fix: --no-open should set open to false, not use the default true
16
+ const shouldOpenBrowser = options.open !== false;
17
+
15
18
  let targetRepo = null;
16
19
  let workingDirectory = process.cwd();
17
20
 
@@ -25,7 +28,7 @@ async function uiCommand(options) {
25
28
  // Check if we're already in a Frigg repository
26
29
  console.log(chalk.blue('Detecting Frigg repository...'));
27
30
  const currentRepo = await getCurrentRepositoryInfo();
28
-
31
+
29
32
  if (currentRepo) {
30
33
  console.log(chalk.green(`✓ Found Frigg repository: ${formatRepositoryInfo(currentRepo)}`));
31
34
  if (currentRepo.currentSubPath) {
@@ -37,40 +40,40 @@ async function uiCommand(options) {
37
40
  // Discover Frigg repositories
38
41
  console.log(chalk.yellow('Current directory is not a Frigg repository.'));
39
42
  console.log(chalk.blue('Searching for Frigg repositories...'));
40
-
43
+
41
44
  const discoveredRepos = await discoverFriggRepositories();
42
-
45
+
43
46
  if (discoveredRepos.length === 0) {
44
47
  console.log(chalk.red('No Frigg repositories found. Please create one first.'));
45
48
  process.exit(1);
46
49
  }
47
-
50
+
48
51
  // For UI command, we'll let the UI handle repository selection
49
52
  // Set a placeholder and pass the discovered repos via environment
50
- targetRepo = {
51
- name: 'Multiple Repositories Available',
53
+ targetRepo = {
54
+ name: 'Multiple Repositories Available',
52
55
  path: process.cwd(),
53
56
  isMultiRepo: true,
54
57
  availableRepos: discoveredRepos
55
58
  };
56
59
  workingDirectory = process.cwd();
57
-
60
+
58
61
  console.log(chalk.blue(`Found ${discoveredRepos.length} Frigg repositories. You'll be able to select one in the UI.`));
59
62
  }
60
63
  }
61
64
 
62
65
  console.log(chalk.blue('🚀 Starting Frigg Management UI...'));
63
-
66
+
64
67
  const processManager = new ProcessManager();
65
-
68
+
66
69
  try {
67
70
  const managementUiPath = path.join(__dirname, '../../management-ui');
68
-
71
+
69
72
  // Check if we're in development mode
70
73
  // For CLI usage, we prefer development mode unless explicitly set to production
71
74
  const fs = require('fs');
72
75
  const isDevelopment = dev || process.env.NODE_ENV !== 'production';
73
-
76
+
74
77
  if (isDevelopment) {
75
78
  const env = {
76
79
  ...process.env,
@@ -80,15 +83,15 @@ async function uiCommand(options) {
80
83
  REPOSITORY_INFO: JSON.stringify(targetRepo),
81
84
  AVAILABLE_REPOSITORIES: targetRepo.isMultiRepo ? JSON.stringify(targetRepo.availableRepos) : null
82
85
  };
83
-
84
- // Start backend server
86
+
87
+ // Start backend server with nodemon for auto-restart
85
88
  processManager.spawnProcess(
86
89
  'backend',
87
90
  'npm',
88
- ['run', 'server'],
91
+ ['run', 'server:dev'],
89
92
  { cwd: managementUiPath, env }
90
93
  );
91
-
94
+
92
95
  // Start frontend dev server
93
96
  processManager.spawnProcess(
94
97
  'frontend',
@@ -96,52 +99,71 @@ async function uiCommand(options) {
96
99
  ['run', 'dev'],
97
100
  { cwd: managementUiPath, env }
98
101
  );
99
-
100
- // Wait for servers to start
101
- await new Promise(resolve => setTimeout(resolve, 2000));
102
-
102
+
103
+ // Wait for backend to be ready by polling health endpoint
104
+ const maxAttempts = 20;
105
+ const delayMs = 250;
106
+ let backendReady = false;
107
+
108
+ for (let attempt = 0; attempt < maxAttempts; attempt++) {
109
+ try {
110
+ const response = await fetch(`http://localhost:${port}/api/health`);
111
+ if (response.ok) {
112
+ backendReady = true;
113
+ break;
114
+ }
115
+ } catch (err) {
116
+ // Backend not ready yet, wait and retry
117
+ }
118
+ await new Promise(resolve => setTimeout(resolve, delayMs));
119
+ }
120
+
121
+ if (!backendReady) {
122
+ console.warn('⚠️ Backend health check timed out, but continuing anyway...');
123
+ }
124
+
103
125
  // Display clean status
104
126
  processManager.printStatus(
105
127
  'http://localhost:5173',
106
128
  `http://localhost:${port}`,
107
129
  targetRepo.name
108
130
  );
109
-
131
+
110
132
  // Open browser if requested
111
- if (shouldOpen) {
133
+ if (shouldOpenBrowser) {
112
134
  setTimeout(() => {
113
135
  open('http://localhost:5173');
114
136
  }, 1000);
115
137
  }
116
-
138
+
117
139
  } else {
118
140
  // Production mode - just start the backend server
119
141
  const { FriggManagementServer } = await import('../../management-ui/server/index.js');
120
-
121
- const server = new FriggManagementServer({
122
- port,
142
+
143
+ const server = new FriggManagementServer({
144
+ port,
123
145
  projectRoot: workingDirectory,
124
146
  repositoryInfo: targetRepo,
125
147
  availableRepositories: targetRepo.isMultiRepo ? targetRepo.availableRepos : null
126
148
  });
127
149
  await server.start();
128
-
150
+
129
151
  processManager.printStatus(
130
152
  `http://localhost:${port}`,
131
153
  `http://localhost:${port}/api`,
132
154
  targetRepo.name
133
155
  );
134
-
135
- if (shouldOpen) {
156
+
157
+ if (shouldOpenBrowser) {
136
158
  setTimeout(() => {
137
159
  open(`http://localhost:${port}`);
138
160
  }, 1000);
139
161
  }
140
162
  }
141
-
163
+
142
164
  // Keep the process running
143
165
  process.stdin.resume();
144
-
166
+
145
167
  } catch (error) {
146
168
  console.error(chalk.red('Failed to start Management UI:'), error.message);
147
169
  if (error.code === 'EADDRINUSE') {
@@ -0,0 +1,55 @@
1
+ const path = require('path');
2
+ const { loadProviderForCli, loadCliAppDefinition } = require('../provider-helper');
3
+
4
+ describe('provider-helper', () => {
5
+ describe('loadCliAppDefinition', () => {
6
+ it('returns null when no index.js exists', () => {
7
+ const result = loadCliAppDefinition('/nonexistent/path');
8
+ expect(result).toBeNull();
9
+ });
10
+
11
+ it('returns null when index.js has no Definition export', () => {
12
+ // Use a directory that has an index.js but no Definition
13
+ const result = loadCliAppDefinition(
14
+ path.join(__dirname, '..', '..')
15
+ );
16
+ // frigg-cli/index.js doesn't export Definition
17
+ expect(result).toBeNull();
18
+ });
19
+ });
20
+
21
+ describe('loadProviderForCli', () => {
22
+ it('returns null when no appDefinition found', () => {
23
+ // Run from a directory with no Frigg app
24
+ const originalCwd = process.cwd();
25
+ try {
26
+ process.chdir('/tmp');
27
+ const result = loadProviderForCli();
28
+ expect(result).toBeNull();
29
+ } finally {
30
+ process.chdir(originalCwd);
31
+ }
32
+ });
33
+
34
+ it('returns null provider for aws (default)', () => {
35
+ // Mock: loadCliAppDefinition returns an appDef with provider: 'aws'
36
+ jest.mock('../provider-helper', () => {
37
+ const original = jest.requireActual('../provider-helper');
38
+ return {
39
+ ...original,
40
+ loadProviderForCli: (options) => {
41
+ // Simulate AWS provider (no provider field defaults to aws)
42
+ return { appDefinition: { provider: 'aws' }, provider: null, providerName: 'aws' };
43
+ },
44
+ };
45
+ });
46
+
47
+ const { loadProviderForCli: mocked } = require('../provider-helper');
48
+ const result = mocked();
49
+ expect(result.providerName).toBe('aws');
50
+ expect(result.provider).toBeNull();
51
+
52
+ jest.restoreAllMocks();
53
+ });
54
+ });
55
+ });