@pinata/sdk 2.0.0-beta.0 → 2.0.0-beta.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pinata/sdk",
3
- "version": "2.0.0-beta.0",
3
+ "version": "2.0.0-beta.2",
4
4
  "description": "Official SDK for the Pinata IPFS platform",
5
5
  "main": "lib/pinata-sdk.js",
6
6
  "types": "types/index.d.ts",
@@ -67,7 +67,7 @@
67
67
  "base-path-converter": "^1.0.2",
68
68
  "form-data": "^2.3.3",
69
69
  "is-ipfs": "^0.6.0",
70
- "recursive-fs": "^1.1.2"
70
+ "path": "^0.12.7"
71
71
  },
72
72
  "jest": {
73
73
  "collectCoverage": true,
@@ -14,7 +14,7 @@ export interface PinataMetadata { [key: string]: string | number | null }
14
14
  export interface PinataMetadataFilter {
15
15
  name?: string | undefined;
16
16
  keyvalues: { [key: string]: {
17
- value: string;
17
+ value: string | number | null;
18
18
  op: string;
19
19
  } }
20
20
  }
@@ -2,9 +2,13 @@ import axios from 'axios';
2
2
  import { baseUrl } from '../../constants';
3
3
  import NodeFormData from 'form-data';
4
4
  import stream from 'stream';
5
- import {createConfigForAxiosHeadersWithFormData, validateMetadata, validatePinataOptions} from '../../util/validators';
5
+ import {
6
+ createConfigForAxiosHeadersWithFormData,
7
+ validateMetadata,
8
+ validatePinataOptions
9
+ } from '../../util/validators';
6
10
  import { handleError } from '../../util/errorResponse';
7
- import { PinataConfig } from '../..';
11
+ import { PinataConfig, PinataMetadata } from '../..';
8
12
 
9
13
  export interface PinataPinPolicyItem {
10
14
  id: string;
@@ -12,7 +16,7 @@ export interface PinataPinPolicyItem {
12
16
  }
13
17
  export interface PinataOptions {
14
18
  hostNodes?: string[] | undefined;
15
- cidVersion?: 0 | 1 ;
19
+ cidVersion?: 0 | 1;
16
20
  wrapWithDirectory?: boolean;
17
21
  customPinPolicy?: {
18
22
  regions: PinataPinPolicyItem[];
@@ -25,45 +29,90 @@ export interface PinataPinResponse {
25
29
  Timestamp: string;
26
30
  }
27
31
  export interface PinataPinOptions {
28
- pinataMetadata?: { [key: string]: string | number | null },
32
+ pinataMetadata?: PinataMetadata;
29
33
  pinataOptions?: PinataOptions | undefined;
30
34
  }
35
+ const endpoint = `${baseUrl}/pinning/pinFileToIPFS`;
31
36
 
32
- export default function pinFileToIPFS(config: PinataConfig, readStream: any, options?: PinataPinOptions):Promise<PinataPinResponse> {
37
+ export function uploadToIPFS(
38
+ config: PinataConfig,
39
+ data: NodeFormData,
40
+ options?: PinataPinOptions
41
+ ): Promise<PinataPinResponse> {
33
42
  return new Promise((resolve, reject) => {
43
+ if (options && options.pinataMetadata) {
44
+ validateMetadata(options.pinataMetadata);
45
+ data.append(
46
+ 'pinataMetadata',
47
+ JSON.stringify(options.pinataMetadata)
48
+ );
49
+ }
50
+ if (options && options.pinataOptions) {
51
+ validatePinataOptions(options.pinataOptions);
52
+ data.append('pinataOptions', JSON.stringify(options.pinataOptions));
53
+ }
54
+
55
+ axios
56
+ .post(
57
+ endpoint,
58
+ data,
59
+ createConfigForAxiosHeadersWithFormData(
60
+ config,
61
+ data.getBoundary()
62
+ )
63
+ )
64
+ .then(function (result) {
65
+ if (result.status !== 200) {
66
+ reject(
67
+ new Error(
68
+ `unknown server response while pinning File to IPFS: ${result}`
69
+ )
70
+ );
71
+ }
72
+ resolve(result.data);
73
+ })
74
+ .catch(function (error) {
75
+ const formattedError = handleError(error);
76
+ reject(formattedError);
77
+ });
78
+ });
79
+ }
34
80
 
81
+ export default function pinFileToIPFS(
82
+ config: PinataConfig,
83
+ readStream: any,
84
+ options?: PinataPinOptions
85
+ ): Promise<PinataPinResponse> {
86
+ return new Promise((resolve, reject) => {
35
87
  const data = new NodeFormData();
36
88
 
37
- data.append('file', readStream);
89
+ if (
90
+ !(
91
+ options?.pinataMetadata?.name &&
92
+ typeof options.pinataMetadata.name === 'string' &&
93
+ options.pinataMetadata.name.length > 0
94
+ )
95
+ ) {
96
+ throw Error(
97
+ 'filename was not provide, make sure to provide options.pinataMetadata.name'
98
+ );
99
+ }
38
100
 
39
- const endpoint = `${baseUrl}/pinning/pinFileToIPFS`;
101
+ data.append('file', readStream, {
102
+ filename: options.pinataMetadata.name
103
+ });
40
104
 
41
- if (!(readStream instanceof stream.Readable || readStream instanceof NodeFormData)) {
42
- reject(new Error('readStream is not a readable stream or form data'));
105
+ if (
106
+ !(
107
+ readStream instanceof stream.Readable ||
108
+ readStream instanceof NodeFormData
109
+ )
110
+ ) {
111
+ reject(
112
+ new Error('readStream is not a readable stream or form data')
113
+ );
43
114
  }
44
115
 
45
- if (options) {
46
- if (options.pinataMetadata) {
47
- validateMetadata(options.pinataMetadata);
48
- data.append('pinataMetadata', JSON.stringify(options.pinataMetadata));
49
- }
50
- if (options.pinataOptions) {
51
- validatePinataOptions(options.pinataOptions);
52
- data.append('pinataOptions', JSON.stringify(options.pinataOptions));
53
- }
54
- }
55
- axios.post(
56
- endpoint,
57
- readStream instanceof NodeFormData ? readStream : data,
58
- createConfigForAxiosHeadersWithFormData(config, data.getBoundary())
59
- ).then(function (result) {
60
- if (result.status !== 200) {
61
- reject(new Error(`unknown server response while pinning File to IPFS: ${result}`));
62
- }
63
- resolve(result.data);
64
- }).catch(function (error) {
65
- const formattedError = handleError(error);
66
- reject(formattedError);
67
- });
116
+ resolve(uploadToIPFS(config, data, options));
68
117
  });
69
118
  }
@@ -1,95 +1,50 @@
1
- import axios from 'axios';
2
- import { baseUrl } from '../../constants';
1
+
3
2
  import NodeFormData from 'form-data';
4
- import {createConfigForAxiosHeadersWithFormData, validateMetadata, validatePinataOptions} from '../../util/validators';
3
+
5
4
  import basePathConverter from 'base-path-converter';
6
- import { handleError } from '../../util/errorResponse';
7
5
  import { PinataConfig } from '../..';
8
- import { PinataPinOptions, PinataPinResponse } from './pinFileToIPFS';
9
- import * as fs from 'fs';
10
- import * as recursive from 'recursive-fs';
11
-
12
- export default function pinFromFS(config: PinataConfig, sourcePath: string, options?: PinataPinOptions): Promise<PinataPinResponse> {
13
-
14
- return new Promise((resolve, reject) => {
15
- const endpoint = `${baseUrl}/pinning/pinFileToIPFS`;
16
-
17
- // eslint-disable-next-line consistent-return
18
- fs.stat(sourcePath, (err: any, stats: any) => {
19
- if (err) {
20
- return reject(err);
21
- }
22
- if (stats.isFile()) {
23
- //we need to create a single read stream instead of reading the directory recursively
24
- const data = new NodeFormData();
25
-
26
- data.append('file', fs.createReadStream(sourcePath));
27
-
28
- if (options) {
29
- if (options.pinataMetadata) {
30
- validateMetadata(options.pinataMetadata);
31
- data.append('pinataMetadata', JSON.stringify(options.pinataMetadata));
32
- }
33
- if (options.pinataOptions) {
34
- validatePinataOptions(options.pinataOptions);
35
- data.append('pinataOptions', JSON.stringify(options.pinataOptions));
36
- }
37
- }
38
-
39
- axios.post(
40
- endpoint,
41
- data,
42
- createConfigForAxiosHeadersWithFormData(config, data.getBoundary()))
43
- .then(function (result) {
44
- if (result.status !== 200) {
45
- reject(new Error(`unknown server response while pinning File to IPFS: ${result}`));
46
- }
47
- resolve(result.data);
48
- }).catch(function (error) {
49
- const formattedError = handleError(error);
50
- reject(formattedError);
6
+ import {
7
+ PinataPinOptions,
8
+ PinataPinResponse,
9
+ uploadToIPFS
10
+ } from './pinFileToIPFS';
11
+ import fs from 'fs';
12
+
13
+ import path from 'path';
14
+
15
+ export default async function pinFromFS(
16
+ config: PinataConfig,
17
+ sourcePath: string,
18
+ options?: PinataPinOptions
19
+ ): Promise<PinataPinResponse> {
20
+ try {
21
+ const stats = await fs.promises.stat(sourcePath);
22
+ if (stats.isFile()) {
23
+
24
+ const stream = fs.createReadStream(sourcePath);
25
+ const data = new NodeFormData();
26
+
27
+ data.append('file', stream, {
28
+ filename: path.basename(sourcePath)
29
+ });
30
+
31
+ return uploadToIPFS(config, data, options);
32
+ }
33
+ const files = await fs.promises.readdir(sourcePath);
34
+ const data = new NodeFormData();
35
+
36
+ files.forEach((file: string) => {
37
+ //for each file stream, we need to include the correct relative file path
38
+ data.append('file', fs.createReadStream(file), {
39
+ filepath: basePathConverter(sourcePath, file)
51
40
  });
52
- } else {
53
- recursive.readdirr(sourcePath, function (err: any, dirs: any, files: any) {
54
- if (err) {
55
- reject(new Error(err));
56
- }
41
+ });
57
42
 
58
- const data = new NodeFormData();
43
+ return uploadToIPFS(config, data, options);
59
44
 
60
- files.forEach((file: any) => {
61
- //for each file stream, we need to include the correct relative file path
62
- data.append('file', fs.createReadStream(file), {
63
- filepath: basePathConverter(sourcePath, file)
64
- });
65
- });
45
+ } catch (err) {
46
+ console.log(err);
47
+ throw err;
48
+ }
66
49
 
67
- if (options) {
68
- if (options.pinataMetadata) {
69
- validateMetadata(options.pinataMetadata);
70
- data.append('pinataMetadata', JSON.stringify(options.pinataMetadata));
71
- }
72
- if (options.pinataOptions) {
73
- validatePinataOptions(options.pinataOptions);
74
- data.append('pinataOptions', JSON.stringify(options.pinataOptions));
75
- }
76
- }
77
-
78
- axios.post(
79
- endpoint,
80
- data,
81
- createConfigForAxiosHeadersWithFormData(config, data.getBoundary()))
82
- .then(function (result) {
83
- if (result.status !== 200) {
84
- reject(new Error(`unknown server response while pinning File to IPFS: ${result}`));
85
- }
86
- resolve(result.data);
87
- }).catch(function (error) {
88
- const formattedError = handleError(error);
89
- reject(formattedError);
90
- });
91
- });
92
- }
93
- });
94
- });
95
50
  }
@@ -2,6 +2,15 @@ import isIPFS from 'is-ipfs';
2
2
  import { PinataConfig } from '..';
3
3
  import { ERROR_NO_CREDENTIALS_PROVIDED} from '../constants';
4
4
 
5
+ export interface axiosHeaders
6
+ { maxContentLength: number;
7
+ maxBodyLength: number;
8
+ headers: {
9
+ [key: string]: any
10
+ };
11
+ withCredentials?: boolean;
12
+ }
13
+
5
14
  export function validateApiKeys(pinataApiKey?: string, pinataSecretApiKey?: string) {
6
15
  if (!pinataApiKey || pinataApiKey === '') {
7
16
  throw new Error(
@@ -41,15 +50,13 @@ export function createConfigForAxiosHeaders(config: PinataConfig) {
41
50
  }
42
51
 
43
52
  export function createConfigForAxiosHeadersWithFormData(config: PinataConfig, boundaryValue: string) {
44
- const requestOptions = {
53
+ const requestOptions: axiosHeaders = {
45
54
  ...createConfigForAxiosHeaders(config),
46
55
  maxContentLength: Infinity, //this is needed to prevent axios from erroring out with large files
47
- maxBodyLength: Infinity,
48
- headers: {
49
- 'Content-type': `multipart/form-data; boundary= ${boundaryValue}`
50
- }
56
+ maxBodyLength: Infinity
51
57
  };
52
58
 
59
+ requestOptions.headers['Content-type'] = `multipart/form-data; boundary=${boundaryValue}`;
53
60
  return requestOptions;
54
61
  }
55
62
 
@@ -23,7 +23,7 @@ test('200 status is returned with valid stream', () => {
23
23
  };
24
24
  (axios.post as jest.Mock).mockResolvedValue(goodStatus);
25
25
  expect.assertions(1);
26
- expect(pinFileToIPFS({ pinataApiKey: 'test', pinataSecretApiKey: 'test' }, validStream)).resolves.toEqual(goodStatus.data);
26
+ expect(pinFileToIPFS({ pinataApiKey: 'test', pinataSecretApiKey: 'test' }, validStream, {pinataMetadata: { name: 'text.txt'}})).resolves.toEqual(goodStatus.data);
27
27
  });
28
28
 
29
29
  test('200 status is returned with valid form data', () => {
@@ -33,7 +33,7 @@ test('200 status is returned with valid form data', () => {
33
33
  };
34
34
  (axios.post as jest.Mock).mockResolvedValue(goodStatus);
35
35
  expect.assertions(1);
36
- expect(pinFileToIPFS({ pinataApiKey: 'test', pinataSecretApiKey: 'test' }, validFormData)).resolves.toEqual(goodStatus.data);
36
+ expect(pinFileToIPFS({ pinataApiKey: 'test', pinataSecretApiKey: 'test' }, validFormData, {pinataMetadata: { name: 'text.txt'}})).resolves.toEqual(goodStatus.data);
37
37
  });
38
38
 
39
39
  test('Result other than 200 status is returned', () => {
@@ -42,12 +42,12 @@ test('Result other than 200 status is returned', () => {
42
42
  };
43
43
  (axios.post as jest.Mock).mockResolvedValue(badStatus);
44
44
  expect.assertions(1);
45
- expect(pinFileToIPFS({ pinataApiKey: 'test', pinataSecretApiKey: 'test' }, validStream)).rejects.toEqual(Error(`unknown server response while pinning File to IPFS: ${badStatus}`));
45
+ expect(pinFileToIPFS({ pinataApiKey: 'test', pinataSecretApiKey: 'test' }, validStream, {pinataMetadata: { name: 'text.txt'}})).rejects.toEqual(Error(`unknown server response while pinning File to IPFS: ${badStatus}`));
46
46
  });
47
47
 
48
48
  test('Rejection handled', () => {
49
49
  (axios.post as jest.Mock).mockRejectedValue('test error');
50
50
  expect.assertions(1);
51
- expect(pinFileToIPFS({ pinataApiKey: 'test', pinataSecretApiKey: 'test' }, validStream)).rejects.toEqual('test error');
51
+ expect(pinFileToIPFS({ pinataApiKey: 'test', pinataSecretApiKey: 'test' }, validStream, {pinataMetadata: { name: 'text.txt'}})).rejects.toEqual('test error');
52
52
  });
53
53
 
@@ -11,7 +11,7 @@ export interface PinataMetadataFilter {
11
11
  name?: string | undefined;
12
12
  keyvalues: {
13
13
  [key: string]: {
14
- value: string;
14
+ value: string | number | null;
15
15
  op: string;
16
16
  };
17
17
  };
@@ -1,4 +1,5 @@
1
- import { PinataConfig } from '../..';
1
+ import NodeFormData from "form-data";
2
+ import { PinataConfig, PinataMetadata } from "../..";
2
3
  export interface PinataPinPolicyItem {
3
4
  id: string;
4
5
  desiredReplicationCount: number;
@@ -17,17 +18,8 @@ export interface PinataPinResponse {
17
18
  Timestamp: string;
18
19
  }
19
20
  export interface PinataPinOptions {
20
- pinataMetadata?: {
21
- [key: string]: string | number | null;
22
- };
21
+ pinataMetadata?: PinataMetadata;
23
22
  pinataOptions?: PinataOptions | undefined;
24
23
  }
25
- /**
26
- * Pin File to IPFS
27
- * @param {string} pinataApiKey
28
- * @param {string} pinataSecretApiKey
29
- * @param {*} readStream
30
- * @param {*} options
31
- * @returns {Promise<unknown>}
32
- */
24
+ export declare function uploadToIPFS(config: PinataConfig, data: NodeFormData, options?: PinataPinOptions): Promise<PinataPinResponse>;
33
25
  export default function pinFileToIPFS(config: PinataConfig, readStream: any, options?: PinataPinOptions): Promise<PinataPinResponse>;
@@ -1,3 +1,3 @@
1
- import { PinataConfig } from '../..';
2
- import { PinataPinOptions, PinataPinResponse } from './pinFileToIPFS';
1
+ import { PinataConfig } from "../..";
2
+ import { PinataPinOptions, PinataPinResponse } from "./pinFileToIPFS";
3
3
  export default function pinFromFS(config: PinataConfig, sourcePath: string, options?: PinataPinOptions): Promise<PinataPinResponse>;
@@ -1,4 +1,12 @@
1
1
  import { PinataConfig } from '..';
2
+ export interface axiosHeaders {
3
+ maxContentLength: number;
4
+ maxBodyLength: number;
5
+ headers: {
6
+ [key: string]: any;
7
+ };
8
+ withCredentials?: boolean;
9
+ }
2
10
  export declare function validateApiKeys(pinataApiKey?: string, pinataSecretApiKey?: string): void;
3
11
  export declare function createConfigForAxiosHeaders(config: PinataConfig): {
4
12
  withCredentials: boolean;
@@ -15,21 +23,7 @@ export declare function createConfigForAxiosHeaders(config: PinataConfig): {
15
23
  };
16
24
  withCredentials?: undefined;
17
25
  };
18
- export declare function createConfigForAxiosHeadersWithFormData(config: PinataConfig, boundaryValue: string): {
19
- maxContentLength: number;
20
- maxBodyLength: number;
21
- headers: {
22
- 'Content-type': string;
23
- };
24
- withCredentials: boolean;
25
- } | {
26
- maxContentLength: number;
27
- maxBodyLength: number;
28
- headers: {
29
- 'Content-type': string;
30
- };
31
- withCredentials?: undefined;
32
- };
26
+ export declare function createConfigForAxiosHeadersWithFormData(config: PinataConfig, boundaryValue: string): axiosHeaders;
33
27
  export declare function validateHostNodes(hostNodes: any): void;
34
28
  export declare function validateMetadata(metadata: any): void;
35
29
  export declare function validatePinPolicyStructure(pinPolicy: {
@@ -1,5 +0,0 @@
1
- declare module 'recursive-fs' {
2
- export function readdirr(sourcePath: string, callback: (err: any, dirs: any, files: any) => void): void
3
-
4
- }
5
-