import { program } from "commander"; import { version as packageVersion } from "../package.json"; import { resocketDev } from "./dev"; import { resocketDeploy } from "./deploy"; import { removeUserConfig } from "./config"; import { requireAuth } from "./auth/auth"; import { getUser, logout } from "./auth/user"; import { fromZodError } from "zod-validation-error"; import chalk from "chalk"; import ora from "ora"; import { ZodError } from "zod"; import { ResocketError } from "./errors/RescoketError"; import { CustomError } from "./errors/BaseError"; import { removeUndefined } from "./utils"; //*-------------------------------------------------------------------------------------------------- //* Core Commands, essential commands that make resocket cli */ //*-------------------------------------------------------------------------------------------------- program .name("resocket") .version(packageVersion, "-v, --version", "Output the current version") .description( "thankx for using resocket, hopefully we make your multiplayer development easier :)" ) .action(async () => { program.help(); }); program .command("dev") .description("starts a resocket dev server") .option("-c, --config ", "Path to config file") .option("-p, --port ", "Port to run the server on") .action(async (options) => { const overrides = { config: options.config, configOverrides: removeUndefined({ port: options.port ? parseInt(options.port) : undefined, }), }; await resocketDev(removeUndefined(overrides)); }); //todo evenutally add all the possible configs and cli commands & polish this program .command("deploy") .description("deploys the project to the cloud :)") .option("-c, --config ", "Path to config file") .action(async (options) => { const overrides = { config: options.config, }; await resocketDeploy(removeUndefined(overrides)); }); program .command("logout") .description("logsout the current user") .action(async () => { const spinner = ora(`${chalk.bold("logging out...")} `).start(); try { await logout(); spinner.succeed(chalk.green("Logged out successfully")); } catch (error) { spinner.clear(); throw error; } }); program .command("login") .description("logs the user in! uses the cli") .action(async () => { const user = await getUser(); console.log( chalk.green(`\nLogged in as ${chalk.bold(user.config.username)}`) ); //- done //[done] make the ui reidrect correctly //[done] refactor the stolen code & potentially make it look original or more closer to the docs itself //[done] store this session. somewhere //[done] make a fetch based on the api token stores //[done] make a helper to get & the user config //[done] complete the login auth cli properly //[done] make a delete functionlity. basically logout //[done] improving the hopepage of resocket //[done] improve a bit on the success/fail pages UI. even simple modal like page would also look nice //[done] make the 'not you? change account' feature work on the ui //[done] test in case of invalid json. for zod //[done] better zod errors //[done] process.exit zod errors //[done] add resocket clean command to clear .resocket files & stuff //[done] bug fix shooting meteor star //[done] refactor the above junk that youhave made ~ surprisingly enough it was not a mess //[done] add supabase to the backend //[done] add email list to supabase //[done] upsert create email on conflict do nothing //[done] sync users to the database (supabase) i.e. clerk webhooks or something. actually we will just use findOrCreate instead :) //*on it -- //todo move the backend api to backend dashboard instead! (no need for it to be on worker. just becomes an extra pain in the ass to manage. this way we can keep it all nice and tidy in a simple deployment //todo deploy command //*on it -- //todo--- progressively added overtime and then focus towards the ending //todo add proper zod to every part of the cli //todo add proper overrides for the cli //todo add proper program parameters foe the cli commands //todo------------------------------------------------------- //todo move on the deploy commands, once we have those up & running //todo deploy on a workers url just like wrangler would //todo upload projects and create other options like list-projects. active projects etc. //todo then create some sort of history for durable object uploads //todo also create option of dashboard & potentially even paid option of teams. //todo whipp out the cli properly and move on to working on the core framework itself. now that the logistics are mostly taken care of //todo a future potential way of linking cloudflare account in the dashboard //todo also vercel like account system & dashboard. even logs //todo map existing partykit features on a cli/infra/dashboard level //todo core framework from here on //todo will take more or less 2 weeks at min //todo since it is about quality i also want the users to experience good quality //todo only then we will use resocket's open source websocket library once we have something to launch here :) //todo honestly looks like about a month of effort or even 2 months worth of effort for now !! so strip useless things. //todo what is the experience that i want to give to the users }); program .command("clean") .description("removes internal config files for storing user auth") .action(() => { const spinner = ora(`${chalk.bold("cleaning...")} `).start(); removeUserConfig(); spinner.succeed(chalk.green("done!")); }); //*-------------------------------------------------------------------------------------------------- //* Additional Commands, non-essential but nice to have commands, aims to improve the ux */ //*-------------------------------------------------------------------------------------------------- const getCommand = program .command("get") .description( "displays the info about the deployed project, like 'resocket get rooms' will print the deployed rooms list" ); //todo this is going to be a sub command of someting like resocket get rooms, resocket get config, resocket get users(the cloud team for this project) etc getCommand .command("rooms") .description("[todo] displays the current rooms for the projects") .action(async () => { requireAuth(); //todo this gets the data from the api? // const { existingBindings } = await getBindings(getResocketConfig()); console.log( `[todo] This currently deployed project has the following rooms :)` ); // console.log(`rooms -> ` + existingBindings); }); const configCommand = program .command("config") .description( "config related operatoins for dx, like config prune, or config recover" ); configCommand .command("prune") .description( "[todo] this will esentially remove all the applied migrations from resocket.config" ); configCommand .command("recover") .description( "[todo] this will recover and add all the non existing already ran migrations. counterpart of reoscket config prune" ); program.parse(process.argv); process.on("uncaughtException", (err) => { if (err instanceof ZodError) { const error = new ResocketError({ coreMessage: fromZodError(err).toString(), complexity: "tricky", humanMessage: "\n\tThis is a validation error from zod, this can be hard to debug! make sure your resocket config is correct & please do reach out if you get stuck :)", proTip: "you can run 'resocket clean' command (this clears any stale config files that might be causing this error) and try again!", // docs: `[coming soon]`, }); error.printWithStack(); process.exit(1); } if (err instanceof CustomError) { err.print(); process.exit(1); } // Handle other types of errors console.error("Unhandled Error:", err); throw err; });