Skip to content

Commit ede4bdb

Browse files
authored
feat: export functions to make custom matchers (#38)
To allow creating custom matchers, export all the necessary functions from the `/utils` path.
1 parent 3d8210d commit ede4bdb

File tree

6 files changed

+261
-209
lines changed

6 files changed

+261
-209
lines changed

.github/dependabot.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ updates:
55
schedule:
66
interval: daily
77
time: "10:00"
8-
open-pull-requests-limit: 10
8+
open-pull-requests-limit: 20
99
commit-message:
1010
prefix: "deps"
1111
prefix-development: "deps(dev)"

README.md

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
# @multiformats/multiaddr-matcher
2+
13
[![multiformats.io](https://img.shields.io/badge/project-IPFS-blue.svg?style=flat-square)](http://multiformats.io)
24
[![codecov](https://img.shields.io/codecov/c/github/multiformats/js-multiaddr-matcher.svg?style=flat-square)](https://codecov.io/gh/multiformats/js-multiaddr-matcher)
35
[![CI](https://img.shields.io/github/actions/workflow/status/multiformats/js-multiaddr-matcher/js-test-and-release.yml?branch=main\&style=flat-square)](https://github.com/multiformats/js-multiaddr-matcher/actions/workflows/js-test-and-release.yml?query=branch%3Amain)
@@ -6,6 +8,21 @@
68
79
# About
810

11+
<!--
12+
13+
!IMPORTANT!
14+
15+
Everything in this README between "# About" and "# Install" is automatically
16+
generated and will be overwritten the next time the doc generator is run.
17+
18+
To make changes to this section, please update the @packageDocumentation section
19+
of src/index.js or src/index.ts
20+
21+
To experiment with formatting, please run "npm run docs" from the root of this
22+
repo and examine the changes made.
23+
24+
-->
25+
926
This module exports various matchers that can be used to infer the type of a
1027
passed multiaddr.
1128

@@ -44,7 +61,7 @@ $ npm i @multiformats/multiaddr-matcher
4461

4562
## Browser `<script>` tag
4663

47-
Loading this module through a script tag will make it's exports available as `MultiformatsMultiaddrMatcher` in the global namespace.
64+
Loading this module through a script tag will make its exports available as `MultiformatsMultiaddrMatcher` in the global namespace.
4865

4966
```html
5067
<script src="https://unpkg.com/@multiformats/multiaddr-matcher/dist/index.min.js"></script>
@@ -58,8 +75,8 @@ Loading this module through a script tag will make it's exports available as `Mu
5875

5976
Licensed under either of
6077

61-
- Apache 2.0, ([LICENSE-APACHE](LICENSE-APACHE) / <http://www.apache.org/licenses/LICENSE-2.0>)
62-
- MIT ([LICENSE-MIT](LICENSE-MIT) / <http://opensource.org/licenses/MIT>)
78+
- Apache 2.0, ([LICENSE-APACHE](https://github.com/multiformats/js-multiaddr-matcher/LICENSE-APACHE) / <http://www.apache.org/licenses/LICENSE-2.0>)
79+
- MIT ([LICENSE-MIT](https://github.com/multiformats/js-multiaddr-matcher/LICENSE-MIT) / <http://opensource.org/licenses/MIT>)
6380

6481
# Contribution
6582

package.json

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,31 @@
1111
"bugs": {
1212
"url": "https://github.com/multiformats/js-multiaddr-matcher/issues"
1313
},
14+
"publishConfig": {
15+
"access": "public",
16+
"provenance": true
17+
},
1418
"keywords": [
1519
"multiaddr"
1620
],
1721
"type": "module",
1822
"types": "./dist/src/index.d.ts",
23+
"typesVersions": {
24+
"*": {
25+
"*": [
26+
"*",
27+
"dist/*",
28+
"dist/src/*",
29+
"dist/src/*/index"
30+
],
31+
"src/*": [
32+
"*",
33+
"dist/*",
34+
"dist/src/*",
35+
"dist/src/*/index"
36+
]
37+
}
38+
},
1939
"files": [
2040
"src",
2141
"dist",
@@ -26,6 +46,10 @@
2646
".": {
2747
"types": "./dist/src/index.d.ts",
2848
"import": "./dist/src/index.js"
49+
},
50+
"./utils": {
51+
"types": "./dist/src/utils.d.ts",
52+
"import": "./dist/src/utils.js"
2953
}
3054
},
3155
"eslintConfig": {

src/index.ts

Lines changed: 9 additions & 204 deletions
Original file line numberDiff line numberDiff line change
@@ -33,224 +33,29 @@
3333
*/
3434

3535
import { isIPv4, isIPv6 } from '@chainsafe/is-ip'
36-
import { type Multiaddr } from '@multiformats/multiaddr'
37-
import { base58btc } from 'multiformats/bases/base58'
38-
import { base64url } from 'multiformats/bases/base64'
39-
40-
/**
41-
* Split a multiaddr into path components
42-
*/
43-
const toParts = (ma: Multiaddr): string[] => {
44-
return ma.toString().split('/').slice(1)
45-
}
36+
import { and, or, literal, string, peerId, optional, fmt, func, number, certhash } from './utils.js'
37+
import type { Multiaddr } from '@multiformats/multiaddr'
4638

4739
/**
4840
* A matcher accepts multiaddr components and either fails to match and returns
4941
* false or returns a sublist of unmatched components
5042
*/
51-
interface Matcher {
43+
export interface Matcher {
5244
match(parts: string[]): string[] | false
5345
pattern: string
5446
}
5547

56-
const func = (fn: (val: string) => boolean): Matcher => {
57-
return {
58-
match: (vals) => {
59-
if (vals.length < 1) {
60-
return false
61-
}
62-
63-
if (fn(vals[0])) {
64-
return vals.slice(1)
65-
}
66-
67-
return false
68-
},
69-
pattern: 'fn'
70-
}
71-
}
72-
73-
const literal = (str: string): Matcher => {
74-
return {
75-
match: (vals) => func((val) => val === str).match(vals),
76-
pattern: str
77-
}
78-
}
79-
80-
const string = (): Matcher => {
81-
return {
82-
match: (vals) => func((val) => typeof val === 'string').match(vals),
83-
pattern: '{string}'
84-
}
85-
}
86-
87-
const number = (): Matcher => {
88-
return {
89-
match: (vals) => func((val) => !isNaN(parseInt(val))).match(vals),
90-
pattern: '{number}'
91-
}
92-
}
93-
94-
const peerId = (): Matcher => {
95-
return {
96-
match: (vals) => {
97-
if (vals.length < 2) {
98-
return false
99-
}
100-
101-
if (vals[0] !== 'p2p' && vals[0] !== 'ipfs') {
102-
return false
103-
}
104-
105-
// Q is RSA, 1 is Ed25519 or Secp256k1
106-
if (vals[1].startsWith('Q') || vals[1].startsWith('1')) {
107-
try {
108-
base58btc.decode(`z${vals[1]}`)
109-
} catch (err) {
110-
return false
111-
}
112-
} else {
113-
return false
114-
}
115-
116-
return vals.slice(2)
117-
},
118-
pattern: '/p2p/{peerid}'
119-
}
120-
}
121-
122-
const certhash = (): Matcher => {
123-
return {
124-
match: (vals) => {
125-
if (vals.length < 2) {
126-
return false
127-
}
128-
129-
if (vals[0] !== 'certhash') {
130-
return false
131-
}
132-
133-
try {
134-
base64url.decode(vals[1])
135-
} catch {
136-
return false
137-
}
138-
139-
return vals.slice(2)
140-
},
141-
pattern: '/certhash/{certhash}'
142-
}
143-
}
144-
145-
const optional = (matcher: Matcher): Matcher => {
146-
return {
147-
match: (vals) => {
148-
const result = matcher.match(vals)
149-
150-
if (result === false) {
151-
return vals
152-
}
153-
154-
return result
155-
},
156-
pattern: `optional(${matcher.pattern})`
157-
}
158-
}
159-
160-
const or = (...matchers: Matcher[]): Matcher => {
161-
return {
162-
match: (vals) => {
163-
let matches: string[] | undefined
164-
165-
for (const matcher of matchers) {
166-
const result = matcher.match(vals)
167-
168-
// no match
169-
if (result === false) {
170-
continue
171-
}
172-
173-
// choose greediest matcher
174-
if (matches == null || result.length < matches.length) {
175-
matches = result
176-
}
177-
}
178-
179-
if (matches == null) {
180-
return false
181-
}
182-
183-
return matches
184-
},
185-
pattern: `or(${matchers.map(m => m.pattern).join(', ')})`
186-
}
187-
}
188-
189-
const and = (...matchers: Matcher[]): Matcher => {
190-
return {
191-
match: (vals) => {
192-
for (const matcher of matchers) {
193-
// pass what's left of the array
194-
const result = matcher.match(vals)
195-
196-
// no match
197-
if (result === false) {
198-
return false
199-
}
200-
201-
vals = result
202-
}
203-
204-
return vals
205-
},
206-
pattern: `and(${matchers.map(m => m.pattern).join(', ')})`
207-
}
208-
}
209-
210-
function fmt (...matchers: Matcher[]): MultiaddrMatcher {
211-
function match (ma: Multiaddr): string[] | false {
212-
let parts = toParts(ma)
213-
214-
for (const matcher of matchers) {
215-
const result = matcher.match(parts)
216-
217-
if (result === false) {
218-
return false
219-
}
220-
221-
parts = result
222-
}
223-
224-
return parts
225-
}
226-
227-
function matches (ma: Multiaddr): boolean {
228-
const result = match(ma)
229-
230-
return result !== false
231-
}
232-
233-
function exactMatch (ma: Multiaddr): boolean {
234-
const result = match(ma)
235-
236-
if (result === false) {
237-
return false
238-
}
239-
240-
return result.length === 0
241-
}
242-
243-
return {
244-
matches,
245-
exactMatch
246-
}
247-
}
248-
24948
/**
25049
* A MultiaddrMatcher allows interpreting a multiaddr as a certain type of
25150
* multiaddr
25251
*/
25352
export interface MultiaddrMatcher {
53+
/**
54+
* The matchers that make up this MultiaddrMatcher - useful if you want to
55+
* make your own custom matchers
56+
*/
57+
matchers: Matcher[]
58+
25459
/**
25560
* Returns true if the passed multiaddr can be treated as this type of
25661
* multiaddr

0 commit comments

Comments
 (0)