UNPKG

@asterflow/url-parser

Version:

High-performance typed URL parser with AST-based analysis, automatic type casting, and route pattern matching for modern web applications

499 lines (496 loc) 16.4 kB
// Generated by dts-bundle-generator v9.5.1 export declare class AST<const Path extends string> { readonly errors: ErrorLog[]; readonly expressions: Set<number>; readonly input: Path; readonly nodes: Node[]; constructor(input: Path, errors?: ErrorLog[]); /** * Finds a Node by its index or its content string. * * @param {string | number} idOrName - Node index or content to search. * @param {Node[]} [nodes=this.nodes] - Optional node array to search. * @param {?string} [input] - Optional input string context. * @returns {Node | undefined} The matching Node or undefined. */ getNode(idOrName: string | number, nodes?: Node[], input?: string): Node | undefined; /** * Finds the Nth Node of a given expression type. * * @param {AllValues} type - Expression or delimiter type code. * @param {number} [position=0] - Zero-based occurrence index. * @param {Node[]} [nodes=this.nodes] - Optional node array to search. * @returns {Node | undefined} The matching Node or undefined. */ getNodeByType(type: AllValues, position?: number, nodes?: Node[]): Node | undefined; /** * Retrieves the declared type of a Node's next value token. * * @param {string | number} idOrName - Node index or content to search. * @param {Node[]} [nodes=this.nodes] - Optional node array to search. * @param {?string} [input] - Optional input string context. */ getType(idOrName: string | number, nodes?: Node[], input?: string): InternalExpression.Null | ContentTypes | undefined; /** * Extracts raw content string from a Node's start/end positions. * * @param {Node} node - The Node to extract from. * @param {string} [input=this.input] - Optional input string context. * @returns {string} The substring for the node. */ getContent(node: Node, input?: string): string; /** * Retrieves the raw value string following a parameter or variable Node. * * @param {string | number} idOrName - Node index or content to search. * @param {Node[]} [nodes=this.nodes] - Optional node array to search. * @param {?string} [input] - Optional input string context. * @returns {string | undefined} The raw value, or undefined if absent. */ getValue(idOrName: string | number, nodes?: Node[], input?: string): string | undefined; /** * Prints a formatted table of nodes and colors the path output. * * @param {Node[]} [node=this.nodes] - Optional node array to display. * @param {string} [input=this.input] - Optional input context for coloring. */ display(node?: Node[], input?: string): string; parser(input: string, errors: ErrorLog[]): { nodes: Node[]; expressions: Set<number>; }; } /** * Parses and analyzes a URL template or real URL, extracting parameters, variables, * path segments, search params, fragment, and origin components (protocol, hostname, port). * Provides helper methods to retrieve and cast values according to defined types. * * @example * ```ts * import { Analyze } from '@asterflow/url-parser' * * // Template with typed parameter declaration and defaults * const template = 'http://example.com/users/:id=number?active=boolean' * const parser = new Analyze(template) * * // Display internal node table * console.log(parser.display()) * console.log(parser.getPathname()) // '/users/:id' * console.log(parser.getParams()) // ['id'] * console.log(parser.getSearchParams()) // Map(1) { "active": "boolean" } * * // Parse an actual URL * const url = 'http://example.com/users/42?active=true' * const parsed = new Analyze(url, parser) * * console.log(parsed.getPathname()) // '/users/42' * console.log(parsed.getParams()) // { id: 42 } * console.log(parsed.getSearchParams()) // { active: true } * ``` */ export declare class Analyze<const Path extends string, const TypedPath extends ParsePath<Path> = ParsePath<Path>, const Parser extends Analyze<string> | undefined = undefined> { readonly input: Path; readonly errors: ErrorLog[]; readonly ast: AST<Path>; base?: Parser; isParser: Parser extends undefined ? true : false; constructor(input: Path, base?: Parser); /** * Returns an array of parameter names declared in the template (without values). * @returns {string[]} List of parameter names. * @throws {Error} If duplicate parameter names are detected. * @example * ```ts * const analyzer = new Analyze('http://localhost:3000/:a/:b') * console.log(analyzer.getParams()) // ['a','b'] * ``` */ getParams(this: Analyze<Path, TypedPath, undefined>): string[]; getParams<P extends Analyze<any>>(this: Analyze<Path, TypedPath, P>): P extends Analyze<infer _TemplatePath, infer TemplateTypedPath> ? TemplateTypedPath["params"] : never; /** * Returns a Map of search parameters and their values (string or string[] for multiples). * @returns {Map<string, string | string[]>} The search parameters map. * @example * ```ts * const analyzer = new Analyze('?a=1&a=2&b=xyz') * console.log(analyzer.getSearchParams().get('a')) // ['1','2'] * console.log(analyzer.getSearchParams().get('b')) // 'xyz' * ``` */ getSearchParams(this: Analyze<Path, TypedPath, undefined>): Map<string, string | number | boolean | string[]>; getSearchParams<P extends Analyze<any>>(this: Analyze<Path, TypedPath, P>): P extends Analyze<infer _TemplatePath, infer TemplateTypedPath> ? TemplateTypedPath["searchParams"] : never; /** * Retrieves the fragment identifier from the input URL or template. * * @remarks * The fragment identifier (part after '#') is not sent to the server in HTTP requests * because browsers strip it before sending. This method extracts and returns that fragment * on the client side only. * * @returns {string | undefined | Record<string,string | string>} * - When called on a template without a base, returns the fragment string (without '#'), or undefined if none. * - When called on a template with a base AST, returns a record mapping the fragment key to its value. * * @example * ```ts * const analyzer = new Analyze('http://localhost:3000/page#section1'); * console.log(analyzer.getFragment()); // 'section1' * ``` */ getFragment(this: Analyze<Path, TypedPath, undefined>): string | undefined; getFragment<P extends Analyze<any>>(this: Analyze<Path, TypedPath, P>): P extends Analyze<infer _TemplatePath, infer TemplateTypedPath> ? TemplateTypedPath["fragment"] : never; /** * Extracts dynamic route parameters by navigating the base AST and finding corresponding * values in the instance AST. It supports `[id]` and `[...slug]` formats. * * @returns {Record<string, string | string[]>} An object mapping parameter names to their extracted values. */ getStaticProps(): Record<string, string | string[]>; /** * Retrieves the pathname (path + variables) from the parsed template. * @returns {string} The pathname (e.g., '/users/:id'). * @example * ```ts * const analyzer = new Analyze('http://localhost:3000/users/:userId/profile') * console.log(analyzer.getPathname()) // '/users/:userId/profile' * ``` */ getPathname(): string; /** * Retrieves the port from the input URL, if present. * @returns {string | undefined} The port string, or undefined if none. * @example * ```ts * const analyzer = new Analyze('http://localhost:8080') * console.log(analyzer.getPort()) // '8080' * ``` */ getPort(): string | undefined; /** * Retrieves the hostname from the input URL. * @returns {string | undefined} The hostname, or undefined if not found. * @example * ```ts * const analyzer = new Analyze('https://example.com/path') * console.log(analyzer.getHostname()) // 'example.com' * ``` */ getHostname(): string | undefined; /** * Retrieves the protocol (http or https) from the input URL. * @returns {string | undefined} The protocol, or undefined if not found. * @example * ```ts * const analyzer = new Analyze('https://site.org') * console.log(analyzer.getProtocol()) // 'https' * ``` */ getProtocol(): string | undefined; setParser(base: Parser): void; /** * Serializes the internal Node array into a Buffer representation. * @returns {Buffer} The Buffer containing node data. */ getBuffer(): Buffer; /** * Checks if any errors were found during parsing. * @returns {boolean} True if errors exist. */ hasErrors(errors?: ErrorLog[]): boolean; /** * Returns a formatted string of all parsing errors. * @returns {string} The formatted error report. */ displayErrors(errors?: ErrorLog[]): string; /** * Casts a raw string value to the given content type. * Logs an error if casting fails. */ private castValue; /** * Adds or appends a parameter value into the map, handling duplicates. * * @private * @param {Map<string, string | string[]>} map - The map to populate. * @param {string} variable - Parameter name. * @param {string} content - Parameter value. */ private appendParam; withParser<P extends Analyze<Path, TypedPath, any>>(parser: P): Analyze<Path, TypedPath, P>; } /** * Represents a parsing error with structured information. */ export declare class ErrorLog { readonly code: string; readonly message: string; readonly start: number; readonly end: number; constructor(code: string, message: string, start: number, end: number); /** * Displays the error message with its location. */ display(input: string): string; } export declare class Node { /** * @size 1 Byte */ readonly id: number; /** * @size 1 Byte */ expression: AllValues; /** * @size 2 Byte */ start: number; /** * @size 2 Byte */ end: number; /** * @size 1 Byte */ type: ContentTypes | InternalExpression.Null; static readonly SIZE: 7; constructor( /** * @size 1 Byte */ id: number, /** * @size 1 Byte */ expression?: AllValues, /** * @size 2 Byte */ start?: number, /** * @size 2 Byte */ end?: number, /** * @size 1 Byte */ type?: ContentTypes | InternalExpression.Null); setExpression(expression: AllValues): this; setType(type: ContentTypes): this; setPosition(x1: number, x2: number): this; writeToBuffer(buffer: Buffer, off: number): void; /** read a Node out of a Buffer at byte-offset `off` */ static fromBuffer(buffer: Buffer): Node[]; } export declare const RawTokens: { readonly [x: number]: string; readonly Boolean: ContentTypes.Boolean; readonly String: ContentTypes.String; readonly Number: ContentTypes.Number; readonly Array: ContentTypes.Array; readonly Null: InternalExpression.Null; readonly Slug: InternalExpression.Slug; readonly Ellipsis: InternalExpression.Ellipsis; /** * Essa expressão é utilizada no Delimitador Asterisk (*) */ readonly Void: InternalExpression.Void; readonly Path: InternalExpression.Path; readonly Variable: InternalExpression.Variable; /** * Essa expressão é utilizada no Delimitador Hash (#) */ readonly Fragment: InternalExpression.Fragment; /** * Essa expressão é utilizada após os Delimitadores Query (?), Ampersand (&) e Semicolon (;) */ readonly Parameter: InternalExpression.Parameter; /** * Essa expressão é utilizada após os Delimitador Equal (=) */ readonly Value: InternalExpression.Value; readonly Protocol: OriginExpression.Protocol; readonly Hostname: OriginExpression.Hostname; readonly Port: OriginExpression.Port; /** * @property = */ readonly Equal: EncodingSymbols.Equal; /** * @property & */ readonly Ampersand: Delimiters.Ampersand; /** * @property ; */ readonly Semicolon: Delimiters.Semicolon; /** * @property # */ readonly Hash: Delimiters.Hash; /** * @property ? */ readonly Query: Delimiters.Query; /** * @property : */ readonly Colon: Delimiters.Colon; /** * @property / */ readonly Slash: Delimiters.Slash; /** * @property * */ readonly Asterisk: Delimiters.Asterisk; /** * @property [ */ readonly LeftBracket: Delimiters.LeftBracket; /** * @property ] */ readonly RightBracket: Delimiters.RightBracket; }; export declare const delimitersValues: (string | Delimiters)[]; export declare enum CatchAllExpression { /** * @property . */ Point = 46 } export declare enum ContentTypes { Boolean = 247, String = 248, Number = 249, Array = 250 } export declare enum Delimiters { /** * @property & */ Ampersand = 38, /** * @property ; */ Semicolon = 59, /** * @property # */ Hash = 35, /** * @property ? */ Query = 63, /** * @property : */ Colon = 58, /** * @property / */ Slash = 47, /** * @property * */ Asterisk = 42, /** * @property [ */ LeftBracket = 91, /** * @property ] */ RightBracket = 93 } export declare enum EncodingSymbols { /** * @property = */ Equal = 61 } export declare enum InternalExpression { Null = 0,// no active token Slug = 243, Ellipsis = 242, /** * Essa expressão é utilizada no Delimitador Asterisk (*) */ Void = 250, Path = 251, Variable = 252, /** * Essa expressão é utilizada no Delimitador Hash (#) */ Fragment = 253, /** * Essa expressão é utilizada após os Delimitadores Query (?), Ampersand (&) e Semicolon (;) */ Parameter = 254, /** * Essa expressão é utilizada após os Delimitador Equal (=) */ Value = 255 } export declare enum OriginExpression { Protocol = 246, Hostname = 245, Port = 244 } export type AllValues = Delimiters | EncodingSymbols | InternalExpression | OriginExpression; export type ExtractFrag<S extends string> = S extends `${string}#${infer F}` ? F : ""; export type ExtractPath<S extends string> = S extends `${infer P}?${string}` ? P : S extends `${infer P}#${string}` ? P : S extends `/${infer Rest}` ? Rest : S; export type ExtractQuery<S extends string> = S extends `${string}?${infer Q}` ? Q extends `${infer QnoFrag}#${string}` ? QnoFrag : Q : ""; /** * Normalizes a URL path by performing several cleaning operations: * 1. Replaces double slashes ('//') with a single slash. * 2. Removes the URL fragment (e.g., '#about'). * 3. Removes the query string (e.g., '?data=1'). * 4. Removes type definitions from path segments (e.g., ':id=number' -> ':id'). * @example * // '/users//:id=number?data=1#profile' -> '/users/:id' */ export type NormalizePath<Path extends string> = Path extends `${infer Head}//${infer Tail}` ? NormalizePath<`${Head}/${Tail}`> : SanitizeSegments<RemoveQueryString<RemoveFragment<Path>>>; export type ParseFragment<S extends string> = ExtractFrag<S> extends "" ? {} : { [K in ExtractFrag<S>]: string; }; export type ParseParams<S extends string> = { [Seg in Split<ExtractPath<S>, "/">[number] as Seg extends `:${infer Key}=${infer _T}` ? Key : Seg extends `:${infer Key}` ? Key : never]: Seg extends `:${infer _Key}=${infer T}` ? TypeMap<T> : Seg extends `:${infer _Key}` ? string : never; }; export type ParsePath<S extends string> = { params: ParseParams<S>; searchParams: ParseSearch<S>; fragment: ParseFragment<S>; }; export type ParseSearch<S extends string> = { [Param in Split<ExtractQuery<S>, "&">[number] as Param extends `${infer Key}=${infer _V}` ? Key : Param]: Param extends `${infer _Key}=${infer V}` ? TypeMap<V> : string; }; /** * Removes the fragment identifier from a URL path. * @example * // '/page#about' -> '/page' */ export type RemoveFragment<Path extends string> = Path extends `${infer CleanPath}#${string}` ? CleanPath : Path; /** * Removes the query string from a URL path. * @example * // '/users?data=1' -> '/users' */ export type RemoveQueryString<Path extends string> = Path extends `${infer CleanPath}?${string}` ? CleanPath : Path; /** * Removes type definitions from path segments. * Recursively processes the path to clean parts like '=number'. * @example * // '/users/:id=number' -> '/users/:id' */ export type SanitizeSegments<Path extends string> = Path extends `/${infer Rest}` ? `/${SanitizeSegments<Rest>}` : Path extends `${infer Segment}/${infer Rest}` ? `${(Segment extends `${infer Name}=${string}` ? Name : Segment)}/${SanitizeSegments<Rest>}` : Path extends `${infer Name}=${string}` ? Name : Path; export type Split<S extends string, D extends string> = S extends `${infer Head}${D}${infer Tail}` ? [ Head, ...Split<Tail, D> ] : S extends "" ? [ ] : [ S ]; export type TypeMap<T extends string> = T extends "string" ? string : T extends "number" ? number : T extends "boolean" ? boolean : T extends "array" ? string[] : T extends `${infer First},${infer _Rest}` ? TypeMap<First>[] : string; export {};