Comparing version 0.0.2 to 0.0.3
@@ -5,2 +5,10 @@ # Changelog | ||
### [0.0.3](https://github.com/nuxt-contrib/ohmyfetch/compare/v0.0.2...v0.0.3) (2020-12-12) | ||
### Features | ||
* bundle ufo and destr for easier bundler integration ([8f5ba88](https://github.com/nuxt-contrib/ohmyfetch/commit/8f5ba88f1ac0aa40ff2c99316da98a71d6dcc7e8)) | ||
* support `$fetch.raw` and improve docs ([f9f70a5](https://github.com/nuxt-contrib/ohmyfetch/commit/f9f70a59222bc0d0166cbe9a03eebf2a73682398)) | ||
### [0.0.2](https://github.com/nuxt-contrib/ohmyfetch/compare/v0.0.1...v0.0.2) (2020-12-09) | ||
@@ -7,0 +15,0 @@ |
@@ -9,16 +9,26 @@ declare type Fetch = typeof globalThis.fetch; | ||
} | ||
declare type $FetchInput = RequestInfo; | ||
interface $FetchOptions extends RequestInit { | ||
declare type FetchRequest = RequestInfo; | ||
interface FetchOptions extends RequestInit { | ||
baseURL?: string; | ||
response?: boolean; | ||
} | ||
declare type $Fetch<T = any> = (input: $FetchInput, opts?: $FetchOptions) => Promise<T>; | ||
interface FetchResponse<T> extends Response { | ||
data?: T; | ||
} | ||
interface $Fetch { | ||
<T = any>(request: FetchRequest, opts?: FetchOptions): Promise<T>; | ||
raw<T = any>(request: FetchRequest, opts?: FetchOptions): Promise<FetchResponse<T>>; | ||
} | ||
declare function createFetch({ fetch }: CreateFetchOptions): $Fetch; | ||
declare class FetchError extends Error { | ||
declare class FetchError<T = any> extends Error { | ||
name: 'FetchError'; | ||
request?: FetchRequest; | ||
response?: FetchResponse<T>; | ||
data?: T; | ||
} | ||
declare function createFetchError<T = any>(response: Response, input: RequestInfo, data: T): FetchError; | ||
declare function createFetchError<T = any>(request: FetchRequest, response: FetchResponse<T>): FetchError<T>; | ||
declare const $fetch: $Fetch<any>; | ||
declare const $fetch: $Fetch; | ||
export { $Fetch, $FetchInput, $FetchOptions, $fetch, CreateFetchOptions, Fetch, FetchError, RequestInfo, RequestInit, Response, createFetch, createFetchError }; | ||
export { $Fetch, $fetch, CreateFetchOptions, FetchError, FetchOptions, FetchRequest, FetchResponse, createFetch, createFetchError }; |
@@ -5,9 +5,99 @@ 'use strict'; | ||
const destr2 = require('destr'); | ||
const ufo = require('@nuxt/ufo'); | ||
// https://github.com/fastify/secure-json-parse | ||
// https://github.com/hapijs/bourne | ||
var suspectProtoRx = /"(?:_|\\u005[Ff])(?:_|\\u005[Ff])(?:p|\\u0070)(?:r|\\u0072)(?:o|\\u006[Ff])(?:t|\\u0074)(?:o|\\u006[Ff])(?:_|\\u005[Ff])(?:_|\\u005[Ff])"\s*:/; | ||
var suspectConstructorRx = /"(?:c|\\u0063)(?:o|\\u006[Ff])(?:n|\\u006[Ee])(?:s|\\u0073)(?:t|\\u0074)(?:r|\\u0072)(?:u|\\u0075)(?:c|\\u0063)(?:t|\\u0074)(?:o|\\u006[Ff])(?:r|\\u0072)"\s*:/; | ||
var JsonSigRx = /^["{[]|^-?[0-9][0-9.]{0,14}$/; | ||
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; } | ||
function jsonParseTransform(key, value) { | ||
if (key === '__proto__' || key === 'constructor') { | ||
return; | ||
} | ||
const destr2__default = /*#__PURE__*/_interopDefaultLegacy(destr2); | ||
return value; | ||
} | ||
function destr(val) { | ||
if (typeof val !== 'string') { | ||
return val; | ||
} | ||
var _lval = val.toLowerCase(); | ||
if (_lval === 'true') { | ||
return true; | ||
} | ||
if (_lval === 'false') { | ||
return false; | ||
} | ||
if (_lval === 'null') { | ||
return null; | ||
} | ||
if (_lval === 'nan') { | ||
return NaN; | ||
} | ||
if (_lval === 'infinity') { | ||
return Infinity; | ||
} | ||
if (_lval === 'undefined') { | ||
return undefined; | ||
} | ||
if (!JsonSigRx.test(val)) { | ||
return val; | ||
} | ||
try { | ||
if (suspectProtoRx.test(val) || suspectConstructorRx.test(val)) { | ||
return JSON.parse(val, jsonParseTransform); | ||
} | ||
return JSON.parse(val); | ||
} catch (_e) { | ||
return val; | ||
} | ||
} | ||
var dist = destr; | ||
function withoutTrailingSlash(input = "/") { | ||
return input.endsWith("/") ? input.slice(0, -1) : input; | ||
} | ||
function hasProtocol(inputStr) { | ||
return /^\w+:\//.test(inputStr); | ||
} | ||
function parseURL(input = "/") { | ||
if (typeof input !== "string") { | ||
if (!input || !input.url) { | ||
throw new Error(`Invalid url: ${JSON.stringify(input)}`); | ||
} | ||
return input; | ||
} | ||
const _hasProtocol = hasProtocol(input); | ||
const url = new URL(input, _hasProtocol ? void 0 : "default:/"); | ||
return {url, hasProtocol: _hasProtocol}; | ||
} | ||
function joinPath(...path) { | ||
const last = path.pop(); | ||
if (!last) { | ||
return "/"; | ||
} | ||
return path.map(withoutTrailingSlash).join("") + last; | ||
} | ||
function normalizeURL(input, stripBase) { | ||
const {url, hasProtocol: hasProtocol2} = parseURL(input); | ||
return !stripBase && hasProtocol2 ? url.href : url.pathname + url.search + url.hash; | ||
} | ||
function joinURL(input0, ...input) { | ||
const path = input.map(parseURL); | ||
const baseURL = parseURL(input0); | ||
baseURL.url.pathname = joinPath(baseURL.url.pathname, ...path.map((p) => p.url.pathname)); | ||
return normalizeURL(baseURL); | ||
} | ||
class FetchError extends Error { | ||
@@ -19,7 +109,13 @@ constructor() { | ||
} | ||
function createFetchError(response, input, data) { | ||
const message = `${response.status} ${response.statusText} (${input.toString()})`; | ||
function createFetchError(request, response) { | ||
const message = `${response.status} ${response.statusText} (${request.toString()})`; | ||
const error = new FetchError(message); | ||
Object.defineProperty(error, "request", {get() { | ||
return request; | ||
}}); | ||
Object.defineProperty(error, "response", {get() { | ||
return response; | ||
}}); | ||
Object.defineProperty(error, "data", {get() { | ||
return data; | ||
return response.data; | ||
}}); | ||
@@ -37,14 +133,19 @@ const stack = error.stack; | ||
function createFetch({fetch}) { | ||
return async function $fetch(input, opts) { | ||
if (opts && opts.baseURL && typeof input === "string") { | ||
input = ufo.joinURL(opts.baseURL, input); | ||
const raw = async function(request, opts) { | ||
if (opts && opts.baseURL && typeof request === "string") { | ||
request = joinURL(opts.baseURL, request); | ||
} | ||
const response = await fetch(input, opts); | ||
const response = await fetch(request, opts); | ||
const text = await response.text(); | ||
const data = destr2__default['default'](text); | ||
response.data = dist(text); | ||
if (!response.ok) { | ||
throw createFetchError(response, input, data); | ||
throw createFetchError(request, response); | ||
} | ||
return data; | ||
return response; | ||
}; | ||
const $fetch = function(request, opts) { | ||
return raw(request, opts).then((r) => r.data); | ||
}; | ||
$fetch.raw = raw; | ||
return $fetch; | ||
} | ||
@@ -51,0 +152,0 @@ |
131
node.js
@@ -6,4 +6,2 @@ 'use strict'; | ||
const nodeFetch = require('node-fetch'); | ||
const destr2 = require('destr'); | ||
const ufo = require('@nuxt/ufo'); | ||
@@ -13,4 +11,100 @@ function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; } | ||
const nodeFetch__default = /*#__PURE__*/_interopDefaultLegacy(nodeFetch); | ||
const destr2__default = /*#__PURE__*/_interopDefaultLegacy(destr2); | ||
// https://github.com/fastify/secure-json-parse | ||
// https://github.com/hapijs/bourne | ||
var suspectProtoRx = /"(?:_|\\u005[Ff])(?:_|\\u005[Ff])(?:p|\\u0070)(?:r|\\u0072)(?:o|\\u006[Ff])(?:t|\\u0074)(?:o|\\u006[Ff])(?:_|\\u005[Ff])(?:_|\\u005[Ff])"\s*:/; | ||
var suspectConstructorRx = /"(?:c|\\u0063)(?:o|\\u006[Ff])(?:n|\\u006[Ee])(?:s|\\u0073)(?:t|\\u0074)(?:r|\\u0072)(?:u|\\u0075)(?:c|\\u0063)(?:t|\\u0074)(?:o|\\u006[Ff])(?:r|\\u0072)"\s*:/; | ||
var JsonSigRx = /^["{[]|^-?[0-9][0-9.]{0,14}$/; | ||
function jsonParseTransform(key, value) { | ||
if (key === '__proto__' || key === 'constructor') { | ||
return; | ||
} | ||
return value; | ||
} | ||
function destr(val) { | ||
if (typeof val !== 'string') { | ||
return val; | ||
} | ||
var _lval = val.toLowerCase(); | ||
if (_lval === 'true') { | ||
return true; | ||
} | ||
if (_lval === 'false') { | ||
return false; | ||
} | ||
if (_lval === 'null') { | ||
return null; | ||
} | ||
if (_lval === 'nan') { | ||
return NaN; | ||
} | ||
if (_lval === 'infinity') { | ||
return Infinity; | ||
} | ||
if (_lval === 'undefined') { | ||
return undefined; | ||
} | ||
if (!JsonSigRx.test(val)) { | ||
return val; | ||
} | ||
try { | ||
if (suspectProtoRx.test(val) || suspectConstructorRx.test(val)) { | ||
return JSON.parse(val, jsonParseTransform); | ||
} | ||
return JSON.parse(val); | ||
} catch (_e) { | ||
return val; | ||
} | ||
} | ||
var dist = destr; | ||
function withoutTrailingSlash(input = "/") { | ||
return input.endsWith("/") ? input.slice(0, -1) : input; | ||
} | ||
function hasProtocol(inputStr) { | ||
return /^\w+:\//.test(inputStr); | ||
} | ||
function parseURL(input = "/") { | ||
if (typeof input !== "string") { | ||
if (!input || !input.url) { | ||
throw new Error(`Invalid url: ${JSON.stringify(input)}`); | ||
} | ||
return input; | ||
} | ||
const _hasProtocol = hasProtocol(input); | ||
const url = new URL(input, _hasProtocol ? void 0 : "default:/"); | ||
return {url, hasProtocol: _hasProtocol}; | ||
} | ||
function joinPath(...path) { | ||
const last = path.pop(); | ||
if (!last) { | ||
return "/"; | ||
} | ||
return path.map(withoutTrailingSlash).join("") + last; | ||
} | ||
function normalizeURL(input, stripBase) { | ||
const {url, hasProtocol: hasProtocol2} = parseURL(input); | ||
return !stripBase && hasProtocol2 ? url.href : url.pathname + url.search + url.hash; | ||
} | ||
function joinURL(input0, ...input) { | ||
const path = input.map(parseURL); | ||
const baseURL = parseURL(input0); | ||
baseURL.url.pathname = joinPath(baseURL.url.pathname, ...path.map((p) => p.url.pathname)); | ||
return normalizeURL(baseURL); | ||
} | ||
class FetchError extends Error { | ||
@@ -22,7 +116,13 @@ constructor() { | ||
} | ||
function createFetchError(response, input, data) { | ||
const message = `${response.status} ${response.statusText} (${input.toString()})`; | ||
function createFetchError(request, response) { | ||
const message = `${response.status} ${response.statusText} (${request.toString()})`; | ||
const error = new FetchError(message); | ||
Object.defineProperty(error, "request", {get() { | ||
return request; | ||
}}); | ||
Object.defineProperty(error, "response", {get() { | ||
return response; | ||
}}); | ||
Object.defineProperty(error, "data", {get() { | ||
return data; | ||
return response.data; | ||
}}); | ||
@@ -40,14 +140,19 @@ const stack = error.stack; | ||
function createFetch({fetch}) { | ||
return async function $fetch(input, opts) { | ||
if (opts && opts.baseURL && typeof input === "string") { | ||
input = ufo.joinURL(opts.baseURL, input); | ||
const raw = async function(request, opts) { | ||
if (opts && opts.baseURL && typeof request === "string") { | ||
request = joinURL(opts.baseURL, request); | ||
} | ||
const response = await fetch(input, opts); | ||
const response = await fetch(request, opts); | ||
const text = await response.text(); | ||
const data = destr2__default['default'](text); | ||
response.data = dist(text); | ||
if (!response.ok) { | ||
throw createFetchError(response, input, data); | ||
throw createFetchError(request, response); | ||
} | ||
return data; | ||
return response; | ||
}; | ||
const $fetch = function(request, opts) { | ||
return raw(request, opts).then((r) => r.data); | ||
}; | ||
$fetch.raw = raw; | ||
return $fetch; | ||
} | ||
@@ -54,0 +159,0 @@ |
{ | ||
"name": "ohmyfetch", | ||
"version": "0.0.2", | ||
"version": "0.0.3", | ||
"description": "oh-my-fetch", | ||
@@ -32,4 +32,2 @@ "repository": "nuxt-contrib/ohmyfetch", | ||
"dependencies": { | ||
"@nuxt/ufo": "^0.0.4", | ||
"destr": "^1.0.1", | ||
"node-fetch": "^2.6.1" | ||
@@ -39,2 +37,3 @@ }, | ||
"@nuxt/h2": "^0.0.14", | ||
"@nuxt/ufo": "^0.0.4", | ||
"@nuxtjs/eslint-config-typescript": "latest", | ||
@@ -45,2 +44,3 @@ "@types/flat": "latest", | ||
"@types/node-fetch": "^2.5.7", | ||
"destr": "^1.0.1", | ||
"eslint": "latest", | ||
@@ -47,0 +47,0 @@ "jest": "latest", |
@@ -1,8 +0,14 @@ | ||
<!-- [![npm version][npm-version-src]][npm-version-href] | ||
[![npm downloads][npm-downloads-src]][npm-downloads-href] | ||
[![npm version][npm-version-src]][npm-version-href] | ||
[![Github Actions][github-actions-src]][github-actions-href] | ||
[![Codecov][codecov-src]][codecov-href] --> | ||
[![Codecov][codecov-src]][codecov-href] | ||
[![bundle][bundle-src]][bundle-href] | ||
# 😱 ohmyfetch | ||
![ohmyfetch](https://user-images.githubusercontent.com/904724/101663230-bb578c80-3a4a-11eb-89eb-14cd3e08dd8c.png) | ||
<!-- # 😱 ohmyfetch --> | ||
<!-- [![npm downloads][npm-downloads-src]][npm-downloads-href] --> | ||
## 🚀 Quick Start | ||
@@ -33,10 +39,12 @@ | ||
![oh-my-fetch](https://media.giphy.com/media/Dn1QRA9hqMcoMz9zVZ/giphy.gif) | ||
<details> | ||
<summary>Spoiler</summary> | ||
<img src="https://media.giphy.com/media/Dn1QRA9hqMcoMz9zVZ/giphy.gif"> | ||
</details> | ||
## 🤔 Why? | ||
### ✔️ Parse Response | ||
## ✔️ Parsing Response | ||
**`$fetch`:** | ||
`$fetch` Smartly parses JSON and native valuesusing [destr](https://github.com/nuxt-contrib/destr) and fallback to text if cannot parse | ||
@@ -47,17 +55,8 @@ ```js | ||
- Using [destr](https://github.com/nuxt-contrib/destr) | ||
- Smartly parse JSON and native values like `true` | ||
- Fallback to text if cannot parse | ||
- Secure against prototype pollution | ||
## ✔️ Handling Errors | ||
**`fetch`:** | ||
`$fetch` Automatically throw errors when `response.ok` is `false` with a friendly error message and compact stack (hiding internals). | ||
```js | ||
const { users } = await fetch('/api/users').then(r => r.json()) | ||
``` | ||
Parsed error body is available with `error.data`. You may also use `FetchError` type. | ||
### ✔️ Handle Errors | ||
**`$fetch`:** | ||
```ts | ||
@@ -69,35 +68,22 @@ await $fetch('http://google.com/404') | ||
- Automatically throw errors when `response.ok` is `false` | ||
- Friendly error message with compact stack (hiding internals) | ||
- Parsed error body is available with `error.data` | ||
In order to bypass errors as reponse you can use `error.data`: | ||
**`fetch`:** | ||
```js | ||
const resonse = await fetch('http://google.com/404') | ||
// You need to manually check response.ok and throw an error | ||
```ts | ||
await $fetch(...).catch((error) => error.data) | ||
``` | ||
### ✔️ Type Friendly | ||
## ✔️ Type Friendly | ||
**`$fetch`:** | ||
Response can be type assisted: | ||
```ts | ||
const { article } = await $fetch<Article>(`/api/article/${id}`) | ||
// article object is type assisted | ||
// Auto complete working with article.id | ||
``` | ||
- Expected response type can be specified | ||
**`fetch`:** | ||
## ✔️ Support baseURL | ||
```js | ||
const { article } = await fetch(`/api/article/${id}`).then(r => r.json()) | ||
// article type is any | ||
``` | ||
By setting `baseURL` option `$fetch` prepends it with respecting to trailing/leading slashes and query params for baseURL using [ufo](https://github.com/nuxt-contrib/ufo): | ||
### ✔️ Support baseURL | ||
**`$fetch`:** | ||
```js | ||
@@ -107,15 +93,15 @@ await $fetch('/config', { baseURL }) | ||
- Allow making factory functions to add baseURL | ||
- Prepend baseURL with respecting trailing/leading slashes and query params for baseURL (using [ufo](https://github.com/nuxt-contrib/ufo)) | ||
## 🍣 Access to Raw Response | ||
**`fetch`:** | ||
If you need to access raw response (for headers, etc), can use `$fetch.raw`: | ||
```js | ||
await $fetch(baseURL + '/config') | ||
const response = await $fetch.raw('/sushi') | ||
// response.data | ||
// response.headers | ||
// ... | ||
``` | ||
### ✔️ Univeral | ||
Supporting browsers, workers and NodeJS | ||
## 📦 Bundler Notes | ||
@@ -132,5 +118,5 @@ | ||
Using same name of `fetch` can be confusing since API is different but still it is a fetch so using closesest possible alternative. | ||
Using the same name of `fetch` can be confusing since API is different but still it is a fetch so using closest possible alternative. | ||
**Why note having default export?** | ||
**Why not having default export?** | ||
@@ -145,3 +131,3 @@ Default exports are always risky to be mixed with CommonJS exports. | ||
If you need to support legacy users, can optionally transpile the library to build pipelines. | ||
If you need to support legacy users, can optionally transpile the library in build pipeline. | ||
@@ -164,1 +150,5 @@ ## License | ||
[codecov-href]: https://codecov.io/gh/nuxt-contrib/ohmyfetch | ||
[bundle-src]: https://img.shields.io/bundlephobia/minzip/ohmyfetch?style=flat-square | ||
[bundle-href]: https://bundlephobia.com/result?p=ohmyfetch | ||
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
28646
1
684
16
148
- Removed@nuxt/ufo@^0.0.4
- Removeddestr@^1.0.1
- Removed@nuxt/ufo@0.0.4(transitive)
- Removeddestr@1.2.2(transitive)