@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/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
- /** Read-only Git inspection of a Session's current directory in the Session's execution world. */
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 a read-only confinement of the Session's
207
- * sandbox policy and reads refs and configuration only, never the working
208
- * tree. The top-level and shared Git directories Git prints are resolved
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`, under the same confinement as {@link inspectCheckout}.
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, under the same confinement as {@link inspectCheckout}.
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 confined Git runner to one Session and directory: every command
327
- * runs under a read-only confinement of the Session's sandbox policy with the
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
- ...ctx.sandboxPolicy.resolve({
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 program = await ctx.subprocess.resolveExecutable(config.gitCommand, void 0, timeout.signal);
374
- const confined = await ctx.sandbox.confine([
375
- program,
367
+ const command = [
368
+ await ctx.subprocess.resolveExecutable(config.gitCommand, void 0, timeout.signal),
376
369
  ...GIT_PREFIX,
377
370
  ...args
378
- ], policy, timeout.signal);
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: confined.argv,
379
+ argv,
382
380
  cwd,
383
- env: GIT_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 confined runner; untracked and working-tree files are read through
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
- /** Literal paths, no external diff driver or text conversion, rename detection, and fixed `a/` and `b/` prefixes. */
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 the changes one scope covers in the checkout at `root`.
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`; DiffTooLarge over `maxDiffBytes`; an Error when Git fails.
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 DiffTooLarge when the untracked file list exceeds `maxDiffBytes`; an Error when Git fails.
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
- "-c",
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
- table;
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, table) {
2095
+ constructor(sources, readers, storage) {
2064
2096
  this.sources = sources;
2065
2097
  this.readers = readers;
2066
- this.table = table;
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
- await Promise.all(this.writes);
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 accepted storage writes.
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 table = await this.sources.open();
2214
- aborter.signal.throwIfAborted();
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 run.stop();
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 read-only in the Session's execution world, recognizes the
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 confined to the Session's read-only sandbox.
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/diff-too-large` over `maxDiffBytes`; the Git error when Git fails.
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 () => (await storage.open(changeRequestStatesDomain)).table("states"),
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;