@robelest/convex-auth 0.0.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.
- package/README.md +6 -0
- package/dist/bin.cjs +27733 -0
- package/dist/client/index.d.ts +49 -0
- package/dist/client/index.d.ts.map +1 -0
- package/dist/client/index.js +283 -0
- package/dist/client/index.js.map +1 -0
- package/dist/component/_generated/api.d.ts +36 -0
- package/dist/component/_generated/api.d.ts.map +1 -0
- package/dist/component/_generated/api.js +31 -0
- package/dist/component/_generated/api.js.map +1 -0
- package/dist/component/_generated/component.d.ts +295 -0
- package/dist/component/_generated/component.d.ts.map +1 -0
- package/dist/component/_generated/component.js +11 -0
- package/dist/component/_generated/component.js.map +1 -0
- package/dist/component/_generated/dataModel.d.ts +46 -0
- package/dist/component/_generated/dataModel.d.ts.map +1 -0
- package/dist/component/_generated/dataModel.js +11 -0
- package/dist/component/_generated/dataModel.js.map +1 -0
- package/dist/component/_generated/server.d.ts +121 -0
- package/dist/component/_generated/server.d.ts.map +1 -0
- package/dist/component/_generated/server.js +78 -0
- package/dist/component/_generated/server.js.map +1 -0
- package/dist/component/convex.config.d.ts +3 -0
- package/dist/component/convex.config.d.ts.map +1 -0
- package/dist/component/convex.config.js +4 -0
- package/dist/component/convex.config.js.map +1 -0
- package/dist/component/index.d.ts +15 -0
- package/dist/component/index.d.ts.map +1 -0
- package/dist/component/index.js +13 -0
- package/dist/component/index.js.map +1 -0
- package/dist/component/public.d.ts +450 -0
- package/dist/component/public.d.ts.map +1 -0
- package/dist/component/public.js +528 -0
- package/dist/component/public.js.map +1 -0
- package/dist/component/schema.d.ts +107 -0
- package/dist/component/schema.d.ts.map +1 -0
- package/dist/component/schema.js +26 -0
- package/dist/component/schema.js.map +1 -0
- package/dist/providers/Anonymous.d.ts +50 -0
- package/dist/providers/Anonymous.d.ts.map +1 -0
- package/dist/providers/Anonymous.js +39 -0
- package/dist/providers/Anonymous.js.map +1 -0
- package/dist/providers/ConvexCredentials.d.ts +88 -0
- package/dist/providers/ConvexCredentials.d.ts.map +1 -0
- package/dist/providers/ConvexCredentials.js +37 -0
- package/dist/providers/ConvexCredentials.js.map +1 -0
- package/dist/providers/Email.d.ts +33 -0
- package/dist/providers/Email.d.ts.map +1 -0
- package/dist/providers/Email.js +50 -0
- package/dist/providers/Email.js.map +1 -0
- package/dist/providers/Password.d.ts +95 -0
- package/dist/providers/Password.d.ts.map +1 -0
- package/dist/providers/Password.js +174 -0
- package/dist/providers/Password.js.map +1 -0
- package/dist/providers/Phone.d.ts +22 -0
- package/dist/providers/Phone.d.ts.map +1 -0
- package/dist/providers/Phone.js +37 -0
- package/dist/providers/Phone.js.map +1 -0
- package/dist/server/convex_types.d.ts +17 -0
- package/dist/server/convex_types.d.ts.map +1 -0
- package/dist/server/convex_types.js +2 -0
- package/dist/server/convex_types.js.map +1 -0
- package/dist/server/cookies.d.ts +35 -0
- package/dist/server/cookies.d.ts.map +1 -0
- package/dist/server/cookies.js +34 -0
- package/dist/server/cookies.js.map +1 -0
- package/dist/server/implementation/db.d.ts +80 -0
- package/dist/server/implementation/db.d.ts.map +1 -0
- package/dist/server/implementation/db.js +59 -0
- package/dist/server/implementation/db.js.map +1 -0
- package/dist/server/implementation/index.d.ts +370 -0
- package/dist/server/implementation/index.d.ts.map +1 -0
- package/dist/server/implementation/index.js +521 -0
- package/dist/server/implementation/index.js.map +1 -0
- package/dist/server/implementation/mutations/createAccountFromCredentials.d.ts +33 -0
- package/dist/server/implementation/mutations/createAccountFromCredentials.d.ts.map +1 -0
- package/dist/server/implementation/mutations/createAccountFromCredentials.js +71 -0
- package/dist/server/implementation/mutations/createAccountFromCredentials.js.map +1 -0
- package/dist/server/implementation/mutations/createVerificationCode.d.ts +25 -0
- package/dist/server/implementation/mutations/createVerificationCode.d.ts.map +1 -0
- package/dist/server/implementation/mutations/createVerificationCode.js +84 -0
- package/dist/server/implementation/mutations/createVerificationCode.js.map +1 -0
- package/dist/server/implementation/mutations/index.d.ts +304 -0
- package/dist/server/implementation/mutations/index.d.ts.map +1 -0
- package/dist/server/implementation/mutations/index.js +108 -0
- package/dist/server/implementation/mutations/index.js.map +1 -0
- package/dist/server/implementation/mutations/invalidateSessions.d.ts +13 -0
- package/dist/server/implementation/mutations/invalidateSessions.d.ts.map +1 -0
- package/dist/server/implementation/mutations/invalidateSessions.js +35 -0
- package/dist/server/implementation/mutations/invalidateSessions.js.map +1 -0
- package/dist/server/implementation/mutations/modifyAccount.d.ts +23 -0
- package/dist/server/implementation/mutations/modifyAccount.d.ts.map +1 -0
- package/dist/server/implementation/mutations/modifyAccount.js +48 -0
- package/dist/server/implementation/mutations/modifyAccount.js.map +1 -0
- package/dist/server/implementation/mutations/refreshSession.d.ts +16 -0
- package/dist/server/implementation/mutations/refreshSession.d.ts.map +1 -0
- package/dist/server/implementation/mutations/refreshSession.js +116 -0
- package/dist/server/implementation/mutations/refreshSession.js.map +1 -0
- package/dist/server/implementation/mutations/retrieveAccountWithCredentials.d.ts +27 -0
- package/dist/server/implementation/mutations/retrieveAccountWithCredentials.d.ts.map +1 -0
- package/dist/server/implementation/mutations/retrieveAccountWithCredentials.js +55 -0
- package/dist/server/implementation/mutations/retrieveAccountWithCredentials.js.map +1 -0
- package/dist/server/implementation/mutations/signIn.d.ts +17 -0
- package/dist/server/implementation/mutations/signIn.d.ts.map +1 -0
- package/dist/server/implementation/mutations/signIn.js +26 -0
- package/dist/server/implementation/mutations/signIn.js.map +1 -0
- package/dist/server/implementation/mutations/signOut.d.ts +11 -0
- package/dist/server/implementation/mutations/signOut.d.ts.map +1 -0
- package/dist/server/implementation/mutations/signOut.js +24 -0
- package/dist/server/implementation/mutations/signOut.js.map +1 -0
- package/dist/server/implementation/mutations/userOAuth.d.ts +19 -0
- package/dist/server/implementation/mutations/userOAuth.d.ts.map +1 -0
- package/dist/server/implementation/mutations/userOAuth.js +84 -0
- package/dist/server/implementation/mutations/userOAuth.js.map +1 -0
- package/dist/server/implementation/mutations/verifier.d.ts +8 -0
- package/dist/server/implementation/mutations/verifier.d.ts.map +1 -0
- package/dist/server/implementation/mutations/verifier.js +19 -0
- package/dist/server/implementation/mutations/verifier.js.map +1 -0
- package/dist/server/implementation/mutations/verifierSignature.d.ts +15 -0
- package/dist/server/implementation/mutations/verifierSignature.d.ts.map +1 -0
- package/dist/server/implementation/mutations/verifierSignature.js +29 -0
- package/dist/server/implementation/mutations/verifierSignature.js.map +1 -0
- package/dist/server/implementation/mutations/verifyCodeAndSignIn.d.ts +21 -0
- package/dist/server/implementation/mutations/verifyCodeAndSignIn.d.ts.map +1 -0
- package/dist/server/implementation/mutations/verifyCodeAndSignIn.js +127 -0
- package/dist/server/implementation/mutations/verifyCodeAndSignIn.js.map +1 -0
- package/dist/server/implementation/provider.d.ts +6 -0
- package/dist/server/implementation/provider.d.ts.map +1 -0
- package/dist/server/implementation/provider.js +21 -0
- package/dist/server/implementation/provider.js.map +1 -0
- package/dist/server/implementation/rateLimit.d.ts +6 -0
- package/dist/server/implementation/rateLimit.d.ts.map +1 -0
- package/dist/server/implementation/rateLimit.js +76 -0
- package/dist/server/implementation/rateLimit.js.map +1 -0
- package/dist/server/implementation/redirects.d.ts +6 -0
- package/dist/server/implementation/redirects.d.ts.map +1 -0
- package/dist/server/implementation/redirects.js +40 -0
- package/dist/server/implementation/redirects.js.map +1 -0
- package/dist/server/implementation/refreshTokens.d.ts +40 -0
- package/dist/server/implementation/refreshTokens.d.ts.map +1 -0
- package/dist/server/implementation/refreshTokens.js +160 -0
- package/dist/server/implementation/refreshTokens.js.map +1 -0
- package/dist/server/implementation/sessions.d.ts +43 -0
- package/dist/server/implementation/sessions.d.ts.map +1 -0
- package/dist/server/implementation/sessions.js +94 -0
- package/dist/server/implementation/sessions.js.map +1 -0
- package/dist/server/implementation/signIn.d.ts +31 -0
- package/dist/server/implementation/signIn.d.ts.map +1 -0
- package/dist/server/implementation/signIn.js +148 -0
- package/dist/server/implementation/signIn.js.map +1 -0
- package/dist/server/implementation/tokens.d.ts +7 -0
- package/dist/server/implementation/tokens.d.ts.map +1 -0
- package/dist/server/implementation/tokens.js +18 -0
- package/dist/server/implementation/tokens.js.map +1 -0
- package/dist/server/implementation/types.d.ts +288 -0
- package/dist/server/implementation/types.d.ts.map +1 -0
- package/dist/server/implementation/types.js +182 -0
- package/dist/server/implementation/types.js.map +1 -0
- package/dist/server/implementation/users.d.ts +27 -0
- package/dist/server/implementation/users.d.ts.map +1 -0
- package/dist/server/implementation/users.js +181 -0
- package/dist/server/implementation/users.js.map +1 -0
- package/dist/server/implementation/utils.d.ts +17 -0
- package/dist/server/implementation/utils.d.ts.map +1 -0
- package/dist/server/implementation/utils.js +72 -0
- package/dist/server/implementation/utils.js.map +1 -0
- package/dist/server/index.d.ts +17 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/index.js +54 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server/oauth/authorizationUrl.d.ts +13 -0
- package/dist/server/oauth/authorizationUrl.d.ts.map +1 -0
- package/dist/server/oauth/authorizationUrl.js +91 -0
- package/dist/server/oauth/authorizationUrl.js.map +1 -0
- package/dist/server/oauth/callback.d.ts +19 -0
- package/dist/server/oauth/callback.d.ts.map +1 -0
- package/dist/server/oauth/callback.js +173 -0
- package/dist/server/oauth/callback.js.map +1 -0
- package/dist/server/oauth/checks.d.ts +52 -0
- package/dist/server/oauth/checks.d.ts.map +1 -0
- package/dist/server/oauth/checks.js +106 -0
- package/dist/server/oauth/checks.js.map +1 -0
- package/dist/server/oauth/convexAuth.d.ts +12 -0
- package/dist/server/oauth/convexAuth.d.ts.map +1 -0
- package/dist/server/oauth/convexAuth.js +137 -0
- package/dist/server/oauth/convexAuth.js.map +1 -0
- package/dist/server/oauth/lib/utils/customFetch.d.ts +9 -0
- package/dist/server/oauth/lib/utils/customFetch.d.ts.map +1 -0
- package/dist/server/oauth/lib/utils/customFetch.js +11 -0
- package/dist/server/oauth/lib/utils/customFetch.js.map +1 -0
- package/dist/server/oauth/lib/utils/providers.d.ts +3 -0
- package/dist/server/oauth/lib/utils/providers.d.ts.map +1 -0
- package/dist/server/oauth/lib/utils/providers.js +7 -0
- package/dist/server/oauth/lib/utils/providers.js.map +1 -0
- package/dist/server/oauth/providers/oauth.d.ts +43 -0
- package/dist/server/oauth/providers/oauth.d.ts.map +1 -0
- package/dist/server/oauth/providers/oauth.js +3 -0
- package/dist/server/oauth/providers/oauth.js.map +1 -0
- package/dist/server/oauth/types.d.ts +24 -0
- package/dist/server/oauth/types.d.ts.map +1 -0
- package/dist/server/oauth/types.js +5 -0
- package/dist/server/oauth/types.js.map +1 -0
- package/dist/server/provider_utils.d.ts +76 -0
- package/dist/server/provider_utils.d.ts.map +1 -0
- package/dist/server/provider_utils.js +177 -0
- package/dist/server/provider_utils.js.map +1 -0
- package/dist/server/types.d.ts +412 -0
- package/dist/server/types.d.ts.map +1 -0
- package/dist/server/types.js +2 -0
- package/dist/server/types.js.map +1 -0
- package/dist/server/utils.d.ts +3 -0
- package/dist/server/utils.d.ts.map +1 -0
- package/dist/server/utils.js +11 -0
- package/dist/server/utils.js.map +1 -0
- package/package.json +126 -0
- package/providers/Anonymous/package.json +6 -0
- package/providers/ConvexCredentials/package.json +6 -0
- package/providers/Email/package.json +6 -0
- package/providers/Password/package.json +6 -0
- package/providers/Phone/package.json +6 -0
- package/server/package.json +6 -0
- package/src/cli/command.ts +69 -0
- package/src/cli/generateKeys.ts +20 -0
- package/src/cli/index.ts +840 -0
- package/src/client/index.ts +415 -0
- package/src/component/_generated/api.ts +52 -0
- package/src/component/_generated/component.ts +586 -0
- package/src/component/_generated/dataModel.ts +60 -0
- package/src/component/_generated/server.ts +156 -0
- package/src/component/convex.config.ts +5 -0
- package/src/component/index.ts +40 -0
- package/src/component/public.ts +607 -0
- package/src/component/schema.ts +35 -0
- package/src/providers/Anonymous.ts +79 -0
- package/src/providers/ConvexCredentials.ts +108 -0
- package/src/providers/Email.ts +60 -0
- package/src/providers/Password.ts +253 -0
- package/src/providers/Phone.ts +46 -0
- package/src/server/convex_types.ts +55 -0
- package/src/server/cookies.ts +42 -0
- package/src/server/implementation/db.ts +125 -0
- package/src/server/implementation/index.ts +815 -0
- package/src/server/implementation/mutations/createAccountFromCredentials.ts +113 -0
- package/src/server/implementation/mutations/createVerificationCode.ts +139 -0
- package/src/server/implementation/mutations/index.ts +157 -0
- package/src/server/implementation/mutations/invalidateSessions.ts +47 -0
- package/src/server/implementation/mutations/modifyAccount.ts +65 -0
- package/src/server/implementation/mutations/refreshSession.ts +188 -0
- package/src/server/implementation/mutations/retrieveAccountWithCredentials.ts +87 -0
- package/src/server/implementation/mutations/signIn.ts +51 -0
- package/src/server/implementation/mutations/signOut.ts +38 -0
- package/src/server/implementation/mutations/userOAuth.ts +112 -0
- package/src/server/implementation/mutations/verifier.ts +29 -0
- package/src/server/implementation/mutations/verifierSignature.ts +44 -0
- package/src/server/implementation/mutations/verifyCodeAndSignIn.ts +205 -0
- package/src/server/implementation/provider.ts +38 -0
- package/src/server/implementation/rateLimit.ts +105 -0
- package/src/server/implementation/redirects.ts +58 -0
- package/src/server/implementation/refreshTokens.ts +221 -0
- package/src/server/implementation/sessions.ts +155 -0
- package/src/server/implementation/signIn.ts +253 -0
- package/src/server/implementation/tokens.ts +29 -0
- package/src/server/implementation/types.ts +220 -0
- package/src/server/implementation/users.ts +286 -0
- package/src/server/implementation/utils.ts +91 -0
- package/src/server/index.ts +74 -0
- package/src/server/oauth/NOTICE.txt +21 -0
- package/src/server/oauth/README.md +7 -0
- package/src/server/oauth/authorizationUrl.ts +113 -0
- package/src/server/oauth/callback.ts +243 -0
- package/src/server/oauth/checks.ts +136 -0
- package/src/server/oauth/convexAuth.ts +168 -0
- package/src/server/oauth/lib/utils/customFetch.ts +18 -0
- package/src/server/oauth/lib/utils/providers.ts +12 -0
- package/src/server/oauth/providers/oauth.ts +56 -0
- package/src/server/oauth/types.ts +60 -0
- package/src/server/provider_utils.ts +222 -0
- package/src/server/types.ts +470 -0
- package/src/server/utils.ts +12 -0
- package/src/test.ts +24 -0
package/src/cli/index.ts
ADDED
|
@@ -0,0 +1,840 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { Command } from "@commander-js/extra-typings";
|
|
4
|
+
import chalk from "chalk";
|
|
5
|
+
import { execSync } from "child_process";
|
|
6
|
+
import { config as loadEnvFile } from "dotenv";
|
|
7
|
+
import { existsSync, readFileSync, writeFileSync } from "fs";
|
|
8
|
+
import inquirer from "inquirer";
|
|
9
|
+
import path from "path";
|
|
10
|
+
import * as v from "valibot";
|
|
11
|
+
import { actionDescription } from "./command.js";
|
|
12
|
+
import { generateKeys } from "./generateKeys.js";
|
|
13
|
+
|
|
14
|
+
new Command()
|
|
15
|
+
.name("@robelest/convex-auth")
|
|
16
|
+
.description(
|
|
17
|
+
"Add code and set environment variables for @robelest/convex-auth.\n\n" +
|
|
18
|
+
"The steps are detailed here: https://labs.convex.dev/auth/setup/manual",
|
|
19
|
+
)
|
|
20
|
+
.option(
|
|
21
|
+
"--variables <json>",
|
|
22
|
+
"Configure additional variables for interactive configuration.",
|
|
23
|
+
)
|
|
24
|
+
.option("--skip-git-check", "Don't warn when running outside a Git checkout.")
|
|
25
|
+
.option("--allow-dirty-git-state", "Don't warn when Git state is not clean.")
|
|
26
|
+
.option(
|
|
27
|
+
"--web-server-url <url>",
|
|
28
|
+
"URL of web server, e.g. 'http://localhost:5173' if local",
|
|
29
|
+
)
|
|
30
|
+
.addDeploymentSelectionOptions(
|
|
31
|
+
actionDescription("Set environment variables on"),
|
|
32
|
+
)
|
|
33
|
+
.action(async (options) => {
|
|
34
|
+
await checkSourceControl(options);
|
|
35
|
+
|
|
36
|
+
const packageJson = readPackageJson();
|
|
37
|
+
const convexJson = readConvexJson();
|
|
38
|
+
const deployment = readConvexDeployment(options);
|
|
39
|
+
const convexFolderPath = convexJson.functions ?? "convex";
|
|
40
|
+
|
|
41
|
+
const isNextjs = !!packageJson.dependencies?.next;
|
|
42
|
+
const usesTypeScript = !!(
|
|
43
|
+
packageJson.dependencies?.typescript ||
|
|
44
|
+
packageJson.devDependencies?.typescript
|
|
45
|
+
);
|
|
46
|
+
const isVite = !!(
|
|
47
|
+
packageJson.dependencies?.vite || packageJson.devDependencies?.vite
|
|
48
|
+
);
|
|
49
|
+
const isExpo = !!(
|
|
50
|
+
packageJson.dependencies?.expo || packageJson.devDependencies?.expo
|
|
51
|
+
);
|
|
52
|
+
const config = {
|
|
53
|
+
isNextjs,
|
|
54
|
+
isVite,
|
|
55
|
+
isExpo,
|
|
56
|
+
usesTypeScript,
|
|
57
|
+
convexFolderPath,
|
|
58
|
+
deployment,
|
|
59
|
+
step: 1,
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
// Step 1: Configure SITE_URL
|
|
63
|
+
// We check for existing config.
|
|
64
|
+
// We default to localhost and port depending on framework
|
|
65
|
+
await configureSiteUrl(config, options.webServerUrl);
|
|
66
|
+
|
|
67
|
+
// Step 2: Configure private and public key
|
|
68
|
+
// We ask if we would overwrite existing keys
|
|
69
|
+
await configureKeys(config);
|
|
70
|
+
|
|
71
|
+
// Step 3: Change moduleResolution to "bundler"
|
|
72
|
+
// and turn on skipLibCheck
|
|
73
|
+
// Skipped if there's no tsconfig.json
|
|
74
|
+
await modifyTsConfig(config);
|
|
75
|
+
|
|
76
|
+
// Step 4: Configure auth.config.ts
|
|
77
|
+
// To avoid having to execute it we just give instructions
|
|
78
|
+
// if it exists already.
|
|
79
|
+
await configureAuthConfig(config);
|
|
80
|
+
|
|
81
|
+
// Step 5: Initialize auth.ts
|
|
82
|
+
// We do nothing if the file already contains the code,
|
|
83
|
+
// and give instructions otherwise
|
|
84
|
+
await initializeAuth(config);
|
|
85
|
+
|
|
86
|
+
// Step 6: Configure http.ts
|
|
87
|
+
// We do nothing if the file already contains the code,
|
|
88
|
+
// and give instructions otherwise
|
|
89
|
+
await configureHttp(config);
|
|
90
|
+
|
|
91
|
+
// Extra: Configure providers interactively.
|
|
92
|
+
if (options.variables !== undefined) {
|
|
93
|
+
await configureOtherVariables(config, options.variables);
|
|
94
|
+
} else {
|
|
95
|
+
logSuccess(
|
|
96
|
+
"You're all set. Continue by configuring your schema and frontend.",
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
})
|
|
100
|
+
.parse(process.argv);
|
|
101
|
+
|
|
102
|
+
type ProjectConfig = {
|
|
103
|
+
isExpo: boolean;
|
|
104
|
+
isNextjs: boolean;
|
|
105
|
+
isVite: boolean;
|
|
106
|
+
usesTypeScript: boolean;
|
|
107
|
+
convexFolderPath: string;
|
|
108
|
+
deployment: {
|
|
109
|
+
name: string | null;
|
|
110
|
+
type: string | null;
|
|
111
|
+
options: {
|
|
112
|
+
url?: string;
|
|
113
|
+
adminKey?: string;
|
|
114
|
+
prod?: boolean;
|
|
115
|
+
previewName?: string;
|
|
116
|
+
deploymentName?: string;
|
|
117
|
+
};
|
|
118
|
+
};
|
|
119
|
+
// Mutated along the way
|
|
120
|
+
step: number;
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
async function configureSiteUrl(config: ProjectConfig, forcedValue?: string) {
|
|
124
|
+
logStep(config, "Configure SITE_URL");
|
|
125
|
+
if (config.isExpo) {
|
|
126
|
+
logInfo("React Native projects don't require a SITE_URL.");
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Default to localhost for dev and also for local backend
|
|
131
|
+
// this is not perfect but OK since it's just the default.
|
|
132
|
+
const value =
|
|
133
|
+
config.deployment.type === "dev" || config.deployment.type === null
|
|
134
|
+
? config.isVite
|
|
135
|
+
? "http://localhost:5173"
|
|
136
|
+
: "http://localhost:3000"
|
|
137
|
+
: undefined;
|
|
138
|
+
const description =
|
|
139
|
+
config.deployment.type === "dev"
|
|
140
|
+
? "the URL of your local web server (e.g. http://localhost:1234)"
|
|
141
|
+
: "the URL where your site is hosted (e.g. https://example.com)";
|
|
142
|
+
|
|
143
|
+
await configureEnvVar(config, {
|
|
144
|
+
name: "SITE_URL",
|
|
145
|
+
default: value,
|
|
146
|
+
description,
|
|
147
|
+
validate: (input) => {
|
|
148
|
+
try {
|
|
149
|
+
new URL(input);
|
|
150
|
+
return true;
|
|
151
|
+
} catch {
|
|
152
|
+
return "The URL must start with http:// or https://";
|
|
153
|
+
}
|
|
154
|
+
},
|
|
155
|
+
forcedValue,
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
async function configureEnvVar(
|
|
160
|
+
config: ProjectConfig,
|
|
161
|
+
variable: {
|
|
162
|
+
name: string;
|
|
163
|
+
default?: string;
|
|
164
|
+
description: string;
|
|
165
|
+
validate?: (input: string) => true | string;
|
|
166
|
+
forcedValue?: string;
|
|
167
|
+
},
|
|
168
|
+
) {
|
|
169
|
+
if (
|
|
170
|
+
variable.forcedValue &&
|
|
171
|
+
(variable.validate ? variable.validate(variable.forcedValue) : true)
|
|
172
|
+
) {
|
|
173
|
+
await setEnvVar(config, variable.name, variable.forcedValue);
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
const existing = await backendEnvVar(config, variable.name);
|
|
177
|
+
if (existing !== "") {
|
|
178
|
+
if (
|
|
179
|
+
!(await promptForConfirmation(
|
|
180
|
+
`The ${printDeployment(config)} already has ${variable.name} configured to ${chalk.bold(existing)}. Do you want to change it?`,
|
|
181
|
+
{ default: false },
|
|
182
|
+
))
|
|
183
|
+
) {
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
const chosenValue = await promptForInput(`Enter ${variable.description}`, {
|
|
188
|
+
default: variable.default,
|
|
189
|
+
validate: variable.validate,
|
|
190
|
+
});
|
|
191
|
+
await setEnvVar(config, variable.name, chosenValue);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
async function configureKeys(config: ProjectConfig) {
|
|
195
|
+
logStep(config, "Configure private and public key");
|
|
196
|
+
const { JWT_PRIVATE_KEY, JWKS } = await generateKeys();
|
|
197
|
+
// TODO: We should just list all the 3 env vars in one command
|
|
198
|
+
// to speed things up, but the convex CLI doesn't quote the
|
|
199
|
+
// values correctly right now, so we can't.
|
|
200
|
+
const existingPrivateKey = await backendEnvVar(config, "JWT_PRIVATE_KEY");
|
|
201
|
+
const existingJwks = await backendEnvVar(config, "JWKS");
|
|
202
|
+
if (existingPrivateKey !== "" || existingJwks !== "") {
|
|
203
|
+
if (
|
|
204
|
+
!(await promptForConfirmation(
|
|
205
|
+
`The ${printDeployment(config)} already has JWT_PRIVATE_KEY or JWKS configured. Overwrite them?`,
|
|
206
|
+
{ default: false },
|
|
207
|
+
))
|
|
208
|
+
) {
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
// TODO: We should set both env vars in one command, but the convex CLI doesn't
|
|
213
|
+
// support setting multiple env vars.
|
|
214
|
+
await setEnvVar(config, "JWT_PRIVATE_KEY", JWT_PRIVATE_KEY, {
|
|
215
|
+
hideValue: true,
|
|
216
|
+
});
|
|
217
|
+
await setEnvVar(config, "JWKS", JWKS, { hideValue: true });
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
async function backendEnvVar(config: ProjectConfig, name: string) {
|
|
221
|
+
return (
|
|
222
|
+
execSync(`npx convex env get ${deploymentOptions(config)} ${name}`, {
|
|
223
|
+
stdio: "pipe",
|
|
224
|
+
})
|
|
225
|
+
.toString()
|
|
226
|
+
// Remove trailing newline
|
|
227
|
+
.slice(0, -1)
|
|
228
|
+
);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
async function setEnvVar(
|
|
232
|
+
config: ProjectConfig,
|
|
233
|
+
name: string,
|
|
234
|
+
value: string,
|
|
235
|
+
options?: { hideValue: boolean },
|
|
236
|
+
) {
|
|
237
|
+
const valueEscaped = value.replace(/"/g, '\\"');
|
|
238
|
+
execSync(
|
|
239
|
+
`npx convex env set ${deploymentOptions(config)} -- ${name} "${valueEscaped}"`,
|
|
240
|
+
{
|
|
241
|
+
stdio: options?.hideValue ? "ignore" : "inherit",
|
|
242
|
+
},
|
|
243
|
+
);
|
|
244
|
+
if (options?.hideValue) {
|
|
245
|
+
logSuccess(
|
|
246
|
+
`Successfully set ${chalk.bold(name)} (on ${printDeployment(config)})`,
|
|
247
|
+
);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
function deploymentOptions(config: ProjectConfig) {
|
|
252
|
+
const {
|
|
253
|
+
deployment: {
|
|
254
|
+
options: { adminKey },
|
|
255
|
+
},
|
|
256
|
+
} = config;
|
|
257
|
+
const adminKeyOption =
|
|
258
|
+
adminKey !== undefined ? `--admin-key '${adminKey}' ` : "";
|
|
259
|
+
return adminKeyOption + deploymentNameOptions(config);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
function deploymentNameOptions(config: ProjectConfig) {
|
|
263
|
+
const {
|
|
264
|
+
deployment: {
|
|
265
|
+
options: { url, prod, previewName, deploymentName },
|
|
266
|
+
},
|
|
267
|
+
} = config;
|
|
268
|
+
if (url) {
|
|
269
|
+
return `--url ${url}`;
|
|
270
|
+
} else if (prod) {
|
|
271
|
+
return "--prod";
|
|
272
|
+
} else if (previewName) {
|
|
273
|
+
return `--preview-name ${previewName}`;
|
|
274
|
+
} else if (deploymentName) {
|
|
275
|
+
return `--deployment-name ${deploymentName}`;
|
|
276
|
+
} else {
|
|
277
|
+
return "";
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
function printDeployment(config: ProjectConfig) {
|
|
282
|
+
const { name, type } = config.deployment;
|
|
283
|
+
return (
|
|
284
|
+
(type !== null ? `${chalk.bold(type)} ` : "") +
|
|
285
|
+
"deployment" +
|
|
286
|
+
(name !== null ? ` ${chalk.bold(name)}` : "")
|
|
287
|
+
);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// Match `"compilerOptions": {"`
|
|
291
|
+
// ignore comments after the bracket
|
|
292
|
+
// and capture the space between the bracket/last comment
|
|
293
|
+
// and the quote.
|
|
294
|
+
const compilerOptionsPattern =
|
|
295
|
+
/("compilerOptions"\s*:\s*\{(?:\s*(?:\/\*(?:[^*]|\*(?!\/))*\*\/))*(\s*))(?=")/;
|
|
296
|
+
|
|
297
|
+
const validTsConfig = `\
|
|
298
|
+
{
|
|
299
|
+
/* This TypeScript project config describes the environment that
|
|
300
|
+
* Convex functions run in and is used to typecheck them.
|
|
301
|
+
* You can modify it, but some settings required to use Convex.
|
|
302
|
+
*/
|
|
303
|
+
"compilerOptions": {
|
|
304
|
+
/* These settings are not required by Convex and can be modified. */
|
|
305
|
+
"allowJs": true,
|
|
306
|
+
"strict": true,
|
|
307
|
+
"skipLibCheck": true,
|
|
308
|
+
"jsx": "react",
|
|
309
|
+
|
|
310
|
+
/* These compiler options are required by Convex */
|
|
311
|
+
"target": "ESNext",
|
|
312
|
+
"lib": ["ES2021", "dom", "ES2023.Array"],
|
|
313
|
+
"forceConsistentCasingInFileNames": true,
|
|
314
|
+
"allowSyntheticDefaultImports": true,
|
|
315
|
+
"module": "ESNext",
|
|
316
|
+
"moduleResolution": "Bundler",
|
|
317
|
+
"isolatedModules": true,
|
|
318
|
+
"noEmit": true
|
|
319
|
+
},
|
|
320
|
+
"include": ["./**/*"],
|
|
321
|
+
"exclude": ["./_generated"]
|
|
322
|
+
}
|
|
323
|
+
`;
|
|
324
|
+
|
|
325
|
+
async function modifyTsConfig(config: ProjectConfig) {
|
|
326
|
+
logStep(config, "Modify tsconfig file");
|
|
327
|
+
const projectLevelTsConfigPath = "tsconfig.json";
|
|
328
|
+
const tsConfigPath = path.join(config.convexFolderPath, "tsconfig.json");
|
|
329
|
+
if (!existsSync(tsConfigPath)) {
|
|
330
|
+
if (existsSync(projectLevelTsConfigPath)) {
|
|
331
|
+
if (config.isExpo) {
|
|
332
|
+
writeFileSync(tsConfigPath, validTsConfig);
|
|
333
|
+
logSuccess(`Added ${chalk.bold(tsConfigPath)}`);
|
|
334
|
+
return;
|
|
335
|
+
}
|
|
336
|
+
// else assume that the project-level tsconfig already
|
|
337
|
+
// has the right settings, which is true for Vite and Next.js
|
|
338
|
+
}
|
|
339
|
+
logInfo(`No ${chalk.bold(tsConfigPath)} found. Skipping.`);
|
|
340
|
+
return;
|
|
341
|
+
}
|
|
342
|
+
const existingTsConfig = readFileSync(tsConfigPath, "utf8");
|
|
343
|
+
const moduleResolutionPattern = /"moduleResolution"\s*:\s*"(\w+)"/;
|
|
344
|
+
const [, existingModuleResolution] =
|
|
345
|
+
existingTsConfig.match(moduleResolutionPattern) ?? [];
|
|
346
|
+
const skipLibCheckPattern = /"skipLibCheck"\s*:\s*(\w+)/;
|
|
347
|
+
const [, existingSkipLibCheck] =
|
|
348
|
+
existingTsConfig.match(skipLibCheckPattern) ?? [];
|
|
349
|
+
if (
|
|
350
|
+
/Bundler/i.test(existingModuleResolution) &&
|
|
351
|
+
existingSkipLibCheck === "true"
|
|
352
|
+
) {
|
|
353
|
+
logSuccess(`The ${chalk.bold(tsConfigPath)} is already set up.`);
|
|
354
|
+
return;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
if (!compilerOptionsPattern.test(existingTsConfig)) {
|
|
358
|
+
logInfo(
|
|
359
|
+
`Modify your ${chalk.bold(tsConfigPath)} to include the following:`,
|
|
360
|
+
);
|
|
361
|
+
const source = `\
|
|
362
|
+
{
|
|
363
|
+
"compilerOptions": {
|
|
364
|
+
"moduleResolution": "Bundler",
|
|
365
|
+
"skipLibCheck": true
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
`;
|
|
369
|
+
print(indent(`\n${source}\n`));
|
|
370
|
+
await promptForConfirmationOrExit("Ready to continue?");
|
|
371
|
+
}
|
|
372
|
+
const changedTsConfig = addCompilerOption(
|
|
373
|
+
addCompilerOption(
|
|
374
|
+
existingTsConfig,
|
|
375
|
+
existingModuleResolution,
|
|
376
|
+
moduleResolutionPattern,
|
|
377
|
+
'"moduleResolution": "Bundler"',
|
|
378
|
+
),
|
|
379
|
+
existingSkipLibCheck,
|
|
380
|
+
skipLibCheckPattern,
|
|
381
|
+
'"skipLibCheck": true',
|
|
382
|
+
);
|
|
383
|
+
writeFileSync(tsConfigPath, changedTsConfig);
|
|
384
|
+
logSuccess(`Modified ${chalk.bold(tsConfigPath)}`);
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
function addCompilerOption(
|
|
388
|
+
tsconfig: string,
|
|
389
|
+
existingValue: string | undefined,
|
|
390
|
+
pattern: RegExp,
|
|
391
|
+
optionAndValue: string,
|
|
392
|
+
) {
|
|
393
|
+
if (existingValue === undefined) {
|
|
394
|
+
return tsconfig.replace(compilerOptionsPattern, `$1${optionAndValue},$2`);
|
|
395
|
+
} else {
|
|
396
|
+
return tsconfig.replace(pattern, optionAndValue);
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
async function configureAuthConfig(config: ProjectConfig) {
|
|
401
|
+
logStep(config, "Configure auth config file");
|
|
402
|
+
const sourceTemplate = `\
|
|
403
|
+
export default {
|
|
404
|
+
providers: [$$
|
|
405
|
+
{
|
|
406
|
+
domain: process.env.CONVEX_SITE_URL,
|
|
407
|
+
applicationID: "convex",
|
|
408
|
+
},$$
|
|
409
|
+
],
|
|
410
|
+
};
|
|
411
|
+
`;
|
|
412
|
+
const source = templateToSource(sourceTemplate);
|
|
413
|
+
const authConfigPath = path.join(config.convexFolderPath, "auth.config");
|
|
414
|
+
const existingConfigPath = await existingNonEmptySourcePath(authConfigPath);
|
|
415
|
+
if (existingConfigPath !== null) {
|
|
416
|
+
const existingConfig = readFileSync(existingConfigPath, "utf8");
|
|
417
|
+
if (doesAlreadyMatchTemplate(existingConfig, sourceTemplate)) {
|
|
418
|
+
logSuccess(`The ${chalk.bold(existingConfigPath)} is already set up.`);
|
|
419
|
+
} else {
|
|
420
|
+
logInfo(
|
|
421
|
+
`You already have a ${chalk.bold(existingConfigPath)}, make sure the \`providers\` include the following config:`,
|
|
422
|
+
);
|
|
423
|
+
print(indent(`\n${source}\n`));
|
|
424
|
+
await promptForConfirmationOrExit("Ready to continue?");
|
|
425
|
+
}
|
|
426
|
+
} else {
|
|
427
|
+
const newConfigPath = config.usesTypeScript
|
|
428
|
+
? `${authConfigPath}.ts`
|
|
429
|
+
: `${authConfigPath}.js`;
|
|
430
|
+
writeFileSync(newConfigPath, source);
|
|
431
|
+
logSuccess(`Created ${chalk.bold(newConfigPath)}`);
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
async function initializeAuth(config: ProjectConfig) {
|
|
436
|
+
logStep(config, "Initialize auth file");
|
|
437
|
+
const sourceTemplate = `\
|
|
438
|
+
import { Auth } from "@robelest/convex-auth/component";
|
|
439
|
+
|
|
440
|
+
export const { auth, signIn, signOut, store } = Auth({$$
|
|
441
|
+
providers: [$$],$$
|
|
442
|
+
});
|
|
443
|
+
`;
|
|
444
|
+
const source = templateToSource(sourceTemplate);
|
|
445
|
+
const authPath = path.join(config.convexFolderPath, "auth");
|
|
446
|
+
const existingAuthPath = await existingNonEmptySourcePath(authPath);
|
|
447
|
+
if (existingAuthPath !== null) {
|
|
448
|
+
const existingAuth = readFileSync(existingAuthPath, "utf8");
|
|
449
|
+
if (doesAlreadyMatchTemplate(existingAuth, sourceTemplate)) {
|
|
450
|
+
logSuccess(`The ${chalk.bold(existingAuthPath)} is already set up.`);
|
|
451
|
+
} else {
|
|
452
|
+
logInfo(
|
|
453
|
+
`You already have a ${chalk.bold(existingAuthPath)}, make sure it initializes \`Auth\` like this:`,
|
|
454
|
+
);
|
|
455
|
+
print(indent(`\n${source}\n`));
|
|
456
|
+
await promptForConfirmationOrExit("Ready to continue?");
|
|
457
|
+
}
|
|
458
|
+
} else {
|
|
459
|
+
const newAuthPath = config.usesTypeScript
|
|
460
|
+
? `${authPath}.ts`
|
|
461
|
+
: `${authPath}.js`;
|
|
462
|
+
writeFileSync(newAuthPath, source);
|
|
463
|
+
logSuccess(`Created ${chalk.bold(newAuthPath)}`);
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
async function configureHttp(config: ProjectConfig) {
|
|
468
|
+
logStep(config, "Configure http file");
|
|
469
|
+
const sourceTemplate = `\
|
|
470
|
+
import { httpRouter } from "convex/server";
|
|
471
|
+
import { auth } from "./auth";
|
|
472
|
+
|
|
473
|
+
const http = httpRouter();
|
|
474
|
+
|
|
475
|
+
auth.addHttpRoutes(http);
|
|
476
|
+
|
|
477
|
+
export default http;
|
|
478
|
+
`;
|
|
479
|
+
const source = templateToSource(sourceTemplate);
|
|
480
|
+
const httpPath = path.join(config.convexFolderPath, "http");
|
|
481
|
+
const existingHttpPath = await existingNonEmptySourcePath(httpPath);
|
|
482
|
+
if (existingHttpPath !== null) {
|
|
483
|
+
const existingHttp = readFileSync(existingHttpPath, "utf8");
|
|
484
|
+
if (doesAlreadyMatchTemplate(existingHttp, sourceTemplate)) {
|
|
485
|
+
logSuccess(`The ${chalk.bold(existingHttpPath)} is already set up.`);
|
|
486
|
+
} else {
|
|
487
|
+
logInfo(
|
|
488
|
+
`You already have a ${chalk.bold(existingHttpPath)}, make sure it includes the call to \`auth.addHttpRoutes\`:`,
|
|
489
|
+
);
|
|
490
|
+
print(indent(`\n${source}\n`));
|
|
491
|
+
await promptForConfirmationOrExit("Ready to continue?");
|
|
492
|
+
}
|
|
493
|
+
} else {
|
|
494
|
+
const newHttpPath = config.usesTypeScript
|
|
495
|
+
? `${httpPath}.ts`
|
|
496
|
+
: `${httpPath}.js`;
|
|
497
|
+
writeFileSync(newHttpPath, source);
|
|
498
|
+
logSuccess(`Created ${chalk.bold(newHttpPath)}`);
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
const VariablesSchema = v.object({
|
|
503
|
+
help: v.optional(v.string()),
|
|
504
|
+
providers: v.array(
|
|
505
|
+
v.object({
|
|
506
|
+
name: v.string(),
|
|
507
|
+
help: v.optional(v.string()),
|
|
508
|
+
variables: v.array(
|
|
509
|
+
v.object({
|
|
510
|
+
name: v.string(),
|
|
511
|
+
description: v.string(),
|
|
512
|
+
}),
|
|
513
|
+
),
|
|
514
|
+
}),
|
|
515
|
+
),
|
|
516
|
+
success: v.optional(v.string()),
|
|
517
|
+
});
|
|
518
|
+
|
|
519
|
+
async function configureOtherVariables(config: ProjectConfig, json: string) {
|
|
520
|
+
const variables = v.parse(VariablesSchema, JSON.parse(json));
|
|
521
|
+
logStep(config, "Configure extra environment variables");
|
|
522
|
+
// Ex: The default setup includes sign-in with GitHub OAuth
|
|
523
|
+
// and sending magic links via Resend.
|
|
524
|
+
if (variables.help !== undefined) {
|
|
525
|
+
print(variables.help);
|
|
526
|
+
}
|
|
527
|
+
for (const provider of variables.providers) {
|
|
528
|
+
if (
|
|
529
|
+
!(await promptForConfirmation(
|
|
530
|
+
`Do you want to configure ${provider.name}?`,
|
|
531
|
+
))
|
|
532
|
+
) {
|
|
533
|
+
continue;
|
|
534
|
+
}
|
|
535
|
+
if (provider.help !== undefined) {
|
|
536
|
+
print(provider.help);
|
|
537
|
+
}
|
|
538
|
+
for (const variable of provider.variables) {
|
|
539
|
+
await configureEnvVar(config, {
|
|
540
|
+
name: variable.name,
|
|
541
|
+
description: variable.description,
|
|
542
|
+
});
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
if (variables.success !== undefined) {
|
|
546
|
+
logSuccess(variables.success);
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
function doesAlreadyMatchTemplate(existing: string, template: string) {
|
|
551
|
+
const regex = new RegExp(
|
|
552
|
+
template
|
|
553
|
+
.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")
|
|
554
|
+
.replace(/\\\$\\\$/g, ".*")
|
|
555
|
+
.replace(/;\n/g, ";.*"),
|
|
556
|
+
"s",
|
|
557
|
+
);
|
|
558
|
+
return regex.test(existing);
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
function templateToSource(template: string) {
|
|
562
|
+
return template.replace(/\$\$/g, "");
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
async function existingNonEmptySourcePath(path: string) {
|
|
566
|
+
return (await existsAndNotEmpty(`${path}.ts`))
|
|
567
|
+
? `${path}.ts`
|
|
568
|
+
: (await existsAndNotEmpty(`${path}.js`))
|
|
569
|
+
? `${path}.js`
|
|
570
|
+
: null;
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
async function existsAndNotEmpty(path: string) {
|
|
574
|
+
return existsSync(path) && readFileSync(path, "utf8").trim() !== "";
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
function logStep(config: ProjectConfig, message: string) {
|
|
578
|
+
if (config.step > 1) {
|
|
579
|
+
print();
|
|
580
|
+
}
|
|
581
|
+
logInfo(chalk.bold(`Step ${config.step++}: ${message}`));
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
async function checkSourceControl(options: {
|
|
585
|
+
skipGitCheck?: boolean;
|
|
586
|
+
allowDirtyGitState?: boolean;
|
|
587
|
+
}) {
|
|
588
|
+
if (options.allowDirtyGitState) {
|
|
589
|
+
return;
|
|
590
|
+
}
|
|
591
|
+
const isGit = existsSync(".git");
|
|
592
|
+
if (isGit) {
|
|
593
|
+
const gitStatus = execSync("git status --porcelain").toString();
|
|
594
|
+
const changedFiles = gitStatus
|
|
595
|
+
.split("\n")
|
|
596
|
+
.filter(
|
|
597
|
+
(line) => !/\bpackage(-lock)?.json/.test(line) && line.length > 0,
|
|
598
|
+
);
|
|
599
|
+
if (changedFiles.length > 0) {
|
|
600
|
+
logError(
|
|
601
|
+
"There are unstaged or uncommitted changes in the working directory. " +
|
|
602
|
+
"Please commit or stash them before proceeding.",
|
|
603
|
+
);
|
|
604
|
+
await promptForConfirmationOrExit("Continue anyway?", { default: false });
|
|
605
|
+
}
|
|
606
|
+
} else {
|
|
607
|
+
if (options.skipGitCheck) {
|
|
608
|
+
return;
|
|
609
|
+
}
|
|
610
|
+
logWarning(
|
|
611
|
+
"No source control detected. We strongly recommend committing the current state of your code before proceeding.",
|
|
612
|
+
);
|
|
613
|
+
await promptForConfirmationOrExit("Continue anyway?");
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
type PackageJSON = { __isPackageJSON: true; [key: string]: any };
|
|
618
|
+
|
|
619
|
+
function readPackageJson(): PackageJSON {
|
|
620
|
+
try {
|
|
621
|
+
const data = readFileSync("package.json", "utf8");
|
|
622
|
+
return JSON.parse(data);
|
|
623
|
+
} catch (error: any) {
|
|
624
|
+
logErrorAndExit(
|
|
625
|
+
"`@robelest/convex-auth` must be run from a project directory which " +
|
|
626
|
+
'includes a valid "package.json" file. You can create one by running ' +
|
|
627
|
+
"`npm init`.",
|
|
628
|
+
error.message,
|
|
629
|
+
);
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
type ConvexJSON = { __isConvexJSON: true; [key: string]: any };
|
|
634
|
+
|
|
635
|
+
function readConvexJson(): ConvexJSON {
|
|
636
|
+
if (!existsSync("convex.json")) {
|
|
637
|
+
return {} as ConvexJSON;
|
|
638
|
+
}
|
|
639
|
+
try {
|
|
640
|
+
const data = readFileSync("convex.json", "utf8");
|
|
641
|
+
return JSON.parse(data);
|
|
642
|
+
} catch (error: any) {
|
|
643
|
+
logErrorAndExit(
|
|
644
|
+
"Could not parse your convex.json. Is it valid JSON?",
|
|
645
|
+
error.message,
|
|
646
|
+
);
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
function readConvexDeployment(options: {
|
|
651
|
+
url?: string;
|
|
652
|
+
adminKey?: string;
|
|
653
|
+
prod?: boolean;
|
|
654
|
+
previewName?: string;
|
|
655
|
+
deploymentName?: string;
|
|
656
|
+
}) {
|
|
657
|
+
const { adminKey, url, prod, previewName, deploymentName } = options;
|
|
658
|
+
const adminKeyName = adminKey ? deploymentNameFromAdminKey(adminKey) : null;
|
|
659
|
+
const adminKeyType = adminKey ? deploymentTypeFromAdminKey(adminKey) : null;
|
|
660
|
+
if (url) {
|
|
661
|
+
return { name: adminKeyName ?? url, type: adminKeyType, options };
|
|
662
|
+
} else if (prod) {
|
|
663
|
+
return { name: adminKeyName, type: "prod", options };
|
|
664
|
+
}
|
|
665
|
+
if (previewName) {
|
|
666
|
+
return { name: previewName, type: "preview", options };
|
|
667
|
+
}
|
|
668
|
+
if (deploymentName) {
|
|
669
|
+
return { name: deploymentName, type: adminKeyType, options };
|
|
670
|
+
}
|
|
671
|
+
if (adminKey) {
|
|
672
|
+
return { name: adminKeyName, type: adminKeyType, options };
|
|
673
|
+
}
|
|
674
|
+
loadEnvFile({ path: ".env.local" });
|
|
675
|
+
loadEnvFile();
|
|
676
|
+
if (process.env.CONVEX_DEPLOYMENT) {
|
|
677
|
+
return {
|
|
678
|
+
name: stripDeploymentTypePrefix(process.env.CONVEX_DEPLOYMENT),
|
|
679
|
+
type: getDeploymentTypeFromConfiguredDeployment(
|
|
680
|
+
process.env.CONVEX_DEPLOYMENT,
|
|
681
|
+
),
|
|
682
|
+
options,
|
|
683
|
+
};
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
logErrorAndExit(
|
|
687
|
+
"Could not find a configured CONVEX_DEPLOYMENT. Did you forget to run `npx convex dev` first?",
|
|
688
|
+
);
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
// NOTE: CONVEX CLI DEP
|
|
692
|
+
// Given a deployment string like "dev:tall-forest-1234"
|
|
693
|
+
// returns only the slug "tall-forest-1234".
|
|
694
|
+
// If there's no prefix returns the original string.
|
|
695
|
+
export function stripDeploymentTypePrefix(deployment: string) {
|
|
696
|
+
return deployment.split(":").at(-1)!;
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
// NOTE: CONVEX CLI DEP
|
|
700
|
+
// Handling legacy CONVEX_DEPLOYMENT without type prefix as well
|
|
701
|
+
function getDeploymentTypeFromConfiguredDeployment(raw: string) {
|
|
702
|
+
const typeRaw = raw.split(":")[0];
|
|
703
|
+
const type =
|
|
704
|
+
typeRaw === "prod" || typeRaw === "dev" || typeRaw === "preview"
|
|
705
|
+
? typeRaw
|
|
706
|
+
: null;
|
|
707
|
+
return type;
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
// NOTE: CONVEX CLI DEP
|
|
711
|
+
function deploymentNameFromAdminKey(adminKey: string) {
|
|
712
|
+
const parts = adminKey.split("|");
|
|
713
|
+
if (parts.length === 1) {
|
|
714
|
+
return null;
|
|
715
|
+
}
|
|
716
|
+
if (isPreviewDeployKey(adminKey)) {
|
|
717
|
+
// Preview deploy keys do not contain a deployment name.
|
|
718
|
+
return null;
|
|
719
|
+
}
|
|
720
|
+
return stripDeploymentTypePrefix(parts[0]);
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
// NOTE: CONVEX CLI DEP - but modified to not default to "prod"
|
|
724
|
+
//
|
|
725
|
+
// For current keys returns prod|dev|preview,
|
|
726
|
+
// for legacy keys returns "prod".
|
|
727
|
+
// Examples:
|
|
728
|
+
// "prod:deploymentName|key" -> "prod"
|
|
729
|
+
// "preview:deploymentName|key" -> "preview"
|
|
730
|
+
// "dev:deploymentName|key" -> "dev"
|
|
731
|
+
// "key" -> "prod"
|
|
732
|
+
export function deploymentTypeFromAdminKey(adminKey: string) {
|
|
733
|
+
const parts = adminKey.split(":");
|
|
734
|
+
if (parts.length === 1) {
|
|
735
|
+
return null;
|
|
736
|
+
}
|
|
737
|
+
return parts.at(0)!;
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
// NOTE: CONVEX CLI DEP
|
|
741
|
+
// Needed to differentiate a preview deploy key
|
|
742
|
+
// from a concrete preview deployment's deploy key.
|
|
743
|
+
// preview deploy key: `preview:team:project|key`
|
|
744
|
+
// preview deployment's deploy key: `preview:deploymentName|key`
|
|
745
|
+
export function isPreviewDeployKey(adminKey: string) {
|
|
746
|
+
const parts = adminKey.split("|");
|
|
747
|
+
if (parts.length === 1) {
|
|
748
|
+
return false;
|
|
749
|
+
}
|
|
750
|
+
const [prefix] = parts;
|
|
751
|
+
const prefixParts = prefix.split(":");
|
|
752
|
+
return prefixParts[0] === "preview" && prefixParts.length === 3;
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
async function promptForConfirmationOrExit(
|
|
756
|
+
message: string,
|
|
757
|
+
options: { default?: boolean } = {},
|
|
758
|
+
) {
|
|
759
|
+
if (!(await promptForConfirmation(message, options))) {
|
|
760
|
+
process.exit(1);
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
async function promptForConfirmation(
|
|
765
|
+
message: string,
|
|
766
|
+
options: { default?: boolean } = {},
|
|
767
|
+
) {
|
|
768
|
+
if (process.stdout.isTTY) {
|
|
769
|
+
const { confirmed } = await inquirer.prompt<{ confirmed: boolean }>([
|
|
770
|
+
{
|
|
771
|
+
type: "confirm",
|
|
772
|
+
name: "confirmed",
|
|
773
|
+
message,
|
|
774
|
+
default: options.default ?? true,
|
|
775
|
+
},
|
|
776
|
+
]);
|
|
777
|
+
return confirmed;
|
|
778
|
+
} else {
|
|
779
|
+
return options.default ?? true;
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
async function promptForInput(
|
|
784
|
+
message: string,
|
|
785
|
+
options: { default?: string; validate?: (input: string) => true | string },
|
|
786
|
+
) {
|
|
787
|
+
if (process.stdout.isTTY) {
|
|
788
|
+
const { input } = await inquirer.prompt<{ input: string }>([
|
|
789
|
+
{
|
|
790
|
+
type: "input",
|
|
791
|
+
name: "input",
|
|
792
|
+
message,
|
|
793
|
+
default: options.default,
|
|
794
|
+
validate: options.validate,
|
|
795
|
+
},
|
|
796
|
+
]);
|
|
797
|
+
return input;
|
|
798
|
+
} else {
|
|
799
|
+
if (options.default !== undefined) {
|
|
800
|
+
return options.default;
|
|
801
|
+
} else {
|
|
802
|
+
logErrorAndExit(
|
|
803
|
+
"Run this command in an interactive terminal to provide input.",
|
|
804
|
+
);
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
function logErrorAndExit(message: string, error?: string): never {
|
|
810
|
+
logError(message, error);
|
|
811
|
+
process.exit(1);
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
function logError(message: string, error?: string) {
|
|
815
|
+
print(
|
|
816
|
+
`${chalk.red(`✖`)} ${indent(message)}${
|
|
817
|
+
error !== undefined ? `\n ${chalk.grey(`Error: ${indent(error)}`)}` : ""
|
|
818
|
+
}`,
|
|
819
|
+
);
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
function logWarning(message: string) {
|
|
823
|
+
print(`${chalk.yellow.bold(`!`)} ${indent(message)}`);
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
function logInfo(message: string) {
|
|
827
|
+
print(`${chalk.blue.bold(`i`)} ${indent(message)}`);
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
function logSuccess(message: string) {
|
|
831
|
+
print(`${chalk.green(`✔`)} ${indent(message)}`);
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
function print(message?: string) {
|
|
835
|
+
process.stderr.write((message ?? "") + "\n");
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
function indent(string: string) {
|
|
839
|
+
return string.replace(/^/gm, " ").slice(2);
|
|
840
|
+
}
|