@rexeus/typeweaver-aws-cdk 0.0.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 ADDED
@@ -0,0 +1,264 @@
1
+ # @rexeus/typeweaver-aws-cdk
2
+
3
+ AWS CDK constructs and deployment utilities for TypeWeaver APIs.
4
+
5
+ ## Overview
6
+
7
+ This plugin generates AWS CDK constructs and HTTP API Gateway routers from your TypeWeaver API
8
+ definitions, making it easy to deploy type-safe APIs to AWS.
9
+
10
+ ## Installation
11
+
12
+ ```bash
13
+ npm install @rexeus/typeweaver-aws-cdk
14
+ ```
15
+
16
+ **Peer Dependencies:**
17
+
18
+ ```bash
19
+ npm install @rexeus/typeweaver-core @rexeus/typeweaver-gen
20
+ ```
21
+
22
+ ## Usage
23
+
24
+ ### CLI
25
+
26
+ ```bash
27
+ npx typeweaver generate --input ./api/definitions --output ./api/generated --plugins aws-cdk
28
+ ```
29
+
30
+ ### Configuration File
31
+
32
+ ```javascript
33
+ // typeweaver.config.js
34
+ export default {
35
+ input: "./api/definitions",
36
+ output: "./api/generated",
37
+ plugins: ["aws-cdk"],
38
+ };
39
+ ```
40
+
41
+ ## Generated Output
42
+
43
+ This plugin generates HTTP API Gateway routers for each entity in your API definitions.
44
+
45
+ ### Example Generated Router
46
+
47
+ For an API definition with a `users` entity, the plugin generates:
48
+
49
+ ```typescript
50
+ // UsersHttpApiRouter.ts
51
+ import { HttpMethod } from "@rexeus/typeweaver-core";
52
+
53
+ export const UsersHttpApiRouter = {
54
+ "/users": [HttpMethod.POST],
55
+ "/users/{userId}": [HttpMethod.GET, HttpMethod.PUT, HttpMethod.DELETE],
56
+ };
57
+ ```
58
+
59
+ ### Router Structure
60
+
61
+ The generated routers provide:
62
+
63
+ - **Route Definitions** - Express-style paths converted to API Gateway format
64
+ - **HTTP Methods** - Supported methods for each route
65
+ - **Parameter Mapping** - Path parameters (`:param` → `{param}`)
66
+
67
+ ### Path Conversion
68
+
69
+ The plugin automatically converts Express-style paths to API Gateway format:
70
+
71
+ - `/users/:userId` → `/users/{userId}`
72
+ - `/users/:userId/posts/:postId` → `/users/{userId}/posts/{postId}`
73
+
74
+ ## AWS CDK Integration
75
+
76
+ Use the generated routers in your CDK stacks:
77
+
78
+ ```typescript
79
+ import { HttpApi, HttpMethod } from "@aws-cdk/aws-apigatewayv2-alpha";
80
+ import { UsersHttpApiRouter, PostsHttpApiRouter } from "./api/generated";
81
+
82
+ export class ApiStack extends Stack {
83
+ constructor(scope: Construct, id: string, props?: StackProps) {
84
+ super(scope, id, props);
85
+
86
+ const api = new HttpApi(this, "TypeWeaverApi");
87
+
88
+ // Add routes from generated routers
89
+ Object.entries(UsersHttpApiRouter).forEach(([path, methods]) => {
90
+ methods.forEach(method => {
91
+ api.addRoutes({
92
+ path,
93
+ methods: [method],
94
+ integration: new HttpLambdaIntegration("UsersIntegration", usersHandler),
95
+ });
96
+ });
97
+ });
98
+
99
+ Object.entries(PostsHttpApiRouter).forEach(([path, methods]) => {
100
+ methods.forEach(method => {
101
+ api.addRoutes({
102
+ path,
103
+ methods: [method],
104
+ integration: new HttpLambdaIntegration("PostsIntegration", postsHandler),
105
+ });
106
+ });
107
+ });
108
+ }
109
+ }
110
+ ```
111
+
112
+ ## Complete AWS Deployment Example
113
+
114
+ ### 1. Define Your API
115
+
116
+ ```typescript
117
+ // api/definitions/users/GetUserDefinition.ts
118
+ import { HttpOperationDefinition, HttpMethod, HttpStatusCode } from "@rexeus/typeweaver-core";
119
+ import { z } from "zod/v4";
120
+
121
+ export default new HttpOperationDefinition({
122
+ operationId: "GetUser",
123
+ method: HttpMethod.GET,
124
+ path: "/users/:userId",
125
+ request: {
126
+ param: z.object({
127
+ userId: z.string(),
128
+ }),
129
+ },
130
+ responses: [
131
+ {
132
+ statusCode: HttpStatusCode.OK,
133
+ body: z.object({
134
+ id: z.string(),
135
+ name: z.string(),
136
+ email: z.email(),
137
+ }),
138
+ },
139
+ ],
140
+ });
141
+ ```
142
+
143
+ ### 2. Generate Code
144
+
145
+ ```bash
146
+ npx typeweaver generate --input ./api/definitions --output ./api/generated --plugins aws-cdk,types,clients
147
+ ```
148
+
149
+ ### 3. Create CDK Stack
150
+
151
+ ```typescript
152
+ // lib/api-stack.ts
153
+ import { Stack, StackProps } from "aws-cdk-lib";
154
+ import { Construct } from "constructs";
155
+ import { HttpApi } from "@aws-cdk/aws-apigatewayv2-alpha";
156
+ import { HttpLambdaIntegration } from "@aws-cdk/aws-apigatewayv2-integrations-alpha";
157
+ import { UsersHttpApiRouter } from "../api/generated/users/UsersHttpApiRouter";
158
+
159
+ export class ApiStack extends Stack {
160
+ constructor(scope: Construct, id: string, props?: StackProps) {
161
+ super(scope, id, props);
162
+
163
+ const api = new HttpApi(this, "TypeWeaverApi");
164
+
165
+ // Create Lambda handler (your implementation)
166
+ const usersHandler = new Function(this, "UsersHandler", {
167
+ // Lambda configuration
168
+ });
169
+
170
+ // Add routes using generated router
171
+ Object.entries(UsersHttpApiRouter).forEach(([path, methods]) => {
172
+ methods.forEach(method => {
173
+ api.addRoutes({
174
+ path,
175
+ methods: [method],
176
+ integration: new HttpLambdaIntegration("UsersIntegration", usersHandler),
177
+ });
178
+ });
179
+ });
180
+ }
181
+ }
182
+ ```
183
+
184
+ ### 4. Deploy
185
+
186
+ ```bash
187
+ cdk deploy
188
+ ```
189
+
190
+ ## Features
191
+
192
+ ### Route Organization
193
+
194
+ - **Entity-based routing** - Each entity gets its own router file
195
+ - **Method grouping** - Routes grouped by HTTP method support
196
+ - **Parameter extraction** - Path parameters automatically identified
197
+
198
+ ### Type Safety
199
+
200
+ - **Generated types** - Work seamlessly with other TypeWeaver plugins
201
+ - **CDK integration** - Type-safe AWS CDK constructs
202
+ - **Validation** - Runtime validation via TypeWeaver Core
203
+
204
+ ### Development Workflow
205
+
206
+ - **Hot reload** - Regenerate routes when API definitions change
207
+ - **Version control** - Generated files can be committed for review
208
+ - **Testing** - Generated routes can be unit tested
209
+
210
+ ## Plugin Architecture
211
+
212
+ This plugin extends the TypeWeaver plugin system:
213
+
214
+ ```typescript
215
+ import { BasePlugin, type GeneratorContext } from "@rexeus/typeweaver-gen";
216
+
217
+ export default class AwsCdkPlugin extends BasePlugin {
218
+ public name = "aws-cdk";
219
+
220
+ public override generate(context: GeneratorContext): void {
221
+ // Generates HTTP API routers for each entity
222
+ }
223
+ }
224
+ ```
225
+
226
+ ## Best Practices
227
+
228
+ ### CDK Integration
229
+
230
+ - Use the generated routers as single source of truth for API routes
231
+ - Combine with other TypeWeaver plugins for complete type safety
232
+ - Version generated files in source control
233
+
234
+ ### Deployment
235
+
236
+ - Generate code before CDK synthesis
237
+ - Use CDK context for environment-specific configuration
238
+ - Implement proper error handling in Lambda functions
239
+
240
+ ### Monitoring
241
+
242
+ - Add CloudWatch metrics for generated routes
243
+ - Implement distributed tracing
244
+ - Monitor API Gateway throttling and errors
245
+
246
+ ## Troubleshooting
247
+
248
+ ### Common Issues
249
+
250
+ **Route conflicts**: Ensure API definitions don't have conflicting paths **Method mismatches**:
251
+ Verify HTTP methods in definitions match usage **Parameter mapping**: Check path parameter names are
252
+ consistent
253
+
254
+ ### Debug Mode
255
+
256
+ Enable verbose logging during generation:
257
+
258
+ ```bash
259
+ DEBUG=typeweaver:* npx typeweaver generate --plugins aws-cdk
260
+ ```
261
+
262
+ ## License
263
+
264
+ ISC © Dennis Wentzien 2025
@@ -0,0 +1,8 @@
1
+ import { BasePlugin, GeneratorContext } from '@rexeus/typeweaver-gen';
2
+
3
+ declare class AwsCdkPlugin extends BasePlugin {
4
+ name: string;
5
+ generate(context: GeneratorContext): Promise<void> | void;
6
+ }
7
+
8
+ export { AwsCdkPlugin as default };
package/dist/index.js ADDED
@@ -0,0 +1,238 @@
1
+ import { BasePlugin } from '@rexeus/typeweaver-gen';
2
+ import path from 'path';
3
+ import { fileURLToPath } from 'url';
4
+
5
+ function getDefaultExportFromCjs (x) {
6
+ return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
7
+ }
8
+
9
+ var Case$2 = {exports: {}};
10
+
11
+ var Case$1 = Case$2.exports;
12
+
13
+ var hasRequiredCase;
14
+
15
+ function requireCase () {
16
+ if (hasRequiredCase) return Case$2.exports;
17
+ hasRequiredCase = 1;
18
+ (function (module) {
19
+ /*! Case - v1.6.2 - 2020-03-24
20
+ * Copyright (c) 2020 Nathan Bubna; Licensed MIT, GPL */
21
+ (function() {
22
+ var unicodes = function(s, prefix) {
23
+ prefix = prefix || "";
24
+ return s.replace(/(^|-)/g, "$1\\u" + prefix).replace(/,/g, "\\u" + prefix);
25
+ }, basicSymbols = unicodes("20-26,28-2F,3A-40,5B-60,7B-7E,A0-BF,D7,F7", "00"), baseLowerCase = "a-z" + unicodes("DF-F6,F8-FF", "00"), baseUpperCase = "A-Z" + unicodes("C0-D6,D8-DE", "00"), improperInTitle = "A|An|And|As|At|But|By|En|For|If|In|Of|On|Or|The|To|Vs?\\.?|Via", regexps = function(symbols, lowers, uppers, impropers) {
26
+ symbols = symbols || basicSymbols;
27
+ lowers = lowers || baseLowerCase;
28
+ uppers = uppers || baseUpperCase;
29
+ impropers = impropers || improperInTitle;
30
+ return {
31
+ capitalize: new RegExp("(^|[" + symbols + "])([" + lowers + "])", "g"),
32
+ pascal: new RegExp("(^|[" + symbols + "])+([" + lowers + uppers + "])", "g"),
33
+ fill: new RegExp("[" + symbols + "]+(.|$)", "g"),
34
+ sentence: new RegExp('(^\\s*|[\\?\\!\\.]+"?\\s+"?|,\\s+")([' + lowers + "])", "g"),
35
+ improper: new RegExp("\\b(" + impropers + ")\\b", "g"),
36
+ relax: new RegExp("([^" + uppers + "])([" + uppers + "]*)([" + uppers + "])(?=[^" + uppers + "]|$)", "g"),
37
+ upper: new RegExp("^[^" + lowers + "]+$"),
38
+ hole: /[^\s]\s[^\s]/,
39
+ apostrophe: /'/g,
40
+ room: new RegExp("[" + symbols + "]")
41
+ };
42
+ }, re = regexps(), _ = {
43
+ re,
44
+ unicodes,
45
+ regexps,
46
+ types: [],
47
+ up: String.prototype.toUpperCase,
48
+ low: String.prototype.toLowerCase,
49
+ cap: function(s) {
50
+ return _.up.call(s.charAt(0)) + s.slice(1);
51
+ },
52
+ decap: function(s) {
53
+ return _.low.call(s.charAt(0)) + s.slice(1);
54
+ },
55
+ deapostrophe: function(s) {
56
+ return s.replace(re.apostrophe, "");
57
+ },
58
+ fill: function(s, fill, deapostrophe) {
59
+ if (fill != null) {
60
+ s = s.replace(re.fill, function(m, next) {
61
+ return next ? fill + next : "";
62
+ });
63
+ }
64
+ if (deapostrophe) {
65
+ s = _.deapostrophe(s);
66
+ }
67
+ return s;
68
+ },
69
+ prep: function(s, fill, pascal, upper) {
70
+ s = s == null ? "" : s + "";
71
+ if (!upper && re.upper.test(s)) {
72
+ s = _.low.call(s);
73
+ }
74
+ if (!fill && !re.hole.test(s)) {
75
+ var holey = _.fill(s, " ");
76
+ if (re.hole.test(holey)) {
77
+ s = holey;
78
+ }
79
+ }
80
+ if (!pascal && !re.room.test(s)) {
81
+ s = s.replace(re.relax, _.relax);
82
+ }
83
+ return s;
84
+ },
85
+ relax: function(m, before, acronym, caps) {
86
+ return before + " " + (acronym ? acronym + " " : "") + caps;
87
+ }
88
+ }, Case = {
89
+ _,
90
+ of: function(s) {
91
+ for (var i = 0, m = _.types.length; i < m; i++) {
92
+ if (Case[_.types[i]].apply(Case, arguments) === s) {
93
+ return _.types[i];
94
+ }
95
+ }
96
+ },
97
+ flip: function(s) {
98
+ return s.replace(/\w/g, function(l) {
99
+ return (l == _.up.call(l) ? _.low : _.up).call(l);
100
+ });
101
+ },
102
+ random: function(s) {
103
+ return s.replace(/\w/g, function(l) {
104
+ return (Math.round(Math.random()) ? _.up : _.low).call(l);
105
+ });
106
+ },
107
+ type: function(type2, fn) {
108
+ Case[type2] = fn;
109
+ _.types.push(type2);
110
+ }
111
+ }, types = {
112
+ lower: function(s, fill, deapostrophe) {
113
+ return _.fill(_.low.call(_.prep(s, fill)), fill, deapostrophe);
114
+ },
115
+ snake: function(s) {
116
+ return Case.lower(s, "_", true);
117
+ },
118
+ constant: function(s) {
119
+ return Case.upper(s, "_", true);
120
+ },
121
+ camel: function(s) {
122
+ return _.decap(Case.pascal(s));
123
+ },
124
+ kebab: function(s) {
125
+ return Case.lower(s, "-", true);
126
+ },
127
+ upper: function(s, fill, deapostrophe) {
128
+ return _.fill(_.up.call(_.prep(s, fill, false, true)), fill, deapostrophe);
129
+ },
130
+ capital: function(s, fill, deapostrophe) {
131
+ return _.fill(_.prep(s).replace(re.capitalize, function(m, border, letter) {
132
+ return border + _.up.call(letter);
133
+ }), fill, deapostrophe);
134
+ },
135
+ header: function(s) {
136
+ return Case.capital(s, "-", true);
137
+ },
138
+ pascal: function(s) {
139
+ return _.fill(_.prep(s, false, true).replace(re.pascal, function(m, border, letter) {
140
+ return _.up.call(letter);
141
+ }), "", true);
142
+ },
143
+ title: function(s) {
144
+ return Case.capital(s).replace(re.improper, function(small, p, i, s2) {
145
+ return i > 0 && i < s2.lastIndexOf(" ") ? _.low.call(small) : small;
146
+ });
147
+ },
148
+ sentence: function(s, names, abbreviations) {
149
+ s = Case.lower(s).replace(re.sentence, function(m, prelude, letter) {
150
+ return prelude + _.up.call(letter);
151
+ });
152
+ if (names) {
153
+ names.forEach(function(name) {
154
+ s = s.replace(new RegExp("\\b" + Case.lower(name) + "\\b", "g"), _.cap);
155
+ });
156
+ }
157
+ if (abbreviations) {
158
+ abbreviations.forEach(function(abbr) {
159
+ s = s.replace(new RegExp("(\\b" + Case.lower(abbr) + "\\. +)(\\w)"), function(m, abbrAndSpace, letter) {
160
+ return abbrAndSpace + _.low.call(letter);
161
+ });
162
+ });
163
+ }
164
+ return s;
165
+ }
166
+ };
167
+ types.squish = types.pascal;
168
+ Case.default = Case;
169
+ for (var type in types) {
170
+ Case.type(type, types[type]);
171
+ }
172
+ var define = typeof define === "function" ? define : function() {
173
+ };
174
+ define(module.exports ? module.exports = Case : this.Case = Case);
175
+ }).call(Case$1);
176
+ } (Case$2));
177
+ return Case$2.exports;
178
+ }
179
+
180
+ var CaseExports = requireCase();
181
+ var Case = /*@__PURE__*/getDefaultExportFromCjs(CaseExports);
182
+
183
+ const __dirname$1 = path.dirname(fileURLToPath(import.meta.url));
184
+ class HttpApiRouterGenerator {
185
+ static generate(context) {
186
+ const templateFile = path.join(__dirname$1, "templates", "HttpApiRouter.ejs");
187
+ for (const [entityName, operationResources] of Object.entries(
188
+ context.resources.entityResources
189
+ )) {
190
+ this.writeHttpApiRoutes(entityName, templateFile, operationResources, context);
191
+ }
192
+ }
193
+ static writeHttpApiRoutes(entityName, templateFile, operationResources, context) {
194
+ const routes = {};
195
+ const pascalCaseEntityName = Case.pascal(entityName);
196
+ const outputDir = operationResources[0].outputDir;
197
+ const outputFile = path.join(
198
+ outputDir,
199
+ `${pascalCaseEntityName}HttpApiRouter.ts`
200
+ );
201
+ for (const operation of operationResources) {
202
+ const path2 = this.createRoutePath(operation.definition.path);
203
+ if (!routes[path2]) {
204
+ routes[path2] = [];
205
+ }
206
+ routes[path2].push(operation.definition.method);
207
+ }
208
+ const content = context.renderTemplate(templateFile, {
209
+ entityName,
210
+ pascalCaseEntityName,
211
+ routes,
212
+ coreDir: context.coreDir
213
+ });
214
+ const relativePath = path.relative(context.outputDir, outputFile);
215
+ context.writeFile(relativePath, content);
216
+ }
217
+ static createRoutePath(path2) {
218
+ const parts = path2.split("/").map((part) => {
219
+ if (part.startsWith(":")) {
220
+ return `{${part.slice(1)}}`;
221
+ }
222
+ return part;
223
+ });
224
+ return parts.join("/");
225
+ }
226
+ }
227
+
228
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
229
+ class AwsCdkPlugin extends BasePlugin {
230
+ name = "aws-cdk";
231
+ generate(context) {
232
+ const libDir = path.join(__dirname, "lib");
233
+ this.copyLibFiles(context, libDir, "aws-cdk");
234
+ HttpApiRouterGenerator.generate(context);
235
+ }
236
+ }
237
+
238
+ export { AwsCdkPlugin as default };
@@ -0,0 +1,10 @@
1
+ import { HttpMethod } from "@rexeus/typeweaver-core";
2
+
3
+ export type AwsHttpApiGatewayRoute = {
4
+ path: string;
5
+ methods: HttpMethod[];
6
+ };
7
+
8
+ export abstract class AwsHttpApiGatewayRouter {
9
+ public abstract getRoutes(): AwsHttpApiGatewayRoute[];
10
+ }
@@ -0,0 +1 @@
1
+ export * from "./AwsHttpApiGatewayRouter";
@@ -0,0 +1,24 @@
1
+ import { HttpMethod } from "<%= coreDir %>";
2
+ import {
3
+ type AwsHttpApiGatewayRoute,
4
+ AwsHttpApiGatewayRouter,
5
+ } from "../lib/aws-cdk";
6
+
7
+ export class <%= pascalCaseEntityName %>HttpApiRouter extends AwsHttpApiGatewayRouter {
8
+ private routes: AwsHttpApiGatewayRoute[] = [
9
+ <% for(const [path, methods] of Object.entries(routes)) { %>
10
+ {
11
+ path: "<%= path %>",
12
+ methods: [<% for(const method of methods) { %>HttpMethod.<%= method %>,<% } %>],
13
+ },
14
+ <% } %>
15
+ ];
16
+
17
+ public constructor() {
18
+ super();
19
+ }
20
+
21
+ public getRoutes(): AwsHttpApiGatewayRoute[] {
22
+ return this.routes;
23
+ }
24
+ }
package/package.json ADDED
@@ -0,0 +1,47 @@
1
+ {
2
+ "name": "@rexeus/typeweaver-aws-cdk",
3
+ "version": "0.0.1",
4
+ "description": "AWS CDK constructs and deployment utilities for TypeWeaver APIs",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "files": [
9
+ "dist",
10
+ "package.json",
11
+ "README.md"
12
+ ],
13
+ "keywords": [
14
+ "api",
15
+ "spec",
16
+ "definition",
17
+ "typescript",
18
+ "zod",
19
+ "generator",
20
+ "typeweaver"
21
+ ],
22
+ "author": "Dennis Wentzien <dw@rexeus.com>",
23
+ "license": "ISC",
24
+ "repository": {
25
+ "type": "git",
26
+ "url": "git+https://github.com/rexeus/typeweaver.git",
27
+ "directory": "packages/aws-cdk"
28
+ },
29
+ "bugs": {
30
+ "url": "https://github.com/rexeus/typeweaver/issues"
31
+ },
32
+ "homepage": "https://github.com/rexeus/typeweaver#readme",
33
+ "peerDependencies": {
34
+ "@rexeus/typeweaver-core": "*",
35
+ "@rexeus/typeweaver-gen": "*"
36
+ },
37
+ "devDependencies": {
38
+ "@rexeus/typeweaver-core": "0.0.1",
39
+ "@rexeus/typeweaver-gen": "0.0.1"
40
+ },
41
+ "scripts": {
42
+ "typecheck": "tsc --noEmit",
43
+ "format": "prettier --write .",
44
+ "build": "pkgroll --clean-dist && cp -r ./src/templates ./dist/templates && cp -r ./src/lib ./dist/lib",
45
+ "preversion": "npm run build"
46
+ }
47
+ }