pats-dat-api v8.1.0
pauls-dat-api
A library of functions that make working with dat / hyperdrive easier. Includes common operations, and some sugars. These functions were factored out of beaker browser's internal APIs.
All async methods work with callbacks and promises. If no callback is provided, a promise will be returned.
NOTE: this library is written natively for node 7 and above.
To use with node versions lesser than 7 use:
var pda = require('pauls-dat-api/es5');const pda = require('pauls-dat-api')Lookup
stat(archive, name, cb)
archiveHyperdrive archive (object).nameEntry name (string).- Returns a Hyperdrive Stat entry (object).
- Throws NotFoundError
// by name:
var st = await pda.stat(archive, '/dat.json')
st.isDirectory()
st.isFile()
console.log(st) /* =>
Stat {
dev: 0,
nlink: 1,
rdev: 0,
blksize: 0,
ino: 0,
mode: 16877,
uid: 0,
gid: 0,
size: 0,
offset: 0,
blocks: 0,
atime: 2017-04-10T18:59:00.147Z,
mtime: 2017-04-10T18:59:00.147Z,
ctime: 2017-04-10T18:59:00.147Z,
linkname: undefined } */Read
readFile(archive, name, opts, cb)
archiveHyperdrive archive (object).nameEntry path (string).opts. Options (object|string). If a string, will act asopts.encoding.opts.encodingDesired output encoding (string). May be 'binary', 'utf8', 'hex', or 'base64'. Default 'utf8'.- Returns the content of the file in the requested encoding.
- Throws NotFoundError, NotAFileError.
var manifestStr = await pda.readFile(archive, '/dat.json')
var imageBase64 = await pda.readFile(archive, '/favicon.png', 'base64')readdir(archive, path, opts, cb)
archiveHyperdrive archive (object).pathTarget directory path (string).opts.recursiveRead all subfolders and their files as well?- Returns an array of file and folder names.
var listing = await pda.readdir(archive, '/assets')
console.log(listing) // => ['profile.png', 'styles.css']
var listing = await pda.readdir(archive, '/', { recursive: true })
console.log(listing) /* => [
'index.html',
'assets',
'assets/profile.png',
'assets/styles.css'
]*/readSize(archive, path, cb)
archiveHyperdrive archive (object).pathTarget directory path (string).- Returns a number (size in bytes).
This method will recurse on folders.
var size = await pda.readSize(archive, '/assets')
console.log(size) // => 123Write
writeFile(archive, name, data, opts, cb)
archiveHyperdrive archive (object).nameEntry path (string).dataData to write (string|Buffer).opts. Options (object|string). If a string, will act asopts.encoding.opts.encodingDesired file encoding (string). May be 'binary', 'utf8', 'hex', or 'base64'. Default 'utf8' ifdatais a string, 'binary' ifdatais a Buffer.- Throws ArchiveNotWritableError, InvalidPathError, EntryAlreadyExistsError, ParentFolderDoesntExistError, InvalidEncodingError.
await pda.writeFile(archive, '/hello.txt', 'world', 'utf8')
await pda.writeFile(archive, '/profile.png', fs.readFileSync('/tmp/dog.png'))mkdir(archive, name, cb)
archiveHyperdrive archive (object).nameDirectory path (string).- Throws ArchiveNotWritableError, InvalidPathError, EntryAlreadyExistsError, ParentFolderDoesntExistError, InvalidEncodingError.
await pda.mkdir(archive, '/stuff')copy(archive, sourceName, targetName, cb)
archiveHyperdrive archive (object).sourceNamePath to file or directory to copy (string).targetNameWhere to copy the file or folder to (string).- Throws ArchiveNotWritableError, InvalidPathError, EntryAlreadyExistsError, ParentFolderDoesntExistError, InvalidEncodingError.
// copy file:
await pda.copy(archive, '/foo.txt', '/foo.txt.back')
// copy folder:
await pda.copy(archive, '/stuff', '/stuff-copy')rename(archive, sourceName, targetName, cb)
archiveHyperdrive archive (object).sourceNamePath to file or directory to rename (string).targetNameWhat the file or folder should be named (string).- Throws ArchiveNotWritableError, InvalidPathError, EntryAlreadyExistsError, ParentFolderDoesntExistError, InvalidEncodingError.
This is equivalent to moving a file/folder.
// move file:
await pda.rename(archive, '/foo.txt', '/foo.md')
// move folder:
await pda.rename(archive, '/stuff', '/things')Delete
unlink(archive, name, cb)
archiveHyperdrive archive (object).nameEntry path (string).- Throws ArchiveNotWritableError, NotFoundError, NotAFileError
await pda.unlink(archive, '/hello.txt')rmdir(archive, name, opts, cb)
archiveHyperdrive archive (object).nameEntry path (string).opts.recursiveDelete all subfolders and files if the directory is not empty.- Throws ArchiveNotWritableError, NotFoundError, NotAFolderError, DestDirectoryNotEmpty
await pda.rmdir(archive, '/stuff', {recursive: true})Network
download(archive, name, cb)
archiveHyperdrive archive (object).nameEntry path (string). Can point to a file or folder.
Download an archive file or folder-tree.
// download a specific file:
await pda.download(archive, '/foo.txt')
// download a specific folder and all children:
await pda.download(archive, '/bar/')
// download the entire archive:
await pda.download(archive, '/')Activity Streams
watch(archive, path)
archiveHyperdrive archive (object).pathEntry path (string) or anymatch pattern (array of strings). If falsy, will watch all files.- Returns a Readable stream.
Watches the given path or path-pattern for file events, which it emits as an emit-stream. Supported events:
['invalidated',{path}]- The contents of the file has changed, but may not have been downloaded yet.pathis the path-string of the file.['changed',{path}]- The contents of the file has changed, and the new version is ready to read.pathis the path-string of the file.
An archive will emit "invalidated" first, when it receives the new metadata for the file. It will then emit "changed" when the content arrives. (A local archive will emit "invalidated" immediately before "changed.")
var es = pda.watch(archive)
var es = pda.watch(archive, 'foo.txt')
var es = pda.watch(archive, ['**/*.txt', '**/*.md'])
es.on('data', ([event, args]) => {
if (event === 'invalidated') {
console.log(args.path, 'has been invalidated')
pda.download(archive, args.path)
} else if (event === 'changed') {
console.log(args.path, 'has changed')
}
})
// alternatively, via emit-stream:
var emitStream = require('emit-stream')
var events = emitStream(pda.watch(archive))
events.on('invalidated', args => {
console.log(args.path, 'has been invalidated')
pda.download(archive, args.path)
})
events.on('changed', args => {
console.log(args.path, 'has changed')
})createNetworkActivityStream(archive)
archiveHyperdrive archive (object).- Returns a Readable stream.
Watches the archive for network events, which it emits as an emit-stream. Supported events:
['network-changed',{connections}]- The number of connections has changed.connectionsis a number.['download',{feed,block,bytes}]- A block has been downloaded.feedwill either be "metadata" or "content".blockis the index of data downloaded.bytesis the number of bytes in the block.['upload',{feed,block,bytes}]- A block has been uploaded.feedwill either be "metadata" or "content".blockis the index of data downloaded.bytesis the number of bytes in the block.['sync',{feed}]- All known blocks have been downloaded.feedwill either be "metadata" or "content".
var es = pda.createNetworkActivityStream(archive)
es.on('data', ([event, args]) => {
if (event === 'network-changed') {
console.log('Connected to %d peers', args.connections)
} else if (event === 'download') {
console.log('Just downloaded %d bytes (block %d) of the %s feed', args.bytes, args.block, args.feed)
} else if (event === 'upload') {
console.log('Just uploaded %d bytes (block %d) of the %s feed', args.bytes, args.block, args.feed)
} else if (event === 'sync') {
console.log('Finished downloading', args.feed)
}
})
// alternatively, via emit-stream:
var emitStream = require('emit-stream')
var events = emitStream(es)
events.on('network-changed', args => {
console.log('Connected to %d peers', args.connections)
})
events.on('download', args => {
console.log('Just downloaded %d bytes (block %d) of the %s feed', args.bytes, args.block, args.feed)
})
events.on('upload', args => {
console.log('Just uploaded %d bytes (block %d) of the %s feed', args.bytes, args.block, args.feed)
})
events.on('sync', args => {
console.log('Finished downloading', args.feed)
})Manifest
readManifest(archive, cb)
archiveHyperdrive archive (object).
A sugar to get the manifest object.
var manifestObj = await pda.readManifest(archive)writeManifest(archive, manifest, cb)
archiveHyperdrive archive (object).manifestManifest values (object).
A sugar to write the manifest object.
await pda.writeManifest(archive, { title: 'My dat!' })updateManifest(archive, manifest, cb)
archiveHyperdrive archive (object).manifestManifest values (object).
A sugar to modify the manifest object.
await pda.writeManifest(archive, { title: 'My dat!', description: 'the desc' })
await pda.writeManifest(archive, { title: 'My new title!' }) // preserves descriptiongenerateManifest(opts)
optsManifest options (object).
Helper to generate a manifest object. Opts in detail:
{
url: String, the dat's url
title: String
description: String
type: Array<String>
author: String | Object{name: String, url: String}
links: Object
web_root: String
fallback_page: String
}See: https://github.com/datprotocol/dat.json
Diff/Merge
diff(srcArchive, srcPath, dstArchive, dstPath, opts, cb)
srcArchiveSource archive (object). Required.srcPathSource path within the source archive (string). Required.dstArchiveDestination archive (object). Required.dstPathDestination path within the destination archive (string). Required.opts.shallowDon't descend into changed folders (bool). Optional, default false.opts.compareContent. Compare the content of the files, rather than the mtime and size. Optional, default false.opts.pathsWhitelist of files to diff (array). Optional.opts.opsWhitelist of operations to include in the diff (array). Optional. Valid values are'add','mod', and'del'.- Returns diff data.
Get a list of differences between the two archives at the given paths.
await pda.diff(archiveA, '/', archiveB, '/')
await pda.diff(archiveA, '/', archiveB, '/', {shallow: false, compareContent: true})
await pda.diff(archiveA, '/', archiveB, '/', {paths: ['/foo', '/bar']})
await pda.diff(archiveA, '/', archiveB, '/', {ops: ['add']}) // additions onlyOutput looks like:
[
{change: 'mod', type: 'file', path: '/hello.txt'},
{change: 'add', type: 'dir', path: '/pics'},
{change: 'add', type: 'file', path: '/pics/kitty.png'},
{change: 'del', type: 'file', path: '/backup/hello.txt'},
{change: 'del', type: 'dir', path: '/backup'},
{change: 'del', type: 'file', path: '/hello.txt'},
]merge(srcArchive, srcPath, dstArchive, dstPath, opts, cb)
srcArchiveSource archive (object). Required.srcPathSource path within the source archive (string). Required.dstArchiveDestination archive (object). Required.dstPathDestination path within the destination archive (string). Required.opts.shallowDon't descend into changed folders (bool). Optional, default false.opts.compareContent. Compare the content of the files, rather than the mtime and size. Optional, default false.opts.pathsWhitelist of files to diff (array). Optional.opts.opsWhitelist of operations to include in the diff (array). Optional. Valid values are'add','mod', and'del'.- Returns the changes applied.
Merges the source archive into the destinatio archive at the given paths, causing dstArchive content to match srcArchive.
await pda.merge(archiveA, '/', archiveB, '/')
await pda.merge(archiveA, '/', archiveB, '/', {shallow: false, compareContent: true})
await pda.merge(archiveA, '/', archiveB, '/', {paths: ['/foo', '/bar']})
await pda.merge(archiveA, '/', archiveB, '/', {ops: ['add']}) // additions onlyOutput looks like:
[
{change: 'mod', type: 'file', path: '/hello.txt'},
{change: 'add', type: 'dir', path: '/pics'},
{change: 'add', type: 'file', path: '/pics/kitty.png'},
{change: 'del', type: 'file', path: '/backup/hello.txt'},
{change: 'del', type: 'dir', path: '/backup'},
{change: 'del', type: 'file', path: '/hello.txt'},
]Helpers
findEntryByContentBlock(archive, block)
archiveHyperdrive archive (object).blockContent-block index- Returns a Promise for
{name:, start:, end:}
Runs a binary search to find the file-entry that the given content-block index belongs to.
await pda.findEntryByContentBlock(archive, 5)
/* => {
name: '/foo.txt',
start: 4,
end: 6
}*/6 years ago