Skip to content

Commit ca8d5eb

Browse files
feat!: support paths in @helia/ipns (#410)
Fixes #402 Adds support for publishing/resolving paths. Eg: - `/ipfs/QmFoo/deep/link.txt` - `/ipns/example.com/deep/link.html` BREAKING CHANGE: to support paths in `@helia/ipns`, the return type of `ipns.resolve` is now `{ path: string, cid: CID }` instead of just `CID` **Before** ```typescript const cidFromPeerId = await ipns.resolve(peerId) const cidFromDnsLink = await ipns.resolve(domainName) ``` **After** ```typescript const { cid, path } = await ipns.resolve(peerId) const { cid, path } = await ipns.resolve(domainName) ``` --------- Co-authored-by: achingbrain <alex@achingbrain.net>
1 parent 94b0cd1 commit ca8d5eb

8 files changed

Lines changed: 304 additions & 49 deletions

File tree

packages/interop/src/ipns-http.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ describe('@helia/ipns - http', () => {
6262

6363
const key = peerIdFromString(res.name)
6464

65-
const resolvedCid = await name.resolve(key)
65+
const { cid: resolvedCid } = await name.resolve(key)
6666
expect(resolvedCid.toString()).to.equal(cid.toString())
6767
})
6868
})

packages/interop/src/ipns-pubsub.spec.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import { createHeliaNode } from './fixtures/create-helia.js'
2020
import { createKuboNode } from './fixtures/create-kubo.js'
2121
import { keyTypes } from './fixtures/key-types.js'
2222
import { waitFor } from './fixtures/wait-for.js'
23-
import type { IPNS } from '@helia/ipns'
23+
import type { IPNS, ResolveResult } from '@helia/ipns'
2424
import type { Libp2p, PubSub } from '@libp2p/interface'
2525
import type { Keychain } from '@libp2p/keychain'
2626
import type { HeliaLibp2p } from 'helia'
@@ -161,12 +161,12 @@ keyTypes.filter(keyType => keyType !== 'RSA').forEach(keyType => {
161161
key: keyName
162162
})
163163

164-
let resolvedCid: CID | undefined
164+
let resolveResult: ResolveResult | undefined
165165

166166
// we should get an update eventually
167167
await waitFor(async () => {
168168
try {
169-
resolvedCid = await name.resolve(peerId)
169+
resolveResult = await name.resolve(peerId)
170170

171171
return true
172172
} catch {
@@ -177,11 +177,11 @@ keyTypes.filter(keyType => keyType !== 'RSA').forEach(keyType => {
177177
message: 'Helia could not resolve the IPNS record'
178178
})
179179

180-
if (resolvedCid == null) {
180+
if (resolveResult == null) {
181181
throw new Error('Failed to resolve CID')
182182
}
183183

184-
expect(resolvedCid.toString()).to.equal(cid.toString())
184+
expect(resolveResult.cid.toString()).to.equal(cid.toString())
185185
})
186186
})
187187
})

packages/interop/src/ipns.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ keyTypes.forEach(type => {
176176
key: keyName
177177
})
178178

179-
const resolvedCid = await name.resolve(key)
179+
const { cid: resolvedCid } = await name.resolve(key)
180180
expect(resolvedCid.toString()).to.equal(cid.toString())
181181
})
182182
})

packages/ipns/README.md

Lines changed: 83 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ IPNS operations using a Helia node
2121

2222
With IPNSRouting routers:
2323

24-
```typescript
24+
```TypeScript
2525
import { createHelia } from 'helia'
2626
import { ipns } from '@helia/ipns'
2727
import { unixfs } from '@helia/unixfs'
@@ -41,7 +41,78 @@ const cid = await fs.add(Uint8Array.from([0, 1, 2, 3, 4]))
4141
await name.publish(peerId, cid)
4242

