@friggframework/devtools 2.0.0--canary.395.04851d8.0 → 2.0.0--canary.395.8f4c094.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.
@@ -83,12 +83,16 @@ describe('DB Setup Command', () => {
83
83
  describe('Success Cases', () => {
84
84
  it('should complete setup successfully for MongoDB', async () => {
85
85
  mockValidator.getDatabaseType.mockReturnValue({ dbType: 'mongodb' });
86
+ mockValidator.checkPrismaClientGenerated.mockReturnValue({
87
+ generated: false // Client doesn't exist, will generate
88
+ });
86
89
  mockRunner.checkDatabaseState.mockResolvedValue({ upToDate: false });
87
90
 
88
91
  await dbSetupCommand({ verbose: false, stage: 'development' });
89
92
 
90
93
  expect(mockValidator.validateDatabaseUrl).toHaveBeenCalled();
91
94
  expect(mockValidator.getDatabaseType).toHaveBeenCalled();
95
+ expect(mockValidator.checkPrismaClientGenerated).toHaveBeenCalledWith('mongodb');
92
96
  expect(mockValidator.testDatabaseConnection).toHaveBeenCalled();
93
97
  expect(mockRunner.runPrismaGenerate).toHaveBeenCalledWith('mongodb', false);
94
98
  expect(mockRunner.runPrismaDbPush).toHaveBeenCalled();
@@ -97,10 +101,14 @@ describe('DB Setup Command', () => {
97
101
 
98
102
  it('should complete setup successfully for PostgreSQL', async () => {
99
103
  mockValidator.getDatabaseType.mockReturnValue({ dbType: 'postgresql' });
104
+ mockValidator.checkPrismaClientGenerated.mockReturnValue({
105
+ generated: false // Client doesn't exist, will generate
106
+ });
100
107
  mockRunner.checkDatabaseState.mockResolvedValue({ upToDate: false });
101
108
 
102
109
  await dbSetupCommand({ verbose: false, stage: 'development' });
103
110
 
111
+ expect(mockValidator.checkPrismaClientGenerated).toHaveBeenCalledWith('postgresql');
104
112
  expect(mockRunner.runPrismaGenerate).toHaveBeenCalledWith('postgresql', false);
105
113
  expect(mockRunner.runPrismaMigrate).toHaveBeenCalled();
106
114
  expect(mockProcessExit).not.toHaveBeenCalled();
@@ -140,6 +148,10 @@ describe('DB Setup Command', () => {
140
148
  });
141
149
 
142
150
  it('should respect --verbose flag', async () => {
151
+ mockValidator.checkPrismaClientGenerated.mockReturnValue({
152
+ generated: false // Client doesn't exist, will generate
153
+ });
154
+
143
155
  await dbSetupCommand({ verbose: true, stage: 'development' });
144
156
 
145
157
  expect(mockRunner.runPrismaGenerate).toHaveBeenCalledWith('mongodb', true);
@@ -154,6 +166,89 @@ describe('DB Setup Command', () => {
154
166
  });
155
167
  });
156
168
 
169
+ describe('Conditional Client Generation', () => {
170
+ it('should skip generation when client already exists', async () => {
171
+ // Client exists
172
+ mockValidator.checkPrismaClientGenerated.mockReturnValue({
173
+ generated: true,
174
+ path: '/path/to/client'
175
+ });
176
+
177
+ await dbSetupCommand({ verbose: false, stage: 'development' });
178
+
179
+ // Should check if client exists
180
+ expect(mockValidator.checkPrismaClientGenerated).toHaveBeenCalledWith('mongodb');
181
+ // Should NOT call generate
182
+ expect(mockRunner.runPrismaGenerate).not.toHaveBeenCalled();
183
+ // Should still test connection and complete setup
184
+ expect(mockValidator.testDatabaseConnection).toHaveBeenCalled();
185
+ expect(mockProcessExit).not.toHaveBeenCalled();
186
+ });
187
+
188
+ it('should generate client when it does not exist', async () => {
189
+ // Client does NOT exist
190
+ mockValidator.checkPrismaClientGenerated.mockReturnValue({
191
+ generated: false,
192
+ error: 'Client not found'
193
+ });
194
+
195
+ await dbSetupCommand({ verbose: false, stage: 'development' });
196
+
197
+ // Should check if client exists
198
+ expect(mockValidator.checkPrismaClientGenerated).toHaveBeenCalledWith('mongodb');
199
+ // Should call generate
200
+ expect(mockRunner.runPrismaGenerate).toHaveBeenCalledWith('mongodb', false);
201
+ // Should complete setup
202
+ expect(mockProcessExit).not.toHaveBeenCalled();
203
+ });
204
+
205
+ it('should regenerate client when --force flag is provided', async () => {
206
+ // Client exists
207
+ mockValidator.checkPrismaClientGenerated.mockReturnValue({
208
+ generated: true,
209
+ path: '/path/to/client'
210
+ });
211
+
212
+ await dbSetupCommand({ verbose: false, stage: 'development', force: true });
213
+
214
+ // Should check if client exists
215
+ expect(mockValidator.checkPrismaClientGenerated).toHaveBeenCalledWith('mongodb');
216
+ // Should STILL call generate because of --force
217
+ expect(mockRunner.runPrismaGenerate).toHaveBeenCalledWith('mongodb', false);
218
+ // Should complete setup
219
+ expect(mockProcessExit).not.toHaveBeenCalled();
220
+ });
221
+
222
+ it('should show client location in verbose mode when skipping generation', async () => {
223
+ mockValidator.checkPrismaClientGenerated.mockReturnValue({
224
+ generated: true,
225
+ path: '/path/to/prisma/client'
226
+ });
227
+
228
+ await dbSetupCommand({ verbose: true, stage: 'development' });
229
+
230
+ // Should log the client path
231
+ expect(mockConsoleLog).toHaveBeenCalledWith(
232
+ expect.stringContaining('/path/to/prisma/client')
233
+ );
234
+ expect(mockRunner.runPrismaGenerate).not.toHaveBeenCalled();
235
+ });
236
+
237
+ it('should generate for different database types', async () => {
238
+ // Test PostgreSQL
239
+ mockValidator.getDatabaseType.mockReturnValue({ dbType: 'postgresql' });
240
+ mockValidator.checkPrismaClientGenerated.mockReturnValue({
241
+ generated: false
242
+ });
243
+ mockRunner.checkDatabaseState.mockResolvedValue({ upToDate: false });
244
+
245
+ await dbSetupCommand({ verbose: false, stage: 'development' });
246
+
247
+ expect(mockValidator.checkPrismaClientGenerated).toHaveBeenCalledWith('postgresql');
248
+ expect(mockRunner.runPrismaGenerate).toHaveBeenCalledWith('postgresql', false);
249
+ });
250
+ });
251
+
157
252
  describe('Failure Cases - Validation', () => {
158
253
  it('should fail when DATABASE_URL missing', async () => {
159
254
  mockValidator.validateDatabaseUrl.mockReturnValue({
@@ -242,6 +337,9 @@ describe('DB Setup Command', () => {
242
337
 
243
338
  describe('Failure Cases - Prisma Operations', () => {
244
339
  it('should fail when prisma generate fails', async () => {
340
+ mockValidator.checkPrismaClientGenerated.mockReturnValue({
341
+ generated: false // Client doesn't exist, will attempt generation
342
+ });
245
343
  mockRunner.runPrismaGenerate.mockResolvedValue({
246
344
  success: false,
247
345
  error: 'Generation failed'
@@ -304,6 +402,9 @@ describe('DB Setup Command', () => {
304
402
  });
305
403
 
306
404
  it('should fail when schema file missing', async () => {
405
+ mockValidator.checkPrismaClientGenerated.mockReturnValue({
406
+ generated: false // Client doesn't exist, will attempt generation
407
+ });
307
408
  mockRunner.runPrismaGenerate.mockResolvedValue({
308
409
  success: false,
309
410
  error: 'Schema not found'
@@ -342,6 +443,9 @@ describe('DB Setup Command', () => {
342
443
  });
343
444
 
344
445
  it('should display helpful error for Prisma failures', async () => {
446
+ mockValidator.checkPrismaClientGenerated.mockReturnValue({
447
+ generated: false // Client doesn't exist, will attempt generation
448
+ });
345
449
  mockRunner.runPrismaGenerate.mockResolvedValue({
346
450
  success: false,
347
451
  error: 'Some error',
@@ -392,6 +496,10 @@ describe('DB Setup Command', () => {
392
496
  });
393
497
 
394
498
  it('should pass verbose flag to Prisma commands', async () => {
499
+ mockValidator.checkPrismaClientGenerated.mockReturnValue({
500
+ generated: false // Client doesn't exist, will generate
501
+ });
502
+
395
503
  await dbSetupCommand({ verbose: true });
396
504
 
397
505
  expect(mockRunner.runPrismaGenerate).toHaveBeenCalledWith('mongodb', true);
@@ -399,6 +507,10 @@ describe('DB Setup Command', () => {
399
507
  });
400
508
 
401
509
  it('should not show verbose output when flag disabled', async () => {
510
+ mockValidator.checkPrismaClientGenerated.mockReturnValue({
511
+ generated: false // Client doesn't exist, will generate
512
+ });
513
+
402
514
  await dbSetupCommand({ verbose: false });
403
515
 
404
516
  expect(mockRunner.runPrismaGenerate).toHaveBeenCalledWith('mongodb', false);
@@ -4,7 +4,7 @@ const dotenv = require('dotenv');
4
4
  const {
5
5
  validateDatabaseUrl,
6
6
  getDatabaseType,
7
- testDatabaseConnection
7
+ checkPrismaClientGenerated
8
8
  } = require('../utils/database-validator');
9
9
  const {
10
10
  runPrismaGenerate,
@@ -16,7 +16,6 @@ const {
16
16
  const {
17
17
  getDatabaseUrlMissingError,
18
18
  getDatabaseTypeNotConfiguredError,
19
- getDatabaseConnectionError,
20
19
  getPrismaCommandError,
21
20
  getDatabaseSetupSuccess
22
21
  } = require('../utils/error-messages');
@@ -75,44 +74,52 @@ async function dbSetupCommand(options = {}) {
75
74
  console.log(chalk.green(`✓ Using ${dbType}\n`));
76
75
  }
77
76
 
78
- // Step 3: Test database connection
77
+ // Step 3: Check if Prisma client exists, generate if needed
79
78
  if (verbose) {
80
- console.log(chalk.gray('Step 3: Testing database connection...'));
79
+ console.log(chalk.gray('Step 3: Checking Prisma client...'));
81
80
  }
82
81
 
83
- console.log(chalk.gray('Connecting to database...'));
84
- const connectionTest = await testDatabaseConnection(urlValidation.url, dbType);
82
+ const clientCheck = checkPrismaClientGenerated(dbType);
83
+ const forceRegenerate = options.force || false;
85
84
 
86
- if (!connectionTest.connected) {
87
- console.error(getDatabaseConnectionError(connectionTest.error, dbType));
88
- process.exit(1);
89
- }
90
-
91
- console.log(chalk.green('✓ Database connection verified\n'));
92
-
93
- // Step 4: Generate Prisma client
94
- console.log(chalk.cyan('Generating Prisma client...'));
85
+ if (clientCheck.generated && !forceRegenerate) {
86
+ // Client already exists and --force not specified
87
+ console.log(chalk.green('✓ Prisma client already exists (skipping generation)\n'));
88
+ if (verbose) {
89
+ console.log(chalk.gray(` Client location: ${clientCheck.path}\n`));
90
+ }
91
+ } else {
92
+ // Client doesn't exist OR --force specified - generate it
93
+ if (forceRegenerate && clientCheck.generated) {
94
+ console.log(chalk.yellow('⚠️ Forcing Prisma client regeneration...'));
95
+ } else {
96
+ console.log(chalk.cyan('Generating Prisma client...'));
97
+ }
95
98
 
96
- const generateResult = await runPrismaGenerate(dbType, verbose);
99
+ const generateResult = await runPrismaGenerate(dbType, verbose);
97
100
 
98
- if (!generateResult.success) {
99
- console.error(getPrismaCommandError('generate', generateResult.error));
100
- if (generateResult.output) {
101
- console.error(chalk.gray(generateResult.output));
101
+ if (!generateResult.success) {
102
+ console.error(getPrismaCommandError('generate', generateResult.error));
103
+ if (generateResult.output) {
104
+ console.error(chalk.gray(generateResult.output));
105
+ }
106
+ process.exit(1);
102
107
  }
103
- process.exit(1);
104
- }
105
108
 
106
- console.log(chalk.green('✓ Prisma client generated\n'));
109
+ console.log(chalk.green('✓ Prisma client generated\n'));
110
+ }
107
111
 
108
- // Step 5: Check database state
112
+ // Step 4: Check database state
113
+ // Note: We skip connection testing in db:setup because when using frigg:local,
114
+ // the CLI code runs from tmp/frigg but the client is in backend/node_modules,
115
+ // causing module resolution mismatches. Connection testing happens in frigg start.
109
116
  if (verbose) {
110
- console.log(chalk.gray('Step 5: Checking database state...'));
117
+ console.log(chalk.gray('Step 4: Checking database state...'));
111
118
  }
112
119
 
113
120
  const stateCheck = await checkDatabaseState(dbType);
114
121
 
115
- // Step 6: Run migrations or db push
122
+ // Step 5: Run migrations or db push
116
123
  if (dbType === 'postgresql') {
117
124
  console.log(chalk.cyan('Running database migrations...'));
118
125
 
@@ -9,7 +9,7 @@ const {
9
9
  validatePackageExists,
10
10
  searchAndSelectPackage,
11
11
  } = require('./validate-package');
12
- const { findNearestBackendPackageJson, validateBackendPath } = require('@friggframework/core');
12
+ const { findNearestBackendPackageJson, validateBackendPath } = require('@friggframework/core/utils');
13
13
 
14
14
  const installCommand = async (apiModuleName) => {
15
15
  try {
@@ -5,13 +5,11 @@ const chalk = require('chalk');
5
5
  const {
6
6
  validateDatabaseUrl,
7
7
  getDatabaseType,
8
- testDatabaseConnection,
9
8
  checkPrismaClientGenerated
10
9
  } = require('../utils/database-validator');
11
10
  const {
12
11
  getDatabaseUrlMissingError,
13
12
  getDatabaseTypeNotConfiguredError,
14
- getDatabaseConnectionError,
15
13
  getPrismaClientNotGeneratedError
16
14
  } = require('../utils/error-messages');
17
15
 
@@ -142,21 +140,10 @@ async function performDatabaseChecks(verbose) {
142
140
  console.log(chalk.green('✓ Prisma client generated'));
143
141
  }
144
142
 
145
- // Check 4: Test database connection (only after confirming client exists)
146
- if (verbose) {
147
- console.log(chalk.gray('Testing database connection...'));
148
- }
149
-
150
- const connectionTest = await testDatabaseConnection(urlValidation.url, dbType, 5000);
151
-
152
- if (!connectionTest.connected) {
153
- console.error(getDatabaseConnectionError(connectionTest.error, dbType));
154
- throw new Error('Database connection failed');
155
- }
156
-
157
- if (verbose) {
158
- console.log(chalk.green('✓ Database connection verified'));
159
- }
143
+ // Note: We skip connection testing in the start command because when using frigg:local,
144
+ // the CLI code runs from tmp/frigg but the client is in backend/node_modules,
145
+ // causing module resolution mismatches. The backend will test its own database
146
+ // connection when it starts.
160
147
  }
161
148
 
162
149
  module.exports = { startCommand };
@@ -15,7 +15,6 @@
15
15
  const mockValidator = {
16
16
  validateDatabaseUrl: jest.fn(),
17
17
  getDatabaseType: jest.fn(),
18
- testDatabaseConnection: jest.fn(),
19
18
  checkPrismaClientGenerated: jest.fn()
20
19
  };
21
20
 
@@ -55,7 +54,6 @@ describe('startCommand', () => {
55
54
  const defaultValidator = createMockDatabaseValidator();
56
55
  mockValidator.validateDatabaseUrl.mockReturnValue(defaultValidator.validateDatabaseUrl());
57
56
  mockValidator.getDatabaseType.mockReturnValue(defaultValidator.getDatabaseType());
58
- mockValidator.testDatabaseConnection.mockResolvedValue(defaultValidator.testDatabaseConnection());
59
57
  mockValidator.checkPrismaClientGenerated.mockReturnValue(defaultValidator.checkPrismaClientGenerated());
60
58
 
61
59
  // Mock dotenv
@@ -213,7 +211,6 @@ describe('startCommand', () => {
213
211
 
214
212
  expect(mockValidator.validateDatabaseUrl).toHaveBeenCalled();
215
213
  expect(mockValidator.getDatabaseType).toHaveBeenCalled();
216
- expect(mockValidator.testDatabaseConnection).toHaveBeenCalled();
217
214
  expect(mockValidator.checkPrismaClientGenerated).toHaveBeenCalled();
218
215
  expect(mockProcessExit).not.toHaveBeenCalled();
219
216
  expect(spawn).toHaveBeenCalled();
@@ -244,19 +241,6 @@ describe('startCommand', () => {
244
241
  expect(spawn).not.toHaveBeenCalled();
245
242
  });
246
243
 
247
- it('should fail when database connection fails', async () => {
248
- mockValidator.testDatabaseConnection.mockResolvedValue({
249
- connected: false,
250
- error: 'Connection failed'
251
- });
252
-
253
- await expect(startCommand({})).rejects.toThrow('process.exit called');
254
-
255
- expect(mockConsoleError).toHaveBeenCalled();
256
- expect(mockProcessExit).toHaveBeenCalledWith(1);
257
- expect(spawn).not.toHaveBeenCalled();
258
- });
259
-
260
244
  it('should fail when Prisma client not generated', async () => {
261
245
  mockValidator.checkPrismaClientGenerated.mockReturnValue({
262
246
  generated: false,
@@ -104,7 +104,7 @@ async function testDatabaseConnection(databaseUrl, dbType, timeout = 5000) {
104
104
 
105
105
  /**
106
106
  * Checks if Prisma client is generated for the database type
107
- * Uses require.resolve() to find the client regardless of package manager hoisting
107
+ * Uses require.resolve to find the client in node_modules
108
108
  *
109
109
  * @param {'mongodb'|'postgresql'} dbType - Database type
110
110
  * @param {string} projectRoot - Project root directory (used for require.resolve context)
@@ -17,9 +17,11 @@ const chalk = require('chalk');
17
17
  */
18
18
  function getPrismaSchemaPath(dbType, projectRoot = process.cwd()) {
19
19
  // Try multiple locations for the schema file
20
- // 1. Local node_modules (standard install)
20
+ // Priority order:
21
+ // 1. Local node_modules (where @friggframework/core is installed - production scenario)
21
22
  // 2. Parent node_modules (workspace/monorepo setup)
22
23
  const possiblePaths = [
24
+ // Check where Frigg is installed via npm (production scenario)
23
25
  path.join(projectRoot, 'node_modules', '@friggframework', 'core', `prisma-${dbType}`, 'schema.prisma'),
24
26
  path.join(projectRoot, '..', 'node_modules', '@friggframework', 'core', `prisma-${dbType}`, 'schema.prisma')
25
27
  ];
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@friggframework/devtools",
3
3
  "prettier": "@friggframework/prettier-config",
4
- "version": "2.0.0--canary.395.04851d8.0",
4
+ "version": "2.0.0--canary.395.8f4c094.0",
5
5
  "dependencies": {
6
6
  "@aws-sdk/client-ec2": "^3.835.0",
7
7
  "@aws-sdk/client-kms": "^3.835.0",
@@ -9,8 +9,8 @@
9
9
  "@babel/eslint-parser": "^7.18.9",
10
10
  "@babel/parser": "^7.25.3",
11
11
  "@babel/traverse": "^7.25.3",
12
- "@friggframework/schemas": "2.0.0--canary.395.04851d8.0",
13
- "@friggframework/test": "2.0.0--canary.395.04851d8.0",
12
+ "@friggframework/schemas": "2.0.0--canary.395.8f4c094.0",
13
+ "@friggframework/test": "2.0.0--canary.395.8f4c094.0",
14
14
  "@hapi/boom": "^10.0.1",
15
15
  "@inquirer/prompts": "^5.3.8",
16
16
  "axios": "^1.7.2",
@@ -32,8 +32,8 @@
32
32
  "serverless-http": "^2.7.0"
33
33
  },
34
34
  "devDependencies": {
35
- "@friggframework/eslint-config": "2.0.0--canary.395.04851d8.0",
36
- "@friggframework/prettier-config": "2.0.0--canary.395.04851d8.0",
35
+ "@friggframework/eslint-config": "2.0.0--canary.395.8f4c094.0",
36
+ "@friggframework/prettier-config": "2.0.0--canary.395.8f4c094.0",
37
37
  "aws-sdk-client-mock": "^4.1.0",
38
38
  "aws-sdk-client-mock-jest": "^4.1.0",
39
39
  "jest": "^30.1.3",
@@ -68,5 +68,5 @@
68
68
  "publishConfig": {
69
69
  "access": "public"
70
70
  },
71
- "gitHead": "04851d8d253adc1abe6aeae2d8e26e9887d7334b"
71
+ "gitHead": "8f4c0943481f1c896d9dc593f3bc766ed7537a58"
72
72
  }