diff --git a/.eslintignore b/.eslintignore deleted file mode 100644 index 1123264..0000000 --- a/.eslintignore +++ /dev/null @@ -1,3 +0,0 @@ -node_modules/ -coverage/ -.nyc_output/ \ No newline at end of file diff --git a/examples/download.js b/examples/download.js index 7f0b6f3..42b5ffb 100644 --- a/examples/download.js +++ b/examples/download.js @@ -1,8 +1,9 @@ -import fs from 'fs'; +import fs from 'node:fs'; -import { download } from '../index'; +import { download } from '../index.js'; const { API_TOKEN = '' } = process.env; const fileToSave = fs.createWriteStream('./Mountains.jpg'); -download(API_TOKEN, 'disk:/Горы.jpg', (download) => download.pipe(fileToSave)); +const stream = await download(API_TOKEN, 'disk:/Горы.jpg'); +stream.pipe(fileToSave); diff --git a/examples/upload.js b/examples/upload.js index 2900057..e03bf63 100644 --- a/examples/upload.js +++ b/examples/upload.js @@ -1,15 +1,20 @@ -import fs from 'fs'; -import path from 'path'; +import fs from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; -import { upload } from '../index'; +import { upload } from '../index.js'; +const __dirname = path.dirname(fileURLToPath(import.meta.url)); const { API_TOKEN = '' } = process.env; const fileToUpload = fs.createReadStream(path.join(__dirname, 'upload.js')); -upload( - API_TOKEN, - 'disk:/Приложения/ya-disk-api/upload.js', - true, - (stream) => fileToUpload.pipe(stream), - (err) => process.stderr.write(err) -); +try { + const stream = await upload( + API_TOKEN, + 'disk:/Приложения/ya-disk-api/upload.js', + true + ); + fileToUpload.pipe(stream); +} catch (err) { + process.stderr.write(err); +} diff --git a/features/upload-and-download-file.feature.js b/features/upload-and-download-file.feature.js index 8d0d1a1..50036cc 100644 --- a/features/upload-and-download-file.feature.js +++ b/features/upload-and-download-file.feature.js @@ -1,19 +1,22 @@ -const fs = require('fs'); -const https = require('https'); -const path = require('path'); -const assert = require('node:assert/strict'); -const { after, before, describe, it } = require('node:test'); +import fs from 'node:fs'; +import https from 'node:https'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; +import assert from 'node:assert/strict'; +import { after, before, describe, it } from 'node:test'; -const { meta, resources } = require('ya-disk'); +import { meta, resources } from 'ya-disk'; const { API_TOKEN } = process.env; -const { upload: uploadStream, download: downloadStream } = require('../index'); +const { upload: uploadStream, download: downloadStream } = + await import('../index.js'); const shouldRun = API_TOKEN && process.env.GITHUB_ACTOR !== 'dependabot[bot]'; const describeOrSkip = shouldRun ? describe : describe.skip; +const yaDiskPath = fileURLToPath(import.meta.resolve('ya-disk')); const localFileName = path.resolve( - require.resolve('ya-disk').split('node_modules/')[0], + yaDiskPath.split('node_modules/')[0], 'package.json' ); const localReadStream = fs.createReadStream(localFileName); @@ -21,51 +24,33 @@ const { size: localFileSize } = fs.statSync(localFileName); const remoteFileName = `package_${Math.round(Math.random() * 100)}.json`; const remoteFilePath = `disk:/${remoteFileName}`; -function getRemoteFileSize(remoteFile) { - return new Promise((resolve, reject) => { - meta.get( - API_TOKEN, - remoteFile, - { fields: 'name,size' }, - ({ size }) => resolve(size), - reject - ); +async function getRemoteFileSize(remoteFile) { + const { size } = await meta.get(API_TOKEN, remoteFile, { + fields: 'name,size' }); + return size; } -function uploadFile(token, filePath, stream) { - return new Promise((resolve, reject) => { - uploadStream( - token, - filePath, - true, - (writeStream) => { - writeStream.on('finish', resolve); - stream.pipe(writeStream); - }, - reject - ); - }); +async function uploadFile(token, filePath, stream) { + const writeStream = await uploadStream(token, filePath, true); + const { promise, resolve, reject } = Promise.withResolvers(); + writeStream.on('finish', resolve); + writeStream.on('error', reject); + stream.pipe(writeStream); + return promise; } -function downloadFile(token, filePath, writeStream) { - return new Promise((resolve, reject) => { - downloadStream( - token, - filePath, - (readStream) => { - writeStream.on('finish', resolve); - readStream.pipe(writeStream); - }, - reject - ); - }); +async function downloadFile(token, filePath, writeStream) { + const readStream = await downloadStream(token, filePath); + const { promise, resolve, reject } = Promise.withResolvers(); + writeStream.on('finish', resolve); + writeStream.on('error', reject); + readStream.pipe(writeStream); + return promise; } -function removeFile(token, filePath) { - return new Promise((resolve) => { - resources.remove(token, filePath, true, resolve, resolve); - }); +async function removeFile(token, filePath) { + await resources.remove(token, filePath, true); } let localWriteStream; diff --git a/index.js b/index.js index ee1ab79..e64ed7f 100644 --- a/index.js +++ b/index.js @@ -1,4 +1,2 @@ -module.exports = { - upload: require('./lib/upload'), - download: require('./lib/download') -}; +export { upload } from './lib/upload.js'; +export { download } from './lib/download.js'; diff --git a/lib/download.js b/lib/download.js index 7a1f7e0..57837bb 100644 --- a/lib/download.js +++ b/lib/download.js @@ -1,18 +1,14 @@ -const { download } = require('ya-disk'); +import { download as yaDiskDownload } from 'ya-disk'; -const followRedirect = require('./followRedirect'); +import { followRedirect } from './followRedirect.js'; /** - * Creates readable stream to download file from the Yadex.Disk + * Creates readable stream to download file from Yandex.Disk * @param {string} token OAuth-token * @param {string} file Path+filename on the storage - * @param {function} [onReady] Callback to be called after stream being ready - * @param {function} [onError] Callback to be called in case of any error + * @returns {Promise} */ -module.exports = (token, file, onReady, onError) => - download.link( - token, - file, - ({ href, method }) => followRedirect(href, method, (req) => onReady(req)), - onError - ); +export async function download(token, file) { + const { href, method } = await yaDiskDownload.link(token, file); + return followRedirect(href, method); +} diff --git a/lib/followRedirect.js b/lib/followRedirect.js index 57fde52..55187db 100644 --- a/lib/followRedirect.js +++ b/lib/followRedirect.js @@ -1,20 +1,23 @@ -const https = require('https'); -const { parse: urlParse } = require('url'); +import https from 'node:https'; -const followRedirect = (url, method = 'GET', onReady) => { - const req = https.request( - Object.assign({}, urlParse(url), { method }), - (res) => { - if (res.statusCode >= 300 && res.statusCode < 400) { - res.destroy(); - followRedirect(res.headers.location, method, onReady); - } else if (res.statusCode === 200 && typeof onReady === 'function') { - onReady(res); - } - } - ); +export function followRedirect(url, method = 'GET') { + return new Promise((resolve, reject) => { + const makeRequest = (url) => { + const req = https.request(url, { method }, (res) => { + if (res.statusCode >= 300 && res.statusCode < 400) { + res.destroy(); + makeRequest(res.headers.location); + } else if (res.statusCode === 200) { + resolve(res); + } else { + reject(new Error(`Unexpected status code: ${res.statusCode}`)); + } + }); - req.end(); -}; + req.on('error', reject); + req.end(); + }; -module.exports = followRedirect; + makeRequest(url); + }); +} diff --git a/lib/upload.js b/lib/upload.js index 89f8cc4..2ca6053 100644 --- a/lib/upload.js +++ b/lib/upload.js @@ -1,21 +1,14 @@ -const https = require('https'); -const { upload } = require('ya-disk'); -const { parse: urlParse } = require('url'); +import https from 'node:https'; +import { upload as yaDiskUpload } from 'ya-disk'; /** - * Create writable stream for uploading file to Yandex.Disk + * Creates writable stream for uploading file to Yandex.Disk * @param {string} token OAuth-token * @param {string} file Path+filename on the storage * @param {boolean} [overwrite=true] Overwrite existing file - * @param {function} onReady Callback to be called after stream being ready - * @param {function} onError Callback to be called in case of any error + * @returns {Promise} */ -module.exports = (token, file, overwrite = true, onReady, onError) => - upload.link( - token, - file, - overwrite, - ({ href, method }) => - onReady(https.request(Object.assign({}, urlParse(href), { method }))), - onError - ); +export async function upload(token, file, overwrite = true) { + const { href, method } = await yaDiskUpload.link(token, file, overwrite); + return https.request(href, { method }); +} diff --git a/package-lock.json b/package-lock.json index 8060b90..8e5fe4b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,10 +9,11 @@ "version": "2.0.0", "license": "MIT", "dependencies": { - "ya-disk": "^3.0.0" + "ya-disk": "5.0.1" }, "devDependencies": { "c8": "12.0.0", + "esbuild": "0.28.1", "husky": "9.1.7", "lint-staged": "17.3.0", "oxfmt": "0.62.0", @@ -32,6 +33,448 @@ "node": ">=18" } }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.1.tgz", + "integrity": "sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.1.tgz", + "integrity": "sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.1.tgz", + "integrity": "sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.1.tgz", + "integrity": "sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.1.tgz", + "integrity": "sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.1.tgz", + "integrity": "sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.1.tgz", + "integrity": "sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.1.tgz", + "integrity": "sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.1.tgz", + "integrity": "sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.1.tgz", + "integrity": "sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.1.tgz", + "integrity": "sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.1.tgz", + "integrity": "sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.1.tgz", + "integrity": "sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.1.tgz", + "integrity": "sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.1.tgz", + "integrity": "sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.1.tgz", + "integrity": "sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.1.tgz", + "integrity": "sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.1.tgz", + "integrity": "sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.1.tgz", + "integrity": "sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.1.tgz", + "integrity": "sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.1.tgz", + "integrity": "sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.1.tgz", + "integrity": "sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.1.tgz", + "integrity": "sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.1.tgz", + "integrity": "sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.1.tgz", + "integrity": "sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.1.tgz", + "integrity": "sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, "node_modules/@istanbuljs/schema": { "version": "0.1.6", "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.6.tgz", @@ -848,6 +1291,48 @@ "dev": true, "license": "MIT" }, + "node_modules/esbuild": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.1.tgz", + "integrity": "sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.28.1", + "@esbuild/android-arm": "0.28.1", + "@esbuild/android-arm64": "0.28.1", + "@esbuild/android-x64": "0.28.1", + "@esbuild/darwin-arm64": "0.28.1", + "@esbuild/darwin-x64": "0.28.1", + "@esbuild/freebsd-arm64": "0.28.1", + "@esbuild/freebsd-x64": "0.28.1", + "@esbuild/linux-arm": "0.28.1", + "@esbuild/linux-arm64": "0.28.1", + "@esbuild/linux-ia32": "0.28.1", + "@esbuild/linux-loong64": "0.28.1", + "@esbuild/linux-mips64el": "0.28.1", + "@esbuild/linux-ppc64": "0.28.1", + "@esbuild/linux-riscv64": "0.28.1", + "@esbuild/linux-s390x": "0.28.1", + "@esbuild/linux-x64": "0.28.1", + "@esbuild/netbsd-arm64": "0.28.1", + "@esbuild/netbsd-x64": "0.28.1", + "@esbuild/openbsd-arm64": "0.28.1", + "@esbuild/openbsd-x64": "0.28.1", + "@esbuild/openharmony-arm64": "0.28.1", + "@esbuild/sunos-x64": "0.28.1", + "@esbuild/win32-arm64": "0.28.1", + "@esbuild/win32-ia32": "0.28.1", + "@esbuild/win32-x64": "0.28.1" + } + }, "node_modules/escalade": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", @@ -1570,11 +2055,12 @@ } }, "node_modules/ya-disk": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ya-disk/-/ya-disk-3.0.1.tgz", - "integrity": "sha512-PtMgI7IoB9Eccq2j+CbasCRF8ZOBWYM9NsZc9h8S1KUOgn+YNxGppuC2rIS8P0LXbW7dLiq1Iavk+nAQwrmUXQ==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ya-disk/-/ya-disk-5.0.1.tgz", + "integrity": "sha512-i8rKkKWPF/nDzz/YPN+oKmpLsvP9mT0Egx/ZkcgfPPgjBdsKm45g2hkO9yjnUj6gOXx500MLWHBcf8HJ/lj0TQ==", + "license": "MIT", "engines": { - "node": ">= 10.0.0" + "node": ">= 22.0.0" } }, "node_modules/yaml": { @@ -1652,6 +2138,188 @@ "integrity": "sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==", "dev": true }, + "@esbuild/aix-ppc64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.1.tgz", + "integrity": "sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ==", + "dev": true, + "optional": true + }, + "@esbuild/android-arm": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.1.tgz", + "integrity": "sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ==", + "dev": true, + "optional": true + }, + "@esbuild/android-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.1.tgz", + "integrity": "sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg==", + "dev": true, + "optional": true + }, + "@esbuild/android-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.1.tgz", + "integrity": "sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng==", + "dev": true, + "optional": true + }, + "@esbuild/darwin-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.1.tgz", + "integrity": "sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q==", + "dev": true, + "optional": true + }, + "@esbuild/darwin-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.1.tgz", + "integrity": "sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ==", + "dev": true, + "optional": true + }, + "@esbuild/freebsd-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.1.tgz", + "integrity": "sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw==", + "dev": true, + "optional": true + }, + "@esbuild/freebsd-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.1.tgz", + "integrity": "sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ==", + "dev": true, + "optional": true + }, + "@esbuild/linux-arm": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.1.tgz", + "integrity": "sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ==", + "dev": true, + "optional": true + }, + "@esbuild/linux-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.1.tgz", + "integrity": "sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g==", + "dev": true, + "optional": true + }, + "@esbuild/linux-ia32": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.1.tgz", + "integrity": "sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w==", + "dev": true, + "optional": true + }, + "@esbuild/linux-loong64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.1.tgz", + "integrity": "sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg==", + "dev": true, + "optional": true + }, + "@esbuild/linux-mips64el": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.1.tgz", + "integrity": "sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ==", + "dev": true, + "optional": true + }, + "@esbuild/linux-ppc64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.1.tgz", + "integrity": "sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ==", + "dev": true, + "optional": true + }, + "@esbuild/linux-riscv64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.1.tgz", + "integrity": "sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ==", + "dev": true, + "optional": true + }, + "@esbuild/linux-s390x": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.1.tgz", + "integrity": "sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag==", + "dev": true, + "optional": true + }, + "@esbuild/linux-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.1.tgz", + "integrity": "sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA==", + "dev": true, + "optional": true + }, + "@esbuild/netbsd-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.1.tgz", + "integrity": "sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw==", + "dev": true, + "optional": true + }, + "@esbuild/netbsd-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.1.tgz", + "integrity": "sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg==", + "dev": true, + "optional": true + }, + "@esbuild/openbsd-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.1.tgz", + "integrity": "sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q==", + "dev": true, + "optional": true + }, + "@esbuild/openbsd-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.1.tgz", + "integrity": "sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw==", + "dev": true, + "optional": true + }, + "@esbuild/openharmony-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.1.tgz", + "integrity": "sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg==", + "dev": true, + "optional": true + }, + "@esbuild/sunos-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.1.tgz", + "integrity": "sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ==", + "dev": true, + "optional": true + }, + "@esbuild/win32-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.1.tgz", + "integrity": "sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA==", + "dev": true, + "optional": true + }, + "@esbuild/win32-ia32": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.1.tgz", + "integrity": "sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg==", + "dev": true, + "optional": true + }, + "@esbuild/win32-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.1.tgz", + "integrity": "sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A==", + "dev": true, + "optional": true + }, "@istanbuljs/schema": { "version": "0.1.6", "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.6.tgz", @@ -2033,6 +2701,40 @@ "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", "dev": true }, + "esbuild": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.1.tgz", + "integrity": "sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw==", + "dev": true, + "requires": { + "@esbuild/aix-ppc64": "0.28.1", + "@esbuild/android-arm": "0.28.1", + "@esbuild/android-arm64": "0.28.1", + "@esbuild/android-x64": "0.28.1", + "@esbuild/darwin-arm64": "0.28.1", + "@esbuild/darwin-x64": "0.28.1", + "@esbuild/freebsd-arm64": "0.28.1", + "@esbuild/freebsd-x64": "0.28.1", + "@esbuild/linux-arm": "0.28.1", + "@esbuild/linux-arm64": "0.28.1", + "@esbuild/linux-ia32": "0.28.1", + "@esbuild/linux-loong64": "0.28.1", + "@esbuild/linux-mips64el": "0.28.1", + "@esbuild/linux-ppc64": "0.28.1", + "@esbuild/linux-riscv64": "0.28.1", + "@esbuild/linux-s390x": "0.28.1", + "@esbuild/linux-x64": "0.28.1", + "@esbuild/netbsd-arm64": "0.28.1", + "@esbuild/netbsd-x64": "0.28.1", + "@esbuild/openbsd-arm64": "0.28.1", + "@esbuild/openbsd-x64": "0.28.1", + "@esbuild/openharmony-arm64": "0.28.1", + "@esbuild/sunos-x64": "0.28.1", + "@esbuild/win32-arm64": "0.28.1", + "@esbuild/win32-ia32": "0.28.1", + "@esbuild/win32-x64": "0.28.1" + } + }, "escalade": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", @@ -2468,9 +3170,9 @@ "dev": true }, "ya-disk": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ya-disk/-/ya-disk-3.0.1.tgz", - "integrity": "sha512-PtMgI7IoB9Eccq2j+CbasCRF8ZOBWYM9NsZc9h8S1KUOgn+YNxGppuC2rIS8P0LXbW7dLiq1Iavk+nAQwrmUXQ==" + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ya-disk/-/ya-disk-5.0.1.tgz", + "integrity": "sha512-i8rKkKWPF/nDzz/YPN+oKmpLsvP9mT0Egx/ZkcgfPPgjBdsKm45g2hkO9yjnUj6gOXx500MLWHBcf8HJ/lj0TQ==" }, "yaml": { "version": "2.9.0", diff --git a/package.json b/package.json index 5745243..ac8d925 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,16 @@ "name": "ya-disk-stream", "version": "2.0.0", "description": "Readable and writable streams for uploading/downloading files from Yandex.Disk", - "main": "index.js", + "type": "module", + "main": "./dist/index.cjs", + "exports": { + ".": { + "require": "./dist/index.cjs", + "import": "./index.js", + "default": "./index.js" + }, + "./package.json": "./package.json" + }, "repository": "git@github.com:RomiC/ya-disk-stream.git", "author": "Roman Charugin ", "license": "MIT", @@ -10,17 +19,19 @@ "node": ">= 22" }, "dependencies": { - "ya-disk": "^3.0.0" + "ya-disk": "5.0.1" }, "devDependencies": { "c8": "12.0.0", + "esbuild": "0.28.1", "husky": "9.1.7", "lint-staged": "17.3.0", "oxfmt": "0.62.0", "oxlint": "1.77.0" }, "scripts": { - "example": "babel-node", + "build": "esbuild --bundle --platform=node --target=node22 --outfile=dist/index.cjs index.js", + "prepublishOnly": "npm run build", "test": "node --env-file-if-exists=.env --test 'specs/*.spec.js' 'features/*.feature.js'", "test:coverage": "c8 --all --include='lib/*.js' --include='index.js' --reporter=text --reporter=lcov --reporter=clover node --env-file-if-exists=.env --test 'specs/*.spec.js' 'features/*.feature.js'", "lint": "oxlint -c oxlint.config.mjs", @@ -30,7 +41,7 @@ "prepare": "husky" }, "lint-staged": { - "*.{js,mjs}": [ + "*.{js,mjs,cjs}": [ "npm run lint:fix", "npm run format" ] diff --git a/specs/download.spec.js b/specs/download.spec.js index de62bea..6befabc 100644 --- a/specs/download.spec.js +++ b/specs/download.spec.js @@ -1,86 +1,75 @@ -const fs = require('fs'); -const path = require('path'); -const https = require('https'); -const { parse: urlParse } = require('url'); -const assert = require('node:assert/strict'); -const { afterEach, beforeEach, describe, mock, test } = require('node:test'); +import https from 'node:https'; +import assert from 'node:assert/strict'; +import { afterEach, describe, mock, test } from 'node:test'; -const yaDisk = require('ya-disk'); -const download = require('../lib/download'); +import { download } from '../lib/download.js'; const token = 'it-is-just-a-token-sample'; const file = 'disk:/file.txt'; const downloadLink = 'https://disk.yandex.ru/download'; const downloadMethod = 'GET'; -const readableStream = fs.createReadStream( - path.join(__dirname, '..', 'package.json') -); - describe('download', () => { - let linkMock; - let onReadyCallback; - let onErrorCallback; - - beforeEach(() => { - onReadyCallback = mock.fn(); - onErrorCallback = mock.fn(); - - linkMock = mock.method( - yaDisk.download, - 'link', - (token, file, success, error) => { - linkMock._onSuccessCallback = success; - linkMock._onErrorCallback = error; - } - ); - }); - afterEach(() => mock.restoreAll()); - test('should call download.link with correct params and fire onReady callback in case of success', () => { - download(token, file, onReadyCallback); - - assert.deepStrictEqual(linkMock.mock.calls[0].arguments.slice(0, 2), [ - token, - file - ]); - assert.strictEqual(typeof linkMock.mock.calls[0].arguments[2], 'function'); + test('should get download link and follow redirect', async () => { + mock.method(globalThis, 'fetch', () => + Promise.resolve({ + ok: true, + status: 200, + text: () => + Promise.resolve( + JSON.stringify({ href: downloadLink, method: downloadMethod }) + ) + }) + ); const httpsRequestMock = mock.method( https, 'request', - (options, callback) => { + (url, options, callback) => { httpsRequestMock._callback = callback; - return { end: () => {} }; + return { end: () => {}, on: () => {} }; } ); - linkMock._onSuccessCallback({ - href: downloadLink, - method: downloadMethod - }); + const promise = download(token, file); - assert.deepStrictEqual( - httpsRequestMock.mock.calls[0].arguments[0], - Object.assign({}, urlParse(downloadLink), { method: downloadMethod }) - ); - - httpsRequestMock._callback({ statusCode: 200, ...readableStream }); + // Wait for the async chain to settle + await new Promise((r) => setTimeout(r, 0)); assert.strictEqual( - onReadyCallback.mock.calls[0].arguments[0].statusCode, - 200 + httpsRequestMock.mock.calls[0].arguments[0], + downloadLink ); - }); + assert.deepStrictEqual(httpsRequestMock.mock.calls[0].arguments[1], { + method: downloadMethod + }); - test('should fire the onError callback in case of error', () => { - const error = new Error('error message'); + httpsRequestMock._callback({ statusCode: 200 }); - download(token, file, onReadyCallback, onErrorCallback); + const stream = await promise; + assert.strictEqual(stream.statusCode, 200); + }); - linkMock._onErrorCallback(error); + test('should reject when ya-disk API call fails', async () => { + mock.method(globalThis, 'fetch', () => + Promise.resolve({ + ok: false, + status: 401, + text: () => + Promise.resolve( + JSON.stringify({ + error: 'UnauthorizedError', + description: 'Not authorized' + }) + ) + }) + ); - assert.deepStrictEqual(onErrorCallback.mock.calls[0].arguments[0], error); + await assert.rejects(download(token, file), { + name: 'UnauthorizedError', + message: 'Not authorized' + }); }); }); diff --git a/specs/followRedirect.spec.js b/specs/followRedirect.spec.js index d916dab..b90d18b 100644 --- a/specs/followRedirect.spec.js +++ b/specs/followRedirect.spec.js @@ -1,21 +1,13 @@ -const { parse: urlParse } = require('url'); -const https = require('https'); -const assert = require('node:assert/strict'); -const { afterEach, describe, mock, test } = require('node:test'); +import https from 'node:https'; +import assert from 'node:assert/strict'; +import { afterEach, describe, mock, test } from 'node:test'; -const followRedirect = require('../lib/followRedirect'); +import { followRedirect } from '../lib/followRedirect.js'; const method = 'GET'; const originalUrl = 'https://yandex.ru/'; -const originalParsedUrl = Object.assign({}, urlParse(originalUrl), { method }); const firstRedirectUrl = 'https://bing.com/'; -const firstRedirectParsedUrl = Object.assign({}, urlParse(firstRedirectUrl), { - method -}); const secondRedirectUrl = 'https://google.com/'; -const secondRedirectParsedUrl = Object.assign({}, urlParse(secondRedirectUrl), { - method -}); const redirectResponse = (url) => ({ statusCode: 302, @@ -33,40 +25,69 @@ describe('followRedirect', () => { afterEach(() => mock.restoreAll()); - test('should follow redirects', () => { - const finalCallback = mock.fn(); - - httpsRequestMock = mock.method(https, 'request', (options, callback) => { - httpsRequestMock._callback = callback; - return { end: () => {} }; - }); + test('should follow redirects', async () => { + httpsRequestMock = mock.method( + https, + 'request', + (url, options, callback) => { + httpsRequestMock._callback = callback; + return { end: () => {}, on: () => {} }; + } + ); - followRedirect(originalUrl, method, finalCallback); + const promise = followRedirect(originalUrl, method); - assert.deepStrictEqual( + assert.strictEqual( httpsRequestMock.mock.calls[0].arguments[0], - originalParsedUrl + originalUrl ); + assert.deepStrictEqual(httpsRequestMock.mock.calls[0].arguments[1], { + method + }); httpsRequestMock._callback(redirectResponse(firstRedirectUrl)); - assert.deepStrictEqual( + assert.strictEqual( httpsRequestMock.mock.calls[1].arguments[0], - firstRedirectParsedUrl + firstRedirectUrl ); + assert.deepStrictEqual(httpsRequestMock.mock.calls[1].arguments[1], { + method + }); httpsRequestMock._callback(redirectResponse(secondRedirectUrl)); - assert.deepStrictEqual( + assert.strictEqual( httpsRequestMock.mock.calls[2].arguments[0], - secondRedirectParsedUrl + secondRedirectUrl ); + assert.deepStrictEqual(httpsRequestMock.mock.calls[2].arguments[1], { + method + }); httpsRequestMock._callback(lastResponse); - assert.deepStrictEqual( - finalCallback.mock.calls[0].arguments[0], - lastResponse + const result = await promise; + + assert.deepStrictEqual(result, lastResponse); + }); + + test('should reject on unexpected status code', async () => { + httpsRequestMock = mock.method( + https, + 'request', + (url, options, callback) => { + httpsRequestMock._callback = callback; + return { end: () => {}, on: () => {} }; + } ); + + const promise = followRedirect(originalUrl, method); + + httpsRequestMock._callback({ statusCode: 500 }); + + await assert.rejects(promise, { + message: 'Unexpected status code: 500' + }); }); }); diff --git a/specs/upload.spec.js b/specs/upload.spec.js index c48932f..732d63d 100644 --- a/specs/upload.spec.js +++ b/specs/upload.spec.js @@ -1,74 +1,64 @@ -const https = require('https'); -const { parse: urlParse } = require('url'); -const assert = require('node:assert/strict'); -const { afterEach, beforeEach, describe, mock, test } = require('node:test'); +import https from 'node:https'; +import assert from 'node:assert/strict'; +import { afterEach, describe, mock, test } from 'node:test'; -const yaDisk = require('ya-disk'); -const upload = require('../lib/upload'); +import { upload } from '../lib/upload.js'; const token = 'it-is-just-a-token-sample'; const file = 'disk:/file.txt'; const overwrite = false; -const uploadLinkResponseMock = { +const uploadLinkResponse = { href: 'https://example.com/', method: 'PUT' }; -const httpsRequestParamsMock = Object.assign( - {}, - urlParse(uploadLinkResponseMock.href), - { method: uploadLinkResponseMock.method } -); describe('upload', () => { - let linkMock; - - beforeEach(() => { - linkMock = mock.method( - yaDisk.upload, - 'link', - (token, file, overwrite, success, error) => { - linkMock._onSuccessCallback = success; - linkMock._onErrorCallback = error; - } - ); - }); - afterEach(() => mock.restoreAll()); - test('should call upload.link with correct params and fire onReady callback in case of success', () => { - const onReadyMock = mock.fn(); - - upload(token, file, overwrite, onReadyMock); + test('should get upload link and create writable stream', async () => { + mock.method(globalThis, 'fetch', () => + Promise.resolve({ + ok: true, + status: 200, + text: () => Promise.resolve(JSON.stringify(uploadLinkResponse)) + }) + ); - assert.deepStrictEqual(linkMock.mock.calls[0].arguments.slice(0, 3), [ - token, - file, - overwrite - ]); - assert.strictEqual(typeof linkMock.mock.calls[0].arguments[3], 'function'); + const httpsRequestMock = mock.method(https, 'request', () => ({ + end: () => {}, + on: () => {} + })); - const httpsRequestMock = mock.method(https, 'request', () => { - return { end: () => {} }; - }); + await upload(token, file, overwrite); - linkMock._onSuccessCallback(uploadLinkResponseMock); - - assert.deepStrictEqual( + assert.strictEqual( httpsRequestMock.mock.calls[0].arguments[0], - httpsRequestParamsMock + uploadLinkResponse.href ); - assert.strictEqual(onReadyMock.mock.callCount(), 1); + assert.deepStrictEqual(httpsRequestMock.mock.calls[0].arguments[1], { + method: uploadLinkResponse.method + }); }); - test('should fire the onError callback in case of error', () => { - const error = new Error('error message'); - const onErrorMock = mock.fn(); - - upload(token, file, overwrite, undefined, onErrorMock); - - linkMock._onErrorCallback(error); + test('should reject when ya-disk API call fails', async () => { + mock.method(globalThis, 'fetch', () => + Promise.resolve({ + ok: false, + status: 401, + text: () => + Promise.resolve( + JSON.stringify({ + error: 'UnauthorizedError', + description: 'Not authorized' + }) + ) + }) + ); - assert.deepStrictEqual(onErrorMock.mock.calls[0].arguments[0], error); + await assert.rejects(upload(token, file, overwrite), { + name: 'UnauthorizedError', + message: 'Not authorized' + }); }); });