From 66d6a98ac4314a75d8fcca53694f0d6bd98adfd8 Mon Sep 17 00:00:00 2001 From: Kylesoda <249518290+kylesoda@users.noreply.github.com> Date: Mon, 2 Mar 2026 00:13:29 -0500 Subject: [PATCH] feat: enhance configuration handling and add type definitions for query and URL serializers --- .gitignore | 1 + lib/core/config-defaults.ts | 39 ++++++++++------- lib/core/fluent-url.ts | 19 ++++++--- lib/core/serializer/query-serializer.ts | 14 ++++-- lib/core/serializer/url-serializer.ts | 21 +++++++-- lib/shared/types/index.ts | 57 ++++++++++++++++++------- package.json | 1 + 7 files changed, 109 insertions(+), 43 deletions(-) diff --git a/.gitignore b/.gitignore index 2aa8c99..5fdfea3 100644 --- a/.gitignore +++ b/.gitignore @@ -142,3 +142,4 @@ dist vite.config.js.timestamp-* vite.config.ts.timestamp-* .vite/ +*.local diff --git a/lib/core/config-defaults.ts b/lib/core/config-defaults.ts index d78620a..1127f91 100644 --- a/lib/core/config-defaults.ts +++ b/lib/core/config-defaults.ts @@ -4,28 +4,32 @@ import { deepClone } from '@/shared/helpers/deep-clone' import type { FLuentUrlPlainObject, FluentUrlConfig, - QuerySerializer, + NormalizeOptionsArgs, + PlainObjectConstraint, } from '@/shared/types' -export function normalizeConfig( - config?: Partial, -): FluentUrlConfig { +export function normalizeConfig< + PQO extends PlainObjectConstraint, + SQO extends PlainObjectConstraint, +>(config?: Partial>): FluentUrlConfig { return { parseQuery: config?.parseQuery ?? parseQuery, + parseQueryOptions: config?.parseQueryOptions, stringifyQuery: config?.stringifyQuery ?? stringifyQuery, + stringifyQueryOptions: config?.stringifyQueryOptions, } } -export function normalizeOptions(args: { - urlOptions?: Partial | string - parseQuery: QuerySerializer['parseQuery'] -}): FLuentUrlPlainObject { - const { urlOptions: urlOrOptions, parseQuery } = args - +export function normalizeOptions({ + urlOrOptions, + parseQuery, + parseQueryOptions, +}: NormalizeOptionsArgs): FLuentUrlPlainObject { if (typeof urlOrOptions === 'string') { return parseUrl({ str: urlOrOptions, - parseQuery: parseQuery, + parseQuery, + parseQueryOptions, }) } @@ -39,13 +43,18 @@ export function normalizeOptions(args: { } } -export function mergeConfig( - current: FluentUrlConfig, - merge?: Partial, -): FluentUrlConfig { +export function mergeConfig< + PQO extends PlainObjectConstraint, + SQO extends PlainObjectConstraint, +>( + current: FluentUrlConfig, + merge?: Partial>, +): FluentUrlConfig { return { parseQuery: merge?.parseQuery ?? current.parseQuery, + parseQueryOptions: merge?.parseQueryOptions, stringifyQuery: merge?.stringifyQuery ?? current.stringifyQuery, + stringifyQueryOptions: merge?.stringifyQueryOptions, } } diff --git a/lib/core/fluent-url.ts b/lib/core/fluent-url.ts index f0fff34..9856c94 100644 --- a/lib/core/fluent-url.ts +++ b/lib/core/fluent-url.ts @@ -9,11 +9,17 @@ import { deepClone } from '@/shared/helpers/deep-clone' import type { FLuentUrlPlainObject, FluentUrlConfig, + ParseQueryOptions, + PlainObjectConstraint, QueriesPlainObject, + StringifyQueryOptions, } from '@/shared/types' -export class FluentUrl { - private readonly config: FluentUrlConfig +export class FluentUrl< + PQO extends PlainObjectConstraint = ParseQueryOptions, + SQO extends PlainObjectConstraint = StringifyQueryOptions, +> { + private readonly config: FluentUrlConfig private readonly protocol?: string private readonly hostname?: string private readonly paths: string[] @@ -23,14 +29,15 @@ export class FluentUrl { constructor( urlOrOptions?: Partial | string, - config?: Partial, + config?: Partial>, ) { this.config = normalizeConfig(config) const { protocol, hostname, paths, port, fragment, queries } = normalizeOptions({ - urlOptions: urlOrOptions, + urlOrOptions, parseQuery: this.config.parseQuery, + parseQueryOptions: this.config.parseQueryOptions, }) this.protocol = protocol @@ -76,8 +83,8 @@ export class FluentUrl { public clone( options?: Partial, - config?: Partial, - ): FluentUrl { + config?: Partial>, + ): FluentUrl { return new FluentUrl( mergeOptions( { diff --git a/lib/core/serializer/query-serializer.ts b/lib/core/serializer/query-serializer.ts index bc8cbf8..5aee180 100644 --- a/lib/core/serializer/query-serializer.ts +++ b/lib/core/serializer/query-serializer.ts @@ -1,9 +1,17 @@ -import type { QuerySerializer } from '@/shared/types' +import type { + ParseQuery, + PlainObjectConstraint, + StringifyQuery, +} from '@/shared/types' -export const parseQuery: QuerySerializer['parseQuery'] = () => { +export function parseQuery(): ReturnType< + ParseQuery +> { throw new Error('Not implemented') } -export const stringifyQuery: QuerySerializer['stringifyQuery'] = () => { +export function stringifyQuery(): ReturnType< + StringifyQuery +> { throw new Error('Not implemented') } diff --git a/lib/core/serializer/url-serializer.ts b/lib/core/serializer/url-serializer.ts index cab9bdc..130efa7 100644 --- a/lib/core/serializer/url-serializer.ts +++ b/lib/core/serializer/url-serializer.ts @@ -1,4 +1,9 @@ -import type { FLuentUrlPlainObject, UrlSerializer } from '@/shared/types' +import type { + FLuentUrlPlainObject, + ParseUrlArgs, + PlainObjectConstraint, + StringifyUrlArgs, +} from '@/shared/types' const URLTypesObj = { absolute: 'absolute', @@ -11,7 +16,11 @@ type URLTypes = (typeof URLTypesObj)[keyof typeof URLTypesObj] const defaultProtocol = 'https' const defaultHost = 'example.com' -export const parseUrl: UrlSerializer['parseUrl'] = ({ str, parseQuery }) => { +export function parseUrl({ + str, + parseQuery, + parseQueryOptions, +}: ParseUrlArgs): FLuentUrlPlainObject { const urlString = str.trim() const urlType: URLTypes = urlString.startsWith('//') @@ -64,11 +73,15 @@ export const parseUrl: UrlSerializer['parseUrl'] = ({ str, parseQuery }) => { url.hash !== '' ? url.hash.replace('#', '') : undefined urlPlainObject.queries = - url.search !== '' ? parseQuery(url.search) : Object.create(null) + url.search !== '' + ? parseQuery(url.search, parseQueryOptions) + : Object.create(null) return urlPlainObject } -export const stringifyUrl: UrlSerializer['stringifyUrl'] = (_args) => { +export function stringifyUrl( + _args: StringifyUrlArgs, +): string { throw new Error('Not implemented') } diff --git a/lib/shared/types/index.ts b/lib/shared/types/index.ts index fe9488a..766e58b 100644 --- a/lib/shared/types/index.ts +++ b/lib/shared/types/index.ts @@ -9,23 +9,50 @@ export interface FLuentUrlPlainObject { queries: QueriesPlainObject } -export interface QuerySerializer { - parseQuery: (str: string) => QueriesPlainObject - stringifyQuery: (obj: QueriesPlainObject) => string +export type PlainObjectConstraint = Record + +export interface ParseQueryOptions extends PlainObjectConstraint { + [k: string]: any } -export interface UrlSerializer { - parseUrl: (args: { - str: string - parseQuery: QuerySerializer['parseQuery'] - }) => FLuentUrlPlainObject - stringifyUrl: (args: { - obj: FLuentUrlPlainObject - stringifyQuery: QuerySerializer['stringifyQuery'] - }) => string +export type ParseQuery = ( + str: string, + options?: T, +) => QueriesPlainObject + +export interface StringifyQueryOptions extends PlainObjectConstraint { + [k: string]: any } -export interface FluentUrlConfig { - parseQuery: QuerySerializer['parseQuery'] - stringifyQuery: QuerySerializer['stringifyQuery'] +export type StringifyQuery = ( + obj: QueriesPlainObject, + options?: T, +) => string + +export interface ParseUrlArgs { + str: string + parseQuery: ParseQuery + parseQueryOptions?: T +} + +export interface StringifyUrlArgs { + obj: FLuentUrlPlainObject + stringifyQuery: StringifyQuery + stringifyQueryOptions?: T +} + +export interface FluentUrlConfig< + PQO extends PlainObjectConstraint, + SQO extends PlainObjectConstraint, +> { + parseQuery: ParseQuery + parseQueryOptions?: PQO + stringifyQuery: StringifyQuery + stringifyQueryOptions?: SQO +} + +export interface NormalizeOptionsArgs { + urlOrOptions?: Partial | string + parseQuery: ParseQuery + parseQueryOptions?: T } diff --git a/package.json b/package.json index 7977a28..ab1dcd4 100644 --- a/package.json +++ b/package.json @@ -5,6 +5,7 @@ "main": "index.js", "scripts": { "build": "rimraf ./dist && tsc -p tsconfig.build.json && tsc-alias -p tsconfig.build.json", + "type-check": "tsc --noEmit --skipLibCheck", "prepare": "pnpm build", "test": "vitest" },