diff --git a/.changeset/migrate-effect-v4.md b/.changeset/migrate-effect-v4.md new file mode 100644 index 00000000..6b099147 --- /dev/null +++ b/.changeset/migrate-effect-v4.md @@ -0,0 +1,5 @@ +--- +"@effect/language-service": minor +--- + +Migrate internal Effect dependency from v3 to v4. This updates all CLI and core modules to use the Effect v4 API while maintaining full backward compatibility with existing functionality. diff --git a/CLAUDE.md b/CLAUDE.md index cb27bdd6..0437da0a 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -8,7 +8,7 @@ This workflow should be initiated only if asked by the user. The following steps can be skipped if no typescript file has been changed in this branch, do not attempt any file change without user consent. - run "pnpm lint-fix" to fix code formatting - run "pnpm check" to see if you should fix some type errors -- run "pnpm test" to validate that changes did not broke anything. +- run "pnpm test" and "pnpm test:v4" to validate that changes did not broke anything. ### 3. Documentation checks - if new diagnostics, completions or refactor are added, ensure they are already mentioned in the README.md. Ensure to read examples and test/__snapshots__ related to the change to ensure full understanding of whats changed diff --git a/packages/harness-effect-v3/__snapshots__/layerinfo/documented.ts.AppLive.layerinfo b/packages/harness-effect-v3/__snapshots__/layerinfo/documented.ts.AppLive.layerinfo index 9e5e1242..744bdaa7 100644 --- a/packages/harness-effect-v3/__snapshots__/layerinfo/documented.ts.AppLive.layerinfo +++ b/packages/harness-effect-v3/__snapshots__/layerinfo/documented.ts.AppLive.layerinfo @@ -12,12 +12,12 @@ Not sure you got your composition right? Just write all layers inside a Layer.mergeAll(...) then run this command again and use --outputs to select which outputs to include in composition. Example: --outputs 1,2,3 -  + [x] 1. Cache [ ] 2. DbConnection [ ] 3. FileSystem [x] 4. UserRepository -  + export const AppLive = UserRepository.Default.pipe( Layer.provide(DbConnection.Default), Layer.provideMerge(Cache.Default), diff --git a/packages/harness-effect-v3/__snapshots__/layerinfo/documented.ts.CacheLive.layerinfo b/packages/harness-effect-v3/__snapshots__/layerinfo/documented.ts.CacheLive.layerinfo index e400a971..7e7978a4 100644 --- a/packages/harness-effect-v3/__snapshots__/layerinfo/documented.ts.CacheLive.layerinfo +++ b/packages/harness-effect-v3/__snapshots__/layerinfo/documented.ts.CacheLive.layerinfo @@ -11,10 +11,10 @@ Not sure you got your composition right? Just write all layers inside a Layer.mergeAll(...) then run this command again and use --outputs to select which outputs to include in composition. Example: --outputs 1,2,3 -  + [x] 1. Cache [ ] 2. FileSystem -  + export const CacheLive = Cache.Default.pipe( Layer.provide(FileSystem.Default) ) \ No newline at end of file diff --git a/packages/harness-effect-v3/__snapshots__/layerinfo/duplicated.ts.AppLive.layerinfo b/packages/harness-effect-v3/__snapshots__/layerinfo/duplicated.ts.AppLive.layerinfo index 6b50e107..567343db 100644 --- a/packages/harness-effect-v3/__snapshots__/layerinfo/duplicated.ts.AppLive.layerinfo +++ b/packages/harness-effect-v3/__snapshots__/layerinfo/duplicated.ts.AppLive.layerinfo @@ -24,7 +24,7 @@ Not sure you got your composition right? Just write all layers inside a Layer.mergeAll(...) then run this command again and use --outputs to select which outputs to include in composition. Example: --outputs 1,2,3 -  + [x] 1. Analytics [x] 2. AppService [x] 3. Database @@ -32,7 +32,7 @@ [x] 5. EventsRepository [x] 6. UserRepository [x] 7. UserService -  + export const AppLive = AppService.Default.pipe( Layer.provideMerge(EventService.Default), Layer.provideMerge(UserService.Default), diff --git a/packages/harness-effect-v3/__snapshots__/layerinfo/effect.ts.AppLive.layerinfo b/packages/harness-effect-v3/__snapshots__/layerinfo/effect.ts.AppLive.layerinfo index 9fa424fc..8f03b713 100644 --- a/packages/harness-effect-v3/__snapshots__/layerinfo/effect.ts.AppLive.layerinfo +++ b/packages/harness-effect-v3/__snapshots__/layerinfo/effect.ts.AppLive.layerinfo @@ -9,5 +9,5 @@ Not sure you got your composition right? Just write all layers inside a Layer.mergeAll(...) then run this command again and use --outputs to select which outputs to include in composition. Example: --outputs 1,2,3 -  + [ ] 1. DatabaseContext \ No newline at end of file diff --git a/packages/harness-effect-v3/__snapshots__/layerinfo/followSymbols.ts.followSymbols.layerinfo b/packages/harness-effect-v3/__snapshots__/layerinfo/followSymbols.ts.followSymbols.layerinfo index a0830478..53b1b27f 100644 --- a/packages/harness-effect-v3/__snapshots__/layerinfo/followSymbols.ts.followSymbols.layerinfo +++ b/packages/harness-effect-v3/__snapshots__/layerinfo/followSymbols.ts.followSymbols.layerinfo @@ -13,10 +13,10 @@ Not sure you got your composition right? Just write all layers inside a Layer.mergeAll(...) then run this command again and use --outputs to select which outputs to include in composition. Example: --outputs 1,2,3 -  + [ ] 1. DbConnection [x] 2. UserRepository -  + export const followSymbols = simplePipeIn.pipe( Layer.provide(DbConnection.Default) ) \ No newline at end of file diff --git a/packages/harness-effect-v3/__snapshots__/layerinfo/followSymbols.ts.moreComplex.layerinfo b/packages/harness-effect-v3/__snapshots__/layerinfo/followSymbols.ts.moreComplex.layerinfo index ae79021c..54b1b339 100644 --- a/packages/harness-effect-v3/__snapshots__/layerinfo/followSymbols.ts.moreComplex.layerinfo +++ b/packages/harness-effect-v3/__snapshots__/layerinfo/followSymbols.ts.moreComplex.layerinfo @@ -14,11 +14,11 @@ Not sure you got your composition right? Just write all layers inside a Layer.mergeAll(...) then run this command again and use --outputs to select which outputs to include in composition. Example: --outputs 1,2,3 -  + [ ] 1. Cache [x] 2. DbConnection [x] 3. UserRepository -  + export const moreComplex = UserRepository.Default.pipe( Layer.provideMerge(DbConnection.Default), Layer.provide(cacheWithFs) diff --git a/packages/harness-effect-v3/__snapshots__/layerinfo/generics.ts.NoComment.layerinfo b/packages/harness-effect-v3/__snapshots__/layerinfo/generics.ts.NoComment.layerinfo index 393596aa..4e8cb29c 100644 --- a/packages/harness-effect-v3/__snapshots__/layerinfo/generics.ts.NoComment.layerinfo +++ b/packages/harness-effect-v3/__snapshots__/layerinfo/generics.ts.NoComment.layerinfo @@ -10,5 +10,5 @@ Not sure you got your composition right? Just write all layers inside a Layer.mergeAll(...) then run this command again and use --outputs to select which outputs to include in composition. Example: --outputs 1,2,3 -  + [x] 1. IsGeneric \ No newline at end of file diff --git a/packages/harness-effect-v3/__snapshots__/layerinfo/incorrectInference.ts.testInference.layerinfo b/packages/harness-effect-v3/__snapshots__/layerinfo/incorrectInference.ts.testInference.layerinfo index 9d8cc155..ad5476c4 100644 --- a/packages/harness-effect-v3/__snapshots__/layerinfo/incorrectInference.ts.testInference.layerinfo +++ b/packages/harness-effect-v3/__snapshots__/layerinfo/incorrectInference.ts.testInference.layerinfo @@ -9,5 +9,5 @@ Not sure you got your composition right? Just write all layers inside a Layer.mergeAll(...) then run this command again and use --outputs to select which outputs to include in composition. Example: --outputs 1,2,3 -  + [ ] 1. string \ No newline at end of file diff --git a/packages/harness-effect-v3/__snapshots__/layerinfo/multiple.ts.AppLive.layerinfo b/packages/harness-effect-v3/__snapshots__/layerinfo/multiple.ts.AppLive.layerinfo index b4e5df20..2754029c 100644 --- a/packages/harness-effect-v3/__snapshots__/layerinfo/multiple.ts.AppLive.layerinfo +++ b/packages/harness-effect-v3/__snapshots__/layerinfo/multiple.ts.AppLive.layerinfo @@ -24,7 +24,7 @@ Not sure you got your composition right? Just write all layers inside a Layer.mergeAll(...) then run this command again and use --outputs to select which outputs to include in composition. Example: --outputs 1,2,3 -  + [x] 1. Analytics [x] 2. AppService [x] 3. Database @@ -32,7 +32,7 @@ [x] 5. EventsRepository [x] 6. UserRepository [x] 7. UserService -  + export const AppLive = AppService.Default.pipe( Layer.provideMerge(EventService.Default), Layer.provideMerge(UserService.Default), diff --git a/packages/harness-effect-v3/__snapshots__/layerinfo/simple.ts.cacheWithFs.layerinfo b/packages/harness-effect-v3/__snapshots__/layerinfo/simple.ts.cacheWithFs.layerinfo index 783a17ff..111ee15f 100644 --- a/packages/harness-effect-v3/__snapshots__/layerinfo/simple.ts.cacheWithFs.layerinfo +++ b/packages/harness-effect-v3/__snapshots__/layerinfo/simple.ts.cacheWithFs.layerinfo @@ -10,10 +10,10 @@ Not sure you got your composition right? Just write all layers inside a Layer.mergeAll(...) then run this command again and use --outputs to select which outputs to include in composition. Example: --outputs 1,2,3 -  + [x] 1. Cache [ ] 2. FileSystem -  + export const cacheWithFs = Cache.Default.pipe( Layer.provide(FileSystem.Default) ) \ No newline at end of file diff --git a/packages/harness-effect-v3/__snapshots__/layerinfo/simple.ts.expect.layerinfo b/packages/harness-effect-v3/__snapshots__/layerinfo/simple.ts.expect.layerinfo index 1687819f..cb2de6b8 100644 --- a/packages/harness-effect-v3/__snapshots__/layerinfo/simple.ts.expect.layerinfo +++ b/packages/harness-effect-v3/__snapshots__/layerinfo/simple.ts.expect.layerinfo @@ -14,5 +14,5 @@ Not sure you got your composition right? Just write all layers inside a Layer.mergeAll(...) then run this command again and use --outputs to select which outputs to include in composition. Example: --outputs 1,2,3 -  + [x] 1. UserRepository \ No newline at end of file diff --git a/packages/harness-effect-v3/__snapshots__/layerinfo/simple.ts.liveWithPipeable.layerinfo b/packages/harness-effect-v3/__snapshots__/layerinfo/simple.ts.liveWithPipeable.layerinfo index 541ec1ca..24c870c4 100644 --- a/packages/harness-effect-v3/__snapshots__/layerinfo/simple.ts.liveWithPipeable.layerinfo +++ b/packages/harness-effect-v3/__snapshots__/layerinfo/simple.ts.liveWithPipeable.layerinfo @@ -16,11 +16,11 @@ Not sure you got your composition right? Just write all layers inside a Layer.mergeAll(...) then run this command again and use --outputs to select which outputs to include in composition. Example: --outputs 1,2,3 -  + [x] 1. Cache [x] 2. DbConnection [x] 3. UserRepository -  + export const liveWithPipeable = UserRepository.Default.pipe( Layer.provideMerge(Cache.Default), Layer.provideMerge(DbConnection.Default) diff --git a/packages/harness-effect-v3/__snapshots__/layerinfo/simple.ts.simplePipeIn.layerinfo b/packages/harness-effect-v3/__snapshots__/layerinfo/simple.ts.simplePipeIn.layerinfo index c71573e4..8c62cfc4 100644 --- a/packages/harness-effect-v3/__snapshots__/layerinfo/simple.ts.simplePipeIn.layerinfo +++ b/packages/harness-effect-v3/__snapshots__/layerinfo/simple.ts.simplePipeIn.layerinfo @@ -14,10 +14,10 @@ Not sure you got your composition right? Just write all layers inside a Layer.mergeAll(...) then run this command again and use --outputs to select which outputs to include in composition. Example: --outputs 1,2,3 -  + [ ] 1. Cache [x] 2. UserRepository -  + export const simplePipeIn = UserRepository.Default.pipe( Layer.provide(Cache.Default) ) \ No newline at end of file diff --git a/packages/harness-effect-v3/__snapshots__/layerinfo/specialChars.ts.NoComment.layerinfo b/packages/harness-effect-v3/__snapshots__/layerinfo/specialChars.ts.NoComment.layerinfo index 8c30b7c6..80eab5ba 100644 --- a/packages/harness-effect-v3/__snapshots__/layerinfo/specialChars.ts.NoComment.layerinfo +++ b/packages/harness-effect-v3/__snapshots__/layerinfo/specialChars.ts.NoComment.layerinfo @@ -10,5 +10,5 @@ Not sure you got your composition right? Just write all layers inside a Layer.mergeAll(...) then run this command again and use --outputs to select which outputs to include in composition. Example: --outputs 1,2,3 -  + [x] 1. IsGeneric<"WithChars#!"> \ No newline at end of file diff --git a/packages/harness-effect-v4/__snapshots__/layerinfo/documented.ts.AppLive.layerinfo b/packages/harness-effect-v4/__snapshots__/layerinfo/documented.ts.AppLive.layerinfo index 3a7c1116..c4e97ccd 100644 --- a/packages/harness-effect-v4/__snapshots__/layerinfo/documented.ts.AppLive.layerinfo +++ b/packages/harness-effect-v4/__snapshots__/layerinfo/documented.ts.AppLive.layerinfo @@ -12,12 +12,12 @@ Not sure you got your composition right? Just write all layers inside a Layer.mergeAll(...) then run this command again and use --outputs to select which outputs to include in composition. Example: --outputs 1,2,3 -  + [x] 1. Cache [ ] 2. DbConnection [ ] 3. FileSystem [x] 4. UserRepository -  + export const AppLive = UserRepository.Default.pipe( Layer.provide(DbConnection.Default), Layer.provideMerge(Cache.Default), diff --git a/packages/harness-effect-v4/__snapshots__/layerinfo/documented.ts.CacheLive.layerinfo b/packages/harness-effect-v4/__snapshots__/layerinfo/documented.ts.CacheLive.layerinfo index 28649c66..2fa0ef1b 100644 --- a/packages/harness-effect-v4/__snapshots__/layerinfo/documented.ts.CacheLive.layerinfo +++ b/packages/harness-effect-v4/__snapshots__/layerinfo/documented.ts.CacheLive.layerinfo @@ -11,10 +11,10 @@ Not sure you got your composition right? Just write all layers inside a Layer.mergeAll(...) then run this command again and use --outputs to select which outputs to include in composition. Example: --outputs 1,2,3 -  + [x] 1. Cache [ ] 2. FileSystem -  + export const CacheLive = Cache.Default.pipe( Layer.provide(FileSystem.Default) ) \ No newline at end of file diff --git a/packages/harness-effect-v4/__snapshots__/layerinfo/duplicated.ts.AppLive.layerinfo b/packages/harness-effect-v4/__snapshots__/layerinfo/duplicated.ts.AppLive.layerinfo index f3976bba..69a6aa8d 100644 --- a/packages/harness-effect-v4/__snapshots__/layerinfo/duplicated.ts.AppLive.layerinfo +++ b/packages/harness-effect-v4/__snapshots__/layerinfo/duplicated.ts.AppLive.layerinfo @@ -24,7 +24,7 @@ Not sure you got your composition right? Just write all layers inside a Layer.mergeAll(...) then run this command again and use --outputs to select which outputs to include in composition. Example: --outputs 1,2,3 -  + [x] 1. Analytics [x] 2. AppService [x] 3. Database @@ -32,7 +32,7 @@ [x] 5. EventsRepository [x] 6. UserRepository [x] 7. UserService -  + export const AppLive = AppService.Default.pipe( Layer.provideMerge(EventService.Default), Layer.provideMerge(UserService.Default), diff --git a/packages/harness-effect-v4/__snapshots__/layerinfo/effect.ts.AppLive.layerinfo b/packages/harness-effect-v4/__snapshots__/layerinfo/effect.ts.AppLive.layerinfo index 26644788..b06c35fd 100644 --- a/packages/harness-effect-v4/__snapshots__/layerinfo/effect.ts.AppLive.layerinfo +++ b/packages/harness-effect-v4/__snapshots__/layerinfo/effect.ts.AppLive.layerinfo @@ -9,5 +9,5 @@ Not sure you got your composition right? Just write all layers inside a Layer.mergeAll(...) then run this command again and use --outputs to select which outputs to include in composition. Example: --outputs 1,2,3 -  + [ ] 1. DatabaseContext \ No newline at end of file diff --git a/packages/harness-effect-v4/__snapshots__/layerinfo/followSymbols.ts.followSymbols.layerinfo b/packages/harness-effect-v4/__snapshots__/layerinfo/followSymbols.ts.followSymbols.layerinfo index a0830478..53b1b27f 100644 --- a/packages/harness-effect-v4/__snapshots__/layerinfo/followSymbols.ts.followSymbols.layerinfo +++ b/packages/harness-effect-v4/__snapshots__/layerinfo/followSymbols.ts.followSymbols.layerinfo @@ -13,10 +13,10 @@ Not sure you got your composition right? Just write all layers inside a Layer.mergeAll(...) then run this command again and use --outputs to select which outputs to include in composition. Example: --outputs 1,2,3 -  + [ ] 1. DbConnection [x] 2. UserRepository -  + export const followSymbols = simplePipeIn.pipe( Layer.provide(DbConnection.Default) ) \ No newline at end of file diff --git a/packages/harness-effect-v4/__snapshots__/layerinfo/followSymbols.ts.moreComplex.layerinfo b/packages/harness-effect-v4/__snapshots__/layerinfo/followSymbols.ts.moreComplex.layerinfo index ae79021c..54b1b339 100644 --- a/packages/harness-effect-v4/__snapshots__/layerinfo/followSymbols.ts.moreComplex.layerinfo +++ b/packages/harness-effect-v4/__snapshots__/layerinfo/followSymbols.ts.moreComplex.layerinfo @@ -14,11 +14,11 @@ Not sure you got your composition right? Just write all layers inside a Layer.mergeAll(...) then run this command again and use --outputs to select which outputs to include in composition. Example: --outputs 1,2,3 -  + [ ] 1. Cache [x] 2. DbConnection [x] 3. UserRepository -  + export const moreComplex = UserRepository.Default.pipe( Layer.provideMerge(DbConnection.Default), Layer.provide(cacheWithFs) diff --git a/packages/harness-effect-v4/__snapshots__/layerinfo/multiple.ts.AppLive.layerinfo b/packages/harness-effect-v4/__snapshots__/layerinfo/multiple.ts.AppLive.layerinfo index a12109f5..6d6eb03f 100644 --- a/packages/harness-effect-v4/__snapshots__/layerinfo/multiple.ts.AppLive.layerinfo +++ b/packages/harness-effect-v4/__snapshots__/layerinfo/multiple.ts.AppLive.layerinfo @@ -24,7 +24,7 @@ Not sure you got your composition right? Just write all layers inside a Layer.mergeAll(...) then run this command again and use --outputs to select which outputs to include in composition. Example: --outputs 1,2,3 -  + [x] 1. Analytics [x] 2. AppService [x] 3. Database @@ -32,7 +32,7 @@ [x] 5. EventsRepository [x] 6. UserRepository [x] 7. UserService -  + export const AppLive = AppService.Default.pipe( Layer.provideMerge(EventService.Default), Layer.provideMerge(UserService.Default), diff --git a/packages/harness-effect-v4/__snapshots__/layerinfo/simple.ts.cacheWithFs.layerinfo b/packages/harness-effect-v4/__snapshots__/layerinfo/simple.ts.cacheWithFs.layerinfo index b7353a47..62424e11 100644 --- a/packages/harness-effect-v4/__snapshots__/layerinfo/simple.ts.cacheWithFs.layerinfo +++ b/packages/harness-effect-v4/__snapshots__/layerinfo/simple.ts.cacheWithFs.layerinfo @@ -10,10 +10,10 @@ Not sure you got your composition right? Just write all layers inside a Layer.mergeAll(...) then run this command again and use --outputs to select which outputs to include in composition. Example: --outputs 1,2,3 -  + [x] 1. Cache [ ] 2. FileSystem -  + export const cacheWithFs = Cache.Default.pipe( Layer.provide(FileSystem.Default) ) \ No newline at end of file diff --git a/packages/harness-effect-v4/__snapshots__/layerinfo/simple.ts.expect.layerinfo b/packages/harness-effect-v4/__snapshots__/layerinfo/simple.ts.expect.layerinfo index 89eb7023..d6839b8f 100644 --- a/packages/harness-effect-v4/__snapshots__/layerinfo/simple.ts.expect.layerinfo +++ b/packages/harness-effect-v4/__snapshots__/layerinfo/simple.ts.expect.layerinfo @@ -14,5 +14,5 @@ Not sure you got your composition right? Just write all layers inside a Layer.mergeAll(...) then run this command again and use --outputs to select which outputs to include in composition. Example: --outputs 1,2,3 -  + [x] 1. UserRepository \ No newline at end of file diff --git a/packages/harness-effect-v4/__snapshots__/layerinfo/simple.ts.liveWithPipeable.layerinfo b/packages/harness-effect-v4/__snapshots__/layerinfo/simple.ts.liveWithPipeable.layerinfo index 98e4af0d..243a3f8e 100644 --- a/packages/harness-effect-v4/__snapshots__/layerinfo/simple.ts.liveWithPipeable.layerinfo +++ b/packages/harness-effect-v4/__snapshots__/layerinfo/simple.ts.liveWithPipeable.layerinfo @@ -16,11 +16,11 @@ Not sure you got your composition right? Just write all layers inside a Layer.mergeAll(...) then run this command again and use --outputs to select which outputs to include in composition. Example: --outputs 1,2,3 -  + [x] 1. Cache [x] 2. DbConnection [x] 3. UserRepository -  + export const liveWithPipeable = UserRepository.Default.pipe( Layer.provideMerge(Cache.Default), Layer.provideMerge(DbConnection.Default) diff --git a/packages/harness-effect-v4/__snapshots__/layerinfo/simple.ts.simplePipeIn.layerinfo b/packages/harness-effect-v4/__snapshots__/layerinfo/simple.ts.simplePipeIn.layerinfo index 4067627b..fe35c42f 100644 --- a/packages/harness-effect-v4/__snapshots__/layerinfo/simple.ts.simplePipeIn.layerinfo +++ b/packages/harness-effect-v4/__snapshots__/layerinfo/simple.ts.simplePipeIn.layerinfo @@ -14,10 +14,10 @@ Not sure you got your composition right? Just write all layers inside a Layer.mergeAll(...) then run this command again and use --outputs to select which outputs to include in composition. Example: --outputs 1,2,3 -  + [ ] 1. Cache [x] 2. UserRepository -  + export const simplePipeIn = UserRepository.Default.pipe( Layer.provide(Cache.Default) ) \ No newline at end of file diff --git a/packages/harness-effect-v4/__snapshots__/layerinfo/specialChars.ts.NoComment.layerinfo b/packages/harness-effect-v4/__snapshots__/layerinfo/specialChars.ts.NoComment.layerinfo index 8c30b7c6..80eab5ba 100644 --- a/packages/harness-effect-v4/__snapshots__/layerinfo/specialChars.ts.NoComment.layerinfo +++ b/packages/harness-effect-v4/__snapshots__/layerinfo/specialChars.ts.NoComment.layerinfo @@ -10,5 +10,5 @@ Not sure you got your composition right? Just write all layers inside a Layer.mergeAll(...) then run this command again and use --outputs to select which outputs to include in composition. Example: --outputs 1,2,3 -  + [x] 1. IsGeneric<"WithChars#!"> \ No newline at end of file diff --git a/packages/language-service/package.json b/packages/language-service/package.json index ac078cea..453225bb 100644 --- a/packages/language-service/package.json +++ b/packages/language-service/package.json @@ -43,15 +43,9 @@ "devDependencies": { "pako": "^2.1.0", "@typescript-eslint/project-service": "^8.52.0", - "@effect/cli": "^0.73.0", - "@effect/experimental": "^0.58.0", - "@effect/platform": "0.94.1", - "@effect/platform-node": "0.104.0", - "@effect/printer-ansi": "^0.47.0", - "@effect/rpc": "^0.73.0", - "@effect/sql": "^0.49.0", + "@effect/platform-node": "^4.0.0-beta.12", "@types/pako": "^2.0.4", - "effect": "^3.19.14", + "effect": "^4.0.0-beta.12", "ts-patch": "^3.3.0" } } diff --git a/packages/language-service/src/cli.ts b/packages/language-service/src/cli.ts index 69c05d3e..7a23fee2 100644 --- a/packages/language-service/src/cli.ts +++ b/packages/language-service/src/cli.ts @@ -1,11 +1,11 @@ #!/usr/bin/env node -import * as Command from "@effect/cli/Command" -import * as NodeContext from "@effect/platform-node/NodeContext" import * as NodeRuntime from "@effect/platform-node/NodeRuntime" +import * as NodeServices from "@effect/platform-node/NodeServices" import * as Console from "effect/Console" import * as Effect from "effect/Effect" import * as Layer from "effect/Layer" +import { Command } from "effect/unstable/cli" import packageJson from "../package.json" import { check } from "./cli/check" import { codegen } from "./cli/codegen" @@ -22,18 +22,32 @@ const cliCommand = Command.make( "effect-language-service", {}, () => Console.log("Please select a command or run --help.") -).pipe(Command.withSubcommands([setup, patch, unpatch, check, diagnostics, quickfixes, codegen, overview, layerInfo])) +).pipe(Command.withSubcommands([ + { + group: "Getting started", + commands: [setup] + }, + { + group: "Diagnostics at compile-time", + commands: [patch, unpatch, check] + }, + { + group: "Diagnostics", + commands: [diagnostics, quickfixes, codegen] + }, + { + group: "Project utilities", + commands: [overview, layerInfo] + } +])) const main = Command.run(cliCommand, { - name: "effect-language-service", version: packageJson.version }) -const cliLayers = Layer.merge(NodeContext.layer, TypeScriptContext.live(process.cwd())) +const cliLayers = Layer.merge(NodeServices.layer, TypeScriptContext.live(process.cwd())) -main(process.argv).pipe( - Effect.provide(cliLayers), - NodeRuntime.runMain({ - disableErrorReporting: false - }) +NodeRuntime.runMain( + main.pipe(Effect.provide(cliLayers)), + { disableErrorReporting: false } ) diff --git a/packages/language-service/src/cli/ansi.ts b/packages/language-service/src/cli/ansi.ts new file mode 100644 index 00000000..28264971 --- /dev/null +++ b/packages/language-service/src/cli/ansi.ts @@ -0,0 +1,26 @@ +// ANSI escape codes +export const RESET = "\x1b[0m" +export const BOLD = "\x1b[0;1m" +export const DIM = "\x1b[0;90m" // blackBright +export const RED = "\x1b[0;31m" +export const GREEN = "\x1b[0;32m" +export const YELLOW = "\x1b[0;33m" +export const BLUE = "\x1b[0;34m" +export const CYAN = "\x1b[0;36m" +export const WHITE = "\x1b[0;37m" +export const CYAN_BRIGHT = "\x1b[0;96m" +export const BG_BLACK_BRIGHT = "\x1b[0;100m" +export const BG_RED = "\x1b[41m" +export const BG_YELLOW = "\x1b[43m" +export const BG_BLUE = "\x1b[0;44m" +export const BG_CYAN = "\x1b[0;46m" + +export const ansi = (text: string, code: string): string => `${code}${text}${RESET}` + +// Terminal control sequences +export const ERASE_LINE = "\x1b[2K" +export const CURSOR_LEFT = "\r" +export const CURSOR_HIDE = "\x1b[?25l" +export const CURSOR_SHOW = "\x1b[?25h" +export const CURSOR_TO_0 = "\x1b[G" +export const BEEP = "\x07" diff --git a/packages/language-service/src/cli/check.ts b/packages/language-service/src/cli/check.ts index 2ab72d28..04167c13 100644 --- a/packages/language-service/src/cli/check.ts +++ b/packages/language-service/src/cli/check.ts @@ -1,16 +1,15 @@ -import * as Command from "@effect/cli/Command" -import * as Options from "@effect/cli/Options" -import * as FileSystem from "@effect/platform/FileSystem" import { pipe } from "effect" import * as Array from "effect/Array" import * as Effect from "effect/Effect" +import * as FileSystem from "effect/FileSystem" +import { Command, Flag } from "effect/unstable/cli" import { extractAppliedEffectLspPatches, getModuleFilePath, getPackageJsonData, TypeScriptContext } from "./utils" const LOCAL_TYPESCRIPT_DIR = "./node_modules/typescript" -const dirPath = Options.directory("dir").pipe( - Options.withDefault(LOCAL_TYPESCRIPT_DIR), - Options.withDescription("The directory of the typescript package to patch.") +const dirPath = Flag.directory("dir").pipe( + Flag.withDefault(LOCAL_TYPESCRIPT_DIR), + Flag.withDescription("The directory of the typescript package to patch.") ) export const check = Command.make( diff --git a/packages/language-service/src/cli/codegen.ts b/packages/language-service/src/cli/codegen.ts index 17a4638a..08575488 100644 --- a/packages/language-service/src/cli/codegen.ts +++ b/packages/language-service/src/cli/codegen.ts @@ -1,14 +1,13 @@ -import * as Command from "@effect/cli/Command" -import * as Options from "@effect/cli/Options" -import * as FileSystem from "@effect/platform/FileSystem" -import * as Path from "@effect/platform/Path" import { createProjectService } from "@typescript-eslint/project-service" import * as Array from "effect/Array" import * as Data from "effect/Data" import * as Effect from "effect/Effect" -import * as Either from "effect/Either" +import * as FileSystem from "effect/FileSystem" import { pipe } from "effect/Function" import * as Option from "effect/Option" +import * as Path from "effect/Path" +import * as Result from "effect/Result" +import { Command, Flag } from "effect/unstable/cli" import type * as ts from "typescript" import { codegens as codegensDefinitions } from "../codegens" import * as LanguageServicePluginOptions from "../core/LanguageServicePluginOptions" @@ -27,24 +26,24 @@ export class NoFilesToCodegenError extends Data.TaggedError("NoFilesToCodegenErr } } -const file = Options.file("file").pipe( - Options.optional, - Options.withDescription("The full path of the file to codegen.") +const file = Flag.file("file").pipe( + Flag.optional, + Flag.withDescription("The full path of the file to codegen.") ) -const project = Options.file("project").pipe( - Options.optional, - Options.withDescription("The full path of the project tsconfig.json file to codegen.") +const project = Flag.file("project").pipe( + Flag.optional, + Flag.withDescription("The full path of the project tsconfig.json file to codegen.") ) -const verbose = Options.boolean("verbose").pipe( - Options.withDefault(false), - Options.withDescription("Verbose output.") +const verbose = Flag.boolean("verbose").pipe( + Flag.withDefault(false), + Flag.withDescription("Verbose output.") ) -const force = Options.boolean("force").pipe( - Options.withDefault(false), - Options.withDescription("Force codegen even if no changes are needed.") +const force = Flag.boolean("force").pipe( + Flag.withDefault(false), + Flag.withDescription("Force codegen even if no changes are needed.") ) const BATCH_SIZE = 50 @@ -157,13 +156,15 @@ export const codegen = Command.make( { ...LanguageServicePluginOptions.parse(pluginConfig), diagnosticsName: false } ), Nano.run, - Either.getOrElse(() => [] as Array) + Result.getOrElse(() => [] as Array) ) checkedFilesCount++ // only changes to this file const thisFileChanges = allFileChanges.filter((change) => change.fileName === sourceFile.fileName) - const flattenedChanges = Array.flatten(thisFileChanges.map((change) => change.textChanges)) + const flattenedChanges = Array.flatten( + thisFileChanges.map((change) => change.textChanges) + ) if (verbose) { if (flattenedChanges.length > 0) { @@ -183,7 +184,7 @@ export const codegen = Command.make( service.closeClientFile(filePath) } } - yield* Effect.yieldNow() + yield* Effect.yieldNow } disposeIfLanguageServiceChanged(undefined) diff --git a/packages/language-service/src/cli/diagnostics.ts b/packages/language-service/src/cli/diagnostics.ts index fa6df497..fc3529f9 100644 --- a/packages/language-service/src/cli/diagnostics.ts +++ b/packages/language-service/src/cli/diagnostics.ts @@ -1,14 +1,13 @@ -import * as Command from "@effect/cli/Command" -import * as Options from "@effect/cli/Options" -import * as Path from "@effect/platform/Path" import { createProjectService } from "@typescript-eslint/project-service" import * as Array from "effect/Array" import * as Console from "effect/Console" import * as Data from "effect/Data" import * as Effect from "effect/Effect" -import * as Either from "effect/Either" import { identity, pipe } from "effect/Function" import * as Option from "effect/Option" +import * as Path from "effect/Path" +import * as Result from "effect/Result" +import { Command, Flag } from "effect/unstable/cli" import type * as ts from "typescript" import * as LanguageServicePluginOptions from "../core/LanguageServicePluginOptions" import * as LSP from "../core/LSP" @@ -165,7 +164,7 @@ const diagnosticPrettyFormatter = Effect.gen(function*() { (text, def) => text.replace(new RegExp(`TS${def.code}:`, "g"), `effect(${def.name}):`), rawFormatted ) - }).pipe(Effect.flatMap(Console.log), Effect.when(() => diagnostics.length > 0)), + }).pipe(Effect.flatMap(Console.log), Effect.when(Effect.sync(() => diagnostics.length > 0))), onEnd: (state) => Console.log( `Checked ${state.checkedCount} files out of ${state.totalFilesCount} files. \n${state.errorsCount} errors, ${state.warningsCount} warnings and ${state.messagesCount} messages.` @@ -188,7 +187,7 @@ const diagnosticTextFormatter = Effect.gen(function*() { (text, def) => text.replace(new RegExp(`TS${def.code}:`, "g"), `effect(${def.name}):`), rawFormatted ) - }).pipe(Effect.flatMap(Console.log), Effect.when(() => diagnostics.length > 0)), + }).pipe(Effect.flatMap(Console.log), Effect.when(Effect.sync(() => diagnostics.length > 0))), onEnd: (state) => Console.log( `Checked ${state.checkedCount} files out of ${state.totalFilesCount} files. \n${state.errorsCount} errors, ${state.warningsCount} warnings and ${state.messagesCount} messages.` @@ -278,33 +277,33 @@ const BATCH_SIZE = 50 export const diagnostics = Command.make( "diagnostics", { - file: Options.file("file").pipe( - Options.optional, - Options.withDescription("The full path of the file to check for diagnostics.") + file: Flag.file("file").pipe( + Flag.optional, + Flag.withDescription("The full path of the file to check for diagnostics.") ), - project: Options.file("project").pipe( - Options.optional, - Options.withDescription("The full path of the project tsconfig.json file to check for diagnostics.") + project: Flag.file("project").pipe( + Flag.optional, + Flag.withDescription("The full path of the project tsconfig.json file to check for diagnostics.") ), - format: Options.choice("format", ["json", "pretty", "text", "github-actions"] as ReadonlyArray) + format: Flag.choice("format", ["json", "pretty", "text", "github-actions"] as ReadonlyArray) .pipe( - Options.withDefault("pretty" as const), - Options.withDescription( + Flag.withDefault("pretty" as const), + Flag.withDescription( "Output format: json (machine-readable), pretty (colored with context), text (plain text), github-actions (workflow commands)" ) ), - strict: Options.boolean("strict").pipe( - Options.withDefault(false), - Options.withDescription("Treat warnings as errors (affects exit code)") + strict: Flag.boolean("strict").pipe( + Flag.withDefault(false), + Flag.withDescription("Treat warnings as errors (affects exit code)") ), - severity: Options.text("severity").pipe( - Options.optional, - Options.withDescription("Filter by severity levels (comma-separated: error,warning,message)") + severity: Flag.string("severity").pipe( + Flag.optional, + Flag.withDescription("Filter by severity levels (comma-separated: error,warning,message)") ), - progress: Options.boolean("progress").pipe( - Options.withDefault(false), - Options.withDescription("Show progress as files are checked (outputs to stderr)") + progress: Flag.boolean("progress").pipe( + Flag.withDefault(false), + Flag.withDescription("Show progress as files are checked (outputs to stderr)") ) }, Effect.fn("diagnostics")(function*({ file, format, progress, project, severity, strict }) { @@ -400,15 +399,15 @@ export const diagnostics = Command.make( { ...LanguageServicePluginOptions.parse(pluginConfig), diagnosticsName: false } ), Nano.run, - Either.map((_) => _.diagnostics), - Either.map( + Result.map((_) => _.diagnostics), + Result.map( Array.map((_) => _.category === state.tsInstance.DiagnosticCategory.Suggestion ? { ..._, category: state.tsInstance.DiagnosticCategory.Message } : _ ) ), - Either.getOrElse(() => []) + Result.getOrElse(() => []) ) // Apply severity filter if specified @@ -430,7 +429,7 @@ export const diagnostics = Command.make( service.closeClientFile(filePath) } } - yield* Effect.yieldNow() + yield* Effect.yieldNow } disposeIfLanguageServiceChanged(undefined) diff --git a/packages/language-service/src/cli/layerinfo.ts b/packages/language-service/src/cli/layerinfo.ts index bcdfd4dc..cfad6fdc 100644 --- a/packages/language-service/src/cli/layerinfo.ts +++ b/packages/language-service/src/cli/layerinfo.ts @@ -1,14 +1,11 @@ -import * as Command from "@effect/cli/Command" -import * as Options from "@effect/cli/Options" -import * as Path from "@effect/platform/Path" -import * as Ansi from "@effect/printer-ansi/Ansi" -import * as Doc from "@effect/printer-ansi/AnsiDoc" import { createProjectService } from "@typescript-eslint/project-service" import * as Console from "effect/Console" import * as Effect from "effect/Effect" -import * as Either from "effect/Either" import { pipe } from "effect/Function" import * as Option from "effect/Option" +import * as Path from "effect/Path" +import * as Result from "effect/Result" +import { Command, Flag } from "effect/unstable/cli" import type * as ts from "typescript" import * as LayerGraph from "../core/LayerGraph" @@ -18,6 +15,7 @@ import * as TypeCheckerUtils from "../core/TypeCheckerUtils" import * as TypeParser from "../core/TypeParser" import * as TypeScriptApi from "../core/TypeScriptApi" import * as TypeScriptUtils from "../core/TypeScriptUtils" +import { ansi, BOLD, DIM } from "./ansi" import { collectExportedItems, type LayerInfo } from "./overview" import { TypeScriptContext } from "./utils" @@ -88,29 +86,24 @@ const toRelativePath = (absolutePath: string, cwd: string): string => { } /** - * Renders a dim text line + * Renders the layer info result as a styled string */ -const dimLine = (text: string): Doc.AnsiDoc => Doc.annotate(Doc.text(text), Ansi.blackBright) - -/** - * Renders the layer info result as a styled document - */ -export const renderLayerInfo = (result: LayerInfoResult, cwd: string): Doc.AnsiDoc => { +export const renderLayerInfo = (result: LayerInfoResult, cwd: string): string => { const { layer, providersAndRequirers } = result - const lines: Array = [] + const lines: Array = [] // Header with layer name - lines.push(Doc.empty) - lines.push(Doc.annotate(Doc.text(layer.name), Ansi.bold)) + lines.push("") + lines.push(ansi(layer.name, BOLD)) // Location and type indented under the name const relativePath = toRelativePath(layer.filePath, cwd) - lines.push(Doc.indent(dimLine(`${relativePath}:${layer.line}:${layer.column}`), 2)) - lines.push(Doc.indent(dimLine(layer.layerType), 2)) + lines.push(` ${ansi(`${relativePath}:${layer.line}:${layer.column}`, DIM)}`) + lines.push(` ${ansi(layer.layerType, DIM)}`) // Description if present if (layer.description) { - lines.push(Doc.indent(dimLine(layer.description), 2)) + lines.push(` ${ansi(layer.description, DIM)}`) } // Providers and Requirers @@ -118,75 +111,62 @@ export const renderLayerInfo = (result: LayerInfoResult, cwd: string): Doc.AnsiD const requiredItems = providersAndRequirers.filter((_) => _.kind === "required") if (providedItems.length > 0) { - lines.push(Doc.empty) - lines.push(Doc.annotate(Doc.text(`Provides (${providedItems.length}):`), Ansi.bold)) + lines.push("") + lines.push(ansi(`Provides (${providedItems.length}):`, BOLD)) for (const item of providedItems) { - lines.push(Doc.indent(dimLine(`- ${item.typeString}`), 2)) + lines.push(` ${ansi(`- ${item.typeString}`, DIM)}`) } } if (requiredItems.length > 0) { - lines.push(Doc.empty) - lines.push(Doc.annotate(Doc.text(`Requires (${requiredItems.length}):`), Ansi.bold)) + lines.push("") + lines.push(ansi(`Requires (${requiredItems.length}):`, BOLD)) for (const item of requiredItems) { - lines.push(Doc.indent(dimLine(`- ${item.typeString}`), 2)) + lines.push(` ${ansi(`- ${item.typeString}`, DIM)}`) } } if (providedItems.length === 0 && requiredItems.length === 0) { - lines.push(Doc.empty) - lines.push(dimLine("No providers or requirements detected.")) + lines.push("") + lines.push(ansi("No providers or requirements detected.", DIM)) } // Suggested Composition section (includes tip, output types list, and composition) - lines.push(Doc.empty) - lines.push(Doc.annotate(Doc.text("Suggested Composition:"), Ansi.bold)) + lines.push("") + lines.push(ansi("Suggested Composition:", BOLD)) // Tip about using Layer.mergeAll and --outputs lines.push( - Doc.indent( - dimLine("Not sure you got your composition right? Just write all layers inside a Layer.mergeAll(...)"), - 2 - ) + ` ${ansi("Not sure you got your composition right? Just write all layers inside a Layer.mergeAll(...)", DIM)}` ) lines.push( - Doc.indent( - dimLine( - "then run this command again and use --outputs to select which outputs to include in composition." - ), - 2 - ) - ) - lines.push( - Doc.indent( - dimLine("Example: --outputs 1,2,3"), - 2 - ) + ` ${ansi("then run this command again and use --outputs to select which outputs to include in composition.", DIM)}` ) + lines.push(` ${ansi("Example: --outputs 1,2,3", DIM)}`) // Output types list with checkboxes if (result.outputTypes.length > 0) { - lines.push(Doc.indent(dimLine(""), 2)) + lines.push("") for (const output of result.outputTypes) { const marker = output.included ? "[x]" : "[ ]" - lines.push(Doc.indent(dimLine(`${marker} ${output.index}. ${output.typeString}`), 2)) + lines.push(` ${ansi(`${marker} ${output.index}. ${output.typeString}`, DIM)}`) } } // Actual composition code if (result.suggestedComposition && result.suggestedComposition.length > 1) { - lines.push(Doc.indent(dimLine(""), 2)) + lines.push("") const [first, ...rest] = result.suggestedComposition - lines.push(Doc.indent(dimLine(`export const ${layer.name} = ${first!.layerName}.pipe(`), 2)) + lines.push(` ${ansi(`export const ${layer.name} = ${first!.layerName}.pipe(`, DIM)}`) for (let i = 0; i < rest.length; i++) { const step = rest[i]! const suffix = i === rest.length - 1 ? "" : "," - lines.push(Doc.indent(dimLine(`Layer.${step.operation}(${step.layerName})${suffix}`), 4)) + lines.push(` ${ansi(`Layer.${step.operation}(${step.layerName})${suffix}`, DIM)}`) } - lines.push(Doc.indent(dimLine(")"), 2)) + lines.push(` ${ansi(")", DIM)}`) } - return Doc.vsep(lines) + return lines.join("\n") } /** @@ -442,17 +422,17 @@ const parseOutputIndices = (outputs: Option.Option): ReadonlyArray { return absolutePath } -/** - * Renders a dim text line - */ -const dimLine = (text: string): Doc.AnsiDoc => Doc.annotate(Doc.text(text), Ansi.blackBright) - /** * Renders a service item */ -const renderService = (svc: ServiceInfo, cwd: string): Doc.AnsiDoc => { +const renderService = (svc: ServiceInfo, cwd: string): string => { const relativePath = toRelativePath(svc.filePath, cwd) - const details: Array = [ - dimLine(`${relativePath}:${svc.line}:${svc.column}`), - dimLine(svc.serviceType) + const details = [ + ansi(`${relativePath}:${svc.line}:${svc.column}`, DIM), + ansi(svc.serviceType, DIM) ] if (svc.description) { - details.push(dimLine(svc.description)) + details.push(ansi(svc.description, DIM)) } - - return Doc.vsep([ - Doc.text(svc.name), - Doc.indent(Doc.vsep(details), 2), - Doc.empty - ]) + return [ + ` ${svc.name}`, + ...details.map((d) => ` ${d}`), + "" + ].join("\n") } /** * Renders a layer item */ -const renderLayer = (layer: LayerInfo, cwd: string): Doc.AnsiDoc => { +const renderLayer = (layer: LayerInfo, cwd: string): string => { const relativePath = toRelativePath(layer.filePath, cwd) - const details: Array = [ - dimLine(`${relativePath}:${layer.line}:${layer.column}`), - dimLine(layer.layerType) + const details = [ + ansi(`${relativePath}:${layer.line}:${layer.column}`, DIM), + ansi(layer.layerType, DIM) ] if (layer.description) { - details.push(dimLine(layer.description)) + details.push(ansi(layer.description, DIM)) } - - return Doc.vsep([ - Doc.text(layer.name), - Doc.indent(Doc.vsep(details), 2), - Doc.empty - ]) + return [ + ` ${layer.name}`, + ...details.map((d) => ` ${d}`), + "" + ].join("\n") } /** * Renders an error item */ -const renderError = (error: ErrorInfo, cwd: string): Doc.AnsiDoc => { +const renderError = (error: ErrorInfo, cwd: string): string => { const relativePath = toRelativePath(error.filePath, cwd) - const details: Array = [ - dimLine(`${relativePath}:${error.line}:${error.column}`), - dimLine(error.errorType) + const details = [ + ansi(`${relativePath}:${error.line}:${error.column}`, DIM), + ansi(error.errorType, DIM) ] if (error.description) { - details.push(dimLine(error.description)) + details.push(ansi(error.description, DIM)) } - - return Doc.vsep([ - Doc.text(error.name), - Doc.indent(Doc.vsep(details), 2), - Doc.empty - ]) + return [ + ` ${error.name}`, + ...details.map((d) => ` ${d}`), + "" + ].join("\n") } /** - * Renders the overview result as a styled document + * Renders the overview result as a styled string */ -export const renderOverview = (result: OverviewResult, cwd: string): Doc.AnsiDoc => { - const lines: Array = [] +export const renderOverview = (result: OverviewResult, cwd: string): string => { + const lines: Array = [] // Errors section if (result.errors.length > 0) { - lines.push(Doc.empty) - lines.push(Doc.annotate(Doc.text(`Yieldable Errors (${result.errors.length})`), Ansi.bold)) + lines.push("") + lines.push(ansi(`Yieldable Errors (${result.errors.length})`, BOLD)) const sortedErrors = Array.sort(result.errors, itemOrder) - const errorDocs = sortedErrors.map((error) => renderError(error, cwd)) - lines.push(Doc.indent(Doc.vsep(errorDocs), 2)) + for (const error of sortedErrors) { + lines.push(renderError(error, cwd)) + } } // Services section if (result.services.length > 0) { - lines.push(Doc.empty) - lines.push(Doc.annotate(Doc.text(`Services (${result.services.length})`), Ansi.bold)) + lines.push("") + lines.push(ansi(`Services (${result.services.length})`, BOLD)) const sortedServices = Array.sort(result.services, itemOrder) - const serviceDocs = sortedServices.map((svc) => renderService(svc, cwd)) - lines.push(Doc.indent(Doc.vsep(serviceDocs), 2)) + for (const svc of sortedServices) { + lines.push(renderService(svc, cwd)) + } } // Layers section if (result.layers.length > 0) { - lines.push(Doc.empty) - lines.push(Doc.annotate(Doc.text(`Layers (${result.layers.length})`), Ansi.bold)) + lines.push("") + lines.push(ansi(`Layers (${result.layers.length})`, BOLD)) const sortedLayers = Array.sort(result.layers, itemOrder) - const layerDocs = sortedLayers.map((layer) => renderLayer(layer, cwd)) - lines.push(Doc.indent(Doc.vsep(layerDocs), 2)) + for (const layer of sortedLayers) { + lines.push(renderLayer(layer, cwd)) + } } if (result.services.length === 0 && result.layers.length === 0 && result.errors.length === 0) { - lines.push(Doc.empty) - lines.push(Doc.text("No exported services, layers, or errors found.")) + lines.push("") + lines.push("No exported services, layers, or errors found.") } // Hint for getting automatic composition if (result.layers.length > 0) { - lines.push(Doc.empty) + lines.push("") lines.push( - dimLine( - "Tip: Not sure you got your composition right? Just write all layers inside a Layer.mergeAll(...) command, and then run the layerinfo command to get the suggested composition order to use." + ansi( + "Tip: Not sure you got your composition right? Just write all layers inside a Layer.mergeAll(...) command, and then run the layerinfo command to get the suggested composition order to use.", + DIM ) ) } - return Doc.vsep(lines) + return lines.join("\n") } /** @@ -397,7 +391,7 @@ const collectAllItems = ( Nano.provideService(TypeScriptApi.TypeScriptProgram, program), Nano.provideService(TypeScriptApi.TypeScriptApi, tsInstance), Nano.run, - Either.getOrElse(() => ({ services: [], layers: [], errors: [] })) + Result.getOrElse(() => ({ services: [], layers: [], errors: [] })) ) for (const svc of result.services) { @@ -413,7 +407,7 @@ const collectAllItems = ( service.closeClientFile(filePath) } } - yield* Effect.yieldNow() + yield* Effect.yieldNow } return { services, layers, errors } @@ -422,17 +416,17 @@ const collectAllItems = ( export const overview = Command.make( "overview", { - file: Options.file("file").pipe( - Options.optional, - Options.withDescription("The full path of the file to analyze.") + file: Flag.file("file").pipe( + Flag.optional, + Flag.withDescription("The full path of the file to analyze.") ), - project: Options.file("project").pipe( - Options.optional, - Options.withDescription("The full path of the project tsconfig.json file to analyze.") + project: Flag.file("project").pipe( + Flag.optional, + Flag.withDescription("The full path of the project tsconfig.json file to analyze.") ), - maxSymbolDepth: Options.integer("max-symbol-depth").pipe( - Options.withDefault(3), - Options.withDescription( + maxSymbolDepth: Flag.integer("max-symbol-depth").pipe( + Flag.withDefault(3), + Flag.withDescription( "Maximum depth to traverse nested symbol properties. 0 = only root exports, 1 = root + one level, etc." ) ) @@ -469,8 +463,8 @@ export const overview = Command.make( } ) - const doc = renderOverview({ services, layers, errors, totalFilesCount: filesToCheck.size }, cwd) - yield* Console.log(Doc.render(doc, { style: "pretty" })) + const output = renderOverview({ services, layers, errors, totalFilesCount: filesToCheck.size }, cwd) + yield* Console.log(output) }) ).pipe( Command.withDescription("Provides an overview of Effect-related exports in the given files or project.") diff --git a/packages/language-service/src/cli/patch.ts b/packages/language-service/src/cli/patch.ts index f219e8fd..f7d69164 100644 --- a/packages/language-service/src/cli/patch.ts +++ b/packages/language-service/src/cli/patch.ts @@ -1,9 +1,8 @@ -import * as Command from "@effect/cli/Command" -import * as Options from "@effect/cli/Options" -import * as FileSystem from "@effect/platform/FileSystem" import * as Data from "effect/Data" import * as Effect from "effect/Effect" +import * as FileSystem from "effect/FileSystem" import * as Option from "effect/Option" +import { Command, Flag } from "effect/unstable/cli" import type * as ts from "typescript" import { applyTextChanges, @@ -27,22 +26,22 @@ export class UnableToFindPositionToPatchError extends Data.TaggedError("UnableTo const LOCAL_TYPESCRIPT_DIR = "./node_modules/typescript" -const dirPath = Options.directory("dir").pipe( - Options.withDefault(LOCAL_TYPESCRIPT_DIR), - Options.withDescription("The directory of the typescript package to patch.") +const dirPath = Flag.directory("dir").pipe( + Flag.withDefault(LOCAL_TYPESCRIPT_DIR), + Flag.withDescription("The directory of the typescript package to patch.") ) -const moduleNames = Options.choice("module", [ +const moduleNames = Flag.choice("module", [ "tsc", "typescript" ]).pipe( - Options.repeated, - Options.withDescription("The name of the module to patch.") + Flag.atLeast(0), + Flag.withDescription("The name of the module to patch.") ) -const force = Options.boolean("force").pipe( - Options.withDefault(false), - Options.withDescription("Force patch even if already patched.") +const force = Flag.boolean("force").pipe( + Flag.withDefault(false), + Flag.withDescription("Force patch even if already patched.") ) const getPatchedMarker = (version: string) => { @@ -195,9 +194,7 @@ const getPatchesForModule = Effect.fn("getPatchesForModule")( // insert the clearSourceFileMetadata call if (Option.isNone(insertClearSourceFileEffectMetadataPosition)) { - return yield* Effect.fail( - new UnableToFindPositionToPatchError({ positionToFind: "clearSourceFileEffectMetadata" }) - ) + return yield* new UnableToFindPositionToPatchError({ positionToFind: "clearSourceFileEffectMetadata" }) } patches.push( yield* makeEffectLspPatchChange( @@ -212,7 +209,7 @@ const getPatchesForModule = Effect.fn("getPatchesForModule")( // insert the checkSourceFile call if (Option.isNone(insertCheckSourceFilePosition)) { - return yield* Effect.fail(new UnableToFindPositionToPatchError({ positionToFind: "checkSourceFileWorker" })) + return yield* new UnableToFindPositionToPatchError({ positionToFind: "checkSourceFileWorker" }) } patches.push( yield* makeEffectLspPatchChange( @@ -229,9 +226,7 @@ const getPatchesForModule = Effect.fn("getPatchesForModule")( // insert the appendMetadataRelationError call if (Option.isNone(insertAppendMetadataRelationErrorPosition)) { - return yield* Effect.fail( - new UnableToFindPositionToPatchError({ positionToFind: "appendMetadataRelationError" }) - ) + return yield* new UnableToFindPositionToPatchError({ positionToFind: "appendMetadataRelationError" }) } const { sourceIdentifier, targetIdentifier } = insertAppendMetadataRelationErrorPosition.value patches.push( diff --git a/packages/language-service/src/cli/quickfixes.ts b/packages/language-service/src/cli/quickfixes.ts index 044b0eeb..fcd3c84f 100644 --- a/packages/language-service/src/cli/quickfixes.ts +++ b/packages/language-service/src/cli/quickfixes.ts @@ -1,18 +1,13 @@ -import * as Command from "@effect/cli/Command" -import * as HelpDoc from "@effect/cli/HelpDoc" -import * as Options from "@effect/cli/Options" -import * as ValidationError from "@effect/cli/ValidationError" -import * as Path from "@effect/platform/Path" -import * as Ansi from "@effect/printer-ansi/Ansi" -import * as Doc from "@effect/printer-ansi/AnsiDoc" import { createProjectService } from "@typescript-eslint/project-service" import * as Arr from "effect/Array" import * as Console from "effect/Console" import * as Data from "effect/Data" import * as Effect from "effect/Effect" -import * as Either from "effect/Either" import { pipe } from "effect/Function" import * as Option from "effect/Option" +import * as Path from "effect/Path" +import * as Result from "effect/Result" +import { CliError, Command, Flag } from "effect/unstable/cli" import type * as ts from "typescript" import * as LanguageServicePluginOptions from "../core/LanguageServicePluginOptions" import * as LSP from "../core/LSP" @@ -23,6 +18,7 @@ import * as TypeParser from "../core/TypeParser" import * as TypeScriptApi from "../core/TypeScriptApi" import * as TypeScriptUtils from "../core/TypeScriptUtils" import { diagnostics as diagnosticsDefinitions } from "../diagnostics" +import { ansi, BOLD, CYAN, DIM, YELLOW } from "./ansi" import { NoFilesToCheckError } from "./diagnostics" import { renderTextChange } from "./setup/diff-renderer" import { extractEffectLspOptions, getFileNamesInTsConfig, TypeScriptContext } from "./utils" @@ -64,24 +60,13 @@ const isSkipFix = (fixName: string): boolean => fixName.endsWith("_skipNextLine" const renderQuickFix = ( sourceFile: ts.SourceFile, fix: { fixName: string; description: string; changes: ReadonlyArray } -): Doc.AnsiDoc => { - const lines: Array = [] +): string => { + const lines: Array = [] // Fix header - lines.push(Doc.empty) - lines.push( - Doc.cat( - Doc.cat( - Doc.cat( - Doc.annotate(Doc.text(" Fix: "), Ansi.bold), - Doc.annotate(Doc.text(fix.fixName), Ansi.cyan) - ), - Doc.text(" - ") - ), - Doc.text(fix.description) - ) - ) - lines.push(Doc.annotate(Doc.text(" " + "─".repeat(60)), Ansi.blackBright)) + lines.push("") + lines.push(` ${ansi("Fix: ", BOLD)}${ansi(fix.fixName, CYAN)} - ${fix.description}`) + lines.push(ansi(" " + "\u2500".repeat(60), DIM)) // Render the diff for each file change for (const fileChange of fix.changes) { @@ -89,13 +74,13 @@ const renderQuickFix = ( for (const textChange of fileChange.textChanges) { const diffLines = renderTextChange(sourceFile, textChange) for (const diffLine of diffLines) { - lines.push(Doc.cat(Doc.text(" "), diffLine)) + lines.push(` ${diffLine}`) } } } } - return Doc.vsep(lines) + return lines.join("\n") } /** @@ -105,8 +90,8 @@ const renderDiagnosticWithFixes = ( sourceFile: ts.SourceFile, info: QuickFixInfo, tsInstance: typeof ts -): Doc.AnsiDoc => { - const lines: Array = [] +): string => { + const lines: Array = [] // Get line and column for the diagnostic const { character, line } = tsInstance.getLineAndCharacterOfPosition(sourceFile, info.diagnostic.start) @@ -114,19 +99,7 @@ const renderDiagnosticWithFixes = ( // Diagnostic header: file:line:col effect(ruleName): message const locationStr = `${sourceFile.fileName}:${line + 1}:${character + 1}` lines.push( - Doc.cat( - Doc.cat( - Doc.cat( - Doc.cat( - Doc.annotate(Doc.text(locationStr), Ansi.cyan), - Doc.text(" ") - ), - Doc.annotate(Doc.text(`effect(${info.diagnostic.ruleName})`), Ansi.yellow) - ), - Doc.text(": ") - ), - Doc.text(info.diagnostic.messageText) - ) + `${ansi(locationStr, CYAN)} ${ansi(`effect(${info.diagnostic.ruleName})`, YELLOW)}: ${info.diagnostic.messageText}` ) // Render each fix @@ -134,9 +107,9 @@ const renderDiagnosticWithFixes = ( lines.push(renderQuickFix(sourceFile, fix)) } - lines.push(Doc.empty) + lines.push("") - return Doc.vsep(lines) + return lines.join("\n") } const BATCH_SIZE = 50 @@ -144,17 +117,17 @@ const BATCH_SIZE = 50 export const quickfixes = Command.make( "quickfixes", { - file: Options.file("file").pipe( - Options.optional, - Options.withDescription("The full path of the file to check for quick fixes.") + file: Flag.file("file").pipe( + Flag.optional, + Flag.withDescription("The full path of the file to check for quick fixes.") ), - project: Options.file("project").pipe( - Options.optional, - Options.withDescription("The full path of the project tsconfig.json file to check for quick fixes.") + project: Flag.file("project").pipe( + Flag.optional, + Flag.withDescription("The full path of the project tsconfig.json file to check for quick fixes.") ), - code: Options.text("code").pipe( - Options.withDescription("Filter by diagnostic name or code (e.g., 'floatingEffect' or '5')."), - Options.mapEffect((value) => { + code: Flag.string("code").pipe( + Flag.withDescription("Filter by diagnostic name or code (e.g., 'floatingEffect' or '5')."), + Flag.mapEffect((value) => { // Validate that the code is a known diagnostic name or code if (validDiagnosticNames.has(value)) { // It's a diagnostic name, return the corresponding code @@ -167,24 +140,24 @@ export const quickfixes = Command.make( // Invalid code const validValues = [...validDiagnosticNames].sort().join(", ") return Effect.fail( - ValidationError.invalidValue( - HelpDoc.p(`Invalid diagnostic code '${value}'. Valid values: ${validValues}`) - ) + new CliError.UserError({ + cause: new Error(`Invalid diagnostic code '${value}'. Valid values: ${validValues}`) + }) ) }), - Options.optional + Flag.optional ), - line: Options.integer("line").pipe( - Options.withDescription("Filter by line number (1-based)."), - Options.optional + line: Flag.integer("line").pipe( + Flag.withDescription("Filter by line number (1-based)."), + Flag.optional ), - column: Options.integer("column").pipe( - Options.withDescription("Filter by column number (1-based). Requires --line to be specified."), - Options.optional + column: Flag.integer("column").pipe( + Flag.withDescription("Filter by column number (1-based). Requires --line to be specified."), + Flag.optional ), - fix: Options.text("fix").pipe( - Options.withDescription("Filter by fix name (e.g., 'floatingEffect_yieldStar')."), - Options.optional + fix: Flag.string("fix").pipe( + Flag.withDescription("Filter by fix name (e.g., 'floatingEffect_yieldStar')."), + Flag.optional ) }, Effect.fn("quickfixes")(function*({ code, column, file, fix, line, project }) { @@ -245,7 +218,9 @@ export const quickfixes = Command.make( { ...LanguageServicePluginOptions.parse(pluginConfig), diagnosticsName: false } ), Nano.run, - Either.getOrElse(() => ({ diagnostics: [], codeFixes: [] })) + Result.getOrElse( + () => ({ diagnostics: [], codeFixes: [] } as { diagnostics: Array; codeFixes: Array }) + ) ) // Group fixes by diagnostic position and code @@ -309,7 +284,7 @@ export const quickfixes = Command.make( }, (changeTracker) => pipe( - codeFix.apply, + codeFix.apply as Nano.Nano, Nano.provideService(TypeScriptApi.ChangeTracker, changeTracker), Nano.run ) @@ -336,24 +311,20 @@ export const quickfixes = Command.make( // Render output for this file for (const info of diagnosticsWithFixes) { - const doc = renderDiagnosticWithFixes(sourceFile, info, tsInstance) - yield* Console.log(doc.pipe(Doc.render({ style: "pretty" }))) + yield* Console.log(renderDiagnosticWithFixes(sourceFile, info, tsInstance)) } } finally { service.closeClientFile(filePath) } } - yield* Effect.yieldNow() + yield* Effect.yieldNow } if (totalDiagnosticsWithFixes === 0) { yield* Console.log("No quick fixes available.") } else { yield* Console.log( - Doc.annotate( - Doc.text(`Found ${totalDiagnosticsWithFixes} diagnostic(s) with quick fixes.`), - Ansi.bold - ).pipe(Doc.render({ style: "pretty" })) + ansi(`Found ${totalDiagnosticsWithFixes} diagnostic(s) with quick fixes.`, BOLD) ) } }) diff --git a/packages/language-service/src/cli/setup.ts b/packages/language-service/src/cli/setup.ts index a99bb767..1f09115c 100644 --- a/packages/language-service/src/cli/setup.ts +++ b/packages/language-service/src/cli/setup.ts @@ -1,11 +1,11 @@ -import * as Command from "@effect/cli/Command" -import * as Prompt from "@effect/cli/Prompt" -import type * as PlatformError from "@effect/platform/Error" -import * as FileSystem from "@effect/platform/FileSystem" -import * as Path from "@effect/platform/Path" import * as Console from "effect/Console" import * as Effect from "effect/Effect" +import * as FileSystem from "effect/FileSystem" import * as Option from "effect/Option" +import * as Path from "effect/Path" +import type * as PlatformError from "effect/PlatformError" +import { Command } from "effect/unstable/cli" +import * as Prompt from "effect/unstable/cli/Prompt" import packageJson from "../../package.json" import { assess, type Assessment } from "./setup/assessment" import { computeChanges } from "./setup/changes" @@ -73,7 +73,8 @@ const createAssessmentInput = ( if (agentsMdExists) { // Check if it's a symlink - skip if it is const agentsMdStat = yield* fs.stat(agentsMdPath).pipe(Effect.option) - const isAgentsMdSymlink = Option.isSome(agentsMdStat) && agentsMdStat.value.type === "SymbolicLink" + const isAgentsMdSymlink = Option.isSome(agentsMdStat) && + agentsMdStat.value.type === "SymbolicLink" if (!isAgentsMdSymlink) { const agentsMdText = yield* fs.readFileString(agentsMdPath).pipe( @@ -98,7 +99,8 @@ const createAssessmentInput = ( if (claudeMdExists) { // Check if it's a symlink - skip if it is const claudeMdStat = yield* fs.stat(claudeMdPath).pipe(Effect.option) - const isClaudeMdSymlink = Option.isSome(claudeMdStat) && claudeMdStat.value.type === "SymbolicLink" + const isClaudeMdSymlink = Option.isSome(claudeMdStat) && + claudeMdStat.value.type === "SymbolicLink" if (!isClaudeMdSymlink) { const claudeMdText = yield* fs.readFileString(claudeMdPath).pipe( diff --git a/packages/language-service/src/cli/setup/diagnostic-prompt.ts b/packages/language-service/src/cli/setup/diagnostic-prompt.ts index 0146f736..35680e41 100644 --- a/packages/language-service/src/cli/setup/diagnostic-prompt.ts +++ b/packages/language-service/src/cli/setup/diagnostic-prompt.ts @@ -1,32 +1,59 @@ -import * as Prompt from "@effect/cli/Prompt" -import * as Terminal from "@effect/platform/Terminal" -import * as Ansi from "@effect/printer-ansi/Ansi" -import * as Doc from "@effect/printer-ansi/AnsiDoc" import * as Arr from "effect/Array" import * as Data from "effect/Data" import * as Effect from "effect/Effect" +import * as Terminal from "effect/Terminal" +import * as Prompt from "effect/unstable/cli/Prompt" import type { DiagnosticSeverity } from "../../core/LanguageServicePluginOptions" import type { DiagnosticInfo } from "./diagnostic-info" import { cycleSeverity, getSeverityShortName, MAX_SEVERITY_LENGTH } from "./diagnostic-info" +import { + ansi, + BEEP, + BG_BLACK_BRIGHT, + BG_BLUE, + BG_CYAN, + BG_RED, + BG_YELLOW, + BOLD, + CURSOR_HIDE, + CURSOR_LEFT, + CURSOR_TO_0, + CYAN_BRIGHT, + DIM, + ERASE_LINE, + GREEN, + WHITE +} from "../ansi" + +function eraseLines(count: number): string { + let result = "" + for (let i = 0; i < count; i++) { + if (i > 0) result += "\x1b[1A" // cursor up + result += ERASE_LINE + } + if (count > 0) result += CURSOR_LEFT + return result +} + // ============================================================================ // Copied internals from @effect/cli (not exported) // ============================================================================ -const Action = Data.taggedEnum() +const Action = Data.taggedEnum() const NEWLINE_REGEX = /\r?\n/ -function eraseText(text: string, columns: number): Doc.AnsiDoc { +function eraseText(text: string, columns: number): string { if (columns === 0) { - return Doc.cat(Doc.eraseLine, Doc.cursorTo(0)) + return ERASE_LINE + CURSOR_TO_0 } let rows = 0 const lines = text.split(/\r?\n/) for (const line of lines) { rows += 1 + Math.floor(Math.max(line.length - 1, 0) / columns) } - return Doc.eraseLines(rows) + return eraseLines(rows) } function entriesToDisplay( @@ -44,17 +71,15 @@ function entriesToDisplay( } const defaultFigures = { - arrowUp: Doc.text("↑"), - arrowDown: Doc.text("↓"), - tick: Doc.text("✔"), - pointerSmall: Doc.text("›") + arrowUp: "\u2191", + arrowDown: "\u2193", + tick: "\u2714", + pointerSmall: "\u203A" } type Figures = typeof defaultFigures -const figures: Effect.Effect = Effect.succeed(defaultFigures) - -const renderBeep = Doc.render(Doc.beep, { style: "pretty" }) +const figuresValue: Figures = defaultFigures // ============================================================================ // Diagnostic prompt types and state @@ -75,34 +100,29 @@ interface DiagnosticPromptOptions { // Rendering functions (adapted from multi-select) // ============================================================================ -function getSeverityStyle(severity: DiagnosticSeverity | "off"): Ansi.Ansi { +function getSeverityStyle(severity: DiagnosticSeverity | "off"): string { const styles = { - off: Ansi.combine(Ansi.white, Ansi.bgBlackBright), - suggestion: Ansi.combine(Ansi.white, Ansi.bgCyan), - message: Ansi.combine(Ansi.white, Ansi.bgBlue), - warning: Ansi.combine(Ansi.white, Ansi.bgYellow), - error: Ansi.combine(Ansi.white, Ansi.bgRed) + off: WHITE + BG_BLACK_BRIGHT, + suggestion: WHITE + BG_CYAN, + message: WHITE + BG_BLUE, + warning: WHITE + BG_YELLOW, + error: WHITE + BG_RED } return styles[severity] } function renderOutput( - leadingSymbol: Doc.AnsiDoc, - trailingSymbol: Doc.AnsiDoc, + leadingSymbol: string, + trailingSymbol: string, options: DiagnosticPromptOptions -) { - const annotateLine = (line: string): Doc.AnsiDoc => Doc.annotate(Doc.text(line), Ansi.bold) - const prefix = Doc.cat(leadingSymbol, Doc.space) +): string { + const annotateLine = (line: string): string => ansi(line, BOLD) + const prefix = leadingSymbol + " " return Arr.match(options.message.split(NEWLINE_REGEX), { - onEmpty: () => Doc.hsep([prefix, trailingSymbol]), + onEmpty: () => `${prefix}${trailingSymbol}`, onNonEmpty: (promptLines) => { const lines = Arr.map(promptLines, (line) => annotateLine(line)) - return prefix.pipe( - Doc.cat(Doc.nest(Doc.vsep(lines), 2)), - Doc.cat(Doc.space), - Doc.cat(trailingSymbol), - Doc.cat(Doc.space) - ) + return `${prefix}${lines.join("\n ")} ${trailingSymbol} ` } }) } @@ -110,12 +130,12 @@ function renderOutput( function renderDiagnostics( state: State, options: DiagnosticPromptOptions, - figures: Figures, + figs: Figures, columns: number ) { const diagnostics = options.diagnostics const toDisplay = entriesToDisplay(state.index, diagnostics.length, options.maxPerPage) - const documents: Array = [] + const documents: Array = [] for (let index = toDisplay.startIndex; index < toDisplay.endIndex; index++) { const diagnostic = diagnostics[index] @@ -124,33 +144,25 @@ function renderDiagnostics( const hasChanged = currentSeverity !== diagnostic.defaultSeverity // Arrow prefix for scroll indicators - let prefix: Doc.AnsiDoc = Doc.space + let prefix: string = " " if (index === toDisplay.startIndex && toDisplay.startIndex > 0) { - prefix = figures.arrowUp + prefix = figs.arrowUp } else if (index === toDisplay.endIndex - 1 && toDisplay.endIndex < diagnostics.length) { - prefix = figures.arrowDown + prefix = figs.arrowDown } // Severity badge with fixed width and background color const shortName = getSeverityShortName(currentSeverity) const paddedSeverity = shortName.padEnd(MAX_SEVERITY_LENGTH, " ") - const severityDoc = Doc.annotate( - Doc.text(` ${paddedSeverity} `), - getSeverityStyle(currentSeverity) - ) + const severityStr = ansi(` ${paddedSeverity} `, getSeverityStyle(currentSeverity)) // Diagnostic name with optional * for changed, highlight if selected const nameText = hasChanged ? `${diagnostic.name}*` : diagnostic.name - const nameDoc = isHighlighted - ? Doc.annotate(Doc.text(nameText), Ansi.cyanBright) - : Doc.text(nameText) - - const mainLine = prefix.pipe( - Doc.cat(Doc.space), - Doc.cat(severityDoc), - Doc.cat(Doc.space), - Doc.cat(nameDoc) - ) + const nameStr = isHighlighted + ? ansi(nameText, CYAN_BRIGHT) + : nameText + + const mainLine = `${prefix} ${severityStr} ${nameStr}` // Description - show on separate line below when highlighted if (isHighlighted && diagnostic.description) { @@ -160,76 +172,59 @@ function renderDiagnostics( // Truncate description to fit terminal width const availableWidth = columns - indentWidth const truncatedDescription = availableWidth > 0 && diagnostic.description.length > availableWidth - ? diagnostic.description.substring(0, availableWidth - 1) + "…" + ? diagnostic.description.substring(0, availableWidth - 1) + "\u2026" : diagnostic.description - const descriptionDoc = Doc.annotate( - Doc.text(indent + truncatedDescription), - Ansi.blackBright - ) + const descriptionStr = ansi(indent + truncatedDescription, DIM) - documents.push(Doc.vsep([mainLine, descriptionDoc])) + documents.push(mainLine + "\n" + descriptionStr) } else { documents.push(mainLine) } } - return Doc.vsep(documents) + return documents.join("\n") } function renderNextFrame(state: State, options: DiagnosticPromptOptions) { return Effect.gen(function*() { const terminal = yield* Terminal.Terminal const columns = yield* terminal.columns - const figs = yield* figures + const figs = figuresValue - const diagnosticsDoc = renderDiagnostics(state, options, figs, columns) - const leadingSymbol = Doc.annotate(Doc.text("?"), Ansi.cyanBright) + const diagnosticsStr = renderDiagnostics(state, options, figs, columns) + const leadingSymbol = ansi("?", CYAN_BRIGHT) const trailingSymbol = figs.pointerSmall const promptMsg = renderOutput(leadingSymbol, trailingSymbol, options) - const helpText = Doc.annotate( - Doc.text("Use ↑/↓ to navigate, ←/→ to change severity, Enter to finish"), - Ansi.blackBright + const helpText = ansi( + "Use \u2191/\u2193 to navigate, \u2190/\u2192 to change severity, Enter to finish", + DIM ) - return Doc.cursorHide.pipe( - Doc.cat(promptMsg), - Doc.cat(Doc.hardLine), - Doc.cat(helpText), - Doc.cat(Doc.hardLine), - Doc.cat(diagnosticsDoc), - Doc.render({ style: "pretty", options: { lineWidth: columns } }) - ) + return CURSOR_HIDE + promptMsg + "\n" + helpText + "\n" + diagnosticsStr }) } function renderSubmission(state: State, options: DiagnosticPromptOptions) { return Effect.gen(function*() { - const terminal = yield* Terminal.Terminal - const columns = yield* terminal.columns - const figs = yield* figures + const figs = figuresValue const changedCount = Object.entries(state.severities).filter(([name, severity]) => { const diagnostic = options.diagnostics.find((d) => d.name === name) return diagnostic && severity !== diagnostic.defaultSeverity }).length - const result = Doc.annotate( - Doc.text(`${changedCount} diagnostic${changedCount === 1 ? "" : "s"} configured`), - Ansi.white + const result = ansi( + `${changedCount} diagnostic${changedCount === 1 ? "" : "s"} configured`, + WHITE ) - const leadingSymbol = Doc.annotate(figs.tick, Ansi.green) - const trailingSymbol = Doc.text("") + const leadingSymbol = ansi(figs.tick, GREEN) + const trailingSymbol = "" const promptMsg = renderOutput(leadingSymbol, trailingSymbol, options) - return promptMsg.pipe( - Doc.cat(Doc.space), - Doc.cat(result), - Doc.cat(Doc.hardLine), - Doc.render({ style: "pretty", options: { lineWidth: columns } }) - ) + return promptMsg + " " + result + "\n" }) } @@ -301,7 +296,7 @@ function handleClear(options: DiagnosticPromptOptions) { return Effect.gen(function*() { const terminal = yield* Terminal.Terminal const columns = yield* terminal.columns - const clearPrompt = Doc.cat(Doc.eraseLine, Doc.cursorLeft) + const clearPrompt = ERASE_LINE + CURSOR_LEFT // Match Effect CLI pattern: "\n".repeat(visibleCount) + message // We have 2 extra lines (helpText + hardLine) compared to Effect CLI @@ -310,20 +305,17 @@ function handleClear(options: DiagnosticPromptOptions) { const visibleCount = Math.min(options.diagnostics.length, options.maxPerPage) const text = "\n".repeat(visibleCount + 2) + options.message const clearOutput = eraseText(text, columns) - return clearOutput.pipe( - Doc.cat(clearPrompt), - Doc.render({ style: "pretty", options: { lineWidth: columns } }) - ) + return clearOutput + clearPrompt }) } function handleRender(options: DiagnosticPromptOptions) { return ( state: State, - action: Prompt.Prompt.Action> + action: Prompt.Action> ) => { return Action.$match(action, { - Beep: () => Effect.succeed(renderBeep), + Beep: () => Effect.succeed(BEEP), NextFrame: ({ state }) => renderNextFrame(state, options), Submit: () => renderSubmission(state, options) }) diff --git a/packages/language-service/src/cli/setup/diff-renderer.ts b/packages/language-service/src/cli/setup/diff-renderer.ts index f8f041c3..757317d7 100644 --- a/packages/language-service/src/cli/setup/diff-renderer.ts +++ b/packages/language-service/src/cli/setup/diff-renderer.ts @@ -1,9 +1,8 @@ -import * as Ansi from "@effect/printer-ansi/Ansi" -import * as Doc from "@effect/printer-ansi/AnsiDoc" import * as Console from "effect/Console" import * as Effect from "effect/Effect" import * as Option from "effect/Option" import type * as ts from "typescript" +import { ansi, BOLD, CYAN, DIM, GREEN, RED, YELLOW } from "../ansi" import type { Assessment } from "./assessment" import type { ComputeChangesResult } from "./changes" import { renderPlainTextFileChanges } from "./text-diff-renderer" @@ -20,19 +19,19 @@ function getLines(text: string): ReadonlyArray { * @param lineNum - Line number (1-based) or undefined for lines without numbers (changes) * @param symbol - Symbol to display: "|" for unchanged, "-" for deletion, "+" for addition * @param text - The actual line text - * @param color - The ANSI color to apply + * @param color - The ANSI color code to apply */ function renderLine( lineNum: number | undefined, symbol: "|" | "-" | "+", text: string, - color: Ansi.Ansi -): Doc.AnsiDoc { + color: string +): string { const lineNumPart = lineNum !== undefined ? String(lineNum).padStart(4, " ") : " " - return Doc.annotate(Doc.text(`${lineNumPart} ${symbol} ${text}`), color) + return ansi(`${lineNumPart} ${symbol} ${text}`, color) } /** @@ -41,7 +40,7 @@ function renderLine( export function renderTextChange( sourceFile: ts.SourceFile, textChange: ts.TextChange -): ReadonlyArray { +): ReadonlyArray { const startPos = textChange.span.start const endPos = textChange.span.start + textChange.span.length @@ -53,13 +52,13 @@ export function renderTextChange( const startCol = startLineAndChar.character const endCol = endLineAndChar.character - const lines: Array = [] + const lines: Array = [] const allLines = getLines(sourceFile.text) // Show 1 line before the change (if exists) if (startLine > 0) { const contextBefore = allLines[startLine - 1] - lines.push(renderLine(startLine, "|", contextBefore, Ansi.blackBright)) + lines.push(renderLine(startLine, "|", contextBefore, DIM)) } // ============================================================================ @@ -74,7 +73,7 @@ export function renderTextChange( // Only show the kept part if it contains non-whitespace characters const hasNonWhitespaceKept = keptBeforeDeletion.trim().length > 0 if (hasNonWhitespaceKept) { - lines.push(renderLine(startLine + 1, "|", keptBeforeDeletion, Ansi.blackBright)) + lines.push(renderLine(startLine + 1, "|", keptBeforeDeletion, DIM)) } // Show the deleted part of the first line @@ -85,7 +84,7 @@ export function renderTextChange( if (deletedOnFirstLine.length > 0) { // Align with spaces to match the kept part's position const spacePadding = hasNonWhitespaceKept ? " ".repeat(keptBeforeDeletion.length) : "" - lines.push(renderLine(undefined, "-", `${spacePadding}${deletedOnFirstLine}`, Ansi.red)) + lines.push(renderLine(undefined, "-", `${spacePadding}${deletedOnFirstLine}`, RED)) } } @@ -93,7 +92,7 @@ export function renderTextChange( for (let i = startLine + 1; i < endLine; i++) { const lineText = allLines[i] if (lineText !== undefined) { - lines.push(renderLine(undefined, "-", lineText, Ansi.red)) + lines.push(renderLine(undefined, "-", lineText, RED)) } } @@ -103,7 +102,7 @@ export function renderTextChange( const deletedOnLastLine = lastLineText.slice(0, endCol) if (deletedOnLastLine.length > 0) { - lines.push(renderLine(undefined, "-", deletedOnLastLine, Ansi.red)) + lines.push(renderLine(undefined, "-", deletedOnLastLine, RED)) } } @@ -130,7 +129,7 @@ export function renderTextChange( // Align first line of addition with the kept part const padding = (i === 0 && hasNonWhitespaceKept) ? spacePadding : "" - lines.push(renderLine(undefined, "+", `${padding}${newLine}`, Ansi.green)) + lines.push(renderLine(undefined, "+", `${padding}${newLine}`, GREEN)) } } @@ -183,7 +182,7 @@ export function renderTextChange( const keptAfterDeletion = lastLineText.slice(endCol) if (keptAfterDeletion.trim().length > 0) { const alignment = " ".repeat(alignmentForKeptPart) - lines.push(renderLine(endLine + 1, "|", `${alignment}${keptAfterDeletion}`, Ansi.blackBright)) + lines.push(renderLine(endLine + 1, "|", `${alignment}${keptAfterDeletion}`, DIM)) } } else if (startLine === endLine) { // Single line case: show the kept part after deletion @@ -191,14 +190,14 @@ export function renderTextChange( const keptAfterDeletion = firstLineText.slice(endCol) if (keptAfterDeletion.trim().length > 0) { const alignment = " ".repeat(alignmentForKeptPart) - lines.push(renderLine(startLine + 1, "|", `${alignment}${keptAfterDeletion}`, Ansi.blackBright)) + lines.push(renderLine(startLine + 1, "|", `${alignment}${keptAfterDeletion}`, DIM)) } } // Show 1 line after the change (if exists) if (endLine + 1 < allLines.length) { const contextAfter = allLines[endLine + 1] - lines.push(renderLine(endLine + 2, "|", contextAfter, Ansi.blackBright)) + lines.push(renderLine(endLine + 2, "|", contextAfter, DIM)) } return lines @@ -210,8 +209,8 @@ export function renderTextChange( export function renderFileChanges( sourceFile: ts.SourceFile, textChanges: ReadonlyArray -): ReadonlyArray { - const lines: Array = [] +): ReadonlyArray { + const lines: Array = [] // Sort changes by position const sortedChanges = [...textChanges].sort((a, b) => a.span.start - b.span.start) @@ -227,7 +226,7 @@ export function renderFileChanges( // Add separator between changes if there are multiple if (i < sortedChanges.length - 1) { - lines.push(Doc.text("")) + lines.push("") } } @@ -235,7 +234,7 @@ export function renderFileChanges( } /** - * Render code actions with diffs using @effect/printer-ansi + * Render code actions with diffs */ export const renderCodeActions = ( result: ComputeChangesResult, @@ -244,12 +243,7 @@ export const renderCodeActions = ( Effect.gen(function*() { // Check if there are no changes if (result.codeActions.length === 0) { - const noChanges = Doc.annotate( - Doc.text("No changes needed - your configuration is already up to date!"), - Ansi.green - ) - const noChangesStr = noChanges.pipe(Doc.render({ style: "pretty" })) - yield* Console.log(noChangesStr) + yield* Console.log(ansi("No changes needed - your configuration is already up to date!", GREEN)) return } @@ -285,31 +279,25 @@ export const renderCodeActions = ( const plainTextFile = plainTextFiles.find((pf) => pf.path === fileChange.fileName) // Render description and file name - const header = Doc.vsep([ - Doc.empty, - Doc.annotate(Doc.text(codeAction.description), Ansi.bold), - Doc.annotate(Doc.text(fileChange.fileName), Ansi.cyan), - Doc.empty - ]) - const headerStr = header.pipe(Doc.render({ style: "pretty" })) - yield* Console.log(headerStr) + const header = [ + "", + ansi(codeAction.description, BOLD), + ansi(fileChange.fileName, CYAN), + "" + ].join("\n") + yield* Console.log(header) if (sourceFile) { // Use source file for diff generation (JSON files) const diffLines = renderFileChanges(sourceFile, fileChange.textChanges) - const diff = Doc.vsep(diffLines) - const diffStr = diff.pipe(Doc.render({ style: "pretty" })) - yield* Console.log(diffStr) + yield* Console.log(diffLines.join("\n")) } else if (plainTextFile) { // Use plain text renderer for diff generation (markdown files) const diffLines = renderPlainTextFileChanges(plainTextFile.text, fileChange.textChanges) - const diff = Doc.vsep(diffLines) - const diffStr = diff.pipe(Doc.render({ style: "pretty" })) - yield* Console.log(diffStr) + yield* Console.log(diffLines.join("\n")) } else { // File not in assessment state, just mention we want to change it - const noticeStr = Doc.text(" (file will be modified)").pipe(Doc.render({ style: "pretty" })) - yield* Console.log(noticeStr) + yield* Console.log(" (file will be modified)") } } } @@ -318,11 +306,10 @@ export const renderCodeActions = ( if (result.messages.length > 0) { yield* Console.log("") for (const message of result.messages) { - const messageDoc = message.includes("WARNING") - ? Doc.annotate(Doc.text(message), Ansi.yellow) - : Doc.text(message) + const messageStr = message.includes("WARNING") + ? ansi(message, YELLOW) + : message - const messageStr = messageDoc.pipe(Doc.render({ style: "pretty" })) yield* Console.log(messageStr) } } diff --git a/packages/language-service/src/cli/setup/target-prompt.ts b/packages/language-service/src/cli/setup/target-prompt.ts index 77614499..1eeba6d5 100644 --- a/packages/language-service/src/cli/setup/target-prompt.ts +++ b/packages/language-service/src/cli/setup/target-prompt.ts @@ -1,11 +1,10 @@ -import * as Prompt from "@effect/cli/Prompt" -import type { QuitException, Terminal } from "@effect/platform/Terminal" import * as Effect from "effect/Effect" import * as Option from "effect/Option" +import * as Prompt from "effect/unstable/cli/Prompt" import type { Assessment } from "./assessment" import { getAllDiagnostics } from "./diagnostic-info" import { createDiagnosticPrompt } from "./diagnostic-prompt" -import type { Editor, Target } from "./target" +import type { Editor } from "./target" /** * Context input for gathering target state @@ -20,7 +19,7 @@ export interface GatherTargetContext { export const gatherTargetState = ( assessment: Assessment.State, context: GatherTargetContext -): Effect.Effect => +) => Effect.gen(function*() { // Determine current LSP installation state const currentLspState = Option.match(assessment.packageJson.lspVersion, { diff --git a/packages/language-service/src/cli/setup/text-diff-renderer.ts b/packages/language-service/src/cli/setup/text-diff-renderer.ts index 3d4fbce0..2431746a 100644 --- a/packages/language-service/src/cli/setup/text-diff-renderer.ts +++ b/packages/language-service/src/cli/setup/text-diff-renderer.ts @@ -1,5 +1,4 @@ -import * as Ansi from "@effect/printer-ansi/Ansi" -import * as Doc from "@effect/printer-ansi/AnsiDoc" +import { ansi, DIM, GREEN, RED } from "../ansi" /** * A text change span (similar to ts.TextChange but for plain text) @@ -48,19 +47,19 @@ function getLineAndCharacterOfPosition( * @param lineNum - Line number (1-based) or undefined for lines without numbers (changes) * @param symbol - Symbol to display: "|" for unchanged, "-" for deletion, "+" for addition * @param text - The actual line text - * @param color - The ANSI color to apply + * @param color - The ANSI color code to apply */ function renderLine( lineNum: number | undefined, symbol: "|" | "-" | "+", text: string, - color: Ansi.Ansi -): Doc.AnsiDoc { + color: string +): string { const lineNumPart = lineNum !== undefined ? String(lineNum).padStart(4, " ") : " " - return Doc.annotate(Doc.text(`${lineNumPart} ${symbol} ${text}`), color) + return ansi(`${lineNumPart} ${symbol} ${text}`, color) } /** @@ -69,7 +68,7 @@ function renderLine( export function renderPlainTextChange( text: string, textChange: TextChange -): ReadonlyArray { +): ReadonlyArray { const startPos = textChange.span.start const endPos = textChange.span.start + textChange.span.length @@ -81,13 +80,13 @@ export function renderPlainTextChange( const startCol = startLineAndChar.character const endCol = endLineAndChar.character - const lines: Array = [] + const lines: Array = [] const allLines = getLines(text) // Show 1 line before the change (if exists) if (startLine > 0) { const contextBefore = allLines[startLine - 1] - lines.push(renderLine(startLine, "|", contextBefore, Ansi.blackBright)) + lines.push(renderLine(startLine, "|", contextBefore, DIM)) } // ============================================================================ @@ -102,7 +101,7 @@ export function renderPlainTextChange( // Only show the kept part if it contains non-whitespace characters const hasNonWhitespaceKept = keptBeforeDeletion.trim().length > 0 if (hasNonWhitespaceKept) { - lines.push(renderLine(startLine + 1, "|", keptBeforeDeletion, Ansi.blackBright)) + lines.push(renderLine(startLine + 1, "|", keptBeforeDeletion, DIM)) } // Show the deleted part of the first line @@ -113,7 +112,7 @@ export function renderPlainTextChange( if (deletedOnFirstLine.length > 0) { // Align with spaces to match the kept part's position const spacePadding = hasNonWhitespaceKept ? " ".repeat(keptBeforeDeletion.length) : "" - lines.push(renderLine(undefined, "-", `${spacePadding}${deletedOnFirstLine}`, Ansi.red)) + lines.push(renderLine(undefined, "-", `${spacePadding}${deletedOnFirstLine}`, RED)) } } @@ -121,7 +120,7 @@ export function renderPlainTextChange( for (let i = startLine + 1; i < endLine; i++) { const lineText = allLines[i] if (lineText !== undefined) { - lines.push(renderLine(undefined, "-", lineText, Ansi.red)) + lines.push(renderLine(undefined, "-", lineText, RED)) } } @@ -131,7 +130,7 @@ export function renderPlainTextChange( const deletedOnLastLine = lastLineText.slice(0, endCol) if (deletedOnLastLine.length > 0) { - lines.push(renderLine(undefined, "-", deletedOnLastLine, Ansi.red)) + lines.push(renderLine(undefined, "-", deletedOnLastLine, RED)) } } @@ -158,7 +157,7 @@ export function renderPlainTextChange( // Align first line of addition with the kept part const padding = (i === 0 && hasNonWhitespaceKept) ? spacePadding : "" - lines.push(renderLine(undefined, "+", `${padding}${newLine}`, Ansi.green)) + lines.push(renderLine(undefined, "+", `${padding}${newLine}`, GREEN)) } } @@ -200,21 +199,21 @@ export function renderPlainTextChange( const keptAfterDeletion = lastLineText.slice(endCol) if (keptAfterDeletion.trim().length > 0) { const alignment = " ".repeat(alignmentForKeptPart) - lines.push(renderLine(endLine + 1, "|", `${alignment}${keptAfterDeletion}`, Ansi.blackBright)) + lines.push(renderLine(endLine + 1, "|", `${alignment}${keptAfterDeletion}`, DIM)) } } else if (startLine === endLine) { const firstLineText = allLines[startLine] const keptAfterDeletion = firstLineText.slice(endCol) if (keptAfterDeletion.trim().length > 0) { const alignment = " ".repeat(alignmentForKeptPart) - lines.push(renderLine(startLine + 1, "|", `${alignment}${keptAfterDeletion}`, Ansi.blackBright)) + lines.push(renderLine(startLine + 1, "|", `${alignment}${keptAfterDeletion}`, DIM)) } } // Show 1 line after the change (if exists) if (endLine + 1 < allLines.length) { const contextAfter = allLines[endLine + 1] - lines.push(renderLine(endLine + 2, "|", contextAfter, Ansi.blackBright)) + lines.push(renderLine(endLine + 2, "|", contextAfter, DIM)) } return lines @@ -226,8 +225,8 @@ export function renderPlainTextChange( export function renderPlainTextFileChanges( text: string, textChanges: ReadonlyArray -): ReadonlyArray { - const lines: Array = [] +): ReadonlyArray { + const lines: Array = [] // Sort changes by position const sortedChanges = [...textChanges].sort((a, b) => a.span.start - b.span.start) @@ -243,7 +242,7 @@ export function renderPlainTextFileChanges( // Add separator between changes if there are multiple if (i < sortedChanges.length - 1) { - lines.push(Doc.text("")) + lines.push("") } } diff --git a/packages/language-service/src/cli/setup/tsconfig-prompt.ts b/packages/language-service/src/cli/setup/tsconfig-prompt.ts index 91319aeb..0ae2b55a 100644 --- a/packages/language-service/src/cli/setup/tsconfig-prompt.ts +++ b/packages/language-service/src/cli/setup/tsconfig-prompt.ts @@ -1,11 +1,9 @@ -import * as Prompt from "@effect/cli/Prompt" -import type * as PlatformError from "@effect/platform/Error" -import * as FileSystem from "@effect/platform/FileSystem" -import * as Path from "@effect/platform/Path" -import type { QuitException, Terminal } from "@effect/platform/Terminal" import * as Array from "effect/Array" import * as Effect from "effect/Effect" -import type { FileInput } from "../utils" +import * as FileSystem from "effect/FileSystem" +import * as Path from "effect/Path" +import type * as PlatformError from "effect/PlatformError" +import * as Prompt from "effect/unstable/cli/Prompt" import { FileReadError, TsConfigNotFoundError } from "./errors" /** @@ -32,11 +30,7 @@ const findTsConfigFiles = ( */ export const selectTsConfigFile = ( currentDir: string -): Effect.Effect< - FileInput, - PlatformError.PlatformError | QuitException | TsConfigNotFoundError | FileReadError, - FileSystem.FileSystem | Path.Path | Terminal -> => +) => Effect.gen(function*() { const fs = yield* FileSystem.FileSystem const path = yield* Path.Path diff --git a/packages/language-service/src/cli/unpatch.ts b/packages/language-service/src/cli/unpatch.ts index 504d2cb3..917fbc12 100644 --- a/packages/language-service/src/cli/unpatch.ts +++ b/packages/language-service/src/cli/unpatch.ts @@ -1,22 +1,21 @@ -import * as Command from "@effect/cli/Command" -import * as Options from "@effect/cli/Options" -import * as FileSystem from "@effect/platform/FileSystem" import * as Effect from "effect/Effect" +import * as FileSystem from "effect/FileSystem" +import { Command, Flag } from "effect/unstable/cli" import { getModuleFilePath, getSourceFileText, getUnpatchedSourceFile } from "./utils" const LOCAL_TYPESCRIPT_DIR = "./node_modules/typescript" -const dirPath = Options.directory("dir").pipe( - Options.withDefault(LOCAL_TYPESCRIPT_DIR), - Options.withDescription("The directory of the typescript package to patch.") +const dirPath = Flag.directory("dir").pipe( + Flag.withDefault(LOCAL_TYPESCRIPT_DIR), + Flag.withDescription("The directory of the typescript package to patch.") ) -const moduleNames = Options.choice("module", [ +const moduleNames = Flag.choice("module", [ "tsc", "typescript" ]).pipe( - Options.repeated, - Options.withDescription("The name of the module to patch.") + Flag.atLeast(0), + Flag.withDescription("The name of the module to patch.") ) export const unpatch = Command.make( diff --git a/packages/language-service/src/cli/utils.ts b/packages/language-service/src/cli/utils.ts index df413396..1fdde494 100644 --- a/packages/language-service/src/cli/utils.ts +++ b/packages/language-service/src/cli/utils.ts @@ -1,21 +1,20 @@ -import * as FileSystem from "@effect/platform/FileSystem" -import * as Path from "@effect/platform/Path" -import * as Context from "effect/Context" import * as Data from "effect/Data" import * as Effect from "effect/Effect" +import * as Encoding from "effect/Encoding" +import * as FileSystem from "effect/FileSystem" import * as Layer from "effect/Layer" +import * as Path from "effect/Path" import * as Predicate from "effect/Predicate" +import * as Result from "effect/Result" import * as Schema from "effect/Schema" +import * as ServiceMap from "effect/ServiceMap" import type * as ts from "typescript" import * as TypeScriptUtils from "../core/TypeScriptUtils" const PackageJsonSchema = Schema.Struct({ name: Schema.String, version: Schema.String, - scripts: Schema.optional(Schema.Record({ - key: Schema.String, - value: Schema.String - })) + scripts: Schema.optional(Schema.Record(Schema.String, Schema.String)) }) export class UnableToFindPackageJsonError extends Data.TaggedError("UnableToFindPackageError")<{ @@ -66,10 +65,7 @@ export interface FileInput { /** * TypeScript API context for CLI operations */ -export class TypeScriptContext extends Context.Tag("TypeScriptContext")< - TypeScriptContext, - TypeScriptApi ->() { +export class TypeScriptContext extends ServiceMap.Service()("TypeScriptContext") { static live = (cwd: string) => Layer.effect( TypeScriptContext, @@ -78,7 +74,7 @@ export class TypeScriptContext extends Context.Tag("TypeScriptContext")< try: () => require(require.resolve("typescript", { paths: [cwd] })) as typeof ts, catch: (cause) => new UnableToFindInstalledTypeScriptPackage({ cause }) }).pipe( - Effect.orElse(() => + Effect.catch(() => Effect.try({ // eslint-disable-next-line @typescript-eslint/no-require-imports try: () => require("typescript") as typeof ts, @@ -96,7 +92,7 @@ export const getPackageJsonData = Effect.fn("getPackageJsonData")(function*(pack const packageJsonContent = yield* fs.readFileString(packageJsonPath).pipe( Effect.mapError((cause) => new UnableToFindPackageJsonError({ packageJsonPath, cause })) ) - const packageJsonData = yield* Schema.decode(Schema.parseJson(PackageJsonSchema))(packageJsonContent).pipe( + const packageJsonData = yield* Schema.decodeEffect(Schema.fromJsonString(PackageJsonSchema))(packageJsonContent).pipe( Effect.mapError((cause) => new MalformedPackageJsonError({ packageJsonPath, cause })) ) return { ...packageJsonData } @@ -149,16 +145,30 @@ function effectLspPatchUtils(){ return patchWithWrappingFunction }) -export const AppliedPatchMetadata = Schema.compose( - Schema.StringFromBase64, - Schema.parseJson(Schema.Struct({ - version: Schema.String, - replacedText: Schema.String, - insertedPrefixLength: Schema.Int, - insertedTextLength: Schema.Int - })) -) -export type AppliedPatchMetadata = Schema.Schema.Type +const AppliedPatchMetadataStruct = Schema.Struct({ + version: Schema.String, + replacedText: Schema.String, + insertedPrefixLength: Schema.Int, + insertedTextLength: Schema.Int +}) + +export type AppliedPatchMetadata = typeof AppliedPatchMetadataStruct.Type + +/** Decode a base64-encoded JSON string into AppliedPatchMetadata */ +const decodeAppliedPatchMetadata = (base64str: string): Effect.Effect => { + const decoded = Encoding.decodeBase64(base64str) + if (Result.isFailure(decoded)) { + return Effect.fail(decoded.failure) + } + const jsonStr = new TextDecoder().decode(decoded.success) + return Schema.decodeEffect(Schema.fromJsonString(AppliedPatchMetadataStruct))(jsonStr) +} + +/** Encode AppliedPatchMetadata into a base64-encoded JSON string */ +const encodeAppliedPatchMetadata = (metadata: AppliedPatchMetadata): Effect.Effect => + Schema.encodeEffect(Schema.fromJsonString(AppliedPatchMetadataStruct))(metadata).pipe( + Effect.map((jsonStr) => Encoding.encodeBase64(jsonStr)) + ) export const makeEffectLspPatchChange = Effect.fn("makeEffectLspPatchChange")( function*( @@ -176,7 +186,7 @@ export const makeEffectLspPatchChange = Effect.fn("makeEffectLspPatchChange")( insertedPrefixLength: insertedPrefix.length, insertedTextLength: insertedText.length } - const encodedMetadata = yield* Schema.encode(AppliedPatchMetadata)(metadata) + const encodedMetadata = yield* encodeAppliedPatchMetadata(metadata) const textChange: ts.TextChange = { span: { start: pos, length: end - pos }, newText: insertedPrefix + "/* @effect-lsp-patch " + encodedMetadata + " */ " + insertedText @@ -198,7 +208,7 @@ export const extractAppliedEffectLspPatches = Effect.fn("extractAppliedEffectLsp const commentTextMetadata = match[1] const commentRange = tsUtils.getCommentAtPosition(sourceFile, match.index) if (!commentRange) continue - const metadata = yield* Schema.decode(AppliedPatchMetadata)(commentTextMetadata).pipe( + const metadata = yield* decodeAppliedPatchMetadata(commentTextMetadata).pipe( Effect.mapError((cause) => new CorruptedPatchedSourceFileError({ filePath: sourceFile.fileName, cause })) ) patches.push(metadata) diff --git a/packages/language-service/src/cli/utils/Spinner.ts b/packages/language-service/src/cli/utils/Spinner.ts index ef9cbecc..fc1a5058 100644 --- a/packages/language-service/src/cli/utils/Spinner.ts +++ b/packages/language-service/src/cli/utils/Spinner.ts @@ -1,11 +1,12 @@ -import * as Terminal from "@effect/platform/Terminal" -import * as Ansi from "@effect/printer-ansi/Ansi" -import * as Doc from "@effect/printer-ansi/AnsiDoc" import * as Cause from "effect/Cause" import * as Effect from "effect/Effect" import * as Exit from "effect/Exit" import { dual } from "effect/Function" import * as Option from "effect/Option" +import * as Terminal from "effect/Terminal" +import { ansi, BLUE, BOLD, CURSOR_HIDE, CURSOR_LEFT, CURSOR_SHOW, ERASE_LINE, GREEN, RED } from "../ansi" + +const CLEAR_LINE = ERASE_LINE + CURSOR_LEFT /** * Options for the spinner @@ -26,37 +27,27 @@ export interface SpinnerHandle { // Full classic dots spinner sequence const DEFAULT_FRAMES: ReadonlyArray = [ - "⠋", - "⠙", - "⠹", - "⠸", - "⠼", - "⠴", - "⠦", - "⠧", - "⠇", - "⠏" + "\u280B", + "\u2819", + "\u2839", + "\u2838", + "\u283C", + "\u2834", + "\u2826", + "\u2827", + "\u2807", + "\u280F" ] // Figures for different platforms const isWindows = typeof process !== "undefined" && process.platform === "win32" const figures = { - tick: isWindows ? "√" : "✔", - cross: isWindows ? "×" : "✖" -} - -// Small render helpers to reduce per-frame work. -const CLEAR_LINE = Doc.cat(Doc.eraseLine, Doc.cursorLeft) -const CURSOR_HIDE = Doc.render(Doc.cursorHide, { style: "pretty" }) -const CURSOR_SHOW = Doc.render(Doc.cursorShow, { style: "pretty" }) - -const renderDoc = (columns: number, doc: Doc.AnsiDoc, addNewline = false): string => { - const prepared = addNewline ? Doc.cat(doc, Doc.hardLine) : doc - return Doc.render(prepared, { style: "pretty", options: { lineWidth: columns } }) + tick: isWindows ? "\u221A" : "\u2714", + cross: isWindows ? "\u00D7" : "\u2716" } /** - * A spinner that renders while `effect` runs and prints ✔/✖ on completion. + * A spinner that renders while `effect` runs and prints tick/cross on completion. * Provides a handle to update the message while running. */ export const spinner: { @@ -87,21 +78,15 @@ export const spinner: { const frames = options.frames ?? DEFAULT_FRAMES const frameCount = frames.length - const displayDoc = (doc: Doc.AnsiDoc, addNewline = false) => - Effect.gen(function*() { - const columns = yield* terminal.columns - const out = renderDoc(columns, doc, addNewline) - yield* Effect.orDie(terminal.display(out)) - }) + const displayStr = (str: string) => Effect.orDie(terminal.display(str)) const renderFrame = Effect.gen(function*() { const i = index index = index + 1 - const spinnerDoc = Doc.annotate(Doc.text(frames[i % frameCount]!), Ansi.blue) - const messageDoc = Doc.annotate(Doc.text(currentMessage), Ansi.bold) - - const line = Doc.hsep([spinnerDoc, messageDoc]) - yield* displayDoc(Doc.cat(CLEAR_LINE, line)) + const spinnerStr = ansi(frames[i % frameCount]!, BLUE) + const messageStr = ansi(currentMessage, BOLD) + const line = `${spinnerStr} ${messageStr}` + yield* displayStr(CLEAR_LINE + line) }) const computeFinalMessage = (exit: Exit.Exit): string => @@ -109,14 +94,14 @@ export const spinner: { onFailure: (cause) => { let baseMessage = currentMessage if (options.onFailure) { - const failureOption = Cause.failureOption(cause) + const failureOption = Cause.findErrorOption(cause) if (Option.isSome(failureOption)) { baseMessage = options.onFailure(failureOption.value) } } - if (Cause.isInterrupted(cause)) { + if (Cause.hasInterrupts(cause)) { return `${baseMessage} (interrupted)` - } else if (Cause.isDie(cause)) { + } else if (Cause.hasDies(cause)) { return `${baseMessage} (died)` } else { return baseMessage @@ -128,15 +113,14 @@ export const spinner: { const renderFinal = (exit: Exit.Exit) => Effect.gen(function*() { const icon = Exit.isSuccess(exit) - ? Doc.annotate(Doc.text(figures.tick), Ansi.green) - : Doc.annotate(Doc.text(figures.cross), Ansi.red) + ? ansi(figures.tick, GREEN) + : ansi(figures.cross, RED) const finalMessage = computeFinalMessage(exit) + const msgStr = ansi(finalMessage, BOLD) + const line = `${icon} ${msgStr}` - const msgDoc = Doc.annotate(Doc.text(finalMessage), Ansi.bold) - const line = Doc.hsep([icon, msgDoc]) - - yield* displayDoc(Doc.cat(CLEAR_LINE, line), true) + yield* displayStr(CLEAR_LINE + line + "\n") }) const handle: SpinnerHandle = { @@ -157,11 +141,7 @@ export const spinner: { ({ handle }) => effect(handle), // release ({ renderFinal, terminal }, exitValue) => - Effect.gen(function*() { - // Signal the spinner fiber to finish by setting the exit. - // (No external interrupt of the spinner fiber.) - yield* renderFinal(exitValue) - }).pipe( + renderFinal(exitValue).pipe( // Ensure cursor is shown even if something above failed. Effect.ensuring(Effect.orDie(terminal.display(CURSOR_SHOW))) ) diff --git a/packages/language-service/src/codegens/annotate.ts b/packages/language-service/src/codegens/annotate.ts index d60333ac..6752ab3d 100644 --- a/packages/language-service/src/codegens/annotate.ts +++ b/packages/language-service/src/codegens/annotate.ts @@ -40,7 +40,7 @@ export const annotate = LSP.createCodegen({ if (!initializerType) continue const enclosingNode = ts.findAncestor(variableDeclaration, (_) => tsUtils.isDeclarationKind(_.kind)) || sourceFile - const initializerTypeNode = Option.fromNullable(typeCheckerUtils.typeToSimplifiedTypeNode( + const initializerTypeNode = Option.fromNullishOr(typeCheckerUtils.typeToSimplifiedTypeNode( initializerType, enclosingNode, ts.NodeBuilderFlags.NoTruncation | ts.NodeBuilderFlags.IgnoreErrors diff --git a/packages/language-service/src/completions/effectCodegensComment.ts b/packages/language-service/src/completions/effectCodegensComment.ts index 5afc4c1c..5eac778f 100644 --- a/packages/language-service/src/completions/effectCodegensComment.ts +++ b/packages/language-service/src/completions/effectCodegensComment.ts @@ -23,7 +23,7 @@ export const effectCodegensComment = LSP.createCompletion({ length: Math.max(0, position - lastIndex) } - const allCodegens = Array.sort(Object.values(codegens).map((codegen) => codegen.name), Order.string) + const allCodegens = Array.sort(Object.values(codegens).map((codegen) => codegen.name), Order.String) .join(",") const enableSnippet = "${1|" + allCodegens + "|} $0" diff --git a/packages/language-service/src/completions/effectDiagnosticsComment.ts b/packages/language-service/src/completions/effectDiagnosticsComment.ts index 8606f8e2..e37194a2 100644 --- a/packages/language-service/src/completions/effectDiagnosticsComment.ts +++ b/packages/language-service/src/completions/effectDiagnosticsComment.ts @@ -23,7 +23,7 @@ export const effectDiagnosticsComment = LSP.createCompletion({ length: Math.max(0, position - lastIndex) } - const allDiagnostics = Array.sort(Object.values(diagnostics).map((diagnostic) => diagnostic.name), Order.string) + const allDiagnostics = Array.sort(Object.values(diagnostics).map((diagnostic) => diagnostic.name), Order.String) .join(",") const disableSnippet = "${1|" + allDiagnostics + "|}:${2|off,warning,error,message,suggestion|}$0" diff --git a/packages/language-service/src/core/LanguageServicePluginOptions.ts b/packages/language-service/src/core/LanguageServicePluginOptions.ts index 6eeb570e..ef1525d2 100644 --- a/packages/language-service/src/core/LanguageServicePluginOptions.ts +++ b/packages/language-service/src/core/LanguageServicePluginOptions.ts @@ -1,7 +1,7 @@ import { isArray } from "effect/Array" import * as Array from "effect/Array" import { pipe } from "effect/Function" -import { hasProperty, isBoolean, isNumber, isObject, isRecord, isString } from "effect/Predicate" +import { hasProperty, isBoolean, isNumber, isObject, isString } from "effect/Predicate" import * as Record from "effect/Record" import * as Nano from "./Nano" @@ -52,7 +52,7 @@ function isValidSeverityLevel(value: string): value is DiagnosticSeverity | "off } function parseDiagnosticSeverity(config: Record): Record { - if (!isRecord(config)) return {} + if (!isObject(config)) return {} return Object.fromEntries( pipe( Object.entries(config), @@ -132,8 +132,8 @@ export function parse(config: any): LanguageServicePluginOptions { ? config.diagnostics : defaults.diagnostics, diagnosticSeverity: - isObject(config) && hasProperty(config, "diagnosticSeverity") && isRecord(config.diagnosticSeverity) - ? parseDiagnosticSeverity(config.diagnosticSeverity) + isObject(config) && hasProperty(config, "diagnosticSeverity") && isObject(config.diagnosticSeverity) + ? parseDiagnosticSeverity(config.diagnosticSeverity as Record) : defaults.diagnosticSeverity, diagnosticsName: isObject(config) && hasProperty(config, "diagnosticsName") && isBoolean(config.diagnosticsName) ? config.diagnosticsName @@ -188,8 +188,8 @@ export function parse(config: any): LanguageServicePluginOptions { isArray(config.barrelImportPackages) && config.barrelImportPackages.every(isString) ? config.barrelImportPackages.map((_) => _.toLowerCase()) : defaults.barrelImportPackages, - importAliases: isObject(config) && hasProperty(config, "importAliases") && isRecord(config.importAliases) - ? Record.map(config.importAliases, (value) => String(value)) + importAliases: isObject(config) && hasProperty(config, "importAliases") && isObject(config.importAliases) + ? Record.map(config.importAliases as Record, (value) => String(value)) : defaults.importAliases, topLevelNamedReexports: isObject(config) && hasProperty(config, "topLevelNamedReexports") && isString(config.topLevelNamedReexports) && diff --git a/packages/language-service/src/core/LayerGraph.ts b/packages/language-service/src/core/LayerGraph.ts index 8af30b3c..63aa12f2 100644 --- a/packages/language-service/src/core/LayerGraph.ts +++ b/packages/language-service/src/core/LayerGraph.ts @@ -616,11 +616,11 @@ export const convertOutlineGraphToLayerMagic = Nano.fn("convertOutlineGraphToLay const missingOutputTypes = new Set(outputTypes) const currentRequiredTypes = new Set() const orderByProvidedCount = Order.mapInput( - Order.reverse(Order.number), + Order.flip(Order.Number), (_: LayerOutlineGraphNodeInfo) => _.provides.length ) const orderByRequiredCount = Order.mapInput( - Order.reverse(Order.number), + Order.flip(Order.Number), (_: LayerOutlineGraphNodeInfo) => _.requires.length ) const layerOrder = Order.combine(orderByProvidedCount, orderByRequiredCount) diff --git a/packages/language-service/src/core/Nano.ts b/packages/language-service/src/core/Nano.ts index 41c1b996..71e86fd7 100644 --- a/packages/language-service/src/core/Nano.ts +++ b/packages/language-service/src/core/Nano.ts @@ -1,7 +1,7 @@ -import * as Either from "effect/Either" import { dual } from "effect/Function" import type { TypeLambda } from "effect/HKT" import * as Option from "effect/Option" +import * as Result from "effect/Result" export const debugPerformance = false @@ -235,25 +235,25 @@ export const withSpan = ( return nano } -export const unsafeRun = (nano: Nano): Either.Either => { +export const unsafeRun = (nano: Nano): Result.Result => { const fiber = new NanoFiber() const result = fiber.runLoop(nano)! if (result._tag === "Success") { - return Either.right(result.value) + return Result.succeed(result.value) } - return Either.left(result.value) + return Result.fail(result.value) } -export const run = (nano: Nano): Either.Either => { +export const run = (nano: Nano): Result.Result => { const fiber = new NanoFiber() try { const result = fiber.runLoop(nano)! if (result._tag === "Success") { - return Either.right(result.value) + return Result.succeed(result.value) } - return Either.left(result.value) + return Result.fail(result.value) } catch (e) { - return Either.left(new NanoDefectException(e, fiber._lastSpan)) + return Result.fail(new NanoDefectException(e, fiber._lastSpan)) } } diff --git a/packages/language-service/src/core/TypeCheckerApi.ts b/packages/language-service/src/core/TypeCheckerApi.ts index fd1cd302..8b570557 100644 --- a/packages/language-service/src/core/TypeCheckerApi.ts +++ b/packages/language-service/src/core/TypeCheckerApi.ts @@ -1,5 +1,4 @@ -import { isFunction } from "effect/Function" -import { hasProperty } from "effect/Predicate" +import { hasProperty, isFunction } from "effect/Predicate" import type ts from "typescript" import * as Nano from "../core/Nano.js" @@ -17,7 +16,7 @@ export function makeResolveExternalModuleName(typeChecker: TypeCheckerApi) { if (!(hasProperty(typeChecker, "resolveExternalModuleName") && isFunction(typeChecker.resolveExternalModuleName))) { return } - const _internal = typeChecker.resolveExternalModuleName + const _internal = typeChecker.resolveExternalModuleName as (moduleSpecifier: ts.Expression) => ts.Symbol | undefined return (moduleSpecifier: ts.Expression): ts.Symbol | undefined => { return _internal(moduleSpecifier) } diff --git a/packages/language-service/src/core/TypeScriptUtils.ts b/packages/language-service/src/core/TypeScriptUtils.ts index e094445b..6dc2f1e7 100644 --- a/packages/language-service/src/core/TypeScriptUtils.ts +++ b/packages/language-service/src/core/TypeScriptUtils.ts @@ -499,7 +499,7 @@ export function makeTypeScriptUtils(ts: TypeScriptApi.TypeScriptApi): TypeScript } } else if (ts.isNamedImports(namedBindings)) { for (const importSpecifier of namedBindings.elements) { - const importProperty = Option.fromNullable(importSpecifier.propertyName).pipe( + const importProperty = Option.fromNullishOr(importSpecifier.propertyName).pipe( Option.orElse(() => Option.some(importSpecifier.name)) ) if (test(importSpecifier.name, statement.moduleSpecifier, importProperty)) { diff --git a/packages/language-service/src/diagnostics/missingEffectError.ts b/packages/language-service/src/diagnostics/missingEffectError.ts index 5261dce5..eb6c4201 100644 --- a/packages/language-service/src/diagnostics/missingEffectError.ts +++ b/packages/language-service/src/diagnostics/missingEffectError.ts @@ -119,7 +119,7 @@ export const missingEffectError = LSP.createDiagnostic({ ReadonlyArray.map((_) => _.literal), ReadonlyArray.filter((_) => ts.isLiteralExpression(_)), ReadonlyArray.map((_) => _.text), - ReadonlyArray.sort(Order.string), + ReadonlyArray.sort(Order.String), ReadonlyArray.map((_) => ts.factory.createPropertyAssignment( ts.factory.createIdentifier(_), diff --git a/packages/language-service/src/effect-lsp-patch-utils.ts b/packages/language-service/src/effect-lsp-patch-utils.ts index 7362748a..722fc8e4 100644 --- a/packages/language-service/src/effect-lsp-patch-utils.ts +++ b/packages/language-service/src/effect-lsp-patch-utils.ts @@ -1,7 +1,7 @@ import * as Array from "effect/Array" -import * as Either from "effect/Either" import { pipe } from "effect/Function" import * as Predicate from "effect/Predicate" +import * as Result from "effect/Result" import type * as ts from "typescript" import * as LanguageServicePluginOptions from "./core/LanguageServicePluginOptions" import * as LSP from "./core/LSP" @@ -76,8 +76,8 @@ export function checkSourceFileWorker( parsedOptions ), Nano.unsafeRun, - Either.map((_) => _.diagnostics), - Either.map( + Result.map((_) => _.diagnostics), + Result.map( Array.filter((_) => _.category === tsInstance.DiagnosticCategory.Error || _.category === tsInstance.DiagnosticCategory.Warning || @@ -87,7 +87,7 @@ export function checkSourceFileWorker( )) ) ), - Either.map( + Result.map( Array.map((_) => { if ( moduleName === "tsc" && parsedOptions.includeSuggestionsInTsc && @@ -98,7 +98,7 @@ export function checkSourceFileWorker( return _ }) ), - Either.getOrElse((e) => { + Result.getOrElse((e) => { console.error(e.message, "at", e.lastSpan) return [] }), diff --git a/packages/language-service/src/index.ts b/packages/language-service/src/index.ts index 980e61d9..afd0518b 100644 --- a/packages/language-service/src/index.ts +++ b/packages/language-service/src/index.ts @@ -1,5 +1,5 @@ -import * as Either from "effect/Either" import { pipe } from "effect/Function" +import * as Result from "effect/Result" import type ts from "typescript" import { completions } from "./completions.js" import { @@ -117,11 +117,11 @@ const init = ( return pipe( LSP.getSemanticDiagnosticsWithCodeFixes(diagnostics, sourceFile), runNano(program), - Either.map(({ codeFixes, diagnostics }) => { + Result.map(({ codeFixes, diagnostics }) => { effectCodeFixesForFile.set(fileName, codeFixes) return diagnostics }), - Either.getOrElse(() => []) + Result.getOrElse(() => []) ) } } @@ -215,7 +215,7 @@ const init = ( ) ), runNano(program), - Either.getOrElse(() => applicableCodeFixes) + Result.getOrElse(() => applicableCodeFixes) ) } } @@ -248,8 +248,8 @@ const init = ( ) ), runNano(program), - Either.map((effectCodeActions) => applicableRefactors.concat(effectCodeActions)), - Either.getOrElse(() => applicableRefactors) + Result.map((effectCodeActions) => applicableRefactors.concat(effectCodeActions)), + Result.getOrElse(() => applicableRefactors) ) } } @@ -307,7 +307,7 @@ const init = ( runNano(program) ) - if (Either.isRight(result)) return result.right + if (Result.isSuccess(result)) return result.success } } @@ -337,7 +337,7 @@ const init = ( applicableQuickInfo ), runNano(program), - Either.getOrElse(() => applicableQuickInfo) + Result.getOrElse(() => applicableQuickInfo) ) } } @@ -388,7 +388,7 @@ const init = ( ) ), runNano(program), - Either.getOrElse(() => applicableCompletions) + Result.getOrElse(() => applicableCompletions) ) } } @@ -433,7 +433,7 @@ const init = ( info.languageServiceHost ), runNano(program), - Either.getOrElse(() => applicableCompletionEntryDetails) + Result.getOrElse(() => applicableCompletionEntryDetails) ) } } @@ -453,7 +453,7 @@ const init = ( return pipe( goto(applicableDefinition, sourceFile, position), runNano(program), - Either.getOrElse(() => applicableDefinition) + Result.getOrElse(() => applicableDefinition) ) } } @@ -473,7 +473,7 @@ const init = ( return pipe( middlewareGenLike(sourceFile, span, preferences, applicableInlayHints), runNano(program), - Either.getOrElse(() => applicableInlayHints) + Result.getOrElse(() => applicableInlayHints) ) } } @@ -507,7 +507,7 @@ const init = ( applicableRenameInfo ), runNano(program), - Either.getOrElse(() => applicableRenameInfo) + Result.getOrElse(() => applicableRenameInfo) ) } } @@ -542,7 +542,7 @@ const init = ( } })), runNano(program), - Either.getOrElse((e) => ({ + Result.getOrElse((e) => ({ response: { success: false, error: e.message diff --git a/packages/language-service/src/quickinfo/dedupeJsDocs.ts b/packages/language-service/src/quickinfo/dedupeJsDocs.ts index d75f193e..fd64bad6 100644 --- a/packages/language-service/src/quickinfo/dedupeJsDocs.ts +++ b/packages/language-service/src/quickinfo/dedupeJsDocs.ts @@ -7,7 +7,7 @@ const SymbolDisplayPartEq = Eq.make((fa, fb) => fa.kind == const JSDocTagInfoEq = Eq.make((fa, fb) => fa.name === fb.name && typeof fa.text === typeof fb.text && - (typeof fa.text !== "undefined" ? Eq.array(SymbolDisplayPartEq)(fa.text!, fb.text!) : true) + (typeof fa.text !== "undefined" ? Eq.Array(SymbolDisplayPartEq)(fa.text!, fb.text!) : true) ) export function dedupeJsDocs(quickInfo: ts.QuickInfo | undefined): Nano.Nano { diff --git a/packages/language-service/src/refactors/layerMagic.ts b/packages/language-service/src/refactors/layerMagic.ts index c3d43a51..6daba08c 100644 --- a/packages/language-service/src/refactors/layerMagic.ts +++ b/packages/language-service/src/refactors/layerMagic.ts @@ -71,7 +71,7 @@ export const layerMagic = LSP.createRefactor({ Array.map((_) => _.node), Array.filter(ts.isExpression), Array.sort(Order.mapInput( - Order.number, + Order.Number, (_: ts.Node) => _.pos )) ) diff --git a/packages/language-service/src/refactors/toggleTypeAnnotation.ts b/packages/language-service/src/refactors/toggleTypeAnnotation.ts index 1cd63667..8f982dad 100644 --- a/packages/language-service/src/refactors/toggleTypeAnnotation.ts +++ b/packages/language-service/src/refactors/toggleTypeAnnotation.ts @@ -43,7 +43,7 @@ export const toggleTypeAnnotation = LSP.createRefactor({ const initializer = node.initializer! const initializerType = typeChecker.getTypeAtLocation(initializer) const enclosingNode = ts.findAncestor(node, (_) => tsUtils.isDeclarationKind(_.kind)) || sourceFile - const initializerTypeNode = Option.fromNullable(typeCheckerUtils.typeToSimplifiedTypeNode( + const initializerTypeNode = Option.fromNullishOr(typeCheckerUtils.typeToSimplifiedTypeNode( initializerType, enclosingNode, ts.NodeBuilderFlags.NoTruncation | ts.NodeBuilderFlags.IgnoreErrors diff --git a/packages/language-service/src/transform.ts b/packages/language-service/src/transform.ts index e110564e..5c9898f6 100644 --- a/packages/language-service/src/transform.ts +++ b/packages/language-service/src/transform.ts @@ -1,6 +1,6 @@ import * as Array from "effect/Array" -import * as Either from "effect/Either" import { pipe } from "effect/Function" +import * as Result from "effect/Result" import type { PluginConfig, TransformerExtras } from "ts-patch" import type * as ts from "typescript" import * as LanguageServicePluginOptions from "./core/LanguageServicePluginOptions" @@ -34,14 +34,14 @@ export default function( LanguageServicePluginOptions.parse(pluginConfig) ), Nano.run, - Either.map((_) => _.diagnostics), - Either.map( + Result.map((_) => _.diagnostics), + Result.map( Array.filter((_) => _.category === tsInstance.DiagnosticCategory.Error || _.category === tsInstance.DiagnosticCategory.Warning ) ), - Either.getOrElse(() => []), + Result.getOrElse(() => []), Array.map(addDiagnostic) ) diff --git a/packages/language-service/test/autoimport.test.ts b/packages/language-service/test/autoimport.test.ts index 77d620ff..76a088c2 100644 --- a/packages/language-service/test/autoimport.test.ts +++ b/packages/language-service/test/autoimport.test.ts @@ -41,10 +41,10 @@ function testAutoImport( Nano.run ) - expect(test._tag).toBe("Right") - if (test._tag === "Left") throw new Error("error in execution of nano") + expect(test._tag).toBe("Success") + if (test._tag === "Failure") throw new Error("error in execution of nano") return { - result: test.right, + result: test.success, toFilename: (moduleName: string) => { const expectedResolution = ts.resolveModuleName( moduleName, diff --git a/packages/language-service/test/completions.test.ts b/packages/language-service/test/completions.test.ts index f45c9e4a..e6dbde20 100644 --- a/packages/language-service/test/completions.test.ts +++ b/packages/language-service/test/completions.test.ts @@ -7,8 +7,8 @@ import * as TypeCheckerUtils from "@effect/language-service/core/TypeCheckerUtil import * as TypeParser from "@effect/language-service/core/TypeParser" import * as TypeScriptApi from "@effect/language-service/core/TypeScriptApi" import * as TypeScriptUtils from "@effect/language-service/core/TypeScriptUtils" -import * as Either from "effect/Either" import { pipe } from "effect/Function" +import * as Result from "effect/Result" import * as fs from "fs" import * as path from "path" import * as ts from "typescript" @@ -66,12 +66,12 @@ function testCompletionOnExample( Nano.unsafeRun ) - if (Either.isLeft(maybeEntries)) { + if (Result.isFailure(maybeEntries)) { expect(sourceText).toMatchSnapshot() return } - expect(maybeEntries.right).toMatchSnapshot() + expect(maybeEntries.success).toMatchSnapshot() } function testAllCompletions() { diff --git a/packages/language-service/test/diagnostics.test.ts b/packages/language-service/test/diagnostics.test.ts index 8b0798ca..2b0c9926 100644 --- a/packages/language-service/test/diagnostics.test.ts +++ b/packages/language-service/test/diagnostics.test.ts @@ -7,8 +7,8 @@ import * as TypeParser from "@effect/language-service/core/TypeParser" import * as TypeScriptApi from "@effect/language-service/core/TypeScriptApi" import * as TypeScriptUtils from "@effect/language-service/core/TypeScriptUtils" import { diagnostics } from "@effect/language-service/diagnostics" -import * as Either from "effect/Either" import { pipe } from "effect/Function" +import * as Result from "effect/Result" import * as fs from "fs" import * as path from "path" import * as ts from "typescript" @@ -96,8 +96,8 @@ function testDiagnosticOnExample( }), Nano.unsafeRun, async (result) => { - expect(Either.isRight(result), "should run with no error " + result).toEqual(true) - await expect(Either.getOrElse(result, () => "// no codefixes available")).toMatchFileSnapshot( + expect(Result.isSuccess(result), "should run with no error " + result).toEqual(true) + await expect(Result.getOrElse(result, () => "// no codefixes available")).toMatchFileSnapshot( snapshotFilePath ) } @@ -159,7 +159,7 @@ function testDiagnosticQuickfixesOnExample( codeFix.apply, Nano.provideService(TypeScriptApi.ChangeTracker, changeTracker), Nano.unsafeRun, - (result) => expect(Either.isRight(result), "should run with no error").toEqual(true) + (result) => expect(Result.isSuccess(result), "should run with no error").toEqual(true) ) ) // final source @@ -210,9 +210,9 @@ function testDiagnosticQuickfixesOnExample( ), Nano.unsafeRun, async (result) => { - expect(Either.isRight(result), "should run with no error " + result).toEqual(true) + expect(Result.isSuccess(result), "should run with no error " + result).toEqual(true) await Promise.all(promises) - await expect(Either.getOrElse(result, () => "// no codefixes available")).toMatchFileSnapshot( + await expect(Result.getOrElse(result, () => "// no codefixes available")).toMatchFileSnapshot( snapshotFilePathList ) } diff --git a/packages/language-service/test/internals.test.ts b/packages/language-service/test/internals.test.ts index 282b0323..0f7899ec 100644 --- a/packages/language-service/test/internals.test.ts +++ b/packages/language-service/test/internals.test.ts @@ -1,6 +1,6 @@ import * as Nano from "@effect/language-service/core/Nano" -import * as Either from "effect/Either" import { pipe } from "effect/Function" +import * as Result from "effect/Result" import { configFromSourceComment } from "./utils/mocks" import { describe, expect, it } from "vitest" @@ -22,18 +22,18 @@ describe("configFromSourceComment", () => { describe("nano", () => { it("flatMap-ping", () => { const program = Nano.flatMap(Nano.succeed(2), (_) => Nano.succeed(_ * 2)) - expect(Nano.run(program)).toEqual(Either.right(4)) + expect(Nano.run(program)).toEqual(Result.succeed(4)) }) it("sync", () => { const program = Nano.sync(() => 42) - expect(Nano.run(program)).toEqual(Either.right(42)) + expect(Nano.run(program)).toEqual(Result.succeed(42)) }) it("provide service test", () => { const Tag = Nano.Tag<{ value: number }>("test") const program = Nano.provideService(Tag, { value: 42 })(Nano.service(Tag)) - expect(Nano.run(program)).toEqual(Either.right({ value: 42 })) + expect(Nano.run(program)).toEqual(Result.succeed({ value: 42 })) const program2 = Nano.provideService(Tag, { value: 1 })(Nano.service(Tag)) - expect(Nano.run(program2)).toEqual(Either.right({ value: 1 })) + expect(Nano.run(program2)).toEqual(Result.succeed({ value: 1 })) const nested = pipe( Nano.service(Tag), Nano.provideService(Tag, { value: 1 }), @@ -42,7 +42,7 @@ describe("nano", () => { Nano.provideService(Tag, { value: 3 }), Nano.run ) - expect(nested).toEqual(Either.right([{ value: 1 }, { value: 2 }])) + expect(nested).toEqual(Result.succeed([{ value: 1 }, { value: 2 }])) }) it("should run gen", () => { const program = Nano.gen(function*() { @@ -50,7 +50,7 @@ describe("nano", () => { const b = yield* Nano.succeed(2) return a + b }) - expect(Nano.run(program)).toEqual(Either.right(3)) + expect(Nano.run(program)).toEqual(Result.succeed(3)) }) it("should run fn", () => { const program = Nano.fn("program")(function*(v1: number, v2: number) { @@ -58,7 +58,7 @@ describe("nano", () => { const b = yield* Nano.succeed(v2) return a + b }) - expect(Nano.run(program(1, 2))).toEqual(Either.right(3)) + expect(Nano.run(program(1, 2))).toEqual(Result.succeed(3)) }) it("should handle simple context", () => { @@ -72,7 +72,7 @@ describe("nano", () => { Nano.run ) expect(result).toEqual( - Either.right({ value: 42 }) + Result.succeed({ value: 42 }) ) }) @@ -85,7 +85,7 @@ describe("nano", () => { Nano.run ) expect(result).toEqual( - Either.left(new Nano.NanoDefectException("error", "")) + Result.fail(new Nano.NanoDefectException("error", "")) ) }) it("orElse", () => { @@ -93,6 +93,6 @@ describe("nano", () => { Nano.fail("error"), Nano.orElse((_) => Nano.succeed(42)) ) - expect(Nano.run(program)).toEqual(Either.right(42)) + expect(Nano.run(program)).toEqual(Result.succeed(42)) }) }) diff --git a/packages/language-service/test/layer-graph.test.ts b/packages/language-service/test/layer-graph.test.ts index 14b81805..5a70f8d4 100644 --- a/packages/language-service/test/layer-graph.test.ts +++ b/packages/language-service/test/layer-graph.test.ts @@ -6,8 +6,8 @@ import * as TypeCheckerUtils from "@effect/language-service/core/TypeCheckerUtil import * as TypeParser from "@effect/language-service/core/TypeParser" import * as TypeScriptApi from "@effect/language-service/core/TypeScriptApi" import * as TypeScriptUtils from "@effect/language-service/core/TypeScriptUtils" -import * as Either from "effect/Either" import { pipe } from "effect/Function" +import * as Result from "effect/Result" import * as fs from "fs" import * as path from "path" import * as ts from "typescript" @@ -93,17 +93,17 @@ async function testLayerGraphOnExample(fileName: string, sourceText: string) { Nano.unsafeRun ) - if (Either.isLeft(maybeGraph)) { + if (Result.isFailure(maybeGraph)) { await expect("no graph").toMatchFileSnapshot(baseSnapshotFilePath + ".output") return } - await expect(maybeGraph.right.layerGraph, "layerGraph").toMatchFileSnapshot(baseSnapshotFilePath + ".output") - await expect(maybeGraph.right.layerNestedGraph, "layerNestedGraph").toMatchFileSnapshot( + await expect(maybeGraph.success.layerGraph, "layerGraph").toMatchFileSnapshot(baseSnapshotFilePath + ".output") + await expect(maybeGraph.success.layerNestedGraph, "layerNestedGraph").toMatchFileSnapshot( baseSnapshotFilePath + ".nested" ) - await expect(maybeGraph.right.outlineGraph, "outlineGraph").toMatchFileSnapshot(baseSnapshotFilePath + ".outline") - await expect(maybeGraph.right.providersAndRequirers, "providersAndRequirers").toMatchFileSnapshot( + await expect(maybeGraph.success.outlineGraph, "outlineGraph").toMatchFileSnapshot(baseSnapshotFilePath + ".outline") + await expect(maybeGraph.success.providersAndRequirers, "providersAndRequirers").toMatchFileSnapshot( baseSnapshotFilePath + ".quickinfo" ) } diff --git a/packages/language-service/test/layerinfo.test.ts b/packages/language-service/test/layerinfo.test.ts index 49d5b070..9eb80eed 100644 --- a/packages/language-service/test/layerinfo.test.ts +++ b/packages/language-service/test/layerinfo.test.ts @@ -6,9 +6,8 @@ import * as TypeCheckerUtils from "@effect/language-service/core/TypeCheckerUtil import * as TypeParser from "@effect/language-service/core/TypeParser" import * as TypeScriptApi from "@effect/language-service/core/TypeScriptApi" import * as TypeScriptUtils from "@effect/language-service/core/TypeScriptUtils" -import * as Doc from "@effect/printer-ansi/AnsiDoc" -import * as Either from "effect/Either" import { pipe } from "effect/Function" +import * as Result from "effect/Result" import * as fs from "fs" import * as path from "path" import * as ts from "typescript" @@ -55,14 +54,14 @@ function testAllLayerInfoExamples() { Nano.unsafeRun ) - if (Either.isLeft(layersResult)) { + if (Result.isFailure(layersResult)) { it("should collect layers without error", () => { - expect(Either.isRight(layersResult)).toEqual(true) + expect(Result.isSuccess(layersResult)).toEqual(true) }) return } - const layerNames = layersResult.right.layers.map((l) => l.name) + const layerNames = layersResult.success.layers.map((l: { name: string }) => l.name) if (layerNames.length === 0) { it("has no layers to test", () => { @@ -109,12 +108,12 @@ async function testLayerInfoByName( Nano.run ) - if (Either.isLeft(result)) { - await expect(`// error: ${String(result.left)}`).toMatchFileSnapshot(snapshotFilePath) + if (Result.isFailure(result)) { + await expect(`// error: ${String(result.failure)}`).toMatchFileSnapshot(snapshotFilePath) return } - const layerInfoResult = result.right + const layerInfoResult = result.success if (!layerInfoResult) { await expect(`// error: layer "${layerName}" not found`).toMatchFileSnapshot(snapshotFilePath) return @@ -122,8 +121,7 @@ async function testLayerInfoByName( // Render the layer info with the file's directory as cwd (so paths are relative) const cwd = path.dirname(path.resolve(fileName)) - const doc = renderLayerInfo(layerInfoResult, cwd) - const rendered = Doc.render(doc, { style: "pretty" }) + const rendered = renderLayerInfo(layerInfoResult, cwd) await expect(rendered).toMatchFileSnapshot(snapshotFilePath) } diff --git a/packages/language-service/test/overview.test.ts b/packages/language-service/test/overview.test.ts index b9ba40ce..16dc0d20 100644 --- a/packages/language-service/test/overview.test.ts +++ b/packages/language-service/test/overview.test.ts @@ -5,9 +5,8 @@ import * as TypeCheckerUtils from "@effect/language-service/core/TypeCheckerUtil import * as TypeParser from "@effect/language-service/core/TypeParser" import * as TypeScriptApi from "@effect/language-service/core/TypeScriptApi" import * as TypeScriptUtils from "@effect/language-service/core/TypeScriptUtils" -import * as Doc from "@effect/printer-ansi/AnsiDoc" -import * as Either from "effect/Either" import { pipe } from "effect/Function" +import * as Result from "effect/Result" import * as fs from "fs" import * as path from "path" import * as ts from "typescript" @@ -38,19 +37,18 @@ async function testOverviewOnExample(fileName: string, sourceText: string) { Nano.unsafeRun ) - expect(Either.isRight(result), "should run with no error " + result).toEqual(true) + expect(Result.isSuccess(result), "should run with no error " + result).toEqual(true) - if (Either.isLeft(result)) { + if (Result.isFailure(result)) { await expect("// error running collectExportedItems").toMatchFileSnapshot(snapshotFilePath) return } - const items = result.right + const items = result.success // Render the overview with the file's directory as cwd (so paths are relative) const cwd = path.dirname(path.resolve(fileName)) - const doc = renderOverview({ ...items, totalFilesCount: 1 }, cwd) - const rendered = Doc.render(doc, { style: "pretty" }) + const rendered = renderOverview({ ...items, totalFilesCount: 1 }, cwd) await expect(rendered).toMatchFileSnapshot(snapshotFilePath) } diff --git a/packages/language-service/test/perf.ts b/packages/language-service/test/perf.ts index 63ec98db..452c2307 100644 --- a/packages/language-service/test/perf.ts +++ b/packages/language-service/test/perf.ts @@ -1,5 +1,5 @@ -import * as Either from "effect/Either" import { pipe } from "effect/Function" +import * as Result from "effect/Result" import * as fs from "fs" import * as path from "path" import * as ts from "typescript" @@ -53,7 +53,7 @@ function testAllDagnostics() { goto: false }), Nano.unsafeRun, - Either.getOrElse(() => "// no diagnostics") + Result.getOrElse(() => "// no diagnostics") ) const end = performance.now() if (i !== -1) totalTime += end - start diff --git a/packages/language-service/test/piping-flows.test.ts b/packages/language-service/test/piping-flows.test.ts index 8220fe38..46492062 100644 --- a/packages/language-service/test/piping-flows.test.ts +++ b/packages/language-service/test/piping-flows.test.ts @@ -5,8 +5,8 @@ import * as TypeCheckerUtils from "@effect/language-service/core/TypeCheckerUtil import * as TypeParser from "@effect/language-service/core/TypeParser" import * as TypeScriptApi from "@effect/language-service/core/TypeScriptApi" import * as TypeScriptUtils from "@effect/language-service/core/TypeScriptUtils" -import * as Either from "effect/Either" import { pipe } from "effect/Function" +import * as Result from "effect/Result" import * as fs from "fs" import * as path from "path" import * as ts from "typescript" @@ -109,8 +109,8 @@ function testPipingFlowsOnExample( }), Nano.unsafeRun, async (result) => { - expect(Either.isRight(result), "should run with no error " + result).toEqual(true) - await expect(Either.getOrElse(result, () => "// error")).toMatchFileSnapshot( + expect(Result.isSuccess(result), "should run with no error " + result).toEqual(true) + await expect(Result.getOrElse(result, () => "// error")).toMatchFileSnapshot( snapshotFilePath ) } diff --git a/packages/language-service/test/refactors.test.ts b/packages/language-service/test/refactors.test.ts index df3b8a76..652f3bf1 100644 --- a/packages/language-service/test/refactors.test.ts +++ b/packages/language-service/test/refactors.test.ts @@ -7,8 +7,8 @@ import * as TypeParser from "@effect/language-service/core/TypeParser" import * as TypeScriptApi from "@effect/language-service/core/TypeScriptApi" import * as TypeScriptUtils from "@effect/language-service/core/TypeScriptUtils" import { refactors } from "@effect/language-service/refactors" -import * as Either from "effect/Either" import { pipe } from "effect/Function" +import * as Result from "effect/Result" import * as fs from "fs" import * as path from "path" import * as ts from "typescript" @@ -92,13 +92,13 @@ function testRefactorOnExample( Nano.unsafeRun ) - if (!(Either.isRight(canApply) && canApply.right.length > 0)) { + if (!(Result.isSuccess(canApply) && canApply.success.length > 0)) { return expect(sourceText).toMatchFileSnapshot(snapshotFilePath) } // then get the actual edits to run it const applicableRefactor = pipe( - LSP.getEditsForRefactor([refactor], sourceFile, textRange, canApply.right[0].name), + LSP.getEditsForRefactor([refactor], sourceFile, textRange, canApply.success[0].name), TypeParser.nanoLayer, TypeCheckerUtils.nanoLayer, TypeScriptUtils.nanoLayer, @@ -120,7 +120,7 @@ function testRefactorOnExample( Nano.unsafeRun ) - if (Either.isLeft(applicableRefactor)) { + if (Result.isFailure(applicableRefactor)) { return expect(sourceText).toMatchFileSnapshot(snapshotFilePath) } @@ -137,7 +137,7 @@ function testRefactorOnExample( }, (changeTracker) => pipe( - applicableRefactor.right.apply, + applicableRefactor.success.apply, Nano.provideService(TypeScriptApi.ChangeTracker, changeTracker), Nano.unsafeRun ) diff --git a/packages/language-service/test/renames.test.ts b/packages/language-service/test/renames.test.ts index 3a05f3f2..2431514c 100644 --- a/packages/language-service/test/renames.test.ts +++ b/packages/language-service/test/renames.test.ts @@ -6,8 +6,8 @@ import * as TypeParser from "@effect/language-service/core/TypeParser" import * as TypeScriptApi from "@effect/language-service/core/TypeScriptApi" import * as TypeScriptUtils from "@effect/language-service/core/TypeScriptUtils" import * as keyStrings from "@effect/language-service/renames/keyStrings" -import * as Either from "effect/Either" import { pipe } from "effect/Function" +import * as Result from "effect/Result" import * as fs from "fs" import * as path from "path" import * as ts from "typescript" @@ -110,11 +110,11 @@ function testRenamesOnExample( Nano.unsafeRun ) - if (!(Either.isRight(canApply))) { + if (!(Result.isSuccess(canApply))) { return expect(sourceText).toMatchFileSnapshot(snapshotFilePath) } - const textChanges = (canApply.right || []).map((_) => ({ span: _.textSpan, newText: "NewText" })) + const textChanges = (canApply.success || []).map((_: ts.RenameLocation) => ({ span: _.textSpan, newText: "NewText" })) return expect(applyEdits([{ fileName, textChanges }], fileName, sourceFile.text)).toMatchFileSnapshot( snapshotFilePath diff --git a/packages/language-service/tsup.config.ts b/packages/language-service/tsup.config.ts index 03f3d9df..210f8f4d 100644 --- a/packages/language-service/tsup.config.ts +++ b/packages/language-service/tsup.config.ts @@ -1,9 +1,9 @@ import * as NodeFileSystem from "@effect/platform-node/NodeFileSystem" import * as NodePath from "@effect/platform-node/NodePath" -import * as FileSystem from "@effect/platform/FileSystem" -import * as Path from "@effect/platform/Path" import * as Effect from "effect/Effect" +import * as FileSystem from "effect/FileSystem" import * as Layer from "effect/Layer" +import * as Path from "effect/Path" import { defineConfig } from "tsup" export default defineConfig({ diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4eb34205..5279b7ac 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -121,31 +121,13 @@ importers: version: 1.1.0 effect: specifier: https://pkg.pr.new/Effect-TS/effect-smol/effect@2153bea - version: '@pkg.pr.new/Effect-TS/effect-smol/effect@2153bea' + version: '@pkg.pr.new/Effect-TS/effect-smol/effect/2153bea' packages/language-service: devDependencies: - '@effect/cli': - specifier: ^0.73.0 - version: 0.73.0(@effect/platform@0.94.1)(@effect/printer-ansi@0.47.0)(@effect/printer@0.47.0)(effect@3.19.14) - '@effect/experimental': - specifier: ^0.58.0 - version: 0.58.0(@effect/platform@0.94.1)(effect@3.19.14) - '@effect/platform': - specifier: 0.94.1 - version: 0.94.1(effect@3.19.14) '@effect/platform-node': - specifier: 0.104.0 - version: 0.104.0(@effect/cluster@0.56.1)(@effect/platform@0.94.1)(@effect/rpc@0.73.0)(@effect/sql@0.49.0)(effect@3.19.14) - '@effect/printer-ansi': - specifier: ^0.47.0 - version: 0.47.0(@effect/typeclass@0.38.0)(effect@3.19.14) - '@effect/rpc': - specifier: ^0.73.0 - version: 0.73.0(@effect/platform@0.94.1)(effect@3.19.14) - '@effect/sql': - specifier: ^0.49.0 - version: 0.49.0(@effect/experimental@0.58.0)(@effect/platform@0.94.1)(effect@3.19.14) + specifier: ^4.0.0-beta.12 + version: 4.0.0-beta.12(effect@4.0.0-beta.12)(ioredis@5.9.3) '@types/pako': specifier: ^2.0.4 version: 2.0.4 @@ -153,8 +135,8 @@ importers: specifier: ^8.52.0 version: 8.52.0(typescript@5.9.3) effect: - specifier: ^3.19.14 - version: 3.19.14 + specifier: ^4.0.0-beta.12 + version: 4.0.0-beta.12 pako: specifier: ^2.1.0 version: 2.1.0 @@ -557,23 +539,7 @@ packages: ini: 4.1.3 toml: 3.0.0 yaml: 2.8.2 - - /@effect/cluster@0.56.1(@effect/platform@0.94.1)(@effect/rpc@0.73.0)(@effect/sql@0.49.0)(@effect/workflow@0.16.0)(effect@3.19.14): - resolution: {integrity: sha512-gnrsH6kfrUjn+82j/bw1IR4yFqJqV8tc7xZvrbJPRgzANycc6K1hu3LMg548uYbUkTzD8YYyqrSatMO1mkQpzw==} - peerDependencies: - '@effect/platform': ^0.94.1 - '@effect/rpc': ^0.73.0 - '@effect/sql': ^0.49.0 - '@effect/workflow': ^0.16.0 - effect: ^3.19.14 - dependencies: - '@effect/platform': 0.94.1(effect@3.19.14) - '@effect/rpc': 0.73.0(@effect/platform@0.94.1)(effect@3.19.14) - '@effect/sql': 0.49.0(@effect/experimental@0.58.0)(@effect/platform@0.94.1)(effect@3.19.14) - '@effect/workflow': 0.16.0(@effect/experimental@0.58.0)(@effect/platform@0.94.1)(@effect/rpc@0.73.0)(effect@3.19.14) - effect: 3.19.14 - kubernetes-types: 1.30.0 - dev: true + dev: false /@effect/eslint-plugin@0.3.2: resolution: {integrity: sha512-c4Vs9t3r54A4Zpl+wo8+PGzZz3JWYsip41H+UrebRLjQ2Hk/ap63IeCgN/HWcYtxtyhRopjp7gW9nOQ2Snbl+g==} @@ -599,47 +565,34 @@ packages: '@effect/platform': 0.94.1(effect@3.19.14) effect: 3.19.14 uuid: 11.1.0 + dev: false - /@effect/platform-node-shared@0.57.0(@effect/cluster@0.56.1)(@effect/platform@0.94.1)(@effect/rpc@0.73.0)(@effect/sql@0.49.0)(effect@3.19.14): - resolution: {integrity: sha512-QXuvmLNlABCQLcTl+lN1YPhKosR6KqArPYjC2reU0fb5lroCo3YRb/aGpXIgLthHzQL8cLU5XMGA3Cu5hKY2Tw==} + /@effect/platform-node-shared@4.0.0-beta.12(effect@4.0.0-beta.12): + resolution: {integrity: sha512-GP6ebikFIVYhhzQE5AV+GGtt/hdpeGpp3YBqfg7MD/6rDdd9Z/k20U5Mpj0X3Izrb+eOmEm8kJ4Trjg3T0qbqw==} + engines: {node: '>=18.0.0'} peerDependencies: - '@effect/cluster': ^0.56.0 - '@effect/platform': ^0.94.0 - '@effect/rpc': ^0.73.0 - '@effect/sql': ^0.49.0 - effect: ^3.19.13 + effect: ^4.0.0-beta.12 dependencies: - '@effect/cluster': 0.56.1(@effect/platform@0.94.1)(@effect/rpc@0.73.0)(@effect/sql@0.49.0)(@effect/workflow@0.16.0)(effect@3.19.14) - '@effect/platform': 0.94.1(effect@3.19.14) - '@effect/rpc': 0.73.0(@effect/platform@0.94.1)(effect@3.19.14) - '@effect/sql': 0.49.0(@effect/experimental@0.58.0)(@effect/platform@0.94.1)(effect@3.19.14) - '@parcel/watcher': 2.5.4 - effect: 3.19.14 - multipasta: 0.2.7 + '@types/ws': 8.18.1 + effect: 4.0.0-beta.12 ws: 8.19.0 transitivePeerDependencies: - bufferutil - utf-8-validate dev: true - /@effect/platform-node@0.104.0(@effect/cluster@0.56.1)(@effect/platform@0.94.1)(@effect/rpc@0.73.0)(@effect/sql@0.49.0)(effect@3.19.14): - resolution: {integrity: sha512-2ZkUDDTxLD95ARdYIKBx4tdIIgqA3cwb3jlnVVBxmHUf0Pg5N2HdMuD0Q+CXQ7Q94FDwnLW3ZvaSfxDh6FvrNw==} + /@effect/platform-node@4.0.0-beta.12(effect@4.0.0-beta.12)(ioredis@5.9.3): + resolution: {integrity: sha512-vWrGNYjTmlSYh829uISO0xAREzsmAXL4ZvEQF1gw8nmHlzz9YV6FYPNtXoM5ZgVji+X/zGsygQAxQLCIE5SGQw==} + engines: {node: '>=18.0.0'} peerDependencies: - '@effect/cluster': ^0.56.0 - '@effect/platform': ^0.94.0 - '@effect/rpc': ^0.73.0 - '@effect/sql': ^0.49.0 - effect: ^3.19.13 - dependencies: - '@effect/cluster': 0.56.1(@effect/platform@0.94.1)(@effect/rpc@0.73.0)(@effect/sql@0.49.0)(@effect/workflow@0.16.0)(effect@3.19.14) - '@effect/platform': 0.94.1(effect@3.19.14) - '@effect/platform-node-shared': 0.57.0(@effect/cluster@0.56.1)(@effect/platform@0.94.1)(@effect/rpc@0.73.0)(@effect/sql@0.49.0)(effect@3.19.14) - '@effect/rpc': 0.73.0(@effect/platform@0.94.1)(effect@3.19.14) - '@effect/sql': 0.49.0(@effect/experimental@0.58.0)(@effect/platform@0.94.1)(effect@3.19.14) - effect: 3.19.14 - mime: 3.0.0 - undici: 7.18.2 - ws: 8.19.0 + effect: ^4.0.0-beta.12 + ioredis: ^5.7.0 + dependencies: + '@effect/platform-node-shared': 4.0.0-beta.12(effect@4.0.0-beta.12) + effect: 4.0.0-beta.12 + ioredis: 5.9.3 + mime: 4.1.0 + undici: 7.22.0 transitivePeerDependencies: - bufferutil - utf-8-validate @@ -654,6 +607,7 @@ packages: find-my-way-ts: 0.1.6 msgpackr: 1.11.8 multipasta: 0.2.7 + dev: false /@effect/printer-ansi@0.47.0(@effect/typeclass@0.38.0)(effect@3.19.14): resolution: {integrity: sha512-tDEQ9XJpXDNYoWMQJHFRMxKGmEOu6z32x3Kb8YLOV5nkauEKnKmWNs7NBp8iio/pqoJbaSwqDwUg9jXVquxfWQ==} @@ -664,6 +618,7 @@ packages: '@effect/printer': 0.47.0(@effect/typeclass@0.38.0)(effect@3.19.14) '@effect/typeclass': 0.38.0(effect@3.19.14) effect: 3.19.14 + dev: false /@effect/printer@0.47.0(@effect/typeclass@0.38.0)(effect@3.19.14): resolution: {integrity: sha512-VgR8e+YWWhMEAh9qFOjwiZ3OXluAbcVLIOtvp2S5di1nSrPOZxj78g8LE77JSvyfp5y5bS2gmFW+G7xD5uU+2Q==} @@ -673,6 +628,7 @@ packages: dependencies: '@effect/typeclass': 0.38.0(effect@3.19.14) effect: 3.19.14 + dev: false /@effect/rpc@0.73.0(@effect/platform@0.94.1)(effect@3.19.14): resolution: {integrity: sha512-iMPf6tTriz8sK0l5x4koFId8Hz5nFptHYg8WqyjHGIIVLTpZxuiSqhmXZG7FnAs5N2n6uCEws4wWGcIgXNUrFg==} @@ -683,6 +639,7 @@ packages: '@effect/platform': 0.94.1(effect@3.19.14) effect: 3.19.14 msgpackr: 1.11.8 + dev: false /@effect/sql@0.49.0(@effect/experimental@0.58.0)(@effect/platform@0.94.1)(effect@3.19.14): resolution: {integrity: sha512-9UEKR+z+MrI/qMAmSvb/RiD9KlgIazjZUCDSpwNgm0lEK9/Q6ExEyfziiYFVCPiptp52cBw8uBHRic8hHnwqXA==} @@ -695,6 +652,7 @@ packages: '@effect/platform': 0.94.1(effect@3.19.14) effect: 3.19.14 uuid: 11.1.0 + dev: false /@effect/typeclass@0.38.0(effect@3.19.14): resolution: {integrity: sha512-lMUcJTRtG8KXhXoczapZDxbLK5os7M6rn0zkvOgncJW++A0UyelZfMVMKdT5R+fgpZcsAU/1diaqw3uqLJwGxA==} @@ -702,20 +660,7 @@ packages: effect: ^3.19.0 dependencies: effect: 3.19.14 - - /@effect/workflow@0.16.0(@effect/experimental@0.58.0)(@effect/platform@0.94.1)(@effect/rpc@0.73.0)(effect@3.19.14): - resolution: {integrity: sha512-MiAdlxx3TixkgHdbw+Yf1Z3tHAAE0rOQga12kIydJqj05Fnod+W/I+kQGRMY/XWRg+QUsVxhmh1qTr7Ype6lrw==} - peerDependencies: - '@effect/experimental': ^0.58.0 - '@effect/platform': ^0.94.0 - '@effect/rpc': ^0.73.0 - effect: ^3.19.13 - dependencies: - '@effect/experimental': 0.58.0(@effect/platform@0.94.1)(effect@3.19.14) - '@effect/platform': 0.94.1(effect@3.19.14) - '@effect/rpc': 0.73.0(@effect/platform@0.94.1)(effect@3.19.14) - effect: 3.19.14 - dev: true + dev: false /@emnapi/core@1.8.1: resolution: {integrity: sha512-AvT9QFpxK0Zd8J0jopedNm+w/2fIzvtPKPjqyw9jwvBaReTTqPBk9Hixaz7KbjimP+QNz605/XnjFcDAL2pqBg==} @@ -1108,6 +1053,10 @@ packages: iconv-lite: 0.7.2 dev: true + /@ioredis/commands@1.5.0: + resolution: {integrity: sha512-eUgLqrMf8nJkZxT24JvVRrQya1vZkQh8BBeYNwGDqa5I0VUi8ACx7uFvAaLxintokpTenkK6DASvo/bvNbBGow==} + dev: true + /@isaacs/balanced-match@4.0.1: resolution: {integrity: sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==} engines: {node: 20 || >=22} @@ -1281,148 +1230,6 @@ packages: fastq: 1.20.1 dev: true - /@parcel/watcher-android-arm64@2.5.4: - resolution: {integrity: sha512-hoh0vx4v+b3BNI7Cjoy2/B0ARqcwVNrzN/n7DLq9ZB4I3lrsvhrkCViJyfTj/Qi5xM9YFiH4AmHGK6pgH1ss7g==} - engines: {node: '>= 10.0.0'} - cpu: [arm64] - os: [android] - requiresBuild: true - dev: true - optional: true - - /@parcel/watcher-darwin-arm64@2.5.4: - resolution: {integrity: sha512-kphKy377pZiWpAOyTgQYPE5/XEKVMaj6VUjKT5VkNyUJlr2qZAn8gIc7CPzx+kbhvqHDT9d7EqdOqRXT6vk0zw==} - engines: {node: '>= 10.0.0'} - cpu: [arm64] - os: [darwin] - requiresBuild: true - dev: true - optional: true - - /@parcel/watcher-darwin-x64@2.5.4: - resolution: {integrity: sha512-UKaQFhCtNJW1A9YyVz3Ju7ydf6QgrpNQfRZ35wNKUhTQ3dxJ/3MULXN5JN/0Z80V/KUBDGa3RZaKq1EQT2a2gg==} - engines: {node: '>= 10.0.0'} - cpu: [x64] - os: [darwin] - requiresBuild: true - dev: true - optional: true - - /@parcel/watcher-freebsd-x64@2.5.4: - resolution: {integrity: sha512-Dib0Wv3Ow/m2/ttvLdeI2DBXloO7t3Z0oCp4bAb2aqyqOjKPPGrg10pMJJAQ7tt8P4V2rwYwywkDhUia/FgS+Q==} - engines: {node: '>= 10.0.0'} - cpu: [x64] - os: [freebsd] - requiresBuild: true - dev: true - optional: true - - /@parcel/watcher-linux-arm-glibc@2.5.4: - resolution: {integrity: sha512-I5Vb769pdf7Q7Sf4KNy8Pogl/URRCKu9ImMmnVKYayhynuyGYMzuI4UOWnegQNa2sGpsPSbzDsqbHNMyeyPCgw==} - engines: {node: '>= 10.0.0'} - cpu: [arm] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /@parcel/watcher-linux-arm-musl@2.5.4: - resolution: {integrity: sha512-kGO8RPvVrcAotV4QcWh8kZuHr9bXi9a3bSZw7kFarYR0+fGliU7hd/zevhjw8fnvIKG3J9EO5G6sXNGCSNMYPQ==} - engines: {node: '>= 10.0.0'} - cpu: [arm] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /@parcel/watcher-linux-arm64-glibc@2.5.4: - resolution: {integrity: sha512-KU75aooXhqGFY2W5/p8DYYHt4hrjHZod8AhcGAmhzPn/etTa+lYCDB2b1sJy3sWJ8ahFVTdy+EbqSBvMx3iFlw==} - engines: {node: '>= 10.0.0'} - cpu: [arm64] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /@parcel/watcher-linux-arm64-musl@2.5.4: - resolution: {integrity: sha512-Qx8uNiIekVutnzbVdrgSanM+cbpDD3boB1f8vMtnuG5Zau4/bdDbXyKwIn0ToqFhIuob73bcxV9NwRm04/hzHQ==} - engines: {node: '>= 10.0.0'} - cpu: [arm64] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /@parcel/watcher-linux-x64-glibc@2.5.4: - resolution: {integrity: sha512-UYBQvhYmgAv61LNUn24qGQdjtycFBKSK3EXr72DbJqX9aaLbtCOO8+1SkKhD/GNiJ97ExgcHBrukcYhVjrnogA==} - engines: {node: '>= 10.0.0'} - cpu: [x64] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /@parcel/watcher-linux-x64-musl@2.5.4: - resolution: {integrity: sha512-YoRWCVgxv8akZrMhdyVi6/TyoeeMkQ0PGGOf2E4omODrvd1wxniXP+DBynKoHryStks7l+fDAMUBRzqNHrVOpg==} - engines: {node: '>= 10.0.0'} - cpu: [x64] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /@parcel/watcher-win32-arm64@2.5.4: - resolution: {integrity: sha512-iby+D/YNXWkiQNYcIhg8P5hSjzXEHaQrk2SLrWOUD7VeC4Ohu0WQvmV+HDJokZVJ2UjJ4AGXW3bx7Lls9Ln4TQ==} - engines: {node: '>= 10.0.0'} - cpu: [arm64] - os: [win32] - requiresBuild: true - dev: true - optional: true - - /@parcel/watcher-win32-ia32@2.5.4: - resolution: {integrity: sha512-vQN+KIReG0a2ZDpVv8cgddlf67J8hk1WfZMMP7sMeZmJRSmEax5xNDNWKdgqSe2brOKTQQAs3aCCUal2qBHAyg==} - engines: {node: '>= 10.0.0'} - cpu: [ia32] - os: [win32] - requiresBuild: true - dev: true - optional: true - - /@parcel/watcher-win32-x64@2.5.4: - resolution: {integrity: sha512-3A6efb6BOKwyw7yk9ro2vus2YTt2nvcd56AuzxdMiVOxL9umDyN5PKkKfZ/gZ9row41SjVmTVQNWQhaRRGpOKw==} - engines: {node: '>= 10.0.0'} - cpu: [x64] - os: [win32] - requiresBuild: true - dev: true - optional: true - - /@parcel/watcher@2.5.4: - resolution: {integrity: sha512-WYa2tUVV5HiArWPB3ydlOc4R2ivq0IDrlqhMi3l7mVsFEXNcTfxYFPIHXHXIh/ca/y/V5N4E1zecyxdIBjYnkQ==} - engines: {node: '>= 10.0.0'} - requiresBuild: true - dependencies: - detect-libc: 2.1.2 - is-glob: 4.0.3 - node-addon-api: 7.1.1 - picomatch: 4.0.3 - optionalDependencies: - '@parcel/watcher-android-arm64': 2.5.4 - '@parcel/watcher-darwin-arm64': 2.5.4 - '@parcel/watcher-darwin-x64': 2.5.4 - '@parcel/watcher-freebsd-x64': 2.5.4 - '@parcel/watcher-linux-arm-glibc': 2.5.4 - '@parcel/watcher-linux-arm-musl': 2.5.4 - '@parcel/watcher-linux-arm64-glibc': 2.5.4 - '@parcel/watcher-linux-arm64-musl': 2.5.4 - '@parcel/watcher-linux-x64-glibc': 2.5.4 - '@parcel/watcher-linux-x64-musl': 2.5.4 - '@parcel/watcher-win32-arm64': 2.5.4 - '@parcel/watcher-win32-ia32': 2.5.4 - '@parcel/watcher-win32-x64': 2.5.4 - dev: true - /@pkgjs/parseargs@0.11.0: resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} @@ -1828,6 +1635,12 @@ packages: resolution: {integrity: sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==} dev: true + /@types/ws@8.18.1: + resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==} + dependencies: + '@types/node': 25.0.6 + dev: true + /@types/yargs-parser@21.0.3: resolution: {integrity: sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==} dev: true @@ -2646,6 +2459,11 @@ packages: engines: {node: '>=0.8'} dev: true + /cluster-key-slot@1.1.2: + resolution: {integrity: sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==} + engines: {node: '>=0.10.0'} + dev: true + /color-convert@2.0.1: resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} engines: {node: '>=7.0.0'} @@ -2813,6 +2631,11 @@ packages: object-keys: 1.1.1 dev: true + /denque@2.1.0: + resolution: {integrity: sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==} + engines: {node: '>=0.10'} + dev: true + /dependency-tree@11.2.0: resolution: {integrity: sha512-+C1H3mXhcvMCeu5i2Jpg9dc0N29TWTuT6vJD7mHLAfVmAbo9zW8NlkvQ1tYd3PDMab0IRQM0ccoyX68EZtx9xw==} engines: {node: '>=18'} @@ -2835,6 +2658,7 @@ packages: resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} engines: {node: '>=8'} requiresBuild: true + optional: true /detective-amd@6.0.1: resolution: {integrity: sha512-TtyZ3OhwUoEEIhTFoc1C9IyJIud3y+xYkSRjmvCt65+ycQuc3VcBrPRTMWoO/AnuCyOB8T5gky+xf7Igxtjd3g==} @@ -2995,6 +2819,22 @@ packages: dependencies: '@standard-schema/spec': 1.1.0 fast-check: 3.23.2 + dev: false + + /effect@4.0.0-beta.12: + resolution: {integrity: sha512-sD06m119HGZeKHfuRc1waI4F9F8KwZHkR/kKGxHtKySXdCtiJiB3fjQYiW36Tx8UofME1+KF5DrRRyuNZAzHzw==} + 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 + dev: true /electron-to-chromium@1.5.267: resolution: {integrity: sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw==} @@ -3548,13 +3388,13 @@ packages: engines: {node: '>=8.0.0'} dependencies: pure-rand: 6.1.0 + dev: false /fast-check@4.5.3: resolution: {integrity: sha512-IE9csY7lnhxBnA8g/WI5eg/hygA6MGWJMSNfFRrBlXUciADEhS1EDB0SIsMSvzubzIlOBbVITSsypCsW717poA==} engines: {node: '>=12.17.0'} dependencies: pure-rand: 7.0.1 - dev: false /fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} @@ -4028,7 +3868,6 @@ packages: /ini@6.0.0: resolution: {integrity: sha512-IBTdIkzZNOpqm7q3dRqJvMaldXjDHWkEDfrwGEQTs5eaQMWV+djAhR+wahyNNMAa+qpbDUhBMVt4ZKNwpPm7xQ==} engines: {node: ^20.17.0 || >=22.9.0} - dev: false /internal-slot@1.1.0: resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==} @@ -4054,6 +3893,23 @@ packages: fp-ts: 2.16.11 dev: true + /ioredis@5.9.3: + resolution: {integrity: sha512-VI5tMCdeoxZWU5vjHWsiE/Su76JGhBvWF1MJnV9ZtGltHk9BmD48oDq8Tj8haZ85aceXZMxLNDQZRVo5QKNgXA==} + engines: {node: '>=12.22.0'} + dependencies: + '@ioredis/commands': 1.5.0 + cluster-key-slot: 1.1.2 + debug: 4.4.3 + denque: 2.1.0 + lodash.defaults: 4.2.0 + lodash.isarguments: 3.1.0 + redis-errors: 1.2.0 + redis-parser: 3.0.0 + standard-as-callback: 2.1.0 + transitivePeerDependencies: + - supports-color + dev: true + /is-alphabetical@1.0.4: resolution: {integrity: sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg==} dev: true @@ -4526,6 +4382,14 @@ packages: p-locate: 5.0.0 dev: true + /lodash.defaults@4.2.0: + resolution: {integrity: sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==} + dev: true + + /lodash.isarguments@3.1.0: + resolution: {integrity: sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==} + dev: true + /lodash.merge@4.6.2: resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} dev: true @@ -4652,9 +4516,9 @@ packages: picomatch: 2.3.1 dev: true - /mime@3.0.0: - resolution: {integrity: sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==} - engines: {node: '>=10.0.0'} + /mime@4.1.0: + resolution: {integrity: sha512-X5ju04+cAzsojXKes0B/S4tcYtFAJ6tTMuSPBEn9CPGlrWr8Fiw7qYeLT0XyH80HSoAoqWCaz+MWKh22P7G1cw==} + engines: {node: '>=16'} hasBin: true dev: true @@ -4781,10 +4645,6 @@ packages: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} dev: true - /node-addon-api@7.1.1: - resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==} - dev: true - /node-fetch@2.7.0: resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} engines: {node: 4.x || >=6.0.0} @@ -5250,10 +5110,10 @@ packages: /pure-rand@6.1.0: resolution: {integrity: sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==} + dev: false /pure-rand@7.0.1: resolution: {integrity: sha512-oTUZM/NAZS8p7ANR3SHh30kXB+zK2r2BPcEn/awJIbOvq82WoMN4p62AWWp3Hhw50G0xMsw1mhIBLqHw64EcNQ==} - dev: false /quansync@0.2.11: resolution: {integrity: sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA==} @@ -5335,6 +5195,18 @@ packages: tslib: 2.8.1 dev: true + /redis-errors@1.2.0: + resolution: {integrity: sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==} + engines: {node: '>=4'} + dev: true + + /redis-parser@3.0.0: + resolution: {integrity: sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==} + engines: {node: '>=4'} + dependencies: + redis-errors: 1.2.0 + dev: true + /reflect.getprototypeof@1.0.10: resolution: {integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==} engines: {node: '>= 0.4'} @@ -5699,6 +5571,10 @@ packages: resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} dev: true + /standard-as-callback@2.1.0: + resolution: {integrity: sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==} + dev: true + /std-env@3.10.0: resolution: {integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==} dev: true @@ -6122,6 +5998,11 @@ packages: engines: {node: '>=20.18.1'} dev: true + /undici@7.22.0: + resolution: {integrity: sha512-RqslV2Us5BrllB+JeiZnK4peryVTndy9Dnqq62S3yYRRTj0tFQCwEniUy2167skdGOy3vqRzEvl1Dm4sV2ReDg==} + engines: {node: '>=20.18.1'} + dev: true + /unist-util-stringify-position@2.0.3: resolution: {integrity: sha512-3faScn5I+hy9VleOq/qNbAd6pAx7iH5jYBMS9I1HgQVijz/4mv5Bvw5iw1sC/90CODiKo81G/ps8AJrISn687g==} dependencies: @@ -6184,11 +6065,11 @@ packages: /uuid@11.1.0: resolution: {integrity: sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==} hasBin: true + dev: false /uuid@13.0.0: resolution: {integrity: sha512-XQegIaBTVUjSHliKqcnFqYypAd4S+WCYt5NIeRs6w/UAry7z8Y9j5ZwRRL4kzq9U3sD6v+85er9FvkEaBpji2w==} hasBin: true - dev: false /validate-npm-package-license@3.0.4: resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==} @@ -6495,7 +6376,7 @@ packages: hasBin: true dev: true - '@pkg.pr.new/Effect-TS/effect-smol/effect@2153bea': + '@pkg.pr.new/Effect-TS/effect-smol/effect/2153bea': resolution: {tarball: https://pkg.pr.new/Effect-TS/effect-smol/effect@2153bea} name: effect version: 4.0.0