@taqueria/plugin-ipfs-pinata 0.7.2-rc2 → 0.8.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/index.js +46 -46
- package/index.js.map +1 -1
- package/package.json +3 -3
package/index.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import {Plugin as $
|
|
2
|
-
import $
|
|
1
|
+
import {Plugin as $khbt3$Plugin, Task as $khbt3$Task, PositionalArg as $khbt3$PositionalArg, sendJsonRes as $khbt3$sendJsonRes, sendAsyncErr as $khbt3$sendAsyncErr} from "@taqueria/node-sdk";
|
|
2
|
+
import $khbt3$path from "path";
|
|
3
3
|
import "dotenv/config";
|
|
4
|
-
import $
|
|
5
|
-
import $
|
|
6
|
-
import $
|
|
7
|
-
import $
|
|
8
|
-
import $
|
|
4
|
+
import $khbt3$fspromises from "fs/promises";
|
|
5
|
+
import $khbt3$formdata from "form-data";
|
|
6
|
+
import $khbt3$fs from "fs";
|
|
7
|
+
import $khbt3$ipfsonlyhash from "ipfs-only-hash";
|
|
8
|
+
import $khbt3$nodefetch from "node-fetch";
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
|
|
@@ -14,34 +14,34 @@ import $8CNkB$nodefetch from "node-fetch";
|
|
|
14
14
|
|
|
15
15
|
// Async generator
|
|
16
16
|
// https://stackoverflow.com/questions/5827612/node-js-fs-readdir-recursive-directory-search
|
|
17
|
-
async function* $
|
|
18
|
-
const dirInfo = await (0, $
|
|
17
|
+
async function* $11f5d2da33680230$var$getFiles(fileOrDirPath) {
|
|
18
|
+
const dirInfo = await (0, $khbt3$fspromises).stat(fileOrDirPath);
|
|
19
19
|
if (dirInfo.isFile()) {
|
|
20
20
|
yield fileOrDirPath;
|
|
21
21
|
return;
|
|
22
22
|
}
|
|
23
|
-
const dirents = await (0, $
|
|
23
|
+
const dirents = await (0, $khbt3$fspromises).readdir(fileOrDirPath, {
|
|
24
24
|
withFileTypes: true
|
|
25
25
|
});
|
|
26
26
|
for (const dirent of dirents){
|
|
27
|
-
const res = (0, $
|
|
28
|
-
if (dirent.isDirectory()) yield* $
|
|
27
|
+
const res = (0, $khbt3$path).resolve(fileOrDirPath, dirent.name);
|
|
28
|
+
if (dirent.isDirectory()) yield* $11f5d2da33680230$var$getFiles(res);
|
|
29
29
|
else yield res;
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
|
-
const $
|
|
33
|
-
fileOrDirPath = (0, $
|
|
34
|
-
const pathInfo = await (0, $
|
|
32
|
+
const $11f5d2da33680230$var$createFileProvider = async ({ fileOrDirPath: fileOrDirPath , filter: filter , shouldEstimateFileCount: shouldEstimateFileCount })=>{
|
|
33
|
+
fileOrDirPath = (0, $khbt3$path).resolve(fileOrDirPath);
|
|
34
|
+
const pathInfo = await (0, $khbt3$fspromises).stat(fileOrDirPath);
|
|
35
35
|
if (!pathInfo.isFile() && !pathInfo.isDirectory()) throw new Error(`The path '${fileOrDirPath}' is not a file or directory`);
|
|
36
36
|
let estimateFileCount = undefined;
|
|
37
37
|
if (shouldEstimateFileCount) {
|
|
38
38
|
estimateFileCount = 0;
|
|
39
|
-
for await (const filePath of $
|
|
39
|
+
for await (const filePath of $11f5d2da33680230$var$getFiles(fileOrDirPath)){
|
|
40
40
|
if (filter && !filter(filePath)) continue;
|
|
41
41
|
estimateFileCount++;
|
|
42
42
|
}
|
|
43
43
|
}
|
|
44
|
-
const fileGenerator = $
|
|
44
|
+
const fileGenerator = $11f5d2da33680230$var$getFiles(fileOrDirPath);
|
|
45
45
|
const getNextFile = async ()=>{
|
|
46
46
|
let nextFile = (await fileGenerator.next()).value;
|
|
47
47
|
if (!filter) return nextFile;
|
|
@@ -53,8 +53,8 @@ const $362846282c87e345$var$createFileProvider = async ({ fileOrDirPath: fileOrD
|
|
|
53
53
|
estimateFileCount: estimateFileCount
|
|
54
54
|
};
|
|
55
55
|
};
|
|
56
|
-
const $
|
|
57
|
-
const { getNextFile: getNextFile , estimateFileCount: estimateFileCount } = await $
|
|
56
|
+
const $11f5d2da33680230$export$8f4b23801f8f7529 = async ({ fileOrDirPath: fileOrDirPath , processFile: processFile , filter: filter , parallelCount: parallelCount = 10 , onProgress: onProgress })=>{
|
|
57
|
+
const { getNextFile: getNextFile , estimateFileCount: estimateFileCount } = await $11f5d2da33680230$var$createFileProvider({
|
|
58
58
|
fileOrDirPath: fileOrDirPath,
|
|
59
59
|
filter: filter,
|
|
60
60
|
shouldEstimateFileCount: true
|
|
@@ -105,7 +105,7 @@ const $362846282c87e345$export$8f4b23801f8f7529 = async ({ fileOrDirPath: fileOr
|
|
|
105
105
|
|
|
106
106
|
|
|
107
107
|
|
|
108
|
-
const $
|
|
108
|
+
const $3f348f69899f8bee$export$77ce72def6804f3 = async ({ auth: auth , item: item })=>{
|
|
109
109
|
// The data api to check for existing file is limited to 30 requests per minute
|
|
110
110
|
// While uploading allows 180 requests per minute
|
|
111
111
|
// i.e. it's faster to just upload again
|
|
@@ -116,12 +116,12 @@ const $8299ce3b64e7f5fc$export$77ce72def6804f3 = async ({ auth: auth , item: ite
|
|
|
116
116
|
// ipfsHash,
|
|
117
117
|
// };
|
|
118
118
|
// }
|
|
119
|
-
const data = new (0, $
|
|
120
|
-
data.append("file", (0, $
|
|
119
|
+
const data = new (0, $khbt3$formdata)();
|
|
120
|
+
data.append("file", (0, $khbt3$fs).createReadStream(item.filePath));
|
|
121
121
|
data.append("pinataMetadata", JSON.stringify({
|
|
122
122
|
name: item.name
|
|
123
123
|
}));
|
|
124
|
-
const response = await (0, $
|
|
124
|
+
const response = await (0, $khbt3$nodefetch)(`https://api.pinata.cloud/pinning/pinFileToIPFS`, {
|
|
125
125
|
headers: {
|
|
126
126
|
Authorization: `Bearer ${auth.pinataJwtToken}`,
|
|
127
127
|
"Content-Type": `multipart/form-data; boundary=${data._boundary}`
|
|
@@ -135,9 +135,9 @@ const $8299ce3b64e7f5fc$export$77ce72def6804f3 = async ({ auth: auth , item: ite
|
|
|
135
135
|
ipfsHash: uploadResult.IpfsHash
|
|
136
136
|
};
|
|
137
137
|
};
|
|
138
|
-
const $
|
|
139
|
-
const ipfsHash = await (0, $
|
|
140
|
-
const response = await (0, $
|
|
138
|
+
const $3f348f69899f8bee$var$checkIfFileIsPinned = async ({ auth: auth , item: item })=>{
|
|
139
|
+
const ipfsHash = await (0, $khbt3$ipfsonlyhash).of((0, $khbt3$fs).createReadStream(item.filePath));
|
|
140
|
+
const response = await (0, $khbt3$nodefetch)(`https://api.pinata.cloud/data/pinList?status=pinned&hashContains=${ipfsHash}`, {
|
|
141
141
|
headers: {
|
|
142
142
|
Authorization: `Bearer ${auth.pinataJwtToken}`
|
|
143
143
|
},
|
|
@@ -153,12 +153,12 @@ const $8299ce3b64e7f5fc$var$checkIfFileIsPinned = async ({ auth: auth , item: it
|
|
|
153
153
|
};
|
|
154
154
|
|
|
155
155
|
|
|
156
|
-
async function $
|
|
156
|
+
async function $81b0536a41de4891$export$1391212d75b2ee65(timeout) {
|
|
157
157
|
return await new Promise((resolve)=>{
|
|
158
158
|
setTimeout(resolve, timeout);
|
|
159
159
|
});
|
|
160
160
|
}
|
|
161
|
-
const $
|
|
161
|
+
const $81b0536a41de4891$export$568be7ef485b9273 = ({ retryCount: retryCount = 5 , targetRequestsPerMinute: targetRequestsPerMinute = 180 })=>{
|
|
162
162
|
let averageTimePerRequest = 5000;
|
|
163
163
|
let targetTimePerRequest = 60000 / targetRequestsPerMinute;
|
|
164
164
|
let lastTime = Date.now();
|
|
@@ -169,7 +169,7 @@ const $6b0ddb031a0df909$export$568be7ef485b9273 = ({ retryCount: retryCount = 5
|
|
|
169
169
|
try {
|
|
170
170
|
let delayTimeMs = Math.max(10, targetTimePerRequest - averageTimePerRequest);
|
|
171
171
|
// Partially randomized delay to ensure parallel requests don't line up
|
|
172
|
-
await $
|
|
172
|
+
await $81b0536a41de4891$export$1391212d75b2ee65(Math.floor(delayTimeMs * (1 + 0.5 * Math.random())));
|
|
173
173
|
const result = await process();
|
|
174
174
|
const timeNow = Date.now();
|
|
175
175
|
const timeElapsed = timeNow - lastTime;
|
|
@@ -194,25 +194,25 @@ const $6b0ddb031a0df909$export$568be7ef485b9273 = ({ retryCount: retryCount = 5
|
|
|
194
194
|
|
|
195
195
|
|
|
196
196
|
|
|
197
|
-
const $
|
|
197
|
+
const $e26bf59b4820ce36$var$publishToIpfs = async (fileOrDirPath, auth)=>{
|
|
198
198
|
if (!fileOrDirPath) throw new Error(`path was not provided`);
|
|
199
199
|
// Pinata is limited to 180 requests per minute
|
|
200
200
|
// So for the first 180 requests they can go fast
|
|
201
|
-
const { processWithBackoff: processWithBackoff } = (0, $
|
|
201
|
+
const { processWithBackoff: processWithBackoff } = (0, $81b0536a41de4891$export$568be7ef485b9273)({
|
|
202
202
|
retryCount: 5,
|
|
203
203
|
targetRequestsPerMinute: 180
|
|
204
204
|
});
|
|
205
|
-
const result = await (0, $
|
|
205
|
+
const result = await (0, $11f5d2da33680230$export$8f4b23801f8f7529)({
|
|
206
206
|
fileOrDirPath: fileOrDirPath,
|
|
207
207
|
parallelCount: 10,
|
|
208
208
|
processFile: async (filePath)=>{
|
|
209
209
|
// // TEMP: Debug
|
|
210
210
|
// console.log(`publishing: ${filePath}`);
|
|
211
|
-
return processWithBackoff(()=>(0, $
|
|
211
|
+
return processWithBackoff(()=>(0, $3f348f69899f8bee$export$77ce72def6804f3)({
|
|
212
212
|
auth: auth,
|
|
213
213
|
item: {
|
|
214
214
|
filePath: filePath,
|
|
215
|
-
name: (0, $
|
|
215
|
+
name: (0, $khbt3$path).basename(filePath)
|
|
216
216
|
}
|
|
217
217
|
}));
|
|
218
218
|
},
|
|
@@ -247,12 +247,12 @@ const $b297f5d0aa12bc82$var$publishToIpfs = async (fileOrDirPath, auth)=>{
|
|
|
247
247
|
]
|
|
248
248
|
};
|
|
249
249
|
};
|
|
250
|
-
const $
|
|
250
|
+
const $e26bf59b4820ce36$var$pinToIpfs = async (hash, auth)=>{
|
|
251
251
|
if (!hash) throw new Error(`ipfs hash was not provided`);
|
|
252
252
|
// TODO: Implement pinning
|
|
253
253
|
throw new Error("pinToIpfs: Not Implemented");
|
|
254
254
|
};
|
|
255
|
-
const $
|
|
255
|
+
const $e26bf59b4820ce36$var$execute = async (opts)=>{
|
|
256
256
|
const { task: task , path: path , hash: hash , config: config , } = opts;
|
|
257
257
|
const auth = {
|
|
258
258
|
// TODO: Where should this be stored?
|
|
@@ -262,41 +262,41 @@ const $b297f5d0aa12bc82$var$execute = async (opts)=>{
|
|
|
262
262
|
if (!auth.pinataJwtToken) throw new Error(`The 'credentials.pinataJwtToken' was not found in config`);
|
|
263
263
|
switch(task){
|
|
264
264
|
case "publish":
|
|
265
|
-
return $
|
|
265
|
+
return $e26bf59b4820ce36$var$publishToIpfs(path, auth);
|
|
266
266
|
case "pin":
|
|
267
|
-
return $
|
|
267
|
+
return $e26bf59b4820ce36$var$pinToIpfs(hash, auth);
|
|
268
268
|
default:
|
|
269
269
|
throw new Error(`${task} is not an understood task by the ipfs-pinata plugin`);
|
|
270
270
|
}
|
|
271
271
|
};
|
|
272
|
-
var $
|
|
272
|
+
var $e26bf59b4820ce36$export$2e2bcd8739ae039 = async (args)=>{
|
|
273
273
|
const opts = args;
|
|
274
274
|
try {
|
|
275
|
-
const resultRaw = await $
|
|
275
|
+
const resultRaw = await $e26bf59b4820ce36$var$execute(opts);
|
|
276
276
|
// TODO: Fix deno parsing
|
|
277
277
|
// Without this, `data.reduce is not a function`
|
|
278
278
|
const result = "data" in resultRaw ? resultRaw.data : resultRaw;
|
|
279
|
-
return (0, $
|
|
279
|
+
return (0, $khbt3$sendJsonRes)(result);
|
|
280
280
|
} catch (err) {
|
|
281
281
|
const error = err;
|
|
282
|
-
if (error.message) return (0, $
|
|
282
|
+
if (error.message) return (0, $khbt3$sendAsyncErr)(error.message);
|
|
283
283
|
}
|
|
284
284
|
};
|
|
285
285
|
|
|
286
286
|
|
|
287
|
-
(0, $
|
|
287
|
+
(0, $khbt3$Plugin).create(()=>({
|
|
288
288
|
schema: "0.1",
|
|
289
289
|
version: "0.4.0",
|
|
290
290
|
alias: "pinata",
|
|
291
291
|
tasks: [
|
|
292
|
-
(0, $
|
|
292
|
+
(0, $khbt3$Task).create({
|
|
293
293
|
task: "publish",
|
|
294
294
|
command: "publish [path]",
|
|
295
295
|
description: "Upload and pin files using your pinata account.",
|
|
296
296
|
aliases: [],
|
|
297
297
|
handler: "proxy",
|
|
298
298
|
positionals: [
|
|
299
|
-
(0, $
|
|
299
|
+
(0, $khbt3$PositionalArg).create({
|
|
300
300
|
placeholder: "path",
|
|
301
301
|
description: "Directory or file path to publish",
|
|
302
302
|
type: "string"
|
|
@@ -305,7 +305,7 @@ var $b297f5d0aa12bc82$export$2e2bcd8739ae039 = async (args)=>{
|
|
|
305
305
|
encoding: "json"
|
|
306
306
|
}),
|
|
307
307
|
],
|
|
308
|
-
proxy: $
|
|
308
|
+
proxy: $e26bf59b4820ce36$export$2e2bcd8739ae039
|
|
309
309
|
}), process.argv);
|
|
310
310
|
|
|
311
311
|
|
package/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"mappings":"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","sources":["index.ts","src/proxy.ts","src/file-processing.ts","src/pinata-api.ts","src/utils.ts"],"sourcesContent":["import { Option, Plugin, PositionalArg, Task } from '@taqueria/node-sdk';\nimport proxy from './src/proxy';\n\nPlugin.create(() => ({\n\tschema: '0.1',\n\tversion: '0.4.0',\n\talias: 'pinata',\n\ttasks: [\n\t\tTask.create({\n\t\t\ttask: 'publish',\n\t\t\tcommand: 'publish [path]',\n\t\t\tdescription: 'Upload and pin files using your pinata account.',\n\t\t\taliases: [],\n\t\t\thandler: 'proxy',\n\t\t\tpositionals: [\n\t\t\t\tPositionalArg.create({\n\t\t\t\t\tplaceholder: 'path',\n\t\t\t\t\tdescription: 'Directory or file path to publish',\n\t\t\t\t\ttype: 'string',\n\t\t\t\t}),\n\t\t\t],\n\t\t\tencoding: 'json',\n\t\t}),\n\t\t// Pinning Not Implemented Yet\n\t\t// Task.create({\n\t\t// \ttask: 'pin',\n\t\t// \tcommand: 'pin [hash]',\n\t\t// \tdescription: 'Pin a file already on ipfs with your pinata account.',\n\t\t// \taliases: [],\n\t\t// \thandler: 'proxy',\n\t\t// \tpositionals: [\n\t\t// \t\tPositionalArg.create({\n\t\t// \t\t\tplaceholder: 'hash',\n\t\t// \t\t\tdescription: 'Ipfs hash of the file or directory that is already on the ipfs network.',\n\t\t// \t\t\ttype: 'string',\n\t\t// \t\t}),\n\t\t// \t]\n\t\t// }),\n\t],\n\tproxy,\n}), process.argv);\n","import { sendAsyncErr, sendAsyncRes, sendErr, sendJsonRes } from '@taqueria/node-sdk';\nimport { LoadedConfig, RequestArgs, SanitizedAbsPath } from '@taqueria/node-sdk/types';\nimport path from 'path';\nimport { processFiles } from './file-processing';\nimport { PinataAuth, publishFileToIpfs } from './pinata-api';\nimport { createProcessBackoffController } from './utils';\n\n// Load .env for jwt token\n// TODO: How should this be stored in a secure way?\nimport 'dotenv/config';\n\n// TODO: What should this be, it was removed from the sdk\ntype PluginResponse =\n\t| void\n\t| {\n\t\trender: 'table';\n\t\tdata: unknown[];\n\t};\n\ninterface Opts extends RequestArgs.ProxyRequestArgs {\n\treadonly path?: string;\n\treadonly hash?: string;\n}\n\nconst publishToIpfs = async (fileOrDirPath: undefined | string, auth: PinataAuth): Promise<PluginResponse> => {\n\tif (!fileOrDirPath) {\n\t\tthrow new Error(`path was not provided`);\n\t}\n\n\t// Pinata is limited to 180 requests per minute\n\t// So for the first 180 requests they can go fast\n\n\tconst { processWithBackoff } = createProcessBackoffController({\n\t\tretryCount: 5,\n\t\ttargetRequestsPerMinute: 180,\n\t});\n\n\tconst result = await processFiles({\n\t\tfileOrDirPath,\n\t\tparallelCount: 10,\n\t\tprocessFile: async filePath => {\n\t\t\t// // TEMP: Debug\n\t\t\t// console.log(`publishing: ${filePath}`);\n\n\t\t\treturn processWithBackoff(() =>\n\t\t\t\tpublishFileToIpfs({\n\t\t\t\t\tauth,\n\t\t\t\t\titem: { filePath, name: path.basename(filePath) },\n\t\t\t\t})\n\t\t\t);\n\t\t},\n\t\tonProgress: ({ processedFilesCount, estimateFileCount }) => {\n\t\t\tif (estimateFileCount && processedFilesCount % 10) {\n\t\t\t\tlet ratio = processedFilesCount / estimateFileCount;\n\t\t\t\tif (ratio > 1) ratio = 1;\n\n\t\t\t\t// // TODO: Call task sdk progress\n\t\t\t\t// console.log(`Progress: ${(ratio * 100).toFixed(0)}%`);\n\t\t\t}\n\t\t},\n\t});\n\n\t// // TEMP: DEBUG: Show error\n\t// if (result.failures.length) {\n\t// \tconsole.log('❗ Failures:\\n' + result.failures.map(f => `${f.filePath}: ${f.error}`).join('\\n'));\n\t// }\n\n\treturn {\n\t\trender: 'table',\n\t\tdata: [\n\t\t\t...result.failures.map(x => ({\n\t\t\t\t'?': '❌',\n\t\t\t\tfilePath: x.filePath,\n\t\t\t\tipfsHash: undefined,\n\t\t\t\terror: (x.error as { message?: string })?.message ?? JSON.stringify(x.error),\n\t\t\t})),\n\t\t\t...result.successes.map(x => ({\n\t\t\t\t'?': '✔',\n\t\t\t\tfilePath: x.filePath,\n\t\t\t\tipfsHash: x.result.ipfsHash,\n\t\t\t\terror: undefined,\n\t\t\t})),\n\t\t],\n\t};\n};\n\nconst pinToIpfs = async (hash: undefined | string, auth: PinataAuth): Promise<PluginResponse> => {\n\tif (!hash) {\n\t\tthrow new Error(`ipfs hash was not provided`);\n\t}\n\n\t// TODO: Implement pinning\n\tthrow new Error('pinToIpfs: Not Implemented');\n};\n\nconst execute = async (opts: Opts): Promise<PluginResponse> => {\n\tconst {\n\t\ttask,\n\t\tpath,\n\t\thash,\n\t\tconfig,\n\t} = opts;\n\n\tconst auth: PinataAuth = {\n\t\t// TODO: Where should this be stored?\n\t\t// pinataJwtToken: (config as Record<string, any>).credentials.pinataJwtToken,\n\t\tpinataJwtToken: process.env['pinataJwtToken'] as string,\n\t};\n\n\tif (!auth.pinataJwtToken) {\n\t\tthrow new Error(`The 'credentials.pinataJwtToken' was not found in config`);\n\t}\n\n\tswitch (task) {\n\t\tcase 'publish':\n\t\t\treturn publishToIpfs(path, auth);\n\t\tcase 'pin':\n\t\t\treturn pinToIpfs(hash, auth);\n\t\tdefault:\n\t\t\tthrow new Error(`${task} is not an understood task by the ipfs-pinata plugin`);\n\t}\n};\n\nexport default async (args: RequestArgs.ProxyRequestArgs): Promise<PluginResponse> => {\n\tconst opts = args as Opts;\n\n\ttry {\n\t\tconst resultRaw = await execute(opts) as Record<string, unknown>;\n\t\t// TODO: Fix deno parsing\n\t\t// Without this, `data.reduce is not a function`\n\t\tconst result = ('data' in resultRaw) ? resultRaw.data : resultRaw;\n\t\treturn sendJsonRes(result);\n\t} catch (err) {\n\t\tconst error = err as Error;\n\t\tif (error.message) {\n\t\t\treturn sendAsyncErr(error.message);\n\t\t}\n\t}\n};\n","import fs from 'fs/promises';\nimport path from 'path';\n\n// Async generator\n// https://stackoverflow.com/questions/5827612/node-js-fs-readdir-recursive-directory-search\nasync function* getFiles(fileOrDirPath: string): AsyncGenerator<string, void, unknown> {\n\tconst dirInfo = await fs.stat(fileOrDirPath);\n\tif (dirInfo.isFile()) {\n\t\tyield fileOrDirPath;\n\t\treturn;\n\t}\n\n\tconst dirents = await fs.readdir(fileOrDirPath, { withFileTypes: true });\n\tfor (const dirent of dirents) {\n\t\tconst res = path.resolve(fileOrDirPath, dirent.name);\n\t\tif (dirent.isDirectory()) {\n\t\t\tyield* getFiles(res);\n\t\t} else {\n\t\t\tyield res;\n\t\t}\n\t}\n}\n\nconst createFileProvider = async ({\n\tfileOrDirPath,\n\tfilter,\n\tshouldEstimateFileCount,\n}: {\n\tfileOrDirPath: string;\n\tfilter?: (filePath: string) => boolean;\n\tshouldEstimateFileCount?: boolean;\n}) => {\n\tfileOrDirPath = path.resolve(fileOrDirPath);\n\tconst pathInfo = await fs.stat(fileOrDirPath);\n\tif (\n\t\t!pathInfo.isFile()\n\t\t&& !pathInfo.isDirectory()\n\t) {\n\t\tthrow new Error(`The path '${fileOrDirPath}' is not a file or directory`);\n\t}\n\n\tlet estimateFileCount = undefined as undefined | number;\n\tif (shouldEstimateFileCount) {\n\t\testimateFileCount = 0;\n\t\tfor await (const filePath of getFiles(fileOrDirPath)) {\n\t\t\tif (filter && !filter(filePath)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\testimateFileCount++;\n\t\t}\n\t}\n\n\tconst fileGenerator = getFiles(fileOrDirPath);\n\tconst getNextFile = async () => {\n\t\tlet nextFile = (await fileGenerator.next()).value;\n\t\tif (!filter) {\n\t\t\treturn nextFile;\n\t\t}\n\n\t\twhile (nextFile && !filter(nextFile)) {\n\t\t\tnextFile = await getNextFile();\n\t\t}\n\n\t\treturn nextFile;\n\t};\n\treturn {\n\t\tgetNextFile,\n\t\testimateFileCount,\n\t};\n};\n\ntype ProgressInfo = { processedFilesCount: number; estimateFileCount: undefined | number };\nexport const processFiles = async <TResult>({\n\tfileOrDirPath,\n\tprocessFile,\n\tfilter,\n\tparallelCount = 10,\n\tonProgress,\n}: {\n\tfileOrDirPath: string;\n\tprocessFile: (filePath: string, progress: ProgressInfo) => Promise<TResult>;\n\tfilter?: (filePath: string) => boolean;\n\tparallelCount?: number;\n\tonProgress?: (progress: ProgressInfo) => void;\n}) => {\n\tconst { getNextFile, estimateFileCount } = await createFileProvider({\n\t\tfileOrDirPath,\n\t\tfilter,\n\t\tshouldEstimateFileCount: true,\n\t});\n\n\tconst successes = [] as { filePath: string; result: TResult }[];\n\tconst failures = [] as { filePath: string; error: unknown }[];\n\n\tonProgress?.({\n\t\tprocessedFilesCount: 0,\n\t\testimateFileCount,\n\t});\n\n\tawait Promise.all([...new Array(parallelCount)].map(async x => {\n\t\tlet fileToProcess = await getNextFile();\n\t\twhile (fileToProcess) {\n\t\t\tconst progressInfo = {\n\t\t\t\tprocessedFilesCount: successes.length + failures.length,\n\t\t\t\testimateFileCount,\n\t\t\t};\n\t\t\tonProgress?.(progressInfo);\n\n\t\t\ttry {\n\t\t\t\tconst result = await processFile(fileToProcess, progressInfo);\n\t\t\t\tsuccesses.push({ filePath: fileToProcess, result });\n\t\t\t} catch (err) {\n\t\t\t\tfailures.push({ filePath: fileToProcess, error: err });\n\t\t\t}\n\n\t\t\tfileToProcess = await getNextFile();\n\t\t}\n\t}));\n\n\tonProgress?.({\n\t\tprocessedFilesCount: successes.length + failures.length,\n\t\testimateFileCount,\n\t});\n\n\treturn {\n\t\tsuccesses,\n\t\tfailures,\n\t};\n};\n","import FormData from 'form-data';\nimport fs from 'fs';\nimport Hash from 'ipfs-only-hash';\nimport fetch from 'node-fetch';\n\nexport type PinataAuth = {\n\tpinataJwtToken: string;\n};\n\nexport type PublishFileResult = {\n\tipfsHash: string;\n};\n\nexport const publishFileToIpfs = async ({\n\tauth,\n\titem,\n}: {\n\tauth: PinataAuth;\n\titem: {\n\t\tname: string;\n\t\tfilePath: string;\n\t};\n}): Promise<PublishFileResult> => {\n\t// The data api to check for existing file is limited to 30 requests per minute\n\t// While uploading allows 180 requests per minute\n\t// i.e. it's faster to just upload again\n\n\t// // Skip if already pinned\n\t// const { isPinned, ipfsHash } = await checkIfFileIsPinned({ auth, item });\n\t// if (isPinned) {\n\t// \treturn {\n\t// \t\tipfsHash,\n\t// \t};\n\t// }\n\n\tconst data = new FormData();\n\tdata.append('file', fs.createReadStream(item.filePath));\n\tdata.append(\n\t\t'pinataMetadata',\n\t\tJSON.stringify({\n\t\t\tname: item.name,\n\t\t}),\n\t);\n\n\tconst response = await fetch(`https://api.pinata.cloud/pinning/pinFileToIPFS`, {\n\t\theaders: {\n\t\t\tAuthorization: `Bearer ${auth.pinataJwtToken}`,\n\t\t\t'Content-Type': `multipart/form-data; boundary=${(data as unknown as { _boundary: string })._boundary}`,\n\t\t},\n\t\tbody: data,\n\t\tmethod: 'post',\n\t});\n\n\tif (!response.ok) {\n\t\tthrow new Error(`Failed to upload '${item.name}' to ipfs ${response.statusText}`);\n\t}\n\n\tconst uploadResult = await response.json() as {\n\t\tIpfsHash: string; // This is the IPFS multi-hash provided back for your content,\n\t\tPinSize: string; // This is how large (in bytes) the content you just pinned is,\n\t\tTimestamp: string; // This is the timestamp for your content pinning (represented in ISO 8601 format)\n\t};\n\n\treturn {\n\t\tipfsHash: uploadResult.IpfsHash,\n\t};\n};\n\nconst checkIfFileIsPinned = async ({\n\tauth,\n\titem,\n}: {\n\tauth: PinataAuth;\n\titem: {\n\t\tname: string;\n\t\tfilePath: string;\n\t};\n}) => {\n\tconst ipfsHash = await Hash.of(fs.createReadStream(item.filePath));\n\n\tconst response = await fetch(`https://api.pinata.cloud/data/pinList?status=pinned&hashContains=${ipfsHash}`, {\n\t\theaders: {\n\t\t\tAuthorization: `Bearer ${auth.pinataJwtToken}`,\n\t\t},\n\t\tmethod: 'get',\n\t});\n\n\tif (!response.ok) {\n\t\tthrow new Error(`Failed to query '${item.name}' status from pinata ${response.statusText}`);\n\t}\n\n\tconst pinResult = await response.json() as {\n\t\tcount: number;\n\t\trows: {\n\t\t\tid: string;\n\t\t\tipfs_pin_hash: string;\n\t\t\tsize: number;\n\t\t\tuser_id: string;\n\t\t\tdate_pinned: null | string;\n\t\t\tdate_unpinned: null | string;\n\t\t\tmetadata: {\n\t\t\t\tname: string;\n\t\t\t\tkeyvalues: null | string;\n\t\t\t};\n\t\t\tregions: {\n\t\t\t\tregionId: string;\n\t\t\t\tcurrentReplicationCount: number;\n\t\t\t\tdesiredReplicationCount: number;\n\t\t\t}[];\n\t\t}[];\n\t};\n\n\tconst isPinned = pinResult.rows.some(x =>\n\t\tx.ipfs_pin_hash === ipfsHash\n\t\t&& x.date_pinned\n\t\t&& !x.date_unpinned\n\t);\n\n\treturn {\n\t\tisPinned,\n\t\tipfsHash,\n\t};\n};\n","export async function delay(timeout: number): Promise<void> {\n\treturn await new Promise(resolve => {\n\t\tsetTimeout(resolve, timeout);\n\t});\n}\n\nexport const createProcessBackoffController = ({\n\tretryCount = 5,\n\ttargetRequestsPerMinute = 180,\n}: {\n\tretryCount?: number;\n\ttargetRequestsPerMinute?: number;\n}) => {\n\tlet averageTimePerRequest = 5000;\n\tlet targetTimePerRequest = 60000 / targetRequestsPerMinute;\n\tlet lastTime = Date.now();\n\n\tconst processWithBackoff = async <TResult>(process: () => Promise<TResult>) => {\n\t\tlet attempt = 0;\n\t\tlet lastError = undefined as unknown;\n\t\twhile (attempt < retryCount) {\n\t\t\ttry {\n\t\t\t\tlet delayTimeMs = Math.max(10, targetTimePerRequest - averageTimePerRequest);\n\n\t\t\t\t// Partially randomized delay to ensure parallel requests don't line up\n\t\t\t\tawait delay(Math.floor(delayTimeMs * (1 + 0.5 * Math.random())));\n\n\t\t\t\tconst result = await process();\n\n\t\t\t\tconst timeNow = Date.now();\n\t\t\t\tconst timeElapsed = timeNow - lastTime;\n\t\t\t\tlastTime = timeNow;\n\n\t\t\t\t// Running average\n\t\t\t\taverageTimePerRequest = averageTimePerRequest * 0.97 + timeElapsed * 0.03;\n\n\t\t\t\treturn result;\n\t\t\t} catch (err) {\n\t\t\t\tlastError = err;\n\t\t\t}\n\n\t\t\t// Quickly increase time to wait if failure (allow negatives to wait longer than target)\n\t\t\taverageTimePerRequest -= (attempt + 1) * 1000;\n\t\t\tattempt++;\n\t\t}\n\n\t\t// All attempts failed\n\t\tthrow lastError;\n\t};\n\n\treturn {\n\t\tprocessWithBackoff,\n\t};\n};\n"],"names":[],"version":3,"file":"index.js.map","sourceRoot":"/"}
|
|
1
|
+
{"mappings":"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","sources":["taqueria-plugin-ipfs-pinata/index.ts","taqueria-plugin-ipfs-pinata/src/proxy.ts","taqueria-plugin-ipfs-pinata/src/file-processing.ts","taqueria-plugin-ipfs-pinata/src/pinata-api.ts","taqueria-plugin-ipfs-pinata/src/utils.ts"],"sourcesContent":["import { Option, Plugin, PositionalArg, Task } from '@taqueria/node-sdk';\nimport proxy from './src/proxy';\n\nPlugin.create(() => ({\n\tschema: '0.1',\n\tversion: '0.4.0',\n\talias: 'pinata',\n\ttasks: [\n\t\tTask.create({\n\t\t\ttask: 'publish',\n\t\t\tcommand: 'publish [path]',\n\t\t\tdescription: 'Upload and pin files using your pinata account.',\n\t\t\taliases: [],\n\t\t\thandler: 'proxy',\n\t\t\tpositionals: [\n\t\t\t\tPositionalArg.create({\n\t\t\t\t\tplaceholder: 'path',\n\t\t\t\t\tdescription: 'Directory or file path to publish',\n\t\t\t\t\ttype: 'string',\n\t\t\t\t}),\n\t\t\t],\n\t\t\tencoding: 'json',\n\t\t}),\n\t\t// Pinning Not Implemented Yet\n\t\t// Task.create({\n\t\t// \ttask: 'pin',\n\t\t// \tcommand: 'pin [hash]',\n\t\t// \tdescription: 'Pin a file already on ipfs with your pinata account.',\n\t\t// \taliases: [],\n\t\t// \thandler: 'proxy',\n\t\t// \tpositionals: [\n\t\t// \t\tPositionalArg.create({\n\t\t// \t\t\tplaceholder: 'hash',\n\t\t// \t\t\tdescription: 'Ipfs hash of the file or directory that is already on the ipfs network.',\n\t\t// \t\t\ttype: 'string',\n\t\t// \t\t}),\n\t\t// \t]\n\t\t// }),\n\t],\n\tproxy,\n}), process.argv);\n","import { sendAsyncErr, sendAsyncRes, sendErr, sendJsonRes } from '@taqueria/node-sdk';\nimport { LoadedConfig, RequestArgs, SanitizedAbsPath } from '@taqueria/node-sdk/types';\nimport path from 'path';\nimport { processFiles } from './file-processing';\nimport { PinataAuth, publishFileToIpfs } from './pinata-api';\nimport { createProcessBackoffController } from './utils';\n\n// Load .env for jwt token\n// TODO: How should this be stored in a secure way?\nimport 'dotenv/config';\n\n// TODO: What should this be, it was removed from the sdk\ntype PluginResponse =\n\t| void\n\t| {\n\t\trender: 'table';\n\t\tdata: unknown[];\n\t};\n\ninterface Opts extends RequestArgs.ProxyRequestArgs {\n\treadonly path?: string;\n\treadonly hash?: string;\n}\n\nconst publishToIpfs = async (fileOrDirPath: undefined | string, auth: PinataAuth): Promise<PluginResponse> => {\n\tif (!fileOrDirPath) {\n\t\tthrow new Error(`path was not provided`);\n\t}\n\n\t// Pinata is limited to 180 requests per minute\n\t// So for the first 180 requests they can go fast\n\n\tconst { processWithBackoff } = createProcessBackoffController({\n\t\tretryCount: 5,\n\t\ttargetRequestsPerMinute: 180,\n\t});\n\n\tconst result = await processFiles({\n\t\tfileOrDirPath,\n\t\tparallelCount: 10,\n\t\tprocessFile: async filePath => {\n\t\t\t// // TEMP: Debug\n\t\t\t// console.log(`publishing: ${filePath}`);\n\n\t\t\treturn processWithBackoff(() =>\n\t\t\t\tpublishFileToIpfs({\n\t\t\t\t\tauth,\n\t\t\t\t\titem: { filePath, name: path.basename(filePath) },\n\t\t\t\t})\n\t\t\t);\n\t\t},\n\t\tonProgress: ({ processedFilesCount, estimateFileCount }) => {\n\t\t\tif (estimateFileCount && processedFilesCount % 10) {\n\t\t\t\tlet ratio = processedFilesCount / estimateFileCount;\n\t\t\t\tif (ratio > 1) ratio = 1;\n\n\t\t\t\t// // TODO: Call task sdk progress\n\t\t\t\t// console.log(`Progress: ${(ratio * 100).toFixed(0)}%`);\n\t\t\t}\n\t\t},\n\t});\n\n\t// // TEMP: DEBUG: Show error\n\t// if (result.failures.length) {\n\t// \tconsole.log('❗ Failures:\\n' + result.failures.map(f => `${f.filePath}: ${f.error}`).join('\\n'));\n\t// }\n\n\treturn {\n\t\trender: 'table',\n\t\tdata: [\n\t\t\t...result.failures.map(x => ({\n\t\t\t\t'?': '❌',\n\t\t\t\tfilePath: x.filePath,\n\t\t\t\tipfsHash: undefined,\n\t\t\t\terror: (x.error as { message?: string })?.message ?? JSON.stringify(x.error),\n\t\t\t})),\n\t\t\t...result.successes.map(x => ({\n\t\t\t\t'?': '✔',\n\t\t\t\tfilePath: x.filePath,\n\t\t\t\tipfsHash: x.result.ipfsHash,\n\t\t\t\terror: undefined,\n\t\t\t})),\n\t\t],\n\t};\n};\n\nconst pinToIpfs = async (hash: undefined | string, auth: PinataAuth): Promise<PluginResponse> => {\n\tif (!hash) {\n\t\tthrow new Error(`ipfs hash was not provided`);\n\t}\n\n\t// TODO: Implement pinning\n\tthrow new Error('pinToIpfs: Not Implemented');\n};\n\nconst execute = async (opts: Opts): Promise<PluginResponse> => {\n\tconst {\n\t\ttask,\n\t\tpath,\n\t\thash,\n\t\tconfig,\n\t} = opts;\n\n\tconst auth: PinataAuth = {\n\t\t// TODO: Where should this be stored?\n\t\t// pinataJwtToken: (config as Record<string, any>).credentials.pinataJwtToken,\n\t\tpinataJwtToken: process.env['pinataJwtToken'] as string,\n\t};\n\n\tif (!auth.pinataJwtToken) {\n\t\tthrow new Error(`The 'credentials.pinataJwtToken' was not found in config`);\n\t}\n\n\tswitch (task) {\n\t\tcase 'publish':\n\t\t\treturn publishToIpfs(path, auth);\n\t\tcase 'pin':\n\t\t\treturn pinToIpfs(hash, auth);\n\t\tdefault:\n\t\t\tthrow new Error(`${task} is not an understood task by the ipfs-pinata plugin`);\n\t}\n};\n\nexport default async (args: RequestArgs.ProxyRequestArgs): Promise<PluginResponse> => {\n\tconst opts = args as Opts;\n\n\ttry {\n\t\tconst resultRaw = await execute(opts) as Record<string, unknown>;\n\t\t// TODO: Fix deno parsing\n\t\t// Without this, `data.reduce is not a function`\n\t\tconst result = ('data' in resultRaw) ? resultRaw.data : resultRaw;\n\t\treturn sendJsonRes(result);\n\t} catch (err) {\n\t\tconst error = err as Error;\n\t\tif (error.message) {\n\t\t\treturn sendAsyncErr(error.message);\n\t\t}\n\t}\n};\n","import fs from 'fs/promises';\nimport path from 'path';\n\n// Async generator\n// https://stackoverflow.com/questions/5827612/node-js-fs-readdir-recursive-directory-search\nasync function* getFiles(fileOrDirPath: string): AsyncGenerator<string, void, unknown> {\n\tconst dirInfo = await fs.stat(fileOrDirPath);\n\tif (dirInfo.isFile()) {\n\t\tyield fileOrDirPath;\n\t\treturn;\n\t}\n\n\tconst dirents = await fs.readdir(fileOrDirPath, { withFileTypes: true });\n\tfor (const dirent of dirents) {\n\t\tconst res = path.resolve(fileOrDirPath, dirent.name);\n\t\tif (dirent.isDirectory()) {\n\t\t\tyield* getFiles(res);\n\t\t} else {\n\t\t\tyield res;\n\t\t}\n\t}\n}\n\nconst createFileProvider = async ({\n\tfileOrDirPath,\n\tfilter,\n\tshouldEstimateFileCount,\n}: {\n\tfileOrDirPath: string;\n\tfilter?: (filePath: string) => boolean;\n\tshouldEstimateFileCount?: boolean;\n}) => {\n\tfileOrDirPath = path.resolve(fileOrDirPath);\n\tconst pathInfo = await fs.stat(fileOrDirPath);\n\tif (\n\t\t!pathInfo.isFile()\n\t\t&& !pathInfo.isDirectory()\n\t) {\n\t\tthrow new Error(`The path '${fileOrDirPath}' is not a file or directory`);\n\t}\n\n\tlet estimateFileCount = undefined as undefined | number;\n\tif (shouldEstimateFileCount) {\n\t\testimateFileCount = 0;\n\t\tfor await (const filePath of getFiles(fileOrDirPath)) {\n\t\t\tif (filter && !filter(filePath)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\testimateFileCount++;\n\t\t}\n\t}\n\n\tconst fileGenerator = getFiles(fileOrDirPath);\n\tconst getNextFile = async () => {\n\t\tlet nextFile = (await fileGenerator.next()).value;\n\t\tif (!filter) {\n\t\t\treturn nextFile;\n\t\t}\n\n\t\twhile (nextFile && !filter(nextFile)) {\n\t\t\tnextFile = await getNextFile();\n\t\t}\n\n\t\treturn nextFile;\n\t};\n\treturn {\n\t\tgetNextFile,\n\t\testimateFileCount,\n\t};\n};\n\ntype ProgressInfo = { processedFilesCount: number; estimateFileCount: undefined | number };\nexport const processFiles = async <TResult>({\n\tfileOrDirPath,\n\tprocessFile,\n\tfilter,\n\tparallelCount = 10,\n\tonProgress,\n}: {\n\tfileOrDirPath: string;\n\tprocessFile: (filePath: string, progress: ProgressInfo) => Promise<TResult>;\n\tfilter?: (filePath: string) => boolean;\n\tparallelCount?: number;\n\tonProgress?: (progress: ProgressInfo) => void;\n}) => {\n\tconst { getNextFile, estimateFileCount } = await createFileProvider({\n\t\tfileOrDirPath,\n\t\tfilter,\n\t\tshouldEstimateFileCount: true,\n\t});\n\n\tconst successes = [] as { filePath: string; result: TResult }[];\n\tconst failures = [] as { filePath: string; error: unknown }[];\n\n\tonProgress?.({\n\t\tprocessedFilesCount: 0,\n\t\testimateFileCount,\n\t});\n\n\tawait Promise.all([...new Array(parallelCount)].map(async x => {\n\t\tlet fileToProcess = await getNextFile();\n\t\twhile (fileToProcess) {\n\t\t\tconst progressInfo = {\n\t\t\t\tprocessedFilesCount: successes.length + failures.length,\n\t\t\t\testimateFileCount,\n\t\t\t};\n\t\t\tonProgress?.(progressInfo);\n\n\t\t\ttry {\n\t\t\t\tconst result = await processFile(fileToProcess, progressInfo);\n\t\t\t\tsuccesses.push({ filePath: fileToProcess, result });\n\t\t\t} catch (err) {\n\t\t\t\tfailures.push({ filePath: fileToProcess, error: err });\n\t\t\t}\n\n\t\t\tfileToProcess = await getNextFile();\n\t\t}\n\t}));\n\n\tonProgress?.({\n\t\tprocessedFilesCount: successes.length + failures.length,\n\t\testimateFileCount,\n\t});\n\n\treturn {\n\t\tsuccesses,\n\t\tfailures,\n\t};\n};\n","import FormData from 'form-data';\nimport fs from 'fs';\nimport Hash from 'ipfs-only-hash';\nimport fetch from 'node-fetch';\n\nexport type PinataAuth = {\n\tpinataJwtToken: string;\n};\n\nexport type PublishFileResult = {\n\tipfsHash: string;\n};\n\nexport const publishFileToIpfs = async ({\n\tauth,\n\titem,\n}: {\n\tauth: PinataAuth;\n\titem: {\n\t\tname: string;\n\t\tfilePath: string;\n\t};\n}): Promise<PublishFileResult> => {\n\t// The data api to check for existing file is limited to 30 requests per minute\n\t// While uploading allows 180 requests per minute\n\t// i.e. it's faster to just upload again\n\n\t// // Skip if already pinned\n\t// const { isPinned, ipfsHash } = await checkIfFileIsPinned({ auth, item });\n\t// if (isPinned) {\n\t// \treturn {\n\t// \t\tipfsHash,\n\t// \t};\n\t// }\n\n\tconst data = new FormData();\n\tdata.append('file', fs.createReadStream(item.filePath));\n\tdata.append(\n\t\t'pinataMetadata',\n\t\tJSON.stringify({\n\t\t\tname: item.name,\n\t\t}),\n\t);\n\n\tconst response = await fetch(`https://api.pinata.cloud/pinning/pinFileToIPFS`, {\n\t\theaders: {\n\t\t\tAuthorization: `Bearer ${auth.pinataJwtToken}`,\n\t\t\t'Content-Type': `multipart/form-data; boundary=${(data as unknown as { _boundary: string })._boundary}`,\n\t\t},\n\t\tbody: data,\n\t\tmethod: 'post',\n\t});\n\n\tif (!response.ok) {\n\t\tthrow new Error(`Failed to upload '${item.name}' to ipfs ${response.statusText}`);\n\t}\n\n\tconst uploadResult = await response.json() as {\n\t\tIpfsHash: string; // This is the IPFS multi-hash provided back for your content,\n\t\tPinSize: string; // This is how large (in bytes) the content you just pinned is,\n\t\tTimestamp: string; // This is the timestamp for your content pinning (represented in ISO 8601 format)\n\t};\n\n\treturn {\n\t\tipfsHash: uploadResult.IpfsHash,\n\t};\n};\n\nconst checkIfFileIsPinned = async ({\n\tauth,\n\titem,\n}: {\n\tauth: PinataAuth;\n\titem: {\n\t\tname: string;\n\t\tfilePath: string;\n\t};\n}) => {\n\tconst ipfsHash = await Hash.of(fs.createReadStream(item.filePath));\n\n\tconst response = await fetch(`https://api.pinata.cloud/data/pinList?status=pinned&hashContains=${ipfsHash}`, {\n\t\theaders: {\n\t\t\tAuthorization: `Bearer ${auth.pinataJwtToken}`,\n\t\t},\n\t\tmethod: 'get',\n\t});\n\n\tif (!response.ok) {\n\t\tthrow new Error(`Failed to query '${item.name}' status from pinata ${response.statusText}`);\n\t}\n\n\tconst pinResult = await response.json() as {\n\t\tcount: number;\n\t\trows: {\n\t\t\tid: string;\n\t\t\tipfs_pin_hash: string;\n\t\t\tsize: number;\n\t\t\tuser_id: string;\n\t\t\tdate_pinned: null | string;\n\t\t\tdate_unpinned: null | string;\n\t\t\tmetadata: {\n\t\t\t\tname: string;\n\t\t\t\tkeyvalues: null | string;\n\t\t\t};\n\t\t\tregions: {\n\t\t\t\tregionId: string;\n\t\t\t\tcurrentReplicationCount: number;\n\t\t\t\tdesiredReplicationCount: number;\n\t\t\t}[];\n\t\t}[];\n\t};\n\n\tconst isPinned = pinResult.rows.some(x =>\n\t\tx.ipfs_pin_hash === ipfsHash\n\t\t&& x.date_pinned\n\t\t&& !x.date_unpinned\n\t);\n\n\treturn {\n\t\tisPinned,\n\t\tipfsHash,\n\t};\n};\n","export async function delay(timeout: number): Promise<void> {\n\treturn await new Promise(resolve => {\n\t\tsetTimeout(resolve, timeout);\n\t});\n}\n\nexport const createProcessBackoffController = ({\n\tretryCount = 5,\n\ttargetRequestsPerMinute = 180,\n}: {\n\tretryCount?: number;\n\ttargetRequestsPerMinute?: number;\n}) => {\n\tlet averageTimePerRequest = 5000;\n\tlet targetTimePerRequest = 60000 / targetRequestsPerMinute;\n\tlet lastTime = Date.now();\n\n\tconst processWithBackoff = async <TResult>(process: () => Promise<TResult>) => {\n\t\tlet attempt = 0;\n\t\tlet lastError = undefined as unknown;\n\t\twhile (attempt < retryCount) {\n\t\t\ttry {\n\t\t\t\tlet delayTimeMs = Math.max(10, targetTimePerRequest - averageTimePerRequest);\n\n\t\t\t\t// Partially randomized delay to ensure parallel requests don't line up\n\t\t\t\tawait delay(Math.floor(delayTimeMs * (1 + 0.5 * Math.random())));\n\n\t\t\t\tconst result = await process();\n\n\t\t\t\tconst timeNow = Date.now();\n\t\t\t\tconst timeElapsed = timeNow - lastTime;\n\t\t\t\tlastTime = timeNow;\n\n\t\t\t\t// Running average\n\t\t\t\taverageTimePerRequest = averageTimePerRequest * 0.97 + timeElapsed * 0.03;\n\n\t\t\t\treturn result;\n\t\t\t} catch (err) {\n\t\t\t\tlastError = err;\n\t\t\t}\n\n\t\t\t// Quickly increase time to wait if failure (allow negatives to wait longer than target)\n\t\t\taverageTimePerRequest -= (attempt + 1) * 1000;\n\t\t\tattempt++;\n\t\t}\n\n\t\t// All attempts failed\n\t\tthrow lastError;\n\t};\n\n\treturn {\n\t\tprocessWithBackoff,\n\t};\n};\n"],"names":[],"version":3,"file":"index.js.map","sourceRoot":"../"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@taqueria/plugin-ipfs-pinata",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"description": "A plugin for Taqueria providing ipfs publishing and pinning using the Pinata service",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"taqueria",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"directory": "taqueria-plugin-ipfs-pinata"
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"@taqueria/node-sdk": "^0.
|
|
36
|
+
"@taqueria/node-sdk": "^0.8.0",
|
|
37
37
|
"dotenv": "^16.0.0",
|
|
38
38
|
"form-data": "^4.0.0",
|
|
39
39
|
"ipfs-only-hash": "^4.0.0",
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
43
43
|
"@types/node-fetch": "^2.6.1",
|
|
44
|
-
"parcel": "
|
|
44
|
+
"parcel": "2.6.1",
|
|
45
45
|
"typescript": "4.7.2"
|
|
46
46
|
}
|
|
47
47
|
}
|