@deepseek-ai/dsh-experimental-client-ui-forge 0.2.2-alpha.2 → 0.2.2-alpha.3
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.i18n.yaml +12 -12
- package/README.md +19 -11
- package/README.zh.md +20 -12
- package/lib/client.js +7869 -4812
- package/lib/index.js +159 -52
- package/lib/typert.host.js +298 -18
- package/lib/typert.remote-client.d.ts +7 -1
- package/lib/typert.remote-client.js +267 -13
- package/lib/types/client/ChangeRequestCard.d.ts +3 -1
- package/lib/types/client/ChangedFiles.d.ts +36 -0
- package/lib/types/client/ChangesTab.d.ts +11 -1
- package/lib/types/client/DiffFile.d.ts +3 -2
- package/lib/types/client/FileTreePane.d.ts +15 -0
- package/lib/types/client/ForgeTab.d.ts +7 -4
- package/lib/types/client/LocalChangesCard.d.ts +3 -3
- package/lib/types/client/ReviewComposer.d.ts +40 -0
- package/lib/types/client/ReviewThread.d.ts +26 -0
- package/lib/types/client/SubmitReview.d.ts +22 -0
- package/lib/types/client/WorktreeToggle.d.ts +6 -5
- package/lib/types/client/changes-address.d.ts +1 -1
- package/lib/types/client/code-selection.d.ts +23 -0
- package/lib/types/client/file-tree.d.ts +33 -0
- package/lib/types/client/locales.d.ts +58 -4
- package/lib/types/client/review-queue.d.ts +136 -0
- package/lib/types/client/review.d.ts +56 -0
- package/lib/types/client/status.d.ts +6 -2
- package/lib/types/git.d.ts +10 -10
- package/lib/types/index.d.ts +25 -4
- package/lib/types/local-diff.d.ts +11 -4
- package/lib/types/states.d.ts +9 -3
- package/lib/types/types.d.ts +4 -0
- package/package.json +47 -47
package/lib/index.js
CHANGED
|
@@ -101,7 +101,7 @@ function remoteOrder(upstream, remotes) {
|
|
|
101
101
|
}
|
|
102
102
|
//#endregion
|
|
103
103
|
//#region lib/types/git.js
|
|
104
|
-
/**
|
|
104
|
+
/** Git queries in a Session's current directory, using its permissions and execution world. */
|
|
105
105
|
var __addDisposableResource$1 = function(env, value, async) {
|
|
106
106
|
if (value !== null && value !== void 0) {
|
|
107
107
|
if (typeof value !== "object" && typeof value !== "function") throw new TypeError("Object expected.");
|
|
@@ -203,9 +203,9 @@ const GIT_PREFIX = [
|
|
|
203
203
|
const NOT_A_CHECKOUT = /not a git repository|must be run in a work tree/i;
|
|
204
204
|
/**
|
|
205
205
|
* Inspect the checkout containing `directory`. Every command runs through the
|
|
206
|
-
* mounted subprocess provider under
|
|
207
|
-
*
|
|
208
|
-
*
|
|
206
|
+
* mounted subprocess provider under the Session's sandbox policy and reads
|
|
207
|
+
* refs and configuration only, never the working tree.
|
|
208
|
+
* The top-level and shared Git directories Git prints are resolved
|
|
209
209
|
* through `ctx.fs` into canonical process paths, the form the
|
|
210
210
|
* working-directory service records.
|
|
211
211
|
* @param ctx - context that provides `fs`, `subprocess`, `sandbox`, and `sandboxPolicy`.
|
|
@@ -265,7 +265,7 @@ async function inspectCheckout(ctx, config, session, directory, signal) {
|
|
|
265
265
|
}
|
|
266
266
|
/**
|
|
267
267
|
* List the local branches that can start a worktree of the checkout containing
|
|
268
|
-
* `directory`,
|
|
268
|
+
* `directory`, using the Session's permissions as in {@link inspectCheckout}.
|
|
269
269
|
* @param ctx - context that provides `subprocess`, `sandbox`, and `sandboxPolicy`.
|
|
270
270
|
* @param config - executable and process limits.
|
|
271
271
|
* @param session - Session whose sandbox policy and execution world apply.
|
|
@@ -304,7 +304,7 @@ async function together(signal, start) {
|
|
|
304
304
|
}
|
|
305
305
|
}
|
|
306
306
|
/**
|
|
307
|
-
* Report whether a local branch exists,
|
|
307
|
+
* Report whether a local branch exists, using the Session's permissions as in {@link inspectCheckout}.
|
|
308
308
|
* @param ctx - context that provides `subprocess`, `sandbox`, and `sandboxPolicy`.
|
|
309
309
|
* @param config - executable and process limits.
|
|
310
310
|
* @param session - Session whose sandbox policy and execution world apply.
|
|
@@ -323,8 +323,8 @@ async function hasBranch(ctx, config, session, repositoryRoot, branch, signal) {
|
|
|
323
323
|
], [0, 1])).exitCode === 0;
|
|
324
324
|
}
|
|
325
325
|
/**
|
|
326
|
-
* Bind the
|
|
327
|
-
* runs under
|
|
326
|
+
* Bind the Git runner to one Session and directory: every command
|
|
327
|
+
* runs under the Session's sandbox policy with the
|
|
328
328
|
* scrubbed environment, the fixed command prefix, and `config`'s limits.
|
|
329
329
|
* @param ctx - context that provides `subprocess`, `sandbox`, and `sandboxPolicy`.
|
|
330
330
|
* @param config - executable and process limits.
|
|
@@ -334,14 +334,8 @@ async function hasBranch(ctx, config, session, repositoryRoot, branch, signal) {
|
|
|
334
334
|
* @returns the runner.
|
|
335
335
|
*/
|
|
336
336
|
function gitIn(ctx, config, session, directory, signal) {
|
|
337
|
-
const policy = {
|
|
338
|
-
|
|
339
|
-
session,
|
|
340
|
-
mode: "read-only"
|
|
341
|
-
}),
|
|
342
|
-
mode: "read-only"
|
|
343
|
-
};
|
|
344
|
-
return (name, args, allowedExitCodes = [0]) => runGit(ctx, config, policy, directory, name, args, signal, allowedExitCodes);
|
|
337
|
+
const policy = ctx.sandboxPolicy.resolve({ session });
|
|
338
|
+
return (name, args, allowedExitCodes = [0], configOverrides = {}) => runGit(ctx, config, policy, directory, name, args, signal, allowedExitCodes, configOverrides);
|
|
345
339
|
}
|
|
346
340
|
/** Read the checked-out branch, including an unborn one; null when HEAD is detached. */
|
|
347
341
|
async function currentBranch(git) {
|
|
@@ -362,7 +356,7 @@ async function headCommit(git) {
|
|
|
362
356
|
], [0, 128]);
|
|
363
357
|
return head.exitCode === 0 ? parseName(head.stdout) : null;
|
|
364
358
|
}
|
|
365
|
-
async function runGit(ctx, config, policy, cwd, name, args, signal, allowedExitCodes) {
|
|
359
|
+
async function runGit(ctx, config, policy, cwd, name, args, signal, allowedExitCodes, configOverrides) {
|
|
366
360
|
const env_1 = {
|
|
367
361
|
stack: [],
|
|
368
362
|
error: void 0,
|
|
@@ -370,17 +364,25 @@ async function runGit(ctx, config, policy, cwd, name, args, signal, allowedExitC
|
|
|
370
364
|
};
|
|
371
365
|
try {
|
|
372
366
|
const timeout = __addDisposableResource$1(env_1, deadline(signal, config.gitTimeoutMs, "FORGE_PANEL_GIT_TIMEOUT"), false);
|
|
373
|
-
const
|
|
374
|
-
|
|
375
|
-
program,
|
|
367
|
+
const command = [
|
|
368
|
+
await ctx.subprocess.resolveExecutable(config.gitCommand, void 0, timeout.signal),
|
|
376
369
|
...GIT_PREFIX,
|
|
377
370
|
...args
|
|
378
|
-
]
|
|
371
|
+
];
|
|
372
|
+
const argv = policy.mode === "danger-full-access" ? command : (await ctx.sandbox.confine(command, {
|
|
373
|
+
...policy,
|
|
374
|
+
mode: policy.mode
|
|
375
|
+
}, timeout.signal)).argv;
|
|
379
376
|
timeout.signal.throwIfAborted();
|
|
377
|
+
const overrides = Object.entries(configOverrides);
|
|
380
378
|
const child = ctx.subprocess.spawn({
|
|
381
|
-
argv
|
|
379
|
+
argv,
|
|
382
380
|
cwd,
|
|
383
|
-
env:
|
|
381
|
+
env: {
|
|
382
|
+
...GIT_ENV,
|
|
383
|
+
GIT_CONFIG_COUNT: String(overrides.length),
|
|
384
|
+
...Object.fromEntries(overrides.flatMap(([key, value], index) => [[`GIT_CONFIG_KEY_${index}`, key], [`GIT_CONFIG_VALUE_${index}`, value]]))
|
|
385
|
+
},
|
|
384
386
|
signal: timeout.signal,
|
|
385
387
|
graceMs: config.graceMs,
|
|
386
388
|
stdio: {
|
|
@@ -557,29 +559,59 @@ function pathOf(name, prefix) {
|
|
|
557
559
|
* Local changes of a Session's checkout: where the `branch` scope forks, the
|
|
558
560
|
* patch of one scope with its untracked files, the new-side text of one file,
|
|
559
561
|
* and the size of the `branch` scope for the panel's card. Git runs through
|
|
560
|
-
* the
|
|
562
|
+
* the Session's Git runner; untracked and working-tree files are read through
|
|
561
563
|
* `ctx.fs`, both within `maxDiffBytes`.
|
|
562
564
|
*/
|
|
563
565
|
/** Thrown when a patch, a file list, or a file exceeds `maxDiffBytes`. */
|
|
564
566
|
var DiffTooLarge = class extends Error {
|
|
565
567
|
name = "DiffTooLarge";
|
|
566
568
|
};
|
|
569
|
+
/** Thrown when filter configuration cannot be read completely; no comparison has run. */
|
|
570
|
+
var FilterConfigError = class extends Error {
|
|
571
|
+
name = "FilterConfigError";
|
|
572
|
+
};
|
|
567
573
|
/** Thrown for the `branch` scope of a checkout without a fork point. */
|
|
568
574
|
var NoForkPoint = class extends Error {
|
|
569
575
|
name = "NoForkPoint";
|
|
570
576
|
};
|
|
571
|
-
/**
|
|
577
|
+
/** No executable diff helpers or submodule working-tree inspection; preserve recorded submodule commit changes. */
|
|
572
578
|
const DIFF = [
|
|
573
579
|
"-c",
|
|
574
580
|
"core.quotePath=false",
|
|
575
581
|
"diff",
|
|
576
582
|
"--no-ext-diff",
|
|
577
583
|
"--no-textconv",
|
|
584
|
+
"--submodule=short",
|
|
585
|
+
"--ignore-submodules=dirty",
|
|
578
586
|
"--no-color",
|
|
579
587
|
"-M",
|
|
580
588
|
"--src-prefix=a/",
|
|
581
589
|
"--dst-prefix=b/"
|
|
582
590
|
];
|
|
591
|
+
/** Disable enumerated clean/process filters for one diff without modifying Git configuration. */
|
|
592
|
+
async function diffFilterOverrides(git) {
|
|
593
|
+
let configured;
|
|
594
|
+
try {
|
|
595
|
+
configured = await git("filters", [
|
|
596
|
+
"config",
|
|
597
|
+
"--null",
|
|
598
|
+
"--name-only",
|
|
599
|
+
"--get-regexp",
|
|
600
|
+
"^filter\\."
|
|
601
|
+
], [0, 1]);
|
|
602
|
+
} catch (error) {
|
|
603
|
+
throw new FilterConfigError("Cannot read complete Git filter configuration; comparison was not run", { cause: error });
|
|
604
|
+
}
|
|
605
|
+
const overrides = {};
|
|
606
|
+
for (const key of configured.stdout.split("\0")) {
|
|
607
|
+
if (!key.endsWith(".clean") && !key.endsWith(".process")) continue;
|
|
608
|
+
const driver = key.slice(0, key.lastIndexOf("."));
|
|
609
|
+
overrides[`${driver}.clean`] = "";
|
|
610
|
+
overrides[`${driver}.process`] = "";
|
|
611
|
+
overrides[`${driver}.required`] = "false";
|
|
612
|
+
}
|
|
613
|
+
return overrides;
|
|
614
|
+
}
|
|
583
615
|
/** Characters Git inspects for a NUL character before it calls content binary. */
|
|
584
616
|
const BINARY_PROBE = 8e3;
|
|
585
617
|
/**
|
|
@@ -628,7 +660,8 @@ async function forkPoint(git, inspection, created) {
|
|
|
628
660
|
return null;
|
|
629
661
|
}
|
|
630
662
|
/**
|
|
631
|
-
* Read
|
|
663
|
+
* Read changes at `root` with command-local clean/process filter overrides and without
|
|
664
|
+
* submodule working-tree inspection. Git's built-in newline, ident, and encoding conversions still apply.
|
|
632
665
|
* @param ctx - context that provides `fs`, `subprocess`, `sandbox`, and `sandboxPolicy`.
|
|
633
666
|
* @param config - Git and size limits.
|
|
634
667
|
* @param session - Session whose sandbox policy and execution world apply.
|
|
@@ -637,7 +670,8 @@ async function forkPoint(git, inspection, created) {
|
|
|
637
670
|
* @param scope - which changes to read.
|
|
638
671
|
* @param signal - cancellation of every command and read.
|
|
639
672
|
* @returns tracked files in Git's order, then untracked files unless the scope is `staged`.
|
|
640
|
-
* @throws NoForkPoint for the `branch` scope without `fork`;
|
|
673
|
+
* @throws NoForkPoint for the `branch` scope without `fork`; FilterConfigError when filter configuration
|
|
674
|
+
* cannot be read within `maxDiffBytes`; DiffTooLarge for oversized comparison output; an Error when Git fails.
|
|
641
675
|
*/
|
|
642
676
|
async function localDiff(ctx, config, session, root, fork, scope, signal) {
|
|
643
677
|
const git = gitIn(ctx, {
|
|
@@ -662,11 +696,12 @@ async function localDiff(ctx, config, session, root, fork, scope, signal) {
|
|
|
662
696
|
/* v8 ignore next -- closed ForgeLocalScope union exhaustiveness guard */
|
|
663
697
|
default: return assertNever(scope, "ForgeLocalScope.kind");
|
|
664
698
|
}
|
|
699
|
+
const overrides = await diffFilterOverrides(git);
|
|
665
700
|
const tracked = parseDiff((await bounded(git("diff", [
|
|
666
701
|
...DIFF,
|
|
667
702
|
...range,
|
|
668
703
|
"--"
|
|
669
|
-
]))).stdout);
|
|
704
|
+
], void 0, overrides))).stdout);
|
|
670
705
|
if (scope.kind === "staged") return {
|
|
671
706
|
base: null,
|
|
672
707
|
files: tracked,
|
|
@@ -714,25 +749,22 @@ async function localText(ctx, config, session, root, scope, path, signal) {
|
|
|
714
749
|
* @param created - worktree the Session created, or null.
|
|
715
750
|
* @param signal - cancellation of every command and read.
|
|
716
751
|
* @returns the base and the changed files, or null without a fork point.
|
|
717
|
-
* @throws
|
|
752
|
+
* @throws FilterConfigError when filter configuration cannot be read within `maxOutputBytes`;
|
|
753
|
+
* DiffTooLarge when the untracked file list exceeds `maxDiffBytes`; an Error when Git fails.
|
|
718
754
|
*/
|
|
719
755
|
async function localChanges(ctx, config, session, inspection, created, signal) {
|
|
720
756
|
const root = inspection.checkout.root;
|
|
721
757
|
const git = gitIn(ctx, config, session, root, signal);
|
|
722
758
|
const fork = await forkPoint(git, inspection, created);
|
|
723
759
|
if (fork === null) return null;
|
|
760
|
+
const overrides = await diffFilterOverrides(git);
|
|
724
761
|
const numstat = await git("numstat", [
|
|
725
|
-
|
|
726
|
-
"core.quotePath=false",
|
|
727
|
-
"diff",
|
|
728
|
-
"--no-ext-diff",
|
|
729
|
-
"--no-textconv",
|
|
762
|
+
...DIFF,
|
|
730
763
|
"--numstat",
|
|
731
764
|
"-z",
|
|
732
|
-
"-M",
|
|
733
765
|
fork.commit,
|
|
734
766
|
"--"
|
|
735
|
-
]);
|
|
767
|
+
], void 0, overrides);
|
|
736
768
|
const untracked = await untrackedFiles(ctx, config, gitIn(ctx, {
|
|
737
769
|
...config,
|
|
738
770
|
maxOutputBytes: config.maxDiffBytes
|
|
@@ -2050,7 +2082,7 @@ var Reader = class {
|
|
|
2050
2082
|
var Run = class {
|
|
2051
2083
|
sources;
|
|
2052
2084
|
readers;
|
|
2053
|
-
|
|
2085
|
+
storage;
|
|
2054
2086
|
states = /* @__PURE__ */ new Map();
|
|
2055
2087
|
sessionsOf = /* @__PURE__ */ new Map();
|
|
2056
2088
|
urlsOf = /* @__PURE__ */ new Map();
|
|
@@ -2060,11 +2092,11 @@ var Run = class {
|
|
|
2060
2092
|
starting = true;
|
|
2061
2093
|
writes = /* @__PURE__ */ new Set();
|
|
2062
2094
|
stopped = false;
|
|
2063
|
-
constructor(sources, readers,
|
|
2095
|
+
constructor(sources, readers, storage) {
|
|
2064
2096
|
this.sources = sources;
|
|
2065
2097
|
this.readers = readers;
|
|
2066
|
-
this.
|
|
2067
|
-
for (const [, state] of table.entries()) this.states.set(state.url, state);
|
|
2098
|
+
this.storage = storage;
|
|
2099
|
+
for (const [, state] of storage.table.entries()) this.states.set(state.url, state);
|
|
2068
2100
|
this.stopBindings = sources.onBindings((id, urls) => {
|
|
2069
2101
|
if (this.starting) this.startupBindings.set(id, urls);
|
|
2070
2102
|
else this.bind(id, urls);
|
|
@@ -2085,7 +2117,7 @@ var Run = class {
|
|
|
2085
2117
|
for (const [url] of this.states) {
|
|
2086
2118
|
if (this.sessionsOf.has(url)) continue;
|
|
2087
2119
|
this.states.delete(url);
|
|
2088
|
-
this.write(() => this.table.delete(storedKey(url)));
|
|
2120
|
+
this.write(() => this.storage.table.delete(storedKey(url)));
|
|
2089
2121
|
}
|
|
2090
2122
|
}
|
|
2091
2123
|
/** The known states of every change request that some Session the reader may read binds. */
|
|
@@ -2102,7 +2134,11 @@ var Run = class {
|
|
|
2102
2134
|
this.stopBindings();
|
|
2103
2135
|
for (const stop of this.polls.values()) stop();
|
|
2104
2136
|
this.polls.clear();
|
|
2105
|
-
|
|
2137
|
+
try {
|
|
2138
|
+
await Promise.all(this.writes);
|
|
2139
|
+
} finally {
|
|
2140
|
+
await this.storage.close();
|
|
2141
|
+
}
|
|
2106
2142
|
}
|
|
2107
2143
|
bind(id, urls) {
|
|
2108
2144
|
const next = new Set(urls);
|
|
@@ -2143,7 +2179,7 @@ var Run = class {
|
|
|
2143
2179
|
settle(url, ids, state) {
|
|
2144
2180
|
if (this.stopped || sameState(this.states.get(url), state)) return;
|
|
2145
2181
|
this.states.set(url, state);
|
|
2146
|
-
this.write(() => this.table.put(storedKey(url), state));
|
|
2182
|
+
this.write(() => this.storage.table.put(storedKey(url), state));
|
|
2147
2183
|
for (const reader of this.readers) if ([...ids].some(reader.allowed)) reader.push(state);
|
|
2148
2184
|
}
|
|
2149
2185
|
write(operation) {
|
|
@@ -2195,7 +2231,7 @@ var ChangeRequestStates = class {
|
|
|
2195
2231
|
}
|
|
2196
2232
|
}
|
|
2197
2233
|
/**
|
|
2198
|
-
* End every stream, stop polling, and wait for initialization and
|
|
2234
|
+
* End every stream, stop polling, and wait for initialization, accepted writes, and storage closure.
|
|
2199
2235
|
* @returns settles after all owned work stops; subsequent streams end without starting work.
|
|
2200
2236
|
*/
|
|
2201
2237
|
async dispose() {
|
|
@@ -2210,14 +2246,16 @@ var ChangeRequestStates = class {
|
|
|
2210
2246
|
const started = (async () => {
|
|
2211
2247
|
await preceding;
|
|
2212
2248
|
aborter.signal.throwIfAborted();
|
|
2213
|
-
const
|
|
2214
|
-
|
|
2215
|
-
const run = new Run(this.sources, this.readers, table);
|
|
2249
|
+
const storage = await this.sources.open();
|
|
2250
|
+
let run;
|
|
2216
2251
|
try {
|
|
2252
|
+
aborter.signal.throwIfAborted();
|
|
2253
|
+
run = new Run(this.sources, this.readers, storage);
|
|
2217
2254
|
await run.start(aborter.signal);
|
|
2218
2255
|
return run;
|
|
2219
2256
|
} catch (error) {
|
|
2220
|
-
await
|
|
2257
|
+
if (run === void 0) await storage.close();
|
|
2258
|
+
else await run.stop();
|
|
2221
2259
|
throw error;
|
|
2222
2260
|
}
|
|
2223
2261
|
})();
|
|
@@ -2279,7 +2317,7 @@ var __esDecorate = function(ctor, descriptorIn, decorators, contextIn, initializ
|
|
|
2279
2317
|
};
|
|
2280
2318
|
/**
|
|
2281
2319
|
* Serves the forge panel. Following a Session inspects the checkout of its
|
|
2282
|
-
* current directory
|
|
2320
|
+
* current directory using the Session's permissions and execution world, recognizes the
|
|
2283
2321
|
* code host of the upstream remote or `origin`, probes sign-in for that host,
|
|
2284
2322
|
* and polls the Session's bound change requests through `ctx.forge` until the
|
|
2285
2323
|
* last follower of that Session leaves. A worktree request makes the panel
|
|
@@ -2303,8 +2341,10 @@ let ForgePanelController = (() => {
|
|
|
2303
2341
|
let _refresh_decorators;
|
|
2304
2342
|
let _localDiff_decorators;
|
|
2305
2343
|
let _localText_decorators;
|
|
2344
|
+
let _changeRequestChecks_decorators;
|
|
2306
2345
|
let _changeRequestDiff_decorators;
|
|
2307
2346
|
let _changeRequestText_decorators;
|
|
2347
|
+
let _submitReview_decorators;
|
|
2308
2348
|
let _markReady_decorators;
|
|
2309
2349
|
let _merge_decorators;
|
|
2310
2350
|
let _requestWorktree_decorators;
|
|
@@ -2324,8 +2364,10 @@ let ForgePanelController = (() => {
|
|
|
2324
2364
|
_refresh_decorators = [Remote];
|
|
2325
2365
|
_localDiff_decorators = [Remote];
|
|
2326
2366
|
_localText_decorators = [Remote];
|
|
2367
|
+
_changeRequestChecks_decorators = [Remote];
|
|
2327
2368
|
_changeRequestDiff_decorators = [Remote];
|
|
2328
2369
|
_changeRequestText_decorators = [Remote];
|
|
2370
|
+
_submitReview_decorators = [Remote];
|
|
2329
2371
|
_markReady_decorators = [Remote];
|
|
2330
2372
|
_merge_decorators = [Remote];
|
|
2331
2373
|
_requestWorktree_decorators = [Remote];
|
|
@@ -2422,6 +2464,17 @@ let ForgePanelController = (() => {
|
|
|
2422
2464
|
},
|
|
2423
2465
|
metadata: _metadata
|
|
2424
2466
|
}, null, _instanceExtraInitializers);
|
|
2467
|
+
__esDecorate(this, null, _changeRequestChecks_decorators, {
|
|
2468
|
+
kind: "method",
|
|
2469
|
+
name: "changeRequestChecks",
|
|
2470
|
+
static: false,
|
|
2471
|
+
private: false,
|
|
2472
|
+
access: {
|
|
2473
|
+
has: (obj) => "changeRequestChecks" in obj,
|
|
2474
|
+
get: (obj) => obj.changeRequestChecks
|
|
2475
|
+
},
|
|
2476
|
+
metadata: _metadata
|
|
2477
|
+
}, null, _instanceExtraInitializers);
|
|
2425
2478
|
__esDecorate(this, null, _changeRequestDiff_decorators, {
|
|
2426
2479
|
kind: "method",
|
|
2427
2480
|
name: "changeRequestDiff",
|
|
@@ -2444,6 +2497,17 @@ let ForgePanelController = (() => {
|
|
|
2444
2497
|
},
|
|
2445
2498
|
metadata: _metadata
|
|
2446
2499
|
}, null, _instanceExtraInitializers);
|
|
2500
|
+
__esDecorate(this, null, _submitReview_decorators, {
|
|
2501
|
+
kind: "method",
|
|
2502
|
+
name: "submitReview",
|
|
2503
|
+
static: false,
|
|
2504
|
+
private: false,
|
|
2505
|
+
access: {
|
|
2506
|
+
has: (obj) => "submitReview" in obj,
|
|
2507
|
+
get: (obj) => obj.submitReview
|
|
2508
|
+
},
|
|
2509
|
+
metadata: _metadata
|
|
2510
|
+
}, null, _instanceExtraInitializers);
|
|
2447
2511
|
__esDecorate(this, null, _markReady_decorators, {
|
|
2448
2512
|
kind: "method",
|
|
2449
2513
|
name: "markReady",
|
|
@@ -2712,13 +2776,14 @@ let ForgePanelController = (() => {
|
|
|
2712
2776
|
}
|
|
2713
2777
|
/**
|
|
2714
2778
|
* Read the changes one scope covers in the checkout of the Session's
|
|
2715
|
-
* current directory, with Git
|
|
2779
|
+
* current directory, with Git using the Session's sandbox policy.
|
|
2716
2780
|
* @param session - Session whose checkout is read.
|
|
2717
2781
|
* @param scope - which changes to read.
|
|
2718
2782
|
* @param signal - request cancellation.
|
|
2719
2783
|
* @returns the files, the base of the `branch` scope, and the untracked files left out.
|
|
2720
2784
|
* @throws `forgePanel/not-a-checkout` outside a checkout; `forgePanel/no-fork-point` for the `branch`
|
|
2721
|
-
* scope of a checkout without one; `forgePanel/
|
|
2785
|
+
* scope of a checkout without one; `forgePanel/filter-config-failed` when filter configuration cannot be read
|
|
2786
|
+
* completely, before any comparison; `forgePanel/diff-too-large` over `maxDiffBytes`; the Git error when Git fails.
|
|
2722
2787
|
*/
|
|
2723
2788
|
async localDiff(session, scope, signal) {
|
|
2724
2789
|
signal.throwIfAborted();
|
|
@@ -2744,6 +2809,24 @@ let ForgePanelController = (() => {
|
|
|
2744
2809
|
return await diffing(localText(this.home, this.config, session, inspection.checkout.root, scope, path, signal));
|
|
2745
2810
|
}
|
|
2746
2811
|
/**
|
|
2812
|
+
* Load complete checks for the change request whose list the user opened.
|
|
2813
|
+
* @param session - Session retaining the visible binding.
|
|
2814
|
+
* @param url - binding URL.
|
|
2815
|
+
* @param headSha - commit displayed by the card; a different head rejects the result.
|
|
2816
|
+
* @param signal - cancellation when the list closes.
|
|
2817
|
+
* @returns the complete snapshot at the displayed head.
|
|
2818
|
+
* @throws `forgePanel/not-bound` for an invisible binding, `forgePanel/stale-head` for a moved head,
|
|
2819
|
+
* or `forgePanel/checks-failed` when the provider cannot complete the read.
|
|
2820
|
+
*/
|
|
2821
|
+
async changeRequestChecks(session, url, headSha, signal) {
|
|
2822
|
+
signal.throwIfAborted();
|
|
2823
|
+
const result = await this.home.forge.fetchChecks(this.bound(session, url).ref, signal);
|
|
2824
|
+
this.bound(session, url);
|
|
2825
|
+
if ("code" in result) throw new RemoteError("forgePanel/checks-failed", result.message, {});
|
|
2826
|
+
if (result.headSha !== headSha) throw new RemoteError("forgePanel/stale-head", "The change request head changed; refresh its status", {});
|
|
2827
|
+
return result;
|
|
2828
|
+
}
|
|
2829
|
+
/**
|
|
2747
2830
|
* Read the changes of a change request of a Session as its code host
|
|
2748
2831
|
* computes them, at the head commit of its latest status. A patch already
|
|
2749
2832
|
* read at the same head commit and base branch is reused from memory, for
|
|
@@ -2804,6 +2887,23 @@ let ForgePanelController = (() => {
|
|
|
2804
2887
|
return text;
|
|
2805
2888
|
}
|
|
2806
2889
|
/**
|
|
2890
|
+
* Publish a line comment or completed review for a visible Session binding.
|
|
2891
|
+
* @param session - Session retaining the change request.
|
|
2892
|
+
* @param url - visible binding URL.
|
|
2893
|
+
* @param request - reviewed head, ranges, plain text, and explicit review decision.
|
|
2894
|
+
* @param signal - request cancellation.
|
|
2895
|
+
* @returns confirmed publication or a typed failure; unknown outcomes require reconciliation on the host.
|
|
2896
|
+
*/
|
|
2897
|
+
async submitReview(session, url, request, signal) {
|
|
2898
|
+
signal.throwIfAborted();
|
|
2899
|
+
const { ref } = this.bound(session, url);
|
|
2900
|
+
if (!/^(?:[a-f0-9]{40}|[a-f0-9]{64})$/i.test(request.headSha)) throw new Error("Invalid review commit");
|
|
2901
|
+
const comments = request.kind === "comment" ? [request.comment] : request.comments;
|
|
2902
|
+
if (comments.some((comment) => comment.path === "" || comment.body.trim() === "" || !Number.isSafeInteger(comment.startLine) || comment.startLine < 1 || !Number.isSafeInteger(comment.endLine) || comment.endLine < comment.startLine)) throw new Error("Invalid review comment");
|
|
2903
|
+
if (request.kind === "review" && request.event !== "approve" && request.body.trim() === "" && comments.length === 0) throw new Error("Empty review");
|
|
2904
|
+
return await this.home.forge.submitReview(ref, request, signal);
|
|
2905
|
+
}
|
|
2906
|
+
/**
|
|
2807
2907
|
* Mark a draft change request of a Session ready for review; the change
|
|
2808
2908
|
* request is polled again once the host answers.
|
|
2809
2909
|
* @param session - Session the change request is bound to.
|
|
@@ -3061,7 +3161,13 @@ let ForgePanelController = (() => {
|
|
|
3061
3161
|
}),
|
|
3062
3162
|
refOf: (url) => ctx.forge.changeRequestFromUrl(url),
|
|
3063
3163
|
watch: (ref, listener) => ctx.forge.watch(ref, listener),
|
|
3064
|
-
open: async () =>
|
|
3164
|
+
open: async () => {
|
|
3165
|
+
const domain = await storage.open(changeRequestStatesDomain);
|
|
3166
|
+
return {
|
|
3167
|
+
table: domain.table("states"),
|
|
3168
|
+
close: () => domain.close()
|
|
3169
|
+
};
|
|
3170
|
+
},
|
|
3065
3171
|
report: (error) => {
|
|
3066
3172
|
ctx.logger.warn(`forge-panel: storing a change request state failed: ${String(error)}`);
|
|
3067
3173
|
}
|
|
@@ -3100,6 +3206,7 @@ async function diffing(reading) {
|
|
|
3100
3206
|
try {
|
|
3101
3207
|
return await reading;
|
|
3102
3208
|
} catch (error) {
|
|
3209
|
+
if (error instanceof FilterConfigError) throw new RemoteError("forgePanel/filter-config-failed", error.message, {}, { cause: error });
|
|
3103
3210
|
if (error instanceof DiffTooLarge) throw new RemoteError("forgePanel/diff-too-large", error.message, {}, { cause: error });
|
|
3104
3211
|
if (error instanceof NoForkPoint) throw new RemoteError("forgePanel/no-fork-point", error.message, {}, { cause: error });
|
|
3105
3212
|
throw error;
|