@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
@@ -52,6 +52,9 @@ export type AuthUserData = Omit<CompleteUserEntityWithAuth, '{= authFieldOnUserE
52
52
  {=# enabledProviders.isGitHubAuthEnabled =}
53
53
  github: Expand<UserFacingProviderData<'github'>> | null
54
54
  {=/ enabledProviders.isGitHubAuthEnabled =}
55
+ {=# enabledProviders.isMicrosoftAuthEnabled =}
56
+ microsoft: Expand<UserFacingProviderData<'microsoft'>> | null
57
+ {=/ enabledProviders.isMicrosoftAuthEnabled =}
55
58
  },
56
59
  }
57
60
 
@@ -117,6 +120,9 @@ This should never happen, but it did which means there is a bug in the code.`)
117
120
  {=# enabledProviders.isGitHubAuthEnabled =}
118
121
  github: getProviderInfo<'github'>({= authFieldOnUserEntityName =}, 'github'),
119
122
  {=/ enabledProviders.isGitHubAuthEnabled =}
123
+ {=# enabledProviders.isMicrosoftAuthEnabled =}
124
+ microsoft: getProviderInfo<'microsoft'>({= authFieldOnUserEntityName =}, 'microsoft'),
125
+ {=/ enabledProviders.isMicrosoftAuthEnabled =}
120
126
  }
121
127
  return {
122
128
  ...rest,
@@ -102,6 +102,17 @@ const waspServerCommonSchema = z.object({
102
102
  message: 'KEYCLOAK_REALM_URL must be a valid URL',
103
103
  }),
104
104
  {=/ enabledAuthProviders.isKeycloakAuthEnabled =}
105
+ {=# enabledAuthProviders.isMicrosoftAuthEnabled =}
106
+ MICROSOFT_TENANT_ID: z.string({
107
+ required_error: getRequiredEnvVarErrorMessage('Microsoft auth provider', 'MICROSOFT_TENANT_ID'),
108
+ }),
109
+ MICROSOFT_CLIENT_ID: z.string({
110
+ required_error: getRequiredEnvVarErrorMessage('Microsoft auth provider', 'MICROSOFT_CLIENT_ID'),
111
+ }),
112
+ MICROSOFT_CLIENT_SECRET: z.string({
113
+ required_error: getRequiredEnvVarErrorMessage('Microsoft auth provider', 'MICROSOFT_CLIENT_SECRET'),
114
+ }),
115
+ {=/ enabledAuthProviders.isMicrosoftAuthEnabled =}
105
116
  {=/ isAuthEnabled =}
106
117
  })
107
118
 
@@ -89,6 +89,10 @@
89
89
  ],
90
90
  "exclude": [
91
91
  "node_modules",
92
- "dist"
92
+ "dist",
93
+ // Virtual modules don't belong in the SDK runtime, they are used by the
94
+ // web-app runtime via Vite virtual modules. That's why we don't want the
95
+ // SDK to compile them.
96
+ "client/vite/virtual-files/files",
93
97
  ]
94
98
  }
@@ -0,0 +1,65 @@
1
+ {{={= =}=}}
2
+ import type { ProviderConfig } from "wasp/auth/providers/types";
3
+ import { microsoft } from "wasp/server/auth";
4
+
5
+ import { mergeDefaultAndUserConfig } from "../oauth/config.js";
6
+ import { createOAuthProviderRouter } from "../oauth/handler.js";
7
+
8
+ {=# userSignupFields.isDefined =}
9
+ {=& userSignupFields.importStatement =}
10
+ const _waspUserSignupFields = {= userSignupFields.importIdentifier =}
11
+ {=/ userSignupFields.isDefined =}
12
+ {=^ userSignupFields.isDefined =}
13
+ const _waspUserSignupFields = undefined
14
+ {=/ userSignupFields.isDefined =}
15
+ {=# configFn.isDefined =}
16
+ {=& configFn.importStatement =}
17
+ const _waspUserDefinedConfigFn = {= configFn.importIdentifier =}
18
+ {=/ configFn.isDefined =}
19
+ {=^ configFn.isDefined =}
20
+ const _waspUserDefinedConfigFn = undefined
21
+ {=/ configFn.isDefined =}
22
+
23
+ const _waspConfig: ProviderConfig = {
24
+ id: microsoft.id,
25
+ displayName: microsoft.displayName,
26
+ createRouter(provider) {
27
+ const config = mergeDefaultAndUserConfig({
28
+ scopes: {=& requiredScopes =},
29
+ }, _waspUserDefinedConfigFn);
30
+
31
+ async function getMicrosoftProfile(accessToken: string): Promise<{
32
+ providerProfile: unknown;
33
+ providerUserId: string;
34
+ }> {
35
+ const response = await fetch(
36
+ "https://graph.microsoft.com/oidc/userinfo",
37
+ {
38
+ headers: {
39
+ Authorization: `Bearer ${accessToken}`,
40
+ },
41
+ }
42
+ );
43
+ const providerProfile = (await response.json()) as {
44
+ sub?: string;
45
+ };
46
+
47
+ if (!providerProfile.sub) {
48
+ throw new Error("Invalid profile");
49
+ }
50
+
51
+ return { providerProfile, providerUserId: providerProfile.sub };
52
+ }
53
+
54
+ return createOAuthProviderRouter({
55
+ provider,
56
+ oAuthType: 'OAuth2WithPKCE',
57
+ userSignupFields: _waspUserSignupFields,
58
+ getAuthorizationUrl: ({ state, codeVerifier }) => microsoft.oAuthClient.createAuthorizationURL(state, codeVerifier, config),
59
+ getProviderTokens: ({ code, codeVerifier }) => microsoft.oAuthClient.validateAuthorizationCode(code, codeVerifier),
60
+ getProviderInfo: ({ accessToken }) => getMicrosoftProfile(accessToken),
61
+ });
62
+ },
63
+ }
64
+
65
+ export default _waspConfig;
@@ -2,7 +2,7 @@ import {
2
2
  Request as ExpressRequest,
3
3
  Response as ExpressResponse,
4
4
  } from 'express';
5
- import { parseCookies } from 'oslo/cookie';
5
+ import { parseCookies } from '@wasp.sh/lib-auth/node';
6
6
 
7
7
  import type { ProviderConfig } from 'wasp/auth/providers/types';
8
8
  import { config } from 'wasp/server';
@@ -1,15 +1,14 @@
1
1
  import { waspSays } from "./terminal.js";
2
2
  import { getClientBuildArtefactsDir, getClientBuildDir, } from "./waspProject.js";
3
3
  import { createCommandWithCwd } from "./zx.js";
4
- export const serverUrlEnvVarName = "REACT_APP_API_URL";
5
- export async function buildClient(defaultServerAppUrl, { waspProjectDir }) {
4
+ const serverUrlEnvVarName = "REACT_APP_API_URL";
5
+ export async function buildClient(serverUrl, { waspProjectDir }) {
6
6
  waspSays("Building web client for production...");
7
- waspSays(`If you configured a custom domain for the server, you should run the command with an env variable: ${serverUrlEnvVarName}=https://serverUrl.com <command>`);
8
7
  const clientBuildDir = getClientBuildDir(waspProjectDir);
