@lafken/main 0.7.0 → 0.8.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/README.md CHANGED
@@ -1,58 +1,219 @@
1
1
  # @lafken/main
2
2
 
3
- This is the core entry point of an Lafken application. Internally, it initializes the @cdktn/provider-aws, allowing you to register resolvers, modules, and global configuration. It orchestrates the creation of all the infrastructure required for a fully serverless application.
3
+ Core entry point for a Lafken serverless application. `@lafken/main` initializes the AWS provider, orchestrates resolvers and modules, and synthesizes the resulting Terraform configuration through CDKTN. It provides `createApp` to bootstrap the application and `createModule` to organize resources into logical groups.
4
4
 
5
5
  ## Installation
6
6
 
7
-
8
7
  ```bash
9
- npm install @lafken/main
8
+ npm install @lafken/main
10
9
  ```
11
10
 
11
+ ## Getting Started
12
12
 
13
- ## Features
13
+ Create an application with resolvers and modules, then let Lafken generate all the infrastructure:
14
14
 
15
+ ```typescript
16
+ import { createApp, createModule } from '@lafken/main';
17
+ import { ApiResolver } from '@lafken/api/resolver';
18
+ import { QueueResolver } from '@lafken/queue/resolver';
15
19
 
16
- ### createApp
20
+ // 1. Define modules that group related resources
21
+ const userModule = createModule({
22
+ name: 'users',
23
+ resources: [UserApi, UserQueue],
24
+ });
25
+
26
+ const billingModule = createModule({
27
+ name: 'billing',
28
+ resources: [InvoiceSchedule],
29
+ });
17
30
 
31
+ // 2. Create the application
32
+ await createApp({
33
+ name: 'my-app',
34
+ modules: [userModule, billingModule],
35
+ resolvers: [
36
+ new ApiResolver({ restApi: { name: 'my-api' } }),
37
+ new QueueResolver(),
38
+ ],
39
+ });
40
+ ```
41
+
42
+ ## Features
18
43
 
19
- This is the main entry point of the application. It allows you to register the resolvers required for processing your infrastructure, as well as define global configuration.
44
+ ### createApp
20
45
 
21
- Creating an application is straightforward.
46
+ `createApp` is the main entry point. It initializes the AWS stack, runs all resolver lifecycle hooks (`beforeCreate` → `create` → `afterCreate`), and synthesizes the Terraform output:
22
47
 
