chunk.js 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. 'use strict'
  2. const assert = require('check-types').assert
  3. const OError = require('@overleaf/o-error')
  4. const History = require('./history')
  5. /**
  6. * @import { BlobStore, RawChunk } from "./types"
  7. * @import Change from "./change"
  8. * @import Snapshot from "./snapshot"
  9. */
  10. class ConflictingEndVersion extends OError {
  11. constructor(clientEndVersion, latestEndVersion) {
  12. const message =
  13. 'client sent updates with end_version ' +
  14. clientEndVersion +
  15. ' but latest chunk has end_version ' +
  16. latestEndVersion
  17. super(message, { clientEndVersion, latestEndVersion })
  18. this.clientEndVersion = clientEndVersion
  19. this.latestEndVersion = latestEndVersion
  20. }
  21. }
  22. class NotFoundError extends OError {
  23. // `message` and `info` optional arguments allow children classes to override
  24. // these values, ensuring backwards compatibility with previous implementation
  25. // based on the `overleaf-error-type` library
  26. constructor(projectId, message, info) {
  27. const errorMessage = message || `no chunks for project ${projectId}`
  28. const errorInfo = info || { projectId }
  29. super(errorMessage, errorInfo)
  30. this.projectId = projectId
  31. }
  32. }
  33. class VersionNotFoundError extends NotFoundError {
  34. constructor(projectId, version) {
  35. super(projectId, `chunk for ${projectId} v ${version} not found`, {
  36. projectId,
  37. version,
  38. })
  39. this.projectId = projectId
  40. this.version = version
  41. }
  42. }
  43. class BeforeTimestampNotFoundError extends NotFoundError {
  44. constructor(projectId, timestamp) {
  45. super(projectId, `chunk for ${projectId} timestamp ${timestamp} not found`)
  46. this.projectId = projectId
  47. this.timestamp = timestamp
  48. }
  49. }
  50. class NotPersistedError extends NotFoundError {
  51. constructor(projectId) {
  52. super(projectId, `chunk for ${projectId} not persisted yet`)
  53. this.projectId = projectId
  54. }
  55. }
  56. /**
  57. * A Chunk is a {@link History} that is part of a project's overall history. It
  58. * has a start and an end version that place its History in context.
  59. */
  60. class Chunk {
  61. static ConflictingEndVersion = ConflictingEndVersion
  62. static NotFoundError = NotFoundError
  63. static VersionNotFoundError = VersionNotFoundError
  64. static BeforeTimestampNotFoundError = BeforeTimestampNotFoundError
  65. static NotPersistedError = NotPersistedError
  66. /**
  67. * @param {History} history
  68. * @param {number} startVersion
  69. */
  70. constructor(history, startVersion) {
  71. assert.instance(history, History, 'bad history')
  72. assert.integer(startVersion, 'bad startVersion')
  73. this.history = history
  74. this.startVersion = startVersion
  75. }
  76. /**
  77. * @param {RawChunk} raw
  78. * @return {Chunk}
  79. */
  80. static fromRaw(raw) {
  81. return new Chunk(History.fromRaw(raw.history), raw.startVersion)
  82. }
  83. toRaw() {
  84. return { history: this.history.toRaw(), startVersion: this.startVersion }
  85. }
  86. /**
  87. * The history for this chunk.
  88. *
  89. * @return {History}
  90. */
  91. getHistory() {
  92. return this.history
  93. }
  94. /**
  95. * {@see History#getSnapshot}
  96. * @return {Snapshot}
  97. */
  98. getSnapshot() {
  99. return this.history.getSnapshot()
  100. }
  101. /**
  102. * {@see History#getChanges}
  103. * @return {Array.<Change>}
  104. */
  105. getChanges() {
  106. return this.history.getChanges()
  107. }
  108. /**
  109. * {@see History#pushChanges}
  110. * @param {Array.<Change>} changes
  111. */
  112. pushChanges(changes) {
  113. this.history.pushChanges(changes)
  114. }
  115. /**
  116. * The version of the project after applying all changes in this chunk.
  117. *
  118. * @return {number} non-negative, greater than or equal to start version
  119. */
  120. getEndVersion() {
  121. return this.startVersion + this.history.countChanges()
  122. }
  123. /**
  124. * The timestamp of the last change in this chunk
  125. */
  126. getEndTimestamp() {
  127. if (!this.history.countChanges()) return null
  128. return this.history.getChanges().slice(-1)[0].getTimestamp()
  129. }
  130. /**
  131. * The version of the project before applying all changes in this chunk.
  132. *
  133. * @return {number} non-negative, less than or equal to end version
  134. */
  135. getStartVersion() {
  136. return this.startVersion
  137. }
  138. /**
  139. * {@see History#loadFiles}
  140. *
  141. * @param {string} kind
  142. * @param {BlobStore} blobStore
  143. * @return {Promise<void>}
  144. */
  145. async loadFiles(kind, blobStore) {
  146. await this.history.loadFiles(kind, blobStore)
  147. }
  148. }
  149. module.exports = Chunk