@base44-preview/cli 0.0.55-pr.541.ec65d32 → 0.0.55-pr.545.0401b80

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/dist/cli/index.js CHANGED
@@ -234018,7 +234018,7 @@ function normalizeBase44Env() {
234018
234018
  loadProjectEnvFiles();
234019
234019
 
234020
234020
  // src/cli/index.ts
234021
- import { dirname as dirname23, join as join29 } from "node:path";
234021
+ import { dirname as dirname23, join as join28 } from "node:path";
234022
234022
  import { fileURLToPath as fileURLToPath6 } from "node:url";
234023
234023
 
234024
234024
  // ../../node_modules/@clack/core/dist/index.mjs
@@ -241707,7 +241707,7 @@ async function initAppContext(options = {}) {
241707
241707
  if (!id) {
241708
241708
  throw new InvalidInputError("App id cannot be empty.");
241709
241709
  }
241710
- cache2 = { id, projectRoot: projectRoot?.root };
241710
+ cache2 = { id };
241711
241711
  return cache2;
241712
241712
  }
241713
241713
  if (cache2) {
@@ -253953,10 +253953,6 @@ function getTypesCommand() {
253953
253953
  return new Command("types").description("Manage TypeScript type generation").addCommand(getTypesGenerateCommand());
253954
253954
  }
253955
253955
 
253956
- // src/cli/commands/dev.ts
253957
- import { join as join28 } from "node:path";
253958
- import process21 from "node:process";
253959
-
253960
253956
  // src/cli/dev/dev-server/main.ts
253961
253957
  var import_cors = __toESM(require_lib4(), 1);
253962
253958
  var import_express6 = __toESM(require_express(), 1);
@@ -254112,12 +254108,11 @@ var stringify = (item) => {
254112
254108
  return String(item);
254113
254109
  }
254114
254110
  };
254115
- function createDevLogger() {
254111
+ function createDevLogger(label2, labelColor = theme.styles.dim) {
254112
+ const prefix = label2 ? `${labelColor(`[${label2}]`)} ` : "";
254116
254113
  const print = (type, ...args) => {
254117
254114
  const colorize = colorByType[type];
254118
- console[type](...args.map((item) => {
254119
- return colorize(stringify(item));
254120
- }));
254115
+ console[type](prefix + args.map((item) => colorize(stringify(item))).join(" "));
254121
254116
  };
254122
254117
  return {
254123
254118
  log: (...args) => print("log", ...args),
@@ -255484,6 +255479,109 @@ function createCustomIntegrationRoutes(remoteProxy, logger2) {
255484
255479
  return router;
255485
255480
  }
255486
255481
 
255482
+ // src/cli/dev/dev-server/serve-runner.ts
255483
+ import { spawn as spawn3 } from "node:child_process";
255484
+ import process21 from "node:process";
255485
+
255486
+ class ServeRunner {
255487
+ command;
255488
+ cwd;
255489
+ env;
255490
+ logger;
255491
+ child;
255492
+ stopping = false;
255493
+ stopPromise;
255494
+ exitListeners = [];
255495
+ constructor(options8) {
255496
+ this.command = options8.command;
255497
+ this.cwd = options8.cwd;
255498
+ this.env = options8.env;
255499
+ this.logger = options8.logger;
255500
+ }
255501
+ start() {
255502
+ if (this.child) {
255503
+ return;
255504
+ }
255505
+ const stdin2 = process21.platform === "win32" ? "ignore" : "inherit";
255506
+ const child = spawn3(this.command, {
255507
+ cwd: this.cwd,
255508
+ shell: true,
255509
+ detached: process21.platform !== "win32",
255510
+ env: { ...process21.env, ...this.env },
255511
+ stdio: [stdin2, "pipe", "pipe"],
255512
+ windowsHide: true
255513
+ });
255514
+ this.child = child;
255515
+ this.setupHandlers(child);
255516
+ }
255517
+ onExit(listener) {
255518
+ this.exitListeners.push(listener);
255519
+ }
255520
+ async stop() {
255521
+ if (this.stopPromise) {
255522
+ return this.stopPromise;
255523
+ }
255524
+ this.stopPromise = this.stopChild();
255525
+ return this.stopPromise;
255526
+ }
255527
+ async stopChild() {
255528
+ const child = this.child;
255529
+ if (!child || child.exitCode !== null) {
255530
+ return;
255531
+ }
255532
+ this.stopping = true;
255533
+ const exited = new Promise((resolve10) => child.once("exit", () => resolve10()));
255534
+ if (process21.platform === "win32" && child.pid) {
255535
+ const taskkill = spawn3("taskkill", ["/pid", String(child.pid), "/T", "/F"], {
255536
+ stdio: "ignore",
255537
+ windowsHide: true
255538
+ });
255539
+ await new Promise((resolve10) => {
255540
+ taskkill.once("exit", () => resolve10());
255541
+ taskkill.once("error", () => resolve10());
255542
+ });
255543
+ } else if (child.pid) {
255544
+ try {
255545
+ process21.kill(-child.pid, "SIGTERM");
255546
+ } catch {
255547
+ child.kill();
255548
+ }
255549
+ }
255550
+ await exited;
255551
+ }
255552
+ setupHandlers(child) {
255553
+ child.stdout?.on("data", (data) => this.emitLines(data, "log"));
255554
+ child.stderr?.on("data", (data) => this.emitLines(data, "error"));
255555
+ child.on("error", (error48) => {
255556
+ this.logger.error("Frontend dev server failed to start:", error48);
255557
+ this.notifyExit(null);
255558
+ });
255559
+ child.on("exit", (code2) => {
255560
+ if (this.stopping) {
255561
+ return;
255562
+ }
255563
+ this.logger.error(`Frontend dev server exited with code ${code2}`);
255564
+ this.notifyExit(code2);
255565
+ });
255566
+ }
255567
+ notifyExit(code2) {
255568
+ for (const listener of this.exitListeners) {
255569
+ listener(code2);
255570
+ }
255571
+ }
255572
+ emitLines(data, type) {
255573
+ const lines = data.toString().trimEnd().split(`
255574
+ `);
255575
+ for (const line3 of lines) {
255576
+ if (type === "error") {
255577
+ this.logger.error(line3);
255578
+ } else {
255579
+ this.logger.log(line3);
255580
+ }
255581
+ }
255582
+ }
255583
+ }
255584
+
255487
255585
  // src/cli/dev/dev-server/watcher.ts
255488
255586
  import { EventEmitter as EventEmitter4 } from "node:events";
255489
255587
  import { relative as relative6 } from "node:path";
@@ -257189,7 +257287,7 @@ async function createDevServer(options8) {
257189
257287
  port: process.env.IS_TEST === "true" ? undefined : DEFAULT_PORT
257190
257288
  });
257191
257289
  const baseUrl = `http://localhost:${port}`;
257192
- const { functions, entities, project: project2 } = await options8.loadResources();
257290
+ const { functions, entities, project: project2, siteUrl } = await options8.loadResources();
257193
257291
  const app = import_express6.default();
257194
257292
  const remoteProxy = import_http_proxy_middleware2.createProxyMiddleware({
257195
257293
  target: BASE44_APP_URL,
@@ -257207,17 +257305,17 @@ async function createDevServer(options8) {
257207
257305
  }
257208
257306
  next();
257209
257307
  });
257210
- const devLogger = createDevLogger();
257308
+ const devLogger = createDevLogger("backend", theme.styles.info);
257211
257309
  const functionManager = new FunctionManager(functions, devLogger, options8.denoWrapperPath);
257212
257310
  const functionRoutes = createFunctionRouter(functionManager, devLogger);
257213
257311
  app.use("/api/apps/:appId/functions", functionRoutes);
257214
257312
  if (functionManager.getFunctionNames().length > 0) {
257215
- options8.log.info(`Loaded functions: ${functionManager.getFunctionNames().join(", ")}`);
257313
+ devLogger.log(`Loaded functions: ${functionManager.getFunctionNames().join(", ")}`);
257216
257314
  }
257217
257315
  const db2 = new Database;
257218
257316
  await db2.load(entities);
257219
257317
  if (db2.getCollectionNames().length > 0) {
257220
- options8.log.info(`Loaded entities: ${db2.getCollectionNames().join(", ")}`);
257318
+ devLogger.log(`Loaded entities: ${db2.getCollectionNames().join(", ")}`);
257221
257319
  }
257222
257320
  let emitEntityEvent = () => {};
257223
257321
  const entityRoutes = await createEntityRoutes(db2, devLogger, (...args) => emitEntityEvent(...args));
@@ -257249,6 +257347,12 @@ async function createDevServer(options8) {
257249
257347
  const customIntegrationRoutes = createCustomIntegrationRoutes(remoteProxy, devLogger);
257250
257348
  app.use("/api/apps/:appId/integrations/custom", customIntegrationRoutes);
257251
257349
  app.use((req, res, next) => {
257350
+ if (siteUrl && (req.path === "/login" || req.path.startsWith("/login/"))) {
257351
+ const targetUrl = new URL(req.originalUrl, siteUrl);
257352
+ devLogger.warn(`"${req.originalUrl}" requires hosted login, redirecting to ${targetUrl.toString()}`);
257353
+ res.redirect(targetUrl.toString());
257354
+ return;
257355
+ }
257252
257356
  if (!req.originalUrl.endsWith("analytics/track/batch")) {
257253
257357
  devLogger.warn(`"${req.originalUrl}" is not supported in local development, passing call to production`);
257254
257358
  }
@@ -257305,59 +257409,96 @@ async function createDevServer(options8) {
257305
257409
  }
257306
257410
  });
257307
257411
  await base44ConfigWatcher.start();
257308
- const shutdown = async () => {
257412
+ const serveCommand = project2.site?.serveCommand;
257413
+ let serveRunner;
257414
+ if (options8.appId && serveCommand) {
257415
+ serveRunner = new ServeRunner({
257416
+ command: serveCommand,
257417
+ cwd: project2.root,
257418
+ env: {
257419
+ VITE_BASE44_APP_ID: options8.appId,
257420
+ VITE_BASE44_APP_BASE_URL: baseUrl
257421
+ },
257422
+ logger: createDevLogger("frontend", theme.colors.base44Orange)
257423
+ });
257424
+ }
257425
+ const closeServer = () => new Promise((resolve12, reject) => {
257426
+ server.close((error48) => {
257427
+ if (error48) {
257428
+ reject(error48);
257429
+ return;
257430
+ }
257431
+ resolve12();
257432
+ });
257433
+ });
257434
+ const handleShutdownError = (error48) => {
257435
+ const errorMessage = error48 instanceof Error ? error48.message : String(error48);
257436
+ devLogger.error(`Failed to shut down dev server: ${errorMessage}`);
257437
+ };
257438
+ const runShutdown = async () => {
257309
257439
  base44ConfigWatcher.close();
257310
257440
  io6.close();
257311
257441
  await functionManager.stopAll();
257312
- server.close();
257442
+ await serveRunner?.stop();
257443
+ await closeServer();
257444
+ };
257445
+ let shutdownPromise;
257446
+ const shutdown = () => {
257447
+ shutdownPromise ??= runShutdown().catch(handleShutdownError);
257448
+ return shutdownPromise;
257313
257449
  };
257314
257450
  process.on("SIGINT", shutdown);
257315
257451
  process.on("SIGTERM", shutdown);
257316
- return { port, server };
257452
+ serveRunner?.onExit(() => {
257453
+ shutdown().finally(() => process.exit(1));
257454
+ });
257455
+ if (serveRunner) {
257456
+ devLogger.log(`Backend running on ${baseUrl}`);
257457
+ serveRunner.start();
257458
+ }
257459
+ return { port, server, isServingFrontend: serveRunner !== undefined };
257317
257460
  }
257318
257461
 
257319
257462
  // src/cli/commands/dev.ts
257320
257463
  function localServerUrl(port) {
257321
257464
  return `http://localhost:${port}`;
257322
257465
  }
257323
- async function writeEnvLocalIfMissing(projectRoot, port, log) {
257324
- const envLocalPath = join28(projectRoot, ".env.local");
257325
- if (await pathExists(envLocalPath)) {
257326
- return;
257466
+ function validateDevOptions(command2) {
257467
+ const { appId } = command2.optsWithGlobals();
257468
+ if (appId !== undefined) {
257469
+ command2.error(`base44 dev cannot be used with --app-id or ${BASE44_APP_ID_ENV_VAR}.`);
257327
257470
  }
257328
- const { id: appId } = await initAppContext();
257329
- await writeFile(envLocalPath, `VITE_BASE44_APP_ID=${appId}
257330
- VITE_BASE44_APP_BASE_URL=${localServerUrl(port)}
257331
- `);
257332
- log.info("Created .env.local with app ID and dev server URL");
257333
257471
  }
257334
- async function devAction({ log }, options8) {
257472
+ async function devAction(ctx, options8) {
257473
+ const { log, app } = ctx;
257474
+ if (!app?.projectRoot) {
257475
+ throw new ConfigInvalidError("base44 dev requires a linked local project. Run it from a project with base44/.app.jsonc.");
257476
+ }
257335
257477
  const port = options8.port ? Number(options8.port) : undefined;
257336
- let projectRoot;
257337
- const { port: resolvedPort } = await createDevServer({
257478
+ const appId = app.id;
257479
+ const siteUrlPromise = getSiteUrl().catch(() => {
257480
+ return;
257481
+ });
257482
+ const { port: resolvedPort, isServingFrontend } = await createDevServer({
257338
257483
  log,
257339
257484
  port,
257340
- cwd: process21.cwd(),
257485
+ appId,
257341
257486
  denoWrapperPath: getDenoWrapperPath(),
257342
257487
  loadResources: async () => {
257343
257488
  const { functions, entities, project: project2 } = await readProjectConfig();
257344
- projectRoot = project2.root;
257345
- return { functions, entities, project: project2 };
257489
+ const siteUrl = await siteUrlPromise;
257490
+ return { functions, entities, project: project2, siteUrl };
257346
257491
  }
257347
257492
  });
257348
- if (projectRoot) {
257349
- await writeEnvLocalIfMissing(projectRoot, resolvedPort, log);
257350
- }
257351
- return {
257352
- outroMessage: `Dev server is available at ${theme.colors.links(localServerUrl(resolvedPort))}`
257353
- };
257493
+ const outroMessage = isServingFrontend ? "Open your app using the frontend dev server URL" : `Dev server is available at ${theme.colors.links(localServerUrl(resolvedPort))}`;
257494
+ return { outroMessage };
257354
257495
  }
257355
257496
  function getDevCommand() {
257356
- return new Base44Command("dev").description("Start the development server").option("-p, --port <number>", "Port for the development server").action(devAction);
257497
+ return new Base44Command("dev").description("Start the development server").option("-p, --port <number>", "Port for the development server").hook("preAction", validateDevOptions).action(devAction);
257357
257498
  }
257358
257499
 
257359
257500
  // src/core/exec/run-script.ts
257360
- import { spawn as spawn3 } from "node:child_process";
257501
+ import { spawn as spawn4 } from "node:child_process";
257361
257502
  import { copyFileSync, writeFileSync as writeFileSync2 } from "node:fs";
257362
257503
  async function runScript(options8) {
257363
257504
  const { appId, code: code2 } = options8;
@@ -257376,7 +257517,7 @@ async function runScript(options8) {
257376
257517
  copyFileSync(getExecWrapperPath(), tempWrapper.path);
257377
257518
  try {
257378
257519
  const exitCode = await new Promise((resolvePromise) => {
257379
- const child = spawn3("deno", ["run", "--allow-all", "--node-modules-dir=auto", tempWrapper.path], {
257520
+ const child = spawn4("deno", ["run", "--allow-all", "--node-modules-dir=auto", tempWrapper.path], {
257380
257521
  env: {
257381
257522
  ...process.env,
257382
257523
  SCRIPT_PATH: scriptPath,
@@ -257585,7 +257726,7 @@ function createProgram(context) {
257585
257726
  program2.addCommand(getSiteCommand());
257586
257727
  program2.addCommand(getTypesCommand());
257587
257728
  program2.addCommand(getExecCommand());
257588
- program2.addCommand(getDevCommand(), { hidden: true });
257729
+ program2.addCommand(getDevCommand());
257589
257730
  program2.addCommand(getLogsCommand());
257590
257731
  return program2;
257591
257732
  }
@@ -261795,7 +261936,7 @@ function addCommandInfoToErrorReporter(program2, errorReporter) {
261795
261936
  // src/cli/index.ts
261796
261937
  var __dirname4 = dirname23(fileURLToPath6(import.meta.url));
261797
261938
  async function runCLI(options8) {
261798
- ensureNpmAssets(join29(__dirname4, "../assets"));
261939
+ ensureNpmAssets(join28(__dirname4, "../assets"));
261799
261940
  const errorReporter = new ErrorReporter;
261800
261941
  errorReporter.registerProcessErrorHandlers();
261801
261942
  const isNonInteractive = !process.stdin.isTTY || !process.stdout.isTTY;
@@ -261832,4 +261973,4 @@ export {
261832
261973
  CLIExitError
261833
261974
  };
261834
261975
 
261835
- //# debugId=7B666BE0D75AFE4564756E2164756E21
261976
+ //# debugId=EDDC63E20F783F9364756E2164756E21