pauls-dat-api
Version:
Library of functions that make working with dat / hyperdrive easier.
392 lines (244 loc) • 23.3 kB
JavaScript
'use strict';function _asyncToGenerator(fn) {return function () {var gen = fn.apply(this, arguments);return new Promise(function (resolve, reject) {function step(key, arg) {try {var info = gen[key](arg);var value = info.value;} catch (error) {reject(error);return;}if (info.done) {resolve(value);} else {return Promise.resolve(value).then(function (value) {step("next", value);}, function (err) {step("throw", err);});}}return step("next");});};}var assert = require('assert');
var path = require('path');
var fse = require('fs-extra');
var pump = require('pump');
var match = require('anymatch');var _require =
require('beaker-error-constants'),ArchiveNotWritableError = _require.ArchiveNotWritableError,SourceNotFoundError = _require.SourceNotFoundError,DestDirectoryNotEmpty = _require.DestDirectoryNotEmpty,ParentFolderDoesntExistError = _require.ParentFolderDoesntExistError;var _require2 =
require('./common'),maybe = _require2.maybe;var _require3 =
require('./lookup'),stat = _require3.stat;var _require4 =
require('./read'),readdir = _require4.readdir;var _require5 =
require('./write'),mkdir = _require5.mkdir;var _require6 =
require('./delete'),unlink = _require6.unlink,rmdir = _require6.rmdir;
var DEFAULT_IGNORE = ['.dat', '**/.dat', '.git', '**/.git'];
// copy files from the filesystem into an archive
function exportFilesystemToArchive(opts, cb) {
return maybe(cb, _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee4() {var srcPath, dstArchive, dstPath, ignore, inplaceImport, dryRun, srcStat, dstStat, statThenExport, exportFile, exportDirectory, stats;return regeneratorRuntime.wrap(function _callee4$(_context4) {while (1) {switch (_context4.prev = _context4.next) {case 0:
assert(opts && typeof opts === 'object', 'opts object is required');
// core arguments, srcPath and dstArchive
srcPath = opts.srcPath;
dstArchive = opts.dstArchive;
assert(srcPath && typeof srcPath === 'string', 'srcPath is required');
assert(dstArchive && typeof dstArchive === 'object', 'dstArchive is required');
// options
dstPath = typeof opts.dstPath === 'string' ? opts.dstPath : '/';
ignore = Array.isArray(opts.ignore) ? opts.ignore : DEFAULT_IGNORE;
inplaceImport = opts.inplaceImport === true;
dryRun = opts.dryRun === true;
// ensure we have the archive's private key
if (!(!dstArchive.writable && !dryRun)) {_context4.next = 11;break;}throw (
new ArchiveNotWritableError());case 11:_context4.prev = 11;_context4.next = 14;return (
fse.stat(srcPath));case 14:srcStat = _context4.sent;_context4.next = 20;break;case 17:_context4.prev = 17;_context4.t0 = _context4['catch'](11);throw (
new SourceNotFoundError(_context4.t0.toString()));case 20:
// if reading from a directory, and not doing an implace-import,
// then put the dstPath at a subpath so that the folder's contents dont
// get imported in-place into the archive
if (srcStat.isDirectory() && !inplaceImport) {
dstPath = path.join(dstPath, path.basename(srcPath));
}
// make sure the destination is a folder
_context4.prev = 21;_context4.next = 24;return (
stat(dstArchive, dstPath));case 24:dstStat = _context4.sent;_context4.next = 29;break;case 27:_context4.prev = 27;_context4.t1 = _context4['catch'](21);case 29:if (
dstStat) {_context4.next = 38;break;}_context4.prev = 30;_context4.next = 33;return (
stat(dstArchive, path.dirname(dstPath)));case 33:dstStat = _context4.sent;_context4.next = 38;break;case 36:_context4.prev = 36;_context4.t2 = _context4['catch'](30);case 38:if (!(
!dstStat || !dstStat.isDirectory())) {_context4.next = 40;break;}throw (
new ParentFolderDoesntExistError());case 40:
// dont overwrite directories with files
if (!srcStat.isDirectory() && dstStat.isDirectory()) {
dstPath = path.join(dstPath, path.basename(srcPath));
}
statThenExport = function () {var _ref2 = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee(srcPath, dstPath) {var srcStat;return regeneratorRuntime.wrap(function _callee$(_context) {while (1) {switch (_context.prev = _context.next) {case 0:if (!(
ignore && match(ignore, srcPath))) {_context.next = 2;break;}return _context.abrupt('return');case 2:_context.next = 4;return (
fse.stat(srcPath));case 4:srcStat = _context.sent;if (!
srcStat.isFile()) {_context.next = 10;break;}_context.next = 8;return (
exportFile(srcPath, srcStat, dstPath));case 8:_context.next = 13;break;case 10:if (!
srcStat.isDirectory()) {_context.next = 13;break;}_context.next = 13;return (
exportDirectory(srcPath, dstPath));case 13:case 'end':return _context.stop();}}}, _callee, this);}));return function statThenExport(_x, _x2) {return _ref2.apply(this, arguments);};}();
exportFile = function () {var _ref3 = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee2(srcPath, srcStat, dstPath) {var dstFileStats;return regeneratorRuntime.wrap(function _callee2$(_context2) {while (1) {switch (_context2.prev = _context2.next) {case 0:
// fetch dest stats
dstFileStats = null;_context2.prev = 1;_context2.next = 4;return (
stat(dstArchive, dstPath));case 4:dstFileStats = _context2.sent;_context2.next = 9;break;case 7:_context2.prev = 7;_context2.t0 = _context2['catch'](1);case 9:
// track the stats
stats.fileCount++;
stats.totalSize += srcStat.size || 0;if (!
dstFileStats) {_context2.next = 23;break;}if (!
dstFileStats.isDirectory()) {_context2.next = 20;break;}if (
dryRun) {_context2.next = 16;break;}_context2.next = 16;return rmdir(dstArchive, dstPath, { recursive: true });case 16:
stats.removedFolders.push(dstPath);
stats.addedFiles.push(dstPath);_context2.next = 21;break;case 20:
stats.updatedFiles.push(dstPath);case 21:_context2.next = 24;break;case 23:
stats.addedFiles.push(dstPath);case 24:if (!
dryRun) {_context2.next = 26;break;}return _context2.abrupt('return');case 26:return _context2.abrupt('return',
new Promise(function (resolve, reject) {
pump(
fse.createReadStream(srcPath),
dstArchive.createWriteStream(dstPath),
function (err) {
if (err) reject(err);else
resolve();
});
}));case 27:case 'end':return _context2.stop();}}}, _callee2, this, [[1, 7]]);}));return function exportFile(_x3, _x4, _x5) {return _ref3.apply(this, arguments);};}();
exportDirectory = function () {var _ref4 = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee3(srcPath, dstPath) {var dstStat, fileNames, promises;return regeneratorRuntime.wrap(function _callee3$(_context3) {while (1) {switch (_context3.prev = _context3.next) {case 0:_context3.prev = 0;_context3.next = 3;return (
stat(dstArchive, dstPath));case 3:dstStat = _context3.sent;_context3.next = 8;break;case 6:_context3.prev = 6;_context3.t0 = _context3['catch'](0);case 8:if (
dstStat) {_context3.next = 15;break;}if (
dryRun) {_context3.next = 12;break;}_context3.next = 12;return mkdir(dstArchive, dstPath);case 12:
stats.addedFolders.push(dstPath);_context3.next = 25;break;case 15:if (!
dstStat.isFile()) {_context3.next = 25;break;}if (
dryRun) {_context3.next = 21;break;}_context3.next = 19;return (
unlink(dstArchive, dstPath));case 19:_context3.next = 21;return (
mkdir(dstArchive, dstPath));case 21:
stats.addedFolders.push(dstPath);
stats.removedFiles.push(dstPath);if (!
dryRun) {_context3.next = 25;break;}return _context3.abrupt('return');case 25:_context3.next = 27;return (
fse.readdir(srcPath));case 27:fileNames = _context3.sent;
// recurse into each
promises = fileNames.map(function (name) {
return statThenExport(path.join(srcPath, name), path.join(dstPath, name));
});_context3.next = 31;return (
Promise.all(promises));case 31:case 'end':return _context3.stop();}}}, _callee3, this, [[0, 6]]);}));return function exportDirectory(_x6, _x7) {return _ref4.apply(this, arguments);};}();
// recursively export
stats = {
addedFiles: [],
addedFolders: [],
updatedFiles: [],
removedFiles: [],
removedFolders: [],
skipCount: 0,
fileCount: 0,
totalSize: 0 };_context4.next = 47;return (
statThenExport(srcPath, dstPath));case 47:return _context4.abrupt('return',
stats);case 48:case 'end':return _context4.stop();}}}, _callee4, this, [[11, 17], [21, 27], [30, 36]]);})));
}
// copy files from an archive into the filesystem
function exportArchiveToFilesystem(opts, cb) {
return maybe(cb, _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee8() {var srcArchive, dstPath, srcPath, overwriteExisting, skipUndownloadedFiles, ignore, files, statThenExport, exportFile, exportDirectory, stats;return regeneratorRuntime.wrap(function _callee8$(_context8) {while (1) {switch (_context8.prev = _context8.next) {case 0:
assert(opts && typeof opts === 'object', 'opts object is required');
// core arguments, dstPath and srcArchive
srcArchive = opts.srcArchive;
dstPath = opts.dstPath;
assert(srcArchive && typeof srcArchive === 'object', 'srcArchive is required');
assert(dstPath && typeof dstPath === 'string', 'dstPath is required');
// options
srcPath = typeof opts.srcPath === 'string' ? opts.srcPath : '/';
overwriteExisting = opts.overwriteExisting === true;
skipUndownloadedFiles = opts.skipUndownloadedFiles === true;
ignore = Array.isArray(opts.ignore) ? opts.ignore : DEFAULT_IGNORE;
// abort if nonempty and not overwriting existing
if (overwriteExisting) {_context8.next = 21;break;}
files = void 0;_context8.prev = 11;_context8.next = 14;return (
fse.readdir(dstPath));case 14:files = _context8.sent;_context8.next = 19;break;case 17:_context8.prev = 17;_context8.t0 = _context8['catch'](11);case 19:if (!(
files && files.length > 0)) {_context8.next = 21;break;}throw (
new DestDirectoryNotEmpty());case 21:
statThenExport = function () {var _ref6 = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee5(srcPath, dstPath) {var srcStat;return regeneratorRuntime.wrap(function _callee5$(_context5) {while (1) {switch (_context5.prev = _context5.next) {case 0:if (!(
ignore && match(ignore, srcPath))) {_context5.next = 2;break;}return _context5.abrupt('return');case 2:_context5.next = 4;return (
stat(srcArchive, srcPath));case 4:srcStat = _context5.sent;if (!
srcStat.isFile()) {_context5.next = 10;break;}_context5.next = 8;return (
exportFile(srcPath, srcStat, dstPath));case 8:_context5.next = 13;break;case 10:if (!
srcStat.isDirectory()) {_context5.next = 13;break;}_context5.next = 13;return (
exportDirectory(srcPath, dstPath));case 13:case 'end':return _context5.stop();}}}, _callee5, this);}));return function statThenExport(_x8, _x9) {return _ref6.apply(this, arguments);};}();
exportFile = function () {var _ref7 = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee6(srcPath, srcStat, dstPath) {var dstFileStats;return regeneratorRuntime.wrap(function _callee6$(_context6) {while (1) {switch (_context6.prev = _context6.next) {case 0:if (!(
skipUndownloadedFiles && srcStat.downloaded < srcStat.blocks)) {_context6.next = 2;break;}return _context6.abrupt('return');case 2:
// fetch dest stats
dstFileStats = null;_context6.prev = 3;_context6.next = 6;return (
fse.stat(dstPath));case 6:dstFileStats = _context6.sent;_context6.next = 11;break;case 9:_context6.prev = 9;_context6.t0 = _context6['catch'](3);case 11:
// track the stats
stats.fileCount++;
stats.totalSize += srcStat.size || 0;if (!
dstFileStats) {_context6.next = 23;break;}if (!
dstFileStats.isDirectory()) {_context6.next = 20;break;}_context6.next = 17;return (
fse.remove(dstPath));case 17:
stats.addedFiles.push(dstPath);_context6.next = 21;break;case 20:
stats.updatedFiles.push(dstPath);case 21:_context6.next = 24;break;case 23:
stats.addedFiles.push(dstPath);case 24:return _context6.abrupt('return',
new Promise(function (resolve, reject) {
pump(
srcArchive.createReadStream(srcPath),
fse.createWriteStream(dstPath),
function (err) {
if (err) reject(err);else
resolve();
});
}));case 25:case 'end':return _context6.stop();}}}, _callee6, this, [[3, 9]]);}));return function exportFile(_x10, _x11, _x12) {return _ref7.apply(this, arguments);};}();
exportDirectory = function () {var _ref8 = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee7(srcPath, dstPath) {var fileNames, promises;return regeneratorRuntime.wrap(function _callee7$(_context7) {while (1) {switch (_context7.prev = _context7.next) {case 0:_context7.next = 2;return (
fse.ensureDir(dstPath));case 2:_context7.next = 4;return (
readdir(srcArchive, srcPath));case 4:fileNames = _context7.sent;
// recurse into each
promises = fileNames.map(function (name) {
return statThenExport(path.join(srcPath, name), path.join(dstPath, name));
});_context7.next = 8;return (
Promise.all(promises));case 8:case 'end':return _context7.stop();}}}, _callee7, this);}));return function exportDirectory(_x13, _x14) {return _ref8.apply(this, arguments);};}();
// recursively export
stats = { addedFiles: [], updatedFiles: [], skipCount: 0, fileCount: 0, totalSize: 0 };_context8.next = 27;return (
statThenExport(srcPath, dstPath));case 27:return _context8.abrupt('return',
stats);case 28:case 'end':return _context8.stop();}}}, _callee8, this, [[11, 17]]);})));
}
// copy files from one archive into another
function exportArchiveToArchive(opts, cb) {
return maybe(cb, _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee12() {var srcArchive, dstArchive, srcPath, dstPath, skipUndownloadedFiles, ignore, dstStat, statThenExport, exportFile, exportDirectory, stats;return regeneratorRuntime.wrap(function _callee12$(_context12) {while (1) {switch (_context12.prev = _context12.next) {case 0:
assert(opts && typeof opts === 'object', 'opts object is required');
// core arguments, dstArchive and srcArchive
srcArchive = opts.srcArchive;
dstArchive = opts.dstArchive;
assert(srcArchive && typeof srcArchive === 'object', 'srcArchive is required');
assert(dstArchive && typeof dstArchive === 'object', 'dstArchive is required');
// options
srcPath = typeof opts.srcPath === 'string' ? opts.srcPath : '/';
dstPath = typeof opts.dstPath === 'string' ? opts.dstPath : '/';
skipUndownloadedFiles = opts.skipUndownloadedFiles === true;
ignore = Array.isArray(opts.ignore) ? opts.ignore : DEFAULT_IGNORE;
// ensure we have the archive's private key
if (dstArchive.writable) {_context12.next = 11;break;}throw (
new ArchiveNotWritableError());case 11:_context12.prev = 11;_context12.next = 14;return (
stat(dstArchive, dstPath));case 14:dstStat = _context12.sent;_context12.next = 19;break;case 17:_context12.prev = 17;_context12.t0 = _context12['catch'](11);case 19:if (
dstStat) {_context12.next = 28;break;}_context12.prev = 20;_context12.next = 23;return (
stat(dstArchive, path.dirname(dstPath)));case 23:dstStat = _context12.sent;_context12.next = 28;break;case 26:_context12.prev = 26;_context12.t1 = _context12['catch'](20);case 28:if (!(
!dstStat || !dstStat.isDirectory())) {_context12.next = 30;break;}throw (
new ParentFolderDoesntExistError());case 30:
statThenExport = function () {var _ref10 = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee9(srcPath, dstPath) {var srcStat;return regeneratorRuntime.wrap(function _callee9$(_context9) {while (1) {switch (_context9.prev = _context9.next) {case 0:if (!(
ignore && match(ignore, srcPath))) {_context9.next = 2;break;}return _context9.abrupt('return');case 2:_context9.next = 4;return (
stat(srcArchive, srcPath));case 4:srcStat = _context9.sent;if (!
srcStat.isFile()) {_context9.next = 10;break;}_context9.next = 8;return (
exportFile(srcPath, srcStat, dstPath));case 8:_context9.next = 13;break;case 10:if (!
srcStat.isDirectory()) {_context9.next = 13;break;}_context9.next = 13;return (
exportDirectory(srcPath, dstPath));case 13:case 'end':return _context9.stop();}}}, _callee9, this);}));return function statThenExport(_x15, _x16) {return _ref10.apply(this, arguments);};}();
exportFile = function () {var _ref11 = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee10(srcPath, srcStat, dstPath) {var dstFileStats;return regeneratorRuntime.wrap(function _callee10$(_context10) {while (1) {switch (_context10.prev = _context10.next) {case 0:if (!(
skipUndownloadedFiles && srcStat.downloaded < srcStat.blocks)) {_context10.next = 2;break;}return _context10.abrupt('return');case 2:
// fetch dest stats
dstFileStats = null;_context10.prev = 3;_context10.next = 6;return (
stat(dstArchive, dstPath));case 6:dstFileStats = _context10.sent;_context10.next = 11;break;case 9:_context10.prev = 9;_context10.t0 = _context10['catch'](3);case 11:
// track the stats
stats.fileCount++;
stats.totalSize += srcStat.size || 0;if (!
dstFileStats) {_context10.next = 23;break;}if (!
dstFileStats.isDirectory()) {_context10.next = 20;break;}_context10.next = 17;return (
rmdir(dstArchive, dstPath, { recursive: true }));case 17:
stats.addedFiles.push(dstPath);_context10.next = 21;break;case 20:
stats.updatedFiles.push(dstPath);case 21:_context10.next = 24;break;case 23:
stats.addedFiles.push(dstPath);case 24:return _context10.abrupt('return',
new Promise(function (resolve, reject) {
pump(
srcArchive.createReadStream(srcPath),
dstArchive.createWriteStream(dstPath),
function (err) {
if (err) reject(err);else
resolve();
});
}));case 25:case 'end':return _context10.stop();}}}, _callee10, this, [[3, 9]]);}));return function exportFile(_x17, _x18, _x19) {return _ref11.apply(this, arguments);};}();
exportDirectory = function () {var _ref12 = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee11(srcPath, dstPath) {var dstStat, fileNames, promises;return regeneratorRuntime.wrap(function _callee11$(_context11) {while (1) {switch (_context11.prev = _context11.next) {case 0:_context11.prev = 0;_context11.next = 3;return (
stat(dstArchive, dstPath));case 3:dstStat = _context11.sent;_context11.next = 8;break;case 6:_context11.prev = 6;_context11.t0 = _context11['catch'](0);case 8:if (
dstStat) {_context11.next = 13;break;}_context11.next = 11;return (
mkdir(dstArchive, dstPath));case 11:_context11.next = 18;break;case 13:if (!
dstStat.isFile()) {_context11.next = 18;break;}_context11.next = 16;return (
unlink(dstArchive, dstPath));case 16:_context11.next = 18;return (
mkdir(dstArchive, dstPath));case 18:_context11.next = 20;return (
readdir(srcArchive, srcPath));case 20:fileNames = _context11.sent;
// recurse into each
promises = fileNames.map(function (name) {
return statThenExport(path.join(srcPath, name), path.join(dstPath, name));
});_context11.next = 24;return (
Promise.all(promises));case 24:case 'end':return _context11.stop();}}}, _callee11, this, [[0, 6]]);}));return function exportDirectory(_x20, _x21) {return _ref12.apply(this, arguments);};}();
// recursively export
stats = { addedFiles: [], updatedFiles: [], skipCount: 0, fileCount: 0, totalSize: 0 };_context12.next = 36;return (
statThenExport(srcPath, dstPath));case 36:return _context12.abrupt('return',
stats);case 37:case 'end':return _context12.stop();}}}, _callee12, this, [[11, 17], [20, 26]]);})));
}
module.exports = { exportFilesystemToArchive, exportArchiveToFilesystem, exportArchiveToArchive };