updated nextjs example with app router

This commit is contained in:
dswbx
2025-02-27 20:09:03 +01:00
parent 2d5da63eb2
commit 09074f6591
36 changed files with 334 additions and 284 deletions

View File

@@ -48,7 +48,7 @@ describe("App", () => {
const todos = await app.getApi().data.readMany("todos");
expect(todos.length).toBe(2);
expect(todos[0].title).toBe("ctx");
expect(todos[1].title).toBe("api");
expect(todos[0]?.title).toBe("ctx");
expect(todos[1]?.title).toBe("api");
});
});

View File

@@ -71,16 +71,19 @@ async function buildApi() {
});
}
async function rewriteClient(path: string) {
const bundle = await Bun.file(path).text();
await Bun.write(path, '"use client";\n' + bundle.replaceAll("ui/client", "bknd/client"));
}
/**
* Building UI for direct imports
*/
async function buildUi() {
await tsup.build({
const base = {
minify,
sourcemap,
watch,
entry: ["src/ui/index.ts", "src/ui/client/index.ts", "src/ui/main.css", "src/ui/styles.css"],
outDir: "dist/ui",
external: [
"bun:test",
"react",
@@ -104,7 +107,24 @@ async function buildUi() {
esbuildOptions: (options) => {
options.logLevel = "silent";
},
} satisfies tsup.Options;
await tsup.build({
...base,
entry: ["src/ui/index.ts", "src/ui/main.css", "src/ui/styles.css"],
outDir: "dist/ui",
onSuccess: async () => {
await rewriteClient("./dist/ui/index.js");
delayTypes();
},
});
await tsup.build({
...base,
entry: ["src/ui/client/index.ts"],
outDir: "dist/ui/client",
onSuccess: async () => {
await rewriteClient("./dist/ui/client/index.js");
delayTypes();
},
});
@@ -146,11 +166,7 @@ async function buildUiElements() {
};
},
onSuccess: async () => {
// manually replace ui/client with bknd/client
const path = "./dist/ui/elements/index.js";
const bundle = await Bun.file(path).text();
await Bun.write(path, bundle.replaceAll("ui/client", "bknd/client"));
await rewriteClient("./dist/ui/elements/index.js");
delayTypes();
},
});

View File

@@ -3,7 +3,7 @@
"type": "module",
"sideEffects": false,
"bin": "./dist/cli/index.js",
"version": "0.9.0-rc.1",
"version": "0.9.0-rc.1-7",
"description": "Lightweight Firebase/Supabase alternative built to run anywhere — incl. Next.js, Remix, Astro, Cloudflare, Bun, Node, AWS Lambda & more.",
"homepage": "https://bknd.io",
"repository": {

View File

@@ -1,4 +1,3 @@
import { Api, type ApiOptions } from "Api";
import type { CreateUserPayload } from "auth/AppAuth";
import { $console } from "core";
import { Event } from "core/events";
@@ -188,12 +187,10 @@ export class App {
return this.module.auth.createUser(p);
}
async getApi(options?: LocalApiOptions) {
getApi(options?: LocalApiOptions) {
const fetcher = this.server.request as typeof fetch;
if (options && options instanceof Request) {
const api = new Api({ request: options, headers: options.headers, fetcher });
await api.verifyAuth();
return api;
return new Api({ request: options, headers: options.headers, fetcher });
}
return new Api({ host: "http://localhost", ...(options ?? {}), fetcher });

View File

@@ -54,9 +54,9 @@ function AdminInternal() {
);
}
const Skeleton = ({ theme }: { theme?: string }) => {
const actualTheme =
(theme ?? document.querySelector("html")?.classList.contains("light")) ? "light" : "dark";
const Skeleton = ({ theme }: { theme?: any }) => {
const t = useTheme();
const actualTheme = theme ?? t.theme;
return (
<div id="bknd-admin" className={actualTheme + " antialiased"}>

View File

@@ -2,7 +2,7 @@ import type { AppTheme } from "modules/server/AppServer";
import { useBkndWindowContext } from "ui/client/ClientProvider";
import { useBknd } from "ui/client/bknd";
export function useTheme(fallback: AppTheme = "system"): { theme: AppTheme } {
export function useTheme(fallback: AppTheme = "system") {
const b = useBknd();
const winCtx = useBkndWindowContext();
@@ -14,13 +14,16 @@ export function useTheme(fallback: AppTheme = "system"): { theme: AppTheme } {
const override = b?.adminOverride?.color_scheme;
const config = b?.config.server.admin.color_scheme;
const win = winCtx.color_scheme;
const prefersDark = window.matchMedia("(prefers-color-scheme: dark)").matches;
const prefersDark =
typeof window !== "undefined" && window.matchMedia("(prefers-color-scheme: dark)").matches;
const theme = override ?? config ?? win ?? fallback;
if (theme === "system") {
return { theme: prefersDark ? "dark" : "light" };
}
return { theme };
return {
theme: (theme === "system" ? (prefersDark ? "dark" : "light") : theme) as AppTheme,
prefersDark,
override,
config,
win,
};
}

View File

@@ -4,15 +4,15 @@
// there is no lifecycle or Hook in React that we can use to switch
// .current at the right timing."
// So we will have to make do with this "close enough" approach for now.
import { useInsertionEffect, useRef } from "react";
import { useEffect, useRef } from "react";
export const useEvent = <Fn>(fn: Fn | ((...args: any[]) => any) | undefined): Fn => {
const ref = useRef([fn, (...args) => ref[0](...args)]).current;
// Per Dan Abramov: useInsertionEffect executes marginally closer to the
// correct timing for ref synchronization than useLayoutEffect on React 18.
// See: https://github.com/facebook/react/pull/25881#issuecomment-1356244360
useInsertionEffect(() => {
useEffect(() => {
ref[0] = fn;
});
}, []);
return ref[1];
};

View File

@@ -45,7 +45,6 @@ export function StepEntityFields() {
const values = watch();
const updateListener = useEvent((data: TAppDataEntityFields) => {
console.log("updateListener", data);
setValue("fields", data as any);
});

View File

@@ -2,7 +2,7 @@ import { Type } from "core/utils";
import { type Entity, querySchema } from "data";
import { Fragment } from "react";
import { TbDots } from "react-icons/tb";
import { useApi, useApiQuery } from "ui/client";
import { useApiQuery } from "ui/client";
import { useBknd } from "ui/client/bknd";
import { useBkndData } from "ui/client/schema/data/use-bknd-data";
import { Button } from "ui/components/buttons/Button";
@@ -83,7 +83,7 @@ export function DataEntityList({ params }) {
search.set("perPage", perPage);
}
const isUpdating = $q.isLoading && $q.isValidating;
const isUpdating = $q.isLoading || $q.isValidating;
return (
<Fragment key={entity.name}>

View File

@@ -108,9 +108,7 @@ export const EntityFieldsForm = forwardRef<EntityFieldsFormRef, EntityFieldsForm
useEffect(() => {
if (props?.onChange) {
console.log("----set");
watch((data: any) => {
console.log("---calling");
props?.onChange?.(toCleanValues(data));
});
}