@friggframework/devtools 2.0.0--canary.657.e372ef6.0 → 2.0.0--canary.639.0335a94.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/index.js +7 -4
- package/frigg-cli/init-command/index.js +19 -6
- package/frigg-cli/templates/backend/README.md +69 -0
- package/frigg-cli/templates/backend/index.js +62 -0
- package/frigg-cli/templates/backend/infrastructure.js +20 -0
- package/infrastructure/domains/integration/integration-builder.test.js +0 -33
- package/infrastructure/domains/scheduler/scheduler-builder.js +1 -7
- package/infrastructure/domains/scheduler/scheduler-builder.test.js +0 -55
- package/infrastructure/domains/shared/utilities/base-definition-factory.js +0 -1
- package/infrastructure/domains/shared/utilities/base-definition-factory.test.js +0 -12
- package/package.json +8 -7
package/frigg-cli/index.js
CHANGED
|
@@ -91,11 +91,14 @@ const { ssmPushCommand } = require('./ssm-command');
|
|
|
91
91
|
const program = new Command();
|
|
92
92
|
|
|
93
93
|
program
|
|
94
|
-
.command('init [
|
|
94
|
+
.command('init [projectName]')
|
|
95
95
|
.description('Initialize a new Frigg application')
|
|
96
|
-
.option('-
|
|
97
|
-
.option('-
|
|
98
|
-
.option('-
|
|
96
|
+
.option('-n, --name <name>', 'project name (alternative to the positional argument)')
|
|
97
|
+
.option('-m, --mode <mode>', 'deployment mode: standalone or embedded')
|
|
98
|
+
.option('--no-frontend', 'skip the optional demo frontend')
|
|
99
|
+
.option('-f, --force', 'scaffold into a non-empty directory')
|
|
100
|
+
.option('-v, --verbose', 'enable verbose output')
|
|
101
|
+
.option('-t, --template <template>', 'legacy template (no longer supported)')
|
|
99
102
|
.action(initCommand);
|
|
100
103
|
|
|
101
104
|
program
|
|
@@ -50,19 +50,32 @@ function checkNodeVersion() {
|
|
|
50
50
|
}
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
-
async function initCommand(projectName, options) {
|
|
53
|
+
async function initCommand(projectName, options = {}) {
|
|
54
54
|
const verbose = options.verbose || false;
|
|
55
55
|
const force = options.force || false;
|
|
56
|
-
|
|
56
|
+
|
|
57
57
|
checkNodeVersion();
|
|
58
58
|
|
|
59
|
-
|
|
59
|
+
// Accept the project name from the positional argument or the --name flag.
|
|
60
|
+
const targetName = projectName || options.name;
|
|
61
|
+
if (!targetName) {
|
|
62
|
+
console.error(
|
|
63
|
+
chalk.red(
|
|
64
|
+
'Please specify a project name:\n' +
|
|
65
|
+
` ${chalk.cyan('frigg init')} ${chalk.green('<project-name>')}\n`
|
|
66
|
+
)
|
|
67
|
+
);
|
|
68
|
+
process.exit(1);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const root = path.resolve(targetName);
|
|
60
72
|
const appName = path.basename(root);
|
|
61
73
|
|
|
62
74
|
checkAppName(appName);
|
|
63
|
-
|
|
64
|
-
//
|
|
65
|
-
|
|
75
|
+
|
|
76
|
+
// A --template flag is legacy and no longer supported; without it, the
|
|
77
|
+
// backend-first handler is the default path for `frigg init`.
|
|
78
|
+
if (!options.template) {
|
|
66
79
|
try {
|
|
67
80
|
const handler = new BackendFirstHandler(root, {
|
|
68
81
|
force,
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# Frigg Backend
|
|
2
|
+
|
|
3
|
+
A [Frigg Framework](https://docs.friggframework.org) backend application, scaffolded with `frigg init`.
|
|
4
|
+
|
|
5
|
+
## Project structure
|
|
6
|
+
|
|
7
|
+
| File | Purpose |
|
|
8
|
+
| --- | --- |
|
|
9
|
+
| `index.js` | Your **app definition** — exports `Definition`, which declares integrations, the database, encryption, users, and VPC settings. This is the file you edit. |
|
|
10
|
+
| `infrastructure.js` | Serverless entry point consumed by `frigg build` / `frigg deploy`. Reads `Definition` from `index.js`; you normally never edit it. |
|
|
11
|
+
|
|
12
|
+
## Getting started
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
# 1. Install dependencies (if not already installed by `frigg init`)
|
|
16
|
+
npm install
|
|
17
|
+
|
|
18
|
+
# 2. Set up the database schema and generate the Prisma client
|
|
19
|
+
frigg db:setup
|
|
20
|
+
|
|
21
|
+
# 3. Run locally
|
|
22
|
+
frigg start
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Add an integration (installs the API module and scaffolds an integration file):
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
frigg install hubspot
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Then register the generated integration class in the `integrations` array in `index.js`.
|
|
32
|
+
|
|
33
|
+
## Configuration
|
|
34
|
+
|
|
35
|
+
The app definition in `index.js` is validated against the Frigg app-definition schema.
|
|
36
|
+
Common settings:
|
|
37
|
+
|
|
38
|
+
- **Database** — enable `postgres` or `mongoDB` under `database`. The connection
|
|
39
|
+
string comes from the `DATABASE_URL` environment variable.
|
|
40
|
+
- **Encryption** — `encryption.fieldLevelEncryptionMethod` is `kms` (recommended
|
|
41
|
+
for production) or `aes`. Encryption is automatically bypassed in the
|
|
42
|
+
`dev`/`test`/`local` stages.
|
|
43
|
+
- **VPC** — set `vpc.enable` to `true` for production deployments in private subnets.
|
|
44
|
+
|
|
45
|
+
### Environment variables
|
|
46
|
+
|
|
47
|
+
Create a `.env` file for local development. Typical values:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
STAGE=dev
|
|
51
|
+
AWS_REGION=us-east-1
|
|
52
|
+
DATABASE_URL=postgresql://postgres:postgres@localhost:5432/frigg
|
|
53
|
+
# For AES encryption in any stage:
|
|
54
|
+
# AES_KEY_ID=local-dev-key
|
|
55
|
+
# AES_KEY=change-me-to-a-32-character-secret
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Deploy
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
frigg build --production # build with AWS resource discovery
|
|
62
|
+
frigg deploy --stage prod # deploy via osls
|
|
63
|
+
frigg doctor <stack-name> # verify the deployed stack
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Learn more
|
|
67
|
+
|
|
68
|
+
- Documentation: https://docs.friggframework.org
|
|
69
|
+
- Issues & support: https://github.com/friggframework/frigg/issues
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Frigg Application Definition
|
|
3
|
+
*
|
|
4
|
+
* This is the entry point for your Frigg backend. The exported `Definition`
|
|
5
|
+
* object is read by `infrastructure.js` (via `createFriggInfrastructure()`) to
|
|
6
|
+
* compose your serverless infrastructure, and by the Frigg runtime to wire up
|
|
7
|
+
* your integrations, database, encryption, and HTTP handlers.
|
|
8
|
+
*
|
|
9
|
+
* Add integrations with `frigg install <module>` (e.g. `frigg install hubspot`),
|
|
10
|
+
* which installs the API module and scaffolds an integration file under
|
|
11
|
+
* `src/integrations/`.
|
|
12
|
+
*
|
|
13
|
+
* See https://docs.friggframework.org for the full app-definition reference.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
'use strict';
|
|
17
|
+
|
|
18
|
+
// Import your integrations here
|
|
19
|
+
// const ExampleIntegration = require('./src/integrations/ExampleIntegration');
|
|
20
|
+
|
|
21
|
+
const appDefinition = {
|
|
22
|
+
// Human-friendly name for this Frigg application.
|
|
23
|
+
name: 'frigg-app',
|
|
24
|
+
|
|
25
|
+
// The integrations this application exposes. Each entry is an
|
|
26
|
+
// IntegrationBase subclass. Install modules with `frigg install <module>`.
|
|
27
|
+
integrations: [
|
|
28
|
+
// Add your integrations here as you install them
|
|
29
|
+
// Example:
|
|
30
|
+
// ExampleIntegration,
|
|
31
|
+
],
|
|
32
|
+
|
|
33
|
+
// User model. Password auth is enabled by default so the Management UI and
|
|
34
|
+
// API can authenticate users out of the box.
|
|
35
|
+
user: {
|
|
36
|
+
usePassword: true,
|
|
37
|
+
},
|
|
38
|
+
|
|
39
|
+
// Field-level encryption for sensitive data (credentials, tokens, mappings).
|
|
40
|
+
// KMS is recommended for production; encryption is automatically bypassed in
|
|
41
|
+
// the dev/test/local stages. Set to 'aes' to use AES with AES_KEY/AES_KEY_ID.
|
|
42
|
+
encryption: {
|
|
43
|
+
fieldLevelEncryptionMethod: 'kms',
|
|
44
|
+
},
|
|
45
|
+
|
|
46
|
+
// Database. Enable exactly one backend. PostgreSQL/Aurora is shown here;
|
|
47
|
+
// set `mongoDB: { enable: true }` instead to use MongoDB. The connection
|
|
48
|
+
// string comes from the DATABASE_URL environment variable.
|
|
49
|
+
database: {
|
|
50
|
+
postgres: {
|
|
51
|
+
enable: true,
|
|
52
|
+
management: 'discover',
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
|
|
56
|
+
// VPC deployment. Enable for production so Lambdas run in private subnets.
|
|
57
|
+
vpc: {
|
|
58
|
+
enable: false,
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
module.exports = { Definition: appDefinition };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Frigg Infrastructure Entry Point
|
|
3
|
+
*
|
|
4
|
+
* This file is the serverless configuration consumed by the Frigg CLI
|
|
5
|
+
* (`frigg build` / `frigg deploy`, which run `osls --config infrastructure.js`).
|
|
6
|
+
*
|
|
7
|
+
* `createFriggInfrastructure()` reads the `Definition` exported from `index.js`,
|
|
8
|
+
* runs AWS resource discovery (VPC, KMS, Aurora, etc.), and composes the full
|
|
9
|
+
* serverless definition. It returns a Promise that resolves to that definition,
|
|
10
|
+
* which Serverless/osls awaits automatically.
|
|
11
|
+
*
|
|
12
|
+
* You normally never need to edit this file — customize your application through
|
|
13
|
+
* the app definition in `index.js` instead.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
'use strict';
|
|
17
|
+
|
|
18
|
+
const { createFriggInfrastructure } = require('@friggframework/devtools');
|
|
19
|
+
|
|
20
|
+
module.exports = createFriggInfrastructure();
|
|
@@ -10,8 +10,6 @@ const {
|
|
|
10
10
|
} = require('@friggframework/core/queues/queue-delivery');
|
|
11
11
|
const { IntegrationBuilder } = require('./integration-builder');
|
|
12
12
|
const { ValidationResult } = require('../shared/base-builder');
|
|
13
|
-
const { BuilderOrchestrator } = require('../shared/builder-orchestrator');
|
|
14
|
-
const { SchedulerBuilder } = require('../scheduler/scheduler-builder');
|
|
15
13
|
|
|
16
14
|
describe('IntegrationBuilder', () => {
|
|
17
15
|
let integrationBuilder;
|
|
@@ -261,37 +259,6 @@ describe('IntegrationBuilder', () => {
|
|
|
261
259
|
).toBeUndefined();
|
|
262
260
|
});
|
|
263
261
|
|
|
264
|
-
it('adds no resource, function or env var when no module declares rateLimit', async () => {
|
|
265
|
-
class Api {}
|
|
266
|
-
const { merged } = await new BuilderOrchestrator([
|
|
267
|
-
new IntegrationBuilder(),
|
|
268
|
-
new SchedulerBuilder(),
|
|
269
|
-
]).buildAll({
|
|
270
|
-
integrations: [
|
|
271
|
-
{
|
|
272
|
-
Definition: {
|
|
273
|
-
name: 'test',
|
|
274
|
-
modules: { crm: { definition: { API: Api } } },
|
|
275
|
-
},
|
|
276
|
-
},
|
|
277
|
-
],
|
|
278
|
-
});
|
|
279
|
-
const before = await integrationBuilder.build(
|
|
280
|
-
{ integrations: [{ Definition: { name: 'test' } }] },
|
|
281
|
-
{}
|
|
282
|
-
);
|
|
283
|
-
|
|
284
|
-
expect(Object.keys(merged.resources)).toEqual(
|
|
285
|
-
Object.keys(before.resources)
|
|
286
|
-
);
|
|
287
|
-
expect(Object.keys(merged.functions)).toEqual(
|
|
288
|
-
Object.keys(before.functions)
|
|
289
|
-
);
|
|
290
|
-
expect(Object.keys(merged.environment)).toEqual(
|
|
291
|
-
Object.keys(before.environment)
|
|
292
|
-
);
|
|
293
|
-
});
|
|
294
|
-
|
|
295
262
|
it('should create queue worker function', async () => {
|
|
296
263
|
const appDefinition = {
|
|
297
264
|
integrations: [
|
|
@@ -19,11 +19,6 @@ const {
|
|
|
19
19
|
getAdminFunctionNames,
|
|
20
20
|
} = require('../shared/function-environments');
|
|
21
21
|
|
|
22
|
-
const declaresRateLimit = (integration) =>
|
|
23
|
-
Object.values(integration?.Definition?.modules ?? {}).some((module) =>
|
|
24
|
-
Boolean(module?.definition?.API?.rateLimit)
|
|
25
|
-
);
|
|
26
|
-
|
|
27
22
|
class SchedulerBuilder extends InfrastructureBuilder {
|
|
28
23
|
constructor() {
|
|
29
24
|
super();
|
|
@@ -42,8 +37,7 @@ class SchedulerBuilder extends InfrastructureBuilder {
|
|
|
42
37
|
return appDefinition.integrations.some(
|
|
43
38
|
(integration) =>
|
|
44
39
|
integration?.Definition?.webhooks?.enabled === true ||
|
|
45
|
-
integration?.Definition?.webhooks === true
|
|
46
|
-
declaresRateLimit(integration)
|
|
40
|
+
integration?.Definition?.webhooks === true
|
|
47
41
|
);
|
|
48
42
|
}
|
|
49
43
|
|
|
@@ -38,61 +38,6 @@ describe('SchedulerBuilder', () => {
|
|
|
38
38
|
).toBe(true);
|
|
39
39
|
});
|
|
40
40
|
|
|
41
|
-
it('runs when any module declares a rate limit', () => {
|
|
42
|
-
class Api {
|
|
43
|
-
static rateLimit = { minRetryAfterMs: 1_000 };
|
|
44
|
-
}
|
|
45
|
-
expect(
|
|
46
|
-
schedulerBuilder.shouldExecute({
|
|
47
|
-
integrations: [
|
|
48
|
-
{ Definition: { name: 'a' } },
|
|
49
|
-
{
|
|
50
|
-
Definition: {
|
|
51
|
-
name: 'b',
|
|
52
|
-
modules: { crm: { definition: { API: Api } } },
|
|
53
|
-
},
|
|
54
|
-
},
|
|
55
|
-
],
|
|
56
|
-
})
|
|
57
|
-
).toBe(true);
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
it('skips a module whose API declares no rate limit', () => {
|
|
61
|
-
class Api {}
|
|
62
|
-
expect(
|
|
63
|
-
schedulerBuilder.shouldExecute({
|
|
64
|
-
integrations: [
|
|
65
|
-
{
|
|
66
|
-
Definition: {
|
|
67
|
-
name: 'a',
|
|
68
|
-
modules: { crm: { definition: { API: Api } } },
|
|
69
|
-
},
|
|
70
|
-
},
|
|
71
|
-
],
|
|
72
|
-
})
|
|
73
|
-
).toBe(false);
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
it.each([
|
|
77
|
-
['no modules', { name: 'a' }],
|
|
78
|
-
['an empty modules object', { name: 'a', modules: {} }],
|
|
79
|
-
[
|
|
80
|
-
'a module with no definition',
|
|
81
|
-
{ name: 'a', modules: { crm: {} } },
|
|
82
|
-
],
|
|
83
|
-
['a null module', { name: 'a', modules: { crm: null } }],
|
|
84
|
-
[
|
|
85
|
-
'a definition with no API',
|
|
86
|
-
{ name: 'a', modules: { crm: { definition: {} } } },
|
|
87
|
-
],
|
|
88
|
-
])('skips an integration with %s', (_label, Definition) => {
|
|
89
|
-
expect(
|
|
90
|
-
schedulerBuilder.shouldExecute({
|
|
91
|
-
integrations: [{ Definition }],
|
|
92
|
-
})
|
|
93
|
-
).toBe(false);
|
|
94
|
-
});
|
|
95
|
-
|
|
96
41
|
it('skips otherwise', () => {
|
|
97
42
|
expect(
|
|
98
43
|
schedulerBuilder.shouldExecute({
|
|
@@ -135,18 +135,6 @@ describe('Base Definition Factory', () => {
|
|
|
135
135
|
expect(sqsPermission).toBeDefined();
|
|
136
136
|
});
|
|
137
137
|
|
|
138
|
-
it('should let a function extend the visibility timeout of a message on an integration queue', () => {
|
|
139
|
-
const result = createBaseDefinition({}, {}, {});
|
|
140
|
-
|
|
141
|
-
const sqsPermission = result.provider.iamRoleStatements.find(
|
|
142
|
-
(stmt) => stmt.Action.includes('sqs:SendMessage')
|
|
143
|
-
);
|
|
144
|
-
expect(sqsPermission.Action).toContain(
|
|
145
|
-
'sqs:ChangeMessageVisibility'
|
|
146
|
-
);
|
|
147
|
-
expect(JSON.stringify(sqsPermission.Resource)).toContain('*Queue');
|
|
148
|
-
});
|
|
149
|
-
|
|
150
138
|
it('should include required plugins', () => {
|
|
151
139
|
const result = createBaseDefinition({}, {}, {});
|
|
152
140
|
|
package/package.json
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@friggframework/devtools",
|
|
3
3
|
"prettier": "@friggframework/prettier-config",
|
|
4
|
-
"version": "2.0.0--canary.
|
|
4
|
+
"version": "2.0.0--canary.639.0335a94.0",
|
|
5
5
|
"bin": {
|
|
6
6
|
"frigg": "./frigg-cli/index.js"
|
|
7
7
|
},
|
|
8
8
|
"files": [
|
|
9
9
|
"frigg-cli/",
|
|
10
|
+
"frigg-cli/templates/",
|
|
10
11
|
"infrastructure/",
|
|
11
12
|
"migrations/",
|
|
12
13
|
"management-ui/dist/",
|
|
@@ -26,9 +27,9 @@
|
|
|
26
27
|
"@babel/eslint-parser": "^7.18.9",
|
|
27
28
|
"@babel/parser": "^7.25.3",
|
|
28
29
|
"@babel/traverse": "^7.25.3",
|
|
29
|
-
"@friggframework/core": "2.0.0--canary.
|
|
30
|
-
"@friggframework/schemas": "2.0.0--canary.
|
|
31
|
-
"@friggframework/test": "2.0.0--canary.
|
|
30
|
+
"@friggframework/core": "2.0.0--canary.639.0335a94.0",
|
|
31
|
+
"@friggframework/schemas": "2.0.0--canary.639.0335a94.0",
|
|
32
|
+
"@friggframework/test": "2.0.0--canary.639.0335a94.0",
|
|
32
33
|
"@hapi/boom": "^10.0.1",
|
|
33
34
|
"@inquirer/prompts": "^5.3.8",
|
|
34
35
|
"axios": "^1.18.0",
|
|
@@ -56,8 +57,8 @@
|
|
|
56
57
|
"validate-npm-package-name": "^5.0.0"
|
|
57
58
|
},
|
|
58
59
|
"devDependencies": {
|
|
59
|
-
"@friggframework/eslint-config": "2.0.0--canary.
|
|
60
|
-
"@friggframework/prettier-config": "2.0.0--canary.
|
|
60
|
+
"@friggframework/eslint-config": "2.0.0--canary.639.0335a94.0",
|
|
61
|
+
"@friggframework/prettier-config": "2.0.0--canary.639.0335a94.0",
|
|
61
62
|
"aws-sdk-client-mock": "^4.1.0",
|
|
62
63
|
"aws-sdk-client-mock-jest": "^4.1.0",
|
|
63
64
|
"jest": "^30.1.3",
|
|
@@ -89,5 +90,5 @@
|
|
|
89
90
|
"publishConfig": {
|
|
90
91
|
"access": "public"
|
|
91
92
|
},
|
|
92
|
-
"gitHead": "
|
|
93
|
+
"gitHead": "0335a948a591f52cd167a421d3ce31d870ce4e43"
|
|
93
94
|
}
|