chunk.js 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. 'use strict'
  2. const assert = require('check-types').assert
  3. const OError = require('@overleaf/o-error')
  4. const History = require('./history')
  5. /**
  6. * @typedef {import("./types").BlobStore} BlobStore
  7. * @typedef {import("./change")} Change
  8. * @typedef {import("./snapshot")} 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. static fromRaw(raw) {
  77. return new Chunk(History.fromRaw(raw.history), raw.startVersion)
  78. }
  79. toRaw() {
  80. return { history: this.history.toRaw(), startVersion: this.startVersion }
  81. }
  82. /**
  83. * The history for this chunk.
  84. *
  85. * @return {History}
  86. */
  87. getHistory() {
  88. return this.history
  89. }
  90. /**
  91. * {@see History#getSnapshot}
  92. * @return {Snapshot}
  93. */
  94. getSnapshot() {
  95. return this.history.getSnapshot()
  96. }
  97. /**
  98. * {@see History#getChanges}
  99. * @return {Array.<Change>}
  100. */
  101. getChanges() {
  102. return this.history.getChanges()
  103. }
  104. /**
  105. * {@see History#pushChanges}
  106. * @param {Array.<Change>} changes
  107. */
  108. pushChanges(changes) {
  109. this.history.pushChanges(changes)
  110. }
  111. /**
  112. * The version of the project after applying all changes in this chunk.
  113. *
  114. * @return {number} non-negative, greater than or equal to start version
  115. */
  116. getEndVersion() {
  117. return this.startVersion + this.history.countChanges()
  118. }
  119. /**
  120. * The timestamp of the last change in this chunk
  121. */
  122. getEndTimestamp() {
  123. if (!this.history.countChanges()) return null
  124. return this.history.getChanges().slice(-1)[0].getTimestamp()
  125. }
  126. /**
  127. * The version of the project before applying all changes in this chunk.
  128. *
  129. * @return {number} non-negative, less than or equal to end version
  130. */
  131. getStartVersion() {
  132. return this.startVersion
  133. }
  134. /**
  135. * {@see History#loadFiles}
  136. *
  137. * @param {string} kind
  138. * @param {BlobStore} blobStore
  139. * @return {Promise<void>}
  140. */
  141. async loadFiles(kind, blobStore) {
  142. await this.history.loadFiles(kind, blobStore)
  143. }
  144. }
  145. module.exports = Chunk