This commit is contained in:
parent
fa2be029a2
commit
724d70e58b
3339 changed files with 1075535 additions and 0 deletions
34
frontend/node_modules/tus-js-client/lib/node/fileReader.js
generated
vendored
Normal file
34
frontend/node_modules/tus-js-client/lib/node/fileReader.js
generated
vendored
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
import { ReadStream } from 'fs'
|
||||
import isStream from 'is-stream'
|
||||
|
||||
import BufferSource from './sources/BufferSource.js'
|
||||
import getFileSource from './sources/FileSource.js'
|
||||
import StreamSource from './sources/StreamSource.js'
|
||||
|
||||
export default class FileReader {
|
||||
openFile(input, chunkSize) {
|
||||
if (Buffer.isBuffer(input)) {
|
||||
return Promise.resolve(new BufferSource(input))
|
||||
}
|
||||
|
||||
if (input instanceof ReadStream && input.path != null) {
|
||||
return getFileSource(input)
|
||||
}
|
||||
|
||||
if (isStream.readable(input)) {
|
||||
chunkSize = Number(chunkSize)
|
||||
if (!Number.isFinite(chunkSize)) {
|
||||
return Promise.reject(
|
||||
new Error(
|
||||
'cannot create source for stream without a finite value for the `chunkSize` option; specify a chunkSize to control the memory consumption',
|
||||
),
|
||||
)
|
||||
}
|
||||
return Promise.resolve(new StreamSource(input))
|
||||
}
|
||||
|
||||
return Promise.reject(
|
||||
new Error('source object may only be an instance of Buffer or Readable in this environment'),
|
||||
)
|
||||
}
|
||||
}
|
||||
39
frontend/node_modules/tus-js-client/lib/node/fileSignature.js
generated
vendored
Normal file
39
frontend/node_modules/tus-js-client/lib/node/fileSignature.js
generated
vendored
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
import { createHash } from 'crypto'
|
||||
import * as fs from 'fs'
|
||||
import * as path from 'path'
|
||||
|
||||
/**
|
||||
* Generate a fingerprint for a file which will be used the store the endpoint
|
||||
*
|
||||
* @param {File} file
|
||||
* @param {Object} options
|
||||
*/
|
||||
export default function fingerprint(file, options) {
|
||||
if (Buffer.isBuffer(file)) {
|
||||
// create MD5 hash for buffer type
|
||||
const blockSize = 64 * 1024 // 64kb
|
||||
const content = file.slice(0, Math.min(blockSize, file.length))
|
||||
const hash = createHash('md5').update(content).digest('hex')
|
||||
const ret = ['node-buffer', hash, file.length, options.endpoint].join('-')
|
||||
return Promise.resolve(ret)
|
||||
}
|
||||
|
||||
if (file instanceof fs.ReadStream && file.path != null) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const name = path.resolve(file.path)
|
||||
fs.stat(file.path, (err, info) => {
|
||||
if (err) {
|
||||
reject(err)
|
||||
return
|
||||
}
|
||||
|
||||
const ret = ['node-file', name, info.size, info.mtime.getTime(), options.endpoint].join('-')
|
||||
|
||||
resolve(ret)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// fingerprint cannot be computed for file input type
|
||||
return Promise.resolve(null)
|
||||
}
|
||||
212
frontend/node_modules/tus-js-client/lib/node/httpStack.js
generated
vendored
Normal file
212
frontend/node_modules/tus-js-client/lib/node/httpStack.js
generated
vendored
Normal file
|
|
@ -0,0 +1,212 @@
|
|||
// The url.parse method is superseeded by the url.URL constructor,
|
||||
// but it is still included in Node.js
|
||||
import * as http from 'http'
|
||||
import * as https from 'https'
|
||||
import { Readable, Transform } from 'stream'
|
||||
import { parse } from 'url'
|
||||
import throttle from 'lodash.throttle'
|
||||
|
||||
export default class NodeHttpStack {
|
||||
constructor(requestOptions = {}) {
|
||||
this._requestOptions = requestOptions
|
||||
}
|
||||
|
||||
createRequest(method, url) {
|
||||
return new Request(method, url, this._requestOptions)
|
||||
}
|
||||
|
||||
getName() {
|
||||
return 'NodeHttpStack'
|
||||
}
|
||||
}
|
||||
|
||||
class Request {
|
||||
constructor(method, url, options) {
|
||||
this._method = method
|
||||
this._url = url
|
||||
this._headers = {}
|
||||
this._request = null
|
||||
this._progressHandler = () => {}
|
||||
this._requestOptions = options || {}
|
||||
}
|
||||
|
||||
getMethod() {
|
||||
return this._method
|
||||
}
|
||||
|
||||
getURL() {
|
||||
return this._url
|
||||
}
|
||||
|
||||
setHeader(header, value) {
|
||||
this._headers[header] = value
|
||||
}
|
||||
|
||||
getHeader(header) {
|
||||
return this._headers[header]
|
||||
}
|
||||
|
||||
setProgressHandler(progressHandler) {
|
||||
this._progressHandler = progressHandler
|
||||
}
|
||||
|
||||
send(body = null) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const options = {
|
||||
...parse(this._url),
|
||||
...this._requestOptions,
|
||||
|
||||
method: this._method,
|
||||
headers: {
|
||||
...(this._requestOptions.headers || {}),
|
||||
...this._headers,
|
||||
},
|
||||
}
|
||||
|
||||
if (body?.size) {
|
||||
options.headers['Content-Length'] = body.size
|
||||
}
|
||||
|
||||
const httpModule = options.protocol === 'https:' ? https : http
|
||||
this._request = httpModule.request(options)
|
||||
const req = this._request
|
||||
req.on('response', (res) => {
|
||||
const resChunks = []
|
||||
res.on('data', (data) => {
|
||||
resChunks.push(data)
|
||||
})
|
||||
|
||||
res.on('end', () => {
|
||||
const responseText = Buffer.concat(resChunks).toString('utf8')
|
||||
resolve(new Response(res, responseText))
|
||||
})
|
||||
})
|
||||
|
||||
req.on('error', (err) => {
|
||||
reject(err)
|
||||
})
|
||||
|
||||
if (body instanceof Readable) {
|
||||
// Readable stream are piped through a PassThrough instance, which
|
||||
// counts the number of bytes passed through. This is used, for example,
|
||||
// when an fs.ReadStream is provided to tus-js-client.
|
||||
body.pipe(new ProgressEmitter(this._progressHandler)).pipe(req)
|
||||
} else if (body instanceof Uint8Array) {
|
||||
// For Buffers and Uint8Arrays (in Node.js all buffers are instances of Uint8Array),
|
||||
// we write chunks of the buffer to the stream and use that to track the progress.
|
||||
// This is used when either a Buffer or a normal readable stream is provided
|
||||
// to tus-js-client.
|
||||
writeBufferToStreamWithProgress(req, body, this._progressHandler)
|
||||
} else {
|
||||
req.end(body)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
abort() {
|
||||
if (this._request !== null) this._request.abort()
|
||||
return Promise.resolve()
|
||||
}
|
||||
|
||||
getUnderlyingObject() {
|
||||
return this._request
|
||||
}
|
||||
}
|
||||
|
||||
class Response {
|
||||
constructor(res, body) {
|
||||
this._response = res
|
||||
this._body = body
|
||||
}
|
||||
|
||||
getStatus() {
|
||||
return this._response.statusCode
|
||||
}
|
||||
|
||||
getHeader(header) {
|
||||
return this._response.headers[header.toLowerCase()]
|
||||
}
|
||||
|
||||
getBody() {
|
||||
return this._body
|
||||
}
|
||||
|
||||
getUnderlyingObject() {
|
||||
return this._response
|
||||
}
|
||||
}
|
||||
|
||||
// ProgressEmitter is a simple PassThrough-style transform stream which keeps
|
||||
// track of the number of bytes which have been piped through it and will
|
||||
// invoke the `onprogress` function whenever new number are available.
|
||||
class ProgressEmitter extends Transform {
|
||||
constructor(onprogress) {
|
||||
super()
|
||||
|
||||
// The _onprogress property will be invoked, whenever a chunk is piped
|
||||
// through this transformer. Since chunks are usually quite small (64kb),
|
||||
// these calls can occur frequently, especially when you have a good
|
||||
// connection to the remote server. Therefore, we are throtteling them to
|
||||
// prevent accessive function calls.
|
||||
this._onprogress = throttle(onprogress, 100, {
|
||||
leading: true,
|
||||
trailing: false,
|
||||
})
|
||||
this._position = 0
|
||||
}
|
||||
|
||||
_transform(chunk, _encoding, callback) {
|
||||
this._position += chunk.length
|
||||
this._onprogress(this._position)
|
||||
callback(null, chunk)
|
||||
}
|
||||
}
|
||||
|
||||
// writeBufferToStreamWithProgress writes chunks from `source` (either a
|
||||
// Buffer or Uint8Array) to the readable stream `stream`.
|
||||
// The size of the chunk depends on the stream's highWaterMark to fill the
|
||||
// stream's internal buffer as best as possible.
|
||||
// If the internal buffer is full, the callback `onprogress` will be invoked
|
||||
// to notify about the write progress. Writing will be resumed once the internal
|
||||
// buffer is empty, as indicated by the emitted `drain` event.
|
||||
// See https://nodejs.org/docs/latest/api/stream.html#buffering for more details
|
||||
// on the buffering behavior of streams.
|
||||
const writeBufferToStreamWithProgress = (stream, source, onprogress) => {
|
||||
onprogress = throttle(onprogress, 100, {
|
||||
leading: true,
|
||||
trailing: false,
|
||||
})
|
||||
|
||||
let offset = 0
|
||||
|
||||
function writeNextChunk() {
|
||||
// Take at most the amount of bytes from highWaterMark. This should fill the streams
|
||||
// internal buffer already.
|
||||
const chunkSize = Math.min(stream.writableHighWaterMark, source.length - offset)
|
||||
|
||||
// Note: We use subarray instead of slice because it works without copying data for
|
||||
// Buffers and Uint8Arrays.
|
||||
const chunk = source.subarray(offset, offset + chunkSize)
|
||||
offset += chunk.length
|
||||
|
||||
// `write` returns true if the internal buffer is not full and we should write more.
|
||||
// If the stream is destroyed because the request is aborted, it will return false
|
||||
// and no 'drain' event is emitted, so won't continue writing data.
|
||||
const canContinue = stream.write(chunk)
|
||||
|
||||
if (!canContinue) {
|
||||
// If the buffer is full, wait for the 'drain' event to write more data.
|
||||
stream.once('drain', writeNextChunk)
|
||||
onprogress(offset)
|
||||
} else if (offset < source.length) {
|
||||
// If there's still data to write and the buffer is not full, write next chunk.
|
||||
writeNextChunk()
|
||||
} else {
|
||||
// If all data has been written, close the stream if needed, and emit a 'finish' event.
|
||||
stream.end()
|
||||
}
|
||||
}
|
||||
|
||||
// Start writing the first chunk.
|
||||
writeNextChunk()
|
||||
}
|
||||
47
frontend/node_modules/tus-js-client/lib/node/index.js
generated
vendored
Normal file
47
frontend/node_modules/tus-js-client/lib/node/index.js
generated
vendored
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
import DetailedError from '../error.js'
|
||||
import { enableDebugLog } from '../logger.js'
|
||||
import NoopUrlStorage from '../noopUrlStorage.js'
|
||||
import BaseUpload from '../upload.js'
|
||||
|
||||
import FileReader from './fileReader.js'
|
||||
import fingerprint from './fileSignature.js'
|
||||
import DefaultHttpStack from './httpStack.js'
|
||||
import StreamSource from './sources/StreamSource.js'
|
||||
import { FileUrlStorage, canStoreURLs } from './urlStorage.js'
|
||||
|
||||
const defaultOptions = {
|
||||
...BaseUpload.defaultOptions,
|
||||
httpStack: new DefaultHttpStack(),
|
||||
fileReader: new FileReader(),
|
||||
urlStorage: new NoopUrlStorage(),
|
||||
fingerprint,
|
||||
}
|
||||
|
||||
class Upload extends BaseUpload {
|
||||
constructor(file = null, options = {}) {
|
||||
options = { ...defaultOptions, ...options }
|
||||
super(file, options)
|
||||
}
|
||||
|
||||
static terminate(url, options = {}) {
|
||||
options = { ...defaultOptions, ...options }
|
||||
return BaseUpload.terminate(url, options)
|
||||
}
|
||||
}
|
||||
|
||||
// The Node.js environment does not have restrictions which may cause
|
||||
// tus-js-client not to function.
|
||||
const isSupported = true
|
||||
|
||||
export {
|
||||
Upload,
|
||||
defaultOptions,
|
||||
isSupported,
|
||||
// Make FileUrlStorage module available as it will not be set by default.
|
||||
FileUrlStorage,
|
||||
canStoreURLs,
|
||||
enableDebugLog,
|
||||
DefaultHttpStack,
|
||||
DetailedError,
|
||||
StreamSource,
|
||||
}
|
||||
15
frontend/node_modules/tus-js-client/lib/node/sources/BufferSource.js
generated
vendored
Normal file
15
frontend/node_modules/tus-js-client/lib/node/sources/BufferSource.js
generated
vendored
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
export default class BufferSource {
|
||||
constructor(buffer) {
|
||||
this._buffer = buffer
|
||||
this.size = buffer.length
|
||||
}
|
||||
|
||||
slice(start, end) {
|
||||
const value = this._buffer.slice(start, end)
|
||||
value.size = value.length
|
||||
const done = end >= this.size
|
||||
return Promise.resolve({ value, done })
|
||||
}
|
||||
|
||||
close() {}
|
||||
}
|
||||
57
frontend/node_modules/tus-js-client/lib/node/sources/FileSource.js
generated
vendored
Normal file
57
frontend/node_modules/tus-js-client/lib/node/sources/FileSource.js
generated
vendored
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
import { createReadStream, promises as fsPromises } from 'fs'
|
||||
|
||||
export default async function getFileSource(stream) {
|
||||
const path = stream.path.toString()
|
||||
const { size } = await fsPromises.stat(path)
|
||||
|
||||
// The fs.ReadStream might be configured to not read from the beginning
|
||||
// to the end, but instead from a slice in between. In this case, we consider
|
||||
// that range to indicate the actual uploadable size.
|
||||
// This happens, for example, if a fs.ReadStream is used with the `parallelUploads`
|
||||
// option. There, the ReadStream is sliced into multiple ReadStreams to fit the number
|
||||
// of number of `parallelUploads`. Each ReadStream has `start` and `end` set.
|
||||
// Note: `stream.end` is Infinity by default, so we need the check `isFinite`.
|
||||
// Note: `stream.end` is treated inclusively, so we need to add 1 here.
|
||||
// See the comment in slice() for more context.
|
||||
const start = stream.start ?? 0
|
||||
const end = Number.isFinite(stream.end) ? stream.end + 1 : size
|
||||
const actualSize = end - start
|
||||
|
||||
return new FileSource(stream, path, actualSize)
|
||||
}
|
||||
|
||||
class FileSource {
|
||||
constructor(stream, path, size) {
|
||||
this._stream = stream
|
||||
this._path = path
|
||||
this.size = size
|
||||
}
|
||||
|
||||
slice(start, end) {
|
||||
// The fs.ReadStream might be configured to not read from the beginning,
|
||||
// but instead start at a different offset. The start value from the caller
|
||||
// does not include the offset, so we need to add this offset to our range later.
|
||||
// This happens, for example, if a fs.ReadStream is used with the `parallelUploads`
|
||||
// option. First, the ReadStream is sliced into multiple ReadStreams to fit the number
|
||||
// of number of `parallelUploads`. Each ReadStream has `start` set.
|
||||
const offset = this._stream.start ?? 0
|
||||
|
||||
const stream = createReadStream(this._path, {
|
||||
start: offset + start,
|
||||
// The `end` option for createReadStream is treated inclusively
|
||||
// (see https://nodejs.org/api/fs.html#fs_fs_createreadstream_path_options).
|
||||
// However, the Buffer#slice(start, end) and also our Source#slice(start, end)
|
||||
// method treat the end range exclusively, so we have to subtract 1.
|
||||
// This prevents an off-by-one error when reporting upload progress.
|
||||
end: offset + end - 1,
|
||||
autoClose: true,
|
||||
})
|
||||
stream.size = Math.min(end - start, this.size)
|
||||
const done = stream.size >= this.size
|
||||
return Promise.resolve({ value: stream, done })
|
||||
}
|
||||
|
||||
close() {
|
||||
this._stream.destroy()
|
||||
}
|
||||
}
|
||||
123
frontend/node_modules/tus-js-client/lib/node/sources/StreamSource.js
generated
vendored
Normal file
123
frontend/node_modules/tus-js-client/lib/node/sources/StreamSource.js
generated
vendored
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
/**
|
||||
* readChunk reads a chunk with the given size from the given
|
||||
* stream. It will wait until enough data is available to satisfy
|
||||
* the size requirement before resolving.
|
||||
* Only if the stream ends, the function may resolve with a buffer
|
||||
* smaller than the size argument.
|
||||
* Note that we rely on the stream behaving as Node.js documents:
|
||||
* https://nodejs.org/api/stream.html#readablereadsize
|
||||
*/
|
||||
async function readChunk(stream, size) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const onError = (err) => {
|
||||
stream.off('readable', onReadable)
|
||||
reject(err)
|
||||
}
|
||||
|
||||
const onReadable = () => {
|
||||
// TODO: Node requires size to be less than 1GB. Add a validation for that
|
||||
const chunk = stream.read(size)
|
||||
|
||||
if (chunk !== null) {
|
||||
stream.off('error', onError)
|
||||
stream.off('readable', onReadable)
|
||||
|
||||
resolve(chunk)
|
||||
}
|
||||
}
|
||||
|
||||
stream.once('error', onError)
|
||||
stream.on('readable', onReadable)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* StreamSource provides an interface to obtain slices of a Readable stream for
|
||||
* various ranges.
|
||||
* It will buffer read data, to allow for following pattern:
|
||||
* - Call slice(startA, endA) will buffer the data of the requested range
|
||||
* - Call slice(startB, endB) will return data from the buffer if startA <= startB <= endA.
|
||||
* If endB > endA, it will also consume new data from the stream.
|
||||
* Note that it is forbidden to call with startB < startA or startB > endA. In other words,
|
||||
* the slice calls cannot seek back and must not skip data from the stream.
|
||||
*/
|
||||
export default class StreamSource {
|
||||
constructor(stream) {
|
||||
this._stream = stream
|
||||
|
||||
// Setting the size to null indicates that we have no calculation available
|
||||
// for how much data this stream will emit requiring the user to specify
|
||||
// it manually (see the `uploadSize` option).
|
||||
this.size = null
|
||||
|
||||
this._buf = Buffer.alloc(0)
|
||||
this._bufPos = 0
|
||||
|
||||
this._ended = false
|
||||
this._error = null
|
||||
|
||||
stream.pause()
|
||||
stream.on('end', () => {
|
||||
this._ended = true
|
||||
})
|
||||
stream.on('error', (err) => {
|
||||
this._error = err
|
||||
})
|
||||
}
|
||||
|
||||
async slice(start, end) {
|
||||
// Fail fast if the caller requests a proportion of the data which is not
|
||||
// available any more.
|
||||
if (start < this._bufPos) {
|
||||
throw new Error('cannot slice from position which we already seeked away')
|
||||
}
|
||||
|
||||
if (start > this._bufPos + this._buf.length) {
|
||||
throw new Error('slice start is outside of buffer (currently not implemented)')
|
||||
}
|
||||
|
||||
if (this._error) {
|
||||
throw this._error
|
||||
}
|
||||
|
||||
let returnBuffer
|
||||
// Always attempt to drain the buffer first, even if this means that we
|
||||
// return less data than the caller requested.
|
||||
if (start < this._bufPos + this._buf.length) {
|
||||
const bufStart = start - this._bufPos
|
||||
const bufEnd = Math.min(this._buf.length, end - this._bufPos)
|
||||
|
||||
returnBuffer = this._buf.slice(bufStart, bufEnd)
|
||||
} else {
|
||||
returnBuffer = Buffer.alloc(0)
|
||||
}
|
||||
|
||||
// If the stream has ended already, read calls would not finish, so return early here.
|
||||
if (this._ended) {
|
||||
returnBuffer.size = returnBuffer.length
|
||||
return { value: returnBuffer, done: true }
|
||||
}
|
||||
|
||||
// If we could not satisfy the slice request from the buffer only, read more data from
|
||||
// the stream and add it to the buffer.
|
||||
const requestedSize = end - start
|
||||
if (requestedSize > returnBuffer.length) {
|
||||
// Note: We assume that the stream returns not more than the requested size.
|
||||
const newChunk = await readChunk(this._stream, requestedSize - returnBuffer.length)
|
||||
|
||||
// Append the new chunk to the buffer
|
||||
returnBuffer = Buffer.concat([returnBuffer, newChunk])
|
||||
}
|
||||
|
||||
// Important: Store the read data, so consecutive slice calls can access the same data.
|
||||
this._buf = returnBuffer
|
||||
this._bufPos = start
|
||||
|
||||
returnBuffer.size = returnBuffer.length
|
||||
return { value: returnBuffer, done: this._ended }
|
||||
}
|
||||
|
||||
close() {
|
||||
this._stream.destroy()
|
||||
}
|
||||
}
|
||||
156
frontend/node_modules/tus-js-client/lib/node/urlStorage.js
generated
vendored
Normal file
156
frontend/node_modules/tus-js-client/lib/node/urlStorage.js
generated
vendored
Normal file
|
|
@ -0,0 +1,156 @@
|
|||
import { readFile, writeFile } from 'fs'
|
||||
import combineErrors from 'combine-errors'
|
||||
import * as lockfile from 'proper-lockfile'
|
||||
|
||||
export const canStoreURLs = true
|
||||
|
||||
export class FileUrlStorage {
|
||||
constructor(filePath) {
|
||||
this.path = filePath
|
||||
}
|
||||
|
||||
findAllUploads() {
|
||||
return new Promise((resolve, reject) => {
|
||||
this._getItems('tus::', (err, results) => {
|
||||
if (err) reject(err)
|
||||
else resolve(results)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
findUploadsByFingerprint(fingerprint) {
|
||||
return new Promise((resolve, reject) => {
|
||||
this._getItems(`tus::${fingerprint}`, (err, results) => {
|
||||
if (err) reject(err)
|
||||
else resolve(results)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
removeUpload(urlStorageKey) {
|
||||
return new Promise((resolve, reject) => {
|
||||
this._removeItem(urlStorageKey, (err) => {
|
||||
if (err) reject(err)
|
||||
else resolve()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
addUpload(fingerprint, upload) {
|
||||
const id = Math.round(Math.random() * 1e12)
|
||||
const key = `tus::${fingerprint}::${id}`
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
this._setItem(key, upload, (err) => {
|
||||
if (err) reject(err)
|
||||
else resolve(key)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
_setItem(key, value, cb) {
|
||||
lockfile
|
||||
.lock(this.path, this._lockfileOptions())
|
||||
.then((release) => {
|
||||
cb = this._releaseAndCb(release, cb)
|
||||
this._getData((err, data) => {
|
||||
if (err) {
|
||||
cb(err)
|
||||
return
|
||||
}
|
||||
|
||||
data[key] = value
|
||||
this._writeData(data, (err2) => cb(err2))
|
||||
})
|
||||
})
|
||||
.catch(cb)
|
||||
}
|
||||
|
||||
_getItems(prefix, cb) {
|
||||
this._getData((err, data) => {
|
||||
if (err) {
|
||||
cb(err)
|
||||
return
|
||||
}
|
||||
|
||||
const results = Object.keys(data)
|
||||
.filter((key) => key.startsWith(prefix))
|
||||
.map((key) => {
|
||||
const obj = data[key]
|
||||
obj.urlStorageKey = key
|
||||
return obj
|
||||
})
|
||||
|
||||
cb(null, results)
|
||||
})
|
||||
}
|
||||
|
||||
_removeItem(key, cb) {
|
||||
lockfile
|
||||
.lock(this.path, this._lockfileOptions())
|
||||
.then((release) => {
|
||||
cb = this._releaseAndCb(release, cb)
|
||||
this._getData((err, data) => {
|
||||
if (err) {
|
||||
cb(err)
|
||||
return
|
||||
}
|
||||
|
||||
delete data[key]
|
||||
this._writeData(data, (err2) => cb(err2))
|
||||
})
|
||||
})
|
||||
.catch(cb)
|
||||
}
|
||||
|
||||
_lockfileOptions() {
|
||||
return {
|
||||
realpath: false,
|
||||
retries: {
|
||||
retries: 5,
|
||||
minTimeout: 20,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
_releaseAndCb(release, cb) {
|
||||
return (err) => {
|
||||
if (err) {
|
||||
release()
|
||||
.then(() => cb(err))
|
||||
.catch((releaseErr) => cb(combineErrors([err, releaseErr])))
|
||||
return
|
||||
}
|
||||
|
||||
release().then(cb).catch(cb)
|
||||
}
|
||||
}
|
||||
|
||||
_writeData(data, cb) {
|
||||
const opts = {
|
||||
encoding: 'utf8',
|
||||
mode: 0o660,
|
||||
flag: 'w',
|
||||
}
|
||||
writeFile(this.path, JSON.stringify(data), opts, (err) => cb(err))
|
||||
}
|
||||
|
||||
_getData(cb) {
|
||||
readFile(this.path, 'utf8', (err, data) => {
|
||||
if (err) {
|
||||
// return empty data if file does not exist
|
||||
if (err.code === 'ENOENT') cb(null, {})
|
||||
else cb(err)
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
data = !data.trim().length ? {} : JSON.parse(data)
|
||||
} catch (error) {
|
||||
cb(error)
|
||||
return
|
||||
}
|
||||
cb(null, data)
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in a new issue