--- /dev/null
+{
+ "env": {
+ "browser": false,
+ "es6": true,
+ "node": true
+ },
+ "extends": [
+ "eslint:recommended",
+ "plugin:node/recommended",
+ "plugin:security/recommended",
+ "plugin:sonarjs/recommended"
+ ],
+ "parserOptions": {
+ "ecmaVersion": 2018
+ },
+ "plugins": [
+ "node",
+ "security",
+ "sonarjs"
+ ],
+ "rules": {
+ "array-element-newline": [
+ "error",
+ "consistent"
+ ],
+ "arrow-parens": [
+ "error",
+ "always"
+ ],
+ "arrow-spacing": [
+ "error",
+ {
+ "after": true,
+ "before": true
+ }
+ ],
+ "block-scoped-var": "error",
+ "block-spacing": "error",
+ "brace-style": "error",
+ "callback-return": "error",
+ "camelcase": "error",
+ "capitalized-comments": "warn",
+ "class-methods-use-this": "error",
+ "comma-dangle": [
+ "error",
+ "always-multiline"
+ ],
+ "comma-spacing": [
+ "error",
+ {
+ "after": true,
+ "before": false
+ }
+ ],
+ "comma-style": [
+ "error",
+ "last"
+ ],
+ "sonarjs/cognitive-complexity": "warn",
+ "keyword-spacing": "error",
+ "linebreak-style": [
+ "error",
+ "unix"
+ ],
+ "no-unused-vars": [
+ "error", {
+ "varsIgnorePattern": "^_"
+ }
+ ],
+ "object-curly-spacing": [
+ "error",
+ "always"
+ ],
+ "prefer-const": "error",
+ "quotes": [
+ "error",
+ "single"
+ ],
+ "strict": "error",
+ "vars-on-top": "error"
+ }
+}
\ No newline at end of file
--- /dev/null
+.nyc_output
+node_modules
--- /dev/null
+# Another in-house query string parser
+
+This exists due to needing to support parsing a certain legacy format, which the built-in parser cannot quite handle meaningfully.
+
+Primary differences:
+ * recognize both & and ; as parameter separators
+ * for valueless keys, value is set to null, not ''
+ * empty parameters are included as a '' key
+
+These behaviors are admittedly idiosyncratic.
+
--- /dev/null
+'use strict';
+
+const { parse } = require('./lib/parse');
+
+module.exports = {
+ parse: parse,
+};
+
--- /dev/null
+/* eslint-disable security/detect-object-injection */
+'use strict';
+
+const SPACES = /\+/ug;
+const DELIM = /[&;]/u;
+
+/**
+ * Separate a string into decoded key and value.
+ * @param {string} param
+ * @returns {object} .key
+ * .val
+ */
+const splitKeyValue = (param) => {
+ const idx = param.indexOf('=');
+ let key, val;
+ if (idx >= 0) {
+ key = decodeURIComponent(param.slice(0, idx));
+ val = decodeURIComponent(param.slice(idx + 1));
+ } else {
+ key = decodeURIComponent(param);
+ val = null;
+ }
+ return {
+ key,
+ val,
+ };
+};
+
+/**
+ * Parse a raw querystring (as provided by url.parse) into a map of
+ * parameter values.
+ * This is nearly the same as the node-provided querystring.parse,
+ * but is more accepting of legacy conventions and (subjectively) more convenient in output.
+ * Primary differences:
+ * for valueless keys, value is set to null, not ''
+ * ; is a valid parameter separator
+ * empty parameters are included as a '' key
+ *
+ * @param {string} querystring - raw query string
+ * @param {boolean} arraySuffix - allow param[] to explicitly treat param as array, à la PHP
+ * @returns {object} - parsed data
+ */
+function parse(querystring, arraySuffix=false) {
+ const params = {};
+ if (querystring) {
+ const queries = querystring.replace(SPACES, '%20').split(DELIM);
+ queries.forEach((query) => {
+ // eslint-disable-next-line prefer-const
+ let { key, val } = splitKeyValue(query);
+
+ if (arraySuffix
+ && key.endsWith('[]')) {
+ key = key.slice(0, key.length - 2);
+ if (!Object.prototype.hasOwnProperty.call(params, key)) {
+ params[key] = Array();
+ }
+ }
+
+ if (!Object.prototype.hasOwnProperty.call(params, key)) {
+ params[key] = val;
+ } else if (Array.isArray(params[key])) {
+ params[key].push(val);
+ } else {
+ params[key] = Array(params[key], val);
+ }
+ });
+ }
+ return params;
+}
+
+module.exports = {
+ parse,
+};
--- /dev/null
+{
+ "name": "@squeep/querystring",
+ "version": "1.0.0",
+ "description": "An in-house query string parser.",
+ "repository": {
+ "type": "git",
+ "url": "https://git.squeep.com/squeep-querystring/"
+ },
+ "main": "index.js",
+ "scripts": {
+ "test": "mocha",
+ "coverage": "nyc mocha",
+ "eslint": "eslint index.js lib"
+ },
+ "author": "Justin Wind <jwind-npm@squeep.com>",
+ "license": "ISC",
+ "devDependencies": {
+ "eslint": "^7.9.0",
+ "eslint-plugin-node": "^11.1.0",
+ "eslint-plugin-security": "^1.4.0",
+ "eslint-plugin-sonarjs": "^0.5.0",
+ "mocha": "^8.1.3",
+ "nyc": "^15.1.0"
+ }
+}
--- /dev/null
+/* eslint-disable capitalized-comments */
+/* eslint-env mocha */
+'use strict';
+
+
+const assert = require('assert');
+const qs = require('../index.js');
+
+describe('querystring', function () {
+ describe('parse', function () {
+
+ it('should return empty object from no input', function () {
+ assert.deepStrictEqual(
+ qs.parse(undefined),
+ {},
+ );
+ });
+
+ it('should return empty object from empty string', function () {
+ assert.deepStrictEqual(
+ qs.parse(''),
+ {},
+ );
+ });
+
+ it('should return simple mapping', function () {
+ assert.deepStrictEqual(
+ qs.parse('foo=1&bar=2&baz=quux'),
+ {
+ foo: '1',
+ bar: '2',
+ baz: 'quux',
+ },
+ );
+ });
+
+ it('should return array for repeated keys', function () {
+ assert.deepStrictEqual(
+ qs.parse('foo=1&foo=2&foo=quux'),
+ {
+ foo: [ '1', '2', 'quux' ],
+ },
+ );
+ });
+
+ it('should return null values for bare keys', function () {
+ assert.deepStrictEqual(
+ qs.parse('foo&foo&bar&quux'),
+ {
+ foo: [ null, null ],
+ bar: null,
+ quux: null,
+ },
+ );
+ });
+
+ it('should handle space encodings and equals in values', function () {
+ assert.deepStrictEqual(
+ qs.parse('this+key=+spacey+string+;&foo=equally=string&'),
+ {
+ '': [ null, null ],
+ 'this key': ' spacey string ',
+ foo: 'equally=string',
+ },
+ );
+ });
+
+ it('should handle raw spaces and encoded spaces', function () {
+ assert.deepStrictEqual(
+ qs.parse('this+has actual+spaces=but okay%20whatever'),
+ {
+ 'this has actual spaces': 'but okay whatever',
+ },
+ );
+ });
+
+ it('should handle empty values', function () {
+ assert.deepStrictEqual(
+ qs.parse(';;;;;;;+;;;;'),
+ {
+ '': [ null, null, null, null, null, null, null, null, null, null, null ],
+ ' ': null,
+ },
+ );
+ });
+
+ it('should handle PHP style array declarations', function () {
+ assert.deepStrictEqual(
+ qs.parse('foo%5B%5D&bar[]=1&bar[]=2&baz', true),
+ {
+ 'foo': [null],
+ bar: ['1', '2'],
+ baz: null,
+ },
+ )
+ });
+
+ }); // parse
+});
+