convex 1.41.0 → 1.42.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/CHANGELOG.md +38 -0
- package/dist/browser.bundle.js +24 -6
- package/dist/browser.bundle.js.map +2 -2
- package/dist/cjs/browser/simple_client-node.js +85 -27
- package/dist/cjs/browser/simple_client-node.js.map +3 -3
- package/dist/cjs/browser/sync/authentication_manager.js +21 -4
- package/dist/cjs/browser/sync/authentication_manager.js.map +2 -2
- package/dist/cjs/browser/sync/client.js +2 -1
- package/dist/cjs/browser/sync/client.js.map +2 -2
- package/dist/cjs/cli/env.js +12 -5
- package/dist/cjs/cli/env.js.map +2 -2
- package/dist/cjs/cli/envDefault.js +12 -7
- package/dist/cjs/cli/envDefault.js.map +2 -2
- package/dist/cjs/cli/insights.js +11 -1
- package/dist/cjs/cli/insights.js.map +2 -2
- package/dist/cjs/cli/lib/env.js +5 -1
- package/dist/cjs/cli/lib/env.js.map +2 -2
- package/dist/cjs/cli/lib/login.js +16 -12
- package/dist/cjs/cli/lib/login.js.map +2 -2
- package/dist/cjs/cli/lib/logs.js +4 -1
- package/dist/cjs/cli/lib/logs.js.map +2 -2
- package/dist/cjs/cli/lib/run.js +27 -2
- package/dist/cjs/cli/lib/run.js.map +2 -2
- package/dist/cjs/cli/lib/typecheck.js +28 -10
- package/dist/cjs/cli/lib/typecheck.js.map +2 -2
- package/dist/cjs/cli/lib/utils/utils.js +30 -0
- package/dist/cjs/cli/lib/utils/utils.js.map +2 -2
- package/dist/cjs/cli/program.js +2 -1
- package/dist/cjs/cli/program.js.map +2 -2
- package/dist/cjs/cli/project.js +27 -0
- package/dist/cjs/cli/project.js.map +7 -0
- package/dist/cjs/cli/projectCreate.js +90 -0
- package/dist/cjs/cli/projectCreate.js.map +7 -0
- package/dist/cjs/index.js +1 -1
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/server/database.js.map +1 -1
- package/dist/cjs/server/impl/meta_impl.js +6 -6
- package/dist/cjs/server/impl/meta_impl.js.map +2 -2
- package/dist/cjs/server/impl/registration_impl.js +9 -4
- package/dist/cjs/server/impl/registration_impl.js.map +2 -2
- package/dist/cjs/server/index.js.map +2 -2
- package/dist/cjs/server/meta.js.map +1 -1
- package/dist/cjs/server/query.js.map +1 -1
- package/dist/cjs/server/registration.js.map +1 -1
- package/dist/cjs/server/storage.js.map +1 -1
- package/dist/cjs/server/system_fields.js.map +1 -1
- package/dist/cjs-types/browser/sync/authentication_manager.d.ts +2 -0
- package/dist/cjs-types/browser/sync/authentication_manager.d.ts.map +1 -1
- package/dist/cjs-types/browser/sync/client.d.ts +16 -0
- package/dist/cjs-types/browser/sync/client.d.ts.map +1 -1
- package/dist/cjs-types/cli/env.d.ts.map +1 -1
- package/dist/cjs-types/cli/envDefault.d.ts.map +1 -1
- package/dist/cjs-types/cli/insights.d.ts +1 -0
- package/dist/cjs-types/cli/insights.d.ts.map +1 -1
- package/dist/cjs-types/cli/lib/env.d.ts +3 -1
- package/dist/cjs-types/cli/lib/env.d.ts.map +1 -1
- package/dist/cjs-types/cli/lib/env.test.d.ts +2 -0
- package/dist/cjs-types/cli/lib/env.test.d.ts.map +1 -0
- package/dist/cjs-types/cli/lib/login.d.ts.map +1 -1
- package/dist/cjs-types/cli/lib/logs.d.ts +1 -1
- package/dist/cjs-types/cli/lib/logs.d.ts.map +1 -1
- package/dist/cjs-types/cli/lib/run.d.ts +1 -1
- package/dist/cjs-types/cli/lib/run.d.ts.map +1 -1
- package/dist/cjs-types/cli/lib/utils/utils.d.ts +10 -0
- package/dist/cjs-types/cli/lib/utils/utils.d.ts.map +1 -1
- package/dist/cjs-types/cli/program.d.ts.map +1 -1
- package/dist/cjs-types/cli/project.d.ts +3 -0
- package/dist/cjs-types/cli/project.d.ts.map +1 -0
- package/dist/cjs-types/cli/projectCreate.d.ts +5 -0
- package/dist/cjs-types/cli/projectCreate.d.ts.map +1 -0
- package/dist/cjs-types/index.d.ts +1 -1
- package/dist/cjs-types/server/database.d.ts +21 -3
- package/dist/cjs-types/server/database.d.ts.map +1 -1
- package/dist/cjs-types/server/impl/meta_impl.d.ts.map +1 -1
- package/dist/cjs-types/server/impl/registration_impl.d.ts.map +1 -1
- package/dist/cjs-types/server/index.d.ts +1 -1
- package/dist/cjs-types/server/index.d.ts.map +1 -1
- package/dist/cjs-types/server/meta.d.ts +10 -0
- package/dist/cjs-types/server/meta.d.ts.map +1 -1
- package/dist/cjs-types/server/query.d.ts +1 -1
- package/dist/cjs-types/server/registration.d.ts +15 -3
- package/dist/cjs-types/server/registration.d.ts.map +1 -1
- package/dist/cjs-types/server/storage.d.ts +20 -3
- package/dist/cjs-types/server/storage.d.ts.map +1 -1
- package/dist/cli.bundle.cjs +1099 -634
- package/dist/cli.bundle.cjs.map +4 -4
- package/dist/esm/browser/simple_client-node.js +85 -27
- package/dist/esm/browser/simple_client-node.js.map +3 -3
- package/dist/esm/browser/sync/authentication_manager.js +21 -4
- package/dist/esm/browser/sync/authentication_manager.js.map +2 -2
- package/dist/esm/browser/sync/client.js +2 -1
- package/dist/esm/browser/sync/client.js.map +2 -2
- package/dist/esm/cli/env.js +12 -5
- package/dist/esm/cli/env.js.map +2 -2
- package/dist/esm/cli/envDefault.js +12 -7
- package/dist/esm/cli/envDefault.js.map +2 -2
- package/dist/esm/cli/insights.js +11 -1
- package/dist/esm/cli/insights.js.map +2 -2
- package/dist/esm/cli/lib/env.js +5 -1
- package/dist/esm/cli/lib/env.js.map +2 -2
- package/dist/esm/cli/lib/login.js +18 -13
- package/dist/esm/cli/lib/login.js.map +2 -2
- package/dist/esm/cli/lib/logs.js +5 -2
- package/dist/esm/cli/lib/logs.js.map +2 -2
- package/dist/esm/cli/lib/run.js +32 -3
- package/dist/esm/cli/lib/run.js.map +2 -2
- package/dist/esm/cli/lib/typecheck.js +28 -10
- package/dist/esm/cli/lib/typecheck.js.map +3 -3
- package/dist/esm/cli/lib/utils/utils.js +29 -0
- package/dist/esm/cli/lib/utils/utils.js.map +2 -2
- package/dist/esm/cli/program.js +2 -1
- package/dist/esm/cli/program.js.map +2 -2
- package/dist/esm/cli/project.js +5 -0
- package/dist/esm/cli/project.js.map +7 -0
- package/dist/esm/cli/projectCreate.js +68 -0
- package/dist/esm/cli/projectCreate.js.map +7 -0
- package/dist/esm/index.js +1 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/server/impl/meta_impl.js +6 -6
- package/dist/esm/server/impl/meta_impl.js.map +2 -2
- package/dist/esm/server/impl/registration_impl.js +9 -4
- package/dist/esm/server/impl/registration_impl.js.map +2 -2
- package/dist/esm/server/index.js.map +2 -2
- package/dist/esm-types/browser/sync/authentication_manager.d.ts +2 -0
- package/dist/esm-types/browser/sync/authentication_manager.d.ts.map +1 -1
- package/dist/esm-types/browser/sync/client.d.ts +16 -0
- package/dist/esm-types/browser/sync/client.d.ts.map +1 -1
- package/dist/esm-types/cli/env.d.ts.map +1 -1
- package/dist/esm-types/cli/envDefault.d.ts.map +1 -1
- package/dist/esm-types/cli/insights.d.ts +1 -0
- package/dist/esm-types/cli/insights.d.ts.map +1 -1
- package/dist/esm-types/cli/lib/env.d.ts +3 -1
- package/dist/esm-types/cli/lib/env.d.ts.map +1 -1
- package/dist/esm-types/cli/lib/env.test.d.ts +2 -0
- package/dist/esm-types/cli/lib/env.test.d.ts.map +1 -0
- package/dist/esm-types/cli/lib/login.d.ts.map +1 -1
- package/dist/esm-types/cli/lib/logs.d.ts +1 -1
- package/dist/esm-types/cli/lib/logs.d.ts.map +1 -1
- package/dist/esm-types/cli/lib/run.d.ts +1 -1
- package/dist/esm-types/cli/lib/run.d.ts.map +1 -1
- package/dist/esm-types/cli/lib/utils/utils.d.ts +10 -0
- package/dist/esm-types/cli/lib/utils/utils.d.ts.map +1 -1
- package/dist/esm-types/cli/program.d.ts.map +1 -1
- package/dist/esm-types/cli/project.d.ts +3 -0
- package/dist/esm-types/cli/project.d.ts.map +1 -0
- package/dist/esm-types/cli/projectCreate.d.ts +5 -0
- package/dist/esm-types/cli/projectCreate.d.ts.map +1 -0
- package/dist/esm-types/index.d.ts +1 -1
- package/dist/esm-types/server/database.d.ts +21 -3
- package/dist/esm-types/server/database.d.ts.map +1 -1
- package/dist/esm-types/server/impl/meta_impl.d.ts.map +1 -1
- package/dist/esm-types/server/impl/registration_impl.d.ts.map +1 -1
- package/dist/esm-types/server/index.d.ts +1 -1
- package/dist/esm-types/server/index.d.ts.map +1 -1
- package/dist/esm-types/server/meta.d.ts +10 -0
- package/dist/esm-types/server/meta.d.ts.map +1 -1
- package/dist/esm-types/server/query.d.ts +1 -1
- package/dist/esm-types/server/registration.d.ts +15 -3
- package/dist/esm-types/server/registration.d.ts.map +1 -1
- package/dist/esm-types/server/storage.d.ts +20 -3
- package/dist/esm-types/server/storage.d.ts.map +1 -1
- package/dist/react.bundle.js +24 -6
- package/dist/react.bundle.js.map +2 -2
- package/package.json +4 -4
- package/src/browser/sync/authentication_manager.ts +42 -14
- package/src/browser/sync/client.ts +17 -0
- package/src/cli/env.ts +16 -6
- package/src/cli/envDefault.ts +13 -7
- package/src/cli/insights.ts +12 -0
- package/src/cli/lib/env.test.ts +60 -0
- package/src/cli/lib/env.ts +9 -1
- package/src/cli/lib/login.ts +18 -18
- package/src/cli/lib/logs.ts +7 -2
- package/src/cli/lib/run.ts +37 -3
- package/src/cli/lib/typecheck.ts +26 -10
- package/src/cli/lib/utils/utils.ts +56 -0
- package/src/cli/program.ts +2 -0
- package/src/cli/project.ts +7 -0
- package/src/cli/projectCreate.ts +99 -0
- package/src/index.ts +1 -1
- package/src/react/ConvexAuthState.test.tsx +145 -0
- package/src/server/database.ts +21 -3
- package/src/server/impl/meta_impl.ts +7 -6
- package/src/server/impl/registration_impl.ts +11 -5
- package/src/server/index.ts +1 -0
- package/src/server/meta.ts +10 -0
- package/src/server/query.ts +1 -1
- package/src/server/registration.test.ts +25 -1
- package/src/server/registration.ts +16 -1
- package/src/server/storage.ts +22 -3
- package/src/server/system_fields.ts +1 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/cli/envDefault.ts"],
|
|
4
|
-
"sourcesContent": ["import { Command, Option } from \"@commander-js/extra-typings\";\nimport {\n envGet,\n envList,\n envRemove,\n envSet,\n EnvVarBackend,\n} from \"./lib/env.js\";\nimport { ensureHasConvexDependency } from \"./lib/utils/utils.js\";\nimport { defaultEnvBackend } from \"./lib/defaultEnv.js\";\nimport {\n CloudDeploymentType,\n DeploymentSelectionOptions,\n DeploymentType,\n fetchTeamAndProject,\n} from \"./lib/api.js\";\nimport { Context, oneoffContext } from \"../bundler/context.js\";\nimport { selectEnvDeployment } from \"./env.js\";\nimport { getProjectDetails } from \"./lib/deploymentSelection.js\";\n\ntype EnvDefaultExtraOptions = {\n type?: string;\n project?: string;\n};\n\nfunction addEnvDefaultOptions<T extends Command<any, any>>(cmd: T): T {\n return cmd\n .addOption(\n new Option(\n \"--type <type>\",\n \"Manage default env vars for the given deployment type (dev, preview, prod) instead of inferring from the current deployment.\",\n ),\n )\n .addOption(\n new Option(\n \"--project <project>\",\n \"Select a project manually. Accepts `team-slug:project-slug` or just `project-slug` (team inferred from your current project). Requires --type.\",\n ),\n ) as T;\n}\n\nconst envDefaultSet = addEnvDefaultOptions(\n new Command(\"set\")\n .usage(\"[options] <name> <value>\")\n .argument(\"[name]\", \"The name of the default environment variable to set.\")\n .argument(\n \"[value]\",\n \"The value to set the variable to. Omit to set it interactively.\",\n )\n .summary(\"Set a default variable\")\n .description(\n [\n \"Set default environment variables for your project's deployment type.\",\n \"\",\n \"\u2022 `npx convex env default set NAME 'value'`\",\n \"\u2022 `npx convex env default set NAME # omit a value to set one interactively`\",\n \"\u2022 `npx convex env default set NAME --from-file value.txt`\",\n \"\u2022 `npx convex env default set --from-file .env.defaults`\",\n \"\",\n \"When setting multiple values, it will refuse all changes if any variables are already set to different values by default. Pass --force to overwrite the provided values.\",\n \"\",\n \"The deployment type is determined by the current deployment (local maps to dev), or by --type if provided.\",\n ].join(\"\\n\"),\n )\n .option(\n \"--from-file <file>\",\n \"Read environment variables from a .env file. Without --force, fails if any existing variable has a different value.\",\n )\n .option(\n \"--force\",\n \"When setting multiple variables, overwrite existing environment variable values instead of failing on mismatch.\",\n )\n .configureHelp({ showGlobalOptions: true })\n .allowExcessArguments(false),\n).action(async (name, value, cmdOptions, cmd) => {\n const options = cmd.optsWithGlobals() as DeploymentSelectionOptions &\n EnvDefaultExtraOptions;\n const { ctx, backend } = await resolveEnvDefaultBackend(options);\n await ensureHasConvexDependency(ctx, \"env default set\");\n const didAnything = await envSet(ctx, backend, name, value, cmdOptions);\n if (didAnything === false) {\n cmd.outputHelp({ error: true });\n return await ctx.crash({\n exitCode: 1,\n errorType: \"fatal\",\n printedMessage: \"error: No environment variables specified to be set.\",\n });\n }\n});\n\nconst envDefaultGet = addEnvDefaultOptions(\n new Command(\"get\")\n .argument(\n \"<name>\",\n \"The name of the default environment variable to print.\",\n )\n .summary(\"Print a default variable's value\")\n .description(\n \"Print a default variable's value: `npx convex env default get NAME`\\n\" +\n \"The deployment type is determined by the current deployment (local maps to dev), or by --type if provided.\",\n )\n .configureHelp({ showGlobalOptions: true })\n .allowExcessArguments(false),\n).action(async (envVarName, _options, cmd) => {\n const options = cmd.optsWithGlobals() as DeploymentSelectionOptions &\n EnvDefaultExtraOptions;\n const { ctx, backend } = await resolveEnvDefaultBackend(options);\n await ensureHasConvexDependency(ctx, \"env default get\");\n await envGet(ctx, backend, envVarName);\n});\n\nconst envDefaultRemove = addEnvDefaultOptions(\n new Command(\"remove\")\n .alias(\"rm\")\n .alias(\"unset\")\n .argument(\n \"<name>\",\n \"The name of the default environment variable to unset.\",\n )\n .summary(\"Unset a default variable\")\n .description(\n [\n \"Unset a default variable.\",\n \"\",\n \"\u2022 `npx convex env default remove NAME`\",\n \"\",\n \"If the variable doesn't exist, the command doesn't do anything and succeeds.\",\n \"\",\n \"The deployment type is determined by the current deployment (local maps to dev), or by --type if provided.\",\n ].join(\"\\n\"),\n )\n .configureHelp({ showGlobalOptions: true })\n .allowExcessArguments(false),\n).action(async (name, _options, cmd) => {\n const options = cmd.optsWithGlobals() as DeploymentSelectionOptions &\n EnvDefaultExtraOptions;\n const { ctx, backend } = await resolveEnvDefaultBackend(options);\n await ensureHasConvexDependency(ctx, \"env default remove\");\n await envRemove(ctx, backend, name);\n});\n\nconst envDefaultList = addEnvDefaultOptions(\n new Command(\"list\")\n .summary(\"List all default variables\")\n .description(\n [\n \"List all default variables.\",\n \"\",\n \"\u2022 `npx convex env default list`\",\n \"\",\n \"The deployment type is determined by the current deployment (local maps to dev), or by --type if provided.\",\n ].join(\"\\n\"),\n )\n .configureHelp({ showGlobalOptions: true })\n .allowExcessArguments(false),\n).action(async (_options, cmd) => {\n const options = cmd.optsWithGlobals() as DeploymentSelectionOptions &\n EnvDefaultExtraOptions;\n const { ctx, backend } = await resolveEnvDefaultBackend(options);\n await ensureHasConvexDependency(ctx, \"env default list\");\n await envList(ctx, backend);\n});\n\nexport const envDefault = new Command(\"default\")\n .summary(\"Manage project-level default environment variables\")\n .description(\n [\n \"Manage default environment variables for your project.\",\n \"\",\n \"The default environment variables read and written to by this command are the ones for the deployment type of the current deployment (i.e. dev in most cases), unless --type is provided.\",\n \"\",\n \"\u2022 Set a default variable: `npx convex env default set NAME 'value'`\",\n \"\u2022 Unset a default variable: `npx convex env default remove NAME`\",\n \"\u2022 List all default variables: `npx convex env default list`\",\n \"\u2022 Print a default variable's value: `npx convex env default get NAME`\",\n ].join(\"\\n\"),\n )\n .addCommand(envDefaultSet)\n .addCommand(envDefaultGet)\n .addCommand(envDefaultRemove)\n .addCommand(envDefaultList)\n .helpCommand(false);\n\ntype ParsedProjectOption =\n | { kind: \"teamAndProject\"; teamSlug: string; projectSlug: string }\n | { kind: \"projectOnly\"; projectSlug: string };\n\nasync function resolveEnvDefaultBackend(\n options: DeploymentSelectionOptions & EnvDefaultExtraOptions,\n): Promise<{ ctx: Context; backend: EnvVarBackend }> {\n const dtypeOverride = normalizeTypeOption(options.type);\n\n if (options.project !== undefined) {\n const parsedProject = parseProjectOption(options.project);\n if (parsedProject === null) {\n const ctx = await oneoffContext(options);\n return await ctx.crash({\n exitCode: 1,\n errorType: \"fatal\",\n printedMessage:\n \"error: --project must be `team-slug:project-slug` or `project-slug`.\",\n });\n }\n if (dtypeOverride === undefined) {\n const ctx = await oneoffContext(options);\n return await ctx.crash({\n exitCode: 1,\n errorType: \"fatal\",\n printedMessage: \"error: --project requires --type to also be set.\",\n });\n }\n\n let ctx: Context;\n let resolved: { teamSlug: string; projectSlug: string };\n if (parsedProject.kind === \"teamAndProject\") {\n ctx = await oneoffContext(options);\n resolved = {\n teamSlug: parsedProject.teamSlug,\n projectSlug: parsedProject.projectSlug,\n };\n } else {\n const selected = await selectEnvDeployment(options);\n ctx = selected.ctx;\n if (selected.deployment.deploymentFields === null) {\n return await ctx.crash({\n exitCode: 1,\n errorType: \"fatal\",\n printedMessage:\n \"error: --project <project-slug> requires a current cloud deployment to infer the team from. Use `team-slug:project-slug` to specify the team explicitly.\",\n });\n }\n const { team } = await fetchTeamAndProject(\n ctx,\n selected.deployment.deploymentFields.deploymentName,\n );\n resolved = { teamSlug: team, projectSlug: parsedProject.projectSlug };\n }\n\n const details = await getProjectDetails(ctx, {\n kind: \"teamAndProjectSlugs\",\n teamSlug: resolved.teamSlug,\n projectSlug: resolved.projectSlug,\n });\n return {\n ctx,\n backend: defaultEnvBackend(ctx, details.id, dtypeOverride),\n };\n }\n\n const { ctx, deployment } = await selectEnvDeployment(options);\n const backend = await resolveDefaultEnvBackend(\n ctx,\n deployment.deploymentFields,\n dtypeOverride,\n );\n return { ctx, backend };\n}\n\nfunction normalizeTypeOption(\n type: string | undefined,\n): CloudDeploymentType | undefined {\n if (type === undefined) return undefined;\n if (type === \"development\") return \"dev\";\n if (type === \"production\") return \"prod\";\n return type as CloudDeploymentType;\n}\n\nfunction parseProjectOption(value: string): ParsedProjectOption | null {\n const parts = value.split(\":\");\n if (parts.length === 1 && parts[0].length > 0) {\n return { kind: \"projectOnly\", projectSlug: parts[0] };\n }\n if (parts.length === 2 && parts[0].length > 0 && parts[1].length > 0) {\n return {\n kind: \"teamAndProject\",\n teamSlug: parts[0],\n projectSlug: parts[1],\n };\n }\n return null;\n}\n\nexport async function resolveDefaultEnvBackend(\n ctx: Context,\n deploymentFields: {\n deploymentName: string;\n deploymentType: DeploymentType;\n } | null,\n dtypeOverride?: CloudDeploymentType,\n): Promise<EnvVarBackend> {\n if (deploymentFields === null) {\n return await ctx.crash({\n exitCode: 1,\n errorType: \"fatal\",\n printedMessage:\n \"Default environment variables are only available for cloud projects.\",\n });\n }\n if (deploymentFields.deploymentType === \"anonymous\") {\n return await ctx.crash({\n exitCode: 1,\n errorType: \"fatal\",\n printedMessage:\n \"Default environment variables are not available for anonymous deployments.\",\n });\n }\n const dtype =\n dtypeOverride ?? resolveDefaultEnvDtype(deploymentFields.deploymentType);\n const { projectId } = await fetchTeamAndProject(\n ctx,\n deploymentFields.deploymentName,\n );\n return defaultEnvBackend(ctx, projectId, dtype);\n}\n\nfunction resolveDefaultEnvDtype(\n deploymentType: Exclude<DeploymentType, \"anonymous\">,\n): CloudDeploymentType {\n if (deploymentType === \"local\") return \"dev\";\n return deploymentType;\n}\n"],
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,2BAAgC;AAChC,iBAMO;AACP,mBAA0C;AAC1C,wBAAkC;AAClC,iBAKO;AACP,qBAAuC;AACvC,IAAAA,cAAoC;AACpC,iCAAkC;AAOlC,SAAS,qBAAkD,KAAW;AACpE,SAAO,IACJ;AAAA,IACC,IAAI;AAAA,MACF;AAAA,MACA;AAAA,IACF;AAAA,EACF,EACC;AAAA,IACC,IAAI;AAAA,MACF;AAAA,MACA;AAAA,IACF;AAAA,EACF;AACJ;AAEA,MAAM,gBAAgB;AAAA,EACpB,IAAI,6BAAQ,KAAK,EACd,MAAM,0BAA0B,EAChC,SAAS,UAAU,sDAAsD,EACzE;AAAA,IACC;AAAA,IACA;AAAA,EACF,EACC,QAAQ,wBAAwB,EAChC;AAAA,IACC;AAAA,MACE;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF,EAAE,KAAK,IAAI;AAAA,EACb,EACC;AAAA,IACC;AAAA,IACA;AAAA,EACF,EACC;AAAA,IACC;AAAA,IACA;AAAA,EACF,EACC,cAAc,EAAE,mBAAmB,KAAK,CAAC,EACzC,qBAAqB,KAAK;AAC/B,EAAE,OAAO,OAAO,MAAM,OAAO,YAAY,QAAQ;AAC/C,QAAM,UAAU,IAAI,gBAAgB;AAEpC,QAAM,EAAE,KAAK,QAAQ,IAAI,MAAM,yBAAyB,OAAO;AAC/D,YAAM,wCAA0B,KAAK,iBAAiB;AACtD,QAAM,cAAc,UAAM,mBAAO,KAAK,SAAS,MAAM,OAAO,UAAU;AACtE,MAAI,gBAAgB,OAAO;AACzB,QAAI,WAAW,EAAE,OAAO,KAAK,CAAC;AAC9B,WAAO,MAAM,IAAI,MAAM;AAAA,MACrB,UAAU;AAAA,MACV,WAAW;AAAA,MACX,gBAAgB;AAAA,IAClB,CAAC;AAAA,EACH;AACF,CAAC;AAED,MAAM,gBAAgB;AAAA,EACpB,IAAI,6BAAQ,KAAK,EACd;AAAA,IACC;AAAA,IACA;AAAA,EACF,EACC,QAAQ,kCAAkC,EAC1C;AAAA,IACC;AAAA,EAEF,EACC,cAAc,EAAE,mBAAmB,KAAK,CAAC,EACzC,qBAAqB,KAAK;AAC/B,EAAE,OAAO,OAAO,YAAY,UAAU,QAAQ;AAC5C,QAAM,UAAU,IAAI,gBAAgB;AAEpC,QAAM,EAAE,KAAK,QAAQ,IAAI,MAAM,yBAAyB,OAAO;AAC/D,YAAM,wCAA0B,KAAK,iBAAiB;AACtD,YAAM,mBAAO,KAAK,SAAS,UAAU;AACvC,CAAC;AAED,MAAM,mBAAmB;AAAA,EACvB,IAAI,6BAAQ,QAAQ,EACjB,MAAM,IAAI,EACV,MAAM,OAAO,EACb;AAAA,IACC;AAAA,IACA;AAAA,EACF,EACC,QAAQ,0BAA0B,EAClC;AAAA,IACC;AAAA,MACE;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF,EAAE,KAAK,IAAI;AAAA,EACb,EACC,cAAc,EAAE,mBAAmB,KAAK,CAAC,EACzC,qBAAqB,KAAK;AAC/B,EAAE,OAAO,OAAO,MAAM,UAAU,QAAQ;AACtC,QAAM,UAAU,IAAI,gBAAgB;AAEpC,QAAM,EAAE,KAAK,QAAQ,IAAI,MAAM,yBAAyB,OAAO;AAC/D,YAAM,wCAA0B,KAAK,oBAAoB;AACzD,YAAM,sBAAU,KAAK,SAAS,IAAI;AACpC,CAAC;AAED,MAAM,iBAAiB;AAAA,EACrB,IAAI,6BAAQ,MAAM,EACf,QAAQ,
|
|
4
|
+
"sourcesContent": ["import { Command, Option } from \"@commander-js/extra-typings\";\nimport {\n envGet,\n envList,\n envRemove,\n envSet,\n EnvVarBackend,\n} from \"./lib/env.js\";\nimport { ensureHasConvexDependency } from \"./lib/utils/utils.js\";\nimport { defaultEnvBackend } from \"./lib/defaultEnv.js\";\nimport {\n CloudDeploymentType,\n DeploymentSelectionOptions,\n DeploymentType,\n fetchTeamAndProject,\n} from \"./lib/api.js\";\nimport { Context, oneoffContext } from \"../bundler/context.js\";\nimport { selectEnvDeployment } from \"./env.js\";\nimport { getProjectDetails } from \"./lib/deploymentSelection.js\";\n\ntype EnvDefaultExtraOptions = {\n type?: string;\n project?: string;\n};\n\nfunction addEnvDefaultOptions<T extends Command<any, any>>(cmd: T): T {\n return cmd\n .addOption(\n new Option(\n \"--type <type>\",\n \"Manage default env vars for the given deployment type (dev, preview, prod) instead of inferring from the current deployment.\",\n ),\n )\n .addOption(\n new Option(\n \"--project <project>\",\n \"Select a project manually. Accepts `team-slug:project-slug` or just `project-slug` (team inferred from your current project). Requires --type.\",\n ),\n ) as T;\n}\n\nconst envDefaultSet = addEnvDefaultOptions(\n new Command(\"set\")\n .usage(\"[options] <name> <value>\")\n .argument(\"[name]\", \"The name of the default environment variable to set.\")\n .argument(\n \"[value]\",\n \"The value to set the variable to. Omit to set it interactively.\",\n )\n .summary(\"Set a default variable\")\n .description(\n [\n \"Set default environment variables for your project's deployment type.\",\n \"\",\n \"\u2022 `npx convex env default set NAME 'value'`\",\n \"\u2022 `npx convex env default set NAME # omit a value to set one interactively`\",\n \"\u2022 `npx convex env default set NAME --from-file value.txt`\",\n \"\u2022 `npx convex env default set --from-file .env.defaults`\",\n \"\",\n \"When setting multiple values, it will refuse all changes if any variables are already set to different values by default. Pass --force to overwrite the provided values.\",\n \"\",\n \"The deployment type is determined by the current deployment (local maps to dev), or by --type if provided.\",\n ].join(\"\\n\"),\n )\n .option(\n \"--from-file <file>\",\n \"Read environment variables from a .env file. Without --force, fails if any existing variable has a different value.\",\n )\n .option(\n \"--force\",\n \"When setting multiple variables, overwrite existing environment variable values instead of failing on mismatch.\",\n )\n .configureHelp({ showGlobalOptions: true })\n .allowExcessArguments(false),\n).action(async (name, value, cmdOptions, cmd) => {\n const options = cmd.optsWithGlobals() as DeploymentSelectionOptions &\n EnvDefaultExtraOptions;\n const { ctx, backend } = await resolveEnvDefaultBackend(options);\n await ensureHasConvexDependency(ctx, \"env default set\");\n const didAnything = await envSet(ctx, backend, name, value, cmdOptions);\n if (didAnything === false) {\n cmd.outputHelp({ error: true });\n return await ctx.crash({\n exitCode: 1,\n errorType: \"fatal\",\n printedMessage: \"error: No environment variables specified to be set.\",\n });\n }\n});\n\nconst envDefaultGet = addEnvDefaultOptions(\n new Command(\"get\")\n .argument(\n \"<name>\",\n \"The name of the default environment variable to print.\",\n )\n .summary(\"Print a default variable's value\")\n .description(\n \"Print a default variable's value: `npx convex env default get NAME`\\n\" +\n \"The deployment type is determined by the current deployment (local maps to dev), or by --type if provided.\",\n )\n .configureHelp({ showGlobalOptions: true })\n .allowExcessArguments(false),\n).action(async (envVarName, _options, cmd) => {\n const options = cmd.optsWithGlobals() as DeploymentSelectionOptions &\n EnvDefaultExtraOptions;\n const { ctx, backend } = await resolveEnvDefaultBackend(options);\n await ensureHasConvexDependency(ctx, \"env default get\");\n await envGet(ctx, backend, envVarName);\n});\n\nconst envDefaultRemove = addEnvDefaultOptions(\n new Command(\"remove\")\n .alias(\"rm\")\n .alias(\"unset\")\n .argument(\n \"<name>\",\n \"The name of the default environment variable to unset.\",\n )\n .summary(\"Unset a default variable\")\n .description(\n [\n \"Unset a default variable.\",\n \"\",\n \"\u2022 `npx convex env default remove NAME`\",\n \"\",\n \"If the variable doesn't exist, the command doesn't do anything and succeeds.\",\n \"\",\n \"The deployment type is determined by the current deployment (local maps to dev), or by --type if provided.\",\n ].join(\"\\n\"),\n )\n .configureHelp({ showGlobalOptions: true })\n .allowExcessArguments(false),\n).action(async (name, _options, cmd) => {\n const options = cmd.optsWithGlobals() as DeploymentSelectionOptions &\n EnvDefaultExtraOptions;\n const { ctx, backend } = await resolveEnvDefaultBackend(options);\n await ensureHasConvexDependency(ctx, \"env default remove\");\n await envRemove(ctx, backend, name);\n});\n\nconst envDefaultList = addEnvDefaultOptions(\n new Command(\"list\")\n .summary(\"List all default environment variables and their values\")\n .description(\n [\n \"\u2022 List all default variables and their values: `npx convex env default list`\",\n \"\u2022 List only default variable names (no values): `npx convex env default list --names-only`\",\n \"\",\n \"The deployment type is determined by the current deployment (local maps to dev), or by --type if provided.\",\n ].join(\"\\n\"),\n )\n .option(\n \"--names-only\",\n \"List only the names of environment variables, without their values\",\n )\n .configureHelp({ showGlobalOptions: true })\n .allowExcessArguments(false),\n).action(async (cmdOptions, cmd) => {\n const options = cmd.optsWithGlobals() as DeploymentSelectionOptions &\n EnvDefaultExtraOptions;\n const { ctx, backend } = await resolveEnvDefaultBackend(options);\n await ensureHasConvexDependency(ctx, \"env default list\");\n await envList(ctx, backend, {\n namesOnly: cmdOptions.namesOnly ?? false,\n });\n});\n\nexport const envDefault = new Command(\"default\")\n .summary(\"Manage project-level default environment variables\")\n .description(\n [\n \"Manage default environment variables for your project.\",\n \"\",\n \"The default environment variables read and written to by this command are the ones for the deployment type of the current deployment (i.e. dev in most cases), unless --type is provided.\",\n \"\",\n \"\u2022 Set a default variable: `npx convex env default set NAME 'value'`\",\n \"\u2022 Unset a default variable: `npx convex env default remove NAME`\",\n \"\u2022 List all default variables and their values: `npx convex env default list`\",\n \"\u2022 List only default variable names (no values): `npx convex env default list --names-only`\",\n \"\u2022 Print a default variable's value: `npx convex env default get NAME`\",\n ].join(\"\\n\"),\n )\n .addCommand(envDefaultSet)\n .addCommand(envDefaultGet)\n .addCommand(envDefaultRemove)\n .addCommand(envDefaultList)\n .helpCommand(false);\n\ntype ParsedProjectOption =\n | { kind: \"teamAndProject\"; teamSlug: string; projectSlug: string }\n | { kind: \"projectOnly\"; projectSlug: string };\n\nasync function resolveEnvDefaultBackend(\n options: DeploymentSelectionOptions & EnvDefaultExtraOptions,\n): Promise<{ ctx: Context; backend: EnvVarBackend }> {\n const dtypeOverride = normalizeTypeOption(options.type);\n\n if (options.project !== undefined) {\n const parsedProject = parseProjectOption(options.project);\n if (parsedProject === null) {\n const ctx = await oneoffContext(options);\n return await ctx.crash({\n exitCode: 1,\n errorType: \"fatal\",\n printedMessage:\n \"error: --project must be `team-slug:project-slug` or `project-slug`.\",\n });\n }\n if (dtypeOverride === undefined) {\n const ctx = await oneoffContext(options);\n return await ctx.crash({\n exitCode: 1,\n errorType: \"fatal\",\n printedMessage: \"error: --project requires --type to also be set.\",\n });\n }\n\n let ctx: Context;\n let resolved: { teamSlug: string; projectSlug: string };\n if (parsedProject.kind === \"teamAndProject\") {\n ctx = await oneoffContext(options);\n resolved = {\n teamSlug: parsedProject.teamSlug,\n projectSlug: parsedProject.projectSlug,\n };\n } else {\n const selected = await selectEnvDeployment(options);\n ctx = selected.ctx;\n if (selected.deployment.deploymentFields === null) {\n return await ctx.crash({\n exitCode: 1,\n errorType: \"fatal\",\n printedMessage:\n \"error: --project <project-slug> requires a current cloud deployment to infer the team from. Use `team-slug:project-slug` to specify the team explicitly.\",\n });\n }\n const { team } = await fetchTeamAndProject(\n ctx,\n selected.deployment.deploymentFields.deploymentName,\n );\n resolved = { teamSlug: team, projectSlug: parsedProject.projectSlug };\n }\n\n const details = await getProjectDetails(ctx, {\n kind: \"teamAndProjectSlugs\",\n teamSlug: resolved.teamSlug,\n projectSlug: resolved.projectSlug,\n });\n return {\n ctx,\n backend: defaultEnvBackend(ctx, details.id, dtypeOverride),\n };\n }\n\n const { ctx, deployment } = await selectEnvDeployment(options);\n const backend = await resolveDefaultEnvBackend(\n ctx,\n deployment.deploymentFields,\n dtypeOverride,\n );\n return { ctx, backend };\n}\n\nfunction normalizeTypeOption(\n type: string | undefined,\n): CloudDeploymentType | undefined {\n if (type === undefined) return undefined;\n if (type === \"development\") return \"dev\";\n if (type === \"production\") return \"prod\";\n return type as CloudDeploymentType;\n}\n\nfunction parseProjectOption(value: string): ParsedProjectOption | null {\n const parts = value.split(\":\");\n if (parts.length === 1 && parts[0].length > 0) {\n return { kind: \"projectOnly\", projectSlug: parts[0] };\n }\n if (parts.length === 2 && parts[0].length > 0 && parts[1].length > 0) {\n return {\n kind: \"teamAndProject\",\n teamSlug: parts[0],\n projectSlug: parts[1],\n };\n }\n return null;\n}\n\nexport async function resolveDefaultEnvBackend(\n ctx: Context,\n deploymentFields: {\n deploymentName: string;\n deploymentType: DeploymentType;\n } | null,\n dtypeOverride?: CloudDeploymentType,\n): Promise<EnvVarBackend> {\n if (deploymentFields === null) {\n return await ctx.crash({\n exitCode: 1,\n errorType: \"fatal\",\n printedMessage:\n \"Default environment variables are only available for cloud projects.\",\n });\n }\n if (deploymentFields.deploymentType === \"anonymous\") {\n return await ctx.crash({\n exitCode: 1,\n errorType: \"fatal\",\n printedMessage:\n \"Default environment variables are not available for anonymous deployments.\",\n });\n }\n const dtype =\n dtypeOverride ?? resolveDefaultEnvDtype(deploymentFields.deploymentType);\n const { projectId } = await fetchTeamAndProject(\n ctx,\n deploymentFields.deploymentName,\n );\n return defaultEnvBackend(ctx, projectId, dtype);\n}\n\nfunction resolveDefaultEnvDtype(\n deploymentType: Exclude<DeploymentType, \"anonymous\">,\n): CloudDeploymentType {\n if (deploymentType === \"local\") return \"dev\";\n return deploymentType;\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["import_env", "ctx"]
|
|
7
7
|
}
|
package/dist/cjs/cli/insights.js
CHANGED
|
@@ -109,7 +109,7 @@ const insights = new import_extra_typings.Command("insights").summary("Show heal
|
|
|
109
109
|
"",
|
|
110
110
|
"This command is only available for Convex cloud deployments when logged in as a user."
|
|
111
111
|
].join("\n")
|
|
112
|
-
).allowExcessArguments(false).option("--details", "Show recent events for each insight", false).addDeploymentSelectionOptions((0, import_command.actionDescription)("Show insights for")).showHelpAfterError().action(async (cmdOptions) => {
|
|
112
|
+
).allowExcessArguments(false).option("--details", "Show recent events for each insight", false).option("--json", "Output insights as JSON", false).addDeploymentSelectionOptions((0, import_command.actionDescription)("Show insights for")).showHelpAfterError().action(async (cmdOptions) => {
|
|
113
113
|
const ctx = await (0, import_context.oneoffContext)(cmdOptions);
|
|
114
114
|
const deploymentSelection = await (0, import_deploymentSelection.getDeploymentSelection)(ctx, cmdOptions);
|
|
115
115
|
const credentials = await (0, import_api.loadSelectedDeploymentCredentials)(
|
|
@@ -139,6 +139,16 @@ const insights = new import_extra_typings.Command("insights").summary("Show heal
|
|
|
139
139
|
deploymentName,
|
|
140
140
|
"?view=insights"
|
|
141
141
|
);
|
|
142
|
+
if (cmdOptions.json) {
|
|
143
|
+
(0, import_log.logOutput)(
|
|
144
|
+
JSON.stringify(
|
|
145
|
+
{ deploymentName, dashboardUrl, insights: insightsList },
|
|
146
|
+
null,
|
|
147
|
+
2
|
|
148
|
+
)
|
|
149
|
+
);
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
142
152
|
if (insightsList.length === 0) {
|
|
143
153
|
(0, import_log.logOutput)(
|
|
144
154
|
import_chalk.default.green(
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/cli/insights.ts"],
|
|
4
|
-
"sourcesContent": ["import { Command } from \"@commander-js/extra-typings\";\n// eslint-disable-next-line no-restricted-imports -- stdout output uses default chalk\nimport chalk from \"chalk\";\nimport { oneoffContext } from \"../bundler/context.js\";\nimport { logOutput } from \"../bundler/log.js\";\nimport { loadSelectedDeploymentCredentials } from \"./lib/api.js\";\nimport { actionDescription } from \"./lib/command.js\";\nimport { deploymentDashboardUrlPage } from \"./lib/dashboard.js\";\nimport { getDeploymentSelection } from \"./lib/deploymentSelection.js\";\nimport { type Insight, fetchInsights } from \"./lib/insights.js\";\n\nfunction formatInsightKind(kind: string): string {\n switch (kind) {\n case \"occRetried\":\n return \"OCC Retried\";\n case \"occFailedPermanently\":\n return \"OCC Failed Permanently\";\n case \"bytesReadLimit\":\n return \"Bytes Read Limit Exceeded\";\n case \"bytesReadThreshold\":\n return \"Bytes Read Near Limit\";\n case \"documentsReadLimit\":\n return \"Documents Read Limit Exceeded\";\n case \"documentsReadThreshold\":\n return \"Documents Read Near Limit\";\n default:\n return kind;\n }\n}\n\nfunction formatFunctionName(insight: Insight): string {\n if (insight.componentPath) {\n return `${insight.componentPath}:${insight.functionId}`;\n }\n return insight.functionId;\n}\n\nfunction formatInsight(insight: Insight, details: boolean): string {\n const severity =\n insight.severity === \"error\"\n ? chalk.red(`[ERROR]`)\n : chalk.yellow(`[WARNING]`);\n const kind = formatInsightKind(insight.kind);\n const fn = chalk.bold(formatFunctionName(insight));\n\n let detail: string;\n if (\"occCalls\" in insight) {\n const table = insight.occTableName\n ? ` on table ${chalk.cyan(insight.occTableName)}`\n : \"\";\n detail = `${insight.occCalls} OCC conflict${insight.occCalls !== 1 ? \"s\" : \"\"}${table}`;\n } else {\n detail = `${insight.count} occurrence${insight.count !== 1 ? \"s\" : \"\"}`;\n }\n\n let output = `${severity} ${kind}: ${fn} \u2014 ${detail}`;\n\n if (details && insight.recentEvents && insight.recentEvents.length > 0) {\n output += \"\\n\";\n for (const event of insight.recentEvents) {\n const time = chalk.dim(new Date(event.timestamp).toLocaleString());\n const reqId = chalk.dim(`req:${event.request_id}`);\n\n if (\"occ_retry_count\" in event) {\n const docId = event.occ_document_id\n ? ` doc:${event.occ_document_id}`\n : \"\";\n const source = event.occ_write_source\n ? ` source:${event.occ_write_source}`\n : \"\";\n output += ` ${time} ${reqId} retries:${event.occ_retry_count}${docId}${source}\\n`;\n } else {\n const status = event.success ? chalk.green(\"ok\") : chalk.red(\"fail\");\n const calls = event.calls\n .map(\n (c) =>\n `${c.table_name}(${c.documents_read} docs, ${c.bytes_read} bytes)`,\n )\n .join(\", \");\n output += ` ${time} ${reqId} ${status} ${calls}\\n`;\n }\n }\n }\n\n return output;\n}\n\nexport const insights = new Command(\"insights\")\n .summary(\"Show health insights for your deployment\")\n .description(\n [\n \"Show health insights for a Convex deployment over the last 72 hours.\",\n \"Displays OCC conflicts and resource limit issues that may indicate performance problems.\",\n \"\",\n \"\u2022 Show insights: `npx convex insights`\",\n \"\u2022 Include recent events for each insight: `npx convex insights --details`\",\n \"\u2022 Check the production deployment: `npx convex insights --prod`\",\n \"\",\n \"This command is only available for Convex cloud deployments when logged in as a user.\",\n ].join(\"\\n\"),\n )\n .allowExcessArguments(false)\n .option(\"--details\", \"Show recent events for each insight\", false)\n .addDeploymentSelectionOptions(actionDescription(\"Show insights for\"))\n .showHelpAfterError()\n .action(async (cmdOptions) => {\n const ctx = await oneoffContext(cmdOptions);\n\n const deploymentSelection = await getDeploymentSelection(ctx, cmdOptions);\n const credentials = await loadSelectedDeploymentCredentials(\n ctx,\n deploymentSelection,\n );\n\n const deploymentName = credentials.deploymentFields?.deploymentName ?? null;\n if (deploymentName === null) {\n return await ctx.crash({\n exitCode: 1,\n errorType: \"fatal\",\n printedMessage:\n \"Insights are only available for cloud deployments. Local deployments do not have insights data.\",\n });\n }\n\n const auth = ctx.bigBrainAuth();\n if (\n auth === null ||\n auth.kind === \"deploymentKey\" ||\n auth.kind === \"projectKey\"\n ) {\n return await ctx.crash({\n exitCode: 1,\n errorType: \"fatal\",\n printedMessage:\n \"Insights require to be logged in as a user. Deploy keys and project keys cannot access team usage data.\",\n });\n }\n\n const insightsList = await fetchInsights(ctx, deploymentName, {\n includeRecentEvents: cmdOptions.details,\n });\n\n const dashboardUrl = deploymentDashboardUrlPage(\n deploymentName,\n \"?view=insights\",\n );\n\n if (insightsList.length === 0) {\n logOutput(\n chalk.green(\n \"No issues found. The deployment is healthy over the last 72 hours.\",\n ),\n );\n } else {\n const errorCount = insightsList.filter(\n (i) => i.severity === \"error\",\n ).length;\n const warningCount = insightsList.filter(\n (i) => i.severity === \"warning\",\n ).length;\n\n const parts: string[] = [];\n if (errorCount > 0)\n parts.push(\n chalk.red(`${errorCount} error${errorCount > 1 ? \"s\" : \"\"}`),\n );\n if (warningCount > 0)\n parts.push(\n chalk.yellow(`${warningCount} warning${warningCount > 1 ? \"s\" : \"\"}`),\n );\n logOutput(`Found ${parts.join(\" and \")} in the last 72 hours:\\n`);\n\n for (const insight of insightsList) {\n logOutput(formatInsight(insight, cmdOptions.details));\n }\n }\n\n logOutput(`\\nDashboard: ${chalk.cyan(dashboardUrl)}`);\n });\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["import { Command } from \"@commander-js/extra-typings\";\n// eslint-disable-next-line no-restricted-imports -- stdout output uses default chalk\nimport chalk from \"chalk\";\nimport { oneoffContext } from \"../bundler/context.js\";\nimport { logOutput } from \"../bundler/log.js\";\nimport { loadSelectedDeploymentCredentials } from \"./lib/api.js\";\nimport { actionDescription } from \"./lib/command.js\";\nimport { deploymentDashboardUrlPage } from \"./lib/dashboard.js\";\nimport { getDeploymentSelection } from \"./lib/deploymentSelection.js\";\nimport { type Insight, fetchInsights } from \"./lib/insights.js\";\n\nfunction formatInsightKind(kind: string): string {\n switch (kind) {\n case \"occRetried\":\n return \"OCC Retried\";\n case \"occFailedPermanently\":\n return \"OCC Failed Permanently\";\n case \"bytesReadLimit\":\n return \"Bytes Read Limit Exceeded\";\n case \"bytesReadThreshold\":\n return \"Bytes Read Near Limit\";\n case \"documentsReadLimit\":\n return \"Documents Read Limit Exceeded\";\n case \"documentsReadThreshold\":\n return \"Documents Read Near Limit\";\n default:\n return kind;\n }\n}\n\nfunction formatFunctionName(insight: Insight): string {\n if (insight.componentPath) {\n return `${insight.componentPath}:${insight.functionId}`;\n }\n return insight.functionId;\n}\n\nfunction formatInsight(insight: Insight, details: boolean): string {\n const severity =\n insight.severity === \"error\"\n ? chalk.red(`[ERROR]`)\n : chalk.yellow(`[WARNING]`);\n const kind = formatInsightKind(insight.kind);\n const fn = chalk.bold(formatFunctionName(insight));\n\n let detail: string;\n if (\"occCalls\" in insight) {\n const table = insight.occTableName\n ? ` on table ${chalk.cyan(insight.occTableName)}`\n : \"\";\n detail = `${insight.occCalls} OCC conflict${insight.occCalls !== 1 ? \"s\" : \"\"}${table}`;\n } else {\n detail = `${insight.count} occurrence${insight.count !== 1 ? \"s\" : \"\"}`;\n }\n\n let output = `${severity} ${kind}: ${fn} \u2014 ${detail}`;\n\n if (details && insight.recentEvents && insight.recentEvents.length > 0) {\n output += \"\\n\";\n for (const event of insight.recentEvents) {\n const time = chalk.dim(new Date(event.timestamp).toLocaleString());\n const reqId = chalk.dim(`req:${event.request_id}`);\n\n if (\"occ_retry_count\" in event) {\n const docId = event.occ_document_id\n ? ` doc:${event.occ_document_id}`\n : \"\";\n const source = event.occ_write_source\n ? ` source:${event.occ_write_source}`\n : \"\";\n output += ` ${time} ${reqId} retries:${event.occ_retry_count}${docId}${source}\\n`;\n } else {\n const status = event.success ? chalk.green(\"ok\") : chalk.red(\"fail\");\n const calls = event.calls\n .map(\n (c) =>\n `${c.table_name}(${c.documents_read} docs, ${c.bytes_read} bytes)`,\n )\n .join(\", \");\n output += ` ${time} ${reqId} ${status} ${calls}\\n`;\n }\n }\n }\n\n return output;\n}\n\nexport const insights = new Command(\"insights\")\n .summary(\"Show health insights for your deployment\")\n .description(\n [\n \"Show health insights for a Convex deployment over the last 72 hours.\",\n \"Displays OCC conflicts and resource limit issues that may indicate performance problems.\",\n \"\",\n \"\u2022 Show insights: `npx convex insights`\",\n \"\u2022 Include recent events for each insight: `npx convex insights --details`\",\n \"\u2022 Check the production deployment: `npx convex insights --prod`\",\n \"\",\n \"This command is only available for Convex cloud deployments when logged in as a user.\",\n ].join(\"\\n\"),\n )\n .allowExcessArguments(false)\n .option(\"--details\", \"Show recent events for each insight\", false)\n .option(\"--json\", \"Output insights as JSON\", false)\n .addDeploymentSelectionOptions(actionDescription(\"Show insights for\"))\n .showHelpAfterError()\n .action(async (cmdOptions) => {\n const ctx = await oneoffContext(cmdOptions);\n\n const deploymentSelection = await getDeploymentSelection(ctx, cmdOptions);\n const credentials = await loadSelectedDeploymentCredentials(\n ctx,\n deploymentSelection,\n );\n\n const deploymentName = credentials.deploymentFields?.deploymentName ?? null;\n if (deploymentName === null) {\n return await ctx.crash({\n exitCode: 1,\n errorType: \"fatal\",\n printedMessage:\n \"Insights are only available for cloud deployments. Local deployments do not have insights data.\",\n });\n }\n\n const auth = ctx.bigBrainAuth();\n if (\n auth === null ||\n auth.kind === \"deploymentKey\" ||\n auth.kind === \"projectKey\"\n ) {\n return await ctx.crash({\n exitCode: 1,\n errorType: \"fatal\",\n printedMessage:\n \"Insights require to be logged in as a user. Deploy keys and project keys cannot access team usage data.\",\n });\n }\n\n const insightsList = await fetchInsights(ctx, deploymentName, {\n includeRecentEvents: cmdOptions.details,\n });\n\n const dashboardUrl = deploymentDashboardUrlPage(\n deploymentName,\n \"?view=insights\",\n );\n\n if (cmdOptions.json) {\n logOutput(\n JSON.stringify(\n { deploymentName, dashboardUrl, insights: insightsList },\n null,\n 2,\n ),\n );\n return;\n }\n\n if (insightsList.length === 0) {\n logOutput(\n chalk.green(\n \"No issues found. The deployment is healthy over the last 72 hours.\",\n ),\n );\n } else {\n const errorCount = insightsList.filter(\n (i) => i.severity === \"error\",\n ).length;\n const warningCount = insightsList.filter(\n (i) => i.severity === \"warning\",\n ).length;\n\n const parts: string[] = [];\n if (errorCount > 0)\n parts.push(\n chalk.red(`${errorCount} error${errorCount > 1 ? \"s\" : \"\"}`),\n );\n if (warningCount > 0)\n parts.push(\n chalk.yellow(`${warningCount} warning${warningCount > 1 ? \"s\" : \"\"}`),\n );\n logOutput(`Found ${parts.join(\" and \")} in the last 72 hours:\\n`);\n\n for (const insight of insightsList) {\n logOutput(formatInsight(insight, cmdOptions.details));\n }\n }\n\n logOutput(`\\nDashboard: ${chalk.cyan(dashboardUrl)}`);\n });\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["chalk"]
|
|
7
7
|
}
|
package/dist/cjs/cli/lib/env.js
CHANGED
|
@@ -144,13 +144,17 @@ async function envRemove(ctx, backend, name) {
|
|
|
144
144
|
`Successfully unset ${import_chalk.chalkStderr.bold(name)}${backend.notice}`
|
|
145
145
|
);
|
|
146
146
|
}
|
|
147
|
-
async function envList(ctx, backend) {
|
|
147
|
+
async function envList(ctx, backend, options) {
|
|
148
148
|
const envs = await backend.list();
|
|
149
149
|
if (envs.length === 0) {
|
|
150
150
|
(0, import_log.logMessage)(`No environment variables set${backend.notice}`);
|
|
151
151
|
return;
|
|
152
152
|
}
|
|
153
153
|
for (const { name, value } of envs) {
|
|
154
|
+
if (options?.namesOnly) {
|
|
155
|
+
(0, import_log.logOutput)(name);
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
154
158
|
const { formatted, warning } = (0, import_formatEnvValueForDotfile.formatEnvValueForDotfile)(value);
|
|
155
159
|
if (warning) {
|
|
156
160
|
(0, import_log.logMessage)(`Warning (${name}): ${warning}`);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/cli/lib/env.ts"],
|
|
4
|
-
"sourcesContent": ["import { chalkStderr } from \"chalk\";\nimport * as dotenv from \"dotenv\";\nimport { Context } from \"../../bundler/context.js\";\nimport {\n logFailure,\n logFinishedStep,\n logMessage,\n logOutput,\n} from \"../../bundler/log.js\";\nimport { runSystemQuery } from \"./run.js\";\nimport { deploymentFetch, logAndHandleFetchError } from \"./utils/utils.js\";\nimport { readFromStdin } from \"./utils/stdin.js\";\nimport { promptSecret } from \"./utils/prompts.js\";\nimport {\n EXPECTED_CONVEX_URL_NAMES,\n EXPECTED_SITE_URL_NAMES,\n} from \"./envvars.js\";\nimport { formatEnvValueForDotfile } from \"./formatEnvValueForDotfile.js\";\nimport {\n CONVEX_DEPLOY_KEY_ENV_VAR_NAME,\n CONVEX_DEPLOYMENT_ENV_VAR_NAME,\n CONVEX_DEPLOYMENT_TOKEN_ENV_VAR_NAME,\n CONVEX_SELF_HOSTED_URL_VAR_NAME,\n CONVEX_SELF_HOSTED_ADMIN_KEY_VAR_NAME,\n} from \"./utils/utils.js\";\n\nfunction formatList(items: string[]): string {\n if (items.length === 0) return \"\";\n if (items.length === 1) return items[0];\n if (items.length === 2) return `${items[0]} and ${items[1]}`;\n return `${items.slice(0, -1).join(\", \")}, and ${items[items.length - 1]}`;\n}\n\nexport type EnvVarChange = {\n name: string;\n value: string | null; // null = delete\n};\n\nexport type EnvVar = {\n name: string;\n value: string;\n};\n\nexport interface EnvVarBackend {\n get(name: string): Promise<EnvVar | null>;\n list(): Promise<EnvVar[]>;\n update(changes: EnvVarChange[]): Promise<void>;\n notice: string;\n}\n\nexport function deploymentEnvBackend(\n ctx: Context,\n deployment: {\n deploymentUrl: string;\n adminKey: string;\n deploymentNotice?: string;\n },\n): EnvVarBackend {\n return {\n async get(name) {\n const envVar = (await runSystemQuery(ctx, {\n ...deployment,\n functionName: \"_system/cli/queryEnvironmentVariables:get\",\n componentPath: undefined,\n args: { name },\n })) as EnvVar | null;\n return envVar;\n },\n async list() {\n return (await runSystemQuery(ctx, {\n ...deployment,\n functionName: \"_system/cli/queryEnvironmentVariables\",\n componentPath: undefined,\n args: {},\n })) as EnvVar[];\n },\n async update(changes) {\n const fetch = deploymentFetch(ctx, deployment);\n try {\n await fetch(\"/api/update_environment_variables\", {\n body: JSON.stringify({ changes }),\n method: \"POST\",\n });\n } catch (e) {\n return await logAndHandleFetchError(ctx, e);\n }\n },\n notice: deployment.deploymentNotice ?? \"\",\n };\n}\n\nexport async function envSet(\n ctx: Context,\n backend: EnvVarBackend,\n originalName: string | undefined,\n originalValue: string | undefined,\n options?: {\n fromFile?: string;\n force?: boolean;\n secret?: boolean;\n },\n) {\n const { fromFile, force = false } = options ?? {};\n if (originalName) {\n let name = originalName,\n value: string;\n const parsed = await allowEqualsSyntax(ctx, originalName, originalValue);\n if (parsed) {\n [name, value] = parsed;\n } else if (fromFile) {\n value = await getFileContents(ctx, fromFile);\n } else if (!process.stdin.isTTY) {\n value = await getStdIn(ctx);\n } else {\n value = await promptSecret(ctx, {\n message: `Enter value for ${name}:`,\n });\n }\n await backend.update([{ name, value }]);\n if (options?.secret) {\n const formatted = /\\s/.test(value) ? `\"${value}\"` : value;\n logFinishedStep(\n `Successfully set ${chalkStderr.bold(name)} to ${chalkStderr.bold(formatted)}${backend.notice}`,\n );\n } else {\n logFinishedStep(\n `Successfully set ${chalkStderr.bold(name)}${backend.notice}`,\n );\n }\n return true;\n }\n let content: string, source: string;\n if (fromFile) {\n content = await getFileContents(ctx, fromFile);\n source = fromFile;\n } else if (!process.stdin.isTTY) {\n content = await getStdIn(ctx);\n source = \"stdin\";\n } else {\n return false;\n }\n await envSetFromContent(ctx, backend, {\n content,\n source,\n force,\n });\n return true;\n}\n\nexport async function envGet(\n ctx: Context,\n backend: EnvVarBackend,\n name: string,\n) {\n const envVar = await backend.get(name);\n if (envVar === null) {\n logFailure(`Environment variable \"${name}\" not found${backend.notice}`);\n return;\n }\n logOutput(`${envVar.value}`);\n}\n\nexport async function envRemove(\n ctx: Context,\n backend: EnvVarBackend,\n name: string,\n) {\n await backend.update([{ name, value: null }]);\n logFinishedStep(\n `Successfully unset ${chalkStderr.bold(name)}${backend.notice}`,\n );\n}\n\nexport async function envList(ctx: Context, backend: EnvVarBackend) {\n const envs = await backend.list();\n if (envs.length === 0) {\n logMessage(`No environment variables set${backend.notice}`);\n return;\n }\n for (const { name, value } of envs) {\n const { formatted, warning } = formatEnvValueForDotfile(value);\n if (warning) {\n logMessage(`Warning (${name}): ${warning}`);\n }\n logOutput(`${name}=${formatted}`);\n }\n}\n\nasync function getFileContents(\n ctx: Context,\n filePath: string,\n): Promise<string> {\n if (!ctx.fs.exists(filePath)) {\n return await ctx.crash({\n exitCode: 1,\n errorType: \"fatal\",\n printedMessage: `error: file not found: ${filePath}`,\n });\n }\n return ctx.fs.readUtf8File(filePath);\n}\n\nasync function getStdIn(ctx: Context): Promise<string> {\n try {\n return await readFromStdin();\n } catch (error) {\n return await ctx.crash({\n exitCode: 1,\n errorType: \"fatal\",\n printedMessage: `error: failed to read from stdin: ${error instanceof Error ? error.message : String(error)}`,\n });\n }\n}\n\nasync function envSetFromContent(\n ctx: Context,\n backend: EnvVarBackend,\n options: {\n content: string;\n source: string;\n force: boolean;\n },\n) {\n const { content, source, force } = options;\n const parsedEnv = dotenv.parse(content);\n\n // Filter out CLI-managed environment variables\n const envVars = Object.entries(parsedEnv);\n const filteredVars: string[] = [];\n\n const envVarsToSet: [string, string][] = [];\n const managedVars = new Set<string>([\n CONVEX_DEPLOY_KEY_ENV_VAR_NAME,\n CONVEX_DEPLOYMENT_TOKEN_ENV_VAR_NAME,\n CONVEX_DEPLOYMENT_ENV_VAR_NAME,\n CONVEX_SELF_HOSTED_URL_VAR_NAME,\n CONVEX_SELF_HOSTED_ADMIN_KEY_VAR_NAME,\n ...EXPECTED_CONVEX_URL_NAMES,\n ...EXPECTED_SITE_URL_NAMES,\n ]);\n for (const [name, value] of envVars) {\n if (managedVars.has(name)) {\n filteredVars.push(name);\n } else {\n envVarsToSet.push([name, value]);\n }\n }\n\n if (filteredVars.length > 0) {\n const varNames = filteredVars.map((n) => chalkStderr.bold(n));\n const formattedNames = formatList(varNames);\n logMessage(\n `Skipping ${filteredVars.length} CLI-managed environment variable${filteredVars.length === 1 ? \"\" : \"s\"}: ${formattedNames}`,\n );\n }\n\n if (envVarsToSet.length === 0) {\n if (envVars.length === 0) {\n logMessage(`No environment variables found in ${source}.`);\n }\n return;\n }\n\n // Fetch existing environment variables\n const existingEnvVars = await backend.list();\n\n const existingEnvMap = new Map(\n existingEnvVars.map((env) => [env.name, env.value]),\n );\n\n // Categorize the environment variables\n const newVars: [string, string][] = [];\n const updatedVars: [string, string][] = [];\n const unchangedVars: [string, string][] = [];\n const conflicts: { name: string; existing: string; new: string }[] = [];\n\n for (const [name, value] of envVarsToSet) {\n const existingValue = existingEnvMap.get(name);\n if (existingValue === undefined) {\n newVars.push([name, value]);\n } else if (existingValue === value) {\n unchangedVars.push([name, value]);\n } else if (force) {\n updatedVars.push([name, value]);\n } else {\n conflicts.push({ name, existing: existingValue, new: value });\n }\n }\n\n // Check for conflicts if not replacing\n if (conflicts.length > 0) {\n const varNames = conflicts.map((c) => chalkStderr.bold(c.name));\n const formattedNames = formatList(varNames);\n return await ctx.crash({\n exitCode: 1,\n errorType: \"fatal\",\n printedMessage:\n `error: environment variable${conflicts.length === 1 ? \"\" : \"s\"} ${formattedNames} already exist${conflicts.length === 1 ? \"s\" : \"\"} with different value${conflicts.length === 1 ? \"\" : \"s\"}.\\n\\n` +\n `Use ${chalkStderr.bold(\"--force\")} to overwrite existing values.`,\n });\n }\n\n // Build the changes: only new vars when not replacing, new + updated when replacing\n const varsToUpdate = force ? [...newVars, ...updatedVars] : newVars;\n const changes: EnvVarChange[] = varsToUpdate.map(([name, value]) => ({\n name,\n value,\n }));\n\n if (changes.length > 0) {\n await backend.update(changes);\n }\n\n const newCount = newVars.length;\n const updatedCount = updatedVars.length;\n const unchangedCount = unchangedVars.length;\n\n const parts = [];\n if (newCount > 0) parts.push(`${newCount} new`);\n if (updatedCount > 0) parts.push(`${updatedCount} updated`);\n if (unchangedCount > 0) parts.push(`${unchangedCount} unchanged`);\n\n const totalProcessed = newCount + updatedCount + unchangedCount;\n if (changes.length === 0) {\n logMessage(\n `All ${totalProcessed} environment variable${totalProcessed === 1 ? \"\" : \"s\"} from ${chalkStderr.bold(source)} already set${backend.notice}`,\n );\n } else {\n logFinishedStep(\n `Successfully set ${changes.length} environment variable${changes.length === 1 ? \"\" : \"s\"} from ${chalkStderr.bold(source)} (${parts.join(\", \")})${backend.notice}`,\n );\n }\n}\n\nasync function allowEqualsSyntax(\n ctx: Context,\n name: string,\n value: string | undefined,\n): Promise<[string, string] | null> {\n if (/^[a-zA-Z][a-zA-Z0-9_]*=/.test(name)) {\n const [n, ...values] = name.split(\"=\");\n if (value === undefined) {\n return [n, values.join(\"=\")];\n } else {\n await ctx.crash({\n exitCode: 1,\n errorType: \"fatal\",\n printedMessage: `When setting an environment variable, you can either set a value with 'NAME=value', or with NAME value, but not both. Are you missing quotes around the CLI argument? Try: \\n npx convex env set '${name} ${value}'`,\n });\n }\n }\n if (value === undefined) return null;\n return [name, value];\n}\n"],
|
|
5
|
-
"mappings": "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
|
|
4
|
+
"sourcesContent": ["import { chalkStderr } from \"chalk\";\nimport * as dotenv from \"dotenv\";\nimport { Context } from \"../../bundler/context.js\";\nimport {\n logFailure,\n logFinishedStep,\n logMessage,\n logOutput,\n} from \"../../bundler/log.js\";\nimport { runSystemQuery } from \"./run.js\";\nimport { deploymentFetch, logAndHandleFetchError } from \"./utils/utils.js\";\nimport { readFromStdin } from \"./utils/stdin.js\";\nimport { promptSecret } from \"./utils/prompts.js\";\nimport {\n EXPECTED_CONVEX_URL_NAMES,\n EXPECTED_SITE_URL_NAMES,\n} from \"./envvars.js\";\nimport { formatEnvValueForDotfile } from \"./formatEnvValueForDotfile.js\";\nimport {\n CONVEX_DEPLOY_KEY_ENV_VAR_NAME,\n CONVEX_DEPLOYMENT_ENV_VAR_NAME,\n CONVEX_DEPLOYMENT_TOKEN_ENV_VAR_NAME,\n CONVEX_SELF_HOSTED_URL_VAR_NAME,\n CONVEX_SELF_HOSTED_ADMIN_KEY_VAR_NAME,\n} from \"./utils/utils.js\";\n\nfunction formatList(items: string[]): string {\n if (items.length === 0) return \"\";\n if (items.length === 1) return items[0];\n if (items.length === 2) return `${items[0]} and ${items[1]}`;\n return `${items.slice(0, -1).join(\", \")}, and ${items[items.length - 1]}`;\n}\n\nexport type EnvVarChange = {\n name: string;\n value: string | null; // null = delete\n};\n\nexport type EnvVar = {\n name: string;\n value: string;\n};\n\nexport interface EnvVarBackend {\n get(name: string): Promise<EnvVar | null>;\n list(): Promise<EnvVar[]>;\n update(changes: EnvVarChange[]): Promise<void>;\n notice: string;\n}\n\nexport function deploymentEnvBackend(\n ctx: Context,\n deployment: {\n deploymentUrl: string;\n adminKey: string;\n deploymentNotice?: string;\n },\n): EnvVarBackend {\n return {\n async get(name) {\n const envVar = (await runSystemQuery(ctx, {\n ...deployment,\n functionName: \"_system/cli/queryEnvironmentVariables:get\",\n componentPath: undefined,\n args: { name },\n })) as EnvVar | null;\n return envVar;\n },\n async list() {\n return (await runSystemQuery(ctx, {\n ...deployment,\n functionName: \"_system/cli/queryEnvironmentVariables\",\n componentPath: undefined,\n args: {},\n })) as EnvVar[];\n },\n async update(changes) {\n const fetch = deploymentFetch(ctx, deployment);\n try {\n await fetch(\"/api/update_environment_variables\", {\n body: JSON.stringify({ changes }),\n method: \"POST\",\n });\n } catch (e) {\n return await logAndHandleFetchError(ctx, e);\n }\n },\n notice: deployment.deploymentNotice ?? \"\",\n };\n}\n\nexport async function envSet(\n ctx: Context,\n backend: EnvVarBackend,\n originalName: string | undefined,\n originalValue: string | undefined,\n options?: {\n fromFile?: string;\n force?: boolean;\n secret?: boolean;\n },\n) {\n const { fromFile, force = false } = options ?? {};\n if (originalName) {\n let name = originalName,\n value: string;\n const parsed = await allowEqualsSyntax(ctx, originalName, originalValue);\n if (parsed) {\n [name, value] = parsed;\n } else if (fromFile) {\n value = await getFileContents(ctx, fromFile);\n } else if (!process.stdin.isTTY) {\n value = await getStdIn(ctx);\n } else {\n value = await promptSecret(ctx, {\n message: `Enter value for ${name}:`,\n });\n }\n await backend.update([{ name, value }]);\n if (options?.secret) {\n const formatted = /\\s/.test(value) ? `\"${value}\"` : value;\n logFinishedStep(\n `Successfully set ${chalkStderr.bold(name)} to ${chalkStderr.bold(formatted)}${backend.notice}`,\n );\n } else {\n logFinishedStep(\n `Successfully set ${chalkStderr.bold(name)}${backend.notice}`,\n );\n }\n return true;\n }\n let content: string, source: string;\n if (fromFile) {\n content = await getFileContents(ctx, fromFile);\n source = fromFile;\n } else if (!process.stdin.isTTY) {\n content = await getStdIn(ctx);\n source = \"stdin\";\n } else {\n return false;\n }\n await envSetFromContent(ctx, backend, {\n content,\n source,\n force,\n });\n return true;\n}\n\nexport async function envGet(\n ctx: Context,\n backend: EnvVarBackend,\n name: string,\n) {\n const envVar = await backend.get(name);\n if (envVar === null) {\n logFailure(`Environment variable \"${name}\" not found${backend.notice}`);\n return;\n }\n logOutput(`${envVar.value}`);\n}\n\nexport async function envRemove(\n ctx: Context,\n backend: EnvVarBackend,\n name: string,\n) {\n await backend.update([{ name, value: null }]);\n logFinishedStep(\n `Successfully unset ${chalkStderr.bold(name)}${backend.notice}`,\n );\n}\n\nexport async function envList(\n ctx: Context,\n backend: EnvVarBackend,\n options?: { namesOnly?: boolean },\n) {\n const envs = await backend.list();\n if (envs.length === 0) {\n logMessage(`No environment variables set${backend.notice}`);\n return;\n }\n for (const { name, value } of envs) {\n if (options?.namesOnly) {\n logOutput(name);\n continue;\n }\n const { formatted, warning } = formatEnvValueForDotfile(value);\n if (warning) {\n logMessage(`Warning (${name}): ${warning}`);\n }\n logOutput(`${name}=${formatted}`);\n }\n}\n\nasync function getFileContents(\n ctx: Context,\n filePath: string,\n): Promise<string> {\n if (!ctx.fs.exists(filePath)) {\n return await ctx.crash({\n exitCode: 1,\n errorType: \"fatal\",\n printedMessage: `error: file not found: ${filePath}`,\n });\n }\n return ctx.fs.readUtf8File(filePath);\n}\n\nasync function getStdIn(ctx: Context): Promise<string> {\n try {\n return await readFromStdin();\n } catch (error) {\n return await ctx.crash({\n exitCode: 1,\n errorType: \"fatal\",\n printedMessage: `error: failed to read from stdin: ${error instanceof Error ? error.message : String(error)}`,\n });\n }\n}\n\nasync function envSetFromContent(\n ctx: Context,\n backend: EnvVarBackend,\n options: {\n content: string;\n source: string;\n force: boolean;\n },\n) {\n const { content, source, force } = options;\n const parsedEnv = dotenv.parse(content);\n\n // Filter out CLI-managed environment variables\n const envVars = Object.entries(parsedEnv);\n const filteredVars: string[] = [];\n\n const envVarsToSet: [string, string][] = [];\n const managedVars = new Set<string>([\n CONVEX_DEPLOY_KEY_ENV_VAR_NAME,\n CONVEX_DEPLOYMENT_TOKEN_ENV_VAR_NAME,\n CONVEX_DEPLOYMENT_ENV_VAR_NAME,\n CONVEX_SELF_HOSTED_URL_VAR_NAME,\n CONVEX_SELF_HOSTED_ADMIN_KEY_VAR_NAME,\n ...EXPECTED_CONVEX_URL_NAMES,\n ...EXPECTED_SITE_URL_NAMES,\n ]);\n for (const [name, value] of envVars) {\n if (managedVars.has(name)) {\n filteredVars.push(name);\n } else {\n envVarsToSet.push([name, value]);\n }\n }\n\n if (filteredVars.length > 0) {\n const varNames = filteredVars.map((n) => chalkStderr.bold(n));\n const formattedNames = formatList(varNames);\n logMessage(\n `Skipping ${filteredVars.length} CLI-managed environment variable${filteredVars.length === 1 ? \"\" : \"s\"}: ${formattedNames}`,\n );\n }\n\n if (envVarsToSet.length === 0) {\n if (envVars.length === 0) {\n logMessage(`No environment variables found in ${source}.`);\n }\n return;\n }\n\n // Fetch existing environment variables\n const existingEnvVars = await backend.list();\n\n const existingEnvMap = new Map(\n existingEnvVars.map((env) => [env.name, env.value]),\n );\n\n // Categorize the environment variables\n const newVars: [string, string][] = [];\n const updatedVars: [string, string][] = [];\n const unchangedVars: [string, string][] = [];\n const conflicts: { name: string; existing: string; new: string }[] = [];\n\n for (const [name, value] of envVarsToSet) {\n const existingValue = existingEnvMap.get(name);\n if (existingValue === undefined) {\n newVars.push([name, value]);\n } else if (existingValue === value) {\n unchangedVars.push([name, value]);\n } else if (force) {\n updatedVars.push([name, value]);\n } else {\n conflicts.push({ name, existing: existingValue, new: value });\n }\n }\n\n // Check for conflicts if not replacing\n if (conflicts.length > 0) {\n const varNames = conflicts.map((c) => chalkStderr.bold(c.name));\n const formattedNames = formatList(varNames);\n return await ctx.crash({\n exitCode: 1,\n errorType: \"fatal\",\n printedMessage:\n `error: environment variable${conflicts.length === 1 ? \"\" : \"s\"} ${formattedNames} already exist${conflicts.length === 1 ? \"s\" : \"\"} with different value${conflicts.length === 1 ? \"\" : \"s\"}.\\n\\n` +\n `Use ${chalkStderr.bold(\"--force\")} to overwrite existing values.`,\n });\n }\n\n // Build the changes: only new vars when not replacing, new + updated when replacing\n const varsToUpdate = force ? [...newVars, ...updatedVars] : newVars;\n const changes: EnvVarChange[] = varsToUpdate.map(([name, value]) => ({\n name,\n value,\n }));\n\n if (changes.length > 0) {\n await backend.update(changes);\n }\n\n const newCount = newVars.length;\n const updatedCount = updatedVars.length;\n const unchangedCount = unchangedVars.length;\n\n const parts = [];\n if (newCount > 0) parts.push(`${newCount} new`);\n if (updatedCount > 0) parts.push(`${updatedCount} updated`);\n if (unchangedCount > 0) parts.push(`${unchangedCount} unchanged`);\n\n const totalProcessed = newCount + updatedCount + unchangedCount;\n if (changes.length === 0) {\n logMessage(\n `All ${totalProcessed} environment variable${totalProcessed === 1 ? \"\" : \"s\"} from ${chalkStderr.bold(source)} already set${backend.notice}`,\n );\n } else {\n logFinishedStep(\n `Successfully set ${changes.length} environment variable${changes.length === 1 ? \"\" : \"s\"} from ${chalkStderr.bold(source)} (${parts.join(\", \")})${backend.notice}`,\n );\n }\n}\n\nasync function allowEqualsSyntax(\n ctx: Context,\n name: string,\n value: string | undefined,\n): Promise<[string, string] | null> {\n if (/^[a-zA-Z][a-zA-Z0-9_]*=/.test(name)) {\n const [n, ...values] = name.split(\"=\");\n if (value === undefined) {\n return [n, values.join(\"=\")];\n } else {\n await ctx.crash({\n exitCode: 1,\n errorType: \"fatal\",\n printedMessage: `When setting an environment variable, you can either set a value with 'NAME=value', or with NAME value, but not both. Are you missing quotes around the CLI argument? Try: \\n npx convex env set '${name} ${value}'`,\n });\n }\n }\n if (value === undefined) return null;\n return [name, value];\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["import_utils"]
|
|
7
7
|
}
|
|
@@ -283,18 +283,22 @@ async function performLogin(ctx, {
|
|
|
283
283
|
printedMessage: null
|
|
284
284
|
});
|
|
285
285
|
}
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
};
|
|
291
|
-
const data = await (0, import_utils.bigBrainAPI)({
|
|
292
|
-
ctx,
|
|
293
|
-
method: "POST",
|
|
294
|
-
path: "authorize",
|
|
295
|
-
data: authorizeArgs
|
|
286
|
+
ctx._updateBigBrainAuth({
|
|
287
|
+
accessToken,
|
|
288
|
+
kind: "accessToken",
|
|
289
|
+
header: `Bearer ${accessToken}`
|
|
296
290
|
});
|
|
297
|
-
const
|
|
291
|
+
const response = await (0, import_utils.typedPlatformClient)(ctx).POST(
|
|
292
|
+
"/create_personal_access_token",
|
|
293
|
+
{
|
|
294
|
+
// `anonymousId` links a prior anonymous session to this account. It's
|
|
295
|
+
// intentionally absent from the generated platform API types,
|
|
296
|
+
// so cast to pass it through.
|
|
297
|
+
body: { name: deviceName, anonymousId }
|
|
298
|
+
}
|
|
299
|
+
);
|
|
300
|
+
const newAccessToken = response.data.accessToken;
|
|
301
|
+
const globalConfig = { accessToken: newAccessToken };
|
|
298
302
|
try {
|
|
299
303
|
await (0, import_globalConfig.modifyGlobalConfig)(ctx, globalConfig);
|
|
300
304
|
const path = (0, import_globalConfig.globalConfigPath)();
|
|
@@ -308,7 +312,7 @@ async function performLogin(ctx, {
|
|
|
308
312
|
});
|
|
309
313
|
}
|
|
310
314
|
(0, import_log.logVerbose)(`performLogin: updating big brain auth after login`);
|
|
311
|
-
await (0, import_deploymentSelection.updateBigBrainAuthAfterLogin)(ctx,
|
|
315
|
+
await (0, import_deploymentSelection.updateBigBrainAuthAfterLogin)(ctx, newAccessToken);
|
|
312
316
|
(0, import_log.logVerbose)(`performLogin: checking opt ins, acceptOptIns: ${acceptOptIns}`);
|
|
313
317
|
const shouldContinue = await optins(ctx, acceptOptIns ?? false);
|
|
314
318
|
if (!shouldContinue) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/cli/lib/login.ts"],
|
|
4
|
-
"sourcesContent": ["import { errors, BaseClient, custom } from \"openid-client\";\nimport {\n bigBrainAPI,\n bigBrainFetch,\n logAndHandleFetchError,\n throwingFetch,\n isWebContainer,\n} from \"./utils/utils.js\";\nimport open from \"open\";\nimport { chalkStderr } from \"chalk\";\nimport { provisionHost } from \"./config.js\";\nimport { version } from \"../version.js\";\nimport { Context } from \"../../bundler/context.js\";\nimport {\n changeSpinner,\n logError,\n logFailure,\n logFinishedStep,\n logMessage,\n logOutput,\n logVerbose,\n showSpinner,\n} from \"../../bundler/log.js\";\nimport { Issuer } from \"openid-client\";\nimport { hostname } from \"os\";\nimport { execSync } from \"child_process\";\nimport { promptString, promptYesNo } from \"./utils/prompts.js\";\nimport {\n formatPathForPrinting,\n globalConfigPath,\n modifyGlobalConfig,\n} from \"./utils/globalConfig.js\";\nimport { updateBigBrainAuthAfterLogin } from \"./deploymentSelection.js\";\n\n// Per https://github.com/panva/node-openid-client/tree/main/docs#customizing\ncustom.setHttpOptionsDefaults({\n timeout: parseInt(process.env.OPENID_CLIENT_TIMEOUT || \"10000\"),\n});\n\ninterface AuthorizeArgs {\n authnToken: string;\n deviceName: string;\n anonymousId?: string | undefined;\n}\n\nexport async function checkAuthorization(\n ctx: Context,\n acceptOptIns: boolean,\n): Promise<boolean> {\n const header = ctx.bigBrainAuth()?.header ?? null;\n if (header === null) {\n return false;\n }\n try {\n const resp = await fetch(`${provisionHost}/api/authorize`, {\n method: \"HEAD\",\n headers: {\n Authorization: header,\n \"Convex-Client\": `npm-cli-${version}`,\n },\n });\n // Don't throw an error if this request returns a non-200 status.\n // Big Brain responds with a variety of error codes -- 401 if the token is correctly-formed but not valid, and either 400 or 500 if the token is ill-formed.\n // We only care if this check returns a 200 code (so we can skip logging in again) -- any other errors should be silently skipped and we'll run the whole login flow again.\n if (resp.status !== 200) {\n return false;\n }\n } catch (e: any) {\n // This `catch` block should only be hit if a network error was encountered\n logError(\n `Unexpected error when authorizing - are you connected to the internet?`,\n );\n return await logAndHandleFetchError(ctx, e);\n }\n\n // Check that we have optin as well\n const shouldContinue = await optins(ctx, acceptOptIns);\n if (!shouldContinue) {\n return await ctx.crash({\n exitCode: 1,\n errorType: \"fatal\",\n printedMessage: null,\n });\n }\n return true;\n}\n\nasync function performDeviceAuthorization(\n ctx: Context,\n authClient: BaseClient,\n shouldOpen: boolean,\n vercel?: boolean,\n vercelOverride?: string,\n): Promise<string> {\n // Device authorization flow follows this guide: https://github.com/auth0/auth0-device-flow-cli-sample/blob/9f0f3b76a6cd56ea8d99e76769187ea5102d519d/cli.js\n // License: MIT License\n // Copyright (c) 2019 Auth0 Samples\n /*\n The MIT License (MIT)\n\n Copyright (c) 2019 Auth0 Samples\n\n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n\n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n\n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.\n */\n\n // Device Authorization Request - https://tools.ietf.org/html/rfc8628#section-3.1\n // Get authentication URL\n let handle;\n try {\n handle = await authClient.deviceAuthorization();\n } catch {\n // We couldn't get verification URL from the auth provider, proceed with manual auth\n return promptString(ctx, {\n message:\n \"Open https://dashboard.convex.dev/auth, log in and paste the token here:\",\n });\n }\n\n // Device Authorization Response - https://tools.ietf.org/html/rfc8628#section-3.2\n // Open authentication URL\n const { verification_uri_complete, user_code, expires_in } = handle;\n\n // Construct Vercel URL if --vercel flag is used\n const urlToOpen = vercel\n ? `https://vercel.com/sso/integrations/${vercelOverride || \"convex\"}?url=${verification_uri_complete}`\n : verification_uri_complete;\n\n logMessage(\n `Visit ${urlToOpen} to finish logging in.\\n` +\n `You should see the following code which expires in ${\n expires_in % 60 === 0\n ? `${expires_in / 60} minutes`\n : `${expires_in} seconds`\n }: ${user_code}`,\n );\n if (shouldOpen) {\n shouldOpen = await promptYesNo(ctx, {\n message: `Open the browser?`,\n default: true,\n });\n }\n\n if (shouldOpen) {\n showSpinner(`Opening ${urlToOpen} in your browser to log in...\\n`);\n try {\n const p = await open(urlToOpen);\n p.once(\"error\", () => {\n changeSpinner(`Manually open ${urlToOpen} in your browser to log in.`);\n });\n changeSpinner(\"Waiting for the confirmation...\");\n } catch {\n logError(chalkStderr.red(`Unable to open browser.`));\n changeSpinner(`Manually open ${urlToOpen} in your browser to log in.`);\n }\n } else {\n showSpinner(`Open ${urlToOpen} in your browser to log in.`);\n }\n\n // Device Access Token Request - https://tools.ietf.org/html/rfc8628#section-3.4\n // Device Access Token Response - https://tools.ietf.org/html/rfc8628#section-3.5\n try {\n const tokens = await handle.poll();\n if (typeof tokens.access_token === \"string\") {\n return tokens.access_token;\n } else {\n // Unexpected error\n // eslint-disable-next-line no-restricted-syntax\n throw Error(\"Access token is missing\");\n }\n } catch (err: any) {\n switch (err.error) {\n case \"access_denied\": // end-user declined the device confirmation prompt, consent or rules failed\n return await ctx.crash({\n exitCode: 1,\n errorType: \"fatal\",\n printedMessage: \"Access denied.\",\n errForSentry: err,\n });\n case \"expired_token\": // end-user did not complete the interaction in time\n return await ctx.crash({\n exitCode: 1,\n errorType: \"fatal\",\n printedMessage: \"Device flow expired.\",\n errForSentry: err,\n });\n default: {\n const message =\n err instanceof errors.OPError\n ? `Error = ${err.error}; error_description = ${err.error_description}`\n : `Login failed with error: ${err}`;\n return await ctx.crash({\n exitCode: 1,\n errorType: \"fatal\",\n printedMessage: message,\n errForSentry: err,\n });\n }\n }\n }\n}\n\nasync function performPasswordAuthentication(\n ctx: Context,\n clientId: string,\n username: string,\n password: string,\n): Promise<string> {\n if (!process.env.WORKOS_API_SECRET) {\n return await ctx.crash({\n exitCode: 1,\n errorType: \"fatal\",\n printedMessage: \"WORKOS_API_SECRET environment variable is not set\",\n });\n }\n\n // Unfortunately, `openid-client` doesn't support the resource owner password credentials flow so we need to manually send the requests.\n const options: Parameters<typeof throwingFetch>[1] = {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n grant_type: \"password\",\n email: username,\n password: password,\n client_id: clientId,\n client_secret: process.env.WORKOS_API_SECRET,\n }),\n };\n\n try {\n const response = await throwingFetch(\n \"https://apiauth.convex.dev/user_management/authenticate\",\n options,\n );\n const data = await response.json();\n if (typeof data.access_token === \"string\") {\n return data.access_token;\n } else {\n // Unexpected error\n // eslint-disable-next-line no-restricted-syntax\n throw Error(\"Access token is missing\");\n }\n } catch (err: any) {\n logFailure(`Password flow failed: ${err}`);\n if (err.response) {\n logError(chalkStderr.red(`${JSON.stringify(err.response.data)}`));\n }\n return await ctx.crash({\n exitCode: 1,\n errorType: \"fatal\",\n errForSentry: err,\n printedMessage: null,\n });\n }\n}\n\nexport async function performLogin(\n ctx: Context,\n {\n overrideAuthUrl,\n overrideAuthClient,\n overrideAuthUsername,\n overrideAuthPassword,\n overrideAccessToken,\n loginFlow,\n open,\n acceptOptIns,\n dumpAccessToken,\n deviceName: deviceNameOverride,\n anonymousId,\n vercel,\n vercelOverride,\n }: {\n overrideAuthUrl?: string | undefined;\n overrideAuthClient?: string | undefined;\n overrideAuthUsername?: string | undefined;\n overrideAuthPassword?: string | undefined;\n overrideAccessToken?: string | undefined;\n loginFlow?: \"auto\" | \"paste\" | \"poll\" | undefined;\n // default `true`\n open?: boolean | undefined;\n // default `false`\n acceptOptIns?: boolean | undefined;\n dumpAccessToken?: boolean | undefined;\n deviceName?: string | undefined;\n anonymousId?: string | undefined;\n vercel?: boolean | undefined;\n vercelOverride?: string | undefined;\n } = {},\n) {\n loginFlow = loginFlow || \"auto\";\n // Get access token from big-brain\n // Default the device name to the hostname, but allow the user to change this if the terminal is interactive.\n // On Macs, the `hostname()` may be a weirdly-truncated form of the computer name. Attempt to read the \"real\" name before falling back to hostname.\n let deviceName = deviceNameOverride ?? \"\";\n if (!deviceName && process.platform === \"darwin\") {\n try {\n deviceName = execSync(\"scutil --get ComputerName\").toString().trim();\n } catch {\n // Just fall back to the hostname default below.\n }\n }\n if (!deviceName) {\n deviceName = hostname();\n }\n if (!deviceNameOverride) {\n logMessage(\n chalkStderr.bold(\n `Welcome to developing with Convex, let's get you logged in.`,\n ),\n );\n deviceName = await promptString(ctx, {\n message: \"Device name:\",\n default: deviceName,\n });\n }\n\n const issuer = overrideAuthUrl ?? \"https://auth.convex.dev\";\n let authIssuer;\n let accessToken: string;\n\n if (loginFlow === \"paste\" || (loginFlow === \"auto\" && isWebContainer())) {\n accessToken = await promptString(ctx, {\n message:\n \"Open https://dashboard.convex.dev/auth, log in and paste the token here:\",\n });\n } else {\n try {\n authIssuer = await Issuer.discover(issuer);\n } catch {\n // Couldn't contact https://auth.convex.dev/.well-known/openid-configuration,\n // proceed with manual auth.\n accessToken = await promptString(ctx, {\n message:\n \"Open https://dashboard.convex.dev/auth, log in and paste the token here:\",\n });\n }\n }\n\n // typical path\n if (authIssuer) {\n const clientId = overrideAuthClient ?? \"HFtA247jp9iNs08NTLIB7JsNPMmRIyfi\";\n const authClient = new authIssuer.Client({\n client_id: clientId,\n token_endpoint_auth_method: \"none\",\n id_token_signed_response_alg: \"RS256\",\n });\n\n if (overrideAccessToken) {\n accessToken = overrideAccessToken;\n } else if (overrideAuthUsername && overrideAuthPassword) {\n accessToken = await performPasswordAuthentication(\n ctx,\n clientId,\n overrideAuthUsername,\n overrideAuthPassword,\n );\n } else {\n accessToken = await performDeviceAuthorization(\n ctx,\n authClient,\n open ?? true,\n vercel,\n vercelOverride,\n );\n }\n }\n\n if (dumpAccessToken) {\n logOutput(`${accessToken!}`);\n return await ctx.crash({\n exitCode: 0,\n errorType: \"fatal\",\n printedMessage: null,\n });\n }\n\n const authorizeArgs: AuthorizeArgs = {\n authnToken: accessToken!,\n deviceName: deviceName,\n anonymousId: anonymousId,\n };\n const data = await bigBrainAPI({\n ctx,\n method: \"POST\",\n path: \"authorize\",\n data: authorizeArgs,\n });\n const globalConfig = { accessToken: data.accessToken };\n try {\n await modifyGlobalConfig(ctx, globalConfig);\n const path = globalConfigPath();\n logFinishedStep(`Saved credentials to ${formatPathForPrinting(path)}`);\n } catch (err: unknown) {\n return await ctx.crash({\n exitCode: 1,\n errorType: \"invalid filesystem data\",\n errForSentry: err,\n printedMessage: null,\n });\n }\n\n logVerbose(`performLogin: updating big brain auth after login`);\n await updateBigBrainAuthAfterLogin(ctx, data.accessToken);\n\n logVerbose(`performLogin: checking opt ins, acceptOptIns: ${acceptOptIns}`);\n // Do opt in to TOS and Privacy Policy stuff\n const shouldContinue = await optins(ctx, acceptOptIns ?? false);\n if (!shouldContinue) {\n return await ctx.crash({\n exitCode: 1,\n errorType: \"fatal\",\n printedMessage: null,\n });\n }\n\n if (vercel) {\n await promptJoinVercelTeams(ctx);\n }\n}\n\ntype PotentialVercelTeam = {\n teamId: number;\n teamName: string;\n teamSlug: string;\n planId: string;\n planName: string;\n pricingNotice: string | null;\n};\n\n// After `--vercel` login, surface any Vercel-marketplace teams the user has\n// access to but isn't yet a member of, and prompt to join each.\nasync function promptJoinVercelTeams(ctx: Context): Promise<void> {\n const fetch = bigBrainFetch(ctx);\n let teams: PotentialVercelTeam[];\n try {\n const res = await fetch(\n new URL(\"vercel/potential_teams\", `${provisionHost}/`),\n );\n if (!res.ok) {\n logVerbose(\n `vercel/potential_teams returned ${res.status}; skipping team-join prompt`,\n );\n return;\n }\n teams = await res.json();\n } catch (err) {\n logVerbose(`Failed to fetch potential Vercel teams: ${String(err)}`);\n return;\n }\n if (teams.length === 0) return;\n\n for (const [index, team] of teams.entries()) {\n const displayName = team.teamName.replace(/ \\(Vercel\\)$/, \"\");\n const counter = teams.length > 1 ? `[${index + 1}/${teams.length}] ` : \"\";\n const lines = [\n chalkStderr.bold(`${counter}You've been invited to join ${displayName}`) +\n ` (${team.planName}) through the Vercel marketplace.`,\n ];\n if (team.pricingNotice) {\n lines.push(chalkStderr.yellow(team.pricingNotice));\n }\n lines.push(`Join \"${displayName}\"?`);\n const join = await promptYesNo(ctx, {\n message: `${lines.join(\"\\n\")}`,\n default: true,\n });\n if (!join) continue;\n try {\n await fetch(\n new URL(\n `vercel/potential_teams/${team.teamId}/join`,\n `${provisionHost}/`,\n ),\n { method: \"POST\" },\n );\n logFinishedStep(`Joined ${displayName}`);\n } catch (err) {\n logFailure(`Failed to join ${displayName}: ${String(err)}`);\n }\n }\n}\n\n/// There are fields like version, but we keep them opaque\ntype OptIn = Record<string, unknown>;\n\ntype OptInToAccept = {\n optIn: OptIn;\n message: string;\n};\n\ntype AcceptOptInsArgs = {\n optInsAccepted: OptIn[];\n};\n\n// Returns whether we can proceed or not.\nasync function optins(ctx: Context, acceptOptIns: boolean): Promise<boolean> {\n const bbAuth = ctx.bigBrainAuth();\n if (bbAuth === null) {\n // This should never happen, but if we're not even logged in, we can't proceed.\n return false;\n }\n switch (bbAuth.kind) {\n case \"accessToken\":\n break;\n case \"deploymentKey\":\n case \"projectKey\":\n case \"previewDeployKey\":\n // If we have a key configured as auth, we do not need to check opt ins.\n return true;\n default: {\n bbAuth satisfies never;\n return await ctx.crash({\n exitCode: 1,\n errorType: \"fatal\",\n errForSentry: `Unexpected auth kind ${(bbAuth as any).kind}`,\n printedMessage: \"Hit an unexpected error while logging in.\",\n });\n }\n }\n const data = await bigBrainAPI({\n ctx,\n method: \"POST\",\n path: \"check_opt_ins\",\n });\n if (data.optInsToAccept.length === 0) {\n return true;\n }\n for (const optInToAccept of data.optInsToAccept) {\n const confirmed =\n acceptOptIns ||\n (await promptYesNo(ctx, {\n message: optInToAccept.message,\n }));\n if (!confirmed) {\n logFailure(\"Please accept the Terms of Service to use Convex.\");\n return Promise.resolve(false);\n }\n }\n\n const optInsAccepted = data.optInsToAccept.map((o: OptInToAccept) => o.optIn);\n const args: AcceptOptInsArgs = { optInsAccepted };\n await bigBrainAPI({\n ctx,\n method: \"POST\",\n path: \"accept_opt_ins\",\n data: args,\n });\n return true;\n}\n\nexport async function ensureLoggedIn(\n ctx: Context,\n options?: {\n message?: string | undefined;\n overrideAuthUrl?: string | undefined;\n overrideAuthClient?: string | undefined;\n overrideAuthUsername?: string | undefined;\n overrideAuthPassword?: string | undefined;\n },\n) {\n const isLoggedIn = await checkAuthorization(ctx, false);\n if (!isLoggedIn) {\n if (options?.message) {\n logMessage(options.message);\n }\n await performLogin(ctx, {\n acceptOptIns: false,\n overrideAuthUrl: options?.overrideAuthUrl,\n overrideAuthClient: options?.overrideAuthClient,\n overrideAuthUsername: options?.overrideAuthUsername,\n overrideAuthPassword: options?.overrideAuthPassword,\n });\n }\n}\n"],
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,2BAA2C;AAC3C,
|
|
4
|
+
"sourcesContent": ["import { errors, BaseClient, custom } from \"openid-client\";\nimport {\n bigBrainAPI,\n bigBrainFetch,\n logAndHandleFetchError,\n throwingFetch,\n isWebContainer,\n typedPlatformClient,\n} from \"./utils/utils.js\";\nimport open from \"open\";\nimport { chalkStderr } from \"chalk\";\nimport { provisionHost } from \"./config.js\";\nimport { version } from \"../version.js\";\nimport { Context } from \"../../bundler/context.js\";\nimport {\n changeSpinner,\n logError,\n logFailure,\n logFinishedStep,\n logMessage,\n logOutput,\n logVerbose,\n showSpinner,\n} from \"../../bundler/log.js\";\nimport { Issuer } from \"openid-client\";\nimport { hostname } from \"os\";\nimport { execSync } from \"child_process\";\nimport { promptString, promptYesNo } from \"./utils/prompts.js\";\nimport {\n formatPathForPrinting,\n globalConfigPath,\n modifyGlobalConfig,\n} from \"./utils/globalConfig.js\";\nimport { updateBigBrainAuthAfterLogin } from \"./deploymentSelection.js\";\n\n// Per https://github.com/panva/node-openid-client/tree/main/docs#customizing\ncustom.setHttpOptionsDefaults({\n timeout: parseInt(process.env.OPENID_CLIENT_TIMEOUT || \"10000\"),\n});\n\nexport async function checkAuthorization(\n ctx: Context,\n acceptOptIns: boolean,\n): Promise<boolean> {\n const header = ctx.bigBrainAuth()?.header ?? null;\n if (header === null) {\n return false;\n }\n try {\n const resp = await fetch(`${provisionHost}/api/authorize`, {\n method: \"HEAD\",\n headers: {\n Authorization: header,\n \"Convex-Client\": `npm-cli-${version}`,\n },\n });\n // Don't throw an error if this request returns a non-200 status.\n // Big Brain responds with a variety of error codes -- 401 if the token is correctly-formed but not valid, and either 400 or 500 if the token is ill-formed.\n // We only care if this check returns a 200 code (so we can skip logging in again) -- any other errors should be silently skipped and we'll run the whole login flow again.\n if (resp.status !== 200) {\n return false;\n }\n } catch (e: any) {\n // This `catch` block should only be hit if a network error was encountered\n logError(\n `Unexpected error when authorizing - are you connected to the internet?`,\n );\n return await logAndHandleFetchError(ctx, e);\n }\n\n // Check that we have optin as well\n const shouldContinue = await optins(ctx, acceptOptIns);\n if (!shouldContinue) {\n return await ctx.crash({\n exitCode: 1,\n errorType: \"fatal\",\n printedMessage: null,\n });\n }\n return true;\n}\n\nasync function performDeviceAuthorization(\n ctx: Context,\n authClient: BaseClient,\n shouldOpen: boolean,\n vercel?: boolean,\n vercelOverride?: string,\n): Promise<string> {\n // Device authorization flow follows this guide: https://github.com/auth0/auth0-device-flow-cli-sample/blob/9f0f3b76a6cd56ea8d99e76769187ea5102d519d/cli.js\n // License: MIT License\n // Copyright (c) 2019 Auth0 Samples\n /*\n The MIT License (MIT)\n\n Copyright (c) 2019 Auth0 Samples\n\n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n\n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n\n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.\n */\n\n // Device Authorization Request - https://tools.ietf.org/html/rfc8628#section-3.1\n // Get authentication URL\n let handle;\n try {\n handle = await authClient.deviceAuthorization();\n } catch {\n // We couldn't get verification URL from the auth provider, proceed with manual auth\n return promptString(ctx, {\n message:\n \"Open https://dashboard.convex.dev/auth, log in and paste the token here:\",\n });\n }\n\n // Device Authorization Response - https://tools.ietf.org/html/rfc8628#section-3.2\n // Open authentication URL\n const { verification_uri_complete, user_code, expires_in } = handle;\n\n // Construct Vercel URL if --vercel flag is used\n const urlToOpen = vercel\n ? `https://vercel.com/sso/integrations/${vercelOverride || \"convex\"}?url=${verification_uri_complete}`\n : verification_uri_complete;\n\n logMessage(\n `Visit ${urlToOpen} to finish logging in.\\n` +\n `You should see the following code which expires in ${\n expires_in % 60 === 0\n ? `${expires_in / 60} minutes`\n : `${expires_in} seconds`\n }: ${user_code}`,\n );\n if (shouldOpen) {\n shouldOpen = await promptYesNo(ctx, {\n message: `Open the browser?`,\n default: true,\n });\n }\n\n if (shouldOpen) {\n showSpinner(`Opening ${urlToOpen} in your browser to log in...\\n`);\n try {\n const p = await open(urlToOpen);\n p.once(\"error\", () => {\n changeSpinner(`Manually open ${urlToOpen} in your browser to log in.`);\n });\n changeSpinner(\"Waiting for the confirmation...\");\n } catch {\n logError(chalkStderr.red(`Unable to open browser.`));\n changeSpinner(`Manually open ${urlToOpen} in your browser to log in.`);\n }\n } else {\n showSpinner(`Open ${urlToOpen} in your browser to log in.`);\n }\n\n // Device Access Token Request - https://tools.ietf.org/html/rfc8628#section-3.4\n // Device Access Token Response - https://tools.ietf.org/html/rfc8628#section-3.5\n try {\n const tokens = await handle.poll();\n if (typeof tokens.access_token === \"string\") {\n return tokens.access_token;\n } else {\n // Unexpected error\n // eslint-disable-next-line no-restricted-syntax\n throw Error(\"Access token is missing\");\n }\n } catch (err: any) {\n switch (err.error) {\n case \"access_denied\": // end-user declined the device confirmation prompt, consent or rules failed\n return await ctx.crash({\n exitCode: 1,\n errorType: \"fatal\",\n printedMessage: \"Access denied.\",\n errForSentry: err,\n });\n case \"expired_token\": // end-user did not complete the interaction in time\n return await ctx.crash({\n exitCode: 1,\n errorType: \"fatal\",\n printedMessage: \"Device flow expired.\",\n errForSentry: err,\n });\n default: {\n const message =\n err instanceof errors.OPError\n ? `Error = ${err.error}; error_description = ${err.error_description}`\n : `Login failed with error: ${err}`;\n return await ctx.crash({\n exitCode: 1,\n errorType: \"fatal\",\n printedMessage: message,\n errForSentry: err,\n });\n }\n }\n }\n}\n\nasync function performPasswordAuthentication(\n ctx: Context,\n clientId: string,\n username: string,\n password: string,\n): Promise<string> {\n if (!process.env.WORKOS_API_SECRET) {\n return await ctx.crash({\n exitCode: 1,\n errorType: \"fatal\",\n printedMessage: \"WORKOS_API_SECRET environment variable is not set\",\n });\n }\n\n // Unfortunately, `openid-client` doesn't support the resource owner password credentials flow so we need to manually send the requests.\n const options: Parameters<typeof throwingFetch>[1] = {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n grant_type: \"password\",\n email: username,\n password: password,\n client_id: clientId,\n client_secret: process.env.WORKOS_API_SECRET,\n }),\n };\n\n try {\n const response = await throwingFetch(\n \"https://apiauth.convex.dev/user_management/authenticate\",\n options,\n );\n const data = await response.json();\n if (typeof data.access_token === \"string\") {\n return data.access_token;\n } else {\n // Unexpected error\n // eslint-disable-next-line no-restricted-syntax\n throw Error(\"Access token is missing\");\n }\n } catch (err: any) {\n logFailure(`Password flow failed: ${err}`);\n if (err.response) {\n logError(chalkStderr.red(`${JSON.stringify(err.response.data)}`));\n }\n return await ctx.crash({\n exitCode: 1,\n errorType: \"fatal\",\n errForSentry: err,\n printedMessage: null,\n });\n }\n}\n\nexport async function performLogin(\n ctx: Context,\n {\n overrideAuthUrl,\n overrideAuthClient,\n overrideAuthUsername,\n overrideAuthPassword,\n overrideAccessToken,\n loginFlow,\n open,\n acceptOptIns,\n dumpAccessToken,\n deviceName: deviceNameOverride,\n anonymousId,\n vercel,\n vercelOverride,\n }: {\n overrideAuthUrl?: string | undefined;\n overrideAuthClient?: string | undefined;\n overrideAuthUsername?: string | undefined;\n overrideAuthPassword?: string | undefined;\n overrideAccessToken?: string | undefined;\n loginFlow?: \"auto\" | \"paste\" | \"poll\" | undefined;\n // default `true`\n open?: boolean | undefined;\n // default `false`\n acceptOptIns?: boolean | undefined;\n dumpAccessToken?: boolean | undefined;\n deviceName?: string | undefined;\n anonymousId?: string | undefined;\n vercel?: boolean | undefined;\n vercelOverride?: string | undefined;\n } = {},\n) {\n loginFlow = loginFlow || \"auto\";\n // Get access token from big-brain\n // Default the device name to the hostname, but allow the user to change this if the terminal is interactive.\n // On Macs, the `hostname()` may be a weirdly-truncated form of the computer name. Attempt to read the \"real\" name before falling back to hostname.\n let deviceName = deviceNameOverride ?? \"\";\n if (!deviceName && process.platform === \"darwin\") {\n try {\n deviceName = execSync(\"scutil --get ComputerName\").toString().trim();\n } catch {\n // Just fall back to the hostname default below.\n }\n }\n if (!deviceName) {\n deviceName = hostname();\n }\n if (!deviceNameOverride) {\n logMessage(\n chalkStderr.bold(\n `Welcome to developing with Convex, let's get you logged in.`,\n ),\n );\n deviceName = await promptString(ctx, {\n message: \"Device name:\",\n default: deviceName,\n });\n }\n\n const issuer = overrideAuthUrl ?? \"https://auth.convex.dev\";\n let authIssuer;\n let accessToken: string;\n\n if (loginFlow === \"paste\" || (loginFlow === \"auto\" && isWebContainer())) {\n accessToken = await promptString(ctx, {\n message:\n \"Open https://dashboard.convex.dev/auth, log in and paste the token here:\",\n });\n } else {\n try {\n authIssuer = await Issuer.discover(issuer);\n } catch {\n // Couldn't contact https://auth.convex.dev/.well-known/openid-configuration,\n // proceed with manual auth.\n accessToken = await promptString(ctx, {\n message:\n \"Open https://dashboard.convex.dev/auth, log in and paste the token here:\",\n });\n }\n }\n\n // typical path\n if (authIssuer) {\n const clientId = overrideAuthClient ?? \"HFtA247jp9iNs08NTLIB7JsNPMmRIyfi\";\n const authClient = new authIssuer.Client({\n client_id: clientId,\n token_endpoint_auth_method: \"none\",\n id_token_signed_response_alg: \"RS256\",\n });\n\n if (overrideAccessToken) {\n accessToken = overrideAccessToken;\n } else if (overrideAuthUsername && overrideAuthPassword) {\n accessToken = await performPasswordAuthentication(\n ctx,\n clientId,\n overrideAuthUsername,\n overrideAuthPassword,\n );\n } else {\n accessToken = await performDeviceAuthorization(\n ctx,\n authClient,\n open ?? true,\n vercel,\n vercelOverride,\n );\n }\n }\n\n if (dumpAccessToken) {\n logOutput(`${accessToken!}`);\n return await ctx.crash({\n exitCode: 0,\n errorType: \"fatal\",\n printedMessage: null,\n });\n }\n\n // Exchange the WorkOS access token for a Convex personal access token.\n ctx._updateBigBrainAuth({\n accessToken: accessToken!,\n kind: \"accessToken\",\n header: `Bearer ${accessToken!}`,\n });\n const response = await typedPlatformClient(ctx).POST(\n \"/create_personal_access_token\",\n {\n // `anonymousId` links a prior anonymous session to this account. It's\n // intentionally absent from the generated platform API types,\n // so cast to pass it through.\n body: { name: deviceName, anonymousId } as { name: string },\n },\n );\n const newAccessToken = response.data!.accessToken;\n const globalConfig = { accessToken: newAccessToken };\n try {\n await modifyGlobalConfig(ctx, globalConfig);\n const path = globalConfigPath();\n logFinishedStep(`Saved credentials to ${formatPathForPrinting(path)}`);\n } catch (err: unknown) {\n return await ctx.crash({\n exitCode: 1,\n errorType: \"invalid filesystem data\",\n errForSentry: err,\n printedMessage: null,\n });\n }\n\n logVerbose(`performLogin: updating big brain auth after login`);\n await updateBigBrainAuthAfterLogin(ctx, newAccessToken);\n\n logVerbose(`performLogin: checking opt ins, acceptOptIns: ${acceptOptIns}`);\n // Do opt in to TOS and Privacy Policy stuff\n const shouldContinue = await optins(ctx, acceptOptIns ?? false);\n if (!shouldContinue) {\n return await ctx.crash({\n exitCode: 1,\n errorType: \"fatal\",\n printedMessage: null,\n });\n }\n\n if (vercel) {\n await promptJoinVercelTeams(ctx);\n }\n}\n\ntype PotentialVercelTeam = {\n teamId: number;\n teamName: string;\n teamSlug: string;\n planId: string;\n planName: string;\n pricingNotice: string | null;\n};\n\n// After `--vercel` login, surface any Vercel-marketplace teams the user has\n// access to but isn't yet a member of, and prompt to join each.\nasync function promptJoinVercelTeams(ctx: Context): Promise<void> {\n const fetch = bigBrainFetch(ctx);\n let teams: PotentialVercelTeam[];\n try {\n const res = await fetch(\n new URL(\"vercel/potential_teams\", `${provisionHost}/`),\n );\n if (!res.ok) {\n logVerbose(\n `vercel/potential_teams returned ${res.status}; skipping team-join prompt`,\n );\n return;\n }\n teams = await res.json();\n } catch (err) {\n logVerbose(`Failed to fetch potential Vercel teams: ${String(err)}`);\n return;\n }\n if (teams.length === 0) return;\n\n for (const [index, team] of teams.entries()) {\n const displayName = team.teamName.replace(/ \\(Vercel\\)$/, \"\");\n const counter = teams.length > 1 ? `[${index + 1}/${teams.length}] ` : \"\";\n const lines = [\n chalkStderr.bold(`${counter}You've been invited to join ${displayName}`) +\n ` (${team.planName}) through the Vercel marketplace.`,\n ];\n if (team.pricingNotice) {\n lines.push(chalkStderr.yellow(team.pricingNotice));\n }\n lines.push(`Join \"${displayName}\"?`);\n const join = await promptYesNo(ctx, {\n message: `${lines.join(\"\\n\")}`,\n default: true,\n });\n if (!join) continue;\n try {\n await fetch(\n new URL(\n `vercel/potential_teams/${team.teamId}/join`,\n `${provisionHost}/`,\n ),\n { method: \"POST\" },\n );\n logFinishedStep(`Joined ${displayName}`);\n } catch (err) {\n logFailure(`Failed to join ${displayName}: ${String(err)}`);\n }\n }\n}\n\n/// There are fields like version, but we keep them opaque\ntype OptIn = Record<string, unknown>;\n\ntype OptInToAccept = {\n optIn: OptIn;\n message: string;\n};\n\ntype AcceptOptInsArgs = {\n optInsAccepted: OptIn[];\n};\n\n// Returns whether we can proceed or not.\nasync function optins(ctx: Context, acceptOptIns: boolean): Promise<boolean> {\n const bbAuth = ctx.bigBrainAuth();\n if (bbAuth === null) {\n // This should never happen, but if we're not even logged in, we can't proceed.\n return false;\n }\n switch (bbAuth.kind) {\n case \"accessToken\":\n break;\n case \"deploymentKey\":\n case \"projectKey\":\n case \"previewDeployKey\":\n // If we have a key configured as auth, we do not need to check opt ins.\n return true;\n default: {\n bbAuth satisfies never;\n return await ctx.crash({\n exitCode: 1,\n errorType: \"fatal\",\n errForSentry: `Unexpected auth kind ${(bbAuth as any).kind}`,\n printedMessage: \"Hit an unexpected error while logging in.\",\n });\n }\n }\n const data = await bigBrainAPI({\n ctx,\n method: \"POST\",\n path: \"check_opt_ins\",\n });\n if (data.optInsToAccept.length === 0) {\n return true;\n }\n for (const optInToAccept of data.optInsToAccept) {\n const confirmed =\n acceptOptIns ||\n (await promptYesNo(ctx, {\n message: optInToAccept.message,\n }));\n if (!confirmed) {\n logFailure(\"Please accept the Terms of Service to use Convex.\");\n return Promise.resolve(false);\n }\n }\n\n const optInsAccepted = data.optInsToAccept.map((o: OptInToAccept) => o.optIn);\n const args: AcceptOptInsArgs = { optInsAccepted };\n await bigBrainAPI({\n ctx,\n method: \"POST\",\n path: \"accept_opt_ins\",\n data: args,\n });\n return true;\n}\n\nexport async function ensureLoggedIn(\n ctx: Context,\n options?: {\n message?: string | undefined;\n overrideAuthUrl?: string | undefined;\n overrideAuthClient?: string | undefined;\n overrideAuthUsername?: string | undefined;\n overrideAuthPassword?: string | undefined;\n },\n) {\n const isLoggedIn = await checkAuthorization(ctx, false);\n if (!isLoggedIn) {\n if (options?.message) {\n logMessage(options.message);\n }\n await performLogin(ctx, {\n acceptOptIns: false,\n overrideAuthUrl: options?.overrideAuthUrl,\n overrideAuthClient: options?.overrideAuthClient,\n overrideAuthUsername: options?.overrideAuthUsername,\n overrideAuthPassword: options?.overrideAuthPassword,\n });\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["import_openid_client", "open", "fetch"]
|
|
7
7
|
}
|
package/dist/cjs/cli/lib/logs.js
CHANGED
|
@@ -112,7 +112,10 @@ async function watchLogs(ctx, url, adminKey, dest, options) {
|
|
|
112
112
|
options?.jsonl
|
|
113
113
|
);
|
|
114
114
|
}
|
|
115
|
-
} catch {
|
|
115
|
+
} catch (e) {
|
|
116
|
+
if (e instanceof import_utils.ThrowingFetchError && e.response.status === 403) {
|
|
117
|
+
return await e.handle(ctx);
|
|
118
|
+
}
|
|
116
119
|
numFailures += 1;
|
|
117
120
|
}
|
|
118
121
|
if (numFailures > 0) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/cli/lib/logs.ts"],
|
|
4
|
-
"sourcesContent": ["import { Context } from \"../../bundler/context.js\";\nimport { logMessage, logOutput, logWarning } from \"../../bundler/log.js\";\nimport { nextBackoff } from \"./dev.js\";\n// eslint-disable-next-line no-restricted-imports -- chalk used for writing to stdout\nimport chalk, { chalkStderr } from \"chalk\";\nimport { stripVTControlCharacters } from \"node:util\";\nimport { format } from \"node:util\";\nimport { deploymentFetch } from \"./utils/utils.js\";\nimport { FunctionExecution } from \"./apiTypes.js\";\n\nexport type LogMode = \"always\" | \"pause-on-deploy\" | \"disable\";\n\nexport class LogManager {\n private paused: boolean = false;\n\n constructor(private mode: LogMode) {}\n\n async waitForUnpaused() {\n while (this.paused) {\n await new Promise((resolve) => setTimeout(resolve, 100));\n }\n }\n\n beginDeploy() {\n if (this.mode === \"pause-on-deploy\") {\n this.paused = true;\n }\n }\n\n endDeploy() {\n if (this.mode === \"pause-on-deploy\") {\n this.paused = false;\n }\n }\n}\n\nconst MAX_UDF_STREAM_FAILURE_COUNT = 5;\n\ntype LogDestination = \"stdout\" | \"stderr\";\n\nexport async function logsForDeployment(\n ctx: Context,\n credentials: {\n url: string;\n adminKey: string;\n },\n options: {\n success: boolean;\n history: number;\n jsonl: boolean;\n deploymentNotice: string;\n },\n) {\n logMessage(chalkStderr.yellow(`Watching logs${options.deploymentNotice}...`));\n await watchLogs(ctx, credentials.url, credentials.adminKey, \"stdout\", {\n history: options.history,\n success: options.success,\n jsonl: options.jsonl,\n });\n}\n\nexport async function watchLogs(\n ctx: Context,\n url: string,\n adminKey: string,\n dest: LogDestination,\n options?: {\n success: boolean;\n history?: number | boolean;\n jsonl?: boolean;\n logManager?: LogManager;\n },\n) {\n let numFailures = 0;\n let isFirst = true;\n let cursorMs = 0;\n\n // Select the appropriate chalk instance and write function based on destination\n const chalkInstance = dest === \"stdout\" ? chalk : chalkStderr;\n const write = (s: string) => logToDestination(dest, s);\n\n for (;;) {\n try {\n const { entries, newCursor } = await pollUdfLog(\n ctx,\n cursorMs,\n url,\n adminKey,\n );\n cursorMs = newCursor;\n numFailures = 0;\n\n // Delay printing logs until the log manager is unpaused.\n await options?.logManager?.waitForUnpaused();\n\n // The first execution, we just want to fetch the current head cursor so we don't send stale\n // logs to the client.\n if (isFirst) {\n isFirst = false;\n if (\n options?.history === true ||\n (typeof options?.history === \"number\" && options?.history > 0)\n ) {\n const entriesSlice =\n options?.history === true\n ? entries\n : entries.slice(entries.length - options?.history);\n processLogs(\n entriesSlice,\n write,\n options?.success,\n chalkInstance,\n options?.jsonl,\n );\n }\n } else {\n processLogs(\n entries,\n write,\n options?.success === true,\n chalkInstance,\n options?.jsonl,\n );\n }\n } catch {\n numFailures += 1;\n }\n // Handle backoff\n if (numFailures > 0) {\n const backoff = nextBackoff(numFailures);\n\n // If we exceed a threshold number of failures, warn the user and display backoff.\n if (numFailures > MAX_UDF_STREAM_FAILURE_COUNT) {\n logWarning(\n `Convex [WARN] Failed to fetch logs. Waiting ${backoff}ms before next retry.`,\n );\n }\n await new Promise((resolve) => {\n setTimeout(() => resolve(null), backoff);\n });\n }\n }\n}\n\ntype UdfType = \"Query\" | \"Mutation\" | \"Action\" | \"HttpAction\";\n\ntype StructuredLogLine = {\n messages: string[];\n level: \"LOG\" | \"DEBUG\" | \"INFO\" | \"WARN\" | \"ERROR\";\n timestamp: number;\n isTruncated: boolean;\n};\ntype LogLine = string | StructuredLogLine;\n\nasync function pollUdfLog(\n ctx: Context,\n cursor: number,\n url: string,\n adminKey: string,\n): Promise<{ entries: FunctionExecution[]; newCursor: number }> {\n const fetch = deploymentFetch(ctx, {\n deploymentUrl: url,\n adminKey,\n });\n const response = await fetch(`/api/stream_function_logs?cursor=${cursor}`, {\n method: \"GET\",\n });\n return await response.json();\n}\n\nconst prefixForSource = (udfType: UdfType): string => {\n return udfType.charAt(0);\n};\n\nfunction processLogs(\n rawLogs: FunctionExecution[],\n write: (message: string) => void,\n shouldShowSuccessLogs: boolean,\n chalkInstance: typeof chalk,\n jsonl?: boolean,\n) {\n if (jsonl) {\n for (let i = 0; i < rawLogs.length; i++) {\n const log = rawLogs[i];\n write(JSON.stringify(log));\n }\n return;\n }\n\n for (let i = 0; i < rawLogs.length; i++) {\n const log = rawLogs[i];\n if (log.logLines) {\n const id = log.identifier;\n const udfType = log.udfType;\n const timestampMs = log.timestamp * 1000;\n const executionTimeMs =\n \"executionTime\" in log ? log.executionTime * 1000 : NaN;\n\n for (let j = 0; j < log.logLines.length; j++) {\n const formatted = formatLogLineMessage(\n \"info\",\n timestampMs,\n udfType,\n id,\n log.logLines[j],\n chalkInstance,\n );\n write(formatted);\n }\n\n if (\"error\" in log && log.error) {\n const formatted = formatLogLineMessage(\n \"error\",\n timestampMs,\n udfType,\n id,\n log.error!,\n chalkInstance,\n );\n write(formatted);\n } else if (log.kind === \"Completion\" && shouldShowSuccessLogs) {\n const formatted = chalkInstance.green(\n formatFunctionExecutionMessage(\n timestampMs,\n udfType,\n id,\n executionTimeMs,\n ),\n );\n write(formatted);\n }\n }\n }\n}\n\nexport function formatFunctionExecutionMessage(\n timestampMs: number,\n udfType: UdfType,\n udfPath: string,\n executionTimeMs: number,\n): string {\n return `${prefixLog(timestampMs, udfType, udfPath)} Function executed in ${Math.ceil(executionTimeMs)} ms`;\n}\n\nexport function formatLogLineMessage(\n type: \"info\" | \"error\",\n timestampMs: number,\n udfType: UdfType,\n udfPath: string,\n message: LogLine,\n chalkInstance: typeof chalk = chalk,\n): string {\n const prefix = prefixForSource(udfType);\n if (typeof message === \"string\") {\n if (type === \"info\") {\n const match = message.match(/^\\[.*?\\] /);\n if (match === null) {\n return chalkInstance.red(\n `[CONVEX ${prefix}(${udfPath})] Could not parse console.log`,\n );\n }\n const level = message.slice(1, match[0].length - 2);\n const args = message.slice(match[0].length);\n return `${chalkInstance.cyan(`${prefixLog(timestampMs, udfType, udfPath)} [${level}]`)} ${format(args)}`;\n } else {\n return chalkInstance.red(\n `${prefixLog(timestampMs, udfType, udfPath)} ${message}`,\n );\n }\n } else {\n const level = message.level;\n const formattedMessage = `${message.messages.join(\" \")}${message.isTruncated ? \" (truncated due to length)\" : \"\"}`;\n return `${chalkInstance.cyan(\n `${prefixLog(message.timestamp, udfType, udfPath)} [${level}]`,\n )} ${formattedMessage}`;\n }\n}\n\nfunction logToDestination(dest: LogDestination, s: string) {\n switch (dest) {\n case \"stdout\":\n logOutput(s);\n break;\n case \"stderr\":\n logMessage(s);\n break;\n }\n}\n\nfunction prefixLog(timestampMs: number, udfType: UdfType, udfPath: string) {\n const prefix = prefixForSource(udfType);\n const localizedTimestamp = new Date(timestampMs).toLocaleString();\n\n return `${localizedTimestamp} [CONVEX ${prefix}(${udfPath})]`;\n}\n\nexport function formatLogsAsText(\n rawLogs: FunctionExecution[],\n shouldShowSuccessLogs: boolean = false,\n): string {\n const lines: string[] = [];\n const write = (message: string) =>\n lines.push(stripVTControlCharacters(message));\n processLogs(rawLogs, write, shouldShowSuccessLogs, chalk);\n return lines.join(\"\\n\");\n}\n"],
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA,iBAAkD;AAClD,iBAA4B;AAE5B,mBAAmC;AACnC,uBAAyC;AACzC,IAAAA,oBAAuB;AACvB,
|
|
4
|
+
"sourcesContent": ["import { Context } from \"../../bundler/context.js\";\nimport { logMessage, logOutput, logWarning } from \"../../bundler/log.js\";\nimport { nextBackoff } from \"./dev.js\";\n// eslint-disable-next-line no-restricted-imports -- chalk used for writing to stdout\nimport chalk, { chalkStderr } from \"chalk\";\nimport { stripVTControlCharacters } from \"node:util\";\nimport { format } from \"node:util\";\nimport { deploymentFetch, ThrowingFetchError } from \"./utils/utils.js\";\nimport { FunctionExecution } from \"./apiTypes.js\";\n\nexport type LogMode = \"always\" | \"pause-on-deploy\" | \"disable\";\n\nexport class LogManager {\n private paused: boolean = false;\n\n constructor(private mode: LogMode) {}\n\n async waitForUnpaused() {\n while (this.paused) {\n await new Promise((resolve) => setTimeout(resolve, 100));\n }\n }\n\n beginDeploy() {\n if (this.mode === \"pause-on-deploy\") {\n this.paused = true;\n }\n }\n\n endDeploy() {\n if (this.mode === \"pause-on-deploy\") {\n this.paused = false;\n }\n }\n}\n\nconst MAX_UDF_STREAM_FAILURE_COUNT = 5;\n\ntype LogDestination = \"stdout\" | \"stderr\";\n\nexport async function logsForDeployment(\n ctx: Context,\n credentials: {\n url: string;\n adminKey: string;\n },\n options: {\n success: boolean;\n history: number;\n jsonl: boolean;\n deploymentNotice: string;\n },\n) {\n logMessage(chalkStderr.yellow(`Watching logs${options.deploymentNotice}...`));\n await watchLogs(ctx, credentials.url, credentials.adminKey, \"stdout\", {\n history: options.history,\n success: options.success,\n jsonl: options.jsonl,\n });\n}\n\nexport async function watchLogs(\n ctx: Context,\n url: string,\n adminKey: string,\n dest: LogDestination,\n options?: {\n success: boolean;\n history?: number | boolean;\n jsonl?: boolean;\n logManager?: LogManager;\n },\n) {\n let numFailures = 0;\n let isFirst = true;\n let cursorMs = 0;\n\n // Select the appropriate chalk instance and write function based on destination\n const chalkInstance = dest === \"stdout\" ? chalk : chalkStderr;\n const write = (s: string) => logToDestination(dest, s);\n\n for (;;) {\n try {\n const { entries, newCursor } = await pollUdfLog(\n ctx,\n cursorMs,\n url,\n adminKey,\n );\n cursorMs = newCursor;\n numFailures = 0;\n\n // Delay printing logs until the log manager is unpaused.\n await options?.logManager?.waitForUnpaused();\n\n // The first execution, we just want to fetch the current head cursor so we don't send stale\n // logs to the client.\n if (isFirst) {\n isFirst = false;\n if (\n options?.history === true ||\n (typeof options?.history === \"number\" && options?.history > 0)\n ) {\n const entriesSlice =\n options?.history === true\n ? entries\n : entries.slice(entries.length - options?.history);\n processLogs(\n entriesSlice,\n write,\n options?.success,\n chalkInstance,\n options?.jsonl,\n );\n }\n } else {\n processLogs(\n entries,\n write,\n options?.success === true,\n chalkInstance,\n options?.jsonl,\n );\n }\n } catch (e) {\n // A 403 means the admin key isn't authorized for this deployment.\n // Retrying won't help, so surface the error and fail immediately.\n if (e instanceof ThrowingFetchError && e.response.status === 403) {\n return await e.handle(ctx);\n }\n numFailures += 1;\n }\n // Handle backoff\n if (numFailures > 0) {\n const backoff = nextBackoff(numFailures);\n\n // If we exceed a threshold number of failures, warn the user and display backoff.\n if (numFailures > MAX_UDF_STREAM_FAILURE_COUNT) {\n logWarning(\n `Convex [WARN] Failed to fetch logs. Waiting ${backoff}ms before next retry.`,\n );\n }\n await new Promise((resolve) => {\n setTimeout(() => resolve(null), backoff);\n });\n }\n }\n}\n\ntype UdfType = \"Query\" | \"Mutation\" | \"Action\" | \"HttpAction\";\n\ntype StructuredLogLine = {\n messages: string[];\n level: \"LOG\" | \"DEBUG\" | \"INFO\" | \"WARN\" | \"ERROR\";\n timestamp: number;\n isTruncated: boolean;\n};\ntype LogLine = string | StructuredLogLine;\n\nasync function pollUdfLog(\n ctx: Context,\n cursor: number,\n url: string,\n adminKey: string,\n): Promise<{ entries: FunctionExecution[]; newCursor: number }> {\n const fetch = deploymentFetch(ctx, {\n deploymentUrl: url,\n adminKey,\n });\n const response = await fetch(`/api/stream_function_logs?cursor=${cursor}`, {\n method: \"GET\",\n });\n return await response.json();\n}\n\nconst prefixForSource = (udfType: UdfType): string => {\n return udfType.charAt(0);\n};\n\nfunction processLogs(\n rawLogs: FunctionExecution[],\n write: (message: string) => void,\n shouldShowSuccessLogs: boolean,\n chalkInstance: typeof chalk,\n jsonl?: boolean,\n) {\n if (jsonl) {\n for (let i = 0; i < rawLogs.length; i++) {\n const log = rawLogs[i];\n write(JSON.stringify(log));\n }\n return;\n }\n\n for (let i = 0; i < rawLogs.length; i++) {\n const log = rawLogs[i];\n if (log.logLines) {\n const id = log.identifier;\n const udfType = log.udfType;\n const timestampMs = log.timestamp * 1000;\n const executionTimeMs =\n \"executionTime\" in log ? log.executionTime * 1000 : NaN;\n\n for (let j = 0; j < log.logLines.length; j++) {\n const formatted = formatLogLineMessage(\n \"info\",\n timestampMs,\n udfType,\n id,\n log.logLines[j],\n chalkInstance,\n );\n write(formatted);\n }\n\n if (\"error\" in log && log.error) {\n const formatted = formatLogLineMessage(\n \"error\",\n timestampMs,\n udfType,\n id,\n log.error!,\n chalkInstance,\n );\n write(formatted);\n } else if (log.kind === \"Completion\" && shouldShowSuccessLogs) {\n const formatted = chalkInstance.green(\n formatFunctionExecutionMessage(\n timestampMs,\n udfType,\n id,\n executionTimeMs,\n ),\n );\n write(formatted);\n }\n }\n }\n}\n\nexport function formatFunctionExecutionMessage(\n timestampMs: number,\n udfType: UdfType,\n udfPath: string,\n executionTimeMs: number,\n): string {\n return `${prefixLog(timestampMs, udfType, udfPath)} Function executed in ${Math.ceil(executionTimeMs)} ms`;\n}\n\nexport function formatLogLineMessage(\n type: \"info\" | \"error\",\n timestampMs: number,\n udfType: UdfType,\n udfPath: string,\n message: LogLine,\n chalkInstance: typeof chalk = chalk,\n): string {\n const prefix = prefixForSource(udfType);\n if (typeof message === \"string\") {\n if (type === \"info\") {\n const match = message.match(/^\\[.*?\\] /);\n if (match === null) {\n return chalkInstance.red(\n `[CONVEX ${prefix}(${udfPath})] Could not parse console.log`,\n );\n }\n const level = message.slice(1, match[0].length - 2);\n const args = message.slice(match[0].length);\n return `${chalkInstance.cyan(`${prefixLog(timestampMs, udfType, udfPath)} [${level}]`)} ${format(args)}`;\n } else {\n return chalkInstance.red(\n `${prefixLog(timestampMs, udfType, udfPath)} ${message}`,\n );\n }\n } else {\n const level = message.level;\n const formattedMessage = `${message.messages.join(\" \")}${message.isTruncated ? \" (truncated due to length)\" : \"\"}`;\n return `${chalkInstance.cyan(\n `${prefixLog(message.timestamp, udfType, udfPath)} [${level}]`,\n )} ${formattedMessage}`;\n }\n}\n\nfunction logToDestination(dest: LogDestination, s: string) {\n switch (dest) {\n case \"stdout\":\n logOutput(s);\n break;\n case \"stderr\":\n logMessage(s);\n break;\n }\n}\n\nfunction prefixLog(timestampMs: number, udfType: UdfType, udfPath: string) {\n const prefix = prefixForSource(udfType);\n const localizedTimestamp = new Date(timestampMs).toLocaleString();\n\n return `${localizedTimestamp} [CONVEX ${prefix}(${udfPath})]`;\n}\n\nexport function formatLogsAsText(\n rawLogs: FunctionExecution[],\n shouldShowSuccessLogs: boolean = false,\n): string {\n const lines: string[] = [];\n const write = (message: string) =>\n lines.push(stripVTControlCharacters(message));\n processLogs(rawLogs, write, shouldShowSuccessLogs, chalk);\n return lines.join(\"\\n\");\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["import_node_util", "chalk"]
|
|
7
7
|
}
|
package/dist/cjs/cli/lib/run.js
CHANGED
|
@@ -76,6 +76,14 @@ async function runFunctionAndLog(ctx, args) {
|
|
|
76
76
|
);
|
|
77
77
|
} catch (err) {
|
|
78
78
|
const errorMessage = err.toString().trim();
|
|
79
|
+
const permissionError = (0, import_utils.permissionDeniedErrorMessage)(errorMessage);
|
|
80
|
+
if (permissionError !== null) {
|
|
81
|
+
return await ctx.crash({
|
|
82
|
+
exitCode: 1,
|
|
83
|
+
errorType: "fatal",
|
|
84
|
+
printedMessage: import_chalk.chalkStderr.red(permissionError)
|
|
85
|
+
});
|
|
86
|
+
}
|
|
79
87
|
if (errorMessage.includes("Could not find function")) {
|
|
80
88
|
const functions = await runSystemQuery(ctx, {
|
|
81
89
|
deploymentUrl: args.deploymentUrl,
|
|
@@ -300,12 +308,29 @@ async function subscribeAndLog(ctx, args) {
|
|
|
300
308
|
}
|
|
301
309
|
});
|
|
302
310
|
}
|
|
303
|
-
async function subscribe(
|
|
311
|
+
async function subscribe(ctx, args) {
|
|
304
312
|
const client = new import_browser.BaseConvexClient(
|
|
305
313
|
args.deploymentUrl,
|
|
306
314
|
(updatedQueries) => {
|
|
307
315
|
for (const queryToken of updatedQueries) {
|
|
308
|
-
|
|
316
|
+
let result;
|
|
317
|
+
try {
|
|
318
|
+
result = client.localQueryResultByToken(queryToken);
|
|
319
|
+
} catch (err) {
|
|
320
|
+
const permissionError = (0, import_utils.permissionDeniedErrorMessage)(
|
|
321
|
+
err.toString()
|
|
322
|
+
);
|
|
323
|
+
if (permissionError !== null) {
|
|
324
|
+
void ctx.crash({
|
|
325
|
+
exitCode: 1,
|
|
326
|
+
errorType: "fatal",
|
|
327
|
+
printedMessage: import_chalk.chalkStderr.red(permissionError)
|
|
328
|
+
});
|
|
329
|
+
return;
|
|
330
|
+
}
|
|
331
|
+
throw err;
|
|
332
|
+
}
|
|
333
|
+
args.callbacks?.onChange?.(result);
|
|
309
334
|
}
|
|
310
335
|
},
|
|
311
336
|
{
|