| author | |
| committer | |
| log | 9c4095aaaa31c5628d40026cbddf094c0f85146d |
| tree | fb5dcc2d84b2da9f247308cbdc804ecbe954aedb |
| parent | 961df8597c4c8149febbc95d0ab52f895ca7ff94 |
- Use generic type parameter for stream errors instead of unknown
- Type ServerShape errors as ServerLifecycleError instead of unknown
- Add error mapping for autoBootstrapProject and httpServerListen
- Type makeServerProviderLayer with ProviderUnsupportedError
- Use Effect.try with decodeUnknownSync in projector to avoid unknown requirements
- Fix test layer types from any/unknown to never
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>6 files changed, 41 insertions(+), 29 deletions(-)
apps/server/src/git/Layers/CodexTextGeneration.ts+2-2| ... | @@ -129,9 +129,9 @@ const makeCodexTextGeneration = Effect.gen(function* () { | ... | @@ -129,9 +129,9 @@ const makeCodexTextGeneration = Effect.gen(function* () { |
| 129 | const path = yield* Path.Path; | 129 | const path = yield* Path.Path; |
| 130 | const commandSpawner = yield* ChildProcessSpawner.ChildProcessSpawner; | 130 | const commandSpawner = yield* ChildProcessSpawner.ChildProcessSpawner; |
| 131 | 131 | ||
| 132 | const readStreamAsString = ( | 132 | const readStreamAsString = <E>( |
| 133 | operation: string, | 133 | operation: string, |
| 134 | stream: Stream.Stream<Uint8Array, unknown>, | 134 | stream: Stream.Stream<Uint8Array, E>, |
| 135 | ): Effect.Effect<string, TextGenerationError> => | 135 | ): Effect.Effect<string, TextGenerationError> => |
| 136 | Effect.gen(function* () { | 136 | Effect.gen(function* () { |
| 137 | let text = ""; | 137 | let text = ""; |
apps/server/src/git/Layers/GitService.ts+2-2| ... | @@ -39,9 +39,9 @@ function toGitCommandError( | ... | @@ -39,9 +39,9 @@ function toGitCommandError( |
| 39 | }); | 39 | }); |
| 40 | } | 40 | } |
| 41 | 41 | ||
| 42 | const collectOutput = Effect.fn(function* ( | 42 | const collectOutput = Effect.fn(function* <E>( |
| 43 | input: Pick<ExecuteGitInput, "operation" | "cwd" | "args">, | 43 | input: Pick<ExecuteGitInput, "operation" | "cwd" | "args">, |
| 44 | stream: Stream.Stream<Uint8Array, unknown>, | 44 | stream: Stream.Stream<Uint8Array, E>, |
| 45 | maxOutputBytes: number, | 45 | maxOutputBytes: number, |
| 46 | ): Effect.fn.Return<string, GitCommandError> { | 46 | ): Effect.fn.Return<string, GitCommandError> { |
| 47 | const decoder = new TextDecoder(); | 47 | const decoder = new TextDecoder(); |
apps/server/src/orchestration/projector.ts+6-3| ... | @@ -40,9 +40,12 @@ function decodeForEvent<A>( | ... | @@ -40,9 +40,12 @@ function decodeForEvent<A>( |
| 40 | eventType: OrchestrationEvent["type"], | 40 | eventType: OrchestrationEvent["type"], |
| 41 | field: string, | 41 | field: string, |
| 42 | ): Effect.Effect<A, OrchestrationProjectorDecodeError> { | 42 | ): Effect.Effect<A, OrchestrationProjectorDecodeError> { |
| 43 | return Schema.decodeUnknownEffect(schema)(value).pipe( | 43 | return Effect.try({ |
| 44 | Effect.mapError(toProjectorDecodeError(`${eventType}:${field}`)), | 44 | // eslint-disable-next-line @typescript-eslint/no-explicit-any |
| 45 | ) as Effect.Effect<A, OrchestrationProjectorDecodeError>; | 45 | try: () => Schema.decodeUnknownSync(schema as any)(value) as A, |
| 46 | catch: (error) => | ||
| 47 | toProjectorDecodeError(`${eventType}:${field}`)(error as Schema.SchemaError), | ||
| 48 | }); | ||
| 46 | } | 49 | } |
| 47 | 50 | ||
| 48 | function retainThreadMessagesAfterRevert( | 51 | function retainThreadMessagesAfterRevert( |
apps/server/src/serverLayers.ts+2-1| ... | @@ -17,6 +17,7 @@ import { ProviderCommandReactorLive } from "./orchestration/Layers/ProviderComma | ... | @@ -17,6 +17,7 @@ import { ProviderCommandReactorLive } from "./orchestration/Layers/ProviderComma |
| 17 | import { OrchestrationProjectionPipelineLive } from "./orchestration/Layers/ProjectionPipeline"; | 17 | import { OrchestrationProjectionPipelineLive } from "./orchestration/Layers/ProjectionPipeline"; |
| 18 | import { OrchestrationProjectionSnapshotQueryLive } from "./orchestration/Layers/ProjectionSnapshotQuery"; | 18 | import { OrchestrationProjectionSnapshotQueryLive } from "./orchestration/Layers/ProjectionSnapshotQuery"; |
| 19 | import { ProviderRuntimeIngestionLive } from "./orchestration/Layers/ProviderRuntimeIngestion"; | 19 | import { ProviderRuntimeIngestionLive } from "./orchestration/Layers/ProviderRuntimeIngestion"; |
| 20 | import { ProviderUnsupportedError } from "./provider/Errors"; | ||
| 20 | import { makeCodexAdapterLive } from "./provider/Layers/CodexAdapter"; | 21 | import { makeCodexAdapterLive } from "./provider/Layers/CodexAdapter"; |
| 21 | import { ProviderAdapterRegistryLive } from "./provider/Layers/ProviderAdapterRegistry"; | 22 | import { ProviderAdapterRegistryLive } from "./provider/Layers/ProviderAdapterRegistry"; |
| 22 | import { makeProviderServiceLive } from "./provider/Layers/ProviderService"; | 23 | import { makeProviderServiceLive } from "./provider/Layers/ProviderService"; |
| ... | @@ -35,7 +36,7 @@ import { NodePtyAdapterLive } from "./terminal/Layers/NodePTY"; | ... | @@ -35,7 +36,7 @@ import { NodePtyAdapterLive } from "./terminal/Layers/NodePTY"; |
| 35 | 36 | ||
| 36 | export function makeServerProviderLayer(): Layer.Layer< | 37 | export function makeServerProviderLayer(): Layer.Layer< |
| 37 | ProviderService, | 38 | ProviderService, |
| 38 | unknown, | 39 | ProviderUnsupportedError, |
| 39 | SqlClient.SqlClient | ServerConfig | 40 | SqlClient.SqlClient | ServerConfig |
| 40 | > { | 41 | > { |
| 41 | return Effect.gen(function* () { | 42 | return Effect.gen(function* () { |
apps/server/src/wsServer.test.ts+2-2| ... | @@ -323,14 +323,14 @@ describe("WebSocket Server", () => { | ... | @@ -323,14 +323,14 @@ describe("WebSocket Server", () => { |
| 323 | 323 | ||
| 324 | async function createTestServer( | 324 | async function createTestServer( |
| 325 | options: { | 325 | options: { |
| 326 | persistenceLayer?: Layer.Layer<SqlClient.SqlClient, any>; | 326 | persistenceLayer?: Layer.Layer<SqlClient.SqlClient, never>; |
| 327 | cwd?: string; | 327 | cwd?: string; |
| 328 | autoBootstrapProjectFromCwd?: boolean; | 328 | autoBootstrapProjectFromCwd?: boolean; |
| 329 | logWebSocketEvents?: boolean; | 329 | logWebSocketEvents?: boolean; |
| 330 | devUrl?: string; | 330 | devUrl?: string; |
| 331 | authToken?: string; | 331 | authToken?: string; |
| 332 | stateDir?: string; | 332 | stateDir?: string; |
| 333 | providerLayer?: Layer.Layer<ProviderService, unknown>; | 333 | providerLayer?: Layer.Layer<ProviderService, never>; |
| 334 | open?: OpenShape; | 334 | open?: OpenShape; |
| 335 | gitManager?: GitManagerShape; | 335 | gitManager?: GitManagerShape; |
| 336 | gitCore?: Pick<GitCoreShape, "listBranches" | "initRepo" | "pullCurrentBranch">; | 336 | gitCore?: Pick<GitCoreShape, "listBranches" | "initRepo" | "pullCurrentBranch">; |
apps/server/src/wsServer.ts+27-19| ... | @@ -66,14 +66,14 @@ export interface ServerShape { | ... | @@ -66,14 +66,14 @@ export interface ServerShape { |
| 66 | */ | 66 | */ |
| 67 | readonly start: Effect.Effect< | 67 | readonly start: Effect.Effect< |
| 68 | http.Server, | 68 | http.Server, |
| 69 | unknown, | 69 | ServerLifecycleError, |
| 70 | Scope.Scope | ServerRuntimeServices | ServerConfig | 70 | Scope.Scope | ServerRuntimeServices | ServerConfig |
| 71 | >; | 71 | >; |
| 72 | 72 | ||
| 73 | /** | 73 | /** |
| 74 | * Wait for process shutdown signals. | 74 | * Wait for process shutdown signals. |
| 75 | */ | 75 | */ |
| 76 | readonly stopSignal: Effect.Effect<unknown, never>; | 76 | readonly stopSignal: Effect.Effect<void, never>; |
| 77 | } | 77 | } |
| 78 | 78 | ||
| 79 | /** | 79 | /** |
| ... | @@ -165,7 +165,7 @@ class RouteRequestError extends Schema.TaggedErrorClass<RouteRequestError>()("Ro | ... | @@ -165,7 +165,7 @@ class RouteRequestError extends Schema.TaggedErrorClass<RouteRequestError>()("Ro |
| 165 | 165 | ||
| 166 | export const createServer = Effect.fn(function* (): Effect.fn.Return< | 166 | export const createServer = Effect.fn(function* (): Effect.fn.Return< |
| 167 | http.Server, | 167 | http.Server, |
| 168 | unknown, // TODO: This should not be unknown | 168 | ServerLifecycleError, |
| 169 | Scope.Scope | ServerRuntimeServices | ServerConfig | 169 | Scope.Scope | ServerRuntimeServices | ServerConfig |
| 170 | > { | 170 | > { |
| 171 | const serverConfig = yield* ServerConfig; | 171 | const serverConfig = yield* ServerConfig; |
| ... | @@ -338,21 +338,27 @@ export const createServer = Effect.fn(function* (): Effect.fn.Return< | ... | @@ -338,21 +338,27 @@ export const createServer = Effect.fn(function* (): Effect.fn.Return< |
| 338 | yield* Scope.provide(orchestrationReactor.start, subscriptionsScope); | 338 | yield* Scope.provide(orchestrationReactor.start, subscriptionsScope); |
| 339 | 339 | ||
| 340 | if (autoBootstrapProjectFromCwd) { | 340 | if (autoBootstrapProjectFromCwd) { |
| 341 | const snapshot = yield* projectionReadModelQuery.getSnapshot(); | 341 | yield* Effect.gen(function* () { |
| 342 | const existing = snapshot.projects.find((project) => project.workspaceRoot === cwd); | 342 | const snapshot = yield* projectionReadModelQuery.getSnapshot(); |
| 343 | if (!existing) { | 343 | const existing = snapshot.projects.find((project) => project.workspaceRoot === cwd); |
| 344 | const createdAt = new Date().toISOString(); | 344 | if (!existing) { |
| 345 | const projectName = path.basename(cwd) || "project"; | 345 | const createdAt = new Date().toISOString(); |
| 346 | yield* orchestrationEngine.dispatch({ | 346 | const projectName = path.basename(cwd) || "project"; |
| 347 | type: "project.create", | 347 | yield* orchestrationEngine.dispatch({ |
| 348 | commandId: CommandId.makeUnsafe(crypto.randomUUID()), | 348 | type: "project.create", |
| 349 | projectId: ProjectId.makeUnsafe(crypto.randomUUID()), | 349 | commandId: CommandId.makeUnsafe(crypto.randomUUID()), |
| 350 | title: projectName, | 350 | projectId: ProjectId.makeUnsafe(crypto.randomUUID()), |
| 351 | workspaceRoot: cwd, | 351 | title: projectName, |
| 352 | defaultModel: "gpt-5-codex", | 352 | workspaceRoot: cwd, |
| 353 | createdAt, | 353 | defaultModel: "gpt-5-codex", |
| 354 | }); | 354 | createdAt, |
| 355 | } | 355 | }); |
| 356 | } | ||
| 357 | }).pipe( | ||
| 358 | Effect.mapError( | ||
| 359 | (cause) => new ServerLifecycleError({ operation: "autoBootstrapProject", cause }), | ||
| 360 | ), | ||
| 361 | ); | ||
| 356 | } | 362 | } |
| 357 | 363 | ||
| 358 | const routeRequest = Effect.fnUntraced(function* (request: WebSocketRequest) { | 364 | const routeRequest = Effect.fnUntraced(function* (request: WebSocketRequest) { |
| ... | @@ -618,7 +624,9 @@ export const createServer = Effect.fn(function* (): Effect.fn.Return< | ... | @@ -618,7 +624,9 @@ export const createServer = Effect.fn(function* (): Effect.fn.Return< |
| 618 | ); | 624 | ); |
| 619 | yield* Effect.addFinalizer(() => Effect.sync(() => unsubscribeTerminalEvents())); | 625 | yield* Effect.addFinalizer(() => Effect.sync(() => unsubscribeTerminalEvents())); |
| 620 | 626 | ||
| 621 | yield* NodeHttpServer.make(() => httpServer, listenOptions); | 627 | yield* NodeHttpServer.make(() => httpServer, listenOptions).pipe( |
| 628 | Effect.mapError((cause) => new ServerLifecycleError({ operation: "httpServerListen", cause })), | ||
| 629 | ); | ||
| 622 | 630 | ||
| 623 | yield* Effect.addFinalizer(() => | 631 | yield* Effect.addFinalizer(() => |
| 624 | Effect.all([ | 632 | Effect.all([ |