nested-query-params
Advanced tools
Comparing version
@@ -14,28 +14,6 @@ /** | ||
*/ | ||
declare type MaybeArray<T> = T | Array<MaybeArray<T>>; | ||
export declare type SearchParamKey = string; | ||
export declare type SearchParamVal = MaybeArray<SearchParams | string>; | ||
export declare type SearchParam = [SearchParamKey, SearchParamVal]; | ||
export declare type SearchParams = { | ||
[key: SearchParamKey]: SearchParamVal; | ||
}; | ||
export declare enum SearchParamPathComponentKind { | ||
Map = 0, | ||
List = 1 | ||
} | ||
export declare type SearchParamPathComponent = [SearchParamPathComponentKind.Map, string] | [SearchParamPathComponentKind.List]; | ||
/** | ||
* Expands a search params into structural types. Supported types are Arrays, | ||
* Objects and basic value types. Heavily inspired by | ||
* {@link https://github.com/rack/rack/blob/bad8fe37c8867596855dcd0b3fe3030acc6b8621/lib/rack/query_parser.rb#L63-L68|Rack's nested query parser}. | ||
* | ||
* @example | ||
* // returns { one: { two: "3" } }; | ||
* parseQuery("?one[two]=3") | ||
*/ | ||
export declare function parseQuery(query: string): SearchParams; | ||
/** | ||
* Parses nested search parameters keys into their individual path components. | ||
*/ | ||
export declare function parseQueryParamKey(key: SearchParamKey): SearchParamPathComponent[]; | ||
export {}; | ||
export * from "./parse"; | ||
export * from "./print"; | ||
export * from "./types"; | ||
export { printQuery as print } from "./print"; | ||
export { parseQuery as parse } from "./parse"; |
162
lib/main.js
@@ -15,143 +15,25 @@ "use strict"; | ||
*/ | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
o[k2] = m[k]; | ||
})); | ||
var __exportStar = (this && this.__exportStar) || function(m, exports) { | ||
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p); | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.parseQueryParamKey = exports.parseQuery = exports.SearchParamPathComponentKind = void 0; | ||
const util_1 = require("./util"); | ||
var SearchParamPathComponentKind; | ||
(function (SearchParamPathComponentKind) { | ||
SearchParamPathComponentKind[SearchParamPathComponentKind["Map"] = 0] = "Map"; | ||
SearchParamPathComponentKind[SearchParamPathComponentKind["List"] = 1] = "List"; | ||
})(SearchParamPathComponentKind = exports.SearchParamPathComponentKind || (exports.SearchParamPathComponentKind = {})); | ||
const logger = prepareConsole(console, "nested-query-params"); | ||
/** | ||
* Expands a search params into structural types. Supported types are Arrays, | ||
* Objects and basic value types. Heavily inspired by | ||
* {@link https://github.com/rack/rack/blob/bad8fe37c8867596855dcd0b3fe3030acc6b8621/lib/rack/query_parser.rb#L63-L68|Rack's nested query parser}. | ||
* | ||
* @example | ||
* // returns { one: { two: "3" } }; | ||
* parseQuery("?one[two]=3") | ||
*/ | ||
function parseQuery(query) { | ||
const pairs = (0, util_1.splitQuery)(query); | ||
return pairs.reduce((acc, [key, value]) => { | ||
const comps = parseQueryParamKey(key); | ||
if (Array.isArray(value)) { | ||
return value.reduce((acc, value) => applyNestedParam(acc, comps, value), acc); | ||
} | ||
return applyNestedParam(acc, comps, value); | ||
}, {}); | ||
} | ||
exports.parseQuery = parseQuery; | ||
/** | ||
* Parses nested search parameters keys into their individual path components. | ||
*/ | ||
function parseQueryParamKey(key) { | ||
if (key.trim() === "") { | ||
return []; | ||
} | ||
let start; | ||
if ((start = key.indexOf("[", 1)) === -1) { | ||
return [[SearchParamPathComponentKind.Map, key]]; | ||
} | ||
const head = key.slice(0, start); | ||
const rest = key.slice(start); | ||
return [ | ||
[SearchParamPathComponentKind.Map, head], | ||
...parseQueryParamKeyComponents(rest), | ||
]; | ||
} | ||
exports.parseQueryParamKey = parseQueryParamKey; | ||
/** | ||
* Applies given key value pair to search parameters. | ||
*/ | ||
function applyNestedParam(params, comps, value) { | ||
const [head] = comps; | ||
if (!head) { | ||
return params; | ||
} | ||
if (head[0] !== SearchParamPathComponentKind.Map) { | ||
logger.warn("#applyNestedParam", "cannot apply list op to root"); | ||
return params; | ||
} | ||
return applyComponent(params, comps, value); | ||
} | ||
function applyComponent(params, comps, value) { | ||
const log = prepareConsole(logger, "#applyComponent"); | ||
const [head, ...rest] = comps; | ||
if (!head) { | ||
return params; | ||
} | ||
if (head[0] === SearchParamPathComponentKind.Map) { | ||
if (typeof params !== "object" || Array.isArray(params)) { | ||
params = {}; | ||
} | ||
if (rest.length === 0) { | ||
return { | ||
...params, | ||
[head[1]]: Array.isArray(value) ? value[value.length - 1] : value, | ||
}; | ||
} | ||
const key = head[1]; | ||
return { | ||
...params, | ||
[head[1]]: applyComponent(params[key], rest, value), | ||
}; | ||
} | ||
if (head[0] === SearchParamPathComponentKind.List) { | ||
if (!Array.isArray(params)) { | ||
params = []; | ||
} | ||
if (rest.length === 0) { | ||
return [ | ||
...params, | ||
...(Array.isArray(value) ? value : [value]), | ||
]; | ||
} | ||
const [peek] = rest; | ||
if (peek[0] !== SearchParamPathComponentKind.Map) { | ||
log.warn("expected map"); | ||
return params; | ||
} | ||
const last = params[params.length - 1] || undefined; | ||
if (typeof last === "object" && !Array.isArray(last) && !last[peek[1]]) { | ||
const next = applyNestedParam(last, rest, value); | ||
return [...params.slice(0, -1), next]; | ||
} | ||
else if (Array.isArray(last)) { | ||
log.warn("nested arrays are not supported"); | ||
} | ||
else if (last && typeof last !== "object") { | ||
log.warn("expected map"); | ||
} | ||
// add value to list if list was previously empty or the given key is | ||
// already in the map. | ||
const next = applyNestedParam({}, rest, value); | ||
return [...params, next]; | ||
} | ||
return params; | ||
} | ||
function parseQueryParamKeyComponents(key) { | ||
// list | ||
if (key.startsWith("[]")) { | ||
return [ | ||
[SearchParamPathComponentKind.List], | ||
...parseQueryParamKeyComponents(key.slice(2)), | ||
]; | ||
} | ||
// map | ||
let start; | ||
if (key.startsWith("[") && (start = key.indexOf("]", 1)) !== -1) { | ||
return [ | ||
[SearchParamPathComponentKind.Map, key.slice(1, start)], | ||
...parseQueryParamKeyComponents(key.slice(start + 1)), | ||
]; | ||
} | ||
return []; | ||
} | ||
function prepareConsole(parent, ...init) { | ||
return ["info", "error", "warn", "debug"].reduce((acc, method) => ({ | ||
[method]: (...msgs) => parent[method](...init, ...msgs), | ||
...acc, | ||
}), {}); | ||
} | ||
exports.parse = exports.print = void 0; | ||
__exportStar(require("./parse"), exports); | ||
__exportStar(require("./print"), exports); | ||
__exportStar(require("./types"), exports); | ||
var print_1 = require("./print"); | ||
Object.defineProperty(exports, "print", { enumerable: true, get: function () { return print_1.printQuery; } }); | ||
var parse_1 = require("./parse"); | ||
Object.defineProperty(exports, "parse", { enumerable: true, get: function () { return parse_1.parseQuery; } }); | ||
//# sourceMappingURL=main.js.map |
{ | ||
"name": "nested-query-params", | ||
"version": "1.0.1", | ||
"version": "1.1.1", | ||
"description": "Rack like parsing of nested query parameters", | ||
"scripts": { | ||
"prepublish": "npm run build", | ||
"build": "rm -r lib && npx tsc -p ./", | ||
@@ -7,0 +8,0 @@ "format": "prettier --write .", |
@@ -1,21 +0,7 @@ | ||
<p align="center"> | ||
<img src="./logo.svg" width="120px"/> | ||
</p> | ||
<header align="center"> | ||
<p align="center"><img align="center" src="./logo.svg" width="200px"/></p> | ||
<h3 align="center">Nested Query Params</h3> | ||
<p align="center">A TypeScript implementation of Rack's query string parser.</p> | ||
</header> | ||
<h3 align="center">Nested Query Params</h3> | ||
<p align="center"> | ||
A TypeScript implementation of Rack's query string parser. | ||
</p> | ||
<p align="center"> | ||
<a href="#"> | ||
<img src="https://img.shields.io/github/commit-activity/m/jamesdphillips/nested-query-params.svg?style=flat" /> | ||
</a> | ||
<a href="https://github.com/jamesdphillips/nested-query-params/blob/main/LICENSE"> | ||
<img src="https://img.shields.io/github/license/jamesdphillips/nested-query-params.svg?style=flat" /> | ||
</a> | ||
<a href="https://circleci.com/gh/jamesdphillips/nested-query-params/tree/main"> | ||
<img src="https://circleci.com/gh/jamesdphillips/nested-query-params/tree/main.svg?style=svg" /> | ||
</a> | ||
</p> | ||
## Overview | ||
@@ -38,3 +24,3 @@ | ||
```typescript | ||
import { parseQuery } from "nested-query-params"; | ||
import { parseQuery, printQuery } from "nested-query-params"; | ||
@@ -52,4 +38,12 @@ // maps | ||
console.debug(mixed); // prints { bar: "baz", foo: { bar: ["baz", "42"] } } | ||
const simple = parseQuery("?foo[bar]=baz"); | ||
console.debug(printQuery(simple)); // prints "?foo[bar]=baz" | ||
// print valid | ||
const edited = { ...simple, foo: "bar" }; | ||
console.debug(printQuery(edited)); // prints "?foo=bar" | ||
``` | ||
[query string parser]: https://github.com/rack/rack/blob/bad8fe37c8867596855dcd0b3fe3030acc6b8621/lib/rack/query_parser.rb#L63 | ||
[query string parser]: https://github.com/rack/rack/blob/bad8fe37c8867596855dcd0b3fe3030acc6b8621/lib/rack/query_parser.rb#L63 |
Sorry, the diff of this file is not supported yet
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
22379
32.99%18
100%321
40.17%48
-9.43%1
Infinity%