Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
- App visibility: `base44 visibility <public|private|workspace>` sets it on the server directly (accepts `--app-id` to target any app). Also configurable via `"visibility"` in `config.jsonc`, which `base44 deploy` applies. New projects scaffold `"visibility": "public"`.
- `base44 build` runs the site's `buildCommand` with `VITE_BASE44_APP_ID` injected, so built bundles always carry the linked app's id.
- `base44 deploy` (and `base44 site deploy`) can now build first: interactive runs ask, and `--build` / `--no-build` pre-answer the prompt.
- `base44 dev --remote` serves the frontend against the production backend: it runs `site.serveCommand` with `VITE_BASE44_APP_ID` and `VITE_BASE44_APP_BASE_URL` pointing at the app's own published URL, without starting the local backend. Fails if the app has no published URL.
- `base44 dev --remote` serves the frontend against the production backend: it runs `site.serveCommand` with `VITE_BASE44_APP_ID` and `VITE_BASE44_APP_BASE_URL` pointing at the app's own published URL, without starting the local backend. An app that was never published is served through its preview URL (`https://preview--<appId>.base44.app`), as `base44 exec` does.

### Changed

Expand Down
3 changes: 2 additions & 1 deletion packages/cli/src/cli/commands/dev.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { getDenoWrapperPath } from "@/core/assets.js";
import { BASE44_APP_ID_ENV_VAR } from "@/core/consts.js";
import { ConfigInvalidError } from "@/core/errors.js";
import { getSiteUrl } from "@/core/project/api.js";
import { resolveAppBaseUrl } from "@/core/project/app-base-url.js";
import { readProjectConfig } from "@/core/project/config.js";

interface DevOptions {
Expand Down Expand Up @@ -91,7 +92,7 @@ async function remoteDevAction(app: LinkedApp): Promise<RunCommandResult> {
);
}

const appBaseUrl = await getSiteUrl();
const appBaseUrl = await resolveAppBaseUrl(app.id);
const runner = createServeCommandRunner({
...site,
appId: app.id,
Expand Down
17 changes: 2 additions & 15 deletions packages/cli/src/core/exec/run-script.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@ import { readFileSync, writeFileSync } from "node:fs";
import { join } from "node:path";
import { dir, file } from "tmp-promise";
import { getExecWrapperPath } from "@/core/assets.js";
import { ApiError } from "@/core/errors.js";
import {
DATA_ENV_ENV_VAR,
EXEC_ENV_VARS,
PRIVILEGED_ENV_VAR,
} from "@/core/exec/env-target.js";
import { getAppUserToken, getSiteUrl } from "@/core/project/api.js";
import { getAppUserToken } from "@/core/project/api.js";
import { resolveAppBaseUrl } from "@/core/project/app-base-url.js";
import { verifyDenoInstalled } from "@/core/utils/index.js";

interface RunScriptOptions {
Expand Down Expand Up @@ -58,19 +58,6 @@ function inheritedEnv(): NodeJS.ProcessEnv {
return env;
}

const appPreviewUrl = (appId: string) => `https://preview--${appId}.base44.app`;

async function resolveAppBaseUrl(appId: string) {
try {
return await getSiteUrl();
} catch (error) {
if (error instanceof ApiError && error.statusCode === 404) {
return appPreviewUrl(appId);
}
throw error;
}
}

interface RunScriptResult {
exitCode: number;
}
Expand Down
16 changes: 16 additions & 0 deletions packages/cli/src/core/project/app-base-url.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { ApiError } from "@/core/errors.js";
import { getSiteUrl } from "@/core/project/api.js";

const appPreviewUrl = (appId: string) => `https://preview--${appId}.base44.app`;

const isAppNotPublished = (error: unknown) =>
error instanceof ApiError && error.statusCode === 404;

export async function resolveAppBaseUrl(appId: string) {
try {
return await getSiteUrl();
} catch (error) {
if (isAppNotPublished(error)) return appPreviewUrl(appId);
throw error;
}
}
14 changes: 13 additions & 1 deletion packages/cli/tests/cli/dev.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,10 +116,22 @@ describe("dev command", () => {
expect(output).not.toContain("Backend running on");
});

it("--remote fails when the app has no published URL", async () => {
it("--remote serves an unpublished app through its preview URL", async () => {
await t.givenLoggedInWithProject(fixture("with-serve-command"));
t.api.mockSiteUrlError({ status: 404, body: { detail: "App not found" } });

const handle = await t.runLive("dev", "--remote");
await handle.waitForOutput(/SERVE_APP=/);
await handle.stop();

const output = handle.stdout.join("");
expect(output).toContain(`URL=https://preview--${t.api.appId}.base44.app`);
});

it("--remote fails when the site URL lookup fails for another reason", async () => {
await t.givenLoggedInWithProject(fixture("with-serve-command"));
t.api.mockSiteUrlError({ status: 500, body: { detail: "Server error" } });

const result = await t.run("dev", "--remote");

t.expectResult(result).toFail();
Expand Down
Loading