import * as Cache from "effect/Cache";import * as Context from "effect/Context";import * as Duration from "effect/Duration";import * as Effect from "effect/Effect";import * as Exit from "effect/Exit";import * as Layer from "effect/Layer"; import type { VcsDriverKind, VcsError, VcsRepositoryIdentity } from "@t3tools/contracts";import { VcsUnsupportedOperationError } from "@t3tools/contracts";import * as GitVcsDriver from "./GitVcsDriver.ts";import * as VcsProjectConfig from "./VcsProjectConfig.ts";import * as VcsDriver from "./VcsDriver.ts"; const DETECTION_CACHE_CAPACITY = 2_048;const DETECTION_CACHE_TTL = Duration.seconds(2); export interface VcsDriverResolveInput { readonly cwd: string; readonly requestedKind?: VcsDriverKind | "auto";} export interface VcsDriverHandle { readonly kind: VcsDriverKind; readonly repository: VcsRepositoryIdentity; readonly driver: VcsDriver.VcsDriver["Service"];} export class VcsDriverRegistry extends Context.Service< VcsDriverRegistry, { readonly get: (kind: VcsDriverKind) => Effect.Effect<VcsDriver.VcsDriver["Service"], VcsError>; readonly detect: ( input: VcsDriverResolveInput, ) => Effect.Effect<VcsDriverHandle | null, VcsError>; readonly resolve: (input: VcsDriverResolveInput) => Effect.Effect<VcsDriverHandle, VcsError>; }>()("t3/vcs/VcsDriverRegistry") {} function detectionCacheKey(input: { readonly cwd: string; readonly requestedKind: VcsDriverKind | "auto";}): string { return `${input.requestedKind}\0${input.cwd}`;} function parseDetectionCacheKey(key: string): { readonly cwd: string; readonly requestedKind: VcsDriverKind | "auto";} { const separatorIndex = key.indexOf("\0"); if (separatorIndex === -1) { return { cwd: key, requestedKind: "auto", }; } return { requestedKind: key.slice(0, separatorIndex) as VcsDriverKind | "auto", cwd: key.slice(separatorIndex + 1), };} export const make = Effect.gen(function* () { const projectConfig = yield* VcsProjectConfig.VcsProjectConfig; const git = yield* GitVcsDriver.makeVcsDriver; const drivers: Partial<Record<VcsDriverKind, VcsDriver.VcsDriver["Service"]>> = { git, }; const get: VcsDriverRegistry["Service"]["get"] = (kind) => { const driver = drivers[kind]; if (!driver) { return Effect.fail( new VcsUnsupportedOperationError({ operation: "VcsDriverRegistry.get", kind, detail: `No ${kind} VCS driver is registered.`, }), ); } return Effect.succeed(driver); }; const detectWithDriver = Effect.fn("VcsDriverRegistry.detectWithDriver")(function* ( kind: VcsDriverKind, driver: VcsDriver.VcsDriver["Service"], cwd: string, ) { const repository = yield* driver.detectRepository(cwd); if (!repository) { return null; } return { kind, repository, driver, } satisfies VcsDriverHandle; }); const detectResolvedKind = Effect.fn("VcsDriverRegistry.detectResolvedKind")(function* (input: { readonly cwd: string; readonly requestedKind: VcsDriverKind | "auto"; }) { const requestedKind = input.requestedKind; if (requestedKind !== "auto" && requestedKind !== "unknown") { const driver = yield* get(requestedKind); return yield* detectWithDriver(requestedKind, driver, input.cwd); } return yield* detectWithDriver("git", git, input.cwd); }); const detectionCache = yield* Cache.makeWith<string, VcsDriverHandle | null, VcsError>( (key) => detectResolvedKind(parseDetectionCacheKey(key)), { capacity: DETECTION_CACHE_CAPACITY, timeToLive: Exit.match({ onSuccess: (detected) => (detected === null ? Duration.zero : DETECTION_CACHE_TTL), onFailure: () => Duration.zero, }), }, ); const detect: VcsDriverRegistry["Service"]["detect"] = Effect.fn("VcsDriverRegistry.detect")( function* (input) { const requestedKind = yield* projectConfig.resolveKind(input); return yield* Cache.get(detectionCache, detectionCacheKey({ cwd: input.cwd, requestedKind })); }, ); const resolve: VcsDriverRegistry["Service"]["resolve"] = Effect.fn("VcsDriverRegistry.resolve")( function* (input) { const detected = yield* detect(input); if (detected) { return detected; } const requestedKind = input.requestedKind ?? "auto"; return yield* new VcsUnsupportedOperationError({ operation: "VcsDriverRegistry.resolve", kind: requestedKind === "auto" ? "unknown" : requestedKind, detail: requestedKind === "auto" ? `No supported VCS repository was detected at ${input.cwd}.` : `No ${requestedKind} repository was detected at ${input.cwd}.`, }); }, ); return VcsDriverRegistry.of({ get, detect, resolve, });}); export const layer = Layer.effect(VcsDriverRegistry, make).pipe( Layer.provide(VcsProjectConfig.layer),);
import * as Cache from "effect/Cache";
import * as Context from "effect/Context";
import * as Duration from "effect/Duration";
import * as Effect from "effect/Effect";
import * as Exit from "effect/Exit";
import * as Layer from "effect/Layer";
import type { VcsDriverKind, VcsError, VcsRepositoryIdentity } from "@t3tools/contracts";
import { VcsUnsupportedOperationError } from "@t3tools/contracts";
import * as GitVcsDriver from "./GitVcsDriver.ts";
import * as VcsProjectConfig from "./VcsProjectConfig.ts";
import * as VcsDriver from "./VcsDriver.ts";
const DETECTION_CACHE_CAPACITY = 2_048;
const DETECTION_CACHE_TTL = Duration.seconds(2);
export interface VcsDriverResolveInput {
readonly cwd: string;
readonly requestedKind?: VcsDriverKind | "auto";
}
export interface VcsDriverHandle {
readonly kind: VcsDriverKind;
readonly repository: VcsRepositoryIdentity;
readonly driver: VcsDriver.VcsDriver["Service"];
}
export class VcsDriverRegistry extends Context.Service<
VcsDriverRegistry,
{
readonly get: (kind: VcsDriverKind) => Effect.Effect<VcsDriver.VcsDriver["Service"], VcsError>;
readonly detect: (
input: VcsDriverResolveInput,
) => Effect.Effect<VcsDriverHandle | null, VcsError>;
readonly resolve: (input: VcsDriverResolveInput) => Effect.Effect<VcsDriverHandle, VcsError>;
}
>()("t3/vcs/VcsDriverRegistry") {}
function detectionCacheKey(input: {
readonly cwd: string;
readonly requestedKind: VcsDriverKind | "auto";
}): string {
return `${input.requestedKind}\0${input.cwd}`;
}
function parseDetectionCacheKey(key: string): {
readonly cwd: string;
readonly requestedKind: VcsDriverKind | "auto";
} {
const separatorIndex = key.indexOf("\0");
if (separatorIndex === -1) {
return {
cwd: key,
requestedKind: "auto",
};
}
return {
requestedKind: key.slice(0, separatorIndex) as VcsDriverKind | "auto",
cwd: key.slice(separatorIndex + 1),
};
}
export const make = Effect.gen(function* () {
const projectConfig = yield* VcsProjectConfig.VcsProjectConfig;
const git = yield* GitVcsDriver.makeVcsDriver;
const drivers: Partial<Record<VcsDriverKind, VcsDriver.VcsDriver["Service"]>> = {
git,
};
const get: VcsDriverRegistry["Service"]["get"] = (kind) => {
const driver = drivers[kind];
if (!driver) {
return Effect.fail(
new VcsUnsupportedOperationError({
operation: "VcsDriverRegistry.get",
kind,
detail: `No ${kind} VCS driver is registered.`,
}),
);
}
return Effect.succeed(driver);
};
const detectWithDriver = Effect.fn("VcsDriverRegistry.detectWithDriver")(function* (
kind: VcsDriverKind,
driver: VcsDriver.VcsDriver["Service"],
cwd: string,
) {
const repository = yield* driver.detectRepository(cwd);
if (!repository) {
return null;
}
return {
kind,
repository,
driver,
} satisfies VcsDriverHandle;
});
const detectResolvedKind = Effect.fn("VcsDriverRegistry.detectResolvedKind")(function* (input: {
readonly cwd: string;
readonly requestedKind: VcsDriverKind | "auto";
}) {
const requestedKind = input.requestedKind;
if (requestedKind !== "auto" && requestedKind !== "unknown") {
const driver = yield* get(requestedKind);
return yield* detectWithDriver(requestedKind, driver, input.cwd);
}
return yield* detectWithDriver("git", git, input.cwd);
});
const detectionCache = yield* Cache.makeWith<string, VcsDriverHandle | null, VcsError>(
(key) => detectResolvedKind(parseDetectionCacheKey(key)),
{
capacity: DETECTION_CACHE_CAPACITY,
timeToLive: Exit.match({
onSuccess: (detected) => (detected === null ? Duration.zero : DETECTION_CACHE_TTL),
onFailure: () => Duration.zero,
}),
},
);
const detect: VcsDriverRegistry["Service"]["detect"] = Effect.fn("VcsDriverRegistry.detect")(
function* (input) {
const requestedKind = yield* projectConfig.resolveKind(input);
return yield* Cache.get(detectionCache, detectionCacheKey({ cwd: input.cwd, requestedKind }));
},
);
const resolve: VcsDriverRegistry["Service"]["resolve"] = Effect.fn("VcsDriverRegistry.resolve")(
function* (input) {
const detected = yield* detect(input);
if (detected) {
return detected;
}
const requestedKind = input.requestedKind ?? "auto";
return yield* new VcsUnsupportedOperationError({
operation: "VcsDriverRegistry.resolve",
kind: requestedKind === "auto" ? "unknown" : requestedKind,
detail:
requestedKind === "auto"
? `No supported VCS repository was detected at ${input.cwd}.`
: `No ${requestedKind} repository was detected at ${input.cwd}.`,
});
},
);
return VcsDriverRegistry.of({
get,
detect,
resolve,
});
});
export const layer = Layer.effect(VcsDriverRegistry, make).pipe(
Layer.provide(VcsProjectConfig.layer),
);