From e76e3531d7c1881c12aa33101617be6819373833 Mon Sep 17 00:00:00 2001 From: stormbyte Date: Thu, 26 Jun 2025 15:43:51 +0200 Subject: [PATCH 01/11] Update docker.mdx Add docker compose self build image --- docs/integration/docker.mdx | 37 ++++++++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/docs/integration/docker.mdx b/docs/integration/docker.mdx index c535580a..4c9b888d 100644 --- a/docs/integration/docker.mdx +++ b/docs/integration/docker.mdx @@ -42,4 +42,39 @@ docker run -p 1337:1337 -e ARGS="--db-url libsql://.turso.io --db-token Date: Wed, 2 Jul 2025 18:14:35 +0200 Subject: [PATCH 02/11] admin: fix theme flash --- app/src/modules/server/AdminController.tsx | 2 +- app/src/ui/Admin.tsx | 2 +- app/src/ui/layouts/AppShell/Header.tsx | 4 +--- bun.lock | 2 +- 4 files changed, 4 insertions(+), 6 deletions(-) diff --git a/app/src/modules/server/AdminController.tsx b/app/src/modules/server/AdminController.tsx index 35ebc819..227e22b8 100644 --- a/app/src/modules/server/AdminController.tsx +++ b/app/src/modules/server/AdminController.tsx @@ -51,7 +51,7 @@ export class AdminController extends Controller { basepath: this._options.basepath ?? "/", adminBasepath: this._options.adminBasepath ?? "", assetsPath: this._options.assetsPath ?? config.server.assets_path, - theme: this._options.theme ?? "system", + //theme: this._options.theme ?? "system", logo_return_path: this._options.logoReturnPath ?? "/", }; } diff --git a/app/src/ui/Admin.tsx b/app/src/ui/Admin.tsx index 86b4e8ed..df142acd 100644 --- a/app/src/ui/Admin.tsx +++ b/app/src/ui/Admin.tsx @@ -55,7 +55,7 @@ export default function Admin({ const Skeleton = ({ theme }: { theme?: any }) => { const t = useTheme(); - const actualTheme = theme ?? t.theme; + const actualTheme = theme && ["dark", "light"].includes(theme) ? theme : t.theme; return (
diff --git a/app/src/ui/layouts/AppShell/Header.tsx b/app/src/ui/layouts/AppShell/Header.tsx index 0b313c5a..629d5374 100644 --- a/app/src/ui/layouts/AppShell/Header.tsx +++ b/app/src/ui/layouts/AppShell/Header.tsx @@ -185,9 +185,7 @@ function UserMenu() { } } - if (!options.theme) { - items.push(() => ); - } + items.push(() => ); items.push(() => (
{getVersion()} diff --git a/bun.lock b/bun.lock index 434b81f2..008d7b47 100644 --- a/bun.lock +++ b/bun.lock @@ -15,7 +15,7 @@ }, "app": { "name": "bknd", - "version": "0.15.0-rc.3", + "version": "0.15.0-rc.8", "bin": "./dist/cli/index.js", "dependencies": { "@cfworker/json-schema": "^4.1.1", From 2f684765deed840d4e07ab55f2955a810ecf1f4a Mon Sep 17 00:00:00 2001 From: dswbx Date: Thu, 3 Jul 2025 14:11:58 +0200 Subject: [PATCH 03/11] libsql: update typings to allow minimal client fns (execute, batch) --- .../data/connection/sqlite/libsql/LibsqlConnection.ts | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/app/src/data/connection/sqlite/libsql/LibsqlConnection.ts b/app/src/data/connection/sqlite/libsql/LibsqlConnection.ts index c99ad590..32de8fc4 100644 --- a/app/src/data/connection/sqlite/libsql/LibsqlConnection.ts +++ b/app/src/data/connection/sqlite/libsql/LibsqlConnection.ts @@ -1,4 +1,4 @@ -import type { Client, Config, ResultSet } from "@libsql/client"; +import type { Client, Config, InStatement, ResultSet, TransactionMode } from "@libsql/client"; import { createClient } from "libsql-stateless-easy"; import { FilterNumericKeysPlugin } from "data/plugins/FilterNumericKeysPlugin"; import { @@ -10,7 +10,12 @@ import type { QueryResult } from "kysely"; export type LibsqlConnection = GenericSqliteConnection; export type LibSqlCredentials = Config; -function getClient(clientOrCredentials: Client | LibSqlCredentials): Client { +export type LibsqlClientFns = { + execute: (statement: InStatement) => Promise; + batch: (statements: InStatement[], mode?: TransactionMode) => Promise; +}; + +function getClient(clientOrCredentials: Client | LibSqlCredentials | LibsqlClientFns): Client { if (clientOrCredentials && "url" in clientOrCredentials) { const { url, authToken } = clientOrCredentials; return createClient({ url, authToken }); @@ -19,7 +24,7 @@ function getClient(clientOrCredentials: Client | LibSqlCredentials): Client { return clientOrCredentials as Client; } -export function libsql(config: LibSqlCredentials | Client) { +export function libsql(config: LibSqlCredentials | Client | LibsqlClientFns) { const db = getClient(config); return genericSqlite( From a4ddf780ce6966e0fcbd3275cff18c491902a5f8 Mon Sep 17 00:00:00 2001 From: "N.A. Walhof" Date: Thu, 3 Jul 2025 15:05:49 +0200 Subject: [PATCH 04/11] Finish the sentences --- docs/integration/docker.mdx | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/docs/integration/docker.mdx b/docs/integration/docker.mdx index 4c9b888d..ca3038b3 100644 --- a/docs/integration/docker.mdx +++ b/docs/integration/docker.mdx @@ -46,7 +46,9 @@ docker run -p 1337:1337 -v /path/to/data:/data bknd ## Docker compose example -If you want to use docker compose and build the image directly from +If you want to use docker compose and build the image directly from the git repository. + +`compose.yml` ```yaml services: bknd: @@ -60,17 +62,22 @@ services: - ${DATA_DIR:-.}/data:/data ``` -When you want to build for a specific version +The docker compose can be extended to build a specific version of bknd. +Extend the build command with `args` and `labels`. +Within the args you can pass a `VERSION` build argument. +and labels will ensure that the image that has been build a unique identifier has. + +`compose.yml` ```yaml services: bknd: pull_policy: build - build: + build: context: https://github.com/bknd-io/bknd.git#main:docker args: - VERSION: 0.14.0 + VERSION: labels: - - x-bknd-version=0.14.0 + - x-bknd-version= ports: - 1337:1337 environment: From a5959acb342f8ee50fb5a837a8f5de3148963d49 Mon Sep 17 00:00:00 2001 From: dswbx Date: Thu, 3 Jul 2025 15:06:21 +0200 Subject: [PATCH 05/11] fix admin's assetPath for remote static assets --- app/package.json | 2 +- app/src/adapter/sqlite/bun.ts | 2 +- app/src/adapter/sqlite/node.ts | 2 +- app/src/modules/server/AdminController.tsx | 2 +- bun.lock | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/package.json b/app/package.json index f0624c21..fd1460f2 100644 --- a/app/package.json +++ b/app/package.json @@ -3,7 +3,7 @@ "type": "module", "sideEffects": false, "bin": "./dist/cli/index.js", - "version": "0.15.0-rc.8", + "version": "0.15.0-rc.10", "description": "Lightweight Firebase/Supabase alternative built to run anywhere — incl. Next.js, React Router, Astro, Cloudflare, Bun, Node, AWS Lambda & more.", "homepage": "https://bknd.io", "repository": { diff --git a/app/src/adapter/sqlite/bun.ts b/app/src/adapter/sqlite/bun.ts index eaf02c4b..6d549181 100644 --- a/app/src/adapter/sqlite/bun.ts +++ b/app/src/adapter/sqlite/bun.ts @@ -1,6 +1,6 @@ import type { Connection } from "bknd/data"; import { bunSqlite } from "../bun/connection/BunSqliteConnection"; -export function sqlite(config: { url: string }): Connection { +export function sqlite(config?: { url: string }): Connection { return bunSqlite(config); } diff --git a/app/src/adapter/sqlite/node.ts b/app/src/adapter/sqlite/node.ts index 7bc6ebda..f14a8569 100644 --- a/app/src/adapter/sqlite/node.ts +++ b/app/src/adapter/sqlite/node.ts @@ -1,6 +1,6 @@ import type { Connection } from "bknd/data"; import { nodeSqlite } from "../node/connection/NodeSqliteConnection"; -export function sqlite(config: { url: string }): Connection { +export function sqlite(config?: { url: string }): Connection { return nodeSqlite(config); } diff --git a/app/src/modules/server/AdminController.tsx b/app/src/modules/server/AdminController.tsx index 227e22b8..5cb66d6c 100644 --- a/app/src/modules/server/AdminController.tsx +++ b/app/src/modules/server/AdminController.tsx @@ -195,7 +195,7 @@ export class AdminController extends Controller { if (isProd) { let manifest: any; if (this.options.assetsPath.startsWith("http")) { - manifest = await fetch(this.options.assetsPath + "manifest.json", { + manifest = await fetch(this.options.assetsPath + ".vite/manifest.json", { headers: { Accept: "application/json", }, diff --git a/bun.lock b/bun.lock index 008d7b47..3a98944d 100644 --- a/bun.lock +++ b/bun.lock @@ -15,7 +15,7 @@ }, "app": { "name": "bknd", - "version": "0.15.0-rc.8", + "version": "0.15.0-rc.9", "bin": "./dist/cli/index.js", "dependencies": { "@cfworker/json-schema": "^4.1.1", From d4076803c6eaed340568b5f4afd5b7110677163c Mon Sep 17 00:00:00 2001 From: dswbx Date: Thu, 3 Jul 2025 15:15:18 +0200 Subject: [PATCH 06/11] docs: move file name to block, slightly reformulate docker compose instructions --- docs/integration/docker.mdx | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/docs/integration/docker.mdx b/docs/integration/docker.mdx index ca3038b3..4372cbf3 100644 --- a/docs/integration/docker.mdx +++ b/docs/integration/docker.mdx @@ -48,8 +48,7 @@ docker run -p 1337:1337 -v /path/to/data:/data bknd If you want to use docker compose and build the image directly from the git repository. -`compose.yml` -```yaml +```yaml compose.yml services: bknd: pull_policy: build @@ -62,13 +61,12 @@ services: - ${DATA_DIR:-.}/data:/data ``` -The docker compose can be extended to build a specific version of bknd. -Extend the build command with `args` and `labels`. -Within the args you can pass a `VERSION` build argument. -and labels will ensure that the image that has been build a unique identifier has. -`compose.yml` -```yaml +The docker compose file can be extended to build a specific version of bknd. +Extend the `build` section with `args` and `labels`. +Inside `args`, you can pass a `VERSION` build argument, and use `labels` so the built image receives a unique identifier. + +```yaml compose.yml services: bknd: pull_policy: build From 109c72e84f18e43ca3ab2793c7d041ae02ce702c Mon Sep 17 00:00:00 2001 From: dswbx Date: Thu, 3 Jul 2025 15:31:14 +0200 Subject: [PATCH 07/11] update readme and docs on dbs --- README.md | 8 ++++++++ bun.lock | 2 +- docs/usage/database.mdx | 14 ++++++++------ 3 files changed, 17 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index d473e360..41ae4b14 100644 --- a/README.md +++ b/README.md @@ -9,10 +9,18 @@

bknd simplifies app development by providing a fully functional backend for database management, authentication, media and workflows. Being lightweight and built on Web Standards, it can be deployed nearly anywhere, including running inside your framework of choice. No more deploying multiple separate services! +* **Runtimes**: Node.js 22+, Bun 1.0+, Deno, Browser, Cloudflare Workers/Pages, Vercel, Netlify, AWS Lambda, etc. +* **Databases**: + * SQLite: LibSQL, Node SQLite, Bun SQLite, Cloudflare D1, Cloudflare Durable Objects SQLite, SQLocal + * Postgres: Vanilla Postgres, Supabase, Neon, Xata +* **Frameworks**: React, Next.js, React Router, Astro, Vite, Waku +* **Storage**: AWS S3, S3-compatible (Tigris, R2, Minio, etc.), Cloudflare R2 (binding), Cloudinary **For documentation and examples, please visit https://docs.bknd.io.** > [!WARNING] +> This project requires Node.js 22 or higher (because of `node:sqlite`). +> > Please keep in mind that **bknd** is still under active development > and therefore full backward compatibility is not guaranteed before reaching v1.0.0. diff --git a/bun.lock b/bun.lock index 3a98944d..7f982e0a 100644 --- a/bun.lock +++ b/bun.lock @@ -15,7 +15,7 @@ }, "app": { "name": "bknd", - "version": "0.15.0-rc.9", + "version": "0.15.0-rc.10", "bin": "./dist/cli/index.js", "dependencies": { "@cfworker/json-schema": "^4.1.1", diff --git a/docs/usage/database.mdx b/docs/usage/database.mdx index dc520d21..8a43de33 100644 --- a/docs/usage/database.mdx +++ b/docs/usage/database.mdx @@ -107,8 +107,8 @@ import { libsql } from "bknd/data"; export default { connection: libsql({ - url: "libsql://your-database-url.turso.io", - authToken: "your-auth-token", + url: "libsql://.turso.io", + authToken: "", }), } as const satisfies BkndConfig; ``` @@ -120,11 +120,13 @@ import type { BkndConfig } from "bknd"; import { libsql } from "bknd/data"; import { createClient } from "@libsql/client"; +const client = createClient({ + url: "libsql://.turso.io", + authToken: "", +}) + export default { - connection: libsql(createClient({ - url: "libsql://your-database-url.turso.io", - authToken: "your-auth-token", - })), + connection: libsql(client), } as const satisfies BkndConfig; ``` From b35ee36fb1619dceffc23f08a90a0941c0a99f8e Mon Sep 17 00:00:00 2001 From: dswbx Date: Sat, 5 Jul 2025 09:42:53 +0200 Subject: [PATCH 08/11] admin: fix back behavior to not rely on history object --- app/src/ui/layouts/AppShell/Breadcrumbs2.tsx | 20 +++---------------- app/src/ui/lib/routes.ts | 18 ++++++++++++++++- app/src/ui/routes/data/data.$entity.$id.tsx | 13 +++++------- .../ui/routes/data/data.$entity.create.tsx | 15 +++++--------- 4 files changed, 30 insertions(+), 36 deletions(-) diff --git a/app/src/ui/layouts/AppShell/Breadcrumbs2.tsx b/app/src/ui/layouts/AppShell/Breadcrumbs2.tsx index b495b435..2b7c5621 100644 --- a/app/src/ui/layouts/AppShell/Breadcrumbs2.tsx +++ b/app/src/ui/layouts/AppShell/Breadcrumbs2.tsx @@ -4,6 +4,7 @@ import { Link, useLocation } from "wouter"; import { IconButton } from "../../components/buttons/IconButton"; import { Dropdown } from "../../components/overlay/Dropdown"; import { useEvent } from "../../hooks/use-event"; +import { useNavigate } from "ui/lib/routes"; type Breadcrumb = { label: string | Element; @@ -17,26 +18,11 @@ export type Breadcrumbs2Props = { }; export const Breadcrumbs2 = ({ path: _path, backTo, onBack }: Breadcrumbs2Props) => { - const [_, navigate] = useLocation(); - const location = window.location.pathname; + const [, , _goBack] = useNavigate(); const path = Array.isArray(_path) ? _path : [_path]; - const loc = location.split("/").filter((v) => v !== ""); const hasBack = path.length > 1; - const goBack = onBack - ? onBack - : useEvent(() => { - if (backTo) { - navigate(backTo, { replace: true }); - return; - } else if (_path.length > 0 && _path[0]?.href) { - navigate(_path[0].href, { replace: true }); - return; - } - - const href = loc.slice(0, path.length + 1).join("/"); - navigate(`~/${href}`, { replace: true }); - }); + const goBack = onBack ? onBack : () => _goBack({ fallback: backTo }); const crumbs = useMemo( () => diff --git a/app/src/ui/lib/routes.ts b/app/src/ui/lib/routes.ts index b94e550f..42b896ba 100644 --- a/app/src/ui/lib/routes.ts +++ b/app/src/ui/lib/routes.ts @@ -102,13 +102,29 @@ export function useNavigate() { } const _url = options?.absolute ? `~/${basepath}${url}`.replace(/\/+/g, "/") : url; + const state = { + ...options?.state, + referrer: location, + }; + navigate(options?.query ? withQuery(_url, options?.query) : _url, { replace: options?.replace, - state: options?.state, + state, }); }); }, location, + (opts?: { fallback?: string }) => { + const state = window.history.state; + if (state?.referrer) { + //window.history.replaceState(state, "", state.referrer); + navigate(state.referrer, { replace: true }); + } else if (opts?.fallback) { + navigate(opts.fallback, { replace: true }); + } else { + window.history.back(); + } + }, ] as const; } diff --git a/app/src/ui/routes/data/data.$entity.$id.tsx b/app/src/ui/routes/data/data.$entity.$id.tsx index 978573ab..e531452d 100644 --- a/app/src/ui/routes/data/data.$entity.$id.tsx +++ b/app/src/ui/routes/data/data.$entity.$id.tsx @@ -31,7 +31,7 @@ function DataEntityUpdateImpl({ params }) { const entityId = params.id as PrimaryFieldType; const [error, setError] = useState(null); - const [navigate] = useNavigate(); + const [navigate, _, _goBack] = useNavigate(); useBrowserTitle(["Data", entity.label, `#${entityId}`]); const targetRelations = relations.listableRelationsOf(entity); @@ -52,9 +52,8 @@ function DataEntityUpdateImpl({ params }) { }, ); - function goBack() { - window.history.go(-1); - } + const backHref = routes.data.entity.list(entity.name); + const goBack = () => _goBack({ fallback: backHref }); async function onSubmitted(changeSet?: EntityData) { //return; @@ -162,10 +161,8 @@ function DataEntityUpdateImpl({ params }) { className="pl-3" > {$q.isLoading ? ( diff --git a/app/src/ui/routes/data/data.$entity.create.tsx b/app/src/ui/routes/data/data.$entity.create.tsx index bb83eaf6..07b2bb58 100644 --- a/app/src/ui/routes/data/data.$entity.create.tsx +++ b/app/src/ui/routes/data/data.$entity.create.tsx @@ -8,13 +8,14 @@ import { useBrowserTitle } from "ui/hooks/use-browser-title"; import { useSearch } from "ui/hooks/use-search"; import * as AppShell from "ui/layouts/AppShell/AppShell"; import { Breadcrumbs2 } from "ui/layouts/AppShell/Breadcrumbs2"; -import { routes } from "ui/lib/routes"; +import { routes, useNavigate } from "ui/lib/routes"; import { EntityForm } from "ui/modules/data/components/EntityForm"; import { useEntityForm } from "ui/modules/data/hooks/useEntityForm"; import { s } from "core/object/schema"; export function DataEntityCreate({ params }) { const { $data } = useBkndData(); + const [navigate, _, _goBack] = useNavigate(); const entity = $data.entity(params.entity as string); if (!entity) { return ; @@ -30,9 +31,8 @@ export function DataEntityCreate({ params }) { // @todo: use entity schema for prefilling const search = useSearch(s.object({}), {}); - function goBack() { - window.history.go(-1); - } + const backHref = routes.data.entity.list(entity.name); + const goBack = () => _goBack({ fallback: backHref }); async function onSubmitted(changeSet?: EntityData) { console.log("create:changeSet", changeSet); @@ -80,12 +80,7 @@ export function DataEntityCreate({ params }) { } > - + {error && ( From e939debba58453066edd4799fb402c9fa352aa04 Mon Sep 17 00:00:00 2001 From: dswbx Date: Sat, 5 Jul 2025 09:43:14 +0200 Subject: [PATCH 09/11] auth: add delete auth after flash in case only one is picked up --- app/src/auth/authenticate/Authenticator.ts | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/app/src/auth/authenticate/Authenticator.ts b/app/src/auth/authenticate/Authenticator.ts index 29b65971..d28ec4a2 100644 --- a/app/src/auth/authenticate/Authenticator.ts +++ b/app/src/auth/authenticate/Authenticator.ts @@ -342,9 +342,9 @@ export class Authenticator = Record< await setSignedCookie(c, "auth", token, secret, this.cookieOptions); } - private async deleteAuthCookie(c: Context) { + private deleteAuthCookie(c: Context) { $console.debug("deleting auth cookie"); - await deleteCookie(c, "auth", this.cookieOptions); + deleteCookie(c, "auth", this.cookieOptions); } async logout(c: Context) { @@ -353,9 +353,13 @@ export class Authenticator = Record< const cookie = await this.getAuthCookie(c); if (cookie) { - await this.deleteAuthCookie(c); - await addFlashMessage(c, "Signed out", "info"); + addFlashMessage(c, "Signed out", "info"); } + + // on waku, only one cookie setting is performed + // therefore adding deleting cookie at the end + // as the flash isn't that important + this.deleteAuthCookie(c); } // @todo: move this to a server helper From 5355f2593ecc431f7e0d47bdc91b8df6fb5b3cb2 Mon Sep 17 00:00:00 2001 From: dswbx Date: Sat, 5 Jul 2025 09:43:22 +0200 Subject: [PATCH 10/11] readme: add filesystem --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 41ae4b14..ab86304e 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ bknd simplifies app development by providing a fully functional backend for data * SQLite: LibSQL, Node SQLite, Bun SQLite, Cloudflare D1, Cloudflare Durable Objects SQLite, SQLocal * Postgres: Vanilla Postgres, Supabase, Neon, Xata * **Frameworks**: React, Next.js, React Router, Astro, Vite, Waku -* **Storage**: AWS S3, S3-compatible (Tigris, R2, Minio, etc.), Cloudflare R2 (binding), Cloudinary +* **Storage**: AWS S3, S3-compatible (Tigris, R2, Minio, etc.), Cloudflare R2 (binding), Cloudinary, Filesystem **For documentation and examples, please visit https://docs.bknd.io.** From 957355c862a2129972e1bdc0b709db7b4468d24c Mon Sep 17 00:00:00 2001 From: dswbx Date: Sat, 5 Jul 2025 11:08:26 +0200 Subject: [PATCH 11/11] bump v0.15 --- app/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/package.json b/app/package.json index fd1460f2..a1ced704 100644 --- a/app/package.json +++ b/app/package.json @@ -3,7 +3,7 @@ "type": "module", "sideEffects": false, "bin": "./dist/cli/index.js", - "version": "0.15.0-rc.10", + "version": "0.15.0", "description": "Lightweight Firebase/Supabase alternative built to run anywhere — incl. Next.js, React Router, Astro, Cloudflare, Bun, Node, AWS Lambda & more.", "homepage": "https://bknd.io", "repository": {