9
8
  const npmCli = createCommandWithCwd("npm", clientBuildDir);
9
+ const npxCli = createCommandWithCwd("npx", clientBuildDir);
10
10
  await npmCli(["install"]);
11
- const serverUrl = getServerAppUrlEnvValue() ?? defaultServerAppUrl;
12
- await npmCli(["run", "build"], {
11
+ await npxCli(["vite", "build"], {
13
12
  env: {
14
13
  ...process.env,
15
14
  [serverUrlEnvVarName]: serverUrl,
@@ -17,10 +16,4 @@ export async function buildClient(defaultServerAppUrl, { waspProjectDir }) {
17
16
  });
18
17
  return getClientBuildArtefactsDir(waspProjectDir);
19
18
  }
20
- // We allow users to specify the server URL that the client will connect to
21
- // using an environment variable.
22
- // TODO: improve this API to allow specifying the server URL via options or a config file.
23
- function getServerAppUrlEnvValue() {
24
- return process.env[serverUrlEnvVarName];
25
- }
26
19
  //# sourceMappingURL=clientApp.js.map
@@ -6,6 +6,13 @@ export function waspSays(str) {
6
6
  .join("\n");
7
7
  console.log(chalk.yellow(formattedStr));
8
8
  }
9
+ export function waspInfo(str) {
10
+ const formattedStr = str
11
+ .split("\n")
12
+ .map((line) => `👉 ${line}`)
13
+ .join("\n");
14
+ console.log(chalk.blue(formattedStr));
15
+ }
9
16
  export function displayWaspRocketImage() {
10
17
  // Escaping backslashes makes it look weird here, but it works in console.
11
18
  const asciiArt = `
@@ -24,20 +24,24 @@ export function buildDirExists(waspProjectDir) {
24
24
  return fs.existsSync(getWaspBuildDir(waspProjectDir));
25
25
  }
26
26
  export function getServerBuildArtefactsDir(waspProjectDir) {
27
- return getServerBuildDir(waspProjectDir);
27
+ return getServerDeploymentDir(waspProjectDir);
28
28
  }
29
29
  export function getClientBuildArtefactsDir(waspProjectDir) {
30
- const clientBuildDir = getClientBuildDir(waspProjectDir);
31
- return path.join(clientBuildDir, "build");
30
+ return path.join(getClientDeploymentDir(waspProjectDir), "build");
32
31
  }
33
32
  function getWaspBuildDir(waspProjectDir) {
34
- return path.join(waspProjectDir, ".wasp", "build");
33
+ return path.join(waspProjectDir, ".wasp", "out");
35
34
  }
36
- export function getServerBuildDir(waspProjectDir) {
37
- // The server is built from the Wasp build directory.
35
+ export function getServerDeploymentDir(waspProjectDir) {
36
+ // The server is built from the Wasp out directory.
38
37
  return path.join(getWaspBuildDir(waspProjectDir), ".");
39
38
  }
40
- export function getClientBuildDir(waspProjectDir) {
39
+ export function getClientDeploymentDir(waspProjectDir) {
40
+ // The client is deployed from the `.out/web-app` dir.
41
41
  return path.join(getWaspBuildDir(waspProjectDir), "web-app");
42
42
  }
43
+ export function getClientBuildDir(waspProjectDir) {
44
+ // The client is built from the project root dir.
45
+ return path.join(waspProjectDir, ".");
46
+ }
43
47
  //# sourceMappingURL=waspProject.js.map
@@ -1,5 +1,5 @@
1
1
  import { cd } from "zx";
2
- import { getClientBuildDir, getServerBuildDir, } from "../../common/waspProject.js";
2
+ import { getClientDeploymentDir, getServerDeploymentDir, } from "../../common/waspProject.js";
3
3
  import { clientTomlExistsInProject, copyLocalClientTomlToProject, copyLocalServerTomlToProject, copyProjectClientTomlLocally, copyProjectServerTomlLocally, serverTomlExistsInProject, } from "./tomlFile.js";
4
4
  export var ContextOption;
5
5
  (function (ContextOption) {
@@ -17,7 +17,7 @@ function createClientCommonOps(waspProjectDir, paths) {
17
17
  return {
18
18
  waspProjectDir,
19
19
  paths,
20
- cdToBuildDir: () => cd(getClientBuildDir(waspProjectDir)),
20
+ cdToDeploymentDir: () => cd(getClientDeploymentDir(waspProjectDir)),
21
21
  tomlExistsInProject: () => clientTomlExistsInProject(paths),
22
22
  copyLocalTomlToProject: () => copyLocalClientTomlToProject(paths),
23
23
  copyProjectTomlLocally: () => copyProjectClientTomlLocally(paths),
@@ -27,7 +27,7 @@ function createServerCommonOps(waspProjectDir, paths) {
27
27
  return {
28
28
  waspProjectDir,
29
29
  paths,
30
- cdToBuildDir: () => cd(getServerBuildDir(waspProjectDir)),
30
+ cdToDeploymentDir: () => cd(getServerDeploymentDir(waspProjectDir)),
31
31
  tomlExistsInProject: () => serverTomlExistsInProject(paths),
32
32
  copyLocalTomlToProject: () => copyLocalServerTomlToProject(paths),
33
33
  copyProjectTomlLocally: () => copyProjectServerTomlLocally(paths),
@@ -16,7 +16,7 @@ export async function cmd(flyctlArgs, cmdOptions) {
16
16
  await runFlyctlCommand(commonOps, flyctlArgs);
17
17
  }
18
18
  async function runFlyctlCommand(commonOps, flyctlArgs) {
19
- commonOps.cdToBuildDir();
19
+ commonOps.cdToDeploymentDir();
20
20
  deleteLocalToml();
21
21
  if (commonOps.tomlExistsInProject()) {
22
22
  commonOps.copyProjectTomlLocally();
@@ -3,7 +3,7 @@ import { buildClient } from "../../../../common/clientApp.js";
3
3
  import { getFullCommandName } from "../../../../common/commander.js";
4
4
  import { displayWaspRocketImage, waspSays, } from "../../../../common/terminal.js";
5
5
  import { ensureWaspProjectIsBuilt } from "../../../../common/waspBuild.js";
6
- import { getClientBuildDir, getServerBuildDir, } from "../../../../common/waspProject.js";
6
+ import { getClientDeploymentDir, getServerDeploymentDir, } from "../../../../common/waspProject.js";
7
7
  import { createDeploymentInstructions, } from "../../DeploymentInstructions.js";
8
8
  import { getFlyAppUrl } from "../../flyAppUrl.js";
9
9
  import { secretExists } from "../../flyCli.js";
@@ -48,7 +48,7 @@ export async function deploy(cmdOptions) {
48
48
  }
49
49
  async function deployServer(deploymentInstructions, { buildLocally }) {
50
50
  waspSays("Deploying your server now...");
51
- cd(getServerBuildDir(deploymentInstructions.cmdOptions.waspProjectDir));
51
+ cd(getServerDeploymentDir(deploymentInstructions.cmdOptions.waspProjectDir));
52
52
  copyProjectServerTomlLocally(deploymentInstructions.tomlFilePaths);
53
53
  // Make sure we have a DATABASE_URL present. If not, they need to create/attach their DB first.
54
54
  const databaseUrlSet = await secretExists("DATABASE_URL");
@@ -65,10 +65,11 @@ async function deployServer(deploymentInstructions, { buildLocally }) {
65
65
  }
66
66
  async function deployClient(deploymentInstructions, { buildLocally }) {
67
67
  waspSays("Deploying your client now...");
68
- cd(getClientBuildDir(deploymentInstructions.cmdOptions.waspProjectDir));
68
+ cd(getClientDeploymentDir(deploymentInstructions.cmdOptions.waspProjectDir));
69
69
  copyProjectClientTomlLocally(deploymentInstructions.tomlFilePaths);
70
- const serverFlyAppUrl = getFlyAppUrl(deploymentInstructions.serverFlyAppName);
71
- await buildClient(serverFlyAppUrl, deploymentInstructions.cmdOptions);
70
+ const serverUrl = deploymentInstructions.cmdOptions.customServerUrl ??
71
+ getFlyAppUrl(deploymentInstructions.serverFlyAppName);
72
+ await buildClient(serverUrl, deploymentInstructions.cmdOptions);
72
73
  // Creates the necessary Dockerfile for deploying static websites to Fly.io.
73
74
  // Adds dummy .dockerignore to supress CLI question.
74
75
  // Ref: https://fly.io/docs/languages-and-frameworks/static/
@@ -3,7 +3,7 @@ import { getFullCommandName } from "../../../../common/commander.js";
3
3
  import { generateRandomHexString } from "../../../../common/random.js";
4
4
  import { waspSays } from "../../../../common/terminal.js";
5
5
  import { ensureWaspProjectIsBuilt } from "../../../../common/waspBuild.js";
6
- import { getClientBuildDir, getServerBuildDir, } from "../../../../common/waspProject.js";
6
+ import { getClientDeploymentDir, getServerDeploymentDir, } from "../../../../common/waspProject.js";
7
7
  import { createDeploymentInstructions, } from "../../DeploymentInstructions.js";
8
8
  import { getFlyAppUrl } from "../../flyAppUrl.js";
9
9
  import { createFlyDbCommand } from "../../index.js";
@@ -37,7 +37,7 @@ export async function setup(baseName, region, cmdOptions) {
37
37
  }
38
38
  async function setupServer(deploymentInstructions) {
39
39
  waspSays(`Setting up server app with name ${deploymentInstructions.serverFlyAppName}`);
40
- cd(getServerBuildDir(deploymentInstructions.cmdOptions.waspProjectDir));
40
+ cd(getServerDeploymentDir(deploymentInstructions.cmdOptions.waspProjectDir));
41
41
  deleteLocalToml();
42
42
  const launchArgs = [
43
43
  "--name",
@@ -104,7 +104,7 @@ Press any key to continue or Ctrl+C to cancel.`);
104
104
  }
105
105
  async function setupClient(deploymentInstructions) {
106
106
  waspSays(`Setting up client app with name ${deploymentInstructions.clientFlyAppName}`);
107
- cd(getClientBuildDir(deploymentInstructions.cmdOptions.waspProjectDir));
107
+ cd(getClientDeploymentDir(deploymentInstructions.cmdOptions.waspProjectDir));
108
108
  deleteLocalToml();
109
109
  const launchArgs = [
110
110
  "--name",
@@ -30,6 +30,9 @@ class FlyCommand extends Command {
30
30
  }
31
31
  return this.option("--server-secret <serverSecret>", "secret to set on the server app (of form FOO=BAR)", collect, []).option("--client-secret <clientSecret>", "secret to set on the client app (of form FOO=BAR)", collect, []);
32
32
  }
33
+ addCustomServerUrlOption() {
34
+ return this.option("--custom-server-url <url>", "URL of the server that the client will connect to");
35
+ }
33
36
  }
34
37
  const flyLaunchCommand = makeFlyLaunchCommand();
35
38
  export const flySetupCommand = makeFlySetupCommand();
@@ -83,6 +86,7 @@ function makeFlyLaunchCommand() {
83
86
  .addDbOptions()
84
87
  .addLocalBuildOption()
85
88
  .addSecretsOptions()
89
+ .addCustomServerUrlOption()
86
90
  .action(launchFn);
87
91
  }
88
92
  function makeFlySetupCommand() {
@@ -99,6 +103,7 @@ function makeFlyDeployCommand() {
99
103
  .option("--skip-client", "do not deploy the web client")
100
104
  .option("--skip-server", "do not deploy the server")
101
105
  .addLocalBuildOption()
106
+ .addCustomServerUrlOption()
102
107
  .action(deployFn);
103
108
  }
104
109
  function makeExecuteFlyCommand() {
@@ -5,7 +5,8 @@ import { generateServiceUrl } from "../../railwayService/url.js";
5
5
  import { deployServiceWithStreamingLogs, ServiceDeploymentStatus, } from "./common.js";
6
6
  export async function deployClient({ cmdOptions: options, serverServiceName, clientServiceName, }) {
7
7
  waspSays("Deploying your client now...");
8
- const serverServiceUrl = await generateServiceUrl(serverServiceName, serverAppPort, options);
8
+ const serverServiceUrl = options.customServerUrl ??
9
+ (await generateServiceUrl(serverServiceName, serverAppPort, options));
9
10
  const clientBuildArtefactsDir = await buildClient(serverServiceUrl, options);
10
11
  const deploymentStatus = await deployServiceWithStreamingLogs({
11
12
  name: clientServiceName,
@@ -2,7 +2,7 @@ import { $ } from "zx";
2
2
  import { generateRandomHexString } from "../../../../common/random.js";
3
3
  import { waspSays } from "../../../../common/terminal.js";
4
4
  import { ensureWaspProjectIsBuilt } from "../../../../common/waspBuild.js";
5
- import { getClientBuildDir, getServerBuildDir, } from "../../../../common/waspProject.js";
5
+ import { getClientDeploymentDir, getServerDeploymentDir, } from "../../../../common/waspProject.js";
6
6
  import { createCommandWithCwd } from "../../../../common/zx.js";
7
7
  import { createDeploymentInstructions, } from "../../DeploymentInstructions.js";
8
8
  import { clientAppPort, serverAppPort } from "../../ports.js";
@@ -104,12 +104,14 @@ async function setupServer({ cmdOptions: options, serverServiceName, clientServi
104
104
  // The client service needs a URL so it can be referenced in the
105
105
  // server service env variables.
106
106
  await generateServiceUrl(clientServiceName, clientAppPort, options);
107
- const serverBuildDir = getServerBuildDir(options.waspProjectDir);
108
- const railwayCli = createCommandWithCwd(options.railwayExe, serverBuildDir);
109
- const clientUrl = `https://${getRailwayEnvVarValueReference(`${clientServiceName}.RAILWAY_PUBLIC_DOMAIN`)}`;
107
+ const serverDeploymentDir = getServerDeploymentDir(options.waspProjectDir);
108
+ const railwayCli = createCommandWithCwd(options.railwayExe, serverDeploymentDir);
109
+ const clientUrl = `https://${getRailwayEnvVarValueReference("RAILWAY_PUBLIC_DOMAIN", { serviceName: clientServiceName })}`;
110
110
  // If we reference the service URL in its OWN env variables, we don't prefix it with the service name.
111
111
  const serverUrl = `https://${getRailwayEnvVarValueReference("RAILWAY_PUBLIC_DOMAIN")}`;
112
- const databaseUrl = getRailwayEnvVarValueReference(`${dbServiceName}.DATABASE_URL`);
112
+ const databaseUrl = getRailwayEnvVarValueReference("DATABASE_URL", {
113
+ serviceName: dbServiceName,
114
+ });
113
115
  const jwtSecret = generateRandomHexString();
114
116
  await railwayCli([
115
117
  "add",
@@ -128,10 +130,10 @@ async function setupServer({ cmdOptions: options, serverServiceName, clientServi
128
130
  }
129
131
  async function setupClient({ cmdOptions: options, clientServiceName, }) {
130
132
  waspSays(`Setting up client app with name ${clientServiceName}`);
131
- const clientBuildDir = getClientBuildDir(options.waspProjectDir);
132
- const railwayCli = createCommandWithCwd(options.railwayExe, clientBuildDir);
133
+ const clientDeploymentDir = getClientDeploymentDir(options.waspProjectDir);
134
+ const railwayCli = createCommandWithCwd(options.railwayExe, clientDeploymentDir);
133
135
  // Having a Staticfile tells Railway to use a static file server.
134
- await $({ cwd: clientBuildDir }) `touch Staticfile`;
136
+ await $({ cwd: clientDeploymentDir }) `touch Staticfile`;
135
137
  await railwayCli([
136
138
  "add",
137
139
  ["--service", clientServiceName],
@@ -140,7 +142,16 @@ async function setupClient({ cmdOptions: options, clientServiceName, }) {
140
142
  ].flat());
141
143
  waspSays("Client setup complete!");
142
144
  }
143
- function getRailwayEnvVarValueReference(name) {
144
- return "${{" + name + "}}";
145
+ function getRailwayEnvVarValueReference(name, { serviceName } = {}) {
146
+ // Railway variable references have the format ${{VARIABLE}} for local variables
147
+ // or ${{serviceName.VARIABLE}} for cross-service references.
148
+ // When the service name contains special characters (like hyphens with numbers),
149
+ // Railway requires it to be quoted: ${{"service-name".VARIABLE}}
150
+ const parts = [name];
151
+ if (serviceName) {
152
+ // JSON.stringify wraps the string in quotes and escapes any special characters.
153
+ parts.unshift(JSON.stringify(serviceName));
154
+ }
155
+ return "${{" + parts.join(".") + "}}";
145
156
  }
146
157
  //# sourceMappingURL=setup.js.map
@@ -14,6 +14,9 @@ class RailwayCommand extends Command {
14
14
  }
15
15
  return this.option("--server-secret <serverSecret>", "secret to set on the server app (of form FOO=BAR)", collect, []).option("--client-secret <clientSecret>", "secret to set on the client app (of form FOO=BAR)", collect, []);
16
16
  }
17
+ addCustomServerUrlOption() {
18
+ return this.option("--custom-server-url <url>", "URL of the server that the client will connect to");
19
+ }
17
20
  }
18
21
  export const railwaySetupCommand = makeRailwaySetupCommand();
19
22
  export const railwayDeployCommand = makeRailwayDeployCommand();
@@ -68,6 +71,7 @@ function makeRailwayDeployCommand() {
68
71
  .option("--skip-client", "do not deploy the web client")
69
72
  .option("--skip-server", "do not deploy the server")
70
73
  .option("--existing-project-id [projectId]", "use existing project for deployment")
74
+ .addCustomServerUrlOption()
71
75
  .action(deployFn);
72
76
  }
73
77
  function makeRailwayLaunchCommand() {
@@ -78,6 +82,7 @@ function makeRailwayLaunchCommand() {
78
82
  .option("--existing-project-id [projectId]", "use existing project instead of creating a new one")
79
83
  .option("--workspace [workspace]", "the Railway workspace to use if a new project needs to be created (if not provided, will ask interactively)")
80
84
  .option("--db-image <dbImage>", "custom Docker image for the PostgreSQL database")
85
+ .addCustomServerUrlOption()
81
86
  .action(launchFn);
82
87
  }
83
88
  //# sourceMappingURL=index.js.map
@@ -13,7 +13,13 @@ export const RailwayCliProjectSchema = z.object({
13
13
  }),
14
14
  });
15
15
  export const RailwayProjectListSchema = z.array(RailwayCliProjectSchema);
16
- export const RailwayCliDomainSchema = z.object({
17
- domain: z.string(),
18
- });
16
+ export const RailwayCliDomainSchema = z.union([
17
+ // Railway CLI >=4.18.1
18
+ z.object({ domains: z.array(z.string()).min(1) }),
19
+ // Railway CLI <4.18.1
20
+ z
21
+ .object({ domain: z.string() })
22
+ // Convert to the newer format
23
+ .transform(({ domain }) => ({ domains: [domain] })),
24
+ ]);
19
25
  //# sourceMappingURL=jsonOutputSchemas.js.map
@@ -1,3 +1,4 @@
1
+ import { waspInfo } from "../../../common/terminal.js";
1
2
  import { createCommandWithCwd } from "../../../common/zx.js";
2
3
  import { RailwayCliDomainSchema } from "../jsonOutputSchemas.js";
3
4
  export var ServiceUrlStatus;
@@ -19,7 +20,13 @@ export async function generateServiceUrl(serviceName, port, options) {
19
20
  return extractServiceUrlFromString(result.stdout);
20
21
  }
21
22
  else {
22
- return RailwayCliDomainSchema.parse(result.json()).domain;
23
+ const { domains } = RailwayCliDomainSchema.parse(result.json());
24
+ const domain = domains[0];
25
+ if (domains.length > 1) {
26
+ waspInfo(`Multiple domains detected, using the first one: ${domain}.`);
27
+ waspInfo('If you want to use a custom domain for the server, you should add the "--custom-server-url <url>" flag.');
28
+ }
29
+ return domain;
23
30
  }
24
31
  }
25
32
  function extractServiceUrlFromString(text) {