From 2b32a836cd591437e4998b5c365da16159e1bd3e Mon Sep 17 00:00:00 2001 From: dswbx Date: Wed, 9 Jul 2025 08:00:52 +0200 Subject: [PATCH 1/5] auth error should not redirect to parameter, adapter app function should fire correctly --- app/src/adapter/index.ts | 2 +- app/src/auth/authenticate/Authenticator.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/adapter/index.ts b/app/src/adapter/index.ts index 9e74dd01..ebab187d 100644 --- a/app/src/adapter/index.ts +++ b/app/src/adapter/index.ts @@ -70,7 +70,7 @@ export async function createAdapterApp = Record< await addFlashMessage(c, String(error), "error"); - const referer = this.getSafeUrl(c, opts?.redirect ?? c.req.header("Referer") ?? "/"); + const referer = this.getSafeUrl(c, c.req.header("Referer") ?? "/"); return c.redirect(referer); } From 2ca1f64ae6f9e77c23bae10674278b4f9c715193 Mon Sep 17 00:00:00 2001 From: dswbx Date: Wed, 9 Jul 2025 08:13:32 +0200 Subject: [PATCH 2/5] fix json field form, replaced auth form --- app/package.json | 5 +- app/src/modules/ModuleApi.ts | 8 ++- .../ui/components/display/ErrorBoundary.tsx | 10 +-- .../form/native-form/NativeForm.tsx | 1 - app/src/ui/elements/auth/AuthForm.tsx | 66 ++++++------------- .../routes/data/forms/entity.fields.form.tsx | 47 +++++++++---- 6 files changed, 68 insertions(+), 69 deletions(-) diff --git a/app/package.json b/app/package.json index 95d2a495..746e016b 100644 --- a/app/package.json +++ b/app/package.json @@ -60,8 +60,7 @@ "bcryptjs": "^3.0.2", "dayjs": "^1.11.13", "fast-xml-parser": "^5.0.8", - "hono": "^4.7.11", - "json-schema-form-react": "^0.0.2", + "hono": "4.8.3", "json-schema-library": "10.0.0-rc7", "json-schema-to-ts": "^3.1.1", "kysely": "^0.27.6", @@ -100,7 +99,7 @@ "dotenv": "^16.4.7", "jotai": "^2.12.2", "jsdom": "^26.0.0", - "jsonv-ts": "^0.2.2", + "jsonv-ts": "link:jsonv-ts", "kysely-d1": "^0.3.0", "kysely-generic-sqlite": "^1.2.1", "libsql-stateless-easy": "^1.8.0", diff --git a/app/src/modules/ModuleApi.ts b/app/src/modules/ModuleApi.ts index f8a295c4..d8955b8c 100644 --- a/app/src/modules/ModuleApi.ts +++ b/app/src/modules/ModuleApi.ts @@ -1,5 +1,5 @@ import type { PrimaryFieldType } from "core"; -import { $console } from "core/utils"; +import { $console, isPlainObject } from "core/utils"; import { isDebug } from "core/env"; import { encodeSearch } from "core/utils/reqres"; import type { ApiFetcher } from "Api"; @@ -95,7 +95,11 @@ export abstract class ModuleApi { private renderFallback() { if (this.props.fallback) { - return typeof this.props.fallback === "function" - ? this.props.fallback({ error: this.state.error!, resetError: this.resetError }) - : this.props.fallback; + return typeof this.props.fallback === "function" ? ( + this.props.fallback({ error: this.state.error!, resetError: this.resetError }) + ) : ( + {this.props.fallback} + ); } - return Error; + return Error1; } override render() { diff --git a/app/src/ui/components/form/native-form/NativeForm.tsx b/app/src/ui/components/form/native-form/NativeForm.tsx index 15b6d5cd..17cc6497 100644 --- a/app/src/ui/components/form/native-form/NativeForm.tsx +++ b/app/src/ui/components/form/native-form/NativeForm.tsx @@ -6,7 +6,6 @@ import { useRef, useState, } from "react"; -import { useEvent } from "ui/hooks/use-event"; import { type CleanOptions, type InputElement, diff --git a/app/src/ui/elements/auth/AuthForm.tsx b/app/src/ui/elements/auth/AuthForm.tsx index 4f22b170..0865317f 100644 --- a/app/src/ui/elements/auth/AuthForm.tsx +++ b/app/src/ui/elements/auth/AuthForm.tsx @@ -1,20 +1,11 @@ import type { AppAuthOAuthStrategy, AppAuthSchema } from "auth/auth-schema"; import clsx from "clsx"; -import { Form } from "json-schema-form-react"; +import { NativeForm } from "ui/components/form/native-form/NativeForm"; import { transform } from "lodash-es"; import type { ComponentPropsWithoutRef } from "react"; import { Button } from "ui/components/buttons/Button"; import { Group, Input, Password, Label } from "ui/components/form/Formy/components"; import { SocialLink } from "./SocialLink"; -import type { Validator } from "json-schema-form-react"; -import { s } from "bknd/core"; -import type { ErrorDetail } from "jsonv-ts"; - -class JsonvTsValidator implements Validator { - async validate(schema: s.Schema, data: any) { - return schema.validate(data).errors; - } -} export type LoginFormProps = Omit, "onSubmit" | "action"> & { className?: string; @@ -25,16 +16,6 @@ export type LoginFormProps = Omit, "onSubmit" | buttonLabel?: string; }; -const validator = new JsonvTsValidator(); -const schema = s.strictObject({ - email: s.string({ - pattern: "^[\\w-\\.]+@([\\w-]+\\.)+[\\w-]{2,4}$", - }), - password: s.string({ - minLength: 8, // @todo: this should be configurable - }), -}); - export function AuthForm({ formData, className, @@ -79,38 +60,31 @@ export function AuthForm({ )} -
- {({ errors, submitting }) => ( - <> - - - - - - - - + + + + + + + + - - - )} -
+ + ); } diff --git a/app/src/ui/routes/data/forms/entity.fields.form.tsx b/app/src/ui/routes/data/forms/entity.fields.form.tsx index 60be4c8e..ee8bf76e 100644 --- a/app/src/ui/routes/data/forms/entity.fields.form.tsx +++ b/app/src/ui/routes/data/forms/entity.fields.form.tsx @@ -23,6 +23,7 @@ import { MantineSelect } from "ui/components/form/hook-form-mantine/MantineSelec import type { TPrimaryFieldFormat } from "data/fields/PrimaryField"; import { s, stringIdentifier } from "bknd/core"; import { standardSchemaResolver } from "@hookform/resolvers/standard-schema"; +import ErrorBoundary from "ui/components/display/ErrorBoundary"; const fieldsSchemaObject = originalFieldsSchemaObject; const fieldsSchema = s.anyOf(Object.values(fieldsSchemaObject)); @@ -453,19 +454,17 @@ function EntityField({
- { - setValue(`${prefix}.config`, { - ...getValues([`fields.${index}.config`])[0], - ...value, - }); - }} - /> + + { + setValue(`${prefix}.config`, { + ...getValues([`fields.${index}.config`])[0], + ...value, + }); + }} + /> +
@@ -490,3 +489,25 @@ function EntityField({ ); } + +const SpecificForm = ({ + field, + onChange, +}: { + field: FieldArrayWithId; + onChange: (value: any) => void; +}) => { + const type = field.field.type; + const specificData = omit(field.field.config, commonProps); + + return ( + + ); +}; From a9efee35553298f73332c36d4842ac0c021bf409 Mon Sep 17 00:00:00 2001 From: dswbx Date: Wed, 9 Jul 2025 08:13:42 +0200 Subject: [PATCH 3/5] initial waku --- bun.lock | 11 ++- examples/waku/.gitignore | 7 ++ examples/waku/bknd-types.d.ts | 15 ++++ examples/waku/bknd.config.ts | 64 +++++++++++++++++ examples/waku/package.json | 26 +++++++ examples/waku/postcss.config.js | 5 ++ examples/waku/public/images/favicon.png | Bin 0 -> 5713 bytes examples/waku/public/robots.txt | 2 + examples/waku/src/bknd/admin/impl.tsx | 23 ++++++ examples/waku/src/bknd/admin/index.tsx | 18 +++++ examples/waku/src/bknd/index.ts | 22 ++++++ examples/waku/src/components/counter.tsx | 21 ++++++ examples/waku/src/components/footer.tsx | 18 +++++ examples/waku/src/components/header.tsx | 11 +++ examples/waku/src/lib/waku/client.tsx | 20 ++++++ examples/waku/src/lib/waku/server.ts | 26 +++++++ examples/waku/src/pages.gen.ts | 30 ++++++++ examples/waku/src/pages/_layout.tsx | 28 ++++++++ examples/waku/src/pages/about.tsx | 32 +++++++++ examples/waku/src/pages/admin/[...admin].tsx | 32 +++++++++ examples/waku/src/pages/admin/index.tsx | 6 ++ examples/waku/src/pages/api/[...api].ts | 5 ++ examples/waku/src/pages/index.tsx | 72 +++++++++++++++++++ examples/waku/src/pages/login.tsx | 9 +++ examples/waku/src/pages/test.tsx | 58 +++++++++++++++ examples/waku/src/styles.css | 3 + examples/waku/tsconfig.json | 17 +++++ 27 files changed, 575 insertions(+), 6 deletions(-) create mode 100644 examples/waku/.gitignore create mode 100644 examples/waku/bknd-types.d.ts create mode 100644 examples/waku/bknd.config.ts create mode 100644 examples/waku/package.json create mode 100644 examples/waku/postcss.config.js create mode 100644 examples/waku/public/images/favicon.png create mode 100644 examples/waku/public/robots.txt create mode 100644 examples/waku/src/bknd/admin/impl.tsx create mode 100644 examples/waku/src/bknd/admin/index.tsx create mode 100644 examples/waku/src/bknd/index.ts create mode 100644 examples/waku/src/components/counter.tsx create mode 100644 examples/waku/src/components/footer.tsx create mode 100644 examples/waku/src/components/header.tsx create mode 100644 examples/waku/src/lib/waku/client.tsx create mode 100644 examples/waku/src/lib/waku/server.ts create mode 100644 examples/waku/src/pages.gen.ts create mode 100644 examples/waku/src/pages/_layout.tsx create mode 100644 examples/waku/src/pages/about.tsx create mode 100644 examples/waku/src/pages/admin/[...admin].tsx create mode 100644 examples/waku/src/pages/admin/index.tsx create mode 100644 examples/waku/src/pages/api/[...api].ts create mode 100644 examples/waku/src/pages/index.tsx create mode 100644 examples/waku/src/pages/login.tsx create mode 100644 examples/waku/src/pages/test.tsx create mode 100644 examples/waku/src/styles.css create mode 100644 examples/waku/tsconfig.json diff --git a/bun.lock b/bun.lock index a002cc6e..ec7c7bda 100644 --- a/bun.lock +++ b/bun.lock @@ -15,7 +15,7 @@ }, "app": { "name": "bknd", - "version": "0.15.0", + "version": "0.16.0-rc.0", "bin": "./dist/cli/index.js", "dependencies": { "@cfworker/json-schema": "^4.1.1", @@ -32,7 +32,7 @@ "bcryptjs": "^3.0.2", "dayjs": "^1.11.13", "fast-xml-parser": "^5.0.8", - "hono": "^4.7.11", + "hono": "4.8.3", "json-schema-form-react": "^0.0.2", "json-schema-library": "10.0.0-rc7", "json-schema-to-ts": "^3.1.1", @@ -72,7 +72,7 @@ "dotenv": "^16.4.7", "jotai": "^2.12.2", "jsdom": "^26.0.0", - "jsonv-ts": "^0.2.2", + "jsonv-ts": "link:jsonv-ts", "kysely-d1": "^0.3.0", "kysely-generic-sqlite": "^1.2.1", "libsql-stateless-easy": "^1.8.0", @@ -124,7 +124,6 @@ "version": "0.5.1", "devDependencies": { "@types/bun": "latest", - "bknd": "workspace:*", "tsdx": "^0.14.1", "typescript": "^5.0.0", }, @@ -2208,7 +2207,7 @@ "headers-polyfill": ["headers-polyfill@4.0.3", "", {}, "sha512-IScLbePpkvO846sIwOtOTDjutRMWdXdJmXdMvk6gCBHxFO8d+QKOQedyZSxFTTFYRSmlgSTDtXqqq4pcenBXLQ=="], - "hono": ["hono@4.7.11", "", {}, "sha512-rv0JMwC0KALbbmwJDEnxvQCeJh+xbS3KEWW5PC9cMJ08Ur9xgatI0HmtgYZfOdOSOeYsp5LO2cOhdI8cLEbDEQ=="], + "hono": ["hono@4.8.3", "", {}, "sha512-jYZ6ZtfWjzBdh8H/0CIFfCBHaFL75k+KMzaM177hrWWm2TWL39YMYaJgB74uK/niRc866NMlH9B8uCvIo284WQ=="], "hosted-git-info": ["hosted-git-info@2.8.9", "", {}, "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw=="], @@ -2500,7 +2499,7 @@ "jsonpointer": ["jsonpointer@5.0.1", "", {}, "sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ=="], - "jsonv-ts": ["jsonv-ts@0.2.2", "", { "optionalDependencies": { "hono": "4.7.11" }, "peerDependencies": { "typescript": "^5.0.0" } }, "sha512-g4kUDYYPBb32Nfbv3R55Se6G4CAv4UOXOPJIEPqw8XX+0SSy44T/AREkwFfGz9GwYuX9UxcvOILbgac2nshL4A=="], + "jsonv-ts": ["jsonv-ts@link:jsonv-ts", {}], "jsonwebtoken": ["jsonwebtoken@9.0.2", "", { "dependencies": { "jws": "^3.2.2", "lodash.includes": "^4.3.0", "lodash.isboolean": "^3.0.3", "lodash.isinteger": "^4.0.4", "lodash.isnumber": "^3.0.3", "lodash.isplainobject": "^4.0.6", "lodash.isstring": "^4.0.1", "lodash.once": "^4.0.0", "ms": "^2.1.1", "semver": "^7.5.4" } }, "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ=="], diff --git a/examples/waku/.gitignore b/examples/waku/.gitignore new file mode 100644 index 00000000..ad583432 --- /dev/null +++ b/examples/waku/.gitignore @@ -0,0 +1,7 @@ +node_modules +dist +.env* +*.tsbuildinfo +.cache +.DS_Store +*.pem diff --git a/examples/waku/bknd-types.d.ts b/examples/waku/bknd-types.d.ts new file mode 100644 index 00000000..91125535 --- /dev/null +++ b/examples/waku/bknd-types.d.ts @@ -0,0 +1,15 @@ +import type { DB } from "bknd/core"; +import type { Insertable, Selectable, Updateable, Generated } from "kysely"; + +declare global { + type BkndEntity = Selectable; + type BkndEntityCreate = Insertable; + type BkndEntityUpdate = Updateable; +} + +interface Database { +} + +declare module "bknd/core" { + interface DB extends Database {} +} \ No newline at end of file diff --git a/examples/waku/bknd.config.ts b/examples/waku/bknd.config.ts new file mode 100644 index 00000000..4cf18aab --- /dev/null +++ b/examples/waku/bknd.config.ts @@ -0,0 +1,64 @@ +import { registerLocalMediaAdapter } from "bknd/adapter/node"; +import type { BkndConfig } from "bknd/adapter"; +import { boolean, em, entity, text } from "bknd/data"; +import { secureRandomString } from "bknd/utils"; + +// since we're running in node, we can register the local media adapter +const local = registerLocalMediaAdapter(); + +const schema = em({ + todos: entity("todos", { + title: text(), + done: boolean(), + }), +}); + +// register your schema to get automatic type completion +type Database = (typeof schema)["DB"]; +declare module "bknd/core" { + interface DB extends Database {} +} + +export default { + // we can use any libsql config, and if omitted, uses in-memory + app: (env) => ({ + connection: { + url: env?.DB_URL ?? "file:data.db", + }, + }), + // an initial config is only applied if the database is empty + initialConfig: { + data: schema.toJSON(), + // we're enabling auth ... + auth: { + enabled: true, + jwt: { + issuer: "bknd-remix-example", + secret: secureRandomString(64), + }, + }, + // ... and media + media: { + enabled: true, + adapter: local({ + path: "./public/uploads", + }), + }, + }, + options: { + // the seed option is only executed if the database was empty + seed: async (ctx) => { + // create some entries + await ctx.em.mutator("todos").insertMany([ + { title: "Learn bknd", done: true }, + { title: "Build something cool", done: false }, + ]); + + // and create a user + await ctx.app.module.auth.createUser({ + email: "test@bknd.io", + password: "12345678", + }); + }, + }, +} as const satisfies BkndConfig<{ DB_URL?: string }>; diff --git a/examples/waku/package.json b/examples/waku/package.json new file mode 100644 index 00000000..1ce31e49 --- /dev/null +++ b/examples/waku/package.json @@ -0,0 +1,26 @@ +{ + "name": "waku", + "version": "0.0.0", + "type": "module", + "private": true, + "scripts": { + "dev": "waku dev", + "build": "waku build", + "start": "waku start" + }, + "dependencies": { + "react": "19.0.0", + "react-dom": "19.0.0", + "react-server-dom-webpack": "19.0.0", + "waku": "0.23.3", + "bknd": "file:../../app" + }, + "devDependencies": { + "@tailwindcss/postcss": "4.1.10", + "@types/react": "19.1.8", + "@types/react-dom": "19.1.6", + "postcss": "8.5.6", + "tailwindcss": "4.1.10", + "typescript": "5.8.3" + } +} diff --git a/examples/waku/postcss.config.js b/examples/waku/postcss.config.js new file mode 100644 index 00000000..a34a3d56 --- /dev/null +++ b/examples/waku/postcss.config.js @@ -0,0 +1,5 @@ +export default { + plugins: { + '@tailwindcss/postcss': {}, + }, +}; diff --git a/examples/waku/public/images/favicon.png b/examples/waku/public/images/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..cd90d79081c0c40519bb3c583f5b43357b3fcd3b GIT binary patch literal 5713 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 DW2Q3= literal 0 HcmV?d00001 diff --git a/examples/waku/public/robots.txt b/examples/waku/public/robots.txt new file mode 100644 index 00000000..b4d27bb5 --- /dev/null +++ b/examples/waku/public/robots.txt @@ -0,0 +1,2 @@ +User-agent: * +Disallow: /RSC/ \ No newline at end of file diff --git a/examples/waku/src/bknd/admin/impl.tsx b/examples/waku/src/bknd/admin/impl.tsx new file mode 100644 index 00000000..e1c9afd0 --- /dev/null +++ b/examples/waku/src/bknd/admin/impl.tsx @@ -0,0 +1,23 @@ +"use client"; + +import { Admin, type BkndAdminProps } from "bknd/ui"; + +export const AdminImpl = (props: BkndAdminProps) => { + if (typeof window === "undefined") { + return null; + } + + return ( + + ); +}; + +export default AdminImpl; diff --git a/examples/waku/src/bknd/admin/index.tsx b/examples/waku/src/bknd/admin/index.tsx new file mode 100644 index 00000000..fe7643d9 --- /dev/null +++ b/examples/waku/src/bknd/admin/index.tsx @@ -0,0 +1,18 @@ +"use client"; + +import type { BkndAdminProps } from "bknd/ui"; +import { lazy } from "react"; +import { BrowserOnly } from "../../lib/waku/client"; + +const AdminImpl = import.meta.env.SSR ? undefined : lazy(() => import("./impl")); + +export const AdminLoader = (props: BkndAdminProps) => { + return ( + + {/* @ts-expect-error */} + + + ); +}; + +export default AdminLoader; diff --git a/examples/waku/src/bknd/index.ts b/examples/waku/src/bknd/index.ts new file mode 100644 index 00000000..5f9d4ae6 --- /dev/null +++ b/examples/waku/src/bknd/index.ts @@ -0,0 +1,22 @@ +import { createFrameworkApp } from "bknd/adapter"; +import config from "../../bknd.config"; + +export async function getApp() { + return await createFrameworkApp(config, process.env, { + force: import.meta.env && !import.meta.env.PROD, + }); +} + +export async function getApi(opts?: { + headers?: Headers | any; + verify?: boolean; +}) { + const app = await getApp(); + if (opts?.verify && opts.headers) { + const api = app.getApi({ headers: opts.headers }); + await api.verifyAuth(); + return api; + } + + return app.getApi(); +} diff --git a/examples/waku/src/components/counter.tsx b/examples/waku/src/components/counter.tsx new file mode 100644 index 00000000..0e540b8b --- /dev/null +++ b/examples/waku/src/components/counter.tsx @@ -0,0 +1,21 @@ +'use client'; + +import { useState } from 'react'; + +export const Counter = () => { + const [count, setCount] = useState(0); + + const handleIncrement = () => setCount((c) => c + 1); + + return ( +
+
Count: {count}
+ +
+ ); +}; diff --git a/examples/waku/src/components/footer.tsx b/examples/waku/src/components/footer.tsx new file mode 100644 index 00000000..8cfd9c89 --- /dev/null +++ b/examples/waku/src/components/footer.tsx @@ -0,0 +1,18 @@ +export const Footer = () => { + return ( + + ); +}; diff --git a/examples/waku/src/components/header.tsx b/examples/waku/src/components/header.tsx new file mode 100644 index 00000000..1b03ba54 --- /dev/null +++ b/examples/waku/src/components/header.tsx @@ -0,0 +1,11 @@ +import { Link } from 'waku'; + +export const Header = () => { + return ( +
+

+ Waku starter +

+
+ ); +}; diff --git a/examples/waku/src/lib/waku/client.tsx b/examples/waku/src/lib/waku/client.tsx new file mode 100644 index 00000000..2d889c97 --- /dev/null +++ b/examples/waku/src/lib/waku/client.tsx @@ -0,0 +1,20 @@ +"use client"; + +import * as React from "react"; + +export function BrowserOnly(props: React.SuspenseProps) { + const hydrated = useHydrated(); + if (!hydrated) { + return props.fallback; + } + return ; +} + +const noopStore = () => () => {}; + +const useHydrated = () => + React.useSyncExternalStore( + noopStore, + () => true, + () => false + ); diff --git a/examples/waku/src/lib/waku/server.ts b/examples/waku/src/lib/waku/server.ts new file mode 100644 index 00000000..608aa26e --- /dev/null +++ b/examples/waku/src/lib/waku/server.ts @@ -0,0 +1,26 @@ +"use server"; + +import { getContext } from "waku/middleware/context"; +import { getApi } from "../../bknd"; + +export { unstable_rerenderRoute as rerender } from "waku/router/server"; + +export function context() { + return getContext(); +} + +export function handlerReq() { + return getContext().req; +} + +export function headers() { + const context = getContext(); + return new Headers(context.req.headers); +} + +export async function getUserApi(opts?: { verify?: boolean }) { + return await getApi({ + headers: headers(), + verify: !!opts?.verify, + }); +} diff --git a/examples/waku/src/pages.gen.ts b/examples/waku/src/pages.gen.ts new file mode 100644 index 00000000..a3c9fb7f --- /dev/null +++ b/examples/waku/src/pages.gen.ts @@ -0,0 +1,30 @@ +// deno-fmt-ignore-file +// biome-ignore format: generated types do not need formatting +// prettier-ignore +import type { PathsForPages, GetConfigResponse } from 'waku/router'; + +// prettier-ignore +import type { getConfig as File_About_getConfig } from './pages/about'; +// prettier-ignore +import type { getConfig as File_AdminAdmin_getConfig } from './pages/admin/[...admin]'; +// prettier-ignore +import type { getConfig as File_Index_getConfig } from './pages/index'; + +// prettier-ignore +type Page = +| ({ path: '/about' } & GetConfigResponse) +| ({ path: '/admin/[...admin]' } & GetConfigResponse) +| { path: '/admin'; render: 'dynamic' } +| ({ path: '/' } & GetConfigResponse) +| { path: '/login'; render: 'dynamic' } +| { path: '/test'; render: 'dynamic' }; + +// prettier-ignore +declare module 'waku/router' { + interface RouteConfig { + paths: PathsForPages; + } + interface CreatePagesConfig { + pages: Page; + } +} diff --git a/examples/waku/src/pages/_layout.tsx b/examples/waku/src/pages/_layout.tsx new file mode 100644 index 00000000..2e3c7e93 --- /dev/null +++ b/examples/waku/src/pages/_layout.tsx @@ -0,0 +1,28 @@ +import "../styles.css"; + +import type { ReactNode } from "react"; + +import { ClientProvider } from "bknd/client"; + +type RootLayoutProps = { children: ReactNode }; + +export default async function RootLayout({ children }: RootLayoutProps) { + const data = await getData(); + + return children; +} + +const getData = async () => { + const data = { + description: "An internet website!", + icon: "/images/favicon.png", + }; + + return data; +}; + +export const getConfig = async () => { + return { + render: "static", + } as const; +}; diff --git a/examples/waku/src/pages/about.tsx b/examples/waku/src/pages/about.tsx new file mode 100644 index 00000000..15d4c90e --- /dev/null +++ b/examples/waku/src/pages/about.tsx @@ -0,0 +1,32 @@ +import { Link } from 'waku'; + +export default async function AboutPage() { + const data = await getData(); + + return ( +
+ {data.title} +

{data.headline}

+

{data.body}

+ + Return home + +
+ ); +} + +const getData = async () => { + const data = { + title: 'About', + headline: 'About Waku', + body: 'The minimal React framework', + }; + + return data; +}; + +export const getConfig = async () => { + return { + render: 'static', + } as const; +}; diff --git a/examples/waku/src/pages/admin/[...admin].tsx b/examples/waku/src/pages/admin/[...admin].tsx new file mode 100644 index 00000000..72db1a23 --- /dev/null +++ b/examples/waku/src/pages/admin/[...admin].tsx @@ -0,0 +1,32 @@ +/** + * See https://github.com/wakujs/waku/issues/1499 + */ + +import { Suspense, lazy } from "react"; +import { getUserApi } from "../../lib/waku/server"; + +const AdminComponent = lazy(() => import("../../bknd/admin")); + +export default async function HomePage() { + const api = await getUserApi({ verify: true }); + + // @ts-ignore + const styles = await import("bknd/dist/styles.css?inline").then((m) => m.default); + return ( + <> + + + + + + ); +} + +// Enable dynamic server rendering. +// Static rendering is possible if you want to render at build time. +// The Hono context will not be available. +export const getConfig = async () => { + return { + render: "dynamic", + } as const; +}; diff --git a/examples/waku/src/pages/admin/index.tsx b/examples/waku/src/pages/admin/index.tsx new file mode 100644 index 00000000..7d32f6fe --- /dev/null +++ b/examples/waku/src/pages/admin/index.tsx @@ -0,0 +1,6 @@ +import { unstable_redirect as redirect } from "waku/router/server"; + +export default async function AdminIndex() { + await new Promise((resolve) => setTimeout(resolve, 100)); + redirect("/admin/data"); +} diff --git a/examples/waku/src/pages/api/[...api].ts b/examples/waku/src/pages/api/[...api].ts new file mode 100644 index 00000000..eaf8bb35 --- /dev/null +++ b/examples/waku/src/pages/api/[...api].ts @@ -0,0 +1,5 @@ +import { getApp } from "../../bknd"; + +export default async function handler(request: Request) { + return (await getApp()).fetch(request); +} diff --git a/examples/waku/src/pages/index.tsx b/examples/waku/src/pages/index.tsx new file mode 100644 index 00000000..d4c0c2cc --- /dev/null +++ b/examples/waku/src/pages/index.tsx @@ -0,0 +1,72 @@ +import { Link } from "waku"; + +import { Counter } from "../components/counter"; +import { getUserApi, rerender } from "../lib/waku/server"; + +async function toggleTodo(todo: any, path: string) { + "use server"; + console.log("toggleTodo", todo, path); + const api = await getUserApi(); + await api.data.updateOne("todos", todo.id, { + done: !todo.done, + }); + console.log("rerender"); + rerender(path); +} + +export default async function HomePage({ path }: any) { + const api = await getUserApi({ verify: true }); + const todos = await api.data.readMany("todos"); + const user = api.getUser(); + const data = await getData(); + + return ( +
+ {data.title} +

{data.headline}

+

{data.body}

+
    + {todos?.map((todo) => ( +
  • + {todo.title} {todo.done ? "✅" : "❌"} +
    + +
    +
  • + ))} +
+ + + About page + + {user ? ( + + Logout ({user.email}) + + ) : ( + + Login + + )} + + Admin + +
+ ); +} + +const getData = async () => { + const data = { + title: "Waku", + headline: "Waku", + body: "Hello world!", + }; + + return data; +}; + +export const getConfig = async () => { + return { + render: "dynamic", + } as const; +}; diff --git a/examples/waku/src/pages/login.tsx b/examples/waku/src/pages/login.tsx new file mode 100644 index 00000000..db41ac0a --- /dev/null +++ b/examples/waku/src/pages/login.tsx @@ -0,0 +1,9 @@ +export default function LoginPage() { + return ( +
+ + + +
+ ); +} diff --git a/examples/waku/src/pages/test.tsx b/examples/waku/src/pages/test.tsx new file mode 100644 index 00000000..d74ce923 --- /dev/null +++ b/examples/waku/src/pages/test.tsx @@ -0,0 +1,58 @@ +"use client"; + +import { useEffect } from "react"; + +export default function Test() { + useEffect(() => { + bridge(); + }, []); + return null; +} + +async function bridge() { + const aud = new URLSearchParams(location.search).get("aud") || ""; + // 1. Verify the user still has an auth cookie + const me = await fetch("/api/auth/me", { credentials: "include" }); + console.log("sso-bridge:me", me); + if (!me.ok) { + console.log("sso-bridge:no session"); + parent.postMessage({ type: "NOSESSION" }, aud); + } else { + console.log("sso-bridge:session"); + + // 2. Get short-lived JWT (internal endpoint, same origin) + const res = await fetch("/api/issue-jwt", { + credentials: "include", + headers: { "Content-Type": "application/json" }, + method: "POST", + body: JSON.stringify({ + aud, + }), + }); + console.log("sso-bridge:res", res); + const { jwt, exp } = (await res.json()) as any; // exp = unix timestamp seconds + console.log("sso-bridge:jwt", { jwt, exp }); + + // 3. Send token up + parent.postMessage({ type: "JWT", jwt, exp }, aud); + + // 4. Listen for refresh requests + window.addEventListener("message", async (ev) => { + console.log("sso-bridge:message", ev); + if (ev.origin !== aud) return; + if (ev.data !== "REFRESH") return; + console.log("sso-bridge:message:refresh"); + + const r = await fetch("/api/issue-jwt", { + credentials: "include", + method: "POST", + headers: { "content-type": "application/json" }, + body: JSON.stringify({ aud: ev.origin }), + }); + console.log("sso-bridge:message:r", r); + const { jwt, exp } = (await r.json()) as any; + console.log("sso-bridge:message:jwt", { jwt, exp }); + parent.postMessage({ type: "JWT", jwt, exp }, ev.origin); + }); + } +} diff --git a/examples/waku/src/styles.css b/examples/waku/src/styles.css new file mode 100644 index 00000000..f1d3c37c --- /dev/null +++ b/examples/waku/src/styles.css @@ -0,0 +1,3 @@ +@import url('https://fonts.googleapis.com/css2?family=Nunito:ital,wght@0,400;0,700;1,400;1,700&display=swap') +layer(base); +@import 'tailwindcss'; diff --git a/examples/waku/tsconfig.json b/examples/waku/tsconfig.json new file mode 100644 index 00000000..c85d769b --- /dev/null +++ b/examples/waku/tsconfig.json @@ -0,0 +1,17 @@ +{ + "compilerOptions": { + "strict": true, + "target": "esnext", + "noEmit": true, + "isolatedModules": true, + "moduleDetection": "force", + "downlevelIteration": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "bundler", + "skipLibCheck": true, + "noUncheckedIndexedAccess": true, + "exactOptionalPropertyTypes": true, + "jsx": "react-jsx" + } +} From b4d6b1c04dec739540e077e9951a29e442ac8a98 Mon Sep 17 00:00:00 2001 From: dswbx Date: Wed, 9 Jul 2025 08:21:39 +0200 Subject: [PATCH 4/5] finalize waku example --- examples/waku/bknd-types.d.ts | 15 --------------- examples/waku/bknd.config.ts | 10 ++++------ examples/waku/src/{bknd/index.ts => bknd.ts} | 2 +- examples/waku/src/pages/admin/[...admin].tsx | 2 +- .../admin/_components/AdminImpl.tsx} | 0 .../admin/_components/AdminLoader.tsx} | 4 ++-- examples/waku/src/pages/index.tsx | 4 +--- examples/waku/tsconfig.json | 1 + 8 files changed, 10 insertions(+), 28 deletions(-) delete mode 100644 examples/waku/bknd-types.d.ts rename examples/waku/src/{bknd/index.ts => bknd.ts} (92%) rename examples/waku/src/{bknd/admin/impl.tsx => pages/admin/_components/AdminImpl.tsx} (100%) rename examples/waku/src/{bknd/admin/index.tsx => pages/admin/_components/AdminLoader.tsx} (83%) diff --git a/examples/waku/bknd-types.d.ts b/examples/waku/bknd-types.d.ts deleted file mode 100644 index 91125535..00000000 --- a/examples/waku/bknd-types.d.ts +++ /dev/null @@ -1,15 +0,0 @@ -import type { DB } from "bknd/core"; -import type { Insertable, Selectable, Updateable, Generated } from "kysely"; - -declare global { - type BkndEntity = Selectable; - type BkndEntityCreate = Insertable; - type BkndEntityUpdate = Updateable; -} - -interface Database { -} - -declare module "bknd/core" { - interface DB extends Database {} -} \ No newline at end of file diff --git a/examples/waku/bknd.config.ts b/examples/waku/bknd.config.ts index 4cf18aab..28ecf039 100644 --- a/examples/waku/bknd.config.ts +++ b/examples/waku/bknd.config.ts @@ -21,11 +21,9 @@ declare module "bknd/core" { export default { // we can use any libsql config, and if omitted, uses in-memory - app: (env) => ({ - connection: { - url: env?.DB_URL ?? "file:data.db", - }, - }), + connection: { + url: process.env.DB_URL ?? "file:data.db", + }, // an initial config is only applied if the database is empty initialConfig: { data: schema.toJSON(), @@ -33,7 +31,7 @@ export default { auth: { enabled: true, jwt: { - issuer: "bknd-remix-example", + issuer: "bknd-waku-example", secret: secureRandomString(64), }, }, diff --git a/examples/waku/src/bknd/index.ts b/examples/waku/src/bknd.ts similarity index 92% rename from examples/waku/src/bknd/index.ts rename to examples/waku/src/bknd.ts index 5f9d4ae6..577d4b22 100644 --- a/examples/waku/src/bknd/index.ts +++ b/examples/waku/src/bknd.ts @@ -1,5 +1,5 @@ import { createFrameworkApp } from "bknd/adapter"; -import config from "../../bknd.config"; +import config from "../bknd.config"; export async function getApp() { return await createFrameworkApp(config, process.env, { diff --git a/examples/waku/src/pages/admin/[...admin].tsx b/examples/waku/src/pages/admin/[...admin].tsx index 72db1a23..136182bb 100644 --- a/examples/waku/src/pages/admin/[...admin].tsx +++ b/examples/waku/src/pages/admin/[...admin].tsx @@ -5,7 +5,7 @@ import { Suspense, lazy } from "react"; import { getUserApi } from "../../lib/waku/server"; -const AdminComponent = lazy(() => import("../../bknd/admin")); +const AdminComponent = lazy(() => import("./_components/AdminLoader")); export default async function HomePage() { const api = await getUserApi({ verify: true }); diff --git a/examples/waku/src/bknd/admin/impl.tsx b/examples/waku/src/pages/admin/_components/AdminImpl.tsx similarity index 100% rename from examples/waku/src/bknd/admin/impl.tsx rename to examples/waku/src/pages/admin/_components/AdminImpl.tsx diff --git a/examples/waku/src/bknd/admin/index.tsx b/examples/waku/src/pages/admin/_components/AdminLoader.tsx similarity index 83% rename from examples/waku/src/bknd/admin/index.tsx rename to examples/waku/src/pages/admin/_components/AdminLoader.tsx index fe7643d9..6b2e9a74 100644 --- a/examples/waku/src/bknd/admin/index.tsx +++ b/examples/waku/src/pages/admin/_components/AdminLoader.tsx @@ -2,9 +2,9 @@ import type { BkndAdminProps } from "bknd/ui"; import { lazy } from "react"; -import { BrowserOnly } from "../../lib/waku/client"; +import { BrowserOnly } from "../../../lib/waku/client"; -const AdminImpl = import.meta.env.SSR ? undefined : lazy(() => import("./impl")); +const AdminImpl = import.meta.env.SSR ? undefined : lazy(() => import("./AdminImpl")); export const AdminLoader = (props: BkndAdminProps) => { return ( diff --git a/examples/waku/src/pages/index.tsx b/examples/waku/src/pages/index.tsx index d4c0c2cc..553d23bc 100644 --- a/examples/waku/src/pages/index.tsx +++ b/examples/waku/src/pages/index.tsx @@ -1,16 +1,14 @@ import { Link } from "waku"; import { Counter } from "../components/counter"; -import { getUserApi, rerender } from "../lib/waku/server"; +import { rerender, getUserApi } from "../lib/waku/server"; async function toggleTodo(todo: any, path: string) { "use server"; - console.log("toggleTodo", todo, path); const api = await getUserApi(); await api.data.updateOne("todos", todo.id, { done: !todo.done, }); - console.log("rerender"); rerender(path); } diff --git a/examples/waku/tsconfig.json b/examples/waku/tsconfig.json index c85d769b..3b26664b 100644 --- a/examples/waku/tsconfig.json +++ b/examples/waku/tsconfig.json @@ -1,4 +1,5 @@ { + "include": ["**/*.ts", "**/*.tsx"], "compilerOptions": { "strict": true, "target": "esnext", From efbc5f115fe57edcaac44c3fb35b819805964637 Mon Sep 17 00:00:00 2001 From: dswbx Date: Wed, 9 Jul 2025 08:24:39 +0200 Subject: [PATCH 5/5] fix jsonv-ts version --- app/package.json | 2 +- bun.lock | 7 ++----- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/app/package.json b/app/package.json index 746e016b..dccc461e 100644 --- a/app/package.json +++ b/app/package.json @@ -99,7 +99,7 @@ "dotenv": "^16.4.7", "jotai": "^2.12.2", "jsdom": "^26.0.0", - "jsonv-ts": "link:jsonv-ts", + "jsonv-ts": "^0.2.3", "kysely-d1": "^0.3.0", "kysely-generic-sqlite": "^1.2.1", "libsql-stateless-easy": "^1.8.0", diff --git a/bun.lock b/bun.lock index ec7c7bda..5264eeef 100644 --- a/bun.lock +++ b/bun.lock @@ -33,7 +33,6 @@ "dayjs": "^1.11.13", "fast-xml-parser": "^5.0.8", "hono": "4.8.3", - "json-schema-form-react": "^0.0.2", "json-schema-library": "10.0.0-rc7", "json-schema-to-ts": "^3.1.1", "kysely": "^0.27.6", @@ -72,7 +71,7 @@ "dotenv": "^16.4.7", "jotai": "^2.12.2", "jsdom": "^26.0.0", - "jsonv-ts": "link:jsonv-ts", + "jsonv-ts": "^0.2.3", "kysely-d1": "^0.3.0", "kysely-generic-sqlite": "^1.2.1", "libsql-stateless-easy": "^1.8.0", @@ -2477,8 +2476,6 @@ "json-schema-compare": ["json-schema-compare@0.2.2", "", { "dependencies": { "lodash": "^4.17.4" } }, "sha512-c4WYmDKyJXhs7WWvAWm3uIYnfyWFoIp+JEoX34rctVvEkMYCPGhXtvmFFXiffBbxfZsvQ0RNnV5H7GvDF5HCqQ=="], - "json-schema-form-react": ["json-schema-form-react@0.0.2", "", { "peerDependencies": { "react": ">=18", "react-dom": ">=18" } }, "sha512-AoLuYpmKmFitc5eZPXKnPmVpel1VVhMTKBz/U5/esLrd74P3uxN2JNVKI/AI2lwLq6K0u9PBqX2pmpVGcTwzaw=="], - "json-schema-library": ["json-schema-library@10.0.0-rc7", "", { "dependencies": { "@sagold/json-pointer": "^6.0.1", "@sagold/json-query": "^6.2.0", "deepmerge": "^4.3.1", "fast-copy": "^3.0.2", "fast-deep-equal": "^3.1.3", "smtp-address-parser": "1.0.10", "valid-url": "^1.0.9" } }, "sha512-q9DMhftVyO8Xa8cfupS5Kx5Uv1A9OJvyRn8DVDMATQv8bITq18cdZimWilwjHIuf2Mzphy67bSJU9gEFno7BLw=="], "json-schema-merge-allof": ["json-schema-merge-allof@0.8.1", "", { "dependencies": { "compute-lcm": "^1.1.2", "json-schema-compare": "^0.2.2", "lodash": "^4.17.20" } }, "sha512-CTUKmIlPJbsWfzRRnOXz+0MjIqvnleIXwFTzz+t9T86HnYX/Rozria6ZVGLktAU9e+NygNljveP+yxqtQp/Q4w=="], @@ -2499,7 +2496,7 @@ "jsonpointer": ["jsonpointer@5.0.1", "", {}, "sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ=="], - "jsonv-ts": ["jsonv-ts@link:jsonv-ts", {}], + "jsonv-ts": ["jsonv-ts@0.2.3", "", { "optionalDependencies": { "hono": "*" }, "peerDependencies": { "typescript": "^5.0.0" } }, "sha512-WvRQ/Eu9DNgohr/WbfzMS1kmvAJVDJWOYcImwRMee2RXNfRGcXgP8PDk0k1gaeb4OPzvJnFrbFNY2tZq3BvXEQ=="], "jsonwebtoken": ["jsonwebtoken@9.0.2", "", { "dependencies": { "jws": "^3.2.2", "lodash.includes": "^4.3.0", "lodash.isboolean": "^3.0.3", "lodash.isinteger": "^4.0.4", "lodash.isnumber": "^3.0.3", "lodash.isplainobject": "^4.0.6", "lodash.isstring": "^4.0.1", "lodash.once": "^4.0.0", "ms": "^2.1.1", "semver": "^7.5.4" } }, "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ=="],