@devp0nt/route0 1.0.0-next.1

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,7 @@
1
+ Copyright (c) 2025 Sergei Dmitriev, https://p0nt.dev
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
+
5
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6
+
7
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,99 @@
1
+ declare class Route0<TPathOriginalDefinition extends string, TPathDefinition extends Route0._PathDefinition<TPathOriginalDefinition>, TParamsDefinition extends Route0._ParamsDefinition<TPathOriginalDefinition>, TQueryDefinition extends Route0._QueryDefinition<TPathOriginalDefinition>> {
2
+ pathOriginalDefinition: TPathOriginalDefinition;
3
+ private pathDefinition;
4
+ paramsDefinition: TParamsDefinition;
5
+ queryDefinition: TQueryDefinition;
6
+ baseUrl: string;
7
+ private constructor();
8
+ static create<TPathOriginalDefinition extends string, TPathDefinition extends Route0._PathDefinition<TPathOriginalDefinition>, TParamsDefinition extends Route0._ParamsDefinition<TPathOriginalDefinition>, TQueryDefinition extends Route0._QueryDefinition<TPathOriginalDefinition>>(definition: TPathOriginalDefinition, config?: Route0.RouteConfigInput): Route0.Callable<Route0<TPathOriginalDefinition, TPathDefinition, TParamsDefinition, TQueryDefinition>>;
9
+ private static _splitPathDefinitionAndQueryTailDefinition;
10
+ private static _getAbsPath;
11
+ private static _getPathDefinitionByOriginalDefinition;
12
+ private static _getParamsDefinitionByRouteDefinition;
13
+ private static _getQueryDefinitionByRouteDefinition;
14
+ static overrideMany<T extends Record<string, Route0<any, any, any, any>>>(routes: T, config: Route0.RouteConfigInput): T;
15
+ extend<TSuffixDefinition extends string>(suffixDefinition: TSuffixDefinition): Route0.Callable<Route0<Route0._RoutePathOriginalDefinitionExtended<TPathOriginalDefinition, TSuffixDefinition>, Route0._PathDefinition<Route0._RoutePathOriginalDefinitionExtended<TPathOriginalDefinition, TSuffixDefinition>>, Route0._ParamsDefinition<Route0._RoutePathOriginalDefinitionExtended<TPathOriginalDefinition, TSuffixDefinition>>, Route0._QueryDefinition<Route0._RoutePathOriginalDefinitionExtended<TPathOriginalDefinition, TSuffixDefinition>>>>;
16
+ get(input: Route0._OnlyIfHasParams<TParamsDefinition, Route0._WithParamsInput<TParamsDefinition, {
17
+ query?: undefined;
18
+ abs?: false;
19
+ }>>): Route0._OnlyIfHasParams<TParamsDefinition, Route0._PathOnlyRouteValue<TPathOriginalDefinition>>;
20
+ get(input: Route0._OnlyIfHasParams<TParamsDefinition, Route0._WithParamsInput<TParamsDefinition, {
21
+ query: Route0._QueryInput<TQueryDefinition>;
22
+ abs?: false;
23
+ }>>): Route0._OnlyIfHasParams<TParamsDefinition, Route0._WithQueryRouteValue<TPathOriginalDefinition>>;
24
+ get(input: Route0._OnlyIfHasParams<TParamsDefinition, Route0._WithParamsInput<TParamsDefinition, {
25
+ query?: undefined;
26
+ abs: true;
27
+ }>>): Route0._OnlyIfHasParams<TParamsDefinition, Route0._AbsolutePathOnlyRouteValue<TPathOriginalDefinition>>;
28
+ get(input: Route0._OnlyIfHasParams<TParamsDefinition, Route0._WithParamsInput<TParamsDefinition, {
29
+ query: Route0._QueryInput<TQueryDefinition>;
30
+ abs: true;
31
+ }>>): Route0._OnlyIfHasParams<TParamsDefinition, Route0._AbsoluteWithQueryRouteValue<TPathOriginalDefinition>>;
32
+ get(...args: Route0._OnlyIfNoParams<TParamsDefinition, [], [never]>): Route0._PathOnlyRouteValue<TPathOriginalDefinition>;
33
+ get(input: Route0._OnlyIfNoParams<TParamsDefinition, {
34
+ query?: undefined;
35
+ abs?: false;
36
+ }>): Route0._OnlyIfNoParams<TParamsDefinition, Route0._PathOnlyRouteValue<TPathOriginalDefinition>>;
37
+ get(input: Route0._OnlyIfNoParams<TParamsDefinition, {
38
+ query: Route0._QueryInput<TQueryDefinition>;
39
+ abs?: false;
40
+ }>): Route0._OnlyIfNoParams<TParamsDefinition, Route0._WithQueryRouteValue<TPathOriginalDefinition>>;
41
+ get(input: Route0._OnlyIfNoParams<TParamsDefinition, {
42
+ query?: undefined;
43
+ abs: true;
44
+ }>): Route0._OnlyIfNoParams<TParamsDefinition, Route0._AbsolutePathOnlyRouteValue<TPathOriginalDefinition>>;
45
+ get(input: Route0._OnlyIfNoParams<TParamsDefinition, {
46
+ query: Route0._QueryInput<TQueryDefinition>;
47
+ abs: true;
48
+ }>): Route0._OnlyIfNoParams<TParamsDefinition, Route0._AbsoluteWithQueryRouteValue<TPathOriginalDefinition>>;
49
+ getDefinition(): TPathDefinition;
50
+ clone(config?: Route0.RouteConfigInput): Route0<TPathOriginalDefinition, Route0._TrimQueryTailDefinition<TPathOriginalDefinition>, Route0._ParamsDefinition<TPathOriginalDefinition>, Route0._QueryDefinition<TPathOriginalDefinition>>;
51
+ }
52
+ declare namespace Route0 {
53
+ type Callable<T extends Route0<any, any, any, any>> = T & T['get'];
54
+ type RouteConfigInput = {
55
+ baseUrl?: string;
56
+ };
57
+ type Params<TRoute0 extends Route0<any, any, any, any>> = {
58
+ [K in keyof TRoute0['paramsDefinition']]: string;
59
+ };
60
+ type Query<TRoute0 extends Route0<any, any, any, any>> = Partial<{
61
+ [K in keyof TRoute0['queryDefinition']]: string | undefined;
62
+ } & Record<string, string | undefined>>;
63
+ type _TrimQueryTailDefinition<S extends string> = S extends `${infer P}&${string}` ? P : S;
64
+ type _QueryTailDefinitionWithoutFirstAmp<S extends string> = S extends `${string}&${infer T}` ? T : '';
65
+ type _QueryTailDefinitionWithFirstAmp<S extends string> = S extends `${string}&${infer T}` ? `&${T}` : '';
66
+ type _AmpSplit<S extends string> = S extends `${infer A}&${infer B}` ? A | _AmpSplit<B> : S;
67
+ type _NonEmpty<T> = [T] extends ['' | never] ? never : T;
68
+ type _ExtractPathParams<S extends string> = S extends `${string}:${infer After}` ? After extends `${infer Name}/${infer Rest}` ? Name | _ExtractPathParams<`/${Rest}`> : After : never;
69
+ type _ReplacePathParams<S extends string> = S extends `${infer Head}:${infer Tail}` ? Tail extends `${infer _Param}/${infer Rest}` ? _ReplacePathParams<`${Head}${string}/${Rest}`> : `${Head}${string}` : S;
70
+ type _DedupeSlashes<S extends string> = S extends `${infer A}//${infer B}` ? _DedupeSlashes<`${A}/${B}`> : S;
71
+ type _EmptyRecord = Record<never, never>;
72
+ type _JoinPath<Parent extends string, Suffix extends string> = _DedupeSlashes<Route0._PathDefinition<Parent> extends infer A extends string ? _PathDefinition<Suffix> extends infer B extends string ? A extends '' ? B extends '' ? '' : B extends `/${string}` ? B : `/${B}` : B extends '' ? A : A extends `${string}/` ? `${A}${B}` : B extends `/${string}` ? `${A}${B}` : `${A}/${B}` : never : never>;
73
+ type _OnlyIfNoParams<TParams extends object, Yes, No = never> = keyof TParams extends never ? Yes : No;
74
+ type _OnlyIfHasParams<TParams extends object, Yes, No = never> = keyof TParams extends never ? No : Yes;
75
+ type _PathDefinition<TPathOriginalDefinition extends string> = _TrimQueryTailDefinition<TPathOriginalDefinition>;
76
+ type _ParamsDefinition<TPathOriginalDefinition extends string> = _ExtractPathParams<_PathDefinition<TPathOriginalDefinition>> extends infer U ? [U] extends [never] ? _EmptyRecord : {
77
+ [K in Extract<U, string>]: true;
78
+ } : _EmptyRecord;
79
+ type _QueryDefinition<TPathOriginalDefinition extends string> = _NonEmpty<_QueryTailDefinitionWithoutFirstAmp<TPathOriginalDefinition>> extends infer Tail extends string ? _AmpSplit<Tail> extends infer U ? [U] extends [never] ? _EmptyRecord : {
80
+ [K in Extract<U, string>]: true;
81
+ } : _EmptyRecord : _EmptyRecord;
82
+ type _RoutePathOriginalDefinitionExtended<TSourcePathOriginalDefinition extends string, TSuffixPathOriginalDefinition extends string> = `${_JoinPath<TSourcePathOriginalDefinition, TSuffixPathOriginalDefinition>}${_QueryTailDefinitionWithFirstAmp<TSuffixPathOriginalDefinition>}`;
83
+ type _ParamsInput<TParamsDefinition extends object> = {
84
+ [K in keyof TParamsDefinition]: string | number;
85
+ };
86
+ type _QueryInput<TQueryDefinition extends object> = Partial<{
87
+ [K in keyof TQueryDefinition]: string | number;
88
+ }> & Record<string, string | number>;
89
+ type _WithParamsInput<TParamsDefinition extends object, T extends {
90
+ query?: _QueryInput<any>;
91
+ abs?: boolean;
92
+ }> = _ParamsInput<TParamsDefinition> & T;
93
+ type _PathOnlyRouteValue<TPathOriginalDefinition extends string> = `${_ReplacePathParams<_PathDefinition<TPathOriginalDefinition>>}`;
94
+ type _WithQueryRouteValue<TPathOriginalDefinition extends string> = `${_ReplacePathParams<_PathDefinition<TPathOriginalDefinition>>}?${string}`;
95
+ type _AbsolutePathOnlyRouteValue<TPathOriginalDefinition extends string> = `${string}${_PathOnlyRouteValue<TPathOriginalDefinition>}`;
96
+ type _AbsoluteWithQueryRouteValue<TPathOriginalDefinition extends string> = `${string}${_WithQueryRouteValue<TPathOriginalDefinition>}`;
97
+ }
98
+
99
+ export { Route0 };
@@ -0,0 +1,158 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+ var index_exports = {};
20
+ __export(index_exports, {
21
+ Route0: () => Route0
22
+ });
23
+ module.exports = __toCommonJS(index_exports);
24
+ class Route0 {
25
+ pathOriginalDefinition;
26
+ pathDefinition;
27
+ paramsDefinition;
28
+ queryDefinition;
29
+ baseUrl;
30
+ constructor(definition, config = {}) {
31
+ this.pathOriginalDefinition = definition;
32
+ this.pathDefinition = Route0._getPathDefinitionByOriginalDefinition(definition);
33
+ this.paramsDefinition = Route0._getParamsDefinitionByRouteDefinition(definition);
34
+ this.queryDefinition = Route0._getQueryDefinitionByRouteDefinition(definition);
35
+ const { baseUrl } = config;
36
+ if (baseUrl && typeof baseUrl === "string" && baseUrl.length) {
37
+ this.baseUrl = baseUrl;
38
+ } else {
39
+ const g = globalThis;
40
+ if (g?.location?.origin) {
41
+ this.baseUrl = g.location.origin;
42
+ } else {
43
+ this.baseUrl = "https://example.com";
44
+ }
45
+ }
46
+ }
47
+ static create(definition, config) {
48
+ const original = new Route0(
49
+ definition,
50
+ config
51
+ );
52
+ const callable = original.get.bind(original);
53
+ const proxy = new Proxy(callable, {
54
+ get(_target, prop, receiver) {
55
+ const value = original[prop];
56
+ if (typeof value === "function") {
57
+ return value.bind(original);
58
+ }
59
+ return value;
60
+ },
61
+ set(_target, prop, value, receiver) {
62
+ ;
63
+ original[prop] = value;
64
+ return true;
65
+ },
66
+ has(_target, prop) {
67
+ return prop in original;
68
+ }
69
+ });
70
+ Object.setPrototypeOf(proxy, Route0.prototype);
71
+ return proxy;
72
+ }
73
+ static _splitPathDefinitionAndQueryTailDefinition(pathOriginalDefinition) {
74
+ const i = pathOriginalDefinition.indexOf("&");
75
+ if (i === -1) return { pathDefinition: pathOriginalDefinition, queryTailDefinition: "" };
76
+ return {
77
+ pathDefinition: pathOriginalDefinition.slice(0, i),
78
+ queryTailDefinition: pathOriginalDefinition.slice(i)
79
+ };
80
+ }
81
+ static _getAbsPath(baseUrl, pathWithQuery) {
82
+ return new URL(pathWithQuery, baseUrl).toString().replace(/\/$/, "");
83
+ }
84
+ static _getPathDefinitionByOriginalDefinition(pathOriginalDefinition) {
85
+ const { pathDefinition } = Route0._splitPathDefinitionAndQueryTailDefinition(pathOriginalDefinition);
86
+ return pathDefinition;
87
+ }
88
+ static _getParamsDefinitionByRouteDefinition(pathOriginalDefinition) {
89
+ const { pathDefinition } = Route0._splitPathDefinitionAndQueryTailDefinition(pathOriginalDefinition);
90
+ const matches = Array.from(pathDefinition.matchAll(/:([A-Za-z0-9_]+)/g));
91
+ const paramsDefinition = Object.fromEntries(matches.map((m) => [m[1], true]));
92
+ return paramsDefinition;
93
+ }
94
+ static _getQueryDefinitionByRouteDefinition(pathOriginalDefinition) {
95
+ const { queryTailDefinition } = Route0._splitPathDefinitionAndQueryTailDefinition(pathOriginalDefinition);
96
+ if (!queryTailDefinition) {
97
+ return {};
98
+ }
99
+ const keys = queryTailDefinition.split("&").map(Boolean);
100
+ const queryDefinition = Object.fromEntries(keys.map((k) => [k, true]));
101
+ return queryDefinition;
102
+ }
103
+ static overrideMany(routes, config) {
104
+ const result = {};
105
+ for (const [key, value] of Object.entries(routes)) {
106
+ ;
107
+ result[key] = value.clone(config);
108
+ }
109
+ return result;
110
+ }
111
+ extend(suffixDefinition) {
112
+ const { pathDefinition: parentPathDefinition } = Route0._splitPathDefinitionAndQueryTailDefinition(
113
+ this.pathOriginalDefinition
114
+ );
115
+ const { pathDefinition: suffixPathDefinition, queryTailDefinition: suffixQueryTailDefinition } = Route0._splitPathDefinitionAndQueryTailDefinition(suffixDefinition);
116
+ const pathDefinition = `${parentPathDefinition}/${suffixPathDefinition}`.replace(/\/{2,}/g, "/");
117
+ const pathOriginalDefinition = `${pathDefinition}${suffixQueryTailDefinition}`;
118
+ return Route0.create(pathOriginalDefinition, { baseUrl: this.baseUrl });
119
+ }
120
+ // implementation
121
+ get(...args) {
122
+ const { queryInput, paramsInput, absInput } = (() => {
123
+ if (args.length === 0) {
124
+ return { queryInput: {}, paramsInput: {}, absInput: false };
125
+ }
126
+ const input = args[0];
127
+ if (typeof input !== "object" || input === null) {
128
+ return { queryInput: {}, paramsInput: {}, absInput: false };
129
+ }
130
+ const { query, abs, ...params } = input;
131
+ return { queryInput: query || {}, paramsInput: params, absInput: abs ?? false };
132
+ })();
133
+ const neededParamsKeys = Object.keys(this.paramsDefinition);
134
+ const providedParamsKeys = Object.keys(paramsInput);
135
+ const notProvidedKeys = neededParamsKeys.filter((k) => !providedParamsKeys.includes(k));
136
+ if (notProvidedKeys.length) {
137
+ Object.assign(paramsInput, Object.fromEntries(notProvidedKeys.map((k) => [k, "undefined"])));
138
+ }
139
+ let url = String(this.pathDefinition);
140
+ url = url.replace(/:([A-Za-z0-9_]+)/g, (_m, k) => encodeURIComponent(String(paramsInput?.[k] ?? "")));
141
+ const queryInputStringified = Object.fromEntries(Object.entries(queryInput).map(([k, v]) => [k, String(v)]));
142
+ url = [url, new URLSearchParams(queryInputStringified).toString()].filter(Boolean).join("?");
143
+ url = url.replace(/\/{2,}/g, "/");
144
+ url = absInput ? Route0._getAbsPath(this.baseUrl, url) : url;
145
+ return url;
146
+ }
147
+ getDefinition() {
148
+ return this.pathDefinition;
149
+ }
150
+ clone(config) {
151
+ return new Route0(this.pathOriginalDefinition, config);
152
+ }
153
+ }
154
+ // Annotate the CommonJS export names for ESM import in node:
155
+ 0 && (module.exports = {
156
+ Route0
157
+ });
158
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/index.ts"],"sourcesContent":["// TODO: use splats in param definition \"*\"\n// TODO: ? check extend for query only .extend('&x&z')\n// TODO: .create(route, {useQuery, useParams})\n// TODO: Из пас экзакт, из пасвизквери экзает, из чилдрен, из парент, из экзактОр\n// TODO: isEqual, isChildren, isParent\n// TODO: extractParams, extractQuery\n// TODO: getPathDefinition respecting definitionParamPrefix, definitionQueryPrefix\n// TODO: prepend\n// TODO: Route0.createTree({base:{self: x, children: ...})\n// TODO: overrideTree\n// TODO: .create(route, {baseUrl, useLocation})\n// TODO: ? optional path params as @\n// TODO: prependMany, extendMany, overrideMany, with types\n\nexport class Route0<\n TPathOriginalDefinition extends string,\n TPathDefinition extends Route0._PathDefinition<TPathOriginalDefinition>,\n TParamsDefinition extends Route0._ParamsDefinition<TPathOriginalDefinition>,\n TQueryDefinition extends Route0._QueryDefinition<TPathOriginalDefinition>,\n> {\n pathOriginalDefinition: TPathOriginalDefinition\n private pathDefinition: TPathDefinition\n paramsDefinition: TParamsDefinition\n queryDefinition: TQueryDefinition\n baseUrl: string\n\n private constructor(definition: TPathOriginalDefinition, config: Route0.RouteConfigInput = {}) {\n this.pathOriginalDefinition = definition as TPathOriginalDefinition\n this.pathDefinition = Route0._getPathDefinitionByOriginalDefinition(definition) as TPathDefinition\n this.paramsDefinition = Route0._getParamsDefinitionByRouteDefinition(definition) as TParamsDefinition\n this.queryDefinition = Route0._getQueryDefinitionByRouteDefinition(definition) as TQueryDefinition\n\n const { baseUrl } = config\n if (baseUrl && typeof baseUrl === 'string' && baseUrl.length) {\n this.baseUrl = baseUrl\n } else {\n const g = globalThis as unknown as { location?: { origin?: string } }\n if (g?.location?.origin) {\n this.baseUrl = g.location.origin\n } else {\n this.baseUrl = 'https://example.com'\n }\n }\n }\n\n static create<\n TPathOriginalDefinition extends string,\n TPathDefinition extends Route0._PathDefinition<TPathOriginalDefinition>,\n TParamsDefinition extends Route0._ParamsDefinition<TPathOriginalDefinition>,\n TQueryDefinition extends Route0._QueryDefinition<TPathOriginalDefinition>,\n >(\n definition: TPathOriginalDefinition,\n config?: Route0.RouteConfigInput,\n ): Route0.Callable<Route0<TPathOriginalDefinition, TPathDefinition, TParamsDefinition, TQueryDefinition>> {\n const original = new Route0<TPathOriginalDefinition, TPathDefinition, TParamsDefinition, TQueryDefinition>(\n definition,\n config,\n )\n const callable = original.get.bind(original)\n const proxy = new Proxy(callable, {\n get(_target, prop, receiver) {\n const value = (original as any)[prop]\n if (typeof value === 'function') {\n return value.bind(original)\n }\n return value\n },\n set(_target, prop, value, receiver) {\n ;(original as any)[prop] = value\n return true\n },\n has(_target, prop) {\n return prop in original\n },\n })\n Object.setPrototypeOf(proxy, Route0.prototype)\n return proxy as never\n }\n\n private static _splitPathDefinitionAndQueryTailDefinition(pathOriginalDefinition: string) {\n const i = pathOriginalDefinition.indexOf('&')\n if (i === -1) return { pathDefinition: pathOriginalDefinition, queryTailDefinition: '' }\n return {\n pathDefinition: pathOriginalDefinition.slice(0, i),\n queryTailDefinition: pathOriginalDefinition.slice(i),\n }\n }\n\n private static _getAbsPath(baseUrl: string, pathWithQuery: string) {\n return new URL(pathWithQuery, baseUrl).toString().replace(/\\/$/, '')\n }\n\n private static _getPathDefinitionByOriginalDefinition<TPathOriginalDefinition extends string>(\n pathOriginalDefinition: TPathOriginalDefinition,\n ) {\n const { pathDefinition } = Route0._splitPathDefinitionAndQueryTailDefinition(pathOriginalDefinition)\n return pathDefinition as Route0._PathDefinition<TPathOriginalDefinition>\n }\n\n private static _getParamsDefinitionByRouteDefinition<TPathOriginalDefinition extends string>(\n pathOriginalDefinition: TPathOriginalDefinition,\n ) {\n const { pathDefinition } = Route0._splitPathDefinitionAndQueryTailDefinition(pathOriginalDefinition)\n const matches = Array.from(pathDefinition.matchAll(/:([A-Za-z0-9_]+)/g))\n const paramsDefinition = Object.fromEntries(matches.map((m) => [m[1], true]))\n return paramsDefinition as Route0._ParamsDefinition<TPathOriginalDefinition>\n }\n\n private static _getQueryDefinitionByRouteDefinition<TPathOriginalDefinition extends string>(\n pathOriginalDefinition: TPathOriginalDefinition,\n ) {\n const { queryTailDefinition } = Route0._splitPathDefinitionAndQueryTailDefinition(pathOriginalDefinition)\n if (!queryTailDefinition) {\n return {} as Route0._QueryDefinition<TPathOriginalDefinition>\n }\n const keys = queryTailDefinition.split('&').map(Boolean)\n const queryDefinition = Object.fromEntries(keys.map((k) => [k, true]))\n return queryDefinition as Route0._QueryDefinition<TPathOriginalDefinition>\n }\n\n static overrideMany<T extends Record<string, Route0<any, any, any, any>>>(\n routes: T,\n config: Route0.RouteConfigInput,\n ): T {\n const result = {} as T\n for (const [key, value] of Object.entries(routes)) {\n ;(result as any)[key] = value.clone(config)\n }\n return result\n }\n\n extend<TSuffixDefinition extends string>(\n suffixDefinition: TSuffixDefinition,\n ): Route0.Callable<\n Route0<\n Route0._RoutePathOriginalDefinitionExtended<TPathOriginalDefinition, TSuffixDefinition>,\n Route0._PathDefinition<Route0._RoutePathOriginalDefinitionExtended<TPathOriginalDefinition, TSuffixDefinition>>,\n Route0._ParamsDefinition<Route0._RoutePathOriginalDefinitionExtended<TPathOriginalDefinition, TSuffixDefinition>>,\n Route0._QueryDefinition<Route0._RoutePathOriginalDefinitionExtended<TPathOriginalDefinition, TSuffixDefinition>>\n >\n > {\n const { pathDefinition: parentPathDefinition } = Route0._splitPathDefinitionAndQueryTailDefinition(\n this.pathOriginalDefinition,\n )\n const { pathDefinition: suffixPathDefinition, queryTailDefinition: suffixQueryTailDefinition } =\n Route0._splitPathDefinitionAndQueryTailDefinition(suffixDefinition)\n const pathDefinition = `${parentPathDefinition}/${suffixPathDefinition}`.replace(/\\/{2,}/g, '/')\n const pathOriginalDefinition =\n `${pathDefinition}${suffixQueryTailDefinition}` as Route0._RoutePathOriginalDefinitionExtended<\n TPathOriginalDefinition,\n TSuffixDefinition\n >\n return Route0.create<\n Route0._RoutePathOriginalDefinitionExtended<TPathOriginalDefinition, TSuffixDefinition>,\n Route0._PathDefinition<Route0._RoutePathOriginalDefinitionExtended<TPathOriginalDefinition, TSuffixDefinition>>,\n Route0._ParamsDefinition<Route0._RoutePathOriginalDefinitionExtended<TPathOriginalDefinition, TSuffixDefinition>>,\n Route0._QueryDefinition<Route0._RoutePathOriginalDefinitionExtended<TPathOriginalDefinition, TSuffixDefinition>>\n >(pathOriginalDefinition, { baseUrl: this.baseUrl })\n }\n\n // has params\n get(\n input: Route0._OnlyIfHasParams<\n TParamsDefinition,\n Route0._WithParamsInput<TParamsDefinition, { query?: undefined; abs?: false }>\n >,\n ): Route0._OnlyIfHasParams<TParamsDefinition, Route0._PathOnlyRouteValue<TPathOriginalDefinition>>\n get(\n input: Route0._OnlyIfHasParams<\n TParamsDefinition,\n Route0._WithParamsInput<TParamsDefinition, { query: Route0._QueryInput<TQueryDefinition>; abs?: false }>\n >,\n ): Route0._OnlyIfHasParams<TParamsDefinition, Route0._WithQueryRouteValue<TPathOriginalDefinition>>\n get(\n input: Route0._OnlyIfHasParams<\n TParamsDefinition,\n Route0._WithParamsInput<TParamsDefinition, { query?: undefined; abs: true }>\n >,\n ): Route0._OnlyIfHasParams<TParamsDefinition, Route0._AbsolutePathOnlyRouteValue<TPathOriginalDefinition>>\n get(\n input: Route0._OnlyIfHasParams<\n TParamsDefinition,\n Route0._WithParamsInput<TParamsDefinition, { query: Route0._QueryInput<TQueryDefinition>; abs: true }>\n >,\n ): Route0._OnlyIfHasParams<TParamsDefinition, Route0._AbsoluteWithQueryRouteValue<TPathOriginalDefinition>>\n\n // no params\n get(\n ...args: Route0._OnlyIfNoParams<TParamsDefinition, [], [never]>\n ): Route0._PathOnlyRouteValue<TPathOriginalDefinition>\n get(\n input: Route0._OnlyIfNoParams<TParamsDefinition, { query?: undefined; abs?: false }>,\n ): Route0._OnlyIfNoParams<TParamsDefinition, Route0._PathOnlyRouteValue<TPathOriginalDefinition>>\n get(\n input: Route0._OnlyIfNoParams<TParamsDefinition, { query: Route0._QueryInput<TQueryDefinition>; abs?: false }>,\n ): Route0._OnlyIfNoParams<TParamsDefinition, Route0._WithQueryRouteValue<TPathOriginalDefinition>>\n get(\n input: Route0._OnlyIfNoParams<TParamsDefinition, { query?: undefined; abs: true }>,\n ): Route0._OnlyIfNoParams<TParamsDefinition, Route0._AbsolutePathOnlyRouteValue<TPathOriginalDefinition>>\n get(\n input: Route0._OnlyIfNoParams<TParamsDefinition, { query: Route0._QueryInput<TQueryDefinition>; abs: true }>,\n ): Route0._OnlyIfNoParams<TParamsDefinition, Route0._AbsoluteWithQueryRouteValue<TPathOriginalDefinition>>\n\n // implementation\n get(...args: any[]): string {\n const { queryInput, paramsInput, absInput } = ((): {\n queryInput: Record<string, string | number>\n paramsInput: Record<string, string | number>\n absInput: boolean\n } => {\n if (args.length === 0) {\n return { queryInput: {}, paramsInput: {}, absInput: false }\n }\n const input = args[0]\n if (typeof input !== 'object' || input === null) {\n // throw new Error(\"Invalid get route input: expected object\")\n return { queryInput: {}, paramsInput: {}, absInput: false }\n }\n const { query, abs, ...params } = input\n return { queryInput: query || {}, paramsInput: params, absInput: abs ?? false }\n })()\n\n // validate params\n const neededParamsKeys = Object.keys(this.paramsDefinition)\n const providedParamsKeys = Object.keys(paramsInput)\n const notProvidedKeys = neededParamsKeys.filter((k) => !providedParamsKeys.includes(k))\n if (notProvidedKeys.length) {\n // throw new Error(`Missing params: not defined keys ${notProvidedKeys.map((k) => `\"${k}\"`).join(\", \")}.`)\n Object.assign(paramsInput, Object.fromEntries(notProvidedKeys.map((k) => [k, 'undefined'])))\n }\n\n // create url\n let url = String(this.pathDefinition)\n // replace params\n url = url.replace(/:([A-Za-z0-9_]+)/g, (_m, k) => encodeURIComponent(String(paramsInput?.[k] ?? '')))\n // query params\n const queryInputStringified = Object.fromEntries(Object.entries(queryInput).map(([k, v]) => [k, String(v)]))\n url = [url, new URLSearchParams(queryInputStringified).toString()].filter(Boolean).join('?')\n // dedupe slashes\n url = url.replace(/\\/{2,}/g, '/')\n // absolute\n url = absInput ? Route0._getAbsPath(this.baseUrl, url) : url\n\n return url\n }\n\n getDefinition() {\n return this.pathDefinition\n }\n\n clone(config?: Route0.RouteConfigInput) {\n return new Route0(this.pathOriginalDefinition, config)\n }\n}\n\nexport namespace Route0 {\n export type Callable<T extends Route0<any, any, any, any>> = T & T['get']\n export type RouteConfigInput = {\n baseUrl?: string\n }\n export type Params<TRoute0 extends Route0<any, any, any, any>> = {\n [K in keyof TRoute0['paramsDefinition']]: string\n }\n export type Query<TRoute0 extends Route0<any, any, any, any>> = Partial<\n {\n [K in keyof TRoute0['queryDefinition']]: string | undefined\n } & Record<string, string | undefined>\n >\n\n export type _TrimQueryTailDefinition<S extends string> = S extends `${infer P}&${string}` ? P : S\n export type _QueryTailDefinitionWithoutFirstAmp<S extends string> = S extends `${string}&${infer T}` ? T : ''\n export type _QueryTailDefinitionWithFirstAmp<S extends string> = S extends `${string}&${infer T}` ? `&${T}` : ''\n export type _AmpSplit<S extends string> = S extends `${infer A}&${infer B}` ? A | _AmpSplit<B> : S\n export type _NonEmpty<T> = [T] extends ['' | never] ? never : T\n export type _ExtractPathParams<S extends string> = S extends `${string}:${infer After}`\n ? After extends `${infer Name}/${infer Rest}`\n ? Name | _ExtractPathParams<`/${Rest}`>\n : After\n : never\n export type _ReplacePathParams<S extends string> = S extends `${infer Head}:${infer Tail}`\n ? Tail extends `${infer _Param}/${infer Rest}`\n ? _ReplacePathParams<`${Head}${string}/${Rest}`>\n : `${Head}${string}`\n : S\n export type _DedupeSlashes<S extends string> = S extends `${infer A}//${infer B}` ? _DedupeSlashes<`${A}/${B}`> : S\n export type _EmptyRecord = Record<never, never>\n export type _JoinPath<Parent extends string, Suffix extends string> = _DedupeSlashes<\n Route0._PathDefinition<Parent> extends infer A extends string\n ? _PathDefinition<Suffix> extends infer B extends string\n ? A extends ''\n ? B extends ''\n ? ''\n : B extends `/${string}`\n ? B\n : `/${B}`\n : B extends ''\n ? A\n : A extends `${string}/`\n ? `${A}${B}`\n : B extends `/${string}`\n ? `${A}${B}`\n : `${A}/${B}`\n : never\n : never\n >\n\n export type _OnlyIfNoParams<TParams extends object, Yes, No = never> = keyof TParams extends never ? Yes : No\n export type _OnlyIfHasParams<TParams extends object, Yes, No = never> = keyof TParams extends never ? No : Yes\n\n export type _PathDefinition<TPathOriginalDefinition extends string> =\n _TrimQueryTailDefinition<TPathOriginalDefinition>\n export type _ParamsDefinition<TPathOriginalDefinition extends string> = _ExtractPathParams<\n _PathDefinition<TPathOriginalDefinition>\n > extends infer U\n ? [U] extends [never]\n ? _EmptyRecord\n : { [K in Extract<U, string>]: true }\n : _EmptyRecord\n export type _QueryDefinition<TPathOriginalDefinition extends string> = _NonEmpty<\n _QueryTailDefinitionWithoutFirstAmp<TPathOriginalDefinition>\n > extends infer Tail extends string\n ? _AmpSplit<Tail> extends infer U\n ? [U] extends [never]\n ? _EmptyRecord\n : { [K in Extract<U, string>]: true }\n : _EmptyRecord\n : _EmptyRecord\n export type _RoutePathOriginalDefinitionExtended<\n TSourcePathOriginalDefinition extends string,\n TSuffixPathOriginalDefinition extends string,\n > = `${_JoinPath<TSourcePathOriginalDefinition, TSuffixPathOriginalDefinition>}${_QueryTailDefinitionWithFirstAmp<TSuffixPathOriginalDefinition>}`\n\n export type _ParamsInput<TParamsDefinition extends object> = {\n [K in keyof TParamsDefinition]: string | number\n }\n export type _QueryInput<TQueryDefinition extends object> = Partial<{\n [K in keyof TQueryDefinition]: string | number\n }> &\n Record<string, string | number>\n export type _WithParamsInput<\n TParamsDefinition extends object,\n T extends {\n query?: _QueryInput<any>\n abs?: boolean\n },\n > = _ParamsInput<TParamsDefinition> & T\n\n export type _PathOnlyRouteValue<TPathOriginalDefinition extends string> =\n `${_ReplacePathParams<_PathDefinition<TPathOriginalDefinition>>}`\n export type _WithQueryRouteValue<TPathOriginalDefinition extends string> =\n `${_ReplacePathParams<_PathDefinition<TPathOriginalDefinition>>}?${string}`\n export type _AbsolutePathOnlyRouteValue<TPathOriginalDefinition extends string> =\n `${string}${_PathOnlyRouteValue<TPathOriginalDefinition>}`\n export type _AbsoluteWithQueryRouteValue<TPathOriginalDefinition extends string> =\n `${string}${_WithQueryRouteValue<TPathOriginalDefinition>}`\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,99 @@
1
+ declare class Route0<TPathOriginalDefinition extends string, TPathDefinition extends Route0._PathDefinition<TPathOriginalDefinition>, TParamsDefinition extends Route0._ParamsDefinition<TPathOriginalDefinition>, TQueryDefinition extends Route0._QueryDefinition<TPathOriginalDefinition>> {
2
+ pathOriginalDefinition: TPathOriginalDefinition;
3
+ private pathDefinition;
4
+ paramsDefinition: TParamsDefinition;
5
+ queryDefinition: TQueryDefinition;
6
+ baseUrl: string;
7
+ private constructor();
8
+ static create<TPathOriginalDefinition extends string, TPathDefinition extends Route0._PathDefinition<TPathOriginalDefinition>, TParamsDefinition extends Route0._ParamsDefinition<TPathOriginalDefinition>, TQueryDefinition extends Route0._QueryDefinition<TPathOriginalDefinition>>(definition: TPathOriginalDefinition, config?: Route0.RouteConfigInput): Route0.Callable<Route0<TPathOriginalDefinition, TPathDefinition, TParamsDefinition, TQueryDefinition>>;
9
+ private static _splitPathDefinitionAndQueryTailDefinition;
10
+ private static _getAbsPath;
11
+ private static _getPathDefinitionByOriginalDefinition;
12
+ private static _getParamsDefinitionByRouteDefinition;
13
+ private static _getQueryDefinitionByRouteDefinition;
14
+ static overrideMany<T extends Record<string, Route0<any, any, any, any>>>(routes: T, config: Route0.RouteConfigInput): T;
15
+ extend<TSuffixDefinition extends string>(suffixDefinition: TSuffixDefinition): Route0.Callable<Route0<Route0._RoutePathOriginalDefinitionExtended<TPathOriginalDefinition, TSuffixDefinition>, Route0._PathDefinition<Route0._RoutePathOriginalDefinitionExtended<TPathOriginalDefinition, TSuffixDefinition>>, Route0._ParamsDefinition<Route0._RoutePathOriginalDefinitionExtended<TPathOriginalDefinition, TSuffixDefinition>>, Route0._QueryDefinition<Route0._RoutePathOriginalDefinitionExtended<TPathOriginalDefinition, TSuffixDefinition>>>>;
16
+ get(input: Route0._OnlyIfHasParams<TParamsDefinition, Route0._WithParamsInput<TParamsDefinition, {
17
+ query?: undefined;
18
+ abs?: false;
19
+ }>>): Route0._OnlyIfHasParams<TParamsDefinition, Route0._PathOnlyRouteValue<TPathOriginalDefinition>>;
20
+ get(input: Route0._OnlyIfHasParams<TParamsDefinition, Route0._WithParamsInput<TParamsDefinition, {
21
+ query: Route0._QueryInput<TQueryDefinition>;
22
+ abs?: false;
23
+ }>>): Route0._OnlyIfHasParams<TParamsDefinition, Route0._WithQueryRouteValue<TPathOriginalDefinition>>;
24
+ get(input: Route0._OnlyIfHasParams<TParamsDefinition, Route0._WithParamsInput<TParamsDefinition, {
25
+ query?: undefined;
26
+ abs: true;
27
+ }>>): Route0._OnlyIfHasParams<TParamsDefinition, Route0._AbsolutePathOnlyRouteValue<TPathOriginalDefinition>>;
28
+ get(input: Route0._OnlyIfHasParams<TParamsDefinition, Route0._WithParamsInput<TParamsDefinition, {
29
+ query: Route0._QueryInput<TQueryDefinition>;
30
+ abs: true;
31
+ }>>): Route0._OnlyIfHasParams<TParamsDefinition, Route0._AbsoluteWithQueryRouteValue<TPathOriginalDefinition>>;
32
+ get(...args: Route0._OnlyIfNoParams<TParamsDefinition, [], [never]>): Route0._PathOnlyRouteValue<TPathOriginalDefinition>;
33
+ get(input: Route0._OnlyIfNoParams<TParamsDefinition, {
34
+ query?: undefined;
35
+ abs?: false;
36
+ }>): Route0._OnlyIfNoParams<TParamsDefinition, Route0._PathOnlyRouteValue<TPathOriginalDefinition>>;
37
+ get(input: Route0._OnlyIfNoParams<TParamsDefinition, {
38
+ query: Route0._QueryInput<TQueryDefinition>;
39
+ abs?: false;
40
+ }>): Route0._OnlyIfNoParams<TParamsDefinition, Route0._WithQueryRouteValue<TPathOriginalDefinition>>;
41
+ get(input: Route0._OnlyIfNoParams<TParamsDefinition, {
42
+ query?: undefined;
43
+ abs: true;
44
+ }>): Route0._OnlyIfNoParams<TParamsDefinition, Route0._AbsolutePathOnlyRouteValue<TPathOriginalDefinition>>;
45
+ get(input: Route0._OnlyIfNoParams<TParamsDefinition, {
46
+ query: Route0._QueryInput<TQueryDefinition>;
47
+ abs: true;
48
+ }>): Route0._OnlyIfNoParams<TParamsDefinition, Route0._AbsoluteWithQueryRouteValue<TPathOriginalDefinition>>;
49
+ getDefinition(): TPathDefinition;
50
+ clone(config?: Route0.RouteConfigInput): Route0<TPathOriginalDefinition, Route0._TrimQueryTailDefinition<TPathOriginalDefinition>, Route0._ParamsDefinition<TPathOriginalDefinition>, Route0._QueryDefinition<TPathOriginalDefinition>>;
51
+ }
52
+ declare namespace Route0 {
53
+ type Callable<T extends Route0<any, any, any, any>> = T & T['get'];
54
+ type RouteConfigInput = {
55
+ baseUrl?: string;
56
+ };
57
+ type Params<TRoute0 extends Route0<any, any, any, any>> = {
58
+ [K in keyof TRoute0['paramsDefinition']]: string;
59
+ };
60
+ type Query<TRoute0 extends Route0<any, any, any, any>> = Partial<{
61
+ [K in keyof TRoute0['queryDefinition']]: string | undefined;
62
+ } & Record<string, string | undefined>>;
63
+ type _TrimQueryTailDefinition<S extends string> = S extends `${infer P}&${string}` ? P : S;
64
+ type _QueryTailDefinitionWithoutFirstAmp<S extends string> = S extends `${string}&${infer T}` ? T : '';
65
+ type _QueryTailDefinitionWithFirstAmp<S extends string> = S extends `${string}&${infer T}` ? `&${T}` : '';
66
+ type _AmpSplit<S extends string> = S extends `${infer A}&${infer B}` ? A | _AmpSplit<B> : S;
67
+ type _NonEmpty<T> = [T] extends ['' | never] ? never : T;
68
+ type _ExtractPathParams<S extends string> = S extends `${string}:${infer After}` ? After extends `${infer Name}/${infer Rest}` ? Name | _ExtractPathParams<`/${Rest}`> : After : never;
69
+ type _ReplacePathParams<S extends string> = S extends `${infer Head}:${infer Tail}` ? Tail extends `${infer _Param}/${infer Rest}` ? _ReplacePathParams<`${Head}${string}/${Rest}`> : `${Head}${string}` : S;
70
+ type _DedupeSlashes<S extends string> = S extends `${infer A}//${infer B}` ? _DedupeSlashes<`${A}/${B}`> : S;
71
+ type _EmptyRecord = Record<never, never>;
72
+ type _JoinPath<Parent extends string, Suffix extends string> = _DedupeSlashes<Route0._PathDefinition<Parent> extends infer A extends string ? _PathDefinition<Suffix> extends infer B extends string ? A extends '' ? B extends '' ? '' : B extends `/${string}` ? B : `/${B}` : B extends '' ? A : A extends `${string}/` ? `${A}${B}` : B extends `/${string}` ? `${A}${B}` : `${A}/${B}` : never : never>;
73
+ type _OnlyIfNoParams<TParams extends object, Yes, No = never> = keyof TParams extends never ? Yes : No;
74
+ type _OnlyIfHasParams<TParams extends object, Yes, No = never> = keyof TParams extends never ? No : Yes;
75
+ type _PathDefinition<TPathOriginalDefinition extends string> = _TrimQueryTailDefinition<TPathOriginalDefinition>;
76
+ type _ParamsDefinition<TPathOriginalDefinition extends string> = _ExtractPathParams<_PathDefinition<TPathOriginalDefinition>> extends infer U ? [U] extends [never] ? _EmptyRecord : {
77
+ [K in Extract<U, string>]: true;
78
+ } : _EmptyRecord;
79
+ type _QueryDefinition<TPathOriginalDefinition extends string> = _NonEmpty<_QueryTailDefinitionWithoutFirstAmp<TPathOriginalDefinition>> extends infer Tail extends string ? _AmpSplit<Tail> extends infer U ? [U] extends [never] ? _EmptyRecord : {
80
+ [K in Extract<U, string>]: true;
81
+ } : _EmptyRecord : _EmptyRecord;
82
+ type _RoutePathOriginalDefinitionExtended<TSourcePathOriginalDefinition extends string, TSuffixPathOriginalDefinition extends string> = `${_JoinPath<TSourcePathOriginalDefinition, TSuffixPathOriginalDefinition>}${_QueryTailDefinitionWithFirstAmp<TSuffixPathOriginalDefinition>}`;
83
+ type _ParamsInput<TParamsDefinition extends object> = {
84
+ [K in keyof TParamsDefinition]: string | number;
85
+ };
86
+ type _QueryInput<TQueryDefinition extends object> = Partial<{
87
+ [K in keyof TQueryDefinition]: string | number;
88
+ }> & Record<string, string | number>;
89
+ type _WithParamsInput<TParamsDefinition extends object, T extends {
90
+ query?: _QueryInput<any>;
91
+ abs?: boolean;
92
+ }> = _ParamsInput<TParamsDefinition> & T;
93
+ type _PathOnlyRouteValue<TPathOriginalDefinition extends string> = `${_ReplacePathParams<_PathDefinition<TPathOriginalDefinition>>}`;
94
+ type _WithQueryRouteValue<TPathOriginalDefinition extends string> = `${_ReplacePathParams<_PathDefinition<TPathOriginalDefinition>>}?${string}`;
95
+ type _AbsolutePathOnlyRouteValue<TPathOriginalDefinition extends string> = `${string}${_PathOnlyRouteValue<TPathOriginalDefinition>}`;
96
+ type _AbsoluteWithQueryRouteValue<TPathOriginalDefinition extends string> = `${string}${_WithQueryRouteValue<TPathOriginalDefinition>}`;
97
+ }
98
+
99
+ export { Route0 };
@@ -0,0 +1,134 @@
1
+ class Route0 {
2
+ pathOriginalDefinition;
3
+ pathDefinition;
4
+ paramsDefinition;
5
+ queryDefinition;
6
+ baseUrl;
7
+ constructor(definition, config = {}) {
8
+ this.pathOriginalDefinition = definition;
9
+ this.pathDefinition = Route0._getPathDefinitionByOriginalDefinition(definition);
10
+ this.paramsDefinition = Route0._getParamsDefinitionByRouteDefinition(definition);
11
+ this.queryDefinition = Route0._getQueryDefinitionByRouteDefinition(definition);
12
+ const { baseUrl } = config;
13
+ if (baseUrl && typeof baseUrl === "string" && baseUrl.length) {
14
+ this.baseUrl = baseUrl;
15
+ } else {
16
+ const g = globalThis;
17
+ if (g?.location?.origin) {
18
+ this.baseUrl = g.location.origin;
19
+ } else {
20
+ this.baseUrl = "https://example.com";
21
+ }
22
+ }
23
+ }
24
+ static create(definition, config) {
25
+ const original = new Route0(
26
+ definition,
27
+ config
28
+ );
29
+ const callable = original.get.bind(original);
30
+ const proxy = new Proxy(callable, {
31
+ get(_target, prop, receiver) {
32
+ const value = original[prop];
33
+ if (typeof value === "function") {
34
+ return value.bind(original);
35
+ }
36
+ return value;
37
+ },
38
+ set(_target, prop, value, receiver) {
39
+ ;
40
+ original[prop] = value;
41
+ return true;
42
+ },
43
+ has(_target, prop) {
44
+ return prop in original;
45
+ }
46
+ });
47
+ Object.setPrototypeOf(proxy, Route0.prototype);
48
+ return proxy;
49
+ }
50
+ static _splitPathDefinitionAndQueryTailDefinition(pathOriginalDefinition) {
51
+ const i = pathOriginalDefinition.indexOf("&");
52
+ if (i === -1) return { pathDefinition: pathOriginalDefinition, queryTailDefinition: "" };
53
+ return {
54
+ pathDefinition: pathOriginalDefinition.slice(0, i),
55
+ queryTailDefinition: pathOriginalDefinition.slice(i)
56
+ };
57
+ }
58
+ static _getAbsPath(baseUrl, pathWithQuery) {
59
+ return new URL(pathWithQuery, baseUrl).toString().replace(/\/$/, "");
60
+ }
61
+ static _getPathDefinitionByOriginalDefinition(pathOriginalDefinition) {
62
+ const { pathDefinition } = Route0._splitPathDefinitionAndQueryTailDefinition(pathOriginalDefinition);
63
+ return pathDefinition;
64
+ }
65
+ static _getParamsDefinitionByRouteDefinition(pathOriginalDefinition) {
66
+ const { pathDefinition } = Route0._splitPathDefinitionAndQueryTailDefinition(pathOriginalDefinition);
67
+ const matches = Array.from(pathDefinition.matchAll(/:([A-Za-z0-9_]+)/g));
68
+ const paramsDefinition = Object.fromEntries(matches.map((m) => [m[1], true]));
69
+ return paramsDefinition;
70
+ }
71
+ static _getQueryDefinitionByRouteDefinition(pathOriginalDefinition) {
72
+ const { queryTailDefinition } = Route0._splitPathDefinitionAndQueryTailDefinition(pathOriginalDefinition);
73
+ if (!queryTailDefinition) {
74
+ return {};
75
+ }
76
+ const keys = queryTailDefinition.split("&").map(Boolean);
77
+ const queryDefinition = Object.fromEntries(keys.map((k) => [k, true]));
78
+ return queryDefinition;
79
+ }
80
+ static overrideMany(routes, config) {
81
+ const result = {};
82
+ for (const [key, value] of Object.entries(routes)) {
83
+ ;
84
+ result[key] = value.clone(config);
85
+ }
86
+ return result;
87
+ }
88
+ extend(suffixDefinition) {
89
+ const { pathDefinition: parentPathDefinition } = Route0._splitPathDefinitionAndQueryTailDefinition(
90
+ this.pathOriginalDefinition
91
+ );
92
+ const { pathDefinition: suffixPathDefinition, queryTailDefinition: suffixQueryTailDefinition } = Route0._splitPathDefinitionAndQueryTailDefinition(suffixDefinition);
93
+ const pathDefinition = `${parentPathDefinition}/${suffixPathDefinition}`.replace(/\/{2,}/g, "/");
94
+ const pathOriginalDefinition = `${pathDefinition}${suffixQueryTailDefinition}`;
95
+ return Route0.create(pathOriginalDefinition, { baseUrl: this.baseUrl });
96
+ }
97
+ // implementation
98
+ get(...args) {
99
+ const { queryInput, paramsInput, absInput } = (() => {
100
+ if (args.length === 0) {
101
+ return { queryInput: {}, paramsInput: {}, absInput: false };
102
+ }
103
+ const input = args[0];
104
+ if (typeof input !== "object" || input === null) {
105
+ return { queryInput: {}, paramsInput: {}, absInput: false };
106
+ }
107
+ const { query, abs, ...params } = input;
108
+ return { queryInput: query || {}, paramsInput: params, absInput: abs ?? false };
109
+ })();
110
+ const neededParamsKeys = Object.keys(this.paramsDefinition);
111
+ const providedParamsKeys = Object.keys(paramsInput);
112
+ const notProvidedKeys = neededParamsKeys.filter((k) => !providedParamsKeys.includes(k));
113
+ if (notProvidedKeys.length) {
114
+ Object.assign(paramsInput, Object.fromEntries(notProvidedKeys.map((k) => [k, "undefined"])));
115
+ }
116
+ let url = String(this.pathDefinition);
117
+ url = url.replace(/:([A-Za-z0-9_]+)/g, (_m, k) => encodeURIComponent(String(paramsInput?.[k] ?? "")));
118
+ const queryInputStringified = Object.fromEntries(Object.entries(queryInput).map(([k, v]) => [k, String(v)]));
119
+ url = [url, new URLSearchParams(queryInputStringified).toString()].filter(Boolean).join("?");
120
+ url = url.replace(/\/{2,}/g, "/");
121
+ url = absInput ? Route0._getAbsPath(this.baseUrl, url) : url;
122
+ return url;
123
+ }
124
+ getDefinition() {
125
+ return this.pathDefinition;
126
+ }
127
+ clone(config) {
128
+ return new Route0(this.pathOriginalDefinition, config);
129
+ }
130
+ }
131
+ export {
132
+ Route0
133
+ };
134
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/index.ts"],"sourcesContent":["// TODO: use splats in param definition \"*\"\n// TODO: ? check extend for query only .extend('&x&z')\n// TODO: .create(route, {useQuery, useParams})\n// TODO: Из пас экзакт, из пасвизквери экзает, из чилдрен, из парент, из экзактОр\n// TODO: isEqual, isChildren, isParent\n// TODO: extractParams, extractQuery\n// TODO: getPathDefinition respecting definitionParamPrefix, definitionQueryPrefix\n// TODO: prepend\n// TODO: Route0.createTree({base:{self: x, children: ...})\n// TODO: overrideTree\n// TODO: .create(route, {baseUrl, useLocation})\n// TODO: ? optional path params as @\n// TODO: prependMany, extendMany, overrideMany, with types\n\nexport class Route0<\n TPathOriginalDefinition extends string,\n TPathDefinition extends Route0._PathDefinition<TPathOriginalDefinition>,\n TParamsDefinition extends Route0._ParamsDefinition<TPathOriginalDefinition>,\n TQueryDefinition extends Route0._QueryDefinition<TPathOriginalDefinition>,\n> {\n pathOriginalDefinition: TPathOriginalDefinition\n private pathDefinition: TPathDefinition\n paramsDefinition: TParamsDefinition\n queryDefinition: TQueryDefinition\n baseUrl: string\n\n private constructor(definition: TPathOriginalDefinition, config: Route0.RouteConfigInput = {}) {\n this.pathOriginalDefinition = definition as TPathOriginalDefinition\n this.pathDefinition = Route0._getPathDefinitionByOriginalDefinition(definition) as TPathDefinition\n this.paramsDefinition = Route0._getParamsDefinitionByRouteDefinition(definition) as TParamsDefinition\n this.queryDefinition = Route0._getQueryDefinitionByRouteDefinition(definition) as TQueryDefinition\n\n const { baseUrl } = config\n if (baseUrl && typeof baseUrl === 'string' && baseUrl.length) {\n this.baseUrl = baseUrl\n } else {\n const g = globalThis as unknown as { location?: { origin?: string } }\n if (g?.location?.origin) {\n this.baseUrl = g.location.origin\n } else {\n this.baseUrl = 'https://example.com'\n }\n }\n }\n\n static create<\n TPathOriginalDefinition extends string,\n TPathDefinition extends Route0._PathDefinition<TPathOriginalDefinition>,\n TParamsDefinition extends Route0._ParamsDefinition<TPathOriginalDefinition>,\n TQueryDefinition extends Route0._QueryDefinition<TPathOriginalDefinition>,\n >(\n definition: TPathOriginalDefinition,\n config?: Route0.RouteConfigInput,\n ): Route0.Callable<Route0<TPathOriginalDefinition, TPathDefinition, TParamsDefinition, TQueryDefinition>> {\n const original = new Route0<TPathOriginalDefinition, TPathDefinition, TParamsDefinition, TQueryDefinition>(\n definition,\n config,\n )\n const callable = original.get.bind(original)\n const proxy = new Proxy(callable, {\n get(_target, prop, receiver) {\n const value = (original as any)[prop]\n if (typeof value === 'function') {\n return value.bind(original)\n }\n return value\n },\n set(_target, prop, value, receiver) {\n ;(original as any)[prop] = value\n return true\n },\n has(_target, prop) {\n return prop in original\n },\n })\n Object.setPrototypeOf(proxy, Route0.prototype)\n return proxy as never\n }\n\n private static _splitPathDefinitionAndQueryTailDefinition(pathOriginalDefinition: string) {\n const i = pathOriginalDefinition.indexOf('&')\n if (i === -1) return { pathDefinition: pathOriginalDefinition, queryTailDefinition: '' }\n return {\n pathDefinition: pathOriginalDefinition.slice(0, i),\n queryTailDefinition: pathOriginalDefinition.slice(i),\n }\n }\n\n private static _getAbsPath(baseUrl: string, pathWithQuery: string) {\n return new URL(pathWithQuery, baseUrl).toString().replace(/\\/$/, '')\n }\n\n private static _getPathDefinitionByOriginalDefinition<TPathOriginalDefinition extends string>(\n pathOriginalDefinition: TPathOriginalDefinition,\n ) {\n const { pathDefinition } = Route0._splitPathDefinitionAndQueryTailDefinition(pathOriginalDefinition)\n return pathDefinition as Route0._PathDefinition<TPathOriginalDefinition>\n }\n\n private static _getParamsDefinitionByRouteDefinition<TPathOriginalDefinition extends string>(\n pathOriginalDefinition: TPathOriginalDefinition,\n ) {\n const { pathDefinition } = Route0._splitPathDefinitionAndQueryTailDefinition(pathOriginalDefinition)\n const matches = Array.from(pathDefinition.matchAll(/:([A-Za-z0-9_]+)/g))\n const paramsDefinition = Object.fromEntries(matches.map((m) => [m[1], true]))\n return paramsDefinition as Route0._ParamsDefinition<TPathOriginalDefinition>\n }\n\n private static _getQueryDefinitionByRouteDefinition<TPathOriginalDefinition extends string>(\n pathOriginalDefinition: TPathOriginalDefinition,\n ) {\n const { queryTailDefinition } = Route0._splitPathDefinitionAndQueryTailDefinition(pathOriginalDefinition)\n if (!queryTailDefinition) {\n return {} as Route0._QueryDefinition<TPathOriginalDefinition>\n }\n const keys = queryTailDefinition.split('&').map(Boolean)\n const queryDefinition = Object.fromEntries(keys.map((k) => [k, true]))\n return queryDefinition as Route0._QueryDefinition<TPathOriginalDefinition>\n }\n\n static overrideMany<T extends Record<string, Route0<any, any, any, any>>>(\n routes: T,\n config: Route0.RouteConfigInput,\n ): T {\n const result = {} as T\n for (const [key, value] of Object.entries(routes)) {\n ;(result as any)[key] = value.clone(config)\n }\n return result\n }\n\n extend<TSuffixDefinition extends string>(\n suffixDefinition: TSuffixDefinition,\n ): Route0.Callable<\n Route0<\n Route0._RoutePathOriginalDefinitionExtended<TPathOriginalDefinition, TSuffixDefinition>,\n Route0._PathDefinition<Route0._RoutePathOriginalDefinitionExtended<TPathOriginalDefinition, TSuffixDefinition>>,\n Route0._ParamsDefinition<Route0._RoutePathOriginalDefinitionExtended<TPathOriginalDefinition, TSuffixDefinition>>,\n Route0._QueryDefinition<Route0._RoutePathOriginalDefinitionExtended<TPathOriginalDefinition, TSuffixDefinition>>\n >\n > {\n const { pathDefinition: parentPathDefinition } = Route0._splitPathDefinitionAndQueryTailDefinition(\n this.pathOriginalDefinition,\n )\n const { pathDefinition: suffixPathDefinition, queryTailDefinition: suffixQueryTailDefinition } =\n Route0._splitPathDefinitionAndQueryTailDefinition(suffixDefinition)\n const pathDefinition = `${parentPathDefinition}/${suffixPathDefinition}`.replace(/\\/{2,}/g, '/')\n const pathOriginalDefinition =\n `${pathDefinition}${suffixQueryTailDefinition}` as Route0._RoutePathOriginalDefinitionExtended<\n TPathOriginalDefinition,\n TSuffixDefinition\n >\n return Route0.create<\n Route0._RoutePathOriginalDefinitionExtended<TPathOriginalDefinition, TSuffixDefinition>,\n Route0._PathDefinition<Route0._RoutePathOriginalDefinitionExtended<TPathOriginalDefinition, TSuffixDefinition>>,\n Route0._ParamsDefinition<Route0._RoutePathOriginalDefinitionExtended<TPathOriginalDefinition, TSuffixDefinition>>,\n Route0._QueryDefinition<Route0._RoutePathOriginalDefinitionExtended<TPathOriginalDefinition, TSuffixDefinition>>\n >(pathOriginalDefinition, { baseUrl: this.baseUrl })\n }\n\n // has params\n get(\n input: Route0._OnlyIfHasParams<\n TParamsDefinition,\n Route0._WithParamsInput<TParamsDefinition, { query?: undefined; abs?: false }>\n >,\n ): Route0._OnlyIfHasParams<TParamsDefinition, Route0._PathOnlyRouteValue<TPathOriginalDefinition>>\n get(\n input: Route0._OnlyIfHasParams<\n TParamsDefinition,\n Route0._WithParamsInput<TParamsDefinition, { query: Route0._QueryInput<TQueryDefinition>; abs?: false }>\n >,\n ): Route0._OnlyIfHasParams<TParamsDefinition, Route0._WithQueryRouteValue<TPathOriginalDefinition>>\n get(\n input: Route0._OnlyIfHasParams<\n TParamsDefinition,\n Route0._WithParamsInput<TParamsDefinition, { query?: undefined; abs: true }>\n >,\n ): Route0._OnlyIfHasParams<TParamsDefinition, Route0._AbsolutePathOnlyRouteValue<TPathOriginalDefinition>>\n get(\n input: Route0._OnlyIfHasParams<\n TParamsDefinition,\n Route0._WithParamsInput<TParamsDefinition, { query: Route0._QueryInput<TQueryDefinition>; abs: true }>\n >,\n ): Route0._OnlyIfHasParams<TParamsDefinition, Route0._AbsoluteWithQueryRouteValue<TPathOriginalDefinition>>\n\n // no params\n get(\n ...args: Route0._OnlyIfNoParams<TParamsDefinition, [], [never]>\n ): Route0._PathOnlyRouteValue<TPathOriginalDefinition>\n get(\n input: Route0._OnlyIfNoParams<TParamsDefinition, { query?: undefined; abs?: false }>,\n ): Route0._OnlyIfNoParams<TParamsDefinition, Route0._PathOnlyRouteValue<TPathOriginalDefinition>>\n get(\n input: Route0._OnlyIfNoParams<TParamsDefinition, { query: Route0._QueryInput<TQueryDefinition>; abs?: false }>,\n ): Route0._OnlyIfNoParams<TParamsDefinition, Route0._WithQueryRouteValue<TPathOriginalDefinition>>\n get(\n input: Route0._OnlyIfNoParams<TParamsDefinition, { query?: undefined; abs: true }>,\n ): Route0._OnlyIfNoParams<TParamsDefinition, Route0._AbsolutePathOnlyRouteValue<TPathOriginalDefinition>>\n get(\n input: Route0._OnlyIfNoParams<TParamsDefinition, { query: Route0._QueryInput<TQueryDefinition>; abs: true }>,\n ): Route0._OnlyIfNoParams<TParamsDefinition, Route0._AbsoluteWithQueryRouteValue<TPathOriginalDefinition>>\n\n // implementation\n get(...args: any[]): string {\n const { queryInput, paramsInput, absInput } = ((): {\n queryInput: Record<string, string | number>\n paramsInput: Record<string, string | number>\n absInput: boolean\n } => {\n if (args.length === 0) {\n return { queryInput: {}, paramsInput: {}, absInput: false }\n }\n const input = args[0]\n if (typeof input !== 'object' || input === null) {\n // throw new Error(\"Invalid get route input: expected object\")\n return { queryInput: {}, paramsInput: {}, absInput: false }\n }\n const { query, abs, ...params } = input\n return { queryInput: query || {}, paramsInput: params, absInput: abs ?? false }\n })()\n\n // validate params\n const neededParamsKeys = Object.keys(this.paramsDefinition)\n const providedParamsKeys = Object.keys(paramsInput)\n const notProvidedKeys = neededParamsKeys.filter((k) => !providedParamsKeys.includes(k))\n if (notProvidedKeys.length) {\n // throw new Error(`Missing params: not defined keys ${notProvidedKeys.map((k) => `\"${k}\"`).join(\", \")}.`)\n Object.assign(paramsInput, Object.fromEntries(notProvidedKeys.map((k) => [k, 'undefined'])))\n }\n\n // create url\n let url = String(this.pathDefinition)\n // replace params\n url = url.replace(/:([A-Za-z0-9_]+)/g, (_m, k) => encodeURIComponent(String(paramsInput?.[k] ?? '')))\n // query params\n const queryInputStringified = Object.fromEntries(Object.entries(queryInput).map(([k, v]) => [k, String(v)]))\n url = [url, new URLSearchParams(queryInputStringified).toString()].filter(Boolean).join('?')\n // dedupe slashes\n url = url.replace(/\\/{2,}/g, '/')\n // absolute\n url = absInput ? Route0._getAbsPath(this.baseUrl, url) : url\n\n return url\n }\n\n getDefinition() {\n return this.pathDefinition\n }\n\n clone(config?: Route0.RouteConfigInput) {\n return new Route0(this.pathOriginalDefinition, config)\n }\n}\n\nexport namespace Route0 {\n export type Callable<T extends Route0<any, any, any, any>> = T & T['get']\n export type RouteConfigInput = {\n baseUrl?: string\n }\n export type Params<TRoute0 extends Route0<any, any, any, any>> = {\n [K in keyof TRoute0['paramsDefinition']]: string\n }\n export type Query<TRoute0 extends Route0<any, any, any, any>> = Partial<\n {\n [K in keyof TRoute0['queryDefinition']]: string | undefined\n } & Record<string, string | undefined>\n >\n\n export type _TrimQueryTailDefinition<S extends string> = S extends `${infer P}&${string}` ? P : S\n export type _QueryTailDefinitionWithoutFirstAmp<S extends string> = S extends `${string}&${infer T}` ? T : ''\n export type _QueryTailDefinitionWithFirstAmp<S extends string> = S extends `${string}&${infer T}` ? `&${T}` : ''\n export type _AmpSplit<S extends string> = S extends `${infer A}&${infer B}` ? A | _AmpSplit<B> : S\n export type _NonEmpty<T> = [T] extends ['' | never] ? never : T\n export type _ExtractPathParams<S extends string> = S extends `${string}:${infer After}`\n ? After extends `${infer Name}/${infer Rest}`\n ? Name | _ExtractPathParams<`/${Rest}`>\n : After\n : never\n export type _ReplacePathParams<S extends string> = S extends `${infer Head}:${infer Tail}`\n ? Tail extends `${infer _Param}/${infer Rest}`\n ? _ReplacePathParams<`${Head}${string}/${Rest}`>\n : `${Head}${string}`\n : S\n export type _DedupeSlashes<S extends string> = S extends `${infer A}//${infer B}` ? _DedupeSlashes<`${A}/${B}`> : S\n export type _EmptyRecord = Record<never, never>\n export type _JoinPath<Parent extends string, Suffix extends string> = _DedupeSlashes<\n Route0._PathDefinition<Parent> extends infer A extends string\n ? _PathDefinition<Suffix> extends infer B extends string\n ? A extends ''\n ? B extends ''\n ? ''\n : B extends `/${string}`\n ? B\n : `/${B}`\n : B extends ''\n ? A\n : A extends `${string}/`\n ? `${A}${B}`\n : B extends `/${string}`\n ? `${A}${B}`\n : `${A}/${B}`\n : never\n : never\n >\n\n export type _OnlyIfNoParams<TParams extends object, Yes, No = never> = keyof TParams extends never ? Yes : No\n export type _OnlyIfHasParams<TParams extends object, Yes, No = never> = keyof TParams extends never ? No : Yes\n\n export type _PathDefinition<TPathOriginalDefinition extends string> =\n _TrimQueryTailDefinition<TPathOriginalDefinition>\n export type _ParamsDefinition<TPathOriginalDefinition extends string> = _ExtractPathParams<\n _PathDefinition<TPathOriginalDefinition>\n > extends infer U\n ? [U] extends [never]\n ? _EmptyRecord\n : { [K in Extract<U, string>]: true }\n : _EmptyRecord\n export type _QueryDefinition<TPathOriginalDefinition extends string> = _NonEmpty<\n _QueryTailDefinitionWithoutFirstAmp<TPathOriginalDefinition>\n > extends infer Tail extends string\n ? _AmpSplit<Tail> extends infer U\n ? [U] extends [never]\n ? _EmptyRecord\n : { [K in Extract<U, string>]: true }\n : _EmptyRecord\n : _EmptyRecord\n export type _RoutePathOriginalDefinitionExtended<\n TSourcePathOriginalDefinition extends string,\n TSuffixPathOriginalDefinition extends string,\n > = `${_JoinPath<TSourcePathOriginalDefinition, TSuffixPathOriginalDefinition>}${_QueryTailDefinitionWithFirstAmp<TSuffixPathOriginalDefinition>}`\n\n export type _ParamsInput<TParamsDefinition extends object> = {\n [K in keyof TParamsDefinition]: string | number\n }\n export type _QueryInput<TQueryDefinition extends object> = Partial<{\n [K in keyof TQueryDefinition]: string | number\n }> &\n Record<string, string | number>\n export type _WithParamsInput<\n TParamsDefinition extends object,\n T extends {\n query?: _QueryInput<any>\n abs?: boolean\n },\n > = _ParamsInput<TParamsDefinition> & T\n\n export type _PathOnlyRouteValue<TPathOriginalDefinition extends string> =\n `${_ReplacePathParams<_PathDefinition<TPathOriginalDefinition>>}`\n export type _WithQueryRouteValue<TPathOriginalDefinition extends string> =\n `${_ReplacePathParams<_PathDefinition<TPathOriginalDefinition>>}?${string}`\n export type _AbsolutePathOnlyRouteValue<TPathOriginalDefinition extends string> =\n `${string}${_PathOnlyRouteValue<TPathOriginalDefinition>}`\n export type _AbsoluteWithQueryRouteValue<TPathOriginalDefinition extends string> =\n `${string}${_WithQueryRouteValue<TPathOriginalDefinition>}`\n}\n"],"mappings":"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","names":[]}
package/package.json ADDED
@@ -0,0 +1,91 @@
1
+ {
2
+ "name": "@devp0nt/route0",
3
+ "license": "MIT",
4
+ "author": {
5
+ "name": "Sergei Dmitriev",
6
+ "url": "https://p0nt.dev"
7
+ },
8
+ "homepage": "https://github.com/devp0nt/route0#readme",
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "git+https://github.com/devp0nt/route0.git"
12
+ },
13
+ "bugs": {
14
+ "url": "https://github.com/devp0nt/route0/issues"
15
+ },
16
+ "type": "module",
17
+ "main": "./dist/cjs/index.js",
18
+ "module": "./dist/esm/index.js",
19
+ "types": "./dist/esm/index.d.ts",
20
+ "exports": {
21
+ ".": {
22
+ "types": "./dist/esm/index.d.ts",
23
+ "import": "./dist/esm/index.js",
24
+ "require": "./dist/cjs/index.js"
25
+ }
26
+ },
27
+ "files": [
28
+ "dist/**/*",
29
+ "src/**/*",
30
+ "README.md",
31
+ "LICENSE"
32
+ ],
33
+ "scripts": {
34
+ "dev": "tsc --watch --preserveWatchOutput --project tsconfig.build.json",
35
+ "build": "tsup",
36
+ "build:test:esm": "tsup --outDir dist-test/esm --tsconfig tsconfig.build.test.json --format esm src",
37
+ "build:test:esm:watch": "bun run build:test:esm --watch",
38
+ "build:test:cjs": "tsup --outDir dist-test/cjs --tsconfig tsconfig.build.test.json --format cjs src",
39
+ "build:test:cjs:watch": "bun run build:test:cjs --watch",
40
+ "build:test": "bun run build:test:esm && bun run build:test:cjs",
41
+ "build:test:watch": "bun run build:test:esm:watch & bun run build:test:cjs:watch",
42
+ "build:all": "bun run build && bun run build:test",
43
+ "test": "bun test src",
44
+ "test:watch": "bun test --watch",
45
+ "test:coverage": "bun test --coverage",
46
+ "test:dist:esm": "bun test dist-test/esm",
47
+ "test:dist:esm:watch": "bun run test:dist:esm --watch",
48
+ "test:dist:esm:build:watch": "bun build:test:esm:watch --no-clean & bun test:dist:esm:watch",
49
+ "test:dist:cjs": "bun test dist-test/cjs",
50
+ "test:dist:cjs:watch": "bun run test:dist:cjs --watch",
51
+ "test:dist:cjs:build:watch": "bun build:test:cjs:watch --no-clean & bun test:dist:cjs:watch",
52
+ "test:dist": "bun run test:dist:esm && bun run test:dist:cjs",
53
+ "test:dist:watch": "bun run test:dist:esm:watch & bun run test:dist:cjs:watch",
54
+ "test:dist:build:watch": "bun build:test:watch & bun test:dist:watch",
55
+ "test:all": "bun run test && bun run test:dist",
56
+ "test:all:watch": "bun run test:watch & bun run test:dist:watch",
57
+ "test:all:build": "bun run build:all && bun run test:all",
58
+ "test:all:build:watch": "bun run build:all:watch & bun run test:all:watch",
59
+ "lint": "biome check --write",
60
+ "types:build": "tsc --noEmit --project tsconfig.build.json",
61
+ "types:dev": "tsc --noEmit",
62
+ "types": "bun run types:dev",
63
+ "pack:dry": "npm pack --dry-run",
64
+ "prepare": "husky"
65
+ },
66
+ "dependencies": {},
67
+ "devDependencies": {
68
+ "@biomejs/biome": "^2.2.4",
69
+ "@commitlint/cli": "^19.8.1",
70
+ "@commitlint/config-conventional": "^19.8.1",
71
+ "@semantic-release/changelog": "^6.0.3",
72
+ "@semantic-release/git": "^10.0.1",
73
+ "@semantic-release/github": "^11.0.6",
74
+ "@semantic-release/npm": "^12.0.2",
75
+ "@types/bun": "^1.2.22",
76
+ "@types/node": "^20.0.0",
77
+ "cross-env": "^10.0.0",
78
+ "husky": "^9.1.7",
79
+ "semantic-release": "^24.2.8",
80
+ "tsup": "^8.0.0",
81
+ "typescript": "^5.0.0"
82
+ },
83
+ "engines": {
84
+ "node": ">=18.0.0",
85
+ "bun": ">=1.0.0"
86
+ },
87
+ "publishConfig": {
88
+ "access": "public"
89
+ },
90
+ "version": "1.0.0-next.1"
91
+ }
@@ -0,0 +1,206 @@
1
+ import { describe, expect, expectTypeOf, it } from 'bun:test'
2
+ import { Route0 } from './index.js'
3
+
4
+ describe('route0', () => {
5
+ it('simple', () => {
6
+ const route0 = Route0.create('/')
7
+ const path = route0.get()
8
+ expect(route0).toBeInstanceOf(Route0)
9
+ expectTypeOf<typeof path>().toEqualTypeOf<'/'>()
10
+ expect(path).toBe('/')
11
+ })
12
+
13
+ it('simple, callable', () => {
14
+ const route0 = Route0.create('/')
15
+ const path = route0()
16
+ expect(route0).toBeInstanceOf(Route0)
17
+ expectTypeOf<typeof path>().toEqualTypeOf<'/'>()
18
+ expect(path).toBe('/')
19
+ })
20
+
21
+ it('simple any query', () => {
22
+ const route0 = Route0.create('/')
23
+ const path = route0.get({ query: { q: '1' } })
24
+ expectTypeOf<typeof path>().toEqualTypeOf<`/?${string}`>()
25
+ expect(path).toBe('/?q=1')
26
+ })
27
+
28
+ it('params', () => {
29
+ const route0 = Route0.create('/prefix/:x/some/:y/:z')
30
+ const path = route0.get({ x: '1', y: 2, z: '3' })
31
+ expectTypeOf<typeof path>().toEqualTypeOf<`/prefix/${string}/some/${string}/${string}`>()
32
+ expect(path).toBe('/prefix/1/some/2/3')
33
+ })
34
+
35
+ it('params and any query', () => {
36
+ const route0 = Route0.create('/prefix/:x/some/:y/:z')
37
+ const path = route0.get({ x: '1', y: 2, z: '3', query: { q: '1' } })
38
+ expectTypeOf<typeof path>().toEqualTypeOf<`/prefix/${string}/some/${string}/${string}?${string}`>()
39
+ expect(path).toBe('/prefix/1/some/2/3?q=1')
40
+ })
41
+
42
+ it('query', () => {
43
+ const route0 = Route0.create('/prefix&y&z')
44
+ expectTypeOf<(typeof route0)['queryDefinition']>().toEqualTypeOf<{ y: true; z: true }>()
45
+ const path = route0.get({ query: { y: '1', z: '2' } })
46
+ expectTypeOf<typeof path>().toEqualTypeOf<`/prefix?${string}`>()
47
+ expect(path).toBe('/prefix?y=1&z=2')
48
+ })
49
+
50
+ it('params and query', () => {
51
+ const route0 = Route0.create('/prefix/:x/some/:y/:z&z&c')
52
+ const path = route0.get({ x: '1', y: '2', z: '3', query: { z: '4', c: '5' } })
53
+ expectTypeOf<typeof path>().toEqualTypeOf<`/prefix/${string}/some/${string}/${string}?${string}`>()
54
+ expect(path).toBe('/prefix/1/some/2/3?z=4&c=5')
55
+ })
56
+
57
+ it('params and query and any query', () => {
58
+ const route0 = Route0.create('/prefix/:x/some/:y/:z&z&c')
59
+ const path = route0.get({ x: '1', y: '2', z: '3', query: { z: '4', c: '5', o: '6' } })
60
+ expectTypeOf<typeof path>().toEqualTypeOf<`/prefix/${string}/some/${string}/${string}?${string}`>()
61
+ expect(path).toBe('/prefix/1/some/2/3?z=4&c=5&o=6')
62
+ })
63
+
64
+ it('simple extend', () => {
65
+ const route0 = Route0.create('/prefix')
66
+ const route1 = route0.extend('/suffix')
67
+ const path = route1.get()
68
+ expectTypeOf<typeof path>().toEqualTypeOf<`/prefix/suffix`>()
69
+ expect(path).toBe('/prefix/suffix')
70
+ })
71
+
72
+ it('simple extend double slash', () => {
73
+ const route0 = Route0.create('/')
74
+ const route1 = route0.extend('/suffix1/')
75
+ const route2 = route1.extend('/suffix2')
76
+ const path = route2.get()
77
+ expectTypeOf<typeof path>().toEqualTypeOf<`/suffix1/suffix2`>()
78
+ expect(path).toBe('/suffix1/suffix2')
79
+ })
80
+
81
+ it('simple extend no slash', () => {
82
+ const route0 = Route0.create('/')
83
+ const route1 = route0.extend('suffix1')
84
+ const route2 = route1.extend('suffix2')
85
+ const path = route2.get()
86
+ expectTypeOf<typeof path>().toEqualTypeOf<`/suffix1/suffix2`>()
87
+ expect(path).toBe('/suffix1/suffix2')
88
+ })
89
+
90
+ it('extend with params', () => {
91
+ const route0 = Route0.create('/prefix/:x')
92
+ const route1 = route0.extend('/suffix/:y')
93
+ const path = route1.get({ x: '1', y: '2' })
94
+ expectTypeOf<typeof path>().toEqualTypeOf<`/prefix/${string}/suffix/${string}`>()
95
+ expect(path).toBe('/prefix/1/suffix/2')
96
+ })
97
+
98
+ it('extend with search params', () => {
99
+ const route0 = Route0.create('/prefix&y&z')
100
+ const route1 = route0.extend('/suffix&z&c')
101
+ const path = route1.get({ query: { y: '2', c: '3', a: '4' } })
102
+ expectTypeOf<(typeof route1)['queryDefinition']>().toEqualTypeOf<{
103
+ z: true
104
+ c: true
105
+ }>()
106
+ expectTypeOf<typeof path>().toEqualTypeOf<`/prefix/suffix?${string}`>()
107
+ expect(path).toBe('/prefix/suffix?y=2&c=3&a=4')
108
+ const path1 = route1.get()
109
+ expectTypeOf<typeof path1>().toEqualTypeOf<`/prefix/suffix`>()
110
+ expect(path1).toBe('/prefix/suffix')
111
+ })
112
+
113
+ it('extend with params and query', () => {
114
+ const route0 = Route0.create('/prefix/:id&y&z')
115
+ const route1 = route0.extend('/:sn/suffix&z&c')
116
+ const path = route1.get({ id: 'myid', sn: 'mysn', query: { y: '2', c: '3', a: '4' } })
117
+ expectTypeOf<(typeof route1)['queryDefinition']>().toEqualTypeOf<{
118
+ z: true
119
+ c: true
120
+ }>()
121
+ expectTypeOf<typeof path>().toEqualTypeOf<`/prefix/${string}/${string}/suffix?${string}`>()
122
+ expect(path).toBe('/prefix/myid/mysn/suffix?y=2&c=3&a=4')
123
+ const path1 = route1.get({ id: 'myid', sn: 'mysn' })
124
+ expectTypeOf<typeof path1>().toEqualTypeOf<`/prefix/${string}/${string}/suffix`>()
125
+ expect(path1).toBe('/prefix/myid/mysn/suffix')
126
+ })
127
+
128
+ it('extend with params and query, callable', () => {
129
+ const route0 = Route0.create('/prefix/:id&y&z')
130
+ const route1 = route0.extend('/:sn/suffix&z&c')
131
+ const path = route1({ id: 'myid', sn: 'mysn', query: { y: '2', c: '3', a: '4' } })
132
+ expectTypeOf<(typeof route1)['queryDefinition']>().toEqualTypeOf<{
133
+ z: true
134
+ c: true
135
+ }>()
136
+ expectTypeOf<typeof path>().toEqualTypeOf<`/prefix/${string}/${string}/suffix?${string}`>()
137
+ expect(path).toBe('/prefix/myid/mysn/suffix?y=2&c=3&a=4')
138
+ const path1 = route1({ id: 'myid', sn: 'mysn' })
139
+ expectTypeOf<typeof path1>().toEqualTypeOf<`/prefix/${string}/${string}/suffix`>()
140
+ expect(path1).toBe('/prefix/myid/mysn/suffix')
141
+ })
142
+
143
+ it('abs default', () => {
144
+ const route0 = Route0.create('/path')
145
+ const path = route0.get({ abs: true })
146
+ expectTypeOf<typeof path>().toEqualTypeOf<`${string}/path`>()
147
+ expect(path).toBe('https://example.com/path')
148
+ })
149
+
150
+ it('abs set', () => {
151
+ const route0 = Route0.create('/path', { baseUrl: 'https://x.com' })
152
+ const path = route0.get({ abs: true })
153
+ expectTypeOf<typeof path>().toEqualTypeOf<`${string}/path`>()
154
+ expect(path).toBe('https://x.com/path')
155
+ })
156
+
157
+ it('abs override', () => {
158
+ const route0 = Route0.create('/path', { baseUrl: 'https://x.com' })
159
+ route0.baseUrl = 'https://y.com'
160
+ const path = route0.get({ abs: true })
161
+ expectTypeOf<typeof path>().toEqualTypeOf<`${string}/path`>()
162
+ expect(path).toBe('https://y.com/path')
163
+ })
164
+
165
+ it('abs override extend', () => {
166
+ const route0 = Route0.create('/path', { baseUrl: 'https://x.com' })
167
+ route0.baseUrl = 'https://y.com'
168
+ const route1 = route0.extend('/suffix')
169
+ const path = route1.get({ abs: true })
170
+ expectTypeOf<typeof path>().toEqualTypeOf<`${string}/path/suffix`>()
171
+ expect(path).toBe('https://y.com/path/suffix')
172
+ })
173
+
174
+ it('abs override many', () => {
175
+ const route0 = Route0.create('/path', { baseUrl: 'https://x.com' })
176
+ const route1 = route0.extend('/suffix')
177
+ const routes = {
178
+ r0: route0,
179
+ r1: route1,
180
+ }
181
+ const routes2 = Route0.overrideMany(routes, { baseUrl: 'https://z.com' })
182
+ const path = routes2.r1.get({ abs: true })
183
+ expectTypeOf<typeof path>().toEqualTypeOf<`${string}/path/suffix`>()
184
+ expect(path).toBe('https://z.com/path/suffix')
185
+ })
186
+
187
+ it('type errors: require params when defined', () => {
188
+ const rWith = Route0.create('/a/:id')
189
+ // @ts-expect-error missing required path params
190
+ expect(rWith.get()).toBe('/a/undefined')
191
+
192
+ // @ts-expect-error missing required path params
193
+ expect(rWith.get({})).toBe('/a/undefined')
194
+ // @ts-expect-error missing required path params (object form abs)
195
+ expect(rWith.get({ abs: true })).toBe('https://example.com/a/undefined')
196
+ // @ts-expect-error missing required path params (object form query)
197
+ expect(rWith.get({ query: { q: '1' } })).toBe('/a/undefined?q=1')
198
+
199
+ // @ts-expect-error params can not be sent as object value it should be argument
200
+ rWith.get({ params: { id: '1' } }) // not throw becouse this will not used
201
+
202
+ const rNo = Route0.create('/b')
203
+ // @ts-expect-error no path params allowed for this route (shorthand)
204
+ expect(rNo.get({ id: '1' })).toBe('/b')
205
+ })
206
+ })
package/src/index.ts ADDED
@@ -0,0 +1,356 @@
1
+ // TODO: use splats in param definition "*"
2
+ // TODO: ? check extend for query only .extend('&x&z')
3
+ // TODO: .create(route, {useQuery, useParams})
4
+ // TODO: Из пас экзакт, из пасвизквери экзает, из чилдрен, из парент, из экзактОр
5
+ // TODO: isEqual, isChildren, isParent
6
+ // TODO: extractParams, extractQuery
7
+ // TODO: getPathDefinition respecting definitionParamPrefix, definitionQueryPrefix
8
+ // TODO: prepend
9
+ // TODO: Route0.createTree({base:{self: x, children: ...})
10
+ // TODO: overrideTree
11
+ // TODO: .create(route, {baseUrl, useLocation})
12
+ // TODO: ? optional path params as @
13
+ // TODO: prependMany, extendMany, overrideMany, with types
14
+
15
+ export class Route0<
16
+ TPathOriginalDefinition extends string,
17
+ TPathDefinition extends Route0._PathDefinition<TPathOriginalDefinition>,
18
+ TParamsDefinition extends Route0._ParamsDefinition<TPathOriginalDefinition>,
19
+ TQueryDefinition extends Route0._QueryDefinition<TPathOriginalDefinition>,
20
+ > {
21
+ pathOriginalDefinition: TPathOriginalDefinition
22
+ private pathDefinition: TPathDefinition
23
+ paramsDefinition: TParamsDefinition
24
+ queryDefinition: TQueryDefinition
25
+ baseUrl: string
26
+
27
+ private constructor(definition: TPathOriginalDefinition, config: Route0.RouteConfigInput = {}) {
28
+ this.pathOriginalDefinition = definition as TPathOriginalDefinition
29
+ this.pathDefinition = Route0._getPathDefinitionByOriginalDefinition(definition) as TPathDefinition
30
+ this.paramsDefinition = Route0._getParamsDefinitionByRouteDefinition(definition) as TParamsDefinition
31
+ this.queryDefinition = Route0._getQueryDefinitionByRouteDefinition(definition) as TQueryDefinition
32
+
33
+ const { baseUrl } = config
34
+ if (baseUrl && typeof baseUrl === 'string' && baseUrl.length) {
35
+ this.baseUrl = baseUrl
36
+ } else {
37
+ const g = globalThis as unknown as { location?: { origin?: string } }
38
+ if (g?.location?.origin) {
39
+ this.baseUrl = g.location.origin
40
+ } else {
41
+ this.baseUrl = 'https://example.com'
42
+ }
43
+ }
44
+ }
45
+
46
+ static create<
47
+ TPathOriginalDefinition extends string,
48
+ TPathDefinition extends Route0._PathDefinition<TPathOriginalDefinition>,
49
+ TParamsDefinition extends Route0._ParamsDefinition<TPathOriginalDefinition>,
50
+ TQueryDefinition extends Route0._QueryDefinition<TPathOriginalDefinition>,
51
+ >(
52
+ definition: TPathOriginalDefinition,
53
+ config?: Route0.RouteConfigInput,
54
+ ): Route0.Callable<Route0<TPathOriginalDefinition, TPathDefinition, TParamsDefinition, TQueryDefinition>> {
55
+ const original = new Route0<TPathOriginalDefinition, TPathDefinition, TParamsDefinition, TQueryDefinition>(
56
+ definition,
57
+ config,
58
+ )
59
+ const callable = original.get.bind(original)
60
+ const proxy = new Proxy(callable, {
61
+ get(_target, prop, receiver) {
62
+ const value = (original as any)[prop]
63
+ if (typeof value === 'function') {
64
+ return value.bind(original)
65
+ }
66
+ return value
67
+ },
68
+ set(_target, prop, value, receiver) {
69
+ ;(original as any)[prop] = value
70
+ return true
71
+ },
72
+ has(_target, prop) {
73
+ return prop in original
74
+ },
75
+ })
76
+ Object.setPrototypeOf(proxy, Route0.prototype)
77
+ return proxy as never
78
+ }
79
+
80
+ private static _splitPathDefinitionAndQueryTailDefinition(pathOriginalDefinition: string) {
81
+ const i = pathOriginalDefinition.indexOf('&')
82
+ if (i === -1) return { pathDefinition: pathOriginalDefinition, queryTailDefinition: '' }
83
+ return {
84
+ pathDefinition: pathOriginalDefinition.slice(0, i),
85
+ queryTailDefinition: pathOriginalDefinition.slice(i),
86
+ }
87
+ }
88
+
89
+ private static _getAbsPath(baseUrl: string, pathWithQuery: string) {
90
+ return new URL(pathWithQuery, baseUrl).toString().replace(/\/$/, '')
91
+ }
92
+
93
+ private static _getPathDefinitionByOriginalDefinition<TPathOriginalDefinition extends string>(
94
+ pathOriginalDefinition: TPathOriginalDefinition,
95
+ ) {
96
+ const { pathDefinition } = Route0._splitPathDefinitionAndQueryTailDefinition(pathOriginalDefinition)
97
+ return pathDefinition as Route0._PathDefinition<TPathOriginalDefinition>
98
+ }
99
+
100
+ private static _getParamsDefinitionByRouteDefinition<TPathOriginalDefinition extends string>(
101
+ pathOriginalDefinition: TPathOriginalDefinition,
102
+ ) {
103
+ const { pathDefinition } = Route0._splitPathDefinitionAndQueryTailDefinition(pathOriginalDefinition)
104
+ const matches = Array.from(pathDefinition.matchAll(/:([A-Za-z0-9_]+)/g))
105
+ const paramsDefinition = Object.fromEntries(matches.map((m) => [m[1], true]))
106
+ return paramsDefinition as Route0._ParamsDefinition<TPathOriginalDefinition>
107
+ }
108
+
109
+ private static _getQueryDefinitionByRouteDefinition<TPathOriginalDefinition extends string>(
110
+ pathOriginalDefinition: TPathOriginalDefinition,
111
+ ) {
112
+ const { queryTailDefinition } = Route0._splitPathDefinitionAndQueryTailDefinition(pathOriginalDefinition)
113
+ if (!queryTailDefinition) {
114
+ return {} as Route0._QueryDefinition<TPathOriginalDefinition>
115
+ }
116
+ const keys = queryTailDefinition.split('&').map(Boolean)
117
+ const queryDefinition = Object.fromEntries(keys.map((k) => [k, true]))
118
+ return queryDefinition as Route0._QueryDefinition<TPathOriginalDefinition>
119
+ }
120
+
121
+ static overrideMany<T extends Record<string, Route0<any, any, any, any>>>(
122
+ routes: T,
123
+ config: Route0.RouteConfigInput,
124
+ ): T {
125
+ const result = {} as T
126
+ for (const [key, value] of Object.entries(routes)) {
127
+ ;(result as any)[key] = value.clone(config)
128
+ }
129
+ return result
130
+ }
131
+
132
+ extend<TSuffixDefinition extends string>(
133
+ suffixDefinition: TSuffixDefinition,
134
+ ): Route0.Callable<
135
+ Route0<
136
+ Route0._RoutePathOriginalDefinitionExtended<TPathOriginalDefinition, TSuffixDefinition>,
137
+ Route0._PathDefinition<Route0._RoutePathOriginalDefinitionExtended<TPathOriginalDefinition, TSuffixDefinition>>,
138
+ Route0._ParamsDefinition<Route0._RoutePathOriginalDefinitionExtended<TPathOriginalDefinition, TSuffixDefinition>>,
139
+ Route0._QueryDefinition<Route0._RoutePathOriginalDefinitionExtended<TPathOriginalDefinition, TSuffixDefinition>>
140
+ >
141
+ > {
142
+ const { pathDefinition: parentPathDefinition } = Route0._splitPathDefinitionAndQueryTailDefinition(
143
+ this.pathOriginalDefinition,
144
+ )
145
+ const { pathDefinition: suffixPathDefinition, queryTailDefinition: suffixQueryTailDefinition } =
146
+ Route0._splitPathDefinitionAndQueryTailDefinition(suffixDefinition)
147
+ const pathDefinition = `${parentPathDefinition}/${suffixPathDefinition}`.replace(/\/{2,}/g, '/')
148
+ const pathOriginalDefinition =
149
+ `${pathDefinition}${suffixQueryTailDefinition}` as Route0._RoutePathOriginalDefinitionExtended<
150
+ TPathOriginalDefinition,
151
+ TSuffixDefinition
152
+ >
153
+ return Route0.create<
154
+ Route0._RoutePathOriginalDefinitionExtended<TPathOriginalDefinition, TSuffixDefinition>,
155
+ Route0._PathDefinition<Route0._RoutePathOriginalDefinitionExtended<TPathOriginalDefinition, TSuffixDefinition>>,
156
+ Route0._ParamsDefinition<Route0._RoutePathOriginalDefinitionExtended<TPathOriginalDefinition, TSuffixDefinition>>,
157
+ Route0._QueryDefinition<Route0._RoutePathOriginalDefinitionExtended<TPathOriginalDefinition, TSuffixDefinition>>
158
+ >(pathOriginalDefinition, { baseUrl: this.baseUrl })
159
+ }
160
+
161
+ // has params
162
+ get(
163
+ input: Route0._OnlyIfHasParams<
164
+ TParamsDefinition,
165
+ Route0._WithParamsInput<TParamsDefinition, { query?: undefined; abs?: false }>
166
+ >,
167
+ ): Route0._OnlyIfHasParams<TParamsDefinition, Route0._PathOnlyRouteValue<TPathOriginalDefinition>>
168
+ get(
169
+ input: Route0._OnlyIfHasParams<
170
+ TParamsDefinition,
171
+ Route0._WithParamsInput<TParamsDefinition, { query: Route0._QueryInput<TQueryDefinition>; abs?: false }>
172
+ >,
173
+ ): Route0._OnlyIfHasParams<TParamsDefinition, Route0._WithQueryRouteValue<TPathOriginalDefinition>>
174
+ get(
175
+ input: Route0._OnlyIfHasParams<
176
+ TParamsDefinition,
177
+ Route0._WithParamsInput<TParamsDefinition, { query?: undefined; abs: true }>
178
+ >,
179
+ ): Route0._OnlyIfHasParams<TParamsDefinition, Route0._AbsolutePathOnlyRouteValue<TPathOriginalDefinition>>
180
+ get(
181
+ input: Route0._OnlyIfHasParams<
182
+ TParamsDefinition,
183
+ Route0._WithParamsInput<TParamsDefinition, { query: Route0._QueryInput<TQueryDefinition>; abs: true }>
184
+ >,
185
+ ): Route0._OnlyIfHasParams<TParamsDefinition, Route0._AbsoluteWithQueryRouteValue<TPathOriginalDefinition>>
186
+
187
+ // no params
188
+ get(
189
+ ...args: Route0._OnlyIfNoParams<TParamsDefinition, [], [never]>
190
+ ): Route0._PathOnlyRouteValue<TPathOriginalDefinition>
191
+ get(
192
+ input: Route0._OnlyIfNoParams<TParamsDefinition, { query?: undefined; abs?: false }>,
193
+ ): Route0._OnlyIfNoParams<TParamsDefinition, Route0._PathOnlyRouteValue<TPathOriginalDefinition>>
194
+ get(
195
+ input: Route0._OnlyIfNoParams<TParamsDefinition, { query: Route0._QueryInput<TQueryDefinition>; abs?: false }>,
196
+ ): Route0._OnlyIfNoParams<TParamsDefinition, Route0._WithQueryRouteValue<TPathOriginalDefinition>>
197
+ get(
198
+ input: Route0._OnlyIfNoParams<TParamsDefinition, { query?: undefined; abs: true }>,
199
+ ): Route0._OnlyIfNoParams<TParamsDefinition, Route0._AbsolutePathOnlyRouteValue<TPathOriginalDefinition>>
200
+ get(
201
+ input: Route0._OnlyIfNoParams<TParamsDefinition, { query: Route0._QueryInput<TQueryDefinition>; abs: true }>,
202
+ ): Route0._OnlyIfNoParams<TParamsDefinition, Route0._AbsoluteWithQueryRouteValue<TPathOriginalDefinition>>
203
+
204
+ // implementation
205
+ get(...args: any[]): string {
206
+ const { queryInput, paramsInput, absInput } = ((): {
207
+ queryInput: Record<string, string | number>
208
+ paramsInput: Record<string, string | number>
209
+ absInput: boolean
210
+ } => {
211
+ if (args.length === 0) {
212
+ return { queryInput: {}, paramsInput: {}, absInput: false }
213
+ }
214
+ const input = args[0]
215
+ if (typeof input !== 'object' || input === null) {
216
+ // throw new Error("Invalid get route input: expected object")
217
+ return { queryInput: {}, paramsInput: {}, absInput: false }
218
+ }
219
+ const { query, abs, ...params } = input
220
+ return { queryInput: query || {}, paramsInput: params, absInput: abs ?? false }
221
+ })()
222
+
223
+ // validate params
224
+ const neededParamsKeys = Object.keys(this.paramsDefinition)
225
+ const providedParamsKeys = Object.keys(paramsInput)
226
+ const notProvidedKeys = neededParamsKeys.filter((k) => !providedParamsKeys.includes(k))
227
+ if (notProvidedKeys.length) {
228
+ // throw new Error(`Missing params: not defined keys ${notProvidedKeys.map((k) => `"${k}"`).join(", ")}.`)
229
+ Object.assign(paramsInput, Object.fromEntries(notProvidedKeys.map((k) => [k, 'undefined'])))
230
+ }
231
+
232
+ // create url
233
+ let url = String(this.pathDefinition)
234
+ // replace params
235
+ url = url.replace(/:([A-Za-z0-9_]+)/g, (_m, k) => encodeURIComponent(String(paramsInput?.[k] ?? '')))
236
+ // query params
237
+ const queryInputStringified = Object.fromEntries(Object.entries(queryInput).map(([k, v]) => [k, String(v)]))
238
+ url = [url, new URLSearchParams(queryInputStringified).toString()].filter(Boolean).join('?')
239
+ // dedupe slashes
240
+ url = url.replace(/\/{2,}/g, '/')
241
+ // absolute
242
+ url = absInput ? Route0._getAbsPath(this.baseUrl, url) : url
243
+
244
+ return url
245
+ }
246
+
247
+ getDefinition() {
248
+ return this.pathDefinition
249
+ }
250
+
251
+ clone(config?: Route0.RouteConfigInput) {
252
+ return new Route0(this.pathOriginalDefinition, config)
253
+ }
254
+ }
255
+
256
+ export namespace Route0 {
257
+ export type Callable<T extends Route0<any, any, any, any>> = T & T['get']
258
+ export type RouteConfigInput = {
259
+ baseUrl?: string
260
+ }
261
+ export type Params<TRoute0 extends Route0<any, any, any, any>> = {
262
+ [K in keyof TRoute0['paramsDefinition']]: string
263
+ }
264
+ export type Query<TRoute0 extends Route0<any, any, any, any>> = Partial<
265
+ {
266
+ [K in keyof TRoute0['queryDefinition']]: string | undefined
267
+ } & Record<string, string | undefined>
268
+ >
269
+
270
+ export type _TrimQueryTailDefinition<S extends string> = S extends `${infer P}&${string}` ? P : S
271
+ export type _QueryTailDefinitionWithoutFirstAmp<S extends string> = S extends `${string}&${infer T}` ? T : ''
272
+ export type _QueryTailDefinitionWithFirstAmp<S extends string> = S extends `${string}&${infer T}` ? `&${T}` : ''
273
+ export type _AmpSplit<S extends string> = S extends `${infer A}&${infer B}` ? A | _AmpSplit<B> : S
274
+ export type _NonEmpty<T> = [T] extends ['' | never] ? never : T
275
+ export type _ExtractPathParams<S extends string> = S extends `${string}:${infer After}`
276
+ ? After extends `${infer Name}/${infer Rest}`
277
+ ? Name | _ExtractPathParams<`/${Rest}`>
278
+ : After
279
+ : never
280
+ export type _ReplacePathParams<S extends string> = S extends `${infer Head}:${infer Tail}`
281
+ ? Tail extends `${infer _Param}/${infer Rest}`
282
+ ? _ReplacePathParams<`${Head}${string}/${Rest}`>
283
+ : `${Head}${string}`
284
+ : S
285
+ export type _DedupeSlashes<S extends string> = S extends `${infer A}//${infer B}` ? _DedupeSlashes<`${A}/${B}`> : S
286
+ export type _EmptyRecord = Record<never, never>
287
+ export type _JoinPath<Parent extends string, Suffix extends string> = _DedupeSlashes<
288
+ Route0._PathDefinition<Parent> extends infer A extends string
289
+ ? _PathDefinition<Suffix> extends infer B extends string
290
+ ? A extends ''
291
+ ? B extends ''
292
+ ? ''
293
+ : B extends `/${string}`
294
+ ? B
295
+ : `/${B}`
296
+ : B extends ''
297
+ ? A
298
+ : A extends `${string}/`
299
+ ? `${A}${B}`
300
+ : B extends `/${string}`
301
+ ? `${A}${B}`
302
+ : `${A}/${B}`
303
+ : never
304
+ : never
305
+ >
306
+
307
+ export type _OnlyIfNoParams<TParams extends object, Yes, No = never> = keyof TParams extends never ? Yes : No
308
+ export type _OnlyIfHasParams<TParams extends object, Yes, No = never> = keyof TParams extends never ? No : Yes
309
+
310
+ export type _PathDefinition<TPathOriginalDefinition extends string> =
311
+ _TrimQueryTailDefinition<TPathOriginalDefinition>
312
+ export type _ParamsDefinition<TPathOriginalDefinition extends string> = _ExtractPathParams<
313
+ _PathDefinition<TPathOriginalDefinition>
314
+ > extends infer U
315
+ ? [U] extends [never]
316
+ ? _EmptyRecord
317
+ : { [K in Extract<U, string>]: true }
318
+ : _EmptyRecord
319
+ export type _QueryDefinition<TPathOriginalDefinition extends string> = _NonEmpty<
320
+ _QueryTailDefinitionWithoutFirstAmp<TPathOriginalDefinition>
321
+ > extends infer Tail extends string
322
+ ? _AmpSplit<Tail> extends infer U
323
+ ? [U] extends [never]
324
+ ? _EmptyRecord
325
+ : { [K in Extract<U, string>]: true }
326
+ : _EmptyRecord
327
+ : _EmptyRecord
328
+ export type _RoutePathOriginalDefinitionExtended<
329
+ TSourcePathOriginalDefinition extends string,
330
+ TSuffixPathOriginalDefinition extends string,
331
+ > = `${_JoinPath<TSourcePathOriginalDefinition, TSuffixPathOriginalDefinition>}${_QueryTailDefinitionWithFirstAmp<TSuffixPathOriginalDefinition>}`
332
+
333
+ export type _ParamsInput<TParamsDefinition extends object> = {
334
+ [K in keyof TParamsDefinition]: string | number
335
+ }
336
+ export type _QueryInput<TQueryDefinition extends object> = Partial<{
337
+ [K in keyof TQueryDefinition]: string | number
338
+ }> &
339
+ Record<string, string | number>
340
+ export type _WithParamsInput<
341
+ TParamsDefinition extends object,
342
+ T extends {
343
+ query?: _QueryInput<any>
344
+ abs?: boolean
345
+ },
346
+ > = _ParamsInput<TParamsDefinition> & T
347
+
348
+ export type _PathOnlyRouteValue<TPathOriginalDefinition extends string> =
349
+ `${_ReplacePathParams<_PathDefinition<TPathOriginalDefinition>>}`
350
+ export type _WithQueryRouteValue<TPathOriginalDefinition extends string> =
351
+ `${_ReplacePathParams<_PathDefinition<TPathOriginalDefinition>>}?${string}`
352
+ export type _AbsolutePathOnlyRouteValue<TPathOriginalDefinition extends string> =
353
+ `${string}${_PathOnlyRouteValue<TPathOriginalDefinition>}`
354
+ export type _AbsoluteWithQueryRouteValue<TPathOriginalDefinition extends string> =
355
+ `${string}${_WithQueryRouteValue<TPathOriginalDefinition>}`
356
+ }