history.js 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  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. /** @type {Array<Change>} */
  24. this.changes = changes || []
  25. }
  26. static fromRaw(raw) {
  27. return new History(
  28. Snapshot.fromRaw(raw.snapshot),
  29. raw.changes.map(Change.fromRaw)
  30. )
  31. }
  32. toRaw() {
  33. function changeToRaw(change) {
  34. return change.toRaw()
  35. }
  36. return {
  37. snapshot: this.snapshot.toRaw(),
  38. changes: this.changes.map(changeToRaw),
  39. }
  40. }
  41. getSnapshot() {
  42. return this.snapshot
  43. }
  44. getChanges() {
  45. return this.changes
  46. }
  47. countChanges() {
  48. return this.changes.length
  49. }
  50. /**
  51. * Add changes to this history.
  52. *
  53. * @param {Array.<Change>} changes
  54. */
  55. pushChanges(changes) {
  56. this.changes.push.apply(this.changes, changes)
  57. }
  58. /**
  59. * If this History references blob hashes, either in the Snapshot or the
  60. * Changes, add them to the given set.
  61. *
  62. * @param {Set.<String>} blobHashes
  63. */
  64. findBlobHashes(blobHashes) {
  65. function findChangeBlobHashes(change) {
  66. change.findBlobHashes(blobHashes)
  67. }
  68. this.snapshot.findBlobHashes(blobHashes)
  69. this.changes.forEach(findChangeBlobHashes)
  70. }
  71. /**
  72. * If this History contains any File objects, load them.
  73. *
  74. * @param {string} kind see {File#load}
  75. * @param {BlobStore} blobStore
  76. * @return {Promise<void>}
  77. */
  78. async loadFiles(kind, blobStore) {
  79. async function loadChangeFiles(changes) {
  80. for (const change of changes) {
  81. await change.loadFiles(kind, blobStore)
  82. }
  83. }
  84. await Promise.all([
  85. this.snapshot.loadFiles(kind, blobStore),
  86. loadChangeFiles(this.changes),
  87. ])
  88. }
  89. /**
  90. * Return a version of this history that is suitable for long term storage.
  91. * This requires that we store the content of file objects in the provided
  92. * blobStore.
  93. *
  94. * @param {BlobStore} blobStore
  95. * @param {number} [concurrency] applies separately to files, changes and
  96. * operations
  97. * @return {Promise.<Object>}
  98. */
  99. async store(blobStore, concurrency) {
  100. assert.maybe.number(concurrency, 'bad concurrency')
  101. /**
  102. * @param {Change} change
  103. */
  104. async function storeChange(change) {
  105. return await change.store(blobStore, concurrency)
  106. }
  107. const [rawSnapshot, rawChanges] = await Promise.all([
  108. this.snapshot.store(blobStore, concurrency),
  109. pMap(this.changes, storeChange, { concurrency: concurrency || 1 }),
  110. ])
  111. return {
  112. snapshot: rawSnapshot,
  113. changes: rawChanges,
  114. }
  115. }
  116. }
  117. module.exports = History