@wasp.sh/wasp-cli-darwin-arm64-unknown 0.20.2 → 0.21.0-rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (118) hide show
  1. package/data/Cli/starters/basic/README.md +1 -1
  2. package/data/Cli/starters/basic/package.json +10 -7
  3. package/data/Cli/starters/basic/src/App.css +30 -12
  4. package/data/Cli/starters/basic/src/App.tsx +1 -1
  5. package/data/Cli/starters/basic/src/auth/AuthLayout.tsx +3 -1
  6. package/data/Cli/starters/basic/src/auth/email/EmailVerificationPage.tsx +2 -2
  7. package/data/Cli/starters/basic/src/auth/email/LoginPage.tsx +3 -3
  8. package/data/Cli/starters/basic/src/auth/email/PasswordResetPage.tsx +2 -2
  9. package/data/Cli/starters/basic/src/auth/email/SignupPage.tsx +2 -2
  10. package/data/Cli/starters/basic/src/shared/components/Button.tsx +3 -2
  11. package/data/Cli/starters/basic/src/shared/components/Dialog.tsx +23 -41
  12. package/data/Cli/starters/basic/src/shared/components/Header.tsx +2 -2
  13. package/data/Cli/starters/basic/src/shared/components/Input.tsx +25 -28
  14. package/data/Cli/starters/basic/src/tags/components/ColorRadioButton.tsx +8 -9
  15. package/data/Cli/starters/basic/src/tags/components/CreateTagDialog.tsx +29 -29
  16. package/data/Cli/starters/basic/src/tags/components/CreateTagForm.tsx +6 -8
  17. package/data/Cli/starters/basic/src/tasks/components/CreateTaskForm.tsx +2 -2
  18. package/data/Cli/starters/basic/src/tasks/components/TaskListItem.tsx +3 -2
  19. package/data/Cli/starters/basic/vite.config.ts +3 -0
  20. package/data/Cli/starters/minimal/package.json +2 -3
  21. package/data/Cli/starters/minimal/vite.config.ts +2 -0
  22. package/data/Cli/starters/skeleton/src/vite-env.d.ts +2 -6
  23. package/data/Generator/libs/auth/wasp.sh-lib-auth-0.21.0.tgz +0 -0
  24. package/data/Generator/templates/Dockerfile +13 -12
  25. package/data/Generator/templates/sdk/wasp/auth/forms/Auth.tsx +2 -11
  26. package/data/Generator/templates/sdk/wasp/auth/forms/internal/common/LoginSignupForm.tsx +10 -4
  27. package/data/Generator/templates/sdk/wasp/auth/forms/internal/email/ForgotPasswordForm.tsx +4 -3
  28. package/data/Generator/templates/sdk/wasp/auth/forms/internal/email/ResetPasswordForm.tsx +4 -4
  29. package/data/Generator/templates/sdk/wasp/auth/forms/internal/email/VerifyEmailForm.tsx +3 -4
  30. package/data/Generator/templates/sdk/wasp/auth/forms/internal/social/SocialIcons.tsx +11 -0
  31. package/data/Generator/templates/sdk/wasp/auth/forms/types.ts +0 -6
  32. package/data/Generator/templates/sdk/wasp/auth/jwt.ts +9 -16
  33. package/data/Generator/templates/sdk/wasp/auth/password.ts +1 -36
  34. package/data/Generator/templates/sdk/wasp/auth/utils.ts +2 -0
  35. package/data/Generator/templates/{react-app/src → sdk/wasp/client/app}/components/DefaultRootErrorBoundary.tsx +1 -1
  36. package/data/Generator/templates/sdk/wasp/client/app/components/WaspApp.tsx +45 -0
  37. package/data/Generator/templates/sdk/wasp/client/app/index.tsx +26 -0
  38. package/data/Generator/templates/{react-app/src/auth → sdk/wasp/client/app}/pages/OAuthCallback.tsx +9 -8
  39. package/data/Generator/templates/{react-app/src/auth → sdk/wasp/client/app}/pages/createAuthRequiredPage.jsx +7 -9
  40. package/data/Generator/templates/sdk/wasp/client/app/router/router.tsx +47 -0
  41. package/data/Generator/templates/sdk/wasp/client/auth/index.ts +3 -0
  42. package/data/Generator/templates/sdk/wasp/client/auth/microsoft.ts +2 -0
  43. package/data/Generator/templates/sdk/wasp/client/auth/ui.ts +3 -0
  44. package/data/Generator/templates/sdk/wasp/client/router/Link.tsx +2 -2
  45. package/data/Generator/templates/sdk/wasp/client/test/vitest/helpers.tsx +10 -12
  46. package/data/Generator/templates/sdk/wasp/client/vite/index.ts +1 -0
  47. package/data/Generator/templates/sdk/wasp/client/vite/plugins/detectServerImports.ts +50 -0
  48. package/data/Generator/templates/sdk/wasp/client/vite/plugins/envFile.ts +112 -0
  49. package/data/Generator/templates/sdk/wasp/client/vite/plugins/html/build.ts +38 -0
  50. package/data/Generator/templates/sdk/wasp/client/vite/plugins/html/dev.ts +35 -0
  51. package/data/Generator/templates/sdk/wasp/client/vite/plugins/typescriptCheck.ts +32 -0
  52. package/data/Generator/templates/{react-app/vite → sdk/wasp/client/vite/plugins}/validateEnv.ts +17 -7
  53. package/data/Generator/templates/sdk/wasp/client/vite/plugins/virtualModules.ts +29 -0
  54. package/data/Generator/templates/sdk/wasp/client/vite/plugins/wasp.ts +36 -0
  55. package/data/Generator/templates/sdk/wasp/client/vite/plugins/waspConfig.ts +56 -0
  56. package/data/Generator/templates/{react-app → sdk/wasp/client/vite/virtual-files/files}/index.html +1 -2
  57. package/data/Generator/templates/sdk/wasp/client/vite/virtual-files/files/index.tsx +34 -0
  58. package/data/Generator/templates/sdk/wasp/client/vite/virtual-files/files/routes.tsx +17 -0
  59. package/data/Generator/templates/sdk/wasp/client/vite/virtual-files/index.ts +20 -0
  60. package/data/Generator/templates/sdk/wasp/client/vite/virtual-files/resolver.ts +28 -0
  61. package/data/Generator/templates/sdk/wasp/package.json +6 -4
  62. package/data/Generator/templates/sdk/wasp/scripts/copy-assets.js +67 -7
  63. package/data/Generator/templates/sdk/wasp/server/auth/hooks.ts +3 -0
  64. package/data/Generator/templates/sdk/wasp/server/auth/oauth/index.ts +4 -0
  65. package/data/Generator/templates/sdk/wasp/server/auth/oauth/providers/microsoft.ts +23 -0
  66. package/data/Generator/templates/sdk/wasp/server/auth/user.ts +6 -0
  67. package/data/Generator/templates/sdk/wasp/server/env.ts +11 -0
  68. package/data/Generator/templates/sdk/wasp/tsconfig.json +5 -1
  69. package/data/Generator/templates/server/src/auth/providers/config/microsoft.ts +65 -0
  70. package/data/Generator/templates/server/src/auth/providers/oauth/cookies.ts +1 -1
  71. package/data/packages/deploy/dist/common/clientApp.js +4 -11
  72. package/data/packages/deploy/dist/common/terminal.js +7 -0
  73. package/data/packages/deploy/dist/common/waspProject.js +11 -7
  74. package/data/packages/deploy/dist/providers/fly/CommonOps.js +3 -3
  75. package/data/packages/deploy/dist/providers/fly/commands/cmd/cmd.js +1 -1
  76. package/data/packages/deploy/dist/providers/fly/commands/deploy/deploy.js +6 -5
  77. package/data/packages/deploy/dist/providers/fly/commands/setup/setup.js +3 -3
  78. package/data/packages/deploy/dist/providers/fly/index.js +5 -0
  79. package/data/packages/deploy/dist/providers/railway/commands/deploy/client.js +2 -1
  80. package/data/packages/deploy/dist/providers/railway/commands/setup/setup.js +21 -10
  81. package/data/packages/deploy/dist/providers/railway/index.js +5 -0
  82. package/data/packages/deploy/dist/providers/railway/jsonOutputSchemas.js +9 -3
  83. package/data/packages/deploy/dist/providers/railway/railwayService/url.js +8 -1
  84. package/data/packages/studio/dist/public/assets/Flow-_d98T2dd.js +26 -0
  85. package/data/packages/studio/dist/public/assets/index-B6X8EdJH.js +21 -0
  86. package/data/packages/studio/dist/public/assets/index-CXlD_bzV.js +1 -0
  87. package/data/packages/studio/dist/public/assets/index-IWX3d-Jz.css +1 -0
  88. package/data/packages/studio/dist/public/index.html +2 -3
  89. package/package.json +1 -1
  90. package/wasp-bin +0 -0
  91. package/data/Cli/starters/basic/postcss.config.js +0 -6
  92. package/data/Cli/starters/basic/src/shared/components/Portal.tsx +0 -26
  93. package/data/Cli/starters/basic/tailwind.config.js +0 -30
  94. package/data/Generator/templates/react-app/README.md +0 -21
  95. package/data/Generator/templates/react-app/gitignore +0 -23
  96. package/data/Generator/templates/react-app/netlify.toml +0 -8
  97. package/data/Generator/templates/react-app/npmrc +0 -1
  98. package/data/Generator/templates/react-app/package.json +0 -31
  99. package/data/Generator/templates/react-app/public/manifest.json +0 -15
  100. package/data/Generator/templates/react-app/src/index.tsx +0 -47
  101. package/data/Generator/templates/react-app/src/logo.png +0 -0
  102. package/data/Generator/templates/react-app/src/router.tsx +0 -59
  103. package/data/Generator/templates/react-app/src/utils.js +0 -3
  104. package/data/Generator/templates/react-app/src/vite-env.d.ts +0 -1
  105. package/data/Generator/templates/react-app/tsconfig.app.json +0 -28
  106. package/data/Generator/templates/react-app/tsconfig.json +0 -11
  107. package/data/Generator/templates/react-app/tsconfig.vite.json +0 -16
  108. package/data/Generator/templates/react-app/vite/detectServerImports.ts +0 -53
  109. package/data/Generator/templates/react-app/vite.config.ts +0 -74
  110. package/data/Generator/templates/sdk/wasp/dev/index.ts +0 -19
  111. package/data/packages/studio/dist/public/assets/Flow-b5112d3d.js +0 -26
  112. package/data/packages/studio/dist/public/assets/index-17ce6ed4.css +0 -1
  113. package/data/packages/studio/dist/public/assets/index-62a9d21a.js +0 -120
  114. /package/data/Generator/templates/{react-app/src → sdk/wasp/client/app}/components/FullPageWrapper.tsx +0 -0
  115. /package/data/Generator/templates/{react-app/src → sdk/wasp/client/app}/components/Loader.module.css +0 -0
  116. /package/data/Generator/templates/{react-app/src → sdk/wasp/client/app}/components/Loader.tsx +0 -0
  117. /package/data/Generator/templates/{react-app/src → sdk/wasp/client/app}/components/Message.tsx +0 -0
  118. /package/data/Generator/templates/{react-app/src/test/vitest → sdk/wasp/client/test}/setup.ts +0 -0
