Open History to see the last commit
1import {
2 type ReviewDiffPreviewInput,
3 type ReviewDiffPreviewResult,
4 VcsUnsupportedOperationError,
5 WS_METHODS,
6} from "@t3tools/contracts";
7import * as DateTime from "effect/DateTime";
8import * as Effect from "effect/Effect";
9import * as Option from "effect/Option";
10import * as Semaphore from "effect/Semaphore";
11import { request } from "../rpc/client.ts";
12import { AsyncResult, Atom } from "effect/reactivity";
13
14import {
15 createAtomCommandScheduler,
16 createEnvironmentRpcCommand,
17 createEnvironmentQueryAtomFamily,
18 createEnvironmentRpcQueryAtomFamily,
19 type QueryAnswerHandler,
20} from "./runtime.ts";
21import type { EnvironmentRegistry } from "../connection/registry.ts";
22
23export function createReviewEnvironmentAtoms<R, E>(
24 runtime: Atom.AtomRuntime<EnvironmentRegistry | R, E>,
25 options: {
26 readonly onDiffPreviewAnswer?: QueryAnswerHandler<
27 ReviewDiffPreviewInput,
28 ReviewDiffPreviewResult
29 >;
30 } = {},
31) {
32 const patchReads = Semaphore.makeUnsafe(4);
33 const diffFileScheduler = createAtomCommandScheduler();
34 const diffPreview = createEnvironmentRpcQueryAtomFamily(runtime, {
35 label: "environment-data:review:diff-preview",
36 tag: WS_METHODS.reviewGetDiffPreview,
37 staleTimeMs: 5_000,
38 execute: Effect.fn("review.diffPreview")(function* (
39 input: ReviewDiffPreviewInput,
40 emit: (value: ReviewDiffPreviewResult) => Effect.Effect<void>,
41 previous: Option.Option<ReviewDiffPreviewResult>,
42 ) {
43 const hasCompletePreview = Option.exists(previous, (preview) => !preview.incomplete);
44 if (
45 !hasCompletePreview &&
46 input.sourceKind &&
47 !input.file &&
48 !input.commitSha &&
49 !input.trackedOnly
50 ) {
51 const tracked = yield* request(WS_METHODS.reviewGetDiffPreview, {
52 ...input,
53 trackedOnly: true,
54 });
55 // Older servers ignore trackedOnly and already return the complete preview.
56 if (!tracked.incomplete) return tracked;
57 yield* emit(tracked);
58 }
59 return yield* request(WS_METHODS.reviewGetDiffPreview, input);
60 }),
61 ...(options.onDiffPreviewAnswer === undefined ? {} : { onAnswer: options.onDiffPreviewAnswer }),
62 });
63 // A file patch is a slice of the comparison its preview generated, so a newer preview
64 // revision makes every patch read under the old one stale.
65 const previewRevisionFamily = Atom.family((key: string) => {
66 const target = JSON.parse(key) as Parameters<typeof diffPreview>[0];
67 return Atom.make((get) => {
68 // A failed refresh keeps the last revision, so an error does not refresh every patch.
69 // Reading the preview here keeps its query mounted while any of its patches is read.
70 return Option.match(AsyncResult.value(get(diffPreview(target))), {
71 onNone: () => null,
72 onSome: (preview) => DateTime.formatIso(preview.generatedAt),
73 });
74 });
75 });
76 return {
77 branchCommits: createEnvironmentRpcQueryAtomFamily(runtime, {
78 label: "environment-data:review:branch-commits",
79 tag: WS_METHODS.reviewGetBranchCommits,
80 staleTimeMs: 5_000,
81 }),
82 diffPreview,
83 diffFilePatch: createEnvironmentQueryAtomFamily(runtime, {
84 label: "environment-data:review:diff-file-patch",
85 staleTimeMs: 5 * 60_000,
86 refreshTrigger: ({ environmentId, input }) =>
87 input.preview === undefined
88 ? undefined
89 : previewRevisionFamily(JSON.stringify({ environmentId, input: input.preview })),
90 execute: (input: {
91 request: ReviewDiffPreviewInput & { file: NonNullable<ReviewDiffPreviewInput["file"]> };
92 cacheKey: string;
93 /** The preview this patch belongs to; its revisions refresh the patch. */
94 preview?: ReviewDiffPreviewInput;
95 }) =>
96 request(WS_METHODS.reviewGetDiffPreview, input.request).pipe(
97 patchReads.withPermit,
98 Effect.flatMap((result) => {
99 const source = result.sources.find(
100 (source) => source.kind === input.request.file.sourceKind,
101 );
102 return source
103 ? Effect.succeed(source)
104 : Effect.fail(
105 new VcsUnsupportedOperationError({
106 operation: "review.diffFilePatch",
107 kind: "git",
108 detail: "Diff no longer available. Refresh the comparison.",
109 }),
110 );
111 }),
112 ),
113 }),
114 diffFileContents: createEnvironmentRpcCommand(runtime, {
115 label: "environment-data:review:diff-file-contents",
116 tag: WS_METHODS.reviewGetDiffFileContents,
117 scheduler: diffFileScheduler,
118 concurrency: {
119 mode: "singleFlight",
120 key: ({ environmentId, input }) =>
121 JSON.stringify([
122 environmentId,
123 input.cwd,
124 input.sourceKind,
125 input.baseRef,
126 input.headRef,
127 input.oldPath,
128 input.newPath,
129 input.changeType,
130 ]),
131 },
132 }),
133 };
134}