authorgravatar for julius0216@outlook.comJulius Marminge <julius0216@outlook.com> 2026-02-26 16:53:39-08:00
committergravatar for julius0216@outlook.comJulius Marminge <julius0216@outlook.com> 2026-02-26 16:53:39-08:00
log8f6b21a12e8c6509649883da8e36d375e5aa1fdf
treecb25078bb068c6f579039c82c7fdabf6ef180c0b
parentdc530f7a7e21dac998126024b2b112fc893df404

fix dmg build, enable some lsp diagnostics


71 files changed, 353 insertions(+), 148 deletions(-)

apps/server/integration/OrchestrationEngineHarness.integration.ts+1-1
......@@ -3,7 +3,7 @@ import os from "node:os";
33import path from "node:path";
44import { execFileSync } from "node:child_process";
55
6import { NodeServices } from "@effect/platform-node";
6import * as NodeServices from "@effect/platform-node/NodeServices";
77import {
88 ApprovalRequestId,
99 type OrchestrationEvent,
apps/server/integration/providerService.integration.test.ts+1-1
......@@ -1,6 +1,6 @@
11import type { ProviderRuntimeEvent, ProviderSessionId } from "@t3tools/contracts";
22import { ThreadId } from "@t3tools/contracts";
3import { NodeServices } from "@effect/platform-node";
3import * as NodeServices from "@effect/platform-node/NodeServices";
44import { it, assert } from "@effect/vitest";
55import { Effect, FileSystem, Layer, Path, Queue, Stream } from "effect";
66
apps/server/package.json+4-4
......@@ -18,17 +18,17 @@
1818 "test": "vitest run"
1919 },
2020 "dependencies": {
21 "@effect/platform-node": "4.0.0-beta.10",
22 "@effect/sql-sqlite-bun": "4.0.0-beta.10",
21 "@effect/platform-node": "catalog:",
22 "@effect/sql-sqlite-bun": "catalog:",
2323 "@pierre/diffs": "^1.1.0-beta.16",
24 "effect": "4.0.0-beta.10",
24 "effect": "catalog:",
2525 "node-pty": "^1.1.0",
2626 "open": "^10.1.0",
2727 "ws": "^8.18.0"
2828 },
2929 "devDependencies": {
3030 "@effect/language-service": "0.75.1",
31 "@effect/vitest": "4.0.0-beta.10",
31 "@effect/vitest": "catalog:",
3232 "@t3tools/contracts": "workspace:*",
3333 "@t3tools/web": "workspace:*",
3434 "@types/bun": "^1.3.9",
apps/server/src/checkpointing/Services/CheckpointDiffQuery.ts+1-1
......@@ -46,4 +46,4 @@ export interface CheckpointDiffQueryShape {
4646export class CheckpointDiffQuery extends ServiceMap.Service<
4747 CheckpointDiffQuery,
4848 CheckpointDiffQueryShape
49>()("checkpointing/CheckpointDiffQuery") {}
49>()("t3/checkpointing/Services/CheckpointDiffQuery") {}
apps/server/src/checkpointing/Services/CheckpointStore.ts+1-1
......@@ -96,5 +96,5 @@ export interface CheckpointStoreShape {
9696 * CheckpointStore - Service tag for checkpoint persistence and restore operations.
9797 */
9898export class CheckpointStore extends ServiceMap.Service<CheckpointStore, CheckpointStoreShape>()(
99 "checkpointing/CheckpointStore",
99 "t3/checkpointing/Services/CheckpointStore",
100100) {}
apps/server/src/config.ts+7-7
......@@ -35,7 +35,7 @@ export interface ServerConfigShape {
3535 * ServerConfig - Service tag for server runtime configuration.
3636 */
3737export class ServerConfig extends ServiceMap.Service<ServerConfig, ServerConfigShape>()(
38 "server/ServerConfig",
38 "t3/config/ServerConfig",
3939) {}
4040
4141// Helpers
......@@ -61,7 +61,7 @@ export interface NetServiceShape {
6161 * NetService - Service tag for startup networking helpers.
6262 */
6363export class NetService extends ServiceMap.Service<NetService, NetServiceShape>()(
64 "server/NetService",
64 "t3/config/NetService",
6565) {
6666 static readonly layer = Layer.succeed(NetService, {
6767 findAvailablePort: (preferred) =>
......@@ -102,18 +102,18 @@ export class NetService extends ServiceMap.Service<NetService, NetServiceShape>(
102102
103103export const resolveStaticDir = Effect.fn(function* () {
104104 const { join, resolve } = yield* Path.Path;
105 const { stat } = yield* FileSystem.FileSystem;
105 const { exists } = yield* FileSystem.FileSystem;
106106 const bundledClient = resolve(join(import.meta.dirname, "client"));
107 const bundledStat = yield* stat(join(bundledClient, "index.html")).pipe(
108 Effect.orElseSucceed(() => undefined),
107 const bundledStat = yield* exists(join(bundledClient, "index.html")).pipe(
108 Effect.orElseSucceed(() => false),
109109 );
110110 if (bundledStat) {
111111 return bundledClient;
112112 }
113113
114114 const monorepoClient = resolve(join(import.meta.dirname, "../../web/dist"));
115 const monorepoStat = yield* stat(join(monorepoClient, "index.html")).pipe(
116 Effect.orElseSucceed(() => undefined),
115 const monorepoStat = yield* exists(join(monorepoClient, "index.html")).pipe(
116 Effect.orElseSucceed(() => false),
117117 );
118118 if (monorepoStat) {
119119 return monorepoClient;
apps/server/src/git/Layers/CodexTextGeneration.test.ts+1-1
......@@ -1,4 +1,4 @@
1import { NodeServices } from "@effect/platform-node";
1import * as NodeServices from "@effect/platform-node/NodeServices";
22import { it } from "@effect/vitest";
33import { Effect, FileSystem, Layer, Path } from "effect";
44import { expect } from "vitest";
apps/server/src/git/Layers/GitCore.test.ts+1-1
......@@ -1,7 +1,7 @@
11import { existsSync } from "node:fs";
22import path from "node:path";
33
4import { NodeServices } from "@effect/platform-node";
4import * as NodeServices from "@effect/platform-node/NodeServices";
55import { it } from "@effect/vitest";
66import { Effect, FileSystem, Layer, PlatformError, Scope } from "effect";
77import { describe, expect, vi } from "vitest";
apps/server/src/git/Layers/GitManager.test.ts+1-1
......@@ -1,7 +1,7 @@
11import fs from "node:fs";
22import path from "node:path";
33
4import { NodeServices } from "@effect/platform-node";
4import * as NodeServices from "@effect/platform-node/NodeServices";
55import { it } from "@effect/vitest";
66import { Effect, FileSystem, Layer, PlatformError, Scope } from "effect";
77import { expect } from "vitest";
apps/server/src/git/Layers/GitService.test.ts+1-1
......@@ -1,4 +1,4 @@
1import { NodeServices } from "@effect/platform-node";
1import * as NodeServices from "@effect/platform-node/NodeServices";
22import { it, assert } from "@effect/vitest";
33import { Effect, Layer } from "effect";
44
apps/server/src/git/Services/GitCore.ts+3-1
......@@ -143,4 +143,6 @@ export interface GitCoreShape {
143143/**
144144 * GitCore - Service tag for low-level Git repository operations.
145145 */
146export class GitCore extends ServiceMap.Service<GitCore, GitCoreShape>()("git/GitCore") {}
146export class GitCore extends ServiceMap.Service<GitCore, GitCoreShape>()(
147 "t3/git/Services/GitCore",
148) {}
apps/server/src/git/Services/GitHubCli.ts+3-1
......@@ -63,4 +63,6 @@ export interface GitHubCliShape {
6363/**
6464 * GitHubCli - Service tag for GitHub CLI process execution.
6565 */
66export class GitHubCli extends ServiceMap.Service<GitHubCli, GitHubCliShape>()("git/GitHubCli") {}
66export class GitHubCli extends ServiceMap.Service<GitHubCli, GitHubCliShape>()(
67 "t3/git/Services/GitHubCli",
68) {}
apps/server/src/git/Services/GitManager.ts+4-2
......@@ -23,7 +23,9 @@ export interface GitManagerShape {
2323 /**
2424 * Read current repository Git status plus open PR metadata when available.
2525 */
26 readonly status: (input: GitStatusInput) => Effect.Effect<GitStatusResult, GitManagerServiceError>;
26 readonly status: (
27 input: GitStatusInput,
28 ) => Effect.Effect<GitStatusResult, GitManagerServiceError>;
2729
2830 /**
2931 * Run a stacked Git action (`commit`, `commit_push`, `commit_push_pr`).
......@@ -37,5 +39,5 @@ export interface GitManagerShape {
3739 * GitManager - Service tag for stacked Git workflow orchestration.
3840 */
3941export class GitManager extends ServiceMap.Service<GitManager, GitManagerShape>()(
40 "git/GitManager",
42 "t3/git/Services/GitManager",
4143) {}
apps/server/src/git/Services/GitService.ts+1-1
......@@ -41,5 +41,5 @@ export interface GitServiceShape {
4141 * GitService - Service for Git command execution.
4242 */
4343export class GitService extends ServiceMap.Service<GitService, GitServiceShape>()(
44 "git/GitService",
44 "t3/git/Services/GitService",
4545) {}
apps/server/src/git/Services/TextGeneration.ts+1-1
......@@ -67,5 +67,5 @@ export interface TextGenerationShape {
6767 * TextGeneration - Service tag for commit and PR text generation.
6868 */
6969export class TextGeneration extends ServiceMap.Service<TextGeneration, TextGenerationShape>()(
70 "git/TextGeneration",
70 "t3/git/Services/TextGeneration",
7171) {}
apps/server/src/keybindings.test.ts+22-5
......@@ -1,5 +1,5 @@
11import { KeybindingCommand, KeybindingRule, KeybindingsConfig } from "@t3tools/contracts";
2import { NodeServices } from "@effect/platform-node";
2import * as NodeServices from "@effect/platform-node/NodeServices";
33import { assert, it } from "@effect/vitest";
44import { assertFailure } from "@effect/vitest/utils";
55import { Effect, FileSystem, Layer, Logger, Path, Schema } from "effect";
......@@ -178,7 +178,10 @@ it.layer(NodeServices.layer)("keybindings", (it) => {
178178 return yield* keybindings.loadConfigState;
179179 });
180180
181 assert.deepEqual(configState.keybindings, compileResolvedKeybindingsConfig(DEFAULT_KEYBINDINGS));
181 assert.deepEqual(
182 configState.keybindings,
183 compileResolvedKeybindingsConfig(DEFAULT_KEYBINDINGS),
184 );
182185 assert.deepEqual(configState.issues, [
183186 {
184187 kind: "keybindings.malformed-config",
......@@ -208,10 +211,24 @@ it.layer(NodeServices.layer)("keybindings", (it) => {
208211 });
209212
210213 assert.isTrue(configState.keybindings.some((entry) => entry.command === "terminal.toggle"));
211 assert.isFalse(configState.keybindings.some((entry) => entry.command === "invalid.command"));
214 assert.isFalse(
215 configState.keybindings.some(
216 (entry) =>
217 // @ts-expect-error - invalid command
218 entry.command === "invalid.command",
219 ),
220 );
212221 assert.deepEqual(configState.issues, [
213 { kind: "keybindings.invalid-entry", index: 1, message: configState.issues[0]?.message ?? "" },
214 { kind: "keybindings.invalid-entry", index: 2, message: configState.issues[1]?.message ?? "" },
222 {
223 kind: "keybindings.invalid-entry",
224 index: 1,
225 message: configState.issues[0]?.message ?? "",
226 },
227 {
228 kind: "keybindings.invalid-entry",
229 index: 2,
230 message: configState.issues[1]?.message ?? "",
231 },
215232 ]);
216233 }).pipe(Effect.provide(makeKeybindingsLayer())),
217234 );
apps/server/src/keybindings.ts+1-1
......@@ -498,7 +498,7 @@ export interface KeybindingsShape {
498498 * Keybindings - Service tag for keybinding configuration operations.
499499 */
500500export class Keybindings extends ServiceMap.Service<Keybindings, KeybindingsShape>()(
501 "server/Keybindings",
501 "t3/keybindings",
502502) {}
503503
504504const makeKeybindings = Effect.gen(function* () {
apps/server/src/main.test.ts+1-1
......@@ -1,5 +1,5 @@
11import * as Http from "node:http";
2import { NodeServices } from "@effect/platform-node";
2import * as NodeServices from "@effect/platform-node/NodeServices";
33import { assert, it, vi } from "@effect/vitest";
44import * as ConfigProvider from "effect/ConfigProvider";
55import * as Effect from "effect/Effect";
apps/server/src/main.ts+3-1
......@@ -63,7 +63,9 @@ export interface CliConfigShape {
6363/**
6464 * CliConfig - Service tag for startup CLI/runtime helpers.
6565 */
66export class CliConfig extends ServiceMap.Service<CliConfig, CliConfigShape>()("server/CliConfig") {
66export class CliConfig extends ServiceMap.Service<CliConfig, CliConfigShape>()(
67 "t3/main/CliConfig",
68) {
6769 static readonly layer = Layer.effect(
6870 CliConfig,
6971 Effect.gen(function* () {
apps/server/src/open.ts+1-1
......@@ -50,7 +50,7 @@ export interface OpenShape {
5050/**
5151 * Open - Service tag for browser/editor launch operations.
5252 */
53export class Open extends ServiceMap.Service<Open, OpenShape>()("server/Open") {}
53export class Open extends ServiceMap.Service<Open, OpenShape>()("t3/open") {}
5454
5555// ==============================
5656// Implementations
apps/server/src/orchestration/Layers/CheckpointReactor.test.ts+1-1
......@@ -14,7 +14,7 @@ import {
1414 ThreadId,
1515 TurnId,
1616} from "@t3tools/contracts";
17import { NodeServices } from "@effect/platform-node";
17import * as NodeServices from "@effect/platform-node/NodeServices";
1818import { Effect, Exit, Layer, ManagedRuntime, PubSub, Scope, Stream } from "effect";
1919import { afterEach, describe, expect, it, vi } from "vitest";
2020
apps/server/src/orchestration/Layers/OrchestrationEngine.ts+2-2
......@@ -5,7 +5,7 @@ import type {
55 ThreadId,
66} from "@t3tools/contracts";
77import { OrchestrationCommand } from "@t3tools/contracts";
8import { Deferred, Effect, Layer, Option, PubSub, Queue, Stream } from "effect";
8import { Deferred, Effect, Layer, Option, PubSub, Queue, Schema, Stream } from "effect";
99import * as SqlClient from "effect/unstable/sql/SqlClient";
1010
1111import { toPersistenceSqlError } from "../../persistence/Errors.ts";
......@@ -181,7 +181,7 @@ const makeOrchestrationEngine = Effect.gen(function* () {
181181 ),
182182 );
183183
184 if (error instanceof OrchestrationCommandInvariantError) {
184 if (Schema.is(OrchestrationCommandInvariantError)(error)) {
185185 const aggregateRef = commandToAggregateRef(envelope.command);
186186 yield* commandReceiptRepository
187187 .upsert({
apps/server/src/orchestration/Layers/ProjectionPipeline.test.ts+1-1
......@@ -10,7 +10,7 @@ import {
1010 ThreadId,
1111 TurnId,
1212} from "@t3tools/contracts";
13import { NodeServices } from "@effect/platform-node";
13import * as NodeServices from "@effect/platform-node/NodeServices";
1414import { assert, it } from "@effect/vitest";
1515import { Effect, Layer } from "effect";
1616import fs from "node:fs";
apps/server/src/orchestration/Services/CheckpointReactor.ts+1-1
......@@ -31,4 +31,4 @@ export interface CheckpointReactorShape {
3131export class CheckpointReactor extends ServiceMap.Service<
3232 CheckpointReactor,
3333 CheckpointReactorShape
34>()("orchestration/CheckpointReactor") {}
34>()("t3/orchestration/Services/CheckpointReactor") {}
apps/server/src/orchestration/Services/OrchestrationEngine.ts+1-1
......@@ -77,4 +77,4 @@ export interface OrchestrationEngineShape {
7777export class OrchestrationEngineService extends ServiceMap.Service<
7878 OrchestrationEngineService,
7979 OrchestrationEngineShape
80>()("orchestration/Engine") {}
80>()("t3/orchestration/Services/OrchestrationEngine/OrchestrationEngineService") {}
apps/server/src/orchestration/Services/OrchestrationReactor.ts+1-1
......@@ -28,4 +28,4 @@ export interface OrchestrationReactorShape {
2828export class OrchestrationReactor extends ServiceMap.Service<
2929 OrchestrationReactor,
3030 OrchestrationReactorShape
31>()("orchestration/OrchestrationReactor") {}
31>()("t3/orchestration/Services/OrchestrationReactor") {}
apps/server/src/orchestration/Services/ProjectionPipeline.ts+1-1
......@@ -39,4 +39,4 @@ export interface OrchestrationProjectionPipelineShape {
3939export class OrchestrationProjectionPipeline extends ServiceMap.Service<
4040 OrchestrationProjectionPipeline,
4141 OrchestrationProjectionPipelineShape
42>()("orchestration/ProjectionPipeline") {}
42>()("t3/orchestration/Services/ProjectionPipeline/OrchestrationProjectionPipeline") {}
apps/server/src/orchestration/Services/ProjectionSnapshotQuery.ts+1-1
......@@ -31,4 +31,4 @@ export interface ProjectionSnapshotQueryShape {
3131export class ProjectionSnapshotQuery extends ServiceMap.Service<
3232 ProjectionSnapshotQuery,
3333 ProjectionSnapshotQueryShape
34>()("orchestration/ProjectionSnapshotQuery") {}
34>()("t3/orchestration/Services/ProjectionSnapshotQuery") {}
apps/server/src/orchestration/Services/ProviderCommandReactor.ts+1-1
......@@ -31,4 +31,4 @@ export interface ProviderCommandReactorShape {
3131export class ProviderCommandReactor extends ServiceMap.Service<
3232 ProviderCommandReactor,
3333 ProviderCommandReactorShape
34>()("orchestration/ProviderCommandReactor") {}
34>()("t3/orchestration/Services/ProviderCommandReactor") {}
apps/server/src/orchestration/Services/ProviderRuntimeIngestion.ts+1-1
......@@ -31,4 +31,4 @@ export interface ProviderRuntimeIngestionShape {
3131export class ProviderRuntimeIngestionService extends ServiceMap.Service<
3232 ProviderRuntimeIngestionService,
3333 ProviderRuntimeIngestionShape
34>()("orchestration/ProviderRuntimeIngestion") {}
34>()("t3/orchestration/Services/ProviderRuntimeIngestion/ProviderRuntimeIngestionService") {}
apps/server/src/persistence/Layers/OrchestrationEventStore.test.ts+2-2
......@@ -1,6 +1,6 @@
11import { CommandId, EventId, ProjectId } from "@t3tools/contracts";
22import { assert, it } from "@effect/vitest";
3import { Effect, Layer, Stream } from "effect";
3import { Effect, Layer, Schema, Stream } from "effect";
44import * as SqlClient from "effect/unstable/sql/SqlClient";
55
66import { PersistenceDecodeError } from "../Errors.ts";
......@@ -107,7 +107,7 @@ layer("OrchestrationEventStore", (it) => {
107107 );
108108 assert.equal(replayResult._tag, "Failure");
109109 if (replayResult._tag === "Failure") {
110 assert.ok(replayResult.failure instanceof PersistenceDecodeError);
110 assert.ok(Schema.is(PersistenceDecodeError)(replayResult.failure));
111111 assert.ok(
112112 replayResult.failure.operation.includes(
113113 "OrchestrationEventStore.readFromSequence:decodeRows",
apps/server/src/persistence/Layers/ProviderSessions.ts+2-6
......@@ -51,14 +51,10 @@ function toValidationError(
5151 });
5252}
5353
54function decodeInput<A>(
55 schema: Schema.Schema<A>,
56 input: unknown,
57 operation: string,
58): Effect.Effect<A, ProviderSessionRepositoryValidationError> {
54function decodeInput<S extends Schema.Top>(schema: S, input: unknown, operation: string) {
5955 return Schema.decodeUnknownEffect(schema)(input).pipe(
6056 Effect.mapError((cause) => toValidationError(operation, cause)),
61 ) as Effect.Effect<A, ProviderSessionRepositoryValidationError>;
57 );
6258}
6359
6460function toPersistenceError(
apps/server/src/persistence/NodeSqliteClient.ts+1-1
......@@ -32,7 +32,7 @@ export type TypeId = "~local/sqlite-node/SqliteClient";
3232/**
3333 * SqliteClient - Effect service tag for the sqlite SQL client.
3434 */
35export const SqliteClient = ServiceMap.Service<Client.SqlClient>("@local/sqlite-node/SqliteClient");
35export const SqliteClient = ServiceMap.Service<Client.SqlClient>("t3/persistence/NodeSqliteClient");
3636
3737export interface SqliteClientConfig {
3838 readonly filename: string;
apps/server/src/persistence/Services/OrchestrationCommandReceipts.ts+1-1
......@@ -66,4 +66,4 @@ export interface OrchestrationCommandReceiptRepositoryShape {
6666export class OrchestrationCommandReceiptRepository extends ServiceMap.Service<
6767 OrchestrationCommandReceiptRepository,
6868 OrchestrationCommandReceiptRepositoryShape
69>()("persistence/OrchestrationCommandReceiptRepository") {}
69>()("t3/persistence/Services/OrchestrationCommandReceipts/OrchestrationCommandReceiptRepository") {}
apps/server/src/persistence/Services/OrchestrationEventStore.ts+1-1
......@@ -67,4 +67,4 @@ export interface OrchestrationEventStoreShape {
6767export class OrchestrationEventStore extends ServiceMap.Service<
6868 OrchestrationEventStore,
6969 OrchestrationEventStoreShape
70>()("orchestration/EventStore") {}
70>()("t3/persistence/Services/OrchestrationEventStore") {}
apps/server/src/persistence/Services/ProjectionCheckpoints.ts+1-1
......@@ -90,4 +90,4 @@ export interface ProjectionCheckpointRepositoryShape {
9090export class ProjectionCheckpointRepository extends ServiceMap.Service<
9191 ProjectionCheckpointRepository,
9292 ProjectionCheckpointRepositoryShape
93>()("persistence/ProjectionCheckpointRepository") {}
93>()("t3/persistence/Services/ProjectionCheckpoints/ProjectionCheckpointRepository") {}
apps/server/src/persistence/Services/ProjectionPendingApprovals.ts+1-1
......@@ -88,4 +88,4 @@ export interface ProjectionPendingApprovalRepositoryShape {
8888export class ProjectionPendingApprovalRepository extends ServiceMap.Service<
8989 ProjectionPendingApprovalRepository,
9090 ProjectionPendingApprovalRepositoryShape
91>()("persistence/ProjectionPendingApprovalRepository") {}
91>()("t3/persistence/Services/ProjectionPendingApprovals/ProjectionPendingApprovalRepository") {}
apps/server/src/persistence/Services/ProjectionProjects.ts+1-1
......@@ -76,4 +76,4 @@ export interface ProjectionProjectRepositoryShape {
7676export class ProjectionProjectRepository extends ServiceMap.Service<
7777 ProjectionProjectRepository,
7878 ProjectionProjectRepositoryShape
79>()("persistence/ProjectionProjectRepository") {}
79>()("t3/persistence/Services/ProjectionProjects/ProjectionProjectRepository") {}
apps/server/src/persistence/Services/ProjectionState.ts+1-1
......@@ -61,4 +61,4 @@ export interface ProjectionStateRepositoryShape {
6161export class ProjectionStateRepository extends ServiceMap.Service<
6262 ProjectionStateRepository,
6363 ProjectionStateRepositoryShape
64>()("persistence/ProjectionStateRepository") {}
64>()("t3/persistence/Services/ProjectionState/ProjectionStateRepository") {}
apps/server/src/persistence/Services/ProjectionThreadActivities.ts+1-1
......@@ -77,4 +77,4 @@ export interface ProjectionThreadActivityRepositoryShape {
7777export class ProjectionThreadActivityRepository extends ServiceMap.Service<
7878 ProjectionThreadActivityRepository,
7979 ProjectionThreadActivityRepositoryShape
80>()("persistence/ProjectionThreadActivityRepository") {}
80>()("t3/persistence/Services/ProjectionThreadActivities/ProjectionThreadActivityRepository") {}
apps/server/src/persistence/Services/ProjectionThreadMessages.ts+1-1
......@@ -76,4 +76,4 @@ export interface ProjectionThreadMessageRepositoryShape {
7676export class ProjectionThreadMessageRepository extends ServiceMap.Service<
7777 ProjectionThreadMessageRepository,
7878 ProjectionThreadMessageRepositoryShape
79>()("persistence/ProjectionThreadMessageRepository") {}
79>()("t3/persistence/Services/ProjectionThreadMessages/ProjectionThreadMessageRepository") {}
apps/server/src/persistence/Services/ProjectionThreadSessions.ts+1-1
......@@ -73,4 +73,4 @@ export interface ProjectionThreadSessionRepositoryShape {
7373export class ProjectionThreadSessionRepository extends ServiceMap.Service<
7474 ProjectionThreadSessionRepository,
7575 ProjectionThreadSessionRepositoryShape
76>()("persistence/ProjectionThreadSessionRepository") {}
76>()("t3/persistence/Services/ProjectionThreadSessions/ProjectionThreadSessionRepository") {}
apps/server/src/persistence/Services/ProjectionThreads.ts+1-1
......@@ -82,4 +82,4 @@ export interface ProjectionThreadRepositoryShape {
8282export class ProjectionThreadRepository extends ServiceMap.Service<
8383 ProjectionThreadRepository,
8484 ProjectionThreadRepositoryShape
85>()("persistence/ProjectionThreadRepository") {}
85>()("t3/persistence/Services/ProjectionThreads/ProjectionThreadRepository") {}
apps/server/src/persistence/Services/ProjectionTurns.ts+1-1
......@@ -158,4 +158,4 @@ export interface ProjectionTurnRepositoryShape {
158158export class ProjectionTurnRepository extends ServiceMap.Service<
159159 ProjectionTurnRepository,
160160 ProjectionTurnRepositoryShape
161>()("persistence/ProjectionTurnRepository") {}
161>()("t3/persistence/Services/ProjectionTurns/ProjectionTurnRepository") {}
apps/server/src/persistence/Services/ProviderSessionRuntime.ts+1-1
......@@ -84,4 +84,4 @@ export interface ProviderSessionRuntimeRepositoryShape {
8484export class ProviderSessionRuntimeRepository extends ServiceMap.Service<
8585 ProviderSessionRuntimeRepository,
8686 ProviderSessionRuntimeRepositoryShape
87>()("persistence/ProviderSessionRuntimeRepository") {}
87>()("t3/persistence/Services/ProviderSessionRuntime/ProviderSessionRuntimeRepository") {}
apps/server/src/persistence/Services/ProviderSessions.ts+1-1
......@@ -78,4 +78,4 @@ export interface ProviderSessionRepositoryShape {
7878export class ProviderSessionRepository extends ServiceMap.Service<
7979 ProviderSessionRepository,
8080 ProviderSessionRepositoryShape
81>()("persistence/ProviderSessionRepository") {}
81>()("t3/persistence/Services/ProviderSessions/ProviderSessionRepository") {}
apps/server/src/provider/Layers/ProviderAdapterRegistry.test.ts+1-1
......@@ -8,7 +8,7 @@ import { CodexAdapter, CodexAdapterShape } from "../Services/CodexAdapter.ts";
88import { ProviderAdapterRegistry } from "../Services/ProviderAdapterRegistry.ts";
99import { ProviderAdapterRegistryLive } from "./ProviderAdapterRegistry.ts";
1010import { ProviderUnsupportedError } from "../Errors.ts";
11import { NodeServices } from "@effect/platform-node";
11import * as NodeServices from "@effect/platform-node/NodeServices";
1212
1313const fakeCodexAdapter: CodexAdapterShape = {
1414 provider: "codex",
apps/server/src/provider/Layers/ProviderService.test.ts+1-1
......@@ -37,7 +37,7 @@ import { ProviderService } from "../Services/ProviderService.ts";
3737import { ProviderSessionDirectory } from "../Services/ProviderSessionDirectory.ts";
3838import { makeProviderServiceLive } from "./ProviderService.ts";
3939import { ProviderSessionDirectoryLive } from "./ProviderSessionDirectory.ts";
40import { NodeServices } from "@effect/platform-node";
40import * as NodeServices from "@effect/platform-node/NodeServices";
4141import { ProviderSessionRuntimeRepositoryLive } from "../../persistence/Layers/ProviderSessionRuntime.ts";
4242import { ProviderSessionRuntimeRepository } from "../../persistence/Services/ProviderSessionRuntime.ts";
4343import {
apps/server/src/provider/Layers/ProviderSessionDirectory.test.ts+1-1
......@@ -2,7 +2,7 @@ import fs from "node:fs";
22import os from "node:os";
33import path from "node:path";
44
5import { NodeServices } from "@effect/platform-node";
5import * as NodeServices from "@effect/platform-node/NodeServices";
66import { ProviderSessionId, ProviderThreadId, ThreadId } from "@t3tools/contracts";
77import { it, assert } from "@effect/vitest";
88import { assertFailure, assertSome } from "@effect/vitest/utils";
apps/server/src/provider/Services/CodexAdapter.ts+1-1
......@@ -26,5 +26,5 @@ export interface CodexAdapterShape extends ProviderAdapterShape<ProviderAdapterE
2626 * CodexAdapter - Service tag for Codex provider adapter operations.
2727 */
2828export class CodexAdapter extends ServiceMap.Service<CodexAdapter, CodexAdapterShape>()(
29 "provider/CodexAdapter",
29 "t3/provider/Services/CodexAdapter",
3030) {}
apps/server/src/provider/Services/ProviderAdapterRegistry.ts+1-1
......@@ -37,6 +37,6 @@ export interface ProviderAdapterRegistryShape {
3737export class ProviderAdapterRegistry extends ServiceMap.Service<
3838 ProviderAdapterRegistry,
3939 ProviderAdapterRegistryShape
40>()("provider/ProviderAdapterRegistry") {}
40>()("t3/provider/Services/ProviderAdapterRegistry") {}
4141
4242// Dummy comment for workflow testing.
apps/server/src/provider/Services/ProviderService.ts+1-1
......@@ -105,5 +105,5 @@ export interface ProviderServiceShape {
105105 * ProviderService - Service tag for provider orchestration.
106106 */
107107export class ProviderService extends ServiceMap.Service<ProviderService, ProviderServiceShape>()(
108 "provider/ProviderService",
108 "t3/provider/Services/ProviderService",
109109) {}
apps/server/src/provider/Services/ProviderSessionDirectory.ts+1-1
......@@ -99,4 +99,4 @@ export interface ProviderSessionDirectoryShape {
9999export class ProviderSessionDirectory extends ServiceMap.Service<
100100 ProviderSessionDirectory,
101101 ProviderSessionDirectoryShape
102>()("provider/ProviderSessionDirectory") {}
102>()("t3/provider/Services/ProviderSessionDirectory") {}
apps/server/src/serverLayers.ts+1-1
......@@ -1,6 +1,6 @@
11import path from "node:path";
22
3import { NodeServices } from "@effect/platform-node";
3import * as NodeServices from "@effect/platform-node/NodeServices";
44import { Effect, Layer } from "effect";
55import * as SqlClient from "effect/unstable/sql/SqlClient";
66
apps/server/src/terminal/Layers/Manager.test.ts+42-4
......@@ -9,7 +9,13 @@ import {
99} from "@t3tools/contracts";
1010import { afterEach, describe, expect, it } from "vitest";
1111
12import type { PtyAdapterShape, PtyExitEvent, PtyProcess, PtySpawnInput } from "../Services/PTY";
12import {
13 PtySpawnError,
14 type PtyAdapterShape,
15 type PtyExitEvent,
16 type PtyProcess,
17 type PtySpawnInput,
18} from "../Services/PTY";
1319import { TerminalManagerRuntime } from "./Manager";
1420import { Effect, Encoding } from "effect";
1521
......@@ -69,14 +75,33 @@ class FakePtyAdapter implements PtyAdapterShape {
6975 readonly spawnFailures: Error[] = [];
7076 private nextPid = 9000;
7177
72 spawn(input: PtySpawnInput): Effect.Effect<PtyProcess> {
78 constructor(private readonly mode: "sync" | "async" = "sync") {}
79
80 spawn(input: PtySpawnInput): Effect.Effect<PtyProcess, PtySpawnError> {
7381 this.spawnInputs.push(input);
7482 const failure = this.spawnFailures.shift();
7583 if (failure) {
76 throw failure;
84 return Effect.fail(
85 new PtySpawnError({
86 adapter: "fake",
87 message: "Failed to spawn PTY process",
88 cause: failure,
89 }),
90 );
7791 }
7892 const process = new FakePtyProcess(this.nextPid++);
7993 this.processes.push(process);
94 if (this.mode === "async") {
95 return Effect.tryPromise({
96 try: async () => process,
97 catch: (cause) =>
98 new PtySpawnError({
99 adapter: "fake",
100 message: "Failed to spawn PTY process",
101 cause,
102 }),
103 });
104 }
80105 return Effect.succeed(process);
81106 }
82107}
......@@ -150,11 +175,12 @@ describe("TerminalManager", () => {
150175 subprocessPollIntervalMs?: number;
151176 processKillGraceMs?: number;
152177 maxRetainedInactiveSessions?: number;
178 ptyAdapter?: FakePtyAdapter;
153179 } = {},
154180 ) {
155181 const logsDir = fs.mkdtempSync(path.join(os.tmpdir(), "t3code-terminal-"));
156182 tempDirs.push(logsDir);
157 const ptyAdapter = new FakePtyAdapter();
183 const ptyAdapter = options.ptyAdapter ?? new FakePtyAdapter();
158184 const manager = new TerminalManagerRuntime({
159185 logsDir,
160186 ptyAdapter,
......@@ -189,6 +215,18 @@ describe("TerminalManager", () => {
189215 manager.dispose();
190216 });
191217
218 it("supports asynchronous PTY spawn effects", async () => {
219 const { manager, ptyAdapter } = makeManager(5, { ptyAdapter: new FakePtyAdapter("async") });
220
221 const snapshot = await manager.open(openInput());
222
223 expect(snapshot.status).toBe("running");
224 expect(ptyAdapter.spawnInputs).toHaveLength(1);
225 expect(ptyAdapter.processes).toHaveLength(1);
226
227 manager.dispose();
228 });
229
192230 it("forwards write and resize to active pty process", async () => {
193231 const { manager, ptyAdapter } = makeManager();
194232 await manager.open(openInput());
apps/server/src/terminal/Layers/Manager.ts+75-20
......@@ -117,8 +117,43 @@ function resolveShellCandidates(shellResolver: () => string): ShellCandidate[] {
117117}
118118
119119function isRetryableShellSpawnError(error: unknown): boolean {
120 if (!(error instanceof Error)) return false;
121 const message = error.message.toLowerCase();
120 const queue: unknown[] = [error];
121 const seen = new Set<unknown>();
122 const messages: string[] = [];
123
124 while (queue.length > 0) {
125 const current = queue.shift();
126 if (!current || seen.has(current)) {
127 continue;
128 }
129 seen.add(current);
130
131 if (typeof current === "string") {
132 messages.push(current);
133 continue;
134 }
135
136 if (current instanceof Error) {
137 messages.push(current.message);
138 const cause = (current as { cause?: unknown }).cause;
139 if (cause) {
140 queue.push(cause);
141 }
142 continue;
143 }
144
145 if (typeof current === "object") {
146 const value = current as { message?: unknown; cause?: unknown };
147 if (typeof value.message === "string") {
148 messages.push(value.message);
149 }
150 if (value.cause) {
151 queue.push(value.cause);
152 }
153 }
154 }
155
156 const message = messages.join(" ").toLowerCase();
122157 return (
123158 message.includes("posix_spawnp failed") ||
124159 message.includes("enoent") ||
......@@ -354,7 +389,7 @@ export class TerminalManagerRuntime extends EventEmitter<TerminalManagerEvents>
354389 };
355390 this.sessions.set(sessionKey, session);
356391 this.evictInactiveSessionsIfNeeded();
357 this.startSession(session, { ...input, cols, rows }, "started");
392 await this.startSession(session, { ...input, cols, rows }, "started");
358393 return this.snapshot(session);
359394 }
360395
......@@ -380,7 +415,7 @@ export class TerminalManagerRuntime extends EventEmitter<TerminalManagerEvents>
380415 }
381416
382417 if (!existing.process) {
383 this.startSession(existing, { ...input, cols: targetCols, rows: targetRows }, "started");
418 await this.startSession(existing, { ...input, cols: targetCols, rows: targetRows }, "started");
384419 return this.snapshot(existing);
385420 }
386421
......@@ -477,7 +512,7 @@ export class TerminalManagerRuntime extends EventEmitter<TerminalManagerEvents>
477512
478513 session.history = "";
479514 await this.persistHistory(input.threadId, input.terminalId, session.history);
480 this.startSession(session, { ...input, cols, rows }, "restarted");
515 await this.startSession(session, { ...input, cols, rows }, "restarted");
481516 return this.snapshot(session);
482517 });
483518 }
......@@ -528,11 +563,11 @@ export class TerminalManagerRuntime extends EventEmitter<TerminalManagerEvents>
528563 this.persistQueues.clear();
529564 }
530565
531 private startSession(
566 private async startSession(
532567 session: TerminalSessionState,
533568 input: TerminalStartInput,
534569 eventType: "started" | "restarted",
535 ): void {
570 ): Promise<void> {
536571 this.stopProcess(session);
537572
538573 session.status = "starting";
......@@ -551,26 +586,46 @@ export class TerminalManagerRuntime extends EventEmitter<TerminalManagerEvents>
551586 const terminalEnv = createTerminalSpawnEnv(process.env, session.runtimeEnv);
552587 let lastSpawnError: unknown = null;
553588
554 for (const candidate of shellCandidates) {
589 const spawnWithCandidate = (candidate: ShellCandidate) =>
590 Effect.runPromise(
591 this.ptyAdapter.spawn({
592 shell: candidate.shell,
593 ...(candidate.args ? { args: candidate.args } : {}),
594 cwd: session.cwd,
595 cols: session.cols,
596 rows: session.rows,
597 env: terminalEnv,
598 }),
599 );
600
601 const trySpawn = async (
602 candidates: ShellCandidate[],
603 index = 0,
604 ): Promise<{ process: PtyProcess; shellLabel: string } | null> => {
605 if (index >= candidates.length) {
606 return null;
607 }
608 const candidate = candidates[index];
609 if (!candidate) {
610 return null;
611 }
612
555613 try {
556 ptyProcess = Effect.runSync(
557 this.ptyAdapter.spawn({
558 shell: candidate.shell,
559 ...(candidate.args ? { args: candidate.args } : {}),
560 cwd: session.cwd,
561 cols: session.cols,
562 rows: session.rows,
563 env: terminalEnv,
564 }),
565 );
566 startedShell = formatShellCandidate(candidate);
567 break;
614 const process = await spawnWithCandidate(candidate);
615 return { process, shellLabel: formatShellCandidate(candidate) };
568616 } catch (error) {
569617 lastSpawnError = error;
570618 if (!isRetryableShellSpawnError(error)) {
571619 throw error;
572620 }
621 return trySpawn(candidates, index + 1);
573622 }
623 };
624
625 const spawnResult = await trySpawn(shellCandidates);
626 if (spawnResult) {
627 ptyProcess = spawnResult.process;
628 startedShell = spawnResult.shellLabel;
574629 }
575630
576631 if (!ptyProcess) {
apps/server/src/terminal/Layers/NodePTY.test.ts+1-1
......@@ -2,7 +2,7 @@ import { FileSystem, Path, Effect } from "effect";
22import { assert, it } from "@effect/vitest";
33
44import { ensureNodePtySpawnHelperExecutable } from "./NodePTY";
5import { NodeServices } from "@effect/platform-node";
5import * as NodeServices from "@effect/platform-node/NodeServices";
66
77it.layer(NodeServices.layer)("ensureNodePtySpawnHelperExecutable", (it) => {
88 it.effect("adds executable bits when helper exists but is not executable", () =>
apps/server/src/terminal/Layers/NodePTY.ts+5-4
......@@ -37,11 +37,12 @@ export const ensureNodePtySpawnHelperExecutable = Effect.fn(function* (explicitP
3737 didEnsureSpawnHelperExecutable = true;
3838 }
3939
40 const stat = yield* fs.stat(helperPath);
41 const mode = stat.mode & 0o777;
42 if ((mode & 0o111) === 0) {
43 yield* fs.chmod(helperPath, mode | 0o111);
40 if (!(yield* fs.exists(helperPath))) {
41 return;
4442 }
43
44 // Best-effort: avoid FileSystem.stat in packaged mode where some fs metadata can be missing.
45 yield* fs.chmod(helperPath, 0o755).pipe(Effect.orElseSucceed(() => undefined));
4546});
4647
4748class NodePtyProcess implements PtyProcess {
apps/server/src/terminal/Services/Manager.ts+1-1
......@@ -113,5 +113,5 @@ export interface TerminalManagerShape {
113113 * TerminalManager - Service tag for terminal session orchestration.
114114 */
115115export class TerminalManager extends ServiceMap.Service<TerminalManager, TerminalManagerShape>()(
116 "terminal/TerminalManager",
116 "t3/terminal/Services/Manager/TerminalManager",
117117) {}
apps/server/src/terminal/Services/PTY.ts+12-3
......@@ -6,7 +6,16 @@
66 *
77 * @module PtyAdapter
88 */
9import { Effect, ServiceMap } from "effect";
9import { Effect, Schema, ServiceMap } from "effect";
10
11/**
12 * PtyError - Error type for PTY adapter operations.
13 */
14export class PtySpawnError extends Schema.TaggedErrorClass<PtySpawnError>()("PtySpawnError", {
15 adapter: Schema.String,
16 message: Schema.String,
17 cause: Schema.optional(Schema.Defect),
18}) {}
1019
1120export interface PtyExitEvent {
1221 exitCode: number;
......@@ -38,12 +47,12 @@ export interface PtyAdapterShape {
3847 /**
3948 * Spawn a PTY process for a terminal session.
4049 */
41 spawn(input: PtySpawnInput): Effect.Effect<PtyProcess>;
50 spawn(input: PtySpawnInput): Effect.Effect<PtyProcess, PtySpawnError>;
4251}
4352
4453/**
4554 * PtyAdapter - Service tag for PTY process integration.
4655 */
4756export class PtyAdapter extends ServiceMap.Service<PtyAdapter, PtyAdapterShape>()(
48 "terminal/PtyAdapter",
57 "t3/terminal/Services/PTY/PtyAdapter",
4958) {}
apps/server/src/wsServer.ts+10-14
......@@ -23,7 +23,7 @@ import {
2323 WsPush,
2424 WsResponse,
2525} from "@t3tools/contracts";
26import { NodeHttpServer } from "@effect/platform-node";
26import * as NodeHttpServer from "@effect/platform-node/NodeHttpServer";
2727import { Cause, Effect, Exit, Layer, Ref, Schema, Scope, ServiceMap, Stream, Struct } from "effect";
2828import { WebSocketServer, type WebSocket } from "ws";
2929
......@@ -79,7 +79,7 @@ export interface ServerShape {
7979/**
8080 * Server - Service tag for HTTP/WebSocket lifecycle management.
8181 */
82export class Server extends ServiceMap.Service<Server, ServerShape>()("server/Server") {}
82export class Server extends ServiceMap.Service<Server, ServerShape>()("t3/wsServer/Server") {}
8383
8484const isServerNotRunningError = (error: unknown): boolean => {
8585 if (!(error instanceof Error)) return false;
......@@ -320,22 +320,18 @@ export const createServer = Effect.fn(function* (): Effect.fn.Return<
320320 yield* Effect.addFinalizer(() => Scope.close(subscriptionsScope, Exit.void));
321321
322322 yield* Stream.runForEach(orchestrationEngine.streamDomainEvents, (event) =>
323 Effect.gen(function* () {
324 yield* broadcastPush({
325 type: "push",
326 channel: ORCHESTRATION_WS_CHANNELS.domainEvent,
327 data: event,
328 });
323 broadcastPush({
324 type: "push",
325 channel: ORCHESTRATION_WS_CHANNELS.domainEvent,
326 data: event,
329327 }),
330328 ).pipe(Effect.forkIn(subscriptionsScope));
331329
332330 yield* Stream.runForEach(keybindingsManager.changes, (event) =>
333 Effect.gen(function* () {
334 yield* broadcastPush({
335 type: "push",
336 channel: WS_CHANNELS.serverConfigUpdated,
337 data: event,
338 });
331 broadcastPush({
332 type: "push",
333 channel: WS_CHANNELS.serverConfigUpdated,
334 data: event,
339335 }),
340336 ).pipe(Effect.forkIn(subscriptionsScope));
341337
apps/server/tsconfig.json+8-1
......@@ -8,7 +8,14 @@
88 "allowImportingTsExtensions": true,
99 "plugins": [
1010 {
11 "name": "@effect/language-service"
11 "name": "@effect/language-service",
12 "namespaceImportPackages": ["@effect/platform-node"],
13 "diagnosticSeverity": {
14 "importFromBarrel": "error",
15 "anyUnknownInErrorContext": "warning",
16 "instanceOfSchema": "warning",
17 "deterministicKeys": "warning"
18 }
1219 }
1320 ]
1421 },
apps/web/package.json+2-1
......@@ -21,7 +21,7 @@
2121 "@xterm/addon-fit": "^0.11.0",
2222 "@xterm/xterm": "^6.0.0",
2323 "class-variance-authority": "^0.7.1",
24 "effect": "4.0.0-beta.10",
24 "effect": "catalog:",
2525 "highlight.js": "^11.11.1",
2626 "lucide-react": "^0.564.0",
2727 "react": "^19.0.0",
......@@ -32,6 +32,7 @@
3232 "tailwind-merge": "^3.4.0"
3333 },
3434 "devDependencies": {
35 "@effect/language-service": "catalog:",
3536 "@tailwindcss/vite": "^4.0.0",
3637 "@tanstack/router-plugin": "^1.161.0",
3738 "@types/react": "^19.0.0",
apps/web/src/components/DiffWorkerPoolProvider.tsx+2-2
......@@ -1,4 +1,5 @@
11import { WorkerPoolContextProvider } from "@pierre/diffs/react";
2import DiffsWorker from "@pierre/diffs/worker/worker.js?worker";
23import { useMemo, type ReactNode } from "react";
34
45export function DiffWorkerPoolProvider({ children }: { children?: ReactNode }) {
......@@ -11,8 +12,7 @@ export function DiffWorkerPoolProvider({ children }: { children?: ReactNode }) {
1112 return (
1213 <WorkerPoolContextProvider
1314 poolOptions={{
14 workerFactory: () =>
15 new Worker(new URL("../workers/diffs.worker.ts", import.meta.url), { type: "module" }),
15 workerFactory: () => new DiffsWorker(),
1616 poolSize: workerPoolSize,
1717 totalASTLRUCacheSize: 240,
1818 }}
apps/web/src/workers/diffs.worker.ts deleted-1
......@@ -1 +0,0 @@
1import "@pierre/diffs/worker/worker.js";
apps/web/tsconfig.json+13-1
......@@ -7,7 +7,19 @@
77 "types": ["vite/client"],
88 "paths": {
99 "~/*": ["./src/*"]
10 }
10 },
11 "plugins": [
12 {
13 "name": "@effect/language-service",
14 "namespaceImportPackages": ["@effect/platform-node"],
15 "diagnosticSeverity": {
16 "importFromBarrel": "error",
17 "anyUnknownInErrorContext": "warning",
18 "instanceOfSchema": "warning",
19 "deterministicKeys": "warning"
20 }
21 }
22 ]
1123 },
1224 "include": ["src", "vite.config.ts"]
1325}
bun.lock+20-12
......@@ -35,17 +35,17 @@
3535 "t3": "./dist/index.mjs",
3636 },
3737 "dependencies": {
38 "@effect/platform-node": "4.0.0-beta.10",
39 "@effect/sql-sqlite-bun": "4.0.0-beta.10",
38 "@effect/platform-node": "catalog:",
39 "@effect/sql-sqlite-bun": "catalog:",
4040 "@pierre/diffs": "^1.1.0-beta.16",
41 "effect": "4.0.0-beta.10",
41 "effect": "catalog:",
4242 "node-pty": "^1.1.0",
4343 "open": "^10.1.0",
4444 "ws": "^8.18.0",
4545 },
4646 "devDependencies": {
4747 "@effect/language-service": "0.75.1",
48 "@effect/vitest": "4.0.0-beta.10",
48 "@effect/vitest": "catalog:",
4949 "@t3tools/contracts": "workspace:*",
5050 "@t3tools/web": "workspace:*",
5151 "@types/bun": "^1.3.9",
......@@ -70,7 +70,7 @@
7070 "@xterm/addon-fit": "^0.11.0",
7171 "@xterm/xterm": "^6.0.0",
7272 "class-variance-authority": "^0.7.1",
73 "effect": "4.0.0-beta.10",
73 "effect": "catalog:",
7474 "highlight.js": "^11.11.1",
7575 "lucide-react": "^0.564.0",
7676 "react": "^19.0.0",
......@@ -81,6 +81,7 @@
8181 "tailwind-merge": "^3.4.0",
8282 },
8383 "devDependencies": {
84 "@effect/language-service": "catalog:",
8485 "@tailwindcss/vite": "^4.0.0",
8586 "@tanstack/router-plugin": "^1.161.0",
8687 "@types/react": "^19.0.0",
......@@ -96,16 +97,23 @@
9697 "name": "@t3tools/contracts",
9798 "version": "0.0.0",
9899 "dependencies": {
99 "effect": "4.0.0-beta.10",
100 "effect": "catalog:",
100101 },
101102 "devDependencies": {
102103 "@effect/language-service": "0.75.1",
103 "@effect/vitest": "4.0.0-beta.10",
104 "@effect/vitest": "catalog:",
104105 "tsdown": "^0.20.3",
105106 "typescript": "^5.7.3",
106107 },
107108 },
108109 },
110 "catalog": {
111 "@effect/language-service": "0.75.1",
112 "@effect/platform-node": "https://pkg.pr.new/Effect-TS/effect-smol/@effect/platform-node@f591738",
113 "@effect/sql-sqlite-bun": "https://pkg.pr.new/Effect-TS/effect-smol/@effect/sql-sqlite-bun@f591738",
114 "@effect/vitest": "https://pkg.pr.new/Effect-TS/effect-smol/@effect/vitest@f591738",
115 "effect": "https://pkg.pr.new/Effect-TS/effect-smol/effect@f591738",
116 },
109117 "packages": {
110118 "@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=="],
111119
......@@ -157,13 +165,13 @@
157165
158166 "@effect/language-service": ["@effect/language-service@0.75.1", "", { "bin": { "effect-language-service": "cli.js" } }, "sha512-g9xD2tAQgRFpYC2YgpZq02VeSL5fBbFJ0B/g1o+14NuNmwtaYJc7SjiLWAA9eyhJHosNrn6h1Ye+Kx6j5mN0AA=="],
159167
160 "@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=="],
168 "@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" } }],
161169
162 "@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=="],
170 "@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" } }],
163171
164 "@effect/sql-sqlite-bun": ["@effect/sql-sqlite-bun@4.0.0-beta.10", "", { "peerDependencies": { "effect": "^4.0.0-beta.10" } }, "sha512-LY1jNu2uqGYsJCVjVU73nzcZ+kAYBDsG7O8x5ddmkIXj10RmnIhjYMStWBa0NZOFg4Hsl+CadxtsDy3edaOVeA=="],
172 "@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" } }],
165173
166 "@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=="],
174 "@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" } }],
167175
168176 "@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=="],
169177
......@@ -729,7 +737,7 @@
729737
730738 "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=="],
731739
732 "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=="],
740 "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" } }],
733741
734742 "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=="],
735743
package.json+13-4
......@@ -1,10 +1,19 @@
11{
22 "name": "@t3tools/monorepo",
33 "private": true,
4 "workspaces": [
5 "apps/*",
6 "packages/*"
7 ],
4 "workspaces": {
5 "packages": [
6 "apps/*",
7 "packages/*"
8 ],
9 "catalog": {
10 "effect": "https://pkg.pr.new/Effect-TS/effect-smol/effect@f591738",
11 "@effect/platform-node": "https://pkg.pr.new/Effect-TS/effect-smol/@effect/platform-node@f591738",
12 "@effect/sql-sqlite-bun": "https://pkg.pr.new/Effect-TS/effect-smol/@effect/sql-sqlite-bun@f591738",
13 "@effect/vitest": "https://pkg.pr.new/Effect-TS/effect-smol/@effect/vitest@f591738",
14 "@effect/language-service": "0.75.1"
15 }
16 },
817 "scripts": {
918 "dev": "bun run scripts/dev-runner.mjs dev",
1019 "dev:server": "bun run scripts/dev-runner.mjs dev:server",
packages/contracts/package.json+2-2
......@@ -24,11 +24,11 @@
2424 "test": "vitest run"
2525 },
2626 "dependencies": {
27 "effect": "4.0.0-beta.10"
27 "effect": "catalog:"
2828 },
2929 "devDependencies": {
3030 "@effect/language-service": "0.75.1",
31 "@effect/vitest": "4.0.0-beta.10",
31 "@effect/vitest": "catalog:",
3232 "tsdown": "^0.20.3",
3333 "typescript": "^5.7.3"
3434 }
packages/contracts/tsconfig.json+8-1
......@@ -3,7 +3,14 @@
33 "compilerOptions": {
44 "plugins": [
55 {
6 "name": "@effect/language-service"
6 "name": "@effect/language-service",
7 "namespaceImportPackages": ["@effect/platform-node"],
8 "diagnosticSeverity": {
9 "importFromBarrel": "error",
10 "anyUnknownInErrorContext": "warning",
11 "instanceOfSchema": "warning",
12 "deterministicKeys": "warning"
13 }
714 }
815 ]
916 },
scripts/build-desktop-dmg.mjs+43-1
......@@ -206,6 +206,42 @@ async function validateBundledClientAssets(clientDir) {
206206 }
207207}
208208
209function readWorkspaceCatalog(rootPackageJson) {
210 const workspaces = rootPackageJson?.workspaces;
211 if (!workspaces || typeof workspaces !== "object" || Array.isArray(workspaces)) {
212 return {};
213 }
214
215 const catalog = workspaces.catalog;
216 if (!catalog || typeof catalog !== "object" || Array.isArray(catalog)) {
217 return {};
218 }
219
220 return catalog;
221}
222
223function resolveCatalogDependencies(dependencies, catalog, dependencySourceLabel) {
224 return Object.fromEntries(
225 Object.entries(dependencies).map(([dependencyName, spec]) => {
226 if (typeof spec !== "string" || !spec.startsWith("catalog:")) {
227 return [dependencyName, spec];
228 }
229
230 const catalogKey = spec.slice("catalog:".length).trim();
231 const lookupKey = catalogKey.length > 0 ? catalogKey : dependencyName;
232 const resolvedSpec = catalog[lookupKey];
233 if (typeof resolvedSpec !== "string" || resolvedSpec.length === 0) {
234 fail(
235 `Unable to resolve '${spec}' for ${dependencySourceLabel} dependency '${dependencyName}'. ` +
236 `Expected key '${lookupKey}' in root workspace catalog.`,
237 );
238 }
239
240 return [dependencyName, resolvedSpec];
241 }),
242 );
243}
244
209245async function main() {
210246 const options = parseArgs(process.argv.slice(2));
211247 if (options.help) {
......@@ -217,6 +253,7 @@ async function main() {
217253 fail("This script only builds macOS .dmg artifacts and must run on macOS.");
218254 }
219255
256 const rootPkg = await readJson(path.join(repoRoot, "package.json"));
220257 const desktopPkg = await readJson(path.join(repoRoot, "apps/desktop/package.json"));
221258 const serverPkg = await readJson(path.join(repoRoot, "apps/server/package.json"));
222259
......@@ -229,6 +266,11 @@ async function main() {
229266 if (!dependencies || Object.keys(dependencies).length === 0) {
230267 fail("Could not resolve production dependencies from apps/server/package.json.");
231268 }
269 const resolvedDependencies = resolveCatalogDependencies(
270 dependencies,
271 readWorkspaceCatalog(rootPkg),
272 "apps/server",
273 );
232274
233275 const appVersion = options.version ?? serverPkg.version ?? "0.1.0";
234276 const stageRoot = path.join(os.tmpdir(), "t3code-desktop-dmg-stage");
......@@ -291,7 +333,7 @@ async function main() {
291333 category: "public.app-category.developer-tools",
292334 },
293335 },
294 dependencies,
336 dependencies: resolvedDependencies,
295337 devDependencies: {
296338 electron: electronVersion,
297339 },