diff --git a/libraries/overleaf-editor-core/lib/file.js b/libraries/overleaf-editor-core/lib/file.js index b5321c39b2..9c8eac4a96 100644 --- a/libraries/overleaf-editor-core/lib/file.js +++ b/libraries/overleaf-editor-core/lib/file.js @@ -123,6 +123,19 @@ class File { return rawFileData } + /** + * @returns {Record} + */ + toStats() { + const stats = this.data.toStats() + if (!_.isEmpty(this.metadata)) { + stats.nMeta = 1 + // Note: Buffer does not exist in frontend. Use string length instead. + stats.metaSize = JSON.stringify(this.metadata).length + } + return stats + } + /** * Hexadecimal SHA-1 hash of the file's content, if known. * diff --git a/libraries/overleaf-editor-core/lib/file_data/binary_file_data.js b/libraries/overleaf-editor-core/lib/file_data/binary_file_data.js index 7919634c5d..dd6c98c794 100644 --- a/libraries/overleaf-editor-core/lib/file_data/binary_file_data.js +++ b/libraries/overleaf-editor-core/lib/file_data/binary_file_data.js @@ -33,6 +33,16 @@ class BinaryFileData extends FileData { return new BinaryFileData(raw.hash, raw.byteLength) } + /** + * @returns {Record} + */ + toStats() { + return { + hashes: 1, + byteLength: this.byteLength, + } + } + /** * @inheritdoc * @returns {RawBinaryFileData} diff --git a/libraries/overleaf-editor-core/lib/file_data/hash_file_data.js b/libraries/overleaf-editor-core/lib/file_data/hash_file_data.js index d12da1cf65..ddfbff956d 100644 --- a/libraries/overleaf-editor-core/lib/file_data/hash_file_data.js +++ b/libraries/overleaf-editor-core/lib/file_data/hash_file_data.js @@ -54,6 +54,15 @@ class HashFileData extends FileData { return raw } + /** + * @returns {Record} + */ + toStats() { + return { + hashes: 1 + (this.rangesHash ? 1 : 0), + } + } + /** * @inheritdoc * @returns {string} diff --git a/libraries/overleaf-editor-core/lib/file_data/hollow_binary_file_data.js b/libraries/overleaf-editor-core/lib/file_data/hollow_binary_file_data.js index 4187024b41..e7df39ff86 100644 --- a/libraries/overleaf-editor-core/lib/file_data/hollow_binary_file_data.js +++ b/libraries/overleaf-editor-core/lib/file_data/hollow_binary_file_data.js @@ -36,6 +36,15 @@ class HollowBinaryFileData extends FileData { return { byteLength: this.byteLength } } + /** + * @returns {Record} + */ + toStats() { + return { + byteLength: this.byteLength, + } + } + /** @inheritdoc */ getByteLength() { return this.byteLength diff --git a/libraries/overleaf-editor-core/lib/file_data/hollow_string_file_data.js b/libraries/overleaf-editor-core/lib/file_data/hollow_string_file_data.js index 717c060ef0..1b392d3616 100644 --- a/libraries/overleaf-editor-core/lib/file_data/hollow_string_file_data.js +++ b/libraries/overleaf-editor-core/lib/file_data/hollow_string_file_data.js @@ -42,6 +42,15 @@ class HollowStringFileData extends FileData { return { stringLength: this.stringLength } } + /** + * @returns {Record} + */ + toStats() { + return { + stringLength: this.stringLength, + } + } + /** @inheritdoc */ getStringLength() { return this.stringLength diff --git a/libraries/overleaf-editor-core/lib/file_data/index.js b/libraries/overleaf-editor-core/lib/file_data/index.js index a6ae574a26..ffb4a28a80 100644 --- a/libraries/overleaf-editor-core/lib/file_data/index.js +++ b/libraries/overleaf-editor-core/lib/file_data/index.js @@ -81,6 +81,13 @@ class FileData { throw new Error('FileData: toRaw not implemented') } + /** + * @returns {Record} + */ + toStats() { + throw new Error('FileData: toStats not implemented') + } + /** * @see File#getHash * @return {string | null | undefined} diff --git a/libraries/overleaf-editor-core/lib/file_data/lazy_string_file_data.js b/libraries/overleaf-editor-core/lib/file_data/lazy_string_file_data.js index abc720d10c..fd06524595 100644 --- a/libraries/overleaf-editor-core/lib/file_data/lazy_string_file_data.js +++ b/libraries/overleaf-editor-core/lib/file_data/lazy_string_file_data.js @@ -71,6 +71,25 @@ class LazyStringFileData extends FileData { return raw } + /** + * @returns {Record} + */ + toStats() { + return { + hashes: 1 + (this.rangesHash ? 1 : 0), + stringLength: this.stringLength, + nOperations: this.operations.length, + operationsSize: + this.operations.length > 0 + ? this.operations.reduce( + // Note: Buffer does not exist in frontend. Use string length instead. + (sum, op) => sum + JSON.stringify(op.toJSON()).length, + 0 + ) + : 0, + } + } + /** @inheritdoc */ getHash() { if (this.operations.length) return null diff --git a/libraries/overleaf-editor-core/lib/file_data/string_file_data.js b/libraries/overleaf-editor-core/lib/file_data/string_file_data.js index c78c1e0414..57bd2cd56d 100644 --- a/libraries/overleaf-editor-core/lib/file_data/string_file_data.js +++ b/libraries/overleaf-editor-core/lib/file_data/string_file_data.js @@ -58,6 +58,27 @@ class StringFileData extends FileData { return raw } + /** + * @returns {Record} + */ + toStats() { + // Note: Buffer does not exist in frontend. Use string length instead. + return { + nContent: 1, + contentSize: this.content.length, + nComments: this.comments.length, + commentsSize: + this.comments.length > 0 + ? JSON.stringify(this.comments.toRaw()).length + : 0, + nTrackedChanges: this.trackedChanges.length, + trackedChangesSize: + this.trackedChanges.length > 0 + ? JSON.stringify(this.trackedChanges.toRaw()).length + : 0, + } + } + /** @inheritdoc */ isEditable() { return true diff --git a/libraries/overleaf-editor-core/lib/file_map.js b/libraries/overleaf-editor-core/lib/file_map.js index 4412319ae3..bc75429254 100644 --- a/libraries/overleaf-editor-core/lib/file_map.js +++ b/libraries/overleaf-editor-core/lib/file_map.js @@ -126,6 +126,18 @@ class FileMap { return _.mapValues(this.files, fileToRaw) } + /** + * @returns {Map>} + */ + toStats() { + const sizes = new Map() + for (const [path, file] of Object.entries(this.files)) { + if (!file) continue + sizes.set(path, file.toStats()) + } + return sizes + } + /** * Create the given file. * diff --git a/services/web/frontend/js/infrastructure/project-snapshot.ts b/services/web/frontend/js/infrastructure/project-snapshot.ts index 0b564cc12b..3d8247437f 100644 --- a/services/web/frontend/js/infrastructure/project-snapshot.ts +++ b/services/web/frontend/js/infrastructure/project-snapshot.ts @@ -256,7 +256,7 @@ export class ProjectSnapshot { /** * Blob store that fetches blobs from the history service */ -class SimpleBlobStore { +export class SimpleBlobStore { private projectId: string constructor(projectId: string) { @@ -280,7 +280,7 @@ async function flushHistory(projectId: string) { await postJSON(`/project/${projectId}/flush`) } -async function fetchLatestChunk(projectId: string): Promise { +export async function fetchLatestChunk(projectId: string): Promise { const response = await getJSON<{ chunk: RawChunk }>( `/project/${projectId}/latest/history` )