UNPKG

better-auth

Version:

The most comprehensive authentication framework for TypeScript.

108 lines (107 loc) 4.34 kB
//#region src/utils/wildcard.ts /** * Escapes a character if it has a special meaning in regular expressions * and returns the character as is if it doesn't */ function escapeRegExpChar(char) { if (char === "-" || char === "^" || char === "$" || char === "+" || char === "." || char === "(" || char === ")" || char === "|" || char === "[" || char === "]" || char === "{" || char === "}" || char === "*" || char === "?" || char === "\\") return `\\${char}`; else return char; } /** * Escapes all characters in a given string that have a special meaning in regular expressions */ function escapeRegExpString(str) { let result = ""; for (let i = 0; i < str.length; i++) result += escapeRegExpChar(str[i]); return result; } /** * Transforms one or more glob patterns into a RegExp pattern */ function transform(pattern, separator = true) { if (Array.isArray(pattern)) return `(?:${pattern.map((p) => `^${transform(p, separator)}$`).join("|")})`; let separatorSplitter = ""; let separatorMatcher = ""; let wildcard = "."; if (separator === true) { separatorSplitter = "/"; separatorMatcher = "[/\\\\]"; wildcard = "[^/\\\\]"; } else if (separator) { separatorSplitter = separator; separatorMatcher = escapeRegExpString(separatorSplitter); if (separatorMatcher.length > 1) { separatorMatcher = `(?:${separatorMatcher})`; wildcard = `((?!${separatorMatcher}).)`; } else wildcard = `[^${separatorMatcher}]`; } const requiredSeparator = separator ? `${separatorMatcher}+?` : ""; const optionalSeparator = separator ? `${separatorMatcher}*?` : ""; const segments = separator ? pattern.split(separatorSplitter) : [pattern]; let result = ""; for (let s = 0; s < segments.length; s++) { const segment = segments[s]; const nextSegment = segments[s + 1]; let currentSeparator = ""; if (!segment && s > 0) continue; if (separator) if (s === segments.length - 1) currentSeparator = optionalSeparator; else if (nextSegment !== "**") currentSeparator = requiredSeparator; else currentSeparator = ""; if (separator && segment === "**") { if (currentSeparator) { result += s === 0 ? "" : currentSeparator; result += `(?:${wildcard}*?${currentSeparator})*?`; } continue; } for (let c = 0; c < segment.length; c++) { const char = segment[c]; if (char === "\\") { if (c < segment.length - 1) { result += escapeRegExpChar(segment[c + 1]); c++; } } else if (char === "?") result += wildcard; else if (char === "*") result += `${wildcard}*?`; else result += escapeRegExpChar(char); } result += currentSeparator; } return result; } function isMatch(regexp, sample) { if (typeof sample !== "string") throw new TypeError(`Sample must be a string, but ${typeof sample} given`); return regexp.test(sample); } /** * Compiles one or more glob patterns into a RegExp and returns an isMatch function. * The isMatch function takes a sample string as its only argument and returns `true` * if the string matches the pattern(s). * * ```js * wildcardMatch('src/*.js')('src/index.js') //=> true * ``` * * ```js * const isMatch = wildcardMatch('*.example.com', '.') * isMatch('foo.example.com') //=> true * isMatch('foo.bar.com') //=> false * ``` */ function wildcardMatch(pattern, options) { if (typeof pattern !== "string" && !Array.isArray(pattern)) throw new TypeError(`The first argument must be a single pattern string or an array of patterns, but ${typeof pattern} given`); if (typeof options === "string" || typeof options === "boolean") options = { separator: options }; if (arguments.length === 2 && !(typeof options === "undefined" || typeof options === "object" && options !== null && !Array.isArray(options))) throw new TypeError(`The second argument must be an options object or a string/boolean separator, but ${typeof options} given`); options = options || {}; if (options.separator === "\\") throw new Error("\\ is not a valid separator because it is used for escaping. Try setting the separator to `true` instead"); const regexpPattern = transform(pattern, options.separator); const regexp = new RegExp(`^${regexpPattern}$`, options.flags); const fn = isMatch.bind(null, regexp); fn.options = options; fn.pattern = pattern; fn.regexp = regexp; return fn; } //#endregion export { wildcardMatch }; //# sourceMappingURL=wildcard.mjs.map