@real-router/route-utils 0.1.4 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,97 +1,58 @@
1
1
  # @real-router/route-utils
2
2
 
3
- [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
4
- [![TypeScript](https://img.shields.io/badge/TypeScript-5.x-blue.svg)](https://www.typescriptlang.org/)
3
+ [![npm](https://img.shields.io/npm/v/@real-router/route-utils.svg?style=flat-square)](https://www.npmjs.com/package/@real-router/route-utils)
4
+ [![npm downloads](https://img.shields.io/npm/dm/@real-router/route-utils.svg?style=flat-square)](https://www.npmjs.com/package/@real-router/route-utils)
5
+ [![bundle size](https://deno.bundlejs.com/?q=@real-router/route-utils&treeshake=[*]&badge=detailed)](https://bundlejs.com/?q=@real-router/route-utils&treeshake=[*])
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg?style=flat-square)](../../LICENSE)
5
7
 
6
- Route tree queries and segment testing utilities for Real-Router. Pre-computed lookups for ancestor chains and siblings, plus regex-based segment matching with currying support.
8
+ > Cached read-only query API for [Real-Router](https://github.com/greydragon888/real-router) route tree structure. Pre-computed ancestor chains, sibling lookups, and regex-based segment testers.
7
9
 
8
10
  ## Installation
9
11
 
10
12
  ```bash
11
13
  npm install @real-router/route-utils
12
- # or
13
- pnpm add @real-router/route-utils
14
14
  ```
15
15
 
16
16
  ## Quick Start
17
17
 
18
18
  ```typescript
19
- import { createRouter, getPluginApi } from "@real-router/core";
19
+ import { createRouter } from "@real-router/core";
20
+ import { getPluginApi } from "@real-router/core/api";
20
21
  import { getRouteUtils, startsWithSegment } from "@real-router/route-utils";
21
22
 
22
23
  const router = createRouter([
23
24
  { name: "home", path: "/" },
24
- {
25
- name: "users",
26
- path: "/users",
27
- children: [
28
- {
29
- name: "profile",
30
- path: "/:id",
31
- children: [{ name: "edit", path: "/edit" }],
32
- },
33
- { name: "settings", path: "/settings" },
34
- ],
35
- },
25
+ { name: "users", path: "/users", children: [
26
+ { name: "profile", path: "/:id", children: [
27
+ { name: "edit", path: "/edit" },
28
+ ]},
29
+ { name: "settings", path: "/settings" },
30
+ ]},
36
31
  { name: "admin", path: "/admin" },
37
32
  ]);
38
33
 
39
34
  const utils = getRouteUtils(getPluginApi(router).getTree());
40
35
 
41
- utils.getChain("users.profile"); // ["users", "users.profile"]
42
- utils.getSiblings("users"); // ["home", "admin"]
43
- utils.isDescendantOf("users.profile", "users"); // true
36
+ utils.getChain("users.profile"); // ["users", "users.profile"]
37
+ utils.getSiblings("users"); // ["home", "admin"]
38
+ utils.isDescendantOf("users.profile", "users"); // true
44
39
 
45
- startsWithSegment("users.profile", "users"); // true
40
+ startsWithSegment("users.profile", "users"); // true
46
41
  ```
47
42
 
48
- ---
43
+ ## RouteUtils
49
44
 
50
- ## API
45
+ Pre-computes all route tree data in the constructor. Subsequent lookups are O(1) Map reads.
51
46
 
52
- ### `RouteUtils` Class
53
-
54
- [Wiki](https://github.com/greydragon888/real-router/wiki/route-utils)
55
-
56
- Pre-computes all route tree data eagerly in the constructor. Subsequent lookups are O(1) Map reads.
57
-
58
- #### `new RouteUtils(root: RouteTreeNode)`
59
-
60
- Creates a new instance. All ancestor chains and sibling lists are frozen during construction.
61
-
62
- #### `utils.getChain(name): readonly string[] | undefined`
63
-
64
- Returns the cumulative ancestor chain for a route. Root returns `[""]`; other routes exclude root.
65
-
66
- ```typescript
67
- utils.getChain("users.profile.edit");
68
- // → ["users", "users.profile", "users.profile.edit"]
69
- ```
70
-
71
- #### `utils.getSiblings(name): readonly string[] | undefined`
72
-
73
- Returns non-absolute siblings of a route (excluding itself). Root returns `undefined`.
74
-
75
- #### `utils.isDescendantOf(child, parent): boolean`
76
-
77
- O(k) string prefix check. Does not perform tree lookup.
78
-
79
- #### Static Facade
80
-
81
- `RouteUtils` exposes segment testers as `static readonly` properties — delegates to standalone functions:
82
-
83
- ```typescript
84
- RouteUtils.startsWithSegment("users.list", "users"); // true
85
- RouteUtils.endsWithSegment("users.profile.edit", "edit"); // true
86
- RouteUtils.includesSegment("a.b.c.d", "b.c"); // true
87
- RouteUtils.areRoutesRelated("users", "users.profile"); // true
88
- ```
89
-
90
- ---
47
+ | Method | Returns | Description |
48
+ |--------|---------|-------------|
49
+ | `getChain(name)` | `readonly string[] \| undefined` | Ancestor chain (e.g., `["users", "users.profile", "users.profile.edit"]`) |
50
+ | `getSiblings(name)` | `readonly string[] \| undefined` | Sibling routes (excluding itself) |
51
+ | `isDescendantOf(child, parent)` | `boolean` | O(k) string prefix check |
91
52
 
92
53
  ### `getRouteUtils(root): RouteUtils`
93
54
 
94
- WeakMap-cached factory. Same `RouteTreeNode` reference same instance. [Wiki](https://github.com/greydragon888/real-router/wiki/route-utils)
55
+ WeakMap-cached factory same tree reference returns the same instance:
95
56
 
96
57
  ```typescript
97
58
  const utils1 = getRouteUtils(tree);
@@ -99,77 +60,64 @@ const utils2 = getRouteUtils(tree);
99
60
  utils1 === utils2; // true
100
61
  ```
101
62
 
102
- ---
63
+ ## Segment Testers
103
64
 
104
- ### Segment Testers
65
+ Standalone functions for testing dot-separated route name segments. Each supports direct, curried, and `State` object calling patterns.
105
66
 
106
- Standalone functions for testing dot-separated route name segments. Each supports direct, curried, and `State` object calling patterns. [Wiki](https://github.com/greydragon888/real-router/wiki/route-utils#startswithsegmentroute-segment)
107
-
108
- #### `startsWithSegment(route, segment?)`
109
-
110
- Tests if a route name starts with the given segment (respects dot boundaries).
67
+ | Function | Description |
68
+ |----------|-------------|
69
+ | `startsWithSegment(route, segment?)` | Route starts with segment (dot-bounded) |
70
+ | `endsWithSegment(route, segment?)` | Route ends with segment |
71
+ | `includesSegment(route, segment?)` | Route includes segment anywhere (contiguous) |
72
+ | `areRoutesRelated(route1, route2)` | Routes are same, parent-child, or child-parent |
111
73
 
112
74
  ```typescript
113
- startsWithSegment("users.list", "users"); // true
114
- startsWithSegment("users2.list", "users"); // false (dot boundary)
75
+ startsWithSegment("users.list", "users"); // true
76
+ startsWithSegment("users2.list", "users"); // false (dot boundary)
77
+ endsWithSegment("users.profile.edit", "edit"); // true
78
+ includesSegment("a.b.c.d", "b.c"); // true
79
+ areRoutesRelated("users", "users.profile"); // true
115
80
 
116
- // Curried form
81
+ // Curried form — first arg is route, returns tester for segments
117
82
  const tester = startsWithSegment("users.list");
118
- tester("users"); // true
119
- ```
120
-
121
- #### `endsWithSegment(route, segment?)`
122
-
123
- Tests if a route name ends with the given segment. [Wiki](https://github.com/greydragon888/real-router/wiki/route-utils#endswithsegmentroute-segment)
124
-
125
- #### `includesSegment(route, segment?)`
126
-
127
- Tests if a route name includes the given segment anywhere (contiguous, dot-bounded). [Wiki](https://github.com/greydragon888/real-router/wiki/route-utils#includessegmentroute-segment)
128
-
129
- #### `areRoutesRelated(route1, route2): boolean`
130
-
131
- Checks if two routes are related in the hierarchy (same, parent-child, or child-parent). [Wiki](https://github.com/greydragon888/real-router/wiki/route-utils#areroutesrelatedroute1-route2)
132
-
133
- ---
83
+ tester("users"); // true
134
84
 
135
- ### Types
136
-
137
- ```typescript
138
- import type { SegmentTestFunction } from "@real-router/route-utils";
85
+ // Static access via RouteUtils
86
+ RouteUtils.startsWithSegment("users.list", "users"); // true
139
87
  ```
140
88
 
141
- See [Wiki](https://github.com/greydragon888/real-router/wiki/route-utils#segmenttestfunction) for the full interface definition.
142
-
143
- ---
144
-
145
- ## Segment Validation
146
-
147
- All segment testers validate input:
89
+ ### Input Validation
148
90
 
149
91
  - **Max length**: 10,000 characters (`RangeError`)
150
92
  - **Allowed characters**: `a-z`, `A-Z`, `0-9`, `.`, `-`, `_` (`TypeError`)
151
93
  - **Empty / null segment**: returns `false` (no error)
152
94
 
153
- ---
154
-
155
95
  ## Performance
156
96
 
157
- | Operation | Complexity | Notes |
158
- | --------------------------- | ---------- | -------------------------------- |
159
- | Construction | O(n) | Single DFS, n = number of routes |
160
- | `getChain` / `getSiblings` | O(1) | Frozen cached arrays |
161
- | `isDescendantOf` | O(k) | String prefix check |
162
- | `getRouteUtils` (cache hit) | O(1) | WeakMap lookup |
163
- | Segment tester (cached) | O(k) | Regex test |
97
+ | Operation | Complexity | Notes |
98
+ |-----------|------------|-------|
99
+ | Construction | O(n) | Single DFS, n = number of routes |
100
+ | `getChain` / `getSiblings` | O(1) | Frozen cached arrays |
101
+ | `isDescendantOf` | O(k) | String prefix check |
102
+ | `getRouteUtils` (cache hit) | O(1) | WeakMap lookup |
103
+ | Segment testers | O(k) | Regex test, cached |
104
+
105
+ ## Documentation
164
106
 
165
- ---
107
+ Full documentation: [Wiki — route-utils](https://github.com/greydragon888/real-router/wiki/route-utils)
166
108
 
167
109
  ## Related Packages
168
110
 
169
- - [@real-router/core](https://www.npmjs.com/package/@real-router/core) Core router
170
- - [@real-router/react](https://www.npmjs.com/package/@real-router/react) — React integration (`useRouteUtils` hook)
171
- - [@real-router/browser-plugin](https://www.npmjs.com/package/@real-router/browser-plugin) Browser history
111
+ | Package | Description |
112
+ |---------|-------------|
113
+ | [@real-router/core](https://www.npmjs.com/package/@real-router/core) | Core router |
114
+ | [@real-router/react](https://www.npmjs.com/package/@real-router/react) | React integration (`useRouteUtils` hook) |
115
+ | [@real-router/sources](https://www.npmjs.com/package/@real-router/sources) | Subscription layer (uses route-utils internally) |
116
+
117
+ ## Contributing
118
+
119
+ See [contributing guidelines](../../CONTRIBUTING.md) for development setup and PR process.
172
120
 
173
121
  ## License
174
122
 
175
- MIT © [Oleg Ivanov](https://github.com/greydragon888)
123
+ [MIT](../../LICENSE) © [Oleg Ivanov](https://github.com/greydragon888)
@@ -1 +1 @@
1
- {"inputs":{"../../node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js":{"bytes":569,"imports":[],"format":"esm"},"src/routeRelation.ts":{"bytes":994,"imports":[{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"},"src/constants.ts":{"bytes":515,"imports":[{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"},"src/segmentTesters.ts":{"bytes":8570,"imports":[{"path":"src/constants.ts","kind":"import-statement","original":"./constants"},{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"},"src/RouteUtils.ts":{"bytes":4807,"imports":[{"path":"src/routeRelation.ts","kind":"import-statement","original":"./routeRelation.js"},{"path":"src/segmentTesters.ts","kind":"import-statement","original":"./segmentTesters.js"},{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"},"src/getRouteUtils.ts":{"bytes":365,"imports":[{"path":"src/RouteUtils.ts","kind":"import-statement","original":"./RouteUtils.js"},{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"},"src/index.ts":{"bytes":310,"imports":[{"path":"src/RouteUtils.ts","kind":"import-statement","original":"./RouteUtils.js"},{"path":"src/getRouteUtils.ts","kind":"import-statement","original":"./getRouteUtils.js"},{"path":"src/segmentTesters.ts","kind":"import-statement","original":"./segmentTesters.js"},{"path":"src/routeRelation.ts","kind":"import-statement","original":"./routeRelation.js"},{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"}},"outputs":{"dist/cjs/index.js.map":{"imports":[],"exports":[],"inputs":{},"bytes":19261},"dist/cjs/index.js":{"imports":[],"exports":["RouteUtils","areRoutesRelated","endsWithSegment","getRouteUtils","includesSegment","startsWithSegment"],"entryPoint":"src/index.ts","inputs":{"src/routeRelation.ts":{"bytesInOutput":144},"src/constants.ts":{"bytesInOutput":105},"src/segmentTesters.ts":{"bytesInOutput":1998},"src/RouteUtils.ts":{"bytesInOutput":3845},"src/index.ts":{"bytesInOutput":0},"src/getRouteUtils.ts":{"bytesInOutput":215}},"bytes":6546}}}
1
+ {"inputs":{"../../node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.8_typescript@5.9.3_yaml@2.8.2/node_modules/tsup/assets/cjs_shims.js":{"bytes":569,"imports":[],"format":"esm"},"src/routeRelation.ts":{"bytes":994,"imports":[{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.8_typescript@5.9.3_yaml@2.8.2/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"},"src/constants.ts":{"bytes":515,"imports":[{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.8_typescript@5.9.3_yaml@2.8.2/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"},"src/segmentTesters.ts":{"bytes":8570,"imports":[{"path":"src/constants.ts","kind":"import-statement","original":"./constants"},{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.8_typescript@5.9.3_yaml@2.8.2/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"},"src/RouteUtils.ts":{"bytes":4807,"imports":[{"path":"src/routeRelation.ts","kind":"import-statement","original":"./routeRelation.js"},{"path":"src/segmentTesters.ts","kind":"import-statement","original":"./segmentTesters.js"},{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.8_typescript@5.9.3_yaml@2.8.2/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"},"src/getRouteUtils.ts":{"bytes":365,"imports":[{"path":"src/RouteUtils.ts","kind":"import-statement","original":"./RouteUtils.js"},{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.8_typescript@5.9.3_yaml@2.8.2/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"},"src/index.ts":{"bytes":310,"imports":[{"path":"src/RouteUtils.ts","kind":"import-statement","original":"./RouteUtils.js"},{"path":"src/getRouteUtils.ts","kind":"import-statement","original":"./getRouteUtils.js"},{"path":"src/segmentTesters.ts","kind":"import-statement","original":"./segmentTesters.js"},{"path":"src/routeRelation.ts","kind":"import-statement","original":"./routeRelation.js"},{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.8_typescript@5.9.3_yaml@2.8.2/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"}},"outputs":{"dist/cjs/index.js.map":{"imports":[],"exports":[],"inputs":{},"bytes":19261},"dist/cjs/index.js":{"imports":[],"exports":["RouteUtils","areRoutesRelated","endsWithSegment","getRouteUtils","includesSegment","startsWithSegment"],"entryPoint":"src/index.ts","inputs":{"src/routeRelation.ts":{"bytesInOutput":144},"src/constants.ts":{"bytesInOutput":105},"src/segmentTesters.ts":{"bytesInOutput":1998},"src/RouteUtils.ts":{"bytesInOutput":3845},"src/index.ts":{"bytesInOutput":0},"src/getRouteUtils.ts":{"bytesInOutput":215}},"bytes":6546}}}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@real-router/route-utils",
3
- "version": "0.1.4",
3
+ "version": "0.1.5",
4
4
  "type": "commonjs",
5
5
  "description": "Cached read-only query API for route tree structure",
6
6
  "main": "./dist/cjs/index.js",
@@ -42,16 +42,17 @@
42
42
  "sideEffects": false,
43
43
  "devDependencies": {
44
44
  "mitata": "1.0.34",
45
- "route-tree": "^0.3.3"
45
+ "route-tree": "^0.3.4"
46
46
  },
47
47
  "dependencies": {
48
- "@real-router/types": "^0.23.0"
48
+ "@real-router/types": "^0.24.0"
49
49
  },
50
50
  "scripts": {
51
51
  "build": "tsup",
52
52
  "type-check": "tsc --noEmit",
53
53
  "lint": "eslint --cache --ext .ts src/ tests/ --fix --max-warnings 0 --no-error-on-unmatched-pattern",
54
54
  "test": "vitest run",
55
+ "test:properties": "vitest --config vitest.config.properties.mts --run",
55
56
  "bench": "NODE_OPTIONS='--expose-gc --max-old-space-size=4096' npx tsx tests/benchmarks/index.ts"
56
57
  }
57
58
  }