@cmtlyt/lingshu-toolkit 0.4.0 → 0.5.0

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/dist/247.js ADDED
@@ -0,0 +1,66 @@
1
+ import { dataHandler, logger, $dt, throwError, $t } from "./707.js";
2
+ const validInfo = $dt({
3
+ storageKey: 'validString',
4
+ storageType: $t["enum"]([
5
+ 'local',
6
+ 'session',
7
+ 'memory'
8
+ ], 'local'),
9
+ autoSaveInterval: $t.number(0)
10
+ });
11
+ const memoryStorage = {
12
+ data: {},
13
+ getItem (key) {
14
+ return this.data[key];
15
+ },
16
+ setItem (key, value) {
17
+ this.data[key] = value;
18
+ },
19
+ removeItem (key) {
20
+ delete this.data[key];
21
+ }
22
+ };
23
+ function getStorage(storageType) {
24
+ try {
25
+ if ('memory' === storageType) return memoryStorage;
26
+ return 'local' === storageType ? localStorage : sessionStorage;
27
+ } catch {
28
+ logger.warn('createStorage', 'Failed to access localStorage or sessionStorage, using memoryStorage instead.');
29
+ return memoryStorage;
30
+ }
31
+ }
32
+ const CLEAR_FLAG = Symbol('cleared');
33
+ function createStorageHandler(storageKey, initialData, options = {}) {
34
+ const { storageKey: validStorageKey, storageType, autoSaveInterval } = dataHandler({
35
+ storageKey,
36
+ ...options
37
+ }, validInfo, {
38
+ unwrap: true
39
+ });
40
+ const storage = getStorage(storageType);
41
+ const storageData = storage.getItem(validStorageKey);
42
+ const context = {
43
+ data: storageData ? JSON.parse(storageData) : initialData || {}
44
+ };
45
+ return {
46
+ get (key) {
47
+ if (context.data === CLEAR_FLAG) throwError('createStorageHandler', 'Storage has been cleared.');
48
+ if (null == key) return context.data;
49
+ return context.data[key];
50
+ },
51
+ set (value, key) {
52
+ if (context.data === CLEAR_FLAG) throwError('createStorageHandler', 'Storage has been cleared.');
53
+ if (null == key) context.data = value;
54
+ else context.data[key] = value;
55
+ if (autoSaveInterval > 0) setTimeout(()=>{
56
+ storage.setItem(validStorageKey, JSON.stringify(context.data));
57
+ }, autoSaveInterval);
58
+ else storage.setItem(validStorageKey, JSON.stringify(context.data));
59
+ },
60
+ clear () {
61
+ context.data = CLEAR_FLAG;
62
+ storage.removeItem(validStorageKey);
63
+ }
64
+ };
65
+ }
66
+ export { createStorageHandler };
package/dist/707.js CHANGED
@@ -9,6 +9,7 @@ function throwType(fnName, message) {
9
9
  }
