@base44-preview/cli 0.1.1-pr.555.067c6ae → 0.1.1-pr.555.6305311

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
@@ -163944,10 +163944,10 @@ var require_view = __commonJS((exports, module) => {
163944
163944
  var debug = require_src4()("express:view");
163945
163945
  var path18 = __require("node:path");
163946
163946
  var fs28 = __require("node:fs");
163947
- var dirname21 = path18.dirname;
163947
+ var dirname22 = path18.dirname;
163948
163948
  var basename7 = path18.basename;
163949
163949
  var extname2 = path18.extname;
163950
- var join27 = path18.join;
163950
+ var join28 = path18.join;
163951
163951
  var resolve12 = path18.resolve;
163952
163952
  module.exports = View;
163953
163953
  function View(name2, options8) {
@@ -163983,7 +163983,7 @@ var require_view = __commonJS((exports, module) => {
163983
163983
  for (var i5 = 0;i5 < roots.length && !path19; i5++) {
163984
163984
  var root2 = roots[i5];
163985
163985
  var loc = resolve12(root2, name2);
163986
- var dir = dirname21(loc);
163986
+ var dir = dirname22(loc);
163987
163987
  var file2 = basename7(loc);
163988
163988
  path19 = this.resolve(dir, file2);
163989
163989
  }
@@ -164009,12 +164009,12 @@ var require_view = __commonJS((exports, module) => {
164009
164009
  };
164010
164010
  View.prototype.resolve = function resolve13(dir, file2) {
164011
164011
  var ext = this.ext;
164012
- var path19 = join27(dir, file2);
164012
+ var path19 = join28(dir, file2);
164013
164013
  var stat2 = tryStat(path19);
164014
164014
  if (stat2 && stat2.isFile()) {
164015
164015
  return path19;
164016
164016
  }
164017
- path19 = join27(dir, basename7(file2, ext), "index" + ext);
164017
+ path19 = join28(dir, basename7(file2, ext), "index" + ext);
164018
164018
  stat2 = tryStat(path19);
164019
164019
  if (stat2 && stat2.isFile()) {
164020
164020
  return path19;
@@ -167709,7 +167709,7 @@ var require_send = __commonJS((exports, module) => {
167709
167709
  var Stream2 = __require("stream");
167710
167710
  var util2 = __require("util");
167711
167711
  var extname2 = path18.extname;
167712
- var join27 = path18.join;
167712
+ var join28 = path18.join;
167713
167713
  var normalize2 = path18.normalize;
167714
167714
  var resolve12 = path18.resolve;
167715
167715
  var sep = path18.sep;
@@ -167881,7 +167881,7 @@ var require_send = __commonJS((exports, module) => {
167881
167881
  return res;
167882
167882
  }
167883
167883
  parts = path19.split(sep);
167884
- path19 = normalize2(join27(root2, path19));
167884
+ path19 = normalize2(join28(root2, path19));
167885
167885
  } else {
167886
167886
  if (UP_PATH_REGEXP.test(path19)) {
167887
167887
  debug('malicious path "%s"', path19);
@@ -168021,7 +168021,7 @@ var require_send = __commonJS((exports, module) => {
168021
168021
  return self2.onStatError(err);
168022
168022
  return self2.error(404);
168023
168023
  }
168024
- var p4 = join27(path19, self2._index[i5]);
168024
+ var p4 = join28(path19, self2._index[i5]);
168025
168025
  debug('stat "%s"', p4);
168026
168026
  fs28.stat(p4, function(err2, stat2) {
168027
168027
  if (err2)
@@ -214616,7 +214616,7 @@ var require_buffer_list = __commonJS((exports, module) => {
214616
214616
  }
214617
214617
  }, {
214618
214618
  key: "join",
214619
- value: function join27(s5) {
214619
+ value: function join28(s5) {
214620
214620
  if (this.length === 0)
214621
214621
  return "";
214622
214622
  var p4 = this.head;
@@ -234018,7 +234018,7 @@ function normalizeBase44Env() {
234018
234018
  loadProjectEnvFiles();
234019
234019
 
234020
234020
  // src/cli/index.ts
234021
- import { dirname as dirname25, join as join30 } from "node:path";
234021
+ import { dirname as dirname26, join as join31 } from "node:path";
234022
234022
  import { fileURLToPath as fileURLToPath6 } from "node:url";
234023
234023
 
234024
234024
  // ../../node_modules/@clack/core/dist/index.mjs
@@ -243757,6 +243757,10 @@ var RealtimeHandlerConfigSchema = exports_external.object({
243757
243757
  name: exports_external.string().min(1),
243758
243758
  entry: exports_external.string().min(1)
243759
243759
  });
243760
+ var RealtimeHandlerSchemaFileSchema = exports_external.object({
243761
+ inbound: exports_external.unknown().optional(),
243762
+ outbound: exports_external.unknown().optional()
243763
+ });
243760
243764
  var DeployRealtimeHandlerResponseSchema = exports_external.object({
243761
243765
  status: exports_external.enum(["deployed", "unchanged"]),
243762
243766
  handler_name: exports_external.string().optional()
@@ -243764,7 +243768,8 @@ var DeployRealtimeHandlerResponseSchema = exports_external.object({
243764
243768
  var RealtimeHandlerSchema = RealtimeHandlerConfigSchema.extend({
243765
243769
  entryPath: exports_external.string().min(1),
243766
243770
  filePaths: exports_external.array(exports_external.string()).min(1),
243767
- source: ResourceSourceSchema
243771
+ source: ResourceSourceSchema,
243772
+ messageSchema: exports_external.unknown().optional()
243768
243773
  });
243769
243774
 
243770
243775
  // src/core/resources/realtime-handler/api.ts
@@ -243783,7 +243788,7 @@ async function deploySingleRealtimeHandler(name2, payload) {
243783
243788
  return result.data;
243784
243789
  }
243785
243790
  // src/core/resources/realtime-handler/config.ts
243786
- import { basename as basename4, dirname as dirname8, relative as relative3 } from "node:path";
243791
+ import { basename as basename4, dirname as dirname8, join as join10, relative as relative3 } from "node:path";
243787
243792
  async function readRealtimeHandler(entryFile, realtimeDir) {
243788
243793
  const handlerDir = dirname8(entryFile);
243789
243794
  const filePaths = await globby("**/*.ts", {
@@ -243801,12 +243806,25 @@ async function readRealtimeHandler(entryFile, realtimeDir) {
243801
243806
  });
243802
243807
  }
243803
243808
  const entry = basename4(entryFile);
243809
+ const schemaPath = join10(handlerDir, "schema.jsonc");
243810
+ let messageSchema;
243811
+ if (await pathExists(schemaPath)) {
243812
+ const parsed = await readJsonFile(schemaPath);
243813
+ const result = RealtimeHandlerSchemaFileSchema.safeParse(parsed);
243814
+ if (result.success) {
243815
+ messageSchema = {
243816
+ inbound: result.data.inbound,
243817
+ outbound: result.data.outbound
243818
+ };
243819
+ }
243820
+ }
243804
243821
  return {
243805
243822
  name: name2,
243806
243823
  entry,
243807
243824
  entryPath: entryFile,
243808
243825
  filePaths,
243809
- source: { type: "project" }
243826
+ source: { type: "project" },
243827
+ messageSchema
243810
243828
  };
243811
243829
  }
243812
243830
  async function readAllRealtimeHandlers(realtimeDir) {
@@ -243925,7 +243943,14 @@ class ProjectConfigReader {
243925
243943
  }
243926
243944
  async readProjectResources(configPath, project) {
243927
243945
  const configDir = dirname10(configPath);
243928
- const [entities, functions, realtimeHandlers, agents, connectors, authConfig] = await Promise.all([
243946
+ const [
243947
+ entities,
243948
+ functions,
243949
+ realtimeHandlers,
243950
+ agents,
243951
+ connectors,
243952
+ authConfig
243953
+ ] = await Promise.all([
243929
243954
  entityResource.readAll(join11(configDir, project.entitiesDir)),
243930
243955
  functionResource.readAll(join11(configDir, project.functionsDir)),
243931
243956
  realtimeHandlerResource.readAll(join11(configDir, project.realtimeDir)),
@@ -243933,7 +243958,14 @@ class ProjectConfigReader {
243933
243958
  connectorResource.readAll(join11(configDir, project.connectorsDir)),
243934
243959
  authConfigResource.readAll(join11(configDir, project.authDir))
243935
243960
  ]);
243936
- return { entities, functions, realtimeHandlers, agents, connectors, authConfig };
243961
+ return {
243962
+ entities,
243963
+ functions,
243964
+ realtimeHandlers,
243965
+ agents,
243966
+ connectors,
243967
+ authConfig
243968
+ };
243937
243969
  }
243938
243970
  assertPluginProjectDoesNotLoadPlugins(project, configPath) {
243939
243971
  if (project.plugin && project.plugins.length > 0) {
@@ -244322,7 +244354,15 @@ async function createArchive(pathToArchive, targetArchivePath) {
244322
244354
  }
244323
244355
  // src/core/project/deploy.ts
244324
244356
  function hasResourcesToDeploy(projectData) {
244325
- const { project, entities, functions, realtimeHandlers, agents, connectors, authConfig } = projectData;
244357
+ const {
244358
+ project,
244359
+ entities,
244360
+ functions,
244361
+ realtimeHandlers,
244362
+ agents,
244363
+ connectors,
244364
+ authConfig
244365
+ } = projectData;
244326
244366
  const hasSite = Boolean(project.site?.outputDirectory);
244327
244367
  const hasEntities = entities.length > 0;
244328
244368
  const hasFunctions = functions.length > 0;
@@ -244333,7 +244373,15 @@ function hasResourcesToDeploy(projectData) {
244333
244373
  return hasEntities || hasFunctions || hasRealtimeHandlers || hasAgents || hasConnectors || hasAuthConfig || hasSite;
244334
244374
  }
244335
244375
  async function deployAll(projectData, options) {
244336
- const { project, entities, functions, realtimeHandlers, agents, connectors, authConfig } = projectData;
244376
+ const {
244377
+ project,
244378
+ entities,
244379
+ functions,
244380
+ realtimeHandlers,
244381
+ agents,
244382
+ connectors,
244383
+ authConfig
244384
+ } = projectData;
244337
244385
  await entityResource.push(entities);
244338
244386
  await deployFunctionsSequentially(functions, {
244339
244387
  onStart: options?.onFunctionStart,
@@ -253285,125 +253333,12 @@ function getFunctionsCommand() {
253285
253333
  return new Command("functions").description("Manage backend functions").addCommand(getDeployCommand()).addCommand(getDeleteCommand()).addCommand(getListCommand()).addCommand(getPullCommand());
253286
253334
  }
253287
253335
 
253288
- // src/cli/commands/realtime/deploy.ts
253289
- function parseNames3(args) {
253290
- return args.flatMap((arg) => arg.split(",")).map((n2) => n2.trim()).filter(Boolean);
253291
- }
253292
- function resolveHandlersToDeploy(names, allHandlers) {
253293
- if (names.length === 0)
253294
- return allHandlers;
253295
- const notFound = names.filter((n2) => !allHandlers.some((h2) => h2.name === n2));
253296
- if (notFound.length > 0) {
253297
- throw new InvalidInputError(`Realtime handler${notFound.length > 1 ? "s" : ""} not found in project: ${notFound.join(", ")}`);
253298
- }
253299
- return allHandlers.filter((h2) => names.includes(h2.name));
253300
- }
253301
- function formatDeployResult2(result, log) {
253302
- const label = result.name.padEnd(25);
253303
- if (result.status === "deployed") {
253304
- const timing = result.durationMs ? theme.styles.dim(` (${(result.durationMs / 1000).toFixed(1)}s)`) : "";
253305
- log.success(`${label} deployed${timing}`);
253306
- } else if (result.status === "unchanged") {
253307
- log.success(`${label} unchanged`);
253308
- } else {
253309
- log.error(`${label} error: ${result.error}`);
253310
- }
253311
- }
253312
- function buildDeploySummary2(results) {
253313
- const deployed = results.filter((r) => r.status === "deployed").length;
253314
- const unchanged = results.filter((r) => r.status === "unchanged").length;
253315
- const failed = results.filter((r) => r.status === "error").length;
253316
- const parts = [];
253317
- if (deployed > 0)
253318
- parts.push(`${deployed} deployed`);
253319
- if (unchanged > 0)
253320
- parts.push(`${unchanged} unchanged`);
253321
- if (failed > 0)
253322
- parts.push(`${failed} error${failed !== 1 ? "s" : ""}`);
253323
- return parts.join(", ") || "No realtime handlers deployed";
253324
- }
253325
- async function deployRealtimeAction({ log }, names) {
253326
- const { realtimeHandlers } = await readProjectConfig();
253327
- const toDeploy = resolveHandlersToDeploy(names, realtimeHandlers);
253328
- if (toDeploy.length === 0) {
253329
- return {
253330
- outroMessage: "No realtime handlers found. Create handlers in the 'realtime' directory."
253331
- };
253332
- }
253333
- log.info(`Found ${toDeploy.length} ${toDeploy.length === 1 ? "realtime handler" : "realtime handlers"} to deploy`);
253334
- let completed = 0;
253335
- const total = toDeploy.length;
253336
- const results = await deployRealtimeHandlersSequentially(toDeploy, {
253337
- onStart: (startNames) => {
253338
- const label = startNames.length === 1 ? startNames[0] : `${startNames.length} realtime handlers`;
253339
- log.step(theme.styles.dim(`[${completed + 1}/${total}] Deploying ${label}...`));
253340
- },
253341
- onResult: (result) => {
253342
- completed++;
253343
- formatDeployResult2(result, log);
253344
- }
253345
- });
253346
- const hasFailures = results.some((r) => r.status === "error");
253347
- if (hasFailures) {
253348
- log.message(buildDeploySummary2(results));
253349
- throw new CLIExitError(1);
253350
- }
253351
- return { outroMessage: buildDeploySummary2(results) };
253352
- }
253353
- function getDeployCommand2() {
253354
- return new Base44Command("deploy").description("Deploy realtime handlers to Base44").argument("[names...]", "Handler names to deploy (deploys all if omitted)").action(async (ctx, rawNames) => {
253355
- const names = parseNames3(rawNames);
253356
- return deployRealtimeAction(ctx, names);
253357
- });
253358
- }
253359
-
253360
- // src/cli/commands/realtime/new.ts
253361
- import { join as join22 } from "node:path";
253362
- function buildHandlerScaffold(handlerName) {
253363
- return `import { RealtimeHandler, type Conn } from "base44";
253364
-
253365
- export class ${handlerName} extends RealtimeHandler {
253366
- handleConnect(conn: Conn) {
253367
- console.log("Connected:", conn.userId);
253368
- }
253369
- handleMessage(conn: Conn, msg: unknown) {
253370
- console.log("Message:", msg);
253371
- }
253372
- handleTick() {}
253373
- handleClose(conn: Conn) {}
253374
- }
253375
- `;
253376
- }
253377
- async function newRealtimeHandlerAction(_ctx, handlerName) {
253378
- const { project: project2 } = await readProjectConfig();
253379
- const realtimeDir = join22(project2.root, project2.realtimeDir);
253380
- const handlerDir = join22(realtimeDir, handlerName);
253381
- if (await pathExists(handlerDir)) {
253382
- throw new InvalidInputError(`Realtime handler "${handlerName}" already exists at ${handlerDir}`);
253383
- }
253384
- const entryPath = join22(handlerDir, "entry.ts");
253385
- await writeFile(entryPath, buildHandlerScaffold(handlerName));
253386
- return {
253387
- outroMessage: `Created realtime handler "${handlerName}" at ${entryPath}`
253388
- };
253389
- }
253390
- function getNewCommand() {
253391
- return new Base44Command("new").description("Create a new realtime handler scaffold").argument("<HandlerName>", "Name of the realtime handler class").action(async (ctx, handlerName) => {
253392
- return newRealtimeHandlerAction(ctx, handlerName);
253393
- });
253394
- }
253395
-
253396
- // src/cli/commands/realtime/index.ts
253397
- function getRealtimeCommand() {
253398
- return new Command("realtime").description("Manage realtime handlers").addCommand(getNewCommand()).addCommand(getDeployCommand2());
253399
- }
253400
-
253401
253336
  // src/cli/commands/project/create.ts
253402
253337
  import { basename as basename5, resolve as resolve7 } from "node:path";
253403
253338
  var import_kebabCase = __toESM(require_kebabCase(), 1);
253404
253339
 
253405
253340
  // src/cli/commands/project/scaffold-shared.ts
253406
- import { join as join23 } from "node:path";
253341
+ import { join as join22 } from "node:path";
253407
253342
  var DEFAULT_TEMPLATE_ID = "backend-only";
253408
253343
  async function getTemplateById(templateId) {
253409
253344
  const templates = await listTemplates();
@@ -253462,7 +253397,7 @@ async function completeProjectSetup({
253462
253397
  updateMessage("Building project...");
253463
253398
  await execa({ cwd: resolvedPath, shell: true })`${buildCommand}`;
253464
253399
  updateMessage("Deploying site...");
253465
- return await deploySite(join23(resolvedPath, outputDirectory));
253400
+ return await deploySite(join22(resolvedPath, outputDirectory));
253466
253401
  }, {
253467
253402
  successMessage: theme.colors.base44Orange("Site deployed successfully"),
253468
253403
  errorMessage: "Failed to deploy site"
@@ -253633,7 +253568,15 @@ async function deployAction({ isNonInteractive, log }, options = {}) {
253633
253568
  outroMessage: "No resources found to deploy"
253634
253569
  };
253635
253570
  }
253636
- const { project: project2, entities, functions, realtimeHandlers, agents, connectors, authConfig } = projectData;
253571
+ const {
253572
+ project: project2,
253573
+ entities,
253574
+ functions,
253575
+ realtimeHandlers,
253576
+ agents,
253577
+ connectors,
253578
+ authConfig
253579
+ } = projectData;
253637
253580
  const summaryLines = [];
253638
253581
  if (entities.length > 0) {
253639
253582
  summaryLines.push(` - ${entities.length} ${entities.length === 1 ? "entity" : "entities"}`);
@@ -253695,7 +253638,7 @@ ${summaryLines.join(`
253695
253638
  }
253696
253639
  return { outroMessage: "App deployed successfully" };
253697
253640
  }
253698
- function getDeployCommand3() {
253641
+ function getDeployCommand2() {
253699
253642
  return new Base44Command("deploy").description("Deploy all project resources (entities, functions, agents, connectors, and site)").option("-y, --yes", "Skip confirmation prompt").action(deployAction);
253700
253643
  }
253701
253644
  async function handleOAuthConnectors(connectorResults, isNonInteractive, options, log) {
@@ -253935,6 +253878,48 @@ function formatEntry(entry) {
253935
253878
  const message = entry.message.trim();
253936
253879
  return `${time3} ${level} ${message}`;
253937
253880
  }
253881
+ function entryKey(entry) {
253882
+ return `${entry.time} ${entry.message}`;
253883
+ }
253884
+ function selectNewEntries(entries, state) {
253885
+ const fresh = entries.filter((e2) => {
253886
+ if (e2.time < state.lastTime)
253887
+ return false;
253888
+ if (e2.time === state.lastTime && state.boundaryKeys.has(entryKey(e2))) {
253889
+ return false;
253890
+ }
253891
+ return true;
253892
+ });
253893
+ if (fresh.length === 0)
253894
+ return { fresh, nextState: state };
253895
+ const newMax = fresh.reduce((max, e2) => e2.time > max ? e2.time : max, state.lastTime);
253896
+ const boundaryKeys = newMax === state.lastTime ? new Set(state.boundaryKeys) : new Set;
253897
+ for (const e2 of fresh) {
253898
+ if (e2.time === newMax)
253899
+ boundaryKeys.add(entryKey(e2));
253900
+ }
253901
+ return { fresh, nextState: { lastTime: newMax, boundaryKeys } };
253902
+ }
253903
+ function writeFollowLine(entry, jsonMode) {
253904
+ const line = jsonMode ? JSON.stringify(entry) : formatEntry(entry);
253905
+ process.stdout.write(`${line}
253906
+ `);
253907
+ }
253908
+ async function followLogs(functionNames, options, availableFunctionNames, jsonMode) {
253909
+ let state = { lastTime: "", boundaryKeys: new Set };
253910
+ let first = true;
253911
+ while (true) {
253912
+ const pollOptions = first ? options : { ...options, since: state.lastTime };
253913
+ const entries = await fetchLogsForFunctions(functionNames, pollOptions, availableFunctionNames);
253914
+ const { fresh, nextState } = selectNewEntries(entries, state);
253915
+ state = nextState;
253916
+ fresh.sort((a2, b) => a2.time.localeCompare(b.time));
253917
+ for (const entry of fresh)
253918
+ writeFollowLine(entry, jsonMode);
253919
+ first = false;
253920
+ await new Promise((resolve8) => setTimeout(resolve8, 2000));
253921
+ }
253922
+ }
253938
253923
  function formatLogs(entries, env3) {
253939
253924
  if (entries.length === 0) {
253940
253925
  if (env3 === "prod") {
@@ -254016,6 +254001,16 @@ async function logsAction(ctx, options) {
254016
254001
  outroMessage: localProjectRoot ? "No functions found in this project." : "No functions found in this app."
254017
254002
  };
254018
254003
  }
254004
+ if (options.follow) {
254005
+ if (options.until) {
254006
+ throw new InvalidInputError("--until cannot be combined with --follow (a stream has no end).");
254007
+ }
254008
+ if (options.order) {
254009
+ throw new InvalidInputError("--order cannot be combined with --follow (a live tail always streams oldest to newest).");
254010
+ }
254011
+ options.order = "asc";
254012
+ return followLogs(functionNames, options, availableFunctionNames, ctx.jsonMode);
254013
+ }
254019
254014
  let entries = await fetchLogsForFunctions(functionNames, options, availableFunctionNames);
254020
254015
  const limit = options.limit ? Number.parseInt(options.limit, 10) : undefined;
254021
254016
  if (limit !== undefined && entries.length > limit) {
@@ -254031,7 +254026,7 @@ async function logsAction(ctx, options) {
254031
254026
  };
254032
254027
  }
254033
254028
  function getLogsCommand() {
254034
- return new Base44Command("logs").description("Fetch function logs for this app").option("--function <names>", "Filter by function name(s), comma-separated. If omitted, fetches logs for all project functions").option("--since <datetime>", "Show logs from this time. ISO datetime or relative shorthand (e.g. 1h, 30m, 2d)", normalizeDatetime).option("--until <datetime>", "Show logs until this time. ISO datetime or relative shorthand (e.g. 1h, 30m, 2d)", normalizeDatetime).addOption(new Option("--level <level>", "Filter by log level").choices([...LogLevelSchema.options]).hideHelp()).option("-n, --limit <n>", "Results per page (1-1000, default: 50)").addOption(new Option("--order <order>", "Sort order").choices(["asc", "desc"])).addOption(new Option("--env <env>", "Which deployment to read logs from: preview (current draft) or prod (published). Default: preview").choices([...LogEnvSchema.options])).action(logsAction);
254029
+ return new Base44Command("logs").description("Fetch function logs for this app").option("--function <names>", "Filter by function name(s), comma-separated. If omitted, fetches logs for all project functions").option("--since <datetime>", "Show logs from this time. ISO datetime or relative shorthand (e.g. 1h, 30m, 2d)", normalizeDatetime).option("--until <datetime>", "Show logs until this time. ISO datetime or relative shorthand (e.g. 1h, 30m, 2d)", normalizeDatetime).addOption(new Option("--level <level>", "Filter by log level").choices([...LogLevelSchema.options]).hideHelp()).option("-n, --limit <n>", "Results per page (1-1000, default: 50)").option("-f, --follow", "Stream new logs as they arrive").addOption(new Option("--order <order>", "Sort order").choices(["asc", "desc"])).addOption(new Option("--env <env>", "Which deployment to read logs from: preview (current draft) or prod (published). Default: preview").choices([...LogEnvSchema.options])).action(logsAction);
254035
254030
  }
254036
254031
 
254037
254032
  // src/cli/commands/project/scaffold.ts
@@ -254088,6 +254083,127 @@ Examples:
254088
254083
  $ base44 scaffold my-app --app-id app_123 Scaffolds the current dir, named "my-app"`).action(scaffoldAction);
254089
254084
  }
254090
254085
 
254086
+ // src/cli/commands/realtime/deploy.ts
254087
+ function parseNames3(args) {
254088
+ return args.flatMap((arg) => arg.split(",")).map((n2) => n2.trim()).filter(Boolean);
254089
+ }
254090
+ function resolveHandlersToDeploy(names, allHandlers) {
254091
+ if (names.length === 0)
254092
+ return allHandlers;
254093
+ const notFound = names.filter((n2) => !allHandlers.some((h2) => h2.name === n2));
254094
+ if (notFound.length > 0) {
254095
+ throw new InvalidInputError(`Realtime handler${notFound.length > 1 ? "s" : ""} not found in project: ${notFound.join(", ")}`);
254096
+ }
254097
+ return allHandlers.filter((h2) => names.includes(h2.name));
254098
+ }
254099
+ function formatDeployResult2(result, log) {
254100
+ const label = result.name.padEnd(25);
254101
+ if (result.status === "deployed") {
254102
+ const timing = result.durationMs ? theme.styles.dim(` (${(result.durationMs / 1000).toFixed(1)}s)`) : "";
254103
+ log.success(`${label} deployed${timing}`);
254104
+ } else if (result.status === "unchanged") {
254105
+ log.success(`${label} unchanged`);
254106
+ } else {
254107
+ log.error(`${label} error: ${result.error}`);
254108
+ }
254109
+ }
254110
+ function buildDeploySummary2(results) {
254111
+ const deployed = results.filter((r) => r.status === "deployed").length;
254112
+ const unchanged = results.filter((r) => r.status === "unchanged").length;
254113
+ const failed = results.filter((r) => r.status === "error").length;
254114
+ const parts = [];
254115
+ if (deployed > 0)
254116
+ parts.push(`${deployed} deployed`);
254117
+ if (unchanged > 0)
254118
+ parts.push(`${unchanged} unchanged`);
254119
+ if (failed > 0)
254120
+ parts.push(`${failed} error${failed !== 1 ? "s" : ""}`);
254121
+ return parts.join(", ") || "No realtime handlers deployed";
254122
+ }
254123
+ async function deployRealtimeAction({ log }, names) {
254124
+ const { realtimeHandlers } = await readProjectConfig();
254125
+ const toDeploy = resolveHandlersToDeploy(names, realtimeHandlers);
254126
+ if (toDeploy.length === 0) {
254127
+ return {
254128
+ outroMessage: "No realtime handlers found. Create handlers in the 'realtime' directory."
254129
+ };
254130
+ }
254131
+ log.info(`Found ${toDeploy.length} ${toDeploy.length === 1 ? "realtime handler" : "realtime handlers"} to deploy`);
254132
+ let completed = 0;
254133
+ const total = toDeploy.length;
254134
+ const results = await deployRealtimeHandlersSequentially(toDeploy, {
254135
+ onStart: (startNames) => {
254136
+ const label = startNames.length === 1 ? startNames[0] : `${startNames.length} realtime handlers`;
254137
+ log.step(theme.styles.dim(`[${completed + 1}/${total}] Deploying ${label}...`));
254138
+ },
254139
+ onResult: (result) => {
254140
+ completed++;
254141
+ formatDeployResult2(result, log);
254142
+ }
254143
+ });
254144
+ const hasFailures = results.some((r) => r.status === "error");
254145
+ if (hasFailures) {
254146
+ log.message(buildDeploySummary2(results));
254147
+ throw new CLIExitError(1);
254148
+ }
254149
+ return { outroMessage: buildDeploySummary2(results) };
254150
+ }
254151
+ function getDeployCommand3() {
254152
+ return new Base44Command("deploy").description("Deploy realtime handlers to Base44").argument("[names...]", "Handler names to deploy (deploys all if omitted)").action(async (ctx, rawNames) => {
254153
+ const names = parseNames3(rawNames);
254154
+ return deployRealtimeAction(ctx, names);
254155
+ });
254156
+ }
254157
+
254158
+ // src/cli/commands/realtime/new.ts
254159
+ import { dirname as dirname19, join as join23 } from "node:path";
254160
+ function buildHandlerScaffold(handlerName) {
254161
+ return `import { RealtimeHandler, type Conn } from "@base44/sdk";
254162
+
254163
+ interface State {
254164
+ // shared state broadcast to all clients
254165
+ }
254166
+
254167
+ interface Message {
254168
+ // messages sent from clients
254169
+ }
254170
+
254171
+ export class ${handlerName} extends RealtimeHandler<State, Message> {
254172
+ handleConnect(conn: Conn) {
254173
+ console.log("Connected:", conn.userId);
254174
+ }
254175
+ handleMessage(conn: Conn, msg: Message) {
254176
+ console.log("Message:", msg);
254177
+ }
254178
+ handleTick() {}
254179
+ handleClose(conn: Conn) {}
254180
+ }
254181
+ `;
254182
+ }
254183
+ async function newRealtimeHandlerAction(_ctx, handlerName) {
254184
+ const { project: project2 } = await readProjectConfig();
254185
+ const realtimeDir = join23(dirname19(project2.configPath), project2.realtimeDir);
254186
+ const handlerDir = join23(realtimeDir, handlerName);
254187
+ if (await pathExists(handlerDir)) {
254188
+ throw new InvalidInputError(`Realtime handler "${handlerName}" already exists at ${handlerDir}`);
254189
+ }
254190
+ const entryPath = join23(handlerDir, "entry.ts");
254191
+ await writeFile(entryPath, buildHandlerScaffold(handlerName));
254192
+ return {
254193
+ outroMessage: `Created realtime handler "${handlerName}" at ${entryPath}`
254194
+ };
254195
+ }
254196
+ function getNewCommand() {
254197
+ return new Base44Command("new").description("Create a new realtime handler scaffold").argument("<HandlerName>", "Name of the realtime handler class").action(async (ctx, handlerName) => {
254198
+ return newRealtimeHandlerAction(ctx, handlerName);
254199
+ });
254200
+ }
254201
+
254202
+ // src/cli/commands/realtime/index.ts
254203
+ function getRealtimeCommand() {
254204
+ return new Command("realtime").description("Manage realtime handlers").addCommand(getNewCommand()).addCommand(getDeployCommand3());
254205
+ }
254206
+
254091
254207
  // src/core/resources/sandbox/schema.ts
254092
254208
  var FileErrorSchema = exports_external.object({
254093
254209
  code: exports_external.string(),
@@ -254559,6 +254675,7 @@ function getSiteCommand() {
254559
254675
  // src/core/types/generator.ts
254560
254676
  var import_common_tags = __toESM(require_lib2(), 1);
254561
254677
  var import_json_schema_to_typescript = __toESM(require_src3(), 1);
254678
+ import { join as join26 } from "node:path";
254562
254679
  var HEADER2 = import_common_tags.stripIndent`
254563
254680
  // Auto-generated by Base44 CLI - DO NOT EDIT
254564
254681
  // Regenerate with: base44 types generate
@@ -254567,24 +254684,43 @@ var EMPTY_TEMPLATE = import_common_tags.stripIndent`
254567
254684
  // Auto-generated by Base44 CLI - DO NOT EDIT
254568
254685
  // Regenerate with: base44 types
254569
254686
  //
254570
- // No entities, functions, agents, or connectors found in project.
254571
- // Add resources to base44/entities/, base44/functions/, base44/agents/, or base44/connectors/
254687
+ // No entities, functions, agents, connectors, or realtime handlers found in project.
254688
+ // Add resources to base44/entities/, base44/functions/, base44/agents/, base44/connectors/, or base44/realtime/
254572
254689
  // and run \`base44 types generate\` again.
254573
254690
 
254574
254691
  declare module '@base44/sdk' {
254575
254692
  // No types to augment - add resources and regenerate
254576
254693
  }
254577
254694
  `;
254695
+ var SDK_PACKAGE_NAMES = ["@base44/sdk", "@base44-preview/sdk"];
254696
+ async function detectSdkPackageName(projectRoot) {
254697
+ try {
254698
+ const pkg = await readJsonFile(join26(projectRoot, "package.json"));
254699
+ const deps = {
254700
+ ...pkg.dependencies,
254701
+ ...pkg.devDependencies
254702
+ };
254703
+ for (const name2 of SDK_PACKAGE_NAMES) {
254704
+ if (name2 in deps)
254705
+ return name2;
254706
+ }
254707
+ } catch {}
254708
+ return "@base44/sdk";
254709
+ }
254578
254710
  async function generateTypesFile(input) {
254579
254711
  const content = await generateContent(input);
254580
254712
  await writeFile(getTypesOutputPath(input.projectRoot), content);
254581
254713
  }
254582
254714
  async function generateContent(input) {
254583
- const { entities, functions, agents, connectors } = input;
254584
- if (!entities.length && !functions.length && !agents.length && !connectors.length) {
254715
+ const { entities, functions, agents, connectors, realtimeHandlers } = input;
254716
+ const sdkPackage = await detectSdkPackageName(input.projectRoot);
254717
+ if (!entities.length && !functions.length && !agents.length && !connectors.length && !realtimeHandlers.length) {
254585
254718
  return EMPTY_TEMPLATE;
254586
254719
  }
254587
- const entityInterfaces = await Promise.all(entities.map((e8) => compileEntity(e8)));
254720
+ const [entityInterfaces, realtimeRegistryEntries] = await Promise.all([
254721
+ Promise.all(entities.map((e8) => compileEntity(e8))),
254722
+ Promise.all(realtimeHandlers.map((h5) => compileRealtimeHandler(h5)))
254723
+ ]);
254588
254724
  const registryEntries = [
254589
254725
  [
254590
254726
  "EntityTypeRegistry",
@@ -254592,16 +254728,28 @@ async function generateContent(input) {
254592
254728
  ],
254593
254729
  ["FunctionNameRegistry", functions.map((f7) => `"${f7.name}": true;`)],
254594
254730
  ["AgentNameRegistry", agents.map((a5) => `"${a5.name}": true;`)],
254595
- ["ConnectorTypeRegistry", connectors.map((c8) => `"${c8.type}": true;`)]
254731
+ ["ConnectorTypeRegistry", connectors.map((c8) => `"${c8.type}": true;`)],
254732
+ [
254733
+ "RealtimeHandlerNameRegistry",
254734
+ realtimeHandlers.map((h5) => `"${h5.name}": true;`)
254735
+ ],
254736
+ [
254737
+ "RealtimeHandlerRegistry",
254738
+ realtimeHandlers.filter((h5) => h5.messageSchema).map((h5, _10, _arr) => {
254739
+ const idx = realtimeHandlers.indexOf(h5);
254740
+ return `"${h5.name}": ${realtimeRegistryEntries[idx]};`;
254741
+ })
254742
+ ]
254596
254743
  ];
254597
254744
  const registries2 = registryEntries.filter(([, entries]) => entries.length > 0).map(([name2, entries]) => registry2(name2, entries));
254598
254745
  return [
254599
254746
  HEADER2,
254747
+ "export {};",
254600
254748
  entityInterfaces.join(`
254601
254749
 
254602
254750
  `),
254603
254751
  import_common_tags.source`
254604
- declare module '@base44/sdk' {
254752
+ declare module '${sdkPackage}' {
254605
254753
  ${registries2.join(`
254606
254754
 
254607
254755
  `)}
@@ -254629,6 +254777,32 @@ async function compileEntity(entity2) {
254629
254777
  throw new TypeGenerationError(`Failed to generate types for entity "${name2}"`, name2, error48);
254630
254778
  }
254631
254779
  }
254780
+ async function compileRealtimeHandler(handler) {
254781
+ const { messageSchema } = handler;
254782
+ if (!messageSchema)
254783
+ return "{ inbound: unknown; outbound: unknown }";
254784
+ const compileSchema = async (schema11, typeName) => {
254785
+ if (!schema11)
254786
+ return "unknown";
254787
+ try {
254788
+ const ts8 = await import_json_schema_to_typescript.compile(schema11, typeName, {
254789
+ bannerComment: "",
254790
+ additionalProperties: false,
254791
+ strictIndexSignatures: true
254792
+ });
254793
+ const match = ts8.match(/\{([\s\S]*)\}/);
254794
+ return match ? `{
254795
+ ${match[1]}}` : "unknown";
254796
+ } catch {
254797
+ return "unknown";
254798
+ }
254799
+ };
254800
+ const [inbound, outbound] = await Promise.all([
254801
+ compileSchema(messageSchema.inbound, `${handler.name}Inbound`),
254802
+ compileSchema(messageSchema.outbound, `${handler.name}Outbound`)
254803
+ ]);
254804
+ return `{ inbound: ${inbound}; outbound: ${outbound} }`;
254805
+ }
254632
254806
  function registry2(name2, entries) {
254633
254807
  return import_common_tags.source`
254634
254808
  interface ${name2} {
@@ -254641,10 +254815,10 @@ function toPascalCase(name2) {
254641
254815
  return name2.split(/[-_\s]+/).map((w8) => w8.charAt(0).toUpperCase() + w8.slice(1)).join("");
254642
254816
  }
254643
254817
  // src/core/types/update-project.ts
254644
- import { join as join26 } from "node:path";
254818
+ import { join as join27 } from "node:path";
254645
254819
  var TYPES_INCLUDE_PATH = `${PROJECT_SUBDIR}/${TYPES_OUTPUT_SUBDIR}/*.d.ts`;
254646
254820
  async function updateProjectConfig(projectRoot) {
254647
- const tsconfigPath = join26(projectRoot, "tsconfig.json");
254821
+ const tsconfigPath = join27(projectRoot, "tsconfig.json");
254648
254822
  if (!await pathExists(tsconfigPath)) {
254649
254823
  return false;
254650
254824
  }
@@ -254668,14 +254842,15 @@ var TYPES_FILE_PATH = "base44/.types/types.d.ts";
254668
254842
  async function generateTypesAction({
254669
254843
  runTask: runTask2
254670
254844
  }) {
254671
- const { entities, functions, agents, connectors, project: project2 } = await readProjectConfig();
254845
+ const { entities, functions, agents, connectors, realtimeHandlers, project: project2 } = await readProjectConfig();
254672
254846
  await runTask2("Generating types", async () => {
254673
254847
  await generateTypesFile({
254674
254848
  projectRoot: project2.root,
254675
254849
  entities,
254676
254850
  functions,
254677
254851
  agents,
254678
- connectors
254852
+ connectors,
254853
+ realtimeHandlers
254679
254854
  });
254680
254855
  });
254681
254856
  const tsconfigUpdated = await updateProjectConfig(project2.root);
@@ -254695,7 +254870,7 @@ function getTypesCommand() {
254695
254870
  // src/cli/dev/dev-server/main.ts
254696
254871
  var import_cors = __toESM(require_lib4(), 1);
254697
254872
  var import_express6 = __toESM(require_express(), 1);
254698
- import { dirname as dirname23, join as join29 } from "node:path";
254873
+ import { dirname as dirname24, join as join30 } from "node:path";
254699
254874
 
254700
254875
  // ../../node_modules/get-port/index.js
254701
254876
  import net from "node:net";
@@ -257053,9 +257228,9 @@ class NodeFsHandler {
257053
257228
  if (this.fsw.closed) {
257054
257229
  return;
257055
257230
  }
257056
- const dirname22 = sp2.dirname(file2);
257231
+ const dirname23 = sp2.dirname(file2);
257057
257232
  const basename8 = sp2.basename(file2);
257058
- const parent = this.fsw._getWatchedDir(dirname22);
257233
+ const parent = this.fsw._getWatchedDir(dirname23);
257059
257234
  let prevStats = stats;
257060
257235
  if (parent.has(basename8))
257061
257236
  return;
@@ -257082,7 +257257,7 @@ class NodeFsHandler {
257082
257257
  prevStats = newStats2;
257083
257258
  }
257084
257259
  } catch (error48) {
257085
- this.fsw._remove(dirname22, basename8);
257260
+ this.fsw._remove(dirname23, basename8);
257086
257261
  }
257087
257262
  } else if (parent.has(basename8)) {
257088
257263
  const at13 = newStats.atimeMs;
@@ -258115,8 +258290,8 @@ async function createDevServer(options8) {
258115
258290
  broadcastEntityEvent(io6, appId, entityName, event);
258116
258291
  };
258117
258292
  const base44ConfigWatcher = new WatchBase44({
258118
- functions: join29(dirname23(project2.configPath), project2.functionsDir),
258119
- entities: join29(dirname23(project2.configPath), project2.entitiesDir)
258293
+ functions: join30(dirname24(project2.configPath), project2.functionsDir),
258294
+ entities: join30(dirname24(project2.configPath), project2.entitiesDir)
258120
258295
  }, devLogger);
258121
258296
  base44ConfigWatcher.on("change", async (name2) => {
258122
258297
  try {
@@ -258458,7 +258633,7 @@ function createProgram(context) {
258458
258633
  program2.addCommand(getCreateCommand());
258459
258634
  program2.addCommand(getScaffoldCommand());
258460
258635
  program2.addCommand(getDashboardCommand());
258461
- program2.addCommand(getDeployCommand3());
258636
+ program2.addCommand(getDeployCommand2());
258462
258637
  program2.addCommand(getLinkCommand());
258463
258638
  program2.addCommand(getEjectCommand());
258464
258639
  program2.addCommand(getEntitiesPushCommand());
@@ -258482,7 +258657,7 @@ var import_detect_agent = __toESM(require_dist5(), 1);
258482
258657
  import { release, type } from "node:os";
258483
258658
 
258484
258659
  // ../../node_modules/posthog-node/dist/extensions/error-tracking/modifiers/module.node.mjs
258485
- import { dirname as dirname24, posix, sep } from "path";
258660
+ import { dirname as dirname25, posix, sep } from "path";
258486
258661
  function createModulerModifier() {
258487
258662
  const getModuleFromFileName = createGetModuleFromFilename();
258488
258663
  return async (frames) => {
@@ -258491,7 +258666,7 @@ function createModulerModifier() {
258491
258666
  return frames;
258492
258667
  };
258493
258668
  }
258494
- function createGetModuleFromFilename(basePath = process.argv[1] ? dirname24(process.argv[1]) : process.cwd(), isWindows5 = sep === "\\") {
258669
+ function createGetModuleFromFilename(basePath = process.argv[1] ? dirname25(process.argv[1]) : process.cwd(), isWindows5 = sep === "\\") {
258495
258670
  const normalizedBase = isWindows5 ? normalizeWindowsPath2(basePath) : basePath;
258496
258671
  return (filename) => {
258497
258672
  if (!filename)
@@ -262680,9 +262855,9 @@ function addCommandInfoToErrorReporter(program2, errorReporter) {
262680
262855
  });
262681
262856
  }
262682
262857
  // src/cli/index.ts
262683
- var __dirname4 = dirname25(fileURLToPath6(import.meta.url));
262858
+ var __dirname4 = dirname26(fileURLToPath6(import.meta.url));
262684
262859
  async function runCLI(options8) {
262685
- ensureNpmAssets(join30(__dirname4, "../assets"));
262860
+ ensureNpmAssets(join31(__dirname4, "../assets"));
262686
262861
  const errorReporter = new ErrorReporter;
262687
262862
  errorReporter.registerProcessErrorHandlers();
262688
262863
  const jsonMode = process.argv.includes("--json");
@@ -262721,4 +262896,4 @@ export {
262721
262896
  CLIExitError
262722
262897
  };
262723
262898
 
262724
- //# debugId=C07D776F1F23BE1C64756E2164756E21
262899
+ //# debugId=8333EEB4A43A09C964756E2164756E21