sveltekit-adapter-middy 0.0.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 will Farrell
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,91 @@
1
+ <div align="center">
2
+ <h1>Middy SvelteKit Adapter</h1>
3
+ <img alt="Middy logo" src="https://raw.githubusercontent.com/middyjs/middy/main/docs/img/middy-logo.svg"/>
4
+ <p><strong>SvelteKit adapter for AWS Lambda using Middy</strong></p>
5
+ <p>
6
+ <a href="https://www.npmjs.com/package/@middy/http-router?activeTab=versions">
7
+ <img src="https://badge.fury.io/js/%40middy%2Fhttp-router.svg" alt="npm version" style="max-width:100%;">
8
+ </a>
9
+ <a href="https://packagephobia.com/result?p=@middy/http-router">
10
+ <img src="https://packagephobia.com/badge?p=@middy/http-router" alt="npm install size" style="max-width:100%;">
11
+ </a>
12
+ <a href="https://github.com/middyjs/middy/actions/workflows/tests.yml">
13
+ <img src="https://github.com/middyjs/middy/actions/workflows/tests.yml/badge.svg?branch=main&event=push" alt="GitHub Actions CI status badge" style="max-width:100%;">
14
+ </a>
15
+ <br/>
16
+ <a href="https://standardjs.com/">
17
+ <img src="https://img.shields.io/badge/code_style-standard-brightgreen.svg" alt="Standard Code Style" style="max-width:100%;">
18
+ </a>
19
+ <a href="https://snyk.io/test/github/middyjs/middy">
20
+ <img src="https://snyk.io/test/github/middyjs/middy/badge.svg" alt="Known Vulnerabilities" data-canonical-src="https://snyk.io/test/github/middyjs/middy" style="max-width:100%;">
21
+ </a>
22
+ <a href="https://github.com/middyjs/middy/actions/workflows/sast.yml">
23
+ <img src="https://github.com/middyjs/middy/actions/workflows/sast.yml/badge.svg
24
+ ?branch=main&event=push" alt="CodeQL" style="max-width:100%;">
25
+ </a>
26
+ <a href="https://bestpractices.coreinfrastructure.org/projects/5280">
27
+ <img src="https://bestpractices.coreinfrastructure.org/projects/5280/badge" alt="Core Infrastructure Initiative (CII) Best Practices" style="max-width:100%;">
28
+ </a>
29
+ <br/>
30
+ <a href="https://gitter.im/middyjs/Lobby">
31
+ <img src="https://badges.gitter.im/gitterHQ/gitter.svg" alt="Chat on Gitter" style="max-width:100%;">
32
+ </a>
33
+ <a href="https://stackoverflow.com/questions/tagged/middy?sort=Newest&uqlId=35052">
34
+ <img src="https://img.shields.io/badge/StackOverflow-[middy]-yellow" alt="Ask questions on StackOverflow" style="max-width:100%;">
35
+ </a>
36
+ </p>
37
+ <!--<p>You can read the documentation at: <a href="https://middy.js.org/docs/ssr/sveltekit">https://middy.js.org/docs/ssr/sveltekit</a></p>-->
38
+ </div>
39
+
40
+ Creates a lambda that supports a Function URL with streaming responses.
41
+
42
+ ## Features
43
+
44
+ - Response Stream
45
+ - Extendable with Middy middlewares:
46
+ - `http-content-encoding`
47
+ - `http-security-headers`
48
+ - `ssm`/`secrets-manger`
49
+ - Removes `x-sveltekit-page` headers
50
+
51
+ Note: Bring your own deployment.
52
+
53
+ ## Getting started
54
+
55
+ ```js
56
+ import adapter from 'sveltekit-adapter-middy'
57
+
58
+ export default {
59
+ kit: {
60
+ adapter: adapter({
61
+ // options
62
+ })
63
+ }
64
+ }
65
+ ```
66
+
67
+ ### Options
68
+
69
+ - `handlerPath` (string): Relative path to handler override file. Overriding allows you to add in Content-Encoding, Security Headers, and pass in secrets more securely. Defaults to build-in minimalist handler.
70
+ - `out` (string): Relative path to build dir. Defaults to `build`
71
+ - `esbuildOptions` (object): `esbuild` option overrides. See [code]() for defaults.
72
+
73
+ ## Recommended Infrastructure
74
+
75
+ - CloudFront: Route to static assets / pages, with fallback to server side rendering
76
+ - S3: store static assets and pages
77
+ - Lambda Function URL: server side rendering
78
+
79
+ ## Roadmap
80
+
81
+ - infra diagram
82
+ - cli to sync static assets to S3 w/ headers
83
+ - LLRT
84
+
85
+ ## License
86
+
87
+ Licensed under [MIT License](LICENSE). Copyright (c) 2017-2024 [Luciano Mammino](https://github.com/lmammino), [will Farrell](https://github.com/willfarrell), and the [Middy team](https://github.com/middyjs/middy/graphs/contributors).
88
+
89
+ <a href="https://app.fossa.io/projects/git%2Bgithub.com%2Fmiddyjs%2Fmiddy?ref=badge_large">
90
+ <img src="https://app.fossa.io/api/projects/git%2Bgithub.com%2Fmiddyjs%2Fmiddy.svg?type=large" alt="FOSSA Status" style="max-width:100%;">
91
+ </a>
package/handler.js ADDED
@@ -0,0 +1,11 @@
1
+ import middy from '@middy/core'
2
+
3
+ import sveltekitMiddleware from './sveltekitMiddleware.js'
4
+ import sveltekitHandler from './sveltekitHandler.js'
5
+
6
+ export const handler = middy({ streamifyResponse: true })
7
+ .use([
8
+ // other middleware here
9
+ sveltekitMiddleware()
10
+ ])
11
+ .handler(sveltekitHandler)
package/index.js ADDED
@@ -0,0 +1,98 @@
1
+ import { readFileSync, writeFileSync } from 'node:fs'
2
+ import { fileURLToPath } from 'node:url'
3
+ import esbuild from 'esbuild'
4
+
5
+ const name = '@middy/sveltekit'
6
+ const files = fileURLToPath(new URL('./', import.meta.url).href)
7
+
8
+ const sveltekitAdapterMiddy = (opts = {}) => {
9
+ const {
10
+ out = 'build',
11
+ handlerPath = `${files}/handler.js`,
12
+ esbuildOptions = {}
13
+ } = opts
14
+
15
+ return {
16
+ name,
17
+ async adapt(builder) {
18
+ const tmp = builder.getBuildDirectory('adapter-middy')
19
+
20
+ builder.rimraf(out)
21
+ builder.rimraf(tmp)
22
+
23
+ builder.log.minor('Copying static assets')
24
+ const clientFiles = await builder.writeClient(
25
+ `${out}/assets${builder.config.kit.paths.base}`
26
+ )
27
+
28
+ builder.log.minor('Building server')
29
+ await builder.writeServer(tmp)
30
+ writeFileSync(
31
+ `${tmp}/manifest.js`,
32
+ [
33
+ `export const manifest = ${builder.generateManifest({
34
+ relativePath: './'
35
+ })};`,
36
+ `export const prerendered = new Set(${JSON.stringify(
37
+ builder.prerendered.paths
38
+ )});`,
39
+ `export const base = ${JSON.stringify(
40
+ builder.config.kit.paths.base
41
+ )};`
42
+ ].join('\n\n')
43
+ )
44
+
45
+ builder.copy(
46
+ `${files}/sveltekitMiddleware.js`,
47
+ `${tmp}/sveltekitMiddleware.js`
48
+ )
49
+ builder.copy(`${files}/sveltekitHandler.js`, `${tmp}/sveltekitHandler.js`)
50
+ builder.copy(handlerPath, `${tmp}/handler.js`)
51
+
52
+ try {
53
+ const result = await esbuild.build({
54
+ target: 'node20',
55
+ bundle: true,
56
+ platform: 'node',
57
+ format: 'esm',
58
+ treeShaking: true,
59
+ ...esbuildOptions,
60
+ entryPoints: [`${tmp}/handler.js`],
61
+ outfile: `${out}/index.mjs`,
62
+ external: ['node:*', ...(esbuildOptions?.external ?? [])]
63
+ })
64
+
65
+ if (result.warnings.length > 0) {
66
+ const formatted = await esbuild.formatMessages(result.warnings, {
67
+ kind: 'warning',
68
+ color: true
69
+ })
70
+
71
+ console.error(formatted.join('\n'))
72
+ }
73
+ } catch (err) {
74
+ const formatted = await esbuild.formatMessages(err.errors, {
75
+ kind: 'error',
76
+ color: true
77
+ })
78
+
79
+ console.error(formatted.join('\n'))
80
+
81
+ throw new Error(
82
+ `Bundling with esbuild failed with ${err.errors.length} ${
83
+ err.errors.length === 1 ? 'error' : 'errors'
84
+ }`
85
+ )
86
+ }
87
+
88
+ builder.log.minor('Prerendering static pages')
89
+ const prerenderedFiles = await builder.writePrerendered(
90
+ `${out}/prerendered${builder.config.kit.paths.base}`
91
+ )
92
+ },
93
+ supports: {
94
+ read: () => true
95
+ }
96
+ }
97
+ }
98
+ export default sveltekitAdapterMiddy
package/package.json ADDED
@@ -0,0 +1,66 @@
1
+ {
2
+ "name": "sveltekit-adapter-middy",
3
+ "version": "0.0.0-alpha.0",
4
+ "description": "SvelteKit adapter for AWS Lambda with Middy",
5
+ "type": "module",
6
+ "engines": {
7
+ "node": ">=20"
8
+ },
9
+ "engineStrict": true,
10
+ "publishConfig": {
11
+ "access": "public"
12
+ },
13
+ "module": "./index.js",
14
+ "exports": {
15
+ ".": {
16
+ "import": {
17
+ "default": "./index.js"
18
+ }
19
+ }
20
+ },
21
+ "files": [
22
+ "index.js",
23
+ "handler.js",
24
+ "sveltekitMiddleware.js",
25
+ "sveltekitHandler.js"
26
+ ],
27
+ "scripts": {
28
+ "test": "echo test"
29
+ },
30
+ "license": "MIT",
31
+ "keywords": [
32
+ "SvelteKit",
33
+ "Middy",
34
+ "Lambda",
35
+ "Middleware",
36
+ "Serverless",
37
+ "AWS",
38
+ "AWS Lambda",
39
+ "Middy",
40
+ "HTTP",
41
+ "SSR"
42
+ ],
43
+ "author": {
44
+ "name": "contributors",
45
+ "url": "https://github.com/willfarrell/sveltekit-adapter-middy/graphs/contributors"
46
+ },
47
+ "repository": {
48
+ "type": "git",
49
+ "url": "github:willfarrell/sveltekit-adapter-middy",
50
+ "directory": ""
51
+ },
52
+ "bugs": {
53
+ "url": "https://github.com/willfarrell/sveltekit-adapter-middy/issues"
54
+ },
55
+ "homepage": "https://middy.js.org",
56
+ "funding": {
57
+ "type": "github",
58
+ "url": "https://github.com/sponsors/willfarrell"
59
+ },
60
+ "dependencies": {
61
+ "@datastream/string": "0.0.36",
62
+ "@middy/core": "5.4.0",
63
+ "esbuild": "^0.21.0",
64
+ "set-cookie-parser": "2.6.0"
65
+ }
66
+ }
@@ -0,0 +1,66 @@
1
+ import { splitCookiesString } from 'set-cookie-parser'
2
+
3
+ const lambdaHandler = async (event, context, { signal }) => {
4
+ const { server, env } = context
5
+ const { headers, rawQueryString, body: rawBody, isBase64Encoded } = event
6
+ const { method, path } = event.requestContext.http
7
+
8
+ // Copy secrets from ssm into import.meta scope
9
+ Object.assign(import.meta.env, env)
10
+
11
+ // Override origin
12
+ headers.origin = process.env.ORIGIN ?? headers.origin
13
+
14
+ const queryString = rawQueryString ? `?${rawQueryString}` : ''
15
+ const url = `${headers.origin}${path}${queryString}`
16
+
17
+ const encoding = isBase64Encoded
18
+ ? 'base64'
19
+ : headers['content-encoding'] ?? 'utf-8'
20
+ const body =
21
+ typeof rawBody === 'string' ? Buffer.from(rawBody, encoding) : rawBody
22
+
23
+ await init
24
+
25
+ const rendered = await server.respond(
26
+ new Request(url, {
27
+ method,
28
+ headers: new Headers(headers),
29
+ body
30
+ }),
31
+ {
32
+ getClientAddress() {
33
+ console.log('getClientAddress', headers['x-forwarded-for'])
34
+ return headers['x-forwarded-for']
35
+ }
36
+ }
37
+ )
38
+
39
+ if (rendered) {
40
+ const response = {
41
+ statusCode: rendered.status,
42
+ headers: {
43
+ 'cache-control': 'no-cache'
44
+ },
45
+ multiValueHeaders: {},
46
+ body: rendered.body
47
+ }
48
+
49
+ for (const [key, value] of rendered.headers.entries()) {
50
+ if (key === 'set-cookie') {
51
+ response.multiValueHeaders[key] = setCookie.splitCookiesString(value)
52
+ } else if (key === 'x-sveltekit-page') {
53
+ // excluded, security
54
+ } else {
55
+ response.headers[key] = value
56
+ }
57
+ }
58
+
59
+ return response
60
+ }
61
+
62
+ return {
63
+ statusCode: 404
64
+ }
65
+ }
66
+ export default lambdaHandler
@@ -0,0 +1,46 @@
1
+ import { createTransformStream } from '@datastream/core'
2
+ import { Server } from './index.js'
3
+ import { manifest } from './manifest.js'
4
+
5
+ const server = new Server(manifest)
6
+ const init = server.init({ env: process.env })
7
+
8
+ // TODO replace with @datastream/string
9
+ export const stringReplaceStream = (options, streamOptions) => {
10
+ const { pattern, replacement } = options
11
+ let previousChunk = ''
12
+ const transform = (chunk, enqueue) => {
13
+ const newChunk = (previousChunk + chunk).replace(pattern, replacement)
14
+ enqueue(newChunk.substring(0, previousChunk.length))
15
+ previousChunk = newChunk.substring(previousChunk.length)
16
+ }
17
+ const flush = (enqueue) => {
18
+ enqueue(previousChunk)
19
+ }
20
+ return createTransformStream(transform, flush, streamOptions)
21
+ }
22
+
23
+ const formActionPattern = /action="\?\//g
24
+ const formActionReplacement = 'action="?%2F'
25
+ const sveltekitMiddleware = () => {
26
+ const sveltekitMiddlewareBefore = async (request) => {
27
+ request.context.server = server
28
+ await init
29
+ }
30
+
31
+ const sveltekitMiddlewareAfter = async (request) => {
32
+ // Workaround: AWS Function URLs doesn't support querystring keys that contain `/`
33
+ if (request.response['Content-Type'].inlcudes('text/html')) {
34
+ const stream = stringReplaceStream({
35
+ pattern: formActionPattern,
36
+ replacement: formActionReplacement
37
+ })
38
+ request.response.body.pipe(stream)
39
+ }
40
+ }
41
+ return {
42
+ before: sveltekitMiddlewareBefore,
43
+ after: sveltekitMiddlewareAfter
44
+ }
45
+ }
46
+ export default sveltekitMiddleware