diff --git a/CHANGELOG.md b/CHANGELOG.md index 5f871affa..3198b1560 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,10 @@ - App visibility: `base44 visibility ` 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"`. +### Changed + +- The `backend-and-client` template now scaffolds the same client convention editor-created apps use: `@base44/vite-plugin` + `src/lib/app-params.js`, with the SDK client on same-origin `/api` (`serverUrl: ''`). Under `base44 dev` the plugin proxies `/api` to the local dev backend, so scaffolded apps get local entities and functions; the app id resolves from env / `base44/.app.jsonc` instead of being baked into source. + ### Fixed - `base44 link` now lists editor-created apps; previously only apps created by the CLI could be linked. diff --git a/packages/cli/templates/backend-and-client/package.json b/packages/cli/templates/backend-and-client/package.json index 91b53dfb8..ef34e2bb5 100644 --- a/packages/cli/templates/backend-and-client/package.json +++ b/packages/cli/templates/backend-and-client/package.json @@ -9,7 +9,8 @@ "preview": "vite preview" }, "dependencies": { - "@base44/sdk": "^0.8.3", + "@base44/sdk": "^0.8.40", + "@base44/vite-plugin": "^1.0.30", "lucide-react": "^0.475.0", "react": "^18.2.0", "react-dom": "^18.2.0" diff --git a/packages/cli/templates/backend-and-client/src/api/base44Client.js b/packages/cli/templates/backend-and-client/src/api/base44Client.js new file mode 100644 index 000000000..302d606a4 --- /dev/null +++ b/packages/cli/templates/backend-and-client/src/api/base44Client.js @@ -0,0 +1,14 @@ +import { createClient } from '@base44/sdk'; +import { appParams } from '@/lib/app-params'; + +const { appId, token, functionsVersion, appBaseUrl } = appParams; + +//Create a client with authentication required +export const base44 = createClient({ + appId, + token, + functionsVersion, + serverUrl: '', + requiresAuth: false, + appBaseUrl +}); diff --git a/packages/cli/templates/backend-and-client/src/api/base44Client.js.ejs b/packages/cli/templates/backend-and-client/src/api/base44Client.js.ejs deleted file mode 100644 index c4c0d5967..000000000 --- a/packages/cli/templates/backend-and-client/src/api/base44Client.js.ejs +++ /dev/null @@ -1,5 +0,0 @@ -import { createClient } from '@base44/sdk'; - -export const base44 = createClient({ - appId: '<%= projectId %>', -}); diff --git a/packages/cli/templates/backend-and-client/src/lib/app-params.js b/packages/cli/templates/backend-and-client/src/lib/app-params.js new file mode 100644 index 000000000..3026d07cf --- /dev/null +++ b/packages/cli/templates/backend-and-client/src/lib/app-params.js @@ -0,0 +1,54 @@ +const isNode = typeof window === 'undefined'; +const windowObj = isNode ? { localStorage: new Map() } : window; +const storage = windowObj.localStorage; + +const toSnakeCase = (str) => { + return str.replace(/([A-Z])/g, '_$1').toLowerCase(); +} + +const getAppParamValue = (paramName, { defaultValue = undefined, removeFromUrl = false } = {}) => { + if (isNode) { + return defaultValue; + } + const storageKey = `base44_${toSnakeCase(paramName)}`; + const urlParams = new URLSearchParams(window.location.search); + const searchParam = urlParams.get(paramName); + if (removeFromUrl) { + urlParams.delete(paramName); + const newUrl = `${window.location.pathname}${urlParams.toString() ? `?${urlParams.toString()}` : "" + }${window.location.hash}`; + window.history.replaceState({}, document.title, newUrl); + } + if (searchParam) { + storage.setItem(storageKey, searchParam); + return searchParam; + } + if (defaultValue) { + storage.setItem(storageKey, defaultValue); + return defaultValue; + } + const storedValue = storage.getItem(storageKey); + if (storedValue) { + return storedValue; + } + return null; +} + +const getAppParams = () => { + if (getAppParamValue("clear_access_token") === 'true') { + storage.removeItem('base44_access_token'); + storage.removeItem('token'); + } + return { + appId: getAppParamValue("app_id", { defaultValue: import.meta.env.VITE_BASE44_APP_ID }), + token: getAppParamValue("access_token", { removeFromUrl: true }), + fromUrl: getAppParamValue("from_url", { defaultValue: window.location.href }), + functionsVersion: getAppParamValue("functions_version", { defaultValue: import.meta.env.VITE_BASE44_FUNCTIONS_VERSION }), + appBaseUrl: getAppParamValue("app_base_url", { defaultValue: import.meta.env.VITE_BASE44_APP_BASE_URL }), + } +} + + +export const appParams = { + ...getAppParams() +} diff --git a/packages/cli/templates/backend-and-client/vite.config.js b/packages/cli/templates/backend-and-client/vite.config.js index 95d423e2d..96cb102c4 100644 --- a/packages/cli/templates/backend-and-client/vite.config.js +++ b/packages/cli/templates/backend-and-client/vite.config.js @@ -1,12 +1,7 @@ -import { defineConfig } from 'vite'; +import base44 from '@base44/vite-plugin'; import react from '@vitejs/plugin-react'; -import path from 'path'; +import { defineConfig } from 'vite'; export default defineConfig({ - plugins: [react()], - resolve: { - alias: { - '@': path.resolve(__dirname, './src'), - }, - }, + plugins: [base44(), react()], }); diff --git a/packages/cli/tests/cli/create.spec.ts b/packages/cli/tests/cli/create.spec.ts index 0da624d8b..0960f8726 100644 --- a/packages/cli/tests/cli/create.spec.ts +++ b/packages/cli/tests/cli/create.spec.ts @@ -76,6 +76,52 @@ describe("create command", () => { expect(config).toContain('"visibility": "public"'); }); + it("scaffolds backend-and-client with the editor-app client convention", async () => { + await t.givenLoggedIn({ email: "test@example.com", name: "Test User" }); + t.api.mockCreateApp({ id: "convention-app-id", name: "Convention App" }); + + const projectPath = join(t.getTempDir(), "convention-app"); + + const result = await t.run( + "create", + "Convention App", + "--path", + projectPath, + "--template", + "backend-and-client", + "--no-skills", + ); + + t.expectResult(result).toSucceed(); + + const client = await readFile( + join(projectPath, "src", "api", "base44Client.js"), + "utf-8", + ); + expect(client).toContain("serverUrl: ''"); + expect(client).toContain("@/lib/app-params"); + expect(client).not.toContain("convention-app-id"); + + const appParams = await readFile( + join(projectPath, "src", "lib", "app-params.js"), + "utf-8", + ); + expect(appParams).toContain("VITE_BASE44_APP_ID"); + expect(appParams).toContain("VITE_BASE44_APP_BASE_URL"); + + const viteConfig = await readFile( + join(projectPath, "vite.config.js"), + "utf-8", + ); + expect(viteConfig).toContain("@base44/vite-plugin"); + + const appConfig = await readFile( + join(projectPath, "base44", ".app.jsonc"), + "utf-8", + ); + expect(appConfig).toContain("convention-app-id"); + }); + it("infers path from name when --path is not provided", async () => { await t.givenLoggedIn({ email: "test@example.com", name: "Test User" }); t.api.mockCreateApp({ id: "inferred-path-id", name: "My App" });