@marianmeres/simple-router 3.3.1 → 3.4.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/AGENTS.md +7 -2
- package/API.md +27 -2
- package/README.md +15 -1
- package/dist/route.js +5 -1
- package/dist/router.d.ts +18 -1
- package/dist/router.js +19 -2
- package/package.json +2 -2
package/AGENTS.md
CHANGED
|
@@ -109,8 +109,8 @@ methods:
|
|
|
109
109
|
important: "Routes matched in registration order - first match wins"
|
|
110
110
|
|
|
111
111
|
- name: "exec"
|
|
112
|
-
signature: "(url: string, fallbackFn?: RouteCallback<T
|
|
113
|
-
description: "Execute pattern matching"
|
|
112
|
+
signature: "(url: string, fallbackFn?: RouteCallback<T>, options?: { skipCatchAll?: boolean }) => T | false"
|
|
113
|
+
description: "Execute pattern matching. Pass { skipCatchAll: true } to match only real routes and return false on a miss instead of firing the registered '*' catch-all (an explicit fallbackFn is unaffected)."
|
|
114
114
|
returns: "Callback result (type T), or false if no match"
|
|
115
115
|
|
|
116
116
|
- name: "subscribe"
|
|
@@ -132,6 +132,11 @@ properties:
|
|
|
132
132
|
type: "RouterCurrent"
|
|
133
133
|
readonly: true
|
|
134
134
|
description: "Current router state"
|
|
135
|
+
|
|
136
|
+
- name: "catchAll"
|
|
137
|
+
type: "RouteCallback<T> | null"
|
|
138
|
+
readonly: true
|
|
139
|
+
description: "The registered catch-all ('*') callback, or null. Does NOT execute it — lets a consumer fire the catch-all as a deliberate final step without re-scanning routes."
|
|
135
140
|
```
|
|
136
141
|
|
|
137
142
|
### SimpleRoute
|
package/API.md
CHANGED
|
@@ -112,6 +112,25 @@ console.log(router.current);
|
|
|
112
112
|
// { route: "/user/[id]", params: { id: "123" }, label: null }
|
|
113
113
|
```
|
|
114
114
|
|
|
115
|
+
#### `catchAll`
|
|
116
|
+
|
|
117
|
+
```ts
|
|
118
|
+
get catchAll(): RouteCallback<T> | null
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Returns the registered catch-all (`*`) callback, or `null` if none was registered. Does **not** execute it — this lets a consumer fire the catch-all as a deliberate final step (e.g. after an `exec(url, undefined, { skipCatchAll: true })` pass across several routers) without re-scanning the registered routes.
|
|
122
|
+
|
|
123
|
+
**Returns:** `RouteCallback<T> | null` - The catch-all callback, or `null`
|
|
124
|
+
|
|
125
|
+
**Example:**
|
|
126
|
+
|
|
127
|
+
```ts
|
|
128
|
+
const fallback = router.catchAll;
|
|
129
|
+
if (fallback) {
|
|
130
|
+
const result = fallback(null, "*"); // fire it manually
|
|
131
|
+
}
|
|
132
|
+
```
|
|
133
|
+
|
|
115
134
|
### Methods
|
|
116
135
|
|
|
117
136
|
#### `on()`
|
|
@@ -172,7 +191,7 @@ router.on("*", () => NotFoundPage);
|
|
|
172
191
|
#### `exec()`
|
|
173
192
|
|
|
174
193
|
```ts
|
|
175
|
-
exec(url: string, fallbackFn?: RouteCallback<T
|
|
194
|
+
exec(url: string, fallbackFn?: RouteCallback<T>, options?: { skipCatchAll?: boolean }): T | false
|
|
176
195
|
```
|
|
177
196
|
|
|
178
197
|
Executes pattern matching against the provided string.
|
|
@@ -183,15 +202,18 @@ Executes pattern matching against the provided string.
|
|
|
183
202
|
|------|------|-------------|
|
|
184
203
|
| `url` | `string` | String to match against registered patterns |
|
|
185
204
|
| `fallbackFn` | `RouteCallback<T>` | Optional fallback function if no route matches |
|
|
205
|
+
| `options` | `{ skipCatchAll?: boolean }` | Optional. Set `skipCatchAll: true` to match only real routes and return `false` on a miss instead of firing the registered `*` catch-all. An explicit `fallbackFn` is unaffected. |
|
|
186
206
|
|
|
187
207
|
**Returns:** `T | false` - The value returned by the matched callback (type `T`), or `false` if no match and no catch-all/fallback.
|
|
188
208
|
|
|
189
209
|
**Match Priority:**
|
|
190
210
|
1. First registered matching route
|
|
191
211
|
2. Fallback function (if provided)
|
|
192
|
-
3. Catch-all `"*"` route (if registered)
|
|
212
|
+
3. Catch-all `"*"` route (if registered, unless `skipCatchAll: true`)
|
|
193
213
|
4. Returns `false`
|
|
194
214
|
|
|
215
|
+
When `skipCatchAll` turns a would-be catch-all into a miss, `current` and subscribers receive the same `{ route: null, params: null, label: null }` "no match" state as a true miss.
|
|
216
|
+
|
|
195
217
|
**Example:**
|
|
196
218
|
|
|
197
219
|
```ts
|
|
@@ -210,6 +232,9 @@ router.exec("/unknown", (params, route) => {
|
|
|
210
232
|
// Return values from callbacks
|
|
211
233
|
const component = router.exec("/home");
|
|
212
234
|
render(component);
|
|
235
|
+
|
|
236
|
+
// Skip the internal catch-all (e.g. when fanning a lookup across multiple routers)
|
|
237
|
+
router.exec("/unknown", undefined, { skipCatchAll: true }); // => false, even if "*" is registered
|
|
213
238
|
```
|
|
214
239
|
|
|
215
240
|
---
|
package/README.md
CHANGED
|
@@ -187,7 +187,7 @@ router.on("/raw", (params) => RawPage, { allowQueryParams: false });
|
|
|
187
187
|
|
|
188
188
|
**Important:** Routes are matched in registration order. First match wins! When `SimpleRouter.debug` is enabled, a warning is logged at registration time if a new route would be shadowed by an already-registered one.
|
|
189
189
|
|
|
190
|
-
##### `exec(url, fallbackFn?)`
|
|
190
|
+
##### `exec(url, fallbackFn?, options?)`
|
|
191
191
|
|
|
192
192
|
Execute route matching against a URL.
|
|
193
193
|
|
|
@@ -199,10 +199,15 @@ router.exec("/unknown", () => console.log("Not found"));
|
|
|
199
199
|
|
|
200
200
|
// With query params
|
|
201
201
|
router.exec("/search?q=hello");
|
|
202
|
+
|
|
203
|
+
// Skip the internal "*" catch-all: returns false on a miss even if "*" is registered
|
|
204
|
+
router.exec("/unknown", undefined, { skipCatchAll: true });
|
|
202
205
|
```
|
|
203
206
|
|
|
204
207
|
Returns the value returned by the matched callback, or `false` if no match.
|
|
205
208
|
|
|
209
|
+
The optional `{ skipCatchAll: true }` flag matches **only real routes** and returns `false` on a miss, instead of falling back to the registered `*` catch-all. An explicitly passed `fallbackFn` is unaffected. This is useful when fanning a single lookup across multiple routers ("try real routes in A, then B, … and fire a catch-all only if none matched") — see the [`catchAll`](#catchall) getter for firing the deferred catch-all yourself.
|
|
210
|
+
|
|
206
211
|
##### `subscribe(callback)`
|
|
207
212
|
|
|
208
213
|
Subscribe to router state changes. Follows the [Svelte store contract](https://svelte.dev/docs#Store_contract).
|
|
@@ -249,6 +254,15 @@ console.log(router.current);
|
|
|
249
254
|
// { route: "/user/[id]", params: { id: "123" }, label: null }
|
|
250
255
|
```
|
|
251
256
|
|
|
257
|
+
##### `catchAll`
|
|
258
|
+
|
|
259
|
+
Gets the registered catch-all (`*`) callback, or `null` if none was registered (readonly). Does **not** execute it — this lets you fire the catch-all as a deliberate final step (e.g. after a multi-router `exec(url, undefined, { skipCatchAll: true })` pass) without re-scanning routes.
|
|
260
|
+
|
|
261
|
+
```ts
|
|
262
|
+
const fallback = router.catchAll;
|
|
263
|
+
if (fallback) fallback(null, "*");
|
|
264
|
+
```
|
|
265
|
+
|
|
252
266
|
##### `static debug`
|
|
253
267
|
|
|
254
268
|
Enable/disable debug logging. When enabled, uses the logger instance (if provided) or falls back to `console.log`.
|
package/dist/route.js
CHANGED
|
@@ -175,7 +175,11 @@ export class SimpleRoute {
|
|
|
175
175
|
.replace(/&$/, "")
|
|
176
176
|
.split("&")
|
|
177
177
|
.reduce((memo, kvpair) => {
|
|
178
|
-
|
|
178
|
+
// Split at the FIRST "=" only: a value may itself contain "="
|
|
179
|
+
// (base64 padding, a nested URL, ...).
|
|
180
|
+
const eqPos = kvpair.indexOf("=");
|
|
181
|
+
const k = eqPos === -1 ? kvpair : kvpair.slice(0, eqPos);
|
|
182
|
+
const v = eqPos === -1 ? "" : kvpair.slice(eqPos + 1);
|
|
179
183
|
if (k.length) {
|
|
180
184
|
try {
|
|
181
185
|
const decodedKey = decodeURIComponent(k);
|
package/dist/router.d.ts
CHANGED
|
@@ -213,6 +213,13 @@ export declare class SimpleRouter<T = unknown> {
|
|
|
213
213
|
* ```
|
|
214
214
|
*/
|
|
215
215
|
get current(): RouterCurrent;
|
|
216
|
+
/**
|
|
217
|
+
* The registered catch-all (`*`) callback, or `null` if none was registered.
|
|
218
|
+
* Does NOT execute it — lets a consumer fire the catch-all as a deliberate
|
|
219
|
+
* final step (e.g. after `exec(url, undefined, { skipCatchAll: true })`)
|
|
220
|
+
* without re-scanning the registered routes.
|
|
221
|
+
*/
|
|
222
|
+
get catchAll(): RouteCallback<T> | null;
|
|
216
223
|
/**
|
|
217
224
|
* Registers one or more route patterns with a callback.
|
|
218
225
|
* Routes are matched in the order they are registered (first match wins).
|
|
@@ -251,6 +258,11 @@ export declare class SimpleRouter<T = unknown> {
|
|
|
251
258
|
*
|
|
252
259
|
* @param url - String to match against registered patterns (can be a URL, file path, command, etc.)
|
|
253
260
|
* @param fallbackFn - Optional fallback function if no route matches
|
|
261
|
+
* @param options - Optional execution options. Set `skipCatchAll: true` to match
|
|
262
|
+
* only real routes and return `false` on a miss instead of falling
|
|
263
|
+
* back to the registered `*` catch-all. An explicitly passed
|
|
264
|
+
* `fallbackFn` is unaffected. Useful when fanning a single lookup
|
|
265
|
+
* across multiple routers and deferring the catch-all decision.
|
|
254
266
|
* @returns `T | false` - The value returned by the matched callback, or `false` if no match
|
|
255
267
|
*
|
|
256
268
|
* @example
|
|
@@ -265,6 +277,9 @@ export declare class SimpleRouter<T = unknown> {
|
|
|
265
277
|
* // With fallback
|
|
266
278
|
* router.exec("/unknown", () => NotFoundPage);
|
|
267
279
|
*
|
|
280
|
+
* // Skip the internal catch-all: a miss returns false even if "*" is registered
|
|
281
|
+
* router.exec("/unknown", undefined, { skipCatchAll: true });
|
|
282
|
+
*
|
|
268
283
|
* // Check for no match
|
|
269
284
|
* const component = router.exec("/path");
|
|
270
285
|
* if (component !== false) {
|
|
@@ -272,7 +287,9 @@ export declare class SimpleRouter<T = unknown> {
|
|
|
272
287
|
* }
|
|
273
288
|
* ```
|
|
274
289
|
*/
|
|
275
|
-
exec(url: string, fallbackFn?: RouteCallback<T
|
|
290
|
+
exec(url: string, fallbackFn?: RouteCallback<T>, options?: {
|
|
291
|
+
skipCatchAll?: boolean;
|
|
292
|
+
}): T | false;
|
|
276
293
|
/**
|
|
277
294
|
* Subscribes to router state changes.
|
|
278
295
|
* Follows the Svelte store contract - subscriber is called immediately with current state.
|
package/dist/router.js
CHANGED
|
@@ -150,6 +150,15 @@ export class SimpleRouter {
|
|
|
150
150
|
get current() {
|
|
151
151
|
return this.#current;
|
|
152
152
|
}
|
|
153
|
+
/**
|
|
154
|
+
* The registered catch-all (`*`) callback, or `null` if none was registered.
|
|
155
|
+
* Does NOT execute it — lets a consumer fire the catch-all as a deliberate
|
|
156
|
+
* final step (e.g. after `exec(url, undefined, { skipCatchAll: true })`)
|
|
157
|
+
* without re-scanning the registered routes.
|
|
158
|
+
*/
|
|
159
|
+
get catchAll() {
|
|
160
|
+
return this.#catchAll;
|
|
161
|
+
}
|
|
153
162
|
/**
|
|
154
163
|
* Registers one or more route patterns with a callback.
|
|
155
164
|
* Routes are matched in the order they are registered (first match wins).
|
|
@@ -234,6 +243,11 @@ export class SimpleRouter {
|
|
|
234
243
|
*
|
|
235
244
|
* @param url - String to match against registered patterns (can be a URL, file path, command, etc.)
|
|
236
245
|
* @param fallbackFn - Optional fallback function if no route matches
|
|
246
|
+
* @param options - Optional execution options. Set `skipCatchAll: true` to match
|
|
247
|
+
* only real routes and return `false` on a miss instead of falling
|
|
248
|
+
* back to the registered `*` catch-all. An explicitly passed
|
|
249
|
+
* `fallbackFn` is unaffected. Useful when fanning a single lookup
|
|
250
|
+
* across multiple routers and deferring the catch-all decision.
|
|
237
251
|
* @returns `T | false` - The value returned by the matched callback, or `false` if no match
|
|
238
252
|
*
|
|
239
253
|
* @example
|
|
@@ -248,6 +262,9 @@ export class SimpleRouter {
|
|
|
248
262
|
* // With fallback
|
|
249
263
|
* router.exec("/unknown", () => NotFoundPage);
|
|
250
264
|
*
|
|
265
|
+
* // Skip the internal catch-all: a miss returns false even if "*" is registered
|
|
266
|
+
* router.exec("/unknown", undefined, { skipCatchAll: true });
|
|
267
|
+
*
|
|
251
268
|
* // Check for no match
|
|
252
269
|
* const component = router.exec("/path");
|
|
253
270
|
* if (component !== false) {
|
|
@@ -255,7 +272,7 @@ export class SimpleRouter {
|
|
|
255
272
|
* }
|
|
256
273
|
* ```
|
|
257
274
|
*/
|
|
258
|
-
exec(url, fallbackFn) {
|
|
275
|
+
exec(url, fallbackFn, options) {
|
|
259
276
|
const dbgPrefix = `'${url}' -> `;
|
|
260
277
|
for (const [route, cb, allowQueryParams, label] of this.#routes) {
|
|
261
278
|
// First match wins
|
|
@@ -272,7 +289,7 @@ export class SimpleRouter {
|
|
|
272
289
|
this.#dbg(`${dbgPrefix}fallback...`);
|
|
273
290
|
return fallbackFn(null, "");
|
|
274
291
|
}
|
|
275
|
-
if (typeof this.#catchAll === "function") {
|
|
292
|
+
if (!options?.skipCatchAll && typeof this.#catchAll === "function") {
|
|
276
293
|
this.#publishCurrent("*", null, null);
|
|
277
294
|
this.#dbg(`${dbgPrefix}catchall...`);
|
|
278
295
|
return this.#catchAll(null, "*");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@marianmeres/simple-router",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.4.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "dist/mod.js",
|
|
6
6
|
"types": "dist/mod.d.ts",
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"author": "Marian Meres",
|
|
22
22
|
"license": "MIT",
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@marianmeres/pubsub": "^3.
|
|
24
|
+
"@marianmeres/pubsub": "^3.1.0"
|
|
25
25
|
},
|
|
26
26
|
"repository": {
|
|
27
27
|
"type": "git",
|