@helia/interop 3.0.0 → 3.0.1-031519c

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.
Files changed (133) hide show
  1. package/.aegir.js +46 -0
  2. package/README.md +11 -0
  3. package/dist/src/bin.d.ts +3 -0
  4. package/dist/src/bin.d.ts.map +1 -0
  5. package/dist/src/bin.js +20 -0
  6. package/dist/src/bin.js.map +1 -0
  7. package/dist/src/car.spec.d.ts +2 -0
  8. package/dist/src/car.spec.d.ts.map +1 -0
  9. package/dist/src/car.spec.js +77 -0
  10. package/dist/src/car.spec.js.map +1 -0
  11. package/dist/src/dag-cbor.spec.d.ts +2 -0
  12. package/dist/src/dag-cbor.spec.d.ts.map +1 -0
  13. package/dist/src/dag-cbor.spec.js +49 -0
  14. package/dist/src/dag-cbor.spec.js.map +1 -0
  15. package/dist/src/dag-json.spec.d.ts +2 -0
  16. package/dist/src/dag-json.spec.d.ts.map +1 -0
  17. package/dist/src/dag-json.spec.js +49 -0
  18. package/dist/src/dag-json.spec.js.map +1 -0
  19. package/dist/src/fixtures/connect.d.ts +7 -0
  20. package/dist/src/fixtures/connect.d.ts.map +1 -0
  21. package/dist/src/fixtures/connect.js +17 -0
  22. package/dist/src/fixtures/connect.js.map +1 -0
  23. package/dist/src/fixtures/create-helia-http.d.ts +4 -0
  24. package/dist/src/fixtures/create-helia-http.d.ts.map +1 -0
  25. package/dist/src/fixtures/create-helia-http.js +7 -0
  26. package/dist/src/fixtures/create-helia-http.js.map +1 -0
  27. package/dist/src/fixtures/create-helia.browser.d.ts +4 -0
  28. package/dist/src/fixtures/create-helia.browser.d.ts.map +1 -0
  29. package/dist/src/fixtures/create-helia.browser.js +55 -0
  30. package/dist/src/fixtures/create-helia.browser.js.map +1 -0
  31. package/dist/src/fixtures/create-helia.d.ts +4 -0
  32. package/dist/src/fixtures/create-helia.d.ts.map +1 -0
  33. package/dist/src/fixtures/create-helia.js +45 -0
  34. package/dist/src/fixtures/create-helia.js.map +1 -0
  35. package/dist/src/fixtures/create-kubo.browser.d.ts +3 -0
  36. package/dist/src/fixtures/create-kubo.browser.d.ts.map +1 -0
  37. package/dist/src/fixtures/create-kubo.browser.js +30 -0
  38. package/dist/src/fixtures/create-kubo.browser.js.map +1 -0
  39. package/dist/src/fixtures/create-kubo.d.ts +3 -0
  40. package/dist/src/fixtures/create-kubo.d.ts.map +1 -0
  41. package/dist/src/fixtures/create-kubo.js +32 -0
  42. package/dist/src/fixtures/create-kubo.js.map +1 -0
  43. package/dist/src/fixtures/create-peer-ids.d.ts +14 -0
  44. package/dist/src/fixtures/create-peer-ids.d.ts.map +1 -0
  45. package/dist/src/fixtures/create-peer-ids.js +37 -0
  46. package/dist/src/fixtures/create-peer-ids.js.map +1 -0
  47. package/dist/src/fixtures/key-types.d.ts +3 -0
  48. package/dist/src/fixtures/key-types.d.ts.map +1 -0
  49. package/dist/src/fixtures/key-types.js +6 -0
  50. package/dist/src/fixtures/key-types.js.map +1 -0
  51. package/dist/src/fixtures/memory-car.d.ts +7 -0
  52. package/dist/src/fixtures/memory-car.d.ts.map +1 -0
  53. package/dist/src/fixtures/memory-car.js +26 -0
  54. package/dist/src/fixtures/memory-car.js.map +1 -0
  55. package/dist/src/fixtures/wait-for.d.ts +7 -0
  56. package/dist/src/fixtures/wait-for.d.ts.map +1 -0
  57. package/dist/src/fixtures/wait-for.js +17 -0
  58. package/dist/src/fixtures/wait-for.js.map +1 -0
  59. package/dist/src/helia-blockstore.spec.d.ts +2 -0
  60. package/dist/src/helia-blockstore.spec.d.ts.map +1 -0
  61. package/dist/src/helia-blockstore.spec.js +48 -0
  62. package/dist/src/helia-blockstore.spec.js.map +1 -0
  63. package/dist/src/helia-hashes.spec.d.ts +2 -0
  64. package/dist/src/helia-hashes.spec.d.ts.map +1 -0
  65. package/dist/src/helia-hashes.spec.js +50 -0
  66. package/dist/src/helia-hashes.spec.js.map +1 -0
  67. package/dist/src/helia-pins.spec.d.ts +2 -0
  68. package/dist/src/helia-pins.spec.d.ts.map +1 -0
  69. package/dist/src/helia-pins.spec.js +48 -0
  70. package/dist/src/helia-pins.spec.js.map +1 -0
  71. package/dist/src/index.d.ts +12 -0
  72. package/dist/src/index.d.ts.map +1 -1
  73. package/dist/src/index.js +12 -0
  74. package/dist/src/index.js.map +1 -1
  75. package/dist/src/ipns-http.spec.d.ts +2 -0
  76. package/dist/src/ipns-http.spec.d.ts.map +1 -0
  77. package/dist/src/ipns-http.spec.js +55 -0
  78. package/dist/src/ipns-http.spec.js.map +1 -0
  79. package/dist/src/ipns-pubsub.spec.d.ts +2 -0
  80. package/dist/src/ipns-pubsub.spec.d.ts.map +1 -0
  81. package/dist/src/ipns-pubsub.spec.js +146 -0
  82. package/dist/src/ipns-pubsub.spec.js.map +1 -0
  83. package/dist/src/ipns.spec.d.ts +2 -0
  84. package/dist/src/ipns.spec.d.ts.map +1 -0
  85. package/dist/src/ipns.spec.js +146 -0
  86. package/dist/src/ipns.spec.js.map +1 -0
  87. package/dist/src/json.spec.d.ts +2 -0
  88. package/dist/src/json.spec.d.ts.map +1 -0
  89. package/dist/src/json.spec.js +49 -0
  90. package/dist/src/json.spec.js.map +1 -0
  91. package/dist/src/mfs.spec.d.ts +2 -0
  92. package/dist/src/mfs.spec.d.ts.map +1 -0
  93. package/dist/src/mfs.spec.js +85 -0
  94. package/dist/src/mfs.spec.js.map +1 -0
  95. package/dist/src/strings.spec.d.ts +2 -0
  96. package/dist/src/strings.spec.d.ts.map +1 -0
  97. package/dist/src/strings.spec.js +51 -0
  98. package/dist/src/strings.spec.js.map +1 -0
  99. package/dist/src/unixfs-bitswap.spec.d.ts +2 -0
  100. package/dist/src/unixfs-bitswap.spec.d.ts.map +1 -0
  101. package/dist/src/unixfs-bitswap.spec.js +65 -0
  102. package/dist/src/unixfs-bitswap.spec.js.map +1 -0
  103. package/dist/src/unixfs-files.spec.d.ts +2 -0
  104. package/dist/src/unixfs-files.spec.d.ts.map +1 -0
  105. package/dist/src/unixfs-files.spec.js +69 -0
  106. package/dist/src/unixfs-files.spec.js.map +1 -0
  107. package/package.json +25 -19
  108. package/src/bin.ts +25 -0
  109. package/src/car.spec.ts +102 -0
  110. package/src/dag-cbor.spec.ts +65 -0
  111. package/src/dag-json.spec.ts +65 -0
  112. package/src/fixtures/connect.ts +19 -0
  113. package/src/fixtures/create-helia-http.ts +8 -0
  114. package/src/fixtures/create-helia.browser.ts +64 -0
  115. package/src/fixtures/create-helia.ts +51 -0
  116. package/src/fixtures/create-kubo.browser.ts +30 -0
  117. package/src/fixtures/create-kubo.ts +32 -0
  118. package/src/fixtures/create-peer-ids.ts +46 -0
  119. package/src/fixtures/key-types.ts +7 -0
  120. package/src/fixtures/memory-car.ts +33 -0
  121. package/src/fixtures/wait-for.ts +26 -0
  122. package/src/helia-blockstore.spec.ts +59 -0
  123. package/src/helia-hashes.spec.ts +61 -0
  124. package/src/helia-pins.spec.ts +64 -0
  125. package/src/index.ts +13 -0
  126. package/src/ipns-http.spec.ts +68 -0
  127. package/src/ipns-pubsub.spec.ts +180 -0
  128. package/src/ipns.spec.ts +183 -0
  129. package/src/json.spec.ts +65 -0
  130. package/src/mfs.spec.ts +105 -0
  131. package/src/strings.spec.ts +67 -0
  132. package/src/unixfs-bitswap.spec.ts +86 -0
  133. package/src/unixfs-files.spec.ts +89 -0
