@openelement/url-pattern-list 0.6.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/LICENSE +7 -0
- package/README.md +163 -0
- package/index.d.ts +77 -0
- package/index.d.ts.map +1 -0
- package/index.js +160 -0
- package/index.js.map +1 -0
- package/package.json +79 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
Copyright 2025 Justin Fagnani
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
4
|
+
|
|
5
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
6
|
+
|
|
7
|
+
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
# @openelement/url-pattern-list
|
|
2
|
+
|
|
3
|
+
Efficiently match URL paths against a collection of URL patterns using a
|
|
4
|
+
fixed pathname-literal index with conservative fallback.
|
|
5
|
+
|
|
6
|
+
> **Fork note:** this is the OpenElement-maintained fork of
|
|
7
|
+
> [justinfagnani/url-pattern-list](https://github.com/justinfagnani/url-pattern-list)
|
|
8
|
+
> v0.5.0. See [PROVENANCE.md](./PROVENANCE.md) for sources and license, and
|
|
9
|
+
> [DIVERGENCE.md](./DIVERGENCE.md) for what differs and why.
|
|
10
|
+
|
|
11
|
+
## Overview
|
|
12
|
+
|
|
13
|
+
`url-pattern-list` is a JavaScript library that provides an efficient way to
|
|
14
|
+
match URLs against multiple
|
|
15
|
+
[URLPattern](https://developer.mozilla.org/en-US/docs/Web/API/URLPattern)
|
|
16
|
+
instances. Instead of testing every pattern linearly, `URLPatternList`
|
|
17
|
+
indexes patterns whose pathname is a canonical literal in a fixed prefix
|
|
18
|
+
tree, keeps all other patterns in a conservative list, and merges both by
|
|
19
|
+
registration order at match time — so only patterns that can possibly match
|
|
20
|
+
are exec'd.
|
|
21
|
+
|
|
22
|
+
`URLPatternList` has exactly the same matching semantics as scanning a
|
|
23
|
+
linear list of patterns, and is differentially tested against such a linear
|
|
24
|
+
oracle for both native and polyfill URLPattern constructors. The first
|
|
25
|
+
pattern (in the order patterns were added to the list) whose complete
|
|
26
|
+
`exec()` matches a URL is returned as the match.
|
|
27
|
+
|
|
28
|
+
Patterns are added to the list along with an additional value that is returned
|
|
29
|
+
with the match. This makes it easy to associate a URLPattern with metadata or an
|
|
30
|
+
object like a server route handler.
|
|
31
|
+
|
|
32
|
+
## Installation
|
|
33
|
+
|
|
34
|
+
```sh
|
|
35
|
+
npm i @openelement/url-pattern-list
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Quick Start
|
|
39
|
+
|
|
40
|
+
```typescript
|
|
41
|
+
import {URLPatternList} from '@openelement/url-pattern-list';
|
|
42
|
+
|
|
43
|
+
// Create a new pattern list
|
|
44
|
+
const routes = new URLPatternList<string>();
|
|
45
|
+
|
|
46
|
+
// Add patterns with associated values
|
|
47
|
+
routes.addPattern(new URLPattern({pathname: '/api/users/:id'}), 'user-detail');
|
|
48
|
+
routes.addPattern(new URLPattern({pathname: '/api/users'}), 'user-list');
|
|
49
|
+
routes.addPattern(new URLPattern({pathname: '/api/posts/:id'}), 'post-detail');
|
|
50
|
+
|
|
51
|
+
// Match against a URL
|
|
52
|
+
const match = routes.match('/api/users/123');
|
|
53
|
+
if (match) {
|
|
54
|
+
console.log('Route:', match.value); // 'user-detail'
|
|
55
|
+
console.log('User ID:', match.result.pathname.groups.id); // '123'
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Performance
|
|
60
|
+
|
|
61
|
+
Lookup cost is driven by the number of candidate patterns exec'd, not the
|
|
62
|
+
number of patterns registered: static-heavy workloads exec a handful of
|
|
63
|
+
candidates at any scale, while workloads dominated by non-literal patterns
|
|
64
|
+
(regex, groups, wildcards) degrade gracefully to linear scan. Benchmarks
|
|
65
|
+
cover construction, hit, miss and memory against a linear oracle and
|
|
66
|
+
upstream v0.5.0; see [BENCHMARKS.md](./BENCHMARKS.md) for numbers and
|
|
67
|
+
methodology.
|
|
68
|
+
|
|
69
|
+
To run the benchmark on your machine:
|
|
70
|
+
|
|
71
|
+
```sh
|
|
72
|
+
npm i --prefix .tmp-upstream url-pattern-list@0.5.0 # optional comparison
|
|
73
|
+
npm run benchmark
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## API Reference
|
|
77
|
+
|
|
78
|
+
### URLPatternList<T>
|
|
79
|
+
|
|
80
|
+
The main class for managing and matching URL patterns.
|
|
81
|
+
|
|
82
|
+
```ts
|
|
83
|
+
import {URLPatternList} from '@openelement/url-pattern-list';
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
#### Methods
|
|
87
|
+
|
|
88
|
+
##### `addPattern(pattern: ListPattern, value: T): void`
|
|
89
|
+
|
|
90
|
+
Add a URL pattern to the collection with an associated value. `ListPattern`
|
|
91
|
+
is any object with a `pathname` getter and the `exec()` method of the
|
|
92
|
+
URLPattern interface — native `URLPattern` and `urlpattern-polyfill`
|
|
93
|
+
instances both work.
|
|
94
|
+
|
|
95
|
+
```typescript
|
|
96
|
+
const list = new URLPatternList<RouteHandler>();
|
|
97
|
+
list.addPattern(new URLPattern({pathname: '/users/:id'}), handleUserDetail);
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
##### `match(url: string | URL, baseUrl?: string): URLPatternListMatch<T> | null`
|
|
101
|
+
|
|
102
|
+
Match a URL against all patterns, returning the first match found. Relative
|
|
103
|
+
string input requires `baseUrl`; invalid input throws a `TypeError`, even
|
|
104
|
+
for an empty list.
|
|
105
|
+
|
|
106
|
+
```typescript
|
|
107
|
+
const match = list.match('/users/123', 'https://example.com');
|
|
108
|
+
if (match) {
|
|
109
|
+
// match.result contains the URLPatternResult
|
|
110
|
+
// match.value contains your associated value
|
|
111
|
+
}
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
##### `candidateCount(url: string | URL, baseUrl?: string): number`
|
|
115
|
+
|
|
116
|
+
Diagnostic upper bound on how many patterns `match()` would exec for the
|
|
117
|
+
given input. Not part of the matching semantics.
|
|
118
|
+
|
|
119
|
+
### Types
|
|
120
|
+
|
|
121
|
+
#### URLPatternListMatch<T>
|
|
122
|
+
|
|
123
|
+
```typescript
|
|
124
|
+
interface URLPatternListMatch<T> {
|
|
125
|
+
result: URLPatternResult; // Standard URLPattern match result
|
|
126
|
+
value: T; // Your associated value
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## Browser Support
|
|
131
|
+
|
|
132
|
+
This library works with any
|
|
133
|
+
[URLPattern](https://developer.mozilla.org/en-US/docs/Web/API/URLPattern)
|
|
134
|
+
implementation you supply — native:
|
|
135
|
+
|
|
136
|
+
- Chrome 95+
|
|
137
|
+
- Firefox 142+ (Preview support)
|
|
138
|
+
- Safari 26.0+ (Preview support)
|
|
139
|
+
|
|
140
|
+
— or the [URLPattern
|
|
141
|
+
polyfill](https://github.com/kenchris/urlpattern-polyfill) (patterns built
|
|
142
|
+
from either constructor can be mixed in one list).
|
|
143
|
+
|
|
144
|
+
## Visualizer
|
|
145
|
+
|
|
146
|
+
The upstream visualizer was removed in 0.6.0 because it rendered the
|
|
147
|
+
internals of the removed per-component prefix tree. See
|
|
148
|
+
[DIVERGENCE.md](./DIVERGENCE.md).
|
|
149
|
+
|
|
150
|
+
## Contributing
|
|
151
|
+
|
|
152
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
153
|
+
|
|
154
|
+
## License
|
|
155
|
+
|
|
156
|
+
MIT License. See [LICENSE](LICENSE) file for details.
|
|
157
|
+
|
|
158
|
+
## Related
|
|
159
|
+
|
|
160
|
+
- [URLPattern on
|
|
161
|
+
MDN](https://developer.mozilla.org/en-US/docs/Web/API/URLPattern)
|
|
162
|
+
- [URLPattern Specification](https://urlpattern.spec.whatwg.org/)
|
|
163
|
+
- [URLPattern Polyfill](https://github.com/kenchris/urlpattern-polyfill)
|
package/index.d.ts
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* url-pattern-list - efficiently match URLs against a collection of URL
|
|
3
|
+
* patterns.
|
|
4
|
+
*
|
|
5
|
+
* This implementation is derived from the semantics OpenElement established
|
|
6
|
+
* for its maintained fork: only canonical pathname literals are indexed, every
|
|
7
|
+
* other pattern is matched conservatively, and candidates are merged by
|
|
8
|
+
* registration order before a full `exec()` decides the match. See
|
|
9
|
+
* PROVENANCE.md and DIVERGENCE.md.
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* The minimal pattern interface stored by a URLPatternList.
|
|
13
|
+
*
|
|
14
|
+
* Any object with a `pathname` getter and the `exec()` method of the
|
|
15
|
+
* URLPattern interface works: native `URLPattern` instances and
|
|
16
|
+
* polyfill (`urlpattern-polyfill`) instances alike. Pattern parsing,
|
|
17
|
+
* compilation and capture semantics belong entirely to the pattern's
|
|
18
|
+
* constructor; this library only stores, indexes and matches patterns in
|
|
19
|
+
* registration order.
|
|
20
|
+
*/
|
|
21
|
+
export interface ListPattern {
|
|
22
|
+
readonly pathname: string;
|
|
23
|
+
exec(input: string, baseURL?: string): URLPatternResult | null;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* The return type of `URLPatternList.match()`.
|
|
27
|
+
*
|
|
28
|
+
* Includes the result of `pattern.exec()` and the matching pattern's associated
|
|
29
|
+
* metadata value.
|
|
30
|
+
*/
|
|
31
|
+
export interface URLPatternListMatch<T> {
|
|
32
|
+
result: URLPatternResult;
|
|
33
|
+
value: T;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* A collection of URL patterns and associated values, with methods for adding
|
|
37
|
+
* patterns and matching URLs against those patterns.
|
|
38
|
+
*
|
|
39
|
+
* Patterns with a canonical pathname literal are stored in a fixed prefix tree
|
|
40
|
+
* keyed by that literal; all other patterns are stored in a conservative list.
|
|
41
|
+
* Matching execs the candidates of both collections in registration order and
|
|
42
|
+
* returns the first complete match. This maintains first-match-wins
|
|
43
|
+
* semantics - the result is the same as scanning a linear list of patterns -
|
|
44
|
+
* while only execing the patterns that can possibly match.
|
|
45
|
+
*
|
|
46
|
+
* Pruning is based solely on canonical pathname literal equality, which is a
|
|
47
|
+
* necessary condition for `exec()` to match. Pruning never involves other URL
|
|
48
|
+
* components, so empty URL components never disappear from matching and the
|
|
49
|
+
* final `exec()` sees every pattern that could match.
|
|
50
|
+
*/
|
|
51
|
+
export declare class URLPatternList<T> {
|
|
52
|
+
#private;
|
|
53
|
+
/**
|
|
54
|
+
* Add a URL pattern to the collection.
|
|
55
|
+
*/
|
|
56
|
+
addPattern(pattern: ListPattern, value: T): void;
|
|
57
|
+
/**
|
|
58
|
+
* Match a URL against the URLPatterns, returning the first match found and
|
|
59
|
+
* its associated value.
|
|
60
|
+
*
|
|
61
|
+
* The input is normalized once with the `URL` constructor and both pruning
|
|
62
|
+
* and `exec()` consume the same normalized URL. Invalid input throws a
|
|
63
|
+
* `TypeError`, including for an empty list; relative string input requires a
|
|
64
|
+
* `baseUrl`.
|
|
65
|
+
*
|
|
66
|
+
* @param url - The URL to match
|
|
67
|
+
* @param baseUrl - Optional base URL for relative path resolution
|
|
68
|
+
*/
|
|
69
|
+
match(url: string | URL, baseUrl?: string): URLPatternListMatch<T> | null;
|
|
70
|
+
/**
|
|
71
|
+
* Diagnostic upper bound on the number of patterns that `match()` would
|
|
72
|
+
* exec for the given input. Not part of the matching semantics; no matcher
|
|
73
|
+
* internals are exposed.
|
|
74
|
+
*/
|
|
75
|
+
candidateCount(url: string | URL, baseUrl?: string): number;
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=index.d.ts.map
|
package/index.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH;;;;;;;;;GASG;AACH,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,gBAAgB,GAAG,IAAI,CAAC;CAChE;AAaD;;;;;GAKG;AACH,MAAM,WAAW,mBAAmB,CAAC,CAAC;IACpC,MAAM,EAAE,gBAAgB,CAAC;IACzB,KAAK,EAAE,CAAC,CAAC;CACV;AAwCD;;;;;;;;;;;;;;;GAeG;AACH,qBAAa,cAAc,CAAC,CAAC;;IAK3B;;OAEG;IACH,UAAU,CAAC,OAAO,EAAE,WAAW,EAAE,KAAK,EAAE,CAAC,GAAG,IAAI;IAqChD;;;;;;;;;;;OAWG;IACH,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,mBAAmB,CAAC,CAAC,CAAC,GAAG,IAAI;IAwCzE;;;;OAIG;IACH,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM;CAI5D"}
|
package/index.js
ADDED
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* url-pattern-list - efficiently match URLs against a collection of URL
|
|
3
|
+
* patterns.
|
|
4
|
+
*
|
|
5
|
+
* This implementation is derived from the semantics OpenElement established
|
|
6
|
+
* for its maintained fork: only canonical pathname literals are indexed, every
|
|
7
|
+
* other pattern is matched conservatively, and candidates are merged by
|
|
8
|
+
* registration order before a full `exec()` decides the match. See
|
|
9
|
+
* PROVENANCE.md and DIVERGENCE.md.
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* A node in the fixed pathname prefix tree. Each node holds the patterns whose
|
|
13
|
+
* canonical pathname literal ends exactly at this node.
|
|
14
|
+
*/
|
|
15
|
+
class FixedPrefixTreeNode {
|
|
16
|
+
children = new Map();
|
|
17
|
+
patterns = [];
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Returns the index key for a pattern pathname, or `undefined` if the pathname
|
|
21
|
+
* is not a canonical literal.
|
|
22
|
+
*
|
|
23
|
+
* This is deliberately a small literal alphabet, not a URLPattern grammar
|
|
24
|
+
* parser. These characters have no pattern operators or escapes. Every other
|
|
25
|
+
* spelling remains conservative, including groups, regex, empty paths and
|
|
26
|
+
* Unicode.
|
|
27
|
+
*
|
|
28
|
+
* ASCII case folding over-selects candidates for case-sensitive patterns and
|
|
29
|
+
* admits ignoreCase patterns without relying on a non-standard URLPattern
|
|
30
|
+
* options getter; `exec()` still determines the real case semantics.
|
|
31
|
+
*/
|
|
32
|
+
const literalPathnameKey = (pathname) => {
|
|
33
|
+
if (!pathname.startsWith('/')) {
|
|
34
|
+
return undefined;
|
|
35
|
+
}
|
|
36
|
+
for (const char of pathname) {
|
|
37
|
+
if (!'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789/_-.%~'.includes(char)) {
|
|
38
|
+
return undefined;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return pathname.toLowerCase();
|
|
42
|
+
};
|
|
43
|
+
/**
|
|
44
|
+
* A collection of URL patterns and associated values, with methods for adding
|
|
45
|
+
* patterns and matching URLs against those patterns.
|
|
46
|
+
*
|
|
47
|
+
* Patterns with a canonical pathname literal are stored in a fixed prefix tree
|
|
48
|
+
* keyed by that literal; all other patterns are stored in a conservative list.
|
|
49
|
+
* Matching execs the candidates of both collections in registration order and
|
|
50
|
+
* returns the first complete match. This maintains first-match-wins
|
|
51
|
+
* semantics - the result is the same as scanning a linear list of patterns -
|
|
52
|
+
* while only execing the patterns that can possibly match.
|
|
53
|
+
*
|
|
54
|
+
* Pruning is based solely on canonical pathname literal equality, which is a
|
|
55
|
+
* necessary condition for `exec()` to match. Pruning never involves other URL
|
|
56
|
+
* components, so empty URL components never disappear from matching and the
|
|
57
|
+
* final `exec()` sees every pattern that could match.
|
|
58
|
+
*/
|
|
59
|
+
export class URLPatternList {
|
|
60
|
+
#root = new FixedPrefixTreeNode();
|
|
61
|
+
#conservative = [];
|
|
62
|
+
#sequenceCounter = 0;
|
|
63
|
+
/**
|
|
64
|
+
* Add a URL pattern to the collection.
|
|
65
|
+
*/
|
|
66
|
+
addPattern(pattern, value) {
|
|
67
|
+
const item = {
|
|
68
|
+
sequence: this.#sequenceCounter++,
|
|
69
|
+
pattern,
|
|
70
|
+
value,
|
|
71
|
+
};
|
|
72
|
+
const key = literalPathnameKey(pattern.pathname);
|
|
73
|
+
if (key === undefined) {
|
|
74
|
+
this.#conservative.push(item);
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
let node = this.#root;
|
|
78
|
+
for (const char of key) {
|
|
79
|
+
let child = node.children.get(char);
|
|
80
|
+
if (child === undefined) {
|
|
81
|
+
child = new FixedPrefixTreeNode();
|
|
82
|
+
node.children.set(char, child);
|
|
83
|
+
}
|
|
84
|
+
node = child;
|
|
85
|
+
}
|
|
86
|
+
node.patterns.push(item);
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* The patterns whose canonical pathname literal equals the URL's pathname.
|
|
90
|
+
*/
|
|
91
|
+
#fixedCandidates(url) {
|
|
92
|
+
let node = this.#root;
|
|
93
|
+
for (const char of url.pathname.toLowerCase()) {
|
|
94
|
+
node = node.children.get(char);
|
|
95
|
+
if (node === undefined) {
|
|
96
|
+
return [];
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return node.patterns;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Match a URL against the URLPatterns, returning the first match found and
|
|
103
|
+
* its associated value.
|
|
104
|
+
*
|
|
105
|
+
* The input is normalized once with the `URL` constructor and both pruning
|
|
106
|
+
* and `exec()` consume the same normalized URL. Invalid input throws a
|
|
107
|
+
* `TypeError`, including for an empty list; relative string input requires a
|
|
108
|
+
* `baseUrl`.
|
|
109
|
+
*
|
|
110
|
+
* @param url - The URL to match
|
|
111
|
+
* @param baseUrl - Optional base URL for relative path resolution
|
|
112
|
+
*/
|
|
113
|
+
match(url, baseUrl) {
|
|
114
|
+
const normalized = new URL(String(url), baseUrl);
|
|
115
|
+
const fullURL = normalized.href;
|
|
116
|
+
const fixed = this.#fixedCandidates(normalized);
|
|
117
|
+
// Merge the fixed-tree candidates and the conservative candidates by
|
|
118
|
+
// original sequence, so the first complete exec match in registration
|
|
119
|
+
// order wins. Both arrays are already sorted by sequence.
|
|
120
|
+
let fixedIndex = 0;
|
|
121
|
+
let conservativeIndex = 0;
|
|
122
|
+
while (fixedIndex < fixed.length ||
|
|
123
|
+
conservativeIndex < this.#conservative.length) {
|
|
124
|
+
let item;
|
|
125
|
+
if (fixedIndex >= fixed.length) {
|
|
126
|
+
item = this.#conservative[conservativeIndex++];
|
|
127
|
+
}
|
|
128
|
+
else if (conservativeIndex >= this.#conservative.length) {
|
|
129
|
+
item = fixed[fixedIndex++];
|
|
130
|
+
}
|
|
131
|
+
else if (fixed[fixedIndex].sequence <
|
|
132
|
+
this.#conservative[conservativeIndex].sequence) {
|
|
133
|
+
item = fixed[fixedIndex++];
|
|
134
|
+
}
|
|
135
|
+
else {
|
|
136
|
+
item = this.#conservative[conservativeIndex++];
|
|
137
|
+
}
|
|
138
|
+
// Pass baseUrl through to exec like upstream did, so the result's
|
|
139
|
+
// `inputs` reflect the caller's arguments. The normalized URL is
|
|
140
|
+
// absolute, so the base never changes whether exec matches.
|
|
141
|
+
const result = baseUrl === undefined
|
|
142
|
+
? item.pattern.exec(fullURL)
|
|
143
|
+
: item.pattern.exec(fullURL, baseUrl);
|
|
144
|
+
if (result !== null) {
|
|
145
|
+
return { result, value: item.value };
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
return null;
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Diagnostic upper bound on the number of patterns that `match()` would
|
|
152
|
+
* exec for the given input. Not part of the matching semantics; no matcher
|
|
153
|
+
* internals are exposed.
|
|
154
|
+
*/
|
|
155
|
+
candidateCount(url, baseUrl) {
|
|
156
|
+
const normalized = new URL(String(url), baseUrl);
|
|
157
|
+
return this.#fixedCandidates(normalized).length + this.#conservative.length;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
//# sourceMappingURL=index.js.map
|
package/index.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAuCH;;;GAGG;AACH,MAAM,mBAAmB;IACd,QAAQ,GAAG,IAAI,GAAG,EAAkC,CAAC;IACrD,QAAQ,GAAiC,EAAE,CAAC;CACtD;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,kBAAkB,GAAG,CAAC,QAAgB,EAAsB,EAAE;IAClE,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QAC9B,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,KAAK,MAAM,IAAI,IAAI,QAAQ,EAAE,CAAC;QAC5B,IACE,CAAC,sEAAsE,CAAC,QAAQ,CAC9E,IAAI,CACL,EACD,CAAC;YACD,OAAO,SAAS,CAAC;QACnB,CAAC;IACH,CAAC;IACD,OAAO,QAAQ,CAAC,WAAW,EAAE,CAAC;AAChC,CAAC,CAAC;AAEF;;;;;;;;;;;;;;;GAeG;AACH,MAAM,OAAO,cAAc;IAChB,KAAK,GAAG,IAAI,mBAAmB,EAAK,CAAC;IACrC,aAAa,GAAiC,EAAE,CAAC;IAC1D,gBAAgB,GAAG,CAAC,CAAC;IAErB;;OAEG;IACH,UAAU,CAAC,OAAoB,EAAE,KAAQ;QACvC,MAAM,IAAI,GAA0B;YAClC,QAAQ,EAAE,IAAI,CAAC,gBAAgB,EAAE;YACjC,OAAO;YACP,KAAK;SACN,CAAC;QACF,MAAM,GAAG,GAAG,kBAAkB,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;QACjD,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;YACtB,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC9B,OAAO;QACT,CAAC;QACD,IAAI,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC;QACtB,KAAK,MAAM,IAAI,IAAI,GAAG,EAAE,CAAC;YACvB,IAAI,KAAK,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YACpC,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;gBACxB,KAAK,GAAG,IAAI,mBAAmB,EAAK,CAAC;gBACrC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;YACjC,CAAC;YACD,IAAI,GAAG,KAAK,CAAC;QACf,CAAC;QACD,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC3B,CAAC;IAED;;OAEG;IACH,gBAAgB,CAAC,GAAQ;QACvB,IAAI,IAAI,GAAuC,IAAI,CAAC,KAAK,CAAC;QAC1D,KAAK,MAAM,IAAI,IAAI,GAAG,CAAC,QAAQ,CAAC,WAAW,EAAE,EAAE,CAAC;YAC9C,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YAC/B,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;gBACvB,OAAO,EAAE,CAAC;YACZ,CAAC;QACH,CAAC;QACD,OAAO,IAAI,CAAC,QAAQ,CAAC;IACvB,CAAC;IAED;;;;;;;;;;;OAWG;IACH,KAAK,CAAC,GAAiB,EAAE,OAAgB;QACvC,MAAM,UAAU,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC,CAAC;QACjD,MAAM,OAAO,GAAG,UAAU,CAAC,IAAI,CAAC;QAChC,MAAM,KAAK,GAAG,IAAI,CAAC,gBAAgB,CAAC,UAAU,CAAC,CAAC;QAChD,qEAAqE;QACrE,sEAAsE;QACtE,0DAA0D;QAC1D,IAAI,UAAU,GAAG,CAAC,CAAC;QACnB,IAAI,iBAAiB,GAAG,CAAC,CAAC;QAC1B,OACE,UAAU,GAAG,KAAK,CAAC,MAAM;YACzB,iBAAiB,GAAG,IAAI,CAAC,aAAa,CAAC,MAAM,EAC7C,CAAC;YACD,IAAI,IAA2B,CAAC;YAChC,IAAI,UAAU,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;gBAC/B,IAAI,GAAG,IAAI,CAAC,aAAa,CAAC,iBAAiB,EAAE,CAAC,CAAC;YACjD,CAAC;iBAAM,IAAI,iBAAiB,IAAI,IAAI,CAAC,aAAa,CAAC,MAAM,EAAE,CAAC;gBAC1D,IAAI,GAAG,KAAK,CAAC,UAAU,EAAE,CAAC,CAAC;YAC7B,CAAC;iBAAM,IACL,KAAK,CAAC,UAAU,CAAC,CAAC,QAAQ;gBAC1B,IAAI,CAAC,aAAa,CAAC,iBAAiB,CAAC,CAAC,QAAQ,EAC9C,CAAC;gBACD,IAAI,GAAG,KAAK,CAAC,UAAU,EAAE,CAAC,CAAC;YAC7B,CAAC;iBAAM,CAAC;gBACN,IAAI,GAAG,IAAI,CAAC,aAAa,CAAC,iBAAiB,EAAE,CAAC,CAAC;YACjD,CAAC;YACD,kEAAkE;YAClE,iEAAiE;YACjE,4DAA4D;YAC5D,MAAM,MAAM,GACV,OAAO,KAAK,SAAS;gBACnB,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC;gBAC5B,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;YAC1C,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;gBACpB,OAAO,EAAC,MAAM,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAC,CAAC;YACrC,CAAC;QACH,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;;;OAIG;IACH,cAAc,CAAC,GAAiB,EAAE,OAAgB;QAChD,MAAM,UAAU,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC,CAAC;QACjD,OAAO,IAAI,CAAC,gBAAgB,CAAC,UAAU,CAAC,CAAC,MAAM,GAAG,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC;IAC9E,CAAC;CACF"}
|
package/package.json
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@openelement/url-pattern-list",
|
|
3
|
+
"version": "0.6.0",
|
|
4
|
+
"description": "Efficiently match URLs against a collection of URL patterns",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "index.js",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"build": "wireit",
|
|
9
|
+
"test": "wireit",
|
|
10
|
+
"benchmark": "wireit",
|
|
11
|
+
"format": "npm run format:fix",
|
|
12
|
+
"format:check": "prettier \"**/*.{cjs,html,js,json,md,ts}\" --check",
|
|
13
|
+
"format:fix": "prettier \"**/*.{cjs,html,js,json,md,ts}\" --write"
|
|
14
|
+
},
|
|
15
|
+
"repository": {
|
|
16
|
+
"type": "git",
|
|
17
|
+
"url": "git+https://github.com/open-element/url-pattern-list.git"
|
|
18
|
+
},
|
|
19
|
+
"keywords": [
|
|
20
|
+
"url",
|
|
21
|
+
"urlpattern",
|
|
22
|
+
"url-pattern"
|
|
23
|
+
],
|
|
24
|
+
"author": "Justin Fagnani <justin@fagnani.com>",
|
|
25
|
+
"license": "MIT",
|
|
26
|
+
"bugs": {
|
|
27
|
+
"url": "https://github.com/open-element/url-pattern-list/issues"
|
|
28
|
+
},
|
|
29
|
+
"homepage": "https://github.com/open-element/url-pattern-list#readme",
|
|
30
|
+
"devDependencies": {
|
|
31
|
+
"@types/node": "^24.2.0",
|
|
32
|
+
"prettier": "^3.6.2",
|
|
33
|
+
"prettier-plugin-curly": "^0.3.2",
|
|
34
|
+
"typescript": "^5.9.2",
|
|
35
|
+
"urlpattern-polyfill": "^10.1.0",
|
|
36
|
+
"wireit": "^0.14.12"
|
|
37
|
+
},
|
|
38
|
+
"files": [
|
|
39
|
+
"index.js",
|
|
40
|
+
"index.d.ts",
|
|
41
|
+
"index.js.map",
|
|
42
|
+
"index.d.ts.map"
|
|
43
|
+
],
|
|
44
|
+
"exports": {
|
|
45
|
+
".": {
|
|
46
|
+
"types": "./index.d.ts",
|
|
47
|
+
"default": "./index.js"
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
"wireit": {
|
|
51
|
+
"build": {
|
|
52
|
+
"command": "tsc --build",
|
|
53
|
+
"files": [
|
|
54
|
+
"tsconfig.json",
|
|
55
|
+
"src/**/*.ts"
|
|
56
|
+
],
|
|
57
|
+
"outputs": [
|
|
58
|
+
"index.{js,d.ts,js.map,d.ts.map}"
|
|
59
|
+
],
|
|
60
|
+
"clean": "if-file-deleted"
|
|
61
|
+
},
|
|
62
|
+
"test": {
|
|
63
|
+
"command": "node --enable-source-maps --test --test-reporter=spec \"test/**/*_test.js\"",
|
|
64
|
+
"dependencies": [
|
|
65
|
+
"build"
|
|
66
|
+
],
|
|
67
|
+
"files": [],
|
|
68
|
+
"output": []
|
|
69
|
+
},
|
|
70
|
+
"benchmark": {
|
|
71
|
+
"command": "node --expose-gc --enable-source-maps benchmark/benchmark.js",
|
|
72
|
+
"dependencies": [
|
|
73
|
+
"build"
|
|
74
|
+
],
|
|
75
|
+
"files": [],
|
|
76
|
+
"output": []
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|