@base44-preview/cli 0.1.15-pr.623.9d0907a → 0.1.15-pr.626.115f2c6

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" ? join28(replacement, currentDoc.split(`
31180
+ return mapDoc(doc, (currentDoc) => typeof currentDoc === "string" ? join30(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 join28(separator, docs) {
31260
+ function join30(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: join28,
31971
+ join: join30,
31972
31972
  line,
31973
31973
  softline,
31974
31974
  hardline,
@@ -228783,7 +228783,7 @@ function normalizeBase44Env() {
228783
228783
  loadProjectEnvFiles();
228784
228784
 
228785
228785
  // src/cli/index.ts
228786
- import { dirname as dirname28, join as join37 } from "node:path";
228786
+ import { dirname as dirname29, join as join39 } from "node:path";
228787
228787
  import { fileURLToPath as fileURLToPath6 } from "node:url";
228788
228788
 
228789
228789
  // ../../node_modules/@clack/core/dist/index.mjs
@@ -239590,91 +239590,8 @@ async function createArchive(pathToArchive, targetArchivePath) {
239590
239590
  import { readFile as readFile3 } from "node:fs/promises";
239591
239591
  import { join as join18 } from "node:path";
239592
239592
 
239593
- // src/core/site/manifest.ts
239594
- import { createHash } from "node:crypto";
239595
- import { createReadStream } from "node:fs";
239596
- import { stat } from "node:fs/promises";
239597
- import { basename as basename5, extname, join as join16 } from "node:path";
239598
- var MAX_ASSET_COUNT = 1e5;
239599
- var ASSETS_IGNORE_FILE = ".assetsignore";
239600
- var ALWAYS_IGNORED = new Set([
239601
- ASSETS_IGNORE_FILE,
239602
- "wrangler.json",
239603
- ".dev.vars"
239604
- ]);
239605
- var MIME_TYPES = {
239606
- ".html": "text/html",
239607
- ".htm": "text/html",
239608
- ".css": "text/css",
239609
- ".js": "text/javascript",
239610
- ".mjs": "text/javascript",
239611
- ".json": "application/json",
239612
- ".map": "application/json",
239613
- ".txt": "text/plain",
239614
- ".xml": "application/xml",
239615
- ".svg": "image/svg+xml",
239616
- ".png": "image/png",
239617
- ".jpg": "image/jpeg",
239618
- ".jpeg": "image/jpeg",
239619
- ".gif": "image/gif",
239620
- ".webp": "image/webp",
239621
- ".avif": "image/avif",
239622
- ".ico": "image/x-icon",
239623
- ".woff": "font/woff",
239624
- ".woff2": "font/woff2",
239625
- ".ttf": "font/ttf",
239626
- ".otf": "font/otf",
239627
- ".eot": "application/vnd.ms-fontobject",
239628
- ".mp3": "audio/mpeg",
239629
- ".mp4": "video/mp4",
239630
- ".webm": "video/webm",
239631
- ".pdf": "application/pdf",
239632
- ".wasm": "application/wasm",
239633
- ".webmanifest": "application/manifest+json"
239634
- };
239635
- function getAssetContentType(filePath) {
239636
- return MIME_TYPES[extname(filePath).toLowerCase()] ?? "application/octet-stream";
239637
- }
239638
- async function hashAssetFile(appId, absolutePath) {
239639
- const hash = createHash("sha256").update(Buffer.from(appId, "utf8"));
239640
- for await (const chunk of createReadStream(absolutePath)) {
239641
- hash.update(chunk);
239642
- }
239643
- return hash.digest("hex").slice(0, 32);
239644
- }
239645
- async function buildAssetManifest(assetsDir, appId) {
239646
- const manifest = {};
239647
- const filesByHash = new Map;
239648
- const found = await globby("**/*", {
239649
- cwd: assetsDir,
239650
- dot: true,
239651
- onlyFiles: true,
239652
- followSymbolicLinks: false,
239653
- ignoreFiles: [ASSETS_IGNORE_FILE]
239654
- });
239655
- const relativeFilePaths = found.filter((path) => !ALWAYS_IGNORED.has(basename5(path)));
239656
- if (relativeFilePaths.length > MAX_ASSET_COUNT) {
239657
- throw new InvalidInputError(`Too many static assets: found ${relativeFilePaths.length}, the limit is ${MAX_ASSET_COUNT} files.`);
239658
- }
239659
- for (const relativePath of relativeFilePaths.sort()) {
239660
- const absolutePath = join16(assetsDir, ...relativePath.split("/"));
239661
- const { size } = await stat(absolutePath);
239662
- const hash = await hashAssetFile(appId, absolutePath);
239663
- manifest[`/${relativePath}`] = { hash, size };
239664
- if (!filesByHash.has(hash)) {
239665
- filesByHash.set(hash, {
239666
- absolutePath,
239667
- hash,
239668
- size,
239669
- contentType: getAssetContentType(absolutePath)
239670
- });
239671
- }
239672
- }
239673
- return { manifest, filesByHash };
239674
- }
239675
-
239676
239593
  // src/core/site/modules.ts
239677
- import { stat as stat2 } from "node:fs/promises";
239594
+ import { stat } from "node:fs/promises";
239678
239595
  import { relative as relative5, resolve as resolve3, sep } from "node:path";
239679
239596
  var MAX_TOTAL_MODULE_BYTES = 40 * 1024 * 1024;
239680
239597
  var MODULE_IGNORE = ["wrangler.json", ".dev.vars"];
@@ -239749,7 +239666,7 @@ async function collectModules(config) {
239749
239666
  const modules = [...modulesByName.values()];
239750
239667
  let totalBytes = 0;
239751
239668
  for (const module of modules) {
239752
- module.size = (await stat2(module.absolutePath)).size;
239669
+ module.size = (await stat(module.absolutePath)).size;
239753
239670
  totalBytes += module.size;
239754
239671
  }
239755
239672
  if (totalBytes > MAX_TOTAL_MODULE_BYTES) {
@@ -239768,8 +239685,104 @@ function addSourcemap(modulesByName, configDir, name) {
239768
239685
  });
239769
239686
  }
239770
239687
 
239771
- // src/core/site/upload.ts
239772
- import { readFile as readFile2 } from "node:fs/promises";
239688
+ // src/core/site/wrangler-config.ts
239689
+ import { dirname as dirname12, join as join16, resolve as resolve4 } from "node:path";
239690
+ var WRANGLER_REDIRECT_PATH = join16(".wrangler", "deploy", "config.json");
239691
+ var RedirectConfigSchema = looseObject({
239692
+ configPath: string2().min(1)
239693
+ });
239694
+ var WranglerConfigSchema = looseObject({
239695
+ main: string2().min(1, "wrangler config is missing a 'main' entry module"),
239696
+ no_bundle: boolean2().optional(),
239697
+ rules: array(looseObject({ type: string2(), globs: array(string2()) })).optional(),
239698
+ assets: looseObject({
239699
+ directory: string2().optional(),
239700
+ html_handling: string2().optional(),
239701
+ not_found_handling: string2().optional(),
239702
+ run_worker_first: union([boolean2(), array(string2())]).optional(),
239703
+ headers: string2().optional(),
239704
+ redirects: string2().optional()
239705
+ }).optional(),
239706
+ compatibility_date: string2().optional(),
239707
+ compatibility_flags: array(string2()).optional(),
239708
+ vars: record(string2(), unknown()).optional(),
239709
+ upload_source_maps: boolean2().optional()
239710
+ });
239711
+ async function detectFullStackArtifact(projectRoot) {
239712
+ const redirectPath = join16(projectRoot, WRANGLER_REDIRECT_PATH);
239713
+ return await pathExists(redirectPath) ? redirectPath : null;
239714
+ }
239715
+ async function resolveWranglerConfig(redirectPath) {
239716
+ const configPath = await resolveRedirectedConfigPath(redirectPath);
239717
+ const parsed = await readJsonFile(configPath);
239718
+ const result = WranglerConfigSchema.safeParse(parsed);
239719
+ if (!result.success) {
239720
+ throw new ConfigInvalidError(`Invalid wrangler config: ${prettifyError(result.error)}`, configPath);
239721
+ }
239722
+ const config = result.data;
239723
+ if (config.no_bundle !== true) {
239724
+ throw new InvalidInputError("This framework's output requires bundling; not yet supported. Base44 only deploys pre-bundled Workers output (no_bundle: true).");
239725
+ }
239726
+ const configDir = dirname12(configPath);
239727
+ const assetsDirectory = config.assets?.directory ? resolve4(configDir, config.assets.directory) : null;
239728
+ return {
239729
+ configPath,
239730
+ configDir,
239731
+ main: config.main,
239732
+ assetsDirectory,
239733
+ assetsConfig: config.assets ? toResolvedAssetsConfig(config.assets) : null,
239734
+ compatibilityDate: config.compatibility_date ?? null,
239735
+ compatibilityFlags: config.compatibility_flags ?? [],
239736
+ rules: (config.rules ?? []).map((rule) => ({
239737
+ type: rule.type,
239738
+ globs: rule.globs
239739
+ })),
239740
+ uploadSourceMaps: config.upload_source_maps ?? false
239741
+ };
239742
+ }
239743
+ async function resolveRedirectedConfigPath(redirectPath) {
239744
+ const parsed = await readJsonFile(redirectPath);
239745
+ const result = RedirectConfigSchema.safeParse(parsed);
239746
+ if (!result.success) {
239747
+ throw new ConfigInvalidError(`Invalid deploy redirect file: ${prettifyError(result.error)}`, redirectPath);
239748
+ }
239749
+ const configPath = resolve4(dirname12(redirectPath), result.data.configPath);
239750
+ if (!await pathExists(configPath)) {
239751
+ throw new ConfigInvalidError(`Wrangler config referenced by ${redirectPath} does not exist: ${configPath}`, redirectPath, {
239752
+ hints: [{ message: "Rebuild the project to regenerate the artifact" }]
239753
+ });
239754
+ }
239755
+ return configPath;
239756
+ }
239757
+ function toResolvedAssetsConfig(assets) {
239758
+ return {
239759
+ htmlHandling: assets.html_handling,
239760
+ notFoundHandling: assets.not_found_handling,
239761
+ runWorkerFirst: assets.run_worker_first,
239762
+ headers: assets.headers,
239763
+ redirects: assets.redirects
239764
+ };
239765
+ }
239766
+
239767
+ // src/core/site/full-stack.ts
239768
+ async function resolveFullStackBuild(projectRoot) {
239769
+ const redirectPath = await detectFullStackArtifact(projectRoot);
239770
+ if (!redirectPath) {
239771
+ return null;
239772
+ }
239773
+ const config = await resolveWranglerConfig(redirectPath);
239774
+ return {
239775
+ config,
239776
+ modules: await collectModules(config),
239777
+ assetsDir: config.assetsDirectory && await pathExists(config.assetsDirectory) ? config.assetsDirectory : null
239778
+ };
239779
+ }
239780
+
239781
+ // src/core/site/manifest.ts
239782
+ import { createHash } from "node:crypto";
239783
+ import { createReadStream } from "node:fs";
239784
+ import { stat as stat2 } from "node:fs/promises";
239785
+ import { basename as basename5, extname, join as join17 } from "node:path";
239773
239786
 
239774
239787
  // ../../node_modules/p-map/index.js
239775
239788
  async function pMap(iterable, mapper, {
@@ -239895,7 +239908,99 @@ async function pMap(iterable, mapper, {
239895
239908
  }
239896
239909
  var pMapSkip = Symbol("skip");
239897
239910
 
239911
+ // src/core/site/manifest.ts
239912
+ var MAX_ASSET_COUNT = 1e5;
239913
+ var ASSETS_IGNORE_FILE = ".assetsignore";
239914
+ var ALWAYS_IGNORED = new Set([
239915
+ ASSETS_IGNORE_FILE,
239916
+ "wrangler.json",
239917
+ ".dev.vars"
239918
+ ]);
239919
+ var MIME_TYPES = {
239920
+ ".html": "text/html",
239921
+ ".htm": "text/html",
239922
+ ".css": "text/css",
239923
+ ".js": "text/javascript",
239924
+ ".mjs": "text/javascript",
239925
+ ".json": "application/json",
239926
+ ".map": "application/json",
239927
+ ".txt": "text/plain",
239928
+ ".xml": "application/xml",
239929
+ ".svg": "image/svg+xml",
239930
+ ".png": "image/png",
239931
+ ".jpg": "image/jpeg",
239932
+ ".jpeg": "image/jpeg",
239933
+ ".gif": "image/gif",
239934
+ ".webp": "image/webp",
239935
+ ".avif": "image/avif",
239936
+ ".ico": "image/x-icon",
239937
+ ".woff": "font/woff",
239938
+ ".woff2": "font/woff2",
239939
+ ".ttf": "font/ttf",
239940
+ ".otf": "font/otf",
239941
+ ".eot": "application/vnd.ms-fontobject",
239942
+ ".mp3": "audio/mpeg",
239943
+ ".mp4": "video/mp4",
239944
+ ".webm": "video/webm",
239945
+ ".pdf": "application/pdf",
239946
+ ".wasm": "application/wasm",
239947
+ ".webmanifest": "application/manifest+json"
239948
+ };
239949
+ function getAssetContentType(filePath) {
239950
+ return MIME_TYPES[extname(filePath).toLowerCase()] ?? "application/octet-stream";
239951
+ }
239952
+ async function walkBuildOutput(outputDir) {
239953
+ const found = await globby("**/*", {
239954
+ cwd: outputDir,
239955
+ dot: true,
239956
+ onlyFiles: true,
239957
+ followSymbolicLinks: false,
239958
+ ignoreFiles: [ASSETS_IGNORE_FILE]
239959
+ });
239960
+ return found.filter((path) => !ALWAYS_IGNORED.has(basename5(path))).sort();
239961
+ }
239962
+ var STAT_CONCURRENCY = 32;
239963
+ async function describeBuildOutput(outputDir) {
239964
+ const relativePaths = await walkBuildOutput(outputDir);
239965
+ return await pMap(relativePaths, async (path) => {
239966
+ const absolutePath = join17(outputDir, ...path.split("/"));
239967
+ return { path, absolutePath, size: (await stat2(absolutePath)).size };
239968
+ }, { concurrency: STAT_CONCURRENCY });
239969
+ }
239970
+ async function hashFileInto(hash, absolutePath) {
239971
+ for await (const chunk of createReadStream(absolutePath)) {
239972
+ hash.update(chunk);
239973
+ }
239974
+ return hash;
239975
+ }
239976
+ async function hashAssetFile(appId, absolutePath) {
239977
+ const hash = await hashFileInto(createHash("sha256").update(Buffer.from(appId, "utf8")), absolutePath);
239978
+ return hash.digest("hex").slice(0, 32);
239979
+ }
239980
+ async function buildAssetManifest(assetsDir, appId) {
239981
+ const manifest = {};
239982
+ const filesByHash = new Map;
239983
+ const files = await describeBuildOutput(assetsDir);
239984
+ if (files.length > MAX_ASSET_COUNT) {
239985
+ throw new InvalidInputError(`Too many static assets: found ${files.length}, the limit is ${MAX_ASSET_COUNT} files.`);
239986
+ }
239987
+ for (const { path: relativePath, absolutePath, size } of files) {
239988
+ const hash = await hashAssetFile(appId, absolutePath);
239989
+ manifest[`/${relativePath}`] = { hash, size };
239990
+ if (!filesByHash.has(hash)) {
239991
+ filesByHash.set(hash, {
239992
+ absolutePath,
239993
+ hash,
239994
+ size,
239995
+ contentType: getAssetContentType(absolutePath)
239996
+ });
239997
+ }
239998
+ }
239999
+ return { manifest, filesByHash };
240000
+ }
240001
+
239898
240002
  // src/core/site/upload.ts
240003
+ import { readFile as readFile2 } from "node:fs/promises";
239899
240004
  var DEFAULT_UPLOAD_CONCURRENCY = 3;
239900
240005
  var MAX_UPLOAD_CONCURRENCY = 50;
239901
240006
  var MAX_UPLOAD_ATTEMPTS = 3;
@@ -239994,11 +240099,17 @@ async function uploadPresignedAsset(upload, assets) {
239994
240099
  if (!file) {
239995
240100
  throw new InternalError(`Server requested upload of unknown asset path: ${upload.path}`);
239996
240101
  }
239997
- const content = await readFile2(file.absolutePath);
240102
+ await putPresigned(upload, file.absolutePath);
240103
+ }
240104
+ async function putPresigned(upload, absolutePath) {
240105
+ const content = await readFile2(absolutePath);
239998
240106
  try {
239999
240107
  await distribution_default.put(upload.url, {
240000
240108
  body: new Uint8Array(content),
240001
- headers: { "Content-Type": upload.contentType },
240109
+ headers: {
240110
+ "Content-Type": upload.contentType,
240111
+ ...upload.checksumSha256 ? { "x-amz-checksum-sha256": upload.checksumSha256 } : {}
240112
+ },
240002
240113
  timeout: 120000,
240003
240114
  retry: UPLOAD_RETRY
240004
240115
  });
@@ -240007,85 +240118,6 @@ async function uploadPresignedAsset(upload, assets) {
240007
240118
  }
240008
240119
  }
240009
240120
 
240010
- // src/core/site/wrangler-config.ts
240011
- import { dirname as dirname12, join as join17, resolve as resolve4 } from "node:path";
240012
- var WRANGLER_REDIRECT_PATH = join17(".wrangler", "deploy", "config.json");
240013
- var RedirectConfigSchema = looseObject({
240014
- configPath: string2().min(1)
240015
- });
240016
- var WranglerConfigSchema = looseObject({
240017
- main: string2().min(1, "wrangler config is missing a 'main' entry module"),
240018
- no_bundle: boolean2().optional(),
240019
- rules: array(looseObject({ type: string2(), globs: array(string2()) })).optional(),
240020
- assets: looseObject({
240021
- directory: string2().optional(),
240022
- html_handling: string2().optional(),
240023
- not_found_handling: string2().optional(),
240024
- run_worker_first: union([boolean2(), array(string2())]).optional(),
240025
- headers: string2().optional(),
240026
- redirects: string2().optional()
240027
- }).optional(),
240028
- compatibility_date: string2().optional(),
240029
- compatibility_flags: array(string2()).optional(),
240030
- vars: record(string2(), unknown()).optional(),
240031
- upload_source_maps: boolean2().optional()
240032
- });
240033
- async function detectFullStackArtifact(projectRoot) {
240034
- const redirectPath = join17(projectRoot, WRANGLER_REDIRECT_PATH);
240035
- return await pathExists(redirectPath) ? redirectPath : null;
240036
- }
240037
- async function resolveWranglerConfig(redirectPath) {
240038
- const configPath = await resolveRedirectedConfigPath(redirectPath);
240039
- const parsed = await readJsonFile(configPath);
240040
- const result = WranglerConfigSchema.safeParse(parsed);
240041
- if (!result.success) {
240042
- throw new ConfigInvalidError(`Invalid wrangler config: ${prettifyError(result.error)}`, configPath);
240043
- }
240044
- const config = result.data;
240045
- if (config.no_bundle !== true) {
240046
- throw new InvalidInputError("This framework's output requires bundling; not yet supported. Base44 only deploys pre-bundled Workers output (no_bundle: true).");
240047
- }
240048
- const configDir = dirname12(configPath);
240049
- const assetsDirectory = config.assets?.directory ? resolve4(configDir, config.assets.directory) : null;
240050
- return {
240051
- configPath,
240052
- configDir,
240053
- main: config.main,
240054
- assetsDirectory,
240055
- assetsConfig: config.assets ? toResolvedAssetsConfig(config.assets) : null,
240056
- compatibilityDate: config.compatibility_date ?? null,
240057
- compatibilityFlags: config.compatibility_flags ?? [],
240058
- rules: (config.rules ?? []).map((rule) => ({
240059
- type: rule.type,
240060
- globs: rule.globs
240061
- })),
240062
- uploadSourceMaps: config.upload_source_maps ?? false
240063
- };
240064
- }
240065
- async function resolveRedirectedConfigPath(redirectPath) {
240066
- const parsed = await readJsonFile(redirectPath);
240067
- const result = RedirectConfigSchema.safeParse(parsed);
240068
- if (!result.success) {
240069
- throw new ConfigInvalidError(`Invalid deploy redirect file: ${prettifyError(result.error)}`, redirectPath);
240070
- }
240071
- const configPath = resolve4(dirname12(redirectPath), result.data.configPath);
240072
- if (!await pathExists(configPath)) {
240073
- throw new ConfigInvalidError(`Wrangler config referenced by ${redirectPath} does not exist: ${configPath}`, redirectPath, {
240074
- hints: [{ message: "Rebuild the project to regenerate the artifact" }]
240075
- });
240076
- }
240077
- return configPath;
240078
- }
240079
- function toResolvedAssetsConfig(assets) {
240080
- return {
240081
- htmlHandling: assets.html_handling,
240082
- notFoundHandling: assets.not_found_handling,
240083
- runWorkerFirst: assets.run_worker_first,
240084
- headers: assets.headers,
240085
- redirects: assets.redirects
240086
- };
240087
- }
240088
-
240089
240121
  // src/core/site/deployment.ts
240090
240122
  var NO_ASSETS = { manifest: {}, filesByHash: new Map };
240091
240123
  var DEPLOYMENTS_API_ENV = "BASE44_DEPLOYMENTS_API";
@@ -240112,12 +240144,11 @@ async function deployToDeployments(options) {
240112
240144
  return { deploymentId: finalized.deploymentId, gitHash };
240113
240145
  }
240114
240146
  async function resolveWorkerBuild(projectRoot, progress) {
240115
- const redirectPath = await detectFullStackArtifact(projectRoot);
240116
- if (!redirectPath) {
240147
+ const built = await resolveFullStackBuild(projectRoot);
240148
+ if (!built) {
240117
240149
  return null;
240118
240150
  }
240119
- const config = await resolveWranglerConfig(redirectPath);
240120
- const assetsDir = config.assetsDirectory && await pathExists(config.assetsDirectory) ? config.assetsDirectory : null;
240151
+ const { config, modules, assetsDir } = built;
240121
240152
  return {
240122
240153
  config: {
240123
240154
  main: config.main,
@@ -240125,7 +240156,7 @@ async function resolveWorkerBuild(projectRoot, progress) {
240125
240156
  compatibility_flags: config.compatibilityFlags,
240126
240157
  assets: buildAssetsConfig(config.assetsConfig, progress)
240127
240158
  },
240128
- modules: await collectModules(config),
240159
+ modules,
240129
240160
  assetsDir
240130
240161
  };
240131
240162
  }
@@ -246669,6 +246700,13 @@ async function resolveGitHash(projectRoot, explicit) {
246669
246700
  }
246670
246701
  return hash;
246671
246702
  }
246703
+ async function resolveProvenanceCommit(projectRoot, explicit) {
246704
+ if (explicit) {
246705
+ return await resolveGitHash(projectRoot, explicit);
246706
+ }
246707
+ const hash = await gitHead(projectRoot);
246708
+ return hash && isGitCommitHash(hash) ? hash : undefined;
246709
+ }
246672
246710
  async function gitHead(projectRoot) {
246673
246711
  try {
246674
246712
  const { stdout } = await execa("git", ["rev-parse", "HEAD"], {
@@ -247038,6 +247076,12 @@ async function ensureAppContext(ctx, options = {}) {
247038
247076
  ctx.app = appContext;
247039
247077
  ctx.errorReporter.setContext({ appId: appContext.id });
247040
247078
  }
247079
+ function requireApp(ctx) {
247080
+ if (!ctx.app) {
247081
+ throw new InternalError("This command read an app context it never resolved — it is declared with requireAppContext: false.");
247082
+ }
247083
+ return ctx.app;
247084
+ }
247041
247085
 
247042
247086
  // src/cli/utils/version-check.ts
247043
247087
  async function checkForUpgrade() {
@@ -247204,6 +247248,155 @@ async function resolveBranchName(name) {
247204
247248
  return matches[0].id;
247205
247249
  }
247206
247250
 
247251
+ // src/core/version/publish.ts
247252
+ import { randomUUID as randomUUID4 } from "node:crypto";
247253
+
247254
+ // src/core/version/schema.ts
247255
+ var DeclareVersionResponseSchema = object({
247256
+ session_id: string2(),
247257
+ uploads: array(object({
247258
+ path: string2(),
247259
+ url: string2(),
247260
+ content_type: string2(),
247261
+ content_length: number2(),
247262
+ checksum_sha256: string2()
247263
+ }))
247264
+ }).transform((data) => ({
247265
+ sessionId: data.session_id,
247266
+ uploads: data.uploads.map((upload) => ({
247267
+ path: upload.path,
247268
+ url: upload.url,
247269
+ contentType: upload.content_type,
247270
+ contentLength: upload.content_length,
247271
+ checksumSha256: upload.checksum_sha256
247272
+ }))
247273
+ }));
247274
+ var CreateVersionResponseSchema = object({
247275
+ version_id: string2(),
247276
+ manifest_hash: string2()
247277
+ }).transform((data) => ({
247278
+ versionId: data.version_id,
247279
+ manifestHash: data.manifest_hash
247280
+ }));
247281
+ var EnvironmentResponseSchema = object({
247282
+ name: string2(),
247283
+ version_id: string2(),
247284
+ manifest_hash: string2(),
247285
+ deployment_id: string2()
247286
+ }).transform((data) => ({
247287
+ name: data.name,
247288
+ versionId: data.version_id,
247289
+ manifestHash: data.manifest_hash,
247290
+ deploymentId: data.deployment_id
247291
+ }));
247292
+
247293
+ // src/core/version/api.ts
247294
+ var DEFAULT_VERSION_UPLOAD_CONCURRENCY = 8;
247295
+ var MAX_VERSION_UPLOAD_CONCURRENCY = 16;
247296
+ function declaredFile({ path, size, digest }) {
247297
+ return { path, size, digest };
247298
+ }
247299
+ async function post(path, json, doing) {
247300
+ try {
247301
+ return await getAppClient().post(path, { json, timeout: 180000 });
247302
+ } catch (error) {
247303
+ throw await ApiError.fromHttpError(error, doing);
247304
+ }
247305
+ }
247306
+ async function patch(path, json, doing) {
247307
+ try {
247308
+ return await getAppClient().patch(path, { json, timeout: 180000 });
247309
+ } catch (error) {
247310
+ throw await ApiError.fromHttpError(error, doing);
247311
+ }
247312
+ }
247313
+ function parse10(schema, body, what) {
247314
+ const result = schema.safeParse(body);
247315
+ if (!result.success) {
247316
+ throw new SchemaValidationError(`Invalid ${what} response from server`, result.error);
247317
+ }
247318
+ return result.data;
247319
+ }
247320
+ async function createVersion(artifacts, options = {}) {
247321
+ const declared = parse10(DeclareVersionResponseSchema, await (await post("versions", {
247322
+ static_bundle: artifacts.files.map(declaredFile),
247323
+ ...artifacts.siteWorker ? {
247324
+ site_worker: {
247325
+ main: artifacts.siteWorker.main,
247326
+ modules: artifacts.siteWorker.modules.map(declaredFile),
247327
+ assets: artifacts.siteWorker.assets.map(declaredFile),
247328
+ compatibility_date: artifacts.siteWorker.compatibilityDate,
247329
+ compatibility_flags: artifacts.siteWorker.compatibilityFlags
247330
+ }
247331
+ } : {},
247332
+ entities: artifacts.entities,
247333
+ agents: artifacts.agents,
247334
+ source_commit: options.sourceCommit
247335
+ }, "declaring a version")).json(), "declare");
247336
+ const declaredFiles = [
247337
+ ...artifacts.files,
247338
+ ...artifacts.siteWorker?.modules ?? [],
247339
+ ...artifacts.siteWorker?.assets ?? []
247340
+ ];
247341
+ options.progress?.onDeclared?.({ fileCount: declaredFiles.length });
247342
+ if (declared.uploads.length !== declaredFiles.length) {
247343
+ throw new InternalError(`Declared ${declaredFiles.length} files but the server signed ${declared.uploads.length} upload URLs.`);
247344
+ }
247345
+ const drifted = declared.uploads.findIndex((upload, index) => upload.path !== declaredFiles[index].path);
247346
+ if (drifted !== -1) {
247347
+ throw new InternalError(`Upload ${drifted} is signed for ${declared.uploads[drifted].path}, but that slot declared ${declaredFiles[drifted].path}.`);
247348
+ }
247349
+ let uploadedFiles = 0;
247350
+ await pMap(declared.uploads, async (upload, index) => {
247351
+ await putPresigned(upload, declaredFiles[index].absolutePath);
247352
+ uploadedFiles++;
247353
+ options.progress?.onUpload?.({
247354
+ uploadedFiles,
247355
+ totalFiles: declared.uploads.length
247356
+ });
247357
+ }, { concurrency: options.concurrency ?? DEFAULT_VERSION_UPLOAD_CONCURRENCY });
247358
+ return parse10(CreateVersionResponseSchema, await (await post(`versions/${encodeURIComponent(declared.sessionId)}/finalize`, {}, "creating a version")).json(), "create version");
247359
+ }
247360
+ async function setEnvironmentVersion(environment, versionId, options = {}) {
247361
+ return parse10(EnvironmentResponseSchema, await (await patch(`environments/${encodeURIComponent(environment)}`, {
247362
+ version_id: versionId,
247363
+ ...options.idempotencyKey ? { idempotency_key: options.idempotencyKey } : {}
247364
+ }, "setting the environment's version")).json(), "environment");
247365
+ }
247366
+
247367
+ // src/core/version/publish.ts
247368
+ var STEP = Symbol.for("base44.publishStep");
247369
+ var DEFAULT_ENVIRONMENT = "production";
247370
+ async function tagStep(step, run) {
247371
+ try {
247372
+ return await run();
247373
+ } catch (error) {
247374
+ if (error !== null && typeof error === "object" && !(STEP in error) && Object.isExtensible(error)) {
247375
+ Object.defineProperty(error, STEP, { value: step, enumerable: false });
247376
+ }
247377
+ throw error;
247378
+ }
247379
+ }
247380
+ function stepOf(error) {
247381
+ return error !== null && typeof error === "object" && STEP in error ? error[STEP] : undefined;
247382
+ }
247383
+ async function publishVersion(artifacts, options = {}) {
247384
+ const version = await tagStep("create_version", () => createVersion(artifacts, {
247385
+ sourceCommit: options.sourceCommit,
247386
+ concurrency: options.concurrency,
247387
+ progress: options.progress
247388
+ }));
247389
+ const environment = await tagStep("deploy", () => setEnvironmentVersion(options.target ?? DEFAULT_ENVIRONMENT, version.versionId, {
247390
+ idempotencyKey: randomUUID4()
247391
+ }));
247392
+ return {
247393
+ environment: environment.name,
247394
+ versionId: environment.versionId,
247395
+ manifestHash: environment.manifestHash,
247396
+ deploymentId: environment.deploymentId
247397
+ };
247398
+ }
247399
+
247207
247400
  // src/cli/utils/command/Base44Command.ts
247208
247401
  function writeJsonSuccess(result) {
247209
247402
  if (result.stdout) {
@@ -247221,6 +247414,10 @@ function writeJsonError(error) {
247221
247414
  const envelope = {
247222
247415
  error: error instanceof Error ? error.message : String(error)
247223
247416
  };
247417
+ const step = stepOf(error);
247418
+ if (step !== undefined) {
247419
+ envelope.step = step;
247420
+ }
247224
247421
  if (isCLIError(error)) {
247225
247422
  envelope.code = error.code;
247226
247423
  if (error.details.length > 0) {
@@ -249563,17 +249760,147 @@ async function maybeAskToBuild(isNonInteractive, buildCommand) {
249563
249760
  });
249564
249761
  return !Ct(answer) && answer;
249565
249762
  }
249763
+ // src/core/version/artifacts.ts
249764
+ import { createHash as createHash2 } from "node:crypto";
249765
+ import { join as join28, resolve as resolve11 } from "node:path";
249766
+
249767
+ // src/core/version/project.ts
249768
+ import { dirname as dirname21, join as join27, resolve as resolve10 } from "node:path";
249769
+ var DEFAULT_BUILD_COMMAND = "npm run build";
249770
+ var DEFAULT_OUTPUT_DIRECTORY = "dist";
249771
+ async function resolvePublishTarget(projectRoot, overrides = {}) {
249772
+ const project = await readSettingsIfPresent(projectRoot);
249773
+ const root = project?.root ?? projectRoot ?? process.cwd();
249774
+ return {
249775
+ root,
249776
+ configDir: project ? dirname21(project.configPath) : join27(root, PROJECT_SUBDIR),
249777
+ buildCommand: project ? project.site?.buildCommand : DEFAULT_BUILD_COMMAND,
249778
+ outputDir: outputDirectory(project, root, overrides.outputDir),
249779
+ entitiesDir: project?.entitiesDir ?? "entities",
249780
+ agentsDir: project?.agentsDir ?? "agents"
249781
+ };
249782
+ }
249783
+ function outputDirectory(project, root, override) {
249784
+ const configured = override ?? (project ? project.site?.outputDirectory : DEFAULT_OUTPUT_DIRECTORY);
249785
+ return configured ? resolve10(root, configured) : null;
249786
+ }
249787
+ function requireOutputDir(target) {
249788
+ if (target.outputDir === null) {
249789
+ throw new ConfigNotFoundError("No site configuration found.", {
249790
+ hints: [
249791
+ {
249792
+ message: `Add 'site.outputDirectory' to your config.jsonc (e.g., "site": { "outputDirectory": "dist" })`
249793
+ },
249794
+ { message: `Or pass --output-dir <dir>, relative to ${target.root}` }
249795
+ ]
249796
+ });
249797
+ }
249798
+ return target.outputDir;
249799
+ }
249800
+ async function readSettingsIfPresent(projectRoot) {
249801
+ try {
249802
+ return await readProjectSettings(projectRoot);
249803
+ } catch (error) {
249804
+ if (error instanceof ConfigNotFoundError) {
249805
+ return null;
249806
+ }
249807
+ throw error;
249808
+ }
249809
+ }
249566
249810
 
249811
+ // src/core/version/artifacts.ts
249812
+ var MAX_FILE_COUNT = 50000;
249813
+ var HASH_CONCURRENCY = 32;
249814
+ var ENTRY2 = "index.html";
249815
+ async function digestFile(absolutePath) {
249816
+ const hash = await hashFileInto(createHash2("sha256"), absolutePath);
249817
+ return `sha256:${hash.digest("hex")}`;
249818
+ }
249819
+ async function collectBuildOutput(outputDir, options = {}) {
249820
+ const found = await describeBuildOutput(outputDir);
249821
+ if (found.length === 0) {
249822
+ throw new InvalidInputError(`No files found in ${outputDir}. Build the site before creating a version.`, {
249823
+ hints: [
249824
+ { message: "Run 'base44 build' first", command: "base44 build" }
249825
+ ]
249826
+ });
249827
+ }
249828
+ if (found.length > MAX_FILE_COUNT) {
249829
+ throw new InvalidInputError(`Too many files: found ${found.length}, the limit is ${MAX_FILE_COUNT}.`);
249830
+ }
249831
+ if (options.requireEntry !== false && !found.some((f) => f.path === ENTRY2)) {
249832
+ throw new InvalidInputError(`${outputDir} has no ${ENTRY2}, so nothing could enter the site.`);
249833
+ }
249834
+ return await pMap(found, async (file) => ({ ...file, digest: await digestFile(file.absolutePath) }), { concurrency: HASH_CONCURRENCY });
249835
+ }
249836
+ async function collectSiteWorker(projectRoot) {
249837
+ const built = await resolveFullStackBuild(projectRoot);
249838
+ if (!built) {
249839
+ return null;
249840
+ }
249841
+ const { config, modules, assetsDir } = built;
249842
+ const entry = resolve11(config.configDir, config.main);
249843
+ const main = modules.find((m) => m.absolutePath === entry)?.name;
249844
+ if (!main) {
249845
+ throw new InvalidInputError(`The Worker's entry module ${config.main} is not among the ${modules.length} modules collected from ${config.configDir}.`);
249846
+ }
249847
+ return {
249848
+ main,
249849
+ modules: await pMap(modules, async ({ name, absolutePath, size }) => ({
249850
+ path: name,
249851
+ absolutePath,
249852
+ size,
249853
+ digest: await digestFile(absolutePath)
249854
+ }), { concurrency: HASH_CONCURRENCY }),
249855
+ assets: assetsDir ? await collectBuildOutput(assetsDir, { requireEntry: false }) : [],
249856
+ compatibilityDate: config.compatibilityDate,
249857
+ compatibilityFlags: config.compatibilityFlags
249858
+ };
249859
+ }
249860
+ async function readRawResources(dir) {
249861
+ if (!await pathExists(dir)) {
249862
+ return {};
249863
+ }
249864
+ const files = await globby(`**/*.${CONFIG_FILE_EXTENSION_GLOB}`, {
249865
+ cwd: dir,
249866
+ onlyFiles: true,
249867
+ followSymbolicLinks: false
249868
+ });
249869
+ const payloads = {};
249870
+ for (const relativePath of files.sort()) {
249871
+ const name = relativePath.replace(/\.jsonc?$/, "");
249872
+ payloads[name] = await readJsonFile(join28(dir, ...relativePath.split("/")));
249873
+ }
249874
+ return payloads;
249875
+ }
249876
+ async function collectResources(configDir, dirs) {
249877
+ const [entities, agents] = await Promise.all([
249878
+ readRawResources(join28(configDir, dirs.entitiesDir)),
249879
+ readRawResources(join28(configDir, dirs.agentsDir))
249880
+ ]);
249881
+ return { entities, agents };
249882
+ }
249883
+ async function collectArtifacts(target) {
249884
+ const siteWorker = await collectSiteWorker(target.root);
249885
+ return {
249886
+ files: siteWorker ? [] : await collectBuildOutput(requireOutputDir(target)),
249887
+ ...siteWorker ? { siteWorker } : {},
249888
+ ...await collectResources(target.configDir, target)
249889
+ };
249890
+ }
249891
+ // src/core/version/gate.ts
249892
+ var VERSIONS_API_ENV = "BASE44_VERSIONS_API";
249893
+ function versionsApiEnabled(env = process.env) {
249894
+ const value = env[VERSIONS_API_ENV];
249895
+ return value === "1" || value === "true";
249896
+ }
249567
249897
  // src/cli/commands/project/build.ts
249568
249898
  async function buildAction(ctx) {
249569
- const { app } = ctx;
249570
- if (!app?.projectRoot) {
249571
- throw new ConfigInvalidError("base44 build requires a linked local project. Run it from a project with base44/.app.jsonc.");
249572
- }
249573
- const { project } = await readProjectConfig(app.projectRoot);
249899
+ const app = requireApp(ctx);
249900
+ const target = await resolvePublishTarget(app.projectRoot);
249574
249901
  await runSiteBuild(ctx, {
249575
- root: project.root,
249576
- buildCommand: project.site?.buildCommand,
249902
+ root: target.root,
249903
+ buildCommand: target.buildCommand,
249577
249904
  appId: app.id
249578
249905
  });
249579
249906
  return {
@@ -249581,15 +249908,15 @@ async function buildAction(ctx) {
249581
249908
  };
249582
249909
  }
249583
249910
  function getBuildCommand() {
249584
- return new Base44Command("build").description("Build the site with the Base44 app id injected").action(buildAction);
249911
+ return new Base44Command("build", { requireAuth: false }).description("Build the site with the Base44 app id injected").action(buildAction);
249585
249912
  }
249586
249913
 
249587
249914
  // src/cli/commands/project/create.ts
249588
- import { basename as basename6, resolve as resolve10 } from "node:path";
249915
+ import { basename as basename6, resolve as resolve12 } from "node:path";
249589
249916
  var import_kebabCase = __toESM(require_kebabCase(), 1);
249590
249917
 
249591
249918
  // src/cli/commands/project/scaffold-shared.ts
249592
- import { join as join27 } from "node:path";
249919
+ import { join as join29 } from "node:path";
249593
249920
  var DEFAULT_TEMPLATE_ID = "backend-only";
249594
249921
  async function getTemplateById(templateId) {
249595
249922
  const templates = await listTemplates();
@@ -249652,7 +249979,7 @@ async function completeProjectSetup({
249652
249979
  env: { VITE_BASE44_APP_ID: projectId }
249653
249980
  })`${buildCommand}`;
249654
249981
  updateMessage("Deploying site...");
249655
- return await deploySite(join27(resolvedPath, outputDirectory));
249982
+ return await deploySite(join29(resolvedPath, outputDirectory));
249656
249983
  }, {
249657
249984
  successMessage: theme.colors.base44Orange("Site deployed successfully"),
249658
249985
  errorMessage: "Failed to deploy site"
@@ -249781,7 +250108,7 @@ async function createInteractive(options, ctx) {
249781
250108
  }, ctx);
249782
250109
  }
249783
250110
  async function createNonInteractive(options, ctx) {
249784
- ctx.log.info(`Creating a new project at ${resolve10(options.path)}`);
250111
+ ctx.log.info(`Creating a new project at ${resolve12(options.path)}`);
249785
250112
  const template = await getTemplateById(options.template ?? DEFAULT_TEMPLATE_ID);
249786
250113
  return await executeCreate({
249787
250114
  template,
@@ -249805,7 +250132,7 @@ async function executeCreate({
249805
250132
  }, ctx) {
249806
250133
  const { log, runTask } = ctx;
249807
250134
  const name = rawName.trim();
249808
- const resolvedPath = resolve10(projectPath);
250135
+ const resolvedPath = resolve12(projectPath);
249809
250136
  const organizationId = await resolveWorkspaceId(ctx, flagWorkspaceId, isInteractive);
249810
250137
  const { projectId } = await runTask("Setting up your project...", async () => {
249811
250138
  return await createProjectFiles({
@@ -250446,7 +250773,7 @@ function getLogsCommand() {
250446
250773
  }
250447
250774
 
250448
250775
  // src/cli/commands/project/scaffold.ts
250449
- import { basename as basename7, resolve as resolve11 } from "node:path";
250776
+ import { basename as basename7, resolve as resolve13 } from "node:path";
250450
250777
  function resolveAppId(options) {
250451
250778
  const appId = options.appId;
250452
250779
  if (!appId) {
@@ -250462,7 +250789,7 @@ function resolveAppId(options) {
250462
250789
  async function scaffoldAction(ctx, name, options, command) {
250463
250790
  const { log, runTask } = ctx;
250464
250791
  const appId = resolveAppId(command.optsWithGlobals());
250465
- const resolvedPath = resolve11("./");
250792
+ const resolvedPath = resolve13("./");
250466
250793
  const projectName = (name ?? basename7(resolvedPath)).trim();
250467
250794
  const template = await getTemplateById("backend-only");
250468
250795
  log.info(`Scaffolding project at ${resolvedPath}`);
@@ -250510,6 +250837,69 @@ function getVisibilityCommand() {
250510
250837
  ])).action(setVisibility);
250511
250838
  }
250512
250839
 
250840
+ // src/cli/commands/versions/options.ts
250841
+ function outputDirOption() {
250842
+ return new Option2("--output-dir <dir>", "Build output directory (defaults to the project's, else dist)");
250843
+ }
250844
+ function targetOption() {
250845
+ return new Option2("--target <name>", "Environment to serve the version at");
250846
+ }
250847
+ function gitHashOption() {
250848
+ return new Option2("--git-hash <hash>", "Commit the build came from (defaults to the checkout's HEAD)").argParser((value) => {
250849
+ if (!isGitCommitHash(value)) {
250850
+ throw new InvalidArgumentError2("Expected a git commit hash (7-64 hex chars).");
250851
+ }
250852
+ return value;
250853
+ });
250854
+ }
250855
+ function concurrencyOption() {
250856
+ return new Option2("--concurrency <n>", "Parallel file uploads").default(DEFAULT_VERSION_UPLOAD_CONCURRENCY).argParser((value) => {
250857
+ const parsed = Number(value);
250858
+ if (!Number.isInteger(parsed) || parsed < 1 || parsed > MAX_VERSION_UPLOAD_CONCURRENCY) {
250859
+ throw new InvalidArgumentError2(`Expected a whole number between 1 and ${MAX_VERSION_UPLOAD_CONCURRENCY}.`);
250860
+ }
250861
+ return parsed;
250862
+ });
250863
+ }
250864
+
250865
+ // src/cli/commands/publish.ts
250866
+ async function publishAction(ctx, options) {
250867
+ const { runTask, log, jsonMode } = ctx;
250868
+ const app = requireApp(ctx);
250869
+ const target = await tagStep("create_version", () => resolvePublishTarget(app.projectRoot, { outputDir: options.outputDir }));
250870
+ if (options.build !== false) {
250871
+ await tagStep("build", () => runSiteBuild(ctx, {
250872
+ root: target.root,
250873
+ buildCommand: target.buildCommand,
250874
+ appId: app.id
250875
+ }));
250876
+ }
250877
+ const gitHash = await resolveProvenanceCommit(target.root, options.gitHash);
250878
+ const result = await runTask("Publishing...", async (updateMessage) => {
250879
+ const artifacts = await tagStep("create_version", () => collectArtifacts(target));
250880
+ return await publishVersion(artifacts, {
250881
+ sourceCommit: gitHash,
250882
+ target: options.target,
250883
+ concurrency: options.concurrency,
250884
+ progress: {
250885
+ onDeclared: ({ fileCount }) => updateMessage(`Uploading ${fileCount} files`),
250886
+ onUpload: ({ uploadedFiles, totalFiles }) => updateMessage(`Uploaded ${uploadedFiles} of ${totalFiles} files`)
250887
+ }
250888
+ });
250889
+ }, { successMessage: "Published", errorMessage: "Publish failed" });
250890
+ if (!jsonMode) {
250891
+ log.message(theme.styles.dim(`version ${result.versionId}`));
250892
+ }
250893
+ return {
250894
+ outroMessage: `${result.environment} now serves ${result.versionId}`,
250895
+ stdout: jsonMode ? `${JSON.stringify(result, null, 2)}
250896
+ ` : undefined
250897
+ };
250898
+ }
250899
+ function getPublishCommand() {
250900
+ 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").addOption(outputDirOption()).addOption(targetOption()).addOption(gitHashOption()).addOption(concurrencyOption()).action(publishAction);
250901
+ }
250902
+
250513
250903
  // src/core/resources/sandbox/schema.ts
250514
250904
  var FileErrorSchema = object({
250515
250905
  code: string2(),
@@ -250879,7 +251269,7 @@ function getSecretsListCommand() {
250879
251269
  }
250880
251270
 
250881
251271
  // src/cli/commands/secrets/set.ts
250882
- import { resolve as resolve12 } from "node:path";
251272
+ import { resolve as resolve14 } from "node:path";
250883
251273
  function parseEntries(entries) {
250884
251274
  const secrets = {};
250885
251275
  for (const entry of entries) {
@@ -250910,7 +251300,7 @@ async function setSecretsAction({ log, runTask }, entries, options) {
250910
251300
  validateInput(entries, options);
250911
251301
  let secrets;
250912
251302
  if (options.envFile) {
250913
- secrets = await parseEnvFile(resolve12(options.envFile));
251303
+ secrets = await parseEnvFile(resolve14(options.envFile));
250914
251304
  if (Object.keys(secrets).length === 0) {
250915
251305
  throw new InvalidInputError("The env file contains no valid KEY=VALUE entries.");
250916
251306
  }
@@ -250939,7 +251329,7 @@ function getSecretsCommand() {
250939
251329
  }
250940
251330
 
250941
251331
  // src/cli/commands/site/deploy.ts
250942
- import { resolve as resolve13 } from "node:path";
251332
+ import { resolve as resolve15 } from "node:path";
250943
251333
  async function deployAction2(ctx, options) {
250944
251334
  const { isNonInteractive } = ctx;
250945
251335
  if (isNonInteractive && !options.yes) {
@@ -251017,7 +251407,7 @@ async function deployTarball({ runTask }, project) {
251017
251407
  }
251018
251408
  function siteOutputDir(project) {
251019
251409
  const outputDirectory = project.site?.outputDirectory;
251020
- return outputDirectory ? resolve13(project.root, outputDirectory) : null;
251410
+ return outputDirectory ? resolve15(project.root, outputDirectory) : null;
251021
251411
  }
251022
251412
  function getSiteDeployCommand() {
251023
251413
  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)");
@@ -251149,10 +251539,10 @@ function toPascalCase(name) {
251149
251539
  return name.split(/[-_\s]+/).map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join("");
251150
251540
  }
251151
251541
  // src/core/types/update-project.ts
251152
- import { join as join30 } from "node:path";
251542
+ import { join as join33 } from "node:path";
251153
251543
  var TYPES_INCLUDE_PATH = `${PROJECT_SUBDIR}/${TYPES_OUTPUT_SUBDIR}/*.d.ts`;
251154
251544
  async function updateProjectConfig(projectRoot) {
251155
- const tsconfigPath = join30(projectRoot, "tsconfig.json");
251545
+ const tsconfigPath = join33(projectRoot, "tsconfig.json");
251156
251546
  if (!await pathExists(tsconfigPath)) {
251157
251547
  return false;
251158
251548
  }
@@ -251201,6 +251591,59 @@ function getTypesCommand() {
251201
251591
  return new Command2("types").description("Manage TypeScript type generation").addCommand(getTypesGenerateCommand());
251202
251592
  }
251203
251593
 
251594
+ // src/cli/commands/versions/create.ts
251595
+ async function createAction2(ctx, options) {
251596
+ const { runTask, jsonMode } = ctx;
251597
+ const target = await resolvePublishTarget(requireApp(ctx).projectRoot, {
251598
+ outputDir: options.outputDir
251599
+ });
251600
+ const gitHash = await resolveProvenanceCommit(target.root, options.gitHash);
251601
+ const version = await runTask("Creating version...", async (updateMessage) => await createVersion(await collectArtifacts(target), {
251602
+ sourceCommit: gitHash,
251603
+ concurrency: options.concurrency,
251604
+ progress: {
251605
+ onDeclared: ({ fileCount }) => updateMessage(`Uploading ${fileCount} files`),
251606
+ onUpload: ({ uploadedFiles, totalFiles }) => updateMessage(`Uploaded ${uploadedFiles} of ${totalFiles} files`)
251607
+ }
251608
+ }), {
251609
+ successMessage: "Version created",
251610
+ errorMessage: "Create version failed"
251611
+ });
251612
+ return {
251613
+ outroMessage: `Version ${version.versionId} (${version.manifestHash})`,
251614
+ stdout: jsonMode ? `${JSON.stringify(version, null, 2)}
251615
+ ` : undefined
251616
+ };
251617
+ }
251618
+ function getVersionCreateCommand() {
251619
+ return new Base44Command("create").description("Record the built output as a version, without deploying it").addOption(outputDirOption()).addOption(gitHashOption()).addOption(concurrencyOption()).action(createAction2);
251620
+ }
251621
+
251622
+ // src/cli/commands/versions/deploy.ts
251623
+ import { randomUUID as randomUUID5 } from "node:crypto";
251624
+ async function deployAction3({ runTask, jsonMode }, versionId, options) {
251625
+ const environment = options.target ?? DEFAULT_ENVIRONMENT;
251626
+ const result = await runTask(`Pointing ${environment} at ${versionId}...`, async () => await setEnvironmentVersion(environment, versionId, {
251627
+ idempotencyKey: randomUUID5()
251628
+ }), {
251629
+ successMessage: "Environment updated",
251630
+ errorMessage: "Could not update the environment"
251631
+ });
251632
+ return {
251633
+ outroMessage: `${result.name} now serves ${result.versionId}`,
251634
+ stdout: jsonMode ? `${JSON.stringify(result, null, 2)}
251635
+ ` : undefined
251636
+ };
251637
+ }
251638
+ function getVersionDeployCommand() {
251639
+ return new Base44Command("deploy").description("Point an environment at an already-recorded version (also the rollback)").argument("<version-id>", "The version to serve").addOption(targetOption()).action(deployAction3);
251640
+ }
251641
+
251642
+ // src/cli/commands/versions/index.ts
251643
+ function getVersionsCommand() {
251644
+ return new Command2("versions").description("Record app versions and serve them").addCommand(getVersionCreateCommand()).addCommand(getVersionDeployCommand());
251645
+ }
251646
+
251204
251647
  // src/core/resources/workflow/schema.ts
251205
251648
  var WorkflowRunStatusSchema = _enum([
251206
251649
  "running",
@@ -251617,7 +252060,7 @@ function createDevLogger(label, labelColor = theme.styles.dim) {
251617
252060
  // src/cli/dev/dev-server/main.ts
251618
252061
  var import_cors = __toESM(require_lib4(), 1);
251619
252062
  var import_express6 = __toESM(require_express(), 1);
251620
- import { dirname as dirname26, join as join36 } from "node:path";
252063
+ import { dirname as dirname27, join as join38 } from "node:path";
251621
252064
 
251622
252065
  // ../../node_modules/get-port/index.js
251623
252066
  import net from "node:net";
@@ -251747,7 +252190,7 @@ var $tmpName = promisify11(tmp.tmpName);
251747
252190
 
251748
252191
  // src/cli/dev/dev-server/function-manager.ts
251749
252192
  import { spawn as spawn2 } from "node:child_process";
251750
- import { dirname as dirname23, join as join31 } from "node:path";
252193
+ import { dirname as dirname24, join as join34 } from "node:path";
251751
252194
  import { pathToFileURL } from "node:url";
251752
252195
 
251753
252196
  // src/cli/dev/dev-server/base-function-manager.ts
@@ -251852,7 +252295,7 @@ class FunctionManager extends BaseFunctionManager {
251852
252295
  }
251853
252296
  spawnFunction(func, port) {
251854
252297
  this.logger.log(`Spawning function "${func.name}" on port ${port}`);
251855
- const importMapPath = join31(dirname23(this.wrapperPath), "import-map.json");
252298
+ const importMapPath = join34(dirname24(this.wrapperPath), "import-map.json");
251856
252299
  const process2 = spawn2("deno", ["run", "--allow-all", "--import-map", importMapPath, this.wrapperPath], {
251857
252300
  env: {
251858
252301
  ...globalThis.process.env,
@@ -251926,7 +252369,7 @@ class FunctionManager extends BaseFunctionManager {
251926
252369
  import { mkdirSync as mkdirSync2, writeFileSync as writeFileSync2 } from "node:fs";
251927
252370
  import { isBuiltin } from "node:module";
251928
252371
  import { homedir as homedir3 } from "node:os";
251929
- import { join as join33 } from "node:path";
252372
+ import { join as join35 } from "node:path";
251930
252373
  import { pathToFileURL as pathToFileURL6 } from "node:url";
251931
252374
  var depsPromise;
251932
252375
  function loadDeps() {
@@ -252047,9 +252490,9 @@ export default {
252047
252490
  };
252048
252491
  `;
252049
252492
  function ensureBundlerConfig() {
252050
- const dir = join33(homedir3(), ".base44", "function-bundler");
252493
+ const dir = join35(homedir3(), ".base44", "function-bundler");
252051
252494
  mkdirSync2(dir, { recursive: true });
252052
- const configPath = join33(dir, "deno.json");
252495
+ const configPath = join35(dir, "deno.json");
252053
252496
  writeFileSync2(configPath, `${JSON.stringify({ nodeModulesDir: "auto" }, null, 2)}
252054
252497
  `);
252055
252498
  return configPath;
@@ -253430,11 +253873,11 @@ async function createEntityRoutes(db, logger, broadcast) {
253430
253873
  // src/cli/dev/dev-server/routes/integrations.ts
253431
253874
  var import_express5 = __toESM(require_express(), 1);
253432
253875
  var import_multer = __toESM(require_multer(), 1);
253433
- import { createHash as createHash2, randomUUID as randomUUID4 } from "node:crypto";
253876
+ import { createHash as createHash3, randomUUID as randomUUID6 } from "node:crypto";
253434
253877
  import fs28 from "node:fs";
253435
253878
  import path18 from "node:path";
253436
253879
  function createFileToken(fileUri) {
253437
- return createHash2("sha256").update(fileUri).digest("hex");
253880
+ return createHash3("sha256").update(fileUri).digest("hex");
253438
253881
  }
253439
253882
  function createIntegrationRoutes(mediaFilesDir, baseUrl, remoteProxy, logger) {
253440
253883
  const router = import_express5.Router({ mergeParams: true });
@@ -253447,14 +253890,14 @@ function createIntegrationRoutes(mediaFilesDir, baseUrl, remoteProxy, logger) {
253447
253890
  destination: mediaFilesDir,
253448
253891
  filename: (_req, file, cb) => {
253449
253892
  const ext = path18.extname(file.originalname);
253450
- cb(null, `${randomUUID4()}${ext}`);
253893
+ cb(null, `${randomUUID6()}${ext}`);
253451
253894
  }
253452
253895
  });
253453
253896
  const privateStorage = import_multer.default.diskStorage({
253454
253897
  destination: privateFilesDir,
253455
253898
  filename: (_req, file, cb) => {
253456
253899
  const ext = path18.extname(file.originalname);
253457
- cb(null, `${randomUUID4()}${ext}`);
253900
+ cb(null, `${randomUUID6()}${ext}`);
253458
253901
  }
253459
253902
  });
253460
253903
  const upload = import_multer.default({ storage, limits: { fileSize: MAX_FILE_SIZE } });
@@ -255316,8 +255759,8 @@ async function createDevServer(options) {
255316
255759
  broadcastEntityEvent(io, appId, entityName, event);
255317
255760
  };
255318
255761
  const base44ConfigWatcher = new WatchBase44({
255319
- functions: join36(dirname26(project.configPath), project.functionsDir),
255320
- entities: join36(dirname26(project.configPath), project.entitiesDir)
255762
+ functions: join38(dirname27(project.configPath), project.functionsDir),
255763
+ entities: join38(dirname27(project.configPath), project.entitiesDir)
255321
255764
  }, devLogger);
255322
255765
  base44ConfigWatcher.on("change", async (name) => {
255323
255766
  try {
@@ -255714,7 +256157,7 @@ Examples:
255714
256157
  }
255715
256158
 
255716
256159
  // src/cli/commands/project/eject.ts
255717
- import { resolve as resolve17 } from "node:path";
256160
+ import { resolve as resolve19 } from "node:path";
255718
256161
  var import_kebabCase2 = __toESM(require_kebabCase(), 1);
255719
256162
  async function eject(ctx, options, command) {
255720
256163
  const { log, runTask, isNonInteractive } = ctx;
@@ -255778,7 +256221,7 @@ async function eject(ctx, options, command) {
255778
256221
  Ne("Operation cancelled.");
255779
256222
  throw new CLIExitError(0);
255780
256223
  }
255781
- const resolvedPath = resolve17(selectedPath);
256224
+ const resolvedPath = resolve19(selectedPath);
255782
256225
  await runTask("Downloading your project's code...", async (updateMessage) => {
255783
256226
  await createProjectFilesForExistingProject({
255784
256227
  projectId,
@@ -255862,6 +256305,10 @@ function createProgram(context) {
255862
256305
  program.addCommand(getBranchesCommand());
255863
256306
  program.addCommand(getAuthCommand());
255864
256307
  program.addCommand(getSiteCommand());
256308
+ if (versionsApiEnabled()) {
256309
+ program.addCommand(getPublishCommand());
256310
+ program.addCommand(getVersionsCommand());
256311
+ }
255865
256312
  program.addCommand(getTypesCommand());
255866
256313
  program.addCommand(getExecCommand());
255867
256314
  program.addCommand(getDevCommand());
@@ -255874,7 +256321,7 @@ var import_detect_agent = __toESM(require_dist5(), 1);
255874
256321
  import { release, type } from "node:os";
255875
256322
 
255876
256323
  // ../../node_modules/posthog-node/dist/extensions/error-tracking/modifiers/module.node.mjs
255877
- import { dirname as dirname27, posix, sep as sep2 } from "path";
256324
+ import { dirname as dirname28, posix, sep as sep2 } from "path";
255878
256325
  function createModulerModifier() {
255879
256326
  const getModuleFromFileName = createGetModuleFromFilename();
255880
256327
  return async (frames) => {
@@ -255883,7 +256330,7 @@ function createModulerModifier() {
255883
256330
  return frames;
255884
256331
  };
255885
256332
  }
255886
- function createGetModuleFromFilename(basePath = process.argv[1] ? dirname27(process.argv[1]) : process.cwd(), isWindows = sep2 === "\\") {
256333
+ function createGetModuleFromFilename(basePath = process.argv[1] ? dirname28(process.argv[1]) : process.cwd(), isWindows = sep2 === "\\") {
255887
256334
  const normalizedBase = isWindows ? normalizeWindowsPath2(basePath) : basePath;
255888
256335
  return (filename) => {
255889
256336
  if (!filename)
@@ -259872,9 +260319,9 @@ function addCommandInfoToErrorReporter(program, errorReporter) {
259872
260319
  });
259873
260320
  }
259874
260321
  // src/cli/index.ts
259875
- var __dirname4 = dirname28(fileURLToPath6(import.meta.url));
260322
+ var __dirname4 = dirname29(fileURLToPath6(import.meta.url));
259876
260323
  async function runCLI(options) {
259877
- ensureNpmAssets(join37(__dirname4, "../assets"));
260324
+ ensureNpmAssets(join39(__dirname4, "../assets"));
259878
260325
  const errorReporter = new ErrorReporter;
259879
260326
  errorReporter.registerProcessErrorHandlers();
259880
260327
  const jsonMode = process.argv.includes("--json");
@@ -259913,4 +260360,4 @@ export {
259913
260360
  runCLI
259914
260361
  };
259915
260362
 
259916
- //# debugId=EF36A7D77029953264756E2164756E21
260363
+ //# debugId=7059640A5E6CFF9564756E2164756E21