@zimic/fetch 0.1.0-canary.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/LICENSE.md +16 -0
- package/README.md +230 -0
- package/dist/index.d.ts +116 -0
- package/dist/index.js +191 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +188 -0
- package/dist/index.mjs.map +1 -0
- package/index.d.ts +1 -0
- package/package.json +92 -0
- package/src/client/FetchClient.ts +232 -0
- package/src/client/errors/FetchResponseError.ts +26 -0
- package/src/client/factory.ts +13 -0
- package/src/client/types/json.ts +15 -0
- package/src/client/types/public.ts +83 -0
- package/src/client/types/requests.ts +149 -0
- package/src/index.ts +13 -0
- package/src/types/requests.ts +99 -0
- package/src/types/strings.d.ts +9 -0
- package/src/types/utils.ts +11 -0
- package/src/utils/files.ts +23 -0
- package/src/utils/imports.ts +14 -0
- package/src/utils/urls.ts +43 -0
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { HttpSchemaPath, HttpSchemaMethod, LiteralHttpSchemaPathFromNonLiteral, HttpSchema } from '@zimic/http';
|
|
2
|
+
|
|
3
|
+
import { Default, PossiblePromise } from '@/types/utils';
|
|
4
|
+
|
|
5
|
+
import FetchResponseError from '../errors/FetchResponseError';
|
|
6
|
+
import { FetchRequest, FetchRequestConstructor, FetchRequestInit, FetchResponse } from './requests';
|
|
7
|
+
|
|
8
|
+
export type FetchInput<
|
|
9
|
+
Schema extends HttpSchema,
|
|
10
|
+
Path extends HttpSchemaPath.NonLiteral<Schema, Method>,
|
|
11
|
+
Method extends HttpSchemaMethod<Schema>,
|
|
12
|
+
> =
|
|
13
|
+
| Path
|
|
14
|
+
| URL
|
|
15
|
+
| FetchRequest<
|
|
16
|
+
LiteralHttpSchemaPathFromNonLiteral<Schema, Method, Path>,
|
|
17
|
+
Method,
|
|
18
|
+
Default<Schema[LiteralHttpSchemaPathFromNonLiteral<Schema, Method, Path>][Method]>
|
|
19
|
+
>;
|
|
20
|
+
|
|
21
|
+
export interface FetchFunction<Schema extends HttpSchema> {
|
|
22
|
+
<Path extends HttpSchemaPath.NonLiteral<Schema, Method>, Method extends HttpSchemaMethod<Schema>>(
|
|
23
|
+
input: Path | URL,
|
|
24
|
+
init: FetchRequestInit<Schema, LiteralHttpSchemaPathFromNonLiteral<Schema, Method, Path>, Method>,
|
|
25
|
+
): Promise<
|
|
26
|
+
FetchResponse<
|
|
27
|
+
LiteralHttpSchemaPathFromNonLiteral<Schema, Method, Path>,
|
|
28
|
+
Method,
|
|
29
|
+
Default<Schema[LiteralHttpSchemaPathFromNonLiteral<Schema, Method, Path>][Method]>
|
|
30
|
+
>
|
|
31
|
+
>;
|
|
32
|
+
|
|
33
|
+
<Path extends HttpSchemaPath.NonLiteral<Schema, Method>, Method extends HttpSchemaMethod<Schema>>(
|
|
34
|
+
input: FetchRequest<
|
|
35
|
+
LiteralHttpSchemaPathFromNonLiteral<Schema, Method, Path>,
|
|
36
|
+
Method,
|
|
37
|
+
Default<Schema[LiteralHttpSchemaPathFromNonLiteral<Schema, Method, Path>][Method]>
|
|
38
|
+
>,
|
|
39
|
+
init?: FetchRequestInit<Schema, LiteralHttpSchemaPathFromNonLiteral<Schema, Method, Path>, Method>,
|
|
40
|
+
): Promise<
|
|
41
|
+
FetchResponse<
|
|
42
|
+
LiteralHttpSchemaPathFromNonLiteral<Schema, Method, Path>,
|
|
43
|
+
Method,
|
|
44
|
+
Default<Schema[LiteralHttpSchemaPathFromNonLiteral<Schema, Method, Path>][Method]>
|
|
45
|
+
>
|
|
46
|
+
>;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export interface FetchOptions<Schema extends HttpSchema> extends Omit<FetchRequestInit.Defaults, 'method'> {
|
|
50
|
+
onRequest?: (request: FetchRequest.Loose, fetch: Fetch<Schema>) => PossiblePromise<Request>;
|
|
51
|
+
onResponse?: (response: FetchResponse.Loose, fetch: Fetch<Schema>) => PossiblePromise<Response>;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export interface FetchClient<Schema extends HttpSchema> {
|
|
55
|
+
defaults: FetchRequestInit.Defaults;
|
|
56
|
+
|
|
57
|
+
Request: FetchRequestConstructor<Schema>;
|
|
58
|
+
|
|
59
|
+
onRequest?: FetchOptions<Schema>['onRequest'];
|
|
60
|
+
onResponse?: FetchOptions<Schema>['onResponse'];
|
|
61
|
+
|
|
62
|
+
isRequest: <Path extends HttpSchemaPath<Schema, Method>, Method extends HttpSchemaMethod<Schema>>(
|
|
63
|
+
request: unknown,
|
|
64
|
+
path: Path,
|
|
65
|
+
method: Method,
|
|
66
|
+
) => request is FetchRequest<Path, Method, Default<Schema[Path][Method]>>;
|
|
67
|
+
|
|
68
|
+
isResponse: <Path extends HttpSchemaPath<Schema, Method>, Method extends HttpSchemaMethod<Schema>>(
|
|
69
|
+
response: unknown,
|
|
70
|
+
path: Path,
|
|
71
|
+
method: Method,
|
|
72
|
+
) => response is FetchResponse<Path, Method, Default<Schema[Path][Method]>>;
|
|
73
|
+
|
|
74
|
+
isResponseError: <Path extends HttpSchemaPath<Schema, Method>, Method extends HttpSchemaMethod<Schema>>(
|
|
75
|
+
error: unknown,
|
|
76
|
+
path: Path,
|
|
77
|
+
method: Method,
|
|
78
|
+
) => error is FetchResponseError<Path, Method, Default<Schema[Path][Method]>>;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export type Fetch<Schema extends HttpSchema> = FetchFunction<Schema> & FetchClient<Schema>;
|
|
82
|
+
|
|
83
|
+
export type InferFetchSchema<FetchInstance> = FetchInstance extends Fetch<infer Schema> ? Schema : never;
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import {
|
|
2
|
+
HttpRequestSchema,
|
|
3
|
+
HttpMethod,
|
|
4
|
+
HttpSchema,
|
|
5
|
+
HttpSchemaPath,
|
|
6
|
+
HttpSchemaMethod,
|
|
7
|
+
HttpMethodSchema,
|
|
8
|
+
HttpResponseSchemaStatusCode,
|
|
9
|
+
HttpStatusCode,
|
|
10
|
+
HttpResponse,
|
|
11
|
+
HttpRequest,
|
|
12
|
+
HttpSearchParams,
|
|
13
|
+
HttpHeaders,
|
|
14
|
+
AllowAnyStringInPathParams,
|
|
15
|
+
LiteralHttpSchemaPathFromNonLiteral,
|
|
16
|
+
JSONValue,
|
|
17
|
+
HttpResponseBodySchema,
|
|
18
|
+
HttpResponseHeadersSchema,
|
|
19
|
+
HttpRequestHeadersSchema,
|
|
20
|
+
} from '@zimic/http';
|
|
21
|
+
|
|
22
|
+
import { Default, DefaultNoExclude, IfNever, ReplaceBy } from '@/types/utils';
|
|
23
|
+
|
|
24
|
+
import FetchResponseError, { AnyFetchRequestError } from '../errors/FetchResponseError';
|
|
25
|
+
import { JSONStringified } from './json';
|
|
26
|
+
import { FetchInput } from './public';
|
|
27
|
+
|
|
28
|
+
type FetchRequestInitWithHeaders<RequestSchema extends HttpRequestSchema> = [RequestSchema['headers']] extends [never]
|
|
29
|
+
? { headers?: undefined }
|
|
30
|
+
: undefined extends RequestSchema['headers']
|
|
31
|
+
? { headers?: RequestSchema['headers'] | HttpHeaders<Default<RequestSchema['headers']>> }
|
|
32
|
+
: { headers: RequestSchema['headers'] | HttpHeaders<Default<RequestSchema['headers']>> };
|
|
33
|
+
|
|
34
|
+
type FetchRequestInitWithSearchParams<RequestSchema extends HttpRequestSchema> = [
|
|
35
|
+
RequestSchema['searchParams'],
|
|
36
|
+
] extends [never]
|
|
37
|
+
? { searchParams?: undefined }
|
|
38
|
+
: undefined extends RequestSchema['searchParams']
|
|
39
|
+
? { searchParams?: RequestSchema['searchParams'] | HttpSearchParams<Default<RequestSchema['searchParams']>> }
|
|
40
|
+
: { searchParams: RequestSchema['searchParams'] | HttpSearchParams<Default<RequestSchema['searchParams']>> };
|
|
41
|
+
|
|
42
|
+
type FetchRequestInitWithBody<RequestSchema extends HttpRequestSchema> = [RequestSchema['body']] extends [never]
|
|
43
|
+
? { body?: null }
|
|
44
|
+
: undefined extends RequestSchema['body']
|
|
45
|
+
? { body?: ReplaceBy<RequestSchema['body'], undefined, null> }
|
|
46
|
+
: RequestSchema['body'] extends string
|
|
47
|
+
? { body: RequestSchema['body'] }
|
|
48
|
+
: RequestSchema['body'] extends JSONValue
|
|
49
|
+
? { body: JSONStringified<RequestSchema['body']> }
|
|
50
|
+
: { body: RequestSchema['body'] };
|
|
51
|
+
|
|
52
|
+
type FetchRequestInitPerPath<RequestSchema extends HttpRequestSchema> = FetchRequestInitWithHeaders<RequestSchema> &
|
|
53
|
+
FetchRequestInitWithSearchParams<RequestSchema> &
|
|
54
|
+
FetchRequestInitWithBody<RequestSchema>;
|
|
55
|
+
|
|
56
|
+
export type FetchRequestInit<
|
|
57
|
+
Schema extends HttpSchema,
|
|
58
|
+
Path extends HttpSchemaPath<Schema, Method>,
|
|
59
|
+
Method extends HttpSchemaMethod<Schema>,
|
|
60
|
+
> = RequestInit & { baseURL?: string; method: Method } & (Path extends Path
|
|
61
|
+
? FetchRequestInitPerPath<Default<Default<Schema[Path][Method]>['request']>>
|
|
62
|
+
: never);
|
|
63
|
+
|
|
64
|
+
export namespace FetchRequestInit {
|
|
65
|
+
export interface Defaults extends RequestInit {
|
|
66
|
+
baseURL: string;
|
|
67
|
+
method?: HttpMethod;
|
|
68
|
+
searchParams?: HttpSearchParams;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
type AllFetchResponseStatusCode<MethodSchema extends HttpMethodSchema> = HttpResponseSchemaStatusCode<
|
|
73
|
+
Default<MethodSchema['response']>
|
|
74
|
+
>;
|
|
75
|
+
|
|
76
|
+
type FetchResponseStatusCode<MethodSchema extends HttpMethodSchema, ErrorOnly extends boolean> = ErrorOnly extends true
|
|
77
|
+
? AllFetchResponseStatusCode<MethodSchema> & (HttpStatusCode.ClientError | HttpStatusCode.ServerError)
|
|
78
|
+
: AllFetchResponseStatusCode<MethodSchema>;
|
|
79
|
+
|
|
80
|
+
export type HttpRequestBodySchema<MethodSchema extends HttpMethodSchema> = ReplaceBy<
|
|
81
|
+
ReplaceBy<IfNever<DefaultNoExclude<Default<MethodSchema['request']>['body']>, null>, undefined, null>,
|
|
82
|
+
ArrayBuffer,
|
|
83
|
+
Blob
|
|
84
|
+
>;
|
|
85
|
+
|
|
86
|
+
export interface FetchRequest<
|
|
87
|
+
Path extends string = string,
|
|
88
|
+
Method extends HttpMethod = HttpMethod,
|
|
89
|
+
MethodSchema extends HttpMethodSchema = HttpMethodSchema,
|
|
90
|
+
> extends HttpRequest<HttpRequestBodySchema<MethodSchema>, HttpRequestHeadersSchema<MethodSchema>> {
|
|
91
|
+
path: AllowAnyStringInPathParams<Path>;
|
|
92
|
+
method: Method;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export namespace FetchRequest {
|
|
96
|
+
export interface Loose extends Request {
|
|
97
|
+
path: string;
|
|
98
|
+
method: HttpMethod;
|
|
99
|
+
clone: () => Loose;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export interface FetchResponsePerStatusCode<
|
|
104
|
+
Path extends string = string,
|
|
105
|
+
Method extends HttpMethod = HttpMethod,
|
|
106
|
+
MethodSchema extends HttpMethodSchema = HttpMethodSchema,
|
|
107
|
+
StatusCode extends HttpStatusCode = HttpStatusCode,
|
|
108
|
+
> extends HttpResponse<
|
|
109
|
+
HttpResponseBodySchema<MethodSchema, StatusCode>,
|
|
110
|
+
StatusCode,
|
|
111
|
+
HttpResponseHeadersSchema<MethodSchema, StatusCode>
|
|
112
|
+
> {
|
|
113
|
+
request: FetchRequest<Path, Method, MethodSchema>;
|
|
114
|
+
|
|
115
|
+
error: StatusCode extends HttpStatusCode.ClientError | HttpStatusCode.ServerError
|
|
116
|
+
? FetchResponseError<Path, Method, MethodSchema>
|
|
117
|
+
: null;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export type FetchResponse<
|
|
121
|
+
Path extends string = string,
|
|
122
|
+
Method extends HttpMethod = HttpMethod,
|
|
123
|
+
MethodSchema extends HttpMethodSchema = HttpMethodSchema,
|
|
124
|
+
ErrorOnly extends boolean = false,
|
|
125
|
+
StatusCode extends FetchResponseStatusCode<MethodSchema, ErrorOnly> = FetchResponseStatusCode<
|
|
126
|
+
MethodSchema,
|
|
127
|
+
ErrorOnly
|
|
128
|
+
>,
|
|
129
|
+
> = StatusCode extends StatusCode ? FetchResponsePerStatusCode<Path, Method, MethodSchema, StatusCode> : never;
|
|
130
|
+
|
|
131
|
+
export namespace FetchResponse {
|
|
132
|
+
export interface Loose extends Response {
|
|
133
|
+
request: FetchRequest.Loose;
|
|
134
|
+
error: AnyFetchRequestError | null;
|
|
135
|
+
clone: () => Loose;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export type FetchRequestConstructor<Schema extends HttpSchema> = new <
|
|
140
|
+
Path extends HttpSchemaPath.NonLiteral<Schema, Method>,
|
|
141
|
+
Method extends HttpSchemaMethod<Schema>,
|
|
142
|
+
>(
|
|
143
|
+
input: FetchInput<Schema, Path, Method>,
|
|
144
|
+
init: FetchRequestInit<Schema, LiteralHttpSchemaPathFromNonLiteral<Schema, Method, Path>, Method>,
|
|
145
|
+
) => FetchRequest<
|
|
146
|
+
LiteralHttpSchemaPathFromNonLiteral<Schema, Method, Path>,
|
|
147
|
+
Method,
|
|
148
|
+
Default<Schema[LiteralHttpSchemaPathFromNonLiteral<Schema, Method, Path>][Method]>
|
|
149
|
+
>;
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export type {
|
|
2
|
+
Fetch,
|
|
3
|
+
FetchFunction,
|
|
4
|
+
FetchClient,
|
|
5
|
+
FetchOptions as FetchClientOptions,
|
|
6
|
+
FetchInput,
|
|
7
|
+
} from './client/types/public';
|
|
8
|
+
|
|
9
|
+
export type { FetchRequestInit, FetchRequest, FetchRequestConstructor, FetchResponse } from './client/types/requests';
|
|
10
|
+
|
|
11
|
+
export { default as FetchResponseError } from './client/errors/FetchResponseError';
|
|
12
|
+
|
|
13
|
+
export { default as createFetch } from './client/factory';
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import {
|
|
2
|
+
HttpFormData,
|
|
3
|
+
HttpFormDataSchema,
|
|
4
|
+
HttpHeaders,
|
|
5
|
+
HttpHeadersSchema,
|
|
6
|
+
HttpSearchParams,
|
|
7
|
+
HttpSearchParamsSchema,
|
|
8
|
+
JSONSerialized,
|
|
9
|
+
JSONValue,
|
|
10
|
+
HttpStatusCode,
|
|
11
|
+
} from '@zimic/http';
|
|
12
|
+
|
|
13
|
+
import { ReplaceBy } from '@/types/utils';
|
|
14
|
+
|
|
15
|
+
/** The body type for HTTP requests and responses. */
|
|
16
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
17
|
+
export type HttpBody = JSONValue | HttpFormData<any> | HttpSearchParams<any> | Blob | ArrayBuffer;
|
|
18
|
+
|
|
19
|
+
export namespace HttpBody {
|
|
20
|
+
/** A loose version of the HTTP body type. JSON values are not strictly typed. */
|
|
21
|
+
export type Loose = ReplaceBy<HttpBody, JSONValue, JSONValue.Loose>;
|
|
22
|
+
|
|
23
|
+
/** Convert a possibly loose HTTP body to be strictly typed. JSON values are serialized to their strict form. */
|
|
24
|
+
export type ConvertToStrict<Type> = Type extends Exclude<HttpBody, JSONValue> ? Type : JSONSerialized<Type>;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* An HTTP headers object with a strictly-typed schema. Fully compatible with the built-in
|
|
29
|
+
* {@link https://developer.mozilla.org/docs/Web/API/Headers `Headers`} class.
|
|
30
|
+
*/
|
|
31
|
+
export type StrictHeaders<Schema extends HttpHeadersSchema = HttpHeadersSchema> = Pick<
|
|
32
|
+
HttpHeaders<Schema>,
|
|
33
|
+
keyof Headers
|
|
34
|
+
>;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* An HTTP search params object with a strictly-typed schema. Fully compatible with the built-in
|
|
38
|
+
* {@link https://developer.mozilla.org/docs/Web/API/URLSearchParams `URLSearchParams`} class.
|
|
39
|
+
*/
|
|
40
|
+
export type StrictURLSearchParams<Schema extends HttpSearchParamsSchema = HttpSearchParamsSchema> = Pick<
|
|
41
|
+
HttpSearchParams<Schema>,
|
|
42
|
+
keyof URLSearchParams
|
|
43
|
+
>;
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* An HTTP form data object with a strictly-typed schema. Fully compatible with the built-in
|
|
47
|
+
* {@link https://developer.mozilla.org/docs/Web/API/FormData `FormData`} class.
|
|
48
|
+
*/
|
|
49
|
+
export type StrictFormData<Schema extends HttpFormDataSchema = HttpFormDataSchema> = Pick<
|
|
50
|
+
HttpFormData<Schema>,
|
|
51
|
+
keyof FormData
|
|
52
|
+
>;
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* An HTTP request with a strictly-typed JSON body. Fully compatible with the built-in
|
|
56
|
+
* {@link https://developer.mozilla.org/docs/Web/API/Request `Request`} class.
|
|
57
|
+
*/
|
|
58
|
+
export interface HttpRequest<
|
|
59
|
+
StrictBody extends HttpBody.Loose = HttpBody,
|
|
60
|
+
StrictHeadersSchema extends HttpHeadersSchema = HttpHeadersSchema,
|
|
61
|
+
> extends Request {
|
|
62
|
+
headers: StrictHeaders<StrictHeadersSchema>;
|
|
63
|
+
text: () => Promise<StrictBody extends string ? StrictBody : string>;
|
|
64
|
+
json: () => Promise<StrictBody extends string | Exclude<HttpBody, JSONValue> ? never : StrictBody>;
|
|
65
|
+
formData: () => Promise<
|
|
66
|
+
StrictBody extends HttpFormData<infer HttpFormDataSchema>
|
|
67
|
+
? StrictFormData<HttpFormDataSchema>
|
|
68
|
+
: StrictBody extends HttpSearchParams<infer HttpSearchParamsSchema>
|
|
69
|
+
? StrictFormData<HttpSearchParamsSchema>
|
|
70
|
+
: FormData
|
|
71
|
+
>;
|
|
72
|
+
clone: () => this;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* An HTTP response with a strictly-typed JSON body and status code. Fully compatible with the built-in
|
|
77
|
+
* {@link https://developer.mozilla.org/docs/Web/API/Response `Response`} class.
|
|
78
|
+
*/
|
|
79
|
+
export interface HttpResponse<
|
|
80
|
+
StrictBody extends HttpBody.Loose = HttpBody,
|
|
81
|
+
StatusCode extends number = number,
|
|
82
|
+
StrictHeadersSchema extends HttpHeadersSchema = HttpHeadersSchema,
|
|
83
|
+
> extends Response {
|
|
84
|
+
ok: StatusCode extends HttpStatusCode.Information | HttpStatusCode.Success | HttpStatusCode.Redirection
|
|
85
|
+
? true
|
|
86
|
+
: false;
|
|
87
|
+
status: StatusCode;
|
|
88
|
+
headers: StrictHeaders<StrictHeadersSchema>;
|
|
89
|
+
text: () => Promise<StrictBody extends string ? StrictBody : string>;
|
|
90
|
+
json: () => Promise<StrictBody extends string | Exclude<HttpBody, JSONValue> ? never : StrictBody>;
|
|
91
|
+
formData: () => Promise<
|
|
92
|
+
StrictBody extends HttpFormData<infer HttpFormDataSchema>
|
|
93
|
+
? StrictFormData<HttpFormDataSchema>
|
|
94
|
+
: StrictBody extends HttpSearchParams<infer HttpSearchParamsSchema>
|
|
95
|
+
? StrictFormData<HttpSearchParamsSchema>
|
|
96
|
+
: FormData
|
|
97
|
+
>;
|
|
98
|
+
clone: () => this;
|
|
99
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
interface String {
|
|
2
|
+
// Using the original method signature style to correctly apply the overload.
|
|
3
|
+
// eslint-disable-next-line @typescript-eslint/method-signature-style
|
|
4
|
+
toLowerCase<Type = string>(): Lowercase<Type>;
|
|
5
|
+
|
|
6
|
+
// Using the original method signature style to correctly apply the overload.
|
|
7
|
+
// eslint-disable-next-line @typescript-eslint/method-signature-style
|
|
8
|
+
toUpperCase<Type = string>(): Uppercase<Type>;
|
|
9
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export type Default<Type, IfEmpty = never> = [undefined | void] extends [Type]
|
|
2
|
+
? IfEmpty
|
|
3
|
+
: Exclude<Type, undefined | void>;
|
|
4
|
+
|
|
5
|
+
export type DefaultNoExclude<Type, IfEmpty = never> = [undefined | void] extends Type ? IfEmpty : Type;
|
|
6
|
+
|
|
7
|
+
export type IfNever<Type, Yes, No = Type> = [Type] extends [never] ? Yes : No;
|
|
8
|
+
|
|
9
|
+
export type PossiblePromise<Type> = Type | PromiseLike<Type>;
|
|
10
|
+
|
|
11
|
+
export type ReplaceBy<Type, Source, Target> = Type extends Source ? Target : Type;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { createCachedDynamicImport } from './imports';
|
|
2
|
+
|
|
3
|
+
export const importBuffer = createCachedDynamicImport(
|
|
4
|
+
/* istanbul ignore next -- @preserve
|
|
5
|
+
* Ignoring as Node.js >=20 provides a global file and the buffer import won't run. */
|
|
6
|
+
() => import('buffer'),
|
|
7
|
+
);
|
|
8
|
+
|
|
9
|
+
let FileSingleton: typeof File | undefined;
|
|
10
|
+
|
|
11
|
+
export async function importFile() {
|
|
12
|
+
/* istanbul ignore if -- @preserve
|
|
13
|
+
* Ignoring as this will only run if this function is called more than once. */
|
|
14
|
+
if (FileSingleton) {
|
|
15
|
+
return FileSingleton;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/* istanbul ignore next -- @preserve
|
|
19
|
+
* Ignoring as Node.js >=20 provides a global File and the import fallback won't run. */
|
|
20
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
21
|
+
FileSingleton = globalThis.File ?? (await importBuffer()).File;
|
|
22
|
+
return FileSingleton;
|
|
23
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/* istanbul ignore next -- @preserve
|
|
2
|
+
* Ignoring as Node.js >=20 provides globals that may cause this dynamic import to not run. */
|
|
3
|
+
export function createCachedDynamicImport<ImportType>(
|
|
4
|
+
importModuleDynamically: () => Promise<ImportType>,
|
|
5
|
+
): () => Promise<ImportType> {
|
|
6
|
+
let cachedImportResult: ImportType | undefined;
|
|
7
|
+
|
|
8
|
+
return async function importModuleDynamicallyWithCache() {
|
|
9
|
+
if (cachedImportResult === undefined) {
|
|
10
|
+
cachedImportResult = await importModuleDynamically();
|
|
11
|
+
}
|
|
12
|
+
return cachedImportResult;
|
|
13
|
+
};
|
|
14
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
export function excludeNonPathParams(url: URL) {
|
|
2
|
+
url.hash = '';
|
|
3
|
+
url.search = '';
|
|
4
|
+
url.username = '';
|
|
5
|
+
url.password = '';
|
|
6
|
+
return url;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function prepareURLForRegex(url: string) {
|
|
10
|
+
const encodedURL = encodeURI(url);
|
|
11
|
+
return encodedURL.replace(/([.()*?+$\\])/g, '\\$1');
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const URL_PATH_PARAM_REGEX = /\/:([^/]+)/g;
|
|
15
|
+
|
|
16
|
+
export function createRegexFromURL(url: string) {
|
|
17
|
+
const urlWithReplacedPathParams = prepareURLForRegex(url)
|
|
18
|
+
.replace(URL_PATH_PARAM_REGEX, '/(?<$1>[^/]+)')
|
|
19
|
+
.replace(/(\/+)$/, '(?:/+)?');
|
|
20
|
+
|
|
21
|
+
return new RegExp(`^${urlWithReplacedPathParams}$`);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function joinURL(...parts: (string | URL)[]) {
|
|
25
|
+
return parts
|
|
26
|
+
.map((part, index) => {
|
|
27
|
+
const isFirstPart = index === 0;
|
|
28
|
+
const isLastPart = index === parts.length - 1;
|
|
29
|
+
|
|
30
|
+
let partAsString = part.toString();
|
|
31
|
+
|
|
32
|
+
if (!isFirstPart) {
|
|
33
|
+
partAsString = partAsString.replace(/^\//, '');
|
|
34
|
+
}
|
|
35
|
+
if (!isLastPart) {
|
|
36
|
+
partAsString = partAsString.replace(/\/$/, '');
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return partAsString;
|
|
40
|
+
})
|
|
41
|
+
.filter((part) => part.length > 0)
|
|
42
|
+
.join('/');
|
|
43
|
+
}
|