+112
| 'use strict'; | ||
| const fs = require('fs'); | ||
| const got = require('got'); | ||
| const out = require('simple-output'); | ||
| const parseHeaders = require('parse-headers'); | ||
| const {parse} = require('query-string'); | ||
| const saveCmd = require('./save'); | ||
| function addCmd({addOptions, mockFolderName, url}) { | ||
| const customHeaders = [].concat(addOptions.header).join('\n'); | ||
| function getData() { | ||
| let data = addOptions.data; | ||
| let body; | ||
| let type; | ||
| let form = false; | ||
| let json = false; | ||
| try { | ||
| data = fs.readFileSync(data).toString(); | ||
| } catch (e) { | ||
| } | ||
| try { | ||
| body = JSON.parse(data); | ||
| type = 'application/json'; | ||
| json = true; | ||
| } catch (e) { | ||
| try { | ||
| body = parse(data); | ||
| type = 'application/x-www-form-urlencoded'; | ||
| form = true; | ||
| } catch (e) { | ||
| body = data; | ||
| type = 'text/plain'; | ||
| } | ||
| } | ||
| return { | ||
| body, | ||
| form, | ||
| json, | ||
| type | ||
| }; | ||
| } | ||
| function getOpts(method) { | ||
| let opts = { | ||
| headers: parseHeaders(customHeaders), | ||
| json: !addOptions.nojson, | ||
| method: method.trim().toLowerCase() | ||
| }; | ||
| // Set body data, skips only TRACE method | ||
| // https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html | ||
| if (method !== 'trace' && addOptions.data) { | ||
| let data = getData(); | ||
| const baseOpts = Object.assign({}, opts, { | ||
| form: data.form, | ||
| json: data.json && !addOptions.nojson | ||
| }); | ||
| // If no method was set using data, defaults to post | ||
| if (!addOptions.method) { | ||
| baseOpts.method = 'post'; | ||
| } | ||
| // Sets data and content-type for request | ||
| opts = Object.assign({}, baseOpts, { | ||
| body: data.body, | ||
| headers: Object.assign({ | ||
| 'content-type': data.type | ||
| }, baseOpts.headers) | ||
| }); | ||
| } | ||
| return opts; | ||
| } | ||
| const reqs = (addOptions.method || 'get') | ||
| .split(',') | ||
| .filter(Boolean) | ||
| .map(name => getOpts(name)); | ||
| Promise.all( | ||
| reqs.map(opts => got(url, opts)) | ||
| ) | ||
| .then(results => { | ||
| reqs.forEach((opts, index) => saveCmd({ | ||
| mockFolderName, | ||
| url: url, | ||
| stdin: results[index].body, | ||
| saveOptions: { | ||
| hashAlgorithm: addOptions.hashAlgorithm, | ||
| hashHeaders: addOptions.hashHeaders, | ||
| hashCookies: addOptions.hashCookies, | ||
| data: opts.body, | ||
| headers: opts.headers, | ||
| method: opts.method, | ||
| nohash: addOptions.nohash, | ||
| nojson: addOptions.nojson | ||
| } | ||
| })); | ||
| }) | ||
| .catch(out.error); | ||
| } | ||
| module.exports = addCmd; | ||
| 'use strict'; | ||
| function helpCmd() { | ||
| console.log(` | ||
| Usage: | ||
| snapstub [command] | ||
| Available commands: | ||
| help Output usage info | ||
| version Get current version number | ||
| save Saves data from stdin straight to a url location | ||
| start Starts the built-in static mock server | ||
| add <url> Takes a snapshot of a given url and stores in the local fs | ||
| Options: | ||
| --data Request payload body to be send along with add|save command | ||
| --method Specifies http method to use along with add|save command | ||
| --header Adds a custom header to the request for add|save command | ||
| --nohash Don't generate hashed-filenames on add|save command | ||
| --nojson Allow for saving html|text only on add|save command | ||
| --hashAlgorithm Algorithm to be used for deterministic responses | ||
| --hashHeaders Comma-separated list of header keys to be used on hash | ||
| --hashCookies Comma-separated list of cookies keys to be used on hash | ||
| --verbose Output debug info when used along with the start command | ||
| --silent Mutes output when used along with the start command | ||
| More info: | ||
| https://github.com/ruyadorno/snapstub | ||
| `); | ||
| } | ||
| module.exports = helpCmd; | ||
| const fs = require('fs'); | ||
| const path = require('path'); | ||
| const {parse: urlParse} = require('url'); | ||
| const mkdirp = require('mkdirp'); | ||
| const out = require('simple-output'); | ||
| const requestHash = require('request-hash'); | ||
| function saveCmd(opts) { | ||
| const {saveOptions, url} = opts; | ||
| const stdin = typeof opts.stdin === 'string' ? opts.stdin : JSON.stringify(opts.stdin, undefined, 2); | ||
| const getOpts = arr => (arr && arr.split(',').filter(Boolean).map(i => i.trim())) || []; | ||
| const {query, pathname} = urlParse(url || '', true); | ||
| const {data, headers, hashAlgorithm, hashHeaders, hashCookies, method, nohash, nojson} = saveOptions; | ||
| const hashHeadersOpts = getOpts(hashHeaders); | ||
| const hashCookiesOpts = getOpts(hashCookies); | ||
| const shouldHash = data || | ||
| Object.keys(query).length > 0 || | ||
| (headers && Object.keys(headers).length > 0 && ( | ||
| (hashHeadersOpts && hashHeadersOpts.length > 0) || | ||
| (hashCookiesOpts && hashCookiesOpts.length > 0) | ||
| )); | ||
| const methods = (method || 'get').split(','); | ||
| const mockFolderName = opts.mockFolderName || '__mocks__'; | ||
| const rootPath = opts.rootPath || path.join(process.cwd(), mockFolderName); | ||
| const folderPath = path.join(rootPath, pathname); | ||
| const fileExt = nojson ? '.js' : '.json'; | ||
| methods.forEach(method => { | ||
| const hashSuffix = !nohash && shouldHash ? | ||
| '-' + requestHash({ | ||
| algorithm: hashAlgorithm, | ||
| headers: hashHeadersOpts, | ||
| cookies: hashCookiesOpts | ||
| })({ | ||
| method, | ||
| url, | ||
| body: data, | ||
| headers | ||
| }) : | ||
| ''; | ||
| const fileName = path.join( | ||
| folderPath, | ||
| method.toLowerCase().trim() + hashSuffix + fileExt | ||
| ); | ||
| const fileContent = nojson ? | ||
| `module.exports = (req, res) => { res.send('${stdin}'); };` : | ||
| stdin; | ||
| // Creates mocks folder | ||
| mkdirp.sync(folderPath); | ||
| // Writes mock file | ||
| fs.writeFileSync(fileName, fileContent); | ||
| out.success(`Successfully added: ${fileName}`); | ||
| }); | ||
| } | ||
| module.exports = saveCmd; | ||
| 'use strict'; | ||
| const out = require('simple-output'); | ||
| const globby = require('globby'); | ||
| const methods = require('methods'); | ||
| const hashLoader = require('stubborn-server-hash-loader'); | ||
| function startCmd(opts) { | ||
| const mockFolderName = opts.mockFolderName || '__mocks__'; | ||
| const port = opts.port || 8059; | ||
| try { | ||
| opts.stubborn.start({ | ||
| logMode: opts.verbose ? 'all' : 'warn', | ||
| namespace: '', | ||
| pathToMocks: mockFolderName, | ||
| servePort: port, | ||
| fallbacks: [], | ||
| plugins: [ | ||
| { | ||
| loader: hashLoader({ | ||
| algorithm: opts.hashAlgorithm, | ||
| headers: opts.hashHeaders || [], | ||
| cookies: opts.hashCookies || [] | ||
| }) | ||
| } | ||
| ] | ||
| }); | ||
| if (!opts.silent) { | ||
| out.success('Successfully launched snapstub server on: ' + | ||
| 'http://localhost:' + port); | ||
| printRoutes(mockFolderName, port); | ||
| } | ||
| } catch (e) { | ||
| out.error('Failed to launch snapstub server'); | ||
| } | ||
| } | ||
| function printRoutes(srcPath, port) { | ||
| const patterns = methods.map(m => `**/${m}.+(json|js)`); | ||
| globby(patterns, {cwd: srcPath}) | ||
| .then(paths => { | ||
| const routes = paths.map(p => { | ||
| // Remove filename from path | ||
| const directoryPath = p.substring(0, p.lastIndexOf('/')); | ||
| return `http://localhost:${port}/${directoryPath}`; | ||
| }); | ||
| for (let route of new Set(routes)) { | ||
| out.info(route); | ||
| } | ||
| }); | ||
| } | ||
| module.exports = startCmd; |
| 'use strict'; | ||
| function versionCmd() { | ||
| console.log(require('../package.json').version); | ||
| } | ||
| module.exports = versionCmd; | ||
+5
-3
| { | ||
| "name": "snapstub", | ||
| "version": "5.0.0", | ||
| "version": "5.0.1", | ||
| "description": "Snapshot-based stub/mocking of APIs", | ||
| "main": "index.js", | ||
| "bin": "cli.js", | ||
| "bin": { | ||
| "snapstub": "cli.js" | ||
| }, | ||
| "engines": { | ||
@@ -60,3 +62,3 @@ "node": ">=10" | ||
| "methods": "^1.1.2", | ||
| "minimist": "^1.2.0", | ||
| "minimist": "^1.2.5", | ||
| "mkdirp": "^0.5.1", | ||
@@ -63,0 +65,0 @@ "parse-headers": "^2.0.1", |
-112
| 'use strict'; | ||
| const fs = require('fs'); | ||
| const got = require('got'); | ||
| const out = require('simple-output'); | ||
| const parseHeaders = require('parse-headers'); | ||
| const {parse} = require('query-string'); | ||
| const saveCmd = require('./save'); | ||
| function addCmd({addOptions, mockFolderName, url}) { | ||
| const customHeaders = [].concat(addOptions.header).join('\n'); | ||
| function getData() { | ||
| let data = addOptions.data; | ||
| let body; | ||
| let type; | ||
| let form = false; | ||
| let json = false; | ||
| try { | ||
| data = fs.readFileSync(data).toString(); | ||
| } catch (e) { | ||
| } | ||
| try { | ||
| body = JSON.parse(data); | ||
| type = 'application/json'; | ||
| json = true; | ||
| } catch (e) { | ||
| try { | ||
| body = parse(data); | ||
| type = 'application/x-www-form-urlencoded'; | ||
| form = true; | ||
| } catch (e) { | ||
| body = data; | ||
| type = 'text/plain'; | ||
| } | ||
| } | ||
| return { | ||
| body, | ||
| form, | ||
| json, | ||
| type | ||
| }; | ||
| } | ||
| function getOpts(method) { | ||
| let opts = { | ||
| headers: parseHeaders(customHeaders), | ||
| json: !addOptions.nojson, | ||
| method: method.trim().toLowerCase() | ||
| }; | ||
| // Set body data, skips only TRACE method | ||
| // https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html | ||
| if (method !== 'trace' && addOptions.data) { | ||
| let data = getData(); | ||
| const baseOpts = Object.assign({}, opts, { | ||
| form: data.form, | ||
| json: data.json && !addOptions.nojson | ||
| }); | ||
| // If no method was set using data, defaults to post | ||
| if (!addOptions.method) { | ||
| baseOpts.method = 'post'; | ||
| } | ||
| // Sets data and content-type for request | ||
| opts = Object.assign({}, baseOpts, { | ||
| body: data.body, | ||
| headers: Object.assign({ | ||
| 'content-type': data.type | ||
| }, baseOpts.headers) | ||
| }); | ||
| } | ||
| return opts; | ||
| } | ||
| const reqs = (addOptions.method || 'get') | ||
| .split(',') | ||
| .filter(Boolean) | ||
| .map(name => getOpts(name)); | ||
| Promise.all( | ||
| reqs.map(opts => got(url, opts)) | ||
| ) | ||
| .then(results => { | ||
| reqs.forEach((opts, index) => saveCmd({ | ||
| mockFolderName, | ||
| url: url, | ||
| stdin: results[index].body, | ||
| saveOptions: { | ||
| hashAlgorithm: addOptions.hashAlgorithm, | ||
| hashHeaders: addOptions.hashHeaders, | ||
| hashCookies: addOptions.hashCookies, | ||
| data: opts.body, | ||
| headers: opts.headers, | ||
| method: opts.method, | ||
| nohash: addOptions.nohash, | ||
| nojson: addOptions.nojson | ||
| } | ||
| })); | ||
| }) | ||
| .catch(out.error); | ||
| } | ||
| module.exports = addCmd; | ||
| 'use strict'; | ||
| function helpCmd() { | ||
| console.log(` | ||
| Usage: | ||
| snapstub [command] | ||
| Available commands: | ||
| help Output usage info | ||
| version Get current version number | ||
| save Saves data from stdin straight to a url location | ||
| start Starts the built-in static mock server | ||
| add <url> Takes a snapshot of a given url and stores in the local fs | ||
| Options: | ||
| --data Request payload body to be send along with add|save command | ||
| --method Specifies http method to use along with add|save command | ||
| --header Adds a custom header to the request for add|save command | ||
| --nohash Don't generate hashed-filenames on add|save command | ||
| --nojson Allow for saving html|text only on add|save command | ||
| --hashAlgorithm Algorithm to be used for deterministic responses | ||
| --hashHeaders Comma-separated list of header keys to be used on hash | ||
| --hashCookies Comma-separated list of cookies keys to be used on hash | ||
| --verbose Output debug info when used along with the start command | ||
| --silent Mutes output when used along with the start command | ||
| More info: | ||
| https://github.com/ruyadorno/snapstub | ||
| `); | ||
| } | ||
| module.exports = helpCmd; | ||
| const fs = require('fs'); | ||
| const path = require('path'); | ||
| const {parse: urlParse} = require('url'); | ||
| const mkdirp = require('mkdirp'); | ||
| const out = require('simple-output'); | ||
| const requestHash = require('request-hash'); | ||
| function saveCmd(opts) { | ||
| const {saveOptions, url} = opts; | ||
| const stdin = typeof opts.stdin === 'string' ? opts.stdin : JSON.stringify(opts.stdin, undefined, 2); | ||
| const getOpts = arr => (arr && arr.split(',').filter(Boolean).map(i => i.trim())) || []; | ||
| const {query, pathname} = urlParse(url || '', true); | ||
| const {data, headers, hashAlgorithm, hashHeaders, hashCookies, method, nohash, nojson} = saveOptions; | ||
| const hashHeadersOpts = getOpts(hashHeaders); | ||
| const hashCookiesOpts = getOpts(hashCookies); | ||
| const shouldHash = data || | ||
| Object.keys(query).length > 0 || | ||
| (headers && Object.keys(headers).length > 0 && ( | ||
| (hashHeadersOpts && hashHeadersOpts.length > 0) || | ||
| (hashCookiesOpts && hashCookiesOpts.length > 0) | ||
| )); | ||
| const methods = (method || 'get').split(','); | ||
| const mockFolderName = opts.mockFolderName || '__mocks__'; | ||
| const rootPath = opts.rootPath || path.join(process.cwd(), mockFolderName); | ||
| const folderPath = path.join(rootPath, pathname); | ||
| const fileExt = nojson ? '.js' : '.json'; | ||
| methods.forEach(method => { | ||
| const hashSuffix = !nohash && shouldHash ? | ||
| '-' + requestHash({ | ||
| algorithm: hashAlgorithm, | ||
| headers: hashHeadersOpts, | ||
| cookies: hashCookiesOpts | ||
| })({ | ||
| method, | ||
| url, | ||
| body: data, | ||
| headers | ||
| }) : | ||
| ''; | ||
| const fileName = path.join( | ||
| folderPath, | ||
| method.toLowerCase().trim() + hashSuffix + fileExt | ||
| ); | ||
| const fileContent = nojson ? | ||
| `module.exports = (req, res) => { res.send('${stdin}'); };` : | ||
| stdin; | ||
| // Creates mocks folder | ||
| mkdirp.sync(folderPath); | ||
| // Writes mock file | ||
| fs.writeFileSync(fileName, fileContent); | ||
| out.success(`Successfully added: ${fileName}`); | ||
| }); | ||
| } | ||
| module.exports = saveCmd; | ||
| 'use strict'; | ||
| const out = require('simple-output'); | ||
| const globby = require('globby'); | ||
| const methods = require('methods'); | ||
| const hashLoader = require('stubborn-server-hash-loader'); | ||
| function startCmd(opts) { | ||
| const mockFolderName = opts.mockFolderName || '__mocks__'; | ||
| const port = opts.port || 8059; | ||
| try { | ||
| opts.stubborn.start({ | ||
| logMode: opts.verbose ? 'all' : 'warn', | ||
| namespace: '', | ||
| pathToMocks: mockFolderName, | ||
| servePort: port, | ||
| fallbacks: [], | ||
| plugins: [ | ||
| { | ||
| loader: hashLoader({ | ||
| algorithm: opts.hashAlgorithm, | ||
| headers: opts.hashHeaders || [], | ||
| cookies: opts.hashCookies || [] | ||
| }) | ||
| } | ||
| ] | ||
| }); | ||
| if (!opts.silent) { | ||
| out.success('Successfully launched snapstub server on: ' + | ||
| 'http://localhost:' + port); | ||
| printRoutes(mockFolderName, port); | ||
| } | ||
| } catch (e) { | ||
| out.error('Failed to launch snapstub server'); | ||
| } | ||
| } | ||
| function printRoutes(srcPath, port) { | ||
| const patterns = methods.map(m => `**/${m}.+(json|js)`); | ||
| globby(patterns, {cwd: srcPath}) | ||
| .then(paths => { | ||
| const routes = paths.map(p => { | ||
| // Remove filename from path | ||
| const directoryPath = p.substring(0, p.lastIndexOf('/')); | ||
| return `http://localhost:${port}/${directoryPath}`; | ||
| }); | ||
| for (let route of new Set(routes)) { | ||
| out.info(route); | ||
| } | ||
| }); | ||
| } | ||
| module.exports = startCmd; |
| 'use strict'; | ||
| function versionCmd() { | ||
| console.log(require('../package.json').version); | ||
| } | ||
| module.exports = versionCmd; | ||
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 2 instances in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 2 instances in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
19125
0.12%1
Infinity%Updated