Skip to content

Commit b19bc17

Browse files
committed
add fixer
1 parent 4c6075e commit b19bc17

16 files changed

Lines changed: 2970 additions & 306 deletions

bun.lock

Lines changed: 17 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "effect-devtui",
3-
"version": "0.1.0",
3+
"version": "0.2.0",
44
"description": "Terminal UI for Effect DevTools",
55
"keywords": [
66
"effect",
@@ -38,6 +38,7 @@
3838
},
3939
"dependencies": {
4040
"@effect/experimental": "^0.57.11",
41+
"@effect/language-service": "^0.62.1",
4142
"@effect/platform": "^0.93.6",
4243
"@effect/platform-bun": "^0.86.0",
4344
"@effect/platform-node": "^0.103.0",
@@ -54,7 +55,8 @@
5455
"@types/node": "~24.1.0",
5556
"@types/react": "^19.0.0",
5657
"@types/ws": "^8.18.1",
58+
"recast": "^0.23.11",
5759
"tsx": "^4.21.0",
58-
"typescript": "^5.8.3"
60+
"typescript": "^5.9.3"
5961
}
6062
}
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
/**
2+
* Test file for layer analyzer
3+
*
4+
* Tests:
5+
* 1. Detection of missing service requirements
6+
* 2. Resolution of available layer candidates
7+
* 3. Transitive dependency resolution (CacheLive depends on ConfigService)
8+
* 4. Multiple candidates per service for user selection
9+
*/
10+
import { Effect, Context, Layer } from "effect";
11+
12+
// ============================================================================
13+
// SERVICE DEFINITIONS
14+
// ============================================================================
15+
16+
class DatabaseService extends Context.Tag("DatabaseService")<
17+
DatabaseService,
18+
{ readonly query: (sql: string) => Effect.Effect<unknown[]> }
19+
>() {}
20+
21+
class LoggingService extends Context.Tag("LoggingService")<
22+
LoggingService,
23+
{ readonly log: (msg: string) => Effect.Effect<void> }
24+
>() {}
25+
26+
class ConfigService extends Context.Tag("ConfigService")<
27+
ConfigService,
28+
{ readonly get: (key: string) => Effect.Effect<string> }
29+
>() {}
30+
31+
class CacheService extends Context.Tag("CacheService")<
32+
CacheService,
33+
{ readonly get: (key: string) => Effect.Effect<string | null> }
34+
>() {}
35+
36+
// ============================================================================
37+
// LAYER IMPLEMENTATIONS
38+
// ============================================================================
39+
40+
// Database layers - no dependencies
41+
const DatabaseLive = Layer.succeed(DatabaseService, {
42+
query: (sql) => Effect.succeed([{ id: 1, name: "Alice" }]),
43+
});
44+
45+
const DatabaseTest = Layer.succeed(DatabaseService, {
46+
query: (sql) => Effect.succeed([{ id: 999, test: true }]),
47+
});
48+
49+
// Logging layers - no dependencies
50+
const LoggingLive = Layer.succeed(LoggingService, {
51+
log: (msg) => Effect.log(msg),
52+
});
53+
54+
const LoggingTest = Layer.succeed(LoggingService, {
55+
log: (msg) => Effect.sync(() => console.log("[TEST]", msg)),
56+
});
57+
58+
// Config layers - no dependencies
59+
const ConfigLive = Layer.succeed(ConfigService, {
60+
get: (key) => Effect.succeed(process.env[key] ?? ""),
61+
});
62+
63+
const ConfigTest = Layer.succeed(ConfigService, {
64+
get: (key) => Effect.succeed("test-value"),
65+
});
66+
67+
// Cache layers - DEPENDS ON ConfigService (tests transitive resolution)
68+
const CacheLive = Layer.effect(
69+
CacheService,
70+
Effect.gen(function* () {
71+
const config = yield* ConfigService;
72+
const ttl = yield* config.get("CACHE_TTL");
73+
return {
74+
get: (key: string) => Effect.succeed(null as string | null),
75+
};
76+
}),
77+
);
78+
79+
const CacheTest = Layer.effect(
80+
CacheService,
81+
Effect.gen(function* () {
82+
const config = yield* ConfigService;
83+
return {
84+
get: (key: string) => Effect.succeed(("cached-" + key) as string | null),
85+
};
86+
}),
87+
);
88+
89+
// ============================================================================
90+
// PROGRAM THAT REQUIRES SERVICES
91+
// ============================================================================
92+
93+
const myProgram = Effect.gen(function* () {
94+
const db = yield* DatabaseService;
95+
const logger = yield* LoggingService;
96+
const cache = yield* CacheService;
97+
98+
yield* logger.log("Starting query");
99+
100+
// Check cache first
101+
const cached = yield* cache.get("users");
102+
if (cached) {
103+
yield* logger.log("Cache hit");
104+
return JSON.parse(cached);
105+
}
106+
107+
// Query database
108+
const results = yield* db.query("SELECT * FROM users");
109+
yield* logger.log(`Found ${results.length} users`);
110+
111+
return results;
112+
});
113+
114+
// ============================================================================
115+
// This line should trigger the layer analyzer
116+
// The program requires: DatabaseService, LoggingService, CacheService
117+
// CacheLive transitively requires: ConfigService
118+
// ============================================================================
119+
120+
// This will fail type checking - no layers provided!
121+
const runnable = Effect.runPromise(myProgram);

src/__tests__/tsconfig.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"extends": "../../tsconfig.json",
3+
"compilerOptions": {
4+
"strict": true,
5+
"noEmit": true
6+
},
7+
"include": ["**/*.ts"]
8+
}

0 commit comments

Comments
 (0)