@friggframework/devtools 2.0.0--canary.461.84ff4f5.0 → 2.0.0--canary.461.ec909cf.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/aws-discovery.js +42 -19
- package/infrastructure/domains/database/aurora-builder.js +307 -0
- package/infrastructure/domains/database/aurora-builder.test.js +482 -0
- package/infrastructure/domains/networking/vpc-builder.js +718 -0
- package/infrastructure/domains/networking/vpc-builder.test.js +772 -0
- package/infrastructure/domains/networking/vpc-discovery.js +159 -0
- package/infrastructure/domains/shared/providers/aws-provider-adapter.js +445 -0
- package/infrastructure/domains/shared/utilities/base-definition-factory.js +385 -0
- package/infrastructure/domains/shared/utilities/handler-path-resolver.js +129 -0
- package/infrastructure/infrastructure-composer.test.js +1895 -0
- package/infrastructure/serverless-template.js +226 -42
- package/infrastructure/serverless-template.test.js +3 -1
- package/package.json +6 -6
|
@@ -0,0 +1,385 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Base Serverless Definition Factory
|
|
3
|
+
*
|
|
4
|
+
* Utility Layer - Hexagonal Architecture
|
|
5
|
+
*
|
|
6
|
+
* Creates the base serverless.yml configuration with core functions,
|
|
7
|
+
* resources, plugins, and provider settings.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const { buildEnvironment } = require('../environment-builder');
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Create base serverless definition with core functions and resources
|
|
14
|
+
*
|
|
15
|
+
* This creates the foundation serverless configuration that all
|
|
16
|
+
* Frigg applications need, including:
|
|
17
|
+
* - Core Lambda functions (auth, user, health, dbMigrate)
|
|
18
|
+
* - Error handling infrastructure (SQS, SNS, CloudWatch)
|
|
19
|
+
* - Prisma Lambda Layer
|
|
20
|
+
* - Base plugins and esbuild configuration
|
|
21
|
+
*
|
|
22
|
+
* @param {Object} AppDefinition - Application definition
|
|
23
|
+
* @param {Object} appEnvironmentVars - Environment variables from app definition
|
|
24
|
+
* @param {Object} discoveredResources - AWS resources discovered during build
|
|
25
|
+
* @returns {Object} Base serverless definition
|
|
26
|
+
*/
|
|
27
|
+
function createBaseDefinition(
|
|
28
|
+
AppDefinition,
|
|
29
|
+
appEnvironmentVars,
|
|
30
|
+
discoveredResources
|
|
31
|
+
) {
|
|
32
|
+
const region = process.env.AWS_REGION || 'us-east-1';
|
|
33
|
+
|
|
34
|
+
// Package config for handlers that skip esbuild (need node_modules dependencies)
|
|
35
|
+
// Since Express and other deps are now in backend/node_modules, exclude only what's not needed
|
|
36
|
+
const skipEsbuildPackageConfig = {
|
|
37
|
+
exclude: [
|
|
38
|
+
// Exclude Prisma (provided via Lambda Layer)
|
|
39
|
+
'node_modules/@prisma/**',
|
|
40
|
+
'node_modules/.prisma/**',
|
|
41
|
+
'node_modules/prisma/**',
|
|
42
|
+
'node_modules/@friggframework/core/generated/**',
|
|
43
|
+
|
|
44
|
+
// Exclude AWS SDK (provided by Lambda runtime)
|
|
45
|
+
'node_modules/aws-sdk/**',
|
|
46
|
+
'node_modules/@aws-sdk/**',
|
|
47
|
+
|
|
48
|
+
// Exclude dev/test dependencies
|
|
49
|
+
'node_modules/@friggframework/test/**',
|
|
50
|
+
'node_modules/@friggframework/eslint-config/**',
|
|
51
|
+
'node_modules/@friggframework/prettier-config/**',
|
|
52
|
+
'node_modules/jest/**',
|
|
53
|
+
'node_modules/prettier/**',
|
|
54
|
+
'node_modules/eslint/**',
|
|
55
|
+
|
|
56
|
+
// Exclude backend source and layers
|
|
57
|
+
'src/**',
|
|
58
|
+
'test/**',
|
|
59
|
+
'layers/**',
|
|
60
|
+
'coverage/**',
|
|
61
|
+
'**/*.test.js',
|
|
62
|
+
'**/*.spec.js',
|
|
63
|
+
'**/.claude-flow/**',
|
|
64
|
+
'**/.swarm/**',
|
|
65
|
+
],
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
// Function-level package config to exclude Prisma and AWS SDK
|
|
69
|
+
const functionPackageConfig = {
|
|
70
|
+
exclude: [
|
|
71
|
+
// Exclude AWS SDK (already in Lambda runtime or externalized by esbuild)
|
|
72
|
+
'node_modules/aws-sdk/**',
|
|
73
|
+
'node_modules/@aws-sdk/**',
|
|
74
|
+
|
|
75
|
+
// Exclude Prisma (provided via Lambda Layer)
|
|
76
|
+
'node_modules/@prisma/**',
|
|
77
|
+
'node_modules/.prisma/**',
|
|
78
|
+
'node_modules/prisma/**',
|
|
79
|
+
'node_modules/@friggframework/core/generated/**',
|
|
80
|
+
|
|
81
|
+
// Exclude nested node_modules from symlinked frigg packages (for npm link development)
|
|
82
|
+
'node_modules/@friggframework/core/node_modules/**',
|
|
83
|
+
'node_modules/@friggframework/devtools/node_modules/**',
|
|
84
|
+
|
|
85
|
+
// Exclude development/test files from backend project
|
|
86
|
+
'coverage/**',
|
|
87
|
+
'test/**',
|
|
88
|
+
'src/**',
|
|
89
|
+
'layers/**',
|
|
90
|
+
'**/*.test.js',
|
|
91
|
+
'**/*.spec.js',
|
|
92
|
+
'.git/**',
|
|
93
|
+
'.github/**',
|
|
94
|
+
|
|
95
|
+
// Exclude AI assistant and development artifacts
|
|
96
|
+
'**/.claude-flow/**',
|
|
97
|
+
'**/.swarm/**',
|
|
98
|
+
'**/CLAUDE.md',
|
|
99
|
+
'**/README.md',
|
|
100
|
+
'**/*.md',
|
|
101
|
+
|
|
102
|
+
// Exclude config and meta files from core
|
|
103
|
+
'node_modules/@friggframework/core/.eslintrc.json',
|
|
104
|
+
'node_modules/@friggframework/core/.gitignore',
|
|
105
|
+
'node_modules/@friggframework/core/jest.config.js',
|
|
106
|
+
'node_modules/@friggframework/core/CHANGELOG.md',
|
|
107
|
+
],
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
return {
|
|
111
|
+
frameworkVersion: '>=3.17.0',
|
|
112
|
+
service: AppDefinition.name || 'create-frigg-app',
|
|
113
|
+
package: {
|
|
114
|
+
individually: true,
|
|
115
|
+
},
|
|
116
|
+
useDotenv: true,
|
|
117
|
+
provider: {
|
|
118
|
+
name: AppDefinition.provider || 'aws',
|
|
119
|
+
...(process.env.AWS_PROFILE && { profile: process.env.AWS_PROFILE }),
|
|
120
|
+
runtime: 'nodejs22.x', // Node.js 22.x (latest Lambda runtime with AWS SDK v3)
|
|
121
|
+
timeout: 29, // Set to 29s to give buffer before API Gateway's 30s timeout
|
|
122
|
+
region,
|
|
123
|
+
stage: '${opt:stage}',
|
|
124
|
+
environment: buildEnvironment(appEnvironmentVars, discoveredResources),
|
|
125
|
+
iamRoleStatements: [
|
|
126
|
+
{
|
|
127
|
+
Effect: 'Allow',
|
|
128
|
+
Action: ['sns:Publish'],
|
|
129
|
+
Resource: { Ref: 'InternalErrorBridgeTopic' },
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
Effect: 'Allow',
|
|
133
|
+
Action: [
|
|
134
|
+
'sqs:SendMessage',
|
|
135
|
+
'sqs:SendMessageBatch',
|
|
136
|
+
'sqs:GetQueueUrl',
|
|
137
|
+
'sqs:GetQueueAttributes',
|
|
138
|
+
],
|
|
139
|
+
Resource: [
|
|
140
|
+
{ 'Fn::GetAtt': ['InternalErrorQueue', 'Arn'] },
|
|
141
|
+
{
|
|
142
|
+
'Fn::Join': [
|
|
143
|
+
':',
|
|
144
|
+
[
|
|
145
|
+
'arn:aws:sqs:${self:provider.region}:*:${self:service}--${self:provider.stage}-*Queue',
|
|
146
|
+
],
|
|
147
|
+
],
|
|
148
|
+
},
|
|
149
|
+
],
|
|
150
|
+
},
|
|
151
|
+
],
|
|
152
|
+
httpApi: {
|
|
153
|
+
payload: '2.0',
|
|
154
|
+
cors: {
|
|
155
|
+
allowedOrigins: ['*'],
|
|
156
|
+
allowedHeaders: ['*'],
|
|
157
|
+
allowedMethods: ['*'],
|
|
158
|
+
allowCredentials: false,
|
|
159
|
+
},
|
|
160
|
+
name: '${opt:stage, "dev"}-${self:service}',
|
|
161
|
+
disableDefaultEndpoint: false,
|
|
162
|
+
},
|
|
163
|
+
},
|
|
164
|
+
plugins: [
|
|
165
|
+
'serverless-esbuild',
|
|
166
|
+
'serverless-dotenv-plugin',
|
|
167
|
+
'serverless-offline-sqs',
|
|
168
|
+
'serverless-offline',
|
|
169
|
+
'@friggframework/serverless-plugin',
|
|
170
|
+
],
|
|
171
|
+
custom: {
|
|
172
|
+
esbuild: {
|
|
173
|
+
bundle: true,
|
|
174
|
+
minify: true,
|
|
175
|
+
sourcemap: true,
|
|
176
|
+
target: 'node22',
|
|
177
|
+
platform: 'node',
|
|
178
|
+
format: 'cjs',
|
|
179
|
+
external: [
|
|
180
|
+
'@aws-sdk/*',
|
|
181
|
+
'aws-sdk',
|
|
182
|
+
'@prisma/client',
|
|
183
|
+
'prisma',
|
|
184
|
+
'.prisma/*',
|
|
185
|
+
],
|
|
186
|
+
packager: 'npm',
|
|
187
|
+
keepNames: true,
|
|
188
|
+
keepOutputDirectory: false, // Clean up .esbuild directory after packaging
|
|
189
|
+
exclude: [
|
|
190
|
+
'aws-sdk',
|
|
191
|
+
'@aws-sdk/*',
|
|
192
|
+
'@prisma/client',
|
|
193
|
+
'prisma',
|
|
194
|
+
],
|
|
195
|
+
},
|
|
196
|
+
'serverless-offline': {
|
|
197
|
+
httpPort: 3001,
|
|
198
|
+
lambdaPort: 4001,
|
|
199
|
+
websocketPort: 3002,
|
|
200
|
+
location: '.', // Set base directory for handler resolution to current directory
|
|
201
|
+
skipCacheInvalidation: false,
|
|
202
|
+
},
|
|
203
|
+
'serverless-offline-sqs': {
|
|
204
|
+
autoCreate: false,
|
|
205
|
+
apiVersion: '2012-11-05',
|
|
206
|
+
endpoint: 'http://localhost:4566',
|
|
207
|
+
region,
|
|
208
|
+
accessKeyId: 'root',
|
|
209
|
+
secretAccessKey: 'root',
|
|
210
|
+
skipCacheInvalidation: false,
|
|
211
|
+
},
|
|
212
|
+
},
|
|
213
|
+
functions: {
|
|
214
|
+
auth: {
|
|
215
|
+
handler: 'node_modules/@friggframework/core/handlers/routers/auth.handler',
|
|
216
|
+
layers: [{ Ref: 'PrismaLambdaLayer' }],
|
|
217
|
+
skipEsbuild: true, // Handlers in node_modules don't need bundling
|
|
218
|
+
package: skipEsbuildPackageConfig,
|
|
219
|
+
events: [
|
|
220
|
+
{ httpApi: { path: '/api/integrations', method: 'ANY' } },
|
|
221
|
+
{
|
|
222
|
+
httpApi: {
|
|
223
|
+
path: '/api/integrations/{proxy+}',
|
|
224
|
+
method: 'ANY',
|
|
225
|
+
},
|
|
226
|
+
},
|
|
227
|
+
{ httpApi: { path: '/api/authorize', method: 'ANY' } },
|
|
228
|
+
],
|
|
229
|
+
},
|
|
230
|
+
user: {
|
|
231
|
+
handler: 'node_modules/@friggframework/core/handlers/routers/user.handler',
|
|
232
|
+
layers: [{ Ref: 'PrismaLambdaLayer' }],
|
|
233
|
+
skipEsbuild: true, // Handlers in node_modules don't need bundling
|
|
234
|
+
package: skipEsbuildPackageConfig,
|
|
235
|
+
events: [{ httpApi: { path: '/user/{proxy+}', method: 'ANY' } }],
|
|
236
|
+
},
|
|
237
|
+
health: {
|
|
238
|
+
handler: 'node_modules/@friggframework/core/handlers/routers/health.handler',
|
|
239
|
+
layers: [{ Ref: 'PrismaLambdaLayer' }],
|
|
240
|
+
skipEsbuild: true, // Handlers in node_modules don't need bundling
|
|
241
|
+
package: skipEsbuildPackageConfig,
|
|
242
|
+
events: [
|
|
243
|
+
{ httpApi: { path: '/health', method: 'GET' } },
|
|
244
|
+
{ httpApi: { path: '/health/{proxy+}', method: 'GET' } },
|
|
245
|
+
],
|
|
246
|
+
},
|
|
247
|
+
dbMigrate: {
|
|
248
|
+
handler: 'node_modules/@friggframework/core/handlers/database-migration-handler.handler',
|
|
249
|
+
// DO NOT use Prisma layer - this function includes Prisma CLI separately
|
|
250
|
+
skipEsbuild: true, // Handlers in node_modules don't need bundling
|
|
251
|
+
timeout: 300, // 5 minutes for long-running migrations
|
|
252
|
+
memorySize: 1024, // Extra memory for Prisma CLI and migration operations
|
|
253
|
+
reservedConcurrency: 1, // Prevent concurrent migrations (CRITICAL for data safety)
|
|
254
|
+
description: 'Runs database migrations via Prisma CLI (invoke manually from CI/CD or triggers). Prisma CLI bundled separately.',
|
|
255
|
+
package: {
|
|
256
|
+
individually: true,
|
|
257
|
+
patterns: [
|
|
258
|
+
// Include handler
|
|
259
|
+
'node_modules/@friggframework/core/handlers/database-migration-handler.js',
|
|
260
|
+
|
|
261
|
+
// Include ONLY PostgreSQL Prisma client (exclude MongoDB)
|
|
262
|
+
'node_modules/@friggframework/core/generated/prisma-postgresql/**',
|
|
263
|
+
'!node_modules/@friggframework/core/generated/prisma-mongodb/**', // Exclude MongoDB client entirely
|
|
264
|
+
|
|
265
|
+
// Include Prisma runtime
|
|
266
|
+
'node_modules/@prisma/client/**',
|
|
267
|
+
'node_modules/.prisma/**',
|
|
268
|
+
'node_modules/prisma/**', // Prisma CLI
|
|
269
|
+
|
|
270
|
+
// Exclude unnecessary engines and files
|
|
271
|
+
'!node_modules/prisma/node_modules/**',
|
|
272
|
+
'!**/query-engine-darwin*', // Exclude macOS binaries (keep rhel for Lambda)
|
|
273
|
+
'!**/runtime/*.wasm', // WASM engines
|
|
274
|
+
'!**/*.md',
|
|
275
|
+
'!**/*.map',
|
|
276
|
+
'!**/LICENSE*',
|
|
277
|
+
'!**/*.d.ts',
|
|
278
|
+
'!**/*.d.mts',
|
|
279
|
+
],
|
|
280
|
+
},
|
|
281
|
+
maximumEventAge: 60,
|
|
282
|
+
maximumRetryAttempts: 0,
|
|
283
|
+
tags: {
|
|
284
|
+
Purpose: 'DatabaseMigration',
|
|
285
|
+
ManagedBy: 'Frigg',
|
|
286
|
+
},
|
|
287
|
+
environment: {
|
|
288
|
+
CI: '1',
|
|
289
|
+
PRISMA_HIDE_UPDATE_MESSAGE: '1',
|
|
290
|
+
PRISMA_MIGRATE_SKIP_SEED: '1',
|
|
291
|
+
},
|
|
292
|
+
},
|
|
293
|
+
},
|
|
294
|
+
layers: {
|
|
295
|
+
prisma: {
|
|
296
|
+
path: 'layers/prisma',
|
|
297
|
+
name: '${self:service}-prisma-${sls:stage}',
|
|
298
|
+
description: 'Prisma runtime client only (NO CLI) with rhel-openssl-3.0.x binaries (~10-15MB). CLI packaged separately in dbMigrate function.',
|
|
299
|
+
compatibleRuntimes: ['nodejs20.x', 'nodejs22.x'],
|
|
300
|
+
retain: false,
|
|
301
|
+
},
|
|
302
|
+
},
|
|
303
|
+
resources: {
|
|
304
|
+
Resources: {
|
|
305
|
+
InternalErrorQueue: {
|
|
306
|
+
Type: 'AWS::SQS::Queue',
|
|
307
|
+
Properties: {
|
|
308
|
+
QueueName:
|
|
309
|
+
'${self:service}-internal-error-queue-${self:provider.stage}',
|
|
310
|
+
MessageRetentionPeriod: 300,
|
|
311
|
+
},
|
|
312
|
+
},
|
|
313
|
+
InternalErrorBridgeTopic: {
|
|
314
|
+
Type: 'AWS::SNS::Topic',
|
|
315
|
+
Properties: {
|
|
316
|
+
Subscription: [
|
|
317
|
+
{
|
|
318
|
+
Protocol: 'sqs',
|
|
319
|
+
Endpoint: {
|
|
320
|
+
'Fn::GetAtt': ['InternalErrorQueue', 'Arn'],
|
|
321
|
+
},
|
|
322
|
+
},
|
|
323
|
+
],
|
|
324
|
+
},
|
|
325
|
+
},
|
|
326
|
+
InternalErrorBridgePolicy: {
|
|
327
|
+
Type: 'AWS::SQS::QueuePolicy',
|
|
328
|
+
Properties: {
|
|
329
|
+
Queues: [{ Ref: 'InternalErrorQueue' }],
|
|
330
|
+
PolicyDocument: {
|
|
331
|
+
Version: '2012-10-17',
|
|
332
|
+
Statement: [
|
|
333
|
+
{
|
|
334
|
+
Sid: 'Allow Dead Letter SNS to publish to SQS',
|
|
335
|
+
Effect: 'Allow',
|
|
336
|
+
Principal: { Service: 'sns.amazonaws.com' },
|
|
337
|
+
Resource: {
|
|
338
|
+
'Fn::GetAtt': [
|
|
339
|
+
'InternalErrorQueue',
|
|
340
|
+
'Arn',
|
|
341
|
+
],
|
|
342
|
+
},
|
|
343
|
+
Action: [
|
|
344
|
+
'SQS:SendMessage',
|
|
345
|
+
'SQS:SendMessageBatch',
|
|
346
|
+
],
|
|
347
|
+
Condition: {
|
|
348
|
+
ArnEquals: {
|
|
349
|
+
'aws:SourceArn': {
|
|
350
|
+
Ref: 'InternalErrorBridgeTopic',
|
|
351
|
+
},
|
|
352
|
+
},
|
|
353
|
+
},
|
|
354
|
+
},
|
|
355
|
+
],
|
|
356
|
+
},
|
|
357
|
+
},
|
|
358
|
+
},
|
|
359
|
+
ApiGatewayAlarm5xx: {
|
|
360
|
+
Type: 'AWS::CloudWatch::Alarm',
|
|
361
|
+
Properties: {
|
|
362
|
+
AlarmDescription: 'API Gateway 5xx Errors',
|
|
363
|
+
Namespace: 'AWS/ApiGateway',
|
|
364
|
+
MetricName: '5XXError',
|
|
365
|
+
Statistic: 'Sum',
|
|
366
|
+
Threshold: 0,
|
|
367
|
+
ComparisonOperator: 'GreaterThanThreshold',
|
|
368
|
+
EvaluationPeriods: 1,
|
|
369
|
+
Period: 60,
|
|
370
|
+
AlarmActions: [{ Ref: 'InternalErrorBridgeTopic' }],
|
|
371
|
+
Dimensions: [
|
|
372
|
+
{ Name: 'ApiId', Value: { Ref: 'HttpApi' } },
|
|
373
|
+
{ Name: 'Stage', Value: '${self:provider.stage}' },
|
|
374
|
+
],
|
|
375
|
+
},
|
|
376
|
+
},
|
|
377
|
+
},
|
|
378
|
+
},
|
|
379
|
+
};
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
module.exports = {
|
|
383
|
+
createBaseDefinition,
|
|
384
|
+
};
|
|
385
|
+
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Handler Path Resolver Utility
|
|
3
|
+
*
|
|
4
|
+
* Utility Layer - Hexagonal Architecture
|
|
5
|
+
*
|
|
6
|
+
* Handles Lambda handler path resolution for offline mode compatibility.
|
|
7
|
+
* In offline mode, handler paths need to be relative to the working directory
|
|
8
|
+
* rather than absolute paths to node_modules.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
const path = require('path');
|
|
12
|
+
const fs = require('fs');
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Find node_modules path for offline mode handler path resolution
|
|
16
|
+
*
|
|
17
|
+
* Searches upward from current directory to locate node_modules directory
|
|
18
|
+
* using multiple fallback strategies.
|
|
19
|
+
*
|
|
20
|
+
* @returns {string} Path to node_modules directory
|
|
21
|
+
*/
|
|
22
|
+
function findNodeModulesPath() {
|
|
23
|
+
try {
|
|
24
|
+
let currentDir = process.cwd();
|
|
25
|
+
let nodeModulesPath = null;
|
|
26
|
+
|
|
27
|
+
// Strategy 1: Search upward through directory tree
|
|
28
|
+
for (let i = 0; i < 5; i++) {
|
|
29
|
+
const potentialPath = path.join(currentDir, 'node_modules');
|
|
30
|
+
if (fs.existsSync(potentialPath)) {
|
|
31
|
+
nodeModulesPath = potentialPath;
|
|
32
|
+
console.log(
|
|
33
|
+
`Found node_modules at: ${nodeModulesPath} (method 1)`
|
|
34
|
+
);
|
|
35
|
+
break;
|
|
36
|
+
}
|
|
37
|
+
const parentDir = path.dirname(currentDir);
|
|
38
|
+
if (parentDir === currentDir) break;
|
|
39
|
+
currentDir = parentDir;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Strategy 2: Use npm root command
|
|
43
|
+
if (!nodeModulesPath) {
|
|
44
|
+
try {
|
|
45
|
+
const { execSync } = require('node:child_process');
|
|
46
|
+
const npmRoot = execSync('npm root', {
|
|
47
|
+
encoding: 'utf8',
|
|
48
|
+
}).trim();
|
|
49
|
+
if (fs.existsSync(npmRoot)) {
|
|
50
|
+
nodeModulesPath = npmRoot;
|
|
51
|
+
console.log(
|
|
52
|
+
`Found node_modules at: ${nodeModulesPath} (method 2)`
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
} catch (npmError) {
|
|
56
|
+
console.error('Error executing npm root:', npmError);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Strategy 3: Search from package.json locations
|
|
61
|
+
if (!nodeModulesPath) {
|
|
62
|
+
currentDir = process.cwd();
|
|
63
|
+
for (let i = 0; i < 5; i++) {
|
|
64
|
+
const packageJsonPath = path.join(currentDir, 'package.json');
|
|
65
|
+
if (fs.existsSync(packageJsonPath)) {
|
|
66
|
+
const potentialNodeModules = path.join(
|
|
67
|
+
currentDir,
|
|
68
|
+
'node_modules'
|
|
69
|
+
);
|
|
70
|
+
if (fs.existsSync(potentialNodeModules)) {
|
|
71
|
+
nodeModulesPath = potentialNodeModules;
|
|
72
|
+
console.log(
|
|
73
|
+
`Found node_modules at: ${nodeModulesPath} (method 3)`
|
|
74
|
+
);
|
|
75
|
+
break;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
const parentDir = path.dirname(currentDir);
|
|
79
|
+
if (parentDir === currentDir) break;
|
|
80
|
+
currentDir = parentDir;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (nodeModulesPath) {
|
|
85
|
+
return nodeModulesPath;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Fallback: Assume parent directory
|
|
89
|
+
console.warn(
|
|
90
|
+
'Could not find node_modules path, falling back to default'
|
|
91
|
+
);
|
|
92
|
+
return path.resolve(process.cwd(), '../node_modules');
|
|
93
|
+
} catch (error) {
|
|
94
|
+
console.error('Error finding node_modules path:', error);
|
|
95
|
+
return path.resolve(process.cwd(), '../node_modules');
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Modify handler paths for offline mode
|
|
101
|
+
*
|
|
102
|
+
* In serverless-offline mode, handler paths need to be relative
|
|
103
|
+
* to the current working directory rather than using absolute
|
|
104
|
+
* node_modules paths.
|
|
105
|
+
*
|
|
106
|
+
* @param {Object} functions - Serverless functions configuration
|
|
107
|
+
* @returns {Object} Functions with modified handler paths
|
|
108
|
+
*/
|
|
109
|
+
function modifyHandlerPaths(functions) {
|
|
110
|
+
const isOffline = process.argv.includes('offline');
|
|
111
|
+
console.log('isOffline', isOffline);
|
|
112
|
+
|
|
113
|
+
if (!isOffline) {
|
|
114
|
+
console.log('Not in offline mode, skipping handler path modification');
|
|
115
|
+
return functions;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// In offline mode, don't modify the handler paths at all
|
|
119
|
+
// serverless-offline will resolve node_modules paths from the working directory
|
|
120
|
+
console.log('Offline mode detected - keeping original handler paths for serverless-offline');
|
|
121
|
+
|
|
122
|
+
return functions;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
module.exports = {
|
|
126
|
+
findNodeModulesPath,
|
|
127
|
+
modifyHandlerPaths,
|
|
128
|
+
};
|
|
129
|
+
|