convex
Version:
Client for the Convex Cloud
447 lines (446 loc) • 14.2 kB
JavaScript
;
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var utils_exports = {};
__export(utils_exports, {
bigBrainAPI: () => bigBrainAPI,
bigBrainClient: () => bigBrainClient,
deprecationCheckWarning: () => deprecationCheckWarning,
ensureHasConvexDependency: () => ensureHasConvexDependency,
ensureProjectDirectory: () => ensureProjectDirectory,
fatalServerErr: () => fatalServerErr,
formatDuration: () => formatDuration,
formatSize: () => formatSize,
functionsDir: () => functionsDir,
getAuthHeader: () => getAuthHeader,
globalConfigPath: () => globalConfigPath,
loadPackageJson: () => loadPackageJson,
poll: () => poll,
productionProvisionHost: () => productionProvisionHost,
prompt: () => prompt,
provisionHost: () => provisionHost,
rootDirectory: () => rootDirectory,
sorted: () => sorted,
validateOrSelectProject: () => validateOrSelectProject,
validateOrSelectTeam: () => validateOrSelectTeam
});
module.exports = __toCommonJS(utils_exports);
var import_axios = __toESM(require("axios"));
var import_chalk = __toESM(require("chalk"));
var import_inquirer = __toESM(require("inquirer"));
var readline = __toESM(require("readline"));
var import_path = __toESM(require("path"));
var import_os = __toESM(require("os"));
var import_zod = require("zod");
var import_config = require("./config.js");
var import_init = require("./init.js");
var import__ = require("../../index.js");
const productionProvisionHost = "https://provision.convex.dev";
const provisionHost = process.env.CONVEX_PROVISION_HOST || productionProvisionHost;
const BIG_BRAIN_URL = `${provisionHost}/api/${import__.version}`;
function prompt(query) {
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
return new Promise(
(resolve) => rl.question(query, (answer) => {
rl.close();
resolve(answer);
})
);
}
async function fatalServerErr(ctx, err) {
if (ctx.spinner) {
ctx.spinner.fail();
}
const res = err.response;
if (res) {
await deprecationCheckError(ctx, res);
console.error(
import_chalk.default.gray(
`${res.status} ${res.statusText}: ${res.data.code}: ${res.data.message}`
)
);
if (res.status == 401) {
console.error(
import_chalk.default.red("Log in to get an access token with `npx convex login`.")
);
}
} else {
console.error(import_chalk.default.gray(err));
}
return await ctx.fatalError(1, "network", err);
}
async function deprecationCheckError(ctx, resp) {
if (ctx.deprecationMessagePrinted) {
return;
}
const headers = resp.headers;
if (headers) {
const deprecationState = headers["x-convex-deprecation-state"];
const deprecationMessage = headers["x-convex-deprecation-message"];
switch (deprecationState) {
case void 0:
break;
case "Upgradable":
console.log(import_chalk.default.yellow(deprecationMessage));
break;
case "Deprecated":
case "UpgradeCritical":
console.log(import_chalk.default.red(deprecationMessage));
return await ctx.fatalError(1, "network");
default:
console.log(deprecationMessage);
break;
}
}
ctx.deprecationMessagePrinted = true;
}
function deprecationCheckWarning(ctx, resp) {
if (ctx.deprecationMessagePrinted) {
return;
}
const headers = resp.headers;
if (headers) {
const deprecationState = headers["x-convex-deprecation-state"];
const deprecationMessage = headers["x-convex-deprecation-message"];
switch (deprecationState) {
case void 0:
break;
case "Deprecated":
case "UpgradeCritical":
throw new Error(
"Called deprecationCheckWarning on a fatal error. This is a bug."
);
case "Upgradable":
console.log(import_chalk.default.yellow(deprecationMessage));
break;
default:
console.log(deprecationMessage);
break;
}
}
ctx.deprecationMessagePrinted = true;
}
async function validateOrSelectTeam(ctx, teamSlug, promptMessage) {
const teams = await bigBrainAPI(ctx, "GET", "teams");
if (teams.length == 0) {
console.error(import_chalk.default.red("Error: No teams found"));
throw new Error("No teams found");
}
if (!teamSlug) {
switch (teams.length) {
case 1:
return teams[0].slug;
default:
return (await import_inquirer.default.prompt([
{
name: "teamSlug",
message: promptMessage,
type: "list",
choices: teams.map((team) => ({
name: `${team.name} (${team.slug})`,
value: team.slug
}))
}
])).teamSlug;
}
} else {
if (!teams.find((team) => team.slug == teamSlug)) {
console.error(import_chalk.default.red(`Error: Team ${teamSlug} not found`));
throw new Error("Team not found");
}
return teamSlug;
}
}
async function validateOrSelectProject(ctx, projectSlug, teamSlug, singleProjectPrompt, multiProjectPrompt) {
const projects = await bigBrainAPI(ctx, "GET", `/teams/${teamSlug}/projects`);
if (projects.length == 0) {
console.error(import_chalk.default.red("Error: No projects found"));
throw new Error("No projects found");
}
if (!projectSlug) {
switch (projects.length) {
case 1: {
console.log("Found 1 project.");
const project = projects[0];
const confirmed = (await import_inquirer.default.prompt([
{
type: "confirm",
name: "confirmed",
message: `${singleProjectPrompt} ${project.name} (${project.slug})?`
}
])).confirmed;
if (!confirmed) {
return null;
}
return projects[0].slug;
}
default:
console.log(`Found ${projects.length} projects.`);
return (await import_inquirer.default.prompt([
{
name: "project",
message: multiProjectPrompt,
type: "list",
choices: projects.map((project) => ({
name: `${project.name} (${project.slug})`,
value: project.slug
}))
}
])).project;
}
} else {
if (!projects.find((project) => project.slug == projectSlug)) {
console.error(import_chalk.default.red(`Error: Project ${projectSlug} not found`));
throw new Error("Project not found");
}
return projectSlug;
}
}
class PackageJsonLoadError extends Error {
}
async function loadPackageJson(ctx) {
let packageJson;
try {
packageJson = ctx.fs.readUtf8File("package.json");
} catch (err) {
console.error(
import_chalk.default.red(
`Unable to read your package.json: ${err}. Make sure you're running this command from the root directory of a Convex app that contains the package.json`
)
);
return await ctx.fatalError(1, "fs");
}
let obj;
try {
obj = JSON.parse(packageJson);
} catch (err) {
console.error(import_chalk.default.red(`Unable to parse package.json: ${err}`));
return await ctx.fatalError(1, "fs", err);
}
if (typeof obj !== "object") {
throw new PackageJsonLoadError(
"Expected to parse an object from package.json"
);
}
const packages = [];
if (obj.dependencies) {
for (const dep in obj.dependencies) {
packages.push({ name: dep, version: obj.dependencies[dep] });
}
}
if (obj.devDependencies) {
for (const dep in obj.devDependencies) {
packages.push({ name: dep, version: obj.devDependencies[dep] });
}
}
return packages;
}
async function ensureHasConvexDependency(ctx, cmd) {
const packages = await loadPackageJson(ctx);
const hasConvexDependency = !!packages.filter(({ name }) => name === "convex").length;
if (!hasConvexDependency) {
console.error(
import_chalk.default.red(
`In order to ${cmd}, add \`convex\` to your package.json dependencies.`
)
);
return await ctx.fatalError(1, "fs");
}
}
const sorted = (arr, key) => {
const newArr = [...arr];
const cmp = (a, b) => {
if (key(a) < key(b))
return -1;
if (key(a) > key(b))
return 1;
return 0;
};
return newArr.sort(cmp);
};
function functionsDir(configPath, projectConfig) {
return import_path.default.join(import_path.default.dirname(configPath), projectConfig.functions);
}
function rootDirectory() {
let dirName;
if (process.env.CONVEX_PROVISION_HOST) {
dirName = ".convex-test";
} else {
dirName = ".convex";
}
return import_path.default.join(import_os.default.homedir(), dirName);
}
function globalConfigPath() {
return import_path.default.join(rootDirectory(), "config.json");
}
async function readGlobalConfig(ctx) {
const configPath = globalConfigPath();
let configFile;
try {
configFile = ctx.fs.readUtf8File(configPath);
} catch (err) {
return null;
}
try {
const schema = import_zod.z.object({
accessToken: import_zod.z.string().min(1)
});
const config = schema.parse(JSON.parse(configFile));
return config;
} catch (err) {
console.error(
import_chalk.default.red(
`Failed to parse global config in ${configPath} with error ${err}.`
)
);
return null;
}
}
async function getAuthHeader(ctx) {
if (process.env.CONVEX_OVERRIDE_ACCESS_TOKEN) {
return `Bearer ${process.env.CONVEX_OVERRIDE_ACCESS_TOKEN}`;
}
const globalConfig = await readGlobalConfig(ctx);
if (globalConfig) {
return `Bearer ${globalConfig.accessToken}`;
}
return null;
}
async function bigBrainClient(ctx) {
const authHeader = await getAuthHeader(ctx);
const headers = authHeader ? { Authorization: authHeader } : {};
return import_axios.default.create({
headers,
baseURL: BIG_BRAIN_URL
});
}
async function bigBrainAPI(ctx, method, url, data) {
let res;
try {
const client = await bigBrainClient(ctx);
res = await client.request({ url, method, data });
deprecationCheckWarning(ctx, res);
return res.data;
} catch (err) {
return await fatalServerErr(ctx, err);
}
}
const poll = async function(fetch, condition, waitMs = 1e3) {
let result = await fetch();
while (!condition(result)) {
await wait(waitMs);
result = await fetch();
}
return result;
};
const wait = function(waitMs) {
return new Promise((resolve) => {
setTimeout(resolve, waitMs);
});
};
function formatSize(n) {
if (n < 1024) {
return `${n} B`;
}
if (n < 1024 * 1024) {
return `${Math.floor(n / 1024)} KB`;
}
if (n < 1024 * 1024 * 1024) {
return `${Math.floor(n / 1024 / 1024)} MB`;
}
return `${n} B`;
}
function formatDuration(ms) {
const twoDigits = (n, unit) => `${n.toLocaleString("en-US", { maximumFractionDigits: 2 })}${unit}`;
if (ms < 1e-3) {
return twoDigits(ms * 1e9, "ns");
}
if (ms < 1) {
return twoDigits(ms * 1e3, "\xB5s");
}
if (ms < 1e3) {
return twoDigits(ms, "ms");
}
const s = ms / 1e3;
if (s < 60) {
return twoDigits(ms / 1e3, "s");
}
return twoDigits(s / 60, "m");
}
function findParentConfigs(ctx) {
const parentPackageJson = findUp(ctx, "package.json");
const candidateConvexJson = parentPackageJson && import_path.default.join(import_path.default.dirname(parentPackageJson), "convex.json");
const parentConvexJson = candidateConvexJson && ctx.fs.exists(candidateConvexJson) ? candidateConvexJson : void 0;
return {
parentPackageJson,
parentConvexJson
};
}
function findUp(ctx, filename) {
let curDir = import_path.default.resolve(".");
let parentDir = curDir;
do {
const candidate = import_path.default.join(curDir, filename);
if (ctx.fs.exists(candidate)) {
return candidate;
}
curDir = parentDir;
parentDir = import_path.default.dirname(curDir);
} while (parentDir !== curDir);
return;
}
async function ensureProjectDirectory(ctx, ensureConvexJson = false) {
const { parentPackageJson, parentConvexJson } = findParentConfigs(ctx);
if (!parentPackageJson) {
console.error(
"No package.json found. If you meant to create a new project, try"
);
console.error(`npx create-next-app@latest -e convex my-convex-app`);
await ctx.fatalError(1);
}
if (parentPackageJson !== import_path.default.resolve("package.json")) {
console.error("Run this command from the root directory of a project.");
return await ctx.fatalError(1, "fs");
}
if (ensureConvexJson && parentPackageJson && !parentConvexJson) {
const expected = await (0, import_config.configFilepath)(ctx);
console.error(`No convex.json file found at ${expected}`);
const { confirmed } = await import_inquirer.default.prompt([
{
type: "confirm",
name: "confirmed",
message: `Would you like to create a new Convex project here? (\`npx convex init\`)`
}
]);
if (!confirmed) {
console.error("Run `npx convex dev` in a directory with a convex.json.");
return await ctx.fatalError(1, "fs");
}
await (0, import_init.init)(ctx, null, null);
}
}
//# sourceMappingURL=utils.js.map