@ashishpandeyone/cdk-lambda-web-function 0.1.7 → 0.1.9

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- **One resource policy per function, written by the function's stack.** The construct creates it (`AWS::Lambda::ResourcePolicy`) with the first statement. CloudFormation can't attach a second one: another stack, or a `CfnResourcePolicy` of your own on the function, fails at change-set validation with \"already exists\". If you attached one yourself, move its statements into `addPermission` or `addToResourcePolicy`. Each update of the policy replaces any statements added outside CloudFormation.\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 must be in the function's stack. 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's `grantInvoke` adds to the caller's IAM policy only, which is all a caller in the function's account needs. A caller in another account also needs the function's resource policy to allow it, which only the function's own stack can change: `addToResourcePolicy` on an imported function adds nothing and warns.\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- **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### 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## 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### 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",
@@ -9840,7 +9840,7 @@
9840
9840
  "@ashishpandeyone/cdk-lambda-web-function.IWebFunction": {
9841
9841
  "assembly": "@ashishpandeyone/cdk-lambda-web-function",
9842
9842
  "docs": {
9843
- "remarks": "`addToResourcePolicy` adds to the function's resource policy (who else may call or manage it). A `WebFunction` has\none, created by its stack on the first statement. An imported function's policy belongs to the stack that created\nit, so the statement isn't added (`statementAdded: false`).",
9843
+ "remarks": "`addToResourcePolicy` adds to the function's resource policy (who else may call or manage it). Each stack writes\nonly its own statements into the function's single policy, so any stack in the function's account can add them,\nincluding one that imports the function. From another account, nothing is added (`statementAdded: false`).",
9844
9844
  "stability": "experimental",
9845
9845
  "summary": "A Lambda Web Function, created in this app (`WebFunction`) or imported (`WebFunction.fromWebFunctionArn`)."
9846
9846
  },
@@ -9853,13 +9853,13 @@
9853
9853
  "kind": "interface",
9854
9854
  "locationInModule": {
9855
9855
  "filename": "src/web-function.ts",
9856
- "line": 160
9856
+ "line": 161
9857
9857
  },
9858
9858
  "methods": [
9859
9859
  {
9860
9860
  "abstract": true,
9861
9861
  "docs": {
9862
- "remarks": "for an imported function, the endpoint named in its attributes, or every endpoint.\n\nFor a `WebFunction`, a grantee in this account gets an IAM policy statement; another account or an AWS service\nalso gets a statement in the function's resource policy. For an imported function, only the grantee's IAM policy\nchanges: across accounts, the function's own resource policy must also allow the grantee.\n[disable-awslint:no-grants]",
9862
+ "remarks": "for an imported function, the endpoint named in its attributes, or every endpoint.\n\nA grantee in the function's account gets an IAM policy statement; another account or an AWS service also gets a\nstatement in the function's resource policy (not when the function itself is in another account than this stack).\n[disable-awslint:no-grants]",
9863
9863
  "stability": "experimental",
9864
9864
  "summary": "Allows `grantee` to call the function's endpoint (`lambda:InvokeWebFunctionEndpoint`): the default endpoint of a `WebFunction`;"
9865
9865
  },
@@ -10059,7 +10059,7 @@
10059
10059
  "immutable": true,
10060
10060
  "locationInModule": {
10061
10061
  "filename": "src/web-function.ts",
10062
- "line": 173
10062
+ "line": 174
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": 166
10081
+ "line": 167
10082
10082
  },
10083
10083
  "name": "webFunctionName",
10084
10084
  "type": {
@@ -10655,7 +10655,7 @@
10655
10655
  },
10656
10656
  "locationInModule": {
10657
10657
  "filename": "src/web-function.ts",
10658
- "line": 420
10658
+ "line": 445
10659
10659
  },
10660
10660
  "parameters": [
10661
10661
  {
@@ -10684,7 +10684,7 @@
10684
10684
  "kind": "class",
10685
10685
  "locationInModule": {
10686
10686
  "filename": "src/web-function.ts",
10687
- "line": 361
10687
+ "line": 387
10688
10688
  },
10689
10689
  "methods": [
10690
10690
  {
@@ -10694,7 +10694,7 @@
10694
10694
  },
10695
10695
  "locationInModule": {
10696
10696
  "filename": "src/web-function.ts",
10697
- "line": 365
10697
+ "line": 391
10698
10698
  },
10699
10699
  "name": "fromWebFunctionArn",
10700
10700
  "parameters": [
@@ -10731,7 +10731,7 @@
10731
10731
  },
10732
10732
  "locationInModule": {
10733
10733
  "filename": "src/web-function.ts",
10734
- "line": 379
10734
+ "line": 405
10735
10735
  },
10736
10736
  "name": "fromWebFunctionAttributes",
10737
10737
  "parameters": [
@@ -10768,7 +10768,7 @@
10768
10768
  },
10769
10769
  "locationInModule": {
10770
10770
  "filename": "src/web-function.ts",
10771
- "line": 372
10771
+ "line": 398
10772
10772
  },
10773
10773
  "name": "fromWebFunctionName",
10774
10774
  "parameters": [
@@ -10805,7 +10805,7 @@
10805
10805
  },
10806
10806
  "locationInModule": {
10807
10807
  "filename": "src/web-function.ts",
10808
- "line": 522
10808
+ "line": 544
10809
10809
  },
10810
10810
  "name": "addEndpoint",
10811
10811
  "parameters": [
@@ -10839,7 +10839,7 @@
10839
10839
  },
10840
10840
  "locationInModule": {
10841
10841
  "filename": "src/web-function.ts",
10842
- "line": 530
10842
+ "line": 552
10843
10843
  },
10844
10844
  "name": "addEnvironment",
10845
10845
  "parameters": [
@@ -10870,7 +10870,7 @@
10870
10870
  },
10871
10871
  "locationInModule": {
10872
10872
  "filename": "src/web-function.ts",
10873
- "line": 559
10873
+ "line": 581
10874
10874
  },
10875
10875
  "name": "addPermission",
10876
10876
  "parameters": [
@@ -10891,32 +10891,6 @@
10891
10891
  }
10892
10892
  ]
10893
10893
  },
10894
- {
10895
- "docs": {
10896
- "remarks": "The function has one resource policy (`AWS::Lambda::ResourcePolicy`), created on the first statement. This stack\nis its only writer: another stack or template can't also attach one, and changes made outside CloudFormation\nare overwritten by the next update of the policy.",
10897
- "stability": "experimental",
10898
- "summary": "Adds a statement to the function's resource policy, e.g. to let another account manage the function. For invoking, prefer `addPermission` or `grantInvoke`."
10899
- },
10900
- "locationInModule": {
10901
- "filename": "src/web-function.ts",
10902
- "line": 590
10903
- },
10904
- "name": "addToResourcePolicy",
10905
- "overrides": "@ashishpandeyone/cdk-lambda-web-function.WebFunctionBase",
10906
- "parameters": [
10907
- {
10908
- "name": "statement",
10909
- "type": {
10910
- "fqn": "aws-cdk-lib.aws_iam.PolicyStatement"
10911
- }
10912
- }
10913
- ],
10914
- "returns": {
10915
- "type": {
10916
- "fqn": "aws-cdk-lib.aws_iam.AddToResourcePolicyResult"
10917
- }
10918
- }
10919
- },
10920
10894
  {
10921
10895
  "docs": {
10922
10896
  "stability": "experimental",
@@ -10924,7 +10898,7 @@
10924
10898
  },
10925
10899
  "locationInModule": {
10926
10900
  "filename": "src/web-function.ts",
10927
- "line": 541
10901
+ "line": 563
10928
10902
  },
10929
10903
  "name": "addToRolePolicy",
10930
10904
  "parameters": [
@@ -10944,7 +10918,7 @@
10944
10918
  },
10945
10919
  "locationInModule": {
10946
10920
  "filename": "src/web-function.ts",
10947
- "line": 549
10921
+ "line": 571
10948
10922
  },
10949
10923
  "name": "grantInvoke",
10950
10924
  "overrides": "@ashishpandeyone/cdk-lambda-web-function.WebFunctionBase",
@@ -10973,7 +10947,7 @@
10973
10947
  "immutable": true,
10974
10948
  "locationInModule": {
10975
10949
  "filename": "src/web-function.ts",
10976
- "line": 390
10950
+ "line": 416
10977
10951
  },
10978
10952
  "name": "currentRevision",
10979
10953
  "type": {
@@ -10989,7 +10963,7 @@
10989
10963
  "immutable": true,
10990
10964
  "locationInModule": {
10991
10965
  "filename": "src/web-function.ts",
10992
- "line": 393
10966
+ "line": 419
10993
10967
  },
10994
10968
  "name": "endpoint",
10995
10969
  "type": {
@@ -11004,7 +10978,7 @@
11004
10978
  "immutable": true,
11005
10979
  "locationInModule": {
11006
10980
  "filename": "src/web-function.ts",
11007
- "line": 402
10981
+ "line": 428
11008
10982
  },
11009
10983
  "name": "grantPrincipal",
11010
10984
  "overrides": "aws-cdk-lib.aws_iam.IGrantable",
@@ -11020,7 +10994,7 @@
11020
10994
  "immutable": true,
11021
10995
  "locationInModule": {
11022
10996
  "filename": "src/web-function.ts",
11023
- "line": 396
10997
+ "line": 422
11024
10998
  },
11025
10999
  "name": "grants",
11026
11000
  "type": {
@@ -11036,7 +11010,7 @@
11036
11010
  "immutable": true,
11037
11011
  "locationInModule": {
11038
11012
  "filename": "src/web-function.ts",
11039
- "line": 408
11013
+ "line": 434
11040
11014
  },
11041
11015
  "name": "logGroup",
11042
11016
  "type": {
@@ -11052,7 +11026,7 @@
11052
11026
  "immutable": true,
11053
11027
  "locationInModule": {
11054
11028
  "filename": "src/web-function.ts",
11055
- "line": 387
11029
+ "line": 413
11056
11030
  },
11057
11031
  "name": "revisionId",
11058
11032
  "type": {
@@ -11067,7 +11041,7 @@
11067
11041
  "immutable": true,
11068
11042
  "locationInModule": {
11069
11043
  "filename": "src/web-function.ts",
11070
- "line": 399
11044
+ "line": 425
11071
11045
  },
11072
11046
  "name": "role",
11073
11047
  "type": {
@@ -11082,7 +11056,7 @@
11082
11056
  "immutable": true,
11083
11057
  "locationInModule": {
11084
11058
  "filename": "src/web-function.ts",
11085
- "line": 384
11059
+ "line": 410
11086
11060
  },
11087
11061
  "name": "webFunctionArn",
11088
11062
  "overrides": "@ashishpandeyone/cdk-lambda-web-function.WebFunctionBase",
@@ -11098,7 +11072,7 @@
11098
11072
  "immutable": true,
11099
11073
  "locationInModule": {
11100
11074
  "filename": "src/web-function.ts",
11101
- "line": 383
11075
+ "line": 409
11102
11076
  },
11103
11077
  "name": "webFunctionName",
11104
11078
  "overrides": "@ashishpandeyone/cdk-lambda-web-function.WebFunctionBase",
@@ -11135,7 +11109,7 @@
11135
11109
  "immutable": true,
11136
11110
  "locationInModule": {
11137
11111
  "filename": "src/web-function.ts",
11138
- "line": 231
11112
+ "line": 232
11139
11113
  },
11140
11114
  "name": "endpointName",
11141
11115
  "optional": true,
@@ -11148,12 +11122,12 @@
11148
11122
  "docs": {
11149
11123
  "default": "- built from `webFunctionName`, in this stack's account and region",
11150
11124
  "stability": "experimental",
11151
- "summary": "The function's ARN, e.g. `arn:aws:lambda:us-east-1:111122223333:web-function/orders`. It may be in another region or account; across accounts, `grantInvoke` also needs the function's resource policy to allow the caller."
11125
+ "summary": "The function's ARN, e.g. `arn:aws:lambda:us-east-1:111122223333:web-function/orders`. It may be in another region or account; across accounts, `grantInvoke` also needs the function's resource policy to allow the caller. A token (e.g. from another stack) is taken to be in this stack's account and region."
11152
11126
  },
11153
11127
  "immutable": true,
11154
11128
  "locationInModule": {
11155
11129
  "filename": "src/web-function.ts",
11156
- "line": 217
11130
+ "line": 218
11157
11131
  },
11158
11132
  "name": "webFunctionArn",
11159
11133
  "optional": true,
@@ -11171,7 +11145,7 @@
11171
11145
  "immutable": true,
11172
11146
  "locationInModule": {
11173
11147
  "filename": "src/web-function.ts",
11174
- "line": 224
11148
+ "line": 225
11175
11149
  },
11176
11150
  "name": "webFunctionName",
11177
11151
  "optional": true,
@@ -11259,18 +11233,18 @@
11259
11233
  "kind": "class",
11260
11234
  "locationInModule": {
11261
11235
  "filename": "src/web-function.ts",
11262
- "line": 272
11236
+ "line": 273
11263
11237
  },
11264
11238
  "methods": [
11265
11239
  {
11266
- "abstract": true,
11267
11240
  "docs": {
11241
+ "remarks": "The function has a single resource policy. Each stack writes only its own statements into it, through a custom\nresource that merges them with every other stack's, and with statements added outside CloudFormation. From a\nstack in another account than the function, nothing is added.",
11268
11242
  "stability": "experimental",
11269
- "summary": "Adds a statement to the function's resource policy (not added for an imported function)."
11243
+ "summary": "Adds a statement to the function's resource policy, e.g. to let another account manage the function. For invoking, prefer `addPermission` or `grantInvoke`."
11270
11244
  },
11271
11245
  "locationInModule": {
11272
11246
  "filename": "src/web-function.ts",
11273
- "line": 290
11247
+ "line": 296
11274
11248
  },
11275
11249
  "name": "addToResourcePolicy",
11276
11250
  "overrides": "aws-cdk-lib.aws_iam.IResourceWithPolicyV2",
@@ -11297,7 +11271,7 @@
11297
11271
  },
11298
11272
  "locationInModule": {
11299
11273
  "filename": "src/web-function.ts",
11300
- "line": 285
11274
+ "line": 286
11301
11275
  },
11302
11276
  "name": "grantInvoke",
11303
11277
  "overrides": "@ashishpandeyone/cdk-lambda-web-function.IWebFunction",
@@ -11324,7 +11298,7 @@
11324
11298
  },
11325
11299
  "locationInModule": {
11326
11300
  "filename": "src/web-function.ts",
11327
- "line": 301
11301
+ "line": 327
11328
11302
  },
11329
11303
  "name": "metric",
11330
11304
  "overrides": "@ashishpandeyone/cdk-lambda-web-function.IWebFunction",
@@ -11356,7 +11330,7 @@
11356
11330
  },
11357
11331
  "locationInModule": {
11358
11332
  "filename": "src/web-function.ts",
11359
- "line": 316
11333
+ "line": 342
11360
11334
  },
11361
11335
  "name": "metricActiveTime",
11362
11336
  "overrides": "@ashishpandeyone/cdk-lambda-web-function.IWebFunction",
@@ -11383,7 +11357,7 @@
11383
11357
  },
11384
11358
  "locationInModule": {
11385
11359
  "filename": "src/web-function.ts",
11386
- "line": 331
11360
+ "line": 357
11387
11361
  },
11388
11362
  "name": "metricConcurrentRequests",
11389
11363
  "overrides": "@ashishpandeyone/cdk-lambda-web-function.IWebFunction",
@@ -11409,7 +11383,7 @@
11409
11383
  },
11410
11384
  "locationInModule": {
11411
11385
  "filename": "src/web-function.ts",
11412
- "line": 321
11386
+ "line": 347
11413
11387
  },
11414
11388
  "name": "metricCpuTime",
11415
11389
  "overrides": "@ashishpandeyone/cdk-lambda-web-function.IWebFunction",
@@ -11437,7 +11411,7 @@
11437
11411
  },
11438
11412
  "locationInModule": {
11439
11413
  "filename": "src/web-function.ts",
11440
- "line": 340
11414
+ "line": 366
11441
11415
  },
11442
11416
  "name": "metricMaxMemoryUsage",
11443
11417
  "overrides": "@ashishpandeyone/cdk-lambda-web-function.IWebFunction",
@@ -11463,7 +11437,7 @@
11463
11437
  },
11464
11438
  "locationInModule": {
11465
11439
  "filename": "src/web-function.ts",
11466
- "line": 345
11440
+ "line": 371
11467
11441
  },
11468
11442
  "name": "metricResponseBytes",
11469
11443
  "overrides": "@ashishpandeyone/cdk-lambda-web-function.IWebFunction",
@@ -11494,7 +11468,7 @@
11494
11468
  "immutable": true,
11495
11469
  "locationInModule": {
11496
11470
  "filename": "src/web-function.ts",
11497
- "line": 274
11471
+ "line": 275
11498
11472
  },
11499
11473
  "name": "webFunctionArn",
11500
11474
  "overrides": "@ashishpandeyone/cdk-lambda-web-function.IWebFunction",
@@ -11511,7 +11485,7 @@
11511
11485
  "immutable": true,
11512
11486
  "locationInModule": {
11513
11487
  "filename": "src/web-function.ts",
11514
- "line": 273
11488
+ "line": 274
11515
11489
  },
11516
11490
  "name": "webFunctionName",
11517
11491
  "overrides": "@ashishpandeyone/cdk-lambda-web-function.IWebFunction",
@@ -11527,7 +11501,7 @@
11527
11501
  "immutable": true,
11528
11502
  "locationInModule": {
11529
11503
  "filename": "src/web-function.ts",
11530
- "line": 277
11504
+ "line": 278
11531
11505
  },
11532
11506
  "name": "webFunctionRef",
11533
11507
  "overrides": "aws-cdk-lib.interfaces.aws_lambda.IWebFunctionRef",
@@ -12458,7 +12432,7 @@
12458
12432
  "methods": [
12459
12433
  {
12460
12434
  "docs": {
12461
- "remarks": "The grant is on the endpoint's home-region ARN, which IAM evaluates for every region the\nendpoint serves from, including a MULTI_REGION global URL.\n\nA grantee in the function's account gets an IAM policy statement. Another account or an AWS service also gets\na statement in the function's resource policy (not for an imported function's endpoints).",
12435
+ "remarks": "The grant is on the endpoint's home-region ARN, which IAM evaluates for every region the\nendpoint serves from, including a MULTI_REGION global URL.\n\nA grantee in the function's account gets an IAM policy statement. Another account or an AWS service also gets\na statement in the function's resource policy.",
12462
12436
  "stability": "experimental",
12463
12437
  "summary": "Allows `grantee` to call the endpoint (`lambda:InvokeWebFunctionEndpoint`)."
12464
12438
  },
@@ -12589,7 +12563,7 @@
12589
12563
  "kind": "interface",
12590
12564
  "locationInModule": {
12591
12565
  "filename": "src/web-function.ts",
12592
- "line": 24
12566
+ "line": 25
12593
12567
  },
12594
12568
  "name": "WebFunctionOptions",
12595
12569
  "properties": [
@@ -12602,7 +12576,7 @@
12602
12576
  "immutable": true,
12603
12577
  "locationInModule": {
12604
12578
  "filename": "src/web-function.ts",
12605
- "line": 28
12579
+ "line": 29
12606
12580
  },
12607
12581
  "name": "authType",
12608
12582
  "type": {
@@ -12619,7 +12593,7 @@
12619
12593
  "immutable": true,
12620
12594
  "locationInModule": {
12621
12595
  "filename": "src/web-function.ts",
12622
- "line": 117
12596
+ "line": 118
12623
12597
  },
12624
12598
  "name": "applicationLogLevelV2",
12625
12599
  "optional": true,
@@ -12637,7 +12611,7 @@
12637
12611
  "immutable": true,
12638
12612
  "locationInModule": {
12639
12613
  "filename": "src/web-function.ts",
12640
- "line": 63
12614
+ "line": 64
12641
12615
  },
12642
12616
  "name": "description",
12643
12617
  "optional": true,
@@ -12655,7 +12629,7 @@
12655
12629
  "immutable": true,
12656
12630
  "locationInModule": {
12657
12631
  "filename": "src/web-function.ts",
12658
- "line": 37
12632
+ "line": 38
12659
12633
  },
12660
12634
  "name": "endpoint",
12661
12635
  "optional": true,
@@ -12674,7 +12648,7 @@
12674
12648
  "immutable": true,
12675
12649
  "locationInModule": {
12676
12650
  "filename": "src/web-function.ts",
12677
- "line": 80
12651
+ "line": 81
12678
12652
  },
12679
12653
  "name": "environment",
12680
12654
  "optional": true,
@@ -12698,7 +12672,7 @@
12698
12672
  "immutable": true,
12699
12673
  "locationInModule": {
12700
12674
  "filename": "src/web-function.ts",
12701
- "line": 110
12675
+ "line": 111
12702
12676
  },
12703
12677
  "name": "logRemovalPolicy",
12704
12678
  "optional": true,
@@ -12716,7 +12690,7 @@
12716
12690
  "immutable": true,
12717
12691
  "locationInModule": {
12718
12692
  "filename": "src/web-function.ts",
12719
- "line": 102
12693
+ "line": 103
12720
12694
  },
12721
12695
  "name": "logRetention",
12722
12696
  "optional": true,
@@ -12735,7 +12709,7 @@
12735
12709
  "immutable": true,
12736
12710
  "locationInModule": {
12737
12711
  "filename": "src/web-function.ts",
12738
- "line": 95
12712
+ "line": 96
12739
12713
  },
12740
12714
  "name": "maxConcurrencyPerEnvironment",
12741
12715
  "optional": true,
@@ -12754,7 +12728,7 @@
12754
12728
  "immutable": true,
12755
12729
  "locationInModule": {
12756
12730
  "filename": "src/web-function.ts",
12757
- "line": 49
12731
+ "line": 50
12758
12732
  },
12759
12733
  "name": "revisionRetention",
12760
12734
  "optional": true,
@@ -12773,7 +12747,7 @@
12773
12747
  "immutable": true,
12774
12748
  "locationInModule": {
12775
12749
  "filename": "src/web-function.ts",
12776
- "line": 73
12750
+ "line": 74
12777
12751
  },
12778
12752
  "name": "role",
12779
12753
  "optional": true,
@@ -12791,7 +12765,7 @@
12791
12765
  "immutable": true,
12792
12766
  "locationInModule": {
12793
12767
  "filename": "src/web-function.ts",
12794
- "line": 124
12768
+ "line": 125
12795
12769
  },
12796
12770
  "name": "systemLogLevelV2",
12797
12771
  "optional": true,
@@ -12809,7 +12783,7 @@
12809
12783
  "immutable": true,
12810
12784
  "locationInModule": {
12811
12785
  "filename": "src/web-function.ts",
12812
- "line": 87
12786
+ "line": 88
12813
12787
  },
12814
12788
  "name": "timeout",
12815
12789
  "optional": true,
@@ -12828,7 +12802,7 @@
12828
12802
  "immutable": true,
12829
12803
  "locationInModule": {
12830
12804
  "filename": "src/web-function.ts",
12831
- "line": 56
12805
+ "line": 57
12832
12806
  },
12833
12807
  "name": "webFunctionName",
12834
12808
  "optional": true,
@@ -12854,7 +12828,7 @@
12854
12828
  },
12855
12829
  "locationInModule": {
12856
12830
  "filename": "src/web-function-origin.ts",
12857
- "line": 95
12831
+ "line": 96
12858
12832
  },
12859
12833
  "parameters": [
12860
12834
  {
@@ -12875,18 +12849,18 @@
12875
12849
  "kind": "class",
12876
12850
  "locationInModule": {
12877
12851
  "filename": "src/web-function-origin.ts",
12878
- "line": 72
12852
+ "line": 73
12879
12853
  },
12880
12854
  "methods": [
12881
12855
  {
12882
12856
  "docs": {
12883
- "remarks": "The distribution must be in the function's stack.",
12857
+ "remarks": "The distribution can be in another stack than the function, in the same account.",
12884
12858
  "stability": "experimental",
12885
12859
  "summary": "An origin for an `AWS_IAM` endpoint, with origin access control: CloudFront signs every request to the endpoint (`always-amz-auth`, which keeps the viewer's `Authorization` header for your application), and the function's resource policy allows `cloudfront.amazonaws.com` to call this endpoint on behalf of this distribution only."
12886
12860
  },
12887
12861
  "locationInModule": {
12888
12862
  "filename": "src/web-function-origin.ts",
12889
- "line": 80
12863
+ "line": 81
12890
12864
  },
12891
12865
  "name": "withOriginAccessControl",
12892
12866
  "parameters": [
@@ -12917,7 +12891,7 @@
12917
12891
  },
12918
12892
  "locationInModule": {
12919
12893
  "filename": "src/web-function-origin.ts",
12920
- "line": 105
12894
+ "line": 106
12921
12895
  },
12922
12896
  "name": "renderCustomOriginConfig",
12923
12897
  "overrides": "aws-cdk-lib.aws_cloudfront.OriginBase",
@@ -12947,7 +12921,7 @@
12947
12921
  "kind": "interface",
12948
12922
  "locationInModule": {
12949
12923
  "filename": "src/web-function-origin.ts",
12950
- "line": 20
12924
+ "line": 21
12951
12925
  },
12952
12926
  "name": "WebFunctionOriginProps",
12953
12927
  "properties": [
@@ -12961,7 +12935,7 @@
12961
12935
  "immutable": true,
12962
12936
  "locationInModule": {
12963
12937
  "filename": "src/web-function-origin.ts",
12964
- "line": 48
12938
+ "line": 49
12965
12939
  },
12966
12940
  "name": "ipAddressType",
12967
12941
  "optional": true,
@@ -12979,7 +12953,7 @@
12979
12953
  "immutable": true,
12980
12954
  "locationInModule": {
12981
12955
  "filename": "src/web-function-origin.ts",
12982
- "line": 41
12956
+ "line": 42
12983
12957
  },
12984
12958
  "name": "keepaliveTimeout",
12985
12959
  "optional": true,
@@ -12998,7 +12972,7 @@
12998
12972
  "immutable": true,
12999
12973
  "locationInModule": {
13000
12974
  "filename": "src/web-function-origin.ts",
13001
- "line": 34
12975
+ "line": 35
13002
12976
  },
13003
12977
  "name": "readTimeout",
13004
12978
  "optional": true,
@@ -13017,7 +12991,7 @@
13017
12991
  "immutable": true,
13018
12992
  "locationInModule": {
13019
12993
  "filename": "src/web-function-origin.ts",
13020
- "line": 27
12994
+ "line": 28
13021
12995
  },
13022
12996
  "name": "region",
13023
12997
  "optional": true,
@@ -13042,7 +13016,7 @@
13042
13016
  "kind": "interface",
13043
13017
  "locationInModule": {
13044
13018
  "filename": "src/web-function-origin.ts",
13045
- "line": 54
13019
+ "line": 55
13046
13020
  },
13047
13021
  "name": "WebFunctionOriginWithOacProps",
13048
13022
  "properties": [
@@ -13057,7 +13031,7 @@
13057
13031
  "immutable": true,
13058
13032
  "locationInModule": {
13059
13033
  "filename": "src/web-function-origin.ts",
13060
- "line": 63
13034
+ "line": 64
13061
13035
  },
13062
13036
  "name": "originAccessControl",
13063
13037
  "optional": true,
@@ -13079,7 +13053,7 @@
13079
13053
  "kind": "interface",
13080
13054
  "locationInModule": {
13081
13055
  "filename": "src/web-function.ts",
13082
- "line": 237
13056
+ "line": 238
13083
13057
  },
13084
13058
  "name": "WebFunctionPermission",
13085
13059
  "properties": [
@@ -13093,7 +13067,7 @@
13093
13067
  "immutable": true,
13094
13068
  "locationInModule": {
13095
13069
  "filename": "src/web-function.ts",
13096
- "line": 244
13070
+ "line": 245
13097
13071
  },
13098
13072
  "name": "principal",
13099
13073
  "type": {
@@ -13111,7 +13085,7 @@
13111
13085
  "immutable": true,
13112
13086
  "locationInModule": {
13113
13087
  "filename": "src/web-function.ts",
13114
- "line": 251
13088
+ "line": 252
13115
13089
  },
13116
13090
  "name": "endpoint",
13117
13091
  "optional": true,
@@ -13129,7 +13103,7 @@
13129
13103
  "immutable": true,
13130
13104
  "locationInModule": {
13131
13105
  "filename": "src/web-function.ts",
13132
- "line": 266
13106
+ "line": 267
13133
13107
  },
13134
13108
  "name": "sourceAccount",
13135
13109
  "optional": true,
@@ -13147,7 +13121,7 @@
13147
13121
  "immutable": true,
13148
13122
  "locationInModule": {
13149
13123
  "filename": "src/web-function.ts",
13150
- "line": 259
13124
+ "line": 260
13151
13125
  },
13152
13126
  "name": "sourceArn",
13153
13127
  "optional": true,
@@ -13172,7 +13146,7 @@
13172
13146
  "kind": "interface",
13173
13147
  "locationInModule": {
13174
13148
  "filename": "src/web-function.ts",
13175
- "line": 130
13149
+ "line": 131
13176
13150
  },
13177
13151
  "name": "WebFunctionProps",
13178
13152
  "properties": [
@@ -13186,7 +13160,7 @@
13186
13160
  "immutable": true,
13187
13161
  "locationInModule": {
13188
13162
  "filename": "src/web-function.ts",
13189
- "line": 142
13163
+ "line": 143
13190
13164
  },
13191
13165
  "name": "code",
13192
13166
  "type": {
@@ -13202,7 +13176,7 @@
13202
13176
  "immutable": true,
13203
13177
  "locationInModule": {
13204
13178
  "filename": "src/web-function.ts",
13205
- "line": 134
13179
+ "line": 135
13206
13180
  },
13207
13181
  "name": "runtime",
13208
13182
  "type": {
@@ -13219,7 +13193,7 @@
13219
13193
  "immutable": true,
13220
13194
  "locationInModule": {
13221
13195
  "filename": "src/web-function.ts",
13222
- "line": 149
13196
+ "line": 150
13223
13197
  },
13224
13198
  "name": "entrypoint",
13225
13199
  "optional": true,
@@ -13551,6 +13525,6 @@
13551
13525
  "symbolId": "src/web-function-deployment:WebFunctionRollbackOptions"
13552
13526
  }
13553
13527
  },
13554
- "version": "0.1.7",
13555
- "fingerprint": "ReNvwm8YKoEP0PP+XYMfq2RiNr5aFe4/3oAyk5ruqjA="
13528
+ "version": "0.1.9",
13529
+ "fingerprint": "Dn29ca+sUsH1dlKk8++TAaaSga5SpeYgvVF0XGm7cX4="
13556
13530
  }