@virentia/router-paths 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Virentia
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,57 @@
1
+ # @virentia/router-paths
2
+
3
+ Typed path compiler for Virentia Router.
4
+
5
+ It turns route templates into parser and builder functions. The router uses this package internally, and it can also be used by adapters, tests, and tools that need the same path semantics without creating routes.
6
+
7
+ ## Links
8
+
9
+ - Documentation: [movpushmov.dev/virentia/router/paths](https://movpushmov.dev/virentia/router/paths)
10
+
11
+ ## Install
12
+
13
+ ```sh
14
+ pnpm add @virentia/router-paths
15
+ ```
16
+
17
+ ## Compile
18
+
19
+ ```ts
20
+ import { compile } from "@virentia/router-paths";
21
+
22
+ const profilePath = compile("/profile/:id<number>");
23
+
24
+ profilePath.parse("/profile/42");
25
+ // { path: "/profile/42", params: { id: 42 } }
26
+
27
+ profilePath.build({ id: 42 });
28
+ // "/profile/42"
29
+ ```
30
+
31
+ Templates support plain string params, `number` params, literal unions, optional params, repeated params, and ranged params.
32
+
33
+ ```ts
34
+ const filesPath = compile("/files/:path<string|image>{1,3}");
35
+
36
+ filesPath.parse("/files/string/image");
37
+ // { path: "/files/string/image", params: { path: ["string", "image"] } }
38
+ ```
39
+
40
+ ## Express conversion
41
+
42
+ `convertPath` converts Virentia templates into an express-compatible shape for integrations that need route matching outside the Virentia router.
43
+
44
+ ```ts
45
+ import { convertPath } from "@virentia/router-paths";
46
+
47
+ convertPath("/files/:id<number>?", "express");
48
+ // "/files{/:id}"
49
+ ```
50
+
51
+ ## Main API
52
+
53
+ `compile`, `convertPath`, `Builder`, `Parser`, `ParseUrlParams`, `ValidatePath`.
54
+
55
+ ## License
56
+
57
+ MIT © 2026 movpushmov
package/dist/index.cjs ADDED
@@ -0,0 +1,181 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ //#region lib/get-token-parameters.ts
3
+ function getTokenParameters(match) {
4
+ if (!match) return null;
5
+ const [, name, rawGenericProps, rawArrayProps, modifier] = match;
6
+ return {
7
+ name,
8
+ genericProps: rawGenericProps?.slice(1, -1).replaceAll(" ", ""),
9
+ arrayProps: rawArrayProps?.slice(1, -1).split(",").map((value) => Number(value)),
10
+ modifier
11
+ };
12
+ }
13
+ //#endregion
14
+ //#region lib/prepare-builder.ts
15
+ function prepareBuilder(tokens) {
16
+ return ((params = void 0) => {
17
+ const result = [];
18
+ if (tokens.length === 0) return "/";
19
+ for (const token of tokens) {
20
+ if (token.type === "const") {
21
+ result.push(token.name);
22
+ continue;
23
+ }
24
+ const value = params?.[token.name];
25
+ if (value == null) continue;
26
+ if (Array.isArray(value)) for (const item of value) result.push(String(item));
27
+ else result.push(String(value));
28
+ }
29
+ return `/${result.join("/")}`;
30
+ });
31
+ }
32
+ //#endregion
33
+ //#region lib/prepare-parser.ts
34
+ function prepareParser(tokens) {
35
+ return (input) => {
36
+ const rawTokens = input.split("/").map((part) => part.trim()).filter((part) => part !== "");
37
+ let params = null;
38
+ function setKey(key, value) {
39
+ params ??= {};
40
+ params[key] = value;
41
+ }
42
+ if (tokens.length === 0) return rawTokens.length === 0 ? {
43
+ path: input,
44
+ params: null
45
+ } : null;
46
+ for (let i = 0; i < tokens.length; i += 1) {
47
+ const token = tokens[i];
48
+ if (!token) return null;
49
+ if (token.type === "const") {
50
+ if (token.name !== rawTokens.shift()) return null;
51
+ continue;
52
+ }
53
+ const { arrayProps, genericProps, required } = token.payload;
54
+ if (arrayProps) {
55
+ const array = [];
56
+ let rawToken;
57
+ while (true) {
58
+ rawToken = rawTokens.shift();
59
+ if (!rawToken) break;
60
+ const parsed = parseToken(rawToken, genericProps);
61
+ if (!parsed.success) return null;
62
+ array.push(parsed.value);
63
+ if (array.length >= (arrayProps.max ?? Infinity)) break;
64
+ }
65
+ if (array.length < (arrayProps.min ?? 0)) return null;
66
+ if (rawTokens.length > 0 && !tokens[i + 1]) return null;
67
+ setKey(token.name, array);
68
+ continue;
69
+ }
70
+ const rawToken = rawTokens.shift();
71
+ if (required && !rawToken) return null;
72
+ if (!rawToken) {
73
+ setKey(token.name, void 0);
74
+ continue;
75
+ }
76
+ const parsed = parseToken(rawToken, genericProps);
77
+ if (!parsed.success) return null;
78
+ setKey(token.name, parsed.value);
79
+ }
80
+ if (rawTokens.length > 0) return null;
81
+ return {
82
+ path: input,
83
+ params
84
+ };
85
+ };
86
+ }
87
+ function parseToken(rawToken, genericProps) {
88
+ if (genericProps?.type === "number") {
89
+ if (Number.isNaN(Number(rawToken))) return { success: false };
90
+ return {
91
+ success: true,
92
+ value: Number(rawToken)
93
+ };
94
+ }
95
+ if (genericProps?.type === "union") {
96
+ if (!genericProps.items.includes(rawToken)) return { success: false };
97
+ return {
98
+ success: true,
99
+ value: rawToken
100
+ };
101
+ }
102
+ return {
103
+ success: true,
104
+ value: rawToken
105
+ };
106
+ }
107
+ //#endregion
108
+ //#region lib/compile.ts
109
+ function compile(path) {
110
+ const tokens = [];
111
+ const regexp = /:(\w+)(<[\s?\w|]+>)?({\d+,\d+})?([+*?])?/;
112
+ const parsedTokens = path.split("/").filter(Boolean);
113
+ for (const parsedToken of parsedTokens) {
114
+ const parameters = getTokenParameters(parsedToken.match(regexp));
115
+ if (!parameters) {
116
+ tokens.push({
117
+ type: "const",
118
+ name: parsedToken,
119
+ payload: void 0
120
+ });
121
+ continue;
122
+ }
123
+ const { arrayProps, genericProps, modifier, name } = parameters;
124
+ if (!name) throw new Error(`Invalid path: "${path}". Name for argument must be provided`);
125
+ const token = {
126
+ type: "parameter",
127
+ name,
128
+ payload: { required: true }
129
+ };
130
+ if (genericProps === "number") token.payload.genericProps = { type: "number" };
131
+ if (genericProps?.includes("|")) token.payload.genericProps = {
132
+ type: "union",
133
+ items: genericProps.split("|")
134
+ };
135
+ switch (modifier) {
136
+ case "*":
137
+ token.payload.arrayProps = {};
138
+ break;
139
+ case "+":
140
+ token.payload.arrayProps = { min: 1 };
141
+ break;
142
+ case "?":
143
+ token.payload.required = false;
144
+ break;
145
+ }
146
+ if (arrayProps) token.payload.arrayProps = {
147
+ ...token.payload.arrayProps,
148
+ min: arrayProps[0],
149
+ max: arrayProps[1]
150
+ };
151
+ tokens.push(token);
152
+ }
153
+ return {
154
+ parse: prepareParser(tokens),
155
+ build: prepareBuilder(tokens)
156
+ };
157
+ }
158
+ //#endregion
159
+ //#region lib/convert-path.ts
160
+ const cases = { express: [
161
+ [/:id<.+>/g, ":id"],
162
+ [/:id\+/g, "*id"],
163
+ [/:id\*/g, "*id"],
164
+ [/:id\{.+\}/g, "*id"],
165
+ [/([a-zA-Z0-9:/_.]+)\/([*:])id\?/g, "$1{/$2id}"],
166
+ [/([*:])id\?/g, "{$1id}"]
167
+ ] };
168
+ function convertPath(path, mode) {
169
+ switch (mode) {
170
+ case "express": {
171
+ let nextPath = path;
172
+ for (const [regex, replacement] of cases.express) if (nextPath.match(regex)) nextPath = nextPath.replace(regex, replacement);
173
+ return nextPath;
174
+ }
175
+ }
176
+ }
177
+ //#endregion
178
+ exports.compile = compile;
179
+ exports.convertPath = convertPath;
180
+
181
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","names":[],"sources":["../lib/get-token-parameters.ts","../lib/prepare-builder.ts","../lib/prepare-parser.ts","../lib/compile.ts","../lib/convert-path.ts"],"sourcesContent":["interface TokenParameters {\n name: string | undefined;\n genericProps: string | undefined;\n arrayProps: [number, number] | undefined;\n modifier: string | undefined;\n}\n\nexport function getTokenParameters(match: RegExpMatchArray | null): TokenParameters | null {\n if (!match) {\n return null;\n }\n\n const [, name, rawGenericProps, rawArrayProps, modifier] = match;\n const genericProps = rawGenericProps?.slice(1, -1).replaceAll(\" \", \"\");\n const arrayProps = rawArrayProps\n ?.slice(1, -1)\n .split(\",\")\n .map((value) => Number(value)) as [number, number] | undefined;\n\n return {\n name,\n genericProps,\n arrayProps,\n modifier\n };\n}\n","import type { Builder, Token } from \"./types\";\n\nexport function prepareBuilder<T>(tokens: Token[]): Builder<T> {\n return ((params: any = undefined) => {\n const result: string[] = [];\n\n if (tokens.length === 0) {\n return \"/\";\n }\n\n for (const token of tokens) {\n if (token.type === \"const\") {\n result.push(token.name);\n continue;\n }\n\n const value = params?.[token.name];\n\n if (value == null) {\n continue;\n }\n\n if (Array.isArray(value)) {\n for (const item of value) {\n result.push(String(item));\n }\n } else {\n result.push(String(value));\n }\n }\n\n return `/${result.join(\"/\")}`;\n }) as Builder<T>;\n}\n","import type { Parser, Token } from \"./types\";\n\nexport function prepareParser<T>(tokens: Token[]): Parser<T> {\n return (input) => {\n const rawTokens = input\n .split(\"/\")\n .map((part) => part.trim())\n .filter((part) => part !== \"\");\n\n let params: Record<string, unknown> | null = null;\n\n function setKey(key: string, value: unknown) {\n params ??= {};\n params[key] = value;\n }\n\n if (tokens.length === 0) {\n return rawTokens.length === 0 ? { path: input, params: null as T } : null;\n }\n\n for (let i = 0; i < tokens.length; i += 1) {\n const token = tokens[i];\n\n if (!token) {\n return null;\n }\n\n if (token.type === \"const\") {\n if (token.name !== rawTokens.shift()) {\n return null;\n }\n\n continue;\n }\n\n const { arrayProps, genericProps, required } = token.payload;\n\n if (arrayProps) {\n const array: unknown[] = [];\n let rawToken: string | undefined;\n\n while (true) {\n rawToken = rawTokens.shift();\n\n if (!rawToken) {\n break;\n }\n\n const parsed = parseToken(rawToken, genericProps);\n\n if (!parsed.success) {\n return null;\n }\n\n array.push(parsed.value);\n\n if (array.length >= (arrayProps.max ?? Infinity)) {\n break;\n }\n }\n\n if (array.length < (arrayProps.min ?? 0)) {\n return null;\n }\n\n if (rawTokens.length > 0 && !tokens[i + 1]) {\n return null;\n }\n\n setKey(token.name, array);\n continue;\n }\n\n const rawToken = rawTokens.shift();\n\n if (required && !rawToken) {\n return null;\n }\n\n if (!rawToken) {\n setKey(token.name, undefined);\n continue;\n }\n\n const parsed = parseToken(rawToken, genericProps);\n\n if (!parsed.success) {\n return null;\n }\n\n setKey(token.name, parsed.value);\n }\n\n if (rawTokens.length > 0) {\n return null;\n }\n\n return { path: input, params: params as T };\n };\n}\n\nfunction parseToken(\n rawToken: string,\n genericProps: { type: \"union\"; items: string[] } | { type: \"number\" } | undefined,\n): { success: true; value: unknown } | { success: false } {\n if (genericProps?.type === \"number\") {\n if (Number.isNaN(Number(rawToken))) {\n return { success: false };\n }\n\n return { success: true, value: Number(rawToken) };\n }\n\n if (genericProps?.type === \"union\") {\n if (!genericProps.items.includes(rawToken)) {\n return { success: false };\n }\n\n return { success: true, value: rawToken };\n }\n\n return { success: true, value: rawToken };\n}\n","import { getTokenParameters } from \"./get-token-parameters\";\nimport { prepareBuilder } from \"./prepare-builder\";\nimport { prepareParser } from \"./prepare-parser\";\nimport type { ParameterToken, ParseUrlParams, Token } from \"./types\";\n\nexport function compile<T extends string, Params = ParseUrlParams<T>>(path: T) {\n const tokens: Token[] = [];\n const regexp = /:(\\w+)(<[\\s?\\w|]+>)?({\\d+,\\d+})?([+*?])?/;\n const parsedTokens = path.split(\"/\").filter(Boolean);\n\n for (const parsedToken of parsedTokens) {\n const parameters = getTokenParameters(parsedToken.match(regexp));\n\n if (!parameters) {\n tokens.push({ type: \"const\", name: parsedToken, payload: undefined });\n continue;\n }\n\n const { arrayProps, genericProps, modifier, name } = parameters;\n\n if (!name) {\n throw new Error(`Invalid path: \"${path}\". Name for argument must be provided`);\n }\n\n const token: ParameterToken = {\n type: \"parameter\",\n name,\n payload: {\n required: true\n }\n };\n\n if (genericProps === \"number\") {\n token.payload.genericProps = { type: \"number\" };\n }\n\n if (genericProps?.includes(\"|\")) {\n token.payload.genericProps = {\n type: \"union\",\n items: genericProps.split(\"|\")\n };\n }\n\n switch (modifier) {\n case \"*\": {\n token.payload.arrayProps = {};\n break;\n }\n case \"+\": {\n token.payload.arrayProps = { min: 1 };\n break;\n }\n case \"?\": {\n token.payload.required = false;\n break;\n }\n }\n\n if (arrayProps) {\n token.payload.arrayProps = {\n ...token.payload.arrayProps,\n min: arrayProps[0],\n max: arrayProps[1]\n };\n }\n\n tokens.push(token);\n }\n\n return {\n parse: prepareParser<Params>(tokens),\n build: prepareBuilder<Params>(tokens)\n };\n}\n","type CompatibilityMode = \"express\";\n\nconst cases = {\n express: [\n [/:id<.+>/g, \":id\"],\n [/:id\\+/g, \"*id\"],\n [/:id\\*/g, \"*id\"],\n [/:id\\{.+\\}/g, \"*id\"],\n [/([a-zA-Z0-9:/_.]+)\\/([*:])id\\?/g, \"$1{/$2id}\"],\n [/([*:])id\\?/g, \"{$1id}\"]\n ]\n} as const;\n\nexport function convertPath(path: string, mode: CompatibilityMode): string {\n switch (mode) {\n case \"express\": {\n let nextPath = path;\n\n for (const [regex, replacement] of cases.express) {\n if (nextPath.match(regex)) {\n nextPath = nextPath.replace(regex, replacement);\n }\n }\n\n return nextPath;\n }\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,40 @@
1
+ //#region lib/types.d.ts
2
+ type ReplaceAll<S, From extends string, To extends string> = From extends "" ? S : S extends `${infer R1}${From}${infer R2}` ? `${R1}${To}${ReplaceAll<R2, From, To>}` : S;
3
+ type Parameter<Name extends string, Payload> = { [Key in Name]: Payload };
4
+ type WithModifier<Type, T extends string> = T extends `${infer _K}{${infer _Start},${infer _End}}+` ? Type[] : T extends `${infer _K}{${infer _Start},${infer _End}}*` ? Type[] : T extends `${infer _K}{${infer _Start},${infer _End}}?` ? Type[] | undefined : T extends `${infer _K}{${infer _Start},${infer _End}}` ? Type[] : T extends `${infer _K}+` ? Type[] : T extends `${infer _K}*` ? Type[] : T extends `${infer _K}?` ? Type | undefined : Type;
5
+ type WithoutModifier$1<T extends string> = T extends `${infer K}{${infer _Start},${infer _End}}${infer _Modifier}` ? K : T extends `${infer K}{${infer _Start},${infer _End}}` ? K : T extends `${infer K}?` ? K : T extends `${infer K}*` ? K : T extends `${infer K}+` ? K : T;
6
+ type Union<T extends string, Result = void> = T extends `${infer Start}|${infer Tail}` ? Union<Tail, Result extends void ? Start : Result | Start> : Result extends void ? T : Result | T;
7
+ type GenericType<T extends string> = ReplaceAll<T, " ", ""> extends infer Trimmed ? Trimmed extends "number" ? number : Trimmed extends `${infer _A}|${infer _B}` ? Union<Trimmed & string> : Trimmed : never;
8
+ type UrlParameter<T extends string> = T extends `:${infer Name}<${infer Type}>${infer Modifier}` ? Parameter<WithoutModifier$1<Name>, WithModifier<GenericType<Type>, T>> : T extends `:${infer Name}<${infer Type}>` ? Parameter<Name, GenericType<Type>> : T extends `:${infer Name}` ? Parameter<WithoutModifier$1<Name>, WithModifier<string, T>> : never;
9
+ type UrlParams<T extends string, Result = void> = T extends `/:${infer Parameter}/${infer Route}` ? Result extends void ? UrlParams<`/${Route}`, UrlParameter<`:${Parameter}`>> : UrlParams<`/${Route}`, Result & UrlParameter<`:${Parameter}`>> : T extends `/:${infer Parameter}` ? Result extends void ? UrlParameter<`:${Parameter}`> : Result & UrlParameter<`:${Parameter}`> : T extends `/${infer _Static}/${infer Next}` ? UrlParams<`/${Next}`, Result> : Result;
10
+ type Unwrap<Result extends UrlParams<any, void>> = { [Key in keyof Result]: Result[Key] };
11
+ type ParseUrlParams<T extends string> = Unwrap<UrlParams<T>>;
12
+ type Builder<T> = [T] extends [void] ? (params?: T) => string : (params: T) => string;
13
+ type Parser<T> = (path: string) => {
14
+ path: string;
15
+ params: T;
16
+ } | null;
17
+ //#endregion
18
+ //#region lib/compile.d.ts
19
+ declare function compile<T extends string, Params = ParseUrlParams<T>>(path: T): {
20
+ parse: Parser<Params>;
21
+ build: Builder<Params>;
22
+ };
23
+ //#endregion
24
+ //#region lib/convert-path.d.ts
25
+ type CompatibilityMode = "express";
26
+ declare function convertPath(path: string, mode: CompatibilityMode): string;
27
+ //#endregion
28
+ //#region lib/validate-path.d.ts
29
+ type SplitPath<S> = string extends S ? string[] : S extends `${infer Head}/${infer Tail}` ? Head extends "" ? SplitPath<Tail> : Tail extends "" ? [Head] : [Head, ...SplitPath<Tail>] : [S];
30
+ type JoinPath<T extends any[]> = `/${Join<T>}`;
31
+ type Join<T extends any[]> = T["length"] extends 0 ? never : T extends [infer F, ...infer Rest] ? Join<Rest> extends infer Tail ? [Tail] extends [never] ? `${F & string}` : `${F & string}/${Tail & string}` : never : never;
32
+ type ValidateRange<Range> = Range extends `${infer L},${infer R}` ? L extends `${number}` ? R extends `${number}` ? ["valid", `{${Range}`] : ["invalid", `{${L},number}`] : R extends `${number}` ? ["invalid", `{number,${R}}`] : ["invalid", `{number,number}`] : ["invalid", `{number,number}`];
33
+ type ValidateTypes<GenTypes> = GenTypes extends "number" ? "valid" : GenTypes extends "" ? ["invalid", "<number,union>"] : GenTypes extends `${string}|${string}` | string ? "valid" : ["invalid", "<number,union>"];
34
+ type ValidateTokenBase<Token extends string, PostFix extends string = ""> = Token extends `${infer Param}<${infer Types}>{${infer Range}}` ? ValidateTypes<Types> extends ["invalid", infer TypesReplacer] ? ["invalid", `:${Param}${TypesReplacer & string}{${Range}}${PostFix}`] : ValidateRange<Range> extends ["invalid", infer RangeReplacer] ? ["invalid", `:${Param}<${Types}>${RangeReplacer & string}${PostFix}`] : "valid" : Token extends `${infer Param}<${infer Types}>` ? ValidateTypes<Types> extends ["invalid", infer TypesReplacer] ? ["invalid", `:${Param}${TypesReplacer & string}${PostFix}`] : "valid" : Token extends `${infer Param}{${infer Range}}` ? ValidateRange<Range> extends ["invalid", infer RangeReplacer] ? ["invalid", `:${Param}${RangeReplacer & string}${PostFix}`] : "valid" : "valid";
35
+ type ValidateToken<Token> = Token extends `:${infer RawParam}` ? RawParam extends `${infer WithoutModifier}${"*" | "?" | "+"}` ? RawParam extends `${WithoutModifier}${infer Modifier}` ? ValidateTokenBase<WithoutModifier, Modifier> : never : ValidateTokenBase<RawParam> : "valid";
36
+ type ValidateTokens<Path, Current, Res extends string[] = []> = Current extends [infer Token, ...infer Rest] ? ValidateToken<ReplaceAll<Token, " ", "">> extends ["invalid", infer TokenReplacer] ? ["invalid", JoinPath<[...Res, TokenReplacer, ...Rest]>] : ValidateTokens<Path, Rest, [...Res, Token & string]> : Path;
37
+ type ValidatePath<Path> = ValidateTokens<Path, SplitPath<Path>>;
38
+ //#endregion
39
+ export { type Builder, type ParseUrlParams, type Parser, type ValidatePath, compile, convertPath };
40
+ //# sourceMappingURL=index.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.cts","names":[],"sources":["../lib/types.ts","../lib/compile.ts","../lib/convert-path.ts","../lib/validate-path.ts"],"mappings":"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"}
@@ -0,0 +1,40 @@
1
+ //#region lib/types.d.ts
2
+ type ReplaceAll<S, From extends string, To extends string> = From extends "" ? S : S extends `${infer R1}${From}${infer R2}` ? `${R1}${To}${ReplaceAll<R2, From, To>}` : S;
3
+ type Parameter<Name extends string, Payload> = { [Key in Name]: Payload };
4
+ type WithModifier<Type, T extends string> = T extends `${infer _K}{${infer _Start},${infer _End}}+` ? Type[] : T extends `${infer _K}{${infer _Start},${infer _End}}*` ? Type[] : T extends `${infer _K}{${infer _Start},${infer _End}}?` ? Type[] | undefined : T extends `${infer _K}{${infer _Start},${infer _End}}` ? Type[] : T extends `${infer _K}+` ? Type[] : T extends `${infer _K}*` ? Type[] : T extends `${infer _K}?` ? Type | undefined : Type;
5
+ type WithoutModifier$1<T extends string> = T extends `${infer K}{${infer _Start},${infer _End}}${infer _Modifier}` ? K : T extends `${infer K}{${infer _Start},${infer _End}}` ? K : T extends `${infer K}?` ? K : T extends `${infer K}*` ? K : T extends `${infer K}+` ? K : T;
6
+ type Union<T extends string, Result = void> = T extends `${infer Start}|${infer Tail}` ? Union<Tail, Result extends void ? Start : Result | Start> : Result extends void ? T : Result | T;
7
+ type GenericType<T extends string> = ReplaceAll<T, " ", ""> extends infer Trimmed ? Trimmed extends "number" ? number : Trimmed extends `${infer _A}|${infer _B}` ? Union<Trimmed & string> : Trimmed : never;
8
+ type UrlParameter<T extends string> = T extends `:${infer Name}<${infer Type}>${infer Modifier}` ? Parameter<WithoutModifier$1<Name>, WithModifier<GenericType<Type>, T>> : T extends `:${infer Name}<${infer Type}>` ? Parameter<Name, GenericType<Type>> : T extends `:${infer Name}` ? Parameter<WithoutModifier$1<Name>, WithModifier<string, T>> : never;
9
+ type UrlParams<T extends string, Result = void> = T extends `/:${infer Parameter}/${infer Route}` ? Result extends void ? UrlParams<`/${Route}`, UrlParameter<`:${Parameter}`>> : UrlParams<`/${Route}`, Result & UrlParameter<`:${Parameter}`>> : T extends `/:${infer Parameter}` ? Result extends void ? UrlParameter<`:${Parameter}`> : Result & UrlParameter<`:${Parameter}`> : T extends `/${infer _Static}/${infer Next}` ? UrlParams<`/${Next}`, Result> : Result;
10
+ type Unwrap<Result extends UrlParams<any, void>> = { [Key in keyof Result]: Result[Key] };
11
+ type ParseUrlParams<T extends string> = Unwrap<UrlParams<T>>;
12
+ type Builder<T> = [T] extends [void] ? (params?: T) => string : (params: T) => string;
13
+ type Parser<T> = (path: string) => {
14
+ path: string;
15
+ params: T;
16
+ } | null;
17
+ //#endregion
18
+ //#region lib/compile.d.ts
19
+ declare function compile<T extends string, Params = ParseUrlParams<T>>(path: T): {
20
+ parse: Parser<Params>;
21
+ build: Builder<Params>;
22
+ };
23
+ //#endregion
24
+ //#region lib/convert-path.d.ts
25
+ type CompatibilityMode = "express";
26
+ declare function convertPath(path: string, mode: CompatibilityMode): string;
27
+ //#endregion
28
+ //#region lib/validate-path.d.ts
29
+ type SplitPath<S> = string extends S ? string[] : S extends `${infer Head}/${infer Tail}` ? Head extends "" ? SplitPath<Tail> : Tail extends "" ? [Head] : [Head, ...SplitPath<Tail>] : [S];
30
+ type JoinPath<T extends any[]> = `/${Join<T>}`;
31
+ type Join<T extends any[]> = T["length"] extends 0 ? never : T extends [infer F, ...infer Rest] ? Join<Rest> extends infer Tail ? [Tail] extends [never] ? `${F & string}` : `${F & string}/${Tail & string}` : never : never;
32
+ type ValidateRange<Range> = Range extends `${infer L},${infer R}` ? L extends `${number}` ? R extends `${number}` ? ["valid", `{${Range}`] : ["invalid", `{${L},number}`] : R extends `${number}` ? ["invalid", `{number,${R}}`] : ["invalid", `{number,number}`] : ["invalid", `{number,number}`];
33
+ type ValidateTypes<GenTypes> = GenTypes extends "number" ? "valid" : GenTypes extends "" ? ["invalid", "<number,union>"] : GenTypes extends `${string}|${string}` | string ? "valid" : ["invalid", "<number,union>"];
34
+ type ValidateTokenBase<Token extends string, PostFix extends string = ""> = Token extends `${infer Param}<${infer Types}>{${infer Range}}` ? ValidateTypes<Types> extends ["invalid", infer TypesReplacer] ? ["invalid", `:${Param}${TypesReplacer & string}{${Range}}${PostFix}`] : ValidateRange<Range> extends ["invalid", infer RangeReplacer] ? ["invalid", `:${Param}<${Types}>${RangeReplacer & string}${PostFix}`] : "valid" : Token extends `${infer Param}<${infer Types}>` ? ValidateTypes<Types> extends ["invalid", infer TypesReplacer] ? ["invalid", `:${Param}${TypesReplacer & string}${PostFix}`] : "valid" : Token extends `${infer Param}{${infer Range}}` ? ValidateRange<Range> extends ["invalid", infer RangeReplacer] ? ["invalid", `:${Param}${RangeReplacer & string}${PostFix}`] : "valid" : "valid";
35
+ type ValidateToken<Token> = Token extends `:${infer RawParam}` ? RawParam extends `${infer WithoutModifier}${"*" | "?" | "+"}` ? RawParam extends `${WithoutModifier}${infer Modifier}` ? ValidateTokenBase<WithoutModifier, Modifier> : never : ValidateTokenBase<RawParam> : "valid";
36
+ type ValidateTokens<Path, Current, Res extends string[] = []> = Current extends [infer Token, ...infer Rest] ? ValidateToken<ReplaceAll<Token, " ", "">> extends ["invalid", infer TokenReplacer] ? ["invalid", JoinPath<[...Res, TokenReplacer, ...Rest]>] : ValidateTokens<Path, Rest, [...Res, Token & string]> : Path;
37
+ type ValidatePath<Path> = ValidateTokens<Path, SplitPath<Path>>;
38
+ //#endregion
39
+ export { type Builder, type ParseUrlParams, type Parser, type ValidatePath, compile, convertPath };
40
+ //# sourceMappingURL=index.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../lib/types.ts","../lib/compile.ts","../lib/convert-path.ts","../lib/validate-path.ts"],"mappings":"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"}
package/dist/index.mjs ADDED
@@ -0,0 +1,179 @@
1
+ //#region lib/get-token-parameters.ts
2
+ function getTokenParameters(match) {
3
+ if (!match) return null;
4
+ const [, name, rawGenericProps, rawArrayProps, modifier] = match;
5
+ return {
6
+ name,
7
+ genericProps: rawGenericProps?.slice(1, -1).replaceAll(" ", ""),
8
+ arrayProps: rawArrayProps?.slice(1, -1).split(",").map((value) => Number(value)),
9
+ modifier
10
+ };
11
+ }
12
+ //#endregion
13
+ //#region lib/prepare-builder.ts
14
+ function prepareBuilder(tokens) {
15
+ return ((params = void 0) => {
16
+ const result = [];
17
+ if (tokens.length === 0) return "/";
18
+ for (const token of tokens) {
19
+ if (token.type === "const") {
20
+ result.push(token.name);
21
+ continue;
22
+ }
23
+ const value = params?.[token.name];
24
+ if (value == null) continue;
25
+ if (Array.isArray(value)) for (const item of value) result.push(String(item));
26
+ else result.push(String(value));
27
+ }
28
+ return `/${result.join("/")}`;
29
+ });
30
+ }
31
+ //#endregion
32
+ //#region lib/prepare-parser.ts
33
+ function prepareParser(tokens) {
34
+ return (input) => {
35
+ const rawTokens = input.split("/").map((part) => part.trim()).filter((part) => part !== "");
36
+ let params = null;
37
+ function setKey(key, value) {
38
+ params ??= {};
39
+ params[key] = value;
40
+ }
41
+ if (tokens.length === 0) return rawTokens.length === 0 ? {
42
+ path: input,
43
+ params: null
44
+ } : null;
45
+ for (let i = 0; i < tokens.length; i += 1) {
46
+ const token = tokens[i];
47
+ if (!token) return null;
48
+ if (token.type === "const") {
49
+ if (token.name !== rawTokens.shift()) return null;
50
+ continue;
51
+ }
52
+ const { arrayProps, genericProps, required } = token.payload;
53
+ if (arrayProps) {
54
+ const array = [];
55
+ let rawToken;
56
+ while (true) {
57
+ rawToken = rawTokens.shift();
58
+ if (!rawToken) break;
59
+ const parsed = parseToken(rawToken, genericProps);
60
+ if (!parsed.success) return null;
61
+ array.push(parsed.value);
62
+ if (array.length >= (arrayProps.max ?? Infinity)) break;
63
+ }
64
+ if (array.length < (arrayProps.min ?? 0)) return null;
65
+ if (rawTokens.length > 0 && !tokens[i + 1]) return null;
66
+ setKey(token.name, array);
67
+ continue;
68
+ }
69
+ const rawToken = rawTokens.shift();
70
+ if (required && !rawToken) return null;
71
+ if (!rawToken) {
72
+ setKey(token.name, void 0);
73
+ continue;
74
+ }
75
+ const parsed = parseToken(rawToken, genericProps);
76
+ if (!parsed.success) return null;
77
+ setKey(token.name, parsed.value);
78
+ }
79
+ if (rawTokens.length > 0) return null;
80
+ return {
81
+ path: input,
82
+ params
83
+ };
84
+ };
85
+ }
86
+ function parseToken(rawToken, genericProps) {
87
+ if (genericProps?.type === "number") {
88
+ if (Number.isNaN(Number(rawToken))) return { success: false };
89
+ return {
90
+ success: true,
91
+ value: Number(rawToken)
92
+ };
93
+ }
94
+ if (genericProps?.type === "union") {
95
+ if (!genericProps.items.includes(rawToken)) return { success: false };
96
+ return {
97
+ success: true,
98
+ value: rawToken
99
+ };
100
+ }
101
+ return {
102
+ success: true,
103
+ value: rawToken
104
+ };
105
+ }
106
+ //#endregion
107
+ //#region lib/compile.ts
108
+ function compile(path) {
109
+ const tokens = [];
110
+ const regexp = /:(\w+)(<[\s?\w|]+>)?({\d+,\d+})?([+*?])?/;
111
+ const parsedTokens = path.split("/").filter(Boolean);
112
+ for (const parsedToken of parsedTokens) {
113
+ const parameters = getTokenParameters(parsedToken.match(regexp));
114
+ if (!parameters) {
115
+ tokens.push({
116
+ type: "const",
117
+ name: parsedToken,
118
+ payload: void 0
119
+ });
120
+ continue;
121
+ }
122
+ const { arrayProps, genericProps, modifier, name } = parameters;
123
+ if (!name) throw new Error(`Invalid path: "${path}". Name for argument must be provided`);
124
+ const token = {
125
+ type: "parameter",
126
+ name,
127
+ payload: { required: true }
128
+ };
129
+ if (genericProps === "number") token.payload.genericProps = { type: "number" };
130
+ if (genericProps?.includes("|")) token.payload.genericProps = {
131
+ type: "union",
132
+ items: genericProps.split("|")
133
+ };
134
+ switch (modifier) {
135
+ case "*":
136
+ token.payload.arrayProps = {};
137
+ break;
138
+ case "+":
139
+ token.payload.arrayProps = { min: 1 };
140
+ break;
141
+ case "?":
142
+ token.payload.required = false;
143
+ break;
144
+ }
145
+ if (arrayProps) token.payload.arrayProps = {
146
+ ...token.payload.arrayProps,
147
+ min: arrayProps[0],
148
+ max: arrayProps[1]
149
+ };
150
+ tokens.push(token);
151
+ }
152
+ return {
153
+ parse: prepareParser(tokens),
154
+ build: prepareBuilder(tokens)
155
+ };
156
+ }
157
+ //#endregion
158
+ //#region lib/convert-path.ts
159
+ const cases = { express: [
160
+ [/:id<.+>/g, ":id"],
161
+ [/:id\+/g, "*id"],
162
+ [/:id\*/g, "*id"],
163
+ [/:id\{.+\}/g, "*id"],
164
+ [/([a-zA-Z0-9:/_.]+)\/([*:])id\?/g, "$1{/$2id}"],
165
+ [/([*:])id\?/g, "{$1id}"]
166
+ ] };
167
+ function convertPath(path, mode) {
168
+ switch (mode) {
169
+ case "express": {
170
+ let nextPath = path;
171
+ for (const [regex, replacement] of cases.express) if (nextPath.match(regex)) nextPath = nextPath.replace(regex, replacement);
172
+ return nextPath;
173
+ }
174
+ }
175
+ }
176
+ //#endregion
177
+ export { compile, convertPath };
178
+
179
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../lib/get-token-parameters.ts","../lib/prepare-builder.ts","../lib/prepare-parser.ts","../lib/compile.ts","../lib/convert-path.ts"],"sourcesContent":["interface TokenParameters {\n name: string | undefined;\n genericProps: string | undefined;\n arrayProps: [number, number] | undefined;\n modifier: string | undefined;\n}\n\nexport function getTokenParameters(match: RegExpMatchArray | null): TokenParameters | null {\n if (!match) {\n return null;\n }\n\n const [, name, rawGenericProps, rawArrayProps, modifier] = match;\n const genericProps = rawGenericProps?.slice(1, -1).replaceAll(\" \", \"\");\n const arrayProps = rawArrayProps\n ?.slice(1, -1)\n .split(\",\")\n .map((value) => Number(value)) as [number, number] | undefined;\n\n return {\n name,\n genericProps,\n arrayProps,\n modifier\n };\n}\n","import type { Builder, Token } from \"./types\";\n\nexport function prepareBuilder<T>(tokens: Token[]): Builder<T> {\n return ((params: any = undefined) => {\n const result: string[] = [];\n\n if (tokens.length === 0) {\n return \"/\";\n }\n\n for (const token of tokens) {\n if (token.type === \"const\") {\n result.push(token.name);\n continue;\n }\n\n const value = params?.[token.name];\n\n if (value == null) {\n continue;\n }\n\n if (Array.isArray(value)) {\n for (const item of value) {\n result.push(String(item));\n }\n } else {\n result.push(String(value));\n }\n }\n\n return `/${result.join(\"/\")}`;\n }) as Builder<T>;\n}\n","import type { Parser, Token } from \"./types\";\n\nexport function prepareParser<T>(tokens: Token[]): Parser<T> {\n return (input) => {\n const rawTokens = input\n .split(\"/\")\n .map((part) => part.trim())\n .filter((part) => part !== \"\");\n\n let params: Record<string, unknown> | null = null;\n\n function setKey(key: string, value: unknown) {\n params ??= {};\n params[key] = value;\n }\n\n if (tokens.length === 0) {\n return rawTokens.length === 0 ? { path: input, params: null as T } : null;\n }\n\n for (let i = 0; i < tokens.length; i += 1) {\n const token = tokens[i];\n\n if (!token) {\n return null;\n }\n\n if (token.type === \"const\") {\n if (token.name !== rawTokens.shift()) {\n return null;\n }\n\n continue;\n }\n\n const { arrayProps, genericProps, required } = token.payload;\n\n if (arrayProps) {\n const array: unknown[] = [];\n let rawToken: string | undefined;\n\n while (true) {\n rawToken = rawTokens.shift();\n\n if (!rawToken) {\n break;\n }\n\n const parsed = parseToken(rawToken, genericProps);\n\n if (!parsed.success) {\n return null;\n }\n\n array.push(parsed.value);\n\n if (array.length >= (arrayProps.max ?? Infinity)) {\n break;\n }\n }\n\n if (array.length < (arrayProps.min ?? 0)) {\n return null;\n }\n\n if (rawTokens.length > 0 && !tokens[i + 1]) {\n return null;\n }\n\n setKey(token.name, array);\n continue;\n }\n\n const rawToken = rawTokens.shift();\n\n if (required && !rawToken) {\n return null;\n }\n\n if (!rawToken) {\n setKey(token.name, undefined);\n continue;\n }\n\n const parsed = parseToken(rawToken, genericProps);\n\n if (!parsed.success) {\n return null;\n }\n\n setKey(token.name, parsed.value);\n }\n\n if (rawTokens.length > 0) {\n return null;\n }\n\n return { path: input, params: params as T };\n };\n}\n\nfunction parseToken(\n rawToken: string,\n genericProps: { type: \"union\"; items: string[] } | { type: \"number\" } | undefined,\n): { success: true; value: unknown } | { success: false } {\n if (genericProps?.type === \"number\") {\n if (Number.isNaN(Number(rawToken))) {\n return { success: false };\n }\n\n return { success: true, value: Number(rawToken) };\n }\n\n if (genericProps?.type === \"union\") {\n if (!genericProps.items.includes(rawToken)) {\n return { success: false };\n }\n\n return { success: true, value: rawToken };\n }\n\n return { success: true, value: rawToken };\n}\n","import { getTokenParameters } from \"./get-token-parameters\";\nimport { prepareBuilder } from \"./prepare-builder\";\nimport { prepareParser } from \"./prepare-parser\";\nimport type { ParameterToken, ParseUrlParams, Token } from \"./types\";\n\nexport function compile<T extends string, Params = ParseUrlParams<T>>(path: T) {\n const tokens: Token[] = [];\n const regexp = /:(\\w+)(<[\\s?\\w|]+>)?({\\d+,\\d+})?([+*?])?/;\n const parsedTokens = path.split(\"/\").filter(Boolean);\n\n for (const parsedToken of parsedTokens) {\n const parameters = getTokenParameters(parsedToken.match(regexp));\n\n if (!parameters) {\n tokens.push({ type: \"const\", name: parsedToken, payload: undefined });\n continue;\n }\n\n const { arrayProps, genericProps, modifier, name } = parameters;\n\n if (!name) {\n throw new Error(`Invalid path: \"${path}\". Name for argument must be provided`);\n }\n\n const token: ParameterToken = {\n type: \"parameter\",\n name,\n payload: {\n required: true\n }\n };\n\n if (genericProps === \"number\") {\n token.payload.genericProps = { type: \"number\" };\n }\n\n if (genericProps?.includes(\"|\")) {\n token.payload.genericProps = {\n type: \"union\",\n items: genericProps.split(\"|\")\n };\n }\n\n switch (modifier) {\n case \"*\": {\n token.payload.arrayProps = {};\n break;\n }\n case \"+\": {\n token.payload.arrayProps = { min: 1 };\n break;\n }\n case \"?\": {\n token.payload.required = false;\n break;\n }\n }\n\n if (arrayProps) {\n token.payload.arrayProps = {\n ...token.payload.arrayProps,\n min: arrayProps[0],\n max: arrayProps[1]\n };\n }\n\n tokens.push(token);\n }\n\n return {\n parse: prepareParser<Params>(tokens),\n build: prepareBuilder<Params>(tokens)\n };\n}\n","type CompatibilityMode = \"express\";\n\nconst cases = {\n express: [\n [/:id<.+>/g, \":id\"],\n [/:id\\+/g, \"*id\"],\n [/:id\\*/g, \"*id\"],\n [/:id\\{.+\\}/g, \"*id\"],\n [/([a-zA-Z0-9:/_.]+)\\/([*:])id\\?/g, \"$1{/$2id}\"],\n [/([*:])id\\?/g, \"{$1id}\"]\n ]\n} as const;\n\nexport function convertPath(path: string, mode: CompatibilityMode): string {\n switch (mode) {\n case \"express\": {\n let nextPath = path;\n\n for (const [regex, replacement] of cases.express) {\n if (nextPath.match(regex)) {\n nextPath = nextPath.replace(regex, replacement);\n }\n }\n\n return nextPath;\n }\n }\n}\n"],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,35 @@
1
+ {
2
+ "name": "@virentia/router-paths",
3
+ "version": "0.1.0",
4
+ "description": "Typed route path compiler for Virentia Router",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/virentia-labs/router.git",
10
+ "directory": "packages/paths"
11
+ },
12
+ "main": "./dist/index.cjs",
13
+ "module": "./dist/index.mjs",
14
+ "types": "./dist/index.d.mts",
15
+ "exports": {
16
+ ".": {
17
+ "types": "./dist/index.d.mts",
18
+ "import": "./dist/index.mjs",
19
+ "require": "./dist/index.cjs"
20
+ }
21
+ },
22
+ "files": [
23
+ "dist"
24
+ ],
25
+ "publishConfig": {
26
+ "access": "public"
27
+ },
28
+ "devDependencies": {
29
+ "vitest": "^4.0.18"
30
+ },
31
+ "scripts": {
32
+ "build": "tsdown",
33
+ "test": "vitest run"
34
+ }
35
+ }