keepass - ¿Cómo convertir "Buffer<ArrayBufferLike>" a "ArrayBuffer" en TypeScript 5.7?
Según el anuncio de TypeScript 5.7, ahora estoy afectado por el cambio de ES2024 para Buffers. Especialmente este cambio de los genéricos de Buffer.
Estoy trabajando con un archivo KeePass. Para eso, cargo el archivo y lo abro mediante el paquete npm "kdbxweb" "^2.1.1".
Contexto: "typescript": "^5.7.3", "@types/node": "^22.10.5". (es decir, actualmente las versiones más recientes)
import fs from 'fs';
import path from 'path';
import * as kdbxweb from 'kdbxweb';
async getClientSecret(): Promise<string> {
const fileBuffer: Buffer<ArrayBufferLike> = fs.readFileSync(KeePassFilePath);
const keePassFileBuffer: ArrayBufferLike = fileBuffer.buffer;
const keePassDB: kdbxweb.Kdbx = await kdbxweb.Kdbx.load(keePassFileBuffer, new kdbxweb.Credentials(kdbxweb.ProtectedValue.fromString(myPW)));
// ...
}
Después de la actualización a "ES2024" (a través de "ESNext"), npx tsc se queja:
error TS2345: Argument of type 'ArrayBufferLike' is not assignable to parameter of type 'ArrayBuffer'.
Type 'SharedArrayBuffer' is missing the following properties from type 'ArrayBuffer': resizable, resize, detached, transfer, transferToFixedLength
No sé cómo resolver esto. La función load de la biblioteca está declarada como:
static load(data: ArrayBuffer, credentials: KdbxCredentials, options?: {
preserveXml?: boolean;
}): Promise<Kdbx>;
Mientras fs.readFileSync() devuelve un Buffer<ArrayBufferLike> (proveniente de "@types/node": "^22.10.5"), lo que hace que la propiedad buffer devuelva un ArrayBufferLike.
¿Cómo puedo unirlos (sin esperar el error de TS o hacer un casting a unknown, etc.)? Es decir, ¿cómo convertir de Buffer<ArrayBufferLike> a ArrayBuffer correctamente?
Conversión a ciegas a
const fileBuffer = fs.readFileSync(KeePassFilePath) as Buffer<ArrayBuffer>;
silencia el error, pero ¿cómo se supone que debo saber el tipo correcto real? Imagina otros proveedores, que no dan algún tipo de garantía de que esto sea un ArrayBuffer real. Así que esto no es una solución viable para mí.
Respuesta #1
En tu código usas KeePassFileBuffer.buffer directamente, lo que puede causar una discrepancia potencial en el contenido, ya que este es el ArrayBuffer interno, que puede contener datos adicionales. De la manera en que lo usas no es muy probable, pero es mejor asegurarse de extraer la porción relevante.
Así es como puedes convertir el Buffer<ArrayBufferLike> a ArrayBuffer:
import fs from 'fs/promises';
import path from 'path';
import * as kdbxweb from 'kdbxweb';
import {ProtectedValue} from "kdbxweb";
// Convert Node.js Buffer to ArrayBuffer
function bufferToArrayBuffer(buffer: Buffer): ArrayBuffer {
return buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength) as ArrayBuffer;
}
// And apply that to your code
class SomeClass {
private keepassPassword: ProtectedValue = ProtectedValue.fromString('123');
async getClientSecret(): Promise<string> {
const KeePassFileBuffer = await fs.readFile(path.resolve('user.kdbx'));
const keePassDB: kdbxweb.Kdbx = await kdbxweb.Kdbx.load(bufferToArrayBuffer(KeePassFileBuffer), new kdbxweb.Credentials(this.keepassPassword));
// ....
return '?';
}
}
No está directamente relacionado con su problema, pero por favor note que cambié a la API asíncrona de Node.js fs/promises, que soporta mejor las promesas.
Respuesta #2
Lo siguiente parece funcionar y satisfacer al verificador de tipos, pero probablemente no sea la forma más eficiente de hacerlo, ya que implica la creación de algunos Uint8Arrays.
const fileBuffer: ArrayBufferLike = new SharedArrayBuffer(16);
const ui8Array: Uint8Array<ArrayBufferLike> = new Uint8Array(fileBuffer);
const actualArrayBuffer: ArrayBuffer = new ArrayBuffer(ui8Array.length);
const actualArrayBufferView: Uint8Array<ArrayBuffer> = new Uint8Array(actualArrayBuffer);
actualArrayBufferView.set(ui8Array);
// actualArrayBuffer now holds the contents of fileBuffer, but is of type ArrayBuffer