4343
// resolve the name
44-
const cid = name.resolve(peerId)
44+
const result = name.resolve(peerId)
45+
46+
console.info(result.cid, result.path)
47+
```
48+
49+
## Example - Publishing a recursive record
50+
51+
A recursive record is a one that points to another record rather than to a
52+
value.
53+
54+
```TypeScript
55+
import { createHelia } from 'helia'
56+
import { ipns } from '@helia/ipns'
57+
import { unixfs } from '@helia/unixfs'
58+
59+
const helia = await createHelia()
60+
const name = ipns(helia)
61+
62+
// create a public key to publish as an IPNS name
63+
const keyInfo = await helia.libp2p.services.keychain.createKey('my-key')
64+
const peerId = await helia.libp2p.services.keychain.exportPeerId(keyInfo.name)
65+
66+
// store some data to publish
67+
const fs = unixfs(helia)
68+
const cid = await fs.add(Uint8Array.from([0, 1, 2, 3, 4]))
69+
70+
// publish the name
71+
await name.publish(peerId, cid)
72+
73+
// create another public key to re-publish the original record
74+
const recursiveKeyInfo = await helia.libp2p.services.keychain.createKey('my-recursive-key')
75+
const recursivePeerId = await helia.libp2p.services.keychain.exportPeerId(recursiveKeyInfo.name)
76+
77+
// publish the recursive name
78+
await name.publish(recursivePeerId, peerId)
79+
80+
// resolve the name recursively - it resolves until a CID is found
81+
const result = name.resolve(recursivePeerId)
82+
console.info(result.cid.toString() === cid.toString()) // true
83+
```
84+
85+
## Example - Publishing a record with a path
86+
87+
It is possible to publish CIDs with an associated path.
88+
89+
```TypeScript
90+
import { createHelia } from 'helia'
91+
import { ipns } from '@helia/ipns'
92+
import { unixfs } from '@helia/unixfs'
93+
94+
const helia = await createHelia()
95+
const name = ipns(helia)
96+
97+
// create a public key to publish as an IPNS name
98+
const keyInfo = await helia.libp2p.services.keychain.createKey('my-key')
99+
const peerId = await helia.libp2p.services.keychain.exportPeerId(keyInfo.name)
100+
101+
// store some data to publish
102+
const fs = unixfs(helia)
103+
const fileCid = await fs.add(Uint8Array.from([0, 1, 2, 3, 4]))
104+
105+
// store the file in a directory
106+
const dirCid = await fs.mkdir()
107+
const finalDirCid = await fs.cp(fileCid, dirCid, '/foo.txt')
108+
109+
// publish the name
110+
await name.publish(peerId, `/ipfs/${finalDirCid}/foo.txt)
111+
112+
// resolve the name
113+
const result = name.resolve(peerId)
114+
115+
console.info(result.cid, result.path) // QmFoo.. 'foo.txt'
45116
```
46117

47118
## Example - Using custom PubSub router
@@ -60,7 +131,7 @@ This router is only suitable for networks where IPNS updates are frequent
60131
and multiple peers are listening on the topic(s), otherwise update messages
61132
may fail to be published with "Insufficient peers" errors.
62133

63-
```typescript
134+
```TypeScript
64135
import { createHelia, libp2pDefaults } from 'helia'
65136
import { ipns } from '@helia/ipns'
66137
import { pubsub } from '@helia/ipns/routing'
@@ -91,14 +162,14 @@ const cid = await fs.add(Uint8Array.from([0, 1, 2, 3, 4]))
91162
await name.publish(peerId, cid)
92163
93164
// resolve the name
94-
const cid = name.resolve(peerId)
165+
const { cid, path } = name.resolve(peerId)
95166
```
96167

97168
## Example - Using custom DNS over HTTPS resolvers
98169

99170
With default DNSResolver resolvers:
100171

101-
```typescript
172+
```TypeScript
102173
import { createHelia } from 'helia'
103174
import { ipns } from '@helia/ipns'
104175
import { unixfs } from '@helia/unixfs'
@@ -111,14 +182,14 @@ const name = ipns(helia, {
111182
]
112183
})
113184
114-
const cid = name.resolveDns('some-domain-with-dnslink-entry.com')
185+
const { cid, path } = name.resolveDns('some-domain-with-dnslink-entry.com')
115186
```
116187

117188
## Example - Resolving a domain with a dnslink entry
118189

119190
Calling `resolveDns` with the `@helia/ipns` instance:
120191

121-
```typescript
192+
```TypeScript
122193
// resolve a CID from a TXT record in a DNS zone file, using the default
123194
// resolver for the current platform eg:
124195
// > dig _dnslink.ipfs.io TXT
@@ -128,7 +199,7 @@ Calling `resolveDns` with the `@helia/ipns` instance:
128199
// ;; ANSWER SECTION:
129200
// _dnslink.website.ipfs.io. 60 IN TXT "dnslink=/ipfs/QmWebsite"
130201
131-
const cid = name.resolveDns('ipfs.io')
202+
const { cid, path } = name.resolveDns('ipfs.io')
132203
133204
console.info(cid)
134205
// QmWebsite
@@ -142,11 +213,11 @@ response which can increase browser bundle sizes.
142213

143214
If this is a concern, use the DNS-JSON-Over-HTTPS resolver instead.
144215

145-
```typescript
216+
```TypeScript
146217
// use DNS-Over-HTTPS
147218
import { dnsOverHttps } from '@helia/ipns/dns-resolvers'
148219
149-
const cid = name.resolveDns('ipfs.io', {
220+
const { cid, path } = name.resolveDns('ipfs.io', {
150221
resolvers: [
151222
dnsOverHttps('https://mozilla.cloudflare-dns.com/dns-query')
152223
]
@@ -158,11 +229,11 @@ const cid = name.resolveDns('ipfs.io', {
158229
DNS-JSON-Over-HTTPS resolvers use the RFC 8427 `application/dns-json` and can
159230
result in a smaller browser bundle due to the response being plain JSON.
160231

161-
```typescript
232+
```TypeScript
162233
// use DNS-JSON-Over-HTTPS
163234
import { dnsJsonOverHttps } from '@helia/ipns/dns-resolvers'
164235
165-
const cid = name.resolveDns('ipfs.io', {
236+
const { cid, path } = name.resolveDns('ipfs.io', {
166237
resolvers: [
167238
dnsJsonOverHttps('https://mozilla.cloudflare-dns.com/dns-query')
168239
]

0 commit comments

Comments
 (0)