@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
TypeScript
// 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 {};