diff --git a/README.md b/README.md index 13bd1fb..5c396bc 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,31 @@ # verstak-sdk -Verstak Plugin SDK — manifest schema, TypeScript SDK, RPC protocol, capability contracts, event schemas, test helpers, packaging tools \ No newline at end of file +Verstak Plugin SDK — manifest schema, TypeScript SDK, RPC protocol, capability contracts, event schemas, test helpers, packaging tools + +## Bundled Frontend API Contract + +Verstak Desktop creates the real API with `createPluginAPI(pluginId)` and passes +it to bundled plugin components at mount time. The SDK exports TypeScript types +for that host-provided object: + +- `settings.read/write/writeAll` +- `capabilities.list/get/has` +- `commands.register/execute` +- `events.publish/subscribe` +- `files.list/metadata/readText/writeText/createFolder/move/trash` +- `workbench.openResource/editResource` +- optional `dispose` + +Files paths are canonical vault-relative slash paths. Backslashes, Windows/UNC +absolute paths, traversal, null bytes, `.verstak` variants, and symlink +read/write/move/trash operations are rejected by the host. Files read/write is +UTF-8 text-only in the current runtime. + +Open/edit routing uses `OpenResourceRequest` with `kind: "vault-file"` and +contexts `generic-text`, `generic-markdown`, and `notes-markdown`. Plugins that +request routing declare `workbench.open`; editor/viewer plugins contribute +`contributes.openProviders`. A no-match route returns `status: "no-provider"`. + +Bundled frontend plugins are trusted/cooperative and run in the desktop JS +context. Current permission checks are contract checks, not a security boundary; +real isolation belongs to a later sidecar/sandbox milestone. diff --git a/dist/index.js b/dist/index.js index d1f97ba..132b1bd 100644 --- a/dist/index.js +++ b/dist/index.js @@ -1,6 +1,6 @@ // Verstak Plugin SDK — Public API export * from './types'; -export { VerstakPluginAPI, createPluginAPI } from './plugin-api'; +export { createPluginAPI } from './plugin-api'; export { RPCServer, RPCClient } from './rpc'; export { createTestManifest, createTestPluginState, createMockPluginAPI, validateManifest, } from './test-utils'; //# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/dist/index.js.map b/dist/index.js.map index 6c29ae3..77ae85e 100644 --- a/dist/index.js.map +++ b/dist/index.js.map @@ -1 +1 @@ -{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,kCAAkC;AAElC,cAAc,SAAS,CAAC;AACxB,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AACjE,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAC7C,OAAO,EACL,kBAAkB,EAClB,qBAAqB,EACrB,mBAAmB,EACnB,gBAAgB,GACjB,MAAM,cAAc,CAAC"} \ No newline at end of file +{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,kCAAkC;AAElC,cAAc,SAAS,CAAC;AACxB,OAAO,EAAoB,eAAe,EAAE,MAAM,cAAc,CAAC;AACjE,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAC7C,OAAO,EACL,kBAAkB,EAClB,qBAAqB,EACrB,mBAAmB,EACnB,gBAAgB,GACjB,MAAM,cAAc,CAAC"} \ No newline at end of file diff --git a/dist/plugin-api.d.ts b/dist/plugin-api.d.ts index 5093c6f..52cf166 100644 --- a/dist/plugin-api.d.ts +++ b/dist/plugin-api.d.ts @@ -1,79 +1,71 @@ -import type { PluginSettings } from './types'; -/** - * VerstakPluginAPI — единственный способ для frontend плагина - * общаться с core платформы. - * - * Экземпляр API передаётся плагину при активации через глобальную - * переменную `window.__VERSTAK_PLUGIN_API__`. - */ -export declare class VerstakPluginAPI { - private pluginId; - private capabilities; - constructor(pluginId: string); - /** - * Инициализация API — вызывается core после загрузки frontend bundle. - * @internal - */ - _init(capabilities: string[]): void; - /** - * Зарегистрировать view для отображения в UI Shell. - */ - registerView(id: string, component: unknown): void; - /** - * Зарегистрировать панель настроек плагина. - */ - registerSettingsPanel(id: string, title: string, component: unknown): void; - /** - * Зарегистрировать команду для command palette. - */ - registerCommand(id: string, title: string, handler: () => void, keybinding?: string): void; - /** - * Зарегистрировать действия для файлов. - */ - registerFileAction(id: string, label: string, handler: (filePath: string) => void, capability?: string): void; - /** - * Зарегистрировать действия для заметок. - */ - registerNoteAction(id: string, label: string, handler: (noteId: string) => void, capability?: string): void; - /** - * Зарегистрировать provider поиска. - */ - registerSearchProvider(id: string, label: string, handler: (query: string) => unknown[]): void; - /** - * Проверить, доступна ли capability. - */ - hasCapability(name: string): boolean; - /** - * Получить список всех доступных capabilities. - */ - getAvailableCapabilities(): string[]; - /** - * Вызвать backend метод плагина через RPC. - */ - callBackend(method: string, args?: unknown[]): Promise; - /** - * Прочитать настройки плагина. - */ - readSettings(): Promise; - /** - * Записать настройки плагина. - */ - writeSettings(settings: PluginSettings): Promise; - /** - * Подписаться на событие event bus. - */ - subscribe(event: string, handler: (payload: unknown) => void): void; - /** - * Опубликовать событие в event bus. - */ - publish(event: string, payload: unknown): void; - private _postMessage; - private _rpcCall; +import type { CapabilityEntry, FileEntry, FileMetadata, MovePathOptions, OpenResourceRequest, OpenResourceResult, PluginSettings, TrashResult, WriteTextOptions } from './types'; +export type PluginCommandArgs = Record; +export type PluginCommandHandler = (args: PluginCommandArgs, declaration: PluginCommandDeclaration) => unknown | Promise; +export type Unsubscribe = () => void; +export interface PluginCommandDeclaration { + status: 'declared'; + pluginId: string; + commandId: string; + handler?: string; + args?: PluginCommandArgs; } -/** - * Создать экземпляр VerstakPluginAPI. - * Core вызывает эту функцию после загрузки frontend bundle, - * передавая pluginId и список доступных capabilities. - */ -export declare function createPluginAPI(pluginId: string): VerstakPluginAPI; +export interface PluginCommandResult { + status: 'handled'; + pluginId: string; + commandId: string; + result: unknown; +} +export interface PluginEvent> { + name: string; + pluginId: string; + payload: TPayload; + timestamp: string; +} +export interface VerstakPluginAPI { + readonly pluginId: string; + settings: { + read(): Promise; + read(key: string): Promise; + write(key: string, value: unknown): Promise; + writeAll(settings: PluginSettings): Promise; + }; + capabilities: { + has(capability: string): Promise; + get(capability: string): Promise<{ + available: boolean; + name?: string; + pluginId?: string; + status?: string; + }>; + list(): Promise; + }; + commands: { + register(commandId: string, handler: PluginCommandHandler): Promise; + execute(commandId: string, args?: PluginCommandArgs): Promise; + }; + events: { + publish(eventName: string, payload?: Record): Promise; + subscribe>(eventName: string, handler: (event: PluginEvent) => void): Promise; + }; + files: { + /** + * Files API uses canonical vault-relative slash paths. Backslashes, + * Windows/UNC absolute paths, traversal, null bytes, `.verstak` variants, + * and symlink read/write/move/trash operations are rejected by the host. + */ + list(relativeDir?: string): Promise; + metadata(relativePath: string): Promise; + readText(relativePath: string): Promise; + writeText(relativePath: string, content: string, options?: WriteTextOptions): Promise; + createFolder(relativePath: string): Promise; + move(fromRelativePath: string, toRelativePath: string, options?: MovePathOptions): Promise; + trash(relativePath: string): Promise; + }; + workbench: { + openResource(request: OpenResourceRequest): Promise; + editResource(request: OpenResourceRequest): Promise; + }; + dispose?: () => void; +} +export declare function createPluginAPI(_pluginId: string): VerstakPluginAPI; //# sourceMappingURL=plugin-api.d.ts.map \ No newline at end of file diff --git a/dist/plugin-api.d.ts.map b/dist/plugin-api.d.ts.map index 0391324..132feae 100644 --- a/dist/plugin-api.d.ts.map +++ b/dist/plugin-api.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"plugin-api.d.ts","sourceRoot":"","sources":["../src/plugin-api.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAE9C;;;;;;GAMG;AACH,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,YAAY,CAAqB;gBAE7B,QAAQ,EAAE,MAAM;IAI5B;;;OAGG;IACH,KAAK,CAAC,YAAY,EAAE,MAAM,EAAE,GAAG,IAAI;IAMnC;;OAEG;IACH,YAAY,CAAC,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,GAAG,IAAI;IAIlD;;OAEG;IACH,qBAAqB,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,GAAG,IAAI;IAI1E;;OAEG;IACH,eAAe,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,IAAI,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI;IAI1F;;OAEG;IACH,kBAAkB,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI;IAI7G;;OAEG;IACH,kBAAkB,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI;IAI3G;;OAEG;IACH,sBAAsB,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,EAAE,GAAG,IAAI;IAM9F;;OAEG;IACH,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAIpC;;OAEG;IACH,wBAAwB,IAAI,MAAM,EAAE;IAMpC;;OAEG;IACG,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,GAAE,OAAO,EAAO,GAAG,OAAO,CAAC,OAAO,CAAC;IAMzE;;OAEG;IACG,YAAY,IAAI,OAAO,CAAC,cAAc,CAAC;IAK7C;;OAEG;IACG,aAAa,CAAC,QAAQ,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;IAM5D;;OAEG;IACH,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,GAAG,IAAI;IAInE;;OAEG;IACH,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,IAAI;IAM9C,OAAO,CAAC,YAAY;YAMN,QAAQ;CAiBvB;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,gBAAgB,CAGlE"} \ No newline at end of file +{"version":3,"file":"plugin-api.d.ts","sourceRoot":"","sources":["../src/plugin-api.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/plugin-api.js b/dist/plugin-api.js index 671f7b4..0dff44b 100644 --- a/dist/plugin-api.js +++ b/dist/plugin-api.js @@ -1,141 +1,10 @@ -// Verstak Plugin SDK — VerstakPluginAPI -// The official runtime API available to all plugins in the frontend context. -/** - * VerstakPluginAPI — единственный способ для frontend плагина - * общаться с core платформы. - * - * Экземпляр API передаётся плагину при активации через глобальную - * переменную `window.__VERSTAK_PLUGIN_API__`. - */ -export class VerstakPluginAPI { - pluginId; - capabilities = new Set(); - constructor(pluginId) { - this.pluginId = pluginId; - } - /** - * Инициализация API — вызывается core после загрузки frontend bundle. - * @internal - */ - _init(capabilities) { - this.capabilities = new Set(capabilities); - } - // ─── View Registration ───────────────────────────────────── - /** - * Зарегистрировать view для отображения в UI Shell. - */ - registerView(id, component) { - this._postMessage('register.view', { id, component }); - } - /** - * Зарегистрировать панель настроек плагина. - */ - registerSettingsPanel(id, title, component) { - this._postMessage('register.settingsPanel', { id, title, component }); - } - /** - * Зарегистрировать команду для command palette. - */ - registerCommand(id, title, handler, keybinding) { - this._postMessage('register.command', { id, title, keybinding, handler: handler.toString() }); - } - /** - * Зарегистрировать действия для файлов. - */ - registerFileAction(id, label, handler, capability) { - this._postMessage('register.fileAction', { id, label, handler: handler.toString(), capability }); - } - /** - * Зарегистрировать действия для заметок. - */ - registerNoteAction(id, label, handler, capability) { - this._postMessage('register.noteAction', { id, label, handler: handler.toString(), capability }); - } - /** - * Зарегистрировать provider поиска. - */ - registerSearchProvider(id, label, handler) { - this._postMessage('register.searchProvider', { id, label, handler: handler.toString() }); - } - // ─── Capabilities ────────────────────────────────────────── - /** - * Проверить, доступна ли capability. - */ - hasCapability(name) { - return this.capabilities.has(name); - } - /** - * Получить список всех доступных capabilities. - */ - getAvailableCapabilities() { - return Array.from(this.capabilities); - } - // ─── Backend Communication ───────────────────────────────── - /** - * Вызвать backend метод плагина через RPC. - */ - async callBackend(method, args = []) { - return this._rpcCall(method, args); - } - // ─── Settings ────────────────────────────────────────────── - /** - * Прочитать настройки плагина. - */ - async readSettings() { - const result = await this._rpcCall('readSettings', []); - return result; - } - /** - * Записать настройки плагина. - */ - async writeSettings(settings) { - await this._rpcCall('writeSettings', [settings]); - } - // ─── Event Bus ───────────────────────────────────────────── - /** - * Подписаться на событие event bus. - */ - subscribe(event, handler) { - this._postMessage('subscribe', { event, handler: handler.toString() }); - } - /** - * Опубликовать событие в event bus. - */ - publish(event, payload) { - this._postMessage('publish', { event, payload }); - } - // ─── Internal ────────────────────────────────────────────── - _postMessage(type, data) { - window.dispatchEvent(new CustomEvent('verstak:plugin', { - detail: { pluginId: this.pluginId, type, data } - })); - } - async _rpcCall(method, args) { - return new Promise((resolve, reject) => { - const callId = `${this.pluginId}:${Date.now()}:${Math.random()}`; - const handler = (event) => { - if (event.detail.callId === callId) { - window.removeEventListener('verstak:rpc:response', handler); - if (event.detail.error) { - reject(new Error(event.detail.error)); - } - else { - resolve(event.detail.result); - } - } - }; - window.addEventListener('verstak:rpc:response', handler); - this._postMessage('rpc', { callId, method, args }); - }); - } -} -/** - * Создать экземпляр VerstakPluginAPI. - * Core вызывает эту функцию после загрузки frontend bundle, - * передавая pluginId и список доступных capabilities. - */ -export function createPluginAPI(pluginId) { - const api = new VerstakPluginAPI(pluginId); - return api; +// Verstak Plugin SDK — bundled frontend plugin API contract. +// +// The desktop host creates the real API with createPluginAPI(pluginId) inside +// VerstakPluginAPI.js and passes it to bundled plugin components at mount time. +// This SDK file intentionally exposes the TypeScript contract only; it is not +// a standalone security boundary or RPC client. +export function createPluginAPI(_pluginId) { + throw new Error('createPluginAPI is provided by Verstak Desktop at plugin runtime'); } //# sourceMappingURL=plugin-api.js.map \ No newline at end of file diff --git a/dist/plugin-api.js.map b/dist/plugin-api.js.map index 706f4a2..a07140b 100644 --- a/dist/plugin-api.js.map +++ b/dist/plugin-api.js.map @@ -1 +1 @@ -{"version":3,"file":"plugin-api.js","sourceRoot":"","sources":["../src/plugin-api.ts"],"names":[],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"plugin-api.js","sourceRoot":"","sources":["../src/plugin-api.ts"],"names":[],"mappings":"AAAA,6DAA6D;AAC7D,EAAE;AACF,8EAA8E;AAC9E,gFAAgF;AAChF,8EAA8E;AAC9E,gDAAgD;AA+FhD,MAAM,UAAU,eAAe,CAAC,SAAiB;IAC/C,MAAM,IAAI,KAAK,CAAC,kEAAkE,CAAC,CAAC;AACtF,CAAC"} \ No newline at end of file diff --git a/dist/test-utils.d.ts b/dist/test-utils.d.ts index d0ae995..a8d28b6 100644 --- a/dist/test-utils.d.ts +++ b/dist/test-utils.d.ts @@ -1,4 +1,5 @@ import type { PluginManifest, PluginState } from './types'; +import type { VerstakPluginAPI } from './plugin-api'; /** * Создать тестовый manifest для unit-тестов. */ @@ -10,15 +11,7 @@ export declare function createTestPluginState(overrides?: Partial): /** * Создать заглушку VerstakPluginAPI для тестов. */ -export declare function createMockPluginAPI(): { - registerView: ReturnType; - registerCommand: ReturnType; - registerSettingsPanel: ReturnType; - hasCapability: ReturnType; - callBackend: ReturnType; - subscribe: ReturnType; - publish: ReturnType; -}; +export declare function createMockPluginAPI(pluginId?: string): VerstakPluginAPI; /** * Валидатор plugin manifest. */ diff --git a/dist/test-utils.d.ts.map b/dist/test-utils.d.ts.map index 2f3d255..1418573 100644 --- a/dist/test-utils.d.ts.map +++ b/dist/test-utils.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"test-utils.d.ts","sourceRoot":"","sources":["../src/test-utils.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAE3D;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,SAAS,CAAC,EAAE,OAAO,CAAC,cAAc,CAAC,GAAG,cAAc,CAetF;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,SAAS,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,GAAG,WAAW,CASnF;AAED;;GAEG;AACH,wBAAgB,mBAAmB,IAAI;IACrC,YAAY,EAAE,UAAU,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC,CAAC;IACvC,eAAe,EAAE,UAAU,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC,CAAC;IAC1C,qBAAqB,EAAE,UAAU,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC,CAAC;IAChD,aAAa,EAAE,UAAU,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC,CAAC;IACxC,WAAW,EAAE,UAAU,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC,CAAC;IACtC,SAAS,EAAE,UAAU,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC,CAAC;IACpC,OAAO,EAAE,UAAU,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC,CAAC;CACnC,CAUA;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,OAAO,GAAG;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,EAAE,CAAA;CAAE,CAgCxF;AAGD,OAAO,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC5B,OAAO,EAAE,EAAE,EAAE,CAAC"} \ No newline at end of file +{"version":3,"file":"test-utils.d.ts","sourceRoot":"","sources":["../src/test-utils.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAC3D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAErD;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,SAAS,CAAC,EAAE,OAAO,CAAC,cAAc,CAAC,GAAG,cAAc,CAetF;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,SAAS,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,GAAG,WAAW,CASnF;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,SAAgB,GAAG,gBAAgB,CAsL9E;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,OAAO,GAAG;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,EAAE,CAAA;CAAE,CAgCxF;AAGD,OAAO,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC5B,OAAO,EAAE,EAAE,EAAE,CAAC"} \ No newline at end of file diff --git a/dist/test-utils.js b/dist/test-utils.js index 6b7407b..6e2870e 100644 --- a/dist/test-utils.js +++ b/dist/test-utils.js @@ -34,15 +34,202 @@ export function createTestPluginState(overrides) { /** * Создать заглушку VerstakPluginAPI для тестов. */ -export function createMockPluginAPI() { +export function createMockPluginAPI(pluginId = 'test.plugin') { + const settings = {}; + const commands = new Map(); + const eventHandlers = new Map(); + const files = new Map(); + files.set('', { type: 'folder', modifiedAt: new Date().toISOString() }); + function normalizePath(path, allowRoot = false) { + const raw = String(path || ''); + if (raw.includes('\0')) + throw new Error('invalid-path: null-byte'); + if (raw.includes('\\')) + throw new Error('invalid-path: backslash not allowed'); + const normalized = raw.replace(/^\.\//, ''); + const parts = normalized.split('/').filter(Boolean); + if (!allowRoot && parts.length === 0) + throw new Error('invalid-path: empty path'); + if (normalized.startsWith('/') || /^[A-Za-z]:/.test(normalized)) + throw new Error('invalid-path: absolute path rejected'); + if (parts.includes('..')) + throw new Error('invalid-path: path-traversal'); + if (parts[0] && parts[0].toLowerCase() === '.verstak') + throw new Error('reserved-path: .verstak is internal'); + return parts.join('/'); + } + function parentPath(path) { + const idx = path.lastIndexOf('/'); + return idx === -1 ? '' : path.slice(0, idx); + } + function baseName(path) { + const idx = path.lastIndexOf('/'); + return idx === -1 ? path : path.slice(idx + 1); + } + function entry(path, node) { + const name = baseName(path); + const dot = name.lastIndexOf('.'); + const extension = dot > 0 ? name.slice(dot + 1) : ''; + return { + name, + relativePath: path, + type: node.type, + size: node.type === 'file' ? (node.content || '').length : 0, + modifiedAt: node.modifiedAt, + extension, + isHidden: name.startsWith('.'), + isReserved: false, + canRead: true, + canWrite: true, + }; + } return { - registerView: vi.fn(), - registerCommand: vi.fn(), - registerSettingsPanel: vi.fn(), - hasCapability: vi.fn().mockReturnValue(false), - callBackend: vi.fn().mockResolvedValue(undefined), - subscribe: vi.fn(), - publish: vi.fn(), + pluginId, + settings: { + read: vi.fn(async (key) => key ? settings[key] : { ...settings }), + write: vi.fn(async (key, value) => { + settings[key] = value; + return { ...settings }; + }), + writeAll: vi.fn(async (nextSettings) => { + Object.keys(settings).forEach((key) => delete settings[key]); + Object.assign(settings, nextSettings); + }), + }, + capabilities: { + has: vi.fn(async () => false), + get: vi.fn(async (name) => ({ available: false, name })), + list: vi.fn(async () => []), + }, + commands: { + register: vi.fn(async (commandId, handler) => { + commands.set(commandId, handler); + return () => { commands.delete(commandId); }; + }), + execute: vi.fn(async (commandId, args = {}) => { + const handler = commands.get(commandId); + if (!handler) { + throw new Error(`declared-but-unhandled: ${commandId}`); + } + return { status: 'handled', pluginId, commandId, result: await handler(args) }; + }), + }, + events: { + publish: vi.fn(async (eventName, payload = {}) => { + const event = { name: eventName, pluginId, payload, timestamp: new Date().toISOString() }; + (eventHandlers.get(eventName) || []).slice().forEach((handler) => handler(event)); + }), + subscribe: vi.fn(async (eventName, handler) => { + const handlers = eventHandlers.get(eventName) || []; + handlers.push(handler); + eventHandlers.set(eventName, handlers); + return () => { + eventHandlers.set(eventName, (eventHandlers.get(eventName) || []).filter((item) => item !== handler)); + }; + }), + }, + files: { + list: vi.fn(async (relativeDir = '') => { + const dir = normalizePath(relativeDir, true); + const node = files.get(dir); + if (!node || node.type !== 'folder') + throw new Error(`not-found: ${dir}`); + const prefix = dir ? `${dir}/` : ''; + return Array.from(files.entries()) + .filter(([path]) => path !== dir && path.startsWith(prefix) && !path.slice(prefix.length).includes('/')) + .map(([path, node]) => entry(path, node)); + }), + metadata: vi.fn(async (relativePath) => { + const path = normalizePath(relativePath); + const node = files.get(path); + if (!node) + throw new Error(`not-found: ${path}`); + return { ...entry(path, node), mimeHint: '', isText: node.type === 'file' }; + }), + readText: vi.fn(async (relativePath) => { + 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}`); + return node.content || ''; + }), + writeText: vi.fn(async (relativePath, content, options = {}) => { + const path = normalizePath(relativePath); + const node = files.get(path); + if (node && node.type !== 'file') + throw new Error(`not-regular-file: ${path}`); + if (node && !options.overwrite) + throw new Error(`conflict: ${path}`); + if (!node && !options.createIfMissing) + throw new Error(`not-found: ${path}`); + const parent = parentPath(path); + if (!files.get(parent) || files.get(parent)?.type !== 'folder') + throw new Error(`parent-not-found: ${parent}`); + files.set(path, { type: 'file', content, modifiedAt: new Date().toISOString() }); + }), + createFolder: vi.fn(async (relativePath) => { + const path = normalizePath(relativePath); + if (files.has(path)) + throw new Error(`conflict: ${path}`); + const parent = parentPath(path); + if (!files.get(parent) || files.get(parent)?.type !== 'folder') + throw new Error(`parent-not-found: ${parent}`); + files.set(path, { type: 'folder', modifiedAt: new Date().toISOString() }); + }), + move: vi.fn(async (fromRelativePath, toRelativePath, options = {}) => { + const from = normalizePath(fromRelativePath); + const to = normalizePath(toRelativePath); + const node = files.get(from); + if (!node) + throw new Error(`not-found: ${from}`); + if (node.type === 'folder' && (to === from || to.startsWith(`${from}/`))) { + throw new Error(`move-into-self: ${from} -> ${to}`); + } + if (files.has(to) && !options.overwrite) + throw new Error(`conflict: ${to}`); + const parent = parentPath(to); + if (!files.get(parent) || files.get(parent)?.type !== 'folder') + throw new Error(`parent-not-found: ${parent}`); + const moving = Array.from(files.entries()).filter(([path]) => path === from || path.startsWith(`${from}/`)); + moving.forEach(([path, movingNode]) => { + const suffix = path.slice(from.length); + files.set(`${to}${suffix}`, movingNode); + files.delete(path); + }); + }), + trash: vi.fn(async (relativePath) => { + const path = normalizePath(relativePath); + if (!files.has(path)) + throw new Error(`not-found: ${path}`); + files.delete(path); + const trashId = `mock-${Date.now()}`; + return { + originalPath: path, + trashPath: `.verstak/trash/files/${trashId}/${baseName(path)}`, + trashId, + deletedAt: new Date().toISOString(), + }; + }), + }, + workbench: { + openResource: vi.fn(async (request) => ({ + status: 'opened', + providerId: request.context?.notesMode ? 'mock.notes-markdown-provider' : 'mock.open-provider', + providerPluginId: 'mock.editor', + providerComponent: 'MockEditor', + request: { ...request, mode: request.mode || 'view' }, + })), + editResource: vi.fn(async (request) => ({ + status: 'opened', + providerId: request.context?.notesMode ? 'mock.notes-markdown-provider' : 'mock.open-provider', + providerPluginId: 'mock.editor', + providerComponent: 'MockEditor', + request: { ...request, mode: 'edit' }, + })), + }, + dispose: vi.fn(), }; } /** diff --git a/dist/test-utils.js.map b/dist/test-utils.js.map index 4ff6659..44f0438 100644 --- a/dist/test-utils.js.map +++ b/dist/test-utils.js.map @@ -1 +1 @@ -{"version":3,"file":"test-utils.js","sourceRoot":"","sources":["../src/test-utils.ts"],"names":[],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"test-utils.js","sourceRoot":"","sources":["../src/test-utils.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/types.d.ts b/dist/types.d.ts index ae2cdd5..516da2a 100644 --- a/dist/types.d.ts +++ b/dist/types.d.ts @@ -41,15 +41,59 @@ export interface SyncConfig { export type CapabilityName = string; export interface CapabilityEntry { name: CapabilityName; - description: string; + description?: string; + pluginId: string; status: 'stable' | 'draft' | 'deprecated'; } -export type Permission = 'vault.read' | 'vault.write' | 'vault.watch' | 'storage.namespace' | 'storage.migrations' | 'events.publish' | 'events.subscribe' | 'ui.register' | 'commands.register' | 'network.local' | 'network.remote' | 'process.spawn' | 'secrets.read' | 'secrets.write' | 'sync.participate'; +export type Permission = 'vault.read' | 'vault.write' | 'vault.watch' | 'files.read' | 'files.write' | 'files.delete' | 'workbench.open' | 'storage.namespace' | 'storage.migrations' | 'events.publish' | 'events.subscribe' | 'ui.register' | 'commands.register' | 'network.local' | 'network.remote' | 'process.spawn' | 'secrets.read' | 'secrets.write' | 'sync.participate'; export interface PermissionEntry { name: Permission; description: string; dangerous: boolean; } +export type FileEntryType = 'file' | 'folder' | 'symlink' | 'unknown'; +export interface FileEntry { + name: string; + relativePath: string; + type: FileEntryType; + size: number; + modifiedAt: string; + extension: string; + isHidden: boolean; + isReserved: boolean; + canRead: boolean; + canWrite: boolean; +} +export interface FileMetadata { + relativePath: string; + type: FileEntryType; + size: number; + modifiedAt: string; + createdAt?: string; + extension: string; + mimeHint: string; + isText: boolean; + isHidden: boolean; + isReserved: boolean; + canRead: boolean; + canWrite: boolean; +} +export interface WriteTextOptions { + /** Create the file when it is missing. Parent folder must already exist. */ + createIfMissing?: boolean; + /** Replace an existing regular file. Existing folders/symlinks are rejected. */ + overwrite?: boolean; +} +export interface MovePathOptions { + /** Replace an existing target path when the host supports it. */ + overwrite?: boolean; +} +export interface TrashResult { + originalPath: string; + trashPath: string; + trashId: string; + deletedAt: string; +} export interface ContributionPoints { views?: ContributionView[]; commands?: ContributionCommand[]; @@ -61,6 +105,7 @@ export interface ContributionPoints { searchProviders?: ContributionSearchProvider[]; activityProviders?: ContributionActivityProvider[]; statusBarItems?: ContributionStatusBarItem[]; + openProviders?: ContributionOpenProvider[]; } export interface ContributionView { id: string; @@ -119,6 +164,45 @@ export interface ContributionStatusBarItem { position?: 'left' | 'right'; handler?: string; } +export type OpenResourceKind = 'vault-file'; +export type OpenResourceMode = 'view' | 'edit'; +export type OpenResourceContextName = 'generic-text' | 'generic-markdown' | 'notes-markdown' | string; +export interface OpenProviderSupport { + kind: OpenResourceKind; + extensions?: string[]; + mime?: string[]; + contexts?: OpenResourceContextName[]; +} +export interface ContributionOpenProvider { + id: string; + title: string; + priority?: number; + component: string; + supports: OpenProviderSupport[]; +} +export interface OpenResourceContext { + sourcePluginId?: string; + sourceView?: 'files' | 'notes' | string; + isInsideNotesFolder?: boolean; + notesScopePath?: string; + notesMode?: boolean; +} +export interface OpenResourceRequest { + kind: OpenResourceKind; + path: string; + mode?: OpenResourceMode; + mime?: string; + extension?: string; + context?: OpenResourceContext; +} +export interface OpenResourceResult { + status: 'opened' | 'no-provider'; + providerId?: string; + providerPluginId?: string; + providerComponent?: string; + request: OpenResourceRequest; + message?: string; +} export type PluginStatus = 'discovered' | 'disabled' | 'loading' | 'loaded' | 'degraded' | 'failed' | 'incompatible' | 'missing-required-capability'; export interface PluginState { id: string; diff --git a/dist/types.d.ts.map b/dist/types.d.ts.map index 0a25e30..afa6a8d 100644 --- a/dist/types.d.ts.map +++ b/dist/types.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,YAAY,GAAG,UAAU,GAAG,OAAO,GAAG,aAAa,CAAC;AAEhE,MAAM,WAAW,cAAc;IAC7B,aAAa,EAAE,CAAC,CAAC;IACjB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC5B,WAAW,EAAE,UAAU,EAAE,CAAC;IAC1B,QAAQ,CAAC,EAAE,cAAc,CAAC;IAC1B,OAAO,CAAC,EAAE,aAAa,CAAC;IACxB,UAAU,CAAC,EAAE,eAAe,CAAC;IAC7B,WAAW,CAAC,EAAE,kBAAkB,CAAC;IACjC,IAAI,CAAC,EAAE,UAAU,CAAC;CACnB;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,SAAS,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9B,WAAW,CAAC,EAAE,iBAAiB,CAAC;CACjC;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,CAAC,EAAE,KAAK,GAAG,OAAO,GAAG,KAAK,CAAC;IAC/B,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,UAAU;IACzB,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAID,MAAM,MAAM,cAAc,GAAG,MAAM,CAAC;AAEpC,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,cAAc,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,QAAQ,GAAG,OAAO,GAAG,YAAY,CAAC;CAC3C;AAID,MAAM,MAAM,UAAU,GAClB,YAAY,GACZ,aAAa,GACb,aAAa,GACb,mBAAmB,GACnB,oBAAoB,GACpB,gBAAgB,GAChB,kBAAkB,GAClB,aAAa,GACb,mBAAmB,GACnB,eAAe,GACf,gBAAgB,GAChB,eAAe,GACf,cAAc,GACd,eAAe,GACf,kBAAkB,CAAC;AAEvB,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,UAAU,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,OAAO,CAAC;CACpB;AAID,MAAM,WAAW,kBAAkB;IACjC,KAAK,CAAC,EAAE,gBAAgB,EAAE,CAAC;IAC3B,QAAQ,CAAC,EAAE,mBAAmB,EAAE,CAAC;IACjC,cAAc,CAAC,EAAE,yBAAyB,EAAE,CAAC;IAC7C,YAAY,CAAC,EAAE,uBAAuB,EAAE,CAAC;IACzC,WAAW,CAAC,EAAE,kBAAkB,EAAE,CAAC;IACnC,WAAW,CAAC,EAAE,kBAAkB,EAAE,CAAC;IACnC,kBAAkB,CAAC,EAAE,4BAA4B,EAAE,CAAC;IACpD,eAAe,CAAC,EAAE,0BAA0B,EAAE,CAAC;IAC/C,iBAAiB,CAAC,EAAE,4BAA4B,EAAE,CAAC;IACnD,cAAc,CAAC,EAAE,yBAAyB,EAAE,CAAC;CAC9C;AAED,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,mBAAmB;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,yBAAyB;IACxC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,uBAAuB;IACtC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,cAAc,CAAC;IAC5B,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,4BAA4B;IAC3C,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,QAAQ,CAAC;IAC7C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,cAAc,CAAC;IAC5B,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,0BAA0B;IACzC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,4BAA4B;IAC3C,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,yBAAyB;IACxC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IAC5B,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAID,MAAM,MAAM,YAAY,GACpB,YAAY,GACZ,UAAU,GACV,SAAS,GACT,QAAQ,GACR,UAAU,GACV,QAAQ,GACR,cAAc,GACd,6BAA6B,CAAC;AAElC,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,cAAc,CAAC;IACzB,MAAM,EAAE,YAAY,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAID,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClC;AAGD,MAAM,WAAW,uBAAwB,SAAQ,YAAY;IAC3D,IAAI,EAAE,sBAAsB,CAAC;IAC7B,OAAO,EAAE;QACP,GAAG,EAAE,MAAM,CAAC;QACZ,KAAK,EAAE,MAAM,CAAC;QACd,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,UAAU,EAAE,MAAM,CAAC;QACnB,MAAM,CAAC,EAAE,MAAM,CAAC;KACjB,CAAC;CACH;AAED,MAAM,WAAW,4BAA6B,SAAQ,YAAY;IAChE,IAAI,EAAE,2BAA2B,CAAC;IAClC,OAAO,EAAE;QACP,GAAG,EAAE,MAAM,CAAC;QACZ,KAAK,EAAE,MAAM,CAAC;QACd,IAAI,EAAE,MAAM,CAAC;QACb,UAAU,EAAE,MAAM,CAAC;QACnB,MAAM,CAAC,EAAE,MAAM,CAAC;KACjB,CAAC;CACH;AAED,MAAM,WAAW,uBAAwB,SAAQ,YAAY;IAC3D,IAAI,EAAE,sBAAsB,CAAC;IAC7B,OAAO,EAAE;QACP,GAAG,EAAE,MAAM,CAAC;QACZ,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,UAAU,EAAE,MAAM,CAAC;QACnB,MAAM,CAAC,EAAE,MAAM,CAAC;KACjB,CAAC;CACH;AAGD,MAAM,WAAW,gBAAiB,SAAQ,YAAY;IACpD,IAAI,EAAE,cAAc,CAAC;IACrB,OAAO,EAAE;QACP,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,QAAQ,EAAE,MAAM,CAAC;KAClB,CAAC;CACH;AAED,MAAM,WAAW,iBAAkB,SAAQ,YAAY;IACrD,IAAI,EAAE,eAAe,CAAC;IACtB,OAAO,EAAE;QACP,MAAM,EAAE,MAAM,CAAC;QACf,QAAQ,EAAE,MAAM,CAAC;QACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,UAAU,EAAE,MAAM,CAAC;KACpB,CAAC;CACH;AAED,MAAM,WAAW,gBAAiB,SAAQ,YAAY;IACpD,IAAI,EAAE,cAAc,CAAC;IACrB,OAAO,EAAE;QACP,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,SAAS,EAAE,MAAM,CAAC;KACnB,CAAC;CACH;AAED,MAAM,WAAW,cAAe,SAAQ,YAAY;IAClD,IAAI,EAAE,YAAY,CAAC;IACnB,OAAO,EAAE;QACP,MAAM,EAAE,MAAM,CAAC;QACf,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,IAAI,EAAE,MAAM,CAAC;QACb,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC;CACH;AAGD,MAAM,WAAW,kBAAmB,SAAQ,YAAY;IACtD,IAAI,EAAE,gBAAgB,CAAC;IACvB,OAAO,EAAE;QACP,QAAQ,EAAE,MAAM,CAAC;QACjB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,SAAS,EAAE,MAAM,CAAC;KACnB,CAAC;CACH;AAED,MAAM,WAAW,mBAAoB,SAAQ,YAAY;IACvD,IAAI,EAAE,iBAAiB,CAAC;IACxB,OAAO,EAAE;QACP,QAAQ,EAAE,MAAM,CAAC;QACjB,UAAU,EAAE,MAAM,CAAC;KACpB,CAAC;CACH;AAID,MAAM,MAAM,UAAU,GAAG,KAAK,GAAG,QAAQ,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAChE,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,MAAM,GAAG,cAAc,GAAG,YAAY,CAAC;AAE7E,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,UAAU,CAAC;IACf,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,cAAc,CAAC;IAC5B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACnC;AAED,MAAM,WAAW,SAAS;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,aAAa,EAAE,CAAC;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,iBAAiB,EAAE,CAAC;CAC9B;AAED,MAAM,WAAW,QAAQ;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC;IACvB,eAAe,EAAE,MAAM,CAAC;IACxB,UAAU,CAAC,EAAE,YAAY,GAAG,aAAa,GAAG,QAAQ,CAAC;IACrD,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAID,MAAM,WAAW,cAAc;IAC7B,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB"} \ No newline at end of file +{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/schemas/manifest.json b/schemas/manifest.json index b3cd309..58f0754 100644 --- a/schemas/manifest.json +++ b/schemas/manifest.json @@ -84,6 +84,10 @@ "vault.read", "vault.write", "vault.watch", + "files.read", + "files.write", + "files.delete", + "workbench.open", "storage.namespace", "storage.migrations", "events.publish", @@ -227,6 +231,12 @@ "items": { "$ref": "#/$defs/ContributionStatusBarItem" } + }, + "openProviders": { + "type": "array", + "items": { + "$ref": "#/$defs/ContributionOpenProvider" + } } } }, @@ -357,6 +367,45 @@ "handler": { "type": "string" } }, "required": ["id", "label"] + }, + "OpenProviderSupport": { + "type": "object", + "properties": { + "kind": { + "type": "string", + "enum": ["vault-file"] + }, + "extensions": { + "type": "array", + "items": { "type": "string" } + }, + "mime": { + "type": "array", + "items": { "type": "string" } + }, + "contexts": { + "type": "array", + "items": { "type": "string" } + } + }, + "required": ["kind"] + }, + "ContributionOpenProvider": { + "type": "object", + "properties": { + "id": { "type": "string" }, + "title": { "type": "string" }, + "priority": { "type": "integer" }, + "component": { "type": "string" }, + "supports": { + "type": "array", + "items": { + "$ref": "#/$defs/OpenProviderSupport" + }, + "minItems": 1 + } + }, + "required": ["id", "title", "component", "supports"] } } } diff --git a/schemas/permissions.json b/schemas/permissions.json index f60a032..d35f7e8 100644 --- a/schemas/permissions.json +++ b/schemas/permissions.json @@ -30,12 +30,16 @@ { "name": "vault.read", "description": "Read vault files and metadata", "dangerous": false }, { "name": "vault.write", "description": "Write vault files and metadata", "dangerous": true }, { "name": "vault.watch", "description": "Watch vault file changes", "dangerous": false }, + { "name": "files.read", "description": "List files and read text files through the vault Files API", "dangerous": false }, + { "name": "files.write", "description": "Create folders, write text files, and move paths through the vault Files API", "dangerous": true }, + { "name": "files.delete", "description": "Trash vault files and folders through the vault Files API", "dangerous": true }, { "name": "storage.namespace", "description": "Read/write plugin's own storage namespace", "dangerous": false }, { "name": "storage.migrations", "description": "Run database migrations in plugin namespace", "dangerous": false }, { "name": "events.publish", "description": "Publish events to the event bus", "dangerous": false }, { "name": "events.subscribe", "description": "Subscribe to events on the event bus", "dangerous": false }, { "name": "ui.register", "description": "Register UI components and contributions", "dangerous": false }, { "name": "commands.register", "description": "Register command palette commands", "dangerous": false }, + { "name": "workbench.open", "description": "Request Workbench open/edit routing for vault resources", "dangerous": false }, { "name": "network.local", "description": "Connect to localhost network services", "dangerous": false }, { "name": "network.remote", "description": "Connect to remote network services", "dangerous": true }, { "name": "process.spawn", "description": "Spawn external processes", "dangerous": true }, diff --git a/scripts/test.sh b/scripts/test.sh index 5dccfbc..70e168d 100755 --- a/scripts/test.sh +++ b/scripts/test.sh @@ -41,18 +41,9 @@ if [ ! -d "$ROOT/node_modules" ]; then fi fi -# Run vitest tests -if grep -q '"test"' "$ROOT/package.json" 2>/dev/null; then - OUTPUT=$(cd "$ROOT" && npm test 2>&1) || true - if echo "$OUTPUT" | grep -q "No test files found"; then - echo " ℹ️ vitest: no test files yet" - else - echo "$OUTPUT" - report "vitest" ${PIPESTATUS[0]} - fi -else - echo " ℹ️ no test script in package.json" -fi +# Run vitest tests. The SDK has contract tests; "no test files" is a failure. +(cd "$ROOT" && npm test) +report "vitest" $? echo "" if [ "$FAILED" -eq 0 ]; then diff --git a/src/plugin-api.test.ts b/src/plugin-api.test.ts new file mode 100644 index 0000000..6c5aa34 --- /dev/null +++ b/src/plugin-api.test.ts @@ -0,0 +1,197 @@ +import { describe, expect, test } from 'vitest'; +import manifestSchema from '../schemas/manifest.json'; +import type { OpenResourceRequest, PluginManifest } from './types'; +import { createMockPluginAPI } from './test-utils'; + +describe('VerstakPluginAPI contract', () => { + test('mock API exposes the bundled runtime shape', async () => { + const api = createMockPluginAPI('verstak.platform-test'); + + expect(api.pluginId).toBe('verstak.platform-test'); + expect(typeof api.settings.read).toBe('function'); + expect(typeof api.settings.write).toBe('function'); + expect(typeof api.capabilities.list).toBe('function'); + expect(typeof api.commands.register).toBe('function'); + expect(typeof api.commands.execute).toBe('function'); + expect(typeof api.events.publish).toBe('function'); + expect(typeof api.events.subscribe).toBe('function'); + 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.writeText).toBe('function'); + expect(typeof api.files.createFolder).toBe('function'); + expect(typeof api.files.move).toBe('function'); + expect(typeof api.files.trash).toBe('function'); + expect(typeof api.workbench.openResource).toBe('function'); + expect(typeof api.workbench.editResource).toBe('function'); + }); + + test('manifest schema accepts files permissions used by platform-test', () => { + const permissionEnum = ((manifestSchema as any).properties.permissions.items.enum || []) as string[]; + + expect(permissionEnum).toContain('files.read'); + expect(permissionEnum).toContain('files.write'); + expect(permissionEnum).toContain('files.delete'); + expect(permissionEnum).toContain('workbench.open'); + }); + + test('manifest types accept open provider contributions', () => { + const manifest: PluginManifest = { + schemaVersion: 1, + id: 'verstak.default-editor', + name: 'Default Editor', + version: '0.1.0', + apiVersion: '1', + provides: ['editor.text', 'editor.text.markdown'], + permissions: ['ui.register', 'files.read', 'files.write', 'workbench.open'], + contributes: { + openProviders: [ + { + id: 'verstak.default-editor.markdown', + title: 'Default Markdown Editor', + priority: 100, + component: 'MarkdownEditor', + supports: [ + { + kind: 'vault-file', + extensions: ['.md', '.markdown'], + contexts: ['generic-markdown', 'notes-markdown'], + }, + { + kind: 'vault-file', + mime: ['text/plain'], + extensions: ['.txt', '.log'], + contexts: ['generic-text'], + }, + ], + }, + ], + }, + }; + + expect(manifest.contributes?.openProviders?.[0].supports[0].contexts).toContain('notes-markdown'); + expect(manifest.contributes?.openProviders?.[0].supports[1].contexts).toContain('generic-text'); + }); + + test('OpenResourceRequest and no-provider result shape are typed', () => { + const request: OpenResourceRequest = { + kind: 'vault-file', + path: 'Docs/todo.txt', + mode: 'edit', + mime: 'text/plain', + extension: '.txt', + context: { + sourcePluginId: 'files.plugin', + sourceView: 'files', + }, + }; + + const result = { + status: 'no-provider' as const, + request, + message: 'no open provider for resource', + }; + + expect(result.status).toBe('no-provider'); + expect(result.request.context?.sourceView).toBe('files'); + }); + + test('workbench mock routes open and edit resources', async () => { + const api = createMockPluginAPI('files.plugin'); + const request: OpenResourceRequest = { + kind: 'vault-file', + path: 'Notes/Overview.md', + mode: 'view', + extension: '.md', + context: { + sourceView: 'notes', + isInsideNotesFolder: true, + notesMode: true, + }, + }; + + await expect(api.workbench.openResource(request)).resolves.toEqual(expect.objectContaining({ + status: 'opened', + providerId: expect.any(String), + request: expect.objectContaining({ path: 'Notes/Overview.md', mode: 'view' }), + })); + await expect(api.workbench.editResource({ ...request, mode: 'edit' })).resolves.toEqual(expect.objectContaining({ + status: 'opened', + request: expect.objectContaining({ mode: 'edit' }), + })); + }); + + test('settings persist in the mock API namespace', async () => { + const api = createMockPluginAPI(); + + await api.settings.write('savedText', 'hello'); + + await expect(api.settings.read('savedText')).resolves.toBe('hello'); + await expect(api.settings.read()).resolves.toEqual({ savedText: 'hello' }); + }); + + test('commands register, execute, and unregister', async () => { + const api = createMockPluginAPI('cmd.plugin'); + + const unregister = await api.commands.register('cmd.plugin.echo', async (args) => args.value); + await expect(api.commands.execute('cmd.plugin.echo', { value: 'ok' })).resolves.toEqual({ + status: 'handled', + pluginId: 'cmd.plugin', + commandId: 'cmd.plugin.echo', + result: 'ok', + }); + + unregister(); + await expect(api.commands.execute('cmd.plugin.echo', {})).rejects.toThrow('declared-but-unhandled'); + }); + + test('events publish to subscribers and unsubscribe cleanly', async () => { + const api = createMockPluginAPI('event.plugin'); + const received: unknown[] = []; + + const unsubscribe = await api.events.subscribe('event.plugin.echo', (event) => { + received.push(event.payload.message); + }); + await api.events.publish('event.plugin.echo', { message: 'first' }); + unsubscribe(); + await api.events.publish('event.plugin.echo', { message: 'second' }); + + expect(received).toEqual(['first']); + }); + + test('files mock supports text write, read, list, move, and trash', async () => { + const api = createMockPluginAPI('files.plugin'); + + 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.list('PlatformTest')).resolves.toEqual([ + expect.objectContaining({ relativePath: 'PlatformTest/one.txt', type: 'file' }), + ]); + await api.files.move('PlatformTest/one.txt', 'PlatformTest/two.txt'); + const trash = await api.files.trash('PlatformTest/two.txt'); + + expect(trash.originalPath).toBe('PlatformTest/two.txt'); + expect(trash.trashId).toBeTruthy(); + expect(trash.trashPath).toMatch(/^\.verstak\/trash\/files\/.+\/two\.txt$/); + }); + + test('files mock rejects non-canonical and reserved paths', async () => { + const api = createMockPluginAPI('files.plugin'); + + await expect(api.files.readText(String.raw`PlatformTest\one.txt`)).rejects.toThrow('backslash'); + await expect(api.files.readText('//server/share')).rejects.toThrow('absolute'); + await expect(api.files.readText('C:/Windows/system.ini')).rejects.toThrow('absolute'); + await expect(api.files.readText('../secret')).rejects.toThrow('path-traversal'); + await expect(api.files.readText('bad\0path')).rejects.toThrow('null-byte'); + await expect(api.files.readText('.Verstak/vault.json')).rejects.toThrow('reserved-path'); + }); + + test('files mock rejects moving a folder into itself', async () => { + const api = createMockPluginAPI('files.plugin'); + + await api.files.createFolder('Folder'); + + await expect(api.files.move('Folder', 'Folder/Child')).rejects.toThrow('move-into-self'); + }); +}); diff --git a/src/plugin-api.ts b/src/plugin-api.ts index 4af96ea..6e8d1f7 100644 --- a/src/plugin-api.ts +++ b/src/plugin-api.ts @@ -1,166 +1,103 @@ -// Verstak Plugin SDK — VerstakPluginAPI -// The official runtime API available to all plugins in the frontend context. +// Verstak Plugin SDK — bundled frontend plugin API contract. +// +// The desktop host creates the real API with createPluginAPI(pluginId) inside +// VerstakPluginAPI.js and passes it to bundled plugin components at mount time. +// This SDK file intentionally exposes the TypeScript contract only; it is not +// a standalone security boundary or RPC client. -import type { PluginSettings } from './types'; +import type { + CapabilityEntry, + FileEntry, + FileMetadata, + MovePathOptions, + OpenResourceRequest, + OpenResourceResult, + PluginSettings, + TrashResult, + WriteTextOptions, +} from './types'; -/** - * VerstakPluginAPI — единственный способ для frontend плагина - * общаться с core платформы. - * - * Экземпляр API передаётся плагину при активации через глобальную - * переменную `window.__VERSTAK_PLUGIN_API__`. - */ -export class VerstakPluginAPI { - private pluginId: string; - private capabilities = new Set(); +export type PluginCommandArgs = Record; +export type PluginCommandHandler = ( + args: PluginCommandArgs, + declaration: PluginCommandDeclaration +) => unknown | Promise; +export type Unsubscribe = () => void; - constructor(pluginId: string) { - this.pluginId = pluginId; - } - - /** - * Инициализация API — вызывается core после загрузки frontend bundle. - * @internal - */ - _init(capabilities: string[]): void { - this.capabilities = new Set(capabilities); - } - - // ─── View Registration ───────────────────────────────────── - - /** - * Зарегистрировать view для отображения в UI Shell. - */ - registerView(id: string, component: unknown): void { - this._postMessage('register.view', { id, component }); - } - - /** - * Зарегистрировать панель настроек плагина. - */ - registerSettingsPanel(id: string, title: string, component: unknown): void { - this._postMessage('register.settingsPanel', { id, title, component }); - } - - /** - * Зарегистрировать команду для command palette. - */ - registerCommand(id: string, title: string, handler: () => void, keybinding?: string): void { - this._postMessage('register.command', { id, title, keybinding, handler: handler.toString() }); - } - - /** - * Зарегистрировать действия для файлов. - */ - registerFileAction(id: string, label: string, handler: (filePath: string) => void, capability?: string): void { - this._postMessage('register.fileAction', { id, label, handler: handler.toString(), capability }); - } - - /** - * Зарегистрировать действия для заметок. - */ - registerNoteAction(id: string, label: string, handler: (noteId: string) => void, capability?: string): void { - this._postMessage('register.noteAction', { id, label, handler: handler.toString(), capability }); - } - - /** - * Зарегистрировать provider поиска. - */ - registerSearchProvider(id: string, label: string, handler: (query: string) => unknown[]): void { - this._postMessage('register.searchProvider', { id, label, handler: handler.toString() }); - } - - // ─── Capabilities ────────────────────────────────────────── - - /** - * Проверить, доступна ли capability. - */ - hasCapability(name: string): boolean { - return this.capabilities.has(name); - } - - /** - * Получить список всех доступных capabilities. - */ - getAvailableCapabilities(): string[] { - return Array.from(this.capabilities); - } - - // ─── Backend Communication ───────────────────────────────── - - /** - * Вызвать backend метод плагина через RPC. - */ - async callBackend(method: string, args: unknown[] = []): Promise { - return this._rpcCall(method, args); - } - - // ─── Settings ────────────────────────────────────────────── - - /** - * Прочитать настройки плагина. - */ - async readSettings(): Promise { - const result = await this._rpcCall('readSettings', []); - return result as PluginSettings; - } - - /** - * Записать настройки плагина. - */ - async writeSettings(settings: PluginSettings): Promise { - await this._rpcCall('writeSettings', [settings]); - } - - // ─── Event Bus ───────────────────────────────────────────── - - /** - * Подписаться на событие event bus. - */ - subscribe(event: string, handler: (payload: unknown) => void): void { - this._postMessage('subscribe', { event, handler: handler.toString() }); - } - - /** - * Опубликовать событие в event bus. - */ - publish(event: string, payload: unknown): void { - this._postMessage('publish', { event, payload }); - } - - // ─── Internal ────────────────────────────────────────────── - - private _postMessage(type: string, data: Record): void { - window.dispatchEvent(new CustomEvent('verstak:plugin', { - detail: { pluginId: this.pluginId, type, data } - })); - } - - private async _rpcCall(method: string, args: unknown[]): Promise { - return new Promise((resolve, reject) => { - const callId = `${this.pluginId}:${Date.now()}:${Math.random()}`; - const handler = (event: CustomEvent) => { - if (event.detail.callId === callId) { - window.removeEventListener('verstak:rpc:response', handler as EventListener); - if (event.detail.error) { - reject(new Error(event.detail.error)); - } else { - resolve(event.detail.result); - } - } - }; - window.addEventListener('verstak:rpc:response', handler as EventListener); - this._postMessage('rpc', { callId, method, args }); - }); - } +export interface PluginCommandDeclaration { + status: 'declared'; + pluginId: string; + commandId: string; + handler?: string; + args?: PluginCommandArgs; } -/** - * Создать экземпляр VerstakPluginAPI. - * Core вызывает эту функцию после загрузки frontend bundle, - * передавая pluginId и список доступных capabilities. - */ -export function createPluginAPI(pluginId: string): VerstakPluginAPI { - const api = new VerstakPluginAPI(pluginId); - return api; +export interface PluginCommandResult { + status: 'handled'; + pluginId: string; + commandId: string; + result: unknown; +} + +export interface PluginEvent> { + name: string; + pluginId: string; + payload: TPayload; + timestamp: string; +} + +export interface VerstakPluginAPI { + readonly pluginId: string; + + settings: { + read(): Promise; + read(key: string): Promise; + write(key: string, value: unknown): Promise; + writeAll(settings: PluginSettings): Promise; + }; + + capabilities: { + has(capability: string): Promise; + get(capability: string): Promise<{ available: boolean; name?: string; pluginId?: string; status?: string }>; + list(): Promise; + }; + + commands: { + register(commandId: string, handler: PluginCommandHandler): Promise; + execute(commandId: string, args?: PluginCommandArgs): Promise; + }; + + events: { + publish(eventName: string, payload?: Record): Promise; + subscribe>( + eventName: string, + handler: (event: PluginEvent) => void + ): Promise; + }; + + files: { + /** + * Files API uses canonical vault-relative slash paths. Backslashes, + * Windows/UNC absolute paths, traversal, null bytes, `.verstak` variants, + * and symlink read/write/move/trash operations are rejected by the host. + */ + list(relativeDir?: string): Promise; + metadata(relativePath: string): Promise; + readText(relativePath: string): Promise; + writeText(relativePath: string, content: string, options?: WriteTextOptions): Promise; + createFolder(relativePath: string): Promise; + move(fromRelativePath: string, toRelativePath: string, options?: MovePathOptions): Promise; + trash(relativePath: string): Promise; + }; + + workbench: { + openResource(request: OpenResourceRequest): Promise; + editResource(request: OpenResourceRequest): Promise; + }; + + dispose?: () => void; +} + +export function createPluginAPI(_pluginId: string): VerstakPluginAPI { + throw new Error('createPluginAPI is provided by Verstak Desktop at plugin runtime'); } diff --git a/src/test-utils.ts b/src/test-utils.ts index b91a475..3d57caa 100644 --- a/src/test-utils.ts +++ b/src/test-utils.ts @@ -1,6 +1,7 @@ // Verstak Plugin SDK — Test Utilities import type { PluginManifest, PluginState } from './types'; +import type { VerstakPluginAPI } from './plugin-api'; /** * Создать тестовый manifest для unit-тестов. @@ -39,23 +40,187 @@ export function createTestPluginState(overrides?: Partial): PluginS /** * Создать заглушку VerstakPluginAPI для тестов. */ -export function createMockPluginAPI(): { - registerView: ReturnType; - registerCommand: ReturnType; - registerSettingsPanel: ReturnType; - hasCapability: ReturnType; - callBackend: ReturnType; - subscribe: ReturnType; - publish: ReturnType; -} { +export function createMockPluginAPI(pluginId = 'test.plugin'): VerstakPluginAPI { + const settings: Record = {}; + const commands = new Map) => unknown>(); + const eventHandlers = new Map void>>(); + const files = new Map(); + files.set('', { type: 'folder', modifiedAt: new Date().toISOString() }); + + function normalizePath(path: string, allowRoot = false): string { + const raw = String(path || ''); + if (raw.includes('\0')) throw new Error('invalid-path: null-byte'); + if (raw.includes('\\')) throw new Error('invalid-path: backslash not allowed'); + const normalized = raw.replace(/^\.\//, ''); + const parts = normalized.split('/').filter(Boolean); + if (!allowRoot && parts.length === 0) throw new Error('invalid-path: empty path'); + if (normalized.startsWith('/') || /^[A-Za-z]:/.test(normalized)) throw new Error('invalid-path: absolute path rejected'); + if (parts.includes('..')) throw new Error('invalid-path: path-traversal'); + if (parts[0] && parts[0].toLowerCase() === '.verstak') throw new Error('reserved-path: .verstak is internal'); + return parts.join('/'); + } + + function parentPath(path: string): string { + const idx = path.lastIndexOf('/'); + return idx === -1 ? '' : path.slice(0, idx); + } + + function baseName(path: string): string { + const idx = path.lastIndexOf('/'); + return idx === -1 ? path : path.slice(idx + 1); + } + + function entry(path: string, node: { type: 'file' | 'folder'; content?: string; modifiedAt: string }) { + const name = baseName(path); + const dot = name.lastIndexOf('.'); + const extension = dot > 0 ? name.slice(dot + 1) : ''; + return { + name, + relativePath: path, + type: node.type, + size: node.type === 'file' ? (node.content || '').length : 0, + modifiedAt: node.modifiedAt, + extension, + isHidden: name.startsWith('.'), + isReserved: false, + canRead: true, + canWrite: true, + }; + } + return { - registerView: vi.fn(), - registerCommand: vi.fn(), - registerSettingsPanel: vi.fn(), - hasCapability: vi.fn().mockReturnValue(false), - callBackend: vi.fn().mockResolvedValue(undefined), - subscribe: vi.fn(), - publish: vi.fn(), + pluginId, + settings: { + read: vi.fn(async (key?: string) => key ? settings[key] : { ...settings }) as VerstakPluginAPI['settings']['read'], + write: vi.fn(async (key: string, value: unknown) => { + settings[key] = value; + return { ...settings }; + }), + writeAll: vi.fn(async (nextSettings: Record) => { + Object.keys(settings).forEach((key) => delete settings[key]); + Object.assign(settings, nextSettings); + }), + }, + capabilities: { + has: vi.fn(async () => false), + get: vi.fn(async (name: string) => ({ available: false, name })), + list: vi.fn(async () => []), + }, + commands: { + register: vi.fn(async (commandId: string, handler: (args: Record) => unknown) => { + commands.set(commandId, handler); + return () => { commands.delete(commandId); }; + }), + execute: vi.fn(async (commandId: string, args: Record = {}) => { + const handler = commands.get(commandId); + if (!handler) { + throw new Error(`declared-but-unhandled: ${commandId}`); + } + return { status: 'handled' as const, pluginId, commandId, result: await handler(args) }; + }), + }, + events: { + publish: vi.fn(async (eventName: string, payload: Record = {}) => { + const event = { name: eventName, pluginId, payload, timestamp: new Date().toISOString() }; + (eventHandlers.get(eventName) || []).slice().forEach((handler) => handler(event)); + }), + subscribe: vi.fn(async (eventName: string, handler: (event: any) => void) => { + const handlers = eventHandlers.get(eventName) || []; + handlers.push(handler); + eventHandlers.set(eventName, handlers); + return () => { + eventHandlers.set(eventName, (eventHandlers.get(eventName) || []).filter((item) => item !== handler)); + }; + }), + }, + files: { + list: vi.fn(async (relativeDir = '') => { + const dir = normalizePath(relativeDir, true); + const node = files.get(dir); + if (!node || node.type !== 'folder') throw new Error(`not-found: ${dir}`); + const prefix = dir ? `${dir}/` : ''; + return Array.from(files.entries()) + .filter(([path]) => path !== dir && path.startsWith(prefix) && !path.slice(prefix.length).includes('/')) + .map(([path, node]) => entry(path, node)); + }), + metadata: vi.fn(async (relativePath: string) => { + const path = normalizePath(relativePath); + const node = files.get(path); + if (!node) throw new Error(`not-found: ${path}`); + return { ...entry(path, node), mimeHint: '', isText: node.type === 'file' }; + }), + readText: 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}`); + return node.content || ''; + }), + writeText: vi.fn(async (relativePath: string, content: string, options = {}) => { + const path = normalizePath(relativePath); + const node = files.get(path); + if (node && node.type !== 'file') throw new Error(`not-regular-file: ${path}`); + if (node && !options.overwrite) throw new Error(`conflict: ${path}`); + if (!node && !options.createIfMissing) throw new Error(`not-found: ${path}`); + const parent = parentPath(path); + if (!files.get(parent) || files.get(parent)?.type !== 'folder') throw new Error(`parent-not-found: ${parent}`); + files.set(path, { type: 'file', content, modifiedAt: new Date().toISOString() }); + }), + createFolder: vi.fn(async (relativePath: string) => { + const path = normalizePath(relativePath); + if (files.has(path)) throw new Error(`conflict: ${path}`); + const parent = parentPath(path); + if (!files.get(parent) || files.get(parent)?.type !== 'folder') throw new Error(`parent-not-found: ${parent}`); + files.set(path, { type: 'folder', modifiedAt: new Date().toISOString() }); + }), + move: vi.fn(async (fromRelativePath: string, toRelativePath: string, options = {}) => { + const from = normalizePath(fromRelativePath); + const to = normalizePath(toRelativePath); + const node = files.get(from); + if (!node) throw new Error(`not-found: ${from}`); + if (node.type === 'folder' && (to === from || to.startsWith(`${from}/`))) { + throw new Error(`move-into-self: ${from} -> ${to}`); + } + if (files.has(to) && !options.overwrite) throw new Error(`conflict: ${to}`); + const parent = parentPath(to); + if (!files.get(parent) || files.get(parent)?.type !== 'folder') throw new Error(`parent-not-found: ${parent}`); + const moving = Array.from(files.entries()).filter(([path]) => path === from || path.startsWith(`${from}/`)); + moving.forEach(([path, movingNode]) => { + const suffix = path.slice(from.length); + files.set(`${to}${suffix}`, movingNode); + files.delete(path); + }); + }), + trash: vi.fn(async (relativePath: string) => { + const path = normalizePath(relativePath); + if (!files.has(path)) throw new Error(`not-found: ${path}`); + files.delete(path); + const trashId = `mock-${Date.now()}`; + return { + originalPath: path, + trashPath: `.verstak/trash/files/${trashId}/${baseName(path)}`, + trashId, + deletedAt: new Date().toISOString(), + }; + }), + }, + workbench: { + openResource: vi.fn(async (request) => ({ + status: 'opened' as const, + providerId: request.context?.notesMode ? 'mock.notes-markdown-provider' : 'mock.open-provider', + providerPluginId: 'mock.editor', + providerComponent: 'MockEditor', + request: { ...request, mode: request.mode || 'view' }, + })), + editResource: vi.fn(async (request) => ({ + status: 'opened' as const, + providerId: request.context?.notesMode ? 'mock.notes-markdown-provider' : 'mock.open-provider', + providerPluginId: 'mock.editor', + providerComponent: 'MockEditor', + request: { ...request, mode: 'edit' as const }, + })), + }, + dispose: vi.fn(), }; } diff --git a/src/types.ts b/src/types.ts index 2e62b45..705f9c6 100644 --- a/src/types.ts +++ b/src/types.ts @@ -55,7 +55,8 @@ export type CapabilityName = string; export interface CapabilityEntry { name: CapabilityName; - description: string; + description?: string; + pluginId: string; status: 'stable' | 'draft' | 'deprecated'; } @@ -65,6 +66,10 @@ export type Permission = | 'vault.read' | 'vault.write' | 'vault.watch' + | 'files.read' + | 'files.write' + | 'files.delete' + | 'workbench.open' | 'storage.namespace' | 'storage.migrations' | 'events.publish' @@ -84,6 +89,57 @@ export interface PermissionEntry { dangerous: boolean; } +// ─── Files API ────────────────────────────────────────────── + +export type FileEntryType = 'file' | 'folder' | 'symlink' | 'unknown'; + +export interface FileEntry { + name: string; + relativePath: string; + type: FileEntryType; + size: number; + modifiedAt: string; + extension: string; + isHidden: boolean; + isReserved: boolean; + canRead: boolean; + canWrite: boolean; +} + +export interface FileMetadata { + relativePath: string; + type: FileEntryType; + size: number; + modifiedAt: string; + createdAt?: string; + extension: string; + mimeHint: string; + isText: boolean; + isHidden: boolean; + isReserved: boolean; + canRead: boolean; + canWrite: boolean; +} + +export interface WriteTextOptions { + /** Create the file when it is missing. Parent folder must already exist. */ + createIfMissing?: boolean; + /** Replace an existing regular file. Existing folders/symlinks are rejected. */ + overwrite?: boolean; +} + +export interface MovePathOptions { + /** Replace an existing target path when the host supports it. */ + overwrite?: boolean; +} + +export interface TrashResult { + originalPath: string; + trashPath: string; + trashId: string; + deletedAt: string; +} + // ─── Contribution Points ───────────────────────────────────── export interface ContributionPoints { @@ -97,6 +153,7 @@ export interface ContributionPoints { searchProviders?: ContributionSearchProvider[]; activityProviders?: ContributionActivityProvider[]; statusBarItems?: ContributionStatusBarItem[]; + openProviders?: ContributionOpenProvider[]; } export interface ContributionView { @@ -165,6 +222,51 @@ export interface ContributionStatusBarItem { handler?: string; } +export type OpenResourceKind = 'vault-file'; +export type OpenResourceMode = 'view' | 'edit'; +export type OpenResourceContextName = 'generic-text' | 'generic-markdown' | 'notes-markdown' | string; + +export interface OpenProviderSupport { + kind: OpenResourceKind; + extensions?: string[]; + mime?: string[]; + contexts?: OpenResourceContextName[]; +} + +export interface ContributionOpenProvider { + id: string; + title: string; + priority?: number; + component: string; + supports: OpenProviderSupport[]; +} + +export interface OpenResourceContext { + sourcePluginId?: string; + sourceView?: 'files' | 'notes' | string; + isInsideNotesFolder?: boolean; + notesScopePath?: string; + notesMode?: boolean; +} + +export interface OpenResourceRequest { + kind: OpenResourceKind; + path: string; + mode?: OpenResourceMode; + mime?: string; + extension?: string; + context?: OpenResourceContext; +} + +export interface OpenResourceResult { + status: 'opened' | 'no-provider'; + providerId?: string; + providerPluginId?: string; + providerComponent?: string; + request: OpenResourceRequest; + message?: string; +} + // ─── Plugin State ──────────────────────────────────────────── export type PluginStatus =