diff --git a/package.json b/package.json index fa0781b..9121931 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,16 @@ "default": "./dist/cjs/index.js" } }, + "./utils": { + "import": { + "types": "./dist/esm/utils.d.mts", + "default": "./dist/esm/utils/index.mjs" + }, + "require": { + "types": "./dist/cjs/utils.d.ts", + "default": "./dist/cjs/utils/index.js" + } + }, "./package.json": "./package.json" }, "files": [ diff --git a/src/nurl.ts b/src/nurl.ts index 4d9ff5d..de40308 100644 --- a/src/nurl.ts +++ b/src/nurl.ts @@ -1,4 +1,5 @@ import {decode, encode} from './punycode' +import {mask, MaskOptions, match as matchUrlPattern} from './utils' import { extractPathKey, getDynamicPaths, @@ -8,8 +9,7 @@ import { refineQueryWithPathname, convertQueryToArray, Query, - getPathPriority, -} from './utils' +} from './utils/internal' interface URLOptions extends Partial< @@ -32,14 +32,6 @@ interface URLOptions basePath?: string } -export interface MaskOptions { - patterns: string[] - sensitiveParams: string[] - maskChar?: string - maskLength?: number - preserveLength?: boolean -} - export default class NURL implements URL { private _href: string = '' private _protocol: string = '' @@ -483,57 +475,10 @@ export default class NURL implements URL { } static match(url: string, pattern: string) { - if (!NURL.canParse(url) || !NURL.canParse(pattern)) { - return null - } - - const urlSegments = url.split(/[?#]/)[0]?.split('/').filter(Boolean) || [] - const patternSegments = pattern.split(/[?#]/)[0]?.split('/').filter(Boolean) || [] - - if (urlSegments.length !== patternSegments.length) { - return null - } - - const params: Record = {} - - for (let i = 0; i < patternSegments.length; i++) { - const patternSegment = patternSegments[i] - const urlSegment = urlSegments[i] - - if (isDynamicPath(patternSegment)) { - const pathKey = extractPathKey(patternSegment) - params[pathKey] = urlSegment - } else if (patternSegment !== urlSegment) { - return null - } - } - - return params + return matchUrlPattern(url, pattern) } - static mask( - url: string, - {patterns, sensitiveParams, maskChar = '*', maskLength = 4, preserveLength = false}: MaskOptions, - ) { - const sortedPatterns = [...patterns].sort((a, b) => (getPathPriority(b) > getPathPriority(a) ? 1 : -1)) - for (const pattern of sortedPatterns) { - const urlObj = NURL.create(url) - const matchedParams = NURL.match(urlObj.pathname, pattern) - if (!matchedParams) { - continue - } - sensitiveParams.forEach((sensitiveParam) => { - if (sensitiveParam in matchedParams) { - const originalValue = matchedParams[sensitiveParam] - const lengthToMask = preserveLength ? originalValue.length : maskLength - matchedParams[sensitiveParam] = maskChar.repeat(lengthToMask) - } - }) - - urlObj.pathname = refinePathnameWithQuery(pattern, matchedParams) - return urlObj.toString() - } - - return url + static mask(url: string, options: MaskOptions) { + return mask(url, options) } } diff --git a/src/utils/external.ts b/src/utils/external.ts new file mode 100644 index 0000000..1e0240f --- /dev/null +++ b/src/utils/external.ts @@ -0,0 +1,65 @@ +import NURL from '../nurl' +import {extractPathKey, getPathPriority, isDynamicPath, refinePathnameWithQuery} from './internal' + +export const match = (url: string, pattern: string): Record | null => { + if (!NURL.canParse(url) || !NURL.canParse(pattern)) { + return null + } + + const urlSegments = url.split(/[?#]/)[0]?.split('/').filter(Boolean) || [] + const patternSegments = pattern.split(/[?#]/)[0]?.split('/').filter(Boolean) || [] + + if (urlSegments.length !== patternSegments.length) { + return null + } + + const params: Record = {} + + for (let i = 0; i < patternSegments.length; i++) { + const patternSegment = patternSegments[i] + const urlSegment = urlSegments[i] + + if (isDynamicPath(patternSegment)) { + const pathKey = extractPathKey(patternSegment) + params[pathKey] = urlSegment + } else if (patternSegment !== urlSegment) { + return null + } + } + + return params +} + +export interface MaskOptions { + patterns: string[] + sensitiveParams: string[] + maskChar?: string + maskLength?: number + preserveLength?: boolean +} + +export const mask = ( + url: string, + {patterns, sensitiveParams, maskChar = '*', maskLength = 4, preserveLength = false}: MaskOptions, +) => { + const sortedPatterns = [...patterns].sort((a, b) => (getPathPriority(b) > getPathPriority(a) ? 1 : -1)) + for (const pattern of sortedPatterns) { + const urlObj = NURL.create(url) + const matchedParams = match(urlObj.pathname, pattern) + if (!matchedParams) { + continue + } + sensitiveParams.forEach((sensitiveParam) => { + if (sensitiveParam in matchedParams) { + const originalValue = matchedParams[sensitiveParam] + const lengthToMask = preserveLength ? originalValue.length : maskLength + matchedParams[sensitiveParam] = maskChar.repeat(lengthToMask) + } + }) + + urlObj.pathname = refinePathnameWithQuery(pattern, matchedParams) + return urlObj.toString() + } + + return url +} diff --git a/src/utils/index.ts b/src/utils/index.ts new file mode 100644 index 0000000..eea7f38 --- /dev/null +++ b/src/utils/index.ts @@ -0,0 +1 @@ +export * from './external' diff --git a/src/utils.ts b/src/utils/internal.ts similarity index 100% rename from src/utils.ts rename to src/utils/internal.ts diff --git a/vite.config.mts b/vite.config.mts index f66a3da..2ebc1d1 100644 --- a/vite.config.mts +++ b/vite.config.mts @@ -9,6 +9,7 @@ export default createViteConfig({ cwd: __dirname, entry: { index: './src/index.ts', + utils: './src/utils/index.ts', }, outputs: [ {format: 'es', dist: 'dist/esm'},