@base44-preview/cli 0.1.11-pr.602.81713bc → 0.1.12-pr.595.1473dd3

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
@@ -247752,6 +247752,141 @@ var functionResource = {
247752
247752
  readAll: readAllFunctions,
247753
247753
  push: (functions) => deployFunctionsSequentially(functions)
247754
247754
  };
247755
+ // src/core/resources/function/stream-api.ts
247756
+ var StreamLogEventSchema = exports_external.object({
247757
+ time: exports_external.string(),
247758
+ level: exports_external.preprocess((value) => value === "warn" ? "warning" : value, LogLevelSchema),
247759
+ function: exports_external.string().nullable(),
247760
+ message: exports_external.string()
247761
+ });
247762
+ var StreamEndEventSchema = exports_external.object({
247763
+ reason: exports_external.string(),
247764
+ retriable: exports_external.boolean()
247765
+ });
247766
+ function buildStreamUrl(filters) {
247767
+ const { id } = getAppContext();
247768
+ const url2 = new URL(`/api/apps/${id}/functions-mgmt/logs/stream`, getBase44ApiUrl());
247769
+ if (filters.functions?.length) {
247770
+ url2.searchParams.set("function", filters.functions.join(","));
247771
+ }
247772
+ if (filters.env) {
247773
+ url2.searchParams.set("env", filters.env);
247774
+ }
247775
+ return url2.href;
247776
+ }
247777
+ async function buildStreamAuthHeaders() {
247778
+ const workspaceApiKey = getWorkspaceApiKeyFromEnv();
247779
+ if (workspaceApiKey && isWorkspaceApiKey(workspaceApiKey)) {
247780
+ return { api_key: workspaceApiKey };
247781
+ }
247782
+ const auth = await readAuth();
247783
+ if (isTokenExpired(auth)) {
247784
+ const refreshedToken = await refreshAndSaveTokens();
247785
+ if (refreshedToken) {
247786
+ return { Authorization: `Bearer ${refreshedToken}` };
247787
+ }
247788
+ }
247789
+ return { Authorization: `Bearer ${auth.accessToken}` };
247790
+ }
247791
+ function parseStreamEvent(eventName, data) {
247792
+ try {
247793
+ const payload = JSON.parse(data);
247794
+ if (eventName === "end") {
247795
+ const result = StreamEndEventSchema.safeParse(payload);
247796
+ return result.success ? { kind: "end", end: result.data } : null;
247797
+ }
247798
+ if (eventName === "") {
247799
+ const result = StreamLogEventSchema.safeParse(payload);
247800
+ return result.success ? { kind: "log", log: result.data } : null;
247801
+ }
247802
+ return null;
247803
+ } catch {
247804
+ return null;
247805
+ }
247806
+ }
247807
+ var STREAM_SILENCE_TIMEOUT_MS = 60000;
247808
+ async function readOrSilence(reader) {
247809
+ let timer;
247810
+ const silence = new Promise((resolve3) => {
247811
+ timer = setTimeout(() => resolve3("silence"), STREAM_SILENCE_TIMEOUT_MS);
247812
+ });
247813
+ try {
247814
+ return await Promise.race([reader.read(), silence]);
247815
+ } finally {
247816
+ clearTimeout(timer);
247817
+ }
247818
+ }
247819
+ async function* readLines(body) {
247820
+ const reader = body.getReader();
247821
+ const decoder = new TextDecoder;
247822
+ let buffered = "";
247823
+ try {
247824
+ while (true) {
247825
+ const result = await readOrSilence(reader);
247826
+ if (result === "silence") {
247827
+ await reader.cancel();
247828
+ return;
247829
+ }
247830
+ if (result.done || !result.value)
247831
+ return;
247832
+ buffered += decoder.decode(result.value, { stream: true });
247833
+ const lines = buffered.split(`
247834
+ `);
247835
+ buffered = lines.pop() ?? "";
247836
+ yield* lines;
247837
+ }
247838
+ } finally {
247839
+ reader.releaseLock();
247840
+ }
247841
+ }
247842
+ async function* readStreamEvents(body) {
247843
+ let eventName = "";
247844
+ for await (const line of readLines(body)) {
247845
+ if (line.startsWith(":")) {
247846
+ yield { kind: "ping" };
247847
+ continue;
247848
+ }
247849
+ if (line.startsWith("event:")) {
247850
+ eventName = line.slice(6).trim();
247851
+ continue;
247852
+ }
247853
+ if (line.startsWith("data:")) {
247854
+ const event = parseStreamEvent(eventName, line.slice(5));
247855
+ eventName = "";
247856
+ if (event)
247857
+ yield event;
247858
+ continue;
247859
+ }
247860
+ if (line.trim() === "")
247861
+ eventName = "";
247862
+ }
247863
+ }
247864
+ var STREAM_CONNECT_TIMEOUT_MS = 1e4;
247865
+ var isWorthReconnecting = (status) => status >= 500;
247866
+ async function openLogStream(filters) {
247867
+ const connectPhase = new AbortController;
247868
+ const connectTimer = setTimeout(() => connectPhase.abort(), STREAM_CONNECT_TIMEOUT_MS);
247869
+ let response;
247870
+ try {
247871
+ response = await fetch(buildStreamUrl(filters), {
247872
+ headers: {
247873
+ Accept: "text/event-stream",
247874
+ ...await buildStreamAuthHeaders()
247875
+ },
247876
+ signal: connectPhase.signal
247877
+ });
247878
+ } catch {
247879
+ return { kind: "transient" };
247880
+ } finally {
247881
+ clearTimeout(connectTimer);
247882
+ }
247883
+ if (!response.ok) {
247884
+ return isWorthReconnecting(response.status) ? { kind: "transient" } : { kind: "refused" };
247885
+ }
247886
+ if (!response.body)
247887
+ return { kind: "transient" };
247888
+ return { kind: "stream", events: readStreamEvents(response.body) };
247889
+ }
247755
247890
  // src/core/project/config.ts
