diff --git a/apps/server/integration/OrchestrationEngineHarness.integration.ts b/apps/server/integration/OrchestrationEngineHarness.integration.ts index 077b62f89863781fc2c01f4159c680076bea34fa..e28839cd05391b574d563daff3cc89b91b5f2e27 100644 --- a/apps/server/integration/OrchestrationEngineHarness.integration.ts +++ b/apps/server/integration/OrchestrationEngineHarness.integration.ts @@ -3,7 +3,7 @@ import os from "node:os"; import path from "node:path"; import { execFileSync } from "node:child_process"; -import { NodeServices } from "@effect/platform-node"; +import * as NodeServices from "@effect/platform-node/NodeServices"; import { ApprovalRequestId, type OrchestrationEvent, diff --git a/apps/server/integration/providerService.integration.test.ts b/apps/server/integration/providerService.integration.test.ts index 9212a72b2cfaefd976ac4c9a9ad7d68fb218261e..2e9ae06b6143bae0095a975aec1af9ce328a9578 100644 --- a/apps/server/integration/providerService.integration.test.ts +++ b/apps/server/integration/providerService.integration.test.ts @@ -1,6 +1,6 @@ import type { ProviderRuntimeEvent, ProviderSessionId } from "@t3tools/contracts"; import { ThreadId } from "@t3tools/contracts"; -import { NodeServices } from "@effect/platform-node"; +import * as NodeServices from "@effect/platform-node/NodeServices"; import { it, assert } from "@effect/vitest"; import { Effect, FileSystem, Layer, Path, Queue, Stream } from "effect"; diff --git a/apps/server/package.json b/apps/server/package.json index 4978cbcd075a34de986db7a09d5683f08382b8a6..20e804e5bfdc0ae4adc4c34b5e1312826d95e36d 100644 --- a/apps/server/package.json +++ b/apps/server/package.json @@ -18,17 +18,17 @@ "test": "vitest run" }, "dependencies": { - "@effect/platform-node": "4.0.0-beta.10", - "@effect/sql-sqlite-bun": "4.0.0-beta.10", + "@effect/platform-node": "catalog:", + "@effect/sql-sqlite-bun": "catalog:", "@pierre/diffs": "^1.1.0-beta.16", - "effect": "4.0.0-beta.10", + "effect": "catalog:", "node-pty": "^1.1.0", "open": "^10.1.0", "ws": "^8.18.0" }, "devDependencies": { "@effect/language-service": "0.75.1", - "@effect/vitest": "4.0.0-beta.10", + "@effect/vitest": "catalog:", "@t3tools/contracts": "workspace:*", "@t3tools/web": "workspace:*", "@types/bun": "^1.3.9", diff --git a/apps/server/src/checkpointing/Services/CheckpointDiffQuery.ts b/apps/server/src/checkpointing/Services/CheckpointDiffQuery.ts index dfa40cd7511fca54489e7df10d7ae51f02061db8..2204294a435d2abef0e0e35c03e33e1193d9d0db 100644 --- a/apps/server/src/checkpointing/Services/CheckpointDiffQuery.ts +++ b/apps/server/src/checkpointing/Services/CheckpointDiffQuery.ts @@ -46,4 +46,4 @@ export interface CheckpointDiffQueryShape { export class CheckpointDiffQuery extends ServiceMap.Service< CheckpointDiffQuery, CheckpointDiffQueryShape ->()("checkpointing/CheckpointDiffQuery") {} +>()("t3/checkpointing/Services/CheckpointDiffQuery") {} diff --git a/apps/server/src/checkpointing/Services/CheckpointStore.ts b/apps/server/src/checkpointing/Services/CheckpointStore.ts index b004c87fda6552ac400650e67be789a84e9903c4..284198c1456a662f589b8782a357dca4666b05d6 100644 --- a/apps/server/src/checkpointing/Services/CheckpointStore.ts +++ b/apps/server/src/checkpointing/Services/CheckpointStore.ts @@ -96,5 +96,5 @@ export interface CheckpointStoreShape { * CheckpointStore - Service tag for checkpoint persistence and restore operations. */ export class CheckpointStore extends ServiceMap.Service()( - "checkpointing/CheckpointStore", + "t3/checkpointing/Services/CheckpointStore", ) {} diff --git a/apps/server/src/config.ts b/apps/server/src/config.ts index ad011173013c0701e7d898bc8f98c7473ac0654a..a875e6c9562590ff980737d2bee99e94e26a54f3 100644 --- a/apps/server/src/config.ts +++ b/apps/server/src/config.ts @@ -35,7 +35,7 @@ export interface ServerConfigShape { * ServerConfig - Service tag for server runtime configuration. */ export class ServerConfig extends ServiceMap.Service()( - "server/ServerConfig", + "t3/config/ServerConfig", ) {} // Helpers @@ -61,7 +61,7 @@ export interface NetServiceShape { * NetService - Service tag for startup networking helpers. */ export class NetService extends ServiceMap.Service()( - "server/NetService", + "t3/config/NetService", ) { static readonly layer = Layer.succeed(NetService, { findAvailablePort: (preferred) => @@ -102,18 +102,18 @@ export class NetService extends ServiceMap.Service( export const resolveStaticDir = Effect.fn(function* () { const { join, resolve } = yield* Path.Path; - const { stat } = yield* FileSystem.FileSystem; + const { exists } = yield* FileSystem.FileSystem; const bundledClient = resolve(join(import.meta.dirname, "client")); - const bundledStat = yield* stat(join(bundledClient, "index.html")).pipe( - Effect.orElseSucceed(() => undefined), + const bundledStat = yield* exists(join(bundledClient, "index.html")).pipe( + Effect.orElseSucceed(() => false), ); if (bundledStat) { return bundledClient; } const monorepoClient = resolve(join(import.meta.dirname, "../../web/dist")); - const monorepoStat = yield* stat(join(monorepoClient, "index.html")).pipe( - Effect.orElseSucceed(() => undefined), + const monorepoStat = yield* exists(join(monorepoClient, "index.html")).pipe( + Effect.orElseSucceed(() => false), ); if (monorepoStat) { return monorepoClient; diff --git a/apps/server/src/git/Layers/CodexTextGeneration.test.ts b/apps/server/src/git/Layers/CodexTextGeneration.test.ts index bba843110caecd63af3198c06e22e5625dffe973..bbc23fb04097b2ebc1c5bd130976436148bc4f1a 100644 --- a/apps/server/src/git/Layers/CodexTextGeneration.test.ts +++ b/apps/server/src/git/Layers/CodexTextGeneration.test.ts @@ -1,4 +1,4 @@ -import { NodeServices } from "@effect/platform-node"; +import * as NodeServices from "@effect/platform-node/NodeServices"; import { it } from "@effect/vitest"; import { Effect, FileSystem, Layer, Path } from "effect"; import { expect } from "vitest"; diff --git a/apps/server/src/git/Layers/GitCore.test.ts b/apps/server/src/git/Layers/GitCore.test.ts index fb5a1385ace042d5b435bd93964b1f24935f5461..32f9942b920a471f6413abbc2a010fc8844cfb50 100644 --- a/apps/server/src/git/Layers/GitCore.test.ts +++ b/apps/server/src/git/Layers/GitCore.test.ts @@ -1,7 +1,7 @@ import { existsSync } from "node:fs"; import path from "node:path"; -import { NodeServices } from "@effect/platform-node"; +import * as NodeServices from "@effect/platform-node/NodeServices"; import { it } from "@effect/vitest"; import { Effect, FileSystem, Layer, PlatformError, Scope } from "effect"; import { describe, expect, vi } from "vitest"; diff --git a/apps/server/src/git/Layers/GitManager.test.ts b/apps/server/src/git/Layers/GitManager.test.ts index 0d2dec1b05b2be57bba5be608fc01cc008833c77..673de05b7242a60908a40297dfecaddfa60c6a02 100644 --- a/apps/server/src/git/Layers/GitManager.test.ts +++ b/apps/server/src/git/Layers/GitManager.test.ts @@ -1,7 +1,7 @@ import fs from "node:fs"; import path from "node:path"; -import { NodeServices } from "@effect/platform-node"; +import * as NodeServices from "@effect/platform-node/NodeServices"; import { it } from "@effect/vitest"; import { Effect, FileSystem, Layer, PlatformError, Scope } from "effect"; import { expect } from "vitest"; diff --git a/apps/server/src/git/Layers/GitService.test.ts b/apps/server/src/git/Layers/GitService.test.ts index adaf32edd72afb783a840ceca8a9f68cc5bf7f28..9a19af1465d3d025792d99488d11ffae2d4481fc 100644 --- a/apps/server/src/git/Layers/GitService.test.ts +++ b/apps/server/src/git/Layers/GitService.test.ts @@ -1,4 +1,4 @@ -import { NodeServices } from "@effect/platform-node"; +import * as NodeServices from "@effect/platform-node/NodeServices"; import { it, assert } from "@effect/vitest"; import { Effect, Layer } from "effect"; diff --git a/apps/server/src/git/Services/GitCore.ts b/apps/server/src/git/Services/GitCore.ts index 5d43ad19f3ab2f53ac944945e3d8aa9f004ab5b7..4e1b3fc5b143432bfcc7d49d2b444257ce372991 100644 --- a/apps/server/src/git/Services/GitCore.ts +++ b/apps/server/src/git/Services/GitCore.ts @@ -143,4 +143,6 @@ export interface GitCoreShape { /** * GitCore - Service tag for low-level Git repository operations. */ -export class GitCore extends ServiceMap.Service()("git/GitCore") {} +export class GitCore extends ServiceMap.Service()( + "t3/git/Services/GitCore", +) {} diff --git a/apps/server/src/git/Services/GitHubCli.ts b/apps/server/src/git/Services/GitHubCli.ts index 1f63e2ba65767298afb932a60417a7481c3fcda2..db0ed95aa94cf2f6eefa25b85f02021360357c7e 100644 --- a/apps/server/src/git/Services/GitHubCli.ts +++ b/apps/server/src/git/Services/GitHubCli.ts @@ -63,4 +63,6 @@ export interface GitHubCliShape { /** * GitHubCli - Service tag for GitHub CLI process execution. */ -export class GitHubCli extends ServiceMap.Service()("git/GitHubCli") {} +export class GitHubCli extends ServiceMap.Service()( + "t3/git/Services/GitHubCli", +) {} diff --git a/apps/server/src/git/Services/GitManager.ts b/apps/server/src/git/Services/GitManager.ts index 9dfb7b668fb0a4ffa3e7495381d8c14ec1954195..7293b3a5294c0056c3a11c223274364eba20cef6 100644 --- a/apps/server/src/git/Services/GitManager.ts +++ b/apps/server/src/git/Services/GitManager.ts @@ -23,7 +23,9 @@ export interface GitManagerShape { /** * Read current repository Git status plus open PR metadata when available. */ - readonly status: (input: GitStatusInput) => Effect.Effect; + readonly status: ( + input: GitStatusInput, + ) => Effect.Effect; /** * Run a stacked Git action (`commit`, `commit_push`, `commit_push_pr`). @@ -37,5 +39,5 @@ export interface GitManagerShape { * GitManager - Service tag for stacked Git workflow orchestration. */ export class GitManager extends ServiceMap.Service()( - "git/GitManager", + "t3/git/Services/GitManager", ) {} diff --git a/apps/server/src/git/Services/GitService.ts b/apps/server/src/git/Services/GitService.ts index 9bd43f3cc86093c75dd57f37f5aacb89ec053f0d..f43a4e6dcc68d5e493dc42c042842726f954a103 100644 --- a/apps/server/src/git/Services/GitService.ts +++ b/apps/server/src/git/Services/GitService.ts @@ -41,5 +41,5 @@ export interface GitServiceShape { * GitService - Service for Git command execution. */ export class GitService extends ServiceMap.Service()( - "git/GitService", + "t3/git/Services/GitService", ) {} diff --git a/apps/server/src/git/Services/TextGeneration.ts b/apps/server/src/git/Services/TextGeneration.ts index 1a64f2bda79e43badae933f4bee563c2b51d8355..6073553ac85bca7a4072c1a69e8b4864a8f6221b 100644 --- a/apps/server/src/git/Services/TextGeneration.ts +++ b/apps/server/src/git/Services/TextGeneration.ts @@ -67,5 +67,5 @@ export interface TextGenerationShape { * TextGeneration - Service tag for commit and PR text generation. */ export class TextGeneration extends ServiceMap.Service()( - "git/TextGeneration", + "t3/git/Services/TextGeneration", ) {} diff --git a/apps/server/src/keybindings.test.ts b/apps/server/src/keybindings.test.ts index 81e950d2ebc7f75ef0ca5c0080f53669dd0cfbb6..2eb8dac0acfdca8048aea79b256e481d06ac2ae9 100644 --- a/apps/server/src/keybindings.test.ts +++ b/apps/server/src/keybindings.test.ts @@ -1,5 +1,5 @@ import { KeybindingCommand, KeybindingRule, KeybindingsConfig } from "@t3tools/contracts"; -import { NodeServices } from "@effect/platform-node"; +import * as NodeServices from "@effect/platform-node/NodeServices"; import { assert, it } from "@effect/vitest"; import { assertFailure } from "@effect/vitest/utils"; import { Effect, FileSystem, Layer, Logger, Path, Schema } from "effect"; @@ -178,7 +178,10 @@ it.layer(NodeServices.layer)("keybindings", (it) => { return yield* keybindings.loadConfigState; }); - assert.deepEqual(configState.keybindings, compileResolvedKeybindingsConfig(DEFAULT_KEYBINDINGS)); + assert.deepEqual( + configState.keybindings, + compileResolvedKeybindingsConfig(DEFAULT_KEYBINDINGS), + ); assert.deepEqual(configState.issues, [ { kind: "keybindings.malformed-config", @@ -208,10 +211,24 @@ it.layer(NodeServices.layer)("keybindings", (it) => { }); assert.isTrue(configState.keybindings.some((entry) => entry.command === "terminal.toggle")); - assert.isFalse(configState.keybindings.some((entry) => entry.command === "invalid.command")); + assert.isFalse( + configState.keybindings.some( + (entry) => + // @ts-expect-error - invalid command + entry.command === "invalid.command", + ), + ); assert.deepEqual(configState.issues, [ - { kind: "keybindings.invalid-entry", index: 1, message: configState.issues[0]?.message ?? "" }, - { kind: "keybindings.invalid-entry", index: 2, message: configState.issues[1]?.message ?? "" }, + { + kind: "keybindings.invalid-entry", + index: 1, + message: configState.issues[0]?.message ?? "", + }, + { + kind: "keybindings.invalid-entry", + index: 2, + message: configState.issues[1]?.message ?? "", + }, ]); }).pipe(Effect.provide(makeKeybindingsLayer())), ); diff --git a/apps/server/src/keybindings.ts b/apps/server/src/keybindings.ts index b82f83b640f0c2ffc4435a83f022cc66f6ae9bc2..7fdd47f210d9dcec59d3972fd4b002eb9ea6f016 100644 --- a/apps/server/src/keybindings.ts +++ b/apps/server/src/keybindings.ts @@ -498,7 +498,7 @@ export interface KeybindingsShape { * Keybindings - Service tag for keybinding configuration operations. */ export class Keybindings extends ServiceMap.Service()( - "server/Keybindings", + "t3/keybindings", ) {} const makeKeybindings = Effect.gen(function* () { diff --git a/apps/server/src/main.test.ts b/apps/server/src/main.test.ts index 9f3f55d4f9cd597375b5997d405f1916f5c8fcff..a74b6283dc6034d5b84d7a4e5ab1de47dcb0001e 100644 --- a/apps/server/src/main.test.ts +++ b/apps/server/src/main.test.ts @@ -1,5 +1,5 @@ import * as Http from "node:http"; -import { NodeServices } from "@effect/platform-node"; +import * as NodeServices from "@effect/platform-node/NodeServices"; import { assert, it, vi } from "@effect/vitest"; import * as ConfigProvider from "effect/ConfigProvider"; import * as Effect from "effect/Effect"; diff --git a/apps/server/src/main.ts b/apps/server/src/main.ts index 97d237d51dcd8b2b868ee5379e5b0f7e93976e45..55ea71c54bfd4e519bce3861f4204b9422a33b17 100644 --- a/apps/server/src/main.ts +++ b/apps/server/src/main.ts @@ -63,7 +63,9 @@ export interface CliConfigShape { /** * CliConfig - Service tag for startup CLI/runtime helpers. */ -export class CliConfig extends ServiceMap.Service()("server/CliConfig") { +export class CliConfig extends ServiceMap.Service()( + "t3/main/CliConfig", +) { static readonly layer = Layer.effect( CliConfig, Effect.gen(function* () { diff --git a/apps/server/src/open.ts b/apps/server/src/open.ts index 3cb4e9338e2fb755a538c7cb59519a8471425cd6..7e318b042e2210b4675df6637048ca89f51ddd56 100644 --- a/apps/server/src/open.ts +++ b/apps/server/src/open.ts @@ -50,7 +50,7 @@ export interface OpenShape { /** * Open - Service tag for browser/editor launch operations. */ -export class Open extends ServiceMap.Service()("server/Open") {} +export class Open extends ServiceMap.Service()("t3/open") {} // ============================== // Implementations diff --git a/apps/server/src/orchestration/Layers/CheckpointReactor.test.ts b/apps/server/src/orchestration/Layers/CheckpointReactor.test.ts index 0bdd0aae7718bb9ac6d5c3f4b2701677781b257b..8be890c5998ecbfac02c5190f0edc44043a3a0fa 100644 --- a/apps/server/src/orchestration/Layers/CheckpointReactor.test.ts +++ b/apps/server/src/orchestration/Layers/CheckpointReactor.test.ts @@ -14,7 +14,7 @@ import { ThreadId, TurnId, } from "@t3tools/contracts"; -import { NodeServices } from "@effect/platform-node"; +import * as NodeServices from "@effect/platform-node/NodeServices"; import { Effect, Exit, Layer, ManagedRuntime, PubSub, Scope, Stream } from "effect"; import { afterEach, describe, expect, it, vi } from "vitest"; diff --git a/apps/server/src/orchestration/Layers/OrchestrationEngine.ts b/apps/server/src/orchestration/Layers/OrchestrationEngine.ts index ce48f1e1b38a6ba07f03aedfb3d231f56c2872c1..0895cd99c9dd528a1180e27a9e3b3815509cff84 100644 --- a/apps/server/src/orchestration/Layers/OrchestrationEngine.ts +++ b/apps/server/src/orchestration/Layers/OrchestrationEngine.ts @@ -5,7 +5,7 @@ import type { ThreadId, } from "@t3tools/contracts"; import { OrchestrationCommand } from "@t3tools/contracts"; -import { Deferred, Effect, Layer, Option, PubSub, Queue, Stream } from "effect"; +import { Deferred, Effect, Layer, Option, PubSub, Queue, Schema, Stream } from "effect"; import * as SqlClient from "effect/unstable/sql/SqlClient"; import { toPersistenceSqlError } from "../../persistence/Errors.ts"; @@ -181,7 +181,7 @@ const makeOrchestrationEngine = Effect.gen(function* () { ), ); - if (error instanceof OrchestrationCommandInvariantError) { + if (Schema.is(OrchestrationCommandInvariantError)(error)) { const aggregateRef = commandToAggregateRef(envelope.command); yield* commandReceiptRepository .upsert({ diff --git a/apps/server/src/orchestration/Layers/ProjectionPipeline.test.ts b/apps/server/src/orchestration/Layers/ProjectionPipeline.test.ts index bede591860f06979d17620ec7a374e3453c32c2c..7381164c867804151372541a4abbe488a1e51fdc 100644 --- a/apps/server/src/orchestration/Layers/ProjectionPipeline.test.ts +++ b/apps/server/src/orchestration/Layers/ProjectionPipeline.test.ts @@ -10,7 +10,7 @@ import { ThreadId, TurnId, } from "@t3tools/contracts"; -import { NodeServices } from "@effect/platform-node"; +import * as NodeServices from "@effect/platform-node/NodeServices"; import { assert, it } from "@effect/vitest"; import { Effect, Layer } from "effect"; import fs from "node:fs"; diff --git a/apps/server/src/orchestration/Services/CheckpointReactor.ts b/apps/server/src/orchestration/Services/CheckpointReactor.ts index 3e4688b89909e66d82337f024cd4e46cc5dee455..7989341b9de14c26cd335ae88f5c645d0f9332cc 100644 --- a/apps/server/src/orchestration/Services/CheckpointReactor.ts +++ b/apps/server/src/orchestration/Services/CheckpointReactor.ts @@ -31,4 +31,4 @@ export interface CheckpointReactorShape { export class CheckpointReactor extends ServiceMap.Service< CheckpointReactor, CheckpointReactorShape ->()("orchestration/CheckpointReactor") {} +>()("t3/orchestration/Services/CheckpointReactor") {} diff --git a/apps/server/src/orchestration/Services/OrchestrationEngine.ts b/apps/server/src/orchestration/Services/OrchestrationEngine.ts index 4907860cb670d8fb06df0ef2ead2ac2812955c2c..33ac75049f8c9a037f8c2f81df739fa9aa29d1e9 100644 --- a/apps/server/src/orchestration/Services/OrchestrationEngine.ts +++ b/apps/server/src/orchestration/Services/OrchestrationEngine.ts @@ -77,4 +77,4 @@ export interface OrchestrationEngineShape { export class OrchestrationEngineService extends ServiceMap.Service< OrchestrationEngineService, OrchestrationEngineShape ->()("orchestration/Engine") {} +>()("t3/orchestration/Services/OrchestrationEngine/OrchestrationEngineService") {} diff --git a/apps/server/src/orchestration/Services/OrchestrationReactor.ts b/apps/server/src/orchestration/Services/OrchestrationReactor.ts index 384da07b2018769f3050ef250dd049c250ea4698..258ef2d614d81e762fe86eb68c36eacd5c8c1280 100644 --- a/apps/server/src/orchestration/Services/OrchestrationReactor.ts +++ b/apps/server/src/orchestration/Services/OrchestrationReactor.ts @@ -28,4 +28,4 @@ export interface OrchestrationReactorShape { export class OrchestrationReactor extends ServiceMap.Service< OrchestrationReactor, OrchestrationReactorShape ->()("orchestration/OrchestrationReactor") {} +>()("t3/orchestration/Services/OrchestrationReactor") {} diff --git a/apps/server/src/orchestration/Services/ProjectionPipeline.ts b/apps/server/src/orchestration/Services/ProjectionPipeline.ts index 370409a9d42e68e0d505f2b560c52c9f0b994ca5..f8c86bf09b8dd26516d4e7648f47b0c18ead518e 100644 --- a/apps/server/src/orchestration/Services/ProjectionPipeline.ts +++ b/apps/server/src/orchestration/Services/ProjectionPipeline.ts @@ -39,4 +39,4 @@ export interface OrchestrationProjectionPipelineShape { export class OrchestrationProjectionPipeline extends ServiceMap.Service< OrchestrationProjectionPipeline, OrchestrationProjectionPipelineShape ->()("orchestration/ProjectionPipeline") {} +>()("t3/orchestration/Services/ProjectionPipeline/OrchestrationProjectionPipeline") {} diff --git a/apps/server/src/orchestration/Services/ProjectionSnapshotQuery.ts b/apps/server/src/orchestration/Services/ProjectionSnapshotQuery.ts index bd8d57cf548204cb2425a3bcef356116a0849bc3..91e42f02ffd1159e69f78130a455386aa5bd64c5 100644 --- a/apps/server/src/orchestration/Services/ProjectionSnapshotQuery.ts +++ b/apps/server/src/orchestration/Services/ProjectionSnapshotQuery.ts @@ -31,4 +31,4 @@ export interface ProjectionSnapshotQueryShape { export class ProjectionSnapshotQuery extends ServiceMap.Service< ProjectionSnapshotQuery, ProjectionSnapshotQueryShape ->()("orchestration/ProjectionSnapshotQuery") {} +>()("t3/orchestration/Services/ProjectionSnapshotQuery") {} diff --git a/apps/server/src/orchestration/Services/ProviderCommandReactor.ts b/apps/server/src/orchestration/Services/ProviderCommandReactor.ts index 7007e8e0fa9cd89eb6f395f20186453c2a52c150..c394435ba9fbfb8859cf17d5ea2d705bb8c9cc5a 100644 --- a/apps/server/src/orchestration/Services/ProviderCommandReactor.ts +++ b/apps/server/src/orchestration/Services/ProviderCommandReactor.ts @@ -31,4 +31,4 @@ export interface ProviderCommandReactorShape { export class ProviderCommandReactor extends ServiceMap.Service< ProviderCommandReactor, ProviderCommandReactorShape ->()("orchestration/ProviderCommandReactor") {} +>()("t3/orchestration/Services/ProviderCommandReactor") {} diff --git a/apps/server/src/orchestration/Services/ProviderRuntimeIngestion.ts b/apps/server/src/orchestration/Services/ProviderRuntimeIngestion.ts index 13574acf90712ddcba3f06172e3d54a4d7e07b2e..edd1c207c11ad1de8141b52f71673078d75f922e 100644 --- a/apps/server/src/orchestration/Services/ProviderRuntimeIngestion.ts +++ b/apps/server/src/orchestration/Services/ProviderRuntimeIngestion.ts @@ -31,4 +31,4 @@ export interface ProviderRuntimeIngestionShape { export class ProviderRuntimeIngestionService extends ServiceMap.Service< ProviderRuntimeIngestionService, ProviderRuntimeIngestionShape ->()("orchestration/ProviderRuntimeIngestion") {} +>()("t3/orchestration/Services/ProviderRuntimeIngestion/ProviderRuntimeIngestionService") {} diff --git a/apps/server/src/persistence/Layers/OrchestrationEventStore.test.ts b/apps/server/src/persistence/Layers/OrchestrationEventStore.test.ts index a99a307e3cbc806ba04fd4d4657d42dea332c462..77f7ee3cacb743b33d65e21e906339448bda39ec 100644 --- a/apps/server/src/persistence/Layers/OrchestrationEventStore.test.ts +++ b/apps/server/src/persistence/Layers/OrchestrationEventStore.test.ts @@ -1,6 +1,6 @@ import { CommandId, EventId, ProjectId } from "@t3tools/contracts"; import { assert, it } from "@effect/vitest"; -import { Effect, Layer, Stream } from "effect"; +import { Effect, Layer, Schema, Stream } from "effect"; import * as SqlClient from "effect/unstable/sql/SqlClient"; import { PersistenceDecodeError } from "../Errors.ts"; @@ -107,7 +107,7 @@ layer("OrchestrationEventStore", (it) => { ); assert.equal(replayResult._tag, "Failure"); if (replayResult._tag === "Failure") { - assert.ok(replayResult.failure instanceof PersistenceDecodeError); + assert.ok(Schema.is(PersistenceDecodeError)(replayResult.failure)); assert.ok( replayResult.failure.operation.includes( "OrchestrationEventStore.readFromSequence:decodeRows", diff --git a/apps/server/src/persistence/Layers/ProviderSessions.ts b/apps/server/src/persistence/Layers/ProviderSessions.ts index 46f72da57454c53d56917691309413bbc864065c..eb868fa20c2ed690f955b1692a8793332b42fad4 100644 --- a/apps/server/src/persistence/Layers/ProviderSessions.ts +++ b/apps/server/src/persistence/Layers/ProviderSessions.ts @@ -51,14 +51,10 @@ function toValidationError( }); } -function decodeInput( - schema: Schema.Schema, - input: unknown, - operation: string, -): Effect.Effect { +function decodeInput(schema: S, input: unknown, operation: string) { return Schema.decodeUnknownEffect(schema)(input).pipe( Effect.mapError((cause) => toValidationError(operation, cause)), - ) as Effect.Effect; + ); } function toPersistenceError( diff --git a/apps/server/src/persistence/NodeSqliteClient.ts b/apps/server/src/persistence/NodeSqliteClient.ts index cfbcb8b9c50071278989636b1120a538144a1dcb..b121764fccd0d5aedfd6739c1d5463544a135cb1 100644 --- a/apps/server/src/persistence/NodeSqliteClient.ts +++ b/apps/server/src/persistence/NodeSqliteClient.ts @@ -32,7 +32,7 @@ export type TypeId = "~local/sqlite-node/SqliteClient"; /** * SqliteClient - Effect service tag for the sqlite SQL client. */ -export const SqliteClient = ServiceMap.Service("@local/sqlite-node/SqliteClient"); +export const SqliteClient = ServiceMap.Service("t3/persistence/NodeSqliteClient"); export interface SqliteClientConfig { readonly filename: string; diff --git a/apps/server/src/persistence/Services/OrchestrationCommandReceipts.ts b/apps/server/src/persistence/Services/OrchestrationCommandReceipts.ts index c1ec79c9fb760a49948db303c1438ee95767f157..0b8f8e9b5261e5f0a80a3c9a53b66ccd2225bcae 100644 --- a/apps/server/src/persistence/Services/OrchestrationCommandReceipts.ts +++ b/apps/server/src/persistence/Services/OrchestrationCommandReceipts.ts @@ -66,4 +66,4 @@ export interface OrchestrationCommandReceiptRepositoryShape { export class OrchestrationCommandReceiptRepository extends ServiceMap.Service< OrchestrationCommandReceiptRepository, OrchestrationCommandReceiptRepositoryShape ->()("persistence/OrchestrationCommandReceiptRepository") {} +>()("t3/persistence/Services/OrchestrationCommandReceipts/OrchestrationCommandReceiptRepository") {} diff --git a/apps/server/src/persistence/Services/OrchestrationEventStore.ts b/apps/server/src/persistence/Services/OrchestrationEventStore.ts index 6a557c03790328174f9f04f3478ae31a5bb2da98..24febd8fded8dc0360258871c02edac968529e9f 100644 --- a/apps/server/src/persistence/Services/OrchestrationEventStore.ts +++ b/apps/server/src/persistence/Services/OrchestrationEventStore.ts @@ -67,4 +67,4 @@ export interface OrchestrationEventStoreShape { export class OrchestrationEventStore extends ServiceMap.Service< OrchestrationEventStore, OrchestrationEventStoreShape ->()("orchestration/EventStore") {} +>()("t3/persistence/Services/OrchestrationEventStore") {} diff --git a/apps/server/src/persistence/Services/ProjectionCheckpoints.ts b/apps/server/src/persistence/Services/ProjectionCheckpoints.ts index d10c86d816d50f3069b7262944f8b417b8853186..191d0af968495b2d09c16c8fe7ac80cf96d95722 100644 --- a/apps/server/src/persistence/Services/ProjectionCheckpoints.ts +++ b/apps/server/src/persistence/Services/ProjectionCheckpoints.ts @@ -90,4 +90,4 @@ export interface ProjectionCheckpointRepositoryShape { export class ProjectionCheckpointRepository extends ServiceMap.Service< ProjectionCheckpointRepository, ProjectionCheckpointRepositoryShape ->()("persistence/ProjectionCheckpointRepository") {} +>()("t3/persistence/Services/ProjectionCheckpoints/ProjectionCheckpointRepository") {} diff --git a/apps/server/src/persistence/Services/ProjectionPendingApprovals.ts b/apps/server/src/persistence/Services/ProjectionPendingApprovals.ts index 8e0d3736be910aa87ad182dff231df56e2a4de2e..8f4ec5eb4b1a44f6768a96b13a303fd1a95b717e 100644 --- a/apps/server/src/persistence/Services/ProjectionPendingApprovals.ts +++ b/apps/server/src/persistence/Services/ProjectionPendingApprovals.ts @@ -88,4 +88,4 @@ export interface ProjectionPendingApprovalRepositoryShape { export class ProjectionPendingApprovalRepository extends ServiceMap.Service< ProjectionPendingApprovalRepository, ProjectionPendingApprovalRepositoryShape ->()("persistence/ProjectionPendingApprovalRepository") {} +>()("t3/persistence/Services/ProjectionPendingApprovals/ProjectionPendingApprovalRepository") {} diff --git a/apps/server/src/persistence/Services/ProjectionProjects.ts b/apps/server/src/persistence/Services/ProjectionProjects.ts index c3fcea002be1e4938715b1f724e9dc8511922abe..1380a9609a4b9bfb1b8db9076de827d2fc546018 100644 --- a/apps/server/src/persistence/Services/ProjectionProjects.ts +++ b/apps/server/src/persistence/Services/ProjectionProjects.ts @@ -76,4 +76,4 @@ export interface ProjectionProjectRepositoryShape { export class ProjectionProjectRepository extends ServiceMap.Service< ProjectionProjectRepository, ProjectionProjectRepositoryShape ->()("persistence/ProjectionProjectRepository") {} +>()("t3/persistence/Services/ProjectionProjects/ProjectionProjectRepository") {} diff --git a/apps/server/src/persistence/Services/ProjectionState.ts b/apps/server/src/persistence/Services/ProjectionState.ts index 7e3a2f6b9220581369f726aef2a07f19cad2c424..e8ef9ceeb0b1c0b87742468d37bb273f351052b5 100644 --- a/apps/server/src/persistence/Services/ProjectionState.ts +++ b/apps/server/src/persistence/Services/ProjectionState.ts @@ -61,4 +61,4 @@ export interface ProjectionStateRepositoryShape { export class ProjectionStateRepository extends ServiceMap.Service< ProjectionStateRepository, ProjectionStateRepositoryShape ->()("persistence/ProjectionStateRepository") {} +>()("t3/persistence/Services/ProjectionState/ProjectionStateRepository") {} diff --git a/apps/server/src/persistence/Services/ProjectionThreadActivities.ts b/apps/server/src/persistence/Services/ProjectionThreadActivities.ts index 2e5d67909c51181060b6815ff40009f7be359be8..e9e39b49d0704d3cb9b2853718b76ded29cc01b0 100644 --- a/apps/server/src/persistence/Services/ProjectionThreadActivities.ts +++ b/apps/server/src/persistence/Services/ProjectionThreadActivities.ts @@ -77,4 +77,4 @@ export interface ProjectionThreadActivityRepositoryShape { export class ProjectionThreadActivityRepository extends ServiceMap.Service< ProjectionThreadActivityRepository, ProjectionThreadActivityRepositoryShape ->()("persistence/ProjectionThreadActivityRepository") {} +>()("t3/persistence/Services/ProjectionThreadActivities/ProjectionThreadActivityRepository") {} diff --git a/apps/server/src/persistence/Services/ProjectionThreadMessages.ts b/apps/server/src/persistence/Services/ProjectionThreadMessages.ts index 6f59b6a8e4663025d6e8664bff1fa69e8353264d..f358ca573963117cebde3bc8e004a887ecc797f4 100644 --- a/apps/server/src/persistence/Services/ProjectionThreadMessages.ts +++ b/apps/server/src/persistence/Services/ProjectionThreadMessages.ts @@ -76,4 +76,4 @@ export interface ProjectionThreadMessageRepositoryShape { export class ProjectionThreadMessageRepository extends ServiceMap.Service< ProjectionThreadMessageRepository, ProjectionThreadMessageRepositoryShape ->()("persistence/ProjectionThreadMessageRepository") {} +>()("t3/persistence/Services/ProjectionThreadMessages/ProjectionThreadMessageRepository") {} diff --git a/apps/server/src/persistence/Services/ProjectionThreadSessions.ts b/apps/server/src/persistence/Services/ProjectionThreadSessions.ts index 57f460b76503e4545343c65b4fd44cf5ae37b82e..a769cae896935503ecb6512666ca56c210b3ea0c 100644 --- a/apps/server/src/persistence/Services/ProjectionThreadSessions.ts +++ b/apps/server/src/persistence/Services/ProjectionThreadSessions.ts @@ -73,4 +73,4 @@ export interface ProjectionThreadSessionRepositoryShape { export class ProjectionThreadSessionRepository extends ServiceMap.Service< ProjectionThreadSessionRepository, ProjectionThreadSessionRepositoryShape ->()("persistence/ProjectionThreadSessionRepository") {} +>()("t3/persistence/Services/ProjectionThreadSessions/ProjectionThreadSessionRepository") {} diff --git a/apps/server/src/persistence/Services/ProjectionThreads.ts b/apps/server/src/persistence/Services/ProjectionThreads.ts index 94099128558a12c44845ffa8a3967cb8da1bb83e..512c72586a870747bec6b495e7a2c1a8bb0800bc 100644 --- a/apps/server/src/persistence/Services/ProjectionThreads.ts +++ b/apps/server/src/persistence/Services/ProjectionThreads.ts @@ -82,4 +82,4 @@ export interface ProjectionThreadRepositoryShape { export class ProjectionThreadRepository extends ServiceMap.Service< ProjectionThreadRepository, ProjectionThreadRepositoryShape ->()("persistence/ProjectionThreadRepository") {} +>()("t3/persistence/Services/ProjectionThreads/ProjectionThreadRepository") {} diff --git a/apps/server/src/persistence/Services/ProjectionTurns.ts b/apps/server/src/persistence/Services/ProjectionTurns.ts index 8933c2c7cd7fc6c91c65aa875f920120ff76cb83..1c791342ff529896b6d7d6c3e725ea665058c266 100644 --- a/apps/server/src/persistence/Services/ProjectionTurns.ts +++ b/apps/server/src/persistence/Services/ProjectionTurns.ts @@ -158,4 +158,4 @@ export interface ProjectionTurnRepositoryShape { export class ProjectionTurnRepository extends ServiceMap.Service< ProjectionTurnRepository, ProjectionTurnRepositoryShape ->()("persistence/ProjectionTurnRepository") {} +>()("t3/persistence/Services/ProjectionTurns/ProjectionTurnRepository") {} diff --git a/apps/server/src/persistence/Services/ProviderSessionRuntime.ts b/apps/server/src/persistence/Services/ProviderSessionRuntime.ts index 77ad0ff698ca4deef168acd2e324fd8cf112b726..e0727a8acd92a23fc96659721eae862a403cca4a 100644 --- a/apps/server/src/persistence/Services/ProviderSessionRuntime.ts +++ b/apps/server/src/persistence/Services/ProviderSessionRuntime.ts @@ -84,4 +84,4 @@ export interface ProviderSessionRuntimeRepositoryShape { export class ProviderSessionRuntimeRepository extends ServiceMap.Service< ProviderSessionRuntimeRepository, ProviderSessionRuntimeRepositoryShape ->()("persistence/ProviderSessionRuntimeRepository") {} +>()("t3/persistence/Services/ProviderSessionRuntime/ProviderSessionRuntimeRepository") {} diff --git a/apps/server/src/persistence/Services/ProviderSessions.ts b/apps/server/src/persistence/Services/ProviderSessions.ts index 54c34e434802bc03966db8bdb7b20661d8a2c1aa..33336a53ac7e3112d3f2c852ffef00524bacffe7 100644 --- a/apps/server/src/persistence/Services/ProviderSessions.ts +++ b/apps/server/src/persistence/Services/ProviderSessions.ts @@ -78,4 +78,4 @@ export interface ProviderSessionRepositoryShape { export class ProviderSessionRepository extends ServiceMap.Service< ProviderSessionRepository, ProviderSessionRepositoryShape ->()("persistence/ProviderSessionRepository") {} +>()("t3/persistence/Services/ProviderSessions/ProviderSessionRepository") {} diff --git a/apps/server/src/provider/Layers/ProviderAdapterRegistry.test.ts b/apps/server/src/provider/Layers/ProviderAdapterRegistry.test.ts index 48c1a76538b68bd4e07bf67f4fab4b5e02a1bc3a..29cfe9b5f3e2a610920b1ff9f680ef884facea15 100644 --- a/apps/server/src/provider/Layers/ProviderAdapterRegistry.test.ts +++ b/apps/server/src/provider/Layers/ProviderAdapterRegistry.test.ts @@ -8,7 +8,7 @@ import { CodexAdapter, CodexAdapterShape } from "../Services/CodexAdapter.ts"; import { ProviderAdapterRegistry } from "../Services/ProviderAdapterRegistry.ts"; import { ProviderAdapterRegistryLive } from "./ProviderAdapterRegistry.ts"; import { ProviderUnsupportedError } from "../Errors.ts"; -import { NodeServices } from "@effect/platform-node"; +import * as NodeServices from "@effect/platform-node/NodeServices"; const fakeCodexAdapter: CodexAdapterShape = { provider: "codex", diff --git a/apps/server/src/provider/Layers/ProviderService.test.ts b/apps/server/src/provider/Layers/ProviderService.test.ts index 7dcd739619ec19b9037c105a58047aec8174a3d5..63262044d01338cfeb29665ac1df8a3c66768aea 100644 --- a/apps/server/src/provider/Layers/ProviderService.test.ts +++ b/apps/server/src/provider/Layers/ProviderService.test.ts @@ -37,7 +37,7 @@ import { ProviderService } from "../Services/ProviderService.ts"; import { ProviderSessionDirectory } from "../Services/ProviderSessionDirectory.ts"; import { makeProviderServiceLive } from "./ProviderService.ts"; import { ProviderSessionDirectoryLive } from "./ProviderSessionDirectory.ts"; -import { NodeServices } from "@effect/platform-node"; +import * as NodeServices from "@effect/platform-node/NodeServices"; import { ProviderSessionRuntimeRepositoryLive } from "../../persistence/Layers/ProviderSessionRuntime.ts"; import { ProviderSessionRuntimeRepository } from "../../persistence/Services/ProviderSessionRuntime.ts"; import { diff --git a/apps/server/src/provider/Layers/ProviderSessionDirectory.test.ts b/apps/server/src/provider/Layers/ProviderSessionDirectory.test.ts index a892c6f0fc7c82af984dbb1bb23c2187657f9c99..0b223c2c7813c4c0668d57f523f0aa3b8853d0fc 100644 --- a/apps/server/src/provider/Layers/ProviderSessionDirectory.test.ts +++ b/apps/server/src/provider/Layers/ProviderSessionDirectory.test.ts @@ -2,7 +2,7 @@ import fs from "node:fs"; import os from "node:os"; import path from "node:path"; -import { NodeServices } from "@effect/platform-node"; +import * as NodeServices from "@effect/platform-node/NodeServices"; import { ProviderSessionId, ProviderThreadId, ThreadId } from "@t3tools/contracts"; import { it, assert } from "@effect/vitest"; import { assertFailure, assertSome } from "@effect/vitest/utils"; diff --git a/apps/server/src/provider/Services/CodexAdapter.ts b/apps/server/src/provider/Services/CodexAdapter.ts index 9eac2c994d0220601468660458747afa2ebc7a90..c9f944bb96ad0879af68bcbbb7d4e4ba29ef6667 100644 --- a/apps/server/src/provider/Services/CodexAdapter.ts +++ b/apps/server/src/provider/Services/CodexAdapter.ts @@ -26,5 +26,5 @@ export interface CodexAdapterShape extends ProviderAdapterShape()( - "provider/CodexAdapter", + "t3/provider/Services/CodexAdapter", ) {} diff --git a/apps/server/src/provider/Services/ProviderAdapterRegistry.ts b/apps/server/src/provider/Services/ProviderAdapterRegistry.ts index 5591742aee3f02f7ed3e7c7022c882da03867ca3..490c4d3d143e3ac17cfc252ade68d6fe7669cfa4 100644 --- a/apps/server/src/provider/Services/ProviderAdapterRegistry.ts +++ b/apps/server/src/provider/Services/ProviderAdapterRegistry.ts @@ -37,6 +37,6 @@ export interface ProviderAdapterRegistryShape { export class ProviderAdapterRegistry extends ServiceMap.Service< ProviderAdapterRegistry, ProviderAdapterRegistryShape ->()("provider/ProviderAdapterRegistry") {} +>()("t3/provider/Services/ProviderAdapterRegistry") {} // Dummy comment for workflow testing. diff --git a/apps/server/src/provider/Services/ProviderService.ts b/apps/server/src/provider/Services/ProviderService.ts index cbe45fe6affc7affc6a162c46bea3e71d082dfde..656d25a9a7bf1c24e03a0756c0bff2fc73877802 100644 --- a/apps/server/src/provider/Services/ProviderService.ts +++ b/apps/server/src/provider/Services/ProviderService.ts @@ -105,5 +105,5 @@ export interface ProviderServiceShape { * ProviderService - Service tag for provider orchestration. */ export class ProviderService extends ServiceMap.Service()( - "provider/ProviderService", + "t3/provider/Services/ProviderService", ) {} diff --git a/apps/server/src/provider/Services/ProviderSessionDirectory.ts b/apps/server/src/provider/Services/ProviderSessionDirectory.ts index 2d3c2397627eca8bd4cba7bd4407b96233650521..67628dfa33d39ba9597099fca06c2d1aef736b89 100644 --- a/apps/server/src/provider/Services/ProviderSessionDirectory.ts +++ b/apps/server/src/provider/Services/ProviderSessionDirectory.ts @@ -99,4 +99,4 @@ export interface ProviderSessionDirectoryShape { export class ProviderSessionDirectory extends ServiceMap.Service< ProviderSessionDirectory, ProviderSessionDirectoryShape ->()("provider/ProviderSessionDirectory") {} +>()("t3/provider/Services/ProviderSessionDirectory") {} diff --git a/apps/server/src/serverLayers.ts b/apps/server/src/serverLayers.ts index 8fc259a6891635a397d230a628c417bae147fc87..3d37cc105d1b7e007065fb43ebcaa276c4370778 100644 --- a/apps/server/src/serverLayers.ts +++ b/apps/server/src/serverLayers.ts @@ -1,6 +1,6 @@ import path from "node:path"; -import { NodeServices } from "@effect/platform-node"; +import * as NodeServices from "@effect/platform-node/NodeServices"; import { Effect, Layer } from "effect"; import * as SqlClient from "effect/unstable/sql/SqlClient"; diff --git a/apps/server/src/terminal/Layers/Manager.test.ts b/apps/server/src/terminal/Layers/Manager.test.ts index ca4ea6b196e7287df81557d6a829d5b23237e3e8..304b49d42b7905ca381154b1c5a97e878ec42b97 100644 --- a/apps/server/src/terminal/Layers/Manager.test.ts +++ b/apps/server/src/terminal/Layers/Manager.test.ts @@ -9,7 +9,13 @@ import { } from "@t3tools/contracts"; import { afterEach, describe, expect, it } from "vitest"; -import type { PtyAdapterShape, PtyExitEvent, PtyProcess, PtySpawnInput } from "../Services/PTY"; +import { + PtySpawnError, + type PtyAdapterShape, + type PtyExitEvent, + type PtyProcess, + type PtySpawnInput, +} from "../Services/PTY"; import { TerminalManagerRuntime } from "./Manager"; import { Effect, Encoding } from "effect"; @@ -69,14 +75,33 @@ class FakePtyAdapter implements PtyAdapterShape { readonly spawnFailures: Error[] = []; private nextPid = 9000; - spawn(input: PtySpawnInput): Effect.Effect { + constructor(private readonly mode: "sync" | "async" = "sync") {} + + spawn(input: PtySpawnInput): Effect.Effect { this.spawnInputs.push(input); const failure = this.spawnFailures.shift(); if (failure) { - throw failure; + return Effect.fail( + new PtySpawnError({ + adapter: "fake", + message: "Failed to spawn PTY process", + cause: failure, + }), + ); } const process = new FakePtyProcess(this.nextPid++); this.processes.push(process); + if (this.mode === "async") { + return Effect.tryPromise({ + try: async () => process, + catch: (cause) => + new PtySpawnError({ + adapter: "fake", + message: "Failed to spawn PTY process", + cause, + }), + }); + } return Effect.succeed(process); } } @@ -150,11 +175,12 @@ describe("TerminalManager", () => { subprocessPollIntervalMs?: number; processKillGraceMs?: number; maxRetainedInactiveSessions?: number; + ptyAdapter?: FakePtyAdapter; } = {}, ) { const logsDir = fs.mkdtempSync(path.join(os.tmpdir(), "t3code-terminal-")); tempDirs.push(logsDir); - const ptyAdapter = new FakePtyAdapter(); + const ptyAdapter = options.ptyAdapter ?? new FakePtyAdapter(); const manager = new TerminalManagerRuntime({ logsDir, ptyAdapter, @@ -189,6 +215,18 @@ describe("TerminalManager", () => { manager.dispose(); }); + it("supports asynchronous PTY spawn effects", async () => { + const { manager, ptyAdapter } = makeManager(5, { ptyAdapter: new FakePtyAdapter("async") }); + + const snapshot = await manager.open(openInput()); + + expect(snapshot.status).toBe("running"); + expect(ptyAdapter.spawnInputs).toHaveLength(1); + expect(ptyAdapter.processes).toHaveLength(1); + + manager.dispose(); + }); + it("forwards write and resize to active pty process", async () => { const { manager, ptyAdapter } = makeManager(); await manager.open(openInput()); diff --git a/apps/server/src/terminal/Layers/Manager.ts b/apps/server/src/terminal/Layers/Manager.ts index e841d33e17de0478318c5b046b362c6b3ddf5390..de024ce400f82cdd0ac196d9e1fe6d0526d17ee4 100644 --- a/apps/server/src/terminal/Layers/Manager.ts +++ b/apps/server/src/terminal/Layers/Manager.ts @@ -117,8 +117,43 @@ function resolveShellCandidates(shellResolver: () => string): ShellCandidate[] { } function isRetryableShellSpawnError(error: unknown): boolean { - if (!(error instanceof Error)) return false; - const message = error.message.toLowerCase(); + const queue: unknown[] = [error]; + const seen = new Set(); + const messages: string[] = []; + + while (queue.length > 0) { + const current = queue.shift(); + if (!current || seen.has(current)) { + continue; + } + seen.add(current); + + if (typeof current === "string") { + messages.push(current); + continue; + } + + if (current instanceof Error) { + messages.push(current.message); + const cause = (current as { cause?: unknown }).cause; + if (cause) { + queue.push(cause); + } + continue; + } + + if (typeof current === "object") { + const value = current as { message?: unknown; cause?: unknown }; + if (typeof value.message === "string") { + messages.push(value.message); + } + if (value.cause) { + queue.push(value.cause); + } + } + } + + const message = messages.join(" ").toLowerCase(); return ( message.includes("posix_spawnp failed") || message.includes("enoent") || @@ -354,7 +389,7 @@ export class TerminalManagerRuntime extends EventEmitter }; this.sessions.set(sessionKey, session); this.evictInactiveSessionsIfNeeded(); - this.startSession(session, { ...input, cols, rows }, "started"); + await this.startSession(session, { ...input, cols, rows }, "started"); return this.snapshot(session); } @@ -380,7 +415,7 @@ export class TerminalManagerRuntime extends EventEmitter } if (!existing.process) { - this.startSession(existing, { ...input, cols: targetCols, rows: targetRows }, "started"); + await this.startSession(existing, { ...input, cols: targetCols, rows: targetRows }, "started"); return this.snapshot(existing); } @@ -477,7 +512,7 @@ export class TerminalManagerRuntime extends EventEmitter session.history = ""; await this.persistHistory(input.threadId, input.terminalId, session.history); - this.startSession(session, { ...input, cols, rows }, "restarted"); + await this.startSession(session, { ...input, cols, rows }, "restarted"); return this.snapshot(session); }); } @@ -528,11 +563,11 @@ export class TerminalManagerRuntime extends EventEmitter this.persistQueues.clear(); } - private startSession( + private async startSession( session: TerminalSessionState, input: TerminalStartInput, eventType: "started" | "restarted", - ): void { + ): Promise { this.stopProcess(session); session.status = "starting"; @@ -551,26 +586,46 @@ export class TerminalManagerRuntime extends EventEmitter const terminalEnv = createTerminalSpawnEnv(process.env, session.runtimeEnv); let lastSpawnError: unknown = null; - for (const candidate of shellCandidates) { + const spawnWithCandidate = (candidate: ShellCandidate) => + Effect.runPromise( + this.ptyAdapter.spawn({ + shell: candidate.shell, + ...(candidate.args ? { args: candidate.args } : {}), + cwd: session.cwd, + cols: session.cols, + rows: session.rows, + env: terminalEnv, + }), + ); + + const trySpawn = async ( + candidates: ShellCandidate[], + index = 0, + ): Promise<{ process: PtyProcess; shellLabel: string } | null> => { + if (index >= candidates.length) { + return null; + } + const candidate = candidates[index]; + if (!candidate) { + return null; + } + try { - ptyProcess = Effect.runSync( - this.ptyAdapter.spawn({ - shell: candidate.shell, - ...(candidate.args ? { args: candidate.args } : {}), - cwd: session.cwd, - cols: session.cols, - rows: session.rows, - env: terminalEnv, - }), - ); - startedShell = formatShellCandidate(candidate); - break; + const process = await spawnWithCandidate(candidate); + return { process, shellLabel: formatShellCandidate(candidate) }; } catch (error) { lastSpawnError = error; if (!isRetryableShellSpawnError(error)) { throw error; } + return trySpawn(candidates, index + 1); } + }; + + const spawnResult = await trySpawn(shellCandidates); + if (spawnResult) { + ptyProcess = spawnResult.process; + startedShell = spawnResult.shellLabel; } if (!ptyProcess) { diff --git a/apps/server/src/terminal/Layers/NodePTY.test.ts b/apps/server/src/terminal/Layers/NodePTY.test.ts index 71a71ffe3fa224bda533a08bfa4b330dee0c4436..58fcc70e4e56fcbd494ce8eea9f1d1b6b9985848 100644 --- a/apps/server/src/terminal/Layers/NodePTY.test.ts +++ b/apps/server/src/terminal/Layers/NodePTY.test.ts @@ -2,7 +2,7 @@ import { FileSystem, Path, Effect } from "effect"; import { assert, it } from "@effect/vitest"; import { ensureNodePtySpawnHelperExecutable } from "./NodePTY"; -import { NodeServices } from "@effect/platform-node"; +import * as NodeServices from "@effect/platform-node/NodeServices"; it.layer(NodeServices.layer)("ensureNodePtySpawnHelperExecutable", (it) => { it.effect("adds executable bits when helper exists but is not executable", () => diff --git a/apps/server/src/terminal/Layers/NodePTY.ts b/apps/server/src/terminal/Layers/NodePTY.ts index c002397a2901d624665c2e668bb212751e659276..60a7ab6220898d5716edde9167e96a4632935544 100644 --- a/apps/server/src/terminal/Layers/NodePTY.ts +++ b/apps/server/src/terminal/Layers/NodePTY.ts @@ -37,11 +37,12 @@ export const ensureNodePtySpawnHelperExecutable = Effect.fn(function* (explicitP didEnsureSpawnHelperExecutable = true; } - const stat = yield* fs.stat(helperPath); - const mode = stat.mode & 0o777; - if ((mode & 0o111) === 0) { - yield* fs.chmod(helperPath, mode | 0o111); + if (!(yield* fs.exists(helperPath))) { + return; } + + // Best-effort: avoid FileSystem.stat in packaged mode where some fs metadata can be missing. + yield* fs.chmod(helperPath, 0o755).pipe(Effect.orElseSucceed(() => undefined)); }); class NodePtyProcess implements PtyProcess { diff --git a/apps/server/src/terminal/Services/Manager.ts b/apps/server/src/terminal/Services/Manager.ts index d342a75da1fb8ed36990c2957a026e76a5877570..9cff3239919ad6b336a4ba9d2c776773d56b7c84 100644 --- a/apps/server/src/terminal/Services/Manager.ts +++ b/apps/server/src/terminal/Services/Manager.ts @@ -113,5 +113,5 @@ export interface TerminalManagerShape { * TerminalManager - Service tag for terminal session orchestration. */ export class TerminalManager extends ServiceMap.Service()( - "terminal/TerminalManager", + "t3/terminal/Services/Manager/TerminalManager", ) {} diff --git a/apps/server/src/terminal/Services/PTY.ts b/apps/server/src/terminal/Services/PTY.ts index 501eb4ee7a7da34e92446b2c7ea23707065510e8..dc164b2621f00206a0343152f4e4381e14ae5dd4 100644 --- a/apps/server/src/terminal/Services/PTY.ts +++ b/apps/server/src/terminal/Services/PTY.ts @@ -6,7 +6,16 @@ * * @module PtyAdapter */ -import { Effect, ServiceMap } from "effect"; +import { Effect, Schema, ServiceMap } from "effect"; + +/** + * PtyError - Error type for PTY adapter operations. + */ +export class PtySpawnError extends Schema.TaggedErrorClass()("PtySpawnError", { + adapter: Schema.String, + message: Schema.String, + cause: Schema.optional(Schema.Defect), +}) {} export interface PtyExitEvent { exitCode: number; @@ -38,12 +47,12 @@ export interface PtyAdapterShape { /** * Spawn a PTY process for a terminal session. */ - spawn(input: PtySpawnInput): Effect.Effect; + spawn(input: PtySpawnInput): Effect.Effect; } /** * PtyAdapter - Service tag for PTY process integration. */ export class PtyAdapter extends ServiceMap.Service()( - "terminal/PtyAdapter", + "t3/terminal/Services/PTY/PtyAdapter", ) {} diff --git a/apps/server/src/wsServer.ts b/apps/server/src/wsServer.ts index 03e3e11e93cd5af8140f141909aceae28885bf4e..0a17b0dc4266ac40fbef3ffd0788f45db1f0890c 100644 --- a/apps/server/src/wsServer.ts +++ b/apps/server/src/wsServer.ts @@ -23,7 +23,7 @@ import { WsPush, WsResponse, } from "@t3tools/contracts"; -import { NodeHttpServer } from "@effect/platform-node"; +import * as NodeHttpServer from "@effect/platform-node/NodeHttpServer"; import { Cause, Effect, Exit, Layer, Ref, Schema, Scope, ServiceMap, Stream, Struct } from "effect"; import { WebSocketServer, type WebSocket } from "ws"; @@ -79,7 +79,7 @@ export interface ServerShape { /** * Server - Service tag for HTTP/WebSocket lifecycle management. */ -export class Server extends ServiceMap.Service()("server/Server") {} +export class Server extends ServiceMap.Service()("t3/wsServer/Server") {} const isServerNotRunningError = (error: unknown): boolean => { if (!(error instanceof Error)) return false; @@ -320,22 +320,18 @@ export const createServer = Effect.fn(function* (): Effect.fn.Return< yield* Effect.addFinalizer(() => Scope.close(subscriptionsScope, Exit.void)); yield* Stream.runForEach(orchestrationEngine.streamDomainEvents, (event) => - Effect.gen(function* () { - yield* broadcastPush({ - type: "push", - channel: ORCHESTRATION_WS_CHANNELS.domainEvent, - data: event, - }); + broadcastPush({ + type: "push", + channel: ORCHESTRATION_WS_CHANNELS.domainEvent, + data: event, }), ).pipe(Effect.forkIn(subscriptionsScope)); yield* Stream.runForEach(keybindingsManager.changes, (event) => - Effect.gen(function* () { - yield* broadcastPush({ - type: "push", - channel: WS_CHANNELS.serverConfigUpdated, - data: event, - }); + broadcastPush({ + type: "push", + channel: WS_CHANNELS.serverConfigUpdated, + data: event, }), ).pipe(Effect.forkIn(subscriptionsScope)); diff --git a/apps/server/tsconfig.json b/apps/server/tsconfig.json index db897c75cadad619856b4dd0a49ea48536cea82e..dd544c7f15ad4831cd9a4c7cba09c4c89d85fbe0 100644 --- a/apps/server/tsconfig.json +++ b/apps/server/tsconfig.json @@ -8,7 +8,14 @@ "allowImportingTsExtensions": true, "plugins": [ { - "name": "@effect/language-service" + "name": "@effect/language-service", + "namespaceImportPackages": ["@effect/platform-node"], + "diagnosticSeverity": { + "importFromBarrel": "error", + "anyUnknownInErrorContext": "warning", + "instanceOfSchema": "warning", + "deterministicKeys": "warning" + } } ] }, diff --git a/apps/web/package.json b/apps/web/package.json index e825c6f26b338ccba06d53bcf674be5986b55ad4..705b6776cfc01be3745a28ee4e492971a2ac08bd 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -21,7 +21,7 @@ "@xterm/addon-fit": "^0.11.0", "@xterm/xterm": "^6.0.0", "class-variance-authority": "^0.7.1", - "effect": "4.0.0-beta.10", + "effect": "catalog:", "highlight.js": "^11.11.1", "lucide-react": "^0.564.0", "react": "^19.0.0", @@ -32,6 +32,7 @@ "tailwind-merge": "^3.4.0" }, "devDependencies": { + "@effect/language-service": "catalog:", "@tailwindcss/vite": "^4.0.0", "@tanstack/router-plugin": "^1.161.0", "@types/react": "^19.0.0", diff --git a/apps/web/src/components/DiffWorkerPoolProvider.tsx b/apps/web/src/components/DiffWorkerPoolProvider.tsx index b4f0672deed4a2fa7b805093f5183df92e028a2b..176ab44d00e82431c893e556cf81319e829571e4 100644 --- a/apps/web/src/components/DiffWorkerPoolProvider.tsx +++ b/apps/web/src/components/DiffWorkerPoolProvider.tsx @@ -1,4 +1,5 @@ import { WorkerPoolContextProvider } from "@pierre/diffs/react"; +import DiffsWorker from "@pierre/diffs/worker/worker.js?worker"; import { useMemo, type ReactNode } from "react"; export function DiffWorkerPoolProvider({ children }: { children?: ReactNode }) { @@ -11,8 +12,7 @@ export function DiffWorkerPoolProvider({ children }: { children?: ReactNode }) { return ( - new Worker(new URL("../workers/diffs.worker.ts", import.meta.url), { type: "module" }), + workerFactory: () => new DiffsWorker(), poolSize: workerPoolSize, totalASTLRUCacheSize: 240, }} diff --git a/apps/web/src/workers/diffs.worker.ts b/apps/web/src/workers/diffs.worker.ts deleted file mode 100644 index 3cbb6a35087fbe93bc5a044f0747754410952441..0000000000000000000000000000000000000000 --- a/apps/web/src/workers/diffs.worker.ts +++ /dev/null @@ -1 +0,0 @@ -import "@pierre/diffs/worker/worker.js"; diff --git a/apps/web/tsconfig.json b/apps/web/tsconfig.json index bc4213c0d2046f36c8a62c4d7c045fbfea9ab0dd..a500ba56c1846a8952b243fbc70604737a8ffe65 100644 --- a/apps/web/tsconfig.json +++ b/apps/web/tsconfig.json @@ -7,7 +7,19 @@ "types": ["vite/client"], "paths": { "~/*": ["./src/*"] - } + }, + "plugins": [ + { + "name": "@effect/language-service", + "namespaceImportPackages": ["@effect/platform-node"], + "diagnosticSeverity": { + "importFromBarrel": "error", + "anyUnknownInErrorContext": "warning", + "instanceOfSchema": "warning", + "deterministicKeys": "warning" + } + } + ] }, "include": ["src", "vite.config.ts"] } diff --git a/bun.lock b/bun.lock index 485ad08a917eebe991de57377c5dcd9783d42277..5ff1864ee7e1ec6eff302373488398c635c6ffbb 100644 --- a/bun.lock +++ b/bun.lock @@ -35,17 +35,17 @@ "t3": "./dist/index.mjs", }, "dependencies": { - "@effect/platform-node": "4.0.0-beta.10", - "@effect/sql-sqlite-bun": "4.0.0-beta.10", + "@effect/platform-node": "catalog:", + "@effect/sql-sqlite-bun": "catalog:", "@pierre/diffs": "^1.1.0-beta.16", - "effect": "4.0.0-beta.10", + "effect": "catalog:", "node-pty": "^1.1.0", "open": "^10.1.0", "ws": "^8.18.0", }, "devDependencies": { "@effect/language-service": "0.75.1", - "@effect/vitest": "4.0.0-beta.10", + "@effect/vitest": "catalog:", "@t3tools/contracts": "workspace:*", "@t3tools/web": "workspace:*", "@types/bun": "^1.3.9", @@ -70,7 +70,7 @@ "@xterm/addon-fit": "^0.11.0", "@xterm/xterm": "^6.0.0", "class-variance-authority": "^0.7.1", - "effect": "4.0.0-beta.10", + "effect": "catalog:", "highlight.js": "^11.11.1", "lucide-react": "^0.564.0", "react": "^19.0.0", @@ -81,6 +81,7 @@ "tailwind-merge": "^3.4.0", }, "devDependencies": { + "@effect/language-service": "catalog:", "@tailwindcss/vite": "^4.0.0", "@tanstack/router-plugin": "^1.161.0", "@types/react": "^19.0.0", @@ -96,16 +97,23 @@ "name": "@t3tools/contracts", "version": "0.0.0", "dependencies": { - "effect": "4.0.0-beta.10", + "effect": "catalog:", }, "devDependencies": { "@effect/language-service": "0.75.1", - "@effect/vitest": "4.0.0-beta.10", + "@effect/vitest": "catalog:", "tsdown": "^0.20.3", "typescript": "^5.7.3", }, }, }, + "catalog": { + "@effect/language-service": "0.75.1", + "@effect/platform-node": "https://pkg.pr.new/Effect-TS/effect-smol/@effect/platform-node@f591738", + "@effect/sql-sqlite-bun": "https://pkg.pr.new/Effect-TS/effect-smol/@effect/sql-sqlite-bun@f591738", + "@effect/vitest": "https://pkg.pr.new/Effect-TS/effect-smol/@effect/vitest@f591738", + "effect": "https://pkg.pr.new/Effect-TS/effect-smol/effect@f591738", + }, "packages": { "@babel/code-frame": ["@babel/code-frame@7.29.0", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.28.5", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw=="], @@ -157,13 +165,13 @@ "@effect/language-service": ["@effect/language-service@0.75.1", "", { "bin": { "effect-language-service": "cli.js" } }, "sha512-g9xD2tAQgRFpYC2YgpZq02VeSL5fBbFJ0B/g1o+14NuNmwtaYJc7SjiLWAA9eyhJHosNrn6h1Ye+Kx6j5mN0AA=="], - "@effect/platform-node": ["@effect/platform-node@4.0.0-beta.10", "", { "dependencies": { "@effect/platform-node-shared": "^4.0.0-beta.10", "mime": "^4.1.0", "undici": "^7.20.0" }, "peerDependencies": { "effect": "^4.0.0-beta.10", "ioredis": "^5.7.0" } }, "sha512-ikZ/uyos5okYAHE+wWOwZkHgriFUTY3aY8Kdqbj/ro8xw0z9YXWN4fbyQAZUWkBcieDKS36N+QfHRMPG4fEWRA=="], + "@effect/platform-node": ["@effect/platform-node@https://pkg.pr.new/Effect-TS/effect-smol/@effect/platform-node@f591738", { "dependencies": { "@effect/platform-node-shared": "https://pkg.pr.new/Effect-TS/effect-smol/@effect/platform-node-shared@f59173885df0eb490dc2e6619ae89467feac0413", "mime": "^4.1.0", "undici": "^7.20.0" }, "peerDependencies": { "effect": "^4.0.0-beta.19", "ioredis": "^5.7.0" } }], - "@effect/platform-node-shared": ["@effect/platform-node-shared@4.0.0-beta.10", "", { "dependencies": { "@types/ws": "^8.18.1", "ws": "^8.19.0" }, "peerDependencies": { "effect": "^4.0.0-beta.10" } }, "sha512-gBSeOG2bfchXFdgdi5oipHKynOzSd7BlzTy5QcjFOoCUTtLP9igqohPB9hTkV3a6seWE2JJqsgsQ79VuLb7fAg=="], + "@effect/platform-node-shared": ["@effect/platform-node-shared@https://pkg.pr.new/Effect-TS/effect-smol/@effect/platform-node-shared@f59173885df0eb490dc2e6619ae89467feac0413", { "dependencies": { "@types/ws": "^8.18.1", "ws": "^8.19.0" }, "peerDependencies": { "effect": "^4.0.0-beta.19" } }], - "@effect/sql-sqlite-bun": ["@effect/sql-sqlite-bun@4.0.0-beta.10", "", { "peerDependencies": { "effect": "^4.0.0-beta.10" } }, "sha512-LY1jNu2uqGYsJCVjVU73nzcZ+kAYBDsG7O8x5ddmkIXj10RmnIhjYMStWBa0NZOFg4Hsl+CadxtsDy3edaOVeA=="], + "@effect/sql-sqlite-bun": ["@effect/sql-sqlite-bun@https://pkg.pr.new/Effect-TS/effect-smol/@effect/sql-sqlite-bun@f591738", { "peerDependencies": { "effect": "^4.0.0-beta.19" } }], - "@effect/vitest": ["@effect/vitest@4.0.0-beta.10", "", { "peerDependencies": { "effect": "^4.0.0-beta.10", "vitest": "^3.0.0" } }, "sha512-WmE8vCNw+eDoAq9CwwymrAdIBw1f+8hDP3AUF7jyC7/Cj/kyAYOFwkbtKmDXIFaTlf7G/2fmulEXeCO5W8j0kQ=="], + "@effect/vitest": ["@effect/vitest@https://pkg.pr.new/Effect-TS/effect-smol/@effect/vitest@f591738", { "peerDependencies": { "effect": "^4.0.0-beta.19", "vitest": "^3.0.0" } }], "@electron/get": ["@electron/get@2.0.3", "", { "dependencies": { "debug": "^4.1.1", "env-paths": "^2.2.0", "fs-extra": "^8.1.0", "got": "^11.8.5", "progress": "^2.0.3", "semver": "^6.2.0", "sumchecker": "^3.0.1" }, "optionalDependencies": { "global-agent": "^3.0.0" } }, "sha512-Qkzpg2s9GnVV2I2BjRksUi43U5e6+zaQMcjoJy0C+C5oxaKl+fmckGDQFtRpZpZV0NQekuZZ+tGz7EA9TVnQtQ=="], @@ -729,7 +737,7 @@ "dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="], - "effect": ["effect@4.0.0-beta.10", "", { "dependencies": { "@standard-schema/spec": "^1.1.0", "fast-check": "^4.5.3", "find-my-way-ts": "^0.1.6", "ini": "^6.0.0", "kubernetes-types": "^1.30.0", "msgpackr": "^1.11.8", "multipasta": "^0.2.7", "toml": "^3.0.0", "uuid": "^13.0.0", "yaml": "^2.8.2" } }, "sha512-UvxWmT0uzWZqNmQODfW7y1zb0LMlH3xtSn/UESpck2Wvv6SogxibYPWKKNcUsRMm5wwprOoW2IwItYLQyZpe8Q=="], + "effect": ["effect@https://pkg.pr.new/Effect-TS/effect-smol/effect@f591738", { "dependencies": { "@standard-schema/spec": "^1.1.0", "fast-check": "^4.5.3", "find-my-way-ts": "^0.1.6", "ini": "^6.0.0", "kubernetes-types": "^1.30.0", "msgpackr": "^1.11.8", "multipasta": "^0.2.7", "toml": "^3.0.0", "uuid": "^13.0.0", "yaml": "^2.8.2" } }], "electron": ["electron@40.6.0", "", { "dependencies": { "@electron/get": "^2.0.0", "@types/node": "^24.9.0", "extract-zip": "^2.0.1" }, "bin": { "electron": "cli.js" } }, "sha512-ett8W+yOFGDuM0vhJMamYSkrbV3LoaffzJd9GfjI96zRAxyrNqUSKqBpf/WGbQCweDxX2pkUCUfrv4wwKpsFZA=="], diff --git a/package.json b/package.json index f7149e5a33a063f1803b3b725ed31251b4e6f051..ac3a58a7525208541953bcc9f96b8f0d3397a294 100644 --- a/package.json +++ b/package.json @@ -1,10 +1,19 @@ { "name": "@t3tools/monorepo", "private": true, - "workspaces": [ - "apps/*", - "packages/*" - ], + "workspaces": { + "packages": [ + "apps/*", + "packages/*" + ], + "catalog": { + "effect": "https://pkg.pr.new/Effect-TS/effect-smol/effect@f591738", + "@effect/platform-node": "https://pkg.pr.new/Effect-TS/effect-smol/@effect/platform-node@f591738", + "@effect/sql-sqlite-bun": "https://pkg.pr.new/Effect-TS/effect-smol/@effect/sql-sqlite-bun@f591738", + "@effect/vitest": "https://pkg.pr.new/Effect-TS/effect-smol/@effect/vitest@f591738", + "@effect/language-service": "0.75.1" + } + }, "scripts": { "dev": "bun run scripts/dev-runner.mjs dev", "dev:server": "bun run scripts/dev-runner.mjs dev:server", diff --git a/packages/contracts/package.json b/packages/contracts/package.json index 9044ff57b1cf00b14c00cc70360cfe8731a479da..b9a70686fa92900c51f4942bd47f240e6a67d486 100644 --- a/packages/contracts/package.json +++ b/packages/contracts/package.json @@ -24,11 +24,11 @@ "test": "vitest run" }, "dependencies": { - "effect": "4.0.0-beta.10" + "effect": "catalog:" }, "devDependencies": { "@effect/language-service": "0.75.1", - "@effect/vitest": "4.0.0-beta.10", + "@effect/vitest": "catalog:", "tsdown": "^0.20.3", "typescript": "^5.7.3" } diff --git a/packages/contracts/tsconfig.json b/packages/contracts/tsconfig.json index a7d84f36f53736c33ed6b4d1d36fccbcb02efb7d..92d639f99bd951235041df17b17ba208b60754fe 100644 --- a/packages/contracts/tsconfig.json +++ b/packages/contracts/tsconfig.json @@ -3,7 +3,14 @@ "compilerOptions": { "plugins": [ { - "name": "@effect/language-service" + "name": "@effect/language-service", + "namespaceImportPackages": ["@effect/platform-node"], + "diagnosticSeverity": { + "importFromBarrel": "error", + "anyUnknownInErrorContext": "warning", + "instanceOfSchema": "warning", + "deterministicKeys": "warning" + } } ] }, diff --git a/scripts/build-desktop-dmg.mjs b/scripts/build-desktop-dmg.mjs index 1d7de144b555d6e41ebb25f4807b4be658f32904..1552dc2d2162ec21c7896fadd6d27c3acc07eedd 100644 --- a/scripts/build-desktop-dmg.mjs +++ b/scripts/build-desktop-dmg.mjs @@ -206,6 +206,42 @@ async function validateBundledClientAssets(clientDir) { } } +function readWorkspaceCatalog(rootPackageJson) { + const workspaces = rootPackageJson?.workspaces; + if (!workspaces || typeof workspaces !== "object" || Array.isArray(workspaces)) { + return {}; + } + + const catalog = workspaces.catalog; + if (!catalog || typeof catalog !== "object" || Array.isArray(catalog)) { + return {}; + } + + return catalog; +} + +function resolveCatalogDependencies(dependencies, catalog, dependencySourceLabel) { + return Object.fromEntries( + Object.entries(dependencies).map(([dependencyName, spec]) => { + if (typeof spec !== "string" || !spec.startsWith("catalog:")) { + return [dependencyName, spec]; + } + + const catalogKey = spec.slice("catalog:".length).trim(); + const lookupKey = catalogKey.length > 0 ? catalogKey : dependencyName; + const resolvedSpec = catalog[lookupKey]; + if (typeof resolvedSpec !== "string" || resolvedSpec.length === 0) { + fail( + `Unable to resolve '${spec}' for ${dependencySourceLabel} dependency '${dependencyName}'. ` + + `Expected key '${lookupKey}' in root workspace catalog.`, + ); + } + + return [dependencyName, resolvedSpec]; + }), + ); +} + async function main() { const options = parseArgs(process.argv.slice(2)); if (options.help) { @@ -217,6 +253,7 @@ async function main() { fail("This script only builds macOS .dmg artifacts and must run on macOS."); } + const rootPkg = await readJson(path.join(repoRoot, "package.json")); const desktopPkg = await readJson(path.join(repoRoot, "apps/desktop/package.json")); const serverPkg = await readJson(path.join(repoRoot, "apps/server/package.json")); @@ -229,6 +266,11 @@ async function main() { if (!dependencies || Object.keys(dependencies).length === 0) { fail("Could not resolve production dependencies from apps/server/package.json."); } + const resolvedDependencies = resolveCatalogDependencies( + dependencies, + readWorkspaceCatalog(rootPkg), + "apps/server", + ); const appVersion = options.version ?? serverPkg.version ?? "0.1.0"; const stageRoot = path.join(os.tmpdir(), "t3code-desktop-dmg-stage"); @@ -291,7 +333,7 @@ async function main() { category: "public.app-category.developer-tools", }, }, - dependencies, + dependencies: resolvedDependencies, devDependencies: { electron: electronVersion, },