@ashishpandeyone/cdk-lambda-web-function 0.1.10 → 0.1.12

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/.jsii CHANGED
@@ -9745,7 +9745,7 @@
9745
9745
  },
9746
9746
  "name": "@ashishpandeyone/cdk-lambda-web-function",
9747
9747
  "readme": {
9748
- "markdown": "# CDK constructs for AWS Lambda Web Functions\n\n<!--BEGIN STABILITY BANNER-->\n\n---\n\n![cdk-constructs: Experimental](https://img.shields.io/badge/cdk--constructs-experimental-important.svg?style=for-the-badge)\n\n> The APIs of higher level constructs in this module are experimental and under active development.\n> They are subject to non-backward compatible changes or removal in any future version. These are\n> not subject to the [Semantic Versioning](https://semver.org/) model and breaking changes will be\n> announced in the release notes. This means that while you may use them, you may need to update\n> your source code when upgrading to a newer version of this package.\n\n---\n\n<!--END STABILITY BANNER-->\n\n[AWS Lambda Web Functions](https://docs.aws.amazon.com/lambda/latest/dg/web-functions.html) run an ordinary Node.js HTTP server (Express, Fastify, Hono, `node:http`) on Lambda and give it an HTTPS endpoint, optionally served from several regions. `WebFunction` deploys one, following the conventions of `aws-cdk-lib/aws-lambda` where the two services overlap.\n\n```ts fixture=bare\nimport * as lambda from 'aws-cdk-lib/aws-lambda';\nimport { WebFunction, WebFunctionAuthType, WebFunctionEndpointType } from '@ashishpandeyone/cdk-lambda-web-function';\n\nconst fn = new WebFunction(this, 'Api', {\n runtime: lambda.Runtime.NODEJS_24_X,\n code: lambda.Code.fromAsset('dist'),\n authType: WebFunctionAuthType.AWS_IAM,\n endpoint: {\n endpointType: WebFunctionEndpointType.MULTI_REGION,\n regions: ['us-east-1', 'eu-west-1'],\n },\n});\n\nnew CfnOutput(this, 'Url', { value: fn.endpoint.url }); // https://<id>.lambda-web.global.on.aws\n```\n\nThe stack's region is the function's **home region**. Multi-region endpoints need a stack with an explicit `env.region`.\n\n## Installation\n\n```bash\nnpm install @ashishpandeyone/cdk-lambda-web-function\n```\n\nIt needs `aws-cdk-lib` 2.273.0 or later and `constructs` 10.5.1 or later (peer dependencies), and Node.js 22 or later. The package is published for TypeScript and JavaScript only.\n\n## Writing the server\n\n1. Listen on the host and port in **`AWS_LAMBDA_HTTP_ENDPOINT`** (`0.0.0.0:3000` today). `PORT` is not set.\n2. Start listening within **10 seconds**, or initialisation fails with `Application.TimeoutListenError`.\n3. Put the entry file at the root of your bundle as **`index.js`**, or set `entrypoint`.\n4. The runtime has **no AWS SDK**: bundle the clients you import.\n5. Code runs on **arm64**.\n\n```js\nconst express = require('express');\n\nconst app = express();\napp.get('/', (req, res) => res.json({ hello: 'world', region: process.env.AWS_REGION }));\n\nconst [host, port] = process.env.AWS_LAMBDA_HTTP_ENDPOINT.split(':');\napp.listen(Number(port), host);\n```\n\nRequests carry `lambda-web-request-id` and `lambda-web-endpoint-name` headers.\n\n## Regions and endpoints\n\nEach `WebFunction` has a default endpoint, `fn.endpoint`, configured with the `endpoint` property. [Add more](#more-endpoints) with `fn.addEndpoint()`.\n\n| `endpointType` | URL | Routing |\n|---|---|---|\n| `HOME_REGION` (default) | `https://<id>.<cell>.lambda-web.<home>.on.aws` | home region only |\n| `MULTI_REGION` | one global `https://<id>.lambda-web.global.on.aws` | nearest region |\n| `PER_REGION` | one URL per region, from `fn.endpoint.regionalUrl(region)` | the region in the hostname |\n\n```ts\nconst perRegion = new WebFunction(this, 'Api', {\n runtime: lambda.Runtime.NODEJS_24_X,\n code: lambda.Code.fromAsset('dist'),\n authType: WebFunctionAuthType.AWS_IAM,\n endpoint: {\n endpointType: WebFunctionEndpointType.PER_REGION,\n regions: ['us-east-1', 'eu-west-1'],\n },\n});\n\nnew CfnOutput(this, 'UsEast1Url', { value: perRegion.endpoint.regionalUrl('us-east-1') });\n// perRegion.endpoint.regions is ['<home region>', 'eu-west-1', 'us-east-1']\n```\n\n- `MULTI_REGION` and `PER_REGION` need at least one region besides the home region; `HOME_REGION` takes none.\n- A MultiRegion endpoint takes about a minute to create.\n- Lambda routes a `MULTI_REGION` endpoint by latency, not by health: it doesn't fail over when a region has problems.\n\n### More endpoints\n\nA function can have up to 10 endpoints, each with its own URL, authentication, regions and limits. For example, a public endpoint next to an IAM-only global one:\n\n```ts\nconst api = new WebFunction(this, 'Api', {\n runtime: lambda.Runtime.NODEJS_24_X,\n code: lambda.Code.fromAsset('dist'),\n authType: WebFunctionAuthType.NONE, // the default endpoint is public\n endpoint: { rateLimit: 1000 }, // requests per second\n});\n\nconst internal = api.addEndpoint('Internal', {\n authType: WebFunctionAuthType.AWS_IAM,\n endpointType: WebFunctionEndpointType.MULTI_REGION,\n regions: ['us-east-1', 'eu-west-1'],\n description: 'Service-to-service traffic',\n maxEnvironments: 50,\n});\ninternal.grantInvoke(consumerRole);\nnew CfnOutput(this, 'InternalUrl', { value: internal.url });\n```\n\n- `rateLimit` takes 0 (blocks all traffic), 100–1,000 in steps of 100, or 2,000–10,000 in steps of 1,000; excess requests get HTTP 429. `maxEnvironments` takes 2–10,000.\n- **Both limits apply separately in each region** an endpoint serves from: a 3-region endpoint with `rateLimit: 1000` accepts up to 1,000 requests per second in each region.\n- Endpoints can serve the same regions. The construct owns one `.local` log group per region, whichever endpoints serve it.\n\n### Changing regions or the endpoint type\n\nAn endpoint's type and regions can't change in place: a change creates a **new endpoint with a new URL**, and the old one is deleted once the new one is live. Two endpoints of one function can't share a name, so:\n\n| `endpointName` | Physical name | Changing `regions` or `endpointType` |\n|---|---|---|\n| omitted (recommended) | `default-<hash>` for `fn.endpoint`, `<id>-<hash>` for added endpoints; the hash covers the type and regions | works: new endpoint, new URL |\n| set, e.g. `'live'` | `live` | also change the name in the same deployment, or it fails with *The endpoint live already exists* (the stack rolls back and the old endpoint keeps serving) |\n\nThe construct warns at synth time when `endpointName` is set.\n\n## Authentication and invoking\n\n| `authType` | Meaning |\n|---|---|\n| `AWS_IAM` | Callers sign requests (SigV4 or SigV4a, signing name `lambda`) and need `lambda:InvokeWebFunctionEndpoint` |\n| `NONE` | **Public**: Lambda does no authentication; your application does (JWT, sessions). The `Authorization` header reaches your application untouched. The construct warns at synth time |\n\n```ts\nfn.grants.invoke(consumerRole); // or fn.grantInvoke(consumerRole), as on lambda.Function\n```\n\nThe grant is on the endpoint's home-region ARN, which IAM evaluates in every region the endpoint serves from, including the global URL. `fn.grants` covers the default endpoint only; grant other endpoints with their own `grantInvoke()` or `grants.invoke()`.\n\n| Endpoint | How to sign |\n|---|---|\n| `HOME_REGION` | SigV4 for the home region |\n| `PER_REGION` | SigV4 for the hostname's region, or SigV4a |\n| `MULTI_REGION` | **SigV4a with region set `*`**: you can't know which region will serve the request |\n\n```js\nimport { SignatureV4a } from '@smithy/signature-v4a';\nimport { Sha256 } from '@aws-crypto/sha256-js';\nimport { fromNodeProviderChain } from '@aws-sdk/credential-providers';\n\nconst host = new URL(url).hostname; // url: the endpoint's URL\nconst signer = new SignatureV4a({ service: 'lambda', region: '*', sha256: Sha256, credentials: fromNodeProviderChain() });\nconst signed = await signer.sign(\n { method: 'GET', protocol: 'https:', hostname: host, path: '/', headers: { host } },\n { signingRegion: '*' },\n);\nconst response = await fetch(`https://${host}/`, { headers: signed.headers });\n```\n\n### Other accounts and AWS services\n\nA caller in another account needs permission twice: in its own IAM policy, and in the function's **resource policy**. `grantInvoke` handles the function's side: a role in the function's account only gets an IAM policy statement, while a principal in another account or an AWS service also gets a statement in the resource policy.\n\n```ts\nfn.grantInvoke(iam.Role.fromRoleArn(this, 'Partner', 'arn:aws:iam::444455556666:role/caller'));\n```\n\nFor principals you can't grant to (a whole account, an organization, an AWS service with conditions), use `addPermission`. The `id` becomes the statement's `Sid`:\n\n```ts\nfn.addPermission('PartnerAccount', { principal: new iam.AccountPrincipal('444455556666') });\nfn.addPermission('MyOrg', { principal: new iam.OrganizationPrincipal('o-a1b2c3d4e5'), endpoint: fn.endpoint });\nfn.addPermission('Cdn', {\n principal: new iam.ServicePrincipal('cloudfront.amazonaws.com'),\n sourceArn: `arn:aws:cloudfront::${this.account}:distribution/EDFDVBD6EXAMPLE`,\n});\n```\n\n`addPermission` allows calling every endpoint unless you pass `endpoint`. For other actions, such as letting another account create revisions, use `fn.addToResourcePolicy(statement)`. Statements use the endpoint ARN for invoking and the function ARN for actions on the function.\n\n- **Each stack writes only its own statements.** A Web Function has a single resource policy, and Web Functions have no `AddPermission`. So each stack that adds statements gets a custom resource (`Custom::LambdaWebResourcePolicyStatements`). It reads the policy, replaces the statements that stack wrote before, and writes the policy back, retrying if another writer changed it in the meantime. Statements from other stacks, and ones added outside CloudFormation, are kept. Writers include the function's stack, a stack with a CloudFront distribution, and a stack that imports the function in the same account.\n- **Sids are unique in the policy.** `addPermission`'s `id` becomes the statement's `Sid`, and other statements get a generated one (`Cdk<hash><n>`). If another stack or tool already uses that `Sid` for different content, the deployment fails with the `Sid`.\n- **Don't also attach an `AWS::Lambda::ResourcePolicy` yourself.** That resource replaces the whole document on every update, dropping every other writer's statements.\n- **Public policies are rejected.** Lambda refuses a policy that lets anyone call the function, and the construct fails synthesis first. A `*` principal is only accepted with a condition that pins the caller, such as `aws:PrincipalOrgID` (`iam.OrganizationPrincipal`) or `aws:SourceAccount`. For a public endpoint, use `authType: NONE`.\n- **Scope AWS services to your resource.** Lambda accepts a service principal with no condition, which would let that service call the endpoint on behalf of any account. The construct warns unless you give `sourceArn` or `sourceAccount` (or an `aws:SourceArn`, `aws:SourceAccount`, `aws:SourceOrgID` or `aws:SourceOrgPaths` condition).\n- Lambda copies the policy to every region the function's endpoints serve from. The copy is eventually consistent, and in tests changes applied in every region within seconds.\n\n### CloudFront\n\nPut a CloudFront distribution (`aws-cdk-lib/aws-cloudfront`) in front of an `AWS_IAM` endpoint to serve the public with a custom domain, caching and AWS WAF. Viewers don't sign anything, and only your distribution may call the endpoint:\n\n```ts\nnew cloudfront.Distribution(this, 'Cdn', {\n defaultBehavior: {\n origin: WebFunctionOrigin.withOriginAccessControl(fn.endpoint),\n viewerProtocolPolicy: cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS,\n allowedMethods: cloudfront.AllowedMethods.ALLOW_ALL,\n cachePolicy: cloudfront.CachePolicy.CACHING_DISABLED,\n originRequestPolicy: cloudfront.OriginRequestPolicy.ALL_VIEWER_EXCEPT_HOST_HEADER,\n },\n});\n```\n\n`withOriginAccessControl` adds:\n- an origin access control (origin type `lambda`, signing behavior `always-amz-auth`, the only one CloudFront accepts for Web Function origins), one per stack and signing protocol: SigV4 for a regional domain, SigV4a for a `MULTI_REGION` endpoint's global domain;\n- a statement in the function's resource policy that allows `cloudfront.amazonaws.com` to call this endpoint on behalf of this distribution only (`aws:SourceArn`).\n\nSome things to know:\n- **Forward everything except `Host`.** The endpoint answers 404 when `Host` is the distribution's domain, so use `ALL_VIEWER_EXCEPT_HOST_HEADER` (not `ALL_VIEWER`), or a policy of your own without `Host`.\n- **Your application can still authenticate viewers.** CloudFront signs in its own headers and passes the viewer's `Authorization` header (a JWT, for example) through unchanged, but only when `Authorization` is in the cache policy. CloudFront drops it from `GET` and `HEAD` requests otherwise:\n\n ```ts\n const forwardAuthorization = new cloudfront.CachePolicy(this, 'ForwardAuthorization', {\n headerBehavior: cloudfront.CacheHeaderBehavior.allowList('Authorization'),\n minTtl: Duration.seconds(0),\n defaultTtl: Duration.seconds(0),\n maxTtl: Duration.seconds(1), // a cache key with headers needs caching enabled; 0/0/1 keeps it off in practice\n });\n ```\n- Request bodies work without clients sending `x-amz-content-sha256`: CloudFront signs `POST`, `PUT` and `PATCH` requests with an unsigned payload.\n- A `PER_REGION` endpoint's origin calls one region: the home region, or the one you pass as `region`. Use one origin per region, for example in an origin group.\n- A `MULTI_REGION` endpoint can't include opt-in regions (such as `me-central-1`), because CloudFront can't sign for them. The construct fails synthesis.\n- The distribution can be in another stack, in the function's account. Its stack writes the CloudFront statement into the function's resource policy, so the function's stack doesn't depend on it.\n- That stack can also be in another region, for example us-east-1 for an ACM certificate. CDK then passes the endpoint's domain and ARN with its cross-region references: SSM parameters plus custom resources of its own.\n- **Those references are strong by default.** A change that replaces the endpoint gives it a new domain and ARN. That includes a new endpoint type or region set, or a renamed function. The function's stack can't update a value while the distribution's stack still uses the old one, the same as with `Fn::ImportValue` in one region. Deploy such a change in two steps, or use weak references: CDK's synth warning `@aws-cdk/core:crossStackReferencesDefaultStrong` explains how.\n- Lambda@Edge origin-request and origin-response triggers aren't supported on behaviors with this origin.\n- The endpoint stays reachable at its own domain for signed requests your policies allow. Until a new distribution finishes deploying, requests through it can get a transient 403.\n- `new WebFunctionOrigin(endpoint)` is for a public (`NONE`) endpoint, for example behind AWS WAF. Lambda doesn't authenticate those requests, so the endpoint's own domain stays public too.\n\n### Functions defined elsewhere\n\nImport a Web Function defined in another stack or app to grant access to it, read its metrics, or reference its revisions:\n\n```ts\nconst orders = WebFunction.fromWebFunctionArn(this, 'Orders', 'arn:aws:lambda:us-east-1:111122223333:web-function/orders');\norders.grantInvoke(consumerRole); // every endpoint of the function\n\nconst live = WebFunction.fromWebFunctionAttributes(this, 'Live', {\n webFunctionName: 'orders', // in this stack's account and region\n endpointName: 'live', // grantInvoke allows this endpoint only\n});\nlive.metricConcurrentRequests().createAlarm(this, 'Busy', { threshold: 60, evaluationPeriods: 3 });\n```\n\nAn imported function's metrics default to its own region. Its endpoints, revisions and log groups stay with the stack that defines it.\n\nAn imported function works like a `WebFunction` here. `grantInvoke` and `addToResourcePolicy` write the importing stack's statements into the function's resource policy, in the function's region. This works only when the function is in the stack's account. For a function in another account, `grantInvoke` adds to the caller's IAM policy only, and `addToResourcePolicy` adds nothing and warns, so ask that account to allow the caller.\n\n## Configuration\n\n```ts\nconst api = new WebFunction(this, 'Api', {\n runtime: lambda.Runtime.NODEJS_24_X,\n code: lambda.Code.fromAsset('build'),\n authType: WebFunctionAuthType.AWS_IAM,\n entrypoint: 'server.js', // default index.js\n webFunctionName: 'orders-api', // default: generated from the construct path\n description: 'Orders HTTP API',\n environment: { TABLE_NAME: table.tableName },\n timeout: Duration.seconds(60), // per request: 3 seconds to 15 minutes, default 30 seconds\n maxConcurrencyPerEnvironment: 32, // requests per environment at once: 1-128, default 64\n});\n\napi.addEnvironment('FEATURE_FLAG', 'on');\ntable.grantReadWriteData(api); // the default role trusts lambda.amazonaws.com\n```\n\n- **Each execution environment has 2 vCPUs and 2 GB of memory.** There's no memory setting as on Lambda functions; scale with `maxConcurrencyPerEnvironment` and more environments instead.\n- Requests handled by one environment share module-level state, so keep handlers concurrency-safe.\n- Environment variable names starting with `AWS_` are reserved.\n- **The default execution role can only write to the function's own log groups**: the home region's, and the `.local` copy in every region it serves from. That's the minimum AWS documents for Web Functions. It doesn't use `AWSLambdaBasicExecutionRole`, which can also create any log group. Pass `role` to use your own, and include those log permissions.\n- **Renaming the function replaces it and changes its URL.** A generated name depends on the construct path, so moving the construct renames it too.\n\n### Defaults for every function (property injection)\n\nTo give every function under a scope the same defaults, register a property injector for `WebFunction.PROPERTY_INJECTION_ID`, as you would for aws-cdk-lib's constructs. For example, a team's log retention:\n\n```ts\nclass TeamDefaults implements IPropertyInjector {\n public readonly constructUniqueId = WebFunction.PROPERTY_INJECTION_ID;\n public inject(originalProps: WebFunctionProps): WebFunctionProps {\n // The team's defaults, with explicit props winning.\n return Object.assign({ logRetention: logs.RetentionDays.ONE_MONTH }, originalProps);\n }\n}\nPropertyInjectors.of(this).add(new TeamDefaults()); // or on the app, for every stack\n```\n\n- An injector applies to functions created under the scope it's added to. The nearest one wins.\n- `NodejsWebFunction` has its own ID (`NodejsWebFunction.PROPERTY_INJECTION_ID`). Its injector runs first, then `WebFunction`'s, as with aws-cdk-lib's `NodejsFunction` and `Function`.\n\n### Bundling TypeScript with `NodejsWebFunction`\n\n`NodejsWebFunction` is a `WebFunction` that bundles your server from a TypeScript or JavaScript entry file with esbuild, like `NodejsFunction` does for Lambda functions:\n\n```ts\nnew NodejsWebFunction(this, 'Api', {\n entry: 'src/server.ts',\n authType: WebFunctionAuthType.AWS_IAM,\n bundling: {\n minify: true, // default true\n sourceMap: true, // default true\n format: lambdaNodejs.OutputFormat.ESM, // default ESM (index.mjs); CJS gives index.js\n externalModules: [], // default: bundle everything you import\n },\n});\n```\n\n- **Defaults:** `runtime` is `NODEJS_24_X`, and the esbuild target follows the runtime (`node24`). The project root is the directory of the nearest lock file above `entry` (override it with `depsLockFilePath` or `projectRoot`).\n- **It also sets what a `lambda.Code` can't:** the entry point that matches the bundle, and `NODE_OPTIONS=--enable-source-maps` when source maps are on, appended to any `NODE_OPTIONS` you set, so stack traces point at your `.ts` files (e.g. `/var/task/src/server.ts:22:12`). Source map paths are relative to the project root, so no build-machine paths are deployed.\n- **ESM bundles include a `require` shim**, so CommonJS dependencies that load Node.js built-ins (Express, for example) work.\n- **The AWS SDK is bundled like any other dependency**, since the runtime has none. Only the clients you import are included.\n- **Bundling runs locally** when esbuild is installed in your project, and otherwise in Docker (an arm64 AWS SAM build image; set `forceDockerBundling` to always use Docker).\n- **Not yet supported:** installing native modules next to the bundle (`nodeModules`) and command hooks. For native dependencies, use `WebFunction` with `Code.fromAsset(..., { bundling: { image, command } })`.\n\n### Code\n\n`code` takes any `lambda.Code` that produces a zip in S3: `fromAsset`, `fromBucket`/`fromBucketV2`, `fromCustomCommand`, `fromDockerBuild`. Inline code and container images fail at synth time.\n\nLambda Web Functions download code as the `lambda.amazonaws.com` service principal, from a **versioned** bucket, and the CDK bootstrap bucket allows neither. So each stack gets a small code bucket, and the code is copied into it on deploy. The bucket is emptied and deleted with the stack.\n\nCopying code, managing the function's log groups in every region, looking up regional URLs, cleaning up old revisions, creating rollback alarms in other regions and emptying the code bucket all run in one custom resource Lambda per stack. Its logs go to a log group the stack owns, kept for a month and deleted with the stack.\n\n## Revisions\n\nAny change to code or configuration creates a new **revision** (`fn.currentRevision`), and endpoints switch to it once it's ready. A failed deployment rolls back to the previous revision. If nothing changed, nothing is deployed.\n\nEarlier revisions are kept, so you can roll back or route traffic to them. After each deployment, the construct deletes the oldest beyond `revisionRetention` (default 20, up to 50). It never deletes a revision an endpoint routes to, or one the previous deployment used, which a rollback returns to.\n\n```ts\nnew WebFunction(this, 'Api', {\n runtime: lambda.Runtime.NODEJS_24_X,\n code: lambda.Code.fromAsset('dist'),\n authType: WebFunctionAuthType.AWS_IAM,\n revisionRetention: 5,\n});\n```\n\n### Routing to earlier revisions\n\nReference an earlier revision by its ID (for example from a `CfnOutput` of `fn.currentRevision.revisionId`, or `aws lambda-web list-web-function-revisions`). Then pin an endpoint to it, or split traffic between two revisions:\n\n```ts\nconst previous = WebFunctionRevision.fromRevisionAttributes(this, 'Previous', {\n webFunction: fn,\n revisionId: 'rev-a1b2c',\n});\n\n// Keep the default endpoint on the previous revision, and send 10% to the new one:\nfn.endpoint.routeTraffic(previous, [{ revision: fn.currentRevision, weight: 0.1 }]);\n\n// Or a second endpoint that stays on the previous revision while fn.endpoint moves on:\nfn.addEndpoint('Stable', { authType: WebFunctionAuthType.AWS_IAM, revision: previous });\n```\n\n- An endpoint routes to at most two revisions. The additional revision's `weight` is a whole percentage, from 0.01 to 0.99.\n- To promote, remove `routeTraffic` (or `revision`) and deploy: the endpoint serves the current revision again.\n- Lambda Web Function metrics have no revision dimension. To compare a split's revisions, log a version you control (for example an environment variable) from your application.\n\n### Canary and linear deployments\n\nBy default an endpoint moves all its traffic to a new revision at once. With `deployment`, it moves in steps instead, watching CloudWatch alarms: if one goes into ALARM, the endpoint goes back to the revision it served before and the deployment fails, so CloudFormation rolls the stack back.\n\n```ts\nconst api = new WebFunction(this, 'Api', {\n runtime: lambda.Runtime.NODEJS_24_X,\n code: lambda.Code.fromAsset('dist'),\n authType: WebFunctionAuthType.AWS_IAM,\n endpoint: {\n // 10% of the traffic for 10 minutes, then all of it; back if more than 2% of requests get a 5XX.\n deployment: WebFunctionDeployment.canary({ percent: 10, bake: Duration.minutes(10), rollbackOn: { serverErrorRate: 2 } }),\n },\n});\n\n// 25% more every 5 minutes, watching an alarm of your own. Each endpoint has its own deployment.\n// errorRate: any cloudwatch.IAlarm, e.g. on a metric your application publishes.\napi.addEndpoint('Public', {\n authType: WebFunctionAuthType.NONE,\n deployment: WebFunctionDeployment.linear({ percent: 25, interval: Duration.minutes(5), alarms: [errorRate] }),\n});\n```\n\n| | Steps | Limits |\n|---|---|---|\n| `allAtOnce()` (default) | 100% | |\n| `canary({ percent, bake })` | `percent`, then 100% after `bake` | `percent` 1–99, `bake` 1–45 minutes |\n| `linear({ percent, interval })` | `percent` more every `interval` | `percent` 5–50, `interval` from 1 minute, at most 45 minutes in all |\n\nHow it works:\n\n- Each deployment that creates a revision (code or configuration) moves the traffic from the revision the endpoint serves at that moment, which is also what it goes back to. If someone switched the endpoint outside CloudFormation, the deployment starts from there.\n- Each step waits until every region of a `MULTI_REGION` or `PER_REGION` endpoint has applied it (usually about 15 seconds), and if a region fails to update, the endpoint goes back as for an alarm. The bake or interval counts from the start of the step.\n- Alarms are checked before the first step (an alarm already in ALARM stops the deployment before any traffic moves), throughout every step, and once the new revision has all the traffic. They can be in any region, and composite alarms work. `INSUFFICIENT_DATA` doesn't stop a deployment.\n- A rollback of the stack, for any reason, moves the traffic back at once, without waiting or checking alarms.\n- A stack's first deployment, and a new endpoint (including one renamed by a region change), serve the revision at once: there's nothing to move from.\n- A deployment can take up to an hour: CloudFormation waits for it before it updates the endpoint. Deployments of different endpoints run in parallel.\n- A canary or linear endpoint always moves to the current revision, so it can't also use `revision`, `additionalRevisions` or `routeTraffic()`. Use `allAtOnce()` to deploy a fix immediately, for example while an alarm is in ALARM.\n\n#### Rollback alarms\n\n`rollbackOn` makes the endpoint create alarms on its own error rates, and the deployment watches them alongside any `alarms` you pass:\n\n| `rollbackOn` | Default | |\n|---|---|---|\n| `serverErrorRate` | none | roll back when more than this % of requests get a 5XX |\n| `clientErrorRate` | none | the same for 4XX (including 429s from `rateLimit` and `maxEnvironments`) |\n| `minimumRequests` | 10 | periods with fewer requests don't count, so one failed request out of two doesn't roll back |\n| `period`, `evaluationPeriods` | 1 minute, 2 | consecutive periods over the threshold before rolling back |\n\n- **One alarm per threshold in every region the endpoint serves from.** Metrics are published in the region that served the request, and an alarm can only watch metrics in its own region. The home region's alarm is a normal CloudFormation alarm; the others are created and deleted with the endpoint by a custom resource, and named `LambdaWeb-<function>-<endpoint>-5xx-rate` (or `-4xx-rate`). `endpoint.rollbackAlarms` lists them all, e.g. to add notifications to the home region's.\n- **The alarms see the whole endpoint.** Lambda Web Function metrics have no revision dimension, so at 10% a new revision's errors are about a tenth of the endpoint's: set the threshold low enough to notice that, or alarm on metrics your application publishes with its version.\n- **5XX includes the platform's 503s** while the endpoint scales up for a sudden burst, not only your application's errors.\n- **A step should outlast the alarm.** CloudWatch needs `period` × `evaluationPeriods`, plus a couple of minutes of delay, to fire; the construct warns when a canary's bake or a linear interval is shorter.\n\nThe deployment runs in a few resources per stack, created on first use: two Lambda functions, a Step Functions state machine that calls them every 15 seconds, and a log group.\n\n## Metrics\n\nEndpoints and functions have helpers for their CloudWatch metrics (namespace `AWS/Lambda/Web`), like `lambda.Function`'s:\n\n```ts\nfn.endpoint.metricServerErrorRate().createAlarm(this, 'ErrorRate', { threshold: 1, evaluationPeriods: 3 });\nfn.endpoint.metricRequestThrottles().createAlarm(this, 'Throttled', { threshold: 10, evaluationPeriods: 3 });\nfn.metricConcurrentRequests().createAlarm(this, 'Saturated', { threshold: 60, evaluationPeriods: 3 });\n```\n\n| Endpoint | Metric |\n|---|---|\n| `metricRequests()` | `ExecutedRequests` (there's no separate 2XX count) |\n| `metricClientErrors()`, `metricServerErrors()` | `HTTPResponseStatusClass4XX`, `HTTPResponseStatusClass5XX` |\n| `metricClientErrorRate()`, `metricServerErrorRate()` | 4XX or 5XX as a % of requests (math expression) |\n| `metricActiveEnvironments()` | `EndpointActiveEnvironments`, to compare with `maxEnvironments` |\n| `metricRequestThrottles()`, `metricEnvironmentThrottles()` | 429s from `rateLimit` and from `maxEnvironments` |\n\n| Function | Metric |\n|---|---|\n| `metricConcurrentRequests()` | `EnvironmentConcurrentRequests` (maximum), to compare with `maxConcurrencyPerEnvironment` |\n| `metricMaxMemoryUsage()` | `EnvironmentMaxMemoryUsage` (maximum) |\n| `metricActiveTime()`, `metricCpuTime()`, `metricResponseBytes()` | `EnvironmentActiveTime`, `EnvironmentCPUTime`, `ResponseBytes` |\n\n- **Metrics stay in the region that served the request**; unlike logs, they aren't copied to the home region. Every helper takes `region`. Dashboards can show any region, but an alarm can only use metrics from its own stack's region.\n- 4XX, 5XX and the throttle metrics only exist once they're non-zero: alarm on them with `treatMissingData: NOT_BREACHING`. The rate helpers count missing errors as 0.\n- There's no latency metric. Publish one from your application (e.g. with the CloudWatch embedded metric format).\n\n## Logs in every region\n\nWeb Functions log in two tiers:\n\n```text\n every serving region home region\n ┌──────────────────┐ ┌──────────────────────────────────────────┐\n │ <group>.local │── copied ───►│ <group> every region's logs │\n │ (that region's │ (seconds) │ streams …[rev]<instance>-<acct>-<region> │\n │ environments) │ │ <group>.local (home-region traffic) │\n └──────────────────┘ └──────────────────────────────────────────┘\n```\n\n**Query `fn.logGroup`**, the home-region group that receives every region's logs. The service would create missing groups without retention and never delete them, so the construct creates and owns all of them: both home-region groups, and `<group>.local` in every other region any of its endpoints serves from.\n\n```ts\nnew WebFunction(this, 'Api', {\n runtime: lambda.Runtime.NODEJS_24_X,\n code: lambda.Code.fromAsset('dist'),\n authType: WebFunctionAuthType.AWS_IAM,\n logRetention: logs.RetentionDays.THREE_MONTHS, // default ONE_MONTH, every group in every region\n logRemovalPolicy: RemovalPolicy.DESTROY, // default RETAIN\n applicationLogLevelV2: lambda.ApplicationLogLevel.WARN,\n systemLogLevelV2: lambda.SystemLogLevel.INFO, // DEBUG, INFO or WARN\n});\n```\n\n- The default group is `/aws/lambda/web/<function name>`. Log lines are JSON; application lines carry `requestId`, `endpointName` and `region`.\n- Every event is stored twice (`.local` plus the home-region copy), so expect about double log ingestion, plus cross-region transfer.\n- With `DESTROY`, deletion waits about a minute after the function is gone, because environments flush their last lines late.\n- Subscribe only to `fn.logGroup`: subscribing to the `.local` groups as well delivers every event twice.\n\n## cdk-nag\n\nThe library acknowledges the [cdk-nag](https://github.com/cdklabs/cdk-nag) AwsSolutions findings on the resources it creates, each with a reason, so they don't show up in your reports:\n\n| Finding | Resource | Reason |\n|---|---|---|\n| IAM5 (wildcards) | the default execution role | log streams of the function's own log groups, in every region it serves from |\n| IAM5 | the deployment handlers' role (canary and linear deployments) | their own log group, `cloudwatch:DescribeAlarms` (composite alarms need `*`), and the revisions and endpoints of the functions they deploy |\n| IAM4, IAM5, SF1, SF2 | the CDK Provider framework behind deployments | CDK's own functions and waiter state machine |\n| S1 (access logs) | the stack's code bucket | it only holds copies of the stack's code assets |\n\n- **This works with both cdk-nag versions**, without depending on cdk-nag. cdk-nag 3 reads CDK's `Validations.of(...).acknowledge()`, one ID per finding. cdk-nag 2 reads `cdk_nag` metadata in the template.\n- **Your own statements and resources are still checked.** That includes statements you add to the function's role (`addToRolePolicy`, grants) and your CloudFront distribution's settings.\n- Only the AwsSolutions pack is covered; findings from the HIPAA, NIST and PCI packs aren't acknowledged.\n\n## Upgrading\n\n### From 0.1.5 to 0.1.7, with a resource policy\n\nVersions 0.1.5 to 0.1.7 wrote the function's resource policy with an `AWS::Lambda::ResourcePolicy`. Later versions don't (0.1.7 has the same code as 0.1.6). When an update removes that resource, CloudFormation deletes the function's **whole** resource policy at the end of the update, including the statements the new version just wrote. Callers that rely on the policy then get 403: other accounts, AWS services, and CloudFront.\n\nIf your function has a resource policy (from `addPermission`, a grant to another account or an AWS service, or `WebFunctionOrigin`), first deploy once on the version you have, with the old resource retained:\n\n```ts\n(fn.node.findChild('ResourcePolicy') as CfnResource).applyRemovalPolicy(RemovalPolicy.RETAIN);\n```\n\nThen upgrade, without changing your statements in the same deployment. The new version takes over the statements it writes itself. A statement you removed in the same deployment would stay in the policy.\n\n## Limitations\n\n- Not yet supported: VPC access (network connectors), KMS encryption, and tracing.\n- **Hotswap skips Web Functions without a warning.** `cdk deploy --hotswap` and `cdk watch`, which hotswaps by default, report `(no changes)` for a code or configuration change and the endpoint keeps serving the old revision. Use `cdk watch --hotswap-fallback` (or `cdk deploy --hotswap-fallback`): it deploys Web Function changes through CloudFormation, in about 40 seconds for a home-region endpoint.\n- Fixed limits: 512 MB of code (unzipped) and 32 KB of environment variables per revision, 512 MB of `/tmp` per execution environment, and 128 KiB of headers per request.\n- Quotas: 10 endpoints and about 50 revisions per function (Lambda cleans up older revisions automatically), and 10,000 requests per second per endpoint. Per account and region: 100,000 requests per second and 2,000 Arm64 vCPUs (each execution environment uses 2). The account quotas are adjustable, and new accounts start lower. See [Lambda quotas](https://docs.aws.amazon.com/lambda/latest/dg/gettingstarted-limits.html).\n\n## Development\n\nTooling is pinned in `mise.toml` (Node 24, AWS CDK CLI). Personal settings, such as the AWS profile for the live end-to-end tests, go in a git-ignored `mise.local.toml`.\n\n```bash\nmise install\nnpx projen build # compile, unit tests, lint, API docs\nnpx projen e2e # deploy, verify and destroy real stacks in three regions (needs AWS credentials)\n```\n"
9748
+ "markdown": "# CDK constructs for AWS Lambda Web Functions\n\n<!--BEGIN STABILITY BANNER-->\n\n---\n\n![cdk-constructs: Experimental](https://img.shields.io/badge/cdk--constructs-experimental-important.svg?style=for-the-badge)\n\n> The APIs of higher level constructs in this module are experimental and under active development.\n> They are subject to non-backward compatible changes or removal in any future version. These are\n> not subject to the [Semantic Versioning](https://semver.org/) model and breaking changes will be\n> announced in the release notes. This means that while you may use them, you may need to update\n> your source code when upgrading to a newer version of this package.\n\n---\n\n<!--END STABILITY BANNER-->\n\n[AWS Lambda Web Functions](https://docs.aws.amazon.com/lambda/latest/dg/web-functions.html) run an ordinary Node.js HTTP server (Express, Fastify, Hono, `node:http`) on Lambda and give it an HTTPS endpoint, optionally served from several regions. `WebFunction` deploys one, following the conventions of `aws-cdk-lib/aws-lambda` where the two services overlap.\n\n```ts fixture=bare\nimport * as lambda from 'aws-cdk-lib/aws-lambda';\nimport { WebFunction, WebFunctionAuthType, WebFunctionEndpointType } from '@ashishpandeyone/cdk-lambda-web-function';\n\nconst fn = new WebFunction(this, 'Api', {\n runtime: lambda.Runtime.NODEJS_24_X,\n code: lambda.Code.fromAsset('dist'),\n authType: WebFunctionAuthType.AWS_IAM,\n endpoint: {\n endpointType: WebFunctionEndpointType.MULTI_REGION,\n regions: ['us-east-1', 'eu-west-1'],\n },\n});\n\nnew CfnOutput(this, 'Url', { value: fn.endpoint.url }); // https://<id>.lambda-web.global.on.aws\n```\n\nThe stack's region is the function's **home region**. Multi-region endpoints need a stack with an explicit `env.region`.\n\n## Installation\n\n```bash\nnpm install @ashishpandeyone/cdk-lambda-web-function\n```\n\nIt needs `aws-cdk-lib` 2.273.0 or later and `constructs` 10.5.1 or later (peer dependencies), and Node.js 22 or later. The package is published for TypeScript and JavaScript only.\n\n## Writing the server\n\n1. Listen on the host and port in **`AWS_LAMBDA_HTTP_ENDPOINT`** (`0.0.0.0:3000` today). `PORT` is not set.\n2. Start listening within **10 seconds**, or initialisation fails with `Application.TimeoutListenError`.\n3. Put the entry file at the root of your bundle as **`index.js`**, or set `entrypoint`.\n4. The runtime has **no AWS SDK**: bundle the clients you import.\n5. Code runs on **arm64**.\n\n```js\nconst express = require('express');\n\nconst app = express();\napp.get('/', (req, res) => res.json({ hello: 'world', region: process.env.AWS_REGION }));\n\nconst [host, port] = process.env.AWS_LAMBDA_HTTP_ENDPOINT.split(':');\napp.listen(Number(port), host);\n```\n\nRequests carry `lambda-web-request-id` and `lambda-web-endpoint-name` headers.\n\n## Regions and endpoints\n\nEach `WebFunction` has a default endpoint, `fn.endpoint`, configured with the `endpoint` property. [Add more](#more-endpoints) with `fn.addEndpoint()`.\n\n| `endpointType` | URL | Routing |\n|---|---|---|\n| `HOME_REGION` (default) | `https://<id>.<cell>.lambda-web.<home>.on.aws` | home region only |\n| `MULTI_REGION` | one global `https://<id>.lambda-web.global.on.aws` | nearest region |\n| `PER_REGION` | one URL per region, from `fn.endpoint.regionalUrl(region)` | the region in the hostname |\n\n```ts\nconst perRegion = new WebFunction(this, 'Api', {\n runtime: lambda.Runtime.NODEJS_24_X,\n code: lambda.Code.fromAsset('dist'),\n authType: WebFunctionAuthType.AWS_IAM,\n endpoint: {\n endpointType: WebFunctionEndpointType.PER_REGION,\n regions: ['us-east-1', 'eu-west-1'],\n },\n});\n\nnew CfnOutput(this, 'UsEast1Url', { value: perRegion.endpoint.regionalUrl('us-east-1') });\n// perRegion.endpoint.regions is ['<home region>', 'eu-west-1', 'us-east-1']\n```\n\n- `MULTI_REGION` and `PER_REGION` need at least one region besides the home region; `HOME_REGION` takes none.\n- A MultiRegion endpoint takes about a minute to create.\n- Lambda routes a `MULTI_REGION` endpoint by latency, not by health: it doesn't fail over when a region has problems.\n\n### More endpoints\n\nA function can have up to 10 endpoints, each with its own URL, authentication, regions and limits. For example, a public endpoint next to an IAM-only global one:\n\n```ts\nconst api = new WebFunction(this, 'Api', {\n runtime: lambda.Runtime.NODEJS_24_X,\n code: lambda.Code.fromAsset('dist'),\n authType: WebFunctionAuthType.NONE, // the default endpoint is public\n endpoint: { rateLimit: 1000 }, // requests per second\n});\n\nconst internal = api.addEndpoint('Internal', {\n authType: WebFunctionAuthType.AWS_IAM,\n endpointType: WebFunctionEndpointType.MULTI_REGION,\n regions: ['us-east-1', 'eu-west-1'],\n description: 'Service-to-service traffic',\n maxEnvironments: 50,\n});\ninternal.grantInvoke(consumerRole);\nnew CfnOutput(this, 'InternalUrl', { value: internal.url });\n```\n\n- `rateLimit` takes 0 (blocks all traffic), 100–1,000 in steps of 100, or 2,000–10,000 in steps of 1,000; excess requests get HTTP 429. `maxEnvironments` takes 2–10,000.\n- **Both limits apply separately in each region** an endpoint serves from: a 3-region endpoint with `rateLimit: 1000` accepts up to 1,000 requests per second in each region.\n- Endpoints can serve the same regions. The construct owns one `.local` log group per region, whichever endpoints serve it.\n\n### Changing regions or the endpoint type\n\nAn endpoint's type and regions can't change in place: a change creates a **new endpoint with a new URL**, and the old one is deleted once the new one is live. Two endpoints of one function can't share a name, so:\n\n| `endpointName` | Physical name | Changing `regions` or `endpointType` |\n|---|---|---|\n| omitted (recommended) | `default-<hash>` for `fn.endpoint`, `<id>-<hash>` for added endpoints; the hash covers the type and regions | works: new endpoint, new URL |\n| set, e.g. `'live'` | `live` | also change the name in the same deployment, or it fails with *The endpoint live already exists* (the stack rolls back and the old endpoint keeps serving) |\n\nThe construct warns at synth time when `endpointName` is set.\n\n## Authentication and invoking\n\n| `authType` | Meaning |\n|---|---|\n| `AWS_IAM` | Callers sign requests (SigV4 or SigV4a, signing name `lambda`) and need `lambda:InvokeWebFunctionEndpoint` |\n| `NONE` | **Public**: Lambda does no authentication; your application does (JWT, sessions). The `Authorization` header reaches your application untouched. The construct warns at synth time |\n\n```ts\nfn.grants.invoke(consumerRole); // or fn.grantInvoke(consumerRole), as on lambda.Function\n```\n\nThe grant is on the endpoint's home-region ARN, which IAM evaluates in every region the endpoint serves from, including the global URL. `fn.grants` covers the default endpoint only; grant other endpoints with their own `grantInvoke()` or `grants.invoke()`.\n\n| Endpoint | How to sign |\n|---|---|\n| `HOME_REGION` | SigV4 for the home region |\n| `PER_REGION` | SigV4 for the hostname's region, or SigV4a |\n| `MULTI_REGION` | **SigV4a with region set `*`**: you can't know which region will serve the request |\n\n```js\nimport { SignatureV4a } from '@smithy/signature-v4a';\nimport { Sha256 } from '@aws-crypto/sha256-js';\nimport { fromNodeProviderChain } from '@aws-sdk/credential-providers';\n\nconst host = new URL(url).hostname; // url: the endpoint's URL\nconst signer = new SignatureV4a({ service: 'lambda', region: '*', sha256: Sha256, credentials: fromNodeProviderChain() });\nconst signed = await signer.sign(\n { method: 'GET', protocol: 'https:', hostname: host, path: '/', headers: { host } },\n { signingRegion: '*' },\n);\nconst response = await fetch(`https://${host}/`, { headers: signed.headers });\n```\n\n### Other accounts and AWS services\n\nA caller in another account needs permission twice: in its own IAM policy, and in the function's **resource policy**. `grantInvoke` handles the function's side: a role in the function's account only gets an IAM policy statement, while a principal in another account or an AWS service also gets a statement in the resource policy.\n\n```ts\nfn.grantInvoke(iam.Role.fromRoleArn(this, 'Partner', 'arn:aws:iam::444455556666:role/caller'));\n```\n\nFor principals you can't grant to (a whole account, an organization, an AWS service with conditions), use `addPermission`. The `id` becomes the statement's `Sid`:\n\n```ts\nfn.addPermission('PartnerAccount', { principal: new iam.AccountPrincipal('444455556666') });\nfn.addPermission('MyOrg', { principal: new iam.OrganizationPrincipal('o-a1b2c3d4e5'), endpoint: fn.endpoint });\nfn.addPermission('Cdn', {\n principal: new iam.ServicePrincipal('cloudfront.amazonaws.com'),\n sourceArn: `arn:aws:cloudfront::${this.account}:distribution/EDFDVBD6EXAMPLE`,\n});\n```\n\n`addPermission` allows calling every endpoint unless you pass `endpoint`. For other actions, such as letting another account create revisions, use `fn.addToResourcePolicy(statement)`. Statements use the endpoint ARN for invoking and the function ARN for actions on the function.\n\n- **Each stack writes only its own statements.** A Web Function has a single resource policy, and Web Functions have no `AddPermission`. So each stack that adds statements gets a custom resource (`Custom::LambdaWebResourcePolicyStatements`). It reads the policy, replaces the statements that stack wrote before, and writes the policy back, retrying if another writer changed it in the meantime. Statements from other stacks, and ones added outside CloudFormation, are kept. Writers include the function's stack, a stack with a CloudFront distribution, and a stack that imports the function in the same account.\n- **Sids are unique in the policy.** `addPermission`'s `id` becomes the statement's `Sid`, and other statements get a generated one (`Cdk<hash><n>`). If another stack or tool already uses that `Sid` for different content, the deployment fails with the `Sid`.\n- **Don't also attach an `AWS::Lambda::ResourcePolicy` yourself.** That resource replaces the whole document on every update, dropping every other writer's statements.\n- **Public policies are rejected.** Lambda refuses a policy that lets anyone call the function, and the construct fails synthesis first. A `*` principal is only accepted with a condition that pins the caller, such as `aws:PrincipalOrgID` (`iam.OrganizationPrincipal`) or `aws:SourceAccount`. For a public endpoint, use `authType: NONE`.\n- **Scope AWS services to your resource.** Lambda accepts a service principal with no condition, which would let that service call the endpoint on behalf of any account. The construct warns unless you give `sourceArn` or `sourceAccount` (or an `aws:SourceArn`, `aws:SourceAccount`, `aws:SourceOrgID` or `aws:SourceOrgPaths` condition).\n- Lambda copies the policy to every region the function's endpoints serve from. The copy is eventually consistent, and in tests changes applied in every region within seconds.\n\n### CloudFront\n\nPut a CloudFront distribution (`aws-cdk-lib/aws-cloudfront`) in front of an `AWS_IAM` endpoint to serve the public with a custom domain, caching and AWS WAF. Viewers don't sign anything, and only your distribution may call the endpoint:\n\n```ts\nnew cloudfront.Distribution(this, 'Cdn', {\n defaultBehavior: {\n origin: WebFunctionOrigin.withOriginAccessControl(fn.endpoint),\n viewerProtocolPolicy: cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS,\n allowedMethods: cloudfront.AllowedMethods.ALLOW_ALL,\n cachePolicy: cloudfront.CachePolicy.CACHING_DISABLED,\n originRequestPolicy: cloudfront.OriginRequestPolicy.ALL_VIEWER_EXCEPT_HOST_HEADER,\n },\n});\n```\n\n`withOriginAccessControl` adds:\n- an origin access control (origin type `lambda`, signing behavior `always-amz-auth`, the only one CloudFront accepts for Web Function origins), one per stack and signing protocol: SigV4 for a regional domain, SigV4a for a `MULTI_REGION` endpoint's global domain;\n- a statement in the function's resource policy that allows `cloudfront.amazonaws.com` to call this endpoint on behalf of this distribution only (`aws:SourceArn`).\n\nSome things to know:\n- **Forward everything except `Host`.** The endpoint answers 404 when `Host` is the distribution's domain, so use `ALL_VIEWER_EXCEPT_HOST_HEADER` (not `ALL_VIEWER`), or a policy of your own without `Host`.\n- **Your application can still authenticate viewers.** CloudFront signs in its own headers and passes the viewer's `Authorization` header (a JWT, for example) through unchanged, but only when `Authorization` is in the cache policy. CloudFront drops it from `GET` and `HEAD` requests otherwise:\n\n ```ts\n const forwardAuthorization = new cloudfront.CachePolicy(this, 'ForwardAuthorization', {\n headerBehavior: cloudfront.CacheHeaderBehavior.allowList('Authorization'),\n minTtl: Duration.seconds(0),\n defaultTtl: Duration.seconds(0),\n maxTtl: Duration.seconds(1), // a cache key with headers needs caching enabled; 0/0/1 keeps it off in practice\n });\n ```\n- Request bodies work without clients sending `x-amz-content-sha256`: CloudFront signs `POST`, `PUT` and `PATCH` requests with an unsigned payload.\n- A `PER_REGION` endpoint's origin calls one region: the home region, or the one you pass as `region`. Use one origin per region, for example in an origin group.\n- A `MULTI_REGION` endpoint can't include opt-in regions (such as `me-central-1`), because CloudFront can't sign for them. The construct fails synthesis.\n- The distribution can be in another stack, in the function's account. Its stack writes the CloudFront statement into the function's resource policy, so the function's stack doesn't depend on it.\n- That stack can also be in another region, for example us-east-1 for an ACM certificate. CDK then passes the endpoint's domain and ARN with its cross-region references: SSM parameters plus custom resources of its own.\n- **Those references are strong by default.** A change that replaces the endpoint gives it a new domain and ARN. That includes a new endpoint type or region set, or a renamed function. The function's stack can't update a value while the distribution's stack still uses the old one, the same as with `Fn::ImportValue` in one region. Deploy such a change in two steps, or use weak references: CDK's synth warning `@aws-cdk/core:crossStackReferencesDefaultStrong` explains how.\n- Lambda@Edge origin-request and origin-response triggers aren't supported on behaviors with this origin.\n- The endpoint stays reachable at its own domain for signed requests your policies allow. Until a new distribution finishes deploying, requests through it can get a transient 403.\n- `new WebFunctionOrigin(endpoint)` is for a public (`NONE`) endpoint, for example behind AWS WAF. Lambda doesn't authenticate those requests, so the endpoint's own domain stays public too.\n\n### Functions defined elsewhere\n\nImport a Web Function defined in another stack or app to grant access to it, read its metrics, or reference its revisions:\n\n```ts\nconst orders = WebFunction.fromWebFunctionArn(this, 'Orders', 'arn:aws:lambda:us-east-1:111122223333:web-function/orders');\norders.grantInvoke(consumerRole); // every endpoint of the function\n\nconst live = WebFunction.fromWebFunctionAttributes(this, 'Live', {\n webFunctionName: 'orders', // in this stack's account and region\n endpointName: 'live', // grantInvoke allows this endpoint only\n});\nlive.metricConcurrentRequests().createAlarm(this, 'Busy', { threshold: 60, evaluationPeriods: 3 });\n```\n\nAn imported function's metrics default to its own region. Its endpoints, revisions and log groups stay with the stack that defines it.\n\nAn imported function works like a `WebFunction` here. `grantInvoke` and `addToResourcePolicy` write the importing stack's statements into the function's resource policy, in the function's region. This works only when the function is in the stack's account. For a function in another account, `grantInvoke` adds to the caller's IAM policy only, and `addToResourcePolicy` adds nothing and warns, so ask that account to allow the caller.\n\n## Configuration\n\n```ts\nconst api = new WebFunction(this, 'Api', {\n runtime: lambda.Runtime.NODEJS_24_X,\n code: lambda.Code.fromAsset('build'),\n authType: WebFunctionAuthType.AWS_IAM,\n entrypoint: 'server.js', // default index.js\n webFunctionName: 'orders-api', // default: generated from the construct path\n description: 'Orders HTTP API',\n environment: { TABLE_NAME: table.tableName },\n timeout: Duration.seconds(60), // per request: 3 seconds to 15 minutes, default 30 seconds\n maxConcurrencyPerEnvironment: 32, // requests per environment at once: 1-128, default 64\n});\n\napi.addEnvironment('FEATURE_FLAG', 'on');\ntable.grantReadWriteData(api); // the default role trusts lambda.amazonaws.com\n```\n\n- **Each execution environment has 2 vCPUs and 2 GB of memory.** There's no memory setting as on Lambda functions; scale with `maxConcurrencyPerEnvironment` and more environments instead.\n- Requests handled by one environment share module-level state, so keep handlers concurrency-safe.\n- Environment variable names starting with `AWS_` are reserved.\n- **The default execution role can only write to the function's own log groups**: the home region's, and the `.local` copy in every region it serves from. That's the minimum AWS documents for Web Functions. It doesn't use `AWSLambdaBasicExecutionRole`, which can also create any log group. Pass `role` to use your own, and include those log permissions.\n- **Renaming the function replaces it and changes its URL.** A generated name depends on the construct path, so moving the construct renames it too.\n\n### Encryption\n\nLambda encrypts each revision's code and environment variables with an AWS owned key. To use your own key, pass `encryptionKey`:\n\n```ts\ndeclare const keyStack: Stack; // deploys first\ndeclare const replicaStack: Stack; // in us-east-1, deploys before this stack\n\nconst key = new kms.Key(keyStack, 'Key', { multiRegion: true });\nconst api = new WebFunction(this, 'Api', {\n runtime: lambda.Runtime.NODEJS_24_X,\n code: lambda.Code.fromAsset('build'),\n authType: WebFunctionAuthType.AWS_IAM,\n endpoint: { endpointType: WebFunctionEndpointType.PER_REGION, regions: ['us-east-1'] },\n encryptionKey: key,\n});\n\n// One replica per region the endpoint serves from, with Lambda's statement in its own policy.\nnew kms.CfnReplicaKey(replicaStack, 'KeyReplica', {\n primaryKeyArn: key.keyArn,\n keyPolicy: new iam.PolicyDocument({\n statements: [\n new iam.PolicyStatement({ principals: [new iam.AccountRootPrincipal()], actions: ['kms:*'], resources: ['*'] }),\n api.encryptionKeyPolicyStatement(),\n ],\n }),\n});\n```\n\n- **A key defined in your app gets Lambda's statement automatically.** It allows `kms:Decrypt` for `lambda.amazonaws.com`, limited to this function's revisions through `aws:SourceArn` and the encryption context Lambda sets. For an imported key, add `encryptionKeyPolicyStatement()` to its policy yourself.\n- **`PER_REGION` and `MULTI_REGION` endpoints need a multi-Region key**, with a replica in every region they serve from. The replicas must exist before the endpoint, so put the key and its replicas in stacks that deploy before the function's. Replica policies aren't copied from the primary, so each one needs `encryptionKeyPolicyStatement()`. With a single-Region key, the endpoint fails to activate in its other regions and the whole deployment rolls back. The construct fails synthesis instead, when it can tell: a key in your app without `multiRegion`, or an imported key whose ID doesn't start with `mrk-`.\n- **CloudFormation's role creates the revisions**, so it needs `kms:DescribeKey`, `kms:GenerateDataKey` and `kms:Decrypt` on the key. A key's default policy delegates to IAM, and CDK's default deployment role is allowed.\n- **Changing the key creates a new revision.** Earlier revisions keep their key, including the ones kept as rollback targets (`revisionRetention`). Keep the old key enabled while any of them might serve traffic again.\n- **Disabling the key, or removing Lambda's access, stops new execution environments from starting.** Running ones keep their decrypted copy for a while, so this doesn't contain traffic immediately.\n\nLog groups are encrypted by CloudWatch Logs. To use your own key for them too, pass `logEncryptionKey`. It can be the same key:\n\n```ts\ndeclare const key: kms.Key; // multiRegion: true, in the function's region\ndeclare const replica: kms.CfnReplicaKey; // in us-east-1\n\nconst api = new WebFunction(this, 'Api', {\n runtime: lambda.Runtime.NODEJS_24_X,\n code: lambda.Code.fromAsset('build'),\n authType: WebFunctionAuthType.AWS_IAM,\n endpoint: { endpointType: WebFunctionEndpointType.PER_REGION, regions: ['us-east-1'] },\n logEncryptionKey: key,\n});\n\n// The replica's policy also allows CloudWatch Logs in its region, for this function's groups.\nreplica.keyPolicy = new iam.PolicyDocument({\n statements: [\n new iam.PolicyStatement({ principals: [new iam.AccountRootPrincipal()], actions: ['kms:*'], resources: ['*'] }),\n api.logEncryptionKeyPolicyStatement('us-east-1'),\n ],\n});\n```\n\n- **CloudWatch Logs only uses a key in the group's own region.** The groups in the key's region use the key. Groups in other regions use the multi-Region key's replica there, which has the same key ID. With a single-Region key, synthesis fails as soon as a group is in another region.\n- **A key defined in your app gets CloudWatch Logs' statement for its own region.** It allows `logs.<region>.amazonaws.com` to use the key for this function's log groups only. Add `logEncryptionKeyPolicyStatement(region)` to each replica's policy, and to an imported key's.\n- **The construct's provider sets the key on each group, and needs `kms:DescribeKey` on it to do so.** The construct grants that through IAM, which a key's default policy allows. If a key's policy doesn't delegate to IAM, allow the provider's role in the key policy.\n- **Adding or changing the key encrypts new events only.** Existing events stay encrypted with the key they were written with. Removing `logEncryptionKey` sets the groups back to CloudWatch Logs' encryption.\n- **Retained log groups need their key to stay readable.** The default `logRemovalPolicy` is `RETAIN`, so keep the key while you keep the logs.\n\n### Defaults for every function (property injection)\n\nTo give every function under a scope the same defaults, register a property injector for `WebFunction.PROPERTY_INJECTION_ID`, as you would for aws-cdk-lib's constructs. For example, a team's log retention:\n\n```ts\nclass TeamDefaults implements IPropertyInjector {\n public readonly constructUniqueId = WebFunction.PROPERTY_INJECTION_ID;\n public inject(originalProps: WebFunctionProps): WebFunctionProps {\n // The team's defaults, with explicit props winning.\n return Object.assign({ logRetention: logs.RetentionDays.ONE_MONTH }, originalProps);\n }\n}\nPropertyInjectors.of(this).add(new TeamDefaults()); // or on the app, for every stack\n```\n\n- An injector applies to functions created under the scope it's added to. The nearest one wins.\n- `NodejsWebFunction` has its own ID (`NodejsWebFunction.PROPERTY_INJECTION_ID`). Its injector runs first, then `WebFunction`'s, as with aws-cdk-lib's `NodejsFunction` and `Function`.\n\n### Bundling TypeScript with `NodejsWebFunction`\n\n`NodejsWebFunction` is a `WebFunction` that bundles your server from a TypeScript or JavaScript entry file with esbuild, like `NodejsFunction` does for Lambda functions:\n\n```ts\nnew NodejsWebFunction(this, 'Api', {\n entry: 'src/server.ts',\n authType: WebFunctionAuthType.AWS_IAM,\n bundling: {\n minify: true, // default true\n sourceMap: true, // default true\n format: lambdaNodejs.OutputFormat.ESM, // default ESM (index.mjs); CJS gives index.js\n externalModules: [], // default: bundle everything you import\n },\n});\n```\n\n- **Defaults:** `runtime` is `NODEJS_24_X`, and the esbuild target follows the runtime (`node24`). The project root is the directory of the nearest lock file above `entry` (override it with `depsLockFilePath` or `projectRoot`).\n- **It also sets what a `lambda.Code` can't:** the entry point that matches the bundle, and `NODE_OPTIONS=--enable-source-maps` when source maps are on, appended to any `NODE_OPTIONS` you set, so stack traces point at your `.ts` files (e.g. `/var/task/src/server.ts:22:12`). Source map paths are relative to the project root, so no build-machine paths are deployed.\n- **ESM bundles include a `require` shim**, so CommonJS dependencies that load Node.js built-ins (Express, for example) work.\n- **The AWS SDK is bundled like any other dependency**, since the runtime has none. Only the clients you import are included.\n- **Bundling runs locally** when esbuild is installed in your project, and otherwise in Docker (an arm64 AWS SAM build image; set `forceDockerBundling` to always use Docker).\n- **Not yet supported:** installing native modules next to the bundle (`nodeModules`) and command hooks. For native dependencies, use `WebFunction` with `Code.fromAsset(..., { bundling: { image, command } })`.\n\n### Code\n\n`code` takes any `lambda.Code` that produces a zip in S3: `fromAsset`, `fromBucket`/`fromBucketV2`, `fromCustomCommand`, `fromDockerBuild`. Inline code and container images fail at synth time.\n\nLambda Web Functions download code as the `lambda.amazonaws.com` service principal, from a **versioned** bucket, and the CDK bootstrap bucket allows neither. So each stack gets a small code bucket, and the code is copied into it on deploy. The bucket is emptied and deleted with the stack.\n\nCopying code, managing the function's log groups in every region, looking up regional URLs, cleaning up old revisions, creating rollback alarms in other regions and emptying the code bucket all run in one custom resource Lambda per stack. Its logs go to a log group the stack owns, kept for a month and deleted with the stack.\n\n## Revisions\n\nAny change to code or configuration creates a new **revision** (`fn.currentRevision`), and endpoints switch to it once it's ready. A failed deployment rolls back to the previous revision. If nothing changed, nothing is deployed.\n\nEarlier revisions are kept, so you can roll back or route traffic to them. After each deployment, the construct deletes the oldest beyond `revisionRetention` (default 20, up to 50). It never deletes a revision an endpoint routes to, or one the previous deployment used, which a rollback returns to.\n\n```ts\nnew WebFunction(this, 'Api', {\n runtime: lambda.Runtime.NODEJS_24_X,\n code: lambda.Code.fromAsset('dist'),\n authType: WebFunctionAuthType.AWS_IAM,\n revisionRetention: 5,\n});\n```\n\n### Routing to earlier revisions\n\nReference an earlier revision by its ID (for example from a `CfnOutput` of `fn.currentRevision.revisionId`, or `aws lambda-web list-web-function-revisions`). Then pin an endpoint to it, or split traffic between two revisions:\n\n```ts\nconst previous = WebFunctionRevision.fromRevisionAttributes(this, 'Previous', {\n webFunction: fn,\n revisionId: 'rev-a1b2c',\n});\n\n// Keep the default endpoint on the previous revision, and send 10% to the new one:\nfn.endpoint.routeTraffic(previous, [{ revision: fn.currentRevision, weight: 0.1 }]);\n\n// Or a second endpoint that stays on the previous revision while fn.endpoint moves on:\nfn.addEndpoint('Stable', { authType: WebFunctionAuthType.AWS_IAM, revision: previous });\n```\n\n- An endpoint routes to at most two revisions. The additional revision's `weight` is a whole percentage, from 0.01 to 0.99.\n- To promote, remove `routeTraffic` (or `revision`) and deploy: the endpoint serves the current revision again.\n- Lambda Web Function metrics have no revision dimension. To compare a split's revisions, log a version you control (for example an environment variable) from your application.\n\n### Canary and linear deployments\n\nBy default an endpoint moves all its traffic to a new revision at once. With `deployment`, it moves in steps instead, watching CloudWatch alarms: if one goes into ALARM, the endpoint goes back to the revision it served before and the deployment fails, so CloudFormation rolls the stack back.\n\n```ts\nconst api = new WebFunction(this, 'Api', {\n runtime: lambda.Runtime.NODEJS_24_X,\n code: lambda.Code.fromAsset('dist'),\n authType: WebFunctionAuthType.AWS_IAM,\n endpoint: {\n // 10% of the traffic for 10 minutes, then all of it; back if more than 2% of requests get a 5XX.\n deployment: WebFunctionDeployment.canary({ percent: 10, bake: Duration.minutes(10), rollbackOn: { serverErrorRate: 2 } }),\n },\n});\n\n// 25% more every 5 minutes, watching an alarm of your own. Each endpoint has its own deployment.\n// errorRate: any cloudwatch.IAlarm, e.g. on a metric your application publishes.\napi.addEndpoint('Public', {\n authType: WebFunctionAuthType.NONE,\n deployment: WebFunctionDeployment.linear({ percent: 25, interval: Duration.minutes(5), alarms: [errorRate] }),\n});\n```\n\n| | Steps | Limits |\n|---|---|---|\n| `allAtOnce()` (default) | 100% | |\n| `canary({ percent, bake })` | `percent`, then 100% after `bake` | `percent` 1–99, `bake` 1–45 minutes |\n| `linear({ percent, interval })` | `percent` more every `interval` | `percent` 5–50, `interval` from 1 minute, at most 45 minutes in all |\n\nHow it works:\n\n- Each deployment that creates a revision (code or configuration) moves the traffic from the revision the endpoint serves at that moment, which is also what it goes back to. If someone switched the endpoint outside CloudFormation, the deployment starts from there.\n- Each step waits until every region of a `MULTI_REGION` or `PER_REGION` endpoint has applied it (usually about 15 seconds), and if a region fails to update, the endpoint goes back as for an alarm. The bake or interval counts from the start of the step.\n- Alarms are checked before the first step (an alarm already in ALARM stops the deployment before any traffic moves), throughout every step, and once the new revision has all the traffic. They can be in any region, and composite alarms work. `INSUFFICIENT_DATA` doesn't stop a deployment.\n- A rollback of the stack, for any reason, moves the traffic back at once, without waiting or checking alarms.\n- A stack's first deployment, and a new endpoint (including one renamed by a region change), serve the revision at once: there's nothing to move from.\n- A deployment can take up to an hour: CloudFormation waits for it before it updates the endpoint. Deployments of different endpoints run in parallel.\n- A canary or linear endpoint always moves to the current revision, so it can't also use `revision`, `additionalRevisions` or `routeTraffic()`. Use `allAtOnce()` to deploy a fix immediately, for example while an alarm is in ALARM.\n\n#### Rollback alarms\n\n`rollbackOn` makes the endpoint create alarms on its own error rates, and the deployment watches them alongside any `alarms` you pass:\n\n| `rollbackOn` | Default | |\n|---|---|---|\n| `serverErrorRate` | none | roll back when more than this % of requests get a 5XX |\n| `clientErrorRate` | none | the same for 4XX (including 429s from `rateLimit` and `maxEnvironments`) |\n| `minimumRequests` | 10 | periods with fewer requests don't count, so one failed request out of two doesn't roll back |\n| `period`, `evaluationPeriods` | 1 minute, 2 | consecutive periods over the threshold before rolling back |\n\n- **One alarm per threshold in every region the endpoint serves from.** Metrics are published in the region that served the request, and an alarm can only watch metrics in its own region. The home region's alarm is a normal CloudFormation alarm; the others are created and deleted with the endpoint by a custom resource, and named `LambdaWeb-<function>-<endpoint>-5xx-rate` (or `-4xx-rate`). `endpoint.rollbackAlarms` lists them all, e.g. to add notifications to the home region's.\n- **The alarms see the whole endpoint.** Lambda Web Function metrics have no revision dimension, so at 10% a new revision's errors are about a tenth of the endpoint's: set the threshold low enough to notice that, or alarm on metrics your application publishes with its version.\n- **5XX includes the platform's 503s** while the endpoint scales up for a sudden burst, not only your application's errors.\n- **A step should outlast the alarm.** CloudWatch needs `period` × `evaluationPeriods`, plus a couple of minutes of delay, to fire; the construct warns when a canary's bake or a linear interval is shorter.\n\nThe deployment runs in a few resources per stack, created on first use: two Lambda functions, a Step Functions state machine that calls them every 15 seconds, and a log group.\n\n## Metrics\n\nEndpoints and functions have helpers for their CloudWatch metrics (namespace `AWS/Lambda/Web`), like `lambda.Function`'s:\n\n```ts\nfn.endpoint.metricServerErrorRate().createAlarm(this, 'ErrorRate', { threshold: 1, evaluationPeriods: 3 });\nfn.endpoint.metricRequestThrottles().createAlarm(this, 'Throttled', { threshold: 10, evaluationPeriods: 3 });\nfn.metricConcurrentRequests().createAlarm(this, 'Saturated', { threshold: 60, evaluationPeriods: 3 });\n```\n\n| Endpoint | Metric |\n|---|---|\n| `metricRequests()` | `ExecutedRequests` (there's no separate 2XX count) |\n| `metricClientErrors()`, `metricServerErrors()` | `HTTPResponseStatusClass4XX`, `HTTPResponseStatusClass5XX` |\n| `metricClientErrorRate()`, `metricServerErrorRate()` | 4XX or 5XX as a % of requests (math expression) |\n| `metricActiveEnvironments()` | `EndpointActiveEnvironments`, to compare with `maxEnvironments` |\n| `metricRequestThrottles()`, `metricEnvironmentThrottles()` | 429s from `rateLimit` and from `maxEnvironments` |\n\n| Function | Metric |\n|---|---|\n| `metricConcurrentRequests()` | `EnvironmentConcurrentRequests` (maximum), to compare with `maxConcurrencyPerEnvironment` |\n| `metricMaxMemoryUsage()` | `EnvironmentMaxMemoryUsage` (maximum) |\n| `metricActiveTime()`, `metricCpuTime()`, `metricResponseBytes()` | `EnvironmentActiveTime`, `EnvironmentCPUTime`, `ResponseBytes` |\n\n- **Metrics stay in the region that served the request**; unlike logs, they aren't copied to the home region. Every helper takes `region`. Dashboards can show any region, but an alarm can only use metrics from its own stack's region.\n- 4XX, 5XX and the throttle metrics only exist once they're non-zero: alarm on them with `treatMissingData: NOT_BREACHING`. The rate helpers count missing errors as 0.\n- There's no latency metric. Publish one from your application (e.g. with the CloudWatch embedded metric format).\n\n## Logs in every region\n\nWeb Functions log in two tiers:\n\n```text\n every serving region home region\n ┌──────────────────┐ ┌──────────────────────────────────────────┐\n │ <group>.local │── copied ───►│ <group> every region's logs │\n │ (that region's │ (seconds) │ streams …[rev]<instance>-<acct>-<region> │\n │ environments) │ │ <group>.local (home-region traffic) │\n └──────────────────┘ └──────────────────────────────────────────┘\n```\n\n**Query `fn.logGroup`**, the home-region group that receives every region's logs. The service would create missing groups without retention and never delete them, so the construct creates and owns all of them: both home-region groups, and `<group>.local` in every other region any of its endpoints serves from.\n\n```ts\nnew WebFunction(this, 'Api', {\n runtime: lambda.Runtime.NODEJS_24_X,\n code: lambda.Code.fromAsset('dist'),\n authType: WebFunctionAuthType.AWS_IAM,\n logRetention: logs.RetentionDays.THREE_MONTHS, // default ONE_MONTH, every group in every region\n logRemovalPolicy: RemovalPolicy.DESTROY, // default RETAIN\n applicationLogLevelV2: lambda.ApplicationLogLevel.WARN,\n systemLogLevelV2: lambda.SystemLogLevel.INFO, // DEBUG, INFO or WARN\n});\n```\n\n- The default group is `/aws/lambda/web/<function name>`. Log lines are JSON; application lines carry `requestId`, `endpointName` and `region`.\n- Every event is stored twice (`.local` plus the home-region copy), so expect about double log ingestion, plus cross-region transfer.\n- With `DESTROY`, deletion waits about a minute after the function is gone, because environments flush their last lines late.\n- Subscribe only to `fn.logGroup`: subscribing to the `.local` groups as well delivers every event twice.\n\n## cdk-nag\n\nThe library acknowledges the [cdk-nag](https://github.com/cdklabs/cdk-nag) AwsSolutions findings on the resources it creates, each with a reason, so they don't show up in your reports:\n\n| Finding | Resource | Reason |\n|---|---|---|\n| IAM5 (wildcards) | the default execution role | log streams of the function's own log groups, in every region it serves from |\n| IAM5 | the deployment handlers' role (canary and linear deployments) | their own log group, `cloudwatch:DescribeAlarms` (composite alarms need `*`), and the revisions and endpoints of the functions they deploy |\n| IAM4, IAM5, SF1, SF2 | the CDK Provider framework behind deployments | CDK's own functions and waiter state machine |\n| S1 (access logs) | the stack's code bucket | it only holds copies of the stack's code assets |\n\n- **This works with both cdk-nag versions**, without depending on cdk-nag. cdk-nag 3 reads CDK's `Validations.of(...).acknowledge()`, one ID per finding. cdk-nag 2 reads `cdk_nag` metadata in the template.\n- **Your own statements and resources are still checked.** That includes statements you add to the function's role (`addToRolePolicy`, grants) and your CloudFront distribution's settings.\n- Only the AwsSolutions pack is covered; findings from the HIPAA, NIST and PCI packs aren't acknowledged.\n\n## Upgrading\n\n### From 0.1.5 to 0.1.7, with a resource policy\n\nVersions 0.1.5 to 0.1.7 wrote the function's resource policy with an `AWS::Lambda::ResourcePolicy`. Later versions don't (0.1.7 has the same code as 0.1.6). When an update removes that resource, CloudFormation deletes the function's **whole** resource policy at the end of the update, including the statements the new version just wrote. Callers that rely on the policy then get 403: other accounts, AWS services, and CloudFront.\n\nIf your function has a resource policy (from `addPermission`, a grant to another account or an AWS service, or `WebFunctionOrigin`), first deploy once on the version you have, with the old resource retained:\n\n```ts\n(fn.node.findChild('ResourcePolicy') as CfnResource).applyRemovalPolicy(RemovalPolicy.RETAIN);\n```\n\nThen upgrade, without changing your statements in the same deployment. The new version takes over the statements it writes itself. A statement you removed in the same deployment would stay in the policy.\n\n## Limitations\n\n- Not yet supported: VPC access (network connectors), KMS encryption, and tracing.\n- **Hotswap skips Web Functions without a warning.** `cdk deploy --hotswap` and `cdk watch`, which hotswaps by default, report `(no changes)` for a code or configuration change and the endpoint keeps serving the old revision. Use `cdk watch --hotswap-fallback` (or `cdk deploy --hotswap-fallback`): it deploys Web Function changes through CloudFormation, in about 40 seconds for a home-region endpoint.\n- Fixed limits: 512 MB of code (unzipped) and 32 KB of environment variables per revision, 512 MB of `/tmp` per execution environment, and 128 KiB of headers per request.\n- Quotas: 10 endpoints and about 50 revisions per function (Lambda cleans up older revisions automatically), and 10,000 requests per second per endpoint. Per account and region: 100,000 requests per second and 2,000 Arm64 vCPUs (each execution environment uses 2). The account quotas are adjustable, and new accounts start lower. See [Lambda quotas](https://docs.aws.amazon.com/lambda/latest/dg/gettingstarted-limits.html).\n\n## Development\n\nTooling is pinned in `mise.toml` (Node 24, AWS CDK CLI). Personal settings, such as the AWS profile for the live end-to-end tests, go in a git-ignored `mise.local.toml`.\n\n```bash\nmise install\nnpx projen build # compile, unit tests, lint, API docs\nnpx projen e2e # deploy, verify and destroy real stacks in three regions (needs AWS credentials)\n```\n"
9749
9749
  },
