extra-request 8.5.0 → 8.5.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "extra-request",
3
- "version": "8.5.0",
3
+ "version": "8.5.1",
4
4
  "description": "Utilities for Request",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -9,7 +9,8 @@
9
9
  "Request"
10
10
  ],
11
11
  "files": [
12
- "lib"
12
+ "lib",
13
+ "src"
13
14
  ],
14
15
  "main": "lib/index.js",
15
16
  "exports": {
package/src/index.ts ADDED
@@ -0,0 +1,3 @@
1
+ export * from './types.js'
2
+ export * from './request.js'
3
+ export * from './pipe-request-options-transformers.js'
@@ -0,0 +1,23 @@
1
+ import { Headers } from 'extra-fetch'
2
+ import { IRequestOptions, IRequestOptionsTransformer } from '@src/types.js'
3
+ import { isntFalsy } from '@blackglory/prelude'
4
+ import { Falsy } from '@blackglory/prelude'
5
+ import { pipe } from 'extra-utils'
6
+
7
+ export function pipeRequestOptionsTransformers(
8
+ ...transformers: Array<IRequestOptionsTransformer | Falsy>
9
+ ): IRequestOptions {
10
+ const base: IRequestOptions = {
11
+ url: new URL(
12
+ typeof document !== 'undefined'
13
+ ? document.baseURI
14
+ : 'http://localhost'
15
+ )
16
+ , headers: new Headers()
17
+ }
18
+
19
+ return pipe(
20
+ base
21
+ , ...transformers.filter<IRequestOptionsTransformer>(isntFalsy)
22
+ )
23
+ }
package/src/request.ts ADDED
@@ -0,0 +1,45 @@
1
+ import { Headers, Request } from 'extra-fetch'
2
+ import { IRequestOptionsTransformer } from '@src/types.js'
3
+ import { pipeRequestOptionsTransformers } from '@src/pipe-request-options-transformers.js'
4
+ import { Falsy } from '@blackglory/prelude'
5
+
6
+ export function get(...transformers: Array<IRequestOptionsTransformer | Falsy>): Request {
7
+ return request('GET', ...transformers)
8
+ }
9
+
10
+ export function head(...transformers: Array<IRequestOptionsTransformer | Falsy>): Request {
11
+ return request('HEAD', ...transformers)
12
+ }
13
+
14
+ export function post(...transformers: Array<IRequestOptionsTransformer | Falsy>): Request {
15
+ return request('POST', ...transformers)
16
+ }
17
+
18
+ export function put(...transformers: Array<IRequestOptionsTransformer | Falsy>): Request {
19
+ return request('PUT', ...transformers)
20
+ }
21
+
22
+ export function patch(...transformers: Array<IRequestOptionsTransformer | Falsy>): Request {
23
+ return request('PATCH', ...transformers)
24
+ }
25
+
26
+ export function del(...transformers: Array<IRequestOptionsTransformer | Falsy>): Request {
27
+ return request('DELETE', ...transformers)
28
+ }
29
+
30
+ function request(
31
+ method: 'GET' | 'HEAD' | 'PUT' | 'POST' | 'PATCH' | 'DELETE'
32
+ , ...transformers: Array<IRequestOptionsTransformer | Falsy>
33
+ ): Request {
34
+ const requestOptions = pipeRequestOptionsTransformers(...transformers)
35
+ const headers = new Headers(requestOptions.headers)
36
+
37
+ return new Request(requestOptions.url.href, {
38
+ method
39
+ , headers
40
+ , signal: requestOptions.signal
41
+ , body: requestOptions.payload as any
42
+ , keepalive: requestOptions.keepalive
43
+ , redirect: requestOptions.redirect
44
+ })
45
+ }
@@ -0,0 +1,6 @@
1
+ import { appendHeader } from './append-header.js'
2
+ import { IRequestOptionsTransformer } from '@src/types.js'
3
+
4
+ export function accept(accept: string): IRequestOptionsTransformer {
5
+ return appendHeader('Accept', accept)
6
+ }
@@ -0,0 +1,14 @@
1
+ import { Headers } from 'extra-fetch'
2
+ import { IRequestOptions, IRequestOptionsTransformer } from '@src/types.js'
3
+
4
+ export function appendHeader(name: string, value: string): IRequestOptionsTransformer {
5
+ return (options: IRequestOptions) => {
6
+ const headers = new Headers(options.headers)
7
+ headers.append(name, value)
8
+
9
+ return {
10
+ ...options
11
+ , headers
12
+ }
13
+ }
14
+ }
@@ -0,0 +1,11 @@
1
+ import { IRequestOptions, IRequestOptionsTransformer } from '@src/types.js'
2
+ import * as URL from 'url-operator'
3
+
4
+ export function appendPathname(pathname: string): IRequestOptionsTransformer {
5
+ return (options: IRequestOptions) => {
6
+ return {
7
+ ...options
8
+ , url: URL.appendPathname(options.url, pathname)
9
+ }
10
+ }
11
+ }
@@ -0,0 +1,14 @@
1
+ import { IRequestOptions, IRequestOptionsTransformer } from '@src/types.js'
2
+ import * as URL from 'url-operator'
3
+
4
+ export function appendSearchParam(
5
+ name: string
6
+ , value: string | number
7
+ ): IRequestOptionsTransformer {
8
+ return (options: IRequestOptions) => {
9
+ return {
10
+ ...options
11
+ , url: URL.appendSearchParam(options.url, name, value)
12
+ }
13
+ }
14
+ }
@@ -0,0 +1,9 @@
1
+ import { header } from './header.js'
2
+ import { IRequestOptionsTransformer } from '@src/types.js'
3
+
4
+ export function basicAuth(
5
+ username: string
6
+ , password: string
7
+ ): IRequestOptionsTransformer {
8
+ return header('Authorization', 'Basic ' + btoa(`${username}:${password}`))
9
+ }
@@ -0,0 +1,6 @@
1
+ import { header } from './header.js'
2
+ import { IRequestOptionsTransformer } from '@src/types.js'
3
+
4
+ export function bearerAuth(token: string): IRequestOptionsTransformer {
5
+ return header('Authorization', `Bearer ${token}`)
6
+ }
@@ -0,0 +1,10 @@
1
+ import { IRequestOptions, IRequestOptionsTransformer } from '@src/types.js'
2
+
3
+ export function body(val: BodyInit | NodeJS.ReadableStream): IRequestOptionsTransformer {
4
+ return (options: IRequestOptions) => {
5
+ return {
6
+ ...options
7
+ , payload: val
8
+ }
9
+ }
10
+ }
@@ -0,0 +1,26 @@
1
+ import { Headers } from 'extra-fetch'
2
+ import { IRequestOptions, IRequestOptionsTransformer } from '@src/types.js'
3
+ import papaparse from 'papaparse'
4
+ import { assert } from '@blackglory/prelude'
5
+
6
+ const { unparse } = papaparse
7
+
8
+ export function csv<T extends object>(payload: T[]): IRequestOptionsTransformer {
9
+ assert(payload.length > 0, 'payload must be a non-empty array')
10
+
11
+ return (options: IRequestOptions) => {
12
+ const headers = new Headers(options.headers)
13
+ headers.set('Content-Type', 'text/csv')
14
+
15
+ return {
16
+ ...options
17
+ , headers
18
+ , payload: stringify(payload)
19
+ }
20
+ }
21
+ }
22
+
23
+ function stringify<T extends object>(data: T[]): string {
24
+ const fields = Object.keys(data[0])
25
+ return unparse({ data, fields })
26
+ }
@@ -0,0 +1,33 @@
1
+ import { FormData } from 'extra-fetch'
2
+ import { IRequestOptions, IRequestOptionsTransformer } from '@src/types.js'
3
+ import { isArray } from '@blackglory/prelude'
4
+
5
+ export function formDataField(
6
+ name: string
7
+ , value: string | string[] | Blob
8
+ ): IRequestOptionsTransformer {
9
+ return (options: IRequestOptions) => {
10
+ const formData = options.payload instanceof FormData
11
+ ? cloneFormData(options.payload)
12
+ : new FormData()
13
+
14
+ if (isArray(value)) {
15
+ value.forEach(x => formData.append(name, x))
16
+ } else {
17
+ formData.append(name, value)
18
+ }
19
+
20
+ return {
21
+ ...options
22
+ , payload: formData
23
+ }
24
+ }
25
+ }
26
+
27
+ function cloneFormData(formData: FormData): FormData{
28
+ const result = new FormData()
29
+ for (const [name, value] of formData.entries()) {
30
+ result.append(name, value)
31
+ }
32
+ return result
33
+ }
@@ -0,0 +1,14 @@
1
+ import { Headers } from 'extra-fetch'
2
+ import { IRequestOptions, IRequestOptionsTransformer } from '@src/types.js'
3
+
4
+ export function header(name: string, value: string): IRequestOptionsTransformer {
5
+ return (options: IRequestOptions) => {
6
+ const headers = new Headers(options.headers)
7
+ headers.set(name, value)
8
+
9
+ return {
10
+ ...options
11
+ , headers
12
+ }
13
+ }
14
+ }
@@ -0,0 +1,16 @@
1
+ import { Headers } from 'extra-fetch'
2
+ import { IRequestOptions, IRequestOptionsTransformer } from '@src/types.js'
3
+
4
+ export function headers(headers: { [name: string]: string }): IRequestOptionsTransformer {
5
+ return (options: IRequestOptions) => {
6
+ const newHeaders = new Headers(options.headers)
7
+ for (const [name, value] of Object.entries(headers)) {
8
+ newHeaders.set(name, value)
9
+ }
10
+
11
+ return {
12
+ ...options
13
+ , headers: newHeaders
14
+ }
15
+ }
16
+ }
@@ -0,0 +1,11 @@
1
+ import { IRequestOptions, IRequestOptionsTransformer } from '@src/types.js'
2
+ import { setHost } from 'url-operator'
3
+
4
+ export function host(host: string): IRequestOptionsTransformer {
5
+ return (options: IRequestOptions) => {
6
+ return {
7
+ ...options
8
+ , url: setHost(options.url, host)
9
+ }
10
+ }
11
+ }
@@ -0,0 +1,29 @@
1
+ export * from '@transformers/url.js'
2
+
3
+ export * from '@transformers/text.js'
4
+ export * from '@transformers/json.js'
5
+ export * from '@transformers/csv.js'
6
+
7
+ export * from '@transformers/header.js'
8
+ export * from '@transformers/append-header.js'
9
+ export * from '@transformers/headers.js'
10
+ export * from '@transformers/accept.js'
11
+
12
+ export * from '@transformers/host.js'
13
+ export * from '@transformers/port.js'
14
+ export * from '@transformers/pathname.js'
15
+ export * from '@transformers/append-pathname.js'
16
+ export * from '@transformers/search.js'
17
+ export * from '@transformers/search-param.js'
18
+ export * from '@transformers/search-params.js'
19
+ export * from '@transformers/append-search-param.js'
20
+
21
+ export * from '@transformers/form-data-field.js'
22
+
23
+ export * from '@transformers/basic-auth.js'
24
+ export * from '@transformers/bearer-auth.js'
25
+
26
+ export * from '@transformers/signal.js'
27
+ export * from '@transformers/keepalive.js'
28
+ export * from '@transformers/redirect.js'
29
+ export * from '@transformers/body.js'
@@ -0,0 +1,18 @@
1
+ import { Headers } from 'extra-fetch'
2
+ import { IRequestOptions, IRequestOptionsTransformer } from '@src/types.js'
3
+ import { JSONValue, JSONSerializable } from 'justypes'
4
+
5
+ export function json<T extends JSONValue | JSONSerializable<any>>(
6
+ payload: T
7
+ ): IRequestOptionsTransformer {
8
+ return (options: IRequestOptions) => {
9
+ const headers = new Headers(options.headers)
10
+ headers.set('Content-Type', 'application/json')
11
+
12
+ return {
13
+ ...options
14
+ , headers
15
+ , payload: JSON.stringify(payload)
16
+ }
17
+ }
18
+ }
@@ -0,0 +1,10 @@
1
+ import { IRequestOptionsTransformer, IRequestOptions } from '@src/types.js'
2
+
3
+ export function keepalive(val: boolean = true): IRequestOptionsTransformer {
4
+ return (options: IRequestOptions) => {
5
+ return {
6
+ ...options
7
+ , keepalive: val
8
+ }
9
+ }
10
+ }
@@ -0,0 +1,11 @@
1
+ import { IRequestOptions, IRequestOptionsTransformer } from '@src/types.js'
2
+ import { setPathname } from 'url-operator'
3
+
4
+ export function pathname(pathname: string): IRequestOptionsTransformer {
5
+ return (options: IRequestOptions) => {
6
+ return {
7
+ ...options
8
+ , url: setPathname(options.url, pathname)
9
+ }
10
+ }
11
+ }
@@ -0,0 +1,11 @@
1
+ import { IRequestOptions, IRequestOptionsTransformer } from '@src/types.js'
2
+ import { setPort } from 'url-operator'
3
+
4
+ export function port(port: number): IRequestOptionsTransformer {
5
+ return (options: IRequestOptions) => {
6
+ return {
7
+ ...options
8
+ , url: setPort(options.url, port)
9
+ }
10
+ }
11
+ }
@@ -0,0 +1,10 @@
1
+ import { IRequestOptions, IRequestOptionsTransformer } from '@src/types.js'
2
+
3
+ export function redirect(val: RequestRedirect): IRequestOptionsTransformer {
4
+ return (options: IRequestOptions) => {
5
+ return {
6
+ ...options
7
+ , redirect: val
8
+ }
9
+ }
10
+ }
@@ -0,0 +1,14 @@
1
+ import { IRequestOptions, IRequestOptionsTransformer } from '@src/types.js'
2
+ import { setSearchParam } from 'url-operator'
3
+
4
+ export function searchParam(
5
+ name: string
6
+ , value: string | number
7
+ ): IRequestOptionsTransformer {
8
+ return (options: IRequestOptions) => {
9
+ return {
10
+ ...options
11
+ , url: setSearchParam(options.url, name, value)
12
+ }
13
+ }
14
+ }
@@ -0,0 +1,13 @@
1
+ import { IRequestOptions, IRequestOptionsTransformer } from '@src/types.js'
2
+ import { setSearchParams } from 'url-operator'
3
+
4
+ export function searchParams(
5
+ searchParams: Record<string, string | number>
6
+ ): IRequestOptionsTransformer {
7
+ return (options: IRequestOptions) => {
8
+ return {
9
+ ...options
10
+ , url: setSearchParams(options.url, searchParams)
11
+ }
12
+ }
13
+ }
@@ -0,0 +1,11 @@
1
+ import { IRequestOptions, IRequestOptionsTransformer } from '@src/types.js'
2
+ import { setSearch } from 'url-operator'
3
+
4
+ export function search(search: string): IRequestOptionsTransformer {
5
+ return (options: IRequestOptions) => {
6
+ return {
7
+ ...options
8
+ , url: setSearch(options.url, search)
9
+ }
10
+ }
11
+ }
@@ -0,0 +1,10 @@
1
+ import { IRequestOptions, IRequestOptionsTransformer } from '@src/types.js'
2
+
3
+ export function signal(signal: AbortSignal): IRequestOptionsTransformer {
4
+ return (options: IRequestOptions) => {
5
+ return {
6
+ ...options
7
+ , signal
8
+ }
9
+ }
10
+ }
@@ -0,0 +1,15 @@
1
+ import { Headers } from 'extra-fetch'
2
+ import { IRequestOptions, IRequestOptionsTransformer } from '@src/types.js'
3
+
4
+ export function text(payload: string): IRequestOptionsTransformer {
5
+ return (options: IRequestOptions) => {
6
+ const headers = new Headers(options.headers)
7
+ headers.set('Content-Type', 'application/x-www-form-urlencoded')
8
+
9
+ return {
10
+ ...options
11
+ , headers
12
+ , payload
13
+ }
14
+ }
15
+ }
@@ -0,0 +1,15 @@
1
+ import { IRequestOptions, IRequestOptionsTransformer } from '@src/types.js'
2
+ import { NonEmptyArray } from 'justypes'
3
+
4
+ export function url(...urls: NonEmptyArray<string | URL>): IRequestOptionsTransformer {
5
+ return (options: IRequestOptions) => {
6
+ const newURL = new URL(
7
+ [options.url, ...urls].reduce((acc, cur) => new URL(cur, acc))
8
+ )
9
+
10
+ return {
11
+ ...options
12
+ , url: newURL
13
+ }
14
+ }
15
+ }
package/src/types.ts ADDED
@@ -0,0 +1,21 @@
1
+ export type IRequestOptionsTransformer = (options: IRequestOptions) => IRequestOptions
2
+
3
+ /**
4
+ * Q: Why not use `Request` interface?
5
+ * A: Because `Request['body']` is `ReadableStream`,
6
+ * it is very difficult to use.
7
+ *
8
+ * Q: Why not use `RequestInit` interface?
9
+ * A: Because `RequestInit` has no `url` property,
10
+ * and its optional properties are not suitable for our cases.
11
+ */
12
+ export interface IRequestOptions {
13
+ url: URL
14
+ headers: Headers
15
+ payload?:
16
+ | BodyInit // WHATWG
17
+ | NodeJS.ReadableStream // node-fetch
18
+ signal?: AbortSignal
19
+ keepalive?: boolean
20
+ redirect?: RequestRedirect
21
+ }
package/lib/browser.d.ts DELETED
@@ -1,3 +0,0 @@
1
- export * from './types.js';
2
- export * from './request.js';
3
- export * from './transformers/index.js';
package/lib/browser.js DELETED
@@ -1,4 +0,0 @@
1
- export * from './types.js';
2
- export * from './request.js';
3
- export * from './transformers/index.js';
4
- //# sourceMappingURL=browser.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"browser.js","sourceRoot":"","sources":["../src/browser.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAA;AAC1B,cAAc,cAAc,CAAA;AAC5B,cAAc,yBAAyB,CAAA"}