From 602235b372c619833f4b258e9696f607ac525a2b Mon Sep 17 00:00:00 2001 From: dswbx Date: Wed, 18 Dec 2024 18:22:01 +0100 Subject: [PATCH 1/7] optimized module manager seeding, added type support for new api hooks and reduced amount of dist chunks --- app/__test__/data/prototype.test.ts | 12 ++- app/build.ts | 50 ++++++++-- app/package.json | 35 +++---- app/src/Api.ts | 2 +- app/src/App.ts | 10 +- app/src/auth/AppAuth.ts | 2 +- app/src/core/utils/test.ts | 23 ++++- app/src/data/api/DataApi.ts | 64 +++++++------ app/src/data/api/DataController.ts | 7 +- app/src/data/entities/EntityManager.ts | 44 ++++----- app/src/data/entities/Mutator.ts | 63 +++++-------- app/src/data/entities/query/Repository.ts | 2 +- app/src/data/prototype/index.ts | 26 ++++-- app/src/index.ts | 6 +- app/src/media/api/MediaController.ts | 2 +- app/src/modules/Module.ts | 4 +- app/src/modules/ModuleManager.ts | 22 ++++- app/src/ui/client/api/use-api.ts | 4 +- app/src/ui/client/api/use-entity.ts | 66 +++++++++----- app/src/ui/components/code/CodeEditor.tsx | 19 ++-- .../form/json-schema/JsonSchemaForm.tsx | 7 +- .../ui/components/form/json-schema/index.tsx | 18 ++++ app/src/ui/modals/debug/SchemaFormModal.tsx | 2 +- .../fields/EntityJsonSchemaFormField.tsx | 40 ++++---- .../flows/components/TriggerComponent.tsx | 29 +++--- .../flows/components/form/TaskForm.tsx | 29 ++---- app/src/ui/routes/auth/auth.settings.tsx | 5 +- app/src/ui/routes/auth/auth.strategies.tsx | 2 - .../ui/routes/data/data.schema.$entity.tsx | 5 +- .../routes/data/forms/entity.fields.form.tsx | 2 +- app/src/ui/routes/index.tsx | 9 +- .../ui/routes/settings/components/Setting.tsx | 5 +- .../settings/components/SettingNewModal.tsx | 15 ++- .../test/tests/flow-create-schema-test.tsx | 2 +- .../routes/test/tests/jsonform-test/index.tsx | 7 +- .../ui/routes/test/tests/query-jsonform.tsx | 86 +++++++++--------- app/src/ui/routes/test/tests/schema-test.tsx | 6 +- app/src/ui/routes/test/tests/swr-and-api.tsx | 17 +++- .../ui/routes/test/tests/swr-and-data-api.tsx | 6 +- app/tsconfig.json | 7 +- bun.lockb | Bin 1084120 -> 1086016 bytes 41 files changed, 434 insertions(+), 328 deletions(-) create mode 100644 app/src/ui/components/form/json-schema/index.tsx diff --git a/app/__test__/data/prototype.test.ts b/app/__test__/data/prototype.test.ts index e5d3753..9d3eebd 100644 --- a/app/__test__/data/prototype.test.ts +++ b/app/__test__/data/prototype.test.ts @@ -3,6 +3,7 @@ import { BooleanField, DateField, Entity, + EntityManager, EnumField, JsonField, ManyToManyRelation, @@ -46,12 +47,17 @@ describe("prototype", () => { }); test("...2", async () => { - const user = entity("users", { - name: text().required(), + const users = entity("users", { + name: text(), bio: text(), age: number(), - some: number().required() + some: number() }); + type db = { + users: Schema; + }; + + const obj: Schema = {} as any; //console.log("user", user.toJSON()); }); diff --git a/app/build.ts b/app/build.ts index 60251f8..6511124 100644 --- a/app/build.ts +++ b/app/build.ts @@ -9,16 +9,44 @@ const watch = args.includes("--watch"); const minify = args.includes("--minify"); const types = args.includes("--types"); const sourcemap = args.includes("--sourcemap"); +const clean = args.includes("--clean"); + +if (clean) { + console.log("Cleaning dist"); + await $`rm -rf dist`; +} + +let types_running = false; +function buildTypes() { + if (types_running) return; + types_running = true; -await $`rm -rf dist`; -if (types) { Bun.spawn(["bun", "build:types"], { onExit: () => { console.log("Types built"); + Bun.spawn(["bun", "tsc-alias"], { + onExit: () => { + console.log("Types aliased"); + types_running = false; + } + }); } }); } +let watcher_timeout: any; +function delayTypes() { + if (!watch) return; + if (watcher_timeout) { + clearTimeout(watcher_timeout); + } + watcher_timeout = setTimeout(buildTypes, 1000); +} + +if (types && !watch) { + buildTypes(); +} + /** * Build static assets * Using esbuild because tsup doesn't include "react" @@ -46,7 +74,8 @@ const result = await esbuild.build({ __isDev: "0", "process.env.NODE_ENV": '"production"' }, - chunkNames: "chunks/[name]-[hash]" + chunkNames: "chunks/[name]-[hash]", + logLevel: "error" }); // Write manifest @@ -96,6 +125,9 @@ await tsup.build({ treeshake: true, loader: { ".svg": "dataurl" + }, + onSuccess: async () => { + delayTypes(); } }); @@ -117,11 +149,12 @@ await tsup.build({ loader: { ".svg": "dataurl" }, - onSuccess: async () => { - console.log("--- ui built"); - }, esbuildOptions: (options) => { + options.logLevel = "silent"; options.chunkNames = "chunks/[name]-[hash]"; + }, + onSuccess: async () => { + delayTypes(); } }); @@ -148,7 +181,10 @@ function baseConfig(adapter: string): tsup.Options { ], metafile: true, splitting: false, - treeshake: true + treeshake: true, + onSuccess: async () => { + delayTypes(); + } }; } diff --git a/app/package.json b/app/package.json index fb02b8e..4e188a1 100644 --- a/app/package.json +++ b/app/package.json @@ -5,14 +5,14 @@ "bin": "./dist/cli/index.js", "version": "0.3.4-alpha1", "scripts": { - "build:all": "bun run build && bun run build:cli", + "build:all": "NODE_ENV=production bun run build.ts --minify --types --clean && bun run build:cli", "dev": "vite", "test": "ALL_TESTS=1 bun test --bail", "build": "NODE_ENV=production bun run build.ts --minify --types", "watch": "bun run build.ts --types --watch", "types": "bun tsc --noEmit", "clean:types": "find ./dist -name '*.d.ts' -delete && rm -f ./dist/tsconfig.tsbuildinfo", - "build:types": "tsc --emitDeclarationOnly", + "build:types": "tsc --emitDeclarationOnly && tsc-alias", "build:css": "bun tailwindcss -i src/ui/main.css -o ./dist/static/styles.css", "watch:css": "bun tailwindcss --watch -i src/ui/main.css -o ./dist/styles.css", "updater": "bun x npm-check-updates -ui", @@ -75,6 +75,7 @@ "tailwind-merge": "^2.5.4", "tailwindcss": "^3.4.14", "tailwindcss-animate": "^1.0.7", + "tsc-alias": "^1.8.10", "tsup": "^8.3.5", "vite": "^5.4.10", "vite-plugin-static-copy": "^2.0.0", @@ -90,75 +91,75 @@ }, "main": "./dist/index.js", "module": "./dist/index.js", - "types": "./dist/index.d.ts", + "types": "./dist/types/index.d.ts", "exports": { ".": { - "types": "./dist/index.d.ts", + "types": "./dist/types/index.d.ts", "import": "./dist/index.js", "require": "./dist/index.cjs" }, "./ui": { - "types": "./dist/ui/index.d.ts", + "types": "./dist/types/ui/index.d.ts", "import": "./dist/ui/index.js", "require": "./dist/ui/index.cjs" }, "./client": { - "types": "./dist/ui/client/index.d.ts", + "types": "./dist/types/ui/client/index.d.ts", "import": "./dist/ui/client/index.js", "require": "./dist/ui/client/index.cjs" }, "./data": { - "types": "./dist/data/index.d.ts", + "types": "./dist/types/data/index.d.ts", "import": "./dist/data/index.js", "require": "./dist/data/index.cjs" }, "./core": { - "types": "./dist/core/index.d.ts", + "types": "./dist/types/core/index.d.ts", "import": "./dist/core/index.js", "require": "./dist/core/index.cjs" }, "./utils": { - "types": "./dist/core/utils/index.d.ts", + "types": "./dist/types/core/utils/index.d.ts", "import": "./dist/core/utils/index.js", "require": "./dist/core/utils/index.cjs" }, "./cli": { - "types": "./dist/cli/index.d.ts", + "types": "./dist/types/cli/index.d.ts", "import": "./dist/cli/index.js", "require": "./dist/cli/index.cjs" }, "./adapter/cloudflare": { - "types": "./dist/adapter/cloudflare/index.d.ts", + "types": "./dist/types/adapter/cloudflare/index.d.ts", "import": "./dist/adapter/cloudflare/index.js", "require": "./dist/adapter/cloudflare/index.cjs" }, "./adapter/vite": { - "types": "./dist/adapter/vite/index.d.ts", + "types": "./dist/types/adapter/vite/index.d.ts", "import": "./dist/adapter/vite/index.js", "require": "./dist/adapter/vite/index.cjs" }, "./adapter/nextjs": { - "types": "./dist/adapter/nextjs/index.d.ts", + "types": "./dist/types/adapter/nextjs/index.d.ts", "import": "./dist/adapter/nextjs/index.js", "require": "./dist/adapter/nextjs/index.cjs" }, "./adapter/remix": { - "types": "./dist/adapter/remix/index.d.ts", + "types": "./dist/types/adapter/remix/index.d.ts", "import": "./dist/adapter/remix/index.js", "require": "./dist/adapter/remix/index.cjs" }, "./adapter/bun": { - "types": "./dist/adapter/bun/index.d.ts", + "types": "./dist/types/adapter/bun/index.d.ts", "import": "./dist/adapter/bun/index.js", "require": "./dist/adapter/bun/index.cjs" }, "./adapter/node": { - "types": "./dist/adapter/node/index.d.ts", + "types": "./dist/types/adapter/node/index.d.ts", "import": "./dist/adapter/node/index.js", "require": "./dist/adapter/node/index.cjs" }, "./adapter/astro": { - "types": "./dist/adapter/astro/index.d.ts", + "types": "./dist/types/adapter/astro/index.d.ts", "import": "./dist/adapter/astro/index.js", "require": "./dist/adapter/astro/index.cjs" }, diff --git a/app/src/Api.ts b/app/src/Api.ts index 5196622..d6c99fe 100644 --- a/app/src/Api.ts +++ b/app/src/Api.ts @@ -38,7 +38,7 @@ export class Api { private token_transport: "header" | "cookie" | "none" = "header"; public system!: SystemApi; - public data!: DataApi; + public data!: DataApi; public auth!: AuthApi; public media!: MediaApi; diff --git a/app/src/App.ts b/app/src/App.ts index d180a51..af68f58 100644 --- a/app/src/App.ts +++ b/app/src/App.ts @@ -37,6 +37,7 @@ export type AppConfig = InitialModuleConfigs; export class App { modules: ModuleManager; static readonly Events = AppEvents; + adminController?: AdminController; constructor( private connection: Connection, @@ -94,8 +95,12 @@ export class App { return this.modules.get(module).schema(); } + get server() { + return this.modules.server; + } + get fetch(): any { - return this.modules.server.fetch; + return this.server.fetch; } get module() { @@ -119,7 +124,8 @@ export class App { registerAdminController(config?: AdminControllerOptions) { // register admin - this.modules.server.route("/", new AdminController(this, config).getController()); + this.adminController = new AdminController(this, config); + this.modules.server.route("/", this.adminController.getController()); return this; } diff --git a/app/src/auth/AppAuth.ts b/app/src/auth/AppAuth.ts index ba7b00d..9a1c708 100644 --- a/app/src/auth/AppAuth.ts +++ b/app/src/auth/AppAuth.ts @@ -197,7 +197,7 @@ export class AppAuth extends Module { throw new Exception("User already exists"); } - const payload = { + const payload: any = { ...profile, strategy: strategy.getName(), strategy_value: identifier diff --git a/app/src/core/utils/test.ts b/app/src/core/utils/test.ts index cf33e1a..b06ac55 100644 --- a/app/src/core/utils/test.ts +++ b/app/src/core/utils/test.ts @@ -9,10 +9,25 @@ export async function withDisabledConsole( fn: () => Promise, severities: ConsoleSeverity[] = ["log"] ): Promise { - const enable = disableConsoleLog(severities); - const result = await fn(); - enable(); - return result; + const _oldConsoles = { + log: console.log, + warn: console.warn, + error: console.error + }; + disableConsoleLog(severities); + const enable = () => { + Object.entries(_oldConsoles).forEach(([severity, fn]) => { + console[severity as ConsoleSeverity] = fn; + }); + }; + try { + const result = await fn(); + enable(); + return result; + } catch (e) { + enable(); + throw e; + } } export function disableConsoleLog(severities: ConsoleSeverity[] = ["log"]) { diff --git a/app/src/data/api/DataApi.ts b/app/src/data/api/DataApi.ts index 967a5f1..caf6a2a 100644 --- a/app/src/data/api/DataApi.ts +++ b/app/src/data/api/DataApi.ts @@ -5,7 +5,7 @@ export type DataApiOptions = BaseModuleApiOptions & { defaultQuery?: Partial; }; -export class DataApi extends ModuleApi { +export class DataApi extends ModuleApi { protected override getDefaultOptions(): Partial { return { basepath: "/api/data", @@ -15,48 +15,60 @@ export class DataApi extends ModuleApi { }; } - readOne( - entity: string, + readOne( + entity: E, id: PrimaryFieldType, query: Partial> = {} ) { - return this.get>([entity, id], query); + return this.get, "meta" | "data">>([entity as any, id], query); } - readMany(entity: string, query: Partial = {}) { - return this.get>( - [entity], - query ?? this.options.defaultQuery - ); - } - - readManyByReference( - entity: string, - id: PrimaryFieldType, - reference: string, + readMany( + entity: E, query: Partial = {} ) { - return this.get>( - [entity, id, reference], + return this.get, "meta" | "data">>( + [entity as any], query ?? this.options.defaultQuery ); } - createOne(entity: string, input: EntityData) { - return this.post>([entity], input); + readManyByReference< + 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 = {}) { + return this.get, "meta" | "data">>( + [entity as any, id, reference], + query ?? this.options.defaultQuery + ); } - updateOne(entity: string, id: PrimaryFieldType, input: EntityData) { - return this.patch>([entity, id], input); + createOne( + entity: E, + input: Data + ) { + return this.post>([entity as any], input); } - deleteOne(entity: string, id: PrimaryFieldType) { - return this.delete>([entity, id]); + updateOne( + entity: E, + id: PrimaryFieldType, + input: Partial + ) { + return this.patch>([entity as any, id], input); } - count(entity: string, where: RepoQuery["where"] = {}) { - return this.post>( - [entity, "fn", "count"], + deleteOne( + entity: E, + id: PrimaryFieldType + ) { + return this.delete>([entity as any, id]); + } + + count(entity: E, where: RepoQuery["where"] = {}) { + return this.post>( + [entity as any, "fn", "count"], where ); } diff --git a/app/src/data/api/DataController.ts b/app/src/data/api/DataController.ts index 3f459dc..86f3e42 100644 --- a/app/src/data/api/DataController.ts +++ b/app/src/data/api/DataController.ts @@ -165,13 +165,12 @@ export class DataController implements ClassController { // read entity schema .get("/schema.json", async (c) => { this.guard.throwUnlessGranted(DataPermissions.entityRead); - const url = new URL(c.req.url); - const $id = `${url.origin}${this.config.basepath}/schema.json`; + const $id = `${this.config.basepath}/schema.json`; const schemas = Object.fromEntries( this.em.entities.map((e) => [ e.name, { - $ref: `schemas/${e.name}` + $ref: `${this.config.basepath}/schemas/${e.name}` } ]) ); @@ -198,7 +197,7 @@ export class DataController implements ClassController { const schema = _entity.toSchema(); const url = new URL(c.req.url); const base = `${url.origin}${this.config.basepath}`; - const $id = `${base}/schemas/${entity}`; + const $id = `${this.config.basepath}/schemas/${entity}`; return c.json({ $schema: `${base}/schema.json`, $id, diff --git a/app/src/data/entities/EntityManager.ts b/app/src/data/entities/EntityManager.ts index 674d7e2..d34c728 100644 --- a/app/src/data/entities/EntityManager.ts +++ b/app/src/data/entities/EntityManager.ts @@ -14,6 +14,14 @@ import { SchemaManager } from "../schema/SchemaManager"; import { Entity } from "./Entity"; import { type EntityData, Mutator, Repository } from "./index"; +type EntitySchema = E extends Entity + ? Name extends keyof DB + ? Name + : never + : E extends keyof DB + ? E + : never; + export class EntityManager { connection: Connection; @@ -87,10 +95,16 @@ export class EntityManager { this.entities.push(entity); } - entity(name: string): Entity { - const entity = this.entities.find((e) => e.name === name); + entity(e: Entity | string): Entity { + let entity: Entity | undefined; + if (typeof e === "string") { + entity = this.entities.find((entity) => entity.name === e); + } else { + entity = e; + } + if (!entity) { - throw new EntityNotDefinedException(name); + throw new EntityNotDefinedException(typeof e === "string" ? e : e.name); } return entity; @@ -162,28 +176,16 @@ export class EntityManager { return this.relations.relationReferencesOf(this.entity(entity_name)); } - repository(_entity: Entity | string) { - const entity = _entity instanceof Entity ? _entity : this.entity(_entity); - return new Repository(this, entity, this.emgr); + repository(entity: E): Repository> { + return this.repo(entity); } - repo( - _entity: E - ): Repository< - DB, - E extends Entity ? (Name extends keyof DB ? Name : never) : never - > { - return new Repository(this, _entity, this.emgr); + repo(entity: E): Repository> { + return new Repository(this, this.entity(entity), this.emgr); } - _repo(_entity: TB): Repository { - const entity = this.entity(_entity as any); - return new Repository(this, entity, this.emgr); - } - - mutator(_entity: Entity | string) { - const entity = _entity instanceof Entity ? _entity : this.entity(_entity); - return new Mutator(this, entity, this.emgr); + mutator(entity: E): Mutator> { + return new Mutator(this, this.entity(entity), this.emgr); } addIndex(index: EntityIndex, force = false) { diff --git a/app/src/data/entities/Mutator.ts b/app/src/data/entities/Mutator.ts index ed7f9ef..53b4cb2 100644 --- a/app/src/data/entities/Mutator.ts +++ b/app/src/data/entities/Mutator.ts @@ -25,7 +25,9 @@ export type MutatorResponse = { data: T; }; -export class Mutator implements EmitsEvents { +export class Mutator> + implements EmitsEvents +{ em: EntityManager; entity: Entity; static readonly Events = MutatorEvents; @@ -47,13 +49,13 @@ export class Mutator implements EmitsEvents { return this.em.connection.kysely; } - async getValidatedData(data: EntityData, context: TActionContext): Promise { + async getValidatedData(data: Given, context: TActionContext): Promise { const entity = this.entity; if (!context) { throw new Error("Context must be provided for validation"); } - const keys = Object.keys(data); + const keys = Object.keys(data as any); const validatedData: EntityData = {}; // get relational references/keys @@ -95,7 +97,7 @@ export class Mutator implements EmitsEvents { throw new Error(`No data left to update "${entity.name}"`); } - return validatedData; + return validatedData as Given; } protected async many(qb: MutatorQB): Promise { @@ -120,7 +122,7 @@ export class Mutator implements EmitsEvents { return { ...response, data: data[0]! }; } - async insertOne(data: EntityData): Promise> { + async insertOne(data: Data): Promise> { const entity = this.entity; if (entity.type === "system" && this.__unstable_disable_system_entity_creation) { throw new Error(`Creation of system entity "${entity.name}" is disabled`); @@ -154,10 +156,10 @@ export class Mutator implements EmitsEvents { await this.emgr.emit(new Mutator.Events.MutatorInsertAfter({ entity, data: res.data })); - return res; + return res as any; } - async updateOne(id: PrimaryFieldType, data: EntityData): Promise> { + async updateOne(id: PrimaryFieldType, data: Data): Promise> { const entity = this.entity; if (!Number.isInteger(id)) { throw new Error("ID must be provided for update"); @@ -166,12 +168,16 @@ export class Mutator implements EmitsEvents { const validatedData = await this.getValidatedData(data, "update"); await this.emgr.emit( - new Mutator.Events.MutatorUpdateBefore({ entity, entityId: id, data: validatedData }) + new Mutator.Events.MutatorUpdateBefore({ + entity, + entityId: id, + data: validatedData as any + }) ); const query = this.conn .updateTable(entity.name) - .set(validatedData) + .set(validatedData as any) .where(entity.id().name, "=", id) .returning(entity.getSelect()); @@ -181,10 +187,10 @@ export class Mutator implements EmitsEvents { new Mutator.Events.MutatorUpdateAfter({ entity, entityId: id, data: res.data }) ); - return res; + return res as any; } - async deleteOne(id: PrimaryFieldType): Promise> { + async deleteOne(id: PrimaryFieldType): Promise> { const entity = this.entity; if (!Number.isInteger(id)) { throw new Error("ID must be provided for deletion"); @@ -203,7 +209,7 @@ export class Mutator implements EmitsEvents { new Mutator.Events.MutatorDeleteAfter({ entity, entityId: id, data: res.data }) ); - return res; + return res as any; } private getValidOptions(options?: Partial): Partial { @@ -250,47 +256,24 @@ export class Mutator implements EmitsEvents { } // @todo: decide whether entries should be deleted all at once or one by one (for events) - async deleteWhere(where?: RepoQuery["where"]): Promise> { + async deleteWhere(where?: RepoQuery["where"]): Promise> { const entity = this.entity; const qb = this.appendWhere(this.conn.deleteFrom(entity.name), where).returning( entity.getSelect() ); - //await this.emgr.emit(new Mutator.Events.MutatorDeleteBefore({ entity, entityId: id })); - - const res = await this.many(qb); - - /*await this.emgr.emit( - new Mutator.Events.MutatorDeleteAfter({ entity, entityId: id, data: res.data }) - );*/ - - return res; + return (await this.many(qb)) as any; } - async updateWhere( - data: EntityData, - where?: RepoQuery["where"] - ): Promise> { + async updateWhere(data: Data, where?: RepoQuery["where"]): Promise> { const entity = this.entity; - const validatedData = await this.getValidatedData(data, "update"); - /*await this.emgr.emit( - new Mutator.Events.MutatorUpdateBefore({ entity, entityId: id, data: validatedData }) - );*/ - const query = this.appendWhere(this.conn.updateTable(entity.name), where) - .set(validatedData) - //.where(entity.id().name, "=", id) + .set(validatedData as any) .returning(entity.getSelect()); - const res = await this.many(query); - - /*await this.emgr.emit( - new Mutator.Events.MutatorUpdateAfter({ entity, entityId: id, data: res.data }) - );*/ - - return res; + return (await this.many(query)) as any; } } diff --git a/app/src/data/entities/query/Repository.ts b/app/src/data/entities/query/Repository.ts index f5b576c..4391b32 100644 --- a/app/src/data/entities/query/Repository.ts +++ b/app/src/data/entities/query/Repository.ts @@ -272,7 +272,7 @@ export class Repository implements EmitsEve async findId( id: PrimaryFieldType, _options?: Partial> - ): Promise> { + ): Promise> { const { qb, options } = this.buildQuery( { ..._options, diff --git a/app/src/data/prototype/index.ts b/app/src/data/prototype/index.ts index d526c2e..8bdd07b 100644 --- a/app/src/data/prototype/index.ts +++ b/app/src/data/prototype/index.ts @@ -1,3 +1,5 @@ +import type { Generated } from "kysely"; +import { MediaField, type MediaFieldConfig, type MediaItem } from "media/MediaField"; import { BooleanField, type BooleanFieldConfig, @@ -25,15 +27,14 @@ import { type TEntityType, TextField, type TextFieldConfig -} from "data"; -import type { Generated } from "kysely"; -import { MediaField, type MediaFieldConfig, type MediaItem } from "media/MediaField"; +} from "../index"; type Options = { entity: { name: string; fields: Record> }; field_name: string; config: Config; is_required: boolean; + another?: string; }; const FieldMap = { @@ -239,7 +240,7 @@ export function relation(local: Local) { }; } -type InferEntityFields = T extends Entity +export type InferEntityFields = T extends Entity ? { [K in keyof Fields]: Fields[K] extends { _type: infer Type; _required: infer Required } ? Required extends true @@ -284,12 +285,25 @@ type OptionalUndefined< } >; -type InferField = Field extends { _type: infer Type; _required: infer Required } +export type InferField = Field extends { _type: infer Type; _required: infer Required } ? Required extends true ? Type : Type | undefined : never; +const n = number(); +type T2 = InferField; + +const users = entity("users", { + name: text(), + email: text(), + created_at: datetime(), + updated_at: datetime() +}); +type TUsersFields = InferEntityFields; +type TUsers = Schema; +type TUsers2 = Simplify>>; + export type InsertSchema = Simplify>>; -export type Schema = { id: Generated } & InsertSchema; +export type Schema = Simplify<{ id: Generated } & InsertSchema>; export type FieldSchema = Simplify>>; diff --git a/app/src/index.ts b/app/src/index.ts index 578ab5d..67d54c8 100644 --- a/app/src/index.ts +++ b/app/src/index.ts @@ -4,8 +4,10 @@ export { getDefaultConfig, getDefaultSchema, type ModuleConfigs, - type ModuleSchemas -} from "modules/ModuleManager"; + type ModuleSchemas, + type ModuleManagerOptions, + type ModuleBuildContext +} from "./modules/ModuleManager"; export type * from "./adapter"; export { Api, type ApiOptions } from "./Api"; diff --git a/app/src/media/api/MediaController.ts b/app/src/media/api/MediaController.ts index 9597759..2a1a304 100644 --- a/app/src/media/api/MediaController.ts +++ b/app/src/media/api/MediaController.ts @@ -174,7 +174,7 @@ export class MediaController implements ClassController { const result = await mutator.insertOne({ ...this.media.uploadedEventDataToMediaPayload(info), ...mediaRef - }); + } as any); mutator.__unstable_toggleSystemEntityCreation(true); // delete items if needed diff --git a/app/src/modules/Module.ts b/app/src/modules/Module.ts index ecdf4ce..704e420 100644 --- a/app/src/modules/Module.ts +++ b/app/src/modules/Module.ts @@ -5,10 +5,10 @@ import type { Static, TSchema } from "core/utils"; import type { Connection, EntityManager } from "data"; import type { Hono } from "hono"; -export type ModuleBuildContext = { +export type ModuleBuildContext = { connection: Connection; server: Hono; - em: EntityManager; + em: EntityManager; emgr: EventManager; guard: Guard; }; diff --git a/app/src/modules/ModuleManager.ts b/app/src/modules/ModuleManager.ts index db7b285..bed6c18 100644 --- a/app/src/modules/ModuleManager.ts +++ b/app/src/modules/ModuleManager.ts @@ -35,6 +35,8 @@ import { AppFlows } from "../flows/AppFlows"; import { AppMedia } from "../media/AppMedia"; import type { Module, ModuleBuildContext } from "./Module"; +export type { ModuleBuildContext }; + export const MODULES = { server: AppServer, data: AppData, @@ -75,7 +77,10 @@ export type ModuleManagerOptions = { ) => Promise; // base path for the hono instance basePath?: string; + // doesn't perform validity checks for given/fetched config trustFetched?: boolean; + // runs when initial config provided on a fresh database + seed?: (ctx: ModuleBuildContext) => Promise; }; type ConfigTable = { @@ -294,7 +299,7 @@ export class ModuleManager { version, json: configs, updated_at: new Date() - }, + } as any, { type: "config", version @@ -448,6 +453,9 @@ export class ModuleManager { await this.buildModules(); await this.save(); + // run initial setup + await this.setupInitial(); + this.logger.clear(); return this; } @@ -462,6 +470,18 @@ export class ModuleManager { return this; } + protected async setupInitial() { + const ctx = { + ...this.ctx(), + // disable events for initial setup + em: this.ctx().em.fork() + }; + + // perform a sync + await ctx.em.schema().sync({ force: true }); + await this.options?.seed?.(ctx); + } + get(key: K): Modules[K] { if (!(key in this.modules)) { throw new Error(`Module "${key}" doesn't exist, cannot get`); diff --git a/app/src/ui/client/api/use-api.ts b/app/src/ui/client/api/use-api.ts index 6d75f82..eab68d4 100644 --- a/app/src/ui/client/api/use-api.ts +++ b/app/src/ui/client/api/use-api.ts @@ -5,14 +5,14 @@ import { useApi } from "ui/client"; export const useApiQuery = < Data, - RefineFn extends (data: ResponseObject) => any = (data: ResponseObject) => Data + RefineFn extends (data: ResponseObject) => unknown = (data: ResponseObject) => Data >( fn: (api: Api) => FetchPromise, options?: SWRConfiguration & { enabled?: boolean; refine?: RefineFn } ) => { const api = useApi(); const promise = fn(api); - const refine = options?.refine ?? ((data: ResponseObject) => data); + const refine = options?.refine ?? ((data: any) => data); const fetcher = () => promise.execute().then(refine); const key = promise.key(); diff --git a/app/src/ui/client/api/use-entity.ts b/app/src/ui/client/api/use-entity.ts index 23be395..3c57bad 100644 --- a/app/src/ui/client/api/use-entity.ts +++ b/app/src/ui/client/api/use-entity.ts @@ -1,8 +1,8 @@ import type { PrimaryFieldType } from "core"; import { objectTransform } from "core/utils"; import type { EntityData, RepoQuery } from "data"; -import type { ResponseObject } from "modules/ModuleApi"; -import useSWR, { type SWRConfiguration } from "swr"; +import type { ModuleApi, ResponseObject } from "modules/ModuleApi"; +import useSWR, { type SWRConfiguration, useSWRConfig } from "swr"; import { useApi } from "ui/client"; export class UseEntityApiError extends Error { @@ -15,9 +15,19 @@ export class UseEntityApiError extends Error { } } +function Test() { + const { read } = useEntity("users"); + async () => { + const data = await read(); + }; + + return null; +} + export const useEntity = < - Entity extends string, - Id extends PrimaryFieldType | undefined = undefined + Entity extends keyof DB | string, + Id extends PrimaryFieldType | undefined = undefined, + Data = Entity extends keyof DB ? DB[Entity] : EntityData >( entity: Entity, id?: Id @@ -25,7 +35,7 @@ export const useEntity = < const api = useApi().data; return { - create: async (input: EntityData) => { + create: async (input: Omit) => { const res = await api.createOne(entity, input); if (!res.ok) { throw new UseEntityApiError(res.data, res.res, "Failed to create entity"); @@ -37,9 +47,12 @@ export const useEntity = < if (!res.ok) { throw new UseEntityApiError(res.data, res.res, "Failed to read entity"); } - return res; + // must be manually typed + return res as unknown as Id extends undefined + ? ResponseObject + : ResponseObject; }, - update: async (input: Partial, _id: PrimaryFieldType | undefined = id) => { + update: async (input: Partial>, _id: PrimaryFieldType | undefined = id) => { if (!_id) { throw new Error("id is required"); } @@ -63,8 +76,17 @@ export const useEntity = < }; }; +export function makeKey(api: ModuleApi, entity: string, id?: PrimaryFieldType) { + return ( + "/" + + [...(api.options?.basepath?.split("/") ?? []), entity, ...(id ? [id] : [])] + .filter(Boolean) + .join("/") + ); +} + export const useEntityQuery = < - Entity extends string, + Entity extends keyof DB | string, Id extends PrimaryFieldType | undefined = undefined >( entity: Entity, @@ -72,28 +94,28 @@ export const useEntityQuery = < query?: Partial, options?: SWRConfiguration & { enabled?: boolean } ) => { + const { mutate } = useSWRConfig(); const api = useApi().data; - const key = - options?.enabled !== false - ? [...(api.options?.basepath?.split("/") ?? []), entity, ...(id ? [id] : [])].filter( - Boolean - ) - : null; - const { read, ...actions } = useEntity(entity, id) as any; + const key = makeKey(api, entity, id); + const { read, ...actions } = useEntity(entity, id); const fetcher = () => read(query); - type T = Awaited>; - const swr = useSWR(key, fetcher, { + type T = Awaited>; + const swr = useSWR(options?.enabled === false ? null : key, fetcher as any, { revalidateOnFocus: false, keepPreviousData: false, ...options }); const mapped = objectTransform(actions, (action) => { - if (action === "read") return; + return async (...args: any) => { + // @ts-ignore + const res = await action(...args); - return async (...args) => { - return swr.mutate(action(...args)) as any; + // mutate the key + list key + mutate(key); + if (id) mutate(makeKey(api, entity)); + return res; }; }) as Omit>, "read">; @@ -106,14 +128,14 @@ export const useEntityQuery = < }; export const useEntityMutate = < - Entity extends string, + Entity extends keyof DB | string, Id extends PrimaryFieldType | undefined = undefined >( entity: Entity, id?: Id, options?: SWRConfiguration ) => { - const { data, ...$q } = useEntityQuery(entity, id, undefined, { + const { data, ...$q } = useEntityQuery(entity, id, undefined, { ...options, enabled: false }); diff --git a/app/src/ui/components/code/CodeEditor.tsx b/app/src/ui/components/code/CodeEditor.tsx index 8dcca60..55d119b 100644 --- a/app/src/ui/components/code/CodeEditor.tsx +++ b/app/src/ui/components/code/CodeEditor.tsx @@ -1,7 +1,6 @@ -import type { ReactCodeMirrorProps } from "@uiw/react-codemirror"; -import { Suspense, lazy } from "react"; +import { default as CodeMirror, type ReactCodeMirrorProps } from "@uiw/react-codemirror"; + import { useBknd } from "ui/client/bknd"; -const CodeMirror = lazy(() => import("@uiw/react-codemirror")); export default function CodeEditor({ editable, basicSetup, ...props }: ReactCodeMirrorProps) { const b = useBknd(); @@ -15,13 +14,11 @@ export default function CodeEditor({ editable, basicSetup, ...props }: ReactCode : basicSetup; return ( - - - + ); } diff --git a/app/src/ui/components/form/json-schema/JsonSchemaForm.tsx b/app/src/ui/components/form/json-schema/JsonSchemaForm.tsx index 73330d3..d722dde 100644 --- a/app/src/ui/components/form/json-schema/JsonSchemaForm.tsx +++ b/app/src/ui/components/form/json-schema/JsonSchemaForm.tsx @@ -1,15 +1,12 @@ import type { Schema } from "@cfworker/json-schema"; import Form from "@rjsf/core"; import type { RJSFSchema, UiSchema } from "@rjsf/utils"; +import { cloneDeep } from "lodash-es"; import { forwardRef, useId, useImperativeHandle, useRef, useState } from "react"; -//import { JsonSchemaValidator } from "./JsonSchemaValidator"; import { fields as Fields } from "./fields"; import { templates as Templates } from "./templates"; -import { widgets as Widgets } from "./widgets"; -import "./styles.css"; -import { filterKeys } from "core/utils"; -import { cloneDeep } from "lodash-es"; import { RJSFTypeboxValidator } from "./typebox/RJSFTypeboxValidator"; +import { widgets as Widgets } from "./widgets"; const validator = new RJSFTypeboxValidator(); diff --git a/app/src/ui/components/form/json-schema/index.tsx b/app/src/ui/components/form/json-schema/index.tsx new file mode 100644 index 0000000..4af8a4e --- /dev/null +++ b/app/src/ui/components/form/json-schema/index.tsx @@ -0,0 +1,18 @@ +import { Suspense, forwardRef, lazy } from "react"; +import type { JsonSchemaFormProps, JsonSchemaFormRef } from "./JsonSchemaForm"; + +export type { JsonSchemaFormProps, JsonSchemaFormRef }; + +const Module = lazy(() => + import("./JsonSchemaForm").then((m) => ({ + default: m.JsonSchemaForm + })) +); + +export const JsonSchemaForm = forwardRef((props, ref) => { + return ( + + + + ); +}); diff --git a/app/src/ui/modals/debug/SchemaFormModal.tsx b/app/src/ui/modals/debug/SchemaFormModal.tsx index 0bfab66..72c1c89 100644 --- a/app/src/ui/modals/debug/SchemaFormModal.tsx +++ b/app/src/ui/modals/debug/SchemaFormModal.tsx @@ -4,7 +4,7 @@ import { JsonSchemaForm, type JsonSchemaFormProps, type JsonSchemaFormRef -} from "ui/components/form/json-schema/JsonSchemaForm"; +} from "ui/components/form/json-schema"; import type { ContextModalProps } from "@mantine/modals"; diff --git a/app/src/ui/modules/data/components/fields/EntityJsonSchemaFormField.tsx b/app/src/ui/modules/data/components/fields/EntityJsonSchemaFormField.tsx index 7b830d2..82a55a9 100644 --- a/app/src/ui/modules/data/components/fields/EntityJsonSchemaFormField.tsx +++ b/app/src/ui/modules/data/components/fields/EntityJsonSchemaFormField.tsx @@ -1,14 +1,8 @@ import type { FieldApi } from "@tanstack/react-form"; import type { EntityData, JsonSchemaField } from "data"; -import { Suspense, lazy } from "react"; import * as Formy from "ui/components/form/Formy"; import { FieldLabel } from "ui/components/form/Formy"; - -const JsonSchemaForm = lazy(() => - import("ui/components/form/json-schema/JsonSchemaForm").then((m) => ({ - default: m.JsonSchemaForm - })) -); +import { JsonSchemaForm } from "ui/components/form/json-schema"; export function EntityJsonSchemaFormField({ fieldApi, @@ -34,23 +28,21 @@ export function EntityJsonSchemaFormField({ return ( - Loading...}> -
- -
-
+
+ +
); } diff --git a/app/src/ui/modules/flows/components/TriggerComponent.tsx b/app/src/ui/modules/flows/components/TriggerComponent.tsx index 7497234..87e906b 100644 --- a/app/src/ui/modules/flows/components/TriggerComponent.tsx +++ b/app/src/ui/modules/flows/components/TriggerComponent.tsx @@ -1,14 +1,9 @@ import { Handle, type Node, type NodeProps, Position } from "@xyflow/react"; import { Const, Type, transformObject } from "core/utils"; -import { type TaskRenderProps, type Trigger, TriggerMap } from "flows"; -import { Suspense, lazy } from "react"; +import { type Trigger, TriggerMap } from "flows"; import type { IconType } from "react-icons"; import { TbCircleLetterT } from "react-icons/tb"; -const JsonSchemaForm = lazy(() => - import("ui/components/form/json-schema/JsonSchemaForm").then((m) => ({ - default: m.JsonSchemaForm - })) -); +import { JsonSchemaForm } from "ui/components/form/json-schema"; export type TaskComponentProps = NodeProps> & { Icon?: IconType; @@ -48,17 +43,15 @@ export function TriggerComponent({
- Loading...
}> - - +
- import("ui/components/form/json-schema/JsonSchemaForm").then((m) => ({ - default: m.JsonSchemaForm - })) -); +import { JsonSchemaForm } from "ui/components/form/json-schema"; export type TaskFormProps = { task: Task; @@ -26,16 +19,14 @@ export function TaskForm({ task, onChange, ...props }: TaskFormProps) { //console.log("uiSchema", uiSchema); return ( - Loading...}> - - + ); } diff --git a/app/src/ui/routes/auth/auth.settings.tsx b/app/src/ui/routes/auth/auth.settings.tsx index 2716d3c..5b19651 100644 --- a/app/src/ui/routes/auth/auth.settings.tsx +++ b/app/src/ui/routes/auth/auth.settings.tsx @@ -5,10 +5,7 @@ import { useBkndAuth } from "ui/client/schema/auth/use-bknd-auth"; import { useBkndData } from "ui/client/schema/data/use-bknd-data"; import { Button } from "ui/components/buttons/Button"; import { Alert } from "ui/components/display/Alert"; -import { - JsonSchemaForm, - type JsonSchemaFormRef -} from "ui/components/form/json-schema/JsonSchemaForm"; +import { JsonSchemaForm, type JsonSchemaFormRef } from "ui/components/form/json-schema"; import * as AppShell from "ui/layouts/AppShell/AppShell"; import { useNavigate } from "ui/lib/routes"; import { extractSchema } from "../settings/utils/schema"; diff --git a/app/src/ui/routes/auth/auth.strategies.tsx b/app/src/ui/routes/auth/auth.strategies.tsx index ebf18c0..2792767 100644 --- a/app/src/ui/routes/auth/auth.strategies.tsx +++ b/app/src/ui/routes/auth/auth.strategies.tsx @@ -1,9 +1,7 @@ import { cloneDeep, omit } from "lodash-es"; import { useBknd } from "ui/client/bknd"; import { Button } from "ui/components/buttons/Button"; -import { JsonSchemaForm } from "ui/components/form/json-schema/JsonSchemaForm"; import * as AppShell from "../../layouts/AppShell/AppShell"; -import { extractSchema } from "../settings/utils/schema"; export function AuthStrategiesList() { useBknd({ withSecrets: true }); diff --git a/app/src/ui/routes/data/data.schema.$entity.tsx b/app/src/ui/routes/data/data.schema.$entity.tsx index 838851c..5d797c7 100644 --- a/app/src/ui/routes/data/data.schema.$entity.tsx +++ b/app/src/ui/routes/data/data.schema.$entity.tsx @@ -13,10 +13,7 @@ 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 { - JsonSchemaForm, - type JsonSchemaFormRef -} from "ui/components/form/json-schema/JsonSchemaForm"; +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"; import { Breadcrumbs2 } from "ui/layouts/AppShell/Breadcrumbs2"; 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 3f70848..bcc315d 100644 --- a/app/src/ui/routes/data/forms/entity.fields.form.tsx +++ b/app/src/ui/routes/data/forms/entity.fields.form.tsx @@ -22,7 +22,7 @@ import { Button } from "ui/components/buttons/Button"; import { IconButton } from "ui/components/buttons/IconButton"; import { JsonViewer } from "ui/components/code/JsonViewer"; import { MantineSwitch } from "ui/components/form/hook-form-mantine/MantineSwitch"; -import { JsonSchemaForm } from "ui/components/form/json-schema/JsonSchemaForm"; +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"; diff --git a/app/src/ui/routes/index.tsx b/app/src/ui/routes/index.tsx index a2c3771..aedfc58 100644 --- a/app/src/ui/routes/index.tsx +++ b/app/src/ui/routes/index.tsx @@ -1,14 +1,19 @@ import { Suspense, lazy } from "react"; import { useBknd } from "ui/client/bknd"; import { Route, Router, Switch } from "wouter"; +import AuthRoutes from "./auth"; import { AuthLogin } from "./auth/auth.login"; +import DataRoutes from "./data"; +import FlowRoutes from "./flows"; +import MediaRoutes from "./media"; import { Root, RootEmpty } from "./root"; +import SettingsRoutes from "./settings"; -const DataRoutes = lazy(() => import("./data")); +/*const DataRoutes = lazy(() => import("./data")); const AuthRoutes = lazy(() => import("./auth")); const MediaRoutes = lazy(() => import("./media")); const FlowRoutes = lazy(() => import("./flows")); -const SettingsRoutes = lazy(() => import("./settings")); +const SettingsRoutes = lazy(() => import("./settings"));*/ // @ts-ignore const TestRoutes = lazy(() => import("./test")); diff --git a/app/src/ui/routes/settings/components/Setting.tsx b/app/src/ui/routes/settings/components/Setting.tsx index 9c02c10..20a852b 100644 --- a/app/src/ui/routes/settings/components/Setting.tsx +++ b/app/src/ui/routes/settings/components/Setting.tsx @@ -8,10 +8,7 @@ import { Button } from "ui/components/buttons/Button"; import { IconButton } from "ui/components/buttons/IconButton"; import { Alert } from "ui/components/display/Alert"; import { Empty } from "ui/components/display/Empty"; -import { - JsonSchemaForm, - type JsonSchemaFormRef -} from "ui/components/form/json-schema/JsonSchemaForm"; +import { JsonSchemaForm, type JsonSchemaFormRef } from "ui/components/form/json-schema"; import { Dropdown } from "ui/components/overlay/Dropdown"; import { DataTable } from "ui/components/table/DataTable"; import { useEvent } from "ui/hooks/use-event"; diff --git a/app/src/ui/routes/settings/components/SettingNewModal.tsx b/app/src/ui/routes/settings/components/SettingNewModal.tsx index a8bc93c..5268f53 100644 --- a/app/src/ui/routes/settings/components/SettingNewModal.tsx +++ b/app/src/ui/routes/settings/components/SettingNewModal.tsx @@ -3,16 +3,13 @@ import type { TObject } from "core/utils"; import { omit } from "lodash-es"; import { useRef, useState } from "react"; import { TbCirclePlus, TbVariable } from "react-icons/tb"; +import { useBknd } from "ui/client/BkndProvider"; +import { Button } from "ui/components/buttons/Button"; +import * as Formy from "ui/components/form/Formy"; +import { JsonSchemaForm, type JsonSchemaFormRef } from "ui/components/form/json-schema"; +import { Dropdown } from "ui/components/overlay/Dropdown"; +import { Modal } from "ui/components/overlay/Modal"; import { useLocation } from "wouter"; -import { useBknd } from "../../../client/BkndProvider"; -import { Button } from "../../../components/buttons/Button"; -import * as Formy from "../../../components/form/Formy"; -import { - JsonSchemaForm, - type JsonSchemaFormRef -} from "../../../components/form/json-schema/JsonSchemaForm"; -import { Dropdown } from "../../../components/overlay/Dropdown"; -import { Modal } from "../../../components/overlay/Modal"; export type SettingsNewModalProps = { schema: TObject; diff --git a/app/src/ui/routes/test/tests/flow-create-schema-test.tsx b/app/src/ui/routes/test/tests/flow-create-schema-test.tsx index e27d170..2301240 100644 --- a/app/src/ui/routes/test/tests/flow-create-schema-test.tsx +++ b/app/src/ui/routes/test/tests/flow-create-schema-test.tsx @@ -2,7 +2,7 @@ import { parse } from "core/utils"; import { AppFlows } from "flows/AppFlows"; import { useState } from "react"; import { JsonViewer } from "../../../components/code/JsonViewer"; -import { JsonSchemaForm } from "../../../components/form/json-schema/JsonSchemaForm"; +import { JsonSchemaForm } from "../../../components/form/json-schema"; import { Scrollable } from "../../../layouts/AppShell/AppShell"; export default function FlowCreateSchemaTest() { diff --git a/app/src/ui/routes/test/tests/jsonform-test/index.tsx b/app/src/ui/routes/test/tests/jsonform-test/index.tsx index 2909887..dd6ec4c 100644 --- a/app/src/ui/routes/test/tests/jsonform-test/index.tsx +++ b/app/src/ui/routes/test/tests/jsonform-test/index.tsx @@ -2,12 +2,9 @@ import Form from "@rjsf/core"; import type { RJSFSchema, UiSchema } from "@rjsf/utils"; import { useRef } from "react"; import { TbPlus, TbTrash } from "react-icons/tb"; -import { Button } from "../../../../components/buttons/Button"; +import { Button } from "ui/components/buttons/Button"; +import { JsonSchemaForm, type JsonSchemaFormRef } from "ui/components/form/json-schema"; import * as Formy from "../../../../components/form/Formy"; -import { - JsonSchemaForm, - type JsonSchemaFormRef -} from "../../../../components/form/json-schema/JsonSchemaForm"; import * as AppShell from "../../../../layouts/AppShell/AppShell"; class CfJsonSchemaValidator {} diff --git a/app/src/ui/routes/test/tests/query-jsonform.tsx b/app/src/ui/routes/test/tests/query-jsonform.tsx index 204785d..41b63a6 100644 --- a/app/src/ui/routes/test/tests/query-jsonform.tsx +++ b/app/src/ui/routes/test/tests/query-jsonform.tsx @@ -1,7 +1,7 @@ import type { Schema } from "@cfworker/json-schema"; import { useState } from "react"; -import { JsonSchemaForm } from "../../../components/form/json-schema/JsonSchemaForm"; -import { Scrollable } from "../../../layouts/AppShell/AppShell"; +import { JsonSchemaForm } from "ui/components/form/json-schema"; +import { Scrollable } from "ui/layouts/AppShell/AppShell"; const schema: Schema = { definitions: { @@ -9,52 +9,52 @@ const schema: Schema = { anyOf: [ { title: "String", - type: "string", + type: "string" }, { title: "Number", - type: "number", + type: "number" }, { title: "Boolean", - type: "boolean", - }, - ], + type: "boolean" + } + ] }, numeric: { anyOf: [ { title: "Number", - type: "number", + type: "number" }, { title: "Datetime", type: "string", - format: "date-time", + format: "date-time" }, { title: "Date", type: "string", - format: "date", + format: "date" }, { title: "Time", type: "string", - format: "time", - }, - ], + format: "time" + } + ] }, boolean: { title: "Boolean", - type: "boolean", - }, + type: "boolean" + } }, type: "object", properties: { operand: { enum: ["$and", "$or"], default: "$and", - type: "string", + type: "string" }, conditions: { type: "array", @@ -64,10 +64,10 @@ const schema: Schema = { operand: { enum: ["$and", "$or"], default: "$and", - type: "string", + type: "string" }, key: { - type: "string", + type: "string" }, operator: { type: "array", @@ -78,30 +78,30 @@ const schema: Schema = { type: "object", properties: { $eq: { - $ref: "#/definitions/primitive", - }, + $ref: "#/definitions/primitive" + } }, - required: ["$eq"], + required: ["$eq"] }, { title: "Lower than", type: "object", properties: { $lt: { - $ref: "#/definitions/numeric", - }, + $ref: "#/definitions/numeric" + } }, - required: ["$lt"], + required: ["$lt"] }, { title: "Greather than", type: "object", properties: { $gt: { - $ref: "#/definitions/numeric", - }, + $ref: "#/definitions/numeric" + } }, - required: ["$gt"], + required: ["$gt"] }, { title: "Between", @@ -110,13 +110,13 @@ const schema: Schema = { $between: { type: "array", items: { - $ref: "#/definitions/numeric", + $ref: "#/definitions/numeric" }, minItems: 2, - maxItems: 2, - }, + maxItems: 2 + } }, - required: ["$between"], + required: ["$between"] }, { title: "In", @@ -125,23 +125,23 @@ const schema: Schema = { $in: { type: "array", items: { - $ref: "#/definitions/primitive", + $ref: "#/definitions/primitive" }, - minItems: 1, - }, - }, - }, - ], + minItems: 1 + } + } + } + ] }, - minItems: 1, - }, + minItems: 1 + } }, - required: ["key", "operator"], + required: ["key", "operator"] }, - minItems: 1, - }, + minItems: 1 + } }, - required: ["operand", "conditions"], + required: ["operand", "conditions"] }; export default function QueryJsonFormTest() { diff --git a/app/src/ui/routes/test/tests/schema-test.tsx b/app/src/ui/routes/test/tests/schema-test.tsx index 8865ed6..496fe70 100644 --- a/app/src/ui/routes/test/tests/schema-test.tsx +++ b/app/src/ui/routes/test/tests/schema-test.tsx @@ -1,8 +1,8 @@ import { useEffect, useState } from "react"; import { twMerge } from "tailwind-merge"; -import { useBknd } from "../../../client/BkndProvider"; -import { JsonSchemaForm } from "../../../components/form/json-schema/JsonSchemaForm"; -import { Scrollable } from "../../../layouts/AppShell/AppShell"; +import { useBknd } from "ui/client/BkndProvider"; +import { JsonSchemaForm } from "ui/components/form/json-schema"; +import { Scrollable } from "ui/layouts/AppShell/AppShell"; function useSchema() { const [schema, setSchema] = useState(); diff --git a/app/src/ui/routes/test/tests/swr-and-api.tsx b/app/src/ui/routes/test/tests/swr-and-api.tsx index 53c632e..4d4d88f 100644 --- a/app/src/ui/routes/test/tests/swr-and-api.tsx +++ b/app/src/ui/routes/test/tests/swr-and-api.tsx @@ -1,7 +1,20 @@ import { useEffect, useState } from "react"; -import { useApiQuery } from "ui/client"; +import { useApi, useApiQuery } from "ui/client"; import { Scrollable } from "ui/layouts/AppShell/AppShell"; +function Bla() { + const api = useApi(); + + useEffect(() => { + (async () => { + const one = await api.data.readOne("users", 1); + const many = await api.data.readMany("users"); + })(); + }, []); + + return null; +} + export default function SWRAndAPI() { const [text, setText] = useState(""); const { data, ...r } = useApiQuery((api) => api.data.readOne("comments", 1), { @@ -16,7 +29,7 @@ export default function SWRAndAPI() { return ( -
{JSON.stringify(r.promise.keyArray({ search: false }))}
+
{JSON.stringify(r.key)}
{r.error &&
failed to load
} {r.isLoading &&
loading...
} {data &&
{JSON.stringify(data, null, 2)}
} diff --git a/app/src/ui/routes/test/tests/swr-and-data-api.tsx b/app/src/ui/routes/test/tests/swr-and-data-api.tsx index 7c2e2a6..ab82e24 100644 --- a/app/src/ui/routes/test/tests/swr-and-data-api.tsx +++ b/app/src/ui/routes/test/tests/swr-and-data-api.tsx @@ -13,7 +13,7 @@ export default function SwrAndDataApi() { function QueryDataApi() { const [text, setText] = useState(""); - const { data, update, ...r } = useEntityQuery("comments", 1, {}); + const { data, update, ...r } = useEntityQuery("comments", 2); const comment = data ? data : null; useEffect(() => { @@ -45,10 +45,10 @@ function QueryDataApi() { function DirectDataApi() { const [data, setData] = useState(); - const { create, read, update, _delete } = useEntity("comments", 1); + const { create, read, update, _delete } = useEntity("users"); useEffect(() => { - read().then(setData); + read().then((data) => setData(data)); }, []); return
{JSON.stringify(data, null, 2)}
; diff --git a/app/tsconfig.json b/app/tsconfig.json index 764dacd..abc39f6 100644 --- a/app/tsconfig.json +++ b/app/tsconfig.json @@ -26,14 +26,13 @@ "esModuleInterop": true, "skipLibCheck": true, "rootDir": "./src", - "outDir": "./dist", - "tsBuildInfoFile": "./dist/tsconfig.tsbuildinfo", + "outDir": "./dist/types", "baseUrl": ".", "paths": { "*": ["./src/*"], "bknd": ["./src/*"] } }, - "include": ["./src/**/*.ts", "./src/**/*.tsx", "./env.d.ts"], - "exclude": ["node_modules", "dist/**/*", "../examples/bun"] + "include": ["./src/**/*.ts", "./src/**/*.tsx"], + "exclude": ["node_modules", "dist", "dist/types", "**/*.d.ts"] } \ No newline at end of file diff --git a/bun.lockb b/bun.lockb index 26c68430654f936168f156103b2580d019f12d4e..7b73841dedcae6a350f65edafce51890e9d8c069 100755 GIT binary patch delta 178889 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 E0H^7QumAu6 From 386c0d3ff09249c18052ee7729eb622099741257 Mon Sep 17 00:00:00 2001 From: dswbx Date: Thu, 19 Dec 2024 07:38:40 +0100 Subject: [PATCH 2/7] added new em() shorthand for prototyping, added insertMany mutator function for seeding --- app/__test__/data/mutation.simple.test.ts | 16 ++++- app/__test__/data/prototype.test.ts | 27 +++++++++ app/__test__/data/specs/Mutator.spec.ts | 8 +-- app/src/data/connection/DummyConnection.ts | 7 +++ app/src/data/entities/Mutator.ts | 35 +++++++++++ app/src/data/prototype/index.ts | 69 ++++++++++++++++++---- 6 files changed, 145 insertions(+), 17 deletions(-) create mode 100644 app/src/data/connection/DummyConnection.ts diff --git a/app/__test__/data/mutation.simple.test.ts b/app/__test__/data/mutation.simple.test.ts index dd385af..b54bca8 100644 --- a/app/__test__/data/mutation.simple.test.ts +++ b/app/__test__/data/mutation.simple.test.ts @@ -16,7 +16,7 @@ describe("Mutator simple", async () => { new TextField("label", { required: true, minLength: 1 }), new NumberField("count", { default_value: 0 }) ]); - const em = new EntityManager([items], connection); + const em = new EntityManager([items], connection); await em.connection.kysely.schema .createTable("items") @@ -175,4 +175,18 @@ describe("Mutator simple", async () => { { id: 8, label: "keep", count: 0 } ]); }); + + test("insertMany", async () => { + const oldCount = (await em.repo(items).count()).count; + const inserts = [{ label: "insert 1" }, { label: "insert 2" }]; + const { data } = await em.mutator(items).insertMany(inserts); + + expect(data.length).toBe(2); + expect(data.map((d) => ({ label: d.label }))).toEqual(inserts); + const newCount = (await em.repo(items).count()).count; + expect(newCount).toBe(oldCount + inserts.length); + + const { data: data2 } = await em.repo(items).findMany(); + expect(data2).toEqual(data); + }); }); diff --git a/app/__test__/data/prototype.test.ts b/app/__test__/data/prototype.test.ts index 9d3eebd..f905775 100644 --- a/app/__test__/data/prototype.test.ts +++ b/app/__test__/data/prototype.test.ts @@ -13,6 +13,7 @@ import { PolymorphicRelation, TextField } from "../../src/data"; +import { DummyConnection } from "../../src/data/connection/DummyConnection"; import { FieldPrototype, type FieldSchema, @@ -21,6 +22,7 @@ import { boolean, date, datetime, + em, entity, enumm, json, @@ -272,4 +274,29 @@ describe("prototype", () => { const obj: Schema = {} as any; }); + + test("schema", async () => { + const _em = em( + { + posts: entity("posts", { name: text() }), + comments: entity("comments", { some: text() }) + }, + (relation, { posts, comments }) => { + relation(posts).manyToOne(comments); + } + ); + + type LocalDb = (typeof _em)["DB"]; + + const es = [ + new Entity("posts", [new TextField("name")]), + new Entity("comments", [new TextField("some")]) + ]; + const _em2 = new EntityManager(es, new DummyConnection(), [ + new ManyToOneRelation(es[0], es[1]) + ]); + + // @ts-ignore + expect(_em2.toJSON()).toEqual(_em.toJSON()); + }); }); diff --git a/app/__test__/data/specs/Mutator.spec.ts b/app/__test__/data/specs/Mutator.spec.ts index 04bd8a3..5552543 100644 --- a/app/__test__/data/specs/Mutator.spec.ts +++ b/app/__test__/data/specs/Mutator.spec.ts @@ -22,7 +22,7 @@ describe("[data] Mutator (base)", async () => { new TextField("hidden", { hidden: true }), new TextField("not_fillable", { fillable: false }) ]); - const em = new EntityManager([entity], dummyConnection); + const em = new EntityManager([entity], dummyConnection); await em.schema().sync({ force: true }); const payload = { label: "item 1", count: 1 }; @@ -61,7 +61,7 @@ describe("[data] Mutator (ManyToOne)", async () => { const posts = new Entity("posts", [new TextField("title")]); const users = new Entity("users", [new TextField("username")]); const relations = [new ManyToOneRelation(posts, users)]; - const em = new EntityManager([posts, users], dummyConnection, relations); + const em = new EntityManager([posts, users], dummyConnection, relations); await em.schema().sync({ force: true }); test("RelationMutator", async () => { @@ -192,7 +192,7 @@ describe("[data] Mutator (OneToOne)", async () => { const users = new Entity("users", [new TextField("username")]); const settings = new Entity("settings", [new TextField("theme")]); const relations = [new OneToOneRelation(users, settings)]; - const em = new EntityManager([users, settings], dummyConnection, relations); + const em = new EntityManager([users, settings], dummyConnection, relations); await em.schema().sync({ force: true }); test("insertOne: missing ref", async () => { @@ -276,7 +276,7 @@ describe("[data] Mutator (ManyToMany)", async () => { describe("[data] Mutator (Events)", async () => { const entity = new Entity("test", [new TextField("label")]); - const em = new EntityManager([entity], dummyConnection); + const em = new EntityManager([entity], dummyConnection); await em.schema().sync({ force: true }); const events = new Map(); diff --git a/app/src/data/connection/DummyConnection.ts b/app/src/data/connection/DummyConnection.ts new file mode 100644 index 0000000..451575d --- /dev/null +++ b/app/src/data/connection/DummyConnection.ts @@ -0,0 +1,7 @@ +import { Connection } from "./Connection"; + +export class DummyConnection extends Connection { + constructor() { + super(undefined as any); + } +} diff --git a/app/src/data/entities/Mutator.ts b/app/src/data/entities/Mutator.ts index 53b4cb2..3f81c6e 100644 --- a/app/src/data/entities/Mutator.ts +++ b/app/src/data/entities/Mutator.ts @@ -276,4 +276,39 @@ export class Mutator> { + const entity = this.entity; + if (entity.type === "system" && this.__unstable_disable_system_entity_creation) { + throw new Error(`Creation of system entity "${entity.name}" is disabled`); + } + + const validated: any[] = []; + for (const row of data) { + const validatedData = { + ...entity.getDefaultObject(), + ...(await this.getValidatedData(row, "create")) + }; + + // check if required fields are present + const required = entity.getRequiredFields(); + for (const field of required) { + if ( + typeof validatedData[field.name] === "undefined" || + validatedData[field.name] === null + ) { + throw new Error(`Field "${field.name}" is required`); + } + } + + validated.push(validatedData); + } + + const query = this.conn + .insertInto(entity.name) + .values(validated) + .returning(entity.getSelect()); + + return (await this.many(query)) as any; + } } diff --git a/app/src/data/prototype/index.ts b/app/src/data/prototype/index.ts index 8bdd07b..0e44138 100644 --- a/app/src/data/prototype/index.ts +++ b/app/src/data/prototype/index.ts @@ -1,3 +1,5 @@ +import { DummyConnection } from "data/connection/DummyConnection"; +import { EntityManager } from "data/entities/EntityManager"; import type { Generated } from "kysely"; import { MediaField, type MediaFieldConfig, type MediaItem } from "media/MediaField"; import { @@ -7,6 +9,7 @@ import { type DateFieldConfig, Entity, type EntityConfig, + type EntityRelation, EnumField, type EnumFieldConfig, type Field, @@ -240,6 +243,57 @@ export function relation(local: Local) { }; } +class EntityManagerPrototype> extends EntityManager< + Schema +> { + constructor( + public __entities: Entities, + relations: EntityRelation[] + ) { + super(Object.values(__entities), new DummyConnection(), relations); + } +} + +export function em>( + entities: Entities, + schema?: (rel: typeof relation, entities: Entities) => void +) { + const relations: EntityRelation[] = []; + const relationProxy = (local: Entity) => { + return new Proxy(relation(local), { + get(target, prop) { + if (typeof target[prop] === "function") { + return (...args: any[]) => { + const result = target[prop](...args); + relations.push(result); + return result; + }; + } + return target[prop]; + } + }); + }; + + if (schema) { + schema(relationProxy, entities); + } + + const e = new EntityManagerPrototype(entities, relations); + return { + DB: e.__entities as unknown as Schemas, + entities: e.__entities, + relations, + indices: [], + toJSON: () => { + return e.toJSON() as unknown as { + entities: Schemas; + relations: EntityRelation[]; + indices: any[]; + }; + } + }; +} + export type InferEntityFields = T extends Entity ? { [K in keyof Fields]: Fields[K] extends { _type: infer Type; _required: infer Required } @@ -291,18 +345,9 @@ export type InferField = Field extends { _type: infer Type; _required: in : Type | undefined : never; -const n = number(); -type T2 = InferField; - -const users = entity("users", { - name: text(), - email: text(), - created_at: datetime(), - updated_at: datetime() -}); -type TUsersFields = InferEntityFields; -type TUsers = Schema; -type TUsers2 = Simplify>>; +export type Schemas> = { + [K in keyof T]: Schema; +}; export type InsertSchema = Simplify>>; export type Schema = Simplify<{ id: Generated } & InsertSchema>; From 8e987d58f249b9e7dd5d62f1c9e23051c26f6ab6 Mon Sep 17 00:00:00 2001 From: dswbx Date: Thu, 19 Dec 2024 07:58:41 +0100 Subject: [PATCH 3/7] to em() toJSON to match expected type --- app/src/data/prototype/index.ts | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/app/src/data/prototype/index.ts b/app/src/data/prototype/index.ts index 0e44138..37ca5b2 100644 --- a/app/src/data/prototype/index.ts +++ b/app/src/data/prototype/index.ts @@ -2,6 +2,7 @@ import { DummyConnection } from "data/connection/DummyConnection"; import { EntityManager } from "data/entities/EntityManager"; import type { Generated } from "kysely"; import { MediaField, type MediaFieldConfig, type MediaItem } from "media/MediaField"; +import type { ModuleConfigs } from "modules"; import { BooleanField, type BooleanFieldConfig, @@ -284,13 +285,8 @@ export function em>( entities: e.__entities, relations, indices: [], - toJSON: () => { - return e.toJSON() as unknown as { - entities: Schemas; - relations: EntityRelation[]; - indices: any[]; - }; - } + toJSON: () => + e.toJSON() as unknown as Pick }; } From d7ee13011f5d3a8a8f203b7ae14228aedb137630 Mon Sep 17 00:00:00 2001 From: dswbx Date: Thu, 19 Dec 2024 08:16:47 +0100 Subject: [PATCH 4/7] isolated constructor functions for entities and relations from config --- app/src/data/AppData.ts | 54 ++++--------------- app/src/data/index.ts | 2 + app/src/data/schema/constructor.ts | 34 ++++++++++++ app/src/ui/client/api/use-data.ts | 2 +- .../ui/client/schema/data/use-bknd-data.ts | 5 +- app/src/ui/client/utils/AppReduced.ts | 7 ++- 6 files changed, 53 insertions(+), 51 deletions(-) create mode 100644 app/src/data/schema/constructor.ts diff --git a/app/src/data/AppData.ts b/app/src/data/AppData.ts index 821a3c6..6f885f0 100644 --- a/app/src/data/AppData.ts +++ b/app/src/data/AppData.ts @@ -1,52 +1,20 @@ import { transformObject } from "core/utils"; -import { DataPermissions, Entity, EntityIndex, type EntityManager, type Field } from "data"; +import { + DataPermissions, + type Entity, + EntityIndex, + type EntityManager, + constructEntity, + constructRelation +} from "data"; import { Module } from "modules/Module"; import { DataController } from "./api/DataController"; -import { - type AppDataConfig, - FIELDS, - RELATIONS, - type TAppDataEntity, - type TAppDataRelation, - dataConfigSchema -} from "./data-schema"; +import { type AppDataConfig, dataConfigSchema } from "./data-schema"; export class AppData extends Module { - static constructEntity(name: string, entityConfig: TAppDataEntity) { - const fields = transformObject(entityConfig.fields ?? {}, (fieldConfig, name) => { - const { type } = fieldConfig; - if (!(type in FIELDS)) { - throw new Error(`Field type "${type}" not found`); - } - - const { field } = FIELDS[type as any]; - const returnal = new field(name, fieldConfig.config) as Field; - return returnal; - }); - - // @todo: entity must be migrated to typebox - return new Entity( - name, - Object.values(fields), - entityConfig.config as any, - entityConfig.type as any - ); - } - - static constructRelation( - relationConfig: TAppDataRelation, - resolver: (name: Entity | string) => Entity - ) { - return new RELATIONS[relationConfig.type].cls( - resolver(relationConfig.source), - resolver(relationConfig.target), - relationConfig.config - ); - } - override async build() { const entities = transformObject(this.config.entities ?? {}, (entityConfig, name) => { - return AppData.constructEntity(name, entityConfig); + return constructEntity(name, entityConfig); }); const _entity = (_e: Entity | string): Entity => { @@ -57,7 +25,7 @@ export class AppData extends Module { }; const relations = transformObject(this.config.relations ?? {}, (relation) => - AppData.constructRelation(relation, _entity) + constructRelation(relation, _entity) ); const indices = transformObject(this.config.indices ?? {}, (index, name) => { diff --git a/app/src/data/index.ts b/app/src/data/index.ts index 284c653..3a287e6 100644 --- a/app/src/data/index.ts +++ b/app/src/data/index.ts @@ -18,6 +18,8 @@ export { LibsqlConnection, type LibSqlCredentials } from "./connection/LibsqlCon export { SqliteConnection } from "./connection/SqliteConnection"; export { SqliteLocalConnection } from "./connection/SqliteLocalConnection"; +export { constructEntity, constructRelation } from "./schema/constructor"; + export const DatabaseEvents = { ...MutatorEvents, ...RepositoryEvents diff --git a/app/src/data/schema/constructor.ts b/app/src/data/schema/constructor.ts new file mode 100644 index 0000000..a88ba5e --- /dev/null +++ b/app/src/data/schema/constructor.ts @@ -0,0 +1,34 @@ +import { transformObject } from "core/utils"; +import { Entity, type Field } from "data"; +import { FIELDS, RELATIONS, type TAppDataEntity, type TAppDataRelation } from "data/data-schema"; + +export function constructEntity(name: string, entityConfig: TAppDataEntity) { + const fields = transformObject(entityConfig.fields ?? {}, (fieldConfig, name) => { + const { type } = fieldConfig; + if (!(type in FIELDS)) { + throw new Error(`Field type "${type}" not found`); + } + + const { field } = FIELDS[type as any]; + const returnal = new field(name, fieldConfig.config) as Field; + return returnal; + }); + + return new Entity( + name, + Object.values(fields), + entityConfig.config as any, + entityConfig.type as any + ); +} + +export function constructRelation( + relationConfig: TAppDataRelation, + resolver: (name: Entity | string) => Entity +) { + return new RELATIONS[relationConfig.type].cls( + resolver(relationConfig.source), + resolver(relationConfig.target), + relationConfig.config + ); +} diff --git a/app/src/ui/client/api/use-data.ts b/app/src/ui/client/api/use-data.ts index 46cc81a..cd4ff22 100644 --- a/app/src/ui/client/api/use-data.ts +++ b/app/src/ui/client/api/use-data.ts @@ -10,7 +10,7 @@ type OmitFirstArg = F extends (x: any, ...args: infer P) => any * the first argument "entity" for convenience * @param entity */ -export const useData = (entity: string) => { +export const useData = >(entity: string) => { const api = useApi().data; const methods = [ "readOne", 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 9de8636..36db148 100644 --- a/app/src/ui/client/schema/data/use-bknd-data.ts +++ b/app/src/ui/client/schema/data/use-bknd-data.ts @@ -1,6 +1,5 @@ import { Type, TypeInvalidError, parse, transformObject } from "core/utils"; -import type { Entity } from "data"; -import { AppData } from "data/AppData"; +import { constructEntity } from "data"; import { type TAppDataEntity, type TAppDataEntityFields, @@ -19,7 +18,7 @@ export function useBkndData() { // @todo: potentially store in ref, so it doesn't get recomputed? or use memo? const entities = transformObject(config.data.entities ?? {}, (entity, name) => { - return AppData.constructEntity(name, entity); + return constructEntity(name, entity); }); const actions = { diff --git a/app/src/ui/client/utils/AppReduced.ts b/app/src/ui/client/utils/AppReduced.ts index 0abc27b..8fa684f 100644 --- a/app/src/ui/client/utils/AppReduced.ts +++ b/app/src/ui/client/utils/AppReduced.ts @@ -1,6 +1,5 @@ import type { App } from "App"; -import type { Entity, EntityRelation } from "data"; -import { AppData } from "data/AppData"; +import { type Entity, type EntityRelation, constructEntity, constructRelation } from "data"; import { RelationAccessor } from "data/relations/RelationAccessor"; import { Flow, TaskMap } from "flows"; @@ -20,11 +19,11 @@ export class AppReduced { //console.log("received appjson", appJson); this._entities = Object.entries(this.appJson.data.entities ?? {}).map(([name, entity]) => { - return AppData.constructEntity(name, entity); + return constructEntity(name, entity); }); this._relations = Object.entries(this.appJson.data.relations ?? {}).map(([, relation]) => { - return AppData.constructRelation(relation, this.entity.bind(this)); + return constructRelation(relation, this.entity.bind(this)); }); for (const [name, obj] of Object.entries(this.appJson.flows.flows ?? {})) { From a7e3ce878a7d61cc97ecb377535c7c05c2b5d87b Mon Sep 17 00:00:00 2001 From: dswbx Date: Thu, 19 Dec 2024 16:12:30 +0100 Subject: [PATCH 5/7] added chainable functions in em() --- app/__test__/data/mutation.simple.test.ts | 2 +- app/__test__/data/prototype.test.ts | 28 ++++++--- app/src/data/entities/Entity.ts | 3 +- app/src/data/fields/TextField.ts | 6 ++ app/src/data/prototype/index.ts | 71 +++++++++++++++++------ 5 files changed, 82 insertions(+), 28 deletions(-) diff --git a/app/__test__/data/mutation.simple.test.ts b/app/__test__/data/mutation.simple.test.ts index b54bca8..ac19935 100644 --- a/app/__test__/data/mutation.simple.test.ts +++ b/app/__test__/data/mutation.simple.test.ts @@ -186,7 +186,7 @@ describe("Mutator simple", async () => { const newCount = (await em.repo(items).count()).count; expect(newCount).toBe(oldCount + inserts.length); - const { data: data2 } = await em.repo(items).findMany(); + const { data: data2 } = await em.repo(items).findMany({ offset: oldCount }); expect(data2).toEqual(data); }); }); diff --git a/app/__test__/data/prototype.test.ts b/app/__test__/data/prototype.test.ts index f905775..8b12aa4 100644 --- a/app/__test__/data/prototype.test.ts +++ b/app/__test__/data/prototype.test.ts @@ -3,6 +3,7 @@ import { BooleanField, DateField, Entity, + EntityIndex, EntityManager, EnumField, JsonField, @@ -278,23 +279,32 @@ describe("prototype", () => { test("schema", async () => { const _em = em( { - posts: entity("posts", { name: text() }), - comments: entity("comments", { some: text() }) + posts: entity("posts", { name: text(), slug: text().required() }), + comments: entity("comments", { some: text() }), + users: entity("users", { email: text() }) }, - (relation, { posts, comments }) => { - relation(posts).manyToOne(comments); + ({ relation, index }, { posts, comments, users }) => { + relation(posts).manyToOne(comments).manyToOne(users); + index(posts).on(["name"]).on(["slug"], true); } ); type LocalDb = (typeof _em)["DB"]; const es = [ - new Entity("posts", [new TextField("name")]), - new Entity("comments", [new TextField("some")]) + new Entity("posts", [new TextField("name"), new TextField("slug", { required: true })]), + new Entity("comments", [new TextField("some")]), + new Entity("users", [new TextField("email")]) ]; - const _em2 = new EntityManager(es, new DummyConnection(), [ - new ManyToOneRelation(es[0], es[1]) - ]); + const _em2 = new EntityManager( + es, + new DummyConnection(), + [new ManyToOneRelation(es[0], es[1]), new ManyToOneRelation(es[0], es[2])], + [ + new EntityIndex(es[0], [es[0].field("name")!]), + new EntityIndex(es[0], [es[0].field("slug")!], true) + ] + ); // @ts-ignore expect(_em2.toJSON()).toEqual(_em.toJSON()); diff --git a/app/src/data/entities/Entity.ts b/app/src/data/entities/Entity.ts index 0a285e7..579ffb2 100644 --- a/app/src/data/entities/Entity.ts +++ b/app/src/data/entities/Entity.ts @@ -220,7 +220,8 @@ export class Entity< readOnly: !field.isFillable("update") ? true : undefined, writeOnly: !field.isFillable("create") ? true : undefined, ...field.toJsonSchema() - })) + })), + { additionalProperties: false } ); return clean ? JSON.parse(JSON.stringify(schema)) : schema; diff --git a/app/src/data/fields/TextField.ts b/app/src/data/fields/TextField.ts index 6314618..6dc17d3 100644 --- a/app/src/data/fields/TextField.ts +++ b/app/src/data/fields/TextField.ts @@ -104,6 +104,12 @@ export class TextField extends Field< ); } + if (this.config.pattern && value && !new RegExp(this.config.pattern).test(value)) { + throw new TransformPersistFailedException( + `Field "${this.name}" must match the pattern ${this.config.pattern}` + ); + } + return value; } diff --git a/app/src/data/prototype/index.ts b/app/src/data/prototype/index.ts index 37ca5b2..e9e868f 100644 --- a/app/src/data/prototype/index.ts +++ b/app/src/data/prototype/index.ts @@ -10,6 +10,7 @@ import { type DateFieldConfig, Entity, type EntityConfig, + EntityIndex, type EntityRelation, EnumField, type EnumFieldConfig, @@ -244,47 +245,83 @@ export function relation(local: Local) { }; } +export function index(entity: E) { + return { + on: (fields: (keyof InsertSchema)[], unique?: boolean) => { + const _fields = fields.map((f) => { + const field = entity.field(f as any); + if (!field) { + throw new Error(`Field "${String(f)}" not found on entity "${entity.name}"`); + } + return field; + }); + return new EntityIndex(entity, _fields, unique); + } + }; +} + class EntityManagerPrototype> extends EntityManager< Schema > { constructor( public __entities: Entities, - relations: EntityRelation[] + relations: EntityRelation[] = [], + indices: EntityIndex[] = [] ) { - super(Object.values(__entities), new DummyConnection(), relations); + super(Object.values(__entities), new DummyConnection(), relations, indices); } } +type Chained any, Rt = ReturnType> = ( + e: E +) => { + [K in keyof Rt]: Rt[K] extends (...args: any[]) => any + ? (...args: Parameters) => Rt + : never; +}; + export function em>( entities: Entities, - schema?: (rel: typeof relation, entities: Entities) => void + schema?: ( + fns: { relation: Chained; index: Chained }, + entities: Entities + ) => void ) { const relations: EntityRelation[] = []; - const relationProxy = (local: Entity) => { - return new Proxy(relation(local), { + const indices: EntityIndex[] = []; + + const relationProxy = (e: Entity) => { + return new Proxy(relation(e), { get(target, prop) { - if (typeof target[prop] === "function") { - return (...args: any[]) => { - const result = target[prop](...args); - relations.push(result); - return result; - }; - } - return target[prop]; + return (...args: any[]) => { + relations.push(target[prop](...args)); + return relationProxy(e); + }; } - }); + }) as any; + }; + + const indexProxy = (e: Entity) => { + return new Proxy(index(e), { + get(target, prop) { + return (...args: any[]) => { + indices.push(target[prop](...args)); + return indexProxy(e); + }; + } + }) as any; }; if (schema) { - schema(relationProxy, entities); + schema({ relation: relationProxy, index: indexProxy }, entities); } - const e = new EntityManagerPrototype(entities, relations); + const e = new EntityManagerPrototype(entities, relations, indices); return { DB: e.__entities as unknown as Schemas, entities: e.__entities, relations, - indices: [], + indices, toJSON: () => e.toJSON() as unknown as Pick }; From deddf00c381555510941f89126e3dfcc675458be Mon Sep 17 00:00:00 2001 From: dswbx Date: Fri, 20 Dec 2024 20:11:49 +0100 Subject: [PATCH 6/7] added pausing to event manager, added context aware entity schemas, fixed typings, first boot event, improved useEntityQuery mutation behavior --- app/src/App.ts | 30 ++++++++-- app/src/core/events/EventManager.ts | 20 +++++++ app/src/data/api/DataApi.ts | 4 +- app/src/data/api/DataController.ts | 16 ++++-- app/src/data/entities/Entity.ts | 37 ++++++++---- app/src/data/entities/Mutator.ts | 20 ++++--- app/src/modules/ModuleManager.ts | 5 ++ app/src/ui/client/api/use-entity.ts | 57 +++++++++++++------ app/src/ui/routes/data/data.$entity.$id.tsx | 2 +- app/src/ui/routes/test/tests/swr-and-api.tsx | 4 +- .../ui/routes/test/tests/swr-and-data-api.tsx | 4 +- app/vite.dev.ts | 4 +- 12 files changed, 148 insertions(+), 55 deletions(-) diff --git a/app/src/App.ts b/app/src/App.ts index af68f58..45c6ef7 100644 --- a/app/src/App.ts +++ b/app/src/App.ts @@ -12,13 +12,17 @@ import { SystemController } from "modules/server/SystemController"; export type AppPlugin = (app: App) => void; -export class AppConfigUpdatedEvent extends Event<{ app: App }> { +abstract class AppEvent extends Event<{ app: App } & A> {} +export class AppConfigUpdatedEvent extends AppEvent { static override slug = "app-config-updated"; } -export class AppBuiltEvent extends Event<{ app: App }> { +export class AppBuiltEvent extends AppEvent { static override slug = "app-built"; } -export const AppEvents = { AppConfigUpdatedEvent, AppBuiltEvent } as const; +export class AppFirstBoot extends AppEvent { + static override slug = "app-first-boot"; +} +export const AppEvents = { AppConfigUpdatedEvent, AppBuiltEvent, AppFirstBoot } as const; export type CreateAppConfig = { connection?: @@ -38,6 +42,7 @@ export class App { modules: ModuleManager; static readonly Events = AppEvents; adminController?: AdminController; + private trigger_first_boot = false; constructor( private connection: Connection, @@ -49,9 +54,20 @@ export class App { ...moduleManagerOptions, initial: _initialConfig, onUpdated: async (key, config) => { - //console.log("[APP] config updated", key, config); + // if the EventManager was disabled, we assume we shouldn't + // respond to events, such as "onUpdated". + if (!this.emgr.enabled) { + console.warn("[APP] config updated, but event manager is disabled, skip."); + return; + } + + console.log("[APP] config updated", key); await this.build({ sync: true, save: true }); await this.emgr.emit(new AppConfigUpdatedEvent({ app: this })); + }, + onFirstBoot: async () => { + console.log("[APP] first boot"); + this.trigger_first_boot = true; } }); this.modules.ctx().emgr.registerEvents(AppEvents); @@ -89,6 +105,12 @@ export class App { if (options?.save) { await this.modules.save(); } + + // first boot is set from ModuleManager when there wasn't a config table + if (this.trigger_first_boot) { + this.trigger_first_boot = false; + await this.emgr.emit(new AppFirstBoot({ app: this })); + } } mutateConfig(module: Module) { diff --git a/app/src/core/events/EventManager.ts b/app/src/core/events/EventManager.ts index 3b85d23..9233666 100644 --- a/app/src/core/events/EventManager.ts +++ b/app/src/core/events/EventManager.ts @@ -15,6 +15,7 @@ export class EventManager< > { protected events: EventClass[] = []; protected listeners: EventListener[] = []; + enabled: boolean = true; constructor(events?: RegisteredEvents, listeners?: EventListener[]) { if (events) { @@ -28,6 +29,16 @@ export class EventManager< } } + enable() { + this.enabled = true; + return this; + } + + disable() { + this.enabled = false; + return this; + } + clearEvents() { this.events = []; return this; @@ -39,6 +50,10 @@ export class EventManager< return this; } + getListeners(): EventListener[] { + return [...this.listeners]; + } + get Events(): { [K in keyof RegisteredEvents]: RegisteredEvents[K] } { // proxy class to access events return new Proxy(this, { @@ -133,6 +148,11 @@ export class EventManager< async emit(event: Event) { // @ts-expect-error slug is static const slug = event.constructor.slug; + if (!this.enabled) { + console.log("EventManager disabled, not emitting", slug); + return; + } + if (!this.eventExists(event)) { throw new Error(`Event "${slug}" not registered`); } diff --git a/app/src/data/api/DataApi.ts b/app/src/data/api/DataApi.ts index caf6a2a..ad7c670 100644 --- a/app/src/data/api/DataApi.ts +++ b/app/src/data/api/DataApi.ts @@ -46,7 +46,7 @@ export class DataApi extends ModuleApi { createOne( entity: E, - input: Data + input: Omit ) { return this.post>([entity as any], input); } @@ -54,7 +54,7 @@ export class DataApi extends ModuleApi { updateOne( entity: E, id: PrimaryFieldType, - input: Partial + input: Partial> ) { return this.patch>([entity as any, id], input); } diff --git a/app/src/data/api/DataController.ts b/app/src/data/api/DataController.ts index 86f3e42..68a5417 100644 --- a/app/src/data/api/DataController.ts +++ b/app/src/data/api/DataController.ts @@ -1,5 +1,5 @@ import { type ClassController, isDebug, tbValidator as tb } from "core"; -import { Type, objectCleanEmpty, objectTransform } from "core/utils"; +import { StringEnum, Type, objectCleanEmpty, objectTransform } from "core/utils"; import { DataPermissions, type EntityData, @@ -182,19 +182,25 @@ export class DataController implements ClassController { }) // read schema .get( - "/schemas/:entity", - tb("param", Type.Object({ entity: Type.String() })), + "/schemas/:entity/:context?", + tb( + "param", + Type.Object({ + entity: Type.String(), + context: Type.Optional(StringEnum(["create", "update"])) + }) + ), async (c) => { this.guard.throwUnlessGranted(DataPermissions.entityRead); //console.log("request", c.req.raw); - const { entity } = c.req.param(); + const { entity, context } = c.req.param(); if (!this.entityExists(entity)) { console.log("not found", entity, definedEntities); return c.notFound(); } const _entity = this.em.entity(entity); - const schema = _entity.toSchema(); + const schema = _entity.toSchema({ context } as any); const url = new URL(c.req.url); const base = `${url.origin}${this.config.basepath}`; const $id = `${this.config.basepath}/schemas/${entity}`; diff --git a/app/src/data/entities/Entity.ts b/app/src/data/entities/Entity.ts index 579ffb2..aa3d75c 100644 --- a/app/src/data/entities/Entity.ts +++ b/app/src/data/entities/Entity.ts @@ -158,7 +158,7 @@ export class Entity< } get label(): string { - return snakeToPascalWithSpaces(this.config.name || this.name); + return this.config.name ?? snakeToPascalWithSpaces(this.name); } field(name: string): Field | undefined { @@ -210,21 +210,34 @@ export class Entity< return true; } - toSchema(clean?: boolean): object { - const fields = Object.fromEntries(this.fields.map((field) => [field.name, field])); + toSchema(options?: { clean: boolean; context?: "create" | "update" }): object { + let fields: Field[]; + switch (options?.context) { + case "create": + case "update": + fields = this.getFillableFields(options.context); + break; + default: + fields = this.getFields(true); + } + + const _fields = Object.fromEntries(fields.map((field) => [field.name, field])); const schema = Type.Object( - transformObject(fields, (field) => ({ - title: field.config.label, - $comment: field.config.description, - $field: field.type, - readOnly: !field.isFillable("update") ? true : undefined, - writeOnly: !field.isFillable("create") ? true : undefined, - ...field.toJsonSchema() - })), + transformObject(_fields, (field) => { + //const hidden = field.isHidden(options?.context); + const fillable = field.isFillable(options?.context); + return { + title: field.config.label, + $comment: field.config.description, + $field: field.type, + readOnly: !fillable ? true : undefined, + ...field.toJsonSchema() + }; + }), { additionalProperties: false } ); - return clean ? JSON.parse(JSON.stringify(schema)) : schema; + return options?.clean ? JSON.parse(JSON.stringify(schema)) : schema; } toJSON() { diff --git a/app/src/data/entities/Mutator.ts b/app/src/data/entities/Mutator.ts index 3f81c6e..51a1d12 100644 --- a/app/src/data/entities/Mutator.ts +++ b/app/src/data/entities/Mutator.ts @@ -25,8 +25,12 @@ export type MutatorResponse = { data: T; }; -export class Mutator> - implements EmitsEvents +export class Mutator< + DB = any, + TB extends keyof DB = any, + Output = DB[TB], + Input = Omit +> implements EmitsEvents { em: EntityManager; entity: Entity; @@ -122,7 +126,7 @@ export class Mutator> { + async insertOne(data: Input): Promise> { const entity = this.entity; if (entity.type === "system" && this.__unstable_disable_system_entity_creation) { throw new Error(`Creation of system entity "${entity.name}" is disabled`); @@ -159,7 +163,7 @@ export class Mutator> { + async updateOne(id: PrimaryFieldType, data: Input): Promise> { const entity = this.entity; if (!Number.isInteger(id)) { throw new Error("ID must be provided for update"); @@ -190,7 +194,7 @@ export class Mutator> { + async deleteOne(id: PrimaryFieldType): Promise> { const entity = this.entity; if (!Number.isInteger(id)) { throw new Error("ID must be provided for deletion"); @@ -256,7 +260,7 @@ export class Mutator> { + async deleteWhere(where?: RepoQuery["where"]): Promise> { const entity = this.entity; const qb = this.appendWhere(this.conn.deleteFrom(entity.name), where).returning( @@ -266,7 +270,7 @@ export class Mutator> { + async updateWhere(data: Input, where?: RepoQuery["where"]): Promise> { const entity = this.entity; const validatedData = await this.getValidatedData(data, "update"); @@ -277,7 +281,7 @@ export class Mutator> { + async insertMany(data: Input[]): Promise> { const entity = this.entity; if (entity.type === "system" && this.__unstable_disable_system_entity_creation) { throw new Error(`Creation of system entity "${entity.name}" is disabled`); diff --git a/app/src/modules/ModuleManager.ts b/app/src/modules/ModuleManager.ts index bed6c18..e5b6c9c 100644 --- a/app/src/modules/ModuleManager.ts +++ b/app/src/modules/ModuleManager.ts @@ -75,6 +75,8 @@ export type ModuleManagerOptions = { module: Module, config: ModuleConfigs[Module] ) => Promise; + // triggered when no config table existed + onFirstBoot?: () => Promise; // base path for the hono instance basePath?: string; // doesn't perform validity checks for given/fetched config @@ -480,6 +482,9 @@ export class ModuleManager { // perform a sync await ctx.em.schema().sync({ force: true }); await this.options?.seed?.(ctx); + + // run first boot event + await this.options?.onFirstBoot?.(); } get(key: K): Modules[K] { diff --git a/app/src/ui/client/api/use-entity.ts b/app/src/ui/client/api/use-entity.ts index 3c57bad..dd93455 100644 --- a/app/src/ui/client/api/use-entity.ts +++ b/app/src/ui/client/api/use-entity.ts @@ -1,16 +1,23 @@ import type { PrimaryFieldType } from "core"; -import { objectTransform } from "core/utils"; +import { encodeSearch, objectTransform } from "core/utils"; import type { EntityData, RepoQuery } from "data"; import type { ModuleApi, ResponseObject } from "modules/ModuleApi"; -import useSWR, { type SWRConfiguration, useSWRConfig } from "swr"; +import useSWR, { type SWRConfiguration, mutate } from "swr"; import { useApi } from "ui/client"; export class UseEntityApiError extends Error { constructor( - public payload: Payload, - public response: Response, - message?: string + public response: ResponseObject, + fallback?: string ) { + let message = fallback; + if ("error" in response) { + message = response.error as string; + if (fallback) { + message = `${fallback}: ${message}`; + } + } + super(message ?? "UseEntityApiError"); } } @@ -38,14 +45,14 @@ export const useEntity = < create: async (input: Omit) => { const res = await api.createOne(entity, input); if (!res.ok) { - throw new UseEntityApiError(res.data, res.res, "Failed to create entity"); + throw new UseEntityApiError(res, `Failed to create entity "${entity}"`); } return res; }, read: async (query: Partial = {}) => { const res = id ? await api.readOne(entity, id!, query) : await api.readMany(entity, query); if (!res.ok) { - throw new UseEntityApiError(res.data, res.res, "Failed to read entity"); + throw new UseEntityApiError(res as any, `Failed to read entity "${entity}"`); } // must be manually typed return res as unknown as Id extends undefined @@ -58,7 +65,7 @@ export const useEntity = < } const res = await api.updateOne(entity, _id, input); if (!res.ok) { - throw new UseEntityApiError(res.data, res.res, "Failed to update entity"); + throw new UseEntityApiError(res, `Failed to update entity "${entity}"`); } return res; }, @@ -69,19 +76,26 @@ export const useEntity = < const res = await api.deleteOne(entity, _id); if (!res.ok) { - throw new UseEntityApiError(res.data, res.res, "Failed to delete entity"); + throw new UseEntityApiError(res, `Failed to delete entity "${entity}"`); } return res; } }; }; -export function makeKey(api: ModuleApi, entity: string, id?: PrimaryFieldType) { +// @todo: try to get from ModuleApi directly +export function makeKey( + api: ModuleApi, + entity: string, + id?: PrimaryFieldType, + query?: Partial +) { return ( "/" + [...(api.options?.basepath?.split("/") ?? []), entity, ...(id ? [id] : [])] .filter(Boolean) - .join("/") + .join("/") + + (query ? "?" + encodeSearch(query) : "") ); } @@ -92,29 +106,36 @@ export const useEntityQuery = < entity: Entity, id?: Id, query?: Partial, - options?: SWRConfiguration & { enabled?: boolean } + options?: SWRConfiguration & { enabled?: boolean; revalidateOnMutate?: boolean } ) => { - const { mutate } = useSWRConfig(); const api = useApi().data; - const key = makeKey(api, entity, id); + const key = makeKey(api, entity, id, query); const { read, ...actions } = useEntity(entity, id); const fetcher = () => read(query); type T = Awaited>; const swr = useSWR(options?.enabled === false ? null : key, fetcher as any, { revalidateOnFocus: false, - keepPreviousData: false, + keepPreviousData: true, ...options }); + const mutateAll = async () => { + const entityKey = makeKey(api, entity); + return mutate((key) => typeof key === "string" && key.startsWith(entityKey), undefined, { + revalidate: true + }); + }; + const mapped = objectTransform(actions, (action) => { return async (...args: any) => { // @ts-ignore const res = await action(...args); - // mutate the key + list key - mutate(key); - if (id) mutate(makeKey(api, entity)); + // mutate all keys of entity by default + if (options?.revalidateOnMutate !== false) { + await mutateAll(); + } return res; }; }) as Omit>, "read">; diff --git a/app/src/ui/routes/data/data.$entity.$id.tsx b/app/src/ui/routes/data/data.$entity.$id.tsx index 09cd38a..a641187 100644 --- a/app/src/ui/routes/data/data.$entity.$id.tsx +++ b/app/src/ui/routes/data/data.$entity.$id.tsx @@ -101,7 +101,7 @@ export function DataEntityUpdate({ params }) { data: { data: data as any, entity: entity.toJSON(), - schema: entity.toSchema(true), + schema: entity.toSchema({ clean: true }), form: Form.state.values, state: Form.state } diff --git a/app/src/ui/routes/test/tests/swr-and-api.tsx b/app/src/ui/routes/test/tests/swr-and-api.tsx index 4d4d88f..45807eb 100644 --- a/app/src/ui/routes/test/tests/swr-and-api.tsx +++ b/app/src/ui/routes/test/tests/swr-and-api.tsx @@ -39,12 +39,12 @@ export default function SWRAndAPI() { e.preventDefault(); if (!comment) return; - await r.mutate(async () => { + /*await r.mutate(async () => { const res = await r.api.data.updateOne("comments", comment.id, { content: text }); return res.data; - }); + });*/ return false; }} diff --git a/app/src/ui/routes/test/tests/swr-and-data-api.tsx b/app/src/ui/routes/test/tests/swr-and-data-api.tsx index ab82e24..fe8d7b1 100644 --- a/app/src/ui/routes/test/tests/swr-and-data-api.tsx +++ b/app/src/ui/routes/test/tests/swr-and-data-api.tsx @@ -13,7 +13,9 @@ export default function SwrAndDataApi() { function QueryDataApi() { const [text, setText] = useState(""); - const { data, update, ...r } = useEntityQuery("comments", 2); + const { data, update, ...r } = useEntityQuery("comments", 2, { + sort: { by: "id", dir: "desc" } + }); const comment = data ? data : null; useEffect(() => { diff --git a/app/vite.dev.ts b/app/vite.dev.ts index 608e24e..1fdcdbe 100644 --- a/app/vite.dev.ts +++ b/app/vite.dev.ts @@ -24,8 +24,8 @@ export default { async fetch(request: Request) { const app = App.create({ connection }); - app.emgr.on( - "app-built", + app.emgr.onEvent( + App.Events.AppBuiltEvent, async () => { app.registerAdminController({ forceDev: true }); app.module.server.client.get("/assets/*", serveStatic({ root: "./" })); From 3a79ce2cf82a288f331c78382357089d41c3a69c Mon Sep 17 00:00:00 2001 From: dswbx Date: Sat, 21 Dec 2024 15:03:14 +0100 Subject: [PATCH 7/7] added a new mutate replacement for useEntityMutate to quickly update cache without revalidating --- app/src/data/entities/Mutator.ts | 2 +- app/src/ui/client/api/use-entity.ts | 51 +++++++++++++- .../ui/routes/test/tests/swr-and-data-api.tsx | 68 ++++++++++++------- 3 files changed, 91 insertions(+), 30 deletions(-) diff --git a/app/src/data/entities/Mutator.ts b/app/src/data/entities/Mutator.ts index 51a1d12..cb25ddf 100644 --- a/app/src/data/entities/Mutator.ts +++ b/app/src/data/entities/Mutator.ts @@ -270,7 +270,7 @@ export class Mutator< return (await this.many(qb)) as any; } - async updateWhere(data: Input, where?: RepoQuery["where"]): Promise> { + async updateWhere(data: Partial, where?: RepoQuery["where"]): Promise> { const entity = this.entity; const validatedData = await this.getValidatedData(data, "update"); diff --git a/app/src/ui/client/api/use-entity.ts b/app/src/ui/client/api/use-entity.ts index dd93455..89ff6e0 100644 --- a/app/src/ui/client/api/use-entity.ts +++ b/app/src/ui/client/api/use-entity.ts @@ -3,7 +3,7 @@ import { encodeSearch, objectTransform } from "core/utils"; import type { EntityData, RepoQuery } from "data"; import type { ModuleApi, ResponseObject } from "modules/ModuleApi"; import useSWR, { type SWRConfiguration, mutate } from "swr"; -import { useApi } from "ui/client"; +import { type Api, useApi } from "ui/client"; export class UseEntityApiError extends Error { constructor( @@ -148,9 +148,44 @@ export const useEntityQuery = < }; }; +export async function mutateEntityCache< + Entity extends keyof DB | string, + Data = Entity extends keyof DB ? Omit : EntityData +>(api: Api["data"], entity: Entity, id: PrimaryFieldType, partialData: Partial) { + function update(prev: any, partialNext: any) { + if ( + typeof prev !== "undefined" && + typeof partialNext !== "undefined" && + "id" in prev && + prev.id === id + ) { + return { ...prev, ...partialNext }; + } + + return prev; + } + + const entityKey = makeKey(api, entity); + + return mutate( + (key) => typeof key === "string" && key.startsWith(entityKey), + async (data) => { + if (typeof data === "undefined") return; + if (Array.isArray(data)) { + return data.map((item) => update(item, partialData)); + } + return update(data, partialData); + }, + { + revalidate: false + } + ); +} + export const useEntityMutate = < Entity extends keyof DB | string, - Id extends PrimaryFieldType | undefined = undefined + Id extends PrimaryFieldType | undefined = undefined, + Data = Entity extends keyof DB ? Omit : EntityData >( entity: Entity, id?: Id, @@ -160,5 +195,15 @@ export const useEntityMutate = < ...options, enabled: false }); - return $q; + + const _mutate = id + ? (data) => mutateEntityCache($q.api, entity, id, data) + : (id, data) => mutateEntityCache($q.api, entity, id, data); + + return { + ...$q, + mutate: _mutate as unknown as Id extends undefined + ? (id: PrimaryFieldType, data: Partial) => Promise + : (data: Partial) => Promise + }; }; diff --git a/app/src/ui/routes/test/tests/swr-and-data-api.tsx b/app/src/ui/routes/test/tests/swr-and-data-api.tsx index fe8d7b1..ebc6d59 100644 --- a/app/src/ui/routes/test/tests/swr-and-data-api.tsx +++ b/app/src/ui/routes/test/tests/swr-and-data-api.tsx @@ -1,53 +1,69 @@ import { useEffect, useState } from "react"; -import { useEntity, useEntityQuery } from "ui/client/api/use-entity"; +import { useEntity, useEntityMutate, useEntityQuery } from "ui/client/api/use-entity"; import { Scrollable } from "ui/layouts/AppShell/AppShell"; export default function SwrAndDataApi() { return ( -
+ + asdf -
+ +
); } -function QueryDataApi() { - const [text, setText] = useState(""); - const { data, update, ...r } = useEntityQuery("comments", 2, { - sort: { by: "id", dir: "desc" } +function QueryMutateDataApi() { + const { mutate } = useEntityMutate("comments"); + const { data, ...r } = useEntityQuery("comments", undefined, { + limit: 2 }); - const comment = data ? data : null; - - useEffect(() => { - setText(comment?.content ?? ""); - }, [comment]); return ( - +
+ bla
{JSON.stringify(r.key)}
{r.error &&
failed to load
} {r.isLoading &&
loading...
} {data &&
{JSON.stringify(data, null, 2)}
} {data && ( -
{ - e.preventDefault(); - if (!comment) return; - await update({ content: text }); - return false; - }} - > - setText(e.target.value)} /> - -
+
+ {data.map((comment) => ( + { + await mutate(comment.id, { content: e.target.value }); + }} + className="border border-black" + /> + ))} +
)} - +
+ ); +} + +function QueryDataApi() { + const { data, update, ...r } = useEntityQuery("comments", undefined, { + sort: { by: "id", dir: "asc" }, + limit: 3 + }); + + return ( +
+
{JSON.stringify(r.key)}
+ {r.error &&
failed to load
} + {r.isLoading &&
loading...
} + {data &&
{JSON.stringify(data, null, 2)}
} +
); } function DirectDataApi() { const [data, setData] = useState(); - const { create, read, update, _delete } = useEntity("users"); + const { create, read, update, _delete } = useEntity("comments"); useEffect(() => { read().then((data) => setData(data));