@openvcs/sdk 0.4.0 → 0.4.1-beta.128
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/README.md +21 -6
- package/lib/build.d.ts +5 -8
- package/lib/build.js +42 -25
- package/lib/init.d.ts +4 -0
- package/lib/init.js +5 -14
- package/lib/npm-runner.d.ts +11 -0
- package/lib/npm-runner.js +62 -0
- package/lib/runtime/dispatcher.js +19 -11
- package/lib/runtime/vcs-delegate-base.d.ts +3 -1
- package/lib/runtime/vcs-delegate-base.js +4 -0
- package/lib/runtime/vcs-delegate-metadata.d.ts +2 -0
- package/lib/runtime/vcs-delegate-metadata.js +1 -0
- package/lib/types/vcs.d.ts +34 -10
- package/package.json +6 -3
- package/src/lib/build.ts +52 -25
- package/src/lib/init.ts +6 -17
- package/src/lib/npm-runner.ts +47 -0
- package/src/lib/runtime/dispatcher.ts +19 -11
- package/src/lib/runtime/vcs-delegate-base.ts +9 -1
- package/src/lib/runtime/vcs-delegate-metadata.ts +2 -0
- package/src/lib/types/vcs.ts +40 -11
- package/test/build.test.js +153 -2
- package/test/cli-direct.test.js +57 -0
- package/test/dispatcher.test.js +104 -0
- package/test/fs-utils.test.js +57 -0
- package/test/host.test.js +39 -0
- package/test/init.test.js +209 -1
- package/test/menu.test.js +147 -0
- package/test/modal.test.js +74 -0
- package/test/npm-runner.test.js +89 -0
- package/test/runtime.test.js +143 -0
- package/test/transport.test.js +71 -0
- package/test/vcs-delegate-base.test.js +87 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openvcs/sdk",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.1-beta.128",
|
|
4
4
|
"description": "OpenVCS SDK CLI for plugin scaffolding and runtime asset builds",
|
|
5
5
|
"license": "GPL-3.0-or-later",
|
|
6
6
|
"homepage": "https://openvcs.app/",
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"url": "https://github.com/Open-VCS/OpenVCS-SDK/issues"
|
|
13
13
|
},
|
|
14
14
|
"engines": {
|
|
15
|
-
"node": ">=
|
|
15
|
+
"node": ">=20"
|
|
16
16
|
},
|
|
17
17
|
"bin": {
|
|
18
18
|
"openvcs": "bin/openvcs.js"
|
|
@@ -32,7 +32,9 @@
|
|
|
32
32
|
},
|
|
33
33
|
"scripts": {
|
|
34
34
|
"build": "node scripts/build-sdk.js",
|
|
35
|
-
"test": "npm run build && npm run test:types && node
|
|
35
|
+
"test": "npm run build && npm run test:types && node scripts/run-tests.js",
|
|
36
|
+
"coverage": "npm run build && npm run test:types && c8 --check-coverage --lines 95 --functions 95 --branches 85 --statements 95 --reporter=text --reporter=lcov node scripts/run-tests.js",
|
|
37
|
+
"test:coverage": "npm run coverage",
|
|
36
38
|
"test:types": "node ./node_modules/typescript/bin/tsc -p test/tsconfig.json",
|
|
37
39
|
"prepack": "npm run build",
|
|
38
40
|
"preopenvcs": "npm run build",
|
|
@@ -40,6 +42,7 @@
|
|
|
40
42
|
},
|
|
41
43
|
"devDependencies": {
|
|
42
44
|
"@types/node": "^25.3.3",
|
|
45
|
+
"c8": "^11.0.0",
|
|
43
46
|
"typescript": "^6.0.3"
|
|
44
47
|
},
|
|
45
48
|
"files": [
|
package/src/lib/build.ts
CHANGED
|
@@ -6,6 +6,9 @@ import * as path from "node:path";
|
|
|
6
6
|
import { spawnSync } from "node:child_process";
|
|
7
7
|
|
|
8
8
|
import { isPathInside } from "./fs-utils";
|
|
9
|
+
import { npmArgsPrefix, npmExecutable } from "./npm-runner";
|
|
10
|
+
|
|
11
|
+
export { npmExecutable } from "./npm-runner";
|
|
9
12
|
|
|
10
13
|
type UsageError = Error & { code?: string };
|
|
11
14
|
|
|
@@ -23,6 +26,10 @@ export interface ManifestInfo {
|
|
|
23
26
|
manifestPath: string;
|
|
24
27
|
}
|
|
25
28
|
|
|
29
|
+
function writeBuildProgress(message: string): void {
|
|
30
|
+
process.stderr.write(`openvcs build: ${message}\n`);
|
|
31
|
+
}
|
|
32
|
+
|
|
26
33
|
interface CommandResult {
|
|
27
34
|
status: number | null;
|
|
28
35
|
error?: Error;
|
|
@@ -34,21 +41,6 @@ interface PackageScripts {
|
|
|
34
41
|
|
|
35
42
|
const AUTHORED_PLUGIN_MODULE_BASENAME = "plugin.js";
|
|
36
43
|
|
|
37
|
-
/** Returns the npm executable name for the current platform. */
|
|
38
|
-
export function npmExecutable(): string {
|
|
39
|
-
return "npm";
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
/** Returns whether a command must be launched via the Windows shell. */
|
|
43
|
-
export function shouldUseWindowsShell(program: string): boolean {
|
|
44
|
-
if (process.platform !== "win32") {
|
|
45
|
-
return false;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
const normalized = program.toLowerCase();
|
|
49
|
-
return normalized === "npm" || normalized.endsWith(".cmd") || normalized.endsWith(".bat");
|
|
50
|
-
}
|
|
51
|
-
|
|
52
44
|
/** Formats help text for the build command. */
|
|
53
45
|
export function buildUsage(commandName = "openvcs"): string {
|
|
54
46
|
return `${commandName} build [args]\n\n --plugin-dir <path> Plugin repository root (contains package.json with openvcs metadata)\n -V, --verbose Enable verbose output\n`;
|
|
@@ -88,11 +80,14 @@ export function parseBuildArgs(args: string[]): BuildArgs {
|
|
|
88
80
|
}
|
|
89
81
|
|
|
90
82
|
/** Reads and validates the plugin manifest. */
|
|
91
|
-
export function readManifest(pluginDir: string): ManifestInfo {
|
|
83
|
+
export function readManifest(pluginDir: string, verbose = false): ManifestInfo {
|
|
92
84
|
const manifestPath = path.join(pluginDir, "package.json");
|
|
93
85
|
let manifestRaw: string;
|
|
94
86
|
let manifestFd: number | undefined;
|
|
95
87
|
let manifest: unknown;
|
|
88
|
+
if (verbose) {
|
|
89
|
+
writeBuildProgress(`reading manifest at ${manifestPath}`);
|
|
90
|
+
}
|
|
96
91
|
try {
|
|
97
92
|
manifestFd = fs.openSync(manifestPath, "r");
|
|
98
93
|
const manifestStat = fs.fstatSync(manifestFd);
|
|
@@ -142,6 +137,10 @@ export function readManifest(pluginDir: string): ManifestInfo {
|
|
|
142
137
|
const entryValue = openvcs.entry;
|
|
143
138
|
const entry = typeof entryValue === "string" ? entryValue.trim() : undefined;
|
|
144
139
|
|
|
140
|
+
if (verbose) {
|
|
141
|
+
writeBuildProgress(`manifest loaded for ${pluginId}${moduleExec ? ` (module.exec: ${moduleExec})` : ""}`);
|
|
142
|
+
}
|
|
143
|
+
|
|
145
144
|
return {
|
|
146
145
|
pluginId,
|
|
147
146
|
moduleExec,
|
|
@@ -151,12 +150,19 @@ export function readManifest(pluginDir: string): ManifestInfo {
|
|
|
151
150
|
}
|
|
152
151
|
|
|
153
152
|
/** Verifies that a declared module entry resolves to a real file under `bin/`. */
|
|
154
|
-
export function validateDeclaredModuleExec(
|
|
153
|
+
export function validateDeclaredModuleExec(
|
|
154
|
+
pluginDir: string,
|
|
155
|
+
moduleExec: string | undefined,
|
|
156
|
+
verbose = false,
|
|
157
|
+
): void {
|
|
155
158
|
if (!moduleExec) {
|
|
156
159
|
return;
|
|
157
160
|
}
|
|
158
161
|
|
|
159
162
|
const targetPath = resolveDeclaredModuleExecPath(pluginDir, moduleExec);
|
|
163
|
+
if (verbose) {
|
|
164
|
+
writeBuildProgress(`checking declared bootstrap at ${targetPath}`);
|
|
165
|
+
}
|
|
160
166
|
if (!fs.existsSync(targetPath) || !fs.lstatSync(targetPath).isFile()) {
|
|
161
167
|
throw new Error(`module entrypoint not found at ${targetPath}`);
|
|
162
168
|
}
|
|
@@ -191,11 +197,15 @@ export function authoredPluginModulePath(pluginDir: string): string {
|
|
|
191
197
|
export function validateGeneratedBootstrapTargets(
|
|
192
198
|
pluginDir: string,
|
|
193
199
|
moduleExec: string | undefined,
|
|
200
|
+
verbose = false,
|
|
194
201
|
): void {
|
|
195
202
|
if (!moduleExec) {
|
|
196
203
|
return;
|
|
197
204
|
}
|
|
198
205
|
|
|
206
|
+
if (verbose) {
|
|
207
|
+
writeBuildProgress(`validating generated bootstrap target for ${moduleExec}`);
|
|
208
|
+
}
|
|
199
209
|
resolveDeclaredModuleExecPath(pluginDir, moduleExec);
|
|
200
210
|
const normalizedExec = moduleExec.trim().toLowerCase();
|
|
201
211
|
if (normalizedExec === AUTHORED_PLUGIN_MODULE_BASENAME.toLowerCase()) {
|
|
@@ -242,18 +252,28 @@ export function renderGeneratedBootstrap(
|
|
|
242
252
|
}
|
|
243
253
|
|
|
244
254
|
/** Writes the generated SDK-owned module entrypoint under `bin/<module.exec>`. */
|
|
245
|
-
export function generateModuleBootstrap(
|
|
255
|
+
export function generateModuleBootstrap(
|
|
256
|
+
pluginDir: string,
|
|
257
|
+
moduleExec: string | undefined,
|
|
258
|
+
verbose = false,
|
|
259
|
+
): void {
|
|
246
260
|
if (!moduleExec) {
|
|
247
|
-
|
|
261
|
+
if (verbose) {
|
|
262
|
+
writeBuildProgress(`no module.exec defined for ${pluginDir}; skipping bootstrap generation`);
|
|
263
|
+
}
|
|
248
264
|
return;
|
|
249
265
|
}
|
|
250
266
|
|
|
251
|
-
validateGeneratedBootstrapTargets(pluginDir, moduleExec);
|
|
267
|
+
validateGeneratedBootstrapTargets(pluginDir, moduleExec, verbose);
|
|
252
268
|
const execPath = resolveDeclaredModuleExecPath(pluginDir, moduleExec);
|
|
253
269
|
const pluginModulePath = authoredPluginModulePath(pluginDir);
|
|
254
270
|
const pluginModuleImportPath = relativeBinImport(execPath, pluginModulePath);
|
|
255
271
|
const isEsm = detectEsmMode(pluginDir, moduleExec);
|
|
256
272
|
|
|
273
|
+
if (verbose) {
|
|
274
|
+
writeBuildProgress(`writing bootstrap ${execPath} -> ${pluginModuleImportPath}${isEsm ? " (esm)" : " (cjs)"}`);
|
|
275
|
+
}
|
|
276
|
+
|
|
257
277
|
fs.mkdirSync(path.dirname(execPath), { recursive: true });
|
|
258
278
|
fs.writeFileSync(execPath, renderGeneratedBootstrap(pluginModuleImportPath, isEsm), "utf8");
|
|
259
279
|
}
|
|
@@ -288,8 +308,8 @@ export function runCommand(program: string, args: string[], cwd: string, verbose
|
|
|
288
308
|
|
|
289
309
|
const result = spawnSync(program, args, {
|
|
290
310
|
cwd,
|
|
291
|
-
shell: shouldUseWindowsShell(program),
|
|
292
311
|
stdio: ["ignore", verbose ? "inherit" : "ignore", "inherit"],
|
|
312
|
+
windowsHide: true,
|
|
293
313
|
}) as CommandResult;
|
|
294
314
|
|
|
295
315
|
if (result.error) {
|
|
@@ -325,11 +345,14 @@ function readPackageScripts(pluginDir: string): PackageScripts {
|
|
|
325
345
|
|
|
326
346
|
/** Builds a plugin's runtime assets when it declares a code module. */
|
|
327
347
|
export function buildPluginAssets(parsedArgs: BuildArgs): ManifestInfo {
|
|
328
|
-
|
|
348
|
+
writeBuildProgress(`reading plugin manifest in ${parsedArgs.pluginDir}`);
|
|
349
|
+
const manifest = readManifest(parsedArgs.pluginDir, parsedArgs.verbose);
|
|
329
350
|
if (!manifest.moduleExec) {
|
|
351
|
+
writeBuildProgress(`theme-only plugin ${manifest.pluginId}; nothing to build`);
|
|
330
352
|
return manifest;
|
|
331
353
|
}
|
|
332
354
|
|
|
355
|
+
writeBuildProgress(`building plugin ${manifest.pluginId}`);
|
|
333
356
|
if (!hasPackageJson(parsedArgs.pluginDir)) {
|
|
334
357
|
throw new Error(`code plugins must include package.json: ${path.join(parsedArgs.pluginDir, "package.json")}`);
|
|
335
358
|
}
|
|
@@ -342,8 +365,12 @@ export function buildPluginAssets(parsedArgs: BuildArgs): ManifestInfo {
|
|
|
342
365
|
);
|
|
343
366
|
}
|
|
344
367
|
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
368
|
+
writeBuildProgress(`running build:plugin`);
|
|
369
|
+
runCommand(npmExecutable(), [...npmArgsPrefix(), "run", "build:plugin"], parsedArgs.pluginDir, parsedArgs.verbose);
|
|
370
|
+
writeBuildProgress(`generating bootstrap ${manifest.moduleExec}`);
|
|
371
|
+
generateModuleBootstrap(parsedArgs.pluginDir, manifest.moduleExec, parsedArgs.verbose);
|
|
372
|
+
writeBuildProgress(`validating bootstrap ${manifest.moduleExec}`);
|
|
373
|
+
validateDeclaredModuleExec(parsedArgs.pluginDir, manifest.moduleExec, parsedArgs.verbose);
|
|
374
|
+
writeBuildProgress(`build complete for ${manifest.pluginId}`);
|
|
348
375
|
return manifest;
|
|
349
376
|
}
|
package/src/lib/init.ts
CHANGED
|
@@ -4,6 +4,8 @@ import * as readline from "node:readline/promises";
|
|
|
4
4
|
import { stdin, stdout } from "node:process";
|
|
5
5
|
import { spawnSync } from "node:child_process";
|
|
6
6
|
|
|
7
|
+
import { npmArgsPrefix, npmExecutable } from "./npm-runner";
|
|
8
|
+
|
|
7
9
|
const packageJson: { version: string } = require("../package.json");
|
|
8
10
|
|
|
9
11
|
type UsageError = Error & { code?: string };
|
|
@@ -33,19 +35,6 @@ interface InitCommandError {
|
|
|
33
35
|
code?: string;
|
|
34
36
|
}
|
|
35
37
|
|
|
36
|
-
function npmExecutable(): string {
|
|
37
|
-
return "npm";
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
function shouldUseWindowsShell(program: string): boolean {
|
|
41
|
-
if (process.platform !== "win32") {
|
|
42
|
-
return false;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
const normalized = program.toLowerCase();
|
|
46
|
-
return normalized === "npm" || normalized.endsWith(".cmd") || normalized.endsWith(".bat");
|
|
47
|
-
}
|
|
48
|
-
|
|
49
38
|
export function initUsage(commandName = "openvcs"): string {
|
|
50
39
|
return `Usage: ${commandName} init [--theme] [target-dir]\n\nOptions:\n --theme Start with a theme-only plugin template\n`;
|
|
51
40
|
}
|
|
@@ -214,10 +203,10 @@ async function collectAnswers(
|
|
|
214
203
|
}
|
|
215
204
|
|
|
216
205
|
function runNpmInstall(targetDir: string): void {
|
|
217
|
-
const result = spawnSync(npmExecutable(), ["install"], {
|
|
206
|
+
const result = spawnSync(npmExecutable(), [...npmArgsPrefix(), "install"], {
|
|
218
207
|
cwd: targetDir,
|
|
219
|
-
shell: shouldUseWindowsShell(npmExecutable()),
|
|
220
208
|
stdio: "inherit",
|
|
209
|
+
windowsHide: true,
|
|
221
210
|
});
|
|
222
211
|
if (result.error) {
|
|
223
212
|
throw new Error(`failed to spawn npm install in ${targetDir}: ${result.error.message}`);
|
|
@@ -241,7 +230,6 @@ function writeModuleTemplate(answers: InitAnswers): void {
|
|
|
241
230
|
openvcs: {
|
|
242
231
|
id: answers.pluginId,
|
|
243
232
|
name: answers.pluginName,
|
|
244
|
-
version: answers.pluginVersion,
|
|
245
233
|
default_enabled: answers.defaultEnabled,
|
|
246
234
|
module: { exec: "openvcs-plugin.js" },
|
|
247
235
|
},
|
|
@@ -286,7 +274,6 @@ function writeThemeTemplate(answers: InitAnswers): void {
|
|
|
286
274
|
openvcs: {
|
|
287
275
|
id: answers.pluginId,
|
|
288
276
|
name: answers.pluginName,
|
|
289
|
-
version: answers.pluginVersion,
|
|
290
277
|
default_enabled: answers.defaultEnabled,
|
|
291
278
|
},
|
|
292
279
|
scripts: {
|
|
@@ -376,8 +363,10 @@ export function isUsageError(error: unknown): error is InitCommandError {
|
|
|
376
363
|
export const __private = {
|
|
377
364
|
collectAnswers,
|
|
378
365
|
createReadlinePromptDriver,
|
|
366
|
+
defaultPluginNameFromId,
|
|
379
367
|
defaultPluginIdFromDir,
|
|
380
368
|
sanitizeIdToken,
|
|
381
369
|
validatePluginId,
|
|
382
370
|
writeModuleTemplate,
|
|
371
|
+
writeThemeTemplate,
|
|
383
372
|
};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
// Copyright © 2025-2026 OpenVCS Contributors
|
|
2
|
+
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
3
|
+
|
|
4
|
+
import * as fs from "node:fs";
|
|
5
|
+
import * as path from "node:path";
|
|
6
|
+
|
|
7
|
+
type ExistsFn = (path: string) => boolean;
|
|
8
|
+
type ResolveFn = (specifier: string) => string;
|
|
9
|
+
|
|
10
|
+
export interface NpmCommand {
|
|
11
|
+
program: string;
|
|
12
|
+
argsPrefix: string[];
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function resolveNpmCli(
|
|
16
|
+
execPath = process.execPath,
|
|
17
|
+
exists: ExistsFn = fs.existsSync,
|
|
18
|
+
resolve: ResolveFn = require.resolve,
|
|
19
|
+
): string {
|
|
20
|
+
const localNodeModules = path.join(path.dirname(execPath), "node_modules", "npm", "bin", "npm-cli.js");
|
|
21
|
+
if (exists(localNodeModules)) {
|
|
22
|
+
return localNodeModules;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return resolve("npm/bin/npm-cli.js");
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function npmCommand(
|
|
29
|
+
platform = process.platform,
|
|
30
|
+
execPath = process.execPath,
|
|
31
|
+
exists: ExistsFn = fs.existsSync,
|
|
32
|
+
resolve: ResolveFn = require.resolve,
|
|
33
|
+
): NpmCommand {
|
|
34
|
+
if (platform !== "win32") {
|
|
35
|
+
return { program: "npm", argsPrefix: [] };
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return { program: execPath, argsPrefix: [resolveNpmCli(execPath, exists, resolve)] };
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function npmExecutable(): string {
|
|
42
|
+
return npmCommand().program;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function npmArgsPrefix(): string[] {
|
|
46
|
+
return npmCommand().argsPrefix;
|
|
47
|
+
}
|
|
@@ -133,22 +133,30 @@ export function createRuntimeDispatcher(
|
|
|
133
133
|
|
|
134
134
|
let result: unknown;
|
|
135
135
|
if (timeout && timeout > 0) {
|
|
136
|
-
|
|
137
|
-
const
|
|
136
|
+
let timeoutId: ReturnType<typeof setTimeout> | undefined;
|
|
137
|
+
const timeoutPromise = new Promise<never>((_resolve, reject) => {
|
|
138
|
+
timeoutId = setTimeout(() => {
|
|
139
|
+
reject(pluginError('request-timeout', `method '${method}' timed out after ${timeout}ms`));
|
|
140
|
+
}, timeout);
|
|
141
|
+
});
|
|
138
142
|
try {
|
|
139
|
-
result = await
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
143
|
+
result = await Promise.race([
|
|
144
|
+
handler(params, {
|
|
145
|
+
host,
|
|
146
|
+
requestId: id,
|
|
147
|
+
method,
|
|
148
|
+
}),
|
|
149
|
+
timeoutPromise,
|
|
150
|
+
]);
|
|
144
151
|
} catch (error) {
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
throw pluginError('request-timeout', `method '${method}' timed out after ${timeout}ms`);
|
|
152
|
+
if (timeoutId !== undefined) {
|
|
153
|
+
clearTimeout(timeoutId);
|
|
148
154
|
}
|
|
149
155
|
throw error;
|
|
150
156
|
}
|
|
151
|
-
|
|
157
|
+
if (timeoutId !== undefined) {
|
|
158
|
+
clearTimeout(timeoutId);
|
|
159
|
+
}
|
|
152
160
|
} else {
|
|
153
161
|
result = await handler(params, {
|
|
154
162
|
host,
|
|
@@ -259,7 +259,7 @@ export abstract class VcsDelegateBase<
|
|
|
259
259
|
diffFile(
|
|
260
260
|
_params: VcsTypes.VcsDiffFileParams,
|
|
261
261
|
_context: TContext,
|
|
262
|
-
): VcsHandlerResult<
|
|
262
|
+
): VcsHandlerResult<VcsTypes.VcsDiffFileResponse> {
|
|
263
263
|
return this.unimplemented('diffFile');
|
|
264
264
|
}
|
|
265
265
|
|
|
@@ -303,6 +303,14 @@ export abstract class VcsDelegateBase<
|
|
|
303
303
|
return this.unimplemented('stagePatch');
|
|
304
304
|
}
|
|
305
305
|
|
|
306
|
+
/** Handles `vcs.stage_selections` (structured hunk/line selections). */
|
|
307
|
+
stageSelections(
|
|
308
|
+
_params: VcsTypes.VcsStageSelectionsParams,
|
|
309
|
+
_context: TContext,
|
|
310
|
+
): VcsHandlerResult<null> {
|
|
311
|
+
return this.unimplemented('stageSelections');
|
|
312
|
+
}
|
|
313
|
+
|
|
306
314
|
/** Handles `vcs.stage_paths`. */
|
|
307
315
|
stagePaths(
|
|
308
316
|
_params: VcsTypes.VcsStagePathsParams,
|
|
@@ -53,6 +53,7 @@ export type VcsDelegateBindings<TContext> = {
|
|
|
53
53
|
VcsTypes.VcsDelegates<TContext>['vcs.write_merge_result']
|
|
54
54
|
>;
|
|
55
55
|
stagePatch: NonNullable<VcsTypes.VcsDelegates<TContext>['vcs.stage_patch']>;
|
|
56
|
+
stageSelections: NonNullable<VcsTypes.VcsDelegates<TContext>['vcs.stage_selections']>;
|
|
56
57
|
stagePaths: NonNullable<VcsTypes.VcsDelegates<TContext>['vcs.stage_paths']>;
|
|
57
58
|
discardPaths: NonNullable<VcsTypes.VcsDelegates<TContext>['vcs.discard_paths']>;
|
|
58
59
|
applyReversePatch: NonNullable<
|
|
@@ -123,6 +124,7 @@ export const VCS_DELEGATE_METHOD_MAPPINGS = {
|
|
|
123
124
|
checkoutConflictSide: 'vcs.checkout_conflict_side',
|
|
124
125
|
writeMergeResult: 'vcs.write_merge_result',
|
|
125
126
|
stagePatch: 'vcs.stage_patch',
|
|
127
|
+
stageSelections: 'vcs.stage_selections',
|
|
126
128
|
stagePaths: 'vcs.stage_paths',
|
|
127
129
|
discardPaths: 'vcs.discard_paths',
|
|
128
130
|
applyReversePatch: 'vcs.apply_reverse_patch',
|
package/src/lib/types/vcs.ts
CHANGED
|
@@ -17,6 +17,8 @@ export interface VcsCapabilities {
|
|
|
17
17
|
push_pull: boolean;
|
|
18
18
|
/** Indicates whether fast-forward helpers are supported. */
|
|
19
19
|
fast_forward: boolean;
|
|
20
|
+
/** Merge strategies the backend supports (values like "merge", "squash", "rebase"). */
|
|
21
|
+
merge_strategies?: string[];
|
|
20
22
|
}
|
|
21
23
|
|
|
22
24
|
/** Describes params that carry a repository session id. */
|
|
@@ -110,20 +112,13 @@ export interface VcsRemoveRemoteParams extends VcsSessionParams {
|
|
|
110
112
|
name: string;
|
|
111
113
|
}
|
|
112
114
|
|
|
113
|
-
/** Describes optional fetch flags. */
|
|
114
|
-
export interface VcsFetchOptions {
|
|
115
|
-
/** Indicates whether stale remote references should be pruned. */
|
|
116
|
-
prune?: boolean;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
115
|
/** Describes params for fetch methods. */
|
|
116
|
+
|
|
120
117
|
export interface VcsFetchParams extends VcsSessionParams {
|
|
121
118
|
/** Stores the remote to fetch when one is supplied. */
|
|
122
119
|
remote?: string;
|
|
123
120
|
/** Stores the refspec to fetch when one is supplied. */
|
|
124
121
|
refspec?: string;
|
|
125
|
-
/** Stores optional fetch flags. */
|
|
126
|
-
opts?: VcsFetchOptions;
|
|
127
122
|
}
|
|
128
123
|
|
|
129
124
|
/** Describes params for push. */
|
|
@@ -180,6 +175,8 @@ export interface StatusFileEntry {
|
|
|
180
175
|
resolved_conflict: boolean;
|
|
181
176
|
/** Stores placeholder hunk information until richer diff support exists. */
|
|
182
177
|
hunks: never[];
|
|
178
|
+
/** Indicates whether the file content should be treated as binary. */
|
|
179
|
+
binary?: boolean | null;
|
|
183
180
|
}
|
|
184
181
|
|
|
185
182
|
/** Describes the structured status payload returned to the host. */
|
|
@@ -256,6 +253,17 @@ export interface VcsDiffCommitParams extends VcsSessionParams {
|
|
|
256
253
|
rev: string;
|
|
257
254
|
}
|
|
258
255
|
|
|
256
|
+
/** Describes one structured diff payload returned for a file. */
|
|
257
|
+
export interface VcsDiffResult {
|
|
258
|
+
/** Stores line-oriented diff output. */
|
|
259
|
+
lines: string[];
|
|
260
|
+
/** Indicates whether the diff target should be treated as binary. */
|
|
261
|
+
binary?: boolean | null;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/** Describes the accepted `vcs.diff_file` response shapes. */
|
|
265
|
+
export type VcsDiffFileResponse = VcsDiffResult | string[];
|
|
266
|
+
|
|
259
267
|
/** Describes params for `vcs.get_conflict_details`. */
|
|
260
268
|
export interface VcsGetConflictDetailsParams extends VcsSessionParams {
|
|
261
269
|
/** Stores the conflicted path. */
|
|
@@ -274,8 +282,6 @@ export interface VcsConflictDetails {
|
|
|
274
282
|
theirs: string | null;
|
|
275
283
|
/** Indicates whether the conflict is binary. */
|
|
276
284
|
binary: boolean;
|
|
277
|
-
/** Indicates whether the conflict references Git LFS content. */
|
|
278
|
-
lfs_pointer: boolean;
|
|
279
285
|
}
|
|
280
286
|
|
|
281
287
|
/** Describes params for checking out one side of a conflict. */
|
|
@@ -300,6 +306,22 @@ export interface VcsStagePatchParams extends VcsSessionParams {
|
|
|
300
306
|
patch: string;
|
|
301
307
|
}
|
|
302
308
|
|
|
309
|
+
/** Describes a single file's hunk/line selection for partial staging. */
|
|
310
|
+
export interface HunkSelection {
|
|
311
|
+
/** Repository-relative file path. */
|
|
312
|
+
path: string;
|
|
313
|
+
/** Indices of whole hunks to include. */
|
|
314
|
+
whole_hunks: number[];
|
|
315
|
+
/** Per-hunk line selections: maps hunk index → 1-based line offsets. */
|
|
316
|
+
partial_hunks: Record<number, number[]>;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/** Describes params for staging structured selections (VCS-agnostic). */
|
|
320
|
+
export interface VcsStageSelectionsParams extends VcsSessionParams {
|
|
321
|
+
/** Structured hunk/line selections for multiple files. */
|
|
322
|
+
selections: HunkSelection[];
|
|
323
|
+
}
|
|
324
|
+
|
|
303
325
|
/** Describes params for staging repository-relative paths into the index. */
|
|
304
326
|
export interface VcsStagePathsParams extends VcsSessionParams {
|
|
305
327
|
/** Stores the paths to stage. */
|
|
@@ -334,12 +356,17 @@ export interface VcsRenameBranchParams extends VcsSessionParams {
|
|
|
334
356
|
new: string;
|
|
335
357
|
}
|
|
336
358
|
|
|
359
|
+
/** Describes the supported merge strategies. */
|
|
360
|
+
export type VcsMergeStrategy = 'merge' | 'squash' | 'rebase';
|
|
361
|
+
|
|
337
362
|
/** Describes params for merging another branch into the current branch. */
|
|
338
363
|
export interface VcsMergeIntoCurrentParams extends VcsSessionParams {
|
|
339
364
|
/** Stores the branch name to merge. */
|
|
340
365
|
name: string;
|
|
341
366
|
/** Stores an optional merge commit message. */
|
|
342
367
|
message?: string;
|
|
368
|
+
/** Stores the merge strategy. Defaults to 'merge' when absent. */
|
|
369
|
+
strategy?: VcsMergeStrategy;
|
|
343
370
|
}
|
|
344
371
|
|
|
345
372
|
/** Describes params for setting a branch upstream. */
|
|
@@ -503,7 +530,7 @@ export interface VcsDelegates<TContext = unknown> {
|
|
|
503
530
|
TContext
|
|
504
531
|
>;
|
|
505
532
|
/** Handles `vcs.diff_file`. */
|
|
506
|
-
'vcs.diff_file'?: RpcMethodHandler<VcsDiffFileParams,
|
|
533
|
+
'vcs.diff_file'?: RpcMethodHandler<VcsDiffFileParams, VcsDiffFileResponse, TContext>;
|
|
507
534
|
/** Handles `vcs.diff_commit`. */
|
|
508
535
|
'vcs.diff_commit'?: RpcMethodHandler<VcsDiffCommitParams, string[], TContext>;
|
|
509
536
|
/** Handles `vcs.get_conflict_details`. */
|
|
@@ -526,6 +553,8 @@ export interface VcsDelegates<TContext = unknown> {
|
|
|
526
553
|
>;
|
|
527
554
|
/** Handles `vcs.stage_patch`. */
|
|
528
555
|
'vcs.stage_patch'?: RpcMethodHandler<VcsStagePatchParams, null, TContext>;
|
|
556
|
+
/** Handles `vcs.stage_selections` (structured hunk/line selections). */
|
|
557
|
+
'vcs.stage_selections'?: RpcMethodHandler<VcsStageSelectionsParams, null, TContext>;
|
|
529
558
|
/** Handles `vcs.stage_paths`. */
|
|
530
559
|
'vcs.stage_paths'?: RpcMethodHandler<VcsStagePathsParams, null, TContext>;
|
|
531
560
|
/** Handles `vcs.discard_paths`. */
|