convex
Version:
Client for the Convex Cloud
227 lines (226 loc) • 8.57 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 dev_exports = {};
__export(dev_exports, {
dev: () => dev
});
module.exports = __toCommonJS(dev_exports);
var import_boxen = __toESM(require("boxen"));
var import_chalk = __toESM(require("chalk"));
var import_commander = require("commander");
var import_http_proxy = __toESM(require("http-proxy"));
var import_path = __toESM(require("path"));
var import_perf_hooks = require("perf_hooks");
var import_clientConfig = require("./codegen_templates/clientConfig");
var import_api = require("./lib/api");
var import_config = require("./lib/config");
var import_context = require("./lib/context");
var import_login = require("./lib/login");
var import_push = require("./lib/push");
var import_utils = require("./lib/utils");
var import_watch = require("./lib/watch");
const dev = new import_commander.Command("dev").description(
"Watch the local filesystem. When your Convex functions change, push them to your dev deployment and update generated code."
).option("-v, --verbose", "Show full listing of changes").addOption(
new import_commander.Option(
"--typecheck <mode>",
`Check TypeScript files with \`tsc --noEmit\`.`
).choices(["enable", "try", "disable"]).default("try")
).addOption(new import_commander.Option("--trace-events").hideHelp()).addOption(new import_commander.Option("--once").hideHelp()).addOption(new import_commander.Option("--admin-key <adminKey>").hideHelp()).addOption(new import_commander.Option("--url <url>").hideHelp()).addOption(
new import_commander.Option("--codegen <mode>", "Regenerate code in `convex/_generated/`").choices(["enable", "disable"]).default("enable")
).action(async (cmdOptions) => {
const ctx = import_context.oneoffContext;
if (!cmdOptions.url || !cmdOptions.adminKey) {
if (!await (0, import_login.checkAuthorization)(ctx)) {
await (0, import_login.performLogin)(ctx);
}
}
await (0, import_utils.ensureProjectDirectory)(ctx, true);
const config = await (0, import_config.readProjectConfig)(import_context.oneoffContext);
const projectSlug = config.projectConfig.project;
const teamSlug = config.projectConfig.team;
let devDeployment;
if (!cmdOptions.url || !cmdOptions.adminKey) {
devDeployment = await (0, import_api.getDevDeployment)(import_context.oneoffContext, {
projectSlug,
teamSlug
});
}
const adminKey = cmdOptions.adminKey ?? devDeployment?.adminKey;
const url = cmdOptions.url ?? devDeployment?.url;
const options = {
adminKey,
verbose: !!cmdOptions.verbose,
dryRun: false,
typecheck: cmdOptions.typecheck,
debug: false,
codegen: cmdOptions.codegen === "enable",
deploymentType: "dev",
url
};
let watcher;
let numFailures = 0;
import_http_proxy.default.createProxyServer({
target: url,
ws: true,
changeOrigin: true
}).on("error", (err) => {
console.log(
`Network error connecting to dev deployment: ${err.message}`
);
}).listen(import_clientConfig.LOCALHOST_PORT);
const boxedText = import_chalk.default.whiteBright.bold("Personal dev deployment ready!") + import_chalk.default.white(
"\n\nKeep this command running to sync Convex functions when they change."
);
const boxenOptions = {
align: "center",
padding: 1,
margin: 1,
borderColor: "green",
backgroundColor: "#555555"
};
if (!cmdOptions.once) {
console.log((0, import_boxen.default)(boxedText, boxenOptions));
}
while (true) {
console.log("Preparing Convex functions...");
const start = import_perf_hooks.performance.now();
const ctx2 = new import_watch.WatchContext(cmdOptions.traceEvents);
const config2 = await (0, import_config.readProjectConfig)(ctx2);
if (projectSlug !== config2.projectConfig.project || teamSlug !== config2.projectConfig.team) {
console.log("Detected a change in your `convex.json`. Exiting...");
return await ctx2.fatalError(1, "fs");
}
try {
await (0, import_push.runPush)(ctx2, options);
const end = import_perf_hooks.performance.now();
numFailures = 0;
console.log(
import_chalk.default.green(
`Convex functions ready! (${(0, import_utils.formatDuration)(end - start)})`
)
);
} catch (e) {
if (!(e instanceof import_watch.FatalError) || !e.reason) {
throw e;
}
if (e.reason === "network") {
const delay = nextBackoff(numFailures);
numFailures += 1;
console.log(
import_chalk.default.yellow(
`Failed due to network error, retrying in ${(0, import_utils.formatDuration)(
delay
)}...`
)
);
await new Promise((resolve) => setTimeout(resolve, delay));
continue;
}
console.assert(e.reason === "fs");
if (cmdOptions.once) {
await ctx2.fatalError(1, "fs");
}
}
if (cmdOptions.once) {
return;
}
const observations = ctx2.fs.finalize();
if (observations === "invalidated") {
console.log("Filesystem changed during push, retrying...");
continue;
}
if (!watcher) {
watcher = new import_watch.Watcher(observations);
await watcher.ready();
}
watcher.update(observations);
let anyChanges = false;
do {
await watcher.waitForEvent();
for (const event of watcher.drainEvents()) {
if (cmdOptions.traceEvents) {
console.log(
"Processing",
event.name,
import_path.default.relative("", event.absPath)
);
}
const result = observations.overlaps(event);
if (result.overlaps) {
const relPath = import_path.default.relative("", event.absPath);
if (cmdOptions.traceEvents) {
console.log(`${relPath} ${result.reason}, rebuilding...`);
}
anyChanges = true;
break;
}
}
} while (!anyChanges);
let deadline = import_perf_hooks.performance.now() + quiescenceDelay;
while (true) {
const now = import_perf_hooks.performance.now();
if (now >= deadline) {
break;
}
const remaining = deadline - now;
if (cmdOptions.traceEvents) {
console.log(`Waiting for ${(0, import_utils.formatDuration)(remaining)} to quiesce...`);
}
const remainingWait = new Promise(
(resolve) => setTimeout(() => resolve("timeout"), deadline - now)
);
const result = await Promise.race([
remainingWait,
watcher.waitForEvent().then(() => "newEvents")
]);
if (result === "newEvents") {
for (const event of watcher.drainEvents()) {
const result2 = observations.overlaps(event);
if (result2.overlaps) {
if (cmdOptions.traceEvents) {
console.log(
`Received an overlapping event at ${event.absPath}, delaying push.`
);
}
deadline = import_perf_hooks.performance.now() + quiescenceDelay;
}
}
} else {
console.assert(result === "timeout");
}
}
}
});
const initialBackoff = 500;
const maxBackoff = 16e3;
const quiescenceDelay = 500;
function nextBackoff(prevFailures) {
const baseBackoff = initialBackoff * Math.pow(2, prevFailures);
const actualBackoff = Math.min(baseBackoff, maxBackoff);
const jitter = actualBackoff * (Math.random() - 0.5);
return actualBackoff + jitter;
}
//# sourceMappingURL=dev.js.map