From d717a891632a74635d6ecec93d2391d77be87890 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Tue, 6 Jan 2026 12:16:38 +0200 Subject: [PATCH] chore(core): fix references to Buffer --- packages/commons/src/lib/rows.ts | 4 ++-- packages/commons/src/lib/server_api.ts | 2 +- packages/commons/src/lib/ws_api.ts | 2 +- .../becca/entities/abstract_becca_entity.ts | 23 +++++++++++-------- .../src/becca/entities/battachment.ts | 6 ++--- .../trilium-core/src/becca/entities/bblob.ts | 2 +- .../trilium-core/src/becca/entities/bnote.ts | 2 +- .../src/becca/entities/brevision.ts | 6 ++--- packages/trilium-core/src/services/blob.ts | 6 ++--- .../services/encryption/data_encryption.ts | 4 ++-- .../trilium-core/src/services/sql/types.ts | 2 +- .../trilium-core/src/services/utils/binary.ts | 21 +++++++++++++++++ 12 files changed, 53 insertions(+), 27 deletions(-) diff --git a/packages/commons/src/lib/rows.ts b/packages/commons/src/lib/rows.ts index d1e8a0e409..03599a8b61 100644 --- a/packages/commons/src/lib/rows.ts +++ b/packages/commons/src/lib/rows.ts @@ -68,7 +68,7 @@ export interface EtapiTokenRow { export interface BlobRow { blobId: string; - content: string | Buffer; + content: string | Uint8Array; contentLength: number; dateModified: string; utcDateModified: string; @@ -137,6 +137,6 @@ export interface NoteRow { dateModified?: string; utcDateCreated?: string; utcDateModified?: string; - content?: string | Buffer; + content?: string | Uint8Array; } diff --git a/packages/commons/src/lib/server_api.ts b/packages/commons/src/lib/server_api.ts index a15192fd28..5a78d36769 100644 --- a/packages/commons/src/lib/server_api.ts +++ b/packages/commons/src/lib/server_api.ts @@ -50,7 +50,7 @@ export interface RevisionPojo { utcDateLastEdited?: string; utcDateCreated?: string; utcDateModified?: string; - content?: string | Buffer; + content?: string | Uint8Array; contentLength?: number; } diff --git a/packages/commons/src/lib/ws_api.ts b/packages/commons/src/lib/ws_api.ts index 67beb0b42e..5b2b164a3f 100644 --- a/packages/commons/src/lib/ws_api.ts +++ b/packages/commons/src/lib/ws_api.ts @@ -18,7 +18,7 @@ export interface EntityChange { export interface EntityRow { isDeleted?: boolean; - content?: Buffer | string; + content?: Uint8Array | string; } export interface EntityChangeRecord { diff --git a/packages/trilium-core/src/becca/entities/abstract_becca_entity.ts b/packages/trilium-core/src/becca/entities/abstract_becca_entity.ts index b224349ca2..d14626781e 100644 --- a/packages/trilium-core/src/becca/entities/abstract_becca_entity.ts +++ b/packages/trilium-core/src/becca/entities/abstract_becca_entity.ts @@ -10,6 +10,7 @@ import utils from "../../services/utils.js"; import becca from "../becca.js"; import type { ConstructorData,default as Becca } from "../becca-interface.js"; import { getSql } from "src/services/sql"; +import { concat2, encodeUtf8, unwrapStringOrBuffer, wrapStringOrBuffer } from "src/services/utils/binary"; interface ContentOpts { forceSave?: boolean; @@ -137,7 +138,7 @@ abstract class AbstractBeccaEntity> { return this; } - protected _setContent(content: string | Buffer, opts: ContentOpts = {}) { + protected _setContent(content: string | Uint8Array, opts: ContentOpts = {}) { // client code asks to save entity even if blobId didn't change (something else was changed) opts.forceSave = !!opts.forceSave; opts.forceFrontendReload = !!opts.forceFrontendReload; @@ -148,9 +149,9 @@ abstract class AbstractBeccaEntity> { } if (this.hasStringContent()) { - content = content.toString(); + content = unwrapStringOrBuffer(content); } else { - content = Buffer.isBuffer(content) ? content : Buffer.from(content); + content = wrapStringOrBuffer(content); } const unencryptedContentForHashCalculation = this.getUnencryptedContentForHashCalculation(content); @@ -202,17 +203,21 @@ abstract class AbstractBeccaEntity> { sql.execute("DELETE FROM entity_changes WHERE entityName = 'blobs' AND entityId = ?", [oldBlobId]); } - private getUnencryptedContentForHashCalculation(unencryptedContent: Buffer | string) { + private getUnencryptedContentForHashCalculation(unencryptedContent: Uint8Array | string) { if (this.isProtected) { // a "random" prefix makes sure that the calculated hash/blobId is different for a decrypted/encrypted content const encryptedPrefixSuffix = "t$[nvQg7q)&_ENCRYPTED_?M:Bf&j3jr_"; - return Buffer.isBuffer(unencryptedContent) ? Buffer.concat([Buffer.from(encryptedPrefixSuffix), unencryptedContent]) : `${encryptedPrefixSuffix}${unencryptedContent}`; + if (typeof unencryptedContent === "string") { + return `${encryptedPrefixSuffix}${unencryptedContent}`; + } else { + return concat2(encodeUtf8(encryptedPrefixSuffix), unencryptedContent) + } } return unencryptedContent; } - private saveBlob(content: string | Buffer, unencryptedContentForHashCalculation: string | Buffer, opts: ContentOpts = {}) { + private saveBlob(content: string | Uint8Array, unencryptedContentForHashCalculation: string | Uint8Array, opts: ContentOpts = {}) { /* * We're using the unencrypted blob for the hash calculation, because otherwise the random IV would * cause every content blob to be unique which would balloon the database size (esp. with revisioning). @@ -260,16 +265,16 @@ abstract class AbstractBeccaEntity> { return newBlobId; } - protected _getContent(): string | Buffer { + protected _getContent(): string | Uint8Array { const sql = getSql(); - const row = sql.getRow<{ content: string | Buffer }>(/*sql*/`SELECT content FROM blobs WHERE blobId = ?`, [this.blobId]); + const row = sql.getRow<{ content: string | Uint8Array }>(/*sql*/`SELECT content FROM blobs WHERE blobId = ?`, [this.blobId]); if (!row) { const constructorData = this.constructor as unknown as ConstructorData; throw new Error(`Cannot find content for ${constructorData.primaryKeyName} '${(this as any)[constructorData.primaryKeyName]}', blobId '${this.blobId}'`); } - return blobService.processContent(row.content, this.isProtected || false, this.hasStringContent()) as string | Buffer; + return blobService.processContent(row.content, this.isProtected || false, this.hasStringContent()) as string | Uint8Array; } /** diff --git a/packages/trilium-core/src/becca/entities/battachment.ts b/packages/trilium-core/src/becca/entities/battachment.ts index cfd0a638b9..b8b86ace1a 100644 --- a/packages/trilium-core/src/becca/entities/battachment.ts +++ b/packages/trilium-core/src/becca/entities/battachment.ts @@ -136,11 +136,11 @@ class BAttachment extends AbstractBeccaEntity { } } - getContent(): Buffer { - return this._getContent() as Buffer; + getContent(): Uint8Array { + return this._getContent() as Uint8Array; } - setContent(content: string | Buffer, opts?: ContentOpts) { + setContent(content: string | Uint8Array, opts?: ContentOpts) { this._setContent(content, opts); } diff --git a/packages/trilium-core/src/becca/entities/bblob.ts b/packages/trilium-core/src/becca/entities/bblob.ts index 2cff185d5c..a3ec261382 100644 --- a/packages/trilium-core/src/becca/entities/bblob.ts +++ b/packages/trilium-core/src/becca/entities/bblob.ts @@ -13,7 +13,7 @@ class BBlob extends AbstractBeccaEntity { return ["blobId", "content"]; } - content!: string | Buffer; + content!: string | Uint8Array; contentLength!: number; constructor(row: BlobRow) { diff --git a/packages/trilium-core/src/becca/entities/bnote.ts b/packages/trilium-core/src/becca/entities/bnote.ts index 3d9a9fe2cc..4f7b70a3c0 100644 --- a/packages/trilium-core/src/becca/entities/bnote.ts +++ b/packages/trilium-core/src/becca/entities/bnote.ts @@ -251,7 +251,7 @@ class BNote extends AbstractBeccaEntity { } } - setContent(content: Buffer | string, opts: ContentOpts = {}) { + setContent(content: Uint8Array | string, opts: ContentOpts = {}) { this._setContent(content, opts); eventService.emit(eventService.NOTE_CONTENT_CHANGE, { entity: this }); diff --git a/packages/trilium-core/src/becca/entities/brevision.ts b/packages/trilium-core/src/becca/entities/brevision.ts index 2df8a4c9e6..c09e9ff211 100644 --- a/packages/trilium-core/src/becca/entities/brevision.ts +++ b/packages/trilium-core/src/becca/entities/brevision.ts @@ -42,7 +42,7 @@ class BRevision extends AbstractBeccaEntity { dateLastEdited?: string; utcDateLastEdited?: string; contentLength?: number; - content?: string | Buffer; + content?: string | Uint8Array; constructor(row: RevisionRow, titleDecrypted = false) { super(); @@ -95,7 +95,7 @@ class BRevision extends AbstractBeccaEntity { * * This is the same approach as is used for Note's content. */ - getContent(): string | Buffer { + getContent(): string | Uint8Array { return this._getContent(); } @@ -120,7 +120,7 @@ class BRevision extends AbstractBeccaEntity { } } - setContent(content: string | Buffer, opts: ContentOpts = {}) { + setContent(content: string | Uint8Array, opts: ContentOpts = {}) { this._setContent(content, opts); } diff --git a/packages/trilium-core/src/services/blob.ts b/packages/trilium-core/src/services/blob.ts index 912ed64164..06e76d5bd9 100644 --- a/packages/trilium-core/src/services/blob.ts +++ b/packages/trilium-core/src/services/blob.ts @@ -23,13 +23,13 @@ function getBlobPojo(entityName: string, entityId: string, opts?: { preview: boo if (!entity.hasStringContent()) { pojo.content = null; } else { - pojo.content = processContent(pojo.content, !!entity.isProtected, true) as string | Buffer; + pojo.content = processContent(pojo.content, !!entity.isProtected, true) as string | Uint8Array; } return pojo; } -function processContent(content: Buffer | Uint8Array | string | null, isProtected: boolean, isStringContent: boolean) { +function processContent(content: Uint8Array | string | null, isProtected: boolean, isStringContent: boolean) { if (isProtected) { if (protectedSessionService.isProtectedSessionAvailable()) { content = content === null ? null : protectedSessionService.decrypt(content as Uint8Array); @@ -47,7 +47,7 @@ function processContent(content: Buffer | Uint8Array | string | null, isProtecte // IIRC a zero-sized buffer can be returned as null from the database if (content === null) { // this will force de/encryption - content = Buffer.alloc(0); + content = new Uint8Array(0); } return content; diff --git a/packages/trilium-core/src/services/encryption/data_encryption.ts b/packages/trilium-core/src/services/encryption/data_encryption.ts index e6bb9d1e84..ffae2c1e9d 100644 --- a/packages/trilium-core/src/services/encryption/data_encryption.ts +++ b/packages/trilium-core/src/services/encryption/data_encryption.ts @@ -1,5 +1,5 @@ import { getLog } from "../log.js"; -import { concat2, decodeBase64, decodeUtf8, encodeBase64 } from "../utils/binary.js"; +import { concat2, decodeBase64, decodeUtf8, encodeBase64, encodeUtf8 } from "../utils/binary.js"; import { getCrypto } from "./crypto.js"; function arraysIdentical(a: any[] | Uint8Array, b: any[] | Uint8Array) { @@ -55,7 +55,7 @@ function decrypt(key: Uint8Array, cipherText: string | Uint8Array): Uint8Array | } if (!key) { - return Uint8Array.from("[protected]"); + return encodeUtf8("[protected]"); } try { diff --git a/packages/trilium-core/src/services/sql/types.ts b/packages/trilium-core/src/services/sql/types.ts index eff81ae23b..e4bd5cb1c7 100644 --- a/packages/trilium-core/src/services/sql/types.ts +++ b/packages/trilium-core/src/services/sql/types.ts @@ -21,7 +21,7 @@ export interface RunResult { export interface DatabaseProvider { loadFromFile(path: string, isReadOnly: boolean): void; loadFromMemory(): void; - loadFromBuffer(buffer: NonSharedBuffer): void; + loadFromBuffer(buffer: Uint8Array): void; backup(destinationFile: string): void; prepare(query: string): Statement; transaction(func: (statement: Statement) => T): Transaction; diff --git a/packages/trilium-core/src/services/utils/binary.ts b/packages/trilium-core/src/services/utils/binary.ts index 5861ac9067..94272039f6 100644 --- a/packages/trilium-core/src/services/utils/binary.ts +++ b/packages/trilium-core/src/services/utils/binary.ts @@ -1,4 +1,5 @@ const utf8Decoder = new TextDecoder("utf-8"); +const utf8Encoder = new TextEncoder(); export function concat2(a: Uint8Array, b: Uint8Array): Uint8Array { const out = new Uint8Array(a.length + b.length); @@ -33,3 +34,23 @@ export function decodeBase64(base64: string): Uint8Array { export function decodeUtf8(bytes: Uint8Array) { return utf8Decoder.decode(bytes); } + +export function encodeUtf8(string: string) { + return utf8Encoder.encode(string); +} + +export function unwrapStringOrBuffer(stringOrBuffer: string | Uint8Array) { + if (typeof stringOrBuffer === "string") { + return stringOrBuffer; + } else { + return decodeUtf8(stringOrBuffer); + } +} + +export function wrapStringOrBuffer(stringOrBuffer: string | Uint8Array) { + if (typeof stringOrBuffer === "string") { + return encodeUtf8(stringOrBuffer); + } else { + return stringOrBuffer; + } +}