helia 6.1.4 → 7.0.0
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.
- package/dist/index.min.js +65 -55
- package/dist/index.min.js.map +4 -4
- package/dist/src/block-storage.d.ts +62 -0
- package/dist/src/block-storage.d.ts.map +1 -0
- package/dist/src/block-storage.js +159 -0
- package/dist/src/block-storage.js.map +1 -0
- package/dist/src/datastore-version.d.ts +3 -0
- package/dist/src/datastore-version.d.ts.map +1 -0
- package/dist/src/datastore-version.js +20 -0
- package/dist/src/datastore-version.js.map +1 -0
- package/dist/src/get-codec.d.ts +4 -0
- package/dist/src/get-codec.d.ts.map +1 -0
- package/dist/src/get-codec.js +31 -0
- package/dist/src/get-codec.js.map +1 -0
- package/dist/src/get-crypto.d.ts +4 -0
- package/dist/src/get-crypto.d.ts.map +1 -0
- package/dist/src/get-crypto.js +35 -0
- package/dist/src/get-crypto.js.map +1 -0
- package/dist/src/get-hasher.d.ts +4 -0
- package/dist/src/get-hasher.d.ts.map +1 -0
- package/dist/src/get-hasher.js +31 -0
- package/dist/src/get-hasher.js.map +1 -0
- package/dist/src/helia.d.ts +165 -0
- package/dist/src/helia.d.ts.map +1 -0
- package/dist/src/helia.js +165 -0
- package/dist/src/helia.js.map +1 -0
- package/dist/src/index.d.ts +30 -19
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +78 -12
- package/dist/src/index.js.map +1 -1
- package/dist/src/networked-storage.d.ts +28 -0
- package/dist/src/networked-storage.d.ts.map +1 -0
- package/dist/src/networked-storage.js +52 -0
- package/dist/src/networked-storage.js.map +1 -0
- package/dist/src/pins.d.ts +21 -0
- package/dist/src/pins.d.ts.map +1 -0
- package/dist/src/pins.js +140 -0
- package/dist/src/pins.js.map +1 -0
- package/dist/src/routing.d.ts +51 -0
- package/dist/src/routing.d.ts.map +1 -0
- package/dist/src/routing.js +311 -0
- package/dist/src/routing.js.map +1 -0
- package/dist/src/session-storage.d.ts +47 -0
- package/dist/src/session-storage.d.ts.map +1 -0
- package/dist/src/session-storage.js +148 -0
- package/dist/src/session-storage.js.map +1 -0
- package/dist/src/storage.d.ts +55 -0
- package/dist/src/storage.d.ts.map +1 -0
- package/dist/src/storage.js +225 -0
- package/dist/src/storage.js.map +1 -0
- package/dist/src/version.d.ts +1 -1
- package/dist/src/version.js +1 -1
- package/dist/typedoc-urls.json +3 -6
- package/package.json +38 -41
- package/src/block-storage.ts +194 -0
- package/src/datastore-version.ts +25 -0
- package/src/get-codec.ts +39 -0
- package/src/get-crypto.ts +44 -0
- package/src/get-hasher.ts +39 -0
- package/src/helia.ts +364 -0
- package/src/index.ts +58 -30
- package/src/networked-storage.ts +74 -0
- package/src/pins.ts +208 -0
- package/src/routing.ts +389 -0
- package/src/session-storage.ts +174 -0
- package/src/storage.ts +294 -0
- package/src/version.ts +1 -1
- package/dist/src/utils/bootstrappers.d.ts +0 -4
- package/dist/src/utils/bootstrappers.d.ts.map +0 -1
- package/dist/src/utils/bootstrappers.js +0 -13
- package/dist/src/utils/bootstrappers.js.map +0 -1
- package/dist/src/utils/helia-defaults.d.ts +0 -48
- package/dist/src/utils/helia-defaults.d.ts.map +0 -1
- package/dist/src/utils/helia-defaults.js +0 -85
- package/dist/src/utils/helia-defaults.js.map +0 -1
- package/dist/src/utils/libp2p-defaults.browser.d.ts +0 -19
- package/dist/src/utils/libp2p-defaults.browser.d.ts.map +0 -1
- package/dist/src/utils/libp2p-defaults.browser.js +0 -73
- package/dist/src/utils/libp2p-defaults.browser.js.map +0 -1
- package/dist/src/utils/libp2p-defaults.d.ts +0 -49
- package/dist/src/utils/libp2p-defaults.d.ts.map +0 -1
- package/dist/src/utils/libp2p-defaults.js +0 -113
- package/dist/src/utils/libp2p-defaults.js.map +0 -1
- package/dist/src/utils/libp2p.d.ts +0 -19
- package/dist/src/utils/libp2p.d.ts.map +0 -1
- package/dist/src/utils/libp2p.js +0 -19
- package/dist/src/utils/libp2p.js.map +0 -1
- package/src/utils/bootstrappers.ts +0 -12
- package/src/utils/helia-defaults.ts +0 -92
- package/src/utils/libp2p-defaults.browser.ts +0 -92
- package/src/utils/libp2p-defaults.ts +0 -137
- package/src/utils/libp2p.ts +0 -42
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { UnknownCryptoError } from '@helia/interface'
|
|
2
|
+
import { isPromise } from '@helia/utils'
|
|
3
|
+
import { ecdsaCrypto, ed25519Crypto, rsaCrypto } from '@ipshipyard/crypto'
|
|
4
|
+
import type { CryptoLoader } from '@helia/interface'
|
|
5
|
+
import type { Crypto } from '@ipshipyard/crypto'
|
|
6
|
+
|
|
7
|
+
export function getCrypto (initialCryptos: Array<Crypto> = [], loadCrypto?: CryptoLoader): CryptoLoader {
|
|
8
|
+
const cryptos: Record<string | number, Crypto> = {}
|
|
9
|
+
|
|
10
|
+
initialCryptos = [
|
|
11
|
+
ecdsaCrypto(),
|
|
12
|
+
ed25519Crypto(),
|
|
13
|
+
rsaCrypto(),
|
|
14
|
+
...initialCryptos
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
initialCryptos.forEach(crypto => {
|
|
18
|
+
cryptos[crypto.type] = crypto
|
|
19
|
+
cryptos[crypto.code] = crypto
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
return async (nameOrCode) => {
|
|
23
|
+
let crypto = cryptos[nameOrCode]
|
|
24
|
+
|
|
25
|
+
if (crypto == null && loadCrypto != null) {
|
|
26
|
+
const res = loadCrypto(nameOrCode)
|
|
27
|
+
|
|
28
|
+
if (isPromise(res)) {
|
|
29
|
+
crypto = await res
|
|
30
|
+
} else {
|
|
31
|
+
crypto = res
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
cryptos[crypto.type] = crypto
|
|
35
|
+
cryptos[crypto.code] = crypto
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (crypto != null) {
|
|
39
|
+
return crypto
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
throw new UnknownCryptoError(`Could not load crypto for ${crypto}`)
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { UnknownHashAlgorithmError } from '@helia/interface'
|
|
2
|
+
import { isPromise } from '@helia/utils'
|
|
3
|
+
import { identity } from 'multiformats/hashes/identity'
|
|
4
|
+
import { sha256 } from 'multiformats/hashes/sha2'
|
|
5
|
+
import type { HasherLoader } from '@helia/interface'
|
|
6
|
+
import type { MultihashHasher } from 'multiformats/hashes/interface'
|
|
7
|
+
|
|
8
|
+
export function getHasher (initialHashers: MultihashHasher[] = [], loadHasher?: HasherLoader): HasherLoader {
|
|
9
|
+
const hashers: Record<number, MultihashHasher> = {
|
|
10
|
+
[sha256.code]: sha256,
|
|
11
|
+
[identity.code]: identity
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
initialHashers.forEach(hasher => {
|
|
15
|
+
hashers[hasher.code] = hasher
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
return async (code) => {
|
|
19
|
+
let hasher = hashers[code]
|
|
20
|
+
|
|
21
|
+
if (hasher == null && loadHasher != null) {
|
|
22
|
+
const res = loadHasher(code)
|
|
23
|
+
|
|
24
|
+
if (isPromise(res)) {
|
|
25
|
+
hasher = await res
|
|
26
|
+
} else {
|
|
27
|
+
hasher = res
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
hashers[hasher.code] = hasher
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (hasher != null) {
|
|
34
|
+
return hasher
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
throw new UnknownHashAlgorithmError(`No hasher configured for multihash code 0x${code.toString(16)}, please configure one. You can look up which hash this is at https://github.com/multiformats/multicodec/blob/master/table.csv`)
|
|
38
|
+
}
|
|
39
|
+
}
|
package/src/helia.ts
ADDED
|
@@ -0,0 +1,364 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @packageDocumentation
|
|
3
|
+
*
|
|
4
|
+
* This module contains utility code that is shared between various Helia
|
|
5
|
+
* modules such as `helia`, `@helia/http`, etc.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { InvalidConfigurationError } from '@helia/utils'
|
|
9
|
+
import { keychain } from '@ipshipyard/keychain'
|
|
10
|
+
import { start, stop } from '@libp2p/interface'
|
|
11
|
+
import { dns } from '@multiformats/dns'
|
|
12
|
+
import { defaultLogger } from 'birnam'
|
|
13
|
+
import { MemoryBlockstore } from 'blockstore-core'
|
|
14
|
+
import { MemoryDatastore } from 'datastore-core'
|
|
15
|
+
import drain from 'it-drain'
|
|
16
|
+
import { TypedEventEmitter } from 'main-event'
|
|
17
|
+
import { CustomProgressEvent } from 'progress-events'
|
|
18
|
+
import { BlockStorage } from './block-storage.ts'
|
|
19
|
+
import { assertDatastoreVersionIsCurrent } from './datastore-version.ts'
|
|
20
|
+
import { getCodec } from './get-codec.ts'
|
|
21
|
+
import { getCrypto } from './get-crypto.ts'
|
|
22
|
+
import { getHasher } from './get-hasher.ts'
|
|
23
|
+
import { NetworkedStorage } from './networked-storage.ts'
|
|
24
|
+
import { PinsImpl } from './pins.ts'
|
|
25
|
+
import { Routing as RoutingClass } from './routing.ts'
|
|
26
|
+
import type { BlockStorageInit } from './block-storage.ts'
|
|
27
|
+
import type { CodecLoader, GCOptions, HasherLoader, Helia as HeliaInterface, HeliaEvents, Routing, CryptoLoader, Crypto, NodeInfo, Router, HeliaMixin } from '@helia/interface'
|
|
28
|
+
import type { BlockBroker } from '@helia/interface'
|
|
29
|
+
import type { Pins } from '@helia/interface'
|
|
30
|
+
import type { Keychain, KeychainInit } from '@ipshipyard/keychain'
|
|
31
|
+
import type { ComponentLogger, Logger, Metrics } from '@libp2p/interface'
|
|
32
|
+
import type { DNS } from '@multiformats/dns'
|
|
33
|
+
import type { Blockstore } from 'interface-blockstore'
|
|
34
|
+
import type { Datastore } from 'interface-datastore'
|
|
35
|
+
import type { BlockCodec } from 'multiformats'
|
|
36
|
+
import type { CID } from 'multiformats/cid'
|
|
37
|
+
import type { MultihashHasher } from 'multiformats/hashes/interface'
|
|
38
|
+
|
|
39
|
+
export type { BlockStorage, BlockStorageInit }
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Options used to create a Helia node.
|
|
43
|
+
*/
|
|
44
|
+
export interface HeliaInit {
|
|
45
|
+
/**
|
|
46
|
+
* By default Helia stores the node's PeerId in an encrypted form in a
|
|
47
|
+
* libp2p keystore. These options control how that keystore is configured.
|
|
48
|
+
*/
|
|
49
|
+
keychain?: KeychainInit
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* The blockstore is where blocks are stored
|
|
53
|
+
*/
|
|
54
|
+
blockstore?: Blockstore
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* The datastore is where data is stored
|
|
58
|
+
*/
|
|
59
|
+
datastore?: Datastore
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* By default sha256, sha512 and identity hashes are supported for
|
|
63
|
+
* bitswap operations. To bitswap blocks with CIDs using other hashes
|
|
64
|
+
* pass appropriate MultihashHashers here.
|
|
65
|
+
*/
|
|
66
|
+
hashers?: MultihashHasher[]
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* An optional function that can load a MultihashHasher on demand. May return
|
|
70
|
+
* a promise.
|
|
71
|
+
*/
|
|
72
|
+
loadHasher?(code: number): MultihashHasher | Promise<MultihashHasher>
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* A BlockCodec allows converting a block to an object representation
|
|
76
|
+
*/
|
|
77
|
+
codecs?: Array<BlockCodec<any, any>>
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* An optional function that can load a BlockCodec on demand. May return a
|
|
81
|
+
* promise.
|
|
82
|
+
*/
|
|
83
|
+
loadCodec?(code: number): BlockCodec<any, any> | Promise<BlockCodec<any, any>>
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* A list of pre-supported public/private key implementations
|
|
87
|
+
*/
|
|
88
|
+
cryptos?: Array<Crypto>
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Dynamically load a cryptography implementation
|
|
92
|
+
*/
|
|
93
|
+
loadCrypto?: CryptoLoader
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Garbage collection requires preventing blockstore writes during searches
|
|
97
|
+
* for unpinned blocks as DAGs are typically pinned after they've been
|
|
98
|
+
* imported - without locking this could lead to the deletion of blocks while
|
|
99
|
+
* they are being added to the blockstore.
|
|
100
|
+
*
|
|
101
|
+
* By default this lock is held on the current process and other processes
|
|
102
|
+
* will contact this process for access.
|
|
103
|
+
*
|
|
104
|
+
* If Helia is being run in multiple processes, one process must hold the GC
|
|
105
|
+
* lock so use this option to control which process that is.
|
|
106
|
+
*
|
|
107
|
+
* @default true
|
|
108
|
+
*/
|
|
109
|
+
holdGcLock?: boolean
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* An optional logging component to pass to libp2p. If not specified the
|
|
113
|
+
* default implementation from libp2p will be used.
|
|
114
|
+
*/
|
|
115
|
+
logger?: ComponentLogger
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* A list of strategies used to fetch blocks when they are not present in
|
|
119
|
+
* the local blockstore
|
|
120
|
+
*/
|
|
121
|
+
blockBrokers?: Array<BlockBroker | ((components: any) => BlockBroker)>
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Routers perform operations such as looking up content providers,
|
|
125
|
+
* information about network peers or getting/putting records.
|
|
126
|
+
*/
|
|
127
|
+
routers?: Array<Router | ((components: any) => Router)>
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* During provider lookups, peers can be returned from routing implementations
|
|
131
|
+
* with no multiaddrs.
|
|
132
|
+
*
|
|
133
|
+
* This can happen when they've been retrieved from network peers that only
|
|
134
|
+
* store multiaddrs for a limited amount of time.
|
|
135
|
+
*
|
|
136
|
+
* When this happens the peer's info has to be looked up with a further query.
|
|
137
|
+
*
|
|
138
|
+
* To not have this query block the yielding of other providers returned with
|
|
139
|
+
* multiaddrs, a separate queue is used to perform this lookup.
|
|
140
|
+
*
|
|
141
|
+
* This config value controls the concurrency of that queue.
|
|
142
|
+
*
|
|
143
|
+
* @default 5
|
|
144
|
+
*/
|
|
145
|
+
providerLookupConcurrency?: number
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Components used by subclasses
|
|
149
|
+
*/
|
|
150
|
+
components?: Record<string, any>
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* An optional DNS implementation used to perform queries for DNS records.
|
|
154
|
+
*/
|
|
155
|
+
dns?: DNS
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* A metrics object that can be used to collected arbitrary stats about node
|
|
159
|
+
* usage.
|
|
160
|
+
*/
|
|
161
|
+
metrics?: Metrics
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Limit the maximum supported size of identity hash digests to this value
|
|
165
|
+
*
|
|
166
|
+
* @default 128
|
|
167
|
+
*/
|
|
168
|
+
maxIdentityHashDigestLength?: number
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
interface Components {
|
|
172
|
+
blockstore: Blockstore
|
|
173
|
+
datastore: Datastore
|
|
174
|
+
logger: ComponentLogger
|
|
175
|
+
blockBrokers: BlockBroker[]
|
|
176
|
+
routing: Routing
|
|
177
|
+
dns: DNS
|
|
178
|
+
keychain: Keychain
|
|
179
|
+
metrics?: Metrics
|
|
180
|
+
getCodec: CodecLoader
|
|
181
|
+
getHasher: HasherLoader
|
|
182
|
+
getCrypto: CryptoLoader
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
export class Helia implements HeliaInterface {
|
|
186
|
+
public info: NodeInfo
|
|
187
|
+
public blockstore: BlockStorage
|
|
188
|
+
public datastore: Datastore
|
|
189
|
+
public events: TypedEventEmitter<HeliaEvents<this>>
|
|
190
|
+
public pins: Pins
|
|
191
|
+
public logger: ComponentLogger
|
|
192
|
+
public routing: RoutingClass
|
|
193
|
+
public getCodec: CodecLoader
|
|
194
|
+
public getHasher: HasherLoader
|
|
195
|
+
public getCrypto: CryptoLoader
|
|
196
|
+
public dns: DNS
|
|
197
|
+
public keychain: Keychain
|
|
198
|
+
public metrics?: Metrics
|
|
199
|
+
public status: 'stopped' | 'stopping' | 'starting' | 'started'
|
|
200
|
+
private readonly log: Logger
|
|
201
|
+
private readonly blockBrokers: BlockBroker[]
|
|
202
|
+
private readonly mixins: HeliaMixin[]
|
|
203
|
+
|
|
204
|
+
constructor (init: HeliaInit & { name: string, version: string }) {
|
|
205
|
+
this.info = {
|
|
206
|
+
name: init.name,
|
|
207
|
+
version: init.version
|
|
208
|
+
}
|
|
209
|
+
this.logger = init.logger ?? defaultLogger()
|
|
210
|
+
this.log = this.logger.forComponent('helia')
|
|
211
|
+
this.getHasher = getHasher(init.hashers, init.loadHasher)
|
|
212
|
+
this.getCodec = getCodec(init.codecs, init.loadCodec)
|
|
213
|
+
this.getCrypto = getCrypto(init.cryptos, init.loadCrypto)
|
|
214
|
+
this.dns = init.dns ?? dns()
|
|
215
|
+
this.metrics = init.metrics
|
|
216
|
+
this.events = new TypedEventEmitter<HeliaEvents<typeof this>>()
|
|
217
|
+
this.status = 'stopped'
|
|
218
|
+
this.mixins = []
|
|
219
|
+
|
|
220
|
+
// @ts-expect-error routing and keychain are not set
|
|
221
|
+
const components: Components = {
|
|
222
|
+
blockstore: init.blockstore ?? new MemoryBlockstore(),
|
|
223
|
+
datastore: init.datastore ?? new MemoryDatastore(),
|
|
224
|
+
logger: this.logger,
|
|
225
|
+
blockBrokers: [],
|
|
226
|
+
getHasher: this.getHasher,
|
|
227
|
+
getCodec: this.getCodec,
|
|
228
|
+
getCrypto: this.getCrypto,
|
|
229
|
+
dns: this.dns,
|
|
230
|
+
metrics: this.metrics,
|
|
231
|
+
...(init.components ?? {})
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
this.keychain = components.keychain = keychain()(components)
|
|
235
|
+
|
|
236
|
+
this.routing = components.routing = new RoutingClass(components, {
|
|
237
|
+
routers: (init.routers ?? []).flatMap((router: Router | ((components: any) => Router)) => {
|
|
238
|
+
if (typeof router === 'function') {
|
|
239
|
+
router = router(components)
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// if the router itself is a router
|
|
243
|
+
const routers = [
|
|
244
|
+
router
|
|
245
|
+
]
|
|
246
|
+
|
|
247
|
+
return routers
|
|
248
|
+
}),
|
|
249
|
+
providerLookupConcurrency: init.providerLookupConcurrency
|
|
250
|
+
})
|
|
251
|
+
|
|
252
|
+
this.blockBrokers = components.blockBrokers = (init.blockBrokers ?? []).map((broker) => {
|
|
253
|
+
if (typeof broker === 'function') {
|
|
254
|
+
broker = broker(components)
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
return broker
|
|
258
|
+
})
|
|
259
|
+
|
|
260
|
+
const networkedStorage = new NetworkedStorage(components, init)
|
|
261
|
+
this.pins = new PinsImpl(components.datastore, networkedStorage, this.getCodec)
|
|
262
|
+
this.blockstore = new BlockStorage(networkedStorage, this.pins, this.routing, {
|
|
263
|
+
holdGcLock: init.holdGcLock ?? true
|
|
264
|
+
})
|
|
265
|
+
this.datastore = components.datastore
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
hasRouter (name: string): boolean {
|
|
269
|
+
return this.routing.hasRouter(name)
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
addRouter (router: Router): void {
|
|
273
|
+
this.routing.addRouter(router)
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
hasBlockBroker (name: string): boolean {
|
|
277
|
+
return this.blockBrokers.findIndex(b => b.name === name) !== -1
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
addBlockBroker (blockBroker: BlockBroker): void {
|
|
281
|
+
this.blockBrokers.push(blockBroker)
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
addMixin (mixin: HeliaMixin): void {
|
|
285
|
+
if (this.mixins.find(m => m.name === mixin.name) != null) {
|
|
286
|
+
throw new InvalidConfigurationError(`A mixin with the name "${mixin.name} is already present`)
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
this.mixins.push(mixin)
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
async start (): Promise<this> {
|
|
293
|
+
this.status = 'starting'
|
|
294
|
+
|
|
295
|
+
await assertDatastoreVersionIsCurrent(this.datastore)
|
|
296
|
+
await start(
|
|
297
|
+
this.blockstore,
|
|
298
|
+
this.datastore,
|
|
299
|
+
this.routing,
|
|
300
|
+
...this.blockBrokers
|
|
301
|
+
)
|
|
302
|
+
|
|
303
|
+
for (const mixin of this.mixins) {
|
|
304
|
+
await mixin.start?.(this)
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
this.status = 'started'
|
|
308
|
+
this.events.dispatchEvent(new CustomEvent('start', { detail: this }))
|
|
309
|
+
|
|
310
|
+
return this
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
async stop (): Promise<this> {
|
|
314
|
+
this.status = 'stopping'
|
|
315
|
+
|
|
316
|
+
for (const mixin of this.mixins) {
|
|
317
|
+
await mixin.stop?.(this)
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
await stop(
|
|
321
|
+
this.blockstore,
|
|
322
|
+
this.datastore,
|
|
323
|
+
this.routing,
|
|
324
|
+
...this.blockBrokers
|
|
325
|
+
)
|
|
326
|
+
|
|
327
|
+
this.status = 'stopped'
|
|
328
|
+
this.events.dispatchEvent(new CustomEvent('stop', { detail: this }))
|
|
329
|
+
|
|
330
|
+
return this
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
async gc (options: GCOptions = {}): Promise<void> {
|
|
334
|
+
const releaseLock = await this.blockstore.lock.writeLock()
|
|
335
|
+
|
|
336
|
+
try {
|
|
337
|
+
const helia = this
|
|
338
|
+
const blockstore = this.blockstore.unwrap()
|
|
339
|
+
|
|
340
|
+
this.log('gc start')
|
|
341
|
+
|
|
342
|
+
await drain(blockstore.deleteMany((async function * (): AsyncGenerator<CID> {
|
|
343
|
+
for await (const { cid } of blockstore.getAll()) {
|
|
344
|
+
try {
|
|
345
|
+
if (await helia.pins.isPinned(cid, options)) {
|
|
346
|
+
continue
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
yield cid
|
|
350
|
+
|
|
351
|
+
options.onProgress?.(new CustomProgressEvent<CID>('helia:gc:deleted', cid))
|
|
352
|
+
} catch (err: any) {
|
|
353
|
+
helia.log.error('error during gc - %e', err)
|
|
354
|
+
options.onProgress?.(new CustomProgressEvent<Error>('helia:gc:error', err))
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
}())))
|
|
358
|
+
} finally {
|
|
359
|
+
releaseLock()
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
this.log('gc finished')
|
|
363
|
+
}
|
|
364
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -19,15 +19,17 @@
|
|
|
19
19
|
* ```
|
|
20
20
|
*/
|
|
21
21
|
|
|
22
|
-
import {
|
|
23
|
-
import {
|
|
24
|
-
import {
|
|
25
|
-
import
|
|
26
|
-
import
|
|
22
|
+
import { withBitswap } from '@helia/bitswap'
|
|
23
|
+
import { withHTTP } from '@helia/http'
|
|
24
|
+
import { withLibp2p } from '@helia/libp2p'
|
|
25
|
+
import * as dagCbor from '@ipld/dag-cbor'
|
|
26
|
+
import * as dagJson from '@ipld/dag-json'
|
|
27
|
+
import * as json from 'multiformats/codecs/json'
|
|
28
|
+
import { sha512 } from 'multiformats/hashes/sha2'
|
|
29
|
+
import { Helia as HeliaClass } from './helia.ts'
|
|
30
|
+
import { name, version } from './version.ts'
|
|
31
|
+
import type { HeliaInit } from './helia.ts'
|
|
27
32
|
import type { Helia } from '@helia/interface'
|
|
28
|
-
import type { HeliaInit } from '@helia/utils'
|
|
29
|
-
import type { Libp2p } from '@libp2p/interface'
|
|
30
|
-
import type { CID } from 'multiformats/cid'
|
|
31
33
|
|
|
32
34
|
// re-export interface types so people don't have to depend on @helia/interface
|
|
33
35
|
// if they don't want to
|
|
@@ -35,20 +37,6 @@ export * from '@helia/interface'
|
|
|
35
37
|
|
|
36
38
|
export type { HeliaInit }
|
|
37
39
|
|
|
38
|
-
export type { DefaultLibp2pServices, Libp2pDefaultsOptions }
|
|
39
|
-
|
|
40
|
-
// allow amending the default config
|
|
41
|
-
export { libp2pDefaults }
|
|
42
|
-
export { heliaDefaults }
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* DAGWalkers take a block and yield CIDs encoded in that block
|
|
46
|
-
*/
|
|
47
|
-
export interface DAGWalker {
|
|
48
|
-
codec: number
|
|
49
|
-
walk(block: Uint8Array): Generator<CID, void, undefined>
|
|
50
|
-
}
|
|
51
|
-
|
|
52
40
|
/**
|
|
53
41
|
* Create and return a Helia node
|
|
54
42
|
*
|
|
@@ -70,15 +58,55 @@ export interface DAGWalker {
|
|
|
70
58
|
* }
|
|
71
59
|
* ```
|
|
72
60
|
*/
|
|
73
|
-
export
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
61
|
+
export function createHelia (init: HeliaInit = {}): Helia {
|
|
62
|
+
return withBitswap(withLibp2p(withHTTP(createHeliaLight({
|
|
63
|
+
...init,
|
|
64
|
+
codecs: [
|
|
65
|
+
dagCbor,
|
|
66
|
+
dagJson,
|
|
67
|
+
json,
|
|
68
|
+
...(init.codecs ?? [])
|
|
69
|
+
],
|
|
70
|
+
hashers: [
|
|
71
|
+
sha512,
|
|
72
|
+
...(init.hashers ?? [])
|
|
73
|
+
]
|
|
74
|
+
}))))
|
|
75
|
+
}
|
|
78
76
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
77
|
+
/**
|
|
78
|
+
* Create and return a Helia node without and routing or block broker config.
|
|
79
|
+
*
|
|
80
|
+
* The only supported codecs are `dag-pb` and `raw`, and the only supported
|
|
81
|
+
* hashes are `sha2-256` and `identity`.
|
|
82
|
+
*
|
|
83
|
+
* This allows more flexible customization and the smallest possible bundle size
|
|
84
|
+
* in web browsers.
|
|
85
|
+
*
|
|
86
|
+
* @example Creating a Helia node
|
|
87
|
+
*
|
|
88
|
+
* ```ts
|
|
89
|
+
* import { createHeliaLight } from 'helia'
|
|
90
|
+
* import { unixfs } from '@helia/unixfs'
|
|
91
|
+
* import { CID } from 'multiformats/cid'
|
|
92
|
+
*
|
|
93
|
+
* const helia = await createHeliaLight()
|
|
94
|
+
* const fs = unixfs(helia)
|
|
95
|
+
* const cid = CID.parse('QmFoo...')
|
|
96
|
+
*
|
|
97
|
+
* for await (const buf of fs.cat(cid, {
|
|
98
|
+
* signal: AbortSignal.timeout(5_000)
|
|
99
|
+
* })) {
|
|
100
|
+
* console.info(buf)
|
|
101
|
+
* }
|
|
102
|
+
* ```
|
|
103
|
+
*/
|
|
104
|
+
export function createHeliaLight (init: HeliaInit = {}): Helia {
|
|
105
|
+
const helia = new HeliaClass({
|
|
106
|
+
name,
|
|
107
|
+
version,
|
|
108
|
+
...init
|
|
109
|
+
})
|
|
82
110
|
|
|
83
111
|
return helia
|
|
84
112
|
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { InvalidConfigurationError } from '@helia/utils'
|
|
2
|
+
import { start, stop } from '@libp2p/interface'
|
|
3
|
+
import { SessionStorage } from './session-storage.ts'
|
|
4
|
+
import { Storage } from './storage.ts'
|
|
5
|
+
import type { StorageComponents, StorageInit } from './storage.ts'
|
|
6
|
+
import type { BlockBroker, Blocks, CreateSessionOptions, SessionBlockstore } from '@helia/interface'
|
|
7
|
+
import type { Startable } from '@libp2p/interface'
|
|
8
|
+
import type { AbortOptions } from 'abort-error'
|
|
9
|
+
import type { Blockstore } from 'interface-blockstore'
|
|
10
|
+
import type { CID } from 'multiformats/cid'
|
|
11
|
+
|
|
12
|
+
export interface GetOptions extends AbortOptions {
|
|
13
|
+
progress?(evt: Event): void
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export type NetworkedStorageComponents = StorageComponents<BlockBroker>
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Networked storage wraps a regular blockstore - when getting blocks if the
|
|
20
|
+
* blocks are not present, the configured BlockBrokers will be used to fetch them.
|
|
21
|
+
*/
|
|
22
|
+
export class NetworkedStorage extends Storage<BlockBroker> implements Blocks, Startable {
|
|
23
|
+
private started: boolean
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Create a new BlockStorage
|
|
27
|
+
*/
|
|
28
|
+
constructor (components: NetworkedStorageComponents, init: StorageInit = {}) {
|
|
29
|
+
super(components, init)
|
|
30
|
+
|
|
31
|
+
this.started = false
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
isStarted (): boolean {
|
|
35
|
+
return this.started
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async start (): Promise<void> {
|
|
39
|
+
await start(this.child, ...this.blockBrokers)
|
|
40
|
+
this.started = true
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
async stop (): Promise<void> {
|
|
44
|
+
await stop(this.child, ...this.blockBrokers)
|
|
45
|
+
this.started = false
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
unwrap (): Blockstore {
|
|
49
|
+
return this.child
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
createSession (root: CID, options?: CreateSessionOptions): SessionBlockstore {
|
|
53
|
+
if (this.blockBrokers.length === 0) {
|
|
54
|
+
throw new InvalidConfigurationError('No block brokers configured')
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const blockBrokers = this.blockBrokers
|
|
58
|
+
.map(broker => broker.createSession?.(options))
|
|
59
|
+
.filter(broker => broker != null)
|
|
60
|
+
|
|
61
|
+
if (blockBrokers.length === 0) {
|
|
62
|
+
throw new InvalidConfigurationError(`No configured block brokers support sessions - tried ${this.blockBrokers.map(b => b.name).join(', ')}`)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return new SessionStorage({
|
|
66
|
+
blockstore: this.child,
|
|
67
|
+
blockBrokers,
|
|
68
|
+
getHasher: this.getHasher,
|
|
69
|
+
logger: this.logger
|
|
70
|
+
}, {
|
|
71
|
+
root
|
|
72
|
+
})
|
|
73
|
+
}
|
|
74
|
+
}
|