@marianmeres/simple-router 2.2.3 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,5 +1,8 @@
1
1
  # @marianmeres/simple-router
2
2
 
3
+ [![NPM version](https://img.shields.io/npm/v/@marianmeres/simple-router)](https://www.npmjs.com/package/@marianmeres/simple-router)
4
+ [![JSR version](https://jsr.io/badges/@marianmeres/simple-router)](https://jsr.io/@marianmeres/simple-router)
5
+
3
6
  A lightweight, framework-agnostic string pattern matcher and router with support for dynamic parameters, wildcards, query strings, and reactive subscriptions.
4
7
 
5
8
  Can match any string identifiers - URLs, file paths, command names, or custom patterns. Originally inspired by [Sapper-like regex routes](https://sapper.svelte.dev/docs#Regexes_in_routes). Primarily designed for client-side SPA routing, but flexible enough for any pattern matching needs.
@@ -100,15 +103,30 @@ The default separator is `/`. Multiple separators are normalized to single, and
100
103
 
101
104
  ## API Reference
102
105
 
106
+ For the complete API documentation with all methods, types, and detailed examples, see [API.md](API.md).
107
+
103
108
  ### SimpleRouter
104
109
 
105
110
  #### Constructor
106
111
 
107
112
  ```ts
108
- const router = new SimpleRouter(config?);
113
+ // Simple config (backwards compatible)
114
+ const router = new SimpleRouter({
115
+ "/": () => HomePage,
116
+ "/about": () => AboutPage
117
+ });
118
+
119
+ // With options object (for logger support)
120
+ const router = new SimpleRouter({
121
+ routes: {
122
+ "/": () => HomePage,
123
+ "/about": () => AboutPage
124
+ },
125
+ logger: myLogger // optional, compatible with @marianmeres/clog
126
+ });
109
127
  ```
110
128
 
111
- - `config` - Optional object mapping route patterns to callbacks
129
+ - `config` - Either a `RouterConfig` object mapping route patterns to callbacks, or a `RouterOptions` object with `routes` and `logger` properties
112
130
 
113
131
  #### Methods
114
132
 
@@ -162,7 +180,7 @@ Returns the value returned by the matched callback, or `false` if no match.
162
180
  Subscribe to router state changes. Follows the [Svelte store contract](https://svelte.dev/docs#Store_contract).
163
181
 
164
182
  ```ts
165
- const { unsubscribe } = router.subscribe((state) => {
183
+ const unsubscribe = router.subscribe((state) => {
166
184
  console.log("Route:", state.route);
167
185
  console.log("Params:", state.params);
168
186
  console.log("Label:", state.label);
@@ -172,7 +190,7 @@ const { unsubscribe } = router.subscribe((state) => {
172
190
  unsubscribe();
173
191
  ```
174
192
 
175
- The callback is called immediately with the current state, then on every route change.
193
+ Returns an unsubscribe function directly. The callback is called immediately with the current state, then on every route change.
176
194
 
177
195
  ##### `reset()`
178
196
 
@@ -205,11 +223,11 @@ console.log(router.current);
205
223
 
206
224
  ##### `static debug`
207
225
 
208
- Enable/disable debug logging.
226
+ Enable/disable debug logging. When enabled, uses the logger instance (if provided) or falls back to `console.log`.
209
227
 
210
228
  ```ts
211
229
  SimpleRouter.debug = true;
212
- router.exec("/test"); // Logs matching details to console
230
+ router.exec("/test"); // Logs matching details to console (or custom logger)
213
231
  ```
214
232
 
215
233
  ### SimpleRoute
@@ -241,13 +259,15 @@ Full TypeScript support with exported types:
241
259
 
242
260
  ```ts
243
261
  import type {
262
+ Logger,
244
263
  RouteParams,
245
264
  RouteCallback,
246
265
  RouterConfig,
247
266
  RouterCurrent,
248
267
  RouterOnOptions,
268
+ RouterOptions,
249
269
  RouterSubscriber,
250
- RouterSubscription,
270
+ RouterUnsubscribe,
251
271
  } from "@marianmeres/simple-router";
252
272
  ```
253
273
 
@@ -346,15 +366,4 @@ dotRouter.exec("app.settings.profile");
346
366
  ```
347
367
 
348
368
 
349
- ## Links
350
-
351
- - [Tests](tests/) - Comprehensive test suite with 82+ tests
352
- - [GitHub Repository](https://github.com/marianmeres/simple-router)
353
- - [JSR Package](https://jsr.io/@marianmeres/simple-router)
354
-
355
- ## Package Identity
356
369
 
357
- - **Name:** @marianmeres/simple-router
358
- - **Author:** Marian Meres
359
- - **Repository:** https://github.com/marianmeres/simple-router
360
- - **License:** MIT
package/dist/mod.d.ts CHANGED
@@ -1,4 +1,4 @@
1
1
  export { SimpleRoute } from "./route.js";
2
2
  export { SimpleRouter } from "./router.js";
3
3
  export type { RouteConfig, RouteParams } from "./route.js";
4
- export type { RouteCallback, RouterConfig, RouterCurrent, RouterOnOptions, RouterSubscriber, RouterSubscription, } from "./router.js";
4
+ export type { Logger, RouteCallback, RouterConfig, RouterCurrent, RouterOnOptions, RouterOptions, RouterSubscriber, RouterUnsubscribe, } from "./router.js";
package/dist/route.d.ts CHANGED
@@ -16,9 +16,15 @@ export type RouteParams = Record<string, any>;
16
16
  export declare class SimpleRoute {
17
17
  #private;
18
18
  readonly route: string;
19
- /** Separator used to split URL paths into segments */
19
+ /**
20
+ * Separator used to split paths into segments.
21
+ * @default "/"
22
+ */
20
23
  static SPLITTER: string;
21
- /** Wildcard symbol for catch-all routes */
24
+ /**
25
+ * Wildcard symbol for catch-all routes. Must be used as the last segment.
26
+ * @default "*"
27
+ */
22
28
  static WILDCARD: string;
23
29
  /**
24
30
  * Creates a new SimpleRoute instance.
package/dist/route.js CHANGED
@@ -1,8 +1,14 @@
1
1
  export class SimpleRoute {
2
2
  route;
3
- /** Separator used to split URL paths into segments */
3
+ /**
4
+ * Separator used to split paths into segments.
5
+ * @default "/"
6
+ */
4
7
  static SPLITTER = "/";
5
- /** Wildcard symbol for catch-all routes */
8
+ /**
9
+ * Wildcard symbol for catch-all routes. Must be used as the last segment.
10
+ * @default "*"
11
+ */
6
12
  static WILDCARD = "*";
7
13
  #parsed;
8
14
  /**
package/dist/router.d.ts CHANGED
@@ -61,13 +61,33 @@ export interface RouterOnOptions {
61
61
  */
62
62
  export type RouterConfig = Record<string, RouteCallback>;
63
63
  /**
64
- * Subscription object returned by the `subscribe()` method.
65
- * Follows the Svelte store contract and RxJS Observable interface.
64
+ * Logger interface compatible with `@marianmeres/clog`.
65
+ * Provides console-compatible logging methods.
66
66
  */
67
- export interface RouterSubscription {
68
- /** Unsubscribe from router state changes */
69
- unsubscribe: () => void;
67
+ export interface Logger {
68
+ /** Log debug level messages */
69
+ debug: (...args: unknown[]) => unknown;
70
+ /** Log info level messages */
71
+ log: (...args: unknown[]) => unknown;
72
+ /** Log warning level messages */
73
+ warn: (...args: unknown[]) => unknown;
74
+ /** Log error level messages */
75
+ error: (...args: unknown[]) => unknown;
70
76
  }
77
+ /**
78
+ * Options for the SimpleRouter factory/constructor.
79
+ */
80
+ export interface RouterOptions {
81
+ /** Route configuration mapping patterns to callbacks */
82
+ routes?: RouterConfig | null;
83
+ /** Optional logger instance for debug output (compatible with @marianmeres/clog) */
84
+ logger?: Logger | null;
85
+ }
86
+ /**
87
+ * Unsubscribe function returned by the `subscribe()` method.
88
+ * Follows the Svelte store contract.
89
+ */
90
+ export type RouterUnsubscribe = () => void;
71
91
  /**
72
92
  * Subscriber callback function that receives router state changes.
73
93
  *
@@ -106,22 +126,37 @@ export type RouterSubscriber = (current: RouterCurrent) => void;
106
126
  */
107
127
  export declare class SimpleRouter {
108
128
  #private;
109
- /** Enable/disable console debug logging for route matching */
129
+ /**
130
+ * Enable/disable console debug logging for route matching.
131
+ * When enabled, logs route matching details to the console.
132
+ * @default false
133
+ */
110
134
  static debug: boolean;
111
135
  /**
112
136
  * Creates a new SimpleRouter instance.
113
137
  *
114
- * @param config - Optional configuration object mapping route patterns to callbacks
138
+ * @param config - Optional configuration: either a RouterConfig object mapping route patterns
139
+ * to callbacks, or a RouterOptions object with routes and logger
115
140
  *
116
141
  * @example
117
142
  * ```ts
143
+ * // Simple config (backwards compatible)
118
144
  * const router = new SimpleRouter({
119
145
  * "/": () => HomePage,
120
146
  * "/about": () => AboutPage
121
147
  * });
148
+ *
149
+ * // With options object
150
+ * const router = new SimpleRouter({
151
+ * routes: {
152
+ * "/": () => HomePage,
153
+ * "/about": () => AboutPage
154
+ * },
155
+ * logger: myLogger // optional, compatible with @marianmeres/clog
156
+ * });
122
157
  * ```
123
158
  */
124
- constructor(config?: RouterConfig | null);
159
+ constructor(config?: RouterConfig | RouterOptions | null);
125
160
  /**
126
161
  * Returns a map of registered route patterns to their labels.
127
162
  * Useful for debugging and introspection.
@@ -189,7 +224,7 @@ export declare class SimpleRouter {
189
224
  * router.on("*", () => NotFoundPage);
190
225
  * ```
191
226
  */
192
- on(routes: string | string[], cb: RouteCallback, { label, allowQueryParams }?: RouterOnOptions): void;
227
+ on(routes: string | string[], cb: RouteCallback, options?: RouterOnOptions): void;
193
228
  /**
194
229
  * Executes pattern matching against the provided string.
195
230
  * Returns the value returned by the matched route's callback.
@@ -220,18 +255,17 @@ export declare class SimpleRouter {
220
255
  * const component = router.exec("/home");
221
256
  * ```
222
257
  */
223
- exec(url: string, fallbackFn?: RouteCallback): any;
258
+ exec(url: string, fallbackFn?: RouteCallback): unknown;
224
259
  /**
225
260
  * Subscribes to router state changes.
226
261
  * Follows the Svelte store contract - subscriber is called immediately with current state.
227
- * Compatible with RxJS Observables (returns object with `unsubscribe` method).
228
262
  *
229
263
  * @param subscription - Callback function that receives router state changes
230
- * @returns Subscription object with `unsubscribe()` method
264
+ * @returns Unsubscribe function
231
265
  *
232
266
  * @example
233
267
  * ```ts
234
- * const { unsubscribe } = router.subscribe((state) => {
268
+ * const unsubscribe = router.subscribe((state) => {
235
269
  * console.log("Route changed:", state.route);
236
270
  * console.log("Params:", state.params);
237
271
  * });
@@ -242,5 +276,5 @@ export declare class SimpleRouter {
242
276
  *
243
277
  * @see https://svelte.dev/docs#Store_contract
244
278
  */
245
- subscribe(subscription: RouterSubscriber): RouterSubscription;
279
+ subscribe(subscription: RouterSubscriber): RouterUnsubscribe;
246
280
  }
package/dist/router.js CHANGED
@@ -32,7 +32,11 @@ const PUBSUB_TOPIC = "current";
32
32
  * ```
33
33
  */
34
34
  export class SimpleRouter {
35
- /** Enable/disable console debug logging for route matching */
35
+ /**
36
+ * Enable/disable console debug logging for route matching.
37
+ * When enabled, logs route matching details to the console.
38
+ * @default false
39
+ */
36
40
  static debug = false;
37
41
  #routes = [];
38
42
  #catchAll = null;
@@ -42,26 +46,58 @@ export class SimpleRouter {
42
46
  label: null,
43
47
  };
44
48
  #pubsub = createPubSub();
49
+ #logger = null;
45
50
  /**
46
51
  * Creates a new SimpleRouter instance.
47
52
  *
48
- * @param config - Optional configuration object mapping route patterns to callbacks
53
+ * @param config - Optional configuration: either a RouterConfig object mapping route patterns
54
+ * to callbacks, or a RouterOptions object with routes and logger
49
55
  *
50
56
  * @example
51
57
  * ```ts
58
+ * // Simple config (backwards compatible)
52
59
  * const router = new SimpleRouter({
53
60
  * "/": () => HomePage,
54
61
  * "/about": () => AboutPage
55
62
  * });
63
+ *
64
+ * // With options object
65
+ * const router = new SimpleRouter({
66
+ * routes: {
67
+ * "/": () => HomePage,
68
+ * "/about": () => AboutPage
69
+ * },
70
+ * logger: myLogger // optional, compatible with @marianmeres/clog
71
+ * });
56
72
  * ```
57
73
  */
58
74
  constructor(config = null) {
59
- Object.entries(config || {}).forEach(([route, cb]) => {
75
+ let routes = null;
76
+ if (config) {
77
+ // Check if config is RouterOptions (has 'routes' or 'logger' key)
78
+ if ("routes" in config || "logger" in config) {
79
+ const options = config;
80
+ routes = options.routes ?? null;
81
+ this.#logger = options.logger ?? null;
82
+ }
83
+ else {
84
+ // Backwards compatible: treat as RouterConfig
85
+ routes = config;
86
+ }
87
+ }
88
+ Object.entries(routes || {}).forEach(([route, cb]) => {
60
89
  this.on(route, cb);
61
90
  });
62
91
  }
63
92
  #dbg(...a) {
64
- SimpleRouter.debug && console.log("[SimpleRouter]", ...a);
93
+ if (SimpleRouter.debug) {
94
+ if (this.#logger) {
95
+ this.#logger.log("[SimpleRouter]", ...a);
96
+ }
97
+ else {
98
+ console.log("[SimpleRouter]", ...a);
99
+ }
100
+ }
65
101
  }
66
102
  /**
67
103
  * Returns a map of registered route patterns to their labels.
@@ -140,7 +176,8 @@ export class SimpleRouter {
140
176
  * router.on("*", () => NotFoundPage);
141
177
  * ```
142
178
  */
143
- on(routes, cb, { label = null, allowQueryParams = true } = {}) {
179
+ on(routes, cb, options = {}) {
180
+ const { label = null, allowQueryParams = true } = options;
144
181
  if (!Array.isArray(routes))
145
182
  routes = [routes];
146
183
  routes.forEach((route) => {
@@ -187,7 +224,6 @@ export class SimpleRouter {
187
224
  * const component = router.exec("/home");
188
225
  * ```
189
226
  */
190
- // deno-lint-ignore no-explicit-any
191
227
  exec(url, fallbackFn) {
192
228
  const dbgPrefix = `'${url}' -> `;
193
229
  for (const [route, cb, allowQueryParams, label] of this.#routes) {
@@ -221,14 +257,13 @@ export class SimpleRouter {
221
257
  /**
222
258
  * Subscribes to router state changes.
223
259
  * Follows the Svelte store contract - subscriber is called immediately with current state.
224
- * Compatible with RxJS Observables (returns object with `unsubscribe` method).
225
260
  *
226
261
  * @param subscription - Callback function that receives router state changes
227
- * @returns Subscription object with `unsubscribe()` method
262
+ * @returns Unsubscribe function
228
263
  *
229
264
  * @example
230
265
  * ```ts
231
- * const { unsubscribe } = router.subscribe((state) => {
266
+ * const unsubscribe = router.subscribe((state) => {
232
267
  * console.log("Route changed:", state.route);
233
268
  * console.log("Params:", state.params);
234
269
  * });
@@ -246,10 +281,6 @@ export class SimpleRouter {
246
281
  const unsubscribe = this.#pubsub.subscribe(PUBSUB_TOPIC, subscription);
247
282
  // immediately call with current value (as per store contract)
248
283
  subscription(this.#current);
249
- // For interoperability with RxJS Observables, the .subscribe method is also
250
- // allowed to return an object with an .unsubscribe method
251
- return {
252
- unsubscribe,
253
- };
284
+ return unsubscribe;
254
285
  }
255
286
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@marianmeres/simple-router",
3
- "version": "2.2.3",
3
+ "version": "3.0.0",
4
4
  "type": "module",
5
5
  "main": "dist/mod.js",
6
6
  "types": "dist/mod.d.ts",