@base44-preview/cli 0.1.14-pr.624.af1a615 → 0.1.14-pr.626.0acc5e4

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
@@ -31177,7 +31177,7 @@ function cleanDoc(doc) {
31177
31177
  return mapDoc(doc, (currentDoc) => cleanDocFn(currentDoc));
31178
31178
  }
31179
31179
  function replaceEndOfLine(doc, replacement = literalline) {
31180
- return mapDoc(doc, (currentDoc) => typeof currentDoc === "string" ? join27(replacement, currentDoc.split(`
31180
+ return mapDoc(doc, (currentDoc) => typeof currentDoc === "string" ? join29(replacement, currentDoc.split(`
31181
31181
  `)) : currentDoc);
31182
31182
  }
31183
31183
  function canBreakFn(doc) {
@@ -31257,7 +31257,7 @@ function indentIfBreak(contents, options) {
31257
31257
  negate: options.negate
31258
31258
  };
31259
31259
  }
31260
- function join27(separator, docs) {
31260
+ function join29(separator, docs) {
31261
31261
  assertDoc(separator);
31262
31262
  assertDocArray(docs);
31263
31263
  const parts = [];
@@ -31968,7 +31968,7 @@ var init_doc = __esm(() => {
31968
31968
  MODE_FLAT = Symbol("MODE_FLAT");
31969
31969
  DOC_FILL_PRINTED_LENGTH = Symbol("DOC_FILL_PRINTED_LENGTH");
31970
31970
  builders = {
31971
- join: join27,
31971
+ join: join29,
31972
31972
  line,
31973
31973
  softline,
31974
31974
  hardline,
@@ -228779,7 +228779,7 @@ function normalizeBase44Env() {
228779
228779
  loadProjectEnvFiles();
228780
228780
 
228781
228781
  // src/cli/index.ts
228782
- import { dirname as dirname26, join as join36 } from "node:path";
228782
+ import { dirname as dirname27, join as join38 } from "node:path";
228783
228783
  import { fileURLToPath as fileURLToPath6 } from "node:url";
228784
228784
 
228785
228785
  // ../../node_modules/@clack/core/dist/index.mjs
@@ -239812,7 +239812,10 @@ async function uploadPresignedAsset(upload, assets) {
239812
239812
  try {
239813
239813
  await distribution_default.put(upload.url, {
239814
239814
  body: new Uint8Array(content),
239815
- headers: { "Content-Type": upload.contentType },
239815
+ headers: {
239816
+ "Content-Type": upload.contentType,
239817
+ ...upload.checksumSha256 ? { "x-amz-checksum-sha256": upload.checksumSha256 } : {}
239818
+ },
239816
239819
  timeout: 120000,
239817
239820
  retry: UPLOAD_RETRY
239818
239821
  });
@@ -246483,6 +246486,13 @@ async function resolveGitHash(projectRoot, explicit) {
246483
246486
  }
246484
246487
  return hash;
246485
246488
  }
246489
+ async function resolveProvenanceCommit(projectRoot, explicit) {
246490
+ if (explicit) {
246491
+ return await resolveGitHash(projectRoot, explicit);
246492
+ }
246493
+ const hash = await gitHead(projectRoot);
246494
+ return hash && isGitCommitHash(hash) ? hash : undefined;
246495
+ }
246486
246496
  async function gitHead(projectRoot) {
246487
246497
  try {
246488
246498
  const { stdout } = await execa("git", ["rev-parse", "HEAD"], {
@@ -246992,6 +247002,136 @@ async function resolveBranchName(name) {
246992
247002
  return matches[0].id;
246993
247003
  }
246994
247004
 
247005
+ // src/core/version/publish.ts
247006
+ import { randomUUID as randomUUID4 } from "node:crypto";
247007
+
247008
+ // src/core/version/schema.ts
247009
+ var DeclareVersionResponseSchema = object({
247010
+ session_id: string2(),
247011
+ uploads: array(object({
247012
+ path: string2(),
247013
+ url: string2(),
247014
+ content_type: string2(),
247015
+ content_length: number2(),
247016
+ checksum_sha256: string2()
247017
+ }))
247018
+ }).transform((data) => ({
247019
+ sessionId: data.session_id,
247020
+ uploads: data.uploads.map((upload) => ({
247021
+ path: upload.path,
247022
+ url: upload.url,
247023
+ contentType: upload.content_type,
247024
+ contentLength: upload.content_length,
247025
+ checksumSha256: upload.checksum_sha256
247026
+ }))
247027
+ }));
247028
+ var CreateVersionResponseSchema = object({
247029
+ version_id: string2(),
247030
+ manifest_hash: string2(),
247031
+ deduplicated: boolean2()
247032
+ }).transform((data) => ({
247033
+ versionId: data.version_id,
247034
+ manifestHash: data.manifest_hash,
247035
+ deduplicated: data.deduplicated
247036
+ }));
247037
+ var DeployVersionResponseSchema = object({
247038
+ deployment_id: string2(),
247039
+ manifest_hash: string2(),
247040
+ revision: number2()
247041
+ }).transform((data) => ({
247042
+ deploymentId: data.deployment_id,
247043
+ manifestHash: data.manifest_hash,
247044
+ revision: data.revision
247045
+ }));
247046
+
247047
+ // src/core/version/api.ts
247048
+ var DEFAULT_VERSION_UPLOAD_CONCURRENCY = 8;
247049
+ var MAX_VERSION_UPLOAD_CONCURRENCY = 16;
247050
+ async function post(path, json, doing) {
247051
+ try {
247052
+ return await getAppClient().post(path, { json, timeout: 180000 });
247053
+ } catch (error) {
247054
+ throw await ApiError.fromHttpError(error, doing);
247055
+ }
247056
+ }
247057
+ function parse10(schema, body, what) {
247058
+ const result = schema.safeParse(body);
247059
+ if (!result.success) {
247060
+ throw new SchemaValidationError(`Invalid ${what} response from server`, result.error);
247061
+ }
247062
+ return result.data;
247063
+ }
247064
+ async function createVersion(artifacts, options = {}) {
247065
+ const declared = parse10(DeclareVersionResponseSchema, await (await post("versions", {
247066
+ static_bundle: artifacts.files.map(({ path, size, digest }) => ({
247067
+ path,
247068
+ size,
247069
+ digest
247070
+ })),
247071
+ entities: artifacts.entities,
247072
+ agents: artifacts.agents,
247073
+ source_commit: options.sourceCommit,
247074
+ frontend_commit: options.sourceCommit
247075
+ }, "declaring a version")).json(), "declare");
247076
+ options.progress?.onDeclared?.({
247077
+ fileCount: artifacts.files.length,
247078
+ owedFiles: declared.uploads.length
247079
+ });
247080
+ await uploadPresignedAssets(declared.uploads, {
247081
+ manifest: Object.fromEntries(artifacts.files.map((file) => [
247082
+ file.path,
247083
+ { hash: file.digest, size: file.size }
247084
+ ])),
247085
+ filesByHash: new Map(artifacts.files.map((file) => [
247086
+ file.digest,
247087
+ {
247088
+ absolutePath: file.absolutePath,
247089
+ hash: file.digest,
247090
+ size: file.size,
247091
+ contentType: "application/octet-stream"
247092
+ }
247093
+ ]))
247094
+ }, {
247095
+ concurrency: options.concurrency ?? DEFAULT_VERSION_UPLOAD_CONCURRENCY,
247096
+ onProgress: options.progress?.onUpload
247097
+ });
247098
+ return parse10(CreateVersionResponseSchema, await (await post(`versions/${encodeURIComponent(declared.sessionId)}/finalize`, {}, "creating a version")).json(), "create version");
247099
+ }
247100
+ async function deployVersion(versionId, options = {}) {
247101
+ return parse10(DeployVersionResponseSchema, await (await post(`versions/${encodeURIComponent(versionId)}/deployments`, {
247102
+ ...options.target ? { target: options.target } : {},
247103
+ ...options.idempotencyKey ? { idempotency_key: options.idempotencyKey } : {}
247104
+ }, "deploying a version")).json(), "deploy");
247105
+ }
247106
+
247107
+ // src/core/version/publish.ts
247108
+ var STEP = Symbol.for("base44.publishStep");
247109
+ async function tagStep(step, run) {
247110
+ try {
247111
+ return await run();
247112
+ } catch (error) {
247113
+ if (error !== null && typeof error === "object" && !(STEP in error)) {
247114
+ Object.defineProperty(error, STEP, { value: step, enumerable: false });
247115
+ }
247116
+ throw error;
247117
+ }
247118
+ }
247119
+ function stepOf(error) {
247120
+ return error !== null && typeof error === "object" && STEP in error ? error[STEP] : undefined;
247121
+ }
247122
+ async function publishVersion(artifacts, options = {}) {
247123
+ const version = await tagStep("create_version", () => createVersion(artifacts, {
247124
+ sourceCommit: options.sourceCommit,
247125
+ concurrency: options.concurrency,
247126
+ progress: options.progress
247127
+ }));
247128
+ const deployment = await tagStep("deploy", () => deployVersion(version.versionId, {
247129
+ target: options.target,
247130
+ idempotencyKey: randomUUID4()
247131
+ }));
247132
+ return { ...version, ...deployment };
247133
+ }
247134
+
246995
247135
  // src/cli/utils/command/Base44Command.ts
246996
247136
  function writeJsonSuccess(result) {
246997
247137
  if (result.stdout) {
@@ -247009,6 +247149,10 @@ function writeJsonError(error) {
247009
247149
  const envelope = {
247010
247150
  error: error instanceof Error ? error.message : String(error)
247011
247151
  };
247152
+ const step = stepOf(error);
247153
+ if (step !== undefined) {
247154
+ envelope.step = step;
247155
+ }
247012
247156
  if (isCLIError(error)) {
247013
247157
  envelope.code = error.code;
247014
247158
  if (error.details.length > 0) {
@@ -249260,33 +249404,154 @@ async function maybeAskToBuild(isNonInteractive, buildCommand) {
249260
249404
  });
249261
249405
  return !Ct(answer) && answer;
249262
249406
  }
249263
-
249407
+ // src/core/version/artifacts.ts
249408
+ import { createHash as createHash2 } from "node:crypto";
249409
+ import { createReadStream as createReadStream3 } from "node:fs";
249410
+ import { stat as stat3 } from "node:fs/promises";
249411
+ import { basename as basename5, join as join26 } from "node:path";
249412
+ var MAX_FILE_COUNT = 1e5;
249413
+ var ASSETS_IGNORE_FILE2 = ".assetsignore";
249414
+ var ALWAYS_IGNORED2 = new Set([
249415
+ ASSETS_IGNORE_FILE2,
249416
+ "wrangler.json",
249417
+ ".dev.vars"
249418
+ ]);
249419
+ var ENTRY2 = "index.html";
249420
+ async function digestFile(absolutePath) {
249421
+ const hash = createHash2("sha256");
249422
+ for await (const chunk of createReadStream3(absolutePath)) {
249423
+ hash.update(chunk);
249424
+ }
249425
+ return `sha256:${hash.digest("hex")}`;
249426
+ }
249427
+ async function collectBuildOutput(outputDir) {
249428
+ const found = await globby("**/*", {
249429
+ cwd: outputDir,
249430
+ dot: true,
249431
+ onlyFiles: true,
249432
+ followSymbolicLinks: false,
249433
+ ignoreFiles: [ASSETS_IGNORE_FILE2]
249434
+ });
249435
+ const relativePaths = found.filter((path) => !ALWAYS_IGNORED2.has(basename5(path))).sort();
249436
+ if (relativePaths.length === 0) {
249437
+ throw new InvalidInputError(`No files found in ${outputDir}. Build the site before creating a version.`, {
249438
+ hints: [
249439
+ { message: "Run 'base44 build' first", command: "base44 build" }
249440
+ ]
249441
+ });
249442
+ }
249443
+ if (relativePaths.length > MAX_FILE_COUNT) {
249444
+ throw new InvalidInputError(`Too many files: found ${relativePaths.length}, the limit is ${MAX_FILE_COUNT}.`);
249445
+ }
249446
+ if (!relativePaths.includes(ENTRY2)) {
249447
+ throw new InvalidInputError(`${outputDir} has no ${ENTRY2}, so nothing could enter the site.`);
249448
+ }
249449
+ return await Promise.all(relativePaths.map(async (path) => {
249450
+ const absolutePath = join26(outputDir, ...path.split("/"));
249451
+ const { size } = await stat3(absolutePath);
249452
+ return {
249453
+ path,
249454
+ absolutePath,
249455
+ size,
249456
+ digest: await digestFile(absolutePath)
249457
+ };
249458
+ }));
249459
+ }
249460
+ async function readRawResources(dir) {
249461
+ if (!await pathExists(dir)) {
249462
+ return {};
249463
+ }
249464
+ const files = await globby(`**/*.${CONFIG_FILE_EXTENSION_GLOB}`, {
249465
+ cwd: dir,
249466
+ onlyFiles: true,
249467
+ followSymbolicLinks: false
249468
+ });
249469
+ const payloads = {};
249470
+ for (const relativePath of files.sort()) {
249471
+ const name = relativePath.replace(/\.jsonc?$/, "");
249472
+ payloads[name] = await readJsonFile(join26(dir, ...relativePath.split("/")));
249473
+ }
249474
+ return payloads;
249475
+ }
249476
+ async function collectResources(configDir, dirs) {
249477
+ const [entities, agents] = await Promise.all([
249478
+ readRawResources(join26(configDir, dirs.entitiesDir)),
249479
+ readRawResources(join26(configDir, dirs.agentsDir))
249480
+ ]);
249481
+ return { entities, agents };
249482
+ }
249483
+ // src/core/version/gate.ts
249484
+ var VERSIONS_API_ENV = "BASE44_VERSIONS_API";
249485
+ function versionsApiEnabled(env = process.env) {
249486
+ const value = env[VERSIONS_API_ENV];
249487
+ return value === "1" || value === "true";
249488
+ }
249489
+ // src/core/version/project.ts
249490
+ import { dirname as dirname19, join as join27, resolve as resolve10 } from "node:path";
249491
+ var DEFAULT_BUILD_COMMAND = "npm run build";
249492
+ var DEFAULT_OUTPUT_DIRECTORY = "dist";
249493
+ async function resolvePublishTarget(projectRoot, overrides = {}) {
249494
+ const project = await readSettingsIfPresent(projectRoot);
249495
+ const root = project?.root ?? projectRoot ?? process.cwd();
249496
+ return {
249497
+ root,
249498
+ configDir: project ? dirname19(project.configPath) : join27(root, PROJECT_SUBDIR),
249499
+ buildCommand: project ? project.site?.buildCommand : DEFAULT_BUILD_COMMAND,
249500
+ outputDir: outputDirectory(project, root, overrides.outputDir),
249501
+ entitiesDir: project?.entitiesDir ?? "entities",
249502
+ agentsDir: project?.agentsDir ?? "agents"
249503
+ };
249504
+ }
249505
+ function outputDirectory(project, root, override) {
249506
+ const configured = override ?? (project ? project.site?.outputDirectory : DEFAULT_OUTPUT_DIRECTORY);
249507
+ return configured ? resolve10(root, configured) : null;
249508
+ }
249509
+ function requireOutputDir(target) {
249510
+ if (target.outputDir === null) {
249511
+ throw new ConfigNotFoundError("No site configuration found.", {
249512
+ hints: [
249513
+ {
249514
+ message: `Add 'site.outputDirectory' to your config.jsonc (e.g., "site": { "outputDirectory": "dist" })`
249515
+ },
249516
+ { message: `Or pass --output-dir <dir>, relative to ${target.root}` }
249517
+ ]
249518
+ });
249519
+ }
249520
+ return target.outputDir;
249521
+ }
249522
+ async function readSettingsIfPresent(projectRoot) {
249523
+ try {
249524
+ return await readProjectSettings(projectRoot);
249525
+ } catch (error) {
249526
+ if (error instanceof ConfigNotFoundError) {
249527
+ return null;
249528
+ }
249529
+ throw error;
249530
+ }
249531
+ }
249264
249532
  // src/cli/commands/project/build.ts
249265
249533
  async function buildAction(ctx) {
249266
249534
  const { app } = ctx;
249267
- if (!app?.projectRoot) {
249268
- throw new ConfigInvalidError("base44 build requires a linked local project. Run it from a project with base44/.app.jsonc.");
249269
- }
249270
- const { project } = await readProjectConfig(app.projectRoot);
249535
+ const target = await resolvePublishTarget(app?.projectRoot);
249271
249536
  await runSiteBuild(ctx, {
249272
- root: project.root,
249273
- buildCommand: project.site?.buildCommand,
249274
- appId: app.id
249537
+ root: target.root,
249538
+ buildCommand: target.buildCommand,
249539
+ appId: app?.id ?? ""
249275
249540
  });
249276
249541
  return {
249277
- outroMessage: `Site built with app id ${theme.styles.bold(app.id)}`
249542
+ outroMessage: `Site built with app id ${theme.styles.bold(app?.id ?? "")}`
249278
249543
  };
249279
249544
  }
249280
249545
  function getBuildCommand() {
249281
- return new Base44Command("build").description("Build the site with the Base44 app id injected").action(buildAction);
249546
+ return new Base44Command("build", { requireAuth: false }).description("Build the site with the Base44 app id injected").action(buildAction);
249282
249547
  }
249283
249548
 
249284
249549
  // src/cli/commands/project/create.ts
249285
- import { basename as basename5, resolve as resolve10 } from "node:path";
249550
+ import { basename as basename6, resolve as resolve11 } from "node:path";
249286
249551
  var import_kebabCase = __toESM(require_kebabCase(), 1);
249287
249552
 
249288
249553
  // src/cli/commands/project/scaffold-shared.ts
249289
- import { join as join26 } from "node:path";
249554
+ import { join as join28 } from "node:path";
249290
249555
  var DEFAULT_TEMPLATE_ID = "backend-only";
249291
249556
  async function getTemplateById(templateId) {
249292
249557
  const templates = await listTemplates();
@@ -249349,7 +249614,7 @@ async function completeProjectSetup({
249349
249614
  env: { VITE_BASE44_APP_ID: projectId }
249350
249615
  })`${buildCommand}`;
249351
249616
  updateMessage("Deploying site...");
249352
- return await deploySite(join26(resolvedPath, outputDirectory));
249617
+ return await deploySite(join28(resolvedPath, outputDirectory));
249353
249618
  }, {
249354
249619
  successMessage: theme.colors.base44Orange("Site deployed successfully"),
249355
249620
  errorMessage: "Failed to deploy site"
@@ -249447,8 +249712,8 @@ async function createInteractive(options, ctx) {
249447
249712
  name: () => {
249448
249713
  return options.name ? Promise.resolve(options.name) : Ze({
249449
249714
  message: "What is the name of your project?",
249450
- placeholder: basename5(process.cwd()),
249451
- initialValue: basename5(process.cwd()),
249715
+ placeholder: basename6(process.cwd()),
249716
+ initialValue: basename6(process.cwd()),
249452
249717
  validate: (value) => {
249453
249718
  if (!value || value.trim().length === 0) {
249454
249719
  return "Every project deserves a name";
@@ -249478,7 +249743,7 @@ async function createInteractive(options, ctx) {
249478
249743
  }, ctx);
249479
249744
  }
249480
249745
  async function createNonInteractive(options, ctx) {
249481
- ctx.log.info(`Creating a new project at ${resolve10(options.path)}`);
249746
+ ctx.log.info(`Creating a new project at ${resolve11(options.path)}`);
249482
249747
  const template = await getTemplateById(options.template ?? DEFAULT_TEMPLATE_ID);
249483
249748
  return await executeCreate({
249484
249749
  template,
@@ -249502,7 +249767,7 @@ async function executeCreate({
249502
249767
  }, ctx) {
249503
249768
  const { log, runTask } = ctx;
249504
249769
  const name = rawName.trim();
249505
- const resolvedPath = resolve10(projectPath);
249770
+ const resolvedPath = resolve11(projectPath);
249506
249771
  const organizationId = await resolveWorkspaceId(ctx, flagWorkspaceId, isInteractive);
249507
249772
  const { projectId } = await runTask("Setting up your project...", async () => {
249508
249773
  return await createProjectFiles({
@@ -250117,11 +250382,11 @@ async function logsAction(ctx, options) {
250117
250382
  function getLogsCommand() {
250118
250383
  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 deployed 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 Option2("--level <level>", "Filter by log level").choices([
250119
250384
  ...LogLevelSchema.options
250120
- ])).option("-n, --limit <n>", "Results per page (1-1000; the server returns at most 500)").option("-f, --follow", "Stream new logs as they arrive").addOption(new Option2("--order <order>", "Sort order").choices(["asc", "desc"])).addOption(new Option2("--env <env>", "Which deployment to read logs from: preview (current draft) or prod (published). Omit to read both.").choices([...LogEnvSchema.options])).action(logsAction);
250385
+ ])).option("-n, --limit <n>", "Results per page (1-1000; the server returns at most 500)").option("-f, --follow", "Stream new logs as they arrive").addOption(new Option2("--order <order>", "Sort order").choices(["asc", "desc"])).addOption(new Option2("--env <env>", "Which deployment to read logs from: preview (current draft) or prod (published). Default: preview").choices([...LogEnvSchema.options])).action(logsAction);
250121
250386
  }
250122
250387
 
250123
250388
  // src/cli/commands/project/scaffold.ts
250124
- import { basename as basename6, resolve as resolve11 } from "node:path";
250389
+ import { basename as basename7, resolve as resolve12 } from "node:path";
250125
250390
  function resolveAppId(options) {
250126
250391
  const appId = options.appId;
250127
250392
  if (!appId) {
@@ -250137,8 +250402,8 @@ function resolveAppId(options) {
250137
250402
  async function scaffoldAction(ctx, name, options, command) {
250138
250403
  const { log, runTask } = ctx;
250139
250404
  const appId = resolveAppId(command.optsWithGlobals());
250140
- const resolvedPath = resolve11("./");
250141
- const projectName = (name ?? basename6(resolvedPath)).trim();
250405
+ const resolvedPath = resolve12("./");
250406
+ const projectName = (name ?? basename7(resolvedPath)).trim();
250142
250407
  const template = await getTemplateById("backend-only");
250143
250408
  log.info(`Scaffolding project at ${resolvedPath}`);
250144
250409
  const { projectId } = await runTask("Setting up your project...", async () => {
@@ -250185,6 +250450,62 @@ function getVisibilityCommand() {
250185
250450
  ])).action(setVisibility);
250186
250451
  }
250187
250452
 
250453
+ // src/cli/commands/publish.ts
250454
+ async function publishAction(ctx, options) {
250455
+ const { runTask, log, jsonMode, app } = ctx;
250456
+ const target = await resolvePublishTarget(app?.projectRoot, {
250457
+ outputDir: options.outputDir
250458
+ });
250459
+ if (options.build !== false) {
250460
+ await tagStep("build", () => runSiteBuild(ctx, {
250461
+ root: target.root,
250462
+ buildCommand: target.buildCommand,
250463
+ appId: app?.id ?? ""
250464
+ }));
250465
+ }
250466
+ const outputDir = requireOutputDir(target);
250467
+ const gitHash = await resolveProvenanceCommit(target.root, options.gitHash);
250468
+ const result = await runTask("Publishing...", async (updateMessage) => {
250469
+ const artifacts = {
250470
+ files: await collectBuildOutput(outputDir),
250471
+ ...await collectResources(target.configDir, target)
250472
+ };
250473
+ return await publishVersion(artifacts, {
250474
+ sourceCommit: gitHash,
250475
+ target: options.target,
250476
+ concurrency: options.concurrency,
250477
+ progress: {
250478
+ onDeclared: ({ fileCount, owedFiles }) => updateMessage(`Uploading ${owedFiles} of ${fileCount} files`),
250479
+ onUpload: ({ uploadedFiles, totalFiles }) => updateMessage(`Uploaded ${uploadedFiles} of ${totalFiles} files`)
250480
+ }
250481
+ });
250482
+ }, { successMessage: "Published", errorMessage: "Publish failed" });
250483
+ if (!jsonMode) {
250484
+ log.message(theme.styles.dim(`version ${result.versionId}${result.deduplicated ? " (existing content)" : ""}`));
250485
+ }
250486
+ return {
250487
+ outroMessage: `Deployment ${result.deploymentId} at revision ${result.revision}`,
250488
+ stdout: jsonMode ? `${JSON.stringify(result, null, 2)}
250489
+ ` : undefined
250490
+ };
250491
+ }
250492
+ function getPublishCommand() {
250493
+ return new Base44Command("publish").description("Build the app, record it as a version, and serve that version").option("--no-build", "Publish the existing build output without rebuilding").option("--output-dir <dir>", "Build output directory (defaults to the project's, else dist)").option("--target <name>", "Environment to serve the version at").addOption(new Option2("--git-hash <hash>", "Commit the build came from (defaults to the checkout's HEAD)").argParser(parseGitHash)).addOption(new Option2("--concurrency <n>", "Parallel file uploads").default(DEFAULT_VERSION_UPLOAD_CONCURRENCY).argParser(parseConcurrency)).action(publishAction);
250494
+ }
250495
+ function parseGitHash(value) {
250496
+ if (!isGitCommitHash(value)) {
250497
+ throw new InvalidArgumentError2("Expected a git commit hash (7-64 hex chars).");
250498
+ }
250499
+ return value;
250500
+ }
250501
+ function parseConcurrency(value) {
250502
+ const parsed = Number(value);
250503
+ if (!Number.isInteger(parsed) || parsed < 1 || parsed > MAX_VERSION_UPLOAD_CONCURRENCY) {
250504
+ throw new InvalidArgumentError2(`Expected a whole number between 1 and ${MAX_VERSION_UPLOAD_CONCURRENCY}.`);
250505
+ }
250506
+ return parsed;
250507
+ }
250508
+
250188
250509
  // src/core/resources/sandbox/schema.ts
250189
250510
  var FileErrorSchema = object({
250190
250511
  code: string2(),
@@ -250554,7 +250875,7 @@ function getSecretsListCommand() {
250554
250875
  }
250555
250876
 
250556
250877
  // src/cli/commands/secrets/set.ts
250557
- import { resolve as resolve12 } from "node:path";
250878
+ import { resolve as resolve13 } from "node:path";
250558
250879
  function parseEntries(entries) {
250559
250880
  const secrets = {};
250560
250881
  for (const entry of entries) {
@@ -250585,7 +250906,7 @@ async function setSecretsAction({ log, runTask }, entries, options) {
250585
250906
  validateInput(entries, options);
250586
250907
  let secrets;
250587
250908
  if (options.envFile) {
250588
- secrets = await parseEnvFile(resolve12(options.envFile));
250909
+ secrets = await parseEnvFile(resolve13(options.envFile));
250589
250910
  if (Object.keys(secrets).length === 0) {
250590
250911
  throw new InvalidInputError("The env file contains no valid KEY=VALUE entries.");
250591
250912
  }
@@ -250614,7 +250935,7 @@ function getSecretsCommand() {
250614
250935
  }
250615
250936
 
250616
250937
  // src/cli/commands/site/deploy.ts
250617
- import { resolve as resolve13 } from "node:path";
250938
+ import { resolve as resolve14 } from "node:path";
250618
250939
  async function deployAction2(ctx, options) {
250619
250940
  const { isNonInteractive } = ctx;
250620
250941
  if (isNonInteractive && !options.yes) {
@@ -250692,23 +251013,23 @@ async function deployTarball({ runTask }, project) {
250692
251013
  }
250693
251014
  function siteOutputDir(project) {
250694
251015
  const outputDirectory = project.site?.outputDirectory;
250695
- return outputDirectory ? resolve13(project.root, outputDirectory) : null;
251016
+ return outputDirectory ? resolve14(project.root, outputDirectory) : null;
250696
251017
  }
250697
251018
  function getSiteDeployCommand() {
250698
251019
  const command = new Base44Command("deploy").description("Deploy built site files to Base44 hosting").option("-y, --yes", "Skip confirmation prompt").option("--build", "Build the site before deploying (skips the prompt)").option("--no-build", "Deploy without building (skips the prompt)");
250699
251020
  if (deploymentsApiEnabled()) {
250700
- command.addOption(new Option2("--git-hash <hash>", "Commit the build came from (defaults to the checkout's HEAD)").argParser(parseGitHash));
250701
- command.addOption(new Option2("--concurrency <n>", "Parallel asset uploads").default(DEFAULT_UPLOAD_CONCURRENCY).argParser(parseConcurrency));
251021
+ command.addOption(new Option2("--git-hash <hash>", "Commit the build came from (defaults to the checkout's HEAD)").argParser(parseGitHash2));
251022
+ command.addOption(new Option2("--concurrency <n>", "Parallel asset uploads").default(DEFAULT_UPLOAD_CONCURRENCY).argParser(parseConcurrency2));
250702
251023
  }
250703
251024
  return command.action(deployAction2);
250704
251025
  }
250705
- function parseGitHash(value) {
251026
+ function parseGitHash2(value) {
250706
251027
  if (!isGitCommitHash(value)) {
250707
251028
  throw new InvalidArgumentError2("Expected a git commit hash (7-64 hex chars).");
250708
251029
  }
250709
251030
  return value;
250710
251031
  }
250711
- function parseConcurrency(value) {
251032
+ function parseConcurrency2(value) {
250712
251033
  const parsed = Number(value);
250713
251034
  if (!Number.isInteger(parsed) || parsed < 1 || parsed > MAX_UPLOAD_CONCURRENCY) {
250714
251035
  throw new InvalidArgumentError2(`Expected a whole number between 1 and ${MAX_UPLOAD_CONCURRENCY}.`);
@@ -250820,10 +251141,10 @@ function toPascalCase(name) {
250820
251141
  return name.split(/[-_\s]+/).map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join("");
250821
251142
  }
250822
251143
  // src/core/types/update-project.ts
250823
- import { join as join29 } from "node:path";
251144
+ import { join as join31 } from "node:path";
250824
251145
  var TYPES_INCLUDE_PATH = `${PROJECT_SUBDIR}/${TYPES_OUTPUT_SUBDIR}/*.d.ts`;
250825
251146
  async function updateProjectConfig(projectRoot) {
250826
- const tsconfigPath = join29(projectRoot, "tsconfig.json");
251147
+ const tsconfigPath = join31(projectRoot, "tsconfig.json");
250827
251148
  if (!await pathExists(tsconfigPath)) {
250828
251149
  return false;
250829
251150
  }
@@ -250871,6 +251192,69 @@ function getTypesCommand() {
250871
251192
  return new Command2("types").description("Manage TypeScript type generation").addCommand(getTypesGenerateCommand());
250872
251193
  }
250873
251194
 
251195
+ // src/cli/commands/versions/create.ts
251196
+ async function createAction2({ runTask, jsonMode, app }, options) {
251197
+ const target = await resolvePublishTarget(app?.projectRoot, {
251198
+ outputDir: options.outputDir
251199
+ });
251200
+ const gitHash = await resolveProvenanceCommit(target.root, options.gitHash);
251201
+ const version = await runTask("Creating version...", async (updateMessage) => await createVersion({
251202
+ files: await collectBuildOutput(requireOutputDir(target)),
251203
+ ...await collectResources(target.configDir, target)
251204
+ }, {
251205
+ sourceCommit: gitHash,
251206
+ concurrency: options.concurrency,
251207
+ progress: {
251208
+ onDeclared: ({ fileCount, owedFiles }) => updateMessage(`Uploading ${owedFiles} of ${fileCount} files`),
251209
+ onUpload: ({ uploadedFiles, totalFiles }) => updateMessage(`Uploaded ${uploadedFiles} of ${totalFiles} files`)
251210
+ }
251211
+ }), {
251212
+ successMessage: "Version created",
251213
+ errorMessage: "Create version failed"
251214
+ });
251215
+ return {
251216
+ outroMessage: `Version ${version.versionId} (${version.manifestHash})`,
251217
+ stdout: jsonMode ? `${JSON.stringify(version, null, 2)}
251218
+ ` : undefined
251219
+ };
251220
+ }
251221
+ function getVersionCreateCommand() {
251222
+ return new Base44Command("create").description("Record the built output as a version, without deploying it").option("--output-dir <dir>", "Build output directory (defaults to the project's, else dist)").addOption(new Option2("--git-hash <hash>", "Commit the build came from (defaults to the checkout's HEAD)").argParser((value) => {
251223
+ if (!isGitCommitHash(value)) {
251224
+ throw new InvalidArgumentError2("Expected a git commit hash (7-64 hex chars).");
251225
+ }
251226
+ return value;
251227
+ })).addOption(new Option2("--concurrency <n>", "Parallel file uploads").default(DEFAULT_VERSION_UPLOAD_CONCURRENCY).argParser((value) => {
251228
+ const parsed = Number(value);
251229
+ if (!Number.isInteger(parsed) || parsed < 1 || parsed > MAX_VERSION_UPLOAD_CONCURRENCY) {
251230
+ throw new InvalidArgumentError2(`Expected a whole number between 1 and ${MAX_VERSION_UPLOAD_CONCURRENCY}.`);
251231
+ }
251232
+ return parsed;
251233
+ })).action(createAction2);
251234
+ }
251235
+
251236
+ // src/cli/commands/versions/deploy.ts
251237
+ import { randomUUID as randomUUID5 } from "node:crypto";
251238
+ async function deployAction3({ runTask, jsonMode }, versionId, options) {
251239
+ const deployment = await runTask(`Deploying version ${versionId}...`, async () => await deployVersion(versionId, {
251240
+ target: options.target,
251241
+ idempotencyKey: randomUUID5()
251242
+ }), { successMessage: "Version deployed", errorMessage: "Deploy failed" });
251243
+ return {
251244
+ outroMessage: `Deployment ${deployment.deploymentId} at revision ${deployment.revision}`,
251245
+ stdout: jsonMode ? `${JSON.stringify(deployment, null, 2)}
251246
+ ` : undefined
251247
+ };
251248
+ }
251249
+ function getVersionDeployCommand() {
251250
+ return new Base44Command("deploy").description("Serve an already-recorded version (also how a rollback is done)").argument("<version-id>", "The version to serve").option("--target <name>", "Environment to serve the version at").action(deployAction3);
251251
+ }
251252
+
251253
+ // src/cli/commands/versions/index.ts
251254
+ function getVersionsCommand() {
251255
+ return new Command2("versions").description("Record app versions and serve them").addCommand(getVersionCreateCommand()).addCommand(getVersionDeployCommand());
251256
+ }
251257
+
250874
251258
  // src/core/resources/workflow/schema.ts
250875
251259
  var WorkflowRunStatusSchema = _enum([
250876
251260
  "running",
@@ -251287,7 +251671,7 @@ function createDevLogger(label, labelColor = theme.styles.dim) {
251287
251671
  // src/cli/dev/dev-server/main.ts
251288
251672
  var import_cors = __toESM(require_lib4(), 1);
251289
251673
  var import_express6 = __toESM(require_express(), 1);
251290
- import { dirname as dirname24, join as join35 } from "node:path";
251674
+ import { dirname as dirname25, join as join37 } from "node:path";
251291
251675
 
251292
251676
  // ../../node_modules/get-port/index.js
251293
251677
  import net from "node:net";
@@ -251417,7 +251801,7 @@ var $tmpName = promisify11(tmp.tmpName);
251417
251801
 
251418
251802
  // src/cli/dev/dev-server/function-manager.ts
251419
251803
  import { spawn as spawn2 } from "node:child_process";
251420
- import { dirname as dirname21, join as join30 } from "node:path";
251804
+ import { dirname as dirname22, join as join33 } from "node:path";
251421
251805
  import { pathToFileURL } from "node:url";
251422
251806
 
251423
251807
  // src/cli/dev/dev-server/base-function-manager.ts
@@ -251522,7 +251906,7 @@ class FunctionManager extends BaseFunctionManager {
251522
251906
  }
251523
251907
  spawnFunction(func, port) {
251524
251908
  this.logger.log(`Spawning function "${func.name}" on port ${port}`);
251525
- const importMapPath = join30(dirname21(this.wrapperPath), "import-map.json");
251909
+ const importMapPath = join33(dirname22(this.wrapperPath), "import-map.json");
251526
251910
  const process2 = spawn2("deno", ["run", "--allow-all", "--import-map", importMapPath, this.wrapperPath], {
251527
251911
  env: {
251528
251912
  ...globalThis.process.env,
@@ -251596,7 +251980,7 @@ class FunctionManager extends BaseFunctionManager {
251596
251980
  import { mkdirSync as mkdirSync2, writeFileSync as writeFileSync2 } from "node:fs";
251597
251981
  import { isBuiltin } from "node:module";
251598
251982
  import { homedir as homedir3 } from "node:os";
251599
- import { join as join31 } from "node:path";
251983
+ import { join as join34 } from "node:path";
251600
251984
  import { pathToFileURL as pathToFileURL6 } from "node:url";
251601
251985
  var depsPromise;
251602
251986
  function loadDeps() {
@@ -251717,9 +252101,9 @@ export default {
251717
252101
  };
251718
252102
  `;
251719
252103
  function ensureBundlerConfig() {
251720
- const dir = join31(homedir3(), ".base44", "function-bundler");
252104
+ const dir = join34(homedir3(), ".base44", "function-bundler");
251721
252105
  mkdirSync2(dir, { recursive: true });
251722
- const configPath = join31(dir, "deno.json");
252106
+ const configPath = join34(dir, "deno.json");
251723
252107
  writeFileSync2(configPath, `${JSON.stringify({ nodeModulesDir: "auto" }, null, 2)}
251724
252108
  `);
251725
252109
  return configPath;
@@ -253100,11 +253484,11 @@ async function createEntityRoutes(db, logger, broadcast) {
253100
253484
  // src/cli/dev/dev-server/routes/integrations.ts
253101
253485
  var import_express5 = __toESM(require_express(), 1);
253102
253486
  var import_multer = __toESM(require_multer(), 1);
253103
- import { createHash as createHash2, randomUUID as randomUUID4 } from "node:crypto";
253487
+ import { createHash as createHash3, randomUUID as randomUUID6 } from "node:crypto";
253104
253488
  import fs28 from "node:fs";
253105
253489
  import path18 from "node:path";
253106
253490
  function createFileToken(fileUri) {
253107
- return createHash2("sha256").update(fileUri).digest("hex");
253491
+ return createHash3("sha256").update(fileUri).digest("hex");
253108
253492
  }
253109
253493
  function createIntegrationRoutes(mediaFilesDir, baseUrl, remoteProxy, logger) {
253110
253494
  const router = import_express5.Router({ mergeParams: true });
@@ -253117,14 +253501,14 @@ function createIntegrationRoutes(mediaFilesDir, baseUrl, remoteProxy, logger) {
253117
253501
  destination: mediaFilesDir,
253118
253502
  filename: (_req, file, cb) => {
253119
253503
  const ext = path18.extname(file.originalname);
253120
- cb(null, `${randomUUID4()}${ext}`);
253504
+ cb(null, `${randomUUID6()}${ext}`);
253121
253505
  }
253122
253506
  });
253123
253507
  const privateStorage = import_multer.default.diskStorage({
253124
253508
  destination: privateFilesDir,
253125
253509
  filename: (_req, file, cb) => {
253126
253510
  const ext = path18.extname(file.originalname);
253127
- cb(null, `${randomUUID4()}${ext}`);
253511
+ cb(null, `${randomUUID6()}${ext}`);
253128
253512
  }
253129
253513
  });
253130
253514
  const upload = import_multer.default({ storage, limits: { fileSize: MAX_FILE_SIZE } });
@@ -253194,11 +253578,11 @@ import { relative as relative7 } from "node:path";
253194
253578
  // ../../node_modules/chokidar/index.js
253195
253579
  import { EventEmitter as EventEmitter3 } from "node:events";
253196
253580
  import { stat as statcb, Stats } from "node:fs";
253197
- import { readdir as readdir3, stat as stat6 } from "node:fs/promises";
253581
+ import { readdir as readdir3, stat as stat7 } from "node:fs/promises";
253198
253582
  import * as sp3 from "node:path";
253199
253583
 
253200
253584
  // ../../node_modules/readdirp/index.js
253201
- import { lstat as lstat2, readdir as readdir2, realpath, stat as stat4 } from "node:fs/promises";
253585
+ import { lstat as lstat2, readdir as readdir2, realpath, stat as stat5 } from "node:fs/promises";
253202
253586
  import { join as pjoin, relative as prelative, resolve as presolve, sep as psep } from "node:path";
253203
253587
  import { Readable as Readable6 } from "node:stream";
253204
253588
  var EntryTypes = {
@@ -253280,7 +253664,7 @@ class ReaddirpStream extends Readable6 {
253280
253664
  const { root, type } = opts;
253281
253665
  this._fileFilter = normalizeFilter(opts.fileFilter);
253282
253666
  this._directoryFilter = normalizeFilter(opts.directoryFilter);
253283
- const statMethod = opts.lstat ? lstat2 : stat4;
253667
+ const statMethod = opts.lstat ? lstat2 : stat5;
253284
253668
  if (wantBigintFsStats) {
253285
253669
  this._stat = (path) => statMethod(path, { bigint: true });
253286
253670
  } else {
@@ -253433,7 +253817,7 @@ function readdirp(root, options = {}) {
253433
253817
 
253434
253818
  // ../../node_modules/chokidar/handler.js
253435
253819
  import { watch as fs_watch, unwatchFile, watchFile } from "node:fs";
253436
- import { realpath as fsrealpath, lstat as lstat3, open as open2, stat as stat5 } from "node:fs/promises";
253820
+ import { realpath as fsrealpath, lstat as lstat3, open as open2, stat as stat6 } from "node:fs/promises";
253437
253821
  import { type as osType } from "node:os";
253438
253822
  import * as sp2 from "node:path";
253439
253823
  var STR_DATA = "data";
@@ -253459,7 +253843,7 @@ var EVENTS = {
253459
253843
  };
253460
253844
  var EV = EVENTS;
253461
253845
  var THROTTLE_MODE_WATCH = "watch";
253462
- var statMethods = { lstat: lstat3, stat: stat5 };
253846
+ var statMethods = { lstat: lstat3, stat: stat6 };
253463
253847
  var KEY_LISTENERS = "listeners";
253464
253848
  var KEY_ERR = "errHandlers";
253465
253849
  var KEY_RAW = "rawEmitters";
@@ -253930,7 +254314,7 @@ class NodeFsHandler {
253930
254314
  return;
253931
254315
  if (!newStats || newStats.mtimeMs === 0) {
253932
254316
  try {
253933
- const newStats = await stat5(file);
254317
+ const newStats = await stat6(file);
253934
254318
  if (this.fsw.closed)
253935
254319
  return;
253936
254320
  const at = newStats.atimeMs;
@@ -254602,7 +254986,7 @@ class FSWatcher extends EventEmitter3 {
254602
254986
  const fullPath = opts.cwd ? sp3.join(opts.cwd, path) : path;
254603
254987
  let stats;
254604
254988
  try {
254605
- stats = await stat6(fullPath);
254989
+ stats = await stat7(fullPath);
254606
254990
  } catch (err) {}
254607
254991
  if (!stats || this.closed)
254608
254992
  return;
@@ -254981,8 +255365,8 @@ async function createDevServer(options) {
254981
255365
  broadcastEntityEvent(io, appId, entityName, event);
254982
255366
  };
254983
255367
  const base44ConfigWatcher = new WatchBase44({
254984
- functions: join35(dirname24(project.configPath), project.functionsDir),
254985
- entities: join35(dirname24(project.configPath), project.entitiesDir)
255368
+ functions: join37(dirname25(project.configPath), project.functionsDir),
255369
+ entities: join37(dirname25(project.configPath), project.entitiesDir)
254986
255370
  }, devLogger);
254987
255371
  base44ConfigWatcher.on("change", async (name) => {
254988
255372
  try {
@@ -255379,7 +255763,7 @@ Examples:
255379
255763
  }
255380
255764
 
255381
255765
  // src/cli/commands/project/eject.ts
255382
- import { resolve as resolve17 } from "node:path";
255766
+ import { resolve as resolve18 } from "node:path";
255383
255767
  var import_kebabCase2 = __toESM(require_kebabCase(), 1);
255384
255768
  async function eject(ctx, options, command) {
255385
255769
  const { log, runTask, isNonInteractive } = ctx;
@@ -255443,7 +255827,7 @@ async function eject(ctx, options, command) {
255443
255827
  Ne("Operation cancelled.");
255444
255828
  throw new CLIExitError(0);
255445
255829
  }
255446
- const resolvedPath = resolve17(selectedPath);
255830
+ const resolvedPath = resolve18(selectedPath);
255447
255831
  await runTask("Downloading your project's code...", async (updateMessage) => {
255448
255832
  await createProjectFilesForExistingProject({
255449
255833
  projectId,
@@ -255526,6 +255910,10 @@ function createProgram(context) {
255526
255910
  program.addCommand(getBranchesCommand());
255527
255911
  program.addCommand(getAuthCommand());
255528
255912
  program.addCommand(getSiteCommand());
255913
+ if (versionsApiEnabled()) {
255914
+ program.addCommand(getPublishCommand());
255915
+ program.addCommand(getVersionsCommand());
255916
+ }
255529
255917
  program.addCommand(getTypesCommand());
255530
255918
  program.addCommand(getExecCommand());
255531
255919
  program.addCommand(getDevCommand());
@@ -255538,7 +255926,7 @@ var import_detect_agent = __toESM(require_dist5(), 1);
255538
255926
  import { release, type } from "node:os";
255539
255927
 
255540
255928
  // ../../node_modules/posthog-node/dist/extensions/error-tracking/modifiers/module.node.mjs
255541
- import { dirname as dirname25, posix, sep as sep2 } from "path";
255929
+ import { dirname as dirname26, posix, sep as sep2 } from "path";
255542
255930
  function createModulerModifier() {
255543
255931
  const getModuleFromFileName = createGetModuleFromFilename();
255544
255932
  return async (frames) => {
@@ -255547,7 +255935,7 @@ function createModulerModifier() {
255547
255935
  return frames;
255548
255936
  };
255549
255937
  }
255550
- function createGetModuleFromFilename(basePath = process.argv[1] ? dirname25(process.argv[1]) : process.cwd(), isWindows = sep2 === "\\") {
255938
+ function createGetModuleFromFilename(basePath = process.argv[1] ? dirname26(process.argv[1]) : process.cwd(), isWindows = sep2 === "\\") {
255551
255939
  const normalizedBase = isWindows ? normalizeWindowsPath2(basePath) : basePath;
255552
255940
  return (filename) => {
255553
255941
  if (!filename)
@@ -257582,7 +257970,7 @@ class ReduceableCache {
257582
257970
  }
257583
257971
  }
257584
257972
  // ../../node_modules/posthog-node/dist/extensions/error-tracking/modifiers/context-lines.node.mjs
257585
- import { createReadStream as createReadStream3 } from "node:fs";
257973
+ import { createReadStream as createReadStream4 } from "node:fs";
257586
257974
  import { createInterface as createInterface2 } from "node:readline";
257587
257975
  var LRU_FILE_CONTENTS_CACHE = new ReduceableCache(25);
257588
257976
  var LRU_FILE_CONTENTS_FS_READ_FAILED = new ReduceableCache(20);
@@ -257626,7 +258014,7 @@ async function addSourceContext(frames) {
257626
258014
  }
257627
258015
  function getContextLinesFromFile(path, ranges, output) {
257628
258016
  return new Promise((resolve) => {
257629
- const stream = createReadStream3(path);
258017
+ const stream = createReadStream4(path);
257630
258018
  const lineReaded = createInterface2({
257631
258019
  input: stream
257632
258020
  });
@@ -259536,9 +259924,9 @@ function addCommandInfoToErrorReporter(program, errorReporter) {
259536
259924
  });
259537
259925
  }
259538
259926
  // src/cli/index.ts
259539
- var __dirname4 = dirname26(fileURLToPath6(import.meta.url));
259927
+ var __dirname4 = dirname27(fileURLToPath6(import.meta.url));
259540
259928
  async function runCLI(options) {
259541
- ensureNpmAssets(join36(__dirname4, "../assets"));
259929
+ ensureNpmAssets(join38(__dirname4, "../assets"));
259542
259930
  const errorReporter = new ErrorReporter;
259543
259931
  errorReporter.registerProcessErrorHandlers();
259544
259932
  const jsonMode = process.argv.includes("--json");
@@ -259577,4 +259965,4 @@ export {
259577
259965
  runCLI
259578
259966
  };
259579
259967
 
259580
- //# debugId=717552FD8532AC4A64756E2164756E21
259968
+ //# debugId=FEA39F6748E8A4DB64756E2164756E21