@defjs/core 0.0.0-test-0000
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 +5 -0
- package/dist/index.d.ts +254 -0
- package/dist/index.js +562 -0
- package/package.json +25 -0
package/README.md
ADDED
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
export type HttpContextToken<T> = () => T;
|
|
2
|
+
export declare function makeHttpContextToken<T>(defaultValue: () => T): HttpContextToken<T>;
|
|
3
|
+
export type HttpContext = {
|
|
4
|
+
set<T>(token: HttpContextToken<T>, value: T): HttpContext;
|
|
5
|
+
get<T>(token: HttpContextToken<T>): T;
|
|
6
|
+
delete(token: HttpContextToken<unknown>): HttpContext;
|
|
7
|
+
has(token: HttpContextToken<unknown>): boolean;
|
|
8
|
+
keys(): IterableIterator<HttpContextToken<unknown>>;
|
|
9
|
+
};
|
|
10
|
+
export declare function makeHttpContext(): HttpContext;
|
|
11
|
+
export type ValidatorFn<T = unknown> = <I>(value: I | T) => Error | null | undefined;
|
|
12
|
+
export type AsyncValidatorFn<T = unknown> = <I>(value: I | T) => Promise<Error | null | undefined>;
|
|
13
|
+
declare const FIELD: unique symbol;
|
|
14
|
+
declare enum FieldType {
|
|
15
|
+
Json = 1,
|
|
16
|
+
Query = 2,
|
|
17
|
+
Param = 3,
|
|
18
|
+
Header = 4,
|
|
19
|
+
Form = 5,
|
|
20
|
+
Body = 6
|
|
21
|
+
}
|
|
22
|
+
export interface FieldMetadata<T = undefined> {
|
|
23
|
+
alias: Map<FieldType, string | undefined>;
|
|
24
|
+
validators: ValidatorFn<T>[];
|
|
25
|
+
asyncValidator: AsyncValidatorFn<T>[];
|
|
26
|
+
}
|
|
27
|
+
export type Field<T = undefined> = (() => T) & {
|
|
28
|
+
withJson(alias?: string): Field<T>;
|
|
29
|
+
withForm(alias?: string): Field<T>;
|
|
30
|
+
withQuery(alias?: string): Field<T>;
|
|
31
|
+
withParam(alias?: string): Field<T>;
|
|
32
|
+
withHeader(alias?: string): Field<T>;
|
|
33
|
+
withBody(): Field<T>;
|
|
34
|
+
withValidators(...fn: ValidatorFn<T>[]): Field<T>;
|
|
35
|
+
withAsyncValidators(...fn: AsyncValidatorFn<T>[]): Field<T>;
|
|
36
|
+
readonly [FIELD]: FieldMetadata<T>;
|
|
37
|
+
};
|
|
38
|
+
export declare function field(): Field;
|
|
39
|
+
export declare function field<T>(): Field<T | undefined>;
|
|
40
|
+
export declare function field<T>(defaultValue: T): Field<T>;
|
|
41
|
+
export declare function isField(value: unknown): value is Field;
|
|
42
|
+
export declare function isFieldGroup(value: unknown): value is Record<PropertyKey, Field>;
|
|
43
|
+
export type HttpResponseBody = string | ArrayBuffer | Blob | object | null;
|
|
44
|
+
export type HttpResponse<R> = {
|
|
45
|
+
ok: true;
|
|
46
|
+
status: number;
|
|
47
|
+
statusText: string;
|
|
48
|
+
url: string;
|
|
49
|
+
headers: Headers;
|
|
50
|
+
body: R | null;
|
|
51
|
+
} | {
|
|
52
|
+
ok: false;
|
|
53
|
+
error: Error;
|
|
54
|
+
status: number;
|
|
55
|
+
statusText: string;
|
|
56
|
+
url: string;
|
|
57
|
+
headers: Headers;
|
|
58
|
+
body: R | null;
|
|
59
|
+
};
|
|
60
|
+
export type MakeResponseOptions<R> = {
|
|
61
|
+
status?: number;
|
|
62
|
+
statusText?: string;
|
|
63
|
+
url?: string;
|
|
64
|
+
headers?: Headers;
|
|
65
|
+
body?: R | null;
|
|
66
|
+
error?: Error;
|
|
67
|
+
};
|
|
68
|
+
export declare function makeResponse<R>(options: MakeResponseOptions<R>): HttpResponse<R>;
|
|
69
|
+
export type InterceptorFn = (req: HttpRequest, next: HttpHandler) => Promise<HttpResponse<unknown>>;
|
|
70
|
+
export type HttpResponseType = "arraybuffer" | "blob" | "json" | "text";
|
|
71
|
+
export interface HttpProgressEvent {
|
|
72
|
+
/** 表示 Progress 所关联的资源是否具有可以计算的长度。否则,Progress.total 属性将是一个无意义的值 */
|
|
73
|
+
readonly lengthComputable: boolean;
|
|
74
|
+
/** 表示底层的进程已经执行的工作量 */
|
|
75
|
+
readonly loaded: number;
|
|
76
|
+
/** 正在处理或者传输的数据的总大小. 如果 Progress.lengthComputable 属性是 false,这个值是没有意义的并且应该被忽略 */
|
|
77
|
+
readonly total: number;
|
|
78
|
+
}
|
|
79
|
+
export type HttpProgressFn = (value: HttpProgressEvent) => void;
|
|
80
|
+
export type TransformRequestFn = (request: HttpRequest) => HttpRequest["body"];
|
|
81
|
+
export type TransformResponseFn<Output = unknown> = (response: HttpResponse<unknown>) => Output;
|
|
82
|
+
export interface HttpRequest {
|
|
83
|
+
host?: string;
|
|
84
|
+
method: string;
|
|
85
|
+
endpoint: string;
|
|
86
|
+
body?: Blob | ArrayBuffer | FormData | object | string | number | boolean | null;
|
|
87
|
+
headers: Headers;
|
|
88
|
+
queryParams: URLSearchParams;
|
|
89
|
+
/** 默认:json */
|
|
90
|
+
responseType: HttpResponseType;
|
|
91
|
+
context: HttpContext;
|
|
92
|
+
timeout: number;
|
|
93
|
+
withCredentials?: boolean;
|
|
94
|
+
/** when use fetch handler, no upload progress */
|
|
95
|
+
uploadProgress?: HttpProgressFn;
|
|
96
|
+
downloadProgress?: HttpProgressFn;
|
|
97
|
+
/** 默认:body */
|
|
98
|
+
observe: "body" | "stream" | "response";
|
|
99
|
+
abort: AbortSignal;
|
|
100
|
+
}
|
|
101
|
+
export type RequestInputValue<T> = T extends Field<infer V> ? V : T extends {
|
|
102
|
+
[K in keyof T]: Field<any>;
|
|
103
|
+
} ? {
|
|
104
|
+
[K in keyof T]: RequestInputValue<T[K]>;
|
|
105
|
+
} : never;
|
|
106
|
+
export type RequestOptions<Output = unknown, Input extends Field<any> | Record<PropertyKey, Field<any>> | undefined = undefined> = {
|
|
107
|
+
method: string;
|
|
108
|
+
endpoint: string;
|
|
109
|
+
input?: Input;
|
|
110
|
+
interceptors?: InterceptorFn[];
|
|
111
|
+
responseType?: HttpResponseType;
|
|
112
|
+
context?: HttpContext;
|
|
113
|
+
observe?: "body" | "response";
|
|
114
|
+
validators?: (ValidatorFn<RequestInputValue<Input>> | AsyncValidatorFn<RequestInputValue<Input>>)[];
|
|
115
|
+
transformRequest?: TransformRequestFn;
|
|
116
|
+
transformResponse?: TransformResponseFn<Output>;
|
|
117
|
+
};
|
|
118
|
+
declare const REQUEST_REF: unique symbol;
|
|
119
|
+
export type RequestRef<_Output = undefined, _Input = undefined> = {
|
|
120
|
+
readonly [REQUEST_REF]: RequestOptions;
|
|
121
|
+
};
|
|
122
|
+
export type MakeRequestFn<Output = undefined, Input = undefined> = () => RequestRef<Output, Input>;
|
|
123
|
+
/** Observe: Body */
|
|
124
|
+
export declare function defineRequest<Output = unknown, Input extends Field<any> | Record<PropertyKey, Field<any>> | undefined = undefined>(options: {
|
|
125
|
+
method: string;
|
|
126
|
+
endpoint: string;
|
|
127
|
+
input?: Input;
|
|
128
|
+
interceptors?: InterceptorFn[];
|
|
129
|
+
responseType?: "json";
|
|
130
|
+
observe?: "body";
|
|
131
|
+
context?: HttpContext;
|
|
132
|
+
validators?: (ValidatorFn<RequestInputValue<Input>> | AsyncValidatorFn<RequestInputValue<Input>>)[];
|
|
133
|
+
transformRequest?: TransformRequestFn;
|
|
134
|
+
transformResponse?: TransformResponseFn<Output>;
|
|
135
|
+
}): MakeRequestFn<Output, Input>;
|
|
136
|
+
export declare function defineRequest<Input extends Field<any> | Record<PropertyKey, Field<any>> | undefined = undefined>(options: {
|
|
137
|
+
method: string;
|
|
138
|
+
endpoint: string;
|
|
139
|
+
input?: Input;
|
|
140
|
+
interceptors?: InterceptorFn[];
|
|
141
|
+
responseType: "text";
|
|
142
|
+
observe?: "body";
|
|
143
|
+
context?: HttpContext;
|
|
144
|
+
validators?: (ValidatorFn<RequestInputValue<Input>> | AsyncValidatorFn<RequestInputValue<Input>>)[];
|
|
145
|
+
transformRequest?: TransformRequestFn;
|
|
146
|
+
transformResponse?: TransformResponseFn;
|
|
147
|
+
}): MakeRequestFn<string, Input>;
|
|
148
|
+
export declare function defineRequest<Input extends Field<any> | Record<PropertyKey, Field<any>> | undefined = undefined>(options: {
|
|
149
|
+
method: string;
|
|
150
|
+
endpoint: string;
|
|
151
|
+
input?: Input;
|
|
152
|
+
interceptors?: InterceptorFn[];
|
|
153
|
+
responseType: "blob";
|
|
154
|
+
observe?: "body";
|
|
155
|
+
context?: HttpContext;
|
|
156
|
+
validators?: (ValidatorFn<RequestInputValue<Input>> | AsyncValidatorFn<RequestInputValue<Input>>)[];
|
|
157
|
+
transformRequest?: TransformRequestFn;
|
|
158
|
+
transformResponse?: TransformResponseFn;
|
|
159
|
+
}): MakeRequestFn<Blob, Input>;
|
|
160
|
+
export declare function defineRequest<Input extends Field<any> | Record<PropertyKey, Field<any>> | undefined = undefined>(options: {
|
|
161
|
+
method: string;
|
|
162
|
+
endpoint: string;
|
|
163
|
+
input?: Input;
|
|
164
|
+
interceptors?: InterceptorFn[];
|
|
165
|
+
responseType: "arraybuffer";
|
|
166
|
+
observe?: "body";
|
|
167
|
+
context?: HttpContext;
|
|
168
|
+
validators?: (ValidatorFn<RequestInputValue<Input>> | AsyncValidatorFn<RequestInputValue<Input>>)[];
|
|
169
|
+
transformRequest?: TransformRequestFn;
|
|
170
|
+
transformResponse?: TransformResponseFn;
|
|
171
|
+
}): MakeRequestFn<ArrayBuffer, Input>;
|
|
172
|
+
/** Observe: Response */
|
|
173
|
+
export declare function defineRequest<Output = unknown, Input extends Field<any> | Record<PropertyKey, Field<any>> | undefined = undefined>(options: {
|
|
174
|
+
method: string;
|
|
175
|
+
endpoint: string;
|
|
176
|
+
input?: Input;
|
|
177
|
+
interceptors?: InterceptorFn[];
|
|
178
|
+
responseType?: "json";
|
|
179
|
+
observe: "response";
|
|
180
|
+
context?: HttpContext;
|
|
181
|
+
validators?: (ValidatorFn<RequestInputValue<Input>> | AsyncValidatorFn<RequestInputValue<Input>>)[];
|
|
182
|
+
transformRequest?: TransformRequestFn;
|
|
183
|
+
transformResponse?: TransformResponseFn<Output>;
|
|
184
|
+
}): MakeRequestFn<HttpResponse<Output>, Input>;
|
|
185
|
+
export declare function defineRequest<Input extends Field<any> | Record<PropertyKey, Field<any>> | undefined = undefined>(options: {
|
|
186
|
+
method: string;
|
|
187
|
+
endpoint: string;
|
|
188
|
+
input?: Input;
|
|
189
|
+
interceptors?: InterceptorFn[];
|
|
190
|
+
responseType?: "text";
|
|
191
|
+
observe: "response";
|
|
192
|
+
context?: HttpContext;
|
|
193
|
+
validators?: (ValidatorFn<RequestInputValue<Input>> | AsyncValidatorFn<RequestInputValue<Input>>)[];
|
|
194
|
+
transformRequest?: TransformRequestFn;
|
|
195
|
+
transformResponse?: TransformResponseFn;
|
|
196
|
+
}): MakeRequestFn<HttpResponse<string>, Input>;
|
|
197
|
+
export declare function defineRequest<Input extends Field<any> | Record<PropertyKey, Field<any>> | undefined = undefined>(options: {
|
|
198
|
+
method: string;
|
|
199
|
+
endpoint: string;
|
|
200
|
+
input?: Input;
|
|
201
|
+
interceptors?: InterceptorFn[];
|
|
202
|
+
responseType: "blob";
|
|
203
|
+
observe: "response";
|
|
204
|
+
context?: HttpContext;
|
|
205
|
+
validators?: (ValidatorFn<RequestInputValue<Input>> | AsyncValidatorFn<RequestInputValue<Input>>)[];
|
|
206
|
+
transformRequest?: TransformRequestFn;
|
|
207
|
+
transformResponse?: TransformResponseFn;
|
|
208
|
+
}): MakeRequestFn<HttpResponse<Blob>, Input>;
|
|
209
|
+
export declare function defineRequest<Input extends Field<any> | Record<PropertyKey, Field<any>> | undefined = undefined>(options: {
|
|
210
|
+
method: string;
|
|
211
|
+
endpoint: string;
|
|
212
|
+
input?: Input;
|
|
213
|
+
interceptors?: InterceptorFn[];
|
|
214
|
+
responseType?: "arraybuffer";
|
|
215
|
+
observe: "response";
|
|
216
|
+
context?: HttpContext;
|
|
217
|
+
validators?: (ValidatorFn<RequestInputValue<Input>> | AsyncValidatorFn<RequestInputValue<Input>>)[];
|
|
218
|
+
transformRequest?: TransformRequestFn;
|
|
219
|
+
transformResponse?: TransformResponseFn;
|
|
220
|
+
}): MakeRequestFn<HttpResponse<ArrayBuffer>, Input>;
|
|
221
|
+
export declare function isRequestRef(value: unknown): value is RequestRef;
|
|
222
|
+
export type DoRequestOptions = {
|
|
223
|
+
uploadProgress?: HttpProgressFn;
|
|
224
|
+
downloadProgress?: HttpProgressFn;
|
|
225
|
+
abort?: AbortSignal;
|
|
226
|
+
handler?: HttpHandler;
|
|
227
|
+
client?: ClientOptions;
|
|
228
|
+
timeout?: number;
|
|
229
|
+
};
|
|
230
|
+
export type RequestRefOutput<T> = T extends RequestRef<infer O> ? O : undefined;
|
|
231
|
+
export type RequestRefInput<T> = T extends RequestRef<unknown, infer I> ? RequestInputValue<I> : never;
|
|
232
|
+
export type RequestRefWithInput<T> = T extends RequestRef<unknown, infer Input> ? (Input extends undefined ? false : true) : false;
|
|
233
|
+
export declare function doRequest<Ref extends RequestRef>(ref: RequestRefWithInput<Ref> extends true ? never : Ref, options?: DoRequestOptions): Promise<RequestRefOutput<Ref>>;
|
|
234
|
+
export declare function doRequest<Ref extends RequestRef>(ref: RequestRefWithInput<Ref> extends true ? Ref : never, input: RequestRefInput<Ref>, options?: DoRequestOptions): Promise<RequestRefOutput<Ref>>;
|
|
235
|
+
export type HttpHandler = (req: HttpRequest) => Promise<HttpResponse<unknown>>;
|
|
236
|
+
export declare const fetchHandler: HttpHandler;
|
|
237
|
+
export interface ClientOptions {
|
|
238
|
+
host: string;
|
|
239
|
+
interceptors?: InterceptorFn[];
|
|
240
|
+
handler?: HttpHandler;
|
|
241
|
+
}
|
|
242
|
+
export interface ClientConfig {
|
|
243
|
+
host: string;
|
|
244
|
+
handler: HttpHandler;
|
|
245
|
+
interceptors?: InterceptorFn[];
|
|
246
|
+
}
|
|
247
|
+
declare const CLIENT: unique symbol;
|
|
248
|
+
export type Client = {} & {
|
|
249
|
+
readonly [CLIENT]: ClientConfig;
|
|
250
|
+
};
|
|
251
|
+
export declare function isClient(value: any): value is Client;
|
|
252
|
+
export declare function createClient(options: ClientOptions): Client;
|
|
253
|
+
|
|
254
|
+
export {};
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,562 @@
|
|
|
1
|
+
// src/context.ts
|
|
2
|
+
function makeHttpContextToken(defaultValue) {
|
|
3
|
+
return defaultValue;
|
|
4
|
+
}
|
|
5
|
+
function makeHttpContext() {
|
|
6
|
+
const ctx = new Map;
|
|
7
|
+
return {
|
|
8
|
+
set(token, value) {
|
|
9
|
+
ctx.set(token, value);
|
|
10
|
+
return this;
|
|
11
|
+
},
|
|
12
|
+
get(token) {
|
|
13
|
+
if (!ctx.has(token)) {
|
|
14
|
+
ctx.set(token, token());
|
|
15
|
+
}
|
|
16
|
+
return ctx.get(token);
|
|
17
|
+
},
|
|
18
|
+
delete(token) {
|
|
19
|
+
ctx.delete(token);
|
|
20
|
+
return this;
|
|
21
|
+
},
|
|
22
|
+
has(token) {
|
|
23
|
+
return ctx.has(token);
|
|
24
|
+
},
|
|
25
|
+
keys() {
|
|
26
|
+
return ctx.keys();
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// src/field.ts
|
|
32
|
+
function field(defaultValue) {
|
|
33
|
+
const meta = {
|
|
34
|
+
alias: new Map,
|
|
35
|
+
validators: [],
|
|
36
|
+
asyncValidator: []
|
|
37
|
+
};
|
|
38
|
+
const getter = () => defaultValue;
|
|
39
|
+
Object.defineProperty(getter, FIELD, {
|
|
40
|
+
value: meta
|
|
41
|
+
});
|
|
42
|
+
getter.withJson = (alias) => {
|
|
43
|
+
meta.alias.set(1 /* Json */, alias);
|
|
44
|
+
return getter;
|
|
45
|
+
};
|
|
46
|
+
getter.withForm = (alias) => {
|
|
47
|
+
meta.alias.set(5 /* Form */, alias);
|
|
48
|
+
return getter;
|
|
49
|
+
};
|
|
50
|
+
getter.withQuery = (alias) => {
|
|
51
|
+
meta.alias.set(2 /* Query */, alias);
|
|
52
|
+
return getter;
|
|
53
|
+
};
|
|
54
|
+
getter.withParam = (alias) => {
|
|
55
|
+
meta.alias.set(3 /* Param */, alias);
|
|
56
|
+
return getter;
|
|
57
|
+
};
|
|
58
|
+
getter.withHeader = (alias) => {
|
|
59
|
+
meta.alias.set(4 /* Header */, alias);
|
|
60
|
+
return getter;
|
|
61
|
+
};
|
|
62
|
+
getter.withBody = () => {
|
|
63
|
+
meta.alias.set(6 /* Body */, undefined);
|
|
64
|
+
return getter;
|
|
65
|
+
};
|
|
66
|
+
getter.withValidators = (...fn) => {
|
|
67
|
+
meta.validators = fn;
|
|
68
|
+
return getter;
|
|
69
|
+
};
|
|
70
|
+
getter.withAsyncValidators = (...fn) => {
|
|
71
|
+
meta.asyncValidator = fn;
|
|
72
|
+
return getter;
|
|
73
|
+
};
|
|
74
|
+
return getter;
|
|
75
|
+
}
|
|
76
|
+
function getFieldMetadata(field2) {
|
|
77
|
+
return field2[FIELD];
|
|
78
|
+
}
|
|
79
|
+
function isField(value) {
|
|
80
|
+
return !!value && typeof value === "function" && FIELD in value;
|
|
81
|
+
}
|
|
82
|
+
function isFieldGroup(value) {
|
|
83
|
+
return !!value && typeof value === "object" && Object.entries(value).every(([, v]) => isField(v));
|
|
84
|
+
}
|
|
85
|
+
var FIELD = Symbol("field");
|
|
86
|
+
|
|
87
|
+
// src/handler/handler.ts
|
|
88
|
+
var globalHttpHandler = undefined;
|
|
89
|
+
var getGlobalHttpHandler = () => globalHttpHandler;
|
|
90
|
+
|
|
91
|
+
// src/interceptor.ts
|
|
92
|
+
function makeInterceptorChain(interceptors) {
|
|
93
|
+
return interceptors.reduceRight((fn, interceptor) => {
|
|
94
|
+
return (initReq, finalHandlerFn) => interceptor(initReq, (req) => fn(req, finalHandlerFn));
|
|
95
|
+
}, (req, fn) => fn(req));
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// src/request.ts
|
|
99
|
+
function serializeBody(body) {
|
|
100
|
+
switch (true) {
|
|
101
|
+
case body instanceof FormData:
|
|
102
|
+
case body instanceof Blob:
|
|
103
|
+
case body instanceof ArrayBuffer:
|
|
104
|
+
case typeof body === "string":
|
|
105
|
+
return body;
|
|
106
|
+
case typeof body === "object":
|
|
107
|
+
case typeof body === "boolean":
|
|
108
|
+
case typeof body === "number":
|
|
109
|
+
case Array.isArray(body):
|
|
110
|
+
return JSON.stringify(body);
|
|
111
|
+
default:
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
function detectContentTypeHeader(body) {
|
|
116
|
+
switch (true) {
|
|
117
|
+
case body instanceof FormData:
|
|
118
|
+
return "multipart/form-data";
|
|
119
|
+
case body instanceof ArrayBuffer:
|
|
120
|
+
return "application/octet-stream";
|
|
121
|
+
case body instanceof Blob: {
|
|
122
|
+
return body.type || "application/octet-stream";
|
|
123
|
+
}
|
|
124
|
+
case body instanceof URLSearchParams: {
|
|
125
|
+
return "application/x-www-form-urlencoded;charset=UTF-8";
|
|
126
|
+
}
|
|
127
|
+
case typeof body === "string":
|
|
128
|
+
return "text/plain";
|
|
129
|
+
case (typeof body === "object" && body !== null):
|
|
130
|
+
case typeof body === "number":
|
|
131
|
+
case typeof body === "boolean":
|
|
132
|
+
return "application/json";
|
|
133
|
+
default:
|
|
134
|
+
return null;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
function defineRequest(options) {
|
|
138
|
+
return () => {
|
|
139
|
+
const opts = {
|
|
140
|
+
...options
|
|
141
|
+
};
|
|
142
|
+
const ref = {};
|
|
143
|
+
Object.defineProperty(ref, REQUEST_REF, {
|
|
144
|
+
value: opts
|
|
145
|
+
});
|
|
146
|
+
return ref;
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
function isRequestRef(value) {
|
|
150
|
+
return !!value && typeof value === "object" && REQUEST_REF in value;
|
|
151
|
+
}
|
|
152
|
+
function getRequestRefOptions(value) {
|
|
153
|
+
if (!isRequestRef(value)) {
|
|
154
|
+
throw new Error("no request ref");
|
|
155
|
+
}
|
|
156
|
+
return value[REQUEST_REF];
|
|
157
|
+
}
|
|
158
|
+
function throwAliaNotSet() {
|
|
159
|
+
throw new Error("alias not set");
|
|
160
|
+
}
|
|
161
|
+
function fillUrl(endpoint, params) {
|
|
162
|
+
for (const [key, value] of params) {
|
|
163
|
+
endpoint = endpoint.replace(`:${key}`, value.toString());
|
|
164
|
+
}
|
|
165
|
+
return endpoint;
|
|
166
|
+
}
|
|
167
|
+
async function fillRequestFromField(request, fieldOrFieldGroup, input) {
|
|
168
|
+
async function doValid(validators, value) {
|
|
169
|
+
for (const validator of validators) {
|
|
170
|
+
const err = await validator(value);
|
|
171
|
+
if (err) {
|
|
172
|
+
return err;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
function inputIsObject(input2) {
|
|
178
|
+
return typeof input2 === "object" && input2 !== null;
|
|
179
|
+
}
|
|
180
|
+
if (isField(fieldOrFieldGroup)) {
|
|
181
|
+
const param = new Map;
|
|
182
|
+
const queryParams = new URLSearchParams;
|
|
183
|
+
const headers = new Headers;
|
|
184
|
+
let body = undefined;
|
|
185
|
+
const fieldValue = input ?? fieldOrFieldGroup();
|
|
186
|
+
const meta = getFieldMetadata(fieldOrFieldGroup);
|
|
187
|
+
const err = await doValid([...meta.validators, ...meta.asyncValidator], fieldValue);
|
|
188
|
+
if (err) {
|
|
189
|
+
throw err;
|
|
190
|
+
}
|
|
191
|
+
for (const [type, aliasName] of meta.alias) {
|
|
192
|
+
switch (type) {
|
|
193
|
+
case 1 /* Json */:
|
|
194
|
+
body = JSON.stringify(fieldValue);
|
|
195
|
+
break;
|
|
196
|
+
case 2 /* Query */: {
|
|
197
|
+
if (!aliasName) {
|
|
198
|
+
throwAliaNotSet();
|
|
199
|
+
}
|
|
200
|
+
queryParams.append(aliasName, String(fieldValue));
|
|
201
|
+
break;
|
|
202
|
+
}
|
|
203
|
+
case 3 /* Param */: {
|
|
204
|
+
if (!aliasName) {
|
|
205
|
+
throwAliaNotSet();
|
|
206
|
+
}
|
|
207
|
+
param.set(aliasName, String(fieldValue));
|
|
208
|
+
break;
|
|
209
|
+
}
|
|
210
|
+
case 4 /* Header */: {
|
|
211
|
+
if (!aliasName) {
|
|
212
|
+
throwAliaNotSet();
|
|
213
|
+
}
|
|
214
|
+
headers.set(aliasName, String(fieldValue));
|
|
215
|
+
break;
|
|
216
|
+
}
|
|
217
|
+
case 5 /* Form */: {
|
|
218
|
+
if (!aliasName) {
|
|
219
|
+
throwAliaNotSet();
|
|
220
|
+
}
|
|
221
|
+
const form = new FormData;
|
|
222
|
+
form.append(aliasName, String(fieldValue));
|
|
223
|
+
body = form;
|
|
224
|
+
break;
|
|
225
|
+
}
|
|
226
|
+
case 6 /* Body */:
|
|
227
|
+
body = fieldValue;
|
|
228
|
+
break;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
request.endpoint = fillUrl(request.endpoint, param);
|
|
232
|
+
request.queryParams = queryParams;
|
|
233
|
+
request.headers = headers;
|
|
234
|
+
request.body = body;
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
if (isFieldGroup(fieldOrFieldGroup) && inputIsObject(input)) {
|
|
238
|
+
const param = new Map;
|
|
239
|
+
const queryParams = new URLSearchParams;
|
|
240
|
+
const headers = new Headers;
|
|
241
|
+
const formData = new FormData;
|
|
242
|
+
const json = {};
|
|
243
|
+
let body = undefined;
|
|
244
|
+
for (const [propertyKey, field3] of Object.entries(fieldOrFieldGroup)) {
|
|
245
|
+
const defaultValue = field3();
|
|
246
|
+
const meta = getFieldMetadata(field3);
|
|
247
|
+
for (const [type, aliasName] of meta.alias) {
|
|
248
|
+
const valueKey = aliasName || propertyKey;
|
|
249
|
+
const value = input[valueKey] || defaultValue;
|
|
250
|
+
const err = await doValid([...meta.validators, ...meta.asyncValidator], value);
|
|
251
|
+
if (err) {
|
|
252
|
+
throw err;
|
|
253
|
+
}
|
|
254
|
+
switch (type) {
|
|
255
|
+
case 1 /* Json */:
|
|
256
|
+
Object.defineProperty(json, valueKey, {
|
|
257
|
+
value,
|
|
258
|
+
writable: true
|
|
259
|
+
});
|
|
260
|
+
break;
|
|
261
|
+
case 2 /* Query */:
|
|
262
|
+
queryParams.append(valueKey, value);
|
|
263
|
+
break;
|
|
264
|
+
case 3 /* Param */:
|
|
265
|
+
param.set(valueKey, value);
|
|
266
|
+
break;
|
|
267
|
+
case 4 /* Header */:
|
|
268
|
+
headers.set(valueKey, value);
|
|
269
|
+
break;
|
|
270
|
+
case 5 /* Form */:
|
|
271
|
+
formData.append(valueKey, value);
|
|
272
|
+
break;
|
|
273
|
+
case 6 /* Body */:
|
|
274
|
+
body = value;
|
|
275
|
+
break;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
request.endpoint = fillUrl(request.endpoint, param);
|
|
279
|
+
request.queryParams = queryParams;
|
|
280
|
+
request.headers = headers;
|
|
281
|
+
if (body) {
|
|
282
|
+
request.body = body;
|
|
283
|
+
} else if (Array.from(formData.keys()).length > 0) {
|
|
284
|
+
request.body = formData;
|
|
285
|
+
} else if (Object.keys(json).length) {
|
|
286
|
+
request.body = JSON.stringify(json);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
throw new Error("unsupported field type");
|
|
292
|
+
}
|
|
293
|
+
async function doRequest(...args) {
|
|
294
|
+
const argsLength = args.length;
|
|
295
|
+
let ref;
|
|
296
|
+
let input;
|
|
297
|
+
let options = {};
|
|
298
|
+
switch (argsLength) {
|
|
299
|
+
case 2: {
|
|
300
|
+
ref = args[0];
|
|
301
|
+
options = args[1];
|
|
302
|
+
break;
|
|
303
|
+
}
|
|
304
|
+
case 3: {
|
|
305
|
+
ref = args[0];
|
|
306
|
+
input = args[1];
|
|
307
|
+
options = args[2];
|
|
308
|
+
break;
|
|
309
|
+
}
|
|
310
|
+
default: {
|
|
311
|
+
throw new Error("invalid arguments");
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
const requestOptions = getRequestRefOptions(ref);
|
|
315
|
+
const requiredInput = !!requestOptions.input;
|
|
316
|
+
if (requiredInput && !input) {
|
|
317
|
+
throw new Error("input is required");
|
|
318
|
+
}
|
|
319
|
+
let abortController;
|
|
320
|
+
let abortSignal;
|
|
321
|
+
if (options.abort) {
|
|
322
|
+
abortSignal = options.abort;
|
|
323
|
+
} else {
|
|
324
|
+
abortController = new AbortController;
|
|
325
|
+
abortSignal = abortController.signal;
|
|
326
|
+
}
|
|
327
|
+
const clientOptions = getClientConfig(options.client);
|
|
328
|
+
const req = {
|
|
329
|
+
host: clientOptions?.host || "",
|
|
330
|
+
method: requestOptions.method,
|
|
331
|
+
endpoint: requestOptions.endpoint,
|
|
332
|
+
queryParams: new URLSearchParams,
|
|
333
|
+
headers: new Headers,
|
|
334
|
+
body: undefined,
|
|
335
|
+
responseType: requestOptions.responseType ?? "json",
|
|
336
|
+
context: requestOptions.context || makeHttpContext(),
|
|
337
|
+
observe: requestOptions.observe ?? "body",
|
|
338
|
+
uploadProgress: options.uploadProgress,
|
|
339
|
+
downloadProgress: options.downloadProgress,
|
|
340
|
+
timeout: options.timeout || 0,
|
|
341
|
+
abort: abortSignal
|
|
342
|
+
};
|
|
343
|
+
let handler2;
|
|
344
|
+
if (options.handler) {
|
|
345
|
+
handler2 = options.handler;
|
|
346
|
+
} else if (clientOptions?.handler) {
|
|
347
|
+
handler2 = clientOptions.handler;
|
|
348
|
+
} else {
|
|
349
|
+
handler2 = getGlobalHttpHandler();
|
|
350
|
+
}
|
|
351
|
+
if (!handler2) {
|
|
352
|
+
throw new Error("no handler");
|
|
353
|
+
}
|
|
354
|
+
await fillRequestFromField(req, requestOptions.input, input);
|
|
355
|
+
if (typeof requestOptions.transformRequest === "function") {
|
|
356
|
+
req.body = requestOptions.transformRequest(req);
|
|
357
|
+
}
|
|
358
|
+
const chain = makeInterceptorChain([...clientOptions?.interceptors || [], ...requestOptions?.interceptors || []]);
|
|
359
|
+
if (req.timeout > 0 && abortController) {
|
|
360
|
+
setTimeout(() => {
|
|
361
|
+
if (!req.abort.aborted) {
|
|
362
|
+
abortController.abort("timeout");
|
|
363
|
+
}
|
|
364
|
+
}, req.timeout);
|
|
365
|
+
}
|
|
366
|
+
const res = await chain(req, handler2);
|
|
367
|
+
if (typeof requestOptions.transformResponse === "function") {
|
|
368
|
+
res.body = requestOptions.transformResponse(res);
|
|
369
|
+
}
|
|
370
|
+
return res;
|
|
371
|
+
}
|
|
372
|
+
var REQUEST_REF = Symbol("RequestRef");
|
|
373
|
+
|
|
374
|
+
// src/response.ts
|
|
375
|
+
function makeResponse(options) {
|
|
376
|
+
const status = options.status ?? 0;
|
|
377
|
+
const statusText = options.statusText ?? "";
|
|
378
|
+
const ok = status >= 200 && status < 300;
|
|
379
|
+
const url = options.url ?? "";
|
|
380
|
+
const headers = options.headers ?? new Headers;
|
|
381
|
+
const body = options.body ?? null;
|
|
382
|
+
if (!ok) {
|
|
383
|
+
const error = options.error || new Error(`Http failure response for ${options.url}: ${status} ${statusText}`);
|
|
384
|
+
return {
|
|
385
|
+
ok: false,
|
|
386
|
+
error,
|
|
387
|
+
status,
|
|
388
|
+
statusText,
|
|
389
|
+
url,
|
|
390
|
+
headers,
|
|
391
|
+
body
|
|
392
|
+
};
|
|
393
|
+
}
|
|
394
|
+
return {
|
|
395
|
+
ok: true,
|
|
396
|
+
status,
|
|
397
|
+
statusText,
|
|
398
|
+
url,
|
|
399
|
+
headers,
|
|
400
|
+
body
|
|
401
|
+
};
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
// src/handler/fetch/fetch.ts
|
|
405
|
+
function createRequest(request2) {
|
|
406
|
+
const url = new URL(request2.endpoint, request2.host);
|
|
407
|
+
url.search = request2.queryParams.toString();
|
|
408
|
+
if (!request2.headers.has("Content-Type")) {
|
|
409
|
+
const detectedType = detectContentTypeHeader(request2.body);
|
|
410
|
+
if (detectedType) {
|
|
411
|
+
request2.headers.set("Content-Type", detectedType);
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
if (!request2.headers.has("Accept")) {
|
|
415
|
+
request2.headers.set("Accept", "application/json, text/plain, */*");
|
|
416
|
+
}
|
|
417
|
+
const credentials = request2.withCredentials ? "include" : undefined;
|
|
418
|
+
const fetchConfig = request2.context.get(FETCH_CONFIG_KEY);
|
|
419
|
+
return new Request(url, {
|
|
420
|
+
...fetchConfig,
|
|
421
|
+
headers: request2.headers,
|
|
422
|
+
method: request2.method,
|
|
423
|
+
body: serializeBody(request2.body),
|
|
424
|
+
signal: request2.abort,
|
|
425
|
+
credentials
|
|
426
|
+
});
|
|
427
|
+
}
|
|
428
|
+
function concatChunks(chunks, totalLength) {
|
|
429
|
+
const chunksAll = new Uint8Array(totalLength);
|
|
430
|
+
let position = 0;
|
|
431
|
+
for (const chunk of chunks) {
|
|
432
|
+
chunksAll.set(chunk, position);
|
|
433
|
+
position += chunk.length;
|
|
434
|
+
}
|
|
435
|
+
return chunksAll;
|
|
436
|
+
}
|
|
437
|
+
function parseBody(request2, response2, content) {
|
|
438
|
+
const responseType = request2.responseType;
|
|
439
|
+
if (!responseType) {
|
|
440
|
+
return null;
|
|
441
|
+
}
|
|
442
|
+
switch (responseType) {
|
|
443
|
+
case "json": {
|
|
444
|
+
const text = new TextDecoder().decode(content);
|
|
445
|
+
if (text === "") {
|
|
446
|
+
return null;
|
|
447
|
+
}
|
|
448
|
+
return JSON.parse(text);
|
|
449
|
+
}
|
|
450
|
+
case "text":
|
|
451
|
+
return new TextDecoder().decode(content);
|
|
452
|
+
case "blob": {
|
|
453
|
+
const contentType = response2.headers.get("Content-Type") || undefined;
|
|
454
|
+
return new Blob([content], { type: contentType });
|
|
455
|
+
}
|
|
456
|
+
case "arraybuffer":
|
|
457
|
+
return content.buffer;
|
|
458
|
+
default:
|
|
459
|
+
return null;
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
function getContentLength(headers) {
|
|
463
|
+
const value = headers.get("Content-Length");
|
|
464
|
+
if (!value) {
|
|
465
|
+
return 0;
|
|
466
|
+
}
|
|
467
|
+
const num = Number(value);
|
|
468
|
+
if (isNaN(num)) {
|
|
469
|
+
return 0;
|
|
470
|
+
}
|
|
471
|
+
return num;
|
|
472
|
+
}
|
|
473
|
+
async function _request(httpRequest) {
|
|
474
|
+
const request2 = createRequest(httpRequest);
|
|
475
|
+
const downloadProgress = httpRequest.downloadProgress;
|
|
476
|
+
let response2;
|
|
477
|
+
try {
|
|
478
|
+
response2 = await (globalThis || window).fetch(request2);
|
|
479
|
+
} catch (error) {
|
|
480
|
+
throw makeResponse({
|
|
481
|
+
error: error instanceof Error ? error : new Error("Unknown Error"),
|
|
482
|
+
url: request2.url
|
|
483
|
+
});
|
|
484
|
+
}
|
|
485
|
+
const { headers, status, statusText, url } = response2;
|
|
486
|
+
const contentLength = getContentLength(headers);
|
|
487
|
+
let body = null;
|
|
488
|
+
if (response2.body) {
|
|
489
|
+
const chunks = [];
|
|
490
|
+
const reader = response2.body.getReader();
|
|
491
|
+
let receivedLength = 0;
|
|
492
|
+
while (true) {
|
|
493
|
+
const { done, value } = await reader.read();
|
|
494
|
+
if (done) {
|
|
495
|
+
break;
|
|
496
|
+
}
|
|
497
|
+
chunks.push(value);
|
|
498
|
+
receivedLength += value.length;
|
|
499
|
+
downloadProgress?.({
|
|
500
|
+
lengthComputable: contentLength > 0,
|
|
501
|
+
loaded: receivedLength,
|
|
502
|
+
total: contentLength
|
|
503
|
+
});
|
|
504
|
+
const chunksAll = concatChunks(chunks, receivedLength);
|
|
505
|
+
try {
|
|
506
|
+
body = parseBody(httpRequest, response2, chunksAll);
|
|
507
|
+
} catch (error) {
|
|
508
|
+
throw makeResponse({
|
|
509
|
+
error: error instanceof Error ? error : new Error("Unknown Error"),
|
|
510
|
+
url,
|
|
511
|
+
status,
|
|
512
|
+
statusText,
|
|
513
|
+
headers
|
|
514
|
+
});
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
return makeResponse({
|
|
519
|
+
url,
|
|
520
|
+
status,
|
|
521
|
+
statusText,
|
|
522
|
+
headers,
|
|
523
|
+
body
|
|
524
|
+
});
|
|
525
|
+
}
|
|
526
|
+
var FETCH_CONFIG_KEY = makeHttpContextToken(() => ({}));
|
|
527
|
+
var fetchHandler = _request;
|
|
528
|
+
// src/client.ts
|
|
529
|
+
function isClient(value) {
|
|
530
|
+
return typeof value === "object" && CLIENT in value;
|
|
531
|
+
}
|
|
532
|
+
function getClientConfig(client2) {
|
|
533
|
+
if (!isClient(client2)) {
|
|
534
|
+
return null;
|
|
535
|
+
}
|
|
536
|
+
return client2[CLIENT];
|
|
537
|
+
}
|
|
538
|
+
function createClient(options) {
|
|
539
|
+
const conf = {
|
|
540
|
+
host: options.host,
|
|
541
|
+
interceptors: options.interceptors ?? [],
|
|
542
|
+
handler: options.handler || fetchHandler
|
|
543
|
+
};
|
|
544
|
+
return {
|
|
545
|
+
[CLIENT]: conf
|
|
546
|
+
};
|
|
547
|
+
}
|
|
548
|
+
var CLIENT = Symbol("Client");
|
|
549
|
+
export {
|
|
550
|
+
makeResponse,
|
|
551
|
+
makeHttpContextToken,
|
|
552
|
+
makeHttpContext,
|
|
553
|
+
isRequestRef,
|
|
554
|
+
isFieldGroup,
|
|
555
|
+
isField,
|
|
556
|
+
isClient,
|
|
557
|
+
field,
|
|
558
|
+
fetchHandler,
|
|
559
|
+
doRequest,
|
|
560
|
+
defineRequest,
|
|
561
|
+
createClient
|
|
562
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@defjs/core",
|
|
3
|
+
"version": "0.0.0-test-0000",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"scripts": {
|
|
6
|
+
"build": "bun run ./scripts/build.ts",
|
|
7
|
+
"test": "bun test",
|
|
8
|
+
"lint": "biome check",
|
|
9
|
+
"lint:fix": "biome check --write",
|
|
10
|
+
"pub": "npm publish"
|
|
11
|
+
},
|
|
12
|
+
"peerDependencies": {
|
|
13
|
+
"typescript": "^5.0"
|
|
14
|
+
},
|
|
15
|
+
"files": ["dist"],
|
|
16
|
+
"module": "dist/index.js",
|
|
17
|
+
"typings": "dist/index.d.ts",
|
|
18
|
+
"exports": {
|
|
19
|
+
"./package.json": "./package.json",
|
|
20
|
+
".": {
|
|
21
|
+
"types": "./index.d.ts",
|
|
22
|
+
"default": "./index.js"
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|