@actuallyjamez/elysian 0.4.0 → 0.5.1
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 +143 -50
- package/dist/cli/commands/build.js +13 -13
- package/dist/cli/commands/dev.js +2 -2
- package/dist/cli/commands/generate-iac.js +3 -3
- package/dist/cli/commands/init/detect.d.ts +1 -1
- package/dist/cli/commands/init/prompts.d.ts +3 -3
- package/dist/cli/commands/init/prompts.js +2 -0
- package/dist/cli/commands/init/scaffold.js +6 -6
- package/dist/cli/commands/init/templates.d.ts +2 -2
- package/dist/cli/commands/init/templates.js +4 -8
- package/dist/cli/commands/init/terraform.d.ts +2 -2
- package/dist/cli/commands/init/terraform.js +5 -5
- package/dist/cli/commands/init.js +19 -12
- package/dist/core/config.d.ts +5 -5
- package/dist/core/config.js +21 -7
- package/dist/core/manifest.d.ts +1 -1
- package/dist/core/manifest.js +5 -5
- package/dist/core/naming.d.ts +7 -6
- package/dist/core/naming.js +9 -8
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,23 +1,31 @@
|
|
|
1
1
|
# elysian
|
|
2
2
|
|
|
3
|
-
Automatic Lambda bundler for [Elysia](https://elysiajs.com/) with AWS API Gateway and Terraform integration.
|
|
3
|
+
> Automatic Lambda bundler for [Elysia](https://elysiajs.com/) with AWS API Gateway and Terraform integration.
|
|
4
4
|
|
|
5
5
|
## Features
|
|
6
6
|
|
|
7
7
|
- **Zero-config Lambda handlers** - Just export your Elysia routes as default, handlers are auto-generated
|
|
8
|
+
- **Interactive init wizard** - Sets up fresh or existing projects with smart defaults
|
|
9
|
+
- **Package manager detection** - Automatically detects bun/npm/pnpm/yarn from lockfiles
|
|
8
10
|
- **Automatic OpenAPI aggregation** - All routes are aggregated into a single OpenAPI spec endpoint
|
|
9
|
-
- **Terraform integration** - Generates
|
|
11
|
+
- **Smart Terraform integration** - Generates modular Terraform files that won't overwrite your existing config
|
|
10
12
|
- **Type-safe configuration** - Full TypeScript support with `defineConfig()`
|
|
11
13
|
- **Watch mode** - Fast rebuilds during development
|
|
12
14
|
|
|
13
15
|
## Installation
|
|
14
16
|
|
|
15
17
|
```bash
|
|
16
|
-
#
|
|
17
|
-
echo "@actuallyjamez:registry=https://npm.pkg.github.com" >> .npmrc
|
|
18
|
-
|
|
19
|
-
# Install
|
|
18
|
+
# With bun (recommended)
|
|
20
19
|
bun add elysia @actuallyjamez/elysian
|
|
20
|
+
|
|
21
|
+
# With npm
|
|
22
|
+
npm install elysia @actuallyjamez/elysian
|
|
23
|
+
|
|
24
|
+
# With pnpm
|
|
25
|
+
pnpm add elysia @actuallyjamez/elysian
|
|
26
|
+
|
|
27
|
+
# With yarn
|
|
28
|
+
yarn add elysia @actuallyjamez/elysian
|
|
21
29
|
```
|
|
22
30
|
|
|
23
31
|
## Quick Start
|
|
@@ -25,13 +33,41 @@ bun add elysia @actuallyjamez/elysian
|
|
|
25
33
|
### 1. Initialize your project
|
|
26
34
|
|
|
27
35
|
```bash
|
|
28
|
-
|
|
36
|
+
# Interactive wizard - prompts for everything you need
|
|
37
|
+
elysian init
|
|
29
38
|
```
|
|
30
39
|
|
|
31
|
-
|
|
32
|
-
-
|
|
33
|
-
-
|
|
34
|
-
-
|
|
40
|
+
The wizard will:
|
|
41
|
+
- Ask where to create the project (default: current directory)
|
|
42
|
+
- Detect your package manager automatically
|
|
43
|
+
- Create all necessary files
|
|
44
|
+
- Install dependencies (fresh projects only)
|
|
45
|
+
|
|
46
|
+
**Fresh project example:**
|
|
47
|
+
```
|
|
48
|
+
? Where would you like to create your project? my-api
|
|
49
|
+
✔ Created directory: my-api
|
|
50
|
+
|
|
51
|
+
ℹ Creating new elysian project: my-api
|
|
52
|
+
|
|
53
|
+
? Package manager: bun
|
|
54
|
+
✔ Created package.json
|
|
55
|
+
✔ Created elysian.config.ts
|
|
56
|
+
✔ Created src/lambdas/hello.ts
|
|
57
|
+
✔ Created terraform/providers.tf
|
|
58
|
+
...
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
**Existing project example:**
|
|
62
|
+
```
|
|
63
|
+
ℹ Adding elysian to: my-existing-app
|
|
64
|
+
|
|
65
|
+
ℹ Detected package manager: bun
|
|
66
|
+
? Install dependencies? Yes
|
|
67
|
+
✔ Created elysian.config.ts
|
|
68
|
+
✔ Updated terraform/variables.tf
|
|
69
|
+
...
|
|
70
|
+
```
|
|
35
71
|
|
|
36
72
|
### 2. Write your lambdas
|
|
37
73
|
|
|
@@ -54,16 +90,16 @@ export default createLambda()
|
|
|
54
90
|
});
|
|
55
91
|
```
|
|
56
92
|
|
|
57
|
-
**That's it!** No need to export a handler -
|
|
93
|
+
**That's it!** No need to export a handler - bundler wraps your default export automatically.
|
|
58
94
|
|
|
59
95
|
### 3. Build
|
|
60
96
|
|
|
61
97
|
```bash
|
|
62
98
|
# Development build
|
|
63
|
-
|
|
99
|
+
elysian build
|
|
64
100
|
|
|
65
101
|
# Production build (minified)
|
|
66
|
-
|
|
102
|
+
elysian build --prod
|
|
67
103
|
```
|
|
68
104
|
|
|
69
105
|
### 4. Deploy
|
|
@@ -82,30 +118,32 @@ Create `elysian.config.ts` in your project root:
|
|
|
82
118
|
import { defineConfig } from "@actuallyjamez/elysian";
|
|
83
119
|
|
|
84
120
|
export default defineConfig({
|
|
85
|
-
// Required
|
|
86
|
-
|
|
121
|
+
// Required: Used for naming your AWS resources
|
|
122
|
+
name: "my-api",
|
|
87
123
|
|
|
88
|
-
// Optional (
|
|
124
|
+
// Optional: Lambda source directory (default: "src/lambdas")
|
|
89
125
|
lambdasDir: "src/lambdas",
|
|
126
|
+
|
|
127
|
+
// Optional: Build output directory (default: "dist")
|
|
90
128
|
outputDir: "dist",
|
|
91
129
|
|
|
92
|
-
// OpenAPI configuration
|
|
130
|
+
// Optional: OpenAPI configuration (default: enabled)
|
|
131
|
+
// Note: title and version have smart defaults
|
|
93
132
|
openapi: {
|
|
94
133
|
enabled: true,
|
|
95
|
-
title
|
|
96
|
-
version
|
|
134
|
+
// title defaults to `name` if not provided
|
|
135
|
+
// version defaults to package.json version if not provided
|
|
97
136
|
description: "API description",
|
|
98
137
|
},
|
|
99
138
|
|
|
100
|
-
// Terraform output
|
|
139
|
+
// Optional: Terraform output directory (default: "terraform")
|
|
101
140
|
terraform: {
|
|
102
141
|
outputDir: "terraform",
|
|
103
|
-
tfvarsFilename: "api-routes.auto.tfvars", // Won't overwrite your existing tfvars
|
|
104
142
|
},
|
|
105
143
|
|
|
106
|
-
// Lambda defaults
|
|
144
|
+
// Optional: Lambda defaults
|
|
107
145
|
lambda: {
|
|
108
|
-
runtime: "
|
|
146
|
+
runtime: "nodejs22.x",
|
|
109
147
|
memorySize: 256,
|
|
110
148
|
timeout: 30,
|
|
111
149
|
},
|
|
@@ -114,37 +152,66 @@ export default defineConfig({
|
|
|
114
152
|
|
|
115
153
|
## CLI Commands
|
|
116
154
|
|
|
155
|
+
### `elysian init`
|
|
156
|
+
|
|
157
|
+
Interactive wizard to initialize a new elysian project.
|
|
158
|
+
|
|
159
|
+
```bash
|
|
160
|
+
# Initialize in current directory
|
|
161
|
+
elysian init
|
|
162
|
+
|
|
163
|
+
# Initialize in subdirectory (creates it if needed)
|
|
164
|
+
elysian init
|
|
165
|
+
# Answer: my-new-api
|
|
166
|
+
|
|
167
|
+
# Force overwrite existing files
|
|
168
|
+
elysian init --force
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
**What it creates (fresh project):**
|
|
172
|
+
- `package.json` - With build scripts
|
|
173
|
+
- `tsconfig.json` - TypeScript configuration
|
|
174
|
+
- `.gitignore` - Ignores node_modules, dist, terraform state
|
|
175
|
+
- `elysian.config.ts` - Elysian configuration
|
|
176
|
+
- `src/lambdas/hello.ts` - Example lambda
|
|
177
|
+
- `terraform/providers.tf` - AWS provider configuration
|
|
178
|
+
- `terraform/variables.tf` - Terraform variables
|
|
179
|
+
- `terraform/main.tf` - Lambda and API Gateway resources
|
|
180
|
+
- `terraform/outputs.tf` - API endpoint output
|
|
181
|
+
|
|
182
|
+
**What it does (existing project):**
|
|
183
|
+
- Detects package manager from lockfiles
|
|
184
|
+
- Creates `elysian.config.ts` (doesn't overwrite if exists)
|
|
185
|
+
- Creates example lambda only if no `.ts` files in `src/lambdas/`
|
|
186
|
+
- Smart-appends to existing Terraform files (won't overwrite your config)
|
|
187
|
+
|
|
117
188
|
### `elysian build`
|
|
118
189
|
|
|
119
190
|
Build all lambdas for deployment.
|
|
120
191
|
|
|
121
192
|
```bash
|
|
122
|
-
|
|
123
|
-
|
|
193
|
+
# Development build
|
|
194
|
+
elysian build
|
|
195
|
+
|
|
196
|
+
# Production build (minified, no sourcemaps)
|
|
197
|
+
elysian build --prod
|
|
124
198
|
```
|
|
125
199
|
|
|
126
200
|
**Output:**
|
|
127
201
|
- `dist/*.js` - Bundled lambda code
|
|
128
202
|
- `dist/*.zip` - Lambda deployment packages
|
|
129
203
|
- `dist/manifest.json` - Route manifest (for debugging)
|
|
130
|
-
- `terraform/api-routes.auto.tfvars` - Terraform variables
|
|
131
204
|
|
|
132
205
|
### `elysian dev`
|
|
133
206
|
|
|
134
207
|
Watch mode for development - rebuilds on file changes.
|
|
135
208
|
|
|
136
209
|
```bash
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
```
|
|
210
|
+
# Watch with packaging
|
|
211
|
+
elysian dev
|
|
140
212
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
Initialize a new project with example files.
|
|
144
|
-
|
|
145
|
-
```bash
|
|
146
|
-
bunx elysian init --name my-api
|
|
147
|
-
bunx elysian init --force # Overwrite existing files
|
|
213
|
+
# Watch without zip creation (faster)
|
|
214
|
+
elysian dev --no-package
|
|
148
215
|
```
|
|
149
216
|
|
|
150
217
|
### `elysian generate-iac`
|
|
@@ -152,15 +219,28 @@ bunx elysian init --force # Overwrite existing files
|
|
|
152
219
|
Regenerate Terraform files without rebuilding lambdas.
|
|
153
220
|
|
|
154
221
|
```bash
|
|
155
|
-
|
|
222
|
+
elysian generate-iac
|
|
156
223
|
```
|
|
157
224
|
|
|
225
|
+
**Smart Terraform file handling:**
|
|
226
|
+
- `providers.tf` - Adds AWS provider if missing
|
|
227
|
+
- `variables.tf` - Adds missing variables only
|
|
228
|
+
- `main.tf` - Adds Lambda/API Gateway resources if missing
|
|
229
|
+
- `outputs.tf` - Adds API endpoint output if missing
|
|
230
|
+
|
|
158
231
|
## How It Works
|
|
159
232
|
|
|
160
233
|
### 1. Route Discovery
|
|
161
234
|
|
|
162
235
|
The bundler scans your `lambdasDir` for `.ts` files. Each file becomes a separate Lambda function.
|
|
163
236
|
|
|
237
|
+
```
|
|
238
|
+
src/lambdas/
|
|
239
|
+
├── users.ts → users.zip → AWS Lambda (users-api)
|
|
240
|
+
├── posts.ts → posts.zip → AWS Lambda (posts-api)
|
|
241
|
+
└── auth.ts → auth.zip → AWS Lambda (auth-api)
|
|
242
|
+
```
|
|
243
|
+
|
|
164
244
|
### 2. Handler Injection
|
|
165
245
|
|
|
166
246
|
When you export an Elysia app as default:
|
|
@@ -187,7 +267,14 @@ An `__openapi__` lambda is automatically generated that imports all your routes
|
|
|
187
267
|
|
|
188
268
|
### 4. Terraform Integration
|
|
189
269
|
|
|
190
|
-
The generated
|
|
270
|
+
The generated Terraform files are modular and won't overwrite your existing configuration:
|
|
271
|
+
|
|
272
|
+
- **providers.tf** - AWS provider with version `~> 6.0`
|
|
273
|
+
- **variables.tf** - All variables (region, lambda config, routes)
|
|
274
|
+
- **main.tf** - API Gateway, Lambda, IAM resources
|
|
275
|
+
- **outputs.tf** - API endpoint URL
|
|
276
|
+
|
|
277
|
+
The `terraform/api-routes.auto.tfvars` file is auto-generated on each build with:
|
|
191
278
|
- List of Lambda names
|
|
192
279
|
- Route-to-Lambda mappings (with API Gateway path format)
|
|
193
280
|
- Lambda configuration defaults
|
|
@@ -197,22 +284,28 @@ The generated `tfvars` file contains:
|
|
|
197
284
|
```
|
|
198
285
|
my-api/
|
|
199
286
|
├── elysian.config.ts # Configuration
|
|
287
|
+
├── package.json # Dependencies & scripts
|
|
288
|
+
├── tsconfig.json # TypeScript config
|
|
289
|
+
├── .gitignore # Git ignore patterns
|
|
200
290
|
├── src/
|
|
201
291
|
│ └── lambdas/
|
|
202
|
-
│ ├── users.ts # → users.zip Lambda
|
|
203
|
-
│ ├── posts.ts # → posts.zip Lambda
|
|
204
|
-
│ └── auth.ts # → auth.zip Lambda
|
|
205
|
-
├── dist/
|
|
206
|
-
│ ├── users.js
|
|
207
|
-
│ ├── users.zip
|
|
208
|
-
│ ├── posts.js
|
|
209
|
-
│ ├── posts.zip
|
|
210
|
-
│ ├── __openapi__.js
|
|
292
|
+
│ ├── users.ts # → my-api-users.zip Lambda
|
|
293
|
+
│ ├── posts.ts # → my-api-posts.zip Lambda
|
|
294
|
+
│ └── auth.ts # → my-api-auth.zip Lambda
|
|
295
|
+
├── dist/ # Build output
|
|
296
|
+
│ ├── my-api-users.js
|
|
297
|
+
│ ├── my-api-users.zip
|
|
298
|
+
│ ├── my-api-posts.js
|
|
299
|
+
│ ├── my-api-posts.zip
|
|
300
|
+
│ ├── __openapi__.js # Auto-generated
|
|
211
301
|
│ ├── __openapi__.zip
|
|
212
302
|
│ └── manifest.json
|
|
213
303
|
└── terraform/
|
|
214
|
-
├──
|
|
215
|
-
|
|
304
|
+
├── providers.tf # AWS provider
|
|
305
|
+
├── variables.tf # Terraform variables
|
|
306
|
+
├── main.tf # Resources (Lambda, API Gateway, IAM)
|
|
307
|
+
├── outputs.tf # Outputs
|
|
308
|
+
└── api-routes.auto.tfvars # Auto-generated on build
|
|
216
309
|
```
|
|
217
310
|
|
|
218
311
|
## Requirements
|
|
@@ -57,7 +57,7 @@ export const buildCommand = defineCommand({
|
|
|
57
57
|
console.log(` ${pc.red("✗")} ${error instanceof Error ? error.message : error}`);
|
|
58
58
|
process.exit(1);
|
|
59
59
|
}
|
|
60
|
-
const
|
|
60
|
+
const name = config.name;
|
|
61
61
|
const lambdasDir = join(process.cwd(), config.lambdasDir);
|
|
62
62
|
const outputDir = join(process.cwd(), config.outputDir);
|
|
63
63
|
const terraformDir = join(process.cwd(), config.terraform.outputDir);
|
|
@@ -85,18 +85,18 @@ export const buildCommand = defineCommand({
|
|
|
85
85
|
mkdirSync(tempDir, { recursive: true });
|
|
86
86
|
const buildResults = [];
|
|
87
87
|
for (const file of lambdaFiles) {
|
|
88
|
-
const
|
|
89
|
-
const bundleName = getLambdaBundleName(
|
|
88
|
+
const lambdaName = file.replace(/\.ts$/, "");
|
|
89
|
+
const bundleName = getLambdaBundleName(name, lambdaName);
|
|
90
90
|
const inputPath = join(lambdasDir, file);
|
|
91
91
|
// Create wrapper entry that imports the original and exports handler
|
|
92
|
-
const wrapperPath = join(tempDir, `${
|
|
92
|
+
const wrapperPath = join(tempDir, `${lambdaName}-wrapper.ts`);
|
|
93
93
|
const wrapperContent = createWrapperEntry(inputPath);
|
|
94
94
|
await Bun.write(wrapperPath, wrapperContent);
|
|
95
95
|
// Bundle the wrapper with prefixed name
|
|
96
96
|
const result = await bundleLambda(bundleName, wrapperPath, outputDir, config);
|
|
97
|
-
buildResults.push({ ...result, name, bundleName });
|
|
97
|
+
buildResults.push({ ...result, name: lambdaName, bundleName });
|
|
98
98
|
if (!result.success) {
|
|
99
|
-
console.log(` ${pc.red("✗")} Failed to build ${
|
|
99
|
+
console.log(` ${pc.red("✗")} Failed to build ${lambdaName}: ${result.error}`);
|
|
100
100
|
process.exit(1);
|
|
101
101
|
}
|
|
102
102
|
}
|
|
@@ -111,25 +111,25 @@ export const buildCommand = defineCommand({
|
|
|
111
111
|
console.log(` ${pc.green("✓")} Packaging lambdas...`);
|
|
112
112
|
const packageSizes = new Map();
|
|
113
113
|
for (const file of lambdaFiles) {
|
|
114
|
-
const
|
|
115
|
-
const bundleName = getLambdaBundleName(
|
|
114
|
+
const lambdaName = file.replace(/\.ts$/, "");
|
|
115
|
+
const bundleName = getLambdaBundleName(name, lambdaName);
|
|
116
116
|
const jsPath = join(outputDir, `${bundleName}.js`);
|
|
117
117
|
const result = await packageLambda(bundleName, jsPath, outputDir);
|
|
118
118
|
if (!result.success) {
|
|
119
|
-
console.log(` ${pc.red("✗")} Failed to package ${
|
|
119
|
+
console.log(` ${pc.red("✗")} Failed to package ${lambdaName}: ${result.error}`);
|
|
120
120
|
process.exit(1);
|
|
121
121
|
}
|
|
122
122
|
// Get zip size (store by original name for display)
|
|
123
123
|
const zipPath = join(outputDir, `${bundleName}.zip`);
|
|
124
124
|
const stat = await Bun.file(zipPath).stat();
|
|
125
125
|
if (stat) {
|
|
126
|
-
packageSizes.set(
|
|
126
|
+
packageSizes.set(lambdaName, stat.size);
|
|
127
127
|
}
|
|
128
128
|
}
|
|
129
129
|
// Generate manifest
|
|
130
130
|
console.log(` ${pc.green("✓")} Generating manifest...`);
|
|
131
131
|
try {
|
|
132
|
-
const manifest = await generateManifest(lambdaFiles, outputDir, config.openapi.enabled,
|
|
132
|
+
const manifest = await generateManifest(lambdaFiles, outputDir, config.openapi.enabled, name);
|
|
133
133
|
// Write JSON manifest
|
|
134
134
|
const manifestPath = join(outputDir, "manifest.json");
|
|
135
135
|
await writeManifest(manifest, manifestPath);
|
|
@@ -145,8 +145,8 @@ export const buildCommand = defineCommand({
|
|
|
145
145
|
const routesByLambda = new Map();
|
|
146
146
|
for (const route of manifest.routes) {
|
|
147
147
|
// Extract display name (original name) from bundle name
|
|
148
|
-
const displayName = route.lambda.startsWith(`${
|
|
149
|
-
? route.lambda.slice(
|
|
148
|
+
const displayName = route.lambda.startsWith(`${name}-`)
|
|
149
|
+
? route.lambda.slice(name.length + 1)
|
|
150
150
|
: route.lambda;
|
|
151
151
|
const existing = routesByLambda.get(displayName) || [];
|
|
152
152
|
existing.push(route);
|
package/dist/cli/commands/dev.js
CHANGED
|
@@ -32,7 +32,7 @@ export const devCommand = defineCommand({
|
|
|
32
32
|
consola.error(error instanceof Error ? error.message : error);
|
|
33
33
|
process.exit(1);
|
|
34
34
|
}
|
|
35
|
-
const
|
|
35
|
+
const name = config.name;
|
|
36
36
|
const lambdasDir = join(process.cwd(), config.lambdasDir);
|
|
37
37
|
const outputDir = join(process.cwd(), config.outputDir);
|
|
38
38
|
const tempDir = join(outputDir, "__temp__");
|
|
@@ -51,7 +51,7 @@ export const devCommand = defineCommand({
|
|
|
51
51
|
// Build function for a single lambda
|
|
52
52
|
async function buildSingleLambda(filename) {
|
|
53
53
|
const name = filename.replace(/\.ts$/, "");
|
|
54
|
-
const bundleName = getLambdaBundleName(
|
|
54
|
+
const bundleName = getLambdaBundleName(name, name);
|
|
55
55
|
const inputPath = join(lambdasDir, filename);
|
|
56
56
|
// Create wrapper entry
|
|
57
57
|
const wrapperPath = join(tempDir, `${name}-wrapper.ts`);
|
|
@@ -25,7 +25,7 @@ export const generateIacCommand = defineCommand({
|
|
|
25
25
|
consola.error(error instanceof Error ? error.message : error);
|
|
26
26
|
process.exit(1);
|
|
27
27
|
}
|
|
28
|
-
const
|
|
28
|
+
const name = config.name;
|
|
29
29
|
const outputDir = join(process.cwd(), config.outputDir);
|
|
30
30
|
const terraformDir = join(process.cwd(), config.terraform.outputDir);
|
|
31
31
|
// Check that build output exists
|
|
@@ -45,14 +45,14 @@ export const generateIacCommand = defineCommand({
|
|
|
45
45
|
// The manifest generator will re-add the prefix
|
|
46
46
|
const lambdaFiles = jsFiles.map((f) => {
|
|
47
47
|
const bundleName = f.replace(/\.js$/, "");
|
|
48
|
-
const originalName = getOriginalLambdaName(
|
|
48
|
+
const originalName = getOriginalLambdaName(name, bundleName);
|
|
49
49
|
return `${originalName}.ts`;
|
|
50
50
|
});
|
|
51
51
|
consola.info(`Found ${lambdaFiles.length} built lambda(s)`);
|
|
52
52
|
// Generate manifest
|
|
53
53
|
consola.start("Generating route manifest...");
|
|
54
54
|
try {
|
|
55
|
-
const manifest = await generateManifest(lambdaFiles, outputDir, config.openapi.enabled,
|
|
55
|
+
const manifest = await generateManifest(lambdaFiles, outputDir, config.openapi.enabled, name);
|
|
56
56
|
// Write JSON manifest
|
|
57
57
|
const manifestPath = join(outputDir, "manifest.json");
|
|
58
58
|
await writeManifest(manifest, manifestPath);
|
|
@@ -24,7 +24,7 @@ export interface ProjectInfo {
|
|
|
24
24
|
hasLambdasDir: boolean;
|
|
25
25
|
/** Whether there are any .ts files in src/lambdas/ */
|
|
26
26
|
hasLambdaFiles: boolean;
|
|
27
|
-
/** Directory name (for default
|
|
27
|
+
/** Directory name (for default name) */
|
|
28
28
|
directoryName: string;
|
|
29
29
|
}
|
|
30
30
|
/**
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
import type { PackageManager } from "./detect";
|
|
5
5
|
export interface WizardAnswers {
|
|
6
6
|
targetDir: string;
|
|
7
|
-
|
|
7
|
+
name: string;
|
|
8
8
|
packageManager: PackageManager;
|
|
9
9
|
installDeps: boolean;
|
|
10
10
|
}
|
|
@@ -16,8 +16,8 @@ export declare function promptTargetDirectory(currentDirName: string): Promise<s
|
|
|
16
16
|
/**
|
|
17
17
|
* Run wizard for a fresh (empty) project
|
|
18
18
|
*/
|
|
19
|
-
export declare function runFreshProjectWizard(apiName: string): Promise<Omit<WizardAnswers, "targetDir"
|
|
19
|
+
export declare function runFreshProjectWizard(apiName: string): Promise<Omit<WizardAnswers, "targetDir"> | null>;
|
|
20
20
|
/**
|
|
21
21
|
* Run wizard for an existing project
|
|
22
22
|
*/
|
|
23
|
-
export declare function runExistingProjectWizard(apiName: string, detectedPackageManager: PackageManager | null): Promise<Omit<WizardAnswers, "targetDir"
|
|
23
|
+
export declare function runExistingProjectWizard(apiName: string, detectedPackageManager: PackageManager | null): Promise<Omit<WizardAnswers, "targetDir"> | null>;
|
|
@@ -46,6 +46,7 @@ export async function runFreshProjectWizard(apiName) {
|
|
|
46
46
|
return null;
|
|
47
47
|
}
|
|
48
48
|
return {
|
|
49
|
+
name: apiName,
|
|
49
50
|
packageManager: packageManager,
|
|
50
51
|
installDeps: true, // Always install for fresh projects
|
|
51
52
|
};
|
|
@@ -87,6 +88,7 @@ export async function runExistingProjectWizard(apiName, detectedPackageManager)
|
|
|
87
88
|
return null;
|
|
88
89
|
}
|
|
89
90
|
return {
|
|
91
|
+
name: apiName,
|
|
90
92
|
packageManager,
|
|
91
93
|
installDeps: installDeps,
|
|
92
94
|
};
|
|
@@ -49,7 +49,7 @@ export async function scaffoldProject(cwd, info, answers, force) {
|
|
|
49
49
|
// For fresh projects, create package.json, .gitignore, tsconfig.json
|
|
50
50
|
if (info.isEmpty) {
|
|
51
51
|
const packageJsonPath = join(cwd, "package.json");
|
|
52
|
-
await writeFile(packageJsonPath, packageJsonTemplate(answers.
|
|
52
|
+
await writeFile(packageJsonPath, packageJsonTemplate(answers.name), result, false);
|
|
53
53
|
const gitignorePath = join(cwd, ".gitignore");
|
|
54
54
|
if (!existsSync(gitignorePath)) {
|
|
55
55
|
await writeFile(gitignorePath, gitignoreTemplate(), result, false);
|
|
@@ -62,7 +62,7 @@ export async function scaffoldProject(cwd, info, answers, force) {
|
|
|
62
62
|
// Create elysian.config.ts
|
|
63
63
|
const configPath = join(cwd, "elysian.config.ts");
|
|
64
64
|
if (!existsSync(configPath) || force) {
|
|
65
|
-
await writeFile(configPath, configTemplate(answers.
|
|
65
|
+
await writeFile(configPath, configTemplate(answers.name), result, existsSync(configPath));
|
|
66
66
|
}
|
|
67
67
|
else {
|
|
68
68
|
result.skipped.push(configPath);
|
|
@@ -73,13 +73,13 @@ export async function scaffoldProject(cwd, info, answers, force) {
|
|
|
73
73
|
await writeFile(exampleLambdaPath, exampleLambdaTemplate(), result, false);
|
|
74
74
|
}
|
|
75
75
|
// Handle Terraform files
|
|
76
|
-
await scaffoldTerraform(cwd, info, answers.
|
|
76
|
+
await scaffoldTerraform(cwd, info, answers.name, result);
|
|
77
77
|
return result;
|
|
78
78
|
}
|
|
79
79
|
/**
|
|
80
80
|
* Scaffold Terraform files with smart appending
|
|
81
81
|
*/
|
|
82
|
-
async function scaffoldTerraform(cwd, info,
|
|
82
|
+
async function scaffoldTerraform(cwd, info, name, result) {
|
|
83
83
|
const tfDir = join(cwd, "terraform");
|
|
84
84
|
// providers.tf
|
|
85
85
|
const providersPath = join(tfDir, "providers.tf");
|
|
@@ -100,7 +100,7 @@ async function scaffoldTerraform(cwd, info, apiName, result) {
|
|
|
100
100
|
const variablesPath = join(tfDir, "variables.tf");
|
|
101
101
|
if (info.terraformFiles.variables) {
|
|
102
102
|
const existing = await readFileOrEmpty(variablesPath);
|
|
103
|
-
const updated = getMissingVariables(existing,
|
|
103
|
+
const updated = getMissingVariables(existing, name);
|
|
104
104
|
if (updated !== existing) {
|
|
105
105
|
await writeFile(variablesPath, updated, result, true);
|
|
106
106
|
}
|
|
@@ -109,7 +109,7 @@ async function scaffoldTerraform(cwd, info, apiName, result) {
|
|
|
109
109
|
}
|
|
110
110
|
}
|
|
111
111
|
else {
|
|
112
|
-
await writeFile(variablesPath, tfTemplates.variables(
|
|
112
|
+
await writeFile(variablesPath, tfTemplates.variables(name), result, false);
|
|
113
113
|
}
|
|
114
114
|
// main.tf
|
|
115
115
|
const mainPath = join(tfDir, "main.tf");
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
/**
|
|
5
5
|
* elysian.config.ts template
|
|
6
6
|
*/
|
|
7
|
-
export declare function configTemplate(
|
|
7
|
+
export declare function configTemplate(name: string): string;
|
|
8
8
|
/**
|
|
9
9
|
* Example lambda template
|
|
10
10
|
*/
|
|
@@ -12,7 +12,7 @@ export declare function exampleLambdaTemplate(): string;
|
|
|
12
12
|
/**
|
|
13
13
|
* package.json template for fresh projects
|
|
14
14
|
*/
|
|
15
|
-
export declare function packageJsonTemplate(
|
|
15
|
+
export declare function packageJsonTemplate(name: string): string;
|
|
16
16
|
/**
|
|
17
17
|
* .gitignore template
|
|
18
18
|
*/
|
|
@@ -4,15 +4,11 @@
|
|
|
4
4
|
/**
|
|
5
5
|
* elysian.config.ts template
|
|
6
6
|
*/
|
|
7
|
-
export function configTemplate(
|
|
7
|
+
export function configTemplate(name) {
|
|
8
8
|
return `import { defineConfig } from "@actuallyjamez/elysian";
|
|
9
9
|
|
|
10
10
|
export default defineConfig({
|
|
11
|
-
|
|
12
|
-
openapi: {
|
|
13
|
-
title: "${apiName}",
|
|
14
|
-
version: "1.0.0",
|
|
15
|
-
},
|
|
11
|
+
name: "${name}",
|
|
16
12
|
});
|
|
17
13
|
`;
|
|
18
14
|
}
|
|
@@ -40,9 +36,9 @@ export default createLambda()
|
|
|
40
36
|
/**
|
|
41
37
|
* package.json template for fresh projects
|
|
42
38
|
*/
|
|
43
|
-
export function packageJsonTemplate(
|
|
39
|
+
export function packageJsonTemplate(name) {
|
|
44
40
|
return JSON.stringify({
|
|
45
|
-
name:
|
|
41
|
+
name: name,
|
|
46
42
|
version: "0.1.0",
|
|
47
43
|
type: "module",
|
|
48
44
|
scripts: {
|
|
@@ -24,7 +24,7 @@ export declare function appendProviders(existing: string): string;
|
|
|
24
24
|
/**
|
|
25
25
|
* Get missing variables and return the block to append
|
|
26
26
|
*/
|
|
27
|
-
export declare function getMissingVariables(existing: string,
|
|
27
|
+
export declare function getMissingVariables(existing: string, name: string): string;
|
|
28
28
|
/**
|
|
29
29
|
* Smart append to main.tf - only add if API Gateway resource is missing
|
|
30
30
|
*/
|
|
@@ -38,7 +38,7 @@ export declare function appendOutputs(existing: string): string;
|
|
|
38
38
|
*/
|
|
39
39
|
export declare const templates: {
|
|
40
40
|
providers: string;
|
|
41
|
-
variables: (
|
|
41
|
+
variables: (name: string) => string;
|
|
42
42
|
main: string;
|
|
43
43
|
outputs: string;
|
|
44
44
|
};
|
|
@@ -58,7 +58,7 @@ export function appendProviders(existing) {
|
|
|
58
58
|
/**
|
|
59
59
|
* Get missing variables and return the block to append
|
|
60
60
|
*/
|
|
61
|
-
export function getMissingVariables(existing,
|
|
61
|
+
export function getMissingVariables(existing, name) {
|
|
62
62
|
const variables = {
|
|
63
63
|
region: `
|
|
64
64
|
variable "region" {
|
|
@@ -95,9 +95,9 @@ variable "lambda_timeout" {
|
|
|
95
95
|
default = 30
|
|
96
96
|
}`,
|
|
97
97
|
api_name: `
|
|
98
|
-
variable "api_name" {
|
|
98
|
+
variable "api_name" {
|
|
99
99
|
type = string
|
|
100
|
-
default = "${
|
|
100
|
+
default = "${name}"
|
|
101
101
|
}`,
|
|
102
102
|
tags: `
|
|
103
103
|
variable "tags" {
|
|
@@ -252,7 +252,7 @@ export function appendOutputs(existing) {
|
|
|
252
252
|
*/
|
|
253
253
|
export const templates = {
|
|
254
254
|
providers: PROVIDERS_BLOCK.trim() + "\n",
|
|
255
|
-
variables: (
|
|
255
|
+
variables: (name) => `# Elysian: Terraform Variables
|
|
256
256
|
|
|
257
257
|
variable "region" {
|
|
258
258
|
type = string
|
|
@@ -290,7 +290,7 @@ variable "lambda_timeout" {
|
|
|
290
290
|
|
|
291
291
|
variable "api_name" {
|
|
292
292
|
type = string
|
|
293
|
-
default = "${
|
|
293
|
+
default = "${name}"
|
|
294
294
|
}
|
|
295
295
|
|
|
296
296
|
variable "tags" {
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import { defineCommand } from "citty";
|
|
5
5
|
import consola from "consola";
|
|
6
|
+
import pc from "picocolors";
|
|
6
7
|
import { existsSync, mkdirSync } from "fs";
|
|
7
8
|
import { resolve, basename } from "path";
|
|
8
9
|
import { detectProject, } from "./init/detect";
|
|
@@ -30,7 +31,7 @@ export const initCommand = defineCommand({
|
|
|
30
31
|
}
|
|
31
32
|
// Resolve the target directory
|
|
32
33
|
const cwd = resolve(originalCwd, targetDir);
|
|
33
|
-
const
|
|
34
|
+
const name = basename(cwd);
|
|
34
35
|
// Create directory if it doesn't exist
|
|
35
36
|
if (!existsSync(cwd)) {
|
|
36
37
|
mkdirSync(cwd, { recursive: true });
|
|
@@ -46,26 +47,24 @@ export const initCommand = defineCommand({
|
|
|
46
47
|
// Run appropriate wizard based on whether directory is empty
|
|
47
48
|
let answers;
|
|
48
49
|
if (info.isEmpty) {
|
|
49
|
-
const result = await runFreshProjectWizard(
|
|
50
|
+
const result = await runFreshProjectWizard(name);
|
|
50
51
|
if (!result) {
|
|
51
52
|
consola.info("Cancelled");
|
|
52
53
|
process.exit(0);
|
|
53
54
|
}
|
|
54
55
|
answers = {
|
|
55
56
|
targetDir,
|
|
56
|
-
apiName,
|
|
57
57
|
...result,
|
|
58
58
|
};
|
|
59
59
|
}
|
|
60
60
|
else {
|
|
61
|
-
const result = await runExistingProjectWizard(
|
|
61
|
+
const result = await runExistingProjectWizard(name, info.packageManager);
|
|
62
62
|
if (!result) {
|
|
63
63
|
consola.info("Cancelled");
|
|
64
64
|
process.exit(0);
|
|
65
65
|
}
|
|
66
66
|
answers = {
|
|
67
67
|
targetDir,
|
|
68
|
-
apiName,
|
|
69
68
|
...result,
|
|
70
69
|
};
|
|
71
70
|
}
|
|
@@ -88,12 +87,20 @@ export const initCommand = defineCommand({
|
|
|
88
87
|
const pm = answers.packageManager;
|
|
89
88
|
const runCmd = pm === "npm" ? "npm run" : pm;
|
|
90
89
|
// If we created in a subdirectory, tell user to cd into it
|
|
91
|
-
const cdStep = targetDir !== "." ? `cd ${targetDir}\n
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
90
|
+
const cdStep = targetDir !== "." ? `cd ${targetDir}\n` : "";
|
|
91
|
+
console.log(` ${pc.green("✓")} Project initialized!`);
|
|
92
|
+
console.log();
|
|
93
|
+
console.log(` ${pc.bold("Next steps")}:`);
|
|
94
|
+
console.log();
|
|
95
|
+
if (cdStep) {
|
|
96
|
+
console.log(` ${cdStep}`);
|
|
97
|
+
}
|
|
98
|
+
if (!answers.installDeps) {
|
|
99
|
+
console.log(` ${pm} add elysia @actuallyjamez/elysian`);
|
|
100
|
+
console.log();
|
|
101
|
+
}
|
|
102
|
+
console.log(` ${runCmd} elysian build`);
|
|
103
|
+
console.log();
|
|
104
|
+
console.log(` cd terraform && terraform init && terraform apply`);
|
|
98
105
|
},
|
|
99
106
|
});
|
package/dist/core/config.d.ts
CHANGED
|
@@ -4,9 +4,9 @@
|
|
|
4
4
|
export interface OpenAPIConfig {
|
|
5
5
|
/** Enable OpenAPI auto-aggregation (default: true) */
|
|
6
6
|
enabled?: boolean;
|
|
7
|
-
/** API title for OpenAPI spec */
|
|
7
|
+
/** API title for OpenAPI spec (defaults to name if not provided) */
|
|
8
8
|
title?: string;
|
|
9
|
-
/** API version for OpenAPI spec */
|
|
9
|
+
/** API version for OpenAPI spec (defaults to package.json version if not provided) */
|
|
10
10
|
version?: string;
|
|
11
11
|
/** API description for OpenAPI spec */
|
|
12
12
|
description?: string;
|
|
@@ -35,7 +35,7 @@ export interface TerraformConfig {
|
|
|
35
35
|
}
|
|
36
36
|
export interface ElysianConfig {
|
|
37
37
|
/** Name of the API (used for resource naming) */
|
|
38
|
-
|
|
38
|
+
name: string;
|
|
39
39
|
/** Directory containing lambda files (default: "src/lambdas") */
|
|
40
40
|
lambdasDir?: string;
|
|
41
41
|
/** Output directory for built lambdas (default: "dist") */
|
|
@@ -50,7 +50,7 @@ export interface ElysianConfig {
|
|
|
50
50
|
terraform?: TerraformConfig;
|
|
51
51
|
}
|
|
52
52
|
export interface ResolvedConfig {
|
|
53
|
-
|
|
53
|
+
name: string;
|
|
54
54
|
lambdasDir: string;
|
|
55
55
|
outputDir: string;
|
|
56
56
|
openapi: Required<OpenAPIConfig>;
|
|
@@ -65,7 +65,7 @@ export declare function defineConfig(config: ElysianConfig): ElysianConfig;
|
|
|
65
65
|
/**
|
|
66
66
|
* Resolve configuration with defaults applied
|
|
67
67
|
*/
|
|
68
|
-
export declare function resolveConfig(config: ElysianConfig): ResolvedConfig
|
|
68
|
+
export declare function resolveConfig(config: ElysianConfig, cwd: string): Promise<ResolvedConfig>;
|
|
69
69
|
/**
|
|
70
70
|
* Load configuration from elysian.config.ts
|
|
71
71
|
*/
|
package/dist/core/config.js
CHANGED
|
@@ -25,6 +25,19 @@ const DEFAULT_CONFIG = {
|
|
|
25
25
|
tfvarsFilename: "api-routes.auto.tfvars",
|
|
26
26
|
},
|
|
27
27
|
};
|
|
28
|
+
/**
|
|
29
|
+
* Read version from package.json
|
|
30
|
+
*/
|
|
31
|
+
async function readPackageVersion(cwd) {
|
|
32
|
+
const packagePath = `${cwd}/package.json`;
|
|
33
|
+
try {
|
|
34
|
+
const content = await Bun.file(packagePath).json();
|
|
35
|
+
return content.version || null;
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
28
41
|
/**
|
|
29
42
|
* Define configuration with type safety and defaults
|
|
30
43
|
*/
|
|
@@ -34,15 +47,16 @@ export function defineConfig(config) {
|
|
|
34
47
|
/**
|
|
35
48
|
* Resolve configuration with defaults applied
|
|
36
49
|
*/
|
|
37
|
-
export function resolveConfig(config) {
|
|
50
|
+
export async function resolveConfig(config, cwd) {
|
|
51
|
+
const pkgVersion = await readPackageVersion(cwd);
|
|
38
52
|
return {
|
|
39
|
-
|
|
53
|
+
name: config.name,
|
|
40
54
|
lambdasDir: config.lambdasDir ?? DEFAULT_CONFIG.lambdasDir,
|
|
41
55
|
outputDir: config.outputDir ?? DEFAULT_CONFIG.outputDir,
|
|
42
56
|
openapi: {
|
|
43
57
|
enabled: config.openapi?.enabled ?? DEFAULT_CONFIG.openapi.enabled,
|
|
44
|
-
title: config.openapi?.title ??
|
|
45
|
-
version: config.openapi?.version ?? DEFAULT_CONFIG.openapi.version,
|
|
58
|
+
title: config.openapi?.title ?? config.name,
|
|
59
|
+
version: config.openapi?.version ?? pkgVersion ?? DEFAULT_CONFIG.openapi.version,
|
|
46
60
|
description: config.openapi?.description ?? DEFAULT_CONFIG.openapi.description,
|
|
47
61
|
},
|
|
48
62
|
build: {
|
|
@@ -70,10 +84,10 @@ export async function loadConfig(cwd = process.cwd()) {
|
|
|
70
84
|
try {
|
|
71
85
|
const configModule = await import(configPath);
|
|
72
86
|
const config = configModule.default;
|
|
73
|
-
if (!config.
|
|
74
|
-
throw new Error("
|
|
87
|
+
if (!config.name) {
|
|
88
|
+
throw new Error("name is required in elysian.config.ts");
|
|
75
89
|
}
|
|
76
|
-
return resolveConfig(config);
|
|
90
|
+
return resolveConfig(config, cwd);
|
|
77
91
|
}
|
|
78
92
|
catch (error) {
|
|
79
93
|
if (error.code === "ERR_MODULE_NOT_FOUND") {
|
package/dist/core/manifest.d.ts
CHANGED
|
@@ -36,7 +36,7 @@ export declare function generateRouteName(lambda: string, method: string, path:
|
|
|
36
36
|
/**
|
|
37
37
|
* Generate manifest by introspecting built lambda modules
|
|
38
38
|
*/
|
|
39
|
-
export declare function generateManifest(lambdaFiles: string[], outputDir: string, openapiEnabled?: boolean,
|
|
39
|
+
export declare function generateManifest(lambdaFiles: string[], outputDir: string, openapiEnabled?: boolean, name?: string): Promise<ApiManifest>;
|
|
40
40
|
/**
|
|
41
41
|
* Write manifest to JSON file
|
|
42
42
|
*/
|
package/dist/core/manifest.js
CHANGED
|
@@ -31,7 +31,7 @@ export function generateRouteName(lambda, method, path) {
|
|
|
31
31
|
/**
|
|
32
32
|
* Generate manifest by introspecting built lambda modules
|
|
33
33
|
*/
|
|
34
|
-
export async function generateManifest(lambdaFiles, outputDir, openapiEnabled = true,
|
|
34
|
+
export async function generateManifest(lambdaFiles, outputDir, openapiEnabled = true, name = "") {
|
|
35
35
|
const manifest = {
|
|
36
36
|
lambdas: [],
|
|
37
37
|
routes: [],
|
|
@@ -43,7 +43,7 @@ export async function generateManifest(lambdaFiles, outputDir, openapiEnabled =
|
|
|
43
43
|
for (const file of sortedFiles) {
|
|
44
44
|
const originalName = file.replace(/\.ts$/, "");
|
|
45
45
|
// Use prefixed bundle name for file lookup
|
|
46
|
-
const bundleName =
|
|
46
|
+
const bundleName = name ? getLambdaBundleName(name, originalName) : originalName;
|
|
47
47
|
const modulePath = isAbsolute(outputDir)
|
|
48
48
|
? join(outputDir, `${bundleName}.js`)
|
|
49
49
|
: join(process.cwd(), outputDir, `${bundleName}.js`);
|
|
@@ -71,12 +71,12 @@ export async function generateManifest(lambdaFiles, outputDir, openapiEnabled =
|
|
|
71
71
|
});
|
|
72
72
|
// Determine which lambda should handle this route
|
|
73
73
|
let targetLambda = bundleName;
|
|
74
|
-
// OpenAPI routes always go to
|
|
74
|
+
// OpenAPI routes always go to __openapi__ lambda if enabled
|
|
75
75
|
if (openapiEnabled && path.startsWith("/openapi")) {
|
|
76
|
-
targetLambda =
|
|
76
|
+
targetLambda = name ? getLambdaBundleName(name, "__openapi__") : "__openapi__";
|
|
77
77
|
}
|
|
78
78
|
else if (originalName === "__openapi__") {
|
|
79
|
-
// Skip non-openapi routes from
|
|
79
|
+
// Skip non-openapi routes from openapi aggregator lambda
|
|
80
80
|
continue;
|
|
81
81
|
}
|
|
82
82
|
// Check for route conflicts
|
package/dist/core/naming.d.ts
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Lambda naming utilities
|
|
3
3
|
*/
|
|
4
4
|
/**
|
|
5
|
-
* Generate
|
|
6
|
-
* Format: {
|
|
5
|
+
* Generate Lambda bundle name with API name prefix
|
|
6
|
+
* Format: {name}-{lambdaName}
|
|
7
7
|
*/
|
|
8
|
-
export declare function getLambdaBundleName(
|
|
8
|
+
export declare function getLambdaBundleName(name: string, lambdaName: string): string;
|
|
9
9
|
/**
|
|
10
|
-
* Extract
|
|
10
|
+
* Extract original lambda name from bundle name
|
|
11
|
+
* Reverses getLambdaBundleName()
|
|
11
12
|
*/
|
|
12
|
-
export declare function getOriginalLambdaName(
|
|
13
|
+
export declare function getOriginalLambdaName(name: string, bundleName: string): string;
|
package/dist/core/naming.js
CHANGED
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Lambda naming utilities
|
|
3
3
|
*/
|
|
4
4
|
/**
|
|
5
|
-
* Generate
|
|
6
|
-
* Format: {
|
|
5
|
+
* Generate Lambda bundle name with API name prefix
|
|
6
|
+
* Format: {name}-{lambdaName}
|
|
7
7
|
*/
|
|
8
|
-
export function getLambdaBundleName(
|
|
9
|
-
return `${
|
|
8
|
+
export function getLambdaBundleName(name, lambdaName) {
|
|
9
|
+
return `${name}-${lambdaName}`;
|
|
10
10
|
}
|
|
11
11
|
/**
|
|
12
|
-
* Extract
|
|
12
|
+
* Extract original lambda name from bundle name
|
|
13
|
+
* Reverses getLambdaBundleName()
|
|
13
14
|
*/
|
|
14
|
-
export function getOriginalLambdaName(
|
|
15
|
-
const prefix = `${
|
|
15
|
+
export function getOriginalLambdaName(name, bundleName) {
|
|
16
|
+
const prefix = `${name}-`;
|
|
16
17
|
if (bundleName.startsWith(prefix)) {
|
|
17
18
|
return bundleName.slice(prefix.length);
|
|
18
19
|
}
|