Skip to content

Commit da27c29

Browse files
committed
fix: v0.1.0
1 parent a0864c8 commit da27c29

File tree

10 files changed

+948
-37
lines changed

10 files changed

+948
-37
lines changed

.eslintrc.js

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
module.exports = {
2+
parser: "@typescript-eslint/parser",
3+
plugins: ["@typescript-eslint"],
4+
extends: ["plugin:@typescript-eslint/recommended"],
5+
parserOptions: {
6+
ecmaFeatures: { jsx: true },
7+
ecmaVersion: 2018,
8+
sourceType: "module",
9+
},
10+
rules: {
11+
"max-len": [
12+
"error",
13+
86,
14+
4,
15+
{
16+
ignoreComments: true,
17+
ignoreUrls: true,
18+
ignoreStrings: true,
19+
ignoreTemplateLiterals: true,
20+
ignoreRegExpLiterals: true,
21+
},
22+
],
23+
"@typescript-eslint/ban-ts-comment": "off",
24+
"@typescript-eslint/no-empty-function": "off",
25+
"@typescript-eslint/no-explicit-any": "off",
26+
"padding-line-between-statements": [
27+
"error",
28+
{ blankLine: "always", prev: "*", next: "return" },
29+
{ blankLine: "always", prev: "*", next: "function" },
30+
],
31+
},
32+
};

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
node_modules
2-
dist
2+
dist
3+
tsconfig.tsbuildinfo

