@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.
Files changed (129) hide show
  1. package/frigg-cli/README.md +1 -1
  2. package/frigg-cli/__tests__/unit/commands/build.test.js +1 -1
  3. package/frigg-cli/__tests__/unit/commands/doctor.test.js +2 -0
  4. package/frigg-cli/__tests__/unit/commands/install.test.js +19 -23
  5. package/frigg-cli/__tests__/unit/dependencies.test.js +2 -2
  6. package/frigg-cli/build-command/index.js +11 -123
  7. package/frigg-cli/deploy-command/index.js +1 -83
  8. package/frigg-cli/doctor-command/index.js +16 -37
  9. package/frigg-cli/generate-iam-command.js +1 -21
  10. package/frigg-cli/index.js +6 -21
  11. package/frigg-cli/index.test.js +2 -7
  12. package/frigg-cli/init-command/backend-first-handler.js +42 -124
  13. package/frigg-cli/init-command/index.js +1 -2
  14. package/frigg-cli/init-command/template-handler.js +3 -13
  15. package/frigg-cli/install-command/backend-js.js +3 -3
  16. package/frigg-cli/install-command/environment-variables.js +19 -16
  17. package/frigg-cli/install-command/environment-variables.test.js +13 -12
  18. package/frigg-cli/install-command/index.js +9 -14
  19. package/frigg-cli/install-command/integration-file.js +3 -3
  20. package/frigg-cli/install-command/logger.js +12 -0
  21. package/frigg-cli/install-command/validate-package.js +9 -5
  22. package/frigg-cli/jest.config.js +1 -4
  23. package/frigg-cli/repair-command/index.js +128 -121
  24. package/frigg-cli/start-command/index.js +2 -324
  25. package/frigg-cli/ui-command/index.js +36 -58
  26. package/frigg-cli/utils/repo-detection.js +37 -85
  27. package/infrastructure/create-frigg-infrastructure.js +0 -93
  28. package/infrastructure/docs/iam-policy-templates.md +1 -1
  29. package/infrastructure/domains/integration/integration-builder.js +3 -2
  30. package/infrastructure/domains/integration/integration-builder.test.js +54 -2
  31. package/infrastructure/domains/networking/vpc-builder.test.js +4 -2
  32. package/infrastructure/domains/networking/vpc-resolver.test.js +1 -1
  33. package/infrastructure/domains/shared/resource-discovery.js +5 -5
  34. package/infrastructure/domains/shared/types/app-definition.js +0 -35
  35. package/infrastructure/domains/shared/types/discovery-result.test.js +1 -1
  36. package/infrastructure/domains/shared/utilities/base-definition-factory.js +1 -10
  37. package/infrastructure/domains/shared/utilities/base-definition-factory.test.js +2 -2
  38. package/infrastructure/infrastructure-composer.js +0 -2
  39. package/infrastructure/infrastructure-composer.test.js +6 -5
  40. package/management-ui/README.md +109 -245
  41. package/package.json +7 -8
  42. package/frigg-cli/__tests__/application/use-cases/AddApiModuleToIntegrationUseCase.test.js +0 -326
  43. package/frigg-cli/__tests__/application/use-cases/CreateApiModuleUseCase.test.js +0 -337
  44. package/frigg-cli/__tests__/domain/entities/ApiModule.test.js +0 -373
  45. package/frigg-cli/__tests__/domain/entities/AppDefinition.test.js +0 -313
  46. package/frigg-cli/__tests__/domain/services/IntegrationValidator.test.js +0 -269
  47. package/frigg-cli/__tests__/domain/value-objects/IntegrationName.test.js +0 -82
  48. package/frigg-cli/__tests__/infrastructure/adapters/IntegrationJsUpdater.test.js +0 -408
  49. package/frigg-cli/__tests__/infrastructure/repositories/FileSystemApiModuleRepository.test.js +0 -583
  50. package/frigg-cli/__tests__/infrastructure/repositories/FileSystemAppDefinitionRepository.test.js +0 -314
  51. package/frigg-cli/__tests__/infrastructure/repositories/FileSystemIntegrationRepository.test.js +0 -383
  52. package/frigg-cli/__tests__/unit/commands/init.test.js +0 -406
  53. package/frigg-cli/__tests__/unit/commands/provider-dispatch.test.js +0 -383
  54. package/frigg-cli/__tests__/unit/commands/repair.test.js +0 -275
  55. package/frigg-cli/__tests__/unit/start-command/application/RunPreflightChecksUseCase.test.js +0 -411
  56. package/frigg-cli/__tests__/unit/start-command/infrastructure/DatabaseAdapter.test.js +0 -405
  57. package/frigg-cli/__tests__/unit/start-command/infrastructure/DockerAdapter.test.js +0 -496
  58. package/frigg-cli/__tests__/unit/start-command/presentation/InteractivePromptAdapter.test.js +0 -474
  59. package/frigg-cli/__tests__/unit/utils/output.test.js +0 -196
  60. package/frigg-cli/application/use-cases/AddApiModuleToIntegrationUseCase.js +0 -93
  61. package/frigg-cli/application/use-cases/CreateApiModuleUseCase.js +0 -93
  62. package/frigg-cli/application/use-cases/CreateIntegrationUseCase.js +0 -103
  63. package/frigg-cli/container.js +0 -172
  64. package/frigg-cli/docs/OUTPUT_MIGRATION_GUIDE.md +0 -286
  65. package/frigg-cli/domain/entities/ApiModule.js +0 -272
  66. package/frigg-cli/domain/entities/AppDefinition.js +0 -227
  67. package/frigg-cli/domain/entities/Integration.js +0 -198
  68. package/frigg-cli/domain/exceptions/DomainException.js +0 -24
  69. package/frigg-cli/domain/ports/IApiModuleRepository.js +0 -53
  70. package/frigg-cli/domain/ports/IAppDefinitionRepository.js +0 -43
  71. package/frigg-cli/domain/ports/IIntegrationRepository.js +0 -61
  72. package/frigg-cli/domain/services/IntegrationValidator.js +0 -185
  73. package/frigg-cli/domain/value-objects/IntegrationId.js +0 -42
  74. package/frigg-cli/domain/value-objects/IntegrationName.js +0 -60
  75. package/frigg-cli/domain/value-objects/SemanticVersion.js +0 -70
  76. package/frigg-cli/infrastructure/UnitOfWork.js +0 -46
  77. package/frigg-cli/infrastructure/adapters/BackendJsUpdater.js +0 -197
  78. package/frigg-cli/infrastructure/adapters/FileSystemAdapter.js +0 -224
  79. package/frigg-cli/infrastructure/adapters/IntegrationJsUpdater.js +0 -249
  80. package/frigg-cli/infrastructure/adapters/SchemaValidator.js +0 -92
  81. package/frigg-cli/infrastructure/repositories/FileSystemApiModuleRepository.js +0 -373
  82. package/frigg-cli/infrastructure/repositories/FileSystemAppDefinitionRepository.js +0 -116
  83. package/frigg-cli/infrastructure/repositories/FileSystemIntegrationRepository.js +0 -277
  84. package/frigg-cli/package-lock.json +0 -16226
  85. package/frigg-cli/start-command/application/RunPreflightChecksUseCase.js +0 -376
  86. package/frigg-cli/start-command/infrastructure/DatabaseAdapter.js +0 -591
  87. package/frigg-cli/start-command/infrastructure/DockerAdapter.js +0 -306
  88. package/frigg-cli/start-command/presentation/InteractivePromptAdapter.js +0 -329
  89. package/frigg-cli/templates/backend/.env.example +0 -62
  90. package/frigg-cli/templates/backend/.eslintrc.json +0 -12
  91. package/frigg-cli/templates/backend/.prettierrc +0 -6
  92. package/frigg-cli/templates/backend/docker-compose.yml +0 -22
  93. package/frigg-cli/templates/backend/index.js +0 -96
  94. package/frigg-cli/templates/backend/infrastructure.js +0 -12
  95. package/frigg-cli/templates/backend/jest.config.js +0 -17
  96. package/frigg-cli/templates/backend/package.json +0 -50
  97. package/frigg-cli/templates/backend/src/api-modules/.gitkeep +0 -10
  98. package/frigg-cli/templates/backend/src/base/.gitkeep +0 -7
  99. package/frigg-cli/templates/backend/src/integrations/.gitkeep +0 -10
  100. package/frigg-cli/templates/backend/src/integrations/ExampleIntegration.js +0 -65
  101. package/frigg-cli/templates/backend/src/utils/.gitkeep +0 -7
  102. package/frigg-cli/templates/backend/test/setup.js +0 -30
  103. package/frigg-cli/templates/backend/ui-extensions/.gitkeep +0 -0
  104. package/frigg-cli/templates/backend/ui-extensions/README.md +0 -77
  105. package/frigg-cli/utils/__tests__/provider-helper.test.js +0 -55
  106. package/frigg-cli/utils/__tests__/repo-detection.test.js +0 -436
  107. package/frigg-cli/utils/output.js +0 -382
  108. package/frigg-cli/utils/provider-helper.js +0 -75
  109. package/frigg-cli/validate-command/__tests__/adapters/validate-command.test.js +0 -205
  110. package/frigg-cli/validate-command/__tests__/application/validate-app-use-case.test.js +0 -104
  111. package/frigg-cli/validate-command/__tests__/domain/fix-suggestion.test.js +0 -153
  112. package/frigg-cli/validate-command/__tests__/domain/validation-error.test.js +0 -162
  113. package/frigg-cli/validate-command/__tests__/domain/validation-result.test.js +0 -152
  114. package/frigg-cli/validate-command/__tests__/infrastructure/api-module-validator.test.js +0 -332
  115. package/frigg-cli/validate-command/__tests__/infrastructure/app-definition-validator.test.js +0 -191
  116. package/frigg-cli/validate-command/__tests__/infrastructure/integration-class-validator.test.js +0 -146
  117. package/frigg-cli/validate-command/__tests__/infrastructure/template-validation.test.js +0 -155
  118. package/frigg-cli/validate-command/adapters/cli/validate-command.js +0 -199
  119. package/frigg-cli/validate-command/application/use-cases/validate-app-use-case.js +0 -35
  120. package/frigg-cli/validate-command/domain/entities/validation-result.js +0 -74
  121. package/frigg-cli/validate-command/domain/value-objects/fix-suggestion.js +0 -74
  122. package/frigg-cli/validate-command/domain/value-objects/validation-error.js +0 -68
  123. package/frigg-cli/validate-command/infrastructure/validators/api-module-validator.js +0 -181
  124. package/frigg-cli/validate-command/infrastructure/validators/app-definition-validator.js +0 -145
  125. package/frigg-cli/validate-command/infrastructure/validators/integration-class-validator.js +0 -113
  126. package/infrastructure/domains/admin-scripts/admin-script-builder.js +0 -200
  127. package/infrastructure/domains/admin-scripts/admin-script-builder.test.js +0 -499
  128. package/infrastructure/domains/admin-scripts/index.js +0 -5
  129. 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/core v2.0+ specifically (REQUIRED for Frigg apps)
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/') && dep !== '@friggframework/core') {
91
- const version = allDeps[dep];
92
- const isV2Plus = version && (
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
- // STRICT VALIDATION: A directory is considered a Frigg app repository ONLY if:
193
- // 1. Has @friggframework/core v2.0+ as a direct dependency (MANDATORY)
194
- // 2. Has actual Frigg app structure (index.js with Definition OR backend/serverless.yml)
195
- // 3. Is NOT a framework package itself (no @friggframework/* package name)
196
-
197
- // Check for Frigg app structure indicators
198
- const hasRootIndexJs = fs.existsSync(path.join(directory, 'index.js'));
199
- const hasBackendIndexJs = fs.existsSync(path.join(directory, 'backend', 'index.js'));
200
- const hasBackendServerless = fs.existsSync(path.join(directory, 'backend', 'serverless.yml'));
201
- const hasAppStructure = hasRootIndexJs || hasBackendIndexJs || hasBackendServerless;
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
- // Determine if it's a Frigg repository with STRICT criteria
204
- const isFriggRepo = indicators.hasFriggDependencies && hasAppStructure;
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: actualPath,
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 = 4, // Increased to handle deeper nested projects (e.g., org/apps/project)
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 init test-deployment
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: 60,
365
+ MessageRetentionPeriod: 345600, // 4 days (SQS default)
365
366
  VisibilityTimeout: 1800,
366
367
  RedrivePolicy: {
367
- maxReceiveCount: 1,
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(60);
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: 1,
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
  /**
@@ -219,7 +219,7 @@ describe('Discovery Result Utilities', () => {
219
219
  ],
220
220
  external: [],
221
221
  fromCloudFormation: true,
222
- stackName: 'frigg-app-production'
222
+ stackName: 'create-frigg-app-production'
223
223
  };
224
224
 
225
225
  expect(discovery.fromCloudFormation).toBe(true);
@@ -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: 60,
1151
+ MessageRetentionPeriod: 345600,
1152
1152
  VisibilityTimeout: 1800,
1153
1153
  RedrivePolicy: {
1154
- maxReceiveCount: 1,
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 () => {