extreme-router 1.1.1 → 1.3.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/CHANGELOG.MD CHANGED
@@ -6,6 +6,28 @@ All notable changes to this project will be documented in this file.
6
6
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
7
7
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
8
8
 
9
+ ## [1.3.0] - 2026-04-14
10
+
11
+ ### Added
12
+ - Introduced the `createPlugin` helper utility (`src/createPlugin.ts`). Allows developers to create fully valid custom plugins from a minimal declarative config - providing `id`, `priority`, `syntax`, a `detect` regex (with a required `paramName` named capture group), and a `test` function - without writing `PluginHandler`, `PluginMeta`, or the factory wrapper by hand. Exported as `createPlugin` from the package root along with its `CreatePluginOptions` type.
13
+ - Added `CreatePluginOptions` interface to `src/types.ts`.
14
+
15
+ ### Documentation
16
+ - Added `createPlugin` Helper section to README with a full option reference table and a UUID plugin example.
17
+ - Added `createPlugin` entry to the README API section.
18
+
19
+ ### Testing
20
+ - Added comprehensive test suite for `createPlugin` (`tests/createPlugin.test.ts`) covering plugin factory output, registration-time detection, match-time behaviour, `wildcard`/`override` forwarding, and end-to-end router integration.
21
+
22
+ ## [1.2.0] - 2025-12-23
23
+
24
+ ### Added
25
+ - Introduced the `skipPluginValidation` option to the router constructor and for use with `router.use()`. When enabled (`true`), plugin validation is skipped during plugin registration, improving router initialization time. Defaults to `false`. **⚠️ WARNING:** This option should only be enabled when using official plugins or well validated custom plugins, as skipping validation can lead to runtime errors if plugins are malformed.
26
+
27
+ ### Documentation
28
+ - Added comprehensive documentation for the `skipPluginValidation` option in the README, including safety warnings and usage guidelines.
29
+ - Added tests for the `skipPluginValidation` feature covering both constructor initialization and `use()` method usage.
30
+
9
31
  ## [1.1.1] - 2025-05-19
10
32
 
11
33
  ### Fixed
package/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2025 Lior Cohen
3
+ Copyright (c) 2026 Lior Cohen
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/README.md CHANGED
@@ -1,41 +1,40 @@
1
- <h1 align="center">⚡ Extreme Router ⚡</h1>
2
-
3
- <h2 align="center">High-Performance, Plugin-Driven JavaScript Router</h2>
4
-
5
1
  <div align="center">
6
- <img src="https://img.shields.io/github/issues/liorcodev/extreme-router.svg" />
7
- &nbsp;
2
+ <img src="./assets/logo.svg" alt="Extreme Router Logo" width="120" height="120">
3
+ <h1>Extreme Router</h1>
4
+ <p><strong>High-Performance, Plugin-Driven JavaScript Router</strong></p>
5
+
8
6
  <img src="https://img.shields.io/npm/v/extreme-router.svg" />
9
7
  &nbsp;
10
8
  <img src="https://img.shields.io/badge/License-MIT-orange.svg?color=orange" alt="License: MIT" />
11
9
  </div>
12
- <br />
10
+
13
11
  <br />
14
12
 
15
- 🔥 **A high-performance, tree-based router for JavaScript and TypeScript, featuring a powerful plugin system for extreme extensibility.**
13
+ **A high-performance, tree-based router for JavaScript and TypeScript, featuring a powerful plugin system for extreme extensibility.**
16
14
 
17
15
  Extreme Router is designed for speed and flexibility. It uses an optimized radix tree (trie) structure for fast dynamic route matching and a dedicated cache for O(1) static route lookups, while its plugin architecture allows you to easily extend its capabilities to handle virtually any URL pattern.
18
16
 
19
- ## 📚 Table of Contents
17
+ ## Table of Contents
20
18
 
