@friggframework/devtools 2.0.0--canary.490.12406f7.0 → 2.0.0--canary.490.c90f03b.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.
@@ -257,6 +257,34 @@ 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
+
260
288
  ## Usage Examples
261
289
 
262
290
  ### Basic Deployment
@@ -57,6 +57,9 @@ 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
+
60
63
  const result = {
61
64
  functions: {}, // Lambda function definitions
62
65
  resources: {},
@@ -76,7 +79,7 @@ class MigrationBuilder extends InfrastructureBuilder {
76
79
  console.log(` Queue: ${decisions.queue.ownership} - ${decisions.queue.reason}`);
77
80
 
78
81
  // Build resources based on ownership decisions
79
- await this.buildFromDecisions(decisions, appDefinition, discoveredResources, result);
82
+ await this.buildFromDecisions(decisions, appDefinition, discoveredResources, result, usePrismaLayer);
80
83
 
81
84
  console.log(`[${this.name}] ✅ Migration infrastructure configuration completed`);
82
85
  return result;
@@ -207,7 +210,7 @@ class MigrationBuilder extends InfrastructureBuilder {
207
210
  /**
208
211
  * Build migration resources based on ownership decisions
209
212
  */
210
- async buildFromDecisions(decisions, appDefinition, discoveredResources, result) {
213
+ async buildFromDecisions(decisions, appDefinition, discoveredResources, result, usePrismaLayer = true) {
211
214
  // Determine if we need to create resources or use existing ones
212
215
  const shouldCreateBucket = decisions.bucket.ownership === ResourceOwnership.STACK;
213
216
  const shouldCreateQueue = decisions.queue.ownership === ResourceOwnership.STACK;
@@ -215,12 +218,12 @@ class MigrationBuilder extends InfrastructureBuilder {
215
218
  if (shouldCreateBucket && shouldCreateQueue && !decisions.bucket.physicalId && !decisions.queue.physicalId) {
216
219
  // Create all new migration infrastructure
217
220
  console.log(' → Creating new migration infrastructure in stack');
218
- await this.createMigrationInfrastructure(appDefinition, result);
221
+ await this.createMigrationInfrastructure(appDefinition, result, usePrismaLayer);
219
222
  } else if ((decisions.bucket.ownership === ResourceOwnership.STACK && decisions.bucket.physicalId) ||
220
223
  (decisions.queue.ownership === ResourceOwnership.STACK && decisions.queue.physicalId)) {
221
224
  // Resources exist in stack - add definitions (CloudFormation idempotency)
222
225
  console.log(' → Adding migration definitions to template (existing in stack)');
223
- await this.createMigrationInfrastructure(appDefinition, result);
226
+ await this.createMigrationInfrastructure(appDefinition, result, usePrismaLayer);
224
227
  } else {
225
228
  // Use external resources
226
229
  console.log(' → Using external migration resources');
@@ -232,18 +235,21 @@ class MigrationBuilder extends InfrastructureBuilder {
232
235
  * Create Lambda function definitions for database migrations
233
236
  * Based on refactor/add-better-support-for-commands branch implementation
234
237
  */
235
- async createFunctionDefinitions(result) {
238
+ async createFunctionDefinitions(result, usePrismaLayer = true) {
236
239
  console.log(' 🔍 DEBUG: createFunctionDefinitions called');
237
240
  console.log(' 🔍 DEBUG: result.functions is:', typeof result.functions, result.functions);
238
241
  // Migration WORKER package config (needs Prisma CLI WASM files)
239
242
  const migrationWorkerPackageConfig = {
240
243
  individually: true,
241
244
  exclude: [
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)
245
+ // Conditionally exclude Prisma (only if using layer)
246
+ ...(usePrismaLayer ? [
247
+ // Exclude Prisma runtime client - it's in the Lambda Layer
248
+ 'node_modules/@prisma/client/**',
249
+ 'node_modules/.prisma/**',
250
+ 'node_modules/@friggframework/core/generated/**',
251
+ // But KEEP node_modules/prisma/** (the CLI with WASM)
252
+ ] : []),
247
253
 
248
254
  // Exclude ALL nested node_modules
249
255
  'node_modules/**/node_modules/**',
@@ -422,7 +428,7 @@ class MigrationBuilder extends InfrastructureBuilder {
422
428
  console.log(' 🔍 DEBUG: About to create dbMigrationWorker...');
423
429
  result.functions.dbMigrationWorker = {
424
430
  handler: 'node_modules/@friggframework/core/handlers/workers/db-migration.handler',
425
- layers: [{ Ref: 'PrismaLambdaLayer' }], // Use layer for Prisma client runtime
431
+ ...(usePrismaLayer && { layers: [{ Ref: 'PrismaLambdaLayer' }] }), // Conditionally use layer
426
432
  skipEsbuild: true,
427
433
  timeout: 900, // 15 minutes for long migrations
428
434
  memorySize: 1024, // Extra memory for Prisma operations
@@ -484,12 +490,12 @@ class MigrationBuilder extends InfrastructureBuilder {
484
490
  * Create migration infrastructure CloudFormation resources
485
491
  * Creates S3 bucket, SQS queue, and Lambda function definitions
486
492
  */
487
- async createMigrationInfrastructure(appDefinition, result) {
493
+ async createMigrationInfrastructure(appDefinition, result, usePrismaLayer = true) {
488
494
  console.log(' 🔍 DEBUG: createMigrationInfrastructure called');
489
495
  console.log(' 🔍 DEBUG: result object before createFunctionDefinitions:', Object.keys(result));
490
496
 
491
497
  // Create Lambda function definitions first (they reference the queue)
492
- await this.createFunctionDefinitions(result);
498
+ await this.createFunctionDefinitions(result, usePrismaLayer);
493
499
 
494
500
  console.log(' 🔍 DEBUG: result.functions after createFunctionDefinitions:', Object.keys(result.functions || {}));
495
501
 
@@ -290,5 +290,62 @@ 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
+ });
293
350
  });
294
351
 
@@ -62,6 +62,9 @@ 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
+
65
68
  const result = {
66
69
  functions: {},
67
70
  resources: {},
@@ -87,7 +90,7 @@ class IntegrationBuilder extends InfrastructureBuilder {
87
90
  });
88
91
 
89
92
  // Build resources based on ownership decisions
90
- await this.buildFromDecisions(decisions, appDefinition, result);
93
+ await this.buildFromDecisions(decisions, appDefinition, result, usePrismaLayer);
91
94
 
92
95
  console.log(`[${this.name}] ✅ Integration configuration completed`);
93
96
  return result;
@@ -142,7 +145,7 @@ class IntegrationBuilder extends InfrastructureBuilder {
142
145
  /**
143
146
  * Build integration resources based on ownership decisions
144
147
  */
145
- async buildFromDecisions(decisions, appDefinition, result) {
148
+ async buildFromDecisions(decisions, appDefinition, result, usePrismaLayer = true) {
146
149
  // Create InternalErrorQueue if ownership = STACK
147
150
  const shouldCreateInternalErrorQueue = decisions.internalErrorQueue.ownership === ResourceOwnership.STACK;
148
151
 
@@ -155,7 +158,7 @@ class IntegrationBuilder extends InfrastructureBuilder {
155
158
  }
156
159
 
157
160
  // Create Lambda function definitions and queue resources for each integration
158
- const functionPackageConfig = this.createFunctionPackageConfig();
161
+ const functionPackageConfig = this.createFunctionPackageConfig(usePrismaLayer);
159
162
 
160
163
  for (const integration of appDefinition.integrations) {
161
164
  const integrationName = integration.Definition.name;
@@ -164,7 +167,7 @@ class IntegrationBuilder extends InfrastructureBuilder {
164
167
  console.log(`\n Adding integration: ${integrationName}`);
165
168
 
166
169
  // Create Lambda function definitions (serverless template code)
167
- await this.createFunctionDefinitions(integration, functionPackageConfig, result);
170
+ await this.createFunctionDefinitions(integration, functionPackageConfig, result, usePrismaLayer);
168
171
 
169
172
  // Create or reference SQS queue based on ownership decision
170
173
  const shouldCreateQueue = queueDecision.ownership === ResourceOwnership.STACK;
@@ -182,18 +185,20 @@ class IntegrationBuilder extends InfrastructureBuilder {
182
185
  /**
183
186
  * Create function package exclusion configuration
184
187
  */
185
- createFunctionPackageConfig() {
188
+ createFunctionPackageConfig(usePrismaLayer = true) {
186
189
  return {
187
190
  exclude: [
188
191
  // Exclude AWS SDK (provided by Lambda runtime)
189
192
  'node_modules/aws-sdk/**',
190
193
  'node_modules/@aws-sdk/**',
191
194
 
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/**',
195
+ // Conditionally exclude Prisma (only if using Lambda Layer)
196
+ ...(usePrismaLayer ? [
197
+ 'node_modules/@prisma/**',
198
+ 'node_modules/.prisma/**',
199
+ 'node_modules/prisma/**',
200
+ 'node_modules/@friggframework/core/generated/**',
201
+ ] : []),
197
202
 
198
203
  // Exclude ALL nested node_modules
199
204
  'node_modules/**/node_modules/**',
@@ -249,7 +254,7 @@ class IntegrationBuilder extends InfrastructureBuilder {
249
254
  * Create Lambda function definitions for an integration
250
255
  * These are serverless framework template function definitions
251
256
  */
252
- async createFunctionDefinitions(integration, functionPackageConfig, result) {
257
+ async createFunctionDefinitions(integration, functionPackageConfig, result, usePrismaLayer = true) {
253
258
  const integrationName = integration.Definition.name;
254
259
 
255
260
  // Add webhook handler if enabled (BEFORE catch-all proxy route)
@@ -261,6 +266,7 @@ class IntegrationBuilder extends InfrastructureBuilder {
261
266
 
262
267
  result.functions[webhookFunctionName] = {
263
268
  handler: `node_modules/@friggframework/core/handlers/routers/integration-webhook-routers.handlers.${integrationName}Webhook.handler`,
269
+ ...(usePrismaLayer && { layers: [{ Ref: 'PrismaLambdaLayer' }] }),
264
270
  skipEsbuild: true, // Nested exports in node_modules - skip esbuild bundling
265
271
  package: functionPackageConfig,
266
272
  events: [
@@ -284,6 +290,7 @@ class IntegrationBuilder extends InfrastructureBuilder {
284
290
  // Create HTTP API handler for integration (catch-all route AFTER webhooks)
285
291
  result.functions[integrationName] = {
286
292
  handler: `node_modules/@friggframework/core/handlers/routers/integration-defined-routers.handlers.${integrationName}.handler`,
293
+ ...(usePrismaLayer && { layers: [{ Ref: 'PrismaLambdaLayer' }] }),
287
294
  skipEsbuild: true, // Nested exports in node_modules - skip esbuild bundling
288
295
  package: functionPackageConfig,
289
296
  events: [
@@ -301,6 +308,7 @@ class IntegrationBuilder extends InfrastructureBuilder {
301
308
  const queueWorkerName = `${integrationName}QueueWorker`;
302
309
  result.functions[queueWorkerName] = {
303
310
  handler: `node_modules/@friggframework/core/handlers/workers/integration-defined-workers.handlers.${integrationName}.queueWorker`,
311
+ ...(usePrismaLayer && { layers: [{ Ref: 'PrismaLambdaLayer' }] }),
304
312
  skipEsbuild: true, // Nested exports in node_modules - skip esbuild bundling
305
313
  package: functionPackageConfig,
306
314
  reservedConcurrency: 5,
@@ -78,8 +78,20 @@ class KmsBuilder extends InfrastructureBuilder {
78
78
  const resolver = new KmsResourceResolver();
79
79
  const decisions = resolver.resolveAll(appDefinition, discovery);
80
80
 
81
+ // Check if external key exists (for accurate logging)
82
+ const externalKmsKey = discoveredResources?.defaultKmsKeyId ||
83
+ discoveredResources?.kmsKeyArn ||
84
+ discoveredResources?.kmsKeyId;
85
+ const willUseExternal = decisions.key.ownership === ResourceOwnership.STACK &&
86
+ !decisions.key.physicalId &&
87
+ externalKmsKey;
88
+
81
89
  console.log('\n 📋 Resource Ownership Decisions:');
82
- console.log(` Key: ${decisions.key.ownership} - ${decisions.key.reason}`);
90
+ if (willUseExternal) {
91
+ console.log(` Key: external - Found external KMS key (not in stack)`);
92
+ } else {
93
+ console.log(` Key: ${decisions.key.ownership} - ${decisions.key.reason}`);
94
+ }
83
95
 
84
96
  // Build resources based on ownership decisions
85
97
  await this.buildFromDecisions(decisions, appDefinition, discoveredResources, result);
@@ -247,7 +259,6 @@ class KmsBuilder extends InfrastructureBuilder {
247
259
  } else if (decisions.key.ownership === ResourceOwnership.STACK && !decisions.key.physicalId && externalKmsKey) {
248
260
  // ORPHANED KEY FIX: Key exists externally but not in stack
249
261
  // Use it as external instead of trying to create (would fail with "already exists")
250
- console.log(' ⚠️ KMS key exists externally but not in stack - using as external resource');
251
262
  console.log(` → Using external KMS key: ${externalKmsKey}`);
252
263
 
253
264
  // Format as ARN if it's just a key ID
@@ -16,18 +16,20 @@ 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 (optional, controlled by usePrismaLayer)
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)
25
26
  * @returns {Object} Base serverless definition
26
27
  */
27
28
  function createBaseDefinition(
28
29
  AppDefinition,
29
30
  appEnvironmentVars,
30
- discoveredResources
31
+ discoveredResources,
32
+ usePrismaLayer = true
31
33
  ) {
32
34
  const region = process.env.AWS_REGION || 'us-east-1';
33
35
 
@@ -42,11 +44,13 @@ function createBaseDefinition(
42
44
  : []),
43
45
  ],
44
46
  exclude: [
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/**',
47
+ // Conditionally exclude Prisma (only if using Lambda Layer)
48
+ ...(usePrismaLayer ? [
49
+ 'node_modules/@prisma/**',
50
+ 'node_modules/.prisma/**',
51
+ 'node_modules/prisma/**',
52
+ 'node_modules/@friggframework/core/generated/**',
53
+ ] : []),
50
54
 
51
55
  // Exclude AWS SDK (provided by Lambda runtime)
52
56
  'node_modules/aws-sdk/**',
@@ -218,9 +222,12 @@ function createBaseDefinition(
218
222
  external: [
219
223
  '@aws-sdk/*',
220
224
  'aws-sdk',
221
- '@prisma/client',
222
- 'prisma',
223
- '.prisma/*',
225
+ // Conditionally mark Prisma as external (only if using layer)
226
+ ...(usePrismaLayer ? [
227
+ '@prisma/client',
228
+ 'prisma',
229
+ '.prisma/*',
230
+ ] : []),
224
231
  ],
225
232
  packager: 'npm',
226
233
  keepNames: true,
@@ -228,8 +235,11 @@ function createBaseDefinition(
228
235
  exclude: [
229
236
  'aws-sdk',
230
237
  '@aws-sdk/*',
231
- '@prisma/client',
232
- 'prisma',
238
+ // Conditionally exclude Prisma (only if using layer)
239
+ ...(usePrismaLayer ? [
240
+ '@prisma/client',
241
+ 'prisma',
242
+ ] : []),
233
243
  ],
234
244
  },
235
245
  'serverless-offline': {
@@ -252,7 +262,7 @@ function createBaseDefinition(
252
262
  functions: {
253
263
  auth: {
254
264
  handler: 'node_modules/@friggframework/core/handlers/routers/auth.handler',
255
- layers: [{ Ref: 'PrismaLambdaLayer' }],
265
+ ...(usePrismaLayer && { layers: [{ Ref: 'PrismaLambdaLayer' }] }),
256
266
  skipEsbuild: true, // Handlers in node_modules don't need bundling
257
267
  package: skipEsbuildPackageConfig,
258
268
  events: [
@@ -268,14 +278,14 @@ function createBaseDefinition(
268
278
  },
269
279
  user: {
270
280
  handler: 'node_modules/@friggframework/core/handlers/routers/user.handler',
271
- layers: [{ Ref: 'PrismaLambdaLayer' }],
281
+ ...(usePrismaLayer && { layers: [{ Ref: 'PrismaLambdaLayer' }] }),
272
282
  skipEsbuild: true, // Handlers in node_modules don't need bundling
273
283
  package: skipEsbuildPackageConfig,
274
284
  events: [{ httpApi: { path: '/user/{proxy+}', method: 'ANY' } }],
275
285
  },
276
286
  health: {
277
287
  handler: 'node_modules/@friggframework/core/handlers/routers/health.handler',
278
- layers: [{ Ref: 'PrismaLambdaLayer' }],
288
+ ...(usePrismaLayer && { layers: [{ Ref: 'PrismaLambdaLayer' }] }),
279
289
  skipEsbuild: true, // Handlers in node_modules don't need bundling
280
290
  package: skipEsbuildPackageConfig,
281
291
  events: [
@@ -286,7 +296,7 @@ function createBaseDefinition(
286
296
  // Note: dbMigrate removed - MigrationBuilder now handles migration infrastructure
287
297
  // See: packages/devtools/infrastructure/domains/database/migration-builder.js
288
298
  },
289
- layers: {
299
+ layers: usePrismaLayer ? {
290
300
  prisma: {
291
301
  path: 'layers/prisma',
292
302
  name: '${self:service}-prisma-${sls:stage}',
@@ -294,7 +304,7 @@ function createBaseDefinition(
294
304
  compatibleRuntimes: ['nodejs20.x', 'nodejs22.x'],
295
305
  retain: false,
296
306
  },
297
- },
307
+ } : {},
298
308
  resources: {
299
309
  Resources: {
300
310
  InternalErrorQueue: {
@@ -243,6 +243,79 @@ 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
+ });
246
319
  });
247
320
  });
248
321
 
@@ -32,8 +32,15 @@ const { validateAndCleanPlugins, validatePackagingConfiguration } = require('./d
32
32
  const composeServerlessDefinition = async (AppDefinition) => {
33
33
  console.log('🏗️ Composing serverless definition with domain builders...');
34
34
 
35
- // Ensure Prisma layer exists (minimal, runtime only)
36
- await ensurePrismaLayerExists(AppDefinition.database || {});
35
+ // Determine if using Prisma Lambda Layer (default true)
36
+ const usePrismaLayer = AppDefinition.usePrismaLambdaLayer !== false;
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
+ }
37
44
 
38
45
  // Create orchestrator with all domain builders
39
46
  const orchestrator = new BuilderOrchestrator([
@@ -55,7 +62,8 @@ const composeServerlessDefinition = async (AppDefinition) => {
55
62
  const definition = createBaseDefinition(
56
63
  AppDefinition,
57
64
  appEnvironmentVars,
58
- discoveredResources
65
+ discoveredResources,
66
+ usePrismaLayer
59
67
  );
60
68
 
61
69
  // Merge builder results into definition
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.490.12406f7.0",
4
+ "version": "2.0.0--canary.490.c90f03b.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.490.12406f7.0",
20
- "@friggframework/schemas": "2.0.0--canary.490.12406f7.0",
21
- "@friggframework/test": "2.0.0--canary.490.12406f7.0",
19
+ "@friggframework/core": "2.0.0--canary.490.c90f03b.0",
20
+ "@friggframework/schemas": "2.0.0--canary.490.c90f03b.0",
21
+ "@friggframework/test": "2.0.0--canary.490.c90f03b.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.490.12406f7.0",
50
- "@friggframework/prettier-config": "2.0.0--canary.490.12406f7.0",
49
+ "@friggframework/eslint-config": "2.0.0--canary.490.c90f03b.0",
50
+ "@friggframework/prettier-config": "2.0.0--canary.490.c90f03b.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": "12406f722951a015ba310ba6c47854d7fda9dee2"
82
+ "gitHead": "c90f03b0bd4de843e14c8350482cfa1a3674e1fb"
83
83
  }