9750
9750
  "repository": {
9751
9751
  "type": "git",
@@ -9853,7 +9853,7 @@
9853
9853
  "kind": "interface",
9854
9854
  "locationInModule": {
9855
9855
  "filename": "src/web-function.ts",
9856
- "line": 162
9856
+ "line": 192
9857
9857
  },
9858
9858
  "methods": [
9859
9859
  {
@@ -9865,7 +9865,7 @@
9865
9865
  },
9866
9866
  "locationInModule": {
9867
9867
  "filename": "src/web-function.ts",
9868
- "line": 185
9868
+ "line": 215
9869
9869
  },
9870
9870
  "name": "grantInvoke",
9871
9871
  "parameters": [
@@ -9890,7 +9890,7 @@
9890
9890
  },
9891
9891
  "locationInModule": {
9892
9892
  "filename": "src/web-function.ts",
9893
- "line": 190
9893
+ "line": 220
9894
9894
  },
9895
9895
  "name": "metric",
9896
9896
  "parameters": [
@@ -9922,7 +9922,7 @@
9922
9922
  },
9923
9923
  "locationInModule": {
9924
9924
  "filename": "src/web-function.ts",
9925
- "line": 193
9925
+ "line": 223
9926
9926
  },
9927
9927
  "name": "metricActiveTime",
9928
9928
  "parameters": [
@@ -9948,7 +9948,7 @@
9948
9948
  },
9949
9949
  "locationInModule": {
9950
9950
  "filename": "src/web-function.ts",
9951
- "line": 199
9951
+ "line": 229
9952
9952
  },
9953
9953
  "name": "metricConcurrentRequests",
9954
9954
  "parameters": [
@@ -9974,7 +9974,7 @@
9974
9974
  },
9975
9975
  "locationInModule": {
9976
9976
  "filename": "src/web-function.ts",
9977
- "line": 196
9977
+ "line": 226
9978
9978
  },
9979
9979
  "name": "metricCpuTime",
9980
9980
  "parameters": [
@@ -10000,7 +10000,7 @@
10000
10000
  },
10001
10001
  "locationInModule": {
10002
10002
  "filename": "src/web-function.ts",
10003
- "line": 202
10003
+ "line": 232
10004
10004
  },
10005
10005
  "name": "metricMaxMemoryUsage",
10006
10006
  "parameters": [
@@ -10026,7 +10026,7 @@
10026
10026
  },
10027
10027
  "locationInModule": {
10028
10028
  "filename": "src/web-function.ts",
10029
- "line": 205
10029
+ "line": 235
10030
10030
  },
10031
10031
  "name": "metricResponseBytes",
10032
10032
  "parameters": [
@@ -10059,7 +10059,7 @@
10059
10059
  "immutable": true,
10060
10060
  "locationInModule": {
10061
10061
  "filename": "src/web-function.ts",
10062
- "line": 175
10062
+ "line": 205
10063
10063
  },
10064
10064
  "name": "webFunctionArn",
10065
10065
  "type": {
@@ -10078,7 +10078,7 @@
10078
10078
  "immutable": true,
10079
10079
  "locationInModule": {
10080
10080
  "filename": "src/web-function.ts",
10081
- "line": 168
10081
+ "line": 198
10082
10082
  },
10083
10083
  "name": "webFunctionName",
10084
10084
  "type": {
@@ -10675,7 +10675,7 @@
10675
10675
  },
10676
10676
  "locationInModule": {
10677
10677
  "filename": "src/web-function.ts",
10678
- "line": 452
10678
+ "line": 488
10679
10679
  },
10680
10680
  "parameters": [
10681
10681
  {
@@ -10704,7 +10704,7 @@
10704
10704
  "kind": "class",
10705
10705
  "locationInModule": {
10706
10706
  "filename": "src/web-function.ts",
10707
- "line": 388
10707
+ "line": 418
10708
10708
  },
10709
10709
  "methods": [
10710
10710
  {
@@ -10714,7 +10714,7 @@
10714
10714
  },
10715
10715
  "locationInModule": {
10716
10716
  "filename": "src/web-function.ts",
10717
- "line": 398
10717
+ "line": 428
10718
10718
  },
10719
10719
  "name": "fromWebFunctionArn",
10720
10720
  "parameters": [
@@ -10751,7 +10751,7 @@
10751
10751
  },
10752
10752
  "locationInModule": {
10753
10753
  "filename": "src/web-function.ts",
10754
- "line": 412
10754
+ "line": 442
10755
10755
  },
10756
10756
  "name": "fromWebFunctionAttributes",
10757
10757
  "parameters": [
@@ -10788,7 +10788,7 @@
10788
10788
  },
10789
10789
  "locationInModule": {
10790
10790
  "filename": "src/web-function.ts",
10791
- "line": 405
10791
+ "line": 435
10792
10792
  },
10793
10793
  "name": "fromWebFunctionName",
10794
10794
  "parameters": [
@@ -10825,7 +10825,7 @@
10825
10825
  },
10826
10826
  "locationInModule": {
10827
10827
  "filename": "src/web-function.ts",
10828
- "line": 552
10828
+ "line": 602
10829
10829
  },
10830
10830
  "name": "addEndpoint",
10831
10831
  "parameters": [
@@ -10859,7 +10859,7 @@
10859
10859
  },
10860
10860
  "locationInModule": {
10861
10861
  "filename": "src/web-function.ts",
10862
- "line": 560
10862
+ "line": 610
10863
10863
  },
10864
10864
  "name": "addEnvironment",
10865
10865
  "parameters": [
@@ -10890,7 +10890,7 @@
10890
10890
  },
10891
10891
  "locationInModule": {
10892
10892
  "filename": "src/web-function.ts",
10893
- "line": 589
10893
+ "line": 639
10894
10894
  },
10895
10895
  "name": "addPermission",
10896
10896
  "parameters": [
@@ -10918,7 +10918,7 @@
10918
10918
  },
10919
10919
  "locationInModule": {
10920
10920
  "filename": "src/web-function.ts",
10921
- "line": 571
10921
+ "line": 621
10922
10922
  },
10923
10923
  "name": "addToRolePolicy",
10924
10924
  "parameters": [
@@ -10930,6 +10930,23 @@
10930
10930
  }
10931
10931
  ]
10932
10932
  },
10933
+ {
10934
+ "docs": {
10935
+ "remarks": "It's added to `encryptionKey` when the key is defined in this app. Add it to every replica of a multi-Region key\nyourself: replicas don't share their primary's policy.",
10936
+ "stability": "experimental",
10937
+ "summary": "The key-policy statement Lambda needs to start this function's environments with `encryptionKey`: `kms:Decrypt` for `lambda.amazonaws.com`, limited to this function's revisions (`aws:SourceArn` and the encryption context Lambda sets)."
10938
+ },
10939
+ "locationInModule": {
10940
+ "filename": "src/web-function.ts",
10941
+ "line": 669
10942
+ },
10943
+ "name": "encryptionKeyPolicyStatement",
10944
+ "returns": {
10945
+ "type": {
10946
+ "fqn": "aws-cdk-lib.aws_iam.PolicyStatement"
10947
+ }
10948
+ }
10949
+ },
10933
10950
  {
10934
10951
  "docs": {
10935
10952
  "remarks": "Shortcut for `grants.invoke(grantee)`, as on `lambda.Function`.\n[disable-awslint:no-grants]",
@@ -10938,7 +10955,7 @@
10938
10955
  },
10939
10956
  "locationInModule": {
10940
10957
  "filename": "src/web-function.ts",
10941
- "line": 579
10958
+ "line": 629
10942
10959
  },
10943
10960
  "name": "grantInvoke",
10944
10961
  "overrides": "@ashishpandeyone/cdk-lambda-web-function.WebFunctionBase",
@@ -10955,6 +10972,34 @@
10955
10972
  "fqn": "aws-cdk-lib.aws_iam.Grant"
10956
10973
  }
10957
10974
  }
10975
+ },
10976
+ {
10977
+ "docs": {
10978
+ "remarks": "It's added to `logEncryptionKey` for the key's own region when the key is defined in this app. Add it to the\nreplica in every other region the function serves from yourself: replicas don't share their primary's policy.",
10979
+ "stability": "experimental",
10980
+ "summary": "The key-policy statement CloudWatch Logs needs in `region` to encrypt this function's log groups there with `logEncryptionKey`, limited to those groups (`kms:EncryptionContext:aws:logs:arn`)."
10981
+ },
10982
+ "locationInModule": {
10983
+ "filename": "src/web-function.ts",
10984
+ "line": 692
10985
+ },
10986
+ "name": "logEncryptionKeyPolicyStatement",
10987
+ "parameters": [
10988
+ {
10989
+ "docs": {
10990
+ "summary": "the region of the key or replica."
10991
+ },
10992
+ "name": "region",
10993
+ "type": {
10994
+ "primitive": "string"
10995
+ }
10996
+ }
10997
+ ],
10998
+ "returns": {
10999
+ "type": {
11000
+ "fqn": "aws-cdk-lib.aws_iam.PolicyStatement"
11001
+ }
11002
+ }
10958
11003
  }
10959
11004
  ],
10960
11005
  "name": "WebFunction",
@@ -10968,7 +11013,7 @@
10968
11013
  "immutable": true,
10969
11014
  "locationInModule": {
10970
11015
  "filename": "src/web-function.ts",
10971
- "line": 393
11016
+ "line": 423
10972
11017
  },
10973
11018
  "name": "PROPERTY_INJECTION_ID",
10974
11019
  "static": true,
@@ -10984,7 +11029,7 @@
10984
11029
  "immutable": true,
10985
11030
  "locationInModule": {
10986
11031
  "filename": "src/web-function.ts",
10987
- "line": 423
11032
+ "line": 453
10988
11033
  },
10989
11034
  "name": "currentRevision",
10990
11035
  "type": {
@@ -11000,7 +11045,7 @@
11000
11045
  "immutable": true,
11001
11046
  "locationInModule": {
11002
11047
  "filename": "src/web-function.ts",
11003
- "line": 426
11048
+ "line": 456
11004
11049
  },
11005
11050
  "name": "endpoint",
11006
11051
  "type": {
@@ -11015,7 +11060,7 @@
11015
11060
  "immutable": true,
11016
11061
  "locationInModule": {
11017
11062
  "filename": "src/web-function.ts",
11018
- "line": 435
11063
+ "line": 471
11019
11064
  },
11020
11065
  "name": "grantPrincipal",
11021
11066
  "overrides": "aws-cdk-lib.aws_iam.IGrantable",
@@ -11031,7 +11076,7 @@
11031
11076
  "immutable": true,
11032
11077
  "locationInModule": {
11033
11078
  "filename": "src/web-function.ts",
11034
- "line": 429
11079
+ "line": 459
11035
11080
  },
11036
11081
  "name": "grants",
11037
11082
  "type": {
@@ -11047,7 +11092,7 @@
11047
11092
  "immutable": true,
11048
11093
  "locationInModule": {
11049
11094
  "filename": "src/web-function.ts",
11050
- "line": 441
11095
+ "line": 477
11051
11096
  },
11052
11097
  "name": "logGroup",
11053
11098
  "type": {
@@ -11063,7 +11108,7 @@
11063
11108
  "immutable": true,
11064
11109
  "locationInModule": {
11065
11110
  "filename": "src/web-function.ts",
11066
- "line": 420
11111
+ "line": 450
11067
11112
  },
11068
11113
  "name": "revisionId",
11069
11114
  "type": {
@@ -11078,7 +11123,7 @@
11078
11123
  "immutable": true,
11079
11124
  "locationInModule": {
11080
11125
  "filename": "src/web-function.ts",
11081
- "line": 432
11126
+ "line": 462
11082
11127
  },
11083
11128
  "name": "role",
11084
11129
  "type": {
@@ -11093,7 +11138,7 @@
11093
11138
  "immutable": true,
11094
11139
  "locationInModule": {
11095
11140
  "filename": "src/web-function.ts",
11096
- "line": 417
11141
+ "line": 447
11097
11142
  },
11098
11143
  "name": "webFunctionArn",
11099
11144
  "overrides": "@ashishpandeyone/cdk-lambda-web-function.WebFunctionBase",
@@ -11109,13 +11154,45 @@
11109
11154
  "immutable": true,
11110
11155
  "locationInModule": {
11111
11156
  "filename": "src/web-function.ts",
11112
- "line": 416
11157
+ "line": 446
11113
11158
  },
11114
11159
  "name": "webFunctionName",
11115
11160
  "overrides": "@ashishpandeyone/cdk-lambda-web-function.WebFunctionBase",
11116
11161
  "type": {
11117
11162
  "primitive": "string"
11118
11163
  }
11164
+ },
11165
+ {
11166
+ "docs": {
11167
+ "stability": "experimental",
11168
+ "summary": "The customer managed key of the function's revisions, if any."
11169
+ },
11170
+ "immutable": true,
11171
+ "locationInModule": {
11172
+ "filename": "src/web-function.ts",
11173
+ "line": 465
11174
+ },
11175
+ "name": "encryptionKey",
11176
+ "optional": true,
11177
+ "type": {
11178
+ "fqn": "aws-cdk-lib.aws_kms.IKey"
11179
+ }
11180
+ },
11181
+ {
11182
+ "docs": {
11183
+ "stability": "experimental",
11184
+ "summary": "The customer managed key of the function's log groups, if any."
11185
+ },
11186
+ "immutable": true,
11187
+ "locationInModule": {
11188
+ "filename": "src/web-function.ts",
11189
+ "line": 468
11190
+ },
11191
+ "name": "logEncryptionKey",
11192
+ "optional": true,
11193
+ "type": {
11194
+ "fqn": "aws-cdk-lib.aws_kms.IKey"
11195
+ }
11119
11196
  }
11120
11197
  ],
11121
11198
  "symbolId": "src/web-function:WebFunction"
@@ -11132,7 +11209,7 @@
11132
11209
  "kind": "interface",
11133
11210
  "locationInModule": {
11134
11211
  "filename": "src/web-function.ts",
11135
- "line": 211
11212
+ "line": 241
11136
11213
  },
11137
11214
  "name": "WebFunctionAttributes",
11138
11215
  "properties": [
@@ -11146,7 +11223,7 @@
11146
11223
  "immutable": true,
11147
11224
  "locationInModule": {
11148
11225
  "filename": "src/web-function.ts",
11149
- "line": 233
11226
+ "line": 263
11150
11227
  },
11151
11228
  "name": "endpointName",
11152
11229
  "optional": true,
@@ -11164,7 +11241,7 @@
11164
11241
  "immutable": true,
11165
11242
  "locationInModule": {
11166
11243
  "filename": "src/web-function.ts",
11167
- "line": 219
11244
+ "line": 249
11168
11245
  },
11169
11246
  "name": "webFunctionArn",
11170
11247
  "optional": true,
@@ -11182,7 +11259,7 @@
11182
11259
  "immutable": true,
11183
11260
  "locationInModule": {
11184
11261
  "filename": "src/web-function.ts",
11185
- "line": 226
11262
+ "line": 256
11186
11263
  },
11187
11264
  "name": "webFunctionName",
11188
11265
  "optional": true,
@@ -11270,7 +11347,7 @@
11270
11347
  "kind": "class",
11271
11348
  "locationInModule": {
11272
11349
  "filename": "src/web-function.ts",
11273
- "line": 274
11350
+ "line": 304
11274
11351
  },
11275
11352
  "methods": [
11276
11353
  {
@@ -11281,7 +11358,7 @@
11281
11358
  },
11282
11359
  "locationInModule": {
11283
11360
  "filename": "src/web-function.ts",
11284
- "line": 297
11361
+ "line": 327
11285
11362
  },
11286
11363
  "name": "addToResourcePolicy",
11287
11364
  "overrides": "aws-cdk-lib.aws_iam.IResourceWithPolicyV2",
@@ -11308,7 +11385,7 @@
11308
11385
  },
11309
11386
  "locationInModule": {
11310
11387
  "filename": "src/web-function.ts",
11311
- "line": 287
11388
+ "line": 317
11312
11389
  },
11313
11390
  "name": "grantInvoke",
11314
11391
  "overrides": "@ashishpandeyone/cdk-lambda-web-function.IWebFunction",
@@ -11335,7 +11412,7 @@
11335
11412
  },
11336
11413
  "locationInModule": {
11337
11414
  "filename": "src/web-function.ts",
11338
- "line": 328
11415
+ "line": 358
11339
11416
  },
11340
11417
  "name": "metric",
11341
11418
  "overrides": "@ashishpandeyone/cdk-lambda-web-function.IWebFunction",
@@ -11367,7 +11444,7 @@
11367
11444
  },
11368
11445
  "locationInModule": {
11369
11446
  "filename": "src/web-function.ts",
11370
- "line": 343
11447
+ "line": 373
11371
11448
  },
11372
11449
  "name": "metricActiveTime",
11373
11450
  "overrides": "@ashishpandeyone/cdk-lambda-web-function.IWebFunction",
@@ -11394,7 +11471,7 @@
11394
11471
  },
11395
11472
  "locationInModule": {
11396
11473
  "filename": "src/web-function.ts",
11397
- "line": 358
11474
+ "line": 388
11398
11475
  },
11399
11476
  "name": "metricConcurrentRequests",
11400
11477
  "overrides": "@ashishpandeyone/cdk-lambda-web-function.IWebFunction",
@@ -11420,7 +11497,7 @@
11420
11497
  },
11421
11498
  "locationInModule": {
11422
11499
  "filename": "src/web-function.ts",
11423
- "line": 348
11500
+ "line": 378
11424
11501
  },
11425
11502
  "name": "metricCpuTime",
11426
11503
  "overrides": "@ashishpandeyone/cdk-lambda-web-function.IWebFunction",
@@ -11448,7 +11525,7 @@
11448
11525
  },
11449
11526
  "locationInModule": {
11450
11527
  "filename": "src/web-function.ts",
11451
- "line": 367
11528
+ "line": 397
11452
11529
  },
11453
11530
  "name": "metricMaxMemoryUsage",
11454
11531
  "overrides": "@ashishpandeyone/cdk-lambda-web-function.IWebFunction",
@@ -11474,7 +11551,7 @@
11474
11551
  },
11475
11552
  "locationInModule": {
11476
11553
  "filename": "src/web-function.ts",
11477
- "line": 372
11554
+ "line": 402
11478
11555
  },
11479
11556
  "name": "metricResponseBytes",
11480
11557
  "overrides": "@ashishpandeyone/cdk-lambda-web-function.IWebFunction",
@@ -11505,7 +11582,7 @@
11505
11582
  "immutable": true,
11506
11583
  "locationInModule": {
11507
11584
  "filename": "src/web-function.ts",
11508
- "line": 276
11585
+ "line": 306
11509
11586
  },
11510
11587
  "name": "webFunctionArn",
11511
11588
  "overrides": "@ashishpandeyone/cdk-lambda-web-function.IWebFunction",
@@ -11522,7 +11599,7 @@
11522
11599
  "immutable": true,
11523
11600
  "locationInModule": {
11524
11601
  "filename": "src/web-function.ts",
11525
- "line": 275
11602
+ "line": 305
11526
11603
  },
11527
11604
  "name": "webFunctionName",
11528
11605
  "overrides": "@ashishpandeyone/cdk-lambda-web-function.IWebFunction",
@@ -11538,7 +11615,7 @@
11538
11615
  "immutable": true,
11539
11616
  "locationInModule": {
11540
11617
  "filename": "src/web-function.ts",
11541
- "line": 279
11618
+ "line": 309
11542
11619
  },
11543
11620
  "name": "webFunctionRef",
11544
11621
  "overrides": "aws-cdk-lib.interfaces.aws_lambda.IWebFunctionRef",
@@ -12600,7 +12677,7 @@
12600
12677
  "kind": "interface",
12601
12678
  "locationInModule": {
12602
12679
  "filename": "src/web-function.ts",
12603
- "line": 26
12680
+ "line": 27
12604
12681
  },
12605
12682
  "name": "WebFunctionOptions",
12606
12683
  "properties": [
@@ -12613,7 +12690,7 @@
12613
12690
  "immutable": true,
12614
12691
  "locationInModule": {
12615
12692
  "filename": "src/web-function.ts",
12616
- "line": 30
12693
+ "line": 31
12617
12694
  },
12618
12695
  "name": "authType",
12619
12696
  "type": {
@@ -12630,7 +12707,7 @@
12630
12707
  "immutable": true,
12631
12708
  "locationInModule": {
12632
12709
  "filename": "src/web-function.ts",
12633
- "line": 119
12710
+ "line": 149
12634
12711
  },
12635
12712
  "name": "applicationLogLevelV2",
12636
12713
  "optional": true,
@@ -12648,7 +12725,7 @@
12648
12725
  "immutable": true,
12649
12726
  "locationInModule": {
12650
12727
  "filename": "src/web-function.ts",
12651
- "line": 65
12728
+ "line": 66
12652
12729
  },
12653
12730
  "name": "description",
12654
12731
  "optional": true,
@@ -12656,6 +12733,25 @@
12656
12733
  "primitive": "string"
12657
12734
  }
12658
12735
  },
12736
+ {
12737
+ "abstract": true,
12738
+ "docs": {
12739
+ "default": "- an AWS owned key",
12740
+ "remarks": "A `PER_REGION` or `MULTI_REGION` endpoint needs a **multi-Region key** with a replica in every region it serves\nfrom, and each replica's policy must allow Lambda to decrypt (see `encryptionKeyPolicyStatement()`). A key defined\nin this app gets that statement added to its own policy.\n\nChanging the key creates a new revision. Earlier revisions, including the ones kept as rollback targets, still\nneed their key.\n[disable-awslint:prefer-ref-interface]",
12741
+ "stability": "experimental",
12742
+ "summary": "A customer managed KMS key for the function's revisions: their code and environment variables."
12743
+ },
12744
+ "immutable": true,
12745
+ "locationInModule": {
12746
+ "filename": "src/web-function.ts",
12747
+ "line": 91
12748
+ },
12749
+ "name": "encryptionKey",
12750
+ "optional": true,
12751
+ "type": {
12752
+ "fqn": "aws-cdk-lib.aws_kms.IKey"
12753
+ }
12754
+ },
12659
12755
  {
12660
12756
  "abstract": true,
12661
12757
  "docs": {
@@ -12666,7 +12762,7 @@
12666
12762
  "immutable": true,
12667
12763
  "locationInModule": {
12668
12764
  "filename": "src/web-function.ts",
12669
- "line": 39
12765
+ "line": 40
12670
12766
  },
12671
12767
  "name": "endpoint",
12672
12768
  "optional": true,
@@ -12685,7 +12781,7 @@
12685
12781
  "immutable": true,
12686
12782
  "locationInModule": {
12687
12783
  "filename": "src/web-function.ts",
12688
- "line": 82
12784
+ "line": 98
12689
12785
  },
12690
12786
  "name": "environment",
12691
12787
  "optional": true,
@@ -12698,6 +12794,25 @@
12698
12794
  }
12699
12795
  }
12700
12796
  },
