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 +22 -0
- package/LICENSE +1 -1
- package/README.md +85 -42
- package/assets/logo.svg +35 -0
- package/dist/bundle-size.json +12 -12
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +57 -1
- package/dist/index.d.ts +57 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/docs/error-types.md +35 -35
- package/docs/examples/browser.md +130 -130
- package/docs/examples/server.bun.md +73 -73
- package/docs/examples/server.deno.md +71 -71
- package/docs/examples/server.node.md +102 -102
- package/docs/optional-parameters-priority.md +64 -64
- package/package.json +10 -9
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
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="
|
|
7
|
-
|
|
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
|
|
|
10
8
|
<img src="https://img.shields.io/badge/License-MIT-orange.svg?color=orange" alt="License: MIT" />
|
|
11
9
|
</div>
|
|
12
|
-
|
|
10
|
+
|
|
13
11
|
<br />
|
|
14
12
|
|
|
15
|
-
|
|
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
|
-
##
|
|
17
|
+
## Table of Contents
|
|
20
18
|
|
|
21
|
-
- [
|
|
22
|
-
- [
|
|
23
|
-
- [
|
|
24
|
-
- [
|
|
25
|
-
- [
|
|
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
|
-
- [
|
|
28
|
-
- [
|
|
25
|
+
- [Custom Plugins](#custom-plugins)
|
|
26
|
+
- [createPlugin Helper](#createplugin-helper)
|
|
27
|
+
- [API](#api)
|
|
29
28
|
- [Error Types](./docs/error-types.md)
|
|
30
|
-
- [
|
|
31
|
-
- [
|
|
32
|
-
- [
|
|
33
|
-
- [
|
|
34
|
-
- [
|
|
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
|
-
##
|
|
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:
|
|
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
|
-
##
|
|
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
|
-
##
|
|
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
|
-
##
|
|
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
|
-
##
|
|
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
|
-
##
|
|
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 [
|
|
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
|
-
##
|
|
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
|
-
##
|
|
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
|
-
##
|
|
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 ([
|
|
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
|
-
##
|
|
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
|
-
##
|
|
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
|
-
##
|
|
701
|
+
## License
|
|
659
702
|
|
|
660
703
|
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
package/assets/logo.svg
ADDED
|
@@ -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>
|
package/dist/bundle-size.json
CHANGED
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
{
|
|
2
2
|
"files": {
|
|
3
3
|
"index.cjs": {
|
|
4
|
-
"minified": "13
|
|
5
|
-
"gzipped": "4.
|
|
6
|
-
"minifiedBytes":
|
|
7
|
-
"gzippedBytes":
|
|
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.
|
|
11
|
-
"gzipped": "
|
|
12
|
-
"minifiedBytes":
|
|
13
|
-
"gzippedBytes":
|
|
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":
|
|
18
|
-
"gzipped":
|
|
19
|
-
"minifiedKB": "
|
|
20
|
-
"gzippedKB": "
|
|
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
|