@friggframework/devtools 2.0.0--canary.545.c459392.0 → 2.0.0--canary.547.67ebb53.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (128) hide show
  1. package/frigg-cli/README.md +1 -1
  2. package/frigg-cli/__tests__/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/networking/vpc-builder.test.js +4 -2
  30. package/infrastructure/domains/networking/vpc-resolver.test.js +1 -1
  31. package/infrastructure/domains/shared/cloudformation-discovery.test.js +7 -4
  32. package/infrastructure/domains/shared/resource-discovery.js +5 -5
  33. package/infrastructure/domains/shared/types/app-definition.js +0 -21
  34. package/infrastructure/domains/shared/types/discovery-result.test.js +1 -1
  35. package/infrastructure/domains/shared/utilities/base-definition-factory.js +1 -10
  36. package/infrastructure/domains/shared/utilities/base-definition-factory.test.js +2 -2
  37. package/infrastructure/infrastructure-composer.js +0 -2
  38. package/infrastructure/infrastructure-composer.test.js +2 -2
  39. package/management-ui/README.md +109 -245
  40. package/package.json +7 -8
  41. package/frigg-cli/__tests__/application/use-cases/AddApiModuleToIntegrationUseCase.test.js +0 -326
  42. package/frigg-cli/__tests__/application/use-cases/CreateApiModuleUseCase.test.js +0 -337
  43. package/frigg-cli/__tests__/domain/entities/ApiModule.test.js +0 -373
  44. package/frigg-cli/__tests__/domain/entities/AppDefinition.test.js +0 -313
  45. package/frigg-cli/__tests__/domain/services/IntegrationValidator.test.js +0 -269
  46. package/frigg-cli/__tests__/domain/value-objects/IntegrationName.test.js +0 -82
  47. package/frigg-cli/__tests__/infrastructure/adapters/IntegrationJsUpdater.test.js +0 -408
  48. package/frigg-cli/__tests__/infrastructure/repositories/FileSystemApiModuleRepository.test.js +0 -583
  49. package/frigg-cli/__tests__/infrastructure/repositories/FileSystemAppDefinitionRepository.test.js +0 -314
  50. package/frigg-cli/__tests__/infrastructure/repositories/FileSystemIntegrationRepository.test.js +0 -383
  51. package/frigg-cli/__tests__/unit/commands/init.test.js +0 -406
  52. package/frigg-cli/__tests__/unit/commands/provider-dispatch.test.js +0 -383
  53. package/frigg-cli/__tests__/unit/commands/repair.test.js +0 -275
  54. package/frigg-cli/__tests__/unit/start-command/application/RunPreflightChecksUseCase.test.js +0 -411
  55. package/frigg-cli/__tests__/unit/start-command/infrastructure/DatabaseAdapter.test.js +0 -405
  56. package/frigg-cli/__tests__/unit/start-command/infrastructure/DockerAdapter.test.js +0 -496
  57. package/frigg-cli/__tests__/unit/start-command/presentation/InteractivePromptAdapter.test.js +0 -474
  58. package/frigg-cli/__tests__/unit/utils/output.test.js +0 -196
  59. package/frigg-cli/application/use-cases/AddApiModuleToIntegrationUseCase.js +0 -93
  60. package/frigg-cli/application/use-cases/CreateApiModuleUseCase.js +0 -93
  61. package/frigg-cli/application/use-cases/CreateIntegrationUseCase.js +0 -103
  62. package/frigg-cli/container.js +0 -172
  63. package/frigg-cli/docs/OUTPUT_MIGRATION_GUIDE.md +0 -286
  64. package/frigg-cli/domain/entities/ApiModule.js +0 -272
  65. package/frigg-cli/domain/entities/AppDefinition.js +0 -227
  66. package/frigg-cli/domain/entities/Integration.js +0 -198
  67. package/frigg-cli/domain/exceptions/DomainException.js +0 -24
  68. package/frigg-cli/domain/ports/IApiModuleRepository.js +0 -53
  69. package/frigg-cli/domain/ports/IAppDefinitionRepository.js +0 -43
  70. package/frigg-cli/domain/ports/IIntegrationRepository.js +0 -61
  71. package/frigg-cli/domain/services/IntegrationValidator.js +0 -185
  72. package/frigg-cli/domain/value-objects/IntegrationId.js +0 -42
  73. package/frigg-cli/domain/value-objects/IntegrationName.js +0 -60
  74. package/frigg-cli/domain/value-objects/SemanticVersion.js +0 -70
  75. package/frigg-cli/infrastructure/UnitOfWork.js +0 -46
  76. package/frigg-cli/infrastructure/adapters/BackendJsUpdater.js +0 -197
  77. package/frigg-cli/infrastructure/adapters/FileSystemAdapter.js +0 -224
  78. package/frigg-cli/infrastructure/adapters/IntegrationJsUpdater.js +0 -249
  79. package/frigg-cli/infrastructure/adapters/SchemaValidator.js +0 -92
  80. package/frigg-cli/infrastructure/repositories/FileSystemApiModuleRepository.js +0 -373
  81. package/frigg-cli/infrastructure/repositories/FileSystemAppDefinitionRepository.js +0 -116
  82. package/frigg-cli/infrastructure/repositories/FileSystemIntegrationRepository.js +0 -277
  83. package/frigg-cli/package-lock.json +0 -16226
  84. package/frigg-cli/start-command/application/RunPreflightChecksUseCase.js +0 -376
  85. package/frigg-cli/start-command/infrastructure/DatabaseAdapter.js +0 -591
  86. package/frigg-cli/start-command/infrastructure/DockerAdapter.js +0 -306
  87. package/frigg-cli/start-command/presentation/InteractivePromptAdapter.js +0 -329
  88. package/frigg-cli/templates/backend/.env.example +0 -62
  89. package/frigg-cli/templates/backend/.eslintrc.json +0 -12
  90. package/frigg-cli/templates/backend/.prettierrc +0 -6
  91. package/frigg-cli/templates/backend/docker-compose.yml +0 -22
  92. package/frigg-cli/templates/backend/index.js +0 -96
  93. package/frigg-cli/templates/backend/infrastructure.js +0 -12
  94. package/frigg-cli/templates/backend/jest.config.js +0 -17
  95. package/frigg-cli/templates/backend/package.json +0 -50
  96. package/frigg-cli/templates/backend/src/api-modules/.gitkeep +0 -10
  97. package/frigg-cli/templates/backend/src/base/.gitkeep +0 -7
  98. package/frigg-cli/templates/backend/src/integrations/.gitkeep +0 -10
  99. package/frigg-cli/templates/backend/src/integrations/ExampleIntegration.js +0 -65
  100. package/frigg-cli/templates/backend/src/utils/.gitkeep +0 -7
  101. package/frigg-cli/templates/backend/test/setup.js +0 -30
  102. package/frigg-cli/templates/backend/ui-extensions/.gitkeep +0 -0
  103. package/frigg-cli/templates/backend/ui-extensions/README.md +0 -77
  104. package/frigg-cli/utils/__tests__/provider-helper.test.js +0 -55
  105. package/frigg-cli/utils/__tests__/repo-detection.test.js +0 -436
  106. package/frigg-cli/utils/output.js +0 -382
  107. package/frigg-cli/utils/provider-helper.js +0 -75
  108. package/frigg-cli/validate-command/__tests__/adapters/validate-command.test.js +0 -205
  109. package/frigg-cli/validate-command/__tests__/application/validate-app-use-case.test.js +0 -104
  110. package/frigg-cli/validate-command/__tests__/domain/fix-suggestion.test.js +0 -153
  111. package/frigg-cli/validate-command/__tests__/domain/validation-error.test.js +0 -162
  112. package/frigg-cli/validate-command/__tests__/domain/validation-result.test.js +0 -152
  113. package/frigg-cli/validate-command/__tests__/infrastructure/api-module-validator.test.js +0 -332
  114. package/frigg-cli/validate-command/__tests__/infrastructure/app-definition-validator.test.js +0 -191
  115. package/frigg-cli/validate-command/__tests__/infrastructure/integration-class-validator.test.js +0 -146
  116. package/frigg-cli/validate-command/__tests__/infrastructure/template-validation.test.js +0 -155
  117. package/frigg-cli/validate-command/adapters/cli/validate-command.js +0 -199
  118. package/frigg-cli/validate-command/application/use-cases/validate-app-use-case.js +0 -35
  119. package/frigg-cli/validate-command/domain/entities/validation-result.js +0 -74
  120. package/frigg-cli/validate-command/domain/value-objects/fix-suggestion.js +0 -74
  121. package/frigg-cli/validate-command/domain/value-objects/validation-error.js +0 -68
  122. package/frigg-cli/validate-command/infrastructure/validators/api-module-validator.js +0 -181
  123. package/frigg-cli/validate-command/infrastructure/validators/app-definition-validator.js +0 -128
  124. package/frigg-cli/validate-command/infrastructure/validators/integration-class-validator.js +0 -113
  125. package/infrastructure/domains/admin-scripts/admin-script-builder.js +0 -200
  126. package/infrastructure/domains/admin-scripts/admin-script-builder.test.js +0 -499
  127. package/infrastructure/domains/admin-scripts/index.js +0 -5
  128. package/infrastructure/jest.config.js +0 -16
