@nosana/kit 0.1.6 → 0.1.7
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/config/types.d.ts +2 -0
- package/dist/config/utils.js +9 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/ipfs/IPFS.d.ts +33 -2
- package/dist/ipfs/IPFS.js +107 -5
- package/package.json +3 -1
package/dist/config/types.d.ts
CHANGED
package/dist/config/utils.js
CHANGED
|
@@ -11,6 +11,10 @@ export const mergeConfigs = (defaultConfig, customConfig) => {
|
|
|
11
11
|
...defaultConfig.solana,
|
|
12
12
|
...customConfig.solana,
|
|
13
13
|
},
|
|
14
|
+
ipfs: {
|
|
15
|
+
...defaultConfig.ipfs,
|
|
16
|
+
...customConfig.ipfs,
|
|
17
|
+
},
|
|
14
18
|
};
|
|
15
19
|
};
|
|
16
20
|
export const getNosanaConfig = (network = NosanaNetwork.MAINNET, config) => {
|
|
@@ -27,4 +31,9 @@ export const getNosanaConfig = (network = NosanaNetwork.MAINNET, config) => {
|
|
|
27
31
|
// solana: {
|
|
28
32
|
// rpcEndpoint: 'your-custom-rpc-endpoint-url',
|
|
29
33
|
// },
|
|
34
|
+
// ipfs: {
|
|
35
|
+
// jwt: 'your-custom-jwt-token',
|
|
36
|
+
// gateway: 'https://your-custom-gateway.com/ipfs/',
|
|
37
|
+
// },
|
|
38
|
+
// logLevel: NosanaLogLevel.DEBUG,
|
|
30
39
|
// });
|
package/dist/index.d.ts
CHANGED
|
@@ -2,11 +2,13 @@ import { ClientConfig, NosanaNetwork, PartialClientConfig, WalletConfig } from '
|
|
|
2
2
|
import { Logger } from './logger/Logger.js';
|
|
3
3
|
import { JobsProgram } from './programs/JobsProgram.js';
|
|
4
4
|
import { SolanaUtils } from './solana/SolanaUtils.js';
|
|
5
|
+
import { IPFS } from './ipfs/IPFS.js';
|
|
5
6
|
import { KeyPairSigner } from 'gill';
|
|
6
7
|
export declare class NosanaClient {
|
|
7
8
|
readonly config: ClientConfig;
|
|
8
9
|
readonly jobs: JobsProgram;
|
|
9
10
|
readonly solana: SolanaUtils;
|
|
11
|
+
readonly ipfs: IPFS;
|
|
10
12
|
readonly logger: Logger;
|
|
11
13
|
wallet: KeyPairSigner | undefined;
|
|
12
14
|
constructor(network?: NosanaNetwork, customConfig?: PartialClientConfig);
|
package/dist/index.js
CHANGED
|
@@ -6,6 +6,7 @@ import { getNosanaConfig, NosanaNetwork } from './config/index.js';
|
|
|
6
6
|
import { Logger } from './logger/Logger.js';
|
|
7
7
|
import { JobsProgram } from './programs/JobsProgram.js';
|
|
8
8
|
import { SolanaUtils } from './solana/SolanaUtils.js';
|
|
9
|
+
import { IPFS } from './ipfs/IPFS.js';
|
|
9
10
|
import { createKeyPairSignerFromBytes } from 'gill';
|
|
10
11
|
import { NosanaError, ErrorCodes } from './errors/NosanaError.js';
|
|
11
12
|
import bs58 from 'bs58';
|
|
@@ -18,6 +19,7 @@ export class NosanaClient {
|
|
|
18
19
|
this.jobs = new JobsProgram(this);
|
|
19
20
|
this.logger = Logger.getInstance();
|
|
20
21
|
this.solana = new SolanaUtils(this);
|
|
22
|
+
this.ipfs = new IPFS(this.config.ipfs);
|
|
21
23
|
}
|
|
22
24
|
async setWallet(wallet) {
|
|
23
25
|
try {
|
package/dist/ipfs/IPFS.d.ts
CHANGED
|
@@ -1,19 +1,50 @@
|
|
|
1
|
+
import { AxiosRequestConfig } from 'axios';
|
|
1
2
|
import { ReadonlyUint8Array } from 'gill';
|
|
3
|
+
import type { IpfsConfig } from '../config/types.js';
|
|
2
4
|
/**
|
|
3
5
|
* Class to interact with Pinata Cloud
|
|
4
6
|
* https://www.pinata.cloud/
|
|
5
7
|
*/
|
|
6
8
|
export declare class IPFS {
|
|
9
|
+
private api;
|
|
10
|
+
config: IpfsConfig;
|
|
11
|
+
constructor(config: IpfsConfig);
|
|
7
12
|
/**
|
|
8
13
|
* Convert the ipfs bytes from a solana job to a CID
|
|
9
14
|
* It prepends the 0x1220 (18,32) to make it 34 bytes and Base58 encodes it.
|
|
10
15
|
* This result is IPFS addressable.
|
|
11
16
|
*/
|
|
12
|
-
static solHashToIpfsHash(hashArray: ReadonlyUint8Array): string | null;
|
|
17
|
+
static solHashToIpfsHash(hashArray: ReadonlyUint8Array | Array<number>): string | null;
|
|
13
18
|
/**
|
|
14
19
|
* Converts IPFS hash to byte array needed to submit results
|
|
15
20
|
* @param hash IPFS hash
|
|
16
21
|
* @returns Array<number>
|
|
17
22
|
*/
|
|
18
|
-
static IpfsHashToByteArray(hash: string):
|
|
23
|
+
static IpfsHashToByteArray(hash: string): Array<number>;
|
|
24
|
+
/**
|
|
25
|
+
* Retrieve data from IPFS using the configured gateway
|
|
26
|
+
* @param hash IPFS hash string or byte array
|
|
27
|
+
* @param options Additional axios request options
|
|
28
|
+
* @returns The retrieved data
|
|
29
|
+
*/
|
|
30
|
+
retrieve(hash: string | Array<number>, options?: AxiosRequestConfig): Promise<any>;
|
|
31
|
+
/**
|
|
32
|
+
* Function to pin data into Pinata Cloud
|
|
33
|
+
* @param data Object to pin into IPFS as JSON
|
|
34
|
+
* @returns The IPFS hash of the pinned data
|
|
35
|
+
*/
|
|
36
|
+
pin(data: object): Promise<string>;
|
|
37
|
+
/**
|
|
38
|
+
* Function to pin a file into Pinata Cloud
|
|
39
|
+
* @param filePath Path to the file to pin
|
|
40
|
+
* @returns The IPFS hash of the pinned file
|
|
41
|
+
*/
|
|
42
|
+
pinFile(filePath: string): Promise<string>;
|
|
43
|
+
/**
|
|
44
|
+
* Function to pin a file from buffer/blob into Pinata Cloud
|
|
45
|
+
* @param fileBuffer Buffer or Blob containing the file data
|
|
46
|
+
* @param fileName Name of the file
|
|
47
|
+
* @returns The IPFS hash of the pinned file
|
|
48
|
+
*/
|
|
49
|
+
pinFileFromBuffer(fileBuffer: Buffer | Blob, fileName: string): Promise<string>;
|
|
19
50
|
}
|
package/dist/ipfs/IPFS.js
CHANGED
|
@@ -1,9 +1,41 @@
|
|
|
1
1
|
import bs58 from 'bs58';
|
|
2
|
+
import axios, { AxiosHeaders } from 'axios';
|
|
3
|
+
// Import form-data dynamically for Node.js environments
|
|
4
|
+
let FormData;
|
|
5
|
+
let fs;
|
|
6
|
+
// Dynamically import Node.js-specific modules
|
|
7
|
+
const loadNodeModules = async () => {
|
|
8
|
+
if (typeof window === 'undefined') {
|
|
9
|
+
try {
|
|
10
|
+
const formDataModule = await import('form-data');
|
|
11
|
+
FormData = formDataModule.default;
|
|
12
|
+
fs = await import('fs');
|
|
13
|
+
}
|
|
14
|
+
catch (error) {
|
|
15
|
+
console.warn('Node.js modules not available for file operations');
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
else {
|
|
19
|
+
// Use browser FormData
|
|
20
|
+
FormData = window.FormData;
|
|
21
|
+
}
|
|
22
|
+
};
|
|
2
23
|
/**
|
|
3
24
|
* Class to interact with Pinata Cloud
|
|
4
25
|
* https://www.pinata.cloud/
|
|
5
26
|
*/
|
|
6
27
|
export class IPFS {
|
|
28
|
+
constructor(config) {
|
|
29
|
+
this.config = config;
|
|
30
|
+
const headers = new AxiosHeaders();
|
|
31
|
+
if (this.config.jwt) {
|
|
32
|
+
headers.set('Authorization', `Bearer ${this.config.jwt}`);
|
|
33
|
+
}
|
|
34
|
+
this.api = axios.create({
|
|
35
|
+
baseURL: this.config.api,
|
|
36
|
+
headers,
|
|
37
|
+
});
|
|
38
|
+
}
|
|
7
39
|
/**
|
|
8
40
|
* Convert the ipfs bytes from a solana job to a CID
|
|
9
41
|
* It prepends the 0x1220 (18,32) to make it 34 bytes and Base58 encodes it.
|
|
@@ -11,16 +43,17 @@ export class IPFS {
|
|
|
11
43
|
*/
|
|
12
44
|
static solHashToIpfsHash(hashArray) {
|
|
13
45
|
let finalArray;
|
|
14
|
-
|
|
46
|
+
const inputArray = Array.isArray(hashArray) ? hashArray : Array.from(hashArray);
|
|
47
|
+
if (inputArray.length === 32) {
|
|
15
48
|
// Create a new array with the prepended bytes [18, 32] + original array
|
|
16
49
|
finalArray = new Uint8Array(34);
|
|
17
50
|
finalArray[0] = 18;
|
|
18
51
|
finalArray[1] = 32;
|
|
19
|
-
finalArray.set(
|
|
52
|
+
finalArray.set(inputArray, 2);
|
|
20
53
|
}
|
|
21
54
|
else {
|
|
22
55
|
// Use the array as-is if it's not 32 bytes
|
|
23
|
-
finalArray = new Uint8Array(
|
|
56
|
+
finalArray = new Uint8Array(inputArray);
|
|
24
57
|
}
|
|
25
58
|
const hash = bs58.encode(Buffer.from(finalArray));
|
|
26
59
|
if (hash === 'QmNLei78zWmzUdbeRB3CiUfAizWUrbeeZh5K1rhAQKCh51') {
|
|
@@ -35,10 +68,79 @@ export class IPFS {
|
|
|
35
68
|
*/
|
|
36
69
|
static IpfsHashToByteArray(hash) {
|
|
37
70
|
if (hash.length === 34) {
|
|
38
|
-
return
|
|
71
|
+
return [...bs58.decode(hash).subarray(2)];
|
|
39
72
|
}
|
|
40
73
|
else {
|
|
41
|
-
return
|
|
74
|
+
return [...bs58.decode(hash)];
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Retrieve data from IPFS using the configured gateway
|
|
79
|
+
* @param hash IPFS hash string or byte array
|
|
80
|
+
* @param options Additional axios request options
|
|
81
|
+
* @returns The retrieved data
|
|
82
|
+
*/
|
|
83
|
+
async retrieve(hash, options = {}) {
|
|
84
|
+
if (typeof hash !== 'string') {
|
|
85
|
+
const convertedHash = IPFS.solHashToIpfsHash(hash);
|
|
86
|
+
if (!convertedHash) {
|
|
87
|
+
throw new Error('Invalid hash provided');
|
|
88
|
+
}
|
|
89
|
+
hash = convertedHash;
|
|
90
|
+
}
|
|
91
|
+
const response = await axios.get(this.config.gateway + hash, options);
|
|
92
|
+
return response.data;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Function to pin data into Pinata Cloud
|
|
96
|
+
* @param data Object to pin into IPFS as JSON
|
|
97
|
+
* @returns The IPFS hash of the pinned data
|
|
98
|
+
*/
|
|
99
|
+
async pin(data) {
|
|
100
|
+
const response = await this.api.post('/pinning/pinJSONToIPFS', data);
|
|
101
|
+
return response.data.IpfsHash;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Function to pin a file into Pinata Cloud
|
|
105
|
+
* @param filePath Path to the file to pin
|
|
106
|
+
* @returns The IPFS hash of the pinned file
|
|
107
|
+
*/
|
|
108
|
+
async pinFile(filePath) {
|
|
109
|
+
// Ensure Node.js modules are loaded
|
|
110
|
+
await loadNodeModules();
|
|
111
|
+
if (!FormData || !fs) {
|
|
112
|
+
throw new Error('File operations are not supported in this environment');
|
|
113
|
+
}
|
|
114
|
+
const data = new FormData();
|
|
115
|
+
data.append('file', fs.createReadStream(filePath));
|
|
116
|
+
const response = await this.api.post('/pinning/pinFileToIPFS', data, {
|
|
117
|
+
headers: {
|
|
118
|
+
'Content-Type': `multipart/form-data; boundary=${data.getBoundary()}`,
|
|
119
|
+
Authorization: `Bearer ${this.config.jwt}`,
|
|
120
|
+
},
|
|
121
|
+
});
|
|
122
|
+
return response.data.IpfsHash;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Function to pin a file from buffer/blob into Pinata Cloud
|
|
126
|
+
* @param fileBuffer Buffer or Blob containing the file data
|
|
127
|
+
* @param fileName Name of the file
|
|
128
|
+
* @returns The IPFS hash of the pinned file
|
|
129
|
+
*/
|
|
130
|
+
async pinFileFromBuffer(fileBuffer, fileName) {
|
|
131
|
+
// Ensure FormData is available
|
|
132
|
+
await loadNodeModules();
|
|
133
|
+
if (!FormData) {
|
|
134
|
+
throw new Error('FormData is not available in this environment');
|
|
42
135
|
}
|
|
136
|
+
const data = new FormData();
|
|
137
|
+
data.append('file', fileBuffer, fileName);
|
|
138
|
+
const response = await this.api.post('/pinning/pinFileToIPFS', data, {
|
|
139
|
+
headers: {
|
|
140
|
+
'Content-Type': `multipart/form-data${data.getBoundary ? `; boundary=${data.getBoundary()}` : ''}`,
|
|
141
|
+
Authorization: `Bearer ${this.config.jwt}`,
|
|
142
|
+
},
|
|
143
|
+
});
|
|
144
|
+
return response.data.IpfsHash;
|
|
43
145
|
}
|
|
44
146
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nosana/kit",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.7",
|
|
4
4
|
"description": "Nosana KIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -24,8 +24,10 @@
|
|
|
24
24
|
"license": "MIT",
|
|
25
25
|
"dependencies": {
|
|
26
26
|
"@solana-program/token": "^0.5.1",
|
|
27
|
+
"axios": "^1.6.0",
|
|
27
28
|
"bs58": "^6.0.0",
|
|
28
29
|
"buffer": "^6.0.3",
|
|
30
|
+
"form-data": "^4.0.0",
|
|
29
31
|
"gill": "^0.9.0"
|
|
30
32
|
},
|
|
31
33
|
"devDependencies": {
|