1import type { Octokit as _Octokit } from "@octokit/rest";
2import * as Effect from "effect/Effect";
3import type { AuthError } from "../Auth/AuthProvider.ts";
4import { normalizeGitHubBaseUrl } from "./BaseUrl.ts";
5import { GitHubCredentials } from "./Credentials.ts";
6
7export const Octokit: Effect.Effect<_Octokit, never, GitHubCredentials> =
8 Effect.gen(function* () {
9 const creds = yield* yield* GitHubCredentials;
10 return creds.octokit();
11 });
12
13/**
14 * An Octokit honoring a per-resource `baseUrl` prop. When `baseUrl` is set,
15 * it is normalized and used for this Octokit only (including an explicit
16 * `"github.com"`, which overrides an enterprise-wide credential host back to
17 * the default). When unset, falls back to the credentials' host — the
18 * `GitHub.providers({ baseUrl })` hard-code or the auth provider's resolved
19 * host.
20 */
21export const octokitFor = (
22 baseUrl: string | undefined,
23): Effect.Effect<_Octokit, AuthError, GitHubCredentials> =>
24 Effect.gen(function* () {
25 const creds = yield* yield* GitHubCredentials;
26 return baseUrl === undefined
27 ? creds.octokit()
28 : creds.octokit({ baseUrl: yield* normalizeGitHubBaseUrl(baseUrl) });
29 });
30
31/**
32 * The host a resource's `baseUrl` prop actually resolves to: the normalized
33 * prop when set, otherwise the credentials' host — which already reflects
34 * `GitHub.providers({ baseUrl })` or the auth provider's resolved host.
35 */
36export const effectiveGitHubBaseUrl = (
37 baseUrl: string | undefined,
38): Effect.Effect<string | undefined, AuthError, GitHubCredentials> =>
39 Effect.gen(function* () {
40 if (baseUrl !== undefined) {
41 return yield* normalizeGitHubBaseUrl(baseUrl);
42 }
43 const creds = yield* yield* GitHubCredentials;
44 return creds.baseUrl;
45 });
46
47/**
48 * Whether a resource's EFFECTIVE GitHub host changed between deploys — used
49 * by resource `diff` implementations to decide replacement (a resource with
50 * the same name on a different GitHub instance is a different physical
51 * resource).
52 *
53 * Each side is resolved through the full fallback chain (explicit prop →
54 * `providers({ baseUrl })` → auth provider host) and normalized before
55 * comparing, so neither a cosmetic rewrite (`github.example.com` →
56 * `https://github.example.com/api/v3`) nor making the ambient default
57 * explicit (prop `undefined` → prop equal to the credentials' host) triggers
58 * a replacement — and dropping back to github.com from an enterprise-wide
59 * credential host is correctly detected as a change.
60 */
61export const gitHubBaseUrlChanged = (
62 olds: { baseUrl?: string },
63 news: { baseUrl?: string },
64): Effect.Effect<boolean, AuthError, GitHubCredentials> =>
65 Effect.gen(function* () {
66 const oldUrl = yield* effectiveGitHubBaseUrl(olds.baseUrl);
67 const newUrl = yield* effectiveGitHubBaseUrl(news.baseUrl);
68 return oldUrl !== newUrl;
69 });