23
- ```ts
24
- createApp({
25
- name: 'awesome',
26
- modules: [],
27
- resolvers: [],
48
+ ```typescript
49
+ await createApp({
50
+ name: 'my-app',
51
+ modules: [userModule, billingModule],
52
+ resolvers: [new ApiResolver(), new QueueResolver()],
28
53
  globalConfig: {
54
+ lambda: {
55
+ memory: 512,
56
+ timeout: 30,
57
+ runtime: 22,
58
+ services: ['dynamodb', 's3', 'sqs'],
59
+ },
29
60
  tags: {
30
- 'global-tag': 'awesome-tag',
61
+ environment: 'production',
62
+ team: 'platform',
31
63
  },
32
64
  },
33
- extend() {
34
- // extend your application
65
+ awsProviderConfig: {
66
+ region: 'us-east-1',
67
+ profile: 'my-aws-profile',
68
+ },
69
+ s3Backend: {
70
+ bucket: 'my-terraform-state',
71
+ key: 'app/terraform.tfstate',
72
+ region: 'us-east-1',
73
+ },
74
+ extend: async (scope) => {
75
+ // Add custom CDKTN constructs after all resolvers finish
35
76
  },
36
77
  });
37
78
  ```
38
79
 
39
- Just give your application a name, import your resolvers—such as `ApiResolver` or `BucketResolver`—add your modules, and you’re ready to go.
80
+ #### Application Options
81
+
82
+ | Option | Type | Required | Description |
83
+ | ------------------- | ------------------- | -------- | --------------------------------------------------------- |
84
+ | `name` | `string` | Yes | Application name, used as the stack identifier |
85
+ | `modules` | `StackModule[]` | Yes | Modules created with `createModule` |
86
+ | `resolvers` | `ResolverType[]` | Yes | Resolvers that process decorated resources |
87
+ | `globalConfig` | `GlobalConfig` | No | Shared Lambda and tag settings for all resources |
88
+ | `awsProviderConfig` | `AwsProviderConfig` | No | AWS provider settings (region, profile, etc.) |
89
+ | `s3Backend` | `S3BackendConfig` | No | Remote S3 backend for Terraform state |
90
+ | `extend` | `(scope) => void` | No | Callback invoked after all resolvers finish |
40
91
 
41
92
  ### createModule
42
93
 
43
- Each module contains one or more resources. With a module, you can import your classes and define the resources that will be processed by a resolver.
44
- Additionally, you can apply global configuration to all resources managed by the module.
94
+ `createModule` groups related resources into a logical unit with its own scope, IAM role, and configuration. Each resource inside the module is processed by the matching resolver based on its decorator type:
45
95
 
46
- ```ts
47
- createModule({
48
- name: 'awesome-module',
49
- resources: [/** API, QUEUE, SCHEDULE */],
96
+ ```typescript
97
+ const orderModule = createModule({
98
+ name: 'orders',
99
+ resources: [OrderApi, OrderQueue, OrderSchedule],
50
100
  globalConfig: {
51
101
  lambda: {
52
- services: ['cloudwatch', 's3', 'sqs', 'dynamodb'],
102
+ memory: 256,
103
+ timeout: 15,
104
+ services: ['dynamodb', 'sqs'],
105
+ },
106
+ tags: {
107
+ domain: 'orders',
53
108
  },
54
109
  },
55
110
  });
56
111
  ```
57
112
 
58
- This allows you to group related functionality, organize your infrastructure, and apply shared settings across multiple resources.
113
+ #### Module Options
114
+
115
+ | Option | Type | Required | Description |
116
+ | -------------- | ----------------- | -------- | ----------------------------------------------------- |
117
+ | `name` | `string` | Yes | Module name, used as scope and tag identifier |
118
+ | `resources` | `ClassResource[]` | Yes | Decorated classes to be processed by resolvers |
119
+ | `globalConfig` | `GlobalConfig` | No | Lambda and tag settings scoped to this module |
120
+
121
+ ### Global Configuration
122
+
123
+ Global configuration applies default settings to all Lambda functions and resources. Values cascade from application to module to individual resource, with more specific settings taking precedence:
124
+
125
+ ```
126
+ App globalConfig → Module globalConfig → Resource-level config
127
+ ```
128
+
129
+ #### Lambda Configuration
130
+
131
+ | Option | Type | Description |
132
+ | ------------- | ---------------- | --------------------------------------------------------- |
133
+ | `memory` | `number` | Memory allocation in MB |
134
+ | `timeout` | `number` | Execution timeout in seconds |
135
+ | `runtime` | `20 \| 22 \| 24` | Node.js runtime version |
136
+ | `services` | `Services[]` | AWS services the Lambda can access (creates IAM role) |
137
+ | `enableTrace` | `boolean` | Enable AWS X-Ray tracing |
138
+ | `env` | `EnvironmentValue` | Environment variables for Lambda functions |
139
+
140
+ #### Available Services
141
+
142
+ Services define which AWS resources the Lambda IAM role can access:
143
+
144
+ | Service | Description |
145
+ | --------------- | -------------------------------- |
146
+ | `dynamodb` | Amazon DynamoDB |
147
+ | `s3` | Amazon S3 |
148
+ | `lambda` | AWS Lambda |
149
+ | `cloudwatch` | Amazon CloudWatch Logs |
150
+ | `sqs` | Amazon SQS |
151
+ | `state_machine` | AWS Step Functions |
152
+ | `kms` | AWS KMS |
153
+ | `ssm` | AWS Systems Manager Parameter Store |
154
+ | `event` | Amazon EventBridge |
155
+
156
+ For fine-grained control, specify individual permissions:
157
+
158
+ ```typescript
159
+ services: [
160
+ 'cloudwatch',
161
+ { type: 'dynamodb', permissions: ['Query', 'GetItem'] },
162
+ { type: 's3', permissions: ['GetObject'], resources: ['arn:aws:s3:::my-bucket/*'] },
163
+ { type: 'custom', serviceName: 'ses', permissions: ['SendEmail'] },
164
+ ]
165
+ ```
166
+
167
+ #### Tags
168
+
169
+ Tags are applied automatically to all taggable resources. Module-level tags merge with app-level tags, and resource-specific tags take highest precedence:
170
+
171
+ ```typescript
172
+ // App-level tags
173
+ globalConfig: {
174
+ tags: {
175
+ environment: 'production',
176
+ project: 'my-app',
177
+ },
178
+ }
179
+
180
+ // Module-level tags (merged with app tags)
181
+ globalConfig: {
182
+ tags: {
183
+ domain: 'orders',
184
+ },
185
+ }
186
+ ```
187
+
188
+ Lafken also adds automatic tags: `lafken:app` with the app name and `lafken:module` with the module name.
189
+
190
+ ### S3 Backend
191
+
192
+ Store Terraform state remotely in an S3 bucket for team collaboration and state locking:
193
+
194
+ ```typescript
195
+ await createApp({
196
+ name: 'my-app',
197
+ s3Backend: {
198
+ bucket: 'terraform-state-bucket',
199
+ key: 'apps/my-app/terraform.tfstate',
200
+ region: 'us-east-1',
201
+ dynamodbTable: 'terraform-locks',
202
+ },
203
+ });
204
+ ```
205
+
206
+ ### Extending the Application
207
+
208
+ The `extend` callback runs after all resolvers have finished processing. Use it to add custom infrastructure that is not covered by the standard resolvers:
209
+
210
+ ```typescript
211
+ await createApp({
212
+ name: 'my-app',
213
+ modules: [userModule],
214
+ resolvers: [new ApiResolver()],
215
+ extend: async (scope) => {
216
+ // Add any CDKTN construct directly to the stack
217
+ },
218
+ });
219
+ ```
package/lib/app/app.d.ts CHANGED
@@ -10,6 +10,31 @@ export declare class AppStack extends TerraformStack {
10
10
  private createRole;
11
11
  private addAspectProperties;
12
12
  }
13
+ /**
14
+ * Creates and synthesizes a Lafken serverless application.
15
+ *
16
+ * Initializes the CDKTN application, sets up the AWS stack with the
17
+ * provided modules and resolvers, executes the full resolver lifecycle
18
+ * (beforeCreate → create → afterCreate), and synthesizes the resulting
19
+ * Terraform configuration.
20
+ *
21
+ * @param props - The application configuration including name, modules,
22
+ * resolvers, global settings, and optional extensions.
23
+ * @returns The CDKTN `App` instance and the `AppStack` created for the application.
24
+ *
25
+ * @example
26
+ * await createApp({
27
+ * name: 'my-app',
28
+ * modules: [
29
+ * //... ,
30
+ * ],
31
+ * resolvers: [new ApiResolver({ restApi: { name: 'my-api' } })],
32
+ * globalConfig: {
33
+ * lambda: { runtime: 22, memory: 512 },
34
+ * tags: { environment: 'production' },
35
+ * },
36
+ * });
37
+ */
13
38
  export declare const createApp: (props: CreateAppProps) => Promise<{
14
39
  app: App;
15
40
  appStack: AppStack;
package/lib/app/app.js CHANGED
@@ -26,6 +26,9 @@ class AppStack extends cdktn_1.TerraformStack {
26
26
  contextCreator: props.name,
27
27
  });
28
28
  new provider_1.AwsProvider(this, 'AWS', props.awsProviderConfig);
29
+ if (props.s3Backend) {
30
+ new cdktn_1.S3Backend(this, props.s3Backend);
31
+ }
29
32
  this.createRole();
30
33
  }
31
34
  async init() {
@@ -83,6 +86,31 @@ class AppStack extends cdktn_1.TerraformStack {
83
86
  }
84
87
  }
85
88
  exports.AppStack = AppStack;
89
+ /**
90
+ * Creates and synthesizes a Lafken serverless application.
91
+ *
92
+ * Initializes the CDKTN application, sets up the AWS stack with the
93
+ * provided modules and resolvers, executes the full resolver lifecycle
94
+ * (beforeCreate → create → afterCreate), and synthesizes the resulting
95
+ * Terraform configuration.
96
+ *
97
+ * @param props - The application configuration including name, modules,
98
+ * resolvers, global settings, and optional extensions.
99
+ * @returns The CDKTN `App` instance and the `AppStack` created for the application.
100
+ *
101
+ * @example
102
+ * await createApp({
103
+ * name: 'my-app',
104
+ * modules: [
105
+ * //... ,
106
+ * ],
107
+ * resolvers: [new ApiResolver({ restApi: { name: 'my-api' } })],
108
+ * globalConfig: {
109
+ * lambda: { runtime: 22, memory: 512 },
110
+ * tags: { environment: 'production' },
111
+ * },
112
+ * });
113
+ */
86
114
  const createApp = async (props) => {
87
115
  const app = new cdktn_1.App({
88
116
  skipValidation: true,
@@ -1,9 +1,32 @@
1
1
  import type { AwsProviderConfig } from '@cdktn/provider-aws/lib/provider';
2
2
  import type { LambdaGlobalConfig, ResolverType } from '@lafken/resolver';
3
+ import type { S3BackendConfig } from 'cdktn';
3
4
  import type { StackModule } from '../module';
4
5
  import type { ModuleResolverType } from '../module/module.types';
5
6
  import type { AppStack } from './app';
7
+ /**
8
+ * Global configuration for the application.
9
+ *
10
+ * Defines shared settings that apply across all resources and Lambda
11
+ * functions in the application. Individual resources can override
12
+ * these values with their own specific configuration.
13
+ */
6
14
  export interface GlobalConfig {
15
+ /**
16
+ * Global Lambda configuration.
17
+ *
18
+ * Specifies default properties for all Lambda functions in the
19
+ * application, such as memory, timeout, runtime, and services.
20
+ * These values can be overridden at the module or resource level.
21
+ *
22
+ * @example
23
+ * lambda: {
24
+ * memory: 512,
25
+ * timeout: 30,
26
+ * runtime: 22,
27
+ * services: ['s3', 'dynamodb'],
28
+ * }
29
+ */
7
30
  lambda?: LambdaGlobalConfig;
8
31
  /**
9
32
  * Global resource tags.
@@ -29,6 +52,18 @@ export interface CreateAppProps {
29
52
  * Application modules.
30
53
  *
31
54
  * Defines the set of modules to be created within the application.
55
+ * Each module groups related resources and handlers into a logical
56
+ * unit with shared configuration. Modules are created using
57
+ * `createModule()` and receive the application stack scope along
58
+ * with the registered resolvers.
59
+ *
60
+ * @example
61
+ * modules: [
62
+ * createModule({
63
+ * name: 'users',
64
+ * resources: [UserApi, UserQueue],
65
+ * }),
66
+ * ]
32
67
  */
33
68
  modules: ((scope: AppStack, resources: Record<string, ModuleResolverType>) => Promise<StackModule>)[];
34
69
  /**
@@ -52,11 +87,49 @@ export interface CreateAppProps {
52
87
  */
53
88
  globalConfig?: GlobalConfig;
54
89
  /**
90
+ * AWS provider configuration.
55
91
  *
92
+ * Specifies the configuration for the AWS provider used by the
93
+ * application stack. This includes settings such as the AWS region,
94
+ * profile, and other provider-level options required for
95
+ * deploying resources.
96
+ *
97
+ * @example
98
+ * awsProviderConfig: {
99
+ * region: 'us-east-1',
100
+ * profile: 'my-aws-profile',
101
+ * }
56
102
  */
57
103
  awsProviderConfig?: AwsProviderConfig;
58
104
  /**
105
+ * S3 backend configuration for Terraform state.
59
106
  *
107
+ * Configures an S3 bucket as the remote backend for storing the
108
+ * Terraform state file. This enables team collaboration, state
109
+ * locking, and centralized state management.
110
+ *
111
+ * @example
112
+ * s3Backend: {
113
+ * bucket: 'my-terraform-state',
114
+ * key: 'app/terraform.tfstate',
115
+ * region: 'us-east-1',
116
+ * }
117
+ */
118
+ s3Backend?: S3BackendConfig;
119
+ /**
120
+ * Extension callback.
121
+ *
122
+ * An optional async callback that is invoked after all modules and
123
+ * resolvers have been fully processed. Use this to add custom
124
+ * infrastructure or perform additional configuration on the
125
+ * application stack that is not covered by the standard resolvers.
126
+ *
127
+ * @param scope - The application stack instance.
128
+ *
129
+ * @example
130
+ * extend: async (scope) => {
131
+ * new S3Bucket(scope, 'custom-bucket', { bucket: 'my-bucket' });
132
+ * }
60
133
  */
61
134
  extend?: (scope: AppStack) => Promise<void>;
62
135
  }
@@ -8,4 +8,28 @@ export declare class StackModule extends Construct {
8
8
  private createRole;
9
9
  private addAspectProperties;
10
10
  }
11
+ /**
12
+ * Creates a module factory for the Lafken application.
13
+ *
14
+ * Returns a function that, when invoked by `createApp`, instantiates a
15
+ * `StackModule` and processes all its declared resources through the
16
+ * registered resolvers. Each module groups related resources into a
17
+ * logical unit with its own scope, IAM role, tags, and optional
18
+ * Lambda configuration.
19
+ *
20
+ * @param props - The module configuration including name, resources, and
21
+ * optional global settings scoped to this module.
22
+ * @returns A factory function consumed by `createApp` to build the module
23
+ * within the application stack.
24
+ *
25
+ * @example
26
+ * createModule({
27
+ * name: 'users',
28
+ * resources: [UserApi, UserQueue],
29
+ * globalConfig: {
30
+ * lambda: { memory: 256, services: ['dynamodb'] },
31
+ * tags: { team: 'backend' },
32
+ * },
33
+ * })
34
+ */
11
35
  export declare const createModule: (props: CreateModuleProps) => (scope: ModuleConstruct, resolvers: Record<string, ModuleResolverType>) => Promise<StackModule>;
@@ -54,6 +54,30 @@ class StackModule extends constructs_1.Construct {
54
54
  }
55
55
  }
56
56
  exports.StackModule = StackModule;
57
+ /**
58
+ * Creates a module factory for the Lafken application.
59
+ *
60
+ * Returns a function that, when invoked by `createApp`, instantiates a
61
+ * `StackModule` and processes all its declared resources through the
62
+ * registered resolvers. Each module groups related resources into a
63
+ * logical unit with its own scope, IAM role, tags, and optional
64
+ * Lambda configuration.
65
+ *
66
+ * @param props - The module configuration including name, resources, and
67
+ * optional global settings scoped to this module.
68
+ * @returns A factory function consumed by `createApp` to build the module
69
+ * within the application stack.
70
+ *
71
+ * @example
72
+ * createModule({
73
+ * name: 'users',
74
+ * resources: [UserApi, UserQueue],
75
+ * globalConfig: {
76
+ * lambda: { memory: 256, services: ['dynamodb'] },
77
+ * tags: { team: 'backend' },
78
+ * },
79
+ * })
80
+ */
57
81
  const createModule = (props) => async (scope, resolvers) => {
58
82
  const module = new StackModule(scope, props.name, {
59
83
  ...props,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lafken/main",
3
- "version": "0.7.0",
3
+ "version": "0.8.0",
4
4
  "private": false,
5
5
  "description": "Lafken core engine - orchestrate AWS serverless infrastructure using decorators with automatic CDKTN code generation",
6
6
  "keywords": [
@@ -31,17 +31,15 @@
31
31
  "dependencies": {
32
32
  "pretty-error": "^4.0.0",
33
33
  "reflect-metadata": "^0.2.2",
34
- "@lafken/resolver": "0.7.0"
34
+ "@lafken/resolver": "0.8.0"
35
35
  },
36
36
  "devDependencies": {
37
37
  "@cdktn/provider-aws": "^23.0.0",
38
- "@types/jest": "^30.0.0",
39
38
  "cdktn": "^0.22.0",
39
+ "cdktn-vitest": "^1.0.0",
40
40
  "constructs": "^10.4.5",
41
- "jest": "^30.2.0",
42
- "ts-jest": "^29.4.6",
43
- "ts-node": "^10.9.2",
44
- "@lafken/common": "0.7.0"
41
+ "vitest": "^4.0.18",
42
+ "@lafken/common": "0.8.0"
45
43
  },
46
44
  "peerDependencies": {
47
45
  "@cdktn/provider-aws": "^23.0.0",
@@ -57,9 +55,9 @@
57
55
  },
58
56
  "scripts": {
59
57
  "build": "pnpm clean && tsc -p ./tsconfig.build.json",
58
+ "check-types": "tsc --noEmit -p ./tsconfig.build.json",
60
59
  "clean": "rm -rf ./lib",
61
60
  "dev": "tsc -w",
62
- "test": "jest",
63
- "test:coverage": "jest --coverage"
61
+ "test": "vitest"
64
62
  }
65
63
  }