@brickflow/http 0.0.15 → 0.0.17
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/README.md +249 -284
- package/dist/create-get.d.ts +12 -0
- package/dist/create-get.d.ts.map +1 -0
- package/dist/http.d.ts +26 -41
- package/dist/http.d.ts.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +48 -21
- package/dist/index.mjs.map +1 -1
- package/dist/nuxt-BsZgB52H.js +233 -0
- package/dist/nuxt-BsZgB52H.js.map +1 -0
- package/dist/nuxt.d.ts +25 -42
- package/dist/nuxt.d.ts.map +1 -1
- package/dist/nuxt.mjs +2 -222
- package/package.json +10 -9
- package/src/app.d.ts +7 -0
- package/src/create-get.ts +43 -0
- package/src/http.ts +286 -0
- package/src/index.ts +3 -0
- package/src/nuxt.ts +354 -0
- package/src/utils.ts +50 -0
- package/dist/nuxt.mjs.map +0 -1
- package/dist/utils-DMSINHi5.js +0 -39
- package/dist/utils-DMSINHi5.js.map +0 -1
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { HttpClient, HttpKey, HttpParam } from './http';
|
|
2
|
+
import type { UseHttpFn, UseHttpOptions, UseHttpResult } from './nuxt';
|
|
3
|
+
export type CreateGetOptions<T extends HttpKey, P extends HttpParam> = Omit<UseHttpOptions<T, P>, 'url'>;
|
|
4
|
+
export type CreateGetPayload<T extends HttpKey, P extends HttpParam> = Pick<UseHttpOptions<T, P>, 'effect' | 'isError' | 'mapParams'>;
|
|
5
|
+
export type CreateGetResult<T extends HttpKey, P extends HttpParam> = UseHttpResult<T, P>;
|
|
6
|
+
export declare function createUseCase<D>(): <T>(callback: (payload: D & {
|
|
7
|
+
httpClient: HttpClient;
|
|
8
|
+
}) => T) => ((payload: D & {
|
|
9
|
+
httpClient: HttpClient;
|
|
10
|
+
}) => T);
|
|
11
|
+
export declare function defineGet(useHttp: UseHttpFn): <P extends HttpParam>() => <T extends HttpKey>(url: T, payload?: CreateGetPayload<T, P>) => (options?: CreateGetOptions<T, P>) => Promise<CreateGetResult<T, P>>;
|
|
12
|
+
//# sourceMappingURL=create-get.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create-get.d.ts","sourceRoot":"","sources":["../src/create-get.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAA;AAC5D,OAAO,KAAK,EAAE,SAAS,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,QAAQ,CAAA;AAEtE,MAAM,MAAM,gBAAgB,CAAC,CAAC,SAAS,OAAO,EAAE,CAAC,SAAS,SAAS,IAAI,IAAI,CAAC,cAAc,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,KAAK,CAAC,CAAA;AAExG,MAAM,MAAM,gBAAgB,CAAC,CAAC,SAAS,OAAO,EAAE,CAAC,SAAS,SAAS,IAAI,IAAI,CACzE,cAAc,CAAC,CAAC,EAAE,CAAC,CAAC,EACpB,QAAQ,GAAG,SAAS,GAAG,WAAW,CACnC,CAAA;AAED,MAAM,MAAM,eAAe,CAAC,CAAC,SAAS,OAAO,EAAE,CAAC,SAAS,SAAS,IAAI,aAAa,CAAC,CAAC,EAAE,CAAC,CAAC,CAAA;AAEzF,wBAAgB,aAAa,CAAC,CAAC,MACrB,CAAC,EACP,UAAU,CAAC,OAAO,EAAE,CAAC,GAAG;IAAE,UAAU,EAAE,UAAU,CAAA;CAAE,KAAK,CAAC,KACvD,CAAC,CAAC,OAAO,EAAE,CAAC,GAAG;IAAE,UAAU,EAAE,UAAU,CAAA;CAAE,KAAK,CAAC,CAAC,CACpD;AAED,wBAAgB,SAAS,CAAC,OAAO,EAAE,SAAS,IACf,CAAC,SAAS,SAAS,QACpB,CAAC,SAAS,OAAO,EACvC,KAAK,CAAC,EACN,UAAU,gBAAgB,CAAC,CAAC,EAAE,CAAC,CAAC,KAC/B,CAAC,OAAO,CAAC,EAAE,gBAAgB,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,OAAO,CAAC,eAAe,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAmB1E"}
|
package/dist/http.d.ts
CHANGED
|
@@ -1,61 +1,46 @@
|
|
|
1
|
-
export type ConventionError = HttpError;
|
|
2
1
|
export interface CreateHttpOptions {
|
|
2
|
+
arrayMode?: HttpArrayMode;
|
|
3
3
|
baseURL: string;
|
|
4
4
|
fetch?: typeof fetch;
|
|
5
|
-
headers?: (() =>
|
|
6
|
-
|
|
7
|
-
requestInit?: Omit<RequestInit, 'body' | 'headers' | 'method' | 'signal'>;
|
|
5
|
+
headers?: (() => HttpHeaders) | HttpHeaders;
|
|
6
|
+
onResponse?: (response: HttpAnyResponse) => Promise<void> | void;
|
|
8
7
|
timeout?: number;
|
|
9
8
|
}
|
|
10
|
-
export
|
|
9
|
+
export type GetConfig<TKey extends HttpKey = HttpKey> = HttpConfigFull<TKey> & {
|
|
11
10
|
retry?: {
|
|
12
11
|
delay?: number;
|
|
13
12
|
retries?: number;
|
|
14
13
|
};
|
|
15
|
-
}
|
|
16
|
-
export
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
}
|
|
26
|
-
export
|
|
27
|
-
[key: string]: unknown;
|
|
28
|
-
status?: 'success';
|
|
29
|
-
}
|
|
30
|
-
export type HttpError = HttpConfigValue<'error', HttpDefaultError>;
|
|
14
|
+
};
|
|
15
|
+
export type HttpAnyResponse = HttpResponse<HttpResponseData<HttpKey>, HttpKey, HttpConfigFull<HttpKey>>;
|
|
16
|
+
export type HttpArrayMode = 'json' | 'repeat';
|
|
17
|
+
export type HttpClient = {
|
|
18
|
+
get: <T extends HttpKey, TConfig extends GetConfig<T> = GetConfig<T>>(url: T, config?: TConfig) => Promise<HttpResponse<HttpResponseData<T>, T, TConfig>>;
|
|
19
|
+
post: <T extends HttpKey, TConfig extends PostConfig<T> = PostConfig<T>>(url: T, data?: FormData | Record<string, unknown>, config?: TConfig) => Promise<HttpResponse<HttpResponseData<T>, T, TConfig>>;
|
|
20
|
+
};
|
|
21
|
+
export interface HttpConfig<TKey extends HttpKey = HttpKey> {
|
|
22
|
+
}
|
|
23
|
+
export interface HttpEndpoint {
|
|
24
|
+
}
|
|
25
|
+
export type HttpErrorGuard<TData = unknown, TError extends TData = TData> = (payload: TData) => payload is TError;
|
|
31
26
|
export type HttpKey = Extract<keyof HttpSchema, string>;
|
|
32
27
|
export type HttpParam = Record<string, boolean | number | string | string[] | undefined>;
|
|
33
|
-
export
|
|
34
|
-
|
|
35
|
-
export type HttpErrorData<TData = unknown> = HttpMatchedError<TData>;
|
|
36
|
-
export type HttpSuccessResult<TData = unknown, TError = HttpErrorData<TData>> = HttpMatchedSuccess<TData, TError>;
|
|
37
|
-
export type HttpErrorGuard<TData = unknown, TError extends TData = HttpMatchedError<TData>> = (payload: TData) => payload is TError;
|
|
38
|
-
export interface HttpResponse<T = HttpResponseData> {
|
|
39
|
-
config: {
|
|
40
|
-
ignore?: ((response: HttpResponse<HttpError>) => boolean) | undefined;
|
|
41
|
-
url: string;
|
|
42
|
-
};
|
|
28
|
+
export interface HttpResponse<T = unknown, TKey extends HttpKey = HttpKey, TConfig extends HttpConfigFull<TKey> = HttpConfigFull<TKey>> {
|
|
29
|
+
config: HttpResponseConfig<TKey, TConfig>;
|
|
43
30
|
data: T;
|
|
44
31
|
status: number;
|
|
45
32
|
}
|
|
33
|
+
export type HttpResponseConfig<TKey extends HttpKey = HttpKey, TConfig extends HttpConfig<TKey> = HttpConfig<TKey>> = Omit<TConfig, 'signal'> & {
|
|
34
|
+
url: string;
|
|
35
|
+
};
|
|
46
36
|
export type HttpResponseData<TKey extends HttpKey = HttpKey> = HttpSchema[TKey];
|
|
47
|
-
export type
|
|
48
|
-
|
|
49
|
-
export interface HttpTypeConfig {
|
|
50
|
-
}
|
|
51
|
-
export interface PostConfig extends HttpConfig {
|
|
52
|
-
}
|
|
53
|
-
interface HttpConfig {
|
|
54
|
-
ignore?: (response: HttpResponse<HttpError>) => boolean;
|
|
37
|
+
export type PostConfig<TKey extends HttpKey = HttpKey> = HttpConfigFull<TKey>;
|
|
38
|
+
type HttpConfigFull<TKey extends HttpKey = HttpKey> = HttpConfig<TKey> & {
|
|
55
39
|
params?: HttpParam;
|
|
56
40
|
signal?: AbortSignal | AbortSignal[];
|
|
57
|
-
}
|
|
58
|
-
type
|
|
41
|
+
};
|
|
42
|
+
type HttpHeaders = Record<string, string | undefined>;
|
|
43
|
+
type HttpSchema = keyof HttpEndpoint extends never ? Record<string, unknown> : HttpEndpoint;
|
|
59
44
|
export declare function createHttp(options: CreateHttpOptions): HttpClient;
|
|
60
45
|
export {};
|
|
61
46
|
//# sourceMappingURL=http.d.ts.map
|
package/dist/http.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"AAAA,MAAM,
|
|
1
|
+
{"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,QAAQ,CAAA;AACtB,cAAc,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAA;AAC5B,cAAc,QAAQ,CAAA;AACtB,cAAc,QAAQ,CAAA"}
|
package/dist/index.mjs
CHANGED
|
@@ -1,27 +1,49 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { t as createUseHttp } from "./nuxt-BsZgB52H.js";
|
|
2
|
+
//#region src/create-get.ts
|
|
3
|
+
function createUseCase() {
|
|
4
|
+
return (callback) => callback;
|
|
5
|
+
}
|
|
6
|
+
function defineGet(useHttp) {
|
|
7
|
+
return function withParams() {
|
|
8
|
+
return function withUrl(url, payload) {
|
|
9
|
+
return async function request(options = {}) {
|
|
10
|
+
return await useHttp({
|
|
11
|
+
...options,
|
|
12
|
+
effect: (...args) => {
|
|
13
|
+
const [data, config] = args;
|
|
14
|
+
payload?.effect?.(data, config);
|
|
15
|
+
options?.effect?.(data, config);
|
|
16
|
+
},
|
|
17
|
+
isError: options.isError ?? payload?.isError,
|
|
18
|
+
mapParams: options.mapParams ?? payload?.mapParams,
|
|
19
|
+
url
|
|
20
|
+
});
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
//#endregion
|
|
2
26
|
//#region src/http.ts
|
|
3
27
|
function createHttp(options) {
|
|
28
|
+
const arrayMode = options.arrayMode ?? "json";
|
|
4
29
|
const clientFetch = options.fetch ?? fetch;
|
|
5
30
|
const timeout = options.timeout ?? 8e4;
|
|
6
31
|
return {
|
|
7
|
-
get(url, config
|
|
8
|
-
const
|
|
9
|
-
const
|
|
32
|
+
get(url, config) {
|
|
33
|
+
const requestConfig = config ?? {};
|
|
34
|
+
const requestUrl = `${joinUrl(options.baseURL, url)}${toQueryString(requestConfig.params, arrayMode)}`;
|
|
35
|
+
const { delay = 300, retries = 3 } = requestConfig.retry ?? {};
|
|
10
36
|
const attemptRequest = async (attempt) => {
|
|
11
37
|
try {
|
|
12
38
|
const result = await clientFetch(requestUrl, {
|
|
13
|
-
...options.requestInit,
|
|
14
39
|
credentials: "include",
|
|
15
40
|
headers: resolveHeaders(options.headers),
|
|
16
41
|
method: "GET",
|
|
17
|
-
signal: createSignal(
|
|
42
|
+
signal: createSignal(requestConfig.signal, timeout)
|
|
18
43
|
});
|
|
19
44
|
const parsedResult = await result.json();
|
|
20
45
|
const response = {
|
|
21
|
-
config:
|
|
22
|
-
ignore: config.ignore,
|
|
23
|
-
url: joinUrl(options.baseURL, url)
|
|
24
|
-
},
|
|
46
|
+
config: createResponseConfig(joinUrl(options.baseURL, url), requestConfig),
|
|
25
47
|
data: parsedResult,
|
|
26
48
|
status: result.status
|
|
27
49
|
};
|
|
@@ -29,7 +51,7 @@ function createHttp(options) {
|
|
|
29
51
|
await wait(getRetryDelay(attempt, delay));
|
|
30
52
|
return attemptRequest(attempt + 1);
|
|
31
53
|
}
|
|
32
|
-
await options.
|
|
54
|
+
await options.onResponse?.(response);
|
|
33
55
|
return response;
|
|
34
56
|
} catch (err) {
|
|
35
57
|
if (isAbortError(err) || attempt >= retries) throw err;
|
|
@@ -39,15 +61,15 @@ function createHttp(options) {
|
|
|
39
61
|
};
|
|
40
62
|
return attemptRequest(0);
|
|
41
63
|
},
|
|
42
|
-
async post(url, data, config
|
|
64
|
+
async post(url, data, config) {
|
|
65
|
+
const requestConfig = config ?? {};
|
|
43
66
|
const isForm = isFormData(data);
|
|
44
|
-
const response = await clientFetch(`${joinUrl(options.baseURL, url)}${toQueryString(
|
|
45
|
-
...options.requestInit,
|
|
67
|
+
const response = await clientFetch(`${joinUrl(options.baseURL, url)}${toQueryString(requestConfig.params, arrayMode)}`, {
|
|
46
68
|
body: isForm || data === void 0 ? data : JSON.stringify(data),
|
|
47
69
|
credentials: "include",
|
|
48
70
|
headers: resolveHeaders(options.headers, data !== void 0 && !isForm ? { "Content-Type": "application/json" } : void 0),
|
|
49
71
|
method: "POST",
|
|
50
|
-
signal: createSignal(
|
|
72
|
+
signal: createSignal(requestConfig.signal, timeout)
|
|
51
73
|
});
|
|
52
74
|
let parsedResult = {};
|
|
53
75
|
try {
|
|
@@ -56,14 +78,11 @@ function createHttp(options) {
|
|
|
56
78
|
parsedResult = {};
|
|
57
79
|
}
|
|
58
80
|
const parsedResponse = {
|
|
59
|
-
config:
|
|
60
|
-
ignore: config.ignore,
|
|
61
|
-
url: joinUrl(options.baseURL, url)
|
|
62
|
-
},
|
|
81
|
+
config: createResponseConfig(joinUrl(options.baseURL, url), requestConfig),
|
|
63
82
|
data: parsedResult,
|
|
64
83
|
status: response.status
|
|
65
84
|
};
|
|
66
|
-
await options.
|
|
85
|
+
await options.onResponse?.(parsedResponse);
|
|
67
86
|
return parsedResponse;
|
|
68
87
|
}
|
|
69
88
|
};
|
|
@@ -76,6 +95,14 @@ function createAnySignal(signals) {
|
|
|
76
95
|
});
|
|
77
96
|
return controller.signal;
|
|
78
97
|
}
|
|
98
|
+
function createResponseConfig(url, config) {
|
|
99
|
+
const responseConfig = { ...config };
|
|
100
|
+
delete responseConfig.signal;
|
|
101
|
+
return {
|
|
102
|
+
...responseConfig,
|
|
103
|
+
url
|
|
104
|
+
};
|
|
105
|
+
}
|
|
79
106
|
function createSignal(signal, timeout) {
|
|
80
107
|
const timeoutSignal = createTimeoutSignal(timeout);
|
|
81
108
|
if (Array.isArray(signal)) return createAnySignal([...signal, timeoutSignal]);
|
|
@@ -139,6 +166,6 @@ function wait(ms) {
|
|
|
139
166
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
140
167
|
}
|
|
141
168
|
//#endregion
|
|
142
|
-
export { createHttp,
|
|
169
|
+
export { createHttp, createUseCase, createUseHttp, defineGet };
|
|
143
170
|
|
|
144
171
|
//# sourceMappingURL=index.mjs.map
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../src/http.ts"],"sourcesContent":["export type ConventionError = HttpError\n\nexport interface CreateHttpOptions {\n baseURL: string\n fetch?: typeof fetch\n headers?: (() => Record<string, string | undefined>) | Record<string, string | undefined>\n onResponseError?: (response: HttpResponse<HttpError>) => Promise<void> | void\n requestInit?: Omit<RequestInit, 'body' | 'headers' | 'method' | 'signal'>\n timeout?: number\n}\n\nexport interface GetConfig extends HttpConfig {\n retry?: {\n delay?: number\n retries?: number\n }\n}\n\nexport interface HttpClient {\n get: <T extends HttpKey>(url: T, config?: GetConfig) => Promise<HttpResponse<HttpResponseData<T>>>\n post: <T extends HttpKey>(\n url: T,\n data?: FormData | Record<string, unknown>,\n config?: PostConfig,\n ) => Promise<HttpResponse<HttpResponseData<T>>>\n}\n\nexport interface HttpDefaultError {\n [key: string]: unknown\n kind?: string\n message?: string\n status: 'error'\n}\nexport interface HttpDefaultSuccess {\n [key: string]: unknown\n status?: 'success'\n}\nexport type HttpError = HttpConfigValue<'error', HttpDefaultError>\nexport type HttpKey = Extract<keyof HttpSchema, string>\nexport type HttpParam = Record<string, boolean | number | string | string[] | undefined>\nexport type HttpMatchedError<TData = unknown, TError = HttpError> = Extract<TData, TError>\nexport type HttpMatchedSuccess<TData = unknown, TError = HttpError> = Exclude<TData, HttpMatchedError<TData, TError>>\nexport type HttpErrorData<TData = unknown> = HttpMatchedError<TData>\nexport type HttpSuccessResult<TData = unknown, TError = HttpErrorData<TData>> = HttpMatchedSuccess<TData, TError>\nexport type HttpErrorGuard<TData = unknown, TError extends TData = HttpMatchedError<TData>> = (\n payload: TData,\n) => payload is TError\nexport interface HttpResponse<T = HttpResponseData> {\n config: {\n ignore?: ((response: HttpResponse<HttpError>) => boolean) | undefined\n url: string\n }\n data: T\n status: number\n}\n\nexport type HttpResponseData<TKey extends HttpKey = HttpKey> = HttpSchema[TKey]\n\nexport type HttpSchema = HttpConfigValue<'endpoints', Record<string, HttpDefaultError | HttpDefaultSuccess>>\n\nexport type HttpSuccessData<TKey extends HttpKey = HttpKey> = Exclude<HttpResponseData<TKey>, HttpError>\n\nexport interface HttpTypeConfig {}\n\nexport interface PostConfig extends HttpConfig {}\n\ninterface HttpConfig {\n ignore?: (response: HttpResponse<HttpError>) => boolean\n params?: HttpParam\n signal?: AbortSignal | AbortSignal[]\n}\n\ntype HttpConfigValue<TKey extends string, TFallback> = TKey extends keyof HttpTypeConfig\n ? HttpTypeConfig[TKey]\n : TFallback\n\ntype QueryArrayMode = 'json' | 'repeat'\n\nexport function createHttp(options: CreateHttpOptions): HttpClient {\n const clientFetch = options.fetch ?? fetch\n const timeout = options.timeout ?? 80000\n\n return {\n get<T extends HttpKey>(url: T, config: GetConfig = {}) {\n const requestUrl = `${joinUrl(options.baseURL, url)}${toQueryString(config.params, 'json')}`\n const { delay = 300, retries = 3 } = config.retry ?? {}\n\n const attemptRequest = async (attempt: number): Promise<HttpResponse<HttpResponseData<T>>> => {\n try {\n const result = await clientFetch(requestUrl, {\n ...options.requestInit,\n credentials: 'include',\n headers: resolveHeaders(options.headers),\n method: 'GET',\n signal: createSignal(config.signal, timeout),\n })\n\n const parsedResult = await result.json()\n const response = {\n config: {\n ignore: config.ignore,\n url: joinUrl(options.baseURL, url),\n },\n data: parsedResult,\n status: result.status,\n }\n\n if (!result.ok && isRetryableStatus(result.status) && attempt < retries) {\n await wait(getRetryDelay(attempt, delay))\n return attemptRequest(attempt + 1)\n }\n\n await options.onResponseError?.(response)\n return response\n } catch (err) {\n if (isAbortError(err) || attempt >= retries) {\n throw err\n }\n\n await wait(getRetryDelay(attempt, delay))\n return attemptRequest(attempt + 1)\n }\n }\n\n return attemptRequest(0)\n },\n async post<T extends HttpKey>(url: T, data?: FormData | Record<string, unknown>, config: PostConfig = {}) {\n const isForm = isFormData(data)\n const requestUrl = `${joinUrl(options.baseURL, url)}${toQueryString(config.params, 'repeat')}`\n const response = await clientFetch(requestUrl, {\n ...options.requestInit,\n body: isForm || data === undefined ? data : JSON.stringify(data),\n credentials: 'include',\n headers: resolveHeaders(\n options.headers,\n data !== undefined && !isForm ? { 'Content-Type': 'application/json' } : undefined,\n ),\n method: 'POST',\n signal: createSignal(config.signal, timeout),\n })\n\n let parsedResult: Awaited<ReturnType<typeof response.json>> = {}\n try {\n parsedResult = await response.json()\n } catch {\n parsedResult = {}\n }\n\n const parsedResponse = {\n config: {\n ignore: config.ignore,\n url: joinUrl(options.baseURL, url),\n },\n data: parsedResult,\n status: response.status,\n }\n\n await options.onResponseError?.(parsedResponse)\n return parsedResponse\n },\n }\n}\n\nfunction createAnySignal(signals: AbortSignal[]): AbortSignal {\n if (typeof AbortSignal?.any === 'function') {\n return AbortSignal.any(signals)\n }\n\n const controller = new AbortController()\n\n signals.filter(Boolean).forEach((signal) => {\n signal.addEventListener('abort', () => controller.abort(), { once: true })\n })\n\n return controller.signal\n}\n\nfunction createSignal(signal: AbortSignal | AbortSignal[] | undefined, timeout: number): AbortSignal {\n const timeoutSignal = createTimeoutSignal(timeout)\n\n if (Array.isArray(signal)) {\n return createAnySignal([...signal, timeoutSignal])\n }\n\n if (signal) {\n return createAnySignal([signal, timeoutSignal])\n }\n\n return timeoutSignal\n}\n\nfunction createTimeoutSignal(ms: number): AbortSignal {\n if (typeof AbortSignal?.timeout === 'function') {\n return AbortSignal.timeout(ms)\n }\n\n const controller = new AbortController()\n const timeoutId = setTimeout(() => controller.abort(), ms)\n\n controller.signal.addEventListener('abort', () => clearTimeout(timeoutId), {\n once: true,\n })\n\n return controller.signal\n}\n\nfunction getRetryDelay(attempt: number, delay: number): number {\n return delay * 2 ** attempt\n}\n\nfunction isAbortError(err: unknown): boolean {\n return err instanceof DOMException && err.name === 'AbortError'\n}\n\nfunction isFormData(value: FormData | Record<string, unknown> | undefined): value is FormData {\n return typeof FormData !== 'undefined' && value instanceof FormData\n}\n\nfunction isRetryableStatus(status: number): boolean {\n return status >= 500 || status === 429\n}\n\nfunction joinUrl(baseURL: string, url: string): string {\n const normalizedBase = baseURL.endsWith('/') ? baseURL.slice(0, -1) : baseURL\n const normalizedUrl = url.startsWith('/') ? url.slice(1) : url\n\n return `${normalizedBase}/${normalizedUrl}`\n}\n\nfunction resolveHeaders(\n value: CreateHttpOptions['headers'],\n extraHeaders?: Record<string, string>,\n): Record<string, string> {\n const headers = typeof value === 'function' ? value() : value\n\n return Object.entries({\n ...headers,\n ...extraHeaders,\n }).reduce<Record<string, string>>((acc, [key, headerValue]) => {\n if (typeof headerValue === 'string') {\n acc[key] = headerValue\n }\n\n return acc\n }, {})\n}\n\nfunction serializeQueryValue(\n urlParams: URLSearchParams,\n key: string,\n value: HttpParam[string],\n arrayMode: QueryArrayMode,\n): void {\n if (value === undefined) {\n return\n }\n\n if (Array.isArray(value)) {\n if (arrayMode === 'json') {\n urlParams.append(key, JSON.stringify(value))\n return\n }\n\n value.forEach((item) => {\n urlParams.append(`${key}[]`, item)\n })\n return\n }\n\n urlParams.append(key, String(value))\n}\n\nfunction toQueryString(params?: HttpParam, arrayMode: QueryArrayMode = 'json'): string {\n const urlParams = new URLSearchParams()\n\n Object.entries(params ?? {}).forEach(([key, value]) => {\n serializeQueryValue(urlParams, key, value, arrayMode)\n })\n\n return urlParams.size > 0 ? `?${urlParams}` : ''\n}\n\nfunction wait(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms))\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../src/create-get.ts","../src/http.ts"],"sourcesContent":["import type { HttpClient, HttpKey, HttpParam } from './http'\nimport type { UseHttpFn, UseHttpOptions, UseHttpResult } from './nuxt'\n\nexport type CreateGetOptions<T extends HttpKey, P extends HttpParam> = Omit<UseHttpOptions<T, P>, 'url'>\n\nexport type CreateGetPayload<T extends HttpKey, P extends HttpParam> = Pick<\n UseHttpOptions<T, P>,\n 'effect' | 'isError' | 'mapParams'\n>\n\nexport type CreateGetResult<T extends HttpKey, P extends HttpParam> = UseHttpResult<T, P>\n\nexport function createUseCase<D>() {\n return <T>(\n callback: (payload: D & { httpClient: HttpClient }) => T,\n ): ((payload: D & { httpClient: HttpClient }) => T) => callback\n}\n\nexport function defineGet(useHttp: UseHttpFn) {\n return function withParams<P extends HttpParam>() {\n return function withUrl<T extends HttpKey>(\n url: T,\n payload?: CreateGetPayload<T, P>,\n ): (options?: CreateGetOptions<T, P>) => Promise<CreateGetResult<T, P>> {\n return async function request(options: CreateGetOptions<T, P> = {}): Promise<CreateGetResult<T, P>> {\n const result = await useHttp({\n ...options,\n effect: (...args) => {\n const [data, config] = args\n\n payload?.effect?.(data, config)\n options?.effect?.(data, config)\n },\n isError: options.isError ?? payload?.isError,\n mapParams: options.mapParams ?? payload?.mapParams,\n url,\n })\n\n return result\n }\n }\n }\n}\n","export interface CreateHttpOptions {\n arrayMode?: HttpArrayMode\n baseURL: string\n fetch?: typeof fetch\n headers?: (() => HttpHeaders) | HttpHeaders\n onResponse?: (response: HttpAnyResponse) => Promise<void> | void\n timeout?: number\n}\n\nexport type GetConfig<TKey extends HttpKey = HttpKey> = HttpConfigFull<TKey> & {\n retry?: {\n delay?: number\n retries?: number\n }\n}\n\nexport type HttpAnyResponse = HttpResponse<HttpResponseData<HttpKey>, HttpKey, HttpConfigFull<HttpKey>>\n\nexport type HttpArrayMode = 'json' | 'repeat'\n\nexport type HttpClient = {\n get: <T extends HttpKey, TConfig extends GetConfig<T> = GetConfig<T>>(\n url: T,\n config?: TConfig,\n ) => Promise<HttpResponse<HttpResponseData<T>, T, TConfig>>\n post: <T extends HttpKey, TConfig extends PostConfig<T> = PostConfig<T>>(\n url: T,\n data?: FormData | Record<string, unknown>,\n config?: TConfig,\n ) => Promise<HttpResponse<HttpResponseData<T>, T, TConfig>>\n}\n\n// eslint-disable-next-line @typescript-eslint/no-unused-vars\nexport interface HttpConfig<TKey extends HttpKey = HttpKey> {}\nexport interface HttpEndpoint {}\nexport type HttpErrorGuard<TData = unknown, TError extends TData = TData> = (payload: TData) => payload is TError\n\nexport type HttpKey = Extract<keyof HttpSchema, string>\nexport type HttpParam = Record<string, boolean | number | string | string[] | undefined>\n\nexport interface HttpResponse<\n T = unknown,\n TKey extends HttpKey = HttpKey,\n TConfig extends HttpConfigFull<TKey> = HttpConfigFull<TKey>,\n> {\n config: HttpResponseConfig<TKey, TConfig>\n data: T\n status: number\n}\nexport type HttpResponseConfig<\n TKey extends HttpKey = HttpKey,\n TConfig extends HttpConfig<TKey> = HttpConfig<TKey>,\n> = Omit<TConfig, 'signal'> & { url: string }\n\nexport type HttpResponseData<TKey extends HttpKey = HttpKey> = HttpSchema[TKey]\n\nexport type PostConfig<TKey extends HttpKey = HttpKey> = HttpConfigFull<TKey>\n\ntype HttpConfigFull<TKey extends HttpKey = HttpKey> = HttpConfig<TKey> & {\n params?: HttpParam\n signal?: AbortSignal | AbortSignal[]\n}\n\ntype HttpHeaders = Record<string, string | undefined>\n\ntype HttpSchema = keyof HttpEndpoint extends never ? Record<string, unknown> : HttpEndpoint\n\nexport function createHttp(options: CreateHttpOptions): HttpClient {\n const arrayMode = options.arrayMode ?? 'json'\n const clientFetch = options.fetch ?? fetch\n const timeout = options.timeout ?? 80000\n\n return {\n get<T extends HttpKey, TConfig extends GetConfig<T> = GetConfig<T>>(url: T, config?: TConfig) {\n const requestConfig = (config ?? {}) as TConfig\n const requestUrl = `${joinUrl(options.baseURL, url)}${toQueryString(requestConfig.params, arrayMode)}`\n const { delay = 300, retries = 3 } = requestConfig.retry ?? {}\n\n const attemptRequest = async (attempt: number): Promise<HttpResponse<HttpResponseData<T>, T, TConfig>> => {\n try {\n const result = await clientFetch(requestUrl, {\n credentials: 'include',\n headers: resolveHeaders(options.headers),\n method: 'GET',\n signal: createSignal(requestConfig.signal, timeout),\n })\n\n const parsedResult = await result.json()\n const response: HttpResponse<HttpResponseData<T>, T, TConfig> = {\n config: createResponseConfig<T, TConfig>(joinUrl(options.baseURL, url), requestConfig),\n data: parsedResult,\n status: result.status,\n }\n\n if (!result.ok && isRetryableStatus(result.status) && attempt < retries) {\n await wait(getRetryDelay(attempt, delay))\n return attemptRequest(attempt + 1)\n }\n\n await options.onResponse?.(response as HttpAnyResponse)\n return response\n } catch (err) {\n if (isAbortError(err) || attempt >= retries) {\n throw err\n }\n\n await wait(getRetryDelay(attempt, delay))\n return attemptRequest(attempt + 1)\n }\n }\n\n return attemptRequest(0)\n },\n async post<T extends HttpKey, TConfig extends PostConfig<T> = PostConfig<T>>(\n url: T,\n data?: FormData | Record<string, unknown>,\n config?: TConfig,\n ) {\n const requestConfig = (config ?? {}) as TConfig\n const isForm = isFormData(data)\n const requestUrl = `${joinUrl(options.baseURL, url)}${toQueryString(requestConfig.params, arrayMode)}`\n const response = await clientFetch(requestUrl, {\n body: isForm || data === undefined ? data : JSON.stringify(data),\n credentials: 'include',\n headers: resolveHeaders(\n options.headers,\n data !== undefined && !isForm ? { 'Content-Type': 'application/json' } : undefined,\n ),\n method: 'POST',\n signal: createSignal(requestConfig.signal, timeout),\n })\n\n let parsedResult: Awaited<ReturnType<typeof response.json>> = {}\n try {\n parsedResult = await response.json()\n } catch {\n parsedResult = {}\n }\n\n const parsedResponse: HttpResponse<HttpResponseData<T>, T, TConfig> = {\n config: createResponseConfig<T, TConfig>(joinUrl(options.baseURL, url), requestConfig),\n data: parsedResult,\n status: response.status,\n }\n\n await options.onResponse?.(parsedResponse as HttpAnyResponse)\n return parsedResponse\n },\n }\n}\n\nfunction createAnySignal(signals: AbortSignal[]): AbortSignal {\n if (typeof AbortSignal?.any === 'function') {\n return AbortSignal.any(signals)\n }\n\n const controller = new AbortController()\n\n signals.filter(Boolean).forEach((signal) => {\n signal.addEventListener('abort', () => controller.abort(), { once: true })\n })\n\n return controller.signal\n}\n\nfunction createResponseConfig<TKey extends HttpKey, TConfig extends HttpConfigFull<TKey>>(\n url: string,\n config: TConfig,\n): HttpResponseConfig<TKey, TConfig> {\n const responseConfig = { ...config } as Record<string, unknown>\n delete responseConfig.signal\n\n return {\n ...responseConfig,\n url,\n } as HttpResponseConfig<TKey, TConfig>\n}\n\nfunction createSignal(signal: AbortSignal | AbortSignal[] | undefined, timeout: number): AbortSignal {\n const timeoutSignal = createTimeoutSignal(timeout)\n\n if (Array.isArray(signal)) {\n return createAnySignal([...signal, timeoutSignal])\n }\n\n if (signal) {\n return createAnySignal([signal, timeoutSignal])\n }\n\n return timeoutSignal\n}\n\nfunction createTimeoutSignal(ms: number): AbortSignal {\n if (typeof AbortSignal?.timeout === 'function') {\n return AbortSignal.timeout(ms)\n }\n\n const controller = new AbortController()\n const timeoutId = setTimeout(() => controller.abort(), ms)\n\n controller.signal.addEventListener('abort', () => clearTimeout(timeoutId), {\n once: true,\n })\n\n return controller.signal\n}\n\nfunction getRetryDelay(attempt: number, delay: number): number {\n return delay * 2 ** attempt\n}\n\nfunction isAbortError(err: unknown): boolean {\n return err instanceof DOMException && err.name === 'AbortError'\n}\n\nfunction isFormData(value: FormData | Record<string, unknown> | undefined): value is FormData {\n return typeof FormData !== 'undefined' && value instanceof FormData\n}\n\nfunction isRetryableStatus(status: number): boolean {\n return status >= 500 || status === 429\n}\n\nfunction joinUrl(baseURL: string, url: string): string {\n const normalizedBase = baseURL.endsWith('/') ? baseURL.slice(0, -1) : baseURL\n const normalizedUrl = url.startsWith('/') ? url.slice(1) : url\n\n return `${normalizedBase}/${normalizedUrl}`\n}\n\nfunction resolveHeaders(\n value: CreateHttpOptions['headers'],\n extraHeaders?: Record<string, string>,\n): Record<string, string> {\n const headers = typeof value === 'function' ? value() : value\n\n return Object.entries({\n ...headers,\n ...extraHeaders,\n }).reduce<Record<string, string>>((acc, [key, headerValue]) => {\n if (typeof headerValue === 'string') {\n acc[key] = headerValue\n }\n\n return acc\n }, {})\n}\n\nfunction serializeQueryValue(\n urlParams: URLSearchParams,\n key: string,\n value: HttpParam[string],\n arrayMode: HttpArrayMode,\n): void {\n if (value === undefined) {\n return\n }\n\n if (Array.isArray(value)) {\n if (arrayMode === 'json') {\n urlParams.append(key, JSON.stringify(value))\n return\n }\n\n value.forEach((item) => {\n urlParams.append(`${key}[]`, item)\n })\n return\n }\n\n urlParams.append(key, String(value))\n}\n\nfunction toQueryString(params?: HttpParam, arrayMode: HttpArrayMode = 'json'): string {\n const urlParams = new URLSearchParams()\n\n Object.entries(params ?? {}).forEach(([key, value]) => {\n serializeQueryValue(urlParams, key, value, arrayMode)\n })\n\n return urlParams.size > 0 ? `?${urlParams}` : ''\n}\n\nfunction wait(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms))\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
import { useLazyAsyncData, useState } from "nuxt/app";
|
|
2
|
+
import { onScopeDispose, shallowReactive, shallowRef } from "vue";
|
|
3
|
+
//#region src/utils.ts
|
|
4
|
+
function createURL(url, params) {
|
|
5
|
+
const urlParams = new URLSearchParams();
|
|
6
|
+
Object.entries(params ?? {}).forEach(([key, value]) => {
|
|
7
|
+
if (value === void 0) return;
|
|
8
|
+
if (Array.isArray(value)) {
|
|
9
|
+
value.forEach((item) => {
|
|
10
|
+
urlParams.append(`${key}[]`, item);
|
|
11
|
+
});
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
urlParams.append(key, String(value));
|
|
15
|
+
});
|
|
16
|
+
const query = urlParams.size > 0 ? `?${urlParams}` : "";
|
|
17
|
+
return urlParams.size > 0 ? `${url}${query}` : url;
|
|
18
|
+
}
|
|
19
|
+
function fastDevHash(data) {
|
|
20
|
+
const str = JSON.stringify(data);
|
|
21
|
+
let hash = 0;
|
|
22
|
+
for (let i = 0; i < str.length; i++) {
|
|
23
|
+
const chr = str.charCodeAt(i);
|
|
24
|
+
hash = (hash << 5) - hash + chr;
|
|
25
|
+
hash |= 0;
|
|
26
|
+
}
|
|
27
|
+
return Math.abs(hash).toString(16);
|
|
28
|
+
}
|
|
29
|
+
async function hashData(data) {
|
|
30
|
+
const subtle = globalThis.crypto?.subtle;
|
|
31
|
+
if (subtle) {
|
|
32
|
+
const encoded = new TextEncoder().encode(JSON.stringify(data));
|
|
33
|
+
const buffer = await subtle.digest("SHA-1", encoded);
|
|
34
|
+
return Array.from(new Uint8Array(buffer)).map((value) => value.toString(16).padStart(2, "0")).join("");
|
|
35
|
+
}
|
|
36
|
+
return fastDevHash(data);
|
|
37
|
+
}
|
|
38
|
+
//#endregion
|
|
39
|
+
//#region src/nuxt.ts
|
|
40
|
+
const DAY = 1e3 * 60 * 60 * 24;
|
|
41
|
+
const DEFAULT_CHANNEL_NAME = "http-tab-sync";
|
|
42
|
+
const DEFAULT_TTL = DAY * 7;
|
|
43
|
+
function createUseHttp(dependencies) {
|
|
44
|
+
let channel;
|
|
45
|
+
const useHttp = async (options) => {
|
|
46
|
+
const mapParams = (params) => {
|
|
47
|
+
if (options.mapParams) return options.mapParams(params);
|
|
48
|
+
return params ?? {};
|
|
49
|
+
};
|
|
50
|
+
const effect = options.effect;
|
|
51
|
+
const isError = createIsErrorGuard(options.isError, dependencies.isError);
|
|
52
|
+
const buildUrl = createURL;
|
|
53
|
+
const initFullUrl = buildUrl(options.url, mapParams(options.initParams));
|
|
54
|
+
const httpClient = dependencies.getHttpClient();
|
|
55
|
+
const cache = import.meta.client ? dependencies.getCache?.() ?? null : null;
|
|
56
|
+
let hasDataFromServer = false;
|
|
57
|
+
const result = shallowReactive({
|
|
58
|
+
data: null,
|
|
59
|
+
error: null,
|
|
60
|
+
fetch: async (_params, _opt) => await void 0,
|
|
61
|
+
hasFirstData: false,
|
|
62
|
+
hasFreshData: false,
|
|
63
|
+
pending: true,
|
|
64
|
+
pendingCache: true
|
|
65
|
+
});
|
|
66
|
+
const setError = (value) => {
|
|
67
|
+
result.error = value;
|
|
68
|
+
};
|
|
69
|
+
const setData = (value) => {
|
|
70
|
+
result.data = value;
|
|
71
|
+
};
|
|
72
|
+
const syncResult = (payload) => {
|
|
73
|
+
if (payload === null || payload === void 0) {
|
|
74
|
+
setData(null);
|
|
75
|
+
setError(null);
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
if (isError(payload)) {
|
|
79
|
+
setData(null);
|
|
80
|
+
setError(payload);
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
setData(payload);
|
|
84
|
+
setError(null);
|
|
85
|
+
};
|
|
86
|
+
const controller = new AbortController();
|
|
87
|
+
const serverData = useState(`http-${initFullUrl}`, () => null);
|
|
88
|
+
if (options.server && import.meta.server) {
|
|
89
|
+
const paramsReactive = shallowRef(mapParams(options.initParams));
|
|
90
|
+
const ssr = await useLazyAsyncData(initFullUrl, async () => {
|
|
91
|
+
return await httpClient.get(options.url, { params: paramsReactive.value });
|
|
92
|
+
});
|
|
93
|
+
result.fetch = async (params) => {
|
|
94
|
+
paramsReactive.value = mapParams(params);
|
|
95
|
+
await ssr.refresh();
|
|
96
|
+
};
|
|
97
|
+
serverData.value = ssr.data.value?.data ?? null;
|
|
98
|
+
const serverPayload = serverData.value;
|
|
99
|
+
syncResult(serverPayload);
|
|
100
|
+
result.pending = false;
|
|
101
|
+
result.pendingCache = false;
|
|
102
|
+
result.hasFirstData = true;
|
|
103
|
+
result.hasFreshData = true;
|
|
104
|
+
if (serverData.value) effect?.(serverData.value, {
|
|
105
|
+
cached: false,
|
|
106
|
+
params: mapParams(options.initParams)
|
|
107
|
+
});
|
|
108
|
+
if (result.data) hasDataFromServer = true;
|
|
109
|
+
}
|
|
110
|
+
if (import.meta.client) {
|
|
111
|
+
channel = getChannel(dependencies.channelName ?? DEFAULT_CHANNEL_NAME, channel);
|
|
112
|
+
const fullUrlHistory = {};
|
|
113
|
+
function onMessage(event) {
|
|
114
|
+
if (event.data.fullUrl && fullUrlHistory[event.data.fullUrl]) {
|
|
115
|
+
const eventPayload = event.data.data;
|
|
116
|
+
if (eventPayload) effect?.(eventPayload, {
|
|
117
|
+
cached: false,
|
|
118
|
+
params: event.data.params
|
|
119
|
+
});
|
|
120
|
+
syncResult(eventPayload);
|
|
121
|
+
result.hasFirstData = true;
|
|
122
|
+
result.hasFreshData = true;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
channel?.addEventListener("message", onMessage);
|
|
126
|
+
if (serverData.value) {
|
|
127
|
+
const clientServerPayload = serverData.value;
|
|
128
|
+
syncResult(clientServerPayload);
|
|
129
|
+
result.pending = false;
|
|
130
|
+
result.pendingCache = false;
|
|
131
|
+
result.hasFirstData = true;
|
|
132
|
+
result.hasFreshData = true;
|
|
133
|
+
}
|
|
134
|
+
const raceCondition = {};
|
|
135
|
+
const ttl = dependencies.ttl ?? DEFAULT_TTL;
|
|
136
|
+
const runFetch = async (params, fetchOpt) => {
|
|
137
|
+
const mappedParams = mapParams(params);
|
|
138
|
+
const fullUrl = buildUrl(options.url, mappedParams);
|
|
139
|
+
const fetchId = Date.now() + getRandom(0, 300);
|
|
140
|
+
if (raceCondition[fullUrl]) {
|
|
141
|
+
console.info("Race Condition affect", fullUrl);
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
raceCondition[fullUrl] = fetchId;
|
|
145
|
+
try {
|
|
146
|
+
result.pending = true;
|
|
147
|
+
result.pendingCache = true;
|
|
148
|
+
const cachedFetch = cache && !dependencies.isDev?.() ? await cache.get(fullUrl) : null;
|
|
149
|
+
if (cachedFetch) {
|
|
150
|
+
const cachedPayload = cachedFetch.value;
|
|
151
|
+
effect?.(cachedPayload, {
|
|
152
|
+
cached: true,
|
|
153
|
+
params: mappedParams
|
|
154
|
+
});
|
|
155
|
+
syncResult(cachedPayload);
|
|
156
|
+
result.hasFirstData = true;
|
|
157
|
+
result.pendingCache = false;
|
|
158
|
+
}
|
|
159
|
+
if (controller.signal.aborted || fetchOpt?.signal?.aborted) throw new DOMException("Aborted", "AbortError");
|
|
160
|
+
const signalHttp = fetchOpt?.signal ? [controller.signal, fetchOpt.signal] : controller.signal;
|
|
161
|
+
const response = await httpClient.get(options.url, {
|
|
162
|
+
params: mappedParams,
|
|
163
|
+
signal: signalHttp
|
|
164
|
+
});
|
|
165
|
+
const responsePayload = response.data;
|
|
166
|
+
effect?.(responsePayload, {
|
|
167
|
+
cached: false,
|
|
168
|
+
params: mappedParams
|
|
169
|
+
});
|
|
170
|
+
if (isError(responsePayload)) syncResult(responsePayload);
|
|
171
|
+
else {
|
|
172
|
+
syncResult(responsePayload);
|
|
173
|
+
const successData = responsePayload;
|
|
174
|
+
fullUrlHistory[fullUrl] = true;
|
|
175
|
+
channel?.postMessage({
|
|
176
|
+
data: normalizeBroadcastValue(successData),
|
|
177
|
+
fullUrl,
|
|
178
|
+
params: normalizeBroadcastValue(mappedParams),
|
|
179
|
+
type: "STATE_UPDATE"
|
|
180
|
+
});
|
|
181
|
+
if (cache) {
|
|
182
|
+
if (cachedFetch) {
|
|
183
|
+
if (await hashData(responsePayload) !== cachedFetch.hash) await cache.deleteKeysWithPart(options.url);
|
|
184
|
+
}
|
|
185
|
+
if (response.status === 200) await cache.set(fullUrl, successData, ttl);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
result.hasFirstData = true;
|
|
189
|
+
result.hasFreshData = true;
|
|
190
|
+
} catch (error) {
|
|
191
|
+
console.error(error);
|
|
192
|
+
} finally {
|
|
193
|
+
if (raceCondition[fullUrl] === fetchId) delete raceCondition[fullUrl];
|
|
194
|
+
result.pending = false;
|
|
195
|
+
result.pendingCache = false;
|
|
196
|
+
}
|
|
197
|
+
};
|
|
198
|
+
result.fetch = runFetch;
|
|
199
|
+
if (!hasDataFromServer && options.lazy !== true) runFetch(options.initParams);
|
|
200
|
+
onScopeDispose(() => {
|
|
201
|
+
channel?.removeEventListener("message", onMessage);
|
|
202
|
+
controller.abort(`Http Abort -> onScopeDispose ${options.url}`);
|
|
203
|
+
if (serverData.value) serverData.value = null;
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
return result;
|
|
207
|
+
};
|
|
208
|
+
return useHttp;
|
|
209
|
+
}
|
|
210
|
+
function createIsErrorGuard(localGuard, globalGuard) {
|
|
211
|
+
if (localGuard) return localGuard;
|
|
212
|
+
if (globalGuard) return globalGuard;
|
|
213
|
+
return () => false;
|
|
214
|
+
}
|
|
215
|
+
function getChannel(name, currentChannel) {
|
|
216
|
+
if (currentChannel) return currentChannel;
|
|
217
|
+
if (import.meta.server || typeof BroadcastChannel !== "function") return null;
|
|
218
|
+
return new BroadcastChannel(name);
|
|
219
|
+
}
|
|
220
|
+
function getRandom(min, max) {
|
|
221
|
+
return Math.floor(Math.random() * (max - min + 1)) + min;
|
|
222
|
+
}
|
|
223
|
+
function normalizeBroadcastValue(payload) {
|
|
224
|
+
try {
|
|
225
|
+
return structuredClone(payload);
|
|
226
|
+
} catch {
|
|
227
|
+
return JSON.parse(JSON.stringify(payload));
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
//#endregion
|
|
231
|
+
export { createUseHttp as t };
|
|
232
|
+
|
|
233
|
+
//# sourceMappingURL=nuxt-BsZgB52H.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nuxt-BsZgB52H.js","names":[],"sources":["../src/utils.ts","../src/nuxt.ts"],"sourcesContent":["import type { HttpParam } from './http'\n\nexport function createURL(url: string, params?: HttpParam): string {\n const urlParams = new URLSearchParams()\n\n Object.entries(params ?? {}).forEach(([key, value]) => {\n if (value === undefined) {\n return\n }\n\n if (Array.isArray(value)) {\n value.forEach((item) => {\n urlParams.append(`${key}[]`, item)\n })\n return\n }\n\n urlParams.append(key, String(value))\n })\n\n const query = urlParams.size > 0 ? `?${urlParams}` : ''\n return urlParams.size > 0 ? `${url}${query}` : url\n}\n\nexport function fastDevHash(data: unknown): string {\n const str = JSON.stringify(data)\n let hash = 0\n\n for (let i = 0; i < str.length; i++) {\n const chr = str.charCodeAt(i)\n hash = (hash << 5) - hash + chr\n hash |= 0\n }\n\n return Math.abs(hash).toString(16)\n}\n\nexport async function hashData(data: unknown): Promise<string> {\n const subtle = globalThis.crypto?.subtle\n\n if (subtle) {\n const encoded = new TextEncoder().encode(JSON.stringify(data))\n const buffer = await subtle.digest('SHA-1', encoded)\n const array = Array.from(new Uint8Array(buffer))\n\n return array.map((value) => value.toString(16).padStart(2, '0')).join('')\n }\n\n return fastDevHash(data)\n}\n","import { useLazyAsyncData, useState } from 'nuxt/app'\nimport { onScopeDispose, shallowReactive, shallowRef } from 'vue'\n\nimport type { GetConfig, HttpClient, HttpErrorGuard, HttpKey, HttpParam, HttpResponseData } from './http'\n\nimport { createURL, hashData } from './utils'\n\nconst DAY = 1000 * 60 * 60 * 24\nconst DEFAULT_CHANNEL_NAME = 'http-tab-sync'\nconst DEFAULT_TTL = DAY * 7\nexport interface CreateUseHttpDependencies {\n channelName?: string\n getCache: () => null | UseHttpCache\n getHttpClient: () => HttpClient\n isDev?: () => boolean\n isError?: HttpErrorGuard<unknown>\n ttl?: number\n}\n\nexport type HttpError = HttpErrorMap[keyof HttpErrorMap]\nexport interface HttpErrorMap {}\nexport type HttpSuccessData<TKey extends HttpKey = HttpKey> = Exclude<HttpResponseData<TKey>, HttpError>\n\nexport interface UseHttpCache {\n deleteKeysWithPart: (part: string) => Promise<void>\n get: <T>(key: string) => Promise<null | UseHttpCacheEntry<T>>\n set: <T>(key: string, value: T, ttl: number) => Promise<void>\n}\n\nexport interface UseHttpCacheEntry<T> {\n hash: string\n value: T\n}\n\nexport interface UseHttpFn {\n <T extends HttpKey, P extends HttpParam>(options: UseHttpOptions<T, P>): Promise<UseHttpResult<T, P>>\n}\n\nexport interface UseHttpOptions<T extends HttpKey, P extends HttpParam> {\n effect?: UseHttpEffect<T, P>\n initParams?: P\n isError?: (payload: HttpResponseData<T>) => boolean\n lazy?: true\n mapParams?: (params?: P) => P\n server?: boolean\n url: T\n}\n\nexport interface UseHttpResult<T extends HttpKey, P extends HttpParam> {\n data: HttpSuccessData<T> | null\n error: null | UseHttpError<T>\n fetch: UseHttpFetch<P>\n hasFirstData: boolean\n hasFreshData: boolean\n pending: boolean\n pendingCache: boolean\n}\n\ntype BroadcastMessage = {\n data: unknown\n fullUrl: string\n params: HttpParam\n type: 'STATE_UPDATE'\n}\n\ntype UseHttpEffect<T extends HttpKey, P extends HttpParam> = (\n data: HttpResponseData<T>,\n config: UseHttpEffectConfig<P>,\n) => void\ntype UseHttpEffectConfig<P extends HttpParam> = {\n cached: boolean\n params: P\n}\ntype UseHttpError<T extends HttpKey> = Extract<HttpResponseData<T>, HttpError>\n\ntype UseHttpFetch<P extends HttpParam> = (params?: P, opt?: { signal: AbortSignal }) => Promise<void>\n\nexport function createUseHttp(dependencies: CreateUseHttpDependencies): UseHttpFn {\n let channel: BroadcastChannel | null\n\n const useHttp: UseHttpFn = async <T extends HttpKey, P extends HttpParam>(\n options: UseHttpOptions<T, P>,\n ): Promise<UseHttpResult<T, P>> => {\n const mapParams = (params?: P): P => {\n if (options.mapParams) {\n return options.mapParams(params)\n }\n\n return params ?? ({} as P)\n }\n const effect = options.effect\n const isError = createIsErrorGuard(options.isError, dependencies.isError)\n\n const buildUrl = createURL\n const initFullUrl = buildUrl(options.url, mapParams(options.initParams))\n const httpClient = dependencies.getHttpClient()\n const cache = import.meta.client ? (dependencies.getCache?.() ?? null) : null\n let hasDataFromServer = false\n\n const result = shallowReactive({\n data: null as null | unknown,\n error: null as null | unknown,\n fetch: async (_params?: P, _opt?: { signal: AbortSignal }): Promise<void> => await undefined,\n hasFirstData: false,\n hasFreshData: false,\n pending: true,\n pendingCache: true,\n })\n const setError = (value: null | unknown): void => {\n result.error = value\n }\n const setData = (value: null | unknown): void => {\n result.data = value\n }\n const syncResult = (payload: HttpResponseData<T> | null | undefined): void => {\n if (payload === null || payload === undefined) {\n setData(null)\n setError(null)\n return\n }\n\n if (isError(payload)) {\n setData(null)\n setError(payload)\n return\n }\n\n setData(payload)\n setError(null)\n }\n\n const controller = new AbortController()\n const serverData = useState<null | unknown>(`http-${initFullUrl}`, () => null)\n\n if (options.server && import.meta.server) {\n const paramsReactive = shallowRef(mapParams(options.initParams))\n const ssr = await useLazyAsyncData(initFullUrl, async () => {\n return await httpClient.get<T>(options.url, {\n params: paramsReactive.value,\n } as GetConfig<T>)\n })\n\n result.fetch = async (params?: P) => {\n paramsReactive.value = mapParams(params)\n await ssr.refresh()\n }\n\n serverData.value = ssr.data.value?.data ?? null\n const serverPayload = serverData.value as HttpResponseData<T>\n syncResult(serverPayload)\n\n result.pending = false\n result.pendingCache = false\n result.hasFirstData = true\n result.hasFreshData = true\n\n if (serverData.value) {\n effect?.(serverData.value as HttpResponseData<T>, {\n cached: false,\n params: mapParams(options.initParams),\n })\n }\n\n if (result.data) {\n hasDataFromServer = true\n }\n }\n\n if (import.meta.client) {\n channel = getChannel(dependencies.channelName ?? DEFAULT_CHANNEL_NAME, channel)\n const fullUrlHistory: Record<string, true> = {}\n\n function onMessage(event: MessageEvent<Partial<BroadcastMessage>>): void {\n if (event.data.fullUrl && fullUrlHistory[event.data.fullUrl]) {\n const eventPayload = event.data.data as HttpResponseData<T> | undefined\n\n if (eventPayload) {\n effect?.(eventPayload, {\n cached: false,\n params: event.data.params as P,\n })\n }\n\n syncResult(eventPayload)\n\n result.hasFirstData = true\n result.hasFreshData = true\n }\n }\n\n channel?.addEventListener('message', onMessage)\n\n if (serverData.value) {\n const clientServerPayload = serverData.value as HttpResponseData<T>\n syncResult(clientServerPayload)\n result.pending = false\n result.pendingCache = false\n result.hasFirstData = true\n result.hasFreshData = true\n }\n\n const raceCondition: Record<string, number> = {}\n const ttl = dependencies.ttl ?? DEFAULT_TTL\n\n const runFetch = async (params?: P, fetchOpt?: { signal?: AbortSignal }): Promise<void> => {\n const mappedParams = mapParams(params)\n const fullUrl = buildUrl(options.url, mappedParams)\n const fetchId = Date.now() + getRandom(0, 300)\n\n if (raceCondition[fullUrl]) {\n console.info('Race Condition affect', fullUrl)\n return\n }\n\n raceCondition[fullUrl] = fetchId\n\n try {\n result.pending = true\n result.pendingCache = true\n\n const cachedFetch = cache && !dependencies.isDev?.() ? await cache.get<unknown>(fullUrl) : null\n\n if (cachedFetch) {\n const cachedPayload = cachedFetch.value as HttpResponseData<T>\n\n effect?.(cachedPayload, {\n cached: true,\n params: mappedParams,\n })\n\n syncResult(cachedPayload)\n result.hasFirstData = true\n\n result.pendingCache = false\n }\n\n if (controller.signal.aborted || fetchOpt?.signal?.aborted) {\n throw new DOMException('Aborted', 'AbortError')\n }\n\n const signalHttp = fetchOpt?.signal ? [controller.signal, fetchOpt.signal] : controller.signal\n const response = await httpClient.get<T>(options.url, {\n params: mappedParams,\n signal: signalHttp,\n } as GetConfig<T>)\n const responsePayload = response.data as HttpResponseData<T>\n\n effect?.(responsePayload, {\n cached: false,\n params: mappedParams,\n })\n\n if (isError(responsePayload)) {\n syncResult(responsePayload)\n } else {\n syncResult(responsePayload)\n const successData = responsePayload\n\n fullUrlHistory[fullUrl] = true\n channel?.postMessage({\n data: normalizeBroadcastValue(successData),\n fullUrl,\n params: normalizeBroadcastValue(mappedParams),\n type: 'STATE_UPDATE',\n } satisfies BroadcastMessage)\n\n if (cache) {\n if (cachedFetch) {\n const newHash = await hashData(responsePayload)\n if (newHash !== cachedFetch.hash) {\n await cache.deleteKeysWithPart(options.url)\n }\n }\n\n if (response.status === 200) {\n await cache.set(fullUrl, successData, ttl)\n }\n }\n }\n\n result.hasFirstData = true\n result.hasFreshData = true\n } catch (error) {\n console.error(error)\n } finally {\n if (raceCondition[fullUrl] === fetchId) {\n delete raceCondition[fullUrl]\n }\n\n result.pending = false\n result.pendingCache = false\n }\n }\n\n result.fetch = runFetch\n\n if (!hasDataFromServer && options.lazy !== true) {\n runFetch(options.initParams)\n }\n\n onScopeDispose(() => {\n channel?.removeEventListener('message', onMessage)\n controller.abort(`Http Abort -> onScopeDispose ${options.url}`)\n\n if (serverData.value) {\n serverData.value = null\n }\n })\n }\n\n return result as UseHttpResult<T, P>\n }\n\n return useHttp\n}\n\nfunction createIsErrorGuard<TData>(\n localGuard?: (payload: TData) => boolean,\n globalGuard?: HttpErrorGuard<unknown>,\n): (payload: TData) => boolean {\n if (localGuard) {\n return localGuard\n }\n\n if (globalGuard) {\n return globalGuard as (payload: TData) => boolean\n }\n\n return (): boolean => false\n}\n\nfunction getChannel(name: string, currentChannel?: BroadcastChannel | null): BroadcastChannel | null {\n if (currentChannel) {\n return currentChannel\n }\n\n if (import.meta.server || typeof BroadcastChannel !== 'function') {\n return null\n }\n\n return new BroadcastChannel(name)\n}\n\nfunction getRandom(min: number, max: number): number {\n return Math.floor(Math.random() * (max - min + 1)) + min\n}\n\nfunction normalizeBroadcastValue<T>(payload: T): T {\n try {\n return structuredClone(payload)\n } catch {\n return JSON.parse(JSON.stringify(payload)) as T\n }\n}\n"],"mappings":"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"}
|