@friggframework/devtools 2.0.0--canary.454.e2a280d.0 → 2.0.0--canary.458.c150d9a.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/__tests__/unit/commands/db-setup.test.js +1 -1
- package/frigg-cli/__tests__/unit/utils/prisma-runner.test.js +486 -0
- package/frigg-cli/build-command/index.js +1 -12
- package/frigg-cli/db-setup-command/index.js +1 -8
- package/frigg-cli/index.js +0 -1
- package/frigg-cli/utils/database-validator.js +1 -4
- package/frigg-cli/utils/prisma-runner.js +280 -0
- package/infrastructure/README.md +0 -51
- package/infrastructure/aws-discovery.js +2 -504
- package/infrastructure/aws-discovery.test.js +1 -447
- package/infrastructure/serverless-template.js +21 -459
- package/infrastructure/serverless-template.test.js +0 -91
- package/management-ui/src/App.jsx +1 -85
- package/management-ui/src/hooks/useFrigg.jsx +1 -215
- package/package.json +6 -8
- package/infrastructure/POSTGRES-CONFIGURATION.md +0 -645
- package/infrastructure/__tests__/postgres-config.test.js +0 -914
- package/infrastructure/scripts/build-prisma-layer.js +0 -394
|
@@ -1,394 +0,0 @@
|
|
|
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
|
-
/**
|
|
28
|
-
* Find @friggframework/core package, handling workspace hoisting
|
|
29
|
-
* Searches up the directory tree to find node_modules/@friggframework/core
|
|
30
|
-
*/
|
|
31
|
-
function findCorePackage(startDir) {
|
|
32
|
-
let currentDir = startDir;
|
|
33
|
-
const root = path.parse(currentDir).root;
|
|
34
|
-
|
|
35
|
-
while (currentDir !== root) {
|
|
36
|
-
const candidatePath = path.join(currentDir, 'node_modules/@friggframework/core');
|
|
37
|
-
if (fs.existsSync(candidatePath)) {
|
|
38
|
-
return candidatePath;
|
|
39
|
-
}
|
|
40
|
-
currentDir = path.dirname(currentDir);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
throw new Error(
|
|
44
|
-
'@friggframework/core not found in node_modules.\n' +
|
|
45
|
-
'Run "npm install" to install dependencies.'
|
|
46
|
-
);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
// Configuration
|
|
50
|
-
// Script runs from integration project root (e.g., backend/)
|
|
51
|
-
// and reads Prisma packages from @friggframework/core
|
|
52
|
-
const PROJECT_ROOT = process.cwd();
|
|
53
|
-
const CORE_PACKAGE_PATH = findCorePackage(PROJECT_ROOT);
|
|
54
|
-
const LAYER_OUTPUT_PATH = path.join(PROJECT_ROOT, 'layers/prisma');
|
|
55
|
-
const LAYER_NODE_MODULES = path.join(LAYER_OUTPUT_PATH, 'nodejs/node_modules');
|
|
56
|
-
|
|
57
|
-
// Packages to include in the layer
|
|
58
|
-
const PRISMA_PACKAGES = [
|
|
59
|
-
'@prisma/client', // From project node_modules
|
|
60
|
-
'generated/prisma-mongodb', // From @friggframework/core package
|
|
61
|
-
'generated/prisma-postgresql', // From @friggframework/core package
|
|
62
|
-
'prisma', // CLI from project node_modules
|
|
63
|
-
];
|
|
64
|
-
|
|
65
|
-
// Binary patterns to remove (non-rhel)
|
|
66
|
-
const BINARY_PATTERNS_TO_REMOVE = [
|
|
67
|
-
'*darwin*',
|
|
68
|
-
'*debian*',
|
|
69
|
-
'*linux-arm*',
|
|
70
|
-
'*linux-musl*',
|
|
71
|
-
'*windows*',
|
|
72
|
-
];
|
|
73
|
-
|
|
74
|
-
// ANSI color codes for output
|
|
75
|
-
const colors = {
|
|
76
|
-
reset: '\x1b[0m',
|
|
77
|
-
bright: '\x1b[1m',
|
|
78
|
-
green: '\x1b[32m',
|
|
79
|
-
yellow: '\x1b[33m',
|
|
80
|
-
blue: '\x1b[34m',
|
|
81
|
-
red: '\x1b[31m',
|
|
82
|
-
};
|
|
83
|
-
|
|
84
|
-
function log(message, color = 'reset') {
|
|
85
|
-
console.log(`${colors[color]}${message}${colors.reset}`);
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
function logStep(step, message) {
|
|
89
|
-
log(`\n[${step}] ${message}`, 'blue');
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
function logSuccess(message) {
|
|
93
|
-
log(`✓ ${message}`, 'green');
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
function logWarning(message) {
|
|
97
|
-
log(`⚠ ${message}`, 'yellow');
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
function logError(message) {
|
|
101
|
-
log(`✗ ${message}`, 'red');
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
* Get directory size in MB
|
|
106
|
-
*/
|
|
107
|
-
function getDirectorySize(dirPath) {
|
|
108
|
-
try {
|
|
109
|
-
const output = execSync(`du -sm "${dirPath}"`, { encoding: 'utf8' });
|
|
110
|
-
const size = parseInt(output.split('\t')[0], 10);
|
|
111
|
-
return size;
|
|
112
|
-
} catch (error) {
|
|
113
|
-
logWarning(`Could not calculate directory size: ${error.message}`);
|
|
114
|
-
return 0;
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
/**
|
|
119
|
-
* Clean existing layer directory
|
|
120
|
-
*/
|
|
121
|
-
async function cleanLayerDirectory() {
|
|
122
|
-
logStep(1, 'Cleaning existing layer directory');
|
|
123
|
-
|
|
124
|
-
if (await fs.pathExists(LAYER_OUTPUT_PATH)) {
|
|
125
|
-
await fs.remove(LAYER_OUTPUT_PATH);
|
|
126
|
-
logSuccess(`Removed existing layer at ${LAYER_OUTPUT_PATH}`);
|
|
127
|
-
} else {
|
|
128
|
-
log('No existing layer to clean');
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
/**
|
|
133
|
-
* Create layer directory structure
|
|
134
|
-
*/
|
|
135
|
-
async function createLayerStructure() {
|
|
136
|
-
logStep(2, 'Creating layer directory structure');
|
|
137
|
-
|
|
138
|
-
await fs.ensureDir(LAYER_NODE_MODULES);
|
|
139
|
-
logSuccess(`Created ${LAYER_NODE_MODULES}`);
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
/**
|
|
143
|
-
* Copy Prisma packages from core to layer
|
|
144
|
-
*/
|
|
145
|
-
async function copyPrismaPackages() {
|
|
146
|
-
logStep(3, 'Copying Prisma packages from @friggframework/core');
|
|
147
|
-
|
|
148
|
-
// Build search paths, handling workspace hoisting
|
|
149
|
-
// Packages can be in:
|
|
150
|
-
// 1. Core's own node_modules (if not hoisted)
|
|
151
|
-
// 2. Project root node_modules (if hoisted from project)
|
|
152
|
-
// 3. Workspace root node_modules (where core is located - handles hoisting)
|
|
153
|
-
// 4. Core package itself (for generated/ directories)
|
|
154
|
-
const workspaceNodeModules = path.join(path.dirname(CORE_PACKAGE_PATH), '..');
|
|
155
|
-
const searchPaths = [
|
|
156
|
-
path.join(CORE_PACKAGE_PATH, 'node_modules'), // Core's own node_modules
|
|
157
|
-
path.join(PROJECT_ROOT, 'node_modules'), // Project node_modules
|
|
158
|
-
workspaceNodeModules, // Workspace root node_modules
|
|
159
|
-
CORE_PACKAGE_PATH, // Core package itself (for generated/)
|
|
160
|
-
];
|
|
161
|
-
|
|
162
|
-
let copiedCount = 0;
|
|
163
|
-
let missingPackages = [];
|
|
164
|
-
|
|
165
|
-
for (const pkg of PRISMA_PACKAGES) {
|
|
166
|
-
let sourcePath = null;
|
|
167
|
-
|
|
168
|
-
// Try to find package in search paths
|
|
169
|
-
for (const searchPath of searchPaths) {
|
|
170
|
-
const candidatePath = path.join(searchPath, pkg);
|
|
171
|
-
if (await fs.pathExists(candidatePath)) {
|
|
172
|
-
sourcePath = candidatePath;
|
|
173
|
-
break;
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
if (sourcePath) {
|
|
178
|
-
const destPath = path.join(LAYER_NODE_MODULES, pkg);
|
|
179
|
-
await fs.copy(sourcePath, destPath, {
|
|
180
|
-
dereference: true, // Follow symlinks
|
|
181
|
-
filter: (src) => {
|
|
182
|
-
// Skip node_modules within Prisma packages
|
|
183
|
-
return !src.includes('/node_modules/node_modules/');
|
|
184
|
-
}
|
|
185
|
-
});
|
|
186
|
-
const fromLocation = sourcePath.includes('@friggframework/core/generated')
|
|
187
|
-
? 'core package (generated)'
|
|
188
|
-
: sourcePath.includes('@friggframework/core/node_modules')
|
|
189
|
-
? 'core node_modules'
|
|
190
|
-
: 'workspace';
|
|
191
|
-
logSuccess(`Copied ${pkg} (from ${fromLocation})`);
|
|
192
|
-
copiedCount++;
|
|
193
|
-
} else {
|
|
194
|
-
missingPackages.push(pkg);
|
|
195
|
-
logWarning(`Package not found: ${pkg}`);
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
if (missingPackages.length > 0) {
|
|
200
|
-
throw new Error(
|
|
201
|
-
`Missing Prisma packages: ${missingPackages.join(', ')}.\n` +
|
|
202
|
-
'Ensure @friggframework/core is installed with "npm install" and Prisma clients are generated.'
|
|
203
|
-
);
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
log(`\nCopied ${copiedCount}/${PRISMA_PACKAGES.length} packages`);
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
/**
|
|
210
|
-
* Remove non-rhel engine binaries to reduce layer size
|
|
211
|
-
*/
|
|
212
|
-
async function removeNonRhelBinaries() {
|
|
213
|
-
logStep(4, 'Removing non-rhel engine binaries');
|
|
214
|
-
|
|
215
|
-
let removedCount = 0;
|
|
216
|
-
let totalSize = 0;
|
|
217
|
-
|
|
218
|
-
for (const pattern of BINARY_PATTERNS_TO_REMOVE) {
|
|
219
|
-
try {
|
|
220
|
-
// Find files matching the pattern
|
|
221
|
-
const findCmd = `find "${LAYER_NODE_MODULES}" -name "${pattern}" 2>/dev/null || true`;
|
|
222
|
-
const files = execSync(findCmd, { encoding: 'utf8' })
|
|
223
|
-
.split('\n')
|
|
224
|
-
.filter(f => f.trim());
|
|
225
|
-
|
|
226
|
-
for (const file of files) {
|
|
227
|
-
if (await fs.pathExists(file)) {
|
|
228
|
-
const stats = await fs.stat(file);
|
|
229
|
-
totalSize += stats.size;
|
|
230
|
-
await fs.remove(file);
|
|
231
|
-
removedCount++;
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
} catch (error) {
|
|
235
|
-
logWarning(`Error removing ${pattern}: ${error.message}`);
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
if (removedCount > 0) {
|
|
240
|
-
const sizeMB = (totalSize / (1024 * 1024)).toFixed(2);
|
|
241
|
-
logSuccess(`Removed ${removedCount} non-rhel binaries (saved ${sizeMB} MB)`);
|
|
242
|
-
} else {
|
|
243
|
-
log('No non-rhel binaries found to remove');
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
/**
|
|
248
|
-
* Verify rhel binaries are present
|
|
249
|
-
*/
|
|
250
|
-
async function verifyRhelBinaries() {
|
|
251
|
-
logStep(5, 'Verifying rhel-openssl-3.0.x binaries are present');
|
|
252
|
-
|
|
253
|
-
try {
|
|
254
|
-
const findCmd = `find "${LAYER_NODE_MODULES}" -name "*rhel-openssl-3.0.x*" 2>/dev/null || true`;
|
|
255
|
-
const rhelFiles = execSync(findCmd, { encoding: 'utf8' })
|
|
256
|
-
.split('\n')
|
|
257
|
-
.filter(f => f.trim());
|
|
258
|
-
|
|
259
|
-
if (rhelFiles.length === 0) {
|
|
260
|
-
throw new Error(
|
|
261
|
-
'No rhel-openssl-3.0.x binaries found!\n' +
|
|
262
|
-
'Check that Prisma schemas have binaryTargets = ["native", "rhel-openssl-3.0.x"]'
|
|
263
|
-
);
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
logSuccess(`Found ${rhelFiles.length} rhel-openssl-3.0.x binaries`);
|
|
267
|
-
|
|
268
|
-
// Show the binaries found
|
|
269
|
-
rhelFiles.forEach(file => {
|
|
270
|
-
const relativePath = path.relative(LAYER_NODE_MODULES, file);
|
|
271
|
-
log(` - ${relativePath}`, 'reset');
|
|
272
|
-
});
|
|
273
|
-
} catch (error) {
|
|
274
|
-
throw new Error(`Binary verification failed: ${error.message}`);
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
/**
|
|
279
|
-
* Verify required files exist
|
|
280
|
-
*/
|
|
281
|
-
async function verifyLayerStructure() {
|
|
282
|
-
logStep(6, 'Verifying layer structure');
|
|
283
|
-
|
|
284
|
-
const requiredPaths = [
|
|
285
|
-
'@prisma/client/runtime',
|
|
286
|
-
'@prisma/client/index.d.ts',
|
|
287
|
-
'generated/prisma-mongodb/schema.prisma',
|
|
288
|
-
'generated/prisma-postgresql/schema.prisma',
|
|
289
|
-
'prisma/build',
|
|
290
|
-
];
|
|
291
|
-
|
|
292
|
-
let allPresent = true;
|
|
293
|
-
|
|
294
|
-
for (const requiredPath of requiredPaths) {
|
|
295
|
-
const fullPath = path.join(LAYER_NODE_MODULES, requiredPath);
|
|
296
|
-
if (await fs.pathExists(fullPath)) {
|
|
297
|
-
log(` ✓ ${requiredPath}`, 'green');
|
|
298
|
-
} else {
|
|
299
|
-
log(` ✗ ${requiredPath} (missing)`, 'red');
|
|
300
|
-
allPresent = false;
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
if (!allPresent) {
|
|
305
|
-
throw new Error('Layer structure verification failed - missing required files');
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
logSuccess('All required files present');
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
/**
|
|
312
|
-
* Calculate and display final layer size
|
|
313
|
-
*/
|
|
314
|
-
async function displayLayerSummary() {
|
|
315
|
-
logStep(7, 'Layer build summary');
|
|
316
|
-
|
|
317
|
-
const layerSizeMB = getDirectorySize(LAYER_OUTPUT_PATH);
|
|
318
|
-
|
|
319
|
-
log('\n' + '='.repeat(60), 'bright');
|
|
320
|
-
log(' Prisma Lambda Layer Built Successfully!', 'bright');
|
|
321
|
-
log('='.repeat(60), 'bright');
|
|
322
|
-
|
|
323
|
-
log(`\nLayer location: ${LAYER_OUTPUT_PATH}`, 'blue');
|
|
324
|
-
log(`Layer size: ~${layerSizeMB} MB`, 'green');
|
|
325
|
-
|
|
326
|
-
log('\nPackages included:', 'bright');
|
|
327
|
-
PRISMA_PACKAGES.forEach(pkg => {
|
|
328
|
-
log(` - ${pkg}`, 'reset');
|
|
329
|
-
});
|
|
330
|
-
|
|
331
|
-
log('\nNext steps:', 'bright');
|
|
332
|
-
log(' 1. Verify layer structure: ls -lah layers/prisma/nodejs/node_modules/', 'reset');
|
|
333
|
-
log(' 2. Deploy to AWS: frigg deploy --stage dev', 'reset');
|
|
334
|
-
log(' 3. Check function sizes in Lambda console (should be ~45-55MB)', 'reset');
|
|
335
|
-
|
|
336
|
-
log('\nSee LAMBDA-LAYER-PRISMA.md for complete documentation.', 'yellow');
|
|
337
|
-
log('='.repeat(60) + '\n', 'bright');
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
/**
|
|
341
|
-
* Main build function
|
|
342
|
-
*/
|
|
343
|
-
async function buildPrismaLayer() {
|
|
344
|
-
const startTime = Date.now();
|
|
345
|
-
|
|
346
|
-
log('\n' + '='.repeat(60), 'bright');
|
|
347
|
-
log(' Building Prisma Lambda Layer', 'bright');
|
|
348
|
-
log('='.repeat(60) + '\n', 'bright');
|
|
349
|
-
|
|
350
|
-
// Log paths
|
|
351
|
-
log(`Project root: ${PROJECT_ROOT}`, 'reset');
|
|
352
|
-
log(`Core package: ${CORE_PACKAGE_PATH}`, 'reset');
|
|
353
|
-
log(`Layer output: ${LAYER_OUTPUT_PATH}\n`, 'reset');
|
|
354
|
-
|
|
355
|
-
try {
|
|
356
|
-
await cleanLayerDirectory();
|
|
357
|
-
await createLayerStructure();
|
|
358
|
-
await copyPrismaPackages();
|
|
359
|
-
await removeNonRhelBinaries();
|
|
360
|
-
await verifyRhelBinaries();
|
|
361
|
-
await verifyLayerStructure();
|
|
362
|
-
await displayLayerSummary();
|
|
363
|
-
|
|
364
|
-
const duration = ((Date.now() - startTime) / 1000).toFixed(2);
|
|
365
|
-
log(`Build completed in ${duration}s\n`, 'green');
|
|
366
|
-
|
|
367
|
-
return { success: true, duration };
|
|
368
|
-
} catch (error) {
|
|
369
|
-
logError(`\nBuild failed: ${error.message}`);
|
|
370
|
-
|
|
371
|
-
if (error.stack) {
|
|
372
|
-
log('\nStack trace:', 'red');
|
|
373
|
-
console.error(error.stack);
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
log('\nTroubleshooting:', 'yellow');
|
|
377
|
-
log(' 1. Ensure @friggframework/core has dependencies installed', 'reset');
|
|
378
|
-
log(' 2. Run "npm run prisma:generate" in core package', 'reset');
|
|
379
|
-
log(' 3. Check that Prisma schemas have correct binaryTargets', 'reset');
|
|
380
|
-
log(' 4. See LAMBDA-LAYER-PRISMA.md for details\n', 'reset');
|
|
381
|
-
|
|
382
|
-
// Throw error instead of exit when used as module
|
|
383
|
-
throw error;
|
|
384
|
-
}
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
// Run the build when executed directly
|
|
388
|
-
if (require.main === module) {
|
|
389
|
-
buildPrismaLayer()
|
|
390
|
-
.then(() => process.exit(0))
|
|
391
|
-
.catch(() => process.exit(1));
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
module.exports = { buildPrismaLayer };
|