@openforge-app/plugin-sdk 0.1.0 → 0.2.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +26 -0
- package/dist/backend.d.ts +2 -2
- package/dist/browserSurfaces.d.ts +78 -0
- package/dist/browserSurfaces.js +17 -0
- package/dist/browserSurfacesTesting.d.ts +60 -0
- package/dist/browserSurfacesTesting.js +240 -0
- package/dist/domain.d.ts +74 -15
- package/dist/domain.js +5 -1
- package/dist/fileIcons.d.ts +6 -0
- package/dist/fileIcons.js +84 -0
- package/dist/frontend.d.ts +5 -2
- package/dist/frontend.js +1 -0
- package/dist/index.d.ts +5 -3
- package/dist/index.js +2 -1
- package/dist/manifest.d.ts +80 -2
- package/dist/manifest.js +55 -1
- package/dist/markdown.d.ts +5 -1
- package/dist/markdown.js +67 -18
- package/dist/openforgePackageMetadataSchema.json +80 -4
- package/dist/pluginIconContract.d.ts +3 -0
- package/dist/pluginIconContract.js +12 -0
- package/dist/pluginIcons.d.ts +7 -0
- package/dist/pluginIcons.js +99 -0
- package/dist/publicUiExports.d.mts +16 -0
- package/dist/publicUiExports.mjs +60 -0
- package/dist/svelteHostRuntimeContract.d.mts +1 -0
- package/dist/svelteHostRuntimeContract.mjs +16 -0
- package/dist/testing/backendServicesFake.d.ts +19 -0
- package/dist/testing/backendServicesFake.js +87 -0
- package/dist/testing/commonApiFake.d.ts +23 -0
- package/dist/testing/commonApiFake.js +306 -0
- package/dist/testing/contracts.d.ts +236 -0
- package/dist/testing/contracts.js +1 -0
- package/dist/testing/frontendContributionFake.d.ts +36 -0
- package/dist/testing/frontendContributionFake.js +204 -0
- package/dist/testing/registryFake.d.ts +40 -0
- package/dist/testing/registryFake.js +124 -0
- package/dist/testing/support.d.ts +40 -0
- package/dist/testing/support.js +247 -0
- package/dist/testing.d.ts +3 -223
- package/dist/testing.js +2 -625
- package/dist/types.d.ts +251 -9
- package/dist/types.js +47 -0
- package/dist/ui/Button.svelte +49 -0
- package/dist/ui/Checkbox.svelte +137 -0
- package/dist/ui/FileTypeIcon.svelte +37 -0
- package/dist/ui/MarkdownContent.svelte +71 -11
- package/dist/ui/Modal.svelte +157 -0
- package/dist/ui/PluginPageHeader.svelte +26 -0
- package/dist/ui/PluginSidebarLink.svelte +54 -0
- package/dist/ui/PluginViewState.svelte +76 -0
- package/dist/ui/ResizablePanel.svelte +10 -0
- package/dist/ui/icons/README.md +9 -0
- package/dist/ui/icons/c.svg +1 -0
- package/dist/ui/icons/console.svg +1 -0
- package/dist/ui/icons/cpp.svg +1 -0
- package/dist/ui/icons/csharp.svg +1 -0
- package/dist/ui/icons/css.svg +3 -0
- package/dist/ui/icons/database.svg +1 -0
- package/dist/ui/icons/docker.svg +1 -0
- package/dist/ui/icons/document.svg +4 -0
- package/dist/ui/icons/file.svg +1 -0
- package/dist/ui/icons/folder-open.svg +1 -0
- package/dist/ui/icons/folder.svg +1 -0
- package/dist/ui/icons/git.svg +3 -0
- package/dist/ui/icons/go.svg +1 -0
- package/dist/ui/icons/graphql.svg +20 -0
- package/dist/ui/icons/h.svg +3 -0
- package/dist/ui/icons/html.svg +3 -0
- package/dist/ui/icons/image.svg +3 -0
- package/dist/ui/icons/java.svg +6 -0
- package/dist/ui/icons/javascript.svg +3 -0
- package/dist/ui/icons/json.svg +3 -0
- package/dist/ui/icons/kotlin.svg +1 -0
- package/dist/ui/icons/lock.svg +3 -0
- package/dist/ui/icons/markdown.svg +6 -0
- package/dist/ui/icons/nodejs.svg +6 -0
- package/dist/ui/icons/npm.svg +3 -0
- package/dist/ui/icons/pdf.svg +1 -0
- package/dist/ui/icons/php.svg +1 -0
- package/dist/ui/icons/python.svg +1 -0
- package/dist/ui/icons/react.svg +8 -0
- package/dist/ui/icons/react_ts.svg +8 -0
- package/dist/ui/icons/readme.svg +4 -0
- package/dist/ui/icons/ruby.svg +1 -0
- package/dist/ui/icons/rust.svg +1 -0
- package/dist/ui/icons/sass.svg +3 -0
- package/dist/ui/icons/settings.svg +4 -0
- package/dist/ui/icons/svelte.svg +1 -0
- package/dist/ui/icons/svg.svg +3 -0
- package/dist/ui/icons/swift.svg +1 -0
- package/dist/ui/icons/tune.svg +11 -0
- package/dist/ui/icons/typescript-def.svg +6 -0
- package/dist/ui/icons/typescript.svg +3 -0
- package/dist/ui/icons/vue.svg +1 -0
- package/dist/ui/icons/xml.svg +1 -0
- package/dist/ui/icons/yaml.svg +3 -0
- package/dist/ui/icons/zip.svg +3 -0
- package/dist/vite.js +5 -2
- package/package.json +35 -7
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
export const DEFAULT_FILE_ICON = 'file';
|
|
2
|
+
export const DEFAULT_FOLDER_ICON = 'folder';
|
|
3
|
+
export const DEFAULT_FOLDER_OPEN_ICON = 'folder-open';
|
|
4
|
+
// Lowercased extension (no leading dot) -> Material icon base name.
|
|
5
|
+
// Only names that exist in vscode-material-icons/generated/icons are used.
|
|
6
|
+
const EXTENSION_ICONS = {
|
|
7
|
+
ts: 'typescript', mts: 'typescript', cts: 'typescript',
|
|
8
|
+
tsx: 'react_ts',
|
|
9
|
+
js: 'javascript', mjs: 'javascript', cjs: 'javascript',
|
|
10
|
+
jsx: 'react',
|
|
11
|
+
json: 'json', jsonc: 'json',
|
|
12
|
+
yaml: 'yaml', yml: 'yaml',
|
|
13
|
+
md: 'markdown', markdown: 'markdown', mdx: 'markdown',
|
|
14
|
+
css: 'css', less: 'css',
|
|
15
|
+
scss: 'sass', sass: 'sass',
|
|
16
|
+
html: 'html', htm: 'html',
|
|
17
|
+
svelte: 'svelte',
|
|
18
|
+
vue: 'vue',
|
|
19
|
+
rs: 'rust',
|
|
20
|
+
py: 'python',
|
|
21
|
+
rb: 'ruby',
|
|
22
|
+
go: 'go',
|
|
23
|
+
java: 'java',
|
|
24
|
+
kt: 'kotlin', kts: 'kotlin',
|
|
25
|
+
swift: 'swift',
|
|
26
|
+
c: 'c',
|
|
27
|
+
h: 'h', hpp: 'h', hh: 'h',
|
|
28
|
+
cc: 'cpp', cpp: 'cpp', cxx: 'cpp',
|
|
29
|
+
cs: 'csharp',
|
|
30
|
+
php: 'php',
|
|
31
|
+
sh: 'console', bash: 'console', zsh: 'console', fish: 'console',
|
|
32
|
+
sql: 'database',
|
|
33
|
+
graphql: 'graphql', gql: 'graphql',
|
|
34
|
+
xml: 'xml',
|
|
35
|
+
svg: 'svg',
|
|
36
|
+
png: 'image', jpg: 'image', jpeg: 'image', gif: 'image',
|
|
37
|
+
webp: 'image', bmp: 'image', ico: 'image', avif: 'image',
|
|
38
|
+
pdf: 'pdf',
|
|
39
|
+
zip: 'zip', tar: 'zip', gz: 'zip', tgz: 'zip', rar: 'zip', '7z': 'zip',
|
|
40
|
+
lock: 'lock',
|
|
41
|
+
toml: 'settings',
|
|
42
|
+
txt: 'document', log: 'document',
|
|
43
|
+
env: 'tune',
|
|
44
|
+
};
|
|
45
|
+
// Exact lowercased filename -> Material icon base name (takes precedence over extension).
|
|
46
|
+
const FILENAME_ICONS = {
|
|
47
|
+
'package.json': 'nodejs',
|
|
48
|
+
'package-lock.json': 'lock',
|
|
49
|
+
'pnpm-lock.yaml': 'lock',
|
|
50
|
+
'yarn.lock': 'lock',
|
|
51
|
+
dockerfile: 'docker',
|
|
52
|
+
'.dockerignore': 'docker',
|
|
53
|
+
'.gitignore': 'git',
|
|
54
|
+
'.gitattributes': 'git',
|
|
55
|
+
'.gitmodules': 'git',
|
|
56
|
+
'.npmrc': 'npm',
|
|
57
|
+
'.npmignore': 'npm',
|
|
58
|
+
};
|
|
59
|
+
export function getFileIconName(filename) {
|
|
60
|
+
const base = (filename.split('/').pop() ?? filename).toLowerCase();
|
|
61
|
+
if (base in FILENAME_ICONS)
|
|
62
|
+
return FILENAME_ICONS[base];
|
|
63
|
+
if (base.endsWith('.d.ts'))
|
|
64
|
+
return 'typescript-def';
|
|
65
|
+
if (base === 'readme' || /^readme\.[^.]+$/.test(base))
|
|
66
|
+
return 'readme';
|
|
67
|
+
if (base === '.env' || base.startsWith('.env.'))
|
|
68
|
+
return 'tune';
|
|
69
|
+
const ext = base.includes('.') ? (base.split('.').pop() ?? '') : '';
|
|
70
|
+
return EXTENSION_ICONS[ext] ?? DEFAULT_FILE_ICON;
|
|
71
|
+
}
|
|
72
|
+
export function getFolderIconName(open) {
|
|
73
|
+
return open ? DEFAULT_FOLDER_OPEN_ICON : DEFAULT_FOLDER_ICON;
|
|
74
|
+
}
|
|
75
|
+
export const BUNDLED_ICON_NAMES = Object.freeze(Array.from(new Set([
|
|
76
|
+
DEFAULT_FILE_ICON,
|
|
77
|
+
DEFAULT_FOLDER_ICON,
|
|
78
|
+
DEFAULT_FOLDER_OPEN_ICON,
|
|
79
|
+
'typescript-def',
|
|
80
|
+
'readme',
|
|
81
|
+
'tune',
|
|
82
|
+
...Object.values(EXTENSION_ICONS),
|
|
83
|
+
...Object.values(FILENAME_ICONS),
|
|
84
|
+
])).sort());
|
package/dist/frontend.d.ts
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { BrowserSurfaceCapture, BrowserSurfaceFeedbackSelection, BrowserSurfaceVisualFeedback, BrowserSurfaceRegion, BrowserSurfaceErrorCode, BrowserSurfaceNavigationError, BrowserSurfacesAPI, GetOrCreateBrowserSurfaceRequest, TaskBrowserSurfaceController, TaskBrowserSurfaceState } from './browserSurfaces';
|
|
2
|
+
import type { CommandDescriptor, CommandRegistry, CommandRegistration, CommandShortcutMetadata, ComposeTaskRequest, ComposeTaskResult, Disposable, FrontendBackendBridge, FrontendOpenForgeAPI, FrontendPlugin, FrontendPluginContext, FrontendSettingsRegistry, FrontendTaskPaneRegistry, FrontendInjectionPointRegistry, FrontendTaskStartRegistry, FrontendTaskUIRegistry, FrontendViewRegistry, InjectionPointLocation, NavigationAPI, OpenForgeContextSnapshot, OpenForgeNavigationRequest, OpenForgeNavigationSnapshot, TaskLinkHandler, TaskLinkHandlerResult, TaskLinkOpenRequest, TaskLinksAPI, PluginIcon, PluginSidebarNavigationProps, PluginSidebarViewIdentity, PluginInjectionPointProps, PluginInjectionPointRegistration, PluginSettingsSectionProps, PluginSettingsSectionRegistration, PluginSettingsSectionScope, PluginStorageScope, PluginSvgIcon, PluginTaskPaneProps, PluginTaskPaneTabRegistration, PluginTaskUISectionProps, PluginTaskUISectionRegistration, PluginViewProps, PluginViewRegistration, TaskStartPrefixContext, TaskStartPrefixProviderRegistration, PtyBufferState, TerminalImageProtocol } from './types';
|
|
2
3
|
export declare const OPENFORGE_FRONTEND_PLUGIN_MARKER = "__openforgeFrontendPlugin";
|
|
3
4
|
export type MarkedFrontendPlugin<TPlugin extends FrontendPlugin = FrontendPlugin> = TPlugin & {
|
|
4
5
|
readonly [OPENFORGE_FRONTEND_PLUGIN_MARKER]: true;
|
|
5
6
|
};
|
|
6
7
|
export declare function defineFrontendPlugin<const TPlugin extends FrontendPlugin>(plugin: TPlugin): MarkedFrontendPlugin<TPlugin>;
|
|
7
|
-
export
|
|
8
|
+
export { BrowserSurfaceError, isAllowedBrowserSurfaceUrl } from './browserSurfaces';
|
|
9
|
+
export type { BrowserSurfaceCapture, BrowserSurfaceFeedbackSelection, BrowserSurfaceVisualFeedback, BrowserSurfaceRegion, BrowserSurfaceErrorCode, BrowserSurfaceNavigationError, BrowserSurfacesAPI, GetOrCreateBrowserSurfaceRequest, TaskBrowserSurfaceController, TaskBrowserSurfaceState, };
|
|
10
|
+
export type { CommandDescriptor, CommandRegistry, CommandRegistration, CommandShortcutMetadata, ComposeTaskRequest, ComposeTaskResult, Disposable, FrontendBackendBridge, FrontendOpenForgeAPI, FrontendPlugin, FrontendPluginContext, FrontendSettingsRegistry, FrontendTaskPaneRegistry, FrontendInjectionPointRegistry, FrontendTaskStartRegistry, FrontendTaskUIRegistry, FrontendViewRegistry, InjectionPointLocation, NavigationAPI, OpenForgeContextSnapshot, OpenForgeNavigationRequest, OpenForgeNavigationSnapshot, TaskLinkHandler, TaskLinkHandlerResult, TaskLinkOpenRequest, TaskLinksAPI, PluginIcon, PluginSidebarNavigationProps, PluginSidebarViewIdentity, PluginInjectionPointProps, PluginInjectionPointRegistration, PluginSettingsSectionProps, PluginSettingsSectionRegistration, PluginSettingsSectionScope, PluginStorageScope, PluginSvgIcon, PluginTaskPaneProps, PluginTaskPaneTabRegistration, PluginTaskUISectionProps, PluginTaskUISectionRegistration, PluginViewProps, PluginViewRegistration, TaskStartPrefixContext, TaskStartPrefixProviderRegistration, PtyBufferState, TerminalImageProtocol, };
|
package/dist/frontend.js
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
export { BrowserSurfaceError, isAllowedBrowserSurfaceUrl } from './browserSurfaces';
|
|
2
|
+
export type { BrowserSurfaceCapture, BrowserSurfaceFeedbackSelection, BrowserSurfaceVisualFeedback, BrowserSurfaceRegion, BrowserSurfaceErrorCode, BrowserSurfaceNavigationError, BrowserSurfacesAPI, GetOrCreateBrowserSurfaceRequest, TaskBrowserSurfaceController, TaskBrowserSurfaceState, } from './browserSurfaces';
|
|
1
3
|
export { OPENFORGE_PACKAGE_METADATA_SCHEMA, OPENFORGE_PLUGIN_CAPABILITIES, isOpenForgePackageMetadata, isPluginPackageMetadata, isSupportedOpenForgeApiVersion, validateOpenForgePackageMetadata, validatePluginPackageMetadata, } from './manifest';
|
|
2
4
|
export { createMemoryPluginStorage, createMockBackendOpenForgeApi, createMockFrontendOpenForgeApi, createMockOpenForgeApi, createMockPluginContext, createOpenForgeRegistryFake, createTestingCalls, TestingOpenForgeRegistryFake, TestingSubscriptionSink, } from './testing';
|
|
3
|
-
export { MAX_SUPPORTED_API_VERSION, MIN_SUPPORTED_API_VERSION, OPENFORGE_PLUGIN_API_VERSION, SUPPORTED_OPENFORGE_API_VERSIONS, isPluginViewKey, makePluginViewKey, parsePluginViewKey, } from './types';
|
|
4
|
-
export type { AttentionAPI, BackendReadyState, CommandDescriptor, CommandRegistry, CommandRegistration, CommandShortcutMetadata, ConfigureStartPromptContributionRequest, CreateTaskRequest, Disposable, FileSystemAPI, ImplementationRun, JsonObject, JsonPrimitive, JsonSchema, JsonValue, MaybePromise, KeyValueConfigAPI, NotificationRequest, NavigationAPI, NotificationsAPI, OpenForgeCommonAPI, OpenForgeContextSnapshot, OpenForgeNavigationRequest, OpenForgeNavigationSnapshot, OpenForgePackageMetadata, OpenForgePluginCapability, OpenForgePluginContext, OpenForgePluginPackageJson, PluginComponentLoader, PluginComponentModule, PluginEntry, PluginSettingsSectionProps, PluginState, PluginTaskPaneProps, PluginViewProps, PluginStorage, PluginStorageScope, PluginViewKey, ProjectsAPI, ShellAPI, ShellResizeRequest, ShellSessionRequest, ShellSpawnRequest, ShellWriteRequest, StartPromptContribution, StartTaskImplementationRequest, SubscriptionSink, SupportedOpenForgeApiVersion, SystemAPI, TasksAPI, ValidationError, } from './types';
|
|
5
|
-
export type { MockBackendOpenForgeAPI, MockFrontendOpenForgeAPI, TestingBackgroundServiceContribution, TestingBackendMethodContribution, TestingCommandContribution, TestingContributionBase, TestingEventListenerContribution, TestingOpenForgeApiCalls, TestingOpenForgeApiOptions, TestingOpenForgeRegistrySnapshot, TestingRuntimeKind, TestingRuntimeScope, TestingSettingsSectionContribution, TestingTaskPaneTabContribution, TestingViewContribution, } from './testing';
|
|
5
|
+
export { TaskFollowUpError, MAX_SUPPORTED_API_VERSION, MIN_SUPPORTED_API_VERSION, OPENFORGE_PLUGIN_API_VERSION, SUPPORTED_OPENFORGE_API_VERSIONS, isPluginViewKey, makePluginViewKey, parsePluginViewKey, } from './types';
|
|
6
|
+
export type { AgentCommandDescriptor, AgentCommandMetadata, AgentCommandRuntime, AttentionAPI, BackendReadyState, CommandDescriptor, CommandRegistry, CommandRegistration, CommandShortcutMetadata, ComposeTaskRequest, ComposeTaskResult, ConfigureStartPromptContributionRequest, CreateTaskRequest, Disposable, BackendFileSystemAPI, ExternalReadDirectoryRequest, ExternalReadFileRequest, ExternalReadFileSystemAPI, FileSystemAPI, UserDataDirectoryRequest, UserDataFileRequest, UserDataFileSystemAPI, UserDataFileWriteRequest, ImplementationRun, InjectionPointLocation, JsonObject, JsonPrimitive, JsonSchema, JsonValue, MaybePromise, KeyValueConfigAPI, NotificationRequest, NavigationAPI, NotificationsAPI, OpenForgeCommonAPI, OpenForgeContextSnapshot, OpenForgeNavigationRequest, OpenForgeNavigationSnapshot, OpenForgePackageMetadata, OpenForgePluginCapability, OpenForgePluginContext, OpenForgePluginPackageJson, PluginCommandInvocationContext, PluginCommandInvocationSource, PluginComponentLoader, PluginComponentModule, PluginEntry, PluginIcon, PluginSidebarNavigationProps, PluginSidebarViewIdentity, PluginInjectionPointProps, PluginSettingsSectionProps, PluginState, PluginSvgIcon, PluginTaskPaneProps, PluginTaskUISectionProps, PluginViewProps, PluginStorage, PluginStorageScope, PluginViewKey, ProjectsAPI, PtyBufferState, ShellAPI, ShellResizeRequest, TaskStartPrefixContext, TaskStartPrefixProviderRegistration, TerminalImageProtocol, ShellSessionRequest, ShellSpawnRequest, ShellWriteRequest, StartPromptContribution, SendTaskFollowUpRequest, StartTaskImplementationRequest, TaskFollowUpDisposition, TaskFollowUpErrorCode, TaskFollowUpReceipt, SubscriptionSink, SupportedOpenForgeApiVersion, SystemAPI, TaskLinkHandler, TaskLinkHandlerResult, TaskLinkOpenRequest, TaskLinksAPI, TasksAPI, ValidationError, } from './types';
|
|
7
|
+
export type { MockBackendOpenForgeAPI, MockFrontendOpenForgeAPI, TestingBackgroundServiceContribution, TestingBackendMethodContribution, TestingCommandContribution, TestingContributionBase, TestingEventListenerContribution, TestingOpenForgeApiCalls, TestingOpenForgeApiOptions, TestingOpenForgeRegistrySnapshot, TestingRuntimeKind, TestingRuntimeScope, TestingSettingsSectionContribution, TestingTaskPaneTabContribution, TestingTaskUISectionContribution, TestingViewContribution, } from './testing';
|
|
6
8
|
export { parseStrictFiniteNumber } from './numberParsing';
|
|
7
9
|
export { buildProjectFileTree, flattenVisibleProjectFileTree, formatProjectFileTreeSize, getProjectFileTreeDepth, getProjectFileTreeItemAccessibility, getProjectFileTreeKeyboardAction, getProjectFileTreeParentPath, hasProjectFileTreeShortcutModifier, projectFileTreePathToId, } from './projectFileTree';
|
|
8
10
|
export type { ProjectFileTreeEntry, ProjectFileTreeItemAccessibility, ProjectFileTreeKeyboardAction, ProjectFileTreeNode, } from './projectFileTree';
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
export { BrowserSurfaceError, isAllowedBrowserSurfaceUrl } from './browserSurfaces';
|
|
1
2
|
export { OPENFORGE_PACKAGE_METADATA_SCHEMA, OPENFORGE_PLUGIN_CAPABILITIES, isOpenForgePackageMetadata, isPluginPackageMetadata, isSupportedOpenForgeApiVersion, validateOpenForgePackageMetadata, validatePluginPackageMetadata, } from './manifest';
|
|
2
3
|
export { createMemoryPluginStorage, createMockBackendOpenForgeApi, createMockFrontendOpenForgeApi, createMockOpenForgeApi, createMockPluginContext, createOpenForgeRegistryFake, createTestingCalls, TestingOpenForgeRegistryFake, TestingSubscriptionSink, } from './testing';
|
|
3
|
-
export { MAX_SUPPORTED_API_VERSION, MIN_SUPPORTED_API_VERSION, OPENFORGE_PLUGIN_API_VERSION, SUPPORTED_OPENFORGE_API_VERSIONS, isPluginViewKey, makePluginViewKey, parsePluginViewKey, } from './types';
|
|
4
|
+
export { TaskFollowUpError, MAX_SUPPORTED_API_VERSION, MIN_SUPPORTED_API_VERSION, OPENFORGE_PLUGIN_API_VERSION, SUPPORTED_OPENFORGE_API_VERSIONS, isPluginViewKey, makePluginViewKey, parsePluginViewKey, } from './types';
|
|
4
5
|
export { parseStrictFiniteNumber } from './numberParsing';
|
|
5
6
|
export { buildProjectFileTree, flattenVisibleProjectFileTree, formatProjectFileTreeSize, getProjectFileTreeDepth, getProjectFileTreeItemAccessibility, getProjectFileTreeKeyboardAction, getProjectFileTreeParentPath, hasProjectFileTreeShortcutModifier, projectFileTreePathToId, } from './projectFileTree';
|
|
6
7
|
export { canMergePullRequest, getMergeReadiness, hasMergeConflicts, isClosedUnmergedPullRequest, isMergedPullRequest, isQueuedForMerge, isReadyToMerge, parseCheckRuns, preservePullRequestState, splitCheckRuns, } from './domain';
|
package/dist/manifest.d.ts
CHANGED
|
@@ -8,6 +8,46 @@ export declare const OPENFORGE_PACKAGE_METADATA_SCHEMA: {
|
|
|
8
8
|
type: string;
|
|
9
9
|
additionalProperties: boolean;
|
|
10
10
|
required: string[];
|
|
11
|
+
dependentRequired: {
|
|
12
|
+
frontendStyles: string[];
|
|
13
|
+
};
|
|
14
|
+
allOf: ({
|
|
15
|
+
if: {
|
|
16
|
+
properties: {
|
|
17
|
+
requires: {
|
|
18
|
+
contains: {
|
|
19
|
+
const: string;
|
|
20
|
+
};
|
|
21
|
+
};
|
|
22
|
+
enablement?: undefined;
|
|
23
|
+
};
|
|
24
|
+
required: string[];
|
|
25
|
+
};
|
|
26
|
+
then: {
|
|
27
|
+
required: string[];
|
|
28
|
+
properties?: undefined;
|
|
29
|
+
};
|
|
30
|
+
} | {
|
|
31
|
+
if: {
|
|
32
|
+
properties: {
|
|
33
|
+
enablement: {
|
|
34
|
+
const: string;
|
|
35
|
+
};
|
|
36
|
+
requires?: undefined;
|
|
37
|
+
};
|
|
38
|
+
required: string[];
|
|
39
|
+
};
|
|
40
|
+
then: {
|
|
41
|
+
properties: {
|
|
42
|
+
requires: {
|
|
43
|
+
contains: {
|
|
44
|
+
const: string;
|
|
45
|
+
};
|
|
46
|
+
};
|
|
47
|
+
};
|
|
48
|
+
required: string[];
|
|
49
|
+
};
|
|
50
|
+
})[];
|
|
11
51
|
properties: {
|
|
12
52
|
id: {
|
|
13
53
|
type: string;
|
|
@@ -26,9 +66,36 @@ export declare const OPENFORGE_PACKAGE_METADATA_SCHEMA: {
|
|
|
26
66
|
type: string;
|
|
27
67
|
minLength: number;
|
|
28
68
|
};
|
|
69
|
+
enablement: {
|
|
70
|
+
enum: string[];
|
|
71
|
+
default: string;
|
|
72
|
+
description: string;
|
|
73
|
+
};
|
|
29
74
|
icon: {
|
|
30
|
-
|
|
31
|
-
|
|
75
|
+
oneOf: ({
|
|
76
|
+
type: string;
|
|
77
|
+
minLength: number;
|
|
78
|
+
pattern: string;
|
|
79
|
+
additionalProperties?: undefined;
|
|
80
|
+
required?: undefined;
|
|
81
|
+
properties?: undefined;
|
|
82
|
+
} | {
|
|
83
|
+
type: string;
|
|
84
|
+
additionalProperties: boolean;
|
|
85
|
+
required: string[];
|
|
86
|
+
properties: {
|
|
87
|
+
type: {
|
|
88
|
+
const: string;
|
|
89
|
+
};
|
|
90
|
+
svg: {
|
|
91
|
+
type: string;
|
|
92
|
+
minLength: number;
|
|
93
|
+
pattern: string;
|
|
94
|
+
};
|
|
95
|
+
};
|
|
96
|
+
minLength?: undefined;
|
|
97
|
+
pattern?: undefined;
|
|
98
|
+
})[];
|
|
32
99
|
description: string;
|
|
33
100
|
};
|
|
34
101
|
frontend: {
|
|
@@ -36,6 +103,17 @@ export declare const OPENFORGE_PACKAGE_METADATA_SCHEMA: {
|
|
|
36
103
|
minLength: number;
|
|
37
104
|
description: string;
|
|
38
105
|
};
|
|
106
|
+
frontendStyles: {
|
|
107
|
+
type: string;
|
|
108
|
+
minItems: number;
|
|
109
|
+
uniqueItems: boolean;
|
|
110
|
+
items: {
|
|
111
|
+
type: string;
|
|
112
|
+
minLength: number;
|
|
113
|
+
pattern: string;
|
|
114
|
+
};
|
|
115
|
+
description: string;
|
|
116
|
+
};
|
|
39
117
|
backend: {
|
|
40
118
|
type: string;
|
|
41
119
|
minLength: number;
|
package/dist/manifest.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import packageMetadataSchemaData from './openforgePackageMetadataSchema.json';
|
|
2
|
+
import { isPluginIconName, isPluginSvgIcon } from './pluginIconContract';
|
|
2
3
|
import { SUPPORTED_OPENFORGE_API_VERSIONS } from './types';
|
|
3
4
|
export const OPENFORGE_PACKAGE_METADATA_SCHEMA = packageMetadataSchemaData;
|
|
4
5
|
export const OPENFORGE_PLUGIN_CAPABILITIES = packageMetadataSchemaData.properties.requires.items.enum;
|
|
@@ -27,6 +28,45 @@ function validateOptionalString(value, path) {
|
|
|
27
28
|
}
|
|
28
29
|
return [];
|
|
29
30
|
}
|
|
31
|
+
function validateEnablement(value) {
|
|
32
|
+
if (value === undefined || value === 'app' || value === 'project') {
|
|
33
|
+
return [];
|
|
34
|
+
}
|
|
35
|
+
return [{ path: 'enablement', message: 'Must be "app" or "project"' }];
|
|
36
|
+
}
|
|
37
|
+
function validatePluginIcon(value) {
|
|
38
|
+
if (value === undefined || isPluginIconName(value) || isPluginSvgIcon(value)) {
|
|
39
|
+
return [];
|
|
40
|
+
}
|
|
41
|
+
return [{ path: 'icon', message: 'Must be a non-empty Lucide icon name or { type: "svg", svg }' }];
|
|
42
|
+
}
|
|
43
|
+
function validateFrontendStyles(value) {
|
|
44
|
+
if (value === undefined)
|
|
45
|
+
return [];
|
|
46
|
+
if (!Array.isArray(value)) {
|
|
47
|
+
return [{ path: 'frontendStyles', message: 'Must be an array' }];
|
|
48
|
+
}
|
|
49
|
+
const errors = [];
|
|
50
|
+
if (value.length === 0) {
|
|
51
|
+
errors.push({ path: 'frontendStyles', message: 'Must contain at least one stylesheet path' });
|
|
52
|
+
}
|
|
53
|
+
const seen = new Set();
|
|
54
|
+
value.forEach((item, index) => {
|
|
55
|
+
if (!isNonEmptyString(item)) {
|
|
56
|
+
errors.push({ path: `frontendStyles[${index}]`, message: 'Must be a non-empty string' });
|
|
57
|
+
}
|
|
58
|
+
else if (!item.endsWith('.css')) {
|
|
59
|
+
errors.push({ path: `frontendStyles[${index}]`, message: 'Must point to a built CSS artifact' });
|
|
60
|
+
}
|
|
61
|
+
else if (seen.has(item)) {
|
|
62
|
+
errors.push({ path: `frontendStyles[${index}]`, message: 'Duplicate stylesheet path' });
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
seen.add(item);
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
return errors;
|
|
69
|
+
}
|
|
30
70
|
export function isSupportedOpenForgeApiVersion(apiVersion) {
|
|
31
71
|
return typeof apiVersion === 'number'
|
|
32
72
|
&& Number.isInteger(apiVersion)
|
|
@@ -70,10 +110,24 @@ export function validateOpenForgePackageMetadata(data) {
|
|
|
70
110
|
errors.push(...validateApiVersion(data.apiVersion));
|
|
71
111
|
errors.push(...validateRequiredString(data.displayName, 'displayName'));
|
|
72
112
|
errors.push(...validateRequiredString(data.description, 'description'));
|
|
73
|
-
errors.push(...
|
|
113
|
+
errors.push(...validateEnablement(data.enablement));
|
|
114
|
+
errors.push(...validatePluginIcon(data.icon));
|
|
74
115
|
errors.push(...validateOptionalString(data.frontend, 'frontend'));
|
|
116
|
+
errors.push(...validateFrontendStyles(data.frontendStyles));
|
|
117
|
+
if (data.frontendStyles !== undefined && !isNonEmptyString(data.frontend)) {
|
|
118
|
+
errors.push({ path: 'frontendStyles', message: 'Requires a frontend entry' });
|
|
119
|
+
}
|
|
75
120
|
errors.push(...validateOptionalString(data.backend, 'backend'));
|
|
76
121
|
errors.push(...validateRequires(data.requires));
|
|
122
|
+
if (data.enablement === 'app' && (!Array.isArray(data.requires) || !data.requires.includes('appEnablement'))) {
|
|
123
|
+
errors.push({ path: 'requires', message: 'App enablement requires the appEnablement capability' });
|
|
124
|
+
}
|
|
125
|
+
if (Array.isArray(data.requires) && data.requires.includes('browserSurfaces') && !isNonEmptyString(data.frontend)) {
|
|
126
|
+
errors.push({ path: 'requires', message: 'browserSurfaces capability requires a frontend entry' });
|
|
127
|
+
}
|
|
128
|
+
if (Array.isArray(data.requires) && data.requires.includes('taskLinks') && !isNonEmptyString(data.frontend)) {
|
|
129
|
+
errors.push({ path: 'requires', message: 'taskLinks capability requires a frontend entry' });
|
|
130
|
+
}
|
|
77
131
|
if (data.contributes !== undefined) {
|
|
78
132
|
errors.push({ path: 'contributes', message: 'Manifest contribution arrays are not supported; register contributions at runtime' });
|
|
79
133
|
}
|
package/dist/markdown.d.ts
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
export interface RenderMarkdownOptions {
|
|
2
2
|
imageBaseUrl?: string | null;
|
|
3
|
+
markdownFilePath?: string | null;
|
|
4
|
+
deferRepositoryImages?: boolean;
|
|
3
5
|
}
|
|
4
|
-
export declare function
|
|
6
|
+
export declare function resolveMarkdownRepositoryPath(value: string | null, markdownFilePath: string): string | null;
|
|
7
|
+
export declare function getMarkdownRepositoryLinkSuffix(value: string): string;
|
|
8
|
+
export declare function resolveMarkdownImageSrc(src: string | null, imageBaseUrl: string | null | undefined, markdownFilePath?: string): string | null;
|
|
5
9
|
export declare function renderMarkdownHtml(content: string, options?: RenderMarkdownOptions): string;
|
package/dist/markdown.js
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import { marked } from 'marked';
|
|
2
2
|
import { sanitizeHtml } from './sanitize';
|
|
3
|
-
const RELATIVE_PARENT_SEGMENT = /^\.\.\//;
|
|
4
|
-
const RELATIVE_CURRENT_SEGMENT = /^\.\//;
|
|
5
3
|
const markedOptions = {
|
|
6
4
|
gfm: true,
|
|
7
5
|
breaks: true,
|
|
@@ -9,39 +7,90 @@ const markedOptions = {
|
|
|
9
7
|
function hasAbsoluteOrSpecialUrl(value) {
|
|
10
8
|
return /^[a-z][a-z\d+.-]*:/i.test(value) || value.startsWith('//') || value.startsWith('#');
|
|
11
9
|
}
|
|
12
|
-
function
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
normalized = normalized.replace(RELATIVE_CURRENT_SEGMENT, '');
|
|
10
|
+
function decodeMarkdownPath(value) {
|
|
11
|
+
try {
|
|
12
|
+
return decodeURI(value);
|
|
16
13
|
}
|
|
17
|
-
|
|
18
|
-
|
|
14
|
+
catch {
|
|
15
|
+
return value;
|
|
19
16
|
}
|
|
20
|
-
|
|
17
|
+
}
|
|
18
|
+
function splitMarkdownRepositoryReference(value) {
|
|
19
|
+
const suffixIndex = value.search(/[?#]/);
|
|
20
|
+
return suffixIndex < 0
|
|
21
|
+
? { pathValue: value, suffix: '' }
|
|
22
|
+
: { pathValue: value.slice(0, suffixIndex), suffix: value.slice(suffixIndex) };
|
|
23
|
+
}
|
|
24
|
+
export function resolveMarkdownRepositoryPath(value, markdownFilePath) {
|
|
25
|
+
if (!value)
|
|
26
|
+
return null;
|
|
27
|
+
const trimmedValue = value.trim();
|
|
28
|
+
if (!trimmedValue || hasAbsoluteOrSpecialUrl(trimmedValue))
|
|
29
|
+
return null;
|
|
30
|
+
const { pathValue } = splitMarkdownRepositoryReference(trimmedValue);
|
|
31
|
+
if (!pathValue)
|
|
32
|
+
return null;
|
|
33
|
+
const repositoryPath = decodeMarkdownPath(pathValue);
|
|
34
|
+
if (repositoryPath.includes('\\'))
|
|
35
|
+
return null;
|
|
36
|
+
const markdownDirectory = markdownFilePath.split('/').slice(0, -1).join('/');
|
|
37
|
+
const candidate = repositoryPath.startsWith('/')
|
|
38
|
+
? repositoryPath.slice(1)
|
|
39
|
+
: [markdownDirectory, repositoryPath].filter(Boolean).join('/');
|
|
40
|
+
const parts = [];
|
|
41
|
+
for (const segment of candidate.split('/')) {
|
|
42
|
+
if (!segment || segment === '.')
|
|
43
|
+
continue;
|
|
44
|
+
if (segment === '..') {
|
|
45
|
+
if (parts.length === 0)
|
|
46
|
+
return null;
|
|
47
|
+
parts.pop();
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
parts.push(segment);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return parts.length > 0 ? parts.join('/') : null;
|
|
54
|
+
}
|
|
55
|
+
export function getMarkdownRepositoryLinkSuffix(value) {
|
|
56
|
+
return splitMarkdownRepositoryReference(value.trim()).suffix;
|
|
21
57
|
}
|
|
22
58
|
function withTrailingSlash(value) {
|
|
23
59
|
return value.endsWith('/') ? value : `${value}/`;
|
|
24
60
|
}
|
|
25
|
-
export function resolveMarkdownImageSrc(src, imageBaseUrl) {
|
|
26
|
-
if (!
|
|
61
|
+
export function resolveMarkdownImageSrc(src, imageBaseUrl, markdownFilePath = '') {
|
|
62
|
+
if (!imageBaseUrl)
|
|
27
63
|
return null;
|
|
28
|
-
const
|
|
29
|
-
if (!
|
|
64
|
+
const repositoryPath = resolveMarkdownRepositoryPath(src, markdownFilePath);
|
|
65
|
+
if (!repositoryPath)
|
|
30
66
|
return null;
|
|
31
67
|
try {
|
|
32
|
-
return new URL(
|
|
68
|
+
return new URL(repositoryPath, withTrailingSlash(imageBaseUrl)).href;
|
|
33
69
|
}
|
|
34
70
|
catch {
|
|
35
71
|
return null;
|
|
36
72
|
}
|
|
37
73
|
}
|
|
38
|
-
function
|
|
39
|
-
if (
|
|
74
|
+
function prepareMarkdownImageSources(html, options) {
|
|
75
|
+
if (typeof document === 'undefined' || (!options.imageBaseUrl && !options.deferRepositoryImages))
|
|
40
76
|
return html;
|
|
41
77
|
const template = document.createElement('template');
|
|
42
78
|
template.innerHTML = html;
|
|
43
79
|
for (const image of template.content.querySelectorAll('img[src]')) {
|
|
44
|
-
const
|
|
80
|
+
const source = image.getAttribute('src')?.trim() ?? '';
|
|
81
|
+
const shouldDefer = options.deferRepositoryImages && source.length > 0 && !hasAbsoluteOrSpecialUrl(source);
|
|
82
|
+
if (shouldDefer) {
|
|
83
|
+
image.removeAttribute('src');
|
|
84
|
+
const repositoryPath = resolveMarkdownRepositoryPath(source, options.markdownFilePath ?? '');
|
|
85
|
+
if (repositoryPath) {
|
|
86
|
+
image.setAttribute('data-markdown-repository-path', repositoryPath);
|
|
87
|
+
}
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
const repositoryPath = resolveMarkdownRepositoryPath(source, options.markdownFilePath ?? '');
|
|
91
|
+
if (!repositoryPath)
|
|
92
|
+
continue;
|
|
93
|
+
const resolvedSrc = resolveMarkdownImageSrc(repositoryPath, options.imageBaseUrl);
|
|
45
94
|
if (resolvedSrc) {
|
|
46
95
|
image.setAttribute('src', resolvedSrc);
|
|
47
96
|
}
|
|
@@ -50,5 +99,5 @@ function resolveMarkdownImageSources(html, imageBaseUrl) {
|
|
|
50
99
|
}
|
|
51
100
|
export function renderMarkdownHtml(content, options = {}) {
|
|
52
101
|
const rawHtml = marked.parse(content, markedOptions);
|
|
53
|
-
return sanitizeHtml(
|
|
102
|
+
return sanitizeHtml(prepareMarkdownImageSources(rawHtml, options));
|
|
54
103
|
}
|
|
@@ -6,6 +6,41 @@
|
|
|
6
6
|
"type": "object",
|
|
7
7
|
"additionalProperties": false,
|
|
8
8
|
"required": ["id", "apiVersion", "displayName", "description"],
|
|
9
|
+
"dependentRequired": {
|
|
10
|
+
"frontendStyles": ["frontend"]
|
|
11
|
+
},
|
|
12
|
+
"allOf": [
|
|
13
|
+
{
|
|
14
|
+
"if": {
|
|
15
|
+
"properties": {
|
|
16
|
+
"requires": { "contains": { "const": "browserSurfaces" } }
|
|
17
|
+
},
|
|
18
|
+
"required": ["requires"]
|
|
19
|
+
},
|
|
20
|
+
"then": { "required": ["frontend"] }
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
"if": {
|
|
24
|
+
"properties": {
|
|
25
|
+
"requires": { "contains": { "const": "taskLinks" } }
|
|
26
|
+
},
|
|
27
|
+
"required": ["requires"]
|
|
28
|
+
},
|
|
29
|
+
"then": { "required": ["frontend"] }
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
"if": {
|
|
33
|
+
"properties": { "enablement": { "const": "app" } },
|
|
34
|
+
"required": ["enablement"]
|
|
35
|
+
},
|
|
36
|
+
"then": {
|
|
37
|
+
"properties": {
|
|
38
|
+
"requires": { "contains": { "const": "appEnablement" } }
|
|
39
|
+
},
|
|
40
|
+
"required": ["requires"]
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
],
|
|
9
44
|
"properties": {
|
|
10
45
|
"id": {
|
|
11
46
|
"type": "string",
|
|
@@ -24,16 +59,50 @@
|
|
|
24
59
|
"type": "string",
|
|
25
60
|
"minLength": 1
|
|
26
61
|
},
|
|
62
|
+
"enablement": {
|
|
63
|
+
"enum": ["app", "project"],
|
|
64
|
+
"default": "project",
|
|
65
|
+
"description": "Lifecycle ownership for this plugin package. Omission means project-owned enablement."
|
|
66
|
+
},
|
|
27
67
|
"icon": {
|
|
28
|
-
"
|
|
29
|
-
|
|
30
|
-
|
|
68
|
+
"oneOf": [
|
|
69
|
+
{
|
|
70
|
+
"type": "string",
|
|
71
|
+
"minLength": 1,
|
|
72
|
+
"pattern": "\\S"
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
"type": "object",
|
|
76
|
+
"additionalProperties": false,
|
|
77
|
+
"required": ["type", "svg"],
|
|
78
|
+
"properties": {
|
|
79
|
+
"type": { "const": "svg" },
|
|
80
|
+
"svg": {
|
|
81
|
+
"type": "string",
|
|
82
|
+
"minLength": 1,
|
|
83
|
+
"pattern": "\\S"
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
],
|
|
88
|
+
"description": "Lucide icon name in kebab-case or inline SVG geometry using the PluginIcon contract."
|
|
31
89
|
},
|
|
32
90
|
"frontend": {
|
|
33
91
|
"type": "string",
|
|
34
92
|
"minLength": 1,
|
|
35
93
|
"description": "Path to the built frontend JavaScript entry artifact."
|
|
36
94
|
},
|
|
95
|
+
"frontendStyles": {
|
|
96
|
+
"type": "array",
|
|
97
|
+
"minItems": 1,
|
|
98
|
+
"uniqueItems": true,
|
|
99
|
+
"items": {
|
|
100
|
+
"type": "string",
|
|
101
|
+
"minLength": 1,
|
|
102
|
+
"pattern": "\\.css$"
|
|
103
|
+
},
|
|
104
|
+
"description": "Paths to built frontend CSS artifacts. The renderer attaches them before importing the frontend entry and removes them when the plugin deactivates."
|
|
105
|
+
},
|
|
37
106
|
"backend": {
|
|
38
107
|
"type": "string",
|
|
39
108
|
"minLength": 1,
|
|
@@ -47,7 +116,9 @@
|
|
|
47
116
|
"commands",
|
|
48
117
|
"events",
|
|
49
118
|
"views",
|
|
119
|
+
"injectionPoints",
|
|
50
120
|
"taskPane",
|
|
121
|
+
"taskStart",
|
|
51
122
|
"settings",
|
|
52
123
|
"background",
|
|
53
124
|
"backend",
|
|
@@ -61,8 +132,13 @@
|
|
|
61
132
|
"notifications",
|
|
62
133
|
"attention",
|
|
63
134
|
"system.openUrl",
|
|
135
|
+
"system.writeClipboardText",
|
|
64
136
|
"config",
|
|
65
|
-
"projectConfig"
|
|
137
|
+
"projectConfig",
|
|
138
|
+
"browserSurfaces",
|
|
139
|
+
"taskLinks",
|
|
140
|
+
"appEnablement",
|
|
141
|
+
"customSidebarNavigation"
|
|
66
142
|
]
|
|
67
143
|
}
|
|
68
144
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export function isPluginIconName(value) {
|
|
2
|
+
return typeof value === 'string' && value.trim().length > 0;
|
|
3
|
+
}
|
|
4
|
+
export function isPluginSvgIcon(value) {
|
|
5
|
+
if (value === null || typeof value !== 'object')
|
|
6
|
+
return false;
|
|
7
|
+
const icon = value;
|
|
8
|
+
return Object.keys(icon).length === 2
|
|
9
|
+
&& icon.type === 'svg'
|
|
10
|
+
&& typeof icon.svg === 'string'
|
|
11
|
+
&& icon.svg.trim().length > 0;
|
|
12
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { PluginIcon } from './types';
|
|
2
|
+
export declare const MAX_PLUGIN_SVG_ICON_CHARACTERS = 10000;
|
|
3
|
+
/**
|
|
4
|
+
* Validates and sanitizes a plugin icon before it crosses a host contribution boundary.
|
|
5
|
+
* Named icons are preserved so unsupported names can retain the host's existing fallback.
|
|
6
|
+
*/
|
|
7
|
+
export declare function sanitizePluginIcon(icon: unknown): PluginIcon;
|