@base44-preview/cli 0.1.8-pr.589.9d22ef2 → 0.1.9-pr.595.21d640d

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
@@ -247201,24 +247201,33 @@ var ResourceSourceSchema = exports_external.discriminatedUnion("type", [
247201
247201
  ]);
247202
247202
 
247203
247203
  // src/core/resources/entity/schema.ts
247204
- var FieldConditionSchema = exports_external.union([
247205
- exports_external.string(),
247206
- exports_external.number(),
247207
- exports_external.boolean(),
247208
- exports_external.null(),
247209
- exports_external.looseObject({
247210
- $in: exports_external.unknown().optional(),
247211
- $nin: exports_external.unknown().optional(),
247212
- $ne: exports_external.unknown().optional(),
247213
- $all: exports_external.unknown().optional()
247214
- })
247204
+ var fieldConditionOperators = new Set([
247205
+ "$eq",
247206
+ "$ne",
247207
+ "$in",
247208
+ "$nin",
247209
+ "$gt",
247210
+ "$gte",
247211
+ "$lt",
247212
+ "$lte",
247213
+ "$exists",
247214
+ "$all",
247215
+ "$size",
247216
+ "$elemMatch"
247215
247217
  ]);
247216
- var userConditionAllowedKeys = new Set(["role", "email", "id"]);
247217
- var UserConditionSchema = exports_external.looseObject({
247218
- role: exports_external.string().optional(),
247219
- email: exports_external.string().optional(),
247220
- id: exports_external.string().optional()
247221
- }).refine((val) => Object.keys(val).every((key) => userConditionAllowedKeys.has(key) || key.startsWith("data.")), "Keys must be role, email, id, or match data.* pattern");
247218
+ var isScalar = (value) => value === null || ["string", "number", "boolean"].includes(typeof value);
247219
+ var isValidFieldCondition = (value) => {
247220
+ if (isScalar(value)) {
247221
+ return true;
247222
+ }
247223
+ if (typeof value === "object" && !Array.isArray(value)) {
247224
+ return Object.keys(value).every((k2) => fieldConditionOperators.has(k2));
247225
+ }
247226
+ return false;
247227
+ };
247228
+ var supportedOperators = [...fieldConditionOperators].sort().join(", ");
247229
+ var FieldConditionSchema = exports_external.union([exports_external.string(), exports_external.number(), exports_external.boolean(), exports_external.null(), exports_external.looseObject({})]).refine(isValidFieldCondition, `Field condition values must be a primitive or an operator object (${supportedOperators})`);
247230
+ var UserConditionSchema = exports_external.looseObject({}).refine((val) => Object.keys(val).length > 0 && Object.entries(val).every(([key, value]) => !key.startsWith("$") && isScalar(value)), "user_condition compares user attributes by exact equality: a non-empty object of attribute: scalar pairs, no operators");
247222
247231
  var rlsConditionAllowedKeys = new Set([
247223
247232
  "user_condition",
247224
247233
  "created_by",
@@ -247250,18 +247259,13 @@ var RLSConditionSchema = exports_external.looseObject({
247250
247259
  return exports_external.array(RefineRLSConditionSchema).optional();
247251
247260
  }
247252
247261
  });
247253
- var fieldConditionOperators = new Set(["$in", "$nin", "$ne", "$all"]);
247254
- var isValidFieldCondition = (value) => {
247255
- if (value === null || typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
247256
- return true;
247257
- }
247258
- if (typeof value === "object") {
247259
- return Object.keys(value).every((k2) => fieldConditionOperators.has(k2));
247260
- }
247261
- return false;
247262
- };
247263
- var RefineRLSConditionSchema = RLSConditionSchema.refine((val) => Object.entries(val).every(([key, value]) => rlsConditionAllowedKeys.has(key) || isValidFieldCondition(value)), "Field condition values must be a primitive or an operator object ($in, $nin, $ne, $all)");
247264
- var RLSRuleSchema = exports_external.union([exports_external.boolean(), RefineRLSConditionSchema]);
247262
+ var RefineRLSConditionSchema = RLSConditionSchema.refine((val) => Object.entries(val).every(([key, value]) => rlsConditionAllowedKeys.has(key) || isValidFieldCondition(value)), `Field condition values must be a primitive or an operator object (${supportedOperators})`);
247263
+ var RLSRuleSchema = exports_external.union([
247264
+ exports_external.boolean(),
247265
+ exports_external.null(),
247266
+ exports_external.literal(""),
247267
+ RefineRLSConditionSchema
247268
+ ]);
247265
247269
  var EntityRLSSchema = exports_external.looseObject({
247266
247270
  create: RLSRuleSchema.optional(),
247267
247271
  read: RLSRuleSchema.optional(),
@@ -247277,7 +247281,7 @@ var FieldRLSSchema = exports_external.looseObject({
247277
247281
  delete: RLSRuleSchema.optional()
247278
247282
  });
247279
247283
  var PropertyDefinitionSchema = exports_external.looseObject({
247280
- type: exports_external.string(),
247284
+ type: exports_external.union([exports_external.string(), exports_external.array(exports_external.string())]),
247281
247285
  title: exports_external.string().optional(),
247282
247286
  description: exports_external.string().optional(),
247283
247287
  minLength: exports_external.number().int().min(0).optional(),
@@ -247301,7 +247305,7 @@ var PropertyDefinitionSchema = exports_external.looseObject({
247301
247305
  });
247302
247306
  var EntitySchema = exports_external.looseObject({
247303
247307
  type: exports_external.literal("object").default("object"),
247304
- name: exports_external.string().min(1).regex(/^[a-zA-Z0-9]+$/, "Entity name must be alphanumeric only"),
247308
+ name: exports_external.string().min(1).regex(/^\w+$/, "Entity name must contain only letters, numbers, and underscores"),
247305
247309
  title: exports_external.string().optional(),
247306
247310
  description: exports_external.string().optional(),
247307
247311
  properties: exports_external.record(exports_external.string(), PropertyDefinitionSchema).default({}),
@@ -247868,13 +247872,145 @@ var functionResource = {
247868
247872
  readAll: readAllFunctions,
247869
247873
  push: (functions) => deployFunctionsSequentially(functions)
247870
247874
  };
247875
+ // src/core/resources/function/stream-api.ts
247876
+ var StreamLogEventSchema = exports_external.object({
247877
+ time: exports_external.string(),
247878
+ level: exports_external.preprocess((value) => value === "warn" ? "warning" : value, LogLevelSchema),
247879
+ function: exports_external.string().nullable(),
247880
+ message: exports_external.string()
247881
+ });
247882
+ var StreamEndEventSchema = exports_external.object({
247883
+ reason: exports_external.string(),
247884
+ retriable: exports_external.boolean()
247885
+ });
247886
+ function buildStreamUrl(filters) {
247887
+ const { id } = getAppContext();
247888
+ const url2 = new URL(`/api/apps/${id}/functions-mgmt/logs/stream`, getBase44ApiUrl());
247889
+ if (filters.functions?.length) {
247890
+ url2.searchParams.set("function", filters.functions.join(","));
247891
+ }
247892
+ if (filters.env) {
247893
+ url2.searchParams.set("env", filters.env);
247894
+ }
247895
+ return url2.href;
247896
+ }
247897
+ async function buildStreamAuthHeaders() {
247898
+ const workspaceApiKey = getWorkspaceApiKeyFromEnv();
247899
+ if (workspaceApiKey && isWorkspaceApiKey(workspaceApiKey)) {
247900
+ return { api_key: workspaceApiKey };
247901
+ }
247902
+ const auth = await readAuth();
247903
+ if (isTokenExpired(auth)) {
247904
+ const refreshedToken = await refreshAndSaveTokens();
247905
+ if (refreshedToken) {
247906
+ return { Authorization: `Bearer ${refreshedToken}` };
247907
+ }
247908
+ }
247909
+ return { Authorization: `Bearer ${auth.accessToken}` };
247910
+ }
247911
+ function parseStreamEvent(eventName, data) {
247912
+ try {
247913
+ const payload = JSON.parse(data);
247914
+ if (eventName === "end") {
247915
+ const result = StreamEndEventSchema.safeParse(payload);
247916
+ return result.success ? { kind: "end", end: result.data } : null;
247917
+ }
247918
+ if (eventName === "") {
247919
+ const result = StreamLogEventSchema.safeParse(payload);
247920
+ return result.success ? { kind: "log", log: result.data } : null;
247921
+ }
247922
+ return null;
247923
+ } catch {
247924
+ return null;
247925
+ }
247926
+ }
247927
+ var STREAM_SILENCE_TIMEOUT_MS = 60000;
247928
+ async function readOrSilence(reader) {
247929
+ let timer;
247930
+ const silence = new Promise((resolve3) => {
247931
+ timer = setTimeout(() => resolve3("silence"), STREAM_SILENCE_TIMEOUT_MS);
247932
+ });
247933
+ try {
247934
+ return await Promise.race([reader.read(), silence]);
247935
+ } finally {
247936
+ clearTimeout(timer);
247937
+ }
247938
+ }
247939
+ async function* readLines(body) {
247940
+ const reader = body.getReader();
247941
+ const decoder = new TextDecoder;
247942
+ let buffered = "";
247943
+ try {
247944
+ while (true) {
247945
+ const result = await readOrSilence(reader);
247946
+ if (result === "silence") {
247947
+ await reader.cancel();
247948
+ return;
247949
+ }
247950
+ if (result.done || !result.value)
247951
+ return;
247952
+ buffered += decoder.decode(result.value, { stream: true });
247953
+ const lines = buffered.split(`
247954
+ `);
247955
+ buffered = lines.pop() ?? "";
247956
+ yield* lines;
247957
+ }
247958
+ } finally {
247959
+ reader.releaseLock();
247960
+ }
247961
+ }
247962
+ async function* readStreamEvents(body) {
247963
+ let eventName = "";
247964
+ for await (const line of readLines(body)) {
247965
+ if (line.startsWith("event:")) {
247966
+ eventName = line.slice(6).trim();
247967
+ continue;
247968
+ }
247969
+ if (line.startsWith("data:")) {
247970
+ const event = parseStreamEvent(eventName, line.slice(5));
247971
+ eventName = "";
247972
+ if (event)
247973
+ yield event;
247974
+ continue;
247975
+ }
247976
+ if (line.trim() === "")
247977
+ eventName = "";
247978
+ }
247979
+ }
247980
+ var STREAM_CONNECT_TIMEOUT_MS = 1e4;
247981
+ async function openLogStream(filters) {
247982
+ const connectPhase = new AbortController;
247983
+ const connectTimer = setTimeout(() => connectPhase.abort(), STREAM_CONNECT_TIMEOUT_MS);
247984
+ let response;
247985
+ try {
247986
+ response = await fetch(buildStreamUrl(filters), {
247987
+ headers: {
247988
+ Accept: "text/event-stream",
247989
+ ...await buildStreamAuthHeaders()
247990
+ },
247991
+ signal: connectPhase.signal
247992
+ });
247993
+ } catch {
247994
+ return null;
247995
+ } finally {
247996
+ clearTimeout(connectTimer);
247997
+ }
247998
+ if (!response.ok || !response.body)
247999
+ return null;
248000
+ return readStreamEvents(response.body);
248001
+ }
247871
248002
  // src/core/project/config.ts
247872
248003
  class ProjectConfigReader {
247873
248004
  pluginSourceByNamespace = new Map;
247874
- async readProjectConfig(projectRoot) {
248005
+ async readProjectSettings(projectRoot) {
247875
248006
  const { root, configPath } = await this.findConfigOrThrow(projectRoot);
247876
248007
  const project = await this.readConfigFile(configPath);
247877
248008
  this.assertPluginProjectDoesNotLoadPlugins(project, configPath);
248009
+ return { ...project, root, configPath };
248010
+ }
248011
+ async readProjectConfig(projectRoot) {
248012
+ const project = await this.readProjectSettings(projectRoot);
248013
+ const { configPath } = project;
247878
248014
  const localResources = await this.readProjectResources(configPath, project);
247879
248015
  const pluginResources = await this.readPlugins(project.plugins, configPath);
247880
248016
  const entities = mergeProjectAndPluginEntities(localResources.entities, pluginResources.entities, configPath);
@@ -247884,7 +248020,7 @@ class ProjectConfigReader {
247884
248020
  ];
247885
248021
  this.validateFunctionNames(functions, configPath);
247886
248022
  return {
247887
- project: { ...project, root, configPath },
248023
+ project,
247888
248024
  entities,
247889
248025
  functions,
247890
248026
  agents: localResources.agents,
@@ -248022,6 +248158,10 @@ async function readProjectConfig(projectRoot) {
248022
248158
  const reader = new ProjectConfigReader;
248023
248159
  return await reader.readProjectConfig(projectRoot);
248024
248160
  }
248161
+ async function readProjectSettings(projectRoot) {
248162
+ const reader = new ProjectConfigReader;
248163
+ return await reader.readProjectSettings(projectRoot);
248164
+ }
248025
248165
  // src/core/project/template.ts
248026
248166
  var import_ejs = __toESM(require_ejs(), 1);
248027
248167
  var import_front_matter2 = __toESM(require_front_matter(), 1);
@@ -248034,7 +248174,7 @@ import { join as join12 } from "node:path";
248034
248174
  // package.json
248035
248175
  var package_default = {
248036
248176
  name: "base44",
248037
- version: "0.1.8",
248177
+ version: "0.1.9",
248038
248178
  description: "Base44 CLI - Unified interface for managing Base44 applications",
248039
248179
  type: "module",
248040
248180
  bin: {
@@ -248335,7 +248475,9 @@ async function deploySite(siteOutputDir) {
248335
248475
  if (!await pathExists(siteOutputDir)) {
248336
248476
  throw new InvalidInputError(`Output directory does not exist: ${siteOutputDir}. Make sure to build your project first.`, {
248337
248477
  hints: [
248338
- { message: "Run your build command (e.g., 'npm run build') first" }
248478
+ {
248479
+ message: "Run 'base44 build' first (it injects your app id; a bare 'npm run build' does not)"
248480
+ }
248339
248481
  ]
248340
248482
  });
248341
248483
  }
@@ -248343,7 +248485,9 @@ async function deploySite(siteOutputDir) {
248343
248485
  if (filePaths.length === 0) {
248344
248486
  throw new ConfigInvalidError(`No files found in output directory: ${siteOutputDir}. Make sure to build your project first.`, siteOutputDir, {
248345
248487
  hints: [
248346
- { message: "Run your build command (e.g., 'npm run build') first" }
248488
+ {
248489
+ message: "Run 'base44 build' first (it injects your app id; a bare 'npm run build' does not)"
248490
+ }
248347
248491
  ]
248348
248492
  });
248349
248493
  }
@@ -255586,6 +255730,14 @@ function writeJsonError(error48) {
255586
255730
  envelope.hints = error48.hints;
255587
255731
  }
255588
255732
  }
255733
+ if (error48 instanceof ApiError) {
255734
+ if (error48.statusCode !== undefined) {
255735
+ envelope.statusCode = error48.statusCode;
255736
+ }
255737
+ if (error48.requestId !== undefined) {
255738
+ envelope.requestId = error48.requestId;
255739
+ }
255740
+ }
255589
255741
  process.stdout.write(`${JSON.stringify(envelope)}
255590
255742
  `);
255591
255743
  }
@@ -255682,6 +255834,18 @@ async function confirmPush({
255682
255834
  });
255683
255835
  return !Ct(proceed) && proceed;
255684
255836
  }
255837
+ // src/cli/utils/datetime.ts
255838
+ var import_ms2 = __toESM(require_ms(), 1);
255839
+ var HAS_TIMEZONE_SUFFIX = /Z$|[+-]\d{2}:\d{2}$/;
255840
+ function normalizeDatetime(value) {
255841
+ const duration3 = import_ms2.default(value);
255842
+ if (duration3 !== undefined) {
255843
+ return new Date(Date.now() - duration3).toISOString();
255844
+ }
255845
+ if (HAS_TIMEZONE_SUFFIX.test(value))
255846
+ return value;
255847
+ return `${value}Z`;
255848
+ }
255685
255849
  // src/cli/utils/json.ts
255686
255850
  function toJsonStdout(result) {
255687
255851
  return `${JSON.stringify(result, null, 2)}
@@ -257539,8 +257703,8 @@ function getDeleteCommand() {
257539
257703
  }
257540
257704
 
257541
257705
  // src/cli/commands/functions/formatDeployResult.ts
257542
- function formatDuration(ms) {
257543
- return `${(ms / 1000).toFixed(1)}s`;
257706
+ function formatDuration(ms2) {
257707
+ return `${(ms2 / 1000).toFixed(1)}s`;
257544
257708
  }
257545
257709
  function formatDeployResult(result, log) {
257546
257710
  const label = result.name.padEnd(25);
@@ -257873,7 +258037,11 @@ async function completeProjectSetup({
257873
258037
  const { appUrl } = await runTask2("Installing dependencies...", async (updateMessage) => {
257874
258038
  await execa({ cwd: resolvedPath, shell: true })`${installCommand}`;
257875
258039
  updateMessage("Building project...");
257876
- await execa({ cwd: resolvedPath, shell: true })`${buildCommand}`;
258040
+ await execa({
258041
+ cwd: resolvedPath,
258042
+ shell: true,
258043
+ env: { VITE_BASE44_APP_ID: projectId }
258044
+ })`${buildCommand}`;
257877
258045
  updateMessage("Deploying site...");
257878
258046
  return await deploySite(join25(resolvedPath, outputDirectory));
257879
258047
  }, {
@@ -258353,7 +258521,6 @@ function getLinkCommand() {
258353
258521
  }
258354
258522
 
258355
258523
  // src/cli/commands/project/logs.ts
258356
- var import_ms2 = __toESM(require_ms(), 1);
258357
258524
  function parseFunctionFilters(options) {
258358
258525
  const filters = {};
258359
258526
  if (options.since) {
@@ -258381,15 +258548,6 @@ function parseFunctionNames(option) {
258381
258548
  return [];
258382
258549
  return option.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
258383
258550
  }
258384
- function normalizeDatetime(value) {
258385
- const duration3 = import_ms2.default(value);
258386
- if (duration3 !== undefined) {
258387
- return new Date(Date.now() - duration3).toISOString();
258388
- }
258389
- if (/Z$|[+-]\d{2}:\d{2}$/.test(value))
258390
- return value;
258391
- return `${value}Z`;
258392
- }
258393
258551
  function formatEntry(entry) {
258394
258552
  const time3 = entry.time.substring(0, 19).replace("T", " ");
258395
258553
  const level = entry.level.toUpperCase().padEnd(5);
@@ -258423,9 +258581,73 @@ function writeFollowLine(entry, jsonMode) {
258423
258581
  process.stdout.write(`${line}
258424
258582
  `);
258425
258583
  }
258426
- async function followLogs(functionNames, options, availableFunctionNames, jsonMode) {
258427
- let state = { lastTime: "", boundaryKeys: new Set };
258428
- let first = true;
258584
+ var delay2 = (ms2) => new Promise((resolve9) => setTimeout(resolve9, ms2));
258585
+ function streamEventToLogEntry(event) {
258586
+ return {
258587
+ time: event.time,
258588
+ level: event.level,
258589
+ message: event.function ? `[${event.function}] ${event.message}` : event.message,
258590
+ source: event.function ?? ""
258591
+ };
258592
+ }
258593
+ async function printStreamUntilEnd(stream, levelFilter, jsonMode, startTime) {
258594
+ let lastTime = startTime;
258595
+ let producedEvents = false;
258596
+ try {
258597
+ for await (const event of stream) {
258598
+ producedEvents = true;
258599
+ if (event.kind === "end")
258600
+ return { lastTime, producedEvents, end: event.end };
258601
+ if (levelFilter && event.log.level !== levelFilter)
258602
+ continue;
258603
+ writeFollowLine(streamEventToLogEntry(event.log), jsonMode);
258604
+ if (event.log.time > lastTime)
258605
+ lastTime = event.log.time;
258606
+ }
258607
+ } catch {}
258608
+ return { lastTime, producedEvents, end: null };
258609
+ }
258610
+ var STREAM_RECONNECT_DELAY_MS = 1000;
258611
+ var MAX_DROPS_SINCE_LAST_EVENT = 2;
258612
+ async function streamUntilExhausted(options, jsonMode) {
258613
+ const filters = {
258614
+ functions: parseFunctionNames(options.function),
258615
+ env: options.env
258616
+ };
258617
+ let everConnected = false;
258618
+ let lastTime = "";
258619
+ let dropsSinceLastEvent = 0;
258620
+ while (true) {
258621
+ const stream = await openLogStream(filters);
258622
+ if (!stream)
258623
+ break;
258624
+ everConnected = true;
258625
+ const ending = await printStreamUntilEnd(stream, options.level, jsonMode, lastTime);
258626
+ lastTime = ending.lastTime;
258627
+ if (ending.end) {
258628
+ if (!ending.end.retriable)
258629
+ break;
258630
+ dropsSinceLastEvent = 0;
258631
+ } else {
258632
+ dropsSinceLastEvent = ending.producedEvents ? 1 : dropsSinceLastEvent + 1;
258633
+ if (dropsSinceLastEvent >= MAX_DROPS_SINCE_LAST_EVENT)
258634
+ break;
258635
+ }
258636
+ await delay2(STREAM_RECONNECT_DELAY_MS);
258637
+ }
258638
+ return { everConnected, lastTime };
258639
+ }
258640
+ async function followLogs(functionNames, options, availableFunctionNames, jsonMode, logger2) {
258641
+ const { everConnected, lastTime } = await streamUntilExhausted(options, jsonMode);
258642
+ logger2.warn(everConnected ? "Realtime stream disconnected — falling back to polling (lines may lag ~20-30s)." : "Realtime stream unavailable — falling back to polling (lines may lag ~20-30s).");
258643
+ return pollLogs(functionNames, options, availableFunctionNames, jsonMode, {
258644
+ lastTime,
258645
+ boundaryKeys: new Set
258646
+ });
258647
+ }
258648
+ async function pollLogs(functionNames, options, availableFunctionNames, jsonMode, initialState) {
258649
+ let state = initialState;
258650
+ let first = state.lastTime === "";
258429
258651
  while (true) {
258430
258652
  const pollOptions = first ? options : { ...options, since: state.lastTime };
258431
258653
  const entries = await fetchLogsForFunctions(functionNames, pollOptions, availableFunctionNames);
@@ -258435,7 +258657,7 @@ async function followLogs(functionNames, options, availableFunctionNames, jsonMo
258435
258657
  for (const entry of fresh)
258436
258658
  writeFollowLine(entry, jsonMode);
258437
258659
  first = false;
258438
- await new Promise((resolve9) => setTimeout(resolve9, 2000));
258660
+ await delay2(2000);
258439
258661
  }
258440
258662
  }
258441
258663
  function formatLogs(entries, env3) {
@@ -258521,6 +258743,9 @@ async function logsAction(ctx, options) {
258521
258743
  };
258522
258744
  }
258523
258745
  if (options.follow) {
258746
+ if (options.since) {
258747
+ throw new InvalidInputError("--since cannot be combined with --follow yet (the realtime stream starts from now).");
258748
+ }
258524
258749
  if (options.until) {
258525
258750
  throw new InvalidInputError("--until cannot be combined with --follow (a stream has no end).");
258526
258751
  }
@@ -258528,7 +258753,7 @@ async function logsAction(ctx, options) {
258528
258753
  throw new InvalidInputError("--order cannot be combined with --follow (a live tail always streams oldest to newest).");
258529
258754
  }
258530
258755
  options.order = "asc";
258531
- return followLogs(functionNames, options, availableFunctionNames, ctx.jsonMode);
258756
+ return followLogs(functionNames, options, availableFunctionNames, ctx.jsonMode, ctx.log);
258532
258757
  }
258533
258758
  let entries = await fetchLogsForFunctions(functionNames, options, availableFunctionNames);
258534
258759
  const limit = options.limit ? Number.parseInt(options.limit, 10) : undefined;
@@ -259030,7 +259255,7 @@ async function deployAction2(ctx, options) {
259030
259255
  if (isNonInteractive && !options.yes) {
259031
259256
  throw new InvalidInputError("--yes is required in non-interactive mode");
259032
259257
  }
259033
- const { project: project2 } = await readProjectConfig();
259258
+ const project2 = await readProjectSettings();
259034
259259
  const outputDirectory = project2.site?.outputDirectory;
259035
259260
  if (!outputDirectory) {
259036
259261
  throw new ConfigNotFoundError("No site configuration found.", {
@@ -259267,6 +259492,235 @@ function getTypesCommand() {
259267
259492
  return new Command("types").description("Manage TypeScript type generation").addCommand(getTypesGenerateCommand());
259268
259493
  }
259269
259494
 
259495
+ // src/core/resources/workflow/schema.ts
259496
+ var WorkflowRunStatusSchema = exports_external.enum([
259497
+ "running",
259498
+ "completed",
259499
+ "failed",
259500
+ "cancelled"
259501
+ ]);
259502
+ var WorkflowListItemSchema = exports_external.object({
259503
+ id: exports_external.string(),
259504
+ name: exports_external.string(),
259505
+ description: exports_external.string().nullable().optional(),
259506
+ status: exports_external.string(),
259507
+ status_reason: exports_external.string().nullable().optional(),
259508
+ total_runs: exports_external.number().default(0),
259509
+ consecutive_failures: exports_external.number().default(0),
259510
+ last_run_at: exports_external.string().nullable().optional(),
259511
+ last_run_status: exports_external.string().nullable().optional()
259512
+ }).transform((data) => ({
259513
+ id: data.id,
259514
+ name: data.name,
259515
+ description: data.description ?? null,
259516
+ status: data.status,
259517
+ statusReason: data.status_reason ?? null,
259518
+ totalRuns: data.total_runs,
259519
+ consecutiveFailures: data.consecutive_failures,
259520
+ lastRunAt: data.last_run_at ?? null,
259521
+ lastRunStatus: data.last_run_status ?? null
259522
+ }));
259523
+ var ListWorkflowsResponseSchema = exports_external.array(WorkflowListItemSchema);
259524
+ var WorkflowRunSchema = exports_external.object({
259525
+ run_id: exports_external.string(),
259526
+ workflow_id: exports_external.string(),
259527
+ workflow_name: exports_external.string().default(""),
259528
+ trigger_type: exports_external.string().default(""),
259529
+ status: exports_external.string(),
259530
+ started_at: exports_external.string().nullable().optional(),
259531
+ completed_at: exports_external.string().nullable().optional(),
259532
+ duration_ms: exports_external.number().default(0),
259533
+ steps_count: exports_external.number().default(0),
259534
+ error_message: exports_external.string().nullable().optional(),
259535
+ is_test_run: exports_external.boolean().default(false),
259536
+ status_reason: exports_external.string().default("")
259537
+ }).transform((data) => ({
259538
+ runId: data.run_id,
259539
+ workflowId: data.workflow_id,
259540
+ workflowName: data.workflow_name,
259541
+ triggerType: data.trigger_type,
259542
+ status: data.status,
259543
+ startedAt: data.started_at ?? null,
259544
+ completedAt: data.completed_at ?? null,
259545
+ durationMs: data.duration_ms,
259546
+ stepsCount: data.steps_count,
259547
+ errorMessage: data.error_message ?? null,
259548
+ isTestRun: data.is_test_run,
259549
+ statusReason: data.status_reason
259550
+ }));
259551
+ var ListWorkflowRunsResponseSchema = exports_external.array(WorkflowRunSchema);
259552
+
259553
+ // src/core/resources/workflow/api.ts
259554
+ async function listWorkflows(limit) {
259555
+ const appClient = getAppClient();
259556
+ const searchParams = new URLSearchParams;
259557
+ if (limit !== undefined) {
259558
+ searchParams.set("limit", String(limit));
259559
+ }
259560
+ let response;
259561
+ try {
259562
+ response = await appClient.get("workflows", { searchParams });
259563
+ } catch (error48) {
259564
+ throw await ApiError.fromHttpError(error48, "listing workflows");
259565
+ }
259566
+ const result = ListWorkflowsResponseSchema.safeParse(await response.json());
259567
+ if (!result.success) {
259568
+ throw new SchemaValidationError("Invalid workflows response from server", result.error);
259569
+ }
259570
+ return result.data;
259571
+ }
259572
+ function buildRunsQueryString(filters) {
259573
+ const params = new URLSearchParams;
259574
+ if (filters.status) {
259575
+ params.set("status", filters.status);
259576
+ }
259577
+ if (filters.since) {
259578
+ params.set("since", filters.since);
259579
+ }
259580
+ if (filters.limit !== undefined) {
259581
+ params.set("limit", String(filters.limit));
259582
+ }
259583
+ return params;
259584
+ }
259585
+ async function listWorkflowRuns(filters = {}) {
259586
+ const appClient = getAppClient();
259587
+ let response;
259588
+ try {
259589
+ response = await appClient.get("workflows/runs", {
259590
+ searchParams: buildRunsQueryString(filters)
259591
+ });
259592
+ } catch (error48) {
259593
+ throw await ApiError.fromHttpError(error48, "listing workflow runs");
259594
+ }
259595
+ const result = ListWorkflowRunsResponseSchema.safeParse(await response.json());
259596
+ if (!result.success) {
259597
+ throw new SchemaValidationError("Invalid workflow runs response from server", result.error);
259598
+ }
259599
+ return result.data;
259600
+ }
259601
+ // src/cli/commands/workflows/legacy-app.ts
259602
+ var isWorkflowsNotEnabled = (error48) => error48 instanceof ApiError && error48.statusCode === 403;
259603
+ function rethrowLegacyAppAsExplanation(error48) {
259604
+ if (isWorkflowsNotEnabled(error48)) {
259605
+ throw new InvalidInputError("Workflows are not enabled for this app — it predates the Workflows system, so its automation runs are not readable via this command.");
259606
+ }
259607
+ throw error48;
259608
+ }
259609
+
259610
+ // src/cli/commands/workflows/limit.ts
259611
+ var MAX_LIMIT = 200;
259612
+ function parseLimit(limit) {
259613
+ if (limit === undefined)
259614
+ return;
259615
+ const parsed = Number.parseInt(limit, 10);
259616
+ if (Number.isNaN(parsed) || parsed < 1 || parsed > MAX_LIMIT) {
259617
+ throw new InvalidInputError(`Invalid limit: "${limit}". Must be a number between 1 and ${MAX_LIMIT}.`);
259618
+ }
259619
+ return parsed;
259620
+ }
259621
+
259622
+ // src/cli/commands/workflows/list.ts
259623
+ function formatWorkflowLine(workflow) {
259624
+ const lastRun = workflow.lastRunAt ? `last run ${workflow.lastRunStatus ?? "unknown"} at ${workflow.lastRunAt}` : "never ran";
259625
+ const failures = workflow.consecutiveFailures > 0 ? ` (${workflow.consecutiveFailures} consecutive failures)` : "";
259626
+ return `${workflow.name} [${workflow.status}] runs: ${workflow.totalRuns}, ${lastRun}${failures}`;
259627
+ }
259628
+ async function listAction({ log, runTask: runTask2, jsonMode }, options8) {
259629
+ const limit = parseLimit(options8.limit);
259630
+ const workflows = await runTask2("Fetching workflows from Base44", async () => listWorkflows(limit).catch(rethrowLegacyAppAsExplanation), {
259631
+ successMessage: "Workflows fetched successfully",
259632
+ errorMessage: "Failed to fetch workflows"
259633
+ });
259634
+ if (jsonMode) {
259635
+ return {
259636
+ outroMessage: `Found ${workflows.length} workflows.`,
259637
+ stdout: `${JSON.stringify({ workflows }, null, 2)}
259638
+ `
259639
+ };
259640
+ }
259641
+ if (workflows.length === 0) {
259642
+ return { outroMessage: "This app has no workflows." };
259643
+ }
259644
+ for (const workflow of workflows) {
259645
+ log.info(formatWorkflowLine(workflow));
259646
+ }
259647
+ return { outroMessage: `Found ${workflows.length} workflows.` };
259648
+ }
259649
+ function getWorkflowsListCommand() {
259650
+ return new Base44Command("list").description("List this app's workflows").option("-n, --limit <n>", `Number of workflows to return (1-${MAX_LIMIT}, default: 30)`).action(listAction);
259651
+ }
259652
+
259653
+ // src/cli/commands/workflows/runs.ts
259654
+ function formatDuration2(durationMs) {
259655
+ if (durationMs === 0)
259656
+ return "";
259657
+ return ` ${(durationMs / 1000).toFixed(1)}s`;
259658
+ }
259659
+ function formatRunTags(run) {
259660
+ const tags = [run.triggerType, run.isTestRun ? "test" : null].filter(Boolean);
259661
+ return tags.length > 0 ? ` (${tags.join(", ")})` : "";
259662
+ }
259663
+ function formatFailureDetail(run) {
259664
+ const reason = run.statusReason ? ` [${run.statusReason}]` : "";
259665
+ const message = run.errorMessage ? `
259666
+ ${run.errorMessage}` : "";
259667
+ return `${reason}${message}`;
259668
+ }
259669
+ function formatRunLine(run) {
259670
+ const time3 = (run.startedAt ?? "").substring(0, 19).replace("T", " ");
259671
+ const status = run.status.toUpperCase().padEnd(9);
259672
+ const base = `${time3} ${status} ${run.workflowName}${formatRunTags(run)}${formatDuration2(run.durationMs)}`;
259673
+ const failureDetail = run.status === "failed" || run.status === "cancelled" ? formatFailureDetail(run) : "";
259674
+ return `${base}${failureDetail}`;
259675
+ }
259676
+ async function describeEmptyResult(hasFilters) {
259677
+ const workflows = await listWorkflows().catch(() => null);
259678
+ if (workflows === null) {
259679
+ return "No runs found.";
259680
+ }
259681
+ if (workflows.length === 0) {
259682
+ return "This app has no workflows, so there are no runs.";
259683
+ }
259684
+ const names = workflows.map((workflow) => workflow.name).join(", ");
259685
+ return hasFilters ? `No runs match your filters. Workflows in this app: ${names}.` : `No runs yet. Workflows in this app: ${names}.`;
259686
+ }
259687
+ async function runsAction(ctx, options8) {
259688
+ const limit = parseLimit(options8.limit);
259689
+ const runs = await ctx.runTask("Fetching workflow runs from Base44", async () => listWorkflowRuns({
259690
+ status: options8.status,
259691
+ since: options8.since,
259692
+ limit
259693
+ }).catch(rethrowLegacyAppAsExplanation), {
259694
+ successMessage: "Workflow runs fetched successfully",
259695
+ errorMessage: "Failed to fetch workflow runs"
259696
+ });
259697
+ if (ctx.jsonMode) {
259698
+ return {
259699
+ outroMessage: `Found ${runs.length} runs.`,
259700
+ stdout: `${JSON.stringify({ runs }, null, 2)}
259701
+ `
259702
+ };
259703
+ }
259704
+ if (runs.length === 0) {
259705
+ const hasFilters = Boolean(options8.status || options8.since);
259706
+ return { outroMessage: await describeEmptyResult(hasFilters) };
259707
+ }
259708
+ for (const run of runs) {
259709
+ ctx.log.info(formatRunLine(run));
259710
+ }
259711
+ return { outroMessage: `Found ${runs.length} runs.` };
259712
+ }
259713
+ function getWorkflowsRunsCommand() {
259714
+ return new Base44Command("runs").description("List workflow runs for this app, newest first (includes scheduled, manual, and test runs)").addOption(new Option("--status <status>", "Filter by run status").choices([
259715
+ ...WorkflowRunStatusSchema.options
259716
+ ])).option("--since <datetime>", "Show runs started after this time. ISO datetime or relative shorthand (e.g. 1h, 30m, 2d)", normalizeDatetime).option("-n, --limit <n>", `Number of runs to return (1-${MAX_LIMIT}, default: 30)`).action(runsAction);
259717
+ }
259718
+
259719
+ // src/cli/commands/workflows/index.ts
259720
+ function getWorkflowsCommand() {
259721
+ return new Command("workflows").description("Inspect this app's workflows and their runs").addCommand(getWorkflowsListCommand()).addCommand(getWorkflowsRunsCommand());
259722
+ }
259723
+
259270
259724
  // src/cli/commands/workspace/shared.ts
259271
259725
  function workspaceTag(workspace2) {
259272
259726
  const role = workspace2.userRole ?? "member";
@@ -260261,7 +260715,8 @@ var fieldTypes = [
260261
260715
  "number",
260262
260716
  "boolean",
260263
260717
  "array",
260264
- "object"
260718
+ "object",
260719
+ "null"
260265
260720
  ];
260266
260721
 
260267
260722
  class Validator {
@@ -260321,6 +260776,14 @@ class Validator {
260321
260776
  if (!property) {
260322
260777
  return { hasError: false };
260323
260778
  }
260779
+ if (Array.isArray(property.type)) {
260780
+ const types = property.type;
260781
+ const matched = types.some((type) => !this.validateValue(value, { ...property, type }, fieldPath).hasError);
260782
+ return matched ? { hasError: false } : {
260783
+ hasError: true,
260784
+ error: this.createValidationError(`Error in field ${fieldPath}: Input should be a valid ${types.join(" or ")}`)
260785
+ };
260786
+ }
260324
260787
  const propertyType = property.type;
260325
260788
  if (!fieldTypes.includes(propertyType)) {
260326
260789
  return {
@@ -260361,6 +260824,14 @@ class Validator {
260361
260824
  }
260362
260825
  }
260363
260826
  break;
260827
+ case "null":
260828
+ if (value !== null) {
260829
+ return {
260830
+ hasError: true,
260831
+ error: this.createValidationError(`Error in field ${fieldPath}: Input should be null`)
260832
+ };
260833
+ }
260834
+ break;
260364
260835
  case "integer":
260365
260836
  if (!Number.isInteger(value)) {
260366
260837
  return {
@@ -260780,7 +261251,9 @@ function evaluateCondition(condition, record2, user) {
260780
261251
  function checkRLS(rule, record2, user) {
260781
261252
  if (user?.is_service === true)
260782
261253
  return true;
260783
- if (rule === undefined)
261254
+ if (rule === undefined || rule === null || rule === "")
261255
+ return true;
261256
+ if (typeof rule === "object" && Object.keys(rule).length === 0)
260784
261257
  return true;
260785
261258
  if (typeof rule === "boolean")
260786
261259
  return rule;
@@ -263331,6 +263804,10 @@ function validateDevOptions(command2) {
263331
263804
  if (appId !== undefined) {
263332
263805
  command2.error(`base44 dev cannot be used with --app-id or ${BASE44_APP_ID_ENV_VAR}.`);
263333
263806
  }
263807
+ const { port, remote } = command2.opts();
263808
+ if (remote && port !== undefined) {
263809
+ command2.error("--port applies to the local backend, which --remote does not start.");
263810
+ }
263334
263811
  }
263335
263812
  function requireLinkedProject({ app }) {
263336
263813
  if (!app?.projectRoot) {
@@ -263338,31 +263815,44 @@ function requireLinkedProject({ app }) {
263338
263815
  }
263339
263816
  return { id: app.id, projectRoot: app.projectRoot };
263340
263817
  }
263341
- async function createConfiguredServeRunner(app, backendUrl) {
263818
+ async function resolveConfiguredSite(app) {
263342
263819
  const { project: project2 } = await readProjectConfig(app.projectRoot);
263343
263820
  const serveCommand = project2.site?.serveCommand;
263344
- if (!serveCommand) {
263345
- return;
263346
- }
263347
- return createServeCommandRunner({
263348
- serveCommand,
263349
- projectRoot: project2.root,
263350
- appId: app.id,
263351
- appBaseUrl: backendUrl
263352
- });
263821
+ return serveCommand ? { serveCommand, projectRoot: project2.root } : undefined;
263353
263822
  }
263354
- function startServeCommand(runner, backend) {
263823
+ function stopRunnerOnProcessSignals(runner) {
263355
263824
  const stop2 = () => void runner.stop();
263356
263825
  process.on("SIGINT", stop2);
263357
263826
  process.on("SIGTERM", stop2);
263827
+ }
263828
+ function startServeCommand(runner, backend) {
263829
+ stopRunnerOnProcessSignals(runner);
263358
263830
  runner.onExit(() => {
263359
263831
  backend.shutdown().finally(() => process.exit(1));
263360
263832
  });
263361
263833
  createDevLogger("backend", theme.styles.info).log(`Backend running on ${backend.url}`);
263362
263834
  runner.start();
263363
263835
  }
263364
- async function devAction(ctx, options8) {
263365
- const app = requireLinkedProject(ctx);
263836
+ async function remoteDevAction(app) {
263837
+ const site2 = await resolveConfiguredSite(app);
263838
+ if (!site2) {
263839
+ throw new ConfigInvalidError("base44 dev --remote serves the frontend against the production backend, but this project has no site.serveCommand in base44/config.jsonc.");
263840
+ }
263841
+ const appBaseUrl = await getSiteUrl();
263842
+ const runner = createServeCommandRunner({
263843
+ ...site2,
263844
+ appId: app.id,
263845
+ appBaseUrl
263846
+ });
263847
+ stopRunnerOnProcessSignals(runner);
263848
+ runner.onExit((code2) => process.exit(code2 ?? 1));
263849
+ runner.start();
263850
+ return {
263851
+ outroMessage: `Frontend dev server targets ${theme.styles.bold(appBaseUrl)} — every write hits your live app`
263852
+ };
263853
+ }
263854
+ async function localDevAction(ctx, app, options8) {
263855
+ const site2 = await resolveConfiguredSite(app);
263366
263856
  const siteUrlPromise = getSiteUrl().catch(() => {
263367
263857
  return;
263368
263858
  });
@@ -263377,15 +263867,23 @@ async function devAction(ctx, options8) {
263377
263867
  }
263378
263868
  });
263379
263869
  const backendUrl = localServerUrl(backend.port);
263380
- const runner = await createConfiguredServeRunner(app, backendUrl);
263381
- if (runner) {
263870
+ if (site2) {
263871
+ const runner = createServeCommandRunner({
263872
+ ...site2,
263873
+ appId: app.id,
263874
+ appBaseUrl: backendUrl
263875
+ });
263382
263876
  startServeCommand(runner, { url: backendUrl, shutdown: backend.shutdown });
263383
263877
  }
263384
- const outroMessage = runner ? "Open your app using the frontend dev server URL" : `Dev server is available at ${theme.colors.links(backendUrl)}`;
263878
+ const outroMessage = site2 ? "Open your app using the frontend dev server URL" : `Dev server is available at ${theme.colors.links(backendUrl)}`;
263385
263879
  return { outroMessage };
263386
263880
  }
263881
+ async function devAction(ctx, options8) {
263882
+ const app = requireLinkedProject(ctx);
263883
+ return options8.remote ? remoteDevAction(app) : localDevAction(ctx, app, options8);
263884
+ }
263387
263885
  function getDevCommand() {
263388
- return new Base44Command("dev").description("Start the development server").option("-p, --port <number>", "Port for the development server").hook("preAction", validateDevOptions).action(devAction);
263886
+ return new Base44Command("dev").description("Start the development server").option("-p, --port <number>", "Port for the development server").option("--remote", "Serve the frontend against the production backend instead of a local one").hook("preAction", validateDevOptions).action(devAction);
263389
263887
  }
263390
263888
 
263391
263889
  // src/core/exec/run-script.ts
@@ -263648,6 +264146,7 @@ function createProgram(context) {
263648
264146
  program2.addCommand(getAgentSkillsCommand());
263649
264147
  program2.addCommand(getConnectorsCommand());
263650
264148
  program2.addCommand(getFunctionsCommand());
264149
+ program2.addCommand(getWorkflowsCommand());
263651
264150
  program2.addCommand(getSecretsCommand());
263652
264151
  program2.addCommand(getSandboxCommand());
263653
264152
  program2.addCommand(getAuthCommand());
@@ -267903,4 +268402,4 @@ export {
267903
268402
  CLIExitError
267904
268403
  };
267905
268404
 
267906
- //# debugId=D124AF9A61B7AEE264756E2164756E21
268405
+ //# debugId=844375621C10BF7464756E2164756E21