2023-01-13 12:42:29 +00:00
|
|
|
'use strict'
|
|
|
|
|
|
|
|
const assert = require('check-types').assert
|
2023-08-22 14:48:24 +00:00
|
|
|
const pMap = require('p-map')
|
2023-01-13 12:42:29 +00:00
|
|
|
|
|
|
|
const Change = require('./change')
|
|
|
|
const Snapshot = require('./snapshot')
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @typedef {import("./types").BlobStore} BlobStore
|
|
|
|
*/
|
|
|
|
|
2023-08-22 14:48:04 +00:00
|
|
|
class History {
|
|
|
|
/**
|
|
|
|
* @constructor
|
|
|
|
* @param {Snapshot} snapshot
|
|
|
|
* @param {Array.<Change>} changes
|
|
|
|
*
|
|
|
|
* @classdesc
|
|
|
|
* A History is a {@link Snapshot} and a sequence of {@link Change}s that can
|
|
|
|
* be applied to produce a new snapshot.
|
|
|
|
*/
|
|
|
|
constructor(snapshot, changes) {
|
|
|
|
assert.instance(snapshot, Snapshot, 'bad snapshot')
|
|
|
|
assert.maybe.array.of.instance(changes, Change, 'bad changes')
|
2023-01-13 12:42:29 +00:00
|
|
|
|
2023-08-22 14:48:04 +00:00
|
|
|
this.snapshot = snapshot
|
|
|
|
this.changes = changes || []
|
|
|
|
}
|
2023-01-13 12:42:29 +00:00
|
|
|
|
2023-08-22 14:48:04 +00:00
|
|
|
static fromRaw(raw) {
|
|
|
|
return new History(
|
|
|
|
Snapshot.fromRaw(raw.snapshot),
|
|
|
|
raw.changes.map(Change.fromRaw)
|
|
|
|
)
|
|
|
|
}
|
2023-01-13 12:42:29 +00:00
|
|
|
|
2023-08-22 14:48:04 +00:00
|
|
|
toRaw() {
|
|
|
|
function changeToRaw(change) {
|
|
|
|
return change.toRaw()
|
|
|
|
}
|
|
|
|
return {
|
|
|
|
snapshot: this.snapshot.toRaw(),
|
|
|
|
changes: this.changes.map(changeToRaw),
|
|
|
|
}
|
2023-01-13 12:42:29 +00:00
|
|
|
}
|
2023-08-22 14:48:04 +00:00
|
|
|
|
|
|
|
getSnapshot() {
|
|
|
|
return this.snapshot
|
2023-01-13 12:42:29 +00:00
|
|
|
}
|
|
|
|
|
2023-08-22 14:48:04 +00:00
|
|
|
getChanges() {
|
|
|
|
return this.changes
|
|
|
|
}
|
2023-01-13 12:42:29 +00:00
|
|
|
|
2023-08-22 14:48:04 +00:00
|
|
|
countChanges() {
|
|
|
|
return this.changes.length
|
|
|
|
}
|
2023-01-13 12:42:29 +00:00
|
|
|
|
2023-08-22 14:48:04 +00:00
|
|
|
/**
|
|
|
|
* Add changes to this history.
|
|
|
|
*
|
|
|
|
* @param {Array.<Change>} changes
|
|
|
|
*/
|
|
|
|
pushChanges(changes) {
|
|
|
|
this.changes.push.apply(this.changes, changes)
|
|
|
|
}
|
2023-01-13 12:42:29 +00:00
|
|
|
|
2023-08-22 14:48:04 +00:00
|
|
|
/**
|
|
|
|
* If this History references blob hashes, either in the Snapshot or the
|
|
|
|
* Changes, add them to the given set.
|
|
|
|
*
|
|
|
|
* @param {Set.<String>} blobHashes
|
|
|
|
*/
|
|
|
|
findBlobHashes(blobHashes) {
|
|
|
|
function findChangeBlobHashes(change) {
|
|
|
|
change.findBlobHashes(blobHashes)
|
|
|
|
}
|
|
|
|
this.snapshot.findBlobHashes(blobHashes)
|
|
|
|
this.changes.forEach(findChangeBlobHashes)
|
2023-01-13 12:42:29 +00:00
|
|
|
}
|
|
|
|
|
2023-08-22 14:48:04 +00:00
|
|
|
/**
|
|
|
|
* If this History contains any File objects, load them.
|
|
|
|
*
|
|
|
|
* @param {string} kind see {File#load}
|
|
|
|
* @param {BlobStore} blobStore
|
2023-08-22 14:48:24 +00:00
|
|
|
* @return {Promise<void>}
|
2023-08-22 14:48:04 +00:00
|
|
|
*/
|
2023-08-22 14:48:24 +00:00
|
|
|
async loadFiles(kind, blobStore) {
|
|
|
|
async function loadChangeFiles(changes) {
|
|
|
|
for (const change of changes) {
|
|
|
|
await change.loadFiles(kind, blobStore)
|
|
|
|
}
|
2023-08-22 14:48:04 +00:00
|
|
|
}
|
2023-08-22 14:48:24 +00:00
|
|
|
|
|
|
|
await Promise.all([
|
2023-08-22 14:48:04 +00:00
|
|
|
this.snapshot.loadFiles(kind, blobStore),
|
2023-08-22 14:48:24 +00:00
|
|
|
loadChangeFiles(this.changes),
|
|
|
|
])
|
2023-01-13 12:42:29 +00:00
|
|
|
}
|
|
|
|
|
2023-08-22 14:48:04 +00:00
|
|
|
/**
|
|
|
|
* Return a version of this history that is suitable for long term storage.
|
|
|
|
* This requires that we store the content of file objects in the provided
|
|
|
|
* blobStore.
|
|
|
|
*
|
|
|
|
* @param {BlobStore} blobStore
|
|
|
|
* @param {number} [concurrency] applies separately to files, changes and
|
|
|
|
* operations
|
|
|
|
* @return {Promise.<Object>}
|
|
|
|
*/
|
2023-08-22 14:48:24 +00:00
|
|
|
async store(blobStore, concurrency) {
|
2023-08-22 14:48:04 +00:00
|
|
|
assert.maybe.number(concurrency, 'bad concurrency')
|
2023-01-13 12:42:29 +00:00
|
|
|
|
2023-08-22 14:48:24 +00:00
|
|
|
async function storeChange(change) {
|
|
|
|
return await change.store(blobStore, concurrency)
|
2023-01-13 12:42:29 +00:00
|
|
|
}
|
2023-08-22 14:48:24 +00:00
|
|
|
|
|
|
|
const [rawSnapshot, rawChanges] = await Promise.all([
|
2023-08-22 14:48:04 +00:00
|
|
|
this.snapshot.store(blobStore, concurrency),
|
2023-08-22 14:48:24 +00:00
|
|
|
pMap(this.changes, storeChange, { concurrency: concurrency || 1 }),
|
|
|
|
])
|
|
|
|
return {
|
|
|
|
snapshot: rawSnapshot,
|
|
|
|
changes: rawChanges,
|
|
|
|
}
|
2023-08-22 14:48:04 +00:00
|
|
|
}
|
2023-01-13 12:42:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
module.exports = History
|