21
- - [Features](#features)
22
- - [🚀 Installation](#installation)
23
- - [💡 Basic Usage](#basic-usage)
24
- - [Advanced Usage](#advanced-usage)
25
- - [🔌 Built-in Plugins](#built-in-plugins)
19
+ - [Features](#features)
20
+ - [Installation](#installation)
21
+ - [Basic Usage](#basic-usage)
22
+ - [Advanced Usage](#advanced-usage)
23
+ - [Built-in Plugins](#built-in-plugins)
26
24
  - [Example using regex param plugin](#example-using-regex-param-plugin)
27
- - [🛠️ Custom Plugins](#custom-plugins)
28
- - [⚙️ API](#api)
25
+ - [Custom Plugins](#custom-plugins)
26
+ - [createPlugin Helper](#createplugin-helper)
27
+ - [API](#api)
29
28
  - [Error Types](./docs/error-types.md)
30
- - [📊 Benchmarks](#benchmarks)
31
- - [Testing](#testing)
32
- - [🙏 Acknowledgments](#acknowledgments)
33
- - [🤝 Contributing](#contributing)
34
- - [📜 License](#license)
29
+ - [Benchmarks](#benchmarks)
30
+ - [Testing](#testing)
31
+ - [Acknowledgments](#acknowledgments)
32
+ - [Contributing](#contributing)
33
+ - [License](#license)
35
34
 
36
35
  <span id="features"></span>
37
36
 
38
- ## Features
37
+ ## Features
39
38
 
40
39
  - **Blazing Fast:** Optimized radix tree implementation for O(k) lookup (k = path length)\*, with a dedicated cache for static routes (O(1)).
41
40
  - **Universal Compatibility:** Runs seamlessly on every JavaScript environment.
@@ -53,9 +52,10 @@ Extreme Router is designed for speed and flexibility. It uses an optimized radix
53
52
  - Group Parameters (`/:paramName(val1|val2)`)
54
53
  - Prefix Group Parameters (`img(png|jpg|gif)`)
55
54
  - Optional Prefix Group Parameters (`img(png|jpg|gif)?`)
55
+ - **`createPlugin` Helper:** Quickly create custom plugins with minimal boilerplate using a simple declarative config.
56
56
  - **TypeScript Native:** Written entirely in TypeScript with excellent type support.
57
57
  - **Zero Dependencies:** Lightweight and dependency-free core.
58
- - **Compact Size:** The core library is lightweight: **13.03 KB minified** / **3.85 KB gzipped** (ESM) and **13.60 KB minified** / **4.10 KB gzipped** (CJS).
58
+ - **Compact Size:** The core library is lightweight: **~13.17 KB minified** / **~3.85 KB gzipped** (ESM) and **~13.73 KB minified** / **~4.10 KB gzipped** (CJS).
59
59
  - **Well-Tested:** Comprehensive test suite ensuring reliability with **100% code coverage**.
60
60
  - **Benchmarked:** Performance is continuously monitored.
61
61
 
@@ -63,7 +63,7 @@ Extreme Router is designed for speed and flexibility. It uses an optimized radix
63
63
 
64
64
  <span id="installation"></span>
65
65
 
66
- ## 🚀 Installation
66
+ ## Installation
67
67
 
68
68
  ```bash
69
69
  bun install extreme-router
@@ -77,7 +77,7 @@ pnpm add extreme-router
77
77
 
78
78
  <span id="basic-usage"></span>
79
79
 
80
- ## 💡 Basic Usage
80
+ ## Basic Usage
81
81
 
82
82
  ```typescript
83
83
  import Extreme, { param, wildcard } from 'extreme-router';
@@ -151,7 +151,7 @@ console.log(router.inspect());
151
151
 
152
152
  <span id="advanced-usage"></span>
153
153
 
154
- ## Advanced Usage
154
+ ## Advanced Usage
155
155
 
156
156
  Here are examples `docs/examples` of how to integrate Extreme Router into simple HTTP servers using different JavaScript runtimes.
157
157
 
@@ -162,7 +162,7 @@ Here are examples `docs/examples` of how to integrate Extreme Router into simple
162
162
 
163
163
  <span id="built-in-plugins"></span>
164
164
 
165
- ## 🔌 Built-in Plugins
165
+ ## Built-in Plugins
166
166
 
167
167
  Extreme Router comes with several pre-built plugins. You need to register them using `router.use()` before registering routes that depend on them. When matching a URL segment against potential dynamic routes, the router checks the registered plugins based on their **`priority`** value. **Lower priority numbers are checked first**.
168
168
 
@@ -206,18 +206,16 @@ const match2 = router.match('/users/abc');
206
206
 
207
207
  <span id="custom-plugins"></span>
208
208
 
209
- ## 🛠️ Custom Plugins
209
+ ## Custom Plugins
210
210
 
211
211
  Extreme Router's power lies in its extensibility. You can easily create your own plugins to handle unique URL patterns or add custom matching logic. The process involves defining a plugin function that returns a configuration object, which in turn includes a handler function responsible for recognizing syntax and providing the runtime matching logic.
212
212
 
213
- **Core Types:** (from [`src/types.ts`](c:\Users\lior3\Development\liodex\extreme-router\src\types.ts))
213
+ **Core Types:** (from [src/types.ts](src/types.ts))
214
214
 
215
215
  1. **`Plugin`**: `() => PluginConfig`
216
-
217
216
  - The function you register with `router.use()`. It's a factory function that, when called, returns a `PluginConfig` object. This allows plugins to be configured or initialized if needed, though simple plugins might just return a static configuration object.
218
217
 
219
218
  2. **`PluginConfig`**: `{ id: string, priority: number, syntax: string, handler: PluginHandler }`
220
-
221
219
  - Defines the plugin's identity, precedence, the **representative syntax pattern** it handles, and the handler function.
222
220
  - **`id: string`**: A unique identifier for the plugin (e.g., `"param"`, `"myCustomPlugin"`). This is used internally and for error reporting.
223
221
  - **`priority: number`**: A number determining the order in which plugins are evaluated during route registration and matching. Lower numbers have higher priority. Built-in plugins have priorities like `param` (700) and `wildcard` (800). Choose a priority that makes sense relative to other plugins.
@@ -225,7 +223,6 @@ Extreme Router's power lies in its extensibility. You can easily create your own
225
223
  - **`handler: PluginHandler`**: The function responsible for processing path segments during route registration.
226
224
 
227
225
  3. **`PluginHandler`**: `(segment: string) => PluginMeta | undefined | null`
228
-
229
226
  - Called during `router.register()`. It receives a path segment string (e.g., `":userId"`, `":id<uuid>"`, `"*"`).
230
227
  - Its job is to determine if this `segment` matches the pattern the plugin is designed for.
231
228
  - If it matches, it should return a `PluginMeta` object containing the necessary information for matching and parameter extraction.
@@ -252,7 +249,7 @@ import Extreme, { param } from 'extreme-router';
252
249
  import type { Plugin, PluginHandler, PluginMeta } from 'extreme-router'; // Import types
253
250
 
254
251
  // Define the UUID regex
255
- const UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
252
+ const UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
256
253
 
257
254
  // 1. Define the Plugin Function
258
255
  const uuidPlugin: Plugin = () => {
@@ -325,14 +322,61 @@ console.log(match2);
325
322
  console.log(match3);
326
323
  ```
327
324
 
325
+ <span id="createplugin-helper"></span>
326
+
327
+ ### createPlugin Helper
328
+
329
+ For the most common plugin pattern - matching a segment against a regex and capturing a named parameter - the `createPlugin` helper eliminates all the boilerplate. Instead of manually writing `PluginHandler`, `PluginMeta`, and the factory wrapper, you provide a minimal declarative config.
330
+
331
+ | Option | Type | Description |
332
+ | :--------- | :---------------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
333
+ | `id` | `string` | Unique plugin identifier. |
334
+ | `priority` | `number` | Evaluation order (lower = higher precedence). |
335
+ | `syntax` | `string` | Representative syntax string used for validation (e.g. `':name<uuid>'`). |
336
+ | `detect` | `RegExp` | Run at **register time** against the path segment. Must have a named capture group `paramName`. Any extra named groups are passed to `test`. **⚠️ Do not use stateful flags (`g`, `y`).** |
337
+ | `test` | `(urlSegment, captures) => boolean` | Run at **match time**. Return `true` to accept the segment. `params[paramName]` is set automatically on `true`. |
338
+ | `wildcard` | `boolean?` | Forward `wildcard: true` to `PluginMeta`. |
339
+ | `override` | `boolean?` | Forward `override: true` to `PluginMeta`. |
340
+
341
+ **Example: UUID plugin using `createPlugin`**
342
+
343
+ ```typescript
344
+ import Extreme, { createPlugin, param } from 'extreme-router';
345
+
346
+ const UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
347
+
348
+ const uuidPlugin = createPlugin({
349
+ id: 'uuid',
350
+ priority: 550,
351
+ syntax: ':name<uuid>',
352
+ detect: /^:(?<paramName>[a-zA-Z0-9_-]+)<uuid>$/,
353
+ test: (urlSegment) => UUID_REGEX.test(urlSegment),
354
+ });
355
+
356
+ const router = new Extreme<{ handler: string }>();
357
+ router.use(uuidPlugin).use(param);
358
+
359
+ router.register('/orders/:orderId<uuid>').handler = 'GetOrder';
360
+ router.register('/users/:userId').handler = 'GetUser';
361
+
362
+ router.match('/orders/123e4567-e89b-12d3-a456-426614174000');
363
+ // { handler: 'GetOrder', params: { orderId: '123e4567-...' } }
364
+
365
+ router.match('/orders/not-a-uuid'); // null
366
+ router.match('/users/regular-id'); // { handler: 'GetUser', params: { userId: 'regular-id' } }
367
+ ```
368
+
369
+ > For advanced plugin patterns that require full control over `PluginMeta` (e.g. wildcard consumption, non-capturing segments, custom `additionalMeta`), use the full manual approach described above.
370
+
328
371
  <span id="api"></span>
329
372
 
330
- ## ⚙️ API
373
+ ## API
331
374
 
332
375
  - **`new Extreme<T>(options?: Options<T>)`**: Creates a new router instance.
333
376
  - `options.storeFactory`: A function that returns a new store object for each registered route. Defaults to `() => Object.create(null)`.
334
377
  - `options.plugins`: An array of plugin functions (`Plugin[]`) to register automatically when the router is created. Defaults to `[]`. Plugins will be applied (and sorted by priority) before any manual `router.use()` calls.
335
378
  - `options.allowRegisterUpdateExisting`: If set to `true`, calling `router.register()` for a path that is already registered will not throw an error; instead, it will return the existing store object for that path, allowing you to update or modify its data. If `false` (default), attempting to register an already registered path will throw an error. This option only affects exact path matches and does not merge or update routes with different parameterizations or plugin handling.
379
+ - `options.skipPluginValidation`: If set to `true`, plugin validation is skipped during plugin registration (both in the constructor and when using `router.use()`), improving router initialization time. Defaults to `false`. **⚠️ WARNING:** Enabling this option is safe **only** when using official plugins or well validated custom plugins. When enabled, the router will not verify plugin structure, handler functions, or match function returns, which could lead to runtime errors if plugins are malformed. Use this option only when you are certain your plugins are properly implemented validated and tested.
336
380
  - **`router.use(plugin: Plugin): this`**: Registers a plugin function and returns the router instance, allowing method chaining.
337
381
  - Example:
338
382
  ```typescript
@@ -352,11 +396,12 @@ console.log(match3);
352
396
  - `path: string`: The registered path string.
353
397
  - `type: 'static' | 'dynamic'`: The type of the route.
354
398
  - `store: T`: The original store object associated with the route.
399
+ - **`createPlugin(options: CreatePluginOptions): Plugin`**: A helper utility that creates a fully valid `Plugin` from a minimal declarative config, eliminating boilerplate for common plugin patterns. See the [createPlugin Helper](#createplugin-helper) section for details and a full option reference.
355
400
  - **Error Handling**: The router uses a set of predefined [Error Types](./docs/error-types.md) for consistent error reporting.
356
401
 
357
402
  <span id="benchmarks"></span>
358
403
 
359
- ## 📊 Benchmarks
404
+ ## Benchmarks
360
405
 
361
406
  The following benchmarks measure the raw speed of the `router.match()` operation (ops/sec) for different route types and route counts.
362
407
 
@@ -585,12 +630,10 @@ Test duration: 30 seconds. Lower heap usage and increase is generally better.
585
630
  The memory benchmark results highlight differing memory usage patterns between Bun and Node.js. These differences primarily stem from their underlying JavaScript engines and memory management strategies:
586
631
 
587
632
  1. **JavaScript Engines:**
588
-
589
633
  - **Bun:** Utilizes JavaScriptCore (JSC), known for quick startup and potentially lower initial memory consumption.
590
634
  - **Node.js:** Employs V8, which is highly optimized for long-running server applications.
591
635
 
592
636
  2. **Initial Heap Size and Growth:**
593
-
594
637
  - **Bun (JSC):** The benchmarks show Bun starting with a very small heap (e.g., `228.86 KB`). This results in a large _percentage_ increase as the application allocates memory, even if the final _absolute_ heap size remains relatively small (around 2 MB).
595
638
  - **Node.js (V8):** Node.js starts with a considerably larger initial heap (e.g., `5.33 MB - 5.67 MB`). Consequently, its _percentage_ increase is smaller for comparable absolute memory growth.
596
639
 
@@ -626,7 +669,7 @@ bun run benchmark:stress --routes=1000
626
669
 
627
670
  <span id="testing"></span>
628
671
 
629
- ## Testing
672
+ ## Testing
630
673
 
631
674
  Run the comprehensive test suite:
632
675
 
@@ -636,25 +679,25 @@ bun test
636
679
  bun run test:coverage
637
680
  ```
638
681
 
639
- The coverage report can be found in the `coverage/` directory ([`coverage/index.html`](c:\Users\lior3\Development\liodex\extreme-router\coverage\index.html)).
682
+ The coverage report can be found in the `coverage/` directory ([coverage/index.html](coverage/index.html)).
640
683
 
641
684
  **100% code coverage** is ensured.
642
685
 
643
686
  <span id="acknowledgments"></span>
644
687
 
645
- ## 🙏 Acknowledgments
688
+ ## Acknowledgments
646
689
 
647
690
  Extreme Router draws inspiration from the high-level routing concepts and per-route register/store design of [Medley Router](https://github.com/medleyjs/router). Sincere thanks to the Medley Router authors for their foundational ideas.
648
691
 
649
692
  <span id="contributing"></span>
650
693
 
651
- ## 🤝 Contributing
694
+ ## Contributing
652
695
 
653
696
  Contributions are welcome!
654
697
  Please read our [CONTRIBUTING.md](./CONTRIBUTING.md) for detailed guidelines on development, testing, benchmarking, and submitting pull requests.
655
698
 
656
699
  <span id="license"></span>
657
700
 
658
- ## 📜 License
701
+ ## License
659
702
 
660
703
  This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
@@ -0,0 +1,35 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" fill="none">
2
+ <defs>
3
+ <linearGradient id="bg" x1="0" y1="48" x2="48" y2="0" gradientUnits="userSpaceOnUse">
4
+ <stop offset="0%" stop-color="#ea580c"/>
5
+ <stop offset="100%" stop-color="#fb923c"/>
6
+ </linearGradient>
7
+ </defs>
8
+
9
+ <rect width="48" height="48" rx="11" fill="url(#bg)"/>
10
+ <rect x="0" y="0" width="48" height="12" rx="11" fill="white" fill-opacity="0.1"/>
11
+
12
+ <!-- Bold Letter E -->
13
+ <!-- Vertical bar -->
14
+ <rect x="13" y="12" width="4.5" height="24" rx="2" fill="white"/>
15
+
16
+ <!-- Top horizontal bar -->
17
+ <rect x="13" y="12" width="18" height="4.5" rx="2" fill="white"/>
18
+
19
+ <!-- Middle horizontal bar -->
20
+ <rect x="13" y="21.75" width="15" height="4.5" rx="2" fill="white"/>
21
+
22
+ <!-- Bottom horizontal bar -->
23
+ <rect x="13" y="31.5" width="18" height="4.5" rx="2" fill="white"/>
24
+
25
+ <!-- Signal routing dots flowing from right side -->
26
+ <circle cx="34" cy="14.25" r="2" fill="white" opacity="0.9"/>
27
+ <circle cx="37" cy="14.25" r="1.5" fill="white" opacity="0.7"/>
28
+
29
+ <circle cx="31" cy="24" r="2" fill="white" opacity="0.9"/>
30
+ <circle cx="34.5" cy="24" r="1.5" fill="white" opacity="0.7"/>
31
+ <circle cx="37.5" cy="24" r="1" fill="white" opacity="0.5"/>
32
+
33
+ <circle cx="34" cy="33.75" r="2" fill="white" opacity="0.9"/>
34
+ <circle cx="37" cy="33.75" r="1.5" fill="white" opacity="0.7"/>
35
+ </svg>
@@ -1,22 +1,22 @@
1
1
  {
2
2
  "files": {
3
3
  "index.cjs": {
4
- "minified": "13.64 KB",
5
- "gzipped": "4.11 KB",
6
- "minifiedBytes": 13964,
7
- "gzippedBytes": 4213
4
+ "minified": "14.13 KB",
5
+ "gzipped": "4.27 KB",
6
+ "minifiedBytes": 14468,
7
+ "gzippedBytes": 4371
8
8
  },
9
9
  "index.js": {
10
- "minified": "13.07 KB",
11
- "gzipped": "3.86 KB",
12
- "minifiedBytes": 13381,
13
- "gzippedBytes": 3954
10
+ "minified": "13.55 KB",
11
+ "gzipped": "4.01 KB",
12
+ "minifiedBytes": 13871,
13
+ "gzippedBytes": 4108
14
14
  }
15
15
  },
16
16
  "total": {
17
- "minified": 27345,
18
- "gzipped": 8167,
19
- "minifiedKB": "26.70 KB",
20
- "gzippedKB": "7.98 KB"
17
+ "minified": 28339,
18
+ "gzipped": 8479,
19
+ "minifiedKB": "27.67 KB",
20
+ "gzippedKB": "8.28 KB"
21
21
  }
22
22
  }
package/dist/index.cjs CHANGED
@@ -1,2 +1,2 @@
1
- "use strict";var x=Object.defineProperty;var H=Object.getOwnPropertyDescriptor;var A=Object.getOwnPropertyNames;var $=Object.prototype.hasOwnProperty;var j=(d,t)=>{for(var r in t)x(d,r,{get:t[r],enumerable:!0})},v=(d,t,r,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let e of A(t))!$.call(d,e)&&e!==r&&x(d,e,{get:()=>t[e],enumerable:!(n=H(t,e))||n.enumerable});return d};var F=d=>v(x({},"__esModule",{value:!0}),d);var G={};j(G,{default:()=>E,extensionParam:()=>M,groupParam:()=>O,optionalParam:()=>D,optionalPrefixGroup:()=>R,param:()=>S,prefixGroup:()=>C,regexParam:()=>w,wildcard:()=>b});module.exports=F(G);var N=(o=>(o[o.StoreIsNotFunction=0]="StoreIsNotFunction",o[o.StoreDoesNotReturnObject=1]="StoreDoesNotReturnObject",o[o.StoreUnexpected=2]="StoreUnexpected",o[o.PathAlreadyRegistered=3]="PathAlreadyRegistered",o[o.PathIsEmpty=4]="PathIsEmpty",o[o.PluginWithSameIdAlreadyExists=5]="PluginWithSameIdAlreadyExists",o[o.PluginWithSamePriorityAlreadyExists=6]="PluginWithSamePriorityAlreadyExists",o[o.PluginIsNotFunction=7]="PluginIsNotFunction",o[o.PluginDoesNotReturnObject=8]="PluginDoesNotReturnObject",o[o.PluginsOptionNotArray=9]="PluginsOptionNotArray",o[o.PluginMissingId=10]="PluginMissingId",o[o.PluginMissingPriority=11]="PluginMissingPriority",o[o.PluginIdIsNotString=12]="PluginIdIsNotString",o[o.PluginPriorityIsNotNumber=13]="PluginPriorityIsNotNumber",o[o.PluginMissingSyntax=14]="PluginMissingSyntax",o[o.PluginSyntaxIsNotString=15]="PluginSyntaxIsNotString",o[o.PluginMissingHandler=16]="PluginMissingHandler",o[o.PluginHandlerIsNotFunction=17]="PluginHandlerIsNotFunction",o[o.PluginHandlerReturnNullOrUndefinedForSyntax=18]="PluginHandlerReturnNullOrUndefinedForSyntax",o[o.PluginHandlerDoesNotReturnObject=19]="PluginHandlerDoesNotReturnObject",o[o.PluginHandlerMissingMatch=20]="PluginHandlerMissingMatch",o[o.PluginHandlerMatchIsNotFunction=21]="PluginHandlerMatchIsNotFunction",o[o.PluginHandlerMatchDoesNotReturnBoolean=22]="PluginHandlerMatchDoesNotReturnBoolean",o[o.PluginUnexpected=23]="PluginUnexpected",o[o.PluginDoesNotExist=24]="PluginDoesNotExist",o[o.DynamicSegmentAlreadyExists=25]="DynamicSegmentAlreadyExists",o[o.WildcardNotAtEnd=26]="WildcardNotAtEnd",o))(N||{});var P=class{constructor(t={}){this.options=this.validateOptions(t),this.staticPathCache=Object.create(null),this.root=this.createNode(),this.errorTypes=this.createErrorTypes(),this.plugins=[],this.options.plugins.length>0&&this.loadPlugins(this.options.plugins)}defaultOptions={storeFactory:()=>Object.create(null),plugins:[],allowRegisterUpdateExisting:!1};options;staticPathCache;root;plugins;errorTypes;matchers={staticPath:/^(?:\/|\/?(?:[a-zA-Z0-9 _.-]+)(?:\/[a-zA-Z0-9 _.-]+)*)$/,paramOptionalInPath:/\/:[a-zA-Z0-9_-]+\?/,staticSegment:/^[a-zA-Z0-9 _.-]+$/,paramOptionalSegment:/^:[a-zA-Z0-9_-]+\?$/};loadPlugins(t){t.forEach(r=>{let n=this.validatePlugin(r);this.plugins.push(n)}),this.plugins.sort((r,n)=>r.priority-n.priority)}validateOptions(t){let r={...this.defaultOptions,...t};if(!r.storeFactory)r.storeFactory=this.defaultOptions.storeFactory;else if(typeof r.storeFactory!="function")this.throwError(0,typeof r.storeFactory);else{let n;try{n=r.storeFactory()}catch(e){this.throwError(2,e instanceof Error?e.message:String(e))}(typeof n!="object"||Array.isArray(n))&&this.throwError(1,typeof n)}return Array.isArray(r.plugins)||this.throwError(9,typeof r.plugins),r}createErrorTypes(){return{0:t=>`Store is not a function: ${t}`,1:t=>`Store does not return an object: ${t}`,2:t=>`Store unexpected error: ${t}`,3:t=>`Path already registered: ${t}`,5:t=>`Plugin with same ID already exists: ${t}`,6:t=>`Plugin with same priority already exists: ${t}`,7:t=>`Plugin is not a function: ${t}`,8:t=>`Plugin does not return an object: ${t}`,9:t=>`Plugins option must be an array, got: ${t}`,10:()=>"Plugin missing ID",11:()=>"Plugin missing priority",12:t=>`Plugin ID is not a string: ${t}`,13:t=>`Plugin priority is not a number: ${t}`,14:()=>"Plugin missing syntax",15:t=>`Plugin syntax is not a string: ${t}`,16:t=>`Plugin missing handler: ${t}`,17:t=>`Plugin handler is not a function: ${t}`,18:t=>`Plugin handler returned null or undefined while matching syntax: ${t}`,19:t=>`Plugin handler does not return an object: ${t}`,20:()=>"Plugin handler missing match function",21:t=>`Plugin handler match is not a function: ${t}`,22:t=>`Plugin handler match does not return a boolean: ${t}`,23:t=>`Plugin unexpected error: ${t}`,24:t=>`Plugin does not exist for: ${t}`,25:t=>`Dynamic segment already exists: ${t}`,26:()=>"Wildcard must be at the end of the path",4:()=>"Path cannot be empty"}}throwError(t,r){if(N[t]!==void 0){let e=this.errorTypes[t](r);throw new Error(e)}throw new Error("Unknown error type")}createNode(){return{staticChildren:Object.create(null)}}generateOptionals(t){let r=t.split("/").filter(Boolean),n=r.map((s,a)=>this.matchers.paramOptionalSegment.test(s)?a:-1).filter(s=>s>=0);if(n.length===0)return["/"+r.join("/")];let e=new Set,i=1<<n.length;for(let s=0;s<i;s++){let a=r.filter((u,c)=>{if(!this.matchers.paramOptionalSegment.test(r[c]))return!0;let g=n.indexOf(c);return!!(s&1<<g)}),l=a.length===0?"/":"/"+a.join("/");e.add(l)}return Array.from(e)}registerStaticPath(t,r){if(this.staticPathCache[t])return this.options.allowRegisterUpdateExisting||this.throwError(3,t),this.staticPathCache[t];let n=Object.create(r??this.options.storeFactory());return this.staticPathCache[t]=n,n}registerDynamicPath(t,r){let n=t.split("/").filter(Boolean),e=this.root,i=r??this.options.storeFactory(),s=n.length;for(let a=0;a<s;a++){let l=n[a];if(this.matchers.staticSegment.test(l))e=e.staticChildren[l]??=this.createNode();else if(this.plugins.length>0){let u=!1;for(let c of this.plugins){let g=c.handler(l);if(g!=null){g.wildcard&&a!==s-1&&this.throwError(26,l),u=!0,g.id=c.id,g.priority=c.priority,g.syntax=c.syntax,e.dynamicChildren??=[];let h=e.dynamicChildren,p=h.find(f=>f.pluginMeta?.id===g.id&&f.pluginMeta?.paramName===g.paramName);if(p)e=p;else{h.find(y=>y.pluginMeta?.id===g.id)&&g.override!==!0&&this.throwError(25,`${l} (Plugin ID conflict: ${g.id})`);let m=this.createNode();m.pluginMeta=g,h.push(m),h.sort((y,I)=>y.pluginMeta.priority-I.pluginMeta.priority),e=m}break}}u||this.throwError(24,l)}else this.throwError(24,l)}return e.store?(e.pluginMeta?.override!==!0&&this.options.allowRegisterUpdateExisting===!1&&this.throwError(3,t),e.pluginMeta?.override===!0&&(e.store=i)):e.store=i,e.registeredPath=t,e.store}unregisterStaticPath(t){return this.staticPathCache[t]?(delete this.staticPathCache[t],!0):!1}unregisterDynamicPath(t){let r=this.root,n=t.split("/").filter(Boolean),{unregistered:e}=this.cleanupTraversal(r,n,0);return e}cleanupTraversal(t,r,n){let e=a=>Object.keys(a.staticChildren).length===0&&!a.dynamicChildren?.length,i=a=>e(a)&&!a.store;if(n===r.length)return t.store?(t.store=void 0,t.registeredPath=void 0,{shouldDelete:e(t),unregistered:!0}):{shouldDelete:e(t),unregistered:!1};let s=r[n];if(this.matchers.staticSegment.test(s)){let a=t.staticChildren[s];if(a){let{shouldDelete:l,unregistered:u}=this.cleanupTraversal(a,r,n+1);if(l)return delete t.staticChildren[s],{shouldDelete:i(t),unregistered:u};if(u)return{shouldDelete:!1,unregistered:!0}}}else if(this.plugins.length>0){let a=t.dynamicChildren;if(a)for(let l=0;l<this.plugins.length;l++){let u=this.plugins[l];for(let c=0;c<a.length;c++){let g=a[c],h=g.pluginMeta,p=u?.handler(s);if(h?.id===u?.id&&h?.paramName===p?.paramName){let{shouldDelete:f,unregistered:m}=this.cleanupTraversal(g,r,n+1);if(f)return a.splice(c,1),{shouldDelete:i(t),unregistered:m};if(m)return{shouldDelete:!1,unregistered:!0}}}}}return{shouldDelete:!1,unregistered:!1}}validatePlugin(t){typeof t!="function"&&this.throwError(7,typeof t);let r;try{r=t()}catch(s){this.throwError(23,s instanceof Error?s.message:String(s))}(!r||typeof r!="object")&&this.throwError(8,typeof r),r.id||this.throwError(10),typeof r.id!="string"&&this.throwError(12,typeof r.id),this.plugins.some(s=>s.id===r.id)&&this.throwError(5,r.id),r.priority||this.throwError(11),typeof r.priority!="number"&&this.throwError(13,typeof r.priority),this.plugins.some(s=>s.priority===r.priority)&&this.throwError(6,String(r.priority)),r.syntax||this.throwError(14),typeof r.syntax!="string"&&this.throwError(15,typeof r.syntax),r.handler||this.throwError(16,r.id),typeof r.handler!="function"&&this.throwError(7,typeof r.handler);let n,e=r.syntax;try{n=r.handler(e)}catch(s){this.throwError(23,s instanceof Error?s.message:String(s))}n==null&&this.throwError(18,e),(!n||typeof n!="object")&&this.throwError(19,typeof n),n.match||this.throwError(20),typeof n.match!="function"&&this.throwError(21,typeof n.match);let i;try{i=n.match({urlSegment:"",urlSegments:[""],index:0,params:{}})}catch(s){this.throwError(23,s instanceof Error?s.message:String(s))}return typeof i!="boolean"&&this.throwError(22,typeof i),r}use(t){let r=this.validatePlugin(t);return this.plugins.push(r),this.plugins.sort((n,e)=>n.priority-e.priority),this}inspect(){let t=[];for(let e in this.staticPathCache)if(Object.prototype.hasOwnProperty.call(this.staticPathCache,e)){let i=this.staticPathCache[e];t.push({path:e,type:"static",store:i})}let r=new Set,n=e=>{e.store&&e.registeredPath&&(r.has(e.registeredPath)||(t.push({path:e.registeredPath,type:"dynamic",store:e.store}),r.add(e.registeredPath)));for(let i in e.staticChildren)Object.prototype.hasOwnProperty.call(e.staticChildren,i)&&n(e.staticChildren[i]);if(e.dynamicChildren)for(let i of e.dynamicChildren)n(i)};return n(this.root),t}register(t){if(t||this.throwError(4),this.matchers.staticPath.test(t))return this.registerStaticPath(t);if(this.matchers.paramOptionalInPath.test(t)){let r=this.generateOptionals(t),n=this.options.storeFactory();for(let e of r)this.matchers.staticPath.test(e)?this.registerStaticPath(e,n):this.registerDynamicPath(e,n);return n}return this.registerDynamicPath(t)}unregister(t){if(this.matchers.staticPath.test(t))return this.unregisterStaticPath(t);if(this.matchers.paramOptionalInPath.test(t)){let r=this.generateOptionals(t),n=!1;for(let e of r)this.matchers.staticPath.test(e)?n=this.unregisterStaticPath(e):n=this.unregisterDynamicPath(e);return n}return this.unregisterDynamicPath(t)}match(t){let r=this.staticPathCache[t];if(r)return r;let n=t.split("/").filter(Boolean),e=n.length,i=this.root,s=Object.create(null),a=0;for(;a<e;a++){let l=n[a],u=i.staticChildren[l];if(u){i=u;continue}let c=i.dynamicChildren;if(c){let g=!1;for(let h=0;h<c.length;h++){let p=c[h],f=p?.pluginMeta;if(f?.match({urlSegment:l,urlSegments:n,index:a,params:s})){if(f.wildcard&&p?.store!==void 0){let m=Object.create(p.store);return m.params=s,m}i=p,g=!0;break}}if(!g)return null}else return null}if(a===e&&i.store){let l=Object.create(i.store);return l.params=s,l}return null}};var S=()=>({id:"param",priority:700,syntax:":paramName",handler:e=>{let i=/^:(?<paramName>[a-zA-Z0-9_-]+)$/.exec(e);if(!i||!i.groups||!i.groups.paramName)return null;let s=i.groups.paramName;return{paramName:s,match({urlSegment:a,params:l}){return l[s]=a,!0}}}});var b=()=>({id:"wildcard",priority:800,syntax:"*",handler:e=>{let i=/^(?:\*|:(?<wildcardName>[a-zA-Z0-9_-]+)\*)$/.exec(e);if(!i)return null;let s=i.groups?.wildcardName??"*";return{paramName:s,wildcard:!0,match({urlSegments:a,index:l,params:u}){let c=a[l],g=a.length;for(let h=l+1;h<g;h++)c+="/"+a[h];return u[s]=c||"",!0}}}});var w=()=>({id:"regexParam",priority:400,syntax:":paramName<\\d+>",handler:e=>{let i=/^:(?<paramName>[a-zA-Z0-9_-]+)<(?<regex>.+)>$/.exec(e);if(!i||!i.groups||!i.groups.paramName||!i.groups.regex)return null;let s=i.groups.paramName,a=new RegExp(`^${i.groups.regex}$`);return{paramName:s,additionalMeta:{regex:a},match({urlSegment:l,params:u}){let c=l.match(a);return c?(u[s]=c[0],!0):!1}}}});var M=()=>({id:"extensionParam",priority:500,syntax:":file.css",handler:e=>{let i=/^:(?<paramName>[a-zA-Z0-9_-]+)\.(?<extension>.+)$/.exec(e);if(!i||!i.groups||!i.groups.paramName||!i.groups.extension)return null;let s=i.groups.paramName,a=i.groups.extension;return{paramName:s,additionalMeta:{extension:a},match({urlSegment:l,params:u}){return l.endsWith(a)?(u[s]=l.slice(0,-(a.length+1)),!0):!1}}}});var O=()=>({id:"groupParam",priority:300,syntax:":paramName(a|b)",handler:e=>{let i=/^:(?<paramName>[a-zA-Z0-9_-]+)\((?<dynamicGroup>[^|)]+(\|[^|)]+)+)\)$/.exec(e);if(!i||!i.groups||!i.groups.paramName||!i.groups.dynamicGroup)return null;let s=i.groups.paramName,a=i.groups.dynamicGroup,l=Object.fromEntries(a.split("|").map(u=>[u,u]));return{paramName:s,additionalMeta:{group:l},match({urlSegment:u,params:c}){return!l||!l[u]?!1:(c[s]=u,!0)}}}});var C=()=>({id:"prefixGroup",priority:100,syntax:"prefix(a|b)",handler:e=>{let i=/^(?<staticName>[a-zA-Z0-9_.-]+)\((?<dynamicGroup>[^|)]+(\|[^|)]+)+)\)$/.exec(e);if(!i||!i.groups||!i.groups.staticName||!i.groups.dynamicGroup)return null;let s=i.groups.staticName,a=i.groups.dynamicGroup,l=Object.fromEntries(a.split("|").map(u=>[s+u,s+u]));return{paramName:"",additionalMeta:{group:l},match({urlSegment:u}){return!!(l&&l[u])}}}});var R=()=>({id:"optionalPrefixGroup",priority:200,syntax:"prefix(a|b)?",handler:e=>{let i=/^(?<staticName>[a-zA-Z0-9_.-]+)\((?<dynamicGroup>[^|)]+(\|[^|)]+)+)\)\?$/.exec(e);if(!i||!i.groups||!i.groups.staticName||!i.groups.dynamicGroup)return null;let s=i.groups.staticName,a=i.groups.dynamicGroup,l=Object.fromEntries(a.split("|").map(u=>[s+u,s+u]));return l[s]=s,{paramName:"",additionalMeta:{group:l},match({urlSegment:u}){return!!(l&&l[u])}}}});var D=()=>({id:"optionalParam",priority:600,syntax:":paramName?",handler:e=>{let i=/^:(?<paramName>[a-zA-Z0-9_-]+)\?$/.exec(e);if(!i||!i.groups||!i.groups.paramName)return null;let s=i.groups.paramName;return{paramName:s,override:!0,match({urlSegment:a,params:l}){return l[s]=a,!0}}}});var E=P;0&&(module.exports={extensionParam,groupParam,optionalParam,optionalPrefixGroup,param,prefixGroup,regexParam,wildcard});
1
+ "use strict";var x=Object.defineProperty;var v=Object.getOwnPropertyDescriptor;var A=Object.getOwnPropertyNames;var $=Object.prototype.hasOwnProperty;var j=(p,t)=>{for(var e in t)x(p,e,{get:t[e],enumerable:!0})},F=(p,t,e,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let i of A(t))!$.call(p,i)&&i!==e&&x(p,i,{get:()=>t[i],enumerable:!(n=v(t,i))||n.enumerable});return p};var E=p=>F(x({},"__esModule",{value:!0}),p);var U={};j(U,{createPlugin:()=>H,default:()=>G,extensionParam:()=>M,groupParam:()=>O,optionalParam:()=>D,optionalPrefixGroup:()=>R,param:()=>S,prefixGroup:()=>C,regexParam:()=>w,wildcard:()=>b});module.exports=E(U);var N=(s=>(s[s.StoreIsNotFunction=0]="StoreIsNotFunction",s[s.StoreDoesNotReturnObject=1]="StoreDoesNotReturnObject",s[s.StoreUnexpected=2]="StoreUnexpected",s[s.PathAlreadyRegistered=3]="PathAlreadyRegistered",s[s.PathIsEmpty=4]="PathIsEmpty",s[s.PluginWithSameIdAlreadyExists=5]="PluginWithSameIdAlreadyExists",s[s.PluginWithSamePriorityAlreadyExists=6]="PluginWithSamePriorityAlreadyExists",s[s.PluginIsNotFunction=7]="PluginIsNotFunction",s[s.PluginDoesNotReturnObject=8]="PluginDoesNotReturnObject",s[s.PluginsOptionNotArray=9]="PluginsOptionNotArray",s[s.PluginMissingId=10]="PluginMissingId",s[s.PluginMissingPriority=11]="PluginMissingPriority",s[s.PluginIdIsNotString=12]="PluginIdIsNotString",s[s.PluginPriorityIsNotNumber=13]="PluginPriorityIsNotNumber",s[s.PluginMissingSyntax=14]="PluginMissingSyntax",s[s.PluginSyntaxIsNotString=15]="PluginSyntaxIsNotString",s[s.PluginMissingHandler=16]="PluginMissingHandler",s[s.PluginHandlerIsNotFunction=17]="PluginHandlerIsNotFunction",s[s.PluginHandlerReturnNullOrUndefinedForSyntax=18]="PluginHandlerReturnNullOrUndefinedForSyntax",s[s.PluginHandlerDoesNotReturnObject=19]="PluginHandlerDoesNotReturnObject",s[s.PluginHandlerMissingMatch=20]="PluginHandlerMissingMatch",s[s.PluginHandlerMatchIsNotFunction=21]="PluginHandlerMatchIsNotFunction",s[s.PluginHandlerMatchDoesNotReturnBoolean=22]="PluginHandlerMatchDoesNotReturnBoolean",s[s.PluginUnexpected=23]="PluginUnexpected",s[s.PluginDoesNotExist=24]="PluginDoesNotExist",s[s.DynamicSegmentAlreadyExists=25]="DynamicSegmentAlreadyExists",s[s.WildcardNotAtEnd=26]="WildcardNotAtEnd",s))(N||{});var y=class{constructor(t={}){this.options=this.validateOptions(t),this.staticPathCache=Object.create(null),this.root=this.createNode(),this.errorTypes=this.createErrorTypes(),this.plugins=[],this.options.plugins.length>0&&this.loadPlugins(this.options.plugins)}defaultOptions={storeFactory:()=>Object.create(null),plugins:[],allowRegisterUpdateExisting:!1,skipPluginValidation:!1};options;staticPathCache;root;plugins;errorTypes;matchers={staticPath:/^(?:\/|\/?(?:[a-zA-Z0-9 _.-]+)(?:\/[a-zA-Z0-9 _.-]+)*)$/,paramOptionalInPath:/\/:[a-zA-Z0-9_-]+\?/,staticSegment:/^[a-zA-Z0-9 _.-]+$/,paramOptionalSegment:/^:[a-zA-Z0-9_-]+\?$/};loadPlugins(t){t.forEach(e=>{let n=this.options.skipPluginValidation?e():this.validatePlugin(e);this.plugins.push(n)}),this.plugins.sort((e,n)=>e.priority-n.priority)}validateOptions(t){let e={...this.defaultOptions,...t};if(!e.storeFactory)e.storeFactory=this.defaultOptions.storeFactory;else if(typeof e.storeFactory!="function")this.throwError(0,typeof e.storeFactory);else{let n;try{n=e.storeFactory()}catch(i){this.throwError(2,i instanceof Error?i.message:String(i))}(typeof n!="object"||Array.isArray(n))&&this.throwError(1,typeof n)}return Array.isArray(e.plugins)||this.throwError(9,typeof e.plugins),e}createErrorTypes(){return{0:t=>`Store is not a function: ${t}`,1:t=>`Store does not return an object: ${t}`,2:t=>`Store unexpected error: ${t}`,3:t=>`Path already registered: ${t}`,5:t=>`Plugin with same ID already exists: ${t}`,6:t=>`Plugin with same priority already exists: ${t}`,7:t=>`Plugin is not a function: ${t}`,8:t=>`Plugin does not return an object: ${t}`,9:t=>`Plugins option must be an array, got: ${t}`,10:()=>"Plugin missing ID",11:()=>"Plugin missing priority",12:t=>`Plugin ID is not a string: ${t}`,13:t=>`Plugin priority is not a number: ${t}`,14:()=>"Plugin missing syntax",15:t=>`Plugin syntax is not a string: ${t}`,16:t=>`Plugin missing handler: ${t}`,17:t=>`Plugin handler is not a function: ${t}`,18:t=>`Plugin handler returned null or undefined while matching syntax: ${t}`,19:t=>`Plugin handler does not return an object: ${t}`,20:()=>"Plugin handler missing match function",21:t=>`Plugin handler match is not a function: ${t}`,22:t=>`Plugin handler match does not return a boolean: ${t}`,23:t=>`Plugin unexpected error: ${t}`,24:t=>`Plugin does not exist for: ${t}`,25:t=>`Dynamic segment already exists: ${t}`,26:()=>"Wildcard must be at the end of the path",4:()=>"Path cannot be empty"}}throwError(t,e){if(N[t]!==void 0){let i=this.errorTypes[t](e);throw new Error(i)}throw new Error("Unknown error type")}createNode(){return{staticChildren:Object.create(null)}}generateOptionals(t){let e=t.split("/").filter(Boolean),n=e.map((o,a)=>this.matchers.paramOptionalSegment.test(o)?a:-1).filter(o=>o>=0);if(n.length===0)return["/"+e.join("/")];let i=new Set,r=1<<n.length;for(let o=0;o<r;o++){let a=e.filter((u,c)=>{if(!this.matchers.paramOptionalSegment.test(e[c]))return!0;let g=n.indexOf(c);return!!(o&1<<g)}),l=a.length===0?"/":"/"+a.join("/");i.add(l)}return Array.from(i)}registerStaticPath(t,e){if(this.staticPathCache[t])return this.options.allowRegisterUpdateExisting||this.throwError(3,t),this.staticPathCache[t];let n=Object.create(e??this.options.storeFactory());return this.staticPathCache[t]=n,n}registerDynamicPath(t,e){let n=t.split("/").filter(Boolean),i=this.root,r=e??this.options.storeFactory(),o=n.length;for(let a=0;a<o;a++){let l=n[a];if(this.matchers.staticSegment.test(l))i=i.staticChildren[l]??=this.createNode();else if(this.plugins.length>0){let u=!1;for(let c of this.plugins){let g=c.handler(l);if(g!=null){g.wildcard&&a!==o-1&&this.throwError(26,l),u=!0,g.id=c.id,g.priority=c.priority,g.syntax=c.syntax,i.dynamicChildren??=[];let d=i.dynamicChildren,m=d.find(P=>P.pluginMeta?.id===g.id&&P.pluginMeta?.paramName===g.paramName);if(m)i=m;else{d.find(f=>f.pluginMeta?.id===g.id)&&g.override!==!0&&this.throwError(25,`${l} (Plugin ID conflict: ${g.id})`);let h=this.createNode();h.pluginMeta=g,d.push(h),d.sort((f,I)=>f.pluginMeta.priority-I.pluginMeta.priority),i=h}break}}u||this.throwError(24,l)}else this.throwError(24,l)}return i.store?(i.pluginMeta?.override!==!0&&this.options.allowRegisterUpdateExisting===!1&&this.throwError(3,t),i.pluginMeta?.override===!0&&(i.store=r)):i.store=r,i.registeredPath=t,i.store}unregisterStaticPath(t){return this.staticPathCache[t]?(delete this.staticPathCache[t],!0):!1}unregisterDynamicPath(t){let e=this.root,n=t.split("/").filter(Boolean),{unregistered:i}=this.cleanupTraversal(e,n,0);return i}cleanupTraversal(t,e,n){let i=a=>Object.keys(a.staticChildren).length===0&&!a.dynamicChildren?.length,r=a=>i(a)&&!a.store;if(n===e.length)return t.store?(t.store=void 0,t.registeredPath=void 0,{shouldDelete:i(t),unregistered:!0}):{shouldDelete:i(t),unregistered:!1};let o=e[n];if(this.matchers.staticSegment.test(o)){let a=t.staticChildren[o];if(a){let{shouldDelete:l,unregistered:u}=this.cleanupTraversal(a,e,n+1);if(l)return delete t.staticChildren[o],{shouldDelete:r(t),unregistered:u};if(u)return{shouldDelete:!1,unregistered:!0}}}else if(this.plugins.length>0){let a=t.dynamicChildren;if(a)for(let l=0;l<this.plugins.length;l++){let u=this.plugins[l];for(let c=0;c<a.length;c++){let g=a[c],d=g.pluginMeta,m=u?.handler(o);if(d?.id===u?.id&&d?.paramName===m?.paramName){let{shouldDelete:P,unregistered:h}=this.cleanupTraversal(g,e,n+1);if(P)return a.splice(c,1),{shouldDelete:r(t),unregistered:h};if(h)return{shouldDelete:!1,unregistered:!0}}}}}return{shouldDelete:!1,unregistered:!1}}validatePlugin(t){typeof t!="function"&&this.throwError(7,typeof t);let e;try{e=t()}catch(o){this.throwError(23,o instanceof Error?o.message:String(o))}(!e||typeof e!="object")&&this.throwError(8,typeof e),e.id||this.throwError(10),typeof e.id!="string"&&this.throwError(12,typeof e.id),this.plugins.some(o=>o.id===e.id)&&this.throwError(5,e.id),e.priority||this.throwError(11),typeof e.priority!="number"&&this.throwError(13,typeof e.priority),this.plugins.some(o=>o.priority===e.priority)&&this.throwError(6,String(e.priority)),e.syntax||this.throwError(14),typeof e.syntax!="string"&&this.throwError(15,typeof e.syntax),e.handler||this.throwError(16,e.id),typeof e.handler!="function"&&this.throwError(7,typeof e.handler);let n,i=e.syntax;try{n=e.handler(i)}catch(o){this.throwError(23,o instanceof Error?o.message:String(o))}n==null&&this.throwError(18,i),(!n||typeof n!="object")&&this.throwError(19,typeof n),n.match||this.throwError(20),typeof n.match!="function"&&this.throwError(21,typeof n.match);let r;try{r=n.match({urlSegment:"",urlSegments:[""],index:0,params:{}})}catch(o){this.throwError(23,o instanceof Error?o.message:String(o))}return typeof r!="boolean"&&this.throwError(22,typeof r),e}use(t){let e=this.options.skipPluginValidation?t():this.validatePlugin(t);return this.plugins.push(e),this.plugins.sort((n,i)=>n.priority-i.priority),this}inspect(){let t=[];for(let i in this.staticPathCache)if(Object.prototype.hasOwnProperty.call(this.staticPathCache,i)){let r=this.staticPathCache[i];t.push({path:i,type:"static",store:r})}let e=new Set,n=i=>{i.store&&i.registeredPath&&(e.has(i.registeredPath)||(t.push({path:i.registeredPath,type:"dynamic",store:i.store}),e.add(i.registeredPath)));for(let r in i.staticChildren)Object.prototype.hasOwnProperty.call(i.staticChildren,r)&&n(i.staticChildren[r]);if(i.dynamicChildren)for(let r of i.dynamicChildren)n(r)};return n(this.root),t}register(t){if(t||this.throwError(4),this.matchers.staticPath.test(t))return this.registerStaticPath(t);if(this.matchers.paramOptionalInPath.test(t)){let e=this.generateOptionals(t),n=this.options.storeFactory();for(let i of e)this.matchers.staticPath.test(i)?this.registerStaticPath(i,n):this.registerDynamicPath(i,n);return n}return this.registerDynamicPath(t)}unregister(t){if(this.matchers.staticPath.test(t))return this.unregisterStaticPath(t);if(this.matchers.paramOptionalInPath.test(t)){let e=this.generateOptionals(t),n=!1;for(let i of e)this.matchers.staticPath.test(i)?n=this.unregisterStaticPath(i):n=this.unregisterDynamicPath(i);return n}return this.unregisterDynamicPath(t)}match(t){let e=this.staticPathCache[t];if(e)return e;let n=t.split("/").filter(Boolean),i=n.length,r=this.root,o=Object.create(null),a=0;for(;a<i;a++){let l=n[a],u=r.staticChildren[l];if(u){r=u;continue}let c=r.dynamicChildren;if(c){let g=!1;for(let d=0;d<c.length;d++){let m=c[d],P=m?.pluginMeta;if(P?.match({urlSegment:l,urlSegments:n,index:a,params:o})){if(P.wildcard&&m?.store!==void 0){let h=Object.create(m.store);return h.params=o,h}r=m,g=!0;break}}if(!g)return null}else return null}if(a===i&&r.store){let l=Object.create(r.store);return l.params=o,l}return null}};var S=()=>({id:"param",priority:700,syntax:":paramName",handler:i=>{let r=/^:(?<paramName>[a-zA-Z0-9_-]+)$/.exec(i);if(!r||!r.groups||!r.groups.paramName)return null;let o=r.groups.paramName;return{paramName:o,match({urlSegment:a,params:l}){return l[o]=a,!0}}}});var b=()=>({id:"wildcard",priority:800,syntax:"*",handler:i=>{let r=/^(?:\*|:(?<wildcardName>[a-zA-Z0-9_-]+)\*)$/.exec(i);if(!r)return null;let o=r.groups?.wildcardName??"*";return{paramName:o,wildcard:!0,match({urlSegments:a,index:l,params:u}){let c=a[l],g=a.length;for(let d=l+1;d<g;d++)c+="/"+a[d];return u[o]=c||"",!0}}}});var w=()=>({id:"regexParam",priority:400,syntax:":paramName<\\d+>",handler:i=>{let r=/^:(?<paramName>[a-zA-Z0-9_-]+)<(?<regex>.+)>$/.exec(i);if(!r||!r.groups||!r.groups.paramName||!r.groups.regex)return null;let o=r.groups.paramName,a=new RegExp(`^${r.groups.regex}$`);return{paramName:o,additionalMeta:{regex:a},match({urlSegment:l,params:u}){let c=l.match(a);return c?(u[o]=c[0],!0):!1}}}});var M=()=>({id:"extensionParam",priority:500,syntax:":file.css",handler:i=>{let r=/^:(?<paramName>[a-zA-Z0-9_-]+)\.(?<extension>.+)$/.exec(i);if(!r||!r.groups||!r.groups.paramName||!r.groups.extension)return null;let o=r.groups.paramName,a=r.groups.extension;return{paramName:o,additionalMeta:{extension:a},match({urlSegment:l,params:u}){return l.endsWith(a)?(u[o]=l.slice(0,-(a.length+1)),!0):!1}}}});var O=()=>({id:"groupParam",priority:300,syntax:":paramName(a|b)",handler:i=>{let r=/^:(?<paramName>[a-zA-Z0-9_-]+)\((?<dynamicGroup>[^|)]+(\|[^|)]+)+)\)$/.exec(i);if(!r||!r.groups||!r.groups.paramName||!r.groups.dynamicGroup)return null;let o=r.groups.paramName,a=r.groups.dynamicGroup,l=Object.fromEntries(a.split("|").map(u=>[u,u]));return{paramName:o,additionalMeta:{group:l},match({urlSegment:u,params:c}){return!l||!l[u]?!1:(c[o]=u,!0)}}}});var C=()=>({id:"prefixGroup",priority:100,syntax:"prefix(a|b)",handler:i=>{let r=/^(?<staticName>[a-zA-Z0-9_.-]+)\((?<dynamicGroup>[^|)]+(\|[^|)]+)+)\)$/.exec(i);if(!r||!r.groups||!r.groups.staticName||!r.groups.dynamicGroup)return null;let o=r.groups.staticName,a=r.groups.dynamicGroup,l=Object.fromEntries(a.split("|").map(u=>[o+u,o+u]));return{paramName:"",additionalMeta:{group:l},match({urlSegment:u}){return!!(l&&l[u])}}}});var R=()=>({id:"optionalPrefixGroup",priority:200,syntax:"prefix(a|b)?",handler:i=>{let r=/^(?<staticName>[a-zA-Z0-9_.-]+)\((?<dynamicGroup>[^|)]+(\|[^|)]+)+)\)\?$/.exec(i);if(!r||!r.groups||!r.groups.staticName||!r.groups.dynamicGroup)return null;let o=r.groups.staticName,a=r.groups.dynamicGroup,l=Object.fromEntries(a.split("|").map(u=>[o+u,o+u]));return l[o]=o,{paramName:"",additionalMeta:{group:l},match({urlSegment:u}){return!!(l&&l[u])}}}});var D=()=>({id:"optionalParam",priority:600,syntax:":paramName?",handler:i=>{let r=/^:(?<paramName>[a-zA-Z0-9_-]+)\?$/.exec(i);if(!r||!r.groups||!r.groups.paramName)return null;let o=r.groups.paramName;return{paramName:o,override:!0,match({urlSegment:a,params:l}){return l[o]=a,!0}}}});var H=p=>{let{id:t,priority:e,syntax:n,detect:i,test:r,wildcard:o,override:a}=p;return()=>({id:t,priority:e,syntax:n,handler:g=>{let d=i.exec(g);if(!d||!d.groups||!d.groups.paramName)return null;let m=d.groups,P=d.groups.paramName;return{paramName:P,...o!==void 0&&{wildcard:o},...a!==void 0&&{override:a},match({urlSegment:h,params:f}){return r(h,m)?(f[P]=h,!0):!1}}}})};var G=y;0&&(module.exports={createPlugin,extensionParam,groupParam,optionalParam,optionalPrefixGroup,param,prefixGroup,regexParam,wildcard});
2
2
  //# sourceMappingURL=index.cjs.map