From a6f48f40b21bcecebf80b5801f47020f071c4d9a Mon Sep 17 00:00:00 2001 From: achingbrain Date: Mon, 7 Jul 2025 11:44:58 +0200 Subject: [PATCH 1/4] feat: compare component codes instead of strings Switches matcher implementation to use multiaddr components and compare the codes rather than encoding/decoding strings which leads to a 2x speed up. --- benchmarks/operations/README.md | 26 ++++++++ benchmarks/operations/package.json | 21 +++++++ benchmarks/operations/src/index.ts | 1 + benchmarks/operations/test/index.ts | 58 +++++++++++++++++ benchmarks/operations/tsconfig.json | 15 +++++ src/index.ts | 98 +++++++++++++++-------------- src/utils.ts | 77 +++++------------------ 7 files changed, 188 insertions(+), 108 deletions(-) create mode 100644 benchmarks/operations/README.md create mode 100644 benchmarks/operations/package.json create mode 100644 benchmarks/operations/src/index.ts create mode 100644 benchmarks/operations/test/index.ts create mode 100644 benchmarks/operations/tsconfig.json diff --git a/benchmarks/operations/README.md b/benchmarks/operations/README.md new file mode 100644 index 0000000..f271f4f --- /dev/null +++ b/benchmarks/operations/README.md @@ -0,0 +1,26 @@ +# multiaddr Benchmark + +Benchmarks multiaddr performance during common operations - parsing strings, +encapsulating/decapsulating addresses, turning to bytes, decoding bytes, etc. + +## Running the benchmarks + +```console +% npm start + +> benchmarks-add-dir@1.0.0 start +> npm run build && node dist/src/index.js + + +> benchmarks-add-dir@1.0.0 build +> aegir build --bundle false + +[06:10:56] tsc [started] +[06:10:56] tsc [completed] +┌─────────┬─────────────────────────────────────────┬──────────────┬────────┬───────┬────────┐ +│ (index) │ Implementation │ ops/s │ ms/op │ runs │ p99 │ +├─────────┼─────────────────────────────────────────┼──────────────┼────────┼───────┼────────┤ +│ 0 │ 'head' │ '2427997.80' │ '0.00' │ 50000 │ '0.00' │ +│ 1 │ '@multiformats/multiaddr-matcher-1.7.2' │ '1098132.24' │ '0.00' │ 50000 │ '0.00' │ +└─────────┴─────────────────────────────────────────┴──────────────┴────────┴───────┴────────┘ +``` diff --git a/benchmarks/operations/package.json b/benchmarks/operations/package.json new file mode 100644 index 0000000..65c812e --- /dev/null +++ b/benchmarks/operations/package.json @@ -0,0 +1,21 @@ +{ + "name": "benchmarks-operations", + "version": "1.0.0", + "main": "index.js", + "private": true, + "type": "module", + "scripts": { + "clean": "aegir clean", + "build": "aegir build --bundle false", + "lint": "aegir lint", + "dep-check": "aegir dep-check", + "doc-check": "aegir doc-check", + "start": "npm run build && node dist/test/index.js" + }, + "devDependencies": { + "@multiformats/multiaddr-matcher-1.7.2": "npm:@multiformats/multiaddr-matcher@1.7.2", + "@multiformats/multiaddr-matcher": "../../", + "aegir": "^47.0.7", + "tinybench": "^4.0.1" + } +} diff --git a/benchmarks/operations/src/index.ts b/benchmarks/operations/src/index.ts new file mode 100644 index 0000000..336ce12 --- /dev/null +++ b/benchmarks/operations/src/index.ts @@ -0,0 +1 @@ +export {} diff --git a/benchmarks/operations/test/index.ts b/benchmarks/operations/test/index.ts new file mode 100644 index 0000000..ecc5cca --- /dev/null +++ b/benchmarks/operations/test/index.ts @@ -0,0 +1,58 @@ +/* eslint-disable no-console */ + +import { multiaddr } from '@multiformats/multiaddr' +import { TCP } from '@multiformats/multiaddr-matcher' +import { TCP as TCP_172 } from '@multiformats/multiaddr-matcher-1.7.2' +import { Bench } from 'tinybench' + +const ITERATIONS = parseInt(process.env.ITERATIONS ?? '50000') +const MIN_TIME = parseInt(process.env.MIN_TIME ?? '1') +const RESULT_PRECISION = 2 + +function bench (m: typeof TCP | typeof TCP_172): void { + const ma = multiaddr('/ip4/127.0.0.1/tcp/1234') + + m.exactMatch(ma) +} + +async function main (): Promise { + const suite = new Bench({ + iterations: ITERATIONS, + time: MIN_TIME + }) + suite.add('head', () => { + bench(TCP) + }) + suite.add('@multiformats/multiaddr-matcher-1.7.2', () => { + bench(TCP_172) + }) + + await suite.run() + + console.table(suite.tasks.map(({ name, result }) => { + if (result?.error != null) { + console.error(result.error) + + return { + Implementation: name, + 'ops/s': 'error', + 'ms/op': 'error', + runs: 'error', + p99: 'error' + } + } + + return { + Implementation: name, + 'ops/s': result?.hz.toFixed(RESULT_PRECISION), + 'ms/op': result?.period.toFixed(RESULT_PRECISION), + runs: result?.samples.length, + p99: result?.p99.toFixed(RESULT_PRECISION) + } + })) +} + +main().catch(err => { + console.error(err) + process.exit(1) +}) diff --git a/benchmarks/operations/tsconfig.json b/benchmarks/operations/tsconfig.json new file mode 100644 index 0000000..4a695eb --- /dev/null +++ b/benchmarks/operations/tsconfig.json @@ -0,0 +1,15 @@ +{ + "extends": "aegir/src/config/tsconfig.aegir.json", + "compilerOptions": { + "outDir": "dist" + }, + "include": [ + "src", + "test" + ], + "references": [ + { + "path": "../../" + } + ] +} diff --git a/src/index.ts b/src/index.ts index 389cf96..f2ea6a5 100644 --- a/src/index.ts +++ b/src/index.ts @@ -33,15 +33,15 @@ */ import { isIPv4, isIPv6 } from '@chainsafe/is-ip' -import { and, or, literal, string, peerId, optional, fmt, func, number, certhash } from './utils.js' -import type { Multiaddr } from '@multiformats/multiaddr' +import { and, or, optional, fmt, func, code, value } from './utils.js' +import { type Multiaddr, type Component, CODE_P2P, CODE_DNS4, CODE_DNS6, CODE_DNSADDR, CODE_DNS, CODE_IP4, CODE_IP6, CODE_TCP, CODE_UDP, CODE_QUIC, CODE_QUIC_V1, CODE_WS, CODE_WSS, CODE_TLS, CODE_SNI, CODE_WEBRTC_DIRECT, CODE_CERTHASH, CODE_WEBTRANSPORT, CODE_P2P_CIRCUIT, CODE_WEBRTC, CODE_HTTP, CODE_UNIX, CODE_HTTPS, CODE_MEMORY } from '@multiformats/multiaddr' /** * A matcher accepts multiaddr components and either fails to match and returns * false or returns a sublist of unmatched components */ export interface Matcher { - match(parts: string[]): string[] | false + match(parts: Component[]): Component[] | false pattern: string } @@ -82,17 +82,17 @@ export interface MultiaddrMatcher { * PEER_ID.matches(multiaddr('/ipfs/Qmfoo')) // true * ``` */ -const _PEER_ID = peerId() +const _PEER_ID = value(CODE_P2P) export const PEER_ID = fmt(_PEER_ID) /** * DNS matchers */ -const _DNS4 = and(literal('dns4'), string()) -const _DNS6 = and(literal('dns6'), string()) -const _DNSADDR = and(literal('dnsaddr'), string()) -const _DNS = and(literal('dns'), string()) +const _DNS4 = value(CODE_DNS4) +const _DNS6 = value(CODE_DNS6) +const _DNSADDR = value(CODE_DNSADDR) +const _DNS = value(CODE_DNS) /** * Matches dns4 addresses. @@ -108,7 +108,7 @@ const _DNS = and(literal('dns'), string()) * DNS4.matches(multiaddr('/dns4/example.org')) // true * ``` */ -export const DNS4 = fmt(_DNS4, optional(peerId())) +export const DNS4 = fmt(_DNS4, optional(value(CODE_P2P))) /** * Matches dns6 addresses. @@ -124,7 +124,7 @@ export const DNS4 = fmt(_DNS4, optional(peerId())) * DNS6.matches(multiaddr('/dns6/example.org')) // true * ``` */ -export const DNS6 = fmt(_DNS6, optional(peerId())) +export const DNS6 = fmt(_DNS6, optional(value(CODE_P2P))) /** * Matches dnsaddr addresses. @@ -141,7 +141,7 @@ export const DNS6 = fmt(_DNS6, optional(peerId())) * DNSADDR.matches(multiaddr('/dnsaddr/example.org/p2p/Qmfoo')) // true * ``` */ -export const DNSADDR = fmt(_DNSADDR, optional(peerId())) +export const DNSADDR = fmt(_DNSADDR, optional(value(CODE_P2P))) /** * Matches any dns address. @@ -158,10 +158,10 @@ export const DNSADDR = fmt(_DNSADDR, optional(peerId())) * DNS.matches(multiaddr('/dns6/example.org/p2p/Qmfoo')) // true * ``` */ -export const DNS = fmt(or(_DNS, _DNSADDR, _DNS4, _DNS6), optional(peerId())) +export const DNS = fmt(or(_DNS, _DNSADDR, _DNS4, _DNS6), optional(value(CODE_P2P))) -const _IP4 = and(literal('ip4'), func(isIPv4)) -const _IP6 = and(literal('ip6'), func(isIPv6)) +const _IP4 = value(CODE_IP4) +const _IP6 = value(CODE_IP6) const _IP = or(_IP4, _IP6) const _IP_OR_DOMAIN = or(_IP, _DNS, _DNS4, _DNS6, _DNSADDR) @@ -181,7 +181,7 @@ const _IP_OR_DOMAIN = or(_IP, _DNS, _DNS4, _DNS6, _DNSADDR) * IP_OR_DOMAIN.matches(multiaddr('/p2p/QmFoo')) // false * ``` */ -export const IP_OR_DOMAIN = fmt(or(_IP, and(or(_DNS, _DNSADDR, _DNS4, _DNS6), optional(peerId())))) +export const IP_OR_DOMAIN = fmt(or(_IP, and(or(_DNS, _DNSADDR, _DNS4, _DNS6), optional(value(CODE_P2P))))) /** * Matches ip4 addresses. @@ -234,8 +234,8 @@ export const IP6 = fmt(_IP6) */ export const IP = fmt(_IP) -const _TCP = and(_IP_OR_DOMAIN, literal('tcp'), number()) -const _UDP = and(_IP_OR_DOMAIN, literal('udp'), number()) +const _TCP = and(_IP_OR_DOMAIN, value(CODE_TCP)) +const _UDP = and(_IP_OR_DOMAIN, value(CODE_UDP)) /** * Matches TCP addresses. @@ -249,7 +249,7 @@ const _UDP = and(_IP_OR_DOMAIN, literal('udp'), number()) * TCP.matches(multiaddr('/ip4/123.123.123.123/tcp/1234')) // true * ``` */ -export const TCP = fmt(and(_TCP, optional(peerId()))) +export const TCP = fmt(and(_TCP, optional(value(CODE_P2P)))) /** * Matches UDP addresses. @@ -265,10 +265,10 @@ export const TCP = fmt(and(_TCP, optional(peerId()))) */ export const UDP = fmt(_UDP) -const _QUIC = and(_UDP, literal('quic'), optional(peerId())) -const _QUICV1 = and(_UDP, literal('quic-v1'), optional(peerId())) +const _QUIC = and(_UDP, code(CODE_QUIC), optional(value(CODE_P2P))) +const _QUIC_V1 = and(_UDP, code(CODE_QUIC_V1), optional(value(CODE_P2P))) -const QUIC_V0_OR_V1 = or(_QUIC, _QUICV1) +const QUIC_V0_OR_V1 = or(_QUIC, _QUIC_V1) /** * Matches QUIC addresses. @@ -296,18 +296,19 @@ export const QUIC = fmt(_QUIC) * QUICV1.matches(multiaddr('/ip4/123.123.123.123/udp/1234/quic-v1')) // true * ``` */ -export const QUICV1 = fmt(_QUICV1) +export const QUIC_V1 = fmt(_QUIC_V1) +export const QUICV1 = QUIC_V1 const _WEB = or( _IP_OR_DOMAIN, _TCP, _UDP, _QUIC, - _QUICV1 + _QUIC_V1 ) const _WebSockets = or( - and(_WEB, literal('ws'), optional(peerId())) + and(_WEB, code(CODE_WS), optional(value(CODE_P2P))) ) /** @@ -325,8 +326,8 @@ const _WebSockets = or( export const WebSockets = fmt(_WebSockets) const _WebSocketsSecure = or( - and(_WEB, literal('wss'), optional(peerId())), - and(_WEB, literal('tls'), optional(and(literal('sni'), string())), literal('ws'), optional(peerId())) + and(_WEB, code(CODE_WSS), optional(value(CODE_P2P))), + and(_WEB, code(CODE_TLS), optional(value(CODE_SNI)), code(CODE_WS), optional(value(CODE_P2P))) ) /** @@ -343,7 +344,7 @@ const _WebSocketsSecure = or( */ export const WebSocketsSecure = fmt(_WebSocketsSecure) -const _WebRTCDirect = and(_UDP, literal('webrtc-direct'), optional(certhash()), optional(certhash()), optional(peerId())) +const _WebRTCDirect = and(_UDP, code(CODE_WEBRTC_DIRECT), optional(value(CODE_CERTHASH)), optional(value(CODE_CERTHASH)), optional(value(CODE_P2P))) /** * Matches WebRTC-direct addresses. @@ -359,7 +360,7 @@ const _WebRTCDirect = and(_UDP, literal('webrtc-direct'), optional(certhash()), */ export const WebRTCDirect = fmt(_WebRTCDirect) -const _WebTransport = and(_QUICV1, literal('webtransport'), optional(certhash()), optional(certhash()), optional(peerId())) +const _WebTransport = and(_QUIC_V1, code(CODE_WEBTRANSPORT), optional(value(CODE_CERTHASH)), optional(value(CODE_CERTHASH)), optional(value(CODE_P2P))) /** * Matches WebTransport addresses. @@ -378,12 +379,12 @@ export const WebTransport = fmt(_WebTransport) const _P2P = or( _WebSockets, _WebSocketsSecure, - and(_TCP, optional(peerId())), - and(QUIC_V0_OR_V1, optional(peerId())), - and(_IP_OR_DOMAIN, optional(peerId())), + and(_TCP, optional(value(CODE_P2P))), + and(QUIC_V0_OR_V1, optional(value(CODE_P2P))), + and(_IP_OR_DOMAIN, optional(value(CODE_P2P))), _WebRTCDirect, _WebTransport, - peerId() + value(CODE_P2P) ) /** @@ -400,7 +401,7 @@ const _P2P = or( */ export const P2P = fmt(_P2P) -const _Circuit = and(_P2P, literal('p2p-circuit'), peerId()) +const _Circuit = and(_P2P, code(CODE_P2P_CIRCUIT), value(CODE_P2P)) /** * Matches circuit relay addresses @@ -417,9 +418,9 @@ const _Circuit = and(_P2P, literal('p2p-circuit'), peerId()) export const Circuit = fmt(_Circuit) const _WebRTC = or( - and(_P2P, literal('p2p-circuit'), literal('webrtc'), optional(peerId())), - and(_P2P, literal('webrtc'), optional(peerId())), - and(literal('webrtc'), optional(peerId())) + and(_P2P, code(CODE_P2P_CIRCUIT), code(CODE_WEBRTC), optional(value(CODE_P2P))), + and(_P2P, code(CODE_WEBRTC), optional(value(CODE_P2P))), + and(code(CODE_WEBRTC), optional(value(CODE_P2P))) ) /** @@ -437,8 +438,8 @@ const _WebRTC = or( export const WebRTC = fmt(_WebRTC) const _HTTP = or( - and(_IP_OR_DOMAIN, literal('tcp'), number(), literal('http'), optional(peerId())), - and(_IP_OR_DOMAIN, literal('http'), optional(peerId())) + and(_IP_OR_DOMAIN, value(CODE_TCP), code(CODE_HTTP), optional(value(CODE_P2P))), + and(_IP_OR_DOMAIN, code(CODE_HTTP), optional(value(CODE_P2P))) ) /** @@ -455,14 +456,15 @@ const _HTTP = or( */ export const HTTP = fmt(_HTTP) -const _HTTPS = or( - and(_IP_OR_DOMAIN, literal('tcp'), or( - and(literal('443'), literal('http')), - and(number(), literal('https')), - and(number(), literal('tls'), literal('http')) - ), optional(peerId())), - and(_IP_OR_DOMAIN, literal('tls'), literal('http'), optional(peerId())), - and(_IP_OR_DOMAIN, literal('https'), optional(peerId())) +const _HTTPS = and(_IP_OR_DOMAIN, or( + and(value(CODE_TCP, '443'), code(CODE_HTTP)), + and(value(CODE_TCP), code(CODE_HTTPS)), + and(value(CODE_TCP), code(CODE_TLS), code(CODE_HTTP)), + and(code(CODE_TLS), code(CODE_HTTP)), + code(CODE_TLS), + code(CODE_HTTPS) +), + optional(value(CODE_P2P)) ) /** @@ -480,7 +482,7 @@ const _HTTPS = or( export const HTTPS = fmt(_HTTPS) const _Memory = or( - and(literal('memory'), string(), optional(peerId())) + and(value(CODE_MEMORY), optional(value(CODE_P2P))) ) /** @@ -498,7 +500,7 @@ const _Memory = or( export const Memory = fmt(_Memory) const _Unix = or( - and(literal('unix'), string(), optional(peerId())) + and(value(CODE_UNIX), optional(value(CODE_P2P))) ) /** diff --git a/src/utils.ts b/src/utils.ts index e1f4ce4..230edfc 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,16 +1,7 @@ -import { base58btc } from 'multiformats/bases/base58' -import { base64url } from 'multiformats/bases/base64' import type { Matcher, MultiaddrMatcher } from './index.js' -import type { Multiaddr } from '@multiformats/multiaddr' +import { type Multiaddr, type Component, CODE_P2P, CODE_CERTHASH } from '@multiformats/multiaddr' -/** - * Split a multiaddr into path components - */ -const toParts = (ma: Multiaddr): string[] => { - return ma.toString().split('/').slice(1) -} - -export const func = (fn: (val: string) => boolean): Matcher => { +export const func = (fn: (val: Component) => boolean): Matcher => { return { match: (vals) => { if (vals.length < 1) { @@ -27,75 +18,41 @@ export const func = (fn: (val: string) => boolean): Matcher => { } } -export const literal = (str: string): Matcher => { - return { - match: (vals) => func((val) => val === str).match(vals), - pattern: str - } -} - -export const string = (): Matcher => { - return { - match: (vals) => func((val) => typeof val === 'string').match(vals), - pattern: '{string}' - } -} - -export const number = (): Matcher => { - return { - match: (vals) => func((val) => !isNaN(parseInt(val))).match(vals), - pattern: '{number}' - } -} - -export const peerId = (): Matcher => { +export const code = (code: number): Matcher => { return { match: (vals) => { - if (vals.length < 2) { - return false - } + const component = vals[0] - if (vals[0] !== 'p2p' && vals[0] !== 'ipfs') { + if (component?.code !== code) { return false } - // Q is RSA, 1 is Ed25519 or Secp256k1 - if (vals[1].startsWith('Q') || vals[1].startsWith('1')) { - try { - base58btc.decode(`z${vals[1]}`) - } catch (err) { - return false - } - } else { - return false - } - - return vals.slice(2) + return vals.slice(1) }, pattern: '/p2p/{peerid}' } } -export const certhash = (): Matcher => { +export const value = (code: number, value?: string): Matcher => { return { match: (vals) => { - if (vals.length < 2) { + const component = vals[0] + + if (component?.code !== code) { return false } - if (vals[0] !== 'certhash') { + if (component.value == null) { return false } - try { - base64url.decode(vals[1]) - } catch { + if (value != null && component.value !== value) { return false } - return vals.slice(2) + return vals.slice(1) }, - pattern: '/certhash/{certhash}' + pattern: '/p2p/{peerid}' } } @@ -117,7 +74,7 @@ export const optional = (matcher: Matcher): Matcher => { export const or = (...matchers: Matcher[]): Matcher => { return { match: (vals) => { - let matches: string[] | undefined + let matches: Component[] | undefined for (const matcher of matchers) { const result = matcher.match(vals) @@ -165,8 +122,8 @@ export const and = (...matchers: Matcher[]): Matcher => { } export function fmt (...matchers: Matcher[]): MultiaddrMatcher { - function match (ma: Multiaddr): string[] | false { - let parts = toParts(ma) + function match (ma: Multiaddr): Component[] | false { + let parts = ma.getComponents() for (const matcher of matchers) { const result = matcher.match(parts) From 577e6b3e138787e331d34e3e53d483bcbe16de10 Mon Sep 17 00:00:00 2001 From: achingbrain Date: Mon, 7 Jul 2025 11:52:00 +0200 Subject: [PATCH 2/4] chore: linting and deps --- package.json | 4 +--- src/index.ts | 13 ++++++------- src/utils.ts | 19 +------------------ test/index.spec.ts | 6 +++--- 4 files changed, 11 insertions(+), 31 deletions(-) diff --git a/package.json b/package.json index 1394b3e..82a5528 100644 --- a/package.json +++ b/package.json @@ -162,9 +162,7 @@ "docs": "aegir docs" }, "dependencies": { - "@chainsafe/is-ip": "^2.0.1", - "@multiformats/multiaddr": "^12.0.0", - "multiformats": "^13.0.0" + "@multiformats/multiaddr": "^12.0.0" }, "devDependencies": { "aegir": "^47.0.19" diff --git a/src/index.ts b/src/index.ts index f2ea6a5..82b554a 100644 --- a/src/index.ts +++ b/src/index.ts @@ -32,9 +32,9 @@ * ``` */ -import { isIPv4, isIPv6 } from '@chainsafe/is-ip' -import { and, or, optional, fmt, func, code, value } from './utils.js' -import { type Multiaddr, type Component, CODE_P2P, CODE_DNS4, CODE_DNS6, CODE_DNSADDR, CODE_DNS, CODE_IP4, CODE_IP6, CODE_TCP, CODE_UDP, CODE_QUIC, CODE_QUIC_V1, CODE_WS, CODE_WSS, CODE_TLS, CODE_SNI, CODE_WEBRTC_DIRECT, CODE_CERTHASH, CODE_WEBTRANSPORT, CODE_P2P_CIRCUIT, CODE_WEBRTC, CODE_HTTP, CODE_UNIX, CODE_HTTPS, CODE_MEMORY } from '@multiformats/multiaddr' +import { CODE_P2P, CODE_DNS4, CODE_DNS6, CODE_DNSADDR, CODE_DNS, CODE_IP4, CODE_IP6, CODE_TCP, CODE_UDP, CODE_QUIC, CODE_QUIC_V1, CODE_WS, CODE_WSS, CODE_TLS, CODE_SNI, CODE_WEBRTC_DIRECT, CODE_CERTHASH, CODE_WEBTRANSPORT, CODE_P2P_CIRCUIT, CODE_WEBRTC, CODE_HTTP, CODE_UNIX, CODE_HTTPS, CODE_MEMORY } from '@multiformats/multiaddr' +import { and, or, optional, fmt, code, value } from './utils.js' +import type { Multiaddr, Component } from '@multiformats/multiaddr' /** * A matcher accepts multiaddr components and either fails to match and returns @@ -291,13 +291,12 @@ export const QUIC = fmt(_QUIC) * * ```ts * import { multiaddr } from '@multiformats/multiaddr' - * import { QUICV1 } from '@multiformats/multiaddr-matcher' + * import { QUIC_V1 } from '@multiformats/multiaddr-matcher' * - * QUICV1.matches(multiaddr('/ip4/123.123.123.123/udp/1234/quic-v1')) // true + * QUIC_V1.matches(multiaddr('/ip4/123.123.123.123/udp/1234/quic-v1')) // true * ``` */ export const QUIC_V1 = fmt(_QUIC_V1) -export const QUICV1 = QUIC_V1 const _WEB = or( _IP_OR_DOMAIN, @@ -464,7 +463,7 @@ const _HTTPS = and(_IP_OR_DOMAIN, or( code(CODE_TLS), code(CODE_HTTPS) ), - optional(value(CODE_P2P)) +optional(value(CODE_P2P)) ) /** diff --git a/src/utils.ts b/src/utils.ts index 230edfc..5a3c3d5 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,22 +1,5 @@ import type { Matcher, MultiaddrMatcher } from './index.js' -import { type Multiaddr, type Component, CODE_P2P, CODE_CERTHASH } from '@multiformats/multiaddr' - -export const func = (fn: (val: Component) => boolean): Matcher => { - return { - match: (vals) => { - if (vals.length < 1) { - return false - } - - if (fn(vals[0])) { - return vals.slice(1) - } - - return false - }, - pattern: 'fn' - } -} +import type { Multiaddr, Component } from '@multiformats/multiaddr' export const code = (code: number): Matcher => { return { diff --git a/test/index.spec.ts b/test/index.spec.ts index 7f7f341..646b14e 100644 --- a/test/index.spec.ts +++ b/test/index.spec.ts @@ -440,9 +440,9 @@ describe('multiaddr matcher', () => { }) it('QUICv1 addresses', () => { - assertMatches(mafmt.QUICV1, goodQUICv1) - assertExactMatches(mafmt.QUICV1, exactQUICv1) - assertMismatches(mafmt.QUICV1, badQUICv1) + assertMatches(mafmt.QUIC_V1, goodQUICv1) + assertExactMatches(mafmt.QUIC_V1, exactQUICv1) + assertMismatches(mafmt.QUIC_V1, badQUICv1) }) it('WebSockets addresses', () => { From 27c27e4d2650a41d268d5ff5cf319b196424930f Mon Sep 17 00:00:00 2001 From: achingbrain Date: Mon, 7 Jul 2025 12:01:42 +0200 Subject: [PATCH 3/4] chore: add docs --- src/index.ts | 1 - src/utils.ts | 22 ++++++++++++---------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/src/index.ts b/src/index.ts index 82b554a..7d65160 100644 --- a/src/index.ts +++ b/src/index.ts @@ -42,7 +42,6 @@ import type { Multiaddr, Component } from '@multiformats/multiaddr' */ export interface Matcher { match(parts: Component[]): Component[] | false - pattern: string } /** diff --git a/src/utils.ts b/src/utils.ts index 5a3c3d5..1e4fc47 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,6 +1,9 @@ import type { Matcher, MultiaddrMatcher } from './index.js' import type { Multiaddr, Component } from '@multiformats/multiaddr' +/** + * Matches a multiaddr component with the specified code but no value + */ export const code = (code: number): Matcher => { return { match: (vals) => { @@ -11,11 +14,14 @@ export const code = (code: number): Matcher => { } return vals.slice(1) - }, - pattern: '/p2p/{peerid}' + } } } +/** + * Matches a multiaddr component with the specified code and value. If the value + * is omitted any non-undefined value is matched. + */ export const value = (code: number, value?: string): Matcher => { return { match: (vals) => { @@ -34,8 +40,7 @@ export const value = (code: number, value?: string): Matcher => { } return vals.slice(1) - }, - pattern: '/p2p/{peerid}' + } } } @@ -49,8 +54,7 @@ export const optional = (matcher: Matcher): Matcher => { } return result - }, - pattern: `optional(${matcher.pattern})` + } } } @@ -78,8 +82,7 @@ export const or = (...matchers: Matcher[]): Matcher => { } return matches - }, - pattern: `or(${matchers.map(m => m.pattern).join(', ')})` + } } } @@ -99,8 +102,7 @@ export const and = (...matchers: Matcher[]): Matcher => { } return vals - }, - pattern: `and(${matchers.map(m => m.pattern).join(', ')})` + } } } From 38774d34f8d14ec7a94bdd03e22b405a732e91b4 Mon Sep 17 00:00:00 2001 From: achingbrain Date: Mon, 7 Jul 2025 12:02:42 +0200 Subject: [PATCH 4/4] chore: add docs --- src/utils.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/utils.ts b/src/utils.ts index 1e4fc47..d0bd3e0 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -44,6 +44,9 @@ export const value = (code: number, value?: string): Matcher => { } } +/** + * An optional matcher + */ export const optional = (matcher: Matcher): Matcher => { return { match: (vals) => { @@ -58,6 +61,9 @@ export const optional = (matcher: Matcher): Matcher => { } } +/** + * Matches any one of the passed matches + */ export const or = (...matchers: Matcher[]): Matcher => { return { match: (vals) => { @@ -86,6 +92,9 @@ export const or = (...matchers: Matcher[]): Matcher => { } } +/** + * Matches all of the passed matchers + */ export const and = (...matchers: Matcher[]): Matcher => { return { match: (vals) => { @@ -106,6 +115,9 @@ export const and = (...matchers: Matcher[]): Matcher => { } } +/** + * Create a multiaddr matcher from the passed component matchers + */ export function fmt (...matchers: Matcher[]): MultiaddrMatcher { function match (ma: Multiaddr): Component[] | false { let parts = ma.getComponents()