12797
+ {
12798
+ "abstract": true,
12799
+ "docs": {
12800
+ "default": "- CloudWatch Logs' own encryption",
12801
+ "remarks": "CloudWatch Logs needs a key in each group's region: for regions other than the key's own, the construct uses the\nreplica of a **multi-Region key** there (the same key ID). Each key's policy must allow CloudWatch Logs in its\nregion (see `logEncryptionKeyPolicyStatement()`). A key defined in this app gets that statement for its own region.\n\nRemoving the key decrypts nothing: the groups keep their existing events encrypted with it.\n[disable-awslint:prefer-ref-interface]",
12802
+ "stability": "experimental",
12803
+ "summary": "A customer managed KMS key for the function's log groups, in every region it serves from."
12804
+ },
12805
+ "immutable": true,
12806
+ "locationInModule": {
12807
+ "filename": "src/web-function.ts",
12808
+ "line": 142
12809
+ },
12810
+ "name": "logEncryptionKey",
12811
+ "optional": true,
12812
+ "type": {
12813
+ "fqn": "aws-cdk-lib.aws_kms.IKey"
12814
+ }
12815
+ },
12701
12816
  {
12702
12817
  "abstract": true,
12703
12818
  "docs": {
@@ -12709,7 +12824,7 @@
12709
12824
  "immutable": true,
12710
12825
  "locationInModule": {
12711
12826
  "filename": "src/web-function.ts",
12712
- "line": 112
12827
+ "line": 128
12713
12828
  },
12714
12829
  "name": "logRemovalPolicy",
12715
12830
  "optional": true,
@@ -12727,7 +12842,7 @@
12727
12842
  "immutable": true,
12728
12843
  "locationInModule": {
12729
12844
  "filename": "src/web-function.ts",
12730
- "line": 104
12845
+ "line": 120
12731
12846
  },
12732
12847
  "name": "logRetention",
12733
12848
  "optional": true,
@@ -12746,7 +12861,7 @@
12746
12861
  "immutable": true,
12747
12862
  "locationInModule": {
12748
12863
  "filename": "src/web-function.ts",
12749
- "line": 97
12864
+ "line": 113
12750
12865
  },
12751
12866
  "name": "maxConcurrencyPerEnvironment",
12752
12867
  "optional": true,
@@ -12765,7 +12880,7 @@
12765
12880
  "immutable": true,
12766
12881
  "locationInModule": {
12767
12882
  "filename": "src/web-function.ts",
12768
- "line": 51
12883
+ "line": 52
12769
12884
  },
12770
12885
  "name": "revisionRetention",
12771
12886
  "optional": true,
@@ -12784,7 +12899,7 @@
12784
12899
  "immutable": true,
12785
12900
  "locationInModule": {
12786
12901
  "filename": "src/web-function.ts",
12787
- "line": 75
12902
+ "line": 76
12788
12903
  },
12789
12904
  "name": "role",
12790
12905
  "optional": true,
@@ -12802,7 +12917,7 @@
12802
12917
  "immutable": true,
12803
12918
  "locationInModule": {
12804
12919
  "filename": "src/web-function.ts",
12805
- "line": 126
12920
+ "line": 156
12806
12921
  },
12807
12922
  "name": "systemLogLevelV2",
12808
12923
  "optional": true,
@@ -12820,7 +12935,7 @@
12820
12935
  "immutable": true,
12821
12936
  "locationInModule": {
12822
12937
  "filename": "src/web-function.ts",
12823
- "line": 89
12938
+ "line": 105
12824
12939
  },
12825
12940
  "name": "timeout",
12826
12941
  "optional": true,
@@ -12839,7 +12954,7 @@
12839
12954
  "immutable": true,
12840
12955
  "locationInModule": {
12841
12956
  "filename": "src/web-function.ts",
12842
- "line": 58
12957
+ "line": 59
12843
12958
  },
12844
12959
  "name": "webFunctionName",
12845
12960
  "optional": true,
@@ -13090,7 +13205,7 @@
13090
13205
  "kind": "interface",
13091
13206
  "locationInModule": {
13092
13207
  "filename": "src/web-function.ts",
13093
- "line": 239
13208
+ "line": 269
13094
13209
  },
13095
13210
  "name": "WebFunctionPermission",
13096
13211
  "properties": [
@@ -13104,7 +13219,7 @@
13104
13219
  "immutable": true,
13105
13220
  "locationInModule": {
13106
13221
  "filename": "src/web-function.ts",
13107
- "line": 246
13222
+ "line": 276
13108
13223
  },
13109
13224
  "name": "principal",
13110
13225
  "type": {
@@ -13122,7 +13237,7 @@
13122
13237
  "immutable": true,
13123
13238
  "locationInModule": {
13124
13239
  "filename": "src/web-function.ts",
13125
- "line": 253
13240
+ "line": 283
13126
13241
  },
13127
13242
  "name": "endpoint",
13128
13243
  "optional": true,
@@ -13140,7 +13255,7 @@
13140
13255
  "immutable": true,
13141
13256
  "locationInModule": {
13142
13257
  "filename": "src/web-function.ts",
13143
- "line": 268
13258
+ "line": 298
13144
13259
  },
13145
13260
  "name": "sourceAccount",
13146
13261
  "optional": true,
@@ -13158,7 +13273,7 @@
13158
13273
  "immutable": true,
13159
13274
  "locationInModule": {
13160
13275
  "filename": "src/web-function.ts",
13161
- "line": 261
13276
+ "line": 291
13162
13277
  },
13163
13278
  "name": "sourceArn",
13164
13279
  "optional": true,
@@ -13183,7 +13298,7 @@
13183
13298
  "kind": "interface",
13184
13299
  "locationInModule": {
13185
13300
  "filename": "src/web-function.ts",
13186
- "line": 132
13301
+ "line": 162
13187
13302
  },
13188
13303
  "name": "WebFunctionProps",
13189
13304
  "properties": [
@@ -13197,7 +13312,7 @@
13197
13312
  "immutable": true,
13198
13313
  "locationInModule": {
13199
13314
  "filename": "src/web-function.ts",
13200
- "line": 144
13315
+ "line": 174
13201
13316
  },
13202
13317
  "name": "code",
13203
13318
  "type": {
@@ -13213,7 +13328,7 @@
13213
13328
  "immutable": true,
13214
13329
  "locationInModule": {
13215
13330
  "filename": "src/web-function.ts",
13216
- "line": 136
13331
+ "line": 166
13217
13332
  },
13218
13333
  "name": "runtime",
13219
13334
  "type": {
@@ -13230,7 +13345,7 @@
13230
13345
  "immutable": true,
13231
13346
  "locationInModule": {
13232
13347
  "filename": "src/web-function.ts",
13233
- "line": 151
13348
+ "line": 181
13234
13349
  },
13235
13350
  "name": "entrypoint",
13236
13351
  "optional": true,
@@ -13562,6 +13677,6 @@
13562
13677
  "symbolId": "src/web-function-deployment:WebFunctionRollbackOptions"
13563
13678
  }
13564
13679
  },
13565
- "version": "0.1.10",
13566
- "fingerprint": "jNpRieNrldxUoisjGaK6PE0jlDtSXgs8VYo6J+/40Ao="
13680
+ "version": "0.1.12",
13681
+ "fingerprint": "eEKvgpGlRCGfraL+IN8F5JTtpuZ3CcK+GeIj4jZ3Q94="
13567
13682
  }