package/src/bin.ts ADDED
@@ -0,0 +1,25 @@
1
+ #! /usr/bin/env node
2
+ /* eslint-disable no-console */
3
+
4
+ import { spawn } from 'node:child_process'
5
+ import { dirname, resolve } from 'path'
6
+ import { fileURLToPath } from 'url'
7
+
8
+ // aegir should be run from `node_modules/@helia/interop`
9
+ const cwd = resolve(dirname(fileURLToPath(import.meta.url)), '../../')
10
+
11
+ const test = spawn('npx', ['aegir', 'test'], {
12
+ cwd
13
+ })
14
+
15
+ test.stdout.on('data', (data) => {
16
+ process.stdout.write(data)
17
+ })
18
+
19
+ test.stderr.on('data', (data) => {
20
+ process.stderr.write(data)
21
+ })
22
+
23
+ test.on('close', (code) => {
24
+ process.exit(code ?? 0)
25
+ })
@@ -0,0 +1,102 @@
1
+ /* eslint-env mocha */
2
+
3
+ import { car } from '@helia/car'
4
+ import { type UnixFS, unixfs } from '@helia/unixfs'
5
+ import { CarReader } from '@ipld/car'
6
+ import { expect } from 'aegir/chai'
7
+ import drain from 'it-drain'
8
+ import toBuffer from 'it-to-buffer'
9
+ import { CID } from 'multiformats/cid'
10
+ import { createHeliaNode } from './fixtures/create-helia.js'
11
+ import { createKuboNode } from './fixtures/create-kubo.js'
12
+ import { memoryCarWriter } from './fixtures/memory-car.js'
13
+ import type { Car } from '@helia/car'
14
+ import type { HeliaLibp2p } from 'helia'
15
+ import type { FileCandidate } from 'ipfs-unixfs-importer'
16
+ import type { Controller } from 'ipfsd-ctl'
17
+
18
+ describe('@helia/car', () => {
19
+ let helia: HeliaLibp2p
20
+ let c: Car
21
+ let u: UnixFS
22
+ let kubo: Controller
23
+
24
+ beforeEach(async () => {
25
+ helia = await createHeliaNode()
26
+ c = car(helia)
27
+ u = unixfs(helia)
28
+ kubo = await createKuboNode()
29
+
30
+ // connect helia to kubo
31
+ await helia.libp2p.dial(kubo.peer.addresses)
32
+ })
33
+
34
+ afterEach(async () => {
35
+ if (helia != null) {
36
+ await helia.stop()
37
+ }
38
+
39
+ if (kubo != null) {
40
+ await kubo.stop()
41
+ }
42
+ })
43
+
44
+ it('should export a car from Helia, import and read the contents from kubo', async () => {
45
+ const chunkSize = 1024 * 1024
46
+ const size = chunkSize * 10
47
+ const input: Uint8Array[] = []
48
+
49
+ const candidate: FileCandidate = {
50
+ content: (async function * () {
51
+ for (let i = 0; i < size; i += chunkSize) {
52
+ const buf = new Uint8Array(chunkSize)
53
+ input.push(buf)
54
+
55
+ yield buf
56
+ }
57
+ }())
58
+ }
59
+
60
+ const cid = await u.addFile(candidate)
61
+ const writer = memoryCarWriter(cid)
62
+ await c.export(cid, writer)
63
+
64
+ const buf = await writer.bytes()
65
+
66
+ await drain(kubo.api.dag.import([buf]))
67
+
68
+ const output: Uint8Array[] = []
69
+
70
+ for await (const b of kubo.api.cat(cid)) {
71
+ output.push(b)
72
+ }
73
+
74
+ expect(toBuffer(output)).to.equalBytes(toBuffer(input))
75
+ })
76
+
77
+ it('should export a car from kubo, import and read the contents from Helia', async () => {
78
+ const chunkSize = 1024 * 1024
79
+ const size = chunkSize * 10
80
+ const input: Uint8Array[] = []
81
+
82
+ const candidate: FileCandidate = {
83
+ content: (async function * () {
84
+ for (let i = 0; i < size; i += chunkSize) {
85
+ const buf = new Uint8Array(chunkSize)
86
+ input.push(buf)
87
+
88
+ yield buf
89
+ }
90
+ }())
91
+ }
92
+
93
+ const { cid } = await kubo.api.add(candidate.content)
94
+ const bytes = await toBuffer(kubo.api.dag.export(cid))
95
+
96
+ const reader = await CarReader.fromBytes(bytes)
97
+
98
+ await c.import(reader)
99
+
100
+ expect(await toBuffer(u.cat(CID.parse(cid.toString())))).to.equalBytes(toBuffer(input))
101
+ })
102
+ })
@@ -0,0 +1,65 @@
1
+ /* eslint-env mocha */
2
+
3
+ import { dagCbor, type DAGCBOR, type AddOptions } from '@helia/dag-cbor'
4
+ import * as codec from '@ipld/dag-cbor'
5
+ import { expect } from 'aegir/chai'
6
+ import { CID } from 'multiformats/cid'
7
+ import { createHeliaNode } from './fixtures/create-helia.js'
8
+ import { createKuboNode } from './fixtures/create-kubo.js'
9
+ import type { HeliaLibp2p } from 'helia'
10
+ import type { PutOptions as KuboAddOptions } from 'ipfs-core-types/src/block/index.js'
11
+ import type { Controller } from 'ipfsd-ctl'
12
+
13
+ describe('@helia/dag-cbor', () => {
14
+ let helia: HeliaLibp2p
15
+ let d: DAGCBOR
16
+ let kubo: Controller
17
+
18
+ async function expectSameCid (data: () => any, heliaOpts: Partial<AddOptions> = {}, kuboOpts: KuboAddOptions = {}): Promise<void> {
19
+ const heliaCid = await d.add(data(), heliaOpts)
20
+ const kuboCid = await kubo.api.dag.put(data(), kuboOpts)
21
+
22
+ expect(heliaCid.toString()).to.equal(kuboCid.toString())
23
+ }
24
+
25
+ beforeEach(async () => {
26
+ helia = await createHeliaNode()
27
+ d = dagCbor(helia)
28
+ kubo = await createKuboNode()
29
+
30
+ await helia.libp2p.dial((await (kubo.api.id())).addresses)
31
+ })
32
+
33
+ afterEach(async () => {
34
+ if (helia != null) {
35
+ await helia.stop()
36
+ }
37
+
38
+ if (kubo != null) {
39
+ await kubo.stop()
40
+ }
41
+ })
42
+
43
+ it('should create the same CID for a string', async () => {
44
+ const candidate = (): any => ({ hello: 'world' })
45
+
46
+ await expectSameCid(candidate)
47
+ })
48
+
49
+ it('should add to helia and fetch from kubo', async () => {
50
+ const input = { hello: 'world' }
51
+ const cid = await d.add(input)
52
+ const block = await kubo.api.block.get(cid)
53
+ const output = codec.decode(block)
54
+
55
+ expect(output).to.deep.equal(input)
56
+ })
57
+
58
+ it('should add to kubo and fetch from helia', async () => {
59
+ const input = { hello: 'world' }
60
+ const cid = await kubo.api.block.put(codec.encode(input))
61
+ const output = await d.get(CID.parse(cid.toString()))
62
+
63
+ expect(output).to.deep.equal(input)
64
+ })
65
+ })
@@ -0,0 +1,65 @@
1
+ /* eslint-env mocha */
2
+
3
+ import { dagJson, type DAGJSON, type AddOptions } from '@helia/dag-json'
4
+ import { expect } from 'aegir/chai'
5
+ import { CID } from 'multiformats/cid'
6
+ import * as codec from 'multiformats/codecs/json'
7
+ import { createHeliaNode } from './fixtures/create-helia.js'
8
+ import { createKuboNode } from './fixtures/create-kubo.js'
9
+ import type { HeliaLibp2p } from 'helia'
10
+ import type { PutOptions as KuboAddOptions } from 'ipfs-core-types/src/block/index.js'
11
+ import type { Controller } from 'ipfsd-ctl'
12
+
13
+ describe('@helia/dag-json', () => {
14
+ let helia: HeliaLibp2p
15
+ let d: DAGJSON
16
+ let kubo: Controller
17
+
18
+ async function expectSameCid (data: () => any, heliaOpts: Partial<AddOptions> = {}, kuboOpts: KuboAddOptions = { format: 'dag-json' }): Promise<void> {
19
+ const heliaCid = await d.add(data(), heliaOpts)
20
+ const kuboCid = await kubo.api.block.put(codec.encode(data()), kuboOpts)
21
+
22
+ expect(heliaCid.toString()).to.equal(kuboCid.toString())
23
+ }
24
+
25
+ beforeEach(async () => {
26
+ helia = await createHeliaNode()
27
+ d = dagJson(helia)
28
+ kubo = await createKuboNode()
29
+
30
+ await helia.libp2p.dial((await (kubo.api.id())).addresses)
31
+ })
32
+
33
+ afterEach(async () => {
34
+ if (helia != null) {
35
+ await helia.stop()
36
+ }
37
+
38
+ if (kubo != null) {
39
+ await kubo.stop()
40
+ }
41
+ })
42
+
43
+ it('should create the same CID for a string', async () => {
44
+ const candidate = (): any => ({ hello: 'world' })
45
+
46
+ await expectSameCid(candidate)
47
+ })
48
+
49
+ it('should add to helia and fetch from kubo', async () => {
50
+ const input = { hello: 'world' }
51
+ const cid = await d.add(input)
52
+ const block = await kubo.api.block.get(cid)
53
+ const output = codec.decode(block)
54
+
55
+ expect(output).to.deep.equal(input)
56
+ })
57
+
58
+ it('should add to kubo and fetch from helia', async () => {
59
+ const input = { hello: 'world' }
60
+ const cid = await kubo.api.block.put(codec.encode(input))
61
+ const output = await d.get(CID.parse(cid.toString()))
62
+
63
+ expect(output).to.deep.equal(input)
64
+ })
65
+ })
@@ -0,0 +1,19 @@
1
+ import { expect } from 'aegir/chai'
2
+ import type { HeliaLibp2p } from 'helia'
3
+ import type { Controller } from 'ipfsd-ctl'
4
+
5
+ /**
6
+ * Connect the two nodes by dialing a protocol stream
7
+ */
8
+ export async function connect (helia: HeliaLibp2p, kubo: Controller, protocol: string): Promise<void> {
9
+ let connected = false
10
+ for (const addr of kubo.peer.addresses) {
11
+ try {
12
+ await helia.libp2p.dialProtocol(addr, protocol)
13
+ connected = true
14
+ break
15
+ } catch { }
16
+ }
17
+
18
+ expect(connected).to.be.true('could not connect Helia to Kubo')
19
+ }
@@ -0,0 +1,8 @@
1
+ import { createHeliaHTTP as createHelia, type HeliaHTTPInit } from '@helia/http'
2
+ import type { Helia } from '@helia/interface'
3
+
4
+ export async function createHeliaHTTP (init: Partial<HeliaHTTPInit> = {}): Promise<Helia> {
5
+ return createHelia({
6
+ ...init
7
+ })
8
+ }
@@ -0,0 +1,64 @@
1
+ import { bitswap } from '@helia/block-brokers'
2
+ import { ipnsValidator, ipnsSelector } from '@helia/ipns'
3
+ import { kadDHT, removePublicAddressesMapper } from '@libp2p/kad-dht'
4
+ import { webSockets } from '@libp2p/websockets'
5
+ import { all } from '@libp2p/websockets/filters'
6
+ import { sha3512 } from '@multiformats/sha3'
7
+ import { createHelia, libp2pDefaults } from 'helia'
8
+ import type { Libp2p } from '@libp2p/interface'
9
+ import type { DefaultLibp2pServices, HeliaLibp2p } from 'helia'
10
+
11
+ export async function createHeliaNode (): Promise<HeliaLibp2p<Libp2p<DefaultLibp2pServices>>> {
12
+ const defaults = libp2pDefaults()
13
+
14
+ // allow dialing insecure WebSockets
15
+ defaults.transports?.pop()
16
+ defaults.transports = [
17
+ ...(defaults.transports ?? []),
18
+ webSockets({
19
+ filter: all
20
+ })
21
+ ]
22
+
23
+ // allow dialing loopback
24
+ defaults.connectionGater = {
25
+ denyDialMultiaddr: () => false
26
+ }
27
+
28
+ // use LAN DHT
29
+ defaults.services = {
30
+ ...(defaults.services ?? {}),
31
+ dht: kadDHT({
32
+ validators: {
33
+ ipns: ipnsValidator
34
+ },
35
+ selectors: {
36
+ ipns: ipnsSelector
37
+ },
38
+ // skips waiting for the initial self-query to find peers
39
+ allowQueryWithZeroPeers: true,
40
+
41
+ protocol: '/ipfs/lan/kad/1.0.0',
42
+ peerInfoMapper: removePublicAddressesMapper,
43
+ clientMode: false
44
+ })
45
+ }
46
+
47
+ // remove bootstrappers
48
+ defaults.peerDiscovery = []
49
+
50
+ // remove services that are not used in tests
51
+ delete defaults.services.autoNAT
52
+ delete defaults.services.dcutr
53
+ delete defaults.services.delegatedRouting
54
+
55
+ return createHelia<Libp2p<DefaultLibp2pServices>>({
56
+ blockBrokers: [
57
+ bitswap()
58
+ ],
59
+ libp2p: defaults,
60
+ hashers: [
61
+ sha3512
62
+ ]
63
+ })
64
+ }
@@ -0,0 +1,51 @@
1
+ import { bitswap } from '@helia/block-brokers'
2
+ import { ipnsValidator, ipnsSelector } from '@helia/ipns'
3
+ import { kadDHT, removePublicAddressesMapper } from '@libp2p/kad-dht'
4
+ import { sha3512 } from '@multiformats/sha3'
5
+ import { createHelia, libp2pDefaults } from 'helia'
6
+ import type { Libp2p } from '@libp2p/interface'
7
+ import type { DefaultLibp2pServices, HeliaLibp2p } from 'helia'
8
+
9
+ export async function createHeliaNode (): Promise<HeliaLibp2p<Libp2p<DefaultLibp2pServices>>> {
10
+ const defaults = libp2pDefaults()
11
+ defaults.addresses = {
12
+ listen: [
13
+ '/ip4/0.0.0.0/tcp/0'
14
+ ]
15
+ }
16
+ defaults.services = {
17
+ ...(defaults.services ?? {}),
18
+ dht: kadDHT({
19
+ validators: {
20
+ ipns: ipnsValidator
21
+ },
22
+ selectors: {
23
+ ipns: ipnsSelector
24
+ },
25
+ // skips waiting for the initial self-query to find peers
26
+ allowQueryWithZeroPeers: true,
27
+
28
+ protocol: '/ipfs/lan/kad/1.0.0',
29
+ peerInfoMapper: removePublicAddressesMapper,
30
+ clientMode: false
31
+ })
32
+ }
33
+
34
+ // remove bootstrappers, mdns, etc
35
+ defaults.peerDiscovery = []
36
+
37
+ // remove services that are not used in tests
38
+ delete defaults.services.autoNAT
39
+ delete defaults.services.dcutr
40
+ delete defaults.services.delegatedRouting
41
+
42
+ return createHelia<Libp2p<DefaultLibp2pServices>>({
43
+ blockBrokers: [
44
+ bitswap()
45
+ ],
46
+ libp2p: defaults,
47
+ hashers: [
48
+ sha3512
49
+ ]
50
+ })
51
+ }
@@ -0,0 +1,30 @@
1
+ import { type Controller, createController } from 'ipfsd-ctl'
2
+ import * as kuboRpcClient from 'kubo-rpc-client'
3
+
4
+ export async function createKuboNode (): Promise<Controller> {
5
+ return createController({
6
+ kuboRpcModule: kuboRpcClient,
7
+ test: true,
8
+ endpoint: process.env.IPFSD_SERVER,
9
+ ipfsOptions: {
10
+ config: {
11
+ Addresses: {
12
+ Swarm: [
13
+ '/ip4/0.0.0.0/tcp/0',
14
+ '/ip4/0.0.0.0/tcp/0/ws'
15
+ ],
16
+ Gateway: '/ip4/127.0.0.1/tcp/8180'
17
+ },
18
+ Gateway: {
19
+ NoFetch: true,
20
+ ExposeRoutingAPI: true,
21
+ HTTPHeaders: {
22
+ 'Access-Control-Allow-Origin': ['*'],
23
+ 'Access-Control-Allow-Methods': ['GET', 'POST', 'PUT', 'OPTIONS']
24
+ }
25
+ }
26
+ }
27
+ },
28
+ args: ['--enable-pubsub-experiment', '--enable-namesys-pubsub']
29
+ })
30
+ }
@@ -0,0 +1,32 @@
1
+ /* eslint-disable @typescript-eslint/ban-ts-comment,@typescript-eslint/prefer-ts-expect-error */
2
+ import { createController, type Controller } from 'ipfsd-ctl'
3
+ import { path as kuboPath } from 'kubo'
4
+ import * as kuboRpcClient from 'kubo-rpc-client'
5
+
6
+ export async function createKuboNode (): Promise<Controller> {
7
+ return createController({
8
+ kuboRpcModule: kuboRpcClient,
9
+ ipfsBin: kuboPath(),
10
+ test: true,
11
+ ipfsOptions: {
12
+ config: {
13
+ Addresses: {
14
+ Swarm: [
15
+ '/ip4/0.0.0.0/tcp/4001',
16
+ '/ip4/0.0.0.0/tcp/4002/ws'
17
+ ],
18
+ Gateway: '/ip4/127.0.0.1/tcp/8180'
19
+ },
20
+ Gateway: {
21
+ NoFetch: true,
22
+ ExposeRoutingAPI: true,
23
+ HTTPHeaders: {
24
+ 'Access-Control-Allow-Origin': ['*'],
25
+ 'Access-Control-Allow-Methods': ['GET', 'POST', 'PUT', 'OPTIONS']
26
+ }
27
+ }
28
+ }
29
+ },
30
+ args: ['--enable-pubsub-experiment', '--enable-namesys-pubsub']
31
+ })
32
+ }
@@ -0,0 +1,46 @@
1
+ import all from 'it-all'
2
+ import map from 'it-map'
3
+ import { sha256 } from 'multiformats/hashes/sha2'
4
+ import { compare as uint8ArrayCompare } from 'uint8arrays/compare'
5
+ import { xor as uint8ArrayXor } from 'uint8arrays/xor'
6
+ import type { PeerId } from '@libp2p/interface'
7
+
8
+ /**
9
+ * Sort peers by distance to the KadID of the passed buffer
10
+ */
11
+ export async function sortClosestPeers (buf: Uint8Array, peers: PeerId[]): Promise<PeerId[]> {
12
+ const kadId = await convertBuffer(buf)
13
+
14
+ const distances = await all(
15
+ map(peers, async (peer) => {
16
+ const id = await convertPeerId(peer)
17
+
18
+ return {
19
+ peer,
20
+ distance: uint8ArrayXor(id, kadId)
21
+ }
22
+ })
23
+ )
24
+
25
+ return distances
26
+ .sort((a, b) => {
27
+ return uint8ArrayCompare(a.distance, b.distance)
28
+ })
29
+ .map((d) => d.peer)
30
+ }
31
+
32
+ /**
33
+ * Creates a DHT ID by hashing a Peer ID
34
+ */
35
+ export async function convertPeerId (peerId: PeerId): Promise<Uint8Array> {
36
+ return convertBuffer(peerId.toBytes())
37
+ }
38
+
39
+ /**
40
+ * Creates a DHT ID by hashing a given Uint8Array
41
+ */
42
+ export async function convertBuffer (buf: Uint8Array): Promise<Uint8Array> {
43
+ const multihash = await sha256.digest(buf)
44
+
45
+ return multihash.digest
46
+ }
@@ -0,0 +1,7 @@
1
+ import type { PeerIdType } from '@libp2p/interface'
2
+
3
+ export const keyTypes: PeerIdType[] = [
4
+ 'Ed25519',
5
+ 'secp256k1',
6
+ 'RSA'
7
+ ]
@@ -0,0 +1,33 @@
1
+ import { CarWriter } from '@ipld/car'
2
+ import toBuffer from 'it-to-buffer'
3
+ import defer from 'p-defer'
4
+ import type { CID } from 'multiformats/cid'
5
+
6
+ export interface MemoryCar extends Pick<CarWriter, 'put' | 'close'> {
7
+ bytes(): Promise<Uint8Array>
8
+ }
9
+
10
+ export function memoryCarWriter (root: CID | CID[]): MemoryCar {
11
+ const deferred = defer<Uint8Array>()
12
+ const { writer, out } = CarWriter.create(Array.isArray(root) ? root : [root])
13
+
14
+ Promise.resolve()
15
+ .then(async () => {
16
+ deferred.resolve(await toBuffer(out))
17
+ })
18
+ .catch(err => {
19
+ deferred.reject(err)
20
+ })
21
+
22
+ return {
23
+ async put (block: { cid: CID, bytes: Uint8Array }): Promise<void> {
24
+ await writer.put(block)
25
+ },
26
+ async close (): Promise<void> {
27
+ await writer.close()
28
+ },
29
+ async bytes (): Promise<Uint8Array> {
30
+ return deferred.promise
31
+ }
32
+ }
33
+ }
@@ -0,0 +1,26 @@
1
+ export interface WaitForOptions {
2
+ timeout: number
3
+ delay?: number
4
+ message?: string
5
+ }
6
+
7
+ export async function waitFor (fn: () => Promise<boolean>, options: WaitForOptions): Promise<void> {
8
+ const delay = options.delay ?? 100
9
+ const timeoutAt = Date.now() + options.timeout
10
+
11
+ while (true) {
12
+ const result = await fn()
13
+
14
+ if (result) {
15
+ return
16
+ }
17
+
18
+ await new Promise<void>((resolve) => {
19
+ setTimeout(() => { resolve() }, delay)
20
+ })
21
+
22
+ if (Date.now() > timeoutAt) {
23
+ throw new Error(options.message ?? 'WaitFor timed out')
24
+ }
25
+ }
26
+ }
@@ -0,0 +1,59 @@
1
+ /* eslint-env mocha */
2
+
3
+ import { peerIdFromString } from '@libp2p/peer-id'
4
+ import { expect } from 'aegir/chai'
5
+ import toBuffer from 'it-to-buffer'
6
+ import { CID } from 'multiformats/cid'
7
+ import * as raw from 'multiformats/codecs/raw'
8
+ import { sha256 } from 'multiformats/hashes/sha2'
9
+ import { createHeliaNode } from './fixtures/create-helia.js'
10
+ import { createKuboNode } from './fixtures/create-kubo.js'
11
+ import type { HeliaLibp2p } from 'helia'
12
+ import type { Controller } from 'ipfsd-ctl'
13
+
14
+ describe('helia - blockstore', () => {
15
+ let helia: HeliaLibp2p
16
+ let kubo: Controller
17
+
18
+ beforeEach(async () => {
19
+ helia = await createHeliaNode()
20
+ kubo = await createKuboNode()
21
+
22
+ // connect the two nodes
23
+ await helia.libp2p.peerStore.merge(peerIdFromString(kubo.peer.id.toString()), {
24
+ multiaddrs: kubo.peer.addresses
25
+ })
26
+ await helia.libp2p.dial(peerIdFromString(kubo.peer.id.toString()))
27
+ })
28
+
29
+ afterEach(async () => {
30
+ if (helia != null) {
31
+ await helia.stop()
32
+ }
33
+
34
+ if (kubo != null) {
35
+ await kubo.stop()
36
+ }
37
+ })
38
+
39
+ it('should be able to send a block', async () => {
40
+ const input = Uint8Array.from([0, 1, 2, 3, 4])
41
+ const digest = await sha256.digest(input)
42
+ const cid = CID.createV1(raw.code, digest)
43
+ await helia.blockstore.put(cid, input)
44
+ const output = await toBuffer(kubo.api.cat(cid))
45
+
46
+ expect(output).to.equalBytes(input)
47
+ })
48
+
49
+ it('should be able to receive a block', async () => {
50
+ const input = Uint8Array.from([0, 1, 2, 3, 4])
51
+ const { cid } = await kubo.api.add({ content: input }, {
52
+ cidVersion: 1,
53
+ rawLeaves: true
54
+ })
55
+ const output = await helia.blockstore.get(CID.parse(cid.toString()))
56
+
57
+ expect(output).to.equalBytes(input)
58
+ })
59
+ })