import { isIPv4, isIPv6 } from '@chainsafe/is-ip';
import { CID } from 'multiformats';
import { base64url } from 'multiformats/bases/base64';
import { CODE_CERTHASH, CODE_DCCP, CODE_DNS, CODE_DNS4, CODE_DNS6, CODE_DNSADDR, CODE_GARLIC32, CODE_GARLIC64, CODE_HTTP, CODE_HTTP_PATH, CODE_HTTPS, CODE_IP4, CODE_IP6, CODE_IP6ZONE, CODE_IPCIDR, CODE_MEMORY, CODE_NOISE, CODE_ONION, CODE_ONION3, CODE_P2P, CODE_P2P_CIRCUIT, CODE_P2P_STARDUST, CODE_P2P_WEBRTC_DIRECT, CODE_P2P_WEBRTC_STAR, CODE_P2P_WEBSOCKET_STAR, CODE_QUIC, CODE_QUIC_V1, CODE_SCTP, CODE_SNI, CODE_TCP, CODE_TLS, CODE_UDP, CODE_UDT, CODE_UNIX, CODE_UTP, CODE_WEBRTC, CODE_WEBRTC_DIRECT, CODE_WEBTRANSPORT, CODE_WS, CODE_WSS } from "./constants.js";
import { UnknownProtocolError, ValidationError } from "./errors.js";
import { bytes2mb, bytes2onion, bytes2port, bytesToString, ip4ToBytes, ip4ToString, ip6StringToValue, ip6ToBytes, ip6ToString, mb2bytes, onion2bytes, onion32bytes, port2bytes, stringToBytes } from "./utils.js";
import { validatePort } from "./validation.js";
export const V = -1;
class Registry {
    protocolsByCode = new Map();
    protocolsByName = new Map();
    getProtocol(key) {
        let codec;
        if (typeof key === 'string') {
            codec = this.protocolsByName.get(key);
        }
        else {
            codec = this.protocolsByCode.get(key);
        }
        if (codec == null) {
            throw new UnknownProtocolError(`Protocol ${key} was unknown`);
        }
        return codec;
    }
    addProtocol(codec) {
        this.protocolsByCode.set(codec.code, codec);
        this.protocolsByName.set(codec.name, codec);
        codec.aliases?.forEach(alias => {
            this.protocolsByName.set(alias, codec);
        });
    }
    removeProtocol(code) {
        const codec = this.protocolsByCode.get(code);
        if (codec == null) {
            return;
        }
        this.protocolsByCode.delete(codec.code);
        this.protocolsByName.delete(codec.name);
        codec.aliases?.forEach(alias => {
            this.protocolsByName.delete(alias);
        });
    }
}
export const registry = new Registry();
const codecs = [{
        code: CODE_IP4,
        name: 'ip4',
        size: 32,
        valueToBytes: ip4ToBytes,
        bytesToValue: ip4ToString,
        validate: (value) => {
            if (!isIPv4(value)) {
                throw new ValidationError(`Invalid IPv4 address "${value}"`);
            }
        }
    }, {
        code: CODE_TCP,
        name: 'tcp',
        size: 16,
        valueToBytes: port2bytes,
        bytesToValue: bytes2port,
        validate: validatePort
    }, {
        code: CODE_UDP,
        name: 'udp',
        size: 16,
        valueToBytes: port2bytes,
        bytesToValue: bytes2port,
        validate: validatePort
    }, {
        code: CODE_DCCP,
        name: 'dccp',
        size: 16,
        valueToBytes: port2bytes,
        bytesToValue: bytes2port,
        validate: validatePort
    }, {
        code: CODE_IP6,
        name: 'ip6',
        size: 128,
        valueToBytes: ip6ToBytes,
        bytesToValue: ip6ToString,
        stringToValue: ip6StringToValue,
        validate: (value) => {
            if (!isIPv6(value)) {
                throw new ValidationError(`Invalid IPv6 address "${value}"`);
            }
        }
    }, {
        code: CODE_IP6ZONE,
        name: 'ip6zone',
        size: V
    }, {
        code: CODE_IPCIDR,
        name: 'ipcidr',
        size: 8,
        bytesToValue: bytesToString('base10'),
        valueToBytes: stringToBytes('base10')
    }, {
        code: CODE_DNS,
        name: 'dns',
        size: V
    }, {
        code: CODE_DNS4,
        name: 'dns4',
        size: V
    }, {
        code: CODE_DNS6,
        name: 'dns6',
        size: V
    }, {
        code: CODE_DNSADDR,
        name: 'dnsaddr',
        size: V
    }, {
        code: CODE_SCTP,
        name: 'sctp',
        size: 16,
        valueToBytes: port2bytes,
        bytesToValue: bytes2port,
        validate: validatePort
    }, {
        code: CODE_UDT,
        name: 'udt'
    }, {
        code: CODE_UTP,
        name: 'utp'
    }, {
        code: CODE_UNIX,
        name: 'unix',
        size: V,
        stringToValue: (str) => decodeURIComponent(str),
        valueToString: (val) => encodeURIComponent(val)
    }, {
        code: CODE_P2P,
        name: 'p2p',
        aliases: ['ipfs'],
        size: V,
        bytesToValue: bytesToString('base58btc'),
        valueToBytes: (val) => {
            if (val.startsWith('Q') || val.startsWith('1')) {
                return stringToBytes('base58btc')(val);
            }
            return CID.parse(val).multihash.bytes;
        }
    }, {
        code: CODE_ONION,
        name: 'onion',
        size: 96,
        bytesToValue: bytes2onion,
        valueToBytes: onion2bytes
    }, {
        code: CODE_ONION3,
        name: 'onion3',
        size: 296,
        bytesToValue: bytes2onion,
        valueToBytes: onion32bytes
    }, {
        code: CODE_GARLIC64,
        name: 'garlic64',
        size: V
    }, {
        code: CODE_GARLIC32,
        name: 'garlic32',
        size: V
    }, {
        code: CODE_TLS,
        name: 'tls'
    }, {
        code: CODE_SNI,
        name: 'sni',
        size: V
    }, {
        code: CODE_NOISE,
        name: 'noise'
    }, {
        code: CODE_QUIC,
        name: 'quic'
    }, {
        code: CODE_QUIC_V1,
        name: 'quic-v1'
    }, {
        code: CODE_WEBTRANSPORT,
        name: 'webtransport'
    }, {
        code: CODE_CERTHASH,
        name: 'certhash',
        size: V,
        bytesToValue: bytes2mb(base64url),
        valueToBytes: mb2bytes
    }, {
        code: CODE_HTTP,
        name: 'http'
    }, {
        code: CODE_HTTP_PATH,
        name: 'http-path',
        size: V,
        stringToValue: (str) => `/${decodeURIComponent(str)}`,
        valueToString: (val) => encodeURIComponent(val.substring(1))
    }, {
        code: CODE_HTTPS,
        name: 'https'
    }, {
        code: CODE_WS,
        name: 'ws'
    }, {
        code: CODE_WSS,
        name: 'wss'
    }, {
        code: CODE_P2P_WEBSOCKET_STAR,
        name: 'p2p-websocket-star'
    }, {
        code: CODE_P2P_STARDUST,
        name: 'p2p-stardust'
    }, {
        code: CODE_P2P_WEBRTC_STAR,
        name: 'p2p-webrtc-star'
    }, {
        code: CODE_P2P_WEBRTC_DIRECT,
        name: 'p2p-webrtc-direct'
    }, {
        code: CODE_WEBRTC_DIRECT,
        name: 'webrtc-direct'
    }, {
        code: CODE_WEBRTC,
        name: 'webrtc'
    }, {
        code: CODE_P2P_CIRCUIT,
        name: 'p2p-circuit'
    }, {
        code: CODE_MEMORY,
        name: 'memory',
        size: V
    }];
codecs.forEach(codec => {
    registry.addProtocol(codec);
});
//# sourceMappingURL=registry.js.map