soon-fetch 1.0.0 → 2.0.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/README.md CHANGED
@@ -10,7 +10,7 @@
10
10
  > - ⭐ rapid define a request api
11
11
  > - ⌛ timeout disconnect
12
12
  > - 🔤 automatic parse or serialization of JSON
13
- > - 📏 .min size less than **2K**, smaller after zip
13
+ > - 📏 .min size less than **3K**, smaller after zip
14
14
  > - 💡 smart type tips with Typescript
15
15
 
16
16
  - [Example](#example)
@@ -20,10 +20,6 @@
20
20
  - [Restful Url Params](#restful-url-params)
21
21
  - [Timeout](#timeout)
22
22
  - [Rapid Define APIs](#rapid-define-apis)
23
- - [API Reference](#api-reference)
24
- - [Create Instance](#create-instance)
25
- - [Request](#request)
26
- - [Response](#response)
27
23
  - [Support Me](#support-me)
28
24
 
29
25
  ### Example
@@ -32,18 +28,30 @@
32
28
  > [github: soon-admin-react-nextjs ](https://github.com/leafio/soon-admin-react-nextjs)
33
29
 
34
30
  ```typescript
35
- export const soon = createSoon({
36
- baseURL: "/",
37
- options: () => ({
38
- timeout: 20 * 1000,
39
- headers: new Headers({
40
- Authorization: localStorage.getItem("token") ?? "",
41
- }),
42
- }),
43
- fetching(url, options) {
44
- return fetch(url, options).then((res) => res.json());
45
- },
46
- });
31
+ import { createSoon, parseUrlOptions, type SoonOptions } from "soon-fetch";
32
+
33
+ const request = (url: string, options?: SoonOptions) => {
34
+ // _url : url handled with baseURL , options.query , options.params
35
+ // _options: 1. merge baseOptions and options,
36
+ // 2. transfer merged options from SoonOptions to raw fetch options
37
+ // 3. json object body would be auto stringified and add header { "Content-Type": "application/json" }
38
+ const [_url, _options] = parseUrlOptions({
39
+ url,
40
+ options,
41
+ baseURL: "/api",
42
+ baseOptions: {
43
+ // would override by options.timeout
44
+ timeout: 20 * 1000,
45
+ // would merged with options.headers
46
+ // the same-key header would override by options.headers
47
+ headers: { Authorization: localStorage.getItem("token") ?? "" },
48
+ },
49
+ });
50
+
51
+ return fetch(_url, _options).then((res) => res.json());
52
+ };
53
+
54
+ const soon = createSoon(request);
47
55
 
48
56
  /** GET */
49
57
  soon.get("/user?id=123");
@@ -117,106 +125,13 @@ soon.get(url, { timeout: 1000 * 20 });
117
125
  })
118
126
  ```
119
127
 
120
- ### API Reference
121
-
122
- ##### Create Instance
123
-
124
- ```typescript
125
- import { createSoon } from "soon";
126
-
127
- declare function createSoon<Options extends SoonOptions = SoonOptions>(
128
- soonInit?: SoonInit<Options>
129
- );
130
-
131
- // options would overwritten by request level options ,but the headers will be merged
132
- export type SoonInit<Options> = {
133
- //**url prefix */
134
- baseURL?: string;
135
- /**the fetch http options */
136
- options?: () => Options;
137
- /** can use return a new fetch request instead*/
138
- fetching?: (
139
- url: string,
140
- options: Options & { headers: Headers }
141
- ) => Promise<any>;
142
- };
143
- ```
144
-
145
- ##### Request
146
-
147
- ```typescript
148
- soon.request(url[,options])
149
- ```
150
-
151
- Request data can choose `query` `params` `body` for easy specification
152
-
153
- ```typescript
154
- type SoonOptions = {
155
- /** url search params like `api/info?name=yes` {name:"yes"} passed here*/
156
- query?:
157
- | Record<
158
- string,
159
- string | number | boolean | string[] | number[] | null | undefined
160
- >
161
- | URLSearchParams;
162
- /** url rest params like `api/info/:id` {id:1} passed here*/
163
- params?: Record<string, string | number>;
164
- /** unit ms */
165
- timeout?: number;
166
- /***** vanilla fetch props *****/
167
- //body can pass json without stringified
168
- body?: any;
169
- signal?: AbortSignal;
170
- method?:
171
- | "get"
172
- | "GET"
173
- | "delete"
174
- | "DELETE"
175
- | "head"
176
- | "HEAD"
177
- | "options"
178
- | "OPTIONS"
179
- | "post"
180
- | "POST"
181
- | "put"
182
- | "PUT"
183
- | "patch"
184
- | "PATCH"
185
- | "purge"
186
- | "PURGE"
187
- | "link"
188
- | "LINK"
189
- | "unlink"
190
- | "UNLINK";
191
- mode?: "cors" | "no-cors" | "same-origin";
192
- cache?: "default" | "no-cache" | "reload" | "force-cache" | "only-if-cached";
193
- credentials?: "include" | "same-origin" | "omit";
194
- headers?: Headers;
195
- redirect?: "manual" | "follow" | "error";
196
- referrerPolicy?:
197
- | "no-referrer"
198
- | "no-referrer-when-downgrade"
199
- | "origin"
200
- | "origin-when-cross-origin"
201
- | "same-origin"
202
- | "strict-origin"
203
- | "strict-origin-when-cross-origin"
204
- | "unsafe-url";
205
- integrity?: string;
206
- };
207
- ```
208
-
209
- ##### Response
210
-
211
- Default : return raw fetch Response , you can customize it in `fetching` params of `createSoon`
212
-
213
128
  ### Support Me
214
129
 
215
130
  If you like this library , you can give a **star** on github.
216
131
  GitHub: https://github.com/leafio/soon-fetch
217
132
 
218
- > I'm looking for a frontend job in Shanghai , hope someone could find a offer for me.
219
- Email: leafnote@outlook.com
133
+ > I'm looking for a frontend job , expecting an offer or chance for me .
134
+ > Email: leafnote@outlook.com
220
135
 
221
136
  [English](#soon-fetch) | [中文](#soon-fetch-1) | [Installation](#安装-installation)
222
137
 
@@ -224,13 +139,13 @@ Email: leafnote@outlook.com
224
139
 
225
140
  #### soon-fetch
226
141
 
227
- **极轻量的请求库,不到 2K**
142
+ **极轻量的请求库,不到 3K**
228
143
 
229
144
  > - 🌐 自动解析 rest Url 的参数
230
145
  > - ⭐ 快捷定义请求 api
231
146
  > - ⌛ 超时断开
232
147
  > - 🔤 自动处理 JSON
233
- > - 📏 不到 **2K** , zip 后会更小
148
+ > - 📏 不到 **3K** , zip 后会更小
234
149
  > - 💡 用 typescript 有智能类型提醒
235
150
 
236
151
  - [示例](#示例)
@@ -240,11 +155,6 @@ Email: leafnote@outlook.com
240
155
  - [Restful Url 参数自动处理](#restful-url-参数自动处理)
241
156
  - [超时](#超时)
242
157
  - [快速定义 API](#快速定义-api)
243
- - [API 参考](#api参考)
244
-
245
- - [创建实例](#创建实例)
246
- - [请求](#请求)
247
- - [响应](#响应)
248
158
 
249
159
  - [支持一下](#支持一下)
250
160
 
@@ -254,18 +164,28 @@ Email: leafnote@outlook.com
254
164
  > [github: soon-admin-react-nextjs ](https://github.com/leafio/soon-admin-react-nextjs)
255
165
 
256
166
  ```typescript
257
- export const soon = createSoon<SoonOptions>({
258
- baseURL: "/",
259
- options: () => ({
260
- timeout: 20 * 1000,
261
- headers: new Headers({
262
- Authorization: localStorage.getItem("token") ?? "",
263
- }),
264
- }),
265
- fetching(url, options) {
266
- return fetch(url, options).then((res) => res.json());
267
- },
268
- });
167
+ const request = (url: string, options?: SoonOptions) => {
168
+ // _url : url 根据 baseURL , options.query , options.params 进行拼接
169
+ // _options: 1. 合并 baseOptions 和 options
170
+ // 2. 合并结果 SoonOptions 转换至 原生 fetch options
171
+ // 3. json object 会自动 stringified , 并且添加 header { "Content-Type": "application/json" }
172
+ const [_url, _options] = parseUrlOptions({
173
+ url,
174
+ options,
175
+ baseURL: "/api",
176
+ baseOptions: {
177
+ // 会被 options.timeout 覆盖
178
+ timeout: 20 * 1000,
179
+ // 将与 options.headers 合并
180
+ // 相同key的 header 会被options.headers覆盖
181
+ headers: { Authorization: localStorage.getItem("token") ?? "" },
182
+ },
183
+ });
184
+
185
+ return fetch(_url, _options).then((res) => res.json());
186
+ };
187
+
188
+ const soon = createSoon(request);
269
189
 
270
190
  /** GET */
271
191
  soon.get("/user?id=123");
@@ -338,105 +258,12 @@ soon.get(url, { timeout: 1000 * 20 });
338
258
  })
339
259
  ```
340
260
 
341
- ### API 参考
342
-
343
- ##### 创建实例
344
-
345
- ```typescript
346
- import { createSoon } from "soon";
347
-
348
- declare function createSoon<Options extends SoonOptions = SoonOptions>(
349
- soonInit?: SoonInit<Options>
350
- );
351
-
352
- // 实例级options会被请求级options覆盖,但headers仅同key的被覆盖,其他合并
353
- export type SoonInit<Options> = {
354
- baseURL?: string;
355
- //默认的options
356
- options?: () => Options;
357
- //可返回自定义的fetch请求
358
- fetching?: (
359
- url: string,
360
- options: Options & { headers: Headers }
361
- ) => Promise<any>;
362
- };
363
- ```
364
-
365
- ##### 请求
366
-
367
- ```typescript
368
- soon.request(url[,options])
369
- ```
370
-
371
- 请求数据可以选择 _`query`_ _`params`_ _`body`_ ,易于传递。
372
-
373
- ```typescript
374
- type SoonOptions = {
375
- /** url ?后的参数 `api/info?name=yes` 传递 {name:"yes"}*/
376
- query?:
377
- | Record<
378
- string,
379
- string | number | boolean | string[] | number[] | null | undefined
380
- >
381
- | URLSearchParams;
382
- /** rest风格url的请求参数 `api/info/:id` 传递 {id:1}*/
383
- params?: Record<string, string | number>;
384
- /** unit 毫秒 */
385
- timeout?: number;
386
-
387
- /*** 原生fetch 参数*/
388
- //可直接传递JSON而不必stringified
389
- body?: any;
390
- signal?: AbortSignal;
391
- method?:
392
- | "get"
393
- | "GET"
394
- | "delete"
395
- | "DELETE"
396
- | "head"
397
- | "HEAD"
398
- | "options"
399
- | "OPTIONS"
400
- | "post"
401
- | "POST"
402
- | "put"
403
- | "PUT"
404
- | "patch"
405
- | "PATCH"
406
- | "purge"
407
- | "PURGE"
408
- | "link"
409
- | "LINK"
410
- | "unlink"
411
- | "UNLINK";
412
- mode?: "cors" | "no-cors" | "same-origin";
413
- cache?: "default" | "no-cache" | "reload" | "force-cache" | "only-if-cached";
414
- credentials?: "include" | "same-origin" | "omit";
415
- headers?: Headers;
416
- redirect?: "manual" | "follow" | "error";
417
- referrerPolicy?:
418
- | "no-referrer"
419
- | "no-referrer-when-downgrade"
420
- | "origin"
421
- | "origin-when-cross-origin"
422
- | "same-origin"
423
- | "strict-origin"
424
- | "strict-origin-when-cross-origin"
425
- | "unsafe-url";
426
- integrity?: string;
427
- };
428
- ```
429
-
430
- ##### 响应
431
-
432
- 默认为原生 fetch 的 Response ,可在 `createSoon` 的 `fetching` 里自定义返回结果
433
-
434
261
  ### 支持一下
435
262
 
436
263
  喜欢 soon-fetch 的话 , 在 github 上给个 **star** 吧.
437
264
  GitHub: https://github.com/leafio/soon-fetch
438
265
 
439
- > 我目前在找前端的工作,位置上海。有岗位机会的话,可以联系我。
266
+ > 我目前在找前端的工作,有岗位机会的话,可以联系我。
440
267
  > Email: leafnote@outlook.com
441
268
 
442
269
  [English](#soon-fetch) | [中文](#soon-fetch-1) | [Installation](#安装-installation)
package/dist/index.cjs.js CHANGED
@@ -1 +1 @@
1
- "use strict";const t=t=>{var e;const s=[];return null===(e=t.match(/\:([^:\/\d]+)\/?/g))||void 0===e||e.forEach((t=>{s.push(t.replace(/\//g,"").replace(/:/g,""))})),s},e=(e,s)=>{const{query:n,params:r,baseURL:a}=s;let o=e.trim();t(e).forEach((t=>{r&&(o=o.replace(":"+t,""+r[t]))}));const c="http",i=t=>(null==t?void 0:t.endsWith("/"))?t.slice(0,-1):null!=t?t:"",h=t=>{let e=null!=t?t:"";return t&&(e=i(e),t.startsWith("/")||(e="/"+e)),e},[l,u]=o.split("?"),p=new URLSearchParams(u);let f;const d=[];n&&Object.keys(n).forEach((t=>{const e=n[t];(Array.isArray(e)?e:[e]).forEach((e=>{d.push([t,null!=e?e:""])}))})),f=new URLSearchParams([...Array.from(p.entries()),...d]);let g=l;return o.startsWith(c)||(g=i(a)+h(l),g.startsWith(c)||(g=h(g))),f.size&&(g=g+"?"+f),g},s=(...t)=>{const e=new Headers;return t.forEach((t=>{t&&t.forEach(((t,s)=>{e.set(s,t)}))})),e};exports.createSoon=function(n={}){const r={};r.baseInit=n;const a=(t,n)=>function(t,n,r){const{options:a,fetching:o,baseURL:c}=r||{},i=a?a():{},{headers:h}=i,{headers:l}=n||{};let u=s(h,l),p=Object.assign({},i,n,{headers:u}),f=p.timeout;const d=[];f&&d.push(AbortSignal.timeout(f));const g=p.signal;g&&d.push(g),p.signal=AbortSignal.any(d);let b=e(t,Object.assign({},p,{baseURL:c})),y=!1;const m=p.body;return m&&(m instanceof Blob||m instanceof ArrayBuffer||m instanceof FormData||"string"==typeof m||(y=!0,p.body=JSON.stringify(m))),y&&p.headers.set("Content-Type","application/json"),o?o(b,p):fetch(b,p)}(t,n,r.baseInit),o=["get","post","put","delete","patch"];return[...o,"head","options"].forEach((t=>{r[t]=(e,s)=>a(e,Object.assign({},s,{method:t}))})),r.request=a,r.API=(e,s)=>{const n={},r=!!t(e).length;return o.forEach((t=>{const o=t.toUpperCase();n[o]=()=>(...n)=>{let[o,c]=n;const i="get"===t?"query":"body";return a(e,Object.assign({},s,{method:t,params:r?o:void 0,[i]:r?c:o}))}})),n},r};
1
+ "use strict";const t=t=>{const r=[],e=t.match(/:([^:/\d]+)\/?/g);return e&&e.forEach((t=>{r.push(t.replace(/\//g,"").replace(/:/g,""))})),r},r=(t="")=>t.startsWith("http"),e=(t="")=>r(t)?t:((t="")=>{let r=t;return t&&(r=((t="")=>t.endsWith("/")?t.slice(0,-1):t)(r),t.startsWith("/")||(r="/"+r)),r})(t),n=t=>{if(!t)return[];if(t instanceof URLSearchParams||"string"==typeof t||Array.isArray(t))return Array.from(new URLSearchParams(t).entries());const r=[];return Object.keys(t).forEach((e=>{const n=t[e];(Array.isArray(n)?n:[n]).forEach((t=>{r.push([e,t??""])}))})),r},a=(a,o)=>{const{query:s,params:i,baseURL:c}=o;let f=a.trim();t(a).forEach((t=>{i&&(f=f.replace(":"+t,""+i[t]))}));const[p,u]=f.split("?"),h=new URLSearchParams([...n(u),...n(s)]);let y=((t,n)=>{let a=e(t);return r(a)||(a=e(n)+a),a})(p,c);return h.size&&(y=y+"?"+h),y},o=(...t)=>{const r=new Headers;return t.forEach((t=>{t&&new Headers(t).forEach(((t,e)=>{r.set(e,t)}))})),r},s=(t,r)=>{const e=[];return t&&e.push(AbortSignal.timeout(t)),r&&e.push(r),e.length?AbortSignal.any(e):void 0};function i(t){let r=!1,e=t;var n;return e&&"object"==typeof e&&!(e instanceof Blob||e instanceof ArrayBuffer||e instanceof FormData||e instanceof File||e instanceof DataView||e instanceof URLSearchParams||e instanceof ReadableStream||(n=e,n instanceof Int8Array||n instanceof Uint8Array||n instanceof Uint8ClampedArray||n instanceof Int16Array||n instanceof Uint16Array||n instanceof Int32Array||n instanceof Uint32Array||n instanceof Float32Array||n instanceof Float64Array||n instanceof BigInt64Array||n instanceof BigUint64Array))&&(r=!0,e=JSON.stringify(e)),[e,r]}const c=["get","post","put","delete","patch"];function f(t,r){const e={};return t.forEach((t=>{e[t]=(e,n)=>r(e,{...n,method:t})})),e}function p(r){return(e,n)=>{const a={},o=!!t(e).length;return c.forEach((t=>{const s=t.toUpperCase();a[s]=()=>(...a)=>{const[s,i]=a,c="get"===t?"query":"body";return r(e,{...n,method:t,params:o?s:void 0,[c]:o?i:s})}})),a}}exports.createShortAPI=p,exports.createShortMethods=f,exports.createSoon=function(t){const r=t;return{request:r,API:p(r),...f([...c,"head","options"],r)}},exports.mergeHeaders=o,exports.mergeSignal=s,exports.mergeUrl=a,exports.parseUrlOptions=function(t){const{url:r,options:e,baseURL:n,baseOptions:c}=t,f={...c,...e};f.signal=s(f.timeout,f.signal);const p=a(r,{...f,baseURL:n}),[u,h]=i(f.body);f.body=u;const y=o(c?.headers,e?.headers,h?{"Content-Type":"application/json"}:void 0);return f.headers=y,[p,f]},exports.stringifyBody=i;
package/dist/index.d.ts CHANGED
@@ -1,45 +1,46 @@
1
- type SoonOptions = {
2
- query?: Record<string, string | number | boolean | string[] | number[] | null | undefined> | URLSearchParams;
3
- params?: Record<string, string | number>;
4
- timeout?: number;
5
- signal?: AbortSignal;
6
- method?: "get" | "GET" | "delete" | "DELETE" | "head" | "HEAD" | "options" | "OPTIONS" | "post" | "POST" | "put" | "PUT" | "patch" | "PATCH" | "purge" | "PURGE" | "link" | "LINK" | "unlink" | "UNLINK";
7
- mode?: "cors" | "no-cors" | "same-origin";
8
- cache?: "default" | "no-cache" | "reload" | "force-cache" | "only-if-cached";
9
- credentials?: "include" | "same-origin" | "omit";
10
- headers?: Headers;
11
- redirect?: "manual" | "follow" | "error";
12
- referrerPolicy?: "no-referrer" | "no-referrer-when-downgrade" | "origin" | "origin-when-cross-origin" | "same-origin" | "strict-origin" | "strict-origin-when-cross-origin" | "unsafe-url";
13
- body?: any;
14
- integrity?: string;
15
- };
16
- type SoonInit<Options> = {
17
- baseURL?: string;
18
- options?: () => Options;
19
- fetching?: (url: string, options: Options & {
20
- headers: Headers;
21
- }) => Promise<any>;
22
- };
23
- declare function createSoon<Options extends SoonOptions = SoonOptions>(soonInit?: SoonInit<Options>): {
24
- request: <T = any>(url: string, options?: Options) => Promise<T>;
25
- get: <T = any>(url: string, options?: Options) => Promise<T>;
26
- post: <T = any>(url: string, options?: Options) => Promise<T>;
27
- put: <T = any>(url: string, options?: Options) => Promise<T>;
28
- patch: <T = any>(url: string, options?: Options) => Promise<T>;
29
- delete: <T = any>(url: string, options?: Options) => Promise<T>;
30
- head: <T = any>(url: string, options?: Options) => Promise<T>;
31
- options: <T = any>(url: string, options?: Options) => Promise<T>;
32
- API: <Url extends string>(url: Url, options?: Options) => {
33
- GET: <Req = undefined, Res = any>() => (...arg: [...OptionParams<{ [key in GetUrlKey<Url>]: string | number; }>, ...OptionQuery<Req>]) => Promise<Res>;
34
- POST: <Req = undefined, Res = any>() => (...arg: [...OptionParams<{ [key in GetUrlKey<Url>]: string | number; }>, ...OptionBody<Req>]) => Promise<Res>;
35
- PATCH: <Req = undefined, Res = any>() => (...arg: [...OptionParams<{ [key in GetUrlKey<Url>]: string | number; }>, ...OptionBody<Req>]) => Promise<Res>;
36
- DELETE: <Req = undefined, Res = any>() => (...arg: [...OptionParams<{ [key in GetUrlKey<Url>]: string | number; }>, ...OptionBody<Req>]) => Promise<Res>;
37
- PUT: <Req = undefined, Res = any>() => (...arg: [...OptionParams<{ [key in GetUrlKey<Url>]: string | number; }>, ...OptionBody<Req>]) => Promise<Res>;
38
- };
39
- };
40
- type GetUrlKey<Url> = Url extends `${string}/:${infer Key}/${infer Right}` ? `${Key}` | GetUrlKey<`/${Right}`> : Url extends `${string}/:${infer Key}` ? `${Key}` : never;
41
- type OptionParams<Args> = Args extends undefined ? [] : keyof Args extends never ? [] : Partial<Args> extends Args ? [params?: Args] : NonNullable<Args> | undefined extends Args ? [params?: Args] : [params: Args];
42
- type OptionQuery<Args> = Args extends undefined ? [] : keyof Args extends never ? [] : Partial<Args> extends Args ? [query?: Args] : NonNullable<Args> | undefined extends Args ? [query?: Args] : [query: Args];
43
- type OptionBody<Args> = Args extends undefined ? [] : keyof Args extends never ? [] : Partial<Args> extends Args ? [body?: Args] : NonNullable<Args> | undefined extends Args ? [body?: Args] : [body: Args];
44
-
45
- export { type SoonInit, type SoonOptions, createSoon };
1
+ type SoonOptions = RequestInit & {
2
+ query?: Record<string, string | number | boolean | null | undefined | (string | number | boolean | null | undefined)[]> | URLSearchParams;
3
+ params?: Record<string, string | number>;
4
+ timeout?: number;
5
+ };
6
+ type GetUrlKey<Url> = Url extends `${string}/:${infer Key}/${infer Right}` ? `${Key}` | GetUrlKey<`/${Right}`> : Url extends `${string}/:${infer Key}` ? `${Key}` : never;
7
+ type OptionParams<Args> = NonNullable<Args> extends never ? [] : keyof NonNullable<Args> extends never ? [] : Exclude<Args, NonNullable<Args>> extends never ? [params: Args] : [params?: Args];
8
+ type OptionQuery<Args> = NonNullable<Args> extends never ? [] : keyof NonNullable<Args> extends never ? [] : Exclude<Args, NonNullable<Args>> extends never ? Partial<Args> extends Args ? [query?: Args] : [query: Args] : [query?: Args];
9
+ type OptionBody<Args> = NonNullable<Args> extends never ? [] : Exclude<Args, NonNullable<Args>> extends never ? [body: Args] : [body?: Args];
10
+ type Tuple2Union<T> = T extends [infer T1, infer T2, ...infer R] ? T1 | T2 | Tuple2Union<R> : T extends [infer T_Only] ? T_Only : never;
11
+ type TupleLast<T> = Required<T> extends [infer First, ...infer Rest] ? Rest : never;
12
+ type TupleFirst<T> = Required<T> extends [...infer Rest, infer Last] ? Rest : never;
13
+
14
+ declare const mergeUrl: (url: string, config: {
15
+ query?: Record<string, string | number | boolean | null | undefined | (string | number | boolean | null | undefined)[]> | URLSearchParams;
16
+ params?: Record<string, string | number>;
17
+ baseURL?: string;
18
+ }) => string;
19
+ declare const mergeHeaders: (...headersList: (HeadersInit | undefined)[]) => Headers;
20
+ declare const mergeSignal: (timeout?: number, signal?: AbortSignal | null) => AbortSignal | undefined;
21
+ declare function stringifyBody(body?: any): [body: any, isBodyJson: boolean];
22
+ declare function parseUrlOptions<Options extends SoonOptions>(urlOptions: {
23
+ url: string;
24
+ options?: Options;
25
+ baseURL?: string;
26
+ baseOptions?: Options;
27
+ }): readonly [string, Options & {
28
+ headers: Headers;
29
+ }];
30
+ declare function createSoon<T extends (url: string, options?: SoonOptions) => Promise<any>>(requestFun: T): {
31
+ options: T extends (...args: infer P) => infer R ? (...args: [...TupleFirst<P>, ...Partial<TupleLast<P>>]) => R : never;
32
+ get: T extends (...args: infer P) => infer R ? (...args: [...TupleFirst<P>, ...Partial<TupleLast<P>>]) => R : never;
33
+ post: T extends (...args: infer P) => infer R ? (...args: [...TupleFirst<P>, ...Partial<TupleLast<P>>]) => R : never;
34
+ put: T extends (...args: infer P) => infer R ? (...args: [...TupleFirst<P>, ...Partial<TupleLast<P>>]) => R : never;
35
+ delete: T extends (...args: infer P) => infer R ? (...args: [...TupleFirst<P>, ...Partial<TupleLast<P>>]) => R : never;
36
+ patch: T extends (...args: infer P) => infer R ? (...args: [...TupleFirst<P>, ...Partial<TupleLast<P>>]) => R : never;
37
+ head: T extends (...args: infer P) => infer R ? (...args: [...TupleFirst<P>, ...Partial<TupleLast<P>>]) => R : never;
38
+ request: T;
39
+ API: <Url extends string>(url: Url, options?: Parameters<T>[1] | undefined) => Record<"GET", <Req = undefined, Res = Awaited<ReturnType<T>>>() => (...arg: [...OptionParams<{ [key in GetUrlKey<Url>]: string | number; }>, ...OptionQuery<Req>]) => Promise<Res>> & Record<"POST" | "PATCH" | "DELETE" | "PUT", <Req = undefined, Res_1 = Awaited<ReturnType<T>>>() => (...arg: [...OptionParams<{ [key in GetUrlKey<Url>]: string | number; }>, ...OptionBody<Req>]) => Promise<Res_1>>;
40
+ };
41
+ declare function createShortMethods<Methods extends string[], RequestFun extends <T>(url: string, options?: {
42
+ method?: string;
43
+ }) => Promise<any>>(methods: Methods, requestFun: RequestFun): Record<Tuple2Union<Methods>, typeof requestFun extends (...args: infer P) => infer R ? (...args: [...TupleFirst<P>, ...Partial<TupleLast<P>>]) => R : never>;
44
+ declare function createShortAPI<requestFun extends <T>(url: string, options?: SoonOptions) => Promise<any>>(requestFun: requestFun): <Url extends string>(url: Url, options?: Parameters<requestFun>[1]) => Record<"GET", <Req = undefined, Res = Awaited<ReturnType<requestFun>>>() => (...arg: [...OptionParams<{ [key in GetUrlKey<Url>]: string | number; }>, ...OptionQuery<Req>]) => Promise<Res>> & Record<"POST" | "PATCH" | "DELETE" | "PUT", <Req = undefined, Res_1 = Awaited<ReturnType<requestFun>>>() => (...arg: [...OptionParams<{ [key in GetUrlKey<Url>]: string | number; }>, ...OptionBody<Req>]) => Promise<Res_1>>;
45
+
46
+ export { type SoonOptions, createShortAPI, createShortMethods, createSoon, mergeHeaders, mergeSignal, mergeUrl, parseUrlOptions, stringifyBody };
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- const t=t=>{var e;const s=[];return null===(e=t.match(/\:([^:\/\d]+)\/?/g))||void 0===e||e.forEach((t=>{s.push(t.replace(/\//g,"").replace(/:/g,""))})),s},e=(e,s)=>{const{query:n,params:r,baseURL:a}=s;let o=e.trim();t(e).forEach((t=>{r&&(o=o.replace(":"+t,""+r[t]))}));const c="http",i=t=>(null==t?void 0:t.endsWith("/"))?t.slice(0,-1):null!=t?t:"",h=t=>{let e=null!=t?t:"";return t&&(e=i(e),t.startsWith("/")||(e="/"+e)),e},[l,u]=o.split("?"),p=new URLSearchParams(u);let f;const d=[];n&&Object.keys(n).forEach((t=>{const e=n[t];(Array.isArray(e)?e:[e]).forEach((e=>{d.push([t,null!=e?e:""])}))})),f=new URLSearchParams([...Array.from(p.entries()),...d]);let g=l;return o.startsWith(c)||(g=i(a)+h(l),g.startsWith(c)||(g=h(g))),f.size&&(g=g+"?"+f),g},s=(...t)=>{const e=new Headers;return t.forEach((t=>{t&&t.forEach(((t,s)=>{e.set(s,t)}))})),e};function n(n={}){const r={};r.baseInit=n;const a=(t,n)=>function(t,n,r){const{options:a,fetching:o,baseURL:c}=r||{},i=a?a():{},{headers:h}=i,{headers:l}=n||{};let u=s(h,l),p=Object.assign({},i,n,{headers:u}),f=p.timeout;const d=[];f&&d.push(AbortSignal.timeout(f));const g=p.signal;g&&d.push(g),p.signal=AbortSignal.any(d);let b=e(t,Object.assign({},p,{baseURL:c})),y=!1;const m=p.body;return m&&(m instanceof Blob||m instanceof ArrayBuffer||m instanceof FormData||"string"==typeof m||(y=!0,p.body=JSON.stringify(m))),y&&p.headers.set("Content-Type","application/json"),o?o(b,p):fetch(b,p)}(t,n,r.baseInit),o=["get","post","put","delete","patch"];return[...o,"head","options"].forEach((t=>{r[t]=(e,s)=>a(e,Object.assign({},s,{method:t}))})),r.request=a,r.API=(e,s)=>{const n={},r=!!t(e).length;return o.forEach((t=>{const o=t.toUpperCase();n[o]=()=>(...n)=>{let[o,c]=n;const i="get"===t?"query":"body";return a(e,Object.assign({},s,{method:t,params:r?o:void 0,[i]:r?c:o}))}})),n},r}export{n as createSoon};
1
+ const t=t=>{const n=[],r=t.match(/:([^:/\d]+)\/?/g);return r&&r.forEach((t=>{n.push(t.replace(/\//g,"").replace(/:/g,""))})),n},n=(t="")=>t.startsWith("http"),r=(t="")=>n(t)?t:((t="")=>{let n=t;return t&&(n=((t="")=>t.endsWith("/")?t.slice(0,-1):t)(n),t.startsWith("/")||(n="/"+n)),n})(t),e=t=>{if(!t)return[];if(t instanceof URLSearchParams||"string"==typeof t||Array.isArray(t))return Array.from(new URLSearchParams(t).entries());const n=[];return Object.keys(t).forEach((r=>{const e=t[r];(Array.isArray(e)?e:[e]).forEach((t=>{n.push([r,t??""])}))})),n},a=(a,o)=>{const{query:s,params:c,baseURL:i}=o;let f=a.trim();t(a).forEach((t=>{c&&(f=f.replace(":"+t,""+c[t]))}));const[u,h]=f.split("?"),y=new URLSearchParams([...e(h),...e(s)]);let p=((t,e)=>{let a=r(t);return n(a)||(a=r(e)+a),a})(u,i);return y.size&&(p=p+"?"+y),p},o=(...t)=>{const n=new Headers;return t.forEach((t=>{t&&new Headers(t).forEach(((t,r)=>{n.set(r,t)}))})),n},s=(t,n)=>{const r=[];return t&&r.push(AbortSignal.timeout(t)),n&&r.push(n),r.length?AbortSignal.any(r):void 0};function c(t){let n=!1,r=t;var e;return r&&"object"==typeof r&&!(r instanceof Blob||r instanceof ArrayBuffer||r instanceof FormData||r instanceof File||r instanceof DataView||r instanceof URLSearchParams||r instanceof ReadableStream||(e=r,e instanceof Int8Array||e instanceof Uint8Array||e instanceof Uint8ClampedArray||e instanceof Int16Array||e instanceof Uint16Array||e instanceof Int32Array||e instanceof Uint32Array||e instanceof Float32Array||e instanceof Float64Array||e instanceof BigInt64Array||e instanceof BigUint64Array))&&(n=!0,r=JSON.stringify(r)),[r,n]}function i(t){const{url:n,options:r,baseURL:e,baseOptions:i}=t,f={...i,...r};f.signal=s(f.timeout,f.signal);const u=a(n,{...f,baseURL:e}),[h,y]=c(f.body);f.body=h;const p=o(i?.headers,r?.headers,y?{"Content-Type":"application/json"}:void 0);return f.headers=p,[u,f]}const f=["get","post","put","delete","patch"];function u(t){const n=t;return{request:n,API:y(n),...h([...f,"head","options"],n)}}function h(t,n){const r={};return t.forEach((t=>{r[t]=(r,e)=>n(r,{...e,method:t})})),r}function y(n){return(r,e)=>{const a={},o=!!t(r).length;return f.forEach((t=>{const s=t.toUpperCase();a[s]=()=>(...a)=>{const[s,c]=a,i="get"===t?"query":"body";return n(r,{...e,method:t,params:o?s:void 0,[i]:o?c:s})}})),a}}export{y as createShortAPI,h as createShortMethods,u as createSoon,o as mergeHeaders,s as mergeSignal,a as mergeUrl,i as parseUrlOptions,c as stringifyBody};
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "soon-fetch",
3
- "version": "1.0.0",
4
- "description": "a 2Kb request lib alternative to axios",
3
+ "version": "2.0.0",
4
+ "description": "a 3Kb request lib alternative to axios",
5
5
  "homepage": "https://github.com/leafio/soon-fetch",
6
6
  "main": "./dist/index.cjs.js",
7
7
  "module": "/dist/index.js",