diff --git a/.changeset/aws-lambda-adapter.md b/.changeset/aws-lambda-adapter.md
new file mode 100644
index 00000000..e31e2b9c
--- /dev/null
+++ b/.changeset/aws-lambda-adapter.md
@@ -0,0 +1,5 @@
+---
+"@marko/run-adapter-aws-lambda": minor
+---
+
+Add an AWS Lambda adapter for deploying @marko/run apps to Lambda (Function URLs / API Gateway HTTP API, payload format v2), including static asset serving and a local preview server.
diff --git a/cspell.json b/cspell.json
index 21f9d12d..ea4cd632 100644
--- a/cspell.json
+++ b/cspell.json
@@ -4,6 +4,8 @@
"words": [
"marko",
"markojs",
+ "fileb",
+ "Millis",
"codegen",
"Vite",
"lightgray",
diff --git a/package-lock.json b/package-lock.json
index a01363b5..dd693dfc 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -2485,6 +2485,10 @@
"resolved": "packages/run",
"link": true
},
+ "node_modules/@marko/run-adapter-aws-lambda": {
+ "resolved": "packages/adapters/aws-lambda",
+ "link": true
+ },
"node_modules/@marko/run-adapter-netlify": {
"resolved": "packages/adapters/netlify",
"link": true
@@ -14038,6 +14042,381 @@
}
}
},
+ "node_modules/netlify-cli/node_modules/@rollup/rollup-android-arm-eabi": {
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.59.0.tgz",
+ "integrity": "sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "peer": true
+ },
+ "node_modules/netlify-cli/node_modules/@rollup/rollup-android-arm64": {
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.59.0.tgz",
+ "integrity": "sha512-hZ+Zxj3SySm4A/DylsDKZAeVg0mvi++0PYVceVyX7hemkw7OreKdCvW2oQ3T1FMZvCaQXqOTHb8qmBShoqk69Q==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "peer": true
+ },
+ "node_modules/netlify-cli/node_modules/@rollup/rollup-darwin-arm64": {
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.59.0.tgz",
+ "integrity": "sha512-W2Psnbh1J8ZJw0xKAd8zdNgF9HRLkdWwwdWqubSVk0pUuQkoHnv7rx4GiF9rT4t5DIZGAsConRE3AxCdJ4m8rg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "peer": true
+ },
+ "node_modules/netlify-cli/node_modules/@rollup/rollup-darwin-x64": {
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.59.0.tgz",
+ "integrity": "sha512-ZW2KkwlS4lwTv7ZVsYDiARfFCnSGhzYPdiOU4IM2fDbL+QGlyAbjgSFuqNRbSthybLbIJ915UtZBtmuLrQAT/w==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "peer": true
+ },
+ "node_modules/netlify-cli/node_modules/@rollup/rollup-freebsd-arm64": {
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.59.0.tgz",
+ "integrity": "sha512-EsKaJ5ytAu9jI3lonzn3BgG8iRBjV4LxZexygcQbpiU0wU0ATxhNVEpXKfUa0pS05gTcSDMKpn3Sx+QB9RlTTA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "peer": true
+ },
+ "node_modules/netlify-cli/node_modules/@rollup/rollup-freebsd-x64": {
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.59.0.tgz",
+ "integrity": "sha512-d3DuZi2KzTMjImrxoHIAODUZYoUUMsuUiY4SRRcJy6NJoZ6iIqWnJu9IScV9jXysyGMVuW+KNzZvBLOcpdl3Vg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "peer": true
+ },
+ "node_modules/netlify-cli/node_modules/@rollup/rollup-linux-arm-gnueabihf": {
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.59.0.tgz",
+ "integrity": "sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "peer": true
+ },
+ "node_modules/netlify-cli/node_modules/@rollup/rollup-linux-arm-musleabihf": {
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.59.0.tgz",
+ "integrity": "sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "peer": true
+ },
+ "node_modules/netlify-cli/node_modules/@rollup/rollup-linux-arm64-gnu": {
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.59.0.tgz",
+ "integrity": "sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "peer": true
+ },
+ "node_modules/netlify-cli/node_modules/@rollup/rollup-linux-arm64-musl": {
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.59.0.tgz",
+ "integrity": "sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "peer": true
+ },
+ "node_modules/netlify-cli/node_modules/@rollup/rollup-linux-loong64-gnu": {
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.59.0.tgz",
+ "integrity": "sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "peer": true
+ },
+ "node_modules/netlify-cli/node_modules/@rollup/rollup-linux-loong64-musl": {
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.59.0.tgz",
+ "integrity": "sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "peer": true
+ },
+ "node_modules/netlify-cli/node_modules/@rollup/rollup-linux-ppc64-gnu": {
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.59.0.tgz",
+ "integrity": "sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "peer": true
+ },
+ "node_modules/netlify-cli/node_modules/@rollup/rollup-linux-ppc64-musl": {
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.59.0.tgz",
+ "integrity": "sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "peer": true
+ },
+ "node_modules/netlify-cli/node_modules/@rollup/rollup-linux-riscv64-gnu": {
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.59.0.tgz",
+ "integrity": "sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "peer": true
+ },
+ "node_modules/netlify-cli/node_modules/@rollup/rollup-linux-riscv64-musl": {
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.59.0.tgz",
+ "integrity": "sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "peer": true
+ },
+ "node_modules/netlify-cli/node_modules/@rollup/rollup-linux-s390x-gnu": {
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.59.0.tgz",
+ "integrity": "sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "peer": true
+ },
+ "node_modules/netlify-cli/node_modules/@rollup/rollup-linux-x64-gnu": {
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.59.0.tgz",
+ "integrity": "sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "peer": true
+ },
+ "node_modules/netlify-cli/node_modules/@rollup/rollup-linux-x64-musl": {
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.59.0.tgz",
+ "integrity": "sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "peer": true
+ },
+ "node_modules/netlify-cli/node_modules/@rollup/rollup-openbsd-x64": {
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.59.0.tgz",
+ "integrity": "sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "peer": true
+ },
+ "node_modules/netlify-cli/node_modules/@rollup/rollup-openharmony-arm64": {
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.59.0.tgz",
+ "integrity": "sha512-tt9KBJqaqp5i5HUZzoafHZX8b5Q2Fe7UjYERADll83O4fGqJ49O1FsL6LpdzVFQcpwvnyd0i+K/VSwu/o/nWlA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openharmony"
+ ],
+ "peer": true
+ },
+ "node_modules/netlify-cli/node_modules/@rollup/rollup-win32-arm64-msvc": {
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.59.0.tgz",
+ "integrity": "sha512-V5B6mG7OrGTwnxaNUzZTDTjDS7F75PO1ae6MJYdiMu60sq0CqN5CVeVsbhPxalupvTX8gXVSU9gq+Rx1/hvu6A==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "peer": true
+ },
+ "node_modules/netlify-cli/node_modules/@rollup/rollup-win32-ia32-msvc": {
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.59.0.tgz",
+ "integrity": "sha512-UKFMHPuM9R0iBegwzKF4y0C4J9u8C6MEJgFuXTBerMk7EJ92GFVFYBfOZaSGLu6COf7FxpQNqhNS4c4icUPqxA==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "peer": true
+ },
+ "node_modules/netlify-cli/node_modules/@rollup/rollup-win32-x64-gnu": {
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.59.0.tgz",
+ "integrity": "sha512-laBkYlSS1n2L8fSo1thDNGrCTQMmxjYY5G0WFWjFFYZkKPjsMBsgJfGf4TLxXrF6RyhI60L8TMOjBMvXiTcxeA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "peer": true
+ },
+ "node_modules/netlify-cli/node_modules/@rollup/rollup-win32-x64-msvc": {
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.59.0.tgz",
+ "integrity": "sha512-2HRCml6OztYXyJXAvdDXPKcawukWY2GpR5/nxKp4iBgiO3wcoEGkAaqctIbZcNB6KlUQBIqt8VYkNSj2397EfA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "peer": true
+ },
"node_modules/netlify-cli/node_modules/@sec-ant/readable-stream": {
"version": "0.4.1",
"dev": true,
@@ -17283,6 +17662,19 @@
"safe-buffer": "~5.1.0"
}
},
+ "node_modules/netlify-cli/node_modules/fsevents": {
+ "version": "2.3.3",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "peer": true,
+ "engines": {
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+ }
+ },
"node_modules/netlify-cli/node_modules/function-bind": {
"version": "1.1.2",
"dev": true,
@@ -21065,6 +21457,53 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/netlify-cli/node_modules/rollup": {
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.59.0.tgz",
+ "integrity": "sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "peer": true,
+ "dependencies": {
+ "@types/estree": "1.0.8"
+ },
+ "bin": {
+ "rollup": "dist/bin/rollup"
+ },
+ "engines": {
+ "node": ">=18.0.0",
+ "npm": ">=8.0.0"
+ },
+ "optionalDependencies": {
+ "@rollup/rollup-android-arm-eabi": "4.59.0",
+ "@rollup/rollup-android-arm64": "4.59.0",
+ "@rollup/rollup-darwin-arm64": "4.59.0",
+ "@rollup/rollup-darwin-x64": "4.59.0",
+ "@rollup/rollup-freebsd-arm64": "4.59.0",
+ "@rollup/rollup-freebsd-x64": "4.59.0",
+ "@rollup/rollup-linux-arm-gnueabihf": "4.59.0",
+ "@rollup/rollup-linux-arm-musleabihf": "4.59.0",
+ "@rollup/rollup-linux-arm64-gnu": "4.59.0",
+ "@rollup/rollup-linux-arm64-musl": "4.59.0",
+ "@rollup/rollup-linux-loong64-gnu": "4.59.0",
+ "@rollup/rollup-linux-loong64-musl": "4.59.0",
+ "@rollup/rollup-linux-ppc64-gnu": "4.59.0",
+ "@rollup/rollup-linux-ppc64-musl": "4.59.0",
+ "@rollup/rollup-linux-riscv64-gnu": "4.59.0",
+ "@rollup/rollup-linux-riscv64-musl": "4.59.0",
+ "@rollup/rollup-linux-s390x-gnu": "4.59.0",
+ "@rollup/rollup-linux-x64-gnu": "4.59.0",
+ "@rollup/rollup-linux-x64-musl": "4.59.0",
+ "@rollup/rollup-openbsd-x64": "4.59.0",
+ "@rollup/rollup-openharmony-arm64": "4.59.0",
+ "@rollup/rollup-win32-arm64-msvc": "4.59.0",
+ "@rollup/rollup-win32-ia32-msvc": "4.59.0",
+ "@rollup/rollup-win32-x64-gnu": "4.59.0",
+ "@rollup/rollup-win32-x64-msvc": "4.59.0",
+ "fsevents": "~2.3.2"
+ }
+ },
"node_modules/netlify-cli/node_modules/router": {
"version": "2.2.0",
"dev": true,
@@ -25556,6 +25995,14 @@
"url": "https://github.com/sponsors/colinhacks"
}
},
+ "packages/adapters/aws-lambda": {
+ "name": "@marko/run-adapter-aws-lambda",
+ "version": "0.1.0",
+ "license": "MIT",
+ "peerDependencies": {
+ "@marko/run": "^0.7||^0.8||^0.9||^0.10"
+ }
+ },
"packages/adapters/netlify": {
"name": "@marko/run-adapter-netlify",
"version": "3.0.3",
diff --git a/packages/adapters/aws-lambda/README.md b/packages/adapters/aws-lambda/README.md
new file mode 100644
index 00000000..c2a516d1
--- /dev/null
+++ b/packages/adapters/aws-lambda/README.md
@@ -0,0 +1,51 @@
+
+
+
+
+
+
+
+
+ @marko/run-adapter-aws-lambda
+
+
+
+Preview and deploy [@marko/run](../../run/README.md) apps to AWS Lambda
+
+## Installation
+
+```sh
+npm install @marko/run-adapter-aws-lambda
+```
+
+That's all the setup required — Marko Run automatically discovers an installed adapter and uses it, so you **don't** need to register it in your Vite config.
+
+`marko-run build` produces a self-contained Lambda handler at `dist/index.mjs` (exported as `handler`) plus the static assets in `dist/public`. The handler targets the [API Gateway HTTP API / Lambda Function URL payload format version 2.0](https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-develop-integrations-lambda.html), and serves the bundled static assets when no route matches.
+
+## Deploying
+
+Package the `dist` directory and deploy it as a Lambda function, using `index.handler` as the handler and a Node.js runtime (eg. `nodejs20.x`). Wire it up to a [Function URL](https://docs.aws.amazon.com/lambda/latest/dg/lambda-urls.html) or an API Gateway HTTP API. For an existing function, with the [AWS CLI](https://docs.aws.amazon.com/cli/):
+
+```sh
+npm run build
+( cd dist && zip -r ../function.zip . )
+aws lambda update-function-code --function-name my-app --zip-file fileb://function.zip
+```
+
+Any IaC tool works too (AWS SAM, CDK, Serverless Framework, Terraform) — point the function's handler at `index.handler` and its code at the `dist` directory.
+
+`marko-run preview` runs the handler behind a local HTTP server so you can test it without deploying.
+
+## Platform info
+
+The raw Lambda event and invocation context are available on the `platform` argument of your route handlers:
+
+```ts
+import type { AWSLambdaPlatformInfo } from "@marko/run-adapter-aws-lambda";
+
+export const GET = (context) => {
+ const { event, context: lambdaContext } =
+ context.platform as AWSLambdaPlatformInfo;
+ return new Response(`Request id: ${lambdaContext.awsRequestId}`);
+};
+```
diff --git a/packages/adapters/aws-lambda/package.json b/packages/adapters/aws-lambda/package.json
new file mode 100644
index 00000000..5795f394
--- /dev/null
+++ b/packages/adapters/aws-lambda/package.json
@@ -0,0 +1,33 @@
+{
+ "name": "@marko/run-adapter-aws-lambda",
+ "version": "0.1.0",
+ "description": "Preview and deploy @marko/run apps to AWS Lambda (Function URLs / API Gateway HTTP API)",
+ "keywords": [
+ "marko"
+ ],
+ "homepage": "https://github.com/marko-js/run/tree/main/packages/adapters/aws-lambda",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/marko-js/run.git"
+ },
+ "license": "MIT",
+ "author": "Ryan Turnquist ",
+ "type": "module",
+ "exports": {
+ ".": "./src/index.ts"
+ },
+ "types": "./src/index.ts",
+ "files": [
+ "dist"
+ ],
+ "scripts": {
+ "build": "tsc -b && tsx scripts/build.ts",
+ "test": ""
+ },
+ "peerDependencies": {
+ "@marko/run": "^0.7||^0.8||^0.9||^0.10"
+ },
+ "logo": {
+ "url": "https://github.com/marko-js/run/raw/main/assets/marko-run.png"
+ }
+}
diff --git a/packages/adapters/aws-lambda/package.toggle.json b/packages/adapters/aws-lambda/package.toggle.json
new file mode 100644
index 00000000..a2f5767a
--- /dev/null
+++ b/packages/adapters/aws-lambda/package.toggle.json
@@ -0,0 +1,15 @@
+{
+ "exports": {
+ ".": {
+ "types": "./dist/index.d.ts",
+ "import": "./dist/index.js",
+ "require": "./dist/index.cjs"
+ }
+ },
+ "types": "./dist/index.d.ts",
+ "typesVersions": {
+ "*": {
+ "*": ["./dist/index.d.ts"]
+ }
+ }
+}
diff --git a/packages/adapters/aws-lambda/scripts/build.ts b/packages/adapters/aws-lambda/scripts/build.ts
new file mode 100644
index 00000000..d70f08ec
--- /dev/null
+++ b/packages/adapters/aws-lambda/scripts/build.ts
@@ -0,0 +1,51 @@
+import { build, BuildOptions } from "esbuild";
+import path from "path";
+
+const srcdir = path.resolve("src");
+const outdir = path.resolve("dist");
+
+const opts: BuildOptions = {
+ entryPoints: ["src/index.ts"],
+ outdir,
+ outbase: srcdir,
+ platform: "node",
+ target: ["node14"],
+ bundle: true,
+ plugins: [
+ {
+ name: "external-modules",
+ setup(build) {
+ build.onResolve(
+ { filter: /^[^./]|^\.[^./]|^\.\.[^/]/ },
+ ({ path }) => ({
+ path,
+ external: true,
+ }),
+ );
+ },
+ },
+ ],
+};
+
+await Promise.all([
+ build({
+ ...opts,
+ format: "cjs",
+ outExtension: { ".js": ".cjs" },
+ define: {
+ "import.meta.url": "__importMetaURL",
+ },
+ inject: ["./scripts/importMetaURL.js"],
+ }),
+ build({
+ ...opts,
+ format: "esm",
+ splitting: true,
+ }),
+ build({
+ ...opts,
+ entryPoints: ["src/default-entry.ts"],
+ format: "esm",
+ outExtension: { ".js": ".mjs" },
+ }),
+]);
diff --git a/packages/adapters/aws-lambda/scripts/importMetaURL.js b/packages/adapters/aws-lambda/scripts/importMetaURL.js
new file mode 100644
index 00000000..b1277af3
--- /dev/null
+++ b/packages/adapters/aws-lambda/scripts/importMetaURL.js
@@ -0,0 +1,2 @@
+import { pathToFileURL } from "url";
+export const __importMetaURL = pathToFileURL(__filename).href;
diff --git a/packages/adapters/aws-lambda/src/default-entry.ts b/packages/adapters/aws-lambda/src/default-entry.ts
new file mode 100644
index 00000000..68d18611
--- /dev/null
+++ b/packages/adapters/aws-lambda/src/default-entry.ts
@@ -0,0 +1,156 @@
+import { readFile } from "node:fs/promises";
+import { fileURLToPath } from "node:url";
+
+import { fetch } from "@marko/run/router";
+
+import type {
+ APIGatewayProxyEventV2,
+ APIGatewayProxyResultV2,
+ AWSLambdaPlatformInfo,
+ LambdaContext,
+} from "./types";
+
+const publicDir = new URL("./public/", import.meta.url);
+
+const mimeTypes: Record = {
+ html: "text/html; charset=utf-8",
+ js: "text/javascript; charset=utf-8",
+ mjs: "text/javascript; charset=utf-8",
+ css: "text/css; charset=utf-8",
+ json: "application/json; charset=utf-8",
+ map: "application/json; charset=utf-8",
+ svg: "image/svg+xml",
+ png: "image/png",
+ jpg: "image/jpeg",
+ jpeg: "image/jpeg",
+ gif: "image/gif",
+ webp: "image/webp",
+ avif: "image/avif",
+ ico: "image/x-icon",
+ woff: "font/woff",
+ woff2: "font/woff2",
+ ttf: "font/ttf",
+ otf: "font/otf",
+ wasm: "application/wasm",
+ txt: "text/plain; charset=utf-8",
+ xml: "application/xml",
+};
+
+function eventToRequest(event: APIGatewayProxyEventV2): Request {
+ const { method } = event.requestContext.http;
+ const headers = new Headers();
+ for (const [key, value] of Object.entries(event.headers)) {
+ if (value !== undefined) {
+ headers.set(key, value);
+ }
+ }
+ if (event.cookies?.length) {
+ headers.set("cookie", event.cookies.join("; "));
+ }
+
+ const host =
+ headers.get("x-forwarded-host") ||
+ headers.get("host") ||
+ event.requestContext.domainName;
+ const proto = headers.get("x-forwarded-proto") || "https";
+ const query = event.rawQueryString ? `?${event.rawQueryString}` : "";
+ const url = `${proto}://${host}${event.rawPath}${query}`;
+
+ let body: Uint8Array | string | undefined;
+ if (method !== "GET" && method !== "HEAD" && event.body != null) {
+ body = event.isBase64Encoded
+ ? Buffer.from(event.body, "base64")
+ : event.body;
+ }
+
+ return new Request(url, { method, headers, body });
+}
+
+async function responseToResult(
+ response: Response,
+): Promise {
+ const headers: Record = {};
+ response.headers.forEach((value, key) => {
+ if (key !== "set-cookie") {
+ headers[key] = value;
+ }
+ });
+ const cookies =
+ "getSetCookie" in response.headers
+ ? response.headers.getSetCookie()
+ : undefined;
+ const body = Buffer.from(await response.arrayBuffer());
+
+ return {
+ statusCode: response.status,
+ headers,
+ cookies: cookies?.length ? cookies : undefined,
+ body: body.toString("base64"),
+ isBase64Encoded: true,
+ };
+}
+
+async function serveStatic(
+ pathname: string,
+): Promise {
+ if (pathname.endsWith("/")) {
+ pathname += "index.html";
+ }
+
+ const fileUrl = new URL("." + pathname, publicDir);
+ // Guard against path traversal outside of the public directory.
+ if (!fileUrl.href.startsWith(publicDir.href)) {
+ return null;
+ }
+
+ let data: Buffer;
+ try {
+ data = await readFile(fileURLToPath(fileUrl));
+ } catch {
+ return null;
+ }
+
+ const ext = pathname.slice(pathname.lastIndexOf(".") + 1).toLowerCase();
+ const headers: Record = {
+ "content-type": mimeTypes[ext] || "application/octet-stream",
+ };
+ if (pathname.startsWith("/assets/")) {
+ headers["cache-control"] = "public, max-age=31536000, immutable";
+ }
+
+ return {
+ statusCode: 200,
+ headers,
+ body: data.toString("base64"),
+ isBase64Encoded: true,
+ };
+}
+
+export async function handler(
+ event: APIGatewayProxyEventV2,
+ context: LambdaContext,
+): Promise {
+ const request = eventToRequest(event);
+ const platform: AWSLambdaPlatformInfo = { event, context };
+
+ // Let matched routes respond first; only fall back to static assets when the
+ // router reports no match (the runtime returns a 404 response in that case).
+ const response = await fetch(request, platform);
+ if (response && response.status !== 404) {
+ return responseToResult(response);
+ }
+
+ const { pathname } = new URL(request.url);
+ let decodedPathname: string;
+ try {
+ decodedPathname = decodeURIComponent(pathname);
+ } catch {
+ return { statusCode: 400, body: "" };
+ }
+ const staticResult = await serveStatic(decodedPathname);
+ if (staticResult) {
+ return staticResult;
+ }
+
+ return response ? responseToResult(response) : { statusCode: 404, body: "" };
+}
diff --git a/packages/adapters/aws-lambda/src/index.ts b/packages/adapters/aws-lambda/src/index.ts
new file mode 100644
index 00000000..6d0944db
--- /dev/null
+++ b/packages/adapters/aws-lambda/src/index.ts
@@ -0,0 +1,188 @@
+import { createServer, type IncomingMessage } from "node:http";
+import type { AddressInfo } from "node:net";
+import path from "node:path";
+import { fileURLToPath, pathToFileURL } from "node:url";
+
+import baseAdapter, { type Adapter } from "@marko/run/adapter";
+import { loadEnv } from "@marko/run/vite";
+
+import type {
+ APIGatewayProxyEventV2,
+ APIGatewayProxyResultV2,
+ LambdaContext,
+} from "./types";
+
+export type {
+ APIGatewayProxyEventV2,
+ APIGatewayProxyResultV2,
+ AWSLambdaPlatformInfo,
+ LambdaContext,
+} from "./types";
+
+const __dirname = fileURLToPath(path.dirname(import.meta.url));
+const defaultEntry = path.join(__dirname, "default-entry");
+
+type Handler = (
+ event: APIGatewayProxyEventV2,
+ context: LambdaContext,
+) => Promise;
+
+export default function awsLambdaAdapter(): Adapter {
+ const { startDev } = baseAdapter();
+
+ return {
+ name: "aws-lambda-adapter",
+
+ viteConfig(config) {
+ if (config.build?.ssr) {
+ return {
+ ssr: {
+ target: "node",
+ // Bundle everything so the Lambda deployment package is
+ // self-contained and needs no `node_modules`.
+ noExternal: true,
+ },
+ };
+ }
+ },
+
+ getEntryFile() {
+ return defaultEntry;
+ },
+
+ startDev(event) {
+ return startDev!({
+ ...event,
+ entry: event.entry === defaultEntry ? undefined : event.entry,
+ });
+ },
+
+ async startPreview({ entry, options }) {
+ const { port = 3000, dir, envFile } = options;
+ const entryFile = entry || path.join(dir, "index.mjs");
+
+ if (envFile) {
+ await loadEnv(envFile);
+ }
+
+ // Invoke the built Lambda handler behind a local HTTP server so the app
+ // can be previewed without deploying (or running SAM).
+ const { handler } = (await import(pathToFileURL(entryFile).href)) as {
+ handler: Handler;
+ };
+
+ const server = createServer((req, res) => {
+ readBody(req)
+ .then(async (body) => {
+ const result = await handler(nodeRequestToEvent(req, body), {
+ functionName: "marko-run-preview",
+ functionVersion: "$LATEST",
+ invokedFunctionArn: "",
+ memoryLimitInMB: "128",
+ awsRequestId: "preview",
+ logGroupName: "",
+ logStreamName: "",
+ getRemainingTimeInMillis: () => 30000,
+ });
+
+ res.statusCode = result.statusCode;
+ for (const [key, value] of Object.entries(result.headers || {})) {
+ res.setHeader(key, value);
+ }
+ if (result.cookies?.length) {
+ res.setHeader("set-cookie", result.cookies);
+ }
+ res.end(
+ result.body
+ ? Buffer.from(
+ result.body,
+ result.isBase64Encoded ? "base64" : "utf8",
+ )
+ : undefined,
+ );
+ })
+ .catch((error) => {
+ res.statusCode = 500;
+ res.end(String(error));
+ });
+ });
+
+ return new Promise((resolve) => {
+ const listener = server.listen(port, () => {
+ const address = listener.address() as AddressInfo;
+ console.log(
+ `Preview server started: http://localhost:${address.port}`,
+ );
+ resolve({
+ port: address.port,
+ close() {
+ listener.close();
+ },
+ });
+ });
+ });
+ },
+
+ typeInfo(writer) {
+ writer(
+ `import type { AWSLambdaPlatformInfo } from '@marko/run-adapter-aws-lambda';`,
+ );
+ return "AWSLambdaPlatformInfo";
+ },
+ };
+}
+
+function readBody(req: IncomingMessage): Promise {
+ return new Promise((resolve, reject) => {
+ const chunks: Buffer[] = [];
+ req.on("data", (chunk) => chunks.push(chunk));
+ req.on("end", () => resolve(Buffer.concat(chunks)));
+ req.on("error", reject);
+ });
+}
+
+function nodeRequestToEvent(
+ req: IncomingMessage,
+ body: Buffer,
+): APIGatewayProxyEventV2 {
+ const url = new URL(req.url || "/", "http://localhost");
+ const headers: Record = {};
+ for (const [key, value] of Object.entries(req.headers)) {
+ if (value !== undefined) {
+ headers[key] = Array.isArray(value) ? value.join(", ") : value;
+ }
+ }
+ // The preview server is plain HTTP; make sure the built request uses it too.
+ headers["x-forwarded-proto"] = "http";
+ const cookies = req.headers.cookie
+ ? req.headers.cookie.split(/;\s*/).filter(Boolean)
+ : undefined;
+
+ return {
+ version: "2.0",
+ routeKey: "$default",
+ rawPath: url.pathname,
+ rawQueryString: url.search.slice(1),
+ cookies,
+ headers,
+ requestContext: {
+ accountId: "preview",
+ apiId: "preview",
+ domainName: headers.host || "localhost",
+ domainPrefix: "preview",
+ http: {
+ method: req.method || "GET",
+ path: url.pathname,
+ protocol: "HTTP/1.1",
+ sourceIp: req.socket.remoteAddress || "127.0.0.1",
+ userAgent: headers["user-agent"] || "",
+ },
+ requestId: "preview",
+ stage: "$default",
+ time: "",
+ timeEpoch: 0,
+ },
+ body: body.length ? body.toString("base64") : undefined,
+ isBase64Encoded: true,
+ };
+}
diff --git a/packages/adapters/aws-lambda/src/types.ts b/packages/adapters/aws-lambda/src/types.ts
new file mode 100644
index 00000000..b3237624
--- /dev/null
+++ b/packages/adapters/aws-lambda/src/types.ts
@@ -0,0 +1,67 @@
+/**
+ * A subset of the AWS API Gateway HTTP API / Lambda Function URL request event
+ * (payload format version 2.0).
+ */
+export interface APIGatewayProxyEventV2 {
+ version: string;
+ routeKey: string;
+ rawPath: string;
+ rawQueryString: string;
+ cookies?: string[];
+ headers: Record;
+ queryStringParameters?: Record;
+ requestContext: {
+ accountId: string;
+ apiId: string;
+ domainName: string;
+ domainPrefix: string;
+ http: {
+ method: string;
+ path: string;
+ protocol: string;
+ sourceIp: string;
+ userAgent: string;
+ };
+ requestId: string;
+ stage: string;
+ time: string;
+ timeEpoch: number;
+ };
+ body?: string;
+ pathParameters?: Record;
+ isBase64Encoded: boolean;
+ stageVariables?: Record;
+}
+
+/**
+ * A structured Lambda response for the API Gateway HTTP API / Function URL
+ * payload format version 2.0.
+ */
+export interface APIGatewayProxyResultV2 {
+ statusCode: number;
+ headers?: Record;
+ cookies?: string[];
+ body?: string;
+ isBase64Encoded?: boolean;
+}
+
+/** The AWS Lambda invocation context. */
+export interface LambdaContext {
+ functionName: string;
+ functionVersion: string;
+ invokedFunctionArn: string;
+ memoryLimitInMB: string;
+ awsRequestId: string;
+ logGroupName: string;
+ logStreamName: string;
+ getRemainingTimeInMillis(): number;
+}
+
+/**
+ * The platform object passed to Marko Run route handlers when deployed to AWS
+ * Lambda. Exposes the raw Lambda event and invocation context.
+ */
+export interface AWSLambdaPlatformInfo {
+ event: APIGatewayProxyEventV2;
+ context: LambdaContext;
+}
diff --git a/packages/adapters/aws-lambda/tsconfig.json b/packages/adapters/aws-lambda/tsconfig.json
new file mode 100644
index 00000000..cd4f0966
--- /dev/null
+++ b/packages/adapters/aws-lambda/tsconfig.json
@@ -0,0 +1,10 @@
+{
+ "extends": "../../../tsconfig.json",
+ "include": ["src/**/*"],
+ "exclude": ["src/**/__tests__/**/*", "src/default-entry.ts"],
+ "compilerOptions": {
+ "rootDir": "src",
+ "outDir": "dist",
+ "tsBuildInfoFile": "dist/.tsbuildinfo"
+ }
+}
diff --git a/packages/run/src/__tests__/fixtures/aws-lambda-adapter/.gitignore b/packages/run/src/__tests__/fixtures/aws-lambda-adapter/.gitignore
new file mode 100644
index 00000000..1521c8b7
--- /dev/null
+++ b/packages/run/src/__tests__/fixtures/aws-lambda-adapter/.gitignore
@@ -0,0 +1 @@
+dist
diff --git a/packages/run/src/__tests__/fixtures/aws-lambda-adapter/.marko-run/routes.d.ts b/packages/run/src/__tests__/fixtures/aws-lambda-adapter/.marko-run/routes.d.ts
new file mode 100644
index 00000000..ace8419e
--- /dev/null
+++ b/packages/run/src/__tests__/fixtures/aws-lambda-adapter/.marko-run/routes.d.ts
@@ -0,0 +1,36 @@
+/*
+ WARNING: This file is automatically generated and any changes made to it will be overwritten without warning.
+ Do NOT manually edit this file or your changes will be lost.
+*/
+
+import { NotHandled, NotMatched, GetPaths, PostPaths, GetablePath, GetableHref, PostablePath, PostableHref, Platform } from "@marko/run/namespace";
+import type * as Run from "@marko/run";
+import type { AWSLambdaPlatformInfo } from '@marko/run-adapter-aws-lambda';
+
+declare module "@marko/run" {
+ interface Platform extends AWSLambdaPlatformInfo {}
+
+ interface AppData extends Run.DefineApp<{
+ routes: {
+ "/": { verb: "get"; };
+ }
+ }> {}
+}
+
+declare module "../src/routes/+page.marko" {
+ namespace MarkoRun {
+ export { NotHandled, NotMatched, GetPaths, PostPaths, GetablePath, GetableHref, PostablePath, PostableHref, Platform };
+ export type Route = Run.Routes["/"];
+ export type Context = Run.MultiRouteContext & Marko.Global;
+ export type Handler = Run.HandlerLike;
+ export type GET = Run.HandlerLike;
+ export type HEAD = Run.HandlerLike;
+ export type POST = Run.HandlerLike;
+ export type PUT = Run.HandlerLike;
+ export type DELETE = Run.HandlerLike;
+ export type PATCH = Run.HandlerLike;
+ export type OPTIONS = Run.HandlerLike;
+ /** @deprecated use `((context, next) => { ... }) satisfies MarkoRun.Handler` instead */
+ export const route: Run.HandlerTypeFn;
+ }
+}
diff --git a/packages/run/src/__tests__/fixtures/aws-lambda-adapter/__snapshots__/dev.expected.md b/packages/run/src/__tests__/fixtures/aws-lambda-adapter/__snapshots__/dev.expected.md
new file mode 100644
index 00000000..1ec2ae91
--- /dev/null
+++ b/packages/run/src/__tests__/fixtures/aws-lambda-adapter/__snapshots__/dev.expected.md
@@ -0,0 +1,35 @@
+# Loading
+
+```html
+
+```
+
+# Step 0
+ctx=>click(ctx)
+
+```diff
+- 0
++ 1
+
+```
+
+# Step 1
+ctx=>click(ctx)
+
+```diff
+- 1
++ 2
+
+```
+
+# Step 2
+ctx=>click(ctx)
+
+```diff
+- 2
++ 3
+
+```
+
diff --git a/packages/run/src/__tests__/fixtures/aws-lambda-adapter/__snapshots__/preview.expected.md b/packages/run/src/__tests__/fixtures/aws-lambda-adapter/__snapshots__/preview.expected.md
new file mode 100644
index 00000000..1ec2ae91
--- /dev/null
+++ b/packages/run/src/__tests__/fixtures/aws-lambda-adapter/__snapshots__/preview.expected.md
@@ -0,0 +1,35 @@
+# Loading
+
+```html
+
+```
+
+# Step 0
+ctx=>click(ctx)
+
+```diff
+- 0
++ 1
+
+```
+
+# Step 1
+ctx=>click(ctx)
+
+```diff
+- 1
++ 2
+
+```
+
+# Step 2
+ctx=>click(ctx)
+
+```diff
+- 2
++ 3
+
+```
+
diff --git a/packages/run/src/__tests__/fixtures/aws-lambda-adapter/package.json b/packages/run/src/__tests__/fixtures/aws-lambda-adapter/package.json
new file mode 100644
index 00000000..428caf23
--- /dev/null
+++ b/packages/run/src/__tests__/fixtures/aws-lambda-adapter/package.json
@@ -0,0 +1,5 @@
+{
+ "name": "aws-lambda-adapter",
+ "version": "1.0.0",
+ "scripts": {}
+}
diff --git a/packages/run/src/__tests__/fixtures/aws-lambda-adapter/src/components/counter.marko b/packages/run/src/__tests__/fixtures/aws-lambda-adapter/src/components/counter.marko
new file mode 100644
index 00000000..436a9eb5
--- /dev/null
+++ b/packages/run/src/__tests__/fixtures/aws-lambda-adapter/src/components/counter.marko
@@ -0,0 +1,11 @@
+class {
+ declare state: { count: number };
+ onCreate() {
+ this.state = { count: 0 };
+ }
+ handleIncrement() {
+ this.state.count++;
+ }
+}
+
+
\ No newline at end of file
diff --git a/packages/run/src/__tests__/fixtures/aws-lambda-adapter/src/routes/+page.marko b/packages/run/src/__tests__/fixtures/aws-lambda-adapter/src/routes/+page.marko
new file mode 100644
index 00000000..5dd8370a
--- /dev/null
+++ b/packages/run/src/__tests__/fixtures/aws-lambda-adapter/src/routes/+page.marko
@@ -0,0 +1,12 @@
+
+
+
+
+ @marko/run Test Fixture
+
+
+
+
+
+
+
diff --git a/packages/run/src/__tests__/fixtures/aws-lambda-adapter/test.config.ts b/packages/run/src/__tests__/fixtures/aws-lambda-adapter/test.config.ts
new file mode 100644
index 00000000..2b2c7170
--- /dev/null
+++ b/packages/run/src/__tests__/fixtures/aws-lambda-adapter/test.config.ts
@@ -0,0 +1,11 @@
+import { Step, StepContext } from "../../main.test";
+
+export const steps: Step[] = [
+ (ctx) => click(ctx),
+ (ctx) => click(ctx),
+ (ctx) => click(ctx),
+];
+
+async function click({ page }: StepContext) {
+ await page.click("button");
+}
diff --git a/packages/run/src/__tests__/fixtures/aws-lambda-adapter/tsconfig.json b/packages/run/src/__tests__/fixtures/aws-lambda-adapter/tsconfig.json
new file mode 100644
index 00000000..03ff88a5
--- /dev/null
+++ b/packages/run/src/__tests__/fixtures/aws-lambda-adapter/tsconfig.json
@@ -0,0 +1,4 @@
+{
+ "extends": "../../tsconfig-base.json",
+ "include": ["src/**/*", ".marko-run/*"],
+}
\ No newline at end of file
diff --git a/packages/run/src/__tests__/fixtures/aws-lambda-adapter/vite.config.ts b/packages/run/src/__tests__/fixtures/aws-lambda-adapter/vite.config.ts
new file mode 100644
index 00000000..2fb0822c
--- /dev/null
+++ b/packages/run/src/__tests__/fixtures/aws-lambda-adapter/vite.config.ts
@@ -0,0 +1,10 @@
+import marko from "@marko/run/vite";
+import adapter from "@marko/run-adapter-aws-lambda";
+import { defineConfig } from "vite";
+
+export default defineConfig({
+ plugins: [marko({ adapter: adapter() })],
+ server: {
+ hmr: false,
+ },
+});