10
10
  const logger = new Proxy(console, {
11
11
  get (target, prop, receiver) {
12
+ if ((globalThis.$$lingshu$$ || {}).disableLogger) return ()=>void 0;
12
13
  const oldLog = Reflect.get(target, prop, receiver).bind(console);
13
14
  return (fnName, ...args)=>{
14
15
  oldLog(`[@cmtlyt/lingshu-toolkit#${fnName}]:`, ...args);
@@ -1,6 +1,6 @@
1
1
  import { useEffect, useEffectEvent, useMemo, useReducer, useRef, useState } from "react";
2
2
  import { dataHandler, logger, $dt, $t, throwType } from "../707.js";
3
- import { createStorageHandler } from "../607.js";
3
+ import { createStorageHandler } from "../247.js";
4
4
  function useToggle(defualtValue = false, reverseValue) {
5
5
  const [state, setState] = useState(defualtValue);
6
6
  const toggleRef = useRef([
@@ -1,5 +1,5 @@
1
1
  import { useEffect, useRef } from "react";
2
- import { $t, dataHandler } from "../../shared/index.js";
2
+ import { $t, dataHandler } from "../../shared/data-handler/index.js";
3
3
  function useMount(callback) {
4
4
  const callbackRef = useRef(callback);
5
5
  useEffect(()=>{
@@ -0,0 +1,26 @@
1
+ import type { APIConfig, APIMap, APIMapTransformMethods, APITransformMethod, DefaultAPIConfig, DefineAPIConfig } from './types';
2
+ /**
3
+ * 通过 API config map 创建请求对象
4
+ *
5
+ * @param apiMap API config map
6
+ * @param defaultConfig 默认配置
7
+ */
8
+ export declare function createApiWithMap<M extends APIMap, D extends DefaultAPIConfig = DefaultAPIConfig>(apiMap: M, defaultConfig?: D): APIMapTransformMethods<M, D>;
9
+ /**
10
+ * 通过 API config 创建一个请求方法
11
+ *
12
+ * @param api API config
13
+ * @param defaultConfig 默认配置
14
+ * @param custom 是否为自定义请求
15
+ */
16
+ export declare function createApi<A extends APIConfig, D extends DefaultAPIConfig = DefaultAPIConfig, C extends boolean = false>(api: A, defaultConfig?: D, custom?: C): APITransformMethod<A, D, C>;
17
+ /**
18
+ * 定义 API, ts 支持, 获取更好的类型声明
19
+ * @warn 不应该被复用, 如果希望复用的话请使用 apiInstance.$
20
+ */
21
+ export declare function defineApi<U extends string, A extends DefineAPIConfig<U>>(_api: A): A;
22
+ /**
23
+ * 定义 API map, ts 支持, 获取更好的类型声明
24
+ * @warn 不应该被复用, 如果希望复用的话请使用 apiInstance.$
25
+ */
26
+ export declare function defineApiMap<U extends string, A extends APIMap<U>>(_apiMap: A): A;
@@ -0,0 +1,79 @@
1
+ import { logger } from "../logger/index.js";
2
+ import { throwType } from "../throw-error/index.js";
3
+ import { isString, isTrue } from "../utils/verify.js";
4
+ import { request } from "./request.js";
5
+ import { apiNamesCheck, createInstance, getInstanceMemberOrApi, instanceMemberGetter, isAbsUrl } from "./utils.js";
6
+ const FROM_DEFINE = Symbol('fromDefine');
7
+ function createApiWithMap(apiMap, defaultConfig) {
8
+ const fromDefine = apiMap[FROM_DEFINE];
9
+ delete apiMap[FROM_DEFINE];
10
+ const proxyCache = Object.create(null);
11
+ const realDefaultConfig = defaultConfig || {};
12
+ if (!isTrue(fromDefine)) apiNamesCheck(apiMap);
13
+ const instanceObj = createInstance(apiMap, realDefaultConfig, defaultConfig);
14
+ const proxy = new Proxy(apiMap, {
15
+ get (target, prop, receiver) {
16
+ if (Reflect.getOwnPropertyDescriptor(proxyCache, prop)) return proxyCache[prop];
17
+ const { instanceMember, api, isCustom = false } = getInstanceMemberOrApi(target, prop, receiver, instanceObj) || {};
18
+ if (instanceMember) return instanceMember;
19
+ if (!api) return;
20
+ let result = null;
21
+ result = isString(api.url) ? createApi({
22
+ ...api,
23
+ [FROM_DEFINE]: fromDefine
24
+ }, realDefaultConfig, isCustom) : createApiWithMap({
25
+ ...api,
26
+ [FROM_DEFINE]: fromDefine
27
+ }, realDefaultConfig);
28
+ proxyCache[prop] = result;
29
+ return result;
30
+ }
31
+ });
32
+ return proxy;
33
+ }
34
+ function createApi(api, defaultConfig, custom) {
35
+ const fromDefine = api[FROM_DEFINE];
36
+ delete api[FROM_DEFINE];
37
+ const realDefaultConfig = defaultConfig || {};
38
+ if (!isString(api.url)) throwType('apiController.createApi', '入参应为 APIConfig 对象');
39
+ if (api.url.includes('/:')) {
40
+ if (!isTrue(fromDefine)) logger.warn('apiController.createApi', 'url 中存在 params 参数, 使用 defineApi 或 defineApiMap 定义 API 或 API map 来获取更好的类型提示');
41
+ if (!isTrue(custom)) throwType('apiController.createApi', 'url 中存在 params 参数, 不支持普通请求, 转为自定义请求');
42
+ }
43
+ let handler = null;
44
+ handler = isTrue(custom) ? (data, config)=>request({
45
+ ...realDefaultConfig,
46
+ ...api,
47
+ ...config,
48
+ url: api.url,
49
+ data,
50
+ oriUrl: api.url
51
+ }) : (data)=>request({
52
+ ...realDefaultConfig,
53
+ ...api,
54
+ data,
55
+ oriUrl: api.url
56
+ });
57
+ const instanceObj = createInstance(api, realDefaultConfig, defaultConfig);
58
+ if (!isAbsUrl(api.url)) instanceObj.$updateBaseUrl(realDefaultConfig.baseUrl);
59
+ return new Proxy(handler, {
60
+ get (target, prop, receiver) {
61
+ if (Reflect.getOwnPropertyDescriptor(instanceObj, prop)) return instanceMemberGetter(prop, instanceObj);
62
+ return Reflect.get(target, prop, receiver);
63
+ }
64
+ });
65
+ }
66
+ function defineApi(_api) {
67
+ return {
68
+ ..._api,
69
+ [FROM_DEFINE]: true
70
+ };
71
+ }
72
+ function defineApiMap(_apiMap) {
73
+ apiNamesCheck(_apiMap);
74
+ return {
75
+ ..._apiMap,
76
+ [FROM_DEFINE]: true
77
+ };
78
+ }
79
+ export { createApi, createApiWithMap, defineApi, defineApiMap };
@@ -0,0 +1,3 @@
1
+ export { createApi, createApiWithMap, defineApi, defineApiMap } from './create-api';
2
+ export { request } from './request';
3
+ export type { APIConfig, APIMap, RequestMethod } from './types';
@@ -0,0 +1,3 @@
1
+ import { createApi, createApiWithMap, defineApi, defineApiMap } from "./create-api.js";
2
+ import { request } from "./request.js";
3
+ export { createApi, createApiWithMap, defineApi, defineApiMap, request };
@@ -0,0 +1,7 @@
1
+ import type { RequestAPIConfig } from './types';
2
+ /**
3
+ * 请求方法
4
+ *
5
+ * @param config 请求配置
6
+ */
7
+ export declare function request<R, C extends RequestAPIConfig<any, R> = RequestAPIConfig<any, R>>(config: C): Promise<R>;
@@ -0,0 +1,66 @@
1
+ import { throwType } from "../throw-error/index.js";
2
+ import { tryCall } from "../try-call/index.js";
3
+ import { isFunction, isNullOrUndef } from "../utils/verify.js";
4
+ import { getBody, targetUrlParser, urlParamsParser } from "./utils.js";
5
+ async function baseRequest(config, getResponse) {
6
+ const { baseUrl, url, method: _method, parser, data, tdto, tvo, onResponse, ...rest } = config;
7
+ const targetUrl = targetUrlParser(url, baseUrl);
8
+ const method = _method?.toUpperCase();
9
+ const requestInfo = tryCall(()=>{
10
+ if (isNullOrUndef(method) || 'GET' === method || 'HEAD' === method) {
11
+ const queryKeys = Object.keys(data || {});
12
+ for(let i = 0; i < queryKeys.length; ++i)targetUrl.searchParams.append(queryKeys[i], data[queryKeys[i]]);
13
+ return new Request(targetUrl, {
14
+ ...rest,
15
+ method
16
+ });
17
+ }
18
+ const body = getBody(data, tdto);
19
+ return new Request(targetUrl, {
20
+ ...rest,
21
+ method,
22
+ body
23
+ });
24
+ });
25
+ const responseInfo = await getResponse(requestInfo);
26
+ const resResult = await tryCall(()=>{
27
+ if (onResponse) return onResponse(responseInfo, config);
28
+ if (!parser) return responseInfo.json();
29
+ if ('stream' === parser) return responseInfo.body;
30
+ const responseHandler = responseInfo[parser];
31
+ if (isFunction(responseHandler)) return Reflect.apply(responseHandler, responseInfo, []);
32
+ throwType('apiController.responseParser', 'Invalid parser');
33
+ });
34
+ return tvo ? tvo(resResult) : resResult;
35
+ }
36
+ async function mockRequest(config) {
37
+ const { onRequest, ...rest } = config;
38
+ return baseRequest(config, async (requestInfo)=>{
39
+ const reqResult = await (onRequest && onRequest(requestInfo, config));
40
+ const responseBody = getBody(reqResult);
41
+ return new Response(responseBody, {
42
+ ...rest
43
+ });
44
+ });
45
+ }
46
+ async function networkRequest(config) {
47
+ return baseRequest(config, fetch);
48
+ }
49
+ function request(config) {
50
+ const url = urlParamsParser(config.url, config.params);
51
+ const { requestMode, requestModeMap } = config;
52
+ const customRequest = (requestModeMap || {})[requestMode || ''];
53
+ if (customRequest) return customRequest({
54
+ ...config,
55
+ url
56
+ });
57
+ if ('mock' === requestMode) return mockRequest({
58
+ ...config,
59
+ url
60
+ });
61
+ return networkRequest({
62
+ ...config,
63
+ url
64
+ });
65
+ }
66
+ export { request };
@@ -0,0 +1,141 @@
1
+ import type { AnyFunc, Cast, Equal, Func } from '../types/base';
2
+ import type { Pack as TPack } from '../types/pack';
3
+ type Empty = {
4
+ __EMPTY__: never;
5
+ };
6
+ /** 请求方法 */
7
+ export type RequestMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'HEAD' | 'OPTIONS' | (string & {});
8
+ type Parser = 'json' | 'text' | 'blob' | 'arrayBuffer' | 'formData' | 'bytes' | 'stream' | (string & {});
9
+ type RequestMode<E extends string = string & {}> = 'mock' | 'network' | E;
10
+ type EmptyUnwrap<UserR = Empty, T = any> = Equal<UserR, Empty> extends true ? T : UserR;
11
+ interface ParserResultMap<UserR = Empty> {
12
+ json: EmptyUnwrap<UserR>;
13
+ text: string;
14
+ blob: Blob;
15
+ arrayBuffer: ArrayBuffer;
16
+ formData: FormData;
17
+ bytes: Uint8Array;
18
+ stream: ReadableStream | null;
19
+ }
20
+ type ParserResult<P extends Parser, UserR = Empty> = P extends keyof ParserResultMap ? ParserResultMap<UserR>[P] : EmptyUnwrap<UserR>;
21
+ type ParserReturn<RM extends RequestMode, P extends Parser, ReqOutput, UserR = Empty> = RM extends 'network' ? ParserResult<P, UserR> : EmptyUnwrap<UserR, RM extends 'mock' ? ReqOutput : any>;
22
+ type URLParamParser<U extends string, Param extends string = never> = U extends `${string}/:${infer P}/${infer Rest}` ? URLParamParser<`/${Rest}`, Param | P> : U extends `${string}/:${infer P}` ? P | Param : Param;
23
+ type CheckNonParamUrlAPIConfig<A extends APIConfig> = Equal<URLParamParser<A['url']>, never>;
24
+ export interface BaseAPIConfig<Input = any, Output = any, ReqOutput = any, ResOutput = any, DefaultConfig extends DefaultAPIConfig = DefaultAPIConfig, ReqModeMapKeys extends string = string & {}, Url extends string = string> extends RequestInit {
25
+ /**
26
+ * 请求地址
27
+ *
28
+ * @example '/api/user'
29
+ * @example 'https://example.com/api/user'
30
+ */
31
+ url: Url;
32
+ params?: Record<URLParamParser<Url>, string | number>;
33
+ /** 请求模式 */
34
+ requestMode?: RequestMode<ReqModeMapKeys>;
35
+ /** 请求方法 */
36
+ method?: RequestMethod | Lowercase<RequestMethod>;
37
+ /**
38
+ * 响应体解析方式
39
+ * 如果存在 onResponse 的话, 则会使用 onResponse 的返回值, 如果想要屏蔽 onResponse 的继承, 则应该设置 onResponse 为 null
40
+ *
41
+ * @default 'json'
42
+ */
43
+ parser?: Parser;
44
+ /**
45
+ * transform data transfer object
46
+ *
47
+ * @tips GET/HEAD 方法因为是无 body 的请求, 不会触发 tdto 的转换
48
+ *
49
+ * @description hook 顺序: tdto -> onRequest -> onResponse/parser -> tvo
50
+ */
51
+ tdto?: ((data: Input) => any) | null;
52
+ /**
53
+ * transform view object
54
+ *
55
+ * @description hook 顺序: tdto -> onRequest -> onResponse/parser -> tvo
56
+ */
57
+ tvo?: ((data: Awaited<FindNonAny<[ResOutput, ReturnType<NonNullable<DefaultConfig['onResponse']>>]>>) => Output) | null;
58
+ /**
59
+ * 请求前 hook
60
+ *
61
+ * @description hook 顺序: tdto -> onRequest -> onResponse/parser -> tvo
62
+ */
63
+ onRequest?: ((req: Request, config: RequestAPIConfig<Input, Output, ReqOutput, ResOutput, ReqModeMapKeys>) => ReqOutput) | null;
64
+ /**
65
+ * 响应前 hook
66
+ * 会覆盖 parser 的解析方式
67
+ *
68
+ * @description hook 顺序: tdto -> onRequest -> onResponse/parser -> tvo
69
+ */
70
+ onResponse?: ((res: Response, config: RequestAPIConfig<Input, Output, ReqOutput, ResOutput, ReqModeMapKeys>) => ResOutput) | null;
71
+ }
72
+ export interface DefaultAPIConfig<Input = any, Output = any, ReqOutput = any, ResOutput = any, ReqModeMapKeys extends string = string & {}> extends Omit<BaseAPIConfig<Input, Output, ReqOutput, ResOutput, DefaultAPIConfig, ReqModeMapKeys>, 'url'> {
73
+ /** 基本地址 */
74
+ baseUrl?: string;
75
+ /** 请求模式 map */
76
+ requestModeMap?: Record<ReqModeMapKeys, (config: RequestAPIConfig<Input, Output, ReqOutput, ResOutput, ReqModeMapKeys>) => any>;
77
+ }
78
+ export interface RequestAPIConfig<Input = any, Output = any, ReqOutput = any, ResOutput = any, ReqModeMapKeys extends string = string & {}> extends DefaultAPIConfig<Input, Output, ReqOutput, ResOutput, ReqModeMapKeys>, APIConfig<Input, Output, ReqOutput, ResOutput, DefaultAPIConfig, ReqModeMapKeys> {
79
+ /** 请求数据 */
80
+ data?: Input;
81
+ oriUrl: string;
82
+ }
83
+ /**
84
+ * API config
85
+ */
86
+ export type APIConfig<Input = any, Output = any, ReqOutput = any, ResOutput = any, DefaultConfig extends DefaultAPIConfig = DefaultAPIConfig, ReqModeMapKeys extends string = string & {}, Url extends string = string> = BaseAPIConfig<Input, Output, ReqOutput, ResOutput, DefaultConfig, ReqModeMapKeys, Url>;
87
+ export type CallAPIConfig<Input = any, Output = any, ReqOutput = any, ResOutput = any, DefaultConfig extends DefaultAPIConfig = DefaultAPIConfig, ReqModeMapKeys extends string = string & {}, Url extends string = string> = Omit<APIConfig<Input, Output, ReqOutput, ResOutput, DefaultConfig, ReqModeMapKeys, Url>, 'url'>;
88
+ export type DefineAPIConfig<U extends string> = APIConfig<any, any, any, any, any, any, U>;
89
+ /** API map */
90
+ export interface APIMap<U extends string = string & {}> {
91
+ [key: string]: DefineAPIConfig<U> | APIMap<U>;
92
+ }
93
+ export type IsUnknownAny<T> = Equal<T, any> extends true ? true : Equal<T, unknown> extends true ? true : false;
94
+ export type FindNonAny<T extends any[], Other = Empty> = T extends [infer F, ...infer Last] ? IsUnknownAny<F> extends true ? FindNonAny<Last, Other> : F extends Other ? FindNonAny<Last, Other> : F : any;
95
+ type APIHandlerArgs<I, C extends CallAPIConfig, Custom, NonParamUrl extends boolean> = FindNonAny<[
96
+ NonParamUrl extends true ? any : [I, C & Required<Pick<C, 'params'>>],
97
+ Custom extends true ? [I?, C?] : [I?],
98
+ [I?]
99
+ ]>;
100
+ type DefineRequestModes<D extends DefaultAPIConfig> = keyof NonNullable<D['requestModeMap']> & string;
101
+ type RealProp<P extends keyof DefaultAPIConfig & keyof APIConfig, C extends Pick<APIConfig, P>, A extends Pick<APIConfig, P>, D extends DefaultAPIConfig, Other = Empty> = FindNonAny<[C[P], A[P], D[P]], Other>;
102
+ type OnResponseReturn<OnResponse> = OnResponse extends AnyFunc ? ReturnType<OnResponse> : any;
103
+ type CustomCallConfigUnwrap<C extends CallAPIConfig, Custom extends boolean> = Custom extends true ? {
104
+ [K in keyof C as undefined extends C[K] ? never : K]: C[K];
105
+ } : Record<never, Empty>;
106
+ interface Pack<T> extends TPack<T> {
107
+ __value: T;
108
+ }
109
+ type PackUnwrap<P> = P extends Pack<any> ? P['__value'] : P;
110
+ interface OriginalPack<T> extends TPack<T> {
111
+ __originValue: T;
112
+ }
113
+ type OriginalPackUnwrap<P> = P extends OriginalPack<any> ? P['__originValue'] : Promise<P>;
114
+ type CustomRequestModeReturn<RealRM extends RequestMode, InputD extends DefaultAPIConfig, CustomReq = NonNullable<InputD['requestModeMap']>[RealRM]> = IsUnknownAny<RealRM> extends true ? any : Equal<RealRM, string> extends true ? any : CustomReq extends AnyFunc ? OriginalPack<ReturnType<CustomReq>> : any;
115
+ type UserInputResult<UserR, CustomRequestResult> = IsUnknownAny<CustomRequestResult> extends true ? UserR : IsUnknownAny<UserR> extends true ? any : UserR extends CustomRequestResult ? OriginalPack<UserR> : UserR;
116
+ type APIHandlerResult<AConfig extends APIConfig, CallConfig extends CallAPIConfig = APIConfig, UserR = Empty, InputDefault extends DefaultAPIConfig = DefaultAPIConfig, ReqOutput = any, Custom extends boolean = false, CC extends CallAPIConfig = CustomCallConfigUnwrap<CallConfig, Custom>, CustomRequestResult = CustomRequestModeReturn<NonNullable<RealProp<'requestMode', CC, AConfig, InputDefault>>, InputDefault>> = OriginalPackUnwrap<Awaited<PackUnwrap<FindNonAny<[
117
+ UserInputResult<EmptyUnwrap<UserR>, OriginalPackUnwrap<CustomRequestResult>>,
118
+ CustomRequestResult,
119
+ ReturnType<Cast<RealProp<'tvo', CC, AConfig, InputDefault>, AnyFunc>>,
120
+ OnResponseReturn<RealProp<'onResponse', CC, AConfig, InputDefault>>,
121
+ ParserReturn<NonNullable<RealProp<'requestMode', CC, AConfig, InputDefault>>, NonNullable<RealProp<'parser', CC, AConfig, InputDefault>>, ReqOutput, UserR>
122
+ ], undefined | null>>>>;
123
+ type APIInputType<A extends Pick<APIConfig, 'tdto'> = APIConfig, D extends Pick<DefaultAPIConfig, 'tdto'> = DefaultAPIConfig> = Parameters<Cast<FindNonAny<[A['tdto'], D['tdto']], undefined | null>, (...args: any[]) => any>>[0];
124
+ type PropResult<A extends APIConfig, P extends keyof A> = A[P] extends AnyFunc ? ReturnType<A[P]> : unknown;
125
+ export type APITransformMethod<A extends APIConfig, InputD extends DefaultAPIConfig = DefaultAPIConfig, Custom extends boolean = false, NonParamUrl extends boolean = CheckNonParamUrlAPIConfig<A>, I extends APIInputType<A, InputD> = APIInputType<A, InputD>> = (<R = Empty, C extends CallAPIConfig<I, any, PropResult<A, 'onRequest'>, PropResult<A, 'onResponse'>, InputD, DefineRequestModes<InputD>, A['url']> = CallAPIConfig<I, any, PropResult<A, 'onRequest'>, PropResult<A, 'onResponse'>, InputD, DefineRequestModes<InputD>, A['url']>>(...args: APIHandlerArgs<Equal<I, APIInputType<A, InputD>> extends true ? APIInputType<C, {
126
+ tdto: Func<[I]>;
127
+ }> : I, C, Custom, NonParamUrl>) => APIHandlerResult<A, Cast<C, Partial<APIConfig>>, R, InputD, ReturnType<Cast<RealProp<'onRequest', C, A, InputD>, AnyFunc>>, Custom>) & APIInstance<A, InputD>;
128
+ export type APIInstance<A, D> = {
129
+ $: A;
130
+ $$: DefaultAPIConfig extends D ? undefined : D;
131
+ $$r: DefaultAPIConfig;
132
+ } & APIInstanceHandler;
133
+ interface APIInstanceHandler {
134
+ $updateBaseUrl(baseUrl?: string): void;
135
+ }
136
+ export type APIMapTransformMethods<M extends APIMap | Record<string, APIConfig>, D extends DefaultAPIConfig = DefaultAPIConfig> = {
137
+ [K in keyof M as M[K] extends APIConfig ? CheckNonParamUrlAPIConfig<M[K]> extends true ? K : never : K]: M[K] extends APIConfig ? APITransformMethod<M[K], D, false> : APIMapTransformMethods<Cast<M[K], APIMap>, D>;
138
+ } & {
139
+ [K in keyof M as M[K] extends APIConfig ? `${K & string}Custom` : never]: APITransformMethod<Cast<M[K], APIConfig>, D, true>;
140
+ } & APIInstance<M, D>;
141
+ export {};
File without changes
@@ -0,0 +1,22 @@
1
+ import type { APIConfig, APIInstance, APIMap, DefaultAPIConfig } from './types';
2
+ export declare function isAbsUrl(url?: string): boolean;
3
+ export declare function targetUrlParser(_url: string, _baseUrl: string): URL;
4
+ export declare function urlParamsParser(url: string, params: Record<string, string> | undefined): string;
5
+ export declare function getBody(data: any, tdto?: APIConfig['tdto']): any;
6
+ export declare function instanceMemberGetter(prop: string, instanceObj: Record<string, any>): any;
7
+ export declare function createInstance(apiMap: APIConfig | APIMap, realDefaultConfig: DefaultAPIConfig, defaultConfig?: DefaultAPIConfig): {
8
+ $: APIMap<string & {}> | APIConfig;
9
+ $$: any;
10
+ $$r: DefaultAPIConfig<any, any, any, any, string & {}>;
11
+ $updateBaseUrl(baseUrl: string | undefined): void;
12
+ };
13
+ export declare function getInstanceMemberOrApi(target: APIMap, prop: string, receiver: any, instanceObj: APIInstance<any, any>): {
14
+ instanceMember: any;
15
+ api?: undefined;
16
+ isCustom?: undefined;
17
+ } | {
18
+ api: APIMap<string & {}> | import("./types").DefineAPIConfig<string & {}>;
19
+ isCustom: boolean;
20
+ instanceMember?: undefined;
21
+ } | undefined;
22
+ export declare function apiNamesCheck(_apiMap: APIMap, isDeep?: boolean): string[];
@@ -0,0 +1,96 @@
1
+ import { logger } from "../logger/index.js";
2
+ import { throwError, throwType } from "../throw-error/index.js";
3
+ import { getType, isPlainNumber, isString } from "../utils/index.js";
4
+ const ABSOLUTE_URL_REG = /^[a-z][a-z\d+\-.]*:/im;
5
+ function isAbsUrl(url) {
6
+ if (!url) return false;
7
+ return ABSOLUTE_URL_REG.test(url);
8
+ }
9
+ function targetUrlParser(_url, _baseUrl) {
10
+ if (isAbsUrl(_url)) return new URL(_url);
11
+ if (!isAbsUrl(_baseUrl)) throwType('apiController.request', 'baseUrl 配置不合法, 必须是绝对路径');
12
+ const baseUrl = new URL(_baseUrl);
13
+ const basePath = '/' === baseUrl.pathname ? '' : baseUrl.pathname.replace(/\/$/, '');
14
+ const relativePath = _url.startsWith('/') ? _url : `/${_url}`;
15
+ const url = `${basePath}${relativePath}`;
16
+ return new URL(url, baseUrl);
17
+ }
18
+ function urlParamsParser(url, params) {
19
+ if (!url.includes('/:')) return url;
20
+ if (!params) throwType('apiController.parseParams', 'url 中存在 params 参数, params 配置不能为空, 请使用 custom 方法调用并传递 params 配置');
21
+ const urlSplit = url.split('/');
22
+ const emptyKeys = [];
23
+ for(let i = 1; i < urlSplit.length; ++i){
24
+ if (':' !== urlSplit[i][0]) continue;
25
+ const param = urlSplit[i].slice(1);
26
+ const originValue = params[param];
27
+ if (!(isPlainNumber(originValue) || originValue)) {
28
+ emptyKeys.push(param);
29
+ continue;
30
+ }
31
+ const paramValue = encodeURIComponent(String(originValue));
32
+ urlSplit[i] = paramValue;
33
+ }
34
+ if (emptyKeys.length) throwType('apiController.parseParams', `params 配置中缺少 [${emptyKeys.join(', ')}] 参数`);
35
+ return urlSplit.join('/');
36
+ }
37
+ function getBody(data, tdto) {
38
+ const _body = tdto ? tdto(data) : data;
39
+ const bodyType = getType(_body);
40
+ switch(bodyType){
41
+ case 'object':
42
+ case 'array':
43
+ case 'number':
44
+ case 'boolean':
45
+ case 'function':
46
+ return JSON.stringify(_body);
47
+ default:
48
+ return _body;
49
+ }
50
+ }
51
+ function instanceMemberGetter(prop, instanceObj) {
52
+ return instanceObj[prop];
53
+ }
54
+ function createInstance(apiMap, realDefaultConfig, defaultConfig) {
55
+ return {
56
+ $: apiMap,
57
+ $$: defaultConfig,
58
+ $$r: realDefaultConfig,
59
+ $updateBaseUrl (baseUrl) {
60
+ if (isAbsUrl(baseUrl)) realDefaultConfig.baseUrl = baseUrl;
61
+ else {
62
+ const { origin } = globalThis.location || {};
63
+ if (!origin) throwError('apiController.$updateBaseUrl', 'location.origin is undefined');
64
+ const normalizedPath = (baseUrl || '/').startsWith('/') ? baseUrl || '' : `/${baseUrl}`;
65
+ realDefaultConfig.baseUrl = `${origin}${normalizedPath}`;
66
+ }
67
+ }
68
+ };
69
+ }
70
+ function getInstanceMemberOrApi(target, prop, receiver, instanceObj) {
71
+ if (Reflect.getOwnPropertyDescriptor(instanceObj, prop)) return {
72
+ instanceMember: instanceMemberGetter(prop, instanceObj)
73
+ };
74
+ const hasExactProp = isString(prop) && Reflect.has(target, prop);
75
+ const isCustom = isString(prop) && prop.endsWith('Custom') && !hasExactProp;
76
+ const name = isCustom ? prop.slice(0, -6) : prop;
77
+ if (!Reflect.getOwnPropertyDescriptor(target, name)) return;
78
+ const api = Reflect.get(target, name, receiver);
79
+ if (isCustom && !isString(api.url)) return;
80
+ return {
81
+ api,
82
+ isCustom
83
+ };
84
+ }
85
+ function apiNamesCheck(_apiMap, isDeep = false) {
86
+ const apiNames = Reflect.ownKeys(_apiMap);
87
+ const warnNames = [];
88
+ for(let i = 0; i < apiNames.length; i++){
89
+ const name = apiNames[i];
90
+ if (name.endsWith('Custom')) warnNames.push(name);
91
+ if (!isString(_apiMap[name].url)) warnNames.push(...apiNamesCheck(_apiMap[name], true));
92
+ }
93
+ if (!isDeep && warnNames.length > 0) logger.warn('apiController.createApiWithMap', 'api 命名不应该使用 Custom 结尾, 因为这是一个内部实现的方法', warnNames);
94
+ return warnNames;
95
+ }
96
+ export { apiNamesCheck, createInstance, getBody, getInstanceMemberOrApi, instanceMemberGetter, isAbsUrl, targetUrlParser, urlParamsParser };
@@ -1,10 +1,12 @@
1
1
  export * from './allx';
2
2
  export * from './animation';
3
+ export * from './api-controller';
3
4
  export * from './condition-merge';
4
5
  export * from './create-storage-handler';
5
6
  export * from './data-handler';
6
7
  export * from './data-mixed-manager';
7
8
  export * from './throw-error';
9
+ export * from './try-call';
8
10
  export * from './types';
9
11
  export * from './utils';
10
12
  export * from './with-resolvers';