From 3a14499523e7b48159fa6d97baad9012d98af109 Mon Sep 17 00:00:00 2001 From: Julien Elbaz Date: Fri, 6 Oct 2017 01:50:34 +0200 Subject: [PATCH] :white_check_mark: Write perfs() tests --- dist/bundle/wretch.js | 2 +- dist/bundle/wretch.js.map | 2 +- dist/config.d.ts | 1 + dist/config.js | 3 +- dist/config.js.map | 2 +- dist/resolver.js | 30 ++++++--- dist/resolver.js.map | 2 +- package.json | 2 +- test/{test.js => wretch.spec.js} | 104 +++++++++++++++++++++---------- 9 files changed, 98 insertions(+), 50 deletions(-) rename test/{test.js => wretch.spec.js} (68%) diff --git a/dist/bundle/wretch.js b/dist/bundle/wretch.js index 55b0ab1..963169e 100644 --- a/dist/bundle/wretch.js +++ b/dist/bundle/wretch.js @@ -1,2 +1,2 @@ -!function(t,r){"object"==typeof exports&&"undefined"!=typeof module?module.exports=r():"function"==typeof define&&define.amd?define(r):t.wretch=r()}(this,function(){"use strict";const t=Object.assign||function(t){for(var r,o=1;o (catchers: Map void> = new Map()) => (opts = {}) => {\n const req = (conf.polyfills.fetch || fetch)(url, mix(conf.defaults, opts))\n const wrapper: Promise = req.then(response => {\n if (!response.ok) {\n return response[conf.errorType || \"text\"]().then(_ => {\n const err = new Error(_)\n err[conf.errorType] = _\n err[\"status\"] = response.status\n err[\"response\"] = response\n throw err\n })\n }\n return response\n })\n\n type TypeParser = (funName: string | null) => (cb?: (type: Type) => Result) => Promise\n\n const doCatch = (promise: Promise): Promise => {\n return promise.catch(err => {\n if(catchers.has(err.status))\n catchers.get(err.status)(err)\n else\n throw err\n })\n }\n const wrapTypeParser: TypeParser = (funName) => (cb) => funName ?\n doCatch(wrapper.then(_ => _ && _[funName]()).then(_ => _ && cb && cb(_) || _)) :\n doCatch(wrapper.then(_ => _ && cb && cb(_) || _))\n\n const responseTypes: {\n res: (cb?: (type: Response) => Result) => Promise,\n json: (cb?: (type: {[key: string]: any}) => Result) => Promise,\n blob: (cb?: (type: Blob) => Result) => Promise,\n formData: (cb?: (type: FormData) => Result) => Promise,\n arrayBuffer: (cb?: (type: ArrayBuffer) => Result) => Promise,\n text: (cb?: (type: string) => Result) => Promise,\n perfs: (cb?: (type: any) => void) => typeof responseTypes,\n error: (code: number, cb: any) => typeof responseTypes,\n badRequest: (cb: (error: WretcherError) => void) => typeof responseTypes,\n unauthorized: (cb: (error: WretcherError) => void) => typeof responseTypes,\n forbidden: (cb: (error: WretcherError) => void) => typeof responseTypes,\n notFound: (cb: (error: WretcherError) => void) => typeof responseTypes,\n timeout: (cb: (error: WretcherError) => void) => typeof responseTypes,\n internalError: (cb: (error: WretcherError) => void) => typeof responseTypes\n } = {\n /**\n * Retrieves the raw result as a promise.\n */\n res: wrapTypeParser(null),\n /**\n * Retrieves the result as a parsed JSON object.\n */\n json: wrapTypeParser(\"json\"),\n /**\n * Retrieves the result as a Blob object.\n */\n blob: wrapTypeParser(\"blob\"),\n /**\n * Retrieves the result as a FormData object.\n */\n formData: wrapTypeParser(\"formData\"),\n /**\n * Retrieves the result as an ArrayBuffer object.\n */\n arrayBuffer: wrapTypeParser(\"arrayBuffer\"),\n /**\n * Retrieves the result as a string.\n */\n text: wrapTypeParser(\"text\"),\n /**\n * Only for browsers !\n *\n * Performs a callback on the API performance timings when they will be available.\n */\n perfs: cb => {\n if(cb && typeof self !== \"undefined\" && typeof self[\"PerformanceObserver\"] !== \"undefined\") {\n wrapper.then(res => {\n const observer = new self[\"PerformanceObserver\"](entries => {\n if(res) cb(entries.getEntriesByName(res.url).reverse()[0])\n observer.disconnect()\n })\n observer.observe({entryTypes: [\"resource\"]})\n })\n }\n return responseTypes\n },\n /**\n * Catches an http response with a specific error code and performs a callback.\n */\n error(code: number, cb) {\n catchers.set(code, cb)\n return responseTypes\n },\n /**\n * Catches a bad request (http code 400) and performs a callback.\n */\n badRequest: cb => responseTypes.error(400, cb),\n /**\n * Catches an unauthorized request (http code 401) and performs a callback.\n */\n unauthorized: cb => responseTypes.error(401, cb),\n /**\n * Catches a forbidden request (http code 403) and performs a callback.\n */\n forbidden: cb => responseTypes.error(403, cb),\n /**\n * Catches a \"not found\" request (http code 404) and performs a callback.\n */\n notFound: cb => responseTypes.error(404, cb),\n /**\n * Catches a timeout (http code 408) and performs a callback.\n */\n timeout: cb => responseTypes.error(408, cb),\n /**\n * Catches an internal server error (http code 500) and performs a callback.\n */\n internalError: cb => responseTypes.error(500, cb)\n }\n\n return responseTypes\n}\n","import { mix } from \"./mix\"\nimport conf from \"./config\"\nimport { resolver, WretcherError } from \"./resolver\"\n\n/**\n * The Wretcher class used to perform easy fetch requests.\n *\n * Immutability : almost every method of this class return a fresh Wretcher object.\n */\nexport class Wretcher {\n\n protected constructor(\n private _url: string,\n private _options: RequestInit = {},\n private _catchers: Map void> = new Map()) {}\n\n static factory(url = \"\", opts: RequestInit = {}) { return new Wretcher(url, opts) }\n private selfFactory({ url = this._url, options = this._options, catchers = this._catchers } = {}) {\n return new Wretcher(url, options, catchers)\n }\n\n /**\n * Sets the default fetch options used for every subsequent fetch call.\n * @param opts New default options\n * @param mixin If true, mixes in instead of replacing the existing options\n */\n defaults(opts: RequestInit, mixin = false) {\n conf.defaults = mixin ? conf.defaults = mix(conf.defaults, opts) : opts\n return this\n }\n\n /**\n * Sets the method (text, json ...) used to parse the data contained in the response body in case of an HTTP error.\n *\n * Persists for every subsequent requests.\n *\n * Default is \"text\".\n */\n errorType(method: \"text\" | \"json\") {\n conf.errorType = method\n return this\n }\n\n /**\n * Sets the non-global polyfills which will be used for every subsequent calls.\n *\n * Needed for libraries like [fetch-ponyfill](https://github.com/qubyte/fetch-ponyfill).\n *\n * @param polyfills An object containing the polyfills.\n */\n polyfills(polyfills: Partial) {\n conf.polyfills = { ...conf.polyfills, ...polyfills}\n return this\n }\n\n /**\n * Returns a new Wretcher object with the url specified and the same options.\n * @param url String url\n */\n url(url: string) {\n return this.selfFactory({ url })\n }\n\n /**\n * Returns a wretch factory which, when called, creates a new Wretcher object with the base url as an url prefix.\n * @param baseurl The base url\n */\n baseUrl(baseurl: string) {\n return (url = \"\", opts: RequestInit = {}) =>\n this.selfFactory({ url: baseurl + url, options: mix(this._options, opts) })\n }\n\n /**\n * Returns a new Wretcher object with the same url and new options.\n * @param options New options\n * @param mixin If true, mixes in instead of replacing the existing options\n */\n options(options: RequestInit, mixin = false) {\n return this.selfFactory({ options: mixin ? mix(this._options, options) : options })\n }\n\n /**\n * Converts a javascript object to query parameters,\n * then appends this query string to the current url.\n *\n * ```\n * let w = wretch(\"http://example.com\") // url is http://example.com\n * w = w.query({ a: 1, b : 2 }) // url is now http://example.com?a=1&b=2\n * ```\n *\n * @param qp An object which will be converted.\n */\n query(qp: object) {\n return this.selfFactory({ url: appendQueryParams(this._url, qp) })\n }\n\n /**\n * Set request headers.\n * @param headerValues An object containing header keys and values\n */\n headers(headerValues: { [headerName: string]: any }) {\n return this.selfFactory({ options: mix(this._options, { headers: headerValues }) })\n }\n\n /**\n * Shortcut to set the \"Accept\" header.\n * @param headerValue Header value\n */\n accept(headerValue: string) {\n return this.headers({ Accept : headerValue })\n }\n\n /**\n * Shortcut to set the \"Content-Type\" header.\n * @param headerValue Header value\n */\n content(headerValue: string) {\n return this.headers({ \"Content-Type\" : headerValue })\n }\n\n /**\n * Adds a default catcher which will be called on every subsequent request error when the error code matches.\n * @param code Error code\n * @param catcher: The catcher method\n */\n catcher(code: number, catcher: (error: WretcherError) => void) {\n const newMap = new Map(this._catchers)\n newMap.set(code, catcher)\n return this.selfFactory({ catchers: newMap })\n }\n\n /**\n * Performs a get request.\n */\n get(opts = {}) {\n return resolver(this._url)(this._catchers)(mix(opts, this._options))\n }\n /**\n * Performs a delete request.\n */\n delete(opts = {}) {\n return resolver(this._url)(this._catchers)({ ...mix(opts, this._options), method: \"DELETE\" })\n }\n /**\n * Performs a put request.\n */\n put(opts = {}) {\n return resolver(this._url)(this._catchers)({ ...mix(opts, this._options), method: \"PUT\" })\n }\n /**\n * Performs a post request.\n */\n post(opts = {}) {\n return resolver(this._url)(this._catchers)({ ...mix(opts, this._options), method: \"POST\" })\n }\n /**\n * Performs a patch request.\n */\n patch(opts = {}) {\n return resolver(this._url)(this._catchers)({ ...mix(opts, this._options), method: \"PATCH\" })\n }\n\n /**\n * Sets the request body with any content.\n * @param contents The body contents\n */\n body(contents: any) {\n return this.selfFactory({ options: { ...this._options, body: contents }})\n }\n /**\n * Sets the content type header, stringifies an object and sets the request body.\n * @param jsObject An object which will be serialized into a JSON\n */\n json(jsObject: object) {\n return this.content(\"application/json\").body(JSON.stringify(jsObject))\n }\n /**\n * Converts the javascript object to a FormData and sets the request body.\n * @param formObject An object which will be converted to a FormData\n */\n formData(formObject: object) {\n const formData = new (conf.polyfills.FormData || FormData)()\n for(const key in formObject) {\n if(formObject[key] instanceof Array) {\n for(const item of formObject[key])\n formData.append(key + \"[]\", item)\n } else {\n formData.append(key, formObject[key])\n }\n }\n\n return this.body(formData)\n }\n}\n\n// Internal helpers\n\nconst appendQueryParams = (url: string, qp: object) => {\n const usp = new (conf.polyfills.URLSearchParams || URLSearchParams)()\n const index = url.indexOf(\"?\")\n for(const key in qp) {\n if(qp[key] instanceof Array) {\n for(const val of qp[key])\n usp.append(key, val)\n } else {\n usp.append(key, qp[key])\n }\n }\n return ~index ?\n `${url.substring(0, index)}?${usp.toString()}` :\n `${url}?${usp.toString()}`\n}\n","import { Wretcher } from \"./wretcher\"\n\n/**\n * Return a fresh Wretcher instance.\n */\nexport default Wretcher.factory\n"],"names":["mix","one","two","mergeArrays","clone","prop","hasOwnProperty","Array","defaults","errorType","polyfills","fetch","FormData","URLSearchParams","resolver","url","catchers","Map","opts","wrapper","conf","then","response","ok","_","err","Error","status","doCatch","promise","catch","has","get","wrapTypeParser","funName","cb","responseTypes","res","json","blob","formData","arrayBuffer","text","perfs","self","observer","entries","getEntriesByName","reverse","disconnect","observe","entryTypes","error","code","set","badRequest","unauthorized","forbidden","notFound","timeout","internalError","_url","_options","_catchers","this","Wretcher","_a","_b","_c","_d","options","_e","mixin","method","selfFactory","baseurl","_this","qp","appendQueryParams","headerValues","headers","headerValue","Accept","Content-Type","catcher","newMap","contents","body","jsObject","content","JSON","stringify","formObject","key","_i","item","append","usp","index","indexOf","val","substring","toString","factory"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"wretch.js","sources":["../../src/mix.ts","../../src/config.ts","../../src/resolver.ts","../../src/wretcher.ts","../../src/index.ts"],"sourcesContent":["export const mix = function(one: object, two: object, mergeArrays: boolean = false) {\n if(!one || !two || typeof one !== \"object\" || typeof two !== \"object\")\n return one\n\n const clone = { ...one }\n for(const prop in two) {\n if(two.hasOwnProperty(prop)) {\n if(two[prop] instanceof Array && one[prop] instanceof Array) {\n clone[prop] = mergeArrays ? [ ...one[prop], ...two[prop] ] : two[prop]\n } else if(typeof two[prop] === \"object\" && typeof one[prop] === \"object\") {\n clone[prop] = mix(one[prop], two[prop], mergeArrays)\n } else {\n clone[prop] = two[prop]\n }\n }\n }\n\n return clone\n}\n","export default {\n // Default options\n defaults: {},\n // Error type\n errorType: null,\n // Polyfills\n polyfills: {\n fetch: null,\n FormData: null,\n URLSearchParams: null,\n PerformanceObserver: null\n }\n}\n","import { mix } from \"./mix\"\nimport conf from \"./config\"\n\nexport type WretcherError = Error & { status: number, response: Response, text?: string, json?: any }\n\nexport const resolver = url => (catchers: Map void> = new Map()) => (opts = {}) => {\n const req = (conf.polyfills.fetch || fetch)(url, mix(conf.defaults, opts))\n const wrapper: Promise = req.then(response => {\n if (!response.ok) {\n return response[conf.errorType || \"text\"]().then(_ => {\n const err = new Error(_)\n err[conf.errorType] = _\n err[\"status\"] = response.status\n err[\"response\"] = response\n throw err\n })\n }\n return response\n })\n\n type TypeParser = (funName: string | null) => (cb?: (type: Type) => Result) => Promise\n\n const doCatch = (promise: Promise): Promise => {\n return promise.catch(err => {\n if(catchers.has(err.status))\n catchers.get(err.status)(err)\n else\n throw err\n })\n }\n const wrapTypeParser: TypeParser = (funName) => (cb) => funName ?\n doCatch(wrapper.then(_ => _ && _[funName]()).then(_ => _ && cb && cb(_) || _)) :\n doCatch(wrapper.then(_ => _ && cb && cb(_) || _))\n\n const responseTypes: {\n res: (cb?: (type: Response) => Result) => Promise,\n json: (cb?: (type: {[key: string]: any}) => Result) => Promise,\n blob: (cb?: (type: Blob) => Result) => Promise,\n formData: (cb?: (type: FormData) => Result) => Promise,\n arrayBuffer: (cb?: (type: ArrayBuffer) => Result) => Promise,\n text: (cb?: (type: string) => Result) => Promise,\n perfs: (cb?: (type: any) => void) => typeof responseTypes,\n error: (code: number, cb: any) => typeof responseTypes,\n badRequest: (cb: (error: WretcherError) => void) => typeof responseTypes,\n unauthorized: (cb: (error: WretcherError) => void) => typeof responseTypes,\n forbidden: (cb: (error: WretcherError) => void) => typeof responseTypes,\n notFound: (cb: (error: WretcherError) => void) => typeof responseTypes,\n timeout: (cb: (error: WretcherError) => void) => typeof responseTypes,\n internalError: (cb: (error: WretcherError) => void) => typeof responseTypes\n } = {\n /**\n * Retrieves the raw result as a promise.\n */\n res: wrapTypeParser(null),\n /**\n * Retrieves the result as a parsed JSON object.\n */\n json: wrapTypeParser(\"json\"),\n /**\n * Retrieves the result as a Blob object.\n */\n blob: wrapTypeParser(\"blob\"),\n /**\n * Retrieves the result as a FormData object.\n */\n formData: wrapTypeParser(\"formData\"),\n /**\n * Retrieves the result as an ArrayBuffer object.\n */\n arrayBuffer: wrapTypeParser(\"arrayBuffer\"),\n /**\n * Retrieves the result as a string.\n */\n text: wrapTypeParser(\"text\"),\n /**\n * Performs a callback on the API performance timings of the request.\n *\n * Warning: Still experimental on browsers and node.js\n */\n perfs: cb => {\n const perfObserver = conf.polyfills.PerformanceObserver || (typeof self !== \"undefined\" ? self[\"PerformanceObserver\"] : null)\n if(cb && perfObserver) {\n let entries = null\n let callback = null\n const observer = new perfObserver(e => {\n entries = e.getEntries()\n if(callback) callback()\n observer.disconnect()\n })\n observer.observe({ entryTypes: [\"resource\", \"measure\"] })\n req.then(res => {\n if(res) {\n if(entries)\n cb(entries.reverse().find(_ => _.name === res.url))\n else\n callback = () => cb(entries.reverse().find(_ => _.name === res.url))\n }\n })\n }\n return responseTypes\n },\n /**\n * Catches an http response with a specific error code and performs a callback.\n */\n error(code: number, cb) {\n catchers.set(code, cb)\n return responseTypes\n },\n /**\n * Catches a bad request (http code 400) and performs a callback.\n */\n badRequest: cb => responseTypes.error(400, cb),\n /**\n * Catches an unauthorized request (http code 401) and performs a callback.\n */\n unauthorized: cb => responseTypes.error(401, cb),\n /**\n * Catches a forbidden request (http code 403) and performs a callback.\n */\n forbidden: cb => responseTypes.error(403, cb),\n /**\n * Catches a \"not found\" request (http code 404) and performs a callback.\n */\n notFound: cb => responseTypes.error(404, cb),\n /**\n * Catches a timeout (http code 408) and performs a callback.\n */\n timeout: cb => responseTypes.error(408, cb),\n /**\n * Catches an internal server error (http code 500) and performs a callback.\n */\n internalError: cb => responseTypes.error(500, cb)\n }\n\n return responseTypes\n}\n","import { mix } from \"./mix\"\nimport conf from \"./config\"\nimport { resolver, WretcherError } from \"./resolver\"\n\n/**\n * The Wretcher class used to perform easy fetch requests.\n *\n * Immutability : almost every method of this class return a fresh Wretcher object.\n */\nexport class Wretcher {\n\n protected constructor(\n private _url: string,\n private _options: RequestInit = {},\n private _catchers: Map void> = new Map()) {}\n\n static factory(url = \"\", opts: RequestInit = {}) { return new Wretcher(url, opts) }\n private selfFactory({ url = this._url, options = this._options, catchers = this._catchers } = {}) {\n return new Wretcher(url, options, catchers)\n }\n\n /**\n * Sets the default fetch options used for every subsequent fetch call.\n * @param opts New default options\n * @param mixin If true, mixes in instead of replacing the existing options\n */\n defaults(opts: RequestInit, mixin = false) {\n conf.defaults = mixin ? conf.defaults = mix(conf.defaults, opts) : opts\n return this\n }\n\n /**\n * Sets the method (text, json ...) used to parse the data contained in the response body in case of an HTTP error.\n *\n * Persists for every subsequent requests.\n *\n * Default is \"text\".\n */\n errorType(method: \"text\" | \"json\") {\n conf.errorType = method\n return this\n }\n\n /**\n * Sets the non-global polyfills which will be used for every subsequent calls.\n *\n * Needed for libraries like [fetch-ponyfill](https://github.com/qubyte/fetch-ponyfill).\n *\n * @param polyfills An object containing the polyfills.\n */\n polyfills(polyfills: Partial) {\n conf.polyfills = { ...conf.polyfills, ...polyfills}\n return this\n }\n\n /**\n * Returns a new Wretcher object with the url specified and the same options.\n * @param url String url\n */\n url(url: string) {\n return this.selfFactory({ url })\n }\n\n /**\n * Returns a wretch factory which, when called, creates a new Wretcher object with the base url as an url prefix.\n * @param baseurl The base url\n */\n baseUrl(baseurl: string) {\n return (url = \"\", opts: RequestInit = {}) =>\n this.selfFactory({ url: baseurl + url, options: mix(this._options, opts) })\n }\n\n /**\n * Returns a new Wretcher object with the same url and new options.\n * @param options New options\n * @param mixin If true, mixes in instead of replacing the existing options\n */\n options(options: RequestInit, mixin = false) {\n return this.selfFactory({ options: mixin ? mix(this._options, options) : options })\n }\n\n /**\n * Converts a javascript object to query parameters,\n * then appends this query string to the current url.\n *\n * ```\n * let w = wretch(\"http://example.com\") // url is http://example.com\n * w = w.query({ a: 1, b : 2 }) // url is now http://example.com?a=1&b=2\n * ```\n *\n * @param qp An object which will be converted.\n */\n query(qp: object) {\n return this.selfFactory({ url: appendQueryParams(this._url, qp) })\n }\n\n /**\n * Set request headers.\n * @param headerValues An object containing header keys and values\n */\n headers(headerValues: { [headerName: string]: any }) {\n return this.selfFactory({ options: mix(this._options, { headers: headerValues }) })\n }\n\n /**\n * Shortcut to set the \"Accept\" header.\n * @param headerValue Header value\n */\n accept(headerValue: string) {\n return this.headers({ Accept : headerValue })\n }\n\n /**\n * Shortcut to set the \"Content-Type\" header.\n * @param headerValue Header value\n */\n content(headerValue: string) {\n return this.headers({ \"Content-Type\" : headerValue })\n }\n\n /**\n * Adds a default catcher which will be called on every subsequent request error when the error code matches.\n * @param code Error code\n * @param catcher: The catcher method\n */\n catcher(code: number, catcher: (error: WretcherError) => void) {\n const newMap = new Map(this._catchers)\n newMap.set(code, catcher)\n return this.selfFactory({ catchers: newMap })\n }\n\n /**\n * Performs a get request.\n */\n get(opts = {}) {\n return resolver(this._url)(this._catchers)(mix(opts, this._options))\n }\n /**\n * Performs a delete request.\n */\n delete(opts = {}) {\n return resolver(this._url)(this._catchers)({ ...mix(opts, this._options), method: \"DELETE\" })\n }\n /**\n * Performs a put request.\n */\n put(opts = {}) {\n return resolver(this._url)(this._catchers)({ ...mix(opts, this._options), method: \"PUT\" })\n }\n /**\n * Performs a post request.\n */\n post(opts = {}) {\n return resolver(this._url)(this._catchers)({ ...mix(opts, this._options), method: \"POST\" })\n }\n /**\n * Performs a patch request.\n */\n patch(opts = {}) {\n return resolver(this._url)(this._catchers)({ ...mix(opts, this._options), method: \"PATCH\" })\n }\n\n /**\n * Sets the request body with any content.\n * @param contents The body contents\n */\n body(contents: any) {\n return this.selfFactory({ options: { ...this._options, body: contents }})\n }\n /**\n * Sets the content type header, stringifies an object and sets the request body.\n * @param jsObject An object which will be serialized into a JSON\n */\n json(jsObject: object) {\n return this.content(\"application/json\").body(JSON.stringify(jsObject))\n }\n /**\n * Converts the javascript object to a FormData and sets the request body.\n * @param formObject An object which will be converted to a FormData\n */\n formData(formObject: object) {\n const formData = new (conf.polyfills.FormData || FormData)()\n for(const key in formObject) {\n if(formObject[key] instanceof Array) {\n for(const item of formObject[key])\n formData.append(key + \"[]\", item)\n } else {\n formData.append(key, formObject[key])\n }\n }\n\n return this.body(formData)\n }\n}\n\n// Internal helpers\n\nconst appendQueryParams = (url: string, qp: object) => {\n const usp = new (conf.polyfills.URLSearchParams || URLSearchParams)()\n const index = url.indexOf(\"?\")\n for(const key in qp) {\n if(qp[key] instanceof Array) {\n for(const val of qp[key])\n usp.append(key, val)\n } else {\n usp.append(key, qp[key])\n }\n }\n return ~index ?\n `${url.substring(0, index)}?${usp.toString()}` :\n `${url}?${usp.toString()}`\n}\n","import { Wretcher } from \"./wretcher\"\n\n/**\n * Return a fresh Wretcher instance.\n */\nexport default Wretcher.factory\n"],"names":["mix","one","two","mergeArrays","clone","prop","hasOwnProperty","Array","defaults","errorType","polyfills","fetch","FormData","URLSearchParams","PerformanceObserver","resolver","url","catchers","Map","opts","req","conf","wrapper","then","response","ok","_","err","Error","status","doCatch","promise","catch","has","get","wrapTypeParser","funName","cb","responseTypes","res","json","blob","formData","arrayBuffer","text","perfs","perfObserver","self","entries_1","callback_1","observer_1","e","getEntries","disconnect","observe","entryTypes","reverse","find","name","error","code","set","badRequest","unauthorized","forbidden","notFound","timeout","internalError","_url","_options","_catchers","this","Wretcher","_a","_b","_c","_d","options","_e","mixin","method","selfFactory","baseurl","_this","qp","appendQueryParams","headerValues","headers","headerValue","Accept","Content-Type","catcher","newMap","contents","body","jsObject","content","JSON","stringify","formObject","key","_i","item","append","usp","index","indexOf","val","substring","toString","factory"],"mappings":"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"} \ No newline at end of file diff --git a/dist/config.d.ts b/dist/config.d.ts index f380532..daa2347 100644 --- a/dist/config.d.ts +++ b/dist/config.d.ts @@ -5,6 +5,7 @@ declare const _default: { fetch: any; FormData: any; URLSearchParams: any; + PerformanceObserver: any; }; }; export default _default; diff --git a/dist/config.js b/dist/config.js index 0832aaf..4a20c43 100644 --- a/dist/config.js +++ b/dist/config.js @@ -7,7 +7,8 @@ export default { polyfills: { fetch: null, FormData: null, - URLSearchParams: null + URLSearchParams: null, + PerformanceObserver: null } }; //# sourceMappingURL=config.js.map \ No newline at end of file diff --git a/dist/config.js.map b/dist/config.js.map index 91918c2..48f5f80 100644 --- a/dist/config.js.map +++ b/dist/config.js.map @@ -1 +1 @@ -{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,eAAe;IACX,kBAAkB;IAClB,QAAQ,EAAE,EAAE;IACZ,aAAa;IACb,SAAS,EAAE,IAAI;IACf,YAAY;IACZ,SAAS,EAAE;QACP,KAAK,EAAE,IAAI;QACX,QAAQ,EAAE,IAAI;QACd,eAAe,EAAE,IAAI;KACxB;CACJ,CAAA"} \ No newline at end of file +{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,eAAe;IACX,kBAAkB;IAClB,QAAQ,EAAE,EAAE;IACZ,aAAa;IACb,SAAS,EAAE,IAAI;IACf,YAAY;IACZ,SAAS,EAAE;QACP,KAAK,EAAE,IAAI;QACX,QAAQ,EAAE,IAAI;QACd,eAAe,EAAE,IAAI;QACrB,mBAAmB,EAAE,IAAI;KAC5B;CACJ,CAAA"} \ No newline at end of file diff --git a/dist/resolver.js b/dist/resolver.js index 9490679..6988f31 100644 --- a/dist/resolver.js +++ b/dist/resolver.js @@ -54,19 +54,29 @@ export var resolver = function (url) { return function (catchers) { */ text: wrapTypeParser("text"), /** - * Only for browsers ! + * Performs a callback on the API performance timings of the request. * - * Performs a callback on the API performance timings when they will be available. + * Warning: Still experimental on browsers and node.js */ perfs: function (cb) { - if (cb && typeof self !== "undefined" && typeof self["PerformanceObserver"] !== "undefined") { - wrapper.then(function (res) { - var observer = new self["PerformanceObserver"](function (entries) { - if (res) - cb(entries.getEntriesByName(res.url).reverse()[0]); - observer.disconnect(); - }); - observer.observe({ entryTypes: ["resource"] }); + var perfObserver = conf.polyfills.PerformanceObserver || (typeof self !== "undefined" ? self["PerformanceObserver"] : null); + if (cb && perfObserver) { + var entries_1 = null; + var callback_1 = null; + var observer_1 = new perfObserver(function (e) { + entries_1 = e.getEntries(); + if (callback_1) + callback_1(); + observer_1.disconnect(); + }); + observer_1.observe({ entryTypes: ["resource", "measure"] }); + req.then(function (res) { + if (res) { + if (entries_1) + cb(entries_1.reverse().find(function (_) { return _.name === res.url; })); + else + callback_1 = function () { return cb(entries_1.reverse().find(function (_) { return _.name === res.url; })); }; + } }); } return responseTypes; diff --git a/dist/resolver.js.map b/dist/resolver.js.map index 3cc223f..c4f193d 100644 --- a/dist/resolver.js.map +++ b/dist/resolver.js.map @@ -1 +1 @@ -{"version":3,"file":"resolver.js","sourceRoot":"","sources":["../src/resolver.ts"],"names":[],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"resolver.js","sourceRoot":"","sources":["../src/resolver.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/package.json b/package.json index 29dec37..1a7c09f 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ "fix": "tslint --fix -p tsconfig.json -t codeFrame", "prebuild": "rimraf dist && rimraf coverage && npm run lint", "build": "tsc -p . && rollup -c", - "test": "nyc mocha --reporter list", + "test": "nyc mocha --reporter list test/**/*.spec.js", "coverage": "nyc report --reporter=text-lcov | coveralls", "changelog": "conventional-changelog -p wretch -i CHANGELOG.md -s -r 0" }, diff --git a/test/test.js b/test/wretch.spec.js similarity index 68% rename from test/test.js rename to test/wretch.spec.js index 341c554..3dcff8d 100644 --- a/test/test.js +++ b/test/wretch.spec.js @@ -1,14 +1,15 @@ -const fetch = require("node-fetch") +const nodeFetch = require("node-fetch") const FormData = require("form-data") +const { performance, PerformanceObserver } = require("perf_hooks") const fs = require("fs") const path = require("path") -const expect = require("chai").expect +const { expect } = require("chai") const mockServer = require("./mock") const wretch = require("../dist/bundle/wretch") -const PORT = 9876 -const URL = `http://localhost:${PORT}/` +const _PORT = 9876 +const _URL = `http://localhost:${_PORT}/` const allRoutes = (obj, type, action) => Promise.all([ obj.get()[type](_ => _).then(action), @@ -18,12 +19,26 @@ const allRoutes = (obj, type, action) => Promise.all([ obj.delete()[type](action) ]) +const fetchPolyfill = (timeout = null) => + function(url, opts) { + performance.mark(url + " - begin") + return nodeFetch(url, opts).then(_ => { + performance.mark(url + " - end") + measure = () => performance.measure(_.url, url + " - begin", url + " - end") + if(timeout) + setTimeout(measure, timeout) + else + measure() + return _ + }) + } + const duckImage = fs.readFileSync(path.resolve(__dirname, "assets", "duck.jpg")) describe("Wretch", function() { before(async function() { - mockServer.launch(PORT) + mockServer.launch(_PORT) }) after(async function(){ @@ -35,22 +50,23 @@ describe("Wretch", function() { expect(() => wretch("...").formData({ a: 1, b: 2})).to.throw("FormData is not defined") expect(() => wretch("...").get("...")).to.throw("fetch is not defined") - return wretch().polyfills({ - fetch: fetch, + wretch().polyfills({ + fetch: fetchPolyfill(), FormData: FormData, - URLSearchParams: require("url").URLSearchParams + URLSearchParams: require("url").URLSearchParams, + PerformanceObserver: PerformanceObserver }) }) it("should perform crud requests and parse a text response", async function() { - const init = wretch(`${URL}/text`) + const init = wretch(`${_URL}/text`) const test = _ => expect(_).to.equal("A text string") await allRoutes(init, "text", test) }) it("should perform crud requests and parse a json response", async function() { const test = _ => expect(_).to.deep.equal({ a: "json", "object": "which", "is": "stringified" }) - const init = wretch(`${URL}/json`) + const init = wretch(`${_URL}/json`) await allRoutes(init, "json", test) }) @@ -58,32 +74,32 @@ describe("Wretch", function() { const test = _ => expect(_).to.satisfy(blob => { return blob.size === duckImage.length }) - const init = wretch(`${URL}/blob`) + const init = wretch(`${_URL}/blob`) await allRoutes(init, "blob", test) }) it("should perform crud requests and parse an arrayBuffer response", async function() { const test = _ => expect(_).to.satisfy(arrayBuffer => { var buffer = new Buffer(arrayBuffer.byteLength) - var view = new Uint8Array(arrayBuffer); + var view = new Uint8Array(arrayBuffer) for (let i = 0; i < buffer.length; ++i) { buffer[i] = view[i] } return buffer.equals(new Buffer.from([ 0x00, 0x01, 0x02, 0x03 ])) }) - const init = wretch(`${URL}/arrayBuffer`) + const init = wretch(`${_URL}/arrayBuffer`) await allRoutes(init, "arrayBuffer", test) }) it("should perform a plain text round trip", async function() { const text = "hello, server !" - const roundTrip = await wretch(`${URL}/text/roundTrip`).content("text/plain").body(text).post().text() + const roundTrip = await wretch(`${_URL}/text/roundTrip`).content("text/plain").body(text).post().text() expect(roundTrip).to.be.equal(text) }) it("should perform a json round trip", async function() { const jsonObject = { a: 1, b: 2, c: 3 } - const roundTrip = await wretch(`${URL}/json/roundTrip`).json(jsonObject).post().json() + const roundTrip = await wretch(`${_URL}/json/roundTrip`).json(jsonObject).post().json() expect(roundTrip).to.deep.equal(jsonObject) }) @@ -92,7 +108,7 @@ describe("Wretch", function() { hello: "world", duck: "Muscovy" } - const decoded = await wretch(`${URL}/formData/decode`).formData(form).post().json() + const decoded = await wretch(`${_URL}/formData/decode`).formData(form).post().json() expect(decoded).to.deep.equal({ hello: "world", "duck": "Muscovy" @@ -100,7 +116,7 @@ describe("Wretch", function() { }) it("should catch common error codes", async function() { - const w = wretch().baseUrl(URL+"/") + const w = wretch().baseUrl(_URL+"/") let check = 0 await w("400").get().badRequest(_ => { @@ -132,7 +148,7 @@ describe("Wretch", function() { it("should catch other error codes", async function() { let check = 0 - await wretch(`${URL}/444`) + await wretch(`${_URL}/444`) .get() .notFound(_ => check++) .error(444, _ => check++) @@ -149,7 +165,7 @@ describe("Wretch", function() { w = w .catcher(400, err => check++) .catcher(401, err => check--) - .baseUrl(URL+"/") + .baseUrl(_URL+"/") await w("text").get().res(_ => check++) await w("/400").get().res(_ => check--) @@ -163,26 +179,26 @@ describe("Wretch", function() { }) it("should set default fetch options", async function() { - let rejected = await new Promise(res => wretch(`${URL}/customHeaders`).get().badRequest(_ => { + let rejected = await new Promise(res => wretch(`${_URL}/customHeaders`).get().badRequest(_ => { res(true) }).res(result => res(!result))) expect(rejected).to.be.true wretch().defaults({ headers: { "X-Custom-Header": "Anything" } }) - rejected = await new Promise(res => wretch(`${URL}/customHeaders`).get().badRequest(_ => { + rejected = await new Promise(res => wretch(`${_URL}/customHeaders`).get().badRequest(_ => { res(true) }).res(result => res(!result))) expect(rejected).to.be.true wretch().defaults({ headers: { "X-Custom-Header-2": "Anything" } }, true) - rejected = await new Promise(res => wretch(`${URL}/customHeaders`).get().badRequest(_ => { + rejected = await new Promise(res => wretch(`${_URL}/customHeaders`).get().badRequest(_ => { res(true) }).res(result => res(!result))) wretch().defaults("not an object", true) expect(rejected).to.be.true - let accepted = await new Promise(res => wretch(`${URL}/customHeaders`) + let accepted = await new Promise(res => wretch(`${_URL}/customHeaders`) .options({ headers: { "X-Custom-Header-3" : "Anything" } }) .options({ headers: { "X-Custom-Header-4" : "Anything" } }, true) .get() @@ -193,28 +209,28 @@ describe("Wretch", function() { it("should allow url, query parameters & options modifications and return a fresh new Wretcher object containing the change", async function() { const obj1 = wretch() - const obj2 = obj1.url(URL) + const obj2 = obj1.url(_URL) expect(obj1._url).to.be.equal("") - expect(obj2._url).to.be.equal(URL) + expect(obj2._url).to.be.equal(_URL) const obj3 = obj1.options({ headers: { "X-test": "test" }}) expect(obj3._options).to.deep.equal({ headers: { "X-test": "test" }}) expect(obj1._options).to.deep.equal({}) const obj4 = obj2.query({a: "1!", b: "2"}) - expect(obj4._url).to.be.equal(`${URL}?a=1%21&b=2`) - expect(obj2._url).to.be.equal(URL) + expect(obj4._url).to.be.equal(`${_URL}?a=1%21&b=2`) + expect(obj2._url).to.be.equal(_URL) const obj5 = obj4.query({c: 6, d: [7, 8]}) - expect(obj4._url).to.be.equal(`${URL}?a=1%21&b=2`) - expect(obj5._url).to.be.equal(`${URL}?c=6&d=7&d=8`) + expect(obj4._url).to.be.equal(`${_URL}?a=1%21&b=2`) + expect(obj5._url).to.be.equal(`${_URL}?c=6&d=7&d=8`) }) it("should modify the Accept header", async function() { - expect(await wretch(`${URL}/accept`).get().text()).to.be.equal("text") - expect(await wretch(`${URL}/accept`).accept("application/json").get().json()).to.deep.equal({ json: "ok" }) + expect(await wretch(`${_URL}/accept`).get().text()).to.be.equal("text") + expect(await wretch(`${_URL}/accept`).accept("application/json").get().json()).to.deep.equal({ json: "ok" }) }) it("should change the parsing used in the default error handler", async function() { wretch().errorType("json") - await wretch(`${URL}/json500`) + await wretch(`${_URL}/json500`) .get() .internalError(error => { expect(error.json).to.deep.equal({ error: 500, message: "ok" }) }) .res(_ => expect.fail("", "", "I should never be called because an error was thrown")) @@ -222,8 +238,28 @@ describe("Wretch", function() { }) it("should mix in options on baseUrl", async function() { - const w = wretch().options({ headers: { "X-test": "test" }}).baseUrl(URL+"/") - const obj1 = w(""); + const w = wretch().options({ headers: { "X-test": "test" }}).baseUrl(_URL+"/") + const obj1 = w("") expect(obj1._options).to.deep.equal({ headers: { "X-test": "test" }}) }) + + it("should retrieve performance timings associated with a fetch request", function(done) { + // Test empty perfs() + wretch(`${_URL}/text`).get().perfs().res(_ => expect(_.ok).to.be.true).then( + // Racing condition : observer triggered before response + wretch(`${_URL}/text`).get().perfs(_ => { + expect(typeof _.startTime).to.be.equal("number") + + // Racing condition : response triggered before observer + wretch().polyfills({ + fetch: fetchPolyfill(10) + }) + + wretch(`${_URL}/text`).get().perfs(_ => { + expect(typeof _.startTime).to.be.equal("number") + done() + }) + }) + ) + }) }) \ No newline at end of file