diff --git a/dist/index.js b/dist/index.js index eb6b9c3..b06ca3d 100644 --- a/dist/index.js +++ b/dist/index.js @@ -6,17 +6,17 @@ parcelRequire=function(e,r,t,n){var i,o="function"==typeof parcelRequire&&parcelRequire,u="function"==typeof require&&require;function f(t,n){if(!r[t]){if(!e[t]){var i="function"==typeof parcelRequire&&parcelRequire;if(!n&&i)return i(t,!0);if(o)return o(t,!0);if(u&&"string"==typeof t)return u(t);var c=new Error("Cannot find module '"+t+"'");throw c.code="MODULE_NOT_FOUND",c}p.resolve=function(r){return e[t][1][r]||r},p.cache={};var l=r[t]=new f.Module(t);e[t][0].call(l.exports,p,l,l.exports,this)}return r[t].exports;function p(e){return f(p.resolve(e))}}f.isParcelRequire=!0,f.Module=function(e){this.id=e,this.bundle=f,this.exports={}},f.modules=e,f.cache=r,f.parent=o,f.register=function(r,t){e[r]=[function(e,r){r.exports=t},{}]};for(var c=0;c{const a=`${n.HOME}/.webmynarc`,o=`${t.default.resolve(process.cwd())}/.webmynarc`,r=[{type:"list",name:"configPath",message:"Where do you want to store the WebMyna configuration file?",choices:[{name:`Global (${a})`,value:a},{name:`Project (${o})`,value:o}],default:"~/.webmynarc"},{type:"input",name:"recordingsPath",message:"Where do you want to store the WebMyna recordings?",default:e=>e.configPath.replace(".webmynarc",".webmyna"),validate:function(e){return!!e.length||"Please, set a folder path"}}];return e.default.prompt(r)},askApiConfiguration:()=>{n.HOME,t.default.resolve(process.cwd());return e.default.prompt([{type:"input",name:"name",message:"What is the name of the API? (in slug format)",validate:function(e){return!(!e.length||!e.match(/^[A-Za-z0-9]+(?:-[A-Za-z0-9]+)*$/))||"Please, set an api name slugified"}},{type:"input",name:"url",message:"What is the basic url of the real API? (without slash at the end)",validate:function(e){return!(!e.length||!e.match(/[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)?/gi)||e.endsWith("/"))||"Please set a valid url, without final slash"}},{type:"confirm",name:"needToken",message:"Does the API need an authentication token?"},{type:"input",name:"tokenKey",message:"What is the name of the http header with the authentication token?",default:"authorization",when:e=>e.needToken},{type:"input",name:"tokenPrefix",message:"Should the token be prefixed in the header?",default:"Bearer",when:e=>e.needToken},{type:"confirm",name:"continue",message:"Do you want to configure another API?"}])}};exports.questions=o; +"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.questions=void 0;var e=a(require("inquirer")),t=a(require("path"));function a(e){return e&&e.__esModule?e:{default:e}}const n=process.env,o={askConfigurationLocation:()=>{const a=`${n.HOME}/.webmynarc`,o=`${t.default.resolve(process.cwd())}/.webmynarc`,i=[{type:"list",name:"configPath",message:"Where do you want to store the WebMyna configuration file?",choices:[{name:`Global (${a})`,value:a},{name:`Project (${o})`,value:o}],default:"~/.webmynarc"},{type:"input",name:"recordingsPath",message:"Where do you want to store the WebMyna recordings?",default:e=>e.configPath.replace(".webmynarc",".webmyna"),validate:function(e){return!!e.length||"Please, set a folder path"}}];return e.default.prompt(i)},askApiConfiguration:()=>{n.HOME,t.default.resolve(process.cwd());return e.default.prompt([{type:"input",name:"name",message:"What is the name of the API? (in slug format)",validate:function(e){return!(!e.length||!e.match(/^[A-Za-z0-9]+(?:-[A-Za-z0-9]+)*$/))||"Please, set an api name slugified"}},{type:"input",name:"url",message:"What is the basic url of the real API? (without slash at the end)",validate:function(e){return!(!e.length||!e.match(/[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)?/gi)||e.endsWith("/"))||"Please set a valid url, without final slash"}},{type:"confirm",name:"requiresAuthentication",message:"Does the API need an authentication token?"},{type:"input",name:"tokenKey",message:"What is the name of the http header with the authentication token?",default:"authorization",when:e=>e.requiresAuthentication},{type:"input",name:"tokenPrefix",message:"Should the token be prefixed in the header?",default:"Bearer",when:e=>e.requiresAuthentication},{type:"confirm",name:"continue",message:"Do you want to configure another API?"}])}};exports.questions=o; },{}],"ANRV":[function(require,module,exports) { "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.MODE_PLAYER=exports.MODE_RECORDER=void 0;const e="recorder";exports.MODE_RECORDER="recorder";const r="player";exports.MODE_PLAYER="player"; },{}],"BDLb":[function(require,module,exports) { -"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.default=void 0;var e=r(require("signale")),t=r(require("convict")),o=r(require("rc")),a=require("./modes.js");function r(e){return e&&e.__esModule?e:{default:e}}const n=process.env,d=n.WEB_MYNA_MODE===a.MODE_PLAYER||!n.WEB_MYNA_MODE,i=(0,o.default)("webmyna",{apis:[],recordingsPath:null});t.default.addFormat({name:"apis",validate:(o,a)=>{if(!Array.isArray(o))throw new Error("must be of type Array");o.map((t,a)=>{const r=t.name?`${t.name.toUpperCase().replace(/-/g,"_")}_TOKEN`:null;if(e.default.debug(r),r||d){const e=n[r];o[a].token=d?"webMynaPlayerToken":e}});for(const e of o)(0,t.default)(a.children).load(e).validate()}});const l=(0,t.default)({apis:{doc:"A collection of APIs to mock.",format:"apis",default:[],children:{name:{doc:"The API name, used as id. So it have to be a slug format, but steel readable",format:String,default:null},url:{doc:"The API main endpoint URL",format:"url",default:null},token:{doc:"Value of the authorization token to add in http headers",format:String,default:null},tokenKey:{doc:"Name of the http header for authentification",format:String,default:"authorization"},tokenPrefix:{doc:"Token prefix into the http header value",format:String,default:"Bearer"}}},env:{doc:"The application environment.",format:["production","development","test"],default:"development",env:"NODE_ENV"},mode:{doc:"Recording mode in which WebMyna is started",format:["player","recorder"],default:"player",env:"WEB_MYNA_MODE"},proxyPort:{doc:"The proxy port to bind.",format:"port",default:i.proxyPort||8080,env:"WEB_MYNA_PROXY_PORT"},recordingsPath:{doc:"path to the main recording folder",format:String,default:i.recordingsPath||"/tmp/webmyna",env:"WEB_MYNA_RECORDINGS_PATH"}});l.load({apis:i.apis}),l.validate({allowed:"strict"});var u=l.getProperties();exports.default=u; +"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.default=exports.getMissingTokensMessage=exports.getMissingEnvironmentTokens=exports.getApiTokenName=void 0;var e=a(require("convict")),t=a(require("rc")),o=require("./modes.js");function a(e){return e&&e.__esModule?e:{default:e}}const n=process.env,r=n.WEB_MYNA_MODE===o.MODE_PLAYER||!n.WEB_MYNA_MODE,i=(0,t.default)("webmyna",{apis:[],recordingsPath:null}),s=e=>"string"==typeof e.name&&e.name?`${e.name.toUpperCase().replace(/-/g,"_")}_TOKEN`:"fakeWebMynaTokenName";exports.getApiTokenName=s;const d=(e=i.apis,t=n)=>{let o=[];return e.filter(e=>e.requiresAuthentication).map(e=>s(e)).map(e=>{t[e]||o.push(e)}),o};exports.getMissingEnvironmentTokens=d;const l=(e,t)=>{const o=e.apis.filter(e=>t.includes(s(e))).map(e=>e.name),a=o.length>1;return`\nYou have declared ${o.length} api${a?"s":""} as requiring an authentication token: ${o.join(", ")}.\n\nYou must therefore declare the following token${a?"s":""} as an environment variable: ${t.map(e=>`\n => ${e}`).join("")}`};exports.getMissingTokensMessage=l,e.default.addFormat({name:"apis",validate:(t,o)=>{if(!Array.isArray(t))throw new Error("must be of type Array");t.map((e,o)=>{const a=s(e),i=n[a]||null;i&&e.requiresAuthentication&&!r&&(t[o].token=i)});for(const a of t)(0,e.default)(o.children).load(a).validate()}});const u=(0,e.default)({apis:{doc:"A collection of APIs to mock.",format:"apis",default:[],children:{name:{doc:"The API name, used as id. So it have to be a slug format, but steel readable",format:String,default:null},url:{doc:"The API main endpoint URL",format:"url",default:null},requiresAuthentication:{doc:"Does the API require authentication or not",format:Boolean,default:!1},token:{doc:"Value of the authorization token to add in http headers",format:String,default:"webMynaDefaultToken"},tokenKey:{doc:"Name of the http header for authentification",format:String,default:"authorization"},tokenPrefix:{doc:"Token prefix into the http header value",format:String,default:"Bearer"}}},env:{doc:"The application environment.",format:["production","development","test"],default:"development",env:"NODE_ENV"},mode:{doc:"Recording mode in which WebMyna is started",format:["player","recorder"],default:"player",env:"WEB_MYNA_MODE"},proxyPort:{doc:"The proxy port to bind.",format:"port",default:i.proxyPort||8080,env:"WEB_MYNA_PROXY_PORT"},recordingsPath:{doc:"path to the main recording folder",format:String,default:i.recordingsPath||"/tmp/webmyna",env:"WEB_MYNA_RECORDINGS_PATH"}});u.load({apis:i.apis}),u.validate({allowed:"strict"});var p=u.getProperties();exports.default=p; },{"./modes.js":"ANRV"}],"I53T":[function(require,module,exports) { "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.buildHar=exports.buildFlattenedNameValueMap=exports.transformBinaryToUtf8=void 0;const e=e=>null==e?e:Buffer.from(String(e),"binary").toString("utf8");exports.transformBinaryToUtf8=e;const t=t=>t?Object.keys(t).reduce((r,s)=>{const a=t[s];return Array.isArray(a)?r.concat(a.map(t=>({name:s,value:e(t)}))):r.concat({name:s,value:e(a)})},[]):[];exports.buildFlattenedNameValueMap=t;const r=(e,r,s,a,n)=>{const o={method:e.method,url:a,httpVersion:"HTTP/1.1",cookies:[],headers:t(e.headers),queryString:t(e.query),postData:{},headersSize:JSON.stringify(e.headers).length,bodySize:e.body?e.body.length:0,comment:""},i={status:r.statusCode||0,statusText:r.statusMessage||"",httpVersion:"HTTP/1.1",cookies:[],headers:t(r.headers),content:JSON.parse(s),redirectURL:"",headersSize:r.headers?JSON.stringify(r.headers).length:0,bodySize:JSON.stringify(s).length,comment:""};return{entries:[{pageref:n,startedDateTime:(new Date).toISOString(),time:50,request:o,response:i,cache:{},timings:{send:0,receive:0,wait:500},comment:""}]}};exports.buildHar=r; },{}],"WPe8":[function(require,module,exports) { "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.myna=void 0;var e=f(require("signale")),r=f(require("query-string")),t=f(require("fs")),s=f(require("path")),n=f(require("md5")),u=require("./harFactory.js"),o=f(require("./config.js")),a=require("./modes.js");function f(e){return e&&e.__esModule?e:{default:e}}const i=f=>(i,d,c)=>{const l=s.default.resolve(o.default.recordingsPath,f.name);t.default.existsSync(l)||o.default.mode!==a.MODE_RECORDER||t.default.mkdirSync(l,parseInt("0777",8));const p=`${f.url}${i.path}?${r.default.stringify(i.query)}`;e.default.info("Request on path",p);const y=`${l}/${(0,n.default)(p)}.har`;if(t.default.existsSync(y)){e.default.success("THE RECORD EXIST! PLAY IT");const r=t.default.readFileSync(y,"utf8"),s=JSON.parse(r).entries[0];return d.json(s.response.content)}if(o.default.mode===a.MODE_PLAYER)return d.status(421).json({message:"This request has not been recorded by WebMyna"});const{write:m,end:h}=d,q=[];d.write=function(e){q.push(Buffer.from(e)),m.apply(d,arguments)},d.end=function(r){r&&q.push(Buffer.from(r));const s=Buffer.concat(q).toString("utf8");try{const r=(0,u.buildHar)(i,d,s,p,f.name);t.default.writeFileSync(y,JSON.stringify(r,null,2),{encoding:"utf8"})}catch(n){e.default.error(n),e.default.error(`Impossible to create .har from response to ${p}`)}h.apply(d,arguments)},c()};exports.myna=i; },{"./harFactory.js":"I53T","./config.js":"BDLb","./modes.js":"ANRV"}],"ffN9":[function(require,module,exports) { -"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.app=void 0;var e=a(require("express")),r=a(require("http-proxy-middleware")),t=a(require("./config.js")),o=require("./myna.js");function a(e){return e&&e.__esModule?e:{default:e}}const n=(0,e.default)();exports.app=n,n.get("/",(e,r)=>r.json({message:"Hello Web Myna"}));for(const i of t.default.apis){const e={changeOrigin:!0,proxyTimeout:5e3,timeout:5e3,target:i.url,ignorePath:!1,pathRewrite:e=>e.replace(`/${i.name}`,""),onProxyReq:e=>e.setHeader(`${i.tokenKey||"authorization"}`,`${i.tokenPrefix||"Bearer"} ${i.token}`)};n.use(`/${i.name}`,(0,o.myna)(i),(0,r.default)(e))} -},{"./config.js":"BDLb","./myna.js":"WPe8"}],"Focm":[function(require,module,exports) { -"use strict";var e=f(require("chalk")),t=f(require("clear")),o=f(require("figlet")),n=f(require("rc")),a=f(require("signale")),i=f(require("boxen")),r=f(require("fs")),l=require("./questions.js"),u=f(require("../src/config.js")),s=require("../src/app.js");function f(e){return e&&e.__esModule?e:{default:e}}const d=()=>{(0,t.default)(),a.default.log(e.default.yellow(o.default.textSync("Web Myna",{horizontalLayout:"full"})))},c=()=>{d(),a.default.log((0,i.default)('You must now configure at least one API.\n \nTo do this, you will need to provide following informations:\n * the name of the API in slug format. For example "rick-and-morty" to call the API on http://localhost/rick-and-morty\n * the real basic url of the API. For example, https://rickandmortyapi.com/api (without final slash)\n\nOptionally, if the API needs an authentication token\n * the name of the http header to add\n * the possible prefix to add to the token\n ',{padding:1}))};d();const h=(0,n.default)("webmyna");if(h.config)s.app.listen(u.default.proxyPort,()=>{a.default.info(`Web Myna is starded on port ${u.default.proxyPort} in environment ${u.default.env}`)});else{(async()=>{const e=await l.questions.askConfigurationLocation();d(),a.default.log((0,i.default)('You must now configure at least one API.\n \nTo do this, you will need to provide following informations:\n * the name of the API in slug format. For example "rick-and-morty" to call the API on http://localhost/rick-and-morty\n * the real basic url of the API. For example, https://rickandmortyapi.com/api (without final slash)\n\nOptionally, if the API needs an authentication token\n * the name of the http header to add\n * the possible prefix to add to the token\n ',{padding:1}));let t=[],o=!0;for(;o;){t.length&&a.default.log(`You already have ${t.length} api.s configured`);const e=await l.questions.askApiConfiguration();t.push({name:e.name,url:e.url,tokenKey:e.tokenKey||"authorization",tokenPrefix:e.tokenPrefix||"Bearer"}),o=e.continue}d();const n={recordingsPath:e.recordingsPath,apis:t};try{r.default.existsSync(e.recordingsPath)||r.default.mkdirSync(e.recordingsPath),r.default.writeFileSync(e.configPath,JSON.stringify(n,null,2),{encoding:"utf8"}),a.default.log("The configuration of Web Myna is completed and saved. You can restart it, everything should work!")}catch(u){a.default.log(`Error when saving your configuration of the ${e.configPath} file.`),a.default.error(u)}process.exit()})()}process.on("SIGINT",function(){(0,t.default)(),a.default.log(e.default.yellow(o.default.textSync("Bye",{horizontalLayout:"full"}))),process.exit(1)}); +"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.app=void 0;var e=n(require("express")),r=n(require("http-proxy-middleware")),t=n(require("./config.js")),o=require("./modes.js"),a=require("./myna.js");function n(e){return e&&e.__esModule?e:{default:e}}const i=(0,e.default)();exports.app=i,i.get("/",(e,r)=>r.json({message:"Hello Web Myna"}));for(const s of t.default.apis)if(t.default.mode===o.MODE_RECORDER){const e={changeOrigin:!0,proxyTimeout:5e3,timeout:5e3,target:s.url,ignorePath:!1,pathRewrite:e=>e.replace(`/${s.name}`,""),onProxyReq:e=>{s.requiresAuthentication&&e.setHeader(`${s.tokenKey||"authorization"}`,`${s.tokenPrefix||"Bearer"} ${s.token}`)}};i.use(`/${s.name}`,(0,a.myna)(s),(0,r.default)(e))}else i.use(`/${s.name}`,(0,a.myna)(s)); +},{"./config.js":"BDLb","./modes.js":"ANRV","./myna.js":"WPe8"}],"Focm":[function(require,module,exports) { +"use strict";var e=h(require("chalk")),t=h(require("clear")),o=h(require("figlet")),n=h(require("rc")),a=h(require("signale")),r=h(require("boxen")),i=h(require("fs")),l=h(require("lodash.omit")),u=require("./questions.js"),s=c(require("../src/config.js")),f=require("../src/app.js");function d(){if("function"!=typeof WeakMap)return null;var e=new WeakMap;return d=function(){return e},e}function c(e){if(e&&e.__esModule)return e;var t=d();if(t&&t.has(e))return t.get(e);var o={};if(null!=e){var n=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var a in e)if(Object.prototype.hasOwnProperty.call(e,a)){var r=n?Object.getOwnPropertyDescriptor(e,a):null;r&&(r.get||r.set)?Object.defineProperty(o,a,r):o[a]=e[a]}}return o.default=e,t&&t.set(e,o),o}function h(e){return e&&e.__esModule?e:{default:e}}const p=()=>{(0,t.default)(),a.default.log(e.default.yellow(o.default.textSync("Web Myna",{horizontalLayout:"full"})))},g=()=>{p(),a.default.log((0,r.default)('You must now configure at least one API.\n \nTo do this, you will need to provide following informations:\n * the name of the API in slug format. For example "rick-and-morty" to call the API on http://localhost/rick-and-morty\n * the real basic url of the API. For example, https://rickandmortyapi.com/api (without final slash)\n\nOptionally, if the API needs an authentication token\n * the name of the http header to add\n * the possible prefix to add to the token\n ',{padding:1}))};p();const y=(0,n.default)("webmyna");if(y.config){const e=(0,s.getMissingEnvironmentTokens)();if(e.length){const t=(0,s.getMissingTokensMessage)(s.default,e);a.default.log((0,r.default)(t,{padding:1,margin:1,borderColor:"red",align:"center"})),process.exit()}else f.app.listen(s.default.proxyPort,()=>{a.default.info(`Web Myna is starded on port ${s.default.proxyPort} in environment ${s.default.env}`)})}else{(async()=>{const e=await u.questions.askConfigurationLocation();p(),a.default.log((0,r.default)('You must now configure at least one API.\n \nTo do this, you will need to provide following informations:\n * the name of the API in slug format. For example "rick-and-morty" to call the API on http://localhost/rick-and-morty\n * the real basic url of the API. For example, https://rickandmortyapi.com/api (without final slash)\n\nOptionally, if the API needs an authentication token\n * the name of the http header to add\n * the possible prefix to add to the token\n ',{padding:1}));let t=[],o=!0;for(;o;){t.length&&a.default.log(`You already have ${t.length} api.s configured`);const e=await u.questions.askApiConfiguration();t.push((0,l.default)(e,["continue"])),o=e.continue}p();const n={recordingsPath:e.recordingsPath,apis:t};try{i.default.existsSync(e.recordingsPath)||i.default.mkdirSync(e.recordingsPath),i.default.writeFileSync(e.configPath,JSON.stringify(n,null,2),{encoding:"utf8"}),a.default.log("The configuration of Web Myna is completed and saved. You can restart it, everything should work!")}catch(s){a.default.log(`Error when saving your configuration of the ${e.configPath} file.`),a.default.error(s)}process.exit()})()}process.on("SIGINT",function(){(0,t.default)(),a.default.log(e.default.yellow(o.default.textSync("Bye",{horizontalLayout:"full"}))),process.exit()}); },{"./questions.js":"oquI","../src/config.js":"BDLb","../src/app.js":"ffN9"}]},{},["Focm"], null) \ No newline at end of file