Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions apps/server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"build:preflight": "tsdown --config tsdown.preflight.config.ts",
"start": "node dist/bin.mjs start --port 8080",
"typecheck": "tsc --noEmit",
"typecheck:tsgo": "tsgo --noEmit",
"test": "vitest run --coverage",
"test:fast": "vitest run"
},
Expand Down
4 changes: 3 additions & 1 deletion apps/server/src/auth/Layers/BootstrapCredentialService.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { AuthPairingLink } from "@t3tools/contracts";
import * as Crypto from "effect/Crypto";
import * as DateTime from "effect/DateTime";
import * as Duration from "effect/Duration";
import * as Effect from "effect/Effect";
Expand Down Expand Up @@ -46,6 +47,7 @@ const generatePairingToken = (): string => {
};

export const makeBootstrapCredentialService = Effect.gen(function* () {
const crypto = yield* Crypto.Crypto;
const config = yield* ServerConfig;
const pairingLinks = yield* AuthPairingLinkRepository;
const seededGrantsRef = yield* Ref.make(new Map<string, StoredBootstrapGrant>());
Expand Down Expand Up @@ -141,7 +143,7 @@ export const makeBootstrapCredentialService = Effect.gen(function* () {

const issueOneTimeToken: BootstrapCredentialServiceShape["issueOneTimeToken"] = (input) =>
Effect.gen(function* () {
const id = crypto.randomUUID();
const id = yield* crypto.randomUUIDv4.pipe(Effect.orDie);
const credential = generatePairingToken();
const ttl = input?.ttl ?? DEFAULT_ONE_TIME_TOKEN_TTL_MINUTES;
const now = yield* DateTime.now;
Expand Down
4 changes: 2 additions & 2 deletions apps/server/src/auth/Layers/ServerAuth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ export const makeServerAuth = Effect.gen(function* () {
if (credential) {
const verified = yield* authenticateToken(credential).pipe(
Effect.map((session): Option.Option<AuthenticatedSession> => Option.some(session)),
Effect.catch(() => Effect.succeed(Option.none<AuthenticatedSession>())),
Effect.orElseSucceed(() => Option.none<AuthenticatedSession>()),
);
if (Option.isSome(verified)) {
const session = verified.value;
Expand All @@ -202,7 +202,7 @@ export const makeServerAuth = Effect.gen(function* () {
if (loopbackBypassEnabled && isLoopbackRequest(request)) {
const issued = yield* issueLoopbackSession(request).pipe(
Effect.map((session): Option.Option<IssuedSession> => Option.some(session)),
Effect.catch(() => Effect.succeed(Option.none<IssuedSession>())),
Effect.orElseSucceed(() => Option.none<IssuedSession>()),
);
if (Option.isSome(issued)) {
const session = issued.value;
Expand Down
2 changes: 1 addition & 1 deletion apps/server/src/auth/Layers/ServerSecretStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ export const makeServerSecretStore = Effect.gen(function* () {
Effect.gen(function* () {
const entries = yield* fileSystem
.readDirectory(serverConfig.secretsDir)
.pipe(Effect.catch(() => Effect.succeed<ReadonlyArray<string>>([])));
.pipe(Effect.orElseSucceed((): ReadonlyArray<string> => []));
for (const entry of entries) {
if (!entry.endsWith(".bin")) {
continue;
Expand Down
4 changes: 3 additions & 1 deletion apps/server/src/auth/Layers/SessionCredentialService.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { AuthSessionId, type AuthClientMetadata, type AuthClientSession } from "@t3tools/contracts";
import * as Clock from "effect/Clock";
import * as Crypto from "effect/Crypto";
import * as DateTime from "effect/DateTime";
import * as Duration from "effect/Duration";
import * as Effect from "effect/Effect";
Expand Down Expand Up @@ -90,6 +91,7 @@ function toAuthClientSession(input: Omit<AuthClientSession, "current">): AuthCli
}

export const makeSessionCredentialService = Effect.gen(function* () {
const crypto = yield* Crypto.Crypto;
const serverConfig = yield* ServerConfig;
const secretStore = yield* ServerSecretStore;
const authSessions = yield* AuthSessionRepository;
Expand Down Expand Up @@ -203,7 +205,7 @@ export const makeSessionCredentialService = Effect.gen(function* () {
const encodeClaims = Schema.encodeEffect(Schema.fromJsonString(SessionClaims));
const issue: SessionCredentialServiceShape["issue"] = (input) =>
Effect.gen(function* () {
const sessionId = AuthSessionId.make(crypto.randomUUID());
const sessionId = AuthSessionId.make(yield* crypto.randomUUIDv4.pipe(Effect.orDie));
const issuedAt = yield* DateTime.now;
const expiresAt = DateTime.add(issuedAt, {
milliseconds: Duration.toMillis(input?.ttl ?? DEFAULT_SESSION_TTL),
Expand Down
2 changes: 1 addition & 1 deletion apps/server/src/checkpointing/Layers/CheckpointStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ const makeCheckpointStore = Effect.gen(function* () {
})
.pipe(
Effect.map((result) => result.exitCode === 0 && result.stdout.trim() === "true"),
Effect.catch(() => Effect.succeed(false)),
Effect.orElseSucceed(() => false),
);

const captureCheckpoint: CheckpointStoreShape["captureCheckpoint"] = Effect.fn(
Expand Down
15 changes: 9 additions & 6 deletions apps/server/src/cli/project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
type ClientOrchestrationCommand,
} from "@t3tools/contracts";
import * as Console from "effect/Console";
import * as Crypto from "effect/Crypto";
import * as Data from "effect/Data";
import * as DateTime from "effect/DateTime";
import * as Duration from "effect/Duration";
Expand Down Expand Up @@ -59,6 +60,8 @@ class ProjectCommandError extends Data.TaggedError("ProjectCommandError")<{
readonly message: string;
}> {}

const projectCommandUuid = Crypto.Crypto.pipe(Effect.flatMap((crypto) => crypto.randomUUIDv4));

const ProjectCliRuntimeLive = Layer.mergeAll(
WorkspacePathsLive,
OrchestrationLayerLive.pipe(
Expand Down Expand Up @@ -103,7 +106,7 @@ const decodeOrchestrationReadModelResponse = (response: HttpClientResponse.HttpC
const readErrorMessageFromResponse = (response: HttpClientResponse.HttpClientResponse) =>
HttpClientResponse.schemaBodyJson(OrchestrationHttpErrorResponse)(response).pipe(
Effect.map((body) => body.error),
Effect.catch(() => Effect.succeed(null)),
Effect.orElseSucceed(() => null),
Effect.map((body) => {
if (typeof body === "string" && body.trim().length > 0) {
return body;
Expand Down Expand Up @@ -260,7 +263,7 @@ const runProjectMutation = Effect.fn("runProjectMutation")(function* (
}) => Effect.Effect<
string,
Error,
FileSystem.FileSystem | HttpClient.HttpClient | Path.Path | WorkspacePaths
Crypto.Crypto | FileSystem.FileSystem | HttpClient.HttpClient | Path.Path | WorkspacePaths
>,
) {
const logLevel = yield* GlobalFlag.LogLevel;
Expand Down Expand Up @@ -343,10 +346,10 @@ const projectAddCommand = Command.make("add", {
}

const title = yield* resolveProjectTitle(workspaceRoot, Option.getOrUndefined(flags.title));
const projectId = ProjectId.make(crypto.randomUUID());
const projectId = ProjectId.make(yield* projectCommandUuid);
yield* dispatch({
type: "project.create",
commandId: CommandId.make(crypto.randomUUID()),
commandId: CommandId.make(yield* projectCommandUuid),
projectId,
title,
workspaceRoot,
Expand Down Expand Up @@ -384,7 +387,7 @@ const projectRemoveCommand = Command.make("remove", {
});
yield* dispatch({
type: "project.delete",
commandId: CommandId.make(crypto.randomUUID()),
commandId: CommandId.make(yield* projectCommandUuid),
projectId: project.id,
});
return `Removed project ${project.id} (${project.title}).`;
Expand Down Expand Up @@ -424,7 +427,7 @@ const projectRenameCommand = Command.make("rename", {

yield* dispatch({
type: "project.meta.update",
commandId: CommandId.make(crypto.randomUUID()),
commandId: CommandId.make(yield* projectCommandUuid),
projectId: project.id,
title: nextTitle,
});
Expand Down
9 changes: 5 additions & 4 deletions apps/server/src/daemonLauncher.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// @effect-diagnostics nodeBuiltinImport:off
// @effect-diagnostics importFromBarrel:off
// @effect-diagnostics globalTimers:off
// @effect-diagnostics globalTimersInEffect:off globalFetchInEffect:off globalFetch:off -- native fetch + AbortController timeout, wrapped in Effect.tryPromise (fire-once bootstrap/shutdown HTTP probes). globalFetch* = tsgo + language-service rule names for the same check.
// @effect-diagnostics globalErrorInEffectFailure:off
import { spawn } from "node:child_process";
import * as fs from "node:fs";
Expand Down Expand Up @@ -75,7 +76,7 @@ const probeHealth = (origin: string) =>
}
},
catch: (cause) => new DaemonLauncherError({ message: "health probe failed", cause }),
}).pipe(Effect.catch(() => Effect.succeed(false)));
}).pipe(Effect.orElseSucceed(() => false));

const pollHealth = (statePath: string) =>
Effect.gen(function* () {
Expand Down Expand Up @@ -229,7 +230,7 @@ const fetchPairingStartupUrl = (origin: string) =>
}
},
catch: (cause) => new DaemonLauncherError({ message: "pairing-startup request failed", cause }),
}).pipe(Effect.catch(() => Effect.succeed(null)));
}).pipe(Effect.orElseSucceed(() => null));

const resolveDerivedPaths = (input: {
readonly baseDirOverride: string | undefined;
Expand Down Expand Up @@ -373,7 +374,7 @@ export const runStopCommand = (input: StopCommandInput) =>
}
},
catch: (cause) => new DaemonLauncherError({ message: "shutdown request failed", cause }),
}).pipe(Effect.catch(() => Effect.succeed(false)));
}).pipe(Effect.orElseSucceed(() => false));

if (!sent) {
yield* Console.log("");
Expand All @@ -400,7 +401,7 @@ export const runStopCommand = (input: StopCommandInput) =>
schedule: Schedule.spaced(Duration.millis(STOP_DRAIN_INTERVAL_MS)),
times: STOP_DRAIN_RETRIES,
}),
Effect.catch(() => Effect.succeed(false)),
Effect.orElseSucceed(() => false),
);

yield* Console.log("");
Expand Down
14 changes: 7 additions & 7 deletions apps/server/src/git/GitManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -696,7 +696,7 @@ export const makeGitManager = Effect.fn("makeGitManager")(function* () {

const tempDir = process.env.TMPDIR ?? process.env.TEMP ?? process.env.TMP ?? "/tmp";
const canonicalizeExistingPath = (value: string) =>
fileSystem.realPath(value).pipe(Effect.catch(() => Effect.succeed(value)));
fileSystem.realPath(value).pipe(Effect.orElseSucceed(() => value));
const normalizeStatusCacheKey = canonicalizeExistingPath;
const nonRepositoryStatusDetails = {
isRepo: false,
Expand Down Expand Up @@ -760,7 +760,7 @@ export const makeGitManager = Effect.fn("makeGitManager")(function* () {
if (details.isDefaultBranch && latest.state !== "open") return null;
return toStatusPr(latest);
}),
Effect.catch(() => Effect.succeed(null)),
Effect.orElseSucceed(() => null),
)
: null;

Expand All @@ -782,7 +782,7 @@ export const makeGitManager = Effect.fn("makeGitManager")(function* () {
);

const readConfigValueNullable = (cwd: string, key: string) =>
gitCore.readConfigValue(cwd, key).pipe(Effect.catch(() => Effect.succeed(null)));
gitCore.readConfigValue(cwd, key).pipe(Effect.orElseSucceed(() => null));

const resolveHostingProvider = Effect.fn("resolveHostingProvider")(function* (
cwd: string,
Expand Down Expand Up @@ -966,7 +966,7 @@ export const makeGitManager = Effect.fn("makeGitManager")(function* () {
) {
const terms = yield* sourceControlProvider(cwd).pipe(
Effect.map((provider) => getChangeRequestTerminologyForKind(provider.kind)),
Effect.catch(() => Effect.succeed(getChangeRequestTerminologyForKind("unknown"))),
Effect.orElseSucceed(() => getChangeRequestTerminologyForKind("unknown")),
);
const summary = summarizeGitActionResult(result, terms);
let latestOpenPr: PullRequestInfo | null = null;
Expand Down Expand Up @@ -1010,7 +1010,7 @@ export const makeGitManager = Effect.fn("makeGitManager")(function* () {
upstreamRef: finalBranchContext.upstreamRef,
}).pipe(
Effect.flatMap((headContext) => findOpenPr(cwd, headContext)),
Effect.catch(() => Effect.succeed(null)),
Effect.orElseSucceed(() => null),
);
}

Expand Down Expand Up @@ -1074,7 +1074,7 @@ export const makeGitManager = Effect.fn("makeGitManager")(function* () {

const defaultFromProvider = yield* sourceControlProvider(cwd).pipe(
Effect.flatMap((provider) => provider.getDefaultBranch({ cwd })),
Effect.catch(() => Effect.succeed(null)),
Effect.orElseSucceed(() => null),
);
if (defaultFromProvider) {
return defaultFromProvider;
Expand Down Expand Up @@ -1680,7 +1680,7 @@ export const makeGitManager = Effect.fn("makeGitManager")(function* () {
const changeRequestTerms = wantsPr
? yield* sourceControlProvider(input.cwd).pipe(
Effect.map((provider) => getChangeRequestTerminologyForKind(provider.kind)),
Effect.catch(() => Effect.succeed(getChangeRequestTerminologyForKind("unknown"))),
Effect.orElseSucceed(() => getChangeRequestTerminologyForKind("unknown")),
)
: null;

Expand Down
25 changes: 13 additions & 12 deletions apps/server/src/http.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,10 +131,11 @@ export const pairingStartupRouteLayer = prefixedRouteLayer(
);
return HttpServerResponse.jsonUnsafe({ url }, { status: 200 });
}).pipe(
Effect.catchTag("LoopbackOnlyError", respondToLoopbackOnlyError),
Effect.catchTag("PairingStartupError", (error) =>
Effect.succeed(HttpServerResponse.jsonUnsafe({ error: error.message }, { status: 500 })),
),
Effect.catchTags({
LoopbackOnlyError: respondToLoopbackOnlyError,
PairingStartupError: (error) =>
Effect.succeed(HttpServerResponse.jsonUnsafe({ error: error.message }, { status: 500 })),
}),
),
);

Expand Down Expand Up @@ -210,7 +211,7 @@ export const attachmentsRouteLayer = prefixedRouteLayer(
const fileSystem = yield* FileSystem.FileSystem;
const fileInfo = yield* fileSystem
.stat(filePath)
.pipe(Effect.catch(() => Effect.succeed(null)));
.pipe(Effect.orElseSucceed(() => null));
if (!fileInfo || fileInfo.type !== "File") {
return HttpServerResponse.text("Not Found", { status: 404 });
}
Expand All @@ -221,8 +222,8 @@ export const attachmentsRouteLayer = prefixedRouteLayer(
"Cache-Control": "public, max-age=31536000, immutable",
},
}).pipe(
Effect.catch(() =>
Effect.succeed(HttpServerResponse.text("Internal Server Error", { status: 500 })),
Effect.orElseSucceed(() =>
HttpServerResponse.text("Internal Server Error", { status: 500 }),
),
);
}).pipe(Effect.catchTag("AuthError", respondToAuthError)),
Expand Down Expand Up @@ -262,8 +263,8 @@ export const projectFaviconRouteLayer = prefixedRouteLayer(
"Cache-Control": PROJECT_FAVICON_CACHE_CONTROL,
},
}).pipe(
Effect.catch(() =>
Effect.succeed(HttpServerResponse.text("Internal Server Error", { status: 500 })),
Effect.orElseSucceed(() =>
HttpServerResponse.text("Internal Server Error", { status: 500 }),
),
);
}).pipe(Effect.catchTag("AuthError", respondToAuthError)),
Expand Down Expand Up @@ -345,12 +346,12 @@ export const staticAndDevRouteLayer = HttpRouter.add(

const fileInfo = yield* fileSystem
.stat(filePath)
.pipe(Effect.catch(() => Effect.succeed(null)));
.pipe(Effect.orElseSucceed(() => null));
if (!fileInfo || fileInfo.type !== "File") {
const indexPath = path.resolve(staticRoot, "index.html");
const indexData = yield* fileSystem
.readFile(indexPath)
.pipe(Effect.catch(() => Effect.succeed(null)));
.pipe(Effect.orElseSucceed(() => null));
if (!indexData) {
return HttpServerResponse.text("Not Found", { status: 404 });
}
Expand All @@ -368,7 +369,7 @@ export const staticAndDevRouteLayer = HttpRouter.add(
const contentType = Mime.getType(filePath) ?? "application/octet-stream";
const data = yield* fileSystem
.readFile(filePath)
.pipe(Effect.catch(() => Effect.succeed(null)));
.pipe(Effect.orElseSucceed(() => null));
if (!data) {
return HttpServerResponse.text("Internal Server Error", { status: 500 });
}
Expand Down
Loading
Loading