mirror of
https://github.com/shishantbiswas/bknd.git
synced 2026-03-17 12:56:05 +00:00
fix RepoQuery typings
This commit is contained in:
@@ -98,14 +98,14 @@ describe("data-query-impl", () => {
|
|||||||
test("with", () => {
|
test("with", () => {
|
||||||
decode({ with: ["posts"] }, { with: { posts: {} } });
|
decode({ with: ["posts"] }, { with: { posts: {} } });
|
||||||
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(
|
decode(
|
||||||
{
|
{
|
||||||
with: {
|
with: {
|
||||||
posts: {
|
posts: {
|
||||||
with: {
|
with: {
|
||||||
images: {
|
images: {
|
||||||
select: "id"
|
select: ["id"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
"type": "module",
|
"type": "module",
|
||||||
"sideEffects": false,
|
"sideEffects": false,
|
||||||
"bin": "./dist/cli/index.js",
|
"bin": "./dist/cli/index.js",
|
||||||
"version": "0.5.0",
|
"version": "0.6.0-rc.0",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
"test": "ALL_TESTS=1 bun test --bail",
|
"test": "ALL_TESTS=1 bun test --bail",
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ type LiteralExpressionCondition<Exps extends Expressions> = {
|
|||||||
[key: string]: Primitive | ExpressionCondition<Exps>;
|
[key: string]: Primitive | ExpressionCondition<Exps>;
|
||||||
};
|
};
|
||||||
|
|
||||||
const OperandOr = "$or";
|
const OperandOr = "$or" as const;
|
||||||
type OperandCondition<Exps extends Expressions> = {
|
type OperandCondition<Exps extends Expressions> = {
|
||||||
[OperandOr]?: LiteralExpressionCondition<Exps> | ExpressionCondition<Exps>;
|
[OperandOr]?: LiteralExpressionCondition<Exps> | ExpressionCondition<Exps>;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import type { DB } from "core";
|
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";
|
import { type BaseModuleApiOptions, ModuleApi, type PrimaryFieldType } from "modules";
|
||||||
|
|
||||||
export type DataApiOptions = BaseModuleApiOptions & {
|
export type DataApiOptions = BaseModuleApiOptions & {
|
||||||
@@ -19,14 +19,14 @@ export class DataApi extends ModuleApi<DataApiOptions> {
|
|||||||
readOne<E extends keyof DB | string, Data = E extends keyof DB ? DB[E] : EntityData>(
|
readOne<E extends keyof DB | string, Data = E extends keyof DB ? DB[E] : EntityData>(
|
||||||
entity: E,
|
entity: E,
|
||||||
id: PrimaryFieldType,
|
id: PrimaryFieldType,
|
||||||
query: Partial<Omit<RepoQuery, "where" | "limit" | "offset">> = {}
|
query: Omit<RepoQueryIn, "where" | "limit" | "offset"> = {}
|
||||||
) {
|
) {
|
||||||
return this.get<Pick<RepositoryResponse<Data>, "meta" | "data">>([entity as any, id], query);
|
return this.get<Pick<RepositoryResponse<Data>, "meta" | "data">>([entity as any, id], query);
|
||||||
}
|
}
|
||||||
|
|
||||||
readMany<E extends keyof DB | string, Data = E extends keyof DB ? DB[E] : EntityData>(
|
readMany<E extends keyof DB | string, Data = E extends keyof DB ? DB[E] : EntityData>(
|
||||||
entity: E,
|
entity: E,
|
||||||
query: Partial<RepoQuery> = {}
|
query: RepoQueryIn = {}
|
||||||
) {
|
) {
|
||||||
return this.get<Pick<RepositoryResponse<Data[]>, "meta" | "data">>(
|
return this.get<Pick<RepositoryResponse<Data[]>, "meta" | "data">>(
|
||||||
[entity as any],
|
[entity as any],
|
||||||
@@ -38,7 +38,7 @@ export class DataApi extends ModuleApi<DataApiOptions> {
|
|||||||
E extends keyof DB | string,
|
E extends keyof DB | string,
|
||||||
R extends keyof DB | string,
|
R extends keyof DB | string,
|
||||||
Data = R extends keyof DB ? DB[R] : EntityData
|
Data = R extends keyof DB ? DB[R] : EntityData
|
||||||
>(entity: E, id: PrimaryFieldType, reference: R, query: Partial<RepoQuery> = {}) {
|
>(entity: E, id: PrimaryFieldType, reference: R, query: RepoQueryIn = {}) {
|
||||||
return this.get<Pick<RepositoryResponse<Data[]>, "meta" | "data">>(
|
return this.get<Pick<RepositoryResponse<Data[]>, "meta" | "data">>(
|
||||||
[entity as any, id, reference],
|
[entity as any, id, reference],
|
||||||
query ?? this.options.defaultQuery
|
query ?? this.options.defaultQuery
|
||||||
|
|||||||
@@ -98,8 +98,8 @@ export class Entity<
|
|||||||
|
|
||||||
getDefaultSort() {
|
getDefaultSort() {
|
||||||
return {
|
return {
|
||||||
by: this.config.sort_field,
|
by: this.config.sort_field ?? "id",
|
||||||
dir: this.config.sort_dir
|
dir: this.config.sort_dir ?? "asc"
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ function key(e: unknown): string {
|
|||||||
return e as string;
|
return e as string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const expressions: TExpression<any, any, any>[] = [
|
const expressions = [
|
||||||
exp(
|
exp(
|
||||||
"$eq",
|
"$eq",
|
||||||
(v: Primitive) => isPrimitive(v),
|
(v: Primitive) => isPrimitive(v),
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ export * from "./prototype";
|
|||||||
|
|
||||||
export {
|
export {
|
||||||
type RepoQuery,
|
type RepoQuery,
|
||||||
|
type RepoQueryIn,
|
||||||
defaultQuerySchema,
|
defaultQuerySchema,
|
||||||
querySchema,
|
querySchema,
|
||||||
whereSchema
|
whereSchema
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import {
|
|||||||
Type,
|
Type,
|
||||||
Value
|
Value
|
||||||
} from "core/utils";
|
} from "core/utils";
|
||||||
import { WhereBuilder } from "../entities";
|
import { WhereBuilder, type WhereQuery } from "../entities";
|
||||||
|
|
||||||
const NumberOrString = (options: SchemaOptions = {}) =>
|
const NumberOrString = (options: SchemaOptions = {}) =>
|
||||||
Type.Transform(Type.Union([Type.Number(), Type.String()], options))
|
Type.Transform(Type.Union([Type.Number(), Type.String()], options))
|
||||||
@@ -15,10 +15,8 @@ const NumberOrString = (options: SchemaOptions = {}) =>
|
|||||||
.Encode(String);
|
.Encode(String);
|
||||||
|
|
||||||
const limit = NumberOrString({ default: 10 });
|
const limit = NumberOrString({ default: 10 });
|
||||||
|
|
||||||
const offset = NumberOrString({ default: 0 });
|
const offset = NumberOrString({ default: 0 });
|
||||||
|
|
||||||
// @todo: allow "id" and "-id"
|
|
||||||
const sort_default = { by: "id", dir: "asc" };
|
const sort_default = { by: "id", dir: "asc" };
|
||||||
const sort = Type.Transform(
|
const sort = Type.Transform(
|
||||||
Type.Union(
|
Type.Union(
|
||||||
@@ -28,20 +26,20 @@ const sort = Type.Transform(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
.Decode((value) => {
|
.Decode((value): { by: string; dir: "asc" | "desc" } => {
|
||||||
if (typeof value === "string") {
|
if (typeof value === "string") {
|
||||||
if (/^-?[a-zA-Z_][a-zA-Z0-9_.]*$/.test(value)) {
|
if (/^-?[a-zA-Z_][a-zA-Z0-9_.]*$/.test(value)) {
|
||||||
const dir = value[0] === "-" ? "desc" : "asc";
|
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)) {
|
} 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(
|
const stringArray = Type.Transform(
|
||||||
Type.Union([Type.String(), Type.Array(Type.String())], { default: [] })
|
Type.Union([Type.String(), Type.Array(Type.String())], { default: [] })
|
||||||
@@ -65,25 +63,18 @@ export const whereSchema = Type.Transform(
|
|||||||
})
|
})
|
||||||
.Encode(JSON.stringify);
|
.Encode(JSON.stringify);
|
||||||
|
|
||||||
export type ShallowRepoQuery = {
|
|
||||||
limit?: number;
|
|
||||||
offset?: number;
|
|
||||||
sort?: string | { by: string; dir: "asc" | "desc" };
|
|
||||||
select?: string[];
|
|
||||||
with?: string[] | Record<string, ShallowRepoQuery>;
|
|
||||||
join?: string[];
|
|
||||||
where?: any;
|
|
||||||
};
|
|
||||||
export type RepoWithSchema = Record<
|
export type RepoWithSchema = Record<
|
||||||
string,
|
string,
|
||||||
Omit<ShallowRepoQuery, "with"> & {
|
Omit<RepoQueryIn, "with"> & {
|
||||||
with?: unknown;
|
with?: unknown;
|
||||||
}
|
}
|
||||||
>;
|
>;
|
||||||
|
|
||||||
export const withSchema = <TSelf extends TThis>(Self: TSelf) =>
|
export const withSchema = <TSelf extends TThis>(Self: TSelf) =>
|
||||||
Type.Transform(Type.Union([stringArray, Type.Record(Type.String(), Self)]))
|
Type.Transform(Type.Union([stringArray, Type.Record(Type.String(), Self)]))
|
||||||
.Decode((value) => {
|
.Decode((value) => {
|
||||||
let _value = value;
|
let _value = typeof value === "string" ? [value] : value;
|
||||||
|
|
||||||
if (Array.isArray(value)) {
|
if (Array.isArray(value)) {
|
||||||
if (!value.every((v) => typeof v === "string")) {
|
if (!value.every((v) => typeof v === "string")) {
|
||||||
throw new Error("Invalid 'with' schema");
|
throw new Error("Invalid 'with' schema");
|
||||||
@@ -121,6 +112,14 @@ export const querySchema = Type.Recursive(
|
|||||||
{ $id: "query-schema" }
|
{ $id: "query-schema" }
|
||||||
);
|
);
|
||||||
|
|
||||||
export type RepoQueryIn = Static<typeof querySchema>;
|
export type RepoQueryIn = {
|
||||||
|
limit?: number;
|
||||||
|
offset?: number;
|
||||||
|
sort?: string | { by: string; dir: "asc" | "desc" };
|
||||||
|
select?: string[];
|
||||||
|
with?: string[] | Record<string, RepoQueryIn>;
|
||||||
|
join?: string[];
|
||||||
|
where?: WhereQuery;
|
||||||
|
};
|
||||||
export type RepoQuery = Required<StaticDecode<typeof querySchema>>;
|
export type RepoQuery = Required<StaticDecode<typeof querySchema>>;
|
||||||
export const defaultQuerySchema = Value.Default(querySchema, {}) as RepoQuery;
|
export const defaultQuerySchema = Value.Default(querySchema, {}) as RepoQuery;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import type { DB, PrimaryFieldType } from "core";
|
import type { DB, PrimaryFieldType } from "core";
|
||||||
import { encodeSearch, objectTransform } from "core/utils";
|
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 type { ModuleApi, ResponseObject } from "modules/ModuleApi";
|
||||||
import useSWR, { type SWRConfiguration, mutate } from "swr";
|
import useSWR, { type SWRConfiguration, mutate } from "swr";
|
||||||
import { type Api, useApi } from "ui/client";
|
import { type Api, useApi } from "ui/client";
|
||||||
@@ -49,7 +49,7 @@ export const useEntity = <
|
|||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
},
|
},
|
||||||
read: async (query: Partial<RepoQuery> = {}) => {
|
read: async (query: RepoQueryIn = {}) => {
|
||||||
const res = id ? await api.readOne(entity, id!, query) : await api.readMany(entity, query);
|
const res = id ? await api.readOne(entity, id!, query) : await api.readMany(entity, query);
|
||||||
if (!res.ok) {
|
if (!res.ok) {
|
||||||
throw new UseEntityApiError(res as any, `Failed to read entity "${entity}"`);
|
throw new UseEntityApiError(res as any, `Failed to read entity "${entity}"`);
|
||||||
@@ -88,7 +88,7 @@ export function makeKey(
|
|||||||
api: ModuleApi,
|
api: ModuleApi,
|
||||||
entity: string,
|
entity: string,
|
||||||
id?: PrimaryFieldType,
|
id?: PrimaryFieldType,
|
||||||
query?: Partial<RepoQuery>
|
query?: RepoQueryIn
|
||||||
) {
|
) {
|
||||||
return (
|
return (
|
||||||
"/" +
|
"/" +
|
||||||
@@ -105,7 +105,7 @@ export const useEntityQuery = <
|
|||||||
>(
|
>(
|
||||||
entity: Entity,
|
entity: Entity,
|
||||||
id?: Id,
|
id?: Id,
|
||||||
query?: Partial<RepoQuery>,
|
query?: RepoQueryIn,
|
||||||
options?: SWRConfiguration & { enabled?: boolean; revalidateOnMutate?: boolean }
|
options?: SWRConfiguration & { enabled?: boolean; revalidateOnMutate?: boolean }
|
||||||
) => {
|
) => {
|
||||||
const api = useApi().data;
|
const api = useApi().data;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { RepoQuery } from "data";
|
import type { RepoQuery, RepoQueryIn } from "data";
|
||||||
import type { MediaFieldSchema } from "media/AppMedia";
|
import type { MediaFieldSchema } from "media/AppMedia";
|
||||||
import type { TAppMediaConfig } from "media/media-schema";
|
import type { TAppMediaConfig } from "media/media-schema";
|
||||||
import { useId } from "react";
|
import { useId } from "react";
|
||||||
@@ -15,7 +15,7 @@ export type DropzoneContainerProps = {
|
|||||||
id: number;
|
id: number;
|
||||||
field: string;
|
field: string;
|
||||||
};
|
};
|
||||||
query?: Partial<RepoQuery>;
|
query?: RepoQueryIn;
|
||||||
} & Partial<Pick<TAppMediaConfig, "basepath" | "entity_name" | "storage">> &
|
} & Partial<Pick<TAppMediaConfig, "basepath" | "entity_name" | "storage">> &
|
||||||
Partial<DropzoneProps>;
|
Partial<DropzoneProps>;
|
||||||
|
|
||||||
|
|||||||
@@ -234,7 +234,7 @@ function EntityDetailInner({
|
|||||||
const other = relation.other(entity);
|
const other = relation.other(entity);
|
||||||
const [navigate] = useNavigate();
|
const [navigate] = useNavigate();
|
||||||
|
|
||||||
const search: Partial<RepoQuery> = {
|
const search = {
|
||||||
select: other.entity.getSelect(undefined, "table"),
|
select: other.entity.getSelect(undefined, "table"),
|
||||||
limit: 10,
|
limit: 10,
|
||||||
offset: 0
|
offset: 0
|
||||||
|
|||||||
Reference in New Issue
Block a user