@friggframework/devtools 2.0.0-next.45 → 2.0.0-next.47

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 (212) hide show
  1. package/infrastructure/ARCHITECTURE.md +487 -0
  2. package/infrastructure/HEALTH.md +468 -0
  3. package/infrastructure/README.md +51 -0
  4. package/infrastructure/__tests__/postgres-config.test.js +914 -0
  5. package/infrastructure/__tests__/template-generation.test.js +687 -0
  6. package/infrastructure/create-frigg-infrastructure.js +1 -1
  7. package/infrastructure/docs/POSTGRES-CONFIGURATION.md +630 -0
  8. package/infrastructure/{DEPLOYMENT-INSTRUCTIONS.md → docs/deployment-instructions.md} +3 -3
  9. package/infrastructure/{IAM-POLICY-TEMPLATES.md → docs/iam-policy-templates.md} +9 -10
  10. package/infrastructure/domains/database/aurora-builder.js +809 -0
  11. package/infrastructure/domains/database/aurora-builder.test.js +950 -0
  12. package/infrastructure/domains/database/aurora-discovery.js +87 -0
  13. package/infrastructure/domains/database/aurora-discovery.test.js +188 -0
  14. package/infrastructure/domains/database/aurora-resolver.js +210 -0
  15. package/infrastructure/domains/database/aurora-resolver.test.js +347 -0
  16. package/infrastructure/domains/database/migration-builder.js +695 -0
  17. package/infrastructure/domains/database/migration-builder.test.js +294 -0
  18. package/infrastructure/domains/database/migration-resolver.js +163 -0
  19. package/infrastructure/domains/database/migration-resolver.test.js +337 -0
  20. package/infrastructure/domains/health/application/ports/IPropertyReconciler.js +164 -0
  21. package/infrastructure/domains/health/application/ports/IResourceDetector.js +129 -0
  22. package/infrastructure/domains/health/application/ports/IResourceImporter.js +142 -0
  23. package/infrastructure/domains/health/application/ports/IStackRepository.js +131 -0
  24. package/infrastructure/domains/health/application/ports/index.js +26 -0
  25. package/infrastructure/domains/health/application/use-cases/__tests__/execute-resource-import-use-case.test.js +679 -0
  26. package/infrastructure/domains/health/application/use-cases/__tests__/mismatch-analyzer-method-name.test.js +167 -0
  27. package/infrastructure/domains/health/application/use-cases/__tests__/repair-via-import-use-case.test.js +1130 -0
  28. package/infrastructure/domains/health/application/use-cases/execute-resource-import-use-case.js +221 -0
  29. package/infrastructure/domains/health/application/use-cases/reconcile-properties-use-case.js +152 -0
  30. package/infrastructure/domains/health/application/use-cases/reconcile-properties-use-case.test.js +343 -0
  31. package/infrastructure/domains/health/application/use-cases/repair-via-import-use-case.js +535 -0
  32. package/infrastructure/domains/health/application/use-cases/repair-via-import-use-case.test.js +376 -0
  33. package/infrastructure/domains/health/application/use-cases/run-health-check-use-case.js +213 -0
  34. package/infrastructure/domains/health/application/use-cases/run-health-check-use-case.test.js +441 -0
  35. package/infrastructure/domains/health/docs/ACME-DEV-DRIFT-ANALYSIS.md +267 -0
  36. package/infrastructure/domains/health/docs/BUILD-VS-DEPLOYED-TEMPLATE-ANALYSIS.md +324 -0
  37. package/infrastructure/domains/health/docs/ORPHAN-DETECTION-ANALYSIS.md +386 -0
  38. package/infrastructure/domains/health/docs/SPEC-CLEANUP-COMMAND.md +1419 -0
  39. package/infrastructure/domains/health/docs/TDD-IMPLEMENTATION-SUMMARY.md +391 -0
  40. package/infrastructure/domains/health/docs/TEMPLATE-COMPARISON-IMPLEMENTATION.md +551 -0
  41. package/infrastructure/domains/health/domain/entities/issue.js +299 -0
  42. package/infrastructure/domains/health/domain/entities/issue.test.js +528 -0
  43. package/infrastructure/domains/health/domain/entities/property-mismatch.js +108 -0
  44. package/infrastructure/domains/health/domain/entities/property-mismatch.test.js +275 -0
  45. package/infrastructure/domains/health/domain/entities/resource.js +159 -0
  46. package/infrastructure/domains/health/domain/entities/resource.test.js +432 -0
  47. package/infrastructure/domains/health/domain/entities/stack-health-report.js +306 -0
  48. package/infrastructure/domains/health/domain/entities/stack-health-report.test.js +601 -0
  49. package/infrastructure/domains/health/domain/services/__tests__/health-score-percentage-based.test.js +380 -0
  50. package/infrastructure/domains/health/domain/services/__tests__/import-progress-monitor.test.js +971 -0
  51. package/infrastructure/domains/health/domain/services/__tests__/import-template-generator.test.js +1150 -0
  52. package/infrastructure/domains/health/domain/services/__tests__/logical-id-mapper.test.js +672 -0
  53. package/infrastructure/domains/health/domain/services/__tests__/template-parser.test.js +496 -0
  54. package/infrastructure/domains/health/domain/services/__tests__/update-progress-monitor.test.js +419 -0
  55. package/infrastructure/domains/health/domain/services/health-score-calculator.js +248 -0
  56. package/infrastructure/domains/health/domain/services/health-score-calculator.test.js +504 -0
  57. package/infrastructure/domains/health/domain/services/import-progress-monitor.js +195 -0
  58. package/infrastructure/domains/health/domain/services/import-template-generator.js +435 -0
  59. package/infrastructure/domains/health/domain/services/logical-id-mapper.js +345 -0
  60. package/infrastructure/domains/health/domain/services/mismatch-analyzer.js +234 -0
  61. package/infrastructure/domains/health/domain/services/mismatch-analyzer.test.js +431 -0
  62. package/infrastructure/domains/health/domain/services/property-mutability-config.js +382 -0
  63. package/infrastructure/domains/health/domain/services/template-parser.js +245 -0
  64. package/infrastructure/domains/health/domain/services/update-progress-monitor.js +192 -0
  65. package/infrastructure/domains/health/domain/value-objects/health-score.js +138 -0
  66. package/infrastructure/domains/health/domain/value-objects/health-score.test.js +267 -0
  67. package/infrastructure/domains/health/domain/value-objects/property-mutability.js +161 -0
  68. package/infrastructure/domains/health/domain/value-objects/property-mutability.test.js +198 -0
  69. package/infrastructure/domains/health/domain/value-objects/resource-state.js +167 -0
  70. package/infrastructure/domains/health/domain/value-objects/resource-state.test.js +196 -0
  71. package/infrastructure/domains/health/domain/value-objects/stack-identifier.js +192 -0
  72. package/infrastructure/domains/health/domain/value-objects/stack-identifier.test.js +262 -0
  73. package/infrastructure/domains/health/infrastructure/adapters/__tests__/orphan-detection-cfn-tagged.test.js +312 -0
  74. package/infrastructure/domains/health/infrastructure/adapters/__tests__/orphan-detection-multi-stack.test.js +367 -0
  75. package/infrastructure/domains/health/infrastructure/adapters/__tests__/orphan-detection-relationship-analysis.test.js +432 -0
  76. package/infrastructure/domains/health/infrastructure/adapters/aws-property-reconciler.js +784 -0
  77. package/infrastructure/domains/health/infrastructure/adapters/aws-property-reconciler.test.js +1133 -0
  78. package/infrastructure/domains/health/infrastructure/adapters/aws-resource-detector.js +565 -0
  79. package/infrastructure/domains/health/infrastructure/adapters/aws-resource-detector.test.js +554 -0
  80. package/infrastructure/domains/health/infrastructure/adapters/aws-resource-importer.js +318 -0
  81. package/infrastructure/domains/health/infrastructure/adapters/aws-resource-importer.test.js +398 -0
  82. package/infrastructure/domains/health/infrastructure/adapters/aws-stack-repository.js +777 -0
  83. package/infrastructure/domains/health/infrastructure/adapters/aws-stack-repository.test.js +580 -0
  84. package/infrastructure/domains/integration/integration-builder.js +397 -0
  85. package/infrastructure/domains/integration/integration-builder.test.js +593 -0
  86. package/infrastructure/domains/integration/integration-resolver.js +170 -0
  87. package/infrastructure/domains/integration/integration-resolver.test.js +369 -0
  88. package/infrastructure/domains/integration/websocket-builder.js +69 -0
  89. package/infrastructure/domains/integration/websocket-builder.test.js +195 -0
  90. package/infrastructure/domains/networking/vpc-builder.js +1829 -0
  91. package/infrastructure/domains/networking/vpc-builder.test.js +1262 -0
  92. package/infrastructure/domains/networking/vpc-discovery.js +177 -0
  93. package/infrastructure/domains/networking/vpc-discovery.test.js +350 -0
  94. package/infrastructure/domains/networking/vpc-resolver.js +324 -0
  95. package/infrastructure/domains/networking/vpc-resolver.test.js +501 -0
  96. package/infrastructure/domains/parameters/ssm-builder.js +79 -0
  97. package/infrastructure/domains/parameters/ssm-builder.test.js +189 -0
  98. package/infrastructure/domains/parameters/ssm-discovery.js +84 -0
  99. package/infrastructure/domains/parameters/ssm-discovery.test.js +210 -0
  100. package/infrastructure/{iam-generator.js → domains/security/iam-generator.js} +2 -2
  101. package/infrastructure/domains/security/kms-builder.js +366 -0
  102. package/infrastructure/domains/security/kms-builder.test.js +374 -0
  103. package/infrastructure/domains/security/kms-discovery.js +80 -0
  104. package/infrastructure/domains/security/kms-discovery.test.js +177 -0
  105. package/infrastructure/domains/security/kms-resolver.js +96 -0
  106. package/infrastructure/domains/security/kms-resolver.test.js +216 -0
  107. package/infrastructure/domains/shared/base-builder.js +112 -0
  108. package/infrastructure/domains/shared/base-resolver.js +186 -0
  109. package/infrastructure/domains/shared/base-resolver.test.js +305 -0
  110. package/infrastructure/domains/shared/builder-orchestrator.js +212 -0
  111. package/infrastructure/domains/shared/builder-orchestrator.test.js +213 -0
  112. package/infrastructure/domains/shared/cloudformation-discovery-v2.js +334 -0
  113. package/infrastructure/domains/shared/cloudformation-discovery.js +375 -0
  114. package/infrastructure/domains/shared/cloudformation-discovery.test.js +590 -0
  115. package/infrastructure/domains/shared/environment-builder.js +119 -0
  116. package/infrastructure/domains/shared/environment-builder.test.js +247 -0
  117. package/infrastructure/domains/shared/providers/aws-provider-adapter.js +544 -0
  118. package/infrastructure/domains/shared/providers/aws-provider-adapter.test.js +377 -0
  119. package/infrastructure/domains/shared/providers/azure-provider-adapter.stub.js +93 -0
  120. package/infrastructure/domains/shared/providers/cloud-provider-adapter.js +136 -0
  121. package/infrastructure/domains/shared/providers/gcp-provider-adapter.stub.js +82 -0
  122. package/infrastructure/domains/shared/providers/provider-factory.js +108 -0
  123. package/infrastructure/domains/shared/providers/provider-factory.test.js +170 -0
  124. package/infrastructure/domains/shared/resource-discovery.js +192 -0
  125. package/infrastructure/domains/shared/resource-discovery.test.js +552 -0
  126. package/infrastructure/domains/shared/types/app-definition.js +205 -0
  127. package/infrastructure/domains/shared/types/discovery-result.js +106 -0
  128. package/infrastructure/domains/shared/types/discovery-result.test.js +258 -0
  129. package/infrastructure/domains/shared/types/index.js +46 -0
  130. package/infrastructure/domains/shared/types/resource-ownership.js +108 -0
  131. package/infrastructure/domains/shared/types/resource-ownership.test.js +101 -0
  132. package/infrastructure/domains/shared/utilities/base-definition-factory.js +380 -0
  133. package/infrastructure/domains/shared/utilities/base-definition-factory.js.bak +338 -0
  134. package/infrastructure/domains/shared/utilities/base-definition-factory.test.js +248 -0
  135. package/infrastructure/domains/shared/utilities/handler-path-resolver.js +134 -0
  136. package/infrastructure/domains/shared/utilities/handler-path-resolver.test.js +268 -0
  137. package/infrastructure/domains/shared/utilities/prisma-layer-manager.js +55 -0
  138. package/infrastructure/domains/shared/utilities/prisma-layer-manager.test.js +138 -0
  139. package/infrastructure/{env-validator.js → domains/shared/validation/env-validator.js} +2 -1
  140. package/infrastructure/domains/shared/validation/env-validator.test.js +173 -0
  141. package/infrastructure/esbuild.config.js +53 -0
  142. package/infrastructure/infrastructure-composer.js +87 -0
  143. package/infrastructure/{serverless-template.test.js → infrastructure-composer.test.js} +115 -24
  144. package/infrastructure/scripts/build-prisma-layer.js +553 -0
  145. package/infrastructure/scripts/build-prisma-layer.test.js +102 -0
  146. package/infrastructure/{build-time-discovery.js → scripts/build-time-discovery.js} +80 -48
  147. package/infrastructure/{build-time-discovery.test.js → scripts/build-time-discovery.test.js} +5 -4
  148. package/layers/prisma/nodejs/package.json +8 -0
  149. package/management-ui/server/utils/cliIntegration.js +1 -1
  150. package/management-ui/server/utils/environment/awsParameterStore.js +29 -18
  151. package/package.json +11 -11
  152. package/frigg-cli/.eslintrc.js +0 -141
  153. package/frigg-cli/__tests__/unit/commands/build.test.js +0 -251
  154. package/frigg-cli/__tests__/unit/commands/db-setup.test.js +0 -548
  155. package/frigg-cli/__tests__/unit/commands/install.test.js +0 -400
  156. package/frigg-cli/__tests__/unit/commands/ui.test.js +0 -346
  157. package/frigg-cli/__tests__/unit/utils/database-validator.test.js +0 -366
  158. package/frigg-cli/__tests__/unit/utils/error-messages.test.js +0 -304
  159. package/frigg-cli/__tests__/unit/utils/prisma-runner.test.js +0 -486
  160. package/frigg-cli/__tests__/utils/mock-factory.js +0 -270
  161. package/frigg-cli/__tests__/utils/prisma-mock.js +0 -194
  162. package/frigg-cli/__tests__/utils/test-fixtures.js +0 -463
  163. package/frigg-cli/__tests__/utils/test-setup.js +0 -287
  164. package/frigg-cli/build-command/index.js +0 -65
  165. package/frigg-cli/db-setup-command/index.js +0 -193
  166. package/frigg-cli/deploy-command/index.js +0 -175
  167. package/frigg-cli/generate-command/__tests__/generate-command.test.js +0 -301
  168. package/frigg-cli/generate-command/azure-generator.js +0 -43
  169. package/frigg-cli/generate-command/gcp-generator.js +0 -47
  170. package/frigg-cli/generate-command/index.js +0 -332
  171. package/frigg-cli/generate-command/terraform-generator.js +0 -555
  172. package/frigg-cli/generate-iam-command.js +0 -118
  173. package/frigg-cli/index.js +0 -75
  174. package/frigg-cli/index.test.js +0 -158
  175. package/frigg-cli/init-command/backend-first-handler.js +0 -756
  176. package/frigg-cli/init-command/index.js +0 -93
  177. package/frigg-cli/init-command/template-handler.js +0 -143
  178. package/frigg-cli/install-command/backend-js.js +0 -33
  179. package/frigg-cli/install-command/commit-changes.js +0 -16
  180. package/frigg-cli/install-command/environment-variables.js +0 -127
  181. package/frigg-cli/install-command/environment-variables.test.js +0 -136
  182. package/frigg-cli/install-command/index.js +0 -54
  183. package/frigg-cli/install-command/install-package.js +0 -13
  184. package/frigg-cli/install-command/integration-file.js +0 -30
  185. package/frigg-cli/install-command/logger.js +0 -12
  186. package/frigg-cli/install-command/template.js +0 -90
  187. package/frigg-cli/install-command/validate-package.js +0 -75
  188. package/frigg-cli/jest.config.js +0 -124
  189. package/frigg-cli/package.json +0 -54
  190. package/frigg-cli/start-command/index.js +0 -149
  191. package/frigg-cli/start-command/start-command.test.js +0 -297
  192. package/frigg-cli/test/init-command.test.js +0 -180
  193. package/frigg-cli/test/npm-registry.test.js +0 -319
  194. package/frigg-cli/ui-command/index.js +0 -154
  195. package/frigg-cli/utils/app-resolver.js +0 -319
  196. package/frigg-cli/utils/backend-path.js +0 -25
  197. package/frigg-cli/utils/database-validator.js +0 -161
  198. package/frigg-cli/utils/error-messages.js +0 -257
  199. package/frigg-cli/utils/npm-registry.js +0 -167
  200. package/frigg-cli/utils/prisma-runner.js +0 -280
  201. package/frigg-cli/utils/process-manager.js +0 -199
  202. package/frigg-cli/utils/repo-detection.js +0 -405
  203. package/infrastructure/aws-discovery.js +0 -1176
  204. package/infrastructure/aws-discovery.test.js +0 -1220
  205. package/infrastructure/serverless-template.js +0 -2094
  206. /package/infrastructure/{WEBSOCKET-CONFIGURATION.md → docs/WEBSOCKET-CONFIGURATION.md} +0 -0
  207. /package/infrastructure/{GENERATE-IAM-DOCS.md → docs/generate-iam-command.md} +0 -0
  208. /package/infrastructure/{iam-generator.test.js → domains/security/iam-generator.test.js} +0 -0
  209. /package/infrastructure/{frigg-deployment-iam-stack.yaml → domains/security/templates/frigg-deployment-iam-stack.yaml} +0 -0
  210. /package/infrastructure/{iam-policy-basic.json → domains/security/templates/iam-policy-basic.json} +0 -0
  211. /package/infrastructure/{iam-policy-full.json → domains/security/templates/iam-policy-full.json} +0 -0
  212. /package/infrastructure/{run-discovery.js → scripts/run-discovery.js} +0 -0
