finalized sqlocal, added BkndBrowserApp, updated react example

This commit is contained in:
dswbx
2025-12-02 14:03:41 +01:00
parent d1aa2da5b1
commit e56fc9c368
16 changed files with 232 additions and 415 deletions
+38 -117
View File
@@ -1,61 +1,7 @@
import { lazy, Suspense, useEffect, useState } from "react";
import { checksum } from "bknd/utils";
import { App, boolean, em, entity, text, registries } from "bknd";
import { SQLocalConnection } from "@bknd/sqlocal";
import { Route, Router, Switch } from "wouter";
import { boolean, em, entity, text } from "bknd";
import { Route } from "wouter";
import IndexPage from "~/routes/_index";
import { Center } from "~/components/Center";
import { type Api, ClientProvider } from "bknd/client";
import { SQLocalKysely } from "sqlocal/kysely";
import { OpfsStorageAdapter } from "~/OpfsStorageAdapter";
const Admin = lazy(() => import("~/routes/admin"));
export default function () {
const [app, setApp] = useState<App | undefined>(undefined);
const [api, setApi] = useState<Api | undefined>(undefined);
const [hash, setHash] = useState<string>("");
async function onBuilt(app: App) {
document.startViewTransition(async () => {
setApp(app);
setApi(app.getApi());
setHash(await checksum(app.toJSON()));
});
}
useEffect(() => {
setup({ onBuilt })
.then((app) => console.log("setup", app?.version()))
.catch(console.error);
}, []);
if (!app || !api)
return (
<Center>
<span className="opacity-20">Loading...</span>
</Center>
);
return (
<ClientProvider api={api}>
<Router key={hash}>
<Switch>
<Route path="/" component={() => <IndexPage app={app} />} />
<Route path="/admin/*?">
<Suspense>
<Admin config={{ basepath: "/admin", logo_return_path: "/../" }} />
</Suspense>
</Route>
<Route path="*">
<Center className="font-mono text-4xl">404</Center>
</Route>
</Switch>
</Router>
</ClientProvider>
);
}
import { BkndBrowserApp, type BrowserBkndConfig } from "bknd/adapter/browser";
const schema = em({
todos: entity("todos", {
@@ -70,66 +16,41 @@ declare module "bknd" {
interface DB extends Database {}
}
let initialized = false;
async function setup(opts?: {
beforeBuild?: (app: App) => Promise<void>;
onBuilt?: (app: App) => Promise<void>;
}) {
if (initialized) return;
initialized = true;
const config = {
config: {
data: schema.toJSON(),
auth: {
enabled: true,
jwt: {
secret: "secret",
},
},
},
adminConfig: {
basepath: "/admin",
logo_return_path: "/../",
},
options: {
// the seed option is only executed if the database was empty
seed: async (ctx) => {
await ctx.em.mutator("todos").insertMany([
{ title: "Learn bknd", done: true },
{ title: "Build something cool", done: false },
]);
const connection = new SQLocalConnection(
new SQLocalKysely({
databasePath: ":localStorage:",
verbose: true,
}),
// @todo: auth is currently not working due to POST request
await ctx.app.module.auth.createUser({
email: "test@bknd.io",
password: "12345678",
});
},
},
} satisfies BrowserBkndConfig;
export default function App() {
return (
<BkndBrowserApp {...config}>
<Route path="/" component={IndexPage} />
</BkndBrowserApp>
);
registries.media.register("opfs", OpfsStorageAdapter);
const app = App.create({
connection,
// an initial config is only applied if the database is empty
config: {
data: schema.toJSON(),
auth: {
enabled: true,
jwt: {
secret: "secret",
},
},
},
options: {
// the seed option is only executed if the database was empty
seed: async (ctx) => {
await ctx.em.mutator("todos").insertMany([
{ title: "Learn bknd", done: true },
{ title: "Build something cool", done: false },
]);
// @todo: auth is currently not working due to POST request
await ctx.app.module.auth.createUser({
email: "test@bknd.io",
password: "12345678",
});
},
},
});
if (opts?.onBuilt) {
app.emgr.onEvent(
App.Events.AppBuiltEvent,
async () => {
await opts.onBuilt?.(app);
// @ts-ignore
window.sql = app.connection.client.sql;
},
"sync",
);
}
await opts?.beforeBuild?.(app);
await app.build({ sync: true });
return app;
}