├── .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 | };
--------------------------------------------------------------------------------