From d8544a18d4ba5f90e37c9aceb586ba3378612d6d Mon Sep 17 00:00:00 2001 From: qgustavor Date: Thu, 18 Apr 2024 15:13:13 -0300 Subject: [PATCH 1/2] Fix node-fetch issues --- lib/api.mjs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/lib/api.mjs b/lib/api.mjs index 9ce0818..da17ff0 100644 --- a/lib/api.mjs +++ b/lib/api.mjs @@ -1,7 +1,6 @@ import { EventEmitter } from 'events' import { Agent as HttpAgent } from 'http' import { Agent as HttpsAgent } from 'https' -import fetch from 'node-fetch' import AbortController from './abort-controller-polyfill.mjs' import { createPromise } from './util.mjs' @@ -63,7 +62,7 @@ class API extends EventEmitter { this.closed = false } - defaultFetch (url, opts) { + async defaultFetch (url, opts) { if (!opts) opts = {} if (!opts.agent) { opts.agent = url => url.protocol === 'http:' ? this.httpAgent : this.httpsAgent @@ -74,7 +73,16 @@ class API extends EventEmitter { if (!opts.headers['user-agent']) opts.headers['user-agent'] = this.userAgent } - return fetch(url, opts) + if (!API.fetchModule) { + if (typeof globalThis.fetch === 'function') { + API.fetchModule = globalThis.fetch + } else { + const nodeFetch = await import('node-fetch') + API.fetchModule = nodeFetch.fetch + } + } + + return API.fetchModule(url, opts) } request (json, originalCb, retryno = 0) { From ecf298da49b2dfcd00b8841c8cd88dc709429bf6 Mon Sep 17 00:00:00 2001 From: qgustavor Date: Thu, 18 Apr 2024 15:13:34 -0300 Subject: [PATCH 2/2] Add find, filter and navigate --- lib/file.mjs | 60 +++++++++++++++++++++++++++++++++++++++++++ lib/storage.mjs | 24 +++++++++++++++++ test/storage.test.mjs | 40 +++++++++++++++++++++++++++++ 3 files changed, 124 insertions(+) diff --git a/lib/file.mjs b/lib/file.mjs index 25e41db..fa86353 100644 --- a/lib/file.mjs +++ b/lib/file.mjs @@ -424,6 +424,66 @@ class File extends EventEmitter { return promise } + find (query, deep) { + if (!this.children) throw Error('You can only call .find on directories') + + if (typeof query === 'string') { + const queryString = query + query = file => file.name === queryString + } else if (Array.isArray(query)) { + const queryArray = query + query = file => queryArray.includes(file.name) + } + if (typeof query !== 'function') { + throw Error('Query must be a file matching function, an array of valid file names or a string with a file name') + } + + return this.children.reduce((result, entry) => { + if (result) return result + if (entry.children) { + return deep ? entry.find(query, deep) : null + } + return query(entry) ? entry : null + }, null) + } + + filter (query, deep) { + if (!this.children) throw Error('You can only call .filter on directories') + + if (typeof query === 'string') { + const queryString = query + query = file => file.name === queryString + } else if (Array.isArray(query)) { + const queryArray = query + query = file => queryArray.includes(file.name) + } + if (typeof query !== 'function') { + throw Error('Query must be a file matching function, an array of valid file names or a string with a file name') + } + + return this.children.reduce((results, entry) => { + if (entry.children) { + if (deep) return results.concat(entry.find(query, deep)) + return results + } + return query(entry) ? results.concat(entry) : results + }, []) + } + + navigate (query, deep) { + if (!this.children) throw Error('You can only call .navigate on directories') + + if (typeof query === 'string') { + query = query.split('/') + } else if (!Array.isArray(query)) { + throw Error('Query must be an array or a string') + } + + return query.reduce((node, name) => { + return node && node.children && node.children.find(e => e.name === name) + }, this) + } + static fromURL (opt, extraOpt = {}) { if (typeof opt === 'object') { // todo: warn to use File directly diff --git a/lib/storage.mjs b/lib/storage.mjs index 35a2b10..a9cfe83 100644 --- a/lib/storage.mjs +++ b/lib/storage.mjs @@ -279,6 +279,30 @@ class Storage extends EventEmitter { return this.root.upload(opt, buffer, cb) } + // alternative to this.root.find + find (query, deep) { + if (this.status !== 'ready') { + throw Error('storage is not ready') + } + return this.root.find(query, deep) + } + + // alternative to this.root.filter + filter (query, deep) { + if (this.status !== 'ready') { + throw Error('storage is not ready') + } + return this.root.filter(query, deep) + } + + // alternative to this.root.navigate + navigate (query, deep) { + if (this.status !== 'ready') { + throw Error('storage is not ready') + } + return this.root.navigate(query, deep) + } + close (cb) { // Does not handle still connecting or incomplete streams this.status = 'closed' diff --git a/test/storage.test.mjs b/test/storage.test.mjs index 47e4dcb..b044dae 100644 --- a/test/storage.test.mjs +++ b/test/storage.test.mjs @@ -526,6 +526,46 @@ test.serial('Should stream as upload arguments', async t => { t.is(file.size, dataSize) }) +test.serial('Should find files using functions', t => { + const matchingFile = storage.find(e => e.name.includes('test file streams')) + t.is(matchingFile.size, 2097152) +}) + +test.serial('Should find files using string', t => { + const matchingFile = storage.find('file in folder 2', true) + t.is(matchingFile.size, 16) +}) + +test.serial('Should find files using arrays', t => { + const matchingFile = storage.find(['file in folder 2'], true) + t.is(matchingFile.size, 16) +}) + +test.serial('Should filter files using functions', t => { + const matchingFiles = storage.filter(e => e.name.includes('test file streams')) + t.is(matchingFiles.length, 4) +}) + +test.serial('Should filter files using string', t => { + const matchingFiles = storage.filter('file in folder 2', true) + t.is(matchingFiles.length, 1) +}) + +test.serial('Should filter files using arrays', t => { + const matchingFiles = storage.filter(['test file streams', 'test file streams 2', 'file in folder 2'], true) + t.is(matchingFiles.length, 5) +}) + +test.serial('Should navigate to files using arrays', t => { + const matchingFile = storage.navigate(['test folder', 'test folder 2', 'file in folder 2']) + t.is(matchingFile.size, 16) +}) + +test.serial('Should navigate to files using strings', t => { + const matchingFile = storage.navigate('test folder/test folder 2/file in folder 2') + t.is(matchingFile.size, 16) +}) + test.serial('Should logout from MEGA', t => { return new Promise((resolve, reject) => { storage.close((error) => {