@marianmeres/simple-router 2.1.0 → 2.2.2
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 +21 -0
- package/README.md +347 -66
- package/dist/mod.d.ts +4 -0
- package/dist/mod.js +3 -0
- package/dist/route.d.ts +88 -9
- package/dist/route.js +248 -0
- package/dist/router.d.ts +241 -28
- package/dist/router.js +255 -0
- package/package.json +9 -40
- package/dist/index.cjs +0 -251
- package/dist/index.d.ts +0 -2
- package/dist/index.js +0 -248
package/dist/route.js
ADDED
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
export class SimpleRoute {
|
|
2
|
+
route;
|
|
3
|
+
/** Separator used to split URL paths into segments */
|
|
4
|
+
static SPLITTER = "/";
|
|
5
|
+
/** Wildcard symbol for catch-all routes */
|
|
6
|
+
static WILDCARD = "*";
|
|
7
|
+
#parsed;
|
|
8
|
+
/**
|
|
9
|
+
* Creates a new SimpleRoute instance.
|
|
10
|
+
*
|
|
11
|
+
* @param route - Pattern string to match against (URL, file path, command, or any string identifier)
|
|
12
|
+
* @throws {Error} If route pattern is invalid (e.g., multiple spread segments)
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```ts
|
|
16
|
+
* // URL patterns
|
|
17
|
+
* new SimpleRoute("/user/[id]");
|
|
18
|
+
* new SimpleRoute("/post/[id([0-9]+)]");
|
|
19
|
+
* new SimpleRoute("/api/[...path]");
|
|
20
|
+
*
|
|
21
|
+
* // File path patterns
|
|
22
|
+
* new SimpleRoute("src/[module]/[file].ts");
|
|
23
|
+
*
|
|
24
|
+
* // Command patterns
|
|
25
|
+
* new SimpleRoute("user:delete:[id]");
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
constructor(route) {
|
|
29
|
+
this.route = route;
|
|
30
|
+
this.#parsed = SimpleRoute.#parse(route);
|
|
31
|
+
}
|
|
32
|
+
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions
|
|
33
|
+
static #escapeRegExp(str) {
|
|
34
|
+
return str.replace(/[.*+\-?^${}()|[\]\\]/g, "\\$&");
|
|
35
|
+
}
|
|
36
|
+
static #sanitizeAndSplit(str) {
|
|
37
|
+
const s = SimpleRoute.#escapeRegExp(SimpleRoute.SPLITTER);
|
|
38
|
+
return (`${str}`
|
|
39
|
+
.trim()
|
|
40
|
+
// Trim splitters from left and right
|
|
41
|
+
.replace(new RegExp(`^(${s})+`), "")
|
|
42
|
+
.replace(new RegExp(`(${s})+$`), "")
|
|
43
|
+
.split(SimpleRoute.SPLITTER)
|
|
44
|
+
// Remove empty segments (normalizes multiple splitters into one)
|
|
45
|
+
.filter(Boolean));
|
|
46
|
+
}
|
|
47
|
+
static #parse(route) {
|
|
48
|
+
let wasSpread = false;
|
|
49
|
+
let wasWildcard = false;
|
|
50
|
+
return SimpleRoute.#sanitizeAndSplit(route).reduce((memo, segment, idx, all) => {
|
|
51
|
+
let name = null;
|
|
52
|
+
// If optional, remove trailing '?' marker
|
|
53
|
+
let isOptional = segment.endsWith("?");
|
|
54
|
+
if (isOptional)
|
|
55
|
+
segment = segment.slice(0, -1);
|
|
56
|
+
const isSpread = segment.startsWith("[...");
|
|
57
|
+
if (isSpread) {
|
|
58
|
+
if (isOptional) {
|
|
59
|
+
throw new Error("Spread segment must not be marked as optional");
|
|
60
|
+
}
|
|
61
|
+
if (wasSpread) {
|
|
62
|
+
throw new Error("Multiple spread segments are invalid");
|
|
63
|
+
}
|
|
64
|
+
wasSpread = true;
|
|
65
|
+
segment = "[" + segment.slice(4);
|
|
66
|
+
}
|
|
67
|
+
let test = new RegExp("^" + SimpleRoute.#escapeRegExp(segment) + "$");
|
|
68
|
+
// Catch-all wildcard
|
|
69
|
+
if (segment === SimpleRoute.WILDCARD) {
|
|
70
|
+
if (idx < all.length - 1) {
|
|
71
|
+
throw new Error(`Wildcard '${SimpleRoute.WILDCARD}' can be used only as a last segment`);
|
|
72
|
+
}
|
|
73
|
+
wasWildcard = true;
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
// Starts with at least one word character within brackets
|
|
77
|
+
const m = segment.match(/^\[(\w.*)]$/);
|
|
78
|
+
if (m) {
|
|
79
|
+
name = m[1];
|
|
80
|
+
test = /.+/;
|
|
81
|
+
// id([0-9]+)
|
|
82
|
+
const m2 = m[1].match(/^(\w.*)\((.+)\)$/);
|
|
83
|
+
if (m2) {
|
|
84
|
+
name = m2[1];
|
|
85
|
+
try {
|
|
86
|
+
test = new RegExp("^" + m2[2] + "$");
|
|
87
|
+
}
|
|
88
|
+
catch (e) {
|
|
89
|
+
throw new Error(`Invalid regex in route pattern '${segment}': ${e.message}`);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
if (wasWildcard) {
|
|
95
|
+
isOptional = true;
|
|
96
|
+
test = /.*/;
|
|
97
|
+
}
|
|
98
|
+
memo.push({ segment, name, test, isOptional, isSpread });
|
|
99
|
+
return memo;
|
|
100
|
+
}, []);
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Parses a query string into an object of key-value pairs.
|
|
104
|
+
* Handles URL decoding of both keys and values.
|
|
105
|
+
*
|
|
106
|
+
* @param str - Query string (without leading "?")
|
|
107
|
+
* @returns Object with parsed query parameters
|
|
108
|
+
*
|
|
109
|
+
* @example
|
|
110
|
+
* ```ts
|
|
111
|
+
* SimpleRoute.parseQueryString("foo=bar&baz=123");
|
|
112
|
+
* // Returns: { foo: "bar", baz: "123" }
|
|
113
|
+
*
|
|
114
|
+
* SimpleRoute.parseQueryString("name=John%20Doe");
|
|
115
|
+
* // Returns: { name: "John Doe" }
|
|
116
|
+
* ```
|
|
117
|
+
*/
|
|
118
|
+
static parseQueryString(str) {
|
|
119
|
+
return `${str}`
|
|
120
|
+
.replace(/^&/, "")
|
|
121
|
+
.replace(/&$/, "")
|
|
122
|
+
.split("&")
|
|
123
|
+
.reduce((memo, kvpair) => {
|
|
124
|
+
const [k, v = ""] = kvpair.split("=");
|
|
125
|
+
if (k.length) {
|
|
126
|
+
try {
|
|
127
|
+
const decodedKey = decodeURIComponent(k);
|
|
128
|
+
const decodedValue = v ? decodeURIComponent(v) : "";
|
|
129
|
+
memo[decodedKey] = decodedValue;
|
|
130
|
+
}
|
|
131
|
+
catch {
|
|
132
|
+
// Fallback for malformed URI components
|
|
133
|
+
memo[k] = v;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
return memo;
|
|
137
|
+
}, {});
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Parses a string against this route pattern.
|
|
141
|
+
* Returns extracted parameters if the string matches, null otherwise.
|
|
142
|
+
*
|
|
143
|
+
* @param url - String to test against the pattern (can be a URL, file path, command, etc.)
|
|
144
|
+
* @param allowQueryParams - Whether to parse query string parameters (default: true)
|
|
145
|
+
* @returns Object with extracted parameters, or null if no match
|
|
146
|
+
*
|
|
147
|
+
* @example
|
|
148
|
+
* ```ts
|
|
149
|
+
* // URL matching
|
|
150
|
+
* const route = new SimpleRoute("/user/[id]");
|
|
151
|
+
*
|
|
152
|
+
* route.parse("/user/123");
|
|
153
|
+
* // Returns: { id: "123" }
|
|
154
|
+
*
|
|
155
|
+
* route.parse("/user/123?tab=profile");
|
|
156
|
+
* // Returns: { id: "123", tab: "profile" }
|
|
157
|
+
*
|
|
158
|
+
* route.parse("/user/123?tab=profile", false);
|
|
159
|
+
* // Returns: { id: "123?tab=profile" } - query string not parsed
|
|
160
|
+
*
|
|
161
|
+
* route.parse("/post/456");
|
|
162
|
+
* // Returns: null - no match
|
|
163
|
+
*
|
|
164
|
+
* // File path matching
|
|
165
|
+
* const fileRoute = new SimpleRoute("src/[module]/[file].ts");
|
|
166
|
+
* fileRoute.parse("src/components/Button.ts");
|
|
167
|
+
* // Returns: { module: "components", file: "Button" }
|
|
168
|
+
*
|
|
169
|
+
* // Command matching
|
|
170
|
+
* const cmdRoute = new SimpleRoute("user:delete:[id]");
|
|
171
|
+
* cmdRoute.parse("user:delete:123");
|
|
172
|
+
* // Returns: { id: "123" }
|
|
173
|
+
* ```
|
|
174
|
+
*/
|
|
175
|
+
parse(url, allowQueryParams = true) {
|
|
176
|
+
let matched = {};
|
|
177
|
+
const qPos = url.indexOf("?");
|
|
178
|
+
if (allowQueryParams && qPos !== -1) {
|
|
179
|
+
const _backup = url;
|
|
180
|
+
url = _backup.slice(0, qPos);
|
|
181
|
+
matched = SimpleRoute.parseQueryString(_backup.slice(qPos + 1));
|
|
182
|
+
}
|
|
183
|
+
let segments = SimpleRoute.#sanitizeAndSplit(url);
|
|
184
|
+
// If there are spread definitions, we need to adjust input by grouping
|
|
185
|
+
// (joining) segments that were initially split
|
|
186
|
+
const hasSpread = this.#parsed.some((v) => v.isSpread);
|
|
187
|
+
if (hasSpread) {
|
|
188
|
+
let newSegments = [];
|
|
189
|
+
this.#parsed.forEach((p, i) => {
|
|
190
|
+
if (p.isSpread) {
|
|
191
|
+
// There are defined segments after the spread definition
|
|
192
|
+
if (this.#parsed[i + 1]) {
|
|
193
|
+
newSegments.push(segments
|
|
194
|
+
.slice(0, this.#parsed.length - i)
|
|
195
|
+
.join(SimpleRoute.SPLITTER));
|
|
196
|
+
segments = segments.slice(this.#parsed.length - i);
|
|
197
|
+
}
|
|
198
|
+
// There are no more defined segments
|
|
199
|
+
else {
|
|
200
|
+
newSegments.push(segments.join(SimpleRoute.SPLITTER));
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
else {
|
|
204
|
+
newSegments = newSegments.concat(segments.slice(0, 1));
|
|
205
|
+
segments = segments.slice(1);
|
|
206
|
+
}
|
|
207
|
+
});
|
|
208
|
+
segments = newSegments;
|
|
209
|
+
}
|
|
210
|
+
// Return early (special case): first segment is a wildcard
|
|
211
|
+
// (no need to process further)
|
|
212
|
+
if (this.#parsed?.[0]?.segment === SimpleRoute.WILDCARD) {
|
|
213
|
+
return matched;
|
|
214
|
+
}
|
|
215
|
+
// Minimum required (non-optional) segment count
|
|
216
|
+
const reqLen = this.#parsed.reduce((memo, p, idx, arr) => {
|
|
217
|
+
const next = arr[idx + 1];
|
|
218
|
+
// If segment is not optional or has non-optional segments still to come
|
|
219
|
+
if (!p.isOptional || (next && !next.isOptional)) {
|
|
220
|
+
memo++;
|
|
221
|
+
}
|
|
222
|
+
return memo;
|
|
223
|
+
}, 0);
|
|
224
|
+
// Quick check: if counts don't match, no match
|
|
225
|
+
if (segments.length < reqLen) {
|
|
226
|
+
return null;
|
|
227
|
+
}
|
|
228
|
+
for (const [i, s] of segments.entries()) {
|
|
229
|
+
const p = this.#parsed[i];
|
|
230
|
+
if (!p || !p.test.test(s)) {
|
|
231
|
+
return null;
|
|
232
|
+
}
|
|
233
|
+
if (p.name) {
|
|
234
|
+
try {
|
|
235
|
+
matched[decodeURIComponent(p.name)] = decodeURIComponent(s);
|
|
236
|
+
}
|
|
237
|
+
catch {
|
|
238
|
+
// Fallback for malformed URI components
|
|
239
|
+
matched[p.name] = s;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
if (p.segment === SimpleRoute.WILDCARD) {
|
|
243
|
+
break;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
return matched;
|
|
247
|
+
}
|
|
248
|
+
}
|
package/dist/router.d.ts
CHANGED
|
@@ -1,33 +1,246 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Route parameters extracted from the URL path and query string.
|
|
3
|
+
* Keys are parameter names, values are the extracted string values.
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* ```ts
|
|
7
|
+
* // For route "/user/[id]" matching "/user/123"
|
|
8
|
+
* const params: RouteParams = { id: "123" };
|
|
9
|
+
* ```
|
|
10
|
+
*/
|
|
11
|
+
export type RouteParams = Record<string, any>;
|
|
12
|
+
/**
|
|
13
|
+
* Callback function executed when a route matches.
|
|
14
|
+
*
|
|
15
|
+
* @param params - Extracted route parameters (null if no match)
|
|
16
|
+
* @param route - The matched route pattern string
|
|
17
|
+
* @returns Any value - useful for returning components, data, or control flow values
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```ts
|
|
21
|
+
* const callback: RouteCallback = (params, route) => {
|
|
22
|
+
* console.log(`Matched ${route} with params:`, params);
|
|
23
|
+
* return MyComponent;
|
|
24
|
+
* };
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
export type RouteCallback = (params: RouteParams | null, route: string) => any;
|
|
28
|
+
/**
|
|
29
|
+
* Represents the current router state including the matched route,
|
|
30
|
+
* extracted parameters, and optional label.
|
|
31
|
+
*/
|
|
32
|
+
export interface RouterCurrent {
|
|
33
|
+
/** The matched route pattern, "*" for catch-all, or null if no match */
|
|
34
|
+
route: string | null;
|
|
35
|
+
/** Extracted parameters from the URL, or null if no match */
|
|
36
|
+
params: RouteParams | null;
|
|
37
|
+
/** Optional label associated with the route for debugging */
|
|
38
|
+
label: string | null;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Options for registering routes with the `on()` method.
|
|
42
|
+
*/
|
|
43
|
+
export interface RouterOnOptions {
|
|
44
|
+
/** Optional label for debugging - useful for identifying routes */
|
|
45
|
+
label?: string | null;
|
|
46
|
+
/** Whether to parse query parameters (default: true) */
|
|
47
|
+
allowQueryParams?: boolean;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Configuration object for initializing the router with routes.
|
|
51
|
+
* Maps route patterns to their callback functions.
|
|
52
|
+
*
|
|
53
|
+
* @example
|
|
54
|
+
* ```ts
|
|
55
|
+
* const config: RouterConfig = {
|
|
56
|
+
* "/": () => HomePage,
|
|
57
|
+
* "/user/[id]": (params) => UserPage(params?.id),
|
|
58
|
+
* "*": () => NotFoundPage
|
|
59
|
+
* };
|
|
60
|
+
* ```
|
|
61
|
+
*/
|
|
62
|
+
export type RouterConfig = Record<string, RouteCallback>;
|
|
63
|
+
/**
|
|
64
|
+
* Subscription object returned by the `subscribe()` method.
|
|
65
|
+
* Follows the Svelte store contract and RxJS Observable interface.
|
|
66
|
+
*/
|
|
67
|
+
export interface RouterSubscription {
|
|
68
|
+
/** Unsubscribe from router state changes */
|
|
69
|
+
unsubscribe: () => void;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Subscriber callback function that receives router state changes.
|
|
73
|
+
*
|
|
74
|
+
* @param current - The current router state
|
|
75
|
+
*/
|
|
76
|
+
export type RouterSubscriber = (current: RouterCurrent) => void;
|
|
77
|
+
/**
|
|
78
|
+
* A simple, framework-agnostic string pattern matcher and router with support
|
|
79
|
+
* for dynamic parameters, wildcards, query strings, and reactive subscriptions.
|
|
80
|
+
*
|
|
81
|
+
* Can match any string identifiers - URLs, file paths, command names, or custom patterns.
|
|
82
|
+
* Primarily designed for client-side SPA routing, but flexible enough for any pattern
|
|
83
|
+
* matching needs.
|
|
84
|
+
*
|
|
85
|
+
* @example
|
|
86
|
+
* ```ts
|
|
87
|
+
* import { SimpleRouter } from "@marianmeres/simple-router";
|
|
88
|
+
*
|
|
89
|
+
* // URL routing
|
|
90
|
+
* const router = new SimpleRouter({
|
|
91
|
+
* "/": () => console.log("Home"),
|
|
92
|
+
* "/user/[id]": (params) => console.log("User:", params?.id),
|
|
93
|
+
* "*": () => console.log("Not found")
|
|
94
|
+
* });
|
|
95
|
+
*
|
|
96
|
+
* router.exec("/user/123"); // Logs: "User: 123"
|
|
97
|
+
*
|
|
98
|
+
* // Command routing
|
|
99
|
+
* const cmdRouter = new SimpleRouter({
|
|
100
|
+
* "user:create": () => createUser(),
|
|
101
|
+
* "user:delete:[id]": ({ id }) => deleteUser(id),
|
|
102
|
+
* });
|
|
103
|
+
*
|
|
104
|
+
* cmdRouter.exec("user:delete:123");
|
|
105
|
+
* ```
|
|
106
|
+
*/
|
|
2
107
|
export declare class SimpleRouter {
|
|
108
|
+
#private;
|
|
109
|
+
/** Enable/disable console debug logging for route matching */
|
|
3
110
|
static debug: boolean;
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
111
|
+
/**
|
|
112
|
+
* Creates a new SimpleRouter instance.
|
|
113
|
+
*
|
|
114
|
+
* @param config - Optional configuration object mapping route patterns to callbacks
|
|
115
|
+
*
|
|
116
|
+
* @example
|
|
117
|
+
* ```ts
|
|
118
|
+
* const router = new SimpleRouter({
|
|
119
|
+
* "/": () => HomePage,
|
|
120
|
+
* "/about": () => AboutPage
|
|
121
|
+
* });
|
|
122
|
+
* ```
|
|
123
|
+
*/
|
|
124
|
+
constructor(config?: RouterConfig | null);
|
|
125
|
+
/**
|
|
126
|
+
* Returns a map of registered route patterns to their labels.
|
|
127
|
+
* Useful for debugging and introspection.
|
|
128
|
+
*
|
|
129
|
+
* @returns Object mapping route patterns to labels
|
|
130
|
+
*
|
|
131
|
+
* @example
|
|
132
|
+
* ```ts
|
|
133
|
+
* router.on("/users", () => {}, { label: "users-list" });
|
|
134
|
+
* console.log(router.info()); // { "/users": "users-list" }
|
|
135
|
+
* ```
|
|
136
|
+
*/
|
|
17
137
|
info(): Record<string, string>;
|
|
138
|
+
/**
|
|
139
|
+
* Clears all registered routes (except catch-all).
|
|
140
|
+
* Useful for testing or dynamic route reconfiguration.
|
|
141
|
+
*
|
|
142
|
+
* @returns The router instance for method chaining
|
|
143
|
+
*
|
|
144
|
+
* @example
|
|
145
|
+
* ```ts
|
|
146
|
+
* router.reset().on("/new-route", () => {});
|
|
147
|
+
* ```
|
|
148
|
+
*/
|
|
18
149
|
reset(): this;
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
150
|
+
/**
|
|
151
|
+
* Gets the current router state (last matched route and params).
|
|
152
|
+
*
|
|
153
|
+
* @returns Current router state
|
|
154
|
+
*
|
|
155
|
+
* @example
|
|
156
|
+
* ```ts
|
|
157
|
+
* router.exec("/user/123");
|
|
158
|
+
* console.log(router.current); // { route: "/user/[id]", params: { id: "123" }, label: null }
|
|
159
|
+
* ```
|
|
160
|
+
*/
|
|
161
|
+
get current(): RouterCurrent;
|
|
162
|
+
/**
|
|
163
|
+
* Registers one or more route patterns with a callback.
|
|
164
|
+
* Routes are matched in the order they are registered (first match wins).
|
|
165
|
+
* Use "*" as a catch-all route.
|
|
166
|
+
*
|
|
167
|
+
* @param routes - Single route pattern or array of patterns
|
|
168
|
+
* @param cb - Callback function to execute when route matches
|
|
169
|
+
* @param options - Optional configuration (label, allowQueryParams)
|
|
170
|
+
*
|
|
171
|
+
* @example
|
|
172
|
+
* ```ts
|
|
173
|
+
* // Single route
|
|
174
|
+
* router.on("/users", () => UsersPage);
|
|
175
|
+
*
|
|
176
|
+
* // Multiple routes to same handler
|
|
177
|
+
* router.on(["/", "/home", "/index.html"], () => HomePage);
|
|
178
|
+
*
|
|
179
|
+
* // With dynamic parameters
|
|
180
|
+
* router.on("/user/[id]", (params) => UserPage(params?.id));
|
|
181
|
+
*
|
|
182
|
+
* // With regex constraint
|
|
183
|
+
* router.on("/post/[id([0-9]+)]", (params) => PostPage(params?.id));
|
|
184
|
+
*
|
|
185
|
+
* // With label for debugging
|
|
186
|
+
* router.on("/admin", () => AdminPage, { label: "admin-dashboard" });
|
|
187
|
+
*
|
|
188
|
+
* // Catch-all route
|
|
189
|
+
* router.on("*", () => NotFoundPage);
|
|
190
|
+
* ```
|
|
191
|
+
*/
|
|
192
|
+
on(routes: string | string[], cb: RouteCallback, { label, allowQueryParams }?: RouterOnOptions): void;
|
|
193
|
+
/**
|
|
194
|
+
* Executes pattern matching against the provided string.
|
|
195
|
+
* Returns the value returned by the matched route's callback.
|
|
196
|
+
* Routes are tested in registration order - first match wins.
|
|
197
|
+
*
|
|
198
|
+
* @param url - String to match against registered patterns (can be a URL, file path, command, etc.)
|
|
199
|
+
* @param fallbackFn - Optional fallback function if no route matches
|
|
200
|
+
* @returns The value returned by the matched callback, or false if no match
|
|
201
|
+
*
|
|
202
|
+
* @example
|
|
203
|
+
* ```ts
|
|
204
|
+
* // URL matching
|
|
205
|
+
* router.exec("/users"); // Returns result of callback
|
|
206
|
+
*
|
|
207
|
+
* // With query parameters
|
|
208
|
+
* router.exec("/users?sort=name");
|
|
209
|
+
*
|
|
210
|
+
* // File path matching
|
|
211
|
+
* router.exec("src/components/Button.ts");
|
|
212
|
+
*
|
|
213
|
+
* // Command matching
|
|
214
|
+
* router.exec("user:delete:123");
|
|
215
|
+
*
|
|
216
|
+
* // With fallback
|
|
217
|
+
* router.exec("/unknown", () => console.log("Not found"));
|
|
218
|
+
*
|
|
219
|
+
* // Can return components, values, etc.
|
|
220
|
+
* const component = router.exec("/home");
|
|
221
|
+
* ```
|
|
222
|
+
*/
|
|
223
|
+
exec(url: string, fallbackFn?: RouteCallback): any;
|
|
224
|
+
/**
|
|
225
|
+
* Subscribes to router state changes.
|
|
226
|
+
* Follows the Svelte store contract - subscriber is called immediately with current state.
|
|
227
|
+
* Compatible with RxJS Observables (returns object with `unsubscribe` method).
|
|
228
|
+
*
|
|
229
|
+
* @param subscription - Callback function that receives router state changes
|
|
230
|
+
* @returns Subscription object with `unsubscribe()` method
|
|
231
|
+
*
|
|
232
|
+
* @example
|
|
233
|
+
* ```ts
|
|
234
|
+
* const { unsubscribe } = router.subscribe((state) => {
|
|
235
|
+
* console.log("Route changed:", state.route);
|
|
236
|
+
* console.log("Params:", state.params);
|
|
237
|
+
* });
|
|
238
|
+
*
|
|
239
|
+
* // Later, to unsubscribe
|
|
240
|
+
* unsubscribe();
|
|
241
|
+
* ```
|
|
242
|
+
*
|
|
243
|
+
* @see https://svelte.dev/docs#Store_contract
|
|
244
|
+
*/
|
|
245
|
+
subscribe(subscription: RouterSubscriber): RouterSubscription;
|
|
33
246
|
}
|