From c6cbd362315cb2b1b2ca9e56b65645af9d00097b Mon Sep 17 00:00:00 2001 From: dswbx Date: Thu, 16 Jan 2025 16:21:32 +0100 Subject: [PATCH] fix RepoQuery typings --- app/__test__/data/data-query-impl.spec.ts | 4 +- app/package.json | 2 +- app/src/core/object/query/query.ts | 2 +- app/src/data/api/DataApi.ts | 8 ++-- app/src/data/entities/Entity.ts | 4 +- app/src/data/entities/query/WhereBuilder.ts | 2 +- app/src/data/index.ts | 1 + app/src/data/server/data-query-impl.ts | 41 +++++++++---------- app/src/ui/client/api/use-entity.ts | 8 ++-- .../ui/elements/media/DropzoneContainer.tsx | 4 +- app/src/ui/routes/data/data.$entity.$id.tsx | 2 +- 11 files changed, 39 insertions(+), 39 deletions(-) diff --git a/app/__test__/data/data-query-impl.spec.ts b/app/__test__/data/data-query-impl.spec.ts index e2cfb29..c03b1fe 100644 --- a/app/__test__/data/data-query-impl.spec.ts +++ b/app/__test__/data/data-query-impl.spec.ts @@ -98,14 +98,14 @@ describe("data-query-impl", () => { test("with", () => { decode({ with: ["posts"] }, { with: { posts: {} } }); decode({ with: { posts: {} } }, { with: { posts: {} } }); - decode({ with: { posts: { limit: "1" } } }, { with: { posts: { limit: 1 } } }); + decode({ with: { posts: { limit: 1 } } }, { with: { posts: { limit: 1 } } }); decode( { with: { posts: { with: { images: { - select: "id" + select: ["id"] } } } diff --git a/app/package.json b/app/package.json index d05eccc..c016cd8 100644 --- a/app/package.json +++ b/app/package.json @@ -3,7 +3,7 @@ "type": "module", "sideEffects": false, "bin": "./dist/cli/index.js", - "version": "0.5.0", + "version": "0.6.0-rc.0", "scripts": { "dev": "vite", "test": "ALL_TESTS=1 bun test --bail", diff --git a/app/src/core/object/query/query.ts b/app/src/core/object/query/query.ts index 07a4c3b..e30979e 100644 --- a/app/src/core/object/query/query.ts +++ b/app/src/core/object/query/query.ts @@ -49,7 +49,7 @@ type LiteralExpressionCondition = { [key: string]: Primitive | ExpressionCondition; }; -const OperandOr = "$or"; +const OperandOr = "$or" as const; type OperandCondition = { [OperandOr]?: LiteralExpressionCondition | ExpressionCondition; }; diff --git a/app/src/data/api/DataApi.ts b/app/src/data/api/DataApi.ts index 47144c2..f2fe4e7 100644 --- a/app/src/data/api/DataApi.ts +++ b/app/src/data/api/DataApi.ts @@ -1,5 +1,5 @@ import type { DB } from "core"; -import type { EntityData, RepoQuery, RepositoryResponse } from "data"; +import type { EntityData, RepoQuery, RepoQueryIn, RepositoryResponse } from "data"; import { type BaseModuleApiOptions, ModuleApi, type PrimaryFieldType } from "modules"; export type DataApiOptions = BaseModuleApiOptions & { @@ -19,14 +19,14 @@ export class DataApi extends ModuleApi { readOne( entity: E, id: PrimaryFieldType, - query: Partial> = {} + query: Omit = {} ) { return this.get, "meta" | "data">>([entity as any, id], query); } readMany( entity: E, - query: Partial = {} + query: RepoQueryIn = {} ) { return this.get, "meta" | "data">>( [entity as any], @@ -38,7 +38,7 @@ export class DataApi extends ModuleApi { E extends keyof DB | string, R extends keyof DB | string, Data = R extends keyof DB ? DB[R] : EntityData - >(entity: E, id: PrimaryFieldType, reference: R, query: Partial = {}) { + >(entity: E, id: PrimaryFieldType, reference: R, query: RepoQueryIn = {}) { return this.get, "meta" | "data">>( [entity as any, id, reference], query ?? this.options.defaultQuery diff --git a/app/src/data/entities/Entity.ts b/app/src/data/entities/Entity.ts index a0bdb29..3365190 100644 --- a/app/src/data/entities/Entity.ts +++ b/app/src/data/entities/Entity.ts @@ -98,8 +98,8 @@ export class Entity< getDefaultSort() { return { - by: this.config.sort_field, - dir: this.config.sort_dir + by: this.config.sort_field ?? "id", + dir: this.config.sort_dir ?? "asc" }; } diff --git a/app/src/data/entities/query/WhereBuilder.ts b/app/src/data/entities/query/WhereBuilder.ts index 5168d0e..3473497 100644 --- a/app/src/data/entities/query/WhereBuilder.ts +++ b/app/src/data/entities/query/WhereBuilder.ts @@ -30,7 +30,7 @@ function key(e: unknown): string { return e as string; } -const expressions: TExpression[] = [ +const expressions = [ exp( "$eq", (v: Primitive) => isPrimitive(v), diff --git a/app/src/data/index.ts b/app/src/data/index.ts index 3a287e6..db70e28 100644 --- a/app/src/data/index.ts +++ b/app/src/data/index.ts @@ -8,6 +8,7 @@ export * from "./prototype"; export { type RepoQuery, + type RepoQueryIn, defaultQuerySchema, querySchema, whereSchema diff --git a/app/src/data/server/data-query-impl.ts b/app/src/data/server/data-query-impl.ts index dcccd64..8abf02e 100644 --- a/app/src/data/server/data-query-impl.ts +++ b/app/src/data/server/data-query-impl.ts @@ -7,7 +7,7 @@ import { Type, Value } from "core/utils"; -import { WhereBuilder } from "../entities"; +import { WhereBuilder, type WhereQuery } from "../entities"; const NumberOrString = (options: SchemaOptions = {}) => Type.Transform(Type.Union([Type.Number(), Type.String()], options)) @@ -15,10 +15,8 @@ const NumberOrString = (options: SchemaOptions = {}) => .Encode(String); const limit = NumberOrString({ default: 10 }); - const offset = NumberOrString({ default: 0 }); -// @todo: allow "id" and "-id" const sort_default = { by: "id", dir: "asc" }; const sort = Type.Transform( Type.Union( @@ -28,20 +26,20 @@ const sort = Type.Transform( } ) ) - .Decode((value) => { + .Decode((value): { by: string; dir: "asc" | "desc" } => { if (typeof value === "string") { if (/^-?[a-zA-Z_][a-zA-Z0-9_.]*$/.test(value)) { const dir = value[0] === "-" ? "desc" : "asc"; - return { by: dir === "desc" ? value.slice(1) : value, dir }; + return { by: dir === "desc" ? value.slice(1) : value, dir } as any; } else if (/^{.*}$/.test(value)) { - return JSON.parse(value); + return JSON.parse(value) as any; } - return sort_default; + return sort_default as any; } - return value; + return value as any; }) - .Encode(JSON.stringify); + .Encode((value) => value); const stringArray = Type.Transform( Type.Union([Type.String(), Type.Array(Type.String())], { default: [] }) @@ -65,25 +63,18 @@ export const whereSchema = Type.Transform( }) .Encode(JSON.stringify); -export type ShallowRepoQuery = { - limit?: number; - offset?: number; - sort?: string | { by: string; dir: "asc" | "desc" }; - select?: string[]; - with?: string[] | Record; - join?: string[]; - where?: any; -}; export type RepoWithSchema = Record< string, - Omit & { + Omit & { with?: unknown; } >; + export const withSchema = (Self: TSelf) => Type.Transform(Type.Union([stringArray, Type.Record(Type.String(), Self)])) .Decode((value) => { - let _value = value; + let _value = typeof value === "string" ? [value] : value; + if (Array.isArray(value)) { if (!value.every((v) => typeof v === "string")) { throw new Error("Invalid 'with' schema"); @@ -121,6 +112,14 @@ export const querySchema = Type.Recursive( { $id: "query-schema" } ); -export type RepoQueryIn = Static; +export type RepoQueryIn = { + limit?: number; + offset?: number; + sort?: string | { by: string; dir: "asc" | "desc" }; + select?: string[]; + with?: string[] | Record; + join?: string[]; + where?: WhereQuery; +}; export type RepoQuery = Required>; export const defaultQuerySchema = Value.Default(querySchema, {}) as RepoQuery; diff --git a/app/src/ui/client/api/use-entity.ts b/app/src/ui/client/api/use-entity.ts index fba6a45..9b721c1 100644 --- a/app/src/ui/client/api/use-entity.ts +++ b/app/src/ui/client/api/use-entity.ts @@ -1,6 +1,6 @@ import type { DB, PrimaryFieldType } from "core"; import { encodeSearch, objectTransform } from "core/utils"; -import type { EntityData, RepoQuery } from "data"; +import type { EntityData, RepoQuery, RepoQueryIn } from "data"; import type { ModuleApi, ResponseObject } from "modules/ModuleApi"; import useSWR, { type SWRConfiguration, mutate } from "swr"; import { type Api, useApi } from "ui/client"; @@ -49,7 +49,7 @@ export const useEntity = < } return res; }, - read: async (query: Partial = {}) => { + read: async (query: RepoQueryIn = {}) => { const res = id ? await api.readOne(entity, id!, query) : await api.readMany(entity, query); if (!res.ok) { throw new UseEntityApiError(res as any, `Failed to read entity "${entity}"`); @@ -88,7 +88,7 @@ export function makeKey( api: ModuleApi, entity: string, id?: PrimaryFieldType, - query?: Partial + query?: RepoQueryIn ) { return ( "/" + @@ -105,7 +105,7 @@ export const useEntityQuery = < >( entity: Entity, id?: Id, - query?: Partial, + query?: RepoQueryIn, options?: SWRConfiguration & { enabled?: boolean; revalidateOnMutate?: boolean } ) => { const api = useApi().data; diff --git a/app/src/ui/elements/media/DropzoneContainer.tsx b/app/src/ui/elements/media/DropzoneContainer.tsx index 87c9933..b9b3a12 100644 --- a/app/src/ui/elements/media/DropzoneContainer.tsx +++ b/app/src/ui/elements/media/DropzoneContainer.tsx @@ -1,4 +1,4 @@ -import type { RepoQuery } from "data"; +import type { RepoQuery, RepoQueryIn } from "data"; import type { MediaFieldSchema } from "media/AppMedia"; import type { TAppMediaConfig } from "media/media-schema"; import { useId } from "react"; @@ -15,7 +15,7 @@ export type DropzoneContainerProps = { id: number; field: string; }; - query?: Partial; + query?: RepoQueryIn; } & Partial> & Partial; diff --git a/app/src/ui/routes/data/data.$entity.$id.tsx b/app/src/ui/routes/data/data.$entity.$id.tsx index a641187..c458814 100644 --- a/app/src/ui/routes/data/data.$entity.$id.tsx +++ b/app/src/ui/routes/data/data.$entity.$id.tsx @@ -234,7 +234,7 @@ function EntityDetailInner({ const other = relation.other(entity); const [navigate] = useNavigate(); - const search: Partial = { + const search = { select: other.entity.getSelect(undefined, "table"), limit: 10, offset: 0