@certik/serverless-api 1.0.12 → 2.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/CHANGELOG.md +8 -0
- package/README.md +0 -1
- package/dist/deploy.d.ts +20 -0
- package/dist/deploy.d.ts.map +1 -0
- package/dist/deploy.js +17 -0
- package/dist/dev.d.ts +8 -0
- package/dist/dev.d.ts.map +1 -0
- package/dist/dev.js +8 -0
- package/dist/entrypoint.d.ts +26 -0
- package/dist/entrypoint.d.ts.map +1 -0
- package/dist/entrypoint.js +78 -0
- package/dist/handler.d.ts +2 -0
- package/dist/handler.d.ts.map +1 -0
- package/{index.js → dist/handler.js} +1 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +13 -0
- package/dist/lib/app.d.ts +39 -0
- package/dist/lib/app.d.ts.map +1 -0
- package/dist/lib/app.js +139 -0
- package/dist/lib/const.d.ts +12 -0
- package/dist/lib/const.d.ts.map +1 -0
- package/dist/lib/const.js +12 -0
- package/dist/lib/cors.d.ts +19 -0
- package/dist/lib/cors.d.ts.map +1 -0
- package/dist/lib/cors.js +38 -0
- package/dist/lib/dev.d.ts +38 -0
- package/dist/lib/dev.d.ts.map +1 -0
- package/dist/lib/dev.js +78 -0
- package/dist/lib/domain.d.ts +16 -0
- package/dist/lib/domain.d.ts.map +1 -0
- package/dist/lib/domain.js +19 -0
- package/dist/lib/env.d.ts +14 -0
- package/dist/lib/env.d.ts.map +1 -0
- package/dist/lib/env.js +37 -0
- package/dist/lib/pack.d.ts +29 -0
- package/dist/lib/pack.d.ts.map +1 -0
- package/dist/lib/pack.js +93 -0
- package/dist/lib/path.d.ts +22 -0
- package/dist/lib/path.d.ts.map +1 -0
- package/dist/lib/path.js +26 -0
- package/dist/lib/routes.d.ts +32 -0
- package/dist/lib/routes.d.ts.map +1 -0
- package/dist/lib/routes.js +76 -0
- package/dist/lib/types.d.ts +125 -0
- package/dist/lib/types.d.ts.map +1 -0
- package/dist/lib/types.js +7 -0
- package/package.json +58 -44
- package/dev.js +0 -4
- package/entrypoint.js +0 -68
- package/lib/app.js +0 -162
- package/lib/const.js +0 -4
- package/lib/cors.js +0 -21
- package/lib/dev.js +0 -54
- package/lib/env.js +0 -24
- package/lib/pack.js +0 -83
- package/lib/path.js +0 -6
- package/lib/routes.js +0 -39
package/CHANGELOG.md
CHANGED
package/README.md
CHANGED
package/dist/deploy.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pulumi program entry point.
|
|
3
|
+
*
|
|
4
|
+
* Loads routes from the local `routes/` directory and deploys
|
|
5
|
+
* the full serverless API stack via {@link createPulumiAPIApp}.
|
|
6
|
+
*/
|
|
7
|
+
export default function main(): Promise<{
|
|
8
|
+
zipFileName: string;
|
|
9
|
+
zipFileHash: string;
|
|
10
|
+
routes: Record<string, {
|
|
11
|
+
method: string;
|
|
12
|
+
timeout: number;
|
|
13
|
+
url: string;
|
|
14
|
+
}>;
|
|
15
|
+
runtime: import("@pulumi/aws/lambda/index.js").Runtime;
|
|
16
|
+
hostedZone: string;
|
|
17
|
+
domainName: string;
|
|
18
|
+
readme: string;
|
|
19
|
+
}>;
|
|
20
|
+
//# sourceMappingURL=deploy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deploy.d.ts","sourceRoot":"","sources":["../src/deploy.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAQH,wBAA8B,IAAI;;;;;;;;;;;;GAOjC"}
|
package/dist/deploy.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pulumi program entry point.
|
|
3
|
+
*
|
|
4
|
+
* Loads routes from the local `routes/` directory and deploys
|
|
5
|
+
* the full serverless API stack via {@link createPulumiAPIApp}.
|
|
6
|
+
*/
|
|
7
|
+
import pathModule from "node:path";
|
|
8
|
+
import { createPulumiAPIApp } from "./lib/app.js";
|
|
9
|
+
import { dirname } from "./lib/path.js";
|
|
10
|
+
import { getRoutes } from "./lib/routes.js";
|
|
11
|
+
export default async function main() {
|
|
12
|
+
return createPulumiAPIApp({
|
|
13
|
+
routes: await getRoutes(pathModule.join(dirname(import.meta.url), "routes")),
|
|
14
|
+
bundleDevDependencies: true,
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=deploy.js.map
|
package/dist/dev.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Re-export of {@link startLocalApp} for consumer convenience.
|
|
3
|
+
*
|
|
4
|
+
* Consumers can `import { startLocalApp } from "@certik/serverless-api/dev"`
|
|
5
|
+
* without pulling in the full library.
|
|
6
|
+
*/
|
|
7
|
+
export { startLocalApp } from "./lib/dev.js";
|
|
8
|
+
//# sourceMappingURL=dev.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dev.d.ts","sourceRoot":"","sources":["../src/dev.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC"}
|
package/dist/dev.js
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Re-export of {@link startLocalApp} for consumer convenience.
|
|
3
|
+
*
|
|
4
|
+
* Consumers can `import { startLocalApp } from "@certik/serverless-api/dev"`
|
|
5
|
+
* without pulling in the full library.
|
|
6
|
+
*/
|
|
7
|
+
export { startLocalApp } from "./lib/dev.js";
|
|
8
|
+
//# sourceMappingURL=dev.js.map
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared Lambda entrypoint.
|
|
3
|
+
*
|
|
4
|
+
* All Lambda functions deployed by this library share this single
|
|
5
|
+
* entrypoint. It discovers routes once on cold start, then matches
|
|
6
|
+
* each incoming API Gateway event to the correct handler, applying
|
|
7
|
+
* timeout enforcement and CORS headers.
|
|
8
|
+
*
|
|
9
|
+
* @module entrypoint
|
|
10
|
+
*/
|
|
11
|
+
import type { LambdaEvent, LambdaContext, LambdaResponse } from "./lib/types.js";
|
|
12
|
+
/**
|
|
13
|
+
* The Lambda handler invoked by API Gateway.
|
|
14
|
+
*
|
|
15
|
+
* On the first invocation (cold start), routes are loaded from the
|
|
16
|
+
* `routes/` directory and cached for subsequent warm invocations.
|
|
17
|
+
* The handler matches the request path and HTTP method to a route,
|
|
18
|
+
* applies the configured timeout, and injects CORS headers when
|
|
19
|
+
* the route defines a `corsOrigin`.
|
|
20
|
+
*
|
|
21
|
+
* @param event - The API Gateway proxy integration event.
|
|
22
|
+
* @param context - The Lambda execution context.
|
|
23
|
+
* @returns The HTTP response to send back through API Gateway.
|
|
24
|
+
*/
|
|
25
|
+
export declare const handler: (event: LambdaEvent, context: LambdaContext) => Promise<LambdaResponse>;
|
|
26
|
+
//# sourceMappingURL=entrypoint.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"entrypoint.d.ts","sourceRoot":"","sources":["../src/entrypoint.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH,OAAO,KAAK,EAEV,WAAW,EACX,aAAa,EACb,cAAc,EACf,MAAM,gBAAgB,CAAC;AAOxB;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,OAAO,GAClB,OAAO,WAAW,EAClB,SAAS,aAAa,KACrB,OAAO,CAAC,cAAc,CAiExB,CAAC"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared Lambda entrypoint.
|
|
3
|
+
*
|
|
4
|
+
* All Lambda functions deployed by this library share this single
|
|
5
|
+
* entrypoint. It discovers routes once on cold start, then matches
|
|
6
|
+
* each incoming API Gateway event to the correct handler, applying
|
|
7
|
+
* timeout enforcement and CORS headers.
|
|
8
|
+
*
|
|
9
|
+
* @module entrypoint
|
|
10
|
+
*/
|
|
11
|
+
import pathModule from "node:path";
|
|
12
|
+
import { getRoutes } from "./lib/routes.js";
|
|
13
|
+
/** Cached routes — resolved once on cold start. */
|
|
14
|
+
let cachedRoutes = null;
|
|
15
|
+
/**
|
|
16
|
+
* The Lambda handler invoked by API Gateway.
|
|
17
|
+
*
|
|
18
|
+
* On the first invocation (cold start), routes are loaded from the
|
|
19
|
+
* `routes/` directory and cached for subsequent warm invocations.
|
|
20
|
+
* The handler matches the request path and HTTP method to a route,
|
|
21
|
+
* applies the configured timeout, and injects CORS headers when
|
|
22
|
+
* the route defines a `corsOrigin`.
|
|
23
|
+
*
|
|
24
|
+
* @param event - The API Gateway proxy integration event.
|
|
25
|
+
* @param context - The Lambda execution context.
|
|
26
|
+
* @returns The HTTP response to send back through API Gateway.
|
|
27
|
+
*/
|
|
28
|
+
export const handler = async (event, context) => {
|
|
29
|
+
if (!cachedRoutes) {
|
|
30
|
+
cachedRoutes = await getRoutes(pathModule.resolve("routes"));
|
|
31
|
+
}
|
|
32
|
+
const currentRoute = cachedRoutes.find((route) => {
|
|
33
|
+
return (route.path === event.path &&
|
|
34
|
+
route.method.toLowerCase() === event.httpMethod.toLowerCase());
|
|
35
|
+
});
|
|
36
|
+
if (!currentRoute) {
|
|
37
|
+
return {
|
|
38
|
+
statusCode: 404,
|
|
39
|
+
body: "Not Found",
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
const extraHeaders = {};
|
|
43
|
+
if (currentRoute.corsOrigin) {
|
|
44
|
+
extraHeaders["Access-Control-Allow-Origin"] = currentRoute.corsOrigin;
|
|
45
|
+
}
|
|
46
|
+
let timeoutId;
|
|
47
|
+
const response = await Promise.race([
|
|
48
|
+
(async () => {
|
|
49
|
+
let body = event.body ? event.body : null;
|
|
50
|
+
if (event.isBase64Encoded && body) {
|
|
51
|
+
body = Buffer.from(body, "base64").toString("utf-8");
|
|
52
|
+
}
|
|
53
|
+
const rp = await currentRoute.handler({
|
|
54
|
+
...event,
|
|
55
|
+
body,
|
|
56
|
+
isBase64Encoded: false,
|
|
57
|
+
}, context);
|
|
58
|
+
return rp;
|
|
59
|
+
})(),
|
|
60
|
+
new Promise(function timeoutResponse(resolve) {
|
|
61
|
+
timeoutId = setTimeout(() => {
|
|
62
|
+
resolve({
|
|
63
|
+
statusCode: 408,
|
|
64
|
+
body: `Request Timeout after ${currentRoute.timeout} seconds`,
|
|
65
|
+
});
|
|
66
|
+
}, currentRoute.timeout * 1000);
|
|
67
|
+
}),
|
|
68
|
+
]);
|
|
69
|
+
clearTimeout(timeoutId);
|
|
70
|
+
return {
|
|
71
|
+
...response,
|
|
72
|
+
headers: {
|
|
73
|
+
...response.headers,
|
|
74
|
+
...extraHeaders,
|
|
75
|
+
},
|
|
76
|
+
};
|
|
77
|
+
};
|
|
78
|
+
//# sourceMappingURL=entrypoint.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handler.d.ts","sourceRoot":"","sources":["../src/handler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public API surface of `@certik/serverless-api`.
|
|
3
|
+
*
|
|
4
|
+
* Re-exports the key utilities consumers need to build and
|
|
5
|
+
* deploy their own serverless APIs.
|
|
6
|
+
*
|
|
7
|
+
* @packageDocumentation
|
|
8
|
+
*/
|
|
9
|
+
export { dirname } from "./lib/path.js";
|
|
10
|
+
export { startLocalApp } from "./lib/dev.js";
|
|
11
|
+
export { createPulumiAPIApp } from "./lib/app.js";
|
|
12
|
+
export { getRoutes } from "./lib/routes.js";
|
|
13
|
+
export type { Route, RouteHandler, RouteModule, LambdaEvent, LambdaContext, LambdaResponse, PulumiAPIAppOptions, LocalAppOptions, PackOptions, PackResult, } from "./lib/types.js";
|
|
14
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AACxC,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,YAAY,EACV,KAAK,EACL,YAAY,EACZ,WAAW,EACX,WAAW,EACX,aAAa,EACb,cAAc,EACd,mBAAmB,EACnB,eAAe,EACf,WAAW,EACX,UAAU,GACX,MAAM,gBAAgB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public API surface of `@certik/serverless-api`.
|
|
3
|
+
*
|
|
4
|
+
* Re-exports the key utilities consumers need to build and
|
|
5
|
+
* deploy their own serverless APIs.
|
|
6
|
+
*
|
|
7
|
+
* @packageDocumentation
|
|
8
|
+
*/
|
|
9
|
+
export { dirname } from "./lib/path.js";
|
|
10
|
+
export { startLocalApp } from "./lib/dev.js";
|
|
11
|
+
export { createPulumiAPIApp } from "./lib/app.js";
|
|
12
|
+
export { getRoutes } from "./lib/routes.js";
|
|
13
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pulumi deployment logic for the serverless API.
|
|
3
|
+
*
|
|
4
|
+
* Creates the full AWS infrastructure stack: Lambda functions,
|
|
5
|
+
* API Gateway, custom domain, ACM certificate, and Route 53
|
|
6
|
+
* DNS records. Each route gets its own Lambda function, all
|
|
7
|
+
* sharing the same source zip and entrypoint.
|
|
8
|
+
*
|
|
9
|
+
* @module app
|
|
10
|
+
*/
|
|
11
|
+
import * as aws from "@pulumi/aws";
|
|
12
|
+
import type { PulumiAPIAppOptions } from "./types.js";
|
|
13
|
+
export { getWildcardCertificateName } from "./domain.js";
|
|
14
|
+
/**
|
|
15
|
+
* Create and deploy the full Pulumi serverless API stack.
|
|
16
|
+
*
|
|
17
|
+
* This provisions:
|
|
18
|
+
* - An S3 bucket object containing the zipped source code.
|
|
19
|
+
* - One Lambda function per route.
|
|
20
|
+
* - An API Gateway REST API wiring routes to Lambdas.
|
|
21
|
+
* - A custom domain with ACM certificate and Route 53 alias record.
|
|
22
|
+
*
|
|
23
|
+
* @param options - Deployment options including routes and bundling flags.
|
|
24
|
+
* @returns Deployment metadata: zip info, route URLs, runtime, and README.
|
|
25
|
+
*/
|
|
26
|
+
export declare function createPulumiAPIApp({ routes, bundleDevDependencies, }: PulumiAPIAppOptions): Promise<{
|
|
27
|
+
zipFileName: string;
|
|
28
|
+
zipFileHash: string;
|
|
29
|
+
routes: Record<string, {
|
|
30
|
+
method: string;
|
|
31
|
+
timeout: number;
|
|
32
|
+
url: string;
|
|
33
|
+
}>;
|
|
34
|
+
runtime: aws.lambda.Runtime;
|
|
35
|
+
hostedZone: string;
|
|
36
|
+
domainName: string;
|
|
37
|
+
readme: string;
|
|
38
|
+
}>;
|
|
39
|
+
//# sourceMappingURL=app.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../../src/lib/app.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,GAAG,MAAM,aAAa,CAAC;AAKnC,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAMtD,OAAO,EAAE,0BAA0B,EAAE,MAAM,aAAa,CAAC;AAEzD;;;;;;;;;;;GAWG;AACH,wBAAsB,kBAAkB,CAAC,EACvC,MAAM,EACN,qBAA6B,GAC9B,EAAE,mBAAmB;;;;gBAkHc,MAAM;iBAAW,MAAM;aAAO,MAAM;;;;;;GAwBvE"}
|
package/dist/lib/app.js
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pulumi deployment logic for the serverless API.
|
|
3
|
+
*
|
|
4
|
+
* Creates the full AWS infrastructure stack: Lambda functions,
|
|
5
|
+
* API Gateway, custom domain, ACM certificate, and Route 53
|
|
6
|
+
* DNS records. Each route gets its own Lambda function, all
|
|
7
|
+
* sharing the same source zip and entrypoint.
|
|
8
|
+
*
|
|
9
|
+
* @module app
|
|
10
|
+
*/
|
|
11
|
+
import * as aws from "@pulumi/aws";
|
|
12
|
+
import * as apigateway from "@pulumi/aws-apigateway";
|
|
13
|
+
import * as pulumi from "@pulumi/pulumi";
|
|
14
|
+
import { readFile } from "node:fs/promises";
|
|
15
|
+
import { getWildcardCertificateName } from "./domain.js";
|
|
16
|
+
import { isReservedEnvName } from "./env.js";
|
|
17
|
+
import { packAndZip } from "./pack.js";
|
|
18
|
+
export { getWildcardCertificateName } from "./domain.js";
|
|
19
|
+
/**
|
|
20
|
+
* Create and deploy the full Pulumi serverless API stack.
|
|
21
|
+
*
|
|
22
|
+
* This provisions:
|
|
23
|
+
* - An S3 bucket object containing the zipped source code.
|
|
24
|
+
* - One Lambda function per route.
|
|
25
|
+
* - An API Gateway REST API wiring routes to Lambdas.
|
|
26
|
+
* - A custom domain with ACM certificate and Route 53 alias record.
|
|
27
|
+
*
|
|
28
|
+
* @param options - Deployment options including routes and bundling flags.
|
|
29
|
+
* @returns Deployment metadata: zip info, route URLs, runtime, and README.
|
|
30
|
+
*/
|
|
31
|
+
export async function createPulumiAPIApp({ routes, bundleDevDependencies = false, }) {
|
|
32
|
+
const { zipFileName, zipFileHash } = await packAndZip({
|
|
33
|
+
bundleDevDependencies,
|
|
34
|
+
});
|
|
35
|
+
const projectName = pulumi.getProject();
|
|
36
|
+
const stackName = pulumi.getStack();
|
|
37
|
+
const namespace = `wf-${stackName}-${projectName}-${stackName}`;
|
|
38
|
+
const config = new pulumi.Config();
|
|
39
|
+
const sourceBucketName = config.require("sourceBucket");
|
|
40
|
+
const hostedZone = config.require("hostedZone");
|
|
41
|
+
const runtime = config.require("runtime");
|
|
42
|
+
let subdomain = config.get("subdomain");
|
|
43
|
+
if (!subdomain) {
|
|
44
|
+
subdomain = projectName.startsWith("api-")
|
|
45
|
+
? projectName.slice(4)
|
|
46
|
+
: projectName;
|
|
47
|
+
}
|
|
48
|
+
const domainName = `${subdomain}.${hostedZone}`;
|
|
49
|
+
const wildcardCertificateName = getWildcardCertificateName(domainName);
|
|
50
|
+
const lambdaRoleName = config.get("lambdaRole");
|
|
51
|
+
const role = aws.iam.getRole({ name: lambdaRoleName });
|
|
52
|
+
const sourceBucket = aws.s3.getBucket({ bucket: sourceBucketName });
|
|
53
|
+
const sourceKey = `${namespace}/sourcecode.zip`;
|
|
54
|
+
const sourceS3File = new aws.s3.BucketObject(`${namespace}-s3-sourcecode`, {
|
|
55
|
+
bucket: sourceBucket.then((b) => b.id),
|
|
56
|
+
key: sourceKey,
|
|
57
|
+
source: new pulumi.asset.FileArchive(zipFileName),
|
|
58
|
+
sourceHash: zipFileHash,
|
|
59
|
+
});
|
|
60
|
+
const site = new apigateway.RestAPI(namespace, {
|
|
61
|
+
routes: routes.map(({ name, path, method, timeout, memorySize, environmentVariables }) => {
|
|
62
|
+
const variables = environmentVariables
|
|
63
|
+
? environmentVariables.reduce((acc, envName) => {
|
|
64
|
+
if (isReservedEnvName(envName)) {
|
|
65
|
+
return acc;
|
|
66
|
+
}
|
|
67
|
+
if (process.env[envName]) {
|
|
68
|
+
acc[envName] = process.env[envName];
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
console.warn(`Environment variable not set: process.env.${envName}`);
|
|
72
|
+
}
|
|
73
|
+
return acc;
|
|
74
|
+
}, {})
|
|
75
|
+
: {};
|
|
76
|
+
return {
|
|
77
|
+
path,
|
|
78
|
+
method: method,
|
|
79
|
+
eventHandler: new aws.lambda.Function(`${namespace}-${name}`.slice(0, 56), {
|
|
80
|
+
name: `${namespace}-${name}`.slice(0, 56),
|
|
81
|
+
role: role.then((r) => r.arn),
|
|
82
|
+
runtime,
|
|
83
|
+
s3Bucket: sourceBucketName,
|
|
84
|
+
s3Key: sourceS3File.key,
|
|
85
|
+
handler: "entrypoint.handler",
|
|
86
|
+
sourceCodeHash: zipFileHash,
|
|
87
|
+
timeout: timeout + 10,
|
|
88
|
+
memorySize,
|
|
89
|
+
environment: { variables },
|
|
90
|
+
}),
|
|
91
|
+
};
|
|
92
|
+
}),
|
|
93
|
+
});
|
|
94
|
+
const certificate = await aws.acm.getCertificate({
|
|
95
|
+
domain: wildcardCertificateName,
|
|
96
|
+
statuses: ["ISSUED"],
|
|
97
|
+
});
|
|
98
|
+
const zone = await aws.route53.getZone({ name: hostedZone }, { async: true });
|
|
99
|
+
const domain = new aws.apigateway.DomainName(namespace, {
|
|
100
|
+
certificateArn: certificate.arn,
|
|
101
|
+
domainName,
|
|
102
|
+
});
|
|
103
|
+
const _basePathMapping = new aws.apigateway.BasePathMapping(namespace, {
|
|
104
|
+
restApi: site.api,
|
|
105
|
+
stageName: site.stage.stageName,
|
|
106
|
+
domainName: domain.domainName,
|
|
107
|
+
});
|
|
108
|
+
const _dnsRecord = new aws.route53.Record(namespace, {
|
|
109
|
+
type: "A",
|
|
110
|
+
zoneId: zone.zoneId,
|
|
111
|
+
name: domainName,
|
|
112
|
+
aliases: [
|
|
113
|
+
{
|
|
114
|
+
name: domain.cloudfrontDomainName,
|
|
115
|
+
zoneId: domain.cloudfrontZoneId,
|
|
116
|
+
evaluateTargetHealth: false,
|
|
117
|
+
},
|
|
118
|
+
],
|
|
119
|
+
});
|
|
120
|
+
const routesInfo = routes.reduce((acc, { name, method, timeout, path }) => {
|
|
121
|
+
acc[name] = {
|
|
122
|
+
method,
|
|
123
|
+
timeout,
|
|
124
|
+
url: `https://${domainName}${path}`,
|
|
125
|
+
};
|
|
126
|
+
return acc;
|
|
127
|
+
}, {});
|
|
128
|
+
const readme = (await readFile("./README.md")).toString();
|
|
129
|
+
return {
|
|
130
|
+
zipFileName,
|
|
131
|
+
zipFileHash,
|
|
132
|
+
routes: routesInfo,
|
|
133
|
+
runtime,
|
|
134
|
+
hostedZone,
|
|
135
|
+
domainName,
|
|
136
|
+
readme,
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
//# sourceMappingURL=app.js.map
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Default and maximum configuration constants for Lambda functions.
|
|
3
|
+
*
|
|
4
|
+
* @module const
|
|
5
|
+
*/
|
|
6
|
+
/** Default Lambda timeout in seconds. */
|
|
7
|
+
export declare const DEFAULT_TIMEOUT = 30;
|
|
8
|
+
/** Maximum allowed Lambda timeout in seconds. */
|
|
9
|
+
export declare const MAX_TIMEOUT = 90;
|
|
10
|
+
/** Default Lambda memory allocation in megabytes. */
|
|
11
|
+
export declare const DEFAULT_MEMORY_SIZE = 128;
|
|
12
|
+
//# sourceMappingURL=const.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"const.d.ts","sourceRoot":"","sources":["../../src/lib/const.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,yCAAyC;AACzC,eAAO,MAAM,eAAe,KAAK,CAAC;AAElC,iDAAiD;AACjD,eAAO,MAAM,WAAW,KAAK,CAAC;AAE9B,qDAAqD;AACrD,eAAO,MAAM,mBAAmB,MAAM,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Default and maximum configuration constants for Lambda functions.
|
|
3
|
+
*
|
|
4
|
+
* @module const
|
|
5
|
+
*/
|
|
6
|
+
/** Default Lambda timeout in seconds. */
|
|
7
|
+
export const DEFAULT_TIMEOUT = 30;
|
|
8
|
+
/** Maximum allowed Lambda timeout in seconds. */
|
|
9
|
+
export const MAX_TIMEOUT = 90;
|
|
10
|
+
/** Default Lambda memory allocation in megabytes. */
|
|
11
|
+
export const DEFAULT_MEMORY_SIZE = 128;
|
|
12
|
+
//# sourceMappingURL=const.js.map
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CORS preflight route generation.
|
|
3
|
+
*
|
|
4
|
+
* @module cors
|
|
5
|
+
*/
|
|
6
|
+
import type { Route } from "./types.js";
|
|
7
|
+
/**
|
|
8
|
+
* Create an `OPTIONS` preflight route that responds with permissive
|
|
9
|
+
* CORS headers.
|
|
10
|
+
*
|
|
11
|
+
* The generated route allows all origins (`*`), all standard HTTP
|
|
12
|
+
* methods, and all request headers.
|
|
13
|
+
*
|
|
14
|
+
* @param name - A unique name for the preflight route.
|
|
15
|
+
* @param path - The URL path this preflight route handles.
|
|
16
|
+
* @returns A fully configured {@link Route} for the OPTIONS method.
|
|
17
|
+
*/
|
|
18
|
+
export declare function corsPreflight(name: string, path: string): Route;
|
|
19
|
+
//# sourceMappingURL=cors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cors.d.ts","sourceRoot":"","sources":["../../src/lib/cors.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAExC;;;;;;;;;;GAUG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,KAAK,CAqB/D"}
|
package/dist/lib/cors.js
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CORS preflight route generation.
|
|
3
|
+
*
|
|
4
|
+
* @module cors
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Create an `OPTIONS` preflight route that responds with permissive
|
|
8
|
+
* CORS headers.
|
|
9
|
+
*
|
|
10
|
+
* The generated route allows all origins (`*`), all standard HTTP
|
|
11
|
+
* methods, and all request headers.
|
|
12
|
+
*
|
|
13
|
+
* @param name - A unique name for the preflight route.
|
|
14
|
+
* @param path - The URL path this preflight route handles.
|
|
15
|
+
* @returns A fully configured {@link Route} for the OPTIONS method.
|
|
16
|
+
*/
|
|
17
|
+
export function corsPreflight(name, path) {
|
|
18
|
+
return {
|
|
19
|
+
name,
|
|
20
|
+
path,
|
|
21
|
+
method: "OPTIONS",
|
|
22
|
+
timeout: 30,
|
|
23
|
+
memorySize: 128,
|
|
24
|
+
environmentVariables: [],
|
|
25
|
+
handler: async () => {
|
|
26
|
+
return {
|
|
27
|
+
body: "",
|
|
28
|
+
statusCode: 200,
|
|
29
|
+
headers: {
|
|
30
|
+
"Access-Control-Allow-Origin": "*",
|
|
31
|
+
"Access-Control-Allow-Methods": "GET, POST, OPTIONS, PUT, PATCH, DELETE",
|
|
32
|
+
"Access-Control-Allow-Headers": "*",
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=cors.js.map
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Local development server powered by Express.
|
|
3
|
+
*
|
|
4
|
+
* Translates incoming HTTP requests into the AWS API Gateway
|
|
5
|
+
* proxy event format so route handlers can run identically
|
|
6
|
+
* in local development and in Lambda.
|
|
7
|
+
*
|
|
8
|
+
* @module dev
|
|
9
|
+
*/
|
|
10
|
+
import { type Request } from "express";
|
|
11
|
+
import type { LambdaEvent, LocalAppOptions } from "./types.js";
|
|
12
|
+
/**
|
|
13
|
+
* Map an Express {@link Request} to an API Gateway
|
|
14
|
+
* {@link LambdaEvent} proxy integration event.
|
|
15
|
+
*
|
|
16
|
+
* The request body is base64-encoded when it is a `Buffer`
|
|
17
|
+
* (as produced by `express.raw()`), matching how API Gateway
|
|
18
|
+
* delivers binary payloads.
|
|
19
|
+
*
|
|
20
|
+
* @param req - The incoming Express request.
|
|
21
|
+
* @returns A synthetic Lambda event object.
|
|
22
|
+
*/
|
|
23
|
+
export declare function mapRequestToEvent(req: Request): LambdaEvent;
|
|
24
|
+
/**
|
|
25
|
+
* Start a local Express server that serves the given routes using
|
|
26
|
+
* the shared Lambda handler.
|
|
27
|
+
*
|
|
28
|
+
* Each route is registered at both `/{path}` and `/dev/{path}`
|
|
29
|
+
* for backward compatibility with the serverless-offline convention.
|
|
30
|
+
* The server listens on the port specified by the `PORT` environment
|
|
31
|
+
* variable, defaulting to `4000`.
|
|
32
|
+
*
|
|
33
|
+
* @param options - The routes and handler to serve.
|
|
34
|
+
* @param options.routes - Resolved route definitions.
|
|
35
|
+
* @param options.handler - The shared Lambda entrypoint handler.
|
|
36
|
+
*/
|
|
37
|
+
export declare function startLocalApp({ routes, handler, }: LocalAppOptions): Promise<void>;
|
|
38
|
+
//# sourceMappingURL=dev.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dev.d.ts","sourceRoot":"","sources":["../../src/lib/dev.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAgB,EAAE,KAAK,OAAO,EAAiB,MAAM,SAAS,CAAC;AAE/D,OAAO,KAAK,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAE/D;;;;;;;;;;GAUG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,OAAO,GAAG,WAAW,CAgB3D;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,aAAa,CAAC,EAClC,MAAM,EACN,OAAO,GACR,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAsCjC"}
|
package/dist/lib/dev.js
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Local development server powered by Express.
|
|
3
|
+
*
|
|
4
|
+
* Translates incoming HTTP requests into the AWS API Gateway
|
|
5
|
+
* proxy event format so route handlers can run identically
|
|
6
|
+
* in local development and in Lambda.
|
|
7
|
+
*
|
|
8
|
+
* @module dev
|
|
9
|
+
*/
|
|
10
|
+
import express from "express";
|
|
11
|
+
/**
|
|
12
|
+
* Map an Express {@link Request} to an API Gateway
|
|
13
|
+
* {@link LambdaEvent} proxy integration event.
|
|
14
|
+
*
|
|
15
|
+
* The request body is base64-encoded when it is a `Buffer`
|
|
16
|
+
* (as produced by `express.raw()`), matching how API Gateway
|
|
17
|
+
* delivers binary payloads.
|
|
18
|
+
*
|
|
19
|
+
* @param req - The incoming Express request.
|
|
20
|
+
* @returns A synthetic Lambda event object.
|
|
21
|
+
*/
|
|
22
|
+
export function mapRequestToEvent(req) {
|
|
23
|
+
return {
|
|
24
|
+
resource: req.path,
|
|
25
|
+
path: req.path,
|
|
26
|
+
httpMethod: req.method,
|
|
27
|
+
requestContext: {
|
|
28
|
+
resourcePath: req.path,
|
|
29
|
+
httpMethod: req.method,
|
|
30
|
+
path: req.path,
|
|
31
|
+
},
|
|
32
|
+
headers: req.headers,
|
|
33
|
+
multiValueHeaders: {},
|
|
34
|
+
queryStringParameters: req.query,
|
|
35
|
+
body: Buffer.isBuffer(req.body) ? req.body.toString("base64") : "",
|
|
36
|
+
isBase64Encoded: true,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Start a local Express server that serves the given routes using
|
|
41
|
+
* the shared Lambda handler.
|
|
42
|
+
*
|
|
43
|
+
* Each route is registered at both `/{path}` and `/dev/{path}`
|
|
44
|
+
* for backward compatibility with the serverless-offline convention.
|
|
45
|
+
* The server listens on the port specified by the `PORT` environment
|
|
46
|
+
* variable, defaulting to `4000`.
|
|
47
|
+
*
|
|
48
|
+
* @param options - The routes and handler to serve.
|
|
49
|
+
* @param options.routes - Resolved route definitions.
|
|
50
|
+
* @param options.handler - The shared Lambda entrypoint handler.
|
|
51
|
+
*/
|
|
52
|
+
export async function startLocalApp({ routes, handler, }) {
|
|
53
|
+
const app = express();
|
|
54
|
+
app.use(express.raw({
|
|
55
|
+
inflate: true,
|
|
56
|
+
limit: "10mb",
|
|
57
|
+
type: () => true,
|
|
58
|
+
}));
|
|
59
|
+
const port = Number(process.env["PORT"] ?? 4000);
|
|
60
|
+
routes.forEach(({ path, method }) => {
|
|
61
|
+
const m = method || "GET";
|
|
62
|
+
console.log(`registering ${m} ${path}`);
|
|
63
|
+
const callback = async (req, res) => {
|
|
64
|
+
const context = {};
|
|
65
|
+
const result = await handler(mapRequestToEvent(req), context);
|
|
66
|
+
for (const [key, value] of Object.entries(result.headers ?? {})) {
|
|
67
|
+
res.set(key, value);
|
|
68
|
+
}
|
|
69
|
+
res.status(result.statusCode);
|
|
70
|
+
res.send(result.body);
|
|
71
|
+
};
|
|
72
|
+
app[m.toLowerCase()](path, callback);
|
|
73
|
+
app[m.toLowerCase()](`/dev${path}`, callback);
|
|
74
|
+
});
|
|
75
|
+
console.log(`listening on http://localhost:${port}`);
|
|
76
|
+
app.listen(port);
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=dev.js.map
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Domain name utilities.
|
|
3
|
+
*
|
|
4
|
+
* @module domain
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Derive a wildcard certificate name from a fully-qualified domain.
|
|
8
|
+
*
|
|
9
|
+
* Replaces the leftmost label with `*` so the result matches an
|
|
10
|
+
* ACM wildcard certificate (e.g. `data.example.com` → `*.example.com`).
|
|
11
|
+
*
|
|
12
|
+
* @param domainName - The fully-qualified domain name.
|
|
13
|
+
* @returns The wildcard domain (e.g. `*.example.com`).
|
|
14
|
+
*/
|
|
15
|
+
export declare function getWildcardCertificateName(domainName: string): string;
|
|
16
|
+
//# sourceMappingURL=domain.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"domain.d.ts","sourceRoot":"","sources":["../../src/lib/domain.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;;;;;;;GAQG;AACH,wBAAgB,0BAA0B,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAGrE"}
|