README.MD

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,32 @@
11
## @lfg/query-coder
2-
URL query coder/decoder with configurable user pattern. It provides the most comfortable experience for encoding and decoding complex nested filter objects. [Check out example for more info](#example).
32

3+
URL query coder/decoder with configurable user pattern. It provides the most comfortable experience for encoding and decoding complex nested filter objects. [Check out example for more info](#example).
44

55
### Installation
6+
67
```zsh
78
yarn add @lfg/query-coder
89
```
910

1011
### Basic Example
12+
1113
Imaging having a such interface for your querying filters:
14+
1215
```ts
1316
type SearchGroupsFilter = {
14-
gameId?: 'WorldOfWarcraft' | 'WildRift' | 'LostArk';
17+
gameId?: "WorldOfWarcraft" | "WildRift" | "LostArk";
1518
gameMode?: GameMode;
1619
from?: Date;
1720
language?: Language;
18-
21+
1922
lostArk?: LostArkFilterInput;
2023
wildRift?: WildRiftFilterInput;
2124
wow?: WowFilterInput;
2225
};
2326
```
2427

2528
Let's take 1 example of this:
29+
2630
```ts
2731
const filters: SearchGroupsFilter = {
2832
gameId: "WorldOfWarcraft",
@@ -35,22 +39,24 @@ const filters: SearchGroupsFilter = {
3539
region: WowRegion.Europe,
3640
},
3741
};
38-
3942
```
40-
Which should result in `const query = "game=wow&mode=mplus&language=en&faction=alliance&dungeon=mots&rating=1400&region=eu"`.
41-
So we want to:
42-
* flatten nested filter object in a query string
43-
* rename certain keys (`gameMode` -> `mode`)
44-
* re-map certain values (`WorldOfWarcraft` -> `wow`)
43+
44+
Which should result in `const query = "game=wow&mode=mplus&language=en&faction=alliance&dungeon=mots&rating=1400&region=eu"`.
45+
So we want to:
46+
47+
- flatten nested filter object in a query string
48+
- rename certain keys (`gameMode` -> `mode`)
49+
- re-map certain values (`WorldOfWarcraft` -> `wow`)
4550

4651
With this lib, you can init `QueryCoder` and that's it! 🎉
52+
4753
```ts
48-
import { QueryCoder, QueryHandler } from '@lfg/query-coder';
54+
import { QueryCoder, QueryHandler } from "@lfg/query-coder";
4955

5056
// passing generic interface checks, whether node keys
5157
// are the same as in provided interface
5258
const query = new QueryCoder<SearchGroupsFilter>({
53-
gameId: new QueryHandler({
59+
gameId: new QueryHandler({
5460
query: "game",
5561
aliases: {
5662
WorldOfWarcraft: "wow",
@@ -61,7 +67,7 @@ const query = new QueryCoder<SearchGroupsFilter>({
6167
gameMode: new QueryHandler({ query: "mode" }),
6268
language: new QueryHandler({ query: "language" }),
6369
wow: {
64-
faction: new QueryHandler({
70+
faction: new QueryHandler({
6571
query: "faction",
6672
/**
6773
* decodeCondition is Partial of generic
@@ -82,7 +88,7 @@ const query = new QueryCoder<SearchGroupsFilter>({
8288
* You should provide a primitive type, which is different from string
8389
* Otherwise, url query "foo=313" can result in an unexpected result
8490
*/
85-
type: Type.Number,
91+
decodeType: Type.Number,
8692
}),
8793
region: new QueryHandler({
8894
query: "region",

package.json

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,20 @@
11
{
22
"name": "@lfg/query-coder",
3-
"version": "1.0.0",
3+
"version": "0.0.1",
44
"description": "URL query coder-decoder",
55
"main": "dist/index.js",
66
"author": "Ivan Dorofeyev",
77
"license": "MIT",
8+
"scripts": {
9+
"build": "tsc --build",
10+
"prepublish": "build"
11+
},
812
"devDependencies": {
9-
"typescript": "^4.6.2"
13+
"typescript": "^4.6.2",
14+
"prettier": "^2.6.0",
15+
"eslint": "^8.11.0",
16+
"@types/node": "^12.0.0",
17+
"@typescript-eslint/eslint-plugin": "^5.15.0",
18+
"@typescript-eslint/parser": "^5.15.0"
1019
}
1120
}

src/helpers.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
export function deepAssign<T extends Record<any, any>>(
2+
obj: T,
3+
path: string,
4+
value: any
5+
): Record<any, any> {
6+
const pathArray = path.split(".") as Array<keyof T>;
7+
let copyObj = obj;
8+
9+
while (pathArray.length - 1) {
10+
const accessibleNode = pathArray.shift();
11+
if (!accessibleNode) {
12+
return copyObj;
13+
}
14+
15+
if (!(accessibleNode in copyObj)) {
16+
copyObj[accessibleNode] = {} as any;
17+
}
18+
copyObj = copyObj[accessibleNode];
19+
}
20+
21+
copyObj[pathArray[0]] = value;
22+
23+
return copyObj;
24+
}

src/index.ts

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
import { QueryHandler } from "./queryHandler";
2+
import { deepAssign } from "./helpers";
23
import {
34
DeepQueryCoder,
45
DeepQueryCoderWithParent,
56
QueryHandlerMap,
7+
Type,
68
} from "./types";
79

10+
export { QueryHandler, Type };
11+
812
/**
913
* QueryCoders
1014
*/
@@ -42,7 +46,7 @@ export class QueryCoder<T> {
4246
acc[value.query] = [];
4347
}
4448
value.setPath([...path, keyStr]); // filter.wow.dungeon
45-
acc[value.query].push(value);
49+
acc[value.query].push(value as any);
4650

4751
return acc;
4852
}
@@ -70,6 +74,7 @@ export class QueryCoder<T> {
7074
): Record<string, string> {
7175
return Object.keys(data).reduce((acc, keyStr) => {
7276
const key = keyStr as keyof D;
77+
7378
const value = data[key] as unknown;
7479
const shallowEncoder = encoder[key] as DeepQueryCoder<typeof value>;
7580

@@ -97,7 +102,6 @@ export class QueryCoder<T> {
97102
*/
98103
encode(data: T): string {
99104
const queryMap = this.deepEncode(data, this.queryEncoder);
100-
console.log(queryMap);
101105
const urlQuery = new URLSearchParams(queryMap);
102106

103107
return urlQuery.toString();
@@ -109,22 +113,22 @@ export class QueryCoder<T> {
109113
* @returns decoded object
110114
*/
111115
decode(query: string): T {
112-
"game=Wow&gameMode=WowMythicPlus&language=En&faction=Alliance&dungeon=MistsOfTirnaScithe&rating=1400&region=Europe";
113116
const searchParams = new URLSearchParams(query);
114-
const params = Object.fromEntries([...(searchParams as any)]);
115117

116-
Object.keys(params).map((key) => {
117-
const value = params[key];
118+
const object = {};
119+
120+
for (const [key, value] of searchParams) {
118121
const handlers = this.queryHandlers[key];
119122
if (!handlers) {
120-
return;
123+
continue;
121124
}
125+
122126
if (handlers.length === 1) {
123127
const handler = handlers[0];
124128
const parsedValue = handler.decode(value);
129+
deepAssign(object, handler.path, parsedValue);
125130
}
126-
});
127-
128-
return {} as T;
131+
}
132+
return object as T;
129133
}
130134
}

src/queryHandler.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -52,12 +52,15 @@ export class QueryHandler<T, DC = undefined> {
5252
constructor(data: QueryHandlerParams<T, DC>) {
5353
this.query = data.query;
5454
this.decodeCondition = data.decodeCondition;
55+
if (data.decodeType) {
56+
this.type = data.decodeType;
57+
}
5558

5659
const strData = data as QueryHandlerTypedParams<string | number | symbol>;
57-
if (strData?.convertMap) {
58-
const convertMap = strData.convertMap as any;
59-
this.aliases = convertMap;
60-
this.reverseAliases = this.reverseMap(convertMap);
60+
if (strData?.aliases) {
61+
const aliases = strData.aliases as any;
62+
this.aliases = aliases;
63+
this.reverseAliases = this.reverseMap(aliases);
6164
}
6265
}
6366

@@ -96,7 +99,7 @@ export class QueryHandler<T, DC = undefined> {
9699
}
97100

98101
public setPath(path: string[]) {
99-
return path.join(".");
102+
this.path = path.join(".");
100103
}
101104

102105
private isPrimitive(data: any): data is string | number | symbol {

src/types.d.ts renamed to src/types.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@ export type QueryHandlerParams<T, P = undefined> = {
66
decodeType?: Type;
77
} & QueryHandlerTypedParams<T>;
88

9-
type QueryHandlerTypedParams<T> = T extends number | string | symbol
9+
export type QueryHandlerTypedParams<T> = T extends number | string | symbol
1010
? {
11-
convertMap?: Record<T, string>;
11+
aliases?: Record<T, string>;
1212
}
1313
: void;
1414

tsconfig.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,20 @@
33
"compilerOptions": {
44
"outDir": "dist",
55
"rootDir": "src",
6-
"baseUrl": "./src",
76
"target": "ES5",
8-
"lib": ["DOM", "DOM.Iterable", "ESNext"],
7+
"downlevelIteration": true,
8+
"lib": ["ESNext"],
99
"skipLibCheck": true,
1010
"esModuleInterop": true,
1111
"allowSyntheticDefaultImports": true,
1212
"strict": true,
1313
"forceConsistentCasingInFileNames": true,
1414
"noFallthroughCasesInSwitch": true,
15-
"module": "ESNext",
15+
"module": "CommonJS",
1616
"moduleResolution": "node",
1717
"resolveJsonModule": true,
18+
"declaration": true,
1819
"isolatedModules": true,
19-
"noEmit": true,
2020
"allowJs": false,
2121
"incremental": true
2222
},

0 commit comments

Comments
 (0)