diff --git a/example/deploy/package.json b/example/deploy/package.json index 4345f0c..9865b43 100644 --- a/example/deploy/package.json +++ b/example/deploy/package.json @@ -1,7 +1,6 @@ { "private": true, "name": "deploy", - "version": "0.1.14", "type": "module", "bin": { "deploy": "bin/deploy.js" diff --git a/package.json b/package.json index 545d287..2cf5a04 100644 --- a/package.json +++ b/package.json @@ -24,22 +24,17 @@ "watch": "tsc --build --watch" }, "peerDependencies": { - "@apimda/npm-layer-version": "1.x", - "@aws-cdk/aws-apigatewayv2-alpha": "2.x", - "@aws-cdk/aws-apigatewayv2-integrations-alpha": "2.x", - "aws-cdk-lib": "2.x", + "aws-cdk-lib": ">= 2.115.0", "constructs": "10.x" }, "devDependencies": { - "@apimda/npm-layer-version": "^1.0.0", - "@aws-cdk/aws-apigatewayv2-alpha": "2.110.1-alpha.0", - "@aws-cdk/aws-apigatewayv2-integrations-alpha": "2.110.1-alpha.0", "@sveltejs/kit": "^1.5.0", "@types/aws-lambda": "^8.10.128", "@types/folder-hash": "^4.0.4", "@types/node": "^20.9.4", "@typescript-eslint/eslint-plugin": "^6.12.0", - "aws-cdk-lib": "^2.110.1", + "aws-cdk": "^2.126.0", + "aws-cdk-lib": "^2.126.0", "constructs": "^10.3.0", "eslint": "^8.54.0", "eslint-config-prettier": "^9.0.0", @@ -63,6 +58,7 @@ "packageManager": "pnpm@8.10.5", "pnpm": { "overrides": { + "aws-cdk": "$aws-cdk-lib", "typescript": "$typescript", "aws-cdk-lib": "$aws-cdk-lib", "constructs": "$constructs", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 34c7deb..336921d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -5,23 +5,15 @@ settings: excludeLinksFromLockfile: false overrides: + aws-cdk: ^2.126.0 typescript: 5.3.2 - aws-cdk-lib: ^2.110.1 + aws-cdk-lib: ^2.126.0 constructs: ^10.3.0 '@types/node': ^20.9.4 importers: .: devDependencies: - '@apimda/npm-layer-version': - specifier: ^1.0.0 - version: 1.0.0 - '@aws-cdk/aws-apigatewayv2-alpha': - specifier: 2.110.1-alpha.0 - version: 2.110.1-alpha.0(aws-cdk-lib@2.110.1)(constructs@10.3.0) - '@aws-cdk/aws-apigatewayv2-integrations-alpha': - specifier: 2.110.1-alpha.0 - version: 2.110.1-alpha.0(@aws-cdk/aws-apigatewayv2-alpha@2.110.1-alpha.0)(aws-cdk-lib@2.110.1)(constructs@10.3.0) '@sveltejs/kit': specifier: ^1.5.0 version: 1.27.6(svelte@3.59.2)(vite@4.5.0) @@ -37,9 +29,12 @@ importers: '@typescript-eslint/eslint-plugin': specifier: ^6.12.0 version: 6.12.0(@typescript-eslint/parser@6.12.0)(eslint@8.54.0)(typescript@5.3.2) + aws-cdk: + specifier: ^2.126.0 + version: 2.126.0 aws-cdk-lib: - specifier: ^2.110.1 - version: 2.110.1(constructs@10.3.0) + specifier: ^2.126.0 + version: 2.126.0(constructs@10.3.0) constructs: specifier: ^10.3.0 version: 10.3.0 @@ -86,11 +81,11 @@ importers: specifier: workspace:* version: link:../.. aws-cdk: - specifier: '*' - version: 2.110.1 + specifier: ^2.126.0 + version: 2.126.0 aws-cdk-lib: - specifier: ^2.110.1 - version: 2.110.1(constructs@10.3.0) + specifier: ^2.126.0 + version: 2.126.0(constructs@10.3.0) constructs: specifier: ^10.3.0 version: 10.3.0 @@ -169,18 +164,9 @@ packages: { integrity: sha512-6ZLjkLEuO/ZkX7do85O1huIBxtB8jihWTfOz5cXVOMMWmJ5OQOrabAh4Pvw1kBPtCWyd/e+0OaccmjP0GyfqVA== } dev: false - /@apimda/npm-layer-version@1.0.0: + /@aws-cdk/asset-awscli-v1@2.2.202: resolution: - { integrity: sha512-zgytKThT6SlYJZiuEfxcw8yrTBrTJRuFrEkT+QU/ryadF9LW7797hyRZJPTg/COAk8Q8M00gBSnO1+7mb7x7bw== } - requiresBuild: true - dependencies: - aws-cdk-lib: 2.110.1(constructs@10.3.0) - constructs: 10.3.0 - dev: true - - /@aws-cdk/asset-awscli-v1@2.2.201: - resolution: - { integrity: sha512-INZqcwDinNaIdb5CtW3ez5s943nX5stGBQS6VOP2JDlOFP81hM3fds/9NDknipqfUkZM43dx+HgVvkXYXXARCQ== } + { integrity: sha512-JqlF0D4+EVugnG5dAsNZMqhu3HW7ehOXm5SDMxMbXNDMdsF0pxtQKNHRl52z1U9igsHmaFpUgSGjbhAJ+0JONg== } dev: true /@aws-cdk/asset-kubectl-v20@2.1.2: @@ -193,32 +179,6 @@ packages: { integrity: sha512-DDt4SLdLOwWCjGtltH4VCST7hpOI5DzieuhGZsBpZ+AgJdSI2GCjklCXm0GCTwJG/SolkL5dtQXyUKgg9luBDg== } dev: true - /@aws-cdk/aws-apigatewayv2-alpha@2.110.1-alpha.0(aws-cdk-lib@2.110.1)(constructs@10.3.0): - resolution: - { integrity: sha512-ldOrRsu5O0KtqMiKh43QbjHX26Q2dmDAFEBsQ4Y8pLWf0cISBkFrrcsv4VF23j9r9NEgLhPzoVOqiCyf4UUliQ== } - engines: { node: '>= 14.15.0' } - peerDependencies: - aws-cdk-lib: ^2.110.1 - constructs: ^10.3.0 - dependencies: - aws-cdk-lib: 2.110.1(constructs@10.3.0) - constructs: 10.3.0 - dev: true - - /@aws-cdk/aws-apigatewayv2-integrations-alpha@2.110.1-alpha.0(@aws-cdk/aws-apigatewayv2-alpha@2.110.1-alpha.0)(aws-cdk-lib@2.110.1)(constructs@10.3.0): - resolution: - { integrity: sha512-kQfymliXlKCkMNVwwYwJZzICi+KX25aoV3g9mXoxvD2XX0hcmCgBog4A2zEVnwGqdGbOPg7BDENqUEYxxdakxQ== } - engines: { node: '>= 14.15.0' } - peerDependencies: - '@aws-cdk/aws-apigatewayv2-alpha': 2.110.1-alpha.0 - aws-cdk-lib: ^2.110.1 - constructs: ^10.3.0 - dependencies: - '@aws-cdk/aws-apigatewayv2-alpha': 2.110.1-alpha.0(aws-cdk-lib@2.110.1)(constructs@10.3.0) - aws-cdk-lib: 2.110.1(constructs@10.3.0) - constructs: 10.3.0 - dev: true - /@babel/code-frame@7.23.4: resolution: { integrity: sha512-r1IONyb6Ia+jYR2vvIDhdWdlTGhqbBoFqLTQidzZ4kepUFH15ejXvFHxCVbtl7BOXIudsIubf4E81xeA3h3IXA== } @@ -2018,14 +1978,14 @@ packages: engines: { node: '>=0.8' } dev: true - /aws-cdk-lib@2.110.1(constructs@10.3.0): + /aws-cdk-lib@2.126.0(constructs@10.3.0): resolution: - { integrity: sha512-Z+42Jc/KSKFdBOpEv4LK9tz6kQUdVvUBquIYhLajam3aGblkonM0/FgexvAqy8iGwUaVEIpVyBTZUP2/VUMalg== } + { integrity: sha512-HlwBYiqNCfhPUyozjoV4zJIBgH0adE/zTVATmeaM1jcNGs6jwkPMpWsF75JuXOoVtkcfpBFyNKloGd82nTiB0A== } engines: { node: '>= 14.15.0' } peerDependencies: constructs: ^10.3.0 dependencies: - '@aws-cdk/asset-awscli-v1': 2.2.201 + '@aws-cdk/asset-awscli-v1': 2.2.202 '@aws-cdk/asset-kubectl-v20': 2.1.2 '@aws-cdk/asset-node-proxy-agent-v6': 2.0.1 constructs: 10.3.0 @@ -2042,9 +2002,9 @@ packages: - table - yaml - /aws-cdk@2.110.1: + /aws-cdk@2.126.0: resolution: - { integrity: sha512-/V0FOgsvD/FkFANrYnSmyb+XK56tm2oE86pUCoEggQ2tka6Zm0z9blKZQV4euMErNSkWz4ReSAKenaqk86Fr5Q== } + { integrity: sha512-hEyy8UCEEUnkieH6JbJBN8XAbvuVZNdBmVQ8wHCqo8RSNqmpwM1qvLiyXV/2JvCqJJ0bl9uBiZ98Ytd5i3wW7g== } engines: { node: '>= 14.15.0' } hasBin: true optionalDependencies: diff --git a/src/svelte-app.ts b/src/svelte-app.ts index 93b164c..37c996e 100644 --- a/src/svelte-app.ts +++ b/src/svelte-app.ts @@ -5,14 +5,13 @@ import * as s3 from 'aws-cdk-lib/aws-s3'; import * as s3d from 'aws-cdk-lib/aws-s3-deployment'; import * as cdk from 'aws-cdk-lib'; import fs from 'fs'; -import * as apigw from '@aws-cdk/aws-apigatewayv2-alpha'; +import * as apigw from 'aws-cdk-lib/aws-apigatewayv2'; import * as lambdaNode from 'aws-cdk-lib/aws-lambda-nodejs'; -import * as apigwInt from '@aws-cdk/aws-apigatewayv2-integrations-alpha'; +import * as apigwInt from 'aws-cdk-lib/aws-apigatewayv2-integrations'; import * as cfo from 'aws-cdk-lib/aws-cloudfront-origins'; import * as cf from 'aws-cdk-lib/aws-cloudfront'; import * as cm from 'aws-cdk-lib/aws-certificatemanager'; import * as r53t from 'aws-cdk-lib/aws-route53-targets'; -import { NpmLayerVersion } from '@apimda/npm-layer-version'; import { LAMBDA_ARCHITECTURE, @@ -142,14 +141,6 @@ export class SvelteApp extends Construct { const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); - const svelteKitLayer = new NpmLayerVersion(this, 'SvelteKitLambdaLayer', { - layerPath: this.stackProps.svelteAppLayerPath ?? path.resolve(__dirname, '../layers/svelte-kit-layer'), - layerVersionProps: { - compatibleArchitectures: [LAMBDA_ARCHITECTURE], - compatibleRuntimes: [LAMBDA_RUNTIME] - } - }); - const serverLambda = new lambdaNode.NodejsFunction(this, 'SvelteServerLambda', { currentVersionOptions: this.stackProps.provisionedConcurrentExecutions ? { @@ -163,10 +154,11 @@ export class SvelteApp extends Construct { entry: path.resolve(__dirname, 'svelte-server-handler.js'), environment: this.stackProps.svelteServerEnvironment, bundling: { + nodeModules: ['@sveltejs/kit'], format: lambdaNode.OutputFormat.ESM, minify: false, target: LAMBDA_ESBUILD_TARGET, - externalModules: [LAMBDA_ESBUILD_EXTERNAL_AWS_SDK, './server/index.js', './server/manifest-full.js'], + externalModules: [LAMBDA_ESBUILD_EXTERNAL_AWS_SDK], commandHooks: { afterBundling(inputDir: string, outputDir: string): string[] { return [ diff --git a/src/svelte-server-handler.ts b/src/svelte-server-handler.ts index f8314ea..64e1bbb 100644 --- a/src/svelte-server-handler.ts +++ b/src/svelte-server-handler.ts @@ -10,7 +10,7 @@ export type Handler = (event: APIGatewayProxyEventV2) => Promise { const { Server } = await import(path.resolve(serverDir, 'index.js')); - const { manifest } = await import(path.resolve(serverDir, 'manifest.js')); + const { manifest } = await import(path.resolve(serverDir, 'manifest-full.js')); const server = new Server(manifest) as Server; await server.init({ env: process.env }); @@ -21,24 +21,22 @@ export function createSvelteServerHandler(serverDir: string): Handler { const queryString = event.rawQueryString ? `?${event.rawQueryString}` : undefined; const url = `${origin}${event.requestContext.http.path}${queryString ?? ''}`; - console.log('url:', url); - if (event.cookies) { event.headers['cookie'] = event.cookies.join('; '); } - const response = await server.respond( - new Request(url, { - method: event.requestContext.http.method, - headers: new Headers((event.headers as Record) || {}), - body - }), - { - getClientAddress() { - return event.requestContext.http.sourceIp; - } + const req = new Request(url, { + method: event.requestContext.http.method, + headers: new Headers((event.headers as Record) || {}), + body + }); + + const response = await server.respond(req, { + platform: { req }, + getClientAddress() { + return event.requestContext.http.sourceIp; } - ); + }); const headers: Record = {}; @@ -54,4 +52,4 @@ export function createSvelteServerHandler(serverDir: string): Handler { }; } -export const handler: APIGatewayProxyHandlerV2 = await createSvelteServerHandler('/opt/server'); +export const handler: APIGatewayProxyHandlerV2 = createSvelteServerHandler('./server'); diff --git a/src/tests/svelte-server-handler.test.ts b/src/tests/svelte-server-handler.test.ts index 6b5d3ec..dfb406b 100644 --- a/src/tests/svelte-server-handler.test.ts +++ b/src/tests/svelte-server-handler.test.ts @@ -1,12 +1,41 @@ -import { test, describe } from 'vitest'; +import { test, describe, expect } from 'vitest'; import path from 'node:path'; import { createSvelteServerHandler } from '../svelte-server-handler.js'; import { createAwsProxyEvent } from './test-utils.js'; +//import formDataToString from 'formdata-to-string'; describe('svelte-server-handler', () => { test('createSvelteServerHandler', async () => { - const handler = await createSvelteServerHandler(path.resolve(__dirname, '../../example/svelte-app/build/server')); - const rootResponse = await handler(createAwsProxyEvent('/', 'GET')); - console.log(rootResponse.statusCode); + const handler = createSvelteServerHandler( + path.resolve(__dirname, '../../example/svelte-app/.svelte-kit/output/server') + ); + + const pages = ['/', '/about', '/sverdle', '/sverdle/how-to-play']; + + for (const page of pages) { + const preRenderedResponse = await handler(createAwsProxyEvent(page, 'GET')); + expect(preRenderedResponse.statusCode).toBe(200); + expect(preRenderedResponse.headers?.['content-type']).toBe('text/html'); + } + + //TODO: Get this working + // const formData = new FormData(); + // formData.append('guess', 'e'); + // formData.append('guess', 'e'); + // formData.append('guess', 'e'); + // formData.append('guess', 'e'); + + // const formDataBody = await formDataToString(formData); + // const [boundary] = formDataBody.split('\n'); + // const formServerAction = await handler( + // createAwsProxyEvent('/sverdle?/enter', 'POST', { + // body: formDataBody, + // headers: { + // 'content-type': `multipart/form-data; boundary=${boundary}`, + // 'content-length': formDataBody.length.toString(), + // origin: 'https://localhost' + // } + // }) + // ); }); }); diff --git a/src/tests/test-utils.ts b/src/tests/test-utils.ts index a3a2318..2ef3e41 100644 --- a/src/tests/test-utils.ts +++ b/src/tests/test-utils.ts @@ -1,6 +1,10 @@ import { APIGatewayProxyEventV2 } from 'aws-lambda'; -export function createAwsProxyEvent(path: string, method: string): APIGatewayProxyEventV2 { +export function createAwsProxyEvent( + path: string, + method: string, + override?: Partial +): APIGatewayProxyEventV2 { return { version: '2.0', routeKey: '$default', @@ -9,7 +13,8 @@ export function createAwsProxyEvent(path: string, method: string): APIGatewayPro cookies: ['cookie1', 'cookie2'], headers: { Header1: 'value1', - Header2: 'value1,value2' + Header2: 'value1,value2', + origin: 'https://localhost' }, queryStringParameters: {}, requestContext: { @@ -47,6 +52,7 @@ export function createAwsProxyEvent(path: string, method: string): APIGatewayPro stageVariables: { stageVariable1: 'value1', stageVariable2: 'value2' - } + }, + ...override }; } diff --git a/todos/index.md b/todos/index.md index 4eb59f2..b740503 100644 --- a/todos/index.md +++ b/todos/index.md @@ -1,17 +1,13 @@ # CDK Svelte App - [] Add turbo to project -- [] Refactor server handler to create function -- [] Use @codegenie/serverless-express to convert AWS Lambda Event to node req, res -- [] Parametrize Svelte output path to get Svelte server -- [] Update CDK version and use core apigw constructs -- [] Add testing for server lambda -- [] Check if SvelteKit Lambda Layer is needed or not, I assume it is not needed -- [] Proxy all methods - - [] PUT - - [] PATCH - - [] DELETE +- [x] Refactor server handler to create function +- [x] Use @codegenie/serverless-express to convert AWS Lambda Event to node req, res +- [x] Parametrize Svelte output path to get Svelte server +- [x] Update CDK version and use core apigw constructs +- [x] Add testing for server lambda +- [x] Check if SvelteKit Lambda Layer is needed or not, I assume it is not needed - [] Check if check for no-cache control header makes sense, some of those 300 are permanent and caching makes sense in this case -- [] Figure TS dev import vs Prd import -- [] Remove the need for package.json magic for esm support in lambda -- [] Check the use if a Lambda Layer for the resulting svelte server code +- [x] Figure TS dev import vs Prd import +- [x] Remove the need for package.json magic for esm support in lambda +- [x] Check the use if a Lambda Layer for the resulting svelte server code