247756
247891
  class ProjectConfigReader {
247757
247892
  pluginSourceByNamespace = new Map;
@@ -247927,7 +248062,7 @@ import { join as join12 } from "node:path";
247927
248062
  // package.json
247928
248063
  var package_default = {
247929
248064
  name: "base44",
247930
- version: "0.1.11",
248065
+ version: "0.1.12",
247931
248066
  description: "Base44 CLI - Unified interface for managing Base44 applications",
247932
248067
  type: "module",
247933
248068
  bin: {
@@ -258338,9 +258473,88 @@ function writeFollowLine(entry, jsonMode) {
258338
258473
  process.stdout.write(`${line}
258339
258474
  `);
258340
258475
  }
258341
- async function followLogs(functionNames, options, availableFunctionNames, jsonMode) {
258342
- let state = { lastTime: "", boundaryKeys: new Set };
258343
- let first = true;
258476
+ var delay2 = (ms2) => new Promise((resolve9) => setTimeout(resolve9, ms2));
258477
+ function streamEventToLogEntry(event) {
258478
+ return {
258479
+ time: event.time,
258480
+ level: event.level,
258481
+ message: event.function ? `[${event.function}] ${event.message}` : event.message,
258482
+ source: event.function ?? ""
258483
+ };
258484
+ }
258485
+ async function printStreamUntilEnd(stream, levelFilter, jsonMode, startTime) {
258486
+ let lastTime = startTime;
258487
+ let provedAlive = false;
258488
+ try {
258489
+ for await (const event of stream) {
258490
+ provedAlive = true;
258491
+ if (event.kind === "end")
258492
+ return { lastTime, provedAlive, end: event.end };
258493
+ if (event.kind === "ping")
258494
+ continue;
258495
+ if (levelFilter && event.log.level !== levelFilter)
258496
+ continue;
258497
+ writeFollowLine(streamEventToLogEntry(event.log), jsonMode);
258498
+ if (event.log.time > lastTime)
258499
+ lastTime = event.log.time;
258500
+ }
258501
+ } catch {}
258502
+ return { lastTime, provedAlive, end: null };
258503
+ }
258504
+ var STREAM_RECONNECT_DELAY_MS = 1000;
258505
+ var MAX_DROPS_SINCE_LAST_EVENT = 2;
258506
+ var CONNECT_RETRY_DELAYS_MS = [1000, 2000, 4000, 8000];
258507
+ var countDropTowardGivingUp = (dropsSinceLastEvent, provedAlive) => provedAlive ? 1 : dropsSinceLastEvent + 1;
258508
+ var shouldGiveUpStreaming = (dropsSinceLastEvent) => dropsSinceLastEvent >= MAX_DROPS_SINCE_LAST_EVENT;
258509
+ async function connectWhileTransientlyUnavailable(filters) {
258510
+ let attempt = await openLogStream(filters);
258511
+ for (const retryDelay of CONNECT_RETRY_DELAYS_MS) {
258512
+ if (attempt.kind !== "transient")
258513
+ return attempt;
258514
+ await delay2(retryDelay);
258515
+ attempt = await openLogStream(filters);
258516
+ }
258517
+ return attempt;
258518
+ }
258519
+ async function streamUntilExhausted(options, jsonMode) {
258520
+ const filters = {
258521
+ functions: parseFunctionNames(options.function),
258522
+ env: options.env
258523
+ };
258524
+ let everConnected = false;
258525
+ let lastTime = "";
258526
+ let dropsSinceLastEvent = 0;
258527
+ while (true) {
258528
+ const attempt = await connectWhileTransientlyUnavailable(filters);
258529
+ if (attempt.kind !== "stream")
258530
+ break;
258531
+ everConnected = true;
258532
+ const ending = await printStreamUntilEnd(attempt.events, options.level, jsonMode, lastTime);
258533
+ lastTime = ending.lastTime;
258534
+ if (ending.end) {
258535
+ if (!ending.end.retriable)
258536
+ break;
258537
+ dropsSinceLastEvent = 0;
258538
+ } else {
258539
+ dropsSinceLastEvent = countDropTowardGivingUp(dropsSinceLastEvent, ending.provedAlive);
258540
+ if (shouldGiveUpStreaming(dropsSinceLastEvent))
258541
+ break;
258542
+ }
258543
+ await delay2(STREAM_RECONNECT_DELAY_MS);
258544
+ }
258545
+ return { everConnected, lastTime };
258546
+ }
258547
+ async function followLogs(functionNames, options, availableFunctionNames, jsonMode, logger2) {
258548
+ const { everConnected, lastTime } = await streamUntilExhausted(options, jsonMode);
258549
+ 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).");
258550
+ return pollLogs(functionNames, options, availableFunctionNames, jsonMode, {
258551
+ lastTime,
258552
+ boundaryKeys: new Set
258553
+ });
258554
+ }
258555
+ async function pollLogs(functionNames, options, availableFunctionNames, jsonMode, initialState) {
258556
+ let state = initialState;
258557
+ let first = state.lastTime === "";
258344
258558
  while (true) {
258345
258559
  const pollOptions = first ? options : { ...options, since: state.lastTime };
258346
258560
  const entries = await fetchLogsForFunctions(functionNames, pollOptions, availableFunctionNames);
@@ -258350,7 +258564,7 @@ async function followLogs(functionNames, options, availableFunctionNames, jsonMo
258350
258564
  for (const entry of fresh)
258351
258565
  writeFollowLine(entry, jsonMode);
258352
258566
  first = false;
258353
- await new Promise((resolve9) => setTimeout(resolve9, 2000));
258567
+ await delay2(2000);
258354
258568
  }
258355
258569
  }
258356
258570
  function formatLogs(entries, env3) {
@@ -258442,6 +258656,9 @@ async function logsAction(ctx, options) {
258442
258656
  return { outroMessage: "No functions found in this app." };
258443
258657
  }
258444
258658
  if (options.follow) {
258659
+ if (options.since) {
258660
+ throw new InvalidInputError("--since cannot be combined with --follow yet (the realtime stream starts from now).");
258661
+ }
258445
258662
  if (options.until) {
258446
258663
  throw new InvalidInputError("--until cannot be combined with --follow (a stream has no end).");
258447
258664
  }
@@ -258449,7 +258666,7 @@ async function logsAction(ctx, options) {
258449
258666
  throw new InvalidInputError("--order cannot be combined with --follow (a live tail always streams oldest to newest).");
258450
258667
  }
258451
258668
  options.order = "asc";
258452
- return followLogs(functionNames, options, availableFunctionNames, ctx.jsonMode);
258669
+ return followLogs(functionNames, options, availableFunctionNames, ctx.jsonMode, ctx.log);
258453
258670
  }
258454
258671
  let entries = await fetchLogsForFunctions(functionNames, options, availableFunctionNames);
258455
258672
  const limit = options.limit ? Number.parseInt(options.limit, 10) : undefined;
@@ -258468,7 +258685,7 @@ async function logsAction(ctx, options) {
258468
258685
  function getLogsCommand() {
258469
258686
  return new Base44Command("logs").description("Fetch function logs for this app").option("--function <names>", "Filter by function name(s), comma-separated. If omitted, fetches logs for all deployed functions").option("--since <datetime>", "Show logs from this time. ISO datetime or relative shorthand (e.g. 1h, 30m, 2d)", normalizeDatetime).option("--until <datetime>", "Show logs until this time. ISO datetime or relative shorthand (e.g. 1h, 30m, 2d)", normalizeDatetime).addOption(new Option("--level <level>", "Filter by log level").choices([
258470
258687
  ...LogLevelSchema.options
258471
- ])).option("-n, --limit <n>", "Results per page (1-1000, default: 50)").option("-f, --follow", "Stream new logs as they arrive").addOption(new Option("--order <order>", "Sort order").choices(["asc", "desc"])).addOption(new Option("--env <env>", "Which deployment to read logs from: preview (current draft) or prod (published). Default: preview").choices([...LogEnvSchema.options])).action(logsAction);
258688
+ ])).option("-n, --limit <n>", "Results per page (1-1000; the server returns at most 500)").option("-f, --follow", "Stream new logs as they arrive").addOption(new Option("--order <order>", "Sort order").choices(["asc", "desc"])).addOption(new Option("--env <env>", "Which deployment to read logs from: preview (current draft) or prod (published). Default: preview").choices([...LogEnvSchema.options])).action(logsAction);
258472
258689
  }
258473
258690
 
258474
258691
  // src/cli/commands/project/scaffold.ts
@@ -268098,4 +268315,4 @@ export {
268098
268315
  runCLI
268099
268316
  };
268100
268317
 
268101
- //# debugId=A79A364B5E1F19E564756E2164756E21
268318
+ //# debugId=53DC5DFCAE82DF0D64756E2164756E21