feat: expose file byte read API
This commit is contained in:
@@ -22,6 +22,7 @@ describe('VerstakPluginAPI contract', () => {
|
||||
expect(typeof api.files.list).toBe('function');
|
||||
expect(typeof api.files.metadata).toBe('function');
|
||||
expect(typeof api.files.readText).toBe('function');
|
||||
expect(typeof api.files.readBytes).toBe('function');
|
||||
expect(typeof api.files.writeText).toBe('function');
|
||||
expect(typeof api.files.createFolder).toBe('function');
|
||||
expect(typeof api.files.move).toBe('function');
|
||||
@@ -250,6 +251,12 @@ describe('VerstakPluginAPI contract', () => {
|
||||
await api.files.createFolder('PlatformTest');
|
||||
await api.files.writeText('PlatformTest/one.txt', 'hello', { createIfMissing: true });
|
||||
await expect(api.files.readText('PlatformTest/one.txt')).resolves.toBe('hello');
|
||||
await expect(api.files.readBytes('PlatformTest/one.txt')).resolves.toEqual({
|
||||
relativePath: 'PlatformTest/one.txt',
|
||||
size: 5,
|
||||
mimeHint: 'text/plain; charset=utf-8',
|
||||
dataBase64: 'aGVsbG8=',
|
||||
});
|
||||
await expect(api.files.list('PlatformTest')).resolves.toEqual([
|
||||
expect.objectContaining({ relativePath: 'PlatformTest/one.txt', type: 'file' }),
|
||||
]);
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
import type {
|
||||
CapabilityEntry,
|
||||
FileBytes,
|
||||
FileEntry,
|
||||
FileMetadata,
|
||||
MovePathOptions,
|
||||
@@ -118,6 +119,7 @@ export interface VerstakPluginAPI {
|
||||
list(relativeDir?: string): Promise<FileEntry[]>;
|
||||
metadata(relativePath: string): Promise<FileMetadata>;
|
||||
readText(relativePath: string): Promise<string>;
|
||||
readBytes(relativePath: string): Promise<FileBytes>;
|
||||
writeText(relativePath: string, content: string, options?: WriteTextOptions): Promise<void>;
|
||||
createFolder(relativePath: string): Promise<void>;
|
||||
move(fromRelativePath: string, toRelativePath: string, options?: MovePathOptions): Promise<void>;
|
||||
|
||||
@@ -82,6 +82,22 @@ export function createMockPluginAPI(pluginId = 'test.plugin', options: MockPlugi
|
||||
return idx === -1 ? path : path.slice(idx + 1);
|
||||
}
|
||||
|
||||
function base64FromString(value: string): string {
|
||||
const alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
|
||||
let result = '';
|
||||
let i = 0;
|
||||
while (i < value.length) {
|
||||
const a = value.charCodeAt(i++) & 0xff;
|
||||
const b = i < value.length ? value.charCodeAt(i++) & 0xff : NaN;
|
||||
const c = i < value.length ? value.charCodeAt(i++) & 0xff : NaN;
|
||||
result += alphabet[a >> 2];
|
||||
result += alphabet[((a & 3) << 4) | (Number.isNaN(b) ? 0 : b >> 4)];
|
||||
result += Number.isNaN(b) ? '=' : alphabet[((b & 15) << 2) | (Number.isNaN(c) ? 0 : c >> 6)];
|
||||
result += Number.isNaN(c) ? '=' : alphabet[c & 63];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function entry(path: string, node: { type: 'file' | 'folder'; content?: string; modifiedAt: string }) {
|
||||
const name = baseName(path);
|
||||
const dot = name.lastIndexOf('.');
|
||||
@@ -190,6 +206,19 @@ export function createMockPluginAPI(pluginId = 'test.plugin', options: MockPlugi
|
||||
if (node.type !== 'file') throw new Error(`not-regular-file: ${path}`);
|
||||
return node.content || '';
|
||||
}),
|
||||
readBytes: vi.fn(async (relativePath: string) => {
|
||||
const path = normalizePath(relativePath);
|
||||
const node = files.get(path);
|
||||
if (!node) throw new Error(`not-found: ${path}`);
|
||||
if (node.type !== 'file') throw new Error(`not-regular-file: ${path}`);
|
||||
const content = node.content || '';
|
||||
return {
|
||||
relativePath: path,
|
||||
size: content.length,
|
||||
mimeHint: path.toLowerCase().endsWith('.txt') ? 'text/plain; charset=utf-8' : '',
|
||||
dataBase64: base64FromString(content),
|
||||
};
|
||||
}),
|
||||
writeText: vi.fn(async (relativePath: string, content: string, options = {}) => {
|
||||
const path = normalizePath(relativePath);
|
||||
const node = files.get(path);
|
||||
|
||||
@@ -122,6 +122,13 @@ export interface FileMetadata {
|
||||
canWrite: boolean;
|
||||
}
|
||||
|
||||
export interface FileBytes {
|
||||
relativePath: string;
|
||||
size: number;
|
||||
mimeHint: string;
|
||||
dataBase64: string;
|
||||
}
|
||||
|
||||
export interface WriteTextOptions {
|
||||
/** Create the file when it is missing. Parent folder must already exist. */
|
||||
createIfMissing?: boolean;
|
||||
|
||||
Reference in New Issue
Block a user