mirror of
https://github.com/shishantbiswas/bknd.git
synced 2026-03-16 04:27:21 +00:00
Merge pull request #125 from bknd-io/feat/cli-load-creds
improve cli creds extraction
This commit is contained in:
3
app/.gitignore
vendored
Normal file
3
app/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
test-results
|
||||||
|
playwright-report
|
||||||
|
bknd.config.*
|
||||||
@@ -78,6 +78,7 @@ export const assetsTmpPath = `${import.meta.dir}/_assets/tmp`;
|
|||||||
export async function enableFetchLogging() {
|
export async function enableFetchLogging() {
|
||||||
const originalFetch = global.fetch;
|
const originalFetch = global.fetch;
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
global.fetch = async (input: RequestInfo | URL, init?: RequestInit) => {
|
global.fetch = async (input: RequestInfo | URL, init?: RequestInit) => {
|
||||||
const response = await originalFetch(input, init);
|
const response = await originalFetch(input, init);
|
||||||
const url = input instanceof URL || typeof input === "string" ? input : input.url;
|
const url = input instanceof URL || typeof input === "string" ? input : input.url;
|
||||||
|
|||||||
@@ -124,52 +124,52 @@
|
|||||||
".": {
|
".": {
|
||||||
"types": "./dist/types/index.d.ts",
|
"types": "./dist/types/index.d.ts",
|
||||||
"import": "./dist/index.js",
|
"import": "./dist/index.js",
|
||||||
"require": "./dist/index.cjs"
|
"require": "./dist/index.js"
|
||||||
},
|
},
|
||||||
"./ui": {
|
"./ui": {
|
||||||
"types": "./dist/types/ui/index.d.ts",
|
"types": "./dist/types/ui/index.d.ts",
|
||||||
"import": "./dist/ui/index.js",
|
"import": "./dist/ui/index.js",
|
||||||
"require": "./dist/ui/index.cjs"
|
"require": "./dist/ui/index.js"
|
||||||
},
|
},
|
||||||
"./elements": {
|
"./elements": {
|
||||||
"types": "./dist/types/ui/elements/index.d.ts",
|
"types": "./dist/types/ui/elements/index.d.ts",
|
||||||
"import": "./dist/ui/elements/index.js",
|
"import": "./dist/ui/elements/index.js",
|
||||||
"require": "./dist/ui/elements/index.cjs"
|
"require": "./dist/ui/elements/index.js"
|
||||||
},
|
},
|
||||||
"./client": {
|
"./client": {
|
||||||
"types": "./dist/types/ui/client/index.d.ts",
|
"types": "./dist/types/ui/client/index.d.ts",
|
||||||
"import": "./dist/ui/client/index.js",
|
"import": "./dist/ui/client/index.js",
|
||||||
"require": "./dist/ui/client/index.cjs"
|
"require": "./dist/ui/client/index.js"
|
||||||
},
|
},
|
||||||
"./data": {
|
"./data": {
|
||||||
"types": "./dist/types/data/index.d.ts",
|
"types": "./dist/types/data/index.d.ts",
|
||||||
"import": "./dist/data/index.js",
|
"import": "./dist/data/index.js",
|
||||||
"require": "./dist/data/index.cjs"
|
"require": "./dist/data/index.js"
|
||||||
},
|
},
|
||||||
"./core": {
|
"./core": {
|
||||||
"types": "./dist/types/core/index.d.ts",
|
"types": "./dist/types/core/index.d.ts",
|
||||||
"import": "./dist/core/index.js",
|
"import": "./dist/core/index.js",
|
||||||
"require": "./dist/core/index.cjs"
|
"require": "./dist/core/index.js"
|
||||||
},
|
},
|
||||||
"./utils": {
|
"./utils": {
|
||||||
"types": "./dist/types/core/utils/index.d.ts",
|
"types": "./dist/types/core/utils/index.d.ts",
|
||||||
"import": "./dist/core/utils/index.js",
|
"import": "./dist/core/utils/index.js",
|
||||||
"require": "./dist/core/utils/index.cjs"
|
"require": "./dist/core/utils/index.js"
|
||||||
},
|
},
|
||||||
"./cli": {
|
"./cli": {
|
||||||
"types": "./dist/types/cli/index.d.ts",
|
"types": "./dist/types/cli/index.d.ts",
|
||||||
"import": "./dist/cli/index.js",
|
"import": "./dist/cli/index.js",
|
||||||
"require": "./dist/cli/index.cjs"
|
"require": "./dist/cli/index.js"
|
||||||
},
|
},
|
||||||
"./media": {
|
"./media": {
|
||||||
"types": "./dist/types/media/index.d.ts",
|
"types": "./dist/types/media/index.d.ts",
|
||||||
"import": "./dist/media/index.js",
|
"import": "./dist/media/index.js",
|
||||||
"require": "./dist/media/index.cjs"
|
"require": "./dist/media/index.js"
|
||||||
},
|
},
|
||||||
"./adapter/cloudflare": {
|
"./adapter/cloudflare": {
|
||||||
"types": "./dist/types/adapter/cloudflare/index.d.ts",
|
"types": "./dist/types/adapter/cloudflare/index.d.ts",
|
||||||
"import": "./dist/adapter/cloudflare/index.js",
|
"import": "./dist/adapter/cloudflare/index.js",
|
||||||
"require": "./dist/adapter/cloudflare/index.cjs"
|
"require": "./dist/adapter/cloudflare/index.js"
|
||||||
},
|
},
|
||||||
"./adapter": {
|
"./adapter": {
|
||||||
"types": "./dist/types/adapter/index.d.ts",
|
"types": "./dist/types/adapter/index.d.ts",
|
||||||
@@ -178,37 +178,37 @@
|
|||||||
"./adapter/vite": {
|
"./adapter/vite": {
|
||||||
"types": "./dist/types/adapter/vite/index.d.ts",
|
"types": "./dist/types/adapter/vite/index.d.ts",
|
||||||
"import": "./dist/adapter/vite/index.js",
|
"import": "./dist/adapter/vite/index.js",
|
||||||
"require": "./dist/adapter/vite/index.cjs"
|
"require": "./dist/adapter/vite/index.js"
|
||||||
},
|
},
|
||||||
"./adapter/nextjs": {
|
"./adapter/nextjs": {
|
||||||
"types": "./dist/types/adapter/nextjs/index.d.ts",
|
"types": "./dist/types/adapter/nextjs/index.d.ts",
|
||||||
"import": "./dist/adapter/nextjs/index.js",
|
"import": "./dist/adapter/nextjs/index.js",
|
||||||
"require": "./dist/adapter/nextjs/index.cjs"
|
"require": "./dist/adapter/nextjs/index.js"
|
||||||
},
|
},
|
||||||
"./adapter/react-router": {
|
"./adapter/react-router": {
|
||||||
"types": "./dist/types/adapter/react-router/index.d.ts",
|
"types": "./dist/types/adapter/react-router/index.d.ts",
|
||||||
"import": "./dist/adapter/react-router/index.js",
|
"import": "./dist/adapter/react-router/index.js",
|
||||||
"require": "./dist/adapter/react-router/index.cjs"
|
"require": "./dist/adapter/react-router/index.js"
|
||||||
},
|
},
|
||||||
"./adapter/bun": {
|
"./adapter/bun": {
|
||||||
"types": "./dist/types/adapter/bun/index.d.ts",
|
"types": "./dist/types/adapter/bun/index.d.ts",
|
||||||
"import": "./dist/adapter/bun/index.js",
|
"import": "./dist/adapter/bun/index.js",
|
||||||
"require": "./dist/adapter/bun/index.cjs"
|
"require": "./dist/adapter/bun/index.js"
|
||||||
},
|
},
|
||||||
"./adapter/node": {
|
"./adapter/node": {
|
||||||
"types": "./dist/types/adapter/node/index.d.ts",
|
"types": "./dist/types/adapter/node/index.d.ts",
|
||||||
"import": "./dist/adapter/node/index.js",
|
"import": "./dist/adapter/node/index.js",
|
||||||
"require": "./dist/adapter/node/index.cjs"
|
"require": "./dist/adapter/node/index.js"
|
||||||
},
|
},
|
||||||
"./adapter/astro": {
|
"./adapter/astro": {
|
||||||
"types": "./dist/types/adapter/astro/index.d.ts",
|
"types": "./dist/types/adapter/astro/index.d.ts",
|
||||||
"import": "./dist/adapter/astro/index.js",
|
"import": "./dist/adapter/astro/index.js",
|
||||||
"require": "./dist/adapter/astro/index.cjs"
|
"require": "./dist/adapter/astro/index.js"
|
||||||
},
|
},
|
||||||
"./adapter/aws": {
|
"./adapter/aws": {
|
||||||
"types": "./dist/types/adapter/aws/index.d.ts",
|
"types": "./dist/types/adapter/aws/index.d.ts",
|
||||||
"import": "./dist/adapter/aws/index.js",
|
"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/main.css": "./dist/ui/main.css",
|
||||||
"./dist/styles.css": "./dist/ui/styles.css",
|
"./dist/styles.css": "./dist/ui/styles.css",
|
||||||
|
|||||||
@@ -72,7 +72,8 @@ 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) {
|
for (const p of paths) {
|
||||||
const _p = path.resolve(process.cwd(), p);
|
const _p = path.resolve(process.cwd(), p);
|
||||||
if (await fileExists(_p)) {
|
if (await fileExists(_p)) {
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import { colorizeConsole, config } from "core";
|
|||||||
import dotenv from "dotenv";
|
import dotenv from "dotenv";
|
||||||
import { registries } from "modules/registries";
|
import { registries } from "modules/registries";
|
||||||
import c from "picocolors";
|
import c from "picocolors";
|
||||||
|
import path from "node:path";
|
||||||
import {
|
import {
|
||||||
PLATFORMS,
|
PLATFORMS,
|
||||||
type Platform,
|
type Platform,
|
||||||
@@ -15,8 +16,12 @@ import {
|
|||||||
getConnectionCredentialsFromEnv,
|
getConnectionCredentialsFromEnv,
|
||||||
startServer,
|
startServer,
|
||||||
} from "./platform";
|
} 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";
|
const isBun = typeof Bun !== "undefined";
|
||||||
|
|
||||||
export const run: CliCommand = (program) => {
|
export const run: CliCommand = (program) => {
|
||||||
@@ -85,24 +90,12 @@ async function makeApp(config: MakeAppConfig) {
|
|||||||
return app;
|
return app;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function makeConfigApp(config: CliBkndConfig, platform?: Platform) {
|
export async function makeConfigApp(_config: CliBkndConfig, platform?: Platform) {
|
||||||
const appConfig = typeof config.app === "function" ? config.app(process.env) : config.app;
|
const config = makeConfig(_config, { env: process.env });
|
||||||
const app = App.create(appConfig);
|
return makeApp({
|
||||||
|
...config,
|
||||||
app.emgr.onEvent(
|
server: { platform },
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function action(options: {
|
async function action(options: {
|
||||||
@@ -118,19 +111,31 @@ async function action(options: {
|
|||||||
const configFilePath = await getConfigPath(options.config);
|
const configFilePath = await getConfigPath(options.config);
|
||||||
|
|
||||||
let app: App | undefined = undefined;
|
let app: App | undefined = undefined;
|
||||||
|
// first start from arguments if given
|
||||||
if (options.dbUrl) {
|
if (options.dbUrl) {
|
||||||
console.info("Using connection from", c.cyan("--db-url"));
|
console.info("Using connection from", c.cyan("--db-url"));
|
||||||
const connection = options.dbUrl
|
const connection = options.dbUrl
|
||||||
? { url: options.dbUrl, authToken: options.dbToken }
|
? { url: options.dbUrl, authToken: options.dbToken }
|
||||||
: undefined;
|
: undefined;
|
||||||
app = await makeApp({ connection, server: { platform: options.server } });
|
app = await makeApp({ connection, server: { platform: options.server } });
|
||||||
|
|
||||||
|
// check configuration file to be present
|
||||||
} else if (configFilePath) {
|
} else if (configFilePath) {
|
||||||
console.info("Using config from", c.cyan(configFilePath));
|
console.info("Using config from", c.cyan(configFilePath));
|
||||||
const config = (await import(configFilePath).then((m) => m.default)) as CliBkndConfig;
|
try {
|
||||||
app = await makeConfigApp(config, options.server);
|
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) {
|
} else if (options.memory) {
|
||||||
console.info("Using", c.cyan("in-memory"), "connection");
|
console.info("Using", c.cyan("in-memory"), "connection");
|
||||||
app = await makeApp({ server: { platform: options.server } });
|
app = await makeApp({ server: { platform: options.server } });
|
||||||
|
|
||||||
|
// finally try to use env variables
|
||||||
} else {
|
} else {
|
||||||
const credentials = getConnectionCredentialsFromEnv();
|
const credentials = getConnectionCredentialsFromEnv();
|
||||||
if (credentials) {
|
if (credentials) {
|
||||||
@@ -139,6 +144,7 @@ async function action(options: {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if nothing helps, create a file based app
|
||||||
if (!app) {
|
if (!app) {
|
||||||
const connection = { url: "file:data.db" } as Config;
|
const connection = { url: "file:data.db" } as Config;
|
||||||
console.info("Using connection", c.cyan(connection.url));
|
console.info("Using connection", c.cyan(connection.url));
|
||||||
|
|||||||
7
app/src/cli/types.d.ts
vendored
7
app/src/cli/types.d.ts
vendored
@@ -1,12 +1,9 @@
|
|||||||
import type { CreateAppConfig } from "App";
|
import type { BkndConfig } from "adapter";
|
||||||
import type { FrameworkBkndConfig } from "adapter";
|
|
||||||
import type { Command } from "commander";
|
import type { Command } from "commander";
|
||||||
|
|
||||||
export type CliCommand = (program: Command) => void;
|
export type CliCommand = (program: Command) => void;
|
||||||
|
|
||||||
export type CliBkndConfig<Env = any> = FrameworkBkndConfig & {
|
export type CliBkndConfig<Env = any> = BkndConfig & {
|
||||||
app: CreateAppConfig | ((env: Env) => CreateAppConfig);
|
|
||||||
setAdminHtml?: boolean;
|
|
||||||
server?: {
|
server?: {
|
||||||
port?: number;
|
port?: number;
|
||||||
platform?: "node" | "bun";
|
platform?: "node" | "bun";
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
{
|
{
|
||||||
"extends": "./tsconfig.json",
|
"extends": "./tsconfig.json",
|
||||||
"include": ["./src/**/*.ts", "./src/**/*.tsx"]
|
"include": ["./src/**/*.ts", "./src/**/*.tsx"],
|
||||||
|
"exclude": ["./node_modules", "./__test__"]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,8 +3,7 @@ title: 'Using the CLI'
|
|||||||
description: 'How to start a bknd instance 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
|
The bknd package includes a command-line interface (CLI) that allows you to run a bknd instance and perform various tasks.
|
||||||
full functional instance. To see all available options, run:
|
|
||||||
|
|
||||||
```
|
```
|
||||||
npx bknd
|
npx bknd
|
||||||
@@ -15,18 +14,21 @@ Here is the output:
|
|||||||
$ npx bknd
|
$ npx bknd
|
||||||
Usage: bknd [options] [command]
|
Usage: bknd [options] [command]
|
||||||
|
|
||||||
bknd cli
|
⚡ bknd cli v0.10.3-rc.1
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
-V, --version output the version number
|
-V, --version output the version number
|
||||||
-h, --help display help for command
|
-h, --help display help for command
|
||||||
|
|
||||||
Commands:
|
Commands:
|
||||||
user <action> create and update user (auth)
|
config [options] get default config
|
||||||
schema [options] get schema
|
copy-assets [options] copy static assets
|
||||||
run [options]
|
create [options] create a new project
|
||||||
config [options] get default config
|
debug <subject> debug bknd
|
||||||
help [command] display help for command
|
run [options] run an instance
|
||||||
|
schema [options] get schema
|
||||||
|
user <action> create and update user (auth)
|
||||||
|
help [command] display help for command
|
||||||
```
|
```
|
||||||
|
|
||||||
## Starting an instance (`run`)
|
## Starting an instance (`run`)
|
||||||
@@ -38,29 +40,87 @@ Usage: bknd run [options]
|
|||||||
|
|
||||||
Options:
|
Options:
|
||||||
-p, --port <port> port to run on (default: 1337, env: PORT)
|
-p, --port <port> port to run on (default: 1337, env: PORT)
|
||||||
|
-m, --memory use in-memory database
|
||||||
-c, --config <config> config file
|
-c, --config <config> config file
|
||||||
--db-url <db> database url, can be any valid libsql url
|
--db-url <db> database url, can be any valid libsql url
|
||||||
--db-token <db> database token
|
--db-token <db> database token
|
||||||
--server <server> server type (choices: "node", "bun", default: "node")
|
--server <server> server type (choices: "node", "bun", default: "bun")
|
||||||
|
--no-open don't open browser window on start
|
||||||
-h, --help display help for command
|
-h, --help display help for command
|
||||||
```
|
```
|
||||||
|
|
||||||
### In-memory database
|
To order in which the connection is determined is as follows:
|
||||||
To start an instance with an ephemeral in-memory database, run the following:
|
1. `--db-url`
|
||||||
```
|
2. `--config` or reading the filesystem looking for `bknd.config.[js|ts|mjs|cjs|json]`
|
||||||
npx bknd run
|
3. `--memory`
|
||||||
```
|
4. Environment variables `DB_URL` and `DB_TOKEN` in `.env` or `.dev.vars`
|
||||||
Keep in mind that the database is not persisted and will be lost when the process is terminated.
|
5. Fallback to file-based database `data.db`
|
||||||
|
|
||||||
### File-based database
|
### 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
|
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 `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
|
||||||
|
```
|
||||||
|
|
||||||
### Turso/LibSQL database
|
### Turso/LibSQL database
|
||||||
To start an instance with a Turso/LibSQL database, run the following:
|
To start an instance with a Turso/LibSQL database, run the following:
|
||||||
```
|
```
|
||||||
npx bknd run --db-url libsql://your-db.turso.io --db-token <your-token>
|
npx bknd run --db-url libsql://your-db.turso.io --db-token <your-token>
|
||||||
```
|
```
|
||||||
The `--db-token` option is optional and only required if the database is protected.
|
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.
|
||||||
Reference in New Issue
Block a user