@friggframework/devtools 2.0.0--canary.545.e256e95.0 → 2.0.0--canary.553.dc5f898.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.
- package/frigg-cli/README.md +1 -1
- package/frigg-cli/__tests__/unit/commands/build.test.js +1 -1
- package/frigg-cli/__tests__/unit/commands/doctor.test.js +2 -0
- package/frigg-cli/__tests__/unit/commands/install.test.js +19 -23
- package/frigg-cli/__tests__/unit/dependencies.test.js +2 -2
- package/frigg-cli/build-command/index.js +11 -123
- package/frigg-cli/deploy-command/index.js +1 -83
- package/frigg-cli/doctor-command/index.js +16 -37
- package/frigg-cli/generate-iam-command.js +1 -21
- package/frigg-cli/index.js +6 -21
- package/frigg-cli/index.test.js +2 -7
- package/frigg-cli/init-command/backend-first-handler.js +42 -124
- package/frigg-cli/init-command/index.js +1 -2
- package/frigg-cli/init-command/template-handler.js +3 -13
- package/frigg-cli/install-command/backend-js.js +3 -3
- package/frigg-cli/install-command/environment-variables.js +19 -16
- package/frigg-cli/install-command/environment-variables.test.js +13 -12
- package/frigg-cli/install-command/index.js +9 -14
- package/frigg-cli/install-command/integration-file.js +3 -3
- package/frigg-cli/install-command/logger.js +12 -0
- package/frigg-cli/install-command/validate-package.js +9 -5
- package/frigg-cli/jest.config.js +1 -4
- package/frigg-cli/repair-command/index.js +128 -121
- package/frigg-cli/start-command/index.js +2 -324
- package/frigg-cli/ui-command/index.js +36 -58
- package/frigg-cli/utils/repo-detection.js +37 -85
- package/infrastructure/create-frigg-infrastructure.js +0 -93
- package/infrastructure/docs/iam-policy-templates.md +1 -1
- package/infrastructure/domains/integration/integration-builder.js +3 -2
- package/infrastructure/domains/integration/integration-builder.test.js +54 -2
- package/infrastructure/domains/networking/vpc-builder.test.js +4 -2
- package/infrastructure/domains/networking/vpc-resolver.test.js +1 -1
- package/infrastructure/domains/shared/resource-discovery.js +5 -5
- package/infrastructure/domains/shared/types/app-definition.js +0 -35
- package/infrastructure/domains/shared/types/discovery-result.test.js +1 -1
- package/infrastructure/domains/shared/utilities/base-definition-factory.js +1 -10
- package/infrastructure/domains/shared/utilities/base-definition-factory.test.js +2 -2
- package/infrastructure/infrastructure-composer.js +0 -2
- package/infrastructure/infrastructure-composer.test.js +6 -5
- package/management-ui/README.md +109 -245
- package/package.json +7 -8
- package/frigg-cli/__tests__/application/use-cases/AddApiModuleToIntegrationUseCase.test.js +0 -326
- package/frigg-cli/__tests__/application/use-cases/CreateApiModuleUseCase.test.js +0 -337
- package/frigg-cli/__tests__/domain/entities/ApiModule.test.js +0 -373
- package/frigg-cli/__tests__/domain/entities/AppDefinition.test.js +0 -313
- package/frigg-cli/__tests__/domain/services/IntegrationValidator.test.js +0 -269
- package/frigg-cli/__tests__/domain/value-objects/IntegrationName.test.js +0 -82
- package/frigg-cli/__tests__/infrastructure/adapters/IntegrationJsUpdater.test.js +0 -408
- package/frigg-cli/__tests__/infrastructure/repositories/FileSystemApiModuleRepository.test.js +0 -583
- package/frigg-cli/__tests__/infrastructure/repositories/FileSystemAppDefinitionRepository.test.js +0 -314
- package/frigg-cli/__tests__/infrastructure/repositories/FileSystemIntegrationRepository.test.js +0 -383
- package/frigg-cli/__tests__/unit/commands/init.test.js +0 -406
- package/frigg-cli/__tests__/unit/commands/provider-dispatch.test.js +0 -383
- package/frigg-cli/__tests__/unit/commands/repair.test.js +0 -275
- package/frigg-cli/__tests__/unit/start-command/application/RunPreflightChecksUseCase.test.js +0 -411
- package/frigg-cli/__tests__/unit/start-command/infrastructure/DatabaseAdapter.test.js +0 -405
- package/frigg-cli/__tests__/unit/start-command/infrastructure/DockerAdapter.test.js +0 -496
- package/frigg-cli/__tests__/unit/start-command/presentation/InteractivePromptAdapter.test.js +0 -474
- package/frigg-cli/__tests__/unit/utils/output.test.js +0 -196
- package/frigg-cli/application/use-cases/AddApiModuleToIntegrationUseCase.js +0 -93
- package/frigg-cli/application/use-cases/CreateApiModuleUseCase.js +0 -93
- package/frigg-cli/application/use-cases/CreateIntegrationUseCase.js +0 -103
- package/frigg-cli/container.js +0 -172
- package/frigg-cli/docs/OUTPUT_MIGRATION_GUIDE.md +0 -286
- package/frigg-cli/domain/entities/ApiModule.js +0 -272
- package/frigg-cli/domain/entities/AppDefinition.js +0 -227
- package/frigg-cli/domain/entities/Integration.js +0 -198
- package/frigg-cli/domain/exceptions/DomainException.js +0 -24
- package/frigg-cli/domain/ports/IApiModuleRepository.js +0 -53
- package/frigg-cli/domain/ports/IAppDefinitionRepository.js +0 -43
- package/frigg-cli/domain/ports/IIntegrationRepository.js +0 -61
- package/frigg-cli/domain/services/IntegrationValidator.js +0 -185
- package/frigg-cli/domain/value-objects/IntegrationId.js +0 -42
- package/frigg-cli/domain/value-objects/IntegrationName.js +0 -60
- package/frigg-cli/domain/value-objects/SemanticVersion.js +0 -70
- package/frigg-cli/infrastructure/UnitOfWork.js +0 -46
- package/frigg-cli/infrastructure/adapters/BackendJsUpdater.js +0 -197
- package/frigg-cli/infrastructure/adapters/FileSystemAdapter.js +0 -224
- package/frigg-cli/infrastructure/adapters/IntegrationJsUpdater.js +0 -249
- package/frigg-cli/infrastructure/adapters/SchemaValidator.js +0 -92
- package/frigg-cli/infrastructure/repositories/FileSystemApiModuleRepository.js +0 -373
- package/frigg-cli/infrastructure/repositories/FileSystemAppDefinitionRepository.js +0 -116
- package/frigg-cli/infrastructure/repositories/FileSystemIntegrationRepository.js +0 -277
- package/frigg-cli/package-lock.json +0 -16226
- package/frigg-cli/start-command/application/RunPreflightChecksUseCase.js +0 -376
- package/frigg-cli/start-command/infrastructure/DatabaseAdapter.js +0 -591
- package/frigg-cli/start-command/infrastructure/DockerAdapter.js +0 -306
- package/frigg-cli/start-command/presentation/InteractivePromptAdapter.js +0 -329
- package/frigg-cli/templates/backend/.env.example +0 -62
- package/frigg-cli/templates/backend/.eslintrc.json +0 -12
- package/frigg-cli/templates/backend/.prettierrc +0 -6
- package/frigg-cli/templates/backend/docker-compose.yml +0 -22
- package/frigg-cli/templates/backend/index.js +0 -96
- package/frigg-cli/templates/backend/infrastructure.js +0 -12
- package/frigg-cli/templates/backend/jest.config.js +0 -17
- package/frigg-cli/templates/backend/package.json +0 -50
- package/frigg-cli/templates/backend/src/api-modules/.gitkeep +0 -10
- package/frigg-cli/templates/backend/src/base/.gitkeep +0 -7
- package/frigg-cli/templates/backend/src/integrations/.gitkeep +0 -10
- package/frigg-cli/templates/backend/src/integrations/ExampleIntegration.js +0 -65
- package/frigg-cli/templates/backend/src/utils/.gitkeep +0 -7
- package/frigg-cli/templates/backend/test/setup.js +0 -30
- package/frigg-cli/templates/backend/ui-extensions/.gitkeep +0 -0
- package/frigg-cli/templates/backend/ui-extensions/README.md +0 -77
- package/frigg-cli/utils/__tests__/provider-helper.test.js +0 -55
- package/frigg-cli/utils/__tests__/repo-detection.test.js +0 -436
- package/frigg-cli/utils/output.js +0 -382
- package/frigg-cli/utils/provider-helper.js +0 -75
- package/frigg-cli/validate-command/__tests__/adapters/validate-command.test.js +0 -205
- package/frigg-cli/validate-command/__tests__/application/validate-app-use-case.test.js +0 -104
- package/frigg-cli/validate-command/__tests__/domain/fix-suggestion.test.js +0 -153
- package/frigg-cli/validate-command/__tests__/domain/validation-error.test.js +0 -162
- package/frigg-cli/validate-command/__tests__/domain/validation-result.test.js +0 -152
- package/frigg-cli/validate-command/__tests__/infrastructure/api-module-validator.test.js +0 -332
- package/frigg-cli/validate-command/__tests__/infrastructure/app-definition-validator.test.js +0 -191
- package/frigg-cli/validate-command/__tests__/infrastructure/integration-class-validator.test.js +0 -146
- package/frigg-cli/validate-command/__tests__/infrastructure/template-validation.test.js +0 -155
- package/frigg-cli/validate-command/adapters/cli/validate-command.js +0 -199
- package/frigg-cli/validate-command/application/use-cases/validate-app-use-case.js +0 -35
- package/frigg-cli/validate-command/domain/entities/validation-result.js +0 -74
- package/frigg-cli/validate-command/domain/value-objects/fix-suggestion.js +0 -74
- package/frigg-cli/validate-command/domain/value-objects/validation-error.js +0 -68
- package/frigg-cli/validate-command/infrastructure/validators/api-module-validator.js +0 -181
- package/frigg-cli/validate-command/infrastructure/validators/app-definition-validator.js +0 -145
- package/frigg-cli/validate-command/infrastructure/validators/integration-class-validator.js +0 -113
- package/infrastructure/domains/admin-scripts/admin-script-builder.js +0 -200
- package/infrastructure/domains/admin-scripts/admin-script-builder.test.js +0 -499
- package/infrastructure/domains/admin-scripts/index.js +0 -5
- package/infrastructure/jest.config.js +0 -16
|
@@ -37,70 +37,17 @@ async function isFriggRepository(directory) {
|
|
|
37
37
|
friggDependencies: []
|
|
38
38
|
};
|
|
39
39
|
|
|
40
|
-
// Check for @friggframework
|
|
41
|
-
// Check both root package.json and backend/package.json for workspace projects
|
|
40
|
+
// Check for @friggframework dependencies
|
|
42
41
|
const allDeps = {
|
|
43
42
|
...packageJson.dependencies,
|
|
44
|
-
...packageJson.devDependencies
|
|
43
|
+
...packageJson.devDependencies,
|
|
44
|
+
...packageJson.peerDependencies
|
|
45
45
|
};
|
|
46
46
|
|
|
47
|
-
// Check root package.json for core dependency
|
|
48
|
-
let coreVersion = allDeps['@friggframework/core'];
|
|
49
|
-
let hasFriggCore = coreVersion && (
|
|
50
|
-
coreVersion === 'next' ||
|
|
51
|
-
coreVersion.includes('2.0.0') ||
|
|
52
|
-
coreVersion.includes('next') ||
|
|
53
|
-
coreVersion.startsWith('^2.') ||
|
|
54
|
-
coreVersion.startsWith('~2.') ||
|
|
55
|
-
coreVersion.startsWith('2.')
|
|
56
|
-
);
|
|
57
|
-
|
|
58
|
-
// If not in root, check backend/package.json for workspace projects
|
|
59
|
-
if (!hasFriggCore) {
|
|
60
|
-
const backendPackagePath = path.join(directory, 'backend', 'package.json');
|
|
61
|
-
if (fs.existsSync(backendPackagePath)) {
|
|
62
|
-
try {
|
|
63
|
-
const backendPackageJson = await fs.readJson(backendPackagePath);
|
|
64
|
-
const backendDeps = {
|
|
65
|
-
...backendPackageJson.dependencies,
|
|
66
|
-
...backendPackageJson.devDependencies
|
|
67
|
-
};
|
|
68
|
-
coreVersion = backendDeps['@friggframework/core'];
|
|
69
|
-
hasFriggCore = coreVersion && (
|
|
70
|
-
coreVersion === 'next' ||
|
|
71
|
-
coreVersion.includes('2.0.0') ||
|
|
72
|
-
coreVersion.includes('next') ||
|
|
73
|
-
coreVersion.startsWith('^2.') ||
|
|
74
|
-
coreVersion.startsWith('~2.') ||
|
|
75
|
-
coreVersion.startsWith('2.')
|
|
76
|
-
);
|
|
77
|
-
} catch (error) {
|
|
78
|
-
// Ignore errors reading backend package.json
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
if (hasFriggCore) {
|
|
84
|
-
indicators.hasFriggDependencies = true;
|
|
85
|
-
indicators.friggDependencies.push('@friggframework/core');
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
// Also track other Frigg dependencies for reference
|
|
89
47
|
for (const dep in allDeps) {
|
|
90
|
-
if (dep.startsWith('@friggframework/')
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
version === 'next' ||
|
|
94
|
-
version.includes('2.0.0') ||
|
|
95
|
-
version.includes('next') ||
|
|
96
|
-
version.startsWith('^2.') ||
|
|
97
|
-
version.startsWith('~2.') ||
|
|
98
|
-
version.startsWith('2.')
|
|
99
|
-
);
|
|
100
|
-
|
|
101
|
-
if (isV2Plus) {
|
|
102
|
-
indicators.friggDependencies.push(dep);
|
|
103
|
-
}
|
|
48
|
+
if (dep.startsWith('@friggframework/')) {
|
|
49
|
+
indicators.hasFriggDependencies = true;
|
|
50
|
+
indicators.friggDependencies.push(dep);
|
|
104
51
|
}
|
|
105
52
|
}
|
|
106
53
|
|
|
@@ -189,33 +136,42 @@ async function isFriggRepository(directory) {
|
|
|
189
136
|
}
|
|
190
137
|
}
|
|
191
138
|
|
|
192
|
-
//
|
|
193
|
-
// 1.
|
|
194
|
-
// 2.
|
|
195
|
-
// 3.
|
|
196
|
-
|
|
197
|
-
//
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
const
|
|
201
|
-
|
|
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
|
+
}
|
|
202
163
|
|
|
203
|
-
//
|
|
204
|
-
|
|
164
|
+
// Additional validation for edge cases
|
|
165
|
+
if (isZapierApp && !indicators.hasFriggDependencies && !indicators.hasFriggConfig) {
|
|
166
|
+
return { isFriggRepo: false, repoInfo: null };
|
|
167
|
+
}
|
|
205
168
|
|
|
206
169
|
if (isFriggRepo) {
|
|
207
|
-
// IMPORTANT: If backend/ has a Frigg app structure, use backend/ as the path
|
|
208
|
-
// This handles workspace projects where the actual app is in backend/
|
|
209
|
-
let actualPath = directory;
|
|
210
|
-
if (hasBackendIndexJs || hasBackendServerless) {
|
|
211
|
-
actualPath = path.join(directory, 'backend');
|
|
212
|
-
}
|
|
213
|
-
|
|
214
170
|
return {
|
|
215
171
|
isFriggRepo: true,
|
|
216
172
|
repoInfo: {
|
|
217
173
|
name: packageJson.name || path.basename(directory),
|
|
218
|
-
path:
|
|
174
|
+
path: directory,
|
|
219
175
|
version: packageJson.version,
|
|
220
176
|
framework: detectFramework(directory, existingFrontendDirs),
|
|
221
177
|
hasBackend: fs.existsSync(path.join(directory, 'backend')),
|
|
@@ -289,16 +245,12 @@ async function discoverFriggRepositories(options = {}) {
|
|
|
289
245
|
searchPaths = [
|
|
290
246
|
process.cwd(),
|
|
291
247
|
path.join(os.homedir(), 'Documents'),
|
|
292
|
-
path.join(os.homedir(), 'Documents', 'GitHub'), // Common GitHub Desktop location
|
|
293
248
|
path.join(os.homedir(), 'Projects'),
|
|
294
249
|
path.join(os.homedir(), 'Development'),
|
|
295
250
|
path.join(os.homedir(), 'dev'),
|
|
296
|
-
path.join(os.homedir(), 'Code')
|
|
297
|
-
path.join(os.homedir(), 'GitHub'), // Alternative GitHub location
|
|
298
|
-
path.join(os.homedir(), 'repos'), // Common repos folder
|
|
299
|
-
path.join(os.homedir(), 'src') // Common source folder
|
|
251
|
+
path.join(os.homedir(), 'Code')
|
|
300
252
|
],
|
|
301
|
-
maxDepth =
|
|
253
|
+
maxDepth = 3,
|
|
302
254
|
excludePatterns = ['node_modules', '.git', 'dist', 'build', '.next', 'coverage']
|
|
303
255
|
} = options;
|
|
304
256
|
|
|
@@ -23,92 +23,6 @@ function isProcessRunning(pid) {
|
|
|
23
23
|
}
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
/**
|
|
27
|
-
* Build for a non-AWS provider.
|
|
28
|
-
*
|
|
29
|
-
* When infrastructure.js is invoked (e.g. `node infrastructure.js package`),
|
|
30
|
-
* but the appDefinition specifies a non-AWS provider, we skip the entire
|
|
31
|
-
* serverless/CloudFormation pipeline and instead:
|
|
32
|
-
* 1. Validate the appDefinition against the provider
|
|
33
|
-
* 2. Generate the provider-specific config (e.g. netlify.toml)
|
|
34
|
-
* 3. Generate function entry points
|
|
35
|
-
*
|
|
36
|
-
* This mirrors what the CLI's buildCommand does for non-AWS providers,
|
|
37
|
-
* but works when invoked directly via `node infrastructure.js package`.
|
|
38
|
-
*/
|
|
39
|
-
async function buildWithProvider(appDefinition, providerName, backendDir) {
|
|
40
|
-
const { resolveProvider } = require('@friggframework/core/providers/resolve-provider');
|
|
41
|
-
const provider = resolveProvider(appDefinition);
|
|
42
|
-
|
|
43
|
-
console.log(`Building for ${providerName} provider (skipping AWS infrastructure)...`);
|
|
44
|
-
|
|
45
|
-
// 1. Validate
|
|
46
|
-
if (typeof provider.validate === 'function') {
|
|
47
|
-
const validation = provider.validate(appDefinition);
|
|
48
|
-
if (validation.errors?.length > 0) {
|
|
49
|
-
console.error(`\nValidation errors for ${providerName}:`);
|
|
50
|
-
for (const error of validation.errors) {
|
|
51
|
-
console.error(` - ${error}`);
|
|
52
|
-
}
|
|
53
|
-
process.exit(1);
|
|
54
|
-
}
|
|
55
|
-
if (validation.warnings?.length > 0) {
|
|
56
|
-
for (const warning of validation.warnings) {
|
|
57
|
-
console.warn(` Warning: ${warning}`);
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
// 2. Generate platform config (e.g. netlify.toml)
|
|
63
|
-
// Written to the project root (one level up from backend/)
|
|
64
|
-
const projectDir = path.dirname(backendDir);
|
|
65
|
-
if (typeof provider.generateConfig === 'function') {
|
|
66
|
-
const config = provider.generateConfig(appDefinition);
|
|
67
|
-
const configFileNames = { netlify: 'netlify.toml' };
|
|
68
|
-
const configFileName = configFileNames[providerName] || `${providerName}.config`;
|
|
69
|
-
const configPath = path.join(projectDir, configFileName);
|
|
70
|
-
|
|
71
|
-
fs.writeFileSync(configPath, config, 'utf-8');
|
|
72
|
-
console.log(` Written ${configFileName}`);
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
// 3. Generate function entry points
|
|
76
|
-
if (typeof provider.getFunctionEntryPoints === 'function') {
|
|
77
|
-
const entryPoints = provider.getFunctionEntryPoints(appDefinition);
|
|
78
|
-
const functionsDir = path.join(projectDir, 'netlify', 'functions');
|
|
79
|
-
|
|
80
|
-
fs.mkdirSync(functionsDir, { recursive: true });
|
|
81
|
-
|
|
82
|
-
for (const [filename, content] of Object.entries(entryPoints)) {
|
|
83
|
-
const filePath = path.join(functionsDir, filename);
|
|
84
|
-
fs.writeFileSync(filePath, content, 'utf-8');
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
console.log(` Generated ${Object.keys(entryPoints).length} function entry points`);
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
// 4. Generate lib entry points (re-export shims for runtime dependencies)
|
|
91
|
-
if (typeof provider.getLibEntryPoints === 'function') {
|
|
92
|
-
const libEntryPoints = provider.getLibEntryPoints(appDefinition);
|
|
93
|
-
const libDir = path.join(projectDir, 'netlify', 'lib');
|
|
94
|
-
|
|
95
|
-
fs.mkdirSync(libDir, { recursive: true });
|
|
96
|
-
|
|
97
|
-
for (const [filename, content] of Object.entries(libEntryPoints)) {
|
|
98
|
-
const filePath = path.join(libDir, filename);
|
|
99
|
-
fs.writeFileSync(filePath, content, 'utf-8');
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
console.log(` Generated ${Object.keys(libEntryPoints).length} lib entry points`);
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
console.log(`\nBuild complete for ${providerName}.`);
|
|
106
|
-
|
|
107
|
-
// Return an empty serverless definition — osls will see no functions
|
|
108
|
-
// and effectively no-op. The real deployment is handled by the provider.
|
|
109
|
-
return {};
|
|
110
|
-
}
|
|
111
|
-
|
|
112
26
|
async function createFriggInfrastructure() {
|
|
113
27
|
const backendPath = findNearestBackendPackageJson();
|
|
114
28
|
if (!backendPath) {
|
|
@@ -190,13 +104,6 @@ async function createFriggInfrastructure() {
|
|
|
190
104
|
const backend = require(backendFilePath);
|
|
191
105
|
const appDefinition = backend.Definition;
|
|
192
106
|
|
|
193
|
-
// Check if a non-AWS provider is configured.
|
|
194
|
-
// If so, run the provider's build pipeline instead of AWS CloudFormation.
|
|
195
|
-
const providerName = appDefinition.provider || 'aws';
|
|
196
|
-
if (providerName !== 'aws') {
|
|
197
|
-
return buildWithProvider(appDefinition, providerName, backendDir);
|
|
198
|
-
}
|
|
199
|
-
|
|
200
107
|
const definition = await composeServerlessDefinition(
|
|
201
108
|
appDefinition,
|
|
202
109
|
);
|
|
@@ -160,7 +160,7 @@ Consider separate policies for different environments:
|
|
|
160
160
|
### Validation
|
|
161
161
|
Test your policy by deploying a simple Frigg app:
|
|
162
162
|
```bash
|
|
163
|
-
frigg
|
|
163
|
+
npx create-frigg-app test-deployment
|
|
164
164
|
cd test-deployment
|
|
165
165
|
frigg deploy
|
|
166
166
|
```
|
|
@@ -316,6 +316,7 @@ class IntegrationBuilder extends InfrastructureBuilder {
|
|
|
316
316
|
sqs: {
|
|
317
317
|
arn: { 'Fn::GetAtt': [`${this.capitalizeFirst(integrationName)}Queue`, 'Arn'] },
|
|
318
318
|
batchSize: 1,
|
|
319
|
+
functionResponseType: 'ReportBatchItemFailures',
|
|
319
320
|
},
|
|
320
321
|
},
|
|
321
322
|
],
|
|
@@ -361,10 +362,10 @@ class IntegrationBuilder extends InfrastructureBuilder {
|
|
|
361
362
|
Type: 'AWS::SQS::Queue',
|
|
362
363
|
Properties: {
|
|
363
364
|
QueueName: `\${self:custom.${queueReference}}`,
|
|
364
|
-
MessageRetentionPeriod:
|
|
365
|
+
MessageRetentionPeriod: 345600, // 4 days (SQS default)
|
|
365
366
|
VisibilityTimeout: 1800,
|
|
366
367
|
RedrivePolicy: {
|
|
367
|
-
maxReceiveCount:
|
|
368
|
+
maxReceiveCount: 3,
|
|
368
369
|
deadLetterTargetArn: {
|
|
369
370
|
'Fn::GetAtt': ['InternalErrorQueue', 'Arn'],
|
|
370
371
|
},
|
|
@@ -186,7 +186,7 @@ describe('IntegrationBuilder', () => {
|
|
|
186
186
|
|
|
187
187
|
const result = await integrationBuilder.build(appDefinition, {});
|
|
188
188
|
|
|
189
|
-
expect(result.resources.TestQueue.Properties.MessageRetentionPeriod).toBe(
|
|
189
|
+
expect(result.resources.TestQueue.Properties.MessageRetentionPeriod).toBe(345600);
|
|
190
190
|
expect(result.resources.TestQueue.Properties.VisibilityTimeout).toBe(1800);
|
|
191
191
|
});
|
|
192
192
|
|
|
@@ -200,7 +200,7 @@ describe('IntegrationBuilder', () => {
|
|
|
200
200
|
const result = await integrationBuilder.build(appDefinition, {});
|
|
201
201
|
|
|
202
202
|
expect(result.resources.TestQueue.Properties.RedrivePolicy).toEqual({
|
|
203
|
-
maxReceiveCount:
|
|
203
|
+
maxReceiveCount: 3,
|
|
204
204
|
deadLetterTargetArn: {
|
|
205
205
|
'Fn::GetAtt': ['InternalErrorQueue', 'Arn'],
|
|
206
206
|
},
|
|
@@ -233,6 +233,7 @@ describe('IntegrationBuilder', () => {
|
|
|
233
233
|
sqs: {
|
|
234
234
|
arn: { 'Fn::GetAtt': ['TestQueue', 'Arn'] },
|
|
235
235
|
batchSize: 1,
|
|
236
|
+
functionResponseType: 'ReportBatchItemFailures',
|
|
236
237
|
},
|
|
237
238
|
},
|
|
238
239
|
]);
|
|
@@ -343,6 +344,57 @@ describe('IntegrationBuilder', () => {
|
|
|
343
344
|
expect(result.functions['my-integration']).toBeDefined();
|
|
344
345
|
expect(result.functions['my-integrationQueueWorker']).toBeDefined();
|
|
345
346
|
});
|
|
347
|
+
|
|
348
|
+
// ============================================================
|
|
349
|
+
// Theory-proving tests: demonstrate current dangerous config
|
|
350
|
+
// These tests document the root cause of the Modern Midstay bug
|
|
351
|
+
// where POST_CREATE_SETUP messages were silently lost.
|
|
352
|
+
// ============================================================
|
|
353
|
+
|
|
354
|
+
it('THEORY: MessageRetentionPeriod is too short for delayed messages', async () => {
|
|
355
|
+
// POST_CREATE_SETUP uses DelaySeconds=35.
|
|
356
|
+
// With MessageRetentionPeriod=60, the message is only visible
|
|
357
|
+
// for 25 seconds before SQS silently deletes it.
|
|
358
|
+
// Messages that expire are NOT sent to DLQ — they vanish.
|
|
359
|
+
const appDefinition = {
|
|
360
|
+
integrations: [{ Definition: { name: 'test' } }],
|
|
361
|
+
};
|
|
362
|
+
|
|
363
|
+
const result = await integrationBuilder.build(appDefinition, {});
|
|
364
|
+
const retention = result.resources.TestQueue.Properties.MessageRetentionPeriod;
|
|
365
|
+
|
|
366
|
+
// The max SQS DelaySeconds is 900. Retention must comfortably
|
|
367
|
+
// exceed this to ensure delayed messages are never silently lost.
|
|
368
|
+
// Current value (60) fails this check.
|
|
369
|
+
expect(retention).toBeGreaterThan(900);
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
it('THEORY: maxReceiveCount=1 means zero retries on transient failures', async () => {
|
|
373
|
+
// A single transient error (network blip, cold start timeout,
|
|
374
|
+
// rate limit) sends the message straight to DLQ with no retry.
|
|
375
|
+
const appDefinition = {
|
|
376
|
+
integrations: [{ Definition: { name: 'test' } }],
|
|
377
|
+
};
|
|
378
|
+
|
|
379
|
+
const result = await integrationBuilder.build(appDefinition, {});
|
|
380
|
+
const maxReceiveCount = result.resources.TestQueue.Properties.RedrivePolicy.maxReceiveCount;
|
|
381
|
+
|
|
382
|
+
// Should allow at least 2 retries (maxReceiveCount >= 3)
|
|
383
|
+
expect(maxReceiveCount).toBeGreaterThanOrEqual(3);
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
it('THEORY: SQS event source should enable ReportBatchItemFailures', async () => {
|
|
387
|
+
// Without this, Lambda can't tell SQS which specific messages
|
|
388
|
+
// failed — it's all-or-nothing for the entire invocation.
|
|
389
|
+
const appDefinition = {
|
|
390
|
+
integrations: [{ Definition: { name: 'test' } }],
|
|
391
|
+
};
|
|
392
|
+
|
|
393
|
+
const result = await integrationBuilder.build(appDefinition, {});
|
|
394
|
+
const sqsEvent = result.functions.testQueueWorker.events[0].sqs;
|
|
395
|
+
|
|
396
|
+
expect(sqsEvent.functionResponseType).toBe('ReportBatchItemFailures');
|
|
397
|
+
});
|
|
346
398
|
});
|
|
347
399
|
|
|
348
400
|
describe('getDependencies()', () => {
|
|
@@ -1500,9 +1500,10 @@ describe('VpcBuilder', () => {
|
|
|
1500
1500
|
}
|
|
1501
1501
|
};
|
|
1502
1502
|
|
|
1503
|
+
// Discovery results matching ACTUAL Frontify production stack
|
|
1503
1504
|
const discoveredResources = {
|
|
1504
1505
|
fromCloudFormationStack: true,
|
|
1505
|
-
stackName: 'frigg-app-production',
|
|
1506
|
+
stackName: 'create-frigg-app-production',
|
|
1506
1507
|
existingLogicalIds: [
|
|
1507
1508
|
'FriggLambdaRouteTable',
|
|
1508
1509
|
'FriggNATRoute', // OLD naming
|
|
@@ -1565,9 +1566,10 @@ describe('VpcBuilder', () => {
|
|
|
1565
1566
|
});
|
|
1566
1567
|
|
|
1567
1568
|
it('should convert OLD logical IDs to structured discovery stackManaged array', () => {
|
|
1569
|
+
// TDD test: Verify that VPCEndpointS3 in existingLogicalIds gets added to stackManaged
|
|
1568
1570
|
const flatDiscovery = {
|
|
1569
1571
|
fromCloudFormationStack: true,
|
|
1570
|
-
stackName: 'frigg-app-production',
|
|
1572
|
+
stackName: 'create-frigg-app-production',
|
|
1571
1573
|
existingLogicalIds: [
|
|
1572
1574
|
'VPCEndpointS3', // OLD naming
|
|
1573
1575
|
'VPCEndpointDynamoDB', // OLD naming
|
|
@@ -746,7 +746,7 @@ describe('VpcResourceResolver', () => {
|
|
|
746
746
|
],
|
|
747
747
|
external: [],
|
|
748
748
|
fromCloudFormation: true,
|
|
749
|
-
stackName: 'frigg-app-production'
|
|
749
|
+
stackName: 'create-frigg-app-production'
|
|
750
750
|
};
|
|
751
751
|
|
|
752
752
|
const decisions = resolver.resolveAll(appDefinition, discovery);
|
|
@@ -88,8 +88,8 @@ async function gatherDiscoveredResources(appDefinition) {
|
|
|
88
88
|
|
|
89
89
|
// Build discovery configuration
|
|
90
90
|
const stage = process.env.SLS_STAGE || 'dev';
|
|
91
|
-
const stackName = `${appDefinition.name || 'frigg-app'}-${stage}`;
|
|
92
|
-
const serviceName = appDefinition.name || 'frigg-app';
|
|
91
|
+
const stackName = `${appDefinition.name || 'create-frigg-app'}-${stage}`;
|
|
92
|
+
const serviceName = appDefinition.name || 'create-frigg-app';
|
|
93
93
|
|
|
94
94
|
// Try CloudFormation-first discovery
|
|
95
95
|
const cfDiscovery = new CloudFormationDiscovery(provider, { serviceName, stage });
|
|
@@ -158,9 +158,9 @@ async function gatherDiscoveredResources(appDefinition) {
|
|
|
158
158
|
// KMS keys CAN be shared across stages (encryption keys are safe to reuse)
|
|
159
159
|
const kmsDiscovery = new KmsDiscovery(provider);
|
|
160
160
|
const kmsConfig = {
|
|
161
|
-
serviceName: appDefinition.name || 'frigg-app',
|
|
161
|
+
serviceName: appDefinition.name || 'create-frigg-app',
|
|
162
162
|
stage,
|
|
163
|
-
keyAlias: `alias/${appDefinition.name || 'frigg-app'}-${stage}-frigg-kms`,
|
|
163
|
+
keyAlias: `alias/${appDefinition.name || 'create-frigg-app'}-${stage}-frigg-kms`,
|
|
164
164
|
};
|
|
165
165
|
const kmsResult = await kmsDiscovery.discover(kmsConfig);
|
|
166
166
|
|
|
@@ -189,7 +189,7 @@ async function gatherDiscoveredResources(appDefinition) {
|
|
|
189
189
|
const ssmDiscovery = new SsmDiscovery(provider);
|
|
190
190
|
|
|
191
191
|
const config = {
|
|
192
|
-
serviceName: appDefinition.name || 'frigg-app',
|
|
192
|
+
serviceName: appDefinition.name || 'create-frigg-app',
|
|
193
193
|
stage,
|
|
194
194
|
vpcId: appDefinition.vpc?.vpcId,
|
|
195
195
|
databaseId: appDefinition.database?.postgres?.clusterId ||
|
|
@@ -106,25 +106,6 @@
|
|
|
106
106
|
* @property {string} Definition.name - Integration name
|
|
107
107
|
*/
|
|
108
108
|
|
|
109
|
-
/**
|
|
110
|
-
* Admin script definition
|
|
111
|
-
* @typedef {Object} AdminScriptDefinition
|
|
112
|
-
* @property {Object} Definition - Static definition from script class
|
|
113
|
-
* @property {string} Definition.name - Script name identifier
|
|
114
|
-
* @property {string} Definition.version - Script version (semver)
|
|
115
|
-
* @property {string} [Definition.description] - Human-readable description
|
|
116
|
-
* @property {Object} [Definition.schedule] - Schedule configuration
|
|
117
|
-
* @property {boolean} [Definition.schedule.enabled] - Whether scheduling is enabled
|
|
118
|
-
* @property {string} [Definition.schedule.cronExpression] - Cron expression
|
|
119
|
-
*/
|
|
120
|
-
|
|
121
|
-
/**
|
|
122
|
-
* Admin configuration
|
|
123
|
-
* @typedef {Object} AdminConfig
|
|
124
|
-
* @property {boolean} [includeBuiltinScripts] - Whether to include built-in scripts
|
|
125
|
-
* @property {boolean} [enableScheduling] - Whether to enable EventBridge scheduling
|
|
126
|
-
*/
|
|
127
|
-
|
|
128
109
|
/**
|
|
129
110
|
* Complete application definition
|
|
130
111
|
* @typedef {Object} AppDefinition
|
|
@@ -141,24 +122,8 @@
|
|
|
141
122
|
* @property {MigrationDefinition} [migrations] - Database migration configuration
|
|
142
123
|
* @property {WebsocketDefinition} [websockets] - WebSocket API configuration
|
|
143
124
|
* @property {IntegrationDefinition[]} [integrations] - Integration definitions
|
|
144
|
-
* @property {AdminScriptDefinition[]} [adminScripts] - Admin script definitions
|
|
145
|
-
* @property {AdminConfig} [admin] - Admin configuration
|
|
146
125
|
*
|
|
147
126
|
* @property {Object} [environment] - Environment variables
|
|
148
|
-
*
|
|
149
|
-
* @property {ExtensionDefinition[]} [extensions] - Extensions that add custom Prisma models, encrypted fields, admin routes, and bootstrap lifecycle hooks
|
|
150
|
-
*/
|
|
151
|
-
|
|
152
|
-
/**
|
|
153
|
-
* Extension definition for extending Frigg Core with custom models, encryption, routes, and bootstrap
|
|
154
|
-
* @typedef {Object} ExtensionDefinition
|
|
155
|
-
* @property {string} name - Unique identifier for this extension
|
|
156
|
-
* @property {string} [schema] - Absolute path to a .prisma schema fragment
|
|
157
|
-
* @property {Object<string, {fields: string[]}>} [encryption] - Model name → encrypted fields mapping
|
|
158
|
-
* @property {Object} [routes] - Express router configuration
|
|
159
|
-
* @property {string} routes.path - Base URL path for the extension routes
|
|
160
|
-
* @property {Function|Object} routes.handler - Router factory (prisma, appDefinition) → Router, or { router } object
|
|
161
|
-
* @property {Function} [bootstrap] - Async function (prisma, appDefinition) → void, runs post-DB pre-router
|
|
162
127
|
*/
|
|
163
128
|
|
|
164
129
|
/**
|
|
@@ -165,7 +165,7 @@ function createBaseDefinition(
|
|
|
165
165
|
|
|
166
166
|
return {
|
|
167
167
|
frameworkVersion: '>=3.17.0',
|
|
168
|
-
service: AppDefinition.name || 'frigg-app',
|
|
168
|
+
service: AppDefinition.name || 'create-frigg-app',
|
|
169
169
|
package: {
|
|
170
170
|
individually: true,
|
|
171
171
|
},
|
|
@@ -311,15 +311,6 @@ function createBaseDefinition(
|
|
|
311
311
|
{ httpApi: { path: '/health/{proxy+}', method: 'GET' } },
|
|
312
312
|
],
|
|
313
313
|
},
|
|
314
|
-
docs: {
|
|
315
|
-
handler: 'node_modules/@friggframework/core/handlers/routers/docs.handler',
|
|
316
|
-
skipEsbuild: true,
|
|
317
|
-
package: skipEsbuildPackageConfig,
|
|
318
|
-
events: [
|
|
319
|
-
{ httpApi: { path: '/api/docs', method: 'GET' } },
|
|
320
|
-
{ httpApi: { path: '/api/openapi.json', method: 'GET' } },
|
|
321
|
-
],
|
|
322
|
-
},
|
|
323
314
|
// Note: dbMigrate removed - MigrationBuilder now handles migration infrastructure
|
|
324
315
|
// See: packages/devtools/infrastructure/domains/database/migration-builder.js
|
|
325
316
|
},
|
|
@@ -30,10 +30,10 @@ describe('Base Definition Factory', () => {
|
|
|
30
30
|
expect(result.provider.stage).toBe('${opt:stage}');
|
|
31
31
|
});
|
|
32
32
|
|
|
33
|
-
it('should default service name to frigg-app', () => {
|
|
33
|
+
it('should default service name to create-frigg-app', () => {
|
|
34
34
|
const result = createBaseDefinition({}, {}, {});
|
|
35
35
|
|
|
36
|
-
expect(result.service).toBe('frigg-app');
|
|
36
|
+
expect(result.service).toBe('create-frigg-app');
|
|
37
37
|
});
|
|
38
38
|
|
|
39
39
|
it('should use custom provider if specified', () => {
|
|
@@ -17,7 +17,6 @@ const { SsmBuilder } = require('./domains/parameters/ssm-builder');
|
|
|
17
17
|
const { WebsocketBuilder } = require('./domains/integration/websocket-builder');
|
|
18
18
|
const { IntegrationBuilder } = require('./domains/integration/integration-builder');
|
|
19
19
|
const { SchedulerBuilder } = require('./domains/scheduler/scheduler-builder');
|
|
20
|
-
const { AdminScriptBuilder } = require('./domains/admin-scripts/admin-script-builder');
|
|
21
20
|
|
|
22
21
|
// Utilities
|
|
23
22
|
const { modifyHandlerPaths } = require('./domains/shared/utilities/handler-path-resolver');
|
|
@@ -54,7 +53,6 @@ const composeServerlessDefinition = async (AppDefinition) => {
|
|
|
54
53
|
new WebsocketBuilder(),
|
|
55
54
|
new IntegrationBuilder(),
|
|
56
55
|
new SchedulerBuilder(), // Add scheduler after IntegrationBuilder (depends on it)
|
|
57
|
-
new AdminScriptBuilder(),
|
|
58
56
|
]);
|
|
59
57
|
|
|
60
58
|
// Build all infrastructure (orchestrator handles validation, dependencies, parallel execution)
|
|
@@ -157,7 +157,7 @@ describe('composeServerlessDefinition', () => {
|
|
|
157
157
|
|
|
158
158
|
const result = await composeServerlessDefinition(appDefinition);
|
|
159
159
|
|
|
160
|
-
expect(result.service).toBe('frigg-app');
|
|
160
|
+
expect(result.service).toBe('create-frigg-app');
|
|
161
161
|
});
|
|
162
162
|
|
|
163
163
|
it('should use custom provider when specified', async () => {
|
|
@@ -1148,10 +1148,10 @@ describe('composeServerlessDefinition', () => {
|
|
|
1148
1148
|
Type: 'AWS::SQS::Queue',
|
|
1149
1149
|
Properties: {
|
|
1150
1150
|
QueueName: '${self:custom.TestIntegrationQueue}',
|
|
1151
|
-
MessageRetentionPeriod:
|
|
1151
|
+
MessageRetentionPeriod: 345600,
|
|
1152
1152
|
VisibilityTimeout: 1800,
|
|
1153
1153
|
RedrivePolicy: {
|
|
1154
|
-
maxReceiveCount:
|
|
1154
|
+
maxReceiveCount: 3,
|
|
1155
1155
|
deadLetterTargetArn: {
|
|
1156
1156
|
'Fn::GetAtt': ['InternalErrorQueue', 'Arn']
|
|
1157
1157
|
}
|
|
@@ -1168,7 +1168,8 @@ describe('composeServerlessDefinition', () => {
|
|
|
1168
1168
|
arn: {
|
|
1169
1169
|
'Fn::GetAtt': ['TestIntegrationQueue', 'Arn']
|
|
1170
1170
|
},
|
|
1171
|
-
batchSize: 1
|
|
1171
|
+
batchSize: 1,
|
|
1172
|
+
functionResponseType: 'ReportBatchItemFailures'
|
|
1172
1173
|
}
|
|
1173
1174
|
}],
|
|
1174
1175
|
timeout: 600
|
|
@@ -1859,7 +1860,7 @@ describe('composeServerlessDefinition', () => {
|
|
|
1859
1860
|
|
|
1860
1861
|
await expect(composeServerlessDefinition(appDefinition)).resolves.not.toThrow();
|
|
1861
1862
|
const result = await composeServerlessDefinition(appDefinition);
|
|
1862
|
-
expect(result.service).toBe('frigg-app');
|
|
1863
|
+
expect(result.service).toBe('create-frigg-app');
|
|
1863
1864
|
});
|
|
1864
1865
|
|
|
1865
1866
|
it('should handle null/undefined integrations', async () => {
|