From 11a28eba887195eb333ef7c7ee4023f76789526d Mon Sep 17 00:00:00 2001 From: dswbx Date: Fri, 28 Mar 2025 18:03:09 +0100 Subject: [PATCH 1/3] improve cli creds extraction --- app/.gitignore | 3 ++ app/src/cli/commands/run/platform.ts | 5 +-- app/src/cli/commands/run/run.ts | 48 ++++++++++++++++------------ app/src/cli/types.d.ts | 7 ++-- 4 files changed, 35 insertions(+), 28 deletions(-) create mode 100644 app/.gitignore diff --git a/app/.gitignore b/app/.gitignore new file mode 100644 index 0000000..74f7dc3 --- /dev/null +++ b/app/.gitignore @@ -0,0 +1,3 @@ +test-results +playwright-report +bknd.config.* \ No newline at end of file diff --git a/app/src/cli/commands/run/platform.ts b/app/src/cli/commands/run/platform.ts index 62707bd..90b8358 100644 --- a/app/src/cli/commands/run/platform.ts +++ b/app/src/cli/commands/run/platform.ts @@ -72,9 +72,10 @@ export async function getConfigPath(filePath?: string) { } } - const paths = ["./bknd.config", "./bknd.config.ts", "./bknd.config.js"]; + const exts = ["", ".js", ".ts", ".mjs", ".cjs", ".json"]; + const paths = exts.map((e) => `bknd.config${e}`); for (const p of paths) { - const _p = path.resolve(process.cwd(), p); + const _p = path.relative(process.cwd(), p); if (await fileExists(_p)) { return _p; } diff --git a/app/src/cli/commands/run/run.ts b/app/src/cli/commands/run/run.ts index ff15f10..2d156e1 100644 --- a/app/src/cli/commands/run/run.ts +++ b/app/src/cli/commands/run/run.ts @@ -7,6 +7,7 @@ import { colorizeConsole, config } from "core"; import dotenv from "dotenv"; import { registries } from "modules/registries"; import c from "picocolors"; +import path from "node:path"; import { PLATFORMS, type Platform, @@ -15,8 +16,12 @@ import { getConnectionCredentialsFromEnv, startServer, } from "./platform"; +import { makeConfig } from "adapter"; -dotenv.config(); +const env_files = [".env", ".dev.vars"]; +dotenv.config({ + path: env_files.map((file) => path.resolve(process.cwd(), file)), +}); const isBun = typeof Bun !== "undefined"; export const run: CliCommand = (program) => { @@ -85,24 +90,12 @@ async function makeApp(config: MakeAppConfig) { return app; } -export async function makeConfigApp(config: CliBkndConfig, platform?: Platform) { - const appConfig = typeof config.app === "function" ? config.app(process.env) : config.app; - const app = App.create(appConfig); - - app.emgr.onEvent( - App.Events.AppBuiltEvent, - async () => { - await attachServeStatic(app, platform ?? "node"); - app.registerAdminController(); - - await config.onBuilt?.(app); - }, - "sync", - ); - - await config.beforeBuild?.(app); - await app.build(config.buildConfig); - return app; +export async function makeConfigApp(_config: CliBkndConfig, platform?: Platform) { + const config = makeConfig(_config, process.env); + return makeApp({ + ...config, + server: { platform }, + }); } async function action(options: { @@ -118,19 +111,31 @@ async function action(options: { const configFilePath = await getConfigPath(options.config); let app: App | undefined = undefined; + // first start from arguments if given if (options.dbUrl) { console.info("Using connection from", c.cyan("--db-url")); const connection = options.dbUrl ? { url: options.dbUrl, authToken: options.dbToken } : undefined; app = await makeApp({ connection, server: { platform: options.server } }); + + // check configuration file to be present } else if (configFilePath) { console.info("Using config from", c.cyan(configFilePath)); - const config = (await import(configFilePath).then((m) => m.default)) as CliBkndConfig; - app = await makeConfigApp(config, options.server); + try { + const config = (await import(configFilePath).then((m) => m.default)) as CliBkndConfig; + app = await makeConfigApp(config, options.server); + } catch (e) { + console.error("Failed to load config:", e); + process.exit(1); + } + + // try to use an in-memory connection } else if (options.memory) { console.info("Using", c.cyan("in-memory"), "connection"); app = await makeApp({ server: { platform: options.server } }); + + // finally try to use env variables } else { const credentials = getConnectionCredentialsFromEnv(); if (credentials) { @@ -139,6 +144,7 @@ async function action(options: { } } + // if nothing helps, create a file based app if (!app) { const connection = { url: "file:data.db" } as Config; console.info("Using connection", c.cyan(connection.url)); diff --git a/app/src/cli/types.d.ts b/app/src/cli/types.d.ts index 30bde3a..b1c20ea 100644 --- a/app/src/cli/types.d.ts +++ b/app/src/cli/types.d.ts @@ -1,12 +1,9 @@ -import type { CreateAppConfig } from "App"; -import type { FrameworkBkndConfig } from "adapter"; +import type { BkndConfig } from "adapter"; import type { Command } from "commander"; export type CliCommand = (program: Command) => void; -export type CliBkndConfig = FrameworkBkndConfig & { - app: CreateAppConfig | ((env: Env) => CreateAppConfig); - setAdminHtml?: boolean; +export type CliBkndConfig = BkndConfig & { server?: { port?: number; platform?: "node" | "bun"; From b29c04e8c9c868bbadaac7773cb1c035bfbc550d Mon Sep 17 00:00:00 2001 From: dswbx Date: Fri, 28 Mar 2025 20:52:00 +0100 Subject: [PATCH 2/3] added more cli instructions --- app/src/cli/commands/run/platform.ts | 2 +- docs/usage/cli.mdx | 92 ++++++++++++++++++++++------ 2 files changed, 74 insertions(+), 20 deletions(-) diff --git a/app/src/cli/commands/run/platform.ts b/app/src/cli/commands/run/platform.ts index 90b8358..480a979 100644 --- a/app/src/cli/commands/run/platform.ts +++ b/app/src/cli/commands/run/platform.ts @@ -75,7 +75,7 @@ export async function getConfigPath(filePath?: string) { const exts = ["", ".js", ".ts", ".mjs", ".cjs", ".json"]; const paths = exts.map((e) => `bknd.config${e}`); for (const p of paths) { - const _p = path.relative(process.cwd(), p); + const _p = path.resolve(process.cwd(), p); if (await fileExists(_p)) { return _p; } diff --git a/docs/usage/cli.mdx b/docs/usage/cli.mdx index 7106774..b203be8 100644 --- a/docs/usage/cli.mdx +++ b/docs/usage/cli.mdx @@ -3,8 +3,7 @@ title: 'Using the CLI' description: 'How to start a bknd instance using the CLI.' --- -Instead of running **bknd** using a framework, you can also use the CLI to quickly spin up a -full functional instance. To see all available options, run: +The bknd package includes a command-line interface (CLI) that allows you to run a bknd instance and perform various tasks. ``` npx bknd @@ -15,18 +14,21 @@ Here is the output: $ npx bknd Usage: bknd [options] [command] -bknd cli +⚡ bknd cli v0.10.3-rc.1 Options: - -V, --version output the version number - -h, --help display help for command + -V, --version output the version number + -h, --help display help for command Commands: - user create and update user (auth) - schema [options] get schema - run [options] - config [options] get default config - help [command] display help for command + config [options] get default config + copy-assets [options] copy static assets + create [options] create a new project + debug debug bknd + run [options] run an instance + schema [options] get schema + user create and update user (auth) + help [command] display help for command ``` ## Starting an instance (`run`) @@ -38,29 +40,81 @@ Usage: bknd run [options] Options: -p, --port port to run on (default: 1337, env: PORT) + -m, --memory use in-memory database -c, --config config file --db-url database url, can be any valid libsql url --db-token database token - --server server type (choices: "node", "bun", default: "node") + --server server type (choices: "node", "bun", default: "bun") + --no-open don't open browser window on start -h, --help display help for command ``` -### In-memory database -To start an instance with an ephemeral in-memory database, run the following: -``` -npx bknd run -``` -Keep in mind that the database is not persisted and will be lost when the process is terminated. +To order in which the connection is determined is as follows: +1. `--db-url` +2. `--config` or reading the filesystem looking for `bknd.config.[js|ts|mjs|cjs|json]` +3. `--memory` +4. Environment variables `DB_URL` and `DB_TOKEN` in `.env` or `.dev.vars` +5. Fallback to file-based database `data.db` ### File-based database -To start an instance with a file-based database, run the following: +By default, a file-based database `data.db` is used when running without any arguments. You can specify a different file name or path using the `--db-url` option. The database file will be created in the current working directory if it does not exist. + ``` npx bknd run --db-url file:data.db ``` +### Using configuration file (`bknd.config.*`) +You can create a configuration file on the working directory that automatically gets picked up: `bknd.config.[js|ts|mjs|cjs|json]` + +Here is an example of a `bknd.config.ts` file: + +```ts +import type { BkndConfig } from "bknd/adapter"; + +export default { + // you can either specify the connection directly + connection: { + url: "file:data.db", + }, + // or use the `app` function which passes the environment variables + app: ({ env }) => ({ + connection: { + url: env.DB_URL, + } + }) +} satisfies BkndConfig; +``` +The `app` function is useful if you need a cross-platform way to access the environment variables. For example, on Cloudflare Workers, you can only access environment variables inside a request handler. If you're exclusively using a node-like environment, it's safe to access the environment variables directly from `process.env`. + + +If you're using `npx bknd run`, make sure to create a file in a file format that `node` can load, otherwise you may run into an error that the file couldn't be found: + +```sh +[INF] 2025-03-28 18:02:21 Using config from bknd.config.ts +[ERR] 2025-03-28 18:02:21 Failed to load config: Error [ERR_MODULE_NOT_FOUND]: Cannot find package 'bknd.config.ts' imported from [...] + at packageResolve (node:internal/modules/esm/resolve:857:9) + at [...] { + code: 'ERR_MODULE_NOT_FOUND' +} +``` + +If you still want to use a `.ts` extension, you can start the CLI e.g. using `tsx`: + +```sh +npx tsx node_modules/.bin/bknd run +``` + ### Turso/LibSQL database To start an instance with a Turso/LibSQL database, run the following: ``` npx bknd run --db-url libsql://your-db.turso.io --db-token ``` -The `--db-token` option is optional and only required if the database is protected. \ No newline at end of file +The `--db-token` option is optional and only required if the database is protected. + + +### In-memory database +To start an instance with an ephemeral in-memory database, run the following: +``` +npx bknd run --memory +``` +Keep in mind that the database is not persisted and will be lost when the process is terminated. \ No newline at end of file From b2fd907e8ca39a60279ee54d4eb6bfd8ee76f34b Mon Sep 17 00:00:00 2001 From: dswbx Date: Fri, 28 Mar 2025 21:12:50 +0100 Subject: [PATCH 3/3] updated docs, fixed run with node/tsx --- app/__test__/helper.ts | 1 + app/package.json | 34 ++++++++++++++++----------------- app/src/cli/commands/run/run.ts | 2 +- app/tsconfig.build.json | 3 ++- docs/usage/cli.mdx | 8 +++++++- 5 files changed, 28 insertions(+), 20 deletions(-) diff --git a/app/__test__/helper.ts b/app/__test__/helper.ts index 405e46f..16b8b8e 100644 --- a/app/__test__/helper.ts +++ b/app/__test__/helper.ts @@ -78,6 +78,7 @@ export const assetsTmpPath = `${import.meta.dir}/_assets/tmp`; export async function enableFetchLogging() { const originalFetch = global.fetch; + // @ts-ignore global.fetch = async (input: RequestInfo | URL, init?: RequestInit) => { const response = await originalFetch(input, init); const url = input instanceof URL || typeof input === "string" ? input : input.url; diff --git a/app/package.json b/app/package.json index 2f26ad3..9faf7ca 100644 --- a/app/package.json +++ b/app/package.json @@ -124,52 +124,52 @@ ".": { "types": "./dist/types/index.d.ts", "import": "./dist/index.js", - "require": "./dist/index.cjs" + "require": "./dist/index.js" }, "./ui": { "types": "./dist/types/ui/index.d.ts", "import": "./dist/ui/index.js", - "require": "./dist/ui/index.cjs" + "require": "./dist/ui/index.js" }, "./elements": { "types": "./dist/types/ui/elements/index.d.ts", "import": "./dist/ui/elements/index.js", - "require": "./dist/ui/elements/index.cjs" + "require": "./dist/ui/elements/index.js" }, "./client": { "types": "./dist/types/ui/client/index.d.ts", "import": "./dist/ui/client/index.js", - "require": "./dist/ui/client/index.cjs" + "require": "./dist/ui/client/index.js" }, "./data": { "types": "./dist/types/data/index.d.ts", "import": "./dist/data/index.js", - "require": "./dist/data/index.cjs" + "require": "./dist/data/index.js" }, "./core": { "types": "./dist/types/core/index.d.ts", "import": "./dist/core/index.js", - "require": "./dist/core/index.cjs" + "require": "./dist/core/index.js" }, "./utils": { "types": "./dist/types/core/utils/index.d.ts", "import": "./dist/core/utils/index.js", - "require": "./dist/core/utils/index.cjs" + "require": "./dist/core/utils/index.js" }, "./cli": { "types": "./dist/types/cli/index.d.ts", "import": "./dist/cli/index.js", - "require": "./dist/cli/index.cjs" + "require": "./dist/cli/index.js" }, "./media": { "types": "./dist/types/media/index.d.ts", "import": "./dist/media/index.js", - "require": "./dist/media/index.cjs" + "require": "./dist/media/index.js" }, "./adapter/cloudflare": { "types": "./dist/types/adapter/cloudflare/index.d.ts", "import": "./dist/adapter/cloudflare/index.js", - "require": "./dist/adapter/cloudflare/index.cjs" + "require": "./dist/adapter/cloudflare/index.js" }, "./adapter": { "types": "./dist/types/adapter/index.d.ts", @@ -178,37 +178,37 @@ "./adapter/vite": { "types": "./dist/types/adapter/vite/index.d.ts", "import": "./dist/adapter/vite/index.js", - "require": "./dist/adapter/vite/index.cjs" + "require": "./dist/adapter/vite/index.js" }, "./adapter/nextjs": { "types": "./dist/types/adapter/nextjs/index.d.ts", "import": "./dist/adapter/nextjs/index.js", - "require": "./dist/adapter/nextjs/index.cjs" + "require": "./dist/adapter/nextjs/index.js" }, "./adapter/react-router": { "types": "./dist/types/adapter/react-router/index.d.ts", "import": "./dist/adapter/react-router/index.js", - "require": "./dist/adapter/react-router/index.cjs" + "require": "./dist/adapter/react-router/index.js" }, "./adapter/bun": { "types": "./dist/types/adapter/bun/index.d.ts", "import": "./dist/adapter/bun/index.js", - "require": "./dist/adapter/bun/index.cjs" + "require": "./dist/adapter/bun/index.js" }, "./adapter/node": { "types": "./dist/types/adapter/node/index.d.ts", "import": "./dist/adapter/node/index.js", - "require": "./dist/adapter/node/index.cjs" + "require": "./dist/adapter/node/index.js" }, "./adapter/astro": { "types": "./dist/types/adapter/astro/index.d.ts", "import": "./dist/adapter/astro/index.js", - "require": "./dist/adapter/astro/index.cjs" + "require": "./dist/adapter/astro/index.js" }, "./adapter/aws": { "types": "./dist/types/adapter/aws/index.d.ts", "import": "./dist/adapter/aws/index.js", - "require": "./dist/adapter/aws/index.cjs" + "require": "./dist/adapter/aws/index.js" }, "./dist/main.css": "./dist/ui/main.css", "./dist/styles.css": "./dist/ui/styles.css", diff --git a/app/src/cli/commands/run/run.ts b/app/src/cli/commands/run/run.ts index 2d156e1..8962c3b 100644 --- a/app/src/cli/commands/run/run.ts +++ b/app/src/cli/commands/run/run.ts @@ -91,7 +91,7 @@ async function makeApp(config: MakeAppConfig) { } export async function makeConfigApp(_config: CliBkndConfig, platform?: Platform) { - const config = makeConfig(_config, process.env); + const config = makeConfig(_config, { env: process.env }); return makeApp({ ...config, server: { platform }, diff --git a/app/tsconfig.build.json b/app/tsconfig.build.json index 5f1c8af..f3a19cb 100644 --- a/app/tsconfig.build.json +++ b/app/tsconfig.build.json @@ -1,4 +1,5 @@ { "extends": "./tsconfig.json", - "include": ["./src/**/*.ts", "./src/**/*.tsx"] + "include": ["./src/**/*.ts", "./src/**/*.tsx"], + "exclude": ["./node_modules", "./__test__"] } diff --git a/docs/usage/cli.mdx b/docs/usage/cli.mdx index b203be8..0c19a5f 100644 --- a/docs/usage/cli.mdx +++ b/docs/usage/cli.mdx @@ -98,7 +98,13 @@ If you're using `npx bknd run`, make sure to create a file in a file format that } ``` -If you still want to use a `.ts` extension, you can start the CLI e.g. using `tsx`: +If you still want to use a `.ts` extension, you can start the CLI e.g. using `node` (>=v22.6.0): + +```sh +node --experimental-strip-types node_modules/.bin/bknd run +``` + +Or with `tsx`: ```sh npx tsx node_modules/.bin/bknd run