@friggframework/devtools 2.0.0--canary.490.a8050a4.0 → 2.0.0--canary.499.2ef107f.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/deploy-command/index.js +3 -9
- package/infrastructure/README.md +0 -28
- package/infrastructure/domains/database/migration-builder.js +13 -19
- package/infrastructure/domains/database/migration-builder.test.js +0 -57
- package/infrastructure/domains/integration/integration-builder.js +14 -19
- package/infrastructure/domains/integration/integration-builder.test.js +74 -0
- package/infrastructure/domains/shared/utilities/base-definition-factory.js +17 -27
- package/infrastructure/domains/shared/utilities/base-definition-factory.test.js +0 -73
- package/infrastructure/infrastructure-composer.js +3 -11
- package/infrastructure/scripts/build-prisma-layer.js +81 -8
- package/infrastructure/scripts/build-prisma-layer.test.js +53 -1
- package/infrastructure/scripts/verify-prisma-layer.js +72 -0
- package/package.json +7 -7
- package/layers/prisma/.build-complete +0 -3
|
@@ -284,13 +284,8 @@ async function deployCommand(options) {
|
|
|
284
284
|
|
|
285
285
|
console.log('\n✓ Deployment completed successfully!');
|
|
286
286
|
|
|
287
|
-
// Run post-deployment health check (unless
|
|
288
|
-
|
|
289
|
-
// 1. CLI flag: --skip-doctor
|
|
290
|
-
// 2. AppDefinition: deployment.skipPostDeploymentHealthCheck: true
|
|
291
|
-
const skipHealthCheck = options.skipDoctor || appDefinition?.deployment?.skipPostDeploymentHealthCheck;
|
|
292
|
-
|
|
293
|
-
if (!skipHealthCheck) {
|
|
287
|
+
// Run post-deployment health check (unless --skip-doctor)
|
|
288
|
+
if (!options.skipDoctor) {
|
|
294
289
|
const stackName = getStackName(appDefinition, options);
|
|
295
290
|
|
|
296
291
|
if (stackName) {
|
|
@@ -300,8 +295,7 @@ async function deployCommand(options) {
|
|
|
300
295
|
console.log(' Run "frigg doctor <stack-name>" manually to check stack health');
|
|
301
296
|
}
|
|
302
297
|
} else {
|
|
303
|
-
|
|
304
|
-
console.log(`\n⏭️ Skipping post-deployment health check (${reason})`);
|
|
298
|
+
console.log('\n⏭️ Skipping post-deployment health check (--skip-doctor)');
|
|
305
299
|
}
|
|
306
300
|
}
|
|
307
301
|
|
package/infrastructure/README.md
CHANGED
|
@@ -257,34 +257,6 @@ aws lambda get-function-configuration \
|
|
|
257
257
|
--query 'Layers[*].Arn'
|
|
258
258
|
```
|
|
259
259
|
|
|
260
|
-
**Disabling Prisma Layer (Bundle with Functions):**
|
|
261
|
-
|
|
262
|
-
By default, Frigg uses a Lambda Layer for Prisma. You can disable this and bundle Prisma directly with each function:
|
|
263
|
-
|
|
264
|
-
```javascript
|
|
265
|
-
const appDefinition = {
|
|
266
|
-
name: 'my-app',
|
|
267
|
-
usePrismaLambdaLayer: false, // Bundle Prisma with each function
|
|
268
|
-
integrations: [{ Definition: { name: 'asana' } }],
|
|
269
|
-
};
|
|
270
|
-
```
|
|
271
|
-
|
|
272
|
-
**When to disable the Prisma Layer:**
|
|
273
|
-
|
|
274
|
-
- ✅ CI/CD IAM user lacks `lambda:PublishLayerVersion` permission
|
|
275
|
-
- ✅ Deploying to environments with Lambda layer restrictions
|
|
276
|
-
- ✅ Prefer simpler deployment without layer management
|
|
277
|
-
- ✅ Debugging Prisma client loading issues
|
|
278
|
-
|
|
279
|
-
**Trade-offs:**
|
|
280
|
-
|
|
281
|
-
| Mode | Function Size | Deploy Speed | IAM Permissions Required |
|
|
282
|
-
|------|--------------|--------------|-------------------------|
|
|
283
|
-
| **Layer (default)** | ~45MB per function | Faster (layer cached) | `lambda:PublishLayerVersion` |
|
|
284
|
-
| **Bundled** | ~80MB per function | Slower (Prisma uploaded 5x) | None (layer-related) |
|
|
285
|
-
|
|
286
|
-
**Note:** When `usePrismaLambdaLayer: false`, Prisma client automatically detects the bundled location at runtime. No additional configuration needed.
|
|
287
|
-
|
|
288
260
|
## Usage Examples
|
|
289
261
|
|
|
290
262
|
### Basic Deployment
|
|
@@ -57,9 +57,6 @@ class MigrationBuilder extends InfrastructureBuilder {
|
|
|
57
57
|
// Backwards compatibility: Translate old schema to new ownership schema
|
|
58
58
|
appDefinition = this.translateLegacyConfig(appDefinition, discoveredResources);
|
|
59
59
|
|
|
60
|
-
// Determine if using Prisma Lambda Layer
|
|
61
|
-
const usePrismaLayer = appDefinition.usePrismaLambdaLayer !== false;
|
|
62
|
-
|
|
63
60
|
const result = {
|
|
64
61
|
functions: {}, // Lambda function definitions
|
|
65
62
|
resources: {},
|
|
@@ -79,7 +76,7 @@ class MigrationBuilder extends InfrastructureBuilder {
|
|
|
79
76
|
console.log(` Queue: ${decisions.queue.ownership} - ${decisions.queue.reason}`);
|
|
80
77
|
|
|
81
78
|
// Build resources based on ownership decisions
|
|
82
|
-
await this.buildFromDecisions(decisions, appDefinition, discoveredResources, result
|
|
79
|
+
await this.buildFromDecisions(decisions, appDefinition, discoveredResources, result);
|
|
83
80
|
|
|
84
81
|
console.log(`[${this.name}] ✅ Migration infrastructure configuration completed`);
|
|
85
82
|
return result;
|
|
@@ -210,7 +207,7 @@ class MigrationBuilder extends InfrastructureBuilder {
|
|
|
210
207
|
/**
|
|
211
208
|
* Build migration resources based on ownership decisions
|
|
212
209
|
*/
|
|
213
|
-
async buildFromDecisions(decisions, appDefinition, discoveredResources, result
|
|
210
|
+
async buildFromDecisions(decisions, appDefinition, discoveredResources, result) {
|
|
214
211
|
// Determine if we need to create resources or use existing ones
|
|
215
212
|
const shouldCreateBucket = decisions.bucket.ownership === ResourceOwnership.STACK;
|
|
216
213
|
const shouldCreateQueue = decisions.queue.ownership === ResourceOwnership.STACK;
|
|
@@ -218,12 +215,12 @@ class MigrationBuilder extends InfrastructureBuilder {
|
|
|
218
215
|
if (shouldCreateBucket && shouldCreateQueue && !decisions.bucket.physicalId && !decisions.queue.physicalId) {
|
|
219
216
|
// Create all new migration infrastructure
|
|
220
217
|
console.log(' → Creating new migration infrastructure in stack');
|
|
221
|
-
await this.createMigrationInfrastructure(appDefinition, result
|
|
218
|
+
await this.createMigrationInfrastructure(appDefinition, result);
|
|
222
219
|
} else if ((decisions.bucket.ownership === ResourceOwnership.STACK && decisions.bucket.physicalId) ||
|
|
223
220
|
(decisions.queue.ownership === ResourceOwnership.STACK && decisions.queue.physicalId)) {
|
|
224
221
|
// Resources exist in stack - add definitions (CloudFormation idempotency)
|
|
225
222
|
console.log(' → Adding migration definitions to template (existing in stack)');
|
|
226
|
-
await this.createMigrationInfrastructure(appDefinition, result
|
|
223
|
+
await this.createMigrationInfrastructure(appDefinition, result);
|
|
227
224
|
} else {
|
|
228
225
|
// Use external resources
|
|
229
226
|
console.log(' → Using external migration resources');
|
|
@@ -235,21 +232,18 @@ class MigrationBuilder extends InfrastructureBuilder {
|
|
|
235
232
|
* Create Lambda function definitions for database migrations
|
|
236
233
|
* Based on refactor/add-better-support-for-commands branch implementation
|
|
237
234
|
*/
|
|
238
|
-
async createFunctionDefinitions(result
|
|
235
|
+
async createFunctionDefinitions(result) {
|
|
239
236
|
console.log(' 🔍 DEBUG: createFunctionDefinitions called');
|
|
240
237
|
console.log(' 🔍 DEBUG: result.functions is:', typeof result.functions, result.functions);
|
|
241
238
|
// Migration WORKER package config (needs Prisma CLI WASM files)
|
|
242
239
|
const migrationWorkerPackageConfig = {
|
|
243
240
|
individually: true,
|
|
244
241
|
exclude: [
|
|
245
|
-
//
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
'node_modules/@friggframework/core/generated/**',
|
|
251
|
-
// But KEEP node_modules/prisma/** (the CLI with WASM)
|
|
252
|
-
] : []),
|
|
242
|
+
// Exclude Prisma runtime client - it's in the Lambda Layer
|
|
243
|
+
'node_modules/@prisma/client/**',
|
|
244
|
+
'node_modules/.prisma/**',
|
|
245
|
+
'node_modules/@friggframework/core/generated/**',
|
|
246
|
+
// But KEEP node_modules/prisma/** (the CLI with WASM)
|
|
253
247
|
|
|
254
248
|
// Exclude ALL nested node_modules
|
|
255
249
|
'node_modules/**/node_modules/**',
|
|
@@ -428,7 +422,7 @@ class MigrationBuilder extends InfrastructureBuilder {
|
|
|
428
422
|
console.log(' 🔍 DEBUG: About to create dbMigrationWorker...');
|
|
429
423
|
result.functions.dbMigrationWorker = {
|
|
430
424
|
handler: 'node_modules/@friggframework/core/handlers/workers/db-migration.handler',
|
|
431
|
-
|
|
425
|
+
layers: [{ Ref: 'PrismaLambdaLayer' }], // Use layer for Prisma client runtime
|
|
432
426
|
skipEsbuild: true,
|
|
433
427
|
timeout: 900, // 15 minutes for long migrations
|
|
434
428
|
memorySize: 1024, // Extra memory for Prisma operations
|
|
@@ -490,12 +484,12 @@ class MigrationBuilder extends InfrastructureBuilder {
|
|
|
490
484
|
* Create migration infrastructure CloudFormation resources
|
|
491
485
|
* Creates S3 bucket, SQS queue, and Lambda function definitions
|
|
492
486
|
*/
|
|
493
|
-
async createMigrationInfrastructure(appDefinition, result
|
|
487
|
+
async createMigrationInfrastructure(appDefinition, result) {
|
|
494
488
|
console.log(' 🔍 DEBUG: createMigrationInfrastructure called');
|
|
495
489
|
console.log(' 🔍 DEBUG: result object before createFunctionDefinitions:', Object.keys(result));
|
|
496
490
|
|
|
497
491
|
// Create Lambda function definitions first (they reference the queue)
|
|
498
|
-
await this.createFunctionDefinitions(result
|
|
492
|
+
await this.createFunctionDefinitions(result);
|
|
499
493
|
|
|
500
494
|
console.log(' 🔍 DEBUG: result.functions after createFunctionDefinitions:', Object.keys(result.functions || {}));
|
|
501
495
|
|
|
@@ -290,62 +290,5 @@ describe('MigrationBuilder', () => {
|
|
|
290
290
|
expect(builder.getName()).toBe('MigrationBuilder');
|
|
291
291
|
});
|
|
292
292
|
});
|
|
293
|
-
|
|
294
|
-
describe('usePrismaLayer configuration', () => {
|
|
295
|
-
it('should include Prisma layer in migration worker when usePrismaLayer=true (default)', async () => {
|
|
296
|
-
const appDef = {
|
|
297
|
-
database: {
|
|
298
|
-
postgres: { enable: true },
|
|
299
|
-
},
|
|
300
|
-
usePrismaLambdaLayer: true,
|
|
301
|
-
};
|
|
302
|
-
|
|
303
|
-
const result = await builder.build(appDef, {});
|
|
304
|
-
|
|
305
|
-
expect(result.functions.dbMigrationWorker.layers).toEqual([{ Ref: 'PrismaLambdaLayer' }]);
|
|
306
|
-
|
|
307
|
-
// Prisma client should be excluded from package (but not CLI)
|
|
308
|
-
expect(result.functions.dbMigrationWorker.package.exclude).toEqual(
|
|
309
|
-
expect.arrayContaining([
|
|
310
|
-
'node_modules/@prisma/client/**',
|
|
311
|
-
'node_modules/.prisma/**',
|
|
312
|
-
'node_modules/@friggframework/core/generated/**',
|
|
313
|
-
])
|
|
314
|
-
);
|
|
315
|
-
});
|
|
316
|
-
|
|
317
|
-
it('should NOT include Prisma layer when usePrismaLayer=false', async () => {
|
|
318
|
-
const appDef = {
|
|
319
|
-
database: {
|
|
320
|
-
postgres: { enable: true },
|
|
321
|
-
},
|
|
322
|
-
usePrismaLambdaLayer: false,
|
|
323
|
-
};
|
|
324
|
-
|
|
325
|
-
const result = await builder.build(appDef, {});
|
|
326
|
-
|
|
327
|
-
expect(result.functions.dbMigrationWorker.layers).toBeUndefined();
|
|
328
|
-
});
|
|
329
|
-
|
|
330
|
-
it('should bundle Prisma CLI with migration worker when usePrismaLayer=false', async () => {
|
|
331
|
-
const appDef = {
|
|
332
|
-
database: {
|
|
333
|
-
postgres: { enable: true },
|
|
334
|
-
},
|
|
335
|
-
usePrismaLambdaLayer: false,
|
|
336
|
-
};
|
|
337
|
-
|
|
338
|
-
const result = await builder.build(appDef, {});
|
|
339
|
-
|
|
340
|
-
// Prisma should NOT be excluded from package (will be bundled)
|
|
341
|
-
expect(result.functions.dbMigrationWorker.package.exclude).not.toEqual(
|
|
342
|
-
expect.arrayContaining([
|
|
343
|
-
'node_modules/@prisma/client/**',
|
|
344
|
-
'node_modules/.prisma/**',
|
|
345
|
-
'node_modules/@friggframework/core/generated/**',
|
|
346
|
-
])
|
|
347
|
-
);
|
|
348
|
-
});
|
|
349
|
-
});
|
|
350
293
|
});
|
|
351
294
|
|
|
@@ -62,9 +62,6 @@ class IntegrationBuilder extends InfrastructureBuilder {
|
|
|
62
62
|
console.log(`\n[${this.name}] Configuring integrations...`);
|
|
63
63
|
console.log(` Processing ${appDefinition.integrations.length} integrations...`);
|
|
64
64
|
|
|
65
|
-
// Determine if using Prisma Lambda Layer
|
|
66
|
-
const usePrismaLayer = appDefinition.usePrismaLambdaLayer !== false;
|
|
67
|
-
|
|
68
65
|
const result = {
|
|
69
66
|
functions: {},
|
|
70
67
|
resources: {},
|
|
@@ -90,7 +87,7 @@ class IntegrationBuilder extends InfrastructureBuilder {
|
|
|
90
87
|
});
|
|
91
88
|
|
|
92
89
|
// Build resources based on ownership decisions
|
|
93
|
-
await this.buildFromDecisions(decisions, appDefinition, result
|
|
90
|
+
await this.buildFromDecisions(decisions, appDefinition, result);
|
|
94
91
|
|
|
95
92
|
console.log(`[${this.name}] ✅ Integration configuration completed`);
|
|
96
93
|
return result;
|
|
@@ -145,7 +142,7 @@ class IntegrationBuilder extends InfrastructureBuilder {
|
|
|
145
142
|
/**
|
|
146
143
|
* Build integration resources based on ownership decisions
|
|
147
144
|
*/
|
|
148
|
-
async buildFromDecisions(decisions, appDefinition, result
|
|
145
|
+
async buildFromDecisions(decisions, appDefinition, result) {
|
|
149
146
|
// Create InternalErrorQueue if ownership = STACK
|
|
150
147
|
const shouldCreateInternalErrorQueue = decisions.internalErrorQueue.ownership === ResourceOwnership.STACK;
|
|
151
148
|
|
|
@@ -158,7 +155,7 @@ class IntegrationBuilder extends InfrastructureBuilder {
|
|
|
158
155
|
}
|
|
159
156
|
|
|
160
157
|
// Create Lambda function definitions and queue resources for each integration
|
|
161
|
-
const functionPackageConfig = this.createFunctionPackageConfig(
|
|
158
|
+
const functionPackageConfig = this.createFunctionPackageConfig();
|
|
162
159
|
|
|
163
160
|
for (const integration of appDefinition.integrations) {
|
|
164
161
|
const integrationName = integration.Definition.name;
|
|
@@ -167,7 +164,7 @@ class IntegrationBuilder extends InfrastructureBuilder {
|
|
|
167
164
|
console.log(`\n Adding integration: ${integrationName}`);
|
|
168
165
|
|
|
169
166
|
// Create Lambda function definitions (serverless template code)
|
|
170
|
-
await this.createFunctionDefinitions(integration, functionPackageConfig, result
|
|
167
|
+
await this.createFunctionDefinitions(integration, functionPackageConfig, result);
|
|
171
168
|
|
|
172
169
|
// Create or reference SQS queue based on ownership decision
|
|
173
170
|
const shouldCreateQueue = queueDecision.ownership === ResourceOwnership.STACK;
|
|
@@ -185,20 +182,18 @@ class IntegrationBuilder extends InfrastructureBuilder {
|
|
|
185
182
|
/**
|
|
186
183
|
* Create function package exclusion configuration
|
|
187
184
|
*/
|
|
188
|
-
createFunctionPackageConfig(
|
|
185
|
+
createFunctionPackageConfig() {
|
|
189
186
|
return {
|
|
190
187
|
exclude: [
|
|
191
188
|
// Exclude AWS SDK (provided by Lambda runtime)
|
|
192
189
|
'node_modules/aws-sdk/**',
|
|
193
190
|
'node_modules/@aws-sdk/**',
|
|
194
191
|
|
|
195
|
-
//
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
'node_modules/@friggframework/core/generated/**',
|
|
201
|
-
] : []),
|
|
192
|
+
// Exclude Prisma (provided via Lambda Layer)
|
|
193
|
+
'node_modules/@prisma/**',
|
|
194
|
+
'node_modules/.prisma/**',
|
|
195
|
+
'node_modules/prisma/**',
|
|
196
|
+
'node_modules/@friggframework/core/generated/**',
|
|
202
197
|
|
|
203
198
|
// Exclude ALL nested node_modules
|
|
204
199
|
'node_modules/**/node_modules/**',
|
|
@@ -254,7 +249,7 @@ class IntegrationBuilder extends InfrastructureBuilder {
|
|
|
254
249
|
* Create Lambda function definitions for an integration
|
|
255
250
|
* These are serverless framework template function definitions
|
|
256
251
|
*/
|
|
257
|
-
async createFunctionDefinitions(integration, functionPackageConfig, result
|
|
252
|
+
async createFunctionDefinitions(integration, functionPackageConfig, result) {
|
|
258
253
|
const integrationName = integration.Definition.name;
|
|
259
254
|
|
|
260
255
|
// Add webhook handler if enabled (BEFORE catch-all proxy route)
|
|
@@ -266,9 +261,9 @@ class IntegrationBuilder extends InfrastructureBuilder {
|
|
|
266
261
|
|
|
267
262
|
result.functions[webhookFunctionName] = {
|
|
268
263
|
handler: `node_modules/@friggframework/core/handlers/routers/integration-webhook-routers.handlers.${integrationName}Webhook.handler`,
|
|
269
|
-
...(usePrismaLayer && { layers: [{ Ref: 'PrismaLambdaLayer' }] }),
|
|
270
264
|
skipEsbuild: true, // Nested exports in node_modules - skip esbuild bundling
|
|
271
265
|
package: functionPackageConfig,
|
|
266
|
+
layers: [{ Ref: 'PrismaLambdaLayer' }], // Webhook handlers need Prisma for credential lookups
|
|
272
267
|
events: [
|
|
273
268
|
{
|
|
274
269
|
httpApi: {
|
|
@@ -290,9 +285,9 @@ class IntegrationBuilder extends InfrastructureBuilder {
|
|
|
290
285
|
// Create HTTP API handler for integration (catch-all route AFTER webhooks)
|
|
291
286
|
result.functions[integrationName] = {
|
|
292
287
|
handler: `node_modules/@friggframework/core/handlers/routers/integration-defined-routers.handlers.${integrationName}.handler`,
|
|
293
|
-
...(usePrismaLayer && { layers: [{ Ref: 'PrismaLambdaLayer' }] }),
|
|
294
288
|
skipEsbuild: true, // Nested exports in node_modules - skip esbuild bundling
|
|
295
289
|
package: functionPackageConfig,
|
|
290
|
+
layers: [{ Ref: 'PrismaLambdaLayer' }], // HTTP handlers need Prisma for integration queries
|
|
296
291
|
events: [
|
|
297
292
|
{
|
|
298
293
|
httpApi: {
|
|
@@ -308,9 +303,9 @@ class IntegrationBuilder extends InfrastructureBuilder {
|
|
|
308
303
|
const queueWorkerName = `${integrationName}QueueWorker`;
|
|
309
304
|
result.functions[queueWorkerName] = {
|
|
310
305
|
handler: `node_modules/@friggframework/core/handlers/workers/integration-defined-workers.handlers.${integrationName}.queueWorker`,
|
|
311
|
-
...(usePrismaLayer && { layers: [{ Ref: 'PrismaLambdaLayer' }] }),
|
|
312
306
|
skipEsbuild: true, // Nested exports in node_modules - skip esbuild bundling
|
|
313
307
|
package: functionPackageConfig,
|
|
308
|
+
layers: [{ Ref: 'PrismaLambdaLayer' }], // Queue workers need Prisma for database operations
|
|
314
309
|
reservedConcurrency: 5,
|
|
315
310
|
events: [
|
|
316
311
|
{
|
|
@@ -589,5 +589,79 @@ describe('IntegrationBuilder', () => {
|
|
|
589
589
|
expect(result.functions.testWebhook.package.exclude).toContain('node_modules/@prisma/**');
|
|
590
590
|
});
|
|
591
591
|
});
|
|
592
|
+
|
|
593
|
+
describe('Prisma Layer Configuration', () => {
|
|
594
|
+
it('should attach Prisma Lambda layer to queue worker functions', async () => {
|
|
595
|
+
const appDefinition = {
|
|
596
|
+
integrations: [
|
|
597
|
+
{ Definition: { name: 'hubspot' } },
|
|
598
|
+
],
|
|
599
|
+
};
|
|
600
|
+
|
|
601
|
+
const result = await integrationBuilder.build(appDefinition, {});
|
|
602
|
+
|
|
603
|
+
// Queue workers need Prisma layer for database operations
|
|
604
|
+
expect(result.functions.hubspotQueueWorker.layers).toEqual([
|
|
605
|
+
{ Ref: 'PrismaLambdaLayer' }
|
|
606
|
+
]);
|
|
607
|
+
});
|
|
608
|
+
|
|
609
|
+
it('should attach Prisma layer to multiple queue workers', async () => {
|
|
610
|
+
const appDefinition = {
|
|
611
|
+
integrations: [
|
|
612
|
+
{ Definition: { name: 'hubspot' } },
|
|
613
|
+
{ Definition: { name: 'salesforce' } },
|
|
614
|
+
{ Definition: { name: 'slack' } },
|
|
615
|
+
],
|
|
616
|
+
};
|
|
617
|
+
|
|
618
|
+
const result = await integrationBuilder.build(appDefinition, {});
|
|
619
|
+
|
|
620
|
+
expect(result.functions.hubspotQueueWorker.layers).toEqual([
|
|
621
|
+
{ Ref: 'PrismaLambdaLayer' }
|
|
622
|
+
]);
|
|
623
|
+
expect(result.functions.salesforceQueueWorker.layers).toEqual([
|
|
624
|
+
{ Ref: 'PrismaLambdaLayer' }
|
|
625
|
+
]);
|
|
626
|
+
expect(result.functions.slackQueueWorker.layers).toEqual([
|
|
627
|
+
{ Ref: 'PrismaLambdaLayer' }
|
|
628
|
+
]);
|
|
629
|
+
});
|
|
630
|
+
|
|
631
|
+
it('should attach Prisma layer to HTTP handlers for database access', async () => {
|
|
632
|
+
const appDefinition = {
|
|
633
|
+
integrations: [
|
|
634
|
+
{ Definition: { name: 'stripe' } },
|
|
635
|
+
],
|
|
636
|
+
};
|
|
637
|
+
|
|
638
|
+
const result = await integrationBuilder.build(appDefinition, {});
|
|
639
|
+
|
|
640
|
+
// HTTP handlers also need Prisma for integration queries
|
|
641
|
+
expect(result.functions.stripe.layers).toEqual([
|
|
642
|
+
{ Ref: 'PrismaLambdaLayer' }
|
|
643
|
+
]);
|
|
644
|
+
});
|
|
645
|
+
|
|
646
|
+
it('should attach Prisma layer to webhook handlers', async () => {
|
|
647
|
+
const appDefinition = {
|
|
648
|
+
integrations: [
|
|
649
|
+
{
|
|
650
|
+
Definition: {
|
|
651
|
+
name: 'hubspot',
|
|
652
|
+
webhooks: true,
|
|
653
|
+
}
|
|
654
|
+
},
|
|
655
|
+
],
|
|
656
|
+
};
|
|
657
|
+
|
|
658
|
+
const result = await integrationBuilder.build(appDefinition, {});
|
|
659
|
+
|
|
660
|
+
// Webhook handlers need Prisma for credential lookups
|
|
661
|
+
expect(result.functions.hubspotWebhook.layers).toEqual([
|
|
662
|
+
{ Ref: 'PrismaLambdaLayer' }
|
|
663
|
+
]);
|
|
664
|
+
});
|
|
665
|
+
});
|
|
592
666
|
});
|
|
593
667
|
|
|
@@ -16,20 +16,18 @@ const { buildEnvironment } = require('../environment-builder');
|
|
|
16
16
|
* Frigg applications need, including:
|
|
17
17
|
* - Core Lambda functions (auth, user, health, dbMigrate)
|
|
18
18
|
* - Error handling infrastructure (SQS, SNS, CloudWatch)
|
|
19
|
-
* - Prisma Lambda Layer
|
|
19
|
+
* - Prisma Lambda Layer
|
|
20
20
|
* - Base plugins and esbuild configuration
|
|
21
21
|
*
|
|
22
22
|
* @param {Object} AppDefinition - Application definition
|
|
23
23
|
* @param {Object} appEnvironmentVars - Environment variables from app definition
|
|
24
24
|
* @param {Object} discoveredResources - AWS resources discovered during build
|
|
25
|
-
* @param {boolean} usePrismaLayer - Whether to use Prisma Lambda Layer (default true)
|
|
26
25
|
* @returns {Object} Base serverless definition
|
|
27
26
|
*/
|
|
28
27
|
function createBaseDefinition(
|
|
29
28
|
AppDefinition,
|
|
30
29
|
appEnvironmentVars,
|
|
31
|
-
discoveredResources
|
|
32
|
-
usePrismaLayer = true
|
|
30
|
+
discoveredResources
|
|
33
31
|
) {
|
|
34
32
|
const region = process.env.AWS_REGION || 'us-east-1';
|
|
35
33
|
|
|
@@ -44,13 +42,11 @@ function createBaseDefinition(
|
|
|
44
42
|
: []),
|
|
45
43
|
],
|
|
46
44
|
exclude: [
|
|
47
|
-
//
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
'node_modules/@friggframework/core/generated/**',
|
|
53
|
-
] : []),
|
|
45
|
+
// Exclude Prisma (provided via Lambda Layer)
|
|
46
|
+
'node_modules/@prisma/**',
|
|
47
|
+
'node_modules/.prisma/**',
|
|
48
|
+
'node_modules/prisma/**',
|
|
49
|
+
'node_modules/@friggframework/core/generated/**',
|
|
54
50
|
|
|
55
51
|
// Exclude AWS SDK (provided by Lambda runtime)
|
|
56
52
|
'node_modules/aws-sdk/**',
|
|
@@ -222,12 +218,9 @@ function createBaseDefinition(
|
|
|
222
218
|
external: [
|
|
223
219
|
'@aws-sdk/*',
|
|
224
220
|
'aws-sdk',
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
'prisma',
|
|
229
|
-
'.prisma/*',
|
|
230
|
-
] : []),
|
|
221
|
+
'@prisma/client',
|
|
222
|
+
'prisma',
|
|
223
|
+
'.prisma/*',
|
|
231
224
|
],
|
|
232
225
|
packager: 'npm',
|
|
233
226
|
keepNames: true,
|
|
@@ -235,11 +228,8 @@ function createBaseDefinition(
|
|
|
235
228
|
exclude: [
|
|
236
229
|
'aws-sdk',
|
|
237
230
|
'@aws-sdk/*',
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
'@prisma/client',
|
|
241
|
-
'prisma',
|
|
242
|
-
] : []),
|
|
231
|
+
'@prisma/client',
|
|
232
|
+
'prisma',
|
|
243
233
|
],
|
|
244
234
|
},
|
|
245
235
|
'serverless-offline': {
|
|
@@ -262,7 +252,7 @@ function createBaseDefinition(
|
|
|
262
252
|
functions: {
|
|
263
253
|
auth: {
|
|
264
254
|
handler: 'node_modules/@friggframework/core/handlers/routers/auth.handler',
|
|
265
|
-
|
|
255
|
+
layers: [{ Ref: 'PrismaLambdaLayer' }],
|
|
266
256
|
skipEsbuild: true, // Handlers in node_modules don't need bundling
|
|
267
257
|
package: skipEsbuildPackageConfig,
|
|
268
258
|
events: [
|
|
@@ -278,14 +268,14 @@ function createBaseDefinition(
|
|
|
278
268
|
},
|
|
279
269
|
user: {
|
|
280
270
|
handler: 'node_modules/@friggframework/core/handlers/routers/user.handler',
|
|
281
|
-
|
|
271
|
+
layers: [{ Ref: 'PrismaLambdaLayer' }],
|
|
282
272
|
skipEsbuild: true, // Handlers in node_modules don't need bundling
|
|
283
273
|
package: skipEsbuildPackageConfig,
|
|
284
274
|
events: [{ httpApi: { path: '/user/{proxy+}', method: 'ANY' } }],
|
|
285
275
|
},
|
|
286
276
|
health: {
|
|
287
277
|
handler: 'node_modules/@friggframework/core/handlers/routers/health.handler',
|
|
288
|
-
|
|
278
|
+
layers: [{ Ref: 'PrismaLambdaLayer' }],
|
|
289
279
|
skipEsbuild: true, // Handlers in node_modules don't need bundling
|
|
290
280
|
package: skipEsbuildPackageConfig,
|
|
291
281
|
events: [
|
|
@@ -296,7 +286,7 @@ function createBaseDefinition(
|
|
|
296
286
|
// Note: dbMigrate removed - MigrationBuilder now handles migration infrastructure
|
|
297
287
|
// See: packages/devtools/infrastructure/domains/database/migration-builder.js
|
|
298
288
|
},
|
|
299
|
-
layers:
|
|
289
|
+
layers: {
|
|
300
290
|
prisma: {
|
|
301
291
|
path: 'layers/prisma',
|
|
302
292
|
name: '${self:service}-prisma-${sls:stage}',
|
|
@@ -304,7 +294,7 @@ function createBaseDefinition(
|
|
|
304
294
|
compatibleRuntimes: ['nodejs20.x', 'nodejs22.x'],
|
|
305
295
|
retain: false,
|
|
306
296
|
},
|
|
307
|
-
}
|
|
297
|
+
},
|
|
308
298
|
resources: {
|
|
309
299
|
Resources: {
|
|
310
300
|
InternalErrorQueue: {
|
|
@@ -243,79 +243,6 @@ describe('Base Definition Factory', () => {
|
|
|
243
243
|
expect(result.useDotenv).toBeDefined();
|
|
244
244
|
expect(typeof result.useDotenv).toBe('boolean');
|
|
245
245
|
});
|
|
246
|
-
|
|
247
|
-
describe('usePrismaLayer configuration', () => {
|
|
248
|
-
it('should include Prisma layer by default (usePrismaLayer=true)', () => {
|
|
249
|
-
const result = createBaseDefinition({}, {}, {}, true);
|
|
250
|
-
|
|
251
|
-
// Layer definition should exist
|
|
252
|
-
expect(result.layers.prisma).toBeDefined();
|
|
253
|
-
expect(result.layers.prisma.path).toBe('layers/prisma');
|
|
254
|
-
|
|
255
|
-
// Functions should reference the layer
|
|
256
|
-
expect(result.functions.auth.layers).toEqual([{ Ref: 'PrismaLambdaLayer' }]);
|
|
257
|
-
expect(result.functions.user.layers).toEqual([{ Ref: 'PrismaLambdaLayer' }]);
|
|
258
|
-
expect(result.functions.health.layers).toEqual([{ Ref: 'PrismaLambdaLayer' }]);
|
|
259
|
-
|
|
260
|
-
// Prisma should be excluded from packages
|
|
261
|
-
expect(result.functions.auth.package.exclude).toEqual(
|
|
262
|
-
expect.arrayContaining([
|
|
263
|
-
'node_modules/@prisma/**',
|
|
264
|
-
'node_modules/.prisma/**',
|
|
265
|
-
'node_modules/prisma/**',
|
|
266
|
-
'node_modules/@friggframework/core/generated/**',
|
|
267
|
-
])
|
|
268
|
-
);
|
|
269
|
-
|
|
270
|
-
// Prisma should be external in esbuild
|
|
271
|
-
expect(result.custom.esbuild.external).toContain('@prisma/client');
|
|
272
|
-
expect(result.custom.esbuild.external).toContain('prisma');
|
|
273
|
-
expect(result.custom.esbuild.exclude).toContain('@prisma/client');
|
|
274
|
-
expect(result.custom.esbuild.exclude).toContain('prisma');
|
|
275
|
-
});
|
|
276
|
-
|
|
277
|
-
it('should NOT include Prisma layer when usePrismaLayer=false', () => {
|
|
278
|
-
const result = createBaseDefinition({}, {}, {}, false);
|
|
279
|
-
|
|
280
|
-
// Layer definition should NOT exist
|
|
281
|
-
expect(result.layers).toEqual({});
|
|
282
|
-
|
|
283
|
-
// Functions should NOT have layer references
|
|
284
|
-
expect(result.functions.auth.layers).toBeUndefined();
|
|
285
|
-
expect(result.functions.user.layers).toBeUndefined();
|
|
286
|
-
expect(result.functions.health.layers).toBeUndefined();
|
|
287
|
-
});
|
|
288
|
-
|
|
289
|
-
it('should bundle Prisma with functions when usePrismaLayer=false', () => {
|
|
290
|
-
const result = createBaseDefinition({}, {}, {}, false);
|
|
291
|
-
|
|
292
|
-
// Prisma should NOT be excluded from packages
|
|
293
|
-
expect(result.functions.auth.package.exclude).not.toEqual(
|
|
294
|
-
expect.arrayContaining([
|
|
295
|
-
'node_modules/@prisma/**',
|
|
296
|
-
'node_modules/.prisma/**',
|
|
297
|
-
'node_modules/prisma/**',
|
|
298
|
-
'node_modules/@friggframework/core/generated/**',
|
|
299
|
-
])
|
|
300
|
-
);
|
|
301
|
-
|
|
302
|
-
// Prisma should NOT be external in esbuild
|
|
303
|
-
expect(result.custom.esbuild.external).not.toContain('@prisma/client');
|
|
304
|
-
expect(result.custom.esbuild.external).not.toContain('prisma');
|
|
305
|
-
expect(result.custom.esbuild.external).not.toContain('.prisma/*');
|
|
306
|
-
expect(result.custom.esbuild.exclude).not.toContain('@prisma/client');
|
|
307
|
-
expect(result.custom.esbuild.exclude).not.toContain('prisma');
|
|
308
|
-
});
|
|
309
|
-
|
|
310
|
-
it('should default to usePrismaLayer=true when parameter not provided', () => {
|
|
311
|
-
// Call without 4th parameter
|
|
312
|
-
const result = createBaseDefinition({}, {}, {});
|
|
313
|
-
|
|
314
|
-
// Should behave as if usePrismaLayer=true
|
|
315
|
-
expect(result.layers.prisma).toBeDefined();
|
|
316
|
-
expect(result.functions.auth.layers).toEqual([{ Ref: 'PrismaLambdaLayer' }]);
|
|
317
|
-
});
|
|
318
|
-
});
|
|
319
246
|
});
|
|
320
247
|
});
|
|
321
248
|
|
|
@@ -32,15 +32,8 @@ const { validateAndCleanPlugins, validatePackagingConfiguration } = require('./d
|
|
|
32
32
|
const composeServerlessDefinition = async (AppDefinition) => {
|
|
33
33
|
console.log('🏗️ Composing serverless definition with domain builders...');
|
|
34
34
|
|
|
35
|
-
//
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
// Ensure Prisma layer exists only if using layer mode
|
|
39
|
-
if (usePrismaLayer) {
|
|
40
|
-
await ensurePrismaLayerExists(AppDefinition.database || {});
|
|
41
|
-
} else {
|
|
42
|
-
console.log('📦 Skipping Prisma Lambda Layer (usePrismaLambdaLayer=false - will bundle with functions)');
|
|
43
|
-
}
|
|
35
|
+
// Ensure Prisma layer exists (minimal, runtime only)
|
|
36
|
+
await ensurePrismaLayerExists(AppDefinition.database || {});
|
|
44
37
|
|
|
45
38
|
// Create orchestrator with all domain builders
|
|
46
39
|
const orchestrator = new BuilderOrchestrator([
|
|
@@ -62,8 +55,7 @@ const composeServerlessDefinition = async (AppDefinition) => {
|
|
|
62
55
|
const definition = createBaseDefinition(
|
|
63
56
|
AppDefinition,
|
|
64
57
|
appEnvironmentVars,
|
|
65
|
-
discoveredResources
|
|
66
|
-
usePrismaLayer
|
|
58
|
+
discoveredResources
|
|
67
59
|
);
|
|
68
60
|
|
|
69
61
|
// Merge builder results into definition
|
|
@@ -10,8 +10,8 @@
|
|
|
10
10
|
* The CLI is only needed for migrations and is packaged separately with the dbMigrate function.
|
|
11
11
|
*
|
|
12
12
|
* The layer is configured based on AppDefinition database settings:
|
|
13
|
-
* - PostgreSQL: Includes PostgreSQL client + query engine
|
|
14
|
-
* - MongoDB: Includes MongoDB client + query engine
|
|
13
|
+
* - PostgreSQL: Includes PostgreSQL client + query engine + migrations
|
|
14
|
+
* - MongoDB: Includes MongoDB client + query engine + migrations (if needed)
|
|
15
15
|
* - Defaults to PostgreSQL only if not specified
|
|
16
16
|
*
|
|
17
17
|
* Usage:
|
|
@@ -22,7 +22,11 @@
|
|
|
22
22
|
* layers/prisma/nodejs/node_modules/
|
|
23
23
|
* ├── @prisma/client (runtime only, ~10-15MB)
|
|
24
24
|
* ├── generated/prisma-postgresql (if PostgreSQL enabled)
|
|
25
|
+
* │ ├── schema.prisma
|
|
26
|
+
* │ └── migrations/
|
|
25
27
|
* └── generated/prisma-mongodb (if MongoDB enabled)
|
|
28
|
+
* ├── schema.prisma
|
|
29
|
+
* └── migrations/
|
|
26
30
|
*
|
|
27
31
|
* See: LAMBDA-LAYER-PRISMA.md for complete documentation
|
|
28
32
|
*/
|
|
@@ -85,6 +89,27 @@ function getGeneratedClientPackages(databaseConfig = {}) {
|
|
|
85
89
|
return packages;
|
|
86
90
|
}
|
|
87
91
|
|
|
92
|
+
function getMigrationsPackages(clientPackages) {
|
|
93
|
+
return clientPackages.map(pkg => ({
|
|
94
|
+
dbType: pkg.replace('generated/prisma-', ''),
|
|
95
|
+
clientPackage: pkg
|
|
96
|
+
}));
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function getMigrationSourcePath(searchPaths, dbType) {
|
|
100
|
+
for (const searchPath of searchPaths) {
|
|
101
|
+
const candidatePath = path.join(searchPath, `prisma-${dbType}`, 'migrations');
|
|
102
|
+
if (fs.existsSync(candidatePath)) {
|
|
103
|
+
return candidatePath;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function getMigrationDestinationPath(layerNodeModules, clientPackage) {
|
|
110
|
+
return path.join(layerNodeModules, clientPackage, 'migrations');
|
|
111
|
+
}
|
|
112
|
+
|
|
88
113
|
// Configuration
|
|
89
114
|
// Script runs from integration project root (e.g., backend/)
|
|
90
115
|
// and reads Prisma packages from @friggframework/core
|
|
@@ -299,11 +324,47 @@ async function copyPrismaPackages(clientPackages) {
|
|
|
299
324
|
logSuccess(`Copied ${copiedCount} generated client packages from @friggframework/core`);
|
|
300
325
|
}
|
|
301
326
|
|
|
327
|
+
async function copyMigrations(clientPackages) {
|
|
328
|
+
logStep(5, 'Copying migrations from @friggframework/core');
|
|
329
|
+
|
|
330
|
+
const workspaceNodeModules = path.join(path.dirname(CORE_PACKAGE_PATH), '..');
|
|
331
|
+
const searchPaths = [
|
|
332
|
+
path.join(CORE_PACKAGE_PATH, 'node_modules'),
|
|
333
|
+
path.join(PROJECT_ROOT, 'node_modules'),
|
|
334
|
+
workspaceNodeModules,
|
|
335
|
+
CORE_PACKAGE_PATH,
|
|
336
|
+
];
|
|
337
|
+
|
|
338
|
+
const migrations = getMigrationsPackages(clientPackages);
|
|
339
|
+
let copiedCount = 0;
|
|
340
|
+
|
|
341
|
+
for (const { dbType, clientPackage } of migrations) {
|
|
342
|
+
const sourcePath = getMigrationSourcePath(searchPaths, dbType);
|
|
343
|
+
|
|
344
|
+
if (sourcePath) {
|
|
345
|
+
const destPath = getMigrationDestinationPath(LAYER_NODE_MODULES, clientPackage);
|
|
346
|
+
await fs.copy(sourcePath, destPath, { dereference: true });
|
|
347
|
+
|
|
348
|
+
const fromLocation = sourcePath.includes('@friggframework/core/prisma')
|
|
349
|
+
? 'core package'
|
|
350
|
+
: 'workspace';
|
|
351
|
+
logSuccess(`Copied migrations for ${dbType} (from ${fromLocation})`);
|
|
352
|
+
copiedCount++;
|
|
353
|
+
} else {
|
|
354
|
+
logWarning(`Migrations not found for ${dbType} - this may cause migration failures`);
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
if (copiedCount > 0) {
|
|
359
|
+
logSuccess(`Copied migrations for ${copiedCount} database ${copiedCount === 1 ? 'type' : 'types'}`);
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
|
|
302
363
|
/**
|
|
303
364
|
* Remove unnecessary files to reduce layer size
|
|
304
365
|
*/
|
|
305
366
|
async function removeUnnecessaryFiles() {
|
|
306
|
-
logStep(
|
|
367
|
+
logStep(6, 'Removing unnecessary files (source maps, docs, tests)');
|
|
307
368
|
|
|
308
369
|
let removedCount = 0;
|
|
309
370
|
let totalSize = 0;
|
|
@@ -341,7 +402,7 @@ async function removeUnnecessaryFiles() {
|
|
|
341
402
|
* Remove non-rhel engine binaries to reduce layer size
|
|
342
403
|
*/
|
|
343
404
|
async function removeNonRhelBinaries() {
|
|
344
|
-
logStep(
|
|
405
|
+
logStep(7, 'Removing non-rhel engine binaries');
|
|
345
406
|
|
|
346
407
|
let removedCount = 0;
|
|
347
408
|
let totalSize = 0;
|
|
@@ -380,7 +441,7 @@ async function removeNonRhelBinaries() {
|
|
|
380
441
|
* @param {Array} expectedClients - List of client packages that should have binaries
|
|
381
442
|
*/
|
|
382
443
|
async function verifyRhelBinaries(expectedClients) {
|
|
383
|
-
logStep(
|
|
444
|
+
logStep(8, 'Verifying rhel-openssl-3.0.x binaries are present');
|
|
384
445
|
|
|
385
446
|
try {
|
|
386
447
|
const findCmd = `find "${LAYER_NODE_MODULES}" -name "*rhel-openssl-3.0.x*" 2>/dev/null || true`;
|
|
@@ -414,7 +475,7 @@ async function verifyRhelBinaries(expectedClients) {
|
|
|
414
475
|
* @param {Array} clientPackages - Generated client packages that were included
|
|
415
476
|
*/
|
|
416
477
|
async function verifyLayerStructure(clientPackages) {
|
|
417
|
-
logStep(
|
|
478
|
+
logStep(9, 'Verifying layer structure (runtime only)');
|
|
418
479
|
|
|
419
480
|
const requiredPaths = [
|
|
420
481
|
'@prisma/client/runtime',
|
|
@@ -426,6 +487,11 @@ async function verifyLayerStructure(clientPackages) {
|
|
|
426
487
|
requiredPaths.push(`${pkg}/schema.prisma`);
|
|
427
488
|
}
|
|
428
489
|
|
|
490
|
+
// Add migrations directory for each included client
|
|
491
|
+
for (const pkg of clientPackages) {
|
|
492
|
+
requiredPaths.push(`${pkg}/migrations/migration_lock.toml`);
|
|
493
|
+
}
|
|
494
|
+
|
|
429
495
|
// Verify CLI is NOT present (keeps layer small)
|
|
430
496
|
const forbiddenPaths = [
|
|
431
497
|
'prisma/build',
|
|
@@ -463,7 +529,7 @@ async function verifyLayerStructure(clientPackages) {
|
|
|
463
529
|
* Calculate and display final layer size
|
|
464
530
|
*/
|
|
465
531
|
async function displayLayerSummary() {
|
|
466
|
-
logStep(
|
|
532
|
+
logStep(10, 'Layer build summary');
|
|
467
533
|
|
|
468
534
|
const layerSizeMB = getDirectorySize(LAYER_OUTPUT_PATH);
|
|
469
535
|
|
|
@@ -514,6 +580,7 @@ async function buildPrismaLayer(databaseConfig = {}) {
|
|
|
514
580
|
await createLayerStructure();
|
|
515
581
|
await installPrismaPackages(); // Install runtime client only (NO CLI)
|
|
516
582
|
await copyPrismaPackages(clientPackages); // Copy generated clients from core
|
|
583
|
+
await copyMigrations(clientPackages); // Copy migrations from core
|
|
517
584
|
await removeUnnecessaryFiles(); // Remove source maps, docs, tests (37MB+)
|
|
518
585
|
await removeNonRhelBinaries(); // Remove non-Linux binaries
|
|
519
586
|
await verifyRhelBinaries(clientPackages); // Verify query engines present
|
|
@@ -550,4 +617,10 @@ if (require.main === module) {
|
|
|
550
617
|
.catch(() => process.exit(1));
|
|
551
618
|
}
|
|
552
619
|
|
|
553
|
-
module.exports = {
|
|
620
|
+
module.exports = {
|
|
621
|
+
buildPrismaLayer,
|
|
622
|
+
getGeneratedClientPackages,
|
|
623
|
+
getMigrationsPackages,
|
|
624
|
+
getMigrationSourcePath,
|
|
625
|
+
getMigrationDestinationPath
|
|
626
|
+
};
|
|
@@ -3,7 +3,12 @@
|
|
|
3
3
|
* Validates database client selection logic
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
const {
|
|
6
|
+
const {
|
|
7
|
+
getGeneratedClientPackages,
|
|
8
|
+
getMigrationsPackages,
|
|
9
|
+
getMigrationSourcePath,
|
|
10
|
+
getMigrationDestinationPath
|
|
11
|
+
} = require('./build-prisma-layer');
|
|
7
12
|
|
|
8
13
|
// Mock the log function
|
|
9
14
|
jest.mock('./build-prisma-layer', () => {
|
|
@@ -11,6 +16,9 @@ jest.mock('./build-prisma-layer', () => {
|
|
|
11
16
|
return {
|
|
12
17
|
...actual,
|
|
13
18
|
getGeneratedClientPackages: actual.getGeneratedClientPackages,
|
|
19
|
+
getMigrationsPackages: actual.getMigrationsPackages,
|
|
20
|
+
getMigrationSourcePath: actual.getMigrationSourcePath,
|
|
21
|
+
getMigrationDestinationPath: actual.getMigrationDestinationPath,
|
|
14
22
|
};
|
|
15
23
|
});
|
|
16
24
|
|
|
@@ -99,4 +107,48 @@ describe('getGeneratedClientPackages()', () => {
|
|
|
99
107
|
});
|
|
100
108
|
});
|
|
101
109
|
|
|
110
|
+
describe('getMigrationsPackages()', () => {
|
|
111
|
+
it('should extract database types from client packages', () => {
|
|
112
|
+
const clientPackages = ['generated/prisma-postgresql', 'generated/prisma-mongodb'];
|
|
113
|
+
const migrations = getMigrationsPackages(clientPackages);
|
|
102
114
|
|
|
115
|
+
expect(migrations).toEqual([
|
|
116
|
+
{ dbType: 'postgresql', clientPackage: 'generated/prisma-postgresql' },
|
|
117
|
+
{ dbType: 'mongodb', clientPackage: 'generated/prisma-mongodb' }
|
|
118
|
+
]);
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
it('should handle single client package', () => {
|
|
122
|
+
const clientPackages = ['generated/prisma-postgresql'];
|
|
123
|
+
const migrations = getMigrationsPackages(clientPackages);
|
|
124
|
+
|
|
125
|
+
expect(migrations).toEqual([
|
|
126
|
+
{ dbType: 'postgresql', clientPackage: 'generated/prisma-postgresql' }
|
|
127
|
+
]);
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
it('should handle empty array', () => {
|
|
131
|
+
const migrations = getMigrationsPackages([]);
|
|
132
|
+
expect(migrations).toEqual([]);
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
describe('getMigrationSourcePath()', () => {
|
|
137
|
+
it('should return correct source path for database type', () => {
|
|
138
|
+
const searchPaths = ['/workspace/packages/core'];
|
|
139
|
+
const dbType = 'postgresql';
|
|
140
|
+
|
|
141
|
+
const sourcePath = getMigrationSourcePath(searchPaths, dbType);
|
|
142
|
+
expect(sourcePath).toBe('/workspace/packages/core/prisma-postgresql/migrations');
|
|
143
|
+
});
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
describe('getMigrationDestinationPath()', () => {
|
|
147
|
+
it('should return correct destination path for client package', () => {
|
|
148
|
+
const layerNodeModules = '/layers/prisma/nodejs/node_modules';
|
|
149
|
+
const clientPackage = 'generated/prisma-postgresql';
|
|
150
|
+
|
|
151
|
+
const destPath = getMigrationDestinationPath(layerNodeModules, clientPackage);
|
|
152
|
+
expect(destPath).toBe('/layers/prisma/nodejs/node_modules/generated/prisma-postgresql/migrations');
|
|
153
|
+
});
|
|
154
|
+
});
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const fs = require('fs-extra');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
|
|
6
|
+
const PROJECT_ROOT = process.cwd();
|
|
7
|
+
const LAYER_PATH = path.join(PROJECT_ROOT, 'layers/prisma/nodejs/node_modules');
|
|
8
|
+
|
|
9
|
+
async function verifyLayerStructure() {
|
|
10
|
+
console.log('Verifying Prisma layer structure...\n');
|
|
11
|
+
|
|
12
|
+
const checks = [
|
|
13
|
+
{
|
|
14
|
+
name: 'PostgreSQL schema',
|
|
15
|
+
path: 'generated/prisma-postgresql/schema.prisma'
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
name: 'PostgreSQL migrations directory',
|
|
19
|
+
path: 'generated/prisma-postgresql/migrations'
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
name: 'PostgreSQL migration_lock.toml',
|
|
23
|
+
path: 'generated/prisma-postgresql/migrations/migration_lock.toml'
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
name: '@prisma/client runtime',
|
|
27
|
+
path: '@prisma/client/runtime'
|
|
28
|
+
}
|
|
29
|
+
];
|
|
30
|
+
|
|
31
|
+
let allPassed = true;
|
|
32
|
+
|
|
33
|
+
for (const check of checks) {
|
|
34
|
+
const fullPath = path.join(LAYER_PATH, check.path);
|
|
35
|
+
const exists = await fs.pathExists(fullPath);
|
|
36
|
+
|
|
37
|
+
if (exists) {
|
|
38
|
+
console.log(`✓ ${check.name}`);
|
|
39
|
+
} else {
|
|
40
|
+
console.log(`✗ ${check.name} (missing)`);
|
|
41
|
+
allPassed = false;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
console.log('\n');
|
|
46
|
+
|
|
47
|
+
if (allPassed) {
|
|
48
|
+
console.log('✓ All checks passed!');
|
|
49
|
+
const migrationsPath = path.join(LAYER_PATH, 'generated/prisma-postgresql/migrations');
|
|
50
|
+
const migrationFiles = await fs.readdir(migrationsPath);
|
|
51
|
+
console.log(`\nFound ${migrationFiles.length} items in migrations directory:`);
|
|
52
|
+
migrationFiles.forEach(file => {
|
|
53
|
+
console.log(` - ${file}`);
|
|
54
|
+
});
|
|
55
|
+
return 0;
|
|
56
|
+
} else {
|
|
57
|
+
console.log('✗ Some checks failed!');
|
|
58
|
+
return 1;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (require.main === module) {
|
|
63
|
+
verifyLayerStructure()
|
|
64
|
+
.then(code => process.exit(code))
|
|
65
|
+
.catch(err => {
|
|
66
|
+
console.error('Error:', err.message);
|
|
67
|
+
process.exit(1);
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
module.exports = { verifyLayerStructure };
|
|
72
|
+
|
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.
|
|
4
|
+
"version": "2.0.0--canary.499.2ef107f.0",
|
|
5
5
|
"bin": {
|
|
6
6
|
"frigg": "./frigg-cli/index.js"
|
|
7
7
|
},
|
|
@@ -16,9 +16,9 @@
|
|
|
16
16
|
"@babel/eslint-parser": "^7.18.9",
|
|
17
17
|
"@babel/parser": "^7.25.3",
|
|
18
18
|
"@babel/traverse": "^7.25.3",
|
|
19
|
-
"@friggframework/core": "2.0.0--canary.
|
|
20
|
-
"@friggframework/schemas": "2.0.0--canary.
|
|
21
|
-
"@friggframework/test": "2.0.0--canary.
|
|
19
|
+
"@friggframework/core": "2.0.0--canary.499.2ef107f.0",
|
|
20
|
+
"@friggframework/schemas": "2.0.0--canary.499.2ef107f.0",
|
|
21
|
+
"@friggframework/test": "2.0.0--canary.499.2ef107f.0",
|
|
22
22
|
"@hapi/boom": "^10.0.1",
|
|
23
23
|
"@inquirer/prompts": "^5.3.8",
|
|
24
24
|
"axios": "^1.7.2",
|
|
@@ -46,8 +46,8 @@
|
|
|
46
46
|
"validate-npm-package-name": "^5.0.0"
|
|
47
47
|
},
|
|
48
48
|
"devDependencies": {
|
|
49
|
-
"@friggframework/eslint-config": "2.0.0--canary.
|
|
50
|
-
"@friggframework/prettier-config": "2.0.0--canary.
|
|
49
|
+
"@friggframework/eslint-config": "2.0.0--canary.499.2ef107f.0",
|
|
50
|
+
"@friggframework/prettier-config": "2.0.0--canary.499.2ef107f.0",
|
|
51
51
|
"aws-sdk-client-mock": "^4.1.0",
|
|
52
52
|
"aws-sdk-client-mock-jest": "^4.1.0",
|
|
53
53
|
"jest": "^30.1.3",
|
|
@@ -79,5 +79,5 @@
|
|
|
79
79
|
"publishConfig": {
|
|
80
80
|
"access": "public"
|
|
81
81
|
},
|
|
82
|
-
"gitHead": "
|
|
82
|
+
"gitHead": "2ef107f34b011aef53330f9c5e4f79316c8fcf3b"
|
|
83
83
|
}
|