@@ -1,591 +0,0 @@
1
- /**
2
- * DatabaseAdapter - Infrastructure adapter for database connectivity checks
3
- *
4
- * Provides methods to parse connection strings and test database reachability
5
- * Used by pre-flight checks to verify database infrastructure is ready
6
- */
7
-
8
- const net = require('net');
9
- const { spawn } = require('child_process');
10
- const path = require('path');
11
-
12
- class DatabaseAdapter {
13
- /**
14
- * Parse a database connection string
15
- * @param {string} connectionString - Database URL
16
- * @returns {object} Parsed connection details or error
17
- */
18
- parseConnectionString(connectionString) {
19
- if (!connectionString || typeof connectionString !== 'string') {
20
- return { error: 'Invalid connection string: must be a non-empty string' };
21
- }
22
-
23
- try {
24
- // Handle mongodb+srv:// protocol separately (URL doesn't parse it well)
25
- if (connectionString.startsWith('mongodb+srv://')) {
26
- return this._parseMongoSrv(connectionString);
27
- }
28
-
29
- // Handle MongoDB replica set connection strings with multiple hosts
30
- // mongodb://host1:port1,host2:port2,host3:port3/database
31
- if (connectionString.startsWith('mongodb://') && connectionString.includes(',')) {
32
- return this._parseMongoReplicaSet(connectionString);
33
- }
34
-
35
- const url = new URL(connectionString);
36
- const protocol = url.protocol.replace(':', '');
37
-
38
- // Determine database type
39
- let type;
40
- let defaultPort;
41
- if (protocol === 'mongodb') {
42
- type = 'mongodb';
43
- defaultPort = 27017;
44
- } else if (protocol === 'postgresql' || protocol === 'postgres') {
45
- type = 'postgresql';
46
- defaultPort = 5432;
47
- } else {
48
- return { error: `Unsupported database type: ${protocol}` };
49
- }
50
-
51
- // Extract first host from potential replica set
52
- const host = url.hostname.split(',')[0];
53
- const port = url.port ? parseInt(url.port, 10) : defaultPort;
54
-
55
- // Extract database name from pathname
56
- const database = url.pathname.replace('/', '') || undefined;
57
-
58
- // Extract user if present
59
- const user = url.username || undefined;
60
-
61
- return {
62
- type,
63
- host,
64
- port,
65
- database,
66
- user
67
- };
68
- } catch (error) {
69
- return { error: `Failed to parse connection string: ${error.message}` };
70
- }
71
- }
72
-
73
- /**
74
- * Parse MongoDB replica set connection string with multiple hosts
75
- * @param {string} connectionString - MongoDB replica set connection string
76
- * @returns {object} Parsed connection details
77
- */
78
- _parseMongoReplicaSet(connectionString) {
79
- try {
80
- // mongodb://user:pass@host1:port1,host2:port2/database?options
81
- const withoutProtocol = connectionString.replace('mongodb://', '');
82
-
83
- // Extract user:pass if present
84
- let hostsPart = withoutProtocol;
85
- let user;
86
- if (withoutProtocol.includes('@')) {
87
- const atIndex = withoutProtocol.indexOf('@');
88
- const credentials = withoutProtocol.substring(0, atIndex);
89
- hostsPart = withoutProtocol.substring(atIndex + 1);
90
- user = credentials.split(':')[0];
91
- }
92
-
93
- // Extract database and options
94
- let database;
95
- const slashIndex = hostsPart.indexOf('/');
96
- if (slashIndex !== -1) {
97
- const pathPart = hostsPart.substring(slashIndex + 1);
98
- hostsPart = hostsPart.substring(0, slashIndex);
99
- database = pathPart.split('?')[0] || undefined;
100
- }
101
-
102
- // Get first host:port pair
103
- const firstHost = hostsPart.split(',')[0];
104
- const [host, portStr] = firstHost.split(':');
105
- const port = portStr ? parseInt(portStr, 10) : 27017;
106
-
107
- return {
108
- type: 'mongodb',
109
- host,
110
- port,
111
- database,
112
- user
113
- };
114
- } catch (error) {
115
- return { error: `Failed to parse MongoDB replica set connection string: ${error.message}` };
116
- }
117
- }
118
-
119
- /**
120
- * Parse mongodb+srv:// connection string
121
- * @param {string} connectionString - MongoDB SRV connection string
122
- * @returns {object} Parsed connection details
123
- */
124
- _parseMongoSrv(connectionString) {
125
- try {
126
- // Replace mongodb+srv with https for URL parsing
127
- const url = new URL(connectionString.replace('mongodb+srv://', 'https://'));
128
-
129
- const host = url.hostname;
130
- const database = url.pathname.replace('/', '') || undefined;
131
- const user = url.username || undefined;
132
-
133
- return {
134
- type: 'mongodb',
135
- host,
136
- port: 27017, // SRV uses standard port
137
- database,
138
- user
139
- };
140
- } catch (error) {
141
- return { error: `Failed to parse MongoDB SRV connection string: ${error.message}` };
142
- }
143
- }
144
-
145
- /**
146
- * Get database type from connection string
147
- * @param {string} connectionString - Database URL
148
- * @returns {string|null} 'mongodb' | 'postgresql' | null
149
- */
150
- getDatabaseType(connectionString) {
151
- if (!connectionString) return null;
152
-
153
- if (connectionString.startsWith('mongodb://') || connectionString.startsWith('mongodb+srv://')) {
154
- return 'mongodb';
155
- }
156
- if (connectionString.startsWith('postgresql://') || connectionString.startsWith('postgres://')) {
157
- return 'postgresql';
158
- }
159
- return null;
160
- }
161
-
162
- /**
163
- * Check if a TCP port is reachable
164
- * @param {string} host - Host to connect to
165
- * @param {number} port - Port to connect to
166
- * @param {number} timeout - Timeout in milliseconds (default: 3000)
167
- * @returns {Promise<boolean>} True if port is reachable
168
- */
169
- async isPortReachable(host, port, timeout = 3000) {
170
- return new Promise((resolve) => {
171
- const socket = net.createConnection(port, host);
172
-
173
- socket.setTimeout(timeout);
174
-
175
- socket.on('connect', () => {
176
- socket.destroy();
177
- resolve(true);
178
- });
179
-
180
- socket.on('timeout', () => {
181
- socket.destroy();
182
- resolve(false);
183
- });
184
-
185
- socket.on('error', () => {
186
- socket.destroy();
187
- resolve(false);
188
- });
189
- });
190
- }
191
-
192
- /**
193
- * Check if database is reachable at the connection string
194
- * @param {string} connectionString - Database URL
195
- * @param {number} timeout - Timeout in milliseconds
196
- * @returns {Promise<{reachable: boolean, host?: string, port?: number, type?: string, error?: string}>}
197
- */
198
- async isDatabaseReachable(connectionString, timeout = 3000) {
199
- const parsed = this.parseConnectionString(connectionString);
200
-
201
- if (parsed.error) {
202
- return {
203
- reachable: false,
204
- error: parsed.error
205
- };
206
- }
207
-
208
- const { type, host, port } = parsed;
209
-
210
- try {
211
- const reachable = await this.isPortReachable(host, port, timeout);
212
-
213
- if (reachable) {
214
- return {
215
- reachable: true,
216
- type,
217
- host,
218
- port
219
- };
220
- } else {
221
- return {
222
- reachable: false,
223
- type,
224
- host,
225
- port,
226
- error: `ECONNREFUSED - Unable to connect to ${host}:${port}`
227
- };
228
- }
229
- } catch (error) {
230
- return {
231
- reachable: false,
232
- type,
233
- host,
234
- port,
235
- error: error.message
236
- };
237
- }
238
- }
239
-
240
- /**
241
- * Get connection details summary
242
- * @param {string} connectionString - Database URL
243
- * @returns {object} Connection details or error
244
- */
245
- getConnectionDetails(connectionString) {
246
- const parsed = this.parseConnectionString(connectionString);
247
-
248
- if (parsed.error) {
249
- return { error: parsed.error };
250
- }
251
-
252
- return {
253
- type: parsed.type,
254
- host: parsed.host,
255
- port: parsed.port,
256
- database: parsed.database,
257
- user: parsed.user,
258
- hasCredentials: !!parsed.user
259
- };
260
- }
261
-
262
- /**
263
- * Suggest Docker service configuration for a database type
264
- * @param {string} dbType - 'mongodb' | 'postgresql'
265
- * @returns {object|null} Docker service suggestion or null
266
- */
267
- suggestDockerService(dbType) {
268
- if (dbType === 'mongodb') {
269
- return {
270
- serviceName: 'mongodb',
271
- image: 'mongo:7',
272
- port: 27017,
273
- envVars: {
274
- MONGO_INITDB_DATABASE: 'frigg'
275
- }
276
- };
277
- }
278
-
279
- if (dbType === 'postgresql') {
280
- return {
281
- serviceName: 'postgres',
282
- image: 'postgres:16',
283
- port: 5432,
284
- envVars: {
285
- POSTGRES_DB: 'frigg',
286
- POSTGRES_USER: 'postgres',
287
- POSTGRES_PASSWORD: 'postgres'
288
- }
289
- };
290
- }
291
-
292
- return null;
293
- }
294
-
295
- /**
296
- * Check PostgreSQL migration status using Prisma
297
- * @param {string} projectPath - Path to the project
298
- * @returns {Promise<{migrated: boolean, pendingMigrations?: string[], error?: string}>}
299
- */
300
- async checkMigrationStatus(projectPath) {
301
- return new Promise((resolve) => {
302
- // Look for prisma schema in common locations
303
- // Frigg apps use the schema from @friggframework/core
304
- const possibleSchemaPaths = [
305
- // Project-local prisma schemas
306
- path.join(projectPath, 'prisma', 'postgresql', 'schema.prisma'),
307
- path.join(projectPath, 'prisma', 'schema.prisma'),
308
- // Frigg core schemas (in node_modules)
309
- path.join(projectPath, 'node_modules', '@friggframework', 'core', 'prisma-postgresql', 'schema.prisma'),
310
- path.join(projectPath, 'node_modules', '@friggframework', 'core', 'generated', 'prisma-postgresql', 'schema.prisma')
311
- ];
312
-
313
- // Find the first existing schema path
314
- const fs = require('fs');
315
- let schemaPath = null;
316
- for (const p of possibleSchemaPaths) {
317
- if (fs.existsSync(p)) {
318
- schemaPath = p;
319
- break;
320
- }
321
- }
322
-
323
- if (!schemaPath) {
324
- // No schema found - might be MongoDB project
325
- resolve({ migrated: true, note: 'No Prisma PostgreSQL schema found' });
326
- return;
327
- }
328
-
329
- // Find prisma binary - check local node_modules first
330
- const possiblePrismaPaths = [
331
- path.join(projectPath, 'node_modules', '.bin', 'prisma'),
332
- path.join(projectPath, 'node_modules', 'prisma', 'build', 'index.js')
333
- ];
334
-
335
- let prismaBin = 'npx';
336
- let args = ['prisma', 'migrate', 'status', '--schema', schemaPath];
337
-
338
- // Try to find local prisma binary for pnpm/yarn workspaces
339
- for (const p of possiblePrismaPaths) {
340
- if (fs.existsSync(p)) {
341
- if (p.endsWith('.js')) {
342
- prismaBin = 'node';
343
- args = [p, 'migrate', 'status', '--schema', schemaPath];
344
- } else {
345
- prismaBin = p;
346
- args = ['migrate', 'status', '--schema', schemaPath];
347
- }
348
- break;
349
- }
350
- }
351
-
352
- const child = spawn(prismaBin, args, {
353
- cwd: projectPath,
354
- env: { ...process.env },
355
- stdio: ['pipe', 'pipe', 'pipe'],
356
- shell: prismaBin === 'npx' // Use shell for npx to help with path resolution
357
- });
358
-
359
- let stdout = '';
360
- let stderr = '';
361
-
362
- child.stdout.on('data', (data) => {
363
- stdout += data.toString();
364
- });
365
-
366
- child.stderr.on('data', (data) => {
367
- stderr += data.toString();
368
- });
369
-
370
- child.on('error', (error) => {
371
- resolve({
372
- migrated: false,
373
- error: `Failed to run prisma migrate status: ${error.message}`
374
- });
375
- });
376
-
377
- child.on('close', (code) => {
378
- // Parse the output to determine migration status
379
- const output = stdout + stderr;
380
-
381
- // Check for Prisma not found / not installed error
382
- if (output.includes('Cannot find module') && output.includes('prisma')) {
383
- resolve({
384
- migrated: false,
385
- error: 'Prisma CLI not properly installed. Run "npm install" or "pnpm install" to fix.',
386
- needsInstall: true
387
- });
388
- return;
389
- }
390
-
391
- // Check for common error patterns
392
- if (output.includes('P1001') || output.includes('Can\'t reach database server')) {
393
- resolve({
394
- migrated: false,
395
- error: 'Cannot connect to database to check migrations'
396
- });
397
- return;
398
- }
399
-
400
- if (output.includes('P1003') || output.includes('does not exist')) {
401
- resolve({
402
- migrated: false,
403
- error: 'Database does not exist',
404
- needsSetup: true
405
- });
406
- return;
407
- }
408
-
409
- // Check for "Database schema is not empty" - tables exist but no migrations
410
- if (output.includes('Database schema is not empty')) {
411
- resolve({
412
- migrated: false,
413
- error: 'Database has tables but no migration history. Run prisma migrate to baseline.',
414
- needsBaseline: true
415
- });
416
- return;
417
- }
418
-
419
- // Check for pending migrations
420
- if (output.includes('Following migration') && output.includes('have not yet been applied')) {
421
- const pendingMatch = output.match(/Following migration[s]? have not yet been applied:\s*([\s\S]*?)(?:To apply|$)/);
422
- const pendingMigrations = pendingMatch
423
- ? pendingMatch[1].trim().split('\n').map(m => m.trim()).filter(Boolean)
424
- : [];
425
-
426
- resolve({
427
- migrated: false,
428
- pendingMigrations,
429
- error: 'Database has pending migrations'
430
- });
431
- return;
432
- }
433
-
434
- // Check for "no migration found" - empty migrations folder
435
- if (output.includes('No migration found') || output.includes('Database schema is up to date')) {
436
- resolve({ migrated: true });
437
- return;
438
- }
439
-
440
- // Check for table not found errors (P2021)
441
- if (output.includes('P2021') || output.includes('does not exist in the current database')) {
442
- resolve({
443
- migrated: false,
444
- error: 'Required database tables do not exist. Run migrations first.',
445
- needsSetup: true
446
- });
447
- return;
448
- }
449
-
450
- // If exit code is 0 and no error patterns, assume migrated
451
- if (code === 0) {
452
- resolve({ migrated: true });
453
- return;
454
- }
455
-
456
- // Unknown error
457
- resolve({
458
- migrated: false,
459
- error: output.trim() || `prisma migrate status exited with code ${code}`
460
- });
461
- });
462
-
463
- // Timeout after 30 seconds
464
- setTimeout(() => {
465
- child.kill();
466
- resolve({
467
- migrated: false,
468
- error: 'Timeout checking migration status'
469
- });
470
- }, 30000);
471
- });
472
- }
473
-
474
- /**
475
- * Run Prisma migrations
476
- * @param {string} projectPath - Path to the project
477
- * @param {object} options - Options
478
- * @param {boolean} options.dev - Use dev mode (interactive, can create migrations)
479
- * @returns {Promise<{success: boolean, output?: string, error?: string}>}
480
- */
481
- async runMigrations(projectPath, options = {}) {
482
- return new Promise((resolve) => {
483
- const fs = require('fs');
484
-
485
- // Look for prisma schema
486
- // Frigg apps use the schema from @friggframework/core
487
- const possibleSchemaPaths = [
488
- // Project-local prisma schemas
489
- path.join(projectPath, 'prisma', 'postgresql', 'schema.prisma'),
490
- path.join(projectPath, 'prisma', 'schema.prisma'),
491
- // Frigg core schemas (in node_modules)
492
- path.join(projectPath, 'node_modules', '@friggframework', 'core', 'prisma-postgresql', 'schema.prisma'),
493
- path.join(projectPath, 'node_modules', '@friggframework', 'core', 'generated', 'prisma-postgresql', 'schema.prisma')
494
- ];
495
-
496
- let schemaPath = null;
497
- for (const p of possibleSchemaPaths) {
498
- if (fs.existsSync(p)) {
499
- schemaPath = p;
500
- break;
501
- }
502
- }
503
-
504
- if (!schemaPath) {
505
- resolve({ success: false, error: 'No Prisma schema found' });
506
- return;
507
- }
508
-
509
- // Find prisma binary - check local node_modules first
510
- const possiblePrismaPaths = [
511
- path.join(projectPath, 'node_modules', '.bin', 'prisma'),
512
- path.join(projectPath, 'node_modules', 'prisma', 'build', 'index.js')
513
- ];
514
-
515
- let prismaBin = 'npx';
516
- // Default to 'deploy' mode - non-interactive, just applies existing migrations
517
- // Use 'dev' only if explicitly requested (which requires interactive terminal)
518
- const migrateCommand = options.dev ? 'dev' : 'deploy';
519
- let args = ['prisma', 'migrate', migrateCommand, '--schema', schemaPath];
520
-
521
- // Try to find local prisma binary for pnpm/yarn workspaces
522
- for (const p of possiblePrismaPaths) {
523
- if (fs.existsSync(p)) {
524
- if (p.endsWith('.js')) {
525
- prismaBin = 'node';
526
- args = [p, 'migrate', migrateCommand, '--schema', schemaPath];
527
- } else {
528
- prismaBin = p;
529
- args = ['migrate', migrateCommand, '--schema', schemaPath];
530
- }
531
- break;
532
- }
533
- }
534
-
535
- console.log(` Executing: ${prismaBin} ${args.join(' ')}`);
536
-
537
- const child = spawn(prismaBin, args, {
538
- cwd: projectPath,
539
- env: { ...process.env },
540
- stdio: ['pipe', 'pipe', 'pipe'],
541
- shell: prismaBin === 'npx'
542
- });
543
-
544
- let stdout = '';
545
- let stderr = '';
546
-
547
- child.stdout.on('data', (data) => {
548
- const text = data.toString();
549
- stdout += text;
550
- // Log progress in real-time
551
- process.stdout.write(text);
552
- });
553
-
554
- child.stderr.on('data', (data) => {
555
- const text = data.toString();
556
- stderr += text;
557
- // Log errors in real-time
558
- process.stderr.write(text);
559
- });
560
-
561
- child.on('error', (error) => {
562
- resolve({
563
- success: false,
564
- error: `Failed to run migrations: ${error.message}`
565
- });
566
- });
567
-
568
- child.on('close', (code) => {
569
- if (code === 0) {
570
- resolve({ success: true, output: stdout });
571
- } else {
572
- resolve({
573
- success: false,
574
- error: stderr || stdout || `Migration failed with exit code ${code}`
575
- });
576
- }
577
- });
578
-
579
- // Timeout after 60 seconds for migrations (deploy mode is fast)
580
- setTimeout(() => {
581
- child.kill();
582
- resolve({
583
- success: false,
584
- error: 'Timeout running migrations (60s)'
585
- });
586
- }, 60000);
587
- });
588
- }
589
- }
590
-
591
- module.exports = { DatabaseAdapter };