From f2818871309377847c3b5042741c5d9c14d45645 Mon Sep 17 00:00:00 2001 From: Julius Marminge <51714798+juliusmarminge@users.noreply.github.com> Date: Fri, 9 Oct 2026 12:09:21 -0700 Subject: [PATCH 1/4] refactor(source-control): add @t3tools/source-control-core Source control providers live in apps/server and reach into its services directly, so a provider cannot be packaged on its own. This adds the core package the provider packages will depend on: - SourceControlHost: the narrow port to the server (settings, process runner), implemented once by ServerSourceControlHost. - The SourceControlProvider and PullRequestProvider interfaces, discovery specs, the per-host rate limit, and the shared merge-message and checks helpers, moved unchanged. Discovery now runs CLIs through the host port instead of VcsProcess. Co-Authored-By: Claude Opus 5.5 --- apps/server/package.json | 1 + apps/server/scripts/evaluate-thread-titles.ts | 2 + apps/server/scripts/measure-pr-preview.ts | 2 +- apps/server/src/git/GitManager.test.ts | 2 +- .../PullRequestSyncReactor.test.ts | 2 +- .../PullRequestSyncReactor.ts | 2 +- .../PullRequestWatchReactor.ts | 2 +- .../src/orchestration-v2/runtimeLayer.test.ts | 2 +- .../pullRequest/AzureDevOpsPullRequestCli.ts | 2 +- .../AzureDevOpsPullRequestProvider.ts | 2 +- .../pullRequest/BitbucketPullRequestApi.ts | 2 +- .../BitbucketPullRequestProvider.ts | 2 +- .../pullRequest/ForgejoPullRequestProvider.ts | 2 +- .../pullRequest/GitHubPullRequestApi.test.ts | 2 +- .../src/pullRequest/GitHubPullRequestApi.ts | 9 ++- .../pullRequest/GitHubPullRequestProvider.ts | 2 +- .../src/pullRequest/GitLabPullRequestCli.ts | 2 +- .../pullRequest/GitLabPullRequestProvider.ts | 2 +- .../PullRequestProviderRateLimit.test.ts | 2 +- .../PullRequestProviderRegistry.ts | 2 +- .../pullRequest/PullRequestService.test.ts | 4 +- .../src/pullRequest/PullRequestService.ts | 4 +- .../pullRequest/bitbucketPullRequestJson.ts | 2 +- .../src/pullRequest/forgejoPullRequestJson.ts | 4 +- .../pullRequest/gitHubConditionalChecks.ts | 2 +- .../src/pullRequest/gitHubPullRequestJson.ts | 2 +- .../src/pullRequest/pullRequestViewedFiles.ts | 5 +- apps/server/src/server.ts | 4 +- .../src/sourceControl/AzureDevOpsCli.ts | 2 +- .../AzureDevOpsSourceControlProvider.ts | 4 +- apps/server/src/sourceControl/BitbucketApi.ts | 4 +- .../BitbucketSourceControlProvider.ts | 4 +- apps/server/src/sourceControl/ForgejoCli.ts | 2 +- .../ForgejoSourceControlProvider.ts | 4 +- .../src/sourceControl/GitHubApi.test.ts | 2 +- apps/server/src/sourceControl/GitHubApi.ts | 2 +- .../GitHubSourceControlProvider.ts | 6 +- apps/server/src/sourceControl/GitLabCli.ts | 2 +- .../GitLabSourceControlProvider.ts | 4 +- .../sourceControl/ServerSourceControlHost.ts | 24 ++++++++ .../SourceControlDiscovery.test.ts | 11 +++- .../sourceControl/SourceControlDiscovery.ts | 2 +- .../SourceControlProviderRegistry.test.ts | 6 ++ .../SourceControlProviderRegistry.ts | 9 +-- .../SourceControlRepositoryService.test.ts | 2 +- .../src/sourceControl/githubQuota.test.ts | 2 +- apps/server/src/sourceControl/githubQuota.ts | 2 +- apps/server/src/ws.ts | 2 + package.json | 2 +- packages/source-control-core/package.json | 48 +++++++++++++++ .../src/server}/PullRequestProvider.ts | 0 .../src/server/SourceControlHost.ts | 59 +++++++++++++++++++ .../src/server}/SourceControlProvider.test.ts | 0 .../src/server}/SourceControlProvider.ts | 2 +- .../server}/SourceControlRateLimit.test.ts | 0 .../src/server}/SourceControlRateLimit.ts | 11 ++-- .../src/server/discovery.ts | 15 +++-- .../src/server}/mergeMessage.test.ts | 0 .../src/server}/mergeMessage.ts | 0 .../src/server}/pullRequestChecks.test.ts | 0 .../src/server}/pullRequestChecks.ts | 0 packages/source-control-core/tsconfig.json | 7 +++ pnpm-lock.yaml | 44 ++++++++++++++ 63 files changed, 288 insertions(+), 69 deletions(-) create mode 100644 apps/server/src/sourceControl/ServerSourceControlHost.ts create mode 100644 packages/source-control-core/package.json rename {apps/server/src/pullRequest => packages/source-control-core/src/server}/PullRequestProvider.ts (100%) create mode 100644 packages/source-control-core/src/server/SourceControlHost.ts rename {apps/server/src/sourceControl => packages/source-control-core/src/server}/SourceControlProvider.test.ts (100%) rename {apps/server/src/sourceControl => packages/source-control-core/src/server}/SourceControlProvider.ts (98%) rename {apps/server/src/sourceControl => packages/source-control-core/src/server}/SourceControlRateLimit.test.ts (100%) rename {apps/server/src/sourceControl => packages/source-control-core/src/server}/SourceControlRateLimit.ts (95%) rename apps/server/src/sourceControl/SourceControlProviderDiscovery.ts => packages/source-control-core/src/server/discovery.ts (95%) rename {apps/server/src/pullRequest => packages/source-control-core/src/server}/mergeMessage.test.ts (100%) rename {apps/server/src/pullRequest => packages/source-control-core/src/server}/mergeMessage.ts (100%) rename {apps/server/src/pullRequest => packages/source-control-core/src/server}/pullRequestChecks.test.ts (100%) rename {apps/server/src/pullRequest => packages/source-control-core/src/server}/pullRequestChecks.ts (100%) create mode 100644 packages/source-control-core/tsconfig.json diff --git a/apps/server/package.json b/apps/server/package.json index bb166039abd9..ea7b9019e415 100644 --- a/apps/server/package.json +++ b/apps/server/package.json @@ -45,6 +45,7 @@ "@t3tools/provider-muse": "workspace:*", "@t3tools/provider-opencode": "workspace:*", "@t3tools/provider-pi": "workspace:*", + "@t3tools/source-control-core": "workspace:*", "diff": "8.0.3", "effect": "catalog:", "jose": "catalog:", diff --git a/apps/server/scripts/evaluate-thread-titles.ts b/apps/server/scripts/evaluate-thread-titles.ts index 0747825dcb5b..196bcbdfbcaa 100644 --- a/apps/server/scripts/evaluate-thread-titles.ts +++ b/apps/server/scripts/evaluate-thread-titles.ts @@ -23,6 +23,7 @@ import { } from "@t3tools/provider-core/server/threadTitleContext"; import * as ThreadTitleLinks from "../src/textGeneration/ThreadTitleLinks.ts"; import * as SourceControlProviderRegistry from "../src/sourceControl/SourceControlProviderRegistry.ts"; +import * as ServerSourceControlHost from "../src/sourceControl/ServerSourceControlHost.ts"; import * as GitHubApi from "../src/sourceControl/GitHubApi.ts"; import * as GitLabCli from "../src/sourceControl/GitLabCli.ts"; import * as ForgejoCli from "../src/sourceControl/ForgejoCli.ts"; @@ -158,6 +159,7 @@ await Effect.runPromise( AzureDevOpsCli.layer, // No saved credentials here; Bitbucket falls back to T3CODE_BITBUCKET_* variables. BitbucketApi.layer, + ServerSourceControlHost.layer, ), ), // Default settings: no saved Bitbucket token, gh's own GitHub account choice. diff --git a/apps/server/scripts/measure-pr-preview.ts b/apps/server/scripts/measure-pr-preview.ts index e8f0727ac93c..0f33ec9a6139 100644 --- a/apps/server/scripts/measure-pr-preview.ts +++ b/apps/server/scripts/measure-pr-preview.ts @@ -16,7 +16,7 @@ import * as GitHubApi from "../src/sourceControl/GitHubApi.ts"; import * as GitHubCredentials from "../src/sourceControl/GitHubCredentials.ts"; import * as ServerSettings from "../src/serverSettings.ts"; import * as GitHubQuota from "../src/sourceControl/githubQuota.ts"; -import * as SourceControlRateLimit from "../src/sourceControl/SourceControlRateLimit.ts"; +import * as SourceControlRateLimit from "@t3tools/source-control-core/server/SourceControlRateLimit"; import * as VcsProcess from "../src/vcs/VcsProcess.ts"; const [repository, ...numbers] = process.argv.slice(2); diff --git a/apps/server/src/git/GitManager.test.ts b/apps/server/src/git/GitManager.test.ts index 9f5f368d2cf2..c7e76f5e71ab 100644 --- a/apps/server/src/git/GitManager.test.ts +++ b/apps/server/src/git/GitManager.test.ts @@ -58,7 +58,7 @@ import { ForgejoPullRequestSchema, toForgejoChangeRequest, } from "../sourceControl/forgejoPullRequests.ts"; -import type { SourceControlProvider } from "../sourceControl/SourceControlProvider.ts"; +import type { SourceControlProvider } from "@t3tools/source-control-core/server/SourceControlProvider"; import * as SourceControlProviderRegistry from "../sourceControl/SourceControlProviderRegistry.ts"; import * as ServerConfig from "../config.ts"; import * as ProjectionStore from "../orchestration-v2/ProjectionStore.ts"; diff --git a/apps/server/src/orchestration-v2/PullRequestSyncReactor.test.ts b/apps/server/src/orchestration-v2/PullRequestSyncReactor.test.ts index 36f4bdb15891..19e67f949b61 100644 --- a/apps/server/src/orchestration-v2/PullRequestSyncReactor.test.ts +++ b/apps/server/src/orchestration-v2/PullRequestSyncReactor.test.ts @@ -31,7 +31,7 @@ import * as Queue from "effect/Queue"; import * as Ref from "effect/Ref"; import { TestClock } from "effect/testing"; -import { PullRequestProviderError } from "../pullRequest/PullRequestProvider.ts"; +import { PullRequestProviderError } from "@t3tools/source-control-core/server/PullRequestProvider"; import * as GitManager from "../git/GitManager.ts"; import * as PullRequestService from "../pullRequest/PullRequestService.ts"; import * as ServerActivation from "../serverActivation.ts"; diff --git a/apps/server/src/orchestration-v2/PullRequestSyncReactor.ts b/apps/server/src/orchestration-v2/PullRequestSyncReactor.ts index 5f1fcd8269ac..a966cbd95456 100644 --- a/apps/server/src/orchestration-v2/PullRequestSyncReactor.ts +++ b/apps/server/src/orchestration-v2/PullRequestSyncReactor.ts @@ -29,7 +29,7 @@ import * as Semaphore from "effect/Semaphore"; import * as Stream from "effect/Stream"; import * as GitManager from "../git/GitManager.ts"; -import { PullRequestProviderError } from "../pullRequest/PullRequestProvider.ts"; +import { PullRequestProviderError } from "@t3tools/source-control-core/server/PullRequestProvider"; import * as PullRequestService from "../pullRequest/PullRequestService.ts"; import { forkParked } from "../serverActivation.ts"; import * as Orchestrator from "./Orchestrator.ts"; diff --git a/apps/server/src/orchestration-v2/PullRequestWatchReactor.ts b/apps/server/src/orchestration-v2/PullRequestWatchReactor.ts index 7005534f89ed..5a5f61ae3c4d 100644 --- a/apps/server/src/orchestration-v2/PullRequestWatchReactor.ts +++ b/apps/server/src/orchestration-v2/PullRequestWatchReactor.ts @@ -29,7 +29,7 @@ import type * as Scope from "effect/Scope"; import { type ProviderChangeRequestWatchFingerprint, PullRequestProviderError, -} from "../pullRequest/PullRequestProvider.ts"; +} from "@t3tools/source-control-core/server/PullRequestProvider"; import * as PullRequestService from "../pullRequest/PullRequestService.ts"; import { forkParked } from "../serverActivation.ts"; import * as Orchestrator from "./Orchestrator.ts"; diff --git a/apps/server/src/orchestration-v2/runtimeLayer.test.ts b/apps/server/src/orchestration-v2/runtimeLayer.test.ts index b11184f44632..bb70c880b061 100644 --- a/apps/server/src/orchestration-v2/runtimeLayer.test.ts +++ b/apps/server/src/orchestration-v2/runtimeLayer.test.ts @@ -65,7 +65,7 @@ import * as ProviderRuntimeRecoveryService from "./ProviderRuntimeRecoveryServic import * as ProjectionMaintenance from "./ProjectionMaintenance.ts"; import * as ProjectionStore from "./ProjectionStore.ts"; import * as PullRequestWatchReactor from "./PullRequestWatchReactor.ts"; -import { PullRequestProviderError } from "../pullRequest/PullRequestProvider.ts"; +import { PullRequestProviderError } from "@t3tools/source-control-core/server/PullRequestProvider"; import * as PullRequestService from "../pullRequest/PullRequestService.ts"; import * as ProjectStore from "./ProjectStore.ts"; import * as ProviderSessionManager from "./ProviderSessionManager.ts"; diff --git a/apps/server/src/pullRequest/AzureDevOpsPullRequestCli.ts b/apps/server/src/pullRequest/AzureDevOpsPullRequestCli.ts index 5f5453a9b3ba..d00d08b7d05f 100644 --- a/apps/server/src/pullRequest/AzureDevOpsPullRequestCli.ts +++ b/apps/server/src/pullRequest/AzureDevOpsPullRequestCli.ts @@ -26,7 +26,7 @@ import { type AzureDevOpsPullRequest, type AzureDevOpsRepositoryLocation, } from "./azureDevOpsPullRequestJson.ts"; -import type { ProviderListCursor } from "./PullRequestProvider.ts"; +import type { ProviderListCursor } from "@t3tools/source-control-core/server/PullRequestProvider"; /** * Names the read that produced unusable output, so a failure reports the call it came from diff --git a/apps/server/src/pullRequest/AzureDevOpsPullRequestProvider.ts b/apps/server/src/pullRequest/AzureDevOpsPullRequestProvider.ts index 2cdf78c56a14..afd045636947 100644 --- a/apps/server/src/pullRequest/AzureDevOpsPullRequestProvider.ts +++ b/apps/server/src/pullRequest/AzureDevOpsPullRequestProvider.ts @@ -24,7 +24,7 @@ import { type ProviderChangeRequestSummary, type ProviderDiffSlice, type PullRequestProviderApi, -} from "./PullRequestProvider.ts"; +} from "@t3tools/source-control-core/server/PullRequestProvider"; import type { AzureDevOpsChangeEntry, AzureDevOpsItemContent, diff --git a/apps/server/src/pullRequest/BitbucketPullRequestApi.ts b/apps/server/src/pullRequest/BitbucketPullRequestApi.ts index c7d017071fc6..f5e108b05d45 100644 --- a/apps/server/src/pullRequest/BitbucketPullRequestApi.ts +++ b/apps/server/src/pullRequest/BitbucketPullRequestApi.ts @@ -39,7 +39,7 @@ import { type BitbucketPullRequest, type BitbucketRawComment, } from "./bitbucketPullRequestJson.ts"; -import type { ProviderListCursor } from "./PullRequestProvider.ts"; +import type { ProviderListCursor } from "@t3tools/source-control-core/server/PullRequestProvider"; /** * Names the read that produced unusable output, so a failure reports the call it came from diff --git a/apps/server/src/pullRequest/BitbucketPullRequestProvider.ts b/apps/server/src/pullRequest/BitbucketPullRequestProvider.ts index f553dfd13bc1..0abcb7d95e7e 100644 --- a/apps/server/src/pullRequest/BitbucketPullRequestProvider.ts +++ b/apps/server/src/pullRequest/BitbucketPullRequestProvider.ts @@ -9,7 +9,7 @@ import { type ProviderChangeRequestActivity, type ProviderChangeRequestDetail, type PullRequestProviderApi, -} from "./PullRequestProvider.ts"; +} from "@t3tools/source-control-core/server/PullRequestProvider"; import type { BitbucketPullRequest } from "./bitbucketPullRequestJson.ts"; const CAPABILITIES: PullRequestCapabilities = { diff --git a/apps/server/src/pullRequest/ForgejoPullRequestProvider.ts b/apps/server/src/pullRequest/ForgejoPullRequestProvider.ts index 12d931279a74..a9337fdbe8fd 100644 --- a/apps/server/src/pullRequest/ForgejoPullRequestProvider.ts +++ b/apps/server/src/pullRequest/ForgejoPullRequestProvider.ts @@ -10,7 +10,7 @@ import { type ProviderChangeRequestDetail, type ProviderRepositoryRef, type PullRequestProviderApi, -} from "./PullRequestProvider.ts"; +} from "@t3tools/source-control-core/server/PullRequestProvider"; import { ForgejoPullRequest, ForgejoRepository, diff --git a/apps/server/src/pullRequest/GitHubPullRequestApi.test.ts b/apps/server/src/pullRequest/GitHubPullRequestApi.test.ts index 294d053d6f05..8888f4c11f45 100644 --- a/apps/server/src/pullRequest/GitHubPullRequestApi.test.ts +++ b/apps/server/src/pullRequest/GitHubPullRequestApi.test.ts @@ -16,7 +16,7 @@ import { AllowGitHubReserve } from "../sourceControl/GitHubApi.ts"; import * as GitHubApi from "../sourceControl/GitHubApi.ts"; import * as GitHubCredentials from "../sourceControl/GitHubCredentials.ts"; import * as GitHubQuota from "../sourceControl/githubQuota.ts"; -import * as SourceControlRateLimit from "../sourceControl/SourceControlRateLimit.ts"; +import * as SourceControlRateLimit from "@t3tools/source-control-core/server/SourceControlRateLimit"; import * as VcsProcess from "../vcs/VcsProcess.ts"; import * as GitHubPullRequestApi from "./GitHubPullRequestApi.ts"; import { KnownWorkflowRuns } from "./gitHubConditionalChecks.ts"; diff --git a/apps/server/src/pullRequest/GitHubPullRequestApi.ts b/apps/server/src/pullRequest/GitHubPullRequestApi.ts index 5e3572915460..dd093df00b77 100644 --- a/apps/server/src/pullRequest/GitHubPullRequestApi.ts +++ b/apps/server/src/pullRequest/GitHubPullRequestApi.ts @@ -1,4 +1,4 @@ -import { removeAgentCredits } from "./mergeMessage.ts"; +import { removeAgentCredits } from "@t3tools/source-control-core/server/mergeMessage"; import { KnownWorkflowRuns, makeChecksRevalidator } from "./gitHubConditionalChecks.ts"; import { runGitHubStackAction, type GitHubStackActionError } from "./githubStackActions.ts"; import * as Cause from "effect/Cause"; @@ -45,7 +45,7 @@ import { import * as GitHubApi from "../sourceControl/GitHubApi.ts"; import { readGraphQlPages } from "../sourceControl/githubGraphQl.ts"; import * as VcsProcess from "../vcs/VcsProcess.ts"; -import * as SourceControlRateLimit from "../sourceControl/SourceControlRateLimit.ts"; +import * as SourceControlRateLimit from "@t3tools/source-control-core/server/SourceControlRateLimit"; import { ACTOR_AVATARS_GRAPHQL_QUERY, ADD_REACTION_GRAPHQL_MUTATION, @@ -128,7 +128,10 @@ import { type GitHubReviewThreadPage, type GitHubViewerAccess, } from "./gitHubPullRequestJson.ts"; -import type { ProviderChangeRequestSummary, ProviderListCursor } from "./PullRequestProvider.ts"; +import type { + ProviderChangeRequestSummary, + ProviderListCursor, +} from "@t3tools/source-control-core/server/PullRequestProvider"; /** * Names the read that produced unusable output, so a failure reports the call it came from diff --git a/apps/server/src/pullRequest/GitHubPullRequestProvider.ts b/apps/server/src/pullRequest/GitHubPullRequestProvider.ts index f2672dbe991f..67f871501ddb 100644 --- a/apps/server/src/pullRequest/GitHubPullRequestProvider.ts +++ b/apps/server/src/pullRequest/GitHubPullRequestProvider.ts @@ -16,7 +16,7 @@ import { type ProviderChangeRequestDetail, type PullRequestProviderApi, type ProviderRepositoryRef, -} from "./PullRequestProvider.ts"; +} from "@t3tools/source-control-core/server/PullRequestProvider"; import type { GitHubViewerAccess, GitHubWorkflowRunApproval } from "./gitHubPullRequestJson.ts"; const CAPABILITIES: PullRequestCapabilities = { diff --git a/apps/server/src/pullRequest/GitLabPullRequestCli.ts b/apps/server/src/pullRequest/GitLabPullRequestCli.ts index c3b607002497..933e68742187 100644 --- a/apps/server/src/pullRequest/GitLabPullRequestCli.ts +++ b/apps/server/src/pullRequest/GitLabPullRequestCli.ts @@ -44,7 +44,7 @@ import { type GitLabMergeRequestListItem, type GitLabProjectUsers, } from "./gitLabMergeRequestJson.ts"; -import type { ProviderListCursor } from "./PullRequestProvider.ts"; +import type { ProviderListCursor } from "@t3tools/source-control-core/server/PullRequestProvider"; /** * Names the read that produced unusable output, so a failure reports the call it came from diff --git a/apps/server/src/pullRequest/GitLabPullRequestProvider.ts b/apps/server/src/pullRequest/GitLabPullRequestProvider.ts index 45034b84787c..1b5b987cd5ac 100644 --- a/apps/server/src/pullRequest/GitLabPullRequestProvider.ts +++ b/apps/server/src/pullRequest/GitLabPullRequestProvider.ts @@ -12,7 +12,7 @@ import { type ProviderChangeRequestActivity, type ProviderChangeRequestDetail, type PullRequestProviderApi, -} from "./PullRequestProvider.ts"; +} from "@t3tools/source-control-core/server/PullRequestProvider"; const CAPABILITIES: PullRequestCapabilities = { diff: true, diff --git a/apps/server/src/pullRequest/PullRequestProviderRateLimit.test.ts b/apps/server/src/pullRequest/PullRequestProviderRateLimit.test.ts index 873cc32a2c7f..ca12da2b8979 100644 --- a/apps/server/src/pullRequest/PullRequestProviderRateLimit.test.ts +++ b/apps/server/src/pullRequest/PullRequestProviderRateLimit.test.ts @@ -3,7 +3,7 @@ import { assert, it } from "@effect/vitest"; import * as AzureDevOpsCli from "../sourceControl/AzureDevOpsCli.ts"; import * as BitbucketApi from "../sourceControl/BitbucketApi.ts"; import * as GitHubApi from "../sourceControl/GitHubApi.ts"; -import * as SourceControlRateLimit from "../sourceControl/SourceControlRateLimit.ts"; +import * as SourceControlRateLimit from "@t3tools/source-control-core/server/SourceControlRateLimit"; import * as GitLabCli from "../sourceControl/GitLabCli.ts"; import { azureDevOpsProviderFailure } from "./AzureDevOpsPullRequestProvider.ts"; import { bitbucketProviderFailure } from "./BitbucketPullRequestProvider.ts"; diff --git a/apps/server/src/pullRequest/PullRequestProviderRegistry.ts b/apps/server/src/pullRequest/PullRequestProviderRegistry.ts index 316cd1e7ae92..8e5a1d9cfd0b 100644 --- a/apps/server/src/pullRequest/PullRequestProviderRegistry.ts +++ b/apps/server/src/pullRequest/PullRequestProviderRegistry.ts @@ -17,7 +17,7 @@ import * as GitHubPullRequestApi from "./GitHubPullRequestApi.ts"; import * as GitHubPullRequestProvider from "./GitHubPullRequestProvider.ts"; import * as GitLabPullRequestCli from "./GitLabPullRequestCli.ts"; import * as GitLabPullRequestProvider from "./GitLabPullRequestProvider.ts"; -import type { PullRequestProviderApi } from "./PullRequestProvider.ts"; +import type { PullRequestProviderApi } from "@t3tools/source-control-core/server/PullRequestProvider"; export class PullRequestProviderRegistry extends Context.Service< PullRequestProviderRegistry, diff --git a/apps/server/src/pullRequest/PullRequestService.test.ts b/apps/server/src/pullRequest/PullRequestService.test.ts index f9015ecbd9ef..ef84de264ec6 100644 --- a/apps/server/src/pullRequest/PullRequestService.test.ts +++ b/apps/server/src/pullRequest/PullRequestService.test.ts @@ -29,14 +29,14 @@ import * as SqlitePersistence from "../persistence/Sqlite.ts"; import * as PullRequestFilesViewed from "../persistence/PullRequestFilesViewed.ts"; import * as RepositoryIdentityResolver from "../project/RepositoryIdentityResolver.ts"; import * as SourceControlProviderRegistry from "../sourceControl/SourceControlProviderRegistry.ts"; -import * as SourceControlRateLimit from "../sourceControl/SourceControlRateLimit.ts"; +import * as SourceControlRateLimit from "@t3tools/source-control-core/server/SourceControlRateLimit"; import * as ForgejoCli from "../sourceControl/ForgejoCli.ts"; import * as ForgejoPullRequestProvider from "./ForgejoPullRequestProvider.ts"; import { PullRequestProviderError, type ProviderChangeRequest, type PullRequestProviderApi, -} from "./PullRequestProvider.ts"; +} from "@t3tools/source-control-core/server/PullRequestProvider"; import * as PullRequestProviderRegistry from "./PullRequestProviderRegistry.ts"; import * as PullRequestService from "./PullRequestService.ts"; import * as PullRequestReadCache from "./PullRequestReadCache.ts"; diff --git a/apps/server/src/pullRequest/PullRequestService.ts b/apps/server/src/pullRequest/PullRequestService.ts index a44bd322adcb..fb14ef896cd5 100644 --- a/apps/server/src/pullRequest/PullRequestService.ts +++ b/apps/server/src/pullRequest/PullRequestService.ts @@ -83,14 +83,14 @@ import * as ServerSettings from "../serverSettings.ts"; import * as PullRequestFilesViewed from "../persistence/PullRequestFilesViewed.ts"; import * as RepositoryIdentityResolver from "../project/RepositoryIdentityResolver.ts"; import * as SourceControlProviderRegistry from "../sourceControl/SourceControlProviderRegistry.ts"; -import * as SourceControlRateLimit from "../sourceControl/SourceControlRateLimit.ts"; +import * as SourceControlRateLimit from "@t3tools/source-control-core/server/SourceControlRateLimit"; import { type ProviderChangeRequest, type ProviderListCursor, type ProviderChangeRequestWatchFingerprint, type PullRequestProviderApi, PullRequestProviderError, -} from "./PullRequestProvider.ts"; +} from "@t3tools/source-control-core/server/PullRequestProvider"; import * as PullRequestReadCache from "./PullRequestReadCache.ts"; import * as PullRequestProviderRegistry from "./PullRequestProviderRegistry.ts"; import * as ViewedFiles from "./pullRequestViewedFiles.ts"; diff --git a/apps/server/src/pullRequest/bitbucketPullRequestJson.ts b/apps/server/src/pullRequest/bitbucketPullRequestJson.ts index f0bc8a71a827..8f0b520ca089 100644 --- a/apps/server/src/pullRequest/bitbucketPullRequestJson.ts +++ b/apps/server/src/pullRequest/bitbucketPullRequestJson.ts @@ -18,7 +18,7 @@ import type { import { TrimmedNonEmptyString } from "@t3tools/contracts"; import { decodeJsonResult } from "@t3tools/shared/schemaJson"; -import { dedupeChecks } from "./pullRequestChecks.ts"; +import { dedupeChecks } from "@t3tools/source-control-core/server/pullRequestChecks"; /** * Bitbucket's enums are decoded as plain strings and normalized here, in the same tolerant diff --git a/apps/server/src/pullRequest/forgejoPullRequestJson.ts b/apps/server/src/pullRequest/forgejoPullRequestJson.ts index cade3b65d9c8..a937c00c2ef8 100644 --- a/apps/server/src/pullRequest/forgejoPullRequestJson.ts +++ b/apps/server/src/pullRequest/forgejoPullRequestJson.ts @@ -10,8 +10,8 @@ import type { PullRequestReactionContent, PullRequestReviewThread, } from "@t3tools/contracts"; -import type { ProviderChangeRequest } from "./PullRequestProvider.ts"; -import { dedupeChecks } from "./pullRequestChecks.ts"; +import type { ProviderChangeRequest } from "@t3tools/source-control-core/server/PullRequestProvider"; +import { dedupeChecks } from "@t3tools/source-control-core/server/pullRequestChecks"; export const ForgejoUser = Schema.Struct({ login: Schema.String, diff --git a/apps/server/src/pullRequest/gitHubConditionalChecks.ts b/apps/server/src/pullRequest/gitHubConditionalChecks.ts index 6975da251936..353089ebf0f9 100644 --- a/apps/server/src/pullRequest/gitHubConditionalChecks.ts +++ b/apps/server/src/pullRequest/gitHubConditionalChecks.ts @@ -10,7 +10,7 @@ import { PositiveInt, type PullRequestChecks } from "@t3tools/contracts"; import * as GitHubApi from "../sourceControl/GitHubApi.ts"; import type { GitHubPullRequestDetail } from "./gitHubPullRequestJson.ts"; import type { GitHubPullRequestApiError } from "./GitHubPullRequestApi.ts"; -import type { ProviderRepositoryRef } from "./PullRequestProvider.ts"; +import type { ProviderRepositoryRef } from "@t3tools/source-control-core/server/PullRequestProvider"; const HeadSchema = Schema.Struct({ head: Schema.Struct({ diff --git a/apps/server/src/pullRequest/gitHubPullRequestJson.ts b/apps/server/src/pullRequest/gitHubPullRequestJson.ts index 028231c513f2..edca11f5a0d0 100644 --- a/apps/server/src/pullRequest/gitHubPullRequestJson.ts +++ b/apps/server/src/pullRequest/gitHubPullRequestJson.ts @@ -36,7 +36,7 @@ import { quoteGitPatchPath } from "@t3tools/shared/gitPatchPath"; import { decodeJsonResult } from "@t3tools/shared/schemaJson"; import { aliasedGraphQlDocument, type GraphQlDocument } from "../sourceControl/githubGraphQl.ts"; -import { dedupeChecks } from "./pullRequestChecks.ts"; +import { dedupeChecks } from "@t3tools/source-control-core/server/pullRequestChecks"; /** * Enum-ish GitHub CLI fields are decoded as plain strings and normalized here: a `gh` diff --git a/apps/server/src/pullRequest/pullRequestViewedFiles.ts b/apps/server/src/pullRequest/pullRequestViewedFiles.ts index 1f9aa33ac796..9f64c8352af5 100644 --- a/apps/server/src/pullRequest/pullRequestViewedFiles.ts +++ b/apps/server/src/pullRequest/pullRequestViewedFiles.ts @@ -16,7 +16,10 @@ import { } from "@t3tools/contracts"; import type * as PullRequestFilesViewed from "../persistence/PullRequestFilesViewed.ts"; -import type { ProviderFileRevisions, PullRequestProviderError } from "./PullRequestProvider.ts"; +import type { + ProviderFileRevisions, + PullRequestProviderError, +} from "@t3tools/source-control-core/server/PullRequestProvider"; import type { PullRequestError, SupportedProject } from "./PullRequestService.ts"; /** diff --git a/apps/server/src/server.ts b/apps/server/src/server.ts index 1bbe647dcd0c..913f8067d250 100644 --- a/apps/server/src/server.ts +++ b/apps/server/src/server.ts @@ -104,8 +104,9 @@ import * as ProjectCloneTracker from "./project/ProjectCloneTracker.ts"; import * as GitWorkflowService from "./git/GitWorkflowService.ts"; import * as ReviewService from "./review/ReviewService.ts"; import * as SourceControlProviderRegistry from "./sourceControl/SourceControlProviderRegistry.ts"; +import * as ServerSourceControlHost from "./sourceControl/ServerSourceControlHost.ts"; import * as PullRequestReadCache from "./pullRequest/PullRequestReadCache.ts"; -import * as SourceControlRateLimit from "./sourceControl/SourceControlRateLimit.ts"; +import * as SourceControlRateLimit from "@t3tools/source-control-core/server/SourceControlRateLimit"; import * as SourceControlRepositoryService from "./sourceControl/SourceControlRepositoryService.ts"; import * as WorktreeSetupTracker from "./project/WorktreeSetupTracker.ts"; import * as Observability from "./observability/Observability.ts"; @@ -305,6 +306,7 @@ const layerSourceControlProviderRegistry = SourceControlProviderRegistry.layer.p GitHubApi.layerWithDependencies, GitLabCli.layer, ForgejoCli.layer, + ServerSourceControlHost.layer, ), ), Layer.provideMerge(GitVcsDriver.layer), diff --git a/apps/server/src/sourceControl/AzureDevOpsCli.ts b/apps/server/src/sourceControl/AzureDevOpsCli.ts index f5e436ef6c56..7fd44de3c40b 100644 --- a/apps/server/src/sourceControl/AzureDevOpsCli.ts +++ b/apps/server/src/sourceControl/AzureDevOpsCli.ts @@ -17,7 +17,7 @@ import { decodeAzureDevOpsPullRequestListJson, type NormalizedAzureDevOpsPullRequestRecord, } from "./azureDevOpsPullRequests.ts"; -import * as SourceControlProvider from "./SourceControlProvider.ts"; +import * as SourceControlProvider from "@t3tools/source-control-core/server/SourceControlProvider"; const DEFAULT_TIMEOUT_MS = 30_000; diff --git a/apps/server/src/sourceControl/AzureDevOpsSourceControlProvider.ts b/apps/server/src/sourceControl/AzureDevOpsSourceControlProvider.ts index c2f6a5efc65a..dcb07e0632f3 100644 --- a/apps/server/src/sourceControl/AzureDevOpsSourceControlProvider.ts +++ b/apps/server/src/sourceControl/AzureDevOpsSourceControlProvider.ts @@ -2,14 +2,14 @@ import * as Effect from "effect/Effect"; import { SourceControlProviderError, type ChangeRequest } from "@t3tools/contracts"; import * as AzureDevOpsCli from "./AzureDevOpsCli.ts"; -import * as SourceControlProvider from "./SourceControlProvider.ts"; +import * as SourceControlProvider from "@t3tools/source-control-core/server/SourceControlProvider"; import { combinedAuthOutput, firstSafeAuthLine, providerAuth, type SourceControlAuthProbeInput, type SourceControlCliDiscoverySpec, -} from "./SourceControlProviderDiscovery.ts"; +} from "@t3tools/source-control-core/server/discovery"; function parseAzureAuth(input: SourceControlAuthProbeInput) { const account = input.stdout.trim().split(/\r?\n/)[0]?.trim(); diff --git a/apps/server/src/sourceControl/BitbucketApi.ts b/apps/server/src/sourceControl/BitbucketApi.ts index fbff83dfedb5..f7fa5cc4568c 100644 --- a/apps/server/src/sourceControl/BitbucketApi.ts +++ b/apps/server/src/sourceControl/BitbucketApi.ts @@ -29,11 +29,11 @@ import { type NormalizedBitbucketPullRequestRecord, } from "./bitbucketPullRequests.ts"; import { collectUint8StreamText } from "@t3tools/provider-core/server/collectStreamText"; -import * as SourceControlProvider from "./SourceControlProvider.ts"; +import * as SourceControlProvider from "@t3tools/source-control-core/server/SourceControlProvider"; import * as ServerSettings from "../serverSettings.ts"; import * as GitVcsDriver from "../vcs/GitVcsDriver.ts"; import * as VcsDriverRegistry from "../vcs/VcsDriverRegistry.ts"; -import { retryAtFromHeader } from "./SourceControlRateLimit.ts"; +import { retryAtFromHeader } from "@t3tools/source-control-core/server/SourceControlRateLimit"; const DEFAULT_API_BASE_URL = "https://api.bitbucket.org/2.0"; /** A response body past this is cut short, so one huge diff cannot exhaust the server. */ diff --git a/apps/server/src/sourceControl/BitbucketSourceControlProvider.ts b/apps/server/src/sourceControl/BitbucketSourceControlProvider.ts index f1a01056fbd0..b9afd60bc389 100644 --- a/apps/server/src/sourceControl/BitbucketSourceControlProvider.ts +++ b/apps/server/src/sourceControl/BitbucketSourceControlProvider.ts @@ -4,8 +4,8 @@ import { SourceControlProviderError, type ChangeRequest } from "@t3tools/contrac import * as BitbucketApi from "./BitbucketApi.ts"; import type { NormalizedBitbucketPullRequestRecord } from "./bitbucketPullRequests.ts"; -import * as SourceControlProvider from "./SourceControlProvider.ts"; -import type { SourceControlApiDiscoverySpec } from "./SourceControlProviderDiscovery.ts"; +import * as SourceControlProvider from "@t3tools/source-control-core/server/SourceControlProvider"; +import type { SourceControlApiDiscoverySpec } from "@t3tools/source-control-core/server/discovery"; function toChangeRequest(summary: NormalizedBitbucketPullRequestRecord): ChangeRequest { return { diff --git a/apps/server/src/sourceControl/ForgejoCli.ts b/apps/server/src/sourceControl/ForgejoCli.ts index eb284af237c8..573c081e5a42 100644 --- a/apps/server/src/sourceControl/ForgejoCli.ts +++ b/apps/server/src/sourceControl/ForgejoCli.ts @@ -15,7 +15,7 @@ import { decodeJsonResult } from "@t3tools/shared/schemaJson"; import { HostProcessPlatform } from "@t3tools/shared/hostProcess"; import * as VcsProcess from "../vcs/VcsProcess.ts"; import { collectUint8StreamText } from "@t3tools/provider-core/server/collectStreamText"; -import type { SourceControlProviderContext } from "./SourceControlProvider.ts"; +import type { SourceControlProviderContext } from "@t3tools/source-control-core/server/SourceControlProvider"; const encodeApiBody = Schema.encodeEffect(Schema.fromJsonString(Schema.Unknown)); diff --git a/apps/server/src/sourceControl/ForgejoSourceControlProvider.ts b/apps/server/src/sourceControl/ForgejoSourceControlProvider.ts index 5a501d96b235..79a1ca6f2aa0 100644 --- a/apps/server/src/sourceControl/ForgejoSourceControlProvider.ts +++ b/apps/server/src/sourceControl/ForgejoSourceControlProvider.ts @@ -5,13 +5,13 @@ import * as Result from "effect/Result"; import { SourceControlProviderError } from "@t3tools/contracts"; import * as VcsProcess from "../vcs/VcsProcess.ts"; import * as ForgejoCli from "./ForgejoCli.ts"; -import * as SourceControlProvider from "./SourceControlProvider.ts"; +import * as SourceControlProvider from "@t3tools/source-control-core/server/SourceControlProvider"; import { providerAuth, probeSourceControlProvider, type SourceControlCliDiscoverySpec, type SourceControlManagedCliDiscoverySpec, -} from "./SourceControlProviderDiscovery.ts"; +} from "@t3tools/source-control-core/server/discovery"; import { ForgejoPullRequestSchema, toForgejoChangeRequest } from "./forgejoPullRequests.ts"; const isForgejoCliError = Schema.is(ForgejoCli.ForgejoCliError); diff --git a/apps/server/src/sourceControl/GitHubApi.test.ts b/apps/server/src/sourceControl/GitHubApi.test.ts index 60e2c6f1b45d..cd4c2fb384f2 100644 --- a/apps/server/src/sourceControl/GitHubApi.test.ts +++ b/apps/server/src/sourceControl/GitHubApi.test.ts @@ -13,7 +13,7 @@ import { HttpClient, HttpClientResponse, type HttpClientRequest } from "effect/h import * as GitHubApi from "./GitHubApi.ts"; import * as GitHubCredentials from "./GitHubCredentials.ts"; import * as GitHubQuota from "./githubQuota.ts"; -import * as SourceControlRateLimit from "./SourceControlRateLimit.ts"; +import * as SourceControlRateLimit from "@t3tools/source-control-core/server/SourceControlRateLimit"; import * as VcsProcess from "../vcs/VcsProcess.ts"; import * as ServerSettings from "../serverSettings.ts"; diff --git a/apps/server/src/sourceControl/GitHubApi.ts b/apps/server/src/sourceControl/GitHubApi.ts index 5a99cf68943b..1cc66a1323c7 100644 --- a/apps/server/src/sourceControl/GitHubApi.ts +++ b/apps/server/src/sourceControl/GitHubApi.ts @@ -13,7 +13,7 @@ import { HttpClient, HttpClientRequest, type HttpClientResponse } from "effect/h import { collectUint8StreamText } from "@t3tools/provider-core/server/collectStreamText"; import * as GitHubCredentials from "./GitHubCredentials.ts"; import * as GitHubQuota from "./githubQuota.ts"; -import * as SourceControlRateLimit from "./SourceControlRateLimit.ts"; +import * as SourceControlRateLimit from "@t3tools/source-control-core/server/SourceControlRateLimit"; const DEFAULT_TIMEOUT = Duration.seconds(30); const DEFAULT_MAX_RESPONSE_BYTES = 8 * 1024 * 1024; diff --git a/apps/server/src/sourceControl/GitHubSourceControlProvider.ts b/apps/server/src/sourceControl/GitHubSourceControlProvider.ts index da3d53330864..35876794048a 100644 --- a/apps/server/src/sourceControl/GitHubSourceControlProvider.ts +++ b/apps/server/src/sourceControl/GitHubSourceControlProvider.ts @@ -37,14 +37,14 @@ import { resolveGitHubRepository, type GitHubRepositoryLocator, } from "./gitHubRepositoryResolution.ts"; -import * as SourceControlRateLimit from "./SourceControlRateLimit.ts"; +import * as SourceControlRateLimit from "@t3tools/source-control-core/server/SourceControlRateLimit"; import { effectiveGitHubAccount, findAuthenticatedGitHubAccount, parseGitHubAuthStatus, type GitHubAuthStatusAccount, } from "./gitHubAuthStatus.ts"; -import * as SourceControlProvider from "./SourceControlProvider.ts"; +import * as SourceControlProvider from "@t3tools/source-control-core/server/SourceControlProvider"; import { combinedAuthOutput, firstSafeAuthLine, @@ -53,7 +53,7 @@ import { type SourceControlAuthProbeInput, type SourceControlCliDiscoverySpec, type SourceControlManagedCliDiscoverySpec, -} from "./SourceControlProviderDiscovery.ts"; +} from "@t3tools/source-control-core/server/discovery"; import * as VcsProcess from "../vcs/VcsProcess.ts"; const decodeLinkSubject = Schema.decodeUnknownEffect( diff --git a/apps/server/src/sourceControl/GitLabCli.ts b/apps/server/src/sourceControl/GitLabCli.ts index 9d03e1ab8aa5..2ec69a527fed 100644 --- a/apps/server/src/sourceControl/GitLabCli.ts +++ b/apps/server/src/sourceControl/GitLabCli.ts @@ -18,7 +18,7 @@ import { decodeGitLabMergeRequestJson, decodeGitLabMergeRequestListJson, } from "./gitLabMergeRequests.ts"; -import type * as SourceControlProvider from "./SourceControlProvider.ts"; +import type * as SourceControlProvider from "@t3tools/source-control-core/server/SourceControlProvider"; const DEFAULT_TIMEOUT_MS = 30_000; diff --git a/apps/server/src/sourceControl/GitLabSourceControlProvider.ts b/apps/server/src/sourceControl/GitLabSourceControlProvider.ts index 00753bef7dea..ef63f11cfdf2 100644 --- a/apps/server/src/sourceControl/GitLabSourceControlProvider.ts +++ b/apps/server/src/sourceControl/GitLabSourceControlProvider.ts @@ -4,7 +4,7 @@ import * as Option from "effect/Option"; import { SourceControlProviderError, type ChangeRequest } from "@t3tools/contracts"; import * as GitLabCli from "./GitLabCli.ts"; -import * as SourceControlProvider from "./SourceControlProvider.ts"; +import * as SourceControlProvider from "@t3tools/source-control-core/server/SourceControlProvider"; import { combinedAuthOutput, firstSafeAuthLine, @@ -14,7 +14,7 @@ import { type SourceControlAuthProbeInput, type SourceControlCliDiscoverySpec, type SourceControlUnknownRemoteRefinementInput, -} from "./SourceControlProviderDiscovery.ts"; +} from "@t3tools/source-control-core/server/discovery"; import { findAuthenticatedGitLabHost, parseGitLabAuthStatusHosts } from "./gitLabAuthStatus.ts"; const decodeLinkSubject = Schema.decodeUnknownEffect( diff --git a/apps/server/src/sourceControl/ServerSourceControlHost.ts b/apps/server/src/sourceControl/ServerSourceControlHost.ts new file mode 100644 index 000000000000..f458795ab76e --- /dev/null +++ b/apps/server/src/sourceControl/ServerSourceControlHost.ts @@ -0,0 +1,24 @@ +/** + * The server's implementation of `SourceControlHost.SourceControlHost`, the only server surface + * source control provider packages may use. + * + * @module sourceControl/ServerSourceControlHost + */ +import * as SourceControlHost from "@t3tools/source-control-core/server/SourceControlHost"; +import * as Effect from "effect/Effect"; +import * as Layer from "effect/Layer"; + +import * as ServerSettings from "../serverSettings.ts"; +import * as VcsProcess from "../vcs/VcsProcess.ts"; + +export const layer = Layer.effect( + SourceControlHost.SourceControlHost, + Effect.gen(function* () { + const serverSettings = yield* ServerSettings.ServerSettingsService; + const process = yield* VcsProcess.VcsProcess; + return SourceControlHost.SourceControlHost.of({ + settings: { get: serverSettings.getSettings }, + process: { run: process.run }, + }); + }), +); diff --git a/apps/server/src/sourceControl/SourceControlDiscovery.test.ts b/apps/server/src/sourceControl/SourceControlDiscovery.test.ts index 6101d148a632..e0d1c3efca23 100644 --- a/apps/server/src/sourceControl/SourceControlDiscovery.test.ts +++ b/apps/server/src/sourceControl/SourceControlDiscovery.test.ts @@ -25,7 +25,8 @@ import * as ForgejoSourceControlProvider from "./ForgejoSourceControlProvider.ts import * as ForgejoPullRequestProvider from "../pullRequest/ForgejoPullRequestProvider.ts"; import * as SourceControlDiscovery from "./SourceControlDiscovery.ts"; import * as SourceControlProviderRegistry from "./SourceControlProviderRegistry.ts"; -import { firstNonEmptyLine } from "./SourceControlProviderDiscovery.ts"; +import * as ServerSourceControlHost from "./ServerSourceControlHost.ts"; +import { firstNonEmptyLine } from "@t3tools/source-control-core/server/discovery"; const layerSourceControlProviderRegistryTest = (input: { readonly bitbucket: Partial; @@ -46,6 +47,14 @@ const layerSourceControlProviderRegistryTest = (input: { Layer.mock(ForgejoCli.ForgejoCli)({ listLogins: () => Effect.succeed([]) }), Layer.mock(VcsDriverRegistry.VcsDriverRegistry)({}), Layer.mock(VcsProcess.VcsProcess)(input.process), + ServerSourceControlHost.layer.pipe( + Layer.provide( + Layer.merge( + Layer.mock(VcsProcess.VcsProcess)(input.process), + ServerSettings.ServerSettingsService.layerTest(), + ), + ), + ), ), ), ); diff --git a/apps/server/src/sourceControl/SourceControlDiscovery.ts b/apps/server/src/sourceControl/SourceControlDiscovery.ts index e178148f131f..f56985fefd63 100644 --- a/apps/server/src/sourceControl/SourceControlDiscovery.ts +++ b/apps/server/src/sourceControl/SourceControlDiscovery.ts @@ -10,7 +10,7 @@ import * as Option from "effect/Option"; import * as ServerConfig from "../config.ts"; import * as VcsProcess from "../vcs/VcsProcess.ts"; -import { detailFromCause, firstNonEmptyLine } from "./SourceControlProviderDiscovery.ts"; +import { detailFromCause, firstNonEmptyLine } from "@t3tools/source-control-core/server/discovery"; import * as SourceControlProviderRegistry from "./SourceControlProviderRegistry.ts"; interface DiscoveryProbe { diff --git a/apps/server/src/sourceControl/SourceControlProviderRegistry.test.ts b/apps/server/src/sourceControl/SourceControlProviderRegistry.test.ts index 705c38b847b7..08a7259eb1e1 100644 --- a/apps/server/src/sourceControl/SourceControlProviderRegistry.test.ts +++ b/apps/server/src/sourceControl/SourceControlProviderRegistry.test.ts @@ -21,6 +21,7 @@ import * as GitHubCredentials from "./GitHubCredentials.ts"; import * as GitLabCli from "./GitLabCli.ts"; import * as ForgejoCli from "./ForgejoCli.ts"; import * as SourceControlProviderRegistry from "./SourceControlProviderRegistry.ts"; +import * as ServerSourceControlHost from "./ServerSourceControlHost.ts"; const TEST_EPOCH = DateTime.makeUnsafe("1970-01-01T00:00:00.000Z"); @@ -96,6 +97,11 @@ function makeRegistry(input: { NodeServices.layer, layerRegistry, layerProcess, + ServerSourceControlHost.layer.pipe( + Layer.provide( + Layer.merge(layerProcess, ServerSettings.ServerSettingsService.layerTest()), + ), + ), Layer.mock(AzureDevOpsCli.AzureDevOpsCli)({}), Layer.mock(BitbucketApi.BitbucketApi)({}), ServerSettings.ServerSettingsService.layerTest(), diff --git a/apps/server/src/sourceControl/SourceControlProviderRegistry.ts b/apps/server/src/sourceControl/SourceControlProviderRegistry.ts index 7a626b13f50f..309f79b1509f 100644 --- a/apps/server/src/sourceControl/SourceControlProviderRegistry.ts +++ b/apps/server/src/sourceControl/SourceControlProviderRegistry.ts @@ -16,15 +16,16 @@ import * as BitbucketSourceControlProvider from "./BitbucketSourceControlProvide import * as GitHubSourceControlProvider from "./GitHubSourceControlProvider.ts"; import * as GitLabSourceControlProvider from "./GitLabSourceControlProvider.ts"; import * as ForgejoSourceControlProvider from "./ForgejoSourceControlProvider.ts"; -import * as SourceControlProvider from "./SourceControlProvider.ts"; +import * as SourceControlProvider from "@t3tools/source-control-core/server/SourceControlProvider"; import { probeSourceControlProvider, refineUnknownRemoteProvider, type SourceControlProviderDiscoverySpec, -} from "./SourceControlProviderDiscovery.ts"; +} from "@t3tools/source-control-core/server/discovery"; +import * as SourceControlHost from "@t3tools/source-control-core/server/SourceControlHost"; + import * as ServerConfig from "../config.ts"; import * as VcsDriverRegistry from "../vcs/VcsDriverRegistry.ts"; -import * as VcsProcess from "../vcs/VcsProcess.ts"; const PROVIDER_DETECTION_CACHE_CAPACITY = 2_048; const PROVIDER_DETECTION_CACHE_TTL = Duration.seconds(5); @@ -201,7 +202,7 @@ function bindProviderContext( export const makeWithProviders = Effect.fn("makeSourceControlProviderRegistryWithProviders")( function* (registrations: ReadonlyArray) { const config = yield* ServerConfig.ServerConfig; - const process = yield* VcsProcess.VcsProcess; + const { process } = yield* SourceControlHost.SourceControlHost; const vcsRegistry = yield* VcsDriverRegistry.VcsDriverRegistry; const providers = new Map< SourceControlProviderKind, diff --git a/apps/server/src/sourceControl/SourceControlRepositoryService.test.ts b/apps/server/src/sourceControl/SourceControlRepositoryService.test.ts index 3495b2270b3e..8aba02284403 100644 --- a/apps/server/src/sourceControl/SourceControlRepositoryService.test.ts +++ b/apps/server/src/sourceControl/SourceControlRepositoryService.test.ts @@ -15,7 +15,7 @@ import * as ServerConfig from "../config.ts"; import * as GitVcsDriver from "../vcs/GitVcsDriver.ts"; import * as BitbucketApi from "./BitbucketApi.ts"; import * as BitbucketSourceControlProvider from "./BitbucketSourceControlProvider.ts"; -import type * as SourceControlProvider from "./SourceControlProvider.ts"; +import type * as SourceControlProvider from "@t3tools/source-control-core/server/SourceControlProvider"; import * as SourceControlProviderRegistry from "./SourceControlProviderRegistry.ts"; import * as SourceControlRepositoryService from "./SourceControlRepositoryService.ts"; diff --git a/apps/server/src/sourceControl/githubQuota.test.ts b/apps/server/src/sourceControl/githubQuota.test.ts index e21970cecca7..e9c1fac93eb0 100644 --- a/apps/server/src/sourceControl/githubQuota.test.ts +++ b/apps/server/src/sourceControl/githubQuota.test.ts @@ -3,7 +3,7 @@ import * as Effect from "effect/Effect"; import * as TestClock from "effect/testing/TestClock"; import * as GitHubQuota from "./githubQuota.ts"; -import { CredentialScope } from "./SourceControlRateLimit.ts"; +import { CredentialScope } from "@t3tools/source-control-core/server/SourceControlRateLimit"; const RESET = Date.parse("2099-08-13T14:00:00Z"); const headers = (remaining: number, resource = "graphql", reset = RESET) => ({ diff --git a/apps/server/src/sourceControl/githubQuota.ts b/apps/server/src/sourceControl/githubQuota.ts index c553535019f4..642c48fddc9a 100644 --- a/apps/server/src/sourceControl/githubQuota.ts +++ b/apps/server/src/sourceControl/githubQuota.ts @@ -4,7 +4,7 @@ import * as Effect from "effect/Effect"; import * as Layer from "effect/Layer"; import * as Ref from "effect/Ref"; -import * as SourceControlRateLimit from "./SourceControlRateLimit.ts"; +import * as SourceControlRateLimit from "@t3tools/source-control-core/server/SourceControlRateLimit"; /** The share of each quota a background read leaves for a user's next click. */ const RESERVE_RATIO = 0.1; diff --git a/apps/server/src/ws.ts b/apps/server/src/ws.ts index a035d6971d45..666805e1d79e 100644 --- a/apps/server/src/ws.ts +++ b/apps/server/src/ws.ts @@ -235,6 +235,7 @@ import * as GitHubApi from "./sourceControl/GitHubApi.ts"; import * as GitLabCli from "./sourceControl/GitLabCli.ts"; import * as ForgejoCli from "./sourceControl/ForgejoCli.ts"; import * as SourceControlProviderRegistry from "./sourceControl/SourceControlProviderRegistry.ts"; +import * as ServerSourceControlHost from "./sourceControl/ServerSourceControlHost.ts"; import * as GitVcsDriver from "./vcs/GitVcsDriver.ts"; import * as VcsDriverRegistry from "./vcs/VcsDriverRegistry.ts"; import * as VcsProjectConfig from "./vcs/VcsProjectConfig.ts"; @@ -3209,6 +3210,7 @@ export const layer = Layer.unwrap( GitHubApi.layerWithDependencies, GitLabCli.layer, ForgejoCli.layer, + ServerSourceControlHost.layer, ), ), Layer.provideMerge(GitVcsDriver.layer), diff --git a/package.json b/package.json index 55869fc4b391..471e5361eef2 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,7 @@ "tc": "vp run -r --concurrency-limit 2 typecheck", "lint": "vp lint --report-unused-disable-directives", "knip": "knip --preprocessor ./scripts/knip-schemas.ts", - "knip:check": "knip --include files,dependencies --no-config-hints && knip --workspace apps/server --workspace apps/desktop --workspace apps/web --workspace packages/client-runtime --workspace packages/contracts --workspace packages/effect-acp --workspace packages/effect-codex-app-server --workspace packages/shared --workspace packages/ssh --workspace packages/tailscale --workspace packages/provider-core --workspace packages/provider-acp --workspace packages/provider-acp-registry --workspace packages/provider-testing --workspace packages/provider-pi --workspace packages/provider-muse --workspace packages/provider-opencode --workspace packages/provider-cursor --workspace packages/provider-grok --exports --preprocessor ./scripts/knip-schemas.ts --no-config-hints", + "knip:check": "knip --include files,dependencies --no-config-hints && knip --workspace apps/server --workspace apps/desktop --workspace apps/web --workspace packages/client-runtime --workspace packages/contracts --workspace packages/effect-acp --workspace packages/effect-codex-app-server --workspace packages/shared --workspace packages/ssh --workspace packages/tailscale --workspace packages/provider-core --workspace packages/provider-acp --workspace packages/provider-acp-registry --workspace packages/provider-testing --workspace packages/provider-pi --workspace packages/provider-muse --workspace packages/provider-opencode --workspace packages/provider-cursor --workspace packages/provider-grok --workspace packages/source-control-core --exports --preprocessor ./scripts/knip-schemas.ts --no-config-hints", "knip:production": "knip --production --preprocessor ./scripts/knip-schemas.ts", "lint:mobile": "node scripts/mobile-native-static-check.ts", "test": "vp run -r test", diff --git a/packages/source-control-core/package.json b/packages/source-control-core/package.json new file mode 100644 index 000000000000..58742f9f3589 --- /dev/null +++ b/packages/source-control-core/package.json @@ -0,0 +1,48 @@ +{ + "name": "@t3tools/source-control-core", + "private": true, + "type": "module", + "exports": { + "./server/discovery": { + "types": "./src/server/discovery.ts", + "import": "./src/server/discovery.ts" + }, + "./server/mergeMessage": { + "types": "./src/server/mergeMessage.ts", + "import": "./src/server/mergeMessage.ts" + }, + "./server/pullRequestChecks": { + "types": "./src/server/pullRequestChecks.ts", + "import": "./src/server/pullRequestChecks.ts" + }, + "./server/PullRequestProvider": { + "types": "./src/server/PullRequestProvider.ts", + "import": "./src/server/PullRequestProvider.ts" + }, + "./server/SourceControlHost": { + "types": "./src/server/SourceControlHost.ts", + "import": "./src/server/SourceControlHost.ts" + }, + "./server/SourceControlProvider": { + "types": "./src/server/SourceControlProvider.ts", + "import": "./src/server/SourceControlProvider.ts" + }, + "./server/SourceControlRateLimit": { + "types": "./src/server/SourceControlRateLimit.ts", + "import": "./src/server/SourceControlRateLimit.ts" + } + }, + "scripts": { + "typecheck": "tsc --noEmit", + "test": "vp test run --config ../../vite.config.ts --dir ." + }, + "dependencies": { + "@t3tools/contracts": "workspace:*", + "effect": "catalog:" + }, + "devDependencies": { + "@effect/vitest": "catalog:", + "@types/node": "catalog:", + "vite-plus": "catalog:" + } +} diff --git a/apps/server/src/pullRequest/PullRequestProvider.ts b/packages/source-control-core/src/server/PullRequestProvider.ts similarity index 100% rename from apps/server/src/pullRequest/PullRequestProvider.ts rename to packages/source-control-core/src/server/PullRequestProvider.ts diff --git a/packages/source-control-core/src/server/SourceControlHost.ts b/packages/source-control-core/src/server/SourceControlHost.ts new file mode 100644 index 000000000000..d779f86fe0e2 --- /dev/null +++ b/packages/source-control-core/src/server/SourceControlHost.ts @@ -0,0 +1,59 @@ +/** + * SourceControlHost — what a source control provider package may ask of the server it runs in. + * + * Providers run inside a T3 server but must not import it. The server provides this one + * service; everything a provider needs from its environment (settings, the process runner) + * goes through it, so a provider package depends only on `@t3tools/source-control-core` and + * its own API and CLI code. HTTP, the filesystem, and paths come from Effect's platform + * services directly. + * + * @module source-control-core/server/SourceControlHost + */ +import type { ServerSettings, ServerSettingsError, VcsError } from "@t3tools/contracts"; +import * as Context from "effect/Context"; +import type * as Effect from "effect/Effect"; +import type * as ChildProcessSpawner from "effect/process/ChildProcessSpawner"; + +/** One CLI invocation. The server bounds concurrency, output, and time for every run. */ +export interface SourceControlProcessInput { + readonly operation: string; + readonly command: string; + readonly args: ReadonlyArray; + readonly cwd: string; + readonly spawnCwd?: string; + readonly stdin?: string; + readonly onStdoutChunk?: (chunk: Uint8Array) => void; + readonly env?: NodeJS.ProcessEnv; + readonly allowNonZeroExit?: boolean; + readonly timeoutMs?: number; + readonly maxOutputBytes?: number; + /** What happens past `maxOutputBytes`: fail the run, or keep the first bytes. */ + readonly outputMode?: "error" | "truncate" | undefined; + readonly appendTruncationMarker?: boolean; +} + +export interface SourceControlProcessOutput { + readonly exitCode: ChildProcessSpawner.ExitCode; + readonly stdout: string; + readonly stderr: string; + readonly stdoutTruncated: boolean; + readonly stderrTruncated: boolean; + /** Present on real process output; optional so narrow test doubles remain lightweight. */ + readonly stdoutInvalidUtf8?: boolean; + readonly stderrInvalidUtf8?: boolean; +} + +export class SourceControlHost extends Context.Service< + SourceControlHost, + { + readonly settings: { + /** Read fresh on each call, so a credential saved in Settings applies without a restart. */ + readonly get: Effect.Effect; + }; + readonly process: { + readonly run: ( + input: SourceControlProcessInput, + ) => Effect.Effect; + }; + } +>()("@t3tools/source-control-core/server/SourceControlHost") {} diff --git a/apps/server/src/sourceControl/SourceControlProvider.test.ts b/packages/source-control-core/src/server/SourceControlProvider.test.ts similarity index 100% rename from apps/server/src/sourceControl/SourceControlProvider.test.ts rename to packages/source-control-core/src/server/SourceControlProvider.test.ts diff --git a/apps/server/src/sourceControl/SourceControlProvider.ts b/packages/source-control-core/src/server/SourceControlProvider.ts similarity index 98% rename from apps/server/src/sourceControl/SourceControlProvider.ts rename to packages/source-control-core/src/server/SourceControlProvider.ts index ec61691fdd42..2bcabceb00e3 100644 --- a/apps/server/src/sourceControl/SourceControlProvider.ts +++ b/packages/source-control-core/src/server/SourceControlProvider.ts @@ -142,4 +142,4 @@ export class SourceControlProvider extends Context.Service< readonly force?: boolean; }) => Effect.Effect; } ->()("t3/sourceControl/SourceControlProvider") {} +>()("@t3tools/source-control-core/server/SourceControlProvider") {} diff --git a/apps/server/src/sourceControl/SourceControlRateLimit.test.ts b/packages/source-control-core/src/server/SourceControlRateLimit.test.ts similarity index 100% rename from apps/server/src/sourceControl/SourceControlRateLimit.test.ts rename to packages/source-control-core/src/server/SourceControlRateLimit.test.ts diff --git a/apps/server/src/sourceControl/SourceControlRateLimit.ts b/packages/source-control-core/src/server/SourceControlRateLimit.ts similarity index 95% rename from apps/server/src/sourceControl/SourceControlRateLimit.ts rename to packages/source-control-core/src/server/SourceControlRateLimit.ts index dc6242a60eb6..f2ef07dd2529 100644 --- a/apps/server/src/sourceControl/SourceControlRateLimit.ts +++ b/packages/source-control-core/src/server/SourceControlRateLimit.ts @@ -13,9 +13,12 @@ import { const FALLBACK_COOLDOWN = Duration.seconds(30); const MAX_FALLBACK_COOLDOWN = Duration.minutes(15); -export const CredentialScope = Context.Reference("t3/sourceControl/CredentialScope", { - defaultValue: () => "", -}); +export const CredentialScope = Context.Reference( + "@t3tools/source-control-core/server/SourceControlRateLimit/CredentialScope", + { + defaultValue: () => "", + }, +); interface RateLimitKey { readonly provider: SourceControlProviderKind; @@ -61,7 +64,7 @@ export class SourceControlRateLimit extends Context.Service< ) => Effect.Effect; readonly recordSuccess: (input: RateLimitLease) => Effect.Effect; } ->()("t3/sourceControl/SourceControlRateLimit") {} +>()("@t3tools/source-control-core/server/SourceControlRateLimit") {} function normalizedKey(key: RateLimitKey, scope: string): string { return `${key.provider}\0${key.host.trim().toLowerCase()}\0${scope}`; diff --git a/apps/server/src/sourceControl/SourceControlProviderDiscovery.ts b/packages/source-control-core/src/server/discovery.ts similarity index 95% rename from apps/server/src/sourceControl/SourceControlProviderDiscovery.ts rename to packages/source-control-core/src/server/discovery.ts index 466e66230649..70d26b4a963c 100644 --- a/apps/server/src/sourceControl/SourceControlProviderDiscovery.ts +++ b/packages/source-control-core/src/server/discovery.ts @@ -1,3 +1,8 @@ +/** + * Discovery specs: how each provider reports its CLI or API, and the shared probe that runs them. + * + * @module source-control-core/server/discovery + */ import * as NodeUtil from "node:util"; import type { SourceControlProviderAuth, @@ -9,12 +14,12 @@ import * as Effect from "effect/Effect"; import * as Option from "effect/Option"; import type * as SourceControlProvider from "./SourceControlProvider.ts"; -import type * as VcsProcess from "../vcs/VcsProcess.ts"; +import type * as SourceControlHost from "./SourceControlHost.ts"; export interface SourceControlAuthProbeInput { readonly stdout: string; readonly stderr: string; - readonly exitCode: VcsProcess.VcsProcessOutput["exitCode"]; + readonly exitCode: SourceControlHost.SourceControlProcessOutput["exitCode"]; } export interface SourceControlUnknownRemoteRefinementInput { @@ -179,7 +184,7 @@ function isCliRemoteRefinementSpec( function probeCli(input: { readonly spec: SourceControlCliDiscoverySpec; - readonly process: VcsProcess.VcsProcess["Service"]; + readonly process: SourceControlHost.SourceControlHost["Service"]["process"]; readonly cwd: string; }): Effect.Effect { return input.process @@ -223,7 +228,7 @@ function probeCli(input: { export function probeSourceControlProvider(input: { readonly spec: SourceControlProviderDiscoverySpec; - readonly process: VcsProcess.VcsProcess["Service"]; + readonly process: SourceControlHost.SourceControlHost["Service"]["process"]; readonly cwd: string; }): Effect.Effect { if (input.spec.type === "managed-cli") return input.spec.probe(input.cwd); @@ -292,7 +297,7 @@ export function probeSourceControlProvider(input: { export const refineUnknownRemoteProvider = Effect.fn("refineUnknownRemoteProvider")( function* (input: { readonly specs: ReadonlyArray; - readonly process: VcsProcess.VcsProcess["Service"]; + readonly process: SourceControlHost.SourceControlHost["Service"]["process"]; readonly cwd: string; readonly context: SourceControlProvider.SourceControlProviderContext | null; }): Effect.fn.Return { diff --git a/apps/server/src/pullRequest/mergeMessage.test.ts b/packages/source-control-core/src/server/mergeMessage.test.ts similarity index 100% rename from apps/server/src/pullRequest/mergeMessage.test.ts rename to packages/source-control-core/src/server/mergeMessage.test.ts diff --git a/apps/server/src/pullRequest/mergeMessage.ts b/packages/source-control-core/src/server/mergeMessage.ts similarity index 100% rename from apps/server/src/pullRequest/mergeMessage.ts rename to packages/source-control-core/src/server/mergeMessage.ts diff --git a/apps/server/src/pullRequest/pullRequestChecks.test.ts b/packages/source-control-core/src/server/pullRequestChecks.test.ts similarity index 100% rename from apps/server/src/pullRequest/pullRequestChecks.test.ts rename to packages/source-control-core/src/server/pullRequestChecks.test.ts diff --git a/apps/server/src/pullRequest/pullRequestChecks.ts b/packages/source-control-core/src/server/pullRequestChecks.ts similarity index 100% rename from apps/server/src/pullRequest/pullRequestChecks.ts rename to packages/source-control-core/src/server/pullRequestChecks.ts diff --git a/packages/source-control-core/tsconfig.json b/packages/source-control-core/tsconfig.json new file mode 100644 index 000000000000..374bac55202d --- /dev/null +++ b/packages/source-control-core/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "types": ["node"] + }, + "include": ["src"] +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5790c62cc13d..5cd0b5fd4c6b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -648,6 +648,9 @@ importers: '@t3tools/provider-pi': specifier: workspace:* version: link:../../packages/provider-pi + '@t3tools/source-control-core': + specifier: workspace:* + version: link:../../packages/source-control-core diff: specifier: 8.0.3 version: 8.0.3 @@ -1510,6 +1513,47 @@ importers: specifier: 1.0.0 version: 1.0.0(@types/node@24.12.4)(@vitest/ui@5.0.1(vitest@5.0.1))(bufferutil@4.1.0)(esbuild@0.28.2)(jiti@2.7.0)(jsdom@30.1.0(@noble/hashes@1.8.0))(msw@2.12.11(@types/node@24.12.4)(typescript@7.0.2))(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(utf-8-validate@6.0.6)(yaml@2.9.0) + packages/source-control-core: + dependencies: + '@t3tools/contracts': + specifier: workspace:* + version: link:../contracts + effect: + specifier: 4.0.1 + version: 4.0.1(patch_hash=a33cba07c41f32374c2aaa86ea4a84d3ab9a872a78c94b65e929b19f8361856f) + devDependencies: + '@effect/vitest': + specifier: 4.0.1 + version: 4.0.1(patch_hash=359f6fb2f7b3ec145bb72208edb9034f02489791aa2491a55cdbd69bd56ee0d2)(@types/node@24.12.4)(@vitest/ui@5.0.1)(bufferutil@4.1.0)(effect@4.0.1(patch_hash=a33cba07c41f32374c2aaa86ea4a84d3ab9a872a78c94b65e929b19f8361856f))(esbuild@0.28.2)(jiti@2.7.0)(jsdom@30.1.0(@noble/hashes@1.8.0))(msw@2.12.11(@types/node@24.12.4)(typescript@7.0.2))(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(utf-8-validate@6.0.6)(yaml@2.9.0) + '@types/node': + specifier: 24.12.4 + version: 24.12.4 + vite-plus: + specifier: 1.0.0 + version: 1.0.0(@types/node@24.12.4)(@vitest/ui@5.0.1(vitest@5.0.1))(bufferutil@4.1.0)(esbuild@0.28.2)(jiti@2.7.0)(jsdom@30.1.0(@noble/hashes@1.8.0))(msw@2.12.11(@types/node@24.12.4)(typescript@7.0.2))(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(utf-8-validate@6.0.6)(yaml@2.9.0) + + packages/source-control-testing: + dependencies: + '@t3tools/contracts': + specifier: workspace:* + version: link:../contracts + '@t3tools/source-control-core': + specifier: workspace:* + version: link:../source-control-core + effect: + specifier: 4.0.1 + version: 4.0.1(patch_hash=a33cba07c41f32374c2aaa86ea4a84d3ab9a872a78c94b65e929b19f8361856f) + devDependencies: + '@effect/vitest': + specifier: 4.0.1 + version: 4.0.1(patch_hash=359f6fb2f7b3ec145bb72208edb9034f02489791aa2491a55cdbd69bd56ee0d2)(@types/node@24.12.4)(@vitest/ui@5.0.1)(bufferutil@4.1.0)(effect@4.0.1(patch_hash=a33cba07c41f32374c2aaa86ea4a84d3ab9a872a78c94b65e929b19f8361856f))(esbuild@0.28.2)(jiti@2.7.0)(jsdom@30.1.0(@noble/hashes@1.8.0))(msw@2.12.11(@types/node@24.12.4)(typescript@7.0.2))(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(utf-8-validate@6.0.6)(yaml@2.9.0) + '@types/node': + specifier: 24.12.4 + version: 24.12.4 + vite-plus: + specifier: 1.0.0 + version: 1.0.0(@types/node@24.12.4)(@vitest/ui@5.0.1(vitest@5.0.1))(bufferutil@4.1.0)(esbuild@0.28.2)(jiti@2.7.0)(jsdom@30.1.0(@noble/hashes@1.8.0))(msw@2.12.11(@types/node@24.12.4)(typescript@7.0.2))(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(utf-8-validate@6.0.6)(yaml@2.9.0) + packages/ssh: dependencies: '@t3tools/contracts': From d297c926e72dcda5e2278fcf571183b73a4d10e1 Mon Sep 17 00:00:00 2001 From: Julius Marminge <51714798+juliusmarminge@users.noreply.github.com> Date: Fri, 9 Oct 2026 12:30:40 -0700 Subject: [PATCH 2/4] fix(scripts): include source-control-core in the release smoke workspace Co-Authored-By: Claude Opus 5.5 --- scripts/release-smoke.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/release-smoke.ts b/scripts/release-smoke.ts index fd8921b67c12..f1c903bc8fe1 100644 --- a/scripts/release-smoke.ts +++ b/scripts/release-smoke.ts @@ -37,6 +37,7 @@ const workspaceFiles = [ "packages/provider-grok/package.json", "packages/provider-muse/package.json", "packages/provider-opencode/package.json", + "packages/source-control-core/package.json", "packages/effect-acp/package.json", "packages/effect-codex-app-server/package.json", "scripts/package.json", From 84fd3307ccfb7271706cae54670ec0ae16a76eea Mon Sep 17 00:00:00 2001 From: Julius Marminge <51714798+juliusmarminge@users.noreply.github.com> Date: Fri, 9 Oct 2026 12:35:36 -0700 Subject: [PATCH 3/4] chore: regenerate the lockfile for source-control-core Co-Authored-By: Claude Opus 5.5 --- pnpm-lock.yaml | 30 ++++-------------------------- 1 file changed, 4 insertions(+), 26 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5cd0b5fd4c6b..387677cd3d52 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1519,34 +1519,12 @@ importers: specifier: workspace:* version: link:../contracts effect: - specifier: 4.0.1 - version: 4.0.1(patch_hash=a33cba07c41f32374c2aaa86ea4a84d3ab9a872a78c94b65e929b19f8361856f) - devDependencies: - '@effect/vitest': - specifier: 4.0.1 - version: 4.0.1(patch_hash=359f6fb2f7b3ec145bb72208edb9034f02489791aa2491a55cdbd69bd56ee0d2)(@types/node@24.12.4)(@vitest/ui@5.0.1)(bufferutil@4.1.0)(effect@4.0.1(patch_hash=a33cba07c41f32374c2aaa86ea4a84d3ab9a872a78c94b65e929b19f8361856f))(esbuild@0.28.2)(jiti@2.7.0)(jsdom@30.1.0(@noble/hashes@1.8.0))(msw@2.12.11(@types/node@24.12.4)(typescript@7.0.2))(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(utf-8-validate@6.0.6)(yaml@2.9.0) - '@types/node': - specifier: 24.12.4 - version: 24.12.4 - vite-plus: - specifier: 1.0.0 - version: 1.0.0(@types/node@24.12.4)(@vitest/ui@5.0.1(vitest@5.0.1))(bufferutil@4.1.0)(esbuild@0.28.2)(jiti@2.7.0)(jsdom@30.1.0(@noble/hashes@1.8.0))(msw@2.12.11(@types/node@24.12.4)(typescript@7.0.2))(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(utf-8-validate@6.0.6)(yaml@2.9.0) - - packages/source-control-testing: - dependencies: - '@t3tools/contracts': - specifier: workspace:* - version: link:../contracts - '@t3tools/source-control-core': - specifier: workspace:* - version: link:../source-control-core - effect: - specifier: 4.0.1 - version: 4.0.1(patch_hash=a33cba07c41f32374c2aaa86ea4a84d3ab9a872a78c94b65e929b19f8361856f) + specifier: 4.0.2 + version: 4.0.2(patch_hash=ec61ee307eaec3e17f757e0e6a16296ea4b7a8c3188df0c6ec5e66301fd78688) devDependencies: '@effect/vitest': - specifier: 4.0.1 - version: 4.0.1(patch_hash=359f6fb2f7b3ec145bb72208edb9034f02489791aa2491a55cdbd69bd56ee0d2)(@types/node@24.12.4)(@vitest/ui@5.0.1)(bufferutil@4.1.0)(effect@4.0.1(patch_hash=a33cba07c41f32374c2aaa86ea4a84d3ab9a872a78c94b65e929b19f8361856f))(esbuild@0.28.2)(jiti@2.7.0)(jsdom@30.1.0(@noble/hashes@1.8.0))(msw@2.12.11(@types/node@24.12.4)(typescript@7.0.2))(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(utf-8-validate@6.0.6)(yaml@2.9.0) + specifier: 4.0.2 + version: 4.0.2(patch_hash=595b7d26fb635caba9c3d39fcb189bb78a000192f8d3b60ead7174b310d1ade9)(@types/node@24.12.4)(@vitest/ui@5.0.1)(bufferutil@4.1.0)(effect@4.0.2(patch_hash=ec61ee307eaec3e17f757e0e6a16296ea4b7a8c3188df0c6ec5e66301fd78688))(esbuild@0.28.2)(jiti@2.7.0)(jsdom@30.1.0(@noble/hashes@1.8.0))(msw@2.12.11(@types/node@24.12.4)(typescript@7.0.2))(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(utf-8-validate@6.0.6)(yaml@2.9.0) '@types/node': specifier: 24.12.4 version: 24.12.4 From 2400cfaf36dc93f42b58194e6ab8205fc4406f0a Mon Sep 17 00:00:00 2001 From: Julius Marminge <51714798+juliusmarminge@users.noreply.github.com> Date: Fri, 9 Oct 2026 14:38:25 -0700 Subject: [PATCH 4/4] docs(source-control): point the file revisions doc at the moved PullRequestProvider Co-Authored-By: Claude Opus 5.5 --- docs/internals/pull-request-file-revisions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/internals/pull-request-file-revisions.md b/docs/internals/pull-request-file-revisions.md index 3a84b8de665d..22eeb63848ea 100644 --- a/docs/internals/pull-request-file-revisions.md +++ b/docs/internals/pull-request-file-revisions.md @@ -17,7 +17,7 @@ a project the token cannot see. Below the boundary, in a patch parser or a response decoder, a missing path means this revision does not carry that file. Above it, in -[`ProviderFileRevisions`](../../apps/server/src/pullRequest/PullRequestProvider.ts), a missing path +[`ProviderFileRevisions`](../../packages/source-control-core/src/server/PullRequestProvider.ts), a missing path means the read could not say. A provider's `getFileRevisions` converts on the way up: a path its host looked at and has no version for arrives as the empty string, and absence is kept for what the provider never got to look at. Absence below the boundary does not survive it.