Release 0.16 (#196)

* initial refactor

* fixes

* test secrets extraction

* updated lock

* fix secret schema

* updated schemas, fixed tests, skipping flow tests for now

* added validator for rjsf, hook form via standard schema

* removed @sinclair/typebox

* remove unneeded vite dep

* fix jsonv literal on Field.tsx

* fix schema import path

* fix schema modals

* fix schema modals

* fix json field form, replaced auth form

* initial waku

* finalize waku example

* fix jsonv-ts version

* fix schema updates with falsy values

* fix media api to respect options' init, improve types

* checking media controller test

* checking media controller test

* checking media controller test

* clean up mediacontroller test

* added cookie option `partitioned`, as well as cors `origin` to be array, option to enable `credentials` (#214)

* added cookie option `partitioned`, as well as cors `origin` to be array, option to enable `credentials`

* fix server test

* fix data api (updated jsonv-ts)

* enhance cloudflare image optimization plugin with new options and explain endpoint (#215)

* feat: add ability to serve static by using dynamic imports (#197)

* feat: add ability to serve static by using dynamic imports

* serveStaticViaImport: make manifest optional

* serveStaticViaImport: add error log

* refactor/imports (#217)

* refactored core and core/utils imports

* refactored core and core/utils imports

* refactored media imports

* refactored auth imports

* refactored data imports

* updated package json exports, fixed mm config

* fix tests

* feat/deno (#219)

* update bun version

* fix module manager's em reference

* add basic deno example

* finalize

* docs: fumadocs migration (#185)

* feat(docs): initialize documentation structure with Fumadocs

* feat(docs): remove home route and move /docs route to /route

* feat(docs): add redirect to /start page

* feat(docs): migrate Getting Started chapters

* feat(docs): migrate Usage and Extending chapters

* feat(callout): add CalloutCaution, CalloutDanger, CalloutInfo, and CalloutPositive

* feat(layout): add Discord and GitHub links to documentation layout

* feat(docs): add integration chapters draft

* feat(docs): add modules chapters draft

* refactor(mdx-components): remove unused Icon import

* refactor(StackBlitz): enhance type safety by using unknown instead of any

* refactor(layout): update navigation mode to 'top' in layout configuration

* feat(docs): add @iconify/react package

* docs(mdx-components): add Icon component to MDX components list

* feat(docs): update Next.js integration guide

* feat(docs): update React Router integration guide

* feat(docs): update Astro integration guide

* feat(docs): update Vite integration guide

* fix(docs): update package manager initialization commands

* feat(docs): migrate Modules chapters

* chore(docs): update package.json with new devDependencies

* feat(docs): migrate Integration Runtimes chapters

* feat(docs): update Database usage chapter

* feat(docs): restructure documentation paths

* chore(docs): clean up unused imports and files in documentation

* style(layout): revert navigation mode to previous state

* fix(docs): routing for documentation structure

* feat(openapi): add API documentation generation from OpenAPI schema

* feat(docs): add icons to documentation pages

* chore(dependencies): remove unused content-collections packages

* fix(types): fix type error for attachFile in source.ts

* feat(redirects): update root redirect destination to '/start'

* feat(search): add static search functionality

* chore(dependencies): update fumadocs-core and fumadocs-ui to latest versions

* feat(search): add Powered by Orama link

* feat(generate-openapi): add error handling for missing OpenAPI schema

* feat(scripts): add OpenAPI generation to build process

* feat(config): enable dynamic redirects and rewrites in development mode

* feat(layout): add GitHub token support for improved API rate limits

* feat(redirects): add 301 redirects for cloudflare pages

* feat(docs): add Vercel redirects configuration

* feat(config): enable standalone output for development environment

* chore(layout): adjust layout settings

* refactor(package): clean up ajv dependency versions

* feat(docs): add twoslash support

* refactor(layout): update DocsLayout import and navigation configuration

* chore(layout): clean up layout.tsx by commenting out GithubInfo

* fix(Search): add locale to search initialization

* chore(package): update fumadocs and orama to latest versions

* docs: add menu items descriptions

* feat(layout): add GitHub URL to the layout component

* feat(docs): add AutoTypeTable component to MDX components

* feat(app): implement AutoTypeTable rendering for AppEvents type

* docs(layout): switch callouts back to default components

* fix(config): use __filename and __dirname for module paths

* docs: add note about node.js 22 requirement

* feat(styles): add custom color variables for light and dark themes

* docs: add S3 setup instructions for media module

* docs: fix typos and indentation in media module docs

* docs: add local media adapter example for Node.js

* docs(media): add S3/R2 URL format examples and fix typo

* docs: add cross-links to initial config and seeding sections

* indent numbered lists content, clarified media serve locations

* fix mediacontroller tests

* feat(layout): add AnimatedGridPattern component for dynamic background

* style(layout): configure fancy ToC style ('clerk')

* fix(AnimatedGridPattern): correct strokeDasharray type

* docs: actualize docs

* feat: add favicon

* style(cloudflare): format code examples

* feat(layout): add Github and Discord footer icons

* feat(footer): add SVG social media icons for GitHub and Discord

* docs: adjusted auto type table, added llm functions

* added static deployment to cloudflare workers

* docs: change cf redirects to proxy *.mdx instead of redirecting

---------

Co-authored-by: dswbx <dennis.senn@gmx.ch>
Co-authored-by: cameronapak <cameronandrewpak@gmail.com>

* build: improve build script

* add missing exports, fix EntityTypescript imports

* media: Dropzone: add programmatic upload, additional events, loading state

* schema object: disable extended defaults to allow empty config values

* Feat/new docs deploy (#224)

* test

* try fixing pm

* try fixing pm

* fix docs on imports, export events correctly

---------

Co-authored-by: Tim Seriakov <59409712+timseriakov@users.noreply.github.com>
Co-authored-by: cameronapak <cameronandrewpak@gmail.com>
This commit is contained in:
dswbx
2025-08-01 15:55:59 +02:00
committed by GitHub
parent daaaae82b6
commit a298b65abf
430 changed files with 15041 additions and 12375 deletions

View File

@@ -1,14 +1,6 @@
import type { PrimaryFieldType, Entity, EntityData, Field } from "bknd";
import type { FieldApi, ReactFormExtendedApi } from "@tanstack/react-form";
import type { JSX } from "react";
import {
type Entity,
type EntityData,
EnumField,
type Field,
JsonField,
JsonSchemaField,
RelationField,
} from "data";
import { useStore } from "@tanstack/react-store";
import { MediaField } from "media/MediaField";
import { type ComponentProps, Suspense } from "react";
@@ -22,7 +14,8 @@ import { EntityRelationalFormField } from "./fields/EntityRelationalFormField";
import ErrorBoundary from "ui/components/display/ErrorBoundary";
import { Alert } from "ui/components/display/Alert";
import { bkndModals } from "ui/modals";
import type { PrimaryFieldType } from "core";
import type { EnumField, JsonField, JsonSchemaField } from "data/fields";
import type { RelationField } from "data/relations";
// simplify react form types 🤦
export type FormApi = ReactFormExtendedApi<any, any, any, any, any, any, any, any, any, any>;
@@ -162,11 +155,11 @@ function EntityFormField({ fieldApi, field, action, data, ...props }: EntityForm
//const required = field.isRequired();
//const customFieldProps = { ...props, action, required };
if (field instanceof RelationField) {
if (field.type === "relation") {
return (
<EntityRelationalFormField
fieldApi={fieldApi}
field={field}
field={field as RelationField}
data={data}
disabled={props.disabled}
tabIndex={props.tabIndex}
@@ -174,15 +167,15 @@ function EntityFormField({ fieldApi, field, action, data, ...props }: EntityForm
);
}
if (field instanceof JsonField) {
return <EntityJsonFormField fieldApi={fieldApi} field={field} {...props} />;
if (field.type === "json") {
return <EntityJsonFormField fieldApi={fieldApi} field={field as JsonField} {...props} />;
}
if (field instanceof JsonSchemaField) {
if (field.type === "jsonschema") {
return (
<EntityJsonSchemaFormField
fieldApi={fieldApi}
field={field}
field={field as JsonSchemaField}
data={data}
disabled={props.disabled}
tabIndex={props.tabIndex}
@@ -191,8 +184,8 @@ function EntityFormField({ fieldApi, field, action, data, ...props }: EntityForm
);
}
if (field instanceof EnumField) {
return <EntityEnumFormField fieldApi={fieldApi} field={field} {...props} />;
if (field.type === "enum") {
return <EntityEnumFormField fieldApi={fieldApi} field={field as EnumField} {...props} />;
}
const fieldElement = field.getHtmlConfig().element;

View File

@@ -1,5 +1,5 @@
import { useToggle } from "@mantine/hooks";
import type { Entity, EntityData } from "data";
import type { Entity, EntityData } from "bknd";
import {
TbArrowDown,
TbArrowUp,

View File

@@ -1,4 +1,4 @@
import type { Entity, EntityData } from "data";
import type { Entity, EntityData } from "bknd";
import { CellValue, DataTable, type DataTableProps } from "ui/components/table/DataTable";
import ErrorBoundary from "ui/components/display/ErrorBoundary";

View File

@@ -1,4 +1,5 @@
import type { EntityData, JsonSchemaField } from "data";
import type { EntityData } from "bknd";
import type { JsonSchemaField } from "data/fields";
import * as Formy from "ui/components/form/Formy";
import { FieldLabel } from "ui/components/form/Formy";
import { JsonSchemaForm } from "ui/components/form/json-schema";

View File

@@ -1,6 +1,7 @@
import { getHotkeyHandler, useHotkeys } from "@mantine/hooks";
import { ucFirst } from "core/utils";
import type { EntityData, RelationField } from "data";
import type { EntityData } from "bknd";
import type { RelationField } from "data/relations";
import { useEffect, useRef, useState } from "react";
import { TbEye } from "react-icons/tb";
import { useEntityQuery } from "ui/client";

View File

@@ -1,7 +1,5 @@
import type { ModalProps } from "@mantine/core";
import type { ContextModalProps } from "@mantine/modals";
import { type Static, StringEnum, StringIdentifier } from "core/utils";
import { entitiesSchema, fieldsSchema, relationsSchema } from "data/data-schema";
import { useState } from "react";
import { type Modal2Ref, ModalBody, ModalFooter, ModalTitle } from "ui/components/modal/Modal2";
import { Step, Steps, useStepContext } from "ui/components/steps/Steps";
@@ -11,66 +9,16 @@ import { StepEntityFields } from "./step.entity.fields";
import { StepRelation } from "./step.relation";
import { StepSelect } from "./step.select";
import Templates from "./templates/register";
import * as tbbox from "@sinclair/typebox";
const { Type } = tbbox;
import type { TCreateModalSchema } from "./schema";
export type CreateModalRef = Modal2Ref;
export const ModalActions = ["entity", "relation", "media"] as const;
export const entitySchema = Type.Composite([
Type.Object({
name: StringIdentifier,
}),
entitiesSchema,
]);
const schemaAction = Type.Union([
StringEnum(["entity", "relation", "media"]),
Type.String({ pattern: "^template-" }),
]);
export type TSchemaAction = Static<typeof schemaAction>;
const createFieldSchema = Type.Object({
entity: StringIdentifier,
name: StringIdentifier,
field: Type.Array(fieldsSchema),
});
export type TFieldCreate = Static<typeof createFieldSchema>;
const createModalSchema = Type.Object(
{
action: schemaAction,
initial: Type.Optional(Type.Any()),
entities: Type.Optional(
Type.Object({
create: Type.Optional(Type.Array(entitySchema)),
}),
),
relations: Type.Optional(
Type.Object({
create: Type.Optional(Type.Array(Type.Union(relationsSchema))),
}),
),
fields: Type.Optional(
Type.Object({
create: Type.Optional(Type.Array(createFieldSchema)),
}),
),
},
{
additionalProperties: false,
},
);
export type TCreateModalSchema = Static<typeof createModalSchema>;
export function CreateModal({
context,
id,
innerProps: { initialPath = [], initialState },
}: ContextModalProps<{ initialPath?: string[]; initialState?: TCreateModalSchema }>) {
const [path, setPath] = useState<string[]>(initialPath);
console.log("...", initialPath, initialState);
function close() {
context.closeModal(id);
@@ -116,4 +64,4 @@ CreateModal.modalProps = {
padding: 0,
} satisfies Partial<ModalProps>;
export { ModalBody, ModalFooter, ModalTitle, useStepContext, relationsSchema };
export { ModalBody, ModalFooter, ModalTitle, useStepContext };

View File

@@ -0,0 +1,44 @@
import { s } from "bknd/utils";
import { entitiesSchema, fieldsSchema, relationsSchema } from "data/data-schema";
export const ModalActions = ["entity", "relation", "media"] as const;
export const entitySchema = s.object({
...entitiesSchema.properties,
name: s.string(),
});
// @todo: this union is not fully working, just "string"
const schemaAction = s.anyOf([
s.string({ enum: ["entity", "relation", "media"] }),
s.string({ pattern: "^template-" }),
]);
export type TSchemaAction = s.Static<typeof schemaAction>;
const createFieldSchema = s.object({
entity: s.string(),
name: s.string(),
field: s.array(fieldsSchema),
});
export type TFieldCreate = s.Static<typeof createFieldSchema>;
const createModalSchema = s.strictObject({
action: schemaAction,
initial: s.any().optional(),
entities: s
.object({
create: s.array(entitySchema).optional(),
})
.optional(),
relations: s
.object({
create: s.array(s.anyOf(relationsSchema)).optional(),
})
.optional(),
fields: s
.object({
create: s.array(createFieldSchema).optional(),
})
.optional(),
});
export type TCreateModalSchema = s.Static<typeof createModalSchema>;

View File

@@ -1,7 +1,6 @@
import { useDisclosure } from "@mantine/hooks";
import {
IconAlignJustified,
IconAugmentedReality,
IconBox,
IconCirclesRelation,
IconInfoCircle,
@@ -15,7 +14,7 @@ import { IconButton, type IconType } from "ui/components/buttons/IconButton";
import { JsonViewer } from "ui/components/code/JsonViewer";
import { ModalBody, ModalFooter } from "ui/components/modal/Modal2";
import { useStepContext } from "ui/components/steps/Steps";
import type { TCreateModalSchema } from "ui/modules/data/components/schema/create-modal/CreateModal";
import type { TCreateModalSchema } from "ui/modules/data/components/schema/create-modal/schema";
type ActionItem = SummaryItemProps & {
run: () => Promise<boolean>;
@@ -35,9 +34,9 @@ export function StepCreate() {
action: "add",
Icon: IconBox,
type: "Entity",
name: entity.name,
name: entity.name!,
json: entity,
run: async () => await $data.actions.entity.add(entity.name, entity),
run: async () => await $data.actions.entity.add(entity.name!, entity),
})),
);
}

View File

@@ -1,5 +1,4 @@
import { typeboxResolver } from "@hookform/resolvers/typebox";
import { type Static, objectCleanEmpty } from "core/utils";
import { objectCleanEmpty, type s } from "bknd/utils";
import { type TAppDataEntityFields, entitiesSchema } from "data/data-schema";
import { mergeWith } from "lodash-es";
import { useRef } from "react";
@@ -10,11 +9,13 @@ import {
EntityFieldsForm,
type EntityFieldsFormRef,
} from "ui/routes/data/forms/entity.fields.form";
import { ModalBody, ModalFooter, type TCreateModalSchema, useStepContext } from "./CreateModal";
import { ModalBody, ModalFooter, useStepContext } from "./CreateModal";
import { useBkndData } from "ui/client/schema/data/use-bknd-data";
import { standardSchemaResolver } from "@hookform/resolvers/standard-schema";
import { entitySchema, type TCreateModalSchema } from "./schema";
const schema = entitiesSchema;
type Schema = Static<typeof schema>;
const schema = entitySchema;
type Schema = s.Static<typeof schema>;
export function StepEntityFields() {
const { nextStep, stepBack, state, setState } = useStepContext<TCreateModalSchema>();
@@ -40,7 +41,7 @@ export function StepEntityFields() {
setValue,
} = useForm({
mode: "onTouched",
resolver: typeboxResolver(schema),
resolver: standardSchemaResolver(schema),
defaultValues: initial as NonNullable<Schema>,
});

View File

@@ -1,33 +1,30 @@
import { typeboxResolver } from "@hookform/resolvers/typebox";
import { standardSchemaResolver } from "@hookform/resolvers/standard-schema";
import { TextInput, Textarea } from "@mantine/core";
import { useFocusTrap } from "@mantine/hooks";
import { useForm } from "react-hook-form";
import {
ModalBody,
ModalFooter,
type TCreateModalSchema,
entitySchema,
useStepContext,
} from "./CreateModal";
import { MantineSelect } from "ui/components/form/hook-form-mantine/MantineSelect";
import { ModalBody, ModalFooter, useStepContext } from "./CreateModal";
import { entitySchema, type TCreateModalSchema } from "./schema";
import { s } from "bknd/utils";
import { cloneSchema } from "core/utils/schema";
const schema = s.object({
name: entitySchema.properties.name,
config: entitySchema.properties.config.partial().optional(),
});
type Schema = s.Static<typeof schema>;
export function StepEntity() {
const focusTrapRef = useFocusTrap();
const { nextStep, stepBack, state, setState } = useStepContext<TCreateModalSchema>();
const { register, handleSubmit, formState, watch, control } = useForm({
mode: "onTouched",
resolver: typeboxResolver(entitySchema),
defaultValues: state.entities?.create?.[0] ?? {},
mode: "onChange",
resolver: standardSchemaResolver(cloneSchema(schema)),
defaultValues: (state.entities?.create?.[0] ?? {}) as Schema,
});
/*const data = watch();
console.log("state", { isValid });
console.log("schema", JSON.stringify(entitySchema));
console.log("data", JSON.stringify(data));*/
function onSubmit(data: any) {
console.log(data);
console.log("onSubmit", data);
setState((prev) => {
const prevEntity = prev.entities?.create?.[0];
if (prevEntity && prevEntity.name !== data.name) {
@@ -47,6 +44,7 @@ export function StepEntity() {
<>
<form onSubmit={handleSubmit(onSubmit)} ref={focusTrapRef}>
<ModalBody>
<input type="hidden" {...register("type")} defaultValue="regular" />
<TextInput
data-autofocus
required

View File

@@ -1,9 +1,6 @@
import { typeboxResolver } from "@hookform/resolvers/typebox";
import { Switch, TextInput } from "@mantine/core";
import { TypeRegistry } from "@sinclair/typebox";
import { IconDatabase } from "@tabler/icons-react";
import { type Static, StringEnum, StringIdentifier, registerCustomTypeboxKinds } from "core/utils";
import { ManyToOneRelation, type RelationType, RelationTypes } from "data";
import { ManyToOneRelation, type RelationType, RelationTypes } from "data/relations";
import type { ReactNode } from "react";
import { type Control, type FieldValues, type UseFormRegister, useForm } from "react-hook-form";
import { TbRefresh } from "react-icons/tb";
@@ -13,12 +10,10 @@ import { MantineNumberInput } from "ui/components/form/hook-form-mantine/Mantine
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";
import * as tbbox from "@sinclair/typebox";
const { Type } = tbbox;
// @todo: check if this could become an issue
registerCustomTypeboxKinds(TypeRegistry);
import { ModalBody, ModalFooter } from "./CreateModal";
import type { TCreateModalSchema } from "./schema";
import { s, stringIdentifier } from "bknd/utils";
import { standardSchemaResolver } from "@hookform/resolvers/standard-schema";
const Relations: {
type: RelationType;
@@ -47,11 +42,11 @@ const Relations: {
},
];
const schema = Type.Object({
type: StringEnum(Relations.map((r) => r.type)),
source: StringIdentifier,
target: StringIdentifier,
config: Type.Object({}),
const schema = s.strictObject({
type: s.string({ enum: Relations.map((r) => r.type) }),
source: stringIdentifier,
target: stringIdentifier,
config: s.object({}),
});
type ComponentCtx<T extends FieldValues = FieldValues> = {
@@ -73,8 +68,8 @@ export function StepRelation() {
watch,
control,
} = useForm({
resolver: typeboxResolver(schema),
defaultValues: (state.relations?.create?.[0] ?? {}) as Static<typeof schema>,
resolver: standardSchemaResolver(schema),
defaultValues: (state.relations?.create?.[0] ?? {}) as s.Static<typeof schema>,
});
const data = watch();

View File

@@ -1,15 +1,9 @@
import type { IconType } from "react-icons";
import { TbBox, TbCirclesRelation, TbPhoto } from "react-icons/tb";
import { twMerge } from "tailwind-merge";
import {
type ModalActions,
ModalBody,
ModalFooter,
type TCreateModalSchema,
type TSchemaAction,
useStepContext,
} from "./CreateModal";
import { ModalBody, ModalFooter, useStepContext } from "./CreateModal";
import Templates from "./templates/register";
import type { TCreateModalSchema, TSchemaAction } from "./schema";
export function StepSelect() {
const { nextStep, stepBack, state, path, setState } = useStepContext<TCreateModalSchema>();

View File

@@ -1,6 +1,5 @@
import { typeboxResolver } from "@hookform/resolvers/typebox";
import { Radio, TextInput } from "@mantine/core";
import { Default, type Static, StringEnum, StringIdentifier, transformObject } from "core/utils";
import { transformObject, s, stringIdentifier } from "bknd/utils";
import type { MediaFieldConfig } from "media/MediaField";
import { useEffect, useState } from "react";
import { useForm } from "react-hook-form";
@@ -8,23 +7,17 @@ import { useBknd } from "ui/client/bknd";
import { MantineNumberInput } from "ui/components/form/hook-form-mantine/MantineNumberInput";
import { MantineRadio } from "ui/components/form/hook-form-mantine/MantineRadio";
import { MantineSelect } from "ui/components/form/hook-form-mantine/MantineSelect";
import {
ModalBody,
ModalFooter,
type TCreateModalSchema,
type TFieldCreate,
useStepContext,
} from "../../CreateModal";
import * as tbbox from "@sinclair/typebox";
const { Type } = tbbox;
import { ModalBody, ModalFooter, useStepContext } from "../../CreateModal";
import type { TCreateModalSchema, TFieldCreate } from "../../schema";
import { standardSchemaResolver } from "@hookform/resolvers/standard-schema";
const schema = Type.Object({
entity: StringIdentifier,
cardinality_type: StringEnum(["single", "multiple"], { default: "multiple" }),
cardinality: Type.Optional(Type.Number({ minimum: 1 })),
name: StringIdentifier,
const schema = s.object({
entity: stringIdentifier,
cardinality_type: s.string({ enum: ["single", "multiple"], default: "multiple" }),
cardinality: s.number({ minimum: 1 }).optional(),
name: stringIdentifier,
});
type TCreateModalMediaSchema = Static<typeof schema>;
type TCreateModalMediaSchema = s.Static<typeof schema>;
export function TemplateMediaComponent() {
const { stepBack, setState, state, path, nextStep } = useStepContext<TCreateModalSchema>();
@@ -36,8 +29,9 @@ export function TemplateMediaComponent() {
control,
} = useForm({
mode: "onChange",
resolver: typeboxResolver(schema),
defaultValues: Default(schema, state.initial ?? {}) as TCreateModalMediaSchema,
resolver: standardSchemaResolver(schema),
defaultValues: schema.template(state.initial ?? {}) as TCreateModalMediaSchema,
//defaultValues: Default(schema, state.initial ?? {}) as TCreateModalMediaSchema,
});
const [forbidden, setForbidden] = useState<boolean>(false);

View File

@@ -1,5 +1,5 @@
import { useForm } from "@tanstack/react-form";
import type { Entity, EntityData } from "data";
import type { Entity, EntityData } from "bknd";
import { getChangeSet, getDefaultValues } from "data/helper";
type EntityFormProps = {

View File

@@ -1,11 +1,10 @@
import { Handle, type Node, type NodeProps, Position } from "@xyflow/react";
import { Const, transformObject } from "core/utils";
import { transformObject } from "core/utils";
import { type Trigger, TriggerMap } from "flows";
import type { IconType } from "react-icons";
import { TbCircleLetterT } from "react-icons/tb";
import { JsonSchemaForm } from "ui/components/form/json-schema";
import * as tbbox from "@sinclair/typebox";
const { Type } = tbbox;
import { s } from "bknd/utils";
export type TaskComponentProps = NodeProps<Node<{ trigger: Trigger }>> & {
Icon?: IconType;
@@ -14,9 +13,9 @@ export type TaskComponentProps = NodeProps<Node<{ trigger: Trigger }>> & {
const triggerSchemas = Object.values(
transformObject(TriggerMap, (trigger, name) =>
Type.Object(
s.object(
{
type: Const(name),
type: s.literal(name),
config: trigger.cls.schema,
},
{ title: String(name), additionalProperties: false },
@@ -47,7 +46,7 @@ export function TriggerComponent({
<div className="flex flex-col gap-2 px-3 py-2">
<JsonSchemaForm
className="legacy"
schema={Type.Union(triggerSchemas)}
schema={s.anyOf(triggerSchemas)}
onChange={console.log}
formData={trigger}
{...props}

View File

@@ -1,6 +1,6 @@
import type { FieldProps, FormContextType, RJSFSchema, StrictRJSFSchema } from "@rjsf/utils";
import { SimpleRenderer } from "core";
import { ucFirst, ucFirstAll } from "core/utils";
import { SimpleRenderer } from "core/template/SimpleRenderer";
import { ucFirst, ucFirstAll } from "bknd/utils";
import { useState } from "react";
const modes = ["field", "code"] as const;

View File

@@ -1,30 +1,26 @@
import { typeboxResolver } from "@hookform/resolvers/typebox";
import { Input, NativeSelect, Select, TextInput } from "@mantine/core";
import { Input, TextInput } from "@mantine/core";
import { useToggle } from "@mantine/hooks";
import { IconMinus, IconPlus, IconWorld } from "@tabler/icons-react";
import type { Node, NodeProps } from "@xyflow/react";
import type { Static } from "core/utils";
import { s } from "bknd/utils";
import { FetchTask } from "flows";
import { useRef, useState } from "react";
import { useState } from "react";
import { useForm } from "react-hook-form";
import { Button } from "ui/components/buttons/Button";
import { JsonViewer } from "ui/components/code/JsonViewer";
import { SegmentedControl } from "ui/components/form/SegmentedControl";
import { MantineSelect } from "ui/components/form/hook-form-mantine/MantineSelect";
import { type TFlowNodeData, useFlowSelector } from "../../../hooks/use-flow";
import type { TFlowNodeData } from "../../../hooks/use-flow";
import { KeyValueInput } from "../../form/KeyValueInput";
import { BaseNode } from "../BaseNode";
import * as tbbox from "@sinclair/typebox";
const { Type } = tbbox;
import { standardSchemaResolver } from "@hookform/resolvers/standard-schema";
const schema = Type.Composite([
FetchTask.schema,
Type.Object({
query: Type.Optional(Type.Record(Type.String(), Type.String())),
}),
]);
const schema = s.object({
query: s.record(s.string()).optional(),
...FetchTask.schema.properties,
});
type TFetchTaskSchema = Static<typeof FetchTask.schema>;
type TFetchTaskSchema = s.Static<typeof FetchTask.schema>;
type FetchTaskFormProps = NodeProps<Node<TFlowNodeData>> & {
params: TFetchTaskSchema;
onChange: (params: any) => void;
@@ -42,8 +38,8 @@ export function FetchTaskForm({ onChange, params, ...props }: FetchTaskFormProps
watch,
control,
} = useForm({
resolver: typeboxResolver(schema),
defaultValues: params as Static<typeof schema>,
resolver: standardSchemaResolver(schema),
defaultValues: params as s.Static<typeof schema>,
mode: "onChange",
//defaultValues: (state.relations?.create?.[0] ?? {}) as Static<typeof schema>
});

View File

@@ -1,14 +1,10 @@
import { TypeRegistry } from "@sinclair/typebox";
import { type Node, type NodeProps, Position } from "@xyflow/react";
import { registerCustomTypeboxKinds } from "core/utils";
import type { TAppFlowTaskSchema } from "flows/AppFlows";
import { useFlowCanvas, useFlowSelector } from "../../../hooks/use-flow";
import { Handle } from "../Handle";
import { FetchTaskForm } from "./FetchTaskNode";
import { RenderNode } from "./RenderNode";
registerCustomTypeboxKinds(TypeRegistry);
const TaskComponents = {
fetch: FetchTaskForm,
render: RenderNode,

View File

@@ -1,7 +1,6 @@
import { typeboxResolver } from "@hookform/resolvers/typebox";
import { TextInput } from "@mantine/core";
import type { Node, NodeProps } from "@xyflow/react";
import { Const, type Static, registerCustomTypeboxKinds, transformObject } from "core/utils";
import { transformObject } from "core/utils";
import { TriggerMap } from "flows";
import type { TAppFlowTriggerSchema } from "flows/AppFlows";
import { useForm } from "react-hook-form";
@@ -11,22 +10,19 @@ import { MantineSelect } from "ui/components/form/hook-form-mantine/MantineSelec
import { useFlowCanvas, useFlowSelector } from "../../../hooks/use-flow";
import { BaseNode } from "../BaseNode";
import { Handle } from "../Handle";
import * as tb from "@sinclair/typebox";
const { Type, TypeRegistry } = tb;
import { s } from "bknd/utils";
import { standardSchemaResolver } from "@hookform/resolvers/standard-schema";
// @todo: check if this could become an issue
registerCustomTypeboxKinds(TypeRegistry);
const schema = Type.Object({
trigger: Type.Union(
const schema = s.object({
trigger: s.anyOf(
Object.values(
transformObject(TriggerMap, (trigger, name) =>
Type.Object(
s.strictObject(
{
type: Const(name),
type: s.literal(name),
config: trigger.cls.schema,
},
{ title: String(name), additionalProperties: false },
{ title: String(name) },
),
),
),
@@ -50,13 +46,13 @@ export const TriggerNode = (props: NodeProps<Node<TAppFlowTriggerSchema & { labe
watch,
control,
} = useForm({
resolver: typeboxResolver(schema),
defaultValues: { trigger: state } as Static<typeof schema>,
resolver: standardSchemaResolver(schema),
defaultValues: { trigger: state } as s.Static<typeof schema>,
mode: "onChange",
});
const data = watch("trigger");
async function onSubmit(data: Static<typeof schema>) {
async function onSubmit(data: s.Static<typeof schema>) {
console.log("submit", data.trigger);
// @ts-ignore
await actions.trigger.update(data.trigger);

View File

@@ -46,7 +46,7 @@ export const flowStateAtom = atom<TFlowState>({
const FlowCanvasContext = createContext<FlowContextType>(undefined!);
const DEFAULT_FLOW = { trigger: {}, tasks: {}, connections: {} };
const DEFAULT_FLOW: TAppFlowSchema = { trigger: { type: "manual" }, tasks: {}, connections: {} };
export function FlowCanvasProvider({ children, name }: { children: any; name?: string }) {
//const [dirty, setDirty] = useState(false);
const setFlowState = useSetAtom(flowStateAtom);
@@ -71,7 +71,7 @@ export function FlowCanvasProvider({ children, name }: { children: any; name?: s
update: async (trigger: TAppFlowTriggerSchema | any) => {
console.log("update trigger", trigger);
setFlowState((state) => {
const flow = state.flow || DEFAULT_FLOW;
const flow = state.flow || (DEFAULT_FLOW as any);
return { ...state, dirty: true, flow: { ...flow, trigger } };
});
//return s.actions.patch("flows", `flows.flows.${name}`, { trigger });