@actuallyjamez/elysian 0.2.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 +225 -0
- package/dist/cli/commands/build.d.ts +10 -0
- package/dist/cli/commands/build.js +199 -0
- package/dist/cli/commands/dev.d.ts +10 -0
- package/dist/cli/commands/dev.js +120 -0
- package/dist/cli/commands/generate-iac.d.ts +4 -0
- package/dist/cli/commands/generate-iac.js +83 -0
- package/dist/cli/commands/init.d.ts +15 -0
- package/dist/cli/commands/init.js +295 -0
- package/dist/cli/index.d.ts +5 -0
- package/dist/cli/index.js +24 -0
- package/dist/core/bundler.d.ts +18 -0
- package/dist/core/bundler.js +62 -0
- package/dist/core/config.d.ts +72 -0
- package/dist/core/config.js +84 -0
- package/dist/core/handler-wrapper.d.ts +30 -0
- package/dist/core/handler-wrapper.js +143 -0
- package/dist/core/manifest.d.ts +43 -0
- package/dist/core/manifest.js +127 -0
- package/dist/core/naming.d.ts +12 -0
- package/dist/core/naming.js +20 -0
- package/dist/core/openapi.d.ts +23 -0
- package/dist/core/openapi.js +85 -0
- package/dist/core/packager.d.ts +17 -0
- package/dist/core/packager.js +54 -0
- package/dist/core/terraform.d.ts +13 -0
- package/dist/core/terraform.js +44 -0
- package/dist/core/version.d.ts +5 -0
- package/dist/core/version.js +30 -0
- package/dist/index.d.ts +38 -0
- package/dist/index.js +58 -0
- package/dist/runtime/adapter.d.ts +32 -0
- package/dist/runtime/adapter.js +29 -0
- package/dist/runtime/index.d.ts +8 -0
- package/dist/runtime/index.js +7 -0
- package/dist/runtime/types.d.ts +5 -0
- package/dist/runtime/types.js +4 -0
- package/package.json +71 -0
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Init command - Initialize a new elysian project
|
|
3
|
+
*/
|
|
4
|
+
import { defineCommand } from "citty";
|
|
5
|
+
import consola from "consola";
|
|
6
|
+
import { existsSync, mkdirSync } from "fs";
|
|
7
|
+
import { join } from "path";
|
|
8
|
+
export const initCommand = defineCommand({
|
|
9
|
+
meta: {
|
|
10
|
+
name: "init",
|
|
11
|
+
description: "Initialize a new elysian project",
|
|
12
|
+
},
|
|
13
|
+
args: {
|
|
14
|
+
name: {
|
|
15
|
+
type: "string",
|
|
16
|
+
description: "API name",
|
|
17
|
+
default: "my-api",
|
|
18
|
+
},
|
|
19
|
+
force: {
|
|
20
|
+
type: "boolean",
|
|
21
|
+
description: "Overwrite existing files",
|
|
22
|
+
default: false,
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
async run({ args }) {
|
|
26
|
+
const cwd = process.cwd();
|
|
27
|
+
const apiName = args.name;
|
|
28
|
+
consola.start(`Initializing elysian project: ${apiName}`);
|
|
29
|
+
// Check for existing config
|
|
30
|
+
const configPath = join(cwd, "elysian.config.ts");
|
|
31
|
+
if (existsSync(configPath) && !args.force) {
|
|
32
|
+
consola.error("elysian.config.ts already exists. Use --force to overwrite.");
|
|
33
|
+
process.exit(1);
|
|
34
|
+
}
|
|
35
|
+
// Create directories
|
|
36
|
+
const lambdasDir = join(cwd, "src/lambdas");
|
|
37
|
+
const terraformDir = join(cwd, "terraform");
|
|
38
|
+
mkdirSync(lambdasDir, { recursive: true });
|
|
39
|
+
mkdirSync(terraformDir, { recursive: true });
|
|
40
|
+
consola.success("Created src/lambdas/");
|
|
41
|
+
consola.success("Created terraform/");
|
|
42
|
+
// Write config file
|
|
43
|
+
const configContent = `import { defineConfig } from "@actuallyjamez/elysian";
|
|
44
|
+
|
|
45
|
+
export default defineConfig({
|
|
46
|
+
apiName: "${apiName}",
|
|
47
|
+
|
|
48
|
+
// Lambda source directory
|
|
49
|
+
lambdasDir: "src/lambdas",
|
|
50
|
+
|
|
51
|
+
// Build output directory
|
|
52
|
+
outputDir: "dist",
|
|
53
|
+
|
|
54
|
+
// OpenAPI configuration
|
|
55
|
+
openapi: {
|
|
56
|
+
enabled: true,
|
|
57
|
+
title: "${apiName}",
|
|
58
|
+
version: "1.0.0",
|
|
59
|
+
description: "API powered by Elysia and AWS Lambda",
|
|
60
|
+
},
|
|
61
|
+
|
|
62
|
+
// Terraform configuration
|
|
63
|
+
terraform: {
|
|
64
|
+
outputDir: "terraform",
|
|
65
|
+
tfvarsFilename: "api-routes.auto.tfvars",
|
|
66
|
+
},
|
|
67
|
+
|
|
68
|
+
// Lambda defaults
|
|
69
|
+
lambda: {
|
|
70
|
+
runtime: "nodejs20.x",
|
|
71
|
+
memorySize: 256,
|
|
72
|
+
timeout: 30,
|
|
73
|
+
},
|
|
74
|
+
});
|
|
75
|
+
`;
|
|
76
|
+
await Bun.write(configPath, configContent);
|
|
77
|
+
consola.success("Created elysian.config.ts");
|
|
78
|
+
// Write example lambda
|
|
79
|
+
const exampleLambdaPath = join(lambdasDir, "hello.ts");
|
|
80
|
+
if (!existsSync(exampleLambdaPath) || args.force) {
|
|
81
|
+
const exampleLambdaContent = `import { createLambda, t } from "@actuallyjamez/elysian";
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Example Lambda - Hello World
|
|
85
|
+
*
|
|
86
|
+
* Routes defined here will be automatically:
|
|
87
|
+
* - Bundled into a Lambda function
|
|
88
|
+
* - Mapped to API Gateway routes
|
|
89
|
+
* - Included in OpenAPI documentation
|
|
90
|
+
*/
|
|
91
|
+
export default createLambda()
|
|
92
|
+
.get("/hello", ({ query }) => {
|
|
93
|
+
return \`Hello, \${query.name ?? "World"}!\`;
|
|
94
|
+
}, {
|
|
95
|
+
response: t.String(),
|
|
96
|
+
query: t.Object({
|
|
97
|
+
name: t.Optional(t.String()),
|
|
98
|
+
}),
|
|
99
|
+
detail: {
|
|
100
|
+
summary: "Say hello",
|
|
101
|
+
description: "Returns a greeting message",
|
|
102
|
+
tags: ["Greeting"],
|
|
103
|
+
},
|
|
104
|
+
});
|
|
105
|
+
`;
|
|
106
|
+
await Bun.write(exampleLambdaPath, exampleLambdaContent);
|
|
107
|
+
consola.success("Created src/lambdas/hello.ts");
|
|
108
|
+
}
|
|
109
|
+
// Write Terraform main.tf template
|
|
110
|
+
const terraformMainPath = join(terraformDir, "main.tf");
|
|
111
|
+
if (!existsSync(terraformMainPath) || args.force) {
|
|
112
|
+
const terraformContent = `terraform {
|
|
113
|
+
required_providers {
|
|
114
|
+
aws = {
|
|
115
|
+
source = "hashicorp/aws"
|
|
116
|
+
version = "~> 5.0"
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
provider "aws" {
|
|
122
|
+
region = var.region
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
variable "region" {
|
|
126
|
+
type = string
|
|
127
|
+
default = "eu-west-2"
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
variable "lambda_names" {
|
|
131
|
+
type = list(string)
|
|
132
|
+
default = []
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
variable "api_routes" {
|
|
136
|
+
type = map(object({
|
|
137
|
+
lambda_key = string
|
|
138
|
+
route_key = string
|
|
139
|
+
path_parameters = list(string)
|
|
140
|
+
}))
|
|
141
|
+
default = {}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
variable "lambda_runtime" {
|
|
145
|
+
type = string
|
|
146
|
+
default = "nodejs20.x"
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
variable "lambda_memory_size" {
|
|
150
|
+
type = number
|
|
151
|
+
default = 256
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
variable "lambda_timeout" {
|
|
155
|
+
type = number
|
|
156
|
+
default = 30
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
variable "api_name" {
|
|
160
|
+
type = string
|
|
161
|
+
default = "${apiName}"
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
variable "tags" {
|
|
165
|
+
type = map(string)
|
|
166
|
+
default = {}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
locals {
|
|
170
|
+
lambda_functions = {
|
|
171
|
+
for name in var.lambda_names : name => {
|
|
172
|
+
filename = "\${path.module}/../dist/\${name}.zip"
|
|
173
|
+
handler = "index.handler"
|
|
174
|
+
source_code_hash = filebase64sha256("\${path.module}/../dist/\${name}.zip")
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
# API Gateway
|
|
180
|
+
resource "aws_apigatewayv2_api" "this" {
|
|
181
|
+
name = var.api_name
|
|
182
|
+
protocol_type = "HTTP"
|
|
183
|
+
description = "API Gateway for \${var.api_name}"
|
|
184
|
+
tags = var.tags
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
# IAM Role for Lambda
|
|
188
|
+
resource "aws_iam_role" "lambda" {
|
|
189
|
+
name = "\${var.api_name}-lambda-role"
|
|
190
|
+
|
|
191
|
+
assume_role_policy = jsonencode({
|
|
192
|
+
Version = "2012-10-17"
|
|
193
|
+
Statement = [{
|
|
194
|
+
Action = "sts:AssumeRole"
|
|
195
|
+
Effect = "Allow"
|
|
196
|
+
Principal = {
|
|
197
|
+
Service = "lambda.amazonaws.com"
|
|
198
|
+
}
|
|
199
|
+
}]
|
|
200
|
+
})
|
|
201
|
+
|
|
202
|
+
tags = var.tags
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
resource "aws_iam_role_policy_attachment" "lambda_basic" {
|
|
206
|
+
role = aws_iam_role.lambda.name
|
|
207
|
+
policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
# Lambda Functions
|
|
211
|
+
resource "aws_lambda_function" "this" {
|
|
212
|
+
for_each = local.lambda_functions
|
|
213
|
+
|
|
214
|
+
filename = each.value.filename
|
|
215
|
+
function_name = "\${var.api_name}-\${each.key}"
|
|
216
|
+
role = aws_iam_role.lambda.arn
|
|
217
|
+
handler = each.value.handler
|
|
218
|
+
source_code_hash = each.value.source_code_hash
|
|
219
|
+
runtime = var.lambda_runtime
|
|
220
|
+
memory_size = var.lambda_memory_size
|
|
221
|
+
timeout = var.lambda_timeout
|
|
222
|
+
|
|
223
|
+
tags = var.tags
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
# API Gateway Integrations
|
|
227
|
+
resource "aws_apigatewayv2_integration" "this" {
|
|
228
|
+
for_each = local.lambda_functions
|
|
229
|
+
|
|
230
|
+
api_id = aws_apigatewayv2_api.this.id
|
|
231
|
+
integration_type = "AWS_PROXY"
|
|
232
|
+
integration_uri = aws_lambda_function.this[each.key].invoke_arn
|
|
233
|
+
integration_method = "POST"
|
|
234
|
+
payload_format_version = "2.0"
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
# Lambda Permissions
|
|
238
|
+
resource "aws_lambda_permission" "apigateway" {
|
|
239
|
+
for_each = local.lambda_functions
|
|
240
|
+
|
|
241
|
+
statement_id = "AllowExecutionFromAPIGateway-\${each.key}"
|
|
242
|
+
action = "lambda:InvokeFunction"
|
|
243
|
+
function_name = aws_lambda_function.this[each.key].function_name
|
|
244
|
+
principal = "apigateway.amazonaws.com"
|
|
245
|
+
source_arn = "\${aws_apigatewayv2_api.this.execution_arn}/*/*"
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
# API Gateway Routes
|
|
249
|
+
resource "aws_apigatewayv2_route" "this" {
|
|
250
|
+
for_each = var.api_routes
|
|
251
|
+
|
|
252
|
+
api_id = aws_apigatewayv2_api.this.id
|
|
253
|
+
route_key = each.value.route_key
|
|
254
|
+
target = "integrations/\${aws_apigatewayv2_integration.this[each.value.lambda_key].id}"
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
# API Gateway Stage
|
|
258
|
+
resource "aws_apigatewayv2_stage" "this" {
|
|
259
|
+
api_id = aws_apigatewayv2_api.this.id
|
|
260
|
+
name = "$default"
|
|
261
|
+
auto_deploy = true
|
|
262
|
+
|
|
263
|
+
tags = var.tags
|
|
264
|
+
|
|
265
|
+
depends_on = [
|
|
266
|
+
aws_apigatewayv2_route.this,
|
|
267
|
+
aws_lambda_permission.apigateway
|
|
268
|
+
]
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
# Outputs
|
|
272
|
+
output "api_endpoint" {
|
|
273
|
+
value = aws_apigatewayv2_stage.this.invoke_url
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
output "lambda_functions" {
|
|
277
|
+
value = { for k, v in aws_lambda_function.this : k => v.arn }
|
|
278
|
+
}
|
|
279
|
+
`;
|
|
280
|
+
await Bun.write(terraformMainPath, terraformContent);
|
|
281
|
+
consola.success("Created terraform/main.tf");
|
|
282
|
+
}
|
|
283
|
+
// Print next steps
|
|
284
|
+
console.log("");
|
|
285
|
+
consola.box("Project initialized!\n\n" +
|
|
286
|
+
"Next steps:\n\n" +
|
|
287
|
+
"1. Install dependencies:\n" +
|
|
288
|
+
" bun add elysia @actuallyjamez/elysian\n\n" +
|
|
289
|
+
"2. Add more lambdas in src/lambdas/\n\n" +
|
|
290
|
+
"3. Build your lambdas:\n" +
|
|
291
|
+
" bunx elysian build\n\n" +
|
|
292
|
+
"4. Deploy with Terraform:\n" +
|
|
293
|
+
" cd terraform && terraform init && terraform apply");
|
|
294
|
+
},
|
|
295
|
+
});
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
/**
|
|
3
|
+
* elysian CLI entry point
|
|
4
|
+
*/
|
|
5
|
+
import { defineCommand, runMain } from "citty";
|
|
6
|
+
import { buildCommand } from "./commands/build";
|
|
7
|
+
import { devCommand } from "./commands/dev";
|
|
8
|
+
import { initCommand } from "./commands/init";
|
|
9
|
+
import { generateIacCommand } from "./commands/generate-iac";
|
|
10
|
+
import { version } from "../core/version";
|
|
11
|
+
const main = defineCommand({
|
|
12
|
+
meta: {
|
|
13
|
+
name: "elysian",
|
|
14
|
+
version,
|
|
15
|
+
description: "Automatic Lambda bundler for Elysia with API Gateway integration",
|
|
16
|
+
},
|
|
17
|
+
subCommands: {
|
|
18
|
+
build: buildCommand,
|
|
19
|
+
dev: devCommand,
|
|
20
|
+
init: initCommand,
|
|
21
|
+
"generate-iac": generateIacCommand,
|
|
22
|
+
},
|
|
23
|
+
});
|
|
24
|
+
runMain(main);
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lambda bundler using Bun.build
|
|
3
|
+
*/
|
|
4
|
+
import type { ResolvedConfig } from "./config";
|
|
5
|
+
export interface BundleResult {
|
|
6
|
+
name: string;
|
|
7
|
+
outputPath: string;
|
|
8
|
+
success: boolean;
|
|
9
|
+
error?: string;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Bundle a single lambda file
|
|
13
|
+
*/
|
|
14
|
+
export declare function bundleLambda(name: string, inputPath: string, outputDir: string, config: ResolvedConfig): Promise<BundleResult>;
|
|
15
|
+
/**
|
|
16
|
+
* Bundle all lambda files in a directory
|
|
17
|
+
*/
|
|
18
|
+
export declare function bundleAllLambdas(lambdaFiles: string[], lambdasDir: string, outputDir: string, config: ResolvedConfig): Promise<BundleResult[]>;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lambda bundler using Bun.build
|
|
3
|
+
*/
|
|
4
|
+
import { build } from "bun";
|
|
5
|
+
import { join } from "path";
|
|
6
|
+
import { createHandlerWrapperPlugin } from "./handler-wrapper";
|
|
7
|
+
/**
|
|
8
|
+
* Bundle a single lambda file
|
|
9
|
+
*/
|
|
10
|
+
export async function bundleLambda(name, inputPath, outputDir, config) {
|
|
11
|
+
try {
|
|
12
|
+
const result = await build({
|
|
13
|
+
entrypoints: [inputPath],
|
|
14
|
+
outdir: outputDir,
|
|
15
|
+
naming: `${name}.js`,
|
|
16
|
+
format: "esm",
|
|
17
|
+
target: "bun",
|
|
18
|
+
external: config.build.external,
|
|
19
|
+
sourcemap: config.build.sourcemap ? "external" : "none",
|
|
20
|
+
minify: config.build.minify,
|
|
21
|
+
plugins: [createHandlerWrapperPlugin()],
|
|
22
|
+
});
|
|
23
|
+
if (!result.success) {
|
|
24
|
+
const errors = result.logs
|
|
25
|
+
.filter((log) => log.level === "error")
|
|
26
|
+
.map((log) => log.message)
|
|
27
|
+
.join("\n");
|
|
28
|
+
return {
|
|
29
|
+
name,
|
|
30
|
+
outputPath: join(outputDir, `${name}.js`),
|
|
31
|
+
success: false,
|
|
32
|
+
error: errors || "Unknown build error",
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
return {
|
|
36
|
+
name,
|
|
37
|
+
outputPath: join(outputDir, `${name}.js`),
|
|
38
|
+
success: true,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
catch (error) {
|
|
42
|
+
return {
|
|
43
|
+
name,
|
|
44
|
+
outputPath: join(outputDir, `${name}.js`),
|
|
45
|
+
success: false,
|
|
46
|
+
error: error instanceof Error ? error.message : String(error),
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Bundle all lambda files in a directory
|
|
52
|
+
*/
|
|
53
|
+
export async function bundleAllLambdas(lambdaFiles, lambdasDir, outputDir, config) {
|
|
54
|
+
const results = [];
|
|
55
|
+
for (const file of lambdaFiles) {
|
|
56
|
+
const name = file.replace(/\.ts$/, "");
|
|
57
|
+
const inputPath = join(lambdasDir, file);
|
|
58
|
+
const result = await bundleLambda(name, inputPath, outputDir, config);
|
|
59
|
+
results.push(result);
|
|
60
|
+
}
|
|
61
|
+
return results;
|
|
62
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration types and loader for elysian
|
|
3
|
+
*/
|
|
4
|
+
export interface OpenAPIConfig {
|
|
5
|
+
/** Enable OpenAPI auto-aggregation (default: true) */
|
|
6
|
+
enabled?: boolean;
|
|
7
|
+
/** API title for OpenAPI spec */
|
|
8
|
+
title?: string;
|
|
9
|
+
/** API version for OpenAPI spec */
|
|
10
|
+
version?: string;
|
|
11
|
+
/** API description for OpenAPI spec */
|
|
12
|
+
description?: string;
|
|
13
|
+
}
|
|
14
|
+
export interface BuildConfig {
|
|
15
|
+
/** Minify output (default: true in production) */
|
|
16
|
+
minify?: boolean;
|
|
17
|
+
/** Generate sourcemaps (default: true in development) */
|
|
18
|
+
sourcemap?: boolean;
|
|
19
|
+
/** External packages to exclude from bundle (default: ["@aws-sdk/*"]) */
|
|
20
|
+
external?: string[];
|
|
21
|
+
}
|
|
22
|
+
export interface LambdaConfig {
|
|
23
|
+
/** Lambda runtime (default: "nodejs20.x") */
|
|
24
|
+
runtime?: string;
|
|
25
|
+
/** Lambda memory size in MB (default: 256) */
|
|
26
|
+
memorySize?: number;
|
|
27
|
+
/** Lambda timeout in seconds (default: 30) */
|
|
28
|
+
timeout?: number;
|
|
29
|
+
}
|
|
30
|
+
export interface TerraformConfig {
|
|
31
|
+
/** Output directory for Terraform files (default: "terraform") */
|
|
32
|
+
outputDir?: string;
|
|
33
|
+
/** Name for the generated tfvars file (default: "api-routes.auto.tfvars") */
|
|
34
|
+
tfvarsFilename?: string;
|
|
35
|
+
}
|
|
36
|
+
export interface ElysianConfig {
|
|
37
|
+
/** Name of the API (used for resource naming) */
|
|
38
|
+
apiName: string;
|
|
39
|
+
/** Directory containing lambda files (default: "src/lambdas") */
|
|
40
|
+
lambdasDir?: string;
|
|
41
|
+
/** Output directory for built lambdas (default: "dist") */
|
|
42
|
+
outputDir?: string;
|
|
43
|
+
/** OpenAPI configuration */
|
|
44
|
+
openapi?: OpenAPIConfig;
|
|
45
|
+
/** Build configuration */
|
|
46
|
+
build?: BuildConfig;
|
|
47
|
+
/** Lambda defaults */
|
|
48
|
+
lambda?: LambdaConfig;
|
|
49
|
+
/** Terraform configuration */
|
|
50
|
+
terraform?: TerraformConfig;
|
|
51
|
+
}
|
|
52
|
+
export interface ResolvedConfig {
|
|
53
|
+
apiName: string;
|
|
54
|
+
lambdasDir: string;
|
|
55
|
+
outputDir: string;
|
|
56
|
+
openapi: Required<OpenAPIConfig>;
|
|
57
|
+
build: Required<BuildConfig>;
|
|
58
|
+
lambda: Required<LambdaConfig>;
|
|
59
|
+
terraform: Required<TerraformConfig>;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Define configuration with type safety and defaults
|
|
63
|
+
*/
|
|
64
|
+
export declare function defineConfig(config: ElysianConfig): ElysianConfig;
|
|
65
|
+
/**
|
|
66
|
+
* Resolve configuration with defaults applied
|
|
67
|
+
*/
|
|
68
|
+
export declare function resolveConfig(config: ElysianConfig): ResolvedConfig;
|
|
69
|
+
/**
|
|
70
|
+
* Load configuration from elysian.config.ts
|
|
71
|
+
*/
|
|
72
|
+
export declare function loadConfig(cwd?: string): Promise<ResolvedConfig>;
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration types and loader for elysian
|
|
3
|
+
*/
|
|
4
|
+
const DEFAULT_CONFIG = {
|
|
5
|
+
lambdasDir: "src/lambdas",
|
|
6
|
+
outputDir: "dist",
|
|
7
|
+
openapi: {
|
|
8
|
+
enabled: true,
|
|
9
|
+
title: "API",
|
|
10
|
+
version: "1.0.0",
|
|
11
|
+
description: "",
|
|
12
|
+
},
|
|
13
|
+
build: {
|
|
14
|
+
minify: process.env.NODE_ENV === "production",
|
|
15
|
+
sourcemap: process.env.NODE_ENV !== "production",
|
|
16
|
+
external: ["@aws-sdk/*"],
|
|
17
|
+
},
|
|
18
|
+
lambda: {
|
|
19
|
+
runtime: "nodejs20.x",
|
|
20
|
+
memorySize: 256,
|
|
21
|
+
timeout: 30,
|
|
22
|
+
},
|
|
23
|
+
terraform: {
|
|
24
|
+
outputDir: "terraform",
|
|
25
|
+
tfvarsFilename: "api-routes.auto.tfvars",
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* Define configuration with type safety and defaults
|
|
30
|
+
*/
|
|
31
|
+
export function defineConfig(config) {
|
|
32
|
+
return config;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Resolve configuration with defaults applied
|
|
36
|
+
*/
|
|
37
|
+
export function resolveConfig(config) {
|
|
38
|
+
return {
|
|
39
|
+
apiName: config.apiName,
|
|
40
|
+
lambdasDir: config.lambdasDir ?? DEFAULT_CONFIG.lambdasDir,
|
|
41
|
+
outputDir: config.outputDir ?? DEFAULT_CONFIG.outputDir,
|
|
42
|
+
openapi: {
|
|
43
|
+
enabled: config.openapi?.enabled ?? DEFAULT_CONFIG.openapi.enabled,
|
|
44
|
+
title: config.openapi?.title ?? DEFAULT_CONFIG.openapi.title,
|
|
45
|
+
version: config.openapi?.version ?? DEFAULT_CONFIG.openapi.version,
|
|
46
|
+
description: config.openapi?.description ?? DEFAULT_CONFIG.openapi.description,
|
|
47
|
+
},
|
|
48
|
+
build: {
|
|
49
|
+
minify: config.build?.minify ?? DEFAULT_CONFIG.build.minify,
|
|
50
|
+
sourcemap: config.build?.sourcemap ?? DEFAULT_CONFIG.build.sourcemap,
|
|
51
|
+
external: config.build?.external ?? DEFAULT_CONFIG.build.external,
|
|
52
|
+
},
|
|
53
|
+
lambda: {
|
|
54
|
+
runtime: config.lambda?.runtime ?? DEFAULT_CONFIG.lambda.runtime,
|
|
55
|
+
memorySize: config.lambda?.memorySize ?? DEFAULT_CONFIG.lambda.memorySize,
|
|
56
|
+
timeout: config.lambda?.timeout ?? DEFAULT_CONFIG.lambda.timeout,
|
|
57
|
+
},
|
|
58
|
+
terraform: {
|
|
59
|
+
outputDir: config.terraform?.outputDir ?? DEFAULT_CONFIG.terraform.outputDir,
|
|
60
|
+
tfvarsFilename: config.terraform?.tfvarsFilename ??
|
|
61
|
+
DEFAULT_CONFIG.terraform.tfvarsFilename,
|
|
62
|
+
},
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Load configuration from elysian.config.ts
|
|
67
|
+
*/
|
|
68
|
+
export async function loadConfig(cwd = process.cwd()) {
|
|
69
|
+
const configPath = `${cwd}/elysian.config.ts`;
|
|
70
|
+
try {
|
|
71
|
+
const configModule = await import(configPath);
|
|
72
|
+
const config = configModule.default;
|
|
73
|
+
if (!config.apiName) {
|
|
74
|
+
throw new Error("apiName is required in elysian.config.ts");
|
|
75
|
+
}
|
|
76
|
+
return resolveConfig(config);
|
|
77
|
+
}
|
|
78
|
+
catch (error) {
|
|
79
|
+
if (error.code === "ERR_MODULE_NOT_FOUND") {
|
|
80
|
+
throw new Error(`Configuration file not found: ${configPath}\nRun 'elysian init' to create one.`);
|
|
81
|
+
}
|
|
82
|
+
throw error;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bun plugin to inject Lambda handler wrapper for default exports
|
|
3
|
+
*
|
|
4
|
+
* This transforms:
|
|
5
|
+
* export default createLambda().get("/hello", ...)
|
|
6
|
+
*
|
|
7
|
+
* Into:
|
|
8
|
+
* const __elysia_route__ = createLambda().get("/hello", ...)
|
|
9
|
+
* export default __elysia_route__;
|
|
10
|
+
* export const handler = __createHandler(__elysia_route__);
|
|
11
|
+
*/
|
|
12
|
+
import type { BunPlugin } from "bun";
|
|
13
|
+
/**
|
|
14
|
+
* Create a Bun plugin that wraps default Elysia exports with a Lambda handler
|
|
15
|
+
*/
|
|
16
|
+
export declare function createHandlerWrapperPlugin(): BunPlugin;
|
|
17
|
+
/**
|
|
18
|
+
* Transform source code to add handler export
|
|
19
|
+
* This is called after the initial bundle to add the handler wrapper
|
|
20
|
+
*/
|
|
21
|
+
export declare function wrapWithHandler(code: string): string;
|
|
22
|
+
/**
|
|
23
|
+
* Alternative approach: Transform the bundle output directly
|
|
24
|
+
* This rewrites the code to capture the default export and wrap it
|
|
25
|
+
*/
|
|
26
|
+
export declare function transformBundleForLambda(code: string, lambdaName: string): string;
|
|
27
|
+
/**
|
|
28
|
+
* Create a wrapper entry file that imports and re-exports with handler
|
|
29
|
+
*/
|
|
30
|
+
export declare function createWrapperEntry(originalPath: string): string;
|