├── .dawn ├── pipe.yml └── rc.yml ├── .eslintrc.json ├── .eslintrc.yml ├── .gitignore ├── .npmignore ├── _config.yml ├── demo └── index.html ├── design └── faked.psd ├── dist └── faked.js ├── docs ├── .gitkeep ├── _config.yml ├── faked.png ├── gui.png └── shot.png ├── jasmine.json ├── jsconfig.json ├── package-lock.json ├── package.json ├── plugins ├── scanner.js └── webpack.js ├── readme.md ├── server.yml ├── src ├── common │ └── sleep.js ├── core │ ├── body.js │ ├── faked.js │ ├── fetch.js │ ├── headers.js │ ├── jsonp.js │ ├── request.js │ ├── response.js │ ├── status.js │ └── xhr.js └── index.js └── webpack.config.js /.dawn/pipe.yml: -------------------------------------------------------------------------------- 1 | init: 2 | - name: pkginfo 3 | 4 | dev: 5 | - name: clean 6 | target: 7 | - ./dist/**/*.* 8 | - name: webpack 9 | entry: 10 | faked: ./src/index.js 11 | folders: 12 | js: ./ 13 | output: ./dist/ 14 | common: 15 | disabled: true 16 | watch: true 17 | - name: server 18 | port: 3004 19 | public: ./ 20 | - name: browser-sync 21 | 22 | build: 23 | - name: clean 24 | target: 25 | - ./dist/**/*.* 26 | - name: webpack 27 | entry: 28 | faked: ./src/index.js 29 | folders: 30 | js: ./ 31 | output: ./dist/ 32 | common: 33 | disabled: true 34 | 35 | test: 36 | - name: lint 37 | 38 | publish: 39 | - name: shell 40 | script: 41 | - dn test 42 | - dn build 43 | - npm pu --registry=http://registry.npmjs.org -------------------------------------------------------------------------------- /.dawn/rc.yml: -------------------------------------------------------------------------------- 1 | server: https://alibaba.github.io/dawn 2 | registry: https://registry.npmjs.com/ -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "dawn" 4 | ] 5 | } -------------------------------------------------------------------------------- /.eslintrc.yml: -------------------------------------------------------------------------------- 1 | extends: 2 | - dawn 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.DS_Store 2 | *.log 3 | *.js.map 4 | *.css.map 5 | logs/ 6 | node_modules/ 7 | .vscode/ 8 | server.local.yml 9 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | *.DS_Store 2 | *.log 3 | *.sh 4 | *.js.map 5 | *.css.map 6 | .babelrc 7 | .eslintrc.yml 8 | .travis.yml 9 | server.yml 10 | server.local.yml 11 | webpack.config.js 12 | logs/ 13 | node_modules/ 14 | scripts/ 15 | docs/ 16 | test/ 17 | examples/ 18 | .vscode/ -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-minimal -------------------------------------------------------------------------------- /demo/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Faked 9 | 10 | 11 | 12 | 13 | 14 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /design/faked.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Houfeng/faked/c9a233d98a4f0809c9700d7c9910055bff33339a/design/faked.psd -------------------------------------------------------------------------------- /dist/faked.js: -------------------------------------------------------------------------------- 1 | !function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define("faked",[],e):"object"==typeof exports?exports.faked=e():t.faked=e()}("undefined"!=typeof self?self:this,function(){return function(t){function e(r){if(n[r])return n[r].exports;var o=n[r]={i:r,l:!1,exports:{}};return t[r].call(o.exports,o,o.exports,e),o.l=!0,o.exports}var n={};return e.m=t,e.c=n,e.d=function(t,n,r){e.o(t,n)||Object.defineProperty(t,n,{configurable:!1,enumerable:!0,get:r})},e.n=function(t){var n=t&&t.__esModule?function(){return t.default}:function(){return t};return e.d(n,"a",n),n},e.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},e.p="",e(e.s=73)}([function(t,e){var n=t.exports={version:"2.6.9"};"number"==typeof __e&&(__e=n)},function(t,e){var n=t.exports="undefined"!=typeof window&&window.Math==Math?window:"undefined"!=typeof self&&self.Math==Math?self:Function("return this")();"number"==typeof __g&&(__g=n)},function(t,e,n){var r=n(41)("wks"),o=n(25),i=n(1).Symbol,s="function"==typeof i;(t.exports=function(t){return r[t]||(r[t]=s&&i[t]||(s?i:o)("Symbol."+t))}).store=r},function(t,e,n){var r=n(1),o=n(0),i=n(17),s=n(9),u=n(10),a=function(t,e,n){var c,f,l,p=t&a.F,h=t&a.G,d=t&a.S,y=t&a.P,v=t&a.B,g=t&a.W,m=h?o:o[e]||(o[e]={}),_=m.prototype,b=h?r:d?r[e]:(r[e]||{}).prototype;h&&(n=e);for(c in n)(f=!p&&b&&void 0!==b[c])&&u(m,c)||(l=f?b[c]:n[c],m[c]=h&&"function"!=typeof b[c]?n[c]:v&&f?i(l,r):g&&b[c]==l?function(t){var e=function(e,n,r){if(this instanceof t){switch(arguments.length){case 0:return new t;case 1:return new t(e);case 2:return new t(e,n)}return new t(e,n,r)}return t.apply(this,arguments)};return e.prototype=t.prototype,e}(l):y&&"function"==typeof l?i(Function.call,l):l,y&&((m.virtual||(m.virtual={}))[c]=l,t&a.R&&_&&!_[c]&&s(_,c,l)))};a.F=1,a.G=2,a.S=4,a.P=8,a.B=16,a.W=32,a.U=64,a.R=128,t.exports=a},function(t,e,n){var r=n(7);t.exports=function(t){if(!r(t))throw TypeError(t+" is not an object!");return t}},function(t,e,n){t.exports=!n(11)(function(){return 7!=Object.defineProperty({},"a",{get:function(){return 7}}).a})},function(t,e,n){var r=n(4),o=n(54),i=n(38),s=Object.defineProperty;e.f=n(5)?Object.defineProperty:function(t,e,n){if(r(t),e=i(e,!0),r(n),o)try{return s(t,e,n)}catch(t){}if("get"in n||"set"in n)throw TypeError("Accessors not supported!");return"value"in n&&(t[e]=n.value),t}},function(t,e){t.exports=function(t){return"object"==typeof t?null!==t:"function"==typeof t}},function(t,e,n){!function(t){t.noop=function(){},t.isNull=function(t){return null===t||void 0===t},t.trim=function(t){return this.isNull(t)?t:t.trim?t.trim():t.replace(/(^[\\s]*)|([\\s]*$)/g,"")},t.replace=function(t,e,n){return this.isNull(t)?t:t.replace(new RegExp(e,"g"),n)},t.startWith=function(t,e){return!this.isNull(t)&&!this.isNull(e)&&0===t.indexOf(e)},t.contains=function(t,e){return!this.isNull(t)&&!this.isNull(e)&&t.indexOf(e)>-1},t.endWith=function(t,e){return!this.isNull(t)&&!this.isNull(e)&&t.indexOf(e)===t.length-e.length},t.has=t.hasProperty=function(t,e){return!this.isNull(t)&&!this.isNull(e)&&(e in t||t.hasOwnProperty(e))},t.isFunction=function(t){return!this.isNull(t)&&"function"==typeof t},t.isString=function(t){return!this.isNull(t)&&("string"==typeof t||t instanceof String)},t.isNumber=function(t){return!this.isNull(t)&&("number"==typeof t||t instanceof Number)},t.isBoolean=function(t){return!this.isNull(t)&&("boolean"==typeof t||t instanceof Boolean)},t.isElement=function(t){return!this.isNull(t)&&(window.Element?t instanceof Element:t.tagName&&t.nodeType&&t.nodeName&&t.attributes&&t.ownerDocument)},t.isText=function(t){return!this.isNull(t)&&t instanceof Text},t.isObject=function(t){return!this.isNull(t)&&"object"==typeof t},t.isArray=function(t){if(this.isNull(t))return!1;var e="[object Array]"===Object.prototype.toString.call(t),n=t instanceof Array,r=!this.isString(t)&&this.isNumber(t.length)&&this.isFunction(t.splice),o=!this.isString(t)&&this.isNumber(t.length)&&t[0];return e||n||r||o},t.isDate=function(t){return!this.isNull(t)&&t instanceof Date},t.isRegexp=function(t){return t instanceof RegExp},t.toArray=function(t){return this.isNull(t)?[]:Array.prototype.slice.call(t)},t.toDate=function(t){var e=this;return e.isNumber(t)?new Date(t):e.isString(t)?new Date(e.replace(e.replace(t,"-","/"),"T"," ")):e.isDate(t)?t:null},t.each=function(t,e,n){if(!this.isNull(t)&&!this.isNull(e))if(this.isArray(t))for(var r=t.length,o=0;o-1))if(delete e[r],Object.getOwnPropertyDescriptor)try{Object.defineProperty(e,r,Object.getOwnPropertyDescriptor(t,r))}catch(n){e[r]=t[r]}else e[r]=t[r]}),e},t.clone=function(t,e){if(this.isNull(t)||this.isString(t)||this.isNumber(t)||this.isBoolean(t)||this.isDate(t))return t;var n=t;try{n=new t.constructor}catch(t){}return this.each(t,function(t,r){n[t]==r||this.contains(e,t)||(this.isObject(r)?n[t]=this.clone(r,e):n[t]=r)},this),["toString","valueOf"].forEach(function(r){this.contains(e,r)||this.defineFreezeProp(n,r,t[r])},this),n},t.mix=function(e,n,r,o,i){if(o)switch(o){case 1:return t.mix(e.prototype,n.prototype,r,0);case 2:t.mix(e.prototype,n.prototype,r,0);break;case 3:return t.mix(e,n.prototype,r,0);case 4:return t.mix(e.prototype,n,r,0)}return n=n||{},e=e||(this.isArray(n)?[]:{}),this.keys(n).forEach(function(o){this.contains(r,o)||i&&this.isNull(n[o])||(!this.isObject(n[o])||n[o].constructor!=Object&&n[o].constructor!=Array&&null!=n[o].constructor?e[o]=n[o]:e[o]=t.mix(e[o],n[o],r,0,i))},this),e},t.defineFreezeProp=function(t,e,n){try{Object.defineProperty(t,e,{value:n,enumerable:!1,configurable:!0,writable:!1})}catch(r){t[e]=n}},t.keys=function(t){if(Object.keys)return Object.keys(t);var e=[];return this.each(t,function(t){e.push(t)}),e},t.create=function(t,e){if(Object.create)return Object.create(t,e);var n=function(){};n.prototype=t;var r=new n;return e&&this.copy(e,r),r},t.setPrototypeOf=function(t,e){if(Object.setPrototypeOf)return Object.setPrototypeOf(t,e||this.create(null));"__proto__"in Object||this.copy(e,t),t.__proto__=e},t.getPrototypeOf=function(t){return t.__proto__?t.__proto__:Object.getPrototypeOf?Object.getPrototypeOf(t):t.constructor?t.constructor.prototype:void 0},t.deepEqual=function(t,e){if(t===e)return!0;if(!this.isObject(t)||!this.isObject(e))return!1;var n=this.keys(t),r=this.keys(e);if(n.length!==r.length)return!1;var o=n.concat(r),i=this.create(null),s=!0;return this.each(o,function(n,r){i[r]||(this.deepEqual(t[r],e[r])||(s=!1),i[r]=!0)},this),s},t.fromTo=function(t,e,n,r){if(r||(r=[n,n=r][0]),n=Math.abs(n||1),t=e;o-=n)r(o)},t.newGuid=function(){var t=function(){return(65536*(1+Math.random())|0).toString(16).substring(1)};return t()+t()+"-"+t()+"-"+t()+"-"+t()+"-"+t()+t()+t()},t.map=function(t,e){var n=this.isArray(t)?[]:{};return this.each(t,function(t,r){n[t]=e(t,r)}),n},t.setByPath=function(t,e,n){this.isNull(t)||this.isNull(e)||""===e||(this.isArray(e)||(e=e.replace(/\[/,".").replace(/\]/,".").split(".")),this.each(e,function(r,o){this.isNull(o)||o.length<1||(r===e.length-1?t[o]=n:(t[o]=t[o]||{},t=t[o]))},this))},t.getByPath=function(t,e){return this.isNull(t)||this.isNull(e)||""===e?t:(this.isArray(e)||(e=e.replace(/\[/,".").replace(/\]/,".").split(".")),this.each(e,function(e,n){this.isNull(n)||n.length<1||this.isNull(t)||(t=t[n])},this),t)},t.unique=function(t){if(this.isNull(t))return t;var e=[];return this.each(t,function(t,n){e.indexOf(n)>-1||e.push(n)}),e},t.getFunctionArgumentNames=function(t){if(!t)return[];var e=t.toString(),n=e.split(")")[0].split("=>")[0].split("(");return(n[1]||n[0]).split(",").map(function(t){return t.trim()}).filter(function(t){return"function"!=t})},t.short=function(t,e){if(!t)return t;e=e||40;var n=t.length,r=e/2;return n>e?t.substr(0,r)+"..."+t.substr(n-r):t},t.firstUpper=function(t){if(!this.isNull(t))return t.substring(0,1).toUpperCase()+t.substring(1)},t.escapeRegExp=function(t){return t.replace(/[-\/\\^$*+?.()|[\]{}]/g,"\\$&")},t.parseDom=function(e){this._PDD_=this._PDD_||document.createElement("div"),this._PDD_.innerHTML=t.trim(e);var n=this._PDD_.childNodes[0];return n&&(n=n.cloneNode(!0)),this._PDD_.innerHTML="",n}}(e)},function(t,e,n){var r=n(6),o=n(24);t.exports=n(5)?function(t,e,n){return r.f(t,e,o(1,n))}:function(t,e,n){return t[e]=n,t}},function(t,e){var n={}.hasOwnProperty;t.exports=function(t,e){return n.call(t,e)}},function(t,e){t.exports=function(t){try{return!!t()}catch(t){return!0}}},function(t,e,n){var r=n(57),o=n(36);t.exports=function(t){return r(o(t))}},function(t,e,n){t.exports=n(98)},function(t,e,n){t.exports={default:n(100),__esModule:!0}},function(t,e,n){"use strict";e.__esModule=!0,e.default=function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}},function(t,e){t.exports=!0},function(t,e,n){var r=n(23);t.exports=function(t,e,n){if(r(t),void 0===e)return t;switch(n){case 1:return function(n){return t.call(e,n)};case 2:return function(n,r){return t.call(e,n,r)};case 3:return function(n,r,o){return t.call(e,n,r,o)}}return function(){return t.apply(e,arguments)}}},function(t,e){t.exports={}},function(t,e,n){var r=n(56),o=n(42);t.exports=Object.keys||function(t){return r(t,o)}},function(t,e){var n={}.toString;t.exports=function(t){return n.call(t).slice(8,-1)}},function(t,e,n){"use strict";e.__esModule=!0;var r=n(33),o=function(t){return t&&t.__esModule?t:{default:t}}(r);e.default=function(t){return function(){var e=t.apply(this,arguments);return new o.default(function(t,n){function r(i,s){try{var u=e[i](s),a=u.value}catch(t){return void n(t)}if(!u.done)return o.default.resolve(a).then(function(t){r("next",t)},function(t){r("throw",t)});t(a)}return r("next")})}}},function(t,e,n){function r(t){return t&&t.__esModule?t:{default:t}}var o=n(33),i=r(o),s=n(13),u=r(s),a=n(21),c=r(a),f=n(14),l=r(f),p=n(15),h=r(p),d=n(102),y=n(8),v=n(66),g=n(127),m=["get","post","put","delete","patch","options","head","copy","link","unlink","lock","unlock","purge","propfind","view"],_=/^\/\/!exec/,b=/^\/\//,w=function(){function t(){(0,h.default)(this,t),this.delay=0,this.timeout=1e4,this.router=new d,this.global=(0,l.default)(null)}return t.prototype.when=function(t,e,n,r){(e=(e||"").trim())&&(y.isArray(t)||(t=[t]),b.test(e)&&(this.when(t,"http:"+e,n,r),this.when(t,"https:"+e,n,r)),this.router.add([{methods:t,pattern:e,handler:n,options:r}]))},t.prototype._findRoute=function(t){var e=this.router.get(t.url.split("?")[0]),n=e.find(function(e){return e.methods.indexOf(t.method.toUpperCase())>-1});return n?(n.method=t.method,n):void(this.debug&&this.warn('Unmatched: "'+t.method+" "+t.url+'"'))},t.prototype._checkTimeout=function(){function t(t){return e.apply(this,arguments)}var e=(0,c.default)(u.default.mark(function t(e){return u.default.wrap(function(t){for(;;)switch(t.prev=t.next){case 0:return t.next=2,g(this.timeout);case 2:if(!e._sended){t.next=4;break}return t.abrupt("return");case 4:this.error("Timeout: "+e.method+" "+e.url);case 5:case"end":return t.stop()}},t,this)}));return t}(),t.prototype._invokeHandler=function(){function t(t,n,r){return e.apply(this,arguments)}var e=(0,c.default)(u.default.mark(function t(e,n,r){var o,i,s,a=this;return u.default.wrap(function(t){for(;;)switch(t.prev=t.next){case 0:if(o=y.create(e),this._checkTimeout(o),o.request=e,o.route=n,o.params=n.params,o._sended=!1,o.send=function(t,e,n){if(o._sended)return a.error("Send and return cannot coexist, and send cannot be repeated");o._sended=!0,e=e||200;var i=new v(t,{status:e,headers:n});r(i),a.info('Response "'+o.method+" "+o.url+'"',{headers:i.headers.toMap(),body:t})},i=n.handler,!y.isFunction(i)){t.next=15;break}return t.next=11,i.call(o,o,this.global);case 11:s=t.sent,y.isNull(s)||o.send(s),t.next=16;break;case 15:o.send(i);case 16:case"end":return t.stop()}},t,this)}));return t}(),t.prototype.error=function(){for(var t,e=arguments.length,n=Array(e),r=0;r0)){t.next=8;break}return t.next=8,g(r);case 8:return t.abrupt("return",new i.default(function(t){o._invokeHandler(e,n,t)}));case 9:case"end":return t.stop()}},t,this)}));return t}(),t.prototype.fromJson=function(t){var e=this;t.forEach(function(t){t.handler=t.handler||t.content,t.pattern=t.pattern||t.url,t.options=t.options||t.option,t.methods=t.methods||t.method||t.type,y.isString(t.handler)&&_.test(t.handler)&&(t.handler=new Function("context","global",t.handler)),e.when(t.methods,t.pattern,t.handler,t.options)})},t.prototype.toJson=function(){return this.router.table.map(function(t){return{methods:t.methods,pattern:t.pattern,handler:t.handler,options:t.options}})},t}();m.forEach(function(t){w.prototype[t]=function(){for(var e=arguments.length,n=Array(e),r=0;r0){if(e>4)return"[...]";var n=o(t[0],e);return t.every(function(t){return o(t,e)===n})?n.trim()+"[]":"["+t.slice(0,15).map(function(t){return o(t,e)}).join(", ")+(t.length>=15?", ...":"")+"]"}return"Array"}var r=(0,a.default)(t);if(!r.length)return t.constructor&&t.constructor.name&&"Object"!==t.constructor.name?t.constructor.name:"Object";if(e>4)return"{...}";var i=" ".repeat(e-1),u=r.slice(0,15).map(function(n){return(/^([A-Z_$][A-Z0-9_$]*)$/i.test(n)?n:(0,s.default)(n))+": "+o(t[n],e)+";"}).join("\n "+i);return r.length>=15&&(u+="\n "+i+"..."),t.constructor&&t.constructor.name&&"Object"!==t.constructor.name?t.constructor.name+" {\n "+i+u+"\n"+i+"}":"{\n "+i+u+"\n"+i+"}"}var i=n(51),s=r(i),u=n(69),a=r(u),c=n(29),f=r(c),l=n(13),p=r(l),h=n(70),d=r(h),y=n(46),v=r(y),g=n(14),m=r(g),_=n(15),b=r(_),w=n(8),x=function t(e,n){(0,b.default)(this,t),this.name=e,this.value=n},O=function(){function t(e){var n=this;(0,b.default)(this,t),e=e||(0,m.default)(null),e.toMap&&(e=e.toMap()),this._list=[],w.each(e,function(t,e){n.append(t,e)})}return t.prototype.append=function(t,e){this._list.push(new x(t,e))},t.prototype.delete=function(t){this._list=this._list.filter(function(e){return e.name!==t})},t.prototype.set=function(t,e){this.delete(t),this.append(t,e)},t.prototype.has=function(t){this.find(function(e){return e.name===t})},t.prototype.get=function(t){var e=this._list.find(function(e){return e.name===t});if(e)return e.value},t.prototype.getAll=function(t){return t?this._list.filter(function(e){return e.name===t}).map(function(t){return t.value}):this._list},t.prototype.keys=function(){return this._list.map(function(t){return t.name})},t.prototype.values=function(){return this._list.map(function(t){return t.value})},t.prototype.entries=p.default.mark(function t(){var e,n,r,i,s,u;return p.default.wrap(function(t){for(;;)switch(t.prev=t.next){case 0:if((i=this._list)&&("function"==typeof i[v.default]||Array.isArray(i))){t.next=3;break}throw new TypeError("Expected _list to be iterable, got "+o(i));case 3:e=i,n=Array.isArray(e),r=0,e=n?e:(0,d.default)(e);case 4:if(!n){t.next=10;break}if(!(r>=e.length)){t.next=7;break}return t.abrupt("break",19);case 7:s=e[r++],t.next=14;break;case 10:if(r=e.next(),!r.done){t.next=13;break}return t.abrupt("break",19);case 13:s=r.value;case 14:return u=s,t.next=17,[u.name,u.value];case 17:t.next=4;break;case 19:case"end":return t.stop()}},t,this)}),t.prototype.toMap=function(){var t=(0,m.default)(null);return this._list.forEach(function(e){t[e.name]=e.value}),t},t.prototype.forEach=function(t,e){var n=this;this._list.forEach(function(r){return t.call(e||n,r.value,r.name)})},t}();t.exports=O},function(t,e,n){function r(t){return t&&t.__esModule?t:{default:t}}function o(){return location.origin||(location.origin=location.protocol+"//"+location.hostname+(location.port?":"+location.port:"")),location.origin}function i(t){if(!t)return t;var e=o();return 0!=t.indexOf(e)?t:t.replace(e,"")}function s(t){return i(t)}var u=n(14),a=r(u),c=n(15),f=r(c),l=n(28),p=r(l),h=n(45),d=r(h),y=n(50),v=r(y),g=n(31),m=n(71),_=n(8),b=n(72),w=function(t){function e(n,r){(0,f.default)(this,e),r=r||(0,a.default)(null);var o=(0,d.default)(this,t.call(this,r.body));return o.opts=(0,a.default)(null),_.isString(n)?o.opts.url=n:_.copy(n,o.opts),_.copy(r,o.opts),o.opts.url=s(o.opts.url),o.url=o.opts.url,o.method=o.opts.method||"GET",o.headers=new g(o.opts.headers),o.context=o.opts.context||window,o.referrer=o.opts.referrer||location.href,o.mode=o.opts.mode,o.credentials=o.opts.credentials,o.redirect=o.opts.redirect,o.integrity=o.opts.integrity,o.cache=o.opts.cache,o}return(0,v.default)(e,t),e.prototype.clone=function(){return new e(this.url,this.opts)},(0,p.default)(e,[{key:"body",get:function(){switch((this.headers.get("Content-Type")||"").split(";")[0]){case"application/json":case"text/json":return _.isString(this.rawBody)?JSON.parse(this.rawBody):this.rawBody;case"application/x-www-form-urlencoded":return _.isString(this.rawBody)?b.parse(this.rawBody):this.rawBody;default:return this.rawBody}},set:function(t){this.rawBody=t}},{key:"url",set:function(t){this._url=t,t&&(this.query=b.parse(t.split("?")[1]))},get:function(){return this._url}}]),e}(m);t.exports=w},function(t,e,n){t.exports={default:n(75),__esModule:!0}},function(t,e,n){"use strict";var r=n(76)(!0);n(53)(String,"String",function(t){this._t=String(t),this._i=0},function(){var t,e=this._t,n=this._i;return n>=e.length?{value:void 0,done:!0}:(t=r(e,n),this._i+=t.length,{value:t,done:!1})})},function(t,e){var n=Math.ceil,r=Math.floor;t.exports=function(t){return isNaN(t=+t)?0:(t>0?r:n)(t)}},function(t,e){t.exports=function(t){if(void 0==t)throw TypeError("Can't call method on "+t);return t}},function(t,e,n){var r=n(7),o=n(1).document,i=r(o)&&r(o.createElement);t.exports=function(t){return i?o.createElement(t):{}}},function(t,e,n){var r=n(7);t.exports=function(t,e){if(!r(t))return t;var n,o;if(e&&"function"==typeof(n=t.toString)&&!r(o=n.call(t)))return o;if("function"==typeof(n=t.valueOf)&&!r(o=n.call(t)))return o;if(!e&&"function"==typeof(n=t.toString)&&!r(o=n.call(t)))return o;throw TypeError("Can't convert object to primitive value")}},function(t,e,n){var r=n(4),o=n(78),i=n(42),s=n(40)("IE_PROTO"),u=function(){},a=function(){var t,e=n(37)("iframe"),r=i.length;for(e.style.display="none",n(59).appendChild(e),e.src="javascript:",t=e.contentWindow.document,t.open(),t.write(" 14 | ``` 15 | 16 | 通过 npm 安装: 17 | ```sh 18 | $ npm i faked --save-dev 19 | ``` 20 | 21 | CommonJs 方式引用 22 | ```js 23 | const faked = require('faked'); 24 | ``` 25 | 26 | ES6 Modules 方式引用 27 | ```js 28 | import * as faked from 'faked' 29 | ``` 30 | 31 | 32 | ## 3. 使用 Faked 33 | 34 | ### 3.1 基本用法 35 | 36 | 通过 `faked.when` 方法你几乎就可以使用 faked 的所有功能了,尽管 faked 还提供了一组「快捷方法」,`faked.when` 方法说明如下: 37 | ```js 38 | //指定单一 Http Method 39 | faked.when(, , ); 40 | 41 | //指定多个 Http Method 42 | faked.when(, , ); 43 | ``` 44 | 45 | 示例,模拟一个获取用户信息的接口,参考如下代码: 46 | ```js 47 | faked.when('get','/user/{id}', function(){ 48 | this.send({name:'Bob'}); 49 | }); 50 | ``` 51 | 52 | 每一个 `handler` 的 `this` 就是当前请求上下文对象,对象有如下主要成员: 53 | - `this.send(data, status, headers)` 方法,用于响应一个请求,status 默认为 200 54 | - `this.params` 路由参数对象,用于访问路由模式中的「路由参数」,如上边示例中的 id 55 | - `this.query` 解析查询字符串对应的对象,比如 `?name=bob` 可以通过 `this.query.name` 访问 56 | - `this.body` 请求的主体内容,通常会是一个 `json` 对象,它取决于发起的请求。 57 | 58 | 除了使用 `send` 方法,还可以直接「返回」数据,参考如下代码: 59 | ```js 60 | faked.when('get','/user/{id}', function(){ 61 | return {name:'Bob'}; 62 | }); 63 | ``` 64 | 当然,在有「异步处理」时你也可以返回一个 `promise` 对象或像上边那样用 `send` 方法。如果你只想 mock 数据,还可以使用简化写法,参考如下代码: 65 | ```js 66 | faked.when('get','/user/{id}', {name:'bob'}); 67 | ``` 68 | 69 | ### 3.2 快捷方法 70 | faked 还基于 when 方法提供了一组快捷方法,对应常用的 Http Methods,包括: 71 | 72 | ``` 73 | get, post, put, delete, options, patch, head, copy, link, unlink, lock, unlock, view 74 | ``` 75 | 76 | 用 `faked.get` 写一个示例: 77 | 78 | ```js 79 | faked.get('/user/{id}',function(){ 80 | this.send({name:'Bob'}); 81 | }); 82 | ``` 83 | 其它快捷方法和 `faked.get` 用法完全一致。 84 | 85 | ### 3.3 路由系统 86 | 在编辑 Mock API 时, faked 提供了路由支持,如上边看到的 `/user/{id}`,就是一个路由「匹配模式」,其中 `{id}` 是一个路由参数,当多个路由同时匹配请求的 URL 时,只会触发第一个执行,不同的 `Http Method` 的 URL 匹配模式可以相同,并不会冲突。路由参数还可以加「限定表达式」,参考如下代码: 87 | 88 | ```js 89 | // User Id 只能是数字 90 | faked.get('/user/{id:\d+}', {name:'test'}); 91 | ``` 92 | 93 | ### 3.4 模拟网络延时 94 | 95 | 有时候,我们希望 Mock API 能延时响应数据,以模拟「网络延时」,faked 目前支持固定的「延时设置」,参考如下代码: 96 | 97 | ```js 98 | const faked = require('faked'); 99 | 100 | //所有的请求都将被延时 2 秒种再响应用 mock 数据 101 | faked.delay = 2000; 102 | ``` 103 | 当 delay 设置 0 时,将禁用延时。 104 | 105 | 106 | ### 3.5 设置超时时间 107 | 108 | faked 还可设置 Mock API 的最大响应时间,这项设置存在的意义还在于「所有 Mock API 的 Handler 默认都是异步的,如果忘记「返回或 Send」一个响应结果,请求将会被一直挂起,有了超时设置,超时时将会抛出一个错误,方便定位问题」,参考如下代码: 109 | 110 | ```js 111 | const faked = require('faked'); 112 | 113 | //在超过 8 秒未响应数据时,将会打印一个错误消息 114 | faked.timeout = 8000; 115 | ``` 116 | 117 | 超时设置和延时设置并不会相互影响,超时计算是从延时结束后开始的。 118 | 119 | ### 3.6 JSONP 处理 120 | 121 | faked 除了能 mock 常规的 `ajax` 和 `fetch` 请求,还能 mock 常常用来处理跨域问题的 `jsonp` 请求,faked 有两个参数用于配置 jsonp,参考如下代码: 122 | 123 | ```js 124 | //指定服务端用于获取「回调函数名」的 「QueryString 参数」 125 | faked.jsonp.param = 'callback'; //默认值为 callback 和 jQuery 一致 126 | 127 | //有些 jsonp 服务可能是固定了「回调函数名」,可以这样设定 128 | faked.jsonp.callback = 'your-callbak-name'; 129 | ``` 130 | 131 | 132 | ## 4. 在项目中使用 133 | 134 | ### 4.1 注意事项 135 | faked 是一个「辅助开发」的工具,除非有特殊需要,一般情况下它不应出现在你的生产代码中,所以需要注意: 136 | 137 | 1. 请勿将 faked 放到「生产环境」的应用或页面中 138 | 2. 找一个合适的你项目的方式决定什么时引用 faked 139 | 140 | 141 | 142 | 143 | ### 4.2. 使用示例 144 | 145 | 146 | 比如,在 `webpack` 中,可以根据环境变量决定入口文件,并只在 `mock` 的入口文件中引用 faked,示例: 147 | 148 | webpack.config.js 149 | ```js 150 | const NODE_ENV = process.env.NODE_ENV; 151 | 152 | module.exports = { 153 | entry: { 154 | //根据 NODE_ENV 决定是 index.js 还是 index.mock.js 155 | bundle: `./src/index${NODE_ENV=='mock'?'.mock':''}.js` 156 | }, 157 | output: { 158 | path: './dist/', 159 | filename: `./[name]${NODE_ENV == 'prod'?'.min':''}.js` 160 | }, 161 | devtool: 'source-map', 162 | module: { 163 | loaders: [...] 164 | }, 165 | plugins: [...] 166 | }; 167 | ``` 168 | 169 | 然后,在 `index.mock.js` 中这样写: 170 | ```js 171 | require('./mock') 172 | require('./index'); 173 | ``` 174 | 175 | 用于存放的 `mock` 代码的 `mock.js` 可能是这样的: 176 | ```js 177 | const faked = require('faked'); 178 | 179 | faked.get('/user/{id}',function(){ 180 | this.send({name:'Bob'}); 181 | }); 182 | ``` 183 | 184 | 根据实际情况,安排你的文或目录结构,使用其它的工具诸如 gulp/browserify 等,都可以有类似的处理。 185 | 186 | 187 | ### 4.3 使用 Webpack 的插件 188 | 189 | 上边提到的使用方式可能稍显床烦,针对基于 webpack 进行构建的工程,faked 提供了一个插件,这是使用 faked 最简单的方式,只需要做一件事,在安装 faked 之后,修改 webpack.config.js,如下: 190 | 191 | ```js 192 | const fakedPlugin = require('faked/plugins/webpack'); 193 | 194 | module.exports = { 195 | ... 196 | plugins:[ 197 | new fakedPlugin({root:'./src'}) // root 可省略,默认为当前目录下的 src 目录 198 | ] 199 | ... 200 | }; 201 | ``` 202 | 203 | 启用插件后,就可在 `root` 指定的目录中编写 `xxx.faked.js` 文件了 204 | 205 | 然后,像之前一样,开发或构建你的工程好就行了,比如: 206 | ``` 207 | NODE_ENV=mock webpack --watch 208 | ``` 209 | 210 | ## 开发与构建 211 | 212 | faked 使用 Dawn 进行开发与构建 213 | 214 | 安装 dawn 215 | ```sh 216 | npm i dawn -g 217 | ``` 218 | 219 | 开发与构建 220 | ```sh 221 | # 克隆代码 222 | git clone 223 | 224 | # 开发 225 | dn dev 226 | 227 | #构建 228 | dn build 229 | ``` 230 | -- END -- 231 | -------------------------------------------------------------------------------- /server.yml: -------------------------------------------------------------------------------- 1 | proxy: 2 | rules: 3 | ^/api(.*): 'https://www.aliyun.com/' -------------------------------------------------------------------------------- /src/common/sleep.js: -------------------------------------------------------------------------------- 1 | module.exports = function (delay) { 2 | return new Promise(resolve => { 3 | setTimeout(resolve, delay); 4 | }); 5 | }; -------------------------------------------------------------------------------- /src/core/body.js: -------------------------------------------------------------------------------- 1 | const utils = require('ntils'); 2 | 3 | const METHODS = ['arrayBuffer', 'blob', 'formData']; 4 | 5 | class Body { 6 | constructor(rawBody) { 7 | this.bodyUsed = false; 8 | this.rawBody = utils.clone(rawBody); 9 | this.bodyText = utils.isString(this.rawBody) 10 | ? this.rawBody 11 | : JSON.stringify(this.rawBody); 12 | } 13 | 14 | // body 属性不是标准 api 的一部分,只为方便使用 15 | get body() { 16 | return this.rawBody; 17 | } 18 | 19 | set body(value) { 20 | this.rawBody = value; 21 | } 22 | 23 | async text() { 24 | if (this.bodyUsed) throw new Error('Body Used'); 25 | if (utils.isString(this.body)) { 26 | return this.body; 27 | } 28 | return JSON.stringify(this.body); 29 | } 30 | 31 | async json() { 32 | if (this.bodyUsed) throw new Error('Body Used'); 33 | if (utils.isString(this.body)) { 34 | return JSON.parse(this.body); 35 | } 36 | return this.body; 37 | } 38 | } 39 | 40 | METHODS.forEach(method => { 41 | Body.prototype[method] = async function() { 42 | if (this.bodyUsed) throw new Error('Body Used'); 43 | this.bodyUsed = true; 44 | return this.body; 45 | }; 46 | }); 47 | 48 | module.exports = Body; 49 | -------------------------------------------------------------------------------- /src/core/faked.js: -------------------------------------------------------------------------------- 1 | const Router = require('general-router'); 2 | const utils = require('ntils'); 3 | const Response = require('./response'); 4 | const sleep = require('../common/sleep'); 5 | 6 | const SHORT_METHDS = [ 7 | 'get', 'post', 'put', 'delete', 'patch', 8 | 'options', 'head', 'copy', 'link', 'unlink', 9 | 'lock', 'unlock', 'purge', 'propfind', 'view' 10 | ]; 11 | 12 | const EXECABLE_STRING = /^\/\/!exec/; 13 | const ABS_PATH_NO_PROTOCOL = /^\/\//; 14 | 15 | class Faked { 16 | 17 | constructor() { 18 | this.delay = 0; 19 | this.timeout = 10000; 20 | this.router = new Router(); 21 | this.global = Object.create(null); 22 | } 23 | 24 | when(methods, pattern, handler, options) { 25 | pattern = (pattern || '').trim(); 26 | if (!pattern) return; 27 | if (!utils.isArray(methods)) { 28 | methods = [methods]; 29 | } 30 | if (ABS_PATH_NO_PROTOCOL.test(pattern)) { 31 | this.when(methods, `http:${pattern}`, handler, options); 32 | this.when(methods, `https:${pattern}`, handler, options); 33 | } 34 | this.router.add([{ methods, pattern, handler, options }]); 35 | } 36 | 37 | _findRoute(request) { 38 | const matchedRoutes = this.router.get(request.url.split('?')[0]); 39 | const route = matchedRoutes.find( 40 | item => item.methods.indexOf(request.method.toUpperCase()) > -1 41 | ); 42 | if (!route) { 43 | if (this.debug) { 44 | this.warn(`Unmatched: "${request.method} ${request.url}"`); 45 | } 46 | return; 47 | } 48 | route.method = request.method; 49 | return route; 50 | } 51 | 52 | async _checkTimeout(ctx) { 53 | await sleep(this.timeout); 54 | if (ctx._sended) return; 55 | this.error(`Timeout: ${ctx.method} ${ctx.url}`); 56 | } 57 | 58 | async _invokeHandler(request, route, done) { 59 | const ctx = utils.create(request); 60 | this._checkTimeout(ctx); 61 | ctx.request = request; 62 | ctx.route = route; 63 | ctx.params = route.params; 64 | ctx._sended = false; 65 | ctx.send = (body, status, headers) => { 66 | if (ctx._sended) { 67 | return this.error( 68 | 'Send and return cannot coexist, and send cannot be repeated' 69 | ); 70 | } 71 | ctx._sended = true; 72 | status = status || 200; 73 | const res = new Response(body, { status, headers }); 74 | done(res); 75 | this.info(`Response "${ctx.method} ${ctx.url}"`, { 76 | headers: res.headers.toMap(), body: body 77 | }); 78 | }; 79 | const handler = route.handler; 80 | if (utils.isFunction(handler)) { 81 | const result = await handler.call(ctx, ctx, this.global); 82 | //如果 result 为 null,认为用户将要手动调用 this.send 方法 83 | //否则,自动调用 send,用 await 可使用户基于 Promise 完成异步操作 84 | if (!utils.isNull(result)) ctx.send(result); 85 | } else { 86 | ctx.send(handler); 87 | } 88 | } 89 | 90 | error(...args) { 91 | console.error('[faked]:', ...args); 92 | } 93 | 94 | log(...args) { 95 | console.log('[faked]:', ...args); 96 | } 97 | 98 | warn(...args) { 99 | console.warn('[faked]:', ...args); 100 | } 101 | 102 | info(...args) { 103 | console.info('[faked]:', ...args); 104 | } 105 | 106 | random(m, n) { 107 | return Math.floor(Math.random() * (n - m + 1) + m); 108 | } 109 | 110 | calcDelay(route) { 111 | const delay = utils.isNull(route.delay) ? this.delay : route.delay; 112 | if (utils.isFunction(delay)) return delay(route); 113 | if (utils.isNumber(delay)) return delay; 114 | if (utils.isArray(delay)) return this.random(delay[0], delay[1]); 115 | return 0; 116 | } 117 | 118 | async handle(request) { 119 | const route = this._findRoute(request); 120 | if (!route) return; 121 | this.info(`Request "${request.method} ${request.url}"`, { 122 | headers: request.headers.toMap(), 123 | query: request.query, 124 | body: request.body 125 | }); 126 | const delay = Number(this.calcDelay(route)); 127 | if (delay > 0) await sleep(delay); 128 | return new Promise(resolve => { 129 | this._invokeHandler(request, route, resolve); 130 | }); 131 | } 132 | 133 | fromJson(list) { 134 | list.forEach(item => { 135 | item.handler = item.handler || item.content; 136 | item.pattern = item.pattern || item.url; 137 | item.options = item.options || item.option; 138 | item.methods = item.methods || item.method || item.type; 139 | if (utils.isString(item.handler) && EXECABLE_STRING.test(item.handler)) { 140 | item.handler = new Function('context', 'global', item.handler); 141 | } 142 | this.when(item.methods, item.pattern, item.handler, item.options); 143 | }); 144 | } 145 | 146 | toJson() { 147 | return this.router.table.map(item => { 148 | return { 149 | methods: item.methods, 150 | pattern: item.pattern, 151 | handler: item.handler, 152 | options: item.options 153 | }; 154 | }); 155 | } 156 | } 157 | 158 | SHORT_METHDS.forEach(method => { 159 | Faked.prototype[method] = function (...args) { 160 | return this.when(method, ...args); 161 | }; 162 | }); 163 | 164 | module.exports = new Faked(); 165 | -------------------------------------------------------------------------------- /src/core/fetch.js: -------------------------------------------------------------------------------- 1 | const faked = require('./faked'); 2 | const Request = require('./request'); 3 | const Headers = require('./headers'); 4 | 5 | async function fetch(req, opts) { 6 | const response = await faked.handle(new Request(req, opts)); 7 | if (response) return response; 8 | if (opts.headers && opts.headers instanceof Headers) { 9 | opts = { 10 | ...opts, 11 | headers: opts.headers.toMap() 12 | }; 13 | } 14 | return window.originFetch(req, opts); 15 | } 16 | 17 | module.exports = fetch; 18 | -------------------------------------------------------------------------------- /src/core/headers.js: -------------------------------------------------------------------------------- 1 | const utils = require('ntils'); 2 | 3 | class HeaderItem { 4 | constructor(name, value) { 5 | this.name = name; 6 | this.value = value; 7 | } 8 | } 9 | 10 | class Headers { 11 | constructor(headers) { 12 | headers = headers || Object.create(null); 13 | if (headers.toMap) headers = headers.toMap(); 14 | this._list = []; 15 | utils.each(headers, (name, value) => { 16 | this.append(name, value); 17 | }); 18 | } 19 | 20 | append(name, value) { 21 | this._list.push(new HeaderItem(name, value)); 22 | } 23 | 24 | delete(name) { 25 | this._list = this._list.filter(item => item.name !== name); 26 | } 27 | 28 | set(name, value) { 29 | this.delete(name); 30 | this.append(name, value); 31 | } 32 | 33 | has(name) { 34 | !!this.find(item => item.name === name); 35 | } 36 | 37 | get(name) { 38 | let item = this._list.find(item => item.name === name); 39 | if (!item) return; 40 | return item.value; 41 | } 42 | 43 | getAll(name) { 44 | if (!name) return this._list; 45 | const items = this._list.filter(item => item.name === name); 46 | return items.map(item => item.value); 47 | } 48 | 49 | keys() { 50 | return this._list.map(item => item.name); 51 | } 52 | 53 | values() { 54 | return this._list.map(item => item.value); 55 | } 56 | 57 | *entries() { 58 | for (let item of this._list) { 59 | yield [item.name, item.value]; 60 | } 61 | } 62 | 63 | toMap() { 64 | const map = Object.create(null); 65 | this._list.forEach(item => { 66 | map[item.name] = item.value; 67 | }); 68 | return map; 69 | } 70 | 71 | forEach(fn, thisArg) { 72 | this._list.forEach(item => fn.call(thisArg || this, item.value, item.name)); 73 | } 74 | } 75 | 76 | module.exports = Headers; 77 | -------------------------------------------------------------------------------- /src/core/jsonp.js: -------------------------------------------------------------------------------- 1 | const utils = require('ntils'); 2 | const faked = require('./faked'); 3 | const Request = require('./request'); 4 | 5 | const jsonp = { param: 'callback', callback: null }; 6 | 7 | document.originCreateElement = document.createElement; 8 | 9 | document.createElement = function (tagName) { 10 | if (!utils.isNull(tagName)) tagName = tagName.toUpperCase(); 11 | const element = document.originCreateElement(tagName); 12 | if (tagName !== 'SCRIPT') return element; 13 | //-- 14 | const setAttribute = element.setAttribute; 15 | element.setAttribute = function (name, value) { 16 | if (name != 'src') { 17 | return setAttribute.call(this, name, value); 18 | } 19 | const request = new Request(value); 20 | const jsonpName = jsonp.callback || request.query[jsonp.param]; 21 | const jsonpFunc = window[jsonpName]; 22 | (async () => { 23 | const response = await faked.handle(request); 24 | if (!response) { 25 | return setAttribute.call(this, name, value); 26 | } 27 | jsonpFunc(await response.json()); 28 | const loadEvent = document.createEvent('HTMLEvents'); 29 | loadEvent.initEvent('load', false, false); 30 | element.dispatchEvent(loadEvent); 31 | })(); 32 | }; 33 | //-- 34 | delete element.src; 35 | Object.defineProperty(element, 'src', { 36 | get() { 37 | return this.getAttribute('src'); 38 | }, 39 | set(value) { 40 | this.setAttribute('src', value); 41 | } 42 | }); 43 | //-- 44 | return element; 45 | }; 46 | 47 | module.exports = jsonp; 48 | -------------------------------------------------------------------------------- /src/core/request.js: -------------------------------------------------------------------------------- 1 | const Headers = require('./headers'); 2 | const Body = require('./body'); 3 | const utils = require('ntils'); 4 | const querystring = require('querystring'); 5 | 6 | function getOrigin() { 7 | if (!location.origin) 8 | location.origin = 9 | location.protocol + 10 | '//' + 11 | location.hostname + 12 | (location.port ? ':' + location.port : ''); 13 | return location.origin; 14 | } 15 | 16 | function removeOrigin(url) { 17 | if (!url) return url; 18 | let origin = getOrigin(); 19 | if (url.indexOf(origin) != 0) return url; 20 | return url.replace(origin, ''); 21 | } 22 | 23 | function trimUrl(url) { 24 | return removeOrigin(url); 25 | } 26 | 27 | class Request extends Body { 28 | constructor(url, opts) { 29 | opts = opts || Object.create(null); 30 | super(opts.body); 31 | this.opts = Object.create(null); 32 | if (!utils.isString(url)) { 33 | utils.copy(url, this.opts); 34 | } else { 35 | this.opts.url = url; 36 | } 37 | utils.copy(opts, this.opts); 38 | this.opts.url = trimUrl(this.opts.url); 39 | this.url = this.opts.url; 40 | this.method = this.opts.method || 'GET'; 41 | this.headers = new Headers(this.opts.headers); 42 | this.context = this.opts.context || window; 43 | this.referrer = this.opts.referrer || location.href; 44 | this.mode = this.opts.mode; 45 | this.credentials = this.opts.credentials; 46 | this.redirect = this.opts.redirect; 47 | this.integrity = this.opts.integrity; 48 | this.cache = this.opts.cache; 49 | } 50 | 51 | //这是一个扩展属性, 不是标准 API 52 | get body() { 53 | const contentType = (this.headers.get('Content-Type') || '') 54 | .split(';')[0]; 55 | switch (contentType) { 56 | case 'application/json': 57 | case 'text/json': 58 | return utils.isString(this.rawBody) 59 | ? JSON.parse(this.rawBody) 60 | : this.rawBody; 61 | case 'application/x-www-form-urlencoded': 62 | return utils.isString(this.rawBody) 63 | ? querystring.parse(this.rawBody) 64 | : this.rawBody; 65 | default: 66 | return this.rawBody; 67 | } 68 | } 69 | 70 | set url(url) { 71 | this._url = url; 72 | if (!url) return; 73 | this.query = querystring.parse(url.split('?')[1]); 74 | } 75 | 76 | get url() { 77 | return this._url; 78 | } 79 | 80 | set body(value) { 81 | this.rawBody = value; 82 | } 83 | 84 | clone() { 85 | return new Request(this.url, this.opts); 86 | } 87 | } 88 | 89 | module.exports = Request; 90 | -------------------------------------------------------------------------------- /src/core/response.js: -------------------------------------------------------------------------------- 1 | const Headers = require('./headers'); 2 | const Body = require('./body'); 3 | const status = require('./status'); 4 | const utils = require('ntils'); 5 | 6 | //response 一定要保持和标准 API 一致 7 | class Response extends Body { 8 | constructor(body, opts) { 9 | super(body); 10 | this.opts = opts || Object.create(null); 11 | this.type = this.opts.type; 12 | this.url = this.opts.url; 13 | this.useFinalURL = this.opts.useFinalURL; 14 | this.status = this.opts.status; 15 | this.headers = new Headers(this.opts.headers); 16 | this.headers.set('status', this.status); 17 | this.headers.set('Date', new Date().toString()); 18 | this.headers.set('X-Powered-By', 'Faked'); 19 | this.headers.set('Cache-Control', 'max-age=0'); 20 | } 21 | 22 | get ok() { 23 | return this.status >= 200 && this.status < 299; 24 | } 25 | 26 | get statusText() { 27 | return status[this.status]; 28 | } 29 | 30 | clone() { 31 | return new Response(this.body, this.opts); 32 | } 33 | 34 | error() { 35 | const opts = utils.clone(opts); 36 | opts.status = 500; 37 | return new Response(this.body, this.opts); 38 | } 39 | 40 | redirect() { 41 | throw new Error('Faked does not support redirect'); 42 | } 43 | } 44 | 45 | module.exports = Response; 46 | -------------------------------------------------------------------------------- /src/core/status.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | '100': 'Continue', 3 | '101': 'Switching Protocols', 4 | '102': 'Processing', 5 | '200': 'OK', 6 | '201': 'Created', 7 | '202': 'Accepted', 8 | '203': 'Non-Authoritative Information', 9 | '204': 'No Content', 10 | '205': 'Reset Content', 11 | '206': 'Partial Content', 12 | '207': 'Multi-Status', 13 | '208': 'Already Reported', 14 | '226': 'IM Used', 15 | '300': 'Multiple Choices', 16 | '301': 'Moved Permanently', 17 | '302': 'Found', 18 | '303': 'See Other', 19 | '304': 'Not Modified', 20 | '305': 'Use Proxy', 21 | '307': 'Temporary Redirect', 22 | '308': 'Permanent Redirect', 23 | '400': 'Bad Request', 24 | '401': 'Unauthorized', 25 | '402': 'Payment Required', 26 | '403': 'Forbidden', 27 | '404': 'Not Found', 28 | '405': 'Method Not Allowed', 29 | '406': 'Not Acceptable', 30 | '407': 'Proxy Authentication Required', 31 | '408': 'Request Timeout', 32 | '409': 'Conflict', 33 | '410': 'Gone', 34 | '411': 'Length Required', 35 | '412': 'Precondition Failed', 36 | '413': 'Payload Too Large', 37 | '414': 'URI Too Long', 38 | '415': 'Unsupported Media Type', 39 | '416': 'Range Not Satisfiable', 40 | '417': 'Expectation Failed', 41 | '418': 'I\'m a teapot', 42 | '421': 'Misdirected Request', 43 | '422': 'Unprocessable Entity', 44 | '423': 'Locked', 45 | '424': 'Failed Dependency', 46 | '425': 'Unordered Collection', 47 | '426': 'Upgrade Required', 48 | '428': 'Precondition Required', 49 | '429': 'Too Many Requests', 50 | '431': 'Request Header Fields Too Large', 51 | '451': 'Unavailable For Legal Reasons', 52 | '500': 'Internal Server Error', 53 | '501': 'Not Implemented', 54 | '502': 'Bad Gateway', 55 | '503': 'Service Unavailable', 56 | '504': 'Gateway Timeout', 57 | '505': 'HTTP Version Not Supported', 58 | '506': 'Variant Also Negotiates', 59 | '507': 'Insufficient Storage', 60 | '508': 'Loop Detected', 61 | '509': 'Bandwidth Limit Exceeded', 62 | '510': 'Not Extended', 63 | '511': 'Network Authentication Required' 64 | }; 65 | -------------------------------------------------------------------------------- /src/core/xhr.js: -------------------------------------------------------------------------------- 1 | const faked = require('./faked'); 2 | const utils = require('ntils'); 3 | const Request = require('./request'); 4 | const querystring = require('querystring'); 5 | const EventEmitter = require('events'); 6 | 7 | const READY_STATES = { 8 | UNSENT: 0, OPENED: 1, HEADERS_RECEIVED: 2, LOADING: 3, DONE: 4 9 | }; 10 | 11 | class XMLHttpRequest extends EventEmitter { 12 | 13 | constructor(...args) { 14 | super(...args); 15 | utils.copy(READY_STATES, this); 16 | this._req = new Request(); 17 | this.sendAsBinary = this.send; 18 | this.openRequest = this.open; 19 | } 20 | 21 | _changeReadyState(state) { 22 | this.readyState = state; 23 | const event = Object.create(null); 24 | if (this.onreadystatechange) { 25 | this.onreadystatechange(event); 26 | } 27 | if (this.readyState === 4) { 28 | if (this.onload) this.onload(event); 29 | this.emit('load', event); 30 | } 31 | } 32 | 33 | abort() { 34 | if (this._originXhr) { 35 | return this._originXhr.abort(); 36 | } 37 | faked.warn('XHR Abort'); 38 | } 39 | 40 | getAllResponseHeaders() { 41 | if (this._originXhr) { 42 | return this._originXhr.getAllResponseHeaders(name); 43 | } 44 | if (!this._res) return; 45 | return this._res.headers.getAll().map(header => { 46 | return `${header.name}:${header.value}`; 47 | }).join('\r\n'); 48 | } 49 | 50 | getResponseHeader(name) { 51 | if (this._originXhr) { 52 | return this._originXhr.getResponseHeader(name); 53 | } 54 | if (!this._res) return; 55 | return this._res.headers.get(name); 56 | } 57 | 58 | //eslint-disable-next-line 59 | open(method, url, isAsync, user, password) { 60 | this._openArgs = arguments; 61 | this._req = new Request(this._req, { 62 | url, 63 | method 64 | }); 65 | this._isAsync = isAsync; 66 | } 67 | 68 | overrideMimeType(mime) { 69 | if (this._originXhr) { 70 | return this._originXhr.overrideMimeType(mime); 71 | } 72 | this._mime = mime; 73 | } 74 | 75 | async send(data) { 76 | const contentType = this._req.headers.get('Content-Type'); 77 | if (contentType == 'application/x-www-form-urlencoded') { 78 | this._req.body = querystring.parse(data); 79 | } else { 80 | this._req.body = data; 81 | } 82 | this._res = await faked.handle(this._req); 83 | if (!this._res) { 84 | return this._originSend(data); 85 | } 86 | this._changeReadyState(READY_STATES.OPENED); 87 | this._changeReadyState(READY_STATES.HEADERS_RECEIVED); 88 | if (this._isAsync === false) { 89 | faked.warn( 90 | 'Unable to synchronize request and has been replaced with an asynchronous request' 91 | ); 92 | } 93 | if (this._mime) { 94 | this._res.headers.set('Content-Type', this._mime); 95 | } 96 | this._changeReadyState(READY_STATES.LOADING); 97 | this._changeReadyState(READY_STATES.DONE); 98 | } 99 | 100 | _originSend(data) { 101 | this._originXhr = new OriginXMLHttpRequest(); //eslint-disable-line 102 | this._originXhr.withCredentials = this.withCredentials; 103 | this._originXhr.timeout = this.timeout; 104 | this._originXhr.onload = this.onload; 105 | this._originXhr.onreadystatechange = this.onreadystatechange; 106 | this._originXhr.addEventListener('load', event => { 107 | this.emit('load', event); 108 | }); 109 | this._originXhr.open(...this._openArgs); 110 | for (let entry of this._req.headers.entries()) { 111 | if (typeof entry[0] === 'string') { 112 | this.setRequestHeader(entry[0], entry[1]); 113 | } 114 | } 115 | return this._originXhr.send(data); 116 | } 117 | 118 | setRequestHeader(name, value) { 119 | if (this._originXhr) { 120 | this._originXhr.setRequestHeader(name, value); 121 | } 122 | this._req.headers.set(name, value); 123 | } 124 | 125 | get responseType() { 126 | if (this._originXhr) { 127 | return this._originXhr.responseType; 128 | } 129 | return this._responseType; 130 | } 131 | 132 | set responseType(value) { 133 | if (this._originXhr) { 134 | this._originXhr.responseType = value; 135 | } 136 | this._responseType = value; 137 | } 138 | 139 | get responseURL() { 140 | if (this._originXhr) { 141 | return this._originXhr.responseURL; 142 | } 143 | if (!this._res) return null; 144 | return this._req.url; 145 | } 146 | 147 | get responseText() { 148 | if (this._originXhr) { 149 | return this._originXhr.responseText; 150 | } 151 | if (utils.isString(this.response)) return this.response; 152 | return JSON.stringify(this.response); 153 | } 154 | 155 | get responseXML() { 156 | if (this._originXhr) { 157 | return this._originXhr.responseXML; 158 | } 159 | if (utils.isString(this.response)) return this.response; 160 | return JSON.stringify(this.response); 161 | } 162 | 163 | get responseJSON() { 164 | if (this._originXhr) { 165 | return this._originXhr.responseJSON; 166 | } 167 | return JSON.parse(this.responseText); 168 | } 169 | 170 | get response() { 171 | if (this._originXhr) { 172 | return this._originXhr.response; 173 | } 174 | if (!this._res) return null; 175 | if (!this.responseType || this.responseType == 'text') { 176 | return this._res.bodyText; 177 | } else if ( 178 | this.responseType == 'blob' && 179 | Object.prototype.toString.call(this._res.body) != '[object Blob]' 180 | ) { 181 | return new Blob([this._res.bodyText]); 182 | } else { 183 | return this._res.body; 184 | } 185 | } 186 | 187 | get status() { 188 | if (this._originXhr) { 189 | return this._originXhr.status; 190 | } 191 | if (!this._res) return null; 192 | return this._res.status; 193 | } 194 | 195 | get statusText() { 196 | if (this._originXhr) { 197 | return this._originXhr.statusText; 198 | } 199 | if (!this._res) return null; 200 | return this._res.statusText; 201 | } 202 | 203 | get withCredentials() { 204 | if (this._originXhr) { 205 | return this._originXhr.withCredentials; 206 | } 207 | return this._withCredentials || 0; 208 | } 209 | 210 | set withCredentials(value) { 211 | if (this._originXhr) { 212 | this._originXhr.withCredentials = value; 213 | } 214 | this._withCredentials = value; 215 | } 216 | 217 | get timeout() { 218 | if (this._originXhr) { 219 | return this._originXhr.timeout; 220 | } 221 | return this._timeout || 0; 222 | } 223 | 224 | set timeout(value) { 225 | if (this._originXhr) { 226 | this._originXhr.timeout = value; 227 | } 228 | this._timeout = value; 229 | } 230 | 231 | get readyState() { 232 | if (this._originXhr) { 233 | return this._originXhr.readyState; 234 | } 235 | return this._readyState || 0; 236 | } 237 | 238 | set readyState(value) { 239 | if (this._originXhr) { 240 | this._originXhr.readyState = value; 241 | } 242 | this._readyState = value; 243 | } 244 | } 245 | 246 | utils.copy(READY_STATES, XMLHttpRequest); 247 | 248 | module.exports = XMLHttpRequest; 249 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | const faked = require('./core/faked'); 2 | 3 | if (typeof window == 'undefined') { 4 | module.exports = faked; 5 | } else if (window.faked) { 6 | module.exports = window.faked; 7 | } else { 8 | faked.Headers = require('./core/headers'); 9 | faked.Request = require('./core/request'); 10 | faked.Response = require('./core/response'); 11 | faked.XMLHttpRequest = require('./core/xhr'); 12 | faked.fetch = require('./core/fetch'); 13 | faked.jsonp = require('./core/jsonp'); 14 | 15 | window.OriginHeaders = window.OriginHeaders || window.Headers; 16 | window.OriginRequest = window.OriginRequest || window.Request; 17 | window.OriginResponse = window.OriginResponse || window.Response; 18 | window.originFetch = window.originFetch || window.fetch; 19 | window.OriginXMLHttpRequest = window.OriginXMLHttpRequest || window.XMLHttpRequest; 20 | 21 | window.Headers = faked.Headers; 22 | window.Request = faked.Request; 23 | window.Response = faked.Response; 24 | window.fetch = faked.fetch; 25 | window.XMLHttpRequest = faked.XMLHttpRequest; 26 | 27 | module.exports = window.faked = faked; 28 | } -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const pkg = require('./package.json'); 2 | 3 | module.exports = function (webpackConfig) { 4 | webpackConfig.output.library = pkg.name; 5 | webpackConfig.output.libraryTarget = 'umd'; 6 | webpackConfig.output.umdNamedDefine = true; 7 | }; --------------------------------------------------------------------------------