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
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ import {
import { ProviderService } from "../src/provider/Services/ProviderService.ts";
import { AnalyticsService } from "../src/telemetry/Services/AnalyticsService.ts";
import { CheckpointReactorLive } from "../src/orchestration/Layers/CheckpointReactor.ts";
import { RepositoryIdentityResolverLive } from "../src/project/Layers/RepositoryIdentityResolver.ts";
import * as RepositoryIdentityResolver from "../src/project/RepositoryIdentityResolver.ts";
import { OrchestrationEngineLive } from "../src/orchestration/Layers/OrchestrationEngine.ts";
import { OrchestrationProjectionPipelineLive } from "../src/orchestration/Layers/ProjectionPipeline.ts";
import { OrchestrationProjectionSnapshotQueryLive } from "../src/orchestration/Layers/ProjectionSnapshotQuery.ts";
Expand All @@ -72,7 +72,7 @@ import {
} from "./TestProviderAdapter.integration.ts";
import { deriveServerPaths, ServerConfig } from "../src/config.ts";
import * as WorkspaceEntries from "../src/workspace/WorkspaceEntries.ts";
import { WorkspacePathsLive } from "../src/workspace/Layers/WorkspacePaths.ts";
import * as WorkspacePaths from "../src/workspace/WorkspacePaths.ts";
import * as VcsDriverRegistry from "../src/vcs/VcsDriverRegistry.ts";
import { VcsStatusBroadcaster } from "../src/vcs/VcsStatusBroadcaster.ts";
import { GitWorkflowService } from "../src/git/GitWorkflowService.ts";
Expand Down Expand Up @@ -348,12 +348,12 @@ export const makeOrchestrationIntegrationHarness = (
),
Layer.provideMerge(
WorkspaceEntries.layer.pipe(
Layer.provide(WorkspacePathsLive),
Layer.provide(WorkspacePaths.layer),
Layer.provideMerge(VcsDriverRegistry.layer),
Layer.provide(NodeServices.layer),
),
),
Layer.provideMerge(WorkspacePathsLive),
Layer.provideMerge(WorkspacePaths.layer),
Layer.provideMerge(VcsProcess.layer),
);
const orchestrationReactorLayer = OrchestrationReactorLive.pipe(
Expand All @@ -378,7 +378,7 @@ export const makeOrchestrationIntegrationHarness = (
Layer.provideMerge(orchestrationReactorLayer),
Layer.provideMerge(providerRegistryLayer),
Layer.provide(persistenceLayer),
Layer.provideMerge(RepositoryIdentityResolverLive),
Layer.provideMerge(RepositoryIdentityResolver.layer),
Layer.provideMerge(ServerSettingsService.layerTest()),
Layer.provideMerge(ServerConfig.layerTest(workspaceDir, rootDir)),
Layer.provideMerge(NodeServices.layer),
Expand Down
14 changes: 7 additions & 7 deletions apps/server/src/assets/AssetAccess.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,18 @@ import * as Layer from "effect/Layer";
import * as Path from "effect/Path";

import * as ServerSecretStore from "../auth/ServerSecretStore.ts";
import { ServerConfig } from "../config.ts";
import { ProjectFaviconResolverLive } from "../project/Layers/ProjectFaviconResolver.ts";
import { WorkspacePathsLive } from "../workspace/Layers/WorkspacePaths.ts";
import * as ServerConfig from "../config.ts";
import * as ProjectFaviconResolver from "../project/ProjectFaviconResolver.ts";
import * as WorkspacePaths from "../workspace/WorkspacePaths.ts";
import { ASSET_ROUTE_PREFIX, issueAssetUrl, resolveAsset } from "./AssetAccess.ts";

const configLayer = ServerConfig.layerTest(process.cwd(), {
const configLayer = ServerConfig.ServerConfig.layerTest(process.cwd(), {
prefix: "t3-asset-access-test-",
});
const testLayer = Layer.mergeAll(
configLayer,
WorkspacePathsLive,
ProjectFaviconResolverLive.pipe(Layer.provide(WorkspacePathsLive)),
WorkspacePaths.layer,
ProjectFaviconResolver.layer.pipe(Layer.provide(WorkspacePaths.layer)),
ServerSecretStore.layer.pipe(Layer.provide(configLayer)),
).pipe(Layer.provideMerge(NodeServices.layer));

Expand Down Expand Up @@ -127,7 +127,7 @@ describe("AssetAccess", () => {

it.effect("issues exact attachment capabilities by attachment id", () =>
Effect.gen(function* () {
const config = yield* ServerConfig;
const config = yield* ServerConfig.ServerConfig;
const fileSystem = yield* FileSystem.FileSystem;
const path = yield* Path.Path;
const attachmentId = "thread-1-00000000-0000-4000-8000-000000000001";
Expand Down
10 changes: 5 additions & 5 deletions apps/server/src/assets/AssetAccess.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ import {
import { ServerSecretStore } from "../auth/ServerSecretStore.ts";
import { resolveAttachmentPathById } from "../attachmentStore.ts";
import { ServerConfig } from "../config.ts";
import { ProjectFaviconResolver } from "../project/Services/ProjectFaviconResolver.ts";
import { WorkspacePaths } from "../workspace/Services/WorkspacePaths.ts";
import * as ProjectFaviconResolver from "../project/ProjectFaviconResolver.ts";
import * as WorkspacePaths from "../workspace/WorkspacePaths.ts";

export const ASSET_ROUTE_PREFIX = "/api/assets";
export const FALLBACK_PROJECT_FAVICON_SVG = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" fill="none" stroke="#6b728080" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" data-fallback="project-favicon"><path d="M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-8l-2-2H4a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2Z"/></svg>`;
Expand Down Expand Up @@ -103,7 +103,7 @@ const failAccess = (message: string, cause?: unknown) =>
const resolveCanonicalWorkspaceFile = Effect.fn("AssetAccess.resolveCanonicalWorkspaceFile")(
function* (input: { readonly workspaceRoot: string; readonly relativePath: string }) {
const fileSystem = yield* FileSystem.FileSystem;
const workspacePaths = yield* WorkspacePaths;
const workspacePaths = yield* WorkspacePaths.WorkspacePaths;
const resolved = yield* workspacePaths
.resolveRelativePathWithinRoot(input)
.pipe(Effect.orElseSucceed(() => null));
Expand All @@ -130,7 +130,7 @@ export const issueAssetUrl = Effect.fn("AssetAccess.issueAssetUrl")(function* (i
}) {
const fileSystem = yield* FileSystem.FileSystem;
const path = yield* Path.Path;
const workspacePaths = yield* WorkspacePaths;
const workspacePaths = yield* WorkspacePaths.WorkspacePaths;
const expiresAt = (yield* Clock.currentTimeMillis) + ASSET_TOKEN_TTL_MS;
let claims: AssetClaims;
let fileName: string;
Expand Down Expand Up @@ -202,7 +202,7 @@ export const issueAssetUrl = Effect.fn("AssetAccess.issueAssetUrl")(function* (i
const workspaceRoot = yield* workspacePaths
.normalizeWorkspaceRoot(input.resource.cwd)
.pipe(Effect.mapError((cause) => failAccess(cause.message, cause)));
const faviconResolver = yield* ProjectFaviconResolver;
const faviconResolver = yield* ProjectFaviconResolver.ProjectFaviconResolver;
const faviconPath = yield* faviconResolver.resolvePath(workspaceRoot);
const relativePath = faviconPath ? path.relative(workspaceRoot, faviconPath) : null;
if (
Expand Down
12 changes: 6 additions & 6 deletions apps/server/src/bin.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// @effect-diagnostics nodeBuiltinImport:off - CLI integration exercises Node HTTP and filesystem boundaries.
import * as NodeHttp from "node:http";
import { createServer } from "node:http";
import { existsSync, mkdirSync, mkdtempSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
Expand All @@ -25,12 +25,12 @@ import * as ProjectionSnapshotQuery from "./orchestration/Services/ProjectionSna
import { OrchestrationLayerLive } from "./orchestration/runtimeLayer.ts";
import { orchestrationHttpApiLayer } from "./orchestration/http.ts";
import { layerConfig as SqlitePersistenceLayerLive } from "./persistence/Layers/Sqlite.ts";
import { RepositoryIdentityResolverLive } from "./project/Layers/RepositoryIdentityResolver.ts";
import * as RepositoryIdentityResolver from "./project/RepositoryIdentityResolver.ts";
import {
makePersistedServerRuntimeState,
persistServerRuntimeState,
} from "./serverRuntimeState.ts";
import { WorkspacePathsLive } from "./workspace/Layers/WorkspacePaths.ts";
import * as WorkspacePaths from "./workspace/WorkspacePaths.ts";
import * as ServerSecretStore from "./auth/ServerSecretStore.ts";
import * as EnvironmentAuth from "./auth/EnvironmentAuth.ts";
import { environmentAuthenticatedAuthLayer } from "./auth/http.ts";
Expand Down Expand Up @@ -90,10 +90,10 @@ const makeCliTestServerConfig = (baseDir: string) =>
const makeProjectPersistenceLayer = (config: ServerConfig.ServerConfig["Service"]) =>
Layer.mergeAll(
OrchestrationLayerLive.pipe(
Layer.provideMerge(RepositoryIdentityResolverLive),
Layer.provideMerge(RepositoryIdentityResolver.layer),
Layer.provideMerge(SqlitePersistenceLayerLive),
),
WorkspacePathsLive,
WorkspacePaths.layer,
).pipe(Layer.provideMerge(NodeServices.layer), Layer.provide(ServerConfig.layer(config)));

const readPersistedSnapshot = (baseDir: string) =>
Expand Down Expand Up @@ -124,7 +124,7 @@ const withLiveProjectCliServer = <A, E, R>(baseDir: string, run: () => Effect.Ef
),
Layer.provideMerge(makeProjectPersistenceLayer(config)),
Layer.provideMerge(
NodeHttpServer.layer(NodeHttp.createServer, {
NodeHttpServer.layer(createServer, {
host: "127.0.0.1",
port: 0,
}),
Expand Down
5 changes: 2 additions & 3 deletions apps/server/src/cli/connect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,7 @@ import { CLOUD_LINKED_USER_ID, RELAY_URL_SECRET } from "../cloud/config.ts";
import { relayUrlConfig } from "../cloud/publicConfig.ts";
import { headlessRelayClientTracingLayer } from "../cloud/relayTracing.ts";
import * as ServerConfig from "../config.ts";
import { ServerEnvironmentLive } from "../environment/Layers/ServerEnvironment.ts";
import * as ServerEnvironment from "../environment/Services/ServerEnvironment.ts";
import * as ServerEnvironment from "../environment/ServerEnvironment.ts";
import { readPersistedServerRuntimeState } from "../serverRuntimeState.ts";
import { projectLocationFlags, resolveCliAuthConfig } from "./config.ts";

Expand Down Expand Up @@ -301,7 +300,7 @@ const runCloudCommand = <A, E>(
CliTokenManager.layer.pipe(Layer.provide(ServerSecretStore.layer)),
RelayClient.layerCloudflared({ baseDir: config.baseDir }),
EnvironmentAuth.runtimeLayer,
ServerEnvironmentLive,
ServerEnvironment.layer,
headlessRelayClientTracingLayer,
).pipe(
Layer.provideMerge(FetchHttpClient.layer),
Expand Down
11 changes: 5 additions & 6 deletions apps/server/src/cli/project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,13 @@ import * as OrchestrationEngine from "../orchestration/Services/OrchestrationEng
import * as ProjectionSnapshotQuery from "../orchestration/Services/ProjectionSnapshotQuery.ts";
import { OrchestrationLayerLive } from "../orchestration/runtimeLayer.ts";
import { layerConfig as SqlitePersistenceLayerLive } from "../persistence/Layers/Sqlite.ts";
import { RepositoryIdentityResolverLive } from "../project/Layers/RepositoryIdentityResolver.ts";
import * as RepositoryIdentityResolver from "../project/RepositoryIdentityResolver.ts";
import * as ServerRuntimeStartup from "../serverRuntimeStartup.ts";
import {
clearPersistedServerRuntimeState,
readPersistedServerRuntimeState,
} from "../serverRuntimeState.ts";
import { WorkspacePathsLive } from "../workspace/Layers/WorkspacePaths.ts";
import * as WorkspacePaths from "../workspace/Services/WorkspacePaths.ts";
import * as WorkspacePaths from "../workspace/WorkspacePaths.ts";
import { type CliAuthLocationFlags, projectLocationFlags, resolveCliAuthConfig } from "./config.ts";

type ProjectMutationTarget = {
Expand Down Expand Up @@ -68,9 +67,9 @@ const projectCommandUuid = Crypto.Crypto.pipe(
);

const ProjectCliRuntimeLive = Layer.mergeAll(
WorkspacePathsLive,
WorkspacePaths.layer,
OrchestrationLayerLive.pipe(
Layer.provideMerge(RepositoryIdentityResolverLive),
Layer.provideMerge(RepositoryIdentityResolver.layer),
Layer.provideMerge(SqlitePersistenceLayerLive),
),
);
Expand Down Expand Up @@ -301,7 +300,7 @@ const runProjectMutation = Effect.fn("runProjectMutation")(function* (
}).pipe(Effect.provide(offlineRuntimeLayer));
}).pipe(
Effect.provide(
Layer.mergeAll(EnvironmentAuth.runtimeLayer, WorkspacePathsLive).pipe(
Layer.mergeAll(EnvironmentAuth.runtimeLayer, WorkspacePaths.layer).pipe(
Layer.provideMerge(FetchHttpClient.layer),
Layer.provide(ServerConfig.layer(config)),
Layer.provide(Layer.succeed(References.MinimumLogLevel, minimumLogLevel)),
Expand Down
2 changes: 1 addition & 1 deletion apps/server/src/cloud/http.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { HttpClient, HttpServerRequest } from "effect/unstable/http";
import { RelayClientTracer } from "@t3tools/shared/relayTracing";
import * as EnvironmentAuth from "../auth/EnvironmentAuth.ts";
import * as ServerSecretStore from "../auth/ServerSecretStore.ts";
import * as ServerEnvironment from "../environment/Services/ServerEnvironment.ts";
import * as ServerEnvironment from "../environment/ServerEnvironment.ts";
import * as CliTokenManager from "./CliTokenManager.ts";
import { consumeCloudReplayGuards, reconcileDesiredCloudLink } from "./http.ts";
import * as ManagedEndpointRuntime from "./ManagedEndpointRuntime.ts";
Expand Down
6 changes: 3 additions & 3 deletions apps/server/src/cloud/http.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import * as NodeCrypto from "node:crypto";
import { createPublicKey } from "node:crypto";
import {
AuthRelayReadScope,
AuthRelayWriteScope,
Expand Down Expand Up @@ -55,7 +55,7 @@ import * as HttpApiBuilder from "effect/unstable/httpapi/HttpApiBuilder";
import * as EnvironmentAuth from "../auth/EnvironmentAuth.ts";
import * as ServerSecretStore from "../auth/ServerSecretStore.ts";
import { requireEnvironmentScope } from "../auth/http.ts";
import * as ServerEnvironment from "../environment/Services/ServerEnvironment.ts";
import * as ServerEnvironment from "../environment/ServerEnvironment.ts";
import * as ManagedEndpointRuntime from "./ManagedEndpointRuntime.ts";
import {
CLOUD_ENDPOINT_RUNTIME_CONFIG,
Expand Down Expand Up @@ -152,7 +152,7 @@ function validateCloudMintPublicKey(
publicKey: string,
): Effect.Effect<void, EnvironmentHttpBadRequestError> {
return Effect.try({
try: () => NodeCrypto.createPublicKey(publicKey.replace(/\\n/g, "\n")),
try: () => createPublicKey(publicKey.replace(/\\n/g, "\n")),
catch: () =>
new EnvironmentHttpBadRequestError({
message: "Cloud mint public key must be a valid Ed25519 public key.",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// @effect-diagnostics nodeBuiltinImport:off
import * as nodePath from "node:path";
import { dirname } from "node:path";
import * as NodeServices from "@effect/platform-node/NodeServices";
import { expect, it } from "@effect/vitest";
import * as Effect from "effect/Effect";
Expand All @@ -8,12 +8,11 @@ import * as FileSystem from "effect/FileSystem";
import * as Layer from "effect/Layer";
import * as PlatformError from "effect/PlatformError";

import * as ServerConfig from "../../config.ts";
import * as ServerEnvironment from "../Services/ServerEnvironment.ts";
import { ServerEnvironmentLive } from "./ServerEnvironment.ts";
import * as ServerConfig from "../config.ts";
import * as ServerEnvironment from "./ServerEnvironment.ts";

const makeServerEnvironmentLayer = (baseDir: string) =>
ServerEnvironmentLive.pipe(Layer.provide(ServerConfig.layerTest(process.cwd(), baseDir)));
ServerEnvironment.layer.pipe(Layer.provide(ServerConfig.layerTest(process.cwd(), baseDir)));

const makeServerConfig = Effect.fn(function* (baseDir: string) {
const derivedPaths = yield* ServerConfig.deriveServerPaths(baseDir, undefined);
Expand Down Expand Up @@ -77,7 +76,7 @@ it.layer(NodeServices.layer)("ServerEnvironmentLive", (it) => {
});
const serverConfig = yield* makeServerConfig(baseDir);
const environmentIdPath = serverConfig.environmentIdPath;
yield* fileSystem.makeDirectory(nodePath.dirname(environmentIdPath), { recursive: true });
yield* fileSystem.makeDirectory(dirname(environmentIdPath), { recursive: true });
yield* fileSystem.writeFileString(environmentIdPath, "persisted-environment-id\n");
const writeAttempts: string[] = [];
const failingFileSystemLayer = FileSystem.layerNoop({
Expand Down Expand Up @@ -113,7 +112,7 @@ it.layer(NodeServices.layer)("ServerEnvironmentLive", (it) => {
return yield* serverEnvironment.getDescriptor;
}).pipe(
Effect.provide(
ServerEnvironmentLive.pipe(
ServerEnvironment.layer.pipe(
Layer.provide(Layer.merge(ServerConfig.layer(serverConfig), failingFileSystemLayer)),
),
),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,25 @@
import { EnvironmentId, type ExecutionEnvironmentDescriptor } from "@t3tools/contracts";
import { HostProcessArchitecture, HostProcessPlatform } from "@t3tools/shared/hostProcess";
import * as Context from "effect/Context";
import * as Crypto from "effect/Crypto";
import * as Effect from "effect/Effect";
import * as FileSystem from "effect/FileSystem";
import * as Layer from "effect/Layer";
import * as Path from "effect/Path";

import { ServerConfig } from "../../config.ts";
import { layer as ProcessRunnerLive } from "../../processRunner.ts";
import { ServerEnvironment, type ServerEnvironmentShape } from "../Services/ServerEnvironment.ts";
import packageJson from "../../../package.json" with { type: "json" };
import packageJson from "../../package.json" with { type: "json" };
import * as ServerConfig from "../config.ts";
import * as ProcessRunner from "../processRunner.ts";
import { resolveServerEnvironmentLabel } from "./ServerEnvironmentLabel.ts";

export class ServerEnvironment extends Context.Service<
ServerEnvironment,
{
readonly getEnvironmentId: Effect.Effect<EnvironmentId>;
readonly getDescriptor: Effect.Effect<ExecutionEnvironmentDescriptor>;
}
>()("t3/environment/ServerEnvironment") {}

function platformOs(platform: NodeJS.Platform): ExecutionEnvironmentDescriptor["platform"]["os"] {
switch (platform) {
case "darwin":
Expand All @@ -38,10 +46,10 @@ function platformArch(
}
}

export const makeServerEnvironment = Effect.fn("makeServerEnvironment")(function* () {
export const make = Effect.gen(function* () {
const fileSystem = yield* FileSystem.FileSystem;
const path = yield* Path.Path;
const serverConfig = yield* ServerConfig;
const serverConfig = yield* ServerConfig.ServerConfig;
const crypto = yield* Crypto.Crypto;
const hostPlatform = yield* HostProcessPlatform;
const hostArchitecture = yield* HostProcessArchitecture;
Expand Down Expand Up @@ -77,9 +85,7 @@ export const makeServerEnvironment = Effect.fn("makeServerEnvironment")(function

const environmentId = EnvironmentId.make(environmentIdRaw);
const cwdBaseName = path.basename(serverConfig.cwd).trim();
const label = yield* resolveServerEnvironmentLabel({
cwdBaseName,
});
const label = yield* resolveServerEnvironmentLabel({ cwdBaseName });

const descriptor: ExecutionEnvironmentDescriptor = {
environmentId,
Expand All @@ -94,12 +100,15 @@ export const makeServerEnvironment = Effect.fn("makeServerEnvironment")(function
},
};

return {
return ServerEnvironment.of({
getEnvironmentId: Effect.succeed(environmentId),
getDescriptor: Effect.succeed(descriptor),
} satisfies ServerEnvironmentShape;
});
});

export const ServerEnvironmentLive = Layer.effect(ServerEnvironment, makeServerEnvironment()).pipe(
Layer.provide(ProcessRunnerLive),
);
/**
* ServerEnvironment is acquired from persisted filesystem and host-process
* state. It intentionally has no fallback Layer.succeed value: callers must
* provide the external platform services and a ServerConfig.
*/
export const layer = Layer.effect(ServerEnvironment, make).pipe(Layer.provide(ProcessRunner.layer));
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ import { afterEach, describe, expect, it } from "@effect/vitest";
import * as Effect from "effect/Effect";
import * as FileSystem from "effect/FileSystem";
import * as Layer from "effect/Layer";
import * as ChildProcessSpawner from "effect/unstable/process/ChildProcessSpawner";
import { HostProcessHostname, HostProcessPlatform } from "@t3tools/shared/hostProcess";
import { vi } from "vite-plus/test";

import * as ProcessRunner from "../../processRunner.ts";
import * as ProcessRunner from "../processRunner.ts";
import { resolveServerEnvironmentLabel } from "./ServerEnvironmentLabel.ts";
import { ChildProcessSpawner } from "effect/unstable/process";

const runMock = vi.fn<ProcessRunner.ProcessRunner["Service"]["run"]>();

Expand Down
Loading
Loading