history.js 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  1. 'use strict'
  2. const assert = require('check-types').assert
  3. const pMap = require('p-map')
  4. const Change = require('./change')
  5. const Snapshot = require('./snapshot')
  6. /**
  7. * @typedef {import("./types").BlobStore} BlobStore
  8. */
  9. class History {
  10. /**
  11. * @constructor
  12. * @param {Snapshot} snapshot
  13. * @param {Array.<Change>} changes
  14. *
  15. * @classdesc
  16. * A History is a {@link Snapshot} and a sequence of {@link Change}s that can
  17. * be applied to produce a new snapshot.
  18. */
  19. constructor(snapshot, changes) {
  20. assert.instance(snapshot, Snapshot, 'bad snapshot')
  21. assert.maybe.array.of.instance(changes, Change, 'bad changes')
  22. this.snapshot = snapshot
  23. this.changes = changes || []
  24. }
  25. static fromRaw(raw) {
  26. return new History(
  27. Snapshot.fromRaw(raw.snapshot),
  28. raw.changes.map(Change.fromRaw)
  29. )
  30. }
  31. toRaw() {
  32. function changeToRaw(change) {
  33. return change.toRaw()
  34. }
  35. return {
  36. snapshot: this.snapshot.toRaw(),
  37. changes: this.changes.map(changeToRaw),
  38. }
  39. }
  40. getSnapshot() {
  41. return this.snapshot
  42. }
  43. getChanges() {
  44. return this.changes
  45. }
  46. countChanges() {
  47. return this.changes.length
  48. }
  49. /**
  50. * Add changes to this history.
  51. *
  52. * @param {Array.<Change>} changes
  53. */
  54. pushChanges(changes) {
  55. this.changes.push.apply(this.changes, changes)
  56. }
  57. /**
  58. * If this History references blob hashes, either in the Snapshot or the
  59. * Changes, add them to the given set.
  60. *
  61. * @param {Set.<String>} blobHashes
  62. */
  63. findBlobHashes(blobHashes) {
  64. function findChangeBlobHashes(change) {
  65. change.findBlobHashes(blobHashes)
  66. }
  67. this.snapshot.findBlobHashes(blobHashes)
  68. this.changes.forEach(findChangeBlobHashes)
  69. }
  70. /**
  71. * If this History contains any File objects, load them.
  72. *
  73. * @param {string} kind see {File#load}
  74. * @param {BlobStore} blobStore
  75. * @return {Promise<void>}
  76. */
  77. async loadFiles(kind, blobStore) {
  78. async function loadChangeFiles(changes) {
  79. for (const change of changes) {
  80. await change.loadFiles(kind, blobStore)
  81. }
  82. }
  83. await Promise.all([
  84. this.snapshot.loadFiles(kind, blobStore),
  85. loadChangeFiles(this.changes),
  86. ])
  87. }
  88. /**
  89. * Return a version of this history that is suitable for long term storage.
  90. * This requires that we store the content of file objects in the provided
  91. * blobStore.
  92. *
  93. * @param {BlobStore} blobStore
  94. * @param {number} [concurrency] applies separately to files, changes and
  95. * operations
  96. * @return {Promise.<Object>}
  97. */
  98. async store(blobStore, concurrency) {
  99. assert.maybe.number(concurrency, 'bad concurrency')
  100. async function storeChange(change) {
  101. return await change.store(blobStore, concurrency)
  102. }
  103. const [rawSnapshot, rawChanges] = await Promise.all([
  104. this.snapshot.store(blobStore, concurrency),
  105. pMap(this.changes, storeChange, { concurrency: concurrency || 1 }),
  106. ])
  107. return {
  108. snapshot: rawSnapshot,
  109. changes: rawChanges,
  110. }
  111. }
  112. }
  113. module.exports = History