@friggframework/devtools 2.0.0-next.1 → 2.0.0-next.10

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.
@@ -0,0 +1,33 @@
1
+ const { spawn } = require('child_process');
2
+ const path = require('path');
3
+
4
+ function buildCommand(options) {
5
+ console.log('Building the serverless application...');
6
+ const backendPath = path.resolve(process.cwd());
7
+ const infrastructurePath = 'infrastructure.js';
8
+ const command = 'serverless';
9
+ const serverlessArgs = [
10
+ 'package',
11
+ '--config',
12
+ infrastructurePath,
13
+ '--stage',
14
+ options.stage
15
+ ];
16
+
17
+ const childProcess = spawn(command, serverlessArgs, {
18
+ cwd: backendPath,
19
+ stdio: 'inherit',
20
+ });
21
+
22
+ childProcess.on('error', (error) => {
23
+ console.error(`Error executing command: ${error.message}`);
24
+ });
25
+
26
+ childProcess.on('close', (code) => {
27
+ if (code !== 0) {
28
+ console.log(`Child process exited with code ${code}`);
29
+ }
30
+ });
31
+ }
32
+
33
+ module.exports = { buildCommand };
@@ -0,0 +1,33 @@
1
+ const { spawn } = require('child_process');
2
+ const path = require('path');
3
+
4
+ function deployCommand(options) {
5
+ console.log('Deploying the serverless application...');
6
+ const backendPath = path.resolve(process.cwd());
7
+ const infrastructurePath = 'infrastructure.js';
8
+ const command = 'serverless';
9
+ const serverlessArgs = [
10
+ 'deploy',
11
+ '--config',
12
+ infrastructurePath,
13
+ '--stage',
14
+ options.stage
15
+ ];
16
+
17
+ const childProcess = spawn(command, serverlessArgs, {
18
+ cwd: backendPath,
19
+ stdio: 'inherit',
20
+ });
21
+
22
+ childProcess.on('error', (error) => {
23
+ console.error(`Error executing command: ${error.message}`);
24
+ });
25
+
26
+ childProcess.on('close', (code) => {
27
+ if (code !== 0) {
28
+ console.log(`Child process exited with code ${code}`);
29
+ }
30
+ });
31
+ }
32
+
33
+ module.exports = { deployCommand };
@@ -3,6 +3,8 @@
3
3
  const { Command } = require('commander');
4
4
  const { installCommand } = require('./install-command');
5
5
  const { startCommand } = require('./start-command'); // Assuming you have a startCommand module
6
+ const { buildCommand } = require('./build-command');
7
+ const { deployCommand } = require('./deploy-command');
6
8
 
7
9
  const program = new Command();
8
10
  program
@@ -13,8 +15,21 @@ program
13
15
  program
14
16
  .command('start')
15
17
  .description('Run the backend and optional frontend')
18
+ .option('-s, --stage <stage>', 'deployment stage', 'dev')
16
19
  .action(startCommand);
17
20
 
21
+ program
22
+ .command('build')
23
+ .description('Build the serverless application')
24
+ .option('-s, --stage <stage>', 'deployment stage', 'dev')
25
+ .action(buildCommand);
26
+
27
+ program
28
+ .command('deploy')
29
+ .description('Deploy the serverless application')
30
+ .option('-s, --stage <stage>', 'deployment stage', 'dev')
31
+ .action(deployCommand);
32
+
18
33
  program.parse(process.argv);
19
34
 
20
- module.exports = { installCommand, startCommand };
35
+ module.exports = { installCommand, startCommand, buildCommand, deployCommand };
@@ -1,7 +1,7 @@
1
1
  const { spawn } = require('child_process');
2
2
  const path = require('path');
3
3
 
