@helia/verified-fetch 1.1.1 → 1.1.3

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.
@@ -2,7 +2,7 @@ import { peerIdFromString } from '@libp2p/peer-id'
2
2
  import { CID } from 'multiformats/cid'
3
3
  import { TLRU } from './tlru.js'
4
4
  import type { RequestFormatShorthand } from '../types.js'
5
- import type { IPNS, IPNSRoutingEvents, ResolveDnsLinkProgressEvents, ResolveProgressEvents, ResolveResult } from '@helia/ipns'
5
+ import type { IPNS, ResolveDNSLinkProgressEvents, ResolveResult } from '@helia/ipns'
6
6
  import type { ComponentLogger } from '@libp2p/interface'
7
7
  import type { ProgressOptions } from 'progress-events'
8
8
 
@@ -13,7 +13,7 @@ export interface ParseUrlStringInput {
13
13
  ipns: IPNS
14
14
  logger: ComponentLogger
15
15
  }
16
- export interface ParseUrlStringOptions extends ProgressOptions<ResolveProgressEvents | IPNSRoutingEvents | ResolveDnsLinkProgressEvents> {
16
+ export interface ParseUrlStringOptions extends ProgressOptions<ResolveDNSLinkProgressEvents> {
17
17
 
18
18
  }
19
19
 
@@ -33,7 +33,7 @@ export interface ParsedUrlStringResults {
33
33
  const URL_REGEX = /^(?<protocol>ip[fn]s):\/\/(?<cidOrPeerIdOrDnsLink>[^/?]+)\/?(?<path>[^?]*)\??(?<queryString>.*)$/
34
34
  const PATH_REGEX = /^\/(?<protocol>ip[fn]s)\/(?<cidOrPeerIdOrDnsLink>[^/?]+)\/?(?<path>[^?]*)\??(?<queryString>.*)$/
35
35
  const PATH_GATEWAY_REGEX = /^https?:\/\/(.*[^/])\/(?<protocol>ip[fn]s)\/(?<cidOrPeerIdOrDnsLink>[^/?]+)\/?(?<path>[^?]*)\??(?<queryString>.*)$/
36
- const SUBDOMAIN_GATEWAY_REGEX = /^https?:\/\/(?<cidOrPeerIdOrDnsLink>[^/.?]+)\.(?<protocol>ip[fn]s)\.([^/?]+)\/?(?<path>[^?]*)\??(?<queryString>.*)$/
36
+ const SUBDOMAIN_GATEWAY_REGEX = /^https?:\/\/(?<cidOrPeerIdOrDnsLink>[^/?]+)\.(?<protocol>ip[fn]s)\.([^/?]+)\/?(?<path>[^?]*)\??(?<queryString>.*)$/
37
37
 
38
38
  function matchURLString (urlString: string): Record<string, string> {
39
39
  for (const pattern of [URL_REGEX, PATH_REGEX, PATH_GATEWAY_REGEX, SUBDOMAIN_GATEWAY_REGEX]) {
@@ -47,6 +47,34 @@ function matchURLString (urlString: string): Record<string, string> {
47
47
  throw new TypeError(`Invalid URL: ${urlString}, please use ipfs://, ipns://, or gateway URLs only`)
48
48
  }
49
49
 
50
+ /**
51
+ * For dnslinks see https://specs.ipfs.tech/http-gateways/subdomain-gateway/#host-request-header
52
+ * DNSLink names include . which means they must be inlined into a single DNS label to provide unique origin and work with wildcard TLS certificates.
53
+ */
54
+
55
+ // DNS label can have up to 63 characters, consisting of alphanumeric
56
+ // characters or hyphens -, but it must not start or end with a hyphen.
57
+ const dnsLabelRegex = /^[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?$/
58
+
59
+ /**
60
+ * Checks if label looks like inlined DNSLink.
61
+ * (https://specs.ipfs.tech/http-gateways/subdomain-gateway/#host-request-header)
62
+ */
63
+ function isInlinedDnsLink (label: string): boolean {
64
+ return dnsLabelRegex.test(label) && label.includes('-') && !label.includes('.')
65
+ }
66
+
67
+ /**
68
+ * DNSLink label decoding
69
+ * * Every standalone - is replaced with .
70
+ * * Every remaining -- is replaced with -
71
+ *
72
+ * @example en-wikipedia--on--ipfs-org.ipns.example.net -> example.net/ipns/en.wikipedia-on-ipfs.org
73
+ */
74
+ function dnsLinkLabelDecoder (linkLabel: string): string {
75
+ return linkLabel.replace(/--/g, '%').replace(/-/g, '.').replace(/%/g, '-')
76
+ }
77
+
50
78
  /**
51
79
  * A function that parses ipfs:// and ipns:// URLs, returning an object with easily recognizable properties.
52
80
  *
@@ -80,7 +108,6 @@ export async function parseUrlString ({ urlString, ipns, logger }: ParseUrlStrin
80
108
  // protocol is ipns
81
109
  log.trace('Attempting to resolve PeerId for %s', cidOrPeerIdOrDnsLink)
82
110
  let peerId = null
83
-
84
111
  try {
85
112
  peerId = peerIdFromString(cidOrPeerIdOrDnsLink)
86
113
  resolveResult = await ipns.resolve(peerId, { onProgress: options?.onProgress })
@@ -99,13 +126,18 @@ export async function parseUrlString ({ urlString, ipns, logger }: ParseUrlStrin
99
126
  }
100
127
 
101
128
  if (cid == null) {
102
- log.trace('Attempting to resolve DNSLink for %s', cidOrPeerIdOrDnsLink)
129
+ let decodedDnsLinkLabel = cidOrPeerIdOrDnsLink
130
+ if (isInlinedDnsLink(cidOrPeerIdOrDnsLink)) {
131
+ decodedDnsLinkLabel = dnsLinkLabelDecoder(cidOrPeerIdOrDnsLink)
132
+ log.trace('decoded dnslink from "%s" to "%s"', cidOrPeerIdOrDnsLink, decodedDnsLinkLabel)
133
+ }
134
+ log.trace('Attempting to resolve DNSLink for %s', decodedDnsLinkLabel)
103
135
 
104
136
  try {
105
- resolveResult = await ipns.resolveDns(cidOrPeerIdOrDnsLink, { onProgress: options?.onProgress })
137
+ resolveResult = await ipns.resolveDNSLink(decodedDnsLinkLabel, { onProgress: options?.onProgress })
106
138
  cid = resolveResult?.cid
107
139
  resolvedPath = resolveResult?.path
108
- log.trace('resolved %s to %c', cidOrPeerIdOrDnsLink, cid)
140
+ log.trace('resolved %s to %c', decodedDnsLinkLabel, cid)
109
141
  ipnsCache.set(cidOrPeerIdOrDnsLink, resolveResult, 60 * 1000 * 2)
110
142
  } catch (err: any) {
111
143
  log.error('Could not resolve DnsLink for "%s"', cidOrPeerIdOrDnsLink, err)
@@ -1,6 +1,5 @@
1
1
  import { car } from '@helia/car'
2
- import { ipns as heliaIpns, type DNSResolver, type IPNS } from '@helia/ipns'
3
- import { dnsJsonOverHttps } from '@helia/ipns/dns-resolvers'
2
+ import { ipns as heliaIpns, type IPNS } from '@helia/ipns'
4
3
  import { unixfs as heliaUnixFs, type UnixFS as HeliaUnixFs, type UnixFSStats } from '@helia/unixfs'
5
4
  import * as ipldDagCbor from '@ipld/dag-cbor'
6
5
  import * as ipldDagJson from '@ipld/dag-json'
@@ -29,6 +28,7 @@ import type { CIDDetail, ContentTypeParser, Resource, VerifiedFetchInit as Verif
29
28
  import type { RequestFormatShorthand } from './types.js'
30
29
  import type { Helia } from '@helia/interface'
31
30
  import type { AbortOptions, Logger, PeerId } from '@libp2p/interface'
31
+ import type { DNSResolver } from '@multiformats/dns/resolvers'
32
32
  import type { UnixFSEntry } from 'ipfs-unixfs-exporter'
33
33
  import type { CID } from 'multiformats/cid'
34
34
 
@@ -126,12 +126,7 @@ export class VerifiedFetch {
126
126
  constructor ({ helia, ipns, unixfs }: VerifiedFetchComponents, init?: VerifiedFetchInit) {
127
127
  this.helia = helia
128
128
  this.log = helia.logger.forComponent('helia:verified-fetch')
129
- this.ipns = ipns ?? heliaIpns(helia, {
130
- resolvers: init?.dnsResolvers ?? [
131
- dnsJsonOverHttps('https://mozilla.cloudflare-dns.com/dns-query'),
132
- dnsJsonOverHttps('https://dns.google/resolve')
133
- ]
134
- })
129
+ this.ipns = ipns ?? heliaIpns(helia)
135
130
  this.unixfs = unixfs ?? heliaUnixFs(helia)
136
131
  this.contentTypeParser = init?.contentTypeParser
137
132
  this.log.trace('created VerifiedFetch instance')