@autonomys/auto-drive 0.7.1 → 0.7.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.
- package/dist/cid/index.d.ts +1 -1
- package/dist/cid/index.d.ts.map +1 -1
- package/dist/cid/index.js +3 -3
- package/dist/ipld/builders.d.ts +1 -1
- package/dist/ipld/builders.d.ts.map +1 -1
- package/dist/ipld/chunker.d.ts +23 -13
- package/dist/ipld/chunker.d.ts.map +1 -1
- package/dist/ipld/chunker.js +148 -45
- package/dist/ipld/index.d.ts +2 -1
- package/dist/ipld/index.d.ts.map +1 -1
- package/dist/ipld/index.js +2 -1
- package/dist/ipld/nodes.d.ts +1 -1
- package/dist/ipld/nodes.d.ts.map +1 -1
- package/dist/ipld/nodes.js +1 -1
- package/dist/ipld/utils.d.ts +7 -4
- package/dist/ipld/utils.d.ts.map +1 -1
- package/dist/ipld/utils.js +49 -9
- package/dist/metadata/offchain/file.d.ts +2 -2
- package/dist/metadata/offchain/file.d.ts.map +1 -1
- package/dist/metadata/offchain/file.js +6 -13
- package/dist/metadata/offchain/folder.d.ts +4 -1
- package/dist/metadata/offchain/folder.d.ts.map +1 -1
- package/dist/metadata/offchain/folder.js +17 -1
- package/dist/metadata/onchain/utils.js +2 -2
- package/package.json +4 -2
- package/src/cid/index.ts +3 -3
- package/src/ipld/builders.ts +1 -1
- package/src/ipld/chunker.ts +114 -56
- package/src/ipld/index.ts +2 -1
- package/src/ipld/nodes.ts +1 -1
- package/src/ipld/utils.ts +17 -11
- package/src/metadata/offchain/file.ts +10 -15
- package/src/metadata/offchain/folder.ts +24 -3
- package/src/metadata/onchain/utils.ts +2 -2
- package/tests/chunker.spec.ts +148 -46
- package/tests/cid.spec.ts +1 -1
- package/tests/nodes.spec.ts +1 -1
package/src/ipld/builders.ts
CHANGED
package/src/ipld/chunker.ts
CHANGED
|
@@ -1,71 +1,110 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { BaseBlockstore } from 'blockstore-core'
|
|
2
|
+
import type { AwaitIterable } from 'interface-store'
|
|
2
3
|
import { CID } from 'multiformats'
|
|
3
4
|
import { cidOfNode } from '../cid/index.js'
|
|
4
|
-
import { OffchainMetadata } from '../metadata/index.js'
|
|
5
|
+
import { decodeIPLDNodeData, OffchainMetadata } from '../metadata/index.js'
|
|
5
6
|
import { Builders, fileBuilders, metadataBuilders } from './builders.js'
|
|
6
7
|
import { createFolderInlinkIpldNode, createFolderIpldNode } from './nodes.js'
|
|
7
|
-
import { chunkBuffer, encodeNode } from './utils.js'
|
|
8
|
+
import { chunkBuffer, encodeNode, PBNode } from './utils.js'
|
|
8
9
|
|
|
9
|
-
export const DEFAULT_MAX_CHUNK_SIZE =
|
|
10
|
-
export const DEFAULT_MAX_LINK_PER_NODE = DEFAULT_MAX_CHUNK_SIZE / 64
|
|
10
|
+
export const DEFAULT_MAX_CHUNK_SIZE = 64 * 1024
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
nodes: Map<CID, PBNode>
|
|
15
|
-
}
|
|
12
|
+
const ESTIMATED_LINK_SIZE_IN_BYTES = 64
|
|
13
|
+
export const DEFAULT_MAX_LINK_PER_NODE = DEFAULT_MAX_CHUNK_SIZE / ESTIMATED_LINK_SIZE_IN_BYTES
|
|
16
14
|
|
|
17
|
-
export const
|
|
18
|
-
|
|
15
|
+
export const processFileToIPLDFormat = (
|
|
16
|
+
blockstore: BaseBlockstore,
|
|
17
|
+
file: AwaitIterable<Buffer>,
|
|
18
|
+
totalSize: number,
|
|
19
19
|
filename?: string,
|
|
20
|
-
{
|
|
21
|
-
|
|
20
|
+
{ maxChunkSize, maxLinkPerNode }: { maxChunkSize: number; maxLinkPerNode: number } = {
|
|
21
|
+
maxChunkSize: DEFAULT_MAX_CHUNK_SIZE,
|
|
22
22
|
maxLinkPerNode: DEFAULT_MAX_LINK_PER_NODE,
|
|
23
23
|
},
|
|
24
|
-
):
|
|
25
|
-
return
|
|
24
|
+
): Promise<CID> => {
|
|
25
|
+
return processBufferToIPLDFormat(blockstore, file, filename, totalSize, fileBuilders, {
|
|
26
|
+
maxChunkSize,
|
|
27
|
+
maxLinkPerNode,
|
|
28
|
+
})
|
|
26
29
|
}
|
|
27
30
|
|
|
28
|
-
export const
|
|
31
|
+
export const processMetadataToIPLDFormat = async (
|
|
32
|
+
blockstore: BaseBlockstore,
|
|
29
33
|
metadata: OffchainMetadata,
|
|
30
|
-
limits: {
|
|
31
|
-
|
|
34
|
+
limits: { maxChunkSize: number; maxLinkPerNode: number } = {
|
|
35
|
+
maxChunkSize: DEFAULT_MAX_CHUNK_SIZE,
|
|
32
36
|
maxLinkPerNode: DEFAULT_MAX_LINK_PER_NODE,
|
|
33
37
|
},
|
|
34
|
-
):
|
|
38
|
+
): Promise<CID> => {
|
|
35
39
|
const buffer = Buffer.from(JSON.stringify(metadata))
|
|
36
40
|
const name = `${metadata.name}.metadata.json`
|
|
37
|
-
return
|
|
41
|
+
return processBufferToIPLDFormat(
|
|
42
|
+
blockstore,
|
|
43
|
+
(async function* () {
|
|
44
|
+
yield buffer
|
|
45
|
+
})(),
|
|
46
|
+
name,
|
|
47
|
+
buffer.byteLength,
|
|
48
|
+
metadataBuilders,
|
|
49
|
+
limits,
|
|
50
|
+
)
|
|
38
51
|
}
|
|
39
52
|
|
|
40
|
-
const
|
|
41
|
-
|
|
53
|
+
const processBufferToIPLDFormat = async (
|
|
54
|
+
blockstore: BaseBlockstore,
|
|
55
|
+
buffer: AwaitIterable<Buffer>,
|
|
42
56
|
filename: string | undefined,
|
|
57
|
+
totalSize: number,
|
|
43
58
|
builders: Builders,
|
|
44
|
-
{
|
|
45
|
-
|
|
59
|
+
{ maxChunkSize, maxLinkPerNode }: { maxChunkSize: number; maxLinkPerNode: number } = {
|
|
60
|
+
maxChunkSize: DEFAULT_MAX_CHUNK_SIZE,
|
|
46
61
|
maxLinkPerNode: DEFAULT_MAX_LINK_PER_NODE,
|
|
47
62
|
},
|
|
48
|
-
):
|
|
49
|
-
|
|
50
|
-
const head = builders.single(buffer, filename)
|
|
51
|
-
const headCID = cidOfNode(head)
|
|
52
|
-
return {
|
|
53
|
-
headCID,
|
|
54
|
-
nodes: new Map([[headCID, head]]),
|
|
55
|
-
}
|
|
56
|
-
}
|
|
63
|
+
): Promise<CID> => {
|
|
64
|
+
const bufferChunks = chunkBuffer(buffer, { maxChunkSize })
|
|
57
65
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
const nodes = new Map<CID, PBNode>()
|
|
61
|
-
|
|
62
|
-
let CIDs: CID[] = bufferChunks.map((chunk) => {
|
|
66
|
+
let CIDs: CID[] = []
|
|
67
|
+
for await (const chunk of bufferChunks) {
|
|
63
68
|
const node = builders.chunk(chunk)
|
|
64
69
|
const cid = cidOfNode(node)
|
|
65
|
-
|
|
70
|
+
await blockstore.put(cid, encodeNode(node))
|
|
71
|
+
CIDs.push(cid)
|
|
72
|
+
}
|
|
66
73
|
|
|
67
|
-
|
|
74
|
+
return processBufferToIPLDFormatFromChunks(blockstore, CIDs, filename, totalSize, builders, {
|
|
75
|
+
maxLinkPerNode,
|
|
76
|
+
maxChunkSize,
|
|
68
77
|
})
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export const processBufferToIPLDFormatFromChunks = async (
|
|
81
|
+
blockstore: BaseBlockstore,
|
|
82
|
+
chunks: AwaitIterable<CID>,
|
|
83
|
+
filename: string | undefined,
|
|
84
|
+
totalSize: number,
|
|
85
|
+
builders: Builders,
|
|
86
|
+
{ maxLinkPerNode, maxChunkSize }: { maxLinkPerNode: number; maxChunkSize: number } = {
|
|
87
|
+
maxLinkPerNode: DEFAULT_MAX_LINK_PER_NODE,
|
|
88
|
+
maxChunkSize: DEFAULT_MAX_CHUNK_SIZE,
|
|
89
|
+
},
|
|
90
|
+
): Promise<CID> => {
|
|
91
|
+
let chunkCount = 0
|
|
92
|
+
let CIDs: CID[] = []
|
|
93
|
+
for await (const chunk of chunks) {
|
|
94
|
+
CIDs.push(chunk)
|
|
95
|
+
chunkCount++
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (CIDs.length === 1) {
|
|
99
|
+
const nodeBytes = await blockstore.get(CIDs[0])
|
|
100
|
+
await blockstore.delete(CIDs[0])
|
|
101
|
+
const data = decodeIPLDNodeData(nodeBytes)
|
|
102
|
+
const singleNode = builders.single(Buffer.from(data.data!), filename)
|
|
103
|
+
await blockstore.put(cidOfNode(singleNode), encodeNode(singleNode))
|
|
104
|
+
const headCID = cidOfNode(singleNode)
|
|
105
|
+
|
|
106
|
+
return headCID
|
|
107
|
+
}
|
|
69
108
|
|
|
70
109
|
let depth = 1
|
|
71
110
|
while (CIDs.length > maxLinkPerNode) {
|
|
@@ -73,31 +112,28 @@ const createBufferIPLDDag = (
|
|
|
73
112
|
for (let i = 0; i < CIDs.length; i += maxLinkPerNode) {
|
|
74
113
|
const chunk = CIDs.slice(i, i + maxLinkPerNode)
|
|
75
114
|
|
|
76
|
-
const node = builders.inlink(chunk, chunk.length, depth,
|
|
115
|
+
const node = builders.inlink(chunk, chunk.length, depth, maxChunkSize)
|
|
77
116
|
const cid = cidOfNode(node)
|
|
78
|
-
|
|
117
|
+
await blockstore.put(cid, encodeNode(node))
|
|
79
118
|
newCIDs.push(cid)
|
|
80
119
|
}
|
|
81
120
|
depth++
|
|
82
121
|
CIDs = newCIDs
|
|
83
122
|
}
|
|
84
|
-
const head = builders.root(CIDs,
|
|
123
|
+
const head = builders.root(CIDs, totalSize, depth, filename, maxChunkSize)
|
|
85
124
|
const headCID = cidOfNode(head)
|
|
86
|
-
|
|
125
|
+
await blockstore.put(headCID, encodeNode(head))
|
|
87
126
|
|
|
88
|
-
return
|
|
89
|
-
headCID,
|
|
90
|
-
nodes,
|
|
91
|
-
}
|
|
127
|
+
return headCID
|
|
92
128
|
}
|
|
93
129
|
|
|
94
|
-
export const
|
|
130
|
+
export const processFolderToIPLDFormat = async (
|
|
131
|
+
blockstore: BaseBlockstore,
|
|
95
132
|
children: CID[],
|
|
96
133
|
name: string,
|
|
97
134
|
size: number,
|
|
98
135
|
{ maxLinkPerNode }: { maxLinkPerNode: number } = { maxLinkPerNode: DEFAULT_MAX_LINK_PER_NODE },
|
|
99
|
-
):
|
|
100
|
-
const nodes = new Map<CID, PBNode>()
|
|
136
|
+
): Promise<CID> => {
|
|
101
137
|
let cids = children
|
|
102
138
|
let depth = 0
|
|
103
139
|
while (cids.length > maxLinkPerNode) {
|
|
@@ -106,7 +142,7 @@ export const createFolderIPLDDag = (
|
|
|
106
142
|
const chunk = cids.slice(i, i + maxLinkPerNode)
|
|
107
143
|
const node = createFolderInlinkIpldNode(chunk, depth)
|
|
108
144
|
const cid = cidOfNode(node)
|
|
109
|
-
|
|
145
|
+
await blockstore.put(cid, encodeNode(node))
|
|
110
146
|
newCIDs.push(cid)
|
|
111
147
|
}
|
|
112
148
|
cids = newCIDs
|
|
@@ -115,12 +151,34 @@ export const createFolderIPLDDag = (
|
|
|
115
151
|
|
|
116
152
|
const node = createFolderIpldNode(cids, name, depth, size)
|
|
117
153
|
const cid = cidOfNode(node)
|
|
118
|
-
|
|
154
|
+
await blockstore.put(cid, encodeNode(node))
|
|
119
155
|
|
|
120
|
-
return
|
|
121
|
-
|
|
122
|
-
|
|
156
|
+
return cid
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Process chunks to IPLD format, return the last chunk if it's not full
|
|
161
|
+
* @returns the last chunk if it's not full, otherwise an empty buffer
|
|
162
|
+
*/
|
|
163
|
+
export const processChunksToIPLDFormat = async (
|
|
164
|
+
blockstore: BaseBlockstore,
|
|
165
|
+
chunks: AwaitIterable<Buffer>,
|
|
166
|
+
builders: Builders,
|
|
167
|
+
{ maxChunkSize = DEFAULT_MAX_CHUNK_SIZE }: { maxChunkSize?: number },
|
|
168
|
+
): Promise<Buffer> => {
|
|
169
|
+
const bufferChunks = chunkBuffer(chunks, { maxChunkSize, ignoreLastChunk: false })
|
|
170
|
+
|
|
171
|
+
for await (const chunk of bufferChunks) {
|
|
172
|
+
if (chunk.byteLength < maxChunkSize) {
|
|
173
|
+
return chunk
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const node = builders.chunk(chunk)
|
|
177
|
+
const cid = cidOfNode(node)
|
|
178
|
+
await blockstore.put(cid, encodeNode(node))
|
|
123
179
|
}
|
|
180
|
+
|
|
181
|
+
return Buffer.alloc(0)
|
|
124
182
|
}
|
|
125
183
|
|
|
126
184
|
export const ensureNodeMaxSize = (
|
package/src/ipld/index.ts
CHANGED
package/src/ipld/nodes.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { createNode, PBNode } from '@ipld/dag-pb'
|
|
2
1
|
import { CID } from 'multiformats/cid'
|
|
2
|
+
import { createNode, PBNode } from '../ipld/index.js'
|
|
3
3
|
import { OffchainMetadata } from '../metadata/index.js'
|
|
4
4
|
import { encodeIPLDNodeData, MetadataType } from '../metadata/onchain/index.js'
|
|
5
5
|
import { DEFAULT_MAX_CHUNK_SIZE, ensureNodeMaxSize } from './chunker.js'
|
package/src/ipld/utils.ts
CHANGED
|
@@ -1,15 +1,21 @@
|
|
|
1
|
-
import { decode, encode, PBNode } from '@ipld/dag-pb'
|
|
1
|
+
import { createNode, decode, encode, PBNode } from '@ipld/dag-pb'
|
|
2
|
+
import { AwaitIterable } from 'interface-store'
|
|
2
3
|
|
|
3
|
-
export const chunkBuffer =
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
export const chunkBuffer = async function* (
|
|
5
|
+
buffer: AwaitIterable<Buffer>,
|
|
6
|
+
{ maxChunkSize, ignoreLastChunk = false }: { maxChunkSize: number; ignoreLastChunk?: boolean },
|
|
7
|
+
): AsyncIterable<Buffer> {
|
|
8
|
+
let target = Buffer.alloc(0)
|
|
9
|
+
for await (let chunk of buffer) {
|
|
10
|
+
target = Buffer.concat([target, chunk])
|
|
11
|
+
while (target.length >= maxChunkSize) {
|
|
12
|
+
yield target.subarray(0, maxChunkSize)
|
|
13
|
+
target = target.subarray(maxChunkSize)
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
if (target.length > 0 && !ignoreLastChunk) {
|
|
17
|
+
yield target
|
|
8
18
|
}
|
|
9
|
-
|
|
10
|
-
return chunks
|
|
11
19
|
}
|
|
12
20
|
|
|
13
|
-
export
|
|
14
|
-
|
|
15
|
-
export const decodeNode = (data: Uint8Array): PBNode => decode(data)
|
|
21
|
+
export { createNode, decode as decodeNode, encode as encodeNode, PBNode }
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { CID } from 'multiformats'
|
|
2
|
+
import { cidToString } from '../../index.js'
|
|
2
3
|
|
|
3
4
|
export type OffchainFileMetadata = {
|
|
4
5
|
type: 'file'
|
|
@@ -16,25 +17,19 @@ export interface ChunkInfo {
|
|
|
16
17
|
}
|
|
17
18
|
|
|
18
19
|
export const fileMetadata = (
|
|
19
|
-
|
|
20
|
+
headCID: CID,
|
|
21
|
+
chunks: ChunkInfo[],
|
|
20
22
|
totalSize: number,
|
|
21
|
-
name?: string,
|
|
22
|
-
mimeType?: string,
|
|
23
|
+
name?: string | null,
|
|
24
|
+
mimeType?: string | null,
|
|
23
25
|
): OffchainFileMetadata => {
|
|
24
|
-
const chunks = Array.from(dag.nodes.values()).filter(
|
|
25
|
-
(n) => n.Data && IPLDNodeData.decode(n.Data).data,
|
|
26
|
-
)
|
|
27
|
-
|
|
28
26
|
return {
|
|
29
27
|
type: 'file',
|
|
30
|
-
dataCid: cidToString(
|
|
31
|
-
name,
|
|
32
|
-
mimeType,
|
|
28
|
+
dataCid: cidToString(headCID),
|
|
29
|
+
name: name ?? undefined,
|
|
30
|
+
mimeType: mimeType ?? undefined,
|
|
33
31
|
totalSize,
|
|
34
32
|
totalChunks: chunks.length,
|
|
35
|
-
chunks
|
|
36
|
-
cid: cidToString(cidOfNode(chunk)),
|
|
37
|
-
size: chunk.Data?.length ?? 0,
|
|
38
|
-
})),
|
|
33
|
+
chunks,
|
|
39
34
|
}
|
|
40
35
|
}
|
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
import { CID } from 'multiformats'
|
|
2
|
+
import { cidOfNode, cidToString } from '../../cid/index.js'
|
|
3
|
+
import { PBNode } from '../../ipld/index.js'
|
|
4
|
+
import { IPLDNodeData, MetadataType } from '../onchain/index.js'
|
|
5
|
+
|
|
1
6
|
interface ChildrenMetadata {
|
|
2
7
|
type: 'folder' | 'file'
|
|
3
8
|
name?: string
|
|
@@ -14,17 +19,33 @@ export type OffchainFolderMetadata = {
|
|
|
14
19
|
children: ChildrenMetadata[]
|
|
15
20
|
}
|
|
16
21
|
|
|
22
|
+
export const childrenMetadataFromNode = (node: PBNode): ChildrenMetadata => {
|
|
23
|
+
const ipldData = IPLDNodeData.decode(node.Data!)
|
|
24
|
+
if (ipldData.type !== MetadataType.File && ipldData.type !== MetadataType.Folder) {
|
|
25
|
+
throw new Error('Invalid metadata type')
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return {
|
|
29
|
+
type: ipldData.type === MetadataType.File ? 'file' : 'folder',
|
|
30
|
+
cid: cidToString(cidOfNode(node)),
|
|
31
|
+
totalSize: ipldData.size ?? 0,
|
|
32
|
+
name: ipldData.name,
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
17
36
|
export const folderMetadata = (
|
|
18
|
-
cid: string,
|
|
37
|
+
cid: CID | string,
|
|
19
38
|
children: ChildrenMetadata[],
|
|
20
|
-
name?: string,
|
|
39
|
+
name?: string | null,
|
|
21
40
|
): OffchainFolderMetadata => {
|
|
41
|
+
cid = typeof cid === 'string' ? cid : cidToString(cid)
|
|
42
|
+
|
|
22
43
|
return {
|
|
23
44
|
dataCid: cid,
|
|
24
45
|
totalSize: children.reduce((acc, child) => acc + child.totalSize, 0),
|
|
25
46
|
totalFiles: children.length,
|
|
26
47
|
children,
|
|
27
48
|
type: 'folder',
|
|
28
|
-
name,
|
|
49
|
+
name: name ?? undefined,
|
|
29
50
|
}
|
|
30
51
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { decodeNode } from '../../ipld/index.js'
|
|
2
2
|
import { IPLDNodeData } from '../onchain/index.js'
|
|
3
3
|
|
|
4
4
|
export const encodeIPLDNodeData = (metadata: IPLDNodeData): Uint8Array => {
|
|
@@ -6,7 +6,7 @@ export const encodeIPLDNodeData = (metadata: IPLDNodeData): Uint8Array => {
|
|
|
6
6
|
}
|
|
7
7
|
|
|
8
8
|
export const decodeIPLDNodeData = (data: Uint8Array): IPLDNodeData => {
|
|
9
|
-
const decoded =
|
|
9
|
+
const decoded = decodeNode(data)
|
|
10
10
|
if (!decoded.Data) {
|
|
11
11
|
throw new Error('Invalid data')
|
|
12
12
|
}
|