4
- function startCommand() {
4
+ function startCommand(options) {
5
5
  console.log('Starting backend and optional frontend...');
6
6
  // Suppress AWS SDK warning message about maintenance mode
7
7
  process.env.AWS_SDK_JS_SUPPRESS_MAINTENANCE_MODE_MESSAGE = 1;
@@ -9,7 +9,13 @@ function startCommand() {
9
9
  console.log(`Starting backend in ${backendPath}...`);
10
10
  const infrastructurePath = 'infrastructure.js';
11
11
  const command = 'serverless';
12
- const args = ['offline', '--config', infrastructurePath, '--stage=dev'];
12
+ const args = [
13
+ 'offline',
14
+ '--config',
15
+ infrastructurePath,
16
+ '--stage',
17
+ options.stage
18
+ ];
13
19
 
14
20
  const childProcess = spawn(command, args, {
15
21
  cwd: backendPath,
package/index.js CHANGED
@@ -1,5 +1,7 @@
1
1
  const test = require('./test');
2
+ const { createFriggInfrastructure } = require('./infrastructure');
2
3
 
3
4
  module.exports = {
4
- ...test
5
- }
5
+ createFriggInfrastructure,
6
+ ...test,
7
+ };
@@ -0,0 +1,38 @@
1
+ const path = require('path');
2
+ const fs = require('fs-extra');
3
+ const { composeServerlessDefinition } = require('./serverless-template');
4
+
5
+ const {
6
+ findNearestBackendPackageJson,
7
+ } = require('../frigg-cli/utils/backend-path');
8
+
9
+ function createFriggInfrastructure() {
10
+ const backendPath = findNearestBackendPackageJson();
11
+ if (!backendPath) {
12
+ throw new Error('Could not find backend package.json');
13
+ }
14
+
15
+ const backendDir = path.dirname(backendPath);
16
+ const backendFilePath = path.join(backendDir, 'index.js');
17
+ if (!fs.existsSync(backendFilePath)) {
18
+ throw new Error('Could not find index.js');
19
+ }
20
+
21
+ const backend = require(backendFilePath);
22
+ const appDefinition = backend.Definition;
23
+
24
+ // const serverlessTemplate = require(path.resolve(
25
+ // __dirname,
26
+ // './serverless-template.js'
27
+ // ));
28
+ const definition = composeServerlessDefinition(
29
+ appDefinition,
30
+ backend.IntegrationFactory
31
+ );
32
+
33
+ return {
34
+ ...definition,
35
+ };
36
+ }
37
+
38
+ module.exports = { createFriggInfrastructure };
@@ -0,0 +1,4 @@
1
+ const { createFriggInfrastructure } = require('./create-frigg-infrastructure');
2
+ module.exports = {
3
+ createFriggInfrastructure,
4
+ };
@@ -0,0 +1,283 @@
1
+ const path = require('path');
2
+ const fs = require('fs');
3
+
4
+ const composeServerlessDefinition = (AppDefinition) => {
5
+ const definition = {
6
+ frameworkVersion: '>=3.17.0',
7
+ service: AppDefinition.name || 'create-frigg-app',
8
+ package: {
9
+ individually: true,
10
+ },
11
+ useDotenv: true,
12
+ provider: {
13
+ name: AppDefinition.provider || 'aws',
14
+ runtime: 'nodejs20.x',
15
+ timeout: 30,
16
+ region: 'us-east-1',
17
+ stage: '${opt:stage}',
18
+ environment: {
19
+ STAGE: '${opt:stage}',
20
+ AWS_NODEJS_CONNECTION_REUSE_ENABLED: 1,
21
+ },
22
+ iamRoleStatements: [
23
+ {
24
+ Effect: 'Allow',
25
+ Action: ['sns:Publish'],
26
+ Resource: {
27
+ Ref: 'InternalErrorBridgeTopic',
28
+ },
29
+ },
30
+ ],
31
+ },
32
+ plugins: [
33
+ // 'serverless-webpack',
34
+ 'serverless-dotenv-plugin',
35
+ 'serverless-offline-sqs',
36
+ 'serverless-offline',
37
+ '@friggframework/serverless-plugin',
38
+ ],
39
+ custom: {
40
+ 'serverless-offline': {
41
+ httpPort: 3001,
42
+ lambdaPort: 4001,
43
+ websocketPort: 3002,
44
+ },
45
+ 'serverless-offline-sqs': {
46
+ autoCreate: false,
47
+ apiVersion: '2012-11-05',
48
+ endpoint: 'http://localhost:4566',
49
+ region: 'us-east-1',
50
+ accessKeyId: 'root',
51
+ secretAccessKey: 'root',
52
+ skipCacheInvalidation: false,
53
+ },
54
+ webpack: {
55
+ webpackConfig: 'webpack.config.js',
56
+ includeModules: {
57
+ forceExclude: ['aws-sdk'],
58
+ },
59
+ packager: 'npm',
60
+ excludeFiles: ['src/**/*.test.js', 'test/'],
61
+ },
62
+ },
63
+ functions: {
64
+ defaultWebsocket: {
65
+ handler: 'node_modules/@friggframework/core/handlers/routers/websocket.handler',
66
+ events: [
67
+ {
68
+ websocket: {
69
+ route: '$connect',
70
+ },
71
+ },
72
+ {
73
+ websocket: {
74
+ route: '$default',
75
+ },
76
+ },
77
+ {
78
+ websocket: {
79
+ route: '$disconnect',
80
+ },
81
+ },
82
+ ],
83
+ },
84
+ auth: {
85
+ handler: 'node_modules/@friggframework/core/handlers/routers/auth.handler',
86
+ events: [
87
+ {
88
+ http: {
89
+ path: '/api/integrations',
90
+ method: 'ANY',
91
+ cors: true,
92
+ },
93
+ },
94
+ {
95
+ http: {
96
+ path: '/api/integrations/{proxy+}',
97
+ method: 'ANY',
98
+ cors: true,
99
+ },
100
+ },
101
+ {
102
+ http: {
103
+ path: '/api/authorize',
104
+ method: 'ANY',
105
+ cors: true,
106
+ },
107
+ },
108
+ ],
109
+ },
110
+ user: {
111
+ handler: 'node_modules/@friggframework/core/handlers/routers/user.handler',
112
+ events: [
113
+ {
114
+ http: {
115
+ path: '/user/{proxy+}',
116
+ method: 'ANY',
117
+ cors: true,
118
+ },
119
+ },
120
+ ],
121
+ },
122
+ },
123
+ resources: {
124
+ Resources: {
125
+ InternalErrorQueue: {
126
+ Type: 'AWS::SQS::Queue',
127
+ Properties: {
128
+ QueueName:
129
+ 'internal-error-queue-${self:provider.stage}',
130
+ MessageRetentionPeriod: 300,
131
+ },
132
+ },
133
+ InternalErrorBridgeTopic: {
134
+ Type: 'AWS::SNS::Topic',
135
+ Properties: {
136
+ Subscription: [
137
+ {
138
+ Protocol: 'sqs',
139
+ Endpoint: {
140
+ 'Fn::GetAtt': ['InternalErrorQueue', 'Arn'],
141
+ },
142
+ },
143
+ ],
144
+ },
145
+ },
146
+ InternalErrorBridgePolicy: {
147
+ Type: 'AWS::SQS::QueuePolicy',
148
+ Properties: {
149
+ Queues: [{ Ref: 'InternalErrorQueue' }],
150
+ PolicyDocument: {
151
+ Version: '2012-10-17',
152
+ Statement: [
153
+ {
154
+ Sid: 'Allow Dead Letter SNS to publish to SQS',
155
+ Effect: 'Allow',
156
+ Principal: {
157
+ Service: 'sns.amazonaws.com',
158
+ },
159
+ Resource: {
160
+ 'Fn::GetAtt': [
161
+ 'InternalErrorQueue',
162
+ 'Arn',
163
+ ],
164
+ },
165
+ Action: [
166
+ 'SQS:SendMessage',
167
+ 'SQS:SendMessageBatch',
168
+ ],
169
+ Condition: {
170
+ ArnEquals: {
171
+ 'aws:SourceArn': {
172
+ Ref: 'InternalErrorBridgeTopic',
173
+ },
174
+ },
175
+ },
176
+ },
177
+ ],
178
+ },
179
+ },
180
+ },
181
+ ApiGatewayAlarm5xx: {
182
+ Type: 'AWS::CloudWatch::Alarm',
183
+ Properties: {
184
+ AlarmDescription: 'API Gateway 5xx Errors',
185
+ Namespace: 'AWS/ApiGateway',
186
+ MetricName: '5XXError',
187
+ Statistic: 'Sum',
188
+ Threshold: 0,
189
+ ComparisonOperator: 'GreaterThanThreshold',
190
+ EvaluationPeriods: 1,
191
+ Period: 60,
192
+ AlarmActions: [{ Ref: 'InternalErrorBridgeTopic' }],
193
+ Dimensions: [
194
+ {
195
+ Name: 'ApiName',
196
+ Value: {
197
+ 'Fn::Join': [
198
+ '-',
199
+ [
200
+ '${self:provider.stage}',
201
+ '${self:service}',
202
+ ],
203
+ ],
204
+ },
205
+ },
206
+ ],
207
+ },
208
+ },
209
+ },
210
+ },
211
+ };
212
+
213
+ // Add integration-specific functions and resources
214
+ for (const integration of AppDefinition.integrations) {
215
+ const integrationName = integration.Definition.name;
216
+
217
+ // Add function for the integration
218
+ definition.functions[integrationName] = {
219
+ handler: `node_modules/@friggframework/core/handlers/routers/integration-defined-routers.handlers.${integrationName}.handler`,
220
+ events: [
221
+ {
222
+ http: {
223
+ path: `/api/${integrationName}-integration/{proxy+}`,
224
+ method: 'ANY',
225
+ cors: true,
226
+ },
227
+ },
228
+ ],
229
+ };
230
+
231
+ // Add SQS Queue for the integration
232
+ const queueReference = `${
233
+ integrationName.charAt(0).toUpperCase() + integrationName.slice(1)
234
+ }Queue`;
235
+ const queueName = `\${self:service}--\${self:provider.stage}-${queueReference}`;
236
+ definition.resources.Resources[queueReference] = {
237
+ Type: 'AWS::SQS::Queue',
238
+ Properties: {
239
+ QueueName: `\${self:custom.${queueReference}}`,
240
+ MessageRetentionPeriod: 60,
241
+ VisibilityTimeout: 1800, // 30 minutes
242
+ RedrivePolicy: {
243
+ maxReceiveCount: 1,
244
+ deadLetterTargetArn: {
245
+ 'Fn::GetAtt': ['InternalErrorQueue', 'Arn'],
246
+ },
247
+ },
248
+ },
249
+ };
250
+
251
+ // Add Queue Worker for the integration
252
+ const queueWorkerName = `${integrationName}QueueWorker`;
253
+ definition.functions[queueWorkerName] = {
254
+ handler: `node_modules/@friggframework/core/handlers/workers/integration-defined-workers.handlers.${integrationName}.queueWorker`,
255
+ reservedConcurrency: 5,
256
+ events: [
257
+ {
258
+ sqs: {
259
+ arn: {
260
+ 'Fn::GetAtt': [queueReference, 'Arn'],
261
+ },
262
+ batchSize: 1,
263
+ },
264
+ },
265
+ ],
266
+ timeout: 600,
267
+ };
268
+
269
+ // Add Queue URL for the integration to the ENVironment variables
270
+ definition.provider.environment = {
271
+ ...definition.provider.environment,
272
+ [`${integrationName.toUpperCase()}_QUEUE_URL`]: {
273
+ Ref: queueReference,
274
+ },
275
+ };
276
+
277
+ definition.custom[queueReference] = queueName;
278
+ }
279
+
280
+ return definition;
281
+ };
282
+
283
+ module.exports = { composeServerlessDefinition };
@@ -0,0 +1,20 @@
1
+ const slsw = require('serverless-webpack');
2
+ const webpack = require('webpack');
3
+ const path = require('path');
4
+ const CopyPlugin = require('copy-webpack-plugin');
5
+
6
+
7
+ module.exports = {
8
+ devtool: 'source-map',
9
+ entry: slsw.lib.entries,
10
+ mode: slsw.lib.webpack.isLocal ? 'development' : 'production',
11
+ target: 'node',
12
+ externals: ['mongoose', 'express', 'node-fetch'],
13
+ plugins: [
14
+ // This defines the window global (with value of `undefined`).
15
+ new webpack.ProvidePlugin({
16
+ window: path.resolve(path.join(__dirname, 'src/utils/webpackFakeWindow')),
17
+ }),
18
+ ]
19
+
20
+ };
package/package.json CHANGED
@@ -1,15 +1,18 @@
1
1
  {
2
2
  "name": "@friggframework/devtools",
3
3
  "prettier": "@friggframework/prettier-config",
4
- "version": "2.0.0-next.1",
4
+ "version": "2.0.0-next.10",
5
5
  "dependencies": {
6
6
  "@babel/eslint-parser": "^7.18.9",
7
7
  "@babel/parser": "^7.25.3",
8
8
  "@babel/traverse": "^7.25.3",
9
- "@friggframework/core": "2.0.0-next.1",
10
- "@friggframework/test": "2.0.0-next.1",
9
+ "@friggframework/test": "2.0.0-next.10",
10
+ "@hapi/boom": "^7.4.11",
11
+ "@inquirer/prompts": "^5.3.8",
11
12
  "axios": "^1.7.2",
13
+ "body-parser": "^1.20.2",
12
14
  "commander": "^12.1.0",
15
+ "cors": "^2.8.5",
13
16
  "dotenv": "^16.4.5",
14
17
  "eslint": "^8.22.0",
15
18
  "eslint-config-prettier": "^8.5.0",
@@ -17,12 +20,20 @@
17
20
  "eslint-plugin-markdown": "^3.0.0",
18
21
  "eslint-plugin-no-only-tests": "^3.0.0",
19
22
  "eslint-plugin-yaml": "^0.5.0",
23
+ "express": "^4.19.2",
24
+ "express-async-handler": "^1.2.0",
20
25
  "fs-extra": "^11.2.0",
21
- "inquirer": "^10.1.6"
26
+ "lodash": "^4.17.21",
27
+ "serverless-http": "^2.7.0"
22
28
  },
23
29
  "devDependencies": {
24
- "@friggframework/eslint-config": "2.0.0-next.1",
25
- "@friggframework/prettier-config": "2.0.0-next.1"
30
+ "@friggframework/eslint-config": "2.0.0-next.10",
31
+ "@friggframework/prettier-config": "2.0.0-next.10",
32
+ "serverless": "3.39.0",
33
+ "serverless-dotenv-plugin": "^6.0.0",
34
+ "serverless-offline": "^13.8.0",
35
+ "serverless-offline-sqs": "^8.0.0",
36
+ "serverless-webpack": "^5.14.1"
26
37
  },
27
38
  "scripts": {
28
39
  "lint:fix": "prettier --write --loglevel error . && eslint . --fix",
@@ -46,5 +57,5 @@
46
57
  "publishConfig": {
47
58
  "access": "public"
48
59
  },
49
- "gitHead": "44b619e288b23d879673518076cf9e4acacb3c71"
60
+ "gitHead": "67dc76e94c02c0074f7ff2d7292c4d2203bf3949"
50
61
  }