@autonomys/auto-drive 0.5.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/README.md +1 -0
- package/dist/cid/index.d.ts +9 -0
- package/dist/cid/index.d.ts.map +1 -0
- package/dist/cid/index.js +20 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3 -0
- package/dist/ipld/chunker.d.ts +17 -0
- package/dist/ipld/chunker.d.ts.map +1 -0
- package/dist/ipld/chunker.js +77 -0
- package/dist/ipld/index.d.ts +4 -0
- package/dist/ipld/index.d.ts.map +1 -0
- package/dist/ipld/index.js +3 -0
- package/dist/ipld/nodes.d.ts +11 -0
- package/dist/ipld/nodes.d.ts.map +1 -0
- package/dist/ipld/nodes.js +60 -0
- package/dist/ipld/utils.d.ts +5 -0
- package/dist/ipld/utils.d.ts.map +1 -0
- package/dist/ipld/utils.js +10 -0
- package/dist/metadata/index.d.ts +3 -0
- package/dist/metadata/index.d.ts.map +1 -0
- package/dist/metadata/index.js +2 -0
- package/dist/metadata/offchain/base.d.ts +4 -0
- package/dist/metadata/offchain/base.d.ts.map +1 -0
- package/dist/metadata/offchain/base.js +1 -0
- package/dist/metadata/offchain/file.d.ts +16 -0
- package/dist/metadata/offchain/file.d.ts.map +1 -0
- package/dist/metadata/offchain/file.js +18 -0
- package/dist/metadata/offchain/folder.d.ts +17 -0
- package/dist/metadata/offchain/folder.d.ts.map +1 -0
- package/dist/metadata/offchain/folder.js +9 -0
- package/dist/metadata/offchain/index.d.ts +4 -0
- package/dist/metadata/offchain/index.d.ts.map +1 -0
- package/dist/metadata/offchain/index.js +3 -0
- package/dist/metadata/onchain/index.d.ts +3 -0
- package/dist/metadata/onchain/index.d.ts.map +1 -0
- package/dist/metadata/onchain/index.js +2 -0
- package/dist/metadata/onchain/protobuf/OnchainMetadat.d.ts +26 -0
- package/dist/metadata/onchain/protobuf/OnchainMetadat.d.ts.map +1 -0
- package/dist/metadata/onchain/protobuf/OnchainMetadat.js +108 -0
- package/dist/metadata/onchain/protobuf/onchainMetadata.d.ts +26 -0
- package/dist/metadata/onchain/protobuf/onchainMetadata.d.ts.map +1 -0
- package/dist/metadata/onchain/protobuf/onchainMetadata.js +108 -0
- package/dist/metadata/onchain/utils.d.ts +4 -0
- package/dist/metadata/onchain/utils.d.ts.map +1 -0
- package/dist/metadata/onchain/utils.js +12 -0
- package/jest.config.ts +17 -0
- package/package.json +42 -0
- package/src/cid/index.ts +26 -0
- package/src/index.ts +3 -0
- package/src/ipld/chunker.ts +117 -0
- package/src/ipld/index.ts +3 -0
- package/src/ipld/nodes.ts +134 -0
- package/src/ipld/utils.ts +15 -0
- package/src/metadata/index.ts +2 -0
- package/src/metadata/offchain/base.ts +4 -0
- package/src/metadata/offchain/file.ts +36 -0
- package/src/metadata/offchain/folder.ts +28 -0
- package/src/metadata/offchain/index.ts +3 -0
- package/src/metadata/onchain/index.ts +2 -0
- package/src/metadata/onchain/protobuf/OnchainMetadata.proto +19 -0
- package/src/metadata/onchain/protobuf/OnchainMetadata.ts +133 -0
- package/src/metadata/onchain/utils.ts +15 -0
- package/tests/chunker.spec.ts +157 -0
- package/tests/cid.spec.ts +20 -0
- package/tests/nodes.spec.ts +70 -0
- package/tsconfig.json +14 -0
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/* eslint-disable import/export */
|
|
2
|
+
/* eslint-disable complexity */
|
|
3
|
+
/* eslint-disable @typescript-eslint/no-namespace */
|
|
4
|
+
/* eslint-disable @typescript-eslint/no-unnecessary-boolean-literal-compare */
|
|
5
|
+
/* eslint-disable @typescript-eslint/no-empty-interface */
|
|
6
|
+
import { decodeMessage, encodeMessage, enumeration, message } from 'protons-runtime';
|
|
7
|
+
export var IPLDNodeData;
|
|
8
|
+
(function (IPLDNodeData) {
|
|
9
|
+
let _codec;
|
|
10
|
+
IPLDNodeData.codec = () => {
|
|
11
|
+
if (_codec == null) {
|
|
12
|
+
_codec = message((obj, w, opts = {}) => {
|
|
13
|
+
if (opts.lengthDelimited !== false) {
|
|
14
|
+
w.fork();
|
|
15
|
+
}
|
|
16
|
+
if (obj.type != null && __MetadataTypeValues[obj.type] !== 0) {
|
|
17
|
+
w.uint32(8);
|
|
18
|
+
MetadataType.codec().encode(obj.type, w);
|
|
19
|
+
}
|
|
20
|
+
if ((obj.linkDepth != null && obj.linkDepth !== 0)) {
|
|
21
|
+
w.uint32(16);
|
|
22
|
+
w.int32(obj.linkDepth);
|
|
23
|
+
}
|
|
24
|
+
if (obj.size != null) {
|
|
25
|
+
w.uint32(24);
|
|
26
|
+
w.int32(obj.size);
|
|
27
|
+
}
|
|
28
|
+
if (obj.name != null) {
|
|
29
|
+
w.uint32(34);
|
|
30
|
+
w.string(obj.name);
|
|
31
|
+
}
|
|
32
|
+
if (obj.data != null) {
|
|
33
|
+
w.uint32(42);
|
|
34
|
+
w.bytes(obj.data);
|
|
35
|
+
}
|
|
36
|
+
if (opts.lengthDelimited !== false) {
|
|
37
|
+
w.ldelim();
|
|
38
|
+
}
|
|
39
|
+
}, (reader, length, opts = {}) => {
|
|
40
|
+
const obj = {
|
|
41
|
+
type: MetadataType.File,
|
|
42
|
+
linkDepth: 0
|
|
43
|
+
};
|
|
44
|
+
const end = length == null ? reader.len : reader.pos + length;
|
|
45
|
+
while (reader.pos < end) {
|
|
46
|
+
const tag = reader.uint32();
|
|
47
|
+
switch (tag >>> 3) {
|
|
48
|
+
case 1: {
|
|
49
|
+
obj.type = MetadataType.codec().decode(reader);
|
|
50
|
+
break;
|
|
51
|
+
}
|
|
52
|
+
case 2: {
|
|
53
|
+
obj.linkDepth = reader.int32();
|
|
54
|
+
break;
|
|
55
|
+
}
|
|
56
|
+
case 3: {
|
|
57
|
+
obj.size = reader.int32();
|
|
58
|
+
break;
|
|
59
|
+
}
|
|
60
|
+
case 4: {
|
|
61
|
+
obj.name = reader.string();
|
|
62
|
+
break;
|
|
63
|
+
}
|
|
64
|
+
case 5: {
|
|
65
|
+
obj.data = reader.bytes();
|
|
66
|
+
break;
|
|
67
|
+
}
|
|
68
|
+
default: {
|
|
69
|
+
reader.skipType(tag & 7);
|
|
70
|
+
break;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return obj;
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
return _codec;
|
|
78
|
+
};
|
|
79
|
+
IPLDNodeData.encode = (obj) => {
|
|
80
|
+
return encodeMessage(obj, IPLDNodeData.codec());
|
|
81
|
+
};
|
|
82
|
+
IPLDNodeData.decode = (buf, opts) => {
|
|
83
|
+
return decodeMessage(buf, IPLDNodeData.codec(), opts);
|
|
84
|
+
};
|
|
85
|
+
})(IPLDNodeData || (IPLDNodeData = {}));
|
|
86
|
+
export var MetadataType;
|
|
87
|
+
(function (MetadataType) {
|
|
88
|
+
MetadataType["File"] = "File";
|
|
89
|
+
MetadataType["FileInlink"] = "FileInlink";
|
|
90
|
+
MetadataType["FileChunk"] = "FileChunk";
|
|
91
|
+
MetadataType["Folder"] = "Folder";
|
|
92
|
+
MetadataType["FolderInlink"] = "FolderInlink";
|
|
93
|
+
MetadataType["Metadata"] = "Metadata";
|
|
94
|
+
})(MetadataType || (MetadataType = {}));
|
|
95
|
+
var __MetadataTypeValues;
|
|
96
|
+
(function (__MetadataTypeValues) {
|
|
97
|
+
__MetadataTypeValues[__MetadataTypeValues["File"] = 0] = "File";
|
|
98
|
+
__MetadataTypeValues[__MetadataTypeValues["FileInlink"] = 1] = "FileInlink";
|
|
99
|
+
__MetadataTypeValues[__MetadataTypeValues["FileChunk"] = 2] = "FileChunk";
|
|
100
|
+
__MetadataTypeValues[__MetadataTypeValues["Folder"] = 3] = "Folder";
|
|
101
|
+
__MetadataTypeValues[__MetadataTypeValues["FolderInlink"] = 4] = "FolderInlink";
|
|
102
|
+
__MetadataTypeValues[__MetadataTypeValues["Metadata"] = 5] = "Metadata";
|
|
103
|
+
})(__MetadataTypeValues || (__MetadataTypeValues = {}));
|
|
104
|
+
(function (MetadataType) {
|
|
105
|
+
MetadataType.codec = () => {
|
|
106
|
+
return enumeration(__MetadataTypeValues);
|
|
107
|
+
};
|
|
108
|
+
})(MetadataType || (MetadataType = {}));
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../src/metadata/onchain/utils.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AAElD,eAAO,MAAM,kBAAkB,aAAc,YAAY,KAAG,UAE3D,CAAA;AAED,eAAO,MAAM,kBAAkB,SAAU,UAAU,KAAG,YAOrD,CAAA"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { decode } from '@ipld/dag-pb';
|
|
2
|
+
import { IPLDNodeData } from '../onchain/index.js';
|
|
3
|
+
export const encodeIPLDNodeData = (metadata) => {
|
|
4
|
+
return IPLDNodeData.encode(metadata);
|
|
5
|
+
};
|
|
6
|
+
export const decodeIPLDNodeData = (data) => {
|
|
7
|
+
const decoded = decode(data);
|
|
8
|
+
if (!decoded.Data) {
|
|
9
|
+
throw new Error('Invalid data');
|
|
10
|
+
}
|
|
11
|
+
return IPLDNodeData.decode(decoded.Data);
|
|
12
|
+
};
|
package/jest.config.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
const { createDefaultEsmPreset } = require('ts-jest')
|
|
2
|
+
|
|
3
|
+
module.exports = {
|
|
4
|
+
...createDefaultEsmPreset(),
|
|
5
|
+
extensionsToTreatAsEsm: ['.ts'],
|
|
6
|
+
moduleNameMapper: {
|
|
7
|
+
'^(\\.{1,2}/.*)\\.js$': '$1',
|
|
8
|
+
},
|
|
9
|
+
transform: {
|
|
10
|
+
'^.+\\.tsx?$': [
|
|
11
|
+
'ts-jest',
|
|
12
|
+
{
|
|
13
|
+
useESM: true,
|
|
14
|
+
},
|
|
15
|
+
],
|
|
16
|
+
},
|
|
17
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@autonomys/auto-drive",
|
|
3
|
+
"packageManager": "yarn@4.1.1",
|
|
4
|
+
"version": "0.5.0",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "https://github.com/autonomys/auto-sdk"
|
|
9
|
+
},
|
|
10
|
+
"author": {
|
|
11
|
+
"name": "Autonomys",
|
|
12
|
+
"url": "https://www.autonomys.net"
|
|
13
|
+
},
|
|
14
|
+
"type": "module",
|
|
15
|
+
"scripts": {
|
|
16
|
+
"build": "tsc",
|
|
17
|
+
"pb": "yarn protons src/metadata/onchain/protobuf/OnchainMetadata.proto -o src/metadata/onchain/protobuf",
|
|
18
|
+
"clean": "rm -rf dist",
|
|
19
|
+
"format": "prettier --write \"src/**/*.ts\"",
|
|
20
|
+
"test": "yarn node --experimental-vm-modules $(yarn bin jest)"
|
|
21
|
+
},
|
|
22
|
+
"exports": {
|
|
23
|
+
".": "./dist/index.js",
|
|
24
|
+
"./protobuf": "./dist/metadata/onchain/protobuf/onchainMetadata.js"
|
|
25
|
+
},
|
|
26
|
+
"devDependencies": {
|
|
27
|
+
"@types/jest": "^29.5.13",
|
|
28
|
+
"jest": "^29.7.0",
|
|
29
|
+
"protobufjs": "^7.4.0",
|
|
30
|
+
"ts-jest": "^29.2.5",
|
|
31
|
+
"typescript": "^5.6.2"
|
|
32
|
+
},
|
|
33
|
+
"dependencies": {
|
|
34
|
+
"@ipld/dag-pb": "^4.1.2",
|
|
35
|
+
"blake3": "1.1.0",
|
|
36
|
+
"multiformats": "^13.2.2",
|
|
37
|
+
"protobufjs": "^7.4.0",
|
|
38
|
+
"protons": "^7.6.0",
|
|
39
|
+
"protons-runtime": "^5.5.0"
|
|
40
|
+
},
|
|
41
|
+
"gitHead": "b7b40dfa6b9cb8bf189fd9d5d3a0ad190b21197a"
|
|
42
|
+
}
|
package/src/cid/index.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { encode, PBNode } from '@ipld/dag-pb'
|
|
2
|
+
import { hash } from 'blake3'
|
|
3
|
+
import { CID } from 'multiformats/cid'
|
|
4
|
+
import * as base32 from 'multiformats/bases/base32'
|
|
5
|
+
import * as raw from 'multiformats/codecs/raw'
|
|
6
|
+
import { create } from 'multiformats/hashes/digest'
|
|
7
|
+
|
|
8
|
+
export const BLAKE3_CODE = 0x1f
|
|
9
|
+
|
|
10
|
+
export const cidOfNode = (node: PBNode) => {
|
|
11
|
+
return cidFromBlakeHash(hash(encode(node)))
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const cidToString = (cid: CID) => {
|
|
15
|
+
return cid.toString(base32.base32)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export const stringToCid = (str: string) => {
|
|
19
|
+
return CID.parse(str, base32.base32)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export const cidFromBlakeHash = (hash: Buffer) => {
|
|
23
|
+
return CID.create(1, raw.code, create(BLAKE3_CODE, hash))
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export const blake3HashFromCid = (cid: CID) => cid.multihash.digest
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { PBNode } from '@ipld/dag-pb'
|
|
2
|
+
import { CID } from 'multiformats'
|
|
3
|
+
import { cidOfNode } from '../cid/index.js'
|
|
4
|
+
import {
|
|
5
|
+
createChunkedFileIpldNode,
|
|
6
|
+
createChunkIpldNode,
|
|
7
|
+
createFileInlinkIpldNode,
|
|
8
|
+
createFolderInlinkIpldNode,
|
|
9
|
+
createFolderIpldNode,
|
|
10
|
+
createSingleFileIpldNode,
|
|
11
|
+
} from './nodes.js'
|
|
12
|
+
import { chunkBuffer, encodeNode } from './utils.js'
|
|
13
|
+
|
|
14
|
+
export const DEFAULT_MAX_CHUNK_SIZE = 1024 * 64
|
|
15
|
+
export const DEFAULT_MAX_LINK_PER_NODE = DEFAULT_MAX_CHUNK_SIZE / 64
|
|
16
|
+
|
|
17
|
+
export interface IPLDDag {
|
|
18
|
+
headCID: CID
|
|
19
|
+
nodes: Map<CID, PBNode>
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export const createFileIPLDDag = (
|
|
23
|
+
file: Buffer,
|
|
24
|
+
filename?: string,
|
|
25
|
+
{ chunkSize, maxLinkPerNode }: { chunkSize: number; maxLinkPerNode: number } = {
|
|
26
|
+
chunkSize: DEFAULT_MAX_CHUNK_SIZE,
|
|
27
|
+
maxLinkPerNode: DEFAULT_MAX_LINK_PER_NODE,
|
|
28
|
+
},
|
|
29
|
+
): IPLDDag => {
|
|
30
|
+
if (file.length <= chunkSize) {
|
|
31
|
+
const head = createSingleFileIpldNode(file, filename)
|
|
32
|
+
const headCID = cidOfNode(head)
|
|
33
|
+
return {
|
|
34
|
+
headCID,
|
|
35
|
+
nodes: new Map([[headCID, head]]),
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const bufferChunks = chunkBuffer(file, chunkSize)
|
|
40
|
+
|
|
41
|
+
const nodes = new Map<CID, PBNode>()
|
|
42
|
+
|
|
43
|
+
let CIDs: CID[] = bufferChunks.map((chunk) => {
|
|
44
|
+
const node = createChunkIpldNode(chunk)
|
|
45
|
+
const cid = cidOfNode(node)
|
|
46
|
+
nodes.set(cid, node)
|
|
47
|
+
|
|
48
|
+
return cid
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
let depth = 1
|
|
52
|
+
while (CIDs.length > maxLinkPerNode) {
|
|
53
|
+
const newCIDs: CID[] = []
|
|
54
|
+
for (let i = 0; i < CIDs.length; i += maxLinkPerNode) {
|
|
55
|
+
const chunk = CIDs.slice(i, i + maxLinkPerNode)
|
|
56
|
+
|
|
57
|
+
const node = createFileInlinkIpldNode(chunk, chunk.length, depth, chunkSize)
|
|
58
|
+
const cid = cidOfNode(node)
|
|
59
|
+
nodes.set(cid, node)
|
|
60
|
+
newCIDs.push(cid)
|
|
61
|
+
}
|
|
62
|
+
depth++
|
|
63
|
+
CIDs = newCIDs
|
|
64
|
+
}
|
|
65
|
+
const head = createChunkedFileIpldNode(CIDs, file.length, depth, filename, chunkSize)
|
|
66
|
+
const headCID = cidOfNode(head)
|
|
67
|
+
nodes.set(headCID, head)
|
|
68
|
+
|
|
69
|
+
return {
|
|
70
|
+
headCID,
|
|
71
|
+
nodes,
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export const createFolderIPLDDag = (
|
|
76
|
+
children: CID[],
|
|
77
|
+
name: string,
|
|
78
|
+
size: number,
|
|
79
|
+
{ maxLinkPerNode }: { maxLinkPerNode: number } = { maxLinkPerNode: DEFAULT_MAX_LINK_PER_NODE },
|
|
80
|
+
): IPLDDag => {
|
|
81
|
+
const nodes = new Map<CID, PBNode>()
|
|
82
|
+
let cids = children
|
|
83
|
+
let depth = 0
|
|
84
|
+
while (cids.length > maxLinkPerNode) {
|
|
85
|
+
const newCIDs: CID[] = []
|
|
86
|
+
for (let i = 0; i < cids.length; i += maxLinkPerNode) {
|
|
87
|
+
const chunk = cids.slice(i, i + maxLinkPerNode)
|
|
88
|
+
const node = createFolderInlinkIpldNode(chunk, depth)
|
|
89
|
+
const cid = cidOfNode(node)
|
|
90
|
+
nodes.set(cid, node)
|
|
91
|
+
newCIDs.push(cid)
|
|
92
|
+
}
|
|
93
|
+
cids = newCIDs
|
|
94
|
+
depth++
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const node = createFolderIpldNode(cids, name, depth, size)
|
|
98
|
+
const cid = cidOfNode(node)
|
|
99
|
+
nodes.set(cid, node)
|
|
100
|
+
|
|
101
|
+
return {
|
|
102
|
+
headCID: cid,
|
|
103
|
+
nodes,
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export const ensureNodeMaxSize = (
|
|
108
|
+
node: PBNode,
|
|
109
|
+
maxSize: number = DEFAULT_MAX_CHUNK_SIZE,
|
|
110
|
+
): PBNode => {
|
|
111
|
+
const nodeSize = encodeNode(node).byteLength
|
|
112
|
+
if (nodeSize > maxSize) {
|
|
113
|
+
throw new Error(`Node is too large to fit in a single chunk: ${nodeSize} > ${maxSize}`)
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return node
|
|
117
|
+
}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { createNode, PBNode } from '@ipld/dag-pb'
|
|
2
|
+
import { CID } from 'multiformats/cid'
|
|
3
|
+
import { OffchainMetadata } from '../metadata/index.js'
|
|
4
|
+
import { encodeIPLDNodeData, MetadataType } from '../metadata/onchain/index.js'
|
|
5
|
+
import { DEFAULT_MAX_CHUNK_SIZE, ensureNodeMaxSize } from './chunker.js'
|
|
6
|
+
|
|
7
|
+
/// Creates a chunk ipld node
|
|
8
|
+
export const createChunkIpldNode = (data: Buffer): PBNode =>
|
|
9
|
+
createNode(
|
|
10
|
+
encodeIPLDNodeData({
|
|
11
|
+
type: MetadataType.FileChunk,
|
|
12
|
+
size: data.length,
|
|
13
|
+
linkDepth: 0,
|
|
14
|
+
data,
|
|
15
|
+
}),
|
|
16
|
+
[],
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
// Creates a file ipld node
|
|
20
|
+
// links: the CIDs of the file's contents
|
|
21
|
+
// @todo: add the file's metadata
|
|
22
|
+
export const createChunkedFileIpldNode = (
|
|
23
|
+
links: CID[],
|
|
24
|
+
size: number,
|
|
25
|
+
linkDepth: number,
|
|
26
|
+
name?: string,
|
|
27
|
+
maxNodeSize: number = DEFAULT_MAX_CHUNK_SIZE,
|
|
28
|
+
): PBNode =>
|
|
29
|
+
ensureNodeMaxSize(
|
|
30
|
+
createNode(
|
|
31
|
+
encodeIPLDNodeData({
|
|
32
|
+
type: MetadataType.File,
|
|
33
|
+
name,
|
|
34
|
+
size,
|
|
35
|
+
linkDepth,
|
|
36
|
+
}),
|
|
37
|
+
links.map((cid) => ({ Hash: cid })),
|
|
38
|
+
),
|
|
39
|
+
maxNodeSize,
|
|
40
|
+
)
|
|
41
|
+
// Creates a file ipld node
|
|
42
|
+
// links: the CIDs of the file's contents
|
|
43
|
+
// @todo: add the file's metadata
|
|
44
|
+
export const createFileInlinkIpldNode = (
|
|
45
|
+
links: CID[],
|
|
46
|
+
size: number,
|
|
47
|
+
linkDepth: number,
|
|
48
|
+
maxNodeSize: number = DEFAULT_MAX_CHUNK_SIZE,
|
|
49
|
+
): PBNode =>
|
|
50
|
+
ensureNodeMaxSize(
|
|
51
|
+
createNode(
|
|
52
|
+
encodeIPLDNodeData({
|
|
53
|
+
type: MetadataType.FileInlink,
|
|
54
|
+
size,
|
|
55
|
+
linkDepth,
|
|
56
|
+
}),
|
|
57
|
+
links.map((cid) => ({ Hash: cid })),
|
|
58
|
+
),
|
|
59
|
+
maxNodeSize,
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
// Creates a file ipld node
|
|
63
|
+
// links: the CIDs of the file's contents
|
|
64
|
+
// @todo: add the file's metadata
|
|
65
|
+
export const createSingleFileIpldNode = (data: Buffer, name?: string): PBNode =>
|
|
66
|
+
createNode(
|
|
67
|
+
encodeIPLDNodeData({
|
|
68
|
+
type: MetadataType.File,
|
|
69
|
+
name,
|
|
70
|
+
size: data.length,
|
|
71
|
+
linkDepth: 0,
|
|
72
|
+
data,
|
|
73
|
+
}),
|
|
74
|
+
[],
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
// Creates a folder ipld node
|
|
78
|
+
// links: the CIDs of the folder's contents
|
|
79
|
+
// @todo: add the folder's metadata
|
|
80
|
+
export const createFolderIpldNode = (
|
|
81
|
+
links: CID[],
|
|
82
|
+
name: string,
|
|
83
|
+
linkDepth: number,
|
|
84
|
+
size: number,
|
|
85
|
+
maxNodeSize: number = DEFAULT_MAX_CHUNK_SIZE,
|
|
86
|
+
): PBNode =>
|
|
87
|
+
ensureNodeMaxSize(
|
|
88
|
+
createNode(
|
|
89
|
+
encodeIPLDNodeData({
|
|
90
|
+
type: MetadataType.Folder,
|
|
91
|
+
name,
|
|
92
|
+
size,
|
|
93
|
+
linkDepth,
|
|
94
|
+
}),
|
|
95
|
+
links.map((cid) => ({ Hash: cid })),
|
|
96
|
+
),
|
|
97
|
+
maxNodeSize,
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
export const createFolderInlinkIpldNode = (
|
|
101
|
+
links: CID[],
|
|
102
|
+
linkDepth: number,
|
|
103
|
+
maxNodeSize: number = DEFAULT_MAX_CHUNK_SIZE,
|
|
104
|
+
): PBNode =>
|
|
105
|
+
ensureNodeMaxSize(
|
|
106
|
+
createNode(
|
|
107
|
+
encodeIPLDNodeData({
|
|
108
|
+
type: MetadataType.FolderInlink,
|
|
109
|
+
linkDepth,
|
|
110
|
+
}),
|
|
111
|
+
links.map((cid) => ({ Hash: cid })),
|
|
112
|
+
),
|
|
113
|
+
maxNodeSize,
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
/// Creates a metadata ipld node
|
|
117
|
+
export const createMetadataNode = (
|
|
118
|
+
metadata: OffchainMetadata,
|
|
119
|
+
maxNodeSize: number = DEFAULT_MAX_CHUNK_SIZE,
|
|
120
|
+
): PBNode => {
|
|
121
|
+
const data = Buffer.from(JSON.stringify(metadata))
|
|
122
|
+
|
|
123
|
+
return ensureNodeMaxSize(
|
|
124
|
+
createNode(
|
|
125
|
+
encodeIPLDNodeData({
|
|
126
|
+
type: MetadataType.Metadata,
|
|
127
|
+
name: metadata.name,
|
|
128
|
+
linkDepth: 0,
|
|
129
|
+
data,
|
|
130
|
+
}),
|
|
131
|
+
),
|
|
132
|
+
maxNodeSize,
|
|
133
|
+
)
|
|
134
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { decode, encode, PBNode } from '@ipld/dag-pb'
|
|
2
|
+
|
|
3
|
+
export const chunkBuffer = (buffer: Buffer, chunkSize: number) => {
|
|
4
|
+
const chunks: Buffer[] = []
|
|
5
|
+
|
|
6
|
+
for (let i = 0; i < buffer.length; i += chunkSize) {
|
|
7
|
+
chunks.push(Buffer.from(buffer.buffer.slice(i, i + chunkSize)))
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
return chunks
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export const encodeNode = (node: PBNode): Buffer => Buffer.from(encode(node))
|
|
14
|
+
|
|
15
|
+
export const decodeNode = (data: Uint8Array): PBNode => decode(data)
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { cidOfNode, cidToString, IPLDDag } from '../../index.js'
|
|
2
|
+
|
|
3
|
+
export type OffchainFileMetadata = {
|
|
4
|
+
type: 'file'
|
|
5
|
+
dataCid: string
|
|
6
|
+
name?: string
|
|
7
|
+
mimeType?: string
|
|
8
|
+
totalSize: number
|
|
9
|
+
totalChunks: number
|
|
10
|
+
chunks: ChunkInfo[]
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface ChunkInfo {
|
|
14
|
+
size: number
|
|
15
|
+
cid: string
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export const fileMetadata = (
|
|
19
|
+
dag: IPLDDag,
|
|
20
|
+
totalSize: number,
|
|
21
|
+
name?: string,
|
|
22
|
+
mimeType?: string,
|
|
23
|
+
): OffchainFileMetadata => {
|
|
24
|
+
return {
|
|
25
|
+
type: 'file',
|
|
26
|
+
dataCid: cidToString(dag.headCID),
|
|
27
|
+
name,
|
|
28
|
+
mimeType,
|
|
29
|
+
totalSize,
|
|
30
|
+
totalChunks: dag.nodes.size,
|
|
31
|
+
chunks: Array.from(dag.nodes.values()).map((chunk) => ({
|
|
32
|
+
cid: cidToString(cidOfNode(chunk)),
|
|
33
|
+
size: chunk.Data?.length ?? 0,
|
|
34
|
+
})),
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
interface ChildrenMetadata {
|
|
2
|
+
type: 'folder' | 'file'
|
|
3
|
+
name?: string
|
|
4
|
+
cid: string
|
|
5
|
+
totalSize: number
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export type OffchainFolderMetadata = {
|
|
9
|
+
type: 'folder'
|
|
10
|
+
dataCid: string
|
|
11
|
+
name?: string
|
|
12
|
+
totalSize: number
|
|
13
|
+
totalFiles: number
|
|
14
|
+
children: ChildrenMetadata[]
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export const folderMetadata = (
|
|
18
|
+
cid: string,
|
|
19
|
+
children: ChildrenMetadata[],
|
|
20
|
+
): OffchainFolderMetadata => {
|
|
21
|
+
return {
|
|
22
|
+
dataCid: cid,
|
|
23
|
+
totalSize: children.reduce((acc, child) => acc + child.totalSize, 0),
|
|
24
|
+
totalFiles: children.length,
|
|
25
|
+
children,
|
|
26
|
+
type: 'folder',
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
syntax = "proto3";
|
|
2
|
+
|
|
3
|
+
message IPLDNodeData {
|
|
4
|
+
MetadataType type = 1;
|
|
5
|
+
int32 linkDepth = 2;
|
|
6
|
+
optional int32 size = 3;
|
|
7
|
+
optional string name = 4;
|
|
8
|
+
optional bytes data = 5;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
// MetadataType defines the possible types of metadata.
|
|
12
|
+
enum MetadataType {
|
|
13
|
+
File = 0;
|
|
14
|
+
FileInlink = 1;
|
|
15
|
+
FileChunk = 2;
|
|
16
|
+
Folder = 3;
|
|
17
|
+
FolderInlink = 4;
|
|
18
|
+
Metadata = 5;
|
|
19
|
+
}
|