@@ -29,27 +29,28 @@ FROM base AS server-builder
29
29
  # `python3` fixes the issue.
30
30
  RUN apk add --no-cache python3 build-base libtool autoconf automake
31
31
  WORKDIR /app
32
- # Since the framwork code in /.wasp/build/server imports the user code in /src
32
+ # Since the framwork code in /.wasp/out/server imports the user code in /src
33
33
  # using relative imports, we must mirror the same directory structure in the
34
34
  # Docker image.
35
35
  COPY src ./src
36
36
  COPY package.json .
37
37
  COPY package-lock.json .
38
38
  COPY tsconfig*.json .
39
- COPY server .wasp/build/server
39
+ COPY server .wasp/out/server
40
40
  COPY sdk .wasp/out/sdk
41
+ COPY libs .wasp/out/libs
41
42
  # Install npm packages, resulting in node_modules/.
42
- RUN npm install && cd .wasp/build/server && npm install
43
+ RUN npm install && cd .wasp/out/server && npm install
43
44
  {=# usingPrisma =}
44
- COPY db/schema.prisma .wasp/build/db/
45
- RUN cd .wasp/build/server && npx prisma generate --schema='{= dbSchemaFileFromServerDir =}'
45
+ COPY db/schema.prisma .wasp/out/db/
46
+ RUN cd .wasp/out/server && npx prisma generate --schema='{= dbSchemaFileFromServerDir =}'
46
47
  {=/ usingPrisma =}
47
48
  # Building the server should come after Prisma generation.
48
- RUN cd .wasp/build/server && npm run bundle
49
+ RUN cd .wasp/out/server && npm run bundle
49
50
 
50
51
  # Depending on `npm`'s dependency resolution, this folder may or may not exist.
51
52
  # We ensure they are created so that the COPY instruction later does not fail.
52
- RUN mkdir -p ./.wasp/build/server/node_modules
53
+ RUN mkdir -p ./.wasp/out/server/node_modules
53
54
 
54
55
 
55
56
  # TODO: Use pm2?
@@ -65,12 +66,12 @@ COPY --from=server-builder /app/node_modules ./node_modules
65
66
  # Copying 'server/node_modules' because we require dotenv package to
66
67
  # load environment variables
67
68
  # TODO: replace dotenv with native Node.js environment variable loading
68
- COPY --from=server-builder /app/.wasp/build/server/node_modules .wasp/build/server/node_modules
69
- COPY --from=server-builder /app/.wasp/build/server/bundle .wasp/build/server/bundle
70
- COPY --from=server-builder /app/.wasp/build/server/package*.json .wasp/build/server/
71
- COPY db/ .wasp/build/db/
69
+ COPY --from=server-builder /app/.wasp/out/server/node_modules .wasp/out/server/node_modules
70
+ COPY --from=server-builder /app/.wasp/out/server/bundle .wasp/out/server/bundle
71
+ COPY --from=server-builder /app/.wasp/out/server/package*.json .wasp/out/server/
72
+ COPY db/ .wasp/out/db/
72
73
  EXPOSE ${PORT}
73
- WORKDIR /app/.wasp/build/server
74
+ WORKDIR /app/.wasp/out/server
74
75
  ENTRYPOINT ["npm", "run", "start-production"]
75
76
 
76
77
 
@@ -1,14 +1,13 @@
1
1
  {{={= =}=}}
2
- import { useState, createContext, useMemo } from 'react'
2
+ import { useState, useMemo } from 'react'
3
+ import { AuthContext, type ErrorMessage } from '@wasp.sh/lib-auth/browser'
3
4
  import styles from './Auth.module.css'
4
5
  import './internal/auth-styles.css'
5
6
  import { tokenObjToCSSVars } from "./internal/util"
6
- import { CSSProperties } from "react"
7
7
 
8
8
  import {
9
9
  type State,
10
10
  type CustomizationOptions,
11
- type ErrorMessage,
12
11
  type AdditionalSignupFields,
13
12
  } from './types'
14
13
  import { LoginSignupForm } from './internal/common/LoginSignupForm'
@@ -24,14 +23,6 @@ const logoStyle = {
24
23
  }
25
24
 
26
25
 
27
- // PRIVATE API
28
- export const AuthContext = createContext({
29
- isLoading: false,
30
- setIsLoading: (isLoading: boolean) => {},
31
- setErrorMessage: (errorMessage: ErrorMessage | null) => {},
32
- setSuccessMessage: (successMessage: string | null) => {},
33
- })
34
-
35
26
  function Auth ({ state, appearance, logo, socialLayout = 'horizontal', additionalSignupFields }: {
36
27
  state: State;
37
28
  } & CustomizationOptions & {
@@ -1,12 +1,11 @@
1
1
  {{={= =}=}}
2
- import { useContext } from 'react'
3
2
  import { useForm, UseFormReturn } from 'react-hook-form'
4
3
  import styles from './LoginSignupForm.module.css'
5
4
  import '../auth-styles.css'
6
5
  import { config } from 'wasp/client'
7
6
  import { clsx } from '../util'
8
7
 
9
- import { AuthContext } from '../../Auth'
8
+ import { useAuthContext } from '@wasp.sh/lib-auth/browser'
10
9
  import {
11
10
  Form,
12
11
  FormInput,
@@ -27,7 +26,7 @@ import * as SocialIcons from '../social/SocialIcons'
27
26
  import { SocialButton } from '../social/SocialButton'
28
27
  {=/ isSocialAuthEnabled =}
29
28
  {=# isAnyPasswordBasedAuthEnabled =}
30
- import { useNavigate } from 'react-router-dom'
29
+ import { useNavigate } from 'react-router'
31
30
  {=/ isAnyPasswordBasedAuthEnabled =}
32
31
  {=# enabledProviders.isUsernameAndPasswordAuthEnabled =}
33
32
  import { useUsernameAndPassword } from '../usernameAndPassword/useUsernameAndPassword'
@@ -55,6 +54,9 @@ const keycloakSignInUrl = `${config.apiUrl}{= keycloakSignInPath =}`
55
54
  {=# enabledProviders.isGitHubAuthEnabled =}
56
55
  const gitHubSignInUrl = `${config.apiUrl}{= gitHubSignInPath =}`
57
56
  {=/ enabledProviders.isGitHubAuthEnabled =}
57
+ {=# enabledProviders.isMicrosoftAuthEnabled =}
58
+ const microsoftSignInUrl = `${config.apiUrl}{= microsoftSignInPath =}`
59
+ {=/ enabledProviders.isMicrosoftAuthEnabled =}
58
60
 
59
61
  {=!
60
62
  // Since we allow users to add additional fields to the signup form, we don't
@@ -81,7 +83,7 @@ export const LoginSignupForm = ({
81
83
  setErrorMessage,
82
84
  setSuccessMessage,
83
85
  setIsLoading,
84
- } = useContext(AuthContext)
86
+ } = useAuthContext();
85
87
  const isLogin = state === 'login'
86
88
  const cta = isLogin ? 'Log in' : 'Sign up';
87
89
  {=# isAnyPasswordBasedAuthEnabled =}
@@ -151,6 +153,10 @@ export const LoginSignupForm = ({
151
153
  {=# enabledProviders.isGitHubAuthEnabled =}
152
154
  <SocialButton href={gitHubSignInUrl}><SocialIcons.GitHub/></SocialButton>
153
155
  {=/ enabledProviders.isGitHubAuthEnabled =}
156
+
157
+ {=# enabledProviders.isMicrosoftAuthEnabled =}
158
+ <SocialButton href={microsoftSignInUrl}><SocialIcons.Microsoft/></SocialButton>
159
+ {=/ enabledProviders.isMicrosoftAuthEnabled =}
154
160
  </div>
155
161
  </div>
156
162
  {=/ isSocialAuthEnabled =}
@@ -1,13 +1,14 @@
1
- import { useContext } from 'react'
2
1
  import { useForm } from 'react-hook-form'
2
+ import { useAuthContext } from '@wasp.sh/lib-auth/browser'
3
+
3
4
  import { requestPasswordReset } from '../../../email/actions/passwordReset.js'
4
5
  import { Form, FormItemGroup, FormLabel, FormInput, SubmitButton, FormError } from '../Form'
5
- import { AuthContext } from '../../Auth'
6
+
6
7
 
7
8
  // PRIVATE API
8
9
  export const ForgotPasswordForm = () => {
9
10
  const { register, handleSubmit, reset, formState: { errors } } = useForm<{ email: string }>()
10
- const { isLoading, setErrorMessage, setSuccessMessage, setIsLoading } = useContext(AuthContext)
11
+ const { isLoading, setErrorMessage, setSuccessMessage, setIsLoading } = useAuthContext()
11
12
 
12
13
  const onSubmit = async (data) => {
13
14
  setIsLoading(true)
@@ -1,14 +1,14 @@
1
- import { useContext } from 'react'
2
1
  import { useForm } from 'react-hook-form'
2
+ import { useLocation } from 'react-router'
3
+ import { useAuthContext } from '@wasp.sh/lib-auth/browser'
4
+
3
5
  import { resetPassword } from '../../../email/actions/passwordReset.js'
4
- import { useLocation } from 'react-router-dom'
5
6
  import { Form, FormItemGroup, FormLabel, FormInput, SubmitButton, FormError } from '../Form'
6
- import { AuthContext } from '../../Auth'
7
7
 
8
8
  // PRIVATE API
9
9
  export const ResetPasswordForm = () => {
10
10
  const { register, handleSubmit, reset, formState: { errors } } = useForm<{ password: string; passwordConfirmation: string }>()
11
- const { isLoading, setErrorMessage, setSuccessMessage, setIsLoading } = useContext(AuthContext)
11
+ const { isLoading, setErrorMessage, setSuccessMessage, setIsLoading } = useAuthContext()
12
12
  const location = useLocation()
13
13
  const token = new URLSearchParams(location.search).get('token')
14
14
  const onSubmit = async (data) => {
@@ -1,13 +1,12 @@
1
- import { useContext } from 'react'
2
- import { useLocation } from 'react-router-dom'
1
+ import { useLocation } from 'react-router'
2
+ import { useAuthContext } from '@wasp.sh/lib-auth/browser'
3
3
  import { verifyEmail } from '../../../email/actions/verifyEmail.js'
4
4
  import { Message } from '../Message'
5
- import { AuthContext } from '../../Auth'
6
5
  import { useEffectOnce } from '../../../../client/hooks.js'
7
6
 
8
7
  // PRIVATE API
9
8
  export const VerifyEmailForm = () => {
10
- const { isLoading, setErrorMessage, setSuccessMessage, setIsLoading } = useContext(AuthContext)
9
+ const { isLoading, setErrorMessage, setSuccessMessage, setIsLoading } = useAuthContext()
11
10
  const location = useLocation()
12
11
  const token = new URLSearchParams(location.search).get('token')
13
12
 
@@ -72,3 +72,14 @@ export const Slack = () => (
72
72
  <path d="M13 10C13 11.1046 13.8954 12 15 12C16.1046 12 17 11.1046 17 10V5C17 3.89543 16.1046 3 15 3C13.8954 3 13 3.89543 13 5V10ZM5 8C3.89543 8 3 8.89543 3 10C3 11.1046 3.89543 12 5 12H10C11.1046 12 12 11.1046 12 10C12 8.89543 11.1046 8 10 8H5ZM15 13C13.8954 13 13 13.8954 13 15C13 16.1046 13.8954 17 15 17H20C21.1046 17 22 16.1046 22 15C22 13.8954 21.1046 13 20 13H15ZM10 22C8.89543 22 8 21.1046 8 20L8 15C8 13.8954 8.89543 13 10 13C11.1046 13 12 13.8954 12 15V20C12 21.1046 11.1046 22 10 22ZM8 5C8 3.89543 8.89543 3 10 3C11.1046 3 12 3.89543 12 5V7H10C8.89543 7 8 6.10457 8 5ZM3 15C3 16.1046 3.89543 17 5 17C6.10457 17 7 16.1046 7 15V13H5C3.89543 13 3 13.8954 3 15ZM17 20C17 21.1046 16.1046 22 15 22C13.8954 22 13 21.1046 13 20V18H15C16.1046 18 17 18.8954 17 20ZM22 10C22 8.89543 21.1046 8 20 8C18.8954 8 18 8.89543 18 10V12H20C21.1046 12 22 11.1046 22 10Z" />
73
73
  </svg>
74
74
  )
75
+
76
+ export const Microsoft = () => (
77
+ <svg
78
+ className={styles.icon}
79
+ aria-hidden="true"
80
+ fill="currentColor"
81
+ viewBox="0 0 24 24"
82
+ >
83
+ <path d="M2 3h9v9H2zm9 19H2v-9h9zM21 3v9h-9V3zm0 19h-9v-9h9z" />
84
+ </svg>
85
+ )
@@ -46,12 +46,6 @@ export type CustomizationOptions = {
46
46
  }
47
47
  }
48
48
 
49
- // PRIVATE API
50
- export type ErrorMessage = {
51
- title: string
52
- description?: string
53
- }
54
-
55
49
  // PRIVATE API
56
50
  export type FormState = {
57
51
  isLoading: boolean
@@ -1,22 +1,15 @@
1
- import * as jwt from 'oslo/jwt'
2
- import { config } from 'wasp/server'
1
+ import { createJWTHelpers } from "@wasp.sh/lib-auth/node";
3
2
 
4
- const JWT_SECRET = new TextEncoder().encode(config.auth.jwtSecret)
5
- const JWT_ALGORITHM = 'HS256'
3
+ import { config } from "wasp/server";
6
4
 
7
- // PRIVATE API
8
- export function createJWT(
9
- data: Parameters<typeof jwt.createJWT>[2],
10
- options: Parameters<typeof jwt.createJWT>[3],
11
- ): Promise<string> {
12
- return jwt.createJWT(JWT_ALGORITHM, JWT_SECRET, data, options)
13
- }
5
+ const JWT_SECRET = new TextEncoder().encode(config.auth.jwtSecret);
6
+ const JWT_ALGORITHM = "HS256";
14
7
 
15
8
  // PRIVATE API
16
- export async function validateJWT<Payload>(token: string): Promise<Payload> {
17
- const { payload } = await jwt.validateJWT(JWT_ALGORITHM, JWT_SECRET, token)
18
- return payload as Payload
19
- }
9
+ export const { createJWT, validateJWT } = createJWTHelpers(
10
+ JWT_SECRET,
11
+ JWT_ALGORITHM,
12
+ );
20
13
 
21
14
  // PRIVATE API
22
- export { TimeSpan } from 'oslo'
15
+ export { TimeSpan } from "@wasp.sh/lib-auth/node";
@@ -1,36 +1 @@
1
- import { hash, verify, Version, type Options } from "@node-rs/argon2";
2
-
3
- // The options are the same as the ones used in the oslo/password library
4
- const hashingOptions: Options = {
5
- memoryCost: 19456,
6
- timeCost: 2,
7
- outputLen: 32,
8
- parallelism: 1,
9
- version: Version.V0x13,
10
- };
11
-
12
- // PRIVATE API
13
- export async function hashPassword(password: string): Promise<string> {
14
- return hash(normalizePassword(password), hashingOptions);
15
- }
16
-
17
- // PRIVATE API
18
- export async function verifyPassword(
19
- hashedPassword: string,
20
- password: string
21
- ): Promise<void> {
22
- const validPassword = await verify(
23
- hashedPassword,
24
- normalizePassword(password),
25
- hashingOptions
26
- );
27
- if (!validPassword) {
28
- throw new Error("Invalid password");
29
- }
30
- }
31
-
32
- // We are normalising the password to ensure that the password is always hashed in the same way
33
- // We have the same normalising process as oslo/password did in the past
34
- function normalizePassword(password: string): string {
35
- return password.normalize("NFKC");
36
- }
1
+ export { hashPassword, verifyPassword } from "@wasp.sh/lib-auth/node";
@@ -45,6 +45,7 @@ export type PossibleProviderData = {
45
45
  google: OAuthProviderData;
46
46
  keycloak: OAuthProviderData;
47
47
  github: OAuthProviderData;
48
+ microsoft: OAuthProviderData;
48
49
  }
49
50
 
50
51
  // PUBLIC API
@@ -97,6 +98,7 @@ export function normalizeProviderUserId(providerName: ProviderName, providerUser
97
98
  case 'discord':
98
99
  case 'keycloak':
99
100
  case 'slack':
101
+ case 'microsoft':
100
102
  return providerUserId;
101
103
  /*
102
104
  Why the default case?
@@ -1,4 +1,4 @@
1
- import { useRouteError } from 'react-router-dom'
1
+ import { useRouteError } from 'react-router'
2
2
 
3
3
  import { FullPageWrapper } from './FullPageWrapper'
4
4
 
@@ -0,0 +1,45 @@
1
+ {{={= =}=}}
2
+ import * as React from 'react'
3
+ import { QueryClientProvider } from '@tanstack/react-query'
4
+
5
+ import { getRouter } from '../router/router'
6
+ import { queryClientInitialized } from '../../operations/index'
7
+
8
+ {=# areWebSocketsUsed =}
9
+ import { WebSocketProvider } from '../../webSocket/WebSocketProvider'
10
+ {=/ areWebSocketsUsed =}
11
+
12
+ export type WaspAppProps = {
13
+ rootElement?: React.ReactNode;
14
+ routesMapping: Record<string, React.ComponentType>;
15
+ }
16
+
17
+ export function WaspApp({ rootElement, routesMapping }: Required<WaspAppProps>) {
18
+ const [queryClient, setQueryClient] = React.useState<any>(null)
19
+
20
+ React.useEffect(() => {
21
+ queryClientInitialized.then(setQueryClient)
22
+ }, [])
23
+
24
+ if (!queryClient) {
25
+ return null
26
+ }
27
+
28
+ const router = getRouter({
29
+ rootElement,
30
+ routesMapping,
31
+ })
32
+
33
+ return (
34
+ <QueryClientProvider client={queryClient}>
35
+ {=# areWebSocketsUsed =}
36
+ <WebSocketProvider>
37
+ {router}
38
+ </WebSocketProvider>
39
+ {=/ areWebSocketsUsed =}
40
+ {=^ areWebSocketsUsed =}
41
+ {router}
42
+ {=/ areWebSocketsUsed =}
43
+ </QueryClientProvider>
44
+ )
45
+ }
@@ -0,0 +1,26 @@
1
+ {{={= =}=}}
2
+ import { Outlet } from 'react-router'
3
+ import { initializeQueryClient } from '../operations'
4
+ import { WaspApp, type WaspAppProps } from './components/WaspApp'
5
+
6
+ const DefaultRootComponent = () => <Outlet />
7
+
8
+ let isAppInitialized = false
9
+
10
+ // PRIVATE API (web-app)
11
+ export function getWaspApp({
12
+ rootElement = <DefaultRootComponent />,
13
+ routesMapping,
14
+ }: WaspAppProps): React.ReactNode {
15
+ if (!isAppInitialized) {
16
+ initializeQueryClient()
17
+ isAppInitialized = true
18
+ }
19
+
20
+ return <WaspApp rootElement={rootElement} routesMapping={routesMapping} />
21
+ }
22
+
23
+ {=# isAuthEnabled =}
24
+ // PRIVATE API (web-app)
25
+ export { createAuthRequiredPage } from './pages/createAuthRequiredPage'
26
+ {=/ isAuthEnabled =}
@@ -1,13 +1,13 @@
1
1
  {{={= =}=}}
2
2
  import { useState } from "react";
3
3
  import { type AxiosResponse } from "axios";
4
- import { Navigate, useLocation } from 'react-router-dom'
5
- import { useAuth } from 'wasp/client/auth'
6
- import { api } from 'wasp/client/api'
7
- import { initSession } from 'wasp/auth/helpers/user'
8
- import { useEffectOnce } from 'wasp/client/hooks'
9
- import { MessageLoading, MessageError } from "../../components/Message";
10
- import { FullPageWrapper } from "../../components/FullPageWrapper";
4
+ import { Navigate, useLocation } from 'react-router'
5
+ import { useAuth } from "../../auth";
6
+ import { api } from "../../../api";
7
+ import { initSession } from "../../../auth/helpers/user";
8
+ import { useEffectOnce } from "../../hooks";
9
+ import { MessageLoading, MessageError } from "../components/Message";
10
+ import { FullPageWrapper } from "../components/FullPageWrapper";
11
11
 
12
12
  const oAuthCallbackWrapperClassName = "wasp-oauth-callback-wrapper";
13
13
 
@@ -89,5 +89,6 @@ async function exchangeOAuthCodeForToken(data: {
89
89
  function isResponseWithSessionId(
90
90
  response: AxiosResponse<unknown>
91
91
  ): response is AxiosResponse<{ sessionId: string }> {
92
- return response.data && typeof (response.data as any).sessionId === 'string'
92
+ const data = response.data as any;
93
+ return !!data && typeof data.sessionId === 'string'
93
94
  }
@@ -1,14 +1,14 @@
1
1
  {{={= =}=}}
2
- import React from 'react'
2
+ import * as React from 'react'
3
3
 
4
- import { Navigate } from 'react-router-dom'
5
- import { useAuth } from 'wasp/client/auth'
4
+ import { Navigate } from 'react-router'
5
+ import { useAuth } from '../../auth'
6
6
 
7
- import { Loader } from '../../components/Loader'
8
- import { MessageError } from '../../components/Message'
9
- import { FullPageWrapper } from '../../components/FullPageWrapper'
7
+ import { Loader } from '../components/Loader'
8
+ import { MessageError } from '../components/Message'
9
+ import { FullPageWrapper } from '../components/FullPageWrapper'
10
10
 
11
- const createAuthRequiredPage = (Page) => {
11
+ export const createAuthRequiredPage = (Page) => {
12
12
  return (props) => {
13
13
  const { data: user, status, error } = useAuth()
14
14
 
@@ -36,5 +36,3 @@ const createAuthRequiredPage = (Page) => {
36
36
  }
37
37
  }
38
38
  }
39
-
40
- export default createAuthRequiredPage
@@ -0,0 +1,47 @@
1
+ {{={= =}=}}
2
+ import * as React from 'react'
3
+ import { createBrowserRouter, RouterProvider } from 'react-router'
4
+
5
+ {=# isExternalAuthEnabled =}
6
+ import { OAuthCallbackPage } from "../pages/OAuthCallback"
7
+ {=/ isExternalAuthEnabled =}
8
+
9
+ import { DefaultRootErrorBoundary } from '../components/DefaultRootErrorBoundary'
10
+
11
+ import { routes } from '../../router/index'
12
+
13
+ export function getRouter({
14
+ routesMapping,
15
+ rootElement,
16
+ }: {
17
+ routesMapping: Record<string, React.ComponentType>,
18
+ rootElement: React.ReactNode,
19
+ }) {
20
+ const waspDefinedRoutes = [
21
+ {=# isExternalAuthEnabled =}
22
+ {
23
+ path: "{= oAuthCallbackPath =}",
24
+ Component: OAuthCallbackPage,
25
+ },
26
+ {=/ isExternalAuthEnabled =}
27
+ ]
28
+ const userDefinedRoutes = Object.entries(routes).map(([routeKey, route]) => {
29
+ return {
30
+ path: route.to,
31
+ Component: routesMapping[routeKey],
32
+ }
33
+ })
34
+
35
+ const browserRouter = createBrowserRouter([{
36
+ path: '/',
37
+ element: rootElement,
38
+ ErrorBoundary: DefaultRootErrorBoundary,
39
+ children: [
40
+ ...waspDefinedRoutes,
41
+ ...userDefinedRoutes,
42
+ ],
43
+ }], {
44
+ basename: '{= baseDir =}',
45
+ })
46
+ return <RouterProvider router={browserRouter} />;
47
+ }
@@ -21,6 +21,9 @@ export * from './keycloak'
21
21
  {=# isGitHubAuthEnabled =}
22
22
  export * from './github'
23
23
  {=/ isGitHubAuthEnabled =}
24
+ {=# isMicrosoftAuthEnabled =}
25
+ export * from './microsoft'
26
+ {=/ isMicrosoftAuthEnabled =}
24
27
  export {
25
28
  default as useAuth,
26
29
  getMe,
@@ -0,0 +1,2 @@
1
+ // PUBLIC API
2
+ export { signInUrl as microsoftSignInUrl } from '../../auth/helpers/Microsoft'
@@ -23,6 +23,9 @@ export { SignInButton as KeycloakSignInButton } from '../../auth/helpers/Keycloa
23
23
  {=# isGitHubAuthEnabled =}
24
24
  export { SignInButton as GitHubSignInButton } from '../../auth/helpers/GitHub'
25
25
  {=/ isGitHubAuthEnabled =}
26
+ {=# isMicrosoftAuthEnabled =}
27
+ export { SignInButton as MicrosoftSignInButton } from '../../auth/helpers/Microsoft'
28
+ {=/ isMicrosoftAuthEnabled =}
26
29
  export {
27
30
  FormError,
28
31
  FormInput,
@@ -1,5 +1,5 @@
1
1
  import { useMemo } from 'react'
2
- import { Link as RouterLink } from 'react-router-dom'
2
+ import { Link as RouterLink } from 'react-router'
3
3
  import { interpolatePath } from './linkHelpers'
4
4
  import { type Routes } from './index'
5
5
 
@@ -16,6 +16,6 @@ export function Link(
16
16
  ): React.JSX.Element {
17
17
  const toPropWithParams = useMemo(() => {
18
18
  return interpolatePath(to, params, search, hash)
19
- }, [to, params])
19
+ }, [to, params, search, hash])
20
20
  return <RouterLink to={toPropWithParams} {...restOfProps} />
21
21
  }
@@ -1,7 +1,7 @@
1
1
  import { ReactElement, ReactNode } from 'react'
2
- import { rest, type ResponseResolver, type RestContext } from 'msw'
2
+ import { http, type HttpResponseResolver, type RequestHandler } from 'msw'
3
3
  import { setupServer, type SetupServer } from 'msw/node'
4
- import { BrowserRouter as Router } from 'react-router-dom'
4
+ import { BrowserRouter as Router } from 'react-router'
5
5
  import { render, RenderResult, cleanup } from '@testing-library/react'
6
6
  import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
7
7
  import { beforeAll, afterEach, afterAll } from 'vitest'
@@ -57,13 +57,11 @@ export function mockServer(): {
57
57
 
58
58
  const mockQuery: MockQuery = (query, mockData) => {
59
59
  const route = (query as unknown as { route: Route }).route
60
- mockRoute(server, route, (_req, res, ctx) =>
61
- res(ctx.json(serialize(mockData)))
62
- )
60
+ mockRoute(server, route, () => Response.json(serialize(mockData)))
63
61
  }
64
62
 
65
63
  const mockApi: MockApi = (route, mockData) => {
66
- mockRoute(server, route, (_req, res, ctx) => res(ctx.json(mockData)))
64
+ mockRoute(server, route, () => Response.json(mockData))
67
65
  }
68
66
 
69
67
  return { server, mockQuery, mockApi }
@@ -72,7 +70,7 @@ export function mockServer(): {
72
70
  function mockRoute(
73
71
  server: SetupServer,
74
72
  route: Route,
75
- responseHandler: ResponseResolver<any, RestContext, any>
73
+ responseHandler: HttpResponseResolver
76
74
  ) {
77
75
  if (!Object.values(HttpMethod).includes(route.method)) {
78
76
  throw new Error(
@@ -84,11 +82,11 @@ function mockRoute(
84
82
 
85
83
  const url = `${config.apiUrl}${route.path}`
86
84
 
87
- const handlers: Record<HttpMethod, Parameters<typeof server.use>[0]> = {
88
- [HttpMethod.Get]: rest.get(url, responseHandler),
89
- [HttpMethod.Post]: rest.post(url, responseHandler),
90
- [HttpMethod.Put]: rest.put(url, responseHandler),
91
- [HttpMethod.Delete]: rest.delete(url, responseHandler),
85
+ const handlers: Record<HttpMethod, RequestHandler> = {
86
+ [HttpMethod.Get]: http.get(url, responseHandler),
87
+ [HttpMethod.Post]: http.post(url, responseHandler),
88
+ [HttpMethod.Put]: http.put(url, responseHandler),
89
+ [HttpMethod.Delete]: http.delete(url, responseHandler),
92
90
  }
93
91
 
94
92
  server.use(handlers[route.method])
@@ -0,0 +1 @@
1
+ export { type WaspPluginOptions, wasp } from "./plugins/wasp.js";
@@ -0,0 +1,50 @@
1
+ {{={= =}=}}
2
+ import { type Plugin } from 'vite'
3
+ import path from 'path'
4
+
5
+ export function detectServerImports(): Plugin {
6
+ let parsePathToUserCode!: ParsePathToUserCodeFn
7
+ return {
8
+ name: 'wasp:detect-server-imports',
9
+ enforce: 'pre',
10
+ configResolved(config) {
11
+ parsePathToUserCode = createPathToUserCodeParser(config.root)
12
+ },
13
+ resolveId(source, importer) {
14
+ if (!importer) {
15
+ return
16
+ }
17
+
18
+ const pathToUserCode = parsePathToUserCode(importer)
19
+ if (!pathToUserCode) {
20
+ return
21
+ }
22
+
23
+ if (isServerImport(source)) {
24
+ throw new Error(
25
+ `Server code cannot be imported in the client code. Import from "${source}" in "${pathToUserCode}" is not allowed.`
26
+ )
27
+ }
28
+ },
29
+ }
30
+ }
31
+
32
+ function isServerImport(moduleName: string): boolean {
33
+ return moduleName.startsWith('wasp/server')
34
+ }
35
+
36
+ type RelativePathToUserCode = string & { _brand: 'relativePathToUserCode' }
37
+
38
+ type ParsePathToUserCodeFn = (importerPath: string) => RelativePathToUserCode | null;
39
+
40
+ function createPathToUserCodeParser(waspProjectDirPath: string): ParsePathToUserCodeFn {
41
+ return (importerPath: string): RelativePathToUserCode | null => {
42
+ const importerPathRelativeToWaspProjectDir = path.relative(
43
+ waspProjectDirPath,
44
+ importerPath
45
+ )
46
+ return importerPathRelativeToWaspProjectDir.startsWith('{= srcDirInWaspProjectDir =}')
47
+ ? (importerPathRelativeToWaspProjectDir as RelativePathToUserCode)
48
+ : null
49
+ }
50
+ }