startx 1.0.2 → 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.dockerignore +4 -0
- package/apps/cli/src/commands/index.ts +1 -1
- package/apps/cli/src/commands/{common → test}/test.ts +4 -2
- package/apps/cli/tsconfig.json +0 -1
- package/apps/core-server/Dockerfile +5 -4
- package/apps/core-server/package.json +1 -1
- package/apps/core-server/tsconfig.json +1 -1
- package/apps/queue-worker/package.json +1 -1
- package/apps/queue-worker/tsconfig.json +1 -1
- package/apps/startx-cli/dist/index.mjs +68 -53
- package/apps/startx-cli/src/commands/package.ts +453 -0
- package/apps/startx-cli/src/configs/scripts.ts +18 -2
- package/apps/startx-cli/src/index.ts +2 -4
- package/apps/startx-cli/src/types.ts +2 -4
- package/apps/startx-cli/src/utils/inquirer.ts +8 -1
- package/apps/web-client/.dockerignore +4 -0
- package/apps/web-client/app/app.css +1 -0
- package/apps/web-client/app/components.json +23 -0
- package/apps/web-client/app/config/auth/auth-state.ts +59 -0
- package/apps/web-client/app/config/axios-client.ts +87 -0
- package/apps/web-client/app/config/env.ts +5 -0
- package/apps/web-client/app/entry.client.tsx +7 -0
- package/apps/web-client/app/eslint.config.ts +4 -0
- package/apps/web-client/app/root.tsx +77 -0
- package/apps/web-client/app/routes/home.tsx +12 -0
- package/apps/web-client/app/routes.ts +3 -0
- package/apps/web-client/eslint.config.ts +4 -0
- package/apps/web-client/package.json +55 -0
- package/apps/web-client/react-router.config.ts +7 -0
- package/apps/web-client/tsconfig.json +22 -0
- package/apps/web-client/vite-env.d.ts +8 -0
- package/apps/web-client/vite.config.ts +30 -0
- package/biome.json +5 -0
- package/configs/eslint-config/eslint.config.ts +1 -0
- package/configs/eslint-config/src/configs/base.ts +0 -1
- package/configs/eslint-config/src/configs/frontend.ts +1 -1
- package/configs/eslint-config/tsconfig.json +1 -1
- package/configs/typescript-config/tsconfig.frontend.json +1 -1
- package/configs/vitest-config/tsconfig.json +1 -1
- package/package.json +1 -1
- package/packages/@db/drizzle/tsconfig.json +1 -1
- package/packages/@db/sqlite/tsconfig.json +1 -1
- package/packages/@repo/env/package.json +1 -2
- package/packages/@repo/env/src/utils.ts +17 -11
- package/packages/@repo/lib/package.json +3 -1
- package/packages/@repo/lib/src/session-module/i-session.ts +108 -0
- package/packages/@repo/lib/src/session-module/index.ts +8 -111
- package/packages/@repo/lib/src/session-module/redis-session.ts +44 -0
- package/packages/@repo/lib/tsconfig.json +0 -1
- package/packages/@repo/logger/package.json +0 -1
- package/packages/@repo/logger/tsconfig.json +1 -1
- package/packages/@repo/mail/tsconfig.json +1 -1
- package/packages/@repo/redis/tsconfig.json +1 -1
- package/packages/aix/package.json +2 -0
- package/packages/aix/src/providers/ai-interface.ts +4 -4
- package/packages/aix/src/providers/bedrock/bedrock.ts +261 -0
- package/packages/aix/src/providers/default-models.ts +65 -0
- package/packages/aix/src/providers/openai/openai.ts +2 -2
- package/packages/aix/src/providers/providers.ts +11 -0
- package/packages/aix/src/providers/types.ts +1 -1
- package/packages/{constants → common}/package.json +4 -2
- package/packages/{constants/src/index.ts → common/src/constants.ts} +0 -5
- package/packages/common/src/types/users.ts +10 -0
- package/packages/{constants → common}/tsconfig.json +0 -3
- package/packages/ui/components.json +15 -8
- package/packages/ui/package.json +23 -36
- package/packages/ui/src/api/axios/i-client.ts +40 -0
- package/packages/ui/src/api/index.ts +6 -0
- package/packages/ui/src/api/query-provider.tsx +34 -0
- package/packages/ui/src/api/use-api/api-builder.ts +139 -0
- package/packages/ui/src/api/use-api/api-helpers.ts +165 -0
- package/packages/ui/src/api/use-api/api-types.ts +138 -0
- package/packages/ui/src/api/use-api/query-factory.ts +66 -0
- package/packages/ui/src/api/use-api/react-query/types.ts +64 -0
- package/packages/ui/src/api/use-api/react-query/use-api-client.ts +56 -0
- package/packages/ui/src/api/use-api/react-query/use-api.ts +297 -0
- package/packages/ui/src/components/custom/form-wrapper.tsx +113 -160
- package/packages/ui/src/components/custom/grid-component.tsx +4 -4
- package/packages/ui/src/components/custom/hover-tool.tsx +1 -1
- package/packages/ui/src/components/custom/image-picker.tsx +18 -20
- package/packages/ui/src/components/custom/no-content.tsx +6 -16
- package/packages/ui/src/components/custom/page-section.tsx +14 -17
- package/packages/ui/src/components/custom/simple-popover.tsx +5 -9
- package/packages/ui/src/components/custom/theme-provider.tsx +117 -42
- package/packages/ui/src/components/custom/typography.tsx +20 -22
- package/packages/ui/src/components/extensions/timeline.tsx +100 -0
- package/packages/ui/src/components/ui/alert-dialog.tsx +46 -108
- package/packages/ui/src/components/ui/avatar.tsx +79 -42
- package/packages/ui/src/components/ui/badge.tsx +29 -34
- package/packages/ui/src/components/ui/breadcrumb.tsx +65 -81
- package/packages/ui/src/components/ui/button.tsx +80 -80
- package/packages/ui/src/components/ui/card.tsx +48 -69
- package/packages/ui/src/components/ui/carousel.tsx +184 -211
- package/packages/ui/src/components/ui/checkbox.tsx +21 -24
- package/packages/ui/src/components/ui/command.tsx +121 -102
- package/packages/ui/src/components/ui/dialog.tsx +45 -32
- package/packages/ui/src/components/ui/dropdown-menu.tsx +45 -33
- package/packages/ui/src/components/ui/field.tsx +218 -0
- package/packages/ui/src/components/ui/form.tsx +63 -76
- package/packages/ui/src/components/ui/input-group.tsx +137 -0
- package/packages/ui/src/components/ui/input-otp.tsx +60 -50
- package/packages/ui/src/components/ui/input.tsx +16 -15
- package/packages/ui/src/components/ui/label.tsx +14 -17
- package/packages/ui/src/components/ui/multiple-select.tsx +22 -33
- package/packages/ui/src/components/ui/popover.tsx +20 -8
- package/packages/ui/src/components/ui/select.tsx +33 -34
- package/packages/ui/src/components/ui/separator.tsx +8 -8
- package/packages/ui/src/components/ui/sheet.tsx +32 -59
- package/packages/ui/src/components/ui/sidebar.tsx +654 -0
- package/packages/ui/src/components/ui/skeleton.tsx +2 -8
- package/packages/ui/src/components/ui/sonner.tsx +39 -0
- package/packages/ui/src/components/ui/spinner.tsx +6 -13
- package/packages/ui/src/components/ui/switch.tsx +15 -10
- package/packages/ui/src/components/ui/table.tsx +48 -89
- package/packages/ui/src/components/ui/tabs.tsx +37 -15
- package/packages/ui/src/components/ui/textarea.tsx +13 -13
- package/packages/ui/src/components/ui/tooltip.tsx +37 -23
- package/packages/ui/src/{components/hooks → hooks}/event/use-click.tsx +6 -10
- package/packages/ui/src/hooks/time/use-timer.tsx +51 -0
- package/packages/ui/src/hooks/use-media-query.tsx +19 -0
- package/packages/ui/src/hooks/use-mobile.tsx +17 -0
- package/packages/ui/src/{components/hooks → hooks}/use-update-effect.tsx +2 -2
- package/packages/ui/src/lib/utils.ts +113 -0
- package/packages/ui/src/styles/globals.css +311 -0
- package/packages/ui/src/styles/tailwind.css +89 -0
- package/packages/ui/tsconfig.json +7 -9
- package/pnpm-workspace.yaml +74 -64
- package/packages/ui/postcss.config.mjs +0 -9
- package/packages/ui/src/components/extensions/carousel.tsx +0 -392
- package/packages/ui/src/components/hooks/time/useTimer.tsx +0 -51
- package/packages/ui/src/components/hooks/use-media-query.tsx +0 -19
- package/packages/ui/src/components/lib/utils.ts +0 -242
- package/packages/ui/src/components/ui/timeline.tsx +0 -118
- package/packages/ui/src/components/util/n-formattor.ts +0 -22
- package/packages/ui/src/components/util/storage.ts +0 -37
- package/packages/ui/src/globals.css +0 -87
- package/packages/ui/tailwind.config.ts +0 -94
- /package/packages/{constants → common}/eslint.config.ts +0 -0
- /package/packages/{constants → common}/src/api.ts +0 -0
- /package/packages/{constants → common}/src/time.ts +0 -0
- /package/packages/{constants → common}/vitest.config.ts +0 -0
- /package/packages/ui/src/{components/hooks/time/useDebounce.tsx → hooks/time/use-debounce.tsx} +0 -0
- /package/packages/ui/src/{components/hooks/time/useInterval.tsx → hooks/time/use-interval.tsx} +0 -0
- /package/packages/ui/src/{components/hooks/time/useTimeout.tsx → hooks/time/use-timeout.tsx} +0 -0
- /package/packages/ui/src/{components/hooks → hooks}/use-persistent-storage.tsx +0 -0
- /package/packages/ui/src/{components/hooks → hooks}/use-window-dimension.tsx +0 -0
- /package/packages/ui/src/{components/sonner.tsx → sonner.ts} +0 -0
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type { z } from "zod";
|
|
2
|
+
import { QueryKey, type RawSchema, type SchemaQueryKeys, type ZParams, type ZQuery } from "./api-types";
|
|
3
|
+
|
|
4
|
+
export type QueryKeyFactory<ZQ extends ZQuery, ZP extends ZParams> = {
|
|
5
|
+
(input?: { params?: z.input<ZP>; query?: z.input<ZQ> }): QueryKey<string>;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
function stringifyKeyValue(value: unknown): string {
|
|
9
|
+
if (value === null) return "::null::";
|
|
10
|
+
if (value === undefined) return "";
|
|
11
|
+
return String(value as string);
|
|
12
|
+
}
|
|
13
|
+
export function makeQueryKeyFactory<StaticKeys extends readonly string[], ZQ extends ZQuery, ZP extends ZParams>(
|
|
14
|
+
staticKey: StaticKeys,
|
|
15
|
+
zParams?: ZP,
|
|
16
|
+
zQuery?: ZQ
|
|
17
|
+
): QueryKeyFactory<ZQ, ZP> {
|
|
18
|
+
return input => {
|
|
19
|
+
const key: string[] = [...staticKey.filter(Boolean)];
|
|
20
|
+
|
|
21
|
+
if (input?.params && zParams) {
|
|
22
|
+
const shapeKeys = Object.keys(zParams?.shape);
|
|
23
|
+
|
|
24
|
+
for (const k of shapeKeys) {
|
|
25
|
+
const v = input.params[k];
|
|
26
|
+
|
|
27
|
+
if (v !== undefined) {
|
|
28
|
+
key.push(`params:${k}:${stringifyKeyValue(v)}`);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (input?.query && zQuery) {
|
|
34
|
+
const shapeKeys = Object.keys(zQuery?.shape);
|
|
35
|
+
|
|
36
|
+
for (const k of shapeKeys) {
|
|
37
|
+
const v = input.query[k];
|
|
38
|
+
|
|
39
|
+
if (v !== undefined) {
|
|
40
|
+
if (Array.isArray(v)) {
|
|
41
|
+
for (const item of v) {
|
|
42
|
+
key.push(`query:${k}:${stringifyKeyValue(item)}`);
|
|
43
|
+
}
|
|
44
|
+
} else {
|
|
45
|
+
key.push(`query:${k}:${stringifyKeyValue(v)}`);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return key as QueryKey<string>;
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function createQueryKeysProxy<Schema extends RawSchema>(schema: Schema): SchemaQueryKeys<Schema> {
|
|
56
|
+
return new Proxy(schema, {
|
|
57
|
+
get(target, prop) {
|
|
58
|
+
if (typeof prop === "symbol" || prop === "then") {
|
|
59
|
+
return Reflect.get(target, prop);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const entry = target[prop as keyof Schema];
|
|
63
|
+
return entry?.queryKey ?? (() => [String(prop)]);
|
|
64
|
+
},
|
|
65
|
+
}) as SchemaQueryKeys<Schema>;
|
|
66
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import type { UseQueryResult, UseMutationResult } from "@tanstack/react-query";
|
|
2
|
+
import type { z } from "zod";
|
|
3
|
+
import type { IPaginatedData, TimeString } from "../api-types";
|
|
4
|
+
|
|
5
|
+
export type ExtractData<E> = "data" extends keyof E ? E["data"] : unknown;
|
|
6
|
+
export type ExtractZQuery<E> = "zQuery" extends keyof E ? E["zQuery"] : never;
|
|
7
|
+
export type ExtractZParams<E> = "zParams" extends keyof E ? E["zParams"] : never;
|
|
8
|
+
export type ExtractZBody<E> = "zBody" extends keyof E ? NonNullable<E["zBody"]> : never;
|
|
9
|
+
|
|
10
|
+
export type WithAbort<T> = T & { abort: () => void };
|
|
11
|
+
|
|
12
|
+
type CommonQueryOptions = {
|
|
13
|
+
staleTime?: number | TimeString;
|
|
14
|
+
enabled?: boolean;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export type FetchOptions<E> = CommonQueryOptions & {
|
|
18
|
+
query?: z.output<ExtractZQuery<E>>;
|
|
19
|
+
params?: z.output<ExtractZParams<E>>;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export type PaginatedFetchOptions<E> = CommonQueryOptions & {
|
|
23
|
+
query?: z.output<ExtractZQuery<E>>;
|
|
24
|
+
params?: z.output<ExtractZParams<E>>;
|
|
25
|
+
page?: number;
|
|
26
|
+
limit?: number;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export type MutationVariables<E> = {
|
|
30
|
+
body?: z.output<ExtractZBody<E>>;
|
|
31
|
+
query?: z.output<ExtractZQuery<E>>;
|
|
32
|
+
params?: z.output<ExtractZParams<E>>;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export type MutationOptions<E> = {
|
|
36
|
+
query?: z.output<ExtractZQuery<E>>;
|
|
37
|
+
params?: z.output<ExtractZParams<E>>;
|
|
38
|
+
body?: z.output<ExtractZBody<E>>;
|
|
39
|
+
isFormData?: boolean;
|
|
40
|
+
onSuccess?: (data: ExtractData<E>, variables: MutationVariables<E>) => void;
|
|
41
|
+
onError?: (error: Error, variables: MutationVariables<E>) => void;
|
|
42
|
+
onMutate?: (variables: MutationVariables<E>) => void;
|
|
43
|
+
overwriteEvents?: boolean;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
export type UseApiOptions<Schema, K extends keyof Schema> = Schema[K] extends infer E
|
|
47
|
+
? E extends { apiType: "fetch" }
|
|
48
|
+
? FetchOptions<E>
|
|
49
|
+
: E extends { apiType: "paginated-fetch" }
|
|
50
|
+
? PaginatedFetchOptions<E>
|
|
51
|
+
: E extends { apiType: "mutation" }
|
|
52
|
+
? MutationOptions<E>
|
|
53
|
+
: never
|
|
54
|
+
: never;
|
|
55
|
+
|
|
56
|
+
export type UseApiReturn<Schema, K extends keyof Schema> = Schema[K] extends { apiType: "fetch" }
|
|
57
|
+
? WithAbort<UseQueryResult<ExtractData<Schema[K]>>>
|
|
58
|
+
: Schema[K] extends { apiType: "paginated-fetch" }
|
|
59
|
+
? WithAbort<
|
|
60
|
+
UseQueryResult<IPaginatedData<ExtractData<Schema[K]>, Schema[K] extends { other: infer O } ? O : unknown>>
|
|
61
|
+
>
|
|
62
|
+
: Schema[K] extends { apiType: "mutation" }
|
|
63
|
+
? WithAbort<UseMutationResult<ExtractData<Schema[K]>, Error, MutationVariables<Schema[K]>>>
|
|
64
|
+
: never;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { useQueryClient, type Updater } from "@tanstack/react-query";
|
|
2
|
+
import { useMemo } from "react";
|
|
3
|
+
import type { z } from "zod";
|
|
4
|
+
import type { RawSchema, IPaginatedData, QueryKey } from "../api-types";
|
|
5
|
+
import { createQueryKeysProxy } from "../query-factory";
|
|
6
|
+
import type { ExtractZQuery, ExtractZParams, ExtractData } from "./types";
|
|
7
|
+
|
|
8
|
+
export type SafeZodInput<T> = T extends z.ZodTypeAny ? z.input<T> : never;
|
|
9
|
+
|
|
10
|
+
export type InputArgs<E> = {
|
|
11
|
+
query?: SafeZodInput<ExtractZQuery<E>>;
|
|
12
|
+
params?: SafeZodInput<ExtractZParams<E>>;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export type ResolvedData<E> = E extends { apiType: "paginated-fetch" }
|
|
16
|
+
? IPaginatedData<ExtractData<E>, E extends { other: infer O } ? O : unknown>
|
|
17
|
+
: ExtractData<E>;
|
|
18
|
+
|
|
19
|
+
export function useApiClient<Schema extends RawSchema>(schema: Schema) {
|
|
20
|
+
const queryClient = useQueryClient();
|
|
21
|
+
const proxy = useMemo(() => createQueryKeysProxy(schema), [schema]);
|
|
22
|
+
|
|
23
|
+
const getQueryKey = <K extends keyof Schema & string>(key: K, input?: InputArgs<Schema[K]>): QueryKey<string> => {
|
|
24
|
+
const factory = proxy[key] as unknown as (input?: InputArgs<Schema[K]>) => QueryKey<string>;
|
|
25
|
+
return factory(input);
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
return {
|
|
29
|
+
invalidate: <K extends keyof Schema & string>(key: K, input?: InputArgs<Schema[K]>) =>
|
|
30
|
+
queryClient.invalidateQueries({ queryKey: getQueryKey(key, input) }),
|
|
31
|
+
|
|
32
|
+
refetch: <K extends keyof Schema & string>(key: K, input?: InputArgs<Schema[K]>) =>
|
|
33
|
+
queryClient.refetchQueries({ queryKey: getQueryKey(key, input) }),
|
|
34
|
+
|
|
35
|
+
getData: <K extends keyof Schema & string>(key: K, input?: InputArgs<Schema[K]>) =>
|
|
36
|
+
queryClient.getQueryData<ResolvedData<Schema[K]>>(getQueryKey(key, input)),
|
|
37
|
+
|
|
38
|
+
setData: <K extends keyof Schema & string>(
|
|
39
|
+
key: K,
|
|
40
|
+
dataOrUpdater: Updater<ResolvedData<Schema[K]> | undefined, ResolvedData<Schema[K]> | undefined>,
|
|
41
|
+
input?: InputArgs<Schema[K]>
|
|
42
|
+
) => queryClient.setQueryData<ResolvedData<Schema[K]>>(getQueryKey(key, input), dataOrUpdater),
|
|
43
|
+
|
|
44
|
+
remove: <K extends keyof Schema & string>(key: K, input?: InputArgs<Schema[K]>) =>
|
|
45
|
+
queryClient.removeQueries({ queryKey: getQueryKey(key, input) }),
|
|
46
|
+
|
|
47
|
+
reset: <K extends keyof Schema & string>(key: K, input?: InputArgs<Schema[K]>) =>
|
|
48
|
+
queryClient.resetQueries({ queryKey: getQueryKey(key, input) }),
|
|
49
|
+
|
|
50
|
+
cancel: <K extends keyof Schema & string>(key?: K, input?: InputArgs<Schema[K]>) =>
|
|
51
|
+
key ? queryClient.cancelQueries({ queryKey: getQueryKey(key, input) }) : queryClient.cancelQueries(),
|
|
52
|
+
|
|
53
|
+
getQueryKey,
|
|
54
|
+
queryKeys: proxy,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
import {
|
|
2
|
+
useQuery,
|
|
3
|
+
useMutation,
|
|
4
|
+
useQueryClient,
|
|
5
|
+
type UseQueryOptions,
|
|
6
|
+
type UseQueryResult,
|
|
7
|
+
type UseMutationResult,
|
|
8
|
+
} from "@tanstack/react-query";
|
|
9
|
+
import { type AxiosError, type AxiosInstance, type AxiosRequestConfig } from "axios";
|
|
10
|
+
import { useMemo, useRef } from "react";
|
|
11
|
+
import { z } from "zod";
|
|
12
|
+
import { FormUtils } from "@repo/ui/lib/utils";
|
|
13
|
+
import { ApiHelper } from "../api-helpers";
|
|
14
|
+
import type {
|
|
15
|
+
IFetchOptions,
|
|
16
|
+
IPaginatedFetchOptions,
|
|
17
|
+
IFetchMutationOptions,
|
|
18
|
+
IPaginatedData,
|
|
19
|
+
QueryEvent,
|
|
20
|
+
CbAction,
|
|
21
|
+
RawSchema,
|
|
22
|
+
QueryKey,
|
|
23
|
+
ZQuery,
|
|
24
|
+
ZParams,
|
|
25
|
+
} from "../api-types";
|
|
26
|
+
import { createQueryKeysProxy } from "../query-factory";
|
|
27
|
+
|
|
28
|
+
import type {
|
|
29
|
+
UseApiOptions,
|
|
30
|
+
UseApiReturn,
|
|
31
|
+
MutationVariables,
|
|
32
|
+
ExtractData,
|
|
33
|
+
FetchOptions,
|
|
34
|
+
PaginatedFetchOptions,
|
|
35
|
+
MutationOptions,
|
|
36
|
+
} from "./types";
|
|
37
|
+
|
|
38
|
+
async function processEvents<
|
|
39
|
+
Schema extends RawSchema,
|
|
40
|
+
IK extends string,
|
|
41
|
+
IQ extends z.output<ZQuery>,
|
|
42
|
+
IP extends z.output<ZParams>,
|
|
43
|
+
IB,
|
|
44
|
+
ID,
|
|
45
|
+
>(
|
|
46
|
+
events: QueryEvent<IK, IQ, IP, IB, ID, Schema> | undefined,
|
|
47
|
+
data: ID,
|
|
48
|
+
variables: { query?: IQ; params?: IP; body?: IB } | undefined,
|
|
49
|
+
schemaProxy: ReturnType<typeof createQueryKeysProxy<Schema>>,
|
|
50
|
+
queryClient: ReturnType<typeof useQueryClient>,
|
|
51
|
+
override?: boolean
|
|
52
|
+
) {
|
|
53
|
+
if (!events) return;
|
|
54
|
+
const payload = { data, ...variables };
|
|
55
|
+
const runAction = async (
|
|
56
|
+
action: CbAction<Schema, IP, IQ, IB, ID, IK, Array<QueryKey<IK>>> | undefined,
|
|
57
|
+
exec: (key: QueryKey<IK>) => Promise<void>
|
|
58
|
+
) => {
|
|
59
|
+
if (!action) return;
|
|
60
|
+
const keys: Array<QueryKey<IK>> = typeof action === "function" ? action(payload, schemaProxy) : action;
|
|
61
|
+
for (const key of keys) {
|
|
62
|
+
await exec(key);
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
await runAction(events.invalidateQuery, key => queryClient.invalidateQueries({ queryKey: key }));
|
|
67
|
+
await runAction(events.refetchQuery, key => queryClient.refetchQueries({ queryKey: key }));
|
|
68
|
+
await runAction(events.clearQuery, key => queryClient.resetQueries({ queryKey: key }));
|
|
69
|
+
|
|
70
|
+
if (!override) {
|
|
71
|
+
if (typeof events.fn === "function") await events.fn?.(payload, schemaProxy);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function useFetchApi<ID, ZQ extends ZQuery, ZP extends ZParams>(
|
|
76
|
+
key: keyof RawSchema,
|
|
77
|
+
endpoint: IFetchOptions<ID, ZQ, ZP>,
|
|
78
|
+
axiosClient: AxiosInstance,
|
|
79
|
+
options: {
|
|
80
|
+
query?: z.output<ZQ>;
|
|
81
|
+
params?: z.output<ZP>;
|
|
82
|
+
staleTime?: number;
|
|
83
|
+
enabled?: boolean;
|
|
84
|
+
}
|
|
85
|
+
): UseQueryResult<ID> & { abort: () => void } {
|
|
86
|
+
const queryClient = useQueryClient();
|
|
87
|
+
const queryKey = useMemo(
|
|
88
|
+
() =>
|
|
89
|
+
ApiHelper.getQueryKey(key, endpoint.key, {
|
|
90
|
+
params: options.params,
|
|
91
|
+
query: options.query,
|
|
92
|
+
}),
|
|
93
|
+
[options.query, options.params]
|
|
94
|
+
);
|
|
95
|
+
const staleTime = ApiHelper.parseTime(ApiHelper.merge(options.staleTime, endpoint.staleTime));
|
|
96
|
+
|
|
97
|
+
const query = useQuery<ID>({
|
|
98
|
+
queryKey,
|
|
99
|
+
queryFn: async ({ signal }) => {
|
|
100
|
+
const config: AxiosRequestConfig = {
|
|
101
|
+
method: endpoint.method || "GET",
|
|
102
|
+
url: ApiHelper.buildUrl({
|
|
103
|
+
route: endpoint.route,
|
|
104
|
+
params: options.params,
|
|
105
|
+
searchParams: options.query,
|
|
106
|
+
}),
|
|
107
|
+
params: options.query,
|
|
108
|
+
signal,
|
|
109
|
+
};
|
|
110
|
+
const resp = await axiosClient.request<ID>(config);
|
|
111
|
+
|
|
112
|
+
return resp.data;
|
|
113
|
+
},
|
|
114
|
+
staleTime,
|
|
115
|
+
enabled: options.enabled ?? endpoint.enable?.isEnable ?? true,
|
|
116
|
+
retry: endpoint.retry?.count,
|
|
117
|
+
retryDelay: endpoint.retry?.interval,
|
|
118
|
+
refetchInterval: endpoint.refetch?.interval?.ms,
|
|
119
|
+
refetchIntervalInBackground: endpoint.refetch?.interval?.inBackground,
|
|
120
|
+
refetchOnMount: endpoint.refetch?.onMount ? "always" : false,
|
|
121
|
+
refetchOnWindowFocus: endpoint.refetch?.onFocus ? "always" : false,
|
|
122
|
+
} as UseQueryOptions<ID>);
|
|
123
|
+
|
|
124
|
+
return {
|
|
125
|
+
...query,
|
|
126
|
+
abort: () => queryClient.cancelQueries({ queryKey }),
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function usePaginatedFetchApi<ID, IO, ZQ extends ZQuery, ZP extends ZParams>(
|
|
131
|
+
key: keyof RawSchema,
|
|
132
|
+
endpoint: IPaginatedFetchOptions<ID, IO, ZQ, ZP>,
|
|
133
|
+
axiosClient: AxiosInstance,
|
|
134
|
+
options: {
|
|
135
|
+
query?: z.output<ZQ>;
|
|
136
|
+
params?: z.output<ZP>;
|
|
137
|
+
page?: number;
|
|
138
|
+
limit?: number;
|
|
139
|
+
staleTime?: number;
|
|
140
|
+
enabled?: boolean;
|
|
141
|
+
}
|
|
142
|
+
): UseQueryResult<{ data: IPaginatedData<ID, IO> }> & { abort: () => void } {
|
|
143
|
+
const queryClient = useQueryClient();
|
|
144
|
+
const mergedQuery = useMemo(
|
|
145
|
+
() =>
|
|
146
|
+
ApiHelper.merge(options.query, {
|
|
147
|
+
page: options.page,
|
|
148
|
+
limit: options.limit,
|
|
149
|
+
}),
|
|
150
|
+
[options.query, options.page, options.limit]
|
|
151
|
+
);
|
|
152
|
+
|
|
153
|
+
const queryKey = useMemo(
|
|
154
|
+
() =>
|
|
155
|
+
ApiHelper.getQueryKey(key, endpoint.key, {
|
|
156
|
+
params: options.params,
|
|
157
|
+
query: mergedQuery,
|
|
158
|
+
}),
|
|
159
|
+
[key, endpoint.key, options.params, mergedQuery]
|
|
160
|
+
);
|
|
161
|
+
|
|
162
|
+
const staleTime = ApiHelper.parseTime(ApiHelper.merge(options.staleTime, endpoint.staleTime));
|
|
163
|
+
|
|
164
|
+
const query = useQuery<{ data: IPaginatedData<ID, IO> }>({
|
|
165
|
+
queryKey,
|
|
166
|
+
queryFn: async ({ signal }) => {
|
|
167
|
+
const config: AxiosRequestConfig = {
|
|
168
|
+
method: endpoint.method || "GET",
|
|
169
|
+
url: ApiHelper.buildUrl({
|
|
170
|
+
route: endpoint.route,
|
|
171
|
+
params: options.params,
|
|
172
|
+
searchParams: mergedQuery,
|
|
173
|
+
}),
|
|
174
|
+
signal,
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
const resp = await axiosClient.request<{
|
|
178
|
+
data: IPaginatedData<ID, IO>;
|
|
179
|
+
}>(config);
|
|
180
|
+
|
|
181
|
+
return resp.data;
|
|
182
|
+
},
|
|
183
|
+
staleTime,
|
|
184
|
+
enabled: options.enabled ?? endpoint.enable?.isEnable ?? true,
|
|
185
|
+
retry: endpoint.retry?.count,
|
|
186
|
+
retryDelay: endpoint.retry?.interval,
|
|
187
|
+
refetchInterval: endpoint.refetch?.interval?.ms,
|
|
188
|
+
refetchIntervalInBackground: endpoint.refetch?.interval?.inBackground,
|
|
189
|
+
refetchOnMount: endpoint.refetch?.onMount ? "always" : false,
|
|
190
|
+
refetchOnWindowFocus: endpoint.refetch?.onFocus ? "always" : false,
|
|
191
|
+
} as UseQueryOptions<{ data: IPaginatedData<ID, IO> }>);
|
|
192
|
+
|
|
193
|
+
return {
|
|
194
|
+
...query,
|
|
195
|
+
abort: () => queryClient.cancelQueries({ queryKey }),
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
function useMutationApi<Schema extends RawSchema, K extends keyof Schema & string>(
|
|
199
|
+
endpoint: IFetchMutationOptions<K>,
|
|
200
|
+
axiosClient: AxiosInstance,
|
|
201
|
+
proxy: ReturnType<typeof createQueryKeysProxy<Schema>>,
|
|
202
|
+
options?: MutationOptions<Schema[K]>
|
|
203
|
+
): UseMutationResult<ExtractData<Schema[K]>, Error, MutationVariables<Schema[K]>> & { abort: () => void } {
|
|
204
|
+
const queryClient = useQueryClient();
|
|
205
|
+
const abortControllerRef = useRef<AbortController | null>(null);
|
|
206
|
+
const isFormData = ApiHelper.merge(options?.isFormData, endpoint.isFormData);
|
|
207
|
+
|
|
208
|
+
const mutation = useMutation<ExtractData<Schema[K]>, Error, MutationVariables<Schema[K]>>({
|
|
209
|
+
mutationFn: async (variables: MutationVariables<Schema[K]>) => {
|
|
210
|
+
abortControllerRef.current = new AbortController();
|
|
211
|
+
|
|
212
|
+
if (endpoint.zBody && endpoint.validateBody !== false && options?.overwriteEvents !== true) {
|
|
213
|
+
const valid = ApiHelper.validateSchema(endpoint.zBody, variables.body);
|
|
214
|
+
if (!valid.success) {
|
|
215
|
+
throw new Error(valid.errors.join(", "));
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
const body = ApiHelper.merge(variables.body, options?.body)!;
|
|
220
|
+
const config: AxiosRequestConfig = {
|
|
221
|
+
method: endpoint.method || "POST",
|
|
222
|
+
url: ApiHelper.buildUrl(
|
|
223
|
+
ApiHelper.merge(
|
|
224
|
+
{ route: endpoint.route, params: variables.params, searchParams: variables.query },
|
|
225
|
+
{ route: endpoint.route, params: options?.params, searchParams: options?.query }
|
|
226
|
+
)
|
|
227
|
+
),
|
|
228
|
+
params: ApiHelper.merge(variables.query, options?.query),
|
|
229
|
+
data: isFormData ? FormUtils.getFormData(body!) : body,
|
|
230
|
+
...(isFormData && { headers: { "Content-Type": "multipart/form-data" } }),
|
|
231
|
+
signal: abortControllerRef.current.signal,
|
|
232
|
+
};
|
|
233
|
+
|
|
234
|
+
const response = await axiosClient.request<ExtractData<Schema[K]>>(config);
|
|
235
|
+
|
|
236
|
+
await processEvents(endpoint.onFetch, response.data, variables, proxy, queryClient, options?.overwriteEvents);
|
|
237
|
+
|
|
238
|
+
return response.data;
|
|
239
|
+
},
|
|
240
|
+
onSuccess: async (data, variables) => {
|
|
241
|
+
options?.onSuccess?.(data, variables);
|
|
242
|
+
await processEvents(endpoint.onSuccess, data, variables, proxy, queryClient, options?.overwriteEvents);
|
|
243
|
+
},
|
|
244
|
+
onError: async (error: Error, variables) => {
|
|
245
|
+
options?.onError?.(error, variables);
|
|
246
|
+
await processEvents(
|
|
247
|
+
endpoint.onError,
|
|
248
|
+
error as AxiosError<any>,
|
|
249
|
+
variables,
|
|
250
|
+
proxy,
|
|
251
|
+
queryClient,
|
|
252
|
+
options?.overwriteEvents
|
|
253
|
+
);
|
|
254
|
+
},
|
|
255
|
+
onMutate: options?.onMutate,
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
return {
|
|
259
|
+
...mutation,
|
|
260
|
+
abort: () => abortControllerRef.current?.abort(),
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
export function useApi<Schema extends RawSchema>(schema: Schema, axiosClient: AxiosInstance) {
|
|
264
|
+
return <K extends keyof Schema & string>(key: K, options?: UseApiOptions<Schema, K>): UseApiReturn<Schema, K> => {
|
|
265
|
+
const endpoint = schema[key];
|
|
266
|
+
const proxy = useMemo(() => createQueryKeysProxy(schema), [schema]);
|
|
267
|
+
|
|
268
|
+
if (endpoint.apiType === "fetch") {
|
|
269
|
+
const ep = endpoint as IFetchOptions<unknown, ZQuery, ZParams>;
|
|
270
|
+
const opts = options as FetchOptions<Schema[K]> | undefined;
|
|
271
|
+
return useFetchApi(key, ep, axiosClient, {
|
|
272
|
+
query: opts?.query,
|
|
273
|
+
params: opts?.params,
|
|
274
|
+
staleTime: ApiHelper.parseTime(ApiHelper.merge(opts?.staleTime, ep.staleTime)),
|
|
275
|
+
enabled: opts?.enabled,
|
|
276
|
+
}) as UseApiReturn<Schema, K>;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
if (endpoint.apiType === "paginated-fetch") {
|
|
280
|
+
const ep = endpoint as IPaginatedFetchOptions<unknown, unknown, ZQuery, ZParams>;
|
|
281
|
+
const opts = options as PaginatedFetchOptions<Schema[K]> | undefined;
|
|
282
|
+
return usePaginatedFetchApi(key, ep, axiosClient, {
|
|
283
|
+
query: opts?.query,
|
|
284
|
+
params: opts?.params,
|
|
285
|
+
staleTime: ApiHelper.parseTime(ApiHelper.merge(opts?.staleTime, ep.staleTime)),
|
|
286
|
+
enabled: opts?.enabled,
|
|
287
|
+
}) as UseApiReturn<Schema, K>;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
if (endpoint.apiType === "mutation") {
|
|
291
|
+
const ep = endpoint as IFetchMutationOptions<K>;
|
|
292
|
+
const opts = options as MutationOptions<Schema[K]> | undefined;
|
|
293
|
+
return useMutationApi(ep, axiosClient, proxy, opts) as UseApiReturn<Schema, K>;
|
|
294
|
+
}
|
|
295
|
+
throw new Error(`Unknown API type: ${endpoint.apiType}`);
|
|
296
|
+
};
|
|
297
|
+
}
|