Merge remote-tracking branch 'origin/main' into feat/jsonv-refactor

# Conflicts:
#	bun.lock
This commit is contained in:
dswbx
2025-07-05 11:11:06 +02:00
16 changed files with 114 additions and 62 deletions
+1 -1
View File
@@ -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);
}
+1 -1
View File
@@ -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);
}
+8 -4
View File
@@ -334,9 +334,9 @@ export class Authenticator<Strategies extends Record<string, Strategy> = 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<ServerEnv>) {
@@ -345,9 +345,13 @@ export class Authenticator<Strategies extends Record<string, Strategy> = 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
@@ -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<Client>;
export type LibSqlCredentials = Config;
function getClient(clientOrCredentials: Client | LibSqlCredentials): Client {
export type LibsqlClientFns = {
execute: (statement: InStatement) => Promise<ResultSet>;
batch: (statements: InStatement[], mode?: TransactionMode) => Promise<ResultSet[]>;
};
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(
+2 -2
View File
@@ -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 ?? "/",
};
}
@@ -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",
},
+1 -1
View File
@@ -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 (
<div id="bknd-admin" className={actualTheme + " antialiased"}>
+3 -17
View File
@@ -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(
() =>
+1 -3
View File
@@ -185,9 +185,7 @@ function UserMenu() {
}
}
if (!options.theme) {
items.push(() => <UserMenuThemeToggler />);
}
items.push(() => <UserMenuThemeToggler />);
items.push(() => (
<div className="font-mono leading-none text-xs text-primary/50 text-center pb-1 pt-2 mt-1 border-t border-primary/5">
{getVersion()}
+17 -1
View File
@@ -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;
}
+5 -8
View File
@@ -31,7 +31,7 @@ function DataEntityUpdateImpl({ params }) {
const entityId = params.id as PrimaryFieldType;
const [error, setError] = useState<string | null>(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"
>
<Breadcrumbs2
path={[
{ label: entity.label, href: routes.data.entity.list(entity.name) },
{ label: `Edit #${entityId}` },
]}
backTo={backHref}
path={[{ label: entity.label, href: backHref }, { label: `Edit #${entityId}` }]}
/>
</AppShell.SectionHeader>
{$q.isLoading ? (
+5 -10
View File
@@ -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 <Message.NotFound description={`Entity "${params.entity}" doesn't exist.`} />;
@@ -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 }) {
</>
}
>
<Breadcrumbs2
path={[
{ label: entity.label, href: routes.data.entity.list(entity.name) },
{ label: "Create" },
]}
/>
<Breadcrumbs2 backTo={backHref} path={[{ label: entity.label }, { label: "Create" }]} />
</AppShell.SectionHeader>
<AppShell.Scrollable key={entity.name}>
{error && (