@@ -1,199 +0,0 @@
1
- const chalk = require('chalk');
2
- const { spawn } = require('child_process');
3
- const readline = require('readline');
4
-
5
- class ProcessManager {
6
- constructor() {
7
- this.processes = new Map();
8
- this.isShuttingDown = false;
9
- this.outputBuffer = new Map();
10
- this.setupShutdownHandlers();
11
- }
12
-
13
- setupShutdownHandlers() {
14
- const shutdown = async () => {
15
- if (this.isShuttingDown) return;
16
- this.isShuttingDown = true;
17
-
18
- console.log('\n' + chalk.yellow('⏹ Shutting down...'));
19
-
20
- const shutdownPromises = [];
21
- for (const [name, proc] of this.processes) {
22
- shutdownPromises.push(this.killProcess(name, proc));
23
- }
24
-
25
- await Promise.all(shutdownPromises);
26
-
27
- console.log(chalk.green('✓ All processes stopped cleanly'));
28
- process.exit(0);
29
- };
30
-
31
- process.on('SIGINT', shutdown);
32
- process.on('SIGTERM', shutdown);
33
- process.on('exit', () => {
34
- for (const [, proc] of this.processes) {
35
- try {
36
- proc.kill('SIGKILL');
37
- } catch (e) {
38
- // Process already dead
39
- }
40
- }
41
- });
42
- }
43
-
44
- async killProcess(name, proc) {
45
- return new Promise((resolve) => {
46
- if (!proc || proc.killed) {
47
- resolve();
48
- return;
49
- }
50
-
51
- const timeout = setTimeout(() => {
52
- try {
53
- proc.kill('SIGKILL');
54
- } catch (e) {
55
- // Process already dead
56
- }
57
- resolve();
58
- }, 5000);
59
-
60
- proc.once('exit', () => {
61
- clearTimeout(timeout);
62
- this.processes.delete(name);
63
- resolve();
64
- });
65
-
66
- try {
67
- proc.kill('SIGTERM');
68
- } catch (e) {
69
- clearTimeout(timeout);
70
- resolve();
71
- }
72
- });
73
- }
74
-
75
- spawnProcess(name, command, args, options = {}) {
76
- const proc = spawn(command, args, {
77
- ...options,
78
- stdio: ['inherit', 'pipe', 'pipe'],
79
- shell: true
80
- });
81
-
82
- this.processes.set(name, proc);
83
- this.outputBuffer.set(name, []);
84
-
85
- // Create readline interfaces for better line handling
86
- const stdoutReader = readline.createInterface({
87
- input: proc.stdout,
88
- crlfDelay: Infinity
89
- });
90
-
91
- const stderrReader = readline.createInterface({
92
- input: proc.stderr,
93
- crlfDelay: Infinity
94
- });
95
-
96
- stdoutReader.on('line', (line) => {
97
- if (!this.isShuttingDown) {
98
- this.handleOutput(name, line, 'stdout');
99
- }
100
- });
101
-
102
- stderrReader.on('line', (line) => {
103
- if (!this.isShuttingDown) {
104
- this.handleOutput(name, line, 'stderr');
105
- }
106
- });
107
-
108
- proc.on('error', (error) => {
109
- if (!this.isShuttingDown) {
110
- console.error(chalk.red(`[${name}] Process error:`), error.message);
111
- }
112
- });
113
-
114
- proc.on('exit', (code, signal) => {
115
- this.processes.delete(name);
116
- if (!this.isShuttingDown && code !== 0 && code !== null) {
117
- console.error(chalk.red(`[${name}] Process exited with code ${code}`));
118
- }
119
- });
120
-
121
- return proc;
122
- }
123
-
124
- handleOutput(processName, line, stream) {
125
- // Filter out noisy/redundant messages
126
- const filters = [
127
- /VITE v\d+\.\d+\.\d+/,
128
- /ready in \d+ ms/,
129
- /Local:/,
130
- /Network:/,
131
- /press h \+ enter to show help/,
132
- /\[nodemon\]/,
133
- /watching for file changes/,
134
- /restarting due to changes/,
135
- /starting/,
136
- /clean exit/,
137
- /waiting for changes before restart/,
138
- /^$/ // empty lines
139
- ];
140
-
141
- if (filters.some(filter => filter.test(line))) {
142
- return;
143
- }
144
-
145
- // Format output based on process
146
- const prefix = this.getProcessPrefix(processName);
147
- const coloredLine = this.colorizeOutput(line, stream);
148
-
149
- console.log(`${prefix} ${coloredLine}`);
150
- }
151
-
152
- getProcessPrefix(processName) {
153
- const prefixes = {
154
- 'frontend': chalk.blue('[Frontend]'),
155
- 'backend': chalk.green('[Backend]'),
156
- 'vite': chalk.blue('[Vite]'),
157
- 'server': chalk.green('[Server]')
158
- };
159
-
160
- return prefixes[processName.toLowerCase()] || chalk.gray(`[${processName}]`);
161
- }
162
-
163
- colorizeOutput(line, stream) {
164
- // Error detection
165
- if (stream === 'stderr' || /error|fail|exception/i.test(line)) {
166
- return chalk.red(line);
167
- }
168
-
169
- // Warning detection
170
- if (/warn|warning/i.test(line)) {
171
- return chalk.yellow(line);
172
- }
173
-
174
- // Success detection
175
- if (/success|ready|started|listening|compiled/i.test(line)) {
176
- return chalk.green(line);
177
- }
178
-
179
- // Info detection
180
- if (/info|starting/i.test(line)) {
181
- return chalk.blue(line);
182
- }
183
-
184
- return chalk.gray(line);
185
- }
186
-
187
- printStatus(frontendUrl, backendUrl, repoName) {
188
- console.log('\n' + chalk.bold.green('✨ Frigg Management UI is ready!'));
189
- console.log('');
190
- console.log(chalk.cyan(' Frontend: ') + chalk.white(frontendUrl));
191
- console.log(chalk.cyan(' Backend: ') + chalk.white(backendUrl));
192
- console.log(chalk.cyan(' Repository:') + chalk.white(` ${repoName}`));
193
- console.log('');
194
- console.log(chalk.gray(' Press ' + chalk.bold('Ctrl+C') + ' to stop all servers'));
195
- console.log('');
196
- }
197
- }
198
-
199
- module.exports = ProcessManager;
@@ -1,405 +0,0 @@
1
- /**
2
- * Copyright (c) 2024 Frigg Integration Framework
3
- *
4
- * This source code is licensed under the MIT license found in the
5
- * LICENSE file in the root directory of this source tree.
6
- */
7
-
8
- 'use strict';
9
-
10
- const fs = require('fs-extra');
11
- const path = require('path');
12
- const os = require('os');
13
- const chalk = require('chalk');
14
-
15
- /**
16
- * Checks if a directory is a Frigg repository
17
- * @param {string} directory - Path to check
18
- * @returns {Promise<{isFriggRepo: boolean, repoInfo: object|null}>}
19
- */
20
- async function isFriggRepository(directory) {
21
- try {
22
- const packageJsonPath = path.join(directory, 'package.json');
23
-
24
- // Check if package.json exists
25
- if (!fs.existsSync(packageJsonPath)) {
26
- return { isFriggRepo: false, repoInfo: null };
27
- }
28
-
29
- const packageJson = await fs.readJson(packageJsonPath);
30
-
31
- // Primary indicators of a Frigg repository
32
- const indicators = {
33
- hasFriggDependencies: false,
34
- hasBackendWorkspace: false,
35
- hasFrontendWorkspace: false,
36
- hasServerlessConfig: false,
37
- friggDependencies: []
38
- };
39
-
40
- // Check for @friggframework dependencies
41
- const allDeps = {
42
- ...packageJson.dependencies,
43
- ...packageJson.devDependencies,
44
- ...packageJson.peerDependencies
45
- };
46
-
47
- for (const dep in allDeps) {
48
- if (dep.startsWith('@friggframework/')) {
49
- indicators.hasFriggDependencies = true;
50
- indicators.friggDependencies.push(dep);
51
- }
52
- }
53
-
54
- // Check for Frigg-specific files
55
- const friggConfigFiles = [
56
- 'frigg.config.js',
57
- 'frigg.config.json',
58
- '.friggrc',
59
- '.friggrc.json',
60
- '.friggrc.js'
61
- ];
62
-
63
- indicators.hasFriggConfig = friggConfigFiles.some(file =>
64
- fs.existsSync(path.join(directory, file))
65
- );
66
-
67
- // Check for Frigg-specific directories
68
- const friggDirs = [
69
- '.frigg',
70
- 'frigg-modules',
71
- 'api-modules'
72
- ];
73
-
74
- indicators.hasFriggDirectories = friggDirs.some(dir =>
75
- fs.existsSync(path.join(directory, dir))
76
- );
77
-
78
- // Check for Frigg-specific scripts in package.json
79
- indicators.hasFriggScripts = false;
80
- if (packageJson.scripts) {
81
- const friggScriptPatterns = ['frigg', 'frigg-dev', 'frigg-build', 'frigg-deploy'];
82
- indicators.hasFriggScripts = Object.keys(packageJson.scripts).some(script =>
83
- friggScriptPatterns.some(pattern => script.includes(pattern)) ||
84
- Object.values(packageJson.scripts).some(cmd =>
85
- typeof cmd === 'string' && cmd.includes('frigg ')
86
- )
87
- );
88
- }
89
-
90
- // Check for workspace structure
91
- if (packageJson.workspaces) {
92
- const workspaces = Array.isArray(packageJson.workspaces)
93
- ? packageJson.workspaces
94
- : packageJson.workspaces.packages || [];
95
-
96
- indicators.hasBackendWorkspace = workspaces.some(ws =>
97
- ws.includes('backend') || ws === 'backend'
98
- );
99
- indicators.hasFrontendWorkspace = workspaces.some(ws =>
100
- ws.includes('frontend') || ws === 'frontend'
101
- );
102
- }
103
-
104
- // Check for backend/serverless.yml
105
- const serverlessPath = path.join(directory, 'backend', 'serverless.yml');
106
- indicators.hasServerlessConfig = fs.existsSync(serverlessPath);
107
-
108
- // Check for individual frontend directories (React, Vue, etc.)
109
- const frontendDirs = ['frontend', 'react', 'vue', 'svelte', 'angular'];
110
- const existingFrontendDirs = frontendDirs.filter(dir =>
111
- fs.existsSync(path.join(directory, dir))
112
- );
113
-
114
- // Skip @friggframework packages (they're framework packages, not Frigg apps)
115
- if (packageJson.name && packageJson.name.startsWith('@friggframework/')) {
116
- return { isFriggRepo: false, repoInfo: null };
117
- }
118
-
119
- // Additional check for Zapier apps that shouldn't be detected as Frigg repos
120
- const isZapierApp = packageJson.name && (
121
- packageJson.name.includes('zapier-public') ||
122
- packageJson.name.includes('zapier-app') ||
123
- (packageJson.scripts && packageJson.scripts.zapier)
124
- );
125
-
126
- // Check for specific Frigg indicators in serverless.yml
127
- let hasFriggServerlessIndicators = false;
128
- if (indicators.hasServerlessConfig) {
129
- try {
130
- const serverlessContent = fs.readFileSync(serverlessPath, 'utf8');
131
- hasFriggServerlessIndicators = serverlessContent.includes('frigg') ||
132
- serverlessContent.includes('FriggHandler') ||
133
- serverlessContent.includes('@friggframework');
134
- } catch (error) {
135
- // Ignore read errors
136
- }
137
- }
138
-
139
- // A directory is considered a Frigg repo if it has:
140
- // 1. Frigg dependencies (MANDATORY - most reliable indicator) OR
141
- // 2. Frigg-specific configuration files OR
142
- // 3. Frigg-specific directories OR
143
- // 4. Frigg-specific scripts in package.json OR
144
- // 5. Serverless config with explicit Frigg references AND proper structure
145
- //
146
- // For Zapier apps, we require explicit Frigg indicators
147
- const hasFriggIndicators = indicators.hasFriggDependencies ||
148
- indicators.hasFriggConfig ||
149
- indicators.hasFriggDirectories ||
150
- indicators.hasFriggScripts ||
151
- hasFriggServerlessIndicators;
152
-
153
- // Determine if it's a Frigg repository
154
- let isFriggRepo = false;
155
-
156
- if (isZapierApp) {
157
- // For Zapier apps, require explicit Frigg dependencies or config
158
- isFriggRepo = indicators.hasFriggDependencies || indicators.hasFriggConfig;
159
- } else {
160
- // For non-Zapier apps, any Frigg indicator is sufficient
161
- isFriggRepo = hasFriggIndicators;
162
- }
163
-
164
- // Additional validation for edge cases
165
- if (isZapierApp && !indicators.hasFriggDependencies && !indicators.hasFriggConfig) {
166
- return { isFriggRepo: false, repoInfo: null };
167
- }
168
-
169
- if (isFriggRepo) {
170
- return {
171
- isFriggRepo: true,
172
- repoInfo: {
173
- name: packageJson.name || path.basename(directory),
174
- path: directory,
175
- version: packageJson.version,
176
- framework: detectFramework(directory, existingFrontendDirs),
177
- hasBackend: fs.existsSync(path.join(directory, 'backend')),
178
- friggDependencies: indicators.friggDependencies,
179
- workspaces: packageJson.workspaces,
180
- hasFriggConfig: indicators.hasFriggConfig,
181
- hasFriggDirectories: indicators.hasFriggDirectories,
182
- isZapierApp: isZapierApp,
183
- ...indicators
184
- }
185
- };
186
- }
187
-
188
- return { isFriggRepo: false, repoInfo: null };
189
-
190
- } catch (error) {
191
- return { isFriggRepo: false, repoInfo: null };
192
- }
193
- }
194
-
195
- /**
196
- * Detects the frontend framework used in the Frigg repository
197
- * @param {string} directory - Repository directory
198
- * @param {string[]} existingFrontendDirs - List of existing frontend directories
199
- * @returns {string} Framework name
200
- */
201
- function detectFramework(directory, existingFrontendDirs) {
202
- // Check for framework-specific directories
203
- const frameworkDirs = {
204
- 'react': 'React',
205
- 'vue': 'Vue',
206
- 'svelte': 'Svelte',
207
- 'angular': 'Angular'
208
- };
209
-
210
- for (const dir of existingFrontendDirs) {
211
- if (frameworkDirs[dir]) {
212
- return frameworkDirs[dir];
213
- }
214
- }
215
-
216
- // Check frontend directory for framework indicators
217
- const frontendPath = path.join(directory, 'frontend');
218
- if (fs.existsSync(frontendPath)) {
219
- try {
220
- const frontendPackageJson = path.join(frontendPath, 'package.json');
221
- if (fs.existsSync(frontendPackageJson)) {
222
- const frontendPkg = fs.readJsonSync(frontendPackageJson);
223
- const deps = { ...frontendPkg.dependencies, ...frontendPkg.devDependencies };
224
-
225
- if (deps.react) return 'React';
226
- if (deps.vue) return 'Vue';
227
- if (deps.svelte) return 'Svelte';
228
- if (deps['@angular/core']) return 'Angular';
229
- }
230
- } catch (error) {
231
- // Ignore errors
232
- }
233
- }
234
-
235
- return 'Unknown';
236
- }
237
-
238
- /**
239
- * Searches for Frigg repositories in common locations
240
- * @param {Object} options - Search options
241
- * @returns {Promise<Array>} Array of discovered repositories
242
- */
243
- async function discoverFriggRepositories(options = {}) {
244
- const {
245
- searchPaths = [
246
- process.cwd(),
247
- path.join(os.homedir(), 'Documents'),
248
- path.join(os.homedir(), 'Projects'),
249
- path.join(os.homedir(), 'Development'),
250
- path.join(os.homedir(), 'dev'),
251
- path.join(os.homedir(), 'Code')
252
- ],
253
- maxDepth = 3,
254
- excludePatterns = ['node_modules', '.git', 'dist', 'build', '.next', 'coverage']
255
- } = options;
256
-
257
- const discoveredRepos = [];
258
- const visited = new Set();
259
-
260
- for (const searchPath of searchPaths) {
261
- if (fs.existsSync(searchPath)) {
262
- await searchDirectory(searchPath, 0, maxDepth, excludePatterns, discoveredRepos, visited);
263
- }
264
- }
265
-
266
- // Remove duplicates and sort by name
267
- const uniqueRepos = Array.from(
268
- new Map(discoveredRepos.map(repo => [repo.path, repo])).values()
269
- );
270
-
271
- return uniqueRepos.sort((a, b) => a.name.localeCompare(b.name));
272
- }
273
-
274
- /**
275
- * Recursively searches a directory for Frigg repositories
276
- */
277
- async function searchDirectory(dirPath, currentDepth, maxDepth, excludePatterns, results, visited) {
278
- // Avoid infinite loops from symlinks
279
- const realPath = fs.realpathSync(dirPath);
280
- if (visited.has(realPath)) return;
281
- visited.add(realPath);
282
-
283
- if (currentDepth > maxDepth) return;
284
-
285
- try {
286
- // Check if current directory is a Frigg repo
287
- const { isFriggRepo, repoInfo } = await isFriggRepository(dirPath);
288
- if (isFriggRepo) {
289
- results.push(repoInfo);
290
- return; // Don't search inside Frigg repos
291
- }
292
-
293
- // Continue searching subdirectories
294
- const entries = await fs.readdir(dirPath, { withFileTypes: true });
295
-
296
- for (const entry of entries) {
297
- if (entry.isDirectory()) {
298
- const entryName = entry.name;
299
-
300
- // Skip excluded patterns
301
- if (excludePatterns.some(pattern => entryName.includes(pattern))) {
302
- continue;
303
- }
304
-
305
- // Skip hidden directories except .git for workspace detection
306
- if (entryName.startsWith('.') && entryName !== '.git') {
307
- continue;
308
- }
309
-
310
- const entryPath = path.join(dirPath, entryName);
311
- await searchDirectory(entryPath, currentDepth + 1, maxDepth, excludePatterns, results, visited);
312
- }
313
- }
314
- } catch (error) {
315
- // Silently skip directories we can't access
316
- }
317
- }
318
-
319
- /**
320
- * Gets the current directory's Frigg repository status
321
- * @returns {Promise<Object>} Current repository info
322
- */
323
- async function getCurrentRepositoryInfo() {
324
- const currentDir = process.cwd();
325
-
326
- // Check current directory
327
- let { isFriggRepo, repoInfo } = await isFriggRepository(currentDir);
328
-
329
- if (isFriggRepo) {
330
- return { ...repoInfo, isCurrent: true };
331
- }
332
-
333
- // Check parent directories up to 3 levels
334
- let checkDir = currentDir;
335
- for (let i = 0; i < 3; i++) {
336
- const parentDir = path.dirname(checkDir);
337
- if (parentDir === checkDir) break; // Reached root
338
-
339
- const result = await isFriggRepository(parentDir);
340
- if (result.isFriggRepo) {
341
- return { ...result.repoInfo, isCurrent: false, currentSubPath: path.relative(parentDir, currentDir) };
342
- }
343
- checkDir = parentDir;
344
- }
345
-
346
- return null;
347
- }
348
-
349
- /**
350
- * Prompts user to select a repository from discovered repos
351
- * @param {Array} repositories - List of discovered repositories
352
- * @returns {Promise<Object|null>} Selected repository or null
353
- */
354
- async function promptRepositorySelection(repositories) {
355
- if (repositories.length === 0) {
356
- console.log(chalk.yellow('No Frigg repositories found.'));
357
- console.log(chalk.gray('To create a new Frigg project, run: frigg init <project-name>'));
358
- return null;
359
- }
360
-
361
- if (repositories.length === 1) {
362
- console.log(chalk.green(`Found 1 Frigg repository: ${repositories[0].name}`));
363
- return repositories[0];
364
- }
365
-
366
- console.log(chalk.blue(`Found ${repositories.length} Frigg repositories:`));
367
- console.log();
368
-
369
- repositories.forEach((repo, index) => {
370
- const framework = repo.framework !== 'Unknown' ? chalk.gray(`(${repo.framework})`) : '';
371
- console.log(` ${chalk.cyan((index + 1).toString().padStart(2))}. ${chalk.white(repo.name)} ${framework}`);
372
- console.log(` ${chalk.gray(repo.path)}`);
373
- });
374
-
375
- console.log();
376
-
377
- // For now, return the first one. In a full implementation, you'd use a prompt library
378
- console.log(chalk.yellow('Auto-selecting first repository. Use interactive selection in future versions.'));
379
- return repositories[0];
380
- }
381
-
382
- /**
383
- * Formats repository information for display
384
- * @param {Object} repoInfo - Repository information
385
- * @returns {string} Formatted display string
386
- */
387
- function formatRepositoryInfo(repoInfo) {
388
- const parts = [
389
- chalk.white(repoInfo.name),
390
- repoInfo.version ? chalk.gray(`v${repoInfo.version}`) : '',
391
- repoInfo.framework !== 'Unknown' ? chalk.blue(`[${repoInfo.framework}]`) : '',
392
- repoInfo.hasBackend ? chalk.green('[Backend]') : ''
393
- ].filter(Boolean);
394
-
395
- return parts.join(' ');
396
- }
397
-
398
- module.exports = {
399
- isFriggRepository,
400
- discoverFriggRepositories,
401
- getCurrentRepositoryInfo,
402
- promptRepositorySelection,
403
- formatRepositoryInfo,
404
- detectFramework
405
- };