From 62257a17dc1fd931adc8b56f5ec22a5a297d3f59 Mon Sep 17 00:00:00 2001 From: Mihovil Ilakovac Date: Wed, 8 Jan 2025 17:35:37 +0100 Subject: [PATCH 01/22] Detect server imports WIP Signed-off-by: Mihovil Ilakovac --- .../templates/react-app/tsconfig.node.json | 2 +- .../templates/react-app/vite.config.ts | 6 +++- .../react-app/vite/detectServerImports.ts | 28 +++++++++++++++++++ waspc/examples/todoApp/src/pages/About.jsx | 2 ++ waspc/src/Wasp/Generator/WebAppGenerator.hs | 1 + 5 files changed, 37 insertions(+), 2 deletions(-) create mode 100644 waspc/data/Generator/templates/react-app/vite/detectServerImports.ts diff --git a/waspc/data/Generator/templates/react-app/tsconfig.node.json b/waspc/data/Generator/templates/react-app/tsconfig.node.json index d90bb30d80..a7f26075bd 100644 --- a/waspc/data/Generator/templates/react-app/tsconfig.node.json +++ b/waspc/data/Generator/templates/react-app/tsconfig.node.json @@ -6,5 +6,5 @@ "moduleResolution": "bundler", "allowSyntheticDefaultImports": true }, - "include": ["vite.config.ts", "./src/ext-src/vite.config.ts"] + "include": ["vite.config.ts", "./src/ext-src/vite.config.ts", "./vite/detectServerImports.ts"] } diff --git a/waspc/data/Generator/templates/react-app/vite.config.ts b/waspc/data/Generator/templates/react-app/vite.config.ts index ab4224729f..048236b7db 100644 --- a/waspc/data/Generator/templates/react-app/vite.config.ts +++ b/waspc/data/Generator/templates/react-app/vite.config.ts @@ -3,6 +3,7 @@ import { mergeConfig } from "vite"; import react from "@vitejs/plugin-react"; import { defaultExclude } from "vitest/config" +import { detectServerImports } from "./vite/detectServerImports" {=# customViteConfig.isDefined =} // Ignoring the TS error because we are importing a file outside of TS root dir. @@ -16,7 +17,10 @@ const _waspUserProvidedConfig = {}; const defaultViteConfig = { base: "{= baseDir =}", - plugins: [react()], + plugins: [ + react(), + detectServerImports(), + ], optimizeDeps: { exclude: ['wasp'] }, diff --git a/waspc/data/Generator/templates/react-app/vite/detectServerImports.ts b/waspc/data/Generator/templates/react-app/vite/detectServerImports.ts new file mode 100644 index 0000000000..983777cd32 --- /dev/null +++ b/waspc/data/Generator/templates/react-app/vite/detectServerImports.ts @@ -0,0 +1,28 @@ +import { type Plugin } from "vite"; + +export function detectServerImports(): Plugin { + return { + name: 'wasp-detect-server-imports', + transform(code, id) { + const isInDotWaspFolder = id.includes("/.wasp/"); + + if (isInDotWaspFolder) { + // Skip checking files in .wasp folder. + return; + } + + const regex = /\s*import\s+(?:(?:[\w*\s{},]*)\s+from\s+)?(['"`])([^'"`]+)\1\s*/g; + + for (const match of code.matchAll(regex)) { + const importPath = match[2]; + const isServerImport = importPath.startsWith("wasp/server"); + if (isServerImport) { + const fileName = id.split('/').pop(); + throw new Error( + `${fileName} contains a server import from ${importPath}. This is not allowed in the client code.` + ); + } + } + }, + }; +} diff --git a/waspc/examples/todoApp/src/pages/About.jsx b/waspc/examples/todoApp/src/pages/About.jsx index fdc9a50859..354f7111c3 100644 --- a/waspc/examples/todoApp/src/pages/About.jsx +++ b/waspc/examples/todoApp/src/pages/About.jsx @@ -1,5 +1,7 @@ import { Link } from "wasp/client/router"; +import { HttpError } from "wasp/server" + import React from 'react' const About = () => { diff --git a/waspc/src/Wasp/Generator/WebAppGenerator.hs b/waspc/src/Wasp/Generator/WebAppGenerator.hs index e1f4b481ed..844c36cdb7 100644 --- a/waspc/src/Wasp/Generator/WebAppGenerator.hs +++ b/waspc/src/Wasp/Generator/WebAppGenerator.hs @@ -67,6 +67,7 @@ genWebApp spec = do genFileCopy [relfile|tsconfig.json|], genFileCopy [relfile|tsconfig.node.json|], genFileCopy [relfile|netlify.toml|], + genFileCopy [relfile|vite/detectServerImports.ts|], genPackageJson spec (npmDepsForWasp spec), genNpmrc, genGitignore, From 1d038d82ad16f1f5839309a09af384556d471cc1 Mon Sep 17 00:00:00 2001 From: Mihovil Ilakovac Date: Thu, 9 Jan 2025 09:50:39 +0100 Subject: [PATCH 02/22] Use generator. Cleanup. --- .../react-app/vite/detectServerImports.ts | 50 ++++++++++++++----- 1 file changed, 38 insertions(+), 12 deletions(-) diff --git a/waspc/data/Generator/templates/react-app/vite/detectServerImports.ts b/waspc/data/Generator/templates/react-app/vite/detectServerImports.ts index 983777cd32..bfdc7811f4 100644 --- a/waspc/data/Generator/templates/react-app/vite/detectServerImports.ts +++ b/waspc/data/Generator/templates/react-app/vite/detectServerImports.ts @@ -3,26 +3,52 @@ import { type Plugin } from "vite"; export function detectServerImports(): Plugin { return { name: 'wasp-detect-server-imports', - transform(code, id) { - const isInDotWaspFolder = id.includes("/.wasp/"); + transform(code, filePath) { + const isInDotWaspFolder = filePath.includes("/.wasp/"); + // We don't want to check for server imports in the Wasp + // framework code. if (isInDotWaspFolder) { - // Skip checking files in .wasp folder. return; } - const regex = /\s*import\s+(?:(?:[\w*\s{},]*)\s+from\s+)?(['"`])([^'"`]+)\1\s*/g; + const imports = getImportsFromCode(code); - for (const match of code.matchAll(regex)) { - const importPath = match[2]; - const isServerImport = importPath.startsWith("wasp/server"); - if (isServerImport) { - const fileName = id.split('/').pop(); - throw new Error( - `${fileName} contains a server import from ${importPath}. This is not allowed in the client code.` - ); + for (const imp of imports) { + if (imp.moduleName.startsWith("wasp/server")) { + throw new Error(getServerImportErrorMessage(imp, filePath)); } } }, }; } + +type Import = { + importStatement: string; + moduleName: string; +} + +const importStatementRegex = /\s*import\s+(?:(?:[\w*\s{},]*)\s+from\s+)?(['"`])([^'"`]+)\1\s*/g; + +function* getImportsFromCode(code: string): Generator { + const matches = code.matchAll(importStatementRegex); + for (const match of matches) { + yield { + importStatement: match[0].trim(), + moduleName: match[2], + }; + } +} + +function getServerImportErrorMessage(imp: Import, filePath: string): string { + return `Client module ${getRelativeFilePath(filePath)} imports server specific module ${imp.moduleName}: + +${imp.importStatement} + +This is not allowed in the client code.` +} + +// TODO: implement this with Wasp project dir passed to template. +function getRelativeFilePath(filePath: string): string { + return filePath; +} From 4409c349a69d30759c4e42bc8aaa02d6f3f201cf Mon Sep 17 00:00:00 2001 From: Mihovil Ilakovac Date: Tue, 14 Jan 2025 15:24:01 +0100 Subject: [PATCH 03/22] Add relative file path generation --- .../templates/react-app/vite/detectServerImports.ts | 10 ++++++---- waspc/src/Wasp/Generator/WebAppGenerator.hs | 12 ++++++++++-- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/waspc/data/Generator/templates/react-app/vite/detectServerImports.ts b/waspc/data/Generator/templates/react-app/vite/detectServerImports.ts index bfdc7811f4..078e7d7c9f 100644 --- a/waspc/data/Generator/templates/react-app/vite/detectServerImports.ts +++ b/waspc/data/Generator/templates/react-app/vite/detectServerImports.ts @@ -1,5 +1,8 @@ +{{={= =}=}} import { type Plugin } from "vite"; +const waspProjectDirAbsPath = "{= waspProjectDirAbsPath =}"; + export function detectServerImports(): Plugin { return { name: 'wasp-detect-server-imports', @@ -41,14 +44,13 @@ function* getImportsFromCode(code: string): Generator { } function getServerImportErrorMessage(imp: Import, filePath: string): string { - return `Client module ${getRelativeFilePath(filePath)} imports server specific module ${imp.moduleName}: + return `Client module "${getRelativeFilePath(filePath)}" imports server code: ${imp.importStatement} -This is not allowed in the client code.` +This is not supported in the client code.`; } -// TODO: implement this with Wasp project dir passed to template. function getRelativeFilePath(filePath: string): string { - return filePath; + return filePath.replace(waspProjectDirAbsPath, ""); } diff --git a/waspc/src/Wasp/Generator/WebAppGenerator.hs b/waspc/src/Wasp/Generator/WebAppGenerator.hs index 844c36cdb7..b4be53e4c2 100644 --- a/waspc/src/Wasp/Generator/WebAppGenerator.hs +++ b/waspc/src/Wasp/Generator/WebAppGenerator.hs @@ -67,12 +67,12 @@ genWebApp spec = do genFileCopy [relfile|tsconfig.json|], genFileCopy [relfile|tsconfig.node.json|], genFileCopy [relfile|netlify.toml|], - genFileCopy [relfile|vite/detectServerImports.ts|], genPackageJson spec (npmDepsForWasp spec), genNpmrc, genGitignore, genIndexHtml spec, - genViteConfig spec + genViteConfig spec, + genDetectServerImportsVitePlugin spec ] <++> genSrcDir spec <++> genPublicDir spec @@ -273,3 +273,11 @@ genViteConfig spec = return $ C.mkTmplFdWithData tmplFile tmplData SP.fromRelDir (Project.dotWaspDirInWaspProjectDir Project.generatedCodeDirInDotWaspDir C.webAppRootDirInProjectRootDir) importName = JsImportModule "customViteConfig" + +genDetectServerImportsVitePlugin :: AppSpec -> Generator FileDraft +genDetectServerImportsVitePlugin spec = return $ C.mkTmplFdWithData tmplFile tmplData + where + tmplFile = C.asTmplFile [relfile|vite/detectServerImports.ts|] + tmplData = object ["waspProjectDirAbsPath" .= waspProjectDirAbsPath] + + waspProjectDirAbsPath = SP.fromAbsDir $ AS.waspProjectDir spec From 92f11f7685756dc8af4afcdb6ddb860d8632d35d Mon Sep 17 00:00:00 2001 From: Mihovil Ilakovac Date: Tue, 14 Jan 2025 15:26:40 +0100 Subject: [PATCH 04/22] e2e tests Signed-off-by: Mihovil Ilakovac --- .../waspBuild-golden/files.manifest | 1 + .../waspBuild/.wasp/build/.waspchecksums | 11 +++- .../.wasp/build/web-app/tsconfig.node.json | 2 +- .../.wasp/build/web-app/vite.config.ts | 6 +- .../build/web-app/vite/detectServerImports.ts | 55 +++++++++++++++++++ .../waspCompile-golden/files.manifest | 1 + .../waspCompile/.wasp/out/.waspchecksums | 11 +++- .../.wasp/out/web-app/tsconfig.node.json | 2 +- .../.wasp/out/web-app/vite.config.ts | 6 +- .../out/web-app/vite/detectServerImports.ts | 55 +++++++++++++++++++ .../waspComplexTest-golden/files.manifest | 1 + .../waspComplexTest/.wasp/out/.waspchecksums | 11 +++- .../.wasp/out/web-app/tsconfig.node.json | 2 +- .../.wasp/out/web-app/vite.config.ts | 6 +- .../out/web-app/vite/detectServerImports.ts | 55 +++++++++++++++++++ .../waspJob-golden/files.manifest | 1 + .../waspJob/.wasp/out/.waspchecksums | 11 +++- .../.wasp/out/web-app/tsconfig.node.json | 2 +- .../waspJob/.wasp/out/web-app/vite.config.ts | 6 +- .../out/web-app/vite/detectServerImports.ts | 55 +++++++++++++++++++ .../waspMigrate-golden/files.manifest | 1 + .../waspMigrate/.wasp/out/.waspchecksums | 11 +++- .../.wasp/out/web-app/tsconfig.node.json | 2 +- .../.wasp/out/web-app/vite.config.ts | 6 +- .../out/web-app/vite/detectServerImports.ts | 55 +++++++++++++++++++ 25 files changed, 355 insertions(+), 20 deletions(-) create mode 100644 waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/web-app/vite/detectServerImports.ts create mode 100644 waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/web-app/vite/detectServerImports.ts create mode 100644 waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/web-app/vite/detectServerImports.ts create mode 100644 waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/web-app/vite/detectServerImports.ts create mode 100644 waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/web-app/vite/detectServerImports.ts diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/files.manifest b/waspc/e2e-test/test-outputs/waspBuild-golden/files.manifest index 55670c2900..02925608e0 100644 --- a/waspc/e2e-test/test-outputs/waspBuild-golden/files.manifest +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/files.manifest @@ -237,6 +237,7 @@ waspBuild/.wasp/build/web-app/src/vite-env.d.ts waspBuild/.wasp/build/web-app/tsconfig.json waspBuild/.wasp/build/web-app/tsconfig.node.json waspBuild/.wasp/build/web-app/vite.config.ts +waspBuild/.wasp/build/web-app/vite/detectServerImports.ts waspBuild/.wasp/out/sdk/wasp/api/events.ts waspBuild/.wasp/out/sdk/wasp/api/index.ts waspBuild/.wasp/out/sdk/wasp/client/config.ts diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/.waspchecksums b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/.waspchecksums index e6032f38d6..42274cd76e 100644 --- a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/.waspchecksums +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/.waspchecksums @@ -662,13 +662,20 @@ "file", "web-app/tsconfig.node.json" ], - "30fed1f6aa4710deb690d1ae47628066f34afda7b633f0c696fafcda9e2dbc1e" + "d665257a3ee4ec198afc7350d71bc5b9ab2c2febb94d3b2f8f274a98db4fb44b" ], [ [ "file", "web-app/vite.config.ts" ], - "64256d93d81f6192f419e836ed4484f0e8c1e28f018bfb3213851608200594ac" + "70cdf971f9f371c886c2d86fc5a7832a520e06695724f9af62428b968b45705a" + ], + [ + [ + "file", + "web-app/vite/detectServerImports.ts" + ], + "dee5af744649129fc98d5a3335380d16e239a932e444187ce7c226ea7560a40f" ] ] \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/web-app/tsconfig.node.json b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/web-app/tsconfig.node.json index d90bb30d80..a7f26075bd 100644 --- a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/web-app/tsconfig.node.json +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/web-app/tsconfig.node.json @@ -6,5 +6,5 @@ "moduleResolution": "bundler", "allowSyntheticDefaultImports": true }, - "include": ["vite.config.ts", "./src/ext-src/vite.config.ts"] + "include": ["vite.config.ts", "./src/ext-src/vite.config.ts", "./vite/detectServerImports.ts"] } diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/web-app/vite.config.ts b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/web-app/vite.config.ts index ed2d3dd5f0..b17dbff5fa 100644 --- a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/web-app/vite.config.ts +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/web-app/vite.config.ts @@ -2,6 +2,7 @@ import { mergeConfig } from "vite"; import react from "@vitejs/plugin-react"; import { defaultExclude } from "vitest/config" +import { detectServerImports } from "./vite/detectServerImports" // Ignoring the TS error because we are importing a file outside of TS root dir. // @ts-ignore @@ -10,7 +11,10 @@ const _waspUserProvidedConfig = customViteConfig const defaultViteConfig = { base: "/", - plugins: [react()], + plugins: [ + react(), + detectServerImports(), + ], optimizeDeps: { exclude: ['wasp'] }, diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/web-app/vite/detectServerImports.ts b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/web-app/vite/detectServerImports.ts new file mode 100644 index 0000000000..a37136d62a --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/web-app/vite/detectServerImports.ts @@ -0,0 +1,55 @@ +import { type Plugin } from "vite"; + +const waspProjectDirAbsPath = "/Users/ilakovac/dev/wasp/waspc/e2e-test/test-outputs/waspBuild-current/waspBuild/"; + +export function detectServerImports(): Plugin { + return { + name: 'wasp-detect-server-imports', + transform(code, filePath) { + const isInDotWaspFolder = filePath.includes("/.wasp/"); + + // We don't want to check for server imports in the Wasp + // framework code. + if (isInDotWaspFolder) { + return; + } + + const imports = getImportsFromCode(code); + + for (const imp of imports) { + if (imp.moduleName.startsWith("wasp/server")) { + throw new Error(getServerImportErrorMessage(imp, filePath)); + } + } + }, + }; +} + +type Import = { + importStatement: string; + moduleName: string; +} + +const importStatementRegex = /\s*import\s+(?:(?:[\w*\s{},]*)\s+from\s+)?(['"`])([^'"`]+)\1\s*/g; + +function* getImportsFromCode(code: string): Generator { + const matches = code.matchAll(importStatementRegex); + for (const match of matches) { + yield { + importStatement: match[0].trim(), + moduleName: match[2], + }; + } +} + +function getServerImportErrorMessage(imp: Import, filePath: string): string { + return `Client module "${getRelativeFilePath(filePath)}" imports server code: + +${imp.importStatement} + +This is not supported in the client code.`; +} + +function getRelativeFilePath(filePath: string): string { + return filePath.replace(waspProjectDirAbsPath, ""); +} diff --git a/waspc/e2e-test/test-outputs/waspCompile-golden/files.manifest b/waspc/e2e-test/test-outputs/waspCompile-golden/files.manifest index 0ebc3d1eef..e47fe56dc6 100644 --- a/waspc/e2e-test/test-outputs/waspCompile-golden/files.manifest +++ b/waspc/e2e-test/test-outputs/waspCompile-golden/files.manifest @@ -234,6 +234,7 @@ waspCompile/.wasp/out/web-app/src/vite-env.d.ts waspCompile/.wasp/out/web-app/tsconfig.json waspCompile/.wasp/out/web-app/tsconfig.node.json waspCompile/.wasp/out/web-app/vite.config.ts +waspCompile/.wasp/out/web-app/vite/detectServerImports.ts waspCompile/.waspignore waspCompile/.wasproot waspCompile/main.wasp diff --git a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/.waspchecksums b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/.waspchecksums index b086a6efd0..8145a2edf9 100644 --- a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/.waspchecksums +++ b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/.waspchecksums @@ -676,13 +676,20 @@ "file", "web-app/tsconfig.node.json" ], - "30fed1f6aa4710deb690d1ae47628066f34afda7b633f0c696fafcda9e2dbc1e" + "d665257a3ee4ec198afc7350d71bc5b9ab2c2febb94d3b2f8f274a98db4fb44b" ], [ [ "file", "web-app/vite.config.ts" ], - "64256d93d81f6192f419e836ed4484f0e8c1e28f018bfb3213851608200594ac" + "70cdf971f9f371c886c2d86fc5a7832a520e06695724f9af62428b968b45705a" + ], + [ + [ + "file", + "web-app/vite/detectServerImports.ts" + ], + "c1a1223064a98519c281bb41983aaf6135ec4c86a97fdd45ac01322040725341" ] ] \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/web-app/tsconfig.node.json b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/web-app/tsconfig.node.json index d90bb30d80..a7f26075bd 100644 --- a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/web-app/tsconfig.node.json +++ b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/web-app/tsconfig.node.json @@ -6,5 +6,5 @@ "moduleResolution": "bundler", "allowSyntheticDefaultImports": true }, - "include": ["vite.config.ts", "./src/ext-src/vite.config.ts"] + "include": ["vite.config.ts", "./src/ext-src/vite.config.ts", "./vite/detectServerImports.ts"] } diff --git a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/web-app/vite.config.ts b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/web-app/vite.config.ts index ed2d3dd5f0..b17dbff5fa 100644 --- a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/web-app/vite.config.ts +++ b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/web-app/vite.config.ts @@ -2,6 +2,7 @@ import { mergeConfig } from "vite"; import react from "@vitejs/plugin-react"; import { defaultExclude } from "vitest/config" +import { detectServerImports } from "./vite/detectServerImports" // Ignoring the TS error because we are importing a file outside of TS root dir. // @ts-ignore @@ -10,7 +11,10 @@ const _waspUserProvidedConfig = customViteConfig const defaultViteConfig = { base: "/", - plugins: [react()], + plugins: [ + react(), + detectServerImports(), + ], optimizeDeps: { exclude: ['wasp'] }, diff --git a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/web-app/vite/detectServerImports.ts b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/web-app/vite/detectServerImports.ts new file mode 100644 index 0000000000..2034f9bdae --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/web-app/vite/detectServerImports.ts @@ -0,0 +1,55 @@ +import { type Plugin } from "vite"; + +const waspProjectDirAbsPath = "/Users/ilakovac/dev/wasp/waspc/e2e-test/test-outputs/waspCompile-current/waspCompile/"; + +export function detectServerImports(): Plugin { + return { + name: 'wasp-detect-server-imports', + transform(code, filePath) { + const isInDotWaspFolder = filePath.includes("/.wasp/"); + + // We don't want to check for server imports in the Wasp + // framework code. + if (isInDotWaspFolder) { + return; + } + + const imports = getImportsFromCode(code); + + for (const imp of imports) { + if (imp.moduleName.startsWith("wasp/server")) { + throw new Error(getServerImportErrorMessage(imp, filePath)); + } + } + }, + }; +} + +type Import = { + importStatement: string; + moduleName: string; +} + +const importStatementRegex = /\s*import\s+(?:(?:[\w*\s{},]*)\s+from\s+)?(['"`])([^'"`]+)\1\s*/g; + +function* getImportsFromCode(code: string): Generator { + const matches = code.matchAll(importStatementRegex); + for (const match of matches) { + yield { + importStatement: match[0].trim(), + moduleName: match[2], + }; + } +} + +function getServerImportErrorMessage(imp: Import, filePath: string): string { + return `Client module "${getRelativeFilePath(filePath)}" imports server code: + +${imp.importStatement} + +This is not supported in the client code.`; +} + +function getRelativeFilePath(filePath: string): string { + return filePath.replace(waspProjectDirAbsPath, ""); +} diff --git a/waspc/e2e-test/test-outputs/waspComplexTest-golden/files.manifest b/waspc/e2e-test/test-outputs/waspComplexTest-golden/files.manifest index 42ed7f7d09..16065f16df 100644 --- a/waspc/e2e-test/test-outputs/waspComplexTest-golden/files.manifest +++ b/waspc/e2e-test/test-outputs/waspComplexTest-golden/files.manifest @@ -526,6 +526,7 @@ waspComplexTest/.wasp/out/web-app/src/vite-env.d.ts waspComplexTest/.wasp/out/web-app/tsconfig.json waspComplexTest/.wasp/out/web-app/tsconfig.node.json waspComplexTest/.wasp/out/web-app/vite.config.ts +waspComplexTest/.wasp/out/web-app/vite/detectServerImports.ts waspComplexTest/.waspignore waspComplexTest/.wasproot waspComplexTest/main.wasp diff --git a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/.waspchecksums b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/.waspchecksums index 3a751612d7..f3452448e8 100644 --- a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/.waspchecksums +++ b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/.waspchecksums @@ -1320,13 +1320,20 @@ "file", "web-app/tsconfig.node.json" ], - "30fed1f6aa4710deb690d1ae47628066f34afda7b633f0c696fafcda9e2dbc1e" + "d665257a3ee4ec198afc7350d71bc5b9ab2c2febb94d3b2f8f274a98db4fb44b" ], [ [ "file", "web-app/vite.config.ts" ], - "64256d93d81f6192f419e836ed4484f0e8c1e28f018bfb3213851608200594ac" + "70cdf971f9f371c886c2d86fc5a7832a520e06695724f9af62428b968b45705a" + ], + [ + [ + "file", + "web-app/vite/detectServerImports.ts" + ], + "c2270341bd7d8395bb4cadab68067ee4510f4da4887250218294af628fb57ef0" ] ] \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/web-app/tsconfig.node.json b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/web-app/tsconfig.node.json index d90bb30d80..a7f26075bd 100644 --- a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/web-app/tsconfig.node.json +++ b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/web-app/tsconfig.node.json @@ -6,5 +6,5 @@ "moduleResolution": "bundler", "allowSyntheticDefaultImports": true }, - "include": ["vite.config.ts", "./src/ext-src/vite.config.ts"] + "include": ["vite.config.ts", "./src/ext-src/vite.config.ts", "./vite/detectServerImports.ts"] } diff --git a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/web-app/vite.config.ts b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/web-app/vite.config.ts index ed2d3dd5f0..b17dbff5fa 100644 --- a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/web-app/vite.config.ts +++ b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/web-app/vite.config.ts @@ -2,6 +2,7 @@ import { mergeConfig } from "vite"; import react from "@vitejs/plugin-react"; import { defaultExclude } from "vitest/config" +import { detectServerImports } from "./vite/detectServerImports" // Ignoring the TS error because we are importing a file outside of TS root dir. // @ts-ignore @@ -10,7 +11,10 @@ const _waspUserProvidedConfig = customViteConfig const defaultViteConfig = { base: "/", - plugins: [react()], + plugins: [ + react(), + detectServerImports(), + ], optimizeDeps: { exclude: ['wasp'] }, diff --git a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/web-app/vite/detectServerImports.ts b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/web-app/vite/detectServerImports.ts new file mode 100644 index 0000000000..a8163c3e6a --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/web-app/vite/detectServerImports.ts @@ -0,0 +1,55 @@ +import { type Plugin } from "vite"; + +const waspProjectDirAbsPath = "/Users/ilakovac/dev/wasp/waspc/e2e-test/test-outputs/waspComplexTest-current/waspComplexTest/"; + +export function detectServerImports(): Plugin { + return { + name: 'wasp-detect-server-imports', + transform(code, filePath) { + const isInDotWaspFolder = filePath.includes("/.wasp/"); + + // We don't want to check for server imports in the Wasp + // framework code. + if (isInDotWaspFolder) { + return; + } + + const imports = getImportsFromCode(code); + + for (const imp of imports) { + if (imp.moduleName.startsWith("wasp/server")) { + throw new Error(getServerImportErrorMessage(imp, filePath)); + } + } + }, + }; +} + +type Import = { + importStatement: string; + moduleName: string; +} + +const importStatementRegex = /\s*import\s+(?:(?:[\w*\s{},]*)\s+from\s+)?(['"`])([^'"`]+)\1\s*/g; + +function* getImportsFromCode(code: string): Generator { + const matches = code.matchAll(importStatementRegex); + for (const match of matches) { + yield { + importStatement: match[0].trim(), + moduleName: match[2], + }; + } +} + +function getServerImportErrorMessage(imp: Import, filePath: string): string { + return `Client module "${getRelativeFilePath(filePath)}" imports server code: + +${imp.importStatement} + +This is not supported in the client code.`; +} + +function getRelativeFilePath(filePath: string): string { + return filePath.replace(waspProjectDirAbsPath, ""); +} diff --git a/waspc/e2e-test/test-outputs/waspJob-golden/files.manifest b/waspc/e2e-test/test-outputs/waspJob-golden/files.manifest index c04620f749..b8a2c274ef 100644 --- a/waspc/e2e-test/test-outputs/waspJob-golden/files.manifest +++ b/waspc/e2e-test/test-outputs/waspJob-golden/files.manifest @@ -279,6 +279,7 @@ waspJob/.wasp/out/web-app/src/vite-env.d.ts waspJob/.wasp/out/web-app/tsconfig.json waspJob/.wasp/out/web-app/tsconfig.node.json waspJob/.wasp/out/web-app/vite.config.ts +waspJob/.wasp/out/web-app/vite/detectServerImports.ts waspJob/.waspignore waspJob/.wasproot waspJob/main.wasp diff --git a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/.waspchecksums b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/.waspchecksums index 3b666d1aac..072d7080ae 100644 --- a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/.waspchecksums +++ b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/.waspchecksums @@ -774,13 +774,20 @@ "file", "web-app/tsconfig.node.json" ], - "30fed1f6aa4710deb690d1ae47628066f34afda7b633f0c696fafcda9e2dbc1e" + "d665257a3ee4ec198afc7350d71bc5b9ab2c2febb94d3b2f8f274a98db4fb44b" ], [ [ "file", "web-app/vite.config.ts" ], - "64256d93d81f6192f419e836ed4484f0e8c1e28f018bfb3213851608200594ac" + "70cdf971f9f371c886c2d86fc5a7832a520e06695724f9af62428b968b45705a" + ], + [ + [ + "file", + "web-app/vite/detectServerImports.ts" + ], + "902512e245e0fe0340b8b6ec7eb780a2f6963af283930c25993ee0e6ebc42493" ] ] \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/web-app/tsconfig.node.json b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/web-app/tsconfig.node.json index d90bb30d80..a7f26075bd 100644 --- a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/web-app/tsconfig.node.json +++ b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/web-app/tsconfig.node.json @@ -6,5 +6,5 @@ "moduleResolution": "bundler", "allowSyntheticDefaultImports": true }, - "include": ["vite.config.ts", "./src/ext-src/vite.config.ts"] + "include": ["vite.config.ts", "./src/ext-src/vite.config.ts", "./vite/detectServerImports.ts"] } diff --git a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/web-app/vite.config.ts b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/web-app/vite.config.ts index ed2d3dd5f0..b17dbff5fa 100644 --- a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/web-app/vite.config.ts +++ b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/web-app/vite.config.ts @@ -2,6 +2,7 @@ import { mergeConfig } from "vite"; import react from "@vitejs/plugin-react"; import { defaultExclude } from "vitest/config" +import { detectServerImports } from "./vite/detectServerImports" // Ignoring the TS error because we are importing a file outside of TS root dir. // @ts-ignore @@ -10,7 +11,10 @@ const _waspUserProvidedConfig = customViteConfig const defaultViteConfig = { base: "/", - plugins: [react()], + plugins: [ + react(), + detectServerImports(), + ], optimizeDeps: { exclude: ['wasp'] }, diff --git a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/web-app/vite/detectServerImports.ts b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/web-app/vite/detectServerImports.ts new file mode 100644 index 0000000000..05a2cfa076 --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/web-app/vite/detectServerImports.ts @@ -0,0 +1,55 @@ +import { type Plugin } from "vite"; + +const waspProjectDirAbsPath = "/Users/ilakovac/dev/wasp/waspc/e2e-test/test-outputs/waspJob-current/waspJob/"; + +export function detectServerImports(): Plugin { + return { + name: 'wasp-detect-server-imports', + transform(code, filePath) { + const isInDotWaspFolder = filePath.includes("/.wasp/"); + + // We don't want to check for server imports in the Wasp + // framework code. + if (isInDotWaspFolder) { + return; + } + + const imports = getImportsFromCode(code); + + for (const imp of imports) { + if (imp.moduleName.startsWith("wasp/server")) { + throw new Error(getServerImportErrorMessage(imp, filePath)); + } + } + }, + }; +} + +type Import = { + importStatement: string; + moduleName: string; +} + +const importStatementRegex = /\s*import\s+(?:(?:[\w*\s{},]*)\s+from\s+)?(['"`])([^'"`]+)\1\s*/g; + +function* getImportsFromCode(code: string): Generator { + const matches = code.matchAll(importStatementRegex); + for (const match of matches) { + yield { + importStatement: match[0].trim(), + moduleName: match[2], + }; + } +} + +function getServerImportErrorMessage(imp: Import, filePath: string): string { + return `Client module "${getRelativeFilePath(filePath)}" imports server code: + +${imp.importStatement} + +This is not supported in the client code.`; +} + +function getRelativeFilePath(filePath: string): string { + return filePath.replace(waspProjectDirAbsPath, ""); +} diff --git a/waspc/e2e-test/test-outputs/waspMigrate-golden/files.manifest b/waspc/e2e-test/test-outputs/waspMigrate-golden/files.manifest index a5f519b0fb..151da17c16 100644 --- a/waspc/e2e-test/test-outputs/waspMigrate-golden/files.manifest +++ b/waspc/e2e-test/test-outputs/waspMigrate-golden/files.manifest @@ -238,6 +238,7 @@ waspMigrate/.wasp/out/web-app/src/vite-env.d.ts waspMigrate/.wasp/out/web-app/tsconfig.json waspMigrate/.wasp/out/web-app/tsconfig.node.json waspMigrate/.wasp/out/web-app/vite.config.ts +waspMigrate/.wasp/out/web-app/vite/detectServerImports.ts waspMigrate/.waspignore waspMigrate/.wasproot waspMigrate/main.wasp diff --git a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/.waspchecksums b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/.waspchecksums index bb773d7cbc..1e337a549d 100644 --- a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/.waspchecksums +++ b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/.waspchecksums @@ -676,13 +676,20 @@ "file", "web-app/tsconfig.node.json" ], - "30fed1f6aa4710deb690d1ae47628066f34afda7b633f0c696fafcda9e2dbc1e" + "d665257a3ee4ec198afc7350d71bc5b9ab2c2febb94d3b2f8f274a98db4fb44b" ], [ [ "file", "web-app/vite.config.ts" ], - "64256d93d81f6192f419e836ed4484f0e8c1e28f018bfb3213851608200594ac" + "70cdf971f9f371c886c2d86fc5a7832a520e06695724f9af62428b968b45705a" + ], + [ + [ + "file", + "web-app/vite/detectServerImports.ts" + ], + "20b838823c154bd928e1e8bc4c8f10a1b3b4c090b990e87e75c44238ac0da8f2" ] ] \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/web-app/tsconfig.node.json b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/web-app/tsconfig.node.json index d90bb30d80..a7f26075bd 100644 --- a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/web-app/tsconfig.node.json +++ b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/web-app/tsconfig.node.json @@ -6,5 +6,5 @@ "moduleResolution": "bundler", "allowSyntheticDefaultImports": true }, - "include": ["vite.config.ts", "./src/ext-src/vite.config.ts"] + "include": ["vite.config.ts", "./src/ext-src/vite.config.ts", "./vite/detectServerImports.ts"] } diff --git a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/web-app/vite.config.ts b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/web-app/vite.config.ts index ed2d3dd5f0..b17dbff5fa 100644 --- a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/web-app/vite.config.ts +++ b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/web-app/vite.config.ts @@ -2,6 +2,7 @@ import { mergeConfig } from "vite"; import react from "@vitejs/plugin-react"; import { defaultExclude } from "vitest/config" +import { detectServerImports } from "./vite/detectServerImports" // Ignoring the TS error because we are importing a file outside of TS root dir. // @ts-ignore @@ -10,7 +11,10 @@ const _waspUserProvidedConfig = customViteConfig const defaultViteConfig = { base: "/", - plugins: [react()], + plugins: [ + react(), + detectServerImports(), + ], optimizeDeps: { exclude: ['wasp'] }, diff --git a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/web-app/vite/detectServerImports.ts b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/web-app/vite/detectServerImports.ts new file mode 100644 index 0000000000..1337434aa5 --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/web-app/vite/detectServerImports.ts @@ -0,0 +1,55 @@ +import { type Plugin } from "vite"; + +const waspProjectDirAbsPath = "/Users/ilakovac/dev/wasp/waspc/e2e-test/test-outputs/waspMigrate-current/waspMigrate/"; + +export function detectServerImports(): Plugin { + return { + name: 'wasp-detect-server-imports', + transform(code, filePath) { + const isInDotWaspFolder = filePath.includes("/.wasp/"); + + // We don't want to check for server imports in the Wasp + // framework code. + if (isInDotWaspFolder) { + return; + } + + const imports = getImportsFromCode(code); + + for (const imp of imports) { + if (imp.moduleName.startsWith("wasp/server")) { + throw new Error(getServerImportErrorMessage(imp, filePath)); + } + } + }, + }; +} + +type Import = { + importStatement: string; + moduleName: string; +} + +const importStatementRegex = /\s*import\s+(?:(?:[\w*\s{},]*)\s+from\s+)?(['"`])([^'"`]+)\1\s*/g; + +function* getImportsFromCode(code: string): Generator { + const matches = code.matchAll(importStatementRegex); + for (const match of matches) { + yield { + importStatement: match[0].trim(), + moduleName: match[2], + }; + } +} + +function getServerImportErrorMessage(imp: Import, filePath: string): string { + return `Client module "${getRelativeFilePath(filePath)}" imports server code: + +${imp.importStatement} + +This is not supported in the client code.`; +} + +function getRelativeFilePath(filePath: string): string { + return filePath.replace(waspProjectDirAbsPath, ""); +} From 153ece5fce2e4b1b6fb26412d799c7b837a924b1 Mon Sep 17 00:00:00 2001 From: Mihovil Ilakovac Date: Tue, 14 Jan 2025 15:33:39 +0100 Subject: [PATCH 05/22] Fix todoApp server import --- waspc/examples/todoApp/src/pages/About.jsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/waspc/examples/todoApp/src/pages/About.jsx b/waspc/examples/todoApp/src/pages/About.jsx index 354f7111c3..fdc9a50859 100644 --- a/waspc/examples/todoApp/src/pages/About.jsx +++ b/waspc/examples/todoApp/src/pages/About.jsx @@ -1,7 +1,5 @@ import { Link } from "wasp/client/router"; -import { HttpError } from "wasp/server" - import React from 'react' const About = () => { From 01e431e4aa4734a09c8bb058b1b9720aae685d60 Mon Sep 17 00:00:00 2001 From: Mihovil Ilakovac Date: Thu, 9 Jan 2025 12:30:43 +0100 Subject: [PATCH 06/22] "Going into production" docs update (#2373) --- .../_addExternalAuthEnvVarsReminder.md | 3 - .../deployment/_building-the-web-client.md | 17 - web/docs/deployment/ci-cd.md | 141 +++++ web/docs/deployment/database.md | 76 +++ .../DeploymentOptionsGrid.css | 0 .../DeploymentOptionsGrid.tsx | 13 +- .../deployment-methods/DeploymentTag.tsx | 18 + .../deployment-methods/GuideLink.css | 37 ++ .../deployment-methods/GuideLink.tsx | 22 + .../_addExternalAuthEnvVarsReminder.md | 3 + .../_building-the-web-client.md | 17 + .../deployment-methods}/cli.md | 60 ++- .../deployment-methods}/overview.md | 5 +- .../deployment-methods/paas.md} | 489 ++++++++++-------- .../deployment-methods/self-hosted.md | 129 +++++ web/docs/deployment/env-vars.md | 53 ++ web/docs/deployment/extras.md | 9 + web/docs/deployment/intro.md | 54 ++ web/docs/project/EnvVarsTable.css | 28 + web/docs/project/EnvVarsTable.tsx | 61 +++ web/docs/project/_clientEnvVarsNote.md | 4 + web/docs/project/env-vars.md | 230 ++++++-- web/sidebars.js | 37 +- web/src/components/Tag.tsx | 6 + web/static/img/deploying/self-hosting.png | Bin 0 -> 37429 bytes web/static/img/deploying/wasp-app-flow.gif | Bin 0 -> 172012 bytes 26 files changed, 1198 insertions(+), 314 deletions(-) delete mode 100644 web/docs/advanced/deployment/_addExternalAuthEnvVarsReminder.md delete mode 100644 web/docs/advanced/deployment/_building-the-web-client.md create mode 100644 web/docs/deployment/ci-cd.md create mode 100644 web/docs/deployment/database.md rename web/docs/{advanced/deployment => deployment/deployment-methods}/DeploymentOptionsGrid.css (100%) rename web/docs/{advanced/deployment => deployment/deployment-methods}/DeploymentOptionsGrid.tsx (74%) create mode 100644 web/docs/deployment/deployment-methods/DeploymentTag.tsx create mode 100644 web/docs/deployment/deployment-methods/GuideLink.css create mode 100644 web/docs/deployment/deployment-methods/GuideLink.tsx create mode 100644 web/docs/deployment/deployment-methods/_addExternalAuthEnvVarsReminder.md create mode 100644 web/docs/deployment/deployment-methods/_building-the-web-client.md rename web/docs/{advanced/deployment => deployment/deployment-methods}/cli.md (85%) rename web/docs/{advanced/deployment => deployment/deployment-methods}/overview.md (98%) rename web/docs/{advanced/deployment/manually.md => deployment/deployment-methods/paas.md} (66%) create mode 100644 web/docs/deployment/deployment-methods/self-hosted.md create mode 100644 web/docs/deployment/env-vars.md create mode 100644 web/docs/deployment/extras.md create mode 100644 web/docs/deployment/intro.md create mode 100644 web/docs/project/EnvVarsTable.css create mode 100644 web/docs/project/EnvVarsTable.tsx create mode 100644 web/docs/project/_clientEnvVarsNote.md create mode 100644 web/static/img/deploying/self-hosting.png create mode 100644 web/static/img/deploying/wasp-app-flow.gif diff --git a/web/docs/advanced/deployment/_addExternalAuthEnvVarsReminder.md b/web/docs/advanced/deployment/_addExternalAuthEnvVarsReminder.md deleted file mode 100644 index 29c532d974..0000000000 --- a/web/docs/advanced/deployment/_addExternalAuthEnvVarsReminder.md +++ /dev/null @@ -1,3 +0,0 @@ -:::tip Using an external auth method? -If your app is using an external authentication method(s) supported by Wasp (such as [Google](../../auth/social-auth/google#4-adding-environment-variables) or [GitHub](../../auth/social-auth/github#4-adding-environment-variables)), make sure to additionally set the necessary environment variables specifically required by these method(s). -::: diff --git a/web/docs/advanced/deployment/_building-the-web-client.md b/web/docs/advanced/deployment/_building-the-web-client.md deleted file mode 100644 index d74cae1f36..0000000000 --- a/web/docs/advanced/deployment/_building-the-web-client.md +++ /dev/null @@ -1,17 +0,0 @@ -To build the web app, position yourself in `.wasp/build/web-app` directory: - -``` -cd .wasp/build/web-app -``` - -Run - -``` -npm install && REACT_APP_API_URL= npm run build -``` - -where `` is the URL of the Wasp server that you previously deployed. - -:::caution Client Environment Variables -Remember, if you have manually defined any other [client-side environment variables](../../project/env-vars#client-env-vars) in your project, make sure to add them to the command above when [building your client](../../project/env-vars#client-env-vars-1) -::: \ No newline at end of file diff --git a/web/docs/deployment/ci-cd.md b/web/docs/deployment/ci-cd.md new file mode 100644 index 0000000000..d2a2b6190e --- /dev/null +++ b/web/docs/deployment/ci-cd.md @@ -0,0 +1,141 @@ +--- +title: CI/CD Scenarios +--- + +Setting up a CI/CD pipeline is an optional but highly recommended part of deploying applications. + +**Continuous Integration (CI)** involves verifying/testing code changes through an automated process whenever code is pushed to the repository. This helps us catch bugs early and make sure that our app works. + +**Continuous Deployment (CD)** refers to the automatic deployment of code changes to the production environment. This is commonly know as "push to deploy" and frees developers from having to manually deploy code changes. + +## Running tests in CI + +### End to end tests + +End to end (e2e) tests simulate real user using your app and you can test different scenarios like login, adding items to cart, etc. Writing end to end tests frees you from +manually testing your app after every change. + +**To run e2e tests with Wasp in the CI**, you'll need to: + +1. Install Wasp in the CI environment. +2. Run your app (with the database) in the CI environment. +3. Run the e2e tests against the running app. + +#### Example app + +We'll show you how to run end-to-end tests in CI using the [Github Actions](https://github.com/features/actions) as our CI and the [Playwright](https://playwright.dev/) as our e2e testing framework. + +1. Check our example app and its e2e tests in the [e2e-tests](https://github.com/wasp-lang/e2e-test-example/tree/main/e2e-tests) directory. + + You can copy the `e2e-tests` directory to your own project and modify it to fit your app. This will enable you to run the e2e tests locally. + +
+ Example e2e test + + ```ts + import { expect, test } from '@playwright/test' + import { generateRandomUser, logUserIn } from './utils' + + const user = generateRandomUser() + + test.describe('basic user flow test', () => { + test('log in and add task', async ({ page }) => { + await logUserIn({ page, user }) + await expect(page).toHaveURL('/') + await expect(page.locator('body')).toContainText('No tasks yet.') + + // Add a task + await page.fill('input[name="description"]', 'First task') + await page.click('input:has-text("Create task")') + await expect(page.locator('body')).toContainText('First task') + }) + }) + ``` + +
+ +2. To run the tests in the Github Actions CI, you'll need to create a workflow file in your repository. + + You should create a `.github/workflows/e2e-tests.yml` file in your repository. You can copy the contents of the [e2e-tests.yml](https://github.com/wasp-lang/e2e-test-example/blob/main/.github/workflows/e2e-tests.yml) file from our example app. + +### Unit tests + +Unit tests test pieces of your code logic in isolation. They are much simpler and faster than e2e tests, but they don't simulate the real user interaction with your app. + +You can use Wasp's built in [client tests](../project/testing.md) support to test the client side code of your app. You are free to use any testing framework for the server side code. + +**You'd run the unit tests in the CI** in a similar way as the e2e tests: + +1. Install Wasp in the CI environment. +2. Run the client tests with `wasp test client run`. +3. Run the server tests with your testing framework. + +## Continuous deployment + +We'll look at two ways you can use the CI/CD pipeline to deploy your Wasp app: + +1. Package the server and client with Docker. +2. Deploy the client as static files. + +### Package the server and client with Docker + +The most common way to package your app for deployment is using Docker images. This way you can easily deploy the same image to different environments (staging, production, etc.). + +**To build the app as a Docker image**, you'll need to: + +1. Install Docker in the CD environment. +2. Build the app with `wasp build`. +3. Build the Docker image and push it to a Docker registry: + - for our server app + - for our client app +4. For some providers: notify them to deploy the new app version. + +:::info What is a Docker Registry? + +Docker Registry is a place where you can store your Docker images and then your deployment provider can pull them from there. The most common Docker Registry is the [Docker Hub](https://hub.docker.com/), but you can also use other registries like the [Github Container Registry (GHCR)](https://docs.github.com/en/packages/guides/about-github-container-registry). + +::: + +#### Example deployment + +We'll take a look at our Coolify deployment example in the [deployment](./deployment-methods/self-hosted.md#coolify) section. We are using Github Actions to build the Docker images and their Github Container Registry (GHCR) to store them. + +Let's go through the [deploy.yml](https://gist.github.com/infomiho/ad6fade7396498ae32a931ca563a4524#file-deploy-yml) file in the Coolify guide: + +1. First, we **authenticate with the Github Container Registry (GHCR)**. + + We are using the `docker/login-action` action to authenticate with the GHCR. + +2. Then, we **prepare the Docker image metadata** for later use. + + We are using the `docker/metadata-action` action to prepare some extra info that we'll use later in the deployment process. + +3. Next, we **build the Wasp app** with `wasp build`. + + This gives our server and the client app in the `.wasp/build` folder. + +4. Then, we **package the server app** into a Docker image and **push it to the GHCR**. + + We use the `Dockerfile` in the `.wasp/build` directory to build and push the server Docker image using the `docker/build-push-action` action. + +5. Next, we create a `Dockerfile` for our client and then **package the client app** into a Docker image and **push it to the GHCR**. + + We create a `Dockerfile` that uses a simple Go static server to serve the client app. We again use the `docker/build-push-action` action to build and push the client Docker image. + +6. Finally, we notify Coolify using their Webhook API to **deploy our new app version**. + + And now you can open the [deploy.yml](https://gist.github.com/infomiho/ad6fade7396498ae32a931ca563a4524#file-deploy-yml) file in the Coolify guide and see the full deployment process. + +### Static build of the client + +Wasp's client app is a single page application (SPA) which you build into static HTML, CSS, and JS files that you can upload to any hosting provider that supports serving static files. This means that for the client app, you don't need to use Docker images if don't want to. It's usually cheaper to host static files than to host Docker images. + +**To deploy the client app as static files**, you'll need to: + +1. Build the app with `wasp build` in the CD environment. +2. Build the client app with `npm run build`. +3. Upload the static files to your hosting provider. + + + +Check out our instructions for deploying the client app to [Netlify](#) or [Cloudflare](#) where you can check out the example deployment using Github Actions. diff --git a/web/docs/deployment/database.md b/web/docs/deployment/database.md new file mode 100644 index 0000000000..ee02d65768 --- /dev/null +++ b/web/docs/deployment/database.md @@ -0,0 +1,76 @@ +--- +title: Database +--- + +In this section, we'll discuss what happens with the database when your app goes live. When you develop your app locally, you probably use a local dev database (started with `wasp start db` or some other way). However, when it's time to deploy your app, you'll need to set up a production database. + +### Production database requirements + +The server app that Wasp generates uses a PostgreSQL database. The only requirement from Wasp's point of view is that the database is accessible from the server via the `DATABASE_URL` server env variable. + +It can be a PostgreSQL database running on the same server as the server app, or it can be a managed PostgreSQL database service like [Fly Postgres](https://fly.io/docs/postgres/), [AWS RDS](https://aws.amazon.com/rds/), or some other service. + +## Migrations + +Every time you make a change in your [Prisma schema](../data-model/prisma-file.md) e.g. adding a new model, changing a field type, etc., you need to create a migration. Migrations are some code that describes the change you made in the schema, and they are used to apply the change to the database. + +The benefit of migrations is that you can apply the same change to multiple databases. If there are multiple people working on the project, they can all apply the same changes to their local databases. When you deploy the app to production, the same chaanges are applied to the production database. + +### Creating migrations + +After you made a change in the Prisma schema, you can create a migration by running the following command: + +```bash +wasp db migrate-dev +``` + +This command will create a new migration in the `migrations` directory. The migration is a set of SQL commands that describe the change you made in the schema. + +### Applying migrations + +**In development**, the migrations are applied as soon as you run the `wasp start` command. + +**In production**, the server app first checks if there are any new migrations that need to be applied, and if there are, it applies them before starting the server. This way, the database schema is always in sync with the Prisma schema. + +:::note How it works + +In the built server app, there are two npm scripts: `start` and `start-production`. The `start` script is used in development, and the `start-production` script is used in production. The `start-production` script first applies any pending migrations before starting the server. + +::: + +The migrations might fail to apply if there is a conflict with the existing data in the database. In that case, you'll need to fix the migration and try again. + +### Debugging failed migrations + +If a migration fails to apply, the server app will log the error message and stop. You should then connect to the production database and see what went wrong. If you check the `_prisma_migrations` table, you'll see the failed migration there. + +You can try resolving the erorr e.g. if you tried adding a `@unique` constraint to a field that already has duplicate values: + +1. Remove any duplicate values from the database +2. Remove the failed migration from the `_prisma_migrations` table +3. Try applying the migration again by restarting the server app + +:::tip Viewing the `_prisma_migrations` table + +You can't use the `wasp db studio` command to view the `_prisma_migrations` table in the production database, but you can use a database management tool like [DBeaver](https://dbeaver.io/) or [pgAdmin](https://www.pgadmin.org/). +::: + +## Connect to the production database + +**In development**, you can use the `wasp db studio` command to open a web-based database management tool that allows you to inspect the database. + +You can use the same tool to inspect the **production database**, but you'll need to set the `DATABASE_URL` env variable to point to the production database. Set the `DATABASE_URL` env variable in your terminal before running the `wasp db studio` command: + +```bash +DATABASE_URL="postgresql://user:password@host:port/dbname" wasp db studio +``` + +:::caution Be careful with the `DATABASE_URL` env variable + +Setting the `DATABASE_URL` env variable in the `.env.server` file to point to your production database also works, but then you might forget to remove it and you could accidentally make changes to the production database when you run `wasp start` in development. + +That's why we recommend setting the `DATABASE_URL` env variable in the terminal to avoid this. + +::: + +If you are looking how to connect to a Fly.io production database, we wrote a [guide](https://github.com/wasp-lang/learning-materials/?tab=readme-ov-file#running-wasp-db-studio-on-production-db) on how to do that. diff --git a/web/docs/advanced/deployment/DeploymentOptionsGrid.css b/web/docs/deployment/deployment-methods/DeploymentOptionsGrid.css similarity index 100% rename from web/docs/advanced/deployment/DeploymentOptionsGrid.css rename to web/docs/deployment/deployment-methods/DeploymentOptionsGrid.css diff --git a/web/docs/advanced/deployment/DeploymentOptionsGrid.tsx b/web/docs/deployment/deployment-methods/DeploymentOptionsGrid.tsx similarity index 74% rename from web/docs/advanced/deployment/DeploymentOptionsGrid.tsx rename to web/docs/deployment/deployment-methods/DeploymentOptionsGrid.tsx index e29452acfe..9d3125f454 100644 --- a/web/docs/advanced/deployment/DeploymentOptionsGrid.tsx +++ b/web/docs/deployment/deployment-methods/DeploymentOptionsGrid.tsx @@ -4,14 +4,19 @@ import './DeploymentOptionsGrid.css' export function DeploymentOptionsGrid() { const deploymentMethods = [ { - title: 'Using Wasp CLI', + title: 'Wasp CLI', description: 'One command deployment & redeployment', linkToDocs: '/docs/advanced/deployment/cli', }, { - title: 'Deploying Manually', - description: 'Build the app and deploy it manually', - linkToDocs: '/docs/advanced/deployment/manually', + title: 'Platform as a Service (PaaS)', + description: 'Deploy your app manually to the cloud', + linkToDocs: '/docs/deployment/deployment-methods/paas', + }, + { + title: 'Self-hosting', + description: 'Use your own servers to host your app', + linkToDocs: '/docs/deployment/deployment-methods/self-hosted', }, ] return ( diff --git a/web/docs/deployment/deployment-methods/DeploymentTag.tsx b/web/docs/deployment/deployment-methods/DeploymentTag.tsx new file mode 100644 index 0000000000..0c12a50221 --- /dev/null +++ b/web/docs/deployment/deployment-methods/DeploymentTag.tsx @@ -0,0 +1,18 @@ +import React from 'react' +import { Tag } from '../../../src/components/Tag' + +// Used to mark something as related to the server +export function Server() { + return server +} + +// Used to mark something as related to the client +export function Client() { + return client +} + +// Used to mark something as related to the database +// e.g. in the deployment guides. +export function Database() { + return database +} diff --git a/web/docs/deployment/deployment-methods/GuideLink.css b/web/docs/deployment/deployment-methods/GuideLink.css new file mode 100644 index 0000000000..e8963fddb9 --- /dev/null +++ b/web/docs/deployment/deployment-methods/GuideLink.css @@ -0,0 +1,37 @@ +.guide-link { + display: flex; + flex-direction: column; + justify-content: center; + border: 1px solid var(--ifm-color-emphasis-300); + border-radius: var(--ifm-global-radius); + padding: 1rem; + transition: all 0.1s ease-in-out; +} + +.guide-link:hover { + border-color: var(--ifm-pagination-nav-color-hover); +} + +.guide-link .subtitle { + margin: 0; + color: var(--ifm-color-primary-contrast-foreground); + text-transform: uppercase; + font-weight: 700; + font-size: 0.7em; + border: 1px solid var(--ifm-color-primary-contrast-foreground); + background-color: var(--ifm-color-primary-contrast-background); + padding: 0.2em 0.5em; + border-radius: 3px; +} + +.guide-link h3 { + margin: 0; + margin-top: 5px; + color: var(--ifm-link-color); +} + +.guide-link .description { + margin: 0; + color: var(--ifm-color-secondary-contrast-foreground); + font-size: 0.9em; +} \ No newline at end of file diff --git a/web/docs/deployment/deployment-methods/GuideLink.tsx b/web/docs/deployment/deployment-methods/GuideLink.tsx new file mode 100644 index 0000000000..7b84fedee7 --- /dev/null +++ b/web/docs/deployment/deployment-methods/GuideLink.tsx @@ -0,0 +1,22 @@ +import React from 'react' +import './GuideLink.css' + +export function GuideLink({ + linkToGuide, + title, + description, +}: { + linkToGuide: string + title: string + description: string +}) { + return ( + +
+ guide +
+

{title} ยป

+

{description}

+
+ ) +} diff --git a/web/docs/deployment/deployment-methods/_addExternalAuthEnvVarsReminder.md b/web/docs/deployment/deployment-methods/_addExternalAuthEnvVarsReminder.md new file mode 100644 index 0000000000..eb666ac952 --- /dev/null +++ b/web/docs/deployment/deployment-methods/_addExternalAuthEnvVarsReminder.md @@ -0,0 +1,3 @@ +:::tip Using an external auth method? +If your app is using an external authentication method(s) supported by Wasp (such as [Google](/auth/social-auth/google.md#4-adding-environment-variables) or [GitHub](/auth/social-auth/github.md#4-adding-environment-variables)), make sure to additionally set the necessary environment variables specifically required by these method(s). +::: diff --git a/web/docs/deployment/deployment-methods/_building-the-web-client.md b/web/docs/deployment/deployment-methods/_building-the-web-client.md new file mode 100644 index 0000000000..09cc4f604b --- /dev/null +++ b/web/docs/deployment/deployment-methods/_building-the-web-client.md @@ -0,0 +1,17 @@ +To build the web app, position yourself in `.wasp/build/web-app` directory: + +``` +cd .wasp/build/web-app +``` + +Run + +``` +npm install && REACT_APP_API_URL= npm run build +``` + +where `` is the URL of the Wasp server that you previously deployed. + +:::caution Client Env Variables +Remember, if you have defined any other [client-side env variables](../../project/env-vars.md#defining-env-vars-in-development) in your project, make sure to add them to the command above when [building your client](../env-vars.md#client-env-vars) +::: diff --git a/web/docs/advanced/deployment/cli.md b/web/docs/deployment/deployment-methods/cli.md similarity index 85% rename from web/docs/advanced/deployment/cli.md rename to web/docs/deployment/deployment-methods/cli.md index e99cc584c4..79a20848d2 100644 --- a/web/docs/advanced/deployment/cli.md +++ b/web/docs/deployment/deployment-methods/cli.md @@ -1,5 +1,5 @@ --- -title: Deploying with the Wasp CLI +title: Wasp CLI --- import { Required } from '@site/src/components/Tag'; @@ -11,14 +11,14 @@ The command automates the manual deployment process and is the recommended way o Wasp supports automated deployment to the following providers: -- [Fly.io](#flyio) - they offer 5$ free credit each month -- Railway (coming soon, track it here [#1157](https://github.com/wasp-lang/wasp/pull/1157)) +- [Fly.io](#flyio) +- Railway (track it here [#1157](https://github.com/wasp-lang/wasp/pull/1157)) ## Fly.io ### Prerequisites -Fly provides [free allowances](https://fly.io/docs/about/pricing/#plans) for up to 3 VMs (so deploying a Wasp app to a new account is free), but all plans require you to add your credit card information before you can proceed. If you don't, the deployment will fail. +Fly requires you to add your credit card information before you can deploy your apps. If you don't, the deployment will fail. Prices are usage based and can be [estimated here](https://fly.io/calculator). You can add the required credit card information on the [account's billing page](https://fly.io/dashboard/personal/billing). @@ -34,6 +34,10 @@ Using the Wasp CLI, you can easily deploy a new app to [Fly.io](https://fly.io) wasp deploy fly launch my-wasp-app mia ``` +:::caution Unique Name +Your app name (e.g. `my-wasp-app`) must be unique across all of Fly or deployment will fail. +::: + :::caution Specifying Org If your account is a member of more than one organization on Fly.io, you will need to specify under which one you want to execute the command. To do that, provide an additional `--org ` option. You can find out the names(slugs) of your organizations by running `fly orgs list`. ::: @@ -53,10 +57,6 @@ wasp deploy fly deploy The commands above use the app basename `my-wasp-app` and deploy it to the _Miami, Florida (US) region_ (called `mia`). Read more about Fly.io regions [here](#flyio-regions). -:::caution Unique Name -Your app name must be unique across all of Fly or deployment will fail. -::: - The basename is used to create all three app tiers, resulting in three separate apps in your Fly dashboard: - `my-wasp-app-client` @@ -64,6 +64,7 @@ The basename is used to create all three app tiers, resulting in three separate - `my-wasp-app-db` You'll notice that Wasp creates two new files in your project root directory: + - `fly-server.toml` - `fly-client.toml` @@ -116,10 +117,9 @@ We need to do this to keep our CORS configuration up to date. That's it, your app should be available at `https://mycoolapp.com`! ๐ŸŽ‰ -#### Adding www Subdomain - +#### Adding a `www` Subdomain -If you'd like to also access your app at `https://www.mycoolapp.com`, you can generate certs for the `www` subdomain. +If you'd like to also access your app at `https://www.mycoolapp.com`, you can generate certs for the `www` subdomain. ```shell wasp deploy fly cmd --context client certs create www.mycoolapp.com @@ -128,13 +128,19 @@ wasp deploy fly cmd --context client certs create www.mycoolapp.com Once you do that, you will need to add another DNS record for your domain. It should be a CNAME record for `www` with the value of your root domain. Here's an example: -| Type | Name | Value | TTL | -|-------|------|----------------------|------| -| CNAME | www | mycoolapp.com | 3600 | +| Type | Name | Value | TTL | +| ----- | ---- | ------------- | ---- | +| CNAME | www | mycoolapp.com | 3600 | + +With the CNAME record (Canonical name), you are assigning the `www` subdomain as an alias to the root domain. + +Your app should now be available both at the root domain `https://mycoolapp.com` and the `www` sub-domain `https://www.mycoolapp.com`. -With the CNAME record (Canonical name), you are assigning the `www` subdomain as an alias to the root domain. +:::caution CORS Configuration -Your app should now be available both at the root domain `https://mycoolapp.com` and the `www` sub-domain `https://www.mycoolapp.com`! ๐ŸŽ‰ +Using the `www` and `non-www` domains at the same time will require you to update your CORS configuration to allow both domains. You'll need to provide [custom CORS configuration](https://gist.github.com/infomiho/5ca98e5e2161df4ea78f76fc858d3ca2) in your server app to allow requests from both domains. + +::: ## API Reference @@ -162,6 +168,7 @@ wasp deploy fly deploy ``` #### Environment Variables + ##### Server If you are deploying an app that requires any other environment variables (like social auth secrets), you can set them with the `--server-secret` option: @@ -242,7 +249,7 @@ If you've added any [client-side environment variables](../../project/env-vars#c REACT_APP_ANOTHER_VAR=somevalue wasp deploy fly deploy ``` -Make sure to add your client-side environment variables every time you redeploy with the above command [to ensure they are included in the build process](../../project/env-vars#client-env-vars-1)! +Make sure to add your client-side environment variables every time you redeploy with the above command [to ensure they are included in the build process](../../project/env-vars#client-env-vars-1). ### `cmd` @@ -253,12 +260,15 @@ wasp deploy fly cmd secrets list --context server ``` ### Environment Variables + #### Server Secrets If your app requires any other server-side environment variables (like social auth secrets), you can set them: + 1. initially in the `launch` command with the [`--server-secret` option](#environment-variables), -or + or 2. after the app has already been deployed by using the `secrets set` command: + ``` wasp deploy fly cmd secrets set GOOGLE_CLIENT_ID=<...> GOOGLE_CLIENT_SECRET=<...> --context=server ``` @@ -271,13 +281,23 @@ If you've added any [client-side environment variables](../../project/env-vars#c REACT_APP_ANOTHER_VAR=somevalue wasp deploy fly launch my-wasp-app mia ``` -or +or ```shell REACT_APP_ANOTHER_VAR=somevalue wasp deploy fly deploy ``` -Please note, you should do this for every deployment, not just the first time you set up the variables! +Please note, you should do this for **every deployment**, not just the first time you set up the variables. One way to make sure you don't forget to add them is to create a `deploy` script in your `package.json` file: + +```json title="package.json" +{ + "scripts": { + "deploy": "REACT_APP_ANOTHER_VAR=somevalue wasp deploy fly deploy" + } +} +``` + +Then you can run `npm run deploy` to deploy your app. ### Fly.io Regions diff --git a/web/docs/advanced/deployment/overview.md b/web/docs/deployment/deployment-methods/overview.md similarity index 98% rename from web/docs/advanced/deployment/overview.md rename to web/docs/deployment/deployment-methods/overview.md index 4d1ac99110..7e85fb58cf 100644 --- a/web/docs/advanced/deployment/overview.md +++ b/web/docs/deployment/deployment-methods/overview.md @@ -5,9 +5,10 @@ title: Overview import { DeploymentOptionsGrid } from './DeploymentOptionsGrid.tsx'; Wasp apps are full-stack apps that consist of: + - A Node.js server. - A static client. -- A PostgreSQL database. +- A PostgreSQL database. You can deploy each part **anywhere** where you can usually deploy Node.js apps or static apps. For example, you can deploy your client on [Netlify](https://www.netlify.com/), the server on [Fly.io](https://fly.io/), and the database on [Neon](https://neon.tech/). @@ -18,6 +19,7 @@ To make deploying as smooth as possible, Wasp also offers a single-command deplo Regardless of how you choose to deploy your app (i.e., manually or using the Wasp CLI), you'll need to know about some common patterns covered below. ## Customizing the Dockerfile + By default, Wasp generates a multi-stage Dockerfile. This file is used to build and run a Docker image with the Wasp-generated server code. It also runs any pending migrations. @@ -37,6 +39,7 @@ A few things to keep in mind: Read more in the official Docker docs on [multi-stage builds](https://docs.docker.com/build/building/multi-stage/). To see what your project's (potentially combined) Dockerfile will look like, run: + ```shell wasp dockerfile ``` diff --git a/web/docs/advanced/deployment/manually.md b/web/docs/deployment/deployment-methods/paas.md similarity index 66% rename from web/docs/advanced/deployment/manually.md rename to web/docs/deployment/deployment-methods/paas.md index 404c1abef0..fe284d967a 100644 --- a/web/docs/advanced/deployment/manually.md +++ b/web/docs/deployment/deployment-methods/paas.md @@ -1,32 +1,32 @@ --- -title: Deploying Manually +title: Platform as a Service (PaaS) --- import useBaseUrl from '@docusaurus/useBaseUrl'; import AddExternalAuthEnvVarsReminder from './\_addExternalAuthEnvVarsReminder.md' import BuildingTheWebClient from './\_building-the-web-client.md' import { Required } from '@site/src/components/Tag' +import { Server, Client, Database } from './DeploymentTag' This document explains how to build and prepare your Wasp app for deployment. -You can then deploy the built Wasp app wherever and however you want, as long as your provider/server -supports Wasp's build format. +You can then deploy the built Wasp app wherever and however you want, as long as your provider/server supports Wasp's build format. After going through the general steps that apply to all deployments, you can follow step-by-step guides for deploying your Wasp app to the most popular providers: -- [Fly.io](#flyio-server-and-database) -- [Netlify](#netlify-client) -- [Railway](#railway-server-client-and-database) -- [Heroku](#heroku-server-and-database) +- [Fly.io](#flyio) +- [Railway](#railway) +- [Heroku](#heroku) +- [Netlify](#netlify) +- [Cloudflare](#cloudflare) No worries, you can still deploy your app if your desired provider isn't on the list - it just means we don't yet have a step-by-step guide for you to follow. Feel free to [open a -PR](https://github.com/wasp-lang/wasp/edit/release/web/docs/advanced/deployment/manually.md) +PR](https://github.com/wasp-lang/wasp/edit/release/web/docs/deployment/deployment-methods/paas.md) if you'd like to write one yourself :) - ## Deploying a Wasp App Deploying a Wasp app comes down to the following: @@ -51,56 +51,27 @@ You won't be able to build the app if you are using SQLite as a database (which You'll have to [switch to PostgreSQL](../../data-model/backends#migrating-from-sqlite-to-postgresql) before deploying to production. ::: -### 2. Deploying the API Server (backend) +### 2. Deploying the API Server There's a Dockerfile that defines an image for building the server in the `.wasp/build` directory. -To run the server in production, deploy this Docker image to a hosting provider and ensure the required environment variables on the provider are correctly set up (the mechanism of setting these up is specific per provider). -All necessary environment variables are listed in the next section. - -#### Environment Variables - -Here are the environment variables your server will be looking for: - -- `DATABASE_URL` - - The URL of the PostgreSQL database you want your app to use (e.g., `postgresql://mydbuser:mypass@localhost:5432/nameofmydb`). - -- `WASP_WEB_CLIENT_URL` - - The URL where you plan to deploy your frontend app is running (e.g., `https://.netlify.app`). - The server needs to know about it to properly configure Same-Origin Policy (CORS) headers. - -- `WASP_SERVER_URL` - - The URL where the server is running (e.g., `https://.fly.dev`). - The server needs it to properly redirect users when logging in with OAuth providers like Google or GitHub. - -- `JWT_SECRET` ( if using Wasp Auth) +To run the server in production, deploy this Docker image to a hosting provider and make sure the required env variables are correctly set up. Usually, you use the provider's dashboard UI or a CLI tool to set up these env variables. - You only need this environment variable if you're using Wasp's `auth` features. - Set it to a random string at least 32 characters long (you can use an [online generator](https://djecrety.ir/)). - -- `PORT` - - The server's HTTP port number. This is where the server listens for requests (default: `3001`). - - - +Check the [required server env variables](../env-vars.md#server-env-vars) and make sure they are set up for your server. While these are the general instructions on deploying the server anywhere, we also have more detailed instructions for chosen providers below, so check that out for more guidance if you are deploying to one of those providers. -### 3. Deploying the Web Client (frontend) +### 3. Deploying the Web Client The command above will build the web client and put it in the `build/` directory in the `.wasp/build/web-app/`. -This is also the moment to provide any additional env vars for the client code, next to `REACT_APP_API_URL`. Check the [env vars docs](../../project/env-vars#client-env-vars-1) for more details. - Since the result of building is just a bunch of static files, you can now deploy your web client to any static hosting provider (e.g. Netlify, Cloudflare, ...) by deploying the contents of `.wasp/build/web-app/build/`. -### 4. Deploying the Database +### 4. Deploying the Database + + Any PostgreSQL database will do, as long as you provide the server with the correct `DATABASE_URL` env var and ensure that the database is accessible from the server. @@ -108,17 +79,18 @@ Any PostgreSQL database will do, as long as you provide the server with the corr We'll cover a few different deployment providers below: -- Fly.io (server and database) -- Netlify (client) -- Railway (server, client and database) -- Heroku (server and database) +- Fly.io +- Railway +- Heroku +- Netlify +- Cloudflare -## Fly.io (server and database) +## Fly.io {#flyio} We will show how to deploy the server and provision a database for it on Fly.io. :::tip We automated this process for you -If you want to do all of the work below with one command, you can use the [Wasp CLI](../../advanced/deployment/cli#flyio). +If you want to do all of the work below with one command, you can use the [Wasp CLI](./cli.md#flyio). Wasp CLI deploys the server, deploys the client, and sets up a database. It also gives you a way to redeploy (update) your app with a single command. @@ -180,6 +152,8 @@ Next, let's copy the `fly.toml` file up to our Wasp project dir for safekeeping. cp fly.toml ../../ ``` + + Next, add a few more environment variables for the server code. ```bash @@ -225,127 +199,15 @@ While we will improve this process in the future, in the meantime, you have a fe 1. Copy the `fly.toml` file to a versioned directory, like your Wasp project dir. - From there, you can reference it in `flyctl deploy --config ` commands, like above. +From there, you can reference it in `flyctl deploy --config ` commands, like above. 1. Backup the `fly.toml` file somewhere before running `wasp build`, and copy it into .wasp/build/ after. - When the `fly.toml` file exists in .wasp/build/ dir, you do not need to specify the `--config `. +When the `fly.toml` file exists in .wasp/build/ dir, you do not need to specify the `--config `. 1. Run `flyctl config save -a ` to regenerate the `fly.toml` file from the remote state stored in Fly.io. -## Netlify (client) - -We'll show how to deploy the client on Netlify. - -Netlify is a static hosting solution that is free for many use cases. You will need a Netlify account and [Netlify CLI](https://docs.netlify.com/cli/get-started/) installed to follow these instructions. - -Make sure you are logged in with Netlify CLI. You can check if you are logged in with `netlify status`, and if you are not, you can log in with `netlify login`. - -First, make sure you have [built the Wasp app](#1-generating-deployable-code). We'll build the client web app next. - - - -We can now deploy the client with: - -```shell -netlify deploy -``` - - - -Carefully follow the instructions i.e. do you want to create a new app or use an existing one, the team under which your app will reside etc. - - - -The final step is to run: - -```shell -netlify deploy --prod -``` - -That is it! Your client should be live at `https://.netlify.app` โœจ - -:::caution Redirecting URLs toward `index.html` -If you follow the instructions above, Netlify will use `netlify.toml` file that Wasp generates by default in `.wasp/build/web-app/`. This will correctly configure Netlify to redirect URLs toward `index.html`, which is important since Wasp is SPA. - -If you instead use another method of deployment to Netlify, e.g. do it via CI, make sure that Netlify picks up that `netlify.toml` file, or configure URL redirecting yourself manually on Netlify. - -It is recommended to deploy through the Netlify CLI in Github Actions. The first deploy can be through the website or manually to get a `NETLIFY_SITE_ID`, the following deploys can then be automatic. -::: - -:::note -Make sure you set this URL as the `WASP_WEB_CLIENT_URL` environment variable in your server hosting environment (e.g., Fly.io or Heroku). -::: - -### Deploying through Github Actions - -To enable automatic deployment of the frontend whenever you push to the `main` branch, you can set up a GitHub Actions workflow. To do this, create a file in your repository at `.github/workflows/deploy.yaml`. Feel free to rename `deploy.yaml` as long as the file type is not changed. - -Hereโ€™s an example configuration file to help you get started. This example workflow will trigger a deployment to Netlify whenever changes are pushed to the main branch. - -
-Example Github Action (Updated for 0.15.0) - -``` -name: Deploy Client to Netlify - -on: - push: - branches: - - main # Deploy on every push to the main branch - -jobs: - deploy: - runs-on: ubuntu-latest - - steps: - - name: Checkout Code - uses: actions/checkout@v2 - - - name: Setup Node.js - id: setup-node - uses: actions/setup-node@v4 - with: - node-version: '20' - - - name: Docker setup - uses: docker/setup-buildx-action@v3 - - - name: Install Wasp - run: curl -sSL https://get.wasp-lang.dev/installer.sh | sh -s -- -v 0.15.0 # Change to your Wasp version - - - name: Wasp Build - run: cd ./app && wasp build - - - name: Install dependencies and build Vite project - run: | - cd ./app/.wasp/build/web-app - npm install - REACT_APP_API_URL=${{ secrets.WASP_SERVER_URL }} npm run build - - - name: Deploy to Netlify - run: | - cd ./app/.wasp/build/web-app - npm install -g netlify-cli - netlify deploy --prod --dir=build --auth=$NETLIFY_AUTH_TOKEN --site=$NETLIFY_SITE_ID - - env: - NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }} - NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }} -``` -
- -
-How do I get the Environment Variables? - -- **`NETLIFY_AUTH_TOKEN` & `NETLIFY_SITE_ID`**: They can be configured through Netlify. - -- **`WASP_SERVER_URL`**: This is the link that points to your backend and is generally only available after **deploying the backend**. This variable can be skipped when the backend is not functional or not deployed, but be aware that backend-dependent functionalities may be broken. - -After obtaining the environment variables, you need to store these values securely in GitHub Secrets. -
- -## Railway (server, client and database) +## Railway {#railway} We will show how to deploy the client, the server, and provision a database on Railway. @@ -358,9 +220,9 @@ To get started, follow these steps: 1. Make sure your Wasp app is built by running `wasp build` in the project dir. 2. Create a [Railway](https://railway.app/) account - :::tip Free Tier - Sign up with your GitHub account to be eligible for the free tier - ::: +:::tip Free Tier +Sign up with your GitHub account to be eligible for the free tier +::: 3. Install the [Railway CLI](https://docs.railway.app/develop/cli#installation) 4. Run `railway login` and a browser tab will open to authenticate you. @@ -393,15 +255,17 @@ Let's deploy our server first: 1. Move into your app's `.wasp/build/` directory: - ```shell - cd .wasp/build - ``` +```shell +cd .wasp/build +``` 2. Link your app build to your newly created Railway project: - ```shell - railway link - ``` +```shell +railway link +``` + + 3. Go into the Railway dashboard and set up the required env variables: @@ -428,21 +292,23 @@ Railway will now locate the Dockerfile and deploy your server ๐Ÿ‘ 1. Next, change into your app's frontend build directory `.wasp/build/web-app`: - ```shell - cd web-app - ``` +```shell +cd web-app +``` 2. Create the production build, using the `server` domain as the `REACT_APP_API_URL`: - ```shell - npm install && REACT_APP_API_URL= npm run build - ``` +```shell +npm install && REACT_APP_API_URL= npm run build +``` 3. Next, we want to link this specific frontend directory to our project as well: - ```shell - railway link - ``` +```shell +railway link +``` + + 4. We need to configure Railway's static hosting for our client. @@ -549,21 +415,15 @@ When you make updates and need to redeploy: - run `railway up` in the `.wasp/build` directory (server) - repeat all the steps in the `.wasp/build/web-app` directory (client) -## Heroku (server and database) - -We will show how to deploy the server and provision a database for it on Heroku. +## Heroku {#heroku} -:::note -Heroku used to offer free apps under certain limits. However, as of November 28, 2022, they ended support for their free tier. https://blog.heroku.com/next-chapter - -As such, we recommend using an alternative provider like [Fly.io](#flyio) for your first apps. -::: +We will show how to deploy the server and provision a database for it on Heroku. You can check their [pricing page](https://www.heroku.com/pricing) for more information on their plans. You will need Heroku account, `heroku` [CLI](https://devcenter.heroku.com/articles/heroku-cli) and `docker` CLI installed to follow these instructions. Make sure you are logged in with `heroku` CLI. You can check if you are logged in with `heroku whoami`, and if you are not, you can log in with `heroku login`. -### Set Up a Heroku App +### Set up a Heroku app :::info You need to do this only once per Wasp app. @@ -578,17 +438,20 @@ heroku create Unless you have an external PostgreSQL database that you want to use, let's create a new database on Heroku and attach it to our app: ``` -heroku addons:create --app heroku-postgresql:mini +heroku addons:create --app heroku-postgresql:essential-0 ``` :::caution -Heroku does not offer a free plan anymore and `mini` is their cheapest database instance - it costs $5/mo. + +We are using the `essential-0` database instance. It's the cheapest database instance Heroku offers and it costs $5/mo. ::: Heroku will also set `DATABASE_URL` env var for us at this point. If you are using an external database, you will have to set it up yourself. The `PORT` env var will also be provided by Heroku, so the ones left to set are the `JWT_SECRET`, `WASP_WEB_CLIENT_URL` and `WASP_SERVER_URL` env vars: + + ``` heroku config:set --app JWT_SECRET= heroku config:set --app WASP_WEB_CLIENT_URL= @@ -599,7 +462,7 @@ heroku config:set --app WASP_SERVER_URL= web +heroku stack:set container --app ``` -App is still not deployed at this point. -This step might take some time, especially the very first time, since there are no cached docker layers. - -:::note Note for Apple Silicon Users -Apple Silicon users need to build a non-Arm image, so the above step will not work at this time. Instead of `heroku container:push`, users instead should: +Build the Docker image and push it to Heroku: ```shell -docker buildx build --platform linux/amd64 -t . -docker tag registry.heroku.com//web -docker push registry.heroku.com//web +heroku container:push --app web ``` -You are now ready to proceed to the next step. -::: +App is still not deployed at this point. +This step might take some time, especially the very first time, since there are no cached Docker layers. Deploy the pushed image and restart the app: @@ -663,8 +520,218 @@ If you wish to deploy an app leveraging [Jobs](../../advanced/jobs) that use `pg Read more: https://devcenter.heroku.com/articles/connecting-heroku-postgres#connecting-in-node-js ::: -## Koyeb (server, client and database) +## Netlify {#netlify} + +Netlify is a static hosting solution that is free for many use cases. You will need a Netlify account to follow these instructions. + +Make sure you are logged in with Netlify CLI. You can check if you are logged in with `npx netlify-cli status`, and if you are not, you can log in with `npx netlify-cli login`. + +First, make sure you have [built the Wasp app](#1-generating-deployable-code). We'll build the client web app next. + + + +We can now deploy the client with: -Check out the tutorial made by the team at Koyeb for detailed instructions on how to deploy a whole Wasp app on Koyeb: [Using Wasp to Build Full-Stack Web Applications on Koyeb](https://www.koyeb.com/tutorials/using-wasp-to-build-full-stack-web-applications-on-koyeb). +```shell +npx netlify-cli deploy +``` -The tutorial was written for Wasp v0.13. + + +Carefully follow the instructions: decide if you want to create a new app or use an existing one, pick the team under which your app will be deployed etc. + + + +The final step is to run: + +```shell +npx netlify-cli deploy --prod +``` + +That is it! Your client should be live at `https://.netlify.app`. + +:::note +Make sure you set the `https://.netlify.app` URL as the `WASP_WEB_CLIENT_URL` environment variable in your server hosting environment. +::: + +:::caution Redirecting URLs toward `index.html` +If you follow the instructions above, the Netlify CLI will use `netlify.toml` file that Wasp generates by default in `.wasp/build/web-app/`. This will correctly configure Netlify to redirect URLs toward `index.html`, which is important since Wasp is a Single Page Application (SPA) and needs to handle routing on the client side. + +If you instead use another method of deployment to Netlify, for example doing it using CI, make sure that Netlify picks up that `netlify.toml` file, or configure URL redirecting yourself manually on Netlify. + +We recommend to deploy using the Netlify CLI in Github Actions. You can find an example Github Action configuration below. +::: + +### Deploying through Github Actions + +To enable automatic deployment of the client whenever you push to the `main` branch, you can set up a GitHub Actions workflow. To do this, create a file in your repository at `.github/workflows/deploy.yaml`. Feel free to rename `deploy.yaml` as long as the file type is not changed. + +Hereโ€™s an example configuration file to help you get started. This example workflow will trigger a deployment to Netlify whenever changes are pushed to the main branch. + +
+Example Github Action + +```yaml +name: Deploy Client to Netlify + +on: + push: + branches: + - main # Deploy on every push to the main branch + +jobs: + deploy: + runs-on: ubuntu-latest + + steps: + - name: Checkout Code + uses: actions/checkout@v2 + + - name: Setup Node.js + id: setup-node + uses: actions/setup-node@v4 + with: + node-version: '20' + + - name: Install Wasp + run: curl -sSL https://get.wasp-lang.dev/installer.sh | sh -s -- -v 0.15.0 # Change to your Wasp version + + - name: Wasp Build + run: wasp build + + - name: Install dependencies and build the client + run: | + cd ./.wasp/build/web-app + npm install + REACT_APP_API_URL=${{ secrets.WASP_SERVER_URL }} npm run build + + - name: Deploy to Netlify + run: | + cd ./.wasp/build/web-app + npx netlify-cli@17.36.1 deploy --prod --dir=build --auth=$NETLIFY_AUTH_TOKEN --site=$NETLIFY_SITE_NAME + + env: + NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }} + NETLIFY_SITE_NAME: netlify-site-name +``` + +
+ +
+How do I get the Environment Variables? + +- **`NETLIFY_AUTH_TOKEN`**: For the auth token, you'll generate a new Personal Access Token on [Netlify](https://docs.netlify.com/cli/get-started/#obtain-a-token-in-the-netlify-ui). + +- **`NETLIFY_SITE_NAME`**: This is the name of your Netlify project. + +- **`WASP_SERVER_URL`**: This is your server's URL and is generally only available after **deploying the backend**. This variable can be skipped when the backend is not functional or not deployed, but be aware that backend-dependent functionalities may be broken. + +After getting the environment variables, you need to set these in GitHub Repository Secrets. + +
+ +## Cloudflare {#cloudflare} + +[Cloudflare](https://www.cloudflare.com/) is a cloud services provider that offers a variety of services, including free static hosting with Cloudflare Pages. You will need a Cloudflare account to follow these instructions. + +Make sure you are logged in with the Cloudflare's CLI called Wrangler. You can log in by running: + +```bash +npx wrangler login +``` + +Before you continue, make sure you have [built the Wasp app](#1-generating-deployable-code). We'll build the client web app next. + + + +To deploy the client, make sure you are positioned in the `.wasp/buld/web-app` folder and then run the following: + +```shell +npx wrangler pages deploy ./build --commit-dirty=true --branch=main +``` + + + +Carefully follow the instructions i.e. do you want to create a new app or use an existing one. + + + +That is it! Your client should be live at `https://.pages.dev`. + +:::note +Make sure you set the `https://.pages.dev` URL as the `WASP_WEB_CLIENT_URL` environment variable in your server hosting environment. +::: + +:::info Redirecting URLs toward `index.html` + +Cloudflare will automatically redirect all paths toward `index.html`, which is important since Wasp's client app is a Single Page Application (SPA) and needs to handle routing on the client side. +::: + +### Deploying through Github Actions + +To enable automatic deployment of the client whenever you push to the `main` branch, you can set up a GitHub Actions workflow. To do this, create a file in your repository at `.github/workflows/deploy.yaml`. Feel free to rename `deploy.yaml` as long as the file type is not changed. + +Hereโ€™s an example configuration file to help you get started. This example workflow will trigger a deployment to Cloudflare Pages whenever changes are pushed to the main branch. + +
+Example Github Action + +```yaml +name: Deploy Client to Cloudflare + +on: + push: + branches: + - main # Deploy on every push to the main branch + +jobs: + deploy: + runs-on: ubuntu-latest + + steps: + - name: Checkout Code + uses: actions/checkout@v2 + + - name: Setup Node.js + id: setup-node + uses: actions/setup-node@v4 + with: + node-version: '20' + + - name: Install Wasp + run: curl -sSL https://get.wasp-lang.dev/installer.sh | sh -s -- -v 0.15.0 # Change to your Wasp version + + - name: Wasp Build + run: cd ./app && wasp build + + - name: Install dependencies and build the client + run: | + cd ./app/.wasp/build/web-app + npm install + REACT_APP_API_URL=${{ secrets.WASP_SERVER_URL }} npm run build + + - name: Deploy to Cloudflare Pages + uses: cloudflare/wrangler-action@v3 + with: + apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }} + accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} + command: pages deploy ./app/.wasp/build/web-app/build --project-name=${{ env.CLIENT_CLOUDFLARE_APP_NAME }} --commit-dirty=true --branch=main + + env: + CLIENT_CLOUDFLARE_APP_NAME: cloudflare-pages-app-name +``` + +
+ +
+How do I get the Environment Variables? + +- **`CLOUDFLARE_API_TOKEN` and `CLOUDFLARE_ACCOUNT_ID`**: You can get these from your [Cloudflare dashboard](https://dash.cloudflare.com/profile/api-tokens). Make sure to give the token `Cloudflare Pages: Read` and `Cloudflare Pages: Edit` permissions. + +- **`CLIENT_CLOUDFLARE_APP_NAME`**: This is the name of your Cloudflare Pages app. You can create a new Cloudflare Pages app with `npx wrangler pages project create `. + +- **`WASP_SERVER_URL`**: This is your server's URL and is generally only available after **deploying the backend**. This variable can be skipped when the backend is not functional or not deployed, but be aware that backend-dependent functionalities may be broken. + +After getting the environment variables, you need to set these in GitHub Repository Secrets. + +
diff --git a/web/docs/deployment/deployment-methods/self-hosted.md b/web/docs/deployment/deployment-methods/self-hosted.md new file mode 100644 index 0000000000..c722774957 --- /dev/null +++ b/web/docs/deployment/deployment-methods/self-hosted.md @@ -0,0 +1,129 @@ +--- +title: Self-Hosted +--- + +import ImgWithCaption from '@site/blog/components/ImgWithCaption' +import { GuideLink } from './GuideLink.tsx' + +If you have your server or rent out a server, you can self-host your Wasp apps. Self-hosting your apps gives you full control over your apps and their data. It can be more cost-effective than a cloud provider since you can deploy multiple apps on a single server. However, you'll need to manage the server yourself, which can be time-consuming and require some technical knowledge. + +## What you'll need + +To successfully self-host your Wasp app, you need to have the following: + +- A server with a public IP address. + - There are many cloud providers you can use to rent a server. Some popular ones are [AWS](https://aws.amazon.com/ec2/), [DigitalOcean](https://www.digitalocean.com/), [OVH](https://www.ovhcloud.com/en/vps/), and [Hetzner](https://www.hetzner.com/cloud/). +- A domain name, for example, `myapp.com` (needed for HTTPS support). + +## Self-hosting steps + +To self-host your Wasp app, you need to follow these general steps: + +1. Get your **app's code** on the server. + + - How you do this depends on your setup, we'll explore a few methods in the next section. + +2. Run your **client and server apps** on the server. Run the **database** on the server or use a managed database service. + + - We'll use [Docker](https://docs.docker.com/engine/install/) to run the server app and the database, but you can run them without Docker if you prefer. + +3. Set up a **reverse proxy** on the server to be able to use a domain name with HTTPS for your app. +4. Configure the **env variables** on your server for the server app. + + + +## Deployment methods + +We'll explore a few methods you can use to self-host your Wasp app. The first method is the most straightforward: you manually set up everything on your server. The other two methods require you to install and configure a self-hosted PaaS on your server and then use that to deploy apps to it. + +### Simple setup + +In this setup, you do all the steps on the server: you install Docker, set up the server env variable, set up a reverse proxy, and run your app. This is a very manual process, but you'll learn how everything works. + +#### Overview of the steps + +On your server: + +1. Install [Docker](https://docs.docker.com/engine/install/), [Node.js](https://github.com/nvm-sh/nvm) and [Wasp CLI](/introduction/quick-start.md#installation). +2. Get your **app's source code**. + - We recommend using Git to clone your app's repository and then pulling the latest changes when you want to deploy a new version. You can use any other method to get your app's code on the server. +3. Build your app with **`wasp build`**. +4. Build and run the **server app**. + - Wasp gives you a `Dockerfile` in the `.wasp/build` directory that you can use to build and run the server app. + - We are using Docker to run the server app, but you can run it without Docker if you prefer - just make sure to replicate the setup in the `Dockerfile`. + - When you run the server app with Docker, you need to setup the server env variables. You can do this with a `.env` file or by passing the env variables directly to the `docker run` command. +5. Start the **database** on the server or use a managed database service. + - We usually run the database in Docker on the same server, but you can run the database directly on the server. + - You can also use a managed database service which you can connect to from your server. This is a great option if you don't want to manage the database yourself, but it can be more expensive. +6. Build the **client app** into static files. + - Wasp outputs the client app in the `.wasp/build/web-app` directory. + + - You should [build the client app](/deployment/deployment-methods/paas.md#3-deploying-the-web-client-frontend) into static files. +7. Install and set up a **reverse proxy** to serve your client and server apps. + - There are many great choices for reverse proxies, like [Nginx](https://www.nginx.com/), [Caddy](https://caddyserver.com/), and [Traefik](https://traefik.io/). + - Make sure to set up the reverse proxy to serve the client app's static files and to proxy requests to the server app. +8. Point your **domain(s)** to your server's IP address. + - We recommend setting `myapp.com` for the client and `api.myapp.com` for the server. + - The reverse proxy should serve the client app on `myapp.com` and proxy requests to the server app on `api.myapp.com`. Make sure your [env variables](../env-vars.md) are using these client and server URLs. + +Check out one of our step-by-step guides for more details: + + + +### Coolify + +[Coolify](https://coolify.io/) is a deployment tool (self-hosted PaaS) that you run on your server. It makes it easier to deploy multple apps on your server. It has a nice looking UI and it helps you with managing your deployments. + +#### Overview of the steps + +1. Install [Coolify](https://coolify.io/) on your server. +2. Create your **Coolify apps** (client, server, and database). + - You can run the database with Coolify on the same server, but you can run the database directly on your server or use a managed database service. +3. In Coolify, set up the **server app env variables**. + - You can set up the env variables in the Coolify UI, check out which [env variables are required](../env-vars.md). +4. Set up some sort of **CI/CD** (for example [Github Actions](https://github.com/features/actions)) to: + - build and upload your Docker images, + - trigger Coolify to pull the Docker images and deploy them. +5. Point your domain to your server's IP address. + - We recommend setting `myapp.com` for the client and `api.myapp.com` for the server. + - Make sure to set the domains in the Coolify UI for the client and the server apps. + - Make sure to set the env variables for the client and the server URLs correctly. + +Check out one of our step-by-step guides for more details: + + + +### CapRover + +[CapRover](https://caprover.com/) is a deployment tool (self-hosted PaaS) that you run on your server. It makes it easier to deploy multple apps on your server. It has a nice looking UI and it helps you with managing your deployments. + +#### Overview of the steps + +1. Install [CapRover](https://caprover.com/) on your server. +2. Create your CapRover apps (client, server, and database). + - You can run the database with CapRover on the same server, but you can run the database directly on your server or use a managed database service. +3. Set up the server env variables. + - You can set up the env variables in the CapRover UI, check out which [env variables are required](../env-vars.md). +4. Set up some sort of **CI/CD** (for example [Github Actions](https://github.com/features/actions)) to: + - build and upload your Docker images to a Docker registry, + - trigger CapRover to pull the Docker images and deploy them. +5. Point your domain to your server's IP address. + - We recommend setting `myapp.com` for the client and `api.myapp.com` for the server. + - Make sure to set the domains in the CapRover UI for the client and the server apps. + - Make sure to set the env variables for the client and the server URLs correctly. + +Check out one of our step-by-step guides for more details: + + + +## Database setup + + + +In all of the guides, we run the **database on your server**. When you run the database on your server, you need to take care of backups, updates, and scaling. We suggest setting up [PostgresSQL periodic backups](https://tembo.io/docs/getting-started/postgres_guides/how-to-backup-and-restore-a-postgres-database) and/or taking snapshots of your server's disk. In case something bad happens to your server, you can restore your database from the backups. + +If you prefer not to manage the database yourself, you can use a **managed database service**. The service provider takes care of backups, updates, and scaling for you but it can be more expensive than running the database on your server. Some popular managed database services are [AWS RDS](https://aws.amazon.com/rds/), [DigitalOcean Managed Databases](https://www.digitalocean.com/products/managed-databases/), and [Supabase](https://supabase.io/). diff --git a/web/docs/deployment/env-vars.md b/web/docs/deployment/env-vars.md new file mode 100644 index 0000000000..77ad2e94cb --- /dev/null +++ b/web/docs/deployment/env-vars.md @@ -0,0 +1,53 @@ +--- +title: Env Variables +--- + +We talked about environment variables in the [project setup section](../project/env-vars.md). If you haven't read it, make sure to check it out first. In this section, we'll talk about environment variables in the context of deploying the app. + +While developing our app on our machine, we had the option of using `.env.client` and `.env.server` files which made it easy to define and manage env vars. + +However, when we are deploying our app, **`.env.client` and `.env.server` files will be ignored, and we need to provide env vars differently.** + +![Env vars usage in development and production](/img/env/prod_dev_fade_2.svg) + +### Client Env Vars + +During the build process, client env vars are injected into the client Javascript code, making them public and readable by anyone. Therefore, you should **never store secrets in them** (such as secret API keys). + +When building for production, the `.env.client` file will be ignored, since it is meant to be used only during development. +Instead, you should provide the production client env vars directly to the build command that turns client code into static files: + +```shell +REACT_APP_API_URL= REACT_APP_SOME_OTHER_VAR_NAME=someothervalue npm run build +``` + +Also, notice **that you can't and shouldn't provide client env vars to the client code by setting them on the hosting provider** (unlike providing server env vars to the server app, in that case this is how you should do it). Your client code will ignore those, as at that point client code is just static files. + +:::info How it works +What happens behind the scenes is that Wasp will replace all occurrences of `import.meta.env.REACT_APP_SOME_VAR_NAME` in your client code with the env var value you provided. This is done during the build process, so the value is injected into the static files produced from the client code. + +Read more about it in Vite's [docs](https://vitejs.dev/guide/env-and-mode.html#production-replacement). +::: + +### Server Env Vars + +When building your Wasp app for production `.env.server` will be ignored, since it is meant to be used only during development. + +You can provide production env vars to your server code in production by defining them and making them available on the server where your server code is running. + +::::caution Set the required env vars + +Make sure to go through [all the required server env vars](../project/env-vars.md#general-configuration-1) like `DATABASE_URL`, `WASP_WEB_CLIENT_URL` etc. and set them up in your production environment. + +**If you are using the [Wasp CLI](./deployment-methods/cli.md)** deployment method, Wasp will set the general configuration env vars for you, but you will need to set the rest of the env vars yourself (like the ones for OAuth auth methods or any other custom env vars you might have defined). +:::: + +Setting server env variables up will highly depend on where you are deploying your server, but in general it comes down to defining the env vars via mechanisms that your hosting provider provides. + +For example, if you deploy your server to [Fly](https://fly.io), you can define them using the `flyctl` CLI tool: + +```shell +flyctl secrets set SOME_VAR_NAME=somevalue +``` + +We talk about specific providers in the [PaaS deployment section](./deployment-methods/paas.md) or the [self-hosted deployment section](./deployment-methods/self-hosted.md). diff --git a/web/docs/deployment/extras.md b/web/docs/deployment/extras.md new file mode 100644 index 0000000000..23270fae1a --- /dev/null +++ b/web/docs/deployment/extras.md @@ -0,0 +1,9 @@ +--- +title: Extras +--- + +TODO: Is Wasp production ready? What are the limitations? + +TODO: DDos and CDN recommendations + +TODO: Custom domain setup \ No newline at end of file diff --git a/web/docs/deployment/intro.md b/web/docs/deployment/intro.md new file mode 100644 index 0000000000..2566a8d76b --- /dev/null +++ b/web/docs/deployment/intro.md @@ -0,0 +1,54 @@ +--- +title: Introduction +--- + +import ImgWithCaption from '@site/blog/components/ImgWithCaption' + +After developing your app locally on your machine, the next step is to deploy it to the web so that others can access it. + +In this section, we'll walk you through the steps to deploy your Wasp app. + +### Wasp app structure + +Before we start, let's understand what Wasp generates when it builds your app. + +What we call a "Wasp app" consists of three different parts: +- **Client app** + - It's a single-page application (SPA), built using [React](https://react.dev/). It's what the user sees and interacts with. + - It's usually served by some static file server or you can host it on a CDN like Cloudflare or Netlify. + +- **Server app**: + - The backend of your app, built using [Express](https://expressjs.com/) on Node.js. + - It handles requests from the client app, interacts with the database, and returns responses. + - It comes with a ready-to-use `Dockerfile` so you can easily package it and deploy it anywhere where Docker is supported. + +- **Database**: + - Wasp uses [PostgreSQL](https://www.postgresql.org/) as its production database. + - You can host the database on your own server or use a cloud service. + + + + + +The thing to take away from this: the client app and server app are separate applications that communicate with each other over HTTP. This means you can deploy them on the same or different servers, depending on your needs. + +We'll show you different ways of how deploy your app in the [deployment methods](./deployment-methods/overview.md) section. + +Server needs to be able to communicate with the database, we'll show you how to set that up using [env variables](./env-vars.md). + +### Deploying your app + +In the following sections, we'll go through all the different things you need to know about deployment: + +- How [env variables](./env-vars.md) work in production - they are different than using .env files in development. +- Production [database setup](./database.md) - how migrations work, how to connect to the database, etc. +- Different deployment methods (using [Wasp's CLI](./deployment-methods/cli.md), [cloud services](./deployment-methods/paas.md), [self-hosting](./deployment-methods/self-hosted.md), etc.) +- How to [set up CI/CD](./ci-cd.md) for your app - automatically deploy your app when you push to your Git repository. +- Some [extras](./extras.md) like custom domains, CDN, etc. + + + diff --git a/web/docs/project/EnvVarsTable.css b/web/docs/project/EnvVarsTable.css new file mode 100644 index 0000000000..4ae3949b9b --- /dev/null +++ b/web/docs/project/EnvVarsTable.css @@ -0,0 +1,28 @@ +table.env-vars-table { + display: table; + /* width: 100%; */ + border-collapse: collapse; + margin-bottom: 1.5rem; +} + +table.env-vars-table tbody tr td:first-child { + width: 200px; +} + +table.env-vars-table thead th { + text-align: left; +} + +.env-var-type { + display: inline-flex; + gap: 0.5rem; + align-items: center; + font-weight: 700; +} + +.env-var-default { + display: inline-block; + margin-top: 5px; + font-size: 0.9rem; + color: var(--ifm-color-secondary-contrast-foreground); +} diff --git a/web/docs/project/EnvVarsTable.tsx b/web/docs/project/EnvVarsTable.tsx new file mode 100644 index 0000000000..420c66d53c --- /dev/null +++ b/web/docs/project/EnvVarsTable.tsx @@ -0,0 +1,61 @@ +import React from 'react' +import './EnvVarsTable.css' + +// @ts-ignore +import { Required, Optional } from '@site/src/components/Tag' + +type EnvVar = { + name: string + type: 'URL' | 'string' | 'number' | 'boolean' + isRequired?: boolean + note: React.ReactNode + defaultValue?: string +} + +export function EnvVarsTable({ envVars }: { envVars: EnvVar[] }) { + return ( + + + + + + + + + + {envVars.map((envVar) => ( + + ))} + +
NameTypeNotes
+ ) +} + +function EnvVarRow({ + name, + type, + isRequired = false, + note, + defaultValue, +}: EnvVar) { + const requiredQualifier = isRequired ? : + return ( + + + {name} + + + + {type} + {requiredQualifier} + + {defaultValue && ( + + Default: {defaultValue} + + )} + + {note} + + ) +} diff --git a/web/docs/project/_clientEnvVarsNote.md b/web/docs/project/_clientEnvVarsNote.md new file mode 100644 index 0000000000..fd4500f233 --- /dev/null +++ b/web/docs/project/_clientEnvVarsNote.md @@ -0,0 +1,4 @@ +:::caution Client Env Var Prefix + +Client env vars must be prefixed with `REACT_APP_`, for example: `REACT_APP_SOME_VAR_NAME=...` for security reasons. Wasp will only inject env vars that start with this prefix into the client code to prevent accidental exposure of sensitive information. +::: \ No newline at end of file diff --git a/web/docs/project/env-vars.md b/web/docs/project/env-vars.md index a523995585..89ec244025 100644 --- a/web/docs/project/env-vars.md +++ b/web/docs/project/env-vars.md @@ -2,18 +2,22 @@ title: Env Variables --- +import ClientEnvVarsNote from './\_ClientEnvVarsNote.md' +import { EnvVarsTable, EnvVar } from './EnvVarsTable' + **Environment variables** are used to configure projects based on the context in which they run. This allows them to exhibit different behaviors in different environments, such as development, staging, or production. -For instance, _during development_, you may want your project to connect to a local development database running on your machine, but _in production_, you may prefer it to connect to the production database. Similarly, in development, you may want to use a test Stripe account, while in production, your app should use a real Stripe account. +For instance, _during development_, you may want your project to connect to a local development database running on your machine, but _in production_, you want it to connect to the production database. Similarly, in development, you may want to use a test Stripe account, while in production, your app should use a real Stripe account. While some env vars are required by Wasp, such as the database connection or secrets for social auth, you can also define your env vars for any other useful purposes, and then access them in the code. -In Wasp, you can use environment variables in both the client and the server code. +Let's go over the available env vars in Wasp, how to define them, and how to use them in your project. + ## Client Env Vars -Client environment variables are embedded into the client code during the build and shipping process, making them public and readable by anyone. Therefore, you should **never store secrets in them** (such as secret API keys -> you can provide those to server instead). +Client environment variables are injected into the client Javascript code during the build process, making them public and readable by anyone. Therefore, you should **never store secrets in them** (such as secret API keys, you should store secrets in the server env variables). -To enable Wasp to pick them up, client env vars must be prefixed with `REACT_APP_`, for example: `REACT_APP_SOME_VAR_NAME=...`. + You can read them from the client code like this: @@ -21,46 +25,147 @@ You can read them from the client code like this: ```js title="src/App.js" -console.log(import.meta.env.REACT_APP_SOME_VAR_NAME) +import { env } from 'wasp/client' + +console.log(env.REACT_APP_SOME_VAR_NAME) ``` + ```ts title="src/App.ts" -console.log(import.meta.env.REACT_APP_SOME_VAR_NAME) +import { env } from 'wasp/client' + +console.log(env.REACT_APP_SOME_VAR_NAME) ``` + +Read more about the `env` object in the [API reference](#client-env-vars-1). + +### Wasp Client Env Vars + +Here are the client env vars that Wasp defines: -Check below on how to define them. +#### General Configuration +These are some general env variables used for various Wasp features: + + ## Server Env Vars -In server environment variables, you can store secret values (e.g. secret API keys) since they are not publicly readable. You can define them without any special prefix, such as `SOME_VAR_NAME=...`. +You can store secret values (e.g. secret API keys) in the server env variables since they are not publicly readable. You can define them without any special prefix, such as `SOME_VAR_NAME=...`. You can read the env vars from server code like this: ```js -console.log(process.env.SOME_VAR_NAME) +import { env } from 'wasp/server' + +console.log(env.SOME_VAR_NAME) ``` + ```ts -console.log(process.env.SOME_VAR_NAME) +import { env } from 'wasp/server' + +console.log(env.SOME_VAR_NAME) ``` + -Check below on how to define them. +Read more about the `env` object in the [API reference](#server-env-vars-1). + +### Wasp Server Env Vars + +#### General Configuration + +These are some general env variables used for various Wasp features: + +Needed to generate secure tokens. Generate a random string at least 32 characters long. }, + { name: "PORT", type: "Integer", isRequired: false, defaultValue: "3001", note: "This is where the server listens for requests." } +]} /> + +#### SMTP Email Sender + +If you are using `SMTP` as your email sender, you need to provide the following environment variables: + + + +#### SendGrid Email Sender + +If you are using `SendGrid` as your email sender, you need to provide the following environment variables: + + + +#### Mailgun Email Sender + +If you are using `Mailgun` as your email sender, you need to provide the following environment variables: + +Useful if you want to use the EU API endpoint (https://api.eu.mailgun.net). } +]} /> + +#### OAuth Providers + +If you are using OAuth, you need to provide the following environment variables: + +_CLIENT_ID", type: "String", isRequired: true, note: "The client ID provided by the OAuth provider." }, + { name: "_CLIENT_SECRET", type: "String", isRequired: true, note: "The client secret provided by the OAuth provider." } +]} /> + + + +\* `` is the uppercase name of the provider you are using. For example, if you are using Google OAuth, you need to provide the `GOOGLE_CLIENT_ID` and `GOOGLE_CLIENT_SECRET` environment variables. + + +If you are using [Keycloak](../auth/social-auth/keycloak.md), you'll need to provide one extra environment variable: + + + +#### Jobs + +It's parsed as JSON. Enables you to provide custom config for PgBoss. } +]} /> + +#### Development + +We provide some helper env variables in development: + + + ## Defining Env Vars in Development During development (`wasp start`), there are two ways to provide env vars to your Wasp project: + 1. Using `.env` files. **(recommended)** 2. Using shell. (useful for overrides) @@ -71,29 +176,31 @@ During development (`wasp start`), there are two ways to provide env vars to you This is the recommended method for providing env vars to your Wasp project during development. In the root of your Wasp project you can create two distinct files: - - `.env.server` for env vars that will be provided to the server. + +- `.env.server` for env vars that will be provided to the server. + +Variables are defined in these files in the form of `NAME=VALUE`, for example: + +```shell title=".env.server" +DATABASE_URL=postgresql://localhost:5432 +SOME_VAR_NAME=somevalue +``` + +- `.env.client` for env vars that will be provided to the client. Variables are defined in these files in the form of `NAME=VALUE`, for example: - ```shell title=".env.server" - DATABASE_URL=postgresql://localhost:5432 - SOME_VAR_NAME=somevalue - ``` - - `.env.client` for env vars that will be provided to the client. + ```shell title=".env.client" + REACT_APP_SOME_VAR_NAME=somevalue + ``` - Variables are defined in these files in the form of `NAME=VALUE`, for example: - ```shell title=".env.client" - REACT_APP_SOME_VAR_NAME=somevalue - ``` + `.env.server` should not be committed to version control as it can contain secrets, while `.env.client` can be versioned as it must not contain any secrets. By default, in the `.gitignore` file that comes with a new Wasp app, we ignore all dotenv files. -:::info Dotenv files - `dotenv` files are a popular method for storing configuration: to learn more about them in general, check out the [dotenv npm package](https://www.npmjs.com/package/dotenv). -::: - ### 2. Using Shell + If you set environment variables in the shell where you run your Wasp commands (e.g., `wasp start`), Wasp will recognize them. You can set environment variables in the `.profile` or a similar file, which will set them permanently, or you can set them temporarily by defining them at the start of a command (`SOME_VAR_NAME=SOMEVALUE wasp start`). @@ -104,43 +211,74 @@ Defining environment variables in this way can be cumbersome even for a single p ## Defining Env Vars in Production -While in development, we had the option of using `.env.client` and `.env.server` files which made it easy to define and manage env vars. -However, for production, `.env.client` and `.env.server` files will be ignored, and we need to provide env vars differently. +Defining env variables in production will depend on where you are deploying your Wasp project. In general, you will define them via mechanisms that your hosting provider provides. + +We talk about how to define env vars for each deployment option in the [deployment section](../deployment/env-vars.md). + +## Custom Env Var Validations -![Env vars usage in development and production](/img/env/prod_dev_fade_2.svg) +TODO: when the Zod validation PRs are merged, describe how users can define their own validations. + +## API Reference ### Client Env Vars -Client env vars are embedded into the client code during the build process, making them public and readable by anyone. Therefore, you should **never store secrets in them** (such as secret API keys). +Access client env vars in your client code using the `env` object like this: + + + + +```js title="src/App.js" +import { env } from 'wasp/client' -When building for production `.env.client` will be ignored, since it is meant to be used only during development. -Instead, you should provide the production client env vars directly to the build command that turns client code into static files: -```shell -REACT_APP_SOME_VAR_NAME=somevalue REACT_APP_SOME_OTHER_VAR_NAME=someothervalue npm run build +console.log(env.REACT_APP_SOME_VAR_NAME) ``` -Check the [deployment docs](../advanced/deployment/manually#3-deploying-the-web-client-frontend) for more details. + + -Also, notice that you can't and shouldn't provide env vars to the client code by setting them on the hosting provider where you deployed them (unlike server env vars, where this is how you should do it). Your client code will ignore those, as at that point client code is just static files. +```ts title="src/App.ts" +import { env } from 'wasp/client' -:::info How it works -What happens behind the scenes is that Wasp will replace all occurrences of `import.meta.env.REACT_APP_SOME_VAR_NAME` in your client code with the env var value you provided. This is done during the build process, so the value is embedded into the static files produced from the client code. +console.log(env.REACT_APP_SOME_VAR_NAME) +``` -Read more about it in Vite's [docs](https://vitejs.dev/guide/env-and-mode.html#production-replacement). -::: + + + +The `env` object is a validated object that Wasp provides to access client env vars. + +You can use `import.meta.env.REACT_APP_SOME_VAR_NAME` directly in your code, but it's not recommended because it's not validated and can lead to runtime errors if the env var is not defined. + + ### Server Env Vars -When building for production `.env.server` will be ignored, since it is meant to be used only during development. +Access server env vars in your server code using the `env` object like this: -You can provide production env vars to your server code in production by defining them and making them available on the server where your server code is running. + + -Setting this up will highly depend on where you are deploying your Wasp project, but in general it comes down to defining the env vars via mechanisms that your hosting provider provides. +```js title="src/App.js" +import { env } from 'wasp/server' -For example, if you deploy your project to [Fly](https://fly.io), you can define them using the `flyctl` CLI tool: +console.log(env.SOME_SECRET) +``` -```shell -flyctl secrets set SOME_VAR_NAME=somevalue + + + +```ts title="src/App.ts" +import { env } from 'wasp/server' + +console.log(env.SOME_SECRET) ``` -You can read a lot more details in the [deployment section](../advanced/deployment/manually) of the docs. We go into detail on how to define env vars for each deployment option. + + + +The `env` object is a validated object that Wasp provides to access server env vars. + +You can use `process.env.SOME_SECRET` directly in your code, but it's not recommended because it's not validated and can lead to runtime errors if the env var is not defined. + + diff --git a/web/sidebars.js b/web/sidebars.js index 2a6571d0b3..2f3a971c0d 100644 --- a/web/sidebars.js +++ b/web/sidebars.js @@ -97,6 +97,30 @@ module.exports = { 'project/custom-vite-config', ], }, + { + type: 'category', + label: 'Deployment', + collapsed: false, + collapsible: true, + items: [ + 'deployment/intro', + 'deployment/env-vars', + 'deployment/database', + { + type: 'category', + label: 'Deployment Methods', + collapsed: true, + items: [ + 'deployment/deployment-methods/overview', + 'deployment/deployment-methods/cli', + 'deployment/deployment-methods/paas', + 'deployment/deployment-methods/self-hosted', + ], + }, + 'deployment/ci-cd', + 'deployment/extras', + ], + }, { type: 'category', label: 'Wasp AI', @@ -119,17 +143,6 @@ module.exports = { 'advanced/links', ], }, - { - type: 'category', - label: 'Deployment', - collapsed: false, - collapsible: true, - items: [ - 'advanced/deployment/overview', - 'advanced/deployment/cli', - 'advanced/deployment/manually', - ], - }, { type: 'category', label: 'General', @@ -139,7 +152,7 @@ module.exports = { 'general/language', 'general/cli', 'general/typescript', - 'general/wasp-ts-config' + 'general/wasp-ts-config', ], }, { diff --git a/web/src/components/Tag.tsx b/web/src/components/Tag.tsx index 1724c49180..8dd7ac8e9e 100644 --- a/web/src/components/Tag.tsx +++ b/web/src/components/Tag.tsx @@ -35,3 +35,9 @@ export function Internal() { export function Required() { return required } + +// Used to mark something as optional e.g. optional +// env vars. +export function Optional() { + return optional +} diff --git a/web/static/img/deploying/self-hosting.png b/web/static/img/deploying/self-hosting.png new file mode 100644 index 0000000000000000000000000000000000000000..15b67fc7b064a6bfd802afd482a6dd52caeccea5 GIT binary patch literal 37429 zcmce-1yo#LvoF|KaMuJ04#6Qvkl+MJaZh;O@}4y99T4X&i#PySs$x{J;Cn zeQ&+F>&dJcR-eu3U9xM}{?$Hp&Z!Pjkds75d4~c30MMnS#FYR5L>2%5Aqoi&+A~B= zWeEVlXe!94NIX0~k&==EvoF@!*~7xaRa8{FdwR#VA9BWSca4l)8?H|h6Y1&c?;aj+ z@9wV#huGNIwzjqpkB-hSF1fh46crT(1qDsb%=q~D)6&zAPfqvt4;Gh}C#R;bZ*DKI zuFHS@8Xksx|L&=)tLx?MJ@b3EqqD27zW)6DVq|nIDLMJuH>0)n4NWaADJkjdnpz+b zSXo)Mvbq`&5NK&>6&n|qk(nhSAyH6RSW;RhA|g6IF#&RP?CT$}v9;aa*==lUZfR}J z&dCW03C+vP_w)0Qj*fA4b+fax9~c}eE-rC$c9E5nQ&Urmh>Ub|bDNuA0E3hC4GiRS zFJ|ZF;}a6v+S-5m`ktPhs~25X{k}6SyF9pjw5+-c>bz-RzQ4MAis`#KyLlA$Y30-p z`XHz@uyGF_yj?na*f@Qh*?aJ6xy~55^`@h%U}CaL?2x0R1ebROXlf;^#2Eqr(O_wD z5f!(^(ZNS%d5STYn-eVJ;|nzrlD;Z znP^!9{D}ms(qCC%_)g*=Vbb*$Ou}^F-VvwNept>$wUz8)Z;Z~5H5$K4WsG1R<8?4- zz<`KE9SoBSOkt{ll|qg$h;EeNK^oy2ZI;4VpwHPhP7{3a|BL260I@Y1=p)V&1N4#l zuN(I&^#NKyk1!T|y5}o1KUg>w8U7&*_$QP2s&{ZB>z7f1Mq4X+SVz3d4%hFbQ# z(9%#)`;#qvYnaIm1t(}p$$PE{ za1+p0{!WS| z%UtiA=)Rn%`v2Wg(dTMKXZ7JfIZ$($L|X7pxJ>(3lKG#tob*nZx4h%W?{4kVH@C=x zoJMG{6~D-Gf#PX$584D85$e2VOkxZgon;{s8?> zcqkP6ZH4B@29oT_7}_ZBokvoYzw0wfkjr3sHCd{fy&@|9g!B6g!`}gO3(|leNtYyT zwd2RCF*L1?s#K27FubX96!5SM0p%a4mQ>xi<<+cL6js_m!$O{Q;XZ_2#C4i~{Cb<% zJV@E=*WU2Ix|c_l+taqA10)lBNb|uwD|xluf4g*CRe5b_`UIrhix_RI@w2l<8vgnjHAR3 z78ieeqs1vRblKYb>9ZT?N64XCRx!2#`eLH{chcp`8Uq`< z=cH2f;R%o#P6I*|!w*bzO`J;hXyK|Kw?-B9Eukv^T8+J7YLP8*EH z*rAn}`^!&wTS!JK>2_O9wcVLGR`%ik`mt>9hfL5@zj<;Q&Gb#LCHeoIWy!%Fju6cz zUy*^*WI!Qn`(ejI(^BCXm^}2 z%J1qQyvV3pHe0}*8`=PGhwj*a%+TDf_8`})Duz{!?<{TW$?^_hLy1BX@Na63DYB&s zqFWBJ!w(KlV^cz;&bB*m%?HkKX;Nv`^tW57;Lw+-XLPE{a@8!g64gr@d zgI|y3p1aj%x)BfrrF&x|=zh$&qjTo$vDDq%4InXxDg7)Y+r_WPA9ck1s$JVN9nxnE z4Lc4?+NRnt9AZu{0uu*>j8Q3I-yOnF=Zl${Y+^ao4_`MQ(umSs*p=#g_w{zO%O!Sw zz4gCxHqfx2mQEbvXbEL1$oz$6s%Z>RIf*Pj^8Oa0(7bUVoKSlgf~xyeQt}1W!WI{@ zq$@F7WTS~wb^fLuL3jUXI$!HNz~fxepexXUMUmJ&@9!2+4Yg%C(f;ITWr%CYIBR#T zt%y~q9Xi!O&VIwVZ2uV5M1-E<45*h)NI9PUPQfyVv{6`#HC^@}VGc`zCX!IQb`%N1 zLl_vin{(SPT$1IG1^W+p~_MoyH)=16f}Wk8oTIocGZ!<*Xs{TCfB& z=enB?RL`T^B4Dkf5%Xhq*@}HICXNY_SM6w#L z3Ero6(VJy0xsB(GO@3m|TfJ6>5*w!3!%NBw0@6CpZE1;~-CdOyAG`K}K{>f_D|BKA z#x@RM%JQ|TOv29W<*=m6{P9Ukp~G=uIGH`x?Wpz9czYo?5Un&7t>v9l{fBJY=`ruu8ISPl~kO(EA|4KCc z!cBH9J+8-y_&fCp_`EcH^kw%u9b@9K!D2KazeVvy z6I0??#p;TappAF!D@Y)HHMIPsWFUQq8so2fzDl#a1}>Y|Ti&38g}xSYxi|G(NHnbJ zlcl52YKBNaXH~aq&oUhWRnG)M1|b8UOm`Wh8K6%376Xh10;I%QM4(LAP4G}{;@db- z0?vO==zV{CI1lfG1u$4AJp03_9cI8M(x^=#ag0O0K;wcOeT>rrFXa-V3rpS73(bCX zzkvZmELA`sWAHppr%eybnlTta=Q4zI`U;r;p9-)ol=|TUM9Bf)NJwt=tLHn%g{}Ffvl5{c3I0kJ3-bO$gL^c zf|Vdk^Ki$dU;`&>DYUfqHe*f8%G4kFO}9UacdvQi9_$?N9xosx5CH94L%GdM1n}(B zSRyM-17~ou3cy7)os1&}jF!mugk8@D2ScJha}Vtz*lF4Y8nEXw4(wjML*NsP*XC%;Dyt2iO5LZ zzrMnIoH0)nq~pXU+pe0q61cw5t|0(Mpb-K{3e$lFun{(S`rh7rf!-hw?ogGlWj458 z8#GkjrNRPybXNJtJoo&*@R2oR0|48gZKkV<*jRMLg=zrQY0~2&*?DK?k^sO=3dEGv zu_I3?b%0Knxt~93@3Js15dbLS0xwZWnWY&17`qAq>}raG-YP=n&=E^h3?X8gf)Cbxu78R|SGp&^@}G`OT8aSx zOq5_-@b1*ybO(NF$(T1?CSI%SCA5F$M>sR)T8CnFKRSt%O7hOgq!*d%#(n@)Aukx@ zlyy*Fuy+aeLsi|ai;D|sqXPg4;vuH)EJBvcgowbBVbhHwo+^0+fUwFddwu?a9O<#; za<(7vv*g|3PO5VPzbh!;V0~K{;G=ze`s;%YISSmlyIGOCKl0jI5t1kZUX`zTd3~)$ z@k?c{&Fd?x#ft2wmQE#CeH5IhR))QKSn*2X{bzQ8h?Q2Db33|8Uc>Q^Jf&xAaf{A4(?qpQE?7LS7g| z`HzrgQYP_($e0DC*XLxyMR|S{P_K6HTEZi~w6f!%3$dYW?@wc3GNemAdt0FSwuI@%JY^`@q z3u<Nr$775PZ-v^OqdYL$hU?w zDVVd`XvVkFOpYolM#(te2;3{zgh||>cNdSp=&~&*m3_R$AEDnS?c&a#ps{|wVC=Ub zelX|{9>a<`MieP$sZkX-$Km}Z_t$;IU_^yg8N&^8=L{AEKQ}hCcr4k^veZa?id9AN6i(yAE+;C?&-BafP7Q9h zedl>AC80I?^P!q13`?|(mjzeE|Gv_hS5|XFx+;0!O}y9cPM^fJUh?pSFu2aDz7K;w zCqUR2))B+hB#LqVef_$YJ(oE_yqzx^waszu8+QqDotUebc6{rmkx++4B_4aUWtJTC zi{`g?FL@Va1#r;tshVv(7I`bmnAj9qO)D4_0+Ejx{G}h)s~nYB4T(D@J!!0)aEaD+ z-5%cdjWdzH0*&QtO`zn#HKxp*uk3G9IMdb6R(jnR$=y2u`PsUrPKXrqFzj8z1LM5a$AaBe;v)<%s6_-;7bdRJNR<4)f1 zQYF+l3j*Yk(@D0yy{H%b=l>_`gNwWSE6buP@(8E(@`^cX9{KvQ7+vxO|_P9 zq8N;?J#{SG9epcR;Xm=`*&>uI#-2~B--T!Nv#5Sg0Ws@6FUHkp!`_|mg&*%ySppo9 z!j;8o;D=r8PT6G6*>HK(!l$oMv@X+_h>_4aX7BufzjPDQm7;sbvqyI)wqZp6ajqxI zbTAw!DxTP@%L#dhq1~d8%=AJqyoZpDI@RW}SXofU-A>($8UC4Rw2Z;AGHuV(FuvbC zk~$y9l*8<8hequyJMP_VVrjybaaU%XnemfPXV@Ey@o?L0Y*8;~%&(Y>+IHIYCfK4# z_3e!U{)U#lgCi-WqtWGXJ5&p{X)mec<*De9%55Hcy-|z!a#8*r>q(qP(RuF4dI#$T zji}``@_1ysx%H724m$=_!3)pITWYE8-&;s2rQ z#{98UY4Mma<&c}&#nz-&KAV|7?w?Umcr(Piovq`qgOK8tHbNq-m_>O1V+dM}QJL-x zuC_(GCEMr49sIqt*_zC)L_uGZsLf?z$hmY`7`@06MUEZYOhh4G@^nhmQzGWJ2{lIdNYfY^{=nsS|gm>V`R9m{K>Wkui^5(Vn@%^q)V?&g-dG-6NN?bS`zW%tuZCx7?3K-i8ozIF)f5& z)WIL$iQ{iZ7BZ~Wf8Dik4#lc< zG+V+j-Y&zseog--mST|kbYMRnyyw{?n5oGIHto=g3aQDcs!gK;0D{?Bu1K`1xEHNc zmn=anAFL(|i+AaLcUgs0YO2{KJ}?z+?7#oTr@j)-HM62gr9Z9(%Tyc!i_<38e+<4m zi24#c&=^k@GY~K{q(mfHC=z!#_xQ0jvLYI_oxOZ)^Y`WK*HJF+>H0zSb_~(iv>$DO ztIjQjiKc7Xh(A&+DH`Mf04#kSJ`jD+*KXXG)uBRz{R)q5el=zT-u%qmYDfEdPw$>= z$_2iDxb^JenOd$!(^0`04drpYpxYdy-_MV4Q%k1cnn|jJqK8fHWUF6PF2yut%)X{0 zo5ee?brauQUH0g?>FdSzgw1gTd^K}IwDfvPCM$wia9R9Fhh}v3jn{duRhaU(DqB56 zJ0dsnyxOSXHCV*amE5xtelBO;@~u}U9O5q=Cr1xEM`~oAv0qeN!9qKe<;Q6;Nsd&A zCIm`fmg;*FnRzJoxSk2vDxF*#2D2kVoWBaSSbbUP>K;Dfr@FDzoR`V_a2&YpJ%uYriQ1vks?B4kqO_4xVDc7m@mkWz8pp z_d7m8tofImU1gUshkT!bYYPqP**Okc*XUOK=$F;I#&;-DaaXi+4}NewlPd96W-heh z=K5-IIEx-7&D#R2*ysSPtsV{OmKbgPaMZGuAu@`ca;JiNU8)SZwN;WM-J;9%R`-yy zHdh`a`EM@;nXee-Fg14c3dx7Cb#)gM?kUpNygb3 zdJQCz8k4VoR2Sy+4m(H^FadxnyBq1r!Q@4^DYKK|$9P-xDBcogR{W|TYRZU>*5T&| z(c`GTCNWa45nHj%=d=eO-T?6wIqjVN(C3m1%~7^a6xGP6e#`%=88vNvG048fo;2lN z%s_nfcq2OC@pP?)V{{Gx(4RIwzx_#ZO_hak8EZvk|#o_r920&iW7?10RX{<3>8q{L_N#zV38!>|yZ3Q_f%v;6{L_J-v za&0G4XDh~-$eOdI!Dz|Mn%?kcipyo&QtRuh?K*AbYHORGbl!m61+a=)lWxtQ4^sHJ za2_N;Tpvo!U`K*M@*ZERk?#F>n+IdV7uhq%6mhG{MyE_lXo%9W(4eIngVqi|@GYrg zY|9TS965CIGL<%3OjRi5KSjG(^e%maBoMCpu8rLaY%N&Embo9IZ~gJ&xYCY-txGGr z9zv~4M8%s_+Oi+882T{H`9#oDw4{;SdC#XR$img#?0r4I=Oh@Dc<^gYy>xU%6^hQc zy>G!P0{p-jH9e{7=?`x+r9InqiF9T^PbuBpc6YCD3K*S0ER<#zqtV9;0OqrzgWsq_GN+2VwIsWv}=9fFl5dp4T{W2B%v)=nmYGuW( z+@qn$W!_f43>N^{1w7GnN6NfU8ZCF0`SzmUzIL%oGgnes@u^A<07!y!7%M*5Qa+;kNu{M9+)sPtn!-AIodApW}6EI>NC|O@)3h=c^@=c!-6^v2 zNQ8e%!99Wp__$*^>J*q8@0`? z@k}Qd%f1}VeVboxEnUCwe#jB(A`0Eu*f2NsT9%*IR&{iSYpT<{HVSfY<`kjxXf0@5 zc1m(Dlz)u^J@q3&96j1;UlpD8rJ+LSp_{RxCy=Yx6DNt>*_Fmt4b3xxeF-(zrGz<^ zzp^Qf&`Xos4=gbBDL^m~+Vs*u9|z!V;s5{!41do%RsMGS6#v7aOaZ;&_Nq}2;zom2 zzyrL#&%&>3WH6TSvzW?j_I+u9frq~%TDJ}HlL7;<4sZaJy0F4|KtM3`sX`3&In@*m zK)e~==f5ECaP^om0VZ}9?Ec8z@d>Q1e8vatU)n$29cs2*{7%g_Fg&wOjScQ*i(H#S zyFhU|K^ z`g{vh^q(T2q@>)PDiUtNvO!~}5f>MiPld?6yC4Q8hGxiak0a5gHKDy1VMNBpo{Zb& z2u)_xG({B7qx9}(Br;M&VH>V)|D);XtsWd4?70I%9Ip2D-)h7?2-Bv(_Fjm$Spg8=oFNc zmoX44`ZLenYQ&u;YakQs$rP72_~xbeMIz( z5eRy)3No9w)O62A7FMHJ!3Sj#wIpPfPT>-1Dq4muP;}7ApG>)*nk*xedN@B+==1~{ zVUl4}=DaRgHjRiu!UiRfw63}~uM$2B+I9SyWo#Q79AU3bReAR@B8%wuBD32UCdv$)LbRg`t5H=NOem|@!1%f)(ithSCVN7vMrVanHcr#oqM zv^3-83g1Un2!lO6G-McEt3Q%tbX16c77}pubZh-)T0XQ;y4&7C3&s_0FlLQZlmZ~0 zKoRiLpa$82|0>pNX~;4IZlcx^Ey*9>a4CXJb+?V5k@ltOb?0+pM%Gi^eWNhvo$Jq3 zPKJaVI4u{Wtgng;5a`P(17d$l_>O;Z$WlBEn{-(TnF`3s+JZXS1pU@ziG!s{h>>K z%tL)DkkmD8R4@nY_9fr)h;T`QhO}_UWf+qrEfSJ|@n-d5hn65?A6rBDJ3i94CwVjy zVS{*y%oaoObplE)s_%X;+0~rFqbqMH{F9W6MqDDPCE?gC;`n8hz6034h|VeU5+NJX{eKXY}sNX^j!lua_A< zL4$O>u{-o zRHr0pVf<99Z3GePmVZ^|;v8i&#otEZ$|E^2OMfO45~@a9SG2XvA0!Mp|$5nIid zV!Y~Ma(}EVgUg|V{xsnaI@&yZZ!e#w?yjS&G~CCOii zDM5;3o$7o|mKeF4v0+y3EOI8KcfAxzd(hZUmI{DG!v!&aGCU;C|dh_8M< z=vGijV@kN0LZe>cgUoNc72-!c5~Dm-g-zYMw2^f2-}(~NTDy8XFe}Z+v9lj&+=mF0 zr-^93w8ITEbjGXaxfpI<_$>;Cg`=)JO*$cpBC%(hhC(Rh(0qS)@E7@RS zV2r#l4K80{)2Fl9uRoz{T$sFh2?%j^qxv0-f#|vtqSJB=Q)SB{Q-5fzi)*W3VMpV$ z(EgTsx+4#3}aL~mJ1Rd zuQiS{E&{pJ&dBj84izU;TJP$boAj;Vd>ZNupQVcY!xllv1}%ToHc;D6^&ECaD6Ps^ z9v4+#8sqvx7E@l8asRbKdN~#!H56pKdr_FL?%U|^D97!!ve0x@e$AsCjwT|$H_z%% zHAvjo42M&dsg|l9%#P2;W3ai}&A6XK{0?S(#J}Vlt6q(q6iA_SSP(BCzBQ~M1!%0F z$VnR0)u~AKSrVo9m?RO=C^o@Z@_J}HlKxn^->$%i++#27*J#mwis8Qy(j~)B=3|A1 zN87@07f$1wWyDU2_&E5J=~IXZMHjQtIpU z=P6Ib1e=bE9-IUp`x%#ha^-4PVq%ya4*w5}Rl=%t9 znC3Q#iHrD_$?yl_r;~pQNm{qWbtWjIkCg?n+yh0tYK`To zKXG*cr&aug!rOR@&!4k^b>mq(%xP|%p!r;Hu5%*Cdn9VDMq0lvECi#idxRhEJ}`Fa z?pO{}liAe>*OtI583O)P1>R6y&IJj!e8?u*;e83!KZew7J@~T%@ z$eLEpwJ^-gn~0AT^H+6=2T8SexT>&(i^i)+&i%P6b1)wt)T;d{oxBuG5O^CN+Jdi# z^UJ1v82foDQxIG;ve(NKfi3dnneLVeFVx2OLE*lT>5{eV;0`^vAt*b%>P_X}=mndG zK%CK-W7?{e9YyTnMBl5X+tios1)yeY?QlTM${ba>HRuC}k-Rj~zD2yR?VwP8`6l2@ z7D35@tTUyfqAN_}_8xMNaY>X9_oZz0QMSgjXq|bynI}zy4c0D>EM!M@jq)|bJm?uS zsabJ|ThZZnfvNfA;0+NHMXyc@4U0N^B@j&GxEmtb06&97HzgdgQ1u*ER3HD1DJ%tb znuPS@Qx>|^=02DN(b3v%m9^$HkI)fqJX4^8Ggao}yDQBr(vM-^h`P8|8LSA_^YD4b zrkXe2@3Q68>{W{2vkE_+-4DMa<?Tynuh1jX)*bf6U+Hx(RvF z+)=jL6t{?gp=Z$j!JXcVdgc4qeKuovw1Z;?_M5R3mfeKts*Z6 zUA?yKO+jDYTMZj3?yQ8;Ons8+Nq1Ijh10mEDR(hiY;(S^5zD+BbJj1@^vE^xg{w-b zOmA{+Wc+9`(;r~t*?!9tgDTm5A{n?;)ZEu*V&<)4*5DROn6bRJ7rTWdKs3~?!riy9${6@oW{Z+6K2 z(`Xr7e{k%4CT_K7B7{tcvC$EIAat5S=Y zt)DJV15mRtTlO?(>cN9f8Cp)1eEk~lZFvPyv;CjjA(A*j4{EgFNG{dy@zF-w8H3xEY22FxrHa&;Iw zE0%d$_Tl>BO*SIAQ5#9(7n1y+v3C{79ab(o$ESvLYG`)|+A;56->y%j4S5iy2XD`y z-xv_*f+Mf%+4m}+;?KGK*dK7I1GF;9RBimAdrYq8CPz>TVmn7M&u8OJKunYnViZV? zLPmr~wZ-+o?&{O$q3V!fn4^_8ls}p7T(-44i(VTLn{Iz%45iVSDC3JBdcHAyp{P4U zE!J=&#N?Fn9B%r#Qle}*7J&p0b?w40t1A@h0s5B~+o$Gmr`R$o5121}jnH>_>_P2Q zhsr?;K~BbclnDTXs8pjKhZL^ofruujFeA)Sp~xMYIsBkEzT2gH{sm@>+Y$wg1r`3MVOdl1$f|_(m+Oo-%Kvt zFj+Czt0*_J8@|b}D6DUhqc~t7w%_d7U|SNzCPgkQfje4b>THPA!rwhuHL;hCpVFx=?Q;gabuu7y3uoLhp=h-(ieU4JGWa#DRF|Pg-;*?etU#5 zQQzKR6Ww}E^ibIqu}(>7rrx6ni6^4w$1=v&ni5gF_swW2ao0e5Xx1rK)EN|6V6?tM z!{{D?7Z~esP9qh08nbp^Of6?@B%%;d(fwrpXzJm?lxXvcPf>Z8Gp_qa>6kmh!Cp=B z_0D4wW=+d&dBQd~@s7u6T0X074y&pL{pVKX8sbRAXuyal3S5*4=;#355z-Ji+^+@) z`)8u%re@&Kkku7OV5dpym+y~Bk<0|3@L)UXnRXmnIc6d40j4zvu;{2#;teC_-xV?q z29gFY7_mh>Un`}JVp#GpBQ1F6D-c^x!dk4{W_)P~c;l{Ux+bs0ABfBAm|Zy@h}KIU z-=StVZNt64H#pDm&dI5rkRg|F{LQrcCtLDW+LpXk;7^^TB?7FaREc1ad1N0#VKGXp zafJlxM6Fp`=mYL>`Np@4d6+ojw3rRs=`LxU@}whDQX3t8Rg$FG!M&fF(v;0s#xU** z{u2QKNeS6i&=kfW#dAbczck+GT>)0hcdW`Vt0ui;cPru~@N|vdafwj%!uMZ2wM#JX z$Yx2XLK5GPKOOdsFC5wnd1%{MsA%fLD!W(dlc%Q2247!F3f%{VjUF!jMtSD{u*&$G zXmz#T+C$_C|3-K^3PLZn8B1@Y*QV-7=BZsQi|~XQcV{q}LwE12dW@$!A;4!GI5qf{W}|c^$85R12$! zZ#_XcMI=>|dn>Gagk+uGn*?;HhP3>~S`4o{Z zvHGgnD_o*8n@7~$dH1>7_K#^z{{yhrdmKXHv+7wvDt*?jWY4((Y7K%F<~Y|$bYW-u zu5o+drDFBtnf~)*8*eGFW-e}Op|5O~uXz>JdIy2>UF#$+nLauy+*%3PbjSh6BiBxU zjiv57>bY>ZTk3NsdagcKoWV|Xbvhyg;HdsxWm5?)g(rtr*FbC70hG|H8ma#ezQzi< zoYMamAwudY66JW(zx;7%a|BVK-<`{-fmIwCkfH=ILq|@8*7E?Nr!FGaPmr|U52(~@Cy%`8OLJuJvtcfVR;kAv!*Vg1&OWfl67Fy^xwY@!M zkNgTfQITQcSr=iUkUha%qMR;8lLR3|?}=}9eHVl(sH@iT)q$O?{o6`wLo4`xnwLN&qiZFr$ic!Y ziBLPp|6x)57x9noCG0kiTQ>UJK^m-tw@~Fdm-GbNIKj>7C$LZq{H;EOmV04A*Ipd- z(4$A120(%n#=`5BJdgR)!i7Lj*X7fnOz8qcUJZrpF%+^b@IHf3$fnqUQ*~j_JzS6h z|N5ByH^hJOCh50Tcj~$z&***6oU$U#ivRI*gvHQj@!j%>eVo3%3`dK9q3Y5jd5^$+ z)74sD*Qdy>O!+iwV`jE7WTQ{Z1#ue~F5*NSGea1K2CaWyJH#Ka-8}47j0n2Ujv6p- zv8M9QJeQQ%gSMwCJ|4b`i`%d&24dkrc4*u1YNT0yv`3FvlT*jgMW7>LLk_8`vhu*+ zXovFa#eiMnow!%NiA$l4|Pb_9pHh3T0{`;H?TevF0ncUi``PX$XEW-5Z; zathI;yAn!17A5(`uFwAFs16lz{Pzb%Y*tb#CoAY_$5 ziv_-~5!a^uPF!r_n*K}12vI9F(Wa5*N6!Y=P|nLPozo6mSFqN;O|ey=uE>W;(9f!L zG*Sd|`Fyx>g^o{-5`Kx=cZ=l${ygofyx9$vQ?by@Z+nW>RZ2VNk*`3h}1=7g{&zb#<>C;HZuu^y`QaDZ~b+e zbu9l;#$mwRg;pWY zA+E*wq@Xnse~Zoj(;02JA8NKHG?Mbhk4!(MQfKOx``#^QsfqEFZTpe%``zNSI97ne zb6Rh7(($jhH6OLga-@Jz1eAJFX2K>Uq>vftE0>vcIagEHrDP)g;Hzgn5N|x^flv*2 z0R~3U;nmgcsuLFU7JQ?jMUg$Ov};hub*q8=4J>Ao!mFN`T3BXb`e7yH`6osnn&v#s zhTeDkVXD&5n%W1N{0^?D0Hdp3A1?iB`1{_M*Nxtf8p14lYXEnVZvMp8pqqU+5i2eN%g)yt|6?gGR22th(70XHN>rx-A<|#xjBU{i@ENgJ|-=Ud2c%BGY zT(+9A@O7oAt`c1J+C-M&kb~ufS-N0BymxEn11$9)tlvK&!Jmq!@{<*7 z6Xci4g1#t zH=tr(=_a;;igT6XWhTOJW>f=A*WeKP`EMKi0nhsyWo_>R%=;sCh#7qSe2*VkQ1w3? z#8T-+!f*9D6UZS)1I@_61~5%Cqz*;vM~LRrwe+D!c6u-TIiu07cSBN}GajZecC*y2cA00mEHJW%Z-&E*?_KSB|T*+dERF&s% zjWO+_(GKgVrSjR|3uL83pN$QVmFd0b&9bvuY{2v%1}o!-;tVqAUp+GbnT=zesfYy8 zz!kO|p`wIOr-6B%0b302Q{W8w{#5l?m)ai~p>Y?^b*>O4MeV!Y>1kHdXsE3IFf%ml zR(|qeLcPkTh{;N>Ya8Y4IxJu2M)EPMm=Be>c7m#qXwWl%imbg()&eAuU=eMc=H|B~ zbslm=Mk0*}wL5QSGY}_|f#jef#&XnXlrCjxkkNs_qf6Qq#GZ5*<}!KuXS2#EHXL{7 zOb3eJ(|_=rT<^O4j^;uA?u9MveJWh&Q2WNXau*5AE$o8xm(m%Bn;4%alJDr8F5!YSXbmdok&6dSN$EGR`DT@%5VO0=BZue zBUL1$z`+w9+Ma1+k&TO020a)&2k)H%0mF}DfRMSPAsy5mm&GqGC)5`^D|%eOFOX4K z;L?*voVJPDN$aoDHP25ckDTg-EiE#>C%yTEgjVo_`Osvm(nsgdN4YP*doE6u8KoX( zduAgCMv~JZM;I?KJ>tSFD}g$`2-}wnMRTt&AuM7nne6{y7BHB96b)n(V}XeV?oogZ z0(D@a7!+gaLTiG*=hveEj}wBBO$TWJpaX$zoDt~HHMXbR2WgThz);Dn|4LGrB|YeG zG$TV%gxLiD5t__5Ig$N`qKCLP4@nDplw39s7IW>nl znvM4VKXd;d64_#s2Tl@ol;VKV5I2ytEHu&G8lvHX{#GTN4m<-*syQQg26yf%IbO|e9E7YtS(U~8is?4;5QFUg_Q{t7bDGj|`_J%dD>H zzJIrxD)%ZMP|}$b?l`n*jom~NJJC)xt{j=C%7(iaEAe(Owr_zFluX_sf+7`T4?AsI zvvy!z`|;k9l?hL~GF5X+{I%B(D{4-M<_Mq8?D#*%Rx7S%Jwx>zPbe%=1! z&;MglhmrYk)r<%k^30xSmdUf$-Cb2ZL#%Z<*yf9nU)p{roDyBy+hmO0-DK=gAFabO zIim$PVUr~o?*i&iT)=4xoOs-}6eD_CjboyscLs{${))o3SY!L$NYR77YfcvPZROX| zx3Nq!uoUE%*n_BeA2WS1?qj@@48Bf9f=oAqX{SYnDouq|5t%~xJY!NR12(aX9A*x_aOJ*#cU*g&f=e`KB8 z>_2PyyF#HU+d9>Q4|9%9Q1iXJz1hJ+afPVh7odTWbze>vGwE-fcut>u`tk9pUGmyFzOJ;(^h>ON~pK_wC}Lykn=rSMrgJ71QgK2 z40XsPCEln}se)S5LJD*HA(pzhLjBtiTHeRl#TwL>95YS)YtKUdD8CaB`HAubakmm) zEbA7}F4r^u?2^y8^oQ(JI(d{vPW7>*=!s38YNQRF>Q7G=*soi*v9+df_JIT+jHF~) zGZ`_gZ-QJsl}4Ka&o=^wnU0I}S4fEMic5){nbZ4G6!MC5mj@Tyd}`%}Z%2VY{W1N^ zmFMMNcy@mjzD>DTAvfWY_O;B@a=80Zx&ZfY>;!CAXFDni&2I4dZx=BLX7t~55z z<@LcmU>TnZ{Gs#R*0hhmbK^V9F@MJ)O6aj^P0_ThTrTMB7_D)fI>163e@ksSQ!PC(!z0S*(Uj!*Y2>1bFT1 zM*qkSo@@P_P(8EZW6M|FDycr4J!aJH@J3R17N6>Bb+@?;H>|HjpWDTK+p3%r)bEw= zk&&nTpN_f=7FC<>nx?xxqFS%i;X42Iaa`ASY6i7{*7%dAdY{S`-&Vv} z1EsV+yWiF;OoB(E|45oUGmcWy*VrSiIrZYM8i{UzaFoE&ztI{txP$YcQO+%ciI+Hj z-!Z4tC5UWg@BD!&IZ!;qj_rC-B|dq0ESZG%$rD$~lZ$hF@tNO)OkpARVuS8w^VbjF+=OJ~Bi zF9!*o;lL!X9tIbT{lr5k`^`2`@AxQ_pPj~HJLNB_v|v0IlIh+R1F`Ss`y@)tnEpOGBlE`U+KR~#hOx; z8yC*o$dk**Tl|2__`iYMM@5Xjs?FA5W=}`KiZgyUJ}*;z*G5xX>S1ScbiU{_r*UsP z#jP>Ra;eJWJun%&k;!t8=pQN2}h`2(35T3Qg9=(d-kKj z3y0j0R+&03xym*E4cNFiz0P`>lguZe$Dn`X$53Vz4}ELi-GgpXrYMzl9@>YV+WLfj z^VbBAbNO*i%l|Go=8Awf@7JZ4q=kBcmDovISe;#>Fr839_;-_P#L!h{VHY)`G15_Q zPo;E^l*lM64vmsEZ7`fl-kx@Tz#?MYDU$DWzt&d2ycqE&Ip4M=S%j#JH(0TEwxuaI z_4$x>kHX>??kH#}=pVpPKA(Fte}rl(2!&hZcNjG#zSpMU#s76={R^*8EKb=3&-!6WT9&Ea87ldONR71##POOe*!tPOebiTb~E z@)C0GZp2g&??>$^{|tM<+Uer;1!rbIxAH@y#{zmiQBISf|VIDa;vT&uS zm21h@ts-)-NkB_)&&Bc!af1vO@DgdpIVPL%mdTGrS3-l<{qb+L$22Pg3-pYo`!=&h z33gW`4F%pUw<`=){<-rzx%BDUvavW86gE(}I)DVsR`D%CKqwwuL}Ij#aQ?bSoYdv&B(doGvAw3J6m~wJyGHPl?HU>VM~&5<$q~*Y!8eUvn+8btKs$0 zQ6BoW*PB|LK1s;y_{TStq+1_Y$iGuA^I$z>Bi^i-6=7qoG-k;wO<~We?)|=exVTKV z;$;Zryjo!4RzcY=-*?uhi;$8Lnw}ZAfleadl<<9LZXN%i$KNCIs|J^Edfd|HG|Ly1nU_M>igGoQ>_fyXuG@2Lswg34#xl!c5D{Y*& z;1mP7#i{K&-7SKD6^Chc^hh^Xmg+nRXwHHk3I{%o8`Y&#)jK$o)$4dDewB`{qpQn^ z`vvDW+4H<9-kmEwZhNuarmj~#ZpVA8bEA2au*W*N+h)UaK0h%yRALXTml|w&=aku* zF!v&)h=&7)rcwno_|P?*O}~6aop1m3^5sLv{ijO1$wG8vZ`1Xg`NF(mFU&R(j_SqF zJ2VHE;#=Lfau*ah*oVq+s1XP#4v9c&==^8PeeXY8?t}kqxkvxmk4GWiQ=IW!ybD0P z;s0#?1OD@(u=ekZ0zcZ5TUcl7!~gg5|F!e~`VfMA<2VY2{vSj7U&nj#@Hv#Ion;cn z8JeGOc*Mz92dPsi6gN?EmaNwa|wTN9@rn@JqhlV3P&Hw)+JZ7y>L$TgtBl=CzgG@8jG<(zJz{_ zZ`eOyM-2M89yl9nL57$N!G||L-fx|xO}lB}kD!zj;tNEQi#AlvZQHWcI(f7iANzOq zwxzwd-Gc~8Hv>d&tY(OUj>5F8UfB6G*pL;GCqPwD3}xh;v_HUi94lNl zQ~`=bi`15^T&Z{PKYcumq-x{WE2cHJ@kO(vzSN8#c+hXrwxUn#-Kz7xnsgV^fyAxJ z*h(`YK8Q&H)Ag?r564Gk`7k{&x{e@MHMjToMd_1d!H8d1;*I)?oIff2O0U2Wk@mn& zW4tKO^q+5X{Dir8dQW@wiI7nxz%N&WYpqOf3`|BNmDaEI5C4APO{Hes$pV7}+mS}( zjxyGelc?oA6MZSGm?FSsx#|%T<9or8@BZ|HeEX1#D*elQU9H?cZuHL2AYJ)RLZU+) z?Wfj^FG76KY8xRLn~e)4gxprsasdAw>-K1bvQ#`)hni7L8{jfuRriLT8sKQFkNFjB z*^-#S2q%XsN4g{-sE`VHAlpNQk>tH17DguBI21xzqGpQX%Rhy9tROVZY=klpk7c4} z^yEe-U`O$^5Wo1*SNlc$xX7pkkmO*$&9*h$buhv(71(~8)&nb)AaBXM;Kpb1*uR7u z7d5+$r-3o)n!r+c_T5G=&R35)UlAQ2e`7k@wYb8V35R3n-Hs^N+e)}k3$vN9qR-g zfGf-N=QV1^h$Q&sqirw$E@SZl`urZ4U5$@_ED>oHB;WKeZPQ>l9c36JK|V$Bxuo!c zvgc~wiG}KGU3&d|V8pF>Y;?ITX)mnMAMp^A2I;pm)~W|FAr2|XXuplRcRQyF zkOv7@j*JRA>7!4?;(Mr41VT9t17{U`2kEk8>=#)FX!@q96UUfB45=F&h&MFpK84f- zje*li-UsrqA`E0y63~ExeuNW|vLVS$Qvt8`w-9~*JdwrGmiF6t{e!4aLB8BJ6527C zHmQh1hoa?+SnO4$$)^M-!Wla*z-U720tCjUs0St`8V`*@AQA2)5&MNOoDPiOtubL0 zb4r9i*lYYu7|sAjaKq6i-EoO<0f|^uaa$a14AtNrsD&w#{2n`cw%itiLTuWED@qeY zk}Ek>J)vgw>46=jecAJD=Trvrx|dP0t#^Bz*&;s)Aj~Hxjp`3mcdZG3KEI^@mRmr3 znRfE|&hbjWu1=8c-Axl;@q4h;w>a9XIM$}mG43}QdD-WCV4TsWBDbXOz3J_eM3QG+ zt}!X!Fwk1&fKI+ZnIQ6&|4>rN?f5|+;Fz@FJmrGrH2sE;&Yb8_W;LsKBW-EymlPn| zR6?f+!%#zTSl9?0N&e%pS2V@IO|iFV9PKa;M_c#p#@O#BDUkTHSgdO6A%r%d1O*Ra zA#kYD^<2Kg*9y6t=@`&IhD6GlPVbO|#D$EN{BPUiEKzX9=^sQle|(@zC0$^L-1e}3 zo4n7o7wro_f2TDQ36ebU<3L6q?nPOh{(3O~>8F*Ev1eM)$V%4+9I8e_PTWB9npag? z`27Cs%O=R^E+_kJvA}4?hlwIZ&3_-@8x^K;wD7Oh91Q62u?>7nDGm<FX{uatsJgm=Kfzapq#B*LLH{4zLao(} ztd?!8_)z)Z63OxC(c9ChwSsa$dJ_DVz^;oBUatF3kz=gY8KjavT0){PU7orm*T0mM zK$4U1junPnHdmV?dB)%LJ8yPBD#xEL6&*6@=ld8(`!{7{Orp_(?KTe4H-d%JObzwG znqqT-sb11H%O_8;*813031swOHSVE;bI=;nB-YZwWdJl`T^1t0 zjMnkzzTV6aBGoZHur|~z8j;Tc22oixeXhh8xC=FAMrzl8ncHkTSbF045Ncl>jet}) zgv{-Aoq^@Y{lWRxpG8+x2pMzUP${K8ldTL*;4@VOg3o=;xt}H>nLaGW9k-D^D2(W@ z^xlcsE>f4~zf(7P9V|>hq*lkmT!72~15@jF@hv%hu-nP_QP1ABZ;z4wLmZ6yrHOEx z1UMQ10|1sfxia)?guWU82Yq^Xj5W+|7F_B)Kyss#6S2yERm4~fT8|0jy#yY-r9m>y z7Sg;$=sy5FioE*gKqaJ13Rc1Zo(T@=vS?ZGy*~c@4+D63_y)~?{1ecT=XIbld)BT zJ@}28&LRBDrhkutW4h2|2rxq;y!gvh77BaZz2`iRw>*+*7dR2#eqsQlLigBTyXYp9 zf$g4_eO@zFNa?me2rmtXql5f=TGN*g#6Yb$XrnWzuzx$;XA8D1hmoCvnD8Z%W>^If zL=GU__vPK1dJHT;DQKMbxY6yWPu}2=DMx0Ohrwk5+oPV*2nH1V<3g1T3hO(+eb2&r zoI~c3)5*t2TXUS?>C&AP`1;uD-mrIQ>ld54b<%)8)pzYx{!WFwH{QhViDq{LsS3F2 z5Qrt}$nTZ=A5M;SxIe5=ZYdL?}_!w%NL#3#M-L^8~o^x)6E|% z`Q8YGEFw=BUj7JIQhY)}NM9afQ^AmDMqlANKEOAv&A#{N{Q3L4z1i8*)7q~L z=P*b6`rg$YzlVc{vin7NNoc*Jna6+7`}Uhq4)(YhW^-2i(T`1h503|}`gOcSrj;bi@8?1*H^`U)ay;@$G8|dO2udY`*5SBad7WsNNnfGyoJmrHR%dVA zZ_Z~YG@{_hYXuqh5xx1>_Xr6;I@9EOuMNYP-Xa`W@TkJ}ABcz6DAdPiPe24x^R4jo zO7bgdAFf52uB|-wc%%s4E|yJB;4bTUhl*=eVLUQDI2n$*RN!s;U719g`UY}0$r^Jd zFCfRYw5tkRJ^D@Ur;gz=B8lePL!2+)w;%T}C1d*w5GXpvY%Jf6y%oU(cH(kPt!dpk zd=qe)-v4puN*?)DSvfWB_@}197Hm{f2wD}eU#<_P5jFz;w5gzFuq~T4jFex{7AOg~ zdN9%WiLe9hT`=cbdry7S*L8>3@PIV)$1qGO8C%{-Al?T%4iQ5y&+5J{-)h@^7T2QX zmRw0P+2>8N<+<5Qb)v~-v6&(ROv_MyA2t(ND?m7~|Az^(UALxCsJ8k6?Z(n?i#@${ zdGmGICPfFPS}v=BOKWN)AQRMP0jbCm2&7(%VIlM zY{8o#3ZElPCDBz;YzZ~Cds~%}$Cin{CN*cu8IpvB-zv@x7 zohB^ks;@lvy~pZC@6LjP#>dn+44!_t*W5}I)BBwlA$P%g9&gh(mWyS#f9>MZCM9&7 z{p^3_m$IhIh`K@v&Hm1m#PLyk47_jaOlX_hK{>nJ0rSw*1}58?n~V8*7jj64&&Ig4 z22Z=c9g*PA41lgdGa5P-_UjaO)&1COERq45*0f1D0TOZ zmbwM&>Nx4s@R{q#`v2bFr3;Dj&OGG#l?04&S>)p&R1Xstklp?^Fq8kqQXO zed_bKh}{mj^w*L8$8e#%L&C#+CmIM=cM?ffxU?@AH7IM*H0<&X zv97lKXkV4g4PQ|B{?yb-h$X@&g?yRK-?Aoidm_M+EyAdG^1O?dH~&6b#hVZbcKN`s zHg-j@4S#IOno~GG%rkv(JspcphD+pZe;ALti_nv{9CEvD<%J^>SAi1CX6Sbnr#bER z6D+>i_$@!oO=gvQHCVO=B;IbOWoi^52cX~y)k)N zlj=r0xMe}{v_TKhpvcI^W&mvaM;JoZy-toZsF3Cja2bbn#k{pZ(a2 z^Z9-uJj!n52g0bJV_(r=Nsl^m5Fgo+3Kv)tcP|aD->*Z|tbaQ(nj)VbRPp(N5~$9_ z4hTNM+f!PH1^aPaGS|8jHjG>P7NvbzwDs}mvJFBzS8_?6@k5&Dxsz>M`_xa3YmugN z&#wh`=0(4k&88$cq*jU8u+tj<7(qz#0Dq*Dx+JSng-tl)v(Bkg%>)BJ z`qZ5{)$;pt8>WPvWEu7qBgVyq74p^@ld65N-h>g5%2;d8f$h6*>6^x`_sK!Jb#skG zsi_t7p9h6{<&BaWXh1i&-3D@Ur$*jurbGB^>~*YDVBG!hY;uM#j7XV2rRXr!{;($bDYz461P%DS{OB1oYk%z}KSU4PeedYgX#L)e-(tyD!2LKi zW73-u}B1>eC+ahx63X zTF=#mw&Adjaz1qDm1@6Y7GAXowBLfGa?Jm>ON=oP&W6_IsOg@pkH(q0i`Qnz=)MIMjSg6+!&ZzgWv z@hBPFp)CI%zEgScWqZGOmZl(@mq!2%O@IAttZLRZZ5l5dm76IZ0%S6>bV)0gVdU7W zCKGR>t$~K|+qbXW*BTUmeCXIR5V&}I)9UC_#P}8a8uu`gHwVjwH4wqqlHrg7mkeT9 zxwQAwI1cWd7I`AC_HZ1z&o$q|^snN-rwfZ7DL07~1MJAL^?c z&$WIwrbmrVv%um5nMg2Hlw-E6q51GImL=X3;SlxGE1)n$*p>6@ zrOYn%pL2U~+WN8aU061>6wFa=3aO0UMd;0Hz zzb?gog&vp(yiWbL~m zVioXmRQ2Iw9&$TMWHe~<;Vyc!Ijff5Zcvw9FQ?e=^V`}GrEdYnHjxRK9}hLW5B&$8 zM1B4GFyy3C$IF~m`T(w(<#cfJ{St2C7W0>vW#!;r z32Td5&UMIM$iT>_~Z%fYl*k`1JWR2r+{Ud(uJRF^sho0s*o zxhDfZ66=kY8jsrUeBO$q`Y06N)@+F%zGqf=J5ckllt9CC_nf)RbL-XV>7A~>h)Qj> zmjw^aAUl==C%OtD3qhsyF7apo(yA|q;XBFuL@xP}JZF4<@l|-{p1@&HnDShY19mh? zV#x4FK(*@6w5a|&`^bF2l}GcNeZ{T7WAneaxX=k~P)rk=5%Hn|M0`GS zhTTtqhdX{)qpeQH=I&6=CEi7v6P>u8l>ZVGX^1UaY`WDkw8i5dt{1kg#2 z6)NPpCa#?51dSehn+l!5mYWv+eV33>B$jIo%KgK{`D+AZK!Ga)ugiKd{$+SZkQj&5 z2%Z=m>&}KOY7jswFXI2yn&z6!=vb^31pTL4Mg+&Yl5%DkzR(Z*E8&Q_S+2H@mj6?p zP5aLnBUvqbxCn52)$;p>FZFcc=~*?}fTS49QHu zCd8UuLLSe^9W`1NzI5k!>J9;7dWk5)(cX~bL{D7A{q@$U~ehz>WLXS*t}w3mPtPP#8j2-aSMVn!7Kk{hu>kj3HuBxVknprzfVf!i^QObqBst6DwOWq)q2|!|C5T+j3 z3p0W)fIyaP<-mE+Y3e{26%v$3k`qD~MWELqvta0onL0KdIlQzEUkj-&nMhnMJYT^8BS6deA|a`zL>T2y0t34WBN2R4y7 zWUt#d2I6^~w&Xs`Su=xDAuY4HSWfgiu@#g_O!UOFh+{aAwg4>-A~(I`5X$YYKzi!9 ziVe^qW!wjA&HB2{45}zw00lft`IdW^lfM4~gFw3{N7HqUQpjjpTfp?okH>EQ6qQ5v z2Vt4__7C^IUgbx>lUPC3Vc3gPSxPLa!P!cV)$31`f}_DSNF&)>kYQ>G?}fh5!`tq=n0S67dPWZ_Y6vki!9<_X*Ig#=$NkX z#-dmE_z(9eAk!{wA95{tIWDYbE?@GVczI*@Uf+kKhI~2n0g}c8M=Pib#+|)rpoV2V zZ~k?KMTDbAyFDd*^IXhbZwR;bJa{=b? zKA_%j7NM4q3C&wAle8)D%@UBYke?T3qqoW7ad~Y8HIo%e7DzI>s~)) zL&GnDK}6aB5h!;*%P_U#`TR{Zq8HDSfPDb)$0lil$#5K!AIa2Oe-Iaj?nD?<)Z$WDAE%qH`uPDni%tYUFZ9oC)g-^hZU?3wc;{6YIfF^KZ zoE?U)Nnvr@S4Sf?F1)t(oambI#J&@M!Od0%VYCV|JsUxdl+lqBsrVHlbHI0jVk@r) zB9H$E`rTFw)Q0%N6-PeXy_#KXMo%=j}`Q`|W=RFc%I!>B1o7?|W2<`7SS% zVPOrB&lmX)d8`5OZ{XpPf9Y_j3&%3Vq}BC>kdn4Ps*s-_nQno`LKzLEd8y>`Jc)4k z9(&})$h4|bH#zP;olL&5!4`%js3O4MaZ2R4wH1nGPSr?2+`(IzH>))duF_r=lD^*nAWl6v{mss&x}O_9IMGOReQ-myjPCmJJM7b$^UcDZmt>iXXuS&G{T zYX1TzFr#I}F@_q{%K9+yr?dKjZ5Sd$Xde+nA{1Szx9N(Q_Ag0bw)WwlpI*GGQHZ7;=z0=zowsfPK7ojj zSYt*1GB#hNmsyxK%D4+xT6SYE=3cIVEDqIEm`0p_bY=R$>i2Y?1R8VqaHcJln0#fl zFGQ1{I`GX3J$LCXovG!YBYPPZSN062_cOWg1J~`VcCoA=@yjCc$!in6T#GGI?|_hJ z0o4m7!{>oB@>_m1zQOwVUNESUE;$=n&;`HI;V4>sVBCGK{cU&X8Zw=C=G2TE{dQCe zZEMLC%Rq9)!ThKH-)!Xv8kyN4xThQa^&Y?Hm7D@$u*ydRx6s^TXzpAKr~Ofm!>#f% zXHKf+-+!K=o+TanSpCqXtTPdf6=6UZyx@$bzvq7R{pg%xG+kpwdvsm!mgu)*i(ZnL zLfnv+Ly^a?FQFfFZf9J@+4pQI1w1ei2eXz?CZ|XIZ_*IV;-0KCO=sej2zNypRv$s` zW6#+a&@nYFrd(hA=c8O7y<)GWY!3h79?7jCd}DZ4`=)(%lvT}q?ocdA@|%qVmfnN) zfEW4IYc9So^KU(*Y}9kA%>L^ey*aaryw;)h7{}u;arNOr1s22d+JRcX1U?3agDpDe z>|}7UdMQxKh7iB{tM2-9Aosqki|*H$*P&q{OU*Z(aQ%mxW*t3b3>a#TPvyl|b;sck zsYmlW!f7Kar8NFNx^`P%r?cV|Nswpw#NpLUq!IIb5k9n?En&x#A8KcAhGfCIpzw#<_XM{Q;k5>n5r_1|hf>NX~E! zDTbGqSQr}|XyBU{GuBTi2Sy4|@}!ZGE{AVOT%vw(MTpY+af0y&Y9x&Vys)oiR#Se* zM%UXc!@C}Q8yP974SN@K|MiOk%h{KRPkB>UxOjWUA`>R|tIbNYFwW_S!56JkN1jP= z9#RIrS1`24qH~2oJ2UYS(`(kpyy`qwTnBl$dERLm%%j8xxk9o6nv<&lUUyah%=@Rn z4vNyg?7r=0$Fg!jr8m)XzBrpDg*i_R)(zspZa5YNqnC(JL~%O}p*Xbkq6jdBH!$P*aL z73{j`QdFXHIem%XX7e)IXNPZ$o3Qa;bdP0{S5UY;`yEW;k1D0V6#3LmwPLplVf`@C z0k+u-Et4%;R4+_R;&*peYgKuCSE(n6+TEJ_Q9R&C17%0)>8Tm>(7gxHCH~MuLi%GXP!9sWs8?$ggOhZ=@VPLhp zk+tLl(zaQ8Z6vXCQoOp(r9YHM515tvPMRtAd3`xIheaP7J;?|ctsB-1V3TX?{^Q4I zl$9h`$g16q8x;ekwn8&GPB$Ki+V)^-Gsq+tcRGQ*vbc^I{3h zV}u@@Y!k=P3f;UdeM;|!rL=%6N$_Rh)yim*7w5=AfLoL!o|JJ}QlmLQ@W7vBsh=Y`^?~VMY!q7eht8+T{!7clK|AUx zq5_U#or00PS2)WrQsF&@sve8ZSj^x?FTe7Vm?&aI_KwT9ADQX6AVJ5%mT**2D0f6D zOXK;sSI-b!Fp@PfrR#Vuy8~`>`8!Qi@`W?QV_n@Ht#rz>Pu# zDf@EHkEOqK5RL(}7xyp3>o(hI`IV%KM|DetxJA@8FGbC(tJI$K#tK66-}B4D z$aF(`kQN*GNhdgL6@@J%D%sE;#tQ~dc)2(;)%1;}@{su?F*i`YbX>cqR&F!krEFCN||0!ep$M}S(4gt?Cc>B9QaM-h#>g4`K=v8DT5a~)=EQwXhxZIL`zvx z<(BgA4-<|CJJ5pELhmXw|NJv<56Y08_$qZ3$`1<8p8HiaP>9i`b`yzc8zn9SU;m_$-nqa4x+lj}mncE4Ofr;ZLO8Z#*Up^&I{r1i( zWaX!K{CjxJ#Qos)A(oX><1)I%jYq63xsm}s@oE!;m1rIKvLpE3tNqCTU0Z48qCAec zIUsgp<~7+|JeGBRe$O+>U=t>g#6dVL|*J ze`ecjzy2pFf(JkBoNLdTdc&^AxTgH>>;9cPRg@QVnFDi9#-?e!BJff92J4RTFAcI4 zm1)&mo5Qo-rmx29tL+9FqUfpy#8=t1$-%5JAKXeR6bz-Uk-jH0_6|Rb1iOoFUGv90 zwlKuheS22@==*T<$ez$SXQv>|xLFr*^J;ts8LoiUhrF#3PuFs1 zJ#2zbk>;mK0W3UA#mVuFx=#}620)gw>D?Z$^+9^@$BpN%d3xUy zzX@kp<{>qna-m;DRrtdB2G!GUU{qe3e+PB);a;uPvb9>*^D)h32&B5|O zptsU~|8tGVos}f@yuQVyVyOw`)xGNcb42sEXSgqFd}-g___BiX5`xjLuyCH72krZQ{OI4fAZUpzo|8 z7k3rK7~bxW`}^nUOr?^bdm%NG^=e7YG&};qka->CUGotY?SZ+ZUlWG|80M!tJm~9p z9<&msb%(JdO+DRls0dGFL!>CEBI><`iK(Ln``fw5*inzOiS8zohauEB(TnaTv-EDA|+t>oTBmCDT@=~Gd z1UMTq8k_pGT&Blf@4-F;RA+xtmrh+LV(0B}{s*-iQDELr?UW4uC&@B&qyBYl`4{LEpWvBsFs zrReDGnxBD#q-HTQXWu{X!WvJlJdZPOG|Ca9)rdTMpYk2Oz_PDrVg+9E8@0naVA_kL zi+hTQQo7W!D$u9v20#vYe-%eB$(2(=#kIo>YIO19Uh*?)D7FRq7-{z5liV^im%j2M z^eKRacKeJ=z|C;1RrGy%PmQE=dSY||DcZOLHWU^K&tO2jVMm^Nn^VY_V{8uPC@n=B8Gqx2H2hzkDMv^eIpFIQ;nu^HT0NCj(GI36P-8VT~=o zq!Orr&>xju(eHN*hnqR~y{eI4MlxA`jtGapfScL$5r6E;+#6M+LBt#>d1`oS)U`Z* z6<%gpRIZkqi}eiACqRFdMpN0=ZSu1i7f2V9@C}XKLc}=6x5Iw**^9-oVEE3n{#f$0 zR8NCct9lkuD<}}+nAZHb!I&%)o_p6kJn&13VlRkWr3+nv}|q_a#E{C##K3*r+VZtKZ}mqz>0frmX6qJ%5qy-kZK zo=ct@_q==Ai$;aasyh)aA7&9qJrSt&RYWznao-0b3XPw*?bXMr+3=)0--*ztlm|mF z%Xy*Y4EbWG2ipE{`{~(x{j!hVu4;RNdGn_s77OI^yC*x+g}w~CtqdIDhkuhlSQ8s1qeD<4VC;{g*9H5 zUFJq6W`_2JhTb6Fe*gYWYSY=dRdrF)!=~$o-<$604ei^jsquzO{`#iz^kn8_7$H<^ zQSuV$9B2&GsTF1`sr*uksz1Z4+OlUeYlMj!5|%=j^jrajn%8jekjJEiGRX9P0qSl%H8eCpn=^|j=OJkKu&0OO=eFEZwaL0b_)2wHeYO`HL!HDF z3$8s6@v#1H$5@f0g_cKWS#^OxMjYnX?foO~k4Bpv%4RfzM0#$y<@iO0bcQaV_Ql0(TJBpsIlDPBdI!?&rjm{=Jq#6R0qcvy?x%fI!;t({3!=wm?4%c zZ{A1t>I$<#EZ6|XdY*L19djYo49KCAy!ippb{!)R2a(90e*wQ@NmFQy@%biyttep9(7*@4bINmPvu>H_LHQpXl zmB)u?2qC?u;2}x@bcaWk`|!%Nym*65KT3aa(mQB^ghT7FJ!qeN=n!!2f|>pZA@Lm(wMx57T10j()-!&C zeqBGM-0}-52<;>V1Oh*vJq#51+q*%Q|NccunEZzIx6dH6HB*7A#o*+=^M<`3L?;v; z{q3D_d7C;wlPFvf{_a)C(0v!=iUh7_#?4=S3*x{+|3y z4}WP#4c)iF)Z3FnJzqk5nf=mf+Jj9mNL|69Zmm<44dWzEtX8UZ83Jlbh>H2=qMrZU zJ^M0uo%Lz^Toc%$qSw&XHebc^K|Vi`$bl&Ru=K8xlpVgQk7aRzk7BkRa&@3NMenVfUekU?dsF&Po_x{{a(VGsC>et`&&jt4-ok^ zLEdy?PRPqh>H=Vi|hERRFs=kL@e5zhHeDT$tJuN(3Co?1&@-}FI$nE;d?S{xfz{2E|i5rs?SNGja+w45|Npyh)1;)OGx3()0c!oPf+QmCRskNVnyHTdoKTEypp3g%g z?{TUw0H3tX1ZOVwVph#oKl_i^%e2}%S2+>}H>tjb6xW5&TVoYUy98@h=e=k+ZC`8yj0zZX)(PAxpHG~$ z=t>kUGg2N>*CDuAT>xu<$4599;>5WH;7cOv`}FtZ5I^T_{lv9bwzY+i3iHIaB8X2` zs~#o@jt|u5EZ4Y&QrgijzS6!r{u#dT(lD!)@lWh#Q|D)QX=Mu!sIkYNFj9MrV*9aDgzcrRV+D_eSfj9o&TKXvkKtwuj|d~|aJwUq2Z^jFimNeOGY`yu@O zU9n?#+L`s5JON0uJ7xmt7-be;5+#r-2Wx)N{Qbt2d-}4u?tL=a_foc>pb3l_TDu0{ z^F8}I`pd+tEIyU{x0dc_1;KcE7kJ~%umzsRlx)HR!QkdCh&`WNq4$h@pA%`uiy{U{*QIZB0>%Eo5_03N6saOf5h)X znl1DF7+O_mG2*-cLp|)+8)LLY3S0ACG+Vo&Pk+CNocmr3$}ObQJ6EuOL}gms>M^&T z>nCjoRlHj*>z-h(d~nA4{Rgl7z1QMlZeQscFP2F2wkMB_9?z5ZJNkNlF%UrNV}|Bd zWvZgy+tH^9ot%>IJL!*%i%)Ge|Iqx^K5{)!CT?HF==a;Nt|rBhENpTRC$<_O)f*&} z9Qs41lG2F)&HHiFg%9j)TmBS{T*SJB-F9a(<4orVb}-8N{4i8AdyAsi@~JT;;&lwY zT7e<4-ZllOF{e?u_o?LDzKAF<7jM5M);_|IojlwF1vjy{yTO~(^u8-f|L)?Bq`iwF^_`1tJS3IXT+wi&9RM`Y7bRrv0|R z^FY?$+Z4a&5RZ6+B)?MOeH_C54~809t@k&C`9vR(2HoRfiMNIMWItyzY?;&~ZnSd`mvkx;yu3FNXL~H2RQ~6znX-WxMWGpPvomyT^r*Su4q5jbK`6}? zB$m(vInh_%-A1bcP#To3P)eC1u%Qkl3Zh4zv#<{5S#t*>*O(e9TMrg+d0P6lA&KXs;Z;k7+6^-XgvB2I~VDiiJgAqn|gPiMOgKbeTR} z8NYc7uvS`z^C5e`sm?NKzS$d#aNi@^Q3kWY4ml#pf|tB8&Nd9I5+e)vj!PnVoovbRfaw6AMC3!b7=qx{3FH~q|x zPk8Ivn=u)cpJpzv%BPJ3*4+gR68SCj>JYNmS;OtEt#5+j?oWQbT}O;!ue*>3MO1m` zOZu*&EcM}wua$>qhO|WP8u$ZiusY&-*px%^JLamN8k?Zc$?+A10qY+`@g&O$>ury6 zvD|(=x57*=(U2_jX&ssM;59A>#6QxR@0=-17EbzC<8*H4YY% z056{>KoEuAjl1H-iM|8vliP15OGDMO0I8OE*dHet6az&$>wM|XI+UCo7|?5A->&nM zoecb}jAuEgSQJT1M}B4N!7ChY!EXBsyvlddl>NU-JNI{}w>^$iu0_R`X|pxC3`s`F zV24UG+h#DEX^i3PHchUBXT~j2l zvcLTW=jl1?x3!-2e%5+EpC8tG*7|&2ADjfbwhfcnKYZy(j&fM@@M4c<6s;WH;oFTj zVqkc52pC}!S%BVTZbT*l)O8~ zVXeIfe?5{GET|!>0hsE#z8DAcOf;UlWz6OWqu>NIk*+-zJ8o{q$Fw5?=m3X7EQDLs*^+U8N-)?LbV8e%S zNzaLw^f5SuHRv$%_-nd{^P9{3>Ap08L-6V1>J=Rw(91T++AZ19WsoO$igNYAZ4ke{ z2PKI`Xhit1hgV~InH}`1sh=Pj;Kb9im)t|(@tdfZUIGIy2~s$ck61WjcC&}4W(~$+ zub?=d8eyr52t3D+D!4N6)ekNZWjuu83(y-&kc7C#DJ!M@0oN3T!tL{G8=SO-@pNyp zrrWnJZ}y{ibUk+^w$%>Qu+4}fPoY->Uzl@ub-XKRYBMFRsjWz?A?wUjOXRFAJdi*? zmx*qIsW3JRdGhTlru|OS7r+bs@9QA|{8b6v4bU9|=qB3mp>oko=tUd~um4{C87~vv zks+d{^OJgcztu7&Ib}x}%!|085_&QdpmV`a@&Zhp!TGI7XaPTCv^*4X;jzLS&?*e%&#XIF=f03sCvfVJeOHWmFb!?x=cBVmJNk;k@ z?2P&9Ua#^S`zQ2sa&<_XMFeC%E%6g3*2w&;0WNAQQrj*rPeW9g?{R)qcxS;ZVnuo2 zEAvqUPPkg({GsRs<1>3TERQ01VwZI)n;7D_C)#X>9TfOeA;52!tc2 zo4Y}L$S>|=Ws^IS=a1|d{QlDZyaZ~QmMcO7oeqB4&Pbh2BW7IsR*g@*UJEcE8|bAq}6SlR}nlqUXoNoItMxSJfF+v2|NZE>@6k&Gypwi^u&GgOk=PG~E= zuK0AJo?Yd;ud&@^#^T=!n8!DuaZJqHn?W#_Os{B(10{uftM$ z%dQ~{HEKUuRZi5w5pGpG1E)I@m~kxumBKVxuxjQHx7m%k+uubOUhR#|p7~)pC@7a@ WZ3V_$p>F;9UG0c+MY8PuQ~m~)jDeE? literal 0 HcmV?d00001 diff --git a/web/static/img/deploying/wasp-app-flow.gif b/web/static/img/deploying/wasp-app-flow.gif new file mode 100644 index 0000000000000000000000000000000000000000..4d27433dfd9d7badfcf4f8b0e387e0171edc0e56 GIT binary patch literal 172012 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
0*F9?L;__xh%lkTgHHxJtfNqfLm*fJO}yxk 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

y$^T2#Dcs*db{82z5uto?~9}{6urS)w@rdF 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¾p%Qjlp)0~7#bo^(i&bfSn3SXehHIj>a6~nvemxN;s z$dzfvAy_f<7PN@@Ys)vR3=_WQ>BlSDwsgG33Yuu0D|dy9$ujXRdegR9EXonWw`Zrt zD}2gS6Kz3TsSu09`*olsEXKbJ%gF-6xU?qiO2f(AI&wEBeN|tqYI&8+qP+RDG8VVB^|R?VO>4QkJ#4IB`>)_!#*jO107kT3 z?4(`Xd$d|w?qw~M%&==oz2}*#()^X6Ik^WtBYT|BMxlHwL!X%`UkV#?liS72+pH_4 zcN4U78VbzwTX%umgqKXwn4-+f9KS7FxEI@9C}#q-mz1VuIk@a_Z1$TkWlxg(eUXa6 z3d+*USV|qIXxJK@X?wOEH)05l%5Vm+d{=QAx0$`u9WBi1)S(~10(NT2TTR-o=G93kg7U(BG4P+34cu0dSh<{< z2z5Znd%eoJz`=RE#TT)lYbn*ddjHJS1OYjpZ91$w1hwnC_^jNQ^4RDNq1A+0= zv6B(e<{cLRJL3N&;ykY0lpM4kkWbRR$H>PhCBAc@+$Tm(am_5WY~9wz+TQ=WvN;|W zJO1N6V&Ga{m$I6|DlB5gdpSsZaYwtGjv|%SOy;@Tw`+dpeeK)l-Q`>E<8)rv&f=@_ z{1FY5UIQJ3y;V?~Z5y@=ltOSTQe0ZJXmNKhPzuG}T}yB$xVyW%ySux)LvYuUMc?mV z|K6IFjm$Hd?Bt%w=gV7D%%FZ_(gwVjxm7Q~4_Qc02jgb}R#N%o~_rO@C9RP)2jv zix+bn{%AUOAyssH>W0M{g-H?__#`^#;<2wAyG-mnt@#a2RC2B@cy=)4s7xI~`BxqG|Bl#f9bxGE^T#L2w6*SNx$$HCaV zA{B@vB)R4cy87aE-Nt=Q?PW#TdCh>9OAEPXzPe)3xuJhPV(*lp&%EK?Okm!;VQRb) z6u6~-x)Bu!+pxQpM#~oOysd1Slq0!|TfSAiz+lz6Q>_eC%DfY%zSV@>O*GxGHZyY24XnQ@lW(Mm+?(93?qZvPl$K;(C$s z&oA>X!0R#0#wR%PE_CxThS4ih=Pp{{DQVI@p7Aa*^C<((HI?Kp9rBbn3CzB@&DD7> z@%mHPd0X81Tm`W&ue_}!d1>$puINc}%JKyQTBMUGcn0@^;`gxvTTM|1SO9^vf}U z8;f}I5==bPfzUs`p>aQj2tw{RAy0vjl1_-r1?0`f;fB$*v=j3BevRhweqB{bGkXYm zd+$cDfvf6-T-k|EW-C58%`rH=;$j#%^2 z_MZw>B17}Z{$M;^An-pG>U_E1db-w&)A@S)?zx8+?qT{I{ zwt2QWn5hGKi{Czk{9hF+nkbXdM_lA8{x@QOCSe4svJn9|#y%zy6pppvB0J3v+p_g#g`gmwL0QHmCP!(ee7ouBsRKwL#S&QjA^A=sy)|W&pdo zSz&&Ix#9;9rwteq6+v8rD*ZnbLM1o2om9~QJ)w_XhJ*t9_-^~c^$?%4%-|i1>pG{rzNP`9cSew z`2NhRn6&>{)CuHsS~e+ccUrX>AMlMa`|%)oaQ zYGGlA>q+GxzuQ^kVTapAC%l0B)gV=;`^}_;fXCgUNvFrdW}txQ(_vw!=gY;QfY;xL z!%i;<1YQ^FBWJ!h6ciZ*Ny`7joznCKW9%q=1kn@%{~`Q-4`#ul+HYq zFTd2**3~XN)ULfWwD&bOHZ?Clv~0YzcJ{V*^|$qov~R!k^!D`(j`eIj_U^v)4ULbE zjgL()j_*BB%q~qFzs}6g%`B|U96ZgQyv{GLubjWGuC1?b>}+iBZ(RP}+1=kcf8ISf z-c#V*yZL)yU~q7Ja%{s7zW;lc;CXg_b)J`Z{`mLe^7^tU`l?Lkx>n=5$>jRx?xw@x zrVDt}?RGmDbUPk@`||g0Hur9^^zPy5ey!<#ckkhF^5JCZ@$~5N;_~qt{P^_z^zi=l z0`>p>fQNVkef|6cK!HKQA)#U65s^{RF|l#+@28ZUlA4yDk(rg9lbaX7S6EbBQd(AC zQCU??R$Eu!@O~;St!?!Pon77UhtWSUIMfaGehd?nQ`6I(b@>a6OUode0TrwIP?7S`u7b2^#PGkvMNi+^Fo*_HNGmlHxQjdI!&@V zr#}?$yZPEsH3bU;xU zA8lm(-Q@Y9Rg1EzYP&ZGi%z=@{SD0Tp<7*qvR+}77HpW{UFE+dUUOdCuVD;j7g6VW7+TKGoDrk}vErOQ2 zAif9^ZjbiXhl{P=&`Coou59$`!BUPyBB+t3likrw`OXfV)LY)L zI7gq{;V1ziP61FwTYl)`)Z701c@oDj#-|{V>)mT;*E?cyc?q+PO~4!$>ls4 zt5mVLN2+Rkyq~BGdXjSya-uN?YK%A>q*%U(YHWr$GT&$ZCVGb@X@`Nub zDh4Z?msSnXp6Hiia-Zm)6X~Cnx7~wJ)au_{7fX9^EKaMHKZbtO=wmoJt({t^Gp{pa zlR2wjGESYYTLBrFG;V?DtcBMiQs)~F3QEsg!nu;@S{2Oz7j3r-FH+6-v|nvHUhhvT z$sgZj<~m?-Et%S(N#5%S5IIh(8W03{v3fC8EbSoG7&@nz{rJFB=914|mY9R&iI(=c zWSMIi!?ca3HK{b6VHl$EjA{a@W(kMeXeM1`QUs^R|Zwl+zBn8n??n+)jkc0gj?)Qah@P z*P9qpw0657RFLxrkz@enfkp!co81gmtx@-q42S+2qQsdOV5 zU;6p4W?gUtdN6;>`L|PKBd82*_=iExNgAM^7UoAtdG5Q&-1NJ``kAZxUJjndfmk8uw=e_Q z+@TR_0oIbMa9o4j9|N}hKFt^r&W=fhf%-5!3>T<4W4WXP4H79;+j@fJqNMP{lA+j` zvW|kVm@62=BC<79!GwL}1jEB1J{AZKf07F2$Le9Z6h&R;8%_NqJU+Xs8OmJPJa~C2 z8HfBCs=#kzUq4p|*tXeY$T4j_lwFPPioI`4l;yJ=D$_WIe@zTIqM&p=j?h={L0YLQ zP>kXlHFCXCAAdFYUH1gA5RN5MA^pg8VLSXAGnQ)YwSadXTW(2-8uP?ol>Z~yxB_gp zYn!2{fCuK}dpCjR9pMN+m)!(^`#YEf6tD>|BLw5;XTgtCH(=7n``}}JLh1)eFba)Z zo+{Gf%@;{$_WvC0j>J6PLtr1Kkagz#6z3&-APo#*hT^(m!$ApPlW2(s{r;NI`=g{X z<>8lzIe8w(Ur~igJzpp8yb@fI!$mR(Zyc`*T$BOXVl_$iFr|XofSZ>+^@{tOL~<9i%l3W zgiii#22NPbmA4HGOy9doh#J6h3vu_Gq;C1JC!OAhqJ{XjxKFhFM7-NaGmAac4AUyW z04({W2YkjiQ!;kKWr)2OfIq||5@Q&ooxO;_2cb88k=GseDedEI9LQjclK5hst29Ux@8cv@K3reWV2KlQsz zI1?+R&jSy6UzYjZh69FbveBbN0(n+r=vRLa+@EHBsE&NVkflo8OM(o z4PD2&`RocM!jt56Y-aQ1>h)z&_>@YY=L7WaZZuI&)NjNVG*Rozf$d4SW}hco4(h8= zb}g7QxtDt7oOD7U>BhU$>dRB?NVPued_N^#R@7;nULv%tEZsC%@iv@FqyL`SXNs-P zO+?m@kDupdtxk-}*t8y|pV>}E>R%T$c9dRQ-)+0Czo0t*cuw!|LVDd&AZvO=j<^K1 zu5MiCs-i)iUxp-#Zg0nO_R~+;Md+rv;a@Zj*<4?p2DtB0CrS-h1Fz#++rFN|csFG( zUMH@$ePPaQzA^W_$#D2`_@KSf=OK5SDYCXHyy-lh$d8&O!Fwzl$t{)^;V6Qv1y=O} z%p;-RmAU;T($H~U?3BB2BuYQ|t>U`8cpjYOh2hF;(z@2UeVER&1G$m=<#+66x3vIs$i{#}c%)F|WdAm+h#fI9Da~L&vyNRvZVBxe8 znDl$Ql}wmDI6Uku&3(K3@|*AY#l&@KHKVh}#N*6n(rvo3BfRyn{meta4F*oMD`%pPM2xAEfarzGzI&3@qi`EeO{C^zzL%|08Omer)_U{)V z5>^e6Q6Z>RaIen|&=?B%nMhFB_1QEDWd2{M7Pn0rwGFc8{4Z3)gDFxVGA#1ynf@25 zdE*87at8UU1~pc{&ng6lGzEn%1x5T9s>N^y$EgMwk?HrJpK#SdN{-SRKwm2riPNjMw-IL zmck|;!=~`UXE?*>RKpjX!K!ndeNH;W(`np+6_co7Yp5t~^NM@td! zbtyqz5z$bQ!Uqxe@3k>SR9Kr-D&$&QUFKxOPkcB(o^MQZkre4f}V4Prb-0o#;a(I!a-XawQs1 z$6L7k{IL%^9c58Rt(Km!C7QxiW9nGVfh7ACohmn=@aRGvA&vq42X{ zxU*o@v*28_qLm43&=HKLvQVG1(4Vth^58zHXXChLi-J;$LRyM~ zLOzCd6}cf6W|9`t`vam}i{Cr{Qd)}BR*Ey8i?c=wBh(AC)k_LoON!oIhb<*#DZJ{?rA;ZNEiI*OE2SOJrCs=CJ=|q|BcsesksG*$0$9{MQ&f>^RC#=W169?0;}Kvet;XTu zWYGYyYSe-$s(rWe_)ge)PHHibYK1j8#VqRZLhIg2>SW#O0;1~tYwLa@2?>@~%4^iC ztk(T%t><2?$BV01B52SLt(Qx!^){$CS*Me^|bzBIVv=$g$$n#&1z3rAaOUwA{2YQx=Hr(#>vMq5j|^Zlye ztae(nklMna;a&CH@v1UdL2E~pZf5{gM*vj&rA9|5Pv>vu_T_xQvm5G$8@#D^ z7qAKrX6%z46#T0O##L)8)Hj@60tAeq)yD3=|x)MaFXC<#0|L6eSz5ma2)|8Vc9# z6Db4m$RY^HBEZX{#-zC>c@O-N9T*=SQu)~v1vMh^4MATQ&Q!OPTy}7!Y7ljL_%Le7 zzIG@t?NjpDV2LH5#oRwmLc*A~+l*I68&Yn#0t8|83}M z2yk82oijCANini~GT~c2?kfoApEa=;1}Git-9jEqwe3$uo(dM62I)@2qfC=YNx{%} z!nR{OhV>AC8#|8H%|&3_io|q}`1(R5`eMY?iYp&{ z?D{facsD!YVj9Y1Sk;J6`dmo)N>@7eQ22uA+4%k%W-n-fBqNy13q20QyG5 z_@dZ40(cyu_iRI(9&pt@h42GYkEj(-2p0Qt6AE?fn;>lH)+V&)7GMGgv4x$r z_3?ZQWda7h0)Wi04WqpsY`cy3wuSEr5bfH=liU6{0YgN*^?`Vs3Uv#=YKx9xi{WkO zO9kY;CU6($ZHHKUi=1JbJpw=$u}kB*D=4=m%&^O>y-R|+gSxScZa5F+vB{zZSS4D& zDjQzxTU|?Ey$@ev3h&?dSU38H@l6-XQ2W4G?m*NNVBmRRxp81-4Y1fau$MasK|geu zIKWmo1Tq}DWB}Y44yEJ(UKj#L zKn3U^>w^FWK>GPH0WsK!;n>gn{dKUfHyBs}26lnnD-MAb$DDg}6&^FFaHAb%`}~@S zhNN94?Q4Ef86H$OiIbsf*}pN~hJ18vXG7|xC+&N3(XdP2KeJmCi> zqSU0}O55N*R8|(At+!=t>JeVjwa*It=u?_lIIY?=_8UE|Iwd7O=a0C!sJM9AI1OO9 z_!0@&mb)0Y22T@%_usA-_^f(CZz6#MSg1``(&y{}e6?p2}4fRG4a`TLs`Hr53&6{G&y99A{&uY{57vx4 z(vG}gC%M1UzV~Cg5750c&%Do$xYHoHPVP{g;q4#s=oT1TmKVUvDTgdFg`G}lVmP54 z1>4@Fza2+K9%Z6F{JFSsp1g8rybbO;vt)SM>j1>T{t?@EU_JkTh;5e1is|}} zlTEPh;r`Bl#P;IyO806;w6Fj69VP#d*wVIej3ssX0kCK_hpNbUnNhifl;94hQ-a>_ z`a}Z%TZ3pFM>31d>W=zv4T8mJ*o{i#f5g@fo5&-S^DsI&b&Qpq8=jJA>YdnH6ChlF z<x1B@=rrQ3|J8mHuxF!iVzW9oVXr zEQ#t;yor5su22&(eWBG(n>>^7wfp=J*peAG$G-#HvE*NqrdZ0v9kETd$Fq5aZl9dc z>fg5@Vyh`}S{oo|s~{(G#508lvW>~PDnc#%M!SI$6%{50pM|T#sjs!F8@%sp5Uxmp za~+AC5~o|ulxXrDZBI8xvnTs}oa8UYcGj+pB^{pQSNAt3o0FZLkP1-V{7+pNhFmy& zi@wDf_m6LfTSAX~@5J`gZ`17nJp3MA5Mc+FULYxmdMD^hLeT<Ang|8{>g1o0jSUKD=9A6efvu2!N3%{c3>S86nsbGrNrwA6?rBe1x=}64;%%d z&g!SEN6#9W)R8KTmk)N&ntC!q&s)etMk@^WXL8S5t0rVF+CQ~~GU{9n7+iE@z9C(9 zJs->1YW?k@xa^K%7`yD<`;1(zj?lS#+2^Spb~P{{HOBlKyEXS}&?Z9mdbrT#y;O;; z(cpSSw*&cRET&lYy8?YJ#m%_t`Pj|mpJn7*S+2_6n<)k2@Y@+xgz-9QvC=$ z;&)<8vhTP+B33?Oq}hq!v}8D2|7RK1g?_@aH>;d-6|RZ?;kU_Tden*qu4B=a77;1<;OkH_sGs`Ig}kP`#v-6#o$W)KV}+tYfYNyXEFkg_N0VSpdQJF(sDc0OL_ zjd%u^$-BFr%oU-&oYvL8xSmZ8MZBE1k+`{Cj2xl9UiRfayIoZRB44k^;9cEslBm7t zklaKf$uQoYl32na$Z#fO*DzU9S!yFDZ};HR7J%5x)RB`pMD(v11NXbh&=VVa_wN)w z3-8T*OBm`Cs@e)x2+R3YDA`Y|9vy-`l!Mba)X(606{-tsz)TAz_F(okSOiubBO2Ie zm{tARDae2@l{%Pi6W?6iorM6i9Z9 z<`>S`o&=$t*|>emP1=!cAzQV}CoAh%gZ0yO4mAupkFa0qk7I@W55toZH8+&!?ouk0MS*#xA{ZDBSxQZ1R=SC?p(vg4MFdYmh*KmQhao2B$@FFjCH=jrg>sXTW+W=cTP zkQLl6-VaYeRFB$_NK0(5SG95bPPh5kq1%yKegV82MfiHT*alI)@Hk8T2t(l9$xO}U zi@ml9)w3Wx4qSHoykn^rD^doyyAM$U;kcZYCtW>-% zdeBvXk_%KJ=y+%)Bl(1rdzJKp<)nOn-rf@fbw>5mXAlh)cbxPZpia4Y*8(zyb<#-{mn z3@7=QwFB{orbQGR*U?qLoi(sy)8oQJXLU0xgn~5G19Iw1)iYiGE1XD^Vj~sAzP9G( z9A!gycXsIdl@ zTcn?)4m>XoZu~J#Oh46Q^jV&LJDYLZg0+f#T3z%!YYt6?cM@pbSbitAHy%q;IxSm2 zeQX!__>6u6dEodN{tPTOe}GbH+x;qVHT9eC+CI>IpJwyV*oXEHDHpE?-!EL0{IYJ{ z;P}8kS*|G>Z?_eP?khg%ZZnH-H&wXp=O&fvT@V4I%dQ8<*>D%xD`S5-p+^>T>yWBH~BMVQV@9j@2;u$LQeCduoGgx5R&@%-Y6oHI_ARv<@e~Te|yGMsd6&P=SY@bFyqglVA8$YC< zDm$WbxKmcnxF7tFlmbnIJS08@F9n4j1r>lkMBzEbJO(Aa??Z6H@J0ovN}8t(1!o-v z3!;alDSRwr56Q!U26AFwq!b5Yzk|84C^`w%O5h=<_Mn|vM`4aUy=llB!w>=g|8NcXE<5d9$H8) z;{Ou*Ss^J}l_XyCe)|GY6=`zoR{eEmVlnC*IaCz+BpJDW!pG9=2pF{;@#Fe=*bswu*JkIN{_GNQ^B6+@vwM2!J(fE-7Gc-|+bmju%i{BKu+V28klog}#6<3wxD zz^0GMA%FbprlsMQ;<>-Xd~vZgXik384Hd$RKZ*}Ny4NmtNkK~R#wmzvh10F3(Jkdl zMKeyt0HtCz7?dxkq70;VNcFWA?$R*?W$=wz%%>*aY>>y(XKJ!M(_!_|j zY-T!n=$70ZzeJNhxR4(ckzyyaVr!bBY{U{r zsggq4lFSkZhm;c0!;&&Y$7*#4;lknuaIpx`i;1eVKE?hmxG3c~Ka{qB1JRocvA72x zEq2A(R0TM{ls5$~`(5n3wxp)t2%K3dTURZwe8??>k5~>VUtIz2Q&%jw<~1FbdnT55 zN-GL>$M=rVhMSiKPmx~Y6?@+)($Tn0w771Xl&`{9z2R3?a#X>&MQ*xQ!K+mPUOZu1 zqtIKcUY{c|Mj>u3ixt5sWmkybZ}3ASbSvR~VzkVuQ|~ID5pvEYst7`AZdE)fUovhA zVHq_tnNOg4c+&g9)pz(Q z_%EmwGRVWGA@pV`8l~PiL-jf*_22Otm;e@98V&G6zNQ2|CaVqRCk+M!jclBaM2`(l z7JeorR4y-du(Q?YkJU;ibt5H}N~#(GZW{7Qpx9-Q?NF18RC35_6KFIbRt6M53QBBk zN~UWLa%)OBZgyV&7){sW&ec+&(Gnq*j58Rm{8HgI+6cAVD3XUyszFs}TF*8RiMkqz)N-iJNY1D}HRDhOVqyrKv#@tqa9y zq$~Efs1SAyzvC>=ql(z2?GQE#+kGk95AFDb(0OLjfhyfuAk}e95P?q9SqtjCvIswP z#y$?JTt4Cc`O^4{0Kd%l6bOx!fy^+{e$= zCnVb^Z`St%A->@tu5q|e2BH5~a39+5ihR|0HK{HRpI)Z1YIxpO?-LAI2?`$f;sCS6 zkYLC_3|EPndx=$BVwl;04ey}I>43#+lih1`u|{J4$0S$6LBHUn9-3sYvB7YJq3B{& zJI=uX&B0WSpZaRCh%+0@b1x>5GI(SgM-qa#A&W6SaRvh2Iz?CG(I zi?A9B^BiZK$#dTE%e9E9ufkhvpH)`*MM^r73oDMyT{ZB_*YP0J zZ8FnO8u@A^o%rF?>M7GGW|Li()8mrkkGNf3+*7cB`_~8tws~zWT6}D2OT6)C8BwfR z=?mMQ3)#zO={RP2&kFdpiri`EJf-J^!VCQivYNtXzA8<9bDTC8E+nTV ztkC{hu{~!IJb8NJbdI#-w?4IkShXfy9=u*2KE8yUUJ)6-6di8{_HEqsdcu1ck_ra`5-4qT zT6H@p8wVbBN9CT9Zne$nwP)?M7wffG?dzxTxs>g57wYSXBIq0nA%~{WHLq*TnCk?r z>xk9V&*24&=?(T8jVKcZwla+rk6TpEjhNcopE`U$6Z_$uZ{b#KlT2(w12L!joNL)IEW>AtAXv5$ioM zxxuv6p&t{lf#NMv6MKpgdl43POsFdwvgih{Y(!EBJ7WGQ(fhsVTi-VzL~Pbm93AMJ zsg>Syt>XlTZ3JzDk8MLdha(z?19XRTZigNdhpznXKB(>f=WRu%N4}^QBjJmz%){1w zN|s_UQo7oc$pEB&D3s`rR-Ol@p2LPI!#;Ff_eui<9@7OCJr^3wAIv&SCpvM)z;l3; zH=fQ4?aq4QnMTx}Emv?|N9RJ*k;`MgIx&ixAPk+gQJ>3)_y_^A}-uv*f|(dRNG&9{MK{kYw)$|%PEBBGF0di)$kIUtOjXf0JxTz zO*h03Neb4q50~8+h#cbWOajRcz8f2}m2ag;hTKkvq~(WXHwOg;hC)u8e##{!6lZ-w z;|E-bhp>K#usv-&+I%g~J)*w(o_QeOet_U}qrNIrdPEJX&jc z@Fq6nb@AYX_V^$>3haC=4BJoD#zLR7^oxUme>k|JTtQ|206lpR6uN;oyn!Nn(BYq0 z3OmV{f4(l8+-n;v_L?lqoUD+4tgalZxmYiROjg;vlvhGtDBd1frv<4PWu3eCmj&Tv zNdZZlsFVK3X_a^x@`70qv>bW#0>%rl``?Am7uuBUwF~2IwaJ&TvB$N)OMjh7+y8Em zyn#DcYS7+J1#||qE}-!58}_af_3!&GKLkguML`2Bf&Uw^^%o@sz(qtN3k1go3w%LF z#QCNiM;;l8>u;$jCF>aX--vCAqq3~DTKWGSv2E}8--vCClMP5$*Qo!$5!-*jHb6IU zXrWm&GXi@x$Z358M~f4WR-x$d_$2NOe9C%$b9Z@5pZ53=d-d|Be=Bo4nZfS|g@DLA z6sgzcv!gA@8z+_{5`YCkQSL*h(CZ5eV0SWH^v#tB`6>}oYtWqvjkB%4LH2+^$Ry4{ z)Xe!OFnm-kKypth-d_xnR1|x!Y#25XGoK+?wj`=HU&f(F8@d^KZlOrAz}l$W<*rXE z*>v{58QWE}!5DhCf5f)cIa}|09(`p_sl}kyk6ze89wlkyLh`aCt)1RCV#b8iBzQyL zxNV~dOBegY5fe7+fmbyLvx#gGe6And^+od)@XQhGSPdpCwSTJ}5oT?eQnI|6)$oNB z$ciE)30t;epm|EV|GKC@V`(pD&7pje^5=EC)poQNEt&`!4;EK8!P5!jcl)w=`QM1G z^_qpl$;l>UvJ+C-9&o!)V7mz`o&}9FM5%6&<5->LN0xt;C63-PkY$NIVyxkhWnZ)s zz$f*n2O@J?$_mEIQQZmpRFVV=!ijmz31u=L5(%dpSRxN41~27?b50EH28$F7#{P2mF{0iwhy&GO>p)M<

@ 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%0D~zhVo-ELwPA5Ch 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 literal 0 HcmV?d00001 From c1569bb989c99d82b7fe552647181663ae01df16 Mon Sep 17 00:00:00 2001 From: Mihovil Ilakovac Date: Thu, 9 Jan 2025 12:45:35 +0100 Subject: [PATCH 07/22] Fixes deployment docs links --- web/docs/advanced/accessing-app-config.md | 4 ++-- .../deployment/deployment-methods/DeploymentOptionsGrid.tsx | 2 +- web/docs/general/cli.md | 4 ++-- web/docs/migration-guides/migrate-from-0-11-to-0-12.md | 4 ++-- web/docusaurus.config.js | 6 +++++- web/src/components/Features.js | 2 +- 6 files changed, 13 insertions(+), 9 deletions(-) diff --git a/web/docs/advanced/accessing-app-config.md b/web/docs/advanced/accessing-app-config.md index 3dcbf08517..cd896c43bd 100644 --- a/web/docs/advanced/accessing-app-config.md +++ b/web/docs/advanced/accessing-app-config.md @@ -7,7 +7,7 @@ Whenever you start a Wasp app, you are starting two processes. During development, this is a dev server with hot reloading. In production, it's a simple process that serves pre-built static files with environment variables - embedded during the build (details depend on [how you deploy it](/advanced/deployment/overview.md)). + embedded during the build (details depend on [how you deploy it](../deployment/intro.md)). - **The server process** - An Express server that implements your app's backend. @@ -18,7 +18,7 @@ Whenever you start a Wasp app, you are starting two processes. Check [the introduction](/introduction/introduction.md) for a more in-depth explanation of Wasp's runtime architecture. -You can configure both processes through environment variables. See [the deployment instructions](/advanced/deployment/manually.md#environment-variables) for a full list of supported variables. +You can configure both processes through environment variables. See [the deployment instructions](../project/env-vars.md) for a full list of supported variables. Wasp gives you runtime access to the processes' configurations through **configuration objects**. diff --git a/web/docs/deployment/deployment-methods/DeploymentOptionsGrid.tsx b/web/docs/deployment/deployment-methods/DeploymentOptionsGrid.tsx index 9d3125f454..2c496058b7 100644 --- a/web/docs/deployment/deployment-methods/DeploymentOptionsGrid.tsx +++ b/web/docs/deployment/deployment-methods/DeploymentOptionsGrid.tsx @@ -6,7 +6,7 @@ export function DeploymentOptionsGrid() { { title: 'Wasp CLI', description: 'One command deployment & redeployment', - linkToDocs: '/docs/advanced/deployment/cli', + linkToDocs: '/docs/deployment/deployment-methods/cli', }, { title: 'Platform as a Service (PaaS)', diff --git a/web/docs/general/cli.md b/web/docs/general/cli.md index 1cea06e716..555e8a8019 100644 --- a/web/docs/general/cli.md +++ b/web/docs/general/cli.md @@ -114,13 +114,13 @@ Newsletter: https://wasp-lang.dev/#signup โœ… --- Deleted the node_modules/ directory. --------------------------------------- ``` - - `wasp build` generates the complete web app code, which is ready for [deployment](../advanced/deployment/overview). Use this command when you're deploying or ejecting. The generated code is stored in the `.wasp/build` folder. + - `wasp build` generates the complete web app code, which is ready for [deployment](../deployment/intro.md). Use this command when you're deploying or ejecting. The generated code is stored in the `.wasp/build` folder. - `wasp deploy` makes it easy to get your app hosted on the web. Currently, Wasp offers support for [Fly.io](https://fly.io). If you prefer a different hosting provider, feel free to let us know on Discord or submit a PR by updating [this TypeScript app](https://github.com/wasp-lang/wasp/tree/main/waspc/packages/deploy). - Read more about automatic deployment [here](../advanced/deployment/cli). + Read more about automatic deployment [here](../deployment/deployment-methods/cli.md). - `wasp telemetry` displays the status of [telemetry](https://wasp-lang.dev/docs/telemetry). diff --git a/web/docs/migration-guides/migrate-from-0-11-to-0-12.md b/web/docs/migration-guides/migrate-from-0-11-to-0-12.md index f3ca373781..19eea35724 100644 --- a/web/docs/migration-guides/migrate-from-0-11-to-0-12.md +++ b/web/docs/migration-guides/migrate-from-0-11-to-0-12.md @@ -664,7 +664,7 @@ You should see the new `Auth`, `AuthIdentity` and `Session` tables in your datab - **First step: deploy the new code** (client and server), either via `wasp deploy` (i.e. `wasp deploy fly deploy`) or manually. - Check our [Deployment docs](advanced/deployment/overview.md) for more details. + Check our [Deployment docs](../deployment/intro.md) for more details. - **Second step: run the data migration functions** on the production database. @@ -693,7 +693,7 @@ Your app should be working correctly and using new auth, but to finish the migra After doing the steps above successfully locally and making sure everything is working, it is time to push these changes to the deployed app again. - _Deploy the app again_, either via `wasp deploy` or manually. Check our [Deployment docs](../advanced/deployment/overview.md) for more details. + _Deploy the app again_, either via `wasp deploy` or manually. Check our [Deployment docs](../deployment/intro.md) for more details. The database migrations will automatically run on successful deployment of the server and delete the now redundant auth-related `User` columns from the database. diff --git a/web/docusaurus.config.js b/web/docusaurus.config.js index 4a559436da..945e38810e 100644 --- a/web/docusaurus.config.js +++ b/web/docusaurus.config.js @@ -241,7 +241,11 @@ module.exports = { }, { from: '/docs/deploying', - to: '/docs/advanced/deployment/overview', + to: '/docs/deployment/intro', + }, + { + from: '/docs/advanced/deployment/overview', + to: '/docs/deployment/intro', }, { from: '/docs/guides/username-password', diff --git a/web/src/components/Features.js b/web/src/components/Features.js index 56da3c3572..273bb776d5 100644 --- a/web/src/components/Features.js +++ b/web/src/components/Features.js @@ -104,7 +104,7 @@ const Features = () => { Date: Thu, 9 Jan 2025 12:52:59 +0100 Subject: [PATCH 08/22] Update client env vars import in docs --- web/docs/project/env-vars.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/docs/project/env-vars.md b/web/docs/project/env-vars.md index 89ec244025..9a9d5b5057 100644 --- a/web/docs/project/env-vars.md +++ b/web/docs/project/env-vars.md @@ -2,7 +2,7 @@ title: Env Variables --- -import ClientEnvVarsNote from './\_ClientEnvVarsNote.md' +import ClientEnvVarsNote from './\_clientEnvVarsNote.md' import { EnvVarsTable, EnvVar } from './EnvVarsTable' **Environment variables** are used to configure projects based on the context in which they run. This allows them to exhibit different behaviors in different environments, such as development, staging, or production. From 1da01dcd6327fa7b87ce481bec31dd40bf7350d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20=C5=A0o=C5=A1i=C4=87?= Date: Fri, 10 Jan 2025 12:23:10 +0100 Subject: [PATCH 09/22] Update config.yml --- .github/ISSUE_TEMPLATE/config.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index b42c0b6a65..19df0222d7 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,7 +1,4 @@ contact_links: - - name: Feature Request / Feedback @ Github Discussions - url: https://github.com/wasp-lang/wasp/discussions/categories/feature-suggestions-feedback - about: Please open new or comment on existing feature suggestions / requests here! - name: Join live chat @ Wasp Discord server url: https://discord.com/invite/rzdnErX about: Join our Discord server to chat live with other Waspeteers (including Wasp Team)! From dcc2119cf721f3fcd0d56c8186fae7fa9ad71372 Mon Sep 17 00:00:00 2001 From: Mihovil Ilakovac Date: Sun, 12 Jan 2025 11:27:32 +0100 Subject: [PATCH 10/22] Deployment docs extras (#2443) --- web/docs/deployment/ci-cd.md | 2 +- web/docs/deployment/deployment-methods/cli.md | 2 +- web/docs/deployment/env-vars.md | 2 +- web/docs/deployment/extras.md | 81 ++++++++++++++++++- web/docs/project/env-vars.md | 43 +++++----- 5 files changed, 103 insertions(+), 27 deletions(-) diff --git a/web/docs/deployment/ci-cd.md b/web/docs/deployment/ci-cd.md index d2a2b6190e..31b16717cf 100644 --- a/web/docs/deployment/ci-cd.md +++ b/web/docs/deployment/ci-cd.md @@ -138,4 +138,4 @@ Wasp's client app is a single page application (SPA) which you build into static -Check out our instructions for deploying the client app to [Netlify](#) or [Cloudflare](#) where you can check out the example deployment using Github Actions. +Check out our instructions for deploying the client app to [Netlify](./deployment-methods/paas.md#netlify) or [Cloudflare](./deployment-methods/paas.md#cloudflare) where you can check out the example deployment using Github Actions. diff --git a/web/docs/deployment/deployment-methods/cli.md b/web/docs/deployment/deployment-methods/cli.md index 79a20848d2..c689aa8646 100644 --- a/web/docs/deployment/deployment-methods/cli.md +++ b/web/docs/deployment/deployment-methods/cli.md @@ -110,7 +110,7 @@ You can validate your ownership of mycoolapp.com by: wasp deploy fly cmd --context server secrets set WASP_WEB_CLIENT_URL=https://mycoolapp.com ``` - + We need to do this to keep our CORS configuration up to date. diff --git a/web/docs/deployment/env-vars.md b/web/docs/deployment/env-vars.md index 77ad2e94cb..465d2c44de 100644 --- a/web/docs/deployment/env-vars.md +++ b/web/docs/deployment/env-vars.md @@ -37,7 +37,7 @@ You can provide production env vars to your server code in production by definin ::::caution Set the required env vars -Make sure to go through [all the required server env vars](../project/env-vars.md#general-configuration-1) like `DATABASE_URL`, `WASP_WEB_CLIENT_URL` etc. and set them up in your production environment. +Make sure to go through [all the required server env vars](../project/env-vars.md#server-general-configuration) like `DATABASE_URL`, `WASP_WEB_CLIENT_URL` etc. and set them up in your production environment. **If you are using the [Wasp CLI](./deployment-methods/cli.md)** deployment method, Wasp will set the general configuration env vars for you, but you will need to set the rest of the env vars yourself (like the ones for OAuth auth methods or any other custom env vars you might have defined). :::: diff --git a/web/docs/deployment/extras.md b/web/docs/deployment/extras.md index 23270fae1a..16be829dc7 100644 --- a/web/docs/deployment/extras.md +++ b/web/docs/deployment/extras.md @@ -2,8 +2,83 @@ title: Extras --- -TODO: Is Wasp production ready? What are the limitations? +In this section, we will cover some additional topics that are important for deploying Wasp apps in production. -TODO: DDos and CDN recommendations +### Custom domain setup -TODO: Custom domain setup \ No newline at end of file +If you want to set up a custom domain for your Wasp app, you can do it for both the client and the server. + +The important part is setting up the custom domain for the client - that's what your users visit from their browsers. Setting up a custom domain for the server is optional, but it can be useful if you'd like to hide some server details (for example, the IP address or auto-generated domain name) from the users. + +#### How to do it? + +It's usually a two-step process, and it's the same for both the client and the server: + +1. Set up the **DNS records** for the domain. + + This will depend on your hosting provider. You can usually do this by adding an `A` record in your DNS settings that points to the app's IPv4 address. You often set the `AAAA` record for IPv6 address as well. Some hosting providers ask you to set the `CNAME` record instead of the `A` and `AAAA` records. + +:::note Using Wasp CLI? + +Check out how to do it for [Fly with Wasp CLI](./deployment-methods/cli.md#using-a-custom-domain-for-your-app) if you are using Wasp CLI to deploy your app. +::: + +2. Set up the **environment variables** for the app. + + You need to set the environment variables so Wasp configures the app correctly (for example, for CORS to work correctly). + + #### Client domain env vars + + When [building the client](./env-vars.md#client-env-vars), set `REACT_APP_API_URL` to point to your server domain: + + ```bash + REACT_APP_API_URL=https://api.myapp.com + ``` + + + + Learn more about client configuration in the [env vars section](../project/env-vars.md#client-general-configuration). + + + #### Server domain env vars + + For the server, you need to [configure two variables](./env-vars.md#server-env-vars): + + - `WASP_WEB_CLIENT_URL`: Your client app's domain + - `WASP_SERVER_URL`: Your server domain + +
+ + ```bash + WASP_WEB_CLIENT_URL=https://myapp.com + WASP_SERVER_URL=https://server.myapp.com + ``` + + + + Learn more about server env variables in the [env vars section](../project/env-vars.md#server-general-configuration). + + +### DDoS protection and CDN recommendations + +When deploying your Wasp app, you might want to consider using a Content Delivery Network (CDN) and DDoS protection service to improve the performance and security of your app: + +1. **Content Delivery Network (CDN)** is a network of servers distributed worldwide that caches static assets like images, CSS, and JavaScript files. + + Using a CDN in front of your **client** can help with caching static assets and serving them faster to users around the world. When a user requests a file, the CDN serves it from the server closest to the user, improving load times. + +2. **Distributed Denial of Service (DDoS)** attacks are a common threat to web applications. + + Attackers send a large amount of traffic to your server, overwhelming it and making it unavailable to legitimate users. You can use a DDoS protection service for both your **client and server** to protect your app from these attacks. + +We recommend using [Cloudflare](https://www.cloudflare.com/) for both CDN and DDoS protection. It's easy to set up and provides a free tier that should be enough for most small to medium-sized apps. + +There are other CDN providers like [Fastly](https://www.fastly.com/), [Bunny](https://bunnycdn.com/) and [Amazon Cloudfront](https://aws.amazon.com/cloudfront/) that you can consider as well. + +### Are Wasp apps production ready? + +As we mentioned in the [introduction](./intro.md) section, what we call **Wasp apps** are three separate pieces: the client, the server, and the database. + +For the server, we are using Node.js and the battle-tested Express.js framework. For the database, we are using PostgreSQL, which is a powerful and reliable database system. For the client, we are using React and Vite, which are both widely used and well-maintained. + +Each of these pieces is production-ready on its own, and Wasp just makes it easy to connect them together. Keep in mind that Wasp is still considered beta software, so there might be some rough edges here and there. diff --git a/web/docs/project/env-vars.md b/web/docs/project/env-vars.md index 9a9d5b5057..a0ac725f74 100644 --- a/web/docs/project/env-vars.md +++ b/web/docs/project/env-vars.md @@ -48,12 +48,12 @@ Read more about the `env` object in the [API reference](#client-env-vars-1). Here are the client env vars that Wasp defines: -#### General Configuration +#### General Configuration {#client-general-configuration} These are some general env variables used for various Wasp features: ## Server Env Vars @@ -86,16 +86,16 @@ Read more about the `env` object in the [API reference](#server-env-vars-1). ### Wasp Server Env Vars -#### General Configuration +#### General Configuration {#server-general-configuration} These are some general env variables used for various Wasp features: Needed to generate secure tokens.
Generate a random string at least 32 characters long. }, - { name: "PORT", type: "Integer", isRequired: false, defaultValue: "3001", note: "This is where the server listens for requests." } +{ name: "DATABASE_URL", type: "String", isRequired: true, note: "The URL of the PostgreSQL database you want your app to use." }, +{ name: "WASP_WEB_CLIENT_URL", type: "URL", isRequired: true, note: "Server uses this value as your client URL in various features e.g. linking to your app in e-mails." }, +{ name: "WASP_SERVER_URL", type: "URL", isRequired: true, note: "Server uses this value as your server URL in various features e.g. to redirect users when logging in with OAuth providers like Google or GitHub." }, +{ name: "JWT_SECRET", type: "String", isRequired: true, note: Needed to generate secure tokens. Generate a random string at least 32 characters long. }, +{ name: "PORT", type: "Integer", isRequired: false, defaultValue: "3001", note: "This is where the server listens for requests." } ]} /> #### SMTP Email Sender @@ -103,10 +103,10 @@ These are some general env variables used for various Wasp features: If you are using `SMTP` as your email sender, you need to provide the following environment variables: #### SendGrid Email Sender @@ -114,7 +114,7 @@ If you are using `SMTP` as your email sender, you need to provide the following If you are using `SendGrid` as your email sender, you need to provide the following environment variables: #### Mailgun Email Sender @@ -122,9 +122,9 @@ If you are using `SendGrid` as your email sender, you need to provide the follow If you are using `Mailgun` as your email sender, you need to provide the following environment variables: Useful if you want to use the EU API endpoint (https://api.eu.mailgun.net). } +{ name: "MAILGUN_API_KEY", type: "String", isRequired: true, note: "The Mailgun API key." }, +{ name: "MAILGUN_DOMAIN", type: "String", isRequired: true, note: "The Mailgun domain." }, +{ name: "MAILGUN_API_URL", type: "URL", isRequired: false, note: Useful if you want to use the EU API endpoint (https://api.eu.mailgun.net). } ]} /> #### OAuth Providers @@ -132,8 +132,8 @@ If you are using `Mailgun` as your email sender, you need to provide the followi If you are using OAuth, you need to provide the following environment variables: _CLIENT_ID", type: "String", isRequired: true, note: "The client ID provided by the OAuth provider." }, - { name: "_CLIENT_SECRET", type: "String", isRequired: true, note: "The client secret provided by the OAuth provider." } +{ name: "_CLIENT_ID", type: "String", isRequired: true, note: "The client ID provided by the OAuth provider." }, +{ name: "_CLIENT_SECRET", type: "String", isRequired: true, note: "The client secret provided by the OAuth provider." } ]} /> @@ -144,13 +144,13 @@ If you are using OAuth, you need to provide the following environment variables: If you are using [Keycloak](../auth/social-auth/keycloak.md), you'll need to provide one extra environment variable: #### Jobs It's parsed as JSON. Enables you to provide custom config for PgBoss. } +{ name: "PG_BOSS_NEW_OPTIONS", type: "String", isRequired: false, note: It's parsed as JSON. Enables you to provide custom config for PgBoss. } ]} /> #### Development @@ -158,8 +158,9 @@ If you are using [Keycloak](../auth/social-auth/keycloak.md), you'll need to pro We provide some helper env variables in development: + ## Defining Env Vars in Development From 52653be75b5120c7dd0aa7c6c30e7938479c2c0c Mon Sep 17 00:00:00 2001 From: Mihovil Ilakovac Date: Mon, 13 Jan 2025 17:06:35 +0100 Subject: [PATCH 11/22] Add `react-dom` and `react-router-dom` deps validation (#2445) --- .../hackathon-submissions/package-lock.json | 16 +++--- examples/hackathon-submissions/package.json | 12 +++-- examples/streaming/package-lock.json | 16 +++--- examples/streaming/package.json | 12 +++-- examples/thoughts/package-lock.json | 34 ++++++------- examples/thoughts/package.json | 2 + examples/todo-typescript/package-lock.json | 16 +++--- examples/todo-typescript/package.json | 12 +++-- examples/tutorials/TodoApp/package-lock.json | 16 +++--- examples/tutorials/TodoApp/package.json | 12 +++-- .../tutorials/TodoAppTs/package-lock.json | 16 +++--- examples/tutorials/TodoAppTs/package.json | 12 +++-- examples/waspello/package-lock.json | 42 +++++++--------- examples/waspello/package.json | 16 +++--- examples/waspleau/package-lock.json | 16 +++--- examples/waspleau/package.json | 12 +++-- .../package-lock.json | 8 +-- .../websockets-realtime-voting/package.json | 12 +++-- waspc/data/Cli/templates/basic/package.json | 4 +- .../waspBuild/.wasp/build/.waspchecksums | 2 +- .../.wasp/build/installedNpmDepsLog.json | 2 +- .../waspBuild/.wasp/build/package.json | 2 + .../.wasp/build/web-app/package.json | 4 +- .../waspBuild-golden/waspBuild/package.json | 2 + .../waspCompile/.wasp/out/.waspchecksums | 2 +- .../.wasp/out/installedNpmDepsLog.json | 2 +- .../.wasp/out/web-app/package.json | 4 +- .../waspCompile/package.json | 2 + .../waspComplexTest/.wasp/out/.waspchecksums | 2 +- .../.wasp/out/installedNpmDepsLog.json | 2 +- .../.wasp/out/web-app/package.json | 4 +- .../waspComplexTest/package.json | 2 + .../waspJob/.wasp/out/.waspchecksums | 2 +- .../.wasp/out/installedNpmDepsLog.json | 2 +- .../waspJob/.wasp/out/web-app/package.json | 4 +- .../waspJob-golden/waspJob/package.json | 2 + .../waspMigrate/.wasp/out/.waspchecksums | 2 +- .../.wasp/out/installedNpmDepsLog.json | 2 +- .../.wasp/out/web-app/package.json | 4 +- .../waspMigrate/package.json | 2 + .../waspNew-golden/waspNew/package.json | 2 + waspc/examples/crud-testing/package-lock.json | 50 +++++++++---------- waspc/examples/crud-testing/package.json | 12 +++-- .../pg-vector-example/package-lock.json | 4 +- waspc/examples/pg-vector-example/package.json | 20 ++++---- .../todo-typescript/package-lock.json | 4 +- waspc/examples/todo-typescript/package.json | 2 + waspc/examples/todoApp/package-lock.json | 4 +- waspc/examples/todoApp/package.json | 2 + .../examples/todoApp/package-lock.json | 28 +++++------ .../examples/todoApp/package.json | 8 +-- .../Generator/ExternalConfig/PackageJson.hs | 6 ++- 52 files changed, 252 insertions(+), 228 deletions(-) diff --git a/examples/hackathon-submissions/package-lock.json b/examples/hackathon-submissions/package-lock.json index 019e7de073..5f44e3422f 100644 --- a/examples/hackathon-submissions/package-lock.json +++ b/examples/hackathon-submissions/package-lock.json @@ -7,7 +7,9 @@ "name": "hackathonSubmissions", "dependencies": { "react": "^18.2.0", + "react-dom": "^18.2.0", "react-feather": "2.0.10", + "react-router-dom": "^6.26.2", "wasp": "file:.wasp/out/sdk/wasp" }, "devDependencies": { @@ -5108,17 +5110,15 @@ } }, "node_modules/react-dom": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", - "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", - "license": "MIT", - "peer": true, + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", + "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", "dependencies": { "loose-envify": "^1.1.0", - "scheduler": "^0.23.2" + "scheduler": "^0.23.0" }, "peerDependencies": { - "react": "^18.3.1" + "react": "^18.2.0" } }, "node_modules/react-feather": { @@ -5174,7 +5174,6 @@ "version": "6.26.2", "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.26.2.tgz", "integrity": "sha512-z7YkaEW0Dy35T3/QKPYB1LjMK2R1fxnHO8kWpUMTBdfVzZrWOiY9a7CtN8HqdWtDUWd5FY6Dl8HFsqVwH4uOtQ==", - "license": "MIT", "dependencies": { "@remix-run/router": "1.19.2", "react-router": "6.26.2" @@ -5421,7 +5420,6 @@ "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", "license": "MIT", - "peer": true, "dependencies": { "loose-envify": "^1.1.0" } diff --git a/examples/hackathon-submissions/package.json b/examples/hackathon-submissions/package.json index d625607a59..069f9da2d1 100644 --- a/examples/hackathon-submissions/package.json +++ b/examples/hackathon-submissions/package.json @@ -2,14 +2,16 @@ "name": "hackathonSubmissions", "type": "module", "dependencies": { + "react": "^18.2.0", + "react-dom": "^18.2.0", "react-feather": "2.0.10", - "wasp": "file:.wasp/out/sdk/wasp", - "react": "^18.2.0" + "react-router-dom": "^6.26.2", + "wasp": "file:.wasp/out/sdk/wasp" }, "devDependencies": { - "typescript": "^5.1.0", - "vite": "^4.3.9", "@types/react": "^18.0.37", - "prisma": "5.19.1" + "prisma": "5.19.1", + "typescript": "^5.1.0", + "vite": "^4.3.9" } } diff --git a/examples/streaming/package-lock.json b/examples/streaming/package-lock.json index 7962a2e6a4..bdd6d6c0d6 100644 --- a/examples/streaming/package-lock.json +++ b/examples/streaming/package-lock.json @@ -7,6 +7,8 @@ "name": "streaming", "dependencies": { "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-router-dom": "^6.26.2", "wasp": "file:.wasp/out/sdk/wasp" }, "devDependencies": { @@ -3179,15 +3181,15 @@ } }, "node_modules/react-dom": { - "version": "18.3.1", - "license": "MIT", - "peer": true, + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", + "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", "dependencies": { "loose-envify": "^1.1.0", - "scheduler": "^0.23.2" + "scheduler": "^0.23.0" }, "peerDependencies": { - "react": "^18.3.1" + "react": "^18.2.0" } }, "node_modules/react-hook-form": { @@ -3223,7 +3225,8 @@ }, "node_modules/react-router-dom": { "version": "6.26.2", - "license": "MIT", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.26.2.tgz", + "integrity": "sha512-z7YkaEW0Dy35T3/QKPYB1LjMK2R1fxnHO8kWpUMTBdfVzZrWOiY9a7CtN8HqdWtDUWd5FY6Dl8HFsqVwH4uOtQ==", "dependencies": { "@remix-run/router": "1.19.2", "react-router": "6.26.2" @@ -3408,7 +3411,6 @@ "node_modules/scheduler": { "version": "0.23.2", "license": "MIT", - "peer": true, "dependencies": { "loose-envify": "^1.1.0" } diff --git a/examples/streaming/package.json b/examples/streaming/package.json index c4808995b5..87520ddae1 100644 --- a/examples/streaming/package.json +++ b/examples/streaming/package.json @@ -2,13 +2,15 @@ "name": "streaming", "type": "module", "dependencies": { - "wasp": "file:.wasp/out/sdk/wasp", - "react": "^18.2.0" + "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-router-dom": "^6.26.2", + "wasp": "file:.wasp/out/sdk/wasp" }, "devDependencies": { - "typescript": "^5.1.0", - "vite": "^4.3.9", "@types/react": "^18.0.37", - "prisma": "5.19.1" + "prisma": "5.19.1", + "typescript": "^5.1.0", + "vite": "^4.3.9" } } diff --git a/examples/thoughts/package-lock.json b/examples/thoughts/package-lock.json index b3b9019aa2..b454f5042f 100644 --- a/examples/thoughts/package-lock.json +++ b/examples/thoughts/package-lock.json @@ -8,7 +8,9 @@ "dependencies": { "color-hash": "2.0.1", "react": "^18.2.0", + "react-dom": "^18.2.0", "react-markdown": "6.0.1", + "react-router-dom": "^6.26.2", "wasp": "file:.wasp/out/sdk/wasp" }, "devDependencies": { @@ -70,7 +72,6 @@ "axios": "^1.4.0", "express": "~4.21.0", "jsdom": "^21.1.1", - "lodash.merge": "^4.6.2", "lucia": "^3.0.1", "mitt": "3.0.0", "msw": "^1.1.0", @@ -80,7 +81,8 @@ "react-hook-form": "^7.45.4", "react-router-dom": "^6.26.2", "superjson": "^2.2.1", - "vitest": "^1.2.1" + "vitest": "^1.2.1", + "zod": "^3.23.8" }, "devDependencies": { "@tsconfig/node18": "latest", @@ -4371,12 +4373,6 @@ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", "license": "MIT" }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "license": "MIT" - }, "node_modules/log-symbols": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", @@ -5673,17 +5669,15 @@ } }, "node_modules/react-dom": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", - "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", - "license": "MIT", - "peer": true, + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", + "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", "dependencies": { "loose-envify": "^1.1.0", - "scheduler": "^0.23.2" + "scheduler": "^0.23.0" }, "peerDependencies": { - "react": "^18.3.1" + "react": "^18.2.0" } }, "node_modules/react-hook-form": { @@ -5755,7 +5749,6 @@ "version": "6.26.2", "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.26.2.tgz", "integrity": "sha512-z7YkaEW0Dy35T3/QKPYB1LjMK2R1fxnHO8kWpUMTBdfVzZrWOiY9a7CtN8HqdWtDUWd5FY6Dl8HFsqVwH4uOtQ==", - "license": "MIT", "dependencies": { "@remix-run/router": "1.19.2", "react-router": "6.26.2" @@ -6002,7 +5995,6 @@ "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", "license": "MIT", - "peer": true, "dependencies": { "loose-envify": "^1.1.0" } @@ -8139,6 +8131,14 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } + }, + "node_modules/zod": { + "version": "3.24.1", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.24.1.tgz", + "integrity": "sha512-muH7gBL9sI1nciMZV67X5fTKKBLtwpZ5VBp1vsOQzj1MhrBZ4wlVCm3gedKZWLp0Oyel8sIGfeiz54Su+OVT+A==", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } } } } diff --git a/examples/thoughts/package.json b/examples/thoughts/package.json index 5aab9231d9..8afdada06d 100644 --- a/examples/thoughts/package.json +++ b/examples/thoughts/package.json @@ -4,7 +4,9 @@ "dependencies": { "color-hash": "2.0.1", "react": "^18.2.0", + "react-dom": "^18.2.0", "react-markdown": "6.0.1", + "react-router-dom": "^6.26.2", "wasp": "file:.wasp/out/sdk/wasp" }, "devDependencies": { diff --git a/examples/todo-typescript/package-lock.json b/examples/todo-typescript/package-lock.json index dcc0d14e9e..8837738de7 100644 --- a/examples/todo-typescript/package-lock.json +++ b/examples/todo-typescript/package-lock.json @@ -7,6 +7,8 @@ "name": "todoTypescript", "dependencies": { "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-router-dom": "^6.26.2", "wasp": "file:.wasp/out/sdk/wasp" }, "devDependencies": { @@ -5370,17 +5372,15 @@ } }, "node_modules/react-dom": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", - "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", - "license": "MIT", - "peer": true, + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", + "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", "dependencies": { "loose-envify": "^1.1.0", - "scheduler": "^0.23.2" + "scheduler": "^0.23.0" }, "peerDependencies": { - "react": "^18.3.1" + "react": "^18.2.0" } }, "node_modules/react-hook-form": { @@ -5424,7 +5424,6 @@ "version": "6.26.2", "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.26.2.tgz", "integrity": "sha512-z7YkaEW0Dy35T3/QKPYB1LjMK2R1fxnHO8kWpUMTBdfVzZrWOiY9a7CtN8HqdWtDUWd5FY6Dl8HFsqVwH4uOtQ==", - "license": "MIT", "dependencies": { "@remix-run/router": "1.19.2", "react-router": "6.26.2" @@ -5645,7 +5644,6 @@ "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", "license": "MIT", - "peer": true, "dependencies": { "loose-envify": "^1.1.0" } diff --git a/examples/todo-typescript/package.json b/examples/todo-typescript/package.json index c65413e99d..460afdb83b 100644 --- a/examples/todo-typescript/package.json +++ b/examples/todo-typescript/package.json @@ -2,13 +2,15 @@ "name": "todoTypescript", "type": "module", "dependencies": { - "wasp": "file:.wasp/out/sdk/wasp", - "react": "^18.2.0" + "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-router-dom": "^6.26.2", + "wasp": "file:.wasp/out/sdk/wasp" }, "devDependencies": { - "typescript": "^5.1.0", - "vite": "^4.3.9", "@types/react": "^18.0.37", - "prisma": "5.19.1" + "prisma": "5.19.1", + "typescript": "^5.1.0", + "vite": "^4.3.9" } } diff --git a/examples/tutorials/TodoApp/package-lock.json b/examples/tutorials/TodoApp/package-lock.json index f2ef05ae54..e189aa4a20 100644 --- a/examples/tutorials/TodoApp/package-lock.json +++ b/examples/tutorials/TodoApp/package-lock.json @@ -7,6 +7,8 @@ "name": "TodoApp", "dependencies": { "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-router-dom": "^6.26.2", "wasp": "file:.wasp/out/sdk/wasp" }, "devDependencies": { @@ -5370,17 +5372,15 @@ } }, "node_modules/react-dom": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", - "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", - "license": "MIT", - "peer": true, + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", + "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", "dependencies": { "loose-envify": "^1.1.0", - "scheduler": "^0.23.2" + "scheduler": "^0.23.0" }, "peerDependencies": { - "react": "^18.3.1" + "react": "^18.2.0" } }, "node_modules/react-hook-form": { @@ -5424,7 +5424,6 @@ "version": "6.26.2", "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.26.2.tgz", "integrity": "sha512-z7YkaEW0Dy35T3/QKPYB1LjMK2R1fxnHO8kWpUMTBdfVzZrWOiY9a7CtN8HqdWtDUWd5FY6Dl8HFsqVwH4uOtQ==", - "license": "MIT", "dependencies": { "@remix-run/router": "1.19.2", "react-router": "6.26.2" @@ -5645,7 +5644,6 @@ "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", "license": "MIT", - "peer": true, "dependencies": { "loose-envify": "^1.1.0" } diff --git a/examples/tutorials/TodoApp/package.json b/examples/tutorials/TodoApp/package.json index 5500162ca6..e9e104d72c 100644 --- a/examples/tutorials/TodoApp/package.json +++ b/examples/tutorials/TodoApp/package.json @@ -2,13 +2,15 @@ "name": "TodoApp", "type": "module", "dependencies": { - "wasp": "file:.wasp/out/sdk/wasp", - "react": "^18.2.0" + "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-router-dom": "^6.26.2", + "wasp": "file:.wasp/out/sdk/wasp" }, "devDependencies": { - "typescript": "^5.1.0", - "vite": "^4.3.9", "@types/react": "^18.0.37", - "prisma": "5.19.1" + "prisma": "5.19.1", + "typescript": "^5.1.0", + "vite": "^4.3.9" } } diff --git a/examples/tutorials/TodoAppTs/package-lock.json b/examples/tutorials/TodoAppTs/package-lock.json index d011bf8c32..dda6060032 100644 --- a/examples/tutorials/TodoAppTs/package-lock.json +++ b/examples/tutorials/TodoAppTs/package-lock.json @@ -7,6 +7,8 @@ "name": "TodoAppTs", "dependencies": { "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-router-dom": "^6.26.2", "wasp": "file:.wasp/out/sdk/wasp" }, "devDependencies": { @@ -5370,17 +5372,15 @@ } }, "node_modules/react-dom": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", - "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", - "license": "MIT", - "peer": true, + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", + "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", "dependencies": { "loose-envify": "^1.1.0", - "scheduler": "^0.23.2" + "scheduler": "^0.23.0" }, "peerDependencies": { - "react": "^18.3.1" + "react": "^18.2.0" } }, "node_modules/react-hook-form": { @@ -5424,7 +5424,6 @@ "version": "6.26.2", "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.26.2.tgz", "integrity": "sha512-z7YkaEW0Dy35T3/QKPYB1LjMK2R1fxnHO8kWpUMTBdfVzZrWOiY9a7CtN8HqdWtDUWd5FY6Dl8HFsqVwH4uOtQ==", - "license": "MIT", "dependencies": { "@remix-run/router": "1.19.2", "react-router": "6.26.2" @@ -5645,7 +5644,6 @@ "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", "license": "MIT", - "peer": true, "dependencies": { "loose-envify": "^1.1.0" } diff --git a/examples/tutorials/TodoAppTs/package.json b/examples/tutorials/TodoAppTs/package.json index cf2818f9b9..0ca91c05ac 100644 --- a/examples/tutorials/TodoAppTs/package.json +++ b/examples/tutorials/TodoAppTs/package.json @@ -2,13 +2,15 @@ "name": "TodoAppTs", "type": "module", "dependencies": { - "wasp": "file:.wasp/out/sdk/wasp", - "react": "^18.2.0" + "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-router-dom": "^6.26.2", + "wasp": "file:.wasp/out/sdk/wasp" }, "devDependencies": { - "typescript": "^5.1.0", - "vite": "^4.3.9", "@types/react": "^18.0.37", - "prisma": "5.19.1" + "prisma": "5.19.1", + "typescript": "^5.1.0", + "vite": "^4.3.9" } } diff --git a/examples/waspello/package-lock.json b/examples/waspello/package-lock.json index 9dff5ba011..66fbc2444f 100644 --- a/examples/waspello/package-lock.json +++ b/examples/waspello/package-lock.json @@ -9,7 +9,9 @@ "@hello-pangea/dnd": "^17.0.0", "classnames": "2.3.1", "react": "^18.2.0", + "react-dom": "^18.2.0", "react-feather": "2.0.10", + "react-router-dom": "^6.26.2", "react-tiny-popover": "7.1.0", "wasp": "file:.wasp/out/sdk/wasp" }, @@ -1509,10 +1511,9 @@ } }, "node_modules/@remix-run/router": { - "version": "1.21.0", - "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.21.0.tgz", - "integrity": "sha512-xfSkCAchbdG5PnbrKqFWwia4Bi61nH+wm8wLEqfHDyp7Y3dZzgqS2itV8i4gAq9pC2HsTpwyBC6Ds8VHZ96JlA==", - "license": "MIT", + "version": "1.19.2", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.19.2.tgz", + "integrity": "sha512-baiMx18+IMuD1yyvOGaHM9QrVUPGGG0jC+z+IPHnRJWUAUvaKuWKyE8gjDj2rzv3sz9zOGoRSPgeBVHRhZnBlA==", "engines": { "node": ">=14.0.0" } @@ -6492,17 +6493,15 @@ } }, "node_modules/react-dom": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", - "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", - "license": "MIT", - "peer": true, + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", + "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", "dependencies": { "loose-envify": "^1.1.0", - "scheduler": "^0.23.2" + "scheduler": "^0.23.0" }, "peerDependencies": { - "react": "^18.3.1" + "react": "^18.2.0" } }, "node_modules/react-feather": { @@ -6540,12 +6539,11 @@ "license": "MIT" }, "node_modules/react-router": { - "version": "6.28.0", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.28.0.tgz", - "integrity": "sha512-HrYdIFqdrnhDw0PqG/AKjAqEqM7AvxCz0DQ4h2W8k6nqmc5uRBYDag0SBxx9iYz5G8gnuNVLzUe13wl9eAsXXg==", - "license": "MIT", + "version": "6.26.2", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.26.2.tgz", + "integrity": "sha512-tvN1iuT03kHgOFnLPfLJ8V95eijteveqdOSk+srqfePtQvqCExB8eHOYnlilbOcyJyKnYkr1vJvf7YqotAJu1A==", "dependencies": { - "@remix-run/router": "1.21.0" + "@remix-run/router": "1.19.2" }, "engines": { "node": ">=14.0.0" @@ -6555,13 +6553,12 @@ } }, "node_modules/react-router-dom": { - "version": "6.28.0", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.28.0.tgz", - "integrity": "sha512-kQ7Unsl5YdyOltsPGl31zOjLrDv+m2VcIEcIHqYYD3Lp0UppLjrzcfJqDJwXxFw3TH/yvapbnUvPlAj7Kx5nbg==", - "license": "MIT", + "version": "6.26.2", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.26.2.tgz", + "integrity": "sha512-z7YkaEW0Dy35T3/QKPYB1LjMK2R1fxnHO8kWpUMTBdfVzZrWOiY9a7CtN8HqdWtDUWd5FY6Dl8HFsqVwH4uOtQ==", "dependencies": { - "@remix-run/router": "1.21.0", - "react-router": "6.28.0" + "@remix-run/router": "1.19.2", + "react-router": "6.26.2" }, "engines": { "node": ">=14.0.0" @@ -6832,7 +6829,6 @@ "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", "license": "MIT", - "peer": true, "dependencies": { "loose-envify": "^1.1.0" } diff --git a/examples/waspello/package.json b/examples/waspello/package.json index f4ed19b8da..7042cd2470 100644 --- a/examples/waspello/package.json +++ b/examples/waspello/package.json @@ -2,17 +2,19 @@ "name": "waspello", "type": "module", "dependencies": { - "react-feather": "2.0.10", + "@hello-pangea/dnd": "^17.0.0", "classnames": "2.3.1", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-feather": "2.0.10", + "react-router-dom": "^6.26.2", "react-tiny-popover": "7.1.0", - "@hello-pangea/dnd": "^17.0.0", - "wasp": "file:.wasp/out/sdk/wasp", - "react": "^18.2.0" + "wasp": "file:.wasp/out/sdk/wasp" }, "devDependencies": { - "typescript": "^5.1.0", - "vite": "^4.3.9", "@types/react": "^18.0.37", - "prisma": "5.19.1" + "prisma": "5.19.1", + "typescript": "^5.1.0", + "vite": "^4.3.9" } } diff --git a/examples/waspleau/package-lock.json b/examples/waspleau/package-lock.json index c08a93fd16..700fc3cef5 100644 --- a/examples/waspleau/package-lock.json +++ b/examples/waspleau/package-lock.json @@ -8,6 +8,8 @@ "dependencies": { "axios": "^1.4.0", "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-router-dom": "^6.26.2", "wasp": "file:.wasp/out/sdk/wasp" }, "devDependencies": { @@ -4619,17 +4621,15 @@ } }, "node_modules/react-dom": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", - "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", - "license": "MIT", - "peer": true, + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", + "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", "dependencies": { "loose-envify": "^1.1.0", - "scheduler": "^0.23.2" + "scheduler": "^0.23.0" }, "peerDependencies": { - "react": "^18.3.1" + "react": "^18.2.0" } }, "node_modules/react-hook-form": { @@ -4673,7 +4673,6 @@ "version": "6.26.2", "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.26.2.tgz", "integrity": "sha512-z7YkaEW0Dy35T3/QKPYB1LjMK2R1fxnHO8kWpUMTBdfVzZrWOiY9a7CtN8HqdWtDUWd5FY6Dl8HFsqVwH4uOtQ==", - "license": "MIT", "dependencies": { "@remix-run/router": "1.19.2", "react-router": "6.26.2" @@ -4894,7 +4893,6 @@ "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", "license": "MIT", - "peer": true, "dependencies": { "loose-envify": "^1.1.0" } diff --git a/examples/waspleau/package.json b/examples/waspleau/package.json index b87cc27582..c92e80ae3b 100644 --- a/examples/waspleau/package.json +++ b/examples/waspleau/package.json @@ -3,13 +3,15 @@ "type": "module", "dependencies": { "axios": "^1.4.0", - "wasp": "file:.wasp/out/sdk/wasp", - "react": "^18.2.0" + "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-router-dom": "^6.26.2", + "wasp": "file:.wasp/out/sdk/wasp" }, "devDependencies": { - "typescript": "^5.1.0", - "vite": "^4.3.9", "@types/react": "^18.0.37", - "prisma": "5.19.1" + "prisma": "5.19.1", + "typescript": "^5.1.0", + "vite": "^4.3.9" } } diff --git a/examples/websockets-realtime-voting/package-lock.json b/examples/websockets-realtime-voting/package-lock.json index 860d532f9a..0153fa0dd6 100644 --- a/examples/websockets-realtime-voting/package-lock.json +++ b/examples/websockets-realtime-voting/package-lock.json @@ -9,6 +9,8 @@ "flowbite": "1.6.6", "flowbite-react": "0.4.9", "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-router-dom": "^6.26.2", "wasp": "file:.wasp/out/sdk/wasp" }, "devDependencies": { @@ -5306,8 +5308,8 @@ }, "node_modules/react-dom": { "version": "18.2.0", - "license": "MIT", - "peer": true, + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", + "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.0" @@ -5377,7 +5379,6 @@ "version": "6.26.2", "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.26.2.tgz", "integrity": "sha512-z7YkaEW0Dy35T3/QKPYB1LjMK2R1fxnHO8kWpUMTBdfVzZrWOiY9a7CtN8HqdWtDUWd5FY6Dl8HFsqVwH4uOtQ==", - "license": "MIT", "dependencies": { "@remix-run/router": "1.19.2", "react-router": "6.26.2" @@ -5588,7 +5589,6 @@ "node_modules/scheduler": { "version": "0.23.0", "license": "MIT", - "peer": true, "dependencies": { "loose-envify": "^1.1.0" } diff --git a/examples/websockets-realtime-voting/package.json b/examples/websockets-realtime-voting/package.json index 8e7ca26173..277acb493d 100644 --- a/examples/websockets-realtime-voting/package.json +++ b/examples/websockets-realtime-voting/package.json @@ -4,13 +4,15 @@ "dependencies": { "flowbite": "1.6.6", "flowbite-react": "0.4.9", - "wasp": "file:.wasp/out/sdk/wasp", - "react": "^18.2.0" + "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-router-dom": "^6.26.2", + "wasp": "file:.wasp/out/sdk/wasp" }, "devDependencies": { - "typescript": "^5.1.0", - "vite": "^4.5.3", "@types/react": "^18.0.37", - "prisma": "5.19.1" + "prisma": "5.19.1", + "typescript": "^5.1.0", + "vite": "^4.5.3" } } diff --git a/waspc/data/Cli/templates/basic/package.json b/waspc/data/Cli/templates/basic/package.json index 47102a526e..11a5b74771 100644 --- a/waspc/data/Cli/templates/basic/package.json +++ b/waspc/data/Cli/templates/basic/package.json @@ -3,7 +3,9 @@ "type": "module", "dependencies": { "wasp": "file:.wasp/out/sdk/wasp", - "react": "^18.2.0" + "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-router-dom": "^6.26.2" }, "devDependencies": { "typescript": "^5.1.0", diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/.waspchecksums b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/.waspchecksums index 42274cd76e..1454c2c731 100644 --- a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/.waspchecksums +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/.waspchecksums @@ -543,7 +543,7 @@ "file", "web-app/package.json" ], - "b9902749188431ba59e788a2e734086cf745bae9b7f7813f17ec90e2a3510daa" + "196b888bc57d79d1c32f4d3af30fbc32817afb983fa890c3749d4fcf92193a5b" ], [ [ diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/installedNpmDepsLog.json b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/installedNpmDepsLog.json index 88f5516a48..a21ddcd4a1 100644 --- a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/installedNpmDepsLog.json +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/installedNpmDepsLog.json @@ -1 +1 @@ -{"_waspSdkNpmDeps":{"dependencies":[{"name":"@prisma/client","version":"5.19.1"},{"name":"prisma","version":"5.19.1"},{"name":"@tanstack/react-query","version":"^4.29.0"},{"name":"axios","version":"^1.4.0"},{"name":"express","version":"~4.21.0"},{"name":"mitt","version":"3.0.0"},{"name":"react","version":"^18.2.0"},{"name":"lodash.merge","version":"^4.6.2"},{"name":"react-router-dom","version":"^6.26.2"},{"name":"react-hook-form","version":"^7.45.4"},{"name":"superjson","version":"^2.2.1"},{"name":"vitest","version":"^1.2.1"},{"name":"@vitest/ui","version":"^1.2.1"},{"name":"jsdom","version":"^21.1.1"},{"name":"@testing-library/react","version":"^14.1.2"},{"name":"@testing-library/jest-dom","version":"^6.3.0"},{"name":"msw","version":"^1.1.0"}],"devDependencies":[{"name":"@tsconfig/node18","version":"latest"},{"name":"@types/express-serve-static-core","version":"^4.17.13"}]},"_userNpmDeps":{"userDependencies":[{"name":"react","version":"^18.2.0"},{"name":"wasp","version":"file:.wasp/out/sdk/wasp"}],"userDevDependencies":[{"name":"@types/react","version":"^18.0.37"},{"name":"prisma","version":"5.19.1"},{"name":"typescript","version":"^5.1.0"},{"name":"vite","version":"^4.3.9"}]},"_waspFrameworkNpmDeps":{"npmDepsForWebApp":{"dependencies":[{"name":"@tanstack/react-query","version":"^4.29.0"},{"name":"axios","version":"^1.4.0"},{"name":"react-dom","version":"^18.2.0"},{"name":"react-router-dom","version":"^6.26.2"}],"devDependencies":[{"name":"@tsconfig/vite-react","version":"^2.0.0"},{"name":"@types/react-dom","version":"^18.0.11"},{"name":"@vitejs/plugin-react","version":"^4.2.1"},{"name":"dotenv","version":"^16.0.3"}]},"npmDepsForServer":{"dependencies":[{"name":"cookie-parser","version":"~1.4.6"},{"name":"cors","version":"^2.8.5"},{"name":"dotenv","version":"^16.0.2"},{"name":"express","version":"~4.21.0"},{"name":"helmet","version":"^6.0.0"},{"name":"morgan","version":"~1.10.0"},{"name":"superjson","version":"^2.2.1"}],"devDependencies":[{"name":"@tsconfig/node18","version":"latest"},{"name":"@types/cors","version":"^2.8.5"},{"name":"@types/express","version":"^4.17.13"},{"name":"@types/express-serve-static-core","version":"^4.17.13"},{"name":"@types/node","version":"^18.0.0"},{"name":"nodemon","version":"^2.0.19"},{"name":"rollup","version":"^4.9.6"},{"name":"rollup-plugin-esbuild","version":"^6.1.1"}]}}} \ No newline at end of file +{"_waspSdkNpmDeps":{"dependencies":[{"name":"@prisma/client","version":"5.19.1"},{"name":"prisma","version":"5.19.1"},{"name":"@tanstack/react-query","version":"^4.29.0"},{"name":"axios","version":"^1.4.0"},{"name":"express","version":"~4.21.0"},{"name":"mitt","version":"3.0.0"},{"name":"react","version":"^18.2.0"},{"name":"lodash.merge","version":"^4.6.2"},{"name":"react-router-dom","version":"^6.26.2"},{"name":"react-hook-form","version":"^7.45.4"},{"name":"superjson","version":"^2.2.1"},{"name":"vitest","version":"^1.2.1"},{"name":"@vitest/ui","version":"^1.2.1"},{"name":"jsdom","version":"^21.1.1"},{"name":"@testing-library/react","version":"^14.1.2"},{"name":"@testing-library/jest-dom","version":"^6.3.0"},{"name":"msw","version":"^1.1.0"}],"devDependencies":[{"name":"@tsconfig/node18","version":"latest"},{"name":"@types/express-serve-static-core","version":"^4.17.13"}]},"_userNpmDeps":{"userDependencies":[{"name":"react","version":"^18.2.0"},{"name":"react-dom","version":"^18.2.0"},{"name":"react-router-dom","version":"^6.26.2"},{"name":"wasp","version":"file:.wasp/out/sdk/wasp"}],"userDevDependencies":[{"name":"@types/react","version":"^18.0.37"},{"name":"prisma","version":"5.19.1"},{"name":"typescript","version":"^5.1.0"},{"name":"vite","version":"^4.3.9"}]},"_waspFrameworkNpmDeps":{"npmDepsForWebApp":{"dependencies":[{"name":"@tanstack/react-query","version":"^4.29.0"},{"name":"axios","version":"^1.4.0"}],"devDependencies":[{"name":"@tsconfig/vite-react","version":"^2.0.0"},{"name":"@types/react-dom","version":"^18.0.11"},{"name":"@vitejs/plugin-react","version":"^4.2.1"},{"name":"dotenv","version":"^16.0.3"}]},"npmDepsForServer":{"dependencies":[{"name":"cookie-parser","version":"~1.4.6"},{"name":"cors","version":"^2.8.5"},{"name":"dotenv","version":"^16.0.2"},{"name":"express","version":"~4.21.0"},{"name":"helmet","version":"^6.0.0"},{"name":"morgan","version":"~1.10.0"},{"name":"superjson","version":"^2.2.1"}],"devDependencies":[{"name":"@tsconfig/node18","version":"latest"},{"name":"@types/cors","version":"^2.8.5"},{"name":"@types/express","version":"^4.17.13"},{"name":"@types/express-serve-static-core","version":"^4.17.13"},{"name":"@types/node","version":"^18.0.0"},{"name":"nodemon","version":"^2.0.19"},{"name":"rollup","version":"^4.9.6"},{"name":"rollup-plugin-esbuild","version":"^6.1.1"}]}}} \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/package.json b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/package.json index 7b5eed4125..0a0016eef6 100644 --- a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/package.json +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/package.json @@ -1,6 +1,8 @@ { "dependencies": { "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-router-dom": "^6.26.2", "wasp": "file:.wasp/out/sdk/wasp" }, "devDependencies": { diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/web-app/package.json b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/web-app/package.json index dd68b15f36..08f5ace058 100644 --- a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/web-app/package.json +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/web-app/package.json @@ -13,9 +13,7 @@ }, "dependencies": { "@tanstack/react-query": "^4.29.0", - "axios": "^1.4.0", - "react-dom": "^18.2.0", - "react-router-dom": "^6.26.2" + "axios": "^1.4.0" }, "devDependencies": { "@tsconfig/vite-react": "^2.0.0", diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/package.json b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/package.json index 7b5eed4125..0a0016eef6 100644 --- a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/package.json +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/package.json @@ -1,6 +1,8 @@ { "dependencies": { "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-router-dom": "^6.26.2", "wasp": "file:.wasp/out/sdk/wasp" }, "devDependencies": { diff --git a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/.waspchecksums b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/.waspchecksums index 8145a2edf9..02790ce12f 100644 --- a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/.waspchecksums +++ b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/.waspchecksums @@ -557,7 +557,7 @@ "file", "web-app/package.json" ], - "e7ed7bc4ce966c2fb9b8fff9844d9602efcabce0eda274ffa0d77204525bd0b6" + "63ecfca14bd00080f48d3c4e6dff009ddf2777569a07c5e7db647e0384722212" ], [ [ diff --git a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/installedNpmDepsLog.json b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/installedNpmDepsLog.json index 88f5516a48..a21ddcd4a1 100644 --- a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/installedNpmDepsLog.json +++ b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/installedNpmDepsLog.json @@ -1 +1 @@ -{"_waspSdkNpmDeps":{"dependencies":[{"name":"@prisma/client","version":"5.19.1"},{"name":"prisma","version":"5.19.1"},{"name":"@tanstack/react-query","version":"^4.29.0"},{"name":"axios","version":"^1.4.0"},{"name":"express","version":"~4.21.0"},{"name":"mitt","version":"3.0.0"},{"name":"react","version":"^18.2.0"},{"name":"lodash.merge","version":"^4.6.2"},{"name":"react-router-dom","version":"^6.26.2"},{"name":"react-hook-form","version":"^7.45.4"},{"name":"superjson","version":"^2.2.1"},{"name":"vitest","version":"^1.2.1"},{"name":"@vitest/ui","version":"^1.2.1"},{"name":"jsdom","version":"^21.1.1"},{"name":"@testing-library/react","version":"^14.1.2"},{"name":"@testing-library/jest-dom","version":"^6.3.0"},{"name":"msw","version":"^1.1.0"}],"devDependencies":[{"name":"@tsconfig/node18","version":"latest"},{"name":"@types/express-serve-static-core","version":"^4.17.13"}]},"_userNpmDeps":{"userDependencies":[{"name":"react","version":"^18.2.0"},{"name":"wasp","version":"file:.wasp/out/sdk/wasp"}],"userDevDependencies":[{"name":"@types/react","version":"^18.0.37"},{"name":"prisma","version":"5.19.1"},{"name":"typescript","version":"^5.1.0"},{"name":"vite","version":"^4.3.9"}]},"_waspFrameworkNpmDeps":{"npmDepsForWebApp":{"dependencies":[{"name":"@tanstack/react-query","version":"^4.29.0"},{"name":"axios","version":"^1.4.0"},{"name":"react-dom","version":"^18.2.0"},{"name":"react-router-dom","version":"^6.26.2"}],"devDependencies":[{"name":"@tsconfig/vite-react","version":"^2.0.0"},{"name":"@types/react-dom","version":"^18.0.11"},{"name":"@vitejs/plugin-react","version":"^4.2.1"},{"name":"dotenv","version":"^16.0.3"}]},"npmDepsForServer":{"dependencies":[{"name":"cookie-parser","version":"~1.4.6"},{"name":"cors","version":"^2.8.5"},{"name":"dotenv","version":"^16.0.2"},{"name":"express","version":"~4.21.0"},{"name":"helmet","version":"^6.0.0"},{"name":"morgan","version":"~1.10.0"},{"name":"superjson","version":"^2.2.1"}],"devDependencies":[{"name":"@tsconfig/node18","version":"latest"},{"name":"@types/cors","version":"^2.8.5"},{"name":"@types/express","version":"^4.17.13"},{"name":"@types/express-serve-static-core","version":"^4.17.13"},{"name":"@types/node","version":"^18.0.0"},{"name":"nodemon","version":"^2.0.19"},{"name":"rollup","version":"^4.9.6"},{"name":"rollup-plugin-esbuild","version":"^6.1.1"}]}}} \ No newline at end of file +{"_waspSdkNpmDeps":{"dependencies":[{"name":"@prisma/client","version":"5.19.1"},{"name":"prisma","version":"5.19.1"},{"name":"@tanstack/react-query","version":"^4.29.0"},{"name":"axios","version":"^1.4.0"},{"name":"express","version":"~4.21.0"},{"name":"mitt","version":"3.0.0"},{"name":"react","version":"^18.2.0"},{"name":"lodash.merge","version":"^4.6.2"},{"name":"react-router-dom","version":"^6.26.2"},{"name":"react-hook-form","version":"^7.45.4"},{"name":"superjson","version":"^2.2.1"},{"name":"vitest","version":"^1.2.1"},{"name":"@vitest/ui","version":"^1.2.1"},{"name":"jsdom","version":"^21.1.1"},{"name":"@testing-library/react","version":"^14.1.2"},{"name":"@testing-library/jest-dom","version":"^6.3.0"},{"name":"msw","version":"^1.1.0"}],"devDependencies":[{"name":"@tsconfig/node18","version":"latest"},{"name":"@types/express-serve-static-core","version":"^4.17.13"}]},"_userNpmDeps":{"userDependencies":[{"name":"react","version":"^18.2.0"},{"name":"react-dom","version":"^18.2.0"},{"name":"react-router-dom","version":"^6.26.2"},{"name":"wasp","version":"file:.wasp/out/sdk/wasp"}],"userDevDependencies":[{"name":"@types/react","version":"^18.0.37"},{"name":"prisma","version":"5.19.1"},{"name":"typescript","version":"^5.1.0"},{"name":"vite","version":"^4.3.9"}]},"_waspFrameworkNpmDeps":{"npmDepsForWebApp":{"dependencies":[{"name":"@tanstack/react-query","version":"^4.29.0"},{"name":"axios","version":"^1.4.0"}],"devDependencies":[{"name":"@tsconfig/vite-react","version":"^2.0.0"},{"name":"@types/react-dom","version":"^18.0.11"},{"name":"@vitejs/plugin-react","version":"^4.2.1"},{"name":"dotenv","version":"^16.0.3"}]},"npmDepsForServer":{"dependencies":[{"name":"cookie-parser","version":"~1.4.6"},{"name":"cors","version":"^2.8.5"},{"name":"dotenv","version":"^16.0.2"},{"name":"express","version":"~4.21.0"},{"name":"helmet","version":"^6.0.0"},{"name":"morgan","version":"~1.10.0"},{"name":"superjson","version":"^2.2.1"}],"devDependencies":[{"name":"@tsconfig/node18","version":"latest"},{"name":"@types/cors","version":"^2.8.5"},{"name":"@types/express","version":"^4.17.13"},{"name":"@types/express-serve-static-core","version":"^4.17.13"},{"name":"@types/node","version":"^18.0.0"},{"name":"nodemon","version":"^2.0.19"},{"name":"rollup","version":"^4.9.6"},{"name":"rollup-plugin-esbuild","version":"^6.1.1"}]}}} \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/web-app/package.json b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/web-app/package.json index 1f9d6dd2df..c62763be23 100644 --- a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/web-app/package.json +++ b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/web-app/package.json @@ -13,9 +13,7 @@ }, "dependencies": { "@tanstack/react-query": "^4.29.0", - "axios": "^1.4.0", - "react-dom": "^18.2.0", - "react-router-dom": "^6.26.2" + "axios": "^1.4.0" }, "devDependencies": { "@tsconfig/vite-react": "^2.0.0", diff --git a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/package.json b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/package.json index 1ffaf97439..1528cb5be8 100644 --- a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/package.json +++ b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/package.json @@ -1,6 +1,8 @@ { "dependencies": { "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-router-dom": "^6.26.2", "wasp": "file:.wasp/out/sdk/wasp" }, "devDependencies": { diff --git a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/.waspchecksums b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/.waspchecksums index f3452448e8..802add6c20 100644 --- a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/.waspchecksums +++ b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/.waspchecksums @@ -1187,7 +1187,7 @@ "file", "web-app/package.json" ], - "fdca7d2013c3fd385a120e3d058f80eea6ed69fd2a22219256299f2f4941fe04" + "7e7cce9cb193df0ebb8cfe1ffc9b62b4d6aefe2d74c8631d8dcca45a21fff77a" ], [ [ diff --git a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/installedNpmDepsLog.json b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/installedNpmDepsLog.json index c0f1ff6bfa..800ff0bab9 100644 --- a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/installedNpmDepsLog.json +++ b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/installedNpmDepsLog.json @@ -1 +1 @@ -{"_waspSdkNpmDeps":{"dependencies":[{"name":"@prisma/client","version":"5.19.1"},{"name":"prisma","version":"5.19.1"},{"name":"@tanstack/react-query","version":"^4.29.0"},{"name":"axios","version":"^1.4.0"},{"name":"express","version":"~4.21.0"},{"name":"mitt","version":"3.0.0"},{"name":"react","version":"^18.2.0"},{"name":"lodash.merge","version":"^4.6.2"},{"name":"react-router-dom","version":"^6.26.2"},{"name":"react-hook-form","version":"^7.45.4"},{"name":"superjson","version":"^2.2.1"},{"name":"@stitches/react","version":"^1.2.8"},{"name":"@node-rs/argon2","version":"^1.8.3"},{"name":"arctic","version":"^1.2.1"},{"name":"lucia","version":"^3.0.1"},{"name":"oslo","version":"^1.1.2"},{"name":"@lucia-auth/adapter-prisma","version":"^4.0.0"},{"name":"@sendgrid/mail","version":"^7.7.0"},{"name":"vitest","version":"^1.2.1"},{"name":"@vitest/ui","version":"^1.2.1"},{"name":"jsdom","version":"^21.1.1"},{"name":"@testing-library/react","version":"^14.1.2"},{"name":"@testing-library/jest-dom","version":"^6.3.0"},{"name":"msw","version":"^1.1.0"},{"name":"pg-boss","version":"^8.4.2"}],"devDependencies":[{"name":"@tsconfig/node18","version":"latest"},{"name":"@types/express-serve-static-core","version":"^4.17.13"}]},"_userNpmDeps":{"userDependencies":[{"name":"react","version":"^18.2.0"},{"name":"wasp","version":"file:.wasp/out/sdk/wasp"}],"userDevDependencies":[{"name":"@types/react","version":"^18.0.37"},{"name":"prisma","version":"5.19.1"},{"name":"typescript","version":"^5.1.0"},{"name":"vite","version":"^4.3.9"}]},"_waspFrameworkNpmDeps":{"npmDepsForWebApp":{"dependencies":[{"name":"@tanstack/react-query","version":"^4.29.0"},{"name":"axios","version":"^1.4.0"},{"name":"react-dom","version":"^18.2.0"},{"name":"react-router-dom","version":"^6.26.2"}],"devDependencies":[{"name":"@tsconfig/vite-react","version":"^2.0.0"},{"name":"@types/react-dom","version":"^18.0.11"},{"name":"@vitejs/plugin-react","version":"^4.2.1"},{"name":"dotenv","version":"^16.0.3"}]},"npmDepsForServer":{"dependencies":[{"name":"cookie-parser","version":"~1.4.6"},{"name":"cors","version":"^2.8.5"},{"name":"dotenv","version":"^16.0.2"},{"name":"express","version":"~4.21.0"},{"name":"helmet","version":"^6.0.0"},{"name":"morgan","version":"~1.10.0"},{"name":"superjson","version":"^2.2.1"}],"devDependencies":[{"name":"@tsconfig/node18","version":"latest"},{"name":"@types/cors","version":"^2.8.5"},{"name":"@types/express","version":"^4.17.13"},{"name":"@types/express-serve-static-core","version":"^4.17.13"},{"name":"@types/node","version":"^18.0.0"},{"name":"nodemon","version":"^2.0.19"},{"name":"rollup","version":"^4.9.6"},{"name":"rollup-plugin-esbuild","version":"^6.1.1"}]}}} \ No newline at end of file +{"_waspSdkNpmDeps":{"dependencies":[{"name":"@prisma/client","version":"5.19.1"},{"name":"prisma","version":"5.19.1"},{"name":"@tanstack/react-query","version":"^4.29.0"},{"name":"axios","version":"^1.4.0"},{"name":"express","version":"~4.21.0"},{"name":"mitt","version":"3.0.0"},{"name":"react","version":"^18.2.0"},{"name":"lodash.merge","version":"^4.6.2"},{"name":"react-router-dom","version":"^6.26.2"},{"name":"react-hook-form","version":"^7.45.4"},{"name":"superjson","version":"^2.2.1"},{"name":"@stitches/react","version":"^1.2.8"},{"name":"@node-rs/argon2","version":"^1.8.3"},{"name":"arctic","version":"^1.2.1"},{"name":"lucia","version":"^3.0.1"},{"name":"oslo","version":"^1.1.2"},{"name":"@lucia-auth/adapter-prisma","version":"^4.0.0"},{"name":"@sendgrid/mail","version":"^7.7.0"},{"name":"vitest","version":"^1.2.1"},{"name":"@vitest/ui","version":"^1.2.1"},{"name":"jsdom","version":"^21.1.1"},{"name":"@testing-library/react","version":"^14.1.2"},{"name":"@testing-library/jest-dom","version":"^6.3.0"},{"name":"msw","version":"^1.1.0"},{"name":"pg-boss","version":"^8.4.2"}],"devDependencies":[{"name":"@tsconfig/node18","version":"latest"},{"name":"@types/express-serve-static-core","version":"^4.17.13"}]},"_userNpmDeps":{"userDependencies":[{"name":"react","version":"^18.2.0"},{"name":"react-dom","version":"^18.2.0"},{"name":"react-router-dom","version":"^6.26.2"},{"name":"wasp","version":"file:.wasp/out/sdk/wasp"}],"userDevDependencies":[{"name":"@types/react","version":"^18.0.37"},{"name":"prisma","version":"5.19.1"},{"name":"typescript","version":"^5.1.0"},{"name":"vite","version":"^4.3.9"}]},"_waspFrameworkNpmDeps":{"npmDepsForWebApp":{"dependencies":[{"name":"@tanstack/react-query","version":"^4.29.0"},{"name":"axios","version":"^1.4.0"}],"devDependencies":[{"name":"@tsconfig/vite-react","version":"^2.0.0"},{"name":"@types/react-dom","version":"^18.0.11"},{"name":"@vitejs/plugin-react","version":"^4.2.1"},{"name":"dotenv","version":"^16.0.3"}]},"npmDepsForServer":{"dependencies":[{"name":"cookie-parser","version":"~1.4.6"},{"name":"cors","version":"^2.8.5"},{"name":"dotenv","version":"^16.0.2"},{"name":"express","version":"~4.21.0"},{"name":"helmet","version":"^6.0.0"},{"name":"morgan","version":"~1.10.0"},{"name":"superjson","version":"^2.2.1"}],"devDependencies":[{"name":"@tsconfig/node18","version":"latest"},{"name":"@types/cors","version":"^2.8.5"},{"name":"@types/express","version":"^4.17.13"},{"name":"@types/express-serve-static-core","version":"^4.17.13"},{"name":"@types/node","version":"^18.0.0"},{"name":"nodemon","version":"^2.0.19"},{"name":"rollup","version":"^4.9.6"},{"name":"rollup-plugin-esbuild","version":"^6.1.1"}]}}} \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/web-app/package.json b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/web-app/package.json index ba4b812b06..92845d4306 100644 --- a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/web-app/package.json +++ b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/web-app/package.json @@ -13,9 +13,7 @@ }, "dependencies": { "@tanstack/react-query": "^4.29.0", - "axios": "^1.4.0", - "react-dom": "^18.2.0", - "react-router-dom": "^6.26.2" + "axios": "^1.4.0" }, "devDependencies": { "@tsconfig/vite-react": "^2.0.0", diff --git a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/package.json b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/package.json index 61062ad976..3af0252add 100644 --- a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/package.json +++ b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/package.json @@ -1,6 +1,8 @@ { "dependencies": { "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-router-dom": "^6.26.2", "wasp": "file:.wasp/out/sdk/wasp" }, "devDependencies": { diff --git a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/.waspchecksums b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/.waspchecksums index 072d7080ae..6b7d97a3d3 100644 --- a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/.waspchecksums +++ b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/.waspchecksums @@ -655,7 +655,7 @@ "file", "web-app/package.json" ], - "b1c4b91445cf4f3c434893a5dfbffac3da58dff66ee823efbd9b5244b914d043" + "bd3bb0b696d11b21492b188790f36d1a09d50d154f3e171f2392406e02304561" ], [ [ diff --git a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/installedNpmDepsLog.json b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/installedNpmDepsLog.json index 3a691152a0..b173928787 100644 --- a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/installedNpmDepsLog.json +++ b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/installedNpmDepsLog.json @@ -1 +1 @@ -{"_waspSdkNpmDeps":{"dependencies":[{"name":"@prisma/client","version":"5.19.1"},{"name":"prisma","version":"5.19.1"},{"name":"@tanstack/react-query","version":"^4.29.0"},{"name":"axios","version":"^1.4.0"},{"name":"express","version":"~4.21.0"},{"name":"mitt","version":"3.0.0"},{"name":"react","version":"^18.2.0"},{"name":"lodash.merge","version":"^4.6.2"},{"name":"react-router-dom","version":"^6.26.2"},{"name":"react-hook-form","version":"^7.45.4"},{"name":"superjson","version":"^2.2.1"},{"name":"vitest","version":"^1.2.1"},{"name":"@vitest/ui","version":"^1.2.1"},{"name":"jsdom","version":"^21.1.1"},{"name":"@testing-library/react","version":"^14.1.2"},{"name":"@testing-library/jest-dom","version":"^6.3.0"},{"name":"msw","version":"^1.1.0"},{"name":"pg-boss","version":"^8.4.2"}],"devDependencies":[{"name":"@tsconfig/node18","version":"latest"},{"name":"@types/express-serve-static-core","version":"^4.17.13"}]},"_userNpmDeps":{"userDependencies":[{"name":"react","version":"^18.2.0"},{"name":"wasp","version":"file:.wasp/out/sdk/wasp"}],"userDevDependencies":[{"name":"@types/react","version":"^18.0.37"},{"name":"prisma","version":"5.19.1"},{"name":"typescript","version":"^5.1.0"},{"name":"vite","version":"^4.3.9"}]},"_waspFrameworkNpmDeps":{"npmDepsForWebApp":{"dependencies":[{"name":"@tanstack/react-query","version":"^4.29.0"},{"name":"axios","version":"^1.4.0"},{"name":"react-dom","version":"^18.2.0"},{"name":"react-router-dom","version":"^6.26.2"}],"devDependencies":[{"name":"@tsconfig/vite-react","version":"^2.0.0"},{"name":"@types/react-dom","version":"^18.0.11"},{"name":"@vitejs/plugin-react","version":"^4.2.1"},{"name":"dotenv","version":"^16.0.3"}]},"npmDepsForServer":{"dependencies":[{"name":"cookie-parser","version":"~1.4.6"},{"name":"cors","version":"^2.8.5"},{"name":"dotenv","version":"^16.0.2"},{"name":"express","version":"~4.21.0"},{"name":"helmet","version":"^6.0.0"},{"name":"morgan","version":"~1.10.0"},{"name":"superjson","version":"^2.2.1"}],"devDependencies":[{"name":"@tsconfig/node18","version":"latest"},{"name":"@types/cors","version":"^2.8.5"},{"name":"@types/express","version":"^4.17.13"},{"name":"@types/express-serve-static-core","version":"^4.17.13"},{"name":"@types/node","version":"^18.0.0"},{"name":"nodemon","version":"^2.0.19"},{"name":"rollup","version":"^4.9.6"},{"name":"rollup-plugin-esbuild","version":"^6.1.1"}]}}} \ No newline at end of file +{"_waspSdkNpmDeps":{"dependencies":[{"name":"@prisma/client","version":"5.19.1"},{"name":"prisma","version":"5.19.1"},{"name":"@tanstack/react-query","version":"^4.29.0"},{"name":"axios","version":"^1.4.0"},{"name":"express","version":"~4.21.0"},{"name":"mitt","version":"3.0.0"},{"name":"react","version":"^18.2.0"},{"name":"lodash.merge","version":"^4.6.2"},{"name":"react-router-dom","version":"^6.26.2"},{"name":"react-hook-form","version":"^7.45.4"},{"name":"superjson","version":"^2.2.1"},{"name":"vitest","version":"^1.2.1"},{"name":"@vitest/ui","version":"^1.2.1"},{"name":"jsdom","version":"^21.1.1"},{"name":"@testing-library/react","version":"^14.1.2"},{"name":"@testing-library/jest-dom","version":"^6.3.0"},{"name":"msw","version":"^1.1.0"},{"name":"pg-boss","version":"^8.4.2"}],"devDependencies":[{"name":"@tsconfig/node18","version":"latest"},{"name":"@types/express-serve-static-core","version":"^4.17.13"}]},"_userNpmDeps":{"userDependencies":[{"name":"react","version":"^18.2.0"},{"name":"react-dom","version":"^18.2.0"},{"name":"react-router-dom","version":"^6.26.2"},{"name":"wasp","version":"file:.wasp/out/sdk/wasp"}],"userDevDependencies":[{"name":"@types/react","version":"^18.0.37"},{"name":"prisma","version":"5.19.1"},{"name":"typescript","version":"^5.1.0"},{"name":"vite","version":"^4.3.9"}]},"_waspFrameworkNpmDeps":{"npmDepsForWebApp":{"dependencies":[{"name":"@tanstack/react-query","version":"^4.29.0"},{"name":"axios","version":"^1.4.0"}],"devDependencies":[{"name":"@tsconfig/vite-react","version":"^2.0.0"},{"name":"@types/react-dom","version":"^18.0.11"},{"name":"@vitejs/plugin-react","version":"^4.2.1"},{"name":"dotenv","version":"^16.0.3"}]},"npmDepsForServer":{"dependencies":[{"name":"cookie-parser","version":"~1.4.6"},{"name":"cors","version":"^2.8.5"},{"name":"dotenv","version":"^16.0.2"},{"name":"express","version":"~4.21.0"},{"name":"helmet","version":"^6.0.0"},{"name":"morgan","version":"~1.10.0"},{"name":"superjson","version":"^2.2.1"}],"devDependencies":[{"name":"@tsconfig/node18","version":"latest"},{"name":"@types/cors","version":"^2.8.5"},{"name":"@types/express","version":"^4.17.13"},{"name":"@types/express-serve-static-core","version":"^4.17.13"},{"name":"@types/node","version":"^18.0.0"},{"name":"nodemon","version":"^2.0.19"},{"name":"rollup","version":"^4.9.6"},{"name":"rollup-plugin-esbuild","version":"^6.1.1"}]}}} \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/web-app/package.json b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/web-app/package.json index e14a48c91a..cd8a6c2ab1 100644 --- a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/web-app/package.json +++ b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/web-app/package.json @@ -13,9 +13,7 @@ }, "dependencies": { "@tanstack/react-query": "^4.29.0", - "axios": "^1.4.0", - "react-dom": "^18.2.0", - "react-router-dom": "^6.26.2" + "axios": "^1.4.0" }, "devDependencies": { "@tsconfig/vite-react": "^2.0.0", diff --git a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/package.json b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/package.json index deb4a4baca..b1b1881d36 100644 --- a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/package.json +++ b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/package.json @@ -1,6 +1,8 @@ { "dependencies": { "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-router-dom": "^6.26.2", "wasp": "file:.wasp/out/sdk/wasp" }, "devDependencies": { diff --git a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/.waspchecksums b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/.waspchecksums index 1e337a549d..8ec88ca7fa 100644 --- a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/.waspchecksums +++ b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/.waspchecksums @@ -557,7 +557,7 @@ "file", "web-app/package.json" ], - "cf272e5e3a4f18f4585575830f29133d931635f20899f7816ca4126579879f98" + "c87e74f65917573dad902b8108ddce459af520bf7d3deb8cad6794fafa86749b" ], [ [ diff --git a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/installedNpmDepsLog.json b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/installedNpmDepsLog.json index 88f5516a48..a21ddcd4a1 100644 --- a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/installedNpmDepsLog.json +++ b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/installedNpmDepsLog.json @@ -1 +1 @@ -{"_waspSdkNpmDeps":{"dependencies":[{"name":"@prisma/client","version":"5.19.1"},{"name":"prisma","version":"5.19.1"},{"name":"@tanstack/react-query","version":"^4.29.0"},{"name":"axios","version":"^1.4.0"},{"name":"express","version":"~4.21.0"},{"name":"mitt","version":"3.0.0"},{"name":"react","version":"^18.2.0"},{"name":"lodash.merge","version":"^4.6.2"},{"name":"react-router-dom","version":"^6.26.2"},{"name":"react-hook-form","version":"^7.45.4"},{"name":"superjson","version":"^2.2.1"},{"name":"vitest","version":"^1.2.1"},{"name":"@vitest/ui","version":"^1.2.1"},{"name":"jsdom","version":"^21.1.1"},{"name":"@testing-library/react","version":"^14.1.2"},{"name":"@testing-library/jest-dom","version":"^6.3.0"},{"name":"msw","version":"^1.1.0"}],"devDependencies":[{"name":"@tsconfig/node18","version":"latest"},{"name":"@types/express-serve-static-core","version":"^4.17.13"}]},"_userNpmDeps":{"userDependencies":[{"name":"react","version":"^18.2.0"},{"name":"wasp","version":"file:.wasp/out/sdk/wasp"}],"userDevDependencies":[{"name":"@types/react","version":"^18.0.37"},{"name":"prisma","version":"5.19.1"},{"name":"typescript","version":"^5.1.0"},{"name":"vite","version":"^4.3.9"}]},"_waspFrameworkNpmDeps":{"npmDepsForWebApp":{"dependencies":[{"name":"@tanstack/react-query","version":"^4.29.0"},{"name":"axios","version":"^1.4.0"},{"name":"react-dom","version":"^18.2.0"},{"name":"react-router-dom","version":"^6.26.2"}],"devDependencies":[{"name":"@tsconfig/vite-react","version":"^2.0.0"},{"name":"@types/react-dom","version":"^18.0.11"},{"name":"@vitejs/plugin-react","version":"^4.2.1"},{"name":"dotenv","version":"^16.0.3"}]},"npmDepsForServer":{"dependencies":[{"name":"cookie-parser","version":"~1.4.6"},{"name":"cors","version":"^2.8.5"},{"name":"dotenv","version":"^16.0.2"},{"name":"express","version":"~4.21.0"},{"name":"helmet","version":"^6.0.0"},{"name":"morgan","version":"~1.10.0"},{"name":"superjson","version":"^2.2.1"}],"devDependencies":[{"name":"@tsconfig/node18","version":"latest"},{"name":"@types/cors","version":"^2.8.5"},{"name":"@types/express","version":"^4.17.13"},{"name":"@types/express-serve-static-core","version":"^4.17.13"},{"name":"@types/node","version":"^18.0.0"},{"name":"nodemon","version":"^2.0.19"},{"name":"rollup","version":"^4.9.6"},{"name":"rollup-plugin-esbuild","version":"^6.1.1"}]}}} \ No newline at end of file +{"_waspSdkNpmDeps":{"dependencies":[{"name":"@prisma/client","version":"5.19.1"},{"name":"prisma","version":"5.19.1"},{"name":"@tanstack/react-query","version":"^4.29.0"},{"name":"axios","version":"^1.4.0"},{"name":"express","version":"~4.21.0"},{"name":"mitt","version":"3.0.0"},{"name":"react","version":"^18.2.0"},{"name":"lodash.merge","version":"^4.6.2"},{"name":"react-router-dom","version":"^6.26.2"},{"name":"react-hook-form","version":"^7.45.4"},{"name":"superjson","version":"^2.2.1"},{"name":"vitest","version":"^1.2.1"},{"name":"@vitest/ui","version":"^1.2.1"},{"name":"jsdom","version":"^21.1.1"},{"name":"@testing-library/react","version":"^14.1.2"},{"name":"@testing-library/jest-dom","version":"^6.3.0"},{"name":"msw","version":"^1.1.0"}],"devDependencies":[{"name":"@tsconfig/node18","version":"latest"},{"name":"@types/express-serve-static-core","version":"^4.17.13"}]},"_userNpmDeps":{"userDependencies":[{"name":"react","version":"^18.2.0"},{"name":"react-dom","version":"^18.2.0"},{"name":"react-router-dom","version":"^6.26.2"},{"name":"wasp","version":"file:.wasp/out/sdk/wasp"}],"userDevDependencies":[{"name":"@types/react","version":"^18.0.37"},{"name":"prisma","version":"5.19.1"},{"name":"typescript","version":"^5.1.0"},{"name":"vite","version":"^4.3.9"}]},"_waspFrameworkNpmDeps":{"npmDepsForWebApp":{"dependencies":[{"name":"@tanstack/react-query","version":"^4.29.0"},{"name":"axios","version":"^1.4.0"}],"devDependencies":[{"name":"@tsconfig/vite-react","version":"^2.0.0"},{"name":"@types/react-dom","version":"^18.0.11"},{"name":"@vitejs/plugin-react","version":"^4.2.1"},{"name":"dotenv","version":"^16.0.3"}]},"npmDepsForServer":{"dependencies":[{"name":"cookie-parser","version":"~1.4.6"},{"name":"cors","version":"^2.8.5"},{"name":"dotenv","version":"^16.0.2"},{"name":"express","version":"~4.21.0"},{"name":"helmet","version":"^6.0.0"},{"name":"morgan","version":"~1.10.0"},{"name":"superjson","version":"^2.2.1"}],"devDependencies":[{"name":"@tsconfig/node18","version":"latest"},{"name":"@types/cors","version":"^2.8.5"},{"name":"@types/express","version":"^4.17.13"},{"name":"@types/express-serve-static-core","version":"^4.17.13"},{"name":"@types/node","version":"^18.0.0"},{"name":"nodemon","version":"^2.0.19"},{"name":"rollup","version":"^4.9.6"},{"name":"rollup-plugin-esbuild","version":"^6.1.1"}]}}} \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/web-app/package.json b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/web-app/package.json index 49ba99276b..8d93f8e94c 100644 --- a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/web-app/package.json +++ b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/web-app/package.json @@ -13,9 +13,7 @@ }, "dependencies": { "@tanstack/react-query": "^4.29.0", - "axios": "^1.4.0", - "react-dom": "^18.2.0", - "react-router-dom": "^6.26.2" + "axios": "^1.4.0" }, "devDependencies": { "@tsconfig/vite-react": "^2.0.0", diff --git a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/package.json b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/package.json index 98da5e7388..dca94a2d85 100644 --- a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/package.json +++ b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/package.json @@ -1,6 +1,8 @@ { "dependencies": { "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-router-dom": "^6.26.2", "wasp": "file:.wasp/out/sdk/wasp" }, "devDependencies": { diff --git a/waspc/e2e-test/test-outputs/waspNew-golden/waspNew/package.json b/waspc/e2e-test/test-outputs/waspNew-golden/waspNew/package.json index 5a4bb9c6ee..d2780893f0 100644 --- a/waspc/e2e-test/test-outputs/waspNew-golden/waspNew/package.json +++ b/waspc/e2e-test/test-outputs/waspNew-golden/waspNew/package.json @@ -1,6 +1,8 @@ { "dependencies": { "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-router-dom": "^6.26.2", "wasp": "file:.wasp/out/sdk/wasp" }, "devDependencies": { diff --git a/waspc/examples/crud-testing/package-lock.json b/waspc/examples/crud-testing/package-lock.json index 7ceed1aefe..531ba5607e 100644 --- a/waspc/examples/crud-testing/package-lock.json +++ b/waspc/examples/crud-testing/package-lock.json @@ -7,6 +7,8 @@ "name": "crudTesting", "dependencies": { "react": "^18.2.0", + "react-dom": "18.3.1", + "react-router-dom": "6.28.1", "wasp": "file:.wasp/out/sdk/wasp", "zod": "^3.22.2" }, @@ -1557,9 +1559,9 @@ } }, "node_modules/@remix-run/router": { - "version": "1.19.2", - "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.19.2.tgz", - "integrity": "sha512-baiMx18+IMuD1yyvOGaHM9QrVUPGGG0jC+z+IPHnRJWUAUvaKuWKyE8gjDj2rzv3sz9zOGoRSPgeBVHRhZnBlA==", + "version": "1.21.0", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.21.0.tgz", + "integrity": "sha512-xfSkCAchbdG5PnbrKqFWwia4Bi61nH+wm8wLEqfHDyp7Y3dZzgqS2itV8i4gAq9pC2HsTpwyBC6Ds8VHZ96JlA==", "engines": { "node": ">=14.0.0" } @@ -5655,9 +5657,9 @@ } }, "node_modules/react": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", - "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", + "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", "dependencies": { "loose-envify": "^1.1.0" }, @@ -5666,16 +5668,15 @@ } }, "node_modules/react-dom": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", - "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", - "peer": true, + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", + "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", "dependencies": { "loose-envify": "^1.1.0", - "scheduler": "^0.23.0" + "scheduler": "^0.23.2" }, "peerDependencies": { - "react": "^18.2.0" + "react": "^18.3.1" } }, "node_modules/react-hook-form": { @@ -5699,11 +5700,11 @@ "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" }, "node_modules/react-router": { - "version": "6.26.2", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.26.2.tgz", - "integrity": "sha512-tvN1iuT03kHgOFnLPfLJ8V95eijteveqdOSk+srqfePtQvqCExB8eHOYnlilbOcyJyKnYkr1vJvf7YqotAJu1A==", + "version": "6.28.1", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.28.1.tgz", + "integrity": "sha512-2omQTA3rkMljmrvvo6WtewGdVh45SpL9hGiCI9uUrwGGfNFDIvGK4gYJsKlJoNVi6AQZcopSCballL+QGOm7fA==", "dependencies": { - "@remix-run/router": "1.19.2" + "@remix-run/router": "1.21.0" }, "engines": { "node": ">=14.0.0" @@ -5713,12 +5714,12 @@ } }, "node_modules/react-router-dom": { - "version": "6.26.2", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.26.2.tgz", - "integrity": "sha512-z7YkaEW0Dy35T3/QKPYB1LjMK2R1fxnHO8kWpUMTBdfVzZrWOiY9a7CtN8HqdWtDUWd5FY6Dl8HFsqVwH4uOtQ==", + "version": "6.28.1", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.28.1.tgz", + "integrity": "sha512-YraE27C/RdjcZwl5UCqF/ffXnZDxpJdk9Q6jw38SZHjXs7NNdpViq2l2c7fO7+4uWaEfcwfGCv3RSg4e1By/fQ==", "dependencies": { - "@remix-run/router": "1.19.2", - "react-router": "6.26.2" + "@remix-run/router": "1.21.0", + "react-router": "6.28.1" }, "engines": { "node": ">=14.0.0" @@ -5915,10 +5916,9 @@ } }, "node_modules/scheduler": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", - "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", - "peer": true, + "version": "0.23.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", + "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", "dependencies": { "loose-envify": "^1.1.0" } diff --git a/waspc/examples/crud-testing/package.json b/waspc/examples/crud-testing/package.json index a0398de0e2..1fe73528d6 100644 --- a/waspc/examples/crud-testing/package.json +++ b/waspc/examples/crud-testing/package.json @@ -1,14 +1,16 @@ { "name": "crudTesting", "dependencies": { - "zod": "^3.22.2", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-router-dom": "^6.26.2", "wasp": "file:.wasp/out/sdk/wasp", - "react": "^18.2.0" + "zod": "^3.22.2" }, "devDependencies": { - "typescript": "^5.1.0", - "vite": "^4.3.9", "@types/react": "^18.0.37", - "prisma": "5.19.1" + "prisma": "5.19.1", + "typescript": "^5.1.0", + "vite": "^4.3.9" } } diff --git a/waspc/examples/pg-vector-example/package-lock.json b/waspc/examples/pg-vector-example/package-lock.json index e38fa540c9..4d5d0e962c 100644 --- a/waspc/examples/pg-vector-example/package-lock.json +++ b/waspc/examples/pg-vector-example/package-lock.json @@ -13,8 +13,10 @@ "openai": "^4.5.0", "pgvector": "0.1.5", "react": "^18.2.0", + "react-dom": "^18.2.0", "react-hook-form": "^7.45.4", "react-markdown": "^8.0.7", + "react-router-dom": "^6.26.2", "wasp": "file:.wasp/out/sdk/wasp" }, "devDependencies": { @@ -8351,7 +8353,6 @@ "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", - "peer": true, "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.0" @@ -8782,7 +8783,6 @@ "version": "0.23.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", - "peer": true, "dependencies": { "loose-envify": "^1.1.0" } diff --git a/waspc/examples/pg-vector-example/package.json b/waspc/examples/pg-vector-example/package.json index 90b465decf..76b8f04575 100644 --- a/waspc/examples/pg-vector-example/package.json +++ b/waspc/examples/pg-vector-example/package.json @@ -1,21 +1,23 @@ { "name": "pgVectorExample", "dependencies": { - "openai": "^4.5.0", - "react-hook-form": "^7.45.4", "@nextui-org/react": "^2.1.10", + "axios": "^1.4.0", + "cheerio": "1.0.0-rc.12", "framer-motion": "^10.16.4", + "openai": "^4.5.0", "pgvector": "0.1.5", - "cheerio": "1.0.0-rc.12", - "axios": "^1.4.0", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-hook-form": "^7.45.4", "react-markdown": "^8.0.7", - "wasp": "file:.wasp/out/sdk/wasp", - "react": "^18.2.0" + "react-router-dom": "^6.26.2", + "wasp": "file:.wasp/out/sdk/wasp" }, "devDependencies": { - "typescript": "^5.1.0", - "vite": "^4.3.9", "@types/react": "^18.0.37", - "prisma": "5.19.1" + "prisma": "5.19.1", + "typescript": "^5.1.0", + "vite": "^4.3.9" } } diff --git a/waspc/examples/todo-typescript/package-lock.json b/waspc/examples/todo-typescript/package-lock.json index 37025893a0..1d106626ae 100644 --- a/waspc/examples/todo-typescript/package-lock.json +++ b/waspc/examples/todo-typescript/package-lock.json @@ -8,6 +8,8 @@ "dependencies": { "cors": "^2.8.5", "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-router-dom": "^6.26.2", "wasp": "file:.wasp/out/sdk/wasp" }, "devDependencies": { @@ -6489,7 +6491,6 @@ "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", - "peer": true, "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.0" @@ -6762,7 +6763,6 @@ "version": "0.23.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", - "peer": true, "dependencies": { "loose-envify": "^1.1.0" } diff --git a/waspc/examples/todo-typescript/package.json b/waspc/examples/todo-typescript/package.json index c27ee94d16..09bfcd5d2c 100644 --- a/waspc/examples/todo-typescript/package.json +++ b/waspc/examples/todo-typescript/package.json @@ -3,6 +3,8 @@ "dependencies": { "cors": "^2.8.5", "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-router-dom": "^6.26.2", "wasp": "file:.wasp/out/sdk/wasp" }, "devDependencies": { diff --git a/waspc/examples/todoApp/package-lock.json b/waspc/examples/todoApp/package-lock.json index 63d0da935f..0e940179a5 100644 --- a/waspc/examples/todoApp/package-lock.json +++ b/waspc/examples/todoApp/package-lock.json @@ -9,6 +9,8 @@ "@tailwindcss/forms": "^0.5.3", "@tailwindcss/typography": "^0.5.7", "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-router-dom": "^6.26.2", "wasp": "file:.wasp/out/sdk/wasp" }, "devDependencies": { @@ -7067,7 +7069,6 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", "license": "MIT", - "peer": true, "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.2" @@ -7370,7 +7371,6 @@ "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", "license": "MIT", - "peer": true, "dependencies": { "loose-envify": "^1.1.0" } diff --git a/waspc/examples/todoApp/package.json b/waspc/examples/todoApp/package.json index f0b36b3d0d..df4c8fc162 100644 --- a/waspc/examples/todoApp/package.json +++ b/waspc/examples/todoApp/package.json @@ -9,6 +9,8 @@ "@tailwindcss/forms": "^0.5.3", "@tailwindcss/typography": "^0.5.7", "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-router-dom": "^6.26.2", "wasp": "file:.wasp/out/sdk/wasp" }, "devDependencies": { diff --git a/waspc/headless-test/examples/todoApp/package-lock.json b/waspc/headless-test/examples/todoApp/package-lock.json index 75a03b5861..482b8a1e19 100644 --- a/waspc/headless-test/examples/todoApp/package-lock.json +++ b/waspc/headless-test/examples/todoApp/package-lock.json @@ -9,6 +9,8 @@ "@tailwindcss/forms": "^0.5.3", "@tailwindcss/typography": "^0.5.7", "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-router-dom": "^6.26.2", "wasp": "file:.wasp/out/sdk/wasp" }, "devDependencies": { @@ -2120,9 +2122,9 @@ } }, "node_modules/@remix-run/router": { - "version": "1.20.0", - "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.20.0.tgz", - "integrity": "sha512-mUnk8rPJBI9loFDZ+YzPGdeniYK+FTmRD1TMCz7ev2SNIozyKKpnGgsxO34u6Z4z/t0ITuu7voi/AshfsGsgFg==", + "version": "1.19.2", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.19.2.tgz", + "integrity": "sha512-baiMx18+IMuD1yyvOGaHM9QrVUPGGG0jC+z+IPHnRJWUAUvaKuWKyE8gjDj2rzv3sz9zOGoRSPgeBVHRhZnBlA==", "engines": { "node": ">=14.0.0" } @@ -7062,7 +7064,6 @@ "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", - "peer": true, "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.0" @@ -7092,11 +7093,11 @@ "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" }, "node_modules/react-router": { - "version": "6.27.0", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.27.0.tgz", - "integrity": "sha512-YA+HGZXz4jaAkVoYBE98VQl+nVzI+cVI2Oj/06F5ZM+0u3TgedN9Y9kmMRo2mnkSK2nCpNQn0DVob4HCsY/WLw==", + "version": "6.26.2", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.26.2.tgz", + "integrity": "sha512-tvN1iuT03kHgOFnLPfLJ8V95eijteveqdOSk+srqfePtQvqCExB8eHOYnlilbOcyJyKnYkr1vJvf7YqotAJu1A==", "dependencies": { - "@remix-run/router": "1.20.0" + "@remix-run/router": "1.19.2" }, "engines": { "node": ">=14.0.0" @@ -7106,12 +7107,12 @@ } }, "node_modules/react-router-dom": { - "version": "6.27.0", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.27.0.tgz", - "integrity": "sha512-+bvtFWMC0DgAFrfKXKG9Fc+BcXWRUO1aJIihbB79xaeq0v5UzfvnM5houGUm1Y461WVRcgAQ+Clh5rdb1eCx4g==", + "version": "6.26.2", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.26.2.tgz", + "integrity": "sha512-z7YkaEW0Dy35T3/QKPYB1LjMK2R1fxnHO8kWpUMTBdfVzZrWOiY9a7CtN8HqdWtDUWd5FY6Dl8HFsqVwH4uOtQ==", "dependencies": { - "@remix-run/router": "1.20.0", - "react-router": "6.27.0" + "@remix-run/router": "1.19.2", + "react-router": "6.26.2" }, "engines": { "node": ">=14.0.0" @@ -7340,7 +7341,6 @@ "version": "0.23.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", - "peer": true, "dependencies": { "loose-envify": "^1.1.0" } diff --git a/waspc/headless-test/examples/todoApp/package.json b/waspc/headless-test/examples/todoApp/package.json index fcca99924d..15751e4481 100644 --- a/waspc/headless-test/examples/todoApp/package.json +++ b/waspc/headless-test/examples/todoApp/package.json @@ -1,10 +1,12 @@ { "name": "todo-app", "dependencies": { - "react": "^18.2.0", - "wasp": "file:.wasp/out/sdk/wasp", "@tailwindcss/forms": "^0.5.3", - "@tailwindcss/typography": "^0.5.7" + "@tailwindcss/typography": "^0.5.7", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-router-dom": "^6.26.2", + "wasp": "file:.wasp/out/sdk/wasp" }, "devDependencies": { "@types/cors": "^2.8.5", diff --git a/waspc/src/Wasp/Generator/ExternalConfig/PackageJson.hs b/waspc/src/Wasp/Generator/ExternalConfig/PackageJson.hs index c8e8952443..f435502614 100644 --- a/waspc/src/Wasp/Generator/ExternalConfig/PackageJson.hs +++ b/waspc/src/Wasp/Generator/ExternalConfig/PackageJson.hs @@ -7,7 +7,7 @@ import qualified Data.Map as M import qualified Wasp.ExternalConfig.PackageJson as P import Wasp.Generator.Common (prismaVersion) import Wasp.Generator.ExternalConfig.Common (ErrorMsg) -import Wasp.Generator.WebAppGenerator.Common (reactRouterVersion) +import Wasp.Generator.WebAppGenerator.Common (reactRouterVersion, reactVersion) validatePackageJson :: P.PackageJson -> [ErrorMsg] validatePackageJson packageJson = @@ -16,7 +16,9 @@ validatePackageJson packageJson = validate ("prisma", show prismaVersion) IsListedAsDevWithExactVersion, -- Installing the wrong version of "react-router-dom" can make users believe that they -- can use features that are not available in the version that Wasp supports. - validate ("react-router-dom", show reactRouterVersion) HasExactVersionIfListed + validate ("react-router-dom", show reactRouterVersion) IsListedWithExactVersion, + validate ("react", show reactVersion) IsListedWithExactVersion, + validate ("react-dom", show reactVersion) IsListedWithExactVersion ] where validate = validateDep packageJson From ba85650f765ffcc6cf93abfb6226e6af1a17ea64 Mon Sep 17 00:00:00 2001 From: Mihovil Ilakovac Date: Wed, 15 Jan 2025 16:10:29 +0100 Subject: [PATCH 12/22] Validate env vars with Zod (#2362) --- waspc/data/Generator/templates/Dockerfile | 8 +- .../{react-app => }/prettier.config.js | 0 .../templates/react-app/package.json | 5 +- .../react-app/scripts/validate-env.mjs | 4 - .../components/DefaultRootErrorBoundary.tsx | 5 +- .../templates/sdk/wasp/client/config.ts | 4 +- .../templates/sdk/wasp/client/env.ts | 25 + .../templates/sdk/wasp/client/index.ts | 5 +- .../Generator/templates/sdk/wasp/env/index.ts | 11 + .../templates/sdk/wasp/env/validation.ts | 25 + .../Generator/templates/sdk/wasp/package.json | 1 + .../templates/sdk/wasp/server/HttpError.ts | 11 +- .../sdk/wasp/server/auth/oauth/env.ts | 15 - .../sdk/wasp/server/auth/oauth/provider.ts | 6 +- .../server/auth/oauth/providers/discord.ts | 18 +- .../server/auth/oauth/providers/github.ts | 16 +- .../server/auth/oauth/providers/google.ts | 18 +- .../server/auth/oauth/providers/keycloak.ts | 18 +- .../templates/sdk/wasp/server/config.ts | 111 +- .../sdk/wasp/server/email/core/index.ts | 16 +- .../templates/sdk/wasp/server/email/index.ts | 35 +- .../templates/sdk/wasp/server/env.ts | 157 + .../templates/sdk/wasp/server/index.ts | 2 + .../wasp/server/jobs/core/pgBoss/pgBoss.ts | 6 +- .../templates/sdk/wasp/universal/url.ts | 2 + .../Generator/templates/server/package.json | 3 +- .../templates/server/scripts/validate-env.mjs | 5 - .../server/src/auth/providers/config/email.ts | 3 +- .../src/auth/providers/config/keycloak.ts | 3 +- .../src/auth/providers/oauth/cookies.ts | 10 +- .../templates/server/src/polyfill.ts | 7 +- .../waspBuild-golden/files.manifest | 34 +- .../waspBuild/.wasp/build/.waspchecksums | 66 +- .../waspBuild/.wasp/build/Dockerfile | 8 +- .../.wasp/build/installedNpmDepsLog.json | 2 +- .../.wasp/build/sdk/wasp/client/config.ts | 3 +- .../.wasp/build/sdk/wasp/client/env.ts | 18 + .../.wasp/build/sdk/wasp/client/index.ts | 5 +- .../build/sdk/wasp/dist/client/config.js | 3 +- .../build/sdk/wasp/dist/client/config.js.map | 2 +- .../.wasp/build/sdk/wasp/dist/client/env.d.ts | 3 + .../.wasp/build/sdk/wasp/dist/client/env.js | 14 + .../build/sdk/wasp/dist/client/env.js.map | 1 + .../build/sdk/wasp/dist/client/index.d.ts | 3 +- .../.wasp/build/sdk/wasp/dist/client/index.js | 4 +- .../build/sdk/wasp/dist/client/index.js.map | 2 +- .../.wasp/build/sdk/wasp/dist/env/index.d.ts | 3 + .../.wasp/build/sdk/wasp/dist/env/index.js | 7 + .../build/sdk/wasp/dist/env/index.js.map | 1 + .../build/sdk/wasp/dist/env/validation.d.ts | 2 + .../build/sdk/wasp/dist/env/validation.js | 23 + .../build/sdk/wasp/dist/env/validation.js.map | 1 + .../sdk/wasp/dist/server/HttpError.js.map | 2 +- .../build/sdk/wasp/dist/server/config.d.ts | 19 +- .../build/sdk/wasp/dist/server/config.js | 56 +- .../build/sdk/wasp/dist/server/config.js.map | 2 +- .../.wasp/build/sdk/wasp/dist/server/env.d.ts | 17 + .../.wasp/build/sdk/wasp/dist/server/env.js | 55 + .../build/sdk/wasp/dist/server/env.js.map | 1 + .../build/sdk/wasp/dist/server/index.d.ts | 1 + .../.wasp/build/sdk/wasp/dist/server/index.js | 2 + .../build/sdk/wasp/dist/server/index.js.map | 2 +- .../build/sdk/wasp/dist/universal/url.d.ts | 3 +- .../build/sdk/wasp/dist/universal/url.js.map | 2 +- .../.wasp/build/sdk/wasp/env/index.ts | 11 + .../.wasp/build/sdk/wasp/env/validation.ts | 25 + .../.wasp/build/sdk/wasp/package.json | 5 +- .../.wasp/build/sdk/wasp/server/HttpError.ts | 11 +- .../.wasp/build/sdk/wasp/server/config.ts | 85 +- .../.wasp/build/sdk/wasp/server/env.ts | 65 + .../.wasp/build/sdk/wasp/server/index.ts | 2 + .../.wasp/build/sdk/wasp/universal/url.ts | 2 + .../waspBuild/.wasp/build/server/package.json | 3 +- .../build/server/scripts/validate-env.mjs | 5 - .../.wasp/build/server/src/polyfill.ts | 7 +- .../.wasp/build/web-app/package.json | 8 +- .../build/web-app/scripts/validate-env.mjs | 4 - .../components/DefaultRootErrorBoundary.tsx | 5 +- .../.wasp/out/sdk/wasp/client/config.ts | 3 +- .../.wasp/out/sdk/wasp/client/env.ts | 18 + .../.wasp/out/sdk/wasp/client/index.ts | 5 +- .../.wasp/out/sdk/wasp/dist/client/config.js | 3 +- .../out/sdk/wasp/dist/client/config.js.map | 2 +- .../.wasp/out/sdk/wasp/dist/client/env.d.ts | 3 + .../.wasp/out/sdk/wasp/dist/client/env.js | 14 + .../.wasp/out/sdk/wasp/dist/client/env.js.map | 1 + .../.wasp/out/sdk/wasp/dist/client/index.d.ts | 3 +- .../.wasp/out/sdk/wasp/dist/client/index.js | 4 +- .../out/sdk/wasp/dist/client/index.js.map | 2 +- .../.wasp/out/sdk/wasp/dist/env/index.d.ts | 3 + .../.wasp/out/sdk/wasp/dist/env/index.js | 7 + .../.wasp/out/sdk/wasp/dist/env/index.js.map | 1 + .../out/sdk/wasp/dist/env/validation.d.ts | 2 + .../.wasp/out/sdk/wasp/dist/env/validation.js | 23 + .../out/sdk/wasp/dist/env/validation.js.map | 1 + .../out/sdk/wasp/dist/server/HttpError.js.map | 2 +- .../out/sdk/wasp/dist/server/config.d.ts | 19 +- .../.wasp/out/sdk/wasp/dist/server/config.js | 56 +- .../out/sdk/wasp/dist/server/config.js.map | 2 +- .../.wasp/out/sdk/wasp/dist/server/env.d.ts | 17 + .../.wasp/out/sdk/wasp/dist/server/env.js | 55 + .../.wasp/out/sdk/wasp/dist/server/env.js.map | 1 + .../.wasp/out/sdk/wasp/dist/server/index.d.ts | 1 + .../.wasp/out/sdk/wasp/dist/server/index.js | 2 + .../out/sdk/wasp/dist/server/index.js.map | 2 +- .../out/sdk/wasp/dist/universal/url.d.ts | 3 +- .../out/sdk/wasp/dist/universal/url.js.map | 2 +- .../waspBuild/.wasp/out/sdk/wasp/env/index.ts | 11 + .../.wasp/out/sdk/wasp/env/validation.ts | 25 + .../waspBuild/.wasp/out/sdk/wasp/package.json | 5 +- .../.wasp/out/sdk/wasp/server/HttpError.ts | 11 +- .../.wasp/out/sdk/wasp/server/config.ts | 85 +- .../.wasp/out/sdk/wasp/server/env.ts | 65 + .../.wasp/out/sdk/wasp/server/index.ts | 2 + .../.wasp/out/sdk/wasp/universal/url.ts | 2 + .../waspCompile-golden/files.manifest | 18 +- .../waspCompile/.wasp/out/.waspchecksums | 66 +- .../waspCompile/.wasp/out/Dockerfile | 8 +- .../.wasp/out/installedNpmDepsLog.json | 2 +- .../.wasp/out/sdk/wasp/client/config.ts | 3 +- .../.wasp/out/sdk/wasp/client/env.ts | 18 + .../.wasp/out/sdk/wasp/client/index.ts | 5 +- .../.wasp/out/sdk/wasp/dist/client/config.js | 3 +- .../out/sdk/wasp/dist/client/config.js.map | 2 +- .../.wasp/out/sdk/wasp/dist/client/env.d.ts | 3 + .../.wasp/out/sdk/wasp/dist/client/env.js | 14 + .../.wasp/out/sdk/wasp/dist/client/env.js.map | 1 + .../.wasp/out/sdk/wasp/dist/client/index.d.ts | 3 +- .../.wasp/out/sdk/wasp/dist/client/index.js | 4 +- .../out/sdk/wasp/dist/client/index.js.map | 2 +- .../.wasp/out/sdk/wasp/dist/env/index.d.ts | 3 + .../.wasp/out/sdk/wasp/dist/env/index.js | 7 + .../.wasp/out/sdk/wasp/dist/env/index.js.map | 1 + .../out/sdk/wasp/dist/env/validation.d.ts | 2 + .../.wasp/out/sdk/wasp/dist/env/validation.js | 23 + .../out/sdk/wasp/dist/env/validation.js.map | 1 + .../out/sdk/wasp/dist/server/HttpError.js.map | 2 +- .../out/sdk/wasp/dist/server/config.d.ts | 19 +- .../.wasp/out/sdk/wasp/dist/server/config.js | 56 +- .../out/sdk/wasp/dist/server/config.js.map | 2 +- .../.wasp/out/sdk/wasp/dist/server/env.d.ts | 17 + .../.wasp/out/sdk/wasp/dist/server/env.js | 55 + .../.wasp/out/sdk/wasp/dist/server/env.js.map | 1 + .../.wasp/out/sdk/wasp/dist/server/index.d.ts | 1 + .../.wasp/out/sdk/wasp/dist/server/index.js | 2 + .../out/sdk/wasp/dist/server/index.js.map | 2 +- .../out/sdk/wasp/dist/universal/url.d.ts | 3 +- .../out/sdk/wasp/dist/universal/url.js.map | 2 +- .../.wasp/out/sdk/wasp/env/index.ts | 11 + .../.wasp/out/sdk/wasp/env/validation.ts | 25 + .../.wasp/out/sdk/wasp/package.json | 5 +- .../.wasp/out/sdk/wasp/server/HttpError.ts | 11 +- .../.wasp/out/sdk/wasp/server/config.ts | 85 +- .../.wasp/out/sdk/wasp/server/env.ts | 65 + .../.wasp/out/sdk/wasp/server/index.ts | 2 + .../.wasp/out/sdk/wasp/universal/url.ts | 2 + .../waspCompile/.wasp/out/server/package.json | 3 +- .../.wasp/out/server/scripts/validate-env.mjs | 5 - .../.wasp/out/server/src/polyfill.ts | 7 +- .../.wasp/out/web-app/package.json | 8 +- .../out/web-app/scripts/validate-env.mjs | 4 - .../components/DefaultRootErrorBoundary.tsx | 5 +- .../waspComplexTest-golden/files.manifest | 22 +- .../waspComplexTest/.wasp/out/.waspchecksums | 83 +- .../waspComplexTest/.wasp/out/Dockerfile | 8 +- .../.wasp/out/installedNpmDepsLog.json | 2 +- .../.wasp/out/sdk/wasp/client/config.ts | 3 +- .../.wasp/out/sdk/wasp/client/env.ts | 18 + .../.wasp/out/sdk/wasp/client/index.ts | 5 +- .../.wasp/out/sdk/wasp/dist/client/config.js | 3 +- .../out/sdk/wasp/dist/client/config.js.map | 2 +- .../.wasp/out/sdk/wasp/dist/client/env.d.ts | 3 + .../.wasp/out/sdk/wasp/dist/client/env.js | 14 + .../.wasp/out/sdk/wasp/dist/client/env.js.map | 1 + .../.wasp/out/sdk/wasp/dist/client/index.d.ts | 3 +- .../.wasp/out/sdk/wasp/dist/client/index.js | 4 +- .../out/sdk/wasp/dist/client/index.js.map | 2 +- .../.wasp/out/sdk/wasp/dist/env/index.d.ts | 3 + .../.wasp/out/sdk/wasp/dist/env/index.js | 7 + .../.wasp/out/sdk/wasp/dist/env/index.js.map | 1 + .../out/sdk/wasp/dist/env/validation.d.ts | 2 + .../.wasp/out/sdk/wasp/dist/env/validation.js | 23 + .../out/sdk/wasp/dist/env/validation.js.map | 1 + .../out/sdk/wasp/dist/server/HttpError.js.map | 2 +- .../sdk/wasp/dist/server/auth/oauth/env.d.ts | 1 - .../sdk/wasp/dist/server/auth/oauth/env.js | 13 - .../wasp/dist/server/auth/oauth/env.js.map | 1 - .../wasp/dist/server/auth/oauth/provider.d.ts | 4 +- .../wasp/dist/server/auth/oauth/provider.js | 3 +- .../dist/server/auth/oauth/provider.js.map | 2 +- .../server/auth/oauth/providers/google.d.ts | 3 +- .../server/auth/oauth/providers/google.js | 14 +- .../server/auth/oauth/providers/google.js.map | 2 +- .../out/sdk/wasp/dist/server/config.d.ts | 25 +- .../.wasp/out/sdk/wasp/dist/server/config.js | 68 +- .../out/sdk/wasp/dist/server/config.js.map | 2 +- .../out/sdk/wasp/dist/server/email/index.js | 5 +- .../sdk/wasp/dist/server/email/index.js.map | 2 +- .../.wasp/out/sdk/wasp/dist/server/env.d.ts | 25 + .../.wasp/out/sdk/wasp/dist/server/env.js | 71 + .../.wasp/out/sdk/wasp/dist/server/env.js.map | 1 + .../.wasp/out/sdk/wasp/dist/server/index.d.ts | 1 + .../.wasp/out/sdk/wasp/dist/server/index.js | 2 + .../out/sdk/wasp/dist/server/index.js.map | 2 +- .../dist/server/jobs/core/pgBoss/pgBoss.js | 6 +- .../server/jobs/core/pgBoss/pgBoss.js.map | 2 +- .../out/sdk/wasp/dist/universal/url.d.ts | 3 +- .../out/sdk/wasp/dist/universal/url.js.map | 2 +- .../.wasp/out/sdk/wasp/env/index.ts | 11 + .../.wasp/out/sdk/wasp/env/validation.ts | 25 + .../.wasp/out/sdk/wasp/package.json | 5 +- .../.wasp/out/sdk/wasp/server/HttpError.ts | 11 +- .../out/sdk/wasp/server/auth/oauth/env.ts | 15 - .../sdk/wasp/server/auth/oauth/provider.ts | 6 +- .../server/auth/oauth/providers/google.ts | 18 +- .../.wasp/out/sdk/wasp/server/config.ts | 101 +- .../.wasp/out/sdk/wasp/server/email/index.ts | 5 +- .../.wasp/out/sdk/wasp/server/env.ts | 81 + .../.wasp/out/sdk/wasp/server/index.ts | 2 + .../wasp/server/jobs/core/pgBoss/pgBoss.ts | 6 +- .../.wasp/out/sdk/wasp/universal/url.ts | 2 + .../.wasp/out/server/package.json | 3 +- .../.wasp/out/server/scripts/validate-env.mjs | 5 - .../src/auth/providers/oauth/cookies.ts | 10 +- .../.wasp/out/server/src/polyfill.ts | 7 +- .../.wasp/out/web-app/package.json | 8 +- .../out/web-app/scripts/validate-env.mjs | 4 - .../components/DefaultRootErrorBoundary.tsx | 5 +- .../waspJob-golden/files.manifest | 18 +- .../waspJob/.wasp/out/.waspchecksums | 68 +- .../waspJob/.wasp/out/Dockerfile | 8 +- .../.wasp/out/installedNpmDepsLog.json | 2 +- .../.wasp/out/sdk/wasp/client/config.ts | 3 +- .../waspJob/.wasp/out/sdk/wasp/client/env.ts | 18 + .../.wasp/out/sdk/wasp/client/index.ts | 5 +- .../.wasp/out/sdk/wasp/dist/client/config.js | 3 +- .../out/sdk/wasp/dist/client/config.js.map | 2 +- .../.wasp/out/sdk/wasp/dist/client/env.d.ts | 3 + .../.wasp/out/sdk/wasp/dist/client/env.js | 14 + .../.wasp/out/sdk/wasp/dist/client/env.js.map | 1 + .../.wasp/out/sdk/wasp/dist/client/index.d.ts | 3 +- .../.wasp/out/sdk/wasp/dist/client/index.js | 4 +- .../out/sdk/wasp/dist/client/index.js.map | 2 +- .../.wasp/out/sdk/wasp/dist/env/index.d.ts | 3 + .../.wasp/out/sdk/wasp/dist/env/index.js | 7 + .../.wasp/out/sdk/wasp/dist/env/index.js.map | 1 + .../out/sdk/wasp/dist/env/validation.d.ts | 2 + .../.wasp/out/sdk/wasp/dist/env/validation.js | 23 + .../out/sdk/wasp/dist/env/validation.js.map | 1 + .../out/sdk/wasp/dist/server/HttpError.js.map | 2 +- .../out/sdk/wasp/dist/server/config.d.ts | 19 +- .../.wasp/out/sdk/wasp/dist/server/config.js | 56 +- .../out/sdk/wasp/dist/server/config.js.map | 2 +- .../.wasp/out/sdk/wasp/dist/server/env.d.ts | 17 + .../.wasp/out/sdk/wasp/dist/server/env.js | 55 + .../.wasp/out/sdk/wasp/dist/server/env.js.map | 1 + .../.wasp/out/sdk/wasp/dist/server/index.d.ts | 1 + .../.wasp/out/sdk/wasp/dist/server/index.js | 2 + .../out/sdk/wasp/dist/server/index.js.map | 2 +- .../dist/server/jobs/core/pgBoss/pgBoss.js | 6 +- .../server/jobs/core/pgBoss/pgBoss.js.map | 2 +- .../out/sdk/wasp/dist/universal/url.d.ts | 3 +- .../out/sdk/wasp/dist/universal/url.js.map | 2 +- .../waspJob/.wasp/out/sdk/wasp/env/index.ts | 11 + .../.wasp/out/sdk/wasp/env/validation.ts | 25 + .../waspJob/.wasp/out/sdk/wasp/package.json | 5 +- .../.wasp/out/sdk/wasp/server/HttpError.ts | 11 +- .../.wasp/out/sdk/wasp/server/config.ts | 85 +- .../waspJob/.wasp/out/sdk/wasp/server/env.ts | 65 + .../.wasp/out/sdk/wasp/server/index.ts | 2 + .../wasp/server/jobs/core/pgBoss/pgBoss.ts | 6 +- .../.wasp/out/sdk/wasp/universal/url.ts | 2 + .../waspJob/.wasp/out/server/package.json | 3 +- .../.wasp/out/server/scripts/validate-env.mjs | 5 - .../waspJob/.wasp/out/server/src/polyfill.ts | 7 +- .../waspJob/.wasp/out/web-app/package.json | 8 +- .../out/web-app/scripts/validate-env.mjs | 4 - .../components/DefaultRootErrorBoundary.tsx | 5 +- .../waspMigrate-golden/files.manifest | 18 +- .../waspMigrate/.wasp/out/.waspchecksums | 66 +- .../waspMigrate/.wasp/out/Dockerfile | 8 +- .../.wasp/out/installedNpmDepsLog.json | 2 +- .../.wasp/out/sdk/wasp/client/config.ts | 3 +- .../.wasp/out/sdk/wasp/client/env.ts | 18 + .../.wasp/out/sdk/wasp/client/index.ts | 5 +- .../.wasp/out/sdk/wasp/dist/client/config.js | 3 +- .../out/sdk/wasp/dist/client/config.js.map | 2 +- .../.wasp/out/sdk/wasp/dist/client/env.d.ts | 3 + .../.wasp/out/sdk/wasp/dist/client/env.js | 14 + .../.wasp/out/sdk/wasp/dist/client/env.js.map | 1 + .../.wasp/out/sdk/wasp/dist/client/index.d.ts | 3 +- .../.wasp/out/sdk/wasp/dist/client/index.js | 4 +- .../out/sdk/wasp/dist/client/index.js.map | 2 +- .../.wasp/out/sdk/wasp/dist/env/index.d.ts | 3 + .../.wasp/out/sdk/wasp/dist/env/index.js | 7 + .../.wasp/out/sdk/wasp/dist/env/index.js.map | 1 + .../out/sdk/wasp/dist/env/validation.d.ts | 2 + .../.wasp/out/sdk/wasp/dist/env/validation.js | 23 + .../out/sdk/wasp/dist/env/validation.js.map | 1 + .../out/sdk/wasp/dist/server/HttpError.js.map | 2 +- .../out/sdk/wasp/dist/server/config.d.ts | 19 +- .../.wasp/out/sdk/wasp/dist/server/config.js | 56 +- .../out/sdk/wasp/dist/server/config.js.map | 2 +- .../.wasp/out/sdk/wasp/dist/server/env.d.ts | 17 + .../.wasp/out/sdk/wasp/dist/server/env.js | 55 + .../.wasp/out/sdk/wasp/dist/server/env.js.map | 1 + .../.wasp/out/sdk/wasp/dist/server/index.d.ts | 1 + .../.wasp/out/sdk/wasp/dist/server/index.js | 2 + .../out/sdk/wasp/dist/server/index.js.map | 2 +- .../out/sdk/wasp/dist/universal/url.d.ts | 3 +- .../out/sdk/wasp/dist/universal/url.js.map | 2 +- .../.wasp/out/sdk/wasp/env/index.ts | 11 + .../.wasp/out/sdk/wasp/env/validation.ts | 25 + .../.wasp/out/sdk/wasp/package.json | 5 +- .../.wasp/out/sdk/wasp/server/HttpError.ts | 11 +- .../.wasp/out/sdk/wasp/server/config.ts | 85 +- .../.wasp/out/sdk/wasp/server/env.ts | 65 + .../.wasp/out/sdk/wasp/server/index.ts | 2 + .../.wasp/out/sdk/wasp/universal/url.ts | 2 + .../waspMigrate/.wasp/out/server/package.json | 3 +- .../.wasp/out/server/scripts/validate-env.mjs | 5 - .../.wasp/out/server/src/polyfill.ts | 7 +- .../.wasp/out/web-app/package.json | 8 +- .../out/web-app/scripts/validate-env.mjs | 4 - .../components/DefaultRootErrorBoundary.tsx | 5 +- waspc/examples/todoApp/.env.server.example | 2 + waspc/examples/todoApp/main.wasp | 6 +- waspc/examples/todoApp/package-lock.json | 12 +- .../todoApp/prettier.config.cjs} | 1 - waspc/examples/todoApp/prettier.config.js | 6 - waspc/examples/todoApp/src/App.tsx | 5 +- waspc/examples/todoApp/src/clientSetup.js | 2 +- waspc/examples/todoApp/src/env.ts | 16 + waspc/examples/todoApp/src/serverSetup.ts | 7 +- .../examples/todoApp/.env.server.example | 4 + .../examples/todoApp/package-lock.json | 4346 +++++++---------- waspc/packages/wasp-config/src/appSpec.ts | 2 + .../src/mapUserSpecToAppSpecDecls.ts | 6 +- waspc/packages/wasp-config/src/userApi.ts | 2 + waspc/src/Wasp/AppSpec/App/Client.hs | 3 +- waspc/src/Wasp/AppSpec/App/Server.hs | 3 +- waspc/src/Wasp/Generator/EmailSenders.hs | 23 + waspc/src/Wasp/Generator/SdkGenerator.hs | 20 +- .../SdkGenerator/EmailSender/Providers.hs | 17 +- .../Generator/SdkGenerator/EnvValidation.hs | 75 + .../SdkGenerator/Server/EmailSenderG.hs | 21 +- .../Generator/SdkGenerator/Server/OAuthG.hs | 1 - waspc/src/Wasp/Generator/ServerGenerator.hs | 7 - waspc/src/Wasp/Generator/WebAppGenerator.hs | 9 - waspc/test/AnalyzerTest.hs | 6 +- waspc/waspc.cabal | 8 +- web/docs/project/env-vars.md | 245 +- 352 files changed, 5145 insertions(+), 3918 deletions(-) rename waspc/data/Generator/templates/{react-app => }/prettier.config.js (100%) delete mode 100644 waspc/data/Generator/templates/react-app/scripts/validate-env.mjs create mode 100644 waspc/data/Generator/templates/sdk/wasp/client/env.ts create mode 100644 waspc/data/Generator/templates/sdk/wasp/env/index.ts create mode 100644 waspc/data/Generator/templates/sdk/wasp/env/validation.ts delete mode 100644 waspc/data/Generator/templates/sdk/wasp/server/auth/oauth/env.ts create mode 100644 waspc/data/Generator/templates/sdk/wasp/server/env.ts delete mode 100644 waspc/data/Generator/templates/server/scripts/validate-env.mjs create mode 100644 waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/client/env.ts create mode 100644 waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/client/env.d.ts create mode 100644 waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/client/env.js create mode 100644 waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/client/env.js.map create mode 100644 waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/env/index.d.ts create mode 100644 waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/env/index.js create mode 100644 waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/env/index.js.map create mode 100644 waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/env/validation.d.ts create mode 100644 waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/env/validation.js create mode 100644 waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/env/validation.js.map create mode 100644 waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/server/env.d.ts create mode 100644 waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/server/env.js create mode 100644 waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/server/env.js.map create mode 100644 waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/env/index.ts create mode 100644 waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/env/validation.ts create mode 100644 waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/server/env.ts delete mode 100644 waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/server/scripts/validate-env.mjs delete mode 100644 waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/web-app/scripts/validate-env.mjs create mode 100644 waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/client/env.ts create mode 100644 waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/client/env.d.ts create mode 100644 waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/client/env.js create mode 100644 waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/client/env.js.map create mode 100644 waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/env/index.d.ts create mode 100644 waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/env/index.js create mode 100644 waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/env/index.js.map create mode 100644 waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/env/validation.d.ts create mode 100644 waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/env/validation.js create mode 100644 waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/env/validation.js.map create mode 100644 waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/server/env.d.ts create mode 100644 waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/server/env.js create mode 100644 waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/server/env.js.map create mode 100644 waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/env/index.ts create mode 100644 waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/env/validation.ts create mode 100644 waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/server/env.ts create mode 100644 waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/client/env.ts create mode 100644 waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/client/env.d.ts create mode 100644 waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/client/env.js create mode 100644 waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/client/env.js.map create mode 100644 waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/env/index.d.ts create mode 100644 waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/env/index.js create mode 100644 waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/env/index.js.map create mode 100644 waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/env/validation.d.ts create mode 100644 waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/env/validation.js create mode 100644 waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/env/validation.js.map create mode 100644 waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/server/env.d.ts create mode 100644 waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/server/env.js create mode 100644 waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/server/env.js.map create mode 100644 waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/env/index.ts create mode 100644 waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/env/validation.ts create mode 100644 waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/server/env.ts delete mode 100644 waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/server/scripts/validate-env.mjs delete mode 100644 waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/web-app/scripts/validate-env.mjs create mode 100644 waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/client/env.ts create mode 100644 waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/client/env.d.ts create mode 100644 waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/client/env.js create mode 100644 waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/client/env.js.map create mode 100644 waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/env/index.d.ts create mode 100644 waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/env/index.js create mode 100644 waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/env/index.js.map create mode 100644 waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/env/validation.d.ts create mode 100644 waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/env/validation.js create mode 100644 waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/env/validation.js.map delete mode 100644 waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/server/auth/oauth/env.d.ts delete mode 100644 waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/server/auth/oauth/env.js delete mode 100644 waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/server/auth/oauth/env.js.map create mode 100644 waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/server/env.d.ts create mode 100644 waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/server/env.js create mode 100644 waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/server/env.js.map create mode 100644 waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/env/index.ts create mode 100644 waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/env/validation.ts delete mode 100644 waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/server/auth/oauth/env.ts create mode 100644 waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/server/env.ts delete mode 100644 waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/server/scripts/validate-env.mjs delete mode 100644 waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/web-app/scripts/validate-env.mjs create mode 100644 waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/client/env.ts create mode 100644 waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/client/env.d.ts create mode 100644 waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/client/env.js create mode 100644 waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/client/env.js.map create mode 100644 waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/env/index.d.ts create mode 100644 waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/env/index.js create mode 100644 waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/env/index.js.map create mode 100644 waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/env/validation.d.ts create mode 100644 waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/env/validation.js create mode 100644 waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/env/validation.js.map create mode 100644 waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/server/env.d.ts create mode 100644 waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/server/env.js create mode 100644 waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/server/env.js.map create mode 100644 waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/env/index.ts create mode 100644 waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/env/validation.ts create mode 100644 waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/server/env.ts delete mode 100644 waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/server/scripts/validate-env.mjs delete mode 100644 waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/web-app/scripts/validate-env.mjs create mode 100644 waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/client/env.ts create mode 100644 waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/client/env.d.ts create mode 100644 waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/client/env.js create mode 100644 waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/client/env.js.map create mode 100644 waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/env/index.d.ts create mode 100644 waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/env/index.js create mode 100644 waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/env/index.js.map create mode 100644 waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/env/validation.d.ts create mode 100644 waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/env/validation.js create mode 100644 waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/env/validation.js.map create mode 100644 waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/server/env.d.ts create mode 100644 waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/server/env.js create mode 100644 waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/server/env.js.map create mode 100644 waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/env/index.ts create mode 100644 waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/env/validation.ts create mode 100644 waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/server/env.ts delete mode 100644 waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/server/scripts/validate-env.mjs delete mode 100644 waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/web-app/scripts/validate-env.mjs rename waspc/{data/Generator/templates/server/prettier.config.js => examples/todoApp/prettier.config.cjs} (56%) delete mode 100644 waspc/examples/todoApp/prettier.config.js create mode 100644 waspc/examples/todoApp/src/env.ts create mode 100644 waspc/src/Wasp/Generator/EmailSenders.hs create mode 100644 waspc/src/Wasp/Generator/SdkGenerator/EnvValidation.hs diff --git a/waspc/data/Generator/templates/Dockerfile b/waspc/data/Generator/templates/Dockerfile index f0713ce6db..f4e271e055 100644 --- a/waspc/data/Generator/templates/Dockerfile +++ b/waspc/data/Generator/templates/Dockerfile @@ -55,15 +55,15 @@ WORKDIR /app # Copying the top level 'node_modules' because it contains the Prisma packages # necessary for migrating the database. COPY --from=server-builder /app/node_modules ./node_modules -# Copying the SDK because 'validate-env.mjs' executes independent of the bundle +# Copying the SDK because the server bundle doesn't bundle the SDK # and references the 'wasp' package. COPY --from=server-builder /app/.wasp/out/sdk .wasp/out/sdk -# Copying 'server/node_modules' because 'validate-env.mjs' executes independent -# of the bundle and references the dotenv package. +# Copying 'server/node_modules' because we require dotenv package to +# load environment variables +# TODO: replace dotenv with native Node.js environment variable loading COPY --from=server-builder /app/.wasp/build/server/node_modules .wasp/build/server/node_modules COPY --from=server-builder /app/.wasp/build/server/bundle .wasp/build/server/bundle COPY --from=server-builder /app/.wasp/build/server/package*.json .wasp/build/server/ -COPY --from=server-builder /app/.wasp/build/server/scripts .wasp/build/server/scripts COPY db/ .wasp/build/db/ EXPOSE ${PORT} WORKDIR /app/.wasp/build/server diff --git a/waspc/data/Generator/templates/react-app/prettier.config.js b/waspc/data/Generator/templates/prettier.config.js similarity index 100% rename from waspc/data/Generator/templates/react-app/prettier.config.js rename to waspc/data/Generator/templates/prettier.config.js diff --git a/waspc/data/Generator/templates/react-app/package.json b/waspc/data/Generator/templates/react-app/package.json index c27f6b4e5a..9d87242f08 100644 --- a/waspc/data/Generator/templates/react-app/package.json +++ b/waspc/data/Generator/templates/react-app/package.json @@ -7,9 +7,8 @@ {=& depsChunk =}, {=& devDepsChunk =}, "scripts": { - "start": "npm run validate-env && vite", - "build": "npm run validate-env && tsc && vite build", - "validate-env": "node -r dotenv/config ./scripts/validate-env.mjs" + "start": "vite", + "build": "tsc && vite build" }, "engineStrict": true, "engines": { diff --git a/waspc/data/Generator/templates/react-app/scripts/validate-env.mjs b/waspc/data/Generator/templates/react-app/scripts/validate-env.mjs deleted file mode 100644 index 18ee507c9e..0000000000 --- a/waspc/data/Generator/templates/react-app/scripts/validate-env.mjs +++ /dev/null @@ -1,4 +0,0 @@ -import { throwIfNotValidAbsoluteURL } from 'wasp/universal/validators'; - -console.info("๐Ÿ” Validating environment variables..."); -throwIfNotValidAbsoluteURL(process.env.REACT_APP_API_URL, 'Environemnt variable REACT_APP_API_URL'); diff --git a/waspc/data/Generator/templates/react-app/src/components/DefaultRootErrorBoundary.tsx b/waspc/data/Generator/templates/react-app/src/components/DefaultRootErrorBoundary.tsx index 7af24e15d6..1e931935e6 100644 --- a/waspc/data/Generator/templates/react-app/src/components/DefaultRootErrorBoundary.tsx +++ b/waspc/data/Generator/templates/react-app/src/components/DefaultRootErrorBoundary.tsx @@ -7,7 +7,10 @@ export function DefaultRootErrorBoundary() { console.error(error) return ( -

There was an error rendering this page. Check the browser console for more information.
+
+ There was an error rendering this page. Check the browser console for + more information. +
) } diff --git a/waspc/data/Generator/templates/sdk/wasp/client/config.ts b/waspc/data/Generator/templates/sdk/wasp/client/config.ts index 39b620af01..fff77cb01c 100644 --- a/waspc/data/Generator/templates/sdk/wasp/client/config.ts +++ b/waspc/data/Generator/templates/sdk/wasp/client/config.ts @@ -1,7 +1,7 @@ -{{={= =}=}} import { stripTrailingSlash } from '../universal/url.js' +import { env } from './env.js' -const apiUrl = stripTrailingSlash(import.meta.env.REACT_APP_API_URL) || '{= defaultServerUrl =}'; +const apiUrl = stripTrailingSlash(env.REACT_APP_API_URL) // PUBLIC API export type ClientConfig = { diff --git a/waspc/data/Generator/templates/sdk/wasp/client/env.ts b/waspc/data/Generator/templates/sdk/wasp/client/env.ts new file mode 100644 index 0000000000..8921eb5ccb --- /dev/null +++ b/waspc/data/Generator/templates/sdk/wasp/client/env.ts @@ -0,0 +1,25 @@ +{{={= =}=}} +import * as z from 'zod' + +import { ensureEnvSchema } from '../env/validation.js' + +{=# envValidationSchema.isDefined =} +{=& envValidationSchema.importStatement =} +const userClientEnvSchema = {= envValidationSchema.importIdentifier =} +{=/ envValidationSchema.isDefined =} +{=^ envValidationSchema.isDefined =} +const userClientEnvSchema = z.object({}) +{=/ envValidationSchema.isDefined =} + +const waspClientEnvSchema = z.object({ + REACT_APP_API_URL: z + .string({ + required_error: 'REACT_APP_API_URL is required', + }) + .default('{= defaultServerUrl =}') +}) + +const clientEnvSchema = userClientEnvSchema.merge(waspClientEnvSchema) + +// PUBLIC API +export const env = ensureEnvSchema(import.meta.env, clientEnvSchema) diff --git a/waspc/data/Generator/templates/sdk/wasp/client/index.ts b/waspc/data/Generator/templates/sdk/wasp/client/index.ts index cfce564c4d..859f8656b6 100644 --- a/waspc/data/Generator/templates/sdk/wasp/client/index.ts +++ b/waspc/data/Generator/templates/sdk/wasp/client/index.ts @@ -11,4 +11,7 @@ export enum HttpMethod { export type Route = { method: HttpMethod; path: string } // PUBLIC API -export { config, ClientConfig } from './config' +export { config, ClientConfig } from './config.js' + +// PUBLIC API +export { env } from './env.js' diff --git a/waspc/data/Generator/templates/sdk/wasp/env/index.ts b/waspc/data/Generator/templates/sdk/wasp/env/index.ts new file mode 100644 index 0000000000..4c783125c1 --- /dev/null +++ b/waspc/data/Generator/templates/sdk/wasp/env/index.ts @@ -0,0 +1,11 @@ +import * as z from 'zod' + +// PUBLIC API +export function defineEnvValidationSchema>( + schema: Schema, +): Schema { + return schema +} + +// PRIVATE API (SDK, Vite config) +export { ensureEnvSchema } from './validation.js' diff --git a/waspc/data/Generator/templates/sdk/wasp/env/validation.ts b/waspc/data/Generator/templates/sdk/wasp/env/validation.ts new file mode 100644 index 0000000000..a5bc334754 --- /dev/null +++ b/waspc/data/Generator/templates/sdk/wasp/env/validation.ts @@ -0,0 +1,25 @@ +import * as z from 'zod' + +const redColor = '\x1b[31m' + +export function ensureEnvSchema( + data: unknown, + schema: Schema +): z.infer { + try { + return schema.parse(data) + } catch (e) { + if (e instanceof z.ZodError) { + const errorOutput = ['', 'โ•โ• Env vars validation failed โ•โ•', ''] + for (const error of e.errors) { + errorOutput.push(` - ${error.message}`) + } + errorOutput.push('') + errorOutput.push('โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•') + console.error(redColor, errorOutput.join('\n')) + throw new Error('Error parsing environment variables') + } else { + throw e + } + } +} diff --git a/waspc/data/Generator/templates/sdk/wasp/package.json b/waspc/data/Generator/templates/sdk/wasp/package.json index 5995a7e777..440c10e913 100644 --- a/waspc/data/Generator/templates/sdk/wasp/package.json +++ b/waspc/data/Generator/templates/sdk/wasp/package.json @@ -109,6 +109,7 @@ "./client/test": "./dist/client/test/index.js", "./client": "./dist/client/index.js", "./dev": "./dist/dev/index.js", + "./env": "./dist/env/index.js", {=! todo(filip): Fixes below are for type errors in 0.13.1, remove ASAP =} {=! Used by our code (SDK for full-stack type safety), uncodumented (but accessible) for users. =} diff --git a/waspc/data/Generator/templates/sdk/wasp/server/HttpError.ts b/waspc/data/Generator/templates/sdk/wasp/server/HttpError.ts index d05a2c841b..bce964712d 100644 --- a/waspc/data/Generator/templates/sdk/wasp/server/HttpError.ts +++ b/waspc/data/Generator/templates/sdk/wasp/server/HttpError.ts @@ -2,7 +2,12 @@ export class HttpError extends Error { public statusCode: number public data: unknown - constructor (statusCode: number, message?: string, data?: Record, options?: ErrorOptions) { + constructor( + statusCode: number, + message?: string, + data?: Record, + options?: ErrorOptions + ) { super(message, options) if (Error.captureStackTrace) { @@ -11,7 +16,9 @@ export class HttpError extends Error { this.name = this.constructor.name - if (!(Number.isInteger(statusCode) && statusCode >= 400 && statusCode < 600)) { + if ( + !(Number.isInteger(statusCode) && statusCode >= 400 && statusCode < 600) + ) { throw new Error('statusCode has to be integer in range [400, 600).') } this.statusCode = statusCode diff --git a/waspc/data/Generator/templates/sdk/wasp/server/auth/oauth/env.ts b/waspc/data/Generator/templates/sdk/wasp/server/auth/oauth/env.ts deleted file mode 100644 index ada2452b8e..0000000000 --- a/waspc/data/Generator/templates/sdk/wasp/server/auth/oauth/env.ts +++ /dev/null @@ -1,15 +0,0 @@ -// PRIVATE API (SDK) -export function ensureEnvVarsForProvider( - envVarNames: EnvVarName[], - providerName: string, -): Record { - const result: Record = {}; - for (const envVarName of envVarNames) { - const value = process.env[envVarName]; - if (!value) { - throw new Error(`${envVarName} env variable is required when using the ${providerName} auth provider.`); - } - result[envVarName] = value; - } - return result as Record; -} diff --git a/waspc/data/Generator/templates/sdk/wasp/server/auth/oauth/provider.ts b/waspc/data/Generator/templates/sdk/wasp/server/auth/oauth/provider.ts index c2aee70897..c02e5adbee 100644 --- a/waspc/data/Generator/templates/sdk/wasp/server/auth/oauth/provider.ts +++ b/waspc/data/Generator/templates/sdk/wasp/server/auth/oauth/provider.ts @@ -1,23 +1,19 @@ import { OAuth2Provider, OAuth2ProviderWithPKCE } from "arctic"; export function defineProvider< - OAuthClient extends OAuth2Provider | OAuth2ProviderWithPKCE, - Env extends Record + OAuthClient extends OAuth2Provider | OAuth2ProviderWithPKCE >({ id, displayName, - env, oAuthClient, }: { id: string; displayName: string; - env: Env; oAuthClient: OAuthClient; }) { return { id, displayName, - env, oAuthClient, }; } diff --git a/waspc/data/Generator/templates/sdk/wasp/server/auth/oauth/providers/discord.ts b/waspc/data/Generator/templates/sdk/wasp/server/auth/oauth/providers/discord.ts index 52e396f7a6..f8799170ca 100644 --- a/waspc/data/Generator/templates/sdk/wasp/server/auth/oauth/providers/discord.ts +++ b/waspc/data/Generator/templates/sdk/wasp/server/auth/oauth/providers/discord.ts @@ -1,17 +1,12 @@ {{={= =}=}} -import { Discord } from "arctic"; +import { Discord } from 'arctic'; -import { defineProvider } from "../provider.js"; -import { ensureEnvVarsForProvider } from "../env.js"; -import { getRedirectUriForCallback } from "../redirect.js"; +import { defineProvider } from '../provider.js'; +import { getRedirectUriForCallback } from '../redirect.js'; +import { env } from '../../../env.js'; -const id = "{= providerId =}"; -const displayName = "{= displayName =}"; - -const env = ensureEnvVarsForProvider( - ["DISCORD_CLIENT_ID", "DISCORD_CLIENT_SECRET"], - displayName -); +const id = '{= providerId =}'; +const displayName = '{= displayName =}'; const oAuthClient = new Discord( env.DISCORD_CLIENT_ID, @@ -23,6 +18,5 @@ const oAuthClient = new Discord( export const discord = defineProvider({ id, displayName, - env, oAuthClient, }); diff --git a/waspc/data/Generator/templates/sdk/wasp/server/auth/oauth/providers/github.ts b/waspc/data/Generator/templates/sdk/wasp/server/auth/oauth/providers/github.ts index e9c5019c37..604946dce5 100644 --- a/waspc/data/Generator/templates/sdk/wasp/server/auth/oauth/providers/github.ts +++ b/waspc/data/Generator/templates/sdk/wasp/server/auth/oauth/providers/github.ts @@ -1,16 +1,11 @@ {{={= =}=}} -import { GitHub } from "arctic"; +import { GitHub } from 'arctic'; -import { ensureEnvVarsForProvider } from "../env.js"; -import { defineProvider } from "../provider.js"; +import { defineProvider } from '../provider.js'; +import { env } from '../../../env.js'; -const id = "{= providerId =}"; -const displayName = "{= displayName =}"; - -const env = ensureEnvVarsForProvider( - ["GITHUB_CLIENT_ID", "GITHUB_CLIENT_SECRET"], - displayName -); +const id = '{= providerId =}'; +const displayName = '{= displayName =}'; const oAuthClient = new GitHub( env.GITHUB_CLIENT_ID, @@ -21,6 +16,5 @@ const oAuthClient = new GitHub( export const github = defineProvider({ id, displayName, - env, oAuthClient, }); diff --git a/waspc/data/Generator/templates/sdk/wasp/server/auth/oauth/providers/google.ts b/waspc/data/Generator/templates/sdk/wasp/server/auth/oauth/providers/google.ts index cf157ae12b..4fc8ceaa47 100644 --- a/waspc/data/Generator/templates/sdk/wasp/server/auth/oauth/providers/google.ts +++ b/waspc/data/Generator/templates/sdk/wasp/server/auth/oauth/providers/google.ts @@ -1,17 +1,12 @@ {{={= =}=}} -import { Google } from "arctic"; +import { Google } from 'arctic'; -import { ensureEnvVarsForProvider } from "../env.js"; -import { getRedirectUriForCallback } from "../redirect.js"; -import { defineProvider } from "../provider.js"; +import { getRedirectUriForCallback } from '../redirect.js'; +import { defineProvider } from '../provider.js'; +import { env } from '../../../env.js'; -const id = "{= providerId =}"; -const displayName = "{= displayName =}"; - -const env = ensureEnvVarsForProvider( - ["GOOGLE_CLIENT_ID", "GOOGLE_CLIENT_SECRET"], - displayName, -); +const id = '{= providerId =}'; +const displayName = '{= displayName =}'; const oAuthClient = new Google( env.GOOGLE_CLIENT_ID, @@ -23,6 +18,5 @@ const oAuthClient = new Google( export const google = defineProvider({ id, displayName, - env, oAuthClient, }); diff --git a/waspc/data/Generator/templates/sdk/wasp/server/auth/oauth/providers/keycloak.ts b/waspc/data/Generator/templates/sdk/wasp/server/auth/oauth/providers/keycloak.ts index 93136732d9..003d5aba62 100644 --- a/waspc/data/Generator/templates/sdk/wasp/server/auth/oauth/providers/keycloak.ts +++ b/waspc/data/Generator/templates/sdk/wasp/server/auth/oauth/providers/keycloak.ts @@ -1,17 +1,12 @@ {{={= =}=}} -import { Keycloak } from "arctic"; +import { Keycloak } from 'arctic'; -import { ensureEnvVarsForProvider } from "../env.js"; -import { getRedirectUriForCallback } from "../redirect.js"; -import { defineProvider } from "../provider.js"; +import { getRedirectUriForCallback } from '../redirect.js'; +import { defineProvider } from '../provider.js'; +import { env } from '../../../env.js'; -const id = "{= providerId =}"; -const displayName = "{= displayName =}"; - -const env = ensureEnvVarsForProvider( - ["KEYCLOAK_REALM_URL", "KEYCLOAK_CLIENT_ID", "KEYCLOAK_CLIENT_SECRET"], - displayName, -); +const id = '{= providerId =}'; +const displayName = '{= displayName =}'; const oAuthClient = new Keycloak( env.KEYCLOAK_REALM_URL, @@ -24,6 +19,5 @@ const oAuthClient = new Keycloak( export const keycloak = defineProvider({ id, displayName, - env, oAuthClient, }); diff --git a/waspc/data/Generator/templates/sdk/wasp/server/config.ts b/waspc/data/Generator/templates/sdk/wasp/server/config.ts index 01f9622c08..1f6b94a258 100644 --- a/waspc/data/Generator/templates/sdk/wasp/server/config.ts +++ b/waspc/data/Generator/templates/sdk/wasp/server/config.ts @@ -1,94 +1,47 @@ {{={= =}=}} -import merge from 'lodash.merge' +import { env } from './env.js' +import { stripTrailingSlash } from '../universal/url.js' -import { stripTrailingSlash } from "../universal/url.js"; +type NodeEnv = typeof env.NODE_ENV -const nodeEnv = process.env.NODE_ENV ?? 'development' - -// TODO: -// - Use dotenv library to consume env vars from a file. -// - Use convict library to define schema and validate env vars. -// https://codingsans.com/blog/node-config-best-practices - -type BaseConfig = { +type Config = { + env: NodeEnv; + isDevelopment: boolean; + port: number; + databaseUrl: string; + frontendUrl: string; + serverUrl: string; allowedCORSOrigins: string | string[]; {=# isAuthEnabled =} auth: { - jwtSecret: string | undefined; + jwtSecret: string; } {=/ isAuthEnabled =} } -type CommonConfig = BaseConfig & { - env: string; - isDevelopment: boolean; - port: number; - databaseUrl: string | undefined; -} +const frontendUrl = stripTrailingSlash(env.WASP_WEB_CLIENT_URL) +const serverUrl = stripTrailingSlash(env.WASP_SERVER_URL) -type EnvConfig = BaseConfig & { - frontendUrl: string; - serverUrl: string; +const allowedCORSOriginsPerEnv: Record = { + development: '*', + production: [frontendUrl] } - -type Config = CommonConfig & EnvConfig - -const config: { - all: CommonConfig, - development: EnvConfig, - production: EnvConfig, -} = { - all: { - env: nodeEnv, - isDevelopment: nodeEnv === 'development', - port: process.env.PORT ? parseInt(process.env.PORT) : {= defaultServerPort =}, - databaseUrl: process.env.{= databaseUrlEnvVarName =}, - allowedCORSOrigins: [], - {=# isAuthEnabled =} - auth: { - jwtSecret: undefined - } - {=/ isAuthEnabled =} - }, - development: getDevelopmentConfig(), - production: getProductionConfig(), -} - -const resolvedConfig: Config = merge(config.all, config[nodeEnv]) -// PUBLIC API -export default resolvedConfig - -function getDevelopmentConfig(): EnvConfig { - const frontendUrl = stripTrailingSlash(process.env.WASP_WEB_CLIENT_URL ?? '{= defaultClientUrl =}'); - const serverUrl = stripTrailingSlash(process.env.WASP_SERVER_URL ?? '{= defaultServerUrl =}'); - return { - // @ts-ignore - frontendUrl, - // @ts-ignore - serverUrl, - allowedCORSOrigins: '*', - {=# isAuthEnabled =} - auth: { - jwtSecret: 'DEVJWTSECRET' - } - {=/ isAuthEnabled =} +const allowedCORSOrigins = allowedCORSOriginsPerEnv[env.NODE_ENV] + +const config: Config = { + frontendUrl, + serverUrl, + allowedCORSOrigins, + env: env.NODE_ENV, + isDevelopment: env.NODE_ENV === 'development', + port: env.PORT, + databaseUrl: env.{= databaseUrlEnvVarName =}, + {=# isAuthEnabled =} + auth: { + jwtSecret: env.JWT_SECRET } + {=/ isAuthEnabled =} } -function getProductionConfig(): EnvConfig { - const frontendUrl = stripTrailingSlash(process.env.WASP_WEB_CLIENT_URL); - const serverUrl = stripTrailingSlash(process.env.WASP_SERVER_URL); - return { - // @ts-ignore - frontendUrl, - // @ts-ignore - serverUrl, - // @ts-ignore - allowedCORSOrigins: [frontendUrl], - {=# isAuthEnabled =} - auth: { - jwtSecret: process.env.JWT_SECRET - } - {=/ isAuthEnabled =} - } -} +// PUBLIC API +export default config diff --git a/waspc/data/Generator/templates/sdk/wasp/server/email/core/index.ts b/waspc/data/Generator/templates/sdk/wasp/server/email/core/index.ts index 746cd023e3..b772a30b46 100644 --- a/waspc/data/Generator/templates/sdk/wasp/server/email/core/index.ts +++ b/waspc/data/Generator/templates/sdk/wasp/server/email/core/index.ts @@ -1,17 +1,17 @@ {{={= =}=}} -{=# isSmtpProviderUsed =} +{=# isSmtpProviderEnabled =} // PRIVATE API export { initSmtpEmailSender as initEmailSender } from "./providers/smtp.js"; -{=/ isSmtpProviderUsed =} -{=# isSendGridProviderUsed =} +{=/ isSmtpProviderEnabled =} +{=# isSendGridProviderEnabled =} // PRIVATE API export { initSendGridEmailSender as initEmailSender } from "./providers/sendgrid.js"; -{=/ isSendGridProviderUsed =} -{=# isMailgunProviderUsed =} +{=/ isSendGridProviderEnabled =} +{=# isMailgunProviderEnabled =} // PRIVATE API export { initMailgunEmailSender as initEmailSender } from "./providers/mailgun.js"; -{=/ isMailgunProviderUsed =} -{=# isDummyProviderUsed =} +{=/ isMailgunProviderEnabled =} +{=# isDummyProviderEnabled =} // PRIVATE API export { initDummyEmailSender as initEmailSender } from "./providers/dummy.js"; -{=/ isDummyProviderUsed =} +{=/ isDummyProviderEnabled =} diff --git a/waspc/data/Generator/templates/sdk/wasp/server/email/index.ts b/waspc/data/Generator/templates/sdk/wasp/server/email/index.ts index 41a30c8871..660740afa1 100644 --- a/waspc/data/Generator/templates/sdk/wasp/server/email/index.ts +++ b/waspc/data/Generator/templates/sdk/wasp/server/email/index.ts @@ -1,37 +1,36 @@ {{={= =}=}} +import { env } from '../env.js'; import { initEmailSender } from "./core/index.js"; import { EmailSender } from "./core/types.js"; -// TODO: We need to validate all the env variables -// For now, we are letting the runtime throw if they are not provided -{=# isSmtpProviderUsed =} +{=# isSmtpProviderEnabled =} const emailProvider = { type: "smtp", - host: process.env.SMTP_HOST!, - port: parseInt(process.env.SMTP_PORT!, 10), - username: process.env.SMTP_USERNAME!, - password: process.env.SMTP_PASSWORD!, + host: env.SMTP_HOST, + port: env.SMTP_PORT, + username: env.SMTP_USERNAME, + password: env.SMTP_PASSWORD, } as const; -{=/ isSmtpProviderUsed =} -{=# isSendGridProviderUsed =} +{=/ isSmtpProviderEnabled =} +{=# isSendGridProviderEnabled =} const emailProvider = { type: "sendgrid", - apiKey: process.env.SENDGRID_API_KEY!, + apiKey: env.SENDGRID_API_KEY, } as const; -{=/ isSendGridProviderUsed =} -{=# isMailgunProviderUsed =} +{=/ isSendGridProviderEnabled =} +{=# isMailgunProviderEnabled =} const emailProvider = { type: "mailgun", - apiKey: process.env.MAILGUN_API_KEY!, - domain: process.env.MAILGUN_DOMAIN!, - apiUrl: process.env.MAILGUN_API_URL!, + apiKey: env.MAILGUN_API_KEY, + domain: env.MAILGUN_DOMAIN, + apiUrl: env.MAILGUN_API_URL, } as const; -{=/ isMailgunProviderUsed =} -{=# isDummyProviderUsed =} +{=/ isMailgunProviderEnabled =} +{=# isDummyProviderEnabled =} const emailProvider = { type: "dummy", } as const; -{=/ isDummyProviderUsed =} +{=/ isDummyProviderEnabled =} // PUBLIC API export const emailSender: EmailSender = initEmailSender(emailProvider); diff --git a/waspc/data/Generator/templates/sdk/wasp/server/env.ts b/waspc/data/Generator/templates/sdk/wasp/server/env.ts new file mode 100644 index 0000000000..94efc3b60f --- /dev/null +++ b/waspc/data/Generator/templates/sdk/wasp/server/env.ts @@ -0,0 +1,157 @@ +{{={= =}=}} +import * as z from 'zod' + +import { ensureEnvSchema } from '../env/validation.js' + +{=# envValidationSchema.isDefined =} +{=& envValidationSchema.importStatement =} +const userServerEnvSchema = {= envValidationSchema.importIdentifier =} +{=/ envValidationSchema.isDefined =} +{=^ envValidationSchema.isDefined =} +const userServerEnvSchema = z.object({}) +{=/ envValidationSchema.isDefined =} + +const waspServerCommonSchema = z.object({ + PORT: z.coerce.number().default({= defaultServerPort =}), + {= databaseUrlEnvVarName =}: z.string({ + required_error: '{= databaseUrlEnvVarName =} is required', + }), + PG_BOSS_NEW_OPTIONS: z.string().optional(), + {=# isEmailSenderEnabled =} + {=# enabledEmailSenders.isSmtpProviderEnabled =} + SMTP_HOST: z.string({ + required_error: getRequiredEnvVarErrorMessage('SMTP email sender', 'SMTP_HOST'), + }), + SMTP_PORT: z.coerce.number({ + required_error: getRequiredEnvVarErrorMessage('SMTP email sender', 'SMTP_PORT'), + invalid_type_error: 'SMTP_PORT must be a number', + }), + SMTP_USERNAME: z.string({ + required_error: getRequiredEnvVarErrorMessage('SMTP email sender', 'SMTP_USERNAME'), + }), + SMTP_PASSWORD: z.string({ + required_error: getRequiredEnvVarErrorMessage('SMTP email sender', 'SMTP_PASSWORD'), + }), + {=/ enabledEmailSenders.isSmtpProviderEnabled =} + {=# enabledEmailSenders.isSendGridProviderEnabled =} + SENDGRID_API_KEY: z.string({ + required_error: getRequiredEnvVarErrorMessage('SendGrid email sender', 'SENDGRID_API_KEY'), + }), + {=/ enabledEmailSenders.isSendGridProviderEnabled =} + {=# enabledEmailSenders.isMailgunProviderEnabled =} + MAILGUN_API_KEY: z.string({ + required_error: getRequiredEnvVarErrorMessage('Mailgun email sender', 'MAILGUN_API_KEY'), + }), + MAILGUN_DOMAIN: z.string({ + required_error: getRequiredEnvVarErrorMessage('Mailgun email sender', 'MAILGUN_DOMAIN'), + }), + MAILGUN_API_URL: z.string().optional(), + {=/ enabledEmailSenders.isMailgunProviderEnabled =} + {=/ isEmailSenderEnabled =} + SKIP_EMAIL_VERIFICATION_IN_DEV: z + .enum(['true', 'false'], { + message: 'SKIP_EMAIL_VERIFICATION_IN_DEV must be either "true" or "false"', + }) + .transform((value) => value === 'true') + .default('false'), + {=# isAuthEnabled =} + {=# enabledAuthProviders.isGoogleAuthEnabled =} + GOOGLE_CLIENT_ID: z.string({ + required_error: getRequiredEnvVarErrorMessage('Google auth provider', 'GOOGLE_CLIENT_ID'), + }), + GOOGLE_CLIENT_SECRET: z.string({ + required_error: getRequiredEnvVarErrorMessage('Google auth provider', 'GOOGLE_CLIENT_SECRET'), + }), + {=/ enabledAuthProviders.isGoogleAuthEnabled =} + {=# enabledAuthProviders.isGitHubAuthEnabled =} + GITHUB_CLIENT_ID: z.string({ + required_error: getRequiredEnvVarErrorMessage('GitHub auth provider', 'GITHUB_CLIENT_ID'), + }), + GITHUB_CLIENT_SECRET: z.string({ + required_error: getRequiredEnvVarErrorMessage('GitHub auth provider', 'GITHUB_CLIENT_SECRET'), + }), + {=/ enabledAuthProviders.isGitHubAuthEnabled =} + {=# enabledAuthProviders.isDiscordAuthEnabled =} + DISCORD_CLIENT_ID: z.string({ + required_error: getRequiredEnvVarErrorMessage('Discord auth provider', 'DISCORD_CLIENT_ID'), + }), + DISCORD_CLIENT_SECRET: z.string({ + required_error: getRequiredEnvVarErrorMessage('Discord auth provider', 'DISCORD_CLIENT_SECRET'), + }), + {=/ enabledAuthProviders.isDiscordAuthEnabled =} + {=# enabledAuthProviders.isKeycloakAuthEnabled =} + KEYCLOAK_CLIENT_ID: z.string({ + required_error: getRequiredEnvVarErrorMessage('Keycloak auth provider', 'KEYCLOAK_CLIENT_ID'), + }), + KEYCLOAK_CLIENT_SECRET: z.string({ + required_error: getRequiredEnvVarErrorMessage('Keycloak auth provider', 'KEYCLOAK_CLIENT_SECRET'), + }), + KEYCLOAK_REALM_URL: z + .string({ + required_error: getRequiredEnvVarErrorMessage('Keycloak auth provider', 'KEYCLOAK_REALM_URL'), + }) + .url({ + message: 'KEYCLOAK_REALM_URL must be a valid URL', + }), + {=/ enabledAuthProviders.isKeycloakAuthEnabled =} + {=/ isAuthEnabled =} +}) + +const serverUrlSchema = z + .string({ + required_error: 'WASP_SERVER_URL is required', + }) + .url({ + message: 'WASP_SERVER_URL must be a valid URL', + }) + +const clientUrlSchema = z + .string({ + required_error: 'WASP_WEB_CLIENT_URL is required', + }) + .url({ + message: 'WASP_WEB_CLIENT_URL must be a valid URL', + }) + +{=# isAuthEnabled =} +const jwtTokenSchema = z + .string({ + required_error: 'JWT_SECRET is required', + }) +{=/ isAuthEnabled =} + +// In development, we provide default values for some environment variables +// to make the development process easier. +const serverDevSchema = z.object({ + NODE_ENV: z.literal('development'), + WASP_SERVER_URL: serverUrlSchema + .default('{= defaultServerUrl =}'), + WASP_WEB_CLIENT_URL: clientUrlSchema + .default('{= defaultClientUrl =}'), + {=# isAuthEnabled =} + JWT_SECRET: jwtTokenSchema + .default('DEVJWTSECRET'), + {=/ isAuthEnabled =} +}) + +const serverProdSchema = z.object({ + NODE_ENV: z.literal('production'), + WASP_SERVER_URL: serverUrlSchema, + WASP_WEB_CLIENT_URL: clientUrlSchema, + {=# isAuthEnabled =} + JWT_SECRET: jwtTokenSchema, + {=/ isAuthEnabled =} +}) + +const serverCommonSchema = userServerEnvSchema.merge(waspServerCommonSchema) +const serverEnvSchema = z.discriminatedUnion('NODE_ENV', [ + serverDevSchema.merge(serverCommonSchema), + serverProdSchema.merge(serverCommonSchema) +]) + +// PUBLIC API +export const env = ensureEnvSchema(process.env, serverEnvSchema) + +function getRequiredEnvVarErrorMessage(featureName: string, envVarName: string) { + return `${envVarName} is required when using ${featureName}` +} diff --git a/waspc/data/Generator/templates/sdk/wasp/server/index.ts b/waspc/data/Generator/templates/sdk/wasp/server/index.ts index 535077900b..0f95deb90a 100644 --- a/waspc/data/Generator/templates/sdk/wasp/server/index.ts +++ b/waspc/data/Generator/templates/sdk/wasp/server/index.ts @@ -10,6 +10,8 @@ export { type ServerSetupFn } from './types/index.js' export { HttpError } from './HttpError.js' // PUBLIC API export { MiddlewareConfigFn } from './middleware/index.js' +// PUBLIC API +export { env } from './env.js' // PUBLIC API export type DbSeedFn = (prisma: PrismaClient) => Promise diff --git a/waspc/data/Generator/templates/sdk/wasp/server/jobs/core/pgBoss/pgBoss.ts b/waspc/data/Generator/templates/sdk/wasp/server/jobs/core/pgBoss/pgBoss.ts index a48992aad0..eed0cf23cc 100644 --- a/waspc/data/Generator/templates/sdk/wasp/server/jobs/core/pgBoss/pgBoss.ts +++ b/waspc/data/Generator/templates/sdk/wasp/server/jobs/core/pgBoss/pgBoss.ts @@ -1,5 +1,5 @@ import PgBoss from 'pg-boss' -import { config } from 'wasp/server' +import { config, env } from '../../../index.js' const boss = createPgBoss() @@ -9,9 +9,9 @@ function createPgBoss() { } // Add an escape hatch for advanced configuration of pg-boss to overwrite our defaults. - if (process.env.PG_BOSS_NEW_OPTIONS) { + if (env.PG_BOSS_NEW_OPTIONS) { try { - pgBossNewOptions = JSON.parse(process.env.PG_BOSS_NEW_OPTIONS) + pgBossNewOptions = JSON.parse(env.PG_BOSS_NEW_OPTIONS) } catch { console.error( 'Environment variable PG_BOSS_NEW_OPTIONS was not parsable by JSON.parse()!' diff --git a/waspc/data/Generator/templates/sdk/wasp/universal/url.ts b/waspc/data/Generator/templates/sdk/wasp/universal/url.ts index d21c06c65c..126678dac9 100644 --- a/waspc/data/Generator/templates/sdk/wasp/universal/url.ts +++ b/waspc/data/Generator/templates/sdk/wasp/universal/url.ts @@ -1,3 +1,5 @@ +export function stripTrailingSlash(url: string): string +export function stripTrailingSlash(url: undefined): undefined export function stripTrailingSlash(url?: string): string | undefined { return url?.replace(/\/$/, ""); } diff --git a/waspc/data/Generator/templates/server/package.json b/waspc/data/Generator/templates/server/package.json index 24d7a2c1a7..14f6464bbe 100644 --- a/waspc/data/Generator/templates/server/package.json +++ b/waspc/data/Generator/templates/server/package.json @@ -7,10 +7,9 @@ "comment-filip": "The server.js location changed because we have now included client source files above .wasp/out/server/src.", "scripts": { "bundle": "rollup --config --silent", - "start": "npm run validate-env && node --enable-source-maps -r dotenv/config bundle/server.js", + "start": "node --enable-source-maps -r dotenv/config bundle/server.js", "bundle-and-start": "npm run bundle && npm run start", "watch": "nodemon --exec 'npm run bundle-and-start || exit 1'", - "validate-env": "node -r dotenv/config ./scripts/validate-env.mjs", "db-seed": "npm run bundle && node --enable-source-maps -r dotenv/config bundle/dbSeed.js", "db-migrate-prod": "prisma migrate deploy --schema=../db/schema.prisma", "start-production": "{=& startProductionScript =}" diff --git a/waspc/data/Generator/templates/server/scripts/validate-env.mjs b/waspc/data/Generator/templates/server/scripts/validate-env.mjs deleted file mode 100644 index f6e50aceca..0000000000 --- a/waspc/data/Generator/templates/server/scripts/validate-env.mjs +++ /dev/null @@ -1,5 +0,0 @@ -import { throwIfNotValidAbsoluteURL } from 'wasp/universal/validators'; - -console.info("๐Ÿ” Validating environment variables..."); -throwIfNotValidAbsoluteURL(process.env.WASP_WEB_CLIENT_URL, 'Environment variable WASP_WEB_CLIENT_URL'); -throwIfNotValidAbsoluteURL(process.env.WASP_SERVER_URL, 'Environment variable WASP_SERVER_URL'); diff --git a/waspc/data/Generator/templates/server/src/auth/providers/config/email.ts b/waspc/data/Generator/templates/server/src/auth/providers/config/email.ts index a350e07b00..a0ba47d756 100644 --- a/waspc/data/Generator/templates/server/src/auth/providers/config/email.ts +++ b/waspc/data/Generator/templates/server/src/auth/providers/config/email.ts @@ -11,6 +11,7 @@ import { resetPassword } from "../email/resetPassword.js"; import { verifyEmail } from "../email/verifyEmail.js"; import { GetVerificationEmailContentFn, GetPasswordResetEmailContentFn } from "wasp/server/auth/email"; import { handleRejection } from "wasp/server/utils"; +import { env } from "wasp/server"; {=# userSignupFields.isDefined =} {=& userSignupFields.importStatement =} @@ -70,7 +71,7 @@ const config: ProviderConfig = { clientRoute: '{= emailVerificationClientRoute =}', getVerificationEmailContent: _waspGetVerificationEmailContent, {=# isDevelopment =} - isEmailAutoVerified: process.env.SKIP_EMAIL_VERIFICATION_IN_DEV === 'true', + isEmailAutoVerified: env.SKIP_EMAIL_VERIFICATION_IN_DEV, {=/ isDevelopment =} {=^ isDevelopment =} isEmailAutoVerified: false, diff --git a/waspc/data/Generator/templates/server/src/auth/providers/config/keycloak.ts b/waspc/data/Generator/templates/server/src/auth/providers/config/keycloak.ts index f80d56ad46..8bcacd390c 100644 --- a/waspc/data/Generator/templates/server/src/auth/providers/config/keycloak.ts +++ b/waspc/data/Generator/templates/server/src/auth/providers/config/keycloak.ts @@ -2,6 +2,7 @@ import type { ProviderConfig } from "wasp/auth/providers/types"; import { keycloak } from "wasp/server/auth"; +import { env } from "wasp/server"; import { mergeDefaultAndUserConfig } from "../oauth/config.js"; import { createOAuthProviderRouter } from "../oauth/handler.js"; @@ -32,7 +33,7 @@ const _waspConfig: ProviderConfig = { providerProfile: unknown; providerUserId: string; }> { - const userInfoEndpoint = `${keycloak.env.KEYCLOAK_REALM_URL}/protocol/openid-connect/userinfo`; + const userInfoEndpoint = `${env.KEYCLOAK_REALM_URL}/protocol/openid-connect/userinfo`; const response = await fetch( userInfoEndpoint, { diff --git a/waspc/data/Generator/templates/server/src/auth/providers/oauth/cookies.ts b/waspc/data/Generator/templates/server/src/auth/providers/oauth/cookies.ts index 10fdd63984..b398c2e40d 100644 --- a/waspc/data/Generator/templates/server/src/auth/providers/oauth/cookies.ts +++ b/waspc/data/Generator/templates/server/src/auth/providers/oauth/cookies.ts @@ -1,10 +1,11 @@ import { Request as ExpressRequest, Response as ExpressResponse, -} from "express"; -import { parseCookies } from "oslo/cookie"; +} from 'express'; +import { parseCookies } from 'oslo/cookie'; -import type { ProviderConfig } from "wasp/auth/providers/types"; +import type { ProviderConfig } from 'wasp/auth/providers/types'; +import { config } from 'wasp/server'; import type { OAuthStateFieldName } from './state'; @@ -17,8 +18,7 @@ export function setOAuthCookieValue( const cookieName = `${provider.id}_${fieldName}`; res.cookie(cookieName, value, { httpOnly: true, - // TODO: use server config to determine if secure - secure: process.env.NODE_ENV === "production", + secure: !config.isDevelopment, path: "/", maxAge: 60 * 60 * 1000, // 1 hour }); diff --git a/waspc/data/Generator/templates/server/src/polyfill.ts b/waspc/data/Generator/templates/server/src/polyfill.ts index 7931d12eb0..9279daf6da 100644 --- a/waspc/data/Generator/templates/server/src/polyfill.ts +++ b/waspc/data/Generator/templates/server/src/polyfill.ts @@ -1,11 +1,10 @@ // This is a polyfill for Node.js 18 webcrypto API so Lucia can use it // for random number generation. -import { webcrypto } from "node:crypto"; +import { webcrypto } from 'node:crypto' // NOTE: node < 19 doesn't have Crypto API, which we need for Lucia, so we apply the polyfill if Crypto API is not defined. -if (typeof globalThis.crypto === "undefined") { +if (typeof globalThis.crypto === 'undefined') { // @ts-ignore - globalThis.crypto = webcrypto as Crypto; + globalThis.crypto = webcrypto as Crypto } - diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/files.manifest b/waspc/e2e-test/test-outputs/waspBuild-golden/files.manifest index 02925608e0..c7acb0362b 100644 --- a/waspc/e2e-test/test-outputs/waspBuild-golden/files.manifest +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/files.manifest @@ -7,6 +7,7 @@ waspBuild/.wasp/build/package.json waspBuild/.wasp/build/sdk/wasp/api/events.ts waspBuild/.wasp/build/sdk/wasp/api/index.ts waspBuild/.wasp/build/sdk/wasp/client/config.ts +waspBuild/.wasp/build/sdk/wasp/client/env.ts waspBuild/.wasp/build/sdk/wasp/client/index.ts waspBuild/.wasp/build/sdk/wasp/client/operations/actions/core.ts waspBuild/.wasp/build/sdk/wasp/client/operations/actions/index.ts @@ -36,6 +37,9 @@ waspBuild/.wasp/build/sdk/wasp/dist/api/index.js.map waspBuild/.wasp/build/sdk/wasp/dist/client/config.d.ts waspBuild/.wasp/build/sdk/wasp/dist/client/config.js waspBuild/.wasp/build/sdk/wasp/dist/client/config.js.map +waspBuild/.wasp/build/sdk/wasp/dist/client/env.d.ts +waspBuild/.wasp/build/sdk/wasp/dist/client/env.js +waspBuild/.wasp/build/sdk/wasp/dist/client/env.js.map waspBuild/.wasp/build/sdk/wasp/dist/client/index.d.ts waspBuild/.wasp/build/sdk/wasp/dist/client/index.js waspBuild/.wasp/build/sdk/wasp/dist/client/index.js.map @@ -99,6 +103,12 @@ waspBuild/.wasp/build/sdk/wasp/dist/dev/index.js.map waspBuild/.wasp/build/sdk/wasp/dist/entities/index.d.ts waspBuild/.wasp/build/sdk/wasp/dist/entities/index.js waspBuild/.wasp/build/sdk/wasp/dist/entities/index.js.map +waspBuild/.wasp/build/sdk/wasp/dist/env/index.d.ts +waspBuild/.wasp/build/sdk/wasp/dist/env/index.js +waspBuild/.wasp/build/sdk/wasp/dist/env/index.js.map +waspBuild/.wasp/build/sdk/wasp/dist/env/validation.d.ts +waspBuild/.wasp/build/sdk/wasp/dist/env/validation.js +waspBuild/.wasp/build/sdk/wasp/dist/env/validation.js.map waspBuild/.wasp/build/sdk/wasp/dist/ext-src/MainPage.d.ts waspBuild/.wasp/build/sdk/wasp/dist/ext-src/MainPage.jsx waspBuild/.wasp/build/sdk/wasp/dist/ext-src/MainPage.jsx.map @@ -120,6 +130,9 @@ waspBuild/.wasp/build/sdk/wasp/dist/server/config.js.map waspBuild/.wasp/build/sdk/wasp/dist/server/dbClient.d.ts waspBuild/.wasp/build/sdk/wasp/dist/server/dbClient.js waspBuild/.wasp/build/sdk/wasp/dist/server/dbClient.js.map +waspBuild/.wasp/build/sdk/wasp/dist/server/env.d.ts +waspBuild/.wasp/build/sdk/wasp/dist/server/env.js +waspBuild/.wasp/build/sdk/wasp/dist/server/env.js.map waspBuild/.wasp/build/sdk/wasp/dist/server/index.d.ts waspBuild/.wasp/build/sdk/wasp/dist/server/index.js waspBuild/.wasp/build/sdk/wasp/dist/server/index.js.map @@ -166,6 +179,8 @@ waspBuild/.wasp/build/sdk/wasp/dist/universal/validators.d.ts waspBuild/.wasp/build/sdk/wasp/dist/universal/validators.js waspBuild/.wasp/build/sdk/wasp/dist/universal/validators.js.map waspBuild/.wasp/build/sdk/wasp/entities/index.ts +waspBuild/.wasp/build/sdk/wasp/env/index.ts +waspBuild/.wasp/build/sdk/wasp/env/validation.ts waspBuild/.wasp/build/sdk/wasp/ext-src/Main.css waspBuild/.wasp/build/sdk/wasp/ext-src/MainPage.jsx waspBuild/.wasp/build/sdk/wasp/ext-src/vite-env.d.ts @@ -178,6 +193,7 @@ waspBuild/.wasp/build/sdk/wasp/server/_types/serialization.ts waspBuild/.wasp/build/sdk/wasp/server/_types/taggedEntities.ts waspBuild/.wasp/build/sdk/wasp/server/config.ts waspBuild/.wasp/build/sdk/wasp/server/dbClient.ts +waspBuild/.wasp/build/sdk/wasp/server/env.ts waspBuild/.wasp/build/sdk/wasp/server/index.ts waspBuild/.wasp/build/sdk/wasp/server/middleware/globalMiddleware.ts waspBuild/.wasp/build/sdk/wasp/server/middleware/index.ts @@ -200,7 +216,6 @@ waspBuild/.wasp/build/server/README.md waspBuild/.wasp/build/server/nodemon.json waspBuild/.wasp/build/server/package.json waspBuild/.wasp/build/server/rollup.config.js -waspBuild/.wasp/build/server/scripts/validate-env.mjs waspBuild/.wasp/build/server/src/app.js waspBuild/.wasp/build/server/src/middleware/globalMiddleware.ts waspBuild/.wasp/build/server/src/middleware/index.ts @@ -222,7 +237,6 @@ waspBuild/.wasp/build/web-app/package.json waspBuild/.wasp/build/web-app/public/.gitkeep waspBuild/.wasp/build/web-app/public/favicon.ico waspBuild/.wasp/build/web-app/public/manifest.json -waspBuild/.wasp/build/web-app/scripts/validate-env.mjs waspBuild/.wasp/build/web-app/src/components/DefaultRootErrorBoundary.tsx waspBuild/.wasp/build/web-app/src/components/FullPageWrapper.tsx waspBuild/.wasp/build/web-app/src/components/Loader.module.css @@ -241,6 +255,7 @@ waspBuild/.wasp/build/web-app/vite/detectServerImports.ts waspBuild/.wasp/out/sdk/wasp/api/events.ts waspBuild/.wasp/out/sdk/wasp/api/index.ts waspBuild/.wasp/out/sdk/wasp/client/config.ts +waspBuild/.wasp/out/sdk/wasp/client/env.ts waspBuild/.wasp/out/sdk/wasp/client/index.ts waspBuild/.wasp/out/sdk/wasp/client/operations/actions/core.ts waspBuild/.wasp/out/sdk/wasp/client/operations/actions/index.ts @@ -270,6 +285,9 @@ waspBuild/.wasp/out/sdk/wasp/dist/api/index.js.map waspBuild/.wasp/out/sdk/wasp/dist/client/config.d.ts waspBuild/.wasp/out/sdk/wasp/dist/client/config.js waspBuild/.wasp/out/sdk/wasp/dist/client/config.js.map +waspBuild/.wasp/out/sdk/wasp/dist/client/env.d.ts +waspBuild/.wasp/out/sdk/wasp/dist/client/env.js +waspBuild/.wasp/out/sdk/wasp/dist/client/env.js.map waspBuild/.wasp/out/sdk/wasp/dist/client/index.d.ts waspBuild/.wasp/out/sdk/wasp/dist/client/index.js waspBuild/.wasp/out/sdk/wasp/dist/client/index.js.map @@ -333,6 +351,12 @@ waspBuild/.wasp/out/sdk/wasp/dist/dev/index.js.map waspBuild/.wasp/out/sdk/wasp/dist/entities/index.d.ts waspBuild/.wasp/out/sdk/wasp/dist/entities/index.js waspBuild/.wasp/out/sdk/wasp/dist/entities/index.js.map +waspBuild/.wasp/out/sdk/wasp/dist/env/index.d.ts +waspBuild/.wasp/out/sdk/wasp/dist/env/index.js +waspBuild/.wasp/out/sdk/wasp/dist/env/index.js.map +waspBuild/.wasp/out/sdk/wasp/dist/env/validation.d.ts +waspBuild/.wasp/out/sdk/wasp/dist/env/validation.js +waspBuild/.wasp/out/sdk/wasp/dist/env/validation.js.map waspBuild/.wasp/out/sdk/wasp/dist/ext-src/MainPage.d.ts waspBuild/.wasp/out/sdk/wasp/dist/ext-src/MainPage.jsx waspBuild/.wasp/out/sdk/wasp/dist/ext-src/MainPage.jsx.map @@ -354,6 +378,9 @@ waspBuild/.wasp/out/sdk/wasp/dist/server/config.js.map waspBuild/.wasp/out/sdk/wasp/dist/server/dbClient.d.ts waspBuild/.wasp/out/sdk/wasp/dist/server/dbClient.js waspBuild/.wasp/out/sdk/wasp/dist/server/dbClient.js.map +waspBuild/.wasp/out/sdk/wasp/dist/server/env.d.ts +waspBuild/.wasp/out/sdk/wasp/dist/server/env.js +waspBuild/.wasp/out/sdk/wasp/dist/server/env.js.map waspBuild/.wasp/out/sdk/wasp/dist/server/index.d.ts waspBuild/.wasp/out/sdk/wasp/dist/server/index.js waspBuild/.wasp/out/sdk/wasp/dist/server/index.js.map @@ -400,6 +427,8 @@ waspBuild/.wasp/out/sdk/wasp/dist/universal/validators.d.ts waspBuild/.wasp/out/sdk/wasp/dist/universal/validators.js waspBuild/.wasp/out/sdk/wasp/dist/universal/validators.js.map waspBuild/.wasp/out/sdk/wasp/entities/index.ts +waspBuild/.wasp/out/sdk/wasp/env/index.ts +waspBuild/.wasp/out/sdk/wasp/env/validation.ts waspBuild/.wasp/out/sdk/wasp/ext-src/Main.css waspBuild/.wasp/out/sdk/wasp/ext-src/MainPage.jsx waspBuild/.wasp/out/sdk/wasp/ext-src/vite-env.d.ts @@ -412,6 +441,7 @@ waspBuild/.wasp/out/sdk/wasp/server/_types/serialization.ts waspBuild/.wasp/out/sdk/wasp/server/_types/taggedEntities.ts waspBuild/.wasp/out/sdk/wasp/server/config.ts waspBuild/.wasp/out/sdk/wasp/server/dbClient.ts +waspBuild/.wasp/out/sdk/wasp/server/env.ts waspBuild/.wasp/out/sdk/wasp/server/index.ts waspBuild/.wasp/out/sdk/wasp/server/middleware/globalMiddleware.ts waspBuild/.wasp/out/sdk/wasp/server/middleware/index.ts diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/.waspchecksums b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/.waspchecksums index 1454c2c731..91730d84b7 100644 --- a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/.waspchecksums +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/.waspchecksums @@ -18,14 +18,21 @@ "file", "../out/sdk/wasp/client/config.ts" ], - "d94e8bee5fd8f6793b511652b9ee558e57f4913595763137a5385cd929648168" + "4c5835ad64352cc87a4a17e41fd70333bc0d1eedd7339d7ad3d3507cdb64038d" + ], + [ + [ + "file", + "../out/sdk/wasp/client/env.ts" + ], + "2a3602d40b299fbfa1a6275fe67828d5653d9f3265ba8204b75178aa41dcbae0" ], [ [ "file", "../out/sdk/wasp/client/index.ts" ], - "2b3651e7040a63cfd6a271c2aa89f21cf01170e0abeb6c5ab74adde324852fe4" + "b55e5c34c2fdb50e608bcb4331fa2e67724f1fbfb21e1b93680b0f5fe2b7005a" ], [ [ @@ -167,6 +174,20 @@ ], "c59b97b122b977b5171686c92ee5ff2d80d397c2e83cc0915affb6ee136406fb" ], + [ + [ + "file", + "../out/sdk/wasp/env/index.ts" + ], + "d92b4832e9d4249ae5b1f00c0f9760d6841aa44bc199550414793a1d57a6845e" + ], + [ + [ + "file", + "../out/sdk/wasp/env/validation.ts" + ], + "e22b2fb8931275d3b1bd3176cbdbf753d22fc61887ded0e93cd8febf1b97d260" + ], [ [ "file", @@ -200,7 +221,7 @@ "file", "../out/sdk/wasp/package.json" ], - "6f1afd30c951e1f49740256440cd626c9cb521cf0822d93d2d2a5d63d8719d7b" + "b991fb5ade61a662dea3f21bf09aa30379f30ea76204a369c0800b1413256a64" ], [ [ @@ -214,7 +235,7 @@ "file", "../out/sdk/wasp/server/HttpError.ts" ], - "f375fb45eeb16381b8cc575503db302c005ddaa1b35d837ca0ea043fe8ec6ccb" + "9c1b2449d8e9d68470c6014054a3c3074069a104ddb291b9089c30d624a2d9d9" ], [ [ @@ -242,7 +263,7 @@ "file", "../out/sdk/wasp/server/config.ts" ], - "5dd35660e0969defaa3c180179919f4922ab7bc041746872dff7f2280c1872ae" + "8431eaf0332cdbdd2f36b4c232b4b6b65fd19bad58fe51922f5b2662f22fc8ad" ], [ [ @@ -251,12 +272,19 @@ ], "50f11eb232174184be5fd44e8ee3875c30707b486c8c70c3f7bfb93609d38e66" ], + [ + [ + "file", + "../out/sdk/wasp/server/env.ts" + ], + "f3cab17868417ffd4432dc4efecde72ccaaf099a79ddc9194835f3608d123b63" + ], [ [ "file", "../out/sdk/wasp/server/index.ts" ], - "51c3e0802764f72b88f6daaf148dba514ae817e233a40cc9b92ec91784f71f98" + "a8d2ffa7dfd0a13f7fb6135193bcdac1f860603bdd0ddc2843033fd4f6f6151f" ], [ [ @@ -354,7 +382,7 @@ "file", "../out/sdk/wasp/universal/url.ts" ], - "8dc6e044a1a231b796465d94985ca47c5efd42a5d411b407a7d83a61ebae4b6d" + "58ff4ad8ffc79264d7215461571d909f3c5fb177ff32c67058d1da9cd4115d6e" ], [ [ @@ -382,7 +410,7 @@ "file", "Dockerfile" ], - "8851d4e81582d58921a5646dcf063b31dbf8ce9a46f3d4710360d2047dc644b5" + "20694e4dd2e7d96d8752ee3d792d4d8b63c46d7a4505c5312923a749146604b7" ], [ [ @@ -424,7 +452,7 @@ "file", "server/package.json" ], - "a4eb7e59ac5309fc5c39ccd7e19f4fdc7a9a568a3b393f62358ef6a6065d65c3" + "eaaa1e16e4962f38deb7220a7439a26b920ed7f852890242d084b4cebc63b7c3" ], [ [ @@ -433,13 +461,6 @@ ], "c1beb8264f11898364288d73b16f08d1923bac5f70038d9827978deb5b58a2da" ], - [ - [ - "file", - "server/scripts/validate-env.mjs" - ], - "100177b4326ccab7362eff378315d532ad1cc17cd28d1ed5978cb167fd627746" - ], [ [ "file", @@ -473,7 +494,7 @@ "file", "server/src/polyfill.ts" ], - "1149661e0aa7228b184bb2c04ac63232a6523b09b33829db6977f79daba3fd8d" + "f62c2088c8ebf5889ab479bb937ffd8f0f96d3e85483a7b088edebb5ae598dca" ], [ [ @@ -543,7 +564,7 @@ "file", "web-app/package.json" ], - "196b888bc57d79d1c32f4d3af30fbc32817afb983fa890c3749d4fcf92193a5b" + "f504812cc7092c09d8b7e25a4f783591e588211ace1e25aa56aaf20cc739f3e9" ], [ [ @@ -566,19 +587,12 @@ ], "696886c4dd2bb66df380e2a9ebf07d54fe39b25af968aeea090ed6fb528d402e" ], - [ - [ - "file", - "web-app/scripts/validate-env.mjs" - ], - "a9a3a7eb6bc3ead49d8e3850a70737c93c789098beb3b40196bf145fd38893cd" - ], [ [ "file", "web-app/src/components/DefaultRootErrorBoundary.tsx" ], - "4a141a28ca7aed9daddd75c156f7bae5eec50f3e5a85b6a43a28fa2648f34c44" + "73bbe8a2eb141e523c6b8169c0831e594b4ab6c7207f107d6d20c1561b44373a" ], [ [ diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/Dockerfile b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/Dockerfile index 44152eecda..2c775ca6af 100644 --- a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/Dockerfile +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/Dockerfile @@ -50,15 +50,15 @@ WORKDIR /app # Copying the top level 'node_modules' because it contains the Prisma packages # necessary for migrating the database. COPY --from=server-builder /app/node_modules ./node_modules -# Copying the SDK because 'validate-env.mjs' executes independent of the bundle +# Copying the SDK because the server bundle doesn't bundle the SDK # and references the 'wasp' package. COPY --from=server-builder /app/.wasp/out/sdk .wasp/out/sdk -# Copying 'server/node_modules' because 'validate-env.mjs' executes independent -# of the bundle and references the dotenv package. +# Copying 'server/node_modules' because we require dotenv package to +# load environment variables +# TODO: replace dotenv with native Node.js environment variable loading COPY --from=server-builder /app/.wasp/build/server/node_modules .wasp/build/server/node_modules COPY --from=server-builder /app/.wasp/build/server/bundle .wasp/build/server/bundle COPY --from=server-builder /app/.wasp/build/server/package*.json .wasp/build/server/ -COPY --from=server-builder /app/.wasp/build/server/scripts .wasp/build/server/scripts COPY db/ .wasp/build/db/ EXPOSE ${PORT} WORKDIR /app/.wasp/build/server diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/installedNpmDepsLog.json b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/installedNpmDepsLog.json index a21ddcd4a1..d9bee4d2fa 100644 --- a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/installedNpmDepsLog.json +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/installedNpmDepsLog.json @@ -1 +1 @@ -{"_waspSdkNpmDeps":{"dependencies":[{"name":"@prisma/client","version":"5.19.1"},{"name":"prisma","version":"5.19.1"},{"name":"@tanstack/react-query","version":"^4.29.0"},{"name":"axios","version":"^1.4.0"},{"name":"express","version":"~4.21.0"},{"name":"mitt","version":"3.0.0"},{"name":"react","version":"^18.2.0"},{"name":"lodash.merge","version":"^4.6.2"},{"name":"react-router-dom","version":"^6.26.2"},{"name":"react-hook-form","version":"^7.45.4"},{"name":"superjson","version":"^2.2.1"},{"name":"vitest","version":"^1.2.1"},{"name":"@vitest/ui","version":"^1.2.1"},{"name":"jsdom","version":"^21.1.1"},{"name":"@testing-library/react","version":"^14.1.2"},{"name":"@testing-library/jest-dom","version":"^6.3.0"},{"name":"msw","version":"^1.1.0"}],"devDependencies":[{"name":"@tsconfig/node18","version":"latest"},{"name":"@types/express-serve-static-core","version":"^4.17.13"}]},"_userNpmDeps":{"userDependencies":[{"name":"react","version":"^18.2.0"},{"name":"react-dom","version":"^18.2.0"},{"name":"react-router-dom","version":"^6.26.2"},{"name":"wasp","version":"file:.wasp/out/sdk/wasp"}],"userDevDependencies":[{"name":"@types/react","version":"^18.0.37"},{"name":"prisma","version":"5.19.1"},{"name":"typescript","version":"^5.1.0"},{"name":"vite","version":"^4.3.9"}]},"_waspFrameworkNpmDeps":{"npmDepsForWebApp":{"dependencies":[{"name":"@tanstack/react-query","version":"^4.29.0"},{"name":"axios","version":"^1.4.0"}],"devDependencies":[{"name":"@tsconfig/vite-react","version":"^2.0.0"},{"name":"@types/react-dom","version":"^18.0.11"},{"name":"@vitejs/plugin-react","version":"^4.2.1"},{"name":"dotenv","version":"^16.0.3"}]},"npmDepsForServer":{"dependencies":[{"name":"cookie-parser","version":"~1.4.6"},{"name":"cors","version":"^2.8.5"},{"name":"dotenv","version":"^16.0.2"},{"name":"express","version":"~4.21.0"},{"name":"helmet","version":"^6.0.0"},{"name":"morgan","version":"~1.10.0"},{"name":"superjson","version":"^2.2.1"}],"devDependencies":[{"name":"@tsconfig/node18","version":"latest"},{"name":"@types/cors","version":"^2.8.5"},{"name":"@types/express","version":"^4.17.13"},{"name":"@types/express-serve-static-core","version":"^4.17.13"},{"name":"@types/node","version":"^18.0.0"},{"name":"nodemon","version":"^2.0.19"},{"name":"rollup","version":"^4.9.6"},{"name":"rollup-plugin-esbuild","version":"^6.1.1"}]}}} \ No newline at end of file +{"_waspSdkNpmDeps":{"dependencies":[{"name":"@prisma/client","version":"5.19.1"},{"name":"prisma","version":"5.19.1"},{"name":"@tanstack/react-query","version":"^4.29.0"},{"name":"axios","version":"^1.4.0"},{"name":"express","version":"~4.21.0"},{"name":"mitt","version":"3.0.0"},{"name":"react","version":"^18.2.0"},{"name":"react-router-dom","version":"^6.26.2"},{"name":"react-hook-form","version":"^7.45.4"},{"name":"superjson","version":"^2.2.1"},{"name":"vitest","version":"^1.2.1"},{"name":"@vitest/ui","version":"^1.2.1"},{"name":"jsdom","version":"^21.1.1"},{"name":"@testing-library/react","version":"^14.1.2"},{"name":"@testing-library/jest-dom","version":"^6.3.0"},{"name":"msw","version":"^1.1.0"},{"name":"zod","version":"^3.23.8"}],"devDependencies":[{"name":"@tsconfig/node18","version":"latest"},{"name":"@types/express-serve-static-core","version":"^4.17.13"}]},"_userNpmDeps":{"userDependencies":[{"name":"react","version":"^18.2.0"},{"name":"react-dom","version":"^18.2.0"},{"name":"react-router-dom","version":"^6.26.2"},{"name":"wasp","version":"file:.wasp/out/sdk/wasp"}],"userDevDependencies":[{"name":"@types/react","version":"^18.0.37"},{"name":"prisma","version":"5.19.1"},{"name":"typescript","version":"^5.1.0"},{"name":"vite","version":"^4.3.9"}]},"_waspFrameworkNpmDeps":{"npmDepsForWebApp":{"dependencies":[{"name":"@tanstack/react-query","version":"^4.29.0"},{"name":"axios","version":"^1.4.0"}],"devDependencies":[{"name":"@tsconfig/vite-react","version":"^2.0.0"},{"name":"@types/react-dom","version":"^18.0.11"},{"name":"@vitejs/plugin-react","version":"^4.2.1"}]},"npmDepsForServer":{"dependencies":[{"name":"cookie-parser","version":"~1.4.6"},{"name":"cors","version":"^2.8.5"},{"name":"dotenv","version":"^16.0.2"},{"name":"express","version":"~4.21.0"},{"name":"helmet","version":"^6.0.0"},{"name":"morgan","version":"~1.10.0"},{"name":"superjson","version":"^2.2.1"}],"devDependencies":[{"name":"@tsconfig/node18","version":"latest"},{"name":"@types/cors","version":"^2.8.5"},{"name":"@types/express","version":"^4.17.13"},{"name":"@types/express-serve-static-core","version":"^4.17.13"},{"name":"@types/node","version":"^18.0.0"},{"name":"nodemon","version":"^2.0.19"},{"name":"rollup","version":"^4.9.6"},{"name":"rollup-plugin-esbuild","version":"^6.1.1"}]}}} \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/client/config.ts b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/client/config.ts index 82b0f80b6a..fff77cb01c 100644 --- a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/client/config.ts +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/client/config.ts @@ -1,6 +1,7 @@ import { stripTrailingSlash } from '../universal/url.js' +import { env } from './env.js' -const apiUrl = stripTrailingSlash(import.meta.env.REACT_APP_API_URL) || 'http://localhost:3001'; +const apiUrl = stripTrailingSlash(env.REACT_APP_API_URL) // PUBLIC API export type ClientConfig = { diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/client/env.ts b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/client/env.ts new file mode 100644 index 0000000000..8ddab287f7 --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/client/env.ts @@ -0,0 +1,18 @@ +import * as z from 'zod' + +import { ensureEnvSchema } from '../env/validation.js' + +const userClientEnvSchema = z.object({}) + +const waspClientEnvSchema = z.object({ + REACT_APP_API_URL: z + .string({ + required_error: 'REACT_APP_API_URL is required', + }) + .default('http://localhost:3001') +}) + +const clientEnvSchema = userClientEnvSchema.merge(waspClientEnvSchema) + +// PUBLIC API +export const env = ensureEnvSchema(import.meta.env, clientEnvSchema) diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/client/index.ts b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/client/index.ts index cfce564c4d..859f8656b6 100644 --- a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/client/index.ts +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/client/index.ts @@ -11,4 +11,7 @@ export enum HttpMethod { export type Route = { method: HttpMethod; path: string } // PUBLIC API -export { config, ClientConfig } from './config' +export { config, ClientConfig } from './config.js' + +// PUBLIC API +export { env } from './env.js' diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/client/config.js b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/client/config.js index 1dffbd68bc..7e6149f386 100644 --- a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/client/config.js +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/client/config.js @@ -1,5 +1,6 @@ import { stripTrailingSlash } from '../universal/url.js'; -const apiUrl = stripTrailingSlash(import.meta.env.REACT_APP_API_URL) || 'http://localhost:3001'; +import { env } from './env.js'; +const apiUrl = stripTrailingSlash(env.REACT_APP_API_URL); // PUBLIC API export const config = { apiUrl, diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/client/config.js.map b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/client/config.js.map index d26d37022b..f9e186b838 100644 --- a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/client/config.js.map +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/client/config.js.map @@ -1 +1 @@ -{"version":3,"file":"config.js","sourceRoot":"","sources":["../../client/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAA;AAExD,MAAM,MAAM,GAAG,kBAAkB,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,iBAAiB,CAAC,IAAI,uBAAuB,CAAC;AAOhG,aAAa;AACb,MAAM,CAAC,MAAM,MAAM,GAAiB;IAClC,MAAM;CACP,CAAA"} \ No newline at end of file +{"version":3,"file":"config.js","sourceRoot":"","sources":["../../client/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAA;AACxD,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAA;AAE9B,MAAM,MAAM,GAAG,kBAAkB,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAA;AAOxD,aAAa;AACb,MAAM,CAAC,MAAM,MAAM,GAAiB;IAClC,MAAM;CACP,CAAA"} \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/client/env.d.ts b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/client/env.d.ts new file mode 100644 index 0000000000..5a9417a810 --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/client/env.d.ts @@ -0,0 +1,3 @@ +export declare const env: { + REACT_APP_API_URL: string; +}; diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/client/env.js b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/client/env.js new file mode 100644 index 0000000000..91164c3768 --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/client/env.js @@ -0,0 +1,14 @@ +import * as z from 'zod'; +import { ensureEnvSchema } from '../env/validation.js'; +const userClientEnvSchema = z.object({}); +const waspClientEnvSchema = z.object({ + REACT_APP_API_URL: z + .string({ + required_error: 'REACT_APP_API_URL is required', + }) + .default('http://localhost:3001') +}); +const clientEnvSchema = userClientEnvSchema.merge(waspClientEnvSchema); +// PUBLIC API +export const env = ensureEnvSchema(import.meta.env, clientEnvSchema); +//# sourceMappingURL=env.js.map \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/client/env.js.map b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/client/env.js.map new file mode 100644 index 0000000000..d230f15c0d --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/client/env.js.map @@ -0,0 +1 @@ +{"version":3,"file":"env.js","sourceRoot":"","sources":["../../client/env.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,KAAK,CAAA;AAExB,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAA;AAEtD,MAAM,mBAAmB,GAAG,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,CAAA;AAExC,MAAM,mBAAmB,GAAG,CAAC,CAAC,MAAM,CAAC;IACnC,iBAAiB,EAAE,CAAC;SACjB,MAAM,CAAC;QACN,cAAc,EAAE,+BAA+B;KAChD,CAAC;SACD,OAAO,CAAC,uBAAuB,CAAC;CACpC,CAAC,CAAA;AAEF,MAAM,eAAe,GAAG,mBAAmB,CAAC,KAAK,CAAC,mBAAmB,CAAC,CAAA;AAEtE,aAAa;AACb,MAAM,CAAC,MAAM,GAAG,GAAG,eAAe,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,eAAe,CAAC,CAAA"} \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/client/index.d.ts b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/client/index.d.ts index 7f5f4bfbc8..6498c439bd 100644 --- a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/client/index.d.ts +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/client/index.d.ts @@ -8,4 +8,5 @@ export type Route = { method: HttpMethod; path: string; }; -export { config, ClientConfig } from './config'; +export { config, ClientConfig } from './config.js'; +export { env } from './env.js'; diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/client/index.js b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/client/index.js index 5a7f13dcae..2f4daa7ae3 100644 --- a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/client/index.js +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/client/index.js @@ -8,5 +8,7 @@ export var HttpMethod; HttpMethod["Delete"] = "DELETE"; })(HttpMethod || (HttpMethod = {})); // PUBLIC API -export { config } from './config'; +export { config } from './config.js'; +// PUBLIC API +export { env } from './env.js'; //# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/client/index.js.map b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/client/index.js.map index 879953c8df..f3611558dc 100644 --- a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/client/index.js.map +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/client/index.js.map @@ -1 +1 @@ -{"version":3,"file":"index.js","sourceRoot":"","sources":["../../client/index.ts"],"names":[],"mappings":"AAAA,aAAa;AACb,mFAAmF;AACnF,MAAM,CAAN,IAAY,UAKX;AALD,WAAY,UAAU;IACrB,yBAAW,CAAA;IACX,2BAAa,CAAA;IACb,yBAAW,CAAA;IACX,+BAAiB,CAAA;AAClB,CAAC,EALW,UAAU,KAAV,UAAU,QAKrB;AAKD,aAAa;AACb,OAAO,EAAE,MAAM,EAAgB,MAAM,UAAU,CAAA"} \ No newline at end of file +{"version":3,"file":"index.js","sourceRoot":"","sources":["../../client/index.ts"],"names":[],"mappings":"AAAA,aAAa;AACb,mFAAmF;AACnF,MAAM,CAAN,IAAY,UAKX;AALD,WAAY,UAAU;IACrB,yBAAW,CAAA;IACX,2BAAa,CAAA;IACb,yBAAW,CAAA;IACX,+BAAiB,CAAA;AAClB,CAAC,EALW,UAAU,KAAV,UAAU,QAKrB;AAKD,aAAa;AACb,OAAO,EAAE,MAAM,EAAgB,MAAM,aAAa,CAAA;AAElD,aAAa;AACb,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAA"} \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/env/index.d.ts b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/env/index.d.ts new file mode 100644 index 0000000000..d1dafc25fd --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/env/index.d.ts @@ -0,0 +1,3 @@ +import * as z from 'zod'; +export declare function defineEnvValidationSchema>(schema: Schema): Schema; +export { ensureEnvSchema } from './validation.js'; diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/env/index.js b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/env/index.js new file mode 100644 index 0000000000..17e695d087 --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/env/index.js @@ -0,0 +1,7 @@ +// PUBLIC API +export function defineEnvValidationSchema(schema) { + return schema; +} +// PRIVATE API (SDK, Vite config) +export { ensureEnvSchema } from './validation.js'; +//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/env/index.js.map b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/env/index.js.map new file mode 100644 index 0000000000..75b4ff7de3 --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/env/index.js.map @@ -0,0 +1 @@ +{"version":3,"file":"index.js","sourceRoot":"","sources":["../../env/index.ts"],"names":[],"mappings":"AAEA,aAAa;AACb,MAAM,UAAU,yBAAyB,CACvC,MAAc;IAEd,OAAO,MAAM,CAAA;AACf,CAAC;AAED,iCAAiC;AACjC,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA"} \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/env/validation.d.ts b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/env/validation.d.ts new file mode 100644 index 0000000000..6bc9d059a3 --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/env/validation.d.ts @@ -0,0 +1,2 @@ +import * as z from 'zod'; +export declare function ensureEnvSchema(data: unknown, schema: Schema): z.infer; diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/env/validation.js b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/env/validation.js new file mode 100644 index 0000000000..8463eb578e --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/env/validation.js @@ -0,0 +1,23 @@ +import * as z from 'zod'; +const redColor = '\x1b[31m'; +export function ensureEnvSchema(data, schema) { + try { + return schema.parse(data); + } + catch (e) { + if (e instanceof z.ZodError) { + const errorOutput = ['', 'โ•โ• Env vars validation failed โ•โ•', '']; + for (const error of e.errors) { + errorOutput.push(` - ${error.message}`); + } + errorOutput.push(''); + errorOutput.push('โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•'); + console.error(redColor, errorOutput.join('\n')); + throw new Error('Error parsing environment variables'); + } + else { + throw e; + } + } +} +//# sourceMappingURL=validation.js.map \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/env/validation.js.map b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/env/validation.js.map new file mode 100644 index 0000000000..1d57b14a0b --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/env/validation.js.map @@ -0,0 +1 @@ +{"version":3,"file":"validation.js","sourceRoot":"","sources":["../../env/validation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,KAAK,CAAA;AAExB,MAAM,QAAQ,GAAG,UAAU,CAAA;AAE3B,MAAM,UAAU,eAAe,CAC7B,IAAa,EACb,MAAc;IAEd,IAAI,CAAC;QACH,OAAO,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;IAC3B,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,IAAI,CAAC,YAAY,CAAC,CAAC,QAAQ,EAAE,CAAC;YAC5B,MAAM,WAAW,GAAG,CAAC,EAAE,EAAE,kCAAkC,EAAE,EAAE,CAAC,CAAA;YAChE,KAAK,MAAM,KAAK,IAAI,CAAC,CAAC,MAAM,EAAE,CAAC;gBAC7B,WAAW,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC,OAAO,EAAE,CAAC,CAAA;YACzC,CAAC;YACD,WAAW,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;YACpB,WAAW,CAAC,IAAI,CAAC,kCAAkC,CAAC,CAAA;YACpD,OAAO,CAAC,KAAK,CAAC,QAAQ,EAAE,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAA;YAC/C,MAAM,IAAI,KAAK,CAAC,qCAAqC,CAAC,CAAA;QACxD,CAAC;aAAM,CAAC;YACN,MAAM,CAAC,CAAA;QACT,CAAC;IACH,CAAC;AACH,CAAC"} \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/server/HttpError.js.map b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/server/HttpError.js.map index 8163354e1b..462e1c1811 100644 --- a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/server/HttpError.js.map +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/server/HttpError.js.map @@ -1 +1 @@ -{"version":3,"file":"HttpError.js","sourceRoot":"","sources":["../../server/HttpError.ts"],"names":[],"mappings":"AAAA,MAAM,OAAO,SAAU,SAAQ,KAAK;IAIlC,YAAa,UAAkB,EAAE,OAAgB,EAAE,IAA8B,EAAE,OAAsB;QACvG,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;QAEvB,IAAI,KAAK,CAAC,iBAAiB,EAAE,CAAC;YAC5B,KAAK,CAAC,iBAAiB,CAAC,IAAI,EAAE,SAAS,CAAC,CAAA;QAC1C,CAAC;QAED,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,WAAW,CAAC,IAAI,CAAA;QAEjC,IAAI,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,UAAU,CAAC,IAAI,UAAU,IAAI,GAAG,IAAI,UAAU,GAAG,GAAG,CAAC,EAAE,CAAC;YAC7E,MAAM,IAAI,KAAK,CAAC,mDAAmD,CAAC,CAAA;QACtE,CAAC;QACD,IAAI,CAAC,UAAU,GAAG,UAAU,CAAA;QAE5B,IAAI,IAAI,EAAE,CAAC;YACT,IAAI,CAAC,IAAI,GAAG,IAAI,CAAA;QAClB,CAAC;IACH,CAAC;CACF"} \ No newline at end of file +{"version":3,"file":"HttpError.js","sourceRoot":"","sources":["../../server/HttpError.ts"],"names":[],"mappings":"AAAA,MAAM,OAAO,SAAU,SAAQ,KAAK;IAIlC,YACE,UAAkB,EAClB,OAAgB,EAChB,IAA8B,EAC9B,OAAsB;QAEtB,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;QAEvB,IAAI,KAAK,CAAC,iBAAiB,EAAE,CAAC;YAC5B,KAAK,CAAC,iBAAiB,CAAC,IAAI,EAAE,SAAS,CAAC,CAAA;QAC1C,CAAC;QAED,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,WAAW,CAAC,IAAI,CAAA;QAEjC,IACE,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,UAAU,CAAC,IAAI,UAAU,IAAI,GAAG,IAAI,UAAU,GAAG,GAAG,CAAC,EACxE,CAAC;YACD,MAAM,IAAI,KAAK,CAAC,mDAAmD,CAAC,CAAA;QACtE,CAAC;QACD,IAAI,CAAC,UAAU,GAAG,UAAU,CAAA;QAE5B,IAAI,IAAI,EAAE,CAAC;YACT,IAAI,CAAC,IAAI,GAAG,IAAI,CAAA;QAClB,CAAC;IACH,CAAC;CACF"} \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/server/config.d.ts b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/server/config.d.ts index 68bb448c56..70f438bdf0 100644 --- a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/server/config.d.ts +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/server/config.d.ts @@ -1,16 +1,13 @@ -type BaseConfig = { - allowedCORSOrigins: string | string[]; -}; -type CommonConfig = BaseConfig & { - env: string; +import { env } from './env.js'; +type NodeEnv = typeof env.NODE_ENV; +type Config = { + env: NodeEnv; isDevelopment: boolean; port: number; - databaseUrl: string | undefined; -}; -type EnvConfig = BaseConfig & { + databaseUrl: string; frontendUrl: string; serverUrl: string; + allowedCORSOrigins: string | string[]; }; -type Config = CommonConfig & EnvConfig; -declare const resolvedConfig: Config; -export default resolvedConfig; +declare const config: Config; +export default config; diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/server/config.js b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/server/config.js index e4f89082c8..dcc80722ab 100644 --- a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/server/config.js +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/server/config.js @@ -1,43 +1,21 @@ -var _a; -import merge from 'lodash.merge'; -import { stripTrailingSlash } from "../universal/url.js"; -const nodeEnv = (_a = process.env.NODE_ENV) !== null && _a !== void 0 ? _a : 'development'; +import { env } from './env.js'; +import { stripTrailingSlash } from '../universal/url.js'; +const frontendUrl = stripTrailingSlash(env.WASP_WEB_CLIENT_URL); +const serverUrl = stripTrailingSlash(env.WASP_SERVER_URL); +const allowedCORSOriginsPerEnv = { + development: '*', + production: [frontendUrl] +}; +const allowedCORSOrigins = allowedCORSOriginsPerEnv[env.NODE_ENV]; const config = { - all: { - env: nodeEnv, - isDevelopment: nodeEnv === 'development', - port: process.env.PORT ? parseInt(process.env.PORT) : 3001, - databaseUrl: process.env.DATABASE_URL, - allowedCORSOrigins: [], - }, - development: getDevelopmentConfig(), - production: getProductionConfig(), + frontendUrl, + serverUrl, + allowedCORSOrigins, + env: env.NODE_ENV, + isDevelopment: env.NODE_ENV === 'development', + port: env.PORT, + databaseUrl: env.DATABASE_URL, }; -const resolvedConfig = merge(config.all, config[nodeEnv]); // PUBLIC API -export default resolvedConfig; -function getDevelopmentConfig() { - var _a, _b; - const frontendUrl = stripTrailingSlash((_a = process.env.WASP_WEB_CLIENT_URL) !== null && _a !== void 0 ? _a : 'http://localhost:3000/'); - const serverUrl = stripTrailingSlash((_b = process.env.WASP_SERVER_URL) !== null && _b !== void 0 ? _b : 'http://localhost:3001'); - return { - // @ts-ignore - frontendUrl, - // @ts-ignore - serverUrl, - allowedCORSOrigins: '*', - }; -} -function getProductionConfig() { - const frontendUrl = stripTrailingSlash(process.env.WASP_WEB_CLIENT_URL); - const serverUrl = stripTrailingSlash(process.env.WASP_SERVER_URL); - return { - // @ts-ignore - frontendUrl, - // @ts-ignore - serverUrl, - // @ts-ignore - allowedCORSOrigins: [frontendUrl], - }; -} +export default config; //# sourceMappingURL=config.js.map \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/server/config.js.map b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/server/config.js.map index e284580bbd..9fd077fa81 100644 --- a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/server/config.js.map +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/server/config.js.map @@ -1 +1 @@ -{"version":3,"file":"config.js","sourceRoot":"","sources":["../../server/config.ts"],"names":[],"mappings":";AAAA,OAAO,KAAK,MAAM,cAAc,CAAA;AAEhC,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAEzD,MAAM,OAAO,GAAG,MAAA,OAAO,CAAC,GAAG,CAAC,QAAQ,mCAAI,aAAa,CAAA;AAyBrD,MAAM,MAAM,GAIR;IACF,GAAG,EAAE;QACH,GAAG,EAAE,OAAO;QACZ,aAAa,EAAE,OAAO,KAAK,aAAa;QACxC,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI;QAC1D,WAAW,EAAE,OAAO,CAAC,GAAG,CAAC,YAAY;QACrC,kBAAkB,EAAE,EAAE;KACvB;IACD,WAAW,EAAE,oBAAoB,EAAE;IACnC,UAAU,EAAE,mBAAmB,EAAE;CAClC,CAAA;AAED,MAAM,cAAc,GAAW,KAAK,CAAC,MAAM,CAAC,GAAG,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC,CAAA;AACjE,aAAa;AACb,eAAe,cAAc,CAAA;AAE7B,SAAS,oBAAoB;;IAC3B,MAAM,WAAW,GAAG,kBAAkB,CAAC,MAAA,OAAO,CAAC,GAAG,CAAC,mBAAmB,mCAAI,wBAAwB,CAAC,CAAC;IACpG,MAAM,SAAS,GAAG,kBAAkB,CAAC,MAAA,OAAO,CAAC,GAAG,CAAC,eAAe,mCAAI,uBAAuB,CAAC,CAAC;IAC7F,OAAO;QACL,aAAa;QACb,WAAW;QACX,aAAa;QACb,SAAS;QACT,kBAAkB,EAAE,GAAG;KACxB,CAAA;AACH,CAAC;AAED,SAAS,mBAAmB;IAC1B,MAAM,WAAW,GAAG,kBAAkB,CAAC,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC,CAAC;IACxE,MAAM,SAAS,GAAG,kBAAkB,CAAC,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC;IAClE,OAAO;QACL,aAAa;QACb,WAAW;QACX,aAAa;QACb,SAAS;QACT,aAAa;QACb,kBAAkB,EAAE,CAAC,WAAW,CAAC;KAClC,CAAA;AACH,CAAC"} \ No newline at end of file +{"version":3,"file":"config.js","sourceRoot":"","sources":["../../server/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAA;AAC9B,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAA;AAcxD,MAAM,WAAW,GAAG,kBAAkB,CAAC,GAAG,CAAC,mBAAmB,CAAC,CAAA;AAC/D,MAAM,SAAS,GAAG,kBAAkB,CAAC,GAAG,CAAC,eAAe,CAAC,CAAA;AAEzD,MAAM,wBAAwB,GAAuC;IACnE,WAAW,EAAE,GAAG;IAChB,UAAU,EAAE,CAAC,WAAW,CAAC;CAC1B,CAAA;AACD,MAAM,kBAAkB,GAAG,wBAAwB,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAA;AAEjE,MAAM,MAAM,GAAW;IACrB,WAAW;IACX,SAAS;IACT,kBAAkB;IAClB,GAAG,EAAE,GAAG,CAAC,QAAQ;IACjB,aAAa,EAAE,GAAG,CAAC,QAAQ,KAAK,aAAa;IAC7C,IAAI,EAAE,GAAG,CAAC,IAAI;IACd,WAAW,EAAE,GAAG,CAAC,YAAY;CAC9B,CAAA;AAED,aAAa;AACb,eAAe,MAAM,CAAA"} \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/server/env.d.ts b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/server/env.d.ts new file mode 100644 index 0000000000..667ea30e35 --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/server/env.d.ts @@ -0,0 +1,17 @@ +export declare const env: { + PORT: number; + DATABASE_URL: string; + SKIP_EMAIL_VERIFICATION_IN_DEV: boolean; + NODE_ENV: "development"; + WASP_SERVER_URL: string; + WASP_WEB_CLIENT_URL: string; + PG_BOSS_NEW_OPTIONS?: string | undefined; +} | { + PORT: number; + DATABASE_URL: string; + SKIP_EMAIL_VERIFICATION_IN_DEV: boolean; + NODE_ENV: "production"; + WASP_SERVER_URL: string; + WASP_WEB_CLIENT_URL: string; + PG_BOSS_NEW_OPTIONS?: string | undefined; +}; diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/server/env.js b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/server/env.js new file mode 100644 index 0000000000..1bfc3e0b6e --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/server/env.js @@ -0,0 +1,55 @@ +import * as z from 'zod'; +import { ensureEnvSchema } from '../env/validation.js'; +const userServerEnvSchema = z.object({}); +const waspServerCommonSchema = z.object({ + PORT: z.coerce.number().default(3001), + DATABASE_URL: z.string({ + required_error: 'DATABASE_URL is required', + }), + PG_BOSS_NEW_OPTIONS: z.string().optional(), + SKIP_EMAIL_VERIFICATION_IN_DEV: z + .enum(['true', 'false'], { + message: 'SKIP_EMAIL_VERIFICATION_IN_DEV must be either "true" or "false"', + }) + .transform((value) => value === 'true') + .default('false'), +}); +const serverUrlSchema = z + .string({ + required_error: 'WASP_SERVER_URL is required', +}) + .url({ + message: 'WASP_SERVER_URL must be a valid URL', +}); +const clientUrlSchema = z + .string({ + required_error: 'WASP_WEB_CLIENT_URL is required', +}) + .url({ + message: 'WASP_WEB_CLIENT_URL must be a valid URL', +}); +// In development, we provide default values for some environment variables +// to make the development process easier. +const serverDevSchema = z.object({ + NODE_ENV: z.literal('development'), + WASP_SERVER_URL: serverUrlSchema + .default('http://localhost:3001'), + WASP_WEB_CLIENT_URL: clientUrlSchema + .default('http://localhost:3000/'), +}); +const serverProdSchema = z.object({ + NODE_ENV: z.literal('production'), + WASP_SERVER_URL: serverUrlSchema, + WASP_WEB_CLIENT_URL: clientUrlSchema, +}); +const serverCommonSchema = userServerEnvSchema.merge(waspServerCommonSchema); +const serverEnvSchema = z.discriminatedUnion('NODE_ENV', [ + serverDevSchema.merge(serverCommonSchema), + serverProdSchema.merge(serverCommonSchema) +]); +// PUBLIC API +export const env = ensureEnvSchema(process.env, serverEnvSchema); +function getRequiredEnvVarErrorMessage(featureName, envVarName) { + return `${envVarName} is required when using ${featureName}`; +} +//# sourceMappingURL=env.js.map \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/server/env.js.map b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/server/env.js.map new file mode 100644 index 0000000000..3dfa10e116 --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/server/env.js.map @@ -0,0 +1 @@ +{"version":3,"file":"env.js","sourceRoot":"","sources":["../../server/env.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,KAAK,CAAA;AAExB,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAA;AAEtD,MAAM,mBAAmB,GAAG,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,CAAA;AAExC,MAAM,sBAAsB,GAAG,CAAC,CAAC,MAAM,CAAC;IACtC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC;IACrC,YAAY,EAAE,CAAC,CAAC,MAAM,CAAC;QACrB,cAAc,EAAE,0BAA0B;KAC3C,CAAC;IACF,mBAAmB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC1C,8BAA8B,EAAE,CAAC;SAC9B,IAAI,CAAC,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE;QACvB,OAAO,EAAE,iEAAiE;KAC3E,CAAC;SACD,SAAS,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,KAAK,MAAM,CAAC;SACtC,OAAO,CAAC,OAAO,CAAC;CACpB,CAAC,CAAA;AAEF,MAAM,eAAe,GAAG,CAAC;KACtB,MAAM,CAAC;IACN,cAAc,EAAE,6BAA6B;CAC9C,CAAC;KACD,GAAG,CAAC;IACH,OAAO,EAAE,qCAAqC;CAC/C,CAAC,CAAA;AAEJ,MAAM,eAAe,GAAG,CAAC;KACtB,MAAM,CAAC;IACN,cAAc,EAAE,iCAAiC;CAClD,CAAC;KACD,GAAG,CAAC;IACH,OAAO,EAAE,yCAAyC;CACnD,CAAC,CAAA;AAGJ,2EAA2E;AAC3E,0CAA0C;AAC1C,MAAM,eAAe,GAAG,CAAC,CAAC,MAAM,CAAC;IAC/B,QAAQ,EAAE,CAAC,CAAC,OAAO,CAAC,aAAa,CAAC;IAClC,eAAe,EAAE,eAAe;SAC7B,OAAO,CAAC,uBAAuB,CAAC;IACnC,mBAAmB,EAAE,eAAe;SACjC,OAAO,CAAC,wBAAwB,CAAC;CACrC,CAAC,CAAA;AAEF,MAAM,gBAAgB,GAAG,CAAC,CAAC,MAAM,CAAC;IAChC,QAAQ,EAAE,CAAC,CAAC,OAAO,CAAC,YAAY,CAAC;IACjC,eAAe,EAAE,eAAe;IAChC,mBAAmB,EAAE,eAAe;CACrC,CAAC,CAAA;AAEF,MAAM,kBAAkB,GAAG,mBAAmB,CAAC,KAAK,CAAC,sBAAsB,CAAC,CAAA;AAC5E,MAAM,eAAe,GAAG,CAAC,CAAC,kBAAkB,CAAC,UAAU,EAAE;IACvD,eAAe,CAAC,KAAK,CAAC,kBAAkB,CAAC;IACzC,gBAAgB,CAAC,KAAK,CAAC,kBAAkB,CAAC;CAC3C,CAAC,CAAA;AAEF,aAAa;AACb,MAAM,CAAC,MAAM,GAAG,GAAG,eAAe,CAAC,OAAO,CAAC,GAAG,EAAE,eAAe,CAAC,CAAA;AAEhE,SAAS,6BAA6B,CAAC,WAAmB,EAAE,UAAkB;IAC5E,OAAO,GAAG,UAAU,2BAA2B,WAAW,EAAE,CAAA;AAC9D,CAAC"} \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/server/index.d.ts b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/server/index.d.ts index dee9613a53..a108f6123f 100644 --- a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/server/index.d.ts +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/server/index.d.ts @@ -4,4 +4,5 @@ export { default as prisma } from './dbClient.js'; export { type ServerSetupFn } from './types/index.js'; export { HttpError } from './HttpError.js'; export { MiddlewareConfigFn } from './middleware/index.js'; +export { env } from './env.js'; export type DbSeedFn = (prisma: PrismaClient) => Promise; diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/server/index.js b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/server/index.js index 0e53e0deab..8e75e6f78c 100644 --- a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/server/index.js +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/server/index.js @@ -4,4 +4,6 @@ export { default as config } from './config.js'; export { default as prisma } from './dbClient.js'; // PUBLIC API export { HttpError } from './HttpError.js'; +// PUBLIC API +export { env } from './env.js'; //# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/server/index.js.map b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/server/index.js.map index b84ac1b303..87ed22f3af 100644 --- a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/server/index.js.map +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/server/index.js.map @@ -1 +1 @@ -{"version":3,"file":"index.js","sourceRoot":"","sources":["../../server/index.ts"],"names":[],"mappings":"AAEA,aAAa;AACb,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,aAAa,CAAA;AAC/C,aAAa;AACb,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,eAAe,CAAA;AAGjD,aAAa;AACb,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA"} \ No newline at end of file +{"version":3,"file":"index.js","sourceRoot":"","sources":["../../server/index.ts"],"names":[],"mappings":"AAEA,aAAa;AACb,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,aAAa,CAAA;AAC/C,aAAa;AACb,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,eAAe,CAAA;AAGjD,aAAa;AACb,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAG1C,aAAa;AACb,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAA"} \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/universal/url.d.ts b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/universal/url.d.ts index aa893e7838..7ceb994694 100644 --- a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/universal/url.d.ts +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/universal/url.d.ts @@ -1 +1,2 @@ -export declare function stripTrailingSlash(url?: string): string | undefined; +export declare function stripTrailingSlash(url: string): string; +export declare function stripTrailingSlash(url: undefined): undefined; diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/universal/url.js.map b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/universal/url.js.map index a7bad96020..e236f1b977 100644 --- a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/universal/url.js.map +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/universal/url.js.map @@ -1 +1 @@ -{"version":3,"file":"url.js","sourceRoot":"","sources":["../../universal/url.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,kBAAkB,CAAC,GAAY;IAC3C,OAAO,GAAG,aAAH,GAAG,uBAAH,GAAG,CAAE,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;AACnC,CAAC"} \ No newline at end of file +{"version":3,"file":"url.js","sourceRoot":"","sources":["../../universal/url.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,kBAAkB,CAAC,GAAY;IAC3C,OAAO,GAAG,aAAH,GAAG,uBAAH,GAAG,CAAE,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;AACnC,CAAC"} \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/env/index.ts b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/env/index.ts new file mode 100644 index 0000000000..4c783125c1 --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/env/index.ts @@ -0,0 +1,11 @@ +import * as z from 'zod' + +// PUBLIC API +export function defineEnvValidationSchema>( + schema: Schema, +): Schema { + return schema +} + +// PRIVATE API (SDK, Vite config) +export { ensureEnvSchema } from './validation.js' diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/env/validation.ts b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/env/validation.ts new file mode 100644 index 0000000000..a5bc334754 --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/env/validation.ts @@ -0,0 +1,25 @@ +import * as z from 'zod' + +const redColor = '\x1b[31m' + +export function ensureEnvSchema( + data: unknown, + schema: Schema +): z.infer { + try { + return schema.parse(data) + } catch (e) { + if (e instanceof z.ZodError) { + const errorOutput = ['', 'โ•โ• Env vars validation failed โ•โ•', ''] + for (const error of e.errors) { + errorOutput.push(` - ${error.message}`) + } + errorOutput.push('') + errorOutput.push('โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•') + console.error(redColor, errorOutput.join('\n')) + throw new Error('Error parsing environment variables') + } else { + throw e + } + } +} diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/package.json b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/package.json index 2589c5362f..55c07a5084 100644 --- a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/package.json +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/package.json @@ -8,7 +8,6 @@ "axios": "^1.4.0", "express": "~4.21.0", "jsdom": "^21.1.1", - "lodash.merge": "^4.6.2", "mitt": "3.0.0", "msw": "^1.1.0", "prisma": "5.19.1", @@ -16,7 +15,8 @@ "react-hook-form": "^7.45.4", "react-router-dom": "^6.26.2", "superjson": "^2.2.1", - "vitest": "^1.2.1" + "vitest": "^1.2.1", + "zod": "^3.23.8" }, "devDependencies": { "@tsconfig/node18": "latest", @@ -51,6 +51,7 @@ "./core/storage": "./dist/core/storage.js", "./dev": "./dist/dev/index.js", "./entities": "./dist/entities/index.js", + "./env": "./dist/env/index.js", "./ext-src/*": "./dist/ext-src/*.js", "./operations": "./dist/operations/index.js", "./operations/*": "./dist/operations/*", diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/server/HttpError.ts b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/server/HttpError.ts index d05a2c841b..bce964712d 100644 --- a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/server/HttpError.ts +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/server/HttpError.ts @@ -2,7 +2,12 @@ export class HttpError extends Error { public statusCode: number public data: unknown - constructor (statusCode: number, message?: string, data?: Record, options?: ErrorOptions) { + constructor( + statusCode: number, + message?: string, + data?: Record, + options?: ErrorOptions + ) { super(message, options) if (Error.captureStackTrace) { @@ -11,7 +16,9 @@ export class HttpError extends Error { this.name = this.constructor.name - if (!(Number.isInteger(statusCode) && statusCode >= 400 && statusCode < 600)) { + if ( + !(Number.isInteger(statusCode) && statusCode >= 400 && statusCode < 600) + ) { throw new Error('statusCode has to be integer in range [400, 600).') } this.statusCode = statusCode diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/server/config.ts b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/server/config.ts index 1dc9bafb93..3a000a897f 100644 --- a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/server/config.ts +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/server/config.ts @@ -1,73 +1,36 @@ -import merge from 'lodash.merge' +import { env } from './env.js' +import { stripTrailingSlash } from '../universal/url.js' -import { stripTrailingSlash } from "../universal/url.js"; +type NodeEnv = typeof env.NODE_ENV -const nodeEnv = process.env.NODE_ENV ?? 'development' - -// TODO: -// - Use dotenv library to consume env vars from a file. -// - Use convict library to define schema and validate env vars. -// https://codingsans.com/blog/node-config-best-practices - -type BaseConfig = { - allowedCORSOrigins: string | string[]; -} - -type CommonConfig = BaseConfig & { - env: string; +type Config = { + env: NodeEnv; isDevelopment: boolean; port: number; - databaseUrl: string | undefined; -} - -type EnvConfig = BaseConfig & { + databaseUrl: string; frontendUrl: string; serverUrl: string; + allowedCORSOrigins: string | string[]; } -type Config = CommonConfig & EnvConfig +const frontendUrl = stripTrailingSlash(env.WASP_WEB_CLIENT_URL) +const serverUrl = stripTrailingSlash(env.WASP_SERVER_URL) -const config: { - all: CommonConfig, - development: EnvConfig, - production: EnvConfig, -} = { - all: { - env: nodeEnv, - isDevelopment: nodeEnv === 'development', - port: process.env.PORT ? parseInt(process.env.PORT) : 3001, - databaseUrl: process.env.DATABASE_URL, - allowedCORSOrigins: [], - }, - development: getDevelopmentConfig(), - production: getProductionConfig(), +const allowedCORSOriginsPerEnv: Record = { + development: '*', + production: [frontendUrl] } - -const resolvedConfig: Config = merge(config.all, config[nodeEnv]) -// PUBLIC API -export default resolvedConfig - -function getDevelopmentConfig(): EnvConfig { - const frontendUrl = stripTrailingSlash(process.env.WASP_WEB_CLIENT_URL ?? 'http://localhost:3000/'); - const serverUrl = stripTrailingSlash(process.env.WASP_SERVER_URL ?? 'http://localhost:3001'); - return { - // @ts-ignore - frontendUrl, - // @ts-ignore - serverUrl, - allowedCORSOrigins: '*', - } +const allowedCORSOrigins = allowedCORSOriginsPerEnv[env.NODE_ENV] + +const config: Config = { + frontendUrl, + serverUrl, + allowedCORSOrigins, + env: env.NODE_ENV, + isDevelopment: env.NODE_ENV === 'development', + port: env.PORT, + databaseUrl: env.DATABASE_URL, } -function getProductionConfig(): EnvConfig { - const frontendUrl = stripTrailingSlash(process.env.WASP_WEB_CLIENT_URL); - const serverUrl = stripTrailingSlash(process.env.WASP_SERVER_URL); - return { - // @ts-ignore - frontendUrl, - // @ts-ignore - serverUrl, - // @ts-ignore - allowedCORSOrigins: [frontendUrl], - } -} +// PUBLIC API +export default config diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/server/env.ts b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/server/env.ts new file mode 100644 index 0000000000..c1bd8f5c0f --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/server/env.ts @@ -0,0 +1,65 @@ +import * as z from 'zod' + +import { ensureEnvSchema } from '../env/validation.js' + +const userServerEnvSchema = z.object({}) + +const waspServerCommonSchema = z.object({ + PORT: z.coerce.number().default(3001), + DATABASE_URL: z.string({ + required_error: 'DATABASE_URL is required', + }), + PG_BOSS_NEW_OPTIONS: z.string().optional(), + SKIP_EMAIL_VERIFICATION_IN_DEV: z + .enum(['true', 'false'], { + message: 'SKIP_EMAIL_VERIFICATION_IN_DEV must be either "true" or "false"', + }) + .transform((value) => value === 'true') + .default('false'), +}) + +const serverUrlSchema = z + .string({ + required_error: 'WASP_SERVER_URL is required', + }) + .url({ + message: 'WASP_SERVER_URL must be a valid URL', + }) + +const clientUrlSchema = z + .string({ + required_error: 'WASP_WEB_CLIENT_URL is required', + }) + .url({ + message: 'WASP_WEB_CLIENT_URL must be a valid URL', + }) + + +// In development, we provide default values for some environment variables +// to make the development process easier. +const serverDevSchema = z.object({ + NODE_ENV: z.literal('development'), + WASP_SERVER_URL: serverUrlSchema + .default('http://localhost:3001'), + WASP_WEB_CLIENT_URL: clientUrlSchema + .default('http://localhost:3000/'), +}) + +const serverProdSchema = z.object({ + NODE_ENV: z.literal('production'), + WASP_SERVER_URL: serverUrlSchema, + WASP_WEB_CLIENT_URL: clientUrlSchema, +}) + +const serverCommonSchema = userServerEnvSchema.merge(waspServerCommonSchema) +const serverEnvSchema = z.discriminatedUnion('NODE_ENV', [ + serverDevSchema.merge(serverCommonSchema), + serverProdSchema.merge(serverCommonSchema) +]) + +// PUBLIC API +export const env = ensureEnvSchema(process.env, serverEnvSchema) + +function getRequiredEnvVarErrorMessage(featureName: string, envVarName: string) { + return `${envVarName} is required when using ${featureName}` +} diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/server/index.ts b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/server/index.ts index 535077900b..0f95deb90a 100644 --- a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/server/index.ts +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/server/index.ts @@ -10,6 +10,8 @@ export { type ServerSetupFn } from './types/index.js' export { HttpError } from './HttpError.js' // PUBLIC API export { MiddlewareConfigFn } from './middleware/index.js' +// PUBLIC API +export { env } from './env.js' // PUBLIC API export type DbSeedFn = (prisma: PrismaClient) => Promise diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/universal/url.ts b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/universal/url.ts index d21c06c65c..126678dac9 100644 --- a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/universal/url.ts +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/universal/url.ts @@ -1,3 +1,5 @@ +export function stripTrailingSlash(url: string): string +export function stripTrailingSlash(url: undefined): undefined export function stripTrailingSlash(url?: string): string | undefined { return url?.replace(/\/$/, ""); } diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/server/package.json b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/server/package.json index 7a57413e11..51ec87354a 100644 --- a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/server/package.json +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/server/package.json @@ -34,9 +34,8 @@ "bundle-and-start": "npm run bundle && npm run start", "db-migrate-prod": "prisma migrate deploy --schema=../db/schema.prisma", "db-seed": "npm run bundle && node --enable-source-maps -r dotenv/config bundle/dbSeed.js", - "start": "npm run validate-env && node --enable-source-maps -r dotenv/config bundle/server.js", + "start": "node --enable-source-maps -r dotenv/config bundle/server.js", "start-production": "NODE_ENV=production npm run start", - "validate-env": "node -r dotenv/config ./scripts/validate-env.mjs", "watch": "nodemon --exec 'npm run bundle-and-start || exit 1'" }, "type": "module", diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/server/scripts/validate-env.mjs b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/server/scripts/validate-env.mjs deleted file mode 100644 index f6e50aceca..0000000000 --- a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/server/scripts/validate-env.mjs +++ /dev/null @@ -1,5 +0,0 @@ -import { throwIfNotValidAbsoluteURL } from 'wasp/universal/validators'; - -console.info("๐Ÿ” Validating environment variables..."); -throwIfNotValidAbsoluteURL(process.env.WASP_WEB_CLIENT_URL, 'Environment variable WASP_WEB_CLIENT_URL'); -throwIfNotValidAbsoluteURL(process.env.WASP_SERVER_URL, 'Environment variable WASP_SERVER_URL'); diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/server/src/polyfill.ts b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/server/src/polyfill.ts index 7931d12eb0..9279daf6da 100644 --- a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/server/src/polyfill.ts +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/server/src/polyfill.ts @@ -1,11 +1,10 @@ // This is a polyfill for Node.js 18 webcrypto API so Lucia can use it // for random number generation. -import { webcrypto } from "node:crypto"; +import { webcrypto } from 'node:crypto' // NOTE: node < 19 doesn't have Crypto API, which we need for Lucia, so we apply the polyfill if Crypto API is not defined. -if (typeof globalThis.crypto === "undefined") { +if (typeof globalThis.crypto === 'undefined') { // @ts-ignore - globalThis.crypto = webcrypto as Crypto; + globalThis.crypto = webcrypto as Crypto } - diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/web-app/package.json b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/web-app/package.json index 08f5ace058..a6a34d1231 100644 --- a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/web-app/package.json +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/web-app/package.json @@ -18,8 +18,7 @@ "devDependencies": { "@tsconfig/vite-react": "^2.0.0", "@types/react-dom": "^18.0.11", - "@vitejs/plugin-react": "^4.2.1", - "dotenv": "^16.0.3" + "@vitejs/plugin-react": "^4.2.1" }, "engineStrict": true, "engines": { @@ -28,9 +27,8 @@ "name": "waspBuild", "private": true, "scripts": { - "build": "npm run validate-env && tsc && vite build", - "start": "npm run validate-env && vite", - "validate-env": "node -r dotenv/config ./scripts/validate-env.mjs" + "build": "tsc && vite build", + "start": "vite" }, "type": "module", "version": "0.0.0" diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/web-app/scripts/validate-env.mjs b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/web-app/scripts/validate-env.mjs deleted file mode 100644 index 18ee507c9e..0000000000 --- a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/web-app/scripts/validate-env.mjs +++ /dev/null @@ -1,4 +0,0 @@ -import { throwIfNotValidAbsoluteURL } from 'wasp/universal/validators'; - -console.info("๐Ÿ” Validating environment variables..."); -throwIfNotValidAbsoluteURL(process.env.REACT_APP_API_URL, 'Environemnt variable REACT_APP_API_URL'); diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/web-app/src/components/DefaultRootErrorBoundary.tsx b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/web-app/src/components/DefaultRootErrorBoundary.tsx index 7af24e15d6..1e931935e6 100644 --- a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/web-app/src/components/DefaultRootErrorBoundary.tsx +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/web-app/src/components/DefaultRootErrorBoundary.tsx @@ -7,7 +7,10 @@ export function DefaultRootErrorBoundary() { console.error(error) return ( -
There was an error rendering this page. Check the browser console for more information.
+
+ There was an error rendering this page. Check the browser console for + more information. +
) } diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/client/config.ts b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/client/config.ts index 82b0f80b6a..fff77cb01c 100644 --- a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/client/config.ts +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/client/config.ts @@ -1,6 +1,7 @@ import { stripTrailingSlash } from '../universal/url.js' +import { env } from './env.js' -const apiUrl = stripTrailingSlash(import.meta.env.REACT_APP_API_URL) || 'http://localhost:3001'; +const apiUrl = stripTrailingSlash(env.REACT_APP_API_URL) // PUBLIC API export type ClientConfig = { diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/client/env.ts b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/client/env.ts new file mode 100644 index 0000000000..8ddab287f7 --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/client/env.ts @@ -0,0 +1,18 @@ +import * as z from 'zod' + +import { ensureEnvSchema } from '../env/validation.js' + +const userClientEnvSchema = z.object({}) + +const waspClientEnvSchema = z.object({ + REACT_APP_API_URL: z + .string({ + required_error: 'REACT_APP_API_URL is required', + }) + .default('http://localhost:3001') +}) + +const clientEnvSchema = userClientEnvSchema.merge(waspClientEnvSchema) + +// PUBLIC API +export const env = ensureEnvSchema(import.meta.env, clientEnvSchema) diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/client/index.ts b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/client/index.ts index cfce564c4d..859f8656b6 100644 --- a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/client/index.ts +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/client/index.ts @@ -11,4 +11,7 @@ export enum HttpMethod { export type Route = { method: HttpMethod; path: string } // PUBLIC API -export { config, ClientConfig } from './config' +export { config, ClientConfig } from './config.js' + +// PUBLIC API +export { env } from './env.js' diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/client/config.js b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/client/config.js index 1dffbd68bc..7e6149f386 100644 --- a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/client/config.js +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/client/config.js @@ -1,5 +1,6 @@ import { stripTrailingSlash } from '../universal/url.js'; -const apiUrl = stripTrailingSlash(import.meta.env.REACT_APP_API_URL) || 'http://localhost:3001'; +import { env } from './env.js'; +const apiUrl = stripTrailingSlash(env.REACT_APP_API_URL); // PUBLIC API export const config = { apiUrl, diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/client/config.js.map b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/client/config.js.map index d26d37022b..f9e186b838 100644 --- a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/client/config.js.map +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/client/config.js.map @@ -1 +1 @@ -{"version":3,"file":"config.js","sourceRoot":"","sources":["../../client/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAA;AAExD,MAAM,MAAM,GAAG,kBAAkB,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,iBAAiB,CAAC,IAAI,uBAAuB,CAAC;AAOhG,aAAa;AACb,MAAM,CAAC,MAAM,MAAM,GAAiB;IAClC,MAAM;CACP,CAAA"} \ No newline at end of file +{"version":3,"file":"config.js","sourceRoot":"","sources":["../../client/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAA;AACxD,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAA;AAE9B,MAAM,MAAM,GAAG,kBAAkB,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAA;AAOxD,aAAa;AACb,MAAM,CAAC,MAAM,MAAM,GAAiB;IAClC,MAAM;CACP,CAAA"} \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/client/env.d.ts b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/client/env.d.ts new file mode 100644 index 0000000000..5a9417a810 --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/client/env.d.ts @@ -0,0 +1,3 @@ +export declare const env: { + REACT_APP_API_URL: string; +}; diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/client/env.js b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/client/env.js new file mode 100644 index 0000000000..91164c3768 --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/client/env.js @@ -0,0 +1,14 @@ +import * as z from 'zod'; +import { ensureEnvSchema } from '../env/validation.js'; +const userClientEnvSchema = z.object({}); +const waspClientEnvSchema = z.object({ + REACT_APP_API_URL: z + .string({ + required_error: 'REACT_APP_API_URL is required', + }) + .default('http://localhost:3001') +}); +const clientEnvSchema = userClientEnvSchema.merge(waspClientEnvSchema); +// PUBLIC API +export const env = ensureEnvSchema(import.meta.env, clientEnvSchema); +//# sourceMappingURL=env.js.map \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/client/env.js.map b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/client/env.js.map new file mode 100644 index 0000000000..d230f15c0d --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/client/env.js.map @@ -0,0 +1 @@ +{"version":3,"file":"env.js","sourceRoot":"","sources":["../../client/env.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,KAAK,CAAA;AAExB,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAA;AAEtD,MAAM,mBAAmB,GAAG,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,CAAA;AAExC,MAAM,mBAAmB,GAAG,CAAC,CAAC,MAAM,CAAC;IACnC,iBAAiB,EAAE,CAAC;SACjB,MAAM,CAAC;QACN,cAAc,EAAE,+BAA+B;KAChD,CAAC;SACD,OAAO,CAAC,uBAAuB,CAAC;CACpC,CAAC,CAAA;AAEF,MAAM,eAAe,GAAG,mBAAmB,CAAC,KAAK,CAAC,mBAAmB,CAAC,CAAA;AAEtE,aAAa;AACb,MAAM,CAAC,MAAM,GAAG,GAAG,eAAe,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,eAAe,CAAC,CAAA"} \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/client/index.d.ts b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/client/index.d.ts index 7f5f4bfbc8..6498c439bd 100644 --- a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/client/index.d.ts +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/client/index.d.ts @@ -8,4 +8,5 @@ export type Route = { method: HttpMethod; path: string; }; -export { config, ClientConfig } from './config'; +export { config, ClientConfig } from './config.js'; +export { env } from './env.js'; diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/client/index.js b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/client/index.js index 5a7f13dcae..2f4daa7ae3 100644 --- a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/client/index.js +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/client/index.js @@ -8,5 +8,7 @@ export var HttpMethod; HttpMethod["Delete"] = "DELETE"; })(HttpMethod || (HttpMethod = {})); // PUBLIC API -export { config } from './config'; +export { config } from './config.js'; +// PUBLIC API +export { env } from './env.js'; //# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/client/index.js.map b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/client/index.js.map index 879953c8df..f3611558dc 100644 --- a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/client/index.js.map +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/client/index.js.map @@ -1 +1 @@ -{"version":3,"file":"index.js","sourceRoot":"","sources":["../../client/index.ts"],"names":[],"mappings":"AAAA,aAAa;AACb,mFAAmF;AACnF,MAAM,CAAN,IAAY,UAKX;AALD,WAAY,UAAU;IACrB,yBAAW,CAAA;IACX,2BAAa,CAAA;IACb,yBAAW,CAAA;IACX,+BAAiB,CAAA;AAClB,CAAC,EALW,UAAU,KAAV,UAAU,QAKrB;AAKD,aAAa;AACb,OAAO,EAAE,MAAM,EAAgB,MAAM,UAAU,CAAA"} \ No newline at end of file +{"version":3,"file":"index.js","sourceRoot":"","sources":["../../client/index.ts"],"names":[],"mappings":"AAAA,aAAa;AACb,mFAAmF;AACnF,MAAM,CAAN,IAAY,UAKX;AALD,WAAY,UAAU;IACrB,yBAAW,CAAA;IACX,2BAAa,CAAA;IACb,yBAAW,CAAA;IACX,+BAAiB,CAAA;AAClB,CAAC,EALW,UAAU,KAAV,UAAU,QAKrB;AAKD,aAAa;AACb,OAAO,EAAE,MAAM,EAAgB,MAAM,aAAa,CAAA;AAElD,aAAa;AACb,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAA"} \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/env/index.d.ts b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/env/index.d.ts new file mode 100644 index 0000000000..d1dafc25fd --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/env/index.d.ts @@ -0,0 +1,3 @@ +import * as z from 'zod'; +export declare function defineEnvValidationSchema>(schema: Schema): Schema; +export { ensureEnvSchema } from './validation.js'; diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/env/index.js b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/env/index.js new file mode 100644 index 0000000000..17e695d087 --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/env/index.js @@ -0,0 +1,7 @@ +// PUBLIC API +export function defineEnvValidationSchema(schema) { + return schema; +} +// PRIVATE API (SDK, Vite config) +export { ensureEnvSchema } from './validation.js'; +//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/env/index.js.map b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/env/index.js.map new file mode 100644 index 0000000000..75b4ff7de3 --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/env/index.js.map @@ -0,0 +1 @@ +{"version":3,"file":"index.js","sourceRoot":"","sources":["../../env/index.ts"],"names":[],"mappings":"AAEA,aAAa;AACb,MAAM,UAAU,yBAAyB,CACvC,MAAc;IAEd,OAAO,MAAM,CAAA;AACf,CAAC;AAED,iCAAiC;AACjC,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA"} \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/env/validation.d.ts b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/env/validation.d.ts new file mode 100644 index 0000000000..6bc9d059a3 --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/env/validation.d.ts @@ -0,0 +1,2 @@ +import * as z from 'zod'; +export declare function ensureEnvSchema(data: unknown, schema: Schema): z.infer; diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/env/validation.js b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/env/validation.js new file mode 100644 index 0000000000..8463eb578e --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/env/validation.js @@ -0,0 +1,23 @@ +import * as z from 'zod'; +const redColor = '\x1b[31m'; +export function ensureEnvSchema(data, schema) { + try { + return schema.parse(data); + } + catch (e) { + if (e instanceof z.ZodError) { + const errorOutput = ['', 'โ•โ• Env vars validation failed โ•โ•', '']; + for (const error of e.errors) { + errorOutput.push(` - ${error.message}`); + } + errorOutput.push(''); + errorOutput.push('โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•'); + console.error(redColor, errorOutput.join('\n')); + throw new Error('Error parsing environment variables'); + } + else { + throw e; + } + } +} +//# sourceMappingURL=validation.js.map \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/env/validation.js.map b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/env/validation.js.map new file mode 100644 index 0000000000..1d57b14a0b --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/env/validation.js.map @@ -0,0 +1 @@ +{"version":3,"file":"validation.js","sourceRoot":"","sources":["../../env/validation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,KAAK,CAAA;AAExB,MAAM,QAAQ,GAAG,UAAU,CAAA;AAE3B,MAAM,UAAU,eAAe,CAC7B,IAAa,EACb,MAAc;IAEd,IAAI,CAAC;QACH,OAAO,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;IAC3B,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,IAAI,CAAC,YAAY,CAAC,CAAC,QAAQ,EAAE,CAAC;YAC5B,MAAM,WAAW,GAAG,CAAC,EAAE,EAAE,kCAAkC,EAAE,EAAE,CAAC,CAAA;YAChE,KAAK,MAAM,KAAK,IAAI,CAAC,CAAC,MAAM,EAAE,CAAC;gBAC7B,WAAW,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC,OAAO,EAAE,CAAC,CAAA;YACzC,CAAC;YACD,WAAW,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;YACpB,WAAW,CAAC,IAAI,CAAC,kCAAkC,CAAC,CAAA;YACpD,OAAO,CAAC,KAAK,CAAC,QAAQ,EAAE,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAA;YAC/C,MAAM,IAAI,KAAK,CAAC,qCAAqC,CAAC,CAAA;QACxD,CAAC;aAAM,CAAC;YACN,MAAM,CAAC,CAAA;QACT,CAAC;IACH,CAAC;AACH,CAAC"} \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/server/HttpError.js.map b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/server/HttpError.js.map index 8163354e1b..462e1c1811 100644 --- a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/server/HttpError.js.map +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/server/HttpError.js.map @@ -1 +1 @@ -{"version":3,"file":"HttpError.js","sourceRoot":"","sources":["../../server/HttpError.ts"],"names":[],"mappings":"AAAA,MAAM,OAAO,SAAU,SAAQ,KAAK;IAIlC,YAAa,UAAkB,EAAE,OAAgB,EAAE,IAA8B,EAAE,OAAsB;QACvG,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;QAEvB,IAAI,KAAK,CAAC,iBAAiB,EAAE,CAAC;YAC5B,KAAK,CAAC,iBAAiB,CAAC,IAAI,EAAE,SAAS,CAAC,CAAA;QAC1C,CAAC;QAED,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,WAAW,CAAC,IAAI,CAAA;QAEjC,IAAI,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,UAAU,CAAC,IAAI,UAAU,IAAI,GAAG,IAAI,UAAU,GAAG,GAAG,CAAC,EAAE,CAAC;YAC7E,MAAM,IAAI,KAAK,CAAC,mDAAmD,CAAC,CAAA;QACtE,CAAC;QACD,IAAI,CAAC,UAAU,GAAG,UAAU,CAAA;QAE5B,IAAI,IAAI,EAAE,CAAC;YACT,IAAI,CAAC,IAAI,GAAG,IAAI,CAAA;QAClB,CAAC;IACH,CAAC;CACF"} \ No newline at end of file +{"version":3,"file":"HttpError.js","sourceRoot":"","sources":["../../server/HttpError.ts"],"names":[],"mappings":"AAAA,MAAM,OAAO,SAAU,SAAQ,KAAK;IAIlC,YACE,UAAkB,EAClB,OAAgB,EAChB,IAA8B,EAC9B,OAAsB;QAEtB,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;QAEvB,IAAI,KAAK,CAAC,iBAAiB,EAAE,CAAC;YAC5B,KAAK,CAAC,iBAAiB,CAAC,IAAI,EAAE,SAAS,CAAC,CAAA;QAC1C,CAAC;QAED,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,WAAW,CAAC,IAAI,CAAA;QAEjC,IACE,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,UAAU,CAAC,IAAI,UAAU,IAAI,GAAG,IAAI,UAAU,GAAG,GAAG,CAAC,EACxE,CAAC;YACD,MAAM,IAAI,KAAK,CAAC,mDAAmD,CAAC,CAAA;QACtE,CAAC;QACD,IAAI,CAAC,UAAU,GAAG,UAAU,CAAA;QAE5B,IAAI,IAAI,EAAE,CAAC;YACT,IAAI,CAAC,IAAI,GAAG,IAAI,CAAA;QAClB,CAAC;IACH,CAAC;CACF"} \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/server/config.d.ts b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/server/config.d.ts index 68bb448c56..70f438bdf0 100644 --- a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/server/config.d.ts +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/server/config.d.ts @@ -1,16 +1,13 @@ -type BaseConfig = { - allowedCORSOrigins: string | string[]; -}; -type CommonConfig = BaseConfig & { - env: string; +import { env } from './env.js'; +type NodeEnv = typeof env.NODE_ENV; +type Config = { + env: NodeEnv; isDevelopment: boolean; port: number; - databaseUrl: string | undefined; -}; -type EnvConfig = BaseConfig & { + databaseUrl: string; frontendUrl: string; serverUrl: string; + allowedCORSOrigins: string | string[]; }; -type Config = CommonConfig & EnvConfig; -declare const resolvedConfig: Config; -export default resolvedConfig; +declare const config: Config; +export default config; diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/server/config.js b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/server/config.js index e4f89082c8..dcc80722ab 100644 --- a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/server/config.js +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/server/config.js @@ -1,43 +1,21 @@ -var _a; -import merge from 'lodash.merge'; -import { stripTrailingSlash } from "../universal/url.js"; -const nodeEnv = (_a = process.env.NODE_ENV) !== null && _a !== void 0 ? _a : 'development'; +import { env } from './env.js'; +import { stripTrailingSlash } from '../universal/url.js'; +const frontendUrl = stripTrailingSlash(env.WASP_WEB_CLIENT_URL); +const serverUrl = stripTrailingSlash(env.WASP_SERVER_URL); +const allowedCORSOriginsPerEnv = { + development: '*', + production: [frontendUrl] +}; +const allowedCORSOrigins = allowedCORSOriginsPerEnv[env.NODE_ENV]; const config = { - all: { - env: nodeEnv, - isDevelopment: nodeEnv === 'development', - port: process.env.PORT ? parseInt(process.env.PORT) : 3001, - databaseUrl: process.env.DATABASE_URL, - allowedCORSOrigins: [], - }, - development: getDevelopmentConfig(), - production: getProductionConfig(), + frontendUrl, + serverUrl, + allowedCORSOrigins, + env: env.NODE_ENV, + isDevelopment: env.NODE_ENV === 'development', + port: env.PORT, + databaseUrl: env.DATABASE_URL, }; -const resolvedConfig = merge(config.all, config[nodeEnv]); // PUBLIC API -export default resolvedConfig; -function getDevelopmentConfig() { - var _a, _b; - const frontendUrl = stripTrailingSlash((_a = process.env.WASP_WEB_CLIENT_URL) !== null && _a !== void 0 ? _a : 'http://localhost:3000/'); - const serverUrl = stripTrailingSlash((_b = process.env.WASP_SERVER_URL) !== null && _b !== void 0 ? _b : 'http://localhost:3001'); - return { - // @ts-ignore - frontendUrl, - // @ts-ignore - serverUrl, - allowedCORSOrigins: '*', - }; -} -function getProductionConfig() { - const frontendUrl = stripTrailingSlash(process.env.WASP_WEB_CLIENT_URL); - const serverUrl = stripTrailingSlash(process.env.WASP_SERVER_URL); - return { - // @ts-ignore - frontendUrl, - // @ts-ignore - serverUrl, - // @ts-ignore - allowedCORSOrigins: [frontendUrl], - }; -} +export default config; //# sourceMappingURL=config.js.map \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/server/config.js.map b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/server/config.js.map index e284580bbd..9fd077fa81 100644 --- a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/server/config.js.map +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/server/config.js.map @@ -1 +1 @@ -{"version":3,"file":"config.js","sourceRoot":"","sources":["../../server/config.ts"],"names":[],"mappings":";AAAA,OAAO,KAAK,MAAM,cAAc,CAAA;AAEhC,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAEzD,MAAM,OAAO,GAAG,MAAA,OAAO,CAAC,GAAG,CAAC,QAAQ,mCAAI,aAAa,CAAA;AAyBrD,MAAM,MAAM,GAIR;IACF,GAAG,EAAE;QACH,GAAG,EAAE,OAAO;QACZ,aAAa,EAAE,OAAO,KAAK,aAAa;QACxC,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI;QAC1D,WAAW,EAAE,OAAO,CAAC,GAAG,CAAC,YAAY;QACrC,kBAAkB,EAAE,EAAE;KACvB;IACD,WAAW,EAAE,oBAAoB,EAAE;IACnC,UAAU,EAAE,mBAAmB,EAAE;CAClC,CAAA;AAED,MAAM,cAAc,GAAW,KAAK,CAAC,MAAM,CAAC,GAAG,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC,CAAA;AACjE,aAAa;AACb,eAAe,cAAc,CAAA;AAE7B,SAAS,oBAAoB;;IAC3B,MAAM,WAAW,GAAG,kBAAkB,CAAC,MAAA,OAAO,CAAC,GAAG,CAAC,mBAAmB,mCAAI,wBAAwB,CAAC,CAAC;IACpG,MAAM,SAAS,GAAG,kBAAkB,CAAC,MAAA,OAAO,CAAC,GAAG,CAAC,eAAe,mCAAI,uBAAuB,CAAC,CAAC;IAC7F,OAAO;QACL,aAAa;QACb,WAAW;QACX,aAAa;QACb,SAAS;QACT,kBAAkB,EAAE,GAAG;KACxB,CAAA;AACH,CAAC;AAED,SAAS,mBAAmB;IAC1B,MAAM,WAAW,GAAG,kBAAkB,CAAC,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC,CAAC;IACxE,MAAM,SAAS,GAAG,kBAAkB,CAAC,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC;IAClE,OAAO;QACL,aAAa;QACb,WAAW;QACX,aAAa;QACb,SAAS;QACT,aAAa;QACb,kBAAkB,EAAE,CAAC,WAAW,CAAC;KAClC,CAAA;AACH,CAAC"} \ No newline at end of file +{"version":3,"file":"config.js","sourceRoot":"","sources":["../../server/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAA;AAC9B,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAA;AAcxD,MAAM,WAAW,GAAG,kBAAkB,CAAC,GAAG,CAAC,mBAAmB,CAAC,CAAA;AAC/D,MAAM,SAAS,GAAG,kBAAkB,CAAC,GAAG,CAAC,eAAe,CAAC,CAAA;AAEzD,MAAM,wBAAwB,GAAuC;IACnE,WAAW,EAAE,GAAG;IAChB,UAAU,EAAE,CAAC,WAAW,CAAC;CAC1B,CAAA;AACD,MAAM,kBAAkB,GAAG,wBAAwB,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAA;AAEjE,MAAM,MAAM,GAAW;IACrB,WAAW;IACX,SAAS;IACT,kBAAkB;IAClB,GAAG,EAAE,GAAG,CAAC,QAAQ;IACjB,aAAa,EAAE,GAAG,CAAC,QAAQ,KAAK,aAAa;IAC7C,IAAI,EAAE,GAAG,CAAC,IAAI;IACd,WAAW,EAAE,GAAG,CAAC,YAAY;CAC9B,CAAA;AAED,aAAa;AACb,eAAe,MAAM,CAAA"} \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/server/env.d.ts b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/server/env.d.ts new file mode 100644 index 0000000000..667ea30e35 --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/server/env.d.ts @@ -0,0 +1,17 @@ +export declare const env: { + PORT: number; + DATABASE_URL: string; + SKIP_EMAIL_VERIFICATION_IN_DEV: boolean; + NODE_ENV: "development"; + WASP_SERVER_URL: string; + WASP_WEB_CLIENT_URL: string; + PG_BOSS_NEW_OPTIONS?: string | undefined; +} | { + PORT: number; + DATABASE_URL: string; + SKIP_EMAIL_VERIFICATION_IN_DEV: boolean; + NODE_ENV: "production"; + WASP_SERVER_URL: string; + WASP_WEB_CLIENT_URL: string; + PG_BOSS_NEW_OPTIONS?: string | undefined; +}; diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/server/env.js b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/server/env.js new file mode 100644 index 0000000000..1bfc3e0b6e --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/server/env.js @@ -0,0 +1,55 @@ +import * as z from 'zod'; +import { ensureEnvSchema } from '../env/validation.js'; +const userServerEnvSchema = z.object({}); +const waspServerCommonSchema = z.object({ + PORT: z.coerce.number().default(3001), + DATABASE_URL: z.string({ + required_error: 'DATABASE_URL is required', + }), + PG_BOSS_NEW_OPTIONS: z.string().optional(), + SKIP_EMAIL_VERIFICATION_IN_DEV: z + .enum(['true', 'false'], { + message: 'SKIP_EMAIL_VERIFICATION_IN_DEV must be either "true" or "false"', + }) + .transform((value) => value === 'true') + .default('false'), +}); +const serverUrlSchema = z + .string({ + required_error: 'WASP_SERVER_URL is required', +}) + .url({ + message: 'WASP_SERVER_URL must be a valid URL', +}); +const clientUrlSchema = z + .string({ + required_error: 'WASP_WEB_CLIENT_URL is required', +}) + .url({ + message: 'WASP_WEB_CLIENT_URL must be a valid URL', +}); +// In development, we provide default values for some environment variables +// to make the development process easier. +const serverDevSchema = z.object({ + NODE_ENV: z.literal('development'), + WASP_SERVER_URL: serverUrlSchema + .default('http://localhost:3001'), + WASP_WEB_CLIENT_URL: clientUrlSchema + .default('http://localhost:3000/'), +}); +const serverProdSchema = z.object({ + NODE_ENV: z.literal('production'), + WASP_SERVER_URL: serverUrlSchema, + WASP_WEB_CLIENT_URL: clientUrlSchema, +}); +const serverCommonSchema = userServerEnvSchema.merge(waspServerCommonSchema); +const serverEnvSchema = z.discriminatedUnion('NODE_ENV', [ + serverDevSchema.merge(serverCommonSchema), + serverProdSchema.merge(serverCommonSchema) +]); +// PUBLIC API +export const env = ensureEnvSchema(process.env, serverEnvSchema); +function getRequiredEnvVarErrorMessage(featureName, envVarName) { + return `${envVarName} is required when using ${featureName}`; +} +//# sourceMappingURL=env.js.map \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/server/env.js.map b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/server/env.js.map new file mode 100644 index 0000000000..3dfa10e116 --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/server/env.js.map @@ -0,0 +1 @@ +{"version":3,"file":"env.js","sourceRoot":"","sources":["../../server/env.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,KAAK,CAAA;AAExB,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAA;AAEtD,MAAM,mBAAmB,GAAG,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,CAAA;AAExC,MAAM,sBAAsB,GAAG,CAAC,CAAC,MAAM,CAAC;IACtC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC;IACrC,YAAY,EAAE,CAAC,CAAC,MAAM,CAAC;QACrB,cAAc,EAAE,0BAA0B;KAC3C,CAAC;IACF,mBAAmB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC1C,8BAA8B,EAAE,CAAC;SAC9B,IAAI,CAAC,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE;QACvB,OAAO,EAAE,iEAAiE;KAC3E,CAAC;SACD,SAAS,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,KAAK,MAAM,CAAC;SACtC,OAAO,CAAC,OAAO,CAAC;CACpB,CAAC,CAAA;AAEF,MAAM,eAAe,GAAG,CAAC;KACtB,MAAM,CAAC;IACN,cAAc,EAAE,6BAA6B;CAC9C,CAAC;KACD,GAAG,CAAC;IACH,OAAO,EAAE,qCAAqC;CAC/C,CAAC,CAAA;AAEJ,MAAM,eAAe,GAAG,CAAC;KACtB,MAAM,CAAC;IACN,cAAc,EAAE,iCAAiC;CAClD,CAAC;KACD,GAAG,CAAC;IACH,OAAO,EAAE,yCAAyC;CACnD,CAAC,CAAA;AAGJ,2EAA2E;AAC3E,0CAA0C;AAC1C,MAAM,eAAe,GAAG,CAAC,CAAC,MAAM,CAAC;IAC/B,QAAQ,EAAE,CAAC,CAAC,OAAO,CAAC,aAAa,CAAC;IAClC,eAAe,EAAE,eAAe;SAC7B,OAAO,CAAC,uBAAuB,CAAC;IACnC,mBAAmB,EAAE,eAAe;SACjC,OAAO,CAAC,wBAAwB,CAAC;CACrC,CAAC,CAAA;AAEF,MAAM,gBAAgB,GAAG,CAAC,CAAC,MAAM,CAAC;IAChC,QAAQ,EAAE,CAAC,CAAC,OAAO,CAAC,YAAY,CAAC;IACjC,eAAe,EAAE,eAAe;IAChC,mBAAmB,EAAE,eAAe;CACrC,CAAC,CAAA;AAEF,MAAM,kBAAkB,GAAG,mBAAmB,CAAC,KAAK,CAAC,sBAAsB,CAAC,CAAA;AAC5E,MAAM,eAAe,GAAG,CAAC,CAAC,kBAAkB,CAAC,UAAU,EAAE;IACvD,eAAe,CAAC,KAAK,CAAC,kBAAkB,CAAC;IACzC,gBAAgB,CAAC,KAAK,CAAC,kBAAkB,CAAC;CAC3C,CAAC,CAAA;AAEF,aAAa;AACb,MAAM,CAAC,MAAM,GAAG,GAAG,eAAe,CAAC,OAAO,CAAC,GAAG,EAAE,eAAe,CAAC,CAAA;AAEhE,SAAS,6BAA6B,CAAC,WAAmB,EAAE,UAAkB;IAC5E,OAAO,GAAG,UAAU,2BAA2B,WAAW,EAAE,CAAA;AAC9D,CAAC"} \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/server/index.d.ts b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/server/index.d.ts index dee9613a53..a108f6123f 100644 --- a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/server/index.d.ts +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/server/index.d.ts @@ -4,4 +4,5 @@ export { default as prisma } from './dbClient.js'; export { type ServerSetupFn } from './types/index.js'; export { HttpError } from './HttpError.js'; export { MiddlewareConfigFn } from './middleware/index.js'; +export { env } from './env.js'; export type DbSeedFn = (prisma: PrismaClient) => Promise; diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/server/index.js b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/server/index.js index 0e53e0deab..8e75e6f78c 100644 --- a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/server/index.js +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/server/index.js @@ -4,4 +4,6 @@ export { default as config } from './config.js'; export { default as prisma } from './dbClient.js'; // PUBLIC API export { HttpError } from './HttpError.js'; +// PUBLIC API +export { env } from './env.js'; //# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/server/index.js.map b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/server/index.js.map index b84ac1b303..87ed22f3af 100644 --- a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/server/index.js.map +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/server/index.js.map @@ -1 +1 @@ -{"version":3,"file":"index.js","sourceRoot":"","sources":["../../server/index.ts"],"names":[],"mappings":"AAEA,aAAa;AACb,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,aAAa,CAAA;AAC/C,aAAa;AACb,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,eAAe,CAAA;AAGjD,aAAa;AACb,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA"} \ No newline at end of file +{"version":3,"file":"index.js","sourceRoot":"","sources":["../../server/index.ts"],"names":[],"mappings":"AAEA,aAAa;AACb,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,aAAa,CAAA;AAC/C,aAAa;AACb,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,eAAe,CAAA;AAGjD,aAAa;AACb,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAG1C,aAAa;AACb,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAA"} \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/universal/url.d.ts b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/universal/url.d.ts index aa893e7838..7ceb994694 100644 --- a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/universal/url.d.ts +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/universal/url.d.ts @@ -1 +1,2 @@ -export declare function stripTrailingSlash(url?: string): string | undefined; +export declare function stripTrailingSlash(url: string): string; +export declare function stripTrailingSlash(url: undefined): undefined; diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/universal/url.js.map b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/universal/url.js.map index a7bad96020..e236f1b977 100644 --- a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/universal/url.js.map +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/universal/url.js.map @@ -1 +1 @@ -{"version":3,"file":"url.js","sourceRoot":"","sources":["../../universal/url.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,kBAAkB,CAAC,GAAY;IAC3C,OAAO,GAAG,aAAH,GAAG,uBAAH,GAAG,CAAE,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;AACnC,CAAC"} \ No newline at end of file +{"version":3,"file":"url.js","sourceRoot":"","sources":["../../universal/url.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,kBAAkB,CAAC,GAAY;IAC3C,OAAO,GAAG,aAAH,GAAG,uBAAH,GAAG,CAAE,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;AACnC,CAAC"} \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/env/index.ts b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/env/index.ts new file mode 100644 index 0000000000..4c783125c1 --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/env/index.ts @@ -0,0 +1,11 @@ +import * as z from 'zod' + +// PUBLIC API +export function defineEnvValidationSchema>( + schema: Schema, +): Schema { + return schema +} + +// PRIVATE API (SDK, Vite config) +export { ensureEnvSchema } from './validation.js' diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/env/validation.ts b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/env/validation.ts new file mode 100644 index 0000000000..a5bc334754 --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/env/validation.ts @@ -0,0 +1,25 @@ +import * as z from 'zod' + +const redColor = '\x1b[31m' + +export function ensureEnvSchema( + data: unknown, + schema: Schema +): z.infer { + try { + return schema.parse(data) + } catch (e) { + if (e instanceof z.ZodError) { + const errorOutput = ['', 'โ•โ• Env vars validation failed โ•โ•', ''] + for (const error of e.errors) { + errorOutput.push(` - ${error.message}`) + } + errorOutput.push('') + errorOutput.push('โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•') + console.error(redColor, errorOutput.join('\n')) + throw new Error('Error parsing environment variables') + } else { + throw e + } + } +} diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/package.json b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/package.json index 2589c5362f..55c07a5084 100644 --- a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/package.json +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/package.json @@ -8,7 +8,6 @@ "axios": "^1.4.0", "express": "~4.21.0", "jsdom": "^21.1.1", - "lodash.merge": "^4.6.2", "mitt": "3.0.0", "msw": "^1.1.0", "prisma": "5.19.1", @@ -16,7 +15,8 @@ "react-hook-form": "^7.45.4", "react-router-dom": "^6.26.2", "superjson": "^2.2.1", - "vitest": "^1.2.1" + "vitest": "^1.2.1", + "zod": "^3.23.8" }, "devDependencies": { "@tsconfig/node18": "latest", @@ -51,6 +51,7 @@ "./core/storage": "./dist/core/storage.js", "./dev": "./dist/dev/index.js", "./entities": "./dist/entities/index.js", + "./env": "./dist/env/index.js", "./ext-src/*": "./dist/ext-src/*.js", "./operations": "./dist/operations/index.js", "./operations/*": "./dist/operations/*", diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/server/HttpError.ts b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/server/HttpError.ts index d05a2c841b..bce964712d 100644 --- a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/server/HttpError.ts +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/server/HttpError.ts @@ -2,7 +2,12 @@ export class HttpError extends Error { public statusCode: number public data: unknown - constructor (statusCode: number, message?: string, data?: Record, options?: ErrorOptions) { + constructor( + statusCode: number, + message?: string, + data?: Record, + options?: ErrorOptions + ) { super(message, options) if (Error.captureStackTrace) { @@ -11,7 +16,9 @@ export class HttpError extends Error { this.name = this.constructor.name - if (!(Number.isInteger(statusCode) && statusCode >= 400 && statusCode < 600)) { + if ( + !(Number.isInteger(statusCode) && statusCode >= 400 && statusCode < 600) + ) { throw new Error('statusCode has to be integer in range [400, 600).') } this.statusCode = statusCode diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/server/config.ts b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/server/config.ts index 1dc9bafb93..3a000a897f 100644 --- a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/server/config.ts +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/server/config.ts @@ -1,73 +1,36 @@ -import merge from 'lodash.merge' +import { env } from './env.js' +import { stripTrailingSlash } from '../universal/url.js' -import { stripTrailingSlash } from "../universal/url.js"; +type NodeEnv = typeof env.NODE_ENV -const nodeEnv = process.env.NODE_ENV ?? 'development' - -// TODO: -// - Use dotenv library to consume env vars from a file. -// - Use convict library to define schema and validate env vars. -// https://codingsans.com/blog/node-config-best-practices - -type BaseConfig = { - allowedCORSOrigins: string | string[]; -} - -type CommonConfig = BaseConfig & { - env: string; +type Config = { + env: NodeEnv; isDevelopment: boolean; port: number; - databaseUrl: string | undefined; -} - -type EnvConfig = BaseConfig & { + databaseUrl: string; frontendUrl: string; serverUrl: string; + allowedCORSOrigins: string | string[]; } -type Config = CommonConfig & EnvConfig +const frontendUrl = stripTrailingSlash(env.WASP_WEB_CLIENT_URL) +const serverUrl = stripTrailingSlash(env.WASP_SERVER_URL) -const config: { - all: CommonConfig, - development: EnvConfig, - production: EnvConfig, -} = { - all: { - env: nodeEnv, - isDevelopment: nodeEnv === 'development', - port: process.env.PORT ? parseInt(process.env.PORT) : 3001, - databaseUrl: process.env.DATABASE_URL, - allowedCORSOrigins: [], - }, - development: getDevelopmentConfig(), - production: getProductionConfig(), +const allowedCORSOriginsPerEnv: Record = { + development: '*', + production: [frontendUrl] } - -const resolvedConfig: Config = merge(config.all, config[nodeEnv]) -// PUBLIC API -export default resolvedConfig - -function getDevelopmentConfig(): EnvConfig { - const frontendUrl = stripTrailingSlash(process.env.WASP_WEB_CLIENT_URL ?? 'http://localhost:3000/'); - const serverUrl = stripTrailingSlash(process.env.WASP_SERVER_URL ?? 'http://localhost:3001'); - return { - // @ts-ignore - frontendUrl, - // @ts-ignore - serverUrl, - allowedCORSOrigins: '*', - } +const allowedCORSOrigins = allowedCORSOriginsPerEnv[env.NODE_ENV] + +const config: Config = { + frontendUrl, + serverUrl, + allowedCORSOrigins, + env: env.NODE_ENV, + isDevelopment: env.NODE_ENV === 'development', + port: env.PORT, + databaseUrl: env.DATABASE_URL, } -function getProductionConfig(): EnvConfig { - const frontendUrl = stripTrailingSlash(process.env.WASP_WEB_CLIENT_URL); - const serverUrl = stripTrailingSlash(process.env.WASP_SERVER_URL); - return { - // @ts-ignore - frontendUrl, - // @ts-ignore - serverUrl, - // @ts-ignore - allowedCORSOrigins: [frontendUrl], - } -} +// PUBLIC API +export default config diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/server/env.ts b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/server/env.ts new file mode 100644 index 0000000000..c1bd8f5c0f --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/server/env.ts @@ -0,0 +1,65 @@ +import * as z from 'zod' + +import { ensureEnvSchema } from '../env/validation.js' + +const userServerEnvSchema = z.object({}) + +const waspServerCommonSchema = z.object({ + PORT: z.coerce.number().default(3001), + DATABASE_URL: z.string({ + required_error: 'DATABASE_URL is required', + }), + PG_BOSS_NEW_OPTIONS: z.string().optional(), + SKIP_EMAIL_VERIFICATION_IN_DEV: z + .enum(['true', 'false'], { + message: 'SKIP_EMAIL_VERIFICATION_IN_DEV must be either "true" or "false"', + }) + .transform((value) => value === 'true') + .default('false'), +}) + +const serverUrlSchema = z + .string({ + required_error: 'WASP_SERVER_URL is required', + }) + .url({ + message: 'WASP_SERVER_URL must be a valid URL', + }) + +const clientUrlSchema = z + .string({ + required_error: 'WASP_WEB_CLIENT_URL is required', + }) + .url({ + message: 'WASP_WEB_CLIENT_URL must be a valid URL', + }) + + +// In development, we provide default values for some environment variables +// to make the development process easier. +const serverDevSchema = z.object({ + NODE_ENV: z.literal('development'), + WASP_SERVER_URL: serverUrlSchema + .default('http://localhost:3001'), + WASP_WEB_CLIENT_URL: clientUrlSchema + .default('http://localhost:3000/'), +}) + +const serverProdSchema = z.object({ + NODE_ENV: z.literal('production'), + WASP_SERVER_URL: serverUrlSchema, + WASP_WEB_CLIENT_URL: clientUrlSchema, +}) + +const serverCommonSchema = userServerEnvSchema.merge(waspServerCommonSchema) +const serverEnvSchema = z.discriminatedUnion('NODE_ENV', [ + serverDevSchema.merge(serverCommonSchema), + serverProdSchema.merge(serverCommonSchema) +]) + +// PUBLIC API +export const env = ensureEnvSchema(process.env, serverEnvSchema) + +function getRequiredEnvVarErrorMessage(featureName: string, envVarName: string) { + return `${envVarName} is required when using ${featureName}` +} diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/server/index.ts b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/server/index.ts index 535077900b..0f95deb90a 100644 --- a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/server/index.ts +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/server/index.ts @@ -10,6 +10,8 @@ export { type ServerSetupFn } from './types/index.js' export { HttpError } from './HttpError.js' // PUBLIC API export { MiddlewareConfigFn } from './middleware/index.js' +// PUBLIC API +export { env } from './env.js' // PUBLIC API export type DbSeedFn = (prisma: PrismaClient) => Promise diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/universal/url.ts b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/universal/url.ts index d21c06c65c..126678dac9 100644 --- a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/universal/url.ts +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/universal/url.ts @@ -1,3 +1,5 @@ +export function stripTrailingSlash(url: string): string +export function stripTrailingSlash(url: undefined): undefined export function stripTrailingSlash(url?: string): string | undefined { return url?.replace(/\/$/, ""); } diff --git a/waspc/e2e-test/test-outputs/waspCompile-golden/files.manifest b/waspc/e2e-test/test-outputs/waspCompile-golden/files.manifest index e47fe56dc6..9e7c4d6969 100644 --- a/waspc/e2e-test/test-outputs/waspCompile-golden/files.manifest +++ b/waspc/e2e-test/test-outputs/waspCompile-golden/files.manifest @@ -6,6 +6,7 @@ waspCompile/.wasp/out/installedNpmDepsLog.json waspCompile/.wasp/out/sdk/wasp/api/events.ts waspCompile/.wasp/out/sdk/wasp/api/index.ts waspCompile/.wasp/out/sdk/wasp/client/config.ts +waspCompile/.wasp/out/sdk/wasp/client/env.ts waspCompile/.wasp/out/sdk/wasp/client/index.ts waspCompile/.wasp/out/sdk/wasp/client/operations/actions/core.ts waspCompile/.wasp/out/sdk/wasp/client/operations/actions/index.ts @@ -35,6 +36,9 @@ waspCompile/.wasp/out/sdk/wasp/dist/api/index.js.map waspCompile/.wasp/out/sdk/wasp/dist/client/config.d.ts waspCompile/.wasp/out/sdk/wasp/dist/client/config.js waspCompile/.wasp/out/sdk/wasp/dist/client/config.js.map +waspCompile/.wasp/out/sdk/wasp/dist/client/env.d.ts +waspCompile/.wasp/out/sdk/wasp/dist/client/env.js +waspCompile/.wasp/out/sdk/wasp/dist/client/env.js.map waspCompile/.wasp/out/sdk/wasp/dist/client/index.d.ts waspCompile/.wasp/out/sdk/wasp/dist/client/index.js waspCompile/.wasp/out/sdk/wasp/dist/client/index.js.map @@ -98,6 +102,12 @@ waspCompile/.wasp/out/sdk/wasp/dist/dev/index.js.map waspCompile/.wasp/out/sdk/wasp/dist/entities/index.d.ts waspCompile/.wasp/out/sdk/wasp/dist/entities/index.js waspCompile/.wasp/out/sdk/wasp/dist/entities/index.js.map +waspCompile/.wasp/out/sdk/wasp/dist/env/index.d.ts +waspCompile/.wasp/out/sdk/wasp/dist/env/index.js +waspCompile/.wasp/out/sdk/wasp/dist/env/index.js.map +waspCompile/.wasp/out/sdk/wasp/dist/env/validation.d.ts +waspCompile/.wasp/out/sdk/wasp/dist/env/validation.js +waspCompile/.wasp/out/sdk/wasp/dist/env/validation.js.map waspCompile/.wasp/out/sdk/wasp/dist/ext-src/MainPage.d.ts waspCompile/.wasp/out/sdk/wasp/dist/ext-src/MainPage.jsx waspCompile/.wasp/out/sdk/wasp/dist/ext-src/MainPage.jsx.map @@ -119,6 +129,9 @@ waspCompile/.wasp/out/sdk/wasp/dist/server/config.js.map waspCompile/.wasp/out/sdk/wasp/dist/server/dbClient.d.ts waspCompile/.wasp/out/sdk/wasp/dist/server/dbClient.js waspCompile/.wasp/out/sdk/wasp/dist/server/dbClient.js.map +waspCompile/.wasp/out/sdk/wasp/dist/server/env.d.ts +waspCompile/.wasp/out/sdk/wasp/dist/server/env.js +waspCompile/.wasp/out/sdk/wasp/dist/server/env.js.map waspCompile/.wasp/out/sdk/wasp/dist/server/index.d.ts waspCompile/.wasp/out/sdk/wasp/dist/server/index.js waspCompile/.wasp/out/sdk/wasp/dist/server/index.js.map @@ -165,6 +178,8 @@ waspCompile/.wasp/out/sdk/wasp/dist/universal/validators.d.ts waspCompile/.wasp/out/sdk/wasp/dist/universal/validators.js waspCompile/.wasp/out/sdk/wasp/dist/universal/validators.js.map waspCompile/.wasp/out/sdk/wasp/entities/index.ts +waspCompile/.wasp/out/sdk/wasp/env/index.ts +waspCompile/.wasp/out/sdk/wasp/env/validation.ts waspCompile/.wasp/out/sdk/wasp/ext-src/Main.css waspCompile/.wasp/out/sdk/wasp/ext-src/MainPage.jsx waspCompile/.wasp/out/sdk/wasp/ext-src/vite-env.d.ts @@ -177,6 +192,7 @@ waspCompile/.wasp/out/sdk/wasp/server/_types/serialization.ts waspCompile/.wasp/out/sdk/wasp/server/_types/taggedEntities.ts waspCompile/.wasp/out/sdk/wasp/server/config.ts waspCompile/.wasp/out/sdk/wasp/server/dbClient.ts +waspCompile/.wasp/out/sdk/wasp/server/env.ts waspCompile/.wasp/out/sdk/wasp/server/index.ts waspCompile/.wasp/out/sdk/wasp/server/middleware/globalMiddleware.ts waspCompile/.wasp/out/sdk/wasp/server/middleware/index.ts @@ -200,7 +216,6 @@ waspCompile/.wasp/out/server/README.md waspCompile/.wasp/out/server/nodemon.json waspCompile/.wasp/out/server/package.json waspCompile/.wasp/out/server/rollup.config.js -waspCompile/.wasp/out/server/scripts/validate-env.mjs waspCompile/.wasp/out/server/src/app.js waspCompile/.wasp/out/server/src/middleware/globalMiddleware.ts waspCompile/.wasp/out/server/src/middleware/index.ts @@ -219,7 +234,6 @@ waspCompile/.wasp/out/web-app/package.json waspCompile/.wasp/out/web-app/public/.gitkeep waspCompile/.wasp/out/web-app/public/favicon.ico waspCompile/.wasp/out/web-app/public/manifest.json -waspCompile/.wasp/out/web-app/scripts/validate-env.mjs waspCompile/.wasp/out/web-app/src/components/DefaultRootErrorBoundary.tsx waspCompile/.wasp/out/web-app/src/components/FullPageWrapper.tsx waspCompile/.wasp/out/web-app/src/components/Loader.module.css diff --git a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/.waspchecksums b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/.waspchecksums index 02790ce12f..1c57ca36f6 100644 --- a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/.waspchecksums +++ b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/.waspchecksums @@ -18,14 +18,21 @@ "file", "../out/sdk/wasp/client/config.ts" ], - "d94e8bee5fd8f6793b511652b9ee558e57f4913595763137a5385cd929648168" + "4c5835ad64352cc87a4a17e41fd70333bc0d1eedd7339d7ad3d3507cdb64038d" + ], + [ + [ + "file", + "../out/sdk/wasp/client/env.ts" + ], + "2a3602d40b299fbfa1a6275fe67828d5653d9f3265ba8204b75178aa41dcbae0" ], [ [ "file", "../out/sdk/wasp/client/index.ts" ], - "2b3651e7040a63cfd6a271c2aa89f21cf01170e0abeb6c5ab74adde324852fe4" + "b55e5c34c2fdb50e608bcb4331fa2e67724f1fbfb21e1b93680b0f5fe2b7005a" ], [ [ @@ -167,6 +174,20 @@ ], "c59b97b122b977b5171686c92ee5ff2d80d397c2e83cc0915affb6ee136406fb" ], + [ + [ + "file", + "../out/sdk/wasp/env/index.ts" + ], + "d92b4832e9d4249ae5b1f00c0f9760d6841aa44bc199550414793a1d57a6845e" + ], + [ + [ + "file", + "../out/sdk/wasp/env/validation.ts" + ], + "e22b2fb8931275d3b1bd3176cbdbf753d22fc61887ded0e93cd8febf1b97d260" + ], [ [ "file", @@ -200,7 +221,7 @@ "file", "../out/sdk/wasp/package.json" ], - "6f1afd30c951e1f49740256440cd626c9cb521cf0822d93d2d2a5d63d8719d7b" + "b991fb5ade61a662dea3f21bf09aa30379f30ea76204a369c0800b1413256a64" ], [ [ @@ -214,7 +235,7 @@ "file", "../out/sdk/wasp/server/HttpError.ts" ], - "f375fb45eeb16381b8cc575503db302c005ddaa1b35d837ca0ea043fe8ec6ccb" + "9c1b2449d8e9d68470c6014054a3c3074069a104ddb291b9089c30d624a2d9d9" ], [ [ @@ -242,7 +263,7 @@ "file", "../out/sdk/wasp/server/config.ts" ], - "5dd35660e0969defaa3c180179919f4922ab7bc041746872dff7f2280c1872ae" + "8431eaf0332cdbdd2f36b4c232b4b6b65fd19bad58fe51922f5b2662f22fc8ad" ], [ [ @@ -251,12 +272,19 @@ ], "50f11eb232174184be5fd44e8ee3875c30707b486c8c70c3f7bfb93609d38e66" ], + [ + [ + "file", + "../out/sdk/wasp/server/env.ts" + ], + "f3cab17868417ffd4432dc4efecde72ccaaf099a79ddc9194835f3608d123b63" + ], [ [ "file", "../out/sdk/wasp/server/index.ts" ], - "51c3e0802764f72b88f6daaf148dba514ae817e233a40cc9b92ec91784f71f98" + "a8d2ffa7dfd0a13f7fb6135193bcdac1f860603bdd0ddc2843033fd4f6f6151f" ], [ [ @@ -354,7 +382,7 @@ "file", "../out/sdk/wasp/universal/url.ts" ], - "8dc6e044a1a231b796465d94985ca47c5efd42a5d411b407a7d83a61ebae4b6d" + "58ff4ad8ffc79264d7215461571d909f3c5fb177ff32c67058d1da9cd4115d6e" ], [ [ @@ -382,7 +410,7 @@ "file", "Dockerfile" ], - "8851d4e81582d58921a5646dcf063b31dbf8ce9a46f3d4710360d2047dc644b5" + "20694e4dd2e7d96d8752ee3d792d4d8b63c46d7a4505c5312923a749146604b7" ], [ [ @@ -431,7 +459,7 @@ "file", "server/package.json" ], - "a4eb7e59ac5309fc5c39ccd7e19f4fdc7a9a568a3b393f62358ef6a6065d65c3" + "eaaa1e16e4962f38deb7220a7439a26b920ed7f852890242d084b4cebc63b7c3" ], [ [ @@ -440,13 +468,6 @@ ], "c1beb8264f11898364288d73b16f08d1923bac5f70038d9827978deb5b58a2da" ], - [ - [ - "file", - "server/scripts/validate-env.mjs" - ], - "100177b4326ccab7362eff378315d532ad1cc17cd28d1ed5978cb167fd627746" - ], [ [ "file", @@ -480,7 +501,7 @@ "file", "server/src/polyfill.ts" ], - "1149661e0aa7228b184bb2c04ac63232a6523b09b33829db6977f79daba3fd8d" + "f62c2088c8ebf5889ab479bb937ffd8f0f96d3e85483a7b088edebb5ae598dca" ], [ [ @@ -557,7 +578,7 @@ "file", "web-app/package.json" ], - "63ecfca14bd00080f48d3c4e6dff009ddf2777569a07c5e7db647e0384722212" + "adf7fab02235a92502d9fa0e25b76ef5b5096216baef4e89f8573a876446ae74" ], [ [ @@ -580,19 +601,12 @@ ], "434b67f4ee148d6a5c8d88879b616d0af36d71abf193e84ef247e5ab959a13bd" ], - [ - [ - "file", - "web-app/scripts/validate-env.mjs" - ], - "a9a3a7eb6bc3ead49d8e3850a70737c93c789098beb3b40196bf145fd38893cd" - ], [ [ "file", "web-app/src/components/DefaultRootErrorBoundary.tsx" ], - "4a141a28ca7aed9daddd75c156f7bae5eec50f3e5a85b6a43a28fa2648f34c44" + "73bbe8a2eb141e523c6b8169c0831e594b4ab6c7207f107d6d20c1561b44373a" ], [ [ diff --git a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/Dockerfile b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/Dockerfile index 44152eecda..2c775ca6af 100644 --- a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/Dockerfile +++ b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/Dockerfile @@ -50,15 +50,15 @@ WORKDIR /app # Copying the top level 'node_modules' because it contains the Prisma packages # necessary for migrating the database. COPY --from=server-builder /app/node_modules ./node_modules -# Copying the SDK because 'validate-env.mjs' executes independent of the bundle +# Copying the SDK because the server bundle doesn't bundle the SDK # and references the 'wasp' package. COPY --from=server-builder /app/.wasp/out/sdk .wasp/out/sdk -# Copying 'server/node_modules' because 'validate-env.mjs' executes independent -# of the bundle and references the dotenv package. +# Copying 'server/node_modules' because we require dotenv package to +# load environment variables +# TODO: replace dotenv with native Node.js environment variable loading COPY --from=server-builder /app/.wasp/build/server/node_modules .wasp/build/server/node_modules COPY --from=server-builder /app/.wasp/build/server/bundle .wasp/build/server/bundle COPY --from=server-builder /app/.wasp/build/server/package*.json .wasp/build/server/ -COPY --from=server-builder /app/.wasp/build/server/scripts .wasp/build/server/scripts COPY db/ .wasp/build/db/ EXPOSE ${PORT} WORKDIR /app/.wasp/build/server diff --git a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/installedNpmDepsLog.json b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/installedNpmDepsLog.json index a21ddcd4a1..d9bee4d2fa 100644 --- a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/installedNpmDepsLog.json +++ b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/installedNpmDepsLog.json @@ -1 +1 @@ -{"_waspSdkNpmDeps":{"dependencies":[{"name":"@prisma/client","version":"5.19.1"},{"name":"prisma","version":"5.19.1"},{"name":"@tanstack/react-query","version":"^4.29.0"},{"name":"axios","version":"^1.4.0"},{"name":"express","version":"~4.21.0"},{"name":"mitt","version":"3.0.0"},{"name":"react","version":"^18.2.0"},{"name":"lodash.merge","version":"^4.6.2"},{"name":"react-router-dom","version":"^6.26.2"},{"name":"react-hook-form","version":"^7.45.4"},{"name":"superjson","version":"^2.2.1"},{"name":"vitest","version":"^1.2.1"},{"name":"@vitest/ui","version":"^1.2.1"},{"name":"jsdom","version":"^21.1.1"},{"name":"@testing-library/react","version":"^14.1.2"},{"name":"@testing-library/jest-dom","version":"^6.3.0"},{"name":"msw","version":"^1.1.0"}],"devDependencies":[{"name":"@tsconfig/node18","version":"latest"},{"name":"@types/express-serve-static-core","version":"^4.17.13"}]},"_userNpmDeps":{"userDependencies":[{"name":"react","version":"^18.2.0"},{"name":"react-dom","version":"^18.2.0"},{"name":"react-router-dom","version":"^6.26.2"},{"name":"wasp","version":"file:.wasp/out/sdk/wasp"}],"userDevDependencies":[{"name":"@types/react","version":"^18.0.37"},{"name":"prisma","version":"5.19.1"},{"name":"typescript","version":"^5.1.0"},{"name":"vite","version":"^4.3.9"}]},"_waspFrameworkNpmDeps":{"npmDepsForWebApp":{"dependencies":[{"name":"@tanstack/react-query","version":"^4.29.0"},{"name":"axios","version":"^1.4.0"}],"devDependencies":[{"name":"@tsconfig/vite-react","version":"^2.0.0"},{"name":"@types/react-dom","version":"^18.0.11"},{"name":"@vitejs/plugin-react","version":"^4.2.1"},{"name":"dotenv","version":"^16.0.3"}]},"npmDepsForServer":{"dependencies":[{"name":"cookie-parser","version":"~1.4.6"},{"name":"cors","version":"^2.8.5"},{"name":"dotenv","version":"^16.0.2"},{"name":"express","version":"~4.21.0"},{"name":"helmet","version":"^6.0.0"},{"name":"morgan","version":"~1.10.0"},{"name":"superjson","version":"^2.2.1"}],"devDependencies":[{"name":"@tsconfig/node18","version":"latest"},{"name":"@types/cors","version":"^2.8.5"},{"name":"@types/express","version":"^4.17.13"},{"name":"@types/express-serve-static-core","version":"^4.17.13"},{"name":"@types/node","version":"^18.0.0"},{"name":"nodemon","version":"^2.0.19"},{"name":"rollup","version":"^4.9.6"},{"name":"rollup-plugin-esbuild","version":"^6.1.1"}]}}} \ No newline at end of file +{"_waspSdkNpmDeps":{"dependencies":[{"name":"@prisma/client","version":"5.19.1"},{"name":"prisma","version":"5.19.1"},{"name":"@tanstack/react-query","version":"^4.29.0"},{"name":"axios","version":"^1.4.0"},{"name":"express","version":"~4.21.0"},{"name":"mitt","version":"3.0.0"},{"name":"react","version":"^18.2.0"},{"name":"react-router-dom","version":"^6.26.2"},{"name":"react-hook-form","version":"^7.45.4"},{"name":"superjson","version":"^2.2.1"},{"name":"vitest","version":"^1.2.1"},{"name":"@vitest/ui","version":"^1.2.1"},{"name":"jsdom","version":"^21.1.1"},{"name":"@testing-library/react","version":"^14.1.2"},{"name":"@testing-library/jest-dom","version":"^6.3.0"},{"name":"msw","version":"^1.1.0"},{"name":"zod","version":"^3.23.8"}],"devDependencies":[{"name":"@tsconfig/node18","version":"latest"},{"name":"@types/express-serve-static-core","version":"^4.17.13"}]},"_userNpmDeps":{"userDependencies":[{"name":"react","version":"^18.2.0"},{"name":"react-dom","version":"^18.2.0"},{"name":"react-router-dom","version":"^6.26.2"},{"name":"wasp","version":"file:.wasp/out/sdk/wasp"}],"userDevDependencies":[{"name":"@types/react","version":"^18.0.37"},{"name":"prisma","version":"5.19.1"},{"name":"typescript","version":"^5.1.0"},{"name":"vite","version":"^4.3.9"}]},"_waspFrameworkNpmDeps":{"npmDepsForWebApp":{"dependencies":[{"name":"@tanstack/react-query","version":"^4.29.0"},{"name":"axios","version":"^1.4.0"}],"devDependencies":[{"name":"@tsconfig/vite-react","version":"^2.0.0"},{"name":"@types/react-dom","version":"^18.0.11"},{"name":"@vitejs/plugin-react","version":"^4.2.1"}]},"npmDepsForServer":{"dependencies":[{"name":"cookie-parser","version":"~1.4.6"},{"name":"cors","version":"^2.8.5"},{"name":"dotenv","version":"^16.0.2"},{"name":"express","version":"~4.21.0"},{"name":"helmet","version":"^6.0.0"},{"name":"morgan","version":"~1.10.0"},{"name":"superjson","version":"^2.2.1"}],"devDependencies":[{"name":"@tsconfig/node18","version":"latest"},{"name":"@types/cors","version":"^2.8.5"},{"name":"@types/express","version":"^4.17.13"},{"name":"@types/express-serve-static-core","version":"^4.17.13"},{"name":"@types/node","version":"^18.0.0"},{"name":"nodemon","version":"^2.0.19"},{"name":"rollup","version":"^4.9.6"},{"name":"rollup-plugin-esbuild","version":"^6.1.1"}]}}} \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/client/config.ts b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/client/config.ts index 82b0f80b6a..fff77cb01c 100644 --- a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/client/config.ts +++ b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/client/config.ts @@ -1,6 +1,7 @@ import { stripTrailingSlash } from '../universal/url.js' +import { env } from './env.js' -const apiUrl = stripTrailingSlash(import.meta.env.REACT_APP_API_URL) || 'http://localhost:3001'; +const apiUrl = stripTrailingSlash(env.REACT_APP_API_URL) // PUBLIC API export type ClientConfig = { diff --git a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/client/env.ts b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/client/env.ts new file mode 100644 index 0000000000..8ddab287f7 --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/client/env.ts @@ -0,0 +1,18 @@ +import * as z from 'zod' + +import { ensureEnvSchema } from '../env/validation.js' + +const userClientEnvSchema = z.object({}) + +const waspClientEnvSchema = z.object({ + REACT_APP_API_URL: z + .string({ + required_error: 'REACT_APP_API_URL is required', + }) + .default('http://localhost:3001') +}) + +const clientEnvSchema = userClientEnvSchema.merge(waspClientEnvSchema) + +// PUBLIC API +export const env = ensureEnvSchema(import.meta.env, clientEnvSchema) diff --git a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/client/index.ts b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/client/index.ts index cfce564c4d..859f8656b6 100644 --- a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/client/index.ts +++ b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/client/index.ts @@ -11,4 +11,7 @@ export enum HttpMethod { export type Route = { method: HttpMethod; path: string } // PUBLIC API -export { config, ClientConfig } from './config' +export { config, ClientConfig } from './config.js' + +// PUBLIC API +export { env } from './env.js' diff --git a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/client/config.js b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/client/config.js index 1dffbd68bc..7e6149f386 100644 --- a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/client/config.js +++ b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/client/config.js @@ -1,5 +1,6 @@ import { stripTrailingSlash } from '../universal/url.js'; -const apiUrl = stripTrailingSlash(import.meta.env.REACT_APP_API_URL) || 'http://localhost:3001'; +import { env } from './env.js'; +const apiUrl = stripTrailingSlash(env.REACT_APP_API_URL); // PUBLIC API export const config = { apiUrl, diff --git a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/client/config.js.map b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/client/config.js.map index d26d37022b..f9e186b838 100644 --- a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/client/config.js.map +++ b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/client/config.js.map @@ -1 +1 @@ -{"version":3,"file":"config.js","sourceRoot":"","sources":["../../client/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAA;AAExD,MAAM,MAAM,GAAG,kBAAkB,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,iBAAiB,CAAC,IAAI,uBAAuB,CAAC;AAOhG,aAAa;AACb,MAAM,CAAC,MAAM,MAAM,GAAiB;IAClC,MAAM;CACP,CAAA"} \ No newline at end of file +{"version":3,"file":"config.js","sourceRoot":"","sources":["../../client/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAA;AACxD,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAA;AAE9B,MAAM,MAAM,GAAG,kBAAkB,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAA;AAOxD,aAAa;AACb,MAAM,CAAC,MAAM,MAAM,GAAiB;IAClC,MAAM;CACP,CAAA"} \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/client/env.d.ts b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/client/env.d.ts new file mode 100644 index 0000000000..5a9417a810 --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/client/env.d.ts @@ -0,0 +1,3 @@ +export declare const env: { + REACT_APP_API_URL: string; +}; diff --git a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/client/env.js b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/client/env.js new file mode 100644 index 0000000000..91164c3768 --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/client/env.js @@ -0,0 +1,14 @@ +import * as z from 'zod'; +import { ensureEnvSchema } from '../env/validation.js'; +const userClientEnvSchema = z.object({}); +const waspClientEnvSchema = z.object({ + REACT_APP_API_URL: z + .string({ + required_error: 'REACT_APP_API_URL is required', + }) + .default('http://localhost:3001') +}); +const clientEnvSchema = userClientEnvSchema.merge(waspClientEnvSchema); +// PUBLIC API +export const env = ensureEnvSchema(import.meta.env, clientEnvSchema); +//# sourceMappingURL=env.js.map \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/client/env.js.map b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/client/env.js.map new file mode 100644 index 0000000000..d230f15c0d --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/client/env.js.map @@ -0,0 +1 @@ +{"version":3,"file":"env.js","sourceRoot":"","sources":["../../client/env.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,KAAK,CAAA;AAExB,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAA;AAEtD,MAAM,mBAAmB,GAAG,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,CAAA;AAExC,MAAM,mBAAmB,GAAG,CAAC,CAAC,MAAM,CAAC;IACnC,iBAAiB,EAAE,CAAC;SACjB,MAAM,CAAC;QACN,cAAc,EAAE,+BAA+B;KAChD,CAAC;SACD,OAAO,CAAC,uBAAuB,CAAC;CACpC,CAAC,CAAA;AAEF,MAAM,eAAe,GAAG,mBAAmB,CAAC,KAAK,CAAC,mBAAmB,CAAC,CAAA;AAEtE,aAAa;AACb,MAAM,CAAC,MAAM,GAAG,GAAG,eAAe,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,eAAe,CAAC,CAAA"} \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/client/index.d.ts b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/client/index.d.ts index 7f5f4bfbc8..6498c439bd 100644 --- a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/client/index.d.ts +++ b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/client/index.d.ts @@ -8,4 +8,5 @@ export type Route = { method: HttpMethod; path: string; }; -export { config, ClientConfig } from './config'; +export { config, ClientConfig } from './config.js'; +export { env } from './env.js'; diff --git a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/client/index.js b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/client/index.js index 5a7f13dcae..2f4daa7ae3 100644 --- a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/client/index.js +++ b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/client/index.js @@ -8,5 +8,7 @@ export var HttpMethod; HttpMethod["Delete"] = "DELETE"; })(HttpMethod || (HttpMethod = {})); // PUBLIC API -export { config } from './config'; +export { config } from './config.js'; +// PUBLIC API +export { env } from './env.js'; //# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/client/index.js.map b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/client/index.js.map index 879953c8df..f3611558dc 100644 --- a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/client/index.js.map +++ b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/client/index.js.map @@ -1 +1 @@ -{"version":3,"file":"index.js","sourceRoot":"","sources":["../../client/index.ts"],"names":[],"mappings":"AAAA,aAAa;AACb,mFAAmF;AACnF,MAAM,CAAN,IAAY,UAKX;AALD,WAAY,UAAU;IACrB,yBAAW,CAAA;IACX,2BAAa,CAAA;IACb,yBAAW,CAAA;IACX,+BAAiB,CAAA;AAClB,CAAC,EALW,UAAU,KAAV,UAAU,QAKrB;AAKD,aAAa;AACb,OAAO,EAAE,MAAM,EAAgB,MAAM,UAAU,CAAA"} \ No newline at end of file +{"version":3,"file":"index.js","sourceRoot":"","sources":["../../client/index.ts"],"names":[],"mappings":"AAAA,aAAa;AACb,mFAAmF;AACnF,MAAM,CAAN,IAAY,UAKX;AALD,WAAY,UAAU;IACrB,yBAAW,CAAA;IACX,2BAAa,CAAA;IACb,yBAAW,CAAA;IACX,+BAAiB,CAAA;AAClB,CAAC,EALW,UAAU,KAAV,UAAU,QAKrB;AAKD,aAAa;AACb,OAAO,EAAE,MAAM,EAAgB,MAAM,aAAa,CAAA;AAElD,aAAa;AACb,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAA"} \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/env/index.d.ts b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/env/index.d.ts new file mode 100644 index 0000000000..d1dafc25fd --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/env/index.d.ts @@ -0,0 +1,3 @@ +import * as z from 'zod'; +export declare function defineEnvValidationSchema>(schema: Schema): Schema; +export { ensureEnvSchema } from './validation.js'; diff --git a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/env/index.js b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/env/index.js new file mode 100644 index 0000000000..17e695d087 --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/env/index.js @@ -0,0 +1,7 @@ +// PUBLIC API +export function defineEnvValidationSchema(schema) { + return schema; +} +// PRIVATE API (SDK, Vite config) +export { ensureEnvSchema } from './validation.js'; +//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/env/index.js.map b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/env/index.js.map new file mode 100644 index 0000000000..75b4ff7de3 --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/env/index.js.map @@ -0,0 +1 @@ +{"version":3,"file":"index.js","sourceRoot":"","sources":["../../env/index.ts"],"names":[],"mappings":"AAEA,aAAa;AACb,MAAM,UAAU,yBAAyB,CACvC,MAAc;IAEd,OAAO,MAAM,CAAA;AACf,CAAC;AAED,iCAAiC;AACjC,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA"} \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/env/validation.d.ts b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/env/validation.d.ts new file mode 100644 index 0000000000..6bc9d059a3 --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/env/validation.d.ts @@ -0,0 +1,2 @@ +import * as z from 'zod'; +export declare function ensureEnvSchema(data: unknown, schema: Schema): z.infer; diff --git a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/env/validation.js b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/env/validation.js new file mode 100644 index 0000000000..8463eb578e --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/env/validation.js @@ -0,0 +1,23 @@ +import * as z from 'zod'; +const redColor = '\x1b[31m'; +export function ensureEnvSchema(data, schema) { + try { + return schema.parse(data); + } + catch (e) { + if (e instanceof z.ZodError) { + const errorOutput = ['', 'โ•โ• Env vars validation failed โ•โ•', '']; + for (const error of e.errors) { + errorOutput.push(` - ${error.message}`); + } + errorOutput.push(''); + errorOutput.push('โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•'); + console.error(redColor, errorOutput.join('\n')); + throw new Error('Error parsing environment variables'); + } + else { + throw e; + } + } +} +//# sourceMappingURL=validation.js.map \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/env/validation.js.map b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/env/validation.js.map new file mode 100644 index 0000000000..1d57b14a0b --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/env/validation.js.map @@ -0,0 +1 @@ +{"version":3,"file":"validation.js","sourceRoot":"","sources":["../../env/validation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,KAAK,CAAA;AAExB,MAAM,QAAQ,GAAG,UAAU,CAAA;AAE3B,MAAM,UAAU,eAAe,CAC7B,IAAa,EACb,MAAc;IAEd,IAAI,CAAC;QACH,OAAO,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;IAC3B,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,IAAI,CAAC,YAAY,CAAC,CAAC,QAAQ,EAAE,CAAC;YAC5B,MAAM,WAAW,GAAG,CAAC,EAAE,EAAE,kCAAkC,EAAE,EAAE,CAAC,CAAA;YAChE,KAAK,MAAM,KAAK,IAAI,CAAC,CAAC,MAAM,EAAE,CAAC;gBAC7B,WAAW,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC,OAAO,EAAE,CAAC,CAAA;YACzC,CAAC;YACD,WAAW,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;YACpB,WAAW,CAAC,IAAI,CAAC,kCAAkC,CAAC,CAAA;YACpD,OAAO,CAAC,KAAK,CAAC,QAAQ,EAAE,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAA;YAC/C,MAAM,IAAI,KAAK,CAAC,qCAAqC,CAAC,CAAA;QACxD,CAAC;aAAM,CAAC;YACN,MAAM,CAAC,CAAA;QACT,CAAC;IACH,CAAC;AACH,CAAC"} \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/server/HttpError.js.map b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/server/HttpError.js.map index 8163354e1b..462e1c1811 100644 --- a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/server/HttpError.js.map +++ b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/server/HttpError.js.map @@ -1 +1 @@ -{"version":3,"file":"HttpError.js","sourceRoot":"","sources":["../../server/HttpError.ts"],"names":[],"mappings":"AAAA,MAAM,OAAO,SAAU,SAAQ,KAAK;IAIlC,YAAa,UAAkB,EAAE,OAAgB,EAAE,IAA8B,EAAE,OAAsB;QACvG,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;QAEvB,IAAI,KAAK,CAAC,iBAAiB,EAAE,CAAC;YAC5B,KAAK,CAAC,iBAAiB,CAAC,IAAI,EAAE,SAAS,CAAC,CAAA;QAC1C,CAAC;QAED,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,WAAW,CAAC,IAAI,CAAA;QAEjC,IAAI,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,UAAU,CAAC,IAAI,UAAU,IAAI,GAAG,IAAI,UAAU,GAAG,GAAG,CAAC,EAAE,CAAC;YAC7E,MAAM,IAAI,KAAK,CAAC,mDAAmD,CAAC,CAAA;QACtE,CAAC;QACD,IAAI,CAAC,UAAU,GAAG,UAAU,CAAA;QAE5B,IAAI,IAAI,EAAE,CAAC;YACT,IAAI,CAAC,IAAI,GAAG,IAAI,CAAA;QAClB,CAAC;IACH,CAAC;CACF"} \ No newline at end of file +{"version":3,"file":"HttpError.js","sourceRoot":"","sources":["../../server/HttpError.ts"],"names":[],"mappings":"AAAA,MAAM,OAAO,SAAU,SAAQ,KAAK;IAIlC,YACE,UAAkB,EAClB,OAAgB,EAChB,IAA8B,EAC9B,OAAsB;QAEtB,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;QAEvB,IAAI,KAAK,CAAC,iBAAiB,EAAE,CAAC;YAC5B,KAAK,CAAC,iBAAiB,CAAC,IAAI,EAAE,SAAS,CAAC,CAAA;QAC1C,CAAC;QAED,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,WAAW,CAAC,IAAI,CAAA;QAEjC,IACE,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,UAAU,CAAC,IAAI,UAAU,IAAI,GAAG,IAAI,UAAU,GAAG,GAAG,CAAC,EACxE,CAAC;YACD,MAAM,IAAI,KAAK,CAAC,mDAAmD,CAAC,CAAA;QACtE,CAAC;QACD,IAAI,CAAC,UAAU,GAAG,UAAU,CAAA;QAE5B,IAAI,IAAI,EAAE,CAAC;YACT,IAAI,CAAC,IAAI,GAAG,IAAI,CAAA;QAClB,CAAC;IACH,CAAC;CACF"} \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/server/config.d.ts b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/server/config.d.ts index 68bb448c56..70f438bdf0 100644 --- a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/server/config.d.ts +++ b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/server/config.d.ts @@ -1,16 +1,13 @@ -type BaseConfig = { - allowedCORSOrigins: string | string[]; -}; -type CommonConfig = BaseConfig & { - env: string; +import { env } from './env.js'; +type NodeEnv = typeof env.NODE_ENV; +type Config = { + env: NodeEnv; isDevelopment: boolean; port: number; - databaseUrl: string | undefined; -}; -type EnvConfig = BaseConfig & { + databaseUrl: string; frontendUrl: string; serverUrl: string; + allowedCORSOrigins: string | string[]; }; -type Config = CommonConfig & EnvConfig; -declare const resolvedConfig: Config; -export default resolvedConfig; +declare const config: Config; +export default config; diff --git a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/server/config.js b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/server/config.js index e4f89082c8..dcc80722ab 100644 --- a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/server/config.js +++ b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/server/config.js @@ -1,43 +1,21 @@ -var _a; -import merge from 'lodash.merge'; -import { stripTrailingSlash } from "../universal/url.js"; -const nodeEnv = (_a = process.env.NODE_ENV) !== null && _a !== void 0 ? _a : 'development'; +import { env } from './env.js'; +import { stripTrailingSlash } from '../universal/url.js'; +const frontendUrl = stripTrailingSlash(env.WASP_WEB_CLIENT_URL); +const serverUrl = stripTrailingSlash(env.WASP_SERVER_URL); +const allowedCORSOriginsPerEnv = { + development: '*', + production: [frontendUrl] +}; +const allowedCORSOrigins = allowedCORSOriginsPerEnv[env.NODE_ENV]; const config = { - all: { - env: nodeEnv, - isDevelopment: nodeEnv === 'development', - port: process.env.PORT ? parseInt(process.env.PORT) : 3001, - databaseUrl: process.env.DATABASE_URL, - allowedCORSOrigins: [], - }, - development: getDevelopmentConfig(), - production: getProductionConfig(), + frontendUrl, + serverUrl, + allowedCORSOrigins, + env: env.NODE_ENV, + isDevelopment: env.NODE_ENV === 'development', + port: env.PORT, + databaseUrl: env.DATABASE_URL, }; -const resolvedConfig = merge(config.all, config[nodeEnv]); // PUBLIC API -export default resolvedConfig; -function getDevelopmentConfig() { - var _a, _b; - const frontendUrl = stripTrailingSlash((_a = process.env.WASP_WEB_CLIENT_URL) !== null && _a !== void 0 ? _a : 'http://localhost:3000/'); - const serverUrl = stripTrailingSlash((_b = process.env.WASP_SERVER_URL) !== null && _b !== void 0 ? _b : 'http://localhost:3001'); - return { - // @ts-ignore - frontendUrl, - // @ts-ignore - serverUrl, - allowedCORSOrigins: '*', - }; -} -function getProductionConfig() { - const frontendUrl = stripTrailingSlash(process.env.WASP_WEB_CLIENT_URL); - const serverUrl = stripTrailingSlash(process.env.WASP_SERVER_URL); - return { - // @ts-ignore - frontendUrl, - // @ts-ignore - serverUrl, - // @ts-ignore - allowedCORSOrigins: [frontendUrl], - }; -} +export default config; //# sourceMappingURL=config.js.map \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/server/config.js.map b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/server/config.js.map index e284580bbd..9fd077fa81 100644 --- a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/server/config.js.map +++ b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/server/config.js.map @@ -1 +1 @@ -{"version":3,"file":"config.js","sourceRoot":"","sources":["../../server/config.ts"],"names":[],"mappings":";AAAA,OAAO,KAAK,MAAM,cAAc,CAAA;AAEhC,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAEzD,MAAM,OAAO,GAAG,MAAA,OAAO,CAAC,GAAG,CAAC,QAAQ,mCAAI,aAAa,CAAA;AAyBrD,MAAM,MAAM,GAIR;IACF,GAAG,EAAE;QACH,GAAG,EAAE,OAAO;QACZ,aAAa,EAAE,OAAO,KAAK,aAAa;QACxC,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI;QAC1D,WAAW,EAAE,OAAO,CAAC,GAAG,CAAC,YAAY;QACrC,kBAAkB,EAAE,EAAE;KACvB;IACD,WAAW,EAAE,oBAAoB,EAAE;IACnC,UAAU,EAAE,mBAAmB,EAAE;CAClC,CAAA;AAED,MAAM,cAAc,GAAW,KAAK,CAAC,MAAM,CAAC,GAAG,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC,CAAA;AACjE,aAAa;AACb,eAAe,cAAc,CAAA;AAE7B,SAAS,oBAAoB;;IAC3B,MAAM,WAAW,GAAG,kBAAkB,CAAC,MAAA,OAAO,CAAC,GAAG,CAAC,mBAAmB,mCAAI,wBAAwB,CAAC,CAAC;IACpG,MAAM,SAAS,GAAG,kBAAkB,CAAC,MAAA,OAAO,CAAC,GAAG,CAAC,eAAe,mCAAI,uBAAuB,CAAC,CAAC;IAC7F,OAAO;QACL,aAAa;QACb,WAAW;QACX,aAAa;QACb,SAAS;QACT,kBAAkB,EAAE,GAAG;KACxB,CAAA;AACH,CAAC;AAED,SAAS,mBAAmB;IAC1B,MAAM,WAAW,GAAG,kBAAkB,CAAC,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC,CAAC;IACxE,MAAM,SAAS,GAAG,kBAAkB,CAAC,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC;IAClE,OAAO;QACL,aAAa;QACb,WAAW;QACX,aAAa;QACb,SAAS;QACT,aAAa;QACb,kBAAkB,EAAE,CAAC,WAAW,CAAC;KAClC,CAAA;AACH,CAAC"} \ No newline at end of file +{"version":3,"file":"config.js","sourceRoot":"","sources":["../../server/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAA;AAC9B,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAA;AAcxD,MAAM,WAAW,GAAG,kBAAkB,CAAC,GAAG,CAAC,mBAAmB,CAAC,CAAA;AAC/D,MAAM,SAAS,GAAG,kBAAkB,CAAC,GAAG,CAAC,eAAe,CAAC,CAAA;AAEzD,MAAM,wBAAwB,GAAuC;IACnE,WAAW,EAAE,GAAG;IAChB,UAAU,EAAE,CAAC,WAAW,CAAC;CAC1B,CAAA;AACD,MAAM,kBAAkB,GAAG,wBAAwB,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAA;AAEjE,MAAM,MAAM,GAAW;IACrB,WAAW;IACX,SAAS;IACT,kBAAkB;IAClB,GAAG,EAAE,GAAG,CAAC,QAAQ;IACjB,aAAa,EAAE,GAAG,CAAC,QAAQ,KAAK,aAAa;IAC7C,IAAI,EAAE,GAAG,CAAC,IAAI;IACd,WAAW,EAAE,GAAG,CAAC,YAAY;CAC9B,CAAA;AAED,aAAa;AACb,eAAe,MAAM,CAAA"} \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/server/env.d.ts b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/server/env.d.ts new file mode 100644 index 0000000000..667ea30e35 --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/server/env.d.ts @@ -0,0 +1,17 @@ +export declare const env: { + PORT: number; + DATABASE_URL: string; + SKIP_EMAIL_VERIFICATION_IN_DEV: boolean; + NODE_ENV: "development"; + WASP_SERVER_URL: string; + WASP_WEB_CLIENT_URL: string; + PG_BOSS_NEW_OPTIONS?: string | undefined; +} | { + PORT: number; + DATABASE_URL: string; + SKIP_EMAIL_VERIFICATION_IN_DEV: boolean; + NODE_ENV: "production"; + WASP_SERVER_URL: string; + WASP_WEB_CLIENT_URL: string; + PG_BOSS_NEW_OPTIONS?: string | undefined; +}; diff --git a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/server/env.js b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/server/env.js new file mode 100644 index 0000000000..1bfc3e0b6e --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/server/env.js @@ -0,0 +1,55 @@ +import * as z from 'zod'; +import { ensureEnvSchema } from '../env/validation.js'; +const userServerEnvSchema = z.object({}); +const waspServerCommonSchema = z.object({ + PORT: z.coerce.number().default(3001), + DATABASE_URL: z.string({ + required_error: 'DATABASE_URL is required', + }), + PG_BOSS_NEW_OPTIONS: z.string().optional(), + SKIP_EMAIL_VERIFICATION_IN_DEV: z + .enum(['true', 'false'], { + message: 'SKIP_EMAIL_VERIFICATION_IN_DEV must be either "true" or "false"', + }) + .transform((value) => value === 'true') + .default('false'), +}); +const serverUrlSchema = z + .string({ + required_error: 'WASP_SERVER_URL is required', +}) + .url({ + message: 'WASP_SERVER_URL must be a valid URL', +}); +const clientUrlSchema = z + .string({ + required_error: 'WASP_WEB_CLIENT_URL is required', +}) + .url({ + message: 'WASP_WEB_CLIENT_URL must be a valid URL', +}); +// In development, we provide default values for some environment variables +// to make the development process easier. +const serverDevSchema = z.object({ + NODE_ENV: z.literal('development'), + WASP_SERVER_URL: serverUrlSchema + .default('http://localhost:3001'), + WASP_WEB_CLIENT_URL: clientUrlSchema + .default('http://localhost:3000/'), +}); +const serverProdSchema = z.object({ + NODE_ENV: z.literal('production'), + WASP_SERVER_URL: serverUrlSchema, + WASP_WEB_CLIENT_URL: clientUrlSchema, +}); +const serverCommonSchema = userServerEnvSchema.merge(waspServerCommonSchema); +const serverEnvSchema = z.discriminatedUnion('NODE_ENV', [ + serverDevSchema.merge(serverCommonSchema), + serverProdSchema.merge(serverCommonSchema) +]); +// PUBLIC API +export const env = ensureEnvSchema(process.env, serverEnvSchema); +function getRequiredEnvVarErrorMessage(featureName, envVarName) { + return `${envVarName} is required when using ${featureName}`; +} +//# sourceMappingURL=env.js.map \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/server/env.js.map b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/server/env.js.map new file mode 100644 index 0000000000..3dfa10e116 --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/server/env.js.map @@ -0,0 +1 @@ +{"version":3,"file":"env.js","sourceRoot":"","sources":["../../server/env.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,KAAK,CAAA;AAExB,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAA;AAEtD,MAAM,mBAAmB,GAAG,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,CAAA;AAExC,MAAM,sBAAsB,GAAG,CAAC,CAAC,MAAM,CAAC;IACtC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC;IACrC,YAAY,EAAE,CAAC,CAAC,MAAM,CAAC;QACrB,cAAc,EAAE,0BAA0B;KAC3C,CAAC;IACF,mBAAmB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC1C,8BAA8B,EAAE,CAAC;SAC9B,IAAI,CAAC,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE;QACvB,OAAO,EAAE,iEAAiE;KAC3E,CAAC;SACD,SAAS,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,KAAK,MAAM,CAAC;SACtC,OAAO,CAAC,OAAO,CAAC;CACpB,CAAC,CAAA;AAEF,MAAM,eAAe,GAAG,CAAC;KACtB,MAAM,CAAC;IACN,cAAc,EAAE,6BAA6B;CAC9C,CAAC;KACD,GAAG,CAAC;IACH,OAAO,EAAE,qCAAqC;CAC/C,CAAC,CAAA;AAEJ,MAAM,eAAe,GAAG,CAAC;KACtB,MAAM,CAAC;IACN,cAAc,EAAE,iCAAiC;CAClD,CAAC;KACD,GAAG,CAAC;IACH,OAAO,EAAE,yCAAyC;CACnD,CAAC,CAAA;AAGJ,2EAA2E;AAC3E,0CAA0C;AAC1C,MAAM,eAAe,GAAG,CAAC,CAAC,MAAM,CAAC;IAC/B,QAAQ,EAAE,CAAC,CAAC,OAAO,CAAC,aAAa,CAAC;IAClC,eAAe,EAAE,eAAe;SAC7B,OAAO,CAAC,uBAAuB,CAAC;IACnC,mBAAmB,EAAE,eAAe;SACjC,OAAO,CAAC,wBAAwB,CAAC;CACrC,CAAC,CAAA;AAEF,MAAM,gBAAgB,GAAG,CAAC,CAAC,MAAM,CAAC;IAChC,QAAQ,EAAE,CAAC,CAAC,OAAO,CAAC,YAAY,CAAC;IACjC,eAAe,EAAE,eAAe;IAChC,mBAAmB,EAAE,eAAe;CACrC,CAAC,CAAA;AAEF,MAAM,kBAAkB,GAAG,mBAAmB,CAAC,KAAK,CAAC,sBAAsB,CAAC,CAAA;AAC5E,MAAM,eAAe,GAAG,CAAC,CAAC,kBAAkB,CAAC,UAAU,EAAE;IACvD,eAAe,CAAC,KAAK,CAAC,kBAAkB,CAAC;IACzC,gBAAgB,CAAC,KAAK,CAAC,kBAAkB,CAAC;CAC3C,CAAC,CAAA;AAEF,aAAa;AACb,MAAM,CAAC,MAAM,GAAG,GAAG,eAAe,CAAC,OAAO,CAAC,GAAG,EAAE,eAAe,CAAC,CAAA;AAEhE,SAAS,6BAA6B,CAAC,WAAmB,EAAE,UAAkB;IAC5E,OAAO,GAAG,UAAU,2BAA2B,WAAW,EAAE,CAAA;AAC9D,CAAC"} \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/server/index.d.ts b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/server/index.d.ts index dee9613a53..a108f6123f 100644 --- a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/server/index.d.ts +++ b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/server/index.d.ts @@ -4,4 +4,5 @@ export { default as prisma } from './dbClient.js'; export { type ServerSetupFn } from './types/index.js'; export { HttpError } from './HttpError.js'; export { MiddlewareConfigFn } from './middleware/index.js'; +export { env } from './env.js'; export type DbSeedFn = (prisma: PrismaClient) => Promise; diff --git a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/server/index.js b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/server/index.js index 0e53e0deab..8e75e6f78c 100644 --- a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/server/index.js +++ b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/server/index.js @@ -4,4 +4,6 @@ export { default as config } from './config.js'; export { default as prisma } from './dbClient.js'; // PUBLIC API export { HttpError } from './HttpError.js'; +// PUBLIC API +export { env } from './env.js'; //# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/server/index.js.map b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/server/index.js.map index b84ac1b303..87ed22f3af 100644 --- a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/server/index.js.map +++ b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/server/index.js.map @@ -1 +1 @@ -{"version":3,"file":"index.js","sourceRoot":"","sources":["../../server/index.ts"],"names":[],"mappings":"AAEA,aAAa;AACb,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,aAAa,CAAA;AAC/C,aAAa;AACb,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,eAAe,CAAA;AAGjD,aAAa;AACb,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA"} \ No newline at end of file +{"version":3,"file":"index.js","sourceRoot":"","sources":["../../server/index.ts"],"names":[],"mappings":"AAEA,aAAa;AACb,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,aAAa,CAAA;AAC/C,aAAa;AACb,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,eAAe,CAAA;AAGjD,aAAa;AACb,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAG1C,aAAa;AACb,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAA"} \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/universal/url.d.ts b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/universal/url.d.ts index aa893e7838..7ceb994694 100644 --- a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/universal/url.d.ts +++ b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/universal/url.d.ts @@ -1 +1,2 @@ -export declare function stripTrailingSlash(url?: string): string | undefined; +export declare function stripTrailingSlash(url: string): string; +export declare function stripTrailingSlash(url: undefined): undefined; diff --git a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/universal/url.js.map b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/universal/url.js.map index a7bad96020..e236f1b977 100644 --- a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/universal/url.js.map +++ b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/universal/url.js.map @@ -1 +1 @@ -{"version":3,"file":"url.js","sourceRoot":"","sources":["../../universal/url.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,kBAAkB,CAAC,GAAY;IAC3C,OAAO,GAAG,aAAH,GAAG,uBAAH,GAAG,CAAE,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;AACnC,CAAC"} \ No newline at end of file +{"version":3,"file":"url.js","sourceRoot":"","sources":["../../universal/url.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,kBAAkB,CAAC,GAAY;IAC3C,OAAO,GAAG,aAAH,GAAG,uBAAH,GAAG,CAAE,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;AACnC,CAAC"} \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/env/index.ts b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/env/index.ts new file mode 100644 index 0000000000..4c783125c1 --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/env/index.ts @@ -0,0 +1,11 @@ +import * as z from 'zod' + +// PUBLIC API +export function defineEnvValidationSchema>( + schema: Schema, +): Schema { + return schema +} + +// PRIVATE API (SDK, Vite config) +export { ensureEnvSchema } from './validation.js' diff --git a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/env/validation.ts b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/env/validation.ts new file mode 100644 index 0000000000..a5bc334754 --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/env/validation.ts @@ -0,0 +1,25 @@ +import * as z from 'zod' + +const redColor = '\x1b[31m' + +export function ensureEnvSchema( + data: unknown, + schema: Schema +): z.infer { + try { + return schema.parse(data) + } catch (e) { + if (e instanceof z.ZodError) { + const errorOutput = ['', 'โ•โ• Env vars validation failed โ•โ•', ''] + for (const error of e.errors) { + errorOutput.push(` - ${error.message}`) + } + errorOutput.push('') + errorOutput.push('โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•') + console.error(redColor, errorOutput.join('\n')) + throw new Error('Error parsing environment variables') + } else { + throw e + } + } +} diff --git a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/package.json b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/package.json index 2589c5362f..55c07a5084 100644 --- a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/package.json +++ b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/package.json @@ -8,7 +8,6 @@ "axios": "^1.4.0", "express": "~4.21.0", "jsdom": "^21.1.1", - "lodash.merge": "^4.6.2", "mitt": "3.0.0", "msw": "^1.1.0", "prisma": "5.19.1", @@ -16,7 +15,8 @@ "react-hook-form": "^7.45.4", "react-router-dom": "^6.26.2", "superjson": "^2.2.1", - "vitest": "^1.2.1" + "vitest": "^1.2.1", + "zod": "^3.23.8" }, "devDependencies": { "@tsconfig/node18": "latest", @@ -51,6 +51,7 @@ "./core/storage": "./dist/core/storage.js", "./dev": "./dist/dev/index.js", "./entities": "./dist/entities/index.js", + "./env": "./dist/env/index.js", "./ext-src/*": "./dist/ext-src/*.js", "./operations": "./dist/operations/index.js", "./operations/*": "./dist/operations/*", diff --git a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/server/HttpError.ts b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/server/HttpError.ts index d05a2c841b..bce964712d 100644 --- a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/server/HttpError.ts +++ b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/server/HttpError.ts @@ -2,7 +2,12 @@ export class HttpError extends Error { public statusCode: number public data: unknown - constructor (statusCode: number, message?: string, data?: Record, options?: ErrorOptions) { + constructor( + statusCode: number, + message?: string, + data?: Record, + options?: ErrorOptions + ) { super(message, options) if (Error.captureStackTrace) { @@ -11,7 +16,9 @@ export class HttpError extends Error { this.name = this.constructor.name - if (!(Number.isInteger(statusCode) && statusCode >= 400 && statusCode < 600)) { + if ( + !(Number.isInteger(statusCode) && statusCode >= 400 && statusCode < 600) + ) { throw new Error('statusCode has to be integer in range [400, 600).') } this.statusCode = statusCode diff --git a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/server/config.ts b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/server/config.ts index 1dc9bafb93..3a000a897f 100644 --- a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/server/config.ts +++ b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/server/config.ts @@ -1,73 +1,36 @@ -import merge from 'lodash.merge' +import { env } from './env.js' +import { stripTrailingSlash } from '../universal/url.js' -import { stripTrailingSlash } from "../universal/url.js"; +type NodeEnv = typeof env.NODE_ENV -const nodeEnv = process.env.NODE_ENV ?? 'development' - -// TODO: -// - Use dotenv library to consume env vars from a file. -// - Use convict library to define schema and validate env vars. -// https://codingsans.com/blog/node-config-best-practices - -type BaseConfig = { - allowedCORSOrigins: string | string[]; -} - -type CommonConfig = BaseConfig & { - env: string; +type Config = { + env: NodeEnv; isDevelopment: boolean; port: number; - databaseUrl: string | undefined; -} - -type EnvConfig = BaseConfig & { + databaseUrl: string; frontendUrl: string; serverUrl: string; + allowedCORSOrigins: string | string[]; } -type Config = CommonConfig & EnvConfig +const frontendUrl = stripTrailingSlash(env.WASP_WEB_CLIENT_URL) +const serverUrl = stripTrailingSlash(env.WASP_SERVER_URL) -const config: { - all: CommonConfig, - development: EnvConfig, - production: EnvConfig, -} = { - all: { - env: nodeEnv, - isDevelopment: nodeEnv === 'development', - port: process.env.PORT ? parseInt(process.env.PORT) : 3001, - databaseUrl: process.env.DATABASE_URL, - allowedCORSOrigins: [], - }, - development: getDevelopmentConfig(), - production: getProductionConfig(), +const allowedCORSOriginsPerEnv: Record = { + development: '*', + production: [frontendUrl] } - -const resolvedConfig: Config = merge(config.all, config[nodeEnv]) -// PUBLIC API -export default resolvedConfig - -function getDevelopmentConfig(): EnvConfig { - const frontendUrl = stripTrailingSlash(process.env.WASP_WEB_CLIENT_URL ?? 'http://localhost:3000/'); - const serverUrl = stripTrailingSlash(process.env.WASP_SERVER_URL ?? 'http://localhost:3001'); - return { - // @ts-ignore - frontendUrl, - // @ts-ignore - serverUrl, - allowedCORSOrigins: '*', - } +const allowedCORSOrigins = allowedCORSOriginsPerEnv[env.NODE_ENV] + +const config: Config = { + frontendUrl, + serverUrl, + allowedCORSOrigins, + env: env.NODE_ENV, + isDevelopment: env.NODE_ENV === 'development', + port: env.PORT, + databaseUrl: env.DATABASE_URL, } -function getProductionConfig(): EnvConfig { - const frontendUrl = stripTrailingSlash(process.env.WASP_WEB_CLIENT_URL); - const serverUrl = stripTrailingSlash(process.env.WASP_SERVER_URL); - return { - // @ts-ignore - frontendUrl, - // @ts-ignore - serverUrl, - // @ts-ignore - allowedCORSOrigins: [frontendUrl], - } -} +// PUBLIC API +export default config diff --git a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/server/env.ts b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/server/env.ts new file mode 100644 index 0000000000..c1bd8f5c0f --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/server/env.ts @@ -0,0 +1,65 @@ +import * as z from 'zod' + +import { ensureEnvSchema } from '../env/validation.js' + +const userServerEnvSchema = z.object({}) + +const waspServerCommonSchema = z.object({ + PORT: z.coerce.number().default(3001), + DATABASE_URL: z.string({ + required_error: 'DATABASE_URL is required', + }), + PG_BOSS_NEW_OPTIONS: z.string().optional(), + SKIP_EMAIL_VERIFICATION_IN_DEV: z + .enum(['true', 'false'], { + message: 'SKIP_EMAIL_VERIFICATION_IN_DEV must be either "true" or "false"', + }) + .transform((value) => value === 'true') + .default('false'), +}) + +const serverUrlSchema = z + .string({ + required_error: 'WASP_SERVER_URL is required', + }) + .url({ + message: 'WASP_SERVER_URL must be a valid URL', + }) + +const clientUrlSchema = z + .string({ + required_error: 'WASP_WEB_CLIENT_URL is required', + }) + .url({ + message: 'WASP_WEB_CLIENT_URL must be a valid URL', + }) + + +// In development, we provide default values for some environment variables +// to make the development process easier. +const serverDevSchema = z.object({ + NODE_ENV: z.literal('development'), + WASP_SERVER_URL: serverUrlSchema + .default('http://localhost:3001'), + WASP_WEB_CLIENT_URL: clientUrlSchema + .default('http://localhost:3000/'), +}) + +const serverProdSchema = z.object({ + NODE_ENV: z.literal('production'), + WASP_SERVER_URL: serverUrlSchema, + WASP_WEB_CLIENT_URL: clientUrlSchema, +}) + +const serverCommonSchema = userServerEnvSchema.merge(waspServerCommonSchema) +const serverEnvSchema = z.discriminatedUnion('NODE_ENV', [ + serverDevSchema.merge(serverCommonSchema), + serverProdSchema.merge(serverCommonSchema) +]) + +// PUBLIC API +export const env = ensureEnvSchema(process.env, serverEnvSchema) + +function getRequiredEnvVarErrorMessage(featureName: string, envVarName: string) { + return `${envVarName} is required when using ${featureName}` +} diff --git a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/server/index.ts b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/server/index.ts index 535077900b..0f95deb90a 100644 --- a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/server/index.ts +++ b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/server/index.ts @@ -10,6 +10,8 @@ export { type ServerSetupFn } from './types/index.js' export { HttpError } from './HttpError.js' // PUBLIC API export { MiddlewareConfigFn } from './middleware/index.js' +// PUBLIC API +export { env } from './env.js' // PUBLIC API export type DbSeedFn = (prisma: PrismaClient) => Promise diff --git a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/universal/url.ts b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/universal/url.ts index d21c06c65c..126678dac9 100644 --- a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/universal/url.ts +++ b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/universal/url.ts @@ -1,3 +1,5 @@ +export function stripTrailingSlash(url: string): string +export function stripTrailingSlash(url: undefined): undefined export function stripTrailingSlash(url?: string): string | undefined { return url?.replace(/\/$/, ""); } diff --git a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/server/package.json b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/server/package.json index 7a57413e11..51ec87354a 100644 --- a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/server/package.json +++ b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/server/package.json @@ -34,9 +34,8 @@ "bundle-and-start": "npm run bundle && npm run start", "db-migrate-prod": "prisma migrate deploy --schema=../db/schema.prisma", "db-seed": "npm run bundle && node --enable-source-maps -r dotenv/config bundle/dbSeed.js", - "start": "npm run validate-env && node --enable-source-maps -r dotenv/config bundle/server.js", + "start": "node --enable-source-maps -r dotenv/config bundle/server.js", "start-production": "NODE_ENV=production npm run start", - "validate-env": "node -r dotenv/config ./scripts/validate-env.mjs", "watch": "nodemon --exec 'npm run bundle-and-start || exit 1'" }, "type": "module", diff --git a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/server/scripts/validate-env.mjs b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/server/scripts/validate-env.mjs deleted file mode 100644 index f6e50aceca..0000000000 --- a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/server/scripts/validate-env.mjs +++ /dev/null @@ -1,5 +0,0 @@ -import { throwIfNotValidAbsoluteURL } from 'wasp/universal/validators'; - -console.info("๐Ÿ” Validating environment variables..."); -throwIfNotValidAbsoluteURL(process.env.WASP_WEB_CLIENT_URL, 'Environment variable WASP_WEB_CLIENT_URL'); -throwIfNotValidAbsoluteURL(process.env.WASP_SERVER_URL, 'Environment variable WASP_SERVER_URL'); diff --git a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/server/src/polyfill.ts b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/server/src/polyfill.ts index 7931d12eb0..9279daf6da 100644 --- a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/server/src/polyfill.ts +++ b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/server/src/polyfill.ts @@ -1,11 +1,10 @@ // This is a polyfill for Node.js 18 webcrypto API so Lucia can use it // for random number generation. -import { webcrypto } from "node:crypto"; +import { webcrypto } from 'node:crypto' // NOTE: node < 19 doesn't have Crypto API, which we need for Lucia, so we apply the polyfill if Crypto API is not defined. -if (typeof globalThis.crypto === "undefined") { +if (typeof globalThis.crypto === 'undefined') { // @ts-ignore - globalThis.crypto = webcrypto as Crypto; + globalThis.crypto = webcrypto as Crypto } - diff --git a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/web-app/package.json b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/web-app/package.json index c62763be23..3a28ba8e99 100644 --- a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/web-app/package.json +++ b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/web-app/package.json @@ -18,8 +18,7 @@ "devDependencies": { "@tsconfig/vite-react": "^2.0.0", "@types/react-dom": "^18.0.11", - "@vitejs/plugin-react": "^4.2.1", - "dotenv": "^16.0.3" + "@vitejs/plugin-react": "^4.2.1" }, "engineStrict": true, "engines": { @@ -28,9 +27,8 @@ "name": "waspCompile", "private": true, "scripts": { - "build": "npm run validate-env && tsc && vite build", - "start": "npm run validate-env && vite", - "validate-env": "node -r dotenv/config ./scripts/validate-env.mjs" + "build": "tsc && vite build", + "start": "vite" }, "type": "module", "version": "0.0.0" diff --git a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/web-app/scripts/validate-env.mjs b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/web-app/scripts/validate-env.mjs deleted file mode 100644 index 18ee507c9e..0000000000 --- a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/web-app/scripts/validate-env.mjs +++ /dev/null @@ -1,4 +0,0 @@ -import { throwIfNotValidAbsoluteURL } from 'wasp/universal/validators'; - -console.info("๐Ÿ” Validating environment variables..."); -throwIfNotValidAbsoluteURL(process.env.REACT_APP_API_URL, 'Environemnt variable REACT_APP_API_URL'); diff --git a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/web-app/src/components/DefaultRootErrorBoundary.tsx b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/web-app/src/components/DefaultRootErrorBoundary.tsx index 7af24e15d6..1e931935e6 100644 --- a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/web-app/src/components/DefaultRootErrorBoundary.tsx +++ b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/web-app/src/components/DefaultRootErrorBoundary.tsx @@ -7,7 +7,10 @@ export function DefaultRootErrorBoundary() { console.error(error) return ( -
There was an error rendering this page. Check the browser console for more information.
+
+ There was an error rendering this page. Check the browser console for + more information. +
) } diff --git a/waspc/e2e-test/test-outputs/waspComplexTest-golden/files.manifest b/waspc/e2e-test/test-outputs/waspComplexTest-golden/files.manifest index 16065f16df..10fb40da59 100644 --- a/waspc/e2e-test/test-outputs/waspComplexTest-golden/files.manifest +++ b/waspc/e2e-test/test-outputs/waspComplexTest-golden/files.manifest @@ -37,6 +37,7 @@ waspComplexTest/.wasp/out/sdk/wasp/client/config.ts waspComplexTest/.wasp/out/sdk/wasp/client/crud/index.ts waspComplexTest/.wasp/out/sdk/wasp/client/crud/operationsHelpers.ts waspComplexTest/.wasp/out/sdk/wasp/client/crud/tasks.ts +waspComplexTest/.wasp/out/sdk/wasp/client/env.ts waspComplexTest/.wasp/out/sdk/wasp/client/index.ts waspComplexTest/.wasp/out/sdk/wasp/client/operations/actions/core.ts waspComplexTest/.wasp/out/sdk/wasp/client/operations/actions/index.ts @@ -155,6 +156,9 @@ waspComplexTest/.wasp/out/sdk/wasp/dist/client/crud/operationsHelpers.js.map waspComplexTest/.wasp/out/sdk/wasp/dist/client/crud/tasks.d.ts waspComplexTest/.wasp/out/sdk/wasp/dist/client/crud/tasks.js waspComplexTest/.wasp/out/sdk/wasp/dist/client/crud/tasks.js.map +waspComplexTest/.wasp/out/sdk/wasp/dist/client/env.d.ts +waspComplexTest/.wasp/out/sdk/wasp/dist/client/env.js +waspComplexTest/.wasp/out/sdk/wasp/dist/client/env.js.map waspComplexTest/.wasp/out/sdk/wasp/dist/client/index.d.ts waspComplexTest/.wasp/out/sdk/wasp/dist/client/index.js waspComplexTest/.wasp/out/sdk/wasp/dist/client/index.js.map @@ -224,6 +228,12 @@ waspComplexTest/.wasp/out/sdk/wasp/dist/dev/index.js.map waspComplexTest/.wasp/out/sdk/wasp/dist/entities/index.d.ts waspComplexTest/.wasp/out/sdk/wasp/dist/entities/index.js waspComplexTest/.wasp/out/sdk/wasp/dist/entities/index.js.map +waspComplexTest/.wasp/out/sdk/wasp/dist/env/index.d.ts +waspComplexTest/.wasp/out/sdk/wasp/dist/env/index.js +waspComplexTest/.wasp/out/sdk/wasp/dist/env/index.js.map +waspComplexTest/.wasp/out/sdk/wasp/dist/env/validation.d.ts +waspComplexTest/.wasp/out/sdk/wasp/dist/env/validation.js +waspComplexTest/.wasp/out/sdk/wasp/dist/env/validation.js.map waspComplexTest/.wasp/out/sdk/wasp/dist/ext-src/MainPage.d.ts waspComplexTest/.wasp/out/sdk/wasp/dist/ext-src/MainPage.jsx waspComplexTest/.wasp/out/sdk/wasp/dist/ext-src/MainPage.jsx.map @@ -278,9 +288,6 @@ waspComplexTest/.wasp/out/sdk/wasp/dist/server/auth/hooks.js.map waspComplexTest/.wasp/out/sdk/wasp/dist/server/auth/index.d.ts waspComplexTest/.wasp/out/sdk/wasp/dist/server/auth/index.js waspComplexTest/.wasp/out/sdk/wasp/dist/server/auth/index.js.map -waspComplexTest/.wasp/out/sdk/wasp/dist/server/auth/oauth/env.d.ts -waspComplexTest/.wasp/out/sdk/wasp/dist/server/auth/oauth/env.js -waspComplexTest/.wasp/out/sdk/wasp/dist/server/auth/oauth/env.js.map waspComplexTest/.wasp/out/sdk/wasp/dist/server/auth/oauth/index.d.ts waspComplexTest/.wasp/out/sdk/wasp/dist/server/auth/oauth/index.js waspComplexTest/.wasp/out/sdk/wasp/dist/server/auth/oauth/index.js.map @@ -326,6 +333,9 @@ waspComplexTest/.wasp/out/sdk/wasp/dist/server/email/core/types.js.map waspComplexTest/.wasp/out/sdk/wasp/dist/server/email/index.d.ts waspComplexTest/.wasp/out/sdk/wasp/dist/server/email/index.js waspComplexTest/.wasp/out/sdk/wasp/dist/server/email/index.js.map +waspComplexTest/.wasp/out/sdk/wasp/dist/server/env.d.ts +waspComplexTest/.wasp/out/sdk/wasp/dist/server/env.js +waspComplexTest/.wasp/out/sdk/wasp/dist/server/env.js.map waspComplexTest/.wasp/out/sdk/wasp/dist/server/index.d.ts waspComplexTest/.wasp/out/sdk/wasp/dist/server/index.js waspComplexTest/.wasp/out/sdk/wasp/dist/server/index.js.map @@ -396,6 +406,8 @@ waspComplexTest/.wasp/out/sdk/wasp/dist/universal/validators.d.ts waspComplexTest/.wasp/out/sdk/wasp/dist/universal/validators.js waspComplexTest/.wasp/out/sdk/wasp/dist/universal/validators.js.map waspComplexTest/.wasp/out/sdk/wasp/entities/index.ts +waspComplexTest/.wasp/out/sdk/wasp/env/index.ts +waspComplexTest/.wasp/out/sdk/wasp/env/validation.ts waspComplexTest/.wasp/out/sdk/wasp/ext-src/Main.css waspComplexTest/.wasp/out/sdk/wasp/ext-src/MainPage.jsx waspComplexTest/.wasp/out/sdk/wasp/ext-src/auth/hooks.ts @@ -419,7 +431,6 @@ waspComplexTest/.wasp/out/sdk/wasp/server/_types/taggedEntities.ts waspComplexTest/.wasp/out/sdk/wasp/server/api/index.ts waspComplexTest/.wasp/out/sdk/wasp/server/auth/hooks.ts waspComplexTest/.wasp/out/sdk/wasp/server/auth/index.ts -waspComplexTest/.wasp/out/sdk/wasp/server/auth/oauth/env.ts waspComplexTest/.wasp/out/sdk/wasp/server/auth/oauth/index.ts waspComplexTest/.wasp/out/sdk/wasp/server/auth/oauth/oneTimeCode.ts waspComplexTest/.wasp/out/sdk/wasp/server/auth/oauth/provider.ts @@ -435,6 +446,7 @@ waspComplexTest/.wasp/out/sdk/wasp/server/email/core/index.ts waspComplexTest/.wasp/out/sdk/wasp/server/email/core/providers/sendgrid.ts waspComplexTest/.wasp/out/sdk/wasp/server/email/core/types.ts waspComplexTest/.wasp/out/sdk/wasp/server/email/index.ts +waspComplexTest/.wasp/out/sdk/wasp/server/env.ts waspComplexTest/.wasp/out/sdk/wasp/server/index.ts waspComplexTest/.wasp/out/sdk/wasp/server/jobs/core/job.ts waspComplexTest/.wasp/out/sdk/wasp/server/jobs/core/pgBoss/index.ts @@ -466,7 +478,6 @@ waspComplexTest/.wasp/out/server/README.md waspComplexTest/.wasp/out/server/nodemon.json waspComplexTest/.wasp/out/server/package.json waspComplexTest/.wasp/out/server/rollup.config.js -waspComplexTest/.wasp/out/server/scripts/validate-env.mjs waspComplexTest/.wasp/out/server/src/actions/mySpecialAction.ts waspComplexTest/.wasp/out/server/src/app.js waspComplexTest/.wasp/out/server/src/auth/hooks.ts @@ -509,7 +520,6 @@ waspComplexTest/.wasp/out/web-app/package.json waspComplexTest/.wasp/out/web-app/public/.gitkeep waspComplexTest/.wasp/out/web-app/public/favicon.ico waspComplexTest/.wasp/out/web-app/public/manifest.json -waspComplexTest/.wasp/out/web-app/scripts/validate-env.mjs waspComplexTest/.wasp/out/web-app/src/auth/pages/OAuthCallback.tsx waspComplexTest/.wasp/out/web-app/src/auth/pages/createAuthRequiredPage.jsx waspComplexTest/.wasp/out/web-app/src/components/DefaultRootErrorBoundary.tsx diff --git a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/.waspchecksums b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/.waspchecksums index 802add6c20..a55c6e39de 100644 --- a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/.waspchecksums +++ b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/.waspchecksums @@ -200,7 +200,7 @@ "file", "../out/sdk/wasp/client/config.ts" ], - "d94e8bee5fd8f6793b511652b9ee558e57f4913595763137a5385cd929648168" + "4c5835ad64352cc87a4a17e41fd70333bc0d1eedd7339d7ad3d3507cdb64038d" ], [ [ @@ -223,12 +223,19 @@ ], "083c93c278fe261989cec8a4ea52e0dc706fae1a79aa91d6f1b8c17eca938c40" ], + [ + [ + "file", + "../out/sdk/wasp/client/env.ts" + ], + "2a3602d40b299fbfa1a6275fe67828d5653d9f3265ba8204b75178aa41dcbae0" + ], [ [ "file", "../out/sdk/wasp/client/index.ts" ], - "2b3651e7040a63cfd6a271c2aa89f21cf01170e0abeb6c5ab74adde324852fe4" + "b55e5c34c2fdb50e608bcb4331fa2e67724f1fbfb21e1b93680b0f5fe2b7005a" ], [ [ @@ -384,6 +391,20 @@ ], "becac9b39c6920dee40a97958331da143e06dd26d6756ad08dcc6402b23e2efb" ], + [ + [ + "file", + "../out/sdk/wasp/env/index.ts" + ], + "d92b4832e9d4249ae5b1f00c0f9760d6841aa44bc199550414793a1d57a6845e" + ], + [ + [ + "file", + "../out/sdk/wasp/env/validation.ts" + ], + "e22b2fb8931275d3b1bd3176cbdbf753d22fc61887ded0e93cd8febf1b97d260" + ], [ [ "file", @@ -487,7 +508,7 @@ "file", "../out/sdk/wasp/package.json" ], - "6fdae74af8f40e1e3287c93d6af2a7ebe9140b1995b6511d2bb4a0c002ab8d48" + "f481b1b2cb68f26b7496bfcc92dfd80958fb01b013853f9fd1a36672a117acd8" ], [ [ @@ -501,7 +522,7 @@ "file", "../out/sdk/wasp/server/HttpError.ts" ], - "f375fb45eeb16381b8cc575503db302c005ddaa1b35d837ca0ea043fe8ec6ccb" + "9c1b2449d8e9d68470c6014054a3c3074069a104ddb291b9089c30d624a2d9d9" ], [ [ @@ -545,13 +566,6 @@ ], "6daa9ed1d7ad0875bd5772a440de2fd229e9cd1bf3f44bde548eff467a68116d" ], - [ - [ - "file", - "../out/sdk/wasp/server/auth/oauth/env.ts" - ], - "50416a8c284fdbb3952e21e1617da2270b99dba717023babc7c32f5b12a63a32" - ], [ [ "file", @@ -571,14 +585,14 @@ "file", "../out/sdk/wasp/server/auth/oauth/provider.ts" ], - "2a2015d905a2ceddb45ff1034b283b242df153ddf2cabd05fddd4746365622df" + "f286a5c5f04b197bc5cf21711722b6265887dae1782a26e41124606616c27122" ], [ [ "file", "../out/sdk/wasp/server/auth/oauth/providers/google.ts" ], - "2a30b51f7be3a627d652e164669b01832345763597aa1e0badf7d321ccdc3bdb" + "42fe943be534df3df054480df3c3672742159a5f9d48a5a2bb0cb0a3500168e7" ], [ [ @@ -599,7 +613,7 @@ "file", "../out/sdk/wasp/server/config.ts" ], - "7222022fceced6be8ce57e8fd06ea6dfe2100de5984d7a9bc752ed4d9ba6f4ef" + "4a78cfcf41a4fa41fa61ee19404fb6f32d5c25a1f854008ad81fcd8c2191e5ab" ], [ [ @@ -655,14 +669,21 @@ "file", "../out/sdk/wasp/server/email/index.ts" ], - "7fc9cea5624d921d9cd2397767522282e3f1a8a0013806f8d3e721560526b177" + "199b8d91048f446e0491964c9dfe0e946875a19b9a5757aa7e600880d6f52f98" + ], + [ + [ + "file", + "../out/sdk/wasp/server/env.ts" + ], + "0331823b6ac18abf1acee809ba959500362c3cf7bf9b258516da0067c156c84c" ], [ [ "file", "../out/sdk/wasp/server/index.ts" ], - "51c3e0802764f72b88f6daaf148dba514ae817e233a40cc9b92ec91784f71f98" + "a8d2ffa7dfd0a13f7fb6135193bcdac1f860603bdd0ddc2843033fd4f6f6151f" ], [ [ @@ -683,7 +704,7 @@ "file", "../out/sdk/wasp/server/jobs/core/pgBoss/pgBoss.ts" ], - "4c405b3cf98a4cea0ee906a46e4676ebe7ce08c1652c1b502403a93ebbbf943a" + "44ea6eb19cb4e283e24b4d3da1105539ef81cd36dfeb71d9fa5695f0b0c15b34" ], [ [ @@ -816,7 +837,7 @@ "file", "../out/sdk/wasp/universal/url.ts" ], - "8dc6e044a1a231b796465d94985ca47c5efd42a5d411b407a7d83a61ebae4b6d" + "58ff4ad8ffc79264d7215461571d909f3c5fb177ff32c67058d1da9cd4115d6e" ], [ [ @@ -844,7 +865,7 @@ "file", "Dockerfile" ], - "db2dae70d212749a7e758117e55f00968f84974d8307dc7d33154bf1429486c2" + "94b45c62cbed2fcb04c69643da5047c27c5eb0acb78b7c164d083a5c30e7c1d1" ], [ [ @@ -893,7 +914,7 @@ "file", "server/package.json" ], - "00ae054bb5597a45cc4e5277f88d24f4399fccabaa3614bf6ea89424fcd23480" + "ddda728300aaca5ccbd8342479cede7930cd5841c01f048415da8172314a1e0a" ], [ [ @@ -902,13 +923,6 @@ ], "c1beb8264f11898364288d73b16f08d1923bac5f70038d9827978deb5b58a2da" ], - [ - [ - "file", - "server/scripts/validate-env.mjs" - ], - "100177b4326ccab7362eff378315d532ad1cc17cd28d1ed5978cb167fd627746" - ], [ [ "file", @@ -956,7 +970,7 @@ "file", "server/src/auth/providers/oauth/cookies.ts" ], - "7fb6658564e8af442d0f0b5874879e6788e91e89d98a8265621dd323135f5916" + "d3550ba07c6685f033962a47b326609f3a15ab5456638b9a4cf6fbb84379044c" ], [ [ @@ -1047,7 +1061,7 @@ "file", "server/src/polyfill.ts" ], - "1149661e0aa7228b184bb2c04ac63232a6523b09b33829db6977f79daba3fd8d" + "f62c2088c8ebf5889ab479bb937ffd8f0f96d3e85483a7b088edebb5ae598dca" ], [ [ @@ -1187,7 +1201,7 @@ "file", "web-app/package.json" ], - "7e7cce9cb193df0ebb8cfe1ffc9b62b4d6aefe2d74c8631d8dcca45a21fff77a" + "c424e09513b200091c5b0c6e73de89a4987657060c796e203f474bfda83dadd7" ], [ [ @@ -1210,13 +1224,6 @@ ], "b57981d1636058192ae6057166991125031f519f24c0a0711ef16dc73958e51a" ], - [ - [ - "file", - "web-app/scripts/validate-env.mjs" - ], - "a9a3a7eb6bc3ead49d8e3850a70737c93c789098beb3b40196bf145fd38893cd" - ], [ [ "file", @@ -1236,7 +1243,7 @@ "file", "web-app/src/components/DefaultRootErrorBoundary.tsx" ], - "4a141a28ca7aed9daddd75c156f7bae5eec50f3e5a85b6a43a28fa2648f34c44" + "73bbe8a2eb141e523c6b8169c0831e594b4ab6c7207f107d6d20c1561b44373a" ], [ [ diff --git a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/Dockerfile b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/Dockerfile index 76baedb0e0..9d828590a3 100644 --- a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/Dockerfile +++ b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/Dockerfile @@ -52,15 +52,15 @@ WORKDIR /app # Copying the top level 'node_modules' because it contains the Prisma packages # necessary for migrating the database. COPY --from=server-builder /app/node_modules ./node_modules -# Copying the SDK because 'validate-env.mjs' executes independent of the bundle +# Copying the SDK because the server bundle doesn't bundle the SDK # and references the 'wasp' package. COPY --from=server-builder /app/.wasp/out/sdk .wasp/out/sdk -# Copying 'server/node_modules' because 'validate-env.mjs' executes independent -# of the bundle and references the dotenv package. +# Copying 'server/node_modules' because we require dotenv package to +# load environment variables +# TODO: replace dotenv with native Node.js environment variable loading COPY --from=server-builder /app/.wasp/build/server/node_modules .wasp/build/server/node_modules COPY --from=server-builder /app/.wasp/build/server/bundle .wasp/build/server/bundle COPY --from=server-builder /app/.wasp/build/server/package*.json .wasp/build/server/ -COPY --from=server-builder /app/.wasp/build/server/scripts .wasp/build/server/scripts COPY db/ .wasp/build/db/ EXPOSE ${PORT} WORKDIR /app/.wasp/build/server diff --git a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/installedNpmDepsLog.json b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/installedNpmDepsLog.json index 800ff0bab9..d18d6662d0 100644 --- a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/installedNpmDepsLog.json +++ b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/installedNpmDepsLog.json @@ -1 +1 @@ -{"_waspSdkNpmDeps":{"dependencies":[{"name":"@prisma/client","version":"5.19.1"},{"name":"prisma","version":"5.19.1"},{"name":"@tanstack/react-query","version":"^4.29.0"},{"name":"axios","version":"^1.4.0"},{"name":"express","version":"~4.21.0"},{"name":"mitt","version":"3.0.0"},{"name":"react","version":"^18.2.0"},{"name":"lodash.merge","version":"^4.6.2"},{"name":"react-router-dom","version":"^6.26.2"},{"name":"react-hook-form","version":"^7.45.4"},{"name":"superjson","version":"^2.2.1"},{"name":"@stitches/react","version":"^1.2.8"},{"name":"@node-rs/argon2","version":"^1.8.3"},{"name":"arctic","version":"^1.2.1"},{"name":"lucia","version":"^3.0.1"},{"name":"oslo","version":"^1.1.2"},{"name":"@lucia-auth/adapter-prisma","version":"^4.0.0"},{"name":"@sendgrid/mail","version":"^7.7.0"},{"name":"vitest","version":"^1.2.1"},{"name":"@vitest/ui","version":"^1.2.1"},{"name":"jsdom","version":"^21.1.1"},{"name":"@testing-library/react","version":"^14.1.2"},{"name":"@testing-library/jest-dom","version":"^6.3.0"},{"name":"msw","version":"^1.1.0"},{"name":"pg-boss","version":"^8.4.2"}],"devDependencies":[{"name":"@tsconfig/node18","version":"latest"},{"name":"@types/express-serve-static-core","version":"^4.17.13"}]},"_userNpmDeps":{"userDependencies":[{"name":"react","version":"^18.2.0"},{"name":"react-dom","version":"^18.2.0"},{"name":"react-router-dom","version":"^6.26.2"},{"name":"wasp","version":"file:.wasp/out/sdk/wasp"}],"userDevDependencies":[{"name":"@types/react","version":"^18.0.37"},{"name":"prisma","version":"5.19.1"},{"name":"typescript","version":"^5.1.0"},{"name":"vite","version":"^4.3.9"}]},"_waspFrameworkNpmDeps":{"npmDepsForWebApp":{"dependencies":[{"name":"@tanstack/react-query","version":"^4.29.0"},{"name":"axios","version":"^1.4.0"}],"devDependencies":[{"name":"@tsconfig/vite-react","version":"^2.0.0"},{"name":"@types/react-dom","version":"^18.0.11"},{"name":"@vitejs/plugin-react","version":"^4.2.1"},{"name":"dotenv","version":"^16.0.3"}]},"npmDepsForServer":{"dependencies":[{"name":"cookie-parser","version":"~1.4.6"},{"name":"cors","version":"^2.8.5"},{"name":"dotenv","version":"^16.0.2"},{"name":"express","version":"~4.21.0"},{"name":"helmet","version":"^6.0.0"},{"name":"morgan","version":"~1.10.0"},{"name":"superjson","version":"^2.2.1"}],"devDependencies":[{"name":"@tsconfig/node18","version":"latest"},{"name":"@types/cors","version":"^2.8.5"},{"name":"@types/express","version":"^4.17.13"},{"name":"@types/express-serve-static-core","version":"^4.17.13"},{"name":"@types/node","version":"^18.0.0"},{"name":"nodemon","version":"^2.0.19"},{"name":"rollup","version":"^4.9.6"},{"name":"rollup-plugin-esbuild","version":"^6.1.1"}]}}} \ No newline at end of file +{"_waspSdkNpmDeps":{"dependencies":[{"name":"@prisma/client","version":"5.19.1"},{"name":"prisma","version":"5.19.1"},{"name":"@tanstack/react-query","version":"^4.29.0"},{"name":"axios","version":"^1.4.0"},{"name":"express","version":"~4.21.0"},{"name":"mitt","version":"3.0.0"},{"name":"react","version":"^18.2.0"},{"name":"react-router-dom","version":"^6.26.2"},{"name":"react-hook-form","version":"^7.45.4"},{"name":"superjson","version":"^2.2.1"},{"name":"@stitches/react","version":"^1.2.8"},{"name":"@node-rs/argon2","version":"^1.8.3"},{"name":"arctic","version":"^1.2.1"},{"name":"lucia","version":"^3.0.1"},{"name":"oslo","version":"^1.1.2"},{"name":"@lucia-auth/adapter-prisma","version":"^4.0.0"},{"name":"@sendgrid/mail","version":"^7.7.0"},{"name":"vitest","version":"^1.2.1"},{"name":"@vitest/ui","version":"^1.2.1"},{"name":"jsdom","version":"^21.1.1"},{"name":"@testing-library/react","version":"^14.1.2"},{"name":"@testing-library/jest-dom","version":"^6.3.0"},{"name":"msw","version":"^1.1.0"},{"name":"pg-boss","version":"^8.4.2"},{"name":"zod","version":"^3.23.8"}],"devDependencies":[{"name":"@tsconfig/node18","version":"latest"},{"name":"@types/express-serve-static-core","version":"^4.17.13"}]},"_userNpmDeps":{"userDependencies":[{"name":"react","version":"^18.2.0"},{"name":"react-dom","version":"^18.2.0"},{"name":"react-router-dom","version":"^6.26.2"},{"name":"wasp","version":"file:.wasp/out/sdk/wasp"}],"userDevDependencies":[{"name":"@types/react","version":"^18.0.37"},{"name":"prisma","version":"5.19.1"},{"name":"typescript","version":"^5.1.0"},{"name":"vite","version":"^4.3.9"}]},"_waspFrameworkNpmDeps":{"npmDepsForWebApp":{"dependencies":[{"name":"@tanstack/react-query","version":"^4.29.0"},{"name":"axios","version":"^1.4.0"}],"devDependencies":[{"name":"@tsconfig/vite-react","version":"^2.0.0"},{"name":"@types/react-dom","version":"^18.0.11"},{"name":"@vitejs/plugin-react","version":"^4.2.1"}]},"npmDepsForServer":{"dependencies":[{"name":"cookie-parser","version":"~1.4.6"},{"name":"cors","version":"^2.8.5"},{"name":"dotenv","version":"^16.0.2"},{"name":"express","version":"~4.21.0"},{"name":"helmet","version":"^6.0.0"},{"name":"morgan","version":"~1.10.0"},{"name":"superjson","version":"^2.2.1"}],"devDependencies":[{"name":"@tsconfig/node18","version":"latest"},{"name":"@types/cors","version":"^2.8.5"},{"name":"@types/express","version":"^4.17.13"},{"name":"@types/express-serve-static-core","version":"^4.17.13"},{"name":"@types/node","version":"^18.0.0"},{"name":"nodemon","version":"^2.0.19"},{"name":"rollup","version":"^4.9.6"},{"name":"rollup-plugin-esbuild","version":"^6.1.1"}]}}} \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/client/config.ts b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/client/config.ts index 82b0f80b6a..fff77cb01c 100644 --- a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/client/config.ts +++ b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/client/config.ts @@ -1,6 +1,7 @@ import { stripTrailingSlash } from '../universal/url.js' +import { env } from './env.js' -const apiUrl = stripTrailingSlash(import.meta.env.REACT_APP_API_URL) || 'http://localhost:3001'; +const apiUrl = stripTrailingSlash(env.REACT_APP_API_URL) // PUBLIC API export type ClientConfig = { diff --git a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/client/env.ts b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/client/env.ts new file mode 100644 index 0000000000..8ddab287f7 --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/client/env.ts @@ -0,0 +1,18 @@ +import * as z from 'zod' + +import { ensureEnvSchema } from '../env/validation.js' + +const userClientEnvSchema = z.object({}) + +const waspClientEnvSchema = z.object({ + REACT_APP_API_URL: z + .string({ + required_error: 'REACT_APP_API_URL is required', + }) + .default('http://localhost:3001') +}) + +const clientEnvSchema = userClientEnvSchema.merge(waspClientEnvSchema) + +// PUBLIC API +export const env = ensureEnvSchema(import.meta.env, clientEnvSchema) diff --git a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/client/index.ts b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/client/index.ts index cfce564c4d..859f8656b6 100644 --- a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/client/index.ts +++ b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/client/index.ts @@ -11,4 +11,7 @@ export enum HttpMethod { export type Route = { method: HttpMethod; path: string } // PUBLIC API -export { config, ClientConfig } from './config' +export { config, ClientConfig } from './config.js' + +// PUBLIC API +export { env } from './env.js' diff --git a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/client/config.js b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/client/config.js index 1dffbd68bc..7e6149f386 100644 --- a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/client/config.js +++ b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/client/config.js @@ -1,5 +1,6 @@ import { stripTrailingSlash } from '../universal/url.js'; -const apiUrl = stripTrailingSlash(import.meta.env.REACT_APP_API_URL) || 'http://localhost:3001'; +import { env } from './env.js'; +const apiUrl = stripTrailingSlash(env.REACT_APP_API_URL); // PUBLIC API export const config = { apiUrl, diff --git a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/client/config.js.map b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/client/config.js.map index d26d37022b..f9e186b838 100644 --- a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/client/config.js.map +++ b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/client/config.js.map @@ -1 +1 @@ -{"version":3,"file":"config.js","sourceRoot":"","sources":["../../client/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAA;AAExD,MAAM,MAAM,GAAG,kBAAkB,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,iBAAiB,CAAC,IAAI,uBAAuB,CAAC;AAOhG,aAAa;AACb,MAAM,CAAC,MAAM,MAAM,GAAiB;IAClC,MAAM;CACP,CAAA"} \ No newline at end of file +{"version":3,"file":"config.js","sourceRoot":"","sources":["../../client/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAA;AACxD,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAA;AAE9B,MAAM,MAAM,GAAG,kBAAkB,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAA;AAOxD,aAAa;AACb,MAAM,CAAC,MAAM,MAAM,GAAiB;IAClC,MAAM;CACP,CAAA"} \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/client/env.d.ts b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/client/env.d.ts new file mode 100644 index 0000000000..5a9417a810 --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/client/env.d.ts @@ -0,0 +1,3 @@ +export declare const env: { + REACT_APP_API_URL: string; +}; diff --git a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/client/env.js b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/client/env.js new file mode 100644 index 0000000000..91164c3768 --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/client/env.js @@ -0,0 +1,14 @@ +import * as z from 'zod'; +import { ensureEnvSchema } from '../env/validation.js'; +const userClientEnvSchema = z.object({}); +const waspClientEnvSchema = z.object({ + REACT_APP_API_URL: z + .string({ + required_error: 'REACT_APP_API_URL is required', + }) + .default('http://localhost:3001') +}); +const clientEnvSchema = userClientEnvSchema.merge(waspClientEnvSchema); +// PUBLIC API +export const env = ensureEnvSchema(import.meta.env, clientEnvSchema); +//# sourceMappingURL=env.js.map \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/client/env.js.map b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/client/env.js.map new file mode 100644 index 0000000000..d230f15c0d --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/client/env.js.map @@ -0,0 +1 @@ +{"version":3,"file":"env.js","sourceRoot":"","sources":["../../client/env.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,KAAK,CAAA;AAExB,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAA;AAEtD,MAAM,mBAAmB,GAAG,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,CAAA;AAExC,MAAM,mBAAmB,GAAG,CAAC,CAAC,MAAM,CAAC;IACnC,iBAAiB,EAAE,CAAC;SACjB,MAAM,CAAC;QACN,cAAc,EAAE,+BAA+B;KAChD,CAAC;SACD,OAAO,CAAC,uBAAuB,CAAC;CACpC,CAAC,CAAA;AAEF,MAAM,eAAe,GAAG,mBAAmB,CAAC,KAAK,CAAC,mBAAmB,CAAC,CAAA;AAEtE,aAAa;AACb,MAAM,CAAC,MAAM,GAAG,GAAG,eAAe,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,eAAe,CAAC,CAAA"} \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/client/index.d.ts b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/client/index.d.ts index 7f5f4bfbc8..6498c439bd 100644 --- a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/client/index.d.ts +++ b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/client/index.d.ts @@ -8,4 +8,5 @@ export type Route = { method: HttpMethod; path: string; }; -export { config, ClientConfig } from './config'; +export { config, ClientConfig } from './config.js'; +export { env } from './env.js'; diff --git a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/client/index.js b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/client/index.js index 5a7f13dcae..2f4daa7ae3 100644 --- a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/client/index.js +++ b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/client/index.js @@ -8,5 +8,7 @@ export var HttpMethod; HttpMethod["Delete"] = "DELETE"; })(HttpMethod || (HttpMethod = {})); // PUBLIC API -export { config } from './config'; +export { config } from './config.js'; +// PUBLIC API +export { env } from './env.js'; //# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/client/index.js.map b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/client/index.js.map index 879953c8df..f3611558dc 100644 --- a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/client/index.js.map +++ b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/client/index.js.map @@ -1 +1 @@ -{"version":3,"file":"index.js","sourceRoot":"","sources":["../../client/index.ts"],"names":[],"mappings":"AAAA,aAAa;AACb,mFAAmF;AACnF,MAAM,CAAN,IAAY,UAKX;AALD,WAAY,UAAU;IACrB,yBAAW,CAAA;IACX,2BAAa,CAAA;IACb,yBAAW,CAAA;IACX,+BAAiB,CAAA;AAClB,CAAC,EALW,UAAU,KAAV,UAAU,QAKrB;AAKD,aAAa;AACb,OAAO,EAAE,MAAM,EAAgB,MAAM,UAAU,CAAA"} \ No newline at end of file +{"version":3,"file":"index.js","sourceRoot":"","sources":["../../client/index.ts"],"names":[],"mappings":"AAAA,aAAa;AACb,mFAAmF;AACnF,MAAM,CAAN,IAAY,UAKX;AALD,WAAY,UAAU;IACrB,yBAAW,CAAA;IACX,2BAAa,CAAA;IACb,yBAAW,CAAA;IACX,+BAAiB,CAAA;AAClB,CAAC,EALW,UAAU,KAAV,UAAU,QAKrB;AAKD,aAAa;AACb,OAAO,EAAE,MAAM,EAAgB,MAAM,aAAa,CAAA;AAElD,aAAa;AACb,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAA"} \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/env/index.d.ts b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/env/index.d.ts new file mode 100644 index 0000000000..d1dafc25fd --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/env/index.d.ts @@ -0,0 +1,3 @@ +import * as z from 'zod'; +export declare function defineEnvValidationSchema>(schema: Schema): Schema; +export { ensureEnvSchema } from './validation.js'; diff --git a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/env/index.js b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/env/index.js new file mode 100644 index 0000000000..17e695d087 --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/env/index.js @@ -0,0 +1,7 @@ +// PUBLIC API +export function defineEnvValidationSchema(schema) { + return schema; +} +// PRIVATE API (SDK, Vite config) +export { ensureEnvSchema } from './validation.js'; +//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/env/index.js.map b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/env/index.js.map new file mode 100644 index 0000000000..75b4ff7de3 --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/env/index.js.map @@ -0,0 +1 @@ +{"version":3,"file":"index.js","sourceRoot":"","sources":["../../env/index.ts"],"names":[],"mappings":"AAEA,aAAa;AACb,MAAM,UAAU,yBAAyB,CACvC,MAAc;IAEd,OAAO,MAAM,CAAA;AACf,CAAC;AAED,iCAAiC;AACjC,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA"} \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/env/validation.d.ts b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/env/validation.d.ts new file mode 100644 index 0000000000..6bc9d059a3 --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/env/validation.d.ts @@ -0,0 +1,2 @@ +import * as z from 'zod'; +export declare function ensureEnvSchema(data: unknown, schema: Schema): z.infer; diff --git a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/env/validation.js b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/env/validation.js new file mode 100644 index 0000000000..8463eb578e --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/env/validation.js @@ -0,0 +1,23 @@ +import * as z from 'zod'; +const redColor = '\x1b[31m'; +export function ensureEnvSchema(data, schema) { + try { + return schema.parse(data); + } + catch (e) { + if (e instanceof z.ZodError) { + const errorOutput = ['', 'โ•โ• Env vars validation failed โ•โ•', '']; + for (const error of e.errors) { + errorOutput.push(` - ${error.message}`); + } + errorOutput.push(''); + errorOutput.push('โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•'); + console.error(redColor, errorOutput.join('\n')); + throw new Error('Error parsing environment variables'); + } + else { + throw e; + } + } +} +//# sourceMappingURL=validation.js.map \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/env/validation.js.map b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/env/validation.js.map new file mode 100644 index 0000000000..1d57b14a0b --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/env/validation.js.map @@ -0,0 +1 @@ +{"version":3,"file":"validation.js","sourceRoot":"","sources":["../../env/validation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,KAAK,CAAA;AAExB,MAAM,QAAQ,GAAG,UAAU,CAAA;AAE3B,MAAM,UAAU,eAAe,CAC7B,IAAa,EACb,MAAc;IAEd,IAAI,CAAC;QACH,OAAO,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;IAC3B,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,IAAI,CAAC,YAAY,CAAC,CAAC,QAAQ,EAAE,CAAC;YAC5B,MAAM,WAAW,GAAG,CAAC,EAAE,EAAE,kCAAkC,EAAE,EAAE,CAAC,CAAA;YAChE,KAAK,MAAM,KAAK,IAAI,CAAC,CAAC,MAAM,EAAE,CAAC;gBAC7B,WAAW,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC,OAAO,EAAE,CAAC,CAAA;YACzC,CAAC;YACD,WAAW,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;YACpB,WAAW,CAAC,IAAI,CAAC,kCAAkC,CAAC,CAAA;YACpD,OAAO,CAAC,KAAK,CAAC,QAAQ,EAAE,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAA;YAC/C,MAAM,IAAI,KAAK,CAAC,qCAAqC,CAAC,CAAA;QACxD,CAAC;aAAM,CAAC;YACN,MAAM,CAAC,CAAA;QACT,CAAC;IACH,CAAC;AACH,CAAC"} \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/server/HttpError.js.map b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/server/HttpError.js.map index 8163354e1b..462e1c1811 100644 --- a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/server/HttpError.js.map +++ b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/server/HttpError.js.map @@ -1 +1 @@ -{"version":3,"file":"HttpError.js","sourceRoot":"","sources":["../../server/HttpError.ts"],"names":[],"mappings":"AAAA,MAAM,OAAO,SAAU,SAAQ,KAAK;IAIlC,YAAa,UAAkB,EAAE,OAAgB,EAAE,IAA8B,EAAE,OAAsB;QACvG,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;QAEvB,IAAI,KAAK,CAAC,iBAAiB,EAAE,CAAC;YAC5B,KAAK,CAAC,iBAAiB,CAAC,IAAI,EAAE,SAAS,CAAC,CAAA;QAC1C,CAAC;QAED,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,WAAW,CAAC,IAAI,CAAA;QAEjC,IAAI,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,UAAU,CAAC,IAAI,UAAU,IAAI,GAAG,IAAI,UAAU,GAAG,GAAG,CAAC,EAAE,CAAC;YAC7E,MAAM,IAAI,KAAK,CAAC,mDAAmD,CAAC,CAAA;QACtE,CAAC;QACD,IAAI,CAAC,UAAU,GAAG,UAAU,CAAA;QAE5B,IAAI,IAAI,EAAE,CAAC;YACT,IAAI,CAAC,IAAI,GAAG,IAAI,CAAA;QAClB,CAAC;IACH,CAAC;CACF"} \ No newline at end of file +{"version":3,"file":"HttpError.js","sourceRoot":"","sources":["../../server/HttpError.ts"],"names":[],"mappings":"AAAA,MAAM,OAAO,SAAU,SAAQ,KAAK;IAIlC,YACE,UAAkB,EAClB,OAAgB,EAChB,IAA8B,EAC9B,OAAsB;QAEtB,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;QAEvB,IAAI,KAAK,CAAC,iBAAiB,EAAE,CAAC;YAC5B,KAAK,CAAC,iBAAiB,CAAC,IAAI,EAAE,SAAS,CAAC,CAAA;QAC1C,CAAC;QAED,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,WAAW,CAAC,IAAI,CAAA;QAEjC,IACE,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,UAAU,CAAC,IAAI,UAAU,IAAI,GAAG,IAAI,UAAU,GAAG,GAAG,CAAC,EACxE,CAAC;YACD,MAAM,IAAI,KAAK,CAAC,mDAAmD,CAAC,CAAA;QACtE,CAAC;QACD,IAAI,CAAC,UAAU,GAAG,UAAU,CAAA;QAE5B,IAAI,IAAI,EAAE,CAAC;YACT,IAAI,CAAC,IAAI,GAAG,IAAI,CAAA;QAClB,CAAC;IACH,CAAC;CACF"} \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/server/auth/oauth/env.d.ts b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/server/auth/oauth/env.d.ts deleted file mode 100644 index 08d2627b88..0000000000 --- a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/server/auth/oauth/env.d.ts +++ /dev/null @@ -1 +0,0 @@ -export declare function ensureEnvVarsForProvider(envVarNames: EnvVarName[], providerName: string): Record; diff --git a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/server/auth/oauth/env.js b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/server/auth/oauth/env.js deleted file mode 100644 index 56281b5e36..0000000000 --- a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/server/auth/oauth/env.js +++ /dev/null @@ -1,13 +0,0 @@ -// PRIVATE API (SDK) -export function ensureEnvVarsForProvider(envVarNames, providerName) { - const result = {}; - for (const envVarName of envVarNames) { - const value = process.env[envVarName]; - if (!value) { - throw new Error(`${envVarName} env variable is required when using the ${providerName} auth provider.`); - } - result[envVarName] = value; - } - return result; -} -//# sourceMappingURL=env.js.map \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/server/auth/oauth/env.js.map b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/server/auth/oauth/env.js.map deleted file mode 100644 index b25f2f9870..0000000000 --- a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/server/auth/oauth/env.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"env.js","sourceRoot":"","sources":["../../../../server/auth/oauth/env.ts"],"names":[],"mappings":"AAAA,oBAAoB;AACpB,MAAM,UAAU,wBAAwB,CACtC,WAAyB,EACzB,YAAoB;IAEpB,MAAM,MAAM,GAA2B,EAAE,CAAC;IAC1C,KAAK,MAAM,UAAU,IAAI,WAAW,EAAE,CAAC;QACrC,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;QACtC,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,MAAM,IAAI,KAAK,CAAC,GAAG,UAAU,4CAA4C,YAAY,iBAAiB,CAAC,CAAC;QAC1G,CAAC;QACD,MAAM,CAAC,UAAU,CAAC,GAAG,KAAK,CAAC;IAC7B,CAAC;IACD,OAAO,MAAoC,CAAC;AAC9C,CAAC"} \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/server/auth/oauth/provider.d.ts b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/server/auth/oauth/provider.d.ts index 44d23b4952..8738b530a5 100644 --- a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/server/auth/oauth/provider.d.ts +++ b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/server/auth/oauth/provider.d.ts @@ -1,12 +1,10 @@ import { OAuth2Provider, OAuth2ProviderWithPKCE } from "arctic"; -export declare function defineProvider>({ id, displayName, env, oAuthClient, }: { +export declare function defineProvider({ id, displayName, oAuthClient, }: { id: string; displayName: string; - env: Env; oAuthClient: OAuthClient; }): { id: string; displayName: string; - env: Env; oAuthClient: OAuthClient; }; diff --git a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/server/auth/oauth/provider.js b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/server/auth/oauth/provider.js index bc0d9a2fd8..eb6692b29c 100644 --- a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/server/auth/oauth/provider.js +++ b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/server/auth/oauth/provider.js @@ -1,8 +1,7 @@ -export function defineProvider({ id, displayName, env, oAuthClient, }) { +export function defineProvider({ id, displayName, oAuthClient, }) { return { id, displayName, - env, oAuthClient, }; } diff --git a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/server/auth/oauth/provider.js.map b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/server/auth/oauth/provider.js.map index 02fe380099..cf0b0f6459 100644 --- a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/server/auth/oauth/provider.js.map +++ b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/server/auth/oauth/provider.js.map @@ -1 +1 @@ -{"version":3,"file":"provider.js","sourceRoot":"","sources":["../../../../server/auth/oauth/provider.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,cAAc,CAG5B,EACA,EAAE,EACF,WAAW,EACX,GAAG,EACH,WAAW,GAMZ;IACC,OAAO;QACL,EAAE;QACF,WAAW;QACX,GAAG;QACH,WAAW;KACZ,CAAC;AACJ,CAAC"} \ No newline at end of file +{"version":3,"file":"provider.js","sourceRoot":"","sources":["../../../../server/auth/oauth/provider.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,cAAc,CAE5B,EACA,EAAE,EACF,WAAW,EACX,WAAW,GAKZ;IACC,OAAO;QACL,EAAE;QACF,WAAW;QACX,WAAW;KACZ,CAAC;AACJ,CAAC"} \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/server/auth/oauth/providers/google.d.ts b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/server/auth/oauth/providers/google.d.ts index 7910449649..bf13237ad7 100644 --- a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/server/auth/oauth/providers/google.d.ts +++ b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/server/auth/oauth/providers/google.d.ts @@ -1,7 +1,6 @@ -import { Google } from "arctic"; +import { Google } from 'arctic'; export declare const google: { id: string; displayName: string; - env: Record<"GOOGLE_CLIENT_ID" | "GOOGLE_CLIENT_SECRET", string>; oAuthClient: Google; }; diff --git a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/server/auth/oauth/providers/google.js b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/server/auth/oauth/providers/google.js index 7a49678dec..43ddbe1c75 100644 --- a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/server/auth/oauth/providers/google.js +++ b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/server/auth/oauth/providers/google.js @@ -1,16 +1,14 @@ -import { Google } from "arctic"; -import { ensureEnvVarsForProvider } from "../env.js"; -import { getRedirectUriForCallback } from "../redirect.js"; -import { defineProvider } from "../provider.js"; -const id = "google"; -const displayName = "Google"; -const env = ensureEnvVarsForProvider(["GOOGLE_CLIENT_ID", "GOOGLE_CLIENT_SECRET"], displayName); +import { Google } from 'arctic'; +import { getRedirectUriForCallback } from '../redirect.js'; +import { defineProvider } from '../provider.js'; +import { env } from '../../../env.js'; +const id = 'google'; +const displayName = 'Google'; const oAuthClient = new Google(env.GOOGLE_CLIENT_ID, env.GOOGLE_CLIENT_SECRET, getRedirectUriForCallback(id).toString()); // PUBLIC API export const google = defineProvider({ id, displayName, - env, oAuthClient, }); //# sourceMappingURL=google.js.map \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/server/auth/oauth/providers/google.js.map b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/server/auth/oauth/providers/google.js.map index 1bebbd38aa..cf4d45a1aa 100644 --- a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/server/auth/oauth/providers/google.js.map +++ b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/server/auth/oauth/providers/google.js.map @@ -1 +1 @@ -{"version":3,"file":"google.js","sourceRoot":"","sources":["../../../../../server/auth/oauth/providers/google.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAG,MAAM,QAAQ,CAAC;AAEjC,OAAO,EAAE,wBAAwB,EAAE,MAAM,WAAW,CAAC;AACrD,OAAO,EAAE,yBAAyB,EAAE,MAAM,gBAAgB,CAAC;AAC3D,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAEhD,MAAM,EAAE,GAAG,QAAQ,CAAC;AACpB,MAAM,WAAW,GAAG,QAAQ,CAAC;AAE7B,MAAM,GAAG,GAAG,wBAAwB,CAClC,CAAC,kBAAkB,EAAE,sBAAsB,CAAC,EAC5C,WAAW,CACZ,CAAC;AAEF,MAAM,WAAW,GAAG,IAAI,MAAM,CAC5B,GAAG,CAAC,gBAAgB,EACpB,GAAG,CAAC,oBAAoB,EACxB,yBAAyB,CAAC,EAAE,CAAC,CAAC,QAAQ,EAAE,CACzC,CAAC;AAEF,aAAa;AACb,MAAM,CAAC,MAAM,MAAM,GAAG,cAAc,CAAC;IACnC,EAAE;IACF,WAAW;IACX,GAAG;IACH,WAAW;CACZ,CAAC,CAAC"} \ No newline at end of file +{"version":3,"file":"google.js","sourceRoot":"","sources":["../../../../../server/auth/oauth/providers/google.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAG,MAAM,QAAQ,CAAC;AAEjC,OAAO,EAAE,yBAAyB,EAAE,MAAM,gBAAgB,CAAC;AAC3D,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,EAAE,GAAG,EAAE,MAAM,iBAAiB,CAAC;AAEtC,MAAM,EAAE,GAAG,QAAQ,CAAC;AACpB,MAAM,WAAW,GAAG,QAAQ,CAAC;AAE7B,MAAM,WAAW,GAAG,IAAI,MAAM,CAC5B,GAAG,CAAC,gBAAgB,EACpB,GAAG,CAAC,oBAAoB,EACxB,yBAAyB,CAAC,EAAE,CAAC,CAAC,QAAQ,EAAE,CACzC,CAAC;AAEF,aAAa;AACb,MAAM,CAAC,MAAM,MAAM,GAAG,cAAc,CAAC;IACnC,EAAE;IACF,WAAW;IACX,WAAW;CACZ,CAAC,CAAC"} \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/server/config.d.ts b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/server/config.d.ts index 56da48e819..a612dded65 100644 --- a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/server/config.d.ts +++ b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/server/config.d.ts @@ -1,19 +1,16 @@ -type BaseConfig = { - allowedCORSOrigins: string | string[]; - auth: { - jwtSecret: string | undefined; - }; -}; -type CommonConfig = BaseConfig & { - env: string; +import { env } from './env.js'; +type NodeEnv = typeof env.NODE_ENV; +type Config = { + env: NodeEnv; isDevelopment: boolean; port: number; - databaseUrl: string | undefined; -}; -type EnvConfig = BaseConfig & { + databaseUrl: string; frontendUrl: string; serverUrl: string; + allowedCORSOrigins: string | string[]; + auth: { + jwtSecret: string; + }; }; -type Config = CommonConfig & EnvConfig; -declare const resolvedConfig: Config; -export default resolvedConfig; +declare const config: Config; +export default config; diff --git a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/server/config.js b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/server/config.js index cb99fb1dd0..b5096b3b07 100644 --- a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/server/config.js +++ b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/server/config.js @@ -1,52 +1,24 @@ -var _a; -import merge from 'lodash.merge'; -import { stripTrailingSlash } from "../universal/url.js"; -const nodeEnv = (_a = process.env.NODE_ENV) !== null && _a !== void 0 ? _a : 'development'; +import { env } from './env.js'; +import { stripTrailingSlash } from '../universal/url.js'; +const frontendUrl = stripTrailingSlash(env.WASP_WEB_CLIENT_URL); +const serverUrl = stripTrailingSlash(env.WASP_SERVER_URL); +const allowedCORSOriginsPerEnv = { + development: '*', + production: [frontendUrl] +}; +const allowedCORSOrigins = allowedCORSOriginsPerEnv[env.NODE_ENV]; const config = { - all: { - env: nodeEnv, - isDevelopment: nodeEnv === 'development', - port: process.env.PORT ? parseInt(process.env.PORT) : 3001, - databaseUrl: process.env.DATABASE_URL, - allowedCORSOrigins: [], - auth: { - jwtSecret: undefined - } - }, - development: getDevelopmentConfig(), - production: getProductionConfig(), + frontendUrl, + serverUrl, + allowedCORSOrigins, + env: env.NODE_ENV, + isDevelopment: env.NODE_ENV === 'development', + port: env.PORT, + databaseUrl: env.DATABASE_URL, + auth: { + jwtSecret: env.JWT_SECRET + } }; -const resolvedConfig = merge(config.all, config[nodeEnv]); // PUBLIC API -export default resolvedConfig; -function getDevelopmentConfig() { - var _a, _b; - const frontendUrl = stripTrailingSlash((_a = process.env.WASP_WEB_CLIENT_URL) !== null && _a !== void 0 ? _a : 'http://localhost:3000/'); - const serverUrl = stripTrailingSlash((_b = process.env.WASP_SERVER_URL) !== null && _b !== void 0 ? _b : 'http://localhost:3001'); - return { - // @ts-ignore - frontendUrl, - // @ts-ignore - serverUrl, - allowedCORSOrigins: '*', - auth: { - jwtSecret: 'DEVJWTSECRET' - } - }; -} -function getProductionConfig() { - const frontendUrl = stripTrailingSlash(process.env.WASP_WEB_CLIENT_URL); - const serverUrl = stripTrailingSlash(process.env.WASP_SERVER_URL); - return { - // @ts-ignore - frontendUrl, - // @ts-ignore - serverUrl, - // @ts-ignore - allowedCORSOrigins: [frontendUrl], - auth: { - jwtSecret: process.env.JWT_SECRET - } - }; -} +export default config; //# sourceMappingURL=config.js.map \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/server/config.js.map b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/server/config.js.map index 0ef81fb2bc..d83a878589 100644 --- a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/server/config.js.map +++ b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/server/config.js.map @@ -1 +1 @@ -{"version":3,"file":"config.js","sourceRoot":"","sources":["../../server/config.ts"],"names":[],"mappings":";AAAA,OAAO,KAAK,MAAM,cAAc,CAAA;AAEhC,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAEzD,MAAM,OAAO,GAAG,MAAA,OAAO,CAAC,GAAG,CAAC,QAAQ,mCAAI,aAAa,CAAA;AA4BrD,MAAM,MAAM,GAIR;IACF,GAAG,EAAE;QACH,GAAG,EAAE,OAAO;QACZ,aAAa,EAAE,OAAO,KAAK,aAAa;QACxC,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI;QAC1D,WAAW,EAAE,OAAO,CAAC,GAAG,CAAC,YAAY;QACrC,kBAAkB,EAAE,EAAE;QACtB,IAAI,EAAE;YACJ,SAAS,EAAE,SAAS;SACrB;KACF;IACD,WAAW,EAAE,oBAAoB,EAAE;IACnC,UAAU,EAAE,mBAAmB,EAAE;CAClC,CAAA;AAED,MAAM,cAAc,GAAW,KAAK,CAAC,MAAM,CAAC,GAAG,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC,CAAA;AACjE,aAAa;AACb,eAAe,cAAc,CAAA;AAE7B,SAAS,oBAAoB;;IAC3B,MAAM,WAAW,GAAG,kBAAkB,CAAC,MAAA,OAAO,CAAC,GAAG,CAAC,mBAAmB,mCAAI,wBAAwB,CAAC,CAAC;IACpG,MAAM,SAAS,GAAG,kBAAkB,CAAC,MAAA,OAAO,CAAC,GAAG,CAAC,eAAe,mCAAI,uBAAuB,CAAC,CAAC;IAC7F,OAAO;QACL,aAAa;QACb,WAAW;QACX,aAAa;QACb,SAAS;QACT,kBAAkB,EAAE,GAAG;QACvB,IAAI,EAAE;YACJ,SAAS,EAAE,cAAc;SAC1B;KACF,CAAA;AACH,CAAC;AAED,SAAS,mBAAmB;IAC1B,MAAM,WAAW,GAAG,kBAAkB,CAAC,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC,CAAC;IACxE,MAAM,SAAS,GAAG,kBAAkB,CAAC,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC;IAClE,OAAO;QACL,aAAa;QACb,WAAW;QACX,aAAa;QACb,SAAS;QACT,aAAa;QACb,kBAAkB,EAAE,CAAC,WAAW,CAAC;QACjC,IAAI,EAAE;YACJ,SAAS,EAAE,OAAO,CAAC,GAAG,CAAC,UAAU;SAClC;KACF,CAAA;AACH,CAAC"} \ No newline at end of file +{"version":3,"file":"config.js","sourceRoot":"","sources":["../../server/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAA;AAC9B,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAA;AAiBxD,MAAM,WAAW,GAAG,kBAAkB,CAAC,GAAG,CAAC,mBAAmB,CAAC,CAAA;AAC/D,MAAM,SAAS,GAAG,kBAAkB,CAAC,GAAG,CAAC,eAAe,CAAC,CAAA;AAEzD,MAAM,wBAAwB,GAAuC;IACnE,WAAW,EAAE,GAAG;IAChB,UAAU,EAAE,CAAC,WAAW,CAAC;CAC1B,CAAA;AACD,MAAM,kBAAkB,GAAG,wBAAwB,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAA;AAEjE,MAAM,MAAM,GAAW;IACrB,WAAW;IACX,SAAS;IACT,kBAAkB;IAClB,GAAG,EAAE,GAAG,CAAC,QAAQ;IACjB,aAAa,EAAE,GAAG,CAAC,QAAQ,KAAK,aAAa;IAC7C,IAAI,EAAE,GAAG,CAAC,IAAI;IACd,WAAW,EAAE,GAAG,CAAC,YAAY;IAC7B,IAAI,EAAE;QACJ,SAAS,EAAE,GAAG,CAAC,UAAU;KAC1B;CACF,CAAA;AAED,aAAa;AACb,eAAe,MAAM,CAAA"} \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/server/email/index.js b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/server/email/index.js index 91c1207b30..c32555c034 100644 --- a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/server/email/index.js +++ b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/server/email/index.js @@ -1,9 +1,8 @@ +import { env } from '../env.js'; import { initEmailSender } from "./core/index.js"; -// TODO: We need to validate all the env variables -// For now, we are letting the runtime throw if they are not provided const emailProvider = { type: "sendgrid", - apiKey: process.env.SENDGRID_API_KEY, + apiKey: env.SENDGRID_API_KEY, }; // PUBLIC API export const emailSender = initEmailSender(emailProvider); diff --git a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/server/email/index.js.map b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/server/email/index.js.map index 1629cdd0f0..18787c5c87 100644 --- a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/server/email/index.js.map +++ b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/server/email/index.js.map @@ -1 +1 @@ -{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../server/email/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAGlD,kDAAkD;AAClD,qEAAqE;AACrE,MAAM,aAAa,GAAG;IACpB,IAAI,EAAE,UAAU;IAChB,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,gBAAiB;CAC7B,CAAC;AAEX,aAAa;AACb,MAAM,CAAC,MAAM,WAAW,GAAgB,eAAe,CAAC,aAAa,CAAC,CAAC"} \ No newline at end of file +{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../server/email/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,WAAW,CAAC;AAChC,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAGlD,MAAM,aAAa,GAAG;IACpB,IAAI,EAAE,UAAU;IAChB,MAAM,EAAE,GAAG,CAAC,gBAAgB;CACpB,CAAC;AAEX,aAAa;AACb,MAAM,CAAC,MAAM,WAAW,GAAgB,eAAe,CAAC,aAAa,CAAC,CAAC"} \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/server/env.d.ts b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/server/env.d.ts new file mode 100644 index 0000000000..72587aed6b --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/server/env.d.ts @@ -0,0 +1,25 @@ +export declare const env: { + PORT: number; + DATABASE_URL: string; + SENDGRID_API_KEY: string; + SKIP_EMAIL_VERIFICATION_IN_DEV: boolean; + GOOGLE_CLIENT_ID: string; + GOOGLE_CLIENT_SECRET: string; + NODE_ENV: "development"; + WASP_SERVER_URL: string; + WASP_WEB_CLIENT_URL: string; + JWT_SECRET: string; + PG_BOSS_NEW_OPTIONS?: string | undefined; +} | { + PORT: number; + DATABASE_URL: string; + SENDGRID_API_KEY: string; + SKIP_EMAIL_VERIFICATION_IN_DEV: boolean; + GOOGLE_CLIENT_ID: string; + GOOGLE_CLIENT_SECRET: string; + NODE_ENV: "production"; + WASP_SERVER_URL: string; + WASP_WEB_CLIENT_URL: string; + JWT_SECRET: string; + PG_BOSS_NEW_OPTIONS?: string | undefined; +}; diff --git a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/server/env.js b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/server/env.js new file mode 100644 index 0000000000..42249b691c --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/server/env.js @@ -0,0 +1,71 @@ +import * as z from 'zod'; +import { ensureEnvSchema } from '../env/validation.js'; +const userServerEnvSchema = z.object({}); +const waspServerCommonSchema = z.object({ + PORT: z.coerce.number().default(3001), + DATABASE_URL: z.string({ + required_error: 'DATABASE_URL is required', + }), + PG_BOSS_NEW_OPTIONS: z.string().optional(), + SENDGRID_API_KEY: z.string({ + required_error: getRequiredEnvVarErrorMessage('SendGrid email sender', 'SENDGRID_API_KEY'), + }), + SKIP_EMAIL_VERIFICATION_IN_DEV: z + .enum(['true', 'false'], { + message: 'SKIP_EMAIL_VERIFICATION_IN_DEV must be either "true" or "false"', + }) + .transform((value) => value === 'true') + .default('false'), + GOOGLE_CLIENT_ID: z.string({ + required_error: getRequiredEnvVarErrorMessage('Google auth provider', 'GOOGLE_CLIENT_ID'), + }), + GOOGLE_CLIENT_SECRET: z.string({ + required_error: getRequiredEnvVarErrorMessage('Google auth provider', 'GOOGLE_CLIENT_SECRET'), + }), +}); +const serverUrlSchema = z + .string({ + required_error: 'WASP_SERVER_URL is required', +}) + .url({ + message: 'WASP_SERVER_URL must be a valid URL', +}); +const clientUrlSchema = z + .string({ + required_error: 'WASP_WEB_CLIENT_URL is required', +}) + .url({ + message: 'WASP_WEB_CLIENT_URL must be a valid URL', +}); +const jwtTokenSchema = z + .string({ + required_error: 'JWT_SECRET is required', +}); +// In development, we provide default values for some environment variables +// to make the development process easier. +const serverDevSchema = z.object({ + NODE_ENV: z.literal('development'), + WASP_SERVER_URL: serverUrlSchema + .default('http://localhost:3001'), + WASP_WEB_CLIENT_URL: clientUrlSchema + .default('http://localhost:3000/'), + JWT_SECRET: jwtTokenSchema + .default('DEVJWTSECRET'), +}); +const serverProdSchema = z.object({ + NODE_ENV: z.literal('production'), + WASP_SERVER_URL: serverUrlSchema, + WASP_WEB_CLIENT_URL: clientUrlSchema, + JWT_SECRET: jwtTokenSchema, +}); +const serverCommonSchema = userServerEnvSchema.merge(waspServerCommonSchema); +const serverEnvSchema = z.discriminatedUnion('NODE_ENV', [ + serverDevSchema.merge(serverCommonSchema), + serverProdSchema.merge(serverCommonSchema) +]); +// PUBLIC API +export const env = ensureEnvSchema(process.env, serverEnvSchema); +function getRequiredEnvVarErrorMessage(featureName, envVarName) { + return `${envVarName} is required when using ${featureName}`; +} +//# sourceMappingURL=env.js.map \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/server/env.js.map b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/server/env.js.map new file mode 100644 index 0000000000..7560809692 --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/server/env.js.map @@ -0,0 +1 @@ +{"version":3,"file":"env.js","sourceRoot":"","sources":["../../server/env.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/server/index.d.ts b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/server/index.d.ts index dee9613a53..a108f6123f 100644 --- a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/server/index.d.ts +++ b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/server/index.d.ts @@ -4,4 +4,5 @@ export { default as prisma } from './dbClient.js'; export { type ServerSetupFn } from './types/index.js'; export { HttpError } from './HttpError.js'; export { MiddlewareConfigFn } from './middleware/index.js'; +export { env } from './env.js'; export type DbSeedFn = (prisma: PrismaClient) => Promise; diff --git a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/server/index.js b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/server/index.js index 0e53e0deab..8e75e6f78c 100644 --- a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/server/index.js +++ b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/server/index.js @@ -4,4 +4,6 @@ export { default as config } from './config.js'; export { default as prisma } from './dbClient.js'; // PUBLIC API export { HttpError } from './HttpError.js'; +// PUBLIC API +export { env } from './env.js'; //# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/server/index.js.map b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/server/index.js.map index b84ac1b303..87ed22f3af 100644 --- a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/server/index.js.map +++ b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/server/index.js.map @@ -1 +1 @@ -{"version":3,"file":"index.js","sourceRoot":"","sources":["../../server/index.ts"],"names":[],"mappings":"AAEA,aAAa;AACb,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,aAAa,CAAA;AAC/C,aAAa;AACb,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,eAAe,CAAA;AAGjD,aAAa;AACb,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA"} \ No newline at end of file +{"version":3,"file":"index.js","sourceRoot":"","sources":["../../server/index.ts"],"names":[],"mappings":"AAEA,aAAa;AACb,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,aAAa,CAAA;AAC/C,aAAa;AACb,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,eAAe,CAAA;AAGjD,aAAa;AACb,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAG1C,aAAa;AACb,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAA"} \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/server/jobs/core/pgBoss/pgBoss.js b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/server/jobs/core/pgBoss/pgBoss.js index d1feb61aed..8fe6f220bc 100644 --- a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/server/jobs/core/pgBoss/pgBoss.js +++ b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/server/jobs/core/pgBoss/pgBoss.js @@ -1,14 +1,14 @@ import PgBoss from 'pg-boss'; -import { config } from 'wasp/server'; +import { config, env } from '../../../index.js'; const boss = createPgBoss(); function createPgBoss() { let pgBossNewOptions = { connectionString: config.databaseUrl, }; // Add an escape hatch for advanced configuration of pg-boss to overwrite our defaults. - if (process.env.PG_BOSS_NEW_OPTIONS) { + if (env.PG_BOSS_NEW_OPTIONS) { try { - pgBossNewOptions = JSON.parse(process.env.PG_BOSS_NEW_OPTIONS); + pgBossNewOptions = JSON.parse(env.PG_BOSS_NEW_OPTIONS); } catch (_a) { console.error('Environment variable PG_BOSS_NEW_OPTIONS was not parsable by JSON.parse()!'); diff --git a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/server/jobs/core/pgBoss/pgBoss.js.map b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/server/jobs/core/pgBoss/pgBoss.js.map index ebe3e7b855..c87ccbfc48 100644 --- a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/server/jobs/core/pgBoss/pgBoss.js.map +++ b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/server/jobs/core/pgBoss/pgBoss.js.map @@ -1 +1 @@ -{"version":3,"file":"pgBoss.js","sourceRoot":"","sources":["../../../../../server/jobs/core/pgBoss/pgBoss.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,SAAS,CAAA;AAC5B,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAEpC,MAAM,IAAI,GAAG,YAAY,EAAE,CAAA;AAE3B,SAAS,YAAY;IACnB,IAAI,gBAAgB,GAAG;QACrB,gBAAgB,EAAE,MAAM,CAAC,WAAW;KACrC,CAAA;IAED,uFAAuF;IACvF,IAAI,OAAO,CAAC,GAAG,CAAC,mBAAmB,EAAE,CAAC;QACpC,IAAI,CAAC;YACH,gBAAgB,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC,CAAA;QAChE,CAAC;QAAC,WAAM,CAAC;YACP,OAAO,CAAC,KAAK,CACX,4EAA4E,CAC7E,CAAA;QACH,CAAC;IACH,CAAC;IAED,OAAO,IAAI,MAAM,CAAC,gBAAgB,CAAC,CAAA;AACrC,CAAC;AAED,IAAI,oBAA4C,CAAA;AAChD,IAAI,mBAA2C,CAAA;AAC/C,cAAc;AACd,yEAAyE;AACzE,MAAM,CAAC,MAAM,aAAa,GAAG,IAAI,OAAO,CAAS,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;IACnE,oBAAoB,GAAG,OAAO,CAAA;IAC9B,mBAAmB,GAAG,MAAM,CAAA;AAC9B,CAAC,CAAC,CAAA;AAEF,IAAK,YAKJ;AALD,WAAK,YAAY;IACf,uCAAuB,CAAA;IACvB,qCAAqB,CAAA;IACrB,mCAAmB,CAAA;IACnB,+BAAe,CAAA;AACjB,CAAC,EALI,YAAY,KAAZ,YAAY,QAKhB;AAED,IAAI,YAAY,GAAiB,YAAY,CAAC,SAAS,CAAA;AAEvD,cAAc;AACd;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW;IAC/B,sEAAsE;IACtE,IAAI,YAAY,KAAK,YAAY,CAAC,SAAS,EAAE,CAAC;QAC5C,OAAM;IACR,CAAC;IACD,YAAY,GAAG,YAAY,CAAC,QAAQ,CAAA;IACpC,OAAO,CAAC,GAAG,CAAC,qBAAqB,CAAC,CAAA;IAElC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAA;IACjD,IAAI,CAAC;QACH,MAAM,IAAI,CAAC,KAAK,EAAE,CAAA;IACpB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CAAC,0BAA0B,CAAC,CAAA;QACzC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAA;QACpB,YAAY,GAAG,YAAY,CAAC,KAAK,CAAA;QACjC,mBAAmB,CAAC,IAAI,CAAC,CAAA;QACzB,OAAM;IACR,CAAC;IAED,oBAAoB,CAAC,IAAI,CAAC,CAAA;IAE1B,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAA;IAC/B,YAAY,GAAG,YAAY,CAAC,OAAO,CAAA;AACrC,CAAC"} \ No newline at end of file +{"version":3,"file":"pgBoss.js","sourceRoot":"","sources":["../../../../../server/jobs/core/pgBoss/pgBoss.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,SAAS,CAAA;AAC5B,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,mBAAmB,CAAA;AAE/C,MAAM,IAAI,GAAG,YAAY,EAAE,CAAA;AAE3B,SAAS,YAAY;IACnB,IAAI,gBAAgB,GAAG;QACrB,gBAAgB,EAAE,MAAM,CAAC,WAAW;KACrC,CAAA;IAED,uFAAuF;IACvF,IAAI,GAAG,CAAC,mBAAmB,EAAE,CAAC;QAC5B,IAAI,CAAC;YACH,gBAAgB,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,mBAAmB,CAAC,CAAA;QACxD,CAAC;QAAC,WAAM,CAAC;YACP,OAAO,CAAC,KAAK,CACX,4EAA4E,CAC7E,CAAA;QACH,CAAC;IACH,CAAC;IAED,OAAO,IAAI,MAAM,CAAC,gBAAgB,CAAC,CAAA;AACrC,CAAC;AAED,IAAI,oBAA4C,CAAA;AAChD,IAAI,mBAA2C,CAAA;AAC/C,cAAc;AACd,yEAAyE;AACzE,MAAM,CAAC,MAAM,aAAa,GAAG,IAAI,OAAO,CAAS,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;IACnE,oBAAoB,GAAG,OAAO,CAAA;IAC9B,mBAAmB,GAAG,MAAM,CAAA;AAC9B,CAAC,CAAC,CAAA;AAEF,IAAK,YAKJ;AALD,WAAK,YAAY;IACf,uCAAuB,CAAA;IACvB,qCAAqB,CAAA;IACrB,mCAAmB,CAAA;IACnB,+BAAe,CAAA;AACjB,CAAC,EALI,YAAY,KAAZ,YAAY,QAKhB;AAED,IAAI,YAAY,GAAiB,YAAY,CAAC,SAAS,CAAA;AAEvD,cAAc;AACd;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW;IAC/B,sEAAsE;IACtE,IAAI,YAAY,KAAK,YAAY,CAAC,SAAS,EAAE,CAAC;QAC5C,OAAM;IACR,CAAC;IACD,YAAY,GAAG,YAAY,CAAC,QAAQ,CAAA;IACpC,OAAO,CAAC,GAAG,CAAC,qBAAqB,CAAC,CAAA;IAElC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAA;IACjD,IAAI,CAAC;QACH,MAAM,IAAI,CAAC,KAAK,EAAE,CAAA;IACpB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CAAC,0BAA0B,CAAC,CAAA;QACzC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAA;QACpB,YAAY,GAAG,YAAY,CAAC,KAAK,CAAA;QACjC,mBAAmB,CAAC,IAAI,CAAC,CAAA;QACzB,OAAM;IACR,CAAC;IAED,oBAAoB,CAAC,IAAI,CAAC,CAAA;IAE1B,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAA;IAC/B,YAAY,GAAG,YAAY,CAAC,OAAO,CAAA;AACrC,CAAC"} \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/universal/url.d.ts b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/universal/url.d.ts index aa893e7838..7ceb994694 100644 --- a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/universal/url.d.ts +++ b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/universal/url.d.ts @@ -1 +1,2 @@ -export declare function stripTrailingSlash(url?: string): string | undefined; +export declare function stripTrailingSlash(url: string): string; +export declare function stripTrailingSlash(url: undefined): undefined; diff --git a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/universal/url.js.map b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/universal/url.js.map index a7bad96020..e236f1b977 100644 --- a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/universal/url.js.map +++ b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/universal/url.js.map @@ -1 +1 @@ -{"version":3,"file":"url.js","sourceRoot":"","sources":["../../universal/url.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,kBAAkB,CAAC,GAAY;IAC3C,OAAO,GAAG,aAAH,GAAG,uBAAH,GAAG,CAAE,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;AACnC,CAAC"} \ No newline at end of file +{"version":3,"file":"url.js","sourceRoot":"","sources":["../../universal/url.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,kBAAkB,CAAC,GAAY;IAC3C,OAAO,GAAG,aAAH,GAAG,uBAAH,GAAG,CAAE,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;AACnC,CAAC"} \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/env/index.ts b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/env/index.ts new file mode 100644 index 0000000000..4c783125c1 --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/env/index.ts @@ -0,0 +1,11 @@ +import * as z from 'zod' + +// PUBLIC API +export function defineEnvValidationSchema>( + schema: Schema, +): Schema { + return schema +} + +// PRIVATE API (SDK, Vite config) +export { ensureEnvSchema } from './validation.js' diff --git a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/env/validation.ts b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/env/validation.ts new file mode 100644 index 0000000000..a5bc334754 --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/env/validation.ts @@ -0,0 +1,25 @@ +import * as z from 'zod' + +const redColor = '\x1b[31m' + +export function ensureEnvSchema( + data: unknown, + schema: Schema +): z.infer { + try { + return schema.parse(data) + } catch (e) { + if (e instanceof z.ZodError) { + const errorOutput = ['', 'โ•โ• Env vars validation failed โ•โ•', ''] + for (const error of e.errors) { + errorOutput.push(` - ${error.message}`) + } + errorOutput.push('') + errorOutput.push('โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•') + console.error(redColor, errorOutput.join('\n')) + throw new Error('Error parsing environment variables') + } else { + throw e + } + } +} diff --git a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/package.json b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/package.json index 39b7641835..7fcae55c1d 100644 --- a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/package.json +++ b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/package.json @@ -13,7 +13,6 @@ "axios": "^1.4.0", "express": "~4.21.0", "jsdom": "^21.1.1", - "lodash.merge": "^4.6.2", "lucia": "^3.0.1", "mitt": "3.0.0", "msw": "^1.1.0", @@ -24,7 +23,8 @@ "react-hook-form": "^7.45.4", "react-router-dom": "^6.26.2", "superjson": "^2.2.1", - "vitest": "^1.2.1" + "vitest": "^1.2.1", + "zod": "^3.23.8" }, "devDependencies": { "@tsconfig/node18": "latest", @@ -59,6 +59,7 @@ "./core/storage": "./dist/core/storage.js", "./dev": "./dist/dev/index.js", "./entities": "./dist/entities/index.js", + "./env": "./dist/env/index.js", "./ext-src/*": "./dist/ext-src/*.js", "./operations": "./dist/operations/index.js", "./operations/*": "./dist/operations/*", diff --git a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/server/HttpError.ts b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/server/HttpError.ts index d05a2c841b..bce964712d 100644 --- a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/server/HttpError.ts +++ b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/server/HttpError.ts @@ -2,7 +2,12 @@ export class HttpError extends Error { public statusCode: number public data: unknown - constructor (statusCode: number, message?: string, data?: Record, options?: ErrorOptions) { + constructor( + statusCode: number, + message?: string, + data?: Record, + options?: ErrorOptions + ) { super(message, options) if (Error.captureStackTrace) { @@ -11,7 +16,9 @@ export class HttpError extends Error { this.name = this.constructor.name - if (!(Number.isInteger(statusCode) && statusCode >= 400 && statusCode < 600)) { + if ( + !(Number.isInteger(statusCode) && statusCode >= 400 && statusCode < 600) + ) { throw new Error('statusCode has to be integer in range [400, 600).') } this.statusCode = statusCode diff --git a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/server/auth/oauth/env.ts b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/server/auth/oauth/env.ts deleted file mode 100644 index ada2452b8e..0000000000 --- a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/server/auth/oauth/env.ts +++ /dev/null @@ -1,15 +0,0 @@ -// PRIVATE API (SDK) -export function ensureEnvVarsForProvider( - envVarNames: EnvVarName[], - providerName: string, -): Record { - const result: Record = {}; - for (const envVarName of envVarNames) { - const value = process.env[envVarName]; - if (!value) { - throw new Error(`${envVarName} env variable is required when using the ${providerName} auth provider.`); - } - result[envVarName] = value; - } - return result as Record; -} diff --git a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/server/auth/oauth/provider.ts b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/server/auth/oauth/provider.ts index c2aee70897..c02e5adbee 100644 --- a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/server/auth/oauth/provider.ts +++ b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/server/auth/oauth/provider.ts @@ -1,23 +1,19 @@ import { OAuth2Provider, OAuth2ProviderWithPKCE } from "arctic"; export function defineProvider< - OAuthClient extends OAuth2Provider | OAuth2ProviderWithPKCE, - Env extends Record + OAuthClient extends OAuth2Provider | OAuth2ProviderWithPKCE >({ id, displayName, - env, oAuthClient, }: { id: string; displayName: string; - env: Env; oAuthClient: OAuthClient; }) { return { id, displayName, - env, oAuthClient, }; } diff --git a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/server/auth/oauth/providers/google.ts b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/server/auth/oauth/providers/google.ts index 9f6aaf216c..792e16d308 100644 --- a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/server/auth/oauth/providers/google.ts +++ b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/server/auth/oauth/providers/google.ts @@ -1,16 +1,11 @@ -import { Google } from "arctic"; +import { Google } from 'arctic'; -import { ensureEnvVarsForProvider } from "../env.js"; -import { getRedirectUriForCallback } from "../redirect.js"; -import { defineProvider } from "../provider.js"; +import { getRedirectUriForCallback } from '../redirect.js'; +import { defineProvider } from '../provider.js'; +import { env } from '../../../env.js'; -const id = "google"; -const displayName = "Google"; - -const env = ensureEnvVarsForProvider( - ["GOOGLE_CLIENT_ID", "GOOGLE_CLIENT_SECRET"], - displayName, -); +const id = 'google'; +const displayName = 'Google'; const oAuthClient = new Google( env.GOOGLE_CLIENT_ID, @@ -22,6 +17,5 @@ const oAuthClient = new Google( export const google = defineProvider({ id, displayName, - env, oAuthClient, }); diff --git a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/server/config.ts b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/server/config.ts index 4ca36bd30f..8106e5b84c 100644 --- a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/server/config.ts +++ b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/server/config.ts @@ -1,85 +1,42 @@ -import merge from 'lodash.merge' +import { env } from './env.js' +import { stripTrailingSlash } from '../universal/url.js' -import { stripTrailingSlash } from "../universal/url.js"; +type NodeEnv = typeof env.NODE_ENV -const nodeEnv = process.env.NODE_ENV ?? 'development' - -// TODO: -// - Use dotenv library to consume env vars from a file. -// - Use convict library to define schema and validate env vars. -// https://codingsans.com/blog/node-config-best-practices - -type BaseConfig = { - allowedCORSOrigins: string | string[]; - auth: { - jwtSecret: string | undefined; - } -} - -type CommonConfig = BaseConfig & { - env: string; +type Config = { + env: NodeEnv; isDevelopment: boolean; port: number; - databaseUrl: string | undefined; -} - -type EnvConfig = BaseConfig & { + databaseUrl: string; frontendUrl: string; serverUrl: string; + allowedCORSOrigins: string | string[]; + auth: { + jwtSecret: string; + } } -type Config = CommonConfig & EnvConfig +const frontendUrl = stripTrailingSlash(env.WASP_WEB_CLIENT_URL) +const serverUrl = stripTrailingSlash(env.WASP_SERVER_URL) -const config: { - all: CommonConfig, - development: EnvConfig, - production: EnvConfig, -} = { - all: { - env: nodeEnv, - isDevelopment: nodeEnv === 'development', - port: process.env.PORT ? parseInt(process.env.PORT) : 3001, - databaseUrl: process.env.DATABASE_URL, - allowedCORSOrigins: [], - auth: { - jwtSecret: undefined - } - }, - development: getDevelopmentConfig(), - production: getProductionConfig(), +const allowedCORSOriginsPerEnv: Record = { + development: '*', + production: [frontendUrl] } - -const resolvedConfig: Config = merge(config.all, config[nodeEnv]) -// PUBLIC API -export default resolvedConfig - -function getDevelopmentConfig(): EnvConfig { - const frontendUrl = stripTrailingSlash(process.env.WASP_WEB_CLIENT_URL ?? 'http://localhost:3000/'); - const serverUrl = stripTrailingSlash(process.env.WASP_SERVER_URL ?? 'http://localhost:3001'); - return { - // @ts-ignore - frontendUrl, - // @ts-ignore - serverUrl, - allowedCORSOrigins: '*', - auth: { - jwtSecret: 'DEVJWTSECRET' - } +const allowedCORSOrigins = allowedCORSOriginsPerEnv[env.NODE_ENV] + +const config: Config = { + frontendUrl, + serverUrl, + allowedCORSOrigins, + env: env.NODE_ENV, + isDevelopment: env.NODE_ENV === 'development', + port: env.PORT, + databaseUrl: env.DATABASE_URL, + auth: { + jwtSecret: env.JWT_SECRET } } -function getProductionConfig(): EnvConfig { - const frontendUrl = stripTrailingSlash(process.env.WASP_WEB_CLIENT_URL); - const serverUrl = stripTrailingSlash(process.env.WASP_SERVER_URL); - return { - // @ts-ignore - frontendUrl, - // @ts-ignore - serverUrl, - // @ts-ignore - allowedCORSOrigins: [frontendUrl], - auth: { - jwtSecret: process.env.JWT_SECRET - } - } -} +// PUBLIC API +export default config diff --git a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/server/email/index.ts b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/server/email/index.ts index 3168a70671..217c640ea7 100644 --- a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/server/email/index.ts +++ b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/server/email/index.ts @@ -1,11 +1,10 @@ +import { env } from '../env.js'; import { initEmailSender } from "./core/index.js"; import { EmailSender } from "./core/types.js"; -// TODO: We need to validate all the env variables -// For now, we are letting the runtime throw if they are not provided const emailProvider = { type: "sendgrid", - apiKey: process.env.SENDGRID_API_KEY!, + apiKey: env.SENDGRID_API_KEY, } as const; // PUBLIC API diff --git a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/server/env.ts b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/server/env.ts new file mode 100644 index 0000000000..d7c817e8ea --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/server/env.ts @@ -0,0 +1,81 @@ +import * as z from 'zod' + +import { ensureEnvSchema } from '../env/validation.js' + +const userServerEnvSchema = z.object({}) + +const waspServerCommonSchema = z.object({ + PORT: z.coerce.number().default(3001), + DATABASE_URL: z.string({ + required_error: 'DATABASE_URL is required', + }), + PG_BOSS_NEW_OPTIONS: z.string().optional(), + SENDGRID_API_KEY: z.string({ + required_error: getRequiredEnvVarErrorMessage('SendGrid email sender', 'SENDGRID_API_KEY'), + }), + SKIP_EMAIL_VERIFICATION_IN_DEV: z + .enum(['true', 'false'], { + message: 'SKIP_EMAIL_VERIFICATION_IN_DEV must be either "true" or "false"', + }) + .transform((value) => value === 'true') + .default('false'), + GOOGLE_CLIENT_ID: z.string({ + required_error: getRequiredEnvVarErrorMessage('Google auth provider', 'GOOGLE_CLIENT_ID'), + }), + GOOGLE_CLIENT_SECRET: z.string({ + required_error: getRequiredEnvVarErrorMessage('Google auth provider', 'GOOGLE_CLIENT_SECRET'), + }), +}) + +const serverUrlSchema = z + .string({ + required_error: 'WASP_SERVER_URL is required', + }) + .url({ + message: 'WASP_SERVER_URL must be a valid URL', + }) + +const clientUrlSchema = z + .string({ + required_error: 'WASP_WEB_CLIENT_URL is required', + }) + .url({ + message: 'WASP_WEB_CLIENT_URL must be a valid URL', + }) + +const jwtTokenSchema = z + .string({ + required_error: 'JWT_SECRET is required', + }) + +// In development, we provide default values for some environment variables +// to make the development process easier. +const serverDevSchema = z.object({ + NODE_ENV: z.literal('development'), + WASP_SERVER_URL: serverUrlSchema + .default('http://localhost:3001'), + WASP_WEB_CLIENT_URL: clientUrlSchema + .default('http://localhost:3000/'), + JWT_SECRET: jwtTokenSchema + .default('DEVJWTSECRET'), +}) + +const serverProdSchema = z.object({ + NODE_ENV: z.literal('production'), + WASP_SERVER_URL: serverUrlSchema, + WASP_WEB_CLIENT_URL: clientUrlSchema, + JWT_SECRET: jwtTokenSchema, +}) + +const serverCommonSchema = userServerEnvSchema.merge(waspServerCommonSchema) +const serverEnvSchema = z.discriminatedUnion('NODE_ENV', [ + serverDevSchema.merge(serverCommonSchema), + serverProdSchema.merge(serverCommonSchema) +]) + +// PUBLIC API +export const env = ensureEnvSchema(process.env, serverEnvSchema) + +function getRequiredEnvVarErrorMessage(featureName: string, envVarName: string) { + return `${envVarName} is required when using ${featureName}` +} diff --git a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/server/index.ts b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/server/index.ts index 535077900b..0f95deb90a 100644 --- a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/server/index.ts +++ b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/server/index.ts @@ -10,6 +10,8 @@ export { type ServerSetupFn } from './types/index.js' export { HttpError } from './HttpError.js' // PUBLIC API export { MiddlewareConfigFn } from './middleware/index.js' +// PUBLIC API +export { env } from './env.js' // PUBLIC API export type DbSeedFn = (prisma: PrismaClient) => Promise diff --git a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/server/jobs/core/pgBoss/pgBoss.ts b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/server/jobs/core/pgBoss/pgBoss.ts index a48992aad0..eed0cf23cc 100644 --- a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/server/jobs/core/pgBoss/pgBoss.ts +++ b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/server/jobs/core/pgBoss/pgBoss.ts @@ -1,5 +1,5 @@ import PgBoss from 'pg-boss' -import { config } from 'wasp/server' +import { config, env } from '../../../index.js' const boss = createPgBoss() @@ -9,9 +9,9 @@ function createPgBoss() { } // Add an escape hatch for advanced configuration of pg-boss to overwrite our defaults. - if (process.env.PG_BOSS_NEW_OPTIONS) { + if (env.PG_BOSS_NEW_OPTIONS) { try { - pgBossNewOptions = JSON.parse(process.env.PG_BOSS_NEW_OPTIONS) + pgBossNewOptions = JSON.parse(env.PG_BOSS_NEW_OPTIONS) } catch { console.error( 'Environment variable PG_BOSS_NEW_OPTIONS was not parsable by JSON.parse()!' diff --git a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/universal/url.ts b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/universal/url.ts index d21c06c65c..126678dac9 100644 --- a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/universal/url.ts +++ b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/universal/url.ts @@ -1,3 +1,5 @@ +export function stripTrailingSlash(url: string): string +export function stripTrailingSlash(url: undefined): undefined export function stripTrailingSlash(url?: string): string | undefined { return url?.replace(/\/$/, ""); } diff --git a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/server/package.json b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/server/package.json index 413d85a86f..e3d0d954da 100644 --- a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/server/package.json +++ b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/server/package.json @@ -34,9 +34,8 @@ "bundle-and-start": "npm run bundle && npm run start", "db-migrate-prod": "prisma migrate deploy --schema=../db/schema.prisma", "db-seed": "npm run bundle && node --enable-source-maps -r dotenv/config bundle/dbSeed.js", - "start": "npm run validate-env && node --enable-source-maps -r dotenv/config bundle/server.js", + "start": "node --enable-source-maps -r dotenv/config bundle/server.js", "start-production": "npm run db-migrate-prod && NODE_ENV=production npm run start", - "validate-env": "node -r dotenv/config ./scripts/validate-env.mjs", "watch": "nodemon --exec 'npm run bundle-and-start || exit 1'" }, "type": "module", diff --git a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/server/scripts/validate-env.mjs b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/server/scripts/validate-env.mjs deleted file mode 100644 index f6e50aceca..0000000000 --- a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/server/scripts/validate-env.mjs +++ /dev/null @@ -1,5 +0,0 @@ -import { throwIfNotValidAbsoluteURL } from 'wasp/universal/validators'; - -console.info("๐Ÿ” Validating environment variables..."); -throwIfNotValidAbsoluteURL(process.env.WASP_WEB_CLIENT_URL, 'Environment variable WASP_WEB_CLIENT_URL'); -throwIfNotValidAbsoluteURL(process.env.WASP_SERVER_URL, 'Environment variable WASP_SERVER_URL'); diff --git a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/server/src/auth/providers/oauth/cookies.ts b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/server/src/auth/providers/oauth/cookies.ts index 10fdd63984..b398c2e40d 100644 --- a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/server/src/auth/providers/oauth/cookies.ts +++ b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/server/src/auth/providers/oauth/cookies.ts @@ -1,10 +1,11 @@ import { Request as ExpressRequest, Response as ExpressResponse, -} from "express"; -import { parseCookies } from "oslo/cookie"; +} from 'express'; +import { parseCookies } from 'oslo/cookie'; -import type { ProviderConfig } from "wasp/auth/providers/types"; +import type { ProviderConfig } from 'wasp/auth/providers/types'; +import { config } from 'wasp/server'; import type { OAuthStateFieldName } from './state'; @@ -17,8 +18,7 @@ export function setOAuthCookieValue( const cookieName = `${provider.id}_${fieldName}`; res.cookie(cookieName, value, { httpOnly: true, - // TODO: use server config to determine if secure - secure: process.env.NODE_ENV === "production", + secure: !config.isDevelopment, path: "/", maxAge: 60 * 60 * 1000, // 1 hour }); diff --git a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/server/src/polyfill.ts b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/server/src/polyfill.ts index 7931d12eb0..9279daf6da 100644 --- a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/server/src/polyfill.ts +++ b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/server/src/polyfill.ts @@ -1,11 +1,10 @@ // This is a polyfill for Node.js 18 webcrypto API so Lucia can use it // for random number generation. -import { webcrypto } from "node:crypto"; +import { webcrypto } from 'node:crypto' // NOTE: node < 19 doesn't have Crypto API, which we need for Lucia, so we apply the polyfill if Crypto API is not defined. -if (typeof globalThis.crypto === "undefined") { +if (typeof globalThis.crypto === 'undefined') { // @ts-ignore - globalThis.crypto = webcrypto as Crypto; + globalThis.crypto = webcrypto as Crypto } - diff --git a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/web-app/package.json b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/web-app/package.json index 92845d4306..bb1aa1d8a0 100644 --- a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/web-app/package.json +++ b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/web-app/package.json @@ -18,8 +18,7 @@ "devDependencies": { "@tsconfig/vite-react": "^2.0.0", "@types/react-dom": "^18.0.11", - "@vitejs/plugin-react": "^4.2.1", - "dotenv": "^16.0.3" + "@vitejs/plugin-react": "^4.2.1" }, "engineStrict": true, "engines": { @@ -28,9 +27,8 @@ "name": "waspComplexTest", "private": true, "scripts": { - "build": "npm run validate-env && tsc && vite build", - "start": "npm run validate-env && vite", - "validate-env": "node -r dotenv/config ./scripts/validate-env.mjs" + "build": "tsc && vite build", + "start": "vite" }, "type": "module", "version": "0.0.0" diff --git a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/web-app/scripts/validate-env.mjs b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/web-app/scripts/validate-env.mjs deleted file mode 100644 index 18ee507c9e..0000000000 --- a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/web-app/scripts/validate-env.mjs +++ /dev/null @@ -1,4 +0,0 @@ -import { throwIfNotValidAbsoluteURL } from 'wasp/universal/validators'; - -console.info("๐Ÿ” Validating environment variables..."); -throwIfNotValidAbsoluteURL(process.env.REACT_APP_API_URL, 'Environemnt variable REACT_APP_API_URL'); diff --git a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/web-app/src/components/DefaultRootErrorBoundary.tsx b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/web-app/src/components/DefaultRootErrorBoundary.tsx index 7af24e15d6..1e931935e6 100644 --- a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/web-app/src/components/DefaultRootErrorBoundary.tsx +++ b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/web-app/src/components/DefaultRootErrorBoundary.tsx @@ -7,7 +7,10 @@ export function DefaultRootErrorBoundary() { console.error(error) return ( -
There was an error rendering this page. Check the browser console for more information.
+
+ There was an error rendering this page. Check the browser console for + more information. +
) } diff --git a/waspc/e2e-test/test-outputs/waspJob-golden/files.manifest b/waspc/e2e-test/test-outputs/waspJob-golden/files.manifest index b8a2c274ef..a07c15b99e 100644 --- a/waspc/e2e-test/test-outputs/waspJob-golden/files.manifest +++ b/waspc/e2e-test/test-outputs/waspJob-golden/files.manifest @@ -7,6 +7,7 @@ waspJob/.wasp/out/installedNpmDepsLog.json waspJob/.wasp/out/sdk/wasp/api/events.ts waspJob/.wasp/out/sdk/wasp/api/index.ts waspJob/.wasp/out/sdk/wasp/client/config.ts +waspJob/.wasp/out/sdk/wasp/client/env.ts waspJob/.wasp/out/sdk/wasp/client/index.ts waspJob/.wasp/out/sdk/wasp/client/operations/actions/core.ts waspJob/.wasp/out/sdk/wasp/client/operations/actions/index.ts @@ -36,6 +37,9 @@ waspJob/.wasp/out/sdk/wasp/dist/api/index.js.map waspJob/.wasp/out/sdk/wasp/dist/client/config.d.ts waspJob/.wasp/out/sdk/wasp/dist/client/config.js waspJob/.wasp/out/sdk/wasp/dist/client/config.js.map +waspJob/.wasp/out/sdk/wasp/dist/client/env.d.ts +waspJob/.wasp/out/sdk/wasp/dist/client/env.js +waspJob/.wasp/out/sdk/wasp/dist/client/env.js.map waspJob/.wasp/out/sdk/wasp/dist/client/index.d.ts waspJob/.wasp/out/sdk/wasp/dist/client/index.js waspJob/.wasp/out/sdk/wasp/dist/client/index.js.map @@ -99,6 +103,12 @@ waspJob/.wasp/out/sdk/wasp/dist/dev/index.js.map waspJob/.wasp/out/sdk/wasp/dist/entities/index.d.ts waspJob/.wasp/out/sdk/wasp/dist/entities/index.js waspJob/.wasp/out/sdk/wasp/dist/entities/index.js.map +waspJob/.wasp/out/sdk/wasp/dist/env/index.d.ts +waspJob/.wasp/out/sdk/wasp/dist/env/index.js +waspJob/.wasp/out/sdk/wasp/dist/env/index.js.map +waspJob/.wasp/out/sdk/wasp/dist/env/validation.d.ts +waspJob/.wasp/out/sdk/wasp/dist/env/validation.js +waspJob/.wasp/out/sdk/wasp/dist/env/validation.js.map waspJob/.wasp/out/sdk/wasp/dist/ext-src/MainPage.d.ts waspJob/.wasp/out/sdk/wasp/dist/ext-src/MainPage.jsx waspJob/.wasp/out/sdk/wasp/dist/ext-src/MainPage.jsx.map @@ -123,6 +133,9 @@ waspJob/.wasp/out/sdk/wasp/dist/server/config.js.map waspJob/.wasp/out/sdk/wasp/dist/server/dbClient.d.ts waspJob/.wasp/out/sdk/wasp/dist/server/dbClient.js waspJob/.wasp/out/sdk/wasp/dist/server/dbClient.js.map +waspJob/.wasp/out/sdk/wasp/dist/server/env.d.ts +waspJob/.wasp/out/sdk/wasp/dist/server/env.js +waspJob/.wasp/out/sdk/wasp/dist/server/env.js.map waspJob/.wasp/out/sdk/wasp/dist/server/index.d.ts waspJob/.wasp/out/sdk/wasp/dist/server/index.js waspJob/.wasp/out/sdk/wasp/dist/server/index.js.map @@ -196,6 +209,8 @@ waspJob/.wasp/out/sdk/wasp/dist/universal/validators.d.ts waspJob/.wasp/out/sdk/wasp/dist/universal/validators.js waspJob/.wasp/out/sdk/wasp/dist/universal/validators.js.map waspJob/.wasp/out/sdk/wasp/entities/index.ts +waspJob/.wasp/out/sdk/wasp/env/index.ts +waspJob/.wasp/out/sdk/wasp/env/validation.ts waspJob/.wasp/out/sdk/wasp/ext-src/Main.css waspJob/.wasp/out/sdk/wasp/ext-src/MainPage.jsx waspJob/.wasp/out/sdk/wasp/ext-src/server/jobs/bar.js @@ -209,6 +224,7 @@ waspJob/.wasp/out/sdk/wasp/server/_types/serialization.ts waspJob/.wasp/out/sdk/wasp/server/_types/taggedEntities.ts waspJob/.wasp/out/sdk/wasp/server/config.ts waspJob/.wasp/out/sdk/wasp/server/dbClient.ts +waspJob/.wasp/out/sdk/wasp/server/env.ts waspJob/.wasp/out/sdk/wasp/server/index.ts waspJob/.wasp/out/sdk/wasp/server/jobs/core/job.ts waspJob/.wasp/out/sdk/wasp/server/jobs/core/pgBoss/index.ts @@ -241,7 +257,6 @@ waspJob/.wasp/out/server/README.md waspJob/.wasp/out/server/nodemon.json waspJob/.wasp/out/server/package.json waspJob/.wasp/out/server/rollup.config.js -waspJob/.wasp/out/server/scripts/validate-env.mjs waspJob/.wasp/out/server/src/app.js waspJob/.wasp/out/server/src/jobs/core/allJobs.ts waspJob/.wasp/out/server/src/jobs/scheduleJob.ts @@ -264,7 +279,6 @@ waspJob/.wasp/out/web-app/package.json waspJob/.wasp/out/web-app/public/.gitkeep waspJob/.wasp/out/web-app/public/favicon.ico waspJob/.wasp/out/web-app/public/manifest.json -waspJob/.wasp/out/web-app/scripts/validate-env.mjs waspJob/.wasp/out/web-app/src/components/DefaultRootErrorBoundary.tsx waspJob/.wasp/out/web-app/src/components/FullPageWrapper.tsx waspJob/.wasp/out/web-app/src/components/Loader.module.css diff --git a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/.waspchecksums b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/.waspchecksums index 6b7d97a3d3..215915741c 100644 --- a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/.waspchecksums +++ b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/.waspchecksums @@ -18,14 +18,21 @@ "file", "../out/sdk/wasp/client/config.ts" ], - "d94e8bee5fd8f6793b511652b9ee558e57f4913595763137a5385cd929648168" + "4c5835ad64352cc87a4a17e41fd70333bc0d1eedd7339d7ad3d3507cdb64038d" + ], + [ + [ + "file", + "../out/sdk/wasp/client/env.ts" + ], + "2a3602d40b299fbfa1a6275fe67828d5653d9f3265ba8204b75178aa41dcbae0" ], [ [ "file", "../out/sdk/wasp/client/index.ts" ], - "2b3651e7040a63cfd6a271c2aa89f21cf01170e0abeb6c5ab74adde324852fe4" + "b55e5c34c2fdb50e608bcb4331fa2e67724f1fbfb21e1b93680b0f5fe2b7005a" ], [ [ @@ -167,6 +174,20 @@ ], "c59b97b122b977b5171686c92ee5ff2d80d397c2e83cc0915affb6ee136406fb" ], + [ + [ + "file", + "../out/sdk/wasp/env/index.ts" + ], + "d92b4832e9d4249ae5b1f00c0f9760d6841aa44bc199550414793a1d57a6845e" + ], + [ + [ + "file", + "../out/sdk/wasp/env/validation.ts" + ], + "e22b2fb8931275d3b1bd3176cbdbf753d22fc61887ded0e93cd8febf1b97d260" + ], [ [ "file", @@ -207,7 +228,7 @@ "file", "../out/sdk/wasp/package.json" ], - "c8e81e8cf56626b9051ba9bd6abc9ad7b3c41419f4b710c28b90382264396402" + "489fe48bafdb9893ee52621efcdefee11132f532ff07c22c9cca41fcb09ae6f3" ], [ [ @@ -221,7 +242,7 @@ "file", "../out/sdk/wasp/server/HttpError.ts" ], - "f375fb45eeb16381b8cc575503db302c005ddaa1b35d837ca0ea043fe8ec6ccb" + "9c1b2449d8e9d68470c6014054a3c3074069a104ddb291b9089c30d624a2d9d9" ], [ [ @@ -249,7 +270,7 @@ "file", "../out/sdk/wasp/server/config.ts" ], - "5dd35660e0969defaa3c180179919f4922ab7bc041746872dff7f2280c1872ae" + "8431eaf0332cdbdd2f36b4c232b4b6b65fd19bad58fe51922f5b2662f22fc8ad" ], [ [ @@ -258,12 +279,19 @@ ], "50f11eb232174184be5fd44e8ee3875c30707b486c8c70c3f7bfb93609d38e66" ], + [ + [ + "file", + "../out/sdk/wasp/server/env.ts" + ], + "f3cab17868417ffd4432dc4efecde72ccaaf099a79ddc9194835f3608d123b63" + ], [ [ "file", "../out/sdk/wasp/server/index.ts" ], - "51c3e0802764f72b88f6daaf148dba514ae817e233a40cc9b92ec91784f71f98" + "a8d2ffa7dfd0a13f7fb6135193bcdac1f860603bdd0ddc2843033fd4f6f6151f" ], [ [ @@ -284,7 +312,7 @@ "file", "../out/sdk/wasp/server/jobs/core/pgBoss/pgBoss.ts" ], - "4c405b3cf98a4cea0ee906a46e4676ebe7ce08c1652c1b502403a93ebbbf943a" + "44ea6eb19cb4e283e24b4d3da1105539ef81cd36dfeb71d9fa5695f0b0c15b34" ], [ [ @@ -424,7 +452,7 @@ "file", "../out/sdk/wasp/universal/url.ts" ], - "8dc6e044a1a231b796465d94985ca47c5efd42a5d411b407a7d83a61ebae4b6d" + "58ff4ad8ffc79264d7215461571d909f3c5fb177ff32c67058d1da9cd4115d6e" ], [ [ @@ -452,7 +480,7 @@ "file", "Dockerfile" ], - "8851d4e81582d58921a5646dcf063b31dbf8ce9a46f3d4710360d2047dc644b5" + "20694e4dd2e7d96d8752ee3d792d4d8b63c46d7a4505c5312923a749146604b7" ], [ [ @@ -501,7 +529,7 @@ "file", "server/package.json" ], - "a4eb7e59ac5309fc5c39ccd7e19f4fdc7a9a568a3b393f62358ef6a6065d65c3" + "eaaa1e16e4962f38deb7220a7439a26b920ed7f852890242d084b4cebc63b7c3" ], [ [ @@ -510,13 +538,6 @@ ], "c1beb8264f11898364288d73b16f08d1923bac5f70038d9827978deb5b58a2da" ], - [ - [ - "file", - "server/scripts/validate-env.mjs" - ], - "100177b4326ccab7362eff378315d532ad1cc17cd28d1ed5978cb167fd627746" - ], [ [ "file", @@ -578,7 +599,7 @@ "file", "server/src/polyfill.ts" ], - "1149661e0aa7228b184bb2c04ac63232a6523b09b33829db6977f79daba3fd8d" + "f62c2088c8ebf5889ab479bb937ffd8f0f96d3e85483a7b088edebb5ae598dca" ], [ [ @@ -655,7 +676,7 @@ "file", "web-app/package.json" ], - "bd3bb0b696d11b21492b188790f36d1a09d50d154f3e171f2392406e02304561" + "961707cbde48e25403472b7604ee342e7064b06447307efdedce1098147c14c1" ], [ [ @@ -678,19 +699,12 @@ ], "f223cbdd7db93d51c3ecf8282a06801449b29bfb6821313c383c1b18ad8c1479" ], - [ - [ - "file", - "web-app/scripts/validate-env.mjs" - ], - "a9a3a7eb6bc3ead49d8e3850a70737c93c789098beb3b40196bf145fd38893cd" - ], [ [ "file", "web-app/src/components/DefaultRootErrorBoundary.tsx" ], - "4a141a28ca7aed9daddd75c156f7bae5eec50f3e5a85b6a43a28fa2648f34c44" + "73bbe8a2eb141e523c6b8169c0831e594b4ab6c7207f107d6d20c1561b44373a" ], [ [ diff --git a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/Dockerfile b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/Dockerfile index 44152eecda..2c775ca6af 100644 --- a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/Dockerfile +++ b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/Dockerfile @@ -50,15 +50,15 @@ WORKDIR /app # Copying the top level 'node_modules' because it contains the Prisma packages # necessary for migrating the database. COPY --from=server-builder /app/node_modules ./node_modules -# Copying the SDK because 'validate-env.mjs' executes independent of the bundle +# Copying the SDK because the server bundle doesn't bundle the SDK # and references the 'wasp' package. COPY --from=server-builder /app/.wasp/out/sdk .wasp/out/sdk -# Copying 'server/node_modules' because 'validate-env.mjs' executes independent -# of the bundle and references the dotenv package. +# Copying 'server/node_modules' because we require dotenv package to +# load environment variables +# TODO: replace dotenv with native Node.js environment variable loading COPY --from=server-builder /app/.wasp/build/server/node_modules .wasp/build/server/node_modules COPY --from=server-builder /app/.wasp/build/server/bundle .wasp/build/server/bundle COPY --from=server-builder /app/.wasp/build/server/package*.json .wasp/build/server/ -COPY --from=server-builder /app/.wasp/build/server/scripts .wasp/build/server/scripts COPY db/ .wasp/build/db/ EXPOSE ${PORT} WORKDIR /app/.wasp/build/server diff --git a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/installedNpmDepsLog.json b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/installedNpmDepsLog.json index b173928787..69b60feffb 100644 --- a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/installedNpmDepsLog.json +++ b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/installedNpmDepsLog.json @@ -1 +1 @@ -{"_waspSdkNpmDeps":{"dependencies":[{"name":"@prisma/client","version":"5.19.1"},{"name":"prisma","version":"5.19.1"},{"name":"@tanstack/react-query","version":"^4.29.0"},{"name":"axios","version":"^1.4.0"},{"name":"express","version":"~4.21.0"},{"name":"mitt","version":"3.0.0"},{"name":"react","version":"^18.2.0"},{"name":"lodash.merge","version":"^4.6.2"},{"name":"react-router-dom","version":"^6.26.2"},{"name":"react-hook-form","version":"^7.45.4"},{"name":"superjson","version":"^2.2.1"},{"name":"vitest","version":"^1.2.1"},{"name":"@vitest/ui","version":"^1.2.1"},{"name":"jsdom","version":"^21.1.1"},{"name":"@testing-library/react","version":"^14.1.2"},{"name":"@testing-library/jest-dom","version":"^6.3.0"},{"name":"msw","version":"^1.1.0"},{"name":"pg-boss","version":"^8.4.2"}],"devDependencies":[{"name":"@tsconfig/node18","version":"latest"},{"name":"@types/express-serve-static-core","version":"^4.17.13"}]},"_userNpmDeps":{"userDependencies":[{"name":"react","version":"^18.2.0"},{"name":"react-dom","version":"^18.2.0"},{"name":"react-router-dom","version":"^6.26.2"},{"name":"wasp","version":"file:.wasp/out/sdk/wasp"}],"userDevDependencies":[{"name":"@types/react","version":"^18.0.37"},{"name":"prisma","version":"5.19.1"},{"name":"typescript","version":"^5.1.0"},{"name":"vite","version":"^4.3.9"}]},"_waspFrameworkNpmDeps":{"npmDepsForWebApp":{"dependencies":[{"name":"@tanstack/react-query","version":"^4.29.0"},{"name":"axios","version":"^1.4.0"}],"devDependencies":[{"name":"@tsconfig/vite-react","version":"^2.0.0"},{"name":"@types/react-dom","version":"^18.0.11"},{"name":"@vitejs/plugin-react","version":"^4.2.1"},{"name":"dotenv","version":"^16.0.3"}]},"npmDepsForServer":{"dependencies":[{"name":"cookie-parser","version":"~1.4.6"},{"name":"cors","version":"^2.8.5"},{"name":"dotenv","version":"^16.0.2"},{"name":"express","version":"~4.21.0"},{"name":"helmet","version":"^6.0.0"},{"name":"morgan","version":"~1.10.0"},{"name":"superjson","version":"^2.2.1"}],"devDependencies":[{"name":"@tsconfig/node18","version":"latest"},{"name":"@types/cors","version":"^2.8.5"},{"name":"@types/express","version":"^4.17.13"},{"name":"@types/express-serve-static-core","version":"^4.17.13"},{"name":"@types/node","version":"^18.0.0"},{"name":"nodemon","version":"^2.0.19"},{"name":"rollup","version":"^4.9.6"},{"name":"rollup-plugin-esbuild","version":"^6.1.1"}]}}} \ No newline at end of file +{"_waspSdkNpmDeps":{"dependencies":[{"name":"@prisma/client","version":"5.19.1"},{"name":"prisma","version":"5.19.1"},{"name":"@tanstack/react-query","version":"^4.29.0"},{"name":"axios","version":"^1.4.0"},{"name":"express","version":"~4.21.0"},{"name":"mitt","version":"3.0.0"},{"name":"react","version":"^18.2.0"},{"name":"react-router-dom","version":"^6.26.2"},{"name":"react-hook-form","version":"^7.45.4"},{"name":"superjson","version":"^2.2.1"},{"name":"vitest","version":"^1.2.1"},{"name":"@vitest/ui","version":"^1.2.1"},{"name":"jsdom","version":"^21.1.1"},{"name":"@testing-library/react","version":"^14.1.2"},{"name":"@testing-library/jest-dom","version":"^6.3.0"},{"name":"msw","version":"^1.1.0"},{"name":"pg-boss","version":"^8.4.2"},{"name":"zod","version":"^3.23.8"}],"devDependencies":[{"name":"@tsconfig/node18","version":"latest"},{"name":"@types/express-serve-static-core","version":"^4.17.13"}]},"_userNpmDeps":{"userDependencies":[{"name":"react","version":"^18.2.0"},{"name":"react-dom","version":"^18.2.0"},{"name":"react-router-dom","version":"^6.26.2"},{"name":"wasp","version":"file:.wasp/out/sdk/wasp"}],"userDevDependencies":[{"name":"@types/react","version":"^18.0.37"},{"name":"prisma","version":"5.19.1"},{"name":"typescript","version":"^5.1.0"},{"name":"vite","version":"^4.3.9"}]},"_waspFrameworkNpmDeps":{"npmDepsForWebApp":{"dependencies":[{"name":"@tanstack/react-query","version":"^4.29.0"},{"name":"axios","version":"^1.4.0"}],"devDependencies":[{"name":"@tsconfig/vite-react","version":"^2.0.0"},{"name":"@types/react-dom","version":"^18.0.11"},{"name":"@vitejs/plugin-react","version":"^4.2.1"}]},"npmDepsForServer":{"dependencies":[{"name":"cookie-parser","version":"~1.4.6"},{"name":"cors","version":"^2.8.5"},{"name":"dotenv","version":"^16.0.2"},{"name":"express","version":"~4.21.0"},{"name":"helmet","version":"^6.0.0"},{"name":"morgan","version":"~1.10.0"},{"name":"superjson","version":"^2.2.1"}],"devDependencies":[{"name":"@tsconfig/node18","version":"latest"},{"name":"@types/cors","version":"^2.8.5"},{"name":"@types/express","version":"^4.17.13"},{"name":"@types/express-serve-static-core","version":"^4.17.13"},{"name":"@types/node","version":"^18.0.0"},{"name":"nodemon","version":"^2.0.19"},{"name":"rollup","version":"^4.9.6"},{"name":"rollup-plugin-esbuild","version":"^6.1.1"}]}}} \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/client/config.ts b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/client/config.ts index 82b0f80b6a..fff77cb01c 100644 --- a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/client/config.ts +++ b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/client/config.ts @@ -1,6 +1,7 @@ import { stripTrailingSlash } from '../universal/url.js' +import { env } from './env.js' -const apiUrl = stripTrailingSlash(import.meta.env.REACT_APP_API_URL) || 'http://localhost:3001'; +const apiUrl = stripTrailingSlash(env.REACT_APP_API_URL) // PUBLIC API export type ClientConfig = { diff --git a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/client/env.ts b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/client/env.ts new file mode 100644 index 0000000000..8ddab287f7 --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/client/env.ts @@ -0,0 +1,18 @@ +import * as z from 'zod' + +import { ensureEnvSchema } from '../env/validation.js' + +const userClientEnvSchema = z.object({}) + +const waspClientEnvSchema = z.object({ + REACT_APP_API_URL: z + .string({ + required_error: 'REACT_APP_API_URL is required', + }) + .default('http://localhost:3001') +}) + +const clientEnvSchema = userClientEnvSchema.merge(waspClientEnvSchema) + +// PUBLIC API +export const env = ensureEnvSchema(import.meta.env, clientEnvSchema) diff --git a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/client/index.ts b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/client/index.ts index cfce564c4d..859f8656b6 100644 --- a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/client/index.ts +++ b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/client/index.ts @@ -11,4 +11,7 @@ export enum HttpMethod { export type Route = { method: HttpMethod; path: string } // PUBLIC API -export { config, ClientConfig } from './config' +export { config, ClientConfig } from './config.js' + +// PUBLIC API +export { env } from './env.js' diff --git a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/client/config.js b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/client/config.js index 1dffbd68bc..7e6149f386 100644 --- a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/client/config.js +++ b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/client/config.js @@ -1,5 +1,6 @@ import { stripTrailingSlash } from '../universal/url.js'; -const apiUrl = stripTrailingSlash(import.meta.env.REACT_APP_API_URL) || 'http://localhost:3001'; +import { env } from './env.js'; +const apiUrl = stripTrailingSlash(env.REACT_APP_API_URL); // PUBLIC API export const config = { apiUrl, diff --git a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/client/config.js.map b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/client/config.js.map index d26d37022b..f9e186b838 100644 --- a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/client/config.js.map +++ b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/client/config.js.map @@ -1 +1 @@ -{"version":3,"file":"config.js","sourceRoot":"","sources":["../../client/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAA;AAExD,MAAM,MAAM,GAAG,kBAAkB,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,iBAAiB,CAAC,IAAI,uBAAuB,CAAC;AAOhG,aAAa;AACb,MAAM,CAAC,MAAM,MAAM,GAAiB;IAClC,MAAM;CACP,CAAA"} \ No newline at end of file +{"version":3,"file":"config.js","sourceRoot":"","sources":["../../client/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAA;AACxD,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAA;AAE9B,MAAM,MAAM,GAAG,kBAAkB,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAA;AAOxD,aAAa;AACb,MAAM,CAAC,MAAM,MAAM,GAAiB;IAClC,MAAM;CACP,CAAA"} \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/client/env.d.ts b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/client/env.d.ts new file mode 100644 index 0000000000..5a9417a810 --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/client/env.d.ts @@ -0,0 +1,3 @@ +export declare const env: { + REACT_APP_API_URL: string; +}; diff --git a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/client/env.js b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/client/env.js new file mode 100644 index 0000000000..91164c3768 --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/client/env.js @@ -0,0 +1,14 @@ +import * as z from 'zod'; +import { ensureEnvSchema } from '../env/validation.js'; +const userClientEnvSchema = z.object({}); +const waspClientEnvSchema = z.object({ + REACT_APP_API_URL: z + .string({ + required_error: 'REACT_APP_API_URL is required', + }) + .default('http://localhost:3001') +}); +const clientEnvSchema = userClientEnvSchema.merge(waspClientEnvSchema); +// PUBLIC API +export const env = ensureEnvSchema(import.meta.env, clientEnvSchema); +//# sourceMappingURL=env.js.map \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/client/env.js.map b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/client/env.js.map new file mode 100644 index 0000000000..d230f15c0d --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/client/env.js.map @@ -0,0 +1 @@ +{"version":3,"file":"env.js","sourceRoot":"","sources":["../../client/env.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,KAAK,CAAA;AAExB,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAA;AAEtD,MAAM,mBAAmB,GAAG,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,CAAA;AAExC,MAAM,mBAAmB,GAAG,CAAC,CAAC,MAAM,CAAC;IACnC,iBAAiB,EAAE,CAAC;SACjB,MAAM,CAAC;QACN,cAAc,EAAE,+BAA+B;KAChD,CAAC;SACD,OAAO,CAAC,uBAAuB,CAAC;CACpC,CAAC,CAAA;AAEF,MAAM,eAAe,GAAG,mBAAmB,CAAC,KAAK,CAAC,mBAAmB,CAAC,CAAA;AAEtE,aAAa;AACb,MAAM,CAAC,MAAM,GAAG,GAAG,eAAe,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,eAAe,CAAC,CAAA"} \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/client/index.d.ts b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/client/index.d.ts index 7f5f4bfbc8..6498c439bd 100644 --- a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/client/index.d.ts +++ b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/client/index.d.ts @@ -8,4 +8,5 @@ export type Route = { method: HttpMethod; path: string; }; -export { config, ClientConfig } from './config'; +export { config, ClientConfig } from './config.js'; +export { env } from './env.js'; diff --git a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/client/index.js b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/client/index.js index 5a7f13dcae..2f4daa7ae3 100644 --- a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/client/index.js +++ b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/client/index.js @@ -8,5 +8,7 @@ export var HttpMethod; HttpMethod["Delete"] = "DELETE"; })(HttpMethod || (HttpMethod = {})); // PUBLIC API -export { config } from './config'; +export { config } from './config.js'; +// PUBLIC API +export { env } from './env.js'; //# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/client/index.js.map b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/client/index.js.map index 879953c8df..f3611558dc 100644 --- a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/client/index.js.map +++ b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/client/index.js.map @@ -1 +1 @@ -{"version":3,"file":"index.js","sourceRoot":"","sources":["../../client/index.ts"],"names":[],"mappings":"AAAA,aAAa;AACb,mFAAmF;AACnF,MAAM,CAAN,IAAY,UAKX;AALD,WAAY,UAAU;IACrB,yBAAW,CAAA;IACX,2BAAa,CAAA;IACb,yBAAW,CAAA;IACX,+BAAiB,CAAA;AAClB,CAAC,EALW,UAAU,KAAV,UAAU,QAKrB;AAKD,aAAa;AACb,OAAO,EAAE,MAAM,EAAgB,MAAM,UAAU,CAAA"} \ No newline at end of file +{"version":3,"file":"index.js","sourceRoot":"","sources":["../../client/index.ts"],"names":[],"mappings":"AAAA,aAAa;AACb,mFAAmF;AACnF,MAAM,CAAN,IAAY,UAKX;AALD,WAAY,UAAU;IACrB,yBAAW,CAAA;IACX,2BAAa,CAAA;IACb,yBAAW,CAAA;IACX,+BAAiB,CAAA;AAClB,CAAC,EALW,UAAU,KAAV,UAAU,QAKrB;AAKD,aAAa;AACb,OAAO,EAAE,MAAM,EAAgB,MAAM,aAAa,CAAA;AAElD,aAAa;AACb,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAA"} \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/env/index.d.ts b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/env/index.d.ts new file mode 100644 index 0000000000..d1dafc25fd --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/env/index.d.ts @@ -0,0 +1,3 @@ +import * as z from 'zod'; +export declare function defineEnvValidationSchema>(schema: Schema): Schema; +export { ensureEnvSchema } from './validation.js'; diff --git a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/env/index.js b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/env/index.js new file mode 100644 index 0000000000..17e695d087 --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/env/index.js @@ -0,0 +1,7 @@ +// PUBLIC API +export function defineEnvValidationSchema(schema) { + return schema; +} +// PRIVATE API (SDK, Vite config) +export { ensureEnvSchema } from './validation.js'; +//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/env/index.js.map b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/env/index.js.map new file mode 100644 index 0000000000..75b4ff7de3 --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/env/index.js.map @@ -0,0 +1 @@ +{"version":3,"file":"index.js","sourceRoot":"","sources":["../../env/index.ts"],"names":[],"mappings":"AAEA,aAAa;AACb,MAAM,UAAU,yBAAyB,CACvC,MAAc;IAEd,OAAO,MAAM,CAAA;AACf,CAAC;AAED,iCAAiC;AACjC,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA"} \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/env/validation.d.ts b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/env/validation.d.ts new file mode 100644 index 0000000000..6bc9d059a3 --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/env/validation.d.ts @@ -0,0 +1,2 @@ +import * as z from 'zod'; +export declare function ensureEnvSchema(data: unknown, schema: Schema): z.infer; diff --git a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/env/validation.js b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/env/validation.js new file mode 100644 index 0000000000..8463eb578e --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/env/validation.js @@ -0,0 +1,23 @@ +import * as z from 'zod'; +const redColor = '\x1b[31m'; +export function ensureEnvSchema(data, schema) { + try { + return schema.parse(data); + } + catch (e) { + if (e instanceof z.ZodError) { + const errorOutput = ['', 'โ•โ• Env vars validation failed โ•โ•', '']; + for (const error of e.errors) { + errorOutput.push(` - ${error.message}`); + } + errorOutput.push(''); + errorOutput.push('โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•'); + console.error(redColor, errorOutput.join('\n')); + throw new Error('Error parsing environment variables'); + } + else { + throw e; + } + } +} +//# sourceMappingURL=validation.js.map \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/env/validation.js.map b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/env/validation.js.map new file mode 100644 index 0000000000..1d57b14a0b --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/env/validation.js.map @@ -0,0 +1 @@ +{"version":3,"file":"validation.js","sourceRoot":"","sources":["../../env/validation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,KAAK,CAAA;AAExB,MAAM,QAAQ,GAAG,UAAU,CAAA;AAE3B,MAAM,UAAU,eAAe,CAC7B,IAAa,EACb,MAAc;IAEd,IAAI,CAAC;QACH,OAAO,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;IAC3B,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,IAAI,CAAC,YAAY,CAAC,CAAC,QAAQ,EAAE,CAAC;YAC5B,MAAM,WAAW,GAAG,CAAC,EAAE,EAAE,kCAAkC,EAAE,EAAE,CAAC,CAAA;YAChE,KAAK,MAAM,KAAK,IAAI,CAAC,CAAC,MAAM,EAAE,CAAC;gBAC7B,WAAW,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC,OAAO,EAAE,CAAC,CAAA;YACzC,CAAC;YACD,WAAW,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;YACpB,WAAW,CAAC,IAAI,CAAC,kCAAkC,CAAC,CAAA;YACpD,OAAO,CAAC,KAAK,CAAC,QAAQ,EAAE,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAA;YAC/C,MAAM,IAAI,KAAK,CAAC,qCAAqC,CAAC,CAAA;QACxD,CAAC;aAAM,CAAC;YACN,MAAM,CAAC,CAAA;QACT,CAAC;IACH,CAAC;AACH,CAAC"} \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/server/HttpError.js.map b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/server/HttpError.js.map index 8163354e1b..462e1c1811 100644 --- a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/server/HttpError.js.map +++ b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/server/HttpError.js.map @@ -1 +1 @@ -{"version":3,"file":"HttpError.js","sourceRoot":"","sources":["../../server/HttpError.ts"],"names":[],"mappings":"AAAA,MAAM,OAAO,SAAU,SAAQ,KAAK;IAIlC,YAAa,UAAkB,EAAE,OAAgB,EAAE,IAA8B,EAAE,OAAsB;QACvG,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;QAEvB,IAAI,KAAK,CAAC,iBAAiB,EAAE,CAAC;YAC5B,KAAK,CAAC,iBAAiB,CAAC,IAAI,EAAE,SAAS,CAAC,CAAA;QAC1C,CAAC;QAED,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,WAAW,CAAC,IAAI,CAAA;QAEjC,IAAI,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,UAAU,CAAC,IAAI,UAAU,IAAI,GAAG,IAAI,UAAU,GAAG,GAAG,CAAC,EAAE,CAAC;YAC7E,MAAM,IAAI,KAAK,CAAC,mDAAmD,CAAC,CAAA;QACtE,CAAC;QACD,IAAI,CAAC,UAAU,GAAG,UAAU,CAAA;QAE5B,IAAI,IAAI,EAAE,CAAC;YACT,IAAI,CAAC,IAAI,GAAG,IAAI,CAAA;QAClB,CAAC;IACH,CAAC;CACF"} \ No newline at end of file +{"version":3,"file":"HttpError.js","sourceRoot":"","sources":["../../server/HttpError.ts"],"names":[],"mappings":"AAAA,MAAM,OAAO,SAAU,SAAQ,KAAK;IAIlC,YACE,UAAkB,EAClB,OAAgB,EAChB,IAA8B,EAC9B,OAAsB;QAEtB,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;QAEvB,IAAI,KAAK,CAAC,iBAAiB,EAAE,CAAC;YAC5B,KAAK,CAAC,iBAAiB,CAAC,IAAI,EAAE,SAAS,CAAC,CAAA;QAC1C,CAAC;QAED,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,WAAW,CAAC,IAAI,CAAA;QAEjC,IACE,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,UAAU,CAAC,IAAI,UAAU,IAAI,GAAG,IAAI,UAAU,GAAG,GAAG,CAAC,EACxE,CAAC;YACD,MAAM,IAAI,KAAK,CAAC,mDAAmD,CAAC,CAAA;QACtE,CAAC;QACD,IAAI,CAAC,UAAU,GAAG,UAAU,CAAA;QAE5B,IAAI,IAAI,EAAE,CAAC;YACT,IAAI,CAAC,IAAI,GAAG,IAAI,CAAA;QAClB,CAAC;IACH,CAAC;CACF"} \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/server/config.d.ts b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/server/config.d.ts index 68bb448c56..70f438bdf0 100644 --- a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/server/config.d.ts +++ b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/server/config.d.ts @@ -1,16 +1,13 @@ -type BaseConfig = { - allowedCORSOrigins: string | string[]; -}; -type CommonConfig = BaseConfig & { - env: string; +import { env } from './env.js'; +type NodeEnv = typeof env.NODE_ENV; +type Config = { + env: NodeEnv; isDevelopment: boolean; port: number; - databaseUrl: string | undefined; -}; -type EnvConfig = BaseConfig & { + databaseUrl: string; frontendUrl: string; serverUrl: string; + allowedCORSOrigins: string | string[]; }; -type Config = CommonConfig & EnvConfig; -declare const resolvedConfig: Config; -export default resolvedConfig; +declare const config: Config; +export default config; diff --git a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/server/config.js b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/server/config.js index e4f89082c8..dcc80722ab 100644 --- a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/server/config.js +++ b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/server/config.js @@ -1,43 +1,21 @@ -var _a; -import merge from 'lodash.merge'; -import { stripTrailingSlash } from "../universal/url.js"; -const nodeEnv = (_a = process.env.NODE_ENV) !== null && _a !== void 0 ? _a : 'development'; +import { env } from './env.js'; +import { stripTrailingSlash } from '../universal/url.js'; +const frontendUrl = stripTrailingSlash(env.WASP_WEB_CLIENT_URL); +const serverUrl = stripTrailingSlash(env.WASP_SERVER_URL); +const allowedCORSOriginsPerEnv = { + development: '*', + production: [frontendUrl] +}; +const allowedCORSOrigins = allowedCORSOriginsPerEnv[env.NODE_ENV]; const config = { - all: { - env: nodeEnv, - isDevelopment: nodeEnv === 'development', - port: process.env.PORT ? parseInt(process.env.PORT) : 3001, - databaseUrl: process.env.DATABASE_URL, - allowedCORSOrigins: [], - }, - development: getDevelopmentConfig(), - production: getProductionConfig(), + frontendUrl, + serverUrl, + allowedCORSOrigins, + env: env.NODE_ENV, + isDevelopment: env.NODE_ENV === 'development', + port: env.PORT, + databaseUrl: env.DATABASE_URL, }; -const resolvedConfig = merge(config.all, config[nodeEnv]); // PUBLIC API -export default resolvedConfig; -function getDevelopmentConfig() { - var _a, _b; - const frontendUrl = stripTrailingSlash((_a = process.env.WASP_WEB_CLIENT_URL) !== null && _a !== void 0 ? _a : 'http://localhost:3000/'); - const serverUrl = stripTrailingSlash((_b = process.env.WASP_SERVER_URL) !== null && _b !== void 0 ? _b : 'http://localhost:3001'); - return { - // @ts-ignore - frontendUrl, - // @ts-ignore - serverUrl, - allowedCORSOrigins: '*', - }; -} -function getProductionConfig() { - const frontendUrl = stripTrailingSlash(process.env.WASP_WEB_CLIENT_URL); - const serverUrl = stripTrailingSlash(process.env.WASP_SERVER_URL); - return { - // @ts-ignore - frontendUrl, - // @ts-ignore - serverUrl, - // @ts-ignore - allowedCORSOrigins: [frontendUrl], - }; -} +export default config; //# sourceMappingURL=config.js.map \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/server/config.js.map b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/server/config.js.map index e284580bbd..9fd077fa81 100644 --- a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/server/config.js.map +++ b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/server/config.js.map @@ -1 +1 @@ -{"version":3,"file":"config.js","sourceRoot":"","sources":["../../server/config.ts"],"names":[],"mappings":";AAAA,OAAO,KAAK,MAAM,cAAc,CAAA;AAEhC,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAEzD,MAAM,OAAO,GAAG,MAAA,OAAO,CAAC,GAAG,CAAC,QAAQ,mCAAI,aAAa,CAAA;AAyBrD,MAAM,MAAM,GAIR;IACF,GAAG,EAAE;QACH,GAAG,EAAE,OAAO;QACZ,aAAa,EAAE,OAAO,KAAK,aAAa;QACxC,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI;QAC1D,WAAW,EAAE,OAAO,CAAC,GAAG,CAAC,YAAY;QACrC,kBAAkB,EAAE,EAAE;KACvB;IACD,WAAW,EAAE,oBAAoB,EAAE;IACnC,UAAU,EAAE,mBAAmB,EAAE;CAClC,CAAA;AAED,MAAM,cAAc,GAAW,KAAK,CAAC,MAAM,CAAC,GAAG,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC,CAAA;AACjE,aAAa;AACb,eAAe,cAAc,CAAA;AAE7B,SAAS,oBAAoB;;IAC3B,MAAM,WAAW,GAAG,kBAAkB,CAAC,MAAA,OAAO,CAAC,GAAG,CAAC,mBAAmB,mCAAI,wBAAwB,CAAC,CAAC;IACpG,MAAM,SAAS,GAAG,kBAAkB,CAAC,MAAA,OAAO,CAAC,GAAG,CAAC,eAAe,mCAAI,uBAAuB,CAAC,CAAC;IAC7F,OAAO;QACL,aAAa;QACb,WAAW;QACX,aAAa;QACb,SAAS;QACT,kBAAkB,EAAE,GAAG;KACxB,CAAA;AACH,CAAC;AAED,SAAS,mBAAmB;IAC1B,MAAM,WAAW,GAAG,kBAAkB,CAAC,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC,CAAC;IACxE,MAAM,SAAS,GAAG,kBAAkB,CAAC,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC;IAClE,OAAO;QACL,aAAa;QACb,WAAW;QACX,aAAa;QACb,SAAS;QACT,aAAa;QACb,kBAAkB,EAAE,CAAC,WAAW,CAAC;KAClC,CAAA;AACH,CAAC"} \ No newline at end of file +{"version":3,"file":"config.js","sourceRoot":"","sources":["../../server/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAA;AAC9B,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAA;AAcxD,MAAM,WAAW,GAAG,kBAAkB,CAAC,GAAG,CAAC,mBAAmB,CAAC,CAAA;AAC/D,MAAM,SAAS,GAAG,kBAAkB,CAAC,GAAG,CAAC,eAAe,CAAC,CAAA;AAEzD,MAAM,wBAAwB,GAAuC;IACnE,WAAW,EAAE,GAAG;IAChB,UAAU,EAAE,CAAC,WAAW,CAAC;CAC1B,CAAA;AACD,MAAM,kBAAkB,GAAG,wBAAwB,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAA;AAEjE,MAAM,MAAM,GAAW;IACrB,WAAW;IACX,SAAS;IACT,kBAAkB;IAClB,GAAG,EAAE,GAAG,CAAC,QAAQ;IACjB,aAAa,EAAE,GAAG,CAAC,QAAQ,KAAK,aAAa;IAC7C,IAAI,EAAE,GAAG,CAAC,IAAI;IACd,WAAW,EAAE,GAAG,CAAC,YAAY;CAC9B,CAAA;AAED,aAAa;AACb,eAAe,MAAM,CAAA"} \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/server/env.d.ts b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/server/env.d.ts new file mode 100644 index 0000000000..667ea30e35 --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/server/env.d.ts @@ -0,0 +1,17 @@ +export declare const env: { + PORT: number; + DATABASE_URL: string; + SKIP_EMAIL_VERIFICATION_IN_DEV: boolean; + NODE_ENV: "development"; + WASP_SERVER_URL: string; + WASP_WEB_CLIENT_URL: string; + PG_BOSS_NEW_OPTIONS?: string | undefined; +} | { + PORT: number; + DATABASE_URL: string; + SKIP_EMAIL_VERIFICATION_IN_DEV: boolean; + NODE_ENV: "production"; + WASP_SERVER_URL: string; + WASP_WEB_CLIENT_URL: string; + PG_BOSS_NEW_OPTIONS?: string | undefined; +}; diff --git a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/server/env.js b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/server/env.js new file mode 100644 index 0000000000..1bfc3e0b6e --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/server/env.js @@ -0,0 +1,55 @@ +import * as z from 'zod'; +import { ensureEnvSchema } from '../env/validation.js'; +const userServerEnvSchema = z.object({}); +const waspServerCommonSchema = z.object({ + PORT: z.coerce.number().default(3001), + DATABASE_URL: z.string({ + required_error: 'DATABASE_URL is required', + }), + PG_BOSS_NEW_OPTIONS: z.string().optional(), + SKIP_EMAIL_VERIFICATION_IN_DEV: z + .enum(['true', 'false'], { + message: 'SKIP_EMAIL_VERIFICATION_IN_DEV must be either "true" or "false"', + }) + .transform((value) => value === 'true') + .default('false'), +}); +const serverUrlSchema = z + .string({ + required_error: 'WASP_SERVER_URL is required', +}) + .url({ + message: 'WASP_SERVER_URL must be a valid URL', +}); +const clientUrlSchema = z + .string({ + required_error: 'WASP_WEB_CLIENT_URL is required', +}) + .url({ + message: 'WASP_WEB_CLIENT_URL must be a valid URL', +}); +// In development, we provide default values for some environment variables +// to make the development process easier. +const serverDevSchema = z.object({ + NODE_ENV: z.literal('development'), + WASP_SERVER_URL: serverUrlSchema + .default('http://localhost:3001'), + WASP_WEB_CLIENT_URL: clientUrlSchema + .default('http://localhost:3000/'), +}); +const serverProdSchema = z.object({ + NODE_ENV: z.literal('production'), + WASP_SERVER_URL: serverUrlSchema, + WASP_WEB_CLIENT_URL: clientUrlSchema, +}); +const serverCommonSchema = userServerEnvSchema.merge(waspServerCommonSchema); +const serverEnvSchema = z.discriminatedUnion('NODE_ENV', [ + serverDevSchema.merge(serverCommonSchema), + serverProdSchema.merge(serverCommonSchema) +]); +// PUBLIC API +export const env = ensureEnvSchema(process.env, serverEnvSchema); +function getRequiredEnvVarErrorMessage(featureName, envVarName) { + return `${envVarName} is required when using ${featureName}`; +} +//# sourceMappingURL=env.js.map \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/server/env.js.map b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/server/env.js.map new file mode 100644 index 0000000000..3dfa10e116 --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/server/env.js.map @@ -0,0 +1 @@ +{"version":3,"file":"env.js","sourceRoot":"","sources":["../../server/env.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,KAAK,CAAA;AAExB,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAA;AAEtD,MAAM,mBAAmB,GAAG,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,CAAA;AAExC,MAAM,sBAAsB,GAAG,CAAC,CAAC,MAAM,CAAC;IACtC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC;IACrC,YAAY,EAAE,CAAC,CAAC,MAAM,CAAC;QACrB,cAAc,EAAE,0BAA0B;KAC3C,CAAC;IACF,mBAAmB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC1C,8BAA8B,EAAE,CAAC;SAC9B,IAAI,CAAC,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE;QACvB,OAAO,EAAE,iEAAiE;KAC3E,CAAC;SACD,SAAS,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,KAAK,MAAM,CAAC;SACtC,OAAO,CAAC,OAAO,CAAC;CACpB,CAAC,CAAA;AAEF,MAAM,eAAe,GAAG,CAAC;KACtB,MAAM,CAAC;IACN,cAAc,EAAE,6BAA6B;CAC9C,CAAC;KACD,GAAG,CAAC;IACH,OAAO,EAAE,qCAAqC;CAC/C,CAAC,CAAA;AAEJ,MAAM,eAAe,GAAG,CAAC;KACtB,MAAM,CAAC;IACN,cAAc,EAAE,iCAAiC;CAClD,CAAC;KACD,GAAG,CAAC;IACH,OAAO,EAAE,yCAAyC;CACnD,CAAC,CAAA;AAGJ,2EAA2E;AAC3E,0CAA0C;AAC1C,MAAM,eAAe,GAAG,CAAC,CAAC,MAAM,CAAC;IAC/B,QAAQ,EAAE,CAAC,CAAC,OAAO,CAAC,aAAa,CAAC;IAClC,eAAe,EAAE,eAAe;SAC7B,OAAO,CAAC,uBAAuB,CAAC;IACnC,mBAAmB,EAAE,eAAe;SACjC,OAAO,CAAC,wBAAwB,CAAC;CACrC,CAAC,CAAA;AAEF,MAAM,gBAAgB,GAAG,CAAC,CAAC,MAAM,CAAC;IAChC,QAAQ,EAAE,CAAC,CAAC,OAAO,CAAC,YAAY,CAAC;IACjC,eAAe,EAAE,eAAe;IAChC,mBAAmB,EAAE,eAAe;CACrC,CAAC,CAAA;AAEF,MAAM,kBAAkB,GAAG,mBAAmB,CAAC,KAAK,CAAC,sBAAsB,CAAC,CAAA;AAC5E,MAAM,eAAe,GAAG,CAAC,CAAC,kBAAkB,CAAC,UAAU,EAAE;IACvD,eAAe,CAAC,KAAK,CAAC,kBAAkB,CAAC;IACzC,gBAAgB,CAAC,KAAK,CAAC,kBAAkB,CAAC;CAC3C,CAAC,CAAA;AAEF,aAAa;AACb,MAAM,CAAC,MAAM,GAAG,GAAG,eAAe,CAAC,OAAO,CAAC,GAAG,EAAE,eAAe,CAAC,CAAA;AAEhE,SAAS,6BAA6B,CAAC,WAAmB,EAAE,UAAkB;IAC5E,OAAO,GAAG,UAAU,2BAA2B,WAAW,EAAE,CAAA;AAC9D,CAAC"} \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/server/index.d.ts b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/server/index.d.ts index dee9613a53..a108f6123f 100644 --- a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/server/index.d.ts +++ b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/server/index.d.ts @@ -4,4 +4,5 @@ export { default as prisma } from './dbClient.js'; export { type ServerSetupFn } from './types/index.js'; export { HttpError } from './HttpError.js'; export { MiddlewareConfigFn } from './middleware/index.js'; +export { env } from './env.js'; export type DbSeedFn = (prisma: PrismaClient) => Promise; diff --git a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/server/index.js b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/server/index.js index 0e53e0deab..8e75e6f78c 100644 --- a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/server/index.js +++ b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/server/index.js @@ -4,4 +4,6 @@ export { default as config } from './config.js'; export { default as prisma } from './dbClient.js'; // PUBLIC API export { HttpError } from './HttpError.js'; +// PUBLIC API +export { env } from './env.js'; //# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/server/index.js.map b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/server/index.js.map index b84ac1b303..87ed22f3af 100644 --- a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/server/index.js.map +++ b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/server/index.js.map @@ -1 +1 @@ -{"version":3,"file":"index.js","sourceRoot":"","sources":["../../server/index.ts"],"names":[],"mappings":"AAEA,aAAa;AACb,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,aAAa,CAAA;AAC/C,aAAa;AACb,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,eAAe,CAAA;AAGjD,aAAa;AACb,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA"} \ No newline at end of file +{"version":3,"file":"index.js","sourceRoot":"","sources":["../../server/index.ts"],"names":[],"mappings":"AAEA,aAAa;AACb,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,aAAa,CAAA;AAC/C,aAAa;AACb,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,eAAe,CAAA;AAGjD,aAAa;AACb,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAG1C,aAAa;AACb,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAA"} \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/server/jobs/core/pgBoss/pgBoss.js b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/server/jobs/core/pgBoss/pgBoss.js index d1feb61aed..8fe6f220bc 100644 --- a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/server/jobs/core/pgBoss/pgBoss.js +++ b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/server/jobs/core/pgBoss/pgBoss.js @@ -1,14 +1,14 @@ import PgBoss from 'pg-boss'; -import { config } from 'wasp/server'; +import { config, env } from '../../../index.js'; const boss = createPgBoss(); function createPgBoss() { let pgBossNewOptions = { connectionString: config.databaseUrl, }; // Add an escape hatch for advanced configuration of pg-boss to overwrite our defaults. - if (process.env.PG_BOSS_NEW_OPTIONS) { + if (env.PG_BOSS_NEW_OPTIONS) { try { - pgBossNewOptions = JSON.parse(process.env.PG_BOSS_NEW_OPTIONS); + pgBossNewOptions = JSON.parse(env.PG_BOSS_NEW_OPTIONS); } catch (_a) { console.error('Environment variable PG_BOSS_NEW_OPTIONS was not parsable by JSON.parse()!'); diff --git a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/server/jobs/core/pgBoss/pgBoss.js.map b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/server/jobs/core/pgBoss/pgBoss.js.map index ebe3e7b855..c87ccbfc48 100644 --- a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/server/jobs/core/pgBoss/pgBoss.js.map +++ b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/server/jobs/core/pgBoss/pgBoss.js.map @@ -1 +1 @@ -{"version":3,"file":"pgBoss.js","sourceRoot":"","sources":["../../../../../server/jobs/core/pgBoss/pgBoss.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,SAAS,CAAA;AAC5B,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAEpC,MAAM,IAAI,GAAG,YAAY,EAAE,CAAA;AAE3B,SAAS,YAAY;IACnB,IAAI,gBAAgB,GAAG;QACrB,gBAAgB,EAAE,MAAM,CAAC,WAAW;KACrC,CAAA;IAED,uFAAuF;IACvF,IAAI,OAAO,CAAC,GAAG,CAAC,mBAAmB,EAAE,CAAC;QACpC,IAAI,CAAC;YACH,gBAAgB,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC,CAAA;QAChE,CAAC;QAAC,WAAM,CAAC;YACP,OAAO,CAAC,KAAK,CACX,4EAA4E,CAC7E,CAAA;QACH,CAAC;IACH,CAAC;IAED,OAAO,IAAI,MAAM,CAAC,gBAAgB,CAAC,CAAA;AACrC,CAAC;AAED,IAAI,oBAA4C,CAAA;AAChD,IAAI,mBAA2C,CAAA;AAC/C,cAAc;AACd,yEAAyE;AACzE,MAAM,CAAC,MAAM,aAAa,GAAG,IAAI,OAAO,CAAS,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;IACnE,oBAAoB,GAAG,OAAO,CAAA;IAC9B,mBAAmB,GAAG,MAAM,CAAA;AAC9B,CAAC,CAAC,CAAA;AAEF,IAAK,YAKJ;AALD,WAAK,YAAY;IACf,uCAAuB,CAAA;IACvB,qCAAqB,CAAA;IACrB,mCAAmB,CAAA;IACnB,+BAAe,CAAA;AACjB,CAAC,EALI,YAAY,KAAZ,YAAY,QAKhB;AAED,IAAI,YAAY,GAAiB,YAAY,CAAC,SAAS,CAAA;AAEvD,cAAc;AACd;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW;IAC/B,sEAAsE;IACtE,IAAI,YAAY,KAAK,YAAY,CAAC,SAAS,EAAE,CAAC;QAC5C,OAAM;IACR,CAAC;IACD,YAAY,GAAG,YAAY,CAAC,QAAQ,CAAA;IACpC,OAAO,CAAC,GAAG,CAAC,qBAAqB,CAAC,CAAA;IAElC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAA;IACjD,IAAI,CAAC;QACH,MAAM,IAAI,CAAC,KAAK,EAAE,CAAA;IACpB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CAAC,0BAA0B,CAAC,CAAA;QACzC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAA;QACpB,YAAY,GAAG,YAAY,CAAC,KAAK,CAAA;QACjC,mBAAmB,CAAC,IAAI,CAAC,CAAA;QACzB,OAAM;IACR,CAAC;IAED,oBAAoB,CAAC,IAAI,CAAC,CAAA;IAE1B,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAA;IAC/B,YAAY,GAAG,YAAY,CAAC,OAAO,CAAA;AACrC,CAAC"} \ No newline at end of file +{"version":3,"file":"pgBoss.js","sourceRoot":"","sources":["../../../../../server/jobs/core/pgBoss/pgBoss.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,SAAS,CAAA;AAC5B,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,mBAAmB,CAAA;AAE/C,MAAM,IAAI,GAAG,YAAY,EAAE,CAAA;AAE3B,SAAS,YAAY;IACnB,IAAI,gBAAgB,GAAG;QACrB,gBAAgB,EAAE,MAAM,CAAC,WAAW;KACrC,CAAA;IAED,uFAAuF;IACvF,IAAI,GAAG,CAAC,mBAAmB,EAAE,CAAC;QAC5B,IAAI,CAAC;YACH,gBAAgB,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,mBAAmB,CAAC,CAAA;QACxD,CAAC;QAAC,WAAM,CAAC;YACP,OAAO,CAAC,KAAK,CACX,4EAA4E,CAC7E,CAAA;QACH,CAAC;IACH,CAAC;IAED,OAAO,IAAI,MAAM,CAAC,gBAAgB,CAAC,CAAA;AACrC,CAAC;AAED,IAAI,oBAA4C,CAAA;AAChD,IAAI,mBAA2C,CAAA;AAC/C,cAAc;AACd,yEAAyE;AACzE,MAAM,CAAC,MAAM,aAAa,GAAG,IAAI,OAAO,CAAS,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;IACnE,oBAAoB,GAAG,OAAO,CAAA;IAC9B,mBAAmB,GAAG,MAAM,CAAA;AAC9B,CAAC,CAAC,CAAA;AAEF,IAAK,YAKJ;AALD,WAAK,YAAY;IACf,uCAAuB,CAAA;IACvB,qCAAqB,CAAA;IACrB,mCAAmB,CAAA;IACnB,+BAAe,CAAA;AACjB,CAAC,EALI,YAAY,KAAZ,YAAY,QAKhB;AAED,IAAI,YAAY,GAAiB,YAAY,CAAC,SAAS,CAAA;AAEvD,cAAc;AACd;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW;IAC/B,sEAAsE;IACtE,IAAI,YAAY,KAAK,YAAY,CAAC,SAAS,EAAE,CAAC;QAC5C,OAAM;IACR,CAAC;IACD,YAAY,GAAG,YAAY,CAAC,QAAQ,CAAA;IACpC,OAAO,CAAC,GAAG,CAAC,qBAAqB,CAAC,CAAA;IAElC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAA;IACjD,IAAI,CAAC;QACH,MAAM,IAAI,CAAC,KAAK,EAAE,CAAA;IACpB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CAAC,0BAA0B,CAAC,CAAA;QACzC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAA;QACpB,YAAY,GAAG,YAAY,CAAC,KAAK,CAAA;QACjC,mBAAmB,CAAC,IAAI,CAAC,CAAA;QACzB,OAAM;IACR,CAAC;IAED,oBAAoB,CAAC,IAAI,CAAC,CAAA;IAE1B,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAA;IAC/B,YAAY,GAAG,YAAY,CAAC,OAAO,CAAA;AACrC,CAAC"} \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/universal/url.d.ts b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/universal/url.d.ts index aa893e7838..7ceb994694 100644 --- a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/universal/url.d.ts +++ b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/universal/url.d.ts @@ -1 +1,2 @@ -export declare function stripTrailingSlash(url?: string): string | undefined; +export declare function stripTrailingSlash(url: string): string; +export declare function stripTrailingSlash(url: undefined): undefined; diff --git a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/universal/url.js.map b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/universal/url.js.map index a7bad96020..e236f1b977 100644 --- a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/universal/url.js.map +++ b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/universal/url.js.map @@ -1 +1 @@ -{"version":3,"file":"url.js","sourceRoot":"","sources":["../../universal/url.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,kBAAkB,CAAC,GAAY;IAC3C,OAAO,GAAG,aAAH,GAAG,uBAAH,GAAG,CAAE,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;AACnC,CAAC"} \ No newline at end of file +{"version":3,"file":"url.js","sourceRoot":"","sources":["../../universal/url.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,kBAAkB,CAAC,GAAY;IAC3C,OAAO,GAAG,aAAH,GAAG,uBAAH,GAAG,CAAE,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;AACnC,CAAC"} \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/env/index.ts b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/env/index.ts new file mode 100644 index 0000000000..4c783125c1 --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/env/index.ts @@ -0,0 +1,11 @@ +import * as z from 'zod' + +// PUBLIC API +export function defineEnvValidationSchema>( + schema: Schema, +): Schema { + return schema +} + +// PRIVATE API (SDK, Vite config) +export { ensureEnvSchema } from './validation.js' diff --git a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/env/validation.ts b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/env/validation.ts new file mode 100644 index 0000000000..a5bc334754 --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/env/validation.ts @@ -0,0 +1,25 @@ +import * as z from 'zod' + +const redColor = '\x1b[31m' + +export function ensureEnvSchema( + data: unknown, + schema: Schema +): z.infer { + try { + return schema.parse(data) + } catch (e) { + if (e instanceof z.ZodError) { + const errorOutput = ['', 'โ•โ• Env vars validation failed โ•โ•', ''] + for (const error of e.errors) { + errorOutput.push(` - ${error.message}`) + } + errorOutput.push('') + errorOutput.push('โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•') + console.error(redColor, errorOutput.join('\n')) + throw new Error('Error parsing environment variables') + } else { + throw e + } + } +} diff --git a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/package.json b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/package.json index ce5f990604..a78accf43a 100644 --- a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/package.json +++ b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/package.json @@ -8,7 +8,6 @@ "axios": "^1.4.0", "express": "~4.21.0", "jsdom": "^21.1.1", - "lodash.merge": "^4.6.2", "mitt": "3.0.0", "msw": "^1.1.0", "pg-boss": "^8.4.2", @@ -17,7 +16,8 @@ "react-hook-form": "^7.45.4", "react-router-dom": "^6.26.2", "superjson": "^2.2.1", - "vitest": "^1.2.1" + "vitest": "^1.2.1", + "zod": "^3.23.8" }, "devDependencies": { "@tsconfig/node18": "latest", @@ -52,6 +52,7 @@ "./core/storage": "./dist/core/storage.js", "./dev": "./dist/dev/index.js", "./entities": "./dist/entities/index.js", + "./env": "./dist/env/index.js", "./ext-src/*": "./dist/ext-src/*.js", "./operations": "./dist/operations/index.js", "./operations/*": "./dist/operations/*", diff --git a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/server/HttpError.ts b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/server/HttpError.ts index d05a2c841b..bce964712d 100644 --- a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/server/HttpError.ts +++ b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/server/HttpError.ts @@ -2,7 +2,12 @@ export class HttpError extends Error { public statusCode: number public data: unknown - constructor (statusCode: number, message?: string, data?: Record, options?: ErrorOptions) { + constructor( + statusCode: number, + message?: string, + data?: Record, + options?: ErrorOptions + ) { super(message, options) if (Error.captureStackTrace) { @@ -11,7 +16,9 @@ export class HttpError extends Error { this.name = this.constructor.name - if (!(Number.isInteger(statusCode) && statusCode >= 400 && statusCode < 600)) { + if ( + !(Number.isInteger(statusCode) && statusCode >= 400 && statusCode < 600) + ) { throw new Error('statusCode has to be integer in range [400, 600).') } this.statusCode = statusCode diff --git a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/server/config.ts b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/server/config.ts index 1dc9bafb93..3a000a897f 100644 --- a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/server/config.ts +++ b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/server/config.ts @@ -1,73 +1,36 @@ -import merge from 'lodash.merge' +import { env } from './env.js' +import { stripTrailingSlash } from '../universal/url.js' -import { stripTrailingSlash } from "../universal/url.js"; +type NodeEnv = typeof env.NODE_ENV -const nodeEnv = process.env.NODE_ENV ?? 'development' - -// TODO: -// - Use dotenv library to consume env vars from a file. -// - Use convict library to define schema and validate env vars. -// https://codingsans.com/blog/node-config-best-practices - -type BaseConfig = { - allowedCORSOrigins: string | string[]; -} - -type CommonConfig = BaseConfig & { - env: string; +type Config = { + env: NodeEnv; isDevelopment: boolean; port: number; - databaseUrl: string | undefined; -} - -type EnvConfig = BaseConfig & { + databaseUrl: string; frontendUrl: string; serverUrl: string; + allowedCORSOrigins: string | string[]; } -type Config = CommonConfig & EnvConfig +const frontendUrl = stripTrailingSlash(env.WASP_WEB_CLIENT_URL) +const serverUrl = stripTrailingSlash(env.WASP_SERVER_URL) -const config: { - all: CommonConfig, - development: EnvConfig, - production: EnvConfig, -} = { - all: { - env: nodeEnv, - isDevelopment: nodeEnv === 'development', - port: process.env.PORT ? parseInt(process.env.PORT) : 3001, - databaseUrl: process.env.DATABASE_URL, - allowedCORSOrigins: [], - }, - development: getDevelopmentConfig(), - production: getProductionConfig(), +const allowedCORSOriginsPerEnv: Record = { + development: '*', + production: [frontendUrl] } - -const resolvedConfig: Config = merge(config.all, config[nodeEnv]) -// PUBLIC API -export default resolvedConfig - -function getDevelopmentConfig(): EnvConfig { - const frontendUrl = stripTrailingSlash(process.env.WASP_WEB_CLIENT_URL ?? 'http://localhost:3000/'); - const serverUrl = stripTrailingSlash(process.env.WASP_SERVER_URL ?? 'http://localhost:3001'); - return { - // @ts-ignore - frontendUrl, - // @ts-ignore - serverUrl, - allowedCORSOrigins: '*', - } +const allowedCORSOrigins = allowedCORSOriginsPerEnv[env.NODE_ENV] + +const config: Config = { + frontendUrl, + serverUrl, + allowedCORSOrigins, + env: env.NODE_ENV, + isDevelopment: env.NODE_ENV === 'development', + port: env.PORT, + databaseUrl: env.DATABASE_URL, } -function getProductionConfig(): EnvConfig { - const frontendUrl = stripTrailingSlash(process.env.WASP_WEB_CLIENT_URL); - const serverUrl = stripTrailingSlash(process.env.WASP_SERVER_URL); - return { - // @ts-ignore - frontendUrl, - // @ts-ignore - serverUrl, - // @ts-ignore - allowedCORSOrigins: [frontendUrl], - } -} +// PUBLIC API +export default config diff --git a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/server/env.ts b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/server/env.ts new file mode 100644 index 0000000000..c1bd8f5c0f --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/server/env.ts @@ -0,0 +1,65 @@ +import * as z from 'zod' + +import { ensureEnvSchema } from '../env/validation.js' + +const userServerEnvSchema = z.object({}) + +const waspServerCommonSchema = z.object({ + PORT: z.coerce.number().default(3001), + DATABASE_URL: z.string({ + required_error: 'DATABASE_URL is required', + }), + PG_BOSS_NEW_OPTIONS: z.string().optional(), + SKIP_EMAIL_VERIFICATION_IN_DEV: z + .enum(['true', 'false'], { + message: 'SKIP_EMAIL_VERIFICATION_IN_DEV must be either "true" or "false"', + }) + .transform((value) => value === 'true') + .default('false'), +}) + +const serverUrlSchema = z + .string({ + required_error: 'WASP_SERVER_URL is required', + }) + .url({ + message: 'WASP_SERVER_URL must be a valid URL', + }) + +const clientUrlSchema = z + .string({ + required_error: 'WASP_WEB_CLIENT_URL is required', + }) + .url({ + message: 'WASP_WEB_CLIENT_URL must be a valid URL', + }) + + +// In development, we provide default values for some environment variables +// to make the development process easier. +const serverDevSchema = z.object({ + NODE_ENV: z.literal('development'), + WASP_SERVER_URL: serverUrlSchema + .default('http://localhost:3001'), + WASP_WEB_CLIENT_URL: clientUrlSchema + .default('http://localhost:3000/'), +}) + +const serverProdSchema = z.object({ + NODE_ENV: z.literal('production'), + WASP_SERVER_URL: serverUrlSchema, + WASP_WEB_CLIENT_URL: clientUrlSchema, +}) + +const serverCommonSchema = userServerEnvSchema.merge(waspServerCommonSchema) +const serverEnvSchema = z.discriminatedUnion('NODE_ENV', [ + serverDevSchema.merge(serverCommonSchema), + serverProdSchema.merge(serverCommonSchema) +]) + +// PUBLIC API +export const env = ensureEnvSchema(process.env, serverEnvSchema) + +function getRequiredEnvVarErrorMessage(featureName: string, envVarName: string) { + return `${envVarName} is required when using ${featureName}` +} diff --git a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/server/index.ts b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/server/index.ts index 535077900b..0f95deb90a 100644 --- a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/server/index.ts +++ b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/server/index.ts @@ -10,6 +10,8 @@ export { type ServerSetupFn } from './types/index.js' export { HttpError } from './HttpError.js' // PUBLIC API export { MiddlewareConfigFn } from './middleware/index.js' +// PUBLIC API +export { env } from './env.js' // PUBLIC API export type DbSeedFn = (prisma: PrismaClient) => Promise diff --git a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/server/jobs/core/pgBoss/pgBoss.ts b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/server/jobs/core/pgBoss/pgBoss.ts index a48992aad0..eed0cf23cc 100644 --- a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/server/jobs/core/pgBoss/pgBoss.ts +++ b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/server/jobs/core/pgBoss/pgBoss.ts @@ -1,5 +1,5 @@ import PgBoss from 'pg-boss' -import { config } from 'wasp/server' +import { config, env } from '../../../index.js' const boss = createPgBoss() @@ -9,9 +9,9 @@ function createPgBoss() { } // Add an escape hatch for advanced configuration of pg-boss to overwrite our defaults. - if (process.env.PG_BOSS_NEW_OPTIONS) { + if (env.PG_BOSS_NEW_OPTIONS) { try { - pgBossNewOptions = JSON.parse(process.env.PG_BOSS_NEW_OPTIONS) + pgBossNewOptions = JSON.parse(env.PG_BOSS_NEW_OPTIONS) } catch { console.error( 'Environment variable PG_BOSS_NEW_OPTIONS was not parsable by JSON.parse()!' diff --git a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/universal/url.ts b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/universal/url.ts index d21c06c65c..126678dac9 100644 --- a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/universal/url.ts +++ b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/universal/url.ts @@ -1,3 +1,5 @@ +export function stripTrailingSlash(url: string): string +export function stripTrailingSlash(url: undefined): undefined export function stripTrailingSlash(url?: string): string | undefined { return url?.replace(/\/$/, ""); } diff --git a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/server/package.json b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/server/package.json index 7a57413e11..51ec87354a 100644 --- a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/server/package.json +++ b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/server/package.json @@ -34,9 +34,8 @@ "bundle-and-start": "npm run bundle && npm run start", "db-migrate-prod": "prisma migrate deploy --schema=../db/schema.prisma", "db-seed": "npm run bundle && node --enable-source-maps -r dotenv/config bundle/dbSeed.js", - "start": "npm run validate-env && node --enable-source-maps -r dotenv/config bundle/server.js", + "start": "node --enable-source-maps -r dotenv/config bundle/server.js", "start-production": "NODE_ENV=production npm run start", - "validate-env": "node -r dotenv/config ./scripts/validate-env.mjs", "watch": "nodemon --exec 'npm run bundle-and-start || exit 1'" }, "type": "module", diff --git a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/server/scripts/validate-env.mjs b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/server/scripts/validate-env.mjs deleted file mode 100644 index f6e50aceca..0000000000 --- a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/server/scripts/validate-env.mjs +++ /dev/null @@ -1,5 +0,0 @@ -import { throwIfNotValidAbsoluteURL } from 'wasp/universal/validators'; - -console.info("๐Ÿ” Validating environment variables..."); -throwIfNotValidAbsoluteURL(process.env.WASP_WEB_CLIENT_URL, 'Environment variable WASP_WEB_CLIENT_URL'); -throwIfNotValidAbsoluteURL(process.env.WASP_SERVER_URL, 'Environment variable WASP_SERVER_URL'); diff --git a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/server/src/polyfill.ts b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/server/src/polyfill.ts index 7931d12eb0..9279daf6da 100644 --- a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/server/src/polyfill.ts +++ b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/server/src/polyfill.ts @@ -1,11 +1,10 @@ // This is a polyfill for Node.js 18 webcrypto API so Lucia can use it // for random number generation. -import { webcrypto } from "node:crypto"; +import { webcrypto } from 'node:crypto' // NOTE: node < 19 doesn't have Crypto API, which we need for Lucia, so we apply the polyfill if Crypto API is not defined. -if (typeof globalThis.crypto === "undefined") { +if (typeof globalThis.crypto === 'undefined') { // @ts-ignore - globalThis.crypto = webcrypto as Crypto; + globalThis.crypto = webcrypto as Crypto } - diff --git a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/web-app/package.json b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/web-app/package.json index cd8a6c2ab1..ab966cfd02 100644 --- a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/web-app/package.json +++ b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/web-app/package.json @@ -18,8 +18,7 @@ "devDependencies": { "@tsconfig/vite-react": "^2.0.0", "@types/react-dom": "^18.0.11", - "@vitejs/plugin-react": "^4.2.1", - "dotenv": "^16.0.3" + "@vitejs/plugin-react": "^4.2.1" }, "engineStrict": true, "engines": { @@ -28,9 +27,8 @@ "name": "waspJob", "private": true, "scripts": { - "build": "npm run validate-env && tsc && vite build", - "start": "npm run validate-env && vite", - "validate-env": "node -r dotenv/config ./scripts/validate-env.mjs" + "build": "tsc && vite build", + "start": "vite" }, "type": "module", "version": "0.0.0" diff --git a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/web-app/scripts/validate-env.mjs b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/web-app/scripts/validate-env.mjs deleted file mode 100644 index 18ee507c9e..0000000000 --- a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/web-app/scripts/validate-env.mjs +++ /dev/null @@ -1,4 +0,0 @@ -import { throwIfNotValidAbsoluteURL } from 'wasp/universal/validators'; - -console.info("๐Ÿ” Validating environment variables..."); -throwIfNotValidAbsoluteURL(process.env.REACT_APP_API_URL, 'Environemnt variable REACT_APP_API_URL'); diff --git a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/web-app/src/components/DefaultRootErrorBoundary.tsx b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/web-app/src/components/DefaultRootErrorBoundary.tsx index 7af24e15d6..1e931935e6 100644 --- a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/web-app/src/components/DefaultRootErrorBoundary.tsx +++ b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/web-app/src/components/DefaultRootErrorBoundary.tsx @@ -7,7 +7,10 @@ export function DefaultRootErrorBoundary() { console.error(error) return ( -
There was an error rendering this page. Check the browser console for more information.
+
+ There was an error rendering this page. Check the browser console for + more information. +
) } diff --git a/waspc/e2e-test/test-outputs/waspMigrate-golden/files.manifest b/waspc/e2e-test/test-outputs/waspMigrate-golden/files.manifest index 151da17c16..2208175e04 100644 --- a/waspc/e2e-test/test-outputs/waspMigrate-golden/files.manifest +++ b/waspc/e2e-test/test-outputs/waspMigrate-golden/files.manifest @@ -10,6 +10,7 @@ waspMigrate/.wasp/out/installedNpmDepsLog.json waspMigrate/.wasp/out/sdk/wasp/api/events.ts waspMigrate/.wasp/out/sdk/wasp/api/index.ts waspMigrate/.wasp/out/sdk/wasp/client/config.ts +waspMigrate/.wasp/out/sdk/wasp/client/env.ts waspMigrate/.wasp/out/sdk/wasp/client/index.ts waspMigrate/.wasp/out/sdk/wasp/client/operations/actions/core.ts waspMigrate/.wasp/out/sdk/wasp/client/operations/actions/index.ts @@ -39,6 +40,9 @@ waspMigrate/.wasp/out/sdk/wasp/dist/api/index.js.map waspMigrate/.wasp/out/sdk/wasp/dist/client/config.d.ts waspMigrate/.wasp/out/sdk/wasp/dist/client/config.js waspMigrate/.wasp/out/sdk/wasp/dist/client/config.js.map +waspMigrate/.wasp/out/sdk/wasp/dist/client/env.d.ts +waspMigrate/.wasp/out/sdk/wasp/dist/client/env.js +waspMigrate/.wasp/out/sdk/wasp/dist/client/env.js.map waspMigrate/.wasp/out/sdk/wasp/dist/client/index.d.ts waspMigrate/.wasp/out/sdk/wasp/dist/client/index.js waspMigrate/.wasp/out/sdk/wasp/dist/client/index.js.map @@ -102,6 +106,12 @@ waspMigrate/.wasp/out/sdk/wasp/dist/dev/index.js.map waspMigrate/.wasp/out/sdk/wasp/dist/entities/index.d.ts waspMigrate/.wasp/out/sdk/wasp/dist/entities/index.js waspMigrate/.wasp/out/sdk/wasp/dist/entities/index.js.map +waspMigrate/.wasp/out/sdk/wasp/dist/env/index.d.ts +waspMigrate/.wasp/out/sdk/wasp/dist/env/index.js +waspMigrate/.wasp/out/sdk/wasp/dist/env/index.js.map +waspMigrate/.wasp/out/sdk/wasp/dist/env/validation.d.ts +waspMigrate/.wasp/out/sdk/wasp/dist/env/validation.js +waspMigrate/.wasp/out/sdk/wasp/dist/env/validation.js.map waspMigrate/.wasp/out/sdk/wasp/dist/ext-src/MainPage.d.ts waspMigrate/.wasp/out/sdk/wasp/dist/ext-src/MainPage.jsx waspMigrate/.wasp/out/sdk/wasp/dist/ext-src/MainPage.jsx.map @@ -123,6 +133,9 @@ waspMigrate/.wasp/out/sdk/wasp/dist/server/config.js.map waspMigrate/.wasp/out/sdk/wasp/dist/server/dbClient.d.ts waspMigrate/.wasp/out/sdk/wasp/dist/server/dbClient.js waspMigrate/.wasp/out/sdk/wasp/dist/server/dbClient.js.map +waspMigrate/.wasp/out/sdk/wasp/dist/server/env.d.ts +waspMigrate/.wasp/out/sdk/wasp/dist/server/env.js +waspMigrate/.wasp/out/sdk/wasp/dist/server/env.js.map waspMigrate/.wasp/out/sdk/wasp/dist/server/index.d.ts waspMigrate/.wasp/out/sdk/wasp/dist/server/index.js waspMigrate/.wasp/out/sdk/wasp/dist/server/index.js.map @@ -169,6 +182,8 @@ waspMigrate/.wasp/out/sdk/wasp/dist/universal/validators.d.ts waspMigrate/.wasp/out/sdk/wasp/dist/universal/validators.js waspMigrate/.wasp/out/sdk/wasp/dist/universal/validators.js.map waspMigrate/.wasp/out/sdk/wasp/entities/index.ts +waspMigrate/.wasp/out/sdk/wasp/env/index.ts +waspMigrate/.wasp/out/sdk/wasp/env/validation.ts waspMigrate/.wasp/out/sdk/wasp/ext-src/Main.css waspMigrate/.wasp/out/sdk/wasp/ext-src/MainPage.jsx waspMigrate/.wasp/out/sdk/wasp/ext-src/vite-env.d.ts @@ -181,6 +196,7 @@ waspMigrate/.wasp/out/sdk/wasp/server/_types/serialization.ts waspMigrate/.wasp/out/sdk/wasp/server/_types/taggedEntities.ts waspMigrate/.wasp/out/sdk/wasp/server/config.ts waspMigrate/.wasp/out/sdk/wasp/server/dbClient.ts +waspMigrate/.wasp/out/sdk/wasp/server/env.ts waspMigrate/.wasp/out/sdk/wasp/server/index.ts waspMigrate/.wasp/out/sdk/wasp/server/middleware/globalMiddleware.ts waspMigrate/.wasp/out/sdk/wasp/server/middleware/index.ts @@ -204,7 +220,6 @@ waspMigrate/.wasp/out/server/README.md waspMigrate/.wasp/out/server/nodemon.json waspMigrate/.wasp/out/server/package.json waspMigrate/.wasp/out/server/rollup.config.js -waspMigrate/.wasp/out/server/scripts/validate-env.mjs waspMigrate/.wasp/out/server/src/app.js waspMigrate/.wasp/out/server/src/middleware/globalMiddleware.ts waspMigrate/.wasp/out/server/src/middleware/index.ts @@ -223,7 +238,6 @@ waspMigrate/.wasp/out/web-app/package.json waspMigrate/.wasp/out/web-app/public/.gitkeep waspMigrate/.wasp/out/web-app/public/favicon.ico waspMigrate/.wasp/out/web-app/public/manifest.json -waspMigrate/.wasp/out/web-app/scripts/validate-env.mjs waspMigrate/.wasp/out/web-app/src/components/DefaultRootErrorBoundary.tsx waspMigrate/.wasp/out/web-app/src/components/FullPageWrapper.tsx waspMigrate/.wasp/out/web-app/src/components/Loader.module.css diff --git a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/.waspchecksums b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/.waspchecksums index 8ec88ca7fa..797f20f58d 100644 --- a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/.waspchecksums +++ b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/.waspchecksums @@ -18,14 +18,21 @@ "file", "../out/sdk/wasp/client/config.ts" ], - "d94e8bee5fd8f6793b511652b9ee558e57f4913595763137a5385cd929648168" + "4c5835ad64352cc87a4a17e41fd70333bc0d1eedd7339d7ad3d3507cdb64038d" + ], + [ + [ + "file", + "../out/sdk/wasp/client/env.ts" + ], + "2a3602d40b299fbfa1a6275fe67828d5653d9f3265ba8204b75178aa41dcbae0" ], [ [ "file", "../out/sdk/wasp/client/index.ts" ], - "2b3651e7040a63cfd6a271c2aa89f21cf01170e0abeb6c5ab74adde324852fe4" + "b55e5c34c2fdb50e608bcb4331fa2e67724f1fbfb21e1b93680b0f5fe2b7005a" ], [ [ @@ -167,6 +174,20 @@ ], "c5077f0d9a9224986a1c42b54de449e48af1f2b9ecc8778eb44cd55ebd9d23a7" ], + [ + [ + "file", + "../out/sdk/wasp/env/index.ts" + ], + "d92b4832e9d4249ae5b1f00c0f9760d6841aa44bc199550414793a1d57a6845e" + ], + [ + [ + "file", + "../out/sdk/wasp/env/validation.ts" + ], + "e22b2fb8931275d3b1bd3176cbdbf753d22fc61887ded0e93cd8febf1b97d260" + ], [ [ "file", @@ -200,7 +221,7 @@ "file", "../out/sdk/wasp/package.json" ], - "6f1afd30c951e1f49740256440cd626c9cb521cf0822d93d2d2a5d63d8719d7b" + "b991fb5ade61a662dea3f21bf09aa30379f30ea76204a369c0800b1413256a64" ], [ [ @@ -214,7 +235,7 @@ "file", "../out/sdk/wasp/server/HttpError.ts" ], - "f375fb45eeb16381b8cc575503db302c005ddaa1b35d837ca0ea043fe8ec6ccb" + "9c1b2449d8e9d68470c6014054a3c3074069a104ddb291b9089c30d624a2d9d9" ], [ [ @@ -242,7 +263,7 @@ "file", "../out/sdk/wasp/server/config.ts" ], - "5dd35660e0969defaa3c180179919f4922ab7bc041746872dff7f2280c1872ae" + "8431eaf0332cdbdd2f36b4c232b4b6b65fd19bad58fe51922f5b2662f22fc8ad" ], [ [ @@ -251,12 +272,19 @@ ], "c5f88f028ad62b4a2fd5d1206aa72165f74c6f46f96250512af6ae55a5354020" ], + [ + [ + "file", + "../out/sdk/wasp/server/env.ts" + ], + "f3cab17868417ffd4432dc4efecde72ccaaf099a79ddc9194835f3608d123b63" + ], [ [ "file", "../out/sdk/wasp/server/index.ts" ], - "51c3e0802764f72b88f6daaf148dba514ae817e233a40cc9b92ec91784f71f98" + "a8d2ffa7dfd0a13f7fb6135193bcdac1f860603bdd0ddc2843033fd4f6f6151f" ], [ [ @@ -354,7 +382,7 @@ "file", "../out/sdk/wasp/universal/url.ts" ], - "8dc6e044a1a231b796465d94985ca47c5efd42a5d411b407a7d83a61ebae4b6d" + "58ff4ad8ffc79264d7215461571d909f3c5fb177ff32c67058d1da9cd4115d6e" ], [ [ @@ -382,7 +410,7 @@ "file", "Dockerfile" ], - "db2dae70d212749a7e758117e55f00968f84974d8307dc7d33154bf1429486c2" + "94b45c62cbed2fcb04c69643da5047c27c5eb0acb78b7c164d083a5c30e7c1d1" ], [ [ @@ -431,7 +459,7 @@ "file", "server/package.json" ], - "00ae054bb5597a45cc4e5277f88d24f4399fccabaa3614bf6ea89424fcd23480" + "ddda728300aaca5ccbd8342479cede7930cd5841c01f048415da8172314a1e0a" ], [ [ @@ -440,13 +468,6 @@ ], "c1beb8264f11898364288d73b16f08d1923bac5f70038d9827978deb5b58a2da" ], - [ - [ - "file", - "server/scripts/validate-env.mjs" - ], - "100177b4326ccab7362eff378315d532ad1cc17cd28d1ed5978cb167fd627746" - ], [ [ "file", @@ -480,7 +501,7 @@ "file", "server/src/polyfill.ts" ], - "1149661e0aa7228b184bb2c04ac63232a6523b09b33829db6977f79daba3fd8d" + "f62c2088c8ebf5889ab479bb937ffd8f0f96d3e85483a7b088edebb5ae598dca" ], [ [ @@ -557,7 +578,7 @@ "file", "web-app/package.json" ], - "c87e74f65917573dad902b8108ddce459af520bf7d3deb8cad6794fafa86749b" + "5ad3b71ea4267ee540ac1eb134ca61adf85869cb8ac86471e880725c3206b528" ], [ [ @@ -580,19 +601,12 @@ ], "c91b7ea515a4889c4abfd355500c8260210602093a94c0beee302450272c3737" ], - [ - [ - "file", - "web-app/scripts/validate-env.mjs" - ], - "a9a3a7eb6bc3ead49d8e3850a70737c93c789098beb3b40196bf145fd38893cd" - ], [ [ "file", "web-app/src/components/DefaultRootErrorBoundary.tsx" ], - "4a141a28ca7aed9daddd75c156f7bae5eec50f3e5a85b6a43a28fa2648f34c44" + "73bbe8a2eb141e523c6b8169c0831e594b4ab6c7207f107d6d20c1561b44373a" ], [ [ diff --git a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/Dockerfile b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/Dockerfile index 76baedb0e0..9d828590a3 100644 --- a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/Dockerfile +++ b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/Dockerfile @@ -52,15 +52,15 @@ WORKDIR /app # Copying the top level 'node_modules' because it contains the Prisma packages # necessary for migrating the database. COPY --from=server-builder /app/node_modules ./node_modules -# Copying the SDK because 'validate-env.mjs' executes independent of the bundle +# Copying the SDK because the server bundle doesn't bundle the SDK # and references the 'wasp' package. COPY --from=server-builder /app/.wasp/out/sdk .wasp/out/sdk -# Copying 'server/node_modules' because 'validate-env.mjs' executes independent -# of the bundle and references the dotenv package. +# Copying 'server/node_modules' because we require dotenv package to +# load environment variables +# TODO: replace dotenv with native Node.js environment variable loading COPY --from=server-builder /app/.wasp/build/server/node_modules .wasp/build/server/node_modules COPY --from=server-builder /app/.wasp/build/server/bundle .wasp/build/server/bundle COPY --from=server-builder /app/.wasp/build/server/package*.json .wasp/build/server/ -COPY --from=server-builder /app/.wasp/build/server/scripts .wasp/build/server/scripts COPY db/ .wasp/build/db/ EXPOSE ${PORT} WORKDIR /app/.wasp/build/server diff --git a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/installedNpmDepsLog.json b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/installedNpmDepsLog.json index a21ddcd4a1..d9bee4d2fa 100644 --- a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/installedNpmDepsLog.json +++ b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/installedNpmDepsLog.json @@ -1 +1 @@ -{"_waspSdkNpmDeps":{"dependencies":[{"name":"@prisma/client","version":"5.19.1"},{"name":"prisma","version":"5.19.1"},{"name":"@tanstack/react-query","version":"^4.29.0"},{"name":"axios","version":"^1.4.0"},{"name":"express","version":"~4.21.0"},{"name":"mitt","version":"3.0.0"},{"name":"react","version":"^18.2.0"},{"name":"lodash.merge","version":"^4.6.2"},{"name":"react-router-dom","version":"^6.26.2"},{"name":"react-hook-form","version":"^7.45.4"},{"name":"superjson","version":"^2.2.1"},{"name":"vitest","version":"^1.2.1"},{"name":"@vitest/ui","version":"^1.2.1"},{"name":"jsdom","version":"^21.1.1"},{"name":"@testing-library/react","version":"^14.1.2"},{"name":"@testing-library/jest-dom","version":"^6.3.0"},{"name":"msw","version":"^1.1.0"}],"devDependencies":[{"name":"@tsconfig/node18","version":"latest"},{"name":"@types/express-serve-static-core","version":"^4.17.13"}]},"_userNpmDeps":{"userDependencies":[{"name":"react","version":"^18.2.0"},{"name":"react-dom","version":"^18.2.0"},{"name":"react-router-dom","version":"^6.26.2"},{"name":"wasp","version":"file:.wasp/out/sdk/wasp"}],"userDevDependencies":[{"name":"@types/react","version":"^18.0.37"},{"name":"prisma","version":"5.19.1"},{"name":"typescript","version":"^5.1.0"},{"name":"vite","version":"^4.3.9"}]},"_waspFrameworkNpmDeps":{"npmDepsForWebApp":{"dependencies":[{"name":"@tanstack/react-query","version":"^4.29.0"},{"name":"axios","version":"^1.4.0"}],"devDependencies":[{"name":"@tsconfig/vite-react","version":"^2.0.0"},{"name":"@types/react-dom","version":"^18.0.11"},{"name":"@vitejs/plugin-react","version":"^4.2.1"},{"name":"dotenv","version":"^16.0.3"}]},"npmDepsForServer":{"dependencies":[{"name":"cookie-parser","version":"~1.4.6"},{"name":"cors","version":"^2.8.5"},{"name":"dotenv","version":"^16.0.2"},{"name":"express","version":"~4.21.0"},{"name":"helmet","version":"^6.0.0"},{"name":"morgan","version":"~1.10.0"},{"name":"superjson","version":"^2.2.1"}],"devDependencies":[{"name":"@tsconfig/node18","version":"latest"},{"name":"@types/cors","version":"^2.8.5"},{"name":"@types/express","version":"^4.17.13"},{"name":"@types/express-serve-static-core","version":"^4.17.13"},{"name":"@types/node","version":"^18.0.0"},{"name":"nodemon","version":"^2.0.19"},{"name":"rollup","version":"^4.9.6"},{"name":"rollup-plugin-esbuild","version":"^6.1.1"}]}}} \ No newline at end of file +{"_waspSdkNpmDeps":{"dependencies":[{"name":"@prisma/client","version":"5.19.1"},{"name":"prisma","version":"5.19.1"},{"name":"@tanstack/react-query","version":"^4.29.0"},{"name":"axios","version":"^1.4.0"},{"name":"express","version":"~4.21.0"},{"name":"mitt","version":"3.0.0"},{"name":"react","version":"^18.2.0"},{"name":"react-router-dom","version":"^6.26.2"},{"name":"react-hook-form","version":"^7.45.4"},{"name":"superjson","version":"^2.2.1"},{"name":"vitest","version":"^1.2.1"},{"name":"@vitest/ui","version":"^1.2.1"},{"name":"jsdom","version":"^21.1.1"},{"name":"@testing-library/react","version":"^14.1.2"},{"name":"@testing-library/jest-dom","version":"^6.3.0"},{"name":"msw","version":"^1.1.0"},{"name":"zod","version":"^3.23.8"}],"devDependencies":[{"name":"@tsconfig/node18","version":"latest"},{"name":"@types/express-serve-static-core","version":"^4.17.13"}]},"_userNpmDeps":{"userDependencies":[{"name":"react","version":"^18.2.0"},{"name":"react-dom","version":"^18.2.0"},{"name":"react-router-dom","version":"^6.26.2"},{"name":"wasp","version":"file:.wasp/out/sdk/wasp"}],"userDevDependencies":[{"name":"@types/react","version":"^18.0.37"},{"name":"prisma","version":"5.19.1"},{"name":"typescript","version":"^5.1.0"},{"name":"vite","version":"^4.3.9"}]},"_waspFrameworkNpmDeps":{"npmDepsForWebApp":{"dependencies":[{"name":"@tanstack/react-query","version":"^4.29.0"},{"name":"axios","version":"^1.4.0"}],"devDependencies":[{"name":"@tsconfig/vite-react","version":"^2.0.0"},{"name":"@types/react-dom","version":"^18.0.11"},{"name":"@vitejs/plugin-react","version":"^4.2.1"}]},"npmDepsForServer":{"dependencies":[{"name":"cookie-parser","version":"~1.4.6"},{"name":"cors","version":"^2.8.5"},{"name":"dotenv","version":"^16.0.2"},{"name":"express","version":"~4.21.0"},{"name":"helmet","version":"^6.0.0"},{"name":"morgan","version":"~1.10.0"},{"name":"superjson","version":"^2.2.1"}],"devDependencies":[{"name":"@tsconfig/node18","version":"latest"},{"name":"@types/cors","version":"^2.8.5"},{"name":"@types/express","version":"^4.17.13"},{"name":"@types/express-serve-static-core","version":"^4.17.13"},{"name":"@types/node","version":"^18.0.0"},{"name":"nodemon","version":"^2.0.19"},{"name":"rollup","version":"^4.9.6"},{"name":"rollup-plugin-esbuild","version":"^6.1.1"}]}}} \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/client/config.ts b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/client/config.ts index 82b0f80b6a..fff77cb01c 100644 --- a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/client/config.ts +++ b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/client/config.ts @@ -1,6 +1,7 @@ import { stripTrailingSlash } from '../universal/url.js' +import { env } from './env.js' -const apiUrl = stripTrailingSlash(import.meta.env.REACT_APP_API_URL) || 'http://localhost:3001'; +const apiUrl = stripTrailingSlash(env.REACT_APP_API_URL) // PUBLIC API export type ClientConfig = { diff --git a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/client/env.ts b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/client/env.ts new file mode 100644 index 0000000000..8ddab287f7 --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/client/env.ts @@ -0,0 +1,18 @@ +import * as z from 'zod' + +import { ensureEnvSchema } from '../env/validation.js' + +const userClientEnvSchema = z.object({}) + +const waspClientEnvSchema = z.object({ + REACT_APP_API_URL: z + .string({ + required_error: 'REACT_APP_API_URL is required', + }) + .default('http://localhost:3001') +}) + +const clientEnvSchema = userClientEnvSchema.merge(waspClientEnvSchema) + +// PUBLIC API +export const env = ensureEnvSchema(import.meta.env, clientEnvSchema) diff --git a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/client/index.ts b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/client/index.ts index cfce564c4d..859f8656b6 100644 --- a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/client/index.ts +++ b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/client/index.ts @@ -11,4 +11,7 @@ export enum HttpMethod { export type Route = { method: HttpMethod; path: string } // PUBLIC API -export { config, ClientConfig } from './config' +export { config, ClientConfig } from './config.js' + +// PUBLIC API +export { env } from './env.js' diff --git a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/client/config.js b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/client/config.js index 1dffbd68bc..7e6149f386 100644 --- a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/client/config.js +++ b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/client/config.js @@ -1,5 +1,6 @@ import { stripTrailingSlash } from '../universal/url.js'; -const apiUrl = stripTrailingSlash(import.meta.env.REACT_APP_API_URL) || 'http://localhost:3001'; +import { env } from './env.js'; +const apiUrl = stripTrailingSlash(env.REACT_APP_API_URL); // PUBLIC API export const config = { apiUrl, diff --git a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/client/config.js.map b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/client/config.js.map index d26d37022b..f9e186b838 100644 --- a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/client/config.js.map +++ b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/client/config.js.map @@ -1 +1 @@ -{"version":3,"file":"config.js","sourceRoot":"","sources":["../../client/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAA;AAExD,MAAM,MAAM,GAAG,kBAAkB,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,iBAAiB,CAAC,IAAI,uBAAuB,CAAC;AAOhG,aAAa;AACb,MAAM,CAAC,MAAM,MAAM,GAAiB;IAClC,MAAM;CACP,CAAA"} \ No newline at end of file +{"version":3,"file":"config.js","sourceRoot":"","sources":["../../client/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAA;AACxD,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAA;AAE9B,MAAM,MAAM,GAAG,kBAAkB,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAA;AAOxD,aAAa;AACb,MAAM,CAAC,MAAM,MAAM,GAAiB;IAClC,MAAM;CACP,CAAA"} \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/client/env.d.ts b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/client/env.d.ts new file mode 100644 index 0000000000..5a9417a810 --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/client/env.d.ts @@ -0,0 +1,3 @@ +export declare const env: { + REACT_APP_API_URL: string; +}; diff --git a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/client/env.js b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/client/env.js new file mode 100644 index 0000000000..91164c3768 --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/client/env.js @@ -0,0 +1,14 @@ +import * as z from 'zod'; +import { ensureEnvSchema } from '../env/validation.js'; +const userClientEnvSchema = z.object({}); +const waspClientEnvSchema = z.object({ + REACT_APP_API_URL: z + .string({ + required_error: 'REACT_APP_API_URL is required', + }) + .default('http://localhost:3001') +}); +const clientEnvSchema = userClientEnvSchema.merge(waspClientEnvSchema); +// PUBLIC API +export const env = ensureEnvSchema(import.meta.env, clientEnvSchema); +//# sourceMappingURL=env.js.map \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/client/env.js.map b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/client/env.js.map new file mode 100644 index 0000000000..d230f15c0d --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/client/env.js.map @@ -0,0 +1 @@ +{"version":3,"file":"env.js","sourceRoot":"","sources":["../../client/env.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,KAAK,CAAA;AAExB,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAA;AAEtD,MAAM,mBAAmB,GAAG,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,CAAA;AAExC,MAAM,mBAAmB,GAAG,CAAC,CAAC,MAAM,CAAC;IACnC,iBAAiB,EAAE,CAAC;SACjB,MAAM,CAAC;QACN,cAAc,EAAE,+BAA+B;KAChD,CAAC;SACD,OAAO,CAAC,uBAAuB,CAAC;CACpC,CAAC,CAAA;AAEF,MAAM,eAAe,GAAG,mBAAmB,CAAC,KAAK,CAAC,mBAAmB,CAAC,CAAA;AAEtE,aAAa;AACb,MAAM,CAAC,MAAM,GAAG,GAAG,eAAe,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,eAAe,CAAC,CAAA"} \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/client/index.d.ts b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/client/index.d.ts index 7f5f4bfbc8..6498c439bd 100644 --- a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/client/index.d.ts +++ b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/client/index.d.ts @@ -8,4 +8,5 @@ export type Route = { method: HttpMethod; path: string; }; -export { config, ClientConfig } from './config'; +export { config, ClientConfig } from './config.js'; +export { env } from './env.js'; diff --git a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/client/index.js b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/client/index.js index 5a7f13dcae..2f4daa7ae3 100644 --- a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/client/index.js +++ b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/client/index.js @@ -8,5 +8,7 @@ export var HttpMethod; HttpMethod["Delete"] = "DELETE"; })(HttpMethod || (HttpMethod = {})); // PUBLIC API -export { config } from './config'; +export { config } from './config.js'; +// PUBLIC API +export { env } from './env.js'; //# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/client/index.js.map b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/client/index.js.map index 879953c8df..f3611558dc 100644 --- a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/client/index.js.map +++ b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/client/index.js.map @@ -1 +1 @@ -{"version":3,"file":"index.js","sourceRoot":"","sources":["../../client/index.ts"],"names":[],"mappings":"AAAA,aAAa;AACb,mFAAmF;AACnF,MAAM,CAAN,IAAY,UAKX;AALD,WAAY,UAAU;IACrB,yBAAW,CAAA;IACX,2BAAa,CAAA;IACb,yBAAW,CAAA;IACX,+BAAiB,CAAA;AAClB,CAAC,EALW,UAAU,KAAV,UAAU,QAKrB;AAKD,aAAa;AACb,OAAO,EAAE,MAAM,EAAgB,MAAM,UAAU,CAAA"} \ No newline at end of file +{"version":3,"file":"index.js","sourceRoot":"","sources":["../../client/index.ts"],"names":[],"mappings":"AAAA,aAAa;AACb,mFAAmF;AACnF,MAAM,CAAN,IAAY,UAKX;AALD,WAAY,UAAU;IACrB,yBAAW,CAAA;IACX,2BAAa,CAAA;IACb,yBAAW,CAAA;IACX,+BAAiB,CAAA;AAClB,CAAC,EALW,UAAU,KAAV,UAAU,QAKrB;AAKD,aAAa;AACb,OAAO,EAAE,MAAM,EAAgB,MAAM,aAAa,CAAA;AAElD,aAAa;AACb,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAA"} \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/env/index.d.ts b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/env/index.d.ts new file mode 100644 index 0000000000..d1dafc25fd --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/env/index.d.ts @@ -0,0 +1,3 @@ +import * as z from 'zod'; +export declare function defineEnvValidationSchema>(schema: Schema): Schema; +export { ensureEnvSchema } from './validation.js'; diff --git a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/env/index.js b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/env/index.js new file mode 100644 index 0000000000..17e695d087 --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/env/index.js @@ -0,0 +1,7 @@ +// PUBLIC API +export function defineEnvValidationSchema(schema) { + return schema; +} +// PRIVATE API (SDK, Vite config) +export { ensureEnvSchema } from './validation.js'; +//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/env/index.js.map b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/env/index.js.map new file mode 100644 index 0000000000..75b4ff7de3 --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/env/index.js.map @@ -0,0 +1 @@ +{"version":3,"file":"index.js","sourceRoot":"","sources":["../../env/index.ts"],"names":[],"mappings":"AAEA,aAAa;AACb,MAAM,UAAU,yBAAyB,CACvC,MAAc;IAEd,OAAO,MAAM,CAAA;AACf,CAAC;AAED,iCAAiC;AACjC,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA"} \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/env/validation.d.ts b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/env/validation.d.ts new file mode 100644 index 0000000000..6bc9d059a3 --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/env/validation.d.ts @@ -0,0 +1,2 @@ +import * as z from 'zod'; +export declare function ensureEnvSchema(data: unknown, schema: Schema): z.infer; diff --git a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/env/validation.js b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/env/validation.js new file mode 100644 index 0000000000..8463eb578e --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/env/validation.js @@ -0,0 +1,23 @@ +import * as z from 'zod'; +const redColor = '\x1b[31m'; +export function ensureEnvSchema(data, schema) { + try { + return schema.parse(data); + } + catch (e) { + if (e instanceof z.ZodError) { + const errorOutput = ['', 'โ•โ• Env vars validation failed โ•โ•', '']; + for (const error of e.errors) { + errorOutput.push(` - ${error.message}`); + } + errorOutput.push(''); + errorOutput.push('โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•'); + console.error(redColor, errorOutput.join('\n')); + throw new Error('Error parsing environment variables'); + } + else { + throw e; + } + } +} +//# sourceMappingURL=validation.js.map \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/env/validation.js.map b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/env/validation.js.map new file mode 100644 index 0000000000..1d57b14a0b --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/env/validation.js.map @@ -0,0 +1 @@ +{"version":3,"file":"validation.js","sourceRoot":"","sources":["../../env/validation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,KAAK,CAAA;AAExB,MAAM,QAAQ,GAAG,UAAU,CAAA;AAE3B,MAAM,UAAU,eAAe,CAC7B,IAAa,EACb,MAAc;IAEd,IAAI,CAAC;QACH,OAAO,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;IAC3B,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,IAAI,CAAC,YAAY,CAAC,CAAC,QAAQ,EAAE,CAAC;YAC5B,MAAM,WAAW,GAAG,CAAC,EAAE,EAAE,kCAAkC,EAAE,EAAE,CAAC,CAAA;YAChE,KAAK,MAAM,KAAK,IAAI,CAAC,CAAC,MAAM,EAAE,CAAC;gBAC7B,WAAW,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC,OAAO,EAAE,CAAC,CAAA;YACzC,CAAC;YACD,WAAW,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;YACpB,WAAW,CAAC,IAAI,CAAC,kCAAkC,CAAC,CAAA;YACpD,OAAO,CAAC,KAAK,CAAC,QAAQ,EAAE,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAA;YAC/C,MAAM,IAAI,KAAK,CAAC,qCAAqC,CAAC,CAAA;QACxD,CAAC;aAAM,CAAC;YACN,MAAM,CAAC,CAAA;QACT,CAAC;IACH,CAAC;AACH,CAAC"} \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/server/HttpError.js.map b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/server/HttpError.js.map index 8163354e1b..462e1c1811 100644 --- a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/server/HttpError.js.map +++ b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/server/HttpError.js.map @@ -1 +1 @@ -{"version":3,"file":"HttpError.js","sourceRoot":"","sources":["../../server/HttpError.ts"],"names":[],"mappings":"AAAA,MAAM,OAAO,SAAU,SAAQ,KAAK;IAIlC,YAAa,UAAkB,EAAE,OAAgB,EAAE,IAA8B,EAAE,OAAsB;QACvG,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;QAEvB,IAAI,KAAK,CAAC,iBAAiB,EAAE,CAAC;YAC5B,KAAK,CAAC,iBAAiB,CAAC,IAAI,EAAE,SAAS,CAAC,CAAA;QAC1C,CAAC;QAED,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,WAAW,CAAC,IAAI,CAAA;QAEjC,IAAI,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,UAAU,CAAC,IAAI,UAAU,IAAI,GAAG,IAAI,UAAU,GAAG,GAAG,CAAC,EAAE,CAAC;YAC7E,MAAM,IAAI,KAAK,CAAC,mDAAmD,CAAC,CAAA;QACtE,CAAC;QACD,IAAI,CAAC,UAAU,GAAG,UAAU,CAAA;QAE5B,IAAI,IAAI,EAAE,CAAC;YACT,IAAI,CAAC,IAAI,GAAG,IAAI,CAAA;QAClB,CAAC;IACH,CAAC;CACF"} \ No newline at end of file +{"version":3,"file":"HttpError.js","sourceRoot":"","sources":["../../server/HttpError.ts"],"names":[],"mappings":"AAAA,MAAM,OAAO,SAAU,SAAQ,KAAK;IAIlC,YACE,UAAkB,EAClB,OAAgB,EAChB,IAA8B,EAC9B,OAAsB;QAEtB,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;QAEvB,IAAI,KAAK,CAAC,iBAAiB,EAAE,CAAC;YAC5B,KAAK,CAAC,iBAAiB,CAAC,IAAI,EAAE,SAAS,CAAC,CAAA;QAC1C,CAAC;QAED,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,WAAW,CAAC,IAAI,CAAA;QAEjC,IACE,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,UAAU,CAAC,IAAI,UAAU,IAAI,GAAG,IAAI,UAAU,GAAG,GAAG,CAAC,EACxE,CAAC;YACD,MAAM,IAAI,KAAK,CAAC,mDAAmD,CAAC,CAAA;QACtE,CAAC;QACD,IAAI,CAAC,UAAU,GAAG,UAAU,CAAA;QAE5B,IAAI,IAAI,EAAE,CAAC;YACT,IAAI,CAAC,IAAI,GAAG,IAAI,CAAA;QAClB,CAAC;IACH,CAAC;CACF"} \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/server/config.d.ts b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/server/config.d.ts index 68bb448c56..70f438bdf0 100644 --- a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/server/config.d.ts +++ b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/server/config.d.ts @@ -1,16 +1,13 @@ -type BaseConfig = { - allowedCORSOrigins: string | string[]; -}; -type CommonConfig = BaseConfig & { - env: string; +import { env } from './env.js'; +type NodeEnv = typeof env.NODE_ENV; +type Config = { + env: NodeEnv; isDevelopment: boolean; port: number; - databaseUrl: string | undefined; -}; -type EnvConfig = BaseConfig & { + databaseUrl: string; frontendUrl: string; serverUrl: string; + allowedCORSOrigins: string | string[]; }; -type Config = CommonConfig & EnvConfig; -declare const resolvedConfig: Config; -export default resolvedConfig; +declare const config: Config; +export default config; diff --git a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/server/config.js b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/server/config.js index e4f89082c8..dcc80722ab 100644 --- a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/server/config.js +++ b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/server/config.js @@ -1,43 +1,21 @@ -var _a; -import merge from 'lodash.merge'; -import { stripTrailingSlash } from "../universal/url.js"; -const nodeEnv = (_a = process.env.NODE_ENV) !== null && _a !== void 0 ? _a : 'development'; +import { env } from './env.js'; +import { stripTrailingSlash } from '../universal/url.js'; +const frontendUrl = stripTrailingSlash(env.WASP_WEB_CLIENT_URL); +const serverUrl = stripTrailingSlash(env.WASP_SERVER_URL); +const allowedCORSOriginsPerEnv = { + development: '*', + production: [frontendUrl] +}; +const allowedCORSOrigins = allowedCORSOriginsPerEnv[env.NODE_ENV]; const config = { - all: { - env: nodeEnv, - isDevelopment: nodeEnv === 'development', - port: process.env.PORT ? parseInt(process.env.PORT) : 3001, - databaseUrl: process.env.DATABASE_URL, - allowedCORSOrigins: [], - }, - development: getDevelopmentConfig(), - production: getProductionConfig(), + frontendUrl, + serverUrl, + allowedCORSOrigins, + env: env.NODE_ENV, + isDevelopment: env.NODE_ENV === 'development', + port: env.PORT, + databaseUrl: env.DATABASE_URL, }; -const resolvedConfig = merge(config.all, config[nodeEnv]); // PUBLIC API -export default resolvedConfig; -function getDevelopmentConfig() { - var _a, _b; - const frontendUrl = stripTrailingSlash((_a = process.env.WASP_WEB_CLIENT_URL) !== null && _a !== void 0 ? _a : 'http://localhost:3000/'); - const serverUrl = stripTrailingSlash((_b = process.env.WASP_SERVER_URL) !== null && _b !== void 0 ? _b : 'http://localhost:3001'); - return { - // @ts-ignore - frontendUrl, - // @ts-ignore - serverUrl, - allowedCORSOrigins: '*', - }; -} -function getProductionConfig() { - const frontendUrl = stripTrailingSlash(process.env.WASP_WEB_CLIENT_URL); - const serverUrl = stripTrailingSlash(process.env.WASP_SERVER_URL); - return { - // @ts-ignore - frontendUrl, - // @ts-ignore - serverUrl, - // @ts-ignore - allowedCORSOrigins: [frontendUrl], - }; -} +export default config; //# sourceMappingURL=config.js.map \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/server/config.js.map b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/server/config.js.map index e284580bbd..9fd077fa81 100644 --- a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/server/config.js.map +++ b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/server/config.js.map @@ -1 +1 @@ -{"version":3,"file":"config.js","sourceRoot":"","sources":["../../server/config.ts"],"names":[],"mappings":";AAAA,OAAO,KAAK,MAAM,cAAc,CAAA;AAEhC,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAEzD,MAAM,OAAO,GAAG,MAAA,OAAO,CAAC,GAAG,CAAC,QAAQ,mCAAI,aAAa,CAAA;AAyBrD,MAAM,MAAM,GAIR;IACF,GAAG,EAAE;QACH,GAAG,EAAE,OAAO;QACZ,aAAa,EAAE,OAAO,KAAK,aAAa;QACxC,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI;QAC1D,WAAW,EAAE,OAAO,CAAC,GAAG,CAAC,YAAY;QACrC,kBAAkB,EAAE,EAAE;KACvB;IACD,WAAW,EAAE,oBAAoB,EAAE;IACnC,UAAU,EAAE,mBAAmB,EAAE;CAClC,CAAA;AAED,MAAM,cAAc,GAAW,KAAK,CAAC,MAAM,CAAC,GAAG,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC,CAAA;AACjE,aAAa;AACb,eAAe,cAAc,CAAA;AAE7B,SAAS,oBAAoB;;IAC3B,MAAM,WAAW,GAAG,kBAAkB,CAAC,MAAA,OAAO,CAAC,GAAG,CAAC,mBAAmB,mCAAI,wBAAwB,CAAC,CAAC;IACpG,MAAM,SAAS,GAAG,kBAAkB,CAAC,MAAA,OAAO,CAAC,GAAG,CAAC,eAAe,mCAAI,uBAAuB,CAAC,CAAC;IAC7F,OAAO;QACL,aAAa;QACb,WAAW;QACX,aAAa;QACb,SAAS;QACT,kBAAkB,EAAE,GAAG;KACxB,CAAA;AACH,CAAC;AAED,SAAS,mBAAmB;IAC1B,MAAM,WAAW,GAAG,kBAAkB,CAAC,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC,CAAC;IACxE,MAAM,SAAS,GAAG,kBAAkB,CAAC,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC;IAClE,OAAO;QACL,aAAa;QACb,WAAW;QACX,aAAa;QACb,SAAS;QACT,aAAa;QACb,kBAAkB,EAAE,CAAC,WAAW,CAAC;KAClC,CAAA;AACH,CAAC"} \ No newline at end of file +{"version":3,"file":"config.js","sourceRoot":"","sources":["../../server/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAA;AAC9B,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAA;AAcxD,MAAM,WAAW,GAAG,kBAAkB,CAAC,GAAG,CAAC,mBAAmB,CAAC,CAAA;AAC/D,MAAM,SAAS,GAAG,kBAAkB,CAAC,GAAG,CAAC,eAAe,CAAC,CAAA;AAEzD,MAAM,wBAAwB,GAAuC;IACnE,WAAW,EAAE,GAAG;IAChB,UAAU,EAAE,CAAC,WAAW,CAAC;CAC1B,CAAA;AACD,MAAM,kBAAkB,GAAG,wBAAwB,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAA;AAEjE,MAAM,MAAM,GAAW;IACrB,WAAW;IACX,SAAS;IACT,kBAAkB;IAClB,GAAG,EAAE,GAAG,CAAC,QAAQ;IACjB,aAAa,EAAE,GAAG,CAAC,QAAQ,KAAK,aAAa;IAC7C,IAAI,EAAE,GAAG,CAAC,IAAI;IACd,WAAW,EAAE,GAAG,CAAC,YAAY;CAC9B,CAAA;AAED,aAAa;AACb,eAAe,MAAM,CAAA"} \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/server/env.d.ts b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/server/env.d.ts new file mode 100644 index 0000000000..667ea30e35 --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/server/env.d.ts @@ -0,0 +1,17 @@ +export declare const env: { + PORT: number; + DATABASE_URL: string; + SKIP_EMAIL_VERIFICATION_IN_DEV: boolean; + NODE_ENV: "development"; + WASP_SERVER_URL: string; + WASP_WEB_CLIENT_URL: string; + PG_BOSS_NEW_OPTIONS?: string | undefined; +} | { + PORT: number; + DATABASE_URL: string; + SKIP_EMAIL_VERIFICATION_IN_DEV: boolean; + NODE_ENV: "production"; + WASP_SERVER_URL: string; + WASP_WEB_CLIENT_URL: string; + PG_BOSS_NEW_OPTIONS?: string | undefined; +}; diff --git a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/server/env.js b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/server/env.js new file mode 100644 index 0000000000..1bfc3e0b6e --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/server/env.js @@ -0,0 +1,55 @@ +import * as z from 'zod'; +import { ensureEnvSchema } from '../env/validation.js'; +const userServerEnvSchema = z.object({}); +const waspServerCommonSchema = z.object({ + PORT: z.coerce.number().default(3001), + DATABASE_URL: z.string({ + required_error: 'DATABASE_URL is required', + }), + PG_BOSS_NEW_OPTIONS: z.string().optional(), + SKIP_EMAIL_VERIFICATION_IN_DEV: z + .enum(['true', 'false'], { + message: 'SKIP_EMAIL_VERIFICATION_IN_DEV must be either "true" or "false"', + }) + .transform((value) => value === 'true') + .default('false'), +}); +const serverUrlSchema = z + .string({ + required_error: 'WASP_SERVER_URL is required', +}) + .url({ + message: 'WASP_SERVER_URL must be a valid URL', +}); +const clientUrlSchema = z + .string({ + required_error: 'WASP_WEB_CLIENT_URL is required', +}) + .url({ + message: 'WASP_WEB_CLIENT_URL must be a valid URL', +}); +// In development, we provide default values for some environment variables +// to make the development process easier. +const serverDevSchema = z.object({ + NODE_ENV: z.literal('development'), + WASP_SERVER_URL: serverUrlSchema + .default('http://localhost:3001'), + WASP_WEB_CLIENT_URL: clientUrlSchema + .default('http://localhost:3000/'), +}); +const serverProdSchema = z.object({ + NODE_ENV: z.literal('production'), + WASP_SERVER_URL: serverUrlSchema, + WASP_WEB_CLIENT_URL: clientUrlSchema, +}); +const serverCommonSchema = userServerEnvSchema.merge(waspServerCommonSchema); +const serverEnvSchema = z.discriminatedUnion('NODE_ENV', [ + serverDevSchema.merge(serverCommonSchema), + serverProdSchema.merge(serverCommonSchema) +]); +// PUBLIC API +export const env = ensureEnvSchema(process.env, serverEnvSchema); +function getRequiredEnvVarErrorMessage(featureName, envVarName) { + return `${envVarName} is required when using ${featureName}`; +} +//# sourceMappingURL=env.js.map \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/server/env.js.map b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/server/env.js.map new file mode 100644 index 0000000000..3dfa10e116 --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/server/env.js.map @@ -0,0 +1 @@ +{"version":3,"file":"env.js","sourceRoot":"","sources":["../../server/env.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,KAAK,CAAA;AAExB,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAA;AAEtD,MAAM,mBAAmB,GAAG,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,CAAA;AAExC,MAAM,sBAAsB,GAAG,CAAC,CAAC,MAAM,CAAC;IACtC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC;IACrC,YAAY,EAAE,CAAC,CAAC,MAAM,CAAC;QACrB,cAAc,EAAE,0BAA0B;KAC3C,CAAC;IACF,mBAAmB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC1C,8BAA8B,EAAE,CAAC;SAC9B,IAAI,CAAC,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE;QACvB,OAAO,EAAE,iEAAiE;KAC3E,CAAC;SACD,SAAS,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,KAAK,MAAM,CAAC;SACtC,OAAO,CAAC,OAAO,CAAC;CACpB,CAAC,CAAA;AAEF,MAAM,eAAe,GAAG,CAAC;KACtB,MAAM,CAAC;IACN,cAAc,EAAE,6BAA6B;CAC9C,CAAC;KACD,GAAG,CAAC;IACH,OAAO,EAAE,qCAAqC;CAC/C,CAAC,CAAA;AAEJ,MAAM,eAAe,GAAG,CAAC;KACtB,MAAM,CAAC;IACN,cAAc,EAAE,iCAAiC;CAClD,CAAC;KACD,GAAG,CAAC;IACH,OAAO,EAAE,yCAAyC;CACnD,CAAC,CAAA;AAGJ,2EAA2E;AAC3E,0CAA0C;AAC1C,MAAM,eAAe,GAAG,CAAC,CAAC,MAAM,CAAC;IAC/B,QAAQ,EAAE,CAAC,CAAC,OAAO,CAAC,aAAa,CAAC;IAClC,eAAe,EAAE,eAAe;SAC7B,OAAO,CAAC,uBAAuB,CAAC;IACnC,mBAAmB,EAAE,eAAe;SACjC,OAAO,CAAC,wBAAwB,CAAC;CACrC,CAAC,CAAA;AAEF,MAAM,gBAAgB,GAAG,CAAC,CAAC,MAAM,CAAC;IAChC,QAAQ,EAAE,CAAC,CAAC,OAAO,CAAC,YAAY,CAAC;IACjC,eAAe,EAAE,eAAe;IAChC,mBAAmB,EAAE,eAAe;CACrC,CAAC,CAAA;AAEF,MAAM,kBAAkB,GAAG,mBAAmB,CAAC,KAAK,CAAC,sBAAsB,CAAC,CAAA;AAC5E,MAAM,eAAe,GAAG,CAAC,CAAC,kBAAkB,CAAC,UAAU,EAAE;IACvD,eAAe,CAAC,KAAK,CAAC,kBAAkB,CAAC;IACzC,gBAAgB,CAAC,KAAK,CAAC,kBAAkB,CAAC;CAC3C,CAAC,CAAA;AAEF,aAAa;AACb,MAAM,CAAC,MAAM,GAAG,GAAG,eAAe,CAAC,OAAO,CAAC,GAAG,EAAE,eAAe,CAAC,CAAA;AAEhE,SAAS,6BAA6B,CAAC,WAAmB,EAAE,UAAkB;IAC5E,OAAO,GAAG,UAAU,2BAA2B,WAAW,EAAE,CAAA;AAC9D,CAAC"} \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/server/index.d.ts b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/server/index.d.ts index dee9613a53..a108f6123f 100644 --- a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/server/index.d.ts +++ b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/server/index.d.ts @@ -4,4 +4,5 @@ export { default as prisma } from './dbClient.js'; export { type ServerSetupFn } from './types/index.js'; export { HttpError } from './HttpError.js'; export { MiddlewareConfigFn } from './middleware/index.js'; +export { env } from './env.js'; export type DbSeedFn = (prisma: PrismaClient) => Promise; diff --git a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/server/index.js b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/server/index.js index 0e53e0deab..8e75e6f78c 100644 --- a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/server/index.js +++ b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/server/index.js @@ -4,4 +4,6 @@ export { default as config } from './config.js'; export { default as prisma } from './dbClient.js'; // PUBLIC API export { HttpError } from './HttpError.js'; +// PUBLIC API +export { env } from './env.js'; //# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/server/index.js.map b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/server/index.js.map index b84ac1b303..87ed22f3af 100644 --- a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/server/index.js.map +++ b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/server/index.js.map @@ -1 +1 @@ -{"version":3,"file":"index.js","sourceRoot":"","sources":["../../server/index.ts"],"names":[],"mappings":"AAEA,aAAa;AACb,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,aAAa,CAAA;AAC/C,aAAa;AACb,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,eAAe,CAAA;AAGjD,aAAa;AACb,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA"} \ No newline at end of file +{"version":3,"file":"index.js","sourceRoot":"","sources":["../../server/index.ts"],"names":[],"mappings":"AAEA,aAAa;AACb,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,aAAa,CAAA;AAC/C,aAAa;AACb,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,eAAe,CAAA;AAGjD,aAAa;AACb,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAG1C,aAAa;AACb,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAA"} \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/universal/url.d.ts b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/universal/url.d.ts index aa893e7838..7ceb994694 100644 --- a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/universal/url.d.ts +++ b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/universal/url.d.ts @@ -1 +1,2 @@ -export declare function stripTrailingSlash(url?: string): string | undefined; +export declare function stripTrailingSlash(url: string): string; +export declare function stripTrailingSlash(url: undefined): undefined; diff --git a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/universal/url.js.map b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/universal/url.js.map index a7bad96020..e236f1b977 100644 --- a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/universal/url.js.map +++ b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/universal/url.js.map @@ -1 +1 @@ -{"version":3,"file":"url.js","sourceRoot":"","sources":["../../universal/url.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,kBAAkB,CAAC,GAAY;IAC3C,OAAO,GAAG,aAAH,GAAG,uBAAH,GAAG,CAAE,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;AACnC,CAAC"} \ No newline at end of file +{"version":3,"file":"url.js","sourceRoot":"","sources":["../../universal/url.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,kBAAkB,CAAC,GAAY;IAC3C,OAAO,GAAG,aAAH,GAAG,uBAAH,GAAG,CAAE,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;AACnC,CAAC"} \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/env/index.ts b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/env/index.ts new file mode 100644 index 0000000000..4c783125c1 --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/env/index.ts @@ -0,0 +1,11 @@ +import * as z from 'zod' + +// PUBLIC API +export function defineEnvValidationSchema>( + schema: Schema, +): Schema { + return schema +} + +// PRIVATE API (SDK, Vite config) +export { ensureEnvSchema } from './validation.js' diff --git a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/env/validation.ts b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/env/validation.ts new file mode 100644 index 0000000000..a5bc334754 --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/env/validation.ts @@ -0,0 +1,25 @@ +import * as z from 'zod' + +const redColor = '\x1b[31m' + +export function ensureEnvSchema( + data: unknown, + schema: Schema +): z.infer { + try { + return schema.parse(data) + } catch (e) { + if (e instanceof z.ZodError) { + const errorOutput = ['', 'โ•โ• Env vars validation failed โ•โ•', ''] + for (const error of e.errors) { + errorOutput.push(` - ${error.message}`) + } + errorOutput.push('') + errorOutput.push('โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•') + console.error(redColor, errorOutput.join('\n')) + throw new Error('Error parsing environment variables') + } else { + throw e + } + } +} diff --git a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/package.json b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/package.json index 2589c5362f..55c07a5084 100644 --- a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/package.json +++ b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/package.json @@ -8,7 +8,6 @@ "axios": "^1.4.0", "express": "~4.21.0", "jsdom": "^21.1.1", - "lodash.merge": "^4.6.2", "mitt": "3.0.0", "msw": "^1.1.0", "prisma": "5.19.1", @@ -16,7 +15,8 @@ "react-hook-form": "^7.45.4", "react-router-dom": "^6.26.2", "superjson": "^2.2.1", - "vitest": "^1.2.1" + "vitest": "^1.2.1", + "zod": "^3.23.8" }, "devDependencies": { "@tsconfig/node18": "latest", @@ -51,6 +51,7 @@ "./core/storage": "./dist/core/storage.js", "./dev": "./dist/dev/index.js", "./entities": "./dist/entities/index.js", + "./env": "./dist/env/index.js", "./ext-src/*": "./dist/ext-src/*.js", "./operations": "./dist/operations/index.js", "./operations/*": "./dist/operations/*", diff --git a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/server/HttpError.ts b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/server/HttpError.ts index d05a2c841b..bce964712d 100644 --- a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/server/HttpError.ts +++ b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/server/HttpError.ts @@ -2,7 +2,12 @@ export class HttpError extends Error { public statusCode: number public data: unknown - constructor (statusCode: number, message?: string, data?: Record, options?: ErrorOptions) { + constructor( + statusCode: number, + message?: string, + data?: Record, + options?: ErrorOptions + ) { super(message, options) if (Error.captureStackTrace) { @@ -11,7 +16,9 @@ export class HttpError extends Error { this.name = this.constructor.name - if (!(Number.isInteger(statusCode) && statusCode >= 400 && statusCode < 600)) { + if ( + !(Number.isInteger(statusCode) && statusCode >= 400 && statusCode < 600) + ) { throw new Error('statusCode has to be integer in range [400, 600).') } this.statusCode = statusCode diff --git a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/server/config.ts b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/server/config.ts index 1dc9bafb93..3a000a897f 100644 --- a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/server/config.ts +++ b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/server/config.ts @@ -1,73 +1,36 @@ -import merge from 'lodash.merge' +import { env } from './env.js' +import { stripTrailingSlash } from '../universal/url.js' -import { stripTrailingSlash } from "../universal/url.js"; +type NodeEnv = typeof env.NODE_ENV -const nodeEnv = process.env.NODE_ENV ?? 'development' - -// TODO: -// - Use dotenv library to consume env vars from a file. -// - Use convict library to define schema and validate env vars. -// https://codingsans.com/blog/node-config-best-practices - -type BaseConfig = { - allowedCORSOrigins: string | string[]; -} - -type CommonConfig = BaseConfig & { - env: string; +type Config = { + env: NodeEnv; isDevelopment: boolean; port: number; - databaseUrl: string | undefined; -} - -type EnvConfig = BaseConfig & { + databaseUrl: string; frontendUrl: string; serverUrl: string; + allowedCORSOrigins: string | string[]; } -type Config = CommonConfig & EnvConfig +const frontendUrl = stripTrailingSlash(env.WASP_WEB_CLIENT_URL) +const serverUrl = stripTrailingSlash(env.WASP_SERVER_URL) -const config: { - all: CommonConfig, - development: EnvConfig, - production: EnvConfig, -} = { - all: { - env: nodeEnv, - isDevelopment: nodeEnv === 'development', - port: process.env.PORT ? parseInt(process.env.PORT) : 3001, - databaseUrl: process.env.DATABASE_URL, - allowedCORSOrigins: [], - }, - development: getDevelopmentConfig(), - production: getProductionConfig(), +const allowedCORSOriginsPerEnv: Record = { + development: '*', + production: [frontendUrl] } - -const resolvedConfig: Config = merge(config.all, config[nodeEnv]) -// PUBLIC API -export default resolvedConfig - -function getDevelopmentConfig(): EnvConfig { - const frontendUrl = stripTrailingSlash(process.env.WASP_WEB_CLIENT_URL ?? 'http://localhost:3000/'); - const serverUrl = stripTrailingSlash(process.env.WASP_SERVER_URL ?? 'http://localhost:3001'); - return { - // @ts-ignore - frontendUrl, - // @ts-ignore - serverUrl, - allowedCORSOrigins: '*', - } +const allowedCORSOrigins = allowedCORSOriginsPerEnv[env.NODE_ENV] + +const config: Config = { + frontendUrl, + serverUrl, + allowedCORSOrigins, + env: env.NODE_ENV, + isDevelopment: env.NODE_ENV === 'development', + port: env.PORT, + databaseUrl: env.DATABASE_URL, } -function getProductionConfig(): EnvConfig { - const frontendUrl = stripTrailingSlash(process.env.WASP_WEB_CLIENT_URL); - const serverUrl = stripTrailingSlash(process.env.WASP_SERVER_URL); - return { - // @ts-ignore - frontendUrl, - // @ts-ignore - serverUrl, - // @ts-ignore - allowedCORSOrigins: [frontendUrl], - } -} +// PUBLIC API +export default config diff --git a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/server/env.ts b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/server/env.ts new file mode 100644 index 0000000000..c1bd8f5c0f --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/server/env.ts @@ -0,0 +1,65 @@ +import * as z from 'zod' + +import { ensureEnvSchema } from '../env/validation.js' + +const userServerEnvSchema = z.object({}) + +const waspServerCommonSchema = z.object({ + PORT: z.coerce.number().default(3001), + DATABASE_URL: z.string({ + required_error: 'DATABASE_URL is required', + }), + PG_BOSS_NEW_OPTIONS: z.string().optional(), + SKIP_EMAIL_VERIFICATION_IN_DEV: z + .enum(['true', 'false'], { + message: 'SKIP_EMAIL_VERIFICATION_IN_DEV must be either "true" or "false"', + }) + .transform((value) => value === 'true') + .default('false'), +}) + +const serverUrlSchema = z + .string({ + required_error: 'WASP_SERVER_URL is required', + }) + .url({ + message: 'WASP_SERVER_URL must be a valid URL', + }) + +const clientUrlSchema = z + .string({ + required_error: 'WASP_WEB_CLIENT_URL is required', + }) + .url({ + message: 'WASP_WEB_CLIENT_URL must be a valid URL', + }) + + +// In development, we provide default values for some environment variables +// to make the development process easier. +const serverDevSchema = z.object({ + NODE_ENV: z.literal('development'), + WASP_SERVER_URL: serverUrlSchema + .default('http://localhost:3001'), + WASP_WEB_CLIENT_URL: clientUrlSchema + .default('http://localhost:3000/'), +}) + +const serverProdSchema = z.object({ + NODE_ENV: z.literal('production'), + WASP_SERVER_URL: serverUrlSchema, + WASP_WEB_CLIENT_URL: clientUrlSchema, +}) + +const serverCommonSchema = userServerEnvSchema.merge(waspServerCommonSchema) +const serverEnvSchema = z.discriminatedUnion('NODE_ENV', [ + serverDevSchema.merge(serverCommonSchema), + serverProdSchema.merge(serverCommonSchema) +]) + +// PUBLIC API +export const env = ensureEnvSchema(process.env, serverEnvSchema) + +function getRequiredEnvVarErrorMessage(featureName: string, envVarName: string) { + return `${envVarName} is required when using ${featureName}` +} diff --git a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/server/index.ts b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/server/index.ts index 535077900b..0f95deb90a 100644 --- a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/server/index.ts +++ b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/server/index.ts @@ -10,6 +10,8 @@ export { type ServerSetupFn } from './types/index.js' export { HttpError } from './HttpError.js' // PUBLIC API export { MiddlewareConfigFn } from './middleware/index.js' +// PUBLIC API +export { env } from './env.js' // PUBLIC API export type DbSeedFn = (prisma: PrismaClient) => Promise diff --git a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/universal/url.ts b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/universal/url.ts index d21c06c65c..126678dac9 100644 --- a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/universal/url.ts +++ b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/universal/url.ts @@ -1,3 +1,5 @@ +export function stripTrailingSlash(url: string): string +export function stripTrailingSlash(url: undefined): undefined export function stripTrailingSlash(url?: string): string | undefined { return url?.replace(/\/$/, ""); } diff --git a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/server/package.json b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/server/package.json index 413d85a86f..e3d0d954da 100644 --- a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/server/package.json +++ b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/server/package.json @@ -34,9 +34,8 @@ "bundle-and-start": "npm run bundle && npm run start", "db-migrate-prod": "prisma migrate deploy --schema=../db/schema.prisma", "db-seed": "npm run bundle && node --enable-source-maps -r dotenv/config bundle/dbSeed.js", - "start": "npm run validate-env && node --enable-source-maps -r dotenv/config bundle/server.js", + "start": "node --enable-source-maps -r dotenv/config bundle/server.js", "start-production": "npm run db-migrate-prod && NODE_ENV=production npm run start", - "validate-env": "node -r dotenv/config ./scripts/validate-env.mjs", "watch": "nodemon --exec 'npm run bundle-and-start || exit 1'" }, "type": "module", diff --git a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/server/scripts/validate-env.mjs b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/server/scripts/validate-env.mjs deleted file mode 100644 index f6e50aceca..0000000000 --- a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/server/scripts/validate-env.mjs +++ /dev/null @@ -1,5 +0,0 @@ -import { throwIfNotValidAbsoluteURL } from 'wasp/universal/validators'; - -console.info("๐Ÿ” Validating environment variables..."); -throwIfNotValidAbsoluteURL(process.env.WASP_WEB_CLIENT_URL, 'Environment variable WASP_WEB_CLIENT_URL'); -throwIfNotValidAbsoluteURL(process.env.WASP_SERVER_URL, 'Environment variable WASP_SERVER_URL'); diff --git a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/server/src/polyfill.ts b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/server/src/polyfill.ts index 7931d12eb0..9279daf6da 100644 --- a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/server/src/polyfill.ts +++ b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/server/src/polyfill.ts @@ -1,11 +1,10 @@ // This is a polyfill for Node.js 18 webcrypto API so Lucia can use it // for random number generation. -import { webcrypto } from "node:crypto"; +import { webcrypto } from 'node:crypto' // NOTE: node < 19 doesn't have Crypto API, which we need for Lucia, so we apply the polyfill if Crypto API is not defined. -if (typeof globalThis.crypto === "undefined") { +if (typeof globalThis.crypto === 'undefined') { // @ts-ignore - globalThis.crypto = webcrypto as Crypto; + globalThis.crypto = webcrypto as Crypto } - diff --git a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/web-app/package.json b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/web-app/package.json index 8d93f8e94c..b80c6eea39 100644 --- a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/web-app/package.json +++ b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/web-app/package.json @@ -18,8 +18,7 @@ "devDependencies": { "@tsconfig/vite-react": "^2.0.0", "@types/react-dom": "^18.0.11", - "@vitejs/plugin-react": "^4.2.1", - "dotenv": "^16.0.3" + "@vitejs/plugin-react": "^4.2.1" }, "engineStrict": true, "engines": { @@ -28,9 +27,8 @@ "name": "waspMigrate", "private": true, "scripts": { - "build": "npm run validate-env && tsc && vite build", - "start": "npm run validate-env && vite", - "validate-env": "node -r dotenv/config ./scripts/validate-env.mjs" + "build": "tsc && vite build", + "start": "vite" }, "type": "module", "version": "0.0.0" diff --git a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/web-app/scripts/validate-env.mjs b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/web-app/scripts/validate-env.mjs deleted file mode 100644 index 18ee507c9e..0000000000 --- a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/web-app/scripts/validate-env.mjs +++ /dev/null @@ -1,4 +0,0 @@ -import { throwIfNotValidAbsoluteURL } from 'wasp/universal/validators'; - -console.info("๐Ÿ” Validating environment variables..."); -throwIfNotValidAbsoluteURL(process.env.REACT_APP_API_URL, 'Environemnt variable REACT_APP_API_URL'); diff --git a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/web-app/src/components/DefaultRootErrorBoundary.tsx b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/web-app/src/components/DefaultRootErrorBoundary.tsx index 7af24e15d6..1e931935e6 100644 --- a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/web-app/src/components/DefaultRootErrorBoundary.tsx +++ b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/web-app/src/components/DefaultRootErrorBoundary.tsx @@ -7,7 +7,10 @@ export function DefaultRootErrorBoundary() { console.error(error) return ( -
There was an error rendering this page. Check the browser console for more information.
+
+ There was an error rendering this page. Check the browser console for + more information. +
) } diff --git a/waspc/examples/todoApp/.env.server.example b/waspc/examples/todoApp/.env.server.example index 76df894076..08a8f0dd11 100644 --- a/waspc/examples/todoApp/.env.server.example +++ b/waspc/examples/todoApp/.env.server.example @@ -22,3 +22,5 @@ GITHUB_CLIENT_SECRET='dummy-gh-client-secret' # Dummy values here will allow app to run, but you will need real values to get Discord Auth to work. DISCORD_CLIENT_SECRET='dummy-discord-client-secret' DISCORD_CLIENT_ID='dummy-discord-client-id' + +MY_ENV_VAR=123 diff --git a/waspc/examples/todoApp/main.wasp b/waspc/examples/todoApp/main.wasp index 9cd2f4b1c6..f827d34e83 100644 --- a/waspc/examples/todoApp/main.wasp +++ b/waspc/examples/todoApp/main.wasp @@ -52,12 +52,14 @@ app todoApp { onAfterLogin: import { onAfterLogin } from "@src/auth/hooks.js", }, server: { - setupFn: import setup from "@src/serverSetup", + setupFn: import { setup } from "@src/serverSetup", middlewareConfigFn: import { serverMiddlewareFn } from "@src/serverSetup", + envValidationSchema: import { serverEnvValidationSchema } from "@src/env", }, client: { rootComponent: import { App } from "@src/App", - setupFn: import setup from "@src/clientSetup" + setupFn: import { setup } from "@src/clientSetup", + envValidationSchema: import { clientEnvValidationSchema } from "@src/env", }, db: { seeds: [ diff --git a/waspc/examples/todoApp/package-lock.json b/waspc/examples/todoApp/package-lock.json index 0e940179a5..9b072296ec 100644 --- a/waspc/examples/todoApp/package-lock.json +++ b/waspc/examples/todoApp/package-lock.json @@ -40,7 +40,6 @@ "axios": "^1.4.0", "express": "~4.21.0", "jsdom": "^21.1.1", - "lodash.merge": "^4.6.2", "lucia": "^3.0.1", "mitt": "3.0.0", "msw": "^1.1.0", @@ -56,7 +55,8 @@ "socket.io-client": "^4.6.1", "superjson": "^2.2.1", "tailwindcss": "^3.2.7", - "vitest": "^1.2.1" + "vitest": "^1.2.1", + "zod": "^3.23.8" }, "devDependencies": { "@tsconfig/node18": "latest", @@ -9966,6 +9966,14 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } + }, + "node_modules/zod": { + "version": "3.23.8", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz", + "integrity": "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } } } } diff --git a/waspc/data/Generator/templates/server/prettier.config.js b/waspc/examples/todoApp/prettier.config.cjs similarity index 56% rename from waspc/data/Generator/templates/server/prettier.config.js rename to waspc/examples/todoApp/prettier.config.cjs index 38597ed8a0..0614ee70c6 100644 --- a/waspc/data/Generator/templates/server/prettier.config.js +++ b/waspc/examples/todoApp/prettier.config.cjs @@ -1,4 +1,3 @@ -// Used for internal Wasp development only, not copied to generated app. module.exports = { trailingComma: 'es5', tabWidth: 2, diff --git a/waspc/examples/todoApp/prettier.config.js b/waspc/examples/todoApp/prettier.config.js deleted file mode 100644 index abcda7180c..0000000000 --- a/waspc/examples/todoApp/prettier.config.js +++ /dev/null @@ -1,6 +0,0 @@ -module.exports = { - trailingComma: 'es5', - tabWidth: 2, - semi: false, - singleQuote: true, -} diff --git a/waspc/examples/todoApp/src/App.tsx b/waspc/examples/todoApp/src/App.tsx index e7f0fbb3b0..25b9867eb5 100644 --- a/waspc/examples/todoApp/src/App.tsx +++ b/waspc/examples/todoApp/src/App.tsx @@ -3,6 +3,7 @@ import { useSocket } from 'wasp/client/webSocket' import { Link } from 'wasp/client/router' import { logout, useAuth } from 'wasp/client/auth' import { useQuery, getDate } from 'wasp/client/operations' +import { env } from 'wasp/client' import './Main.css' import { getName } from './user' @@ -16,9 +17,7 @@ export function App() { const connectionIcon = isConnected ? '๐ŸŸข' : '๐Ÿ”ด' - const appName = import.meta.env.REACT_APP_NAME - ? import.meta.env.REACT_APP_NAME - : 'TODO App' + const appName = env.REACT_APP_NAME return (
diff --git a/waspc/examples/todoApp/src/clientSetup.js b/waspc/examples/todoApp/src/clientSetup.js index 35a39dd00d..fc35f6ec82 100644 --- a/waspc/examples/todoApp/src/clientSetup.js +++ b/waspc/examples/todoApp/src/clientSetup.js @@ -1,7 +1,7 @@ import { testingAction } from 'wasp/client/operations' import { sayHi } from './util' -export default function setup() { +export function setup() { console.log('This was called from the client setup function') testingAction() sayHi() diff --git a/waspc/examples/todoApp/src/env.ts b/waspc/examples/todoApp/src/env.ts new file mode 100644 index 0000000000..288f7a7ae1 --- /dev/null +++ b/waspc/examples/todoApp/src/env.ts @@ -0,0 +1,16 @@ +import * as z from 'zod' +import { defineEnvValidationSchema } from 'wasp/env' + +export const serverEnvValidationSchema = defineEnvValidationSchema( + z.object({ + MY_ENV_VAR: z.string({ + required_error: 'MY_ENV_VAR is required.', + }), + }) +) + +export const clientEnvValidationSchema = defineEnvValidationSchema( + z.object({ + REACT_APP_NAME: z.string().default('TODO App'), + }) +) diff --git a/waspc/examples/todoApp/src/serverSetup.ts b/waspc/examples/todoApp/src/serverSetup.ts index 09b73d42b2..1194f26cf0 100644 --- a/waspc/examples/todoApp/src/serverSetup.ts +++ b/waspc/examples/todoApp/src/serverSetup.ts @@ -2,6 +2,7 @@ import { type Application } from 'express' import { mySpecialJob } from 'wasp/server/jobs' import { config, + env, type MiddlewareConfigFn, type ServerSetupFn, } from 'wasp/server' @@ -13,7 +14,7 @@ let someResource: string | undefined = undefined export const getSomeResource = () => someResource -const setup: ServerSetupFn = async ({ app }) => { +export const setup: ServerSetupFn = async ({ app }) => { addCustomRoute(app) sayHi() @@ -33,6 +34,8 @@ const setup: ServerSetupFn = async ({ app }) => { 'submittedJob.pgBoss.details()', await submittedJob.pgBoss.details() ) + + console.log('Env var MY_ENV_VAR:', env.MY_ENV_VAR) } function addCustomRoute(app: Application) { @@ -51,5 +54,3 @@ export const serverMiddlewareFn: MiddlewareConfigFn = (middlewareConfig) => { ) return middlewareConfig } - -export default setup diff --git a/waspc/headless-test/examples/todoApp/.env.server.example b/waspc/headless-test/examples/todoApp/.env.server.example index c4b5c097c7..d0918e891b 100644 --- a/waspc/headless-test/examples/todoApp/.env.server.example +++ b/waspc/headless-test/examples/todoApp/.env.server.example @@ -1,3 +1,7 @@ GOOGLE_CLIENT_ID="mock-client-id" GOOGLE_CLIENT_SECRET="mock-client-secret" SKIP_EMAIL_VERIFICATION_IN_DEV=true +SMTP_HOST=dummy +SMTP_PORT=465 +SMTP_USERNAME=dummy +SMTP_PASSWORD=dummy diff --git a/waspc/headless-test/examples/todoApp/package-lock.json b/waspc/headless-test/examples/todoApp/package-lock.json index 482b8a1e19..ed9d984f8d 100644 --- a/waspc/headless-test/examples/todoApp/package-lock.json +++ b/waspc/headless-test/examples/todoApp/package-lock.json @@ -39,791 +39,31 @@ "axios": "^1.4.0", "express": "~4.21.0", "jsdom": "^21.1.1", - "lodash.merge": "^4.6.2", - "lucia": "^3.0.1", - "mitt": "3.0.0", - "msw": "^1.1.0", - "nodemailer": "^6.9.1", - "oslo": "^1.1.2", - "pg-boss": "^8.4.2", - "postcss": "^8.4.21", - "prisma": "5.19.1", - "react": "^18.2.0", - "react-hook-form": "^7.45.4", - "react-router-dom": "^6.26.2", - "superjson": "^2.2.1", - "tailwindcss": "^3.2.7", - "vitest": "^1.2.1" - }, - "devDependencies": { - "@tsconfig/node18": "latest", - "@types/express-serve-static-core": "^4.17.13" - } - }, - ".wasp/out/sdk/wasp/node_modules/@napi-rs/wasm-runtime": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.4.tgz", - "integrity": "sha512-9zESzOO5aDByvhIAsOy9TbpZ0Ur2AJbUI7UT73kcUTS2mxAMHOBaa1st/jAymNoCtvrit99kkzT1FZuXVcgfIQ==", - "optional": true, - "dependencies": { - "@emnapi/core": "^1.1.0", - "@emnapi/runtime": "^1.1.0", - "@tybys/wasm-util": "^0.9.0" - } - }, - ".wasp/out/sdk/wasp/node_modules/@napi-rs/wasm-runtime/node_modules/@emnapi/core": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.2.0.tgz", - "integrity": "sha512-E7Vgw78I93we4ZWdYCb4DGAwRROGkMIXk7/y87UmANR+J6qsWusmC3gLt0H+O0KOt5e6O38U8oJamgbudrES/w==", - "optional": true, - "dependencies": { - "@emnapi/wasi-threads": "1.0.1", - "tslib": "^2.4.0" - } - }, - ".wasp/out/sdk/wasp/node_modules/@napi-rs/wasm-runtime/node_modules/@emnapi/runtime": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.2.0.tgz", - "integrity": "sha512-bV21/9LQmcQeCPEg3BDFtvwL6cwiTMksYNWQQ4KOxCZikEGalWtenoZ0wCiukJINlGCIi2KXx01g4FoH/LxpzQ==", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - ".wasp/out/sdk/wasp/node_modules/@napi-rs/wasm-runtime/node_modules/@tybys/wasm-util": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.9.0.tgz", - "integrity": "sha512-6+7nlbMVX/PVDCwaIQ8nTOPveOcFLSt8GcXdx8hD0bt39uWxYT88uXzqTd4fTvqta7oeUJqudepapKNt2DYJFw==", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - ".wasp/out/sdk/wasp/node_modules/@node-rs/argon2": { - "version": "1.8.3", - "resolved": "https://registry.npmjs.org/@node-rs/argon2/-/argon2-1.8.3.tgz", - "integrity": "sha512-sf/QAEI59hsMEEE2J8vO4hKrXrv4Oplte3KI2N4MhMDYpytH0drkVfErmHBfWFZxxIEK03fX1WsBNswS2nIZKg==", - "engines": { - "node": ">= 10" - }, - "optionalDependencies": { - "@node-rs/argon2-android-arm-eabi": "1.8.3", - "@node-rs/argon2-android-arm64": "1.8.3", - "@node-rs/argon2-darwin-arm64": "1.8.3", - "@node-rs/argon2-darwin-x64": "1.8.3", - "@node-rs/argon2-freebsd-x64": "1.8.3", - "@node-rs/argon2-linux-arm-gnueabihf": "1.8.3", - "@node-rs/argon2-linux-arm64-gnu": "1.8.3", - "@node-rs/argon2-linux-arm64-musl": "1.8.3", - "@node-rs/argon2-linux-x64-gnu": "1.8.3", - "@node-rs/argon2-linux-x64-musl": "1.8.3", - "@node-rs/argon2-wasm32-wasi": "1.8.3", - "@node-rs/argon2-win32-arm64-msvc": "1.8.3", - "@node-rs/argon2-win32-ia32-msvc": "1.8.3", - "@node-rs/argon2-win32-x64-msvc": "1.8.3" - } - }, - ".wasp/out/sdk/wasp/node_modules/@node-rs/argon2-android-arm-eabi": { - "version": "1.8.3", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-android-arm-eabi/-/argon2-android-arm-eabi-1.8.3.tgz", - "integrity": "sha512-JFZPlNM0A8Og+Tncb8UZsQrhEMlbHBXPsT3hRoKImzVmTmq28Os0ucFWow0AACp2coLHBSydXH3Dh0lZup3rWw==", - "cpu": [ - "arm" - ], - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 10" - } - }, - ".wasp/out/sdk/wasp/node_modules/@node-rs/argon2-android-arm64": { - "version": "1.8.3", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-android-arm64/-/argon2-android-arm64-1.8.3.tgz", - "integrity": "sha512-zaf8P3T92caeW2xnMA7P1QvRA4pIt/04oilYP44XlTCtMye//vwXDMeK53sl7dvYiJKnzAWDRx41k8vZvpZazg==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 10" - } - }, - ".wasp/out/sdk/wasp/node_modules/@node-rs/argon2-darwin-arm64": { - "version": "1.8.3", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-darwin-arm64/-/argon2-darwin-arm64-1.8.3.tgz", - "integrity": "sha512-DV/IbmLGdNXBtXb5o2UI5ba6kvqXqPAJgmMOTUCuHeBSp992GlLHdfU4rzGu0dNrxudBnunNZv+crd0YdEQSUA==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - ".wasp/out/sdk/wasp/node_modules/@node-rs/argon2-darwin-x64": { - "version": "1.8.3", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-darwin-x64/-/argon2-darwin-x64-1.8.3.tgz", - "integrity": "sha512-YMjmBGFZhLfYjfQ2gll9A+BZu/zAMV7lWZIbKxb7ZgEofILQwuGmExjDtY3Jplido/6leCEdpmlk2oIsME00LA==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - ".wasp/out/sdk/wasp/node_modules/@node-rs/argon2-freebsd-x64": { - "version": "1.8.3", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-freebsd-x64/-/argon2-freebsd-x64-1.8.3.tgz", - "integrity": "sha512-Hq3Rj5Yb2RolTG/luRPnv+XiGCbi5nAK25Pc8ou/tVapwX+iktEm/NXbxc5zsMxraYVkCvfdwBjweC5O+KqCGw==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">= 10" - } - }, - ".wasp/out/sdk/wasp/node_modules/@node-rs/argon2-linux-arm-gnueabihf": { - "version": "1.8.3", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-linux-arm-gnueabihf/-/argon2-linux-arm-gnueabihf-1.8.3.tgz", - "integrity": "sha512-x49l8RgzKoG0/V0IXa5rrEl1TcJEc936ctlYFvqcunSOyowZ6kiWtrp1qrbOR8gbaNILl11KTF52vF6+h8UlEQ==", - "cpu": [ - "arm" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - ".wasp/out/sdk/wasp/node_modules/@node-rs/argon2-linux-arm64-gnu": { - "version": "1.8.3", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-linux-arm64-gnu/-/argon2-linux-arm64-gnu-1.8.3.tgz", - "integrity": "sha512-gJesam/qA63reGkb9qJ2TjFSLBtY41zQh2oei7nfnYsmVQPuHHWItJxEa1Bm21SPW53gZex4jFJbDIgj0+PxIw==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - ".wasp/out/sdk/wasp/node_modules/@node-rs/argon2-linux-arm64-musl": { - "version": "1.8.3", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-linux-arm64-musl/-/argon2-linux-arm64-musl-1.8.3.tgz", - "integrity": "sha512-7O6kQdSKzB4Tjx/EBa8zKIxnmLkQE8VdJgPm6Ksrpn+ueo0mx2xf76fIDnbbTCtm3UbB+y+FkTo2wLA7tOqIKg==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - ".wasp/out/sdk/wasp/node_modules/@node-rs/argon2-linux-x64-gnu": { - "version": "1.8.3", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-linux-x64-gnu/-/argon2-linux-x64-gnu-1.8.3.tgz", - "integrity": "sha512-OBH+EFG7BGjFyldaao2H2gSCLmjtrrwf420B1L+lFn7JLW9UAjsIPFKAcWsYwPa/PwYzIge9Y7SGcpqlsSEX0w==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - ".wasp/out/sdk/wasp/node_modules/@node-rs/argon2-linux-x64-musl": { - "version": "1.8.3", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-linux-x64-musl/-/argon2-linux-x64-musl-1.8.3.tgz", - "integrity": "sha512-bDbMuyekIxZaN7NaX+gHVkOyABB8bcMEJYeRPW1vCXKHj3brJns1wiUFSxqeUXreupifNVJlQfPt1Y5B/vFXgQ==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - ".wasp/out/sdk/wasp/node_modules/@node-rs/argon2-wasm32-wasi": { - "version": "1.8.3", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-wasm32-wasi/-/argon2-wasm32-wasi-1.8.3.tgz", - "integrity": "sha512-NBf2cMCDbNKMzp13Pog8ZPmI0M9U4Ak5b95EUjkp17kdKZFds12dwW67EMnj7Zy+pRqby2QLECaWebDYfNENTg==", - "cpu": [ - "wasm32" - ], - "optional": true, - "dependencies": { - "@napi-rs/wasm-runtime": "^0.2.3" - }, - "engines": { - "node": ">=14.0.0" - } - }, - ".wasp/out/sdk/wasp/node_modules/@node-rs/argon2-win32-arm64-msvc": { - "version": "1.8.3", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-win32-arm64-msvc/-/argon2-win32-arm64-msvc-1.8.3.tgz", - "integrity": "sha512-AHpPo7UbdW5WWjwreVpgFSY0o1RY4A7cUFaqDXZB2OqEuyrhMxBdZct9PX7PQKI18D85pLsODnR+gvVuTwJ6rQ==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - ".wasp/out/sdk/wasp/node_modules/@node-rs/argon2-win32-ia32-msvc": { - "version": "1.8.3", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-win32-ia32-msvc/-/argon2-win32-ia32-msvc-1.8.3.tgz", - "integrity": "sha512-bqzn2rcQkEwCINefhm69ttBVVkgHJb/V03DdBKsPFtiX6H47axXKz62d1imi26zFXhOEYxhKbu3js03GobJOLw==", - "cpu": [ - "ia32" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - ".wasp/out/sdk/wasp/node_modules/@node-rs/argon2-win32-x64-msvc": { - "version": "1.8.3", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-win32-x64-msvc/-/argon2-win32-x64-msvc-1.8.3.tgz", - "integrity": "sha512-ILlrRThdbp5xNR5gwYM2ic1n/vG5rJ8dQZ+YMRqksl+lnTJ/6FDe5BOyIhiPtiDwlCiCtUA+1NxpDB9KlUCAIA==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - ".wasp/out/sdk/wasp/node_modules/@node-rs/bcrypt": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@node-rs/bcrypt/-/bcrypt-1.9.0.tgz", - "integrity": "sha512-u2OlIxW264bFUfvbFqDz9HZKFjwe8FHFtn7T/U8mYjPZ7DWYpbUB+/dkW/QgYfMSfR0ejkyuWaBBe0coW7/7ig==", - "engines": { - "node": ">= 10" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Brooooooklyn" - }, - "optionalDependencies": { - "@node-rs/bcrypt-android-arm-eabi": "1.9.0", - "@node-rs/bcrypt-android-arm64": "1.9.0", - "@node-rs/bcrypt-darwin-arm64": "1.9.0", - "@node-rs/bcrypt-darwin-x64": "1.9.0", - "@node-rs/bcrypt-freebsd-x64": "1.9.0", - "@node-rs/bcrypt-linux-arm-gnueabihf": "1.9.0", - "@node-rs/bcrypt-linux-arm64-gnu": "1.9.0", - "@node-rs/bcrypt-linux-arm64-musl": "1.9.0", - "@node-rs/bcrypt-linux-x64-gnu": "1.9.0", - "@node-rs/bcrypt-linux-x64-musl": "1.9.0", - "@node-rs/bcrypt-wasm32-wasi": "1.9.0", - "@node-rs/bcrypt-win32-arm64-msvc": "1.9.0", - "@node-rs/bcrypt-win32-ia32-msvc": "1.9.0", - "@node-rs/bcrypt-win32-x64-msvc": "1.9.0" - } - }, - ".wasp/out/sdk/wasp/node_modules/@node-rs/bcrypt-android-arm-eabi": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-android-arm-eabi/-/bcrypt-android-arm-eabi-1.9.0.tgz", - "integrity": "sha512-nOCFISGtnodGHNiLrG0WYLWr81qQzZKYfmwHc7muUeq+KY0sQXyHOwZk9OuNQAWv/lnntmtbwkwT0QNEmOyLvA==", - "cpu": [ - "arm" - ], - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 10" - } - }, - ".wasp/out/sdk/wasp/node_modules/@node-rs/bcrypt-android-arm64": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-android-arm64/-/bcrypt-android-arm64-1.9.0.tgz", - "integrity": "sha512-+ZrIAtigVmjYkqZQTThHVlz0+TG6D+GDHWhVKvR2DifjtqJ0i+mb9gjo++hN+fWEQdWNGxKCiBBjwgT4EcXd6A==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 10" - } - }, - ".wasp/out/sdk/wasp/node_modules/@node-rs/bcrypt-darwin-arm64": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-darwin-arm64/-/bcrypt-darwin-arm64-1.9.0.tgz", - "integrity": "sha512-CQiS+F9Pa0XozvkXR1g7uXE9QvBOPOplDg0iCCPRYTN9PqA5qYxhwe48G3o+v2UeQceNRrbnEtWuANm7JRqIhw==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - ".wasp/out/sdk/wasp/node_modules/@node-rs/bcrypt-darwin-x64": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-darwin-x64/-/bcrypt-darwin-x64-1.9.0.tgz", - "integrity": "sha512-4pTKGawYd7sNEjdJ7R/R67uwQH1VvwPZ0SSUMmeNHbxD5QlwAPXdDH11q22uzVXsvNFZ6nGQBg8No5OUGpx6Ug==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - ".wasp/out/sdk/wasp/node_modules/@node-rs/bcrypt-freebsd-x64": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-freebsd-x64/-/bcrypt-freebsd-x64-1.9.0.tgz", - "integrity": "sha512-UmWzySX4BJhT/B8xmTru6iFif3h0Rpx3TqxRLCcbgmH43r7k5/9QuhpiyzpvKGpKHJCFNm4F3rC2wghvw5FCIg==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">= 10" - } - }, - ".wasp/out/sdk/wasp/node_modules/@node-rs/bcrypt-linux-arm-gnueabihf": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-linux-arm-gnueabihf/-/bcrypt-linux-arm-gnueabihf-1.9.0.tgz", - "integrity": "sha512-8qoX4PgBND2cVwsbajoAWo3NwdfJPEXgpCsZQZURz42oMjbGyhhSYbovBCskGU3EBLoC8RA2B1jFWooeYVn5BA==", - "cpu": [ - "arm" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - ".wasp/out/sdk/wasp/node_modules/@node-rs/bcrypt-linux-arm64-gnu": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-linux-arm64-gnu/-/bcrypt-linux-arm64-gnu-1.9.0.tgz", - "integrity": "sha512-TuAC6kx0SbcIA4mSEWPi+OCcDjTQUMl213v5gMNlttF+D4ieIZx6pPDGTaMO6M2PDHTeCG0CBzZl0Lu+9b0c7Q==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - ".wasp/out/sdk/wasp/node_modules/@node-rs/bcrypt-linux-arm64-musl": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-linux-arm64-musl/-/bcrypt-linux-arm64-musl-1.9.0.tgz", - "integrity": "sha512-/sIvKDABOI8QOEnLD7hIj02BVaNOuCIWBKvxcJOt8+TuwJ6zmY1UI5kSv9d99WbiHjTp97wtAUbZQwauU4b9ew==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - ".wasp/out/sdk/wasp/node_modules/@node-rs/bcrypt-linux-x64-gnu": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-linux-x64-gnu/-/bcrypt-linux-x64-gnu-1.9.0.tgz", - "integrity": "sha512-DyyhDHDsLBsCKz1tZ1hLvUZSc1DK0FU0v52jK6IBQxrj24WscSU9zZe7ie/V9kdmA4Ep57BfpWX8Dsa2JxGdgQ==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - ".wasp/out/sdk/wasp/node_modules/@node-rs/bcrypt-linux-x64-musl": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-linux-x64-musl/-/bcrypt-linux-x64-musl-1.9.0.tgz", - "integrity": "sha512-duIiuqQ+Lew8ASSAYm6ZRqcmfBGWwsi81XLUwz86a2HR7Qv6V4yc3ZAUQovAikhjCsIqe8C11JlAZSK6+PlXYg==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - ".wasp/out/sdk/wasp/node_modules/@node-rs/bcrypt-wasm32-wasi": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-wasm32-wasi/-/bcrypt-wasm32-wasi-1.9.0.tgz", - "integrity": "sha512-ylaGmn9Wjwv/D5lxtawttx3H6Uu2WTTR7lWlRHGT6Ga/MB1Vj4OjSGUW8G8zIVnKuXpGbZ92pgHlt4HUpSLctw==", - "cpu": [ - "wasm32" - ], - "optional": true, - "dependencies": { - "@emnapi/core": "^0.45.0", - "@emnapi/runtime": "^0.45.0", - "@tybys/wasm-util": "^0.8.1", - "memfs-browser": "^3.4.13000" - }, - "engines": { - "node": ">=14.0.0" - } - }, - ".wasp/out/sdk/wasp/node_modules/@node-rs/bcrypt-win32-arm64-msvc": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-win32-arm64-msvc/-/bcrypt-win32-arm64-msvc-1.9.0.tgz", - "integrity": "sha512-2h86gF7QFyEzODuDFml/Dp1MSJoZjxJ4yyT2Erf4NkwsiA5MqowUhUsorRwZhX6+2CtlGa7orbwi13AKMsYndw==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - ".wasp/out/sdk/wasp/node_modules/@node-rs/bcrypt-win32-ia32-msvc": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-win32-ia32-msvc/-/bcrypt-win32-ia32-msvc-1.9.0.tgz", - "integrity": "sha512-kqxalCvhs4FkN0+gWWfa4Bdy2NQAkfiqq/CEf6mNXC13RSV673Ev9V8sRlQyNpCHCNkeXfOT9pgoBdJmMs9muA==", - "cpu": [ - "ia32" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - ".wasp/out/sdk/wasp/node_modules/@node-rs/bcrypt-win32-x64-msvc": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-win32-x64-msvc/-/bcrypt-win32-x64-msvc-1.9.0.tgz", - "integrity": "sha512-2y0Tuo6ZAT2Cz8V7DHulSlv1Bip3zbzeXyeur+uR25IRNYXKvI/P99Zl85Fbuu/zzYAZRLLlGTRe6/9IHofe/w==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - ".wasp/out/sdk/wasp/node_modules/oslo": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/oslo/-/oslo-1.2.0.tgz", - "integrity": "sha512-OoFX6rDsNcOQVAD2gQD/z03u4vEjWZLzJtwkmgfRF+KpQUXwdgEXErD7zNhyowmHwHefP+PM9Pw13pgpHMRlzw==", - "dependencies": { - "@node-rs/argon2": "1.7.0", - "@node-rs/bcrypt": "1.9.0" - } - }, - ".wasp/out/sdk/wasp/node_modules/oslo/node_modules/@node-rs/argon2": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@node-rs/argon2/-/argon2-1.7.0.tgz", - "integrity": "sha512-zfULc+/tmcWcxn+nHkbyY8vP3+MpEqKORbszt4UkpqZgBgDAAIYvuDN/zukfTgdmo6tmJKKVfzigZOPk4LlIog==", - "engines": { - "node": ">= 10" - }, - "optionalDependencies": { - "@node-rs/argon2-android-arm-eabi": "1.7.0", - "@node-rs/argon2-android-arm64": "1.7.0", - "@node-rs/argon2-darwin-arm64": "1.7.0", - "@node-rs/argon2-darwin-x64": "1.7.0", - "@node-rs/argon2-freebsd-x64": "1.7.0", - "@node-rs/argon2-linux-arm-gnueabihf": "1.7.0", - "@node-rs/argon2-linux-arm64-gnu": "1.7.0", - "@node-rs/argon2-linux-arm64-musl": "1.7.0", - "@node-rs/argon2-linux-x64-gnu": "1.7.0", - "@node-rs/argon2-linux-x64-musl": "1.7.0", - "@node-rs/argon2-wasm32-wasi": "1.7.0", - "@node-rs/argon2-win32-arm64-msvc": "1.7.0", - "@node-rs/argon2-win32-ia32-msvc": "1.7.0", - "@node-rs/argon2-win32-x64-msvc": "1.7.0" - } - }, - ".wasp/out/sdk/wasp/node_modules/oslo/node_modules/@node-rs/argon2-android-arm-eabi": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-android-arm-eabi/-/argon2-android-arm-eabi-1.7.0.tgz", - "integrity": "sha512-udDqkr5P9E+wYX1SZwAVPdyfYvaF4ry9Tm+R9LkfSHbzWH0uhU6zjIwNRp7m+n4gx691rk+lqqDAIP8RLKwbhg==", - "cpu": [ - "arm" - ], - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 10" - } - }, - ".wasp/out/sdk/wasp/node_modules/oslo/node_modules/@node-rs/argon2-android-arm64": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-android-arm64/-/argon2-android-arm64-1.7.0.tgz", - "integrity": "sha512-s9j/G30xKUx8WU50WIhF0fIl1EdhBGq0RQ06lEhZ0Gi0ap8lhqbE2Bn5h3/G2D1k0Dx+yjeVVNmt/xOQIRG38A==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 10" - } - }, - ".wasp/out/sdk/wasp/node_modules/oslo/node_modules/@node-rs/argon2-darwin-arm64": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-darwin-arm64/-/argon2-darwin-arm64-1.7.0.tgz", - "integrity": "sha512-ZIz4L6HGOB9U1kW23g+m7anGNuTZ0RuTw0vNp3o+2DWpb8u8rODq6A8tH4JRL79S+Co/Nq608m9uackN2pe0Rw==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - ".wasp/out/sdk/wasp/node_modules/oslo/node_modules/@node-rs/argon2-darwin-x64": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-darwin-x64/-/argon2-darwin-x64-1.7.0.tgz", - "integrity": "sha512-5oi/pxqVhODW/pj1+3zElMTn/YukQeywPHHYDbcAW3KsojFjKySfhcJMd1DjKTc+CHQI+4lOxZzSUzK7mI14Hw==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - ".wasp/out/sdk/wasp/node_modules/oslo/node_modules/@node-rs/argon2-freebsd-x64": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-freebsd-x64/-/argon2-freebsd-x64-1.7.0.tgz", - "integrity": "sha512-Ify08683hA4QVXYoIm5SUWOY5DPIT/CMB0CQT+IdxQAg/F+qp342+lUkeAtD5bvStQuCx/dFO3bnnzoe2clMhA==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">= 10" - } - }, - ".wasp/out/sdk/wasp/node_modules/oslo/node_modules/@node-rs/argon2-linux-arm-gnueabihf": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-linux-arm-gnueabihf/-/argon2-linux-arm-gnueabihf-1.7.0.tgz", - "integrity": "sha512-7DjDZ1h5AUHAtRNjD19RnQatbhL+uuxBASuuXIBu4/w6Dx8n7YPxwTP4MXfsvuRgKuMWiOb/Ub/HJ3kXVCXRkg==", - "cpu": [ - "arm" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - ".wasp/out/sdk/wasp/node_modules/oslo/node_modules/@node-rs/argon2-linux-arm64-gnu": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-linux-arm64-gnu/-/argon2-linux-arm64-gnu-1.7.0.tgz", - "integrity": "sha512-nJDoMP4Y3YcqGswE4DvP080w6O24RmnFEDnL0emdI8Nou17kNYBzP2546Nasx9GCyLzRcYQwZOUjrtUuQ+od2g==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - ".wasp/out/sdk/wasp/node_modules/oslo/node_modules/@node-rs/argon2-linux-arm64-musl": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-linux-arm64-musl/-/argon2-linux-arm64-musl-1.7.0.tgz", - "integrity": "sha512-BKWS8iVconhE3jrb9mj6t1J9vwUqQPpzCbUKxfTGJfc+kNL58F1SXHBoe2cDYGnHrFEHTY0YochzXoAfm4Dm/A==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - ".wasp/out/sdk/wasp/node_modules/oslo/node_modules/@node-rs/argon2-linux-x64-gnu": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-linux-x64-gnu/-/argon2-linux-x64-gnu-1.7.0.tgz", - "integrity": "sha512-EmgqZOlf4Jurk/szW1iTsVISx25bKksVC5uttJDUloTgsAgIGReCpUUO1R24pBhu9ESJa47iv8NSf3yAfGv6jQ==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - ".wasp/out/sdk/wasp/node_modules/oslo/node_modules/@node-rs/argon2-linux-x64-musl": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-linux-x64-musl/-/argon2-linux-x64-musl-1.7.0.tgz", - "integrity": "sha512-/o1efYCYIxjfuoRYyBTi2Iy+1iFfhqHCvvVsnjNSgO1xWiWrX0Rrt/xXW5Zsl7vS2Y+yu8PL8KFWRzZhaVxfKA==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - ".wasp/out/sdk/wasp/node_modules/oslo/node_modules/@node-rs/argon2-wasm32-wasi": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-wasm32-wasi/-/argon2-wasm32-wasi-1.7.0.tgz", - "integrity": "sha512-Evmk9VcxqnuwQftfAfYEr6YZYSPLzmKUsbFIMep5nTt9PT4XYRFAERj7wNYp+rOcBenF3X4xoB+LhwcOMTNE5w==", - "cpu": [ - "wasm32" - ], - "optional": true, - "dependencies": { - "@emnapi/core": "^0.45.0", - "@emnapi/runtime": "^0.45.0", - "@tybys/wasm-util": "^0.8.1", - "memfs-browser": "^3.4.13000" + "lucia": "^3.0.1", + "mitt": "3.0.0", + "msw": "^1.1.0", + "nodemailer": "^6.9.1", + "oslo": "^1.1.2", + "pg-boss": "^8.4.2", + "postcss": "^8.4.21", + "prisma": "5.19.1", + "react": "^18.2.0", + "react-hook-form": "^7.45.4", + "react-router-dom": "^6.26.2", + "superjson": "^2.2.1", + "tailwindcss": "^3.2.7", + "vitest": "^1.2.1", + "zod": "^3.23.8" }, - "engines": { - "node": ">=14.0.0" - } - }, - ".wasp/out/sdk/wasp/node_modules/oslo/node_modules/@node-rs/argon2-win32-arm64-msvc": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-win32-arm64-msvc/-/argon2-win32-arm64-msvc-1.7.0.tgz", - "integrity": "sha512-qgsU7T004COWWpSA0tppDqDxbPLgg8FaU09krIJ7FBl71Sz8SFO40h7fDIjfbTT5w7u6mcaINMQ5bSHu75PCaA==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - ".wasp/out/sdk/wasp/node_modules/oslo/node_modules/@node-rs/argon2-win32-ia32-msvc": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-win32-ia32-msvc/-/argon2-win32-ia32-msvc-1.7.0.tgz", - "integrity": "sha512-JGafwWYQ/HpZ3XSwP4adQ6W41pRvhcdXvpzIWtKvX+17+xEXAe2nmGWM6s27pVkg1iV2ZtoYLRDkOUoGqZkCcg==", - "cpu": [ - "ia32" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - ".wasp/out/sdk/wasp/node_modules/oslo/node_modules/@node-rs/argon2-win32-x64-msvc": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-win32-x64-msvc/-/argon2-win32-x64-msvc-1.7.0.tgz", - "integrity": "sha512-9oq4ShyFakw8AG3mRls0AoCpxBFcimYx7+jvXeAf2OqKNO+mSA6eZ9z7KQeVCi0+SOEUYxMGf5UiGiDb9R6+9Q==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" + "devDependencies": { + "@tsconfig/node18": "latest", + "@types/express-serve-static-core": "^4.17.13" } }, "node_modules/@adobe/css-tools": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.3.3.tgz", - "integrity": "sha512-rE0Pygv0sEZ4vBWHlAgJLGDU7Pm8xoO6p3wsEceb7GYAjScrOHpEo8KK/eVkAcnSM+slAEtXjA2JpdjLp4fJQQ==" + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.4.1.tgz", + "integrity": "sha512-12WGKBQzjUAI4ayyF4IAtfw2QR/IDoqk6jTddXDhtYTJF9ASmoE1zst7cVtP0aL/F1jUJL5r+JxKXKEgHNbEUQ==" }, "node_modules/@alloc/quick-lru": { "version": "5.2.0", @@ -837,154 +77,30 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz", - "integrity": "sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==", + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", + "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", "dependencies": { - "@babel/highlight": "^7.23.4", - "chalk": "^2.4.2" + "@babel/helper-validator-identifier": "^7.25.9", + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/code-frame/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/code-frame/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/code-frame/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/@babel/code-frame/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" - }, - "node_modules/@babel/code-frame/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/code-frame/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", - "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.23.4.tgz", - "integrity": "sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==", - "dependencies": { - "@babel/helper-validator-identifier": "^7.22.20", - "chalk": "^2.4.2", - "js-tokens": "^4.0.0" - }, + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", + "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/highlight/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/@babel/highlight/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" - }, - "node_modules/@babel/highlight/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/@babel/runtime": { - "version": "7.23.9", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.9.tgz", - "integrity": "sha512-0CX6F+BI2s9dkUqr08KFrAIZgNFj75rdBU/DjCyYLIaV/quFjkk6T+EJ2LkZHyZTbEV4L5p97mNkUsHl2wLFAw==", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.0.tgz", + "integrity": "sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==", "dependencies": { "regenerator-runtime": "^0.14.0" }, @@ -993,18 +109,19 @@ } }, "node_modules/@emnapi/core": { - "version": "0.45.0", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-0.45.0.tgz", - "integrity": "sha512-DPWjcUDQkCeEM4VnljEOEcXdAD7pp8zSZsgOujk/LGIwCXWbXJngin+MO4zbH429lzeC3WbYLGjE2MaUOwzpyw==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.3.1.tgz", + "integrity": "sha512-pVGjBIt1Y6gg3EJN8jTcfpP/+uuRksIo055oE/OBkDNcjZqVbfkWCksG1Jp4yZnj3iKWyWX8fdG/j6UDYPbFog==", "optional": true, "dependencies": { + "@emnapi/wasi-threads": "1.0.1", "tslib": "^2.4.0" } }, "node_modules/@emnapi/runtime": { - "version": "0.45.0", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-0.45.0.tgz", - "integrity": "sha512-Txumi3td7J4A/xTTwlssKieHKTGl3j4A1tglBx72auZ49YK7ePY6XZricgIg9mnZT4xPfA+UPCUdnhRuEFDL+w==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.3.1.tgz", + "integrity": "sha512-kEBmG8KyqtxJZv+ygbEim+KCGtIq1fC22Ms3S4ziXmYKm8uyoLX0MHONVKwp+9opg390VaKRNt4a7A9NwmpNhw==", "optional": true, "dependencies": { "tslib": "^2.4.0" @@ -1020,9 +137,9 @@ } }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.19.12.tgz", - "integrity": "sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", "cpu": [ "ppc64" ], @@ -1414,52 +531,52 @@ } }, "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", - "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", + "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", "dependencies": { - "@jridgewell/set-array": "^1.0.1", + "@jridgewell/set-array": "^1.2.1", "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" + "@jridgewell/trace-mapping": "^0.3.24" }, "engines": { "node": ">=6.0.0" } }, "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", - "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", "engines": { "node": ">=6.0.0" } }, "node_modules/@jridgewell/set-array": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", - "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", "engines": { "node": ">=6.0.0" } }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==" + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==" }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.22", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.22.tgz", - "integrity": "sha512-Wf963MzWtA2sjrNt+g18IAln9lKnlRp+K2eH4jjIoF1wYeq3aMREpG09xhlhdzS0EjwU7qmUJYangWa+151vZw==", + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "node_modules/@lucia-auth/adapter-prisma": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@lucia-auth/adapter-prisma/-/adapter-prisma-4.0.0.tgz", - "integrity": "sha512-TJg4/U1K1slS4A/OzDYCgmHTrIArntIynue14zGIEaMDu+OD+tGyq9WwQWqOabpx2Gm9xtDsoK+tTokAY3Up7A==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@lucia-auth/adapter-prisma/-/adapter-prisma-4.0.1.tgz", + "integrity": "sha512-3SztRhj1RAHbbhI/0aB7YC5zl6Z6aktPhkWpn2CHhiB03B9x/+A+M6pqJuAt1usU8PzkjVilgRPhrPymMar66A==", "peerDependencies": { "@prisma/client": "^4.2.0 || ^5.0.0", "lucia": "3.x" @@ -1496,11 +613,11 @@ } }, "node_modules/@mswjs/interceptors/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", "dependencies": { - "ms": "2.1.2" + "ms": "^2.1.3" }, "engines": { "node": ">=6.0" @@ -1512,9 +629,9 @@ } }, "node_modules/@mswjs/interceptors/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, "node_modules/@mswjs/interceptors/node_modules/strict-event-emitter": { "version": "0.2.8", @@ -1525,44 +642,44 @@ } }, "node_modules/@napi-rs/wasm-runtime": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.1.1.tgz", - "integrity": "sha512-ATj9ua659JgrkICjJscaeZdmPr44cb/KFjNWuD0N6pux0SpzaM7+iOuuK11mAnQM2N9q0DT4REu6NkL8ZEhopw==", + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.6.tgz", + "integrity": "sha512-z8YVS3XszxFTO73iwvFDNpQIzdMmSDTP/mB3E/ucR37V3Sx57hSExcXyMoNwaucWxnsWf4xfbZv0iZ30jr0M4Q==", "optional": true, "dependencies": { - "@emnapi/core": "^0.45.0", - "@emnapi/runtime": "^0.45.0", - "@tybys/wasm-util": "^0.8.1" + "@emnapi/core": "^1.3.1", + "@emnapi/runtime": "^1.3.1", + "@tybys/wasm-util": "^0.9.0" } }, "node_modules/@node-rs/argon2": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/@node-rs/argon2/-/argon2-1.7.2.tgz", - "integrity": "sha512-+H6pc3M1vIX9YnG59YW7prHhhpv19P8YyxlXHnnFzTimf2q+kKDF7mGWbhvN9STqIY+P70Patn0Q6qb6Ib5/4g==", + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/@node-rs/argon2/-/argon2-1.8.3.tgz", + "integrity": "sha512-sf/QAEI59hsMEEE2J8vO4hKrXrv4Oplte3KI2N4MhMDYpytH0drkVfErmHBfWFZxxIEK03fX1WsBNswS2nIZKg==", "engines": { "node": ">= 10" }, "optionalDependencies": { - "@node-rs/argon2-android-arm-eabi": "1.7.2", - "@node-rs/argon2-android-arm64": "1.7.2", - "@node-rs/argon2-darwin-arm64": "1.7.2", - "@node-rs/argon2-darwin-x64": "1.7.2", - "@node-rs/argon2-freebsd-x64": "1.7.2", - "@node-rs/argon2-linux-arm-gnueabihf": "1.7.2", - "@node-rs/argon2-linux-arm64-gnu": "1.7.2", - "@node-rs/argon2-linux-arm64-musl": "1.7.2", - "@node-rs/argon2-linux-x64-gnu": "1.7.2", - "@node-rs/argon2-linux-x64-musl": "1.7.2", - "@node-rs/argon2-wasm32-wasi": "1.7.2", - "@node-rs/argon2-win32-arm64-msvc": "1.7.2", - "@node-rs/argon2-win32-ia32-msvc": "1.7.2", - "@node-rs/argon2-win32-x64-msvc": "1.7.2" + "@node-rs/argon2-android-arm-eabi": "1.8.3", + "@node-rs/argon2-android-arm64": "1.8.3", + "@node-rs/argon2-darwin-arm64": "1.8.3", + "@node-rs/argon2-darwin-x64": "1.8.3", + "@node-rs/argon2-freebsd-x64": "1.8.3", + "@node-rs/argon2-linux-arm-gnueabihf": "1.8.3", + "@node-rs/argon2-linux-arm64-gnu": "1.8.3", + "@node-rs/argon2-linux-arm64-musl": "1.8.3", + "@node-rs/argon2-linux-x64-gnu": "1.8.3", + "@node-rs/argon2-linux-x64-musl": "1.8.3", + "@node-rs/argon2-wasm32-wasi": "1.8.3", + "@node-rs/argon2-win32-arm64-msvc": "1.8.3", + "@node-rs/argon2-win32-ia32-msvc": "1.8.3", + "@node-rs/argon2-win32-x64-msvc": "1.8.3" } }, "node_modules/@node-rs/argon2-android-arm-eabi": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-android-arm-eabi/-/argon2-android-arm-eabi-1.7.2.tgz", - "integrity": "sha512-WhW84XOzdR4AOGc4BJvIg5lCRVBL0pXp/PPCe8QCyWw493p7VdNCdYpr2xdtjS/0zImmY85HNB/6zpzjLRTT/A==", + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-android-arm-eabi/-/argon2-android-arm-eabi-1.8.3.tgz", + "integrity": "sha512-JFZPlNM0A8Og+Tncb8UZsQrhEMlbHBXPsT3hRoKImzVmTmq28Os0ucFWow0AACp2coLHBSydXH3Dh0lZup3rWw==", "cpu": [ "arm" ], @@ -1575,9 +692,9 @@ } }, "node_modules/@node-rs/argon2-android-arm64": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-android-arm64/-/argon2-android-arm64-1.7.2.tgz", - "integrity": "sha512-CdtayHSMIyDuVhSYFirwA757c4foQuyTjpysgFJLHweP9C7uDiBf9WBYij+UyabpaCadJ0wPyK6Vakinvlk4/g==", + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-android-arm64/-/argon2-android-arm64-1.8.3.tgz", + "integrity": "sha512-zaf8P3T92caeW2xnMA7P1QvRA4pIt/04oilYP44XlTCtMye//vwXDMeK53sl7dvYiJKnzAWDRx41k8vZvpZazg==", "cpu": [ "arm64" ], @@ -1590,9 +707,9 @@ } }, "node_modules/@node-rs/argon2-darwin-arm64": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-darwin-arm64/-/argon2-darwin-arm64-1.7.2.tgz", - "integrity": "sha512-hUOhtgYHTEyzX5sgMZVdXunONOus2HWpWydF5D/RYJ1mZ76FXRnFpQE40DqbzisdPIraKdn40m7JqkPP7wqdyg==", + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-darwin-arm64/-/argon2-darwin-arm64-1.8.3.tgz", + "integrity": "sha512-DV/IbmLGdNXBtXb5o2UI5ba6kvqXqPAJgmMOTUCuHeBSp992GlLHdfU4rzGu0dNrxudBnunNZv+crd0YdEQSUA==", "cpu": [ "arm64" ], @@ -1605,9 +722,9 @@ } }, "node_modules/@node-rs/argon2-darwin-x64": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-darwin-x64/-/argon2-darwin-x64-1.7.2.tgz", - "integrity": "sha512-lfs5HX+t542yUfcv6Aa/NeGD1nUCwyQNgnPEGcik71Ow6V13hkR1bHgmT1u3CHN4fBts0gW+DQEDsq1xlVgkvw==", + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-darwin-x64/-/argon2-darwin-x64-1.8.3.tgz", + "integrity": "sha512-YMjmBGFZhLfYjfQ2gll9A+BZu/zAMV7lWZIbKxb7ZgEofILQwuGmExjDtY3Jplido/6leCEdpmlk2oIsME00LA==", "cpu": [ "x64" ], @@ -1620,9 +737,9 @@ } }, "node_modules/@node-rs/argon2-freebsd-x64": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-freebsd-x64/-/argon2-freebsd-x64-1.7.2.tgz", - "integrity": "sha512-ROoF+4VaCBJUjddrTN1hjuqSl89ppRcjVXJscSPJjWzTlbzFmGGovJvIzUBmCr/Oq3yM1zKHj6MP9oRD5cB+/g==", + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-freebsd-x64/-/argon2-freebsd-x64-1.8.3.tgz", + "integrity": "sha512-Hq3Rj5Yb2RolTG/luRPnv+XiGCbi5nAK25Pc8ou/tVapwX+iktEm/NXbxc5zsMxraYVkCvfdwBjweC5O+KqCGw==", "cpu": [ "x64" ], @@ -1635,9 +752,9 @@ } }, "node_modules/@node-rs/argon2-linux-arm-gnueabihf": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-linux-arm-gnueabihf/-/argon2-linux-arm-gnueabihf-1.7.2.tgz", - "integrity": "sha512-CBSB8KPI8LS74Bcz3dYaa2/khULutz4vSDvFWUERlSLX+mPdDhoZi6UPuUPPF9e01w8AbiK1YCqlLUTm3tIMfw==", + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-linux-arm-gnueabihf/-/argon2-linux-arm-gnueabihf-1.8.3.tgz", + "integrity": "sha512-x49l8RgzKoG0/V0IXa5rrEl1TcJEc936ctlYFvqcunSOyowZ6kiWtrp1qrbOR8gbaNILl11KTF52vF6+h8UlEQ==", "cpu": [ "arm" ], @@ -1650,9 +767,9 @@ } }, "node_modules/@node-rs/argon2-linux-arm64-gnu": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-linux-arm64-gnu/-/argon2-linux-arm64-gnu-1.7.2.tgz", - "integrity": "sha512-6LBTug6ZiWFakP3X3Nqs7ZTM03gmcSWX4YvEn20HhhQE5NDrsrw3zNqGj0cJiNzKKIMSDDuj7uGy+ITEfNo4CA==", + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-linux-arm64-gnu/-/argon2-linux-arm64-gnu-1.8.3.tgz", + "integrity": "sha512-gJesam/qA63reGkb9qJ2TjFSLBtY41zQh2oei7nfnYsmVQPuHHWItJxEa1Bm21SPW53gZex4jFJbDIgj0+PxIw==", "cpu": [ "arm64" ], @@ -1665,9 +782,9 @@ } }, "node_modules/@node-rs/argon2-linux-arm64-musl": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-linux-arm64-musl/-/argon2-linux-arm64-musl-1.7.2.tgz", - "integrity": "sha512-KjhQ+ZPne29t9VRVeIif7JdKwQba+tM6CBNYBoJB1iON0CUKeqSQtZcHuTj9gkf2SNRG5bsU4ABcfxd0OKsKHg==", + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-linux-arm64-musl/-/argon2-linux-arm64-musl-1.8.3.tgz", + "integrity": "sha512-7O6kQdSKzB4Tjx/EBa8zKIxnmLkQE8VdJgPm6Ksrpn+ueo0mx2xf76fIDnbbTCtm3UbB+y+FkTo2wLA7tOqIKg==", "cpu": [ "arm64" ], @@ -1680,9 +797,9 @@ } }, "node_modules/@node-rs/argon2-linux-x64-gnu": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-linux-x64-gnu/-/argon2-linux-x64-gnu-1.7.2.tgz", - "integrity": "sha512-BQvp+iLtKqomHz4q5t1aKoni9osgvUDU5sZtHAlFm5dRTlGHnympcQVATRE5GHyH9C6MIM9W7P1kqEeCLGPolQ==", + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-linux-x64-gnu/-/argon2-linux-x64-gnu-1.8.3.tgz", + "integrity": "sha512-OBH+EFG7BGjFyldaao2H2gSCLmjtrrwf420B1L+lFn7JLW9UAjsIPFKAcWsYwPa/PwYzIge9Y7SGcpqlsSEX0w==", "cpu": [ "x64" ], @@ -1695,9 +812,9 @@ } }, "node_modules/@node-rs/argon2-linux-x64-musl": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-linux-x64-musl/-/argon2-linux-x64-musl-1.7.2.tgz", - "integrity": "sha512-yXJudpBZQ98g+lWaHn9EzZ5KsAyqRdlpub/K+5NP7gHehb8wzBRIFAejIHAG0fvzQEEc86VOnV2koWIVZxWAvw==", + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-linux-x64-musl/-/argon2-linux-x64-musl-1.8.3.tgz", + "integrity": "sha512-bDbMuyekIxZaN7NaX+gHVkOyABB8bcMEJYeRPW1vCXKHj3brJns1wiUFSxqeUXreupifNVJlQfPt1Y5B/vFXgQ==", "cpu": [ "x64" ], @@ -1710,24 +827,24 @@ } }, "node_modules/@node-rs/argon2-wasm32-wasi": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-wasm32-wasi/-/argon2-wasm32-wasi-1.7.2.tgz", - "integrity": "sha512-diXlVjJZY2GIV8ZDwUqXPhacXsFR0klGSv5D9f+XidwWXK4udtzDhkM/7N/Mb7h1HAWaxZ6IN9spYFjvWH1wqg==", + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-wasm32-wasi/-/argon2-wasm32-wasi-1.8.3.tgz", + "integrity": "sha512-NBf2cMCDbNKMzp13Pog8ZPmI0M9U4Ak5b95EUjkp17kdKZFds12dwW67EMnj7Zy+pRqby2QLECaWebDYfNENTg==", "cpu": [ "wasm32" ], "optional": true, "dependencies": { - "@napi-rs/wasm-runtime": "^0.1.1" + "@napi-rs/wasm-runtime": "^0.2.3" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@node-rs/argon2-win32-arm64-msvc": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-win32-arm64-msvc/-/argon2-win32-arm64-msvc-1.7.2.tgz", - "integrity": "sha512-dhIBrY04P9nbmwzBpgERQDmmSu4YBZyeEE32t4TikMz5rQ07iaVC+JpGmtCBZoDIsLDHGC8cikENd3YEqpqIcA==", + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-win32-arm64-msvc/-/argon2-win32-arm64-msvc-1.8.3.tgz", + "integrity": "sha512-AHpPo7UbdW5WWjwreVpgFSY0o1RY4A7cUFaqDXZB2OqEuyrhMxBdZct9PX7PQKI18D85pLsODnR+gvVuTwJ6rQ==", "cpu": [ "arm64" ], @@ -1740,9 +857,9 @@ } }, "node_modules/@node-rs/argon2-win32-ia32-msvc": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-win32-ia32-msvc/-/argon2-win32-ia32-msvc-1.7.2.tgz", - "integrity": "sha512-o1tfqr8gyALCzuxBoQfvhxkeYMaw/0H8Gmt7klTYyEIBvEFu7SD5qytXO9Px7t5420nZL/Wy5cflg3IB1s57Pg==", + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-win32-ia32-msvc/-/argon2-win32-ia32-msvc-1.8.3.tgz", + "integrity": "sha512-bqzn2rcQkEwCINefhm69ttBVVkgHJb/V03DdBKsPFtiX6H47axXKz62d1imi26zFXhOEYxhKbu3js03GobJOLw==", "cpu": [ "ia32" ], @@ -1755,9 +872,9 @@ } }, "node_modules/@node-rs/argon2-win32-x64-msvc": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-win32-x64-msvc/-/argon2-win32-x64-msvc-1.7.2.tgz", - "integrity": "sha512-v0h53XUc7hNgWiWi0qcMcHvj9/kwuItI9NwLK4C+gtzT3UB0cedhfIL8HFMKThMXasy41ZdbpCF2Bi0kJoLNEg==", + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-win32-x64-msvc/-/argon2-win32-x64-msvc-1.8.3.tgz", + "integrity": "sha512-ILlrRThdbp5xNR5gwYM2ic1n/vG5rJ8dQZ+YMRqksl+lnTJ/6FDe5BOyIhiPtiDwlCiCtUA+1NxpDB9KlUCAIA==", "cpu": [ "x64" ], @@ -1770,9 +887,9 @@ } }, "node_modules/@node-rs/bcrypt": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/@node-rs/bcrypt/-/bcrypt-1.9.2.tgz", - "integrity": "sha512-FKUo9iCSIti+ldwoOlY1ztyIFhZxEgT7jZ/UCt/9bg1rLmNdbQQD2JKIMImDCqmTWuLPY4ZF4Q5MyOMIfDCd8Q==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@node-rs/bcrypt/-/bcrypt-1.9.0.tgz", + "integrity": "sha512-u2OlIxW264bFUfvbFqDz9HZKFjwe8FHFtn7T/U8mYjPZ7DWYpbUB+/dkW/QgYfMSfR0ejkyuWaBBe0coW7/7ig==", "engines": { "node": ">= 10" }, @@ -1781,26 +898,26 @@ "url": "https://github.com/sponsors/Brooooooklyn" }, "optionalDependencies": { - "@node-rs/bcrypt-android-arm-eabi": "1.9.2", - "@node-rs/bcrypt-android-arm64": "1.9.2", - "@node-rs/bcrypt-darwin-arm64": "1.9.2", - "@node-rs/bcrypt-darwin-x64": "1.9.2", - "@node-rs/bcrypt-freebsd-x64": "1.9.2", - "@node-rs/bcrypt-linux-arm-gnueabihf": "1.9.2", - "@node-rs/bcrypt-linux-arm64-gnu": "1.9.2", - "@node-rs/bcrypt-linux-arm64-musl": "1.9.2", - "@node-rs/bcrypt-linux-x64-gnu": "1.9.2", - "@node-rs/bcrypt-linux-x64-musl": "1.9.2", - "@node-rs/bcrypt-wasm32-wasi": "1.9.2", - "@node-rs/bcrypt-win32-arm64-msvc": "1.9.2", - "@node-rs/bcrypt-win32-ia32-msvc": "1.9.2", - "@node-rs/bcrypt-win32-x64-msvc": "1.9.2" + "@node-rs/bcrypt-android-arm-eabi": "1.9.0", + "@node-rs/bcrypt-android-arm64": "1.9.0", + "@node-rs/bcrypt-darwin-arm64": "1.9.0", + "@node-rs/bcrypt-darwin-x64": "1.9.0", + "@node-rs/bcrypt-freebsd-x64": "1.9.0", + "@node-rs/bcrypt-linux-arm-gnueabihf": "1.9.0", + "@node-rs/bcrypt-linux-arm64-gnu": "1.9.0", + "@node-rs/bcrypt-linux-arm64-musl": "1.9.0", + "@node-rs/bcrypt-linux-x64-gnu": "1.9.0", + "@node-rs/bcrypt-linux-x64-musl": "1.9.0", + "@node-rs/bcrypt-wasm32-wasi": "1.9.0", + "@node-rs/bcrypt-win32-arm64-msvc": "1.9.0", + "@node-rs/bcrypt-win32-ia32-msvc": "1.9.0", + "@node-rs/bcrypt-win32-x64-msvc": "1.9.0" } }, "node_modules/@node-rs/bcrypt-android-arm-eabi": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-android-arm-eabi/-/bcrypt-android-arm-eabi-1.9.2.tgz", - "integrity": "sha512-er/Q2khwpan9pczvTTqY/DJE4UU65u31xd0NkZlHUTKyB7djRhWfzoGexGx2GN+k831/RR3U8kKE/8QUHeO3hQ==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-android-arm-eabi/-/bcrypt-android-arm-eabi-1.9.0.tgz", + "integrity": "sha512-nOCFISGtnodGHNiLrG0WYLWr81qQzZKYfmwHc7muUeq+KY0sQXyHOwZk9OuNQAWv/lnntmtbwkwT0QNEmOyLvA==", "cpu": [ "arm" ], @@ -1813,9 +930,9 @@ } }, "node_modules/@node-rs/bcrypt-android-arm64": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-android-arm64/-/bcrypt-android-arm64-1.9.2.tgz", - "integrity": "sha512-OUYatOEG5vbLbF73q2TC8UqrDO81zUQxnaFD/OAB1hcm6J+ur0zJ8E53c35/DIqkTp7JarPMraC4rouJ2ugN4w==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-android-arm64/-/bcrypt-android-arm64-1.9.0.tgz", + "integrity": "sha512-+ZrIAtigVmjYkqZQTThHVlz0+TG6D+GDHWhVKvR2DifjtqJ0i+mb9gjo++hN+fWEQdWNGxKCiBBjwgT4EcXd6A==", "cpu": [ "arm64" ], @@ -1828,9 +945,9 @@ } }, "node_modules/@node-rs/bcrypt-darwin-arm64": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-darwin-arm64/-/bcrypt-darwin-arm64-1.9.2.tgz", - "integrity": "sha512-svJKsGbzMAxOB5oluOYneN4YkKUy26WSMgm3KOIhgoX30IeMilj+2jFN/5qrI0oDZ0Iczb3XyL5DuZFtEkdP8A==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-darwin-arm64/-/bcrypt-darwin-arm64-1.9.0.tgz", + "integrity": "sha512-CQiS+F9Pa0XozvkXR1g7uXE9QvBOPOplDg0iCCPRYTN9PqA5qYxhwe48G3o+v2UeQceNRrbnEtWuANm7JRqIhw==", "cpu": [ "arm64" ], @@ -1843,9 +960,9 @@ } }, "node_modules/@node-rs/bcrypt-darwin-x64": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-darwin-x64/-/bcrypt-darwin-x64-1.9.2.tgz", - "integrity": "sha512-9OrySjBi/rWix8NZWD/TrNbNcwMY0pAiMHdL09aJnJ07uPih83GGh1pq4UHCYFCMy7iTX8swOmDlGBUImkOZbg==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-darwin-x64/-/bcrypt-darwin-x64-1.9.0.tgz", + "integrity": "sha512-4pTKGawYd7sNEjdJ7R/R67uwQH1VvwPZ0SSUMmeNHbxD5QlwAPXdDH11q22uzVXsvNFZ6nGQBg8No5OUGpx6Ug==", "cpu": [ "x64" ], @@ -1858,9 +975,9 @@ } }, "node_modules/@node-rs/bcrypt-freebsd-x64": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-freebsd-x64/-/bcrypt-freebsd-x64-1.9.2.tgz", - "integrity": "sha512-/djXV71RO6g5L1mI2pVvmp3x3pH7G4uKI3ODG1JBIXoz334oOcCMh40sB0uq0ljP8WEadker01p4T1rJE98fpg==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-freebsd-x64/-/bcrypt-freebsd-x64-1.9.0.tgz", + "integrity": "sha512-UmWzySX4BJhT/B8xmTru6iFif3h0Rpx3TqxRLCcbgmH43r7k5/9QuhpiyzpvKGpKHJCFNm4F3rC2wghvw5FCIg==", "cpu": [ "x64" ], @@ -1873,9 +990,9 @@ } }, "node_modules/@node-rs/bcrypt-linux-arm-gnueabihf": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-linux-arm-gnueabihf/-/bcrypt-linux-arm-gnueabihf-1.9.2.tgz", - "integrity": "sha512-F7wP950OTAooxEleUN4I2hqryGZK7hi1cSgRF13Wvbc597RFux35KiSxIXUA3mNt2DE7lV2PeceEtCOScaThWQ==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-linux-arm-gnueabihf/-/bcrypt-linux-arm-gnueabihf-1.9.0.tgz", + "integrity": "sha512-8qoX4PgBND2cVwsbajoAWo3NwdfJPEXgpCsZQZURz42oMjbGyhhSYbovBCskGU3EBLoC8RA2B1jFWooeYVn5BA==", "cpu": [ "arm" ], @@ -1888,9 +1005,9 @@ } }, "node_modules/@node-rs/bcrypt-linux-arm64-gnu": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-linux-arm64-gnu/-/bcrypt-linux-arm64-gnu-1.9.2.tgz", - "integrity": "sha512-MehG+yQ0TgKMgKR1rO4hdvHkVsTM91Cof8qI9EJlS5+7+QSwfFA5O0zGwCkISD7bsyauJ5uJgcByGjpEobAHOg==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-linux-arm64-gnu/-/bcrypt-linux-arm64-gnu-1.9.0.tgz", + "integrity": "sha512-TuAC6kx0SbcIA4mSEWPi+OCcDjTQUMl213v5gMNlttF+D4ieIZx6pPDGTaMO6M2PDHTeCG0CBzZl0Lu+9b0c7Q==", "cpu": [ "arm64" ], @@ -1903,9 +1020,9 @@ } }, "node_modules/@node-rs/bcrypt-linux-arm64-musl": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-linux-arm64-musl/-/bcrypt-linux-arm64-musl-1.9.2.tgz", - "integrity": "sha512-PRZTAJjOwKEGsIhmBvfNh81So+wGl4QyCFAt23j+KwBujLStjC0N3YaqtTlWVKG9tcriPtmMYiAQtXWIyIgg/w==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-linux-arm64-musl/-/bcrypt-linux-arm64-musl-1.9.0.tgz", + "integrity": "sha512-/sIvKDABOI8QOEnLD7hIj02BVaNOuCIWBKvxcJOt8+TuwJ6zmY1UI5kSv9d99WbiHjTp97wtAUbZQwauU4b9ew==", "cpu": [ "arm64" ], @@ -1918,9 +1035,9 @@ } }, "node_modules/@node-rs/bcrypt-linux-x64-gnu": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-linux-x64-gnu/-/bcrypt-linux-x64-gnu-1.9.2.tgz", - "integrity": "sha512-5WfGO+O1m7nJ55WZ8XDq+ItA98Z4O7sNWsR+1nIj9YGT+Tx5zkQ2RBhpK6oCWZMluuZ0eKQ0FDmyP6K+2NDRIA==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-linux-x64-gnu/-/bcrypt-linux-x64-gnu-1.9.0.tgz", + "integrity": "sha512-DyyhDHDsLBsCKz1tZ1hLvUZSc1DK0FU0v52jK6IBQxrj24WscSU9zZe7ie/V9kdmA4Ep57BfpWX8Dsa2JxGdgQ==", "cpu": [ "x64" ], @@ -1933,9 +1050,9 @@ } }, "node_modules/@node-rs/bcrypt-linux-x64-musl": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-linux-x64-musl/-/bcrypt-linux-x64-musl-1.9.2.tgz", - "integrity": "sha512-VjCn0388p6PMCVUYHgYmHZrKNc7WwNJRr2WLJsHbQRGDOKbpNL6YolCjQxUchcSPDhzwrq1cIdy4j0fpoXEsdw==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-linux-x64-musl/-/bcrypt-linux-x64-musl-1.9.0.tgz", + "integrity": "sha512-duIiuqQ+Lew8ASSAYm6ZRqcmfBGWwsi81XLUwz86a2HR7Qv6V4yc3ZAUQovAikhjCsIqe8C11JlAZSK6+PlXYg==", "cpu": [ "x64" ], @@ -1948,24 +1065,54 @@ } }, "node_modules/@node-rs/bcrypt-wasm32-wasi": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-wasm32-wasi/-/bcrypt-wasm32-wasi-1.9.2.tgz", - "integrity": "sha512-P06aHfMzm9makwU+nM7WA65yQnS1xuqJ8l/6I/LvXjnl+lfB3DtJ2B0CSLtjnUGpUgcHbWl5gEbNnTPxSAirjQ==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-wasm32-wasi/-/bcrypt-wasm32-wasi-1.9.0.tgz", + "integrity": "sha512-ylaGmn9Wjwv/D5lxtawttx3H6Uu2WTTR7lWlRHGT6Ga/MB1Vj4OjSGUW8G8zIVnKuXpGbZ92pgHlt4HUpSLctw==", "cpu": [ "wasm32" ], "optional": true, "dependencies": { - "@napi-rs/wasm-runtime": "^0.1.1" + "@emnapi/core": "^0.45.0", + "@emnapi/runtime": "^0.45.0", + "@tybys/wasm-util": "^0.8.1", + "memfs-browser": "^3.4.13000" }, "engines": { "node": ">=14.0.0" } }, + "node_modules/@node-rs/bcrypt-wasm32-wasi/node_modules/@emnapi/core": { + "version": "0.45.0", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-0.45.0.tgz", + "integrity": "sha512-DPWjcUDQkCeEM4VnljEOEcXdAD7pp8zSZsgOujk/LGIwCXWbXJngin+MO4zbH429lzeC3WbYLGjE2MaUOwzpyw==", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@node-rs/bcrypt-wasm32-wasi/node_modules/@emnapi/runtime": { + "version": "0.45.0", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-0.45.0.tgz", + "integrity": "sha512-Txumi3td7J4A/xTTwlssKieHKTGl3j4A1tglBx72auZ49YK7ePY6XZricgIg9mnZT4xPfA+UPCUdnhRuEFDL+w==", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@node-rs/bcrypt-wasm32-wasi/node_modules/@tybys/wasm-util": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.8.3.tgz", + "integrity": "sha512-Z96T/L6dUFFxgFJ+pQtkPpne9q7i6kIPYCFnQBHSgSPV9idTsKfIhCss0h5iM9irweZCatkrdeP8yi5uM1eX6Q==", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@node-rs/bcrypt-win32-arm64-msvc": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-win32-arm64-msvc/-/bcrypt-win32-arm64-msvc-1.9.2.tgz", - "integrity": "sha512-Iyo/Q5/eNw27VRd3mLBgh1b9b5fnT3QHTVwxv3Siv/MRAIfJXH/cTOe18qSwYQzNh0ZioW4yemFPYCWSZi7szA==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-win32-arm64-msvc/-/bcrypt-win32-arm64-msvc-1.9.0.tgz", + "integrity": "sha512-2h86gF7QFyEzODuDFml/Dp1MSJoZjxJ4yyT2Erf4NkwsiA5MqowUhUsorRwZhX6+2CtlGa7orbwi13AKMsYndw==", "cpu": [ "arm64" ], @@ -1978,9 +1125,9 @@ } }, "node_modules/@node-rs/bcrypt-win32-ia32-msvc": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-win32-ia32-msvc/-/bcrypt-win32-ia32-msvc-1.9.2.tgz", - "integrity": "sha512-6LHWMaPylyyHoS5863YpxAACVB8DWCxro5W6pQ4h8WKSgHpJp8Um9jphTdN0A2w45HZjUnfcFuiFFC+TbftjCw==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-win32-ia32-msvc/-/bcrypt-win32-ia32-msvc-1.9.0.tgz", + "integrity": "sha512-kqxalCvhs4FkN0+gWWfa4Bdy2NQAkfiqq/CEf6mNXC13RSV673Ev9V8sRlQyNpCHCNkeXfOT9pgoBdJmMs9muA==", "cpu": [ "ia32" ], @@ -1993,9 +1140,9 @@ } }, "node_modules/@node-rs/bcrypt-win32-x64-msvc": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-win32-x64-msvc/-/bcrypt-win32-x64-msvc-1.9.2.tgz", - "integrity": "sha512-vZ9T1MOaYkLO9FTyl28YX0SYJneiYTKNFgM8PUv8nas8xrD+7OzokA0fEtlNp6413T7IKSD/iG9qi8nTWsiyGg==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-win32-x64-msvc/-/bcrypt-win32-x64-msvc-1.9.0.tgz", + "integrity": "sha512-2y0Tuo6ZAT2Cz8V7DHulSlv1Bip3zbzeXyeur+uR25IRNYXKvI/P99Zl85Fbuu/zzYAZRLLlGTRe6/9IHofe/w==", "cpu": [ "x64" ], @@ -2044,6 +1191,33 @@ "resolved": "https://registry.npmjs.org/@open-draft/until/-/until-1.0.3.tgz", "integrity": "sha512-Aq58f5HiWdyDlFffbbSjAlv596h/cOnt2DO1w3DOC7OJ5EHs0hd/nycJfiu9RJbT6Yk6F1knnRRXNSpxoIVZ9Q==" }, + "node_modules/@oslojs/asn1": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@oslojs/asn1/-/asn1-1.0.0.tgz", + "integrity": "sha512-zw/wn0sj0j0QKbIXfIlnEcTviaCzYOY3V5rAyjR6YtOByFtJiT574+8p9Wlach0lZH9fddD4yb9laEAIl4vXQA==", + "dependencies": { + "@oslojs/binary": "1.0.0" + } + }, + "node_modules/@oslojs/binary": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@oslojs/binary/-/binary-1.0.0.tgz", + "integrity": "sha512-9RCU6OwXU6p67H4NODbuxv2S3eenuQ4/WFLrsq+K/k682xrznH5EVWA7N4VFk9VYVcbFtKqur5YQQZc0ySGhsQ==" + }, + "node_modules/@oslojs/crypto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@oslojs/crypto/-/crypto-1.0.1.tgz", + "integrity": "sha512-7n08G8nWjAr/Yu3vu9zzrd0L9XnrJfpMioQcvCMxBIiF5orECHe5/3J0jmXRVvgfqMm/+4oxlQ+Sq39COYLcNQ==", + "dependencies": { + "@oslojs/asn1": "1.0.0", + "@oslojs/binary": "1.0.0" + } + }, + "node_modules/@oslojs/encoding": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@oslojs/encoding/-/encoding-1.1.0.tgz", + "integrity": "sha512-70wQhgYmndg4GCPxPPxPGevRKqTIJ2Nh4OkiMWmDAVYsTQ+Ta7Sq+rPevXyXGdzr30/qZBnyOalCszoMxlyldQ==" + }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", @@ -2054,16 +1228,15 @@ } }, "node_modules/@polka/url": { - "version": "1.0.0-next.24", - "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.24.tgz", - "integrity": "sha512-2LuNTFBIO0m7kKIQvvPHN6UE63VjpmL9rnEEaOOaiSPbZK+zUOYIzBAWcED+3XYzhYsd/0mD57VdxAEqqV52CQ==" + "version": "1.0.0-next.28", + "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.28.tgz", + "integrity": "sha512-8LduaNlMZGwdZ6qWrKlfa+2M4gahzFkprZiAt2TF8uS0qQgBizKXpXURqvTJ4WtmupWxaLqjRb2UCTe72mu+Aw==" }, "node_modules/@prisma/client": { "version": "5.19.1", "resolved": "https://registry.npmjs.org/@prisma/client/-/client-5.19.1.tgz", "integrity": "sha512-x30GFguInsgt+4z5I4WbkZP2CGpotJMUXy+Gl/aaUjHn2o1DnLYNTA+q9XdYmAQZM8fIIkvUiA2NpgosM3fneg==", "hasInstallScript": true, - "license": "Apache-2.0", "engines": { "node": ">=16.13" }, @@ -2079,15 +1252,13 @@ "node_modules/@prisma/debug": { "version": "5.19.1", "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-5.19.1.tgz", - "integrity": "sha512-lAG6A6QnG2AskAukIEucYJZxxcSqKsMK74ZFVfCTOM/7UiyJQi48v6TQ47d6qKG3LbMslqOvnTX25dj/qvclGg==", - "license": "Apache-2.0" + "integrity": "sha512-lAG6A6QnG2AskAukIEucYJZxxcSqKsMK74ZFVfCTOM/7UiyJQi48v6TQ47d6qKG3LbMslqOvnTX25dj/qvclGg==" }, "node_modules/@prisma/engines": { "version": "5.19.1", "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-5.19.1.tgz", "integrity": "sha512-kR/PoxZDrfUmbbXqqb8SlBBgCjvGaJYMCOe189PEYzq9rKqitQ2fvT/VJ8PDSe8tTNxhc2KzsCfCAL+Iwm/7Cg==", "hasInstallScript": true, - "license": "Apache-2.0", "dependencies": { "@prisma/debug": "5.19.1", "@prisma/engines-version": "5.19.1-2.69d742ee20b815d88e17e54db4a2a7a3b30324e3", @@ -2098,14 +1269,12 @@ "node_modules/@prisma/engines-version": { "version": "5.19.1-2.69d742ee20b815d88e17e54db4a2a7a3b30324e3", "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-5.19.1-2.69d742ee20b815d88e17e54db4a2a7a3b30324e3.tgz", - "integrity": "sha512-xR6rt+z5LnNqTP5BBc+8+ySgf4WNMimOKXRn6xfNRDSpHvbOEmd7+qAOmzCrddEc4Cp8nFC0txU14dstjH7FXA==", - "license": "Apache-2.0" + "integrity": "sha512-xR6rt+z5LnNqTP5BBc+8+ySgf4WNMimOKXRn6xfNRDSpHvbOEmd7+qAOmzCrddEc4Cp8nFC0txU14dstjH7FXA==" }, "node_modules/@prisma/fetch-engine": { "version": "5.19.1", "resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-5.19.1.tgz", "integrity": "sha512-pCq74rtlOVJfn4pLmdJj+eI4P7w2dugOnnTXpRilP/6n5b2aZiA4ulJlE0ddCbTPkfHmOL9BfaRgA8o+1rfdHw==", - "license": "Apache-2.0", "dependencies": { "@prisma/debug": "5.19.1", "@prisma/engines-version": "5.19.1-2.69d742ee20b815d88e17e54db4a2a7a3b30324e3", @@ -2116,23 +1285,22 @@ "version": "5.19.1", "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-5.19.1.tgz", "integrity": "sha512-sCeoJ+7yt0UjnR+AXZL7vXlg5eNxaFOwC23h0KvW1YIXUoa7+W2ZcAUhoEQBmJTW4GrFqCuZ8YSP0mkDa4k3Zg==", - "license": "Apache-2.0", "dependencies": { "@prisma/debug": "5.19.1" } }, "node_modules/@remix-run/router": { - "version": "1.19.2", - "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.19.2.tgz", - "integrity": "sha512-baiMx18+IMuD1yyvOGaHM9QrVUPGGG0jC+z+IPHnRJWUAUvaKuWKyE8gjDj2rzv3sz9zOGoRSPgeBVHRhZnBlA==", + "version": "1.21.0", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.21.0.tgz", + "integrity": "sha512-xfSkCAchbdG5PnbrKqFWwia4Bi61nH+wm8wLEqfHDyp7Y3dZzgqS2itV8i4gAq9pC2HsTpwyBC6Ds8VHZ96JlA==", "engines": { "node": ">=14.0.0" } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.10.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.10.0.tgz", - "integrity": "sha512-/MeDQmcD96nVoRumKUljsYOLqfv1YFJps+0pTrb2Z9Nl/w5qNUysMaWQsrd1mvAlNT4yza1iVyIu4Q4AgF6V3A==", + "version": "4.30.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.30.1.tgz", + "integrity": "sha512-pSWY+EVt3rJ9fQ3IqlrEUtXh3cGqGtPDH1FQlNZehO2yYxCHEX1SPsz1M//NXwYfbTlcKr9WObLnJX9FsS9K1Q==", "cpu": [ "arm" ], @@ -2142,9 +1310,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.10.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.10.0.tgz", - "integrity": "sha512-lvu0jK97mZDJdpZKDnZI93I0Om8lSDaiPx3OiCk0RXn3E8CMPJNS/wxjAvSJJzhhZpfjXsjLWL8LnS6qET4VNQ==", + "version": "4.30.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.30.1.tgz", + "integrity": "sha512-/NA2qXxE3D/BRjOJM8wQblmArQq1YoBVJjrjoTSBS09jgUisq7bqxNHJ8kjCHeV21W/9WDGwJEWSN0KQ2mtD/w==", "cpu": [ "arm64" ], @@ -2154,9 +1322,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.10.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.10.0.tgz", - "integrity": "sha512-uFpayx8I8tyOvDkD7X6n0PriDRWxcqEjqgtlxnUA/G9oS93ur9aZ8c8BEpzFmsed1TH5WZNG5IONB8IiW90TQg==", + "version": "4.30.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.30.1.tgz", + "integrity": "sha512-r7FQIXD7gB0WJ5mokTUgUWPl0eYIH0wnxqeSAhuIwvnnpjdVB8cRRClyKLQr7lgzjctkbp5KmswWszlwYln03Q==", "cpu": [ "arm64" ], @@ -2166,9 +1334,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.10.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.10.0.tgz", - "integrity": "sha512-nIdCX03qFKoR/MwQegQBK+qZoSpO3LESurVAC6s6jazLA1Mpmgzo3Nj3H1vydXp/JM29bkCiuF7tDuToj4+U9Q==", + "version": "4.30.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.30.1.tgz", + "integrity": "sha512-x78BavIwSH6sqfP2xeI1hd1GpHL8J4W2BXcVM/5KYKoAD3nNsfitQhvWSw+TFtQTLZ9OmlF+FEInEHyubut2OA==", "cpu": [ "x64" ], @@ -2177,10 +1345,46 @@ "darwin" ] }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.30.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.30.1.tgz", + "integrity": "sha512-HYTlUAjbO1z8ywxsDFWADfTRfTIIy/oUlfIDmlHYmjUP2QRDTzBuWXc9O4CXM+bo9qfiCclmHk1x4ogBjOUpUQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.30.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.30.1.tgz", + "integrity": "sha512-1MEdGqogQLccphhX5myCJqeGNYTNcmTyaic9S7CG3JhwuIByJ7J05vGbZxsizQthP1xpVx7kd3o31eOogfEirw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "freebsd" + ] + }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.10.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.10.0.tgz", - "integrity": "sha512-Fz7a+y5sYhYZMQFRkOyCs4PLhICAnxRX/GnWYReaAoruUzuRtcf+Qnw+T0CoAWbHCuz2gBUwmWnUgQ67fb3FYw==", + "version": "4.30.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.30.1.tgz", + "integrity": "sha512-PaMRNBSqCx7K3Wc9QZkFx5+CX27WFpAMxJNiYGAXfmMIKC7jstlr32UhTgK6T07OtqR+wYlWm9IxzennjnvdJg==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.30.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.30.1.tgz", + "integrity": "sha512-B8Rcyj9AV7ZlEFqvB5BubG5iO6ANDsRKlhIxySXcF1axXYUyqwBok+XZPgIYGBgs7LDXfWfifxhw0Ik57T0Yug==", "cpu": [ "arm" ], @@ -2190,9 +1394,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.10.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.10.0.tgz", - "integrity": "sha512-yPtF9jIix88orwfTi0lJiqINnlWo6p93MtZEoaehZnmCzEmLL0eqjA3eGVeyQhMtxdV+Mlsgfwhh0+M/k1/V7Q==", + "version": "4.30.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.30.1.tgz", + "integrity": "sha512-hqVyueGxAj3cBKrAI4aFHLV+h0Lv5VgWZs9CUGqr1z0fZtlADVV1YPOij6AhcK5An33EXaxnDLmJdQikcn5NEw==", "cpu": [ "arm64" ], @@ -2202,9 +1406,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.10.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.10.0.tgz", - "integrity": "sha512-9GW9yA30ib+vfFiwjX+N7PnjTnCMiUffhWj4vkG4ukYv1kJ4T9gHNg8zw+ChsOccM27G9yXrEtMScf1LaCuoWQ==", + "version": "4.30.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.30.1.tgz", + "integrity": "sha512-i4Ab2vnvS1AE1PyOIGp2kXni69gU2DAUVt6FSXeIqUCPIR3ZlheMW3oP2JkukDfu3PsexYRbOiJrY+yVNSk9oA==", "cpu": [ "arm64" ], @@ -2213,10 +1417,34 @@ "linux" ] }, + "node_modules/@rollup/rollup-linux-loongarch64-gnu": { + "version": "4.30.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.30.1.tgz", + "integrity": "sha512-fARcF5g296snX0oLGkVxPmysetwUk2zmHcca+e9ObOovBR++9ZPOhqFUM61UUZ2EYpXVPN1redgqVoBB34nTpQ==", + "cpu": [ + "loong64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.30.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.30.1.tgz", + "integrity": "sha512-GLrZraoO3wVT4uFXh67ElpwQY0DIygxdv0BNW9Hkm3X34wu+BkqrDrkcsIapAY+N2ATEbvak0XQ9gxZtCIA5Rw==", + "cpu": [ + "ppc64" + ], + "optional": true, + "os": [ + "linux" + ] + }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.10.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.10.0.tgz", - "integrity": "sha512-X1ES+V4bMq2ws5fF4zHornxebNxMXye0ZZjUrzOrf7UMx1d6wMQtfcchZ8SqUnQPPHdOyOLW6fTcUiFgHFadRA==", + "version": "4.30.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.30.1.tgz", + "integrity": "sha512-0WKLaAUUHKBtll0wvOmh6yh3S0wSU9+yas923JIChfxOaaBarmb/lBKPF0w/+jTVozFnOXJeRGZ8NvOxvk/jcw==", "cpu": [ "riscv64" ], @@ -2225,10 +1453,22 @@ "linux" ] }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.30.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.30.1.tgz", + "integrity": "sha512-GWFs97Ruxo5Bt+cvVTQkOJ6TIx0xJDD/bMAOXWJg8TCSTEK8RnFeOeiFTxKniTc4vMIaWvCplMAFBt9miGxgkA==", + "cpu": [ + "s390x" + ], + "optional": true, + "os": [ + "linux" + ] + }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.10.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.10.0.tgz", - "integrity": "sha512-w/5OpT2EnI/Xvypw4FIhV34jmNqU5PZjZue2l2Y3ty1Ootm3SqhI+AmfhlUYGBTd9JnpneZCDnt3uNOiOBkMyw==", + "version": "4.30.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.30.1.tgz", + "integrity": "sha512-UtgGb7QGgXDIO+tqqJ5oZRGHsDLO8SlpE4MhqpY9Llpzi5rJMvrK6ZGhsRCST2abZdBqIBeXW6WPD5fGK5SDwg==", "cpu": [ "x64" ], @@ -2238,9 +1478,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.10.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.10.0.tgz", - "integrity": "sha512-q/meftEe3QlwQiGYxD9rWwB21DoKQ9Q8wA40of/of6yGHhZuGfZO0c3WYkN9dNlopHlNT3mf5BPsUSxoPuVQaw==", + "version": "4.30.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.30.1.tgz", + "integrity": "sha512-V9U8Ey2UqmQsBT+xTOeMzPzwDzyXmnAoO4edZhL7INkwQcaW1Ckv3WJX3qrrp/VHaDkEWIBWhRwP47r8cdrOow==", "cpu": [ "x64" ], @@ -2250,9 +1490,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.10.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.10.0.tgz", - "integrity": "sha512-NrR6667wlUfP0BHaEIKgYM/2va+Oj+RjZSASbBMnszM9k+1AmliRjHc3lJIiOehtSSjqYiO7R6KLNrWOX+YNSQ==", + "version": "4.30.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.30.1.tgz", + "integrity": "sha512-WabtHWiPaFF47W3PkHnjbmWawnX/aE57K47ZDT1BXTS5GgrBUEpvOzq0FI0V/UYzQJgdb8XlhVNH8/fwV8xDjw==", "cpu": [ "arm64" ], @@ -2262,9 +1502,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.10.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.10.0.tgz", - "integrity": "sha512-FV0Tpt84LPYDduIDcXvEC7HKtyXxdvhdAOvOeWMWbQNulxViH2O07QXkT/FffX4FqEI02jEbCJbr+YcuKdyyMg==", + "version": "4.30.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.30.1.tgz", + "integrity": "sha512-pxHAU+Zv39hLUTdQQHUVHf4P+0C47y/ZloorHpzs2SXMRqeAWmGghzAhfOlzFHHwjvgokdFAhC4V+6kC1lRRfw==", "cpu": [ "ia32" ], @@ -2274,9 +1514,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.10.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.10.0.tgz", - "integrity": "sha512-OZoJd+o5TaTSQeFFQ6WjFCiltiYVjIdsXxwu/XZ8qRpsvMQr4UsVrE5UyT9RIvsnuF47DqkJKhhVZ2Q9YW9IpQ==", + "version": "4.30.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.30.1.tgz", + "integrity": "sha512-D6qjsXGcvhTjv0kI4fU8tUuBDF/Ueee4SVX79VfNDXZa64TfCW1Slkb6Z7O1p7vflqZjcmOVdZlqf8gvJxc6og==", "cpu": [ "x64" ], @@ -2299,20 +1539,20 @@ } }, "node_modules/@tailwindcss/forms": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/@tailwindcss/forms/-/forms-0.5.7.tgz", - "integrity": "sha512-QE7X69iQI+ZXwldE+rzasvbJiyV/ju1FGHH0Qn2W3FKbuYtqp8LKcy6iSw79fVUT5/Vvf+0XgLCeYVG+UV6hOw==", + "version": "0.5.10", + "resolved": "https://registry.npmjs.org/@tailwindcss/forms/-/forms-0.5.10.tgz", + "integrity": "sha512-utI1ONF6uf/pPNO68kmN1b8rEwNXv3czukalo8VtJH8ksIkZXr3Q3VYudZLkCsDd4Wku120uF02hYK25XGPorw==", "dependencies": { "mini-svg-data-uri": "^1.2.3" }, "peerDependencies": { - "tailwindcss": ">=3.0.0 || >= 3.0.0-alpha.1" + "tailwindcss": ">=3.0.0 || >= 3.0.0-alpha.1 || >= 4.0.0-alpha.20 || >= 4.0.0-beta.1" } }, "node_modules/@tailwindcss/typography": { - "version": "0.5.10", - "resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.10.tgz", - "integrity": "sha512-Pe8BuPJQJd3FfRnm6H0ulKIGoMEQS+Vq01R6M5aCrFB/ccR/shT+0kXLjouGC1gFLm9hopTFN+DMP0pfwRWzPw==", + "version": "0.5.16", + "resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.16.tgz", + "integrity": "sha512-0wDLwCVF5V3x3b1SGXPCDcdsbDHMBe+lkFzBRaHeLvNi+nrrnZ1lA18u+OTWO8iSWU2GxUOCvlXtDuqftc1oiA==", "dependencies": { "lodash.castarray": "^4.4.0", "lodash.isplainobject": "^4.0.6", @@ -2320,7 +1560,7 @@ "postcss-selector-parser": "6.0.10" }, "peerDependencies": { - "tailwindcss": ">=3.0.0 || insiders" + "tailwindcss": ">=3.0.0 || insiders || >=4.0.0-alpha.20 || >=4.0.0-beta.1" } }, "node_modules/@tanstack/query-core": { @@ -2419,53 +1659,28 @@ "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==" }, "node_modules/@testing-library/jest-dom": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-6.4.2.tgz", - "integrity": "sha512-CzqH0AFymEMG48CpzXFriYYkOjk6ZGPCLMhW9e9jg3KMCn5OfJecF8GtGW7yGfR/IgCe3SX8BSwjdzI6BBbZLw==", + "version": "6.6.3", + "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-6.6.3.tgz", + "integrity": "sha512-IteBhl4XqYNkM54f4ejhLRJiZNqcSCoXUOG2CPK7qbD322KjQozM4kHQOfkG2oln9b9HTYqs+Sae8vBATubxxA==", "dependencies": { - "@adobe/css-tools": "^4.3.2", - "@babel/runtime": "^7.9.2", + "@adobe/css-tools": "^4.4.0", "aria-query": "^5.0.0", "chalk": "^3.0.0", "css.escape": "^1.5.1", "dom-accessibility-api": "^0.6.3", - "lodash": "^4.17.15", + "lodash": "^4.17.21", "redent": "^3.0.0" }, "engines": { "node": ">=14", "npm": ">=6", "yarn": ">=1" - }, - "peerDependencies": { - "@jest/globals": ">= 28", - "@types/bun": "latest", - "@types/jest": ">= 28", - "jest": ">= 28", - "vitest": ">= 0.32" - }, - "peerDependenciesMeta": { - "@jest/globals": { - "optional": true - }, - "@types/bun": { - "optional": true - }, - "@types/jest": { - "optional": true - }, - "jest": { - "optional": true - }, - "vitest": { - "optional": true - } } }, "node_modules/@testing-library/react": { - "version": "14.2.1", - "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-14.2.1.tgz", - "integrity": "sha512-sGdjws32ai5TLerhvzThYFbpnF9XtL65Cjf+gB0Dhr29BGqK+mAeN7SURSdu+eqgET4ANcWoC7FQpkaiGvBr+A==", + "version": "14.3.1", + "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-14.3.1.tgz", + "integrity": "sha512-H99XjUhWQw0lTgyMN05W3xQG1Nh4lq574D8keFf1dDoNTJgp66VbJozRaczoF+wsiaPJNt/TcnfpLGufGxSrZQ==", "dependencies": { "@babel/runtime": "^7.12.5", "@testing-library/dom": "^9.0.0", @@ -2488,15 +1703,15 @@ } }, "node_modules/@tsconfig/node18": { - "version": "18.2.2", - "resolved": "https://registry.npmjs.org/@tsconfig/node18/-/node18-18.2.2.tgz", - "integrity": "sha512-d6McJeGsuoRlwWZmVIeE8CUA27lu6jLjvv1JzqmpsytOYYbVi1tHZEnwCNVOXnj4pyLvneZlFlpXUK+X9wBWyw==", + "version": "18.2.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node18/-/node18-18.2.4.tgz", + "integrity": "sha512-5xxU8vVs9/FNcvm3gE07fPbn9tl6tqGGWA9tSlwsUEkBxtRnTsNmwrV8gasZ9F/EobaSv9+nu8AxUKccw77JpQ==", "dev": true }, "node_modules/@tybys/wasm-util": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.8.1.tgz", - "integrity": "sha512-GSsTwyBl4pIzsxAY5wroZdyQKyhXk0d8PCRZtrSZ2WEB1cBdrp2EgGBwHOGCZtIIPun/DL3+AykCv+J6fyRH4Q==", + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.9.0.tgz", + "integrity": "sha512-6+7nlbMVX/PVDCwaIQ8nTOPveOcFLSt8GcXdx8hD0bt39uWxYT88uXzqTd4fTvqta7oeUJqudepapKNt2DYJFw==", "optional": true, "dependencies": { "tslib": "^2.4.0" @@ -2549,9 +1764,9 @@ } }, "node_modules/@types/estree": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", - "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==" + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==" }, "node_modules/@types/express": { "version": "4.17.21", @@ -2566,9 +1781,9 @@ } }, "node_modules/@types/express-serve-static-core": { - "version": "4.17.43", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.43.tgz", - "integrity": "sha512-oaYtiBirUOPQGSWNGPWnzyAFJ0BP3cwvN4oWZQY+zUBwpVIGsKUkpBpSztp74drYcjavs7SKFZ4DX1V2QeN8rg==", + "version": "4.19.6", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.6.tgz", + "integrity": "sha512-N4LZ2xG7DatVqhCZzOGb1Yi5lMbXSZcmdLDe9EzSndPV2HpWYWzRbaerl2n27irrm94EPpprqa8KpskPT085+A==", "dev": true, "dependencies": { "@types/node": "*", @@ -2600,22 +1815,22 @@ "integrity": "sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==" }, "node_modules/@types/node": { - "version": "20.11.17", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.17.tgz", - "integrity": "sha512-QmgQZGWu1Yw9TDyAP9ZzpFJKynYNeOvwMJmaxABfieQoVoiVOS6MN1WSpqpRcbeA5+RW82kraAVxCCJg+780Qw==", + "version": "22.10.6", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.6.tgz", + "integrity": "sha512-qNiuwC4ZDAUNcY47xgaSuS92cjf8JbSUoaKS77bmLG1rU7MlATVSiw/IlrjtIyyskXBZ8KkNfjK/P5na7rgXbQ==", "dependencies": { - "undici-types": "~5.26.4" + "undici-types": "~6.20.0" } }, "node_modules/@types/prop-types": { - "version": "15.7.11", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.11.tgz", - "integrity": "sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng==" + "version": "15.7.14", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.14.tgz", + "integrity": "sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ==" }, "node_modules/@types/qs": { - "version": "6.9.11", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.11.tgz", - "integrity": "sha512-oGk0gmhnEJK4Yyk+oI7EfXsLayXatCWPHary1MtcmbAifkobT9cM9yutG/hZKIseOU0MqbIwQ/u2nn/Gb+ltuQ==", + "version": "6.9.18", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.18.tgz", + "integrity": "sha512-kK7dgTYDyGqS+e2Q4aK9X3D7q234CIZ1Bv0q/7Z5IwRDoADNU81xXJK/YVyLbLTZCoIwUoDoffFeF+p/eIklAA==", "dev": true }, "node_modules/@types/range-parser": { @@ -2625,28 +1840,22 @@ "dev": true }, "node_modules/@types/react": { - "version": "18.2.55", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.55.tgz", - "integrity": "sha512-Y2Tz5P4yz23brwm2d7jNon39qoAtMMmalOQv6+fEFt1mT+FcM3D841wDpoUvFXhaYenuROCy3FZYqdTjM7qVyA==", + "version": "18.3.18", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.18.tgz", + "integrity": "sha512-t4yC+vtgnkYjNSKlFx1jkAhH8LgTo2N/7Qvi83kdEaUtMDiwpbLAktKDaAMlRcJ5eSxZkH74eEGt1ky31d7kfQ==", "dependencies": { "@types/prop-types": "*", - "@types/scheduler": "*", "csstype": "^3.0.2" } }, "node_modules/@types/react-dom": { - "version": "18.2.19", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.19.tgz", - "integrity": "sha512-aZvQL6uUbIJpjZk4U8JZGbau9KDeAwMfmhyWorxgBkqDIEf6ROjRozcmPIicqsUwPUjbkDfHKgGee1Lq65APcA==", - "dependencies": { - "@types/react": "*" + "version": "18.3.5", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.5.tgz", + "integrity": "sha512-P4t6saawp+b/dFrUr2cvkVsfvPguwsxtH6dNIYRllMsefqFzkZk5UIjzyDOv5g1dXIPdG4Sp1yCR4Z6RCUsG/Q==", + "peerDependencies": { + "@types/react": "^18.0.0" } }, - "node_modules/@types/scheduler": { - "version": "0.16.8", - "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.8.tgz", - "integrity": "sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A==" - }, "node_modules/@types/send": { "version": "0.17.4", "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", @@ -2658,31 +1867,31 @@ } }, "node_modules/@types/serve-static": { - "version": "1.15.5", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.5.tgz", - "integrity": "sha512-PDRk21MnK70hja/YF8AHfC7yIsiQHn1rcXx7ijCFBX/k+XQJhQT/gw3xekXKJvx+5SXaMMS8oqQy09Mzvz2TuQ==", + "version": "1.15.7", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.7.tgz", + "integrity": "sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==", "dev": true, "dependencies": { "@types/http-errors": "*", - "@types/mime": "*", - "@types/node": "*" + "@types/node": "*", + "@types/send": "*" } }, "node_modules/@types/set-cookie-parser": { - "version": "2.4.7", - "resolved": "https://registry.npmjs.org/@types/set-cookie-parser/-/set-cookie-parser-2.4.7.tgz", - "integrity": "sha512-+ge/loa0oTozxip6zmhRIk8Z/boU51wl9Q6QdLZcokIGMzY5lFXYy/x7Htj2HTC6/KZP1hUbZ1ekx8DYXICvWg==", + "version": "2.4.10", + "resolved": "https://registry.npmjs.org/@types/set-cookie-parser/-/set-cookie-parser-2.4.10.tgz", + "integrity": "sha512-GGmQVGpQWUe5qglJozEjZV/5dyxbOOZ0LHe/lqyWssB88Y4svNfst0uqBVscdDeIKl5Jy5+aPSvy7mI9tYRguw==", "dependencies": { "@types/node": "*" } }, "node_modules/@vitest/expect": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-1.2.2.tgz", - "integrity": "sha512-3jpcdPAD7LwHUUiT2pZTj2U82I2Tcgg2oVPvKxhn6mDI2On6tfvPQTjAI4628GUGDZrCm4Zna9iQHm5cEexOAg==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-1.6.0.tgz", + "integrity": "sha512-ixEvFVQjycy/oNgHjqsL6AZCDduC+tflRluaHIzKIsdbzkLn2U/iBnVeJwB6HsIjQBdfMR8Z0tRxKUsvFJEeWQ==", "dependencies": { - "@vitest/spy": "1.2.2", - "@vitest/utils": "1.2.2", + "@vitest/spy": "1.6.0", + "@vitest/utils": "1.6.0", "chai": "^4.3.10" }, "funding": { @@ -2690,11 +1899,11 @@ } }, "node_modules/@vitest/runner": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-1.2.2.tgz", - "integrity": "sha512-JctG7QZ4LSDXr5CsUweFgcpEvrcxOV1Gft7uHrvkQ+fsAVylmWQvnaAr/HDp3LAH1fztGMQZugIheTWjaGzYIg==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-1.6.0.tgz", + "integrity": "sha512-P4xgwPjwesuBiHisAVz/LSSZtDjOTPYZVmNAnpHHSR6ONrf8eCJOFRvUwdHn30F5M1fxhqtl7QZQUk2dprIXAg==", "dependencies": { - "@vitest/utils": "1.2.2", + "@vitest/utils": "1.6.0", "p-limit": "^5.0.0", "pathe": "^1.1.1" }, @@ -2703,9 +1912,9 @@ } }, "node_modules/@vitest/snapshot": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-1.2.2.tgz", - "integrity": "sha512-SmGY4saEw1+bwE1th6S/cZmPxz/Q4JWsl7LvbQIky2tKE35US4gd0Mjzqfr84/4OD0tikGWaWdMja/nWL5NIPA==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-1.6.0.tgz", + "integrity": "sha512-+Hx43f8Chus+DCmygqqfetcAZrDJwvTj0ymqjQq4CvmpKFSTVteEOBzCusu1x2tt4OJcvBflyHUE0DZSLgEMtQ==", "dependencies": { "magic-string": "^0.30.5", "pathe": "^1.1.1", @@ -2740,14 +1949,14 @@ } }, "node_modules/@vitest/snapshot/node_modules/react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==" }, "node_modules/@vitest/spy": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-1.2.2.tgz", - "integrity": "sha512-k9Gcahssw8d7X3pSLq3e3XEu/0L78mUkCjivUqCQeXJm9clfXR/Td8+AP+VC1O6fKPIDLcHDTAmBOINVuv6+7g==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-1.6.0.tgz", + "integrity": "sha512-leUTap6B/cqi/bQkXUu6bQV5TZPx7pmMBKBQiI0rJA8c3pB56ZsaTbREnF7CJfmvAS4V2cXIBAh/3rVwrrCYgw==", "dependencies": { "tinyspy": "^2.2.0" }, @@ -2756,11 +1965,11 @@ } }, "node_modules/@vitest/ui": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@vitest/ui/-/ui-1.2.2.tgz", - "integrity": "sha512-CG+5fa8lyoBr+9i+UZGS31Qw81v33QlD10uecHxN2CLJVN+jLnqx4pGzGvFFeJ7jSnUCT0AlbmVWY6fU6NJZmw==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@vitest/ui/-/ui-1.6.0.tgz", + "integrity": "sha512-k3Lyo+ONLOgylctiGovRKy7V4+dIN2yxstX3eY5cWFXH6WP+ooVX79YSyi0GagdTQzLmT43BF27T0s6dOIPBXA==", "dependencies": { - "@vitest/utils": "1.2.2", + "@vitest/utils": "1.6.0", "fast-glob": "^3.3.2", "fflate": "^0.8.1", "flatted": "^3.2.9", @@ -2772,13 +1981,13 @@ "url": "https://opencollective.com/vitest" }, "peerDependencies": { - "vitest": "^1.0.0" + "vitest": "1.6.0" } }, "node_modules/@vitest/utils": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-1.2.2.tgz", - "integrity": "sha512-WKITBHLsBHlpjnDQahr+XK6RE7MiAsgrIkr0pGhQ9ygoxBfUeG0lUG5iLlzqjmKSlBv3+j5EGsriBzh+C3Tq9g==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-1.6.0.tgz", + "integrity": "sha512-21cPiuGMoMZwiOHa2i4LXkMkMkCGzA+MVFV70jRwHo95dL4x/ts5GZhML1QWuy7yfp3WzK3lRvZi3JnXTYqrBw==", "dependencies": { "diff-sequences": "^29.6.3", "estree-walker": "^3.0.3", @@ -2814,9 +2023,9 @@ } }, "node_modules/@vitest/utils/node_modules/react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==" }, "node_modules/@xmldom/xmldom": { "version": "0.8.10", @@ -2851,9 +2060,9 @@ } }, "node_modules/acorn": { - "version": "8.11.3", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", - "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", + "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", "bin": { "acorn": "bin/acorn" }, @@ -2871,9 +2080,12 @@ } }, "node_modules/acorn-walk": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.2.tgz", - "integrity": "sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==", + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", + "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", + "dependencies": { + "acorn": "^8.11.0" + }, "engines": { "node": ">=0.4.0" } @@ -2890,11 +2102,11 @@ } }, "node_modules/agent-base/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", "dependencies": { - "ms": "2.1.2" + "ms": "^2.1.3" }, "engines": { "node": ">=6.0" @@ -2906,9 +2118,9 @@ } }, "node_modules/agent-base/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, "node_modules/aggregate-error": { "version": "3.1.0", @@ -2948,9 +2160,9 @@ } }, "node_modules/ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", "engines": { "node": ">=12" }, @@ -2990,16 +2202,32 @@ "version": "1.9.2", "resolved": "https://registry.npmjs.org/arctic/-/arctic-1.9.2.tgz", "integrity": "sha512-VTnGpYx+ypboJdNrWnK17WeD7zN/xSCHnpecd5QYsBfVZde/5i+7DJ1wrf/ioSDMiEjagXmyNWAE3V2C9f1hNg==", - "license": "MIT", "dependencies": { "oslo": "1.2.0" } }, + "node_modules/arctic/node_modules/@emnapi/core": { + "version": "0.45.0", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-0.45.0.tgz", + "integrity": "sha512-DPWjcUDQkCeEM4VnljEOEcXdAD7pp8zSZsgOujk/LGIwCXWbXJngin+MO4zbH429lzeC3WbYLGjE2MaUOwzpyw==", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/arctic/node_modules/@emnapi/runtime": { + "version": "0.45.0", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-0.45.0.tgz", + "integrity": "sha512-Txumi3td7J4A/xTTwlssKieHKTGl3j4A1tglBx72auZ49YK7ePY6XZricgIg9mnZT4xPfA+UPCUdnhRuEFDL+w==", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/arctic/node_modules/@node-rs/argon2": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/@node-rs/argon2/-/argon2-1.7.0.tgz", "integrity": "sha512-zfULc+/tmcWcxn+nHkbyY8vP3+MpEqKORbszt4UkpqZgBgDAAIYvuDN/zukfTgdmo6tmJKKVfzigZOPk4LlIog==", - "license": "MIT", "engines": { "node": ">= 10" }, @@ -3027,7 +2255,6 @@ "cpu": [ "arm" ], - "license": "MIT", "optional": true, "os": [ "android" @@ -3043,7 +2270,6 @@ "cpu": [ "arm64" ], - "license": "MIT", "optional": true, "os": [ "android" @@ -3059,7 +2285,6 @@ "cpu": [ "arm64" ], - "license": "MIT", "optional": true, "os": [ "darwin" @@ -3075,7 +2300,6 @@ "cpu": [ "x64" ], - "license": "MIT", "optional": true, "os": [ "darwin" @@ -3091,7 +2315,6 @@ "cpu": [ "x64" ], - "license": "MIT", "optional": true, "os": [ "freebsd" @@ -3107,7 +2330,6 @@ "cpu": [ "arm" ], - "license": "MIT", "optional": true, "os": [ "linux" @@ -3123,7 +2345,6 @@ "cpu": [ "arm64" ], - "license": "MIT", "optional": true, "os": [ "linux" @@ -3135,267 +2356,10 @@ "node_modules/arctic/node_modules/@node-rs/argon2-linux-arm64-musl": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/@node-rs/argon2-linux-arm64-musl/-/argon2-linux-arm64-musl-1.7.0.tgz", - "integrity": "sha512-BKWS8iVconhE3jrb9mj6t1J9vwUqQPpzCbUKxfTGJfc+kNL58F1SXHBoe2cDYGnHrFEHTY0YochzXoAfm4Dm/A==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/arctic/node_modules/@node-rs/argon2-linux-x64-gnu": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-linux-x64-gnu/-/argon2-linux-x64-gnu-1.7.0.tgz", - "integrity": "sha512-EmgqZOlf4Jurk/szW1iTsVISx25bKksVC5uttJDUloTgsAgIGReCpUUO1R24pBhu9ESJa47iv8NSf3yAfGv6jQ==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/arctic/node_modules/@node-rs/argon2-linux-x64-musl": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-linux-x64-musl/-/argon2-linux-x64-musl-1.7.0.tgz", - "integrity": "sha512-/o1efYCYIxjfuoRYyBTi2Iy+1iFfhqHCvvVsnjNSgO1xWiWrX0Rrt/xXW5Zsl7vS2Y+yu8PL8KFWRzZhaVxfKA==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/arctic/node_modules/@node-rs/argon2-wasm32-wasi": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-wasm32-wasi/-/argon2-wasm32-wasi-1.7.0.tgz", - "integrity": "sha512-Evmk9VcxqnuwQftfAfYEr6YZYSPLzmKUsbFIMep5nTt9PT4XYRFAERj7wNYp+rOcBenF3X4xoB+LhwcOMTNE5w==", - "cpu": [ - "wasm32" - ], - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/core": "^0.45.0", - "@emnapi/runtime": "^0.45.0", - "@tybys/wasm-util": "^0.8.1", - "memfs-browser": "^3.4.13000" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/arctic/node_modules/@node-rs/argon2-win32-arm64-msvc": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-win32-arm64-msvc/-/argon2-win32-arm64-msvc-1.7.0.tgz", - "integrity": "sha512-qgsU7T004COWWpSA0tppDqDxbPLgg8FaU09krIJ7FBl71Sz8SFO40h7fDIjfbTT5w7u6mcaINMQ5bSHu75PCaA==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/arctic/node_modules/@node-rs/argon2-win32-ia32-msvc": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-win32-ia32-msvc/-/argon2-win32-ia32-msvc-1.7.0.tgz", - "integrity": "sha512-JGafwWYQ/HpZ3XSwP4adQ6W41pRvhcdXvpzIWtKvX+17+xEXAe2nmGWM6s27pVkg1iV2ZtoYLRDkOUoGqZkCcg==", - "cpu": [ - "ia32" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/arctic/node_modules/@node-rs/argon2-win32-x64-msvc": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-win32-x64-msvc/-/argon2-win32-x64-msvc-1.7.0.tgz", - "integrity": "sha512-9oq4ShyFakw8AG3mRls0AoCpxBFcimYx7+jvXeAf2OqKNO+mSA6eZ9z7KQeVCi0+SOEUYxMGf5UiGiDb9R6+9Q==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/arctic/node_modules/@node-rs/bcrypt": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@node-rs/bcrypt/-/bcrypt-1.9.0.tgz", - "integrity": "sha512-u2OlIxW264bFUfvbFqDz9HZKFjwe8FHFtn7T/U8mYjPZ7DWYpbUB+/dkW/QgYfMSfR0ejkyuWaBBe0coW7/7ig==", - "license": "MIT", - "engines": { - "node": ">= 10" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Brooooooklyn" - }, - "optionalDependencies": { - "@node-rs/bcrypt-android-arm-eabi": "1.9.0", - "@node-rs/bcrypt-android-arm64": "1.9.0", - "@node-rs/bcrypt-darwin-arm64": "1.9.0", - "@node-rs/bcrypt-darwin-x64": "1.9.0", - "@node-rs/bcrypt-freebsd-x64": "1.9.0", - "@node-rs/bcrypt-linux-arm-gnueabihf": "1.9.0", - "@node-rs/bcrypt-linux-arm64-gnu": "1.9.0", - "@node-rs/bcrypt-linux-arm64-musl": "1.9.0", - "@node-rs/bcrypt-linux-x64-gnu": "1.9.0", - "@node-rs/bcrypt-linux-x64-musl": "1.9.0", - "@node-rs/bcrypt-wasm32-wasi": "1.9.0", - "@node-rs/bcrypt-win32-arm64-msvc": "1.9.0", - "@node-rs/bcrypt-win32-ia32-msvc": "1.9.0", - "@node-rs/bcrypt-win32-x64-msvc": "1.9.0" - } - }, - "node_modules/arctic/node_modules/@node-rs/bcrypt-android-arm-eabi": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-android-arm-eabi/-/bcrypt-android-arm-eabi-1.9.0.tgz", - "integrity": "sha512-nOCFISGtnodGHNiLrG0WYLWr81qQzZKYfmwHc7muUeq+KY0sQXyHOwZk9OuNQAWv/lnntmtbwkwT0QNEmOyLvA==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/arctic/node_modules/@node-rs/bcrypt-android-arm64": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-android-arm64/-/bcrypt-android-arm64-1.9.0.tgz", - "integrity": "sha512-+ZrIAtigVmjYkqZQTThHVlz0+TG6D+GDHWhVKvR2DifjtqJ0i+mb9gjo++hN+fWEQdWNGxKCiBBjwgT4EcXd6A==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/arctic/node_modules/@node-rs/bcrypt-darwin-arm64": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-darwin-arm64/-/bcrypt-darwin-arm64-1.9.0.tgz", - "integrity": "sha512-CQiS+F9Pa0XozvkXR1g7uXE9QvBOPOplDg0iCCPRYTN9PqA5qYxhwe48G3o+v2UeQceNRrbnEtWuANm7JRqIhw==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/arctic/node_modules/@node-rs/bcrypt-darwin-x64": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-darwin-x64/-/bcrypt-darwin-x64-1.9.0.tgz", - "integrity": "sha512-4pTKGawYd7sNEjdJ7R/R67uwQH1VvwPZ0SSUMmeNHbxD5QlwAPXdDH11q22uzVXsvNFZ6nGQBg8No5OUGpx6Ug==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/arctic/node_modules/@node-rs/bcrypt-freebsd-x64": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-freebsd-x64/-/bcrypt-freebsd-x64-1.9.0.tgz", - "integrity": "sha512-UmWzySX4BJhT/B8xmTru6iFif3h0Rpx3TqxRLCcbgmH43r7k5/9QuhpiyzpvKGpKHJCFNm4F3rC2wghvw5FCIg==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/arctic/node_modules/@node-rs/bcrypt-linux-arm-gnueabihf": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-linux-arm-gnueabihf/-/bcrypt-linux-arm-gnueabihf-1.9.0.tgz", - "integrity": "sha512-8qoX4PgBND2cVwsbajoAWo3NwdfJPEXgpCsZQZURz42oMjbGyhhSYbovBCskGU3EBLoC8RA2B1jFWooeYVn5BA==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/arctic/node_modules/@node-rs/bcrypt-linux-arm64-gnu": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-linux-arm64-gnu/-/bcrypt-linux-arm64-gnu-1.9.0.tgz", - "integrity": "sha512-TuAC6kx0SbcIA4mSEWPi+OCcDjTQUMl213v5gMNlttF+D4ieIZx6pPDGTaMO6M2PDHTeCG0CBzZl0Lu+9b0c7Q==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/arctic/node_modules/@node-rs/bcrypt-linux-arm64-musl": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-linux-arm64-musl/-/bcrypt-linux-arm64-musl-1.9.0.tgz", - "integrity": "sha512-/sIvKDABOI8QOEnLD7hIj02BVaNOuCIWBKvxcJOt8+TuwJ6zmY1UI5kSv9d99WbiHjTp97wtAUbZQwauU4b9ew==", + "integrity": "sha512-BKWS8iVconhE3jrb9mj6t1J9vwUqQPpzCbUKxfTGJfc+kNL58F1SXHBoe2cDYGnHrFEHTY0YochzXoAfm4Dm/A==", "cpu": [ "arm64" ], - "license": "MIT", "optional": true, "os": [ "linux" @@ -3404,14 +2368,13 @@ "node": ">= 10" } }, - "node_modules/arctic/node_modules/@node-rs/bcrypt-linux-x64-gnu": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-linux-x64-gnu/-/bcrypt-linux-x64-gnu-1.9.0.tgz", - "integrity": "sha512-DyyhDHDsLBsCKz1tZ1hLvUZSc1DK0FU0v52jK6IBQxrj24WscSU9zZe7ie/V9kdmA4Ep57BfpWX8Dsa2JxGdgQ==", + "node_modules/arctic/node_modules/@node-rs/argon2-linux-x64-gnu": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-linux-x64-gnu/-/argon2-linux-x64-gnu-1.7.0.tgz", + "integrity": "sha512-EmgqZOlf4Jurk/szW1iTsVISx25bKksVC5uttJDUloTgsAgIGReCpUUO1R24pBhu9ESJa47iv8NSf3yAfGv6jQ==", "cpu": [ "x64" ], - "license": "MIT", "optional": true, "os": [ "linux" @@ -3420,14 +2383,13 @@ "node": ">= 10" } }, - "node_modules/arctic/node_modules/@node-rs/bcrypt-linux-x64-musl": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-linux-x64-musl/-/bcrypt-linux-x64-musl-1.9.0.tgz", - "integrity": "sha512-duIiuqQ+Lew8ASSAYm6ZRqcmfBGWwsi81XLUwz86a2HR7Qv6V4yc3ZAUQovAikhjCsIqe8C11JlAZSK6+PlXYg==", + "node_modules/arctic/node_modules/@node-rs/argon2-linux-x64-musl": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-linux-x64-musl/-/argon2-linux-x64-musl-1.7.0.tgz", + "integrity": "sha512-/o1efYCYIxjfuoRYyBTi2Iy+1iFfhqHCvvVsnjNSgO1xWiWrX0Rrt/xXW5Zsl7vS2Y+yu8PL8KFWRzZhaVxfKA==", "cpu": [ "x64" ], - "license": "MIT", "optional": true, "os": [ "linux" @@ -3436,14 +2398,13 @@ "node": ">= 10" } }, - "node_modules/arctic/node_modules/@node-rs/bcrypt-wasm32-wasi": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-wasm32-wasi/-/bcrypt-wasm32-wasi-1.9.0.tgz", - "integrity": "sha512-ylaGmn9Wjwv/D5lxtawttx3H6Uu2WTTR7lWlRHGT6Ga/MB1Vj4OjSGUW8G8zIVnKuXpGbZ92pgHlt4HUpSLctw==", + "node_modules/arctic/node_modules/@node-rs/argon2-wasm32-wasi": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-wasm32-wasi/-/argon2-wasm32-wasi-1.7.0.tgz", + "integrity": "sha512-Evmk9VcxqnuwQftfAfYEr6YZYSPLzmKUsbFIMep5nTt9PT4XYRFAERj7wNYp+rOcBenF3X4xoB+LhwcOMTNE5w==", "cpu": [ "wasm32" ], - "license": "MIT", "optional": true, "dependencies": { "@emnapi/core": "^0.45.0", @@ -3455,14 +2416,13 @@ "node": ">=14.0.0" } }, - "node_modules/arctic/node_modules/@node-rs/bcrypt-win32-arm64-msvc": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-win32-arm64-msvc/-/bcrypt-win32-arm64-msvc-1.9.0.tgz", - "integrity": "sha512-2h86gF7QFyEzODuDFml/Dp1MSJoZjxJ4yyT2Erf4NkwsiA5MqowUhUsorRwZhX6+2CtlGa7orbwi13AKMsYndw==", + "node_modules/arctic/node_modules/@node-rs/argon2-win32-arm64-msvc": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-win32-arm64-msvc/-/argon2-win32-arm64-msvc-1.7.0.tgz", + "integrity": "sha512-qgsU7T004COWWpSA0tppDqDxbPLgg8FaU09krIJ7FBl71Sz8SFO40h7fDIjfbTT5w7u6mcaINMQ5bSHu75PCaA==", "cpu": [ "arm64" ], - "license": "MIT", "optional": true, "os": [ "win32" @@ -3471,14 +2431,13 @@ "node": ">= 10" } }, - "node_modules/arctic/node_modules/@node-rs/bcrypt-win32-ia32-msvc": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-win32-ia32-msvc/-/bcrypt-win32-ia32-msvc-1.9.0.tgz", - "integrity": "sha512-kqxalCvhs4FkN0+gWWfa4Bdy2NQAkfiqq/CEf6mNXC13RSV673Ev9V8sRlQyNpCHCNkeXfOT9pgoBdJmMs9muA==", + "node_modules/arctic/node_modules/@node-rs/argon2-win32-ia32-msvc": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-win32-ia32-msvc/-/argon2-win32-ia32-msvc-1.7.0.tgz", + "integrity": "sha512-JGafwWYQ/HpZ3XSwP4adQ6W41pRvhcdXvpzIWtKvX+17+xEXAe2nmGWM6s27pVkg1iV2ZtoYLRDkOUoGqZkCcg==", "cpu": [ "ia32" ], - "license": "MIT", "optional": true, "os": [ "win32" @@ -3487,14 +2446,13 @@ "node": ">= 10" } }, - "node_modules/arctic/node_modules/@node-rs/bcrypt-win32-x64-msvc": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-win32-x64-msvc/-/bcrypt-win32-x64-msvc-1.9.0.tgz", - "integrity": "sha512-2y0Tuo6ZAT2Cz8V7DHulSlv1Bip3zbzeXyeur+uR25IRNYXKvI/P99Zl85Fbuu/zzYAZRLLlGTRe6/9IHofe/w==", + "node_modules/arctic/node_modules/@node-rs/argon2-win32-x64-msvc": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-win32-x64-msvc/-/argon2-win32-x64-msvc-1.7.0.tgz", + "integrity": "sha512-9oq4ShyFakw8AG3mRls0AoCpxBFcimYx7+jvXeAf2OqKNO+mSA6eZ9z7KQeVCi0+SOEUYxMGf5UiGiDb9R6+9Q==", "cpu": [ "x64" ], - "license": "MIT", "optional": true, "os": [ "win32" @@ -3503,11 +2461,19 @@ "node": ">= 10" } }, + "node_modules/arctic/node_modules/@tybys/wasm-util": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.8.3.tgz", + "integrity": "sha512-Z96T/L6dUFFxgFJ+pQtkPpne9q7i6kIPYCFnQBHSgSPV9idTsKfIhCss0h5iM9irweZCatkrdeP8yi5uM1eX6Q==", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/arctic/node_modules/oslo": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/oslo/-/oslo-1.2.0.tgz", "integrity": "sha512-OoFX6rDsNcOQVAD2gQD/z03u4vEjWZLzJtwkmgfRF+KpQUXwdgEXErD7zNhyowmHwHefP+PM9Pw13pgpHMRlzw==", - "license": "MIT", "dependencies": { "@node-rs/argon2": "1.7.0", "@node-rs/bcrypt": "1.9.0" @@ -3519,20 +2485,20 @@ "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==" }, "node_modules/aria-query": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", - "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", - "dependencies": { - "dequal": "^2.0.3" + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz", + "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==", + "engines": { + "node": ">= 0.4" } }, "node_modules/array-buffer-byte-length": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz", - "integrity": "sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", + "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==", "dependencies": { - "call-bind": "^1.0.5", - "is-array-buffer": "^3.0.4" + "call-bound": "^1.0.3", + "is-array-buffer": "^3.0.5" }, "engines": { "node": ">= 0.4" @@ -3560,9 +2526,9 @@ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" }, "node_modules/autoprefixer": { - "version": "10.4.17", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.17.tgz", - "integrity": "sha512-/cpVNRLSfhOtcGflT13P2794gVSgmPgTR+erw5ifnMLZb0UnSlkK4tquLmkd3BhA+nLo5tX8Cu0upUsGKvKbmg==", + "version": "10.4.20", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.20.tgz", + "integrity": "sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g==", "funding": [ { "type": "opencollective", @@ -3578,11 +2544,11 @@ } ], "dependencies": { - "browserslist": "^4.22.2", - "caniuse-lite": "^1.0.30001578", + "browserslist": "^4.23.3", + "caniuse-lite": "^1.0.30001646", "fraction.js": "^4.3.7", "normalize-range": "^0.1.2", - "picocolors": "^1.0.0", + "picocolors": "^1.0.1", "postcss-value-parser": "^4.2.0" }, "bin": { @@ -3596,9 +2562,12 @@ } }, "node_modules/available-typed-arrays": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.6.tgz", - "integrity": "sha512-j1QzY8iPNPG4o4xmO3ptzpRxTciqD3MgEHtifP/YnJpIo58Xu+ne4BejlbkuaLfXn/nz6HFiw29bLpj2PNMdGg==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, "engines": { "node": ">= 0.4" }, @@ -3607,11 +2576,11 @@ } }, "node_modules/axios": { - "version": "1.6.7", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.7.tgz", - "integrity": "sha512-/hDJGff6/c7u0hDkvkGxR/oy6CbCs8ziCsC7SqmhjfozqiJGc8Z11wrv9z9lYfY4K8l+H9TpjcMDX0xOZmx+RA==", + "version": "1.7.9", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.9.tgz", + "integrity": "sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw==", "dependencies": { - "follow-redirects": "^1.15.4", + "follow-redirects": "^1.15.6", "form-data": "^4.0.0", "proxy-from-env": "^1.1.0" } @@ -3641,11 +2610,14 @@ ] }, "node_modules/binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", "engines": { "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/bl": { @@ -3690,20 +2662,20 @@ } }, "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dependencies": { - "fill-range": "^7.0.1" + "fill-range": "^7.1.1" }, "engines": { "node": ">=8" } }, "node_modules/browserslist": { - "version": "4.23.0", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz", - "integrity": "sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==", + "version": "4.24.4", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz", + "integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==", "funding": [ { "type": "opencollective", @@ -3719,10 +2691,10 @@ } ], "dependencies": { - "caniuse-lite": "^1.0.30001587", - "electron-to-chromium": "^1.4.668", - "node-releases": "^2.0.14", - "update-browserslist-db": "^1.0.13" + "caniuse-lite": "^1.0.30001688", + "electron-to-chromium": "^1.5.73", + "node-releases": "^2.0.19", + "update-browserslist-db": "^1.1.1" }, "bin": { "browserslist": "cli.js" @@ -3754,14 +2726,6 @@ "ieee754": "^1.1.13" } }, - "node_modules/buffer-writer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-2.0.0.tgz", - "integrity": "sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw==", - "engines": { - "node": ">=4" - } - }, "node_modules/bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", @@ -3779,15 +2743,41 @@ } }, "node_modules/call-bind": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", - "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", + "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", "dependencies": { + "call-bind-apply-helpers": "^1.0.0", "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", "get-intrinsic": "^1.2.4", - "set-function-length": "^1.2.1" + "set-function-length": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.1.tgz", + "integrity": "sha512-BhYE+WDaywFg2TBWYNXAE+8B1ATnThNBqXHP5nQu0jWJdVvY2hvkpyB3qOmtmDePiS5/BDQ8wASEWGMWRG148g==", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.3.tgz", + "integrity": "sha512-YTd+6wGlNlPxSuri7Y6X8tY2dmm12UMH66RpKMhiX6rsk5wXXnYgbUcOt8kiS31/AjfoTOvCsE+w8nZQLQnzHA==", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "get-intrinsic": "^1.2.6" }, "engines": { "node": ">= 0.4" @@ -3805,9 +2795,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001588", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001588.tgz", - "integrity": "sha512-+hVY9jE44uKLkH0SrUTqxjxqNTOWHsbnQDIKjwkZ3lNTzUUVdBLBGXtj/q5Mp5u98r3droaZAewQuEDzjQdZlQ==", + "version": "1.0.30001692", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001692.tgz", + "integrity": "sha512-A95VKan0kdtrsnMubMKxEKUKImOPSuCpYgxSQBo036P5YYgVIcOYJEgt/txJWqObiRQeISNCfef9nvlQ0vbV7A==", "funding": [ { "type": "opencollective", @@ -3824,9 +2814,9 @@ ] }, "node_modules/chai": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.4.1.tgz", - "integrity": "sha512-13sOfMv2+DWduEU+/xbun3LScLoqN17nBeTLUsmDfKdoiC1fr0n9PU4guu4AhRcOVFk/sW8LyZWHuhWtQZiF+g==", + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.5.0.tgz", + "integrity": "sha512-RITGBfijLkBddZvnn8jdqoTypxvqbOLYQkGGxXzeFjVHvudaPw0HNFD9x928/eUwYWd2dPCugVqspGALTZZQKw==", "dependencies": { "assertion-error": "^1.1.0", "check-error": "^1.0.3", @@ -3834,7 +2824,7 @@ "get-func-name": "^2.0.2", "loupe": "^2.3.6", "pathval": "^1.1.1", - "type-detect": "^4.0.8" + "type-detect": "^4.1.0" }, "engines": { "node": ">=4" @@ -4077,6 +3067,11 @@ "node": ">= 6" } }, + "node_modules/confbox": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz", + "integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==" + }, "node_modules/content-disposition": { "version": "0.5.4", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", @@ -4135,9 +3130,9 @@ } }, "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -4206,9 +3201,9 @@ "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==" }, "node_modules/deep-eql": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.3.tgz", - "integrity": "sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==", + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.4.tgz", + "integrity": "sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg==", "dependencies": { "type-detect": "^4.0.0" }, @@ -4259,14 +3254,13 @@ } }, "node_modules/define-data-property": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.3.tgz", - "integrity": "sha512-h3GBouC+RPtNX2N0hHVLo2ZwPYurq8mLmXpOLTsw71gr7lHt5VaI4vVkDUNOfiWmm48JEXe3VM7PmLX45AMmmg==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", "dependencies": { + "es-define-property": "^1.0.0", "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.4", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.1" + "gopd": "^1.0.1" }, "engines": { "node": ">= 0.4" @@ -4318,14 +3312,6 @@ "node": ">= 0.8" } }, - "node_modules/dequal": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", - "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", - "engines": { - "node": ">=6" - } - }, "node_modules/destroy": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", @@ -4370,6 +3356,19 @@ "node": ">=12" } }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", @@ -4381,9 +3380,9 @@ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" }, "node_modules/electron-to-chromium": { - "version": "1.4.673", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.673.tgz", - "integrity": "sha512-zjqzx4N7xGdl5468G+vcgzDhaHkaYgVcf9MqgexcTqsl2UHSCmOj/Bi3HAprg4BZCpC7HyD8a6nZl6QAZf72gw==" + "version": "1.5.81", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.81.tgz", + "integrity": "sha512-SFsAz1hoR+u1eAWjofSPQnx0InE1QHGUAQ92pqYJPT8GARzmyP1zcEBDBxFFC6okJk2E94Ryfmib4DB8Sc6LBw==" }, "node_modules/emoji-regex": { "version": "9.2.2", @@ -4410,12 +3409,9 @@ } }, "node_modules/es-define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", - "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", - "dependencies": { - "get-intrinsic": "^1.2.4" - }, + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", "engines": { "node": ">= 0.4" } @@ -4447,6 +3443,17 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/es-object-atoms": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.1.tgz", + "integrity": "sha512-BPOBuyUF9QIVhuNLhbToCLHP6+0MHwZ7xLBkPPCZqK4JmpJgGnv10035STzzQwFpqdzNFMB3irvDI63IagvDwA==", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/esbuild": { "version": "0.18.20", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.20.tgz", @@ -4485,9 +3492,9 @@ } }, "node_modules/escalade": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", - "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", "engines": { "node": ">=6" } @@ -4625,9 +3632,9 @@ } }, "node_modules/express": { - "version": "4.21.1", - "resolved": "https://registry.npmjs.org/express/-/express-4.21.1.tgz", - "integrity": "sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ==", + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", + "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", @@ -4648,7 +3655,7 @@ "methods": "~1.1.2", "on-finished": "2.4.1", "parseurl": "~1.3.3", - "path-to-regexp": "0.1.10", + "path-to-regexp": "0.1.12", "proxy-addr": "~2.0.7", "qs": "6.13.0", "range-parser": "~1.2.1", @@ -4663,6 +3670,10 @@ }, "engines": { "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/external-editor": { @@ -4679,15 +3690,15 @@ } }, "node_modules/fast-glob": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", - "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", - "micromatch": "^4.0.4" + "micromatch": "^4.0.8" }, "engines": { "node": ">=8.6.0" @@ -4705,9 +3716,9 @@ } }, "node_modules/fastq": { - "version": "1.17.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", - "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.18.0.tgz", + "integrity": "sha512-QKHXPW0hD8g4UET03SdOdunzSouc9N4AuHdsX8XNcTsuz+yYFILVNIX4l9yHABMhiEI9Db0JTTIpu0wB+Y1QQw==", "dependencies": { "reusify": "^1.0.4" } @@ -4732,9 +3743,9 @@ } }, "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dependencies": { "to-regex-range": "^5.0.1" }, @@ -4760,14 +3771,14 @@ } }, "node_modules/flatted": { - "version": "3.2.9", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.9.tgz", - "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==" + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.2.tgz", + "integrity": "sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA==" }, "node_modules/follow-redirects": { - "version": "1.15.5", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.5.tgz", - "integrity": "sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw==", + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", "funding": [ { "type": "individual", @@ -4792,9 +3803,9 @@ } }, "node_modules/foreground-child": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", - "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", + "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", "dependencies": { "cross-spawn": "^7.0.0", "signal-exit": "^4.0.1" @@ -4807,9 +3818,9 @@ } }, "node_modules/form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz", + "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==", "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", @@ -4848,9 +3859,9 @@ } }, "node_modules/fs-monkey": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.5.tgz", - "integrity": "sha512-8uMbBjrhzW76TYgEV27Y5E//W2f/lTFmx78P2w19FZSxarhI/798APGQyuGCwmkNxgwGRhrLfvWyLBvNtuOmew==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.6.tgz", + "integrity": "sha512-b1FMfwetIKymC0eioW7mTywihSQE4oLzQn1dB6rZB5fx/3NpNEdAWeCSMB+60/AeT0TCXsxzAlcYVEFCTAksWg==", "optional": true }, "node_modules/fsevents": { @@ -4899,15 +3910,20 @@ } }, "node_modules/get-intrinsic": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", - "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.7.tgz", + "integrity": "sha512-VW6Pxhsrk0KAOqs3WEd0klDiF/+V7gQOpAvY1jVU/LHmaD/kQO4523aiJuikX/QAKYiW6x8Jh+RJej1almdtCA==", "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-define-property": "^1.0.1", "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", "function-bind": "^1.1.2", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0" + "get-proto": "^1.0.0", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -4916,6 +3932,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/get-stream": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", @@ -4928,22 +3956,20 @@ } }, "node_modules/glob": { - "version": "10.3.10", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", - "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==", + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", "dependencies": { "foreground-child": "^3.1.0", - "jackspeak": "^2.3.5", - "minimatch": "^9.0.1", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", - "path-scurry": "^1.10.1" + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" }, "bin": { "glob": "dist/esm/bin.mjs" }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, "funding": { "url": "https://github.com/sponsors/isaacs" } @@ -4960,28 +3986,31 @@ } }, "node_modules/gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", - "dependencies": { - "get-intrinsic": "^1.1.3" + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/graphql": { - "version": "16.8.1", - "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.8.1.tgz", - "integrity": "sha512-59LZHPdGZVh695Ud9lRzPBVTtlX9ZCV150Er2W43ro37wVof0ctenSaskPPjN7lVTIN8mSZt8PHUNKZuNQUuxw==", + "version": "16.10.0", + "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.10.0.tgz", + "integrity": "sha512-AjqGKbDGUFRKIRCP9tCKiIGHyriz2oHEbPIbEtcSLSs4YjReZOIPQQWek4+6hjw62H9QShXHyaGivGiYVLeYFQ==", "engines": { "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0" } }, "node_modules/has-bigints": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", - "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", + "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==", + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -5005,21 +4034,10 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/has-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", - "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", "engines": { "node": ">= 0.4" }, @@ -5042,9 +4060,9 @@ } }, "node_modules/hasown": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.1.tgz", - "integrity": "sha512-1/th4MHjnwncwXsIW6QMzlvYL9kG5e/CpVvLRZe4XPa8TOUNbCELqmvhDmnkNsAjwaG4+I8gJJL0JBvTTLO9qA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", "dependencies": { "function-bind": "^1.1.2" }, @@ -5097,11 +4115,11 @@ } }, "node_modules/http-proxy-agent/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", "dependencies": { - "ms": "2.1.2" + "ms": "^2.1.3" }, "engines": { "node": ">=6.0" @@ -5113,9 +4131,9 @@ } }, "node_modules/http-proxy-agent/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, "node_modules/https-proxy-agent": { "version": "5.0.1", @@ -5130,11 +4148,11 @@ } }, "node_modules/https-proxy-agent/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", "dependencies": { - "ms": "2.1.2" + "ms": "^2.1.3" }, "engines": { "node": ">=6.0" @@ -5146,9 +4164,9 @@ } }, "node_modules/https-proxy-agent/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, "node_modules/human-signals": { "version": "5.0.0", @@ -5306,13 +4324,13 @@ } }, "node_modules/internal-slot": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.7.tgz", - "integrity": "sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", + "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", "dependencies": { "es-errors": "^1.3.0", - "hasown": "^2.0.0", - "side-channel": "^1.0.4" + "hasown": "^2.0.2", + "side-channel": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -5327,12 +4345,12 @@ } }, "node_modules/is-arguments": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", - "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.2.0.tgz", + "integrity": "sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA==", "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -5342,12 +4360,13 @@ } }, "node_modules/is-array-buffer": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz", - "integrity": "sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==", + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", + "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.1" + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" }, "engines": { "node": ">= 0.4" @@ -5357,11 +4376,14 @@ } }, "node_modules/is-bigint": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", - "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", + "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", "dependencies": { - "has-bigints": "^1.0.1" + "has-bigints": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -5379,12 +4401,12 @@ } }, "node_modules/is-boolean-object": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", - "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.1.tgz", + "integrity": "sha512-l9qO6eFlUETHtuihLcYOaLKByJ1f+N4kthcU9YjHy3N+B3hWv0y/2Nd0mu/7lTFnRQHTrSdXF50HQ3bl5fEnng==", "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -5405,22 +4427,26 @@ } }, "node_modules/is-core-module": { - "version": "2.13.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", - "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", "dependencies": { - "hasown": "^2.0.0" + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/is-date-object": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", - "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", + "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", "dependencies": { - "has-tostringtag": "^1.0.0" + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -5446,11 +4472,14 @@ } }, "node_modules/is-generator-function": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", - "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.0.tgz", + "integrity": "sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==", "dependencies": { - "has-tostringtag": "^1.0.0" + "call-bound": "^1.0.3", + "get-proto": "^1.0.0", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -5479,9 +4508,12 @@ } }, "node_modules/is-map": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.2.tgz", - "integrity": "sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", + "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -5500,11 +4532,12 @@ } }, "node_modules/is-number-object": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", - "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz", + "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", "dependencies": { - "has-tostringtag": "^1.0.0" + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -5519,12 +4552,14 @@ "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==" }, "node_modules/is-regex": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", - "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" }, "engines": { "node": ">= 0.4" @@ -5534,19 +4569,25 @@ } }, "node_modules/is-set": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.2.tgz", - "integrity": "sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", + "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/is-shared-array-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", - "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", + "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==", "dependencies": { - "call-bind": "^1.0.2" + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -5564,11 +4605,12 @@ } }, "node_modules/is-string": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", - "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", + "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", "dependencies": { - "has-tostringtag": "^1.0.0" + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -5578,11 +4620,13 @@ } }, "node_modules/is-symbol": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", - "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz", + "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", "dependencies": { - "has-symbols": "^1.0.2" + "call-bound": "^1.0.2", + "has-symbols": "^1.1.0", + "safe-regex-test": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -5592,11 +4636,11 @@ } }, "node_modules/is-typed-array": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.13.tgz", - "integrity": "sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==", + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", + "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", "dependencies": { - "which-typed-array": "^1.1.14" + "which-typed-array": "^1.1.16" }, "engines": { "node": ">= 0.4" @@ -5617,20 +4661,26 @@ } }, "node_modules/is-weakmap": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.1.tgz", - "integrity": "sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", + "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/is-weakset": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.2.tgz", - "integrity": "sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz", + "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.1" + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -5658,15 +4708,12 @@ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" }, "node_modules/jackspeak": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", - "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", "dependencies": { "@isaacs/cliui": "^8.0.2" }, - "engines": { - "node": ">=14" - }, "funding": { "url": "https://github.com/sponsors/isaacs" }, @@ -5675,9 +4722,9 @@ } }, "node_modules/jiti": { - "version": "1.21.0", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.0.tgz", - "integrity": "sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q==", + "version": "1.21.7", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz", + "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", "bin": { "jiti": "bin/jiti.js" } @@ -5739,17 +4786,15 @@ } } }, - "node_modules/jsonc-parser": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.1.tgz", - "integrity": "sha512-AilxAyFOAcK5wA1+LeaySVBrHsGQvUFCDWXKpZjzaL0PqW+xfBOttn8GNtWKFWqneyMZj41MWF9Kl6iPWLwgOA==" - }, "node_modules/lilconfig": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", - "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", "engines": { - "node": ">=10" + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" } }, "node_modules/lines-and-columns": { @@ -5758,12 +4803,12 @@ "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" }, "node_modules/local-pkg": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-0.5.0.tgz", - "integrity": "sha512-ok6z3qlYyCDS4ZEU27HaU6x/xZa9Whf8jD4ptH5UZTQYZVYeb9bnZ3ojVhiJNLiXK1Hfc0GNbLXcmZ5plLDDBg==", + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-0.5.1.tgz", + "integrity": "sha512-9rrA30MRRP3gBD3HTGnC6cDFpaE1kVDWxWgqWJUN0RvDNAo+Nz/9GxB+nHOH0ifbVFy0hSA1V6vFDvnx54lTEQ==", "dependencies": { - "mlly": "^1.4.2", - "pkg-types": "^1.0.3" + "mlly": "^1.7.3", + "pkg-types": "^1.2.1" }, "engines": { "node": ">=14" @@ -5861,25 +4906,23 @@ } }, "node_modules/lru-cache": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.0.tgz", - "integrity": "sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==", - "engines": { - "node": "14 || >=16.14" - } + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==" }, "node_modules/lucia": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/lucia/-/lucia-3.0.1.tgz", - "integrity": "sha512-srwUkTCGgr6N4mFpaKZVZy5kwiRZdsrbIDv9Wrjar+xyw1MjojYQQ7oRbegjRWOZ3yI8xOOclK3sz/rga2J7/w==", + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/lucia/-/lucia-3.2.2.tgz", + "integrity": "sha512-P1FlFBGCMPMXu+EGdVD9W4Mjm0DqsusmKgO7Xc33mI5X1bklmsQb0hfzPhXomQr9waWIBDsiOjvr1e6BTaUqpA==", "dependencies": { - "oslo": "1.0.1" + "@oslojs/crypto": "^1.0.1", + "@oslojs/encoding": "^1.1.0" } }, "node_modules/luxon": { - "version": "3.4.4", - "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.4.4.tgz", - "integrity": "sha512-zobTr7akeGHnv7eBOXcRgMeCP6+uyYsczwmeRCauvpvaAltgNyTbLH/+VaEAPUeWBT+1GuNmz4wC/6jtQzbbVA==", + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.5.0.tgz", + "integrity": "sha512-rh+Zjr6DNfUYR3bPwJEnuwDdqMbxZW7LOQfUN4B54+Cl+0o5zaU9RJ6bcidfDtC1cWCZXQ+nvX8bf6bAji37QQ==", "engines": { "node": ">=12" } @@ -5893,14 +4936,19 @@ } }, "node_modules/magic-string": { - "version": "0.30.7", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.7.tgz", - "integrity": "sha512-8vBuFF/I/+OSLRmdf2wwFCJCz+nSn0m6DPvGH1fS/KiQoSaR+sETbov0eIk9KhEKy8CYqIkIAnbohxT/4H0kuA==", + "version": "0.30.17", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", + "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", "dependencies": { - "@jridgewell/sourcemap-codec": "^1.4.15" - }, + "@jridgewell/sourcemap-codec": "^1.5.0" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", "engines": { - "node": ">=12" + "node": ">= 0.4" } }, "node_modules/media-typer": { @@ -5962,11 +5010,11 @@ } }, "node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dependencies": { - "braces": "^3.0.2", + "braces": "^3.0.3", "picomatch": "^2.3.1" }, "engines": { @@ -6028,9 +5076,9 @@ } }, "node_modules/minimatch": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", - "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "dependencies": { "brace-expansion": "^2.0.1" }, @@ -6042,9 +5090,9 @@ } }, "node_modules/minipass": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz", - "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==", + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", "engines": { "node": ">=16 || 14 >=14.17" } @@ -6055,16 +5103,21 @@ "integrity": "sha512-7dX2/10ITVyqh4aOSVI9gdape+t9l2/8QxHrFmUXu4EEUpdlxl6RudZUPZoc+zuY2hk1j7XxVroIVIan/pD/SQ==" }, "node_modules/mlly": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.5.0.tgz", - "integrity": "sha512-NPVQvAY1xr1QoVeG0cy8yUYC7FQcOx6evl/RjT1wL5FvzPnzOysoqB/jmx/DhssT2dYa8nxECLAaFI/+gVLhDQ==", + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.7.4.tgz", + "integrity": "sha512-qmdSIPC4bDJXgZTCR7XosJiNKySV7O215tsPtDN9iEO/7q/76b/ijtgRu/+epFXSJhijtTCCGp3DWS549P3xKw==", "dependencies": { - "acorn": "^8.11.3", - "pathe": "^1.1.2", - "pkg-types": "^1.0.3", - "ufo": "^1.3.2" + "acorn": "^8.14.0", + "pathe": "^2.0.1", + "pkg-types": "^1.3.0", + "ufo": "^1.5.4" } }, + "node_modules/mlly/node_modules/pathe": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.1.tgz", + "integrity": "sha512-6jpjMpOth5S9ITVu5clZ7NOgHNsv5vRQdheL9ztp2vZmM6fRbLvyua1tiBIL4lk8SAe3ARzeXEly6siXCjDHDw==" + }, "node_modules/mrmime": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.0.tgz", @@ -6079,9 +5132,9 @@ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, "node_modules/msw": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/msw/-/msw-1.3.2.tgz", - "integrity": "sha512-wKLhFPR+NitYTkQl5047pia0reNGgf0P6a1eTnA5aNlripmiz0sabMvvHcicE8kQ3/gZcI0YiPFWmYfowfm3lA==", + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/msw/-/msw-1.3.5.tgz", + "integrity": "sha512-nG3fpmBXxFbKSIdk6miPuL3KjU6WMxgoW4tG1YgnP1M+TRG3Qn7b7R0euKAHq4vpwARHb18ZyfZljSxsTnMX2w==", "hasInstallScript": true, "dependencies": { "@mswjs/cookies": "^0.2.2", @@ -6099,7 +5152,7 @@ "js-levenshtein": "^1.1.6", "node-fetch": "^2.6.7", "outvariant": "^1.4.0", - "path-to-regexp": "^6.2.0", + "path-to-regexp": "^6.3.0", "strict-event-emitter": "^0.4.3", "type-fest": "^2.19.0", "yargs": "^17.3.1" @@ -6115,7 +5168,7 @@ "url": "https://opencollective.com/mswjs" }, "peerDependencies": { - "typescript": ">= 4.4.x <= 5.2.x" + "typescript": ">= 4.4.x" }, "peerDependenciesMeta": { "typescript": { @@ -6161,9 +5214,9 @@ } }, "node_modules/msw/node_modules/path-to-regexp": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.2.1.tgz", - "integrity": "sha512-JLyh7xT1kizaEvcaXOQwOc2/Yhw6KZOvPf1S8401UyLk86CU79LN3vl7ztXGm/pZ+YjoyAJ4rxmHwbkBXJX+yw==" + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.3.0.tgz", + "integrity": "sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==" }, "node_modules/mute-stream": { "version": "0.0.8", @@ -6181,9 +5234,9 @@ } }, "node_modules/nanoid": { - "version": "3.3.7", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", - "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "version": "3.3.8", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz", + "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==", "funding": [ { "type": "github", @@ -6244,14 +5297,14 @@ } }, "node_modules/node-releases": { - "version": "2.0.14", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", - "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==" + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", + "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==" }, "node_modules/nodemailer": { - "version": "6.9.9", - "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.9.9.tgz", - "integrity": "sha512-dexTll8zqQoVJEZPwQAKzxxtFn0qTnjdQTchoU6Re9BUUGBJiOy3YMn/0ShTW6J5M0dfQ1NeDeRTTl4oIWgQMA==", + "version": "6.9.16", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.9.16.tgz", + "integrity": "sha512-psAuZdTIRN08HKVd/E8ObdV6NO7NTBY3KsC30F7M4H1OnmLCUNaS56FpYxyb26zWLSyYF9Ozch9KYHhHegsiOQ==", "engines": { "node": ">=6.0.0" } @@ -6273,9 +5326,9 @@ } }, "node_modules/npm-run-path": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.2.0.tgz", - "integrity": "sha512-W4/tgAXFqFA0iL7fk0+uQ3g7wkL8xJmx3XdK0VGb4cHW//eZTtKGvFBBoRKVTpY7n6ze4NL9ly7rgXcHufqXKg==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz", + "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==", "dependencies": { "path-key": "^4.0.0" }, @@ -6298,9 +5351,9 @@ } }, "node_modules/nwsapi": { - "version": "2.2.7", - "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.7.tgz", - "integrity": "sha512-ub5E4+FBPKwAZx0UwIQOjYWGHTEq5sPqHQNRN8Z9e4A7u3Tj1weLJsL59yH9vmvqEtBHaOmT6cYQKIZOxp35FQ==" + "version": "2.2.16", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.16.tgz", + "integrity": "sha512-F1I/bimDpj3ncaNDhfyMWuFqmQDBwDB0Fogc2qpL3BWvkQteFD/8BzWuIRl83rq0DXfm8SGt/HFhLXZyljTXcQ==" }, "node_modules/object-assign": { "version": "4.1.1", @@ -6319,20 +5372,23 @@ } }, "node_modules/object-inspect": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", - "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", + "version": "1.13.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.3.tgz", + "integrity": "sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA==", + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/object-is": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz", - "integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==", + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.6.tgz", + "integrity": "sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1" }, "engines": { "node": ">= 0.4" @@ -6350,13 +5406,15 @@ } }, "node_modules/object.assign": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz", - "integrity": "sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==", + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", + "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", "dependencies": { - "call-bind": "^1.0.5", + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", "define-properties": "^1.2.1", - "has-symbols": "^1.0.3", + "es-object-atoms": "^1.0.0", + "has-symbols": "^1.1.0", "object-keys": "^1.1.1" }, "engines": { @@ -6418,70 +5476,334 @@ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "engines": { - "node": ">=8" + "node": ">=8" + } + }, + "node_modules/ora/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/ora/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/ora/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/oslo": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/oslo/-/oslo-1.2.1.tgz", + "integrity": "sha512-HfIhB5ruTdQv0XX2XlncWQiJ5SIHZ7NHZhVyHth0CSZ/xzge00etRyYy/3wp/Dsu+PkxMC+6+B2lS/GcKoewkA==", + "dependencies": { + "@node-rs/argon2": "1.7.0", + "@node-rs/bcrypt": "1.9.0" + } + }, + "node_modules/oslo/node_modules/@emnapi/core": { + "version": "0.45.0", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-0.45.0.tgz", + "integrity": "sha512-DPWjcUDQkCeEM4VnljEOEcXdAD7pp8zSZsgOujk/LGIwCXWbXJngin+MO4zbH429lzeC3WbYLGjE2MaUOwzpyw==", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/oslo/node_modules/@emnapi/runtime": { + "version": "0.45.0", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-0.45.0.tgz", + "integrity": "sha512-Txumi3td7J4A/xTTwlssKieHKTGl3j4A1tglBx72auZ49YK7ePY6XZricgIg9mnZT4xPfA+UPCUdnhRuEFDL+w==", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/oslo/node_modules/@node-rs/argon2": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@node-rs/argon2/-/argon2-1.7.0.tgz", + "integrity": "sha512-zfULc+/tmcWcxn+nHkbyY8vP3+MpEqKORbszt4UkpqZgBgDAAIYvuDN/zukfTgdmo6tmJKKVfzigZOPk4LlIog==", + "engines": { + "node": ">= 10" + }, + "optionalDependencies": { + "@node-rs/argon2-android-arm-eabi": "1.7.0", + "@node-rs/argon2-android-arm64": "1.7.0", + "@node-rs/argon2-darwin-arm64": "1.7.0", + "@node-rs/argon2-darwin-x64": "1.7.0", + "@node-rs/argon2-freebsd-x64": "1.7.0", + "@node-rs/argon2-linux-arm-gnueabihf": "1.7.0", + "@node-rs/argon2-linux-arm64-gnu": "1.7.0", + "@node-rs/argon2-linux-arm64-musl": "1.7.0", + "@node-rs/argon2-linux-x64-gnu": "1.7.0", + "@node-rs/argon2-linux-x64-musl": "1.7.0", + "@node-rs/argon2-wasm32-wasi": "1.7.0", + "@node-rs/argon2-win32-arm64-msvc": "1.7.0", + "@node-rs/argon2-win32-ia32-msvc": "1.7.0", + "@node-rs/argon2-win32-x64-msvc": "1.7.0" + } + }, + "node_modules/oslo/node_modules/@node-rs/argon2-android-arm-eabi": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-android-arm-eabi/-/argon2-android-arm-eabi-1.7.0.tgz", + "integrity": "sha512-udDqkr5P9E+wYX1SZwAVPdyfYvaF4ry9Tm+R9LkfSHbzWH0uhU6zjIwNRp7m+n4gx691rk+lqqDAIP8RLKwbhg==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/oslo/node_modules/@node-rs/argon2-android-arm64": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-android-arm64/-/argon2-android-arm64-1.7.0.tgz", + "integrity": "sha512-s9j/G30xKUx8WU50WIhF0fIl1EdhBGq0RQ06lEhZ0Gi0ap8lhqbE2Bn5h3/G2D1k0Dx+yjeVVNmt/xOQIRG38A==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/oslo/node_modules/@node-rs/argon2-darwin-arm64": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-darwin-arm64/-/argon2-darwin-arm64-1.7.0.tgz", + "integrity": "sha512-ZIz4L6HGOB9U1kW23g+m7anGNuTZ0RuTw0vNp3o+2DWpb8u8rODq6A8tH4JRL79S+Co/Nq608m9uackN2pe0Rw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/oslo/node_modules/@node-rs/argon2-darwin-x64": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-darwin-x64/-/argon2-darwin-x64-1.7.0.tgz", + "integrity": "sha512-5oi/pxqVhODW/pj1+3zElMTn/YukQeywPHHYDbcAW3KsojFjKySfhcJMd1DjKTc+CHQI+4lOxZzSUzK7mI14Hw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/oslo/node_modules/@node-rs/argon2-freebsd-x64": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-freebsd-x64/-/argon2-freebsd-x64-1.7.0.tgz", + "integrity": "sha512-Ify08683hA4QVXYoIm5SUWOY5DPIT/CMB0CQT+IdxQAg/F+qp342+lUkeAtD5bvStQuCx/dFO3bnnzoe2clMhA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/oslo/node_modules/@node-rs/argon2-linux-arm-gnueabihf": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-linux-arm-gnueabihf/-/argon2-linux-arm-gnueabihf-1.7.0.tgz", + "integrity": "sha512-7DjDZ1h5AUHAtRNjD19RnQatbhL+uuxBASuuXIBu4/w6Dx8n7YPxwTP4MXfsvuRgKuMWiOb/Ub/HJ3kXVCXRkg==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/oslo/node_modules/@node-rs/argon2-linux-arm64-gnu": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-linux-arm64-gnu/-/argon2-linux-arm64-gnu-1.7.0.tgz", + "integrity": "sha512-nJDoMP4Y3YcqGswE4DvP080w6O24RmnFEDnL0emdI8Nou17kNYBzP2546Nasx9GCyLzRcYQwZOUjrtUuQ+od2g==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/oslo/node_modules/@node-rs/argon2-linux-arm64-musl": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-linux-arm64-musl/-/argon2-linux-arm64-musl-1.7.0.tgz", + "integrity": "sha512-BKWS8iVconhE3jrb9mj6t1J9vwUqQPpzCbUKxfTGJfc+kNL58F1SXHBoe2cDYGnHrFEHTY0YochzXoAfm4Dm/A==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/oslo/node_modules/@node-rs/argon2-linux-x64-gnu": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-linux-x64-gnu/-/argon2-linux-x64-gnu-1.7.0.tgz", + "integrity": "sha512-EmgqZOlf4Jurk/szW1iTsVISx25bKksVC5uttJDUloTgsAgIGReCpUUO1R24pBhu9ESJa47iv8NSf3yAfGv6jQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/oslo/node_modules/@node-rs/argon2-linux-x64-musl": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-linux-x64-musl/-/argon2-linux-x64-musl-1.7.0.tgz", + "integrity": "sha512-/o1efYCYIxjfuoRYyBTi2Iy+1iFfhqHCvvVsnjNSgO1xWiWrX0Rrt/xXW5Zsl7vS2Y+yu8PL8KFWRzZhaVxfKA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" } }, - "node_modules/ora/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/oslo/node_modules/@node-rs/argon2-wasm32-wasi": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-wasm32-wasi/-/argon2-wasm32-wasi-1.7.0.tgz", + "integrity": "sha512-Evmk9VcxqnuwQftfAfYEr6YZYSPLzmKUsbFIMep5nTt9PT4XYRFAERj7wNYp+rOcBenF3X4xoB+LhwcOMTNE5w==", + "cpu": [ + "wasm32" + ], + "optional": true, "dependencies": { - "color-convert": "^2.0.1" + "@emnapi/core": "^0.45.0", + "@emnapi/runtime": "^0.45.0", + "@tybys/wasm-util": "^0.8.1", + "memfs-browser": "^3.4.13000" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": ">=14.0.0" } }, - "node_modules/ora/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, + "node_modules/oslo/node_modules/@node-rs/argon2-win32-arm64-msvc": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-win32-arm64-msvc/-/argon2-win32-arm64-msvc-1.7.0.tgz", + "integrity": "sha512-qgsU7T004COWWpSA0tppDqDxbPLgg8FaU09krIJ7FBl71Sz8SFO40h7fDIjfbTT5w7u6mcaINMQ5bSHu75PCaA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "node": ">= 10" } }, - "node_modules/ora/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dependencies": { - "ansi-regex": "^5.0.1" - }, + "node_modules/oslo/node_modules/@node-rs/argon2-win32-ia32-msvc": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-win32-ia32-msvc/-/argon2-win32-ia32-msvc-1.7.0.tgz", + "integrity": "sha512-JGafwWYQ/HpZ3XSwP4adQ6W41pRvhcdXvpzIWtKvX+17+xEXAe2nmGWM6s27pVkg1iV2ZtoYLRDkOUoGqZkCcg==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": ">=8" + "node": ">= 10" } }, - "node_modules/os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", + "node_modules/oslo/node_modules/@node-rs/argon2-win32-x64-msvc": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@node-rs/argon2-win32-x64-msvc/-/argon2-win32-x64-msvc-1.7.0.tgz", + "integrity": "sha512-9oq4ShyFakw8AG3mRls0AoCpxBFcimYx7+jvXeAf2OqKNO+mSA6eZ9z7KQeVCi0+SOEUYxMGf5UiGiDb9R6+9Q==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": ">=0.10.0" + "node": ">= 10" } }, - "node_modules/oslo": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/oslo/-/oslo-1.0.1.tgz", - "integrity": "sha512-esfzZry+HfGgK/GCYkg7BRlLd3RH5aHa08wgLJPYjENXybi0BvXxGk0LbUj+lXfz2TkjPDHe4rB/o6JxRLHxBg==", + "node_modules/oslo/node_modules/@tybys/wasm-util": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.8.3.tgz", + "integrity": "sha512-Z96T/L6dUFFxgFJ+pQtkPpne9q7i6kIPYCFnQBHSgSPV9idTsKfIhCss0h5iM9irweZCatkrdeP8yi5uM1eX6Q==", + "optional": true, "dependencies": { - "@node-rs/argon2": "1.7.2", - "@node-rs/bcrypt": "1.9.2" + "tslib": "^2.4.0" } }, "node_modules/outvariant": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/outvariant/-/outvariant-1.4.2.tgz", - "integrity": "sha512-Ou3dJ6bA/UJ5GVHxah4LnqDwZRwAmWxrG3wtrHrbGnP4RnLCtA64A4F+ae7Y8ww660JaddSoArUR5HjipWSHAQ==" + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/outvariant/-/outvariant-1.4.3.tgz", + "integrity": "sha512-+Sl2UErvtsoajRDKCE5/dBz4DIvHXQQnAxtQTF04OJxY0+DyZXSo5P5Bb7XYWOh81syohlYL24hbDwxedPUJCA==" }, "node_modules/p-limit": { "version": "5.0.0", @@ -6511,17 +5833,17 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/packet-reader": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-1.0.0.tgz", - "integrity": "sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ==" + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==" }, "node_modules/parse5": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", - "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==", + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.2.1.tgz", + "integrity": "sha512-BuBYQYlv1ckiPdQi/ohiivi9Sagc9JG+Ozs0r7b/0iK3sKmrb0b9FdWdBbOdx6hBCM/F9Ir82ofnBhtZOjCRPQ==", "dependencies": { - "entities": "^4.4.0" + "entities": "^4.5.0" }, "funding": { "url": "https://github.com/inikulin/parse5?sponsor=1" @@ -6549,24 +5871,24 @@ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" }, "node_modules/path-scurry": { - "version": "1.10.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.1.tgz", - "integrity": "sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", "dependencies": { - "lru-cache": "^9.1.1 || ^10.0.0", + "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": ">=16 || 14 >=14.18" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, "node_modules/path-to-regexp": { - "version": "0.1.10", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.10.tgz", - "integrity": "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==" + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", + "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==" }, "node_modules/pathe": { "version": "1.1.2", @@ -6582,15 +5904,13 @@ } }, "node_modules/pg": { - "version": "8.11.3", - "resolved": "https://registry.npmjs.org/pg/-/pg-8.11.3.tgz", - "integrity": "sha512-+9iuvG8QfaaUrrph+kpF24cXkH1YOOUeArRNYIxq1viYHZagBxrTno7cecY1Fa44tJeZvaoG+Djpkc3JwehN5g==", - "dependencies": { - "buffer-writer": "2.0.0", - "packet-reader": "1.0.0", - "pg-connection-string": "^2.6.2", - "pg-pool": "^3.6.1", - "pg-protocol": "^1.6.0", + "version": "8.13.1", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.13.1.tgz", + "integrity": "sha512-OUir1A0rPNZlX//c7ksiu7crsGZTKSOXJPgtNiHGIlC9H0lO+NC6ZDYksSgBYY/thSWhnSRBv8w1lieNNGATNQ==", + "dependencies": { + "pg-connection-string": "^2.7.0", + "pg-pool": "^3.7.0", + "pg-protocol": "^1.7.0", "pg-types": "^2.1.0", "pgpass": "1.x" }, @@ -6633,9 +5953,9 @@ "optional": true }, "node_modules/pg-connection-string": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.2.tgz", - "integrity": "sha512-ch6OwaeaPYcova4kKZ15sbJ2hKb/VP48ZD2gE7i1J+L4MspCtBMAx8nMgz7bksc7IojCIIWuEhHibSMFH8m8oA==" + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.7.0.tgz", + "integrity": "sha512-PI2W9mv53rXJQEOb8xNR8lH7Hr+EKa6oJa38zsK0S/ky2er16ios1wLKhZyxzD7jUReiWokc9WK5nxSnC7W1TA==" }, "node_modules/pg-int8": { "version": "1.0.1", @@ -6646,17 +5966,17 @@ } }, "node_modules/pg-pool": { - "version": "3.6.1", - "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.6.1.tgz", - "integrity": "sha512-jizsIzhkIitxCGfPRzJn1ZdcosIt3pz9Sh3V01fm1vZnbnCMgmGl5wvGGdNN2EL9Rmb0EcFoCkixH4Pu+sP9Og==", + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.7.0.tgz", + "integrity": "sha512-ZOBQForurqh4zZWjrgSwwAtzJ7QiRX0ovFkZr2klsen3Nm0aoh33Ls0fzfv3imeH/nw/O27cjdz5kzYJfeGp/g==", "peerDependencies": { "pg": ">=8.0" } }, "node_modules/pg-protocol": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.6.0.tgz", - "integrity": "sha512-M+PDm637OY5WM307051+bsDia5Xej6d9IR4GwJse1qA1DIhiKlksvrneZOYQq42OM+spubpcNYEo2FcKQrDk+Q==" + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.7.0.tgz", + "integrity": "sha512-hTK/mE36i8fDDhgDFjy6xNOG+LCorxLG3WO17tku+ij6sVHXh1jQUJ8hYAnRhNla4QVD2H8er/FOjc/+EgC6yQ==" }, "node_modules/pg-types": { "version": "2.2.0", @@ -6682,9 +6002,9 @@ } }, "node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==" }, "node_modules/picomatch": { "version": "2.3.1", @@ -6714,19 +6034,32 @@ } }, "node_modules/pkg-types": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.0.3.tgz", - "integrity": "sha512-nN7pYi0AQqJnoLPC9eHFQ8AcyaixBUOwvqc5TDnIKCMEE6I0y8P7OKA7fPexsXGCGxQDl/cmrLAp26LhcwxZ4A==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.3.1.tgz", + "integrity": "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==", "dependencies": { - "jsonc-parser": "^3.2.0", - "mlly": "^1.2.0", - "pathe": "^1.1.0" + "confbox": "^0.1.8", + "mlly": "^1.7.4", + "pathe": "^2.0.1" + } + }, + "node_modules/pkg-types/node_modules/pathe": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.1.tgz", + "integrity": "sha512-6jpjMpOth5S9ITVu5clZ7NOgHNsv5vRQdheL9ztp2vZmM6fRbLvyua1tiBIL4lk8SAe3ARzeXEly6siXCjDHDw==" + }, + "node_modules/possible-typed-array-names": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", + "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", + "engines": { + "node": ">= 0.4" } }, "node_modules/postcss": { - "version": "8.4.35", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.35.tgz", - "integrity": "sha512-u5U8qYpBCpN13BsiEB0CbR1Hhh4Gc0zLFuedrHJKMctHCHAGrMdG0PRM/KErzAL3CU6/eckEtmHNB3x6e3c0vA==", + "version": "8.5.1", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.1.tgz", + "integrity": "sha512-6oz2beyjc5VMn/KV1pPw8fliQkhBXrVn1Z3TVyqZxU8kZpzEKhBdmCFqI6ZbmGtamQvQGuU1sgPTk8ZrXDD7jQ==", "funding": [ { "type": "opencollective", @@ -6742,9 +6075,9 @@ } ], "dependencies": { - "nanoid": "^3.3.7", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" + "nanoid": "^3.3.8", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" }, "engines": { "node": "^10 || ^12 || >=14" @@ -6818,36 +6151,34 @@ } } }, - "node_modules/postcss-load-config/node_modules/lilconfig": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.0.0.tgz", - "integrity": "sha512-K2U4W2Ff5ibV7j7ydLr+zLAkIg5JJ4lPn1Ltsdt+Tz/IjQ8buJ55pZAxoP34lqIiwtF9iAvtLv3JGv7CAyAg+g==", - "engines": { - "node": ">=14" - } - }, "node_modules/postcss-nested": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.0.1.tgz", - "integrity": "sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz", + "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], "dependencies": { - "postcss-selector-parser": "^6.0.11" + "postcss-selector-parser": "^6.1.1" }, "engines": { "node": ">=12.0" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, "peerDependencies": { "postcss": "^8.2.14" } }, "node_modules/postcss-nested/node_modules/postcss-selector-parser": { - "version": "6.0.15", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.15.tgz", - "integrity": "sha512-rEYkQOMUCEMhsKbK66tbEU9QVIxbhN18YiniAwA7XQYTVBqrBy+P2p5JcdqsHgKM2zWylp8d7J6eszocfds5Sw==", + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -6945,7 +6276,6 @@ "resolved": "https://registry.npmjs.org/prisma/-/prisma-5.19.1.tgz", "integrity": "sha512-c5K9MiDaa+VAAyh1OiYk76PXOme9s3E992D7kvvIOhCrNsBQfy2mP2QAQtX0WNj140IgG++12kwZpYB9iIydNQ==", "hasInstallScript": true, - "license": "Apache-2.0", "dependencies": { "@prisma/engines": "5.19.1" }, @@ -6977,9 +6307,15 @@ "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" }, "node_modules/psl": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", - "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==" + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.15.0.tgz", + "integrity": "sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==", + "dependencies": { + "punycode": "^2.3.1" + }, + "funding": { + "url": "https://github.com/sponsors/lupomontero" + } }, "node_modules/punycode": { "version": "2.3.1", @@ -7050,9 +6386,9 @@ } }, "node_modules/react": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", - "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", + "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", "dependencies": { "loose-envify": "^1.1.0" }, @@ -7061,30 +6397,30 @@ } }, "node_modules/react-dom": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", - "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", + "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", "dependencies": { "loose-envify": "^1.1.0", - "scheduler": "^0.23.0" + "scheduler": "^0.23.2" }, "peerDependencies": { - "react": "^18.2.0" + "react": "^18.3.1" } }, "node_modules/react-hook-form": { - "version": "7.50.1", - "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.50.1.tgz", - "integrity": "sha512-3PCY82oE0WgeOgUtIr3nYNNtNvqtJ7BZjsbxh6TnYNbXButaD5WpjOmTjdxZfheuHKR68qfeFnEDVYoSSFPMTQ==", + "version": "7.54.2", + "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.54.2.tgz", + "integrity": "sha512-eHpAUgUjWbZocoQYUHposymRb4ZP6d0uwUnooL2uOybA9/3tPUvoAKqEWK1WaSiTxxOfTpffNZP7QwlnM3/gEg==", "engines": { - "node": ">=12.22.0" + "node": ">=18.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/react-hook-form" }, "peerDependencies": { - "react": "^16.8.0 || ^17 || ^18" + "react": "^16.8.0 || ^17 || ^18 || ^19" } }, "node_modules/react-is": { @@ -7093,11 +6429,11 @@ "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" }, "node_modules/react-router": { - "version": "6.26.2", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.26.2.tgz", - "integrity": "sha512-tvN1iuT03kHgOFnLPfLJ8V95eijteveqdOSk+srqfePtQvqCExB8eHOYnlilbOcyJyKnYkr1vJvf7YqotAJu1A==", + "version": "6.28.1", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.28.1.tgz", + "integrity": "sha512-2omQTA3rkMljmrvvo6WtewGdVh45SpL9hGiCI9uUrwGGfNFDIvGK4gYJsKlJoNVi6AQZcopSCballL+QGOm7fA==", "dependencies": { - "@remix-run/router": "1.19.2" + "@remix-run/router": "1.21.0" }, "engines": { "node": ">=14.0.0" @@ -7107,12 +6443,12 @@ } }, "node_modules/react-router-dom": { - "version": "6.26.2", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.26.2.tgz", - "integrity": "sha512-z7YkaEW0Dy35T3/QKPYB1LjMK2R1fxnHO8kWpUMTBdfVzZrWOiY9a7CtN8HqdWtDUWd5FY6Dl8HFsqVwH4uOtQ==", + "version": "6.28.1", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.28.1.tgz", + "integrity": "sha512-YraE27C/RdjcZwl5UCqF/ffXnZDxpJdk9Q6jw38SZHjXs7NNdpViq2l2c7fO7+4uWaEfcwfGCv3RSg4e1By/fQ==", "dependencies": { - "@remix-run/router": "1.19.2", - "react-router": "6.26.2" + "@remix-run/router": "1.21.0", + "react-router": "6.28.1" }, "engines": { "node": ">=14.0.0" @@ -7172,14 +6508,16 @@ "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" }, "node_modules/regexp.prototype.flags": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz", - "integrity": "sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==", + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", + "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==", "dependencies": { - "call-bind": "^1.0.6", + "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-errors": "^1.3.0", - "set-function-name": "^2.0.1" + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "set-function-name": "^2.0.2" }, "engines": { "node": ">= 0.4" @@ -7202,17 +6540,20 @@ "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==" }, "node_modules/resolve": { - "version": "1.22.8", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", - "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "version": "1.22.10", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", + "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", "dependencies": { - "is-core-module": "^2.13.0", + "is-core-module": "^2.16.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" }, + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -7244,9 +6585,9 @@ } }, "node_modules/rollup": { - "version": "3.29.4", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.4.tgz", - "integrity": "sha512-oWzmBZwvYrU0iJHtDmhsm662rC15FRXmcjCk1xD771dFDx5jJ02ufAQQTn0etB2emNk4J9EZg/yWKpsn9BWGRw==", + "version": "3.29.5", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.5.tgz", + "integrity": "sha512-GVsDdsbJzzy4S/v3dqWPJ7EfvZJfCHiDqe80IyrF59LYuP+e6U1LJoUqeuqRbwAWoMNoXivMNeNAOf5E22VA1w==", "dev": true, "bin": { "rollup": "dist/bin/rollup" @@ -7321,6 +6662,22 @@ } ] }, + "node_modules/safe-regex-test": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", + "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-regex": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", @@ -7338,9 +6695,9 @@ } }, "node_modules/scheduler": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", - "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", + "version": "0.23.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", + "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", "dependencies": { "loose-envify": "^1.1.0" } @@ -7421,34 +6778,35 @@ } }, "node_modules/set-cookie-parser": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.6.0.tgz", - "integrity": "sha512-RVnVQxTXuerk653XfuliOxBP81Sf0+qfQE73LIYKcyMYHG94AuH0kgrQpRDuTZnSmjpysHmzxJXKNfa6PjFhyQ==" + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz", + "integrity": "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==" }, "node_modules/set-function-length": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.1.tgz", - "integrity": "sha512-j4t6ccc+VsKwYHso+kElc5neZpjtq9EnRICFZtWyBsLojhmeF/ZBd/elqm22WJh/BziDe/SBiOeAt0m2mfLD0g==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", "dependencies": { - "define-data-property": "^1.1.2", + "define-data-property": "^1.1.4", "es-errors": "^1.3.0", "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.3", + "get-intrinsic": "^1.2.4", "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.1" + "has-property-descriptors": "^1.0.2" }, "engines": { "node": ">= 0.4" } }, "node_modules/set-function-name": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.1.tgz", - "integrity": "sha512-tMNCiqYVkXIZgc2Hnoy2IvC/f8ezc5koaRFkCjrpWzGpCd3qbZXPzVy9MAZzK1ch/X0jvSkojys3oqJN0qCmdA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", "dependencies": { - "define-data-property": "^1.0.1", + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", "functions-have-names": "^1.2.3", - "has-property-descriptors": "^1.0.0" + "has-property-descriptors": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -7479,14 +6837,65 @@ } }, "node_modules/side-channel": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", - "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", "dependencies": { - "call-bind": "^1.0.7", "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.4", - "object-inspect": "^1.13.1" + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" }, "engines": { "node": ">= 0.4" @@ -7534,9 +6943,9 @@ } }, "node_modules/source-map-js": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", - "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", "engines": { "node": ">=0.10.0" } @@ -7563,16 +6972,17 @@ } }, "node_modules/std-env": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.7.0.tgz", - "integrity": "sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==" + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.8.0.tgz", + "integrity": "sha512-Bc3YwwCB+OzldMxOXJIIvC6cPRWr/LxOp48CdQTOkPyk/t4JWWJbrilwBd7RJzKV8QW7tJkcgAmeuLLJugl5/w==" }, "node_modules/stop-iteration-iterator": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz", - "integrity": "sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", + "integrity": "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==", "dependencies": { - "internal-slot": "^1.0.4" + "es-errors": "^1.3.0", + "internal-slot": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -7702,16 +7112,21 @@ } }, "node_modules/strip-literal": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-1.3.0.tgz", - "integrity": "sha512-PugKzOsyXpArk0yWmUwqOZecSO0GH0bPoctLcqNDH9J04pVW3lflYE0ujElBGTloevcxF5MofAOZ7C5l2b+wLg==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-2.1.1.tgz", + "integrity": "sha512-631UJ6O00eNGfMiWG78ck80dfBab8X6IVFB51jZK5Icd7XAs60Z5y7QdSd/wGIklnWvRbUNloVzhOKKmutxQ6Q==", "dependencies": { - "acorn": "^8.10.0" + "js-tokens": "^9.0.1" }, "funding": { "url": "https://github.com/sponsors/antfu" } }, + "node_modules/strip-literal/node_modules/js-tokens": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.1.tgz", + "integrity": "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==" + }, "node_modules/sucrase": { "version": "3.35.0", "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", @@ -7734,9 +7149,9 @@ } }, "node_modules/superjson": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/superjson/-/superjson-2.2.1.tgz", - "integrity": "sha512-8iGv75BYOa0xRJHK5vRLEjE2H/i4lulTjzpUXic3Eg8akftYjkmQDa8JARQ42rlczXyFR3IeRoeFCc7RxHsYZA==", + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/superjson/-/superjson-2.2.2.tgz", + "integrity": "sha512-5JRxVqC8I8NuOUjzBbvVJAKNM8qoVuH0O77h4WInc/qC2q5IreqKxYwgkga3PfA22OayK2ikceb/B26dztPl+Q==", "dependencies": { "copy-anything": "^3.0.2" }, @@ -7772,32 +7187,32 @@ "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==" }, "node_modules/tailwindcss": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.1.tgz", - "integrity": "sha512-qAYmXRfk3ENzuPBakNK0SRrUDipP8NQnEY6772uDhflcQz5EhRdD7JNZxyrFHVQNCwULPBn6FNPp9brpO7ctcA==", + "version": "3.4.17", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.17.tgz", + "integrity": "sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==", "dependencies": { "@alloc/quick-lru": "^5.2.0", "arg": "^5.0.2", - "chokidar": "^3.5.3", + "chokidar": "^3.6.0", "didyoumean": "^1.2.2", "dlv": "^1.1.3", - "fast-glob": "^3.3.0", + "fast-glob": "^3.3.2", "glob-parent": "^6.0.2", "is-glob": "^4.0.3", - "jiti": "^1.19.1", - "lilconfig": "^2.1.0", - "micromatch": "^4.0.5", + "jiti": "^1.21.6", + "lilconfig": "^3.1.3", + "micromatch": "^4.0.8", "normalize-path": "^3.0.0", "object-hash": "^3.0.0", - "picocolors": "^1.0.0", - "postcss": "^8.4.23", + "picocolors": "^1.1.1", + "postcss": "^8.4.47", "postcss-import": "^15.1.0", "postcss-js": "^4.0.1", - "postcss-load-config": "^4.0.1", - "postcss-nested": "^6.0.1", - "postcss-selector-parser": "^6.0.11", - "resolve": "^1.22.2", - "sucrase": "^3.32.0" + "postcss-load-config": "^4.0.2", + "postcss-nested": "^6.2.0", + "postcss-selector-parser": "^6.1.2", + "resolve": "^1.22.8", + "sucrase": "^3.35.0" }, "bin": { "tailwind": "lib/cli.js", @@ -7808,9 +7223,9 @@ } }, "node_modules/tailwindcss/node_modules/postcss-selector-parser": { - "version": "6.0.15", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.15.tgz", - "integrity": "sha512-rEYkQOMUCEMhsKbK66tbEU9QVIxbhN18YiniAwA7XQYTVBqrBy+P2p5JcdqsHgKM2zWylp8d7J6eszocfds5Sw==", + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -7844,14 +7259,14 @@ "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==" }, "node_modules/tinybench": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.6.0.tgz", - "integrity": "sha512-N8hW3PG/3aOoZAN5V/NSAEDz0ZixDSSt5b/a05iqtpgfLWMSVuCo7w0k2vVvEjdrIoeGqZzweX2WlyioNIHchA==" + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==" }, "node_modules/tinypool": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-0.8.2.tgz", - "integrity": "sha512-SUszKYe5wgsxnNOVlBYO6IC+8VGWdVGZWAqUxp3UErNBtptZvWbwyUOyzNL59zigz2rCA92QiL3wvG+JDSdJdQ==", + "version": "0.8.4", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-0.8.4.tgz", + "integrity": "sha512-i11VH5gS6IFeLY3gMBQ00/MmLncVP7JLXOw1vlgkytLmJK7QnEr7NXf0LBdxfmNPAeyetukOk0bOYrJrFGjYJQ==", "engines": { "node": ">=14.0.0" } @@ -7903,9 +7318,9 @@ } }, "node_modules/tough-cookie": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.3.tgz", - "integrity": "sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==", + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz", + "integrity": "sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==", "dependencies": { "psl": "^1.1.33", "punycode": "^2.1.1", @@ -7933,14 +7348,14 @@ "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==" }, "node_modules/tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" }, "node_modules/type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.1.0.tgz", + "integrity": "sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==", "engines": { "node": ">=4" } @@ -7969,9 +7384,9 @@ } }, "node_modules/typescript": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", - "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", + "version": "5.7.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.3.tgz", + "integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==", "devOptional": true, "bin": { "tsc": "bin/tsc", @@ -7982,14 +7397,14 @@ } }, "node_modules/ufo": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.4.0.tgz", - "integrity": "sha512-Hhy+BhRBleFjpJ2vchUNN40qgkh0366FWJGqVLYBHev0vpHTrXSA0ryT+74UiW6KWsldNurQMKGqCm1M2zBciQ==" + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.5.4.tgz", + "integrity": "sha512-UsUk3byDzKd04EyoZ7U4DOlxQaD14JUKQl6/P7wiX4FNvUfm3XL246n9W5AmqwW5RSFJ27NAuM0iLscAOYUiGQ==" }, "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==" }, "node_modules/universalify": { "version": "0.2.0", @@ -8008,9 +7423,9 @@ } }, "node_modules/update-browserslist-db": { - "version": "1.0.13", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", - "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.2.tgz", + "integrity": "sha512-PPypAm5qvlD7XMZC3BujecnaOxwhrtoFR+Dqkk5Aa/6DssiH0ibKoketaj9w8LP7Bont1rYeoV5plxD7RTEPRg==", "funding": [ { "type": "opencollective", @@ -8026,8 +7441,8 @@ } ], "dependencies": { - "escalade": "^3.1.1", - "picocolors": "^1.0.0" + "escalade": "^3.2.0", + "picocolors": "^1.1.1" }, "bin": { "update-browserslist-db": "cli.js" @@ -8046,11 +7461,11 @@ } }, "node_modules/use-sync-external-store": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", - "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.4.0.tgz", + "integrity": "sha512-9WXSPC5fMv61vaupRkCKCxsPxBocVnwakBEkMIHHpkTTg6icbJtg6jzgtLDm4bl3cSHAca52rYWih0k4K3PfHw==", "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "node_modules/util": { @@ -8099,9 +7514,9 @@ } }, "node_modules/vite": { - "version": "4.5.2", - "resolved": "https://registry.npmjs.org/vite/-/vite-4.5.2.tgz", - "integrity": "sha512-tBCZBNSBbHQkaGyhGCDUGqeo2ph8Fstyp6FMSvTtsXeZSPpSMGlviAOav2hxVTqFcx8Hj/twtWKsMJXNY0xI8w==", + "version": "4.5.5", + "resolved": "https://registry.npmjs.org/vite/-/vite-4.5.5.tgz", + "integrity": "sha512-ifW3Lb2sMdX+WU91s3R0FyQlAyLxOzCSCP37ujw0+r5POeHPwe6udWVIElKQq8gk3t7b8rkmvqC6IHBpCff4GQ==", "dev": true, "dependencies": { "esbuild": "^0.18.10", @@ -8154,9 +7569,9 @@ } }, "node_modules/vite-node": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-1.2.2.tgz", - "integrity": "sha512-1as4rDTgVWJO3n1uHmUYqq7nsFgINQ9u+mRcXpjeOMJUmviqNKjcZB7UfRZrlM7MjYXMKpuWp5oGkjaFLnjawg==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-1.6.0.tgz", + "integrity": "sha512-de6HJgzC+TFzOu0NTC4RAIsyf/DY/ibWDYQUcuEA84EMHhcefTUGkjFHKKEJhQN4A+6I0u++kr3l36ZF2d7XRw==", "dependencies": { "cac": "^6.7.14", "debug": "^4.3.4", @@ -8175,9 +7590,9 @@ } }, "node_modules/vite-node/node_modules/@esbuild/android-arm": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.12.tgz", - "integrity": "sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", "cpu": [ "arm" ], @@ -8190,9 +7605,9 @@ } }, "node_modules/vite-node/node_modules/@esbuild/android-arm64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.12.tgz", - "integrity": "sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", "cpu": [ "arm64" ], @@ -8205,9 +7620,9 @@ } }, "node_modules/vite-node/node_modules/@esbuild/android-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.12.tgz", - "integrity": "sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", "cpu": [ "x64" ], @@ -8220,9 +7635,9 @@ } }, "node_modules/vite-node/node_modules/@esbuild/darwin-arm64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.12.tgz", - "integrity": "sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", "cpu": [ "arm64" ], @@ -8235,9 +7650,9 @@ } }, "node_modules/vite-node/node_modules/@esbuild/darwin-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.12.tgz", - "integrity": "sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", "cpu": [ "x64" ], @@ -8250,9 +7665,9 @@ } }, "node_modules/vite-node/node_modules/@esbuild/freebsd-arm64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.12.tgz", - "integrity": "sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", "cpu": [ "arm64" ], @@ -8265,9 +7680,9 @@ } }, "node_modules/vite-node/node_modules/@esbuild/freebsd-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.12.tgz", - "integrity": "sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", "cpu": [ "x64" ], @@ -8280,9 +7695,9 @@ } }, "node_modules/vite-node/node_modules/@esbuild/linux-arm": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.12.tgz", - "integrity": "sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", "cpu": [ "arm" ], @@ -8295,9 +7710,9 @@ } }, "node_modules/vite-node/node_modules/@esbuild/linux-arm64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.12.tgz", - "integrity": "sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", "cpu": [ "arm64" ], @@ -8310,9 +7725,9 @@ } }, "node_modules/vite-node/node_modules/@esbuild/linux-ia32": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.12.tgz", - "integrity": "sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", "cpu": [ "ia32" ], @@ -8325,9 +7740,9 @@ } }, "node_modules/vite-node/node_modules/@esbuild/linux-loong64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.12.tgz", - "integrity": "sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", "cpu": [ "loong64" ], @@ -8340,9 +7755,9 @@ } }, "node_modules/vite-node/node_modules/@esbuild/linux-mips64el": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.12.tgz", - "integrity": "sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", "cpu": [ "mips64el" ], @@ -8355,9 +7770,9 @@ } }, "node_modules/vite-node/node_modules/@esbuild/linux-ppc64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.12.tgz", - "integrity": "sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", "cpu": [ "ppc64" ], @@ -8370,9 +7785,9 @@ } }, "node_modules/vite-node/node_modules/@esbuild/linux-riscv64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.12.tgz", - "integrity": "sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", "cpu": [ "riscv64" ], @@ -8385,9 +7800,9 @@ } }, "node_modules/vite-node/node_modules/@esbuild/linux-s390x": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.12.tgz", - "integrity": "sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", "cpu": [ "s390x" ], @@ -8400,9 +7815,9 @@ } }, "node_modules/vite-node/node_modules/@esbuild/linux-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.12.tgz", - "integrity": "sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", "cpu": [ "x64" ], @@ -8415,9 +7830,9 @@ } }, "node_modules/vite-node/node_modules/@esbuild/netbsd-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.12.tgz", - "integrity": "sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", "cpu": [ "x64" ], @@ -8430,9 +7845,9 @@ } }, "node_modules/vite-node/node_modules/@esbuild/openbsd-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.12.tgz", - "integrity": "sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", "cpu": [ "x64" ], @@ -8445,9 +7860,9 @@ } }, "node_modules/vite-node/node_modules/@esbuild/sunos-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.12.tgz", - "integrity": "sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", "cpu": [ "x64" ], @@ -8460,9 +7875,9 @@ } }, "node_modules/vite-node/node_modules/@esbuild/win32-arm64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.12.tgz", - "integrity": "sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", "cpu": [ "arm64" ], @@ -8475,9 +7890,9 @@ } }, "node_modules/vite-node/node_modules/@esbuild/win32-ia32": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.12.tgz", - "integrity": "sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", "cpu": [ "ia32" ], @@ -8490,9 +7905,9 @@ } }, "node_modules/vite-node/node_modules/@esbuild/win32-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.12.tgz", - "integrity": "sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", "cpu": [ "x64" ], @@ -8505,11 +7920,11 @@ } }, "node_modules/vite-node/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", "dependencies": { - "ms": "2.1.2" + "ms": "^2.1.3" }, "engines": { "node": ">=6.0" @@ -8521,9 +7936,9 @@ } }, "node_modules/vite-node/node_modules/esbuild": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.12.tgz", - "integrity": "sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", "hasInstallScript": true, "bin": { "esbuild": "bin/esbuild" @@ -8532,42 +7947,42 @@ "node": ">=12" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.19.12", - "@esbuild/android-arm": "0.19.12", - "@esbuild/android-arm64": "0.19.12", - "@esbuild/android-x64": "0.19.12", - "@esbuild/darwin-arm64": "0.19.12", - "@esbuild/darwin-x64": "0.19.12", - "@esbuild/freebsd-arm64": "0.19.12", - "@esbuild/freebsd-x64": "0.19.12", - "@esbuild/linux-arm": "0.19.12", - "@esbuild/linux-arm64": "0.19.12", - "@esbuild/linux-ia32": "0.19.12", - "@esbuild/linux-loong64": "0.19.12", - "@esbuild/linux-mips64el": "0.19.12", - "@esbuild/linux-ppc64": "0.19.12", - "@esbuild/linux-riscv64": "0.19.12", - "@esbuild/linux-s390x": "0.19.12", - "@esbuild/linux-x64": "0.19.12", - "@esbuild/netbsd-x64": "0.19.12", - "@esbuild/openbsd-x64": "0.19.12", - "@esbuild/sunos-x64": "0.19.12", - "@esbuild/win32-arm64": "0.19.12", - "@esbuild/win32-ia32": "0.19.12", - "@esbuild/win32-x64": "0.19.12" + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" } }, "node_modules/vite-node/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, "node_modules/vite-node/node_modules/rollup": { - "version": "4.10.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.10.0.tgz", - "integrity": "sha512-t2v9G2AKxcQ8yrG+WGxctBes1AomT0M4ND7jTFBCVPXQ/WFTvNSefIrNSmLKhIKBrvN8SG+CZslimJcT3W2u2g==", + "version": "4.30.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.30.1.tgz", + "integrity": "sha512-mlJ4glW020fPuLi7DkM/lN97mYEZGWeqBnrljzN0gs7GLctqX3lNWxKQ7Gl712UAX+6fog/L3jh4gb7R6aVi3w==", "dependencies": { - "@types/estree": "1.0.5" + "@types/estree": "1.0.6" }, "bin": { "rollup": "dist/bin/rollup" @@ -8577,30 +7992,36 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.10.0", - "@rollup/rollup-android-arm64": "4.10.0", - "@rollup/rollup-darwin-arm64": "4.10.0", - "@rollup/rollup-darwin-x64": "4.10.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.10.0", - "@rollup/rollup-linux-arm64-gnu": "4.10.0", - "@rollup/rollup-linux-arm64-musl": "4.10.0", - "@rollup/rollup-linux-riscv64-gnu": "4.10.0", - "@rollup/rollup-linux-x64-gnu": "4.10.0", - "@rollup/rollup-linux-x64-musl": "4.10.0", - "@rollup/rollup-win32-arm64-msvc": "4.10.0", - "@rollup/rollup-win32-ia32-msvc": "4.10.0", - "@rollup/rollup-win32-x64-msvc": "4.10.0", + "@rollup/rollup-android-arm-eabi": "4.30.1", + "@rollup/rollup-android-arm64": "4.30.1", + "@rollup/rollup-darwin-arm64": "4.30.1", + "@rollup/rollup-darwin-x64": "4.30.1", + "@rollup/rollup-freebsd-arm64": "4.30.1", + "@rollup/rollup-freebsd-x64": "4.30.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.30.1", + "@rollup/rollup-linux-arm-musleabihf": "4.30.1", + "@rollup/rollup-linux-arm64-gnu": "4.30.1", + "@rollup/rollup-linux-arm64-musl": "4.30.1", + "@rollup/rollup-linux-loongarch64-gnu": "4.30.1", + "@rollup/rollup-linux-powerpc64le-gnu": "4.30.1", + "@rollup/rollup-linux-riscv64-gnu": "4.30.1", + "@rollup/rollup-linux-s390x-gnu": "4.30.1", + "@rollup/rollup-linux-x64-gnu": "4.30.1", + "@rollup/rollup-linux-x64-musl": "4.30.1", + "@rollup/rollup-win32-arm64-msvc": "4.30.1", + "@rollup/rollup-win32-ia32-msvc": "4.30.1", + "@rollup/rollup-win32-x64-msvc": "4.30.1", "fsevents": "~2.3.2" } }, "node_modules/vite-node/node_modules/vite": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.1.1.tgz", - "integrity": "sha512-wclpAgY3F1tR7t9LL5CcHC41YPkQIpKUGeIuT8MdNwNZr6OqOTLs7JX5vIHAtzqLWXts0T+GDrh9pN2arneKqg==", + "version": "5.4.11", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.11.tgz", + "integrity": "sha512-c7jFQRklXua0mTzneGW9QVyxFjUgwcihC4bXEtujIo2ouWCe1Ajt/amn2PCxYnhYfd5k09JX3SB7OYWFKYqj8Q==", "dependencies": { - "esbuild": "^0.19.3", - "postcss": "^8.4.35", - "rollup": "^4.2.0" + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" }, "bin": { "vite": "bin/vite.js" @@ -8619,6 +8040,7 @@ "less": "*", "lightningcss": "^1.21.0", "sass": "*", + "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.4.0" @@ -8636,6 +8058,9 @@ "sass": { "optional": true }, + "sass-embedded": { + "optional": true + }, "stylus": { "optional": true }, @@ -8648,17 +8073,16 @@ } }, "node_modules/vitest": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-1.2.2.tgz", - "integrity": "sha512-d5Ouvrnms3GD9USIK36KG8OZ5bEvKEkITFtnGv56HFaSlbItJuYr7hv2Lkn903+AvRAgSixiamozUVfORUekjw==", - "dependencies": { - "@vitest/expect": "1.2.2", - "@vitest/runner": "1.2.2", - "@vitest/snapshot": "1.2.2", - "@vitest/spy": "1.2.2", - "@vitest/utils": "1.2.2", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-1.6.0.tgz", + "integrity": "sha512-H5r/dN06swuFnzNFhq/dnz37bPXnq8xB2xB5JOVk8K09rUtoeNN+LHWkoQ0A/i3hvbUKKcCei9KpbxqHMLhLLA==", + "dependencies": { + "@vitest/expect": "1.6.0", + "@vitest/runner": "1.6.0", + "@vitest/snapshot": "1.6.0", + "@vitest/spy": "1.6.0", + "@vitest/utils": "1.6.0", "acorn-walk": "^8.3.2", - "cac": "^6.7.14", "chai": "^4.3.10", "debug": "^4.3.4", "execa": "^8.0.1", @@ -8667,11 +8091,11 @@ "pathe": "^1.1.1", "picocolors": "^1.0.0", "std-env": "^3.5.0", - "strip-literal": "^1.3.0", + "strip-literal": "^2.0.0", "tinybench": "^2.5.1", - "tinypool": "^0.8.2", + "tinypool": "^0.8.3", "vite": "^5.0.0", - "vite-node": "1.2.2", + "vite-node": "1.6.0", "why-is-node-running": "^2.2.2" }, "bin": { @@ -8686,8 +8110,8 @@ "peerDependencies": { "@edge-runtime/vm": "*", "@types/node": "^18.0.0 || >=20.0.0", - "@vitest/browser": "^1.0.0", - "@vitest/ui": "^1.0.0", + "@vitest/browser": "1.6.0", + "@vitest/ui": "1.6.0", "happy-dom": "*", "jsdom": "*" }, @@ -8713,9 +8137,9 @@ } }, "node_modules/vitest/node_modules/@esbuild/android-arm": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.12.tgz", - "integrity": "sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", "cpu": [ "arm" ], @@ -8728,9 +8152,9 @@ } }, "node_modules/vitest/node_modules/@esbuild/android-arm64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.12.tgz", - "integrity": "sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", "cpu": [ "arm64" ], @@ -8743,9 +8167,9 @@ } }, "node_modules/vitest/node_modules/@esbuild/android-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.12.tgz", - "integrity": "sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", "cpu": [ "x64" ], @@ -8758,9 +8182,9 @@ } }, "node_modules/vitest/node_modules/@esbuild/darwin-arm64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.12.tgz", - "integrity": "sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", "cpu": [ "arm64" ], @@ -8773,9 +8197,9 @@ } }, "node_modules/vitest/node_modules/@esbuild/darwin-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.12.tgz", - "integrity": "sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", "cpu": [ "x64" ], @@ -8788,9 +8212,9 @@ } }, "node_modules/vitest/node_modules/@esbuild/freebsd-arm64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.12.tgz", - "integrity": "sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", "cpu": [ "arm64" ], @@ -8803,9 +8227,9 @@ } }, "node_modules/vitest/node_modules/@esbuild/freebsd-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.12.tgz", - "integrity": "sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", "cpu": [ "x64" ], @@ -8818,9 +8242,9 @@ } }, "node_modules/vitest/node_modules/@esbuild/linux-arm": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.12.tgz", - "integrity": "sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", "cpu": [ "arm" ], @@ -8833,9 +8257,9 @@ } }, "node_modules/vitest/node_modules/@esbuild/linux-arm64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.12.tgz", - "integrity": "sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", "cpu": [ "arm64" ], @@ -8848,9 +8272,9 @@ } }, "node_modules/vitest/node_modules/@esbuild/linux-ia32": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.12.tgz", - "integrity": "sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", "cpu": [ "ia32" ], @@ -8863,9 +8287,9 @@ } }, "node_modules/vitest/node_modules/@esbuild/linux-loong64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.12.tgz", - "integrity": "sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", "cpu": [ "loong64" ], @@ -8878,9 +8302,9 @@ } }, "node_modules/vitest/node_modules/@esbuild/linux-mips64el": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.12.tgz", - "integrity": "sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", "cpu": [ "mips64el" ], @@ -8893,9 +8317,9 @@ } }, "node_modules/vitest/node_modules/@esbuild/linux-ppc64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.12.tgz", - "integrity": "sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", "cpu": [ "ppc64" ], @@ -8908,9 +8332,9 @@ } }, "node_modules/vitest/node_modules/@esbuild/linux-riscv64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.12.tgz", - "integrity": "sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", "cpu": [ "riscv64" ], @@ -8923,9 +8347,9 @@ } }, "node_modules/vitest/node_modules/@esbuild/linux-s390x": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.12.tgz", - "integrity": "sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", "cpu": [ "s390x" ], @@ -8938,9 +8362,9 @@ } }, "node_modules/vitest/node_modules/@esbuild/linux-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.12.tgz", - "integrity": "sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", "cpu": [ "x64" ], @@ -8953,9 +8377,9 @@ } }, "node_modules/vitest/node_modules/@esbuild/netbsd-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.12.tgz", - "integrity": "sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", "cpu": [ "x64" ], @@ -8968,9 +8392,9 @@ } }, "node_modules/vitest/node_modules/@esbuild/openbsd-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.12.tgz", - "integrity": "sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", "cpu": [ "x64" ], @@ -8983,9 +8407,9 @@ } }, "node_modules/vitest/node_modules/@esbuild/sunos-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.12.tgz", - "integrity": "sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", "cpu": [ "x64" ], @@ -8998,9 +8422,9 @@ } }, "node_modules/vitest/node_modules/@esbuild/win32-arm64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.12.tgz", - "integrity": "sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", "cpu": [ "arm64" ], @@ -9013,9 +8437,9 @@ } }, "node_modules/vitest/node_modules/@esbuild/win32-ia32": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.12.tgz", - "integrity": "sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", "cpu": [ "ia32" ], @@ -9028,9 +8452,9 @@ } }, "node_modules/vitest/node_modules/@esbuild/win32-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.12.tgz", - "integrity": "sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", "cpu": [ "x64" ], @@ -9043,11 +8467,11 @@ } }, "node_modules/vitest/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", "dependencies": { - "ms": "2.1.2" + "ms": "^2.1.3" }, "engines": { "node": ">=6.0" @@ -9059,9 +8483,9 @@ } }, "node_modules/vitest/node_modules/esbuild": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.12.tgz", - "integrity": "sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", "hasInstallScript": true, "bin": { "esbuild": "bin/esbuild" @@ -9070,42 +8494,42 @@ "node": ">=12" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.19.12", - "@esbuild/android-arm": "0.19.12", - "@esbuild/android-arm64": "0.19.12", - "@esbuild/android-x64": "0.19.12", - "@esbuild/darwin-arm64": "0.19.12", - "@esbuild/darwin-x64": "0.19.12", - "@esbuild/freebsd-arm64": "0.19.12", - "@esbuild/freebsd-x64": "0.19.12", - "@esbuild/linux-arm": "0.19.12", - "@esbuild/linux-arm64": "0.19.12", - "@esbuild/linux-ia32": "0.19.12", - "@esbuild/linux-loong64": "0.19.12", - "@esbuild/linux-mips64el": "0.19.12", - "@esbuild/linux-ppc64": "0.19.12", - "@esbuild/linux-riscv64": "0.19.12", - "@esbuild/linux-s390x": "0.19.12", - "@esbuild/linux-x64": "0.19.12", - "@esbuild/netbsd-x64": "0.19.12", - "@esbuild/openbsd-x64": "0.19.12", - "@esbuild/sunos-x64": "0.19.12", - "@esbuild/win32-arm64": "0.19.12", - "@esbuild/win32-ia32": "0.19.12", - "@esbuild/win32-x64": "0.19.12" + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" } }, "node_modules/vitest/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, "node_modules/vitest/node_modules/rollup": { - "version": "4.10.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.10.0.tgz", - "integrity": "sha512-t2v9G2AKxcQ8yrG+WGxctBes1AomT0M4ND7jTFBCVPXQ/WFTvNSefIrNSmLKhIKBrvN8SG+CZslimJcT3W2u2g==", + "version": "4.30.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.30.1.tgz", + "integrity": "sha512-mlJ4glW020fPuLi7DkM/lN97mYEZGWeqBnrljzN0gs7GLctqX3lNWxKQ7Gl712UAX+6fog/L3jh4gb7R6aVi3w==", "dependencies": { - "@types/estree": "1.0.5" + "@types/estree": "1.0.6" }, "bin": { "rollup": "dist/bin/rollup" @@ -9115,30 +8539,36 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.10.0", - "@rollup/rollup-android-arm64": "4.10.0", - "@rollup/rollup-darwin-arm64": "4.10.0", - "@rollup/rollup-darwin-x64": "4.10.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.10.0", - "@rollup/rollup-linux-arm64-gnu": "4.10.0", - "@rollup/rollup-linux-arm64-musl": "4.10.0", - "@rollup/rollup-linux-riscv64-gnu": "4.10.0", - "@rollup/rollup-linux-x64-gnu": "4.10.0", - "@rollup/rollup-linux-x64-musl": "4.10.0", - "@rollup/rollup-win32-arm64-msvc": "4.10.0", - "@rollup/rollup-win32-ia32-msvc": "4.10.0", - "@rollup/rollup-win32-x64-msvc": "4.10.0", + "@rollup/rollup-android-arm-eabi": "4.30.1", + "@rollup/rollup-android-arm64": "4.30.1", + "@rollup/rollup-darwin-arm64": "4.30.1", + "@rollup/rollup-darwin-x64": "4.30.1", + "@rollup/rollup-freebsd-arm64": "4.30.1", + "@rollup/rollup-freebsd-x64": "4.30.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.30.1", + "@rollup/rollup-linux-arm-musleabihf": "4.30.1", + "@rollup/rollup-linux-arm64-gnu": "4.30.1", + "@rollup/rollup-linux-arm64-musl": "4.30.1", + "@rollup/rollup-linux-loongarch64-gnu": "4.30.1", + "@rollup/rollup-linux-powerpc64le-gnu": "4.30.1", + "@rollup/rollup-linux-riscv64-gnu": "4.30.1", + "@rollup/rollup-linux-s390x-gnu": "4.30.1", + "@rollup/rollup-linux-x64-gnu": "4.30.1", + "@rollup/rollup-linux-x64-musl": "4.30.1", + "@rollup/rollup-win32-arm64-msvc": "4.30.1", + "@rollup/rollup-win32-ia32-msvc": "4.30.1", + "@rollup/rollup-win32-x64-msvc": "4.30.1", "fsevents": "~2.3.2" } }, "node_modules/vitest/node_modules/vite": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.1.1.tgz", - "integrity": "sha512-wclpAgY3F1tR7t9LL5CcHC41YPkQIpKUGeIuT8MdNwNZr6OqOTLs7JX5vIHAtzqLWXts0T+GDrh9pN2arneKqg==", + "version": "5.4.11", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.11.tgz", + "integrity": "sha512-c7jFQRklXua0mTzneGW9QVyxFjUgwcihC4bXEtujIo2ouWCe1Ajt/amn2PCxYnhYfd5k09JX3SB7OYWFKYqj8Q==", "dependencies": { - "esbuild": "^0.19.3", - "postcss": "^8.4.35", - "rollup": "^4.2.0" + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" }, "bin": { "vite": "bin/vite.js" @@ -9157,6 +8587,7 @@ "less": "*", "lightningcss": "^1.21.0", "sass": "*", + "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.4.0" @@ -9174,6 +8605,9 @@ "sass": { "optional": true }, + "sass-embedded": { + "optional": true + }, "stylus": { "optional": true }, @@ -9284,44 +8718,51 @@ } }, "node_modules/which-boxed-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", - "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz", + "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==", "dependencies": { - "is-bigint": "^1.0.1", - "is-boolean-object": "^1.1.0", - "is-number-object": "^1.0.4", - "is-string": "^1.0.5", - "is-symbol": "^1.0.3" + "is-bigint": "^1.1.0", + "is-boolean-object": "^1.2.1", + "is-number-object": "^1.1.1", + "is-string": "^1.1.1", + "is-symbol": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/which-collection": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.1.tgz", - "integrity": "sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", + "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", "dependencies": { - "is-map": "^2.0.1", - "is-set": "^2.0.1", - "is-weakmap": "^2.0.1", - "is-weakset": "^2.0.1" + "is-map": "^2.0.3", + "is-set": "^2.0.3", + "is-weakmap": "^2.0.2", + "is-weakset": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/which-typed-array": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.14.tgz", - "integrity": "sha512-VnXFiIW8yNn9kIHN88xvZ4yOWchftKDsRJ8fEPacX/wl1lOvBrhsJ/OeJCXq7B0AaijRuqgzSKalJoPk+D8MPg==", + "version": "1.1.18", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.18.tgz", + "integrity": "sha512-qEcY+KJYlWyLH9vNbsr6/5j59AXk5ni5aakf8ldzBvGde6Iz4sxZGkJyWSAueTG7QhOvNRYb1lDdFmL5Td0QKA==", "dependencies": { - "available-typed-arrays": "^1.0.6", - "call-bind": "^1.0.5", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-tostringtag": "^1.0.1" + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -9331,9 +8772,9 @@ } }, "node_modules/why-is-node-running": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.2.2.tgz", - "integrity": "sha512-6tSwToZxTOcotxHeA+qGCq1mVzKR3CwcJGmVcY+QE8SHy6TnpFnh8PAvPNHYr7EcuVeG0QSMxtYCuO1ta/G/oA==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", "dependencies": { "siginfo": "^2.0.0", "stackback": "0.0.2" @@ -9430,9 +8871,9 @@ } }, "node_modules/ws": { - "version": "8.16.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.16.0.tgz", - "integrity": "sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==", + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", + "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", "engines": { "node": ">=10.0.0" }, @@ -9479,9 +8920,12 @@ } }, "node_modules/yaml": { - "version": "2.3.4", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.4.tgz", - "integrity": "sha512-8aAvwVUSHpfEqTQ4w/KMlf3HcRdt50E5ODIQJBw1fQ5RL34xabzxtUlzTXVqc4rkZsPbvrXKWnABCD7kWSmocA==", + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.0.tgz", + "integrity": "sha512-+hSoy/QHluxmC9kCIJyL/uyFmLmc+e5CFR5Wa+bpIhIj85LVb9ZH2nVnqrHoSvKogwODv0ClqZkmiSSaIH5LTA==", + "bin": { + "yaml": "bin.mjs" + }, "engines": { "node": ">= 14" } @@ -9549,15 +8993,23 @@ } }, "node_modules/yocto-queue": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.0.0.tgz", - "integrity": "sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.1.1.tgz", + "integrity": "sha512-b4JR1PFR10y1mKjhHY9LaGo6tmrgjit7hxVIeAmyMw3jegXR4dhYqLaQF5zMXZxY7tLpMyJeLjr1C4rLmkVe8g==", "engines": { "node": ">=12.20" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } + }, + "node_modules/zod": { + "version": "3.24.1", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.24.1.tgz", + "integrity": "sha512-muH7gBL9sI1nciMZV67X5fTKKBLtwpZ5VBp1vsOQzj1MhrBZ4wlVCm3gedKZWLp0Oyel8sIGfeiz54Su+OVT+A==", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } } } } diff --git a/waspc/packages/wasp-config/src/appSpec.ts b/waspc/packages/wasp-config/src/appSpec.ts index 3742119513..41e2149c67 100644 --- a/waspc/packages/wasp-config/src/appSpec.ts +++ b/waspc/packages/wasp-config/src/appSpec.ts @@ -203,12 +203,14 @@ export type Ref = { export type Server = { setupFn: Optional middlewareConfigFn: Optional + envValidationSchema: Optional } export type Client = { setupFn: Optional rootComponent: Optional baseDir: Optional<`/${string}`> + envValidationSchema: Optional } export type Db = { diff --git a/waspc/packages/wasp-config/src/mapUserSpecToAppSpecDecls.ts b/waspc/packages/wasp-config/src/mapUserSpecToAppSpecDecls.ts index f611531154..e932a6831f 100644 --- a/waspc/packages/wasp-config/src/mapUserSpecToAppSpecDecls.ts +++ b/waspc/packages/wasp-config/src/mapUserSpecToAppSpecDecls.ts @@ -329,19 +329,21 @@ function mapEmailSender( } function mapServer(server: User.ServerConfig): AppSpec.Server { - const { setupFn, middlewareConfigFn } = server + const { setupFn, middlewareConfigFn, envValidationSchema } = server return { setupFn: setupFn && mapExtImport(setupFn), middlewareConfigFn: middlewareConfigFn && mapExtImport(middlewareConfigFn), + envValidationSchema: envValidationSchema && mapExtImport(envValidationSchema), } } function mapClient(client: User.ClientConfig): AppSpec.Client { - const { setupFn, rootComponent, baseDir } = client + const { setupFn, rootComponent, baseDir, envValidationSchema } = client return { setupFn: setupFn && mapExtImport(setupFn), rootComponent: rootComponent && mapExtImport(rootComponent), baseDir, + envValidationSchema: envValidationSchema && mapExtImport(envValidationSchema), } } diff --git a/waspc/packages/wasp-config/src/userApi.ts b/waspc/packages/wasp-config/src/userApi.ts index 02291b58ed..0e183fc03f 100644 --- a/waspc/packages/wasp-config/src/userApi.ts +++ b/waspc/packages/wasp-config/src/userApi.ts @@ -111,6 +111,7 @@ export type ExtImport = export type ServerConfig = { setupFn?: ExtImport middlewareConfigFn?: ExtImport + envValidationSchema?: ExtImport } export type PageConfig = { @@ -127,6 +128,7 @@ export type ClientConfig = { rootComponent?: ExtImport setupFn?: ExtImport baseDir?: `/${string}` + envValidationSchema?: ExtImport } export type DbConfig = { diff --git a/waspc/src/Wasp/AppSpec/App/Client.hs b/waspc/src/Wasp/AppSpec/App/Client.hs index 2764c9205d..8adb0f912e 100644 --- a/waspc/src/Wasp/AppSpec/App/Client.hs +++ b/waspc/src/Wasp/AppSpec/App/Client.hs @@ -16,6 +16,7 @@ data Client = Client { setupFn :: Maybe ExtImport, rootComponent :: Maybe ExtImport, -- We expect the base dir to start with a slash e.g. /client - baseDir :: Maybe String + baseDir :: Maybe String, + envValidationSchema :: Maybe ExtImport } deriving (Show, Eq, Data, Generic, FromJSON) diff --git a/waspc/src/Wasp/AppSpec/App/Server.hs b/waspc/src/Wasp/AppSpec/App/Server.hs index 89b9cee5e6..b7c3bc16e7 100644 --- a/waspc/src/Wasp/AppSpec/App/Server.hs +++ b/waspc/src/Wasp/AppSpec/App/Server.hs @@ -14,6 +14,7 @@ import Wasp.AppSpec.ExtImport (ExtImport) data Server = Server { setupFn :: Maybe ExtImport, - middlewareConfigFn :: Maybe ExtImport + middlewareConfigFn :: Maybe ExtImport, + envValidationSchema :: Maybe ExtImport } deriving (Show, Eq, Data, Generic, FromJSON) diff --git a/waspc/src/Wasp/Generator/EmailSenders.hs b/waspc/src/Wasp/Generator/EmailSenders.hs new file mode 100644 index 0000000000..ea804dd9c4 --- /dev/null +++ b/waspc/src/Wasp/Generator/EmailSenders.hs @@ -0,0 +1,23 @@ +module Wasp.Generator.EmailSenders + ( getEnabledEmailProvidersJson, + ) +where + +import Data.Aeson (KeyValue ((.=)), object) +import qualified Data.Aeson as Aeson +import qualified Wasp.AppSpec.App.EmailSender as AS.App.EmailSender + +getEnabledEmailProvidersJson :: AS.App.EmailSender.EmailSender -> Aeson.Value +getEnabledEmailProvidersJson emailSender = + object $ + makeProviderJson + <$> providersKeyAndName + where + providersKeyAndName = + [ ("isSmtpProviderEnabled", AS.App.EmailSender.SMTP), + ("isSendGridProviderEnabled", AS.App.EmailSender.SendGrid), + ("isMailgunProviderEnabled", AS.App.EmailSender.Mailgun), + ("isDummyProviderEnabled", AS.App.EmailSender.Dummy) + ] + makeProviderJson (key, name) = key .= (enabledEmailSenderName == name) + enabledEmailSenderName = AS.App.EmailSender.provider emailSender diff --git a/waspc/src/Wasp/Generator/SdkGenerator.hs b/waspc/src/Wasp/Generator/SdkGenerator.hs index 259d1f97e3..26906464c6 100644 --- a/waspc/src/Wasp/Generator/SdkGenerator.hs +++ b/waspc/src/Wasp/Generator/SdkGenerator.hs @@ -46,6 +46,7 @@ import qualified Wasp.Generator.SdkGenerator.Client.OperationsGenerator as Clien import Wasp.Generator.SdkGenerator.Client.RouterGenerator (genNewClientRouterApi) import qualified Wasp.Generator.SdkGenerator.Common as C import Wasp.Generator.SdkGenerator.CrudG (genCrud) +import Wasp.Generator.SdkGenerator.EnvValidation (depsRequiredByEnvValidation, genEnvValidation) import Wasp.Generator.SdkGenerator.Server.AuthG (genNewServerApi) import Wasp.Generator.SdkGenerator.Server.CrudG (genNewServerCrudApi) import Wasp.Generator.SdkGenerator.Server.EmailSenderG (depsRequiredByEmail, genNewEmailSenderApi) @@ -62,7 +63,6 @@ import Wasp.Generator.WebAppGenerator.Common reactRouterVersion, reactVersion, ) -import qualified Wasp.Generator.WebAppGenerator.Common as WebApp import qualified Wasp.Job as J import Wasp.Job.IO (readJobMessagesAndPrintThemPrefixed) import Wasp.Job.Process (runNodeCommandAsJob) @@ -99,7 +99,7 @@ genSdk spec = genFileCopy [relfile|client/test/index.ts|], genFileCopy [relfile|client/index.ts|], genFileCopy [relfile|dev/index.ts|], - genClientConfigFile, + genFileCopy [relfile|client/config.ts|], genServerConfigFile spec, genTsConfigJson, genServerUtils spec, @@ -125,6 +125,7 @@ genSdk spec = <++> genNewEmailSenderApi spec <++> genNewJobsApi spec <++> genNewClientRouterApi spec + <++> genEnvValidation spec where genFileCopy = return . C.mkTmplFd @@ -195,7 +196,6 @@ npmDepsForSdk spec = ("express", Server.expressVersionStr), ("mitt", "3.0.0"), ("react", show reactVersion), - ("lodash.merge", "^4.6.2"), ("react-router-dom", show reactRouterVersion), ("react-hook-form", "^7.45.4"), ("superjson", show superjsonVersion) @@ -217,7 +217,8 @@ npmDepsForSdk spec = -- These deps need to be installed in the SDK becasue when we run client tests, -- we are running them from the project root dir and PostCSS and Tailwind -- can't be resolved from WebApp node_modules, so we need to install them in the SDK. - ++ depsRequiredByTailwind spec, + ++ depsRequiredByTailwind spec + ++ depsRequiredByEnvValidation, N.devDependencies = AS.Dependency.fromList [ ("@tsconfig/node" <> majorNodeVersionStr, "latest"), @@ -246,18 +247,9 @@ genServerConfigFile spec = return $ C.mkTmplFdWithData relConfigFilePath tmplDat tmplData = object [ "isAuthEnabled" .= isAuthEnabled spec, - "databaseUrlEnvVarName" .= Db.databaseUrlEnvVarName, - "defaultClientUrl" .= WebApp.getDefaultDevClientUrl spec, - "defaultServerUrl" .= Server.defaultDevServerUrl, - "defaultServerPort" .= Server.defaultServerPort + "databaseUrlEnvVarName" .= Db.databaseUrlEnvVarName ] -genClientConfigFile :: Generator FileDraft -genClientConfigFile = return $ C.mkTmplFdWithData relConfigFilePath tmplData - where - relConfigFilePath = [relfile|client/config.ts|] - tmplData = object ["defaultServerUrl" .= Server.defaultDevServerUrl] - -- todo(filip): remove this duplication, we have almost the same thing in the -- ServerGenerator. genTsConfigJson :: Generator FileDraft diff --git a/waspc/src/Wasp/Generator/SdkGenerator/EmailSender/Providers.hs b/waspc/src/Wasp/Generator/SdkGenerator/EmailSender/Providers.hs index cea9734e70..528a550694 100644 --- a/waspc/src/Wasp/Generator/SdkGenerator/EmailSender/Providers.hs +++ b/waspc/src/Wasp/Generator/SdkGenerator/EmailSender/Providers.hs @@ -15,10 +15,7 @@ import qualified Wasp.SemanticVersion as SV data EmailSenderProvider = EmailSenderProvider { npmDependency :: Maybe AS.Dependency.Dependency, - setupFnFile :: Path' (Rel ProvidersDir) File', - -- We have to use explicit boolean keys in templates (e.g. "isSMTPProviderEnabled") so each - -- provider provides its own key which we pass to the template. - isEnabledKey :: String + setupFnFile :: Path' (Rel ProvidersDir) File' } deriving (Show, Eq) @@ -31,8 +28,7 @@ smtp :: EmailSenderProvider smtp = EmailSenderProvider { npmDependency = Just nodeMailerDependency, - setupFnFile = [relfile|smtp.ts|], - isEnabledKey = "isSmtpProviderUsed" + setupFnFile = [relfile|smtp.ts|] } where nodeMailerVersionRange :: SV.Range @@ -45,8 +41,7 @@ sendGrid :: EmailSenderProvider sendGrid = EmailSenderProvider { npmDependency = Just sendGridDependency, - setupFnFile = [relfile|sendgrid.ts|], - isEnabledKey = "isSendGridProviderUsed" + setupFnFile = [relfile|sendgrid.ts|] } where sendGridVersionRange :: SV.Range @@ -59,8 +54,7 @@ mailgun :: EmailSenderProvider mailgun = EmailSenderProvider { npmDependency = Just mailgunDependency, - setupFnFile = [relfile|mailgun.ts|], - isEnabledKey = "isMailgunProviderUsed" + setupFnFile = [relfile|mailgun.ts|] } where mailgunVersionRange :: SV.Range @@ -73,6 +67,5 @@ dummy :: EmailSenderProvider dummy = EmailSenderProvider { npmDependency = Nothing, - setupFnFile = [relfile|dummy.ts|], - isEnabledKey = "isDummyProviderUsed" + setupFnFile = [relfile|dummy.ts|] } diff --git a/waspc/src/Wasp/Generator/SdkGenerator/EnvValidation.hs b/waspc/src/Wasp/Generator/SdkGenerator/EnvValidation.hs new file mode 100644 index 0000000000..d6d5c163d8 --- /dev/null +++ b/waspc/src/Wasp/Generator/SdkGenerator/EnvValidation.hs @@ -0,0 +1,75 @@ +module Wasp.Generator.SdkGenerator.EnvValidation + ( genEnvValidation, + depsRequiredByEnvValidation, + ) +where + +import Data.Aeson (KeyValue ((.=)), object) +import Data.Maybe (isJust) +import StrongPath (relfile) +import Wasp.AppSpec (AppSpec) +import qualified Wasp.AppSpec.App as AS.App +import qualified Wasp.AppSpec.App.Client as AS.App.Client +import qualified Wasp.AppSpec.App.Dependency as AS.Dependency +import qualified Wasp.AppSpec.App.Server as AS.App.Server +import Wasp.AppSpec.Valid (getApp) +import qualified Wasp.Generator.AuthProviders as AuthProviders +import qualified Wasp.Generator.EmailSenders as EmailSenders +import Wasp.Generator.FileDraft (FileDraft) +import qualified Wasp.Generator.JsImport as GJI +import Wasp.Generator.Monad (Generator) +import qualified Wasp.Generator.SdkGenerator.Common as C +import Wasp.Generator.SdkGenerator.Server.OperationsGenerator (extImportToJsImport) +import qualified Wasp.Generator.ServerGenerator.Common as Server +import qualified Wasp.Generator.WebAppGenerator.Common as WebApp +import qualified Wasp.Project.Db as Db + +genEnvValidation :: AppSpec -> Generator [FileDraft] +genEnvValidation spec = + sequence + [ genServerEnv spec, + genClientEnv spec, + genFileCopy [relfile|env/index.ts|], + genFileCopy [relfile|env/validation.ts|] + ] + where + genFileCopy = return . C.mkTmplFd + +genServerEnv :: AppSpec -> Generator FileDraft +genServerEnv spec = return $ C.mkTmplFdWithData tmplPath tmplData + where + tmplPath = [relfile|server/env.ts|] + tmplData = + object + [ "isAuthEnabled" .= isJust maybeAuth, + "databaseUrlEnvVarName" .= Db.databaseUrlEnvVarName, + "defaultClientUrl" .= WebApp.getDefaultDevClientUrl spec, + "defaultServerUrl" .= Server.defaultDevServerUrl, + "defaultServerPort" .= Server.defaultServerPort, + "enabledAuthProviders" .= (AuthProviders.getEnabledAuthProvidersJson <$> maybeAuth), + "isEmailSenderEnabled" .= isJust maybeEmailSender, + "enabledEmailSenders" .= (EmailSenders.getEnabledEmailProvidersJson <$> maybeEmailSender), + "envValidationSchema" .= GJI.jsImportToImportJson (extImportToJsImport <$> maybeEnvValidationSchema) + ] + maybeAuth = AS.App.auth app + maybeEmailSender = AS.App.emailSender app + maybeEnvValidationSchema = AS.App.server app >>= AS.App.Server.envValidationSchema + app = snd $ getApp spec + +genClientEnv :: AppSpec -> Generator FileDraft +genClientEnv spec = return $ C.mkTmplFdWithData tmplPath tmplData + where + tmplPath = [relfile|client/env.ts|] + tmplData = + object + [ "defaultServerUrl" .= Server.defaultDevServerUrl, + "envValidationSchema" .= GJI.jsImportToImportJson (extImportToJsImport <$> maybeEnvValidationSchema) + ] + maybeEnvValidationSchema = AS.App.client app >>= AS.App.Client.envValidationSchema + app = snd $ getApp spec + +depsRequiredByEnvValidation :: [AS.Dependency.Dependency] +depsRequiredByEnvValidation = + AS.Dependency.fromList + [ ("zod", "^3.23.8") + ] diff --git a/waspc/src/Wasp/Generator/SdkGenerator/Server/EmailSenderG.hs b/waspc/src/Wasp/Generator/SdkGenerator/Server/EmailSenderG.hs index fa6cb2b315..87effec2b9 100644 --- a/waspc/src/Wasp/Generator/SdkGenerator/Server/EmailSenderG.hs +++ b/waspc/src/Wasp/Generator/SdkGenerator/Server/EmailSenderG.hs @@ -1,9 +1,11 @@ -module Wasp.Generator.SdkGenerator.Server.EmailSenderG where +module Wasp.Generator.SdkGenerator.Server.EmailSenderG + ( genNewEmailSenderApi, + depsRequiredByEmail, + ) +where import Data.Aeson (object, (.=)) -import qualified Data.Aeson as Aeson import Data.Maybe (fromMaybe, isJust, maybeToList) -import qualified Data.Text import StrongPath (File', Path', Rel, relfile, ()) import Wasp.AppSpec (AppSpec) import qualified Wasp.AppSpec.App as AS.App @@ -11,6 +13,7 @@ import qualified Wasp.AppSpec.App.Dependency as AS.Dependency import Wasp.AppSpec.App.EmailSender (EmailSender) import qualified Wasp.AppSpec.App.EmailSender as AS.EmailSender import Wasp.AppSpec.Valid (getApp) +import qualified Wasp.Generator.EmailSenders as EmailSenders import Wasp.Generator.FileDraft (FileDraft) import Wasp.Generator.Monad (Generator) import qualified Wasp.Generator.SdkGenerator.Common as C @@ -32,7 +35,7 @@ genIndex :: EmailSender -> Generator FileDraft genIndex email = return $ C.mkTmplFdWithData tmplPath tmplData where tmplPath = [relfile|server/email/index.ts|] - tmplData = getEmailProvidersJson email + tmplData = EmailSenders.getEnabledEmailProvidersJson email genCore :: EmailSender -> Generator [FileDraft] genCore email = @@ -47,7 +50,7 @@ genCoreIndex :: EmailSender -> Generator FileDraft genCoreIndex email = return $ C.mkTmplFdWithData tmplPath tmplData where tmplPath = [relfile|server/email/core/index.ts|] - tmplData = getEmailProvidersJson email + tmplData = EmailSenders.getEnabledEmailProvidersJson email genCoreTypes :: EmailSender -> Generator FileDraft genCoreTypes email = return $ C.mkTmplFdWithData tmplPath tmplData @@ -95,14 +98,6 @@ depsRequiredByEmail spec = maybeToList maybeNpmDepedency maybeProvider = getEmailSenderProvider <$> (AS.App.emailSender . snd . getApp $ spec) maybeNpmDepedency = maybeProvider >>= Providers.npmDependency -getEmailProvidersJson :: EmailSender -> Aeson.Value -getEmailProvidersJson email = - object [isEnabledKey .= True] - where - provider :: Providers.EmailSenderProvider - provider = getEmailSenderProvider email - isEnabledKey = Data.Text.pack $ Providers.isEnabledKey provider - getEmailSenderProvider :: EmailSender -> Providers.EmailSenderProvider getEmailSenderProvider email = case AS.EmailSender.provider email of AS.EmailSender.SMTP -> Providers.smtp diff --git a/waspc/src/Wasp/Generator/SdkGenerator/Server/OAuthG.hs b/waspc/src/Wasp/Generator/SdkGenerator/Server/OAuthG.hs index fb533e22f1..0050aff479 100644 --- a/waspc/src/Wasp/Generator/SdkGenerator/Server/OAuthG.hs +++ b/waspc/src/Wasp/Generator/SdkGenerator/Server/OAuthG.hs @@ -35,7 +35,6 @@ genOAuth auth sequence [ genIndexTs auth, genRedirectHelper, - genFileCopy $ oauthDirInSdkTemplatesDir [relfile|env.ts|], genFileCopy $ oauthDirInSdkTemplatesDir [relfile|oneTimeCode.ts|], genFileCopy $ oauthDirInSdkTemplatesDir [relfile|provider.ts|] ] diff --git a/waspc/src/Wasp/Generator/ServerGenerator.hs b/waspc/src/Wasp/Generator/ServerGenerator.hs index 269e878336..e40797511c 100644 --- a/waspc/src/Wasp/Generator/ServerGenerator.hs +++ b/waspc/src/Wasp/Generator/ServerGenerator.hs @@ -75,7 +75,6 @@ genServer spec = <++> genSrcDir spec <++> genDotEnv spec <++> genJobs spec - <++> genEnvValidationScript <++> genApis spec <++> genCrud spec where @@ -252,12 +251,6 @@ genRoutesIndex spec = operationsRouteInRootRouter :: String operationsRouteInRootRouter = "operations" -genEnvValidationScript :: Generator [FileDraft] -genEnvValidationScript = - return - [ C.mkTmplFd [relfile|scripts/validate-env.mjs|] - ] - genMiddleware :: AppSpec -> Generator [FileDraft] genMiddleware spec = sequence diff --git a/waspc/src/Wasp/Generator/WebAppGenerator.hs b/waspc/src/Wasp/Generator/WebAppGenerator.hs index b4be53e4c2..8cc6cf3560 100644 --- a/waspc/src/Wasp/Generator/WebAppGenerator.hs +++ b/waspc/src/Wasp/Generator/WebAppGenerator.hs @@ -77,7 +77,6 @@ genWebApp spec = do <++> genSrcDir spec <++> genPublicDir spec <++> genDotEnv spec - <++> genEnvValidationScript where genFileCopy = return . C.mkTmplFd @@ -139,8 +138,6 @@ npmDepsForWasp _spec = ("@types/react", "^18.0.37"), ("@types/react-dom", "^18.0.11"), ("@vitejs/plugin-react", "^4.2.1"), - -- NOTE: used in the validate-env.mjs script - ("dotenv", "^16.0.3"), -- NOTE: Make sure to bump the version of the tsconfig -- when updating Vite or React versions ("@tsconfig/vite-react", "^2.0.0") @@ -228,12 +225,6 @@ getIndexTs spec = relPathToWebAppSrcDir :: Path Posix (Rel importLocation) (Dir C.WebAppSrcDir) relPathToWebAppSrcDir = [reldirP|./|] -genEnvValidationScript :: Generator [FileDraft] -genEnvValidationScript = - return - [ C.mkTmplFd [relfile|scripts/validate-env.mjs|] - ] - -- todo(filip): Take care of this as well genViteConfig :: AppSpec -> Generator FileDraft genViteConfig spec = return $ C.mkTmplFdWithData tmplFile tmplData diff --git a/waspc/test/AnalyzerTest.hs b/waspc/test/AnalyzerTest.hs index 6b145d7478..07e779d758 100644 --- a/waspc/test/AnalyzerTest.hs +++ b/waspc/test/AnalyzerTest.hs @@ -164,7 +164,8 @@ spec_Analyzer = do ExtImport (ExtImportField "setupServer") (fromJust $ SP.parseRelFileP "bar.js"), - Server.middlewareConfigFn = Nothing + Server.middlewareConfigFn = Nothing, + Server.envValidationSchema = Nothing }, App.client = Just @@ -175,7 +176,8 @@ spec_Analyzer = do Client.rootComponent = Just $ ExtImport (ExtImportField "App") (fromJust $ SP.parseRelFileP "App.jsx"), - Client.baseDir = Just "/" + Client.baseDir = Just "/", + Client.envValidationSchema = Nothing }, App.db = Just diff --git a/waspc/waspc.cabal b/waspc/waspc.cabal index f428c957a8..2d22d2fd79 100644 --- a/waspc/waspc.cabal +++ b/waspc/waspc.cabal @@ -41,7 +41,6 @@ data-files: Generator/templates/**/*.js Generator/templates/**/*.jsx Generator/templates/**/*.tsx - Generator/templates/**/*.mjs Generator/templates/**/*.png Cli/bash-completion Cli/templates/**/*.css @@ -72,6 +71,11 @@ data-files: packages/studio/dist/**/*.css packages/studio/package.json packages/studio/package-lock.json + packages/wasp-config/dist/**/*.js + packages/wasp-config/dist/**/*.d.ts + packages/wasp-config/dist/**/*.map + packages/wasp-config/package.json + packages/wasp-config/package-lock.json data-dir: data/ source-repository head @@ -281,6 +285,7 @@ library Wasp.Generator.DbGenerator.Jobs Wasp.Generator.DbGenerator.Operations Wasp.Generator.DockerGenerator + Wasp.Generator.EmailSenders Wasp.Generator.ExternalCodeGenerator.Common Wasp.Generator.ExternalConfig.Common Wasp.Generator.ExternalConfig.PackageJson @@ -312,6 +317,7 @@ library Wasp.Generator.SdkGenerator.Common Wasp.Generator.SdkGenerator.CrudG Wasp.Generator.SdkGenerator.EmailSender.Providers + Wasp.Generator.SdkGenerator.EnvValidation Wasp.Generator.SdkGenerator.Server.AuthG Wasp.Generator.SdkGenerator.Server.OAuthG Wasp.Generator.SdkGenerator.Server.CrudG diff --git a/web/docs/project/env-vars.md b/web/docs/project/env-vars.md index a0ac725f74..661e21fc7b 100644 --- a/web/docs/project/env-vars.md +++ b/web/docs/project/env-vars.md @@ -218,13 +218,158 @@ We talk about how to define env vars for each deployment option in the [deployme ## Custom Env Var Validations -TODO: when the Zod validation PRs are merged, describe how users can define their own validations. +If your code requires some environment variables, you usually want to ensure that they are correctly defined. In Wasp, you can define your environment variables validation by defining a [Zod object schema](https://zod.dev/?id=basic-usage) and telling Wasp to use it. + +:::info What is Zod? + +[Zod](https://zod.dev/) is a library that lets you define what you expect from your data. For example, you can use Zod to define that: + +- A value should be a string that's a valid email address. +- A value should be a number between 0 and 100. +- ... and much more. + +::: + +Take a look at an example of defining env vars validation: + + + + +```js title="src/env.js" +import * as z from 'zod' + +import { defineEnvValidationSchema } from 'wasp/env' + +export const serverEnvValidationSchema = defineEnvValidationSchema( + z.object({ + STRIPE_API_KEY: z.string({ + required_error: 'STRIPE_API_KEY is required.', + }), + }) +) + +export const clientEnvValidationSchema = defineEnvValidationSchema( + z.object({ + REACT_APP_NAME: z.string().default('TODO App'), + }) +) +``` + + + + +```ts title="src/env.ts" +import * as z from 'zod' + +import { defineEnvValidationSchema } from 'wasp/env' + +export const serverEnvValidationSchema = defineEnvValidationSchema( + z.object({ + STRIPE_API_KEY: z.string({ + required_error: 'STRIPE_API_KEY is required.', + }), + }) +) + +export const clientEnvValidationSchema = defineEnvValidationSchema( + z.object({ + REACT_APP_NAME: z.string().default('TODO App'), + }) +) +``` + +The `defineEnvValidationSchema` function ensures your Zod schema is type-checked. + + + + +```wasp title="main.wasp" +app myApp { + ... + client: { + envValidationSchema: import { clientEnvValidationSchema } from "@src/env", + }, + server: { + envValidationSchema: import { serverEnvValidationSchema } from "@src/env", + }, +} +``` + +You defined schemas for both the client and the server env vars and told Wasp to use them. Wasp merges your env validation schemas with the built-in env vars validation schemas when it validates the `process.env` object on the server and the `import.meta.env` object on the client. + +This means you can use the `env` object to access **your env vars** like this: + +```ts title="src/stripe.ts" +import { env } from 'wasp/server' + +const stripeApiKey = env.STRIPE_API_KEY +``` + +Read more about the env object in the [API Reference](#api-reference). ## API Reference +There are **Wasp-defined** and **user-defined** env vars. Wasp already comes with built-in validation for Wasp-defined env vars. For your env vars, you can define your own validation. + ### Client Env Vars -Access client env vars in your client code using the `env` object like this: +#### User-defined env vars validation + +You can define your client env vars validation like this: + + + + +```js title="src/env.js" +import * as z from 'zod' + +import { defineEnvValidationSchema } from 'wasp/env' + +export const envValidationSchema = defineEnvValidationSchema( + z.object({ + REACT_APP_ANALYTICS_ID: z.string({ + required_error: 'REACT_APP_ANALYTICS_ID is required.', + }), + }) +) +``` + + + + +```ts title="src/env.ts" +import * as z from 'zod' + +import { defineEnvValidationSchema } from 'wasp/env' + +export const envValidationSchema = defineEnvValidationSchema( + z.object({ + REACT_APP_ANALYTICS_ID: z.string({ + required_error: 'REACT_APP_ANALYTICS_ID is required.', + }), + }) +) +``` + +The `defineEnvValidationSchema` function ensures your Zod schema is type-checked. + + + + +```wasp title="main.wasp" +app myApp { + ... + client: { + envValidationSchema: import { envValidationSchema } from "@src/env", + }, +} +``` + +Wasp merges your env validation schemas with the built-in env vars validation schemas when it validates the `import.meta.env` object. + +#### Accessing env vars in client code + +You can access both **Wasp-defined** and **user-defined** client env vars in your client code using the `env` object: @@ -232,7 +377,11 @@ Access client env vars in your client code using the `env` object like this: ```js title="src/App.js" import { env } from 'wasp/client' -console.log(env.REACT_APP_SOME_VAR_NAME) +// Wasp-defined +const apiUrl = env.REACT_APP_API_URL + +// User-defined +const analyticsId = env.REACT_APP_ANALYTICS_ID ``` @@ -241,45 +390,105 @@ console.log(env.REACT_APP_SOME_VAR_NAME) ```ts title="src/App.ts" import { env } from 'wasp/client' -console.log(env.REACT_APP_SOME_VAR_NAME) +// Wasp-defined +const apiUrl = env.REACT_APP_API_URL + +// User-defined +const analyticsId = env.REACT_APP_ANALYTICS_ID ``` -The `env` object is a validated object that Wasp provides to access client env vars. +You can use `import.meta.env.REACT_APP_SOME_VAR_NAME` directly in your code. We don't recommend this since `import.meta.env` isn't validated and missing env vars can cause runtime errors. -You can use `import.meta.env.REACT_APP_SOME_VAR_NAME` directly in your code, but it's not recommended because it's not validated and can lead to runtime errors if the env var is not defined. +### Server Env Vars - +#### User-defined env vars validation -### Server Env Vars +You can define your env vars validation like this: + + + + +```js title="src/env.js" +import * as z from 'zod' + +import { defineEnvValidationSchema } from 'wasp/env' -Access server env vars in your server code using the `env` object like this: +export const envValidationSchema = defineEnvValidationSchema( + z.object({ + STRIPE_API_KEY: z.string({ + required_error: 'STRIPE_API_KEY is required.', + }), + }) +) +``` + + + + +```ts title="src/env.ts" +import * as z from 'zod' + +import { defineEnvValidationSchema } from 'wasp/env' + +export const envValidationSchema = defineEnvValidationSchema( + z.object({ + STRIPE_API_KEY: z.string({ + required_error: 'STRIPE_API_KEY is required.', + }), + }) +) +``` + +The `defineEnvValidationSchema` function ensures your Zod schema is type-checked. + + + + +```wasp title="main.wasp" +app myApp { + ... + server: { + envValidationSchema: import { envValidationSchema } from "@src/env", + }, +} +``` + +Wasp merges your env validation schemas with the built-in env vars validation schemas when it validates the `process.env` object. + +#### Accessing env vars in server code + +You can access both **Wasp-defined** and **user-defined** client env vars in your client code using the `env` object: -```js title="src/App.js" +```js title="src/stripe.js" import { env } from 'wasp/server' -console.log(env.SOME_SECRET) +// Wasp-defined +const serverUrl = env.WASP_SERVER_URL + +// User-defined +const stripeApiKey = env.STRIPE_API_KEY ``` -```ts title="src/App.ts" +```ts title="src/stripe.ts" import { env } from 'wasp/server' -console.log(env.SOME_SECRET) +// Wasp-defined +const serverUrl = env.WASP_SERVER_URL + +// User-defined +const stripeApiKey = env.STRIPE_API_KEY ``` -The `env` object is a validated object that Wasp provides to access server env vars. - -You can use `process.env.SOME_SECRET` directly in your code, but it's not recommended because it's not validated and can lead to runtime errors if the env var is not defined. - - +You can use `process.env.SOME_SECRET` directly in your code. We don't recommend this since `process.env` isn't validated and missing env vars can cause runtime errors. From e42b9fa70ae0f2c626b7adc0dc74157378c349f1 Mon Sep 17 00:00:00 2001 From: Mihovil Ilakovac Date: Fri, 17 Jan 2025 15:26:03 +0100 Subject: [PATCH 13/22] Project dir computation change --- .../react-app/vite/detectServerImports.ts | 16 +++++++++++++--- .../waspBuild/.wasp/build/.waspchecksums | 2 +- .../build/web-app/vite/detectServerImports.ts | 15 +++++++++++++-- .../waspCompile/.wasp/out/.waspchecksums | 2 +- .../out/web-app/vite/detectServerImports.ts | 15 +++++++++++++-- .../waspComplexTest/.wasp/out/.waspchecksums | 2 +- .../out/web-app/vite/detectServerImports.ts | 15 +++++++++++++-- .../waspJob/.wasp/out/.waspchecksums | 2 +- .../out/web-app/vite/detectServerImports.ts | 15 +++++++++++++-- .../waspMigrate/.wasp/out/.waspchecksums | 2 +- .../out/web-app/vite/detectServerImports.ts | 15 +++++++++++++-- waspc/src/Wasp/Generator/WebAppGenerator.hs | 11 ++--------- 12 files changed, 85 insertions(+), 27 deletions(-) diff --git a/waspc/data/Generator/templates/react-app/vite/detectServerImports.ts b/waspc/data/Generator/templates/react-app/vite/detectServerImports.ts index 078e7d7c9f..c06c1a0638 100644 --- a/waspc/data/Generator/templates/react-app/vite/detectServerImports.ts +++ b/waspc/data/Generator/templates/react-app/vite/detectServerImports.ts @@ -1,7 +1,5 @@ -{{={= =}=}} import { type Plugin } from "vite"; - -const waspProjectDirAbsPath = "{= waspProjectDirAbsPath =}"; +import path from "path"; export function detectServerImports(): Plugin { return { @@ -51,6 +49,18 @@ ${imp.importStatement} This is not supported in the client code.`; } +const waspProjectDirAbsPath = getWaspProjectDirAbsPathFromCwd(); + function getRelativeFilePath(filePath: string): string { return filePath.replace(waspProjectDirAbsPath, ""); } + +// We are not passing the waspProjectDir path from Haskell because +// our e2e tests stop working. Becuase we need to absolute path of the +// Wasp project directory, it contains things like the username of the +// user running the tests, which is different on different machines. +function getWaspProjectDirAbsPathFromCwd(): string { + const webAppDirAbsPath = process.cwd(); + const waspProjectDirAbsPath = path.join(webAppDirAbsPath, "../../../"); + return waspProjectDirAbsPath; +} diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/.waspchecksums b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/.waspchecksums index 91730d84b7..9da08f6fac 100644 --- a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/.waspchecksums +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/.waspchecksums @@ -690,6 +690,6 @@ "file", "web-app/vite/detectServerImports.ts" ], - "dee5af744649129fc98d5a3335380d16e239a932e444187ce7c226ea7560a40f" + "f621683457aeeaac58d89559ea0f2aaeb5ab4c9ec291a7a544dc86cc4029d5d6" ] ] \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/web-app/vite/detectServerImports.ts b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/web-app/vite/detectServerImports.ts index a37136d62a..c06c1a0638 100644 --- a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/web-app/vite/detectServerImports.ts +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/web-app/vite/detectServerImports.ts @@ -1,6 +1,5 @@ import { type Plugin } from "vite"; - -const waspProjectDirAbsPath = "/Users/ilakovac/dev/wasp/waspc/e2e-test/test-outputs/waspBuild-current/waspBuild/"; +import path from "path"; export function detectServerImports(): Plugin { return { @@ -50,6 +49,18 @@ ${imp.importStatement} This is not supported in the client code.`; } +const waspProjectDirAbsPath = getWaspProjectDirAbsPathFromCwd(); + function getRelativeFilePath(filePath: string): string { return filePath.replace(waspProjectDirAbsPath, ""); } + +// We are not passing the waspProjectDir path from Haskell because +// our e2e tests stop working. Becuase we need to absolute path of the +// Wasp project directory, it contains things like the username of the +// user running the tests, which is different on different machines. +function getWaspProjectDirAbsPathFromCwd(): string { + const webAppDirAbsPath = process.cwd(); + const waspProjectDirAbsPath = path.join(webAppDirAbsPath, "../../../"); + return waspProjectDirAbsPath; +} diff --git a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/.waspchecksums b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/.waspchecksums index 1c57ca36f6..59fe6124b0 100644 --- a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/.waspchecksums +++ b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/.waspchecksums @@ -704,6 +704,6 @@ "file", "web-app/vite/detectServerImports.ts" ], - "c1a1223064a98519c281bb41983aaf6135ec4c86a97fdd45ac01322040725341" + "f621683457aeeaac58d89559ea0f2aaeb5ab4c9ec291a7a544dc86cc4029d5d6" ] ] \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/web-app/vite/detectServerImports.ts b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/web-app/vite/detectServerImports.ts index 2034f9bdae..c06c1a0638 100644 --- a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/web-app/vite/detectServerImports.ts +++ b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/web-app/vite/detectServerImports.ts @@ -1,6 +1,5 @@ import { type Plugin } from "vite"; - -const waspProjectDirAbsPath = "/Users/ilakovac/dev/wasp/waspc/e2e-test/test-outputs/waspCompile-current/waspCompile/"; +import path from "path"; export function detectServerImports(): Plugin { return { @@ -50,6 +49,18 @@ ${imp.importStatement} This is not supported in the client code.`; } +const waspProjectDirAbsPath = getWaspProjectDirAbsPathFromCwd(); + function getRelativeFilePath(filePath: string): string { return filePath.replace(waspProjectDirAbsPath, ""); } + +// We are not passing the waspProjectDir path from Haskell because +// our e2e tests stop working. Becuase we need to absolute path of the +// Wasp project directory, it contains things like the username of the +// user running the tests, which is different on different machines. +function getWaspProjectDirAbsPathFromCwd(): string { + const webAppDirAbsPath = process.cwd(); + const waspProjectDirAbsPath = path.join(webAppDirAbsPath, "../../../"); + return waspProjectDirAbsPath; +} diff --git a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/.waspchecksums b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/.waspchecksums index a55c6e39de..d65fe73f3d 100644 --- a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/.waspchecksums +++ b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/.waspchecksums @@ -1341,6 +1341,6 @@ "file", "web-app/vite/detectServerImports.ts" ], - "c2270341bd7d8395bb4cadab68067ee4510f4da4887250218294af628fb57ef0" + "f621683457aeeaac58d89559ea0f2aaeb5ab4c9ec291a7a544dc86cc4029d5d6" ] ] \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/web-app/vite/detectServerImports.ts b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/web-app/vite/detectServerImports.ts index a8163c3e6a..c06c1a0638 100644 --- a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/web-app/vite/detectServerImports.ts +++ b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/web-app/vite/detectServerImports.ts @@ -1,6 +1,5 @@ import { type Plugin } from "vite"; - -const waspProjectDirAbsPath = "/Users/ilakovac/dev/wasp/waspc/e2e-test/test-outputs/waspComplexTest-current/waspComplexTest/"; +import path from "path"; export function detectServerImports(): Plugin { return { @@ -50,6 +49,18 @@ ${imp.importStatement} This is not supported in the client code.`; } +const waspProjectDirAbsPath = getWaspProjectDirAbsPathFromCwd(); + function getRelativeFilePath(filePath: string): string { return filePath.replace(waspProjectDirAbsPath, ""); } + +// We are not passing the waspProjectDir path from Haskell because +// our e2e tests stop working. Becuase we need to absolute path of the +// Wasp project directory, it contains things like the username of the +// user running the tests, which is different on different machines. +function getWaspProjectDirAbsPathFromCwd(): string { + const webAppDirAbsPath = process.cwd(); + const waspProjectDirAbsPath = path.join(webAppDirAbsPath, "../../../"); + return waspProjectDirAbsPath; +} diff --git a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/.waspchecksums b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/.waspchecksums index 215915741c..6e2c9f0aa3 100644 --- a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/.waspchecksums +++ b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/.waspchecksums @@ -802,6 +802,6 @@ "file", "web-app/vite/detectServerImports.ts" ], - "902512e245e0fe0340b8b6ec7eb780a2f6963af283930c25993ee0e6ebc42493" + "f621683457aeeaac58d89559ea0f2aaeb5ab4c9ec291a7a544dc86cc4029d5d6" ] ] \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/web-app/vite/detectServerImports.ts b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/web-app/vite/detectServerImports.ts index 05a2cfa076..c06c1a0638 100644 --- a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/web-app/vite/detectServerImports.ts +++ b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/web-app/vite/detectServerImports.ts @@ -1,6 +1,5 @@ import { type Plugin } from "vite"; - -const waspProjectDirAbsPath = "/Users/ilakovac/dev/wasp/waspc/e2e-test/test-outputs/waspJob-current/waspJob/"; +import path from "path"; export function detectServerImports(): Plugin { return { @@ -50,6 +49,18 @@ ${imp.importStatement} This is not supported in the client code.`; } +const waspProjectDirAbsPath = getWaspProjectDirAbsPathFromCwd(); + function getRelativeFilePath(filePath: string): string { return filePath.replace(waspProjectDirAbsPath, ""); } + +// We are not passing the waspProjectDir path from Haskell because +// our e2e tests stop working. Becuase we need to absolute path of the +// Wasp project directory, it contains things like the username of the +// user running the tests, which is different on different machines. +function getWaspProjectDirAbsPathFromCwd(): string { + const webAppDirAbsPath = process.cwd(); + const waspProjectDirAbsPath = path.join(webAppDirAbsPath, "../../../"); + return waspProjectDirAbsPath; +} diff --git a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/.waspchecksums b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/.waspchecksums index 797f20f58d..97e2bde302 100644 --- a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/.waspchecksums +++ b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/.waspchecksums @@ -704,6 +704,6 @@ "file", "web-app/vite/detectServerImports.ts" ], - "20b838823c154bd928e1e8bc4c8f10a1b3b4c090b990e87e75c44238ac0da8f2" + "f621683457aeeaac58d89559ea0f2aaeb5ab4c9ec291a7a544dc86cc4029d5d6" ] ] \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/web-app/vite/detectServerImports.ts b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/web-app/vite/detectServerImports.ts index 1337434aa5..c06c1a0638 100644 --- a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/web-app/vite/detectServerImports.ts +++ b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/web-app/vite/detectServerImports.ts @@ -1,6 +1,5 @@ import { type Plugin } from "vite"; - -const waspProjectDirAbsPath = "/Users/ilakovac/dev/wasp/waspc/e2e-test/test-outputs/waspMigrate-current/waspMigrate/"; +import path from "path"; export function detectServerImports(): Plugin { return { @@ -50,6 +49,18 @@ ${imp.importStatement} This is not supported in the client code.`; } +const waspProjectDirAbsPath = getWaspProjectDirAbsPathFromCwd(); + function getRelativeFilePath(filePath: string): string { return filePath.replace(waspProjectDirAbsPath, ""); } + +// We are not passing the waspProjectDir path from Haskell because +// our e2e tests stop working. Becuase we need to absolute path of the +// Wasp project directory, it contains things like the username of the +// user running the tests, which is different on different machines. +function getWaspProjectDirAbsPathFromCwd(): string { + const webAppDirAbsPath = process.cwd(); + const waspProjectDirAbsPath = path.join(webAppDirAbsPath, "../../../"); + return waspProjectDirAbsPath; +} diff --git a/waspc/src/Wasp/Generator/WebAppGenerator.hs b/waspc/src/Wasp/Generator/WebAppGenerator.hs index 8cc6cf3560..a92c8d78ee 100644 --- a/waspc/src/Wasp/Generator/WebAppGenerator.hs +++ b/waspc/src/Wasp/Generator/WebAppGenerator.hs @@ -72,7 +72,8 @@ genWebApp spec = do genGitignore, genIndexHtml spec, genViteConfig spec, - genDetectServerImportsVitePlugin spec + -- Vite plugins + genFileCopy [relfile|vite/detectServerImports.ts|] ] <++> genSrcDir spec <++> genPublicDir spec @@ -264,11 +265,3 @@ genViteConfig spec = return $ C.mkTmplFdWithData tmplFile tmplData SP.fromRelDir (Project.dotWaspDirInWaspProjectDir Project.generatedCodeDirInDotWaspDir C.webAppRootDirInProjectRootDir) importName = JsImportModule "customViteConfig" - -genDetectServerImportsVitePlugin :: AppSpec -> Generator FileDraft -genDetectServerImportsVitePlugin spec = return $ C.mkTmplFdWithData tmplFile tmplData - where - tmplFile = C.asTmplFile [relfile|vite/detectServerImports.ts|] - tmplData = object ["waspProjectDirAbsPath" .= waspProjectDirAbsPath] - - waspProjectDirAbsPath = SP.fromAbsDir $ AS.waspProjectDir spec From 68767f9c5338b1b5569854c6ff1e3fef119a93c1 Mon Sep 17 00:00:00 2001 From: Mihovil Ilakovac Date: Thu, 6 Feb 2025 17:11:20 +0100 Subject: [PATCH 14/22] Use `resolveId` hook instead of `transform` --- .../react-app/vite/detectServerImports.ts | 72 ++++++++----------- 1 file changed, 30 insertions(+), 42 deletions(-) diff --git a/waspc/data/Generator/templates/react-app/vite/detectServerImports.ts b/waspc/data/Generator/templates/react-app/vite/detectServerImports.ts index c06c1a0638..3fa2e3f67d 100644 --- a/waspc/data/Generator/templates/react-app/vite/detectServerImports.ts +++ b/waspc/data/Generator/templates/react-app/vite/detectServerImports.ts @@ -1,58 +1,46 @@ -import { type Plugin } from "vite"; -import path from "path"; +import { type Plugin } from 'vite' +import path from 'path' export function detectServerImports(): Plugin { return { name: 'wasp-detect-server-imports', - transform(code, filePath) { - const isInDotWaspFolder = filePath.includes("/.wasp/"); - - // We don't want to check for server imports in the Wasp - // framework code. - if (isInDotWaspFolder) { - return; + enforce: 'pre', + resolveId(source, importer) { + if (!importer) { + return } - const imports = getImportsFromCode(code); + const relativeImporter = getRelativeFilePath(importer) - for (const imp of imports) { - if (imp.moduleName.startsWith("wasp/server")) { - throw new Error(getServerImportErrorMessage(imp, filePath)); - } + // Check only for imports from src/ directory which + // contains the user's code. + if (!relativeImporter.startsWith('src/')) { + return } - }, - }; -} - -type Import = { - importStatement: string; - moduleName: string; -} -const importStatementRegex = /\s*import\s+(?:(?:[\w*\s{},]*)\s+from\s+)?(['"`])([^'"`]+)\1\s*/g; - -function* getImportsFromCode(code: string): Generator { - const matches = code.matchAll(importStatementRegex); - for (const match of matches) { - yield { - importStatement: match[0].trim(), - moduleName: match[2], - }; + ensureNoServerImports(source, relativeImporter) + }, } } -function getServerImportErrorMessage(imp: Import, filePath: string): string { - return `Client module "${getRelativeFilePath(filePath)}" imports server code: - -${imp.importStatement} - -This is not supported in the client code.`; +const serverImportChecks = [ + (moduleName: string) => moduleName.startsWith('wasp/server'), +] + +function ensureNoServerImports(source: string, relativeImporter: string) { + for (const check of serverImportChecks) { + if (check(source)) { + throw new Error( + `Server module "${source}" is being imported in "${relativeImporter}" which is a client file. This is not supported.` + ) + } + } } -const waspProjectDirAbsPath = getWaspProjectDirAbsPathFromCwd(); +const waspProjectDirAbsPath = getWaspProjectDirAbsPathFromCwd() function getRelativeFilePath(filePath: string): string { - return filePath.replace(waspProjectDirAbsPath, ""); + return filePath.replace(waspProjectDirAbsPath, '') } // We are not passing the waspProjectDir path from Haskell because @@ -60,7 +48,7 @@ function getRelativeFilePath(filePath: string): string { // Wasp project directory, it contains things like the username of the // user running the tests, which is different on different machines. function getWaspProjectDirAbsPathFromCwd(): string { - const webAppDirAbsPath = process.cwd(); - const waspProjectDirAbsPath = path.join(webAppDirAbsPath, "../../../"); - return waspProjectDirAbsPath; + const webAppDirAbsPath = process.cwd() + const waspProjectDirAbsPath = path.join(webAppDirAbsPath, '../../../') + return waspProjectDirAbsPath } From 7276406b1b2a40efd50e3657b24b0ef425d590ba Mon Sep 17 00:00:00 2001 From: Mihovil Ilakovac Date: Thu, 6 Feb 2025 20:53:45 +0100 Subject: [PATCH 15/22] Cleanup --- .../react-app/vite/detectServerImports.ts | 19 ++++++++------- waspc/examples/todoApp/src/App.tsx | 4 ++++ waspc/src/Wasp/Generator/WebAppGenerator.hs | 6 ++--- .../Generator/WebAppGenerator/VitePlugins.hs | 23 +++++++++++++++++++ waspc/waspc.cabal | 1 + 5 files changed, 42 insertions(+), 11 deletions(-) create mode 100644 waspc/src/Wasp/Generator/WebAppGenerator/VitePlugins.hs diff --git a/waspc/data/Generator/templates/react-app/vite/detectServerImports.ts b/waspc/data/Generator/templates/react-app/vite/detectServerImports.ts index 3fa2e3f67d..ce166b1af4 100644 --- a/waspc/data/Generator/templates/react-app/vite/detectServerImports.ts +++ b/waspc/data/Generator/templates/react-app/vite/detectServerImports.ts @@ -1,3 +1,4 @@ +{{={= =}=}} import { type Plugin } from 'vite' import path from 'path' @@ -10,28 +11,30 @@ export function detectServerImports(): Plugin { return } - const relativeImporter = getRelativeFilePath(importer) + const importerRelativePath = getRelativeFilePath(importer) // Check only for imports from src/ directory which // contains the user's code. - if (!relativeImporter.startsWith('src/')) { + if (!importerRelativePath.startsWith('src/')) { return } - ensureNoServerImports(source, relativeImporter) + ensureNoServerImports(source, importerRelativePath) }, } } -const serverImportChecks = [ +type ImportCheckPredicate = (moduleName: string) => boolean + +const serverImportChecks: ImportCheckPredicate[] = [ (moduleName: string) => moduleName.startsWith('wasp/server'), ] -function ensureNoServerImports(source: string, relativeImporter: string) { +function ensureNoServerImports(source: string, relativeImporter: string): void { for (const check of serverImportChecks) { if (check(source)) { throw new Error( - `Server module "${source}" is being imported in "${relativeImporter}" which is a client file. This is not supported.` + `Server code cannot be imported in the client code. Import from '${source}' in '${relativeImporter}' is not allowed.` ) } } @@ -40,7 +43,7 @@ function ensureNoServerImports(source: string, relativeImporter: string) { const waspProjectDirAbsPath = getWaspProjectDirAbsPathFromCwd() function getRelativeFilePath(filePath: string): string { - return filePath.replace(waspProjectDirAbsPath, '') + return path.relative(waspProjectDirAbsPath, filePath) } // We are not passing the waspProjectDir path from Haskell because @@ -49,6 +52,6 @@ function getRelativeFilePath(filePath: string): string { // user running the tests, which is different on different machines. function getWaspProjectDirAbsPathFromCwd(): string { const webAppDirAbsPath = process.cwd() - const waspProjectDirAbsPath = path.join(webAppDirAbsPath, '../../../') + const waspProjectDirAbsPath = path.join(webAppDirAbsPath, '{= waspProjectDirFromWebAppDir =}') return waspProjectDirAbsPath } diff --git a/waspc/examples/todoApp/src/App.tsx b/waspc/examples/todoApp/src/App.tsx index 25b9867eb5..4ea53ed4c9 100644 --- a/waspc/examples/todoApp/src/App.tsx +++ b/waspc/examples/todoApp/src/App.tsx @@ -10,6 +10,10 @@ import { getName } from './user' // Necessary to trigger type tests. import './testTypes/operations/client' +import { HttpError } from 'wasp/server' + +HttpError + export function App() { const { data: user } = useAuth() const { data: date } = useQuery(getDate) diff --git a/waspc/src/Wasp/Generator/WebAppGenerator.hs b/waspc/src/Wasp/Generator/WebAppGenerator.hs index d2fcd02362..8c32940fc5 100644 --- a/waspc/src/Wasp/Generator/WebAppGenerator.hs +++ b/waspc/src/Wasp/Generator/WebAppGenerator.hs @@ -49,6 +49,7 @@ import Wasp.Generator.WebAppGenerator.Common import qualified Wasp.Generator.WebAppGenerator.Common as C import Wasp.Generator.WebAppGenerator.JsImport (extImportToImportJson) import Wasp.Generator.WebAppGenerator.RouterGenerator (genRouter) +import Wasp.Generator.WebAppGenerator.VitePlugins (genVitePlugins) import qualified Wasp.Generator.WebSocket as AS.WS import Wasp.JsImport ( JsImport, @@ -73,13 +74,12 @@ genWebApp spec = do genNpmrc, genGitignore, genIndexHtml spec, - genViteConfig spec, - -- Vite plugins - genFileCopy [relfile|vite/detectServerImports.ts|] + genViteConfig spec ] <++> genSrcDir spec <++> genPublicDir spec <++> genDotEnv spec + <++> genVitePlugins where genFileCopy = return . C.mkTmplFd diff --git a/waspc/src/Wasp/Generator/WebAppGenerator/VitePlugins.hs b/waspc/src/Wasp/Generator/WebAppGenerator/VitePlugins.hs new file mode 100644 index 0000000000..a02c9517b3 --- /dev/null +++ b/waspc/src/Wasp/Generator/WebAppGenerator/VitePlugins.hs @@ -0,0 +1,23 @@ +module Wasp.Generator.WebAppGenerator.VitePlugins (genVitePlugins) where + +import Data.Aeson (object, (.=)) +import StrongPath (Dir, Path', Rel, relfile) +import qualified StrongPath as SP +import Wasp.Generator.Common (WebAppRootDir) +import Wasp.Generator.FileDraft (FileDraft) +import Wasp.Generator.Monad (Generator) +import qualified Wasp.Generator.WebAppGenerator.Common as C +import Wasp.Project.Common (WaspProjectDir, waspProjectDirFromAppComponentDir) + +genVitePlugins :: Generator [FileDraft] +genVitePlugins = sequence [genDetectServerImportsPlugin] + +genDetectServerImportsPlugin :: Generator FileDraft +genDetectServerImportsPlugin = return $ C.mkTmplFdWithData [relfile|vite/detectServerImports.ts|] tmplData + where + tmplData = + object + [ "waspProjectDirFromWebAppDir" .= SP.fromRelDir waspProjectDirFromWebAppDir + ] + + waspProjectDirFromWebAppDir = waspProjectDirFromAppComponentDir :: Path' (Rel WebAppRootDir) (Dir WaspProjectDir) diff --git a/waspc/waspc.cabal b/waspc/waspc.cabal index a70ea00b5a..8fabfb204a 100644 --- a/waspc/waspc.cabal +++ b/waspc/waspc.cabal @@ -355,6 +355,7 @@ library Wasp.Generator.WebAppGenerator.Setup Wasp.Generator.WebAppGenerator.Start Wasp.Generator.WebAppGenerator.Test + Wasp.Generator.WebAppGenerator.VitePlugins Wasp.Generator.WebSocket Wasp.Generator.WriteFileDrafts Wasp.JsImport From 5279cdabd85072b5d382716f5ffe2460ab6ab47b Mon Sep 17 00:00:00 2001 From: Mihovil Ilakovac Date: Thu, 6 Feb 2025 20:56:50 +0100 Subject: [PATCH 16/22] e2e tests Signed-off-by: Mihovil Ilakovac --- .../waspBuild/.wasp/build/.waspchecksums | 2 +- .../build/web-app/vite/detectServerImports.ts | 70 ++++++++----------- .../waspCompile/.wasp/out/.waspchecksums | 2 +- .../out/web-app/vite/detectServerImports.ts | 70 ++++++++----------- .../waspComplexTest/.wasp/out/.waspchecksums | 2 +- .../out/web-app/vite/detectServerImports.ts | 70 ++++++++----------- .../waspJob/.wasp/out/.waspchecksums | 2 +- .../out/web-app/vite/detectServerImports.ts | 70 ++++++++----------- .../waspMigrate/.wasp/out/.waspchecksums | 2 +- .../out/web-app/vite/detectServerImports.ts | 70 ++++++++----------- 10 files changed, 155 insertions(+), 205 deletions(-) diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/.waspchecksums b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/.waspchecksums index 574b85971b..b6d425064d 100644 --- a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/.waspchecksums +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/.waspchecksums @@ -697,6 +697,6 @@ "file", "web-app/vite/detectServerImports.ts" ], - "f621683457aeeaac58d89559ea0f2aaeb5ab4c9ec291a7a544dc86cc4029d5d6" + "8dc4cacd4cee4d2e01338ec5b015f472afcefcdeb6cfee2dfd5e963d4ab93a38" ] ] \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/web-app/vite/detectServerImports.ts b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/web-app/vite/detectServerImports.ts index c06c1a0638..f332407c66 100644 --- a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/web-app/vite/detectServerImports.ts +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/web-app/vite/detectServerImports.ts @@ -1,58 +1,48 @@ -import { type Plugin } from "vite"; -import path from "path"; +import { type Plugin } from 'vite' +import path from 'path' export function detectServerImports(): Plugin { return { name: 'wasp-detect-server-imports', - transform(code, filePath) { - const isInDotWaspFolder = filePath.includes("/.wasp/"); - - // We don't want to check for server imports in the Wasp - // framework code. - if (isInDotWaspFolder) { - return; + enforce: 'pre', + resolveId(source, importer) { + if (!importer) { + return } - const imports = getImportsFromCode(code); + const importerRelativePath = getRelativeFilePath(importer) - for (const imp of imports) { - if (imp.moduleName.startsWith("wasp/server")) { - throw new Error(getServerImportErrorMessage(imp, filePath)); - } + // Check only for imports from src/ directory which + // contains the user's code. + if (!importerRelativePath.startsWith('src/')) { + return } - }, - }; -} - -type Import = { - importStatement: string; - moduleName: string; -} - -const importStatementRegex = /\s*import\s+(?:(?:[\w*\s{},]*)\s+from\s+)?(['"`])([^'"`]+)\1\s*/g; -function* getImportsFromCode(code: string): Generator { - const matches = code.matchAll(importStatementRegex); - for (const match of matches) { - yield { - importStatement: match[0].trim(), - moduleName: match[2], - }; + ensureNoServerImports(source, importerRelativePath) + }, } } -function getServerImportErrorMessage(imp: Import, filePath: string): string { - return `Client module "${getRelativeFilePath(filePath)}" imports server code: +type ImportCheckPredicate = (moduleName: string) => boolean -${imp.importStatement} +const serverImportChecks: ImportCheckPredicate[] = [ + (moduleName: string) => moduleName.startsWith('wasp/server'), +] -This is not supported in the client code.`; +function ensureNoServerImports(source: string, relativeImporter: string): void { + for (const check of serverImportChecks) { + if (check(source)) { + throw new Error( + `Server code cannot be imported in the client code. Import from '${source}' in '${relativeImporter}' is not allowed.` + ) + } + } } -const waspProjectDirAbsPath = getWaspProjectDirAbsPathFromCwd(); +const waspProjectDirAbsPath = getWaspProjectDirAbsPathFromCwd() function getRelativeFilePath(filePath: string): string { - return filePath.replace(waspProjectDirAbsPath, ""); + return path.relative(waspProjectDirAbsPath, filePath) } // We are not passing the waspProjectDir path from Haskell because @@ -60,7 +50,7 @@ function getRelativeFilePath(filePath: string): string { // Wasp project directory, it contains things like the username of the // user running the tests, which is different on different machines. function getWaspProjectDirAbsPathFromCwd(): string { - const webAppDirAbsPath = process.cwd(); - const waspProjectDirAbsPath = path.join(webAppDirAbsPath, "../../../"); - return waspProjectDirAbsPath; + const webAppDirAbsPath = process.cwd() + const waspProjectDirAbsPath = path.join(webAppDirAbsPath, '../../../') + return waspProjectDirAbsPath } diff --git a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/.waspchecksums b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/.waspchecksums index 99e1eb9dd7..e64a9feb10 100644 --- a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/.waspchecksums +++ b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/.waspchecksums @@ -711,6 +711,6 @@ "file", "web-app/vite/detectServerImports.ts" ], - "f621683457aeeaac58d89559ea0f2aaeb5ab4c9ec291a7a544dc86cc4029d5d6" + "8dc4cacd4cee4d2e01338ec5b015f472afcefcdeb6cfee2dfd5e963d4ab93a38" ] ] \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/web-app/vite/detectServerImports.ts b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/web-app/vite/detectServerImports.ts index c06c1a0638..f332407c66 100644 --- a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/web-app/vite/detectServerImports.ts +++ b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/web-app/vite/detectServerImports.ts @@ -1,58 +1,48 @@ -import { type Plugin } from "vite"; -import path from "path"; +import { type Plugin } from 'vite' +import path from 'path' export function detectServerImports(): Plugin { return { name: 'wasp-detect-server-imports', - transform(code, filePath) { - const isInDotWaspFolder = filePath.includes("/.wasp/"); - - // We don't want to check for server imports in the Wasp - // framework code. - if (isInDotWaspFolder) { - return; + enforce: 'pre', + resolveId(source, importer) { + if (!importer) { + return } - const imports = getImportsFromCode(code); + const importerRelativePath = getRelativeFilePath(importer) - for (const imp of imports) { - if (imp.moduleName.startsWith("wasp/server")) { - throw new Error(getServerImportErrorMessage(imp, filePath)); - } + // Check only for imports from src/ directory which + // contains the user's code. + if (!importerRelativePath.startsWith('src/')) { + return } - }, - }; -} - -type Import = { - importStatement: string; - moduleName: string; -} - -const importStatementRegex = /\s*import\s+(?:(?:[\w*\s{},]*)\s+from\s+)?(['"`])([^'"`]+)\1\s*/g; -function* getImportsFromCode(code: string): Generator { - const matches = code.matchAll(importStatementRegex); - for (const match of matches) { - yield { - importStatement: match[0].trim(), - moduleName: match[2], - }; + ensureNoServerImports(source, importerRelativePath) + }, } } -function getServerImportErrorMessage(imp: Import, filePath: string): string { - return `Client module "${getRelativeFilePath(filePath)}" imports server code: +type ImportCheckPredicate = (moduleName: string) => boolean -${imp.importStatement} +const serverImportChecks: ImportCheckPredicate[] = [ + (moduleName: string) => moduleName.startsWith('wasp/server'), +] -This is not supported in the client code.`; +function ensureNoServerImports(source: string, relativeImporter: string): void { + for (const check of serverImportChecks) { + if (check(source)) { + throw new Error( + `Server code cannot be imported in the client code. Import from '${source}' in '${relativeImporter}' is not allowed.` + ) + } + } } -const waspProjectDirAbsPath = getWaspProjectDirAbsPathFromCwd(); +const waspProjectDirAbsPath = getWaspProjectDirAbsPathFromCwd() function getRelativeFilePath(filePath: string): string { - return filePath.replace(waspProjectDirAbsPath, ""); + return path.relative(waspProjectDirAbsPath, filePath) } // We are not passing the waspProjectDir path from Haskell because @@ -60,7 +50,7 @@ function getRelativeFilePath(filePath: string): string { // Wasp project directory, it contains things like the username of the // user running the tests, which is different on different machines. function getWaspProjectDirAbsPathFromCwd(): string { - const webAppDirAbsPath = process.cwd(); - const waspProjectDirAbsPath = path.join(webAppDirAbsPath, "../../../"); - return waspProjectDirAbsPath; + const webAppDirAbsPath = process.cwd() + const waspProjectDirAbsPath = path.join(webAppDirAbsPath, '../../../') + return waspProjectDirAbsPath } diff --git a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/.waspchecksums b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/.waspchecksums index b0fcdc82ef..0ca8777dbd 100644 --- a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/.waspchecksums +++ b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/.waspchecksums @@ -1348,6 +1348,6 @@ "file", "web-app/vite/detectServerImports.ts" ], - "f621683457aeeaac58d89559ea0f2aaeb5ab4c9ec291a7a544dc86cc4029d5d6" + "8dc4cacd4cee4d2e01338ec5b015f472afcefcdeb6cfee2dfd5e963d4ab93a38" ] ] \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/web-app/vite/detectServerImports.ts b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/web-app/vite/detectServerImports.ts index c06c1a0638..f332407c66 100644 --- a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/web-app/vite/detectServerImports.ts +++ b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/web-app/vite/detectServerImports.ts @@ -1,58 +1,48 @@ -import { type Plugin } from "vite"; -import path from "path"; +import { type Plugin } from 'vite' +import path from 'path' export function detectServerImports(): Plugin { return { name: 'wasp-detect-server-imports', - transform(code, filePath) { - const isInDotWaspFolder = filePath.includes("/.wasp/"); - - // We don't want to check for server imports in the Wasp - // framework code. - if (isInDotWaspFolder) { - return; + enforce: 'pre', + resolveId(source, importer) { + if (!importer) { + return } - const imports = getImportsFromCode(code); + const importerRelativePath = getRelativeFilePath(importer) - for (const imp of imports) { - if (imp.moduleName.startsWith("wasp/server")) { - throw new Error(getServerImportErrorMessage(imp, filePath)); - } + // Check only for imports from src/ directory which + // contains the user's code. + if (!importerRelativePath.startsWith('src/')) { + return } - }, - }; -} - -type Import = { - importStatement: string; - moduleName: string; -} - -const importStatementRegex = /\s*import\s+(?:(?:[\w*\s{},]*)\s+from\s+)?(['"`])([^'"`]+)\1\s*/g; -function* getImportsFromCode(code: string): Generator { - const matches = code.matchAll(importStatementRegex); - for (const match of matches) { - yield { - importStatement: match[0].trim(), - moduleName: match[2], - }; + ensureNoServerImports(source, importerRelativePath) + }, } } -function getServerImportErrorMessage(imp: Import, filePath: string): string { - return `Client module "${getRelativeFilePath(filePath)}" imports server code: +type ImportCheckPredicate = (moduleName: string) => boolean -${imp.importStatement} +const serverImportChecks: ImportCheckPredicate[] = [ + (moduleName: string) => moduleName.startsWith('wasp/server'), +] -This is not supported in the client code.`; +function ensureNoServerImports(source: string, relativeImporter: string): void { + for (const check of serverImportChecks) { + if (check(source)) { + throw new Error( + `Server code cannot be imported in the client code. Import from '${source}' in '${relativeImporter}' is not allowed.` + ) + } + } } -const waspProjectDirAbsPath = getWaspProjectDirAbsPathFromCwd(); +const waspProjectDirAbsPath = getWaspProjectDirAbsPathFromCwd() function getRelativeFilePath(filePath: string): string { - return filePath.replace(waspProjectDirAbsPath, ""); + return path.relative(waspProjectDirAbsPath, filePath) } // We are not passing the waspProjectDir path from Haskell because @@ -60,7 +50,7 @@ function getRelativeFilePath(filePath: string): string { // Wasp project directory, it contains things like the username of the // user running the tests, which is different on different machines. function getWaspProjectDirAbsPathFromCwd(): string { - const webAppDirAbsPath = process.cwd(); - const waspProjectDirAbsPath = path.join(webAppDirAbsPath, "../../../"); - return waspProjectDirAbsPath; + const webAppDirAbsPath = process.cwd() + const waspProjectDirAbsPath = path.join(webAppDirAbsPath, '../../../') + return waspProjectDirAbsPath } diff --git a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/.waspchecksums b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/.waspchecksums index e74c2f4045..5b7abfae00 100644 --- a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/.waspchecksums +++ b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/.waspchecksums @@ -809,6 +809,6 @@ "file", "web-app/vite/detectServerImports.ts" ], - "f621683457aeeaac58d89559ea0f2aaeb5ab4c9ec291a7a544dc86cc4029d5d6" + "8dc4cacd4cee4d2e01338ec5b015f472afcefcdeb6cfee2dfd5e963d4ab93a38" ] ] \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/web-app/vite/detectServerImports.ts b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/web-app/vite/detectServerImports.ts index c06c1a0638..f332407c66 100644 --- a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/web-app/vite/detectServerImports.ts +++ b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/web-app/vite/detectServerImports.ts @@ -1,58 +1,48 @@ -import { type Plugin } from "vite"; -import path from "path"; +import { type Plugin } from 'vite' +import path from 'path' export function detectServerImports(): Plugin { return { name: 'wasp-detect-server-imports', - transform(code, filePath) { - const isInDotWaspFolder = filePath.includes("/.wasp/"); - - // We don't want to check for server imports in the Wasp - // framework code. - if (isInDotWaspFolder) { - return; + enforce: 'pre', + resolveId(source, importer) { + if (!importer) { + return } - const imports = getImportsFromCode(code); + const importerRelativePath = getRelativeFilePath(importer) - for (const imp of imports) { - if (imp.moduleName.startsWith("wasp/server")) { - throw new Error(getServerImportErrorMessage(imp, filePath)); - } + // Check only for imports from src/ directory which + // contains the user's code. + if (!importerRelativePath.startsWith('src/')) { + return } - }, - }; -} - -type Import = { - importStatement: string; - moduleName: string; -} - -const importStatementRegex = /\s*import\s+(?:(?:[\w*\s{},]*)\s+from\s+)?(['"`])([^'"`]+)\1\s*/g; -function* getImportsFromCode(code: string): Generator { - const matches = code.matchAll(importStatementRegex); - for (const match of matches) { - yield { - importStatement: match[0].trim(), - moduleName: match[2], - }; + ensureNoServerImports(source, importerRelativePath) + }, } } -function getServerImportErrorMessage(imp: Import, filePath: string): string { - return `Client module "${getRelativeFilePath(filePath)}" imports server code: +type ImportCheckPredicate = (moduleName: string) => boolean -${imp.importStatement} +const serverImportChecks: ImportCheckPredicate[] = [ + (moduleName: string) => moduleName.startsWith('wasp/server'), +] -This is not supported in the client code.`; +function ensureNoServerImports(source: string, relativeImporter: string): void { + for (const check of serverImportChecks) { + if (check(source)) { + throw new Error( + `Server code cannot be imported in the client code. Import from '${source}' in '${relativeImporter}' is not allowed.` + ) + } + } } -const waspProjectDirAbsPath = getWaspProjectDirAbsPathFromCwd(); +const waspProjectDirAbsPath = getWaspProjectDirAbsPathFromCwd() function getRelativeFilePath(filePath: string): string { - return filePath.replace(waspProjectDirAbsPath, ""); + return path.relative(waspProjectDirAbsPath, filePath) } // We are not passing the waspProjectDir path from Haskell because @@ -60,7 +50,7 @@ function getRelativeFilePath(filePath: string): string { // Wasp project directory, it contains things like the username of the // user running the tests, which is different on different machines. function getWaspProjectDirAbsPathFromCwd(): string { - const webAppDirAbsPath = process.cwd(); - const waspProjectDirAbsPath = path.join(webAppDirAbsPath, "../../../"); - return waspProjectDirAbsPath; + const webAppDirAbsPath = process.cwd() + const waspProjectDirAbsPath = path.join(webAppDirAbsPath, '../../../') + return waspProjectDirAbsPath } diff --git a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/.waspchecksums b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/.waspchecksums index 94bba77d3e..a06e58a185 100644 --- a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/.waspchecksums +++ b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/.waspchecksums @@ -711,6 +711,6 @@ "file", "web-app/vite/detectServerImports.ts" ], - "f621683457aeeaac58d89559ea0f2aaeb5ab4c9ec291a7a544dc86cc4029d5d6" + "8dc4cacd4cee4d2e01338ec5b015f472afcefcdeb6cfee2dfd5e963d4ab93a38" ] ] \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/web-app/vite/detectServerImports.ts b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/web-app/vite/detectServerImports.ts index c06c1a0638..f332407c66 100644 --- a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/web-app/vite/detectServerImports.ts +++ b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/web-app/vite/detectServerImports.ts @@ -1,58 +1,48 @@ -import { type Plugin } from "vite"; -import path from "path"; +import { type Plugin } from 'vite' +import path from 'path' export function detectServerImports(): Plugin { return { name: 'wasp-detect-server-imports', - transform(code, filePath) { - const isInDotWaspFolder = filePath.includes("/.wasp/"); - - // We don't want to check for server imports in the Wasp - // framework code. - if (isInDotWaspFolder) { - return; + enforce: 'pre', + resolveId(source, importer) { + if (!importer) { + return } - const imports = getImportsFromCode(code); + const importerRelativePath = getRelativeFilePath(importer) - for (const imp of imports) { - if (imp.moduleName.startsWith("wasp/server")) { - throw new Error(getServerImportErrorMessage(imp, filePath)); - } + // Check only for imports from src/ directory which + // contains the user's code. + if (!importerRelativePath.startsWith('src/')) { + return } - }, - }; -} - -type Import = { - importStatement: string; - moduleName: string; -} - -const importStatementRegex = /\s*import\s+(?:(?:[\w*\s{},]*)\s+from\s+)?(['"`])([^'"`]+)\1\s*/g; -function* getImportsFromCode(code: string): Generator { - const matches = code.matchAll(importStatementRegex); - for (const match of matches) { - yield { - importStatement: match[0].trim(), - moduleName: match[2], - }; + ensureNoServerImports(source, importerRelativePath) + }, } } -function getServerImportErrorMessage(imp: Import, filePath: string): string { - return `Client module "${getRelativeFilePath(filePath)}" imports server code: +type ImportCheckPredicate = (moduleName: string) => boolean -${imp.importStatement} +const serverImportChecks: ImportCheckPredicate[] = [ + (moduleName: string) => moduleName.startsWith('wasp/server'), +] -This is not supported in the client code.`; +function ensureNoServerImports(source: string, relativeImporter: string): void { + for (const check of serverImportChecks) { + if (check(source)) { + throw new Error( + `Server code cannot be imported in the client code. Import from '${source}' in '${relativeImporter}' is not allowed.` + ) + } + } } -const waspProjectDirAbsPath = getWaspProjectDirAbsPathFromCwd(); +const waspProjectDirAbsPath = getWaspProjectDirAbsPathFromCwd() function getRelativeFilePath(filePath: string): string { - return filePath.replace(waspProjectDirAbsPath, ""); + return path.relative(waspProjectDirAbsPath, filePath) } // We are not passing the waspProjectDir path from Haskell because @@ -60,7 +50,7 @@ function getRelativeFilePath(filePath: string): string { // Wasp project directory, it contains things like the username of the // user running the tests, which is different on different machines. function getWaspProjectDirAbsPathFromCwd(): string { - const webAppDirAbsPath = process.cwd(); - const waspProjectDirAbsPath = path.join(webAppDirAbsPath, "../../../"); - return waspProjectDirAbsPath; + const webAppDirAbsPath = process.cwd() + const waspProjectDirAbsPath = path.join(webAppDirAbsPath, '../../../') + return waspProjectDirAbsPath } From 34fff43e10846b7bd0c0aa4974f56197ff288ed4 Mon Sep 17 00:00:00 2001 From: Mihovil Ilakovac Date: Fri, 7 Feb 2025 14:03:39 +0100 Subject: [PATCH 17/22] Update path logic. --- .../react-app/vite/detectServerImports.ts | 32 +++++++++++-------- .../Generator/WebAppGenerator/VitePlugins.hs | 5 +-- 2 files changed, 22 insertions(+), 15 deletions(-) diff --git a/waspc/data/Generator/templates/react-app/vite/detectServerImports.ts b/waspc/data/Generator/templates/react-app/vite/detectServerImports.ts index ce166b1af4..d0763cc74b 100644 --- a/waspc/data/Generator/templates/react-app/vite/detectServerImports.ts +++ b/waspc/data/Generator/templates/react-app/vite/detectServerImports.ts @@ -2,6 +2,8 @@ import { type Plugin } from 'vite' import path from 'path' +const waspProjectDirAbsPath = getWaspProjectDirAbsPathFromCwd() + export function detectServerImports(): Plugin { return { name: 'wasp-detect-server-imports', @@ -11,11 +13,9 @@ export function detectServerImports(): Plugin { return } - const importerRelativePath = getRelativeFilePath(importer) + const importerRelativePath = getPathRelativeToWaspProjectDir(importer) - // Check only for imports from src/ directory which - // contains the user's code. - if (!importerRelativePath.startsWith('src/')) { + if (!isPathToUserCode(importerRelativePath)) { return } @@ -30,26 +30,32 @@ const serverImportChecks: ImportCheckPredicate[] = [ (moduleName: string) => moduleName.startsWith('wasp/server'), ] -function ensureNoServerImports(source: string, relativeImporter: string): void { +function ensureNoServerImports(source: string, relativeImporter: RelativePath): void { for (const check of serverImportChecks) { if (check(source)) { throw new Error( - `Server code cannot be imported in the client code. Import from '${source}' in '${relativeImporter}' is not allowed.` + `Server code cannot be imported in the client code. Import from "${source}" in "${relativeImporter.relativePath}" is not allowed.` ) } } } -const waspProjectDirAbsPath = getWaspProjectDirAbsPathFromCwd() +type RelativePath = { + relativePath: string +} + +function getPathRelativeToWaspProjectDir(filePath: string): RelativePath { + return { relativePath: path.relative(waspProjectDirAbsPath, filePath) } +} -function getRelativeFilePath(filePath: string): string { - return path.relative(waspProjectDirAbsPath, filePath) +function isPathToUserCode(filePath: RelativePath): boolean { + return filePath.relativePath.startsWith('{= srcDirInWaspProjectDir =}') } -// We are not passing the waspProjectDir path from Haskell because -// our e2e tests stop working. Becuase we need to absolute path of the -// Wasp project directory, it contains things like the username of the -// user running the tests, which is different on different machines. +// We can't pass the "waspProjectDir" path from Haskell because we need the absolute path: +// e.g. /Users/{username}/dev/wasp/waspc/examples/todoApp +// which contains machine specific info like the username which is different in the CI and locally. +// This breaks our e2e tests in the CI because the path is different. function getWaspProjectDirAbsPathFromCwd(): string { const webAppDirAbsPath = process.cwd() const waspProjectDirAbsPath = path.join(webAppDirAbsPath, '{= waspProjectDirFromWebAppDir =}') diff --git a/waspc/src/Wasp/Generator/WebAppGenerator/VitePlugins.hs b/waspc/src/Wasp/Generator/WebAppGenerator/VitePlugins.hs index a02c9517b3..0076ba10f7 100644 --- a/waspc/src/Wasp/Generator/WebAppGenerator/VitePlugins.hs +++ b/waspc/src/Wasp/Generator/WebAppGenerator/VitePlugins.hs @@ -7,7 +7,7 @@ import Wasp.Generator.Common (WebAppRootDir) import Wasp.Generator.FileDraft (FileDraft) import Wasp.Generator.Monad (Generator) import qualified Wasp.Generator.WebAppGenerator.Common as C -import Wasp.Project.Common (WaspProjectDir, waspProjectDirFromAppComponentDir) +import Wasp.Project.Common (WaspProjectDir, srcDirInWaspProjectDir, waspProjectDirFromAppComponentDir) genVitePlugins :: Generator [FileDraft] genVitePlugins = sequence [genDetectServerImportsPlugin] @@ -17,7 +17,8 @@ genDetectServerImportsPlugin = return $ C.mkTmplFdWithData [relfile|vite/detectS where tmplData = object - [ "waspProjectDirFromWebAppDir" .= SP.fromRelDir waspProjectDirFromWebAppDir + [ "waspProjectDirFromWebAppDir" .= SP.fromRelDir waspProjectDirFromWebAppDir, + "srcDirInWaspProjectDir" .= SP.fromRelDir srcDirInWaspProjectDir ] waspProjectDirFromWebAppDir = waspProjectDirFromAppComponentDir :: Path' (Rel WebAppRootDir) (Dir WaspProjectDir) From 30e9ac36f5287619c91a6e6cab876038d26f7ff8 Mon Sep 17 00:00:00 2001 From: Mihovil Ilakovac Date: Fri, 7 Feb 2025 14:47:18 +0100 Subject: [PATCH 18/22] Update tsconfig.node.json generation --- .../templates/react-app/tsconfig.node.json | 7 ++-- waspc/examples/todoApp/vite.config.d.ts | 2 + waspc/examples/todoApp/vite.config.js | 6 +++ waspc/src/Wasp/Generator/WebAppGenerator.hs | 15 +++++-- .../Wasp/Generator/WebAppGenerator/Common.hs | 6 +++ .../Generator/WebAppGenerator/VitePlugins.hs | 40 ++++++++++++++++--- 6 files changed, 65 insertions(+), 11 deletions(-) create mode 100644 waspc/examples/todoApp/vite.config.d.ts create mode 100644 waspc/examples/todoApp/vite.config.js diff --git a/waspc/data/Generator/templates/react-app/tsconfig.node.json b/waspc/data/Generator/templates/react-app/tsconfig.node.json index 24773c36d0..80681a1625 100644 --- a/waspc/data/Generator/templates/react-app/tsconfig.node.json +++ b/waspc/data/Generator/templates/react-app/tsconfig.node.json @@ -1,3 +1,4 @@ +{{={= =}=}} { "compilerOptions": { "composite": true, @@ -8,8 +9,8 @@ "allowSyntheticDefaultImports": true, }, "include": [ - "vite.config.ts", - "./src/ext-src/vite.config.ts", - "./vite/detectServerImports.ts" + {=# includePaths =} + "{= . =}", + {=/ includePaths =} ] } diff --git a/waspc/examples/todoApp/vite.config.d.ts b/waspc/examples/todoApp/vite.config.d.ts new file mode 100644 index 0000000000..340562aff1 --- /dev/null +++ b/waspc/examples/todoApp/vite.config.d.ts @@ -0,0 +1,2 @@ +declare const _default: import("vite").UserConfig; +export default _default; diff --git a/waspc/examples/todoApp/vite.config.js b/waspc/examples/todoApp/vite.config.js new file mode 100644 index 0000000000..5503d97cac --- /dev/null +++ b/waspc/examples/todoApp/vite.config.js @@ -0,0 +1,6 @@ +import { defineConfig } from 'vite'; +export default defineConfig({ + server: { + open: false, + }, +}); diff --git a/waspc/src/Wasp/Generator/WebAppGenerator.hs b/waspc/src/Wasp/Generator/WebAppGenerator.hs index 8c32940fc5..d2ffcac04a 100644 --- a/waspc/src/Wasp/Generator/WebAppGenerator.hs +++ b/waspc/src/Wasp/Generator/WebAppGenerator.hs @@ -49,7 +49,7 @@ import Wasp.Generator.WebAppGenerator.Common import qualified Wasp.Generator.WebAppGenerator.Common as C import Wasp.Generator.WebAppGenerator.JsImport (extImportToImportJson) import Wasp.Generator.WebAppGenerator.RouterGenerator (genRouter) -import Wasp.Generator.WebAppGenerator.VitePlugins (genVitePlugins) +import Wasp.Generator.WebAppGenerator.VitePlugins (genVitePlugins, vitePlugins) import qualified Wasp.Generator.WebSocket as AS.WS import Wasp.JsImport ( JsImport, @@ -68,13 +68,13 @@ genWebApp spec = do [ genFileCopy [relfile|README.md|], genFileCopy [relfile|tsconfig.json|], genAppTsConfigJson spec, - genFileCopy [relfile|tsconfig.node.json|], genFileCopy [relfile|netlify.toml|], genPackageJson spec (npmDepsForWasp spec), genNpmrc, genGitignore, genIndexHtml spec, - genViteConfig spec + genViteConfig spec, + genNodeTsConfigJson ] <++> genSrcDir spec <++> genPublicDir spec @@ -282,3 +282,12 @@ genViteConfig spec = return $ C.mkTmplFdWithData tmplFile tmplData SP.fromRelDir (Project.dotWaspDirInWaspProjectDir Project.generatedCodeDirInDotWaspDir C.webAppRootDirInProjectRootDir) importName = JsImportModule "customViteConfig" + +genNodeTsConfigJson :: Generator FileDraft +genNodeTsConfigJson = return $ C.mkTmplFdWithData [relfile|tsconfig.node.json|] tmplData + where + tmplData = object ["includePaths" .= includePaths] + + includePaths = ["vite.config.ts", "./src/ext-src/vite.config.ts"] ++ vitePluginPaths + + vitePluginPaths = map (SP.fromRelFile . snd) vitePlugins diff --git a/waspc/src/Wasp/Generator/WebAppGenerator/Common.hs b/waspc/src/Wasp/Generator/WebAppGenerator/Common.hs index e6558dfd48..55cc79c522 100644 --- a/waspc/src/Wasp/Generator/WebAppGenerator/Common.hs +++ b/waspc/src/Wasp/Generator/WebAppGenerator/Common.hs @@ -28,6 +28,7 @@ module Wasp.Generator.WebAppGenerator.Common reactQueryVersion, axiosVersion, reactVersion, + vitePluginsDirInWebAppDir, ) where @@ -62,6 +63,8 @@ data WebAppTemplatesSrcDir data WebAppStaticAssetsDir +data WebAppVitePluginsDir + instance GeneratedSrcDir WebAppSrcDir serverRootDirFromWebAppRootDir :: Path' (Rel WebAppRootDir) (Dir ServerRootDir) @@ -87,6 +90,9 @@ webAppSrcDirInWebAppRootDir = [reldir|src|] staticAssetsDirInWebAppDir :: Path' (Rel WebAppRootDir) (Dir WebAppStaticAssetsDir) staticAssetsDirInWebAppDir = [reldir|public|] +vitePluginsDirInWebAppDir :: Path' (Rel WebAppRootDir) (Dir WebAppVitePluginsDir) +vitePluginsDirInWebAppDir = [reldir|vite|] + -- | Path to generated web app src/ directory, relative to the root directory of the whole generated project. webAppSrcDirInProjectRootDir :: Path' (Rel ProjectRootDir) (Dir WebAppSrcDir) webAppSrcDirInProjectRootDir = webAppRootDirInProjectRootDir webAppSrcDirInWebAppRootDir diff --git a/waspc/src/Wasp/Generator/WebAppGenerator/VitePlugins.hs b/waspc/src/Wasp/Generator/WebAppGenerator/VitePlugins.hs index 0076ba10f7..fbcc525aec 100644 --- a/waspc/src/Wasp/Generator/WebAppGenerator/VitePlugins.hs +++ b/waspc/src/Wasp/Generator/WebAppGenerator/VitePlugins.hs @@ -1,19 +1,49 @@ -module Wasp.Generator.WebAppGenerator.VitePlugins (genVitePlugins) where +{-# LANGUAGE TemplateHaskell #-} + +module Wasp.Generator.WebAppGenerator.VitePlugins + ( genVitePlugins, + vitePlugins, + ) +where import Data.Aeson (object, (.=)) -import StrongPath (Dir, Path', Rel, relfile) +import StrongPath (Dir, File', Path', Rel, relfile, ()) import qualified StrongPath as SP import Wasp.Generator.Common (WebAppRootDir) import Wasp.Generator.FileDraft (FileDraft) import Wasp.Generator.Monad (Generator) +import Wasp.Generator.WebAppGenerator.Common (WebAppTemplatesDir, vitePluginsDirInWebAppDir) import qualified Wasp.Generator.WebAppGenerator.Common as C import Wasp.Project.Common (WaspProjectDir, srcDirInWaspProjectDir, waspProjectDirFromAppComponentDir) +data VitePluginName = DetectServerImports + deriving (Enum, Bounded) + +type TmplFilePath = Path' (Rel WebAppTemplatesDir) File' + +-- We define it like this because we need a list of plugin +-- paths which we will use in the tsconfig.node.json "include" section +type VitePlugin = (VitePluginName, TmplFilePath) + +vitePlugins :: [VitePlugin] +vitePlugins = + map + (\name -> (name, getTmplFilePathForVitePlugin name)) + vitePluginNames + where + vitePluginNames = [minBound .. maxBound] + genVitePlugins :: Generator [FileDraft] -genVitePlugins = sequence [genDetectServerImportsPlugin] +genVitePlugins = mapM genVitePlugin vitePlugins + +getTmplFilePathForVitePlugin :: VitePluginName -> TmplFilePath +getTmplFilePathForVitePlugin DetectServerImports = C.asTmplFile $ vitePluginsDirInWebAppDir [relfile|detectServerImports.ts|] + +genVitePlugin :: VitePlugin -> Generator FileDraft +genVitePlugin (DetectServerImports, tmplFile) = genDetectServerImportsPlugin tmplFile -genDetectServerImportsPlugin :: Generator FileDraft -genDetectServerImportsPlugin = return $ C.mkTmplFdWithData [relfile|vite/detectServerImports.ts|] tmplData +genDetectServerImportsPlugin :: Path' (Rel WebAppTemplatesDir) File' -> Generator FileDraft +genDetectServerImportsPlugin tmplFile = return $ C.mkTmplFdWithData tmplFile tmplData where tmplData = object From 4ad29721b26e9eaf3d33481bc87532250c893903 Mon Sep 17 00:00:00 2001 From: Mihovil Ilakovac Date: Fri, 7 Feb 2025 14:50:59 +0100 Subject: [PATCH 19/22] e2e tests Signed-off-by: Mihovil Ilakovac --- .../waspBuild/.wasp/build/.waspchecksums | 4 +-- .../.wasp/build/web-app/tsconfig.node.json | 2 +- .../build/web-app/vite/detectServerImports.ts | 32 +++++++++++-------- .../waspCompile/.wasp/out/.waspchecksums | 4 +-- .../.wasp/out/web-app/tsconfig.node.json | 2 +- .../out/web-app/vite/detectServerImports.ts | 32 +++++++++++-------- .../waspComplexTest/.wasp/out/.waspchecksums | 4 +-- .../.wasp/out/web-app/tsconfig.node.json | 2 +- .../out/web-app/vite/detectServerImports.ts | 32 +++++++++++-------- .../waspJob/.wasp/out/.waspchecksums | 4 +-- .../.wasp/out/web-app/tsconfig.node.json | 2 +- .../out/web-app/vite/detectServerImports.ts | 32 +++++++++++-------- .../waspMigrate/.wasp/out/.waspchecksums | 4 +-- .../.wasp/out/web-app/tsconfig.node.json | 2 +- .../out/web-app/vite/detectServerImports.ts | 32 +++++++++++-------- 15 files changed, 110 insertions(+), 80 deletions(-) diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/.waspchecksums b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/.waspchecksums index b6d425064d..b2d3aa02b1 100644 --- a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/.waspchecksums +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/.waspchecksums @@ -683,7 +683,7 @@ "file", "web-app/tsconfig.node.json" ], - "d55b571b07441ce425f1b7596d2ac25557643bc7c47eff0957238b3b0fe74033" + "bb2bf9acd6049cb78205f30d81de3ca3f105c8b881d1ab36cb68da033c59a7e8" ], [ [ @@ -697,6 +697,6 @@ "file", "web-app/vite/detectServerImports.ts" ], - "8dc4cacd4cee4d2e01338ec5b015f472afcefcdeb6cfee2dfd5e963d4ab93a38" + "d3724c4ec970e8f5c39d5da60220bf52317134f55530c508ee469bef0d2cd811" ] ] \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/web-app/tsconfig.node.json b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/web-app/tsconfig.node.json index 24773c36d0..2f880630e1 100644 --- a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/web-app/tsconfig.node.json +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/web-app/tsconfig.node.json @@ -10,6 +10,6 @@ "include": [ "vite.config.ts", "./src/ext-src/vite.config.ts", - "./vite/detectServerImports.ts" + "vite/detectServerImports.ts", ] } diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/web-app/vite/detectServerImports.ts b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/web-app/vite/detectServerImports.ts index f332407c66..287cdea51c 100644 --- a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/web-app/vite/detectServerImports.ts +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/web-app/vite/detectServerImports.ts @@ -1,6 +1,8 @@ import { type Plugin } from 'vite' import path from 'path' +const waspProjectDirAbsPath = getWaspProjectDirAbsPathFromCwd() + export function detectServerImports(): Plugin { return { name: 'wasp-detect-server-imports', @@ -10,11 +12,9 @@ export function detectServerImports(): Plugin { return } - const importerRelativePath = getRelativeFilePath(importer) + const importerRelativePath = getPathRelativeToWaspProjectDir(importer) - // Check only for imports from src/ directory which - // contains the user's code. - if (!importerRelativePath.startsWith('src/')) { + if (!isPathToUserCode(importerRelativePath)) { return } @@ -29,26 +29,32 @@ const serverImportChecks: ImportCheckPredicate[] = [ (moduleName: string) => moduleName.startsWith('wasp/server'), ] -function ensureNoServerImports(source: string, relativeImporter: string): void { +function ensureNoServerImports(source: string, relativeImporter: RelativePath): void { for (const check of serverImportChecks) { if (check(source)) { throw new Error( - `Server code cannot be imported in the client code. Import from '${source}' in '${relativeImporter}' is not allowed.` + `Server code cannot be imported in the client code. Import from "${source}" in "${relativeImporter.relativePath}" is not allowed.` ) } } } -const waspProjectDirAbsPath = getWaspProjectDirAbsPathFromCwd() +type RelativePath = { + relativePath: string +} + +function getPathRelativeToWaspProjectDir(filePath: string): RelativePath { + return { relativePath: path.relative(waspProjectDirAbsPath, filePath) } +} -function getRelativeFilePath(filePath: string): string { - return path.relative(waspProjectDirAbsPath, filePath) +function isPathToUserCode(filePath: RelativePath): boolean { + return filePath.relativePath.startsWith('src/') } -// We are not passing the waspProjectDir path from Haskell because -// our e2e tests stop working. Becuase we need to absolute path of the -// Wasp project directory, it contains things like the username of the -// user running the tests, which is different on different machines. +// We can't pass the "waspProjectDir" path from Haskell because we need the absolute path: +// e.g. /Users/{username}/dev/wasp/waspc/examples/todoApp +// which contains machine specific info like the username which is different in the CI and locally. +// This breaks our e2e tests in the CI because the path is different. function getWaspProjectDirAbsPathFromCwd(): string { const webAppDirAbsPath = process.cwd() const waspProjectDirAbsPath = path.join(webAppDirAbsPath, '../../../') diff --git a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/.waspchecksums b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/.waspchecksums index e64a9feb10..62b8bf3132 100644 --- a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/.waspchecksums +++ b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/.waspchecksums @@ -697,7 +697,7 @@ "file", "web-app/tsconfig.node.json" ], - "d55b571b07441ce425f1b7596d2ac25557643bc7c47eff0957238b3b0fe74033" + "bb2bf9acd6049cb78205f30d81de3ca3f105c8b881d1ab36cb68da033c59a7e8" ], [ [ @@ -711,6 +711,6 @@ "file", "web-app/vite/detectServerImports.ts" ], - "8dc4cacd4cee4d2e01338ec5b015f472afcefcdeb6cfee2dfd5e963d4ab93a38" + "d3724c4ec970e8f5c39d5da60220bf52317134f55530c508ee469bef0d2cd811" ] ] \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/web-app/tsconfig.node.json b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/web-app/tsconfig.node.json index 24773c36d0..2f880630e1 100644 --- a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/web-app/tsconfig.node.json +++ b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/web-app/tsconfig.node.json @@ -10,6 +10,6 @@ "include": [ "vite.config.ts", "./src/ext-src/vite.config.ts", - "./vite/detectServerImports.ts" + "vite/detectServerImports.ts", ] } diff --git a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/web-app/vite/detectServerImports.ts b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/web-app/vite/detectServerImports.ts index f332407c66..287cdea51c 100644 --- a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/web-app/vite/detectServerImports.ts +++ b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/web-app/vite/detectServerImports.ts @@ -1,6 +1,8 @@ import { type Plugin } from 'vite' import path from 'path' +const waspProjectDirAbsPath = getWaspProjectDirAbsPathFromCwd() + export function detectServerImports(): Plugin { return { name: 'wasp-detect-server-imports', @@ -10,11 +12,9 @@ export function detectServerImports(): Plugin { return } - const importerRelativePath = getRelativeFilePath(importer) + const importerRelativePath = getPathRelativeToWaspProjectDir(importer) - // Check only for imports from src/ directory which - // contains the user's code. - if (!importerRelativePath.startsWith('src/')) { + if (!isPathToUserCode(importerRelativePath)) { return } @@ -29,26 +29,32 @@ const serverImportChecks: ImportCheckPredicate[] = [ (moduleName: string) => moduleName.startsWith('wasp/server'), ] -function ensureNoServerImports(source: string, relativeImporter: string): void { +function ensureNoServerImports(source: string, relativeImporter: RelativePath): void { for (const check of serverImportChecks) { if (check(source)) { throw new Error( - `Server code cannot be imported in the client code. Import from '${source}' in '${relativeImporter}' is not allowed.` + `Server code cannot be imported in the client code. Import from "${source}" in "${relativeImporter.relativePath}" is not allowed.` ) } } } -const waspProjectDirAbsPath = getWaspProjectDirAbsPathFromCwd() +type RelativePath = { + relativePath: string +} + +function getPathRelativeToWaspProjectDir(filePath: string): RelativePath { + return { relativePath: path.relative(waspProjectDirAbsPath, filePath) } +} -function getRelativeFilePath(filePath: string): string { - return path.relative(waspProjectDirAbsPath, filePath) +function isPathToUserCode(filePath: RelativePath): boolean { + return filePath.relativePath.startsWith('src/') } -// We are not passing the waspProjectDir path from Haskell because -// our e2e tests stop working. Becuase we need to absolute path of the -// Wasp project directory, it contains things like the username of the -// user running the tests, which is different on different machines. +// We can't pass the "waspProjectDir" path from Haskell because we need the absolute path: +// e.g. /Users/{username}/dev/wasp/waspc/examples/todoApp +// which contains machine specific info like the username which is different in the CI and locally. +// This breaks our e2e tests in the CI because the path is different. function getWaspProjectDirAbsPathFromCwd(): string { const webAppDirAbsPath = process.cwd() const waspProjectDirAbsPath = path.join(webAppDirAbsPath, '../../../') diff --git a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/.waspchecksums b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/.waspchecksums index 0ca8777dbd..88acb94042 100644 --- a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/.waspchecksums +++ b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/.waspchecksums @@ -1334,7 +1334,7 @@ "file", "web-app/tsconfig.node.json" ], - "d55b571b07441ce425f1b7596d2ac25557643bc7c47eff0957238b3b0fe74033" + "bb2bf9acd6049cb78205f30d81de3ca3f105c8b881d1ab36cb68da033c59a7e8" ], [ [ @@ -1348,6 +1348,6 @@ "file", "web-app/vite/detectServerImports.ts" ], - "8dc4cacd4cee4d2e01338ec5b015f472afcefcdeb6cfee2dfd5e963d4ab93a38" + "d3724c4ec970e8f5c39d5da60220bf52317134f55530c508ee469bef0d2cd811" ] ] \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/web-app/tsconfig.node.json b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/web-app/tsconfig.node.json index 24773c36d0..2f880630e1 100644 --- a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/web-app/tsconfig.node.json +++ b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/web-app/tsconfig.node.json @@ -10,6 +10,6 @@ "include": [ "vite.config.ts", "./src/ext-src/vite.config.ts", - "./vite/detectServerImports.ts" + "vite/detectServerImports.ts", ] } diff --git a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/web-app/vite/detectServerImports.ts b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/web-app/vite/detectServerImports.ts index f332407c66..287cdea51c 100644 --- a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/web-app/vite/detectServerImports.ts +++ b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/web-app/vite/detectServerImports.ts @@ -1,6 +1,8 @@ import { type Plugin } from 'vite' import path from 'path' +const waspProjectDirAbsPath = getWaspProjectDirAbsPathFromCwd() + export function detectServerImports(): Plugin { return { name: 'wasp-detect-server-imports', @@ -10,11 +12,9 @@ export function detectServerImports(): Plugin { return } - const importerRelativePath = getRelativeFilePath(importer) + const importerRelativePath = getPathRelativeToWaspProjectDir(importer) - // Check only for imports from src/ directory which - // contains the user's code. - if (!importerRelativePath.startsWith('src/')) { + if (!isPathToUserCode(importerRelativePath)) { return } @@ -29,26 +29,32 @@ const serverImportChecks: ImportCheckPredicate[] = [ (moduleName: string) => moduleName.startsWith('wasp/server'), ] -function ensureNoServerImports(source: string, relativeImporter: string): void { +function ensureNoServerImports(source: string, relativeImporter: RelativePath): void { for (const check of serverImportChecks) { if (check(source)) { throw new Error( - `Server code cannot be imported in the client code. Import from '${source}' in '${relativeImporter}' is not allowed.` + `Server code cannot be imported in the client code. Import from "${source}" in "${relativeImporter.relativePath}" is not allowed.` ) } } } -const waspProjectDirAbsPath = getWaspProjectDirAbsPathFromCwd() +type RelativePath = { + relativePath: string +} + +function getPathRelativeToWaspProjectDir(filePath: string): RelativePath { + return { relativePath: path.relative(waspProjectDirAbsPath, filePath) } +} -function getRelativeFilePath(filePath: string): string { - return path.relative(waspProjectDirAbsPath, filePath) +function isPathToUserCode(filePath: RelativePath): boolean { + return filePath.relativePath.startsWith('src/') } -// We are not passing the waspProjectDir path from Haskell because -// our e2e tests stop working. Becuase we need to absolute path of the -// Wasp project directory, it contains things like the username of the -// user running the tests, which is different on different machines. +// We can't pass the "waspProjectDir" path from Haskell because we need the absolute path: +// e.g. /Users/{username}/dev/wasp/waspc/examples/todoApp +// which contains machine specific info like the username which is different in the CI and locally. +// This breaks our e2e tests in the CI because the path is different. function getWaspProjectDirAbsPathFromCwd(): string { const webAppDirAbsPath = process.cwd() const waspProjectDirAbsPath = path.join(webAppDirAbsPath, '../../../') diff --git a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/.waspchecksums b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/.waspchecksums index 5b7abfae00..e3028b1cd1 100644 --- a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/.waspchecksums +++ b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/.waspchecksums @@ -795,7 +795,7 @@ "file", "web-app/tsconfig.node.json" ], - "d55b571b07441ce425f1b7596d2ac25557643bc7c47eff0957238b3b0fe74033" + "bb2bf9acd6049cb78205f30d81de3ca3f105c8b881d1ab36cb68da033c59a7e8" ], [ [ @@ -809,6 +809,6 @@ "file", "web-app/vite/detectServerImports.ts" ], - "8dc4cacd4cee4d2e01338ec5b015f472afcefcdeb6cfee2dfd5e963d4ab93a38" + "d3724c4ec970e8f5c39d5da60220bf52317134f55530c508ee469bef0d2cd811" ] ] \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/web-app/tsconfig.node.json b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/web-app/tsconfig.node.json index 24773c36d0..2f880630e1 100644 --- a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/web-app/tsconfig.node.json +++ b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/web-app/tsconfig.node.json @@ -10,6 +10,6 @@ "include": [ "vite.config.ts", "./src/ext-src/vite.config.ts", - "./vite/detectServerImports.ts" + "vite/detectServerImports.ts", ] } diff --git a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/web-app/vite/detectServerImports.ts b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/web-app/vite/detectServerImports.ts index f332407c66..287cdea51c 100644 --- a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/web-app/vite/detectServerImports.ts +++ b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/web-app/vite/detectServerImports.ts @@ -1,6 +1,8 @@ import { type Plugin } from 'vite' import path from 'path' +const waspProjectDirAbsPath = getWaspProjectDirAbsPathFromCwd() + export function detectServerImports(): Plugin { return { name: 'wasp-detect-server-imports', @@ -10,11 +12,9 @@ export function detectServerImports(): Plugin { return } - const importerRelativePath = getRelativeFilePath(importer) + const importerRelativePath = getPathRelativeToWaspProjectDir(importer) - // Check only for imports from src/ directory which - // contains the user's code. - if (!importerRelativePath.startsWith('src/')) { + if (!isPathToUserCode(importerRelativePath)) { return } @@ -29,26 +29,32 @@ const serverImportChecks: ImportCheckPredicate[] = [ (moduleName: string) => moduleName.startsWith('wasp/server'), ] -function ensureNoServerImports(source: string, relativeImporter: string): void { +function ensureNoServerImports(source: string, relativeImporter: RelativePath): void { for (const check of serverImportChecks) { if (check(source)) { throw new Error( - `Server code cannot be imported in the client code. Import from '${source}' in '${relativeImporter}' is not allowed.` + `Server code cannot be imported in the client code. Import from "${source}" in "${relativeImporter.relativePath}" is not allowed.` ) } } } -const waspProjectDirAbsPath = getWaspProjectDirAbsPathFromCwd() +type RelativePath = { + relativePath: string +} + +function getPathRelativeToWaspProjectDir(filePath: string): RelativePath { + return { relativePath: path.relative(waspProjectDirAbsPath, filePath) } +} -function getRelativeFilePath(filePath: string): string { - return path.relative(waspProjectDirAbsPath, filePath) +function isPathToUserCode(filePath: RelativePath): boolean { + return filePath.relativePath.startsWith('src/') } -// We are not passing the waspProjectDir path from Haskell because -// our e2e tests stop working. Becuase we need to absolute path of the -// Wasp project directory, it contains things like the username of the -// user running the tests, which is different on different machines. +// We can't pass the "waspProjectDir" path from Haskell because we need the absolute path: +// e.g. /Users/{username}/dev/wasp/waspc/examples/todoApp +// which contains machine specific info like the username which is different in the CI and locally. +// This breaks our e2e tests in the CI because the path is different. function getWaspProjectDirAbsPathFromCwd(): string { const webAppDirAbsPath = process.cwd() const waspProjectDirAbsPath = path.join(webAppDirAbsPath, '../../../') diff --git a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/.waspchecksums b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/.waspchecksums index a06e58a185..63ee0abb64 100644 --- a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/.waspchecksums +++ b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/.waspchecksums @@ -697,7 +697,7 @@ "file", "web-app/tsconfig.node.json" ], - "d55b571b07441ce425f1b7596d2ac25557643bc7c47eff0957238b3b0fe74033" + "bb2bf9acd6049cb78205f30d81de3ca3f105c8b881d1ab36cb68da033c59a7e8" ], [ [ @@ -711,6 +711,6 @@ "file", "web-app/vite/detectServerImports.ts" ], - "8dc4cacd4cee4d2e01338ec5b015f472afcefcdeb6cfee2dfd5e963d4ab93a38" + "d3724c4ec970e8f5c39d5da60220bf52317134f55530c508ee469bef0d2cd811" ] ] \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/web-app/tsconfig.node.json b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/web-app/tsconfig.node.json index 24773c36d0..2f880630e1 100644 --- a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/web-app/tsconfig.node.json +++ b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/web-app/tsconfig.node.json @@ -10,6 +10,6 @@ "include": [ "vite.config.ts", "./src/ext-src/vite.config.ts", - "./vite/detectServerImports.ts" + "vite/detectServerImports.ts", ] } diff --git a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/web-app/vite/detectServerImports.ts b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/web-app/vite/detectServerImports.ts index f332407c66..287cdea51c 100644 --- a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/web-app/vite/detectServerImports.ts +++ b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/web-app/vite/detectServerImports.ts @@ -1,6 +1,8 @@ import { type Plugin } from 'vite' import path from 'path' +const waspProjectDirAbsPath = getWaspProjectDirAbsPathFromCwd() + export function detectServerImports(): Plugin { return { name: 'wasp-detect-server-imports', @@ -10,11 +12,9 @@ export function detectServerImports(): Plugin { return } - const importerRelativePath = getRelativeFilePath(importer) + const importerRelativePath = getPathRelativeToWaspProjectDir(importer) - // Check only for imports from src/ directory which - // contains the user's code. - if (!importerRelativePath.startsWith('src/')) { + if (!isPathToUserCode(importerRelativePath)) { return } @@ -29,26 +29,32 @@ const serverImportChecks: ImportCheckPredicate[] = [ (moduleName: string) => moduleName.startsWith('wasp/server'), ] -function ensureNoServerImports(source: string, relativeImporter: string): void { +function ensureNoServerImports(source: string, relativeImporter: RelativePath): void { for (const check of serverImportChecks) { if (check(source)) { throw new Error( - `Server code cannot be imported in the client code. Import from '${source}' in '${relativeImporter}' is not allowed.` + `Server code cannot be imported in the client code. Import from "${source}" in "${relativeImporter.relativePath}" is not allowed.` ) } } } -const waspProjectDirAbsPath = getWaspProjectDirAbsPathFromCwd() +type RelativePath = { + relativePath: string +} + +function getPathRelativeToWaspProjectDir(filePath: string): RelativePath { + return { relativePath: path.relative(waspProjectDirAbsPath, filePath) } +} -function getRelativeFilePath(filePath: string): string { - return path.relative(waspProjectDirAbsPath, filePath) +function isPathToUserCode(filePath: RelativePath): boolean { + return filePath.relativePath.startsWith('src/') } -// We are not passing the waspProjectDir path from Haskell because -// our e2e tests stop working. Becuase we need to absolute path of the -// Wasp project directory, it contains things like the username of the -// user running the tests, which is different on different machines. +// We can't pass the "waspProjectDir" path from Haskell because we need the absolute path: +// e.g. /Users/{username}/dev/wasp/waspc/examples/todoApp +// which contains machine specific info like the username which is different in the CI and locally. +// This breaks our e2e tests in the CI because the path is different. function getWaspProjectDirAbsPathFromCwd(): string { const webAppDirAbsPath = process.cwd() const waspProjectDirAbsPath = path.join(webAppDirAbsPath, '../../../') From 29344920aea5e3e0f1b298c6a6b35491e627043d Mon Sep 17 00:00:00 2001 From: Mihovil Ilakovac Date: Fri, 7 Feb 2025 16:58:30 +0100 Subject: [PATCH 20/22] PR comments --- .../react-app/vite/detectServerImports.ts | 27 ++-- waspc/examples/todoApp/src/App.tsx | 4 - waspc/examples/todoApp/vite.config.d.ts | 2 - waspc/examples/todoApp/vite.config.js | 6 - waspc/src/Wasp/Generator/WebAppGenerator.hs | 65 ++-------- .../Wasp/Generator/WebAppGenerator/Common.hs | 6 - .../Wasp/Generator/WebAppGenerator/Vite.hs | 116 ++++++++++++++++++ .../Generator/WebAppGenerator/VitePlugins.hs | 54 -------- waspc/waspc.cabal | 2 +- 9 files changed, 134 insertions(+), 148 deletions(-) delete mode 100644 waspc/examples/todoApp/vite.config.d.ts delete mode 100644 waspc/examples/todoApp/vite.config.js create mode 100644 waspc/src/Wasp/Generator/WebAppGenerator/Vite.hs delete mode 100644 waspc/src/Wasp/Generator/WebAppGenerator/VitePlugins.hs diff --git a/waspc/data/Generator/templates/react-app/vite/detectServerImports.ts b/waspc/data/Generator/templates/react-app/vite/detectServerImports.ts index d0763cc74b..5c2d235157 100644 --- a/waspc/data/Generator/templates/react-app/vite/detectServerImports.ts +++ b/waspc/data/Generator/templates/react-app/vite/detectServerImports.ts @@ -13,13 +13,12 @@ export function detectServerImports(): Plugin { return } - const importerRelativePath = getPathRelativeToWaspProjectDir(importer) - - if (!isPathToUserCode(importerRelativePath)) { + const pathToUserCode = parsePathToUserCode(importer) + if (!pathToUserCode) { return } - ensureNoServerImports(source, importerRelativePath) + ensureNoServerImports(source, pathToUserCode) }, } } @@ -30,26 +29,21 @@ const serverImportChecks: ImportCheckPredicate[] = [ (moduleName: string) => moduleName.startsWith('wasp/server'), ] -function ensureNoServerImports(source: string, relativeImporter: RelativePath): void { +function ensureNoServerImports(source: string, relativeImporter: RelativePathToUserCode): void { for (const check of serverImportChecks) { if (check(source)) { throw new Error( - `Server code cannot be imported in the client code. Import from "${source}" in "${relativeImporter.relativePath}" is not allowed.` + `Server code cannot be imported in the client code. Import from "${source}" in "${relativeImporter}" is not allowed.` ) } } } -type RelativePath = { - relativePath: string -} - -function getPathRelativeToWaspProjectDir(filePath: string): RelativePath { - return { relativePath: path.relative(waspProjectDirAbsPath, filePath) } -} +type RelativePathToUserCode = string & { _brand: 'relativePathToUserCode' } -function isPathToUserCode(filePath: RelativePath): boolean { - return filePath.relativePath.startsWith('{= srcDirInWaspProjectDir =}') +function parsePathToUserCode(importerPath: string): RelativePathToUserCode | null { + const importerPathRelativeToWaspProjectDir = path.relative(waspProjectDirAbsPath, importerPath) + return importerPathRelativeToWaspProjectDir.startsWith('{= srcDirInWaspProjectDir =}') ? importerPathRelativeToWaspProjectDir : null } // We can't pass the "waspProjectDir" path from Haskell because we need the absolute path: @@ -58,6 +52,5 @@ function isPathToUserCode(filePath: RelativePath): boolean { // This breaks our e2e tests in the CI because the path is different. function getWaspProjectDirAbsPathFromCwd(): string { const webAppDirAbsPath = process.cwd() - const waspProjectDirAbsPath = path.join(webAppDirAbsPath, '{= waspProjectDirFromWebAppDir =}') - return waspProjectDirAbsPath + return path.join(webAppDirAbsPath, '{= waspProjectDirFromWebAppDir =}') } diff --git a/waspc/examples/todoApp/src/App.tsx b/waspc/examples/todoApp/src/App.tsx index 4ea53ed4c9..25b9867eb5 100644 --- a/waspc/examples/todoApp/src/App.tsx +++ b/waspc/examples/todoApp/src/App.tsx @@ -10,10 +10,6 @@ import { getName } from './user' // Necessary to trigger type tests. import './testTypes/operations/client' -import { HttpError } from 'wasp/server' - -HttpError - export function App() { const { data: user } = useAuth() const { data: date } = useQuery(getDate) diff --git a/waspc/examples/todoApp/vite.config.d.ts b/waspc/examples/todoApp/vite.config.d.ts deleted file mode 100644 index 340562aff1..0000000000 --- a/waspc/examples/todoApp/vite.config.d.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const _default: import("vite").UserConfig; -export default _default; diff --git a/waspc/examples/todoApp/vite.config.js b/waspc/examples/todoApp/vite.config.js deleted file mode 100644 index 5503d97cac..0000000000 --- a/waspc/examples/todoApp/vite.config.js +++ /dev/null @@ -1,6 +0,0 @@ -import { defineConfig } from 'vite'; -export default defineConfig({ - server: { - open: false, - }, -}); diff --git a/waspc/src/Wasp/Generator/WebAppGenerator.hs b/waspc/src/Wasp/Generator/WebAppGenerator.hs index d2ffcac04a..b0917dee6b 100644 --- a/waspc/src/Wasp/Generator/WebAppGenerator.hs +++ b/waspc/src/Wasp/Generator/WebAppGenerator.hs @@ -8,8 +8,6 @@ where import Data.Aeson (object, (.=)) import Data.List (intercalate) -import Data.Maybe (fromJust) -import qualified FilePath.Extra as FP.Extra import StrongPath ( Dir, File, @@ -23,7 +21,6 @@ import StrongPath (), ) import qualified StrongPath as SP -import qualified System.FilePath.Posix as FP.Posix import Wasp.AppSpec (AppSpec) import qualified Wasp.AppSpec as AS import qualified Wasp.AppSpec.App as AS.App @@ -31,10 +28,9 @@ import qualified Wasp.AppSpec.App.Client as AS.App.Client import qualified Wasp.AppSpec.App.Dependency as AS.Dependency import Wasp.AppSpec.Valid (getApp) import Wasp.Env (envVarsToDotEnvContent) -import Wasp.Generator.Common (makeJsArrayFromHaskellList, typescriptVersion) +import Wasp.Generator.Common (typescriptVersion) import Wasp.Generator.FileDraft (FileDraft, createTextFileDraft) import qualified Wasp.Generator.FileDraft as FD -import Wasp.Generator.JsImport (jsImportToImportJson) import Wasp.Generator.Monad (Generator) import qualified Wasp.Generator.NpmDependencies as N import Wasp.Generator.WebAppGenerator.AuthG (genAuth) @@ -43,23 +39,17 @@ import Wasp.Generator.WebAppGenerator.Common reactQueryVersion, reactRouterVersion, reactVersion, - webAppRootDirInProjectRootDir, - webAppSrcDirInWebAppRootDir, ) import qualified Wasp.Generator.WebAppGenerator.Common as C import Wasp.Generator.WebAppGenerator.JsImport (extImportToImportJson) import Wasp.Generator.WebAppGenerator.RouterGenerator (genRouter) -import Wasp.Generator.WebAppGenerator.VitePlugins (genVitePlugins, vitePlugins) +import Wasp.Generator.WebAppGenerator.Vite (genVite, vitePlugins) import qualified Wasp.Generator.WebSocket as AS.WS -import Wasp.JsImport - ( JsImport, - JsImportName (JsImportModule), - JsImportPath (RelativeImportPath), - makeJsImport, - ) import qualified Wasp.Node.Version as NodeVersion -import Wasp.Project.Common (SrcTsConfigFile, dotWaspDirInWaspProjectDir, generatedCodeDirInDotWaspDir, waspProjectDirFromAppComponentDir) -import qualified Wasp.Project.Common as Project +import Wasp.Project.Common + ( SrcTsConfigFile, + waspProjectDirFromAppComponentDir, + ) import Wasp.Util ((<++>)) genWebApp :: AppSpec -> Generator [FileDraft] @@ -73,13 +63,12 @@ genWebApp spec = do genNpmrc, genGitignore, genIndexHtml spec, - genViteConfig spec, genNodeTsConfigJson ] <++> genSrcDir spec <++> genPublicDir spec <++> genDotEnv spec - <++> genVitePlugins + <++> genVite spec where genFileCopy = return . C.mkTmplFd @@ -243,46 +232,6 @@ getIndexTs spec = relPathToWebAppSrcDir :: Path Posix (Rel importLocation) (Dir C.WebAppSrcDir) relPathToWebAppSrcDir = [reldirP|./|] --- todo(filip): Take care of this as well -genViteConfig :: AppSpec -> Generator FileDraft -genViteConfig spec = return $ C.mkTmplFdWithData tmplFile tmplData - where - tmplFile = C.asTmplFile [relfile|vite.config.ts|] - tmplData = - object - [ "customViteConfig" .= jsImportToImportJson (makeCustomViteConfigJsImport <$> AS.customViteConfigPath spec), - "baseDir" .= SP.fromAbsDirP (C.getBaseDir spec), - "defaultClientPort" .= C.defaultClientPort, - "vitest" - .= object - [ "setupFilesArray" .= makeJsArrayFromHaskellList vitestSetupFiles, - "excludeWaspArtefactsPattern" .= (SP.fromRelDirP (fromJust $ SP.relDirToPosix dotWaspDirInWaspProjectDir) FP.Posix. "**" FP.Posix. "*") - ] - ] - vitestSetupFiles = - [ SP.fromRelFile $ - dotWaspDirInWaspProjectDir - generatedCodeDirInDotWaspDir - webAppRootDirInProjectRootDir - webAppSrcDirInWebAppRootDir - [relfile|test/vitest/setup.ts|] - ] - - makeCustomViteConfigJsImport :: Path' (Rel Project.WaspProjectDir) File' -> JsImport - makeCustomViteConfigJsImport pathToConfig = makeJsImport (RelativeImportPath importPath) importName - where - importPath = SP.castRel $ C.toViteImportPath relPathToConfigInProjectDir - relPathToConfigInProjectDir = relPathFromWebAppRootDirWaspProjectDir (fromJust . SP.relFileToPosix $ pathToConfig) - - relPathFromWebAppRootDirWaspProjectDir :: Path Posix (Rel C.WebAppRootDir) (Dir Project.WaspProjectDir) - relPathFromWebAppRootDirWaspProjectDir = - fromJust $ - SP.parseRelDirP $ - FP.Extra.reversePosixPath $ - SP.fromRelDir (Project.dotWaspDirInWaspProjectDir Project.generatedCodeDirInDotWaspDir C.webAppRootDirInProjectRootDir) - - importName = JsImportModule "customViteConfig" - genNodeTsConfigJson :: Generator FileDraft genNodeTsConfigJson = return $ C.mkTmplFdWithData [relfile|tsconfig.node.json|] tmplData where diff --git a/waspc/src/Wasp/Generator/WebAppGenerator/Common.hs b/waspc/src/Wasp/Generator/WebAppGenerator/Common.hs index 55cc79c522..e6558dfd48 100644 --- a/waspc/src/Wasp/Generator/WebAppGenerator/Common.hs +++ b/waspc/src/Wasp/Generator/WebAppGenerator/Common.hs @@ -28,7 +28,6 @@ module Wasp.Generator.WebAppGenerator.Common reactQueryVersion, axiosVersion, reactVersion, - vitePluginsDirInWebAppDir, ) where @@ -63,8 +62,6 @@ data WebAppTemplatesSrcDir data WebAppStaticAssetsDir -data WebAppVitePluginsDir - instance GeneratedSrcDir WebAppSrcDir serverRootDirFromWebAppRootDir :: Path' (Rel WebAppRootDir) (Dir ServerRootDir) @@ -90,9 +87,6 @@ webAppSrcDirInWebAppRootDir = [reldir|src|] staticAssetsDirInWebAppDir :: Path' (Rel WebAppRootDir) (Dir WebAppStaticAssetsDir) staticAssetsDirInWebAppDir = [reldir|public|] -vitePluginsDirInWebAppDir :: Path' (Rel WebAppRootDir) (Dir WebAppVitePluginsDir) -vitePluginsDirInWebAppDir = [reldir|vite|] - -- | Path to generated web app src/ directory, relative to the root directory of the whole generated project. webAppSrcDirInProjectRootDir :: Path' (Rel ProjectRootDir) (Dir WebAppSrcDir) webAppSrcDirInProjectRootDir = webAppRootDirInProjectRootDir webAppSrcDirInWebAppRootDir diff --git a/waspc/src/Wasp/Generator/WebAppGenerator/Vite.hs b/waspc/src/Wasp/Generator/WebAppGenerator/Vite.hs new file mode 100644 index 0000000000..8de3d035e5 --- /dev/null +++ b/waspc/src/Wasp/Generator/WebAppGenerator/Vite.hs @@ -0,0 +1,116 @@ +module Wasp.Generator.WebAppGenerator.Vite + ( genVite, + vitePlugins, + ) +where + +import Data.Aeson (object, (.=)) +import Data.Maybe (fromJust) +import qualified FilePath.Extra as FP.Extra +import StrongPath (Dir, File', Path, Path', Posix, Rel, reldir, relfile, ()) +import qualified StrongPath as SP +import qualified System.FilePath.Posix as FP.Posix +import Wasp.AppSpec (AppSpec) +import qualified Wasp.AppSpec as AS +import Wasp.Generator.Common (WebAppRootDir, makeJsArrayFromHaskellList) +import Wasp.Generator.FileDraft (FileDraft) +import Wasp.Generator.JsImport (jsImportToImportJson) +import Wasp.Generator.Monad (Generator) +import Wasp.Generator.WebAppGenerator.Common (WebAppTemplatesDir, webAppRootDirInProjectRootDir, webAppSrcDirInWebAppRootDir) +import qualified Wasp.Generator.WebAppGenerator.Common as C +import Wasp.JsImport (JsImport, JsImportName (JsImportModule), JsImportPath (RelativeImportPath), makeJsImport) +import Wasp.Project.Common + ( WaspProjectDir, + dotWaspDirInWaspProjectDir, + generatedCodeDirInDotWaspDir, + srcDirInWaspProjectDir, + waspProjectDirFromAppComponentDir, + ) +import Wasp.Util ((<++>)) + +data VitePluginName = DetectServerImports + deriving (Enum, Bounded) + +type TmplFilePath = Path' (Rel WebAppTemplatesDir) File' + +-- We define it like this because we need a list of plugin +-- paths which we will use in the tsconfig.node.json "include" section +type VitePlugin = (VitePluginName, TmplFilePath) + +data WebAppVitePluginsDir + +genVite :: AppSpec -> Generator [FileDraft] +genVite spec = + sequence [genViteConfig spec] + <++> genVitePlugins + +vitePlugins :: [VitePlugin] +vitePlugins = + map + (\name -> (name, getTmplFilePathForVitePlugin name)) + vitePluginNames + where + vitePluginNames = [minBound .. maxBound] + +genVitePlugins :: Generator [FileDraft] +genVitePlugins = mapM genVitePlugin vitePlugins + +getTmplFilePathForVitePlugin :: VitePluginName -> TmplFilePath +getTmplFilePathForVitePlugin DetectServerImports = C.asTmplFile $ vitePluginsDirInWebAppDir [relfile|detectServerImports.ts|] + +genVitePlugin :: VitePlugin -> Generator FileDraft +genVitePlugin (DetectServerImports, tmplFile) = genDetectServerImportsPlugin tmplFile + +genDetectServerImportsPlugin :: Path' (Rel WebAppTemplatesDir) File' -> Generator FileDraft +genDetectServerImportsPlugin tmplFile = return $ C.mkTmplFdWithData tmplFile tmplData + where + tmplData = + object + [ "waspProjectDirFromWebAppDir" .= SP.fromRelDir waspProjectDirFromWebAppDir, + "srcDirInWaspProjectDir" .= SP.fromRelDir srcDirInWaspProjectDir + ] + + waspProjectDirFromWebAppDir = waspProjectDirFromAppComponentDir :: Path' (Rel WebAppRootDir) (Dir WaspProjectDir) + +-- todo(filip): Take care of this as well +genViteConfig :: AppSpec -> Generator FileDraft +genViteConfig spec = return $ C.mkTmplFdWithData tmplFile tmplData + where + tmplFile = C.asTmplFile [relfile|vite.config.ts|] + tmplData = + object + [ "customViteConfig" .= jsImportToImportJson (makeCustomViteConfigJsImport <$> AS.customViteConfigPath spec), + "baseDir" .= SP.fromAbsDirP (C.getBaseDir spec), + "defaultClientPort" .= C.defaultClientPort, + "vitest" + .= object + [ "setupFilesArray" .= makeJsArrayFromHaskellList vitestSetupFiles, + "excludeWaspArtefactsPattern" .= (SP.fromRelDirP (fromJust $ SP.relDirToPosix dotWaspDirInWaspProjectDir) FP.Posix. "**" FP.Posix. "*") + ] + ] + vitestSetupFiles = + [ SP.fromRelFile $ + dotWaspDirInWaspProjectDir + generatedCodeDirInDotWaspDir + webAppRootDirInProjectRootDir + webAppSrcDirInWebAppRootDir + [relfile|test/vitest/setup.ts|] + ] + + makeCustomViteConfigJsImport :: Path' (Rel WaspProjectDir) File' -> JsImport + makeCustomViteConfigJsImport pathToConfig = makeJsImport (RelativeImportPath importPath) importName + where + importPath = SP.castRel $ C.toViteImportPath relPathToConfigInProjectDir + relPathToConfigInProjectDir = relPathFromWebAppRootDirWaspProjectDir (fromJust . SP.relFileToPosix $ pathToConfig) + + relPathFromWebAppRootDirWaspProjectDir :: Path Posix (Rel C.WebAppRootDir) (Dir WaspProjectDir) + relPathFromWebAppRootDirWaspProjectDir = + fromJust $ + SP.parseRelDirP $ + FP.Extra.reversePosixPath $ + SP.fromRelDir (dotWaspDirInWaspProjectDir generatedCodeDirInDotWaspDir C.webAppRootDirInProjectRootDir) + + importName = JsImportModule "customViteConfig" + +vitePluginsDirInWebAppDir :: Path' (Rel WebAppRootDir) (Dir WebAppVitePluginsDir) +vitePluginsDirInWebAppDir = [reldir|vite|] diff --git a/waspc/src/Wasp/Generator/WebAppGenerator/VitePlugins.hs b/waspc/src/Wasp/Generator/WebAppGenerator/VitePlugins.hs deleted file mode 100644 index fbcc525aec..0000000000 --- a/waspc/src/Wasp/Generator/WebAppGenerator/VitePlugins.hs +++ /dev/null @@ -1,54 +0,0 @@ -{-# LANGUAGE TemplateHaskell #-} - -module Wasp.Generator.WebAppGenerator.VitePlugins - ( genVitePlugins, - vitePlugins, - ) -where - -import Data.Aeson (object, (.=)) -import StrongPath (Dir, File', Path', Rel, relfile, ()) -import qualified StrongPath as SP -import Wasp.Generator.Common (WebAppRootDir) -import Wasp.Generator.FileDraft (FileDraft) -import Wasp.Generator.Monad (Generator) -import Wasp.Generator.WebAppGenerator.Common (WebAppTemplatesDir, vitePluginsDirInWebAppDir) -import qualified Wasp.Generator.WebAppGenerator.Common as C -import Wasp.Project.Common (WaspProjectDir, srcDirInWaspProjectDir, waspProjectDirFromAppComponentDir) - -data VitePluginName = DetectServerImports - deriving (Enum, Bounded) - -type TmplFilePath = Path' (Rel WebAppTemplatesDir) File' - --- We define it like this because we need a list of plugin --- paths which we will use in the tsconfig.node.json "include" section -type VitePlugin = (VitePluginName, TmplFilePath) - -vitePlugins :: [VitePlugin] -vitePlugins = - map - (\name -> (name, getTmplFilePathForVitePlugin name)) - vitePluginNames - where - vitePluginNames = [minBound .. maxBound] - -genVitePlugins :: Generator [FileDraft] -genVitePlugins = mapM genVitePlugin vitePlugins - -getTmplFilePathForVitePlugin :: VitePluginName -> TmplFilePath -getTmplFilePathForVitePlugin DetectServerImports = C.asTmplFile $ vitePluginsDirInWebAppDir [relfile|detectServerImports.ts|] - -genVitePlugin :: VitePlugin -> Generator FileDraft -genVitePlugin (DetectServerImports, tmplFile) = genDetectServerImportsPlugin tmplFile - -genDetectServerImportsPlugin :: Path' (Rel WebAppTemplatesDir) File' -> Generator FileDraft -genDetectServerImportsPlugin tmplFile = return $ C.mkTmplFdWithData tmplFile tmplData - where - tmplData = - object - [ "waspProjectDirFromWebAppDir" .= SP.fromRelDir waspProjectDirFromWebAppDir, - "srcDirInWaspProjectDir" .= SP.fromRelDir srcDirInWaspProjectDir - ] - - waspProjectDirFromWebAppDir = waspProjectDirFromAppComponentDir :: Path' (Rel WebAppRootDir) (Dir WaspProjectDir) diff --git a/waspc/waspc.cabal b/waspc/waspc.cabal index 8fabfb204a..1ea2085ccb 100644 --- a/waspc/waspc.cabal +++ b/waspc/waspc.cabal @@ -355,7 +355,7 @@ library Wasp.Generator.WebAppGenerator.Setup Wasp.Generator.WebAppGenerator.Start Wasp.Generator.WebAppGenerator.Test - Wasp.Generator.WebAppGenerator.VitePlugins + Wasp.Generator.WebAppGenerator.Vite Wasp.Generator.WebSocket Wasp.Generator.WriteFileDrafts Wasp.JsImport From 80cbb469293b11911cb65b76924dec7094a13669 Mon Sep 17 00:00:00 2001 From: Mihovil Ilakovac Date: Fri, 7 Feb 2025 17:10:04 +0100 Subject: [PATCH 21/22] PR comments --- .../templates/react-app/tsconfig.json | 2 +- ...{tsconfig.node.json => tsconfig.vite.json} | 0 .../react-app/vite/detectServerImports.ts | 18 ++++++++--- waspc/src/Wasp/Generator/WebAppGenerator.hs | 14 ++------- .../Wasp/Generator/WebAppGenerator/Vite.hs | 31 +++++++++++++------ 5 files changed, 39 insertions(+), 26 deletions(-) rename waspc/data/Generator/templates/react-app/{tsconfig.node.json => tsconfig.vite.json} (100%) diff --git a/waspc/data/Generator/templates/react-app/tsconfig.json b/waspc/data/Generator/templates/react-app/tsconfig.json index c529e779ec..a4817e8940 100644 --- a/waspc/data/Generator/templates/react-app/tsconfig.json +++ b/waspc/data/Generator/templates/react-app/tsconfig.json @@ -2,6 +2,6 @@ "files": [], "references": [ { "path": "./tsconfig.app.json" }, - { "path": "./tsconfig.node.json" }, + { "path": "./tsconfig.vite.json" }, ] } diff --git a/waspc/data/Generator/templates/react-app/tsconfig.node.json b/waspc/data/Generator/templates/react-app/tsconfig.vite.json similarity index 100% rename from waspc/data/Generator/templates/react-app/tsconfig.node.json rename to waspc/data/Generator/templates/react-app/tsconfig.vite.json diff --git a/waspc/data/Generator/templates/react-app/vite/detectServerImports.ts b/waspc/data/Generator/templates/react-app/vite/detectServerImports.ts index 5c2d235157..a9c11b711d 100644 --- a/waspc/data/Generator/templates/react-app/vite/detectServerImports.ts +++ b/waspc/data/Generator/templates/react-app/vite/detectServerImports.ts @@ -29,7 +29,10 @@ const serverImportChecks: ImportCheckPredicate[] = [ (moduleName: string) => moduleName.startsWith('wasp/server'), ] -function ensureNoServerImports(source: string, relativeImporter: RelativePathToUserCode): void { +function ensureNoServerImports( + source: string, + relativeImporter: RelativePathToUserCode +): void { for (const check of serverImportChecks) { if (check(source)) { throw new Error( @@ -41,9 +44,16 @@ function ensureNoServerImports(source: string, relativeImporter: RelativePathToU type RelativePathToUserCode = string & { _brand: 'relativePathToUserCode' } -function parsePathToUserCode(importerPath: string): RelativePathToUserCode | null { - const importerPathRelativeToWaspProjectDir = path.relative(waspProjectDirAbsPath, importerPath) - return importerPathRelativeToWaspProjectDir.startsWith('{= srcDirInWaspProjectDir =}') ? importerPathRelativeToWaspProjectDir : null +function parsePathToUserCode( + importerPath: string +): RelativePathToUserCode | null { + const importerPathRelativeToWaspProjectDir = path.relative( + waspProjectDirAbsPath, + importerPath + ) + return importerPathRelativeToWaspProjectDir.startsWith('src/') + ? (importerPathRelativeToWaspProjectDir as RelativePathToUserCode) + : null } // We can't pass the "waspProjectDir" path from Haskell because we need the absolute path: diff --git a/waspc/src/Wasp/Generator/WebAppGenerator.hs b/waspc/src/Wasp/Generator/WebAppGenerator.hs index b0917dee6b..7a237d35b6 100644 --- a/waspc/src/Wasp/Generator/WebAppGenerator.hs +++ b/waspc/src/Wasp/Generator/WebAppGenerator.hs @@ -43,7 +43,7 @@ import Wasp.Generator.WebAppGenerator.Common import qualified Wasp.Generator.WebAppGenerator.Common as C import Wasp.Generator.WebAppGenerator.JsImport (extImportToImportJson) import Wasp.Generator.WebAppGenerator.RouterGenerator (genRouter) -import Wasp.Generator.WebAppGenerator.Vite (genVite, vitePlugins) +import Wasp.Generator.WebAppGenerator.Vite (genVite) import qualified Wasp.Generator.WebSocket as AS.WS import qualified Wasp.Node.Version as NodeVersion import Wasp.Project.Common @@ -62,8 +62,7 @@ genWebApp spec = do genPackageJson spec (npmDepsForWasp spec), genNpmrc, genGitignore, - genIndexHtml spec, - genNodeTsConfigJson + genIndexHtml spec ] <++> genSrcDir spec <++> genPublicDir spec @@ -231,12 +230,3 @@ getIndexTs spec = relPathToWebAppSrcDir :: Path Posix (Rel importLocation) (Dir C.WebAppSrcDir) relPathToWebAppSrcDir = [reldirP|./|] - -genNodeTsConfigJson :: Generator FileDraft -genNodeTsConfigJson = return $ C.mkTmplFdWithData [relfile|tsconfig.node.json|] tmplData - where - tmplData = object ["includePaths" .= includePaths] - - includePaths = ["vite.config.ts", "./src/ext-src/vite.config.ts"] ++ vitePluginPaths - - vitePluginPaths = map (SP.fromRelFile . snd) vitePlugins diff --git a/waspc/src/Wasp/Generator/WebAppGenerator/Vite.hs b/waspc/src/Wasp/Generator/WebAppGenerator/Vite.hs index 8de3d035e5..f3312232fd 100644 --- a/waspc/src/Wasp/Generator/WebAppGenerator/Vite.hs +++ b/waspc/src/Wasp/Generator/WebAppGenerator/Vite.hs @@ -1,8 +1,4 @@ -module Wasp.Generator.WebAppGenerator.Vite - ( genVite, - vitePlugins, - ) -where +module Wasp.Generator.WebAppGenerator.Vite (genVite) where import Data.Aeson (object, (.=)) import Data.Maybe (fromJust) @@ -41,7 +37,10 @@ data WebAppVitePluginsDir genVite :: AppSpec -> Generator [FileDraft] genVite spec = - sequence [genViteConfig spec] + sequence + [ genViteConfig spec, + genViteTsconfigJson + ] <++> genVitePlugins vitePlugins :: [VitePlugin] @@ -72,11 +71,12 @@ genDetectServerImportsPlugin tmplFile = return $ C.mkTmplFdWithData tmplFile tmp waspProjectDirFromWebAppDir = waspProjectDirFromAppComponentDir :: Path' (Rel WebAppRootDir) (Dir WaspProjectDir) --- todo(filip): Take care of this as well +viteConfigTmplFile :: Path' (Rel WebAppTemplatesDir) File' +viteConfigTmplFile = C.asTmplFile [relfile|vite.config.ts|] + genViteConfig :: AppSpec -> Generator FileDraft -genViteConfig spec = return $ C.mkTmplFdWithData tmplFile tmplData +genViteConfig spec = return $ C.mkTmplFdWithData viteConfigTmplFile tmplData where - tmplFile = C.asTmplFile [relfile|vite.config.ts|] tmplData = object [ "customViteConfig" .= jsImportToImportJson (makeCustomViteConfigJsImport <$> AS.customViteConfigPath spec), @@ -112,5 +112,18 @@ genViteConfig spec = return $ C.mkTmplFdWithData tmplFile tmplData importName = JsImportModule "customViteConfig" +genViteTsconfigJson :: Generator FileDraft +genViteTsconfigJson = return $ C.mkTmplFdWithData [relfile|tsconfig.vite.json|] tmplData + where + tmplData = object ["includePaths" .= includePaths] + + includePaths = + [ SP.fromRelFile viteConfigTmplFile, + "./src/ext-src/vite.config.ts" + ] + ++ vitePluginPaths + + vitePluginPaths = map (SP.fromRelFile . snd) vitePlugins + vitePluginsDirInWebAppDir :: Path' (Rel WebAppRootDir) (Dir WebAppVitePluginsDir) vitePluginsDirInWebAppDir = [reldir|vite|] From dc177707ba11834d4555823dbdb2266510d29ab6 Mon Sep 17 00:00:00 2001 From: Mihovil Ilakovac Date: Fri, 7 Feb 2025 17:12:20 +0100 Subject: [PATCH 22/22] e2e tests Signed-off-by: Mihovil Ilakovac --- .../waspBuild-golden/files.manifest | 2 +- .../waspBuild/.wasp/build/.waspchecksums | 6 +-- .../.wasp/build/web-app/tsconfig.json | 2 +- ...{tsconfig.node.json => tsconfig.vite.json} | 0 .../build/web-app/vite/detectServerImports.ts | 37 ++++++++++--------- .../waspCompile-golden/files.manifest | 2 +- .../waspCompile/.wasp/out/.waspchecksums | 6 +-- .../.wasp/out/web-app/tsconfig.json | 2 +- ...{tsconfig.node.json => tsconfig.vite.json} | 0 .../out/web-app/vite/detectServerImports.ts | 37 ++++++++++--------- .../waspComplexTest-golden/files.manifest | 2 +- .../waspComplexTest/.wasp/out/.waspchecksums | 6 +-- .../.wasp/out/web-app/tsconfig.json | 2 +- ...{tsconfig.node.json => tsconfig.vite.json} | 0 .../out/web-app/vite/detectServerImports.ts | 37 ++++++++++--------- .../waspJob-golden/files.manifest | 2 +- .../waspJob/.wasp/out/.waspchecksums | 6 +-- .../waspJob/.wasp/out/web-app/tsconfig.json | 2 +- ...{tsconfig.node.json => tsconfig.vite.json} | 0 .../out/web-app/vite/detectServerImports.ts | 37 ++++++++++--------- .../waspMigrate-golden/files.manifest | 2 +- .../waspMigrate/.wasp/out/.waspchecksums | 6 +-- .../.wasp/out/web-app/tsconfig.json | 2 +- ...{tsconfig.node.json => tsconfig.vite.json} | 0 .../out/web-app/vite/detectServerImports.ts | 37 ++++++++++--------- 25 files changed, 125 insertions(+), 110 deletions(-) rename waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/web-app/{tsconfig.node.json => tsconfig.vite.json} (100%) rename waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/web-app/{tsconfig.node.json => tsconfig.vite.json} (100%) rename waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/web-app/{tsconfig.node.json => tsconfig.vite.json} (100%) rename waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/web-app/{tsconfig.node.json => tsconfig.vite.json} (100%) rename waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/web-app/{tsconfig.node.json => tsconfig.vite.json} (100%) diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/files.manifest b/waspc/e2e-test/test-outputs/waspBuild-golden/files.manifest index f7e8ac088a..6f80aae2d2 100644 --- a/waspc/e2e-test/test-outputs/waspBuild-golden/files.manifest +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/files.manifest @@ -250,7 +250,7 @@ waspBuild/.wasp/build/web-app/src/utils.js waspBuild/.wasp/build/web-app/src/vite-env.d.ts waspBuild/.wasp/build/web-app/tsconfig.app.json waspBuild/.wasp/build/web-app/tsconfig.json -waspBuild/.wasp/build/web-app/tsconfig.node.json +waspBuild/.wasp/build/web-app/tsconfig.vite.json waspBuild/.wasp/build/web-app/vite.config.ts waspBuild/.wasp/build/web-app/vite/detectServerImports.ts waspBuild/.wasp/out/sdk/wasp/api/events.ts diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/.waspchecksums b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/.waspchecksums index b2d3aa02b1..c4c6a5326f 100644 --- a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/.waspchecksums +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/.waspchecksums @@ -676,12 +676,12 @@ "file", "web-app/tsconfig.json" ], - "0f84a09c31c7cca7d2bed4fac04c3da0ce54b0996ab80b2b54f1a6a53e6fd139" + "f5cc3ad419dd02d73079da1ec7fd282e09d9753594a2e08b8c4bb7a8c5b483d7" ], [ [ "file", - "web-app/tsconfig.node.json" + "web-app/tsconfig.vite.json" ], "bb2bf9acd6049cb78205f30d81de3ca3f105c8b881d1ab36cb68da033c59a7e8" ], @@ -697,6 +697,6 @@ "file", "web-app/vite/detectServerImports.ts" ], - "d3724c4ec970e8f5c39d5da60220bf52317134f55530c508ee469bef0d2cd811" + "f2b028d0bebc03da4413f85ac0ff1302f4f49c41261cfe24e1bc791096366f1e" ] ] \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/web-app/tsconfig.json b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/web-app/tsconfig.json index c529e779ec..a4817e8940 100644 --- a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/web-app/tsconfig.json +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/web-app/tsconfig.json @@ -2,6 +2,6 @@ "files": [], "references": [ { "path": "./tsconfig.app.json" }, - { "path": "./tsconfig.node.json" }, + { "path": "./tsconfig.vite.json" }, ] } diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/web-app/tsconfig.node.json b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/web-app/tsconfig.vite.json similarity index 100% rename from waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/web-app/tsconfig.node.json rename to waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/web-app/tsconfig.vite.json diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/web-app/vite/detectServerImports.ts b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/web-app/vite/detectServerImports.ts index 287cdea51c..17a4f7c1f5 100644 --- a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/web-app/vite/detectServerImports.ts +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/web-app/vite/detectServerImports.ts @@ -12,13 +12,12 @@ export function detectServerImports(): Plugin { return } - const importerRelativePath = getPathRelativeToWaspProjectDir(importer) - - if (!isPathToUserCode(importerRelativePath)) { + const pathToUserCode = parsePathToUserCode(importer) + if (!pathToUserCode) { return } - ensureNoServerImports(source, importerRelativePath) + ensureNoServerImports(source, pathToUserCode) }, } } @@ -29,26 +28,31 @@ const serverImportChecks: ImportCheckPredicate[] = [ (moduleName: string) => moduleName.startsWith('wasp/server'), ] -function ensureNoServerImports(source: string, relativeImporter: RelativePath): void { +function ensureNoServerImports( + source: string, + relativeImporter: RelativePathToUserCode +): void { for (const check of serverImportChecks) { if (check(source)) { throw new Error( - `Server code cannot be imported in the client code. Import from "${source}" in "${relativeImporter.relativePath}" is not allowed.` + `Server code cannot be imported in the client code. Import from "${source}" in "${relativeImporter}" is not allowed.` ) } } } -type RelativePath = { - relativePath: string -} - -function getPathRelativeToWaspProjectDir(filePath: string): RelativePath { - return { relativePath: path.relative(waspProjectDirAbsPath, filePath) } -} +type RelativePathToUserCode = string & { _brand: 'relativePathToUserCode' } -function isPathToUserCode(filePath: RelativePath): boolean { - return filePath.relativePath.startsWith('src/') +function parsePathToUserCode( + importerPath: string +): RelativePathToUserCode | null { + const importerPathRelativeToWaspProjectDir = path.relative( + waspProjectDirAbsPath, + importerPath + ) + return importerPathRelativeToWaspProjectDir.startsWith('src/') + ? (importerPathRelativeToWaspProjectDir as RelativePathToUserCode) + : null } // We can't pass the "waspProjectDir" path from Haskell because we need the absolute path: @@ -57,6 +61,5 @@ function isPathToUserCode(filePath: RelativePath): boolean { // This breaks our e2e tests in the CI because the path is different. function getWaspProjectDirAbsPathFromCwd(): string { const webAppDirAbsPath = process.cwd() - const waspProjectDirAbsPath = path.join(webAppDirAbsPath, '../../../') - return waspProjectDirAbsPath + return path.join(webAppDirAbsPath, '../../../') } diff --git a/waspc/e2e-test/test-outputs/waspCompile-golden/files.manifest b/waspc/e2e-test/test-outputs/waspCompile-golden/files.manifest index e21f615044..6d4a79a41a 100644 --- a/waspc/e2e-test/test-outputs/waspCompile-golden/files.manifest +++ b/waspc/e2e-test/test-outputs/waspCompile-golden/files.manifest @@ -247,7 +247,7 @@ waspCompile/.wasp/out/web-app/src/utils.js waspCompile/.wasp/out/web-app/src/vite-env.d.ts waspCompile/.wasp/out/web-app/tsconfig.app.json waspCompile/.wasp/out/web-app/tsconfig.json -waspCompile/.wasp/out/web-app/tsconfig.node.json +waspCompile/.wasp/out/web-app/tsconfig.vite.json waspCompile/.wasp/out/web-app/vite.config.ts waspCompile/.wasp/out/web-app/vite/detectServerImports.ts waspCompile/.waspignore diff --git a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/.waspchecksums b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/.waspchecksums index 62b8bf3132..33ecf129b1 100644 --- a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/.waspchecksums +++ b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/.waspchecksums @@ -690,12 +690,12 @@ "file", "web-app/tsconfig.json" ], - "0f84a09c31c7cca7d2bed4fac04c3da0ce54b0996ab80b2b54f1a6a53e6fd139" + "f5cc3ad419dd02d73079da1ec7fd282e09d9753594a2e08b8c4bb7a8c5b483d7" ], [ [ "file", - "web-app/tsconfig.node.json" + "web-app/tsconfig.vite.json" ], "bb2bf9acd6049cb78205f30d81de3ca3f105c8b881d1ab36cb68da033c59a7e8" ], @@ -711,6 +711,6 @@ "file", "web-app/vite/detectServerImports.ts" ], - "d3724c4ec970e8f5c39d5da60220bf52317134f55530c508ee469bef0d2cd811" + "f2b028d0bebc03da4413f85ac0ff1302f4f49c41261cfe24e1bc791096366f1e" ] ] \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/web-app/tsconfig.json b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/web-app/tsconfig.json index c529e779ec..a4817e8940 100644 --- a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/web-app/tsconfig.json +++ b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/web-app/tsconfig.json @@ -2,6 +2,6 @@ "files": [], "references": [ { "path": "./tsconfig.app.json" }, - { "path": "./tsconfig.node.json" }, + { "path": "./tsconfig.vite.json" }, ] } diff --git a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/web-app/tsconfig.node.json b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/web-app/tsconfig.vite.json similarity index 100% rename from waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/web-app/tsconfig.node.json rename to waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/web-app/tsconfig.vite.json diff --git a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/web-app/vite/detectServerImports.ts b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/web-app/vite/detectServerImports.ts index 287cdea51c..17a4f7c1f5 100644 --- a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/web-app/vite/detectServerImports.ts +++ b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/web-app/vite/detectServerImports.ts @@ -12,13 +12,12 @@ export function detectServerImports(): Plugin { return } - const importerRelativePath = getPathRelativeToWaspProjectDir(importer) - - if (!isPathToUserCode(importerRelativePath)) { + const pathToUserCode = parsePathToUserCode(importer) + if (!pathToUserCode) { return } - ensureNoServerImports(source, importerRelativePath) + ensureNoServerImports(source, pathToUserCode) }, } } @@ -29,26 +28,31 @@ const serverImportChecks: ImportCheckPredicate[] = [ (moduleName: string) => moduleName.startsWith('wasp/server'), ] -function ensureNoServerImports(source: string, relativeImporter: RelativePath): void { +function ensureNoServerImports( + source: string, + relativeImporter: RelativePathToUserCode +): void { for (const check of serverImportChecks) { if (check(source)) { throw new Error( - `Server code cannot be imported in the client code. Import from "${source}" in "${relativeImporter.relativePath}" is not allowed.` + `Server code cannot be imported in the client code. Import from "${source}" in "${relativeImporter}" is not allowed.` ) } } } -type RelativePath = { - relativePath: string -} - -function getPathRelativeToWaspProjectDir(filePath: string): RelativePath { - return { relativePath: path.relative(waspProjectDirAbsPath, filePath) } -} +type RelativePathToUserCode = string & { _brand: 'relativePathToUserCode' } -function isPathToUserCode(filePath: RelativePath): boolean { - return filePath.relativePath.startsWith('src/') +function parsePathToUserCode( + importerPath: string +): RelativePathToUserCode | null { + const importerPathRelativeToWaspProjectDir = path.relative( + waspProjectDirAbsPath, + importerPath + ) + return importerPathRelativeToWaspProjectDir.startsWith('src/') + ? (importerPathRelativeToWaspProjectDir as RelativePathToUserCode) + : null } // We can't pass the "waspProjectDir" path from Haskell because we need the absolute path: @@ -57,6 +61,5 @@ function isPathToUserCode(filePath: RelativePath): boolean { // This breaks our e2e tests in the CI because the path is different. function getWaspProjectDirAbsPathFromCwd(): string { const webAppDirAbsPath = process.cwd() - const waspProjectDirAbsPath = path.join(webAppDirAbsPath, '../../../') - return waspProjectDirAbsPath + return path.join(webAppDirAbsPath, '../../../') } diff --git a/waspc/e2e-test/test-outputs/waspComplexTest-golden/files.manifest b/waspc/e2e-test/test-outputs/waspComplexTest-golden/files.manifest index 87dd7c75b9..05fba302ba 100644 --- a/waspc/e2e-test/test-outputs/waspComplexTest-golden/files.manifest +++ b/waspc/e2e-test/test-outputs/waspComplexTest-golden/files.manifest @@ -535,7 +535,7 @@ waspComplexTest/.wasp/out/web-app/src/utils.js waspComplexTest/.wasp/out/web-app/src/vite-env.d.ts waspComplexTest/.wasp/out/web-app/tsconfig.app.json waspComplexTest/.wasp/out/web-app/tsconfig.json -waspComplexTest/.wasp/out/web-app/tsconfig.node.json +waspComplexTest/.wasp/out/web-app/tsconfig.vite.json waspComplexTest/.wasp/out/web-app/vite.config.ts waspComplexTest/.wasp/out/web-app/vite/detectServerImports.ts waspComplexTest/.waspignore diff --git a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/.waspchecksums b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/.waspchecksums index 88acb94042..9627649a5e 100644 --- a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/.waspchecksums +++ b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/.waspchecksums @@ -1327,12 +1327,12 @@ "file", "web-app/tsconfig.json" ], - "0f84a09c31c7cca7d2bed4fac04c3da0ce54b0996ab80b2b54f1a6a53e6fd139" + "f5cc3ad419dd02d73079da1ec7fd282e09d9753594a2e08b8c4bb7a8c5b483d7" ], [ [ "file", - "web-app/tsconfig.node.json" + "web-app/tsconfig.vite.json" ], "bb2bf9acd6049cb78205f30d81de3ca3f105c8b881d1ab36cb68da033c59a7e8" ], @@ -1348,6 +1348,6 @@ "file", "web-app/vite/detectServerImports.ts" ], - "d3724c4ec970e8f5c39d5da60220bf52317134f55530c508ee469bef0d2cd811" + "f2b028d0bebc03da4413f85ac0ff1302f4f49c41261cfe24e1bc791096366f1e" ] ] \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/web-app/tsconfig.json b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/web-app/tsconfig.json index c529e779ec..a4817e8940 100644 --- a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/web-app/tsconfig.json +++ b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/web-app/tsconfig.json @@ -2,6 +2,6 @@ "files": [], "references": [ { "path": "./tsconfig.app.json" }, - { "path": "./tsconfig.node.json" }, + { "path": "./tsconfig.vite.json" }, ] } diff --git a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/web-app/tsconfig.node.json b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/web-app/tsconfig.vite.json similarity index 100% rename from waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/web-app/tsconfig.node.json rename to waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/web-app/tsconfig.vite.json diff --git a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/web-app/vite/detectServerImports.ts b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/web-app/vite/detectServerImports.ts index 287cdea51c..17a4f7c1f5 100644 --- a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/web-app/vite/detectServerImports.ts +++ b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/web-app/vite/detectServerImports.ts @@ -12,13 +12,12 @@ export function detectServerImports(): Plugin { return } - const importerRelativePath = getPathRelativeToWaspProjectDir(importer) - - if (!isPathToUserCode(importerRelativePath)) { + const pathToUserCode = parsePathToUserCode(importer) + if (!pathToUserCode) { return } - ensureNoServerImports(source, importerRelativePath) + ensureNoServerImports(source, pathToUserCode) }, } } @@ -29,26 +28,31 @@ const serverImportChecks: ImportCheckPredicate[] = [ (moduleName: string) => moduleName.startsWith('wasp/server'), ] -function ensureNoServerImports(source: string, relativeImporter: RelativePath): void { +function ensureNoServerImports( + source: string, + relativeImporter: RelativePathToUserCode +): void { for (const check of serverImportChecks) { if (check(source)) { throw new Error( - `Server code cannot be imported in the client code. Import from "${source}" in "${relativeImporter.relativePath}" is not allowed.` + `Server code cannot be imported in the client code. Import from "${source}" in "${relativeImporter}" is not allowed.` ) } } } -type RelativePath = { - relativePath: string -} - -function getPathRelativeToWaspProjectDir(filePath: string): RelativePath { - return { relativePath: path.relative(waspProjectDirAbsPath, filePath) } -} +type RelativePathToUserCode = string & { _brand: 'relativePathToUserCode' } -function isPathToUserCode(filePath: RelativePath): boolean { - return filePath.relativePath.startsWith('src/') +function parsePathToUserCode( + importerPath: string +): RelativePathToUserCode | null { + const importerPathRelativeToWaspProjectDir = path.relative( + waspProjectDirAbsPath, + importerPath + ) + return importerPathRelativeToWaspProjectDir.startsWith('src/') + ? (importerPathRelativeToWaspProjectDir as RelativePathToUserCode) + : null } // We can't pass the "waspProjectDir" path from Haskell because we need the absolute path: @@ -57,6 +61,5 @@ function isPathToUserCode(filePath: RelativePath): boolean { // This breaks our e2e tests in the CI because the path is different. function getWaspProjectDirAbsPathFromCwd(): string { const webAppDirAbsPath = process.cwd() - const waspProjectDirAbsPath = path.join(webAppDirAbsPath, '../../../') - return waspProjectDirAbsPath + return path.join(webAppDirAbsPath, '../../../') } diff --git a/waspc/e2e-test/test-outputs/waspJob-golden/files.manifest b/waspc/e2e-test/test-outputs/waspJob-golden/files.manifest index ec425c1bba..12e81acf1f 100644 --- a/waspc/e2e-test/test-outputs/waspJob-golden/files.manifest +++ b/waspc/e2e-test/test-outputs/waspJob-golden/files.manifest @@ -292,7 +292,7 @@ waspJob/.wasp/out/web-app/src/utils.js waspJob/.wasp/out/web-app/src/vite-env.d.ts waspJob/.wasp/out/web-app/tsconfig.app.json waspJob/.wasp/out/web-app/tsconfig.json -waspJob/.wasp/out/web-app/tsconfig.node.json +waspJob/.wasp/out/web-app/tsconfig.vite.json waspJob/.wasp/out/web-app/vite.config.ts waspJob/.wasp/out/web-app/vite/detectServerImports.ts waspJob/.waspignore diff --git a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/.waspchecksums b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/.waspchecksums index e3028b1cd1..ababf6059d 100644 --- a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/.waspchecksums +++ b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/.waspchecksums @@ -788,12 +788,12 @@ "file", "web-app/tsconfig.json" ], - "0f84a09c31c7cca7d2bed4fac04c3da0ce54b0996ab80b2b54f1a6a53e6fd139" + "f5cc3ad419dd02d73079da1ec7fd282e09d9753594a2e08b8c4bb7a8c5b483d7" ], [ [ "file", - "web-app/tsconfig.node.json" + "web-app/tsconfig.vite.json" ], "bb2bf9acd6049cb78205f30d81de3ca3f105c8b881d1ab36cb68da033c59a7e8" ], @@ -809,6 +809,6 @@ "file", "web-app/vite/detectServerImports.ts" ], - "d3724c4ec970e8f5c39d5da60220bf52317134f55530c508ee469bef0d2cd811" + "f2b028d0bebc03da4413f85ac0ff1302f4f49c41261cfe24e1bc791096366f1e" ] ] \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/web-app/tsconfig.json b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/web-app/tsconfig.json index c529e779ec..a4817e8940 100644 --- a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/web-app/tsconfig.json +++ b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/web-app/tsconfig.json @@ -2,6 +2,6 @@ "files": [], "references": [ { "path": "./tsconfig.app.json" }, - { "path": "./tsconfig.node.json" }, + { "path": "./tsconfig.vite.json" }, ] } diff --git a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/web-app/tsconfig.node.json b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/web-app/tsconfig.vite.json similarity index 100% rename from waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/web-app/tsconfig.node.json rename to waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/web-app/tsconfig.vite.json diff --git a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/web-app/vite/detectServerImports.ts b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/web-app/vite/detectServerImports.ts index 287cdea51c..17a4f7c1f5 100644 --- a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/web-app/vite/detectServerImports.ts +++ b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/web-app/vite/detectServerImports.ts @@ -12,13 +12,12 @@ export function detectServerImports(): Plugin { return } - const importerRelativePath = getPathRelativeToWaspProjectDir(importer) - - if (!isPathToUserCode(importerRelativePath)) { + const pathToUserCode = parsePathToUserCode(importer) + if (!pathToUserCode) { return } - ensureNoServerImports(source, importerRelativePath) + ensureNoServerImports(source, pathToUserCode) }, } } @@ -29,26 +28,31 @@ const serverImportChecks: ImportCheckPredicate[] = [ (moduleName: string) => moduleName.startsWith('wasp/server'), ] -function ensureNoServerImports(source: string, relativeImporter: RelativePath): void { +function ensureNoServerImports( + source: string, + relativeImporter: RelativePathToUserCode +): void { for (const check of serverImportChecks) { if (check(source)) { throw new Error( - `Server code cannot be imported in the client code. Import from "${source}" in "${relativeImporter.relativePath}" is not allowed.` + `Server code cannot be imported in the client code. Import from "${source}" in "${relativeImporter}" is not allowed.` ) } } } -type RelativePath = { - relativePath: string -} - -function getPathRelativeToWaspProjectDir(filePath: string): RelativePath { - return { relativePath: path.relative(waspProjectDirAbsPath, filePath) } -} +type RelativePathToUserCode = string & { _brand: 'relativePathToUserCode' } -function isPathToUserCode(filePath: RelativePath): boolean { - return filePath.relativePath.startsWith('src/') +function parsePathToUserCode( + importerPath: string +): RelativePathToUserCode | null { + const importerPathRelativeToWaspProjectDir = path.relative( + waspProjectDirAbsPath, + importerPath + ) + return importerPathRelativeToWaspProjectDir.startsWith('src/') + ? (importerPathRelativeToWaspProjectDir as RelativePathToUserCode) + : null } // We can't pass the "waspProjectDir" path from Haskell because we need the absolute path: @@ -57,6 +61,5 @@ function isPathToUserCode(filePath: RelativePath): boolean { // This breaks our e2e tests in the CI because the path is different. function getWaspProjectDirAbsPathFromCwd(): string { const webAppDirAbsPath = process.cwd() - const waspProjectDirAbsPath = path.join(webAppDirAbsPath, '../../../') - return waspProjectDirAbsPath + return path.join(webAppDirAbsPath, '../../../') } diff --git a/waspc/e2e-test/test-outputs/waspMigrate-golden/files.manifest b/waspc/e2e-test/test-outputs/waspMigrate-golden/files.manifest index 6a566e49c5..5e871820f8 100644 --- a/waspc/e2e-test/test-outputs/waspMigrate-golden/files.manifest +++ b/waspc/e2e-test/test-outputs/waspMigrate-golden/files.manifest @@ -251,7 +251,7 @@ waspMigrate/.wasp/out/web-app/src/utils.js waspMigrate/.wasp/out/web-app/src/vite-env.d.ts waspMigrate/.wasp/out/web-app/tsconfig.app.json waspMigrate/.wasp/out/web-app/tsconfig.json -waspMigrate/.wasp/out/web-app/tsconfig.node.json +waspMigrate/.wasp/out/web-app/tsconfig.vite.json waspMigrate/.wasp/out/web-app/vite.config.ts waspMigrate/.wasp/out/web-app/vite/detectServerImports.ts waspMigrate/.waspignore diff --git a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/.waspchecksums b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/.waspchecksums index 63ee0abb64..c1eef1af19 100644 --- a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/.waspchecksums +++ b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/.waspchecksums @@ -690,12 +690,12 @@ "file", "web-app/tsconfig.json" ], - "0f84a09c31c7cca7d2bed4fac04c3da0ce54b0996ab80b2b54f1a6a53e6fd139" + "f5cc3ad419dd02d73079da1ec7fd282e09d9753594a2e08b8c4bb7a8c5b483d7" ], [ [ "file", - "web-app/tsconfig.node.json" + "web-app/tsconfig.vite.json" ], "bb2bf9acd6049cb78205f30d81de3ca3f105c8b881d1ab36cb68da033c59a7e8" ], @@ -711,6 +711,6 @@ "file", "web-app/vite/detectServerImports.ts" ], - "d3724c4ec970e8f5c39d5da60220bf52317134f55530c508ee469bef0d2cd811" + "f2b028d0bebc03da4413f85ac0ff1302f4f49c41261cfe24e1bc791096366f1e" ] ] \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/web-app/tsconfig.json b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/web-app/tsconfig.json index c529e779ec..a4817e8940 100644 --- a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/web-app/tsconfig.json +++ b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/web-app/tsconfig.json @@ -2,6 +2,6 @@ "files": [], "references": [ { "path": "./tsconfig.app.json" }, - { "path": "./tsconfig.node.json" }, + { "path": "./tsconfig.vite.json" }, ] } diff --git a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/web-app/tsconfig.node.json b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/web-app/tsconfig.vite.json similarity index 100% rename from waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/web-app/tsconfig.node.json rename to waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/web-app/tsconfig.vite.json diff --git a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/web-app/vite/detectServerImports.ts b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/web-app/vite/detectServerImports.ts index 287cdea51c..17a4f7c1f5 100644 --- a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/web-app/vite/detectServerImports.ts +++ b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/web-app/vite/detectServerImports.ts @@ -12,13 +12,12 @@ export function detectServerImports(): Plugin { return } - const importerRelativePath = getPathRelativeToWaspProjectDir(importer) - - if (!isPathToUserCode(importerRelativePath)) { + const pathToUserCode = parsePathToUserCode(importer) + if (!pathToUserCode) { return } - ensureNoServerImports(source, importerRelativePath) + ensureNoServerImports(source, pathToUserCode) }, } } @@ -29,26 +28,31 @@ const serverImportChecks: ImportCheckPredicate[] = [ (moduleName: string) => moduleName.startsWith('wasp/server'), ] -function ensureNoServerImports(source: string, relativeImporter: RelativePath): void { +function ensureNoServerImports( + source: string, + relativeImporter: RelativePathToUserCode +): void { for (const check of serverImportChecks) { if (check(source)) { throw new Error( - `Server code cannot be imported in the client code. Import from "${source}" in "${relativeImporter.relativePath}" is not allowed.` + `Server code cannot be imported in the client code. Import from "${source}" in "${relativeImporter}" is not allowed.` ) } } } -type RelativePath = { - relativePath: string -} - -function getPathRelativeToWaspProjectDir(filePath: string): RelativePath { - return { relativePath: path.relative(waspProjectDirAbsPath, filePath) } -} +type RelativePathToUserCode = string & { _brand: 'relativePathToUserCode' } -function isPathToUserCode(filePath: RelativePath): boolean { - return filePath.relativePath.startsWith('src/') +function parsePathToUserCode( + importerPath: string +): RelativePathToUserCode | null { + const importerPathRelativeToWaspProjectDir = path.relative( + waspProjectDirAbsPath, + importerPath + ) + return importerPathRelativeToWaspProjectDir.startsWith('src/') + ? (importerPathRelativeToWaspProjectDir as RelativePathToUserCode) + : null } // We can't pass the "waspProjectDir" path from Haskell because we need the absolute path: @@ -57,6 +61,5 @@ function isPathToUserCode(filePath: RelativePath): boolean { // This breaks our e2e tests in the CI because the path is different. function getWaspProjectDirAbsPathFromCwd(): string { const webAppDirAbsPath = process.cwd() - const waspProjectDirAbsPath = path.join(webAppDirAbsPath, '../../../') - return waspProjectDirAbsPath + return path.join(webAppDirAbsPath, '../../../') }