@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 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
@@ -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"}
@@ -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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"}
@@ -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,4 @@
1
+ export { Api } from './Api.js';
2
+ export { buildSchema, sstRoutesToOpenApiSchema } from './OpenApi.js';
3
+ export { SchemaFunction } from './types.js';
4
+ //# sourceMappingURL=index.js.map
@@ -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"}
@@ -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
+ }