@friggframework/devtools 2.0.0--canary.454.29d031a.0 → 2.0.0--canary.454.1c50057.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/infrastructure/README.md
CHANGED
|
@@ -207,6 +207,56 @@ STAGE=production
|
|
|
207
207
|
SERVICE_NAME=my-frigg-app
|
|
208
208
|
```
|
|
209
209
|
|
|
210
|
+
## Lambda Layers
|
|
211
|
+
|
|
212
|
+
### Prisma Layer
|
|
213
|
+
|
|
214
|
+
The Frigg infrastructure uses a Lambda Layer to optimize Prisma deployment, reducing function sizes by ~60%.
|
|
215
|
+
|
|
216
|
+
**What's included:**
|
|
217
|
+
|
|
218
|
+
- `@prisma/client` - Prisma Client runtime
|
|
219
|
+
- `@prisma-mongodb/client` - MongoDB Prisma Client
|
|
220
|
+
- `@prisma-postgresql/client` - PostgreSQL Prisma Client
|
|
221
|
+
- `prisma` - Prisma CLI (for migrations)
|
|
222
|
+
|
|
223
|
+
**Benefits:**
|
|
224
|
+
|
|
225
|
+
- ✅ **Reduces function sizes**: From ~120MB → ~45MB per function (60% reduction)
|
|
226
|
+
- ✅ **Faster deployments**: Layer cached between deployments
|
|
227
|
+
- ✅ **Shared resources**: Prisma uploaded once (~70MB layer), shared by all functions
|
|
228
|
+
- ✅ **Improved cold starts**: Smaller packages = faster initialization
|
|
229
|
+
|
|
230
|
+
**Building the layer:**
|
|
231
|
+
|
|
232
|
+
```bash
|
|
233
|
+
cd packages/devtools
|
|
234
|
+
npm run build:prisma-layer
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
**Expected output:**
|
|
238
|
+
|
|
239
|
+
```
|
|
240
|
+
Building Prisma Lambda Layer...
|
|
241
|
+
✓ Layer built successfully (70MB)
|
|
242
|
+
Layer location: infrastructure/layers/prisma
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
**Automatic deployment:**
|
|
246
|
+
|
|
247
|
+
The layer is automatically deployed when you run `frigg deploy`. All Lambda functions reference the layer via CloudFormation.
|
|
248
|
+
|
|
249
|
+
**Troubleshooting:**
|
|
250
|
+
|
|
251
|
+
If you encounter "Module not found" errors after deployment:
|
|
252
|
+
|
|
253
|
+
```bash
|
|
254
|
+
# Verify layer is attached to function
|
|
255
|
+
aws lambda get-function-configuration \
|
|
256
|
+
--function-name your-app-dev-auth \
|
|
257
|
+
--query 'Layers[*].Arn'
|
|
258
|
+
```
|
|
259
|
+
|
|
210
260
|
## Usage Examples
|
|
211
261
|
|
|
212
262
|
### Basic Deployment
|
|
@@ -435,6 +485,7 @@ npm run test:debug
|
|
|
435
485
|
|
|
436
486
|
## Related Documentation
|
|
437
487
|
|
|
488
|
+
- [Lambda Layer for Prisma](./LAMBDA-LAYER-PRISMA.md) - Complete guide to Prisma Lambda Layer optimization
|
|
438
489
|
- [Phase 3 Deployment Guide](./PHASE3-DEPLOYMENT-GUIDE.md)
|
|
439
490
|
- [Testing Strategy](./README-TESTING.md)
|
|
440
491
|
- [AWS Discovery Troubleshooting](./AWS-DISCOVERY-TROUBLESHOOTING.md)
|
|
@@ -0,0 +1,364 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Build Prisma Lambda Layer
|
|
5
|
+
*
|
|
6
|
+
* Creates a Lambda Layer containing all Prisma packages and rhel-openssl-3.0.x binaries.
|
|
7
|
+
* This reduces individual Lambda function sizes by ~60% (120MB → 45MB).
|
|
8
|
+
*
|
|
9
|
+
* Usage:
|
|
10
|
+
* node scripts/build-prisma-layer.js
|
|
11
|
+
* npm run build:prisma-layer
|
|
12
|
+
*
|
|
13
|
+
* Output:
|
|
14
|
+
* layers/prisma/nodejs/node_modules/
|
|
15
|
+
* ├── @prisma/client
|
|
16
|
+
* ├── @prisma-mongodb/client
|
|
17
|
+
* ├── @prisma-postgresql/client
|
|
18
|
+
* └── prisma (CLI for migrations)
|
|
19
|
+
*
|
|
20
|
+
* See: LAMBDA-LAYER-PRISMA.md for complete documentation
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
const fs = require('fs-extra');
|
|
24
|
+
const path = require('path');
|
|
25
|
+
const { execSync } = require('child_process');
|
|
26
|
+
|
|
27
|
+
// Configuration
|
|
28
|
+
// Script runs from integration project root (e.g., backend/)
|
|
29
|
+
// and reads Prisma packages from @friggframework/core
|
|
30
|
+
const PROJECT_ROOT = process.cwd();
|
|
31
|
+
const CORE_PACKAGE_PATH = path.join(PROJECT_ROOT, 'node_modules/@friggframework/core');
|
|
32
|
+
const LAYER_OUTPUT_PATH = path.join(PROJECT_ROOT, 'layers/prisma');
|
|
33
|
+
const LAYER_NODE_MODULES = path.join(LAYER_OUTPUT_PATH, 'nodejs/node_modules');
|
|
34
|
+
|
|
35
|
+
// Packages to include in the layer
|
|
36
|
+
const PRISMA_PACKAGES = [
|
|
37
|
+
'@prisma/client',
|
|
38
|
+
'@prisma-mongodb/client',
|
|
39
|
+
'@prisma-postgresql/client',
|
|
40
|
+
'prisma', // CLI for migrations
|
|
41
|
+
];
|
|
42
|
+
|
|
43
|
+
// Binary patterns to remove (non-rhel)
|
|
44
|
+
const BINARY_PATTERNS_TO_REMOVE = [
|
|
45
|
+
'*darwin*',
|
|
46
|
+
'*debian*',
|
|
47
|
+
'*linux-arm*',
|
|
48
|
+
'*linux-musl*',
|
|
49
|
+
'*windows*',
|
|
50
|
+
];
|
|
51
|
+
|
|
52
|
+
// ANSI color codes for output
|
|
53
|
+
const colors = {
|
|
54
|
+
reset: '\x1b[0m',
|
|
55
|
+
bright: '\x1b[1m',
|
|
56
|
+
green: '\x1b[32m',
|
|
57
|
+
yellow: '\x1b[33m',
|
|
58
|
+
blue: '\x1b[34m',
|
|
59
|
+
red: '\x1b[31m',
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
function log(message, color = 'reset') {
|
|
63
|
+
console.log(`${colors[color]}${message}${colors.reset}`);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function logStep(step, message) {
|
|
67
|
+
log(`\n[${step}] ${message}`, 'blue');
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function logSuccess(message) {
|
|
71
|
+
log(`✓ ${message}`, 'green');
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function logWarning(message) {
|
|
75
|
+
log(`⚠ ${message}`, 'yellow');
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function logError(message) {
|
|
79
|
+
log(`✗ ${message}`, 'red');
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Get directory size in MB
|
|
84
|
+
*/
|
|
85
|
+
function getDirectorySize(dirPath) {
|
|
86
|
+
try {
|
|
87
|
+
const output = execSync(`du -sm "${dirPath}"`, { encoding: 'utf8' });
|
|
88
|
+
const size = parseInt(output.split('\t')[0], 10);
|
|
89
|
+
return size;
|
|
90
|
+
} catch (error) {
|
|
91
|
+
logWarning(`Could not calculate directory size: ${error.message}`);
|
|
92
|
+
return 0;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Clean existing layer directory
|
|
98
|
+
*/
|
|
99
|
+
async function cleanLayerDirectory() {
|
|
100
|
+
logStep(1, 'Cleaning existing layer directory');
|
|
101
|
+
|
|
102
|
+
if (await fs.pathExists(LAYER_OUTPUT_PATH)) {
|
|
103
|
+
await fs.remove(LAYER_OUTPUT_PATH);
|
|
104
|
+
logSuccess(`Removed existing layer at ${LAYER_OUTPUT_PATH}`);
|
|
105
|
+
} else {
|
|
106
|
+
log('No existing layer to clean');
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Create layer directory structure
|
|
112
|
+
*/
|
|
113
|
+
async function createLayerStructure() {
|
|
114
|
+
logStep(2, 'Creating layer directory structure');
|
|
115
|
+
|
|
116
|
+
await fs.ensureDir(LAYER_NODE_MODULES);
|
|
117
|
+
logSuccess(`Created ${LAYER_NODE_MODULES}`);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Copy Prisma packages from core to layer
|
|
122
|
+
*/
|
|
123
|
+
async function copyPrismaPackages() {
|
|
124
|
+
logStep(3, 'Copying Prisma packages from @friggframework/core');
|
|
125
|
+
|
|
126
|
+
// Prisma packages can be in:
|
|
127
|
+
// 1. node_modules/@friggframework/core/node_modules/ (if core has its own)
|
|
128
|
+
// 2. node_modules/ (if hoisted by npm/yarn to project root)
|
|
129
|
+
const coreNodeModules = path.join(CORE_PACKAGE_PATH, 'node_modules');
|
|
130
|
+
const projectNodeModules = path.join(PROJECT_ROOT, 'node_modules');
|
|
131
|
+
|
|
132
|
+
const nodeModulesPaths = [coreNodeModules, projectNodeModules];
|
|
133
|
+
|
|
134
|
+
let copiedCount = 0;
|
|
135
|
+
let missingPackages = [];
|
|
136
|
+
|
|
137
|
+
for (const pkg of PRISMA_PACKAGES) {
|
|
138
|
+
let sourcePath = null;
|
|
139
|
+
|
|
140
|
+
// Try to find package in core or root node_modules
|
|
141
|
+
for (const nodeModulesPath of nodeModulesPaths) {
|
|
142
|
+
const candidatePath = path.join(nodeModulesPath, pkg);
|
|
143
|
+
if (await fs.pathExists(candidatePath)) {
|
|
144
|
+
sourcePath = candidatePath;
|
|
145
|
+
break;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
if (sourcePath) {
|
|
150
|
+
const destPath = path.join(LAYER_NODE_MODULES, pkg);
|
|
151
|
+
await fs.copy(sourcePath, destPath, {
|
|
152
|
+
dereference: true, // Follow symlinks
|
|
153
|
+
filter: (src) => {
|
|
154
|
+
// Skip node_modules within Prisma packages
|
|
155
|
+
return !src.includes('/node_modules/node_modules/');
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
const fromLocation = sourcePath.includes('@friggframework/core/node_modules')
|
|
159
|
+
? 'core package'
|
|
160
|
+
: 'project root';
|
|
161
|
+
logSuccess(`Copied ${pkg} (from ${fromLocation})`);
|
|
162
|
+
copiedCount++;
|
|
163
|
+
} else {
|
|
164
|
+
missingPackages.push(pkg);
|
|
165
|
+
logWarning(`Package not found: ${pkg}`);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
if (missingPackages.length > 0) {
|
|
170
|
+
throw new Error(
|
|
171
|
+
`Missing Prisma packages: ${missingPackages.join(', ')}.\n` +
|
|
172
|
+
'Ensure @friggframework/core is installed with "npm install" and Prisma clients are generated.'
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
log(`\nCopied ${copiedCount}/${PRISMA_PACKAGES.length} packages`);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Remove non-rhel engine binaries to reduce layer size
|
|
181
|
+
*/
|
|
182
|
+
async function removeNonRhelBinaries() {
|
|
183
|
+
logStep(4, 'Removing non-rhel engine binaries');
|
|
184
|
+
|
|
185
|
+
let removedCount = 0;
|
|
186
|
+
let totalSize = 0;
|
|
187
|
+
|
|
188
|
+
for (const pattern of BINARY_PATTERNS_TO_REMOVE) {
|
|
189
|
+
try {
|
|
190
|
+
// Find files matching the pattern
|
|
191
|
+
const findCmd = `find "${LAYER_NODE_MODULES}" -name "${pattern}" 2>/dev/null || true`;
|
|
192
|
+
const files = execSync(findCmd, { encoding: 'utf8' })
|
|
193
|
+
.split('\n')
|
|
194
|
+
.filter(f => f.trim());
|
|
195
|
+
|
|
196
|
+
for (const file of files) {
|
|
197
|
+
if (await fs.pathExists(file)) {
|
|
198
|
+
const stats = await fs.stat(file);
|
|
199
|
+
totalSize += stats.size;
|
|
200
|
+
await fs.remove(file);
|
|
201
|
+
removedCount++;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
} catch (error) {
|
|
205
|
+
logWarning(`Error removing ${pattern}: ${error.message}`);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
if (removedCount > 0) {
|
|
210
|
+
const sizeMB = (totalSize / (1024 * 1024)).toFixed(2);
|
|
211
|
+
logSuccess(`Removed ${removedCount} non-rhel binaries (saved ${sizeMB} MB)`);
|
|
212
|
+
} else {
|
|
213
|
+
log('No non-rhel binaries found to remove');
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Verify rhel binaries are present
|
|
219
|
+
*/
|
|
220
|
+
async function verifyRhelBinaries() {
|
|
221
|
+
logStep(5, 'Verifying rhel-openssl-3.0.x binaries are present');
|
|
222
|
+
|
|
223
|
+
try {
|
|
224
|
+
const findCmd = `find "${LAYER_NODE_MODULES}" -name "*rhel-openssl-3.0.x*" 2>/dev/null || true`;
|
|
225
|
+
const rhelFiles = execSync(findCmd, { encoding: 'utf8' })
|
|
226
|
+
.split('\n')
|
|
227
|
+
.filter(f => f.trim());
|
|
228
|
+
|
|
229
|
+
if (rhelFiles.length === 0) {
|
|
230
|
+
throw new Error(
|
|
231
|
+
'No rhel-openssl-3.0.x binaries found!\n' +
|
|
232
|
+
'Check that Prisma schemas have binaryTargets = ["native", "rhel-openssl-3.0.x"]'
|
|
233
|
+
);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
logSuccess(`Found ${rhelFiles.length} rhel-openssl-3.0.x binaries`);
|
|
237
|
+
|
|
238
|
+
// Show the binaries found
|
|
239
|
+
rhelFiles.forEach(file => {
|
|
240
|
+
const relativePath = path.relative(LAYER_NODE_MODULES, file);
|
|
241
|
+
log(` - ${relativePath}`, 'reset');
|
|
242
|
+
});
|
|
243
|
+
} catch (error) {
|
|
244
|
+
throw new Error(`Binary verification failed: ${error.message}`);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Verify required files exist
|
|
250
|
+
*/
|
|
251
|
+
async function verifyLayerStructure() {
|
|
252
|
+
logStep(6, 'Verifying layer structure');
|
|
253
|
+
|
|
254
|
+
const requiredPaths = [
|
|
255
|
+
'@prisma/client/runtime',
|
|
256
|
+
'@prisma/client/index.d.ts',
|
|
257
|
+
'@prisma-mongodb/client/schema.prisma',
|
|
258
|
+
'@prisma-postgresql/client/schema.prisma',
|
|
259
|
+
'prisma/build',
|
|
260
|
+
];
|
|
261
|
+
|
|
262
|
+
let allPresent = true;
|
|
263
|
+
|
|
264
|
+
for (const requiredPath of requiredPaths) {
|
|
265
|
+
const fullPath = path.join(LAYER_NODE_MODULES, requiredPath);
|
|
266
|
+
if (await fs.pathExists(fullPath)) {
|
|
267
|
+
log(` ✓ ${requiredPath}`, 'green');
|
|
268
|
+
} else {
|
|
269
|
+
log(` ✗ ${requiredPath} (missing)`, 'red');
|
|
270
|
+
allPresent = false;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
if (!allPresent) {
|
|
275
|
+
throw new Error('Layer structure verification failed - missing required files');
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
logSuccess('All required files present');
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Calculate and display final layer size
|
|
283
|
+
*/
|
|
284
|
+
async function displayLayerSummary() {
|
|
285
|
+
logStep(7, 'Layer build summary');
|
|
286
|
+
|
|
287
|
+
const layerSizeMB = getDirectorySize(LAYER_OUTPUT_PATH);
|
|
288
|
+
|
|
289
|
+
log('\n' + '='.repeat(60), 'bright');
|
|
290
|
+
log(' Prisma Lambda Layer Built Successfully!', 'bright');
|
|
291
|
+
log('='.repeat(60), 'bright');
|
|
292
|
+
|
|
293
|
+
log(`\nLayer location: ${LAYER_OUTPUT_PATH}`, 'blue');
|
|
294
|
+
log(`Layer size: ~${layerSizeMB} MB`, 'green');
|
|
295
|
+
|
|
296
|
+
log('\nPackages included:', 'bright');
|
|
297
|
+
PRISMA_PACKAGES.forEach(pkg => {
|
|
298
|
+
log(` - ${pkg}`, 'reset');
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
log('\nNext steps:', 'bright');
|
|
302
|
+
log(' 1. Verify layer structure: ls -lah layers/prisma/nodejs/node_modules/', 'reset');
|
|
303
|
+
log(' 2. Deploy to AWS: frigg deploy --stage dev', 'reset');
|
|
304
|
+
log(' 3. Check function sizes in Lambda console (should be ~45-55MB)', 'reset');
|
|
305
|
+
|
|
306
|
+
log('\nSee LAMBDA-LAYER-PRISMA.md for complete documentation.', 'yellow');
|
|
307
|
+
log('='.repeat(60) + '\n', 'bright');
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* Main build function
|
|
312
|
+
*/
|
|
313
|
+
async function buildPrismaLayer() {
|
|
314
|
+
const startTime = Date.now();
|
|
315
|
+
|
|
316
|
+
log('\n' + '='.repeat(60), 'bright');
|
|
317
|
+
log(' Building Prisma Lambda Layer', 'bright');
|
|
318
|
+
log('='.repeat(60) + '\n', 'bright');
|
|
319
|
+
|
|
320
|
+
// Log paths
|
|
321
|
+
log(`Project root: ${PROJECT_ROOT}`, 'reset');
|
|
322
|
+
log(`Core package: ${CORE_PACKAGE_PATH}`, 'reset');
|
|
323
|
+
log(`Layer output: ${LAYER_OUTPUT_PATH}\n`, 'reset');
|
|
324
|
+
|
|
325
|
+
try {
|
|
326
|
+
await cleanLayerDirectory();
|
|
327
|
+
await createLayerStructure();
|
|
328
|
+
await copyPrismaPackages();
|
|
329
|
+
await removeNonRhelBinaries();
|
|
330
|
+
await verifyRhelBinaries();
|
|
331
|
+
await verifyLayerStructure();
|
|
332
|
+
await displayLayerSummary();
|
|
333
|
+
|
|
334
|
+
const duration = ((Date.now() - startTime) / 1000).toFixed(2);
|
|
335
|
+
log(`Build completed in ${duration}s\n`, 'green');
|
|
336
|
+
|
|
337
|
+
return { success: true, duration };
|
|
338
|
+
} catch (error) {
|
|
339
|
+
logError(`\nBuild failed: ${error.message}`);
|
|
340
|
+
|
|
341
|
+
if (error.stack) {
|
|
342
|
+
log('\nStack trace:', 'red');
|
|
343
|
+
console.error(error.stack);
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
log('\nTroubleshooting:', 'yellow');
|
|
347
|
+
log(' 1. Ensure @friggframework/core has dependencies installed', 'reset');
|
|
348
|
+
log(' 2. Run "npm run prisma:generate" in core package', 'reset');
|
|
349
|
+
log(' 3. Check that Prisma schemas have correct binaryTargets', 'reset');
|
|
350
|
+
log(' 4. See LAMBDA-LAYER-PRISMA.md for details\n', 'reset');
|
|
351
|
+
|
|
352
|
+
// Throw error instead of exit when used as module
|
|
353
|
+
throw error;
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
// Run the build when executed directly
|
|
358
|
+
if (require.main === module) {
|
|
359
|
+
buildPrismaLayer()
|
|
360
|
+
.then(() => process.exit(0))
|
|
361
|
+
.catch(() => process.exit(1));
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
module.exports = { buildPrismaLayer };
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const path = require('path');
|
|
2
2
|
const fs = require('fs');
|
|
3
3
|
const { AWSDiscovery } = require('./aws-discovery');
|
|
4
|
+
const { buildPrismaLayer } = require('./scripts/build-prisma-layer');
|
|
4
5
|
|
|
5
6
|
const shouldRunDiscovery = (AppDefinition) => {
|
|
6
7
|
console.log('⚙️ Checking FRIGG_SKIP_AWS_DISCOVERY:', process.env.FRIGG_SKIP_AWS_DISCOVERY);
|
|
@@ -530,21 +531,15 @@ const createBaseDefinition = (AppDefinition, appEnvironmentVars, discoveredResou
|
|
|
530
531
|
'!**/node_modules/@aws-sdk/**',
|
|
531
532
|
'!package.json',
|
|
532
533
|
|
|
533
|
-
//
|
|
534
|
-
//
|
|
535
|
-
'!node_modules
|
|
536
|
-
'!node_modules/.prisma
|
|
537
|
-
'!node_modules
|
|
538
|
-
'!node_modules
|
|
539
|
-
'!node_modules
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
'!node_modules/@prisma-mongodb/client/libquery_engine-windows*',
|
|
543
|
-
'!node_modules/@prisma-postgresql/client/libquery_engine-darwin*',
|
|
544
|
-
'!node_modules/@prisma-postgresql/client/libquery_engine-debian*',
|
|
545
|
-
'!node_modules/@prisma-postgresql/client/libquery_engine-linux-*',
|
|
546
|
-
'!node_modules/@prisma-postgresql/client/libquery_engine-windows*',
|
|
547
|
-
// rhel-openssl binaries are kept (not excluded)
|
|
534
|
+
// GLOBAL Prisma exclusions - all Prisma packages moved to Lambda Layer
|
|
535
|
+
// This reduces each function from ~120MB to ~45MB (60% reduction)
|
|
536
|
+
'!node_modules/@prisma/**',
|
|
537
|
+
'!node_modules/.prisma/**',
|
|
538
|
+
'!node_modules/@prisma-mongodb/**',
|
|
539
|
+
'!node_modules/@prisma-postgresql/**',
|
|
540
|
+
'!node_modules/prisma/**',
|
|
541
|
+
// Prisma packages will be provided at runtime via Lambda Layer
|
|
542
|
+
// See: LAMBDA-LAYER-PRISMA.md for complete documentation
|
|
548
543
|
],
|
|
549
544
|
},
|
|
550
545
|
useDotenv: true,
|
|
@@ -617,46 +612,29 @@ const createBaseDefinition = (AppDefinition, appEnvironmentVars, discoveredResou
|
|
|
617
612
|
functions: {
|
|
618
613
|
auth: {
|
|
619
614
|
handler: 'node_modules/@friggframework/core/handlers/routers/auth.handler',
|
|
615
|
+
layers: [{ Ref: 'PrismaLambdaLayer' }],
|
|
620
616
|
events: [
|
|
621
617
|
{ httpApi: { path: '/api/integrations', method: 'ANY' } },
|
|
622
618
|
{ httpApi: { path: '/api/integrations/{proxy+}', method: 'ANY' } },
|
|
623
619
|
{ httpApi: { path: '/api/authorize', method: 'ANY' } },
|
|
624
620
|
],
|
|
625
|
-
// Exclude Prisma CLI - not needed for auth endpoints
|
|
626
|
-
package: {
|
|
627
|
-
patterns: [
|
|
628
|
-
'!node_modules/prisma/**',
|
|
629
|
-
'!node_modules/@prisma/engines/**',
|
|
630
|
-
],
|
|
631
|
-
},
|
|
632
621
|
},
|
|
633
622
|
user: {
|
|
634
623
|
handler: 'node_modules/@friggframework/core/handlers/routers/user.handler',
|
|
624
|
+
layers: [{ Ref: 'PrismaLambdaLayer' }],
|
|
635
625
|
events: [{ httpApi: { path: '/user/{proxy+}', method: 'ANY' } }],
|
|
636
|
-
// Exclude Prisma CLI - not needed for user endpoints
|
|
637
|
-
package: {
|
|
638
|
-
patterns: [
|
|
639
|
-
'!node_modules/prisma/**',
|
|
640
|
-
'!node_modules/@prisma/engines/**',
|
|
641
|
-
],
|
|
642
|
-
},
|
|
643
626
|
},
|
|
644
627
|
health: {
|
|
645
628
|
handler: 'node_modules/@friggframework/core/handlers/routers/health.handler',
|
|
629
|
+
layers: [{ Ref: 'PrismaLambdaLayer' }],
|
|
646
630
|
events: [
|
|
647
631
|
{ httpApi: { path: '/health', method: 'GET' } },
|
|
648
632
|
{ httpApi: { path: '/health/{proxy+}', method: 'GET' } },
|
|
649
633
|
],
|
|
650
|
-
// Exclude Prisma CLI - not needed for health checks
|
|
651
|
-
package: {
|
|
652
|
-
patterns: [
|
|
653
|
-
'!node_modules/prisma/**',
|
|
654
|
-
'!node_modules/@prisma/engines/**',
|
|
655
|
-
],
|
|
656
|
-
},
|
|
657
634
|
},
|
|
658
635
|
dbMigrate: {
|
|
659
636
|
handler: 'node_modules/@friggframework/core/handlers/workers/db-migration.handler',
|
|
637
|
+
layers: [{ Ref: 'PrismaLambdaLayer' }],
|
|
660
638
|
timeout: 300, // 5 minutes for long-running migrations
|
|
661
639
|
memorySize: 512, // Extra memory for Prisma CLI operations
|
|
662
640
|
reservedConcurrency: 1, // Prevent concurrent migrations
|
|
@@ -674,26 +652,26 @@ const createBaseDefinition = (AppDefinition, appEnvironmentVars, discoveredResou
|
|
|
674
652
|
PRISMA_HIDE_UPDATE_MESSAGE: '1', // Suppress update messages
|
|
675
653
|
PRISMA_MIGRATE_SKIP_SEED: '1', // Skip seeding during migrations
|
|
676
654
|
},
|
|
677
|
-
// Function-specific packaging: Include Prisma CLI
|
|
655
|
+
// Function-specific packaging: Include Prisma schemas (CLI from layer)
|
|
678
656
|
package: {
|
|
679
657
|
patterns: [
|
|
680
|
-
// Include Prisma CLI (required for prisma generate, migrate, db push)
|
|
681
|
-
'node_modules/prisma/**',
|
|
682
|
-
'node_modules/@prisma/engines/libquery_engine-rhel-*',
|
|
683
|
-
'node_modules/@prisma/engines/schema-engine-rhel-*',
|
|
684
|
-
'node_modules/@prisma/engines/migration-engine-rhel-*',
|
|
685
|
-
|
|
686
658
|
// Include Prisma schemas from @friggframework/core
|
|
659
|
+
// Note: Prisma CLI and clients come from Lambda Layer
|
|
687
660
|
'node_modules/@friggframework/core/prisma-mongodb/**',
|
|
688
661
|
'node_modules/@friggframework/core/prisma-postgresql/**',
|
|
689
|
-
|
|
690
|
-
// Include generated Prisma clients
|
|
691
|
-
'node_modules/@prisma-mongodb/client/**',
|
|
692
|
-
'node_modules/@prisma-postgresql/client/**',
|
|
693
662
|
],
|
|
694
663
|
},
|
|
695
664
|
},
|
|
696
665
|
},
|
|
666
|
+
layers: {
|
|
667
|
+
prisma: {
|
|
668
|
+
path: 'layers/prisma',
|
|
669
|
+
name: '${self:service}-prisma-${sls:stage}',
|
|
670
|
+
description: 'Prisma ORM clients for MongoDB and PostgreSQL with rhel-openssl-3.0.x binaries. Reduces function sizes by ~60% (120MB → 45MB). See LAMBDA-LAYER-PRISMA.md for details.',
|
|
671
|
+
compatibleRuntimes: ['nodejs18.x', 'nodejs20.x'],
|
|
672
|
+
retain: false, // Don't retain old layer versions
|
|
673
|
+
},
|
|
674
|
+
},
|
|
697
675
|
resources: {
|
|
698
676
|
Resources: {
|
|
699
677
|
InternalErrorQueue: {
|
|
@@ -2016,9 +1994,40 @@ const configureWebsockets = (definition, AppDefinition) => {
|
|
|
2016
1994
|
};
|
|
2017
1995
|
};
|
|
2018
1996
|
|
|
1997
|
+
/**
|
|
1998
|
+
* Ensure Prisma Lambda Layer exists
|
|
1999
|
+
* Automatically builds the layer if it doesn't exist in the project root
|
|
2000
|
+
*/
|
|
2001
|
+
async function ensurePrismaLayerExists() {
|
|
2002
|
+
const projectRoot = process.cwd();
|
|
2003
|
+
const layerPath = path.join(projectRoot, 'layers/prisma');
|
|
2004
|
+
|
|
2005
|
+
// Check if layer already exists
|
|
2006
|
+
if (fs.existsSync(layerPath)) {
|
|
2007
|
+
console.log('✓ Prisma Lambda Layer already exists at', layerPath);
|
|
2008
|
+
return;
|
|
2009
|
+
}
|
|
2010
|
+
|
|
2011
|
+
// Layer doesn't exist - build it automatically
|
|
2012
|
+
console.log('📦 Prisma Lambda Layer not found - building automatically...');
|
|
2013
|
+
console.log(' This may take a minute on first deployment.\n');
|
|
2014
|
+
|
|
2015
|
+
try {
|
|
2016
|
+
await buildPrismaLayer();
|
|
2017
|
+
console.log('✓ Prisma Lambda Layer built successfully\n');
|
|
2018
|
+
} catch (error) {
|
|
2019
|
+
console.error('✗ Failed to build Prisma Lambda Layer:', error.message);
|
|
2020
|
+
console.error(' You may need to run: npm install @friggframework/core\n');
|
|
2021
|
+
throw error;
|
|
2022
|
+
}
|
|
2023
|
+
}
|
|
2024
|
+
|
|
2019
2025
|
const composeServerlessDefinition = async (AppDefinition) => {
|
|
2020
2026
|
console.log('composeServerlessDefinition', AppDefinition);
|
|
2021
2027
|
|
|
2028
|
+
// Ensure Prisma layer exists before generating serverless config
|
|
2029
|
+
await ensurePrismaLayerExists();
|
|
2030
|
+
|
|
2022
2031
|
const discoveredResources = await gatherDiscoveredResources(AppDefinition);
|
|
2023
2032
|
const appEnvironmentVars = getAppEnvironmentVars(AppDefinition);
|
|
2024
2033
|
const definition = createBaseDefinition(AppDefinition, appEnvironmentVars, discoveredResources);
|
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.454.
|
|
4
|
+
"version": "2.0.0--canary.454.1c50057.0",
|
|
5
5
|
"dependencies": {
|
|
6
6
|
"@aws-sdk/client-ec2": "^3.835.0",
|
|
7
7
|
"@aws-sdk/client-kms": "^3.835.0",
|
|
@@ -11,8 +11,8 @@
|
|
|
11
11
|
"@babel/eslint-parser": "^7.18.9",
|
|
12
12
|
"@babel/parser": "^7.25.3",
|
|
13
13
|
"@babel/traverse": "^7.25.3",
|
|
14
|
-
"@friggframework/schemas": "2.0.0--canary.454.
|
|
15
|
-
"@friggframework/test": "2.0.0--canary.454.
|
|
14
|
+
"@friggframework/schemas": "2.0.0--canary.454.1c50057.0",
|
|
15
|
+
"@friggframework/test": "2.0.0--canary.454.1c50057.0",
|
|
16
16
|
"@hapi/boom": "^10.0.1",
|
|
17
17
|
"@inquirer/prompts": "^5.3.8",
|
|
18
18
|
"axios": "^1.7.2",
|
|
@@ -34,8 +34,8 @@
|
|
|
34
34
|
"serverless-http": "^2.7.0"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|
|
37
|
-
"@friggframework/eslint-config": "2.0.0--canary.454.
|
|
38
|
-
"@friggframework/prettier-config": "2.0.0--canary.454.
|
|
37
|
+
"@friggframework/eslint-config": "2.0.0--canary.454.1c50057.0",
|
|
38
|
+
"@friggframework/prettier-config": "2.0.0--canary.454.1c50057.0",
|
|
39
39
|
"aws-sdk-client-mock": "^4.1.0",
|
|
40
40
|
"aws-sdk-client-mock-jest": "^4.1.0",
|
|
41
41
|
"jest": "^30.1.3",
|
|
@@ -70,5 +70,5 @@
|
|
|
70
70
|
"publishConfig": {
|
|
71
71
|
"access": "public"
|
|
72
72
|
},
|
|
73
|
-
"gitHead": "
|
|
73
|
+
"gitHead": "1c5005735e6a245830ae658d28c0448c54bf0e6a"
|
|
74
74
|
}
|