feat: document contribution runtime APIs
This commit is contained in:
parent
8cab72cf29
commit
36846855b6
12
README.md
12
README.md
|
|
@ -10,7 +10,8 @@ for that host-provided object:
|
|||
|
||||
- `settings.read/write/writeAll`
|
||||
- `capabilities.list/get/has`
|
||||
- `commands.register/execute`
|
||||
- `commands.register/execute/executeFor`
|
||||
- `contributions.list`
|
||||
- `events.publish/subscribe`
|
||||
- `files.list/metadata/readText/writeText/createFolder/move/trash`
|
||||
- `workbench.openResource/editResource`
|
||||
|
|
@ -26,6 +27,15 @@ 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"`.
|
||||
|
||||
`contributions.list(point)` returns host-flattened contribution records with
|
||||
`pluginId`. Files and Notes use this with `commands.executeFor(pluginId,
|
||||
handler, args)` to run action providers declared by other plugins.
|
||||
|
||||
Workspace lifecycle events are `workspace.created`, `workspace.renamed`,
|
||||
`workspace.trashed`, and `workspace.selected`. Payloads include
|
||||
`workspaceRootPath` and `workspaceName`; rename/trash events include previous
|
||||
or trash metadata.
|
||||
|
||||
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.
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import type { CapabilityEntry, FileEntry, FileMetadata, MovePathOptions, OpenResourceRequest, OpenResourceResult, PluginSettings, TrashResult, WriteTextOptions } from './types';
|
||||
import type { CapabilityEntry, FileEntry, FileMetadata, MovePathOptions, OpenResourceRequest, OpenResourceResult, PluginSettings, RegisteredContributionPoints, TrashResult, WriteTextOptions } from './types';
|
||||
export type PluginCommandArgs = Record<string, unknown>;
|
||||
export type PluginCommandHandler = (args: PluginCommandArgs, declaration: PluginCommandDeclaration) => unknown | Promise<unknown>;
|
||||
export type Unsubscribe = () => void;
|
||||
|
|
@ -63,6 +63,11 @@ export interface VerstakPluginAPI {
|
|||
commands: {
|
||||
register(commandId: string, handler: PluginCommandHandler): Promise<Unsubscribe>;
|
||||
execute(commandId: string, args?: PluginCommandArgs): Promise<PluginCommandResult>;
|
||||
executeFor(targetPluginId: string, commandId: string, args?: PluginCommandArgs): Promise<PluginCommandResult>;
|
||||
};
|
||||
contributions: {
|
||||
list(): Promise<RegisteredContributionPoints>;
|
||||
list<K extends keyof RegisteredContributionPoints>(point: K): Promise<NonNullable<RegisteredContributionPoints[K]>>;
|
||||
};
|
||||
events: {
|
||||
publish(eventName: string, payload?: Record<string, unknown>): Promise<void>;
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
{"version":3,"file":"plugin-api.d.ts","sourceRoot":"","sources":["../src/plugin-api.ts"],"names":[],"mappings":"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"}
|
||||
{"version":3,"file":"plugin-api.d.ts","sourceRoot":"","sources":["../src/plugin-api.ts"],"names":[],"mappings":"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"}
|
||||
|
|
@ -1 +1 @@
|
|||
{"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;AAkIhD,MAAM,UAAU,eAAe,CAAC,SAAiB;IAC/C,MAAM,IAAI,KAAK,CAAC,kEAAkE,CAAC,CAAC;AACtF,CAAC"}
|
||||
{"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;AA2IhD,MAAM,UAAU,eAAe,CAAC,SAAiB;IAC/C,MAAM,IAAI,KAAK,CAAC,kEAAkE,CAAC,CAAC;AACtF,CAAC"}
|
||||
|
|
@ -1,5 +1,8 @@
|
|||
import type { PluginManifest, PluginState } from './types';
|
||||
import type { PluginManifest, PluginState, RegisteredContributionPoints } from './types';
|
||||
import type { VerstakPluginAPI } from './plugin-api';
|
||||
export interface MockPluginAPIOptions {
|
||||
contributions?: RegisteredContributionPoints;
|
||||
}
|
||||
/**
|
||||
* Создать тестовый manifest для unit-тестов.
|
||||
*/
|
||||
|
|
@ -11,7 +14,7 @@ export declare function createTestPluginState(overrides?: Partial<PluginState>):
|
|||
/**
|
||||
* Создать заглушку VerstakPluginAPI для тестов.
|
||||
*/
|
||||
export declare function createMockPluginAPI(pluginId?: string): VerstakPluginAPI;
|
||||
export declare function createMockPluginAPI(pluginId?: string, options?: MockPluginAPIOptions): VerstakPluginAPI;
|
||||
/**
|
||||
* Валидатор plugin manifest.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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;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,CAoN9E;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"}
|
||||
{"version":3,"file":"test-utils.d.ts","sourceRoot":"","sources":["../src/test-utils.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,WAAW,EAAE,4BAA4B,EAAE,MAAM,SAAS,CAAC;AACzF,OAAO,KAAK,EAAwB,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAQ3E,MAAM,WAAW,oBAAoB;IACnC,aAAa,CAAC,EAAE,4BAA4B,CAAC;CAC9C;AAED;;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,EAAE,OAAO,GAAE,oBAAyB,GAAG,gBAAgB,CA0OlH;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"}
|
||||
|
|
@ -1,4 +1,8 @@
|
|||
// Verstak Plugin SDK — Test Utilities
|
||||
const mockCommandHandlers = new Map();
|
||||
function commandKey(pluginId, commandId) {
|
||||
return `${pluginId}:${commandId}`;
|
||||
}
|
||||
/**
|
||||
* Создать тестовый manifest для unit-тестов.
|
||||
*/
|
||||
|
|
@ -34,7 +38,7 @@ export function createTestPluginState(overrides) {
|
|||
/**
|
||||
* Создать заглушку VerstakPluginAPI для тестов.
|
||||
*/
|
||||
export function createMockPluginAPI(pluginId = 'test.plugin') {
|
||||
export function createMockPluginAPI(pluginId = 'test.plugin', options = {}) {
|
||||
const settings = {};
|
||||
const commands = new Map();
|
||||
const eventHandlers = new Map();
|
||||
|
|
@ -104,14 +108,37 @@ export function createMockPluginAPI(pluginId = 'test.plugin') {
|
|||
commands: {
|
||||
register: vi.fn(async (commandId, handler) => {
|
||||
commands.set(commandId, handler);
|
||||
return () => { commands.delete(commandId); };
|
||||
mockCommandHandlers.set(commandKey(pluginId, commandId), handler);
|
||||
return () => {
|
||||
commands.delete(commandId);
|
||||
mockCommandHandlers.delete(commandKey(pluginId, 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) };
|
||||
return { status: 'handled', pluginId, commandId, result: await handler(args, { status: 'declared', pluginId, commandId, args }) };
|
||||
}),
|
||||
executeFor: vi.fn(async (targetPluginId, commandId, args = {}) => {
|
||||
const handler = mockCommandHandlers.get(commandKey(targetPluginId, commandId));
|
||||
if (!handler) {
|
||||
throw new Error(`declared-but-unhandled: ${targetPluginId}:${commandId}`);
|
||||
}
|
||||
return {
|
||||
status: 'handled',
|
||||
pluginId: targetPluginId,
|
||||
commandId,
|
||||
result: await handler(args, { status: 'declared', pluginId: targetPluginId, commandId, args }),
|
||||
};
|
||||
}),
|
||||
},
|
||||
contributions: {
|
||||
list: vi.fn(async (point) => {
|
||||
if (!point)
|
||||
return { ...(options.contributions || {}) };
|
||||
return ([...((options.contributions && options.contributions[point]) || [])]);
|
||||
}),
|
||||
},
|
||||
events: {
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -188,6 +188,23 @@ export interface ContributionWorkspaceItem {
|
|||
icon?: string;
|
||||
component: string;
|
||||
}
|
||||
export type RegisteredContribution<T> = T & {
|
||||
pluginId: string;
|
||||
};
|
||||
export interface RegisteredContributionPoints {
|
||||
views?: RegisteredContribution<ContributionView>[];
|
||||
commands?: RegisteredContribution<ContributionCommand>[];
|
||||
settingsPanels?: RegisteredContribution<ContributionSettingsPanel>[];
|
||||
sidebarItems?: RegisteredContribution<ContributionSidebarItem>[];
|
||||
fileActions?: RegisteredContribution<ContributionAction>[];
|
||||
noteActions?: RegisteredContribution<ContributionAction>[];
|
||||
contextMenuEntries?: RegisteredContribution<ContributionContextMenuEntry>[];
|
||||
searchProviders?: RegisteredContribution<ContributionSearchProvider>[];
|
||||
activityProviders?: RegisteredContribution<ContributionActivityProvider>[];
|
||||
statusBarItems?: RegisteredContribution<ContributionStatusBarItem>[];
|
||||
openProviders?: RegisteredContribution<ContributionOpenProvider>[];
|
||||
workspaceItems?: RegisteredContribution<ContributionWorkspaceItem>[];
|
||||
}
|
||||
export interface OpenResourceContext {
|
||||
sourcePluginId?: string;
|
||||
sourceView?: 'files' | 'notes' | string;
|
||||
|
|
@ -290,6 +307,48 @@ export interface NoteSavedEvent extends VerstakEvent {
|
|||
savedAt: string;
|
||||
};
|
||||
}
|
||||
export interface WorkspaceCreatedEvent extends VerstakEvent {
|
||||
name: 'workspace.created';
|
||||
payload: {
|
||||
operation: 'create';
|
||||
workspaceRootPath: string;
|
||||
workspaceName: string;
|
||||
title?: string;
|
||||
templateId?: string;
|
||||
};
|
||||
}
|
||||
export interface WorkspaceRenamedEvent extends VerstakEvent {
|
||||
name: 'workspace.renamed';
|
||||
payload: {
|
||||
operation: 'rename';
|
||||
workspaceRootPath: string;
|
||||
workspaceName: string;
|
||||
previousWorkspaceRootPath: string;
|
||||
previousWorkspaceName: string;
|
||||
title?: string;
|
||||
};
|
||||
}
|
||||
export interface WorkspaceTrashedEvent extends VerstakEvent {
|
||||
name: 'workspace.trashed';
|
||||
payload: {
|
||||
operation: 'trash';
|
||||
workspaceRootPath: string;
|
||||
workspaceName: string;
|
||||
title?: string;
|
||||
trashId: string;
|
||||
trashPath: string;
|
||||
deletedAt: string;
|
||||
};
|
||||
}
|
||||
export interface WorkspaceSelectedEvent extends VerstakEvent {
|
||||
name: 'workspace.selected';
|
||||
payload: {
|
||||
operation: 'select';
|
||||
workspaceRootPath: string;
|
||||
workspaceName: string;
|
||||
title?: string;
|
||||
};
|
||||
}
|
||||
export interface PluginEnabledEvent extends VerstakEvent {
|
||||
name: 'plugin.enabled';
|
||||
payload: {
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -71,6 +71,68 @@
|
|||
"required": ["caseId", "casePath"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "workspace.created",
|
||||
"description": "A top-level workspace folder has been created",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"operation": { "type": "string", "const": "create" },
|
||||
"workspaceRootPath": { "type": "string", "description": "Vault-relative top-level workspace folder" },
|
||||
"workspaceName": { "type": "string", "description": "Workspace display/canonical folder name" },
|
||||
"title": { "type": "string", "description": "Activity title" },
|
||||
"templateId": { "type": "string", "description": "Template applied during creation, if any" }
|
||||
},
|
||||
"required": ["operation", "workspaceRootPath", "workspaceName"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "workspace.renamed",
|
||||
"description": "A top-level workspace folder has been renamed",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"operation": { "type": "string", "const": "rename" },
|
||||
"workspaceRootPath": { "type": "string", "description": "New workspace root folder" },
|
||||
"workspaceName": { "type": "string", "description": "New workspace name" },
|
||||
"previousWorkspaceRootPath": { "type": "string", "description": "Previous workspace root folder" },
|
||||
"previousWorkspaceName": { "type": "string", "description": "Previous workspace name" },
|
||||
"title": { "type": "string", "description": "Activity title" }
|
||||
},
|
||||
"required": ["operation", "workspaceRootPath", "workspaceName", "previousWorkspaceRootPath", "previousWorkspaceName"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "workspace.trashed",
|
||||
"description": "A top-level workspace folder has been moved to internal trash",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"operation": { "type": "string", "const": "trash" },
|
||||
"workspaceRootPath": { "type": "string", "description": "Workspace root folder before trash" },
|
||||
"workspaceName": { "type": "string", "description": "Workspace name before trash" },
|
||||
"title": { "type": "string", "description": "Activity title" },
|
||||
"trashId": { "type": "string", "description": "Internal trash item identifier" },
|
||||
"trashPath": { "type": "string", "description": "Vault-relative trash path" },
|
||||
"deletedAt": { "type": "string", "description": "ISO 8601 timestamp" }
|
||||
},
|
||||
"required": ["operation", "workspaceRootPath", "workspaceName", "trashId", "trashPath", "deletedAt"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "workspace.selected",
|
||||
"description": "The current workspace selection has changed",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"operation": { "type": "string", "const": "select" },
|
||||
"workspaceRootPath": { "type": "string", "description": "Selected workspace root folder" },
|
||||
"workspaceName": { "type": "string", "description": "Selected workspace name" },
|
||||
"title": { "type": "string", "description": "Activity title" }
|
||||
},
|
||||
"required": ["operation", "workspaceRootPath", "workspaceName"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "file.added",
|
||||
"description": "A file has been added to the vault",
|
||||
|
|
|
|||
|
|
@ -14,6 +14,8 @@ describe('VerstakPluginAPI contract', () => {
|
|||
expect(typeof api.capabilities.list).toBe('function');
|
||||
expect(typeof api.commands.register).toBe('function');
|
||||
expect(typeof api.commands.execute).toBe('function');
|
||||
expect(typeof api.commands.executeFor).toBe('function');
|
||||
expect(typeof api.contributions.list).toBe('function');
|
||||
expect(typeof api.events.publish).toBe('function');
|
||||
expect(typeof api.events.subscribe).toBe('function');
|
||||
expect(typeof api.files.list).toBe('function');
|
||||
|
|
@ -188,6 +190,32 @@ describe('VerstakPluginAPI contract', () => {
|
|||
await expect(api.commands.execute('cmd.plugin.echo', {})).rejects.toThrow('declared-but-unhandled');
|
||||
});
|
||||
|
||||
test('contributions list and provider command execution', async () => {
|
||||
const api = createMockPluginAPI('consumer.plugin', {
|
||||
contributions: {
|
||||
fileActions: [{
|
||||
pluginId: 'provider.plugin',
|
||||
id: 'provider.file.action',
|
||||
label: 'Provider File Action',
|
||||
handler: 'provider.command',
|
||||
}],
|
||||
},
|
||||
});
|
||||
const providerApi = createMockPluginAPI('provider.plugin');
|
||||
|
||||
await providerApi.commands.register('provider.command', async (args) => args.path);
|
||||
|
||||
await expect(api.contributions.list('fileActions')).resolves.toEqual([
|
||||
expect.objectContaining({ pluginId: 'provider.plugin', id: 'provider.file.action' }),
|
||||
]);
|
||||
await expect(api.commands.executeFor('provider.plugin', 'provider.command', { path: 'Docs/readme.md' })).resolves.toEqual({
|
||||
status: 'handled',
|
||||
pluginId: 'provider.plugin',
|
||||
commandId: 'provider.command',
|
||||
result: 'Docs/readme.md',
|
||||
});
|
||||
});
|
||||
|
||||
test('events publish to subscribers and unsubscribe cleanly', async () => {
|
||||
const api = createMockPluginAPI('event.plugin');
|
||||
const received: unknown[] = [];
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import type {
|
|||
OpenResourceRequest,
|
||||
OpenResourceResult,
|
||||
PluginSettings,
|
||||
RegisteredContributionPoints,
|
||||
TrashResult,
|
||||
WriteTextOptions,
|
||||
} from './types';
|
||||
|
|
@ -88,6 +89,14 @@ export interface VerstakPluginAPI {
|
|||
commands: {
|
||||
register(commandId: string, handler: PluginCommandHandler): Promise<Unsubscribe>;
|
||||
execute(commandId: string, args?: PluginCommandArgs): Promise<PluginCommandResult>;
|
||||
executeFor(targetPluginId: string, commandId: string, args?: PluginCommandArgs): Promise<PluginCommandResult>;
|
||||
};
|
||||
|
||||
contributions: {
|
||||
list(): Promise<RegisteredContributionPoints>;
|
||||
list<K extends keyof RegisteredContributionPoints>(
|
||||
point: K
|
||||
): Promise<NonNullable<RegisteredContributionPoints[K]>>;
|
||||
};
|
||||
|
||||
events: {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,17 @@
|
|||
// Verstak Plugin SDK — Test Utilities
|
||||
|
||||
import type { PluginManifest, PluginState } from './types';
|
||||
import type { VerstakPluginAPI } from './plugin-api';
|
||||
import type { PluginManifest, PluginState, RegisteredContributionPoints } from './types';
|
||||
import type { PluginCommandHandler, VerstakPluginAPI } from './plugin-api';
|
||||
|
||||
const mockCommandHandlers = new Map<string, PluginCommandHandler>();
|
||||
|
||||
function commandKey(pluginId: string, commandId: string): string {
|
||||
return `${pluginId}:${commandId}`;
|
||||
}
|
||||
|
||||
export interface MockPluginAPIOptions {
|
||||
contributions?: RegisteredContributionPoints;
|
||||
}
|
||||
|
||||
/**
|
||||
* Создать тестовый manifest для unit-тестов.
|
||||
|
|
@ -40,9 +50,9 @@ export function createTestPluginState(overrides?: Partial<PluginState>): PluginS
|
|||
/**
|
||||
* Создать заглушку VerstakPluginAPI для тестов.
|
||||
*/
|
||||
export function createMockPluginAPI(pluginId = 'test.plugin'): VerstakPluginAPI {
|
||||
export function createMockPluginAPI(pluginId = 'test.plugin', options: MockPluginAPIOptions = {}): VerstakPluginAPI {
|
||||
const settings: Record<string, unknown> = {};
|
||||
const commands = new Map<string, (args: Record<string, unknown>) => unknown>();
|
||||
const commands = new Map<string, PluginCommandHandler>();
|
||||
const eventHandlers = new Map<string, Array<(event: any) => void>>();
|
||||
const files = new Map<string, { type: 'file' | 'folder'; content?: string; modifiedAt: string }>();
|
||||
files.set('', { type: 'folder', modifiedAt: new Date().toISOString() });
|
||||
|
|
@ -107,17 +117,39 @@ export function createMockPluginAPI(pluginId = 'test.plugin'): VerstakPluginAPI
|
|||
list: vi.fn(async () => []),
|
||||
},
|
||||
commands: {
|
||||
register: vi.fn(async (commandId: string, handler: (args: Record<string, unknown>) => unknown) => {
|
||||
register: vi.fn(async (commandId: string, handler: PluginCommandHandler) => {
|
||||
commands.set(commandId, handler);
|
||||
return () => { commands.delete(commandId); };
|
||||
mockCommandHandlers.set(commandKey(pluginId, commandId), handler);
|
||||
return () => {
|
||||
commands.delete(commandId);
|
||||
mockCommandHandlers.delete(commandKey(pluginId, commandId));
|
||||
};
|
||||
}),
|
||||
execute: vi.fn(async (commandId: string, args: Record<string, unknown> = {}) => {
|
||||
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) };
|
||||
return { status: 'handled' as const, pluginId, commandId, result: await handler(args, { status: 'declared', pluginId, commandId, args }) };
|
||||
}),
|
||||
executeFor: vi.fn(async (targetPluginId: string, commandId: string, args: Record<string, unknown> = {}) => {
|
||||
const handler = mockCommandHandlers.get(commandKey(targetPluginId, commandId));
|
||||
if (!handler) {
|
||||
throw new Error(`declared-but-unhandled: ${targetPluginId}:${commandId}`);
|
||||
}
|
||||
return {
|
||||
status: 'handled' as const,
|
||||
pluginId: targetPluginId,
|
||||
commandId,
|
||||
result: await handler(args, { status: 'declared', pluginId: targetPluginId, commandId, args }),
|
||||
};
|
||||
}),
|
||||
},
|
||||
contributions: {
|
||||
list: vi.fn(async (point?: keyof RegisteredContributionPoints) => {
|
||||
if (!point) return { ...(options.contributions || {}) };
|
||||
return ([...((options.contributions && options.contributions[point]) || [])]) as any;
|
||||
}) as VerstakPluginAPI['contributions']['list'],
|
||||
},
|
||||
events: {
|
||||
publish: vi.fn(async (eventName: string, payload: Record<string, unknown> = {}) => {
|
||||
|
|
|
|||
65
src/types.ts
65
src/types.ts
|
|
@ -251,6 +251,25 @@ export interface ContributionWorkspaceItem {
|
|||
component: string;
|
||||
}
|
||||
|
||||
export type RegisteredContribution<T> = T & {
|
||||
pluginId: string;
|
||||
};
|
||||
|
||||
export interface RegisteredContributionPoints {
|
||||
views?: RegisteredContribution<ContributionView>[];
|
||||
commands?: RegisteredContribution<ContributionCommand>[];
|
||||
settingsPanels?: RegisteredContribution<ContributionSettingsPanel>[];
|
||||
sidebarItems?: RegisteredContribution<ContributionSidebarItem>[];
|
||||
fileActions?: RegisteredContribution<ContributionAction>[];
|
||||
noteActions?: RegisteredContribution<ContributionAction>[];
|
||||
contextMenuEntries?: RegisteredContribution<ContributionContextMenuEntry>[];
|
||||
searchProviders?: RegisteredContribution<ContributionSearchProvider>[];
|
||||
activityProviders?: RegisteredContribution<ContributionActivityProvider>[];
|
||||
statusBarItems?: RegisteredContribution<ContributionStatusBarItem>[];
|
||||
openProviders?: RegisteredContribution<ContributionOpenProvider>[];
|
||||
workspaceItems?: RegisteredContribution<ContributionWorkspaceItem>[];
|
||||
}
|
||||
|
||||
export interface OpenResourceContext {
|
||||
sourcePluginId?: string;
|
||||
sourceView?: 'files' | 'notes' | string;
|
||||
|
|
@ -380,6 +399,52 @@ export interface NoteSavedEvent extends VerstakEvent {
|
|||
};
|
||||
}
|
||||
|
||||
export interface WorkspaceCreatedEvent extends VerstakEvent {
|
||||
name: 'workspace.created';
|
||||
payload: {
|
||||
operation: 'create';
|
||||
workspaceRootPath: string;
|
||||
workspaceName: string;
|
||||
title?: string;
|
||||
templateId?: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface WorkspaceRenamedEvent extends VerstakEvent {
|
||||
name: 'workspace.renamed';
|
||||
payload: {
|
||||
operation: 'rename';
|
||||
workspaceRootPath: string;
|
||||
workspaceName: string;
|
||||
previousWorkspaceRootPath: string;
|
||||
previousWorkspaceName: string;
|
||||
title?: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface WorkspaceTrashedEvent extends VerstakEvent {
|
||||
name: 'workspace.trashed';
|
||||
payload: {
|
||||
operation: 'trash';
|
||||
workspaceRootPath: string;
|
||||
workspaceName: string;
|
||||
title?: string;
|
||||
trashId: string;
|
||||
trashPath: string;
|
||||
deletedAt: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface WorkspaceSelectedEvent extends VerstakEvent {
|
||||
name: 'workspace.selected';
|
||||
payload: {
|
||||
operation: 'select';
|
||||
workspaceRootPath: string;
|
||||
workspaceName: string;
|
||||
title?: string;
|
||||
};
|
||||
}
|
||||
|
||||
// Lifecycle events
|
||||
export interface PluginEnabledEvent extends VerstakEvent {
|
||||
name: 'plugin.enabled';
|
||||
|
|
|
|||
Loading…
Reference in New Issue