From 4ee0f74cdb29e4ffdab66f55441da0169ff29571 Mon Sep 17 00:00:00 2001 From: dswbx Date: Tue, 14 Jan 2025 14:10:19 +0100 Subject: [PATCH 01/32] optimize elements by reducing the bundle size required --- app/build.ts | 50 +++- app/package.json | 10 +- app/src/media/api/MediaApi.ts | 2 +- app/src/media/index.ts | 5 +- app/src/ui/client/ClientProvider.tsx | 2 +- app/src/ui/client/schema/auth/use-auth.ts | 20 -- .../form/Formy/BooleanInputMantine.tsx | 29 +++ .../form/{Formy.tsx => Formy/components.tsx} | 18 +- app/src/ui/components/form/Formy/index.ts | 17 ++ .../{modules => elements}/auth/AuthForm.tsx | 8 +- .../{modules => elements}/auth/AuthScreen.tsx | 14 +- .../{modules => elements}/auth/SocialLink.tsx | 0 app/src/ui/elements/auth/index.ts | 9 + app/src/ui/elements/hooks/use-auth.ts | 23 ++ app/src/ui/elements/index.ts | 2 +- app/src/ui/elements/media.ts | 15 -- .../dropzone => elements/media}/Dropzone.tsx | 0 .../media}/DropzoneContainer.tsx | 9 +- .../media}/file-selector.ts | 8 +- .../ui/{modules => elements}/media/helper.ts | 2 +- app/src/ui/elements/media/index.ts | 15 ++ .../media}/use-dropzone.ts | 0 app/src/ui/elements/mocks/tailwind-merge.ts | 3 + app/src/ui/main.css | 228 ++++-------------- app/src/ui/main.tsx | 1 + app/src/ui/modules/auth/index.ts | 9 - app/src/ui/routes/auth/auth.login.tsx | 15 +- app/src/ui/styles.css | 133 ++++++++++ biome.json | 7 +- bun.lockb | Bin 1078440 -> 1078904 bytes 30 files changed, 373 insertions(+), 281 deletions(-) create mode 100644 app/src/ui/components/form/Formy/BooleanInputMantine.tsx rename app/src/ui/components/form/{Formy.tsx => Formy/components.tsx} (92%) create mode 100644 app/src/ui/components/form/Formy/index.ts rename app/src/ui/{modules => elements}/auth/AuthForm.tsx (94%) rename app/src/ui/{modules => elements}/auth/AuthScreen.tsx (70%) rename app/src/ui/{modules => elements}/auth/SocialLink.tsx (100%) create mode 100644 app/src/ui/elements/auth/index.ts create mode 100644 app/src/ui/elements/hooks/use-auth.ts delete mode 100644 app/src/ui/elements/media.ts rename app/src/ui/{modules/media/components/dropzone => elements/media}/Dropzone.tsx (100%) rename app/src/ui/{modules/media/components/dropzone => elements/media}/DropzoneContainer.tsx (92%) rename app/src/ui/{modules/media/components/dropzone => elements/media}/file-selector.ts (97%) rename app/src/ui/{modules => elements}/media/helper.ts (92%) create mode 100644 app/src/ui/elements/media/index.ts rename app/src/ui/{modules/media/components/dropzone => elements/media}/use-dropzone.ts (100%) create mode 100644 app/src/ui/elements/mocks/tailwind-merge.ts delete mode 100644 app/src/ui/modules/auth/index.ts create mode 100644 app/src/ui/styles.css diff --git a/app/build.ts b/app/build.ts index db8dae3..0366321 100644 --- a/app/build.ts +++ b/app/build.ts @@ -1,4 +1,7 @@ +import fs from "node:fs"; + import { $ } from "bun"; +import { replace } from "esbuild-plugin-replace"; import * as tsup from "tsup"; const args = process.argv.slice(2); @@ -80,7 +83,8 @@ await tsup.build({ "src/ui/index.ts", "src/ui/client/index.ts", "src/ui/elements/index.ts", - "src/ui/main.css" + "src/ui/main.css", + "src/ui/styles.css" ], outDir: "dist/ui", external: [ @@ -108,6 +112,50 @@ await tsup.build({ } }); +/** + * Building UI Elements + * - tailwind-merge is mocked, no exclude + * - ui/client is external, and after built replaced with "bknd/client" + */ +await tsup.build({ + minify, + sourcemap, + watch, + entry: ["src/ui/elements/index.ts"], + outDir: "dist/ui/elements", + external: [ + "ui/client", + "react", + "react-dom", + "react/jsx-runtime", + "react/jsx-dev-runtime", + "use-sync-external-store" + ], + metafile: true, + platform: "browser", + format: ["esm"], + splitting: false, + bundle: true, + treeshake: true, + loader: { + ".svg": "dataurl" + }, + esbuildOptions: (options) => { + options.alias = { + // not important for elements, mock to reduce bundle + "tailwind-merge": "./src/ui/elements/mocks/tailwind-merge.ts" + }; + }, + 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")); + + delayTypes(); + } +}); + /** * Building adapters */ diff --git a/app/package.json b/app/package.json index 8baeefd..e20421b 100644 --- a/app/package.json +++ b/app/package.json @@ -17,7 +17,7 @@ "build:types": "tsc --emitDeclarationOnly && tsc-alias", "updater": "bun x npm-check-updates -ui", "cli": "LOCAL=1 bun src/cli/index.ts", - "prepublishOnly": "bun run test && bun run build:all" + "prepublishOnly": "bun run types && bun run test && bun run build:all" }, "license": "FSL-1.1-MIT", "dependencies": { @@ -29,12 +29,12 @@ "dayjs": "^1.11.13", "fast-xml-parser": "^4.4.0", "hono": "^4.6.12", + "json-schema-form-react": "^0.0.2", "kysely": "^0.27.4", "liquidjs": "^10.15.0", "lodash-es": "^4.17.21", "oauth4webapi": "^2.11.1", - "swr": "^2.2.5", - "json-schema-form-react": "^0.0.2" + "swr": "^2.2.5" }, "devDependencies": { "@aws-sdk/client-s3": "^3.613.0", @@ -62,6 +62,7 @@ "@vitejs/plugin-react": "^4.3.3", "@xyflow/react": "^12.3.2", "autoprefixer": "^10.4.20", + "clsx": "^2.1.1", "esbuild-postcss": "^0.0.4", "jotai": "^2.10.1", "open": "^10.1.0", @@ -168,7 +169,8 @@ "import": "./dist/adapter/astro/index.js", "require": "./dist/adapter/astro/index.cjs" }, - "./dist/styles.css": "./dist/ui/main.css", + "./dist/main.css": "./dist/ui/main.css", + "./dist/styles.css": "./dist/ui/styles.css", "./dist/manifest.json": "./dist/static/.vite/manifest.json" }, "publishConfig": { diff --git a/app/src/media/api/MediaApi.ts b/app/src/media/api/MediaApi.ts index 121c2fc..722f94d 100644 --- a/app/src/media/api/MediaApi.ts +++ b/app/src/media/api/MediaApi.ts @@ -1,5 +1,5 @@ import { type BaseModuleApiOptions, ModuleApi, type PrimaryFieldType } from "modules/ModuleApi"; -import type { FileWithPath } from "ui/modules/media/components/dropzone/file-selector"; +import type { FileWithPath } from "ui/elements/media/file-selector"; export type MediaApiOptions = BaseModuleApiOptions & {}; diff --git a/app/src/media/index.ts b/app/src/media/index.ts index 7a1cdc7..71bcc80 100644 --- a/app/src/media/index.ts +++ b/app/src/media/index.ts @@ -1,7 +1,8 @@ import type { TObject, TString } from "@sinclair/typebox"; import { type Constructor, Registry } from "core"; -export { MIME_TYPES } from "./storage/mime-types"; +//export { MIME_TYPES } from "./storage/mime-types"; +export { guess as guessMimeType } from "./storage/mime-types-tiny"; export { Storage, type StorageAdapter, @@ -19,7 +20,7 @@ import { type S3AdapterConfig, StorageS3Adapter } from "./storage/adapters/Stora export { StorageS3Adapter, type S3AdapterConfig, StorageCloudinaryAdapter, type CloudinaryConfig }; export * as StorageEvents from "./storage/events"; -export { type FileUploadedEventData } from "./storage/events"; +export type { FileUploadedEventData } from "./storage/events"; export * from "./utils"; type ClassThatImplements = Constructor & { prototype: T }; diff --git a/app/src/ui/client/ClientProvider.tsx b/app/src/ui/client/ClientProvider.tsx index 4fd6719..3a81775 100644 --- a/app/src/ui/client/ClientProvider.tsx +++ b/app/src/ui/client/ClientProvider.tsx @@ -12,7 +12,6 @@ export type ClientProviderProps = { }; export const ClientProvider = ({ children, baseUrl, user }: ClientProviderProps) => { - //const [actualBaseUrl, setActualBaseUrl] = useState(null); const winCtx = useBkndWindowContext(); const _ctx_baseUrl = useBaseUrl(); let actualBaseUrl = baseUrl ?? _ctx_baseUrl ?? ""; @@ -31,6 +30,7 @@ export const ClientProvider = ({ children, baseUrl, user }: ClientProviderProps) console.error("error .....", e); } + console.log("api init", { host: actualBaseUrl, user: user ?? winCtx.user }); const api = new Api({ host: actualBaseUrl, user: user ?? winCtx.user }); return ( diff --git a/app/src/ui/client/schema/auth/use-auth.ts b/app/src/ui/client/schema/auth/use-auth.ts index fd2ec84..9ea39bf 100644 --- a/app/src/ui/client/schema/auth/use-auth.ts +++ b/app/src/ui/client/schema/auth/use-auth.ts @@ -73,23 +73,3 @@ export const useAuth = (options?: { baseUrl?: string }): UseAuth => { verify }; }; - -type AuthStrategyData = Pick; -export const useAuthStrategies = (options?: { baseUrl?: string }): Partial & { - loading: boolean; -} => { - const [data, setData] = useState(); - const api = useApi(options?.baseUrl); - - useEffect(() => { - (async () => { - const res = await api.auth.strategies(); - //console.log("res", res); - if (res.res.ok) { - setData(res.body); - } - })(); - }, [options?.baseUrl]); - - return { strategies: data?.strategies, basepath: data?.basepath, loading: !data }; -}; diff --git a/app/src/ui/components/form/Formy/BooleanInputMantine.tsx b/app/src/ui/components/form/Formy/BooleanInputMantine.tsx new file mode 100644 index 0000000..3f3c77a --- /dev/null +++ b/app/src/ui/components/form/Formy/BooleanInputMantine.tsx @@ -0,0 +1,29 @@ +import { Switch } from "@mantine/core"; +import { forwardRef, useEffect, useState } from "react"; + +export const BooleanInputMantine = forwardRef>( + (props, ref) => { + const [checked, setChecked] = useState(Boolean(props.value)); + + useEffect(() => { + setChecked(Boolean(props.value)); + }, [props.value]); + + function handleCheck(e) { + setChecked(e.target.checked); + props.onChange?.(e.target.checked); + } + + return ( +
+ +
+ ); + } +); diff --git a/app/src/ui/components/form/Formy.tsx b/app/src/ui/components/form/Formy/components.tsx similarity index 92% rename from app/src/ui/components/form/Formy.tsx rename to app/src/ui/components/form/Formy/components.tsx index af2eb49..d725238 100644 --- a/app/src/ui/components/form/Formy.tsx +++ b/app/src/ui/components/form/Formy/components.tsx @@ -1,11 +1,10 @@ -import { Switch } from "@mantine/core"; import { getBrowser } from "core/utils"; import type { Field } from "data"; import { forwardRef, useEffect, useImperativeHandle, useRef, useState } from "react"; import { TbCalendar, TbChevronDown, TbInfoCircle } from "react-icons/tb"; import { twMerge } from "tailwind-merge"; -import { useEvent } from "../../hooks/use-event"; -import { IconButton } from "../buttons/IconButton"; +import { IconButton } from "ui/components/buttons/IconButton"; +import { useEvent } from "ui/hooks/use-event"; export const Group: React.FC & { error?: boolean }> = ({ error, @@ -131,17 +130,6 @@ export const BooleanInput = forwardRef - - - ); - /*return (
- );*/ + ); } ); diff --git a/app/src/ui/components/form/Formy/index.ts b/app/src/ui/components/form/Formy/index.ts new file mode 100644 index 0000000..04555f6 --- /dev/null +++ b/app/src/ui/components/form/Formy/index.ts @@ -0,0 +1,17 @@ +import { BooleanInputMantine } from "./BooleanInputMantine"; +import { DateInput, Input, Textarea } from "./components"; + +export const formElementFactory = (element: string, props: any) => { + switch (element) { + case "date": + return DateInput; + case "boolean": + return BooleanInputMantine; + case "textarea": + return Textarea; + default: + return Input; + } +}; + +export * from "./components"; diff --git a/app/src/ui/modules/auth/AuthForm.tsx b/app/src/ui/elements/auth/AuthForm.tsx similarity index 94% rename from app/src/ui/modules/auth/AuthForm.tsx rename to app/src/ui/elements/auth/AuthForm.tsx index fa864ef..88ce0ca 100644 --- a/app/src/ui/modules/auth/AuthForm.tsx +++ b/app/src/ui/elements/auth/AuthForm.tsx @@ -1,13 +1,13 @@ import type { ValueError } from "@sinclair/typebox/value"; import type { AppAuthOAuthStrategy, AppAuthSchema } from "auth/auth-schema"; +import clsx from "clsx"; import { type TSchema, Type, Value } from "core/utils"; import { Form, type Validator } from "json-schema-form-react"; import { transform } from "lodash-es"; import type { ComponentPropsWithoutRef } from "react"; -import { twMerge } from "tailwind-merge"; import { Button } from "ui/components/buttons/Button"; -import { Group, Input, Label } from "ui/components/form/Formy"; -import { SocialLink } from "ui/modules/auth/SocialLink"; +import { Group, Input, Label } from "ui/components/form/Formy/components"; +import { SocialLink } from "./SocialLink"; export type LoginFormProps = Omit, "onSubmit" | "action"> & { className?: string; @@ -86,7 +86,7 @@ export function AuthForm({ schema={schema} validator={validator} validationMode="change" - className={twMerge("flex flex-col gap-3 w-full", className)} + className={clsx("flex flex-col gap-3 w-full", className)} > {({ errors, submitting }) => ( <> diff --git a/app/src/ui/modules/auth/AuthScreen.tsx b/app/src/ui/elements/auth/AuthScreen.tsx similarity index 70% rename from app/src/ui/modules/auth/AuthScreen.tsx rename to app/src/ui/elements/auth/AuthScreen.tsx index 3ac60e1..340a89e 100644 --- a/app/src/ui/modules/auth/AuthScreen.tsx +++ b/app/src/ui/elements/auth/AuthScreen.tsx @@ -1,8 +1,6 @@ import type { ReactNode } from "react"; -import { useAuthStrategies } from "ui/client/schema/auth/use-auth"; -import { Logo } from "ui/components/display/Logo"; -import { Link } from "ui/components/wouter/Link"; -import { AuthForm } from "ui/modules/auth/AuthForm"; +import { useAuthStrategies } from "../hooks/use-auth"; +import { AuthForm } from "./AuthForm"; export type AuthScreenProps = { method?: "POST" | "GET"; @@ -18,13 +16,7 @@ export function AuthScreen({ method = "POST", action = "login", logo, intro }: A
{!loading && (
- {typeof logo !== "undefined" ? ( - logo - ) : ( - - - - )} + {logo ? logo : null} {typeof intro !== "undefined" ? ( intro ) : ( diff --git a/app/src/ui/modules/auth/SocialLink.tsx b/app/src/ui/elements/auth/SocialLink.tsx similarity index 100% rename from app/src/ui/modules/auth/SocialLink.tsx rename to app/src/ui/elements/auth/SocialLink.tsx diff --git a/app/src/ui/elements/auth/index.ts b/app/src/ui/elements/auth/index.ts new file mode 100644 index 0000000..b73224a --- /dev/null +++ b/app/src/ui/elements/auth/index.ts @@ -0,0 +1,9 @@ +import { AuthForm } from "./AuthForm"; +import { AuthScreen } from "./AuthScreen"; +import { SocialLink } from "./SocialLink"; + +export const Auth = { + Screen: AuthScreen, + Form: AuthForm, + SocialLink: SocialLink +}; diff --git a/app/src/ui/elements/hooks/use-auth.ts b/app/src/ui/elements/hooks/use-auth.ts new file mode 100644 index 0000000..5907cf6 --- /dev/null +++ b/app/src/ui/elements/hooks/use-auth.ts @@ -0,0 +1,23 @@ +import type { AppAuthSchema } from "auth/auth-schema"; +import { useEffect, useState } from "react"; +import { useApi } from "ui/client"; + +type AuthStrategyData = Pick; +export const useAuthStrategies = (options?: { baseUrl?: string }): Partial & { + loading: boolean; +} => { + const [data, setData] = useState(); + const api = useApi(options?.baseUrl); + + useEffect(() => { + (async () => { + const res = await api.auth.strategies(); + //console.log("res", res); + if (res.res.ok) { + setData(res.body); + } + })(); + }, [options?.baseUrl]); + + return { strategies: data?.strategies, basepath: data?.basepath, loading: !data }; +}; diff --git a/app/src/ui/elements/index.ts b/app/src/ui/elements/index.ts index 83a292b..c2d2109 100644 --- a/app/src/ui/elements/index.ts +++ b/app/src/ui/elements/index.ts @@ -1,2 +1,2 @@ -export { Auth } from "ui/modules/auth/index"; +export { Auth } from "./auth"; export * from "./media"; diff --git a/app/src/ui/elements/media.ts b/app/src/ui/elements/media.ts deleted file mode 100644 index 5ed6e11..0000000 --- a/app/src/ui/elements/media.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { PreviewWrapperMemoized } from "ui/modules/media/components/dropzone/Dropzone"; -import { DropzoneContainer } from "ui/modules/media/components/dropzone/DropzoneContainer"; - -export const Media = { - Dropzone: DropzoneContainer, - Preview: PreviewWrapperMemoized -}; - -export type { - PreviewComponentProps, - FileState, - DropzoneProps, - DropzoneRenderProps -} from "ui/modules/media/components/dropzone/Dropzone"; -export type { DropzoneContainerProps } from "ui/modules/media/components/dropzone/DropzoneContainer"; diff --git a/app/src/ui/modules/media/components/dropzone/Dropzone.tsx b/app/src/ui/elements/media/Dropzone.tsx similarity index 100% rename from app/src/ui/modules/media/components/dropzone/Dropzone.tsx rename to app/src/ui/elements/media/Dropzone.tsx diff --git a/app/src/ui/modules/media/components/dropzone/DropzoneContainer.tsx b/app/src/ui/elements/media/DropzoneContainer.tsx similarity index 92% rename from app/src/ui/modules/media/components/dropzone/DropzoneContainer.tsx rename to app/src/ui/elements/media/DropzoneContainer.tsx index 96c65c2..87c9933 100644 --- a/app/src/ui/modules/media/components/dropzone/DropzoneContainer.tsx +++ b/app/src/ui/elements/media/DropzoneContainer.tsx @@ -4,13 +4,8 @@ import type { TAppMediaConfig } from "media/media-schema"; import { useId } from "react"; import { useApi, useBaseUrl, useEntityQuery, useInvalidate } from "ui/client"; import { useEvent } from "ui/hooks/use-event"; -import { - Dropzone, - type DropzoneProps, - type DropzoneRenderProps, - type FileState -} from "ui/modules/media/components/dropzone/Dropzone"; -import { mediaItemsToFileStates } from "ui/modules/media/helper"; +import { Dropzone, type DropzoneProps, type DropzoneRenderProps, type FileState } from "./Dropzone"; +import { mediaItemsToFileStates } from "./helper"; export type DropzoneContainerProps = { children?: (props: DropzoneRenderProps) => JSX.Element; diff --git a/app/src/ui/modules/media/components/dropzone/file-selector.ts b/app/src/ui/elements/media/file-selector.ts similarity index 97% rename from app/src/ui/modules/media/components/dropzone/file-selector.ts rename to app/src/ui/elements/media/file-selector.ts index 3695dd2..d718893 100644 --- a/app/src/ui/modules/media/components/dropzone/file-selector.ts +++ b/app/src/ui/elements/media/file-selector.ts @@ -4,7 +4,7 @@ * MIT License (2020 Roland Groza) */ -import { MIME_TYPES } from "media"; +import { guess } from "media/storage/mime-types-tiny"; const FILES_TO_IGNORE = [ // Thumbnail cache files for macOS and Windows @@ -47,10 +47,8 @@ function withMimeType(file: FileWithPath) { console.log("withMimeType", name, hasExtension); if (hasExtension && !file.type) { - const ext = name.split(".").pop()!.toLowerCase(); - const type = MIME_TYPES.get(ext); - - console.log("withMimeType:in", ext, type); + const type = guess(name); + console.log("guessed", type); if (type) { Object.defineProperty(file, "type", { diff --git a/app/src/ui/modules/media/helper.ts b/app/src/ui/elements/media/helper.ts similarity index 92% rename from app/src/ui/modules/media/helper.ts rename to app/src/ui/elements/media/helper.ts index 78f6253..fa4bde2 100644 --- a/app/src/ui/modules/media/helper.ts +++ b/app/src/ui/elements/media/helper.ts @@ -1,5 +1,5 @@ import type { MediaFieldSchema } from "media/AppMedia"; -import type { FileState } from "./components/dropzone/Dropzone"; +import type { FileState } from "./Dropzone"; export function mediaItemToFileState( item: MediaFieldSchema, diff --git a/app/src/ui/elements/media/index.ts b/app/src/ui/elements/media/index.ts new file mode 100644 index 0000000..142d2a7 --- /dev/null +++ b/app/src/ui/elements/media/index.ts @@ -0,0 +1,15 @@ +import { PreviewWrapperMemoized } from "./Dropzone"; +import { DropzoneContainer } from "./DropzoneContainer"; + +export const Media = { + Dropzone: DropzoneContainer, + Preview: PreviewWrapperMemoized +}; + +export type { + PreviewComponentProps, + FileState, + DropzoneProps, + DropzoneRenderProps +} from "./Dropzone"; +export type { DropzoneContainerProps } from "./DropzoneContainer"; diff --git a/app/src/ui/modules/media/components/dropzone/use-dropzone.ts b/app/src/ui/elements/media/use-dropzone.ts similarity index 100% rename from app/src/ui/modules/media/components/dropzone/use-dropzone.ts rename to app/src/ui/elements/media/use-dropzone.ts diff --git a/app/src/ui/elements/mocks/tailwind-merge.ts b/app/src/ui/elements/mocks/tailwind-merge.ts new file mode 100644 index 0000000..8db229a --- /dev/null +++ b/app/src/ui/elements/mocks/tailwind-merge.ts @@ -0,0 +1,3 @@ +export function twMerge(...classes: string[]) { + return classes.filter(Boolean).join(" "); +} diff --git a/app/src/ui/main.css b/app/src/ui/main.css index 6541e5a..c6254b0 100644 --- a/app/src/ui/main.css +++ b/app/src/ui/main.css @@ -1,211 +1,77 @@ -@import "./components/form/json-schema/styles.css"; -@import "@xyflow/react/dist/style.css"; -@import "@mantine/core/styles.css"; -@import "@mantine/notifications/styles.css"; - @tailwind base; @tailwind components; @tailwind utilities; -html.fixed, -html.fixed body { - top: 0; - left: 0; - height: 100%; - width: 100%; - position: fixed; - overflow: hidden; - overscroll-behavior-x: contain; - touch-action: none; -} - -#bknd-admin, -.bknd-admin { - --color-primary: 9 9 11; /* zinc-950 */ - --color-background: 250 250 250; /* zinc-50 */ - --color-muted: 228 228 231; /* ? */ - --color-darkest: 0 0 0; /* black */ - --color-lightest: 255 255 255; /* white */ - - &.dark { - --color-primary: 250 250 250; /* zinc-50 */ - --color-background: 30 31 34; - --color-muted: 47 47 52; - --color-darkest: 255 255 255; /* white */ - --color-lightest: 24 24 27; /* black */ - } - - @mixin light { - --mantine-color-body: rgb(250 250 250); - } - @mixin dark { - --mantine-color-body: rgb(9 9 11); - } - - table { - font-size: inherit; - } -} - -html, -body { - font-size: 14px; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; - overscroll-behavior-y: none; -} - #bknd-admin { - @apply bg-background text-primary overflow-hidden h-dvh w-dvw; + @apply bg-background text-primary overflow-hidden h-dvh w-dvw; - ::selection { - @apply bg-muted; - } + ::selection { + @apply bg-muted; + } - input { - &::selection { - @apply bg-primary/15; - } - } + input { + &::selection { + @apply bg-primary/15; + } + } } body, #bknd-admin { - @apply flex flex-1 flex-col h-dvh w-dvw; + @apply flex flex-1 flex-col h-dvh w-dvw; } @layer components { - .link { - @apply transition-colors active:translate-y-px; - } + .link { + @apply transition-colors active:translate-y-px; + } - .img-responsive { - @apply max-h-full w-auto; - } + .img-responsive { + @apply max-h-full w-auto; + } - /** - * debug classes - */ - .bordered-red { - @apply border-2 border-red-500; - } + /** + * debug classes + */ + .bordered-red { + @apply border-2 border-red-500; + } - .bordered-green { - @apply border-2 border-green-500; - } + .bordered-green { + @apply border-2 border-green-500; + } - .bordered-blue { - @apply border-2 border-blue-500; - } + .bordered-blue { + @apply border-2 border-blue-500; + } - .bordered-violet { - @apply border-2 border-violet-500; - } + .bordered-violet { + @apply border-2 border-violet-500; + } - .bordered-yellow { - @apply border-2 border-yellow-500; - } + .bordered-yellow { + @apply border-2 border-yellow-500; + } } @layer utilities { } -/* Hide scrollbar for Chrome, Safari and Opera */ -.app-scrollbar::-webkit-scrollbar { - display: none; -} -/* Hide scrollbar for IE, Edge and Firefox */ -.app-scrollbar { - -ms-overflow-style: none; /* IE and Edge */ - scrollbar-width: none; /* Firefox */ -} - -div[data-radix-scroll-area-viewport] > div:first-child { - display: block !important; - min-width: 100% !important; - max-width: 100%; -} - -/* hide calendar icon on inputs */ -input[type="datetime-local"]::-webkit-calendar-picker-indicator, -input[type="date"]::-webkit-calendar-picker-indicator { - display: none; -} - -/* cm */ -.cm-editor { - display: flex; - flex: 1; -} - -.animate-fade-in { - animation: fadeInAnimation 200ms ease; -} -@keyframes fadeInAnimation { - 0% { - opacity: 0; - } - 100% { - opacity: 1; - } -} - -input[readonly]::placeholder, -input[disabled]::placeholder { - opacity: 0.1; -} - -.react-flow__pane, -.react-flow__renderer, -.react-flow__node, -.react-flow__edge { - cursor: inherit !important; - .drag-handle { - cursor: grab; - } -} -.react-flow .react-flow__edge path, -.react-flow__connectionline path { - stroke-width: 2; -} - -.mantine-TextInput-wrapper input { - font-family: inherit; - line-height: 1; -} - -.cm-editor { - background: transparent; -} -.cm-editor.cm-focused { - outline: none; -} - -.flex-animate { - transition: flex-grow 0.2s ease, background-color 0.2s ease; -} -.flex-initial { - flex: 0 1 auto; -} -.flex-open { - flex: 1 1 0; -} - #bknd-admin, .bknd-admin { - /* Chrome, Edge, and Safari */ - & *::-webkit-scrollbar { - @apply w-1; - &:horizontal { - @apply h-px; - } - } + /* Chrome, Edge, and Safari */ + & *::-webkit-scrollbar { + @apply w-1; + &:horizontal { + @apply h-px; + } + } - & *::-webkit-scrollbar-track { - @apply bg-transparent w-1; - } + & *::-webkit-scrollbar-track { + @apply bg-transparent w-1; + } - & *::-webkit-scrollbar-thumb { - @apply bg-primary/25; - } + & *::-webkit-scrollbar-thumb { + @apply bg-primary/25; + } } diff --git a/app/src/ui/main.tsx b/app/src/ui/main.tsx index 623e6af..74a358d 100644 --- a/app/src/ui/main.tsx +++ b/app/src/ui/main.tsx @@ -2,6 +2,7 @@ import * as React from "react"; import * as ReactDOM from "react-dom/client"; import Admin from "./Admin"; import "./main.css"; +import "./styles.css"; ReactDOM.createRoot(document.getElementById("root")!).render( diff --git a/app/src/ui/modules/auth/index.ts b/app/src/ui/modules/auth/index.ts deleted file mode 100644 index f3940d7..0000000 --- a/app/src/ui/modules/auth/index.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { AuthForm } from "ui/modules/auth/AuthForm"; -import { AuthScreen } from "ui/modules/auth/AuthScreen"; -import { SocialLink } from "ui/modules/auth/SocialLink"; - -export const Auth = { - Screen: AuthScreen, - Form: AuthForm, - SocialLink: SocialLink -}; diff --git a/app/src/ui/routes/auth/auth.login.tsx b/app/src/ui/routes/auth/auth.login.tsx index b9bf183..659624e 100644 --- a/app/src/ui/routes/auth/auth.login.tsx +++ b/app/src/ui/routes/auth/auth.login.tsx @@ -1,7 +1,18 @@ +import { Logo } from "ui/components/display/Logo"; +import { Link } from "ui/components/wouter/Link"; +import { Auth } from "ui/elements"; import { useBrowserTitle } from "ui/hooks/use-browser-title"; -import { AuthScreen } from "ui/modules/auth/AuthScreen"; export function AuthLogin() { useBrowserTitle(["Login"]); - return ; + return ( + + + + } + /> + ); } diff --git a/app/src/ui/styles.css b/app/src/ui/styles.css new file mode 100644 index 0000000..7c2d1d4 --- /dev/null +++ b/app/src/ui/styles.css @@ -0,0 +1,133 @@ +@import "./components/form/json-schema/styles.css"; +@import "@xyflow/react/dist/style.css"; +@import "@mantine/core/styles.css"; +@import "@mantine/notifications/styles.css"; + +html.fixed, +html.fixed body { + top: 0; + left: 0; + height: 100%; + width: 100%; + position: fixed; + overflow: hidden; + overscroll-behavior-x: contain; + touch-action: none; +} + +#bknd-admin, +.bknd-admin { + --color-primary: 9 9 11; /* zinc-950 */ + --color-background: 250 250 250; /* zinc-50 */ + --color-muted: 228 228 231; /* ? */ + --color-darkest: 0 0 0; /* black */ + --color-lightest: 255 255 255; /* white */ + + &.dark { + --color-primary: 250 250 250; /* zinc-50 */ + --color-background: 30 31 34; + --color-muted: 47 47 52; + --color-darkest: 255 255 255; /* white */ + --color-lightest: 24 24 27; /* black */ + } + + @mixin light { + --mantine-color-body: rgb(250 250 250); + } + @mixin dark { + --mantine-color-body: rgb(9 9 11); + } + + table { + font-size: inherit; + } +} + +html, +body { + font-size: 14px; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + overscroll-behavior-y: none; +} + +/* Hide scrollbar for Chrome, Safari and Opera */ +.app-scrollbar::-webkit-scrollbar { + display: none; +} +/* Hide scrollbar for IE, Edge and Firefox */ +.app-scrollbar { + -ms-overflow-style: none; /* IE and Edge */ + scrollbar-width: none; /* Firefox */ +} + +div[data-radix-scroll-area-viewport] > div:first-child { + display: block !important; + min-width: 100% !important; + max-width: 100%; +} + +/* hide calendar icon on inputs */ +input[type="datetime-local"]::-webkit-calendar-picker-indicator, +input[type="date"]::-webkit-calendar-picker-indicator { + display: none; +} + +/* cm */ +.cm-editor { + display: flex; + flex: 1; +} + +.animate-fade-in { + animation: fadeInAnimation 200ms ease; +} +@keyframes fadeInAnimation { + 0% { + opacity: 0; + } + 100% { + opacity: 1; + } +} + +input[readonly]::placeholder, +input[disabled]::placeholder { + opacity: 0.1; +} + +.react-flow__pane, +.react-flow__renderer, +.react-flow__node, +.react-flow__edge { + cursor: inherit !important; + .drag-handle { + cursor: grab; + } +} +.react-flow .react-flow__edge path, +.react-flow__connectionline path { + stroke-width: 2; +} + +.mantine-TextInput-wrapper input { + font-family: inherit; + line-height: 1; +} + +.cm-editor { + background: transparent; +} +.cm-editor.cm-focused { + outline: none; +} + +.flex-animate { + transition: flex-grow 0.2s ease, background-color 0.2s ease; +} +.flex-initial { + flex: 0 1 auto; +} +.flex-open { + flex: 1 1 0; +} diff --git a/biome.json b/biome.json index 699da84..37a8584 100644 --- a/biome.json +++ b/biome.json @@ -19,6 +19,11 @@ "trailingCommas": "none" } }, + "css": { + "formatter": { + "indentWidth": 3 + } + }, "files": { "ignore": [ "**/node_modules/**", @@ -74,4 +79,4 @@ } } } -} \ No newline at end of file +} diff --git a/bun.lockb b/bun.lockb index 48020ba6c3bbc98aa779e31539edf2ea2a537c80..09c5c1a2e66a2c7b319e4bd7943399dfc9c5b263 100755 GIT binary patch delta 202151 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 z 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 delta 202940 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�ku1EoS)_tVCkbET`rVxWWi;mQ;8arn%Yo~f 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

%Kw(oxg*2Xr% zO4tfpK$)!ot*jNC<=x3duj5t*8ndP4>tGG6hP4(dz8;h&6Fa1IP98rH*SuHNp(=H1 zU^DVHEBr_pW{H%h6$N$D=rzncSf;(+20GVw5^C?9LTN+Vb7v>^5#I}1o*lBm&!HUu z14KT*;_k=kiklX+f~x-zXpdBC73c_|mT3cWzksS%PKWX+p_6tV1y*|Ps6OB8vqgA1 z>R-pwIfhq={{|;O4eKIYfb*bFJtqkhl1UJpBYq0hz|Ih!hO?kr&<1o`YTaMq66pU7 zXg-v13g`|rQSX7ijQoZ$Hlgkp`WGozi7UKMxPtHwVN;eld!f?b0`2Kd!W(cMD3|}& zd-*KP>$u-0{2lJXJt$AZEd!;%TarI{L76=OJ+o_LS#g;l8X06HOb_WG4gAUSKj00# zfM@U!9>XJe3QwRNg%Hn)zlN9a3Y12K>ipkNf)Fwd0wr`2z9W1KpWq|>1s~u&1hYH> zI2+M>G4$Rez5ht>K~lzU!iW$Fv{4l-7IAGy%k_>V#iK>!_{X#vy)Y>Tqy%NAmu@5l zt&oT?3Fw7OaUd?pD@}Ze2U)2(ZA4Cm)H>QJ#kJRo2~}I#j?yH9M_xn`AnJQP_- z5V%Z*QKq>-8^}qh47FFvEC*x*=MZEkuAR{`?O-0zdrh2p3F5_d{EHDO3I#x|KOdo9 zep19j{a=_u#0!ExrxzyFb)ht&8m#lWEHz{`UR~#Px;f{*&KRBZI%Ax(MORy0Q|jsX zf58g62Gk`yMp&D$7U+7ei%t#VI)|$hR)dOA6)JGjY3dNK;pLA$_UNLs8g%0+3Jvk~p#f-5niA^5qrK3jRrd*b?VX&~t4A6Y`tust zif;u1<(mP zLtr6PU=?mY6l9(0gzDcv7iPmO9sij`y21?5u)7Ixn1=Kv;dmGeI!vQs1Pp=UFbwqX z8-@}NhAyBx;UL0+&=-{N0BA+J{)89pxpld@9;<^KL z^DP?hiIc56vt^mB3^kx8REJ*pp3nzYlSOaB4g*b`{&fKyn7qnIBFjfjsS zY)Ckca4LKYr>U*42zAIN5l(^0FcH3iuR&h(3RSbJ6jhv$aE=bG&VYY996F?nNYotE z&YVW82CZiVH3R3+tC8vkrzW8EI&GH|swrq)9b%_Ven&hF%_#f?;m|HaC{r~!WxSMm zCv#<{#;&%cr{*Qhf8NMfuq^N_bI!aKmam>eoPz3PS%uF`UV41U9gRpBVm0Ir@r|6| z$?RR@kB2`?=ovxJ6#tu{zD+F-*+W?H2aD7C-39>v~`%7pO{gG zM~VLet!NMD2+zPtI047u7#r3Hoi;2#&Ai&pDGSdM?jjF8=Ia-O3(Wrt=Rv1RM7?SK9=L0?afZacmODWj+qXge}ZR zA=ChB9ky(%c{^R zz08bY1&!%U4oz<&?TPWpKv&Hqgc<{;Lp7g`X-}V!Mj5M->3cT24EFdOI)6@%=c zG3gDU5QWk2fAxvi0}XP$!Tc2{O1!R{7dF-=QVaC&dutNb@a3B1Nl>mj(P~hTJTykW z3Y3P*kQ<8ND-m8~wc>;op&XO}jf*b{C45sRan~t8Gza9f;^Zf;pDGzL?;kp!moN_~ z+gzY!`AI8Oe19CTcmdo^!rwqeC=AYSe!k4od;e<%N$tN@R$O(ax{$jC*FpOexXg*G zZk5)_L-nD!Q&3f*3STy0{VT2nzXf0GD_&kv+K?zkrm0yiT z2VeVnf%!Jj7FvU|u2XSNBMqEaTqi;cP(y0suJWH1wYM$7*{i?|R0uU2XCq3hy`W{g z+7oKIzd^_Gs*G(w4OGWM+w25gpe?BE9YNW5gAt&tl%@l;gMpxPMU_09P*;y(P?Y%r zggPN~@cTj!Xb)e}U1g+|yAyT=Wqyr~XkHtY(?(Ue_Jm5W0&3qo6DpmS%PVXT zeTeslUeFV?6Uuie3<15tVvw5)Z9gK~17+CX;>tuNmv;)MjX9YpRDqpP=`^o%Snlt6 zTtnua%Z`=>UUs^ws+>L3(WwopN|n@q*&$Q`uCsBS+|FL>u&D|D->yR=N%ODQA(r{3 zP4dM2uS=0G-JiD`wX;C?`Mk3S^5a2Q-ALR9z9jq>)QHEx6qpQ?U?S}03Qn~$h>@AE zB@s;tOTky5i~=|O4fAtB8O{Nn8OnGTjDp!P6LhR}rs+=WSnRd~JNQSZ4>FAlx>CUT?{v{oW+&|ZZSMu6YhPzYhLucOaXBi&+QfI6)LGx~fbCu65p z)O+U-8j_PPFw=i?%H!X*;r+J-^3KMcG)^I$)5`Jx6!!V-umt88M1!?;y#8}jGfuyx z&*R#wh^(Mc8`g52^E%IUx;cBTumm`TQ~`9%0}JakJmnD{=lJW~b2bo@1v(EkuNLoQ zu12Gqj^D)@jlR z^wY8at&c8&T1o$hN-wQw#YLqhq$b{+uo*Ok7_3t!)OTr)CwXwq!!n&ewFVCr?lY-xTJ&SxJ-yGD9ZF2+A}E zp)&0X^I#O{wP<=hT4URZ+?K}KL+yHQX?}ZR3`{M0O%EDKN8p56$`-%SOWT9Xffd;SO|J2bOFc@ zc_9y!;V@|feOX`aMd!S85z5DM_SV0%)d;7+FzBqelXrp+pp7>MZMXqc1-;Cq1Qdh9 zP!NiO(kLtf@=jb~ahrG2RA#;kl!LOM8ZQM(>n&{&%}5SqFSLT@wNgb;?<=iT0m_4x zXEn$X_0!?gvyZ_3$A^}<;R zN~`K_1KO+Ra39}{Pgx#S5W3kuS2T1Hmc={YdwX(5$g1F^6~aV_5~&G1${s(skF+pH)yYVg0le?Kxv(M zCzMkGl$SzhBSVM}25n~$3kgMnY#$ z%~y+5Abi1a|IB}?S-mtz;8e{jpsHBMQ^#Bx74cnJ#3%nzL{(whS#AIGGX9%)Do|Og zf}MSm*E*Va^3nm)0Z{#DpA@(BUVjsrgqR55z*jH;% zIuDf3WKaR*CgA)hkFGsxzu!U`i|dTj38CxFRB%onU3;2=b7JUH;+#Y}(XJ+b9pXW{3i zQ;C&@{ERy4l5#a{PnLnl0aif3A{nv-?-ut z>R+O6A~QX;#b91tRMjTbu=Kk~9Gn`c{^5qx5MvToqtid)NWii=tQ$n=G{(4i^+3{( zG$q)+D?UO6NDK)<$NnQ5&PceJj1u9Jf?Nv1 zzVG+m`~05YAD8Dj^qkYx)z#Hi)z#H~`T&OSyCN-w`+~T5TA;zRAFP$>1{j9pd|$wo z$5jqj5nOza&y@^M_5Bhbeux5*NclSn5xDqU3E{X(yZz}}iDv68B zaTz`?G8ETBv@`^%_N|4o_|1pIGVThv#)x{r|Nlval~J$?uIjLY)o@kE?Vi{n0;r?%iR1;$EckXlUa_;7R_&uCm{^rQQak5q|aA}CR&{Evb z#l?l@Anl2(H!im6!*I35?=DE$h_%LVw(&eNvW65vncKM9;JLOG!@+J}TpZaei8PD@ z)!hNm4j0d?-hirZ_}v-TFSy#{>I;PIQ1B>M9ch1DZE$r&UMr-W*8#sHk(S2w0yr3! ze=~fi@8B4Qcc60~mwUl)nrMi-0W6%~+rHXA8N zVp$_N8q1@j#`1RPhDFG4hO`l`4!B})HODm+*L>v7!_@-6SxdOw0&YG3SctREzv!ghge!Z}=;mL=f#8C=6T zY`}v;JZ?_mI)IC**Ng=ZMy&gX0k;F`Tcp~H9{f5Bzv9BLv#?Q#L;eK(-iY*XTuJ!7 z2I*>Ct8nql9&C92!o}}oP3Flnzx~Aza3ijzNMixK78k!qa}3vd{9cEP@Ou`n^SEZ? z+JI*a&vTvmxcD~{xd|7S-Hvn{uC2KK!L;Q1L`{CbmCh6(0p%*nkVVh^s}-2c09?ZlTi^2B90zof&gKLmh-xY(fF#Kp51p3xk@#eH=W zd0c?uPvCbY5XXJVWsc+8hu?>hYWR-gH{;;4NAR2BRi6tO#(j+SUz;v*LB2Rcd&Y&h zQkHg}IGhHeFr<9XQ5_9w1^ni+f8t^yb|bxu%NrN}W~o1m=bDw&#)S^-|D8t$vyHE9 z0Dg*lu7J7n48I@adWh>5E={s8;y1IAIiSrSuHYH#^(B?}FVf4n7>0i{K28&HUB$)a z|KoL5#xk6E&R)9(uI<4uZ>Apme&4*q&hw2hYjc!-?bypl}3}f4@$VAMD1-;KDq|I zJE+(B`xf{0M}-r~Z!6$dQ4hfB;%VGUW5w(ozXm-c=2I+Z#?pzch%4LCH{6AIqFGMJ zJesu@ReIQMe`eqc3}$5P&^RNfPQ)26w>%DiS#qjV)HWNFxg`K_#OH_wX8#@M9<^;^ z0q9FH0ML!00HeW))^D@1*3GBZ27^-;fjO4qIr{+J%4=})5Rcf#I0Dy_Rl_uMdZ$!dk`#cAWJc_nJ<)+%(fzpe*ATdV*jvLj+`EzzVcc-@&xcan^ z;W|>}ej7(m+0em&=7t733~g|0N1Zkn=qcoIz{YXr4x22xWi-`BqyHcB+yNxVsT3#I z$N|leEwiH@cx3Jab~DHK53hNv$BmsW^tu-QzM;Mtu}4tAXk+e7K~@uJ_s=1_QSuI( z81q{POtMfeG5^!(GbI-81*!nwAnu>6G;t@YpGrDggGqOR;&$4s)fuSAE*no>51P6Q zc>`$;5_1eV3&9-Wxy$K6^!+l6fFsN|ka3Ks8vqF554q$4fGONYADVTL4GUBllPf-J zWJ1oM_!#iNq5Vbn_BF)={%edjP?L<__2bQ$}gPSHnWc3 z9Aw#Ef*h{k^zBkDNA*5*$C9%bIqt~Wmf%{WZA|Z$mYnOzaYK$(`Cn$chQt)H^+e2=X7ch+8=7h&bK z_A6yOMP}ByOZ4eHSb2-0_u9nhW>W$`Tt-QpY$WS_z*vP!aFUz4?Zd+vw0NJ5Q2{w*H2{c9Sk_A z2n93gghDw|!~p=OXH_I{2(>(bI)zoysu9*fyYZs63}ChO$N`&kI&(cQ$>Jr|bFOYV zec?^Lt{R?juQaDG#{t-Z;txUkd#I;9XWBh9cKvO&UdI|10vR8qa(r&MyiB~P`%SM4 zhM^5Z&M3-r*v2$`5&&4fvxN`sIPKWoje1=P2v#5fRv`yRqmF2jIIrr{UP+Dhx;c1a zhbMWusE-LbkvH!Q;a$6$K!N z1*{g;3QjKT0t*?ym2e6-XzLFHU;RJiJZeY1TmISWV*$xn4E}d|RCG?q%2k7Sx>#LxNop8hkE}<)sfFhn^FP)V`LOv$(W1QowO;fSYu8EJuEwyE-gFI@b4TZ zRytw+V3XZXfp__+by1@;MW3?C1J-xLBXdQN%VN~jeB_^jAzvRWI}UYQM~k^kCR(3t zW1{n?Y-%dyc1)*rTcc0gG}h_%)53E=dIEzP+k|chmkuj){c>%+ZWs>^#SwC(Yw^_&dfHW@9C#wRq7Ddhe3SjQ9-q!aHyt~B!0ikR|O4BVCq6YMd;o?z-b$39-^Y;6? zPVr?pF@{@JB|i`t+8BsfscT;8|FL_|J;fwPA*EKGk;8nuRz5QETJ4We7=PaoCeBK0 zhUA6r=2kQ{53?rzYz8gYNwTTdIq+C3COITov-Qb2o4h*s1##e0YOV!mz$Td5>t!Va zKiKBmlB?{Z7kb@U^@LtLvvHzD7i>II<20X$lpW`%n@%lGw0bMb_b(#W-$6Fd3H29(TzV1)|9udF?VK| zfxWc8Gjj1up;Dz3#sq53*(e-HcFlgkorjV;8hvTF-qu=Ii4y7nz=BYr5Q#D^mP}b2 z-M?((s@p@le?k2bG9$@|13|%78z}l;a9|RRLSlBQDs^qks|_#y-094|MzHiD3sVB) zD^5v_ud;$0Y`R}2D7#w|!0}iD5o$p0R{+pi0Ti9Rp{QS&4mJ{q#9%Xs>Rz$w$ZE0c zij9l;Om#U!>0f5r;D`^kGD}$mZ>%2Eb70Z{wbXG^lr+zGdT;rXrDb!Bv|B0qDm>ZC z)axoJyiMadc}lmgq4b9++IQ8)CB(X>^s^(1-FAGk@?&1m18PDrO8jVE;5G~Ul9Ox` z(Ukls>I9t#dRDJPmI_^FtEQlLi&G>3bTz2QGbC-OUm{o-4{&zZoHs9@{%CuR78*mK8cy*duGRiTf+{=KlZdl_o4T-7 z-&;B!<}l)dj&v*+hI%#uZYbl5G{@kz`LZ_NJq}|THN9?A%A7H>%M^QEwC2QhLF_la z^CpMs1``F|fGP`$O_-z3y#avv6n_H%g55#ncGD)<{Gp*#x6(P^=uSroL_%dEP>8W2qF9|H}j8`NmTA z_x-l|=(mTx9!L>k<9CO4-$GNLQt~Z~(e6~{HttqZpWC?GOmVkmpC#UgIPZ&=jNf_o zew&qjHjfeJU3ISA9Z1t#3?c?@+Eu?6gWAvtsh(&P?& zJoue>YTYQZ(B4oGr6wdoeuR;EMm~2@0=~mtly6Gw@1kJS7E)S9U2IV3PQMBcD9FAl z#BeTs0l0ZN0C*sBsNZnK3eVg(1b`qUmIChCY=QktE(F_Ch;;XX{REkjq(goSCLd`1 zD4O9)(Zs?CJXCHa_09g^+V}%s=nI;u=C$=`J|k)k0G7~2Aye0GjyGbuh;cB8-HaZT zz=(!X62nad9FIumJ+|RbUk}?a_$3U{s7%H zhw?uJJC?SQtu^X5Uhds=#Rkz@q2+U^7X$n_tboyi9kK#dPdadL0u-F>BM`i_os|CI z+kIAB_kZ7>rC-d#t=3ZHBT%I9t)$5Scl!|>{Uk!QjlVkpb%Z?8T|$>C6l0(-WIgj_K6HgNrE~F^=La<>`z*%G6%QxWwL}Ps4_`O=pzk z@Jo=4wcVzLPtkXBL^6K>9P6Fq;0}k11{NL)IG#6wU~75`xODtoF}4CACwcNflb=d) zB1RvlEI~ZekwZ{O8;m_{H)EH2*4-LxO4RH6Ln;_|#67e2=TTZiJFqh1*aq1VshWi%zb7dBqmr}mO)>ly{TMA;!*`$TaNXzeB|)#Vq1LaU@y;|Kz-EsD(&QDJvu?_=!Ci-l zDgwFJic$gqbdi+w3RTP?hs!W*GpO5Zl!_ZDc{VrK*2k`+cVX^}{t$-pGA(`$xxPgg zUfV>OUjmNZfXB7l4yiT12n-f`5FFszPT_BWU9{RUtMqaXQm;2)jO}3A*%E~Iys>dL zy9|+J?sF}eWoOG)2sWU9g$b=n2{jGQnK9yclI>g2<_%DL)M?6@vunNlcIOo+`m;1e z0zmhS_MSxvJeWnTZ@>>7_twT;*N;}cMK@HTcW*&Mj$zUVXii@5P()3D?5HK~qTLV} z;2KsA`r0FCkvTH6XcddhO60Jde(9>)&@*3!E2vvp`V*?m`;S99?QiraJe(9FCuigJ$waTr|%|JXZI?h!NE0dq3*(5n|Vc z7IG3n=a87IkHO+4h}h}moU{4l*&{O}zJ!=GM5P0HenP2{R0@fCG2l29dNFY4w}~$s z>MU@(sRsZ;t^&Xv0GkSCZtnkZw4(*!qsj?;F(f*N{g?zxj)`_N?owm17LKCtTg)C7 zJn#573qT7>W*UAa!)L(F16%>XO^b1BJgom4gkv>Y_fZ)Dgj@%J2>@lX*6z@I?6Gzh zfOp8@=x?6E(YkxTUKnA?aiWQg+jpF7pWW?*0PoKyK3f2)Q5*ntJLu|X#9n@<0bjtq z%(UhU9y-z?zAHu_I0>SKOVJMzRQW5kWDNC)f~e_f{a4_3|5L_xKGkhu`uF-D*M%U3 zu$+WY+(^KUp)Y{bZK0S5sGHX}sM}$x{tZQM!9X~G_Q6-(?N7G#8HA#2d@<4vrGP{P zf<$o4hI|51xi@Y4W>e9+C_()wZj{kv&CW=P8D>O;tKcE`;oKLFYYrsNwgn;;s5>|# z+F%MfgM3z|lIv?Ue-IVfSLK7o;4CVz%3uIHw5Wg(_Y+#lmL$yyh-7>(3mOp&H`M`KQrcT zbF$53h{8bBG!qbsra95lOa^z|XzKD6)1Sml23PZ>g_0Sy@6;Y%A^SE&C^gJMVc*WiAkwG7aeZUJ2XZ3%tOY;ZAGT`Hx> zeZKx?aL&zg781yzWSB;4;2lF%tqpS#8R%6M1`)$gG=0PKkh9CBB_H4J&)8j8jLVP5?)daCu)sCI23CrJ_~qpmhxl;^Sk~fJ9bRg zq6?yyZv0DN5M$pqsto|$Z`6h1My--&_`|?a$#)7aK&TFD84&v*t0@#v!RVbYSlZlq zfa6i8@rluIK0dFvPvJv!=WlX2kA*;8HgJ6I+np8wv8JM}^U+=#gEc&MEO06>?GoKU z`IIF^f%7U>a_O6sEiGDgZKVlX|oC#H>LJJD>dp6S*+N76aIbJYS6Q^8$Fv4oCJL zr4K)@J!hww=>>*jjFStO`mfu_%@$s1UJ9`_1euHOkmK@_q`_W&o4vwR8x{fKN^DJ_ zQ9lPLNeR3XT8om9m^T)fU<cJA&;gE7pG`=A+G~QWGmxvrS>9wP4jN7zN((vTq?oGX$?9N*ROI2dP zfh;&UfCg{!LShaB92=6cJ6D`_8Ia)3h{E8#2Uu01mH-H84gl63o0%bFfAwh>1pp3c z!uIt-jxBPot*J0^GHq%7!9MfT*V*s$pI#CHdW0DBe8;p=0t_N6k$GV5(B%9 z?06ETz&d75JjDV4VTTh)+#acV7Y_|x@>wE)(t(=j={O@HwsCw({ zt4w83R3|aeMKa{fuJx~Y76sqyDj?i09F{!0R(Q#oD0I;5 zV^Sc)iZ*O^;9#EeilNZy6|^33q1yn!_$F5T)@%H<*H78_%7wX;$YB}irptbD%%5#x z!L*!P$LOmI+=_RAU|pOU+Gpb-x5Jw(5VjOx0ucqMIwyiM_Kk<*&K!A4;(b(S!ol1w z2}apAHn9yTAPVF35tG5){G(;ZrV+7fgiwJikXE2*SA%n2w4~-~X#j0~gUj7|Qvpk8 z(s}a7@yLKNh1^#Fy{m11=6%^`4FFj9;oH7L4!6GX%A_L|7M~n|9JS=2r}?hn<_b#S z%Iqk~)etZJBh%LNWFv3Y@6?*!*q(LHx`mMiHh!Z!7#C=WbTiR;#m9)?W+ z2i;|&ZJq{KxOwM1(RFfz4d#HQu+PS)+EuB~g*H7|ovCb+^n&4kKr&$JSUNOQY!4?h zCZFLF%g}ncj<E zhvplJa;$O-${AMhOo90j;1CSS5bY$I3IP4{MEbh`+|tu@qX1a6gB%MQqWEc>f`&l1 z+t;Q1FCVukf3ccNB9!W&_}lBWp`c-euJ6`>LSUaHn`gr5&^){Fe?@ah0*K|ATH`hO zAKQ-ZzYDeTObxaDhh{ii2jU%~L>gPf&_Y*~9uzTnnjhYg$PzFhyzX|9SI~I#3+#bp z4V5lx2v!;khK?@^rtPBTMGaAIckfDbJvm$6hl~3xzoeLkUVC+yaukC%n2DMe!`!TF zF?78AjY!-r4)Fa#laUkB_rA3JCAO4mp3nA=y8?wUDfSN}@+vz*nmSGJMeJA%=4hlh z#rea$B^SdqB#9in3?0GFL0*QaTI(N5gVAGFyxp*|H89e^g24q2vO21Af~K_{95;Uv z0=ql$gk6mDNLj!t?`uIra(VR@W!USpy77?wy$yl-+t;ZbQuB{EK0cB-vQn%!*kYy= zoD`vZ-i9crw8Yr!rah)gK8BL|B}p{W2eKqb)2yfpJ9(4nj1OFoPf61Dof`gb=b|Fz zE`uBqU+}le@kGklw>2Mg?W^CetjfVyoR9MOLMHvGHWGdBC$z^8Vc7+~2G`J`D8!=T zvv*6Lj$R{zu;Ah0;{gxL${#swTqi8Cnb&LDUy)d`R5|0H&@*4OTSHXR&)}*b`h*(z zfo6Xi=Vz(J?MEz(oBuu~QCWY3i+;;9G9q=$^<4I+u~n`s*)Kf8>UA**m;$LvV36qJ z4+|l>*`$B)f)Y{Q{PG1};sU)VHn~?T)4A#>G#?HRHgZ|HkX-=Sj;*$_5Ue_MYOWzS zH3={{*|bcSTKztmM$9!hA&gVpDl<)GXa`z@#9a8L9D2hKXDd=S+mS66s>1080Q4w2 z=@qKK20-?T3m#fgx3IZ0qM}@V2)ut8G-5!mBMaA|3c-y6I?aG1AR5|Lxp5h!C{K|s4nLoCa=z`IPrfB_H{hh=WE_nT1(QUyH#hAHHMnJ1-}mUgFD7JyyIS^*Br9!?>K}fhGfUW2 z#GI^ZuUa?wa1mR3V9|vcZ2y^Bg=x0J z1Wq!FQew09Mij~pP8>3>@0V}E&VNxzg>ZHvhi%%E%fq(D-W|ySHU#q6rf>T~pTi({ zK-;yHMv^J?=oi_mj%#~lasIOXf~EeqpQP0Zj(l69#tFiTsYcnAFEp$KP(AxXQ)(ks z_mV^tYM5CM71X8L^@Ucza0}v1=JueDd+_+wkGZ$Dsl$sk=qFx=mB3lq!?U{imHg{M zXkUDlt&D%LJ??tt!ES(7PUptYG_PcuDij96x>N)?WfeR_yYq6p6V@< z;8nmI0Nf}2kmg28W9J3TcP(Co$=0+15TVvtbRuXI zk}qlD_V8)76a))IhGhq4p|4!_UzE)W>=pJG3eFuKJHdiI`6qTCnUT140vvO7L5{8oW2^0) zCgEUJ@kSxliUjGi0KrkLX$5EgbD?TvLkq+Pl~}#I*w(h1t_b3 zl%4jK7jnXLUR@X^MIvHqW23Y3MY$iR%36*$^(v20KnAy!XmL4UlN0Ix9;s|Giv0|? z1p2>Ac(~S%b+$U;@{FsX4=ix7r6X^VMdS;Uon6S`dG>FMT3rm!mlcKwi*%e}wVAF} zKx5;`uad!;Y%3aaxt&#Le&pX-TZ*V?D2aJga-z}66M-pzs|Gog=rwdJG<=p_6^I@} zdZ8GJI_02s6(RaV0Kt)?(Km+;Z)J+*p*0W_aX5dIa?tIHLbE){kjt9U2|`REpHPO? zw%oD~qfd?fCY3>~R6T@QX1~t)Yrcwm*PojNY;Z>qR|&GFp_L3?@a|)(8ca4?{ARWt zovdUirXCm29=ElZf_ZSR|DpFIvlSFXiH$yk?J2mjA+YjfK(JvbzB}vIoo>-b0HLz;H71m$sHG(1o zy+lidj!7=~P;^y;_0K3_UYpI1I)0ls9gQ>7mZ}C%w}}8?`}A+ti4Pi%nYa@5aNHLj z{6a^1Sv6&p*MD}Tvei;Hq= zu~X(PHx;c8?@dfeoY~Vv$$`}k74%ZtJ&?oF>CV!*84+ThI*rTPx!A~?8$Io%hQYm% za;rmdvj#kP8Lu*@8-x>Mij1+D(~ZTk>xRHOVsZa%#7hOpe0kSRh?NZ7h5RINho~F( zM(vzFgY3?9iQuRkN+O)UjZPuv7n)w+P6FIg0Xx6`H>YI3=(*NX^xZLXs|i~y0(XvC zC3pdfu8CH}(=Rm zP!70u=23a&uswA7*)lthe0a`dkZ>oJOQ*S$nkS<z%CpsAw0&c zLjN=mN%@$%_T*IuV%H?eo0#&z}-)OPZaJy2(k$E3vFlL7^`BIm_ds+r#O5aacJV7l`w+wiKF-rN;=E|PZj)WP9FF4}aXR3t`$u-TV{R#I@d(xu%5UTq? z#oM2I&psBhXxgSidYy{6;WhNhqoFUk=xKeJRiAuv;4F1F@4-yJ-N0*q7@)$~4wWU( z27rqqyWtRu?ttSRFkyT1JS)DINZBa{`y#hKRFlE~xif`KbVs1+Xoye{c;D(dW2oN$JaGpT__F%0)`!^GBs;a2b-vy{e zBgB<103t6S@*i$izICQ$ypxV0VC`NZhx?&kJLi{gyn{!IoIuX8r8QhfA!>~}K++Aq zi>4vCGyhUhdRYbQzPInS>Kk@}3w#ij0krT}2=T0fv|$8Vz6pTr**e*{hcxq^Qc?j1 z23egbNY8!+kM0ztuYlBlLW#!UL~aU*K}G)5t22^{)MF@;Cgj-@Nkj@m4%;z6TXE7g#e=G3HvvgAO6Y{7 z8$~z89O-ajN$A?9-CB35O}s|~_J~(yo6xP6h9ElA6j4n1Tfe#pc{GDH%}G1^V>Z;a znIYJm4kLM63$$Cb$T>4SO;J3Beu>4!yt}CM@ePB9ysQwvP2&+t3KC`2s!kV4_Rr`Q zoO8BDb^D4@juv2qq*^BY3S7Sy7@pO`44^e4sYOdPdmsgL1glcxk$0-!a`AQ-_2jJ& zwP=rO?Z~rjno>a;4k5tUH0WCpGiCSZvIZKhyy$!9%&<%nap-W!Dtl_fmbz|e zH}C~svk*=+MvYkzGmg(qTI>F?WzFpzRKRjEk0%hXylp^?Z0C2KP?fs0!AK}kyJd`; zvH;jdHYI`(Oh?$AJJMcy%f2mYQe0JR06LT0ZQ3~~dt0$||xS$xOs`!CM zO~Ms!2rfugf~%w=9kvOiimagT$(9-V>48+EFF0Z$I9Q(n%T2VdgQ4d4I4o>bGLfc} zG(NyyK-ZjUeMdv_wCz$%*BX+F8L1zd(nvjqt8Tx*{B#eM_B62f+HFyvCdtjW!k<(0?`^?LB5%J!J)8J0xQB z3jjC(AZP3SyT82SZQR zlv?Yi?w^oN=~0ShOrcUXfL8RvVwD`7Q?i7_rcf&Rn1)CCLlN|MZ?r0PV>M=_*T~db zBu1C-hZUurKXwy~Vw$ovq^}`Q3c0GIHTEm&(>(q8ZK^VE8B6T7Nb24ntxh8_vbVGz z*O;J0#L~BNY)VhgQ`(Y>5bdeP?DQ+pctejnL6lq3wSf??5#;#?+?>VbbygnGy)XT2 z_l;{|2Wik8xVZA<4XHHWQxJvB%SZUOt=`rGVNERuNp)nUa4YF2QMK!MXYF6h*Su2W z1?I4tfD0S%oLQP|=vEpor6_{zijcH!sy$nL;eY;@j;_M5+*xzRE@>)PG{qn&Pg7%Z zy8fgtmDiY_!@^@G#y92iNna0JeExs$(MehS&vS}YQ-=S?EM%p zY;x+VceAo+xawP93jfWb=qYNNR-H9P$q)eZ&!(7${1gZ=S{H;~AO2bgwD}PBL@|NxOgo2y6r9_{|%XenFMWHB2uPc~G{-XCxr&mBv@Og7AQ8|5X} zz~d9V_U(=g#k6^GHPmXtBLxx2i!MhuGO={zro0)a$q zu7si-2h85Pt(%{%4Zn-SD^X&e+ly=NL*u8yZ5A~i3$CImf%x)M`51#FjLOZ`ahKb<;6jDM7aws3LvhqzT0~#C|+b+KK%B zgaJNSn`+cHy2yh&qV!j4(~EAn{q9T-b4PrhbW+)N zB$v8!6h5=^_V6COt6A-H_fk3g2j1LyqQ)1F${~WqJs(UrHeojIIMy5ls z3Pjk@;`tz=5bfloB3+no=%|M^j97pebXt3f@kCjjM0{78xAWLK=zi;yJ7zNw^7%eM zc&{-{Kv{KeB<)#X$eHaqpg9ad_KP5FJ?Yf~ym!`6s>%hE^2 z2MnemMZdW@&01_2;TDH4dfS8in&sWS4;6ioM^?g9kA&uQ6Pv_vzE;!{puPe?w(57< z+US;C(lrJkYcWjaljbyX3HGdg1qAnD&t}Unl$hGpL!iQW7B~r2?7?!;uW3n-ml)=l zOU{vU-=Ox1nyu|O^IEnNGVxYrlzvicdbSjdR);ZWCC_DG=e|aCawE2_skWeFvC++B z*0q%(s0T-{&+J{_lh@AGj==on@CPi^1G%|RE2rPxmWMP@zbiJjHw88@kBl*fZgcFLi0wcc#E5 zEYI#P)O|Jlrc;1m{Z06NXg}+SBmDk}@_nv%p(U#g9YPOvl{T}q_sETJUH!x>bf6eC zpGOYs#GcZB4S(DDZ6B0Xa(urlMXWJ&K&zQ%{gZCAZ;hdb{&J6*>rv<39&-Q7;dhOO zKHt$6p-YW-AA8Wrb#QE&{B0Ns244FcoNG*;u}Er>!&;Gi|8O|nIs*I?dQpz;V7UU7 ziT0Fku0<5_PH$SYo5QTVsq9{W+Eel#luUg~zpewQqVjTINoA>~7e1}sQ{5n_>Y%RL=4KIC9-i)^yYrXvpqS|sm9Wp6t~gfR8ovBjR0kju0MI;_TsxI{=O=p z1amqehnFxzif(LK{*Qr}7i(=EL%Y$Dp??7&7XXgTzFcsl?yJ8AU>9+z3}skYJ)l8?iqZ?+;9}JmcHMjo=b?$-mu*jfeHA{3f`ry=W>Y!tF4H zJ{~50u@bH|J1004k!x$BLtd*KkBhqPvtC_dDQEkRuKGHRd^ba4`p_+ttvE&lCyHzq zqL|XtSSxHDhet3V$O$PiT=x6+joZpj8*kUyf~O961cV!l*CCCec1-VZ%w!MU|;kAlwubv z`=b8czs(ydrz6-8jAO59_StDu!wF%UfMr%Cj3iFyarn5YoEjx60v{uJfrCaAKkLww5_29kc)@{Xx%w>OZsTzu-@XLx4ti1|!6O z$WFhMHx_z}yx6*>c3~VP zu+mSPMo$hvhBl)RM_~hvpYGJlb8)goG~%a`$3d9;N7HDY8P-jyml9UVtBjNsMnQFr zTc_!AJ4*8pYhF1Pzs`e^5@&4?Rv7zI{zF)MQ4YsroDph`T2-IO#_Qisr+tSk1GQ2s zAq%RYHclvRDRV8WkTR17v#c+Tx1vugliFZWfuw2VBZu zpw}m{TAk``TWWR+BZ`_zVBb1+htHRE?W)n@SF4Q;z6;sSnoqu`5mHzR2%fskE|T}$ zPWR2eAdGbh+kaw_!#+Xt7S#+sM}{JykVXm#B@G`_grx@y}j-yd~nLAWb+WN0(6d1bCL>z*RoC6X&j~uP=zhOMR*MS-jsV58R}l zXJMmi(%ui?T|-*`LB9Aa!&Ks|ZV`AyH1s0?x{>WU&^>6Gbf}llT=2x&qgAA!Tgck) z6nV}tSHE)wT|b95TMjZ42i%zShgOjHc`zqo1$`}#P=q|sM!)bc8hjoki?hM$@Oku| z{M3N?7RFodfioda^%^I>&WaE<8*5C?r&8onIA>|Mp!2a2h;`sEjM`p6cxcZ`I(Y#T zw>S#A2)8*G#au*7@>0uXC|zWg3`|y9{cV+7Y&kn|fC9&a5!H(aoa2vgrk16>%Z#3I z5pOLsikJIa0|$>M3mTQYs6Ws)FAhUt5;1)pK(#Ny^iNtv{>zQI-0@@uU|CE5{q*jz z&C@%hEwFo5(V|NjA1(ocgI`sCx2op-SEK0`h~!mt{t}pEpcBjCoSMn=G8*DT-*A^Z z?NR_<=3b3G^K!pG-s`?>D1`$TcB6#3>{>ZLz8RP6>u;Ak-4$D`#akS;DVb5WC&Rz+ ziv|IXV|>*I=&IFzm1u8)o4A(B{tNY30tlY>^js0KZRC(790P-W-U8EBc^?2#X4*e|vPvgq}^XXc`u7!Op zfQjqqCLr{m*U{hKr|S7nN-EqsS=CQyk;H1G52G1Z4M8CpAKzE%A1RkRtQRlKeC)4S zq20XU*6LUK!mSqR)Z)S0B$n@z-uC}w%P~hj%fvth5^E$M60&bo_mOW%)17K7VkgsYne$Xq@TEh zvRuccW7Q5h>wfF38*l!_9dAzz3zZ*&*}j81y*B!}!%$BI5c}OP^Q;?LZfm{i08*&UnbZ*q+9$M)~y>1|p29K8A z0HtN=-y5(Ll3*%(69!wH$>m%EZA$O3H(r7Yuo|^n4hidjS##l};~r;W6~$mNZ8u%N z38z2X2>HTg{c}$W4DMmi5m#PaK(DIP9p%wqRN)qEvOBfB1uELp+FP*tJ?It^bH07D z+s7Ph^f`b2(!94u4W<6%cN@Lcey<$UzFqXItA98<0sw4`K)3|ZsYIYP(T3ZosSBOB zjaL%R#F6VA!)E=yMRev4s0~^yN0_ZXnU>qdAK(#2`N{Js(_KT$l#^~G3-#|0((iX+ zpA_G6JTT-;c?n|sA$oQfIR8B)yZcR!XNk@q!tfF#%Sxbsn3WVd{i8!v8X{xPbXZPC z+plQUX+iH#Q4%l|@|~*R+}r@izQvNGv!7P4vIbkzHE&l6pGJ;8_AnXmV@Rq^1@B|{ ztbIg!$-ceLjElJvi(@f_RKxHq3Y1{biu>Tu7>Yxo?^@;>k}-j4+vu;02xsK}R({m{ zfe4y15yb~{a`#uES@`ZQL0A8MnKc=%Pj7C>!Guh>J%ky2) zX#Q3O#+SFp=vRgo0cb=E7&<$|@a04m0%ji$d3m74N~JL^zx{fXgcJ1a(h>Q~2ft zT}* z6NJYHP}8S|ExN+w@C@|(lNS>6OPqelijmaq;M_(JYM|)4UfL zl;57F9KLWB#ZihTd`~3S8QzaAwe&+`qTv;ce29TRj*-2_E@~jvPDWCOu6e@0s}V0f z)jAQ5pD$G!>;r0eFhFIENtYJ@9Ra|8{K=`AvtKH4b0`CZU~Mn}q8*hRHH;>Qe26#v z8BewHqM1wM$=40(=6K2*h?wIcl+TIsSI3;w9XqhRtXAIN>QX%Qd1dbw-kWZGFXr2-9-jM~&f!orAk) z$o@5|RK)3DouP;i7=LR88hK5ME0PCuYPjg&YgjjN$Z!e?@+e~ReuFbkPA5oJrI4tb zN5gEzq*9InSCGT*+t8e)1OM6D^_(PvRWvz)etiRXYs*>b$bOk$e7JXAKQGHm^?=v_ z2)#Bl%h2J`*@z*-hqGWpa~&< z))f5??VnG-yu+*@6~8fQOyQf6jNA^qvi%%pRIP&H;|uCxkJk`$BFm&}v^J>b6JUZY~4 zunwU#RFSNS6aD=O?<=HY9Ql4Wbke{zg_Z}Ur$3|#)k}o}u?1GdGAg039g#8xReoJYQk-*1eX8 zf)CwQcSNbUG1ma6Jg~{Yz+=u|J#$@sn`5XBIdXKg5~tt>=|3mZqOXYAXlJuO`3g?m zxJLQD!RbtQ{N{5N<#8z(iTOAkNsIo#by|i(Xj~*B0{9LSDC{WjoN&+>bm+K~huIRR%Bxs{`jl2ZveUtmZNDjpiHM=L&UbomjYb1_I6|yK0K}x}Caoje z&<&JUMi&!&FE*S3OL04a%L# z2u|Ih8kvlNSot2F$ry!!Le%h*9%M4co9o||GOxeUta;NHuUiN{i=ELeC?PW{?MSWd zkl;mUPJX4SIgpH{^|;f|yh}H8!1^w`OWm!Jez;3xtx?km+G)+%_o!7iqYFK=HU^s~ z+*cLVozka&_*S{;L(GsIDLe}p=J`O54Ob&`mEACS6$=&jIN0BldSx-jn3sW69veI= zHr-V7+f-g^W;X;J9ZNa~V92Re7f(|^SW?79jV7YpfHM+V!lD)ciW!_?bqc`wU zopNis)3vN%v?n=aLn*Y%8_h`5s*vC((k6JHnX^49xO8?4y%EUa&0d$AeRZyXVssOM zSj@w#(pts{ejUvQJ~g3FxW&{bS9W7iXvPfqfR^)X=+`DWR2;W+4M)b5GF*R3!?MdB zn3dfasDJR3PU8M2yb7gn*)eJje9ED81&zWSfVf~A5Lb&P+JdP&p35o1 zstbwLdtY*^WT{&<=J<13WWb;wiZ3`nr!y$(mWVRE!Dm{nm9JJm>c{G@4C%e+l-FQ% z34H|!W>FpUwM~)k%hy`4epNZ+3U2&7?!zi}HdR}Jy{iFjloh(b%iV4;M(H&cC_4C? zBBJA*^FnkopaeNmW@I0Wg)Sjw&eoWk#U_(SPGc@U=3fZ9Gd(pL^Qd}raRo6{&=vxc zNR2rWoZ>lQ92f>=wS$V#LmT-OE_tgo2D?qm=YS4MHPZAZtvYRgO&@ch2b9izuZTfu z%IHLm zU&$p+(PeGQCIrz*d!y@5Bu$n5h%>nypgyUZLCdn>Soe7jKv2O$ddA;?_C1g%g_kbmWZ< zd^#5tN&4)e`vG7NfUA3|FTFddQ!{}GrWT+7Mven=KDV*@SngZyKas;G7)6z1k9oH6 zhwgl#GpI83z;)?)%(WS?{aA-X@d_1$d>|YK_JA`zDjv1W>d_!Uh43`rexbZBAU6jU zcQNwrrG+jKgIT2egk3ZbT#W8=sT0G)b;@TlX5c*(=Yd9g3~m`y!M+4gU^u>HLM7H@ za78yL>eZ5JNwLeh>Kl0|N7BPNbL#pXr~!J_2G(Tw@*G~9wW8S5clSFaA&0FetdW{? ziKFPn?nEHRgzYLPo9K-zBtw29K=UTPchQ41DbO+{wx4vf?1MSz=UzT&g*H*_MPJ-- zn@WPT>7hB}DM~8gGPlXB7ektJ4de68dxr2TznJI+TBVvXYVK&dvp-ne_y9B=D|GOZ z(#{2y=xEeSa`hK7lZQJxD0Q1uV>H)V-MY=P)(gQc*t%c7TD^MD7kwm-PJUobFWfQC z_Q|3bBVCfw!(HT4Ii^>rI35L59=i zl*V((PM58|`Ef3z**=@(#srsH`|JOkmpfGrK_-eS0F>(6GWjTm?|f)35_1?zu`kfP z_U*SHyG`eHd$ydA_wp1EfDpe{^3o1ne{`WVln`tQIs8{OMWAsjH!E%4^`!J z)nQqhQ9v=Q2SI1M0EXShCrg+4&yV`N6fk_iIyz=B)h-A!#3ycWvIO9A0*>0BFLY|s zIzLql;O1weMFlaCY|KVG3PN_==qbvX{j*Ey*quG=`BOIvWkmrsx$`5>LdN)XKV66D zP#JugF674#@M%$`A6q+&kIw=f|M8=CF%$>fx}Sa9j-!88|LwQ!EFZNC#s-1^>br7O z$i>z%8$y6=#jB7{QAm*ZGAy@5$UXj!=1=?(ZU^qk?P7*jqUW6l3cJ+v5_7CTeXGC6)K zyA?>2d|?ez&-Jx}D)^?&2U7>#VzeI`X2rMcKy0QjpW}m*b)K7CJ ze}7oC=GA2cZKnN*$e`xyDhk%|!An-(4<@ce{uoPx>6^c0WYDaeYMwIW%|w_?g}`Y& z!o=qA+)^bE%)OZK`}Vb~QHPoz{M4eS3ZSLs)c4exus`EmAm#?C#weW}MD!sfk;_aZ0?zMae;6 zi2`&x3_y-!v{-GI@0rH{Cs7H$4S~;t;I!yqxP>}bz1W=AsL7}X+jLuaxW{*P7$P5X z3Zm@v!oyqFe^&Fo%7J@VnI;AsO`%-@z{mVNE>b6M(V$n8EJdfOoEsspUwm*V@Z6HK z&6N^St@#EZyinGBZdRMpEB~x(fv_RF5ID3Y&3bXj+uCt+ukNeci>0B9b)P%cM~v7T1sYN@L{fm1<7S#x^pm3VK94Z3AxholOhbNcZEc9c-U zxG+U+w1t9HDx$e@YWcuLc@SCzR&%kaNhwU+GXC<7T$j*KDnP4C89U%`B8Sq5LtQUO zr$<9Pk_%F+(#ARP-ag?mOdOxVAJVsj?26Bqs_84%J3SVyhY!GwbM=+8qp)hnp9Ri! z9f*$u^85l8sTmCohtDhD7^55EFf9>dwDdM_P)Z?&!B~0k$_wAt_YopFjmNJC@OuH z%ao@Ky6(qsLJm{knQczB0ia)2l)9FI)@3m4qm?OfyzONqubwgdfrru>k9$0=oA2eC zycLTP1AZ!bmPOk|C@aYA&eD>e(lnv;`a=UI`(4=tXaGIWl|x^59U*)4yU!r1BaFr@llzM*9%$S4RN+^>QMnw$ ztmcy^K9EQK%VE&>p{YQFfWVq^V8ICbP|g^W?k1=8;&tQ=V z!_r$iT;t>Zz+Jf^Dp0O+1xeRAccuUK`t$kLa4~0~OxXZN$ zb2`BQrR@JlD+(qGt7OcntGTs$CF2{N7Si^jrX1OpGe!NDV46||15XA~2e{0$v4Mv@ z@&&)|EWh$gjuoO8giVvX=Jb1m>EtlT%86iU{BF#C8MtEj3SMqQ*obZBrC{=|3T4;U z!0rT7E8Lr3p@cn9kKDTKUF}|_FQWtx{V4JM;!q~~Rt;+99HJKsx6x}820XoF3IQ;S zBBZM*-KvTfm7`Cbw4_|sjC0`&#-bFj2%M~jsxqFNN%SF8O33L->;0aGD$g>#0eYT2 zp!+?Q|JWt`&#}GEB_0tuLaaS$YBa(It*aZygzhXU%RB$s*w|-G-;;_wls~9)UgWFg zIOfAjh#XppV2_nm>aBQr@g!sMRMUO=6;l1FlC-D>v~N6}t6>Z>*DfV1|F><9zek;J zgLwKdo;ATn2~A1)qNE~teiY&yV(&A50TR_p9?**@#O6&A(l$i4EK@o2nz`&PsAbue zRHh~r_UdpMqsx=;R{nk2^KA!!dIhvZj1-k8?V8lTR(f9rptUtNK9P;VXF2Na|2?e9RP!ishrx)|fI<4sta(b;mpZLAgjF#s<;4AoDT+@I0{I z)xkx)+`>BOb${ZCJ)Vd~9j8)aeLfb?I8gO&Wt$qm{p&sgwDN=vmS8V(*x+xP8mqUR za@tBTK-h?cGUQteqkV3wT+8TXeVr?!fZvUI%t`pkyDOH&-5ylv+$0uG4(9~~V2x=# zU<;Vb>aB_ZU^Y?*q*V@vJJ;-86K5xiY9aEg%hFdMH@^mi3m_^S`dp&SiC(;WRn35n z6i^!?o$3v96Sfu%$5V6hNWIwacP8|SuI7nc*fgURt3eJu!M?q;zOxJ;LSirBd!emK zR|n5~k{OBHNR;Q9@zZv9AH5wrM24Wmdg|0js#OQA&O=i#;_Kim0q6`syUYu2w+wWX zj1#i9Cz2M_flyYW1V+4x;homSy zZHy$CdhuN(jYnc`SWa?%ppRG5uf<%o{dCZePP7*QZUX?23yoU&a^RRovpsoDmk*MK zr!b)$JsAWdmX)I%^nB0JYyNFB@XG zUFvc5=~Hb5P9gls<*5}Q@Zo9jM#MUF6{JyEG}|WBeftDfWUe2gCM&>Mv#3lRF~EB~ zGCXa716P7PCN`!KhFR6O;`9R(f@^$iL2AKF3zKd^@zA7G0;#achx*xNsb>>3NQtS0 zF$Y$ZXUSf0+m~2(^TB1RQ5Xe;)QRx85`pybq<*T0*7fXbbjGKTRXrkKe2iY!NH3be z$w+trb!vo2RLU5-p**VqFXPn;V1N_Lh@wovrixCNCz4oi!{P zP(9SJp*fUIu>o}|lBD))jF5zgV<;pCop6AAfR&^>B!dGkfPQb3w7PDUV){S4tarC` zpN>wU$=dlA%wbuhdg8|maB2HSvk;P6jeDt|e%F;+#!pP_53E)K!p#c;D;BrX`rJ7$ z6e(j9CXAI*7om+AgUT+`M1?DSF&J{M_^!k<4<&ar`qFT{t@Zc9rWsSs&}A%Od#Py~ z(B6Z>j63rnya>nJu0QW9{^3OX+U-<#fcdG3(v&du(-cy_R~?JY%cpMMYM{;K zGT+--e<}iX@X8U);F{VrVxU}F73Ny(*iYa=6(o7(?m4|#-9gU;NdjEyU4e zqy9OCN{H3?@gpouK?>vLxX2r@FzP8i*JFtL-1BEcTuO)NX(2|xpNu?8<5T(9$#$UTzW_>|a}#Qn%B8U!n&U;oy8z7Mxv2h2pw^x7Tx5%t{ft{K%4(KD6Jyz@ z3XK@lOWKPZ7DdHfT@)19Rk1e|k)p(?vBX|5nixCA1Why+>}Y6q#e#`k8}@=i1RM4md+!Yu zu~)GS$P^|HFBD}(R^yS)L!WBdKkiMOLoFBGH{G8dr-XwYbN zi&p@|_=(=Rx1$**3G)FI?~JBjs%mPTktywB7UMX_*)Kp=57F9Q$|3E=`3fD z8sNy{Rq~HoY8GxJ4YHT3%f6xl{a==UKfO5o|CfJ@+Im49&LRQ()Wg`bxL!K6QN?Eu z-Pq=ggJ&!S{%;nF$!*X_LX_N!GZ*NBq#vnFDkU)1r?v%k=6z<5l?{%dU4!8+xZ8yc zLmt=SPc&@e#)e3{|_w0^6C|Y>r zDTVHe7U@SR05pB9rea@I4u);&N>ztqBNFN=P95S~IV+_?gVFBmO3_1gmGY(FyX*## z^HJ^%D(!q0b`#pAr|p_uce{<{LzYkpaLYrps=Mgt2H%o$S*joeu(>NL8bzA&{Oq02)@hUr{QXHk#69=}8ut~LGe+Mnbn zpB82O=Z3QGT4m#MUdU{PJ_9P7+L3K;$Mwz zI}LegK!7&U`oY8G{s(yNG9cLc@WY_A;T=9W{bR5hD_hNKM-#VH5LORED(;}2?T4S{jyZWIXMEW1yJOmPamC8 zHrN#FEB0*VY5=Fl@`Q&2cMQazVdOamM+$3bq#uO>M(YY`Z}+9`qjYZSDH5NS{lrkN zwENfS=J`VG`-)KW14`KX<&{)&Mxu6Uh89<>kVzdn*3VRBG-g)TcJtjTFK`iujkzp( zw60R#+dz?RS#-?_=l8CU#^h0)FfPVkbA$)t;QbdVPa_weLHBtr!8)cO&KG&$e^I`| zEoFW{IAA_8AQCN;rMnhakJ(*|HAPaCT*rbS@b6R|3xZ4^K>lOl<+Yy1lU*pA1_%St z_`zGtYMbQwU2C%dzwo4z@wj}B;hmR1?b6rY5!LcmQTPnLr)ru9vSepWi4VP-hc-}3 zz(2kSD6Pay%NwLsvXK0gM?dQq8sq^088fXMl^6%FL_+{5df16!mG%^x3;V4K&;upB zw3Hrpy`IAk>R~Ps$xEG$HkkZSCmK7Z<{Ttn}fOWING z@#w)Y3Gh}^W##LKjh~nS=E{=u`aDjzXT=J~HT>g#nwd%bL%2 z@kt>`9(&q>#(g{n34rZUr-yfIw;;+4&`y>#o-_Ho))`m$xS^~%ob(fb*L(o*cKBP~ z+P^NopPL<60N@edD@&IDa4_+~@(~<4NLGC?h&oRIUJipr-P{chR|}tibgCJm22JJ0 zy8?jk`qlFs8uQnQ679?YV`RyyXC3~$TxWSlb4mPQy2_281_T>~caG{mX>ZE4nPvzX zM=xVAxlaUO=^+9zasH;Znl%%6+m$_NPTeK~i$MUWh?ZKv_DfrQvD7egOEI$K^uAEd zu4z(@M;Dgrr(DOINx5SXTRQeimk(v zSTvNXO#<)i2826Wdc5t(jo*9ylqD`;8cM35B--W=$J>#6vwXuye@wz+*>spz$=Mh@ z>FW-GL-W=&H;#ZZ0MITSMn~`s<4XX7ufsM~no;cW#=q8RZO-5q=IILb0f4?8hHI7B zfG_>K#N2N;YM;5KNwVZz?44~Jj!p|UmuwqOts>EQIv`-73VYe6i*rbF-s@(FG87&O zQv67B@GzmS2J1{%9l!Ab&F|F|*HjdKJ{U|V(2|ec2(6|LAg*s3vH9Phma)h!iPcb+ z1RQsr`Tp;{Sr|EjicLmO<(9=;lbR_xYfwP!kMkuM|4cu`DngZNQ(f3nyUOsh0g!l8<{_1u1C)bxN73-SKP{ON7vCiS&UDpg@V?67w z5EXjUHnd<03Kd60{2Y(?*X;ZU8(t+qOej5_f~`+{N=5w>Z28Fuj#H+h7qa`G4#CLB zOvJV>QbDV=)V^E3e+jGwfwMNDo@!CYCEL47b{VA7+BoKj?vZiSVmdl4$382XrhxH6 zc8&|!FnIETvms_yQoktoyX;XWMRw+_Evc>N)T1mN&(<+34EZJHh=9RqtH0oDS=n1F z_cV3O`W(_~Oi+d^5N+lgH3PTSg0wlI^SSS2Ar7OPjjrlUmElcszNJca_(Ck{_8G>= z#OyP0z~sYB%tq^{p*ZWd$`2}DmWcev_bjBa#s^kOqmJr-{j`kM`k7`dFf_fHB_z8m z-EO_Qwr_1wOd&XCIq16uiA+}WOx9$thze;L!}fE(tEgV=P1jJi$xLdq00Vt2f+ z{F)=xp9g(d(N(;xXdr4$6$kk(x;_tbc$OfyDr;E;yUm^gI@qCz}g{+Mkf~NTzx;*z|8&6>Dnk1TepR{f%b9TQS-&J zirLn8A+Y-7bYecI%?okB$#$bSl(+cs%;EEYJM}A$S=8kF=*YE5j~8N_b*Frba5(S~ z4Oj#xsW0tV1a&onp7CQBDUl^aXe2;(LWCq-0UNwnF-xpitZV5Mw@ahg>VkKM&xmjM zRYWN*ObGO3v97HMjih*o|7W5jvHL$`2{2INAQj0C)l7s!ayI1<2MG`&4iYp_9O+p> zAxp91BJk17rP%zxFz`|SMG&c#z(*$KPe>fk(MQ&^Chpi|NWOeyOa+@aODJF&jtX9; zh3oBmBIJw5au{-oQt#!u%K2I>6;2L!n!gsCO$6>AvEo+yVA=Bx=129& zWrJ@hheH>kgzFr;UaQ-vpuIQDB|A{UYT(9`t}S|m2X!--Ttx|+C^QXzo#pBiUO|?y zVfPJ+IK}1aI@&gOo7yfw5p7$EC|F7i6AnpRB@X7s$XKEAub=g%lIH!^@}+ng^y9^C zb}WsIg8=NlQ#ggy{MCR!)Crg^s@J51II#7u4MpR19cu*R2dIri)P|}+I>6sZNFW;$=ZtizCWGZ3^*q`dJS;pRtVRbYeK!b1@-;kXpxjl zv6dn~>)+g7P1=!;z+!CCy;@e-g8w<+@L~9!5oOROpO}J~tQlo6Y*&hVg+~y1tbrCY z5l!=qunWmUKC6P@0);J#Z8Mt1vZUqRd^i5={)&-fAsq8@E9T=G+=o?(#8K5PL%3&( zMOkD=WpQhR{WFfzl5xJmQzOV{T8z)I(WueM^UmJuGiIR`J}HGR ztc#-s>tLkW2MA27u&Kx1Of8>YwYBUdsC7}6#65etsM^`b2b8)B(PvDfG}N^gcA76w zf_8{-yB=tDpeOv0V=3Q8QtDy+SeV|OGK0t;1qh;x(zHVm{VeM@--MZPBAgO8LLG}t zVl(tAicP}3@=yxRqeRE%4;mu}h)8JO5z|W6I=-u%>^7(?Ta*t`i>7TrOR`yfGb*>< z%lIxq3{d=O|GZ~9-*1B^WbE{oRIH-jo$7AX>jOL3)PWpsGm_oH5>1s1!vH|@`@g}UJPEm*okFsMpEZNeH_v<34)zGK!t&QdIg@e^ot#dZK<4Wmh0(Wz`>WNN%# z98oZp3ieFQh+x-UE_hY{pgjx)Vy06`vvdyYPeUiwkXo#;Fw+PWQpgdm@| zGq&(Rmbr3lra=kes;NbCSY%Xcx7|PmoBeJ~ zH!G35C7;+*)E+2M;}9#KH`IZ9Bkrt8XqL-7h8d&j>*F8cZOVB9s~wfy7az#%wm?pO zRRem%%g({}iE-AR8bgLX_#z_Q9@~efY)DI8fT_`WtW0vfFR69!lJB!%D!!CSZ-F`p}cH=9c6bOIm#qkrd_?-?1c8_Hp~-QYO0`Ys)&;za!aRWq)nb zZYqF1$#>6gasO;xkDgcTiVqr$e#)tz^Ol{wn@-qZf@7*>&L<9iyfq(wv^L3PzKH-j z>4e!RrkP47N)d#7v} z#Ak%$q>)GR?hQPWA}POxZNC4=O9YII8HjeDCTAf&1DM%@WwsCyN#Wi~-B zb0e)xo~JR@vhS0jF;@CkBSu~?*$;MrT{EuZiForAcMbd+|UgV!SeG10`skP``APQDqf za0Av6sAD;nh*njUvVWsEzVSRs2>YX4dLB7czMCuBL@Yd3e5`gx#0@tN#Q9G?0=hf2 zRcgPKEnOvFLPeNC4i|8=qY2f%pj(Wn9jO;U0zb;Y!<2YR2)_SpHe9Ln;6B^=pc*j# z+fPxoiwNaW@U(!K(=4^sim;xW0RhE>A(W^LjudrK=cgVR5wYv&{Y4DcB(h1x6^1I8 zbbiL)&x%I&B~F``JT@(_`7=TY(E@Rsdq-}V!c6AT>IX9!^ zq~vqM`L6&jZ`emsa^>gxP+b7vB5X20aK(=wel(eDJ-;Xxnsy2EPr4hf-8Iup!pU0V zauXG}3WMJkTw-L?izZ@w?14r>?8=jRjMa;Jeyl;_qF&dlSoKp^ic10mmR->dDeuw? zb>*5EPf=Gfxo#y>JOf&}u_(JO5TjQ>#bl*9zE26_;1Xoj6wq`gbx%RFyRXnler-(& zctvoTgDJYo?SH(g)s#X{J`Mw}mRMR|3^u{2!MwsLZ(#6L^}Y;4Tl_CFxB3m9jL^0?C=8sz#i<%-+VP}^6XUu z%mA}dQV1oh8%%$_p?;$zb4mO)>U$j%WB)Y@xs8dTiaaN;(N_6;og2_b!sszRVTw!@ z7T@W+cVBe!cen@w!Cu30e3qwDz%AwC4VvOACgTl3qYkm%^UkL8u(E`}v;pqZ+)JUk zccAD`ya5Ge<_+2jaNnf>=ksl`6*G!AyKoBNd?Oh~FU?Mr@U5b|nnIiNrv5f+E3j(S6r_42#jg_GrJ_?df=GMgqn(kcqzMGyN+ms+hl1PI?uOtR8frG=a^jNmu-fuI;>c0KG! zq3!oDrSw$r0aiPemf}k;#*dgzn#!5!xFyrr$6~#x6LPcT;OpygMUj^tcu&-pnT4hR z5-z+Hd_l4ZPqfSU)o5ip@#ceF(ke!bXctCQ#anQk+E0s*nVLKidvNQEF-NAZ+Q^%f zTtc&SwCY1$pwaMDK(0u+wZ0Pw2SdgZcaLD$2vYGLTB zRaA{01p{Y@ihfsFDMp0p5aZ~d>ALADlgch{PUF>HgBkE4u&Kl(Dw2BAPz45Ua|O3 zEC2clJ^`LlnE9G1^94_}xzQ%HVZM!T6rzAh_(EtE?zo@^1-u43D%lj&pH%e!XUIiI z-oQir?52>{f_FGxb98WeEolahQ9=xQnGR`f72U=*A<%JX*}vd01Synj*5|e4pMfpt zDInO~F@Kitze~;@i+R{Tn8uR3`RR%Z6p>9mkHC=eyl`=w68t@KWfF>i~ z0k8@H9njQoPNCC+l8Vm-pxk@v&<+6N{`t7K(5}zqrmb&*f~<$I-H+d9X{J8{`YQ2y zI~*G5*r!u1G$X~C&QzP5k*!&LJ?Cly-js7c%!n|WKIL=UDnjUV8;=&hrQgF`X747` zBnHG{X(?jvhna!4KXyO~vLKcJi1e^!|GNxGj8cy8c#%vm@uUZ=ZZ=lGa8~E%e9PrPio0tyOlXAh{CO>eI7z{VF@&S&Q{n$ zHTnB$^{tF6>}?gXBC6f#VfB2@U=LKZZG1QLR(sY9(DMEX-?LWknzk{+7slSgQa*?Z=7vEkn-frLk!?@-^-M326p&|F%<7Kw zD$Z^F!Hk|oo3b&Pbxt}zq+v08(+Z-#$w(T_Cj(aPtY5W;TadrP8_FtRCCi?qNOr>{ z43gG5ClSd?lrO56ohVtVoBkt0!fj8@E? zIe<#f7yIbEEzFxXRYi_KksR<7{@-k)`X#uSHun*m;f~OmZ&T}a+Y|J!Ztp6E`4C!i@`zSzQU z=^lE0OPj^CS`XjzJ-``Iy?W93a>1S>SsP_d7Q1W{`hbRD>p(VeM9AlJ3adYP-2`t1sW8Ojnz01ZSwcm#KUkU(Tim{O`rtkv#mf9JG=tKcHPUjb*`vvsX zIh23#g8J&PcC;yoj|rq`UO~NUPMDzw3TDbxRd0HPYB_xtOL-WTG@zFs9BmazEHUls zgmHN~eG9zMM=wZNF4PYIHZp$B27}%YCy$=+PYN!TqtkVH@h6bS;^d$&h6~V>9e}AX zEqBoOgrrfh5Zdok)K)n`;a2?TB27pAH3h-TSi1xj#mySiEda16*f6Ttwn|^mj24y$ z9*(r4G`*0%1u_hlOtmjU{}$30(_VBY$HMyR#*5CP*7PZ^^%}08mjJZoY+g;>3j>Ss zG@`J+r*BVJA$weW+_KZHckVA(OolFqee9F`;_RqzVM;>X!obY%Ua|9>rm&nOt;(u9VGvC!iZw<0`agk|h1*{} z9FV;oWAWlbPMI6yaAr>036>ybx4dabmJg{Is!xu!>WXB)^_(%_W2h4z+Q?iq#91F^ z4096$Zy&g{?zc``mJ2JIH*PUVeN+lArkoY2$!-+jf-w|PqhN-?WL@M&t03+eD};&- zEM(lP<4d2pzBkiUp0tw6tI5jA9!bn~ZdA<`Ok~Ajz$*iIR1UkUX|fPSiLQEYlhx#^ z7*mZVX?HYo_4-&5B;=4=*(g}SMAvYJ0ZD?5R%8lB80d&B!kPJirOtcIsCV@p{i6`c z<_7#Szrm9JHXsLUJD3O~Y+T6CbG<`!e(KrRSXG`ADX}~GOg`MDv6(P z0E$c0A7z?hk?P!7w3JxX$0wO$dp5b$2(3#hmZNRO^gWHy0N~?cVG-fpBkugG0|2_l z+OkME%o5T!I}jNZqC(b;<)#b6ga&u6fM4H2PR*O5A|E@%0Lm=u^~nK_VpGofbp;&! z;TUfvCe5DR)UX6rMt3-f`I6NxyNHRU9q-RU(~{xZQB(%IqSS1ZNYNc4^=hjH&{aNt;0sw#X^D>Y| zB)h4E7($)OGTsWl$YCWt(OG5`T`mi~K~{SoLRHtHe>j2RUJf#j{2AR2D5np^avog) zDx{p4l_{ef)*&k{XgT?$c&YpNT$lt9j%M`$87w}j+hLqbEU#}RKV=NABz6Z~u@S?n z-`>i(cBCmlo*2xUjyh*df+@TLMp2}A=Tnddcl0h>ceL*k=&cueryN-H!Wq#v6;VZc zzkMraE=aPPinaDmBBfPS>b--XTy{bQlIiB9dp&2*I^c!OAv~eZ##8P>)=2N1Kkd;y zo4x3`v`L7=qH2XtoV=8%FRQHgmgsOW{QbB9BncoC2QP6>-BDb=R}?r`+5mn<6qz`8 zkRY29K(E{(*ygMj3N^~OyQ&aPM$~q_>+tFx+YRNElRPLM#yZ%KSoAO4_wb3kf0o7W zkCj_VJ{3p_I5nYmQ}CP;gJ>YVLxJyD53%MS>XF_{JE8A>)I=sr-mhk$gay_3)U{6+ z_MA9TDZx&y(aiIp7TC#5pWW-UIPW{VDpoFOalKs?!@rAcJRnrdL!{&g9Xo4gWjQjp)1w) z#Izf>P8`IZ6tMHj^Gmma70M|>ubiIp(U_Kc;tPTibJz%3smg-5ua$Gqa<^WGeVof9sRTw3Xv3KJW=K>XbW8s}2guwDRh76I`k?sNB~ff4XBX8lU4dD?7Gsy}Q%? zS()7iWhk?DQV0hYwIwPqy@lE;h_edRJU ze8kH0v1eQ@x$Z+pypqi}Z_4PxC26gY-v(GO<*BkyeXm%ablSwu(xPmO&Hz7%`hYkB?pNjNJeh5T6e&(ceS%Ho}^iXY}4tNM7F%M&kIzT=m~l6Hr0rq`C` zH?DP`vZ+pBq1Dat1Itf0&bQm@9~jxEJ$cs9*Tt?lpoYFg$?zc>MQ=IujB1DM8vgh% zyfZp4YfM{KahZ!(46SCc9+0JYt|xy#VoCcrHqG{>TK@z_0&67b<*Si%D$2)uU z*Zkda%<{I=RubL~c)xRRdG#V2+yAzl2GrD-fd4K^JXYgTveRWjA$spszfWTGLiu+I zT${Z**eCbn#qB!LlP?4F^b>RukJWzq5}^&Qh+7*6Ca!t=kAItGZSl_D^BaRAel`5u zG`S1jxkF2PcbXK~xSnZ?LAX+eA5C~0G3<`dUc9sHJmU9r4X^dY1OK7Ti+m^Xx(cz5<>#p9{0+{@q{KtX)mN@HjkIJYMaZ-t-yAE>a;0<{|j!Y)5g%hq`hH)Qd!ev0Bs&*C`;FZ3^nsllRx5xI{E5dsrEp_ zxIbf0=?p{|B1UYZd?i From 1ea6446faba791c64fcdd689de601e6d800ed731 Mon Sep 17 00:00:00 2001 From: dswbx Date: Tue, 14 Jan 2025 14:10:54 +0100 Subject: [PATCH 02/32] clean build.ts --- app/build.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/app/build.ts b/app/build.ts index 0366321..5931a7a 100644 --- a/app/build.ts +++ b/app/build.ts @@ -1,7 +1,4 @@ -import fs from "node:fs"; - import { $ } from "bun"; -import { replace } from "esbuild-plugin-replace"; import * as tsup from "tsup"; const args = process.argv.slice(2); From a4274423a14688c62663040abc1a4be51bd9952f Mon Sep 17 00:00:00 2001 From: dswbx Date: Tue, 14 Jan 2025 16:01:24 +0100 Subject: [PATCH 03/32] fix advanced flow creation due to schema mismatches --- app/src/data/AppData.ts | 11 +---------- app/src/flows/AppFlows.ts | 23 +++++++++++++++-------- app/src/flows/flows-schema.ts | 2 +- app/src/flows/flows/Flow.ts | 4 ++-- app/src/flows/tasks/TaskConnection.ts | 6 +++--- 5 files changed, 22 insertions(+), 24 deletions(-) diff --git a/app/src/data/AppData.ts b/app/src/data/AppData.ts index df90b57..210c834 100644 --- a/app/src/data/AppData.ts +++ b/app/src/data/AppData.ts @@ -69,18 +69,9 @@ export class AppData extends Module { } override getOverwritePaths() { - return [ - /^entities\..*\.config$/, - /^entities\..*\.fields\..*\.config$/ - ///^entities\..*\.fields\..*\.config\.schema$/ - ]; + return [/^entities\..*\.config$/, /^entities\..*\.fields\..*\.config$/]; } - /*registerController(server: AppServer) { - console.log("adding data controller to", this.basepath); - server.add(this.basepath, new DataController(this.em)); - }*/ - override toJSON(secrets?: boolean): AppDataConfig { return { ...this.config, diff --git a/app/src/flows/AppFlows.ts b/app/src/flows/AppFlows.ts index 34026e8..4f75cbf 100644 --- a/app/src/flows/AppFlows.ts +++ b/app/src/flows/AppFlows.ts @@ -12,6 +12,18 @@ export type { TAppFlowTaskSchema } from "./flows-schema"; export class AppFlows extends Module { private flows: Record = {}; + getSchema() { + return flowsConfigSchema; + } + + private getFlowInfo(flow: Flow) { + return { + ...flow.toJSON(), + tasks: flow.tasks.length, + connections: flow.connections + }; + } + override async build() { //console.log("building flows", this.config); const flows = transformObject(this.config.flows, (flowConfig, name) => { @@ -67,15 +79,10 @@ export class AppFlows extends Module { this.setBuilt(); } - getSchema() { - return flowsConfigSchema; - } - - private getFlowInfo(flow: Flow) { + override toJSON() { return { - ...flow.toJSON(), - tasks: flow.tasks.length, - connections: flow.connections + ...this.config, + flows: transformObject(this.flows, (flow) => flow.toJSON()) }; } } diff --git a/app/src/flows/flows-schema.ts b/app/src/flows/flows-schema.ts index 4fc2c2a..d073d15 100644 --- a/app/src/flows/flows-schema.ts +++ b/app/src/flows/flows-schema.ts @@ -62,7 +62,7 @@ export const flowSchema = Type.Object( { trigger: Type.Union(Object.values(triggerSchemaObject)), tasks: Type.Optional(StringRecord(Type.Union(Object.values(taskSchemaObject)))), - connections: Type.Optional(StringRecord(connectionSchema, { default: {} })), + connections: Type.Optional(StringRecord(connectionSchema)), start_task: Type.Optional(Type.String()), responding_task: Type.Optional(Type.String()) }, diff --git a/app/src/flows/flows/Flow.ts b/app/src/flows/flows/Flow.ts index 2890b7f..43356b6 100644 --- a/app/src/flows/flows/Flow.ts +++ b/app/src/flows/flows/Flow.ts @@ -162,8 +162,8 @@ export class Flow { trigger: this.trigger.toJSON(), tasks: Object.fromEntries(this.tasks.map((t) => [t.name, t.toJSON()])), connections: Object.fromEntries(this.connections.map((c) => [c.id, c.toJSON()])), - start_task: this.startTask.name, - responding_task: this.respondingTask ? this.respondingTask.name : null + start_task: this.startTask?.name, + responding_task: this.respondingTask?.name }; } diff --git a/app/src/flows/tasks/TaskConnection.ts b/app/src/flows/tasks/TaskConnection.ts index 1a4e579..fb9e102 100644 --- a/app/src/flows/tasks/TaskConnection.ts +++ b/app/src/flows/tasks/TaskConnection.ts @@ -1,4 +1,4 @@ -import { uuid } from "core/utils"; +import { objectCleanEmpty, uuid } from "core/utils"; import { get } from "lodash-es"; import type { Task, TaskResult } from "./Task"; @@ -34,14 +34,14 @@ export class TaskConnection { } toJSON() { - return { + return objectCleanEmpty({ source: this.source.name, target: this.target.name, config: { ...this.config, condition: this.config.condition?.toJSON() } - }; + }); } } From f47d0b1761731925b5886d0eeb51ffda3a67d19c Mon Sep 17 00:00:00 2001 From: dswbx Date: Wed, 15 Jan 2025 14:50:10 +0100 Subject: [PATCH 04/32] update plasmic package to omit cjs, migrated example from nextjs to vite/wouter --- app/src/adapter/nextjs/nextjs.adapter.ts | 17 ++-- bun.lockb | Bin 1078440 -> 1063560 bytes examples/plasmic/.gitignore | 1 - examples/plasmic/index.html | 13 +++ examples/plasmic/next-env.d.ts | 5 - examples/plasmic/next.config.ts | 8 -- examples/plasmic/package.json | 27 ++---- examples/plasmic/postcss.config.mjs | 8 -- examples/plasmic/public/vite.svg | 1 + examples/plasmic/src/App.tsx | 88 ++++++++++++++++++ examples/plasmic/src/admin.tsx | 8 ++ examples/plasmic/src/main.tsx | 12 +++ .../plasmic/src/pages/[[...catchall]].tsx | 78 ---------------- examples/plasmic/src/pages/_app.tsx | 11 --- examples/plasmic/src/pages/_document.tsx | 13 --- .../plasmic/src/pages/admin/[[...admin]].tsx | 22 ----- examples/plasmic/src/pages/api/[...route].ts | 16 ---- examples/plasmic/src/pages/plasmic-host.tsx | 7 -- examples/plasmic/src/pages/test.tsx | 6 -- examples/plasmic/src/plasmic-init.ts | 17 ---- examples/plasmic/src/server.ts | 43 +++++++++ examples/plasmic/src/styles/globals.css | 3 - examples/plasmic/tailwind.config.ts | 18 ---- examples/plasmic/tsconfig.json | 28 +++--- examples/plasmic/vite.config.ts | 15 +++ packages/plasmic/package.json | 12 +-- packages/plasmic/src/components/Image.tsx | 6 +- .../plasmic/src/components/LazyRender.tsx | 3 +- .../plasmic/src/components/data/BkndData.tsx | 20 ++-- packages/plasmic/src/contexts/BkndContext.tsx | 52 +++-------- 30 files changed, 247 insertions(+), 311 deletions(-) delete mode 100644 examples/plasmic/.gitignore create mode 100644 examples/plasmic/index.html delete mode 100644 examples/plasmic/next-env.d.ts delete mode 100644 examples/plasmic/next.config.ts delete mode 100644 examples/plasmic/postcss.config.mjs create mode 100644 examples/plasmic/public/vite.svg create mode 100644 examples/plasmic/src/App.tsx create mode 100644 examples/plasmic/src/admin.tsx create mode 100644 examples/plasmic/src/main.tsx delete mode 100644 examples/plasmic/src/pages/[[...catchall]].tsx delete mode 100644 examples/plasmic/src/pages/_app.tsx delete mode 100644 examples/plasmic/src/pages/_document.tsx delete mode 100644 examples/plasmic/src/pages/admin/[[...admin]].tsx delete mode 100644 examples/plasmic/src/pages/api/[...route].ts delete mode 100644 examples/plasmic/src/pages/plasmic-host.tsx delete mode 100644 examples/plasmic/src/pages/test.tsx delete mode 100644 examples/plasmic/src/plasmic-init.ts create mode 100644 examples/plasmic/src/server.ts delete mode 100644 examples/plasmic/src/styles/globals.css delete mode 100644 examples/plasmic/tailwind.config.ts create mode 100644 examples/plasmic/vite.config.ts diff --git a/app/src/adapter/nextjs/nextjs.adapter.ts b/app/src/adapter/nextjs/nextjs.adapter.ts index eaee4ab..65d5ffa 100644 --- a/app/src/adapter/nextjs/nextjs.adapter.ts +++ b/app/src/adapter/nextjs/nextjs.adapter.ts @@ -2,7 +2,9 @@ import type { IncomingMessage, ServerResponse } from "node:http"; import { Api, type App } from "bknd"; import { type FrameworkBkndConfig, createFrameworkApp, nodeRequestToRequest } from "../index"; -export type NextjsBkndConfig = FrameworkBkndConfig; +export type NextjsBkndConfig = FrameworkBkndConfig & { + cleanSearch?: string[]; +}; type GetServerSidePropsContext = { req: IncomingMessage; @@ -32,10 +34,13 @@ export function withApi(handler: (ctx: GetServerSidePropsContext & { api: Api }; } -function getCleanRequest(req: Request) { - // clean search params from "route" attribute +function getCleanRequest( + req: Request, + { cleanSearch = ["route"] }: Pick +) { const url = new URL(req.url); - url.searchParams.delete("route"); + cleanSearch?.forEach((k) => url.searchParams.delete(k)); + return new Request(url.toString(), { method: req.method, headers: req.headers, @@ -44,12 +49,12 @@ function getCleanRequest(req: Request) { } let app: App; -export function serve(config: NextjsBkndConfig = {}) { +export function serve({ cleanSearch, ...config }: NextjsBkndConfig = {}) { return async (req: Request) => { if (!app) { app = await createFrameworkApp(config); } - const request = getCleanRequest(req); + const request = getCleanRequest(req, { cleanSearch }); return app.fetch(request, process.env); }; } diff --git a/bun.lockb b/bun.lockb index 48020ba6c3bbc98aa779e31539edf2ea2a537c80..a37ebf525c52cf973d3aa25038fec2b800adfb2c 100755 GIT binary patch delta 175865 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�{d963RhUV17`nf>7?EX>3E<5 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 J8A}9~0|1rF^1J{5 delta 184188 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%0Dx3Z#lo?y_A2V{G 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ν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 diff --git a/examples/plasmic/.gitignore b/examples/plasmic/.gitignore deleted file mode 100644 index d0d878e..0000000 --- a/examples/plasmic/.gitignore +++ /dev/null @@ -1 +0,0 @@ -.next \ No newline at end of file diff --git a/examples/plasmic/index.html b/examples/plasmic/index.html new file mode 100644 index 0000000..e4b78ea --- /dev/null +++ b/examples/plasmic/index.html @@ -0,0 +1,13 @@ + + + + + + + Vite + React + TS + + +

+ + + diff --git a/examples/plasmic/next-env.d.ts b/examples/plasmic/next-env.d.ts deleted file mode 100644 index a4a7b3f..0000000 --- a/examples/plasmic/next-env.d.ts +++ /dev/null @@ -1,5 +0,0 @@ -/// -/// - -// NOTE: This file should not be edited -// see https://nextjs.org/docs/pages/building-your-application/configuring/typescript for more information. diff --git a/examples/plasmic/next.config.ts b/examples/plasmic/next.config.ts deleted file mode 100644 index 3915163..0000000 --- a/examples/plasmic/next.config.ts +++ /dev/null @@ -1,8 +0,0 @@ -import type { NextConfig } from "next"; - -const nextConfig: NextConfig = { - /* config options here */ - reactStrictMode: true, -}; - -export default nextConfig; diff --git a/examples/plasmic/package.json b/examples/plasmic/package.json index d530a6c..49a4f36 100644 --- a/examples/plasmic/package.json +++ b/examples/plasmic/package.json @@ -1,26 +1,17 @@ { - "name": "plasmic-nextjs", - "version": "0.1.0", + "name": "plasmic-wouter", "private": true, + "version": "0.0.0", + "type": "module", "scripts": { - "dev": "next dev", - "build": "next build", - "start": "next start", - "lint": "next lint" + "dev": "vite", + "build": "vite build", + "preview": "vite preview" }, "dependencies": { - "@plasmicapp/loader-nextjs": "^1.0.409", - "bknd": "workspace:*", "@bknd/plasmic": "workspace:*", - "next": "15.0.4", - "react": "^18.3.1", - "react-dom": "^18.3.1" + "@plasmicapp/loader-react": "^1.0.373", + "bknd": "workspace:*" }, - "devDependencies": { - "typescript": "^5", - "@types/node": "^20", - "@types/react": "^19", - "@types/react-dom": "^19", - "postcss": "^8" - } + "devDependencies": {} } diff --git a/examples/plasmic/postcss.config.mjs b/examples/plasmic/postcss.config.mjs deleted file mode 100644 index 1a69fd2..0000000 --- a/examples/plasmic/postcss.config.mjs +++ /dev/null @@ -1,8 +0,0 @@ -/** @type {import('postcss-load-config').Config} */ -const config = { - plugins: { - tailwindcss: {}, - }, -}; - -export default config; diff --git a/examples/plasmic/public/vite.svg b/examples/plasmic/public/vite.svg new file mode 100644 index 0000000..e7b8dfb --- /dev/null +++ b/examples/plasmic/public/vite.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/examples/plasmic/src/App.tsx b/examples/plasmic/src/App.tsx new file mode 100644 index 0000000..da86876 --- /dev/null +++ b/examples/plasmic/src/App.tsx @@ -0,0 +1,88 @@ +import { registerAll } from "@bknd/plasmic"; +import { + type ComponentRenderData, + PlasmicCanvasHost, + PlasmicComponent, + type PlasmicComponentLoader, + PlasmicRootProvider, + initPlasmicLoader +} from "@plasmicapp/loader-react"; +import { Suspense, forwardRef, lazy, useEffect, useState } from "react"; +import { Route, Router, Switch } from "wouter"; +const Admin = lazy(() => import("./admin")); + +export const PLASMIC = initPlasmicLoader({ + projects: [ + { + id: import.meta.env.VITE_PLASMIC_ID as string, + token: import.meta.env.VITE_PLASMIC_TOKEN as string + } + ], + // Fetches the latest revisions, whether or not they were unpublished! + // Disable for production to ensure you render only published changes. + preview: true +}); + +registerAll(PLASMIC); + +export default function App() { + return ( + + + + + + + + + } /> + + + ); +} + +const CustomLink = forwardRef((props, ref) => { + //console.log("rendering custom link", props); + //return null; + if ("data-replace" in props) { + return ; + } + //return ; + // @ts-ignore it's because of the link + return ; +}); + +export function CatchAllPage({ + PLASMIC, + prefix = "" +}: { PLASMIC: PlasmicComponentLoader; prefix?: string }) { + const [loading, setLoading] = useState(true); + const [pageData, setPageData] = useState(null); + + const pathname = location.pathname.replace(prefix, ""); + const path = pathname.length === 0 ? "/" : pathname; + useEffect(() => { + async function load() { + const pageData = await PLASMIC.maybeFetchComponentData(path); + setPageData(pageData); + setLoading(false); + } + load().catch(console.error); + }, []); + + if (loading) { + return <>Loading ...; + } + if (!pageData) { + return <>Not found; + } + + const pageMeta = pageData.entryCompMetas[0]; + + // The page will already be cached from the `load` call above. + return ( + + + + ); +} diff --git a/examples/plasmic/src/admin.tsx b/examples/plasmic/src/admin.tsx new file mode 100644 index 0000000..031f951 --- /dev/null +++ b/examples/plasmic/src/admin.tsx @@ -0,0 +1,8 @@ +/// + +import { Admin } from "bknd/ui"; +import "bknd/dist/styles.css"; + +export default function AdminPage() { + return ; +} diff --git a/examples/plasmic/src/main.tsx b/examples/plasmic/src/main.tsx new file mode 100644 index 0000000..f328973 --- /dev/null +++ b/examples/plasmic/src/main.tsx @@ -0,0 +1,12 @@ +import { ClientProvider } from "bknd/client"; +import { StrictMode } from "react"; +import { createRoot } from "react-dom/client"; +import App from "./App.tsx"; + +createRoot(document.getElementById("root")!).render( + + + + + +); diff --git a/examples/plasmic/src/pages/[[...catchall]].tsx b/examples/plasmic/src/pages/[[...catchall]].tsx deleted file mode 100644 index 4de4a49..0000000 --- a/examples/plasmic/src/pages/[[...catchall]].tsx +++ /dev/null @@ -1,78 +0,0 @@ -import { PLASMIC } from "@/plasmic-init"; -import { - type ComponentRenderData, - PlasmicComponent, - PlasmicRootProvider, - extractPlasmicQueryData -} from "@plasmicapp/loader-nextjs"; -import type { GetServerSideProps } from "next"; -// biome-ignore lint/suspicious/noShadowRestrictedNames: -import Error from "next/error"; -import { useRouter } from "next/router"; -import * as React from "react"; - -export const getServerSideProps: GetServerSideProps = async (context) => { - const { catchall } = context.params ?? {}; - - // Convert the catchall param into a path string - const plasmicPath = - typeof catchall === "string" - ? catchall - : Array.isArray(catchall) - ? `/${catchall.join("/")}` - : "/"; - const plasmicData = await PLASMIC.maybeFetchComponentData(plasmicPath); - if (!plasmicData) { - // This is some non-Plasmic catch-all page - return { - props: {} - }; - } - - // This is a path that Plasmic knows about. - const pageMeta = plasmicData.entryCompMetas[0]; - - // Cache the necessary data fetched for the page. - const queryCache = await extractPlasmicQueryData( - - {/* @ts-ignore */} - - - ); - - // Pass the data in as props. - return { - props: { plasmicData, queryCache } - }; -}; - -export default function CatchallPage(props: { - plasmicData?: ComponentRenderData; - queryCache?: Record; -}) { - const { plasmicData, queryCache } = props; - const router = useRouter(); - if (!plasmicData || plasmicData.entryCompMetas.length === 0) { - return ; - } - const pageMeta = plasmicData.entryCompMetas[0]; - return ( - // Pass in the data fetched in getStaticProps as prefetchedData - - {/* @ts-ignore */} - - - ); -} diff --git a/examples/plasmic/src/pages/_app.tsx b/examples/plasmic/src/pages/_app.tsx deleted file mode 100644 index 80eb165..0000000 --- a/examples/plasmic/src/pages/_app.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import "@/styles/globals.css"; -import { ClientProvider } from "bknd/client"; -import type { AppProps } from "next/app"; - -export default function App({ Component, pageProps }: AppProps) { - return ( - - - - ); -} diff --git a/examples/plasmic/src/pages/_document.tsx b/examples/plasmic/src/pages/_document.tsx deleted file mode 100644 index 628a733..0000000 --- a/examples/plasmic/src/pages/_document.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import { Html, Head, Main, NextScript } from "next/document"; - -export default function Document() { - return ( - - - -
- - - - ); -} diff --git a/examples/plasmic/src/pages/admin/[[...admin]].tsx b/examples/plasmic/src/pages/admin/[[...admin]].tsx deleted file mode 100644 index b854817..0000000 --- a/examples/plasmic/src/pages/admin/[[...admin]].tsx +++ /dev/null @@ -1,22 +0,0 @@ -import { withApi } from "bknd/adapter/nextjs"; -// pages/admin/[[...admin]].tsx -import type { InferGetServerSidePropsType as InferProps } from "next"; -import dynamic from "next/dynamic"; -import "bknd/dist/styles.css"; - -const Admin = dynamic(() => import("bknd/ui").then((mod) => mod.Admin), { - ssr: false -}); - -export const getServerSideProps = withApi(async (context) => { - return { - props: { - user: context.api.getUser() - } - }; -}); - -export default function AdminPage({ user }: InferProps) { - if (typeof document === "undefined") return null; - return ; -} diff --git a/examples/plasmic/src/pages/api/[...route].ts b/examples/plasmic/src/pages/api/[...route].ts deleted file mode 100644 index f296f98..0000000 --- a/examples/plasmic/src/pages/api/[...route].ts +++ /dev/null @@ -1,16 +0,0 @@ -import { serve } from "bknd/adapter/nextjs"; - -export const config = { - runtime: "edge", - unstable_allowDynamic: ["**/*.js"] -}; - -export default serve({ - connection: { - type: "libsql", - config: { - url: process.env.DB_URL!, - authToken: process.env.DB_TOKEN! - } - } -}); diff --git a/examples/plasmic/src/pages/plasmic-host.tsx b/examples/plasmic/src/pages/plasmic-host.tsx deleted file mode 100644 index 7dac28d..0000000 --- a/examples/plasmic/src/pages/plasmic-host.tsx +++ /dev/null @@ -1,7 +0,0 @@ -import * as React from 'react'; -import { PlasmicCanvasHost } from '@plasmicapp/loader-nextjs'; -import { PLASMIC } from '@/plasmic-init'; - -export default function PlasmicHost() { - return PLASMIC && ; -} \ No newline at end of file diff --git a/examples/plasmic/src/pages/test.tsx b/examples/plasmic/src/pages/test.tsx deleted file mode 100644 index 5b66ecc..0000000 --- a/examples/plasmic/src/pages/test.tsx +++ /dev/null @@ -1,6 +0,0 @@ -import { useApi } from "bknd/client"; - -export default function Test() { - const api = useApi(undefined); - return
{api.baseUrl}
; -} diff --git a/examples/plasmic/src/plasmic-init.ts b/examples/plasmic/src/plasmic-init.ts deleted file mode 100644 index 9c77d9d..0000000 --- a/examples/plasmic/src/plasmic-init.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { initPlasmicLoader } from "@plasmicapp/loader-nextjs"; -import { loader } from "@bknd/plasmic"; - -export const PLASMIC = initPlasmicLoader({ - projects: [ - { - id: process.env.PLASMIC_ID!, - token: process.env.PLASMIC_TOKEN!, - } - ], - preview: true, //process.env.NODE_ENV === "development", -}) - -loader(PLASMIC); -/* -PLASMIC.registerComponent(BkndData, BkndDataMeta); -PLASMIC.registerGlobalContext(BkndContext, BkndContextMeta as any);*/ diff --git a/examples/plasmic/src/server.ts b/examples/plasmic/src/server.ts new file mode 100644 index 0000000..5ac41cb --- /dev/null +++ b/examples/plasmic/src/server.ts @@ -0,0 +1,43 @@ +import { App } from "bknd"; +import { serve } from "bknd/adapter/vite"; +import { boolean, em, entity, text } from "bknd/data"; +import { secureRandomString } from "bknd/utils"; + +export default serve({ + initialConfig: { + data: em({ + todos: entity("todos", { + title: text(), + done: boolean() + }) + }).toJSON(), + auth: { + enabled: true, + jwt: { + secret: secureRandomString(64) + } + } + }, + options: { + seed: async (ctx) => { + await ctx.em.mutator("todos" as any).insertMany([ + { title: "Learn bknd", done: true }, + { title: "Build something cool", done: false } + ]); + } + }, + // here we can hook into the app lifecycle events ... + beforeBuild: async (app) => { + app.emgr.onEvent( + App.Events.AppFirstBoot, + async () => { + // ... to create an initial user + await app.module.auth.createUser({ + email: "ds@bknd.io", + password: "12345678" + }); + }, + "sync" + ); + } +}); diff --git a/examples/plasmic/src/styles/globals.css b/examples/plasmic/src/styles/globals.css deleted file mode 100644 index b5c61c9..0000000 --- a/examples/plasmic/src/styles/globals.css +++ /dev/null @@ -1,3 +0,0 @@ -@tailwind base; -@tailwind components; -@tailwind utilities; diff --git a/examples/plasmic/tailwind.config.ts b/examples/plasmic/tailwind.config.ts deleted file mode 100644 index 109807b..0000000 --- a/examples/plasmic/tailwind.config.ts +++ /dev/null @@ -1,18 +0,0 @@ -import type { Config } from "tailwindcss"; - -export default { - content: [ - "./src/pages/**/*.{js,ts,jsx,tsx,mdx}", - "./src/components/**/*.{js,ts,jsx,tsx,mdx}", - "./src/app/**/*.{js,ts,jsx,tsx,mdx}", - ], - theme: { - extend: { - colors: { - background: "var(--background)", - foreground: "var(--foreground)", - }, - }, - }, - plugins: [], -} satisfies Config; diff --git a/examples/plasmic/tsconfig.json b/examples/plasmic/tsconfig.json index ecf7ecf..7c1c2d3 100644 --- a/examples/plasmic/tsconfig.json +++ b/examples/plasmic/tsconfig.json @@ -1,23 +1,21 @@ { "compilerOptions": { - "target": "ES2017", - "lib": ["dom", "dom.iterable", "esnext"], - "allowJs": true, + "target": "ES2022", + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "module": "ESNext", "skipLibCheck": true, - "strict": true, - "noEmit": true, - "esModuleInterop": true, - "module": "esnext", "moduleResolution": "bundler", - "resolveJsonModule": true, + "allowImportingTsExtensions": true, "isolatedModules": true, - "jsx": "preserve", - "incremental": true, + "moduleDetection": "force", + "noEmit": true, + "jsx": "react-jsx", + "strict": true, + "noUnusedLocals": true, "noImplicitAny": false, - "paths": { - "@/*": ["./src/*"] - } + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedSideEffectImports": true }, - "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"], - "exclude": ["node_modules"] + "include": ["src", "vite.config.ts"] } diff --git a/examples/plasmic/vite.config.ts b/examples/plasmic/vite.config.ts new file mode 100644 index 0000000..cc60590 --- /dev/null +++ b/examples/plasmic/vite.config.ts @@ -0,0 +1,15 @@ +import { devServer } from "bknd/adapter/vite"; +import react from "@vitejs/plugin-react"; +import { defineConfig } from "vite"; +import tsconfigPaths from "vite-tsconfig-paths"; + +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [ + react(), + tsconfigPaths(), + devServer({ + entry: "./src/server.ts" + }) as any + ] +}); \ No newline at end of file diff --git a/packages/plasmic/package.json b/packages/plasmic/package.json index 8d128c5..3a31a01 100644 --- a/packages/plasmic/package.json +++ b/packages/plasmic/package.json @@ -1,6 +1,6 @@ { "name": "@bknd/plasmic", - "version": "0.3.4-alpha1", + "version": "0.5.0", "type": "module", "sideEffects": false, "scripts": { @@ -28,24 +28,24 @@ "react": ">=18", "react-dom": ">=18", "@plasmicapp/host": ">=1.0.0", - "@plasmicapp/query": ">=0.1.0" + "@plasmicapp/loader-react": ">=1.0.0" }, "tsup": { "entry": [ "src/index.ts" ], - "minify": false, + "minify": true, "clean": true, "external": [ "react", "react-dom", "@plasmicapp/host", "@plasmicapp/query", + "@plasmicapp/loader-react", "swr" ], "format": [ - "esm", - "cjs" + "esm" ], "platform": "browser", "bundle": true, @@ -56,7 +56,7 @@ }, "types": "dist/index.d.ts", "module": "dist/index.js", - "main": "dist/index.cjs", + "main": "dist/index.js", "files": [ "dist", "README.md", diff --git a/packages/plasmic/src/components/Image.tsx b/packages/plasmic/src/components/Image.tsx index 9c50439..2ffb136 100644 --- a/packages/plasmic/src/components/Image.tsx +++ b/packages/plasmic/src/components/Image.tsx @@ -1,9 +1,7 @@ -import type { CodeComponentMeta } from "@plasmicapp/host"; -import registerComponent, { type ComponentMeta } from "@plasmicapp/host/registerComponent"; +import { type CodeComponentMeta, type ComponentMeta, registerComponent } from "@plasmicapp/host"; // biome-ignore lint/style/useImportType: import React from "react"; -//import { PlasmicCanvasContext } from "@plasmicapp/loader-react"; -import { useContext, useEffect, useRef, useState } from "react"; +import { useEffect, useRef, useState } from "react"; type PlasmicImageProps = { asset: { diff --git a/packages/plasmic/src/components/LazyRender.tsx b/packages/plasmic/src/components/LazyRender.tsx index a49e3a3..d825ee4 100644 --- a/packages/plasmic/src/components/LazyRender.tsx +++ b/packages/plasmic/src/components/LazyRender.tsx @@ -1,5 +1,4 @@ -import type { CodeComponentMeta } from "@plasmicapp/host"; -import registerComponent, { type ComponentMeta } from "@plasmicapp/host/registerComponent"; +import { type CodeComponentMeta, type ComponentMeta, registerComponent } from "@plasmicapp/host"; // biome-ignore lint/style/useImportType: import React from "react"; import { useEffect, useRef, useState } from "react"; diff --git a/packages/plasmic/src/components/data/BkndData.tsx b/packages/plasmic/src/components/data/BkndData.tsx index 6f2068c..7f43334 100644 --- a/packages/plasmic/src/components/data/BkndData.tsx +++ b/packages/plasmic/src/components/data/BkndData.tsx @@ -1,6 +1,10 @@ -import { DataProvider, usePlasmicCanvasContext } from "@plasmicapp/host"; -import registerComponent, { type ComponentMeta } from "@plasmicapp/host/registerComponent"; -import { usePlasmicQueryData } from "@plasmicapp/query"; +import { + type ComponentMeta, + DataProvider, + registerComponent, + usePlasmicCanvasContext +} from "@plasmicapp/host"; +import { usePlasmicQueryData } from "@plasmicapp/loader-react"; import { useApi, useEntityQuery } from "bknd/client"; import type { RepoQuery } from "bknd/data"; // biome-ignore lint/style/useImportType: @@ -109,7 +113,7 @@ export function BkndData({ limit: entityId ? undefined : limit, offset: entityId ? undefined : offset, where: _where, - sort: { by: sortBy, dir: sortDir }, + sort: `${sortDir === "desc" ? "-" : ""}${sortBy}`, with: withRefs, join: joinRefs }; @@ -124,7 +128,7 @@ export function BkndData({ let references: string[] = []; if (entity && entity in entities) { - fields = Object.keys(entities[entity].fields!); + fields = Object.keys(entities[entity]?.fields ?? {}); if (relations) { const rels = Object.values(relations).filter( @@ -198,11 +202,11 @@ const ModeFetch = ({ return ; } - if (hasError) { + if (hasError || !data) { return ; } - if (data.length === 0) { + if (data?.length === 0) { return ; } @@ -248,6 +252,7 @@ export function registerBkndData( export const BkndDataMeta: ComponentMeta = { name: "BKND Data", + importName: "BkndData", section: "BKND", importPath: "@bknd/plasmic", providesData: true, @@ -264,6 +269,7 @@ export const BkndDataMeta: ComponentMeta = { }, select: { type: "choice", + multiSelect: true, options: (props, ctx) => ctx?.fields ?? [] }, limit: { diff --git a/packages/plasmic/src/contexts/BkndContext.tsx b/packages/plasmic/src/contexts/BkndContext.tsx index 7d32f6c..c87332b 100644 --- a/packages/plasmic/src/contexts/BkndContext.tsx +++ b/packages/plasmic/src/contexts/BkndContext.tsx @@ -1,7 +1,10 @@ -import { DataProvider, GlobalActionsProvider, usePlasmicCanvasContext } from "@plasmicapp/host"; -import registerGlobalContext, { - type GlobalContextMeta -} from "@plasmicapp/host/registerGlobalContext"; +import { + DataProvider, + GlobalActionsProvider, + type GlobalContextMeta, + registerGlobalContext, + usePlasmicCanvasContext +} from "@plasmicapp/host"; import type { AppConfig } from "bknd"; // @ts-ignore import { ClientProvider, useApi, useAuth, useBaseUrl } from "bknd/client"; @@ -24,7 +27,6 @@ type BkndContextProps = { const BkndContextContext = createContext({} as any); -// @todo: it's an issue that we need auth, so we cannot make baseurl adjustable (maybe add an option to useAuth with a specific base url?) export const BkndContext = ({ children, baseUrl, @@ -32,7 +34,7 @@ export const BkndContext = ({ }: React.PropsWithChildren) => { const auth = useAuth(); const baseurl = baseUrl ?? useBaseUrl(); - const api = useApi({ host: baseurl }); + const api = useApi(baseurl); const [data, setData] = useState({ baseUrl: baseurl, @@ -49,10 +51,6 @@ export const BkndContext = ({ (async () => { if (inEditor) { const result = await api.system.readConfig(); - - /*const res = await fetch(`${baseurl}/api/system/config`); - const result = (await res.json()) as BkndGlobalContextProps["appConfig"];*/ - console.log("appconfig", result); setData((prev) => ({ ...prev, appConfig: result })); } })(); @@ -60,41 +58,15 @@ export const BkndContext = ({ const actions = useMemo( () => ({ - login: async (data: any) => { - console.log("login", data); - const result = await auth.login(data); - console.log("login:result", result); - if (result.res.ok && "user" in result.data) { - //result.data. - return result.data; - } else { - console.log("login failed", result); - } - - return false; - }, - register: async (data: any) => { - console.log("register", data); - const result = await auth.register(data); - console.log("register:result", result); - if (result.res.ok && "user" in result.data) { - //result.data. - return result.data; - } - - return false; - }, - logout: async () => { - await auth.logout(); - console.log("logged out"); - return true; - }, + login: auth.login, + register: auth.register, + logout: auth.logout, setToken: auth.setToken }), [baseUrl] ); - console.log("plasmic.bknd.context", { baseUrl }); + console.log("plasmic.bknd.context", { baseurl }); return ( From 6625c9bc48e3cdbb39f87e53c1865e35f19381df Mon Sep 17 00:00:00 2001 From: dswbx Date: Wed, 15 Jan 2025 17:21:28 +0100 Subject: [PATCH 05/32] Refactor event system to support returnable events Added support for validating and managing return values in events. Implemented `validate` and `clone` methods in the event base class for event mutation and return handling. Additionally, enhanced error handling, introduced "once" listeners, and improved async execution management in the `EventManager`. --- app/__test__/core/EventManager.spec.ts | 135 +++++++++++++++++++--- app/src/core/events/Event.ts | 24 +++- app/src/core/events/EventListener.ts | 7 +- app/src/core/events/EventManager.ts | 92 ++++++++++++--- app/src/core/events/index.ts | 4 +- tmp/event_manager_returning_test.patch | 150 ------------------------- 6 files changed, 227 insertions(+), 185 deletions(-) delete mode 100644 tmp/event_manager_returning_test.patch diff --git a/app/__test__/core/EventManager.spec.ts b/app/__test__/core/EventManager.spec.ts index 3327449..4f11d19 100644 --- a/app/__test__/core/EventManager.spec.ts +++ b/app/__test__/core/EventManager.spec.ts @@ -1,8 +1,18 @@ -import { describe, expect, test } from "bun:test"; -import { Event, EventManager, NoParamEvent } from "../../src/core/events"; +import { afterAll, beforeAll, describe, expect, mock, test } from "bun:test"; +import { + Event, + EventManager, + InvalidEventReturn, + type ListenerHandler, + NoParamEvent +} from "../../src/core/events"; +import { disableConsoleLog, enableConsoleLog } from "../helper"; + +beforeAll(disableConsoleLog); +afterAll(enableConsoleLog); class SpecialEvent extends Event<{ foo: string }> { - static slug = "special-event"; + static override slug = "special-event"; isBar() { return this.params.foo === "bar"; @@ -10,37 +20,136 @@ class SpecialEvent extends Event<{ foo: string }> { } class InformationalEvent extends NoParamEvent { - static slug = "informational-event"; + static override slug = "informational-event"; +} + +class ReturnEvent extends Event<{ foo: string }, string> { + static override slug = "return-event"; + + override validate(value: string) { + if (typeof value !== "string") { + throw new InvalidEventReturn("string", typeof value); + } + + return this.clone({ + foo: [this.params.foo, value].join("-") + }); + } } describe("EventManager", async () => { - test("test", async () => { + test("executes", async () => { + const call = mock(() => null); + const delayed = mock(() => null); + const emgr = new EventManager(); emgr.registerEvents([SpecialEvent, InformationalEvent]); + expect(emgr.eventExists("special-event")).toBe(true); + expect(emgr.eventExists("informational-event")).toBe(true); + expect(emgr.eventExists("unknown-event")).toBe(false); + emgr.onEvent( SpecialEvent, async (event, name) => { - console.log("Event: ", name, event.params.foo, event.isBar()); - console.log("wait..."); - - await new Promise((resolve) => setTimeout(resolve, 100)); - console.log("done waiting"); + expect(name).toBe("special-event"); + expect(event.isBar()).toBe(true); + call(); + await new Promise((resolve) => setTimeout(resolve, 50)); + delayed(); }, "sync" ); emgr.onEvent(InformationalEvent, async (event, name) => { - console.log("Event: ", name, event.params); + call(); + expect(name).toBe("informational-event"); }); await emgr.emit(new SpecialEvent({ foo: "bar" })); - console.log("done"); + await emgr.emit(new InformationalEvent()); // expect construct signatures to not cause ts errors new SpecialEvent({ foo: "bar" }); new InformationalEvent(); - expect(true).toBe(true); + expect(call).toHaveBeenCalledTimes(2); + expect(delayed).toHaveBeenCalled(); + }); + + test("custom async executor", async () => { + const call = mock(() => null); + const asyncExecutor = (p: Promise[]) => { + call(); + return Promise.all(p); + }; + const emgr = new EventManager( + { InformationalEvent }, + { + asyncExecutor + } + ); + + emgr.onEvent(InformationalEvent, async () => {}); + await emgr.emit(new InformationalEvent()); + expect(call).toHaveBeenCalled(); + }); + + test("piping", async () => { + const onInvalidReturn = mock(() => null); + const asyncEventCallback = mock(() => null); + const emgr = new EventManager( + { ReturnEvent, InformationalEvent }, + { + onInvalidReturn + } + ); + + // @ts-expect-error InformationalEvent has no return value + emgr.onEvent(InformationalEvent, async () => { + asyncEventCallback(); + return 1; + }); + + emgr.onEvent(ReturnEvent, async () => "1", "sync"); + emgr.onEvent(ReturnEvent, async () => "0", "sync"); + + // @ts-expect-error must be string + emgr.onEvent(ReturnEvent, async () => 0, "sync"); + + // return is not required + emgr.onEvent(ReturnEvent, async () => {}, "sync"); + + // was "async", will not return + const e1 = await emgr.emit(new InformationalEvent()); + expect(e1.returned).toBe(false); + + const e2 = await emgr.emit(new ReturnEvent({ foo: "bar" })); + expect(e2.returned).toBe(true); + expect(e2.params.foo).toBe("bar-1-0"); + expect(onInvalidReturn).toHaveBeenCalled(); + expect(asyncEventCallback).toHaveBeenCalled(); + }); + + test("once", async () => { + const call = mock(() => null); + const emgr = new EventManager({ InformationalEvent }); + + emgr.onEventOnce( + InformationalEvent, + async (event, slug) => { + expect(event).toBeInstanceOf(InformationalEvent); + expect(slug).toBe("informational-event"); + call(); + }, + "sync" + ); + + expect(emgr.getListeners().length).toBe(1); + await emgr.emit(new InformationalEvent()); + expect(emgr.getListeners().length).toBe(0); + await emgr.emit(new InformationalEvent()); + expect(emgr.getListeners().length).toBe(0); + expect(call).toHaveBeenCalledTimes(1); }); }); diff --git a/app/src/core/events/Event.ts b/app/src/core/events/Event.ts index 247c7a5..734c7c2 100644 --- a/app/src/core/events/Event.ts +++ b/app/src/core/events/Event.ts @@ -1,17 +1,31 @@ -export abstract class Event { +export abstract class Event { + _returning!: Returning; + /** * Unique event slug * Must be static, because registering events is done by class */ static slug: string = "untitled-event"; params: Params; + returned: boolean = false; + + validate(value: Returning): Event | void {} + + protected clone = Event>( + this: This, + params: Params + ): This { + const cloned = new (this.constructor as any)(params); + cloned.returned = true; + return cloned as This; + } constructor(params: Params) { this.params = params; } } -// @todo: current workaround: potentially there is none and that's the way +// @todo: current workaround: potentially there is "none" and that's the way export class NoParamEvent extends Event { static override slug: string = "noparam-event"; @@ -19,3 +33,9 @@ export class NoParamEvent extends Event { super(null); } } + +export class InvalidEventReturn extends Error { + constructor(expected: string, given: string) { + super(`Expected "${expected}", got "${given}"`); + } +} diff --git a/app/src/core/events/EventListener.ts b/app/src/core/events/EventListener.ts index 951fce8..fc677ed 100644 --- a/app/src/core/events/EventListener.ts +++ b/app/src/core/events/EventListener.ts @@ -4,15 +4,16 @@ import type { EventClass } from "./EventManager"; export const ListenerModes = ["sync", "async"] as const; export type ListenerMode = (typeof ListenerModes)[number]; -export type ListenerHandler = ( +export type ListenerHandler> = ( event: E, - slug: string, -) => Promise | void; + slug: string +) => E extends Event ? R | Promise : never; export class EventListener { mode: ListenerMode = "async"; event: EventClass; handler: ListenerHandler; + once: boolean = false; constructor(event: EventClass, handler: ListenerHandler, mode: ListenerMode = "async") { this.event = event; diff --git a/app/src/core/events/EventManager.ts b/app/src/core/events/EventManager.ts index 9233666..6e48224 100644 --- a/app/src/core/events/EventManager.ts +++ b/app/src/core/events/EventManager.ts @@ -1,4 +1,4 @@ -import type { Event } from "./Event"; +import { type Event, InvalidEventReturn } from "./Event"; import { EventListener, type ListenerHandler, type ListenerMode } from "./EventListener"; export interface EmitsEvents { @@ -6,7 +6,7 @@ export interface EmitsEvents { } export type EventClass = { - new (params: any): Event; + new (params: any): Event; slug: string; }; @@ -17,16 +17,20 @@ export class EventManager< protected listeners: EventListener[] = []; enabled: boolean = true; - constructor(events?: RegisteredEvents, listeners?: EventListener[]) { + constructor( + events?: RegisteredEvents, + private options?: { + listeners?: EventListener[]; + onError?: (event: Event, e: unknown) => void; + onInvalidReturn?: (event: Event, e: InvalidEventReturn) => void; + asyncExecutor?: typeof Promise.all; + } + ) { if (events) { this.registerEvents(events); } - if (listeners) { - for (const listener of listeners) { - this.addListener(listener); - } - } + options?.listeners?.forEach((l) => this.addListener(l)); } enable() { @@ -128,6 +132,18 @@ export class EventManager< this.addListener(listener as any); } + onEventOnce>( + event: ActualEvent, + handler: ListenerHandler, + mode: ListenerMode = "async" + ) { + this.throwIfEventNotRegistered(event); + + const listener = new EventListener(event, handler, mode); + listener.once = true; + this.addListener(listener as any); + } + on( slug: string, handler: ListenerHandler>, @@ -145,27 +161,73 @@ export class EventManager< this.events.forEach((event) => this.onEvent(event, handler, mode)); } - async emit(event: Event) { + protected executeAsyncs(promises: (() => Promise)[]) { + const executor = this.options?.asyncExecutor ?? ((e) => Promise.all(e)); + executor(promises.map((p) => p())).then(() => void 0); + } + + async emit>(event: Actual): Promise { // @ts-expect-error slug is static const slug = event.constructor.slug; if (!this.enabled) { console.log("EventManager disabled, not emitting", slug); - return; + return event; } if (!this.eventExists(event)) { throw new Error(`Event "${slug}" not registered`); } - const listeners = this.listeners.filter((listener) => listener.event.slug === slug); - //console.log("---!-- emitting", slug, listeners.length); + const syncs: EventListener[] = []; + const asyncs: (() => Promise)[] = []; + + this.listeners = this.listeners.filter((listener) => { + // if no match, keep and ignore + if (listener.event.slug !== slug) return true; - for (const listener of listeners) { if (listener.mode === "sync") { - await listener.handler(event, listener.event.slug); + syncs.push(listener); } else { - listener.handler(event, listener.event.slug); + asyncs.push(async () => await listener.handler(event, listener.event.slug)); + } + // Remove if `once` is true, otherwise keep + return !listener.once; + }); + + // execute asyncs + this.executeAsyncs(asyncs); + + // execute syncs + let _event: Actual = event; + for (const listener of syncs) { + try { + const return_value = (await listener.handler(_event, listener.event.slug)) as any; + + if (typeof return_value !== "undefined") { + const newEvent = _event.validate(return_value); + // @ts-expect-error slug is static + if (newEvent && newEvent.constructor.slug === slug) { + if (!newEvent.returned) { + throw new Error( + // @ts-expect-error slug is static + `Returned event ${newEvent.constructor.slug} must be marked as returned.` + ); + } + _event = newEvent as Actual; + } + } + } catch (e) { + if (e instanceof InvalidEventReturn) { + this.options?.onInvalidReturn?.(_event, e); + console.warn(`Invalid return of event listener for "${slug}": ${e.message}`); + } else if (this.options?.onError) { + this.options.onError(_event, e); + } else { + throw e; + } } } + + return _event; } } diff --git a/app/src/core/events/index.ts b/app/src/core/events/index.ts index b823edf..1edb065 100644 --- a/app/src/core/events/index.ts +++ b/app/src/core/events/index.ts @@ -1,8 +1,8 @@ -export { Event, NoParamEvent } from "./Event"; +export { Event, NoParamEvent, InvalidEventReturn } from "./Event"; export { EventListener, ListenerModes, type ListenerMode, - type ListenerHandler, + type ListenerHandler } from "./EventListener"; export { EventManager, type EmitsEvents, type EventClass } from "./EventManager"; diff --git a/tmp/event_manager_returning_test.patch b/tmp/event_manager_returning_test.patch deleted file mode 100644 index 1e194ef..0000000 --- a/tmp/event_manager_returning_test.patch +++ /dev/null @@ -1,150 +0,0 @@ -Subject: [PATCH] event manager returning test ---- -Index: app/__test__/core/EventManager.spec.ts -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/app/__test__/core/EventManager.spec.ts b/app/__test__/core/EventManager.spec.ts ---- a/app/__test__/core/EventManager.spec.ts (revision f06777256f332766de4bc76c23183725c8c7d310) -+++ b/app/__test__/core/EventManager.spec.ts (date 1731498680965) -@@ -1,8 +1,8 @@ - import { describe, expect, test } from "bun:test"; --import { Event, EventManager, NoParamEvent } from "../../src/core/events"; -+import { Event, EventManager, type ListenerHandler, NoParamEvent } from "../../src/core/events"; - - class SpecialEvent extends Event<{ foo: string }> { -- static slug = "special-event"; -+ static override slug = "special-event"; - - isBar() { - return this.params.foo === "bar"; -@@ -10,7 +10,19 @@ - } - - class InformationalEvent extends NoParamEvent { -- static slug = "informational-event"; -+ static override slug = "informational-event"; -+} -+ -+class ReturnEvent extends Event<{ foo: string }, number> { -+ static override slug = "return-event"; -+ static override returning = true; -+ -+ override setValidatedReturn(value: number) { -+ if (typeof value !== "number") { -+ throw new Error("Invalid return value"); -+ } -+ this.params.foo = value.toString(); -+ } - } - - describe("EventManager", async () => { -@@ -43,4 +55,22 @@ - - expect(true).toBe(true); - }); -+ -+ test.only("piping", async () => { -+ const emgr = new EventManager(); -+ emgr.registerEvents([ReturnEvent, InformationalEvent]); -+ -+ type T = ListenerHandler; -+ -+ // @ts-expect-error InformationalEvent has no return value -+ emgr.onEvent(InformationalEvent, async (event, name) => { -+ console.log("Event: ", name, event.params); -+ return 1; -+ }); -+ -+ emgr.onEvent(ReturnEvent, async (event, name) => { -+ console.log("Event: ", name, event.params); -+ return 1; -+ }); -+ }); - }); -Index: app/src/core/events/EventManager.ts -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/app/src/core/events/EventManager.ts b/app/src/core/events/EventManager.ts ---- a/app/src/core/events/EventManager.ts (revision f06777256f332766de4bc76c23183725c8c7d310) -+++ b/app/src/core/events/EventManager.ts (date 1731498680971) -@@ -6,7 +6,7 @@ - } - - export type EventClass = { -- new (params: any): Event; -+ new (params: any): Event; - slug: string; - }; - -@@ -137,6 +137,9 @@ - throw new Error(`Event "${slug}" not registered`); - } - -+ // @ts-expect-error returning is static -+ const returning = Boolean(event.constructor.returning); -+ - const listeners = this.listeners.filter((listener) => listener.event.slug === slug); - //console.log("---!-- emitting", slug, listeners.length); - -Index: app/src/core/events/EventListener.ts -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/app/src/core/events/EventListener.ts b/app/src/core/events/EventListener.ts ---- a/app/src/core/events/EventListener.ts (revision f06777256f332766de4bc76c23183725c8c7d310) -+++ b/app/src/core/events/EventListener.ts (date 1731498680968) -@@ -4,10 +4,10 @@ - export const ListenerModes = ["sync", "async"] as const; - export type ListenerMode = (typeof ListenerModes)[number]; - --export type ListenerHandler = ( -+export type ListenerHandler> = ( - event: E, -- slug: string, --) => Promise | void; -+ slug: string -+) => E extends Event ? R | Promise : never; - - export class EventListener { - mode: ListenerMode = "async"; -Index: app/src/core/events/Event.ts -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/app/src/core/events/Event.ts b/app/src/core/events/Event.ts ---- a/app/src/core/events/Event.ts (revision f06777256f332766de4bc76c23183725c8c7d310) -+++ b/app/src/core/events/Event.ts (date 1731498680973) -@@ -1,17 +1,25 @@ --export abstract class Event { -+export abstract class Event { - /** - * Unique event slug - * Must be static, because registering events is done by class - */ - static slug: string = "untitled-event"; - params: Params; -+ _returning!: Returning; -+ static returning: boolean = false; -+ -+ setValidatedReturn(value: Returning): void { -+ if (typeof value !== "undefined") { -+ throw new Error("Invalid event return value"); -+ } -+ } - - constructor(params: Params) { - this.params = params; - } - } - --// @todo: current workaround: potentially there is none and that's the way -+// @todo: current workaround: potentially there is "none" and that's the way - export class NoParamEvent extends Event { - static override slug: string = "noparam-event"; - From 438e36f185ff0199d8a030600527b38649478aa2 Mon Sep 17 00:00:00 2001 From: dswbx Date: Wed, 15 Jan 2025 17:46:41 +0100 Subject: [PATCH 06/32] refactor event listener registration unified listener creation logic under `createEventListener`, adding support for a flexible `RegisterListenerConfig`. Updated associated tests and improved error handling for unregistered events. --- app/__test__/core/EventManager.spec.ts | 7 +++- app/src/core/events/EventManager.ts | 56 ++++++++++++++------------ 2 files changed, 35 insertions(+), 28 deletions(-) diff --git a/app/__test__/core/EventManager.spec.ts b/app/__test__/core/EventManager.spec.ts index 4f11d19..ba5db93 100644 --- a/app/__test__/core/EventManager.spec.ts +++ b/app/__test__/core/EventManager.spec.ts @@ -61,6 +61,9 @@ describe("EventManager", async () => { "sync" ); + // don't allow unknown + expect(() => emgr.on("unknown", () => void 0)).toThrow(); + emgr.onEvent(InformationalEvent, async (event, name) => { call(); expect(name).toBe("informational-event"); @@ -135,14 +138,14 @@ describe("EventManager", async () => { const call = mock(() => null); const emgr = new EventManager({ InformationalEvent }); - emgr.onEventOnce( + emgr.onEvent( InformationalEvent, async (event, slug) => { expect(event).toBeInstanceOf(InformationalEvent); expect(slug).toBe("informational-event"); call(); }, - "sync" + { mode: "sync", once: true } ); expect(emgr.getListeners().length).toBe(1); diff --git a/app/src/core/events/EventManager.ts b/app/src/core/events/EventManager.ts index 6e48224..ec271a4 100644 --- a/app/src/core/events/EventManager.ts +++ b/app/src/core/events/EventManager.ts @@ -1,6 +1,13 @@ import { type Event, InvalidEventReturn } from "./Event"; import { EventListener, type ListenerHandler, type ListenerMode } from "./EventListener"; +export type RegisterListenerConfig = + | ListenerMode + | { + mode?: ListenerMode; + once?: boolean; + }; + export interface EmitsEvents { emgr: EventManager; } @@ -86,9 +93,11 @@ export class EventManager< return !!this.events.find((e) => slug === e.slug); } - protected throwIfEventNotRegistered(event: EventClass) { - if (!this.eventExists(event)) { - throw new Error(`Event "${event.slug}" not registered`); + protected throwIfEventNotRegistered(event: EventClass | Event | string) { + if (!this.eventExists(event as any)) { + // @ts-expect-error + const name = event.constructor?.slug ?? event.slug ?? event; + throw new Error(`Event "${name}" not registered`); } } @@ -121,44 +130,39 @@ export class EventManager< return this; } - onEvent>( - event: ActualEvent, - handler: ListenerHandler, - mode: ListenerMode = "async" + protected createEventListener( + _event: EventClass | string, + handler: ListenerHandler, + _config: RegisterListenerConfig = "async" ) { - this.throwIfEventNotRegistered(event); - - const listener = new EventListener(event, handler, mode); + const event = + typeof _event === "string" ? this.events.find((e) => e.slug === _event)! : _event; + const config = typeof _config === "string" ? { mode: _config } : _config; + const listener = new EventListener(event, handler, config.mode); + if (config.once) { + listener.once = true; + } this.addListener(listener as any); } - onEventOnce>( + onEvent>( event: ActualEvent, handler: ListenerHandler, - mode: ListenerMode = "async" + config?: RegisterListenerConfig ) { - this.throwIfEventNotRegistered(event); - - const listener = new EventListener(event, handler, mode); - listener.once = true; - this.addListener(listener as any); + this.createEventListener(event, handler, config); } on( slug: string, handler: ListenerHandler>, - mode: ListenerMode = "async" + config?: RegisterListenerConfig ) { - const event = this.events.find((e) => e.slug === slug); - if (!event) { - throw new Error(`Event "${slug}" not registered`); - } - - this.onEvent(event, handler, mode); + this.createEventListener(slug, handler, config); } - onAny(handler: ListenerHandler>, mode: ListenerMode = "async") { - this.events.forEach((event) => this.onEvent(event, handler, mode)); + onAny(handler: ListenerHandler>, config?: RegisterListenerConfig) { + this.events.forEach((event) => this.onEvent(event, handler, config)); } protected executeAsyncs(promises: (() => Promise)[]) { From 6c9707d12c4ac88a4e4a5f2b97b45f87ba9f68ed Mon Sep 17 00:00:00 2001 From: dswbx Date: Thu, 16 Jan 2025 10:10:47 +0100 Subject: [PATCH 07/32] refactored mutator to listen for returned data from event listeners --- app/src/core/config.ts | 9 +++++-- app/src/core/events/Event.ts | 19 ++++++++++++- app/src/core/events/EventManager.ts | 8 +++--- app/src/data/entities/Entity.ts | 20 ++++++++++++-- app/src/data/entities/Mutator.ts | 20 ++++++++------ app/src/data/events/index.ts | 42 ++++++++++++++++++++++++----- app/src/ui/client/api/use-entity.ts | 6 ++--- 7 files changed, 96 insertions(+), 28 deletions(-) diff --git a/app/src/core/config.ts b/app/src/core/config.ts index a99d549..2f2cf06 100644 --- a/app/src/core/config.ts +++ b/app/src/core/config.ts @@ -5,8 +5,13 @@ import type { Generated } from "kysely"; export type PrimaryFieldType = number | Generated; -// biome-ignore lint/suspicious/noEmptyInterface: -export interface DB {} +export interface DB { + // make sure to make unknown as "any" + [key: string]: { + id: PrimaryFieldType; + [key: string]: any; + }; +} export const config = { server: { diff --git a/app/src/core/events/Event.ts b/app/src/core/events/Event.ts index 734c7c2..8073c12 100644 --- a/app/src/core/events/Event.ts +++ b/app/src/core/events/Event.ts @@ -1,3 +1,8 @@ +export type EventClass = { + new (params: any): Event; + slug: string; +}; + export abstract class Event { _returning!: Returning; @@ -9,7 +14,9 @@ export abstract class Event { params: Params; returned: boolean = false; - validate(value: Returning): Event | void {} + validate(value: Returning): Event | void { + throw new EventReturnedWithoutValidation(this as any, value); + } protected clone = Event>( this: This, @@ -39,3 +46,13 @@ export class InvalidEventReturn extends Error { super(`Expected "${expected}", got "${given}"`); } } + +export class EventReturnedWithoutValidation extends Error { + constructor( + event: EventClass, + public data: any + ) { + // @ts-expect-error slug is static + super(`Event "${event.constructor.slug}" returned without validation`); + } +} diff --git a/app/src/core/events/EventManager.ts b/app/src/core/events/EventManager.ts index ec271a4..73764ea 100644 --- a/app/src/core/events/EventManager.ts +++ b/app/src/core/events/EventManager.ts @@ -1,4 +1,4 @@ -import { type Event, InvalidEventReturn } from "./Event"; +import { type Event, type EventClass, InvalidEventReturn } from "./Event"; import { EventListener, type ListenerHandler, type ListenerMode } from "./EventListener"; export type RegisterListenerConfig = @@ -12,10 +12,8 @@ export interface EmitsEvents { emgr: EventManager; } -export type EventClass = { - new (params: any): Event; - slug: string; -}; +// for compatibility, moved it to Event.ts +export type { EventClass }; export class EventManager< RegisteredEvents extends Record = Record diff --git a/app/src/data/entities/Entity.ts b/app/src/data/entities/Entity.ts index a87d609..4665322 100644 --- a/app/src/data/entities/Entity.ts +++ b/app/src/data/entities/Entity.ts @@ -192,10 +192,26 @@ export class Entity< this.data = data; } + // @todo: add tests isValidData(data: EntityData, context: TActionContext, explain?: boolean): boolean { + if (typeof data !== "object") { + if (explain) { + throw new Error(`Entity "${this.name}" data must be an object`); + } + } + const fields = this.getFillableFields(context, false); - //const fields = this.fields; - //console.log("data", data); + const field_names = fields.map((f) => f.name); + const given_keys = Object.keys(data); + + if (given_keys.some((key) => !field_names.includes(key))) { + if (explain) { + throw new Error( + `Entity "${this.name}" data must only contain known keys, got: "${given_keys}"` + ); + } + } + for (const field of fields) { if (!field.isValid(data[field.name], context)) { console.log("Entity.isValidData:invalid", context, field.name, data[field.name]); diff --git a/app/src/data/entities/Mutator.ts b/app/src/data/entities/Mutator.ts index d9bff38..15760bc 100644 --- a/app/src/data/entities/Mutator.ts +++ b/app/src/data/entities/Mutator.ts @@ -132,14 +132,17 @@ export class Mutator< throw new Error(`Creation of system entity "${entity.name}" is disabled`); } - // @todo: establish the original order from "data" + const result = await this.emgr.emit( + new Mutator.Events.MutatorInsertBefore({ entity, data: data as any }) + ); + + // if listener returned, take what's returned + const _data = result.returned ? result.params.data : data; const validatedData = { ...entity.getDefaultObject(), - ...(await this.getValidatedData(data, "create")) + ...(await this.getValidatedData(_data, "create")) }; - await this.emgr.emit(new Mutator.Events.MutatorInsertBefore({ entity, data: validatedData })); - // check if required fields are present const required = entity.getRequiredFields(); for (const field of required) { @@ -169,16 +172,17 @@ export class Mutator< throw new Error("ID must be provided for update"); } - const validatedData = await this.getValidatedData(data, "update"); - - await this.emgr.emit( + const result = await this.emgr.emit( new Mutator.Events.MutatorUpdateBefore({ entity, entityId: id, - data: validatedData as any + data }) ); + const _data = result.returned ? result.params.data : data; + const validatedData = await this.getValidatedData(_data, "update"); + const query = this.conn .updateTable(entity.name) .set(validatedData as any) diff --git a/app/src/data/events/index.ts b/app/src/data/events/index.ts index 01311d8..3ea038c 100644 --- a/app/src/data/events/index.ts +++ b/app/src/data/events/index.ts @@ -1,20 +1,48 @@ import type { PrimaryFieldType } from "core"; -import { Event } from "core/events"; +import { Event, InvalidEventReturn } from "core/events"; import type { Entity, EntityData } from "../entities"; import type { RepoQuery } from "../server/data-query-impl"; -export class MutatorInsertBefore extends Event<{ entity: Entity; data: EntityData }> { +export class MutatorInsertBefore extends Event<{ entity: Entity; data: EntityData }, EntityData> { static override slug = "mutator-insert-before"; + + override validate(data: EntityData) { + const { entity } = this.params; + if (!entity.isValidData(data, "create")) { + throw new InvalidEventReturn("EntityData", "invalid"); + } + + return this.clone({ + entity, + data + }); + } } export class MutatorInsertAfter extends Event<{ entity: Entity; data: EntityData }> { static override slug = "mutator-insert-after"; } -export class MutatorUpdateBefore extends Event<{ - entity: Entity; - entityId: PrimaryFieldType; - data: EntityData; -}> { +export class MutatorUpdateBefore extends Event< + { + entity: Entity; + entityId: PrimaryFieldType; + data: EntityData; + }, + EntityData +> { static override slug = "mutator-update-before"; + + override validate(data: EntityData) { + const { entity, ...rest } = this.params; + if (!entity.isValidData(data, "update")) { + throw new InvalidEventReturn("EntityData", "invalid"); + } + + return this.clone({ + ...rest, + entity, + data + }); + } } export class MutatorUpdateAfter extends Event<{ entity: Entity; diff --git a/app/src/ui/client/api/use-entity.ts b/app/src/ui/client/api/use-entity.ts index 5f4da99..fba6a45 100644 --- a/app/src/ui/client/api/use-entity.ts +++ b/app/src/ui/client/api/use-entity.ts @@ -109,7 +109,7 @@ export const useEntityQuery = < options?: SWRConfiguration & { enabled?: boolean; revalidateOnMutate?: boolean } ) => { const api = useApi().data; - const key = makeKey(api, entity, id, query); + const key = makeKey(api, entity as string, id, query); const { read, ...actions } = useEntity(entity, id); const fetcher = () => read(query); @@ -121,7 +121,7 @@ export const useEntityQuery = < }); const mutateAll = async () => { - const entityKey = makeKey(api, entity); + const entityKey = makeKey(api, entity as string); return mutate((key) => typeof key === "string" && key.startsWith(entityKey), undefined, { revalidate: true }); @@ -167,7 +167,7 @@ export async function mutateEntityCache< return prev; } - const entityKey = makeKey(api, entity); + const entityKey = makeKey(api, entity as string); return mutate( (key) => typeof key === "string" && key.startsWith(entityKey), From aa4aca1a9044c3c3e078218174b7ca9887330f0a Mon Sep 17 00:00:00 2001 From: dswbx Date: Thu, 16 Jan 2025 10:11:10 +0100 Subject: [PATCH 08/32] added event related tests to mutator, fixed tests --- app/__test__/core/EventManager.spec.ts | 8 +- app/__test__/data/specs/Mutator.spec.ts | 74 ++++++++++++++++++- app/__test__/data/specs/Repository.spec.ts | 5 +- app/__test__/data/specs/WithBuilder.spec.ts | 4 +- .../data/specs/fields/EnumField.spec.ts | 4 - app/__test__/data/specs/fields/Field.spec.ts | 4 +- .../data/specs/fields/JsonField.spec.ts | 2 +- .../specs/relations/EntityRelation.spec.ts | 4 +- .../adapters/StorageCloudinaryAdapter.spec.ts | 2 +- .../adapters/StorageLocalAdapter.spec.ts | 2 +- .../media/adapters/StorageS3Adapter.spec.ts | 6 +- app/bunfig.toml | 5 +- app/package.json | 1 + 13 files changed, 89 insertions(+), 32 deletions(-) diff --git a/app/__test__/core/EventManager.spec.ts b/app/__test__/core/EventManager.spec.ts index ba5db93..e332439 100644 --- a/app/__test__/core/EventManager.spec.ts +++ b/app/__test__/core/EventManager.spec.ts @@ -1,11 +1,5 @@ import { afterAll, beforeAll, describe, expect, mock, test } from "bun:test"; -import { - Event, - EventManager, - InvalidEventReturn, - type ListenerHandler, - NoParamEvent -} from "../../src/core/events"; +import { Event, EventManager, InvalidEventReturn, NoParamEvent } from "../../src/core/events"; import { disableConsoleLog, enableConsoleLog } from "../helper"; beforeAll(disableConsoleLog); diff --git a/app/__test__/data/specs/Mutator.spec.ts b/app/__test__/data/specs/Mutator.spec.ts index 5552543..47134e8 100644 --- a/app/__test__/data/specs/Mutator.spec.ts +++ b/app/__test__/data/specs/Mutator.spec.ts @@ -10,6 +10,7 @@ import { RelationMutator, TextField } from "../../../src/data"; +import * as proto from "../../../src/data/prototype"; import { getDummyConnection } from "../helper"; const { dummyConnection, afterAllCleanup } = getDummyConnection(); @@ -83,14 +84,12 @@ describe("[data] Mutator (ManyToOne)", async () => { // persisting reference should ... expect( - postRelMutator.persistReference(relations[0], "users", { + postRelMutator.persistReference(relations[0]!, "users", { $set: { id: userData.data.id } }) ).resolves.toEqual(["users_id", userData.data.id]); // @todo: add what methods are allowed to relation, like $create should not be allowed for post<>users - process.exit(0); - const userRelMutator = new RelationMutator(users, em); expect(userRelMutator.getRelationalKeys()).toEqual(["posts"]); }); @@ -99,7 +98,7 @@ describe("[data] Mutator (ManyToOne)", async () => { expect( em.mutator(posts).insertOne({ title: "post1", - users_id: 1 // user does not exist yet + users_id: 100 // user does not exist yet }) ).rejects.toThrow(); }); @@ -299,4 +298,71 @@ describe("[data] Mutator (Events)", async () => { expect(events.has(MutatorEvents.MutatorDeleteBefore.slug)).toBeTrue(); expect(events.has(MutatorEvents.MutatorDeleteAfter.slug)).toBeTrue(); }); + + /*test("insertOne event return is respected", async () => { + const posts = proto.entity("posts", { + title: proto.text(), + views: proto.number() + }); + + const conn = getDummyConnection(); + const em = new EntityManager([posts], conn.dummyConnection); + await em.schema().sync({ force: true }); + + const emgr = em.emgr as EventManager; + + emgr.onEvent( + // @ts-ignore + EntityManager.Events.MutatorInsertBefore, + async (event) => { + return { + ...event.params.data, + views: 2 + }; + }, + "sync" + ); + + const mutator = em.mutator("posts"); + const result = await mutator.insertOne({ title: "test", views: 1 }); + expect(result.data).toEqual({ + id: 1, + title: "test", + views: 2 + }); + }); + + test("updateOne event return is respected", async () => { + const posts = proto.entity("posts", { + title: proto.text(), + views: proto.number() + }); + + const conn = getDummyConnection(); + const em = new EntityManager([posts], conn.dummyConnection); + await em.schema().sync({ force: true }); + + const emgr = em.emgr as EventManager; + + emgr.onEvent( + // @ts-ignore + EntityManager.Events.MutatorUpdateBefore, + async (event) => { + return { + ...event.params.data, + views: event.params.data.views + 1 + }; + }, + "sync" + ); + + const mutator = em.mutator("posts"); + const created = await mutator.insertOne({ title: "test", views: 1 }); + const result = await mutator.updateOne(created.data.id, { views: 2 }); + expect(result.data).toEqual({ + id: 1, + title: "test", + views: 3 + }); + });*/ }); diff --git a/app/__test__/data/specs/Repository.spec.ts b/app/__test__/data/specs/Repository.spec.ts index 0ce8da1..d873389 100644 --- a/app/__test__/data/specs/Repository.spec.ts +++ b/app/__test__/data/specs/Repository.spec.ts @@ -1,7 +1,6 @@ import { afterAll, describe, expect, test } from "bun:test"; -// @ts-ignore -import { Perf } from "@bknd/core/utils"; import type { Kysely, Transaction } from "kysely"; +import { Perf } from "../../../src/core/utils"; import { Entity, EntityManager, @@ -24,7 +23,7 @@ async function sleep(ms: number) { } describe("[Repository]", async () => { - test("bulk", async () => { + test.skip("bulk", async () => { //const connection = dummyConnection; //const connection = getLocalLibsqlConnection(); const credentials = null as any; // @todo: determine what to do here diff --git a/app/__test__/data/specs/WithBuilder.spec.ts b/app/__test__/data/specs/WithBuilder.spec.ts index 9141b62..367d3f0 100644 --- a/app/__test__/data/specs/WithBuilder.spec.ts +++ b/app/__test__/data/specs/WithBuilder.spec.ts @@ -36,7 +36,7 @@ describe("[data] WithBuilder", async () => { const res = qb.compile(); expect(res.sql).toBe( - 'select (select coalesce(json_group_array(json_object(\'id\', "agg"."id", \'content\', "agg"."content", \'author_id\', "agg"."author_id")), \'[]\') from (select "posts"."id" as "id", "posts"."content" as "content", "posts"."author_id" as "author_id" from "posts" where "users"."id" = "posts"."author_id" limit ?) as agg) as "posts" from "users"' + 'select (select coalesce(json_group_array(json_object(\'id\', "agg"."id", \'content\', "agg"."content", \'author_id\', "agg"."author_id")), \'[]\') from (select "posts"."id" as "id", "posts"."content" as "content", "posts"."author_id" as "author_id" from "posts" as "posts" where "posts"."author_id" = "users"."id" limit ?) as agg) as "posts" from "users"' ); expect(res.parameters).toEqual([5]); @@ -50,7 +50,7 @@ describe("[data] WithBuilder", async () => { const res2 = qb2.compile(); expect(res2.sql).toBe( - 'select (select json_object(\'id\', "obj"."id", \'username\', "obj"."username") from (select "users"."id" as "id", "users"."username" as "username" from "users" where "posts"."author_id" = "users"."id" limit ?) as obj) as "author" from "posts"' + 'select (select json_object(\'id\', "obj"."id", \'username\', "obj"."username") from (select "author"."id" as "id", "author"."username" as "username" from "users" as "author" where "author"."id" = "posts"."author_id" limit ?) as obj) as "author" from "posts"' ); expect(res2.parameters).toEqual([1]); }); diff --git a/app/__test__/data/specs/fields/EnumField.spec.ts b/app/__test__/data/specs/fields/EnumField.spec.ts index d60f2e7..7cde4eb 100644 --- a/app/__test__/data/specs/fields/EnumField.spec.ts +++ b/app/__test__/data/specs/fields/EnumField.spec.ts @@ -13,10 +13,6 @@ describe("[data] EnumField", async () => { { options: options(["a", "b", "c"]) } ); - test("yields if no options", async () => { - expect(() => new EnumField("test", { options: options([]) })).toThrow(); - }); - test("yields if default value is not a valid option", async () => { expect( () => new EnumField("test", { options: options(["a", "b"]), default_value: "c" }) diff --git a/app/__test__/data/specs/fields/Field.spec.ts b/app/__test__/data/specs/fields/Field.spec.ts index 6fd8e04..77eb3fd 100644 --- a/app/__test__/data/specs/fields/Field.spec.ts +++ b/app/__test__/data/specs/fields/Field.spec.ts @@ -15,11 +15,9 @@ describe("[data] Field", async () => { runBaseFieldTests(FieldSpec, { defaultValue: "test", schemaType: "text" }); - test.only("default config", async () => { - const field = new FieldSpec("test"); + test("default config", async () => { const config = Default(baseFieldConfigSchema, {}); expect(stripMark(new FieldSpec("test").config)).toEqual(config); - console.log("config", new TextField("test", { required: true }).toJSON()); }); test("transformPersist (specific)", async () => { diff --git a/app/__test__/data/specs/fields/JsonField.spec.ts b/app/__test__/data/specs/fields/JsonField.spec.ts index f13968a..17fdaaa 100644 --- a/app/__test__/data/specs/fields/JsonField.spec.ts +++ b/app/__test__/data/specs/fields/JsonField.spec.ts @@ -32,7 +32,7 @@ describe("[data] JsonField", async () => { }); test("getValue", async () => { - expect(field.getValue({ test: 1 }, "form")).toBe('{"test":1}'); + expect(field.getValue({ test: 1 }, "form")).toBe('{\n "test": 1\n}'); expect(field.getValue("string", "form")).toBe('"string"'); expect(field.getValue(1, "form")).toBe("1"); diff --git a/app/__test__/data/specs/relations/EntityRelation.spec.ts b/app/__test__/data/specs/relations/EntityRelation.spec.ts index 989b4f9..92c50e3 100644 --- a/app/__test__/data/specs/relations/EntityRelation.spec.ts +++ b/app/__test__/data/specs/relations/EntityRelation.spec.ts @@ -70,9 +70,9 @@ describe("[data] EntityRelation", async () => { it("required", async () => { const relation1 = new TestEntityRelation(); - expect(relation1.config.required).toBe(false); + expect(relation1.required).toBe(false); const relation2 = new TestEntityRelation({ required: true }); - expect(relation2.config.required).toBe(true); + expect(relation2.required).toBe(true); }); }); diff --git a/app/__test__/media/adapters/StorageCloudinaryAdapter.spec.ts b/app/__test__/media/adapters/StorageCloudinaryAdapter.spec.ts index 1294275..e2457b2 100644 --- a/app/__test__/media/adapters/StorageCloudinaryAdapter.spec.ts +++ b/app/__test__/media/adapters/StorageCloudinaryAdapter.spec.ts @@ -3,7 +3,7 @@ import { randomString } from "../../../src/core/utils"; import { StorageCloudinaryAdapter } from "../../../src/media"; import { config } from "dotenv"; -const dotenvOutput = config({ path: `${import.meta.dir}/../../.env` }); +const dotenvOutput = config({ path: `${import.meta.dir}/../../../.env` }); const { CLOUDINARY_CLOUD_NAME, CLOUDINARY_API_KEY, diff --git a/app/__test__/media/adapters/StorageLocalAdapter.spec.ts b/app/__test__/media/adapters/StorageLocalAdapter.spec.ts index 29746d1..a7c6d79 100644 --- a/app/__test__/media/adapters/StorageLocalAdapter.spec.ts +++ b/app/__test__/media/adapters/StorageLocalAdapter.spec.ts @@ -15,7 +15,7 @@ describe("StorageLocalAdapter", () => { test("puts an object", async () => { objects = (await adapter.listObjects()).length; - expect(await adapter.putObject(filename, await file.arrayBuffer())).toBeString(); + expect(await adapter.putObject(filename, file)).toBeString(); }); test("lists objects", async () => { diff --git a/app/__test__/media/adapters/StorageS3Adapter.spec.ts b/app/__test__/media/adapters/StorageS3Adapter.spec.ts index d6274dc..7ea77b1 100644 --- a/app/__test__/media/adapters/StorageS3Adapter.spec.ts +++ b/app/__test__/media/adapters/StorageS3Adapter.spec.ts @@ -3,14 +3,14 @@ import { randomString } from "../../../src/core/utils"; import { StorageS3Adapter } from "../../../src/media"; import { config } from "dotenv"; -const dotenvOutput = config({ path: `${import.meta.dir}/../../.env` }); +const dotenvOutput = config({ path: `${import.meta.dir}/../../../.env` }); const { R2_ACCESS_KEY, R2_SECRET_ACCESS_KEY, R2_URL, AWS_ACCESS_KEY, AWS_SECRET_KEY, AWS_S3_URL } = dotenvOutput.parsed!; // @todo: mock r2/s3 responses for faster tests -const ALL_TESTS = process.env.ALL_TESTS; +const ALL_TESTS = !!process.env.ALL_TESTS; -describe("Storage", async () => { +describe.skipIf(ALL_TESTS)("StorageS3Adapter", async () => { console.log("ALL_TESTS", process.env.ALL_TESTS); const versions = [ [ diff --git a/app/bunfig.toml b/app/bunfig.toml index 82e1cd0..6f4fe9a 100644 --- a/app/bunfig.toml +++ b/app/bunfig.toml @@ -1,2 +1,5 @@ [install] -registry = "http://localhost:4873" \ No newline at end of file +#registry = "http://localhost:4873" + +[test] +coverageSkipTestFiles = true \ No newline at end of file diff --git a/app/package.json b/app/package.json index 8baeefd..d8b73d8 100644 --- a/app/package.json +++ b/app/package.json @@ -7,6 +7,7 @@ "scripts": { "dev": "vite", "test": "ALL_TESTS=1 bun test --bail", + "test:coverage": "ALL_TESTS=1 bun test --bail --coverage", "build": "NODE_ENV=production bun run build.ts --minify --types", "build:all": "rm -rf dist && bun run build:static && NODE_ENV=production bun run build.ts --minify --types --clean && bun run build:cli", "build:cli": "bun build src/cli/index.ts --target node --outdir dist/cli --minify", From f47218708a8453b8ab0088056a90e47cf6cc4df2 Mon Sep 17 00:00:00 2001 From: dswbx Date: Thu, 16 Jan 2025 10:13:54 +0100 Subject: [PATCH 09/32] added event related tests to mutator, fixed tests --- app/__test__/data/specs/Mutator.spec.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/__test__/data/specs/Mutator.spec.ts b/app/__test__/data/specs/Mutator.spec.ts index 47134e8..6493d52 100644 --- a/app/__test__/data/specs/Mutator.spec.ts +++ b/app/__test__/data/specs/Mutator.spec.ts @@ -1,4 +1,5 @@ import { afterAll, describe, expect, test } from "bun:test"; +import type { EventManager } from "../../../src/core/events"; import { Entity, EntityManager, @@ -299,7 +300,7 @@ describe("[data] Mutator (Events)", async () => { expect(events.has(MutatorEvents.MutatorDeleteAfter.slug)).toBeTrue(); }); - /*test("insertOne event return is respected", async () => { + test("insertOne event return is respected", async () => { const posts = proto.entity("posts", { title: proto.text(), views: proto.number() @@ -364,5 +365,5 @@ describe("[data] Mutator (Events)", async () => { title: "test", views: 3 }); - });*/ + }); }); From 5343d0bd9dc1c46d6b34726ef1b9b102af7ece9b Mon Sep 17 00:00:00 2001 From: dswbx Date: Thu, 16 Jan 2025 10:30:28 +0100 Subject: [PATCH 10/32] allow bypassing entity data validation for unknown keys in useEntityForm --- app/src/data/entities/Entity.ts | 31 +++++++++++++------ .../ui/modules/data/hooks/useEntityForm.tsx | 6 +++- 2 files changed, 26 insertions(+), 11 deletions(-) diff --git a/app/src/data/entities/Entity.ts b/app/src/data/entities/Entity.ts index 4665322..a0bdb29 100644 --- a/app/src/data/entities/Entity.ts +++ b/app/src/data/entities/Entity.ts @@ -193,29 +193,40 @@ export class Entity< } // @todo: add tests - isValidData(data: EntityData, context: TActionContext, explain?: boolean): boolean { + isValidData( + data: EntityData, + context: TActionContext, + options?: { + explain?: boolean; + ignoreUnknown?: boolean; + } + ): boolean { if (typeof data !== "object") { - if (explain) { + if (options?.explain) { throw new Error(`Entity "${this.name}" data must be an object`); } } const fields = this.getFillableFields(context, false); - const field_names = fields.map((f) => f.name); - const given_keys = Object.keys(data); - if (given_keys.some((key) => !field_names.includes(key))) { - if (explain) { - throw new Error( - `Entity "${this.name}" data must only contain known keys, got: "${given_keys}"` - ); + if (options?.ignoreUnknown !== true) { + const field_names = fields.map((f) => f.name); + const given_keys = Object.keys(data); + const unknown_keys = given_keys.filter((key) => !field_names.includes(key)); + + if (unknown_keys.length > 0) { + if (options?.explain) { + throw new Error( + `Entity "${this.name}" data must only contain known keys, unknown: "${unknown_keys}"` + ); + } } } for (const field of fields) { if (!field.isValid(data[field.name], context)) { console.log("Entity.isValidData:invalid", context, field.name, data[field.name]); - if (explain) { + if (options?.explain) { throw new Error(`Field "${field.name}" has invalid data: "${data[field.name]}"`); } diff --git a/app/src/ui/modules/data/hooks/useEntityForm.tsx b/app/src/ui/modules/data/hooks/useEntityForm.tsx index 45432d7..ebef8c7 100644 --- a/app/src/ui/modules/data/hooks/useEntityForm.tsx +++ b/app/src/ui/modules/data/hooks/useEntityForm.tsx @@ -29,7 +29,11 @@ export function useEntityForm({ onSubmitAsync: async ({ value }): Promise => { try { //console.log("validating", value, entity.isValidData(value, action)); - entity.isValidData(value, action, true); + entity.isValidData(value, action, { + explain: true, + // unknown will later be removed in getChangeSet + ignoreUnknown: true + }); return undefined; } catch (e) { //console.log("---validation error", e); From 37a65bcaf6d843d65c031fcfbd21cb35088e6bd2 Mon Sep 17 00:00:00 2001 From: dswbx Date: Thu, 16 Jan 2025 13:22:15 +0100 Subject: [PATCH 11/32] updated repo query schema, repo and withbuilder validation, and reworked relation with building --- app/__test__/data/data-query-impl.spec.ts | 54 +++++++++++--- app/__test__/data/relations.test.ts | 34 +++------ app/__test__/data/specs/WithBuilder.spec.ts | 66 ++++++++++++++--- app/src/data/entities/query/Repository.ts | 15 +--- app/src/data/entities/query/WithBuilder.ts | 73 +++++++++++++++++-- app/src/data/relations/EntityRelation.ts | 6 +- app/src/data/relations/ManyToManyRelation.ts | 29 +++++++- app/src/data/relations/ManyToOneRelation.ts | 16 +++- app/src/data/relations/PolymorphicRelation.ts | 15 +++- app/src/data/server/data-query-impl.ts | 66 ++++++++++++++--- 10 files changed, 285 insertions(+), 89 deletions(-) diff --git a/app/__test__/data/data-query-impl.spec.ts b/app/__test__/data/data-query-impl.spec.ts index a2fcdff..e2cfb29 100644 --- a/app/__test__/data/data-query-impl.spec.ts +++ b/app/__test__/data/data-query-impl.spec.ts @@ -1,8 +1,14 @@ import { describe, expect, test } from "bun:test"; -import { Value } from "../../src/core/utils"; -import { WhereBuilder, type WhereQuery, querySchema } from "../../src/data"; +import { Value, _jsonp } from "../../src/core/utils"; +import { type RepoQuery, WhereBuilder, type WhereQuery, querySchema } from "../../src/data"; +import type { RepoQueryIn } from "../../src/data/server/data-query-impl"; import { getDummyConnection } from "./helper"; +const decode = (input: RepoQueryIn, expected: RepoQuery) => { + const result = Value.Decode(querySchema, input); + expect(result).toEqual(expected); +}; + describe("data-query-impl", () => { function qb() { const c = getDummyConnection(); @@ -88,21 +94,47 @@ describe("data-query-impl", () => { expect(keys).toEqual(expectedKeys); } }); + + test("with", () => { + decode({ with: ["posts"] }, { with: { posts: {} } }); + decode({ with: { posts: {} } }, { with: { posts: {} } }); + decode({ with: { posts: { limit: "1" } } }, { with: { posts: { limit: 1 } } }); + decode( + { + with: { + posts: { + with: { + images: { + select: "id" + } + } + } + } + }, + { + with: { + posts: { + with: { + images: { + select: ["id"] + } + } + } + } + } + ); + }); }); describe("data-query-impl: Typebox", () => { test("sort", async () => { - const decode = (input: any, expected: any) => { - const result = Value.Decode(querySchema, input); - expect(result.sort).toEqual(expected); - }; - const _dflt = { by: "id", dir: "asc" }; + const _dflt = { sort: { by: "id", dir: "asc" } }; decode({ sort: "" }, _dflt); - decode({ sort: "name" }, { by: "name", dir: "asc" }); - decode({ sort: "-name" }, { by: "name", dir: "desc" }); - decode({ sort: "-posts.name" }, { by: "posts.name", dir: "desc" }); + decode({ sort: "name" }, { sort: { by: "name", dir: "asc" } }); + decode({ sort: "-name" }, { sort: { by: "name", dir: "desc" } }); + decode({ sort: "-posts.name" }, { sort: { by: "posts.name", dir: "desc" } }); decode({ sort: "-1name" }, _dflt); - decode({ sort: { by: "name", dir: "desc" } }, { by: "name", dir: "desc" }); + decode({ sort: { by: "name", dir: "desc" } }, { sort: { by: "name", dir: "desc" } }); }); }); diff --git a/app/__test__/data/relations.test.ts b/app/__test__/data/relations.test.ts index ea56388..c62decb 100644 --- a/app/__test__/data/relations.test.ts +++ b/app/__test__/data/relations.test.ts @@ -119,12 +119,9 @@ describe("Relations", async () => { - select: users.* - cardinality: 1 */ - const selectPostsFromUsers = postAuthorRel.buildWith( - users, - kysely.selectFrom(users.name), - jsonFrom, - "posts" - ); + const selectPostsFromUsers = kysely + .selectFrom(users.name) + .select((eb) => postAuthorRel.buildWith(users, "posts")(eb).as("posts")); expect(selectPostsFromUsers.compile().sql).toBe( 'select (select "posts"."id" as "id", "posts"."title" as "title", "posts"."author_id" as "author_id" from "posts" as "posts" where "posts"."author_id" = "users"."id" limit ?) as "posts" from "users"' ); @@ -141,12 +138,9 @@ describe("Relations", async () => { - select: posts.* - cardinality: */ - const selectUsersFromPosts = postAuthorRel.buildWith( - posts, - kysely.selectFrom(posts.name), - jsonFrom, - "author" - ); + const selectUsersFromPosts = kysely + .selectFrom(posts.name) + .select((eb) => postAuthorRel.buildWith(posts, "author")(eb).as("author")); expect(selectUsersFromPosts.compile().sql).toBe( 'select (select "author"."id" as "id", "author"."username" as "username" from "users" as "author" where "author"."id" = "posts"."author_id" limit ?) as "author" from "posts"' @@ -315,20 +309,16 @@ describe("Relations", async () => { - select: users.* - cardinality: 1 */ - const selectCategoriesFromPosts = postCategoriesRel.buildWith( - posts, - kysely.selectFrom(posts.name), - jsonFrom - ); + const selectCategoriesFromPosts = kysely + .selectFrom(posts.name) + .select((eb) => postCategoriesRel.buildWith(posts)(eb).as("categories")); expect(selectCategoriesFromPosts.compile().sql).toBe( 'select (select "categories"."id" as "id", "categories"."label" as "label" from "categories" inner join "posts_categories" on "categories"."id" = "posts_categories"."categories_id" where "posts"."id" = "posts_categories"."posts_id" limit ?) as "categories" from "posts"' ); - const selectPostsFromCategories = postCategoriesRel.buildWith( - categories, - kysely.selectFrom(categories.name), - jsonFrom - ); + const selectPostsFromCategories = kysely + .selectFrom(categories.name) + .select((eb) => postCategoriesRel.buildWith(categories)(eb).as("posts")); expect(selectPostsFromCategories.compile().sql).toBe( 'select (select "posts"."id" as "id", "posts"."title" as "title" from "posts" inner join "posts_categories" on "posts"."id" = "posts_categories"."posts_id" where "categories"."id" = "posts_categories"."categories_id" limit ?) as "posts" from "categories"' ); diff --git a/app/__test__/data/specs/WithBuilder.spec.ts b/app/__test__/data/specs/WithBuilder.spec.ts index 367d3f0..b8f70ec 100644 --- a/app/__test__/data/specs/WithBuilder.spec.ts +++ b/app/__test__/data/specs/WithBuilder.spec.ts @@ -8,19 +8,60 @@ import { TextField, WithBuilder } from "../../../src/data"; +import * as proto from "../../../src/data/prototype"; import { getDummyConnection } from "../helper"; const { dummyConnection, afterAllCleanup } = getDummyConnection(); afterAll(afterAllCleanup); +function schemaToEm(s: ReturnType<(typeof proto)["em"]>): EntityManager { + return new EntityManager(Object.values(s.entities), dummyConnection, s.relations, s.indices); +} + describe("[data] WithBuilder", async () => { + test("validate withs", async () => { + const schema = proto.em( + { + posts: proto.entity("posts", {}), + users: proto.entity("users", {}), + media: proto.entity("media", {}) + }, + ({ relation }, { posts, users, media }) => { + relation(posts).manyToOne(users); + relation(users).polyToOne(media, { mappedBy: "avatar" }); + } + ); + const em = schemaToEm(schema); + + expect(WithBuilder.validateWiths(em, "posts", undefined)).toBe(0); + expect(WithBuilder.validateWiths(em, "posts", {})).toBe(0); + expect(WithBuilder.validateWiths(em, "posts", { users: {} })).toBe(1); + expect( + WithBuilder.validateWiths(em, "posts", { + users: { + with: { avatar: {} } + } + }) + ).toBe(2); + expect(() => WithBuilder.validateWiths(em, "posts", { author: {} })).toThrow(); + expect(() => + WithBuilder.validateWiths(em, "posts", { + users: { + with: { glibberish: {} } + } + }) + ).toThrow(); + }); + test("missing relation", async () => { const users = new Entity("users", [new TextField("username")]); const em = new EntityManager([users], dummyConnection); expect(() => - WithBuilder.addClause(em, em.connection.kysely.selectFrom("users"), users, ["posts"]) - ).toThrow('Relation "posts" not found'); + WithBuilder.addClause(em, em.connection.kysely.selectFrom("users"), users, { + posts: {} + }) + ).toThrow('Relation "users<>posts" not found'); }); test("addClause: ManyToOne", async () => { @@ -29,9 +70,9 @@ describe("[data] WithBuilder", async () => { const relations = [new ManyToOneRelation(posts, users, { mappedBy: "author" })]; const em = new EntityManager([users, posts], dummyConnection, relations); - const qb = WithBuilder.addClause(em, em.connection.kysely.selectFrom("users"), users, [ - "posts" - ]); + const qb = WithBuilder.addClause(em, em.connection.kysely.selectFrom("users"), users, { + posts: {} + }); const res = qb.compile(); @@ -44,7 +85,9 @@ describe("[data] WithBuilder", async () => { em, em.connection.kysely.selectFrom("posts"), posts, // @todo: try with "users", it gives output! - ["author"] + { + author: {} + } ); const res2 = qb2.compile(); @@ -56,9 +99,10 @@ describe("[data] WithBuilder", async () => { }); test("test with empty join", async () => { + const em = new EntityManager([], dummyConnection); const qb = { qb: 1 } as any; - expect(WithBuilder.addClause(null as any, qb, null as any, [])).toBe(qb); + expect(WithBuilder.addClause(em, qb, null as any, {})).toBe(qb); }); test("test manytomany", async () => { @@ -89,7 +133,7 @@ describe("[data] WithBuilder", async () => { //console.log((await em.repository().findMany("posts_categories")).result); - const res = await em.repository(posts).findMany({ with: ["categories"] }); + const res = await em.repository(posts).findMany({ with: { categories: {} } }); expect(res.data).toEqual([ { @@ -107,7 +151,7 @@ describe("[data] WithBuilder", async () => { } ]); - const res2 = await em.repository(categories).findMany({ with: ["posts"] }); + const res2 = await em.repository(categories).findMany({ with: { posts: {} } }); //console.log(res2.sql, res2.data); @@ -150,7 +194,7 @@ describe("[data] WithBuilder", async () => { em, em.connection.kysely.selectFrom("categories"), categories, - ["single"] + { single: {} } ); const res = qb.compile(); expect(res.sql).toBe( @@ -162,7 +206,7 @@ describe("[data] WithBuilder", async () => { em, em.connection.kysely.selectFrom("categories"), categories, - ["multiple"] + { multiple: {} } ); const res2 = qb2.compile(); expect(res2.sql).toBe( diff --git a/app/src/data/entities/query/Repository.ts b/app/src/data/entities/query/Repository.ts index a6dc576..638b99b 100644 --- a/app/src/data/entities/query/Repository.ts +++ b/app/src/data/entities/query/Repository.ts @@ -103,17 +103,10 @@ export class Repository 0) { - for (const entry of options.with) { - const related = this.em.relationOf(entity.name, entry); - if (!related) { - throw new InvalidSearchParamsException( - `WITH: "${entry}" is not a relation of "${entity.name}"` - ); - } - - validated.with.push(entry); - } + if (options.with) { + const depth = WithBuilder.validateWiths(this.em, entity.name, options.with); + // @todo: determine allowed depth + validated.with = options.with; } if (options.join && options.join.length > 0) { diff --git a/app/src/data/entities/query/WithBuilder.ts b/app/src/data/entities/query/WithBuilder.ts index 260dc86..1d5cfe5 100644 --- a/app/src/data/entities/query/WithBuilder.ts +++ b/app/src/data/entities/query/WithBuilder.ts @@ -1,11 +1,16 @@ +import { isObject } from "core/utils"; +import type { KyselyJsonFrom, RepoQuery } from "data"; +import { InvalidSearchParamsException } from "data/errors"; +import type { RepoWithSchema } from "data/server/data-query-impl"; import type { Entity, EntityManager, RepositoryQB } from "../../entities"; export class WithBuilder { - private static buildClause( + /*private static buildClause( em: EntityManager, qb: RepositoryQB, entity: Entity, - withString: string + ref: string, + config?: RepoQuery ) { const relation = em.relationOf(entity.name, withString); if (!relation) { @@ -15,7 +20,6 @@ export class WithBuilder { const cardinality = relation.ref(withString).cardinality; //console.log("with--builder", { entity: entity.name, withString, cardinality }); - const fns = em.connection.fn; const jsonFrom = cardinality === 1 ? fns.jsonObjectFrom : fns.jsonArrayFrom; if (!jsonFrom) { @@ -27,16 +31,69 @@ export class WithBuilder { } catch (e) { throw new Error(`Could not build "with" relation "${withString}": ${(e as any).message}`); } - } + }*/ - static addClause(em: EntityManager, qb: RepositoryQB, entity: Entity, withs: string[]) { - if (withs.length === 0) return qb; + static addClause( + em: EntityManager, + qb: RepositoryQB, + entity: Entity, + withs: RepoQuery["with"] + ) { + if (!withs || !isObject(withs)) { + console.warn(`'withs' undefined or invalid, given: ${JSON.stringify(withs)}`); + return qb; + } + const fns = em.connection.fn; let newQb = qb; - for (const entry of withs) { - newQb = WithBuilder.buildClause(em, newQb, entity, entry); + + for (const [ref, query] of Object.entries(withs)) { + const relation = em.relationOf(entity.name, ref); + if (!relation) { + throw new Error(`Relation "${entity.name}<>${ref}" not found`); + } + const cardinality = relation.ref(ref).cardinality; + const jsonFrom: KyselyJsonFrom = + cardinality === 1 ? fns.jsonObjectFrom : fns.jsonArrayFrom; + if (!jsonFrom) { + throw new Error("Connection does not support jsonObjectFrom/jsonArrayFrom"); + } + + const alias = relation.other(entity).reference; + newQb = newQb.select((eb) => { + return jsonFrom(relation.buildWith(entity, ref)(eb)).as(alias); + }); + //newQb = relation.buildWith(entity, qb, jsonFrom, ref); } return newQb; } + + static validateWiths(em: EntityManager, entity: string, withs: RepoQuery["with"]) { + let depth = 0; + if (!withs || !isObject(withs)) { + console.warn(`'withs' undefined or invalid, given: ${JSON.stringify(withs)}`); + return depth; + } + + const child_depths: number[] = []; + for (const [ref, query] of Object.entries(withs)) { + const related = em.relationOf(entity, ref); + if (!related) { + throw new InvalidSearchParamsException( + `WITH: "${ref}" is not a relation of "${entity}"` + ); + } + depth++; + + if ("with" in query) { + child_depths.push(WithBuilder.validateWiths(em, ref, query.with as any)); + } + } + if (child_depths.length > 0) { + depth += Math.max(...child_depths); + } + + return depth; + } } diff --git a/app/src/data/relations/EntityRelation.ts b/app/src/data/relations/EntityRelation.ts index e7d680b..07611d0 100644 --- a/app/src/data/relations/EntityRelation.ts +++ b/app/src/data/relations/EntityRelation.ts @@ -1,5 +1,5 @@ import { type Static, Type, parse } from "core/utils"; -import type { SelectQueryBuilder } from "kysely"; +import type { ExpressionBuilder, SelectQueryBuilder } from "kysely"; import type { Entity, EntityData, EntityManager } from "../entities"; import { type EntityRelationAnchor, @@ -67,10 +67,8 @@ export abstract class EntityRelation< */ abstract buildWith( entity: Entity, - qb: KyselyQueryBuilder, - jsonFrom: KyselyJsonFrom, reference: string - ): KyselyQueryBuilder; + ): (eb: ExpressionBuilder) => KyselyQueryBuilder; abstract buildJoin( entity: Entity, diff --git a/app/src/data/relations/ManyToManyRelation.ts b/app/src/data/relations/ManyToManyRelation.ts index 25dbdca..b4e89f1 100644 --- a/app/src/data/relations/ManyToManyRelation.ts +++ b/app/src/data/relations/ManyToManyRelation.ts @@ -1,4 +1,5 @@ import { type Static, Type } from "core/utils"; +import type { ExpressionBuilder } from "kysely"; import { Entity, type EntityManager } from "../entities"; import { type Field, PrimaryField, VirtualField } from "../fields"; import type { RepoQuery } from "../server/data-query-impl"; @@ -123,7 +124,7 @@ export class ManyToManyRelation extends EntityRelation !(f instanceof RelationField || f instanceof PrimaryField) ); - return qb.select((eb) => { + return (eb: ExpressionBuilder) => + eb + .selectFrom(other.entity.name) + .select((eb2) => { + const select: any[] = other.entity.getSelect(other.entity.name); + if (additionalFields.length > 0) { + const conn = this.connectionEntity.name; + select.push( + jsonBuildObject( + Object.fromEntries( + additionalFields.map((f) => [f.name, eb2.ref(`${conn}.${f.name}`)]) + ) + ).as(this.connectionTableMappedName) + ); + } + + return select; + }) + .whereRef(entityRef, "=", otherRef) + .innerJoin(...join) + .limit(limit); + + /*return qb.select((eb) => { const select: any[] = other.entity.getSelect(other.entity.name); // @todo: also add to find by references if (additionalFields.length > 0) { @@ -160,7 +183,7 @@ export class ManyToManyRelation extends EntityRelation) { diff --git a/app/src/data/relations/ManyToOneRelation.ts b/app/src/data/relations/ManyToOneRelation.ts index 57bb993..e95ea06 100644 --- a/app/src/data/relations/ManyToOneRelation.ts +++ b/app/src/data/relations/ManyToOneRelation.ts @@ -1,6 +1,7 @@ import type { PrimaryFieldType } from "core"; import { snakeToPascalWithSpaces } from "core/utils"; import { type Static, Type } from "core/utils"; +import type { ExpressionBuilder } from "kysely"; import type { Entity, EntityManager } from "../entities"; import type { RepoQuery } from "../server/data-query-impl"; import { EntityRelation, type KyselyJsonFrom, type KyselyQueryBuilder } from "./EntityRelation"; @@ -155,15 +156,22 @@ export class ManyToOneRelation extends EntityRelation + return (eb: ExpressionBuilder) => + eb + .selectFrom(`${self.entity.name} as ${relationRef}`) + .select(self.entity.getSelect(relationRef)) + .whereRef(entityRef, "=", otherRef) + .limit(limit); + + /*return qb.select((eb) => jsonFrom( eb .selectFrom(`${self.entity.name} as ${relationRef}`) @@ -171,7 +179,7 @@ export class ManyToOneRelation extends EntityRelation + return (eb: ExpressionBuilder) => + eb + .selectFrom(other.entity.name) + .select(other.entity.getSelect(other.entity.name)) + .where(whereLhs, "=", reference) + .whereRef(entityRef, "=", otherRef) + .limit(limit); + + /*return qb.select((eb) => jsonFrom( eb .selectFrom(other.entity.name) @@ -100,7 +109,7 @@ export class PolymorphicRelation extends EntityRelation; + join?: string[]; + where?: any; +}; +export type RepoWithSchema = Record< + string, + Omit & { + with?: unknown; } +>; +export const withSchema = (Self: TSelf) => + Type.Transform(Type.Union([stringArray, Type.Record(Type.String(), Self)])) + .Decode((value) => { + let _value = value; + if (Array.isArray(value)) { + if (!value.every((v) => typeof v === "string")) { + throw new Error("Invalid 'with' schema"); + } + + _value = value.reduce((acc, v) => { + acc[v] = {}; + return acc; + }, {} as RepoWithSchema); + } + + return _value as RepoWithSchema; + }) + .Encode((value) => value); + +export const querySchema = Type.Recursive( + (Self) => + Type.Partial( + Type.Object( + { + limit: limit, + offset: offset, + sort: sort, + select: stringArray, + with: withSchema(Self), + join: stringArray, + where: whereSchema + }, + { + // @todo: determine if unknown is allowed, it's ignore anyway + additionalProperties: false + } + ) + ), + { $id: "query-schema" } ); export type RepoQueryIn = Static; From 26a5fd8b34d394c67f3d019720300b12bcb18d8e Mon Sep 17 00:00:00 2001 From: dswbx Date: Thu, 16 Jan 2025 15:25:30 +0100 Subject: [PATCH 12/32] reworked `WithBuilder` to allow recursive `with` operations --- app/__test__/data/relations.test.ts | 5 +- app/__test__/data/specs/WithBuilder.spec.ts | 227 +++++++++++++++++- app/__test__/helper.ts | 13 +- app/package.json | 1 + .../data/connection/SqliteLocalConnection.ts | 5 +- app/src/data/entities/query/Repository.ts | 74 ++++-- app/src/data/entities/query/WithBuilder.ts | 49 ++-- app/src/data/relations/ManyToOneRelation.ts | 18 +- app/src/data/relations/PolymorphicRelation.ts | 15 +- bun.lockb | Bin 1063936 -> 1064392 bytes 10 files changed, 305 insertions(+), 102 deletions(-) diff --git a/app/__test__/data/relations.test.ts b/app/__test__/data/relations.test.ts index c62decb..19eab85 100644 --- a/app/__test__/data/relations.test.ts +++ b/app/__test__/data/relations.test.ts @@ -106,7 +106,6 @@ describe("Relations", async () => { expect(postAuthorRel?.other(posts).entity).toBe(users); const kysely = em.connection.kysely; - const jsonFrom = (e) => e; /** * Relation Helper */ @@ -123,7 +122,7 @@ describe("Relations", async () => { .selectFrom(users.name) .select((eb) => postAuthorRel.buildWith(users, "posts")(eb).as("posts")); expect(selectPostsFromUsers.compile().sql).toBe( - 'select (select "posts"."id" as "id", "posts"."title" as "title", "posts"."author_id" as "author_id" from "posts" as "posts" where "posts"."author_id" = "users"."id" limit ?) as "posts" from "users"' + 'select (select from "posts" as "posts" where "posts"."author_id" = "users"."id") as "posts" from "users"' ); expect(postAuthorRel!.getField()).toBeInstanceOf(RelationField); const userObj = { id: 1, username: "test" }; @@ -143,7 +142,7 @@ describe("Relations", async () => { .select((eb) => postAuthorRel.buildWith(posts, "author")(eb).as("author")); expect(selectUsersFromPosts.compile().sql).toBe( - 'select (select "author"."id" as "id", "author"."username" as "username" from "users" as "author" where "author"."id" = "posts"."author_id" limit ?) as "author" from "posts"' + 'select (select from "users" as "author" where "author"."id" = "posts"."author_id" limit ?) as "author" from "posts"' ); expect(postAuthorRel.getField()).toBeInstanceOf(RelationField); const postObj = { id: 1, title: "test" }; diff --git a/app/__test__/data/specs/WithBuilder.spec.ts b/app/__test__/data/specs/WithBuilder.spec.ts index b8f70ec..bed48a6 100644 --- a/app/__test__/data/specs/WithBuilder.spec.ts +++ b/app/__test__/data/specs/WithBuilder.spec.ts @@ -1,4 +1,5 @@ import { afterAll, describe, expect, test } from "bun:test"; +import { _jsonp } from "../../../src/core/utils"; import { Entity, EntityManager, @@ -9,12 +10,13 @@ import { WithBuilder } from "../../../src/data"; import * as proto from "../../../src/data/prototype"; +import { compileQb, prettyPrintQb } from "../../helper"; import { getDummyConnection } from "../helper"; -const { dummyConnection, afterAllCleanup } = getDummyConnection(); -afterAll(afterAllCleanup); +const { dummyConnection } = getDummyConnection(); function schemaToEm(s: ReturnType<(typeof proto)["em"]>): EntityManager { + const { dummyConnection } = getDummyConnection(); return new EntityManager(Object.values(s.entities), dummyConnection, s.relations, s.indices); } @@ -77,9 +79,9 @@ describe("[data] WithBuilder", async () => { const res = qb.compile(); expect(res.sql).toBe( - 'select (select coalesce(json_group_array(json_object(\'id\', "agg"."id", \'content\', "agg"."content", \'author_id\', "agg"."author_id")), \'[]\') from (select "posts"."id" as "id", "posts"."content" as "content", "posts"."author_id" as "author_id" from "posts" as "posts" where "posts"."author_id" = "users"."id" limit ?) as agg) as "posts" from "users"' + 'select (select coalesce(json_group_array(json_object(\'id\', "agg"."id", \'content\', "agg"."content", \'author_id\', "agg"."author_id")), \'[]\') from (select "posts"."id" as "id", "posts"."content" as "content", "posts"."author_id" as "author_id" from "posts" as "posts" where "posts"."author_id" = "users"."id" order by "posts"."id" asc limit ? offset ?) as agg) as "posts" from "users"' ); - expect(res.parameters).toEqual([5]); + expect(res.parameters).toEqual([10, 0]); const qb2 = WithBuilder.addClause( em, @@ -93,9 +95,9 @@ describe("[data] WithBuilder", async () => { const res2 = qb2.compile(); expect(res2.sql).toBe( - 'select (select json_object(\'id\', "obj"."id", \'username\', "obj"."username") from (select "author"."id" as "id", "author"."username" as "username" from "users" as "author" where "author"."id" = "posts"."author_id" limit ?) as obj) as "author" from "posts"' + 'select (select json_object(\'id\', "obj"."id", \'username\', "obj"."username") from (select "users"."id" as "id", "users"."username" as "username" from "users" as "author" where "author"."id" = "posts"."author_id" order by "users"."id" asc limit ? offset ?) as obj) as "author" from "posts"' ); - expect(res2.parameters).toEqual([1]); + expect(res2.parameters).toEqual([1, 0]); }); test("test with empty join", async () => { @@ -165,8 +167,8 @@ describe("[data] WithBuilder", async () => { id: 2, label: "beauty", posts: [ - { id: 2, title: "beauty post" }, - { id: 1, title: "fashion post" } + { id: 1, title: "fashion post" }, + { id: 2, title: "beauty post" } ] }, { @@ -198,9 +200,9 @@ describe("[data] WithBuilder", async () => { ); const res = qb.compile(); expect(res.sql).toBe( - 'select (select json_object(\'id\', "obj"."id", \'path\', "obj"."path") from (select "media"."id" as "id", "media"."path" as "path" from "media" where "media"."reference" = ? and "categories"."id" = "media"."entity_id" limit ?) as obj) as "single" from "categories"' + 'select (select json_object(\'id\', "obj"."id", \'path\', "obj"."path") from (select "media"."id" as "id", "media"."path" as "path" from "media" where "media"."reference" = ? and "categories"."id" = "media"."entity_id" order by "media"."id" asc limit ? offset ?) as obj) as "single" from "categories"' ); - expect(res.parameters).toEqual(["categories.single", 1]); + expect(res.parameters).toEqual(["categories.single", 1, 0]); const qb2 = WithBuilder.addClause( em, @@ -210,9 +212,9 @@ describe("[data] WithBuilder", async () => { ); const res2 = qb2.compile(); expect(res2.sql).toBe( - 'select (select coalesce(json_group_array(json_object(\'id\', "agg"."id", \'path\', "agg"."path")), \'[]\') from (select "media"."id" as "id", "media"."path" as "path" from "media" where "media"."reference" = ? and "categories"."id" = "media"."entity_id" limit ?) as agg) as "multiple" from "categories"' + 'select (select coalesce(json_group_array(json_object(\'id\', "agg"."id", \'path\', "agg"."path")), \'[]\') from (select "media"."id" as "id", "media"."path" as "path" from "media" where "media"."reference" = ? and "categories"."id" = "media"."entity_id" order by "media"."id" asc limit ? offset ?) as agg) as "multiple" from "categories"' ); - expect(res2.parameters).toEqual(["categories.multiple", 5]); + expect(res2.parameters).toEqual(["categories.multiple", 10, 0]); }); /*test("test manytoone", async () => { @@ -236,4 +238,205 @@ describe("[data] WithBuilder", async () => { const res = await em.repository().findMany("posts", { join: ["author"] }); console.log(res.sql, res.parameters, res.result); });*/ + + describe("recursive", () => { + test("compiles with singles", async () => { + const schema = proto.em( + { + posts: proto.entity("posts", {}), + users: proto.entity("users", { + username: proto.text() + }), + media: proto.entity("media", { + path: proto.text() + }) + }, + ({ relation }, { posts, users, media }) => { + relation(posts).manyToOne(users); + relation(users).polyToOne(media, { mappedBy: "avatar" }); + } + ); + const em = schemaToEm(schema); + + const qb = WithBuilder.addClause( + em, + em.connection.kysely.selectFrom("posts"), + schema.entities.posts, + { + users: { + limit: 5, // ignored + select: ["id", "username"], + sort: { by: "username", dir: "asc" }, + with: { + avatar: { + select: ["id", "path"], + limit: 2 // ignored + } + } + } + } + ); + + //prettyPrintQb(qb); + expect(qb.compile().sql).toBe( + 'select (select json_object(\'id\', "obj"."id", \'username\', "obj"."username", \'avatar\', "obj"."avatar") from (select "users"."id" as "id", "users"."username" as "username", (select json_object(\'id\', "obj"."id", \'path\', "obj"."path") from (select "media"."id" as "id", "media"."path" as "path" from "media" where "media"."reference" = ? and "users"."id" = "media"."entity_id" order by "media"."id" asc limit ? offset ?) as obj) as "avatar" from "users" as "users" where "users"."id" = "posts"."users_id" order by "users"."username" asc limit ? offset ?) as obj) as "users" from "posts"' + ); + expect(qb.compile().parameters).toEqual(["users.avatar", 1, 0, 1, 0]); + }); + + test("compiles with many", async () => { + const schema = proto.em( + { + posts: proto.entity("posts", {}), + comments: proto.entity("comments", {}), + users: proto.entity("users", { + username: proto.text() + }), + media: proto.entity("media", { + path: proto.text() + }) + }, + ({ relation }, { posts, comments, users, media }) => { + relation(posts).manyToOne(users).polyToOne(media, { mappedBy: "images" }); + relation(users).polyToOne(media, { mappedBy: "avatar" }); + relation(comments).manyToOne(posts).manyToOne(users); + } + ); + const em = schemaToEm(schema); + + const qb = WithBuilder.addClause( + em, + em.connection.kysely.selectFrom("posts"), + schema.entities.posts, + { + comments: { + limit: 12, + with: { + users: { + select: ["username"] + } + } + } + } + ); + + expect(qb.compile().sql).toBe( + 'select (select coalesce(json_group_array(json_object(\'id\', "agg"."id", \'posts_id\', "agg"."posts_id", \'users_id\', "agg"."users_id", \'users\', "agg"."users")), \'[]\') from (select "comments"."id" as "id", "comments"."posts_id" as "posts_id", "comments"."users_id" as "users_id", (select json_object(\'username\', "obj"."username") from (select "users"."username" as "username" from "users" as "users" where "users"."id" = "comments"."users_id" order by "users"."id" asc limit ? offset ?) as obj) as "users" from "comments" as "comments" where "comments"."posts_id" = "posts"."id" order by "comments"."id" asc limit ? offset ?) as agg) as "comments" from "posts"' + ); + expect(qb.compile().parameters).toEqual([1, 0, 12, 0]); + }); + + test("returns correct result", async () => { + const schema = proto.em( + { + posts: proto.entity("posts", { + title: proto.text() + }), + comments: proto.entity("comments", { + content: proto.text() + }), + users: proto.entity("users", { + username: proto.text() + }), + media: proto.entity("media", { + path: proto.text() + }) + }, + ({ relation }, { posts, comments, users, media }) => { + relation(posts).manyToOne(users).polyToOne(media, { mappedBy: "images" }); + relation(users).polyToOne(media, { mappedBy: "avatar" }); + relation(comments).manyToOne(posts).manyToOne(users); + } + ); + const em = schemaToEm(schema); + await em.schema().sync({ force: true }); + + // add data + await em.mutator("users").insertMany([{ username: "user1" }, { username: "user2" }]); + await em.mutator("posts").insertMany([ + { title: "post1", users_id: 1 }, + { title: "post2", users_id: 1 }, + { title: "post3", users_id: 2 } + ]); + await em.mutator("comments").insertMany([ + { content: "comment1", posts_id: 1, users_id: 1 }, + { content: "comment1-1", posts_id: 1, users_id: 1 }, + { content: "comment2", posts_id: 1, users_id: 2 }, + { content: "comment3", posts_id: 2, users_id: 1 }, + { content: "comment4", posts_id: 2, users_id: 2 }, + { content: "comment5", posts_id: 3, users_id: 1 }, + { content: "comment6", posts_id: 3, users_id: 2 } + ]); + + const result = await em.repo("posts").findMany({ + select: ["title"], + with: { + comments: { + limit: 2, + select: ["content"], + with: { + users: { + select: ["username"] + } + } + } + } + }); + + expect(result.data).toEqual([ + { + title: "post1", + comments: [ + { + content: "comment1", + users: { + username: "user1" + } + }, + { + content: "comment1-1", + users: { + username: "user1" + } + } + ] + }, + { + title: "post2", + comments: [ + { + content: "comment3", + users: { + username: "user1" + } + }, + { + content: "comment4", + users: { + username: "user2" + } + } + ] + }, + { + title: "post3", + comments: [ + { + content: "comment5", + users: { + username: "user1" + } + }, + { + content: "comment6", + users: { + username: "user2" + } + } + ] + } + ]); + //console.log(_jsonp(result.data)); + }); + }); }); diff --git a/app/__test__/helper.ts b/app/__test__/helper.ts index e11da33..de6993e 100644 --- a/app/__test__/helper.ts +++ b/app/__test__/helper.ts @@ -1,6 +1,7 @@ import { unlink } from "node:fs/promises"; -import type { SqliteDatabase } from "kysely"; +import type { SelectQueryBuilder, SqliteDatabase } from "kysely"; import Database from "libsql"; +import { format as sqlFormat } from "sql-formatter"; import { SqliteLocalConnection } from "../src/data"; export function getDummyDatabase(memory: boolean = true): { @@ -51,3 +52,13 @@ export function enableConsoleLog() { console[severity as ConsoleSeverity] = fn; }); } + +export function compileQb(qb: SelectQueryBuilder) { + const { sql, parameters } = qb.compile(); + return { sql, parameters }; +} + +export function prettyPrintQb(qb: SelectQueryBuilder) { + const { sql, parameters } = qb.compile(); + console.log("$", sqlFormat(sql), "\n[params]", parameters); +} diff --git a/app/package.json b/app/package.json index 038d7cd..d05eccc 100644 --- a/app/package.json +++ b/app/package.json @@ -74,6 +74,7 @@ "react-hook-form": "^7.53.1", "react-icons": "5.2.1", "react-json-view-lite": "^2.0.1", + "sql-formatter": "^15.4.9", "tailwind-merge": "^2.5.4", "tailwindcss": "^3.4.14", "tailwindcss-animate": "^1.0.7", diff --git a/app/src/data/connection/SqliteLocalConnection.ts b/app/src/data/connection/SqliteLocalConnection.ts index 0b1a8c8..b3bfab3 100644 --- a/app/src/data/connection/SqliteLocalConnection.ts +++ b/app/src/data/connection/SqliteLocalConnection.ts @@ -1,6 +1,5 @@ -import type { DatabaseIntrospector, SqliteDatabase } from "kysely"; +import { type DatabaseIntrospector, ParseJSONResultsPlugin, type SqliteDatabase } from "kysely"; import { Kysely, SqliteDialect } from "kysely"; -import { DeserializeJsonValuesPlugin } from "../plugins/DeserializeJsonValuesPlugin"; import { SqliteConnection } from "./SqliteConnection"; import { SqliteIntrospector } from "./SqliteIntrospector"; @@ -14,7 +13,7 @@ class CustomSqliteDialect extends SqliteDialect { export class SqliteLocalConnection extends SqliteConnection { constructor(private database: SqliteDatabase) { - const plugins = [new DeserializeJsonValuesPlugin()]; + const plugins = [new ParseJSONResultsPlugin()]; const kysely = new Kysely({ dialect: new CustomSqliteDialect({ database }), plugins diff --git a/app/src/data/entities/query/Repository.ts b/app/src/data/entities/query/Repository.ts index 638b99b..5234bc4 100644 --- a/app/src/data/entities/query/Repository.ts +++ b/app/src/data/entities/query/Repository.ts @@ -65,7 +65,7 @@ export class Repository): RepoQuery { + getValidOptions(options?: Partial): RepoQuery { const entity = this.entity; // @todo: if not cloned deep, it will keep references and error if multiple requests come in const validated = { @@ -228,43 +228,79 @@ export class Repository, - exclude_options: (keyof RepoQuery)[] = [] - ): { qb: RepositoryQB; options: RepoQuery } { + config?: { + validate?: boolean; + ignore?: (keyof RepoQuery)[]; + alias?: string; + defaults?: Pick; + } + ) { const entity = this.entity; - const options = this.getValidOptions(_options); + let qb = _qb ?? (this.conn.selectFrom(entity.name) as RepositoryQB); - const alias = entity.name; + const options = config?.validate !== false ? this.getValidOptions(_options) : _options; + if (!options) return qb; + + const alias = config?.alias ?? entity.name; const aliased = (field: string) => `${alias}.${field}`; - let qb = this.conn - .selectFrom(entity.name) - .select(entity.getAliasedSelectFrom(options.select, alias)); + const ignore = config?.ignore ?? []; + const defaults = { + limit: 10, + offset: 0, + ...config?.defaults + }; - //console.log("build query options", options); - if (!exclude_options.includes("with") && options.with) { + /*console.log("build query options", { + entity: entity.name, + options, + config + });*/ + + if (!ignore.includes("select") && options.select) { + qb = qb.select(entity.getAliasedSelectFrom(options.select, alias)); + } + + if (!ignore.includes("with") && options.with) { qb = WithBuilder.addClause(this.em, qb, entity, options.with); } - if (!exclude_options.includes("join") && options.join) { + if (!ignore.includes("join") && options.join) { qb = JoinBuilder.addClause(this.em, qb, entity, options.join); } // add where if present - if (!exclude_options.includes("where") && options.where) { + if (!ignore.includes("where") && options.where) { qb = WhereBuilder.addClause(qb, options.where); } - if (!exclude_options.includes("limit")) qb = qb.limit(options.limit); - if (!exclude_options.includes("offset")) qb = qb.offset(options.offset); + if (!ignore.includes("limit")) qb = qb.limit(options.limit ?? defaults.limit); + if (!ignore.includes("offset")) qb = qb.offset(options.offset ?? defaults.offset); // sorting - if (!exclude_options.includes("sort")) { - qb = qb.orderBy(aliased(options.sort.by), options.sort.dir); + if (!ignore.includes("sort")) { + qb = qb.orderBy(aliased(options.sort?.by ?? "id"), options.sort?.dir ?? "asc"); } - //console.log("options", { _options, options, exclude_options }); - return { qb, options }; + return qb as RepositoryQB; + } + + private buildQuery( + _options?: Partial, + ignore: (keyof RepoQuery)[] = [] + ): { qb: RepositoryQB; options: RepoQuery } { + const entity = this.entity; + const options = this.getValidOptions(_options); + + return { + qb: this.addOptionsToQueryBuilder(undefined, options, { + ignore, + alias: entity.name + }), + options + }; } async findId( diff --git a/app/src/data/entities/query/WithBuilder.ts b/app/src/data/entities/query/WithBuilder.ts index 1d5cfe5..ce4f14c 100644 --- a/app/src/data/entities/query/WithBuilder.ts +++ b/app/src/data/entities/query/WithBuilder.ts @@ -1,38 +1,9 @@ import { isObject } from "core/utils"; import type { KyselyJsonFrom, RepoQuery } from "data"; import { InvalidSearchParamsException } from "data/errors"; -import type { RepoWithSchema } from "data/server/data-query-impl"; import type { Entity, EntityManager, RepositoryQB } from "../../entities"; export class WithBuilder { - /*private static buildClause( - em: EntityManager, - qb: RepositoryQB, - entity: Entity, - ref: string, - config?: RepoQuery - ) { - const relation = em.relationOf(entity.name, withString); - if (!relation) { - throw new Error(`Relation "${withString}" not found`); - } - - const cardinality = relation.ref(withString).cardinality; - //console.log("with--builder", { entity: entity.name, withString, cardinality }); - - const jsonFrom = cardinality === 1 ? fns.jsonObjectFrom : fns.jsonArrayFrom; - - if (!jsonFrom) { - throw new Error("Connection does not support jsonObjectFrom/jsonArrayFrom"); - } - - try { - return relation.buildWith(entity, qb, jsonFrom, withString); - } catch (e) { - throw new Error(`Could not build "with" relation "${withString}": ${(e as any).message}`); - } - }*/ - static addClause( em: EntityManager, qb: RepositoryQB, @@ -59,11 +30,23 @@ export class WithBuilder { throw new Error("Connection does not support jsonObjectFrom/jsonArrayFrom"); } - const alias = relation.other(entity).reference; + const other = relation.other(entity); newQb = newQb.select((eb) => { - return jsonFrom(relation.buildWith(entity, ref)(eb)).as(alias); + let subQuery = relation.buildWith(entity, ref)(eb); + if (query) { + subQuery = em.repo(other.entity).addOptionsToQueryBuilder(subQuery, query as any, { + ignore: ["with", "join", cardinality === 1 ? "limit" : undefined].filter( + Boolean + ) as any + }); + } + + if (query.with) { + subQuery = WithBuilder.addClause(em, subQuery, other.entity, query.with as any); + } + + return jsonFrom(subQuery).as(other.reference); }); - //newQb = relation.buildWith(entity, qb, jsonFrom, ref); } return newQb; @@ -72,7 +55,7 @@ export class WithBuilder { static validateWiths(em: EntityManager, entity: string, withs: RepoQuery["with"]) { let depth = 0; if (!withs || !isObject(withs)) { - console.warn(`'withs' undefined or invalid, given: ${JSON.stringify(withs)}`); + withs && console.warn(`'withs' invalid, given: ${JSON.stringify(withs)}`); return depth; } diff --git a/app/src/data/relations/ManyToOneRelation.ts b/app/src/data/relations/ManyToOneRelation.ts index e95ea06..de53ad1 100644 --- a/app/src/data/relations/ManyToOneRelation.ts +++ b/app/src/data/relations/ManyToOneRelation.ts @@ -158,28 +158,12 @@ export class ManyToOneRelation extends EntityRelation) => eb .selectFrom(`${self.entity.name} as ${relationRef}`) - .select(self.entity.getSelect(relationRef)) .whereRef(entityRef, "=", otherRef) - .limit(limit); - - /*return qb.select((eb) => - jsonFrom( - eb - .selectFrom(`${self.entity.name} as ${relationRef}`) - .select(self.entity.getSelect(relationRef)) - .whereRef(entityRef, "=", otherRef) - .limit(limit) - ).as(relationRef) - );*/ + .$if(self.cardinality === 1, (qb) => qb.limit(1)); } /** diff --git a/app/src/data/relations/PolymorphicRelation.ts b/app/src/data/relations/PolymorphicRelation.ts index fa30974..cf77108 100644 --- a/app/src/data/relations/PolymorphicRelation.ts +++ b/app/src/data/relations/PolymorphicRelation.ts @@ -90,26 +90,13 @@ export class PolymorphicRelation extends EntityRelation) => eb .selectFrom(other.entity.name) - .select(other.entity.getSelect(other.entity.name)) .where(whereLhs, "=", reference) .whereRef(entityRef, "=", otherRef) - .limit(limit); - - /*return qb.select((eb) => - jsonFrom( - eb - .selectFrom(other.entity.name) - .select(other.entity.getSelect(other.entity.name)) - .where(whereLhs, "=", reference) - .whereRef(entityRef, "=", otherRef) - .limit(limit) - ).as(other.reference) - );*/ + .$if(other.cardinality === 1, (qb) => qb.limit(1)); } override isListableFor(entity: Entity): boolean { diff --git a/bun.lockb b/bun.lockb index 82c57ef5689e65252ca9ee888853ae070ea65dbb..19836ad79553abab4d05eb8769dd45966e6eea54 100755 GIT binary patch delta 163852 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
- -
- - + <> + {error && } +
+ +
+ + +
-
+ ); } @@ -63,7 +89,7 @@ SchemaFormModal.modalProps = { root: "bknd-admin", header: "!bg-primary/5 border-b border-b-muted !py-3 px-5 !h-auto !min-h-px", content: "rounded-lg select-none", - title: "font-bold !text-md", + title: "!font-bold !text-md", body: "!p-0" } }; diff --git a/app/src/ui/modals/index.tsx b/app/src/ui/modals/index.tsx index 7a69560..3ea2143 100644 --- a/app/src/ui/modals/index.tsx +++ b/app/src/ui/modals/index.tsx @@ -1,7 +1,8 @@ import type { ModalProps } from "@mantine/core"; -import { ModalsProvider, modals as mantineModals } from "@mantine/modals"; +import { modals as $modals, ModalsProvider, closeModal, openContextModal } from "@mantine/modals"; import { transformObject } from "core/utils"; import type { ComponentProps } from "react"; +import { OverlayModal } from "ui/modals/debug/OverlayModal"; import { DebugModal } from "./debug/DebugModal"; import { SchemaFormModal } from "./debug/SchemaFormModal"; import { TestModal } from "./debug/TestModal"; @@ -9,7 +10,8 @@ import { TestModal } from "./debug/TestModal"; const modals = { test: TestModal, debug: DebugModal, - form: SchemaFormModal + form: SchemaFormModal, + overlay: OverlayModal }; declare module "@mantine/modals" { @@ -33,17 +35,22 @@ function open( ) { const title = _title ?? modals[modal].defaultTitle ?? undefined; const cmpModalProps = modals[modal].modalProps ?? {}; - return mantineModals.openContextModal({ + const props = { title, ...modalProps, ...cmpModalProps, modal, innerProps - }); + }; + openContextModal(props); + return { + close: () => close(modal), + closeAll: $modals.closeAll + }; } function close(modal: Modal) { - return mantineModals.close(modal); + return closeModal(modal); } export const bkndModals = { @@ -53,5 +60,5 @@ export const bkndModals = { >, open, close, - closeAll: mantineModals.closeAll + closeAll: $modals.closeAll }; diff --git a/app/src/ui/modules/auth/hooks/use-create-user-modal.ts b/app/src/ui/modules/auth/hooks/use-create-user-modal.ts new file mode 100644 index 0000000..ce08b6b --- /dev/null +++ b/app/src/ui/modules/auth/hooks/use-create-user-modal.ts @@ -0,0 +1,53 @@ +import { useApi, useInvalidate } from "ui/client"; +import { useBkndAuth } from "ui/client/schema/auth/use-bknd-auth"; +import { routes, useNavigate } from "ui/lib/routes"; +import { bkndModals } from "ui/modals"; + +export function useCreateUserModal() { + const api = useApi(); + const { config } = useBkndAuth(); + const invalidate = useInvalidate(); + const [navigate] = useNavigate(); + + const open = async () => { + const loading = bkndModals.open("overlay", { + content: "Loading..." + }); + + const schema = await api.auth.actionSchema("password", "create"); + loading.closeAll(); // currently can't close by id... + + bkndModals.open( + "form", + { + schema, + uiSchema: { + password: { + "ui:widget": "password" + } + }, + autoCloseAfterSubmit: false, + onSubmit: async (data, ctx) => { + console.log("submitted:", data, ctx); + const res = await api.auth.action("password", "create", data); + console.log(res); + if (res.ok) { + // invalidate all data + invalidate(); + navigate(routes.data.entity.edit(config.entity_name, res.data.id)); + ctx.close(); + } else if ("error" in res) { + ctx.setError(res.error); + } else { + ctx.setError("Unknown error"); + } + } + }, + { + title: "Create User" + } + ); + }; + + return { open }; +} diff --git a/app/src/ui/modules/data/components/EntityTable2.tsx b/app/src/ui/modules/data/components/EntityTable2.tsx index 05cbcb4..b9f516c 100644 --- a/app/src/ui/modules/data/components/EntityTable2.tsx +++ b/app/src/ui/modules/data/components/EntityTable2.tsx @@ -34,7 +34,11 @@ export function EntityTable2({ entity, select, ...props }: EntityTableProps) { const field = getField(property)!; _value = field.getValue(value, "table"); } catch (e) { - console.warn("Couldn't render value", { value, property, entity, select, ...props }, e); + console.warn( + "Couldn't render value", + { value, property, entity, select, columns, ...props }, + e + ); } return ; diff --git a/app/src/ui/routes/auth/auth.roles.edit.$role.tsx b/app/src/ui/routes/auth/auth.roles.edit.$role.tsx index 6500bc3..cb7de81 100644 --- a/app/src/ui/routes/auth/auth.roles.edit.$role.tsx +++ b/app/src/ui/routes/auth/auth.roles.edit.$role.tsx @@ -28,14 +28,9 @@ function AuthRolesEditInternal({ params }) { if (!formRef.current?.isValid()) return; const data = formRef.current?.getData(); const success = await actions.roles.patch(roleName, data); - - /*notifications.show({ - id: `role-${roleName}-update`, - position: "top-right", - title: success ? "Update success" : "Update failed", - message: success ? "Role updated successfully" : "Failed to update role", - color: !success ? "red" : undefined - });*/ + if (success) { + navigate(routes.auth.roles.list()); + } } async function handleDelete() { diff --git a/app/src/ui/routes/auth/auth.roles.tsx b/app/src/ui/routes/auth/auth.roles.tsx index c240fe0..616856a 100644 --- a/app/src/ui/routes/auth/auth.roles.tsx +++ b/app/src/ui/routes/auth/auth.roles.tsx @@ -90,9 +90,16 @@ const renderValue = ({ value, property }) => { } if (property === "permissions") { + const max = 3; + let permissions = value || []; + const count = permissions.length; + if (count > max) { + permissions = [...permissions.slice(0, max), `+${count - max}`]; + } + return (
- {[...(value || [])].map((p, i) => ( + {permissions.map((p, i) => ( ; + } + const entityId = Number.parseInt(params.id as string); const [error, setError] = useState(null); const [navigate] = useNavigate(); @@ -36,7 +41,8 @@ export function DataEntityUpdate({ params }) { with: local_relation_refs }, { - revalidateOnFocus: false + revalidateOnFocus: false, + shouldRetryOnError: false } ); @@ -81,6 +87,14 @@ export function DataEntityUpdate({ params }) { onSubmitted }); + if (!data && !$q.isLoading) { + return ( + + ); + } + const makeKey = (key: string | number = "") => `${params.entity.name}_${entityId}_${String(key)}`; diff --git a/app/src/ui/routes/data/data.$entity.create.tsx b/app/src/ui/routes/data/data.$entity.create.tsx index 5b16b64..be37370 100644 --- a/app/src/ui/routes/data/data.$entity.create.tsx +++ b/app/src/ui/routes/data/data.$entity.create.tsx @@ -2,8 +2,9 @@ import { Type } from "core/utils"; import type { EntityData } from "data"; import { useState } from "react"; import { useEntityMutate } from "ui/client"; -import { useBknd } from "ui/client/BkndProvider"; +import { useBkndData } from "ui/client/schema/data/use-bknd-data"; import { Button } from "ui/components/buttons/Button"; +import { Message } from "ui/components/display/Message"; import { useBrowserTitle } from "ui/hooks/use-browser-title"; import { useSearch } from "ui/hooks/use-search"; import * as AppShell from "ui/layouts/AppShell/AppShell"; @@ -13,8 +14,14 @@ import { EntityForm } from "ui/modules/data/components/EntityForm"; import { useEntityForm } from "ui/modules/data/hooks/useEntityForm"; export function DataEntityCreate({ params }) { - const { app } = useBknd(); - const entity = app.entity(params.entity as string)!; + const { $data } = useBkndData(); + const entity = $data.entity(params.entity as string); + if (!entity) { + return ; + } else if (entity.type !== "regular") { + return ; + } + const [error, setError] = useState(null); useBrowserTitle(["Data", entity.label, "Create"]); @@ -43,7 +50,7 @@ export function DataEntityCreate({ params }) { const { Form, handleSubmit } = useEntityForm({ action: "create", - entity, + entity: entity, initialData: search.value, onSubmitted }); diff --git a/app/src/ui/routes/data/data.$entity.index.tsx b/app/src/ui/routes/data/data.$entity.index.tsx index 831e5ff..e92a21f 100644 --- a/app/src/ui/routes/data/data.$entity.index.tsx +++ b/app/src/ui/routes/data/data.$entity.index.tsx @@ -1,7 +1,9 @@ import { Type } from "core/utils"; -import { querySchema } from "data"; +import { type Entity, querySchema } from "data"; +import { Fragment } from "react"; import { TbDots } from "react-icons/tb"; -import { useApiQuery } from "ui/client"; +import { useApi, 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"; import { IconButton } from "ui/components/buttons/IconButton"; @@ -11,6 +13,7 @@ import { useBrowserTitle } from "ui/hooks/use-browser-title"; import { useSearch } from "ui/hooks/use-search"; import * as AppShell from "ui/layouts/AppShell/AppShell"; import { routes, useNavigate } from "ui/lib/routes"; +import { useCreateUserModal } from "ui/modules/auth/hooks/use-create-user-modal"; import { EntityTable2 } from "ui/modules/data/components/EntityTable2"; // @todo: migrate to Typebox @@ -29,7 +32,11 @@ const PER_PAGE_OPTIONS = [5, 10, 25]; export function DataEntityList({ params }) { const { $data } = useBkndData(); - const entity = $data.entity(params.entity as string)!; + const entity = $data.entity(params.entity as string); + if (!entity) { + return ; + } + useBrowserTitle(["Data", entity?.label ?? params.entity]); const [navigate] = useNavigate(); const search = useSearch(searchSchema, { @@ -39,13 +46,14 @@ export function DataEntityList({ params }) { const $q = useApiQuery( (api) => - api.data.readMany(entity.name, { + api.data.readMany(entity?.name as any, { select: search.value.select, limit: search.value.perPage, offset: (search.value.page - 1) * search.value.perPage, sort: search.value.sort }), { + enabled: !!entity, revalidateOnFocus: true, keepPreviousData: true } @@ -75,14 +83,10 @@ export function DataEntityList({ params }) { search.set("perPage", perPage); } - if (!entity) { - return ; - } - const isUpdating = $q.isLoading && $q.isValidating; return ( - <> + @@ -100,14 +104,7 @@ export function DataEntityList({ params }) { > - + } > @@ -140,6 +137,40 @@ export function DataEntityList({ params }) {
- + + ); +} + +function EntityCreateButton({ entity }: { entity: Entity }) { + const b = useBknd(); + const createUserModal = useCreateUserModal(); + + const [navigate] = useNavigate(); + if (!entity) return null; + if (entity.type !== "regular") { + const system = { + users: b.app.config.auth.entity_name, + media: b.app.config.media.entity_name + }; + if (system.users === entity.name) { + return ( + + ); + } + + return null; + } + + return ( + ); } From 1625a0c7c007d97a30d86089160eeb66825a10ed Mon Sep 17 00:00:00 2001 From: dswbx Date: Fri, 17 Jan 2025 11:24:24 +0100 Subject: [PATCH 20/32] made the creation of an entity more accessible and obvious --- .../ui/client/schema/data/use-bknd-data.ts | 13 ++- app/src/ui/components/display/Empty.tsx | 26 ++--- app/src/ui/components/steps/Steps.tsx | 8 +- app/src/ui/modals/index.tsx | 12 +-- .../schema/create-modal/CreateModal.tsx | 99 ++++++++++--------- .../schema/create-modal/step.select.tsx | 3 +- app/src/ui/routes/data/_data.root.tsx | 66 +++++++++---- .../ui/routes/data/data.schema.$entity.tsx | 54 +++++----- app/src/ui/routes/data/data.schema.index.tsx | 21 ++-- app/src/ui/routes/media/_media.root.tsx | 6 +- .../ui/routes/settings/components/Setting.tsx | 5 +- biome.json | 1 + 12 files changed, 176 insertions(+), 138 deletions(-) diff --git a/app/src/ui/client/schema/data/use-bknd-data.ts b/app/src/ui/client/schema/data/use-bknd-data.ts index 36db148..21030a9 100644 --- a/app/src/ui/client/schema/data/use-bknd-data.ts +++ b/app/src/ui/client/schema/data/use-bknd-data.ts @@ -12,6 +12,7 @@ import { } from "data/data-schema"; import { useBknd } from "ui/client/bknd"; import type { TSchemaActions } from "ui/client/schema/actions"; +import { bkndModals } from "ui/modals"; export function useBkndData() { const { config, app, schema, actions: bkndActions } = useBknd(); @@ -62,7 +63,8 @@ export function useBkndData() { } }; const $data = { - entity: (name: string) => entities[name] + entity: (name: string) => entities[name], + modals }; return { @@ -75,6 +77,15 @@ export function useBkndData() { }; } +const modals = { + createAny: () => bkndModals.open(bkndModals.ids.dataCreate, {}), + createEntity: () => + bkndModals.open(bkndModals.ids.dataCreate, { + initialPath: ["entities", "entity"], + initialState: { action: "entity" } + }) +}; + function entityFieldActions(bkndActions: TSchemaActions, entityName: string) { return { add: async (name: string, field: TAppDataField) => { diff --git a/app/src/ui/components/display/Empty.tsx b/app/src/ui/components/display/Empty.tsx index 717b781..9f1291c 100644 --- a/app/src/ui/components/display/Empty.tsx +++ b/app/src/ui/components/display/Empty.tsx @@ -1,33 +1,33 @@ -import { Button } from "../buttons/Button"; +import { twMerge } from "tailwind-merge"; +import { Button, type ButtonProps } from "../buttons/Button"; export type EmptyProps = { Icon?: any; title?: string; description?: string; - buttonText?: string; - buttonOnClick?: () => void; + primary?: ButtonProps; + secondary?: ButtonProps; + className?: string; }; export const Empty: React.FC = ({ Icon = undefined, title = undefined, description = "Check back later my friend.", - buttonText, - buttonOnClick + primary, + secondary, + className }) => ( -
+
{Icon && }
{title &&

{title}

}

{description}

- {buttonText && ( -
- -
- )} +
+ {secondary &&
); diff --git a/app/src/ui/components/steps/Steps.tsx b/app/src/ui/components/steps/Steps.tsx index 0dc02f2..d0ccc47 100644 --- a/app/src/ui/components/steps/Steps.tsx +++ b/app/src/ui/components/steps/Steps.tsx @@ -10,6 +10,7 @@ import { export type TStepsProps = { children: any; initialPath?: string[]; + initialState?: any; lastBack?: () => void; [key: string]: any; }; @@ -19,13 +20,14 @@ type TStepContext = { stepBack: () => void; close: () => void; state: T; + path: string[]; setState: Dispatch>; }; const StepContext = createContext(undefined as any); -export function Steps({ children, initialPath = [], lastBack }: TStepsProps) { - const [state, setState] = useState({}); +export function Steps({ children, initialPath = [], initialState = {}, lastBack }: TStepsProps) { + const [state, setState] = useState(initialState); const [path, setPath] = useState(initialPath); const steps: any[] = Children.toArray(children).filter( (child: any) => child.props.disabled !== true @@ -46,7 +48,7 @@ export function Steps({ children, initialPath = [], lastBack }: TStepsProps) { const current = steps.find((step) => step.props.id === path[path.length - 1]) || steps[0]; return ( - + {current} ); diff --git a/app/src/ui/modals/index.tsx b/app/src/ui/modals/index.tsx index 3ea2143..9869158 100644 --- a/app/src/ui/modals/index.tsx +++ b/app/src/ui/modals/index.tsx @@ -1,8 +1,8 @@ import type { ModalProps } from "@mantine/core"; import { modals as $modals, ModalsProvider, closeModal, openContextModal } from "@mantine/modals"; -import { transformObject } from "core/utils"; import type { ComponentProps } from "react"; import { OverlayModal } from "ui/modals/debug/OverlayModal"; +import { CreateModal } from "ui/modules/data/components/schema/create-modal/CreateModal"; import { DebugModal } from "./debug/DebugModal"; import { SchemaFormModal } from "./debug/SchemaFormModal"; import { TestModal } from "./debug/TestModal"; @@ -11,7 +11,8 @@ const modals = { test: TestModal, debug: DebugModal, form: SchemaFormModal, - overlay: OverlayModal + overlay: OverlayModal, + dataCreate: CreateModal }; declare module "@mantine/modals" { @@ -54,10 +55,9 @@ function close(modal: Modal) { } export const bkndModals = { - ids: transformObject(modals, (key) => key) as unknown as Record< - keyof typeof modals, - keyof typeof modals - >, + ids: Object.fromEntries(Object.keys(modals).map((key) => [key, key])) as { + [K in keyof typeof modals]: K; + }, open, close, closeAll: $modals.closeAll diff --git a/app/src/ui/modules/data/components/schema/create-modal/CreateModal.tsx b/app/src/ui/modules/data/components/schema/create-modal/CreateModal.tsx index 1863d83..791edec 100644 --- a/app/src/ui/modules/data/components/schema/create-modal/CreateModal.tsx +++ b/app/src/ui/modules/data/components/schema/create-modal/CreateModal.tsx @@ -1,15 +1,9 @@ -import { type Static, StringEnum, StringIdentifier, Type, transformObject } from "core/utils"; -import { FieldClassMap } from "data"; +import type { ModalProps } from "@mantine/core"; +import type { ContextModalProps } from "@mantine/modals"; +import { type Static, StringEnum, StringIdentifier, Type } from "core/utils"; import { entitiesSchema, fieldsSchema, relationsSchema } from "data/data-schema"; -import { omit } from "lodash-es"; -import { forwardRef, useState } from "react"; -import { - Modal2, - type Modal2Ref, - ModalBody, - ModalFooter, - ModalTitle -} from "ui/components/modal/Modal2"; +import { useState } from "react"; +import { type Modal2Ref, ModalBody, ModalFooter, ModalTitle } from "ui/components/modal/Modal2"; import { Step, Steps, useStepContext } from "ui/components/steps/Steps"; import { StepCreate } from "ui/modules/data/components/schema/create-modal/step.create"; import { StepEntity } from "./step.entity"; @@ -67,48 +61,59 @@ const createModalSchema = Type.Object( ); export type TCreateModalSchema = Static; -export const CreateModal = forwardRef(function CreateModal(props, ref) { - const [path, setPath] = useState([]); +export function CreateModal({ + context, + id, + innerProps: { initialPath = [], initialState } +}: ContextModalProps<{ initialPath?: string[]; initialState?: TCreateModalSchema }>) { + const [path, setPath] = useState(initialPath); + console.log("...", initialPath, initialState); function close() { - // @ts-ignore - ref?.current?.close(); + context.closeModal(id); } return ( - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - {/* Templates */} - {Templates.map(([Component, meta]) => ( - - - - - ))} - - + {/* Templates */} + {Templates.map(([Component, meta]) => ( + + + + + ))} + ); -}); +} +CreateModal.defaultTitle = undefined; +CreateModal.modalProps = { + withCloseButton: false, + size: "xl", + padding: 0, + classNames: { + root: "bknd-admin" + } +} satisfies Partial; export { ModalBody, ModalFooter, ModalTitle, useStepContext, relationsSchema }; diff --git a/app/src/ui/modules/data/components/schema/create-modal/step.select.tsx b/app/src/ui/modules/data/components/schema/create-modal/step.select.tsx index 30bb3a2..a26c62d 100644 --- a/app/src/ui/modules/data/components/schema/create-modal/step.select.tsx +++ b/app/src/ui/modules/data/components/schema/create-modal/step.select.tsx @@ -12,7 +12,7 @@ import { import Templates from "./templates/register"; export function StepSelect() { - const { nextStep, stepBack, state, setState } = useStepContext(); + const { nextStep, stepBack, state, path, setState } = useStepContext(); const selected = state.action ?? null; function handleSelect(action: TSchemaAction) { @@ -74,6 +74,7 @@ export function StepSelect() { }} prev={{ onClick: stepBack }} prevLabel="Cancel" + debug={{ state, path }} /> ); diff --git a/app/src/ui/routes/data/_data.root.tsx b/app/src/ui/routes/data/_data.root.tsx index d021c54..1ad24fa 100644 --- a/app/src/ui/routes/data/_data.root.tsx +++ b/app/src/ui/routes/data/_data.root.tsx @@ -1,8 +1,10 @@ -import { SegmentedControl } from "@mantine/core"; +import { SegmentedControl, Tooltip } from "@mantine/core"; import { IconDatabase } from "@tabler/icons-react"; import type { Entity, TEntityType } from "data"; +import { TbDatabasePlus } from "react-icons/tb"; import { twMerge } from "tailwind-merge"; -import { useBknd } from "ui/client/bknd"; +import { useBkndData } from "ui/client/schema/data/use-bknd-data"; +import { IconButton } from "ui/components/buttons/IconButton"; import { Empty } from "ui/components/display/Empty"; import { Link } from "ui/components/wouter/Link"; import { useBrowserTitle } from "ui/hooks/use-browser-title"; @@ -11,9 +13,7 @@ import { routes, useNavigate } from "ui/lib/routes"; export function DataRoot({ children }) { // @todo: settings routes should be centralized - const { - app: { entities } - } = useBknd(); + const { entities, $data } = useBkndData(); const entityList: Record = { regular: [], generated: [], @@ -22,7 +22,7 @@ export function DataRoot({ children }) { const [navigate] = useNavigate(); const context = window.location.href.match(/\/schema/) ? "schema" : "data"; - for (const entity of entities) { + for (const entity of Object.values(entities)) { entityList[entity.getType()].push(entity); } @@ -52,14 +52,19 @@ export function DataRoot({ children }) { + <> + + + + + } > Entities @@ -70,7 +75,7 @@ export function DataRoot({ children }) {
*/} - + { - if (entities.length === 0) return null; + context, + suggestCreate = false +}: { entities: Entity[]; title?: string; context: "data" | "schema"; suggestCreate?: boolean }) => { + const { $data } = useBkndData(); + if (entities.length === 0) { + return suggestCreate ? ( + $data.modals.createEntity() + }} + /> + ) : null; + } return (

GdU*?yg__ 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 N{5Gg&3?M6-{tx|0abf@f From 8226b644ae083f8252c74735c8ade96e695f7de7 Mon Sep 17 00:00:00 2001 From: dswbx Date: Thu, 16 Jan 2025 15:45:29 +0100 Subject: [PATCH 13/32] fix double registration of auth middleware on data routes --- app/src/auth/middlewares.ts | 33 ++++++++++++++++-------------- app/src/data/api/DataController.ts | 4 +--- 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/app/src/auth/middlewares.ts b/app/src/auth/middlewares.ts index 50fd2d4..f36c94b 100644 --- a/app/src/auth/middlewares.ts +++ b/app/src/auth/middlewares.ts @@ -26,25 +26,28 @@ export const auth = (options?: { skip?: (string | RegExp)[]; }) => createMiddleware(async (c, next) => { - // make sure to only register once - if (c.get("auth_registered")) { - throw new Error(`auth middleware already registered for ${getPath(c)}`); - } - c.set("auth_registered", true); - const app = c.get("app"); - const skipped = shouldSkip(c, options?.skip) || !app?.module.auth.enabled; const guard = app?.modules.ctx().guard; const authenticator = app?.module.auth.authenticator; - if (!skipped) { - const resolved = c.get("auth_resolved"); - if (!resolved) { - if (!app.module.auth.enabled) { - guard?.setUserContext(undefined); - } else { - guard?.setUserContext(await authenticator?.resolveAuthFromRequest(c)); - c.set("auth_resolved", true); + let skipped = shouldSkip(c, options?.skip) || !app?.module.auth.enabled; + + // make sure to only register once + if (c.get("auth_registered")) { + skipped = true; + console.warn(`auth middleware already registered for ${getPath(c)}`); + } else { + c.set("auth_registered", true); + + if (!skipped) { + const resolved = c.get("auth_resolved"); + if (!resolved) { + if (!app?.module.auth.enabled) { + guard?.setUserContext(undefined); + } else { + guard?.setUserContext(await authenticator?.resolveAuthFromRequest(c)); + c.set("auth_resolved", true); + } } } } diff --git a/app/src/data/api/DataController.ts b/app/src/data/api/DataController.ts index 497ffa9..6735c7a 100644 --- a/app/src/data/api/DataController.ts +++ b/app/src/data/api/DataController.ts @@ -70,7 +70,7 @@ export class DataController extends Controller { override getController() { const { permission, auth } = this.middlewares; - const hono = this.create().use(auth()); + const hono = this.create().use(auth(), permission(SystemPermissions.accessApi)); const definedEntities = this.em.entities.map((e) => e.name); const tbNumber = Type.Transform(Type.String({ pattern: "^[1-9][0-9]{0,}$" })) @@ -85,8 +85,6 @@ export class DataController extends Controller { return func; } - hono.use("*", permission(SystemPermissions.accessApi)); - // info hono.get( "/", From c6cbd362315cb2b1b2ca9e56b65645af9d00097b Mon Sep 17 00:00:00 2001 From: dswbx Date: Thu, 16 Jan 2025 16:21:32 +0100 Subject: [PATCH 14/32] fix RepoQuery typings --- app/__test__/data/data-query-impl.spec.ts | 4 +- app/package.json | 2 +- app/src/core/object/query/query.ts | 2 +- app/src/data/api/DataApi.ts | 8 ++-- app/src/data/entities/Entity.ts | 4 +- app/src/data/entities/query/WhereBuilder.ts | 2 +- app/src/data/index.ts | 1 + app/src/data/server/data-query-impl.ts | 41 +++++++++---------- app/src/ui/client/api/use-entity.ts | 8 ++-- .../ui/elements/media/DropzoneContainer.tsx | 4 +- app/src/ui/routes/data/data.$entity.$id.tsx | 2 +- 11 files changed, 39 insertions(+), 39 deletions(-) diff --git a/app/__test__/data/data-query-impl.spec.ts b/app/__test__/data/data-query-impl.spec.ts index e2cfb29..c03b1fe 100644 --- a/app/__test__/data/data-query-impl.spec.ts +++ b/app/__test__/data/data-query-impl.spec.ts @@ -98,14 +98,14 @@ describe("data-query-impl", () => { test("with", () => { decode({ with: ["posts"] }, { with: { posts: {} } }); decode({ with: { posts: {} } }, { with: { posts: {} } }); - decode({ with: { posts: { limit: "1" } } }, { with: { posts: { limit: 1 } } }); + decode({ with: { posts: { limit: 1 } } }, { with: { posts: { limit: 1 } } }); decode( { with: { posts: { with: { images: { - select: "id" + select: ["id"] } } } diff --git a/app/package.json b/app/package.json index d05eccc..c016cd8 100644 --- a/app/package.json +++ b/app/package.json @@ -3,7 +3,7 @@ "type": "module", "sideEffects": false, "bin": "./dist/cli/index.js", - "version": "0.5.0", + "version": "0.6.0-rc.0", "scripts": { "dev": "vite", "test": "ALL_TESTS=1 bun test --bail", diff --git a/app/src/core/object/query/query.ts b/app/src/core/object/query/query.ts index 07a4c3b..e30979e 100644 --- a/app/src/core/object/query/query.ts +++ b/app/src/core/object/query/query.ts @@ -49,7 +49,7 @@ type LiteralExpressionCondition = { [key: string]: Primitive | ExpressionCondition; }; -const OperandOr = "$or"; +const OperandOr = "$or" as const; type OperandCondition = { [OperandOr]?: LiteralExpressionCondition | ExpressionCondition; }; diff --git a/app/src/data/api/DataApi.ts b/app/src/data/api/DataApi.ts index 47144c2..f2fe4e7 100644 --- a/app/src/data/api/DataApi.ts +++ b/app/src/data/api/DataApi.ts @@ -1,5 +1,5 @@ import type { DB } from "core"; -import type { EntityData, RepoQuery, RepositoryResponse } from "data"; +import type { EntityData, RepoQuery, RepoQueryIn, RepositoryResponse } from "data"; import { type BaseModuleApiOptions, ModuleApi, type PrimaryFieldType } from "modules"; export type DataApiOptions = BaseModuleApiOptions & { @@ -19,14 +19,14 @@ export class DataApi extends ModuleApi { readOne( entity: E, id: PrimaryFieldType, - query: Partial> = {} + query: Omit = {} ) { return this.get, "meta" | "data">>([entity as any, id], query); } readMany( entity: E, - query: Partial = {} + query: RepoQueryIn = {} ) { return this.get, "meta" | "data">>( [entity as any], @@ -38,7 +38,7 @@ export class DataApi extends ModuleApi { E extends keyof DB | string, R extends keyof DB | string, Data = R extends keyof DB ? DB[R] : EntityData - >(entity: E, id: PrimaryFieldType, reference: R, query: Partial = {}) { + >(entity: E, id: PrimaryFieldType, reference: R, query: RepoQueryIn = {}) { return this.get, "meta" | "data">>( [entity as any, id, reference], query ?? this.options.defaultQuery diff --git a/app/src/data/entities/Entity.ts b/app/src/data/entities/Entity.ts index a0bdb29..3365190 100644 --- a/app/src/data/entities/Entity.ts +++ b/app/src/data/entities/Entity.ts @@ -98,8 +98,8 @@ export class Entity< getDefaultSort() { return { - by: this.config.sort_field, - dir: this.config.sort_dir + by: this.config.sort_field ?? "id", + dir: this.config.sort_dir ?? "asc" }; } diff --git a/app/src/data/entities/query/WhereBuilder.ts b/app/src/data/entities/query/WhereBuilder.ts index 5168d0e..3473497 100644 --- a/app/src/data/entities/query/WhereBuilder.ts +++ b/app/src/data/entities/query/WhereBuilder.ts @@ -30,7 +30,7 @@ function key(e: unknown): string { return e as string; } -const expressions: TExpression[] = [ +const expressions = [ exp( "$eq", (v: Primitive) => isPrimitive(v), diff --git a/app/src/data/index.ts b/app/src/data/index.ts index 3a287e6..db70e28 100644 --- a/app/src/data/index.ts +++ b/app/src/data/index.ts @@ -8,6 +8,7 @@ export * from "./prototype"; export { type RepoQuery, + type RepoQueryIn, defaultQuerySchema, querySchema, whereSchema diff --git a/app/src/data/server/data-query-impl.ts b/app/src/data/server/data-query-impl.ts index dcccd64..8abf02e 100644 --- a/app/src/data/server/data-query-impl.ts +++ b/app/src/data/server/data-query-impl.ts @@ -7,7 +7,7 @@ import { Type, Value } from "core/utils"; -import { WhereBuilder } from "../entities"; +import { WhereBuilder, type WhereQuery } from "../entities"; const NumberOrString = (options: SchemaOptions = {}) => Type.Transform(Type.Union([Type.Number(), Type.String()], options)) @@ -15,10 +15,8 @@ const NumberOrString = (options: SchemaOptions = {}) => .Encode(String); const limit = NumberOrString({ default: 10 }); - const offset = NumberOrString({ default: 0 }); -// @todo: allow "id" and "-id" const sort_default = { by: "id", dir: "asc" }; const sort = Type.Transform( Type.Union( @@ -28,20 +26,20 @@ const sort = Type.Transform( } ) ) - .Decode((value) => { + .Decode((value): { by: string; dir: "asc" | "desc" } => { if (typeof value === "string") { if (/^-?[a-zA-Z_][a-zA-Z0-9_.]*$/.test(value)) { const dir = value[0] === "-" ? "desc" : "asc"; - return { by: dir === "desc" ? value.slice(1) : value, dir }; + return { by: dir === "desc" ? value.slice(1) : value, dir } as any; } else if (/^{.*}$/.test(value)) { - return JSON.parse(value); + return JSON.parse(value) as any; } - return sort_default; + return sort_default as any; } - return value; + return value as any; }) - .Encode(JSON.stringify); + .Encode((value) => value); const stringArray = Type.Transform( Type.Union([Type.String(), Type.Array(Type.String())], { default: [] }) @@ -65,25 +63,18 @@ export const whereSchema = Type.Transform( }) .Encode(JSON.stringify); -export type ShallowRepoQuery = { - limit?: number; - offset?: number; - sort?: string | { by: string; dir: "asc" | "desc" }; - select?: string[]; - with?: string[] | Record; - join?: string[]; - where?: any; -}; export type RepoWithSchema = Record< string, - Omit & { + Omit & { with?: unknown; } >; + export const withSchema = (Self: TSelf) => Type.Transform(Type.Union([stringArray, Type.Record(Type.String(), Self)])) .Decode((value) => { - let _value = value; + let _value = typeof value === "string" ? [value] : value; + if (Array.isArray(value)) { if (!value.every((v) => typeof v === "string")) { throw new Error("Invalid 'with' schema"); @@ -121,6 +112,14 @@ export const querySchema = Type.Recursive( { $id: "query-schema" } ); -export type RepoQueryIn = Static; +export type RepoQueryIn = { + limit?: number; + offset?: number; + sort?: string | { by: string; dir: "asc" | "desc" }; + select?: string[]; + with?: string[] | Record; + join?: string[]; + where?: WhereQuery; +}; export type RepoQuery = Required>; export const defaultQuerySchema = Value.Default(querySchema, {}) as RepoQuery; diff --git a/app/src/ui/client/api/use-entity.ts b/app/src/ui/client/api/use-entity.ts index fba6a45..9b721c1 100644 --- a/app/src/ui/client/api/use-entity.ts +++ b/app/src/ui/client/api/use-entity.ts @@ -1,6 +1,6 @@ import type { DB, PrimaryFieldType } from "core"; import { encodeSearch, objectTransform } from "core/utils"; -import type { EntityData, RepoQuery } from "data"; +import type { EntityData, RepoQuery, RepoQueryIn } from "data"; import type { ModuleApi, ResponseObject } from "modules/ModuleApi"; import useSWR, { type SWRConfiguration, mutate } from "swr"; import { type Api, useApi } from "ui/client"; @@ -49,7 +49,7 @@ export const useEntity = < } return res; }, - read: async (query: Partial = {}) => { + read: async (query: RepoQueryIn = {}) => { const res = id ? await api.readOne(entity, id!, query) : await api.readMany(entity, query); if (!res.ok) { throw new UseEntityApiError(res as any, `Failed to read entity "${entity}"`); @@ -88,7 +88,7 @@ export function makeKey( api: ModuleApi, entity: string, id?: PrimaryFieldType, - query?: Partial + query?: RepoQueryIn ) { return ( "/" + @@ -105,7 +105,7 @@ export const useEntityQuery = < >( entity: Entity, id?: Id, - query?: Partial, + query?: RepoQueryIn, options?: SWRConfiguration & { enabled?: boolean; revalidateOnMutate?: boolean } ) => { const api = useApi().data; diff --git a/app/src/ui/elements/media/DropzoneContainer.tsx b/app/src/ui/elements/media/DropzoneContainer.tsx index 87c9933..b9b3a12 100644 --- a/app/src/ui/elements/media/DropzoneContainer.tsx +++ b/app/src/ui/elements/media/DropzoneContainer.tsx @@ -1,4 +1,4 @@ -import type { RepoQuery } from "data"; +import type { RepoQuery, RepoQueryIn } from "data"; import type { MediaFieldSchema } from "media/AppMedia"; import type { TAppMediaConfig } from "media/media-schema"; import { useId } from "react"; @@ -15,7 +15,7 @@ export type DropzoneContainerProps = { id: number; field: string; }; - query?: Partial; + query?: RepoQueryIn; } & Partial> & Partial; diff --git a/app/src/ui/routes/data/data.$entity.$id.tsx b/app/src/ui/routes/data/data.$entity.$id.tsx index a641187..c458814 100644 --- a/app/src/ui/routes/data/data.$entity.$id.tsx +++ b/app/src/ui/routes/data/data.$entity.$id.tsx @@ -234,7 +234,7 @@ function EntityDetailInner({ const other = relation.other(entity); const [navigate] = useNavigate(); - const search: Partial = { + const search = { select: other.entity.getSelect(undefined, "table"), limit: 10, offset: 0 From 69ea5a00eeb502b552ed8cd5540f09ed7b46c871 Mon Sep 17 00:00:00 2001 From: dswbx Date: Thu, 16 Jan 2025 17:18:30 +0100 Subject: [PATCH 15/32] DataApi: automatically switch to POST if the URL is too long --- app/__test__/api/DataApi.spec.ts | 16 ++++++++++++++++ app/__test__/api/ModuleApi.spec.ts | 4 ++++ app/src/data/api/DataApi.ts | 18 +++++++++++++----- 3 files changed, 33 insertions(+), 5 deletions(-) create mode 100644 app/__test__/api/DataApi.spec.ts diff --git a/app/__test__/api/DataApi.spec.ts b/app/__test__/api/DataApi.spec.ts new file mode 100644 index 0000000..4fee5d9 --- /dev/null +++ b/app/__test__/api/DataApi.spec.ts @@ -0,0 +1,16 @@ +import { describe, expect, it } from "bun:test"; +import { DataApi } from "../../src/data/api/DataApi"; + +describe("DataApi", () => { + it("should switch to post for long url reads", async () => { + const api = new DataApi(); + + const get = api.readMany("a".repeat(100), { select: ["id", "name"] }); + expect(get.request.method).toBe("GET"); + expect(new URL(get.request.url).pathname).toBe(`/api/data/${"a".repeat(100)}`); + + const post = api.readMany("a".repeat(1000), { select: ["id", "name"] }); + expect(post.request.method).toBe("POST"); + expect(new URL(post.request.url).pathname).toBe(`/api/data/${"a".repeat(1000)}/query`); + }); +}); diff --git a/app/__test__/api/ModuleApi.spec.ts b/app/__test__/api/ModuleApi.spec.ts index caa42d0..5fb6976 100644 --- a/app/__test__/api/ModuleApi.spec.ts +++ b/app/__test__/api/ModuleApi.spec.ts @@ -28,6 +28,8 @@ describe("ModuleApi", () => { it("fetches endpoint", async () => { const app = new Hono().get("/endpoint", (c) => c.json({ foo: "bar" })); const api = new Api({ host }); + + // @ts-expect-error it's protected api.fetcher = app.request as typeof fetch; const res = await api.get("/endpoint"); @@ -40,6 +42,8 @@ describe("ModuleApi", () => { it("has accessible request", async () => { const app = new Hono().get("/endpoint", (c) => c.json({ foo: "bar" })); const api = new Api({ host }); + + // @ts-expect-error it's protected api.fetcher = app.request as typeof fetch; const promise = api.get("/endpoint"); diff --git a/app/src/data/api/DataApi.ts b/app/src/data/api/DataApi.ts index f2fe4e7..64eb9af 100644 --- a/app/src/data/api/DataApi.ts +++ b/app/src/data/api/DataApi.ts @@ -3,13 +3,15 @@ import type { EntityData, RepoQuery, RepoQueryIn, RepositoryResponse } from "dat import { type BaseModuleApiOptions, ModuleApi, type PrimaryFieldType } from "modules"; export type DataApiOptions = BaseModuleApiOptions & { - defaultQuery?: Partial; + queryLengthLimit: number; + defaultQuery: Partial; }; export class DataApi extends ModuleApi { protected override getDefaultOptions(): Partial { return { basepath: "/api/data", + queryLengthLimit: 1000, defaultQuery: { limit: 10 } @@ -28,10 +30,16 @@ export class DataApi extends ModuleApi { entity: E, query: RepoQueryIn = {} ) { - return this.get, "meta" | "data">>( - [entity as any], - query ?? this.options.defaultQuery - ); + type T = Pick, "meta" | "data">; + + const input = query ?? this.options.defaultQuery; + const exceeds = JSON.stringify([entity, input]).length > this.options.queryLengthLimit; + + if (exceeds) { + return this.post([entity as any, "query"], input); + } + + return this.get([entity as any], input); } readManyByReference< From 8a6d8329f337ae66363f23ac11a822c1ec1a44e8 Mon Sep 17 00:00:00 2001 From: dswbx Date: Thu, 16 Jan 2025 17:25:19 +0100 Subject: [PATCH 16/32] refactor for better precision --- app/__test__/api/DataApi.spec.ts | 4 ++-- app/src/data/api/DataApi.ts | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/__test__/api/DataApi.spec.ts b/app/__test__/api/DataApi.spec.ts index 4fee5d9..706a59d 100644 --- a/app/__test__/api/DataApi.spec.ts +++ b/app/__test__/api/DataApi.spec.ts @@ -5,9 +5,9 @@ describe("DataApi", () => { it("should switch to post for long url reads", async () => { const api = new DataApi(); - const get = api.readMany("a".repeat(100), { select: ["id", "name"] }); + const get = api.readMany("a".repeat(300), { select: ["id", "name"] }); expect(get.request.method).toBe("GET"); - expect(new URL(get.request.url).pathname).toBe(`/api/data/${"a".repeat(100)}`); + expect(new URL(get.request.url).pathname).toBe(`/api/data/${"a".repeat(300)}`); const post = api.readMany("a".repeat(1000), { select: ["id", "name"] }); expect(post.request.method).toBe("POST"); diff --git a/app/src/data/api/DataApi.ts b/app/src/data/api/DataApi.ts index 64eb9af..e444092 100644 --- a/app/src/data/api/DataApi.ts +++ b/app/src/data/api/DataApi.ts @@ -33,13 +33,13 @@ export class DataApi extends ModuleApi { type T = Pick, "meta" | "data">; const input = query ?? this.options.defaultQuery; - const exceeds = JSON.stringify([entity, input]).length > this.options.queryLengthLimit; + const req = this.get([entity as any], input); - if (exceeds) { - return this.post([entity as any, "query"], input); + if (req.request.url.length <= this.options.queryLengthLimit) { + return req; } - return this.get([entity as any], input); + return this.post([entity as any, "query"], input); } readManyByReference< From 2a015ba0a1e600f1a0e1ef531e754060ba2292a0 Mon Sep 17 00:00:00 2001 From: dswbx Date: Fri, 17 Jan 2025 05:02:26 +0100 Subject: [PATCH 17/32] extended dataapi tests --- app/__test__/api/DataApi.spec.ts | 56 ++++++++++++++++++++- app/__test__/data/specs/WithBuilder.spec.ts | 7 +-- app/__test__/helper.ts | 8 ++- app/src/data/api/DataController.ts | 2 +- 4 files changed, 64 insertions(+), 9 deletions(-) diff --git a/app/__test__/api/DataApi.spec.ts b/app/__test__/api/DataApi.spec.ts index 706a59d..dbbe35d 100644 --- a/app/__test__/api/DataApi.spec.ts +++ b/app/__test__/api/DataApi.spec.ts @@ -1,6 +1,16 @@ -import { describe, expect, it } from "bun:test"; +import { afterAll, beforeAll, describe, expect, it } from "bun:test"; +import { Guard } from "../../src/auth"; +import { parse } from "../../src/core/utils"; import { DataApi } from "../../src/data/api/DataApi"; +import { DataController } from "../../src/data/api/DataController"; +import { dataConfigSchema } from "../../src/data/data-schema"; +import * as proto from "../../src/data/prototype"; +import { disableConsoleLog, enableConsoleLog, schemaToEm } from "../helper"; +beforeAll(disableConsoleLog); +afterAll(enableConsoleLog); + +const dataConfig = parse(dataConfigSchema, {}); describe("DataApi", () => { it("should switch to post for long url reads", async () => { const api = new DataApi(); @@ -13,4 +23,48 @@ describe("DataApi", () => { expect(post.request.method).toBe("POST"); expect(new URL(post.request.url).pathname).toBe(`/api/data/${"a".repeat(1000)}/query`); }); + + it("returns result", async () => { + const schema = proto.em({ + posts: proto.entity("posts", { title: proto.text() }) + }); + const em = schemaToEm(schema); + await em.schema().sync({ force: true }); + + const payload = [{ title: "foo" }, { title: "bar" }, { title: "baz" }]; + await em.mutator("posts").insertMany(payload); + + const ctx: any = { em, guard: new Guard() }; + const controller = new DataController(ctx, dataConfig); + const app = controller.getController(); + + { + const res = (await app.request("/posts")) as Response; + const { data } = await res.json(); + expect(data.length).toEqual(3); + } + + // @ts-ignore tests + const api = new DataApi({ basepath: "/", queryLengthLimit: 50 }); + // @ts-ignore protected + api.fetcher = app.request as typeof fetch; + { + const req = api.readMany("posts", { select: ["title"] }); + expect(req.request.method).toBe("GET"); + const res = await req; + expect(res.data).toEqual(payload); + } + + { + const req = api.readMany("posts", { + select: ["title"], + limit: 100000, + offset: 0, + sort: "id" + }); + expect(req.request.method).toBe("POST"); + const res = await req; + expect(res.data).toEqual(payload); + } + }); }); diff --git a/app/__test__/data/specs/WithBuilder.spec.ts b/app/__test__/data/specs/WithBuilder.spec.ts index bed48a6..7b64198 100644 --- a/app/__test__/data/specs/WithBuilder.spec.ts +++ b/app/__test__/data/specs/WithBuilder.spec.ts @@ -10,16 +10,11 @@ import { WithBuilder } from "../../../src/data"; import * as proto from "../../../src/data/prototype"; -import { compileQb, prettyPrintQb } from "../../helper"; +import { compileQb, prettyPrintQb, schemaToEm } from "../../helper"; import { getDummyConnection } from "../helper"; const { dummyConnection } = getDummyConnection(); -function schemaToEm(s: ReturnType<(typeof proto)["em"]>): EntityManager { - const { dummyConnection } = getDummyConnection(); - return new EntityManager(Object.values(s.entities), dummyConnection, s.relations, s.indices); -} - describe("[data] WithBuilder", async () => { test("validate withs", async () => { const schema = proto.em( diff --git a/app/__test__/helper.ts b/app/__test__/helper.ts index de6993e..f07cd34 100644 --- a/app/__test__/helper.ts +++ b/app/__test__/helper.ts @@ -2,7 +2,8 @@ import { unlink } from "node:fs/promises"; import type { SelectQueryBuilder, SqliteDatabase } from "kysely"; import Database from "libsql"; import { format as sqlFormat } from "sql-formatter"; -import { SqliteLocalConnection } from "../src/data"; +import { type Connection, EntityManager, SqliteLocalConnection } from "../src/data"; +import type { em as protoEm } from "../src/data/prototype"; export function getDummyDatabase(memory: boolean = true): { dummyDb: SqliteDatabase; @@ -62,3 +63,8 @@ export function prettyPrintQb(qb: SelectQueryBuilder) { const { sql, parameters } = qb.compile(); console.log("$", sqlFormat(sql), "\n[params]", parameters); } + +export function schemaToEm(s: ReturnType, conn?: Connection): EntityManager { + const connection = conn ? conn : getDummyConnection().dummyConnection; + return new EntityManager(Object.values(s.entities), connection, s.relations, s.indices); +} diff --git a/app/src/data/api/DataController.ts b/app/src/data/api/DataController.ts index 6735c7a..131f3d6 100644 --- a/app/src/data/api/DataController.ts +++ b/app/src/data/api/DataController.ts @@ -281,7 +281,7 @@ export class DataController extends Controller { return c.notFound(); } const options = (await c.req.valid("json")) as RepoQuery; - console.log("options", options); + //console.log("options", options); const result = await this.em.repository(entity).findMany(options); return c.json(this.repoResult(result), { status: result.data ? 200 : 404 }); From d4f647c0db9cfb92177da1bee9b234ea9851fee3 Mon Sep 17 00:00:00 2001 From: dswbx Date: Fri, 17 Jan 2025 05:29:13 +0100 Subject: [PATCH 18/32] add readme to npm publish, updated package.json and README.md --- README.md | 6 +++++- app/package.json | 30 ++++++++++++++++++++++++++++-- 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index b60a344..7f47c83 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,8 @@ -![bknd](docs/_assets/poster.png) +[![npm version](https://img.shields.io/npm/v/bknd.svg)](https://npmjs.org/package/bknd +"View this project on NPM") +[![npm downloads](https://img.shields.io/npm/dm/bknd)](https://www.npmjs.com/package/bknd) + +![bknd](https://raw.githubusercontent.com/bknd-io/bknd/refs/heads/main/docs/_assets/poster.png) bknd simplifies app development by providing fully functional backend for data management, authentication, workflows and media. Since it's lightweight and built on Web Standards, it can diff --git a/app/package.json b/app/package.json index c016cd8..b96ccd8 100644 --- a/app/package.json +++ b/app/package.json @@ -3,7 +3,16 @@ "type": "module", "sideEffects": false, "bin": "./dist/cli/index.js", - "version": "0.6.0-rc.0", + "version": "0.6.0-rc.3", + "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": { + "type": "git", + "url": "https://github.com/bknd-io/bknd.git" + }, + "bugs": { + "url": "https://github.com/bknd-io/bknd/issues" + }, "scripts": { "dev": "vite", "test": "ALL_TESTS=1 bun test --bail", @@ -18,7 +27,8 @@ "build:types": "tsc --emitDeclarationOnly && tsc-alias", "updater": "bun x npm-check-updates -ui", "cli": "LOCAL=1 bun src/cli/index.ts", - "prepublishOnly": "bun run types && bun run test && bun run build:all" + "prepublishOnly": "bun run types && bun run test && bun run build:all && cp ../README.md ./", + "postpublish": "rm -f README.md" }, "license": "FSL-1.1-MIT", "dependencies": { @@ -186,5 +196,21 @@ "!dist/**/*.map", "!dist/metafile*", "!dist/**/metafile*" + ], + "keywords": [ + "api", + "backend", + "database", + "authentication", + "jwt", + "workflows", + "media", + "serverless", + "cloudflare", + "nextjs", + "remix", + "astro", + "bun", + "node" ] } From b61634e261b82a5a2d5f4e391346bd23d1a5e034 Mon Sep 17 00:00:00 2001 From: dswbx Date: Fri, 17 Jan 2025 10:19:26 +0100 Subject: [PATCH 19/32] introduced auth strategy actions to allow user creation in UI --- app/src/auth/AppAuth.ts | 21 ++--- app/src/auth/api/AuthApi.ts | 33 +++++++- app/src/auth/api/AuthController.ts | 78 ++++++++++++++++++- app/src/auth/auth-permissions.ts | 4 + app/src/auth/authenticate/Authenticator.ts | 39 +++++++++- .../strategies/PasswordStrategy.ts | 34 +++++--- app/src/auth/index.ts | 2 + app/src/modules/Module.ts | 22 +++++- app/src/modules/ModuleApi.ts | 7 ++ app/src/ui/client/api/use-api.ts | 17 ++-- app/src/ui/client/api/use-entity.ts | 9 --- app/src/ui/components/display/Message.tsx | 4 +- .../form/json-schema/JsonSchemaForm.tsx | 8 +- app/src/ui/modals/debug/OverlayModal.tsx | 22 ++++++ app/src/ui/modals/debug/SchemaFormModal.tsx | 74 ++++++++++++------ app/src/ui/modals/index.tsx | 19 +++-- .../auth/hooks/use-create-user-modal.ts | 53 +++++++++++++ .../modules/data/components/EntityTable2.tsx | 6 +- .../ui/routes/auth/auth.roles.edit.$role.tsx | 11 +-- app/src/ui/routes/auth/auth.roles.tsx | 9 ++- app/src/ui/routes/data/data.$entity.$id.tsx | 18 ++++- .../ui/routes/data/data.$entity.create.tsx | 15 +++- app/src/ui/routes/data/data.$entity.index.tsx | 67 +++++++++++----- 23 files changed, 464 insertions(+), 108 deletions(-) create mode 100644 app/src/auth/auth-permissions.ts create mode 100644 app/src/ui/modals/debug/OverlayModal.tsx create mode 100644 app/src/ui/modules/auth/hooks/use-create-user-modal.ts diff --git a/app/src/auth/AppAuth.ts b/app/src/auth/AppAuth.ts index dfbe1f3..14a0dea 100644 --- a/app/src/auth/AppAuth.ts +++ b/app/src/auth/AppAuth.ts @@ -1,11 +1,16 @@ -import { type AuthAction, Authenticator, type ProfileExchange, Role, type Strategy } from "auth"; +import { + type AuthAction, + AuthPermissions, + Authenticator, + type ProfileExchange, + Role, + type Strategy +} from "auth"; import type { PasswordStrategy } from "auth/authenticate/strategies"; -import { auth } from "auth/middlewares"; import { type DB, Exception, type PrimaryFieldType } from "core"; import { type Static, secureRandomString, transformObject } from "core/utils"; -import { type Entity, EntityIndex, type EntityManager } from "data"; +import type { Entity, EntityManager } from "data"; import { type FieldSchema, em, entity, enumm, make, text } from "data/prototype"; -import type { Hono } from "hono"; import { pick } from "lodash-es"; import { Module } from "modules/Module"; import { AuthController } from "./api/AuthController"; @@ -79,8 +84,8 @@ export class AppAuth extends Module { super.setBuilt(); this._controller = new AuthController(this); - //this.ctx.server.use(controller.getMiddleware); this.ctx.server.route(this.config.basepath, this._controller.getController()); + this.ctx.guard.registerPermissions(Object.values(AuthPermissions)); } get controller(): AuthController { @@ -260,14 +265,12 @@ export class AppAuth extends Module { try { const roles = Object.keys(this.config.roles ?? {}); - const field = make("role", enumm({ enum: roles })); - users.__replaceField("role", field); + this.replaceEntityField(users, "role", enumm({ enum: roles })); } catch (e) {} try { const strategies = Object.keys(this.config.strategies ?? {}); - const field = make("strategy", enumm({ enum: strategies })); - users.__replaceField("strategy", field); + this.replaceEntityField(users, "strategy", enumm({ enum: strategies })); } catch (e) {} } diff --git a/app/src/auth/api/AuthApi.ts b/app/src/auth/api/AuthApi.ts index 7b43d6d..869103c 100644 --- a/app/src/auth/api/AuthApi.ts +++ b/app/src/auth/api/AuthApi.ts @@ -1,3 +1,4 @@ +import type { AuthActionResponse } from "auth/api/AuthController"; import type { AppAuthSchema, AppAuthStrategies } from "auth/auth-schema"; import type { AuthResponse, SafeUser, Strategy } from "auth/authenticate/Authenticator"; import { type BaseModuleApiOptions, ModuleApi } from "modules/ModuleApi"; @@ -13,22 +14,46 @@ export class AuthApi extends ModuleApi { }; } - async loginWithPassword(input: any) { - const res = await this.post(["password", "login"], input); + async login(strategy: string, input: any) { + const res = await this.post([strategy, "login"], input); if (res.ok && res.body.token) { await this.options.onTokenUpdate?.(res.body.token); } return res; } - async registerWithPassword(input: any) { - const res = await this.post(["password", "register"], input); + async register(strategy: string, input: any) { + const res = await this.post([strategy, "register"], input); if (res.ok && res.body.token) { await this.options.onTokenUpdate?.(res.body.token); } return res; } + async actionSchema(strategy: string, action: string) { + return this.get([strategy, "actions", action, "schema.json"]); + } + + async action(strategy: string, action: string, input: any) { + return this.post([strategy, "actions", action], input); + } + + /** + * @deprecated use login("password", ...) instead + * @param input + */ + async loginWithPassword(input: any) { + return this.login("password", input); + } + + /** + * @deprecated use register("password", ...) instead + * @param input + */ + async registerWithPassword(input: any) { + return this.register("password", input); + } + me() { return this.get<{ user: SafeUser | null }>(["me"]); } diff --git a/app/src/auth/api/AuthController.ts b/app/src/auth/api/AuthController.ts index 553c477..265d8bc 100644 --- a/app/src/auth/api/AuthController.ts +++ b/app/src/auth/api/AuthController.ts @@ -1,5 +1,16 @@ -import type { AppAuth } from "auth"; +import { type AppAuth, AuthPermissions, type SafeUser, type Strategy } from "auth"; +import { TypeInvalidError, parse } from "core/utils"; +import { DataPermissions } from "data"; +import type { Hono } from "hono"; import { Controller } from "modules/Controller"; +import type { ServerEnv } from "modules/Module"; + +export type AuthActionResponse = { + success: boolean; + action: string; + data?: SafeUser; + errors?: any; +}; export class AuthController extends Controller { constructor(private auth: AppAuth) { @@ -10,6 +21,70 @@ export class AuthController extends Controller { return this.auth.ctx.guard; } + private registerStrategyActions(strategy: Strategy, mainHono: Hono) { + const actions = strategy.getActions?.(); + if (!actions) { + return; + } + + const { auth, permission } = this.middlewares; + const hono = this.create().use(auth()); + + const name = strategy.getName(); + const { create, change } = actions; + const em = this.auth.em; + const mutator = em.mutator(this.auth.config.entity_name as "users"); + + if (create) { + hono.post( + "/create", + permission([AuthPermissions.createUser, DataPermissions.entityCreate]), + async (c) => { + try { + const body = await this.auth.authenticator.getBody(c); + const valid = parse(create.schema, body, { + skipMark: true + }); + const processed = (await create.preprocess?.(valid)) ?? valid; + console.log("processed", processed); + + // @todo: check processed for "role" and check permissions + + mutator.__unstable_toggleSystemEntityCreation(false); + const { data: created } = await mutator.insertOne({ + ...processed, + strategy: name + }); + mutator.__unstable_toggleSystemEntityCreation(true); + + return c.json({ + success: true, + action: "create", + strategy: name, + data: created as unknown as SafeUser + } as AuthActionResponse); + } catch (e) { + if (e instanceof TypeInvalidError) { + return c.json( + { + success: false, + errors: e.errors + }, + 400 + ); + } + throw e; + } + } + ); + hono.get("create/schema.json", async (c) => { + return c.json(create.schema); + }); + } + + mainHono.route(`/${name}/actions`, hono); + } + override getController() { const { auth } = this.middlewares; const hono = this.create(); @@ -18,6 +93,7 @@ export class AuthController extends Controller { for (const [name, strategy] of Object.entries(strategies)) { //console.log("registering", name, "at", `/${name}`); hono.route(`/${name}`, strategy.getController(this.auth.authenticator)); + this.registerStrategyActions(strategy, hono); } hono.get("/me", auth(), async (c) => { diff --git a/app/src/auth/auth-permissions.ts b/app/src/auth/auth-permissions.ts new file mode 100644 index 0000000..ed71992 --- /dev/null +++ b/app/src/auth/auth-permissions.ts @@ -0,0 +1,4 @@ +import { Permission } from "core"; + +export const createUser = new Permission("auth.user.create"); +//export const updateUser = new Permission("auth.user.update"); diff --git a/app/src/auth/authenticate/Authenticator.ts b/app/src/auth/authenticate/Authenticator.ts index 0dc479d..7b81ed7 100644 --- a/app/src/auth/authenticate/Authenticator.ts +++ b/app/src/auth/authenticate/Authenticator.ts @@ -1,6 +1,14 @@ -import { Exception } from "core"; +import { type DB, Exception } from "core"; import { addFlashMessage } from "core/server/flash"; -import { type Static, StringEnum, Type, parse, runtimeSupports, transformObject } from "core/utils"; +import { + type Static, + StringEnum, + type TObject, + Type, + parse, + runtimeSupports, + transformObject +} from "core/utils"; import type { Context, Hono } from "hono"; import { deleteCookie, getSignedCookie, setSignedCookie } from "hono/cookie"; import { sign, verify } from "hono/jwt"; @@ -10,6 +18,14 @@ import type { ServerEnv } from "modules/Module"; type Input = any; // workaround export type JWTPayload = Parameters[0]; +export const strategyActions = ["create", "change"] as const; +export type StrategyActionName = (typeof strategyActions)[number]; +export type StrategyAction = { + schema: S; + preprocess: (input: unknown) => Promise>; +}; +export type StrategyActions = Partial>; + // @todo: add schema to interface to ensure proper inference export interface Strategy { getController: (auth: Authenticator) => Hono; @@ -17,6 +33,7 @@ export interface Strategy { getMode: () => "form" | "external"; getName: () => string; toJSON: (secrets?: boolean) => any; + getActions?: () => StrategyActions; } export type User = { @@ -274,6 +291,14 @@ export class Authenticator = Record< return c.req.header("Content-Type") === "application/json"; } + async getBody(c: Context) { + if (this.isJsonRequest(c)) { + return await c.req.json(); + } else { + return Object.fromEntries((await c.req.formData()).entries()); + } + } + private getSuccessPath(c: Context) { const p = (this.config.cookie.pathSuccess ?? "/").replace(/\/+$/, "/"); @@ -338,3 +363,13 @@ export class Authenticator = Record< }; } } + +export function createStrategyAction( + schema: S, + preprocess: (input: Static) => Promise> +) { + return { + schema, + preprocess + } as StrategyAction; +} diff --git a/app/src/auth/authenticate/strategies/PasswordStrategy.ts b/app/src/auth/authenticate/strategies/PasswordStrategy.ts index ef940d7..d8f8a23 100644 --- a/app/src/auth/authenticate/strategies/PasswordStrategy.ts +++ b/app/src/auth/authenticate/strategies/PasswordStrategy.ts @@ -2,6 +2,7 @@ import type { Authenticator, Strategy } from "auth"; import { type Static, StringEnum, Type, parse } from "core/utils"; import { hash } from "core/utils"; import { type Context, Hono } from "hono"; +import { type StrategyAction, type StrategyActions, createStrategyAction } from "../Authenticator"; type LoginSchema = { username: string; password: string } | { email: string; password: string }; type RegisterSchema = { email: string; password: string; [key: string]: any }; @@ -54,17 +55,9 @@ export class PasswordStrategy implements Strategy { getController(authenticator: Authenticator): Hono { const hono = new Hono(); - async function getBody(c: Context) { - if (authenticator.isJsonRequest(c)) { - return await c.req.json(); - } else { - return Object.fromEntries((await c.req.formData()).entries()); - } - } - return hono .post("/login", async (c) => { - const body = await getBody(c); + const body = await authenticator.getBody(c); try { const payload = await this.login(body); @@ -76,7 +69,7 @@ export class PasswordStrategy implements Strategy { } }) .post("/register", async (c) => { - const body = await getBody(c); + const body = await authenticator.getBody(c); const payload = await this.register(body); const data = await authenticator.resolve("register", this, payload.password, payload); @@ -85,6 +78,27 @@ export class PasswordStrategy implements Strategy { }); } + getActions(): StrategyActions { + return { + create: createStrategyAction( + Type.Object({ + email: Type.String({ + pattern: "^[\\w-\\.]+@([\\w-]+\\.)+[\\w-]{2,4}$" + }), + password: Type.String({ + minLength: 8 // @todo: this should be configurable + }) + }), + async ({ password, ...input }) => { + return { + ...input, + strategy_value: await this.hash(password) + }; + } + ) + }; + } + getSchema() { return schema; } diff --git a/app/src/auth/index.ts b/app/src/auth/index.ts index fbb47fb..11c3367 100644 --- a/app/src/auth/index.ts +++ b/app/src/auth/index.ts @@ -19,3 +19,5 @@ export { AppAuth, type UserFieldSchema } from "./AppAuth"; export { Guard, type GuardUserContext, type GuardConfig } from "./authorize/Guard"; export { Role } from "./authorize/Role"; + +export * as AuthPermissions from "./auth-permissions"; diff --git a/app/src/modules/Module.ts b/app/src/modules/Module.ts index 838e964..546db48 100644 --- a/app/src/modules/Module.ts +++ b/app/src/modules/Module.ts @@ -3,7 +3,15 @@ import type { Guard } from "auth"; import { SchemaObject } from "core"; import type { EventManager } from "core/events"; import type { Static, TSchema } from "core/utils"; -import type { Connection, EntityIndex, EntityManager, em as prototypeEm } from "data"; +import { + type Connection, + type EntityIndex, + type EntityManager, + type Field, + FieldPrototype, + make, + type em as prototypeEm +} from "data"; import { Entity } from "data"; import type { Hono } from "hono"; @@ -184,4 +192,16 @@ export abstract class Module { +export const useInvalidate = (options?: { exact?: boolean }) => { const mutate = useSWRConfig().mutate; const api = useApi(); - return async (arg?: string | ((api: Api) => FetchPromise)) => { - if (!arg) return async () => mutate(""); - return mutate(typeof arg === "string" ? arg : arg(api).key()); + return async (arg?: string | ((api: Api) => FetchPromise | ModuleApi)) => { + let key = ""; + if (typeof arg === "string") { + key = arg; + } else if (typeof arg === "function") { + key = arg(api).key(); + } + + if (options?.exact) return mutate(key); + return mutate((k) => typeof k === "string" && k.startsWith(key)); }; }; diff --git a/app/src/ui/client/api/use-entity.ts b/app/src/ui/client/api/use-entity.ts index 9b721c1..85a44bb 100644 --- a/app/src/ui/client/api/use-entity.ts +++ b/app/src/ui/client/api/use-entity.ts @@ -22,15 +22,6 @@ export class UseEntityApiError extends Error { } } -function Test() { - const { read } = useEntity("users"); - async () => { - const data = await read(); - }; - - return null; -} - export const useEntity = < Entity extends keyof DB | string, Id extends PrimaryFieldType | undefined = undefined, diff --git a/app/src/ui/components/display/Message.tsx b/app/src/ui/components/display/Message.tsx index 34069dd..da44346 100644 --- a/app/src/ui/components/display/Message.tsx +++ b/app/src/ui/components/display/Message.tsx @@ -1,7 +1,9 @@ import { Empty, type EmptyProps } from "./Empty"; const NotFound = (props: Partial) => ; +const NotAllowed = (props: Partial) => ; export const Message = { - NotFound + NotFound, + NotAllowed }; diff --git a/app/src/ui/components/form/json-schema/JsonSchemaForm.tsx b/app/src/ui/components/form/json-schema/JsonSchemaForm.tsx index d722dde..8b79f70 100644 --- a/app/src/ui/components/form/json-schema/JsonSchemaForm.tsx +++ b/app/src/ui/components/form/json-schema/JsonSchemaForm.tsx @@ -15,12 +15,13 @@ export type JsonSchemaFormProps = any & { schema: RJSFSchema | Schema; uiSchema?: any; direction?: "horizontal" | "vertical"; - onChange?: (value: any) => void; + onChange?: (value: any, isValid: () => boolean) => void; }; export type JsonSchemaFormRef = { formData: () => any; validateForm: () => boolean; + silentValidate: () => boolean; cancel: () => void; }; @@ -52,15 +53,18 @@ export const JsonSchemaForm = forwardRef const handleChange = ({ formData }: any, e) => { const clean = JSON.parse(JSON.stringify(formData)); //console.log("Data changed: ", clean, JSON.stringify(formData, null, 2)); - onChange?.(clean); setValue(clean); + onChange?.(clean, () => isValid(clean)); }; + const isValid = (data: any) => validator.validateFormData(data, schema).errors.length === 0; + useImperativeHandle( ref, () => ({ formData: () => value, validateForm: () => formRef.current!.validateForm(), + silentValidate: () => isValid(value), cancel: () => formRef.current!.reset() }), [value] diff --git a/app/src/ui/modals/debug/OverlayModal.tsx b/app/src/ui/modals/debug/OverlayModal.tsx new file mode 100644 index 0000000..2dca9a3 --- /dev/null +++ b/app/src/ui/modals/debug/OverlayModal.tsx @@ -0,0 +1,22 @@ +import type { ContextModalProps } from "@mantine/modals"; +import type { ReactNode } from "react"; + +export function OverlayModal({ + context, + id, + innerProps: { content } +}: ContextModalProps<{ content?: ReactNode }>) { + return content; +} + +OverlayModal.defaultTitle = undefined; +OverlayModal.modalProps = { + withCloseButton: false, + classNames: { + size: "md", + root: "bknd-admin", + content: "text-center justify-center", + title: "font-bold !text-md", + body: "py-3 px-5 gap-4 flex flex-col" + } +}; diff --git a/app/src/ui/modals/debug/SchemaFormModal.tsx b/app/src/ui/modals/debug/SchemaFormModal.tsx index 72c1c89..fd9c304 100644 --- a/app/src/ui/modals/debug/SchemaFormModal.tsx +++ b/app/src/ui/modals/debug/SchemaFormModal.tsx @@ -7,21 +7,31 @@ import { } from "ui/components/form/json-schema"; import type { ContextModalProps } from "@mantine/modals"; +import { Alert } from "ui/components/display/Alert"; type Props = JsonSchemaFormProps & { - onSubmit?: (data: any) => void | Promise; + autoCloseAfterSubmit?: boolean; + onSubmit?: ( + data: any, + context: { + close: () => void; + } + ) => void | Promise; }; export function SchemaFormModal({ context, id, - innerProps: { schema, uiSchema, onSubmit } + innerProps: { schema, uiSchema, onSubmit, autoCloseAfterSubmit } }: ContextModalProps) { const [valid, setValid] = useState(false); const formRef = useRef(null); + const [submitting, setSubmitting] = useState(false); + const was_submitted = useRef(false); + const [error, setError] = useState(); - function handleChange(data) { - const valid = formRef.current?.validateForm() ?? false; + function handleChange(data, isValid) { + const valid = isValid(); console.log("Data changed", data, valid); setValid(valid); } @@ -30,29 +40,45 @@ export function SchemaFormModal({ context.closeModal(id); } - async function handleClickAdd() { - await onSubmit?.(formRef.current?.formData()); - handleClose(); + async function handleSubmit() { + was_submitted.current = true; + if (!formRef.current?.validateForm()) { + return; + } + + setSubmitting(true); + await onSubmit?.(formRef.current?.formData(), { + close: handleClose, + setError + }); + setSubmitting(false); + + if (autoCloseAfterSubmit !== false) { + handleClose(); + } } return ( -

diff --git a/app/src/ui/routes/data/data.schema.index.tsx b/app/src/ui/routes/data/data.schema.index.tsx index a9cbe18..8635650 100644 --- a/app/src/ui/routes/data/data.schema.index.tsx +++ b/app/src/ui/routes/data/data.schema.index.tsx @@ -1,10 +1,7 @@ -import { Suspense, lazy, useRef } from "react"; -import { - CreateModal, - type CreateModalRef -} from "ui/modules/data/components/schema/create-modal/CreateModal"; -import { Button } from "../../components/buttons/Button"; -import * as AppShell from "../../layouts/AppShell/AppShell"; +import { Suspense, lazy } from "react"; +import { useBkndData } from "ui/client/schema/data/use-bknd-data"; +import { Button } from "ui/components/buttons/Button"; +import * as AppShell from "ui/layouts/AppShell/AppShell"; const DataSchemaCanvas = lazy(() => import("ui/modules/data/components/canvas/DataSchemaCanvas").then((m) => ({ @@ -13,18 +10,12 @@ const DataSchemaCanvas = lazy(() => ); export function DataSchemaIndex() { - const createModalRef = useRef(null); - + const { $data } = useBkndData(); return ( <> - createModalRef.current?.open()} - > + } diff --git a/app/src/ui/routes/media/_media.root.tsx b/app/src/ui/routes/media/_media.root.tsx index 4b34188..959a636 100644 --- a/app/src/ui/routes/media/_media.root.tsx +++ b/app/src/ui/routes/media/_media.root.tsx @@ -20,8 +20,10 @@ export function MediaRoot({ children }) { Icon={IconPhoto} title="Media not enabled" description="Please enable media in the settings to continue." - buttonText="Manage Settings" - buttonOnClick={() => navigate(app.getSettingsPath(["media"]))} + primary={{ + children: "Manage Settings", + onClick: () => navigate(app.getSettingsPath(["media"])) + }} /> ); } diff --git a/app/src/ui/routes/settings/components/Setting.tsx b/app/src/ui/routes/settings/components/Setting.tsx index 20a852b..d26a877 100644 --- a/app/src/ui/routes/settings/components/Setting.tsx +++ b/app/src/ui/routes/settings/components/Setting.tsx @@ -175,7 +175,10 @@ export function Setting({ goBack() + }} /> ); } diff --git a/biome.json b/biome.json index 37a8584..34a1fb1 100644 --- a/biome.json +++ b/biome.json @@ -52,6 +52,7 @@ "noSwitchDeclarations": "warn" }, "complexity": { + "noUselessFragments": "warn", "noStaticOnlyClass": "off", "noForEach": "off", "useLiteralKeys": "warn", From a723d6f61855bb81051eb83b9fd247eca83d3899 Mon Sep 17 00:00:00 2001 From: dswbx Date: Fri, 17 Jan 2025 13:17:09 +0100 Subject: [PATCH 21/32] added easier access points to modify schema + added relation flip in dialog --- app/src/data/fields/Field.ts | 8 ++- app/src/ui/client/BkndProvider.tsx | 7 +- .../ui/client/schema/data/use-bknd-data.ts | 20 ++++++ app/src/ui/components/buttons/Button.tsx | 12 ++-- .../fields/EntityRelationalFormField.tsx | 13 ++-- .../schema/create-modal/CreateModal.tsx | 1 + .../schema/create-modal/step.create.tsx | 5 +- .../schema/create-modal/step.relation.tsx | 50 ++++++++++---- .../media/template.media.component.tsx | 6 +- app/src/ui/routes/data/data.$entity.$id.tsx | 4 +- app/src/ui/routes/data/data.$entity.index.tsx | 8 +++ .../ui/routes/data/data.schema.$entity.tsx | 65 ++++++++++++++----- app/src/ui/routes/flows_old/_flows.root.tsx | 6 +- 13 files changed, 157 insertions(+), 48 deletions(-) diff --git a/app/src/data/fields/Field.ts b/app/src/data/fields/Field.ts index 5260d61..ffa7f08 100644 --- a/app/src/data/fields/Field.ts +++ b/app/src/data/fields/Field.ts @@ -157,8 +157,12 @@ export abstract class Field< return this.config.virtual ?? false; } - getLabel(): string { - return this.config.label ?? snakeToPascalWithSpaces(this.name); + getLabel(options?: { fallback?: boolean }): string | undefined { + return this.config.label + ? this.config.label + : options?.fallback !== false + ? snakeToPascalWithSpaces(this.name) + : undefined; } getDescription(): string | undefined { diff --git a/app/src/ui/client/BkndProvider.tsx b/app/src/ui/client/BkndProvider.tsx index 4f5293f..cc32221 100644 --- a/app/src/ui/client/BkndProvider.tsx +++ b/app/src/ui/client/BkndProvider.tsx @@ -33,6 +33,7 @@ export function BkndProvider({ useState>(); const [fetched, setFetched] = useState(false); const errorShown = useRef(); + const [local_version, set_local_version] = useState(0); const api = useApi(); async function reloadSchema() { @@ -80,6 +81,7 @@ export function BkndProvider({ setSchema(schema); setWithSecrets(_includeSecrets); setFetched(true); + set_local_version((v) => v + 1); }); } @@ -98,7 +100,10 @@ export function BkndProvider({ const actions = getSchemaActions({ api, setSchema, reloadSchema }); return ( - + {children} ); diff --git a/app/src/ui/client/schema/data/use-bknd-data.ts b/app/src/ui/client/schema/data/use-bknd-data.ts index 21030a9..e5342c6 100644 --- a/app/src/ui/client/schema/data/use-bknd-data.ts +++ b/app/src/ui/client/schema/data/use-bknd-data.ts @@ -83,6 +83,26 @@ const modals = { bkndModals.open(bkndModals.ids.dataCreate, { initialPath: ["entities", "entity"], initialState: { action: "entity" } + }), + createRelation: (rel: { source?: string; target?: string; type?: string }) => + bkndModals.open(bkndModals.ids.dataCreate, { + initialPath: ["entities", "relation"], + initialState: { + action: "relation", + relations: { + create: [rel as any] + } + } + }), + createMedia: (entity?: string) => + bkndModals.open(bkndModals.ids.dataCreate, { + initialPath: ["entities", "template-media"], + initialState: { + action: "template-media", + initial: { + entity + } + } }) }; diff --git a/app/src/ui/components/buttons/Button.tsx b/app/src/ui/components/buttons/Button.tsx index c9df2b6..a9a55e2 100644 --- a/app/src/ui/components/buttons/Button.tsx +++ b/app/src/ui/components/buttons/Button.tsx @@ -4,15 +4,15 @@ import { twMerge } from "tailwind-merge"; import { Link } from "ui/components/wouter/Link"; const sizes = { - small: "px-2 py-1.5 rounded-md gap-1.5 text-sm", - default: "px-3 py-2.5 rounded-md gap-2.5", - large: "px-4 py-3 rounded-md gap-3 text-lg" + small: "px-2 py-1.5 rounded-md gap-1 text-sm", + default: "px-3 py-2.5 rounded-md gap-1.5", + large: "px-4 py-3 rounded-md gap-2.5 text-lg" }; const iconSizes = { - small: 15, - default: 18, - large: 22 + small: 12, + default: 16, + large: 20 }; const styles = { diff --git a/app/src/ui/modules/data/components/fields/EntityRelationalFormField.tsx b/app/src/ui/modules/data/components/fields/EntityRelationalFormField.tsx index 861bc24..1d3788c 100644 --- a/app/src/ui/modules/data/components/fields/EntityRelationalFormField.tsx +++ b/app/src/ui/modules/data/components/fields/EntityRelationalFormField.tsx @@ -9,6 +9,7 @@ import { useBknd } from "ui/client/bknd"; import { Button } from "ui/components/buttons/Button"; import * as Formy from "ui/components/form/Formy"; import { Popover } from "ui/components/overlay/Popover"; +import { Link } from "ui/components/wouter/Link"; import { routes } from "ui/lib/routes"; import { useLocation } from "wouter"; import { EntityTable } from "../EntityTable"; @@ -82,7 +83,9 @@ export function EntityRelationalFormField({ return ( - {field.getLabel()} + + {field.getLabel({ fallback: false }) ?? entity.label} +
- + + + ) : (
- Select -
diff --git a/app/src/ui/modules/data/components/schema/create-modal/CreateModal.tsx b/app/src/ui/modules/data/components/schema/create-modal/CreateModal.tsx index 791edec..e44d04e 100644 --- a/app/src/ui/modules/data/components/schema/create-modal/CreateModal.tsx +++ b/app/src/ui/modules/data/components/schema/create-modal/CreateModal.tsx @@ -39,6 +39,7 @@ export type TFieldCreate = Static; const createModalSchema = Type.Object( { action: schemaAction, + initial: Type.Optional(Type.Any()), entities: Type.Optional( Type.Object({ create: Type.Optional(Type.Array(entitySchema)) diff --git a/app/src/ui/modules/data/components/schema/create-modal/step.create.tsx b/app/src/ui/modules/data/components/schema/create-modal/step.create.tsx index 16cad49..371646e 100644 --- a/app/src/ui/modules/data/components/schema/create-modal/step.create.tsx +++ b/app/src/ui/modules/data/components/schema/create-modal/step.create.tsx @@ -10,6 +10,7 @@ import { ucFirst } from "core/utils"; import { useEffect, useState } from "react"; import { TbCirclesRelation, TbSettings } from "react-icons/tb"; import { twMerge } from "tailwind-merge"; +import { useBknd } from "ui/client/bknd"; import { useBkndData } from "ui/client/schema/data/use-bknd-data"; import { IconButton, type IconType } from "ui/components/buttons/IconButton"; import { JsonViewer } from "ui/components/code/JsonViewer"; @@ -26,6 +27,7 @@ export function StepCreate() { const [states, setStates] = useState<(boolean | string)[]>([]); const [submitting, setSubmitting] = useState(false); const $data = useBkndData(); + const b = useBknd(); const items: ActionItem[] = []; if (state.entities?.create) { @@ -90,7 +92,8 @@ export function StepCreate() { states.every((s) => s === true) ); if (items.length === states.length && states.every((s) => s === true)) { - close(); + b.actions.reload().then(close); + //close(); } else { setSubmitting(false); } diff --git a/app/src/ui/modules/data/components/schema/create-modal/step.relation.tsx b/app/src/ui/modules/data/components/schema/create-modal/step.relation.tsx index 05066a1..f32e4a1 100644 --- a/app/src/ui/modules/data/components/schema/create-modal/step.relation.tsx +++ b/app/src/ui/modules/data/components/schema/create-modal/step.relation.tsx @@ -9,12 +9,15 @@ import { registerCustomTypeboxKinds } from "core/utils"; import { ManyToOneRelation, type RelationType, RelationTypes } from "data"; -import { type ReactNode, useEffect } from "react"; +import { type ReactNode, startTransition, useEffect } from "react"; import { type Control, type FieldValues, type UseFormRegister, useForm } from "react-hook-form"; +import { TbRefresh } from "react-icons/tb"; import { useBknd } from "ui/client/bknd"; +import { Button } from "ui/components/buttons/Button"; import { MantineNumberInput } from "ui/components/form/hook-form-mantine/MantineNumberInput"; import { MantineSelect } from "ui/components/form/hook-form-mantine/MantineSelect"; import { useStepContext } from "ui/components/steps/Steps"; +import { useEvent } from "ui/hooks/use-event"; import { ModalBody, ModalFooter, type TCreateModalSchema } from "./CreateModal"; // @todo: check if this could become an issue @@ -63,7 +66,7 @@ type ComponentCtx = { export function StepRelation() { const { config } = useBknd(); const entities = config.data.entities; - const { nextStep, stepBack, state, setState } = useStepContext(); + const { nextStep, stepBack, state, path, setState } = useStepContext(); const { register, handleSubmit, @@ -93,6 +96,22 @@ export function StepRelation() { } } + const flip = useEvent(() => { + const { source, target } = data; + if (source && target) { + setValue("source", target); + setValue("target", source); + } else { + if (source) { + setValue("target", source); + setValue("source", null as any); + } else { + setValue("source", target); + setValue("target", null as any); + } + } + }); + return ( <>
@@ -109,14 +128,23 @@ export function StepRelation() { disabled: data.target === name }))} /> - setValue("config", {})} - label="Relation Type" - data={Relations.map((r) => ({ value: r.type, label: r.label }))} - allowDeselect={false} - /> +
+ setValue("config", {})} + label="Relation Type" + data={Relations.map((r) => ({ value: r.type, label: r.label }))} + allowDeselect={false} + /> + {data.type && ( +
+ +
+ )} +
diff --git a/app/src/ui/modules/data/components/schema/create-modal/templates/media/template.media.component.tsx b/app/src/ui/modules/data/components/schema/create-modal/templates/media/template.media.component.tsx index 3f5474b..ea4d149 100644 --- a/app/src/ui/modules/data/components/schema/create-modal/templates/media/template.media.component.tsx +++ b/app/src/ui/modules/data/components/schema/create-modal/templates/media/template.media.component.tsx @@ -31,7 +31,7 @@ const schema = Type.Object({ type TCreateModalMediaSchema = Static; export function TemplateMediaComponent() { - const { stepBack, setState, state, nextStep } = useStepContext(); + const { stepBack, setState, state, path, nextStep } = useStepContext(); const { register, handleSubmit, @@ -41,7 +41,7 @@ export function TemplateMediaComponent() { control } = useForm({ resolver: typeboxResolver(schema), - defaultValues: Default(schema, {}) as TCreateModalMediaSchema + defaultValues: Default(schema, state.initial ?? {}) as TCreateModalMediaSchema }); const { config } = useBknd(); @@ -134,7 +134,7 @@ export function TemplateMediaComponent() { prev={{ onClick: stepBack }} - debug={{ state, data }} + debug={{ state, path, data }} /> diff --git a/app/src/ui/routes/data/data.$entity.$id.tsx b/app/src/ui/routes/data/data.$entity.$id.tsx index ec3d569..a914c3d 100644 --- a/app/src/ui/routes/data/data.$entity.$id.tsx +++ b/app/src/ui/routes/data/data.$entity.$id.tsx @@ -41,6 +41,7 @@ export function DataEntityUpdate({ params }) { with: local_relation_refs }, { + keepPreviousData: false, revalidateOnFocus: false, shouldRetryOnError: false } @@ -95,8 +96,7 @@ export function DataEntityUpdate({ params }) { ); } - const makeKey = (key: string | number = "") => - `${params.entity.name}_${entityId}_${String(key)}`; + const makeKey = (key: string | number = "") => `${entity.name}_${entityId}_${String(key)}`; const fieldsDisabled = $q.isLoading || $q.isValidating || Form.state.isSubmitting; diff --git a/app/src/ui/routes/data/data.$entity.index.tsx b/app/src/ui/routes/data/data.$entity.index.tsx index e92a21f..b6862e0 100644 --- a/app/src/ui/routes/data/data.$entity.index.tsx +++ b/app/src/ui/routes/data/data.$entity.index.tsx @@ -94,6 +94,14 @@ export function DataEntityList({ params }) { items={[ { label: "Settings", + onClick: () => navigate(routes.data.schema.entity(entity.name)) + }, + { + label: "Data Schema", + onClick: () => navigate(routes.data.schema.root()) + }, + { + label: "Advanced Settings", onClick: () => navigate(routes.settings.path(["data", "entities", entity.name]), { absolute: true diff --git a/app/src/ui/routes/data/data.schema.$entity.tsx b/app/src/ui/routes/data/data.schema.$entity.tsx index 516ca9a..b477b15 100644 --- a/app/src/ui/routes/data/data.schema.$entity.tsx +++ b/app/src/ui/routes/data/data.schema.$entity.tsx @@ -8,11 +8,12 @@ import { isDebug } from "core"; import type { Entity } from "data"; import { cloneDeep } from "lodash-es"; import { useRef, useState } from "react"; -import { TbDots } from "react-icons/tb"; +import { TbCirclesRelation, TbDots, TbPhoto, TbPlus } from "react-icons/tb"; import { useBkndData } from "ui/client/schema/data/use-bknd-data"; import { Button } from "ui/components/buttons/Button"; import { IconButton } from "ui/components/buttons/IconButton"; import { Empty } from "ui/components/display/Empty"; +import { Message } from "ui/components/display/Message"; import { JsonSchemaForm, type JsonSchemaFormRef } from "ui/components/form/json-schema"; import { Dropdown } from "ui/components/overlay/Dropdown"; import * as AppShell from "ui/layouts/AppShell/AppShell"; @@ -24,7 +25,6 @@ import { EntityFieldsForm, type EntityFieldsFormRef } from "./forms/entity.field export function DataSchemaEntity({ params }) { const { $data } = useBkndData(); const [value, setValue] = useState("fields"); - const fieldsRef = useRef(null); function toggle(value) { return () => setValue(value); @@ -32,25 +32,58 @@ export function DataSchemaEntity({ params }) { const [navigate] = useNavigate(); const entity = $data.entity(params.entity as string)!; + if (!entity) { + return ; + } return ( <> - navigate(routes.settings.path(["data", "entities", entity.name]), { - absolute: true - }) - } - ]} - position="bottom-end" - > - - + <> + + navigate(routes.data.root() + routes.data.entity.list(entity.name), { + absolute: true + }) + }, + { + label: "Advanced Settings", + onClick: () => + navigate(routes.settings.path(["data", "entities", entity.name]), { + absolute: true + }) + } + ]} + position="bottom-end" + > + + + + $data.modals.createRelation({ + target: entity.name, + type: "n:1" + }) + }, + { + icon: TbPhoto, + label: "Add media", + onClick: () => $data.modals.createMedia(entity.name) + } + ]} + position="bottom-end" + > + + + } className="pl-3" > diff --git a/app/src/ui/routes/flows_old/_flows.root.tsx b/app/src/ui/routes/flows_old/_flows.root.tsx index e7bb1c6..15d1e09 100644 --- a/app/src/ui/routes/flows_old/_flows.root.tsx +++ b/app/src/ui/routes/flows_old/_flows.root.tsx @@ -55,8 +55,10 @@ export function FlowsEmpty() { title="No flow selected" description="Please select a flow from the left sidebar or create a new one to continue." - buttonText="Create Flow" - buttonOnClick={() => navigate(app.getSettingsPath(["flows"]))} + primary={{ + children: "Create Flow", + onClick: () => navigate(app.getSettingsPath(["flows"])) + }} /> From c19d3b2d754e242457537a6351771bf7c6f03d0c Mon Sep 17 00:00:00 2001 From: dswbx Date: Fri, 17 Jan 2025 13:43:45 +0100 Subject: [PATCH 22/32] fix entity list unnecessary parameters --- app/src/ui/components/buttons/IconButton.tsx | 6 ++-- app/src/ui/hooks/use-effect.ts | 32 +++++++++++++++++++ app/src/ui/routes/data/data.$entity.index.tsx | 8 ++--- app/src/ui/routes/root.tsx | 7 ++-- app/vite.dev.ts | 13 +++----- 5 files changed, 48 insertions(+), 18 deletions(-) create mode 100644 app/src/ui/hooks/use-effect.ts diff --git a/app/src/ui/components/buttons/IconButton.tsx b/app/src/ui/components/buttons/IconButton.tsx index 30d0263..145b2f8 100644 --- a/app/src/ui/components/buttons/IconButton.tsx +++ b/app/src/ui/components/buttons/IconButton.tsx @@ -10,9 +10,9 @@ export type IconType = const styles = { xs: { className: "p-0.5", size: 13 }, - sm: { className: "p-0.5", size: 16 }, - md: { className: "p-1", size: 20 }, - lg: { className: "p-1.5", size: 24 } + sm: { className: "p-0.5", size: 15 }, + md: { className: "p-1", size: 18 }, + lg: { className: "p-1.5", size: 22 } } as const; interface IconButtonProps extends ComponentPropsWithoutRef<"button"> { diff --git a/app/src/ui/hooks/use-effect.ts b/app/src/ui/hooks/use-effect.ts new file mode 100644 index 0000000..539bc7b --- /dev/null +++ b/app/src/ui/hooks/use-effect.ts @@ -0,0 +1,32 @@ +import { useEffect, useRef } from "react"; + +export function useEffectOnce(effect: () => void | (() => void | undefined), deps: any[]): void { + const hasRunRef = useRef(false); + const savedDepsRef = useRef(deps); + + useEffect(() => { + const depsChanged = !hasRunRef.current || !areDepsEqual(savedDepsRef.current, deps); + + if (depsChanged) { + hasRunRef.current = true; + savedDepsRef.current = deps; + return effect(); + } + }, [deps]); +} + +function areDepsEqual(prevDeps: any[] | undefined, nextDeps: any[]): boolean { + if (prevDeps && prevDeps.length === 0 && nextDeps.length === 0) { + return true; + } + + if (!prevDeps && nextDeps.length === 0) { + return true; + } + + if (!prevDeps || !nextDeps || prevDeps.length !== nextDeps.length) { + return false; + } + + return prevDeps.every((dep, index) => Object.is(dep, nextDeps[index])); +} diff --git a/app/src/ui/routes/data/data.$entity.index.tsx b/app/src/ui/routes/data/data.$entity.index.tsx index b6862e0..d096731 100644 --- a/app/src/ui/routes/data/data.$entity.index.tsx +++ b/app/src/ui/routes/data/data.$entity.index.tsx @@ -40,8 +40,8 @@ export function DataEntityList({ params }) { useBrowserTitle(["Data", entity?.label ?? params.entity]); const [navigate] = useNavigate(); const search = useSearch(searchSchema, { - select: entity?.getSelect(undefined, "table") ?? [], - sort: entity?.getDefaultSort() + select: undefined, + sort: undefined }); const $q = useApiQuery( @@ -50,7 +50,7 @@ export function DataEntityList({ params }) { select: search.value.select, limit: search.value.perPage, offset: (search.value.page - 1) * search.value.perPage, - sort: search.value.sort + sort: `${search.value.sort.dir === "asc" ? "" : "-"}${search.value.sort.by}` }), { enabled: !!entity, @@ -131,7 +131,7 @@ export function DataEntityList({ params }) { { - const { verify } = useAuth(); + const { verify, user } = useAuth(); - useEffect(() => { + useEffectOnce(() => { verify(); - }, []); + }, [user?.id]); return ( diff --git a/app/vite.dev.ts b/app/vite.dev.ts index 244b84d..0d27e5a 100644 --- a/app/vite.dev.ts +++ b/app/vite.dev.ts @@ -7,12 +7,11 @@ import { StorageLocalAdapter } from "./src/media/storage/adapters/StorageLocalAd registries.media.register("local", StorageLocalAdapter); -const run_example: string | boolean = false; -//run_example = "ex-admin-rich"; +const example = import.meta.env.VITE_EXAMPLE; -const credentials = run_example +const credentials = example ? { - url: `file:.configs/${run_example}.db` + url: `file:.configs/${example}.db` //url: ":memory:" } : { @@ -26,10 +25,8 @@ if (!credentials.url) { const connection = new LibsqlConnection(createClient(credentials)); let initialConfig: any = undefined; -if (run_example) { - const { version, ...config } = JSON.parse( - await readFile(`.configs/${run_example}.json`, "utf-8") - ); +if (example) { + const { version, ...config } = JSON.parse(await readFile(`.configs/${example}.json`, "utf-8")); initialConfig = config; } From 9422cc5bb8e82115022c5add6a6eb563a92b77c1 Mon Sep 17 00:00:00 2001 From: dswbx Date: Fri, 17 Jan 2025 13:57:37 +0100 Subject: [PATCH 23/32] fix media initial enable to force reload window --- app/package.json | 2 +- app/src/ui/routes/settings/index.tsx | 17 ++++++++++++++--- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/app/package.json b/app/package.json index b96ccd8..4697eba 100644 --- a/app/package.json +++ b/app/package.json @@ -3,7 +3,7 @@ "type": "module", "sideEffects": false, "bin": "./dist/cli/index.js", - "version": "0.6.0-rc.3", + "version": "0.6.0-rc.5", "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": { diff --git a/app/src/ui/routes/settings/index.tsx b/app/src/ui/routes/settings/index.tsx index 89a17fe..71c5754 100644 --- a/app/src/ui/routes/settings/index.tsx +++ b/app/src/ui/routes/settings/index.tsx @@ -6,7 +6,7 @@ import { Link } from "ui/components/wouter/Link"; import { useBrowserTitle } from "ui/hooks/use-browser-title"; import * as AppShell from "ui/layouts/AppShell/AppShell"; import { Route, Switch } from "wouter"; -import { Setting } from "./components/Setting"; +import { Setting, type SettingProps } from "./components/Setting"; import { AuthSettings } from "./routes/auth.settings"; import { DataSettings } from "./routes/data.settings"; import { FlowsSettings } from "./routes/flows.settings"; @@ -117,13 +117,24 @@ const SettingRoutesRoutes = () => { - + ); }; -const FallbackRoutes = ({ module, schema, config, ...settingProps }) => { +const FallbackRoutes = ({ + module, + schema, + config, + ...settingProps +}: SettingProps & { module: string }) => { const { app } = useBknd(); const basepath = app.getAdminConfig(); const prefix = `~/${basepath}/settings`.replace(/\/+/g, "/"); From a6fd9f0d96ad35554f480066096bbc1af0266827 Mon Sep 17 00:00:00 2001 From: dswbx Date: Fri, 17 Jan 2025 15:46:05 +0100 Subject: [PATCH 24/32] fix system entity registration by re-applying configs to modules --- app/__test__/app/repro.spec.ts | 73 +++++++++++++++++++ app/src/App.ts | 3 - app/src/core/object/SchemaObject.ts | 5 +- app/src/core/utils/typebox/index.ts | 1 + app/src/media/AppMedia.ts | 2 + app/src/modules/ModuleManager.ts | 3 + .../schema/create-modal/step.create.tsx | 9 ++- .../media/template.media.component.tsx | 24 ++++-- .../ui/routes/settings/components/Setting.tsx | 5 +- biome.json | 1 + 10 files changed, 112 insertions(+), 14 deletions(-) create mode 100644 app/__test__/app/repro.spec.ts diff --git a/app/__test__/app/repro.spec.ts b/app/__test__/app/repro.spec.ts new file mode 100644 index 0000000..0d025b9 --- /dev/null +++ b/app/__test__/app/repro.spec.ts @@ -0,0 +1,73 @@ +import { describe, expect, test } from "bun:test"; +import { createApp, registries } from "../../src"; +import * as proto from "../../src/data/prototype"; +import { StorageLocalAdapter } from "../../src/media/storage/adapters/StorageLocalAdapter"; + +describe("repros", async () => { + /** + * steps: + * 1. enable media + * 2. create 'test' entity + * 3. add media to 'test' + * + * There was an issue that AppData had old configs because of system entity "media" + */ + test("registers media entity correctly to relate to it", async () => { + registries.media.register("local", StorageLocalAdapter); + const app = createApp(); + await app.build(); + + { + // 1. enable media + const [, config] = await app.module.media.schema().patch("", { + enabled: true, + adapter: { + type: "local", + config: { + path: "./" + } + } + }); + + expect(config.enabled).toBe(true); + } + + { + // 2. create 'test' entity + await app.module.data.schema().patch( + "entities.test", + proto + .entity("test", { + content: proto.text() + }) + .toJSON() + ); + expect(app.em.entities.map((e) => e.name)).toContain("test"); + } + + { + await app.module.data.schema().patch("entities.test.fields.files", { + type: "media", + config: { + required: false, + fillable: ["update"], + hidden: false, + mime_types: [], + virtual: true, + entity: "test" + } + }); + + expect( + app.module.data.schema().patch("relations.000", { + type: "poly", + source: "test", + target: "media", + config: { mappedBy: "files" } + }) + ).resolves.toBeDefined(); + } + + expect(app.em.entities.map((e) => e.name)).toEqual(["media", "test"]); + }); +}); diff --git a/app/src/App.ts b/app/src/App.ts index 2df3e84..b98fc67 100644 --- a/app/src/App.ts +++ b/app/src/App.ts @@ -1,8 +1,5 @@ import type { CreateUserPayload } from "auth/AppAuth"; -import { auth } from "auth/middlewares"; -import { config } from "core"; import { Event } from "core/events"; -import { patternMatch } from "core/utils"; import { Connection, type LibSqlCredentials, LibsqlConnection } from "data"; import { type InitialModuleConfigs, diff --git a/app/src/core/object/SchemaObject.ts b/app/src/core/object/SchemaObject.ts index aad5b14..7c2e926 100644 --- a/app/src/core/object/SchemaObject.ts +++ b/app/src/core/object/SchemaObject.ts @@ -130,7 +130,10 @@ export class SchemaObject { //console.log("overwritePaths", this.options?.overwritePaths); if (this.options?.overwritePaths) { - const keys = getFullPathKeys(value).map((k) => path + "." + k); + const keys = getFullPathKeys(value).map((k) => { + // only prepend path if given + return path.length > 0 ? path + "." + k : k; + }); const overwritePaths = keys.filter((k) => { return this.options?.overwritePaths?.some((p) => { if (typeof p === "string") { diff --git a/app/src/core/utils/typebox/index.ts b/app/src/core/utils/typebox/index.ts index 2e08d7a..a793e33 100644 --- a/app/src/core/utils/typebox/index.ts +++ b/app/src/core/utils/typebox/index.ts @@ -115,6 +115,7 @@ export function parse( } else if (options?.onError) { options.onError(Errors(schema, data)); } else { + //console.warn("errors", JSON.stringify([...Errors(schema, data)], null, 2)); throw new TypeInvalidError(schema, data); } diff --git a/app/src/media/AppMedia.ts b/app/src/media/AppMedia.ts index c759479..564b008 100644 --- a/app/src/media/AppMedia.ts +++ b/app/src/media/AppMedia.ts @@ -53,6 +53,8 @@ export class AppMedia extends Module { index(media).on(["path"], true).on(["reference"]); }) ); + + this.setBuilt(); } catch (e) { console.error(e); throw new Error( diff --git a/app/src/modules/ModuleManager.ts b/app/src/modules/ModuleManager.ts index d5840c2..26f4d21 100644 --- a/app/src/modules/ModuleManager.ts +++ b/app/src/modules/ModuleManager.ts @@ -329,6 +329,9 @@ export class ModuleManager { } } + // re-apply configs to all modules (important for system entities) + this.setConfigs(configs); + // @todo: cleanup old versions? this.logger.clear(); diff --git a/app/src/ui/modules/data/components/schema/create-modal/step.create.tsx b/app/src/ui/modules/data/components/schema/create-modal/step.create.tsx index 371646e..470d214 100644 --- a/app/src/ui/modules/data/components/schema/create-modal/step.create.tsx +++ b/app/src/ui/modules/data/components/schema/create-modal/step.create.tsx @@ -8,7 +8,6 @@ import { } from "@tabler/icons-react"; import { ucFirst } from "core/utils"; import { useEffect, useState } from "react"; -import { TbCirclesRelation, TbSettings } from "react-icons/tb"; import { twMerge } from "tailwind-merge"; import { useBknd } from "ui/client/bknd"; import { useBkndData } from "ui/client/schema/data/use-bknd-data"; @@ -76,6 +75,10 @@ export function StepCreate() { try { const res = await item.run(); setStates((prev) => [...prev, res]); + if (res !== true) { + // make sure to break out + break; + } } catch (e) { setStates((prev) => [...prev, (e as any).message]); } @@ -147,12 +150,14 @@ const SummaryItem: React.FC = ({ }) => { const [expanded, handlers] = useDisclosure(initialExpanded); const error = typeof state !== "undefined" && state !== true; + const done = state === true; return (
diff --git a/app/src/ui/modules/data/components/schema/create-modal/templates/media/template.media.component.tsx b/app/src/ui/modules/data/components/schema/create-modal/templates/media/template.media.component.tsx index ea4d149..4038880 100644 --- a/app/src/ui/modules/data/components/schema/create-modal/templates/media/template.media.component.tsx +++ b/app/src/ui/modules/data/components/schema/create-modal/templates/media/template.media.component.tsx @@ -9,6 +9,7 @@ import { transformObject } from "core/utils"; import type { MediaFieldConfig } from "media/MediaField"; +import { useEffect, useState } from "react"; import { useForm } from "react-hook-form"; import { useBknd } from "ui/client/bknd"; import { MantineNumberInput } from "ui/components/form/hook-form-mantine/MantineNumberInput"; @@ -35,14 +36,15 @@ export function TemplateMediaComponent() { const { register, handleSubmit, - formState: { isValid }, - setValue, + formState: { isValid, errors }, watch, control } = useForm({ + mode: "onChange", resolver: typeboxResolver(schema), defaultValues: Default(schema, state.initial ?? {}) as TCreateModalMediaSchema }); + const [forbidden, setForbidden] = useState(false); const { config } = useBknd(); const media_enabled = config.media.enabled ?? false; @@ -51,13 +53,16 @@ export function TemplateMediaComponent() { name !== media_entity ? entity : undefined ); const data = watch(); + const forbidden_field_names = Object.keys(config.data.entities?.[data.entity]?.fields ?? {}); + + useEffect(() => { + setForbidden(forbidden_field_names.includes(data.name)); + }, [forbidden_field_names, data.name]); async function handleCreate() { - if (isValid) { - console.log("data", data); + if (isValid && !forbidden) { const { field, relation } = convert(media_entity, data); - console.log("state", { field, relation }); setState((prev) => ({ ...prev, fields: { create: [field] }, @@ -120,6 +125,13 @@ export function TemplateMediaComponent() { data.entity ? data.entity : "the entity" }.`} {...register("name")} + error={ + errors.name?.message + ? errors.name?.message + : forbidden + ? `Property "${data.name}" already exists on entity ${data.entity}` + : undefined + } />
{/*

step template media

@@ -129,7 +141,7 @@ export function TemplateMediaComponent() { ({ console.log("save:success", success); if (success) { if (options?.reloadOnSave) { - window.location.reload(); - //await actions.reload(); + //window.location.reload(); + await actions.reload(); + setSubmitting(false); } } else { setSubmitting(false); diff --git a/biome.json b/biome.json index 34a1fb1..3274f11 100644 --- a/biome.json +++ b/biome.json @@ -40,6 +40,7 @@ }, "linter": { "enabled": true, + "ignore": ["**/*.spec.ts"], "rules": { "recommended": true, "a11y": { From baab70f9da47582ff0cbd2a01834a3146bd54a4e Mon Sep 17 00:00:00 2001 From: dswbx Date: Fri, 17 Jan 2025 17:02:22 +0100 Subject: [PATCH 25/32] fix stackblitz issue uploading media to entity --- app/package.json | 2 +- app/src/media/utils/index.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/package.json b/app/package.json index 4697eba..6b2a9db 100644 --- a/app/package.json +++ b/app/package.json @@ -3,7 +3,7 @@ "type": "module", "sideEffects": false, "bin": "./dist/cli/index.js", - "version": "0.6.0-rc.5", + "version": "0.6.0-rc.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": { diff --git a/app/src/media/utils/index.ts b/app/src/media/utils/index.ts index 7a02cf8..a560c88 100644 --- a/app/src/media/utils/index.ts +++ b/app/src/media/utils/index.ts @@ -10,7 +10,7 @@ export function getExtension(filename: string): string | undefined { export function getRandomizedFilename(file: File, length?: number): string; export function getRandomizedFilename(file: string, length?: number): string; export function getRandomizedFilename(file: File | string, length = 16): string { - const filename = file instanceof File ? file.name : file; + const filename = typeof file === "string" ? file : file.name; if (typeof filename !== "string") { console.error("Couldn't extract filename from", file); From 7ddcfc89b447e318329f967a1dd6fcc593f5a0c6 Mon Sep 17 00:00:00 2001 From: dswbx Date: Fri, 17 Jan 2025 18:08:23 +0100 Subject: [PATCH 26/32] fixed auth strategy toggle, updated astro/remix --- app/build.ts | 2 +- app/src/Api.ts | 12 ++++---- app/src/auth/AppAuth.ts | 12 ++++++-- app/src/auth/api/AuthController.ts | 6 ++-- app/src/ui/styles.css | 1 + app/vite.dev.ts | 30 ++++++++++--------- .../astro/src/pages/admin/[...admin].astro | 1 + examples/astro/src/pages/ssr.astro | 2 ++ examples/remix/app/routes/_index.tsx | 4 +-- 9 files changed, 42 insertions(+), 28 deletions(-) diff --git a/app/build.ts b/app/build.ts index 5931a7a..2e0e6c2 100644 --- a/app/build.ts +++ b/app/build.ts @@ -15,7 +15,7 @@ if (clean) { let types_running = false; function buildTypes() { - if (types_running) return; + if (types_running || !types) return; types_running = true; Bun.spawn(["bun", "build:types"], { diff --git a/app/src/Api.ts b/app/src/Api.ts index 5e288fe..835ff14 100644 --- a/app/src/Api.ts +++ b/app/src/Api.ts @@ -128,15 +128,17 @@ export class Api { }; } - async getVerifiedAuthState(force?: boolean): Promise { - if (force === true || !this.verified) { - await this.verifyAuth(); - } - + async getVerifiedAuthState(): Promise { + await this.verifyAuth(); return this.getAuthState(); } async verifyAuth() { + if (!this.token) { + this.markAuthVerified(false); + return; + } + try { const res = await this.auth.me(); if (!res.ok || !res.body.user) { diff --git a/app/src/auth/AppAuth.ts b/app/src/auth/AppAuth.ts index 14a0dea..20386a5 100644 --- a/app/src/auth/AppAuth.ts +++ b/app/src/auth/AppAuth.ts @@ -226,8 +226,16 @@ export class AppAuth extends Module { private toggleStrategyValueVisibility(visible: boolean) { const field = this.getUsersEntity().field("strategy_value")!; - field.config.hidden = !visible; - field.config.fillable = visible; + if (visible) { + field.config.hidden = false; + field.config.fillable = true; + } else { + // reset to normal + const template = AppAuth.usersFields.strategy_value.config; + field.config.hidden = template.hidden; + field.config.fillable = template.fillable; + } + // @todo: think about a PasswordField that automatically hashes on save? } diff --git a/app/src/auth/api/AuthController.ts b/app/src/auth/api/AuthController.ts index 265d8bc..82b50e1 100644 --- a/app/src/auth/api/AuthController.ts +++ b/app/src/auth/api/AuthController.ts @@ -33,7 +33,6 @@ export class AuthController extends Controller { const name = strategy.getName(); const { create, change } = actions; const em = this.auth.em; - const mutator = em.mutator(this.auth.config.entity_name as "users"); if (create) { hono.post( @@ -46,10 +45,9 @@ export class AuthController extends Controller { skipMark: true }); const processed = (await create.preprocess?.(valid)) ?? valid; - console.log("processed", processed); // @todo: check processed for "role" and check permissions - + const mutator = em.mutator(this.auth.config.entity_name as "users"); mutator.__unstable_toggleSystemEntityCreation(false); const { data: created } = await mutator.insertOne({ ...processed, @@ -98,7 +96,7 @@ export class AuthController extends Controller { hono.get("/me", auth(), async (c) => { if (this.auth.authenticator.isUserLoggedIn()) { - return c.json({ user: await this.auth.authenticator.getUser() }); + return c.json({ user: this.auth.authenticator.getUser() }); } return c.json({ user: null }, 403); diff --git a/app/src/ui/styles.css b/app/src/ui/styles.css index 7c2d1d4..9899752 100644 --- a/app/src/ui/styles.css +++ b/app/src/ui/styles.css @@ -1,3 +1,4 @@ +@import "./main.css"; @import "./components/form/json-schema/styles.css"; @import "@xyflow/react/dist/style.css"; @import "@mantine/core/styles.css"; diff --git a/app/vite.dev.ts b/app/vite.dev.ts index 0d27e5a..2f40dc5 100644 --- a/app/vite.dev.ts +++ b/app/vite.dev.ts @@ -1,8 +1,6 @@ import { readFile } from "node:fs/promises"; import { serveStatic } from "@hono/node-server/serve-static"; -import { createClient } from "@libsql/client/node"; import { App, registries } from "./src"; -import { LibsqlConnection } from "./src/data"; import { StorageLocalAdapter } from "./src/media/storage/adapters/StorageLocalAdapter"; registries.media.register("local", StorageLocalAdapter); @@ -12,17 +10,15 @@ const example = import.meta.env.VITE_EXAMPLE; const credentials = example ? { url: `file:.configs/${example}.db` - //url: ":memory:" } - : { - url: import.meta.env.VITE_DB_URL!, - authToken: import.meta.env.VITE_DB_TOKEN! - }; -if (!credentials.url) { - throw new Error("Missing VITE_DB_URL env variable. Add it to .env file"); -} - -const connection = new LibsqlConnection(createClient(credentials)); + : import.meta.env.VITE_DB_URL + ? { + url: import.meta.env.VITE_DB_URL!, + authToken: import.meta.env.VITE_DB_TOKEN! + } + : { + url: ":memory:" + }; let initialConfig: any = undefined; if (example) { @@ -31,11 +27,17 @@ if (example) { } let app: App; -const recreate = true; +const recreate = import.meta.env.VITE_APP_DISABLE_FRESH !== "1"; export default { async fetch(request: Request) { if (!app || recreate) { - app = App.create({ connection, initialConfig }); + app = App.create({ + connection: { + type: "libsql", + config: credentials + }, + initialConfig + }); app.emgr.onEvent( App.Events.AppBuiltEvent, async () => { diff --git a/examples/astro/src/pages/admin/[...admin].astro b/examples/astro/src/pages/admin/[...admin].astro index 30561b2..612b801 100644 --- a/examples/astro/src/pages/admin/[...admin].astro +++ b/examples/astro/src/pages/admin/[...admin].astro @@ -5,6 +5,7 @@ import "bknd/dist/styles.css"; import { getApi } from "bknd/adapter/astro"; const api = getApi(Astro, { mode: "dynamic" }); +await api.verifyAuth(); const user = api.getUser(); export const prerender = false; diff --git a/examples/astro/src/pages/ssr.astro b/examples/astro/src/pages/ssr.astro index eb3a8aa..726076d 100644 --- a/examples/astro/src/pages/ssr.astro +++ b/examples/astro/src/pages/ssr.astro @@ -3,6 +3,8 @@ import { getApi } from "bknd/adapter/astro"; import Card from "../components/Card.astro"; import Layout from "../layouts/Layout.astro"; const api = getApi(Astro, { mode: "dynamic" }); +await api.verifyAuth(); + const { data } = await api.data.readMany("todos"); const user = api.getUser(); diff --git a/examples/remix/app/routes/_index.tsx b/examples/remix/app/routes/_index.tsx index 5b419f1..eef795d 100644 --- a/examples/remix/app/routes/_index.tsx +++ b/examples/remix/app/routes/_index.tsx @@ -7,9 +7,9 @@ export const meta: MetaFunction = () => { export const loader = async (args: LoaderFunctionArgs) => { const api = args.context.api; - const user = (await api.getVerifiedAuthState(true)).user; + await api.verifyAuth(); const { data } = await api.data.readMany("todos"); - return { data, user }; + return { data, user: api.getUser() }; }; export default function Index() { From 89b29256cf6036887b43db3da4058248af73bcd8 Mon Sep 17 00:00:00 2001 From: dswbx Date: Sat, 18 Jan 2025 09:05:35 +0100 Subject: [PATCH 27/32] adding context menu to entities list --- .../ui/client/schema/data/use-bknd-data.ts | 7 +- app/src/ui/components/overlay/Dropdown.tsx | 43 ++- app/src/ui/layouts/AppShell/AppShell.tsx | 2 +- app/src/ui/lib/routes.ts | 16 +- .../modules/data/components/fields-specs.ts | 2 +- app/src/ui/routes/data/_data.root.tsx | 94 +++++- .../ui/routes/data/data.schema.$entity.tsx | 44 ++- .../routes/data/forms/entity.fields.form.tsx | 287 ++++++++++-------- 8 files changed, 339 insertions(+), 156 deletions(-) diff --git a/app/src/ui/client/schema/data/use-bknd-data.ts b/app/src/ui/client/schema/data/use-bknd-data.ts index e5342c6..0886fed 100644 --- a/app/src/ui/client/schema/data/use-bknd-data.ts +++ b/app/src/ui/client/schema/data/use-bknd-data.ts @@ -64,7 +64,12 @@ export function useBkndData() { }; const $data = { entity: (name: string) => entities[name], - modals + modals, + system: (name: string) => ({ + any: entities[name]?.type === "system", + users: name === config.auth.entity_name, + media: name === config.media.entity_name + }) }; return { diff --git a/app/src/ui/components/overlay/Dropdown.tsx b/app/src/ui/components/overlay/Dropdown.tsx index f6616e9..8081e1c 100644 --- a/app/src/ui/components/overlay/Dropdown.tsx +++ b/app/src/ui/components/overlay/Dropdown.tsx @@ -1,5 +1,11 @@ import { useClickOutside } from "@mantine/hooks"; -import { Fragment, type ReactElement, cloneElement, useState } from "react"; +import { + type ComponentPropsWithoutRef, + Fragment, + type ReactElement, + cloneElement, + useState +} from "react"; import { twMerge } from "tailwind-merge"; import { useEvent } from "../../hooks/use-event"; @@ -14,26 +20,33 @@ export type DropdownItem = [key: string]: any; }; +export type DropdownClickableChild = ReactElement<{ onClick: () => void }>; export type DropdownProps = { className?: string; + openEvent?: "onClick" | "onContextMenu"; defaultOpen?: boolean; + title?: string | ReactElement; + dropdownWrapperProps?: Omit, "style">; position?: "bottom-start" | "bottom-end" | "top-start" | "top-end"; hideOnEmpty?: boolean; items: (DropdownItem | undefined | boolean)[]; itemsClassName?: string; - children: ReactElement<{ onClick: () => void }>; + children: DropdownClickableChild; onClickItem?: (item: DropdownItem) => void; renderItem?: ( item: DropdownItem, props: { key: number; onClick: () => void } - ) => ReactElement<{ onClick: () => void }>; + ) => DropdownClickableChild; }; export function Dropdown({ children, defaultOpen = false, + openEvent = "onClick", position = "bottom-start", + dropdownWrapperProps, items, + title, hideOnEmpty = true, onClickItem, renderItem, @@ -48,6 +61,11 @@ export function Dropdown({ setTimeout(() => setOpen((prev) => !prev), typeof delay === "number" ? delay : 0) ); + const openEventHandler = useEvent((e) => { + e.preventDefault(); + toggle(); + }); + const offset = 4; const dropdownStyle = { "bottom-start": { top: "100%", left: 0, marginTop: offset }, @@ -94,13 +112,26 @@ export function Dropdown({ )); return ( -
- {cloneElement(children as any, { onClick: toggle })} +
+ {cloneElement( + children as any, + openEvent === "onClick" ? { onClick: openEventHandler } : {} + )} {open && (
+ {title &&
{title}
} {menuItems.map((item, i) => itemRenderer(item, { key: i, onClick: () => internalOnClickItem(item) }) )} diff --git a/app/src/ui/layouts/AppShell/AppShell.tsx b/app/src/ui/layouts/AppShell/AppShell.tsx index e61e1d5..e10839a 100644 --- a/app/src/ui/layouts/AppShell/AppShell.tsx +++ b/app/src/ui/layouts/AppShell/AppShell.tsx @@ -191,7 +191,7 @@ export const SidebarLink = ({ className={twMerge( "flex flex-row px-4 py-2.5 items-center gap-2", !disabled && - "cursor-pointer rounded-md [&.active]:bg-primary/10 [&.active]:hover:bg-primary/15 [&.active]:font-medium hover:bg-primary/5 link", + "cursor-pointer rounded-md [&.active]:bg-primary/10 [&.active]:hover:bg-primary/15 [&.active]:font-medium hover:bg-primary/5 focus:bg-primary/5 link", disabled && "opacity-50 cursor-not-allowed pointer-events-none", className )} diff --git a/app/src/ui/lib/routes.ts b/app/src/ui/lib/routes.ts index 44818fe..37f404a 100644 --- a/app/src/ui/lib/routes.ts +++ b/app/src/ui/lib/routes.ts @@ -1,6 +1,6 @@ import type { PrimaryFieldType } from "core"; import { encodeSearch } from "core/utils"; -import { useLocation } from "wouter"; +import { useLocation, useRouter } from "wouter"; import { useBknd } from "../client/BkndProvider"; export const routes = { @@ -55,6 +55,7 @@ export function withAbsolute(url: string) { export function useNavigate() { const [location, navigate] = useLocation(); + const router = useRouter(); const { app } = useBknd(); const basepath = app.getAdminConfig().basepath; return [ @@ -69,6 +70,7 @@ export function useNavigate() { transition?: boolean; } | { reload: true } + | { target: string } ) => { const wrap = (fn: () => void) => { fn(); @@ -81,9 +83,15 @@ export function useNavigate() { }; wrap(() => { - if (options && "reload" in options) { - window.location.href = url; - return; + if (options) { + if ("reload" in options) { + window.location.href = url; + return; + } else if ("target" in options) { + const _url = window.location.origin + basepath + router.base + url; + window.open(_url, options.target); + return; + } } const _url = options?.absolute ? `~/${basepath}${url}`.replace(/\/+/g, "/") : url; diff --git a/app/src/ui/modules/data/components/fields-specs.ts b/app/src/ui/modules/data/components/fields-specs.ts index e182113..79517fe 100644 --- a/app/src/ui/modules/data/components/fields-specs.ts +++ b/app/src/ui/modules/data/components/fields-specs.ts @@ -10,7 +10,7 @@ import { TbToggleLeft } from "react-icons/tb"; -type TFieldSpec = { +export type TFieldSpec = { type: string; label: string; icon: any; diff --git a/app/src/ui/routes/data/_data.root.tsx b/app/src/ui/routes/data/_data.root.tsx index 1ad24fa..a04c100 100644 --- a/app/src/ui/routes/data/_data.root.tsx +++ b/app/src/ui/routes/data/_data.root.tsx @@ -1,11 +1,20 @@ import { SegmentedControl, Tooltip } from "@mantine/core"; -import { IconDatabase } from "@tabler/icons-react"; +import { + IconAlignJustified, + IconCirclesRelation, + IconDatabase, + IconExternalLink, + IconPhoto, + IconPlus, + IconSettings +} from "@tabler/icons-react"; import type { Entity, TEntityType } from "data"; import { TbDatabasePlus } from "react-icons/tb"; import { twMerge } from "tailwind-merge"; import { useBkndData } from "ui/client/schema/data/use-bknd-data"; import { IconButton } from "ui/components/buttons/IconButton"; import { Empty } from "ui/components/display/Empty"; +import { Dropdown, type DropdownClickableChild } from "ui/components/overlay/Dropdown"; import { Link } from "ui/components/wouter/Link"; import { useBrowserTitle } from "ui/hooks/use-browser-title"; import * as AppShell from "ui/layouts/AppShell/AppShell"; @@ -125,15 +134,92 @@ const EntityLinkList = ({ ? routes.data.entity.list(entity.name) : routes.data.schema.entity(entity.name); return ( - - {entity.label} - + + + {entity.label} + + ); })} ); }; +const EntityContextMenu = ({ + entity, + children, + enabled = true +}: { entity: Entity; children: DropdownClickableChild; enabled?: boolean }) => { + if (!enabled) return children; + const [navigate] = useNavigate(); + const { $data } = useBkndData(); + + // get href from children (single item) + const href = (children as any).props.href; + const separator = () =>
; + + return ( + navigate(href, { target: "_blank" }) + }, + separator, + !$data.system(entity.name).any && { + icon: IconPlus, + label: "Create new", + onClick: () => navigate(routes.data.entity.create(entity.name)) + }, + { + icon: IconDatabase, + label: "List entries", + onClick: () => navigate(routes.data.entity.list(entity.name)) + }, + separator, + { + icon: IconAlignJustified, + label: "Manage fields", + onClick: () => navigate(routes.data.schema.entity(entity.name)) + }, + { + icon: IconCirclesRelation, + label: "Add relation", + onClick: () => + $data.modals.createRelation({ + target: entity.name, + type: "n:1" + }) + }, + !$data.system(entity.name).media && { + icon: IconPhoto, + label: "Add media", + onClick: () => $data.modals.createMedia(entity.name) + }, + separator, + { + icon: IconSettings, + label: "Advanced settings", + onClick: () => + navigate(routes.settings.path(["data", "entities", entity.name]), { + absolute: true + }) + } + ]} + openEvent="onContextMenu" + position="bottom-start" + > + {children} + + ); +}; + export function DataEmpty() { useBrowserTitle(["Data"]); const [navigate] = useNavigate(); diff --git a/app/src/ui/routes/data/data.schema.$entity.tsx b/app/src/ui/routes/data/data.schema.$entity.tsx index b477b15..de9b2f9 100644 --- a/app/src/ui/routes/data/data.schema.$entity.tsx +++ b/app/src/ui/routes/data/data.schema.$entity.tsx @@ -8,7 +8,7 @@ import { isDebug } from "core"; import type { Entity } from "data"; import { cloneDeep } from "lodash-es"; import { useRef, useState } from "react"; -import { TbCirclesRelation, TbDots, TbPhoto, TbPlus } from "react-icons/tb"; +import { TbCirclesRelation, TbDots, TbPhoto, TbPlus, TbSitemap } from "react-icons/tb"; import { useBkndData } from "ui/client/schema/data/use-bknd-data"; import { Button } from "ui/components/buttons/Button"; import { IconButton } from "ui/components/buttons/IconButton"; @@ -16,9 +16,11 @@ import { Empty } from "ui/components/display/Empty"; import { Message } from "ui/components/display/Message"; import { JsonSchemaForm, type JsonSchemaFormRef } from "ui/components/form/json-schema"; import { Dropdown } from "ui/components/overlay/Dropdown"; +import { Link } from "ui/components/wouter/Link"; import * as AppShell from "ui/layouts/AppShell/AppShell"; import { Breadcrumbs2 } from "ui/layouts/AppShell/Breadcrumbs2"; import { routes, useNavigate } from "ui/lib/routes"; +import { fieldSpecs } from "ui/modules/data/components/fields-specs"; import { extractSchema } from "../settings/utils/schema"; import { EntityFieldsForm, type EntityFieldsFormRef } from "./forms/entity.fields.form"; @@ -87,10 +89,15 @@ export function DataSchemaEntity({ params }) { } className="pl-3" > - +
+ + + + +
@@ -142,7 +149,7 @@ const Fields = ({ }: { entity: Entity; open: boolean; toggle: () => void }) => { const [submitting, setSubmitting] = useState(false); const [updates, setUpdates] = useState(0); - const { actions } = useBkndData(); + const { actions, $data } = useBkndData(); const [res, setRes] = useState(); const ref = useRef(null); async function handleUpdate() { @@ -175,7 +182,30 @@ const Fields = ({ {submitting && (
)} - + ["relation", "media"].includes(f.type)) + .map((i) => ({ + ...i, + onClick: () => { + switch (i.type) { + case "relation": + $data.modals.createRelation({ + target: entity.name, + type: "n:1" + }); + break; + case "media": + $data.modals.createMedia(entity.name); + break; + } + } + }))} + /> {isDebug() && (
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 bcc315d..55767cc 100644 --- a/app/src/ui/routes/data/forms/entity.fields.form.tsx +++ b/app/src/ui/routes/data/forms/entity.fields.form.tsx @@ -25,7 +25,7 @@ import { MantineSwitch } from "ui/components/form/hook-form-mantine/MantineSwitc import { JsonSchemaForm } from "ui/components/form/json-schema"; import { type SortableItemProps, SortableList } from "ui/components/list/SortableList"; import { Popover } from "ui/components/overlay/Popover"; -import { fieldSpecs } from "ui/modules/data/components/fields-specs"; +import { type TFieldSpec, fieldSpecs } from "ui/modules/data/components/fields-specs"; import { dataFieldsUiSchema } from "../../settings/routes/data.settings"; const fieldsSchemaObject = originalFieldsSchemaObject; @@ -45,7 +45,6 @@ type TFieldsFormSchema = Static; const fieldTypes = Object.keys(fieldsSchemaObject); const defaultType = fieldTypes[0]; -const blank_field = { name: "", field: { type: defaultType, config: {} } } as TFieldSchema; const commonProps = ["label", "description", "required", "fillable", "hidden", "virtual"]; function specificFieldSchema(type: keyof typeof fieldsSchemaObject) { @@ -53,6 +52,13 @@ function specificFieldSchema(type: keyof typeof fieldsSchemaObject) { return Type.Omit(fieldsSchemaObject[type]?.properties.config, commonProps); } +export type EntityFieldsFormProps = { + fields: TAppDataEntityFields; + onChange?: (formData: TAppDataEntityFields) => void; + sortable?: boolean; + additionalFieldTypes?: (TFieldSpec & { onClick: () => void })[]; +}; + export type EntityFieldsFormRef = { getValues: () => TFieldsFormSchema; getData: () => TAppDataEntityFields; @@ -60,146 +66,156 @@ export type EntityFieldsFormRef = { reset: () => void; }; -export const EntityFieldsForm = forwardRef< - EntityFieldsFormRef, - { - fields: TAppDataEntityFields; - onChange?: (formData: TAppDataEntityFields) => void; - sortable?: boolean; - } ->(function EntityFieldsForm({ fields: _fields, sortable, ...props }, ref) { - const entityFields = Object.entries(_fields).map(([name, field]) => ({ - name, - field - })); +export const EntityFieldsForm = forwardRef( + function EntityFieldsForm({ fields: _fields, sortable, additionalFieldTypes, ...props }, ref) { + const entityFields = Object.entries(_fields).map(([name, field]) => ({ + name, + field + })); - const { - control, - formState: { isValid, errors }, - getValues, - watch, - register, - setValue, - setError, - reset - } = useForm({ - mode: "all", - resolver: typeboxResolver(schema), - defaultValues: { - fields: entityFields - } as TFieldsFormSchema - }); - const { fields, append, remove, move } = useFieldArray({ - control, - name: "fields" - }); + const { + control, + formState: { isValid, errors }, + getValues, + watch, + register, + setValue, + setError, + reset + } = useForm({ + mode: "all", + resolver: typeboxResolver(schema), + defaultValues: { + fields: entityFields + } as TFieldsFormSchema + }); + const { fields, append, remove, move } = useFieldArray({ + control, + name: "fields" + }); - function toCleanValues(formData: TFieldsFormSchema): TAppDataEntityFields { - return Object.fromEntries( - formData.fields.map((field) => [field.name, objectCleanEmpty(field.field)]) - ); - } - - useEffect(() => { - if (props?.onChange) { - console.log("----set"); - watch((data: any) => { - console.log("---calling"); - props?.onChange?.(toCleanValues(data)); - }); + function toCleanValues(formData: TFieldsFormSchema): TAppDataEntityFields { + return Object.fromEntries( + formData.fields.map((field) => [field.name, objectCleanEmpty(field.field)]) + ); } - }, []); - useImperativeHandle(ref, () => ({ - reset, - getValues: () => getValues(), - getData: () => { - return toCleanValues(getValues()); - }, - isValid: () => isValid - })); - - function handleAppend(_type: keyof typeof fieldsSchemaObject) { - const newField = { - name: "", - new: true, - field: { - type: _type, - config: {} + useEffect(() => { + if (props?.onChange) { + console.log("----set"); + watch((data: any) => { + console.log("---calling"); + props?.onChange?.(toCleanValues(data)); + }); } - }; - append(newField); - } + }, []); - const formProps = { - watch, - register, - setValue, - getValues, - control, - setError - }; - return ( - <> -
-
-
- {sortable ? ( - item.id} - disableIndices={[0]} - renderItem={({ dnd, ...props }, index) => ( - ({ + reset, + getValues: () => getValues(), + getData: () => { + return toCleanValues(getValues()); + }, + isValid: () => isValid + })); + + function handleAppend(_type: keyof typeof fieldsSchemaObject) { + const newField = { + name: "", + new: true, + field: { + type: _type, + config: {} + } + }; + append(newField); + } + + const formProps = { + watch, + register, + setValue, + getValues, + control, + setError + }; + return ( + <> +
+
+
+ {sortable ? ( + item.id} + disableIndices={[0]} + renderItem={({ dnd, ...props }, index) => ( + + )} + /> + ) : ( +
+ {fields.map((field, index) => ( + + ))} +
+ )} + + ( + { + handleAppend(type as any); + }} /> )} - /> - ) : ( -
- {fields.map((field, index) => ( - - ))} -
- )} - - ( - { - handleAppend(type as any); - toggle(); - }} - /> - )} - > - - + > + +
+
-
- - ); -}); + + ); + } +); -const SelectType = ({ onSelect }: { onSelect: (type: string) => void }) => { - const types = fieldSpecs.filter((s) => s.addable !== false); +const SelectType = ({ + onSelect, + additionalFieldTypes = [], + onSelected +}: { + onSelect: (type: string) => void; + additionalFieldTypes?: (TFieldSpec & { onClick?: () => void })[]; + onSelected?: () => void; +}) => { + const types: (TFieldSpec & { onClick?: () => void })[] = fieldSpecs.filter( + (s) => s.addable !== false + ); + + if (additionalFieldTypes) { + types.push(...additionalFieldTypes); + } return (
@@ -208,7 +224,14 @@ const SelectType = ({ onSelect }: { onSelect: (type: string) => void }) => { key={type.type} IconLeft={type.icon} variant="ghost" - onClick={() => onSelect(type.type)} + onClick={() => { + if (type.addable) { + onSelect(type.type); + } else { + type.onClick?.(); + } + onSelected?.(); + }} > {type.label} From ad61770ef4df3c3953ec5f60f7ee438908f51f9a Mon Sep 17 00:00:00 2001 From: dswbx Date: Sat, 18 Jan 2025 09:15:32 +0100 Subject: [PATCH 28/32] fix entity form fields regression --- app/package.json | 2 +- app/src/ui/routes/data/data.schema.$entity.tsx | 15 ++++++++++++++- .../ui/routes/data/forms/entity.fields.form.tsx | 2 +- 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/app/package.json b/app/package.json index 6b2a9db..93f06fc 100644 --- a/app/package.json +++ b/app/package.json @@ -3,7 +3,7 @@ "type": "module", "sideEffects": false, "bin": "./dist/cli/index.js", - "version": "0.6.0-rc.7", + "version": "0.6.0-rc.10", "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": { diff --git a/app/src/ui/routes/data/data.schema.$entity.tsx b/app/src/ui/routes/data/data.schema.$entity.tsx index de9b2f9..455b945 100644 --- a/app/src/ui/routes/data/data.schema.$entity.tsx +++ b/app/src/ui/routes/data/data.schema.$entity.tsx @@ -8,7 +8,14 @@ import { isDebug } from "core"; import type { Entity } from "data"; import { cloneDeep } from "lodash-es"; import { useRef, useState } from "react"; -import { TbCirclesRelation, TbDots, TbPhoto, TbPlus, TbSitemap } from "react-icons/tb"; +import { + TbCirclesRelation, + TbDatabasePlus, + TbDots, + TbPhoto, + TbPlus, + TbSitemap +} from "react-icons/tb"; import { useBkndData } from "ui/client/schema/data/use-bknd-data"; import { Button } from "ui/components/buttons/Button"; import { IconButton } from "ui/components/buttons/IconButton"; @@ -79,6 +86,12 @@ export function DataSchemaEntity({ params }) { icon: TbPhoto, label: "Add media", onClick: () => $data.modals.createMedia(entity.name) + }, + () =>
, + { + icon: TbDatabasePlus, + label: "Create Entity", + onClick: () => $data.modals.createEntity() } ]} position="bottom-end" 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 55767cc..7d64fc5 100644 --- a/app/src/ui/routes/data/forms/entity.fields.form.tsx +++ b/app/src/ui/routes/data/forms/entity.fields.form.tsx @@ -225,7 +225,7 @@ const SelectType = ({ IconLeft={type.icon} variant="ghost" onClick={() => { - if (type.addable) { + if (type.addable !== false) { onSelect(type.type); } else { type.onClick?.(); From ebd45651662938541c6fe18d22f287dc26ba980d Mon Sep 17 00:00:00 2001 From: dswbx Date: Sat, 18 Jan 2025 09:20:57 +0100 Subject: [PATCH 29/32] fix `__isDev` regression because of new vite static build --- app/package.json | 2 +- app/vite.config.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/package.json b/app/package.json index 93f06fc..4de3aa0 100644 --- a/app/package.json +++ b/app/package.json @@ -3,7 +3,7 @@ "type": "module", "sideEffects": false, "bin": "./dist/cli/index.js", - "version": "0.6.0-rc.10", + "version": "0.6.0-rc.11", "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": { diff --git a/app/vite.config.ts b/app/vite.config.ts index 5da4aeb..9c43d51 100644 --- a/app/vite.config.ts +++ b/app/vite.config.ts @@ -7,7 +7,7 @@ import { devServerConfig } from "./src/adapter/vite/dev-server-config"; // https://vitejs.dev/config/ export default defineConfig({ define: { - __isDev: "1" + __isDev: process.env.NODE_ENV === "production" ? "0" : "1" }, clearScreen: false, publicDir: "./src/ui/assets", From 145b47e942e2db9725f5709983f6e66277bb1cd1 Mon Sep 17 00:00:00 2001 From: dswbx Date: Sat, 18 Jan 2025 09:59:10 +0100 Subject: [PATCH 30/32] minimal popper implementation for context menu placement --- app/src/core/utils/index.ts | 1 + app/src/core/utils/numbers.ts | 5 ++ app/src/ui/components/overlay/Dropdown.tsx | 58 +++++++++++++++++----- app/src/ui/routes/data/_data.root.tsx | 2 +- 4 files changed, 53 insertions(+), 13 deletions(-) create mode 100644 app/src/core/utils/numbers.ts diff --git a/app/src/core/utils/index.ts b/app/src/core/utils/index.ts index 85809e2..c2239e4 100644 --- a/app/src/core/utils/index.ts +++ b/app/src/core/utils/index.ts @@ -12,3 +12,4 @@ export * from "./uuid"; export { FromSchema } from "./typebox/from-schema"; export * from "./test"; export * from "./runtime"; +export * from "./numbers"; diff --git a/app/src/core/utils/numbers.ts b/app/src/core/utils/numbers.ts new file mode 100644 index 0000000..1435f68 --- /dev/null +++ b/app/src/core/utils/numbers.ts @@ -0,0 +1,5 @@ +export function clampNumber(value: number, min: number, max: number): number { + const lower = Math.min(min, max); + const upper = Math.max(min, max); + return Math.max(lower, Math.min(value, upper)); +} diff --git a/app/src/ui/components/overlay/Dropdown.tsx b/app/src/ui/components/overlay/Dropdown.tsx index 8081e1c..3fc49b4 100644 --- a/app/src/ui/components/overlay/Dropdown.tsx +++ b/app/src/ui/components/overlay/Dropdown.tsx @@ -1,4 +1,5 @@ import { useClickOutside } from "@mantine/hooks"; +import { clampNumber } from "core/utils"; import { type ComponentPropsWithoutRef, Fragment, @@ -7,7 +8,7 @@ import { useState } from "react"; import { twMerge } from "tailwind-merge"; -import { useEvent } from "../../hooks/use-event"; +import { useEvent } from "ui/hooks/use-event"; export type DropdownItem = | (() => JSX.Element) @@ -43,7 +44,7 @@ export function Dropdown({ children, defaultOpen = false, openEvent = "onClick", - position = "bottom-start", + position: initialPosition = "bottom-start", dropdownWrapperProps, items, title, @@ -54,24 +55,58 @@ export function Dropdown({ className }: DropdownProps) { const [open, setOpen] = useState(defaultOpen); + const [position, setPosition] = useState(initialPosition); const clickoutsideRef = useClickOutside(() => setOpen(false)); const menuItems = items.filter(Boolean) as DropdownItem[]; + const [_offset, _setOffset] = useState(0); const toggle = useEvent((delay: number = 50) => setTimeout(() => setOpen((prev) => !prev), typeof delay === "number" ? delay : 0) ); - const openEventHandler = useEvent((e) => { + const onClickHandler = openEvent === "onClick" ? toggle : undefined; + const onContextMenuHandler = useEvent((e) => { + if (openEvent !== "onContextMenu") return; e.preventDefault(); + + if (open) { + toggle(0); + setTimeout(() => { + setPosition(initialPosition); + _setOffset(0); + }, 10); + return; + } + + // minimal popper impl, get pos and boundaries + const x = e.clientX - e.currentTarget.getBoundingClientRect().left; + const { left = 0, right = 0 } = clickoutsideRef.current?.getBoundingClientRect() ?? {}; + + // only if boundaries gien + if (left > 0 && right > 0) { + const safe = clampNumber(x, left, right); + // if pos less than half, go left + if (x < (left + right) / 2) { + setPosition("bottom-start"); + _setOffset(safe); + } else { + setPosition("bottom-end"); + _setOffset(right - safe); + } + } else { + setPosition(initialPosition); + _setOffset(0); + } + toggle(); }); const offset = 4; const dropdownStyle = { - "bottom-start": { top: "100%", left: 0, marginTop: offset }, - "bottom-end": { right: 0, top: "100%", marginTop: offset }, + "bottom-start": { top: "100%", left: _offset, marginTop: offset }, + "bottom-end": { right: _offset, top: "100%", marginTop: offset }, "top-start": { bottom: "100%", marginBottom: offset }, - "top-end": { bottom: "100%", right: 0, marginBottom: offset } + "top-end": { bottom: "100%", right: _offset, marginBottom: offset } }[position]; const internalOnClickItem = useEvent((item) => { @@ -116,12 +151,9 @@ export function Dropdown({ role="dropdown" className={twMerge("relative flex", className)} ref={clickoutsideRef} - onContextMenu={openEvent === "onContextMenu" ? openEventHandler : undefined} + onContextMenu={onContextMenuHandler} > - {cloneElement( - children as any, - openEvent === "onClick" ? { onClick: openEventHandler } : {} - )} + {cloneElement(children as any, { onClick: onClickHandler })} {open && (
- {title &&
{title}
} + {title && ( +
{title}
+ )} {menuItems.map((item, i) => itemRenderer(item, { key: i, onClick: () => internalOnClickItem(item) }) )} diff --git a/app/src/ui/routes/data/_data.root.tsx b/app/src/ui/routes/data/_data.root.tsx index a04c100..ad02097 100644 --- a/app/src/ui/routes/data/_data.root.tsx +++ b/app/src/ui/routes/data/_data.root.tsx @@ -205,7 +205,7 @@ const EntityContextMenu = ({ separator, { icon: IconSettings, - label: "Advanced settings", + label: "Settings", onClick: () => navigate(routes.settings.path(["data", "entities", entity.name]), { absolute: true From db101889456f22a84dc9dedca293a7cde9669591 Mon Sep 17 00:00:00 2001 From: dswbx Date: Sat, 18 Jan 2025 12:39:34 +0100 Subject: [PATCH 31/32] strengthened schema ensuring for system entities --- app/__test__/modules/AppAuth.spec.ts | 38 ++++++++++++++++++-- app/__test__/modules/AppMedia.spec.ts | 4 +-- app/__test__/modules/Module.spec.ts | 3 +- app/package.json | 2 +- app/src/App.ts | 2 ++ app/src/auth/AppAuth.ts | 32 ++++++++++------- app/src/data/entities/EntityManager.ts | 4 +-- app/src/data/fields/Field.ts | 3 ++ app/src/modules/Module.ts | 41 +++++++++++++++++----- app/src/modules/ModuleManager.ts | 17 ++++++--- app/src/modules/server/SystemController.ts | 5 +++ app/src/ui/main.css | 3 -- 12 files changed, 118 insertions(+), 36 deletions(-) diff --git a/app/__test__/modules/AppAuth.spec.ts b/app/__test__/modules/AppAuth.spec.ts index 225c9d6..f0ecc86 100644 --- a/app/__test__/modules/AppAuth.spec.ts +++ b/app/__test__/modules/AppAuth.spec.ts @@ -1,7 +1,7 @@ import { afterAll, beforeAll, beforeEach, describe, expect, spyOn, test } from "bun:test"; import { createApp } from "../../src"; import { AuthController } from "../../src/auth/api/AuthController"; -import { em, entity, text } from "../../src/data"; +import { em, entity, make, text } from "../../src/data"; import { AppAuth, type ModuleBuildContext } from "../../src/modules"; import { disableConsoleLog, enableConsoleLog } from "../helper"; import { makeCtx, moduleTestSuite } from "./module-test-suite"; @@ -125,6 +125,40 @@ describe("AppAuth", () => { const fields = e.fields.map((f) => f.name); expect(e.type).toBe("system"); expect(fields).toContain("additional"); - expect(fields).toEqual(["id", "email", "strategy", "strategy_value", "role", "additional"]); + expect(fields).toEqual(["id", "additional", "email", "strategy", "strategy_value", "role"]); + }); + + test("ensure user field configs is always correct", async () => { + const app = createApp({ + initialConfig: { + auth: { + enabled: true + }, + data: em({ + users: entity("users", { + strategy: text({ + fillable: true, + hidden: false + }), + strategy_value: text({ + fillable: true, + hidden: false + }) + }) + }).toJSON() + } + }); + await app.build(); + + const users = app.em.entity("users"); + const props = ["hidden", "fillable", "required"]; + + for (const [name, _authFieldProto] of Object.entries(AppAuth.usersFields)) { + const authField = make(name, _authFieldProto as any); + const field = users.field(name)!; + for (const prop of props) { + expect(field.config[prop]).toBe(authField.config[prop]); + } + } }); }); diff --git a/app/__test__/modules/AppMedia.spec.ts b/app/__test__/modules/AppMedia.spec.ts index 19fa73b..b5ce17f 100644 --- a/app/__test__/modules/AppMedia.spec.ts +++ b/app/__test__/modules/AppMedia.spec.ts @@ -39,6 +39,7 @@ describe("AppMedia", () => { expect(fields).toContain("additional"); expect(fields).toEqual([ "id", + "additional", "path", "folder", "mime_type", @@ -48,8 +49,7 @@ describe("AppMedia", () => { "modified_at", "reference", "entity_id", - "metadata", - "additional" + "metadata" ]); }); }); diff --git a/app/__test__/modules/Module.spec.ts b/app/__test__/modules/Module.spec.ts index 572c5a1..5c20ca5 100644 --- a/app/__test__/modules/Module.spec.ts +++ b/app/__test__/modules/Module.spec.ts @@ -157,8 +157,7 @@ describe("Module", async () => { entities: [ { name: "u", - // ensured properties must come first - fields: ["id", "important", "name"], + fields: ["id", "name", "important"], // ensured type must be present type: "system" }, diff --git a/app/package.json b/app/package.json index 4de3aa0..94b6912 100644 --- a/app/package.json +++ b/app/package.json @@ -3,7 +3,7 @@ "type": "module", "sideEffects": false, "bin": "./dist/cli/index.js", - "version": "0.6.0-rc.11", + "version": "0.6.0-rc.12", "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": { diff --git a/app/src/App.ts b/app/src/App.ts index b98fc67..322554d 100644 --- a/app/src/App.ts +++ b/app/src/App.ts @@ -118,6 +118,8 @@ export class App { this.trigger_first_boot = false; await this.emgr.emit(new AppFirstBoot({ app: this })); } + + console.log("[APP] built"); } mutateConfig(module: Module) { diff --git a/app/src/auth/AppAuth.ts b/app/src/auth/AppAuth.ts index 20386a5..ca5b919 100644 --- a/app/src/auth/AppAuth.ts +++ b/app/src/auth/AppAuth.ts @@ -10,7 +10,7 @@ import type { PasswordStrategy } from "auth/authenticate/strategies"; import { type DB, Exception, type PrimaryFieldType } from "core"; import { type Static, secureRandomString, transformObject } from "core/utils"; import type { Entity, EntityManager } from "data"; -import { type FieldSchema, em, entity, enumm, make, text } from "data/prototype"; +import { type FieldSchema, em, entity, enumm, text } from "data/prototype"; import { pick } from "lodash-es"; import { Module } from "modules/Module"; import { AuthController } from "./api/AuthController"; @@ -224,17 +224,22 @@ export class AppAuth extends Module { } private toggleStrategyValueVisibility(visible: boolean) { - const field = this.getUsersEntity().field("strategy_value")!; + const toggle = (name: string, visible: boolean) => { + const field = this.getUsersEntity().field(name)!; - if (visible) { - field.config.hidden = false; - field.config.fillable = true; - } else { - // reset to normal - const template = AppAuth.usersFields.strategy_value.config; - field.config.hidden = template.hidden; - field.config.fillable = template.fillable; - } + if (visible) { + field.config.hidden = false; + field.config.fillable = true; + } else { + // reset to normal + const template = AppAuth.usersFields.strategy_value.config; + field.config.hidden = template.hidden; + field.config.fillable = template.fillable; + } + }; + + toggle("strategy_value", visible); + toggle("strategy", visible); // @todo: think about a PasswordField that automatically hashes on save? } @@ -250,7 +255,10 @@ export class AppAuth extends Module { static usersFields = { email: text().required(), - strategy: text({ fillable: ["create"], hidden: ["form"] }).required(), + strategy: text({ + fillable: ["create"], + hidden: ["update", "form"] + }).required(), strategy_value: text({ fillable: ["create"], hidden: ["read", "table", "update", "form"] diff --git a/app/src/data/entities/EntityManager.ts b/app/src/data/entities/EntityManager.ts index f8dfd7b..31401b3 100644 --- a/app/src/data/entities/EntityManager.ts +++ b/app/src/data/entities/EntityManager.ts @@ -111,13 +111,13 @@ export class EntityManager { // caused issues because this.entity() was using a reference (for when initial config was given) } - entity(e: Entity | keyof TBD | string): Entity { + entity(e: Entity | keyof TBD | string, silent?: boolean): Entity { // make sure to always retrieve by name const entity = this.entities.find((entity) => e instanceof Entity ? entity.name === e.name : entity.name === e ); - if (!entity) { + if (!entity && !silent) { // @ts-ignore throw new EntityNotDefinedException(e instanceof Entity ? e.name : e); } diff --git a/app/src/data/fields/Field.ts b/app/src/data/fields/Field.ts index ffa7f08..f412f5e 100644 --- a/app/src/data/fields/Field.ts +++ b/app/src/data/fields/Field.ts @@ -12,6 +12,9 @@ import type { HTMLInputTypeAttribute, InputHTMLAttributes } from "react"; import type { EntityManager } from "../entities"; import { InvalidFieldConfigException, TransformPersistFailedException } from "../errors"; +// @todo: contexts need to be reworked +// e.g. "table" is irrelevant, because if read is not given, it fails + export const ActionContext = ["create", "read", "update", "delete"] as const; export type TActionContext = (typeof ActionContext)[number]; diff --git a/app/src/modules/Module.ts b/app/src/modules/Module.ts index 546db48..0d5b8bf 100644 --- a/app/src/modules/Module.ts +++ b/app/src/modules/Module.ts @@ -14,6 +14,7 @@ import { } from "data"; import { Entity } from "data"; import type { Hono } from "hono"; +import { isEqual } from "lodash-es"; export type ServerEnv = { Variables: { @@ -154,28 +155,33 @@ export abstract class Module 0) { + this.ctx.flags.sync_required = true; + } } } // replace entity (mainly to keep the ensured type) this.ctx.em.__replaceEntity( - new Entity(entity.name, entity.fields, instance.config, entity.type) + new Entity(instance.name, instance.fields, instance.config, entity.type) ); } @@ -193,6 +199,21 @@ export abstract class Module = { + id?: number; version: number; type: "config" | "diff" | "backup"; json: Json; @@ -236,10 +237,10 @@ export class ModuleManager { private async fetch(): Promise { this.logger.context("fetch").log("fetching"); + const startTime = performance.now(); // disabling console log, because the table might not exist yet - return await withDisabledConsole(async () => { - const startTime = performance.now(); + const result = await withDisabledConsole(async () => { const { data: result } = await this.repo().findOne( { type: "config" }, { @@ -251,9 +252,16 @@ export class ModuleManager { throw BkndError.with("no config"); } - this.logger.log("took", performance.now() - startTime, "ms", result.version).clear(); - return result as ConfigTable; + return result as unknown as ConfigTable; }, ["log", "error", "warn"]); + + this.logger + .log("took", performance.now() - startTime, "ms", { + version: result.version, + id: result.id + }) + .clear(); + return result; } async save() { @@ -390,6 +398,7 @@ export class ModuleManager { } private setConfigs(configs: ModuleConfigs): void { + this.logger.log("setting configs"); objectEach(configs, (config, key) => { try { // setting "noEmit" to true, to not force listeners to update diff --git a/app/src/modules/server/SystemController.ts b/app/src/modules/server/SystemController.ts index 8fd50cc..be2e548 100644 --- a/app/src/modules/server/SystemController.ts +++ b/app/src/modules/server/SystemController.ts @@ -44,6 +44,11 @@ export class SystemController extends Controller { hono.use(permission(SystemPermissions.configRead)); + hono.get("/raw", permission([SystemPermissions.configReadSecrets]), async (c) => { + // @ts-expect-error "fetch" is private + return c.json(await this.app.modules.fetch()); + }); + hono.get( "/:module?", tb("param", Type.Object({ module: Type.Optional(StringEnum(MODULE_NAMES)) })), diff --git a/app/src/ui/main.css b/app/src/ui/main.css index c6254b0..9968b67 100644 --- a/app/src/ui/main.css +++ b/app/src/ui/main.css @@ -54,9 +54,6 @@ body, } } -@layer utilities { -} - #bknd-admin, .bknd-admin { /* Chrome, Edge, and Safari */ From fb2dff956b9cb5aa077acecdf55cef01c871b40e Mon Sep 17 00:00:00 2001 From: dswbx Date: Sat, 18 Jan 2025 13:31:33 +0100 Subject: [PATCH 32/32] added better error messages if config secret permission is missing --- app/package.json | 2 +- app/src/App.ts | 2 -- app/src/data/entities/EntityManager.ts | 11 ++++--- app/src/ui/client/BkndProvider.tsx | 33 +++++++++++++------ app/src/ui/components/display/Alert.tsx | 17 ++++++++-- app/src/ui/components/display/Message.tsx | 17 +++++++++- .../ui/routes/auth/auth.roles.edit.$role.tsx | 8 +++-- app/src/ui/routes/data/_data.root.tsx | 2 +- app/src/ui/routes/settings/index.tsx | 5 ++- 9 files changed, 72 insertions(+), 25 deletions(-) diff --git a/app/package.json b/app/package.json index 94b6912..c618d9c 100644 --- a/app/package.json +++ b/app/package.json @@ -3,7 +3,7 @@ "type": "module", "sideEffects": false, "bin": "./dist/cli/index.js", - "version": "0.6.0-rc.12", + "version": "0.6.0-rc.13", "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": { diff --git a/app/src/App.ts b/app/src/App.ts index 322554d..b98fc67 100644 --- a/app/src/App.ts +++ b/app/src/App.ts @@ -118,8 +118,6 @@ export class App { this.trigger_first_boot = false; await this.emgr.emit(new AppFirstBoot({ app: this })); } - - console.log("[APP] built"); } mutateConfig(module: Module) { diff --git a/app/src/data/entities/EntityManager.ts b/app/src/data/entities/EntityManager.ts index 31401b3..6018029 100644 --- a/app/src/data/entities/EntityManager.ts +++ b/app/src/data/entities/EntityManager.ts @@ -111,15 +111,18 @@ export class EntityManager { // caused issues because this.entity() was using a reference (for when initial config was given) } - entity(e: Entity | keyof TBD | string, silent?: boolean): Entity { + entity( + e: Entity | keyof TBD | string, + silent?: Silent + ): Silent extends true ? Entity | undefined : Entity { // make sure to always retrieve by name const entity = this.entities.find((entity) => e instanceof Entity ? entity.name === e.name : entity.name === e ); - if (!entity && !silent) { - // @ts-ignore - throw new EntityNotDefinedException(e instanceof Entity ? e.name : e); + if (!entity) { + if (silent === true) return undefined as any; + throw new EntityNotDefinedException(e instanceof Entity ? e.name : (e as string)); } return entity; diff --git a/app/src/ui/client/BkndProvider.tsx b/app/src/ui/client/BkndProvider.tsx index cc32221..1dc51e6 100644 --- a/app/src/ui/client/BkndProvider.tsx +++ b/app/src/ui/client/BkndProvider.tsx @@ -1,7 +1,10 @@ +import { IconAlertHexagon } from "@tabler/icons-react"; import type { ModuleConfigs, ModuleSchemas } from "modules"; import { getDefaultConfig, getDefaultSchema } from "modules/ModuleManager"; import { createContext, startTransition, useContext, useEffect, useRef, useState } from "react"; import { useApi } from "ui/client"; +import { Button } from "ui/components/buttons/Button"; +import { Alert } from "ui/components/display/Alert"; import { type TSchemaActions, getSchemaActions } from "./schema/actions"; import { AppReduced } from "./utils/AppReduced"; @@ -10,6 +13,7 @@ type BkndContext = { schema: ModuleSchemas; config: ModuleConfigs; permissions: string[]; + hasSecrets: boolean; requireSecrets: () => Promise; actions: ReturnType; app: AppReduced; @@ -32,6 +36,7 @@ export function BkndProvider({ const [schema, setSchema] = useState>(); const [fetched, setFetched] = useState(false); + const [error, setError] = useState(); const errorShown = useRef(); const [local_version, set_local_version] = useState(0); const api = useApi(); @@ -50,15 +55,11 @@ export function BkndProvider({ if (!res.ok) { if (errorShown.current) return; errorShown.current = true; - /*notifications.show({ - title: "Failed to fetch schema", - // @ts-ignore - message: body.error, - color: "red", - position: "top-right", - autoClose: false, - withCloseButton: true - });*/ + + setError(true); + return; + } else if (error) { + setError(false); } const schema = res.ok @@ -98,12 +99,24 @@ export function BkndProvider({ if (!fetched || !schema) return fallback; const app = new AppReduced(schema?.config as any); const actions = getSchemaActions({ api, setSchema, reloadSchema }); + const hasSecrets = withSecrets && !error; return ( + {error && ( + + + You attempted to load system configuration with secrets without having proper + permission. + + + + + )} + {children} ); diff --git a/app/src/ui/components/display/Alert.tsx b/app/src/ui/components/display/Alert.tsx index ba3c4cd..ee5aad1 100644 --- a/app/src/ui/components/display/Alert.tsx +++ b/app/src/ui/components/display/Alert.tsx @@ -6,16 +6,27 @@ export type AlertProps = ComponentPropsWithoutRef<"div"> & { visible?: boolean; title?: string; message?: ReactNode | string; + children?: ReactNode; }; -const Base: React.FC = ({ visible = true, title, message, className, ...props }) => +const Base: React.FC = ({ + visible = true, + title, + message, + className, + children, + ...props +}) => visible ? (
{title && {title}:} - {message} + {message || children}
) : null; diff --git a/app/src/ui/components/display/Message.tsx b/app/src/ui/components/display/Message.tsx index da44346..bc262d2 100644 --- a/app/src/ui/components/display/Message.tsx +++ b/app/src/ui/components/display/Message.tsx @@ -1,9 +1,24 @@ +import { IconLockAccessOff } from "@tabler/icons-react"; import { Empty, type EmptyProps } from "./Empty"; const NotFound = (props: Partial) => ; const NotAllowed = (props: Partial) => ; +const MissingPermission = ({ + what, + ...props +}: Partial & { + what?: string; +}) => ( + +); export const Message = { NotFound, - NotAllowed + NotAllowed, + MissingPermission }; diff --git a/app/src/ui/routes/auth/auth.roles.edit.$role.tsx b/app/src/ui/routes/auth/auth.roles.edit.$role.tsx index cb7de81..738e568 100644 --- a/app/src/ui/routes/auth/auth.roles.edit.$role.tsx +++ b/app/src/ui/routes/auth/auth.roles.edit.$role.tsx @@ -1,10 +1,10 @@ -import { notifications } from "@mantine/notifications"; import { useRef } from "react"; import { TbDots } from "react-icons/tb"; import { useBknd } from "ui/client/bknd"; import { useBkndAuth } from "ui/client/schema/auth/use-bknd-auth"; import { Button } from "ui/components/buttons/Button"; import { IconButton } from "ui/components/buttons/IconButton"; +import { Message } from "ui/components/display/Message"; import { Dropdown } from "ui/components/overlay/Dropdown"; import * as AppShell from "ui/layouts/AppShell/AppShell"; import { Breadcrumbs2 } from "ui/layouts/AppShell/Breadcrumbs2"; @@ -12,7 +12,11 @@ import { routes, useNavigate } from "ui/lib/routes"; import { AuthRoleForm, type AuthRoleFormRef } from "ui/routes/auth/forms/role.form"; export function AuthRolesEdit(props) { - useBknd({ withSecrets: true }); + const { hasSecrets } = useBknd({ withSecrets: true }); + if (!hasSecrets) { + return ; + } + return ; } diff --git a/app/src/ui/routes/data/_data.root.tsx b/app/src/ui/routes/data/_data.root.tsx index ad02097..543816c 100644 --- a/app/src/ui/routes/data/_data.root.tsx +++ b/app/src/ui/routes/data/_data.root.tsx @@ -168,7 +168,7 @@ const EntityContextMenu = ({ items={[ href && { icon: IconExternalLink, - label: "Open in new tab", + label: "Open in tab", onClick: () => navigate(href, { target: "_blank" }) }, separator, diff --git a/app/src/ui/routes/settings/index.tsx b/app/src/ui/routes/settings/index.tsx index 71c5754..026005b 100644 --- a/app/src/ui/routes/settings/index.tsx +++ b/app/src/ui/routes/settings/index.tsx @@ -2,6 +2,7 @@ import { IconSettings } from "@tabler/icons-react"; import { ucFirst } from "core/utils"; import { useBknd } from "ui/client/bknd"; import { Empty } from "ui/components/display/Empty"; +import { Message } from "ui/components/display/Message"; import { Link } from "ui/components/wouter/Link"; import { useBrowserTitle } from "ui/hooks/use-browser-title"; import * as AppShell from "ui/layouts/AppShell/AppShell"; @@ -44,7 +45,9 @@ function SettingsSidebar() { } export default function SettingsRoutes() { - useBknd({ withSecrets: true }); + const b = useBknd({ withSecrets: true }); + if (!b.hasSecrets) return ; + return ( <>