-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
- Loading branch information
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
import type { Matcher } from './Matcher.js'; | ||
import type { MatchResult } from './MatchResult.js'; | ||
import type { Validator } from '../validators.js'; | ||
export interface QueryStringMatcherInput { | ||
req: { | ||
url: string; | ||
}; | ||
} | ||
type QueryMatch = Record<string, Validator<string[], any>>; | ||
type QueryResult<T extends QueryMatch> = { | ||
[P in keyof T]: T[P] extends Validator<string[], infer O> ? O : never; | ||
}; | ||
export type QueryStringMatchResult<U extends QueryMatch> = MatchResult<{ | ||
query: QueryResult<U>; | ||
}>; | ||
declare class QueryStringMatcher<U extends QueryMatch, P extends QueryStringMatcherInput> implements Matcher<QueryStringMatchResult<U>, P> { | ||
private readonly listConfig; | ||
constructor(config: U); | ||
match: ({ req }: P) => QueryStringMatchResult<U>; | ||
} | ||
export declare function queryString<U extends QueryMatch, P extends QueryStringMatcherInput>(config: U): QueryStringMatcher<U, P>; | ||
export {}; |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
export interface Validator<I, O> { | ||
(): { | ||
match(value: I): boolean; | ||
parse(value: I): O; | ||
}; | ||
} | ||
export declare const trueVal: Validator<string[], string[]>; | ||
export declare function requiredVal<I>(): Validator<I, NonNullable<NoInfer<I>>>; | ||
export declare function getNthVal<I extends any>(num?: number): Validator<I[], NoInfer<I> | undefined>; | ||
export declare function mapToNumVal<I extends string[]>(): Validator<I, number[]>; | ||
export declare function oneOfVal<const I extends string | number | boolean>(values: I[]): Validator<I, I>; | ||
export declare function atLeastOneVal<const I extends string | number | boolean>(expectedValues: I[]): Validator<I[], I[]>; | ||
export declare function toNumVal<I extends string>(): Validator<I, number>; | ||
export declare function chain<I extends any, O1 extends any, O2 extends any, O3 extends any, O extends any>(val1: Validator<I, O1>, val2: Validator<O1, O2>, val3: Validator<O2, O3>, val4: Validator<O3, O>): Validator<I, O>; | ||
export declare function chain<I extends any, O1 extends any, O2 extends any, O extends any>(val1: Validator<I, O1>, val2: Validator<O1, O2>, val3: Validator<O2, O>): Validator<I, O>; | ||
export declare function chain<I extends any, O1 extends any, O extends any>(val1: Validator<I, O1>, val2: Validator<O1, O>): Validator<I, O>; | ||
export declare function chain<I extends any, O extends any>(val1: Validator<I, O>): Validator<I, O>; |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
import Url from 'urlite' | ||
import type { | ||
Matcher, | ||
} from './Matcher.js' | ||
import type { | ||
MatchResult, | ||
} from './MatchResult.js' | ||
import type { | ||
Validator, | ||
} from '../validators.js' | ||
|
||
export interface QueryStringMatcherInput { | ||
req: { | ||
url: string | ||
} | ||
} | ||
|
||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
type AnyValidator = Validator<string[], any> | ||
|
||
type QueryMatch = Record<string, AnyValidator> | ||
|
||
type QueryResult<T extends QueryMatch> = { | ||
[P in keyof T]: T[P] extends Validator<string[], infer O> ? O : never | ||
} | ||
|
||
export type QueryStringMatchResult<U extends QueryMatch> = MatchResult<{ | ||
query: QueryResult<U> | ||
}> | ||
|
||
class QueryStringMatcher< | ||
U extends QueryMatch, | ||
P extends QueryStringMatcherInput | ||
> implements Matcher<QueryStringMatchResult<U>, P> { | ||
private readonly listConfig: [string, AnyValidator][] | ||
|
||
constructor(config: U) { | ||
this.listConfig = Object.entries(config) | ||
} | ||
|
||
match = ({ req }: P): QueryStringMatchResult<U> => { | ||
// original URL returns '' if search is empty | ||
const search = Url.parse(req.url).search ?? '' | ||
const queryParams = new URLSearchParams(search) | ||
|
||
const queryResult = {} as QueryResult<U> | ||
for (const [key, validatorFactory] of this.listConfig) { | ||
if (queryParams.has(key) === false) { | ||
return { | ||
matched: false, | ||
} | ||
} | ||
const params = queryParams.getAll(key) | ||
const validator = validatorFactory() | ||
if (validator.match(params) === false) { | ||
return { | ||
matched: false, | ||
} | ||
} | ||
// @ts-ignore | ||
queryResult[key] = validator.parse(params) | ||
} | ||
|
||
return { | ||
matched: true, | ||
result: { | ||
query: queryResult, | ||
}, | ||
} | ||
} | ||
} | ||
|
||
export function queryString< | ||
U extends QueryMatch, | ||
P extends QueryStringMatcherInput | ||
>(config: U): QueryStringMatcher<U, P> { | ||
return new QueryStringMatcher(config) | ||
} |