@friggframework/devtools 2.0.0-next.52 → 2.0.0-next.54
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/README.md +13 -14
- package/frigg-cli/__tests__/unit/commands/db-setup.test.js +267 -166
- package/frigg-cli/__tests__/unit/utils/database-validator.test.js +45 -14
- package/frigg-cli/__tests__/unit/utils/error-messages.test.js +44 -3
- package/frigg-cli/db-setup-command/index.js +75 -22
- package/frigg-cli/deploy-command/index.js +6 -3
- package/frigg-cli/utils/database-validator.js +18 -5
- package/frigg-cli/utils/error-messages.js +84 -12
- package/infrastructure/README.md +28 -0
- package/infrastructure/domains/database/migration-builder.js +26 -20
- package/infrastructure/domains/database/migration-builder.test.js +27 -0
- package/infrastructure/domains/integration/integration-builder.js +17 -10
- package/infrastructure/domains/integration/integration-builder.test.js +97 -0
- package/infrastructure/domains/networking/vpc-builder.js +240 -18
- package/infrastructure/domains/networking/vpc-builder.test.js +711 -13
- package/infrastructure/domains/networking/vpc-resolver.js +221 -40
- package/infrastructure/domains/networking/vpc-resolver.test.js +318 -18
- package/infrastructure/domains/security/kms-builder.js +55 -6
- package/infrastructure/domains/security/kms-builder.test.js +19 -1
- package/infrastructure/domains/shared/cloudformation-discovery.js +310 -13
- package/infrastructure/domains/shared/cloudformation-discovery.test.js +395 -0
- package/infrastructure/domains/shared/providers/aws-provider-adapter.js +41 -6
- package/infrastructure/domains/shared/providers/aws-provider-adapter.test.js +39 -0
- package/infrastructure/domains/shared/resource-discovery.js +17 -5
- package/infrastructure/domains/shared/resource-discovery.test.js +36 -0
- package/infrastructure/domains/shared/utilities/base-definition-factory.js +30 -20
- package/infrastructure/domains/shared/utilities/base-definition-factory.test.js +43 -0
- package/infrastructure/infrastructure-composer.js +11 -3
- package/infrastructure/scripts/build-prisma-layer.js +153 -78
- package/infrastructure/scripts/build-prisma-layer.test.js +27 -11
- package/layers/prisma/.build-complete +2 -2
- package/package.json +7 -7
|
@@ -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)
|
|
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 the 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
|
|
|
@@ -43,10 +45,12 @@ function createBaseDefinition(
|
|
|
43
45
|
],
|
|
44
46
|
exclude: [
|
|
45
47
|
// Exclude Prisma (provided via Lambda Layer)
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
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/**',
|
|
@@ -109,10 +113,12 @@ function createBaseDefinition(
|
|
|
109
113
|
'node_modules/@aws-sdk/**',
|
|
110
114
|
|
|
111
115
|
// Exclude Prisma (provided via Lambda Layer)
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
+
...(usePrismaLayer ? [
|
|
117
|
+
'node_modules/@prisma/**',
|
|
118
|
+
'node_modules/.prisma/**',
|
|
119
|
+
'node_modules/prisma/**',
|
|
120
|
+
'node_modules/@friggframework/core/generated/**',
|
|
121
|
+
] : []),
|
|
116
122
|
|
|
117
123
|
// Exclude nested node_modules from symlinked frigg packages (for npm link development)
|
|
118
124
|
'node_modules/@friggframework/core/node_modules/**',
|
|
@@ -218,9 +224,11 @@ function createBaseDefinition(
|
|
|
218
224
|
external: [
|
|
219
225
|
'@aws-sdk/*',
|
|
220
226
|
'aws-sdk',
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
227
|
+
...(usePrismaLayer ? [
|
|
228
|
+
'@prisma/client',
|
|
229
|
+
'prisma',
|
|
230
|
+
'.prisma/*',
|
|
231
|
+
] : []),
|
|
224
232
|
],
|
|
225
233
|
packager: 'npm',
|
|
226
234
|
keepNames: true,
|
|
@@ -228,8 +236,10 @@ function createBaseDefinition(
|
|
|
228
236
|
exclude: [
|
|
229
237
|
'aws-sdk',
|
|
230
238
|
'@aws-sdk/*',
|
|
231
|
-
|
|
232
|
-
|
|
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,49 @@ 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('includes Prisma layer and exclusions by default', () => {
|
|
249
|
+
const result = createBaseDefinition({}, {}, {});
|
|
250
|
+
|
|
251
|
+
expect(result.layers.prisma).toBeDefined();
|
|
252
|
+
expect(result.functions.auth.layers).toEqual([{ Ref: 'PrismaLambdaLayer' }]);
|
|
253
|
+
expect(result.functions.auth.package.exclude).toEqual(
|
|
254
|
+
expect.arrayContaining([
|
|
255
|
+
'node_modules/@prisma/**',
|
|
256
|
+
'node_modules/.prisma/**',
|
|
257
|
+
'node_modules/prisma/**',
|
|
258
|
+
'node_modules/@friggframework/core/generated/**',
|
|
259
|
+
])
|
|
260
|
+
);
|
|
261
|
+
expect(result.custom.esbuild.external).toEqual(
|
|
262
|
+
expect.arrayContaining(['@prisma/client', 'prisma'])
|
|
263
|
+
);
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
it('omits Prisma layer and bundles Prisma when disabled', () => {
|
|
267
|
+
const result = createBaseDefinition({}, {}, {}, false);
|
|
268
|
+
|
|
269
|
+
expect(result.layers).toEqual({});
|
|
270
|
+
expect(result.functions.auth.layers).toBeUndefined();
|
|
271
|
+
expect(result.functions.auth.package.exclude).not.toEqual(
|
|
272
|
+
expect.arrayContaining([
|
|
273
|
+
'node_modules/@prisma/**',
|
|
274
|
+
'node_modules/.prisma/**',
|
|
275
|
+
'node_modules/prisma/**',
|
|
276
|
+
'node_modules/@friggframework/core/generated/**',
|
|
277
|
+
])
|
|
278
|
+
);
|
|
279
|
+
expect(result.custom.esbuild.external).not.toEqual(
|
|
280
|
+
expect.arrayContaining(['@prisma/client', 'prisma'])
|
|
281
|
+
);
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
it('defaults to usePrismaLayer=true when omitted', () => {
|
|
285
|
+
const result = createBaseDefinition({}, {}, {});
|
|
286
|
+
expect(result.functions.auth.layers).toEqual([{ Ref: 'PrismaLambdaLayer' }]);
|
|
287
|
+
});
|
|
288
|
+
});
|
|
246
289
|
});
|
|
247
290
|
});
|
|
248
291
|
|
|
@@ -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
|
-
//
|
|
36
|
-
|
|
35
|
+
// Determine if deployment should use Prisma Lambda Layer (default: true)
|
|
36
|
+
const usePrismaLayer = AppDefinition.usePrismaLambdaLayer !== false;
|
|
37
|
+
|
|
38
|
+
// Ensure Prisma layer exists only when configured to use it
|
|
39
|
+
if (usePrismaLayer) {
|
|
40
|
+
await ensurePrismaLayerExists(AppDefinition.database || {});
|
|
41
|
+
} else {
|
|
42
|
+
console.log('📦 Skipping Prisma Lambda Layer (usePrismaLambdaLayer=false - bundling Prisma 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
|
|
@@ -44,7 +44,10 @@ function findCorePackage(startDir) {
|
|
|
44
44
|
const root = path.parse(currentDir).root;
|
|
45
45
|
|
|
46
46
|
while (currentDir !== root) {
|
|
47
|
-
const candidatePath = path.join(
|
|
47
|
+
const candidatePath = path.join(
|
|
48
|
+
currentDir,
|
|
49
|
+
'node_modules/@friggframework/core'
|
|
50
|
+
);
|
|
48
51
|
if (fs.existsSync(candidatePath)) {
|
|
49
52
|
return candidatePath;
|
|
50
53
|
}
|
|
@@ -53,7 +56,7 @@ function findCorePackage(startDir) {
|
|
|
53
56
|
|
|
54
57
|
throw new Error(
|
|
55
58
|
'@friggframework/core not found in node_modules.\n' +
|
|
56
|
-
|
|
59
|
+
'Run "npm install" to install dependencies.'
|
|
57
60
|
);
|
|
58
61
|
}
|
|
59
62
|
|
|
@@ -66,7 +69,8 @@ function getGeneratedClientPackages(databaseConfig = {}) {
|
|
|
66
69
|
const packages = [];
|
|
67
70
|
|
|
68
71
|
// Check if MongoDB is enabled (via mongoDB or documentDB config)
|
|
69
|
-
const mongoEnabled =
|
|
72
|
+
const mongoEnabled =
|
|
73
|
+
databaseConfig?.mongoDB?.enable === true ||
|
|
70
74
|
databaseConfig?.documentDB?.enable === true;
|
|
71
75
|
if (mongoEnabled) {
|
|
72
76
|
packages.push('generated/prisma-mongodb');
|
|
@@ -90,15 +94,19 @@ function getGeneratedClientPackages(databaseConfig = {}) {
|
|
|
90
94
|
}
|
|
91
95
|
|
|
92
96
|
function getMigrationsPackages(clientPackages) {
|
|
93
|
-
return clientPackages.map(pkg => ({
|
|
97
|
+
return clientPackages.map((pkg) => ({
|
|
94
98
|
dbType: pkg.replace('generated/prisma-', ''),
|
|
95
|
-
clientPackage: pkg
|
|
99
|
+
clientPackage: pkg,
|
|
96
100
|
}));
|
|
97
101
|
}
|
|
98
102
|
|
|
99
103
|
function getMigrationSourcePath(searchPaths, dbType) {
|
|
100
104
|
for (const searchPath of searchPaths) {
|
|
101
|
-
const candidatePath = path.join(
|
|
105
|
+
const candidatePath = path.join(
|
|
106
|
+
searchPath,
|
|
107
|
+
`prisma-${dbType}`,
|
|
108
|
+
'migrations'
|
|
109
|
+
);
|
|
102
110
|
if (fs.existsSync(candidatePath)) {
|
|
103
111
|
return candidatePath;
|
|
104
112
|
}
|
|
@@ -110,6 +118,38 @@ function getMigrationDestinationPath(layerNodeModules, clientPackage) {
|
|
|
110
118
|
return path.join(layerNodeModules, clientPackage, 'migrations');
|
|
111
119
|
}
|
|
112
120
|
|
|
121
|
+
/**
|
|
122
|
+
* Build list of required file paths for layer verification
|
|
123
|
+
* Different databases have different deployment patterns:
|
|
124
|
+
* - PostgreSQL: Uses migrations (prisma migrate) - requires migration_lock.toml
|
|
125
|
+
* - MongoDB: Uses schema push (prisma db push) - NO migrations directory
|
|
126
|
+
*
|
|
127
|
+
* @param {Array} clientPackages - Generated client packages to include (e.g., ['generated/prisma-postgresql'])
|
|
128
|
+
* @returns {Array} List of required file paths that must exist in the layer
|
|
129
|
+
*/
|
|
130
|
+
function getRequiredLayerPaths(clientPackages) {
|
|
131
|
+
const requiredPaths = [
|
|
132
|
+
// Base Prisma client runtime files (always required)
|
|
133
|
+
'@prisma/client/runtime',
|
|
134
|
+
'@prisma/client/index.d.ts',
|
|
135
|
+
];
|
|
136
|
+
|
|
137
|
+
// Add schema.prisma for each database client
|
|
138
|
+
for (const pkg of clientPackages) {
|
|
139
|
+
requiredPaths.push(`${pkg}/schema.prisma`);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Add migrations ONLY for PostgreSQL (MongoDB uses schema push, no migrations)
|
|
143
|
+
for (const pkg of clientPackages) {
|
|
144
|
+
const dbType = pkg.replace('generated/prisma-', '');
|
|
145
|
+
if (dbType === 'postgresql') {
|
|
146
|
+
requiredPaths.push(`${pkg}/migrations/migration_lock.toml`);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
return requiredPaths;
|
|
151
|
+
}
|
|
152
|
+
|
|
113
153
|
// Configuration
|
|
114
154
|
// Script runs from integration project root (e.g., backend/)
|
|
115
155
|
// and reads Prisma packages from @friggframework/core
|
|
@@ -129,16 +169,16 @@ const BINARY_PATTERNS_TO_REMOVE = [
|
|
|
129
169
|
|
|
130
170
|
// Files to remove for size optimization
|
|
131
171
|
const FILES_TO_REMOVE = [
|
|
132
|
-
'*.map',
|
|
133
|
-
'*.md',
|
|
134
|
-
'LICENSE*',
|
|
135
|
-
'CHANGELOG*',
|
|
136
|
-
'*.test.js',
|
|
137
|
-
'*.spec.js',
|
|
138
|
-
'*mysql.wasm*',
|
|
139
|
-
'*cockroachdb.wasm*',
|
|
140
|
-
'*sqlite.wasm*',
|
|
141
|
-
'*sqlserver.wasm*',
|
|
172
|
+
'*.map', // Source maps (37MB savings)
|
|
173
|
+
'*.md', // Markdown files
|
|
174
|
+
'LICENSE*', // License files
|
|
175
|
+
'CHANGELOG*', // Changelog files
|
|
176
|
+
'*.test.js', // Test files
|
|
177
|
+
'*.spec.js', // Spec files
|
|
178
|
+
'*mysql.wasm*', // MySQL WASM files (not needed for PostgreSQL)
|
|
179
|
+
'*cockroachdb.wasm*', // CockroachDB WASM files
|
|
180
|
+
'*sqlite.wasm*', // SQLite WASM files
|
|
181
|
+
'*sqlserver.wasm*', // SQL Server WASM files
|
|
142
182
|
];
|
|
143
183
|
|
|
144
184
|
// ANSI color codes for output
|
|
@@ -211,7 +251,7 @@ async function createLayerStructure() {
|
|
|
211
251
|
|
|
212
252
|
/**
|
|
213
253
|
* Install Prisma client directly into layer (RUNTIME ONLY - NO CLI)
|
|
214
|
-
*
|
|
254
|
+
*
|
|
215
255
|
* The Prisma CLI is NOT included in this layer to keep it small.
|
|
216
256
|
* For migrations, the dbMigrate function has its own separate packaging with CLI.
|
|
217
257
|
*/
|
|
@@ -269,12 +309,15 @@ async function copyPrismaPackages(clientPackages) {
|
|
|
269
309
|
// 2. Project root node_modules (if hoisted from project)
|
|
270
310
|
// 3. Workspace root node_modules (where core is located - handles hoisting)
|
|
271
311
|
// 4. Core package itself (for generated/ directories)
|
|
272
|
-
const workspaceNodeModules = path.join(
|
|
312
|
+
const workspaceNodeModules = path.join(
|
|
313
|
+
path.dirname(CORE_PACKAGE_PATH),
|
|
314
|
+
'..'
|
|
315
|
+
);
|
|
273
316
|
const searchPaths = [
|
|
274
|
-
path.join(CORE_PACKAGE_PATH, 'node_modules'),
|
|
275
|
-
path.join(PROJECT_ROOT, 'node_modules'),
|
|
276
|
-
workspaceNodeModules,
|
|
277
|
-
CORE_PACKAGE_PATH,
|
|
317
|
+
path.join(CORE_PACKAGE_PATH, 'node_modules'), // Core's own node_modules
|
|
318
|
+
path.join(PROJECT_ROOT, 'node_modules'), // Project node_modules
|
|
319
|
+
workspaceNodeModules, // Workspace root node_modules
|
|
320
|
+
CORE_PACKAGE_PATH, // Core package itself (for generated/)
|
|
278
321
|
];
|
|
279
322
|
|
|
280
323
|
let copiedCount = 0;
|
|
@@ -295,17 +338,19 @@ async function copyPrismaPackages(clientPackages) {
|
|
|
295
338
|
if (sourcePath) {
|
|
296
339
|
const destPath = path.join(LAYER_NODE_MODULES, pkg);
|
|
297
340
|
await fs.copy(sourcePath, destPath, {
|
|
298
|
-
dereference: true,
|
|
341
|
+
dereference: true, // Follow symlinks
|
|
299
342
|
filter: (src) => {
|
|
300
343
|
// Skip node_modules within Prisma packages
|
|
301
344
|
return !src.includes('/node_modules/node_modules/');
|
|
302
|
-
}
|
|
345
|
+
},
|
|
303
346
|
});
|
|
304
|
-
const fromLocation = sourcePath.includes(
|
|
347
|
+
const fromLocation = sourcePath.includes(
|
|
348
|
+
'@friggframework/core/generated'
|
|
349
|
+
)
|
|
305
350
|
? 'core package (generated)'
|
|
306
351
|
: sourcePath.includes('@friggframework/core/node_modules')
|
|
307
|
-
|
|
308
|
-
|
|
352
|
+
? 'core node_modules'
|
|
353
|
+
: 'workspace';
|
|
309
354
|
logSuccess(`Copied ${pkg} (from ${fromLocation})`);
|
|
310
355
|
copiedCount++;
|
|
311
356
|
} else {
|
|
@@ -316,18 +361,25 @@ async function copyPrismaPackages(clientPackages) {
|
|
|
316
361
|
|
|
317
362
|
if (missingPackages.length > 0) {
|
|
318
363
|
throw new Error(
|
|
319
|
-
`Missing generated Prisma clients: ${missingPackages.join(
|
|
320
|
-
|
|
364
|
+
`Missing generated Prisma clients: ${missingPackages.join(
|
|
365
|
+
', '
|
|
366
|
+
)}.\n` +
|
|
367
|
+
'Ensure @friggframework/core has generated Prisma clients (run "npm run prisma:generate" in core package).'
|
|
321
368
|
);
|
|
322
369
|
}
|
|
323
370
|
|
|
324
|
-
logSuccess(
|
|
371
|
+
logSuccess(
|
|
372
|
+
`Copied ${copiedCount} generated client packages from @friggframework/core`
|
|
373
|
+
);
|
|
325
374
|
}
|
|
326
375
|
|
|
327
376
|
async function copyMigrations(clientPackages) {
|
|
328
377
|
logStep(5, 'Copying migrations from @friggframework/core');
|
|
329
378
|
|
|
330
|
-
const workspaceNodeModules = path.join(
|
|
379
|
+
const workspaceNodeModules = path.join(
|
|
380
|
+
path.dirname(CORE_PACKAGE_PATH),
|
|
381
|
+
'..'
|
|
382
|
+
);
|
|
331
383
|
const searchPaths = [
|
|
332
384
|
path.join(CORE_PACKAGE_PATH, 'node_modules'),
|
|
333
385
|
path.join(PROJECT_ROOT, 'node_modules'),
|
|
@@ -342,21 +394,34 @@ async function copyMigrations(clientPackages) {
|
|
|
342
394
|
const sourcePath = getMigrationSourcePath(searchPaths, dbType);
|
|
343
395
|
|
|
344
396
|
if (sourcePath) {
|
|
345
|
-
const destPath = getMigrationDestinationPath(
|
|
397
|
+
const destPath = getMigrationDestinationPath(
|
|
398
|
+
LAYER_NODE_MODULES,
|
|
399
|
+
clientPackage
|
|
400
|
+
);
|
|
346
401
|
await fs.copy(sourcePath, destPath, { dereference: true });
|
|
347
|
-
|
|
348
|
-
const fromLocation = sourcePath.includes(
|
|
402
|
+
|
|
403
|
+
const fromLocation = sourcePath.includes(
|
|
404
|
+
'@friggframework/core/prisma'
|
|
405
|
+
)
|
|
349
406
|
? 'core package'
|
|
350
407
|
: 'workspace';
|
|
351
|
-
logSuccess(
|
|
408
|
+
logSuccess(
|
|
409
|
+
`Copied migrations for ${dbType} (from ${fromLocation})`
|
|
410
|
+
);
|
|
352
411
|
copiedCount++;
|
|
353
412
|
} else {
|
|
354
|
-
logWarning(
|
|
413
|
+
logWarning(
|
|
414
|
+
`Migrations not found for ${dbType} - this may cause migration failures`
|
|
415
|
+
);
|
|
355
416
|
}
|
|
356
417
|
}
|
|
357
418
|
|
|
358
419
|
if (copiedCount > 0) {
|
|
359
|
-
logSuccess(
|
|
420
|
+
logSuccess(
|
|
421
|
+
`Copied migrations for ${copiedCount} database ${
|
|
422
|
+
copiedCount === 1 ? 'type' : 'types'
|
|
423
|
+
}`
|
|
424
|
+
);
|
|
360
425
|
}
|
|
361
426
|
}
|
|
362
427
|
|
|
@@ -375,7 +440,7 @@ async function removeUnnecessaryFiles() {
|
|
|
375
440
|
const findCmd = `find "${LAYER_NODE_MODULES}" -name "${pattern}" -type f 2>/dev/null || true`;
|
|
376
441
|
const files = execSync(findCmd, { encoding: 'utf8' })
|
|
377
442
|
.split('\n')
|
|
378
|
-
.filter(f => f.trim());
|
|
443
|
+
.filter((f) => f.trim());
|
|
379
444
|
|
|
380
445
|
for (const file of files) {
|
|
381
446
|
if (await fs.pathExists(file)) {
|
|
@@ -392,7 +457,9 @@ async function removeUnnecessaryFiles() {
|
|
|
392
457
|
|
|
393
458
|
if (removedCount > 0) {
|
|
394
459
|
const sizeMB = (totalSize / (1024 * 1024)).toFixed(2);
|
|
395
|
-
logSuccess(
|
|
460
|
+
logSuccess(
|
|
461
|
+
`Removed ${removedCount} unnecessary files (saved ${sizeMB} MB)`
|
|
462
|
+
);
|
|
396
463
|
} else {
|
|
397
464
|
log('No unnecessary files found to remove');
|
|
398
465
|
}
|
|
@@ -413,7 +480,7 @@ async function removeNonRhelBinaries() {
|
|
|
413
480
|
const findCmd = `find "${LAYER_NODE_MODULES}" -name "${pattern}" 2>/dev/null || true`;
|
|
414
481
|
const files = execSync(findCmd, { encoding: 'utf8' })
|
|
415
482
|
.split('\n')
|
|
416
|
-
.filter(f => f.trim());
|
|
483
|
+
.filter((f) => f.trim());
|
|
417
484
|
|
|
418
485
|
for (const file of files) {
|
|
419
486
|
if (await fs.pathExists(file)) {
|
|
@@ -430,7 +497,9 @@ async function removeNonRhelBinaries() {
|
|
|
430
497
|
|
|
431
498
|
if (removedCount > 0) {
|
|
432
499
|
const sizeMB = (totalSize / (1024 * 1024)).toFixed(2);
|
|
433
|
-
logSuccess(
|
|
500
|
+
logSuccess(
|
|
501
|
+
`Removed ${removedCount} non-rhel binaries (saved ${sizeMB} MB)`
|
|
502
|
+
);
|
|
434
503
|
} else {
|
|
435
504
|
log('No non-rhel binaries found to remove');
|
|
436
505
|
}
|
|
@@ -447,20 +516,24 @@ async function verifyRhelBinaries(expectedClients) {
|
|
|
447
516
|
const findCmd = `find "${LAYER_NODE_MODULES}" -name "*rhel-openssl-3.0.x*" 2>/dev/null || true`;
|
|
448
517
|
const rhelFiles = execSync(findCmd, { encoding: 'utf8' })
|
|
449
518
|
.split('\n')
|
|
450
|
-
.filter(f => f.trim());
|
|
519
|
+
.filter((f) => f.trim());
|
|
451
520
|
|
|
452
521
|
if (rhelFiles.length === 0) {
|
|
453
522
|
throw new Error(
|
|
454
523
|
'No rhel-openssl-3.0.x binaries found!\n' +
|
|
455
|
-
|
|
524
|
+
'Check that Prisma schemas have binaryTargets = ["native", "rhel-openssl-3.0.x"]'
|
|
456
525
|
);
|
|
457
526
|
}
|
|
458
527
|
|
|
459
528
|
const expectedCount = expectedClients.length;
|
|
460
|
-
logSuccess(
|
|
529
|
+
logSuccess(
|
|
530
|
+
`Found ${rhelFiles.length} rhel-openssl-3.0.x ${
|
|
531
|
+
rhelFiles.length === 1 ? 'binary' : 'binaries'
|
|
532
|
+
} (expected ${expectedCount})`
|
|
533
|
+
);
|
|
461
534
|
|
|
462
535
|
// Show the binaries found
|
|
463
|
-
rhelFiles.forEach(file => {
|
|
536
|
+
rhelFiles.forEach((file) => {
|
|
464
537
|
const relativePath = path.relative(LAYER_NODE_MODULES, file);
|
|
465
538
|
log(` - ${relativePath}`, 'reset');
|
|
466
539
|
});
|
|
@@ -477,26 +550,11 @@ async function verifyRhelBinaries(expectedClients) {
|
|
|
477
550
|
async function verifyLayerStructure(clientPackages) {
|
|
478
551
|
logStep(9, 'Verifying layer structure (runtime only)');
|
|
479
552
|
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
'@prisma/client/index.d.ts',
|
|
483
|
-
];
|
|
484
|
-
|
|
485
|
-
// Add schema.prisma for each included client
|
|
486
|
-
for (const pkg of clientPackages) {
|
|
487
|
-
requiredPaths.push(`${pkg}/schema.prisma`);
|
|
488
|
-
}
|
|
489
|
-
|
|
490
|
-
// Add migrations directory for each included client
|
|
491
|
-
for (const pkg of clientPackages) {
|
|
492
|
-
requiredPaths.push(`${pkg}/migrations/migration_lock.toml`);
|
|
493
|
-
}
|
|
553
|
+
// Get required paths based on database types
|
|
554
|
+
const requiredPaths = getRequiredLayerPaths(clientPackages);
|
|
494
555
|
|
|
495
556
|
// Verify CLI is NOT present (keeps layer small)
|
|
496
|
-
const forbiddenPaths = [
|
|
497
|
-
'prisma/build',
|
|
498
|
-
'.bin/prisma',
|
|
499
|
-
];
|
|
557
|
+
const forbiddenPaths = ['prisma/build', '.bin/prisma'];
|
|
500
558
|
|
|
501
559
|
let allPresent = true;
|
|
502
560
|
|
|
@@ -511,7 +569,9 @@ async function verifyLayerStructure(clientPackages) {
|
|
|
511
569
|
}
|
|
512
570
|
|
|
513
571
|
if (!allPresent) {
|
|
514
|
-
throw new Error(
|
|
572
|
+
throw new Error(
|
|
573
|
+
'Layer structure verification failed - missing required files'
|
|
574
|
+
);
|
|
515
575
|
}
|
|
516
576
|
|
|
517
577
|
logSuccess('All required runtime files present');
|
|
@@ -520,7 +580,9 @@ async function verifyLayerStructure(clientPackages) {
|
|
|
520
580
|
for (const forbiddenPath of forbiddenPaths) {
|
|
521
581
|
const fullPath = path.join(LAYER_NODE_MODULES, forbiddenPath);
|
|
522
582
|
if (await fs.pathExists(fullPath)) {
|
|
523
|
-
logWarning(
|
|
583
|
+
logWarning(
|
|
584
|
+
` ⚠ ${forbiddenPath} found (should be excluded for minimal layer)`
|
|
585
|
+
);
|
|
524
586
|
}
|
|
525
587
|
}
|
|
526
588
|
}
|
|
@@ -548,9 +610,15 @@ async function displayLayerSummary() {
|
|
|
548
610
|
log(' - @prisma/engines (minimal - only rhel binary)', 'yellow');
|
|
549
611
|
|
|
550
612
|
log('\nNext steps:', 'bright');
|
|
551
|
-
log(
|
|
613
|
+
log(
|
|
614
|
+
' 1. Verify layer structure: ls -lah layers/prisma/nodejs/node_modules/',
|
|
615
|
+
'reset'
|
|
616
|
+
);
|
|
552
617
|
log(' 2. Deploy to AWS: frigg deploy --stage dev', 'reset');
|
|
553
|
-
log(
|
|
618
|
+
log(
|
|
619
|
+
' 3. Check function sizes in Lambda console (should be ~45-55MB)',
|
|
620
|
+
'reset'
|
|
621
|
+
);
|
|
554
622
|
|
|
555
623
|
log('\nSee LAMBDA-LAYER-PRISMA.md for complete documentation.', 'yellow');
|
|
556
624
|
log('='.repeat(60) + '\n', 'bright');
|
|
@@ -578,12 +646,12 @@ async function buildPrismaLayer(databaseConfig = {}) {
|
|
|
578
646
|
try {
|
|
579
647
|
await cleanLayerDirectory();
|
|
580
648
|
await createLayerStructure();
|
|
581
|
-
await installPrismaPackages();
|
|
582
|
-
await copyPrismaPackages(clientPackages);
|
|
583
|
-
await copyMigrations(clientPackages);
|
|
584
|
-
await removeUnnecessaryFiles();
|
|
585
|
-
await removeNonRhelBinaries();
|
|
586
|
-
await verifyRhelBinaries(clientPackages);
|
|
649
|
+
await installPrismaPackages(); // Install runtime client only (NO CLI)
|
|
650
|
+
await copyPrismaPackages(clientPackages); // Copy generated clients from core
|
|
651
|
+
await copyMigrations(clientPackages); // Copy migrations from core
|
|
652
|
+
await removeUnnecessaryFiles(); // Remove source maps, docs, tests (37MB+)
|
|
653
|
+
await removeNonRhelBinaries(); // Remove non-Linux binaries
|
|
654
|
+
await verifyRhelBinaries(clientPackages); // Verify query engines present
|
|
587
655
|
await verifyLayerStructure(clientPackages); // Verify minimal runtime structure
|
|
588
656
|
await displayLayerSummary();
|
|
589
657
|
|
|
@@ -600,9 +668,15 @@ async function buildPrismaLayer(databaseConfig = {}) {
|
|
|
600
668
|
}
|
|
601
669
|
|
|
602
670
|
log('\nTroubleshooting:', 'yellow');
|
|
603
|
-
log(
|
|
671
|
+
log(
|
|
672
|
+
' 1. Ensure @friggframework/core has dependencies installed',
|
|
673
|
+
'reset'
|
|
674
|
+
);
|
|
604
675
|
log(' 2. Run "npm run prisma:generate" in core package', 'reset');
|
|
605
|
-
log(
|
|
676
|
+
log(
|
|
677
|
+
' 3. Check that Prisma schemas have correct binaryTargets',
|
|
678
|
+
'reset'
|
|
679
|
+
);
|
|
606
680
|
log(' 4. See LAMBDA-LAYER-PRISMA.md for details\n', 'reset');
|
|
607
681
|
|
|
608
682
|
// Throw error instead of exit when used as module
|
|
@@ -617,10 +691,11 @@ if (require.main === module) {
|
|
|
617
691
|
.catch(() => process.exit(1));
|
|
618
692
|
}
|
|
619
693
|
|
|
620
|
-
module.exports = {
|
|
621
|
-
buildPrismaLayer,
|
|
694
|
+
module.exports = {
|
|
695
|
+
buildPrismaLayer,
|
|
622
696
|
getGeneratedClientPackages,
|
|
623
697
|
getMigrationsPackages,
|
|
624
698
|
getMigrationSourcePath,
|
|
625
|
-
getMigrationDestinationPath
|
|
699
|
+
getMigrationDestinationPath,
|
|
700
|
+
getRequiredLayerPaths,
|
|
626
701
|
};
|