@flipdish/cdk-constructs 0.1.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 +88 -0
- package/dist/Api.d.ts +12 -0
- package/dist/Api.d.ts.map +1 -0
- package/dist/Api.js +102 -0
- package/dist/Api.js.map +1 -0
- package/dist/OpenApi.d.ts +6 -0
- package/dist/OpenApi.d.ts.map +1 -0
- package/dist/OpenApi.js +100 -0
- package/dist/OpenApi.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -0
- package/dist/types.d.ts +20 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +9 -0
- package/dist/types.js.map +1 -0
- package/package.json +38 -0
package/README.md
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# @flipdish/cdk-constructs
|
|
2
|
+
|
|
3
|
+
CDK Constructs for Flipdish serverless applications, with a focus on API constructs that provide automatic OpenAPI documentation generation and deployment.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @flipdish/cdk-constructs
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Overview
|
|
12
|
+
|
|
13
|
+
This package provides CDK constructs and utilities for building serverless APIs with strong typing and automatic OpenAPI documentation. It is designed to be used with [SST](https://sst.dev/) and integrates with Zod for schema validation and OpenAPI generation.
|
|
14
|
+
|
|
15
|
+
## Features
|
|
16
|
+
|
|
17
|
+
- **Api Construct**: Drop-in replacement for SST's Api construct, with built-in OpenAPI documentation generation.
|
|
18
|
+
- **Automatic OpenAPI Docs**: Generates and deploys OpenAPI JSON, YAML, and HTML documentation for your API routes.
|
|
19
|
+
- **Schema Integration**: Supports Zod-based schema definitions for routes.
|
|
20
|
+
- **TypeScript-first**: All constructs and utilities are strictly typed.
|
|
21
|
+
|
|
22
|
+
## Usage
|
|
23
|
+
|
|
24
|
+
### Basic Example
|
|
25
|
+
|
|
26
|
+
```typescript
|
|
27
|
+
import { Api, SchemaFunction } from '@flipdish/cdk-constructs';
|
|
28
|
+
import { z } from 'zod';
|
|
29
|
+
import { StackContext } from 'sst/constructs';
|
|
30
|
+
|
|
31
|
+
export function API({ stack }: StackContext) {
|
|
32
|
+
const api = new Api(stack, 'Api', {
|
|
33
|
+
routes: {
|
|
34
|
+
'GET /hello': {
|
|
35
|
+
function: new SchemaFunction(stack, 'HelloHandler', {
|
|
36
|
+
handler: 'src/hello.handler',
|
|
37
|
+
schema: {
|
|
38
|
+
responses: {
|
|
39
|
+
200: z.object({ message: z.string() }),
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
}),
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
// The following endpoints are automatically added:
|
|
48
|
+
// - /openapi.json
|
|
49
|
+
// - /openapi.yaml
|
|
50
|
+
// - /openapi.html
|
|
51
|
+
|
|
52
|
+
return { api };
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### OpenAPI Utilities
|
|
57
|
+
|
|
58
|
+
You can also use the following utilities for advanced OpenAPI schema generation:
|
|
59
|
+
|
|
60
|
+
- `buildSchema(registry, serviceName, url)`: Generates an OpenAPI schema document from a registry.
|
|
61
|
+
- `sstRoutesToOpenApiSchema(routes)`: Converts SST route definitions to OpenAPI route configs.
|
|
62
|
+
|
|
63
|
+
### Types
|
|
64
|
+
|
|
65
|
+
The package exports several types to help with strong typing and schema integration:
|
|
66
|
+
|
|
67
|
+
- `Schema`
|
|
68
|
+
- `SchemaFunction`
|
|
69
|
+
- `SchemaFunctionProps`
|
|
70
|
+
- `SchemaRouteProps`
|
|
71
|
+
- `SchemaApiProps`
|
|
72
|
+
- `SchemaApiFunctionRouteProps`
|
|
73
|
+
|
|
74
|
+
## Development & Testing
|
|
75
|
+
|
|
76
|
+
This package is maintained in the [serverless-app-template](https://github.com/flipdishbytes/serverless-app-template) repository.
|
|
77
|
+
|
|
78
|
+
### Testing
|
|
79
|
+
|
|
80
|
+
You can test the constructs by deploying a stack and verifying that the OpenAPI documentation endpoints are available and accurate. For unit testing, use your preferred test runner (e.g., Vitest or Jest) to check that the constructs behave as expected.
|
|
81
|
+
|
|
82
|
+
## License
|
|
83
|
+
|
|
84
|
+
MIT
|
|
85
|
+
|
|
86
|
+
## Contributing
|
|
87
|
+
|
|
88
|
+
This package is maintained in the [serverless-app-template](https://github.com/flipdishbytes/serverless-app-template) repository.
|
package/dist/Api.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Construct } from 'constructs';
|
|
2
|
+
import { type ApiAuthorizer, type Stack, Api as sstApi } from 'sst/constructs';
|
|
3
|
+
import type { SchemaApiProps, SchemaRouteProps } from './types';
|
|
4
|
+
export declare class Api<Authorizers extends Record<string, ApiAuthorizer> = Record<string, ApiAuthorizer>> extends sstApi<Authorizers> {
|
|
5
|
+
private readonly registry;
|
|
6
|
+
private readonly serviceName;
|
|
7
|
+
private readonly deployment;
|
|
8
|
+
constructor(stack: Stack, id: string, props?: SchemaApiProps<Authorizers>);
|
|
9
|
+
addRoutes(scope: Construct, routes: Record<string, SchemaRouteProps<keyof Authorizers>>): void;
|
|
10
|
+
private exportSchema;
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=Api.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Api.d.ts","sourceRoot":"","sources":["../src/Api.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,SAAS,EAAc,MAAM,YAAY,CAAC;AACxD,OAAO,EAAE,KAAK,aAAa,EAAU,KAAK,KAAK,EAAE,GAAG,IAAI,MAAM,EAAE,MAAM,gBAAgB,CAAC;AAEvF,OAAO,KAAK,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAyBhE,qBAAa,GAAG,CAAC,WAAW,SAAS,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAE,SAAQ,MAAM,CAAC,WAAW,CAAC;IAC7H,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA0C;IACnE,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAmB;gBAElC,KAAK,EAAE,KAAK,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,cAAc,CAAC,WAAW,CAAC;IAwBlE,SAAS,CAAC,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,MAAM,WAAW,CAAC,CAAC,GAAG,IAAI;IAWrG,OAAO,CAAC,YAAY;CAKrB"}
|
package/dist/Api.js
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { OpenAPIRegistry } from '@asteasolutions/zod-to-openapi';
|
|
2
|
+
import { Aspects, RemovalPolicy, Tags } from 'aws-cdk-lib';
|
|
3
|
+
import { BucketDeployment, Source } from 'aws-cdk-lib/aws-s3-deployment';
|
|
4
|
+
import { NagSuppressions } from 'cdk-nag';
|
|
5
|
+
import { Bucket, Api as sstApi } from 'sst/constructs';
|
|
6
|
+
import { buildSchema, sstRoutesToOpenApiSchema } from './OpenApi';
|
|
7
|
+
// This class extends the SST Api class to provide additional functionality for OpenAPI documentation generation.
|
|
8
|
+
// The type of 'ApiProps' has been extended to include a 'schema' property, which is used to define the OpenAPI schema for the API.
|
|
9
|
+
//
|
|
10
|
+
// WE DO THIS NOT BECAUSE IT IS EASY, BUT BECAUSE WE THOUGHT IT WOULD BE EASY.
|
|
11
|
+
// There are 2 strange things in this class.
|
|
12
|
+
//
|
|
13
|
+
// 1.
|
|
14
|
+
// The SST Api class has a design peculiarity that made this difficult to implement.
|
|
15
|
+
// The intended design was that we walk over the routes, collect them into an OpenAPI schema registry,
|
|
16
|
+
// and then generate the OpenAPI schema from the registry, and serve up it over HTTP as a static site.
|
|
17
|
+
// CDK constructs are designed to be immutable, and declarative, but the SST Api class is not.
|
|
18
|
+
// The class has an 'addRoutes' method that can be called multiple times. Each call to 'addRoutes' will add
|
|
19
|
+
// more routes to the API, which adds to the registry. At any point in time, we cannot tell if the registry
|
|
20
|
+
// is complete or not - there might be more calls to 'addRoutes' pending.
|
|
21
|
+
// To work around this, we miss-use the Aspect mechanism, to call a callback function when the Api construct is created.
|
|
22
|
+
// Aspects are called after the construct tree is built, so we can be sure that all routes have been added to the registry.
|
|
23
|
+
//
|
|
24
|
+
// 2.
|
|
25
|
+
// The base class (the SST Api) makes a call to the public 'addRoutes' method from its constructor.
|
|
26
|
+
// We override the 'addRoutes' method to add the routes to the OpenAPI registry. When this class calls 'super' in
|
|
27
|
+
// the constructor, there is a call to 'addRoutes', before the registry is initialized. To work around this,
|
|
28
|
+
// we check if the registry to tell if the call to 'addRoutes' is the first one (from the constructor) or a subsequent call.
|
|
29
|
+
export class Api extends sstApi {
|
|
30
|
+
registry = new OpenAPIRegistry();
|
|
31
|
+
serviceName;
|
|
32
|
+
deployment;
|
|
33
|
+
constructor(stack, id, props) {
|
|
34
|
+
super(stack, id, props);
|
|
35
|
+
this.serviceName = stack.stackName.toLowerCase();
|
|
36
|
+
const { deployment, bucket } = buildWebDeployment(stack, this);
|
|
37
|
+
this.deployment = deployment;
|
|
38
|
+
for (const schema of sstRoutesToOpenApiSchema(props?.routes ?? {})) {
|
|
39
|
+
this.registry.registerPath(schema);
|
|
40
|
+
}
|
|
41
|
+
Aspects.of(this).add(new ApiCallbackAspect((api) => {
|
|
42
|
+
api.exportSchema();
|
|
43
|
+
}));
|
|
44
|
+
this.addRoutes(this, {
|
|
45
|
+
'GET /openapi.html': { type: 'url', url: `https://${bucket.cdk.bucket.bucketDomainName}/openapi.html` },
|
|
46
|
+
'GET /openapi.json': { type: 'url', url: `https://${bucket.cdk.bucket.bucketDomainName}/openapi.json` },
|
|
47
|
+
'GET /openapi.yaml': { type: 'url', url: `https://${bucket.cdk.bucket.bucketDomainName}/openapi.yaml` },
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
addRoutes(scope, routes) {
|
|
51
|
+
super.addRoutes(scope, routes);
|
|
52
|
+
if (!this.registry) {
|
|
53
|
+
return; // this call is from the constructor, and the registry is not initialized yet.
|
|
54
|
+
}
|
|
55
|
+
for (const schema of sstRoutesToOpenApiSchema(routes)) {
|
|
56
|
+
this.registry.registerPath(schema);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
exportSchema() {
|
|
60
|
+
const schema = buildSchema(this.registry, this.serviceName, 'https://example.com');
|
|
61
|
+
this.deployment.addSource(Source.jsonData('openapi.json', schema));
|
|
62
|
+
this.deployment.addSource(Source.yamlData('openapi.yaml', schema));
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
// This function creates a static web site.
|
|
66
|
+
// It creates an S3 bucket to host the static files and a CloudFront distribution to serve them,
|
|
67
|
+
// and returns a deployment object that can be used to deploy the static files to the S3 bucket.
|
|
68
|
+
const buildWebDeployment = (stack, scope) => {
|
|
69
|
+
const bucket = new Bucket(scope, 'OpenApiDocs', {
|
|
70
|
+
cdk: {
|
|
71
|
+
bucket: {
|
|
72
|
+
removalPolicy: RemovalPolicy.DESTROY,
|
|
73
|
+
autoDeleteObjects: true,
|
|
74
|
+
websiteIndexDocument: 'openapi.html',
|
|
75
|
+
publicReadAccess: true,
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
});
|
|
79
|
+
Tags.of(bucket.cdk.bucket).add('flipdish:ephemeral', 'true');
|
|
80
|
+
NagSuppressions.addResourceSuppressions(bucket.cdk.bucket, [
|
|
81
|
+
{ id: 'AwsSolutions-S2', reason: 'This bucket is used to host public files for OpenAPI documentation' },
|
|
82
|
+
{ id: 'AwsSolutions-S5', reason: 'This bucket is used to host public files for OpenAPI documentation' },
|
|
83
|
+
]);
|
|
84
|
+
const deployment = new BucketDeployment(scope, 'DeployOpenApiFiles', {
|
|
85
|
+
sources: [Source.asset('stacks/static/openapi')],
|
|
86
|
+
destinationBucket: bucket.cdk.bucket,
|
|
87
|
+
});
|
|
88
|
+
NagSuppressions.addStackSuppressions(stack, [{ id: 'AwsSolutions-L1', reason: 'cdk deployments use a lambda that is outside of our control' }]);
|
|
89
|
+
return { deployment, bucket };
|
|
90
|
+
};
|
|
91
|
+
class ApiCallbackAspect {
|
|
92
|
+
fn;
|
|
93
|
+
constructor(fn) {
|
|
94
|
+
this.fn = fn;
|
|
95
|
+
}
|
|
96
|
+
visit(node) {
|
|
97
|
+
if (node instanceof Api) {
|
|
98
|
+
this.fn(node);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
//# sourceMappingURL=Api.js.map
|
package/dist/Api.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Api.js","sourceRoot":"","sources":["../src/Api.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { type OpenAPIRegistry, type RouteConfig } from '@asteasolutions/zod-to-openapi';
|
|
2
|
+
import { type ApiAuthorizer } from 'sst/constructs';
|
|
3
|
+
import { type SchemaRouteProps } from './types';
|
|
4
|
+
export declare function buildSchema(registry: OpenAPIRegistry, serviceName: string, url: string): unknown;
|
|
5
|
+
export declare function sstRoutesToOpenApiSchema<Authorizers extends Record<string, ApiAuthorizer> = Record<string, ApiAuthorizer>>(route: Record<string, SchemaRouteProps<keyof Authorizers>>): Array<RouteConfig>;
|
|
6
|
+
//# sourceMappingURL=OpenApi.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"OpenApi.d.ts","sourceRoot":"","sources":["../src/OpenApi.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,eAAe,EAAsB,KAAK,WAAW,EAAE,MAAM,gCAAgC,CAAC;AAC5G,OAAO,EAAE,KAAK,aAAa,EAAsE,MAAM,gBAAgB,CAAC;AACxH,OAAO,EAAwF,KAAK,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAEtI,wBAAgB,WAAW,CAAC,QAAQ,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAiBhG;AAyFD,wBAAgB,wBAAwB,CAAC,WAAW,SAAS,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,EACxH,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,MAAM,WAAW,CAAC,CAAC,GACzD,KAAK,CAAC,WAAW,CAAC,CAOpB"}
|
package/dist/OpenApi.js
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { OpenApiGeneratorV3 } from '@asteasolutions/zod-to-openapi';
|
|
2
|
+
import { Function as sstFunction } from 'sst/constructs';
|
|
3
|
+
import { SchemaFunction } from './types';
|
|
4
|
+
export function buildSchema(registry, serviceName, url) {
|
|
5
|
+
const bearerAuth = registry.registerComponent('securitySchemes', 'ApiKeyAuth', {
|
|
6
|
+
type: 'apiKey',
|
|
7
|
+
in: 'header',
|
|
8
|
+
name: 'Authorization',
|
|
9
|
+
});
|
|
10
|
+
const generator = new OpenApiGeneratorV3(registry.definitions);
|
|
11
|
+
return generator.generateDocument({
|
|
12
|
+
openapi: '3.1.0',
|
|
13
|
+
info: {
|
|
14
|
+
version: '1.0.0',
|
|
15
|
+
title: `${serviceName} API`,
|
|
16
|
+
description: `${serviceName} API`,
|
|
17
|
+
},
|
|
18
|
+
servers: [{ url, description: 'Example server' }],
|
|
19
|
+
security: [{ [bearerAuth.name]: [] }],
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
const NotNull = (item) => {
|
|
23
|
+
return item !== undefined;
|
|
24
|
+
};
|
|
25
|
+
function openApiMethod(method) {
|
|
26
|
+
switch (method.toLowerCase()) {
|
|
27
|
+
case 'get':
|
|
28
|
+
return 'get';
|
|
29
|
+
case 'post':
|
|
30
|
+
return 'post';
|
|
31
|
+
case 'put':
|
|
32
|
+
return 'put';
|
|
33
|
+
case 'delete':
|
|
34
|
+
return 'delete';
|
|
35
|
+
case 'patch':
|
|
36
|
+
return 'patch';
|
|
37
|
+
case 'head':
|
|
38
|
+
return 'head';
|
|
39
|
+
case 'options':
|
|
40
|
+
return 'options';
|
|
41
|
+
default:
|
|
42
|
+
throw new Error(`Unknown method: '${method}'`);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
function isSchemaFunctionProps(route) {
|
|
46
|
+
return 'schema' in route;
|
|
47
|
+
}
|
|
48
|
+
function sstRouteToOpenApiRoute(method, path, route) {
|
|
49
|
+
// RouteProps is a union type, and we need to handle lots of different cases.
|
|
50
|
+
// We try to deal with the cases from simple to complex, and we will throw
|
|
51
|
+
// an error if we encounter a case that we don't know how to handle.
|
|
52
|
+
// the function is just a path to the handler {function: 'path/to/handler'}
|
|
53
|
+
if (typeof route === 'string') {
|
|
54
|
+
return undefined;
|
|
55
|
+
}
|
|
56
|
+
if (route instanceof SchemaFunction && route.schema) {
|
|
57
|
+
return {
|
|
58
|
+
...route.schema,
|
|
59
|
+
path,
|
|
60
|
+
method: method,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
if (route instanceof sstFunction) {
|
|
64
|
+
return undefined;
|
|
65
|
+
}
|
|
66
|
+
if (route.type === 'aws' || route.type === 'url' || route.type === 'alb' || route.type === 'nlb' || route.type === 'graphql') {
|
|
67
|
+
return undefined;
|
|
68
|
+
}
|
|
69
|
+
const fn = route.function;
|
|
70
|
+
if (!fn || typeof fn === 'string') {
|
|
71
|
+
return undefined;
|
|
72
|
+
}
|
|
73
|
+
if (fn instanceof SchemaFunction && fn.schema) {
|
|
74
|
+
return {
|
|
75
|
+
...fn.schema,
|
|
76
|
+
path,
|
|
77
|
+
method: method,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
if (fn instanceof sstFunction) {
|
|
81
|
+
return undefined;
|
|
82
|
+
}
|
|
83
|
+
if (isSchemaFunctionProps(fn) && fn.schema) {
|
|
84
|
+
return {
|
|
85
|
+
...fn.schema,
|
|
86
|
+
path,
|
|
87
|
+
method: method,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
return undefined;
|
|
91
|
+
}
|
|
92
|
+
export function sstRoutesToOpenApiSchema(route) {
|
|
93
|
+
return Object.entries(route)
|
|
94
|
+
.map(([pathPattern, routeProps]) => {
|
|
95
|
+
const [method, path] = pathPattern.split(/\s+/);
|
|
96
|
+
return sstRouteToOpenApiRoute(openApiMethod(method), path, routeProps);
|
|
97
|
+
})
|
|
98
|
+
.filter(NotNull);
|
|
99
|
+
}
|
|
100
|
+
//# sourceMappingURL=OpenApi.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"OpenApi.js","sourceRoot":"","sources":["../src/OpenApi.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { Api } from './Api.js';
|
|
2
|
+
export { buildSchema, sstRoutesToOpenApiSchema } from './OpenApi.js';
|
|
3
|
+
export { Schema, SchemaFunction, SchemaFunctionProps, SchemaRouteProps, SchemaApiProps, SchemaApiFunctionRouteProps } from './types.js';
|
|
4
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAC/B,OAAO,EAAE,WAAW,EAAE,wBAAwB,EAAE,MAAM,cAAc,CAAC;AACrE,OAAO,EAAE,MAAM,EAAE,cAAc,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,cAAc,EAAE,2BAA2B,EAAE,MAAM,YAAY,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAC/B,OAAO,EAAE,WAAW,EAAE,wBAAwB,EAAE,MAAM,cAAc,CAAC;AACrE,OAAO,EAAU,cAAc,EAAsF,MAAM,YAAY,CAAC"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { RouteConfig } from '@asteasolutions/zod-to-openapi';
|
|
2
|
+
import type { Construct } from 'constructs';
|
|
3
|
+
import { type ApiAuthorizer, type ApiFunctionRouteProps, type ApiProps, type ApiRouteProps, type FunctionProps, Function as sstFunction } from 'sst/constructs';
|
|
4
|
+
export type Schema = Omit<RouteConfig, 'path' | 'method'>;
|
|
5
|
+
export interface SchemaFunctionProps extends FunctionProps {
|
|
6
|
+
schema?: Schema;
|
|
7
|
+
}
|
|
8
|
+
export declare class SchemaFunction extends sstFunction {
|
|
9
|
+
readonly schema?: Schema;
|
|
10
|
+
constructor(scope: Construct, id: string, { schema, ...props }: SchemaFunctionProps);
|
|
11
|
+
}
|
|
12
|
+
export type SchemaFunctionDefinition = string | SchemaFunction | SchemaFunctionProps;
|
|
13
|
+
export interface SchemaApiFunctionRouteProps<AuthorizersKeys = string> extends ApiFunctionRouteProps<AuthorizersKeys> {
|
|
14
|
+
function?: SchemaFunctionDefinition;
|
|
15
|
+
}
|
|
16
|
+
export type SchemaRouteProps<AuthorizerKeys> = ApiRouteProps<AuthorizerKeys> | SchemaApiFunctionRouteProps<AuthorizerKeys>;
|
|
17
|
+
export interface SchemaApiProps<Authorizers extends Record<string, ApiAuthorizer> = Record<string, ApiAuthorizer>, AuthorizerKeys = keyof Authorizers> extends ApiProps<Authorizers, AuthorizerKeys> {
|
|
18
|
+
routes?: Record<string, SchemaRouteProps<AuthorizerKeys>>;
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gCAAgC,CAAC;AAClE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAC5C,OAAO,EACL,KAAK,aAAa,EAClB,KAAK,qBAAqB,EAC1B,KAAK,QAAQ,EACb,KAAK,aAAa,EAClB,KAAK,aAAa,EAClB,QAAQ,IAAI,WAAW,EACxB,MAAM,gBAAgB,CAAC;AAExB,MAAM,MAAM,MAAM,GAAG,IAAI,CAAC,WAAW,EAAE,MAAM,GAAG,QAAQ,CAAC,CAAC;AAE1D,MAAM,WAAW,mBAAoB,SAAQ,aAAa;IACxD,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,qBAAa,cAAe,SAAQ,WAAW;IAC7C,SAAgB,MAAM,CAAC,EAAE,MAAM,CAAC;gBACpB,KAAK,EAAE,SAAS,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,GAAG,KAAK,EAAE,EAAE,mBAAmB;CAIpF;AAED,MAAM,MAAM,wBAAwB,GAAG,MAAM,GAAG,cAAc,GAAG,mBAAmB,CAAC;AAErF,MAAM,WAAW,2BAA2B,CAAC,eAAe,GAAG,MAAM,CAAE,SAAQ,qBAAqB,CAAC,eAAe,CAAC;IACnH,QAAQ,CAAC,EAAE,wBAAwB,CAAC;CACrC;AAED,MAAM,MAAM,gBAAgB,CAAC,cAAc,IAAI,aAAa,CAAC,cAAc,CAAC,GAAG,2BAA2B,CAAC,cAAc,CAAC,CAAC;AAE3H,MAAM,WAAW,cAAc,CAAC,WAAW,SAAS,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,EAAE,cAAc,GAAG,MAAM,WAAW,CACnJ,SAAQ,QAAQ,CAAC,WAAW,EAAE,cAAc,CAAC;IAC7C,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,cAAc,CAAC,CAAC,CAAC;CAC3D"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Function as sstFunction, } from 'sst/constructs';
|
|
2
|
+
export class SchemaFunction extends sstFunction {
|
|
3
|
+
schema;
|
|
4
|
+
constructor(scope, id, { schema, ...props }) {
|
|
5
|
+
super(scope, id, props);
|
|
6
|
+
this.schema = schema;
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAEA,OAAO,EAML,QAAQ,IAAI,WAAW,GACxB,MAAM,gBAAgB,CAAC;AAQxB,MAAM,OAAO,cAAe,SAAQ,WAAW;IAC7B,MAAM,CAAU;IAChC,YAAY,KAAgB,EAAE,EAAU,EAAE,EAAE,MAAM,EAAE,GAAG,KAAK,EAAuB;QACjF,KAAK,CAAC,KAAK,EAAE,EAAE,EAAE,KAAK,CAAC,CAAC;QACxB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACvB,CAAC;CACF"}
|
package/package.json
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@flipdish/cdk-constructs",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "CDK Constructs for Flipdish serverless applications",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"default": "./dist/index.js"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"scripts": {
|
|
15
|
+
"build": "tsc",
|
|
16
|
+
"clean": "rm -rf dist",
|
|
17
|
+
"prebuild": "npm run clean"
|
|
18
|
+
},
|
|
19
|
+
"devDependencies": {
|
|
20
|
+
"typescript": "^5.0.0",
|
|
21
|
+
"@tsconfig/node22": "22.0.2",
|
|
22
|
+
"aws-cdk-lib": "2.179.0",
|
|
23
|
+
"constructs": "10.3.0",
|
|
24
|
+
"sst": "2.48.5"
|
|
25
|
+
},
|
|
26
|
+
"files": [
|
|
27
|
+
"dist"
|
|
28
|
+
],
|
|
29
|
+
"repository": {
|
|
30
|
+
"type": "git",
|
|
31
|
+
"url": "git+https://github.com/flipdishbytes/serverless-app-template.git"
|
|
32
|
+
},
|
|
33
|
+
"author": "Flipdish",
|
|
34
|
+
"license": "MIT",
|
|
35
|
+
"publishConfig": {
|
|
36
|
+
"access": "public"
|
|
37
|
+
}
|
|
38
|
+
}
|