Compare commits
10
Commits
d864978ab3
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d95ce4ab9d | ||
|
|
b87fd8da52 | ||
|
|
2a2f1cb712 | ||
|
|
6189e6ff8a | ||
|
|
400bc48ccc | ||
|
|
35c8470e09 | ||
|
|
1463527870 | ||
|
|
a89f862f73 | ||
|
|
6e5fd5ba79 | ||
|
|
1b7678ad3d |
No files matched your search
+2
-1
@@ -1,2 +1,3 @@
|
|||||||
node_modules
|
node_modules/
|
||||||
|
dist/
|
||||||
repos/
|
repos/
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
# lsp-mcp
|
||||||
|
|
||||||
|
An MCP server that exposes one Language Server to agents as a headless editor.
|
||||||
|
Effect 4 (`effect/unstable/ai` for MCP, `effect/unstable/cli` for the CLI) plus
|
||||||
|
`vscode-languageserver-protocol` for LSP. TypeScript is bundled for Node 26 with
|
||||||
|
tsdown; source files can still run directly through Node's type stripping.
|
||||||
|
|
||||||
|
## Commands
|
||||||
|
|
||||||
|
- `pnpm tsc` — type-check (tsgo with the Effect language-service plugin)
|
||||||
|
- `pnpm build` — bundle the CLI to `dist/main.mjs` with tsdown
|
||||||
|
- `pnpm dev` — run the TypeScript source directly
|
||||||
|
- `pnpm test` — vitest; integration tests spawn `test/fixtures/server.ts`, a real LSP server
|
||||||
|
- `pnpm biome check --write --unsafe` — lint/format (run before committing; its rewrites are safe)
|
||||||
|
- `node dist/main.mjs --help` — the built CLI; smoke-test with `node dist/main.mjs --root <dir> -- <server> [args]`
|
||||||
|
- Commit with `jj commit -m "..."`, never `git`
|
||||||
|
|
||||||
|
The shell may have an HTTP proxy configured; when curling the `--http` transport locally, pass `--noproxy '*'`.
|
||||||
|
|
||||||
|
## Layout
|
||||||
|
|
||||||
|
```
|
||||||
|
src/Model.ts agent-facing Schemas (1-based positions); drives tool input/output schemas
|
||||||
|
src/Tools.ts Toolkit definition + handlers wiring + capability gating (supportedBy)
|
||||||
|
src/Resources.ts MCP resources (lsp://server, lsp://diagnostics[/{file}]) and notifications
|
||||||
|
src/Server.ts layer composition: LanguageServer → Documents+Diagnostics → transport → tools/resources
|
||||||
|
src/main.ts CLI entry (Command/Flag/Argument), logs to stderr, NodeRuntime.runMain
|
||||||
|
src/lsp/ the ONLY place vscode-* packages are used at runtime
|
||||||
|
LanguageServer.ts spawn + JSON-RPC + initialize/shutdown, supervised: restarts on crash per policy, reopens documents
|
||||||
|
request() waits for $/progress idle and retries ContentModified / a server that is being restarted
|
||||||
|
Documents.ts on-demand didOpen, LRU via ScopedCache, disk change detection → didChange+didSave, applyEdit
|
||||||
|
Diagnostics.ts publishDiagnostics accumulation; pull (textDocument/diagnostic) or settled push per doc version
|
||||||
|
Editor.ts the operations = MCP tool handlers; LSP results → Model values
|
||||||
|
test/fixtures/server.ts fixture language server (hover/definition/references/rename/symbols/TODO diagnostics)
|
||||||
|
test/fixtures/workspace.ts temp workspace layer running the fixture server
|
||||||
|
```
|
||||||
|
|
||||||
|
Rule: outside `src/lsp/`, `vscode-*` may only be imported as types.
|
||||||
|
|
||||||
|
`EFFECT.md` lists the Effect 4 API shapes already verified against `repos/effect` (renames from Effect 3, Schedule,
|
||||||
|
CLI, MCP, vitest). Read it before grepping the Effect source, and add to it when you verify something new.
|
||||||
|
|
||||||
|
## Design decisions worth knowing
|
||||||
|
|
||||||
|
- Positions handed to agents are 1-based; `Where` accepts `column` or a `symbol` name to find on the line.
|
||||||
|
- Client capabilities are deliberately narrow (no dynamic registration, no watched files, UTF-16 only) so
|
||||||
|
servers fall back to their own file watching. Settings come from `--settings` via `workspace/configuration`.
|
||||||
|
- Tools are registered after LSP `initialize` so unsupported ones are never offered
|
||||||
|
(`Tools.supportedBy`). `diagnostics` is always offered.
|
||||||
|
- Tool failures are `LspError | DocumentError` (declared `failure` schema), so their messages reach the agent.
|
||||||
|
- Optional fields in `Model` use a local `optional` helper that omits the key when encoding, because MCP
|
||||||
|
`structuredContent` must be JSON (no `undefined`).
|
||||||
|
- Diagnostics waiting: pull if the server supports it; otherwise wait for a publish for the doc's version
|
||||||
|
plus a 250ms quiet period, bounded by 5s, memoized per (uri, version).
|
||||||
|
- Tests that talk to the fixture server use `it.layer(..., { excludeTestServices: true })` so the real
|
||||||
|
clock is used (Stream.debounce / timeouts would hang under TestClock).
|
||||||
|
- Crashes: `--restart never|always|N` (default 3). A crash is the process exiting or closing the connection after a
|
||||||
|
successful initialize; a server that cannot initialize fails lsp-mcp immediately. Restarts back off from 500ms to 30s,
|
||||||
|
a crash more than a minute after the previous one starts a new streak, and the streak limit `N` ends restarting for good.
|
||||||
|
Requests in flight during a crash wait for the new process and retry; documents are re-`didOpen`ed with their current
|
||||||
|
text; the capabilities and tool set stay those of the first process. Each restart and the final give-up are logged and
|
||||||
|
sent to the MCP client as `notifications/message`. Once given up, every tool call fails with an `LspError` that says so.
|
||||||
|
|
||||||
|
## Status
|
||||||
|
|
||||||
|
Complete for the first release: 13 tools, 2 resources + 1 template, stdio and HTTP transports,
|
||||||
|
verified against the fixture server and `typescript-language-server`.
|
||||||
|
|
||||||
|
Ideas not done: inlay hints, type hierarchy, semantic tokens, partial results, workspace/didChangeWatchedFiles
|
||||||
|
when a server insists on it.
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
# Effect 4 notes for this repo
|
||||||
|
|
||||||
|
Effect `4.0.0-rc.115` (pinned in `package.json`; `repos/effect` is the same version, check
|
||||||
|
`repos/effect/packages/effect/package.json` if either is bumped). The API differs from Effect 3
|
||||||
|
in places, and the type stripper gives no hints, so agents kept re-verifying the same shapes.
|
||||||
|
This file records what has been verified against the source so the next agent does not have to.
|
||||||
|
|
||||||
|
When something is not listed here, grep the source, not `node_modules`:
|
||||||
|
`grep -n "^export const NAME" repos/effect/packages/effect/src/MODULE.ts` shows the signature
|
||||||
|
and implementation; `repos/effect/packages/effect/test/` has usage examples. Add what you learn.
|
||||||
|
|
||||||
|
## Renames from Effect 3 (the things that bite)
|
||||||
|
|
||||||
|
- `Effect.async` → `Effect.callback`; `Effect.catchAll` → `Effect.catch`; `Effect.catchAllCause` → `Effect.catchCause`.
|
||||||
|
- Every `unsafeX` is `xUnsafe`: `Deferred.doneUnsafe`, `Deferred.isDoneUnsafe`, `PubSub.publishUnsafe`,
|
||||||
|
`SubscriptionRef.getUnsafe`, `Latch.openUnsafe`, `Scope.makeUnsafe`.
|
||||||
|
- `Context.Tag` classes are `Context.Service<Self, Shape>()("id")`; layers are built with `Layer.effect(Tag, make)`.
|
||||||
|
- `Schema.Union([A, B])` and `Schema.Literals(["a", "b"])` take an array. Filters are `.check(Schema.isInt(),
|
||||||
|
Schema.isGreaterThanOrEqualTo(0))`, not `.pipe(Schema.int())`. `.annotate({ description, message })`.
|
||||||
|
`Schema.TaggedError<Self>()("Tag", fields)`; `Schema.NumberFromString`; `Schema.fromJsonString(Schema.Json)`.
|
||||||
|
- `Duration.isGreaterThan` / `isLessThan` (not `greaterThan`); `Duration.format(d)` prints `1s 500ms`.
|
||||||
|
- `Effect.timeout` / `timeoutOption` / `timeoutOrElse`; `Stream.runHead` returns an `Option`.
|
||||||
|
- `Effect.forkScoped(effect, { startImmediately?, uninterruptible? })`; `Effect.forkIn(scope)`; `Effect.forkDetach`.
|
||||||
|
- `Effect.tap` accepts an effect or a function; `Effect.when(condition)` returns an `Option`.
|
||||||
|
- `Effect.fn("name")(function* (...) {})` for traced generator functions (used everywhere in `src/lsp/`).
|
||||||
|
|
||||||
|
## Schedule (reshaped in v4)
|
||||||
|
|
||||||
|
- Constructors: `exponential(base, factor)`, `spaced`, `fixed`, `recurs(n)`, `forever`, `jittered`, `cron`.
|
||||||
|
- Limits: `Schedule.upTo({ times, duration })` (replaces `intersect(recurs(n))`); `Schedule.while(meta => boolean | Effect)`
|
||||||
|
replaces `whileInput` / `whileOutput` / `recurWhile`; `Schedule.modifyDelay(meta => Duration.Input)`; `Schedule.tap(meta => ...)`;
|
||||||
|
`Schedule.passthrough`. Combinators: `Schedule.max(a, b)` recurs while both do, with the slower delay (Effect 3
|
||||||
|
`intersect`); `Schedule.min` recurs while either does, with the faster delay (`union`); `Schedule.concat` runs one
|
||||||
|
then the other (`andThen`). There is no `resetAfter`: write a step with its own state instead (below).
|
||||||
|
- `Metadata` given to those callbacks: `{ input, output, duration, attempt, start, now, elapsed, elapsedSincePrevious }`.
|
||||||
|
`elapsedSincePrevious` is the time between consecutive steps (between crashes, for a restart policy).
|
||||||
|
- Custom schedules: `Schedule.fromStepWithMetadata(Effect.sync(() => (meta: InputMetadata<Input>) => ...))`, where the step
|
||||||
|
returns `Effect.succeed([output, delay])` to continue or `Cause.done(output)` to stop. `Effect.sync` runs once per `retry`,
|
||||||
|
so closure state (a crash streak) is per run. See `restarts` in `src/lsp/LanguageServer.ts`.
|
||||||
|
- `Effect.retry` takes `{ while, until, times, schedule }`, a `Schedule`, or a builder `($) => ...`. `Effect.retryOrElse(schedule,
|
||||||
|
(lastError, output) => ...)` runs the fallback once the schedule stops. The effect under retry/repeat sees
|
||||||
|
`Schedule.CurrentMetadata` as a service.
|
||||||
|
|
||||||
|
## Concurrency primitives (as expected, with v4 names)
|
||||||
|
|
||||||
|
- `Deferred.make<A, E>()`, `await`, `succeed`, `fail`, `done(exit)`, `isDone`; the `*Unsafe` variants are synchronous and return
|
||||||
|
`boolean` (whether this call completed it). Waiters of `Deferred.await` resume synchronously inside `doneUnsafe`.
|
||||||
|
- `PubSub.unbounded<A>({ replay? })`, `publish`, `publishUnsafe`, `subscribe`, `Stream.fromPubSub`, `Stream.fromSubscription`.
|
||||||
|
- `SubscriptionRef.make/get/set/update/changes`; `changes` emits the current value first (replay 1).
|
||||||
|
- `Latch.make(open?)` with `.open`, `.close`, `.await`, `.release`, `.whenOpen(effect)`.
|
||||||
|
- `Semaphore.make(n)`, `.withPermits(n)(effect)`; `FiberSet.makeRuntime<R>()` / `makeRuntimePromise` to run effects from callbacks.
|
||||||
|
- `ScopedCache.makeWith({ capacity, timeToLive: (exit) => Duration, lookup })`, `get`, `has`, `invalidate`, `keys`, `values`, `entries`.
|
||||||
|
- `Cause.done(value)` is the halt signal used by `Pull`, `Schedule` and `Stream` internals; `Cause.hasInterruptsOnly(cause)`.
|
||||||
|
|
||||||
|
## `effect/unstable/cli`
|
||||||
|
|
||||||
|
- `Command.make(name, { flag: Flag..., arg: Argument... }, Effect.fn(function* (config) {}))`, `Command.withDescription`,
|
||||||
|
`Command.withExamples([{ command, description }])`, `Command.run({ version })`.
|
||||||
|
- `Flag.String/Int/Boolean/Directory(name, { mustExist })/KeyValuePair/Literals(name, [...])`; `Flag.withSchema(schema)`
|
||||||
|
(decodes from the flag's type, so `Schema.NumberFromString` for a string flag), `withDescription`, `withDefault`, `withAlias`,
|
||||||
|
`withMetavar("never|always|N")`, `optional` (→ `Option`), `map`, `orElse`. `Argument.String`, `Argument.variadic`.
|
||||||
|
- Help does not print default values; put them in the description. Schema failures print `Invalid value for flag --x: "v".
|
||||||
|
Expected: Schema validation failed: <message annotation or issue>`.
|
||||||
|
|
||||||
|
## `effect/unstable/ai` MCP (see `src/Server.ts`, `src/Tools.ts`, `src/Resources.ts`)
|
||||||
|
|
||||||
|
- `McpServer.layerStdio(info)` / `layerHttp({ ...info, path })` served with `HttpRouter.serve`; `McpServer.registerToolkit(toolkit)`
|
||||||
|
after `Toolkit.toLayer(handlers)`; `McpServer.registerResource({ uri | template, name, description, mimeType, content, completion })`.
|
||||||
|
- `Tool.make(name, { description, parameters, success, failure, dependencies }).annotate(Tool.Readonly, ...)`.
|
||||||
|
- `McpServer.McpServer` service exposes `notifications["notifications/message" | "notifications/resources/updated"]`.
|
||||||
|
|
||||||
|
## `@effect/vitest`
|
||||||
|
|
||||||
|
- `it.effect` / `it.live` tests run inside a `Scope`; `it.layer(layer, { excludeTestServices: true })("name", (it) => ...)` shares the
|
||||||
|
layer across the block and, with that option, uses the real clock (`Stream.debounce` and timeouts hang under `TestClock`).
|
||||||
|
- Real processes and `Effect.sleep` need real time, so every fixture-server test uses `excludeTestServices` or `it.live`.
|
||||||
|
|
||||||
|
## `vscode-jsonrpc` (not Effect, but verified the same way)
|
||||||
|
|
||||||
|
- Loss of the peer: `connection.onClose` fires on transport close and on `dispose()`; pending requests reject with
|
||||||
|
`ResponseError` codes `ConnectionInactive` (-32096), `PendingResponseRejected` (-32097) or `MessageWriteError` (-32099).
|
||||||
|
- Writing to a dead child's stdin rejects a promise nobody handles (EPIPE crashes the process). `LanguageServer.ts` writes through
|
||||||
|
a `PassThrough` piped into stdin and listens for stdin errors instead.
|
||||||
|
- `connection.onRequest((method, params) => ...)` registers a catch-all ("star") handler; handlers registered per type are
|
||||||
|
consulted first.
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2026 EFLKumo
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
@@ -0,0 +1,127 @@
|
|||||||
|
# lsp-mcp
|
||||||
|
|
||||||
|
```sh
|
||||||
|
npx -y @opticlm/lsp-mcp
|
||||||
|
```
|
||||||
|
|
||||||
|
An [MCP](https://modelcontextprotocol.io/) server that exposes a single [Language Server Protocol](https://microsoft.github.io/language-server-protocol/) server to agents as a headless editor.
|
||||||
|
|
||||||
|
`lsp-mcp` starts the language server you provide, keeps its document state in sync with the workspace, and exposes the language server's capabilities through MCP tools and resources. It is written in TypeScript and [Effect](https://effect.website).
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- stdio and Streamable HTTP MCP transports
|
||||||
|
- Diagnostics exposed both as a tool and subscribable MCP resources
|
||||||
|
- Preview-by-default edits for rename and formatting, with explicit disk application
|
||||||
|
- Language-server crash supervision, restart backoff, and document reopening
|
||||||
|
- Initialization options, workspace settings, language-id mappings, and open-document capacity configuration
|
||||||
|
|
||||||
|
Underlying techniques:
|
||||||
|
|
||||||
|
- On-demand document opening, disk change detection, and an LRU cache of open documents
|
||||||
|
- 1-based line and column positions designed for agent-facing use
|
||||||
|
- Capability-aware tool registration: unsupported LSP features are not advertised
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
The general form is:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
npx -y @opticlm/lsp-mcp [lsp-mcp options] -- <language-server> [language-server args...]
|
||||||
|
```
|
||||||
|
|
||||||
|
The `--` separates `lsp-mcp` options from the executable and arguments passed to the language server.
|
||||||
|
|
||||||
|
### stdio
|
||||||
|
|
||||||
|
stdio is the default transport and is suitable for MCP clients that launch a local server process:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
npx -y @opticlm/lsp-mcp --root /path/to/project -- typescript-language-server --stdio
|
||||||
|
```
|
||||||
|
|
||||||
|
For example, an MCP client configuration can point at the checkout like this:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"mcpServers": {
|
||||||
|
"typescript": {
|
||||||
|
"command": "npx",
|
||||||
|
"args": [
|
||||||
|
"-y",
|
||||||
|
"@opticlm/lsp-mcp",
|
||||||
|
"--root",
|
||||||
|
"/path/to/project",
|
||||||
|
"--",
|
||||||
|
"typescript-language-server",
|
||||||
|
"--stdio"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Streamable HTTP
|
||||||
|
|
||||||
|
Run the MCP endpoint on a local port with `--http`:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
npx -y @opticlm/lsp-mcp --http 9010 --root ./crate -- rust-analyzer
|
||||||
|
```
|
||||||
|
|
||||||
|
The endpoint is available at:
|
||||||
|
|
||||||
|
```text
|
||||||
|
http://localhost:9010/mcp
|
||||||
|
```
|
||||||
|
|
||||||
|
### Configuration examples
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# Pass JSON initialization options to the language server.
|
||||||
|
npx -y @opticlm/lsp-mcp \
|
||||||
|
--init-options '{"typescript":{"useInferredProjectPerProjectRoot":true}}' \
|
||||||
|
-- typescript-language-server --stdio
|
||||||
|
|
||||||
|
# Serve workspace/configuration and didChangeConfiguration with JSON settings.
|
||||||
|
npx -y @opticlm/lsp-mcp \
|
||||||
|
--settings '{"gopls":{"staticcheck":true}}' \
|
||||||
|
-- gopls
|
||||||
|
|
||||||
|
# Add a file-extension to language-id mapping.
|
||||||
|
npx -y @opticlm/lsp-mcp --language vue=vue --root ./web -- vue-language-server --stdio
|
||||||
|
|
||||||
|
# Keep more documents open, and always restart a crashed language server.
|
||||||
|
npx -y @opticlm/lsp-mcp --open-documents 64 --restart always -- clangd
|
||||||
|
```
|
||||||
|
|
||||||
|
## Development
|
||||||
|
|
||||||
|
Requirements:
|
||||||
|
|
||||||
|
- Node.js 26+
|
||||||
|
- pnpm 12.4.2+
|
||||||
|
|
||||||
|
```sh
|
||||||
|
pnpm test # Run the Vitest suite, including the fixture LSP integration tests
|
||||||
|
pnpm check # Type-check without emitting JavaScript
|
||||||
|
pnpm lint # Format and lint with Biome
|
||||||
|
```
|
||||||
|
|
||||||
|
The main source layout is:
|
||||||
|
|
||||||
|
```text
|
||||||
|
src/Model.ts Agent-facing schemas and 1-based positions
|
||||||
|
src/Tools.ts MCP tool definitions and capability gating
|
||||||
|
src/Resources.ts MCP resources and notifications
|
||||||
|
src/Server.ts Effect layer composition and transports
|
||||||
|
src/main.ts CLI entry point
|
||||||
|
src/lsp/ LSP process, document, diagnostic, and editor bridge
|
||||||
|
test/ Unit and integration tests with a fixture language server
|
||||||
|
```
|
||||||
|
|
||||||
|
The `vscode-*` packages are intentionally confined to `src/lsp/`; the rest of the application uses the agent-facing schemas and Effect services.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
MIT
|
||||||
+83
@@ -0,0 +1,83 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
import{createRequire as e}from"node:module";import*as t from"node:child_process";import{spawn as n}from"node:child_process";import{Duplex as r,PassThrough as i,Readable as a}from"node:stream";import*as o from"node:http";import{createServer as s}from"node:http";import*as c from"node:crypto";import*as l from"node:fs";import*as u from"node:os";import*as d from"node:path";import*as f from"node:stream/promises";import{pipeline as p}from"node:stream/promises";import*as m from"node:zlib";import*as h from"node:url";import*as g from"node:readline";var _=Object.create,v=Object.defineProperty,ee=Object.getOwnPropertyDescriptor,y=Object.getOwnPropertyNames,te=Object.getPrototypeOf,b=Object.prototype.hasOwnProperty,ne=(e,t,n)=>()=>{if(n)throw n[0];try{return e&&(t=e(e=0)),t}catch(e){throw n=[e],e}},x=(e,t)=>()=>(t||(e((t={exports:{}}).exports,t),e=null),t.exports),re=(e,t)=>{let n={};for(var r in e)v(n,r,{get:e[r],enumerable:!0});return t||v(n,Symbol.toStringTag,{value:`Module`}),n},ie=(e,t,n,r)=>{if(t&&typeof t==`object`||typeof t==`function`)for(var i=y(t),a=0,o=i.length,s;a<o;a++)s=i[a],!b.call(e,s)&&s!==n&&v(e,s,{get:(e=>t[e]).bind(null,s),enumerable:!(r=ee(t,s))||r.enumerable});return e},ae=(e,t,n)=>(n=e==null?{}:_(te(e)),ie(t||!e||!e.__esModule||!b.call(e,`default`)?v(n,`default`,{value:e,enumerable:!0}):n,e)),oe=e=>b.call(e,`module.exports`)?e[`module.exports`]:ie(v({},`__esModule`,{value:!0}),e),se=/* @__PURE__ */ e(import.meta.url);const S=(e,t)=>{switch(t.length){case 0:return e;case 1:return t[0](e);case 2:return t[1](t[0](e));case 3:return t[2](t[1](t[0](e)));case 4:return t[3](t[2](t[1](t[0](e))));case 5:return t[4](t[3](t[2](t[1](t[0](e)))));case 6:return t[5](t[4](t[3](t[2](t[1](t[0](e))))));case 7:return t[6](t[5](t[4](t[3](t[2](t[1](t[0](e)))))));case 8:return t[7](t[6](t[5](t[4](t[3](t[2](t[1](t[0](e))))))));case 9:return t[8](t[7](t[6](t[5](t[4](t[3](t[2](t[1](t[0](e)))))))));default:{let n=e;for(let e=0,r=t.length;e<r;e++)n=t[e](n);return n}}},ce={pipe(){return S(this,arguments)}},le=/*#__PURE__*/ function(){function e(){}return e.prototype=ce,e}(),C=function(e,t){if(typeof e==`function`)return function(){return e(arguments)?t.apply(this,arguments):e=>t(e,...arguments)};switch(e){case 0:case 1:throw RangeError(`Invalid arity ${e}`);case 2:return function(e,n){return arguments.length>=2?t(e,n):function(n){return t(n,e)}};case 3:return function(e,n,r){return arguments.length>=3?t(e,n,r):function(r){return t(r,e,n)}};default:return function(){if(arguments.length>=e)return t.apply(this,arguments);let n=arguments;return function(e){return t(e,...n)}}}},w=e=>e,T=e=>()=>e,ue=/*#__PURE__*/ T(!0),de=/*#__PURE__*/ T(!1),fe=/*#__PURE__*/ T(void 0),pe=fe,me=/*#__PURE__*/ C(2,(e,t)=>n=>t(e(n)));function he(e,...t){return S(e,t)}function ge(e,t,n,r,i,a,o,s,c){switch(arguments.length){case 1:return e;case 2:return function(){return t(e.apply(this,arguments))};case 3:return function(){return n(t(e.apply(this,arguments)))};case 4:return function(){return r(n(t(e.apply(this,arguments))))};case 5:return function(){return i(r(n(t(e.apply(this,arguments)))))};case 6:return function(){return a(i(r(n(t(e.apply(this,arguments))))))};case 7:return function(){return o(a(i(r(n(t(e.apply(this,arguments)))))))};case 8:return function(){return s(o(a(i(r(n(t(e.apply(this,arguments))))))))};case 9:return function(){return c(s(o(a(i(r(n(t(e.apply(this,arguments)))))))))}}}function _e(e){let t=/* @__PURE__ */ new WeakMap;return n=>{let r=t.get(n);if(r!==void 0)return r;let i=e(n);return t.set(n,i),i}}function ve(e){let t=/* @__PURE__ */ new WeakMap;return n=>{let r=t.get(n);if(r!==void 0)return r;let i=e(n);return t.set(n,i),t.set(i,i),i}}const ye=e=>{let t=new Set(Reflect.ownKeys(e));if(e.constructor===Object)return t;e instanceof Error&&t.delete(`stack`);let n=Object.getPrototypeOf(e),r=n;for(;r!==null&&r!==Object.prototype;){let e=Reflect.ownKeys(r);for(let n=0;n<e.length;n++)t.add(e[n]);r=Object.getPrototypeOf(r)}return t.has(`constructor`)&&typeof e.constructor==`function`&&n===e.constructor.prototype&&t.delete(`constructor`),t},be=/*#__PURE__*/ new WeakSet;function xe(e){return typeof e==`string`}function Se(e){return typeof e==`number`}function Ce(e){return typeof e==`boolean`}function we(e){return typeof e==`bigint`}function Te(e){return typeof e==`symbol`}function Ee(e){return xe(e)||Se(e)||Te(e)}function De(e){return typeof e==`function`}function Oe(e){return e===void 0}function ke(e){return e!==void 0}function Ae(e){return e!==null}function je(e){return e==null}function Me(e){return e!=null}function Ne(e){return!1}function Pe(e){return!0}function Fe(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function Ie(e){return typeof e==`object`&&!!e||De(e)}const E=/*#__PURE__*/ C(2,(e,t)=>Ie(e)&&t in e),Le=/*#__PURE__*/ C(2,(e,t)=>E(e,`_tag`)&&e._tag===t);function Re(e){return e instanceof Error}function ze(e){return E(e,Symbol.iterator)||xe(e)}const Be=`~effect/Hash`,Ve=e=>{switch(typeof e){case`number`:return Ke(e);case`bi
Line truncated
|
||||||
|
`+a(n+1))}\n${a(n)}]`;else if(e instanceof Date)l=Mt(e);else if(!t?.ignoreToString&&E(e,`toString`)&&typeof e.toString==`function`&&e.toString!==Object.prototype.toString&&e.toString!==Array.prototype.toString){let t=Nt(e);l=e instanceof Error&&e.cause!==void 0?`${t} (cause: ${c(e.cause,n)})`:t}else if(Symbol.iterator in e)l=`${e.constructor.name}(${c(Array.from(e),n)})`;else{let t=s(e);if(!i||t.length<=1){let r=`{${t.map(t=>`${At(t)}:${c(Pt(e,t),n)}`).join(`,`)}}`;l=o(e,r)}else{let r=`{\n${t.map(t=>`${a(n+1)}${At(t)}: ${c(Pt(e,t),n+1)}`).join(`,
|
||||||
|
`)}\n${a(n)}}`;l=o(e,r)}}return r.delete(e),l}return String(e)}return c(e,0)}const kt=`[Circular]`;function At(e){return typeof e==`string`?JSON.stringify(e):String(e)}function jt(e){return e.map(e=>`[${At(e)}]`).join(``)}function Mt(e){try{return e.toISOString()}catch{return`Invalid Date`}}function Nt(e){try{let t=e.toString();return typeof t==`string`?t:String(t)}catch{return`[toString threw]`}}function Pt(e,t){try{return e[t]}catch{return`[property access threw]`}}function Ft(e,t){let n=[];return JSON.stringify(e,function(e,t){let r=Object.getOwnPropertyDescriptor(this,e)?.value,i=E(r,xt)?Ct(r):Ct(t);if(typeof i==`bigint`)return Ot(i);if(typeof i!=`object`||!i)return i;for(;n.length>0&&n[n.length-1]!==this;)n.pop();if(!n.includes(i))return n.push(i),i},t?.space)??`null`}const It=/*#__PURE__*/ Symbol.for(`nodejs.util.inspect.custom`),Lt=e=>{try{return e=Ct(e),E(e,`toJSON`)&&De(e.toJSON)&&e.toJSON.length===0?e.toJSON():Array.isArray(e)?e.map(Lt):e}catch{return`[toJSON threw]`}},Rt=(e,t=2)=>{if(typeof e==`string`)return e;try{return typeof e==`object`?Ft(e,{space:t}):Ot(e,{space:t})}catch{return String(e)}},zt={toJSON(){return Lt(this)},[It](){return this.toJSON()},toString(){return Ot(this.toJSON())}};var Bt=class{[It](){return this.toJSON()}toString(){return Ot(this.toJSON())}};const Vt=/*#__PURE__*/ (()=>{let e=Object.getOwnPropertyDescriptor(Error,`stackTraceLimit`);return e===void 0?Object.isExtensible(Error):Object.hasOwn(e,`writable`)?e.writable===!0:e.set!==void 0})(),Ht=()=>Error.stackTraceLimit,Ut=e=>{Vt&&(Error.stackTraceLimit=e)};var Wt=class e{called=!1;self;constructor(e){this.self=e}next(e){return this.called?{value:e,done:!0}:(this.called=!0,{value:this.self,done:!1})}[Symbol.iterator](){return new e(this.self)}};const Gt=/*#__PURE__*/ (()=>{let e=`~effect/Utils/internal`,t={[e]:e=>e()},n={[e]:e=>{try{return e()}finally{}}};return Ht()!==0&&t[e](()=>(/* @__PURE__ */ Error()).stack)?.includes(e)===!0?t[e]:n[e]})();function Kt(e,t,n){t===`__proto__`?Object.defineProperty(e,t,{value:n,writable:!0,enumerable:!0,configurable:!0}):e[t]=n}function qt(e,t){for(let n of Reflect.ownKeys(t))Object.prototype.propertyIsEnumerable.call(t,n)&&Kt(e,n,t[n])}const Jt=`~effect/Effect`,Yt=`~effect/Exit`,Xt={_A:w,_E:w,_R:w},Zt=`${Jt}/identifier`,Qt=`${Jt}/args`,$t=`${Jt}/evaluate`,en=`${Jt}/successCont`,tn=`${Jt}/failureCont`,nn=`${Jt}/ensureCont`,rn=/*#__PURE__*/ Symbol.for(`effect/Effect/Yield`),an={pipe(){return S(this,arguments)},toJSON(){return{...this}},toString(){return Ot(this.toJSON(),{ignoreToString:!0,space:2})},[It](){return this.toJSON()}},on={[Jt]:Xt,...an,[Symbol.iterator](){return new Wt(this)},toJSON(){return{_id:`Effect`,op:this[Zt],...Qt in this?{args:this[Qt]}:void 0}}},sn=e=>E(e,Jt),cn=e=>E(e,Yt),ln=`~effect/Cause`,un=`~effect/Cause/Reason`,dn=e=>E(e,ln),fn=e=>E(e,un);var pn=class{constructor(e){this[ln]=ln,this.reasons=e}pipe(){return S(this,arguments)}toJSON(){return{_id:`Cause`,failures:this.reasons.map(e=>e.toJSON())}}toString(){return`Cause(${Ot(this.reasons)})`}[It](){return this.toJSON()}[it](e){return dn(e)&&this.reasons.length===e.reasons.length&&this.reasons.every((t,n)=>at(t,e.reasons[n]))}[Be](){return Ze(this.reasons)}};const mn=/*#__PURE__*/ new WeakMap;var hn=class{[un];annotations;_tag;constructor(e,t,n){if(this[un]=un,this._tag=e,t!==gn&&typeof n==`object`&&n&&t.size>0){let e=mn.get(n);e&&(t=new Map([...e,...t])),mn.set(n,t)}this.annotations=t}annotate(e,t){if(e.mapUnsafe.size===0)return this;let n=new Map(this.annotations);e.mapUnsafe.forEach((e,r)=>{t?.overwrite!==!0&&n.has(r)||n.set(r,e)});let r=Object.assign(Object.create(Object.getPrototypeOf(this)),this);return r.annotations=n,r}pipe(){return S(this,arguments)}toString(){return Ot(this)}[It](){return this.toString()}};const gn=/*#__PURE__*/ new Map;var _n=class extends hn{constructor(e,t=gn){super(`Fail`,t,e),this.error=e}toString(){return`Fail(${Ot(this.error)})`}toJSON(){return{_tag:`Fail`,error:this.error}}[it](e){return wn(e)&&at(this.error,e.error)&&at(this.annotations,e.annotations)}[Be](){return Ue(qe(this._tag))(Ue(Ve(this.error))(Ve(this.annotations)))}};const vn=e=>new pn(e),yn=/*#__PURE__*/ new pn([]),bn=e=>new pn([new _n(e)]);var xn=class extends hn{constructor(e,t=gn){super(`Die`,t,e),this.defect=e}toString(){return`Die(${Ot(this.defect)})`}toJSON(){return{_tag:`Die`,defect:this.defect}}[it](e){return Tn(e)&&at(this.defect,e.defect)&&at(this.annotations,e.annotations)}[Be](){return Ue(qe(this._tag))(Ue(Ve(this.defect))(Ve(this.annotations)))}};const Sn=e=>new pn([new xn(e)]),Cn=/*#__PURE__*/ C(e=>dn(e[0]),(e,t,n)=>t.mapUnsafe.size===0?e:new pn(e.reasons.map(e=>e.annotate(t,n)))),wn=e=>e._tag===`Fail`,Tn=e=>e._tag===`Die`,En=e=>e._tag===`Interrupt`;function Dn(e){return In(`Effect.evaluate: Not implemented`)}const On=e=>({...on,[Zt]:e.op,[$t]:e[$t]??Dn,[en]:e[en],[tn]:e[tn],[nn]:e[nn]}),kn=e=>{let t=On(e),n=function(e){this[Qt]=e};return n.prototype=t,function(e){return new n(e)}},An=e=>{let t={[Yt]:Yt,_taLine truncated
|
||||||
|
/* v8 ignore next 2 */
|
||||||
|
_A:e=>e,_E:e=>e},...an,[Symbol.iterator](){return new Wt(this)}},gr=/*#__PURE__*/ Object.assign(/*#__PURE__*/ Object.create(hr),{_tag:`Success`,_op:`Success`,[it](e){return vr(e)&&br(e)&&at(this.success,e.success)},[Be](){return Ue(Ve(this._tag))(Ve(this.success))},toString(){return`success(${Ot(this.success)})`},toJSON(){return{_id:`Result`,_tag:this._tag,value:Lt(this.success)}}}),_r=/*#__PURE__*/ Object.assign(/*#__PURE__*/ Object.create(hr),{_tag:`Failure`,_op:`Failure`,[it](e){return vr(e)&&yr(e)&&at(this.failure,e.failure)},[Be](){return Ue(Ve(this._tag))(Ve(this.failure))},toString(){return`failure(${Ot(this.failure)})`},toJSON(){return{_id:`Result`,_tag:this._tag,failure:Lt(this.failure)}}}),vr=e=>E(e,mr),yr=e=>e._tag===`Failure`,br=e=>e._tag===`Success`,xr=function(e){this.failure=e};xr.prototype=_r;const Sr=e=>new xr(e),Cr=function(e){this.success=e};Cr.prototype=gr;const wr=e=>new Cr(e);function Tr(e){return(t,n)=>t===n?0:e(t,n)}const Er=/*#__PURE__*/ Tr((e,t)=>globalThis.Number.isNaN(e)&&globalThis.Number.isNaN(t)?0:globalThis.Number.isNaN(e)?-1:globalThis.Number.isNaN(t)?1:e<t?-1:1),Dr=/*#__PURE__*/ C(2,(e,t)=>Tr((n,r)=>e(t(n),t(r)))),Or=e=>C(2,(t,n)=>e(t,n)===-1),kr=e=>C(2,(t,n)=>e(t,n)===1),Ar=e=>C(2,(t,n)=>e(t,n)!==1),jr=e=>C(2,(t,n)=>e(t,n)!==-1),D=()=>dr,O=pr,Mr=cr,Nr=lr,Pr=ur,Fr=/*#__PURE__*/ C(2,(e,{onNone:t,onSome:n})=>Nr(e)?t():n(e.value)),Ir=/*#__PURE__*/ C(2,(e,t)=>Nr(e)?t():e.value),Lr=e=>e==null?D():O(e),Rr=e=>e===void 0?D():O(e),zr=/*#__PURE__*/ Ir(fe),Br=e=>(...t)=>{try{return O(e(...t))}catch{return D()}},Vr=/*#__PURE__*/ C(2,(e,t)=>Nr(e)?D():O(t(e.value))),Hr=/*#__PURE__*/ C(2,(e,t)=>Nr(e)?D():t(e.value)),Ur=/*#__PURE__*/ C(2,(e,t)=>Nr(e)?D():t(e.value)?O(e.value):D()),Wr=/*#__PURE__*/ C(2,(e,t)=>!Nr(e)&&t(e.value)),Gr=wr,Kr=Sr,qr=e=>{if(De(e))try{return Gr(e())}catch(e){return Kr(e)}try{return Gr(e.try())}catch(t){return Kr(e.catch(t))}},Jr=vr,Yr=yr,Xr=br,Zr=/*#__PURE__*/ C(2,(e,t)=>Yr(e)?Kr(t(e.failure)):e),Qr=/*#__PURE__*/ C(2,(e,t)=>Xr(e)?Gr(t(e.success)):e),$r=/*#__PURE__*/ C(2,(e,{onFailure:t,onSuccess:n})=>Yr(e)?t(e.failure):n(e.success)),ei=/*#__PURE__*/ (/* @__PURE__ */ C(2,(e,t)=>{if(Xr(e))return e.success;throw t(e.failure)}))(w),ti=/*#__PURE__*/ C(2,(e,t)=>Yr(e)?Kr(e.failure):t(e.success)),ni=$n,ri=e=>{let t=e[Symbol.iterator]().next();if(t.done)throw Error(`headUnsafe: empty iterable`);return t.value},ii=/*#__PURE__*/ C(2,(e,t)=>{let n=0;for(let r of e){let e=t(r,n);if(Ce(e)){if(e)return O(r)}else if(Pr(e))return e;n++}return D()}),ai={[Symbol.iterator](){return oi}},oi={next(){return{done:!0,value:void 0}}},si=()=>ai,ci=e=>Object.keys(e).length===0,li=/*#__PURE__*/ C(2,(e,t)=>Object.hasOwn(e,t)),ui=/*#__PURE__*/ C(2,(e,t)=>{let n={...e};for(let r of di(e))Kt(n,r,t(e[r],r));return n}),di=e=>Object.keys(e),fi=e=>C(2,(t,n)=>{for(let r of di(t))if(!li(n,r)||!e(t[r],n[r]))return!1;return!0}),pi=e=>{let t=fi(e);return(e,n)=>t(e,n)&&t(n,e)},mi=globalThis.Array,hi=e=>mi.isArray(e)?e:mi.from(e),gi=/*#__PURE__*/ C(2,(e,{onEmpty:t,onNonEmpty:n})=>yi(e)?n(e):t()),_i=/*#__PURE__*/ C(2,(e,t)=>[...e,t]);mi.isArray;const vi=tr,yi=tr;function bi(e,t){return!Number.isFinite(e)||e<0||e>=t.length}const xi=/*#__PURE__*/ C(2,(e,t)=>{let n=Math.floor(t);if(bi(n,e))throw Error(`Index out of bounds: ${n}`);return e[n]}),Si=e=>yi(e)?O(Ci(e)):D(),Ci=e=>e[e.length-1],wi=(e,t)=>Math.min(nr(e),t),Ti=/*#__PURE__*/ C(2,(e,t)=>{let n=hi(e);return n.slice(0,wi(t,n.length))}),Ei=/*#__PURE__*/ C(2,(e,t)=>{let n=0;for(let r of e){if(t(r,n))return O(n);n++}return D()}),Di=ii,Oi=/*#__PURE__*/ C(2,(e,t)=>{let n=hi(e);for(let e=n.length-1;e>=0;e--){let r=n[e],i=t(r,e);if(typeof i==`boolean`){if(i)return O(r)}else if(Pr(i))return i}return D()}),ki=e=>mi.from(e).reverse(),Ai=()=>[],ji=e=>[e],Mi=/*#__PURE__*/ C(2,(e,t)=>e.map(t)),Ni=/*#__PURE__*/ C(3,(e,t,n)=>hi(e).reduce((e,t,r)=>n(e,t,r),t)),Pi=er,Fi=e=>On({op:e.label,[$t]:e.evaluate}),Ii=`~effect/Context/Service`,Li=function(){function e(){}let t=e;Object.setPrototypeOf(t,Ri);let n=(e,n)=>(t.key=e,n?.defaultValue&&(t[Bi]=Bi,t.defaultValue=n.defaultValue),n?.make&&(t.make=n.make),n?.fiberCached&&zi.add(e),t);return arguments.length>0?n(arguments[0],arguments[1]):n},Ri={[Ii]:Ii,.../*#__PURE__*/ Fi({label:`Service`,evaluate(e){return jn(sa(e.context,this))}}),toJSON(){return{_id:`Service`,key:this.key}},of(e){return e},context(e){return ta(this,e)},use(e){return Ln(t=>e(sa(t.context,this)))},useSync(e){return Ln(t=>jn(e(sa(t.context,this))))}},zi=/*#__PURE__*/ new Set,Bi=`~effect/Context/Reference`,Vi=`~effect/Context`,Hi=(e,t,n,r)=>{let i=Object.create(Yi);return i.cacheRoot=e??i,i.base=t,i.overlay=n,i.depth=r,i._flat=void 0,i.baseHits=0,i},Ui=(e,t)=>{t&&(Ui(e,t.parent),e.set(t.key,t.value))},Wi=e=>{if(e._flat)return e._flat;if(!e.overlay)return e._flat=e.base;let t=new Map(e.base);return Ui(t,e.overlay),e._flat=t},Gi=(e,t)=>{let n=new Map(e.mapUnsafe);return t(n),Ji(n)},Ki=/*#__PURE__*/ Symbol(),qi=(e,t)=>{let n=e;for(let e=n.overlay;e;e=e.parent)if(e.keLine truncated
|
||||||
|
`);e.splice(1,3),t.stack=e.join(`
|
||||||
|
`)}return t},da=/*#__PURE__*/ C(2,(e,t)=>{let n=qi(e,t.key);return n===Ki?Qi(t)?O(la(t)):D():O(n)}),fa=/*#__PURE__*/ C(2,(e,t)=>e.mapUnsafe.size===0?t:t.mapUnsafe.size===0?e:Gi(e,e=>t.mapUnsafe.forEach((t,n)=>e.set(n,t)))),pa=(...e)=>{let t=/* @__PURE__ */ new Map;for(let n=0;n<e.length;n++)e[n].mapUnsafe.forEach((e,n)=>{t.set(n,e)});return Ji(t)},ma=Li,ha=`~effect/Duration`,ga=/*#__PURE__*/ BigInt(0),_a=/*#__PURE__*/ BigInt(1),va=/*#__PURE__*/ BigInt(2),ya=/*#__PURE__*/ BigInt(10),ba=/*#__PURE__*/ BigInt(24),xa=/*#__PURE__*/ BigInt(60),Sa=/*#__PURE__*/ BigInt(1e3),Ca=/*#__PURE__*/ BigInt(1e6),wa=e=>BigInt(e<0?Math.ceil(e-.5):Math.floor(e+.5)),Ta=e=>wa(e*1e6),Ea=(e,t)=>{let n=e.indexOf(`.`);if(n===-1)return BigInt(e)*t;let r=e[0]===`-`,i=e.slice(n+1),a=ya**BigInt(i.length),o=(BigInt(e.slice(+!!r,n))*a+BigInt(i))*t,s=o/a+(o%a*va>=a?_a:ga);return r?-s:s},Da=/^(-?\d+(?:\.\d+)?)\s+(nanos?|micros?|millis?|seconds?|minutes?|hours?|days?|weeks?)$/,Oa=e=>{switch(typeof e){case`number`:return Wa(e);case`bigint`:return Ua(e);case`string`:{if(e===`Infinity`)return Va;if(e===`-Infinity`)return Ha;let t=Da.exec(e);if(!t)break;let[n,r,i]=t;if(i===`nano`||i===`nanos`)return Ua(Ea(r,_a));if(i===`micro`||i===`micros`)return Ua(Ea(r,Sa));let a=Number(r);switch(i){case`milli`:case`millis`:return Wa(a);case`second`:case`seconds`:return Ga(a);case`minute`:case`minutes`:return Ka(a);case`hour`:case`hours`:return qa(a);case`day`:case`days`:return Ja(a);case`week`:case`weeks`:return Ya(a)}break}case`object`:{if(e===null)break;if(ha in e)return e;if(Array.isArray(e))return e.length!==2||!e.every(Se)?ka(e):Number.isNaN(e[0])||Number.isNaN(e[1])?Ba:e[0]===-1/0||e[1]===-1/0?Ha:e[0]===1/0||e[1]===1/0?Va:Pa(wa(e[0]*1e9+e[1]));let t=e,n=0;return t.weeks&&(n+=t.weeks*6048e5),t.days&&(n+=t.days*864e5),t.hours&&(n+=t.hours*36e5),t.minutes&&(n+=t.minutes*6e4),t.seconds&&(n+=t.seconds*1e3),t.milliseconds&&(n+=t.milliseconds),!t.microseconds&&!t.nanoseconds?Pa(n):Pa(wa(n*1e6+(t.microseconds??0)*1e3+(t.nanoseconds??0)))}}return ka(e)},ka=e=>{throw Error(`Invalid Input: ${e}`)},Aa={_tag:`Millis`,millis:0},ja={_tag:`Infinity`},Ma={_tag:`NegativeInfinity`},Na={[ha]:ha,[Be](){switch(this.value._tag){case`Millis`:{let e=this.value.millis*1e6;return Number.isFinite(e)?Ve(wa(e)):Ke(this.value.millis)}case`Nanos`:return Ve(this.value.nanos);default:return Ye(this.value)}},[it](e){return Fa(e)&&io(this,e)},toString(){switch(this.value._tag){case`Infinity`:return`Infinity`;case`NegativeInfinity`:return`-Infinity`;case`Nanos`:return`${this.value.nanos} nanos`;case`Millis`:return`${this.value.millis} millis`}},toJSON(){switch(this.value._tag){case`Millis`:return{_id:`Duration`,_tag:`Millis`,millis:this.value.millis};case`Nanos`:return{_id:`Duration`,_tag:`Nanos`,nanos:String(this.value.nanos)};case`Infinity`:return{_id:`Duration`,_tag:`Infinity`};case`NegativeInfinity`:return{_id:`Duration`,_tag:`NegativeInfinity`}}},[It](){return this.toJSON()},pipe(){return S(this,arguments)}},Pa=e=>{let t=Object.create(Na);return t.value=typeof e==`number`?isNaN(e)||e===0||Object.is(e,-0)?Aa:Number.isFinite(e)?Number.isInteger(e)?{_tag:`Millis`,millis:e}:{_tag:`Nanos`,nanos:Ta(e)}:e>0?ja:Ma:e===ga?Aa:{_tag:`Nanos`,nanos:e},t},Fa=e=>E(e,ha),Ia=e=>e.value._tag!==`Infinity`&&e.value._tag!==`NegativeInfinity`,La=e=>{switch(e.value._tag){case`Millis`:return e.value.millis===0;case`Nanos`:return e.value.nanos===ga;case`Infinity`:case`NegativeInfinity`:return!1}},Ra=e=>{switch(e.value._tag){case`Millis`:return e.value.millis<0;case`Nanos`:return e.value.nanos<ga;case`NegativeInfinity`:return!0;case`Infinity`:return!1}},za=e=>{switch(e.value._tag){case`Infinity`:case`NegativeInfinity`:return Va;case`Millis`:return e.value.millis<0?Pa(-e.value.millis):e;case`Nanos`:return e.value.nanos<ga?Pa(-e.value.nanos):e}},Ba=/*#__PURE__*/ Pa(0),Va=/*#__PURE__*/ Pa(1/0),Ha=/*#__PURE__*/ Pa(-1/0),Ua=e=>Pa(e),Wa=e=>Pa(e),Ga=e=>Pa(e*1e3),Ka=e=>Pa(e*6e4),qa=e=>Pa(e*36e5),Ja=e=>Pa(e*864e5),Ya=e=>Pa(e*6048e5),Xa=e=>$a(Oa(e),{onMillis:w,onNanos:e=>Number(e)/1e6,onInfinity:()=>1/0,onNegativeInfinity:()=>-1/0}),Za=e=>$a(Oa(e),{onMillis:e=>e/1e3,onNanos:e=>Number(e)/1e9,onInfinity:()=>1/0,onNegativeInfinity:()=>-1/0}),Qa=e=>{let t=Oa(e);switch(t.value._tag){case`Infinity`:case`NegativeInfinity`:throw Error(`Cannot convert infinite duration to nanos`);case`Nanos`:return t.value.nanos;case`Millis`:return Ta(t.value.millis)}},$a=/*#__PURE__*/ C(2,(e,t)=>{switch(e.value._tag){case`Millis`:return t.onMillis(e.value.millis);case`Nanos`:return t.onNanos(e.value.nanos);case`Infinity`:return t.onInfinity();case`NegativeInfinity`:return(t.onNegativeInfinity??t.onInfinity)()}}),eo=/*#__PURE__*/ C(3,(e,t,n)=>e.value._tag===`Infinity`||e.value._tag===`NegativeInfinity`||t.value._tag===`Infinity`||t.value._tag===`NegativeInfinity`?n.onInfinity(e,t):e.value._tag===`Millis`?t.value._tag===`Millis`?n.onMillis(e.value.millis,t.value.millis):n.onNanos(Qa(e),t.value.nanos):n.onNanos(e.value.nanos,Qa(t))),to=/*#__PURE__
Line truncated
|
||||||
|
`);if(i[e]!==void 0)return n=i[e].trim(),n}},ls=/*#__PURE__*/ cs(3);var us=class extends hn{constructor(e,t=gn){super(`Interrupt`,t,`Interrupted`),this.fiberId=e}toString(){return`Interrupt(${this.fiberId})`}toJSON(){return{_tag:`Interrupt`,fiberId:this.fiberId}}[it](e){return En(e)&&this.fiberId===e.fiberId&&this.annotations===e.annotations}[Be](){return Ue(qe(`${this._tag}:${this.fiberId}`))(He(this.annotations))}};const ds=e=>new us(e),fs=e=>new pn([new us(e)]),ps=e=>{let t=e.reasons.find(wn);return t?Gr(t):Kr(e)},ms=e=>{for(let t=0;t<e.reasons.length;t++){let n=e.reasons[t];if(n._tag===`Fail`)return Gr(n.error)}return Kr(e)},hs=e=>e.reasons.some(Tn),gs=e=>{let t=e.reasons.find(Tn);return t?Gr(t.defect):Kr(e)},_s=e=>e.reasons.some(En),vs=e=>{let t;for(let n=0;n<e.reasons.length;n++){let r=e.reasons[n];r._tag===`Interrupt`&&(t??=/* @__PURE__ */ new Set,r.fiberId!==void 0&&t.add(r.fiberId))}return t?Gr(t):Kr(e)},ys=e=>{let t=vs(e);return Yr(t)?bs:t.success},bs=/*#__PURE__*/ new Set,xs=e=>e.reasons.length>0&&e.reasons.every(En),Ss=(e,t)=>{let n=/* @__PURE__ */ new Map,r=[];for(let i of e.concat(t)){let e=Ve(i),t=n.get(e);if(t===void 0)n.set(e,[i]);else if(t.some(e=>at(e,i)))continue;else t.push(i);r.push(i)}return r},Cs=/*#__PURE__*/ C(2,(e,t)=>{if(e.reasons.length===0)return t;if(t.reasons.length===0)return e;let n=new pn(Ss(e.reasons,t.reasons));return at(e,n)?e:n}),ws=/*#__PURE__*/ C(2,(e,t)=>{let n=!1,r=e.reasons.map(e=>wn(e)?(n=!0,new _n(t(e.error),e.annotations)):e);return n?vn(r):e}),Ts=e=>{let t={Fail:[],Die:[],Interrupt:[]};for(let n=0;n<e.reasons.length;n++)t[e.reasons[n]._tag].push(e.reasons[n]);return t},Es=e=>{let t=Ts(e);return t.Fail.length>0?t.Fail[0].error:t.Die.length>0?t.Die[0].defect:t.Interrupt.length>0?new globalThis.Error(`All fibers interrupted without error`):new globalThis.Error(`Empty cause`)},Ds=(e,t)=>{let n=[],r=[];if(e.reasons.length===0)return n;let i=Ht();i!==0&&Ut(1);for(let i of e.reasons){if(i._tag===`Interrupt`){r.push(i);continue}n.push(Os(i._tag===`Die`?i.defect:i.error,i.annotations,t))}if(n.length===0){let e=/* @__PURE__ */ Error(`The fiber was interrupted by:`);e.name=`InterruptCause`,e.stack=Ns(e,r);let i=new globalThis.Error(`All fibers interrupted without error`,{cause:e});i.name=`InterruptError`,i.stack=`${i.name}: ${i.message}`,n.push(Os(i,r[0].annotations,t))}return i!==0&&Ut(i),n},Os=(e,t,n)=>{let r=typeof e,i;if(e&&r===`object`){if(i=new globalThis.Error(ks(e),{cause:e.cause?Os(e.cause):void 0}),typeof e.name==`string`&&(i.name=e.name),typeof e.stack==`string`)i.stack=js(e.stack,i,t);else{let e=`${i.name}: ${i.message}`;i.stack=t?Ms(e,t):e}n?.includeCauseInStack&&(i.stack=Is(i));for(let t of Object.keys(e))t in i||(i[t]=e[t])}else i=new globalThis.Error(e?r===`string`?e:Ft(e):`Unknown error: ${e}`);return i},ks=e=>{if(typeof e.message==`string`)return e.message;if(typeof e.toString==`function`&&e.toString!==Object.prototype.toString&&e.toString!==Array.prototype.toString)try{return e.toString()}catch{}return Ft(e)},As=/\((.*)\)/g,js=(e,t,n)=>{let r=`${t.name}: ${t.message}`,i=(e.startsWith(r)?e.slice(r.length):e).split(`
|
||||||
|
`),a=[r];for(let e=1;e<i.length&&!/(?:Generator\.next|~effect\/Effect)/.test(i[e]);e++)a.push(i[e]);return n?Ms(a.join(`
|
||||||
|
`),n):a.join(`
|
||||||
|
`)},Ms=(e,t)=>{let n=t?.get(Mn.key);return n&&(e=`${e}\n${Ps(n)}`),e},Ns=(e,t)=>{let n=[`${e.name}: ${e.message}`];for(let e of t){let t=e.fiberId===void 0?`unknown`:`#${e.fiberId}`,r=e.annotations.get(Nn.key);n.push(` at fiber (${t})`),r&&n.push(Ps(r))}return n.join(`
|
||||||
|
`)},Ps=e=>{let t=[],n=e,r=0;for(;n&&r<10;){let e=n.stack();if(e){let r=e.matchAll(As),i=!1;for(let[,e]of r)i=!0,t.push(` at ${n.name} (${e})`);i||t.push(` at ${n.name} (${e.replace(/^at /,``)})`)}else t.push(` at ${n.name}`);n=n.parent,r++}return t.join(`
|
||||||
|
`)},Fs=e=>Ds(e).map(Is).join(`
|
||||||
|
`),Is=e=>e.cause?`${e.stack} {\n${Ls(e.cause,` `)}\n}`:e.stack,Ls=(e,t)=>{let n=e.stack.split(`
|
||||||
|
`),r=`${t}[cause]: ${n[0]}`;for(let e=1,i=n.length;e<i;e++)r+=`\n${t}${n[e]}`;return e.cause&&(r+=` {\n${Ls(e.cause,`${t} `)}\n${t}}`),r},Rs=`~effect/Fiber`,zs={_A:w,_E:w},Bs={id:0},Vs=()=>globalThis[Tt];var Hs=class{constructor(e,t=!0){this[Rs]=zs,this.setContext(e),this.id=++Bs.id,this.currentOpCount=0,this.interruptible=t,this._stack=[],this._observers=void 0,this._exit=void 0,this._children=void 0,this._interruptedCause=void 0,this._yielded=void 0,this._running=!1,this._deferredInterrupt=!1,this._parent=void 0,this.cache.runtimeMetrics?.recordFiberStart(this.context)}[Rs];id;interruptible;currentOpCount;_stack;_observers;_exit;_children;_interruptedCause;_yielded;_running;_deferredInterrupt;_parent;context;cache;_dispatcher=void 0;get currentDispatcher(){return this._dispatcher??=this.cache.scheduler.makeDispatcher()}getRef(e){return sa(this.context,e)}addObserver(e){return this._exit?(e(this._exit),pe):(this._observers===void 0?this._observers=[e]:this._observers.push(e),()=>{if(this._exit||this._observers===void 0)return;let t=this._observers.indexOf(e);t>=0&&this._observers.splice(t,1)})}interruptUnsafe(e,t){if(this._exit)return;let n=fs(e);this.cache.stackFrame&&(n=Cn(n,ta(Mn,this.cache.stackFrame))),t&&(n=Cn(n,t)),this._interruptedCause=this._interruptedCause?Cs(this._interruptedCause,n):n,this.interruptible&&(this._running?this._deferredInterrupt=!0:this.evaluate(ec(this._interruptedCause)))}pollUnsafe(){return this._exit}evaluate(e){if(this._exit)return;if(this._yielded!==void 0){let e=this._yielded;this._yielded=void 0,e()}let t=this.runLoop(e);if(t===rn)return;let n=Gs.interruptChildren&&Gs.interruptChildren(this);if(n!==void 0)return this.evaluate(Gc(n,()=>t));if(this._exit=t,this.cache.runtimeMetrics?.recordFiberEnd(this.context,this._exit),this._parent&&=(this._parent._children?.delete(this),void 0),this._observers!==void 0){let e=this._observers;this._observers=void 0;for(let n=0;n<e.length;n++)e[n](t)}this._stack.length=0,this._children=void 0,this.context=$i()}runLoop(e){let t=globalThis[Tt];globalThis[Tt]=this;let n=this._running;this._running=!0;let r=!1,i=e;this.currentOpCount=0;try{for(;;){this._deferredInterrupt&&(this._deferredInterrupt=!1,i=ec(this._interruptedCause)),this.currentOpCount++;let e=this.cache;if(!r&&!e.preventYield&&e.scheduler.shouldYield(this)){r=!0;let e=i;i=Gc(oc,()=>e)}if(i=e.tracerContext?e.tracerContext(i,this):i[$t](this),i===rn){let e=this._yielded;if(Yt in e)return this._deferredInterrupt=!1,this._yielded=void 0,e;if(this._deferredInterrupt){this._yielded=void 0,e();continue}return rn}}}catch(e){return E(i,$t)?this.runLoop(In(e)):In(`Fiber.runLoop: Not a valid effect: ${String(i)}`)}finally{this._running=n,globalThis[Tt]=t}}getCont(e){if(this._deferredInterrupt)return this._deferredInterrupt=!1,Ws;for(;;){let t=this._stack.pop();if(!t)return;let n=t[nn];if(n!==void 0){let r=n.call(t,this);if(r)return r[e]=r,r}if(t[e])return t}}yieldWith(e){return this._yielded=e,rn}children(){return this._children??=/* @__PURE__ */ new Set}pipe(){return S(this,arguments)}setContext(e){let t=this.context;if(this.context=e,t!==void 0&&Xi(t,e))return;let n=e.cacheRoot,r=n._fiberCache??=Us(e);this.cache!==void 0&&this.cache.scheduler!==r.scheduler&&(this._dispatcher=void 0),this.cache=r}get currentSpanLocal(){let e=this.cache.span;return e?._tag===`Span`?e:void 0}};const Us=e=>{let t=aa(e,qo);return{scheduler:sa(e,uo),tracer:t,tracerContext:t?t.context:void 0,tracerEnabled:sa(e,$o),span:aa(e,Bo),logLevel:sa(e,is),minimumLogLevel:sa(e,as),stackFrame:sa(e,Qo),runtimeMetrics:aa(e,`effect/Metric/FiberRuntimeMetrics`),maxOpsBeforeYield:sa(e,vo),preventYield:sa(e,yo)}},Ws={[en](e,t){return ec(t._interruptedCause)},[tn](e,t){return ec(t._interruptedCause)}},Gs={interruptChildren:void 0},Ks=e=>{if(!e.cache.stackFrame)return;let t=/* @__PURE__ */ new Map;return t.set(Nn.key,e.cache.stackFrame),Ji(t)},qs=e=>{if(e._children!==void 0&&e._children.size!==0)return $s(e._children)},Js=e=>{let t=e;return t._exit?A(t._exit):_c(n=>t._exit?n(A(t._exit)):nc(e.addObserver(e=>n(A(e)))))},Ys=e=>_c(t=>{let n=e[Symbol.iterator](),r=[],i;function a(){let e=n.next();for(;!e.done;){if(e.value._exit){r.push(e.value._exit),e=n.next();continue}i=e.value.addObserver(e=>{r.push(e),a()});return}t(A(r))}return a(),nc(()=>i?.())}),Xs=e=>{let t=e;return t._exit?t._exit:_c(n=>t._exit?n(t._exit):nc(e.addObserver(n)))},Zs=e=>Ln(t=>Qs(e,t.id)),Qs=/*#__PURE__*/ C(e=>E(e[0],Rs),(e,t,n)=>Ln(r=>{let i=Ks(r);return i=i&&n?fa(i,n):i??n,e.interruptUnsafe(t,i),Vc(Js(e))})),$s=e=>Ln(t=>{let n=Ks(t),r=Ai();for(let i of e)i.interruptUnsafe(t.id,n),r.push(i);return Vc(Ys(r))}),A=jn,ec=Pn,tc=Fn,nc=/*#__PURE__*/ kn({op:`Sync`,[$t](e){let t=this[Qt](),n=e.getCont(en);return n?n[en](t,e):e.yieldWith(jn(t))}}),rc=/*#__PURE__*/ kn({op:`Suspend`,[$t](e){return this[Qt]()}}),ic=/*#__PURE__*/ C(e=>e.length>=2||Mr(e[0]),(e,t)=>Nr(e)?tc(t?t():new Un(`Effect.fromOption: Option.none`)):A(e.value)),ac=/*#__PURE__*/ $r({onFailure:tc,onSuccess:
Line truncated
|
||||||
|
`);for(n&&=(o===0&&(e=1,o=a.indexOf(`
|
||||||
|
`,e)),!1);r!==-1||o!==-1;)r===-1||o!==-1&&o<r?(i(a.substring(e,o)),e=o+1,o=a.indexOf(`
|
||||||
|
`,e)):(i(a.substring(e,r)),a.length===r+1?(n=!0,e=a.length,r=-1):(e=r+(o===r+1?2:1),r=a.indexOf(`\r`,e),o=a.indexOf(`
|
||||||
|
`,e)));t+=a.substring(e)}}return yi(r)?r:null}let a=M(()=>r._tag===`Some`?Wp(r.value):bm(e,{onSuccess:o,onFailure:Qm,onDone:e=>{if(r=O(e),t.length>0){let e=t;return t=``,n=!1,j([e])}return Wp(e)}}));function o(e){let t=i(e);return t===null?a:j(t)}return a})),Dy=/*#__PURE__*/ C(2,(e,t)=>Gv((n,r)=>I(Yv(e)(n,r),e=>Yv(t)(e,r)))),Oy=/*#__PURE__*/ C(2,(e,t)=>Gv((n,r)=>I(Yv(e)(n,r),e=>{let n=vh(e,e=>hm(e)?Qm(e):eh(Up(e)));return L(Yv(t)(n,r),e=>yh(e,e=>Hp(e)?Qm(e.value):eh(e)))}))),ky=e=>Gv((t,n)=>{let r;return j(M(()=>r||e.pipe(Bf(n),I(e=>Yv(e)(t,n)),I(e=>r=e))))}),Ay=/*#__PURE__*/ C(2,(e,t)=>Kv(e,e=>j(mm(e,e=>I(t,()=>Wp(e)))))),jy=(e,t,n)=>M(()=>{let r=zf(),i=Yv(e)(Wp(),r);return mm(I(i,t),n||j).pipe(eg(e=>Wf(r,e)))}),My=/*#__PURE__*/ C(2,(e,t)=>Gv((n,r)=>L(Hh(Yv(e)(n,r),t),Hh(t)))),Ny=/*#__PURE__*/ C(2,(e,t)=>jy(e,e=>cg(I(e,t),{disableYield:!0}))),Py=e=>M(()=>{let t=D();return jy(e,e=>e.pipe(ph,I(e=>(t=e,Wp()))),()=>j(t))}),Fy=/*#__PURE__*/ C(3,(e,t,n)=>M(()=>{let r=t();return jy(e,e=>Km({while:ue,body:()=>e,step:e=>{r=n(r,e)}}),()=>j(r))})),Iy=(e,t)=>Yv(e)(Wp(),t),Ly={_R:w,_E:w,_A:w},Ry=function(e){this.channel=e};Ry.prototype={"~effect/Stream":Ly,pipe(){return S(this,arguments)}};const zy=e=>new Ry(e),By=`~effect/Sink`,Vy=/*#__PURE__*/ j([void 0]),Hy={_A:w,_In:w,_L:w,_E:w,_R:w},Uy={[By]:Hy,pipe(){return S(this,arguments)}},Wy=e=>E(e,By),Gy=e=>Ky((t,n)=>Yv(e)(t,n).pipe(I(cg({disableYield:!0})),mm(j))),Ky=e=>{let t=Object.create(Uy);return t.transform=e,t},qy=e=>Gv((t,n)=>j(I(e.transform(t,n),Wp))),Jy=/*#__PURE__*/ Ky(e=>mm(cg(e,{disableYield:!0}),()=>Vy)),Yy=/*#__PURE__*/ ((e,t)=>Ky(n=>{let r=e();return n.pipe(I(e=>(r=t(r,e),P)),cg({disableYield:!0}),mm(()=>j([r])))}))(D,(e,t)=>Si(t)),Xy=()=>Yy,Zy=e=>Qy(Gm(t=>e(t),{discard:!0})),Qy=e=>Ky(t=>t.pipe(I(e),cg({disableYield:!0}),mm(()=>Vy))),$y=e=>Gy(ky(L(e,qy))),eb=`~effect/MutableHashMap`,tb={[eb]:eb,[Symbol.iterator](){return this.backing[Symbol.iterator]()},toString(){return`MutableHashMap(${Ot(Array.from(this))})`},toJSON(){return{_id:`MutableHashMap`,values:Lt(Array.from(this))}},[It](){return this.toJSON()},pipe(){return S(this,arguments)}},nb=()=>{let e=Object.create(tb);return e.backing=/* @__PURE__ */ new Map,e.buckets=/* @__PURE__ */ new Map,e},rb=/*#__PURE__*/ C(2,(e,t)=>{if(e.backing.has(t))return O(e.backing.get(t));if(ab(t))return D();let n=ib.get(e);if(n!==void 0)return e.backing.has(n)?O(e.backing.get(n)):D();let r=Ve(t),i=e.buckets.get(r);return i===void 0?D():sb(e,i,t)}),ib=/*#__PURE__*/ new WeakMap,ab=e=>typeof e!=`object`&&typeof e!=`function`,ob=e=>e.backing.values(),sb=(e,t,n)=>{for(let r=0,i=t.length;r<i;r++)if(at(n,t[r])){let i=t[r];return ib.set(n,i),O(e.backing.get(i))}return D()},cb=/*#__PURE__*/ C(3,(e,t,n)=>{if(e.backing.has(t)||ab(t))return e.backing.set(t,n),e;let r=ib.get(e);if(r!==void 0&&e.backing.has(r))return e.backing.set(r,n),e;let i=Ve(t),a=e.buckets.get(i);return a===void 0?(e.buckets.set(i,[t]),e.backing.set(t,n),e):(r=lb(a,t),r===void 0&&(a.push(t),r=t),e.backing.set(r,n),e)}),lb=(e,t)=>{for(let n=0,r=e.length;n<r;n++)if(at(t,e[n]))return ib.set(t,e[n]),e[n]},ub=/*#__PURE__*/ C(2,(e,t)=>{if(ab(t))return e.backing.delete(t),e;let n=ib.get(e)??t,r=Ve(n),i=e.buckets.get(r);if(i===void 0)return e;for(let t=0,r=i.length;t<r;t++){let r=i[t];if(r===n||at(n,r)){e.backing.delete(r),i.splice(t,1);break}}return i.length===0&&e.buckets.delete(r),e}),db=e=>(e.backing.clear(),e.buckets.clear(),e),fb=e=>e.backing.size,pb=`~effect/RcMap`,mb=e=>({[pb]:pb,lookup:e.lookup,context:e.context,scope:e.scope,idleTimeToLive:e.idleTimeToLive,capacity:e.capacity,state:{_tag:`Open`,map:nb()},pipe(){return S(this,arguments)}}),hb=e=>rh(t=>{let n=t.context,r=sa(n,Lf),i=mb({lookup:e.lookup,context:n,scope:r,idleTimeToLive:typeof e.idleTimeToLive==`function`?ge(e.idleTimeToLive,Oa):T(Oa(e.idleTimeToLive??Ba)),capacity:Math.max(e.capacity??1/0,0)});return fh(Vf(r,()=>{if(i.state._tag===`Closed`)return P;let e=i.state.map;return i.state={_tag:`Closed`},Gm(e,([,e])=>dh(_b(e))).pipe(ch(()=>N(()=>{db(e)})))}),i)}),gb=/*#__PURE__*/ C(2,(e,t)=>og(n=>{if(e.state._tag===`Closed`)return ng;let r=e.state,i=m_(),a=rb(r.map,t),o;if(a._tag===`Some`)o=a.value,o.refCount++;else if(Number.isFinite(e.capacity)&&fb(e.state.map)>=e.capacity)return F(new Kp(`RcMap attempted to exceed capacity of ${e.capacity}`));else{o={deferred:df(),scope:zf(),idleTimeToLive:e.idleTimeToLive(t),finalizer:void 0,fiber:void 0,expiresAt:0,refCount:1},o.finalizer=vb(e,t,o),cb(r.map,t,o);let n=new Map(e.context.mapUnsafe);i.context.mapUnsafe.forEach((e,t)=>{n.set(t,e)}),n.set(Lf.key,o.scope),M(()=>e.lookup(t)).pipe(yg(Ji(n)),h_(o.scope)).addObserver(e=>xf(o.deferred,e))}let s=oa(i.context,Lf);return Hf(s,o.finalizer).pipe(sh(n(pf(o.deferred))))})),_b=e=>e.fiber?f_(e.fiber).pipe(sh(Wf(e.scope,kf))):Wf(e.scope,kf),vb=(e,t,n)=>rh(r=>{if(n.refCount--,n.refCount>0)return P;if(e.state._tag===`Closed`)return _b(n);let i=rb(e.state.map,t);if(i._tag===`None`||i.value!==n)return _b(n);if(La(n.idleTimeToL
Line truncated
|
||||||
|
`);n=uS(e)??aS(Ox(e.ast),e);break;default:n=tS(e)}return t?`${n}\n at ${t}`:n}function uS(e){if(e._tag===`Pointer`)return;if(e._tag===`Encoding`)return uS(e.issue);let t=(e._tag===`Filter`?e.filter.annotations:`annotations`in e?e.annotations:e.ast.annotations)?.[e._tag===`MissingKey`?`messageMissingKey`:e._tag===`UnexpectedKey`?`messageUnexpectedKey`:`message`];if(typeof t==`string`)return t}function dS(e){let t;for(let n of e.reasons){if(!wp(n)||!Lx(n.error))return;t??=n.error}return t}function fS(e,t){let n=dS(e);if(n===void 0)throw Error(t,{cause:e});return n}const pS=tm,mS=nm,hS=am,gS=sm,_S=cm,vS=dm,yS=class extends le{run;constructor(e){super(),this.run=e}map(e){return new yS((t,n)=>this.run(t,n).pipe(Ng(Vr(e))))}compose(e){return wS(this)?e:wS(e)?this:new yS((t,n)=>this.run(t,n).pipe(Ig(t=>e.run(t,n))))}};function bS(e){return new yS((t,n)=>F(e(t,n)))}function xS(e){return bS((t,n)=>{let r={message:e(t)};return Pr(t)?new Yx(r,t.value,n):new Yx(r)})}const SS=/*#__PURE__*/ xS(()=>`Encoding is not supported`),CS=/*#__PURE__*/ new yS(j);function wS(e){return e.run===CS.run}function TS(){return CS}function ES(e){return new yS((t,n)=>Nr(t)?Jm:e(t.value,n))}function DS(e){return kS(Vr(e))}function OS(e){return ES((t,n)=>e(t,n).pipe(Ng(O)))}function kS(e){return new yS(t=>j(e(t)))}function AS(){return new yS(()=>Jm)}function jS(e){return new yS(t=>{let n=Ur(t,ke);return Pr(n)?j(n):Ng(e,O)})}function MS(){return DS(globalThis.String)}function NS(){return DS(globalThis.Number)}function PS(){return DS(globalThis.BigInt)}function FS(e){return ES((t,n)=>th({try:()=>O(JSON.parse(t,e?.reviver)),catch:()=>new qx({expected:`a valid JSON string`},t,n)}))}function IS(e){return ES((t,n)=>th({try:()=>{let n=JSON.stringify(t,e?.replacer,e?.space);if(n===void 0)throw TypeError(`Value cannot be represented as JSON`);return O(n)},catch:()=>new qx({expected:`a JSON-serializable value`},t,n)}))}function LS(){return DS(Do)}function RS(){return OS((e,t)=>Pg(ih(Oo(e)),()=>new qx({expected:`a valid Base64 string`},e,t)))}const zS=`~effect/BigDecimal`,BS={[zS]:zS,[Be](){let e=JS(this);return Ue(Ve(e.value),Ke(e.scale))},[it](e){return VS(e)&&nC(this,e)},toString(){return`BigDecimal(${rC(this)})`},toJSON(){return{_id:`BigDecimal`,value:String(this.value),scale:this.scale}},[It](){return this.toJSON()},pipe(){return S(this,arguments)}},VS=e=>E(e,zS),HS=(e,t)=>{if(!Number.isSafeInteger(t))throw RangeError(`Scale must be a safe integer, got ${t}`);let n=Object.create(BS);return n.value=e,n.scale=t,n},US=(e,t)=>{if(e!==WS&&e%KS===WS)throw RangeError(`Value must be normalized`);let n=HS(e,t);return n.normalized=n,n},WS=/*#__PURE__*/ BigInt(0),GS=/*#__PURE__*/ BigInt(1),KS=/*#__PURE__*/ BigInt(10),qS=/*#__PURE__*/ US(WS,0),JS=e=>{if(e.normalized===void 0){if(e.value===WS)e.normalized=qS;else{let t=`${e.value}`,n=0;for(let e=t.length-1;e>=0&&t[e]===`0`;e--)n++;n===0&&(e.normalized=e);let r=BigInt(t.substring(0,t.length-n)),i=e.scale-n;e.normalized=US(r,i)}}return e.normalized},YS=[GS],XS=(e,t)=>e===t?0:e<t?-1:1,ZS=(e,t)=>{let n=`${e.value<WS?-e.value:e.value}`,r=`${t.value<WS?-t.value:t.value}`,i=BigInt(n.length-r.length)-BigInt(e.scale)+BigInt(t.scale);if(i!==WS)return i<WS?-1:1;let a=Math.max(n.length,r.length);for(let e=0;e<a;e++){let t=e<n.length?n.charCodeAt(e):48,i=e<r.length?r.charCodeAt(e):48;if(t!==i)return t<i?-1:1}return 0},QS=(e,t)=>{if(e.scale===t.scale)return XS(e.value,t.value);let n=$S(e),r=$S(t);if(n!==r)return n<r?-1:1;if(n===0)return 0;let i=e.scale-t.scale,a=Math.abs(i);if(a>100)return n===-1?ZS(t,e):ZS(e,t);let o=YS[a]??=KS**BigInt(a);return i>0?XS(e.value,t.value*o):XS(e.value*o,t.value)},$S=e=>e.value===WS?0:e.value<WS?-1:1,eC=e=>e.value<WS?HS(-e.value,e.scale):e,tC=/*#__PURE__*/ Xn((e,t)=>QS(e,t)===0),nC=/*#__PURE__*/ C(2,(e,t)=>tC(e,t)),rC=e=>{let t=JS(e);if(Math.abs(t.scale)>=16)return iC(t);let n=t.value<WS,r=n?`${t.value}`.substring(1):`${t.value}`,i,a;if(t.scale>=r.length)i=`0`,a=`0`.repeat(t.scale-r.length)+r;else{let e=r.length-t.scale;if(e>r.length){let t=e-r.length;i=`${r}${`0`.repeat(t)}`,a=``}else a=r.slice(e),i=r.slice(0,e)}let o=a===``?i:`${i}.${a}`;return n?`-${o}`:o},iC=e=>{if(aC(e))return`0e+0`;let t=JS(e),n=`${eC(t).value}`,r=n.slice(0,1),i=n.slice(1),a=`${oC(t)?`-`:``}${r}`;i!==``&&(a+=`.${i}`);let o=i.length-t.scale;return`${a}e${o>=0?`+`:``}${o}`},aC=e=>e.value===WS,oC=e=>e.value<WS,sC=`~effect/SchemaTransformation/Transformation`,cC=class{[sC]=sC;_tag=`Transformation`;decode;encode;constructor(e,t){this.decode=e,this.encode=t}flip(){return new cC(this.encode,this.decode)}compose(e){return new cC(this.decode.compose(e.decode),e.encode.compose(this.encode))}};function lC(e){return E(e,sC)&&e[sC]===sC}const uC=e=>lC(e)?e:new cC(e.decode,e.encode);function dC(e){return new cC(OS(e.decode),OS(e.encode))}function fC(e){return new cC(DS(e.decode),DS(e.encode))}const pC=/*#__PURE__*/ new cC(/*#__PURE__*/ TS(),/*#__PURE__*/ TS());function mC(){return pC}const hC=/*#__PURE__*/ new cC(/*#__PURE__*/ NS(),/
Line truncated
|
||||||
|
`]),wE={x:3,u:5},TE=(e,t)=>{if(t.length===0)return{tokens:[],hasLeadingWhitespace:!1,hasTrailingWhitespace:!1};let n=CE.has(t[0]),r=[],i=0;for(let n=0,a=0;n<e.length;n+=1,a+=1){let o=t[a];if(CE.has(o))i!==n&&r.push(e.slice(i,n)),i=n+1;else if(o===`\\`){let e=t[a+1];if(e===`
|
||||||
|
`)--n,a+=1;else if(e===`u`&&t[a+2]===`{`){let e=t.indexOf(`}`,a+3);parseInt(t.slice(a+3,e),16)>65535&&(n+=1),a=e}else a+=wE[e]??1}}let a=i===e.length;return a||r.push(e.slice(i)),{tokens:r,hasLeadingWhitespace:n,hasTrailingWhitespace:a}},EE=(e,t,n)=>n||e.length===0||t.length===0?[...e,...t]:[...e.slice(0,-1),`${e.at(-1)}${t.at(0)}`,...t.slice(1)],DE=(e,t,n)=>{let r=e.detached??n!==`win32`;return{...t,detached:r,shell:e.shell,windowsHide:e.windowsHide??!r}},OE=(e,t)=>(n,[r])=>{let i=`Unknown`;switch(n.code){case`ENOENT`:i=`NotFound`;break;case`EACCES`:i=`PermissionDenied`;break;case`EEXIST`:i=`AlreadyExists`;break;case`EISDIR`:i=`BadResource`;break;case`ENOTDIR`:i=`BadResource`;break;case`EBUSY`:i=`Busy`;break;case`ELOOP`:i=`BadResource`}return l_({_tag:i,module:e,method:t,pathOrDescriptor:r,syscall:n.syscall,cause:n})},kE=e=>Gy(ly(AE(e),e=>[e])),AE=e=>Gv(t=>{let n=e.evaluate();return j(jE({...e,writable:n,pull:t}))}),jE=e=>e.pull.pipe(I(t=>{let n=0;return Ym(r=>{let i=!1,a=()=>{for(;n<t.length;){if(i)return;if(!e.writable.write(t[n++],e.encoding)){i||e.writable.once(`drain`,a);return}}i||r(P)};return a(),N(()=>{i=!0,e.writable.off(`drain`,a)})})}),cg({disableYield:!0}),e.endOnDone===!1?w:mm(t=>`closed`in e.writable&&e.writable.closed?Wp(t):Ym(n=>{let r=()=>n(Wp(t));return e.writable.once(`finish`,r),e.writable.end(),N(()=>{e.writable.off(`finish`,r)})})),Nh(Ym(t=>{let n=n=>t(F(e.onError(n)));return e.writable.once(`error`,n),N(()=>{e.writable.off(`error`,n)})}))),ME=e=>kb(NE(e)),NE=e=>Gv((t,n)=>RE({scope:n,readable:e.evaluate(),onError:e.onError??zE,chunkSize:e.chunkSize,closeOnDone:e.closeOnDone})),PE=e=>Gv((t,n)=>{let r=e.evaluate(),i=b_(void 0),a=g_(!1);return jE({pull:t,writable:r,onError:e.onError??zE,endOnDone:e.endOnDone,encoding:e.encoding}).pipe(vh(e=>hm(e)?P:(i.current=Tf(e),a.openUnsafe(),P)),hg(n),I(()=>RE({scope:n,exit:i,latch:a,readable:r,onError:e.onError??zE,chunkSize:e.chunkSize})))}),FE=/*#__PURE__*/ C(2,(e,t)=>tx(e,PE(t))),IE=(e,t)=>{let n=BE(t?.maxBytes),r=t?.onError??zE;return Ym(t=>{let i=e(),a=[],o=0;return i.once(`error`,e=>{`closed`in i&&!i.closed&&i.destroy(),t(F(r(e)))}),i.once(`end`,()=>{let e=a.length===1?a[0]:Buffer.concat(a);if(e.byteOffset===0&&e.buffer.byteLength===e.byteLength)return t(j(e.buffer));t(j(e.buffer.slice(e.byteOffset,e.byteOffset+e.byteLength)))}),i.on(`data`,e=>{a.push(e),o+=e.length,n!==void 0&&o>n&&(`closed`in i&&!i.closed&&i.destroy(),t(F(r(/* @__PURE__ */ Error(`maxBytes exceeded`)))))}),N(()=>{`closed`in i&&!i.closed&&i.destroy()})})},LE=(e,t)=>L(IE(e,t),e=>new Uint8Array(e)),RE=e=>{let t=e.readable,n=e.closeOnDone??!0,r=e.exit??b_(void 0),i=e.latch??g_(!1);function a(){i.openUnsafe()}function o(t){r.current=Ef(e.onError(t)),i.openUnsafe()}function s(){r.current=Ef(Up()),i.openUnsafe()}t.on(`readable`,a),t.once(`error`,o),t.once(`end`,s);let c=M(function n(){let a=e.readable.read(e.chunkSize);if(a===null)return r.current?r.current:t.readableEnded?F(Up()):(i.closeUnsafe(),I(i.await,n));let o=ji(a);for(;a=e.readable.read(e.chunkSize),a!==null;)o.push(a);return j(o)});return fh(Hf(e.scope,N(()=>{t.off(`readable`,a),t.off(`error`,o),t.off(`end`,s),n&&`closed`in e.readable&&!e.readable.closed&&e.readable.destroy()})),c)},zE=e=>new qp(e),BE=e=>e===void 0||e===1/0?void 0:Number(t_(e)),VE=e=>e instanceof globalThis.Error?e:new globalThis.Error(String(e)),HE=(e,t,n)=>{let{commands:r}=qE(n),i=r.reduce((e,t)=>{let n=`${t.command} ${t.args.join(` `)}`;return e.length===0?n:`${e} | ${n}`},``);return OE(`ChildProcess`,e)(t,[i])},UE=1e3,WE=(e,t)=>{if(!yf(t))return!0;if(globalThis.process.platform===`win32`)return!1;try{return globalThis.process.kill(-e.pid,0),!0}catch{return!1}},GE=(e,n=()=>{})=>t.execFile(`taskkill`,[`/pid`,String(e.pid),`/T`,`/F`],{windowsHide:!0},n),KE=/*#__PURE__*/ cp(fE,/* @__PURE__ */ Zm(function*(){let e=yield*px,n=yield*vx,r=z(function*(t){if(!Oe(t.cwd))return yield*e.access(t.cwd),n.resolve(t.cwd)}),a=e=>e.extendEnv?{...globalThis.process.env,...e.env}:e.env,o=e=>Ob(e)?`pipe`:e,s=e=>Wy(e)?`pipe`:e,c=e=>{let t={stream:`pipe`,encoding:`utf-8`,endOnDone:!0};return Oe(e.stdin)?t:typeof e.stdin==`string`||Ob(e.stdin)?{...t,stream:e.stdin}:{stream:e.stdin.stream,encoding:e.stdin.encoding??t.encoding,endOnDone:e.stdin.endOnDone??t.endOnDone}},l=(e,t)=>{let n=e[t];return Oe(n)?{stream:`pipe`}:typeof n==`string`||Wy(n)?{stream:n}:{stream:n.stream}},u=e=>{if(Oe(e.additionalFds))return[];let t=[];for(let[n,r]of Object.entries(e.additionalFds)){let e=vE(n);ke(e)&&t.push({fd:e,config:r})}return t.sort((e,t)=>e.fd-t.fd)},d=(e,t,n,r)=>{let i=[o(e.stream),s(t.stream),s(n.stream)];if(r.length===0)return i;let a=r.reduce((e,{fd:t})=>Math.max(e,t),2);for(let e=3;e<=a;e++)i[e]=`ignore`;for(let{fd:e}of r)i[e]=`pipe`;return i},f=z(function*(e,t,n){if(n.length===0)return{getInputFd:()=>Jy,getOutputFd:()=>Fb};let r=/* @__PURE__ */ new Map,a=/* @__PURE__ */ new Map;for(let{config:o,fd:s}of n){let n=t.stdio[s];switch(o.type){case`input`:{let t=Jy;n&&`write`in n&&(t=kE(
Line truncated
|
||||||
|
`);function DF(e){return e.filename!==void 0||e.contentType===`application/octet-stream`}function OF(e){return uF(e[`content-type`]).parameters.boundary}function kF(e){}const AF=e=>e===1/0?1/0:Number(t_(e));function jF({headers:e,onFile:t,onField:n,onError:r,onDone:i,isFile:a=DF,maxParts:o=1/0,maxTotalSize:s=1/0,maxPartSize:c=1/0,maxFieldSize:l=1048576}){let u=AF(s),d=AF(c),f=AF(l),p=OF(e);if(p===void 0)return r({_tag:`InvalidBoundary`}),{write:kF,end(){}};let m={state:yF.headers,index:0,parts:0,onChunk:kF,info:void 0,headerSkip:0,partSize:0,totalSize:0,isFile:!1,fieldChunks:[],fieldSize:0,done:!1,stopped:!1};function h(){m.state=yF.body,m.isFile=!0,m.onChunk=kF}function g(e){m.stopped=!0,m.state===yF.body&&m.isFile&&m.onChunk(null),r(e)}let _=gF(),v=vF(`\r\n--${p}`,function(e,s){if(!m.stopped){if(e===0){h();return}if(e!==m.index){if(m.index>0){if(m.isFile)m.onChunk(null);else{if(m.fieldChunks.length===1)n(m.info,m.fieldChunks[0]);else{let e=new Uint8Array(m.fieldSize),t=0;for(let n=0;n<m.fieldChunks.length;n++){let r=m.fieldChunks[n];e.set(r,t),t+=r.length}n(m.info,e)}m.fieldSize=0,m.fieldChunks=[]}}if(m.partSize=0,m.state=yF.headers,m.index=e,m.headerSkip=2,s[0]===45&&s[1]===45)return m.done=!0,i();if(m.parts++,m.parts>o)return g(SF)}if((m.partSize+=s.length)>d)return g(wF);if(m.state===yF.headers){let e=_(s,m.headerSkip);if(m.headerSkip=0,e._tag===`Continue`)return;if(e._tag===`Failure`)return h(),r({_tag:`BadHeaders`,error:e});let n=uF(e.headers[`content-type`]),i=uF(e.headers[`content-disposition`],!0);if(i.value===`form-data`&&!(`name`in i.parameters))return h(),r(bF);let o;if(`filename*`in i.parameters){let e=i.parameters[`filename*`].split(`''`);if(e.length===2)try{o=decodeURIComponent(e[1])}catch{o=e[1]}}if(m.info={name:i.parameters.name??``,filename:o??i.parameters.filename,contentType:n.value===``?i.parameters.filename===void 0?`text/plain`:`application/octet-stream`:n.value,contentTypeParameters:n.parameters,contentDisposition:i.value,contentDispositionParameters:i.parameters,headers:e.headers},m.state=yF.body,m.isFile=a(m.info),m.isFile&&(m.onChunk=t(m.info)),e.endPosition<s.length){if(m.isFile)m.onChunk(s.subarray(e.endPosition));else{let t=s.subarray(e.endPosition);if((m.fieldSize+=t.length)>f)return g(TF);m.fieldChunks.push(t)}}}else if(m.isFile)m.onChunk(s);else{if((m.fieldSize+=s.length)>f)return g(TF);m.fieldChunks.push(s)}}},EF,2);return{write(e){if(!m.stopped)return(m.totalSize+=e.length)>u?g(CF):v.write(e)},end(){v.end(),!m.done&&!m.stopped&&g(xF),m.state=yF.headers,m.index=0,m.parts=0,m.onChunk=kF,m.info=void 0,m.totalSize=0,m.partSize=0,m.fieldChunks=[],m.fieldSize=0,m.done=!1,m.stopped=!1}}}const MF=/*#__PURE__*/ new TextDecoder(`utf-8`);function NF(e){if(e===`utf-8`||e===`utf8`||e===``)return MF;try{return new TextDecoder(e)}catch{return MF}}function PF(e,t){return NF(e.contentTypeParameters.charset??`utf-8`).decode(t)}const FF=jF,IF=DF,LF=PF,RF=`~effect/http/Multipart`,zF=`~effect/http/Multipart/MultipartError`;var BF=class extends Co{};const VF={FileTooLarge:413,FieldTooLarge:413,BodyTooLarge:413,TooManyParts:413,InternalError:500,Parse:400};var HF=class e extends (/*#__PURE__*/ wo(`MultipartError`)){static fromReason(t,n){return new e({reason:new BF({_tag:t,cause:n})})}[zF]=zF;[ZN]=!0;[xP](){return j(nP({status:VF[this.reason._tag]}))}get message(){return this.reason._tag}};const UF=e=>rh(t=>{let n=sa(t.context,ZF);return j({headers:e,maxParts:t.getRef(JF),maxFieldSize:t.getRef(YF),maxPartSize:t.getRef(XF),maxTotalSize:t.getRef(aN),isFile:n.length===0?void 0:e=>!n.some(t=>e.contentType.includes(t))&&IF(e)})});var WF=class extends Bt{[RF];constructor(){super(),this[RF]=RF}};const GF=(e,t)=>I(px,n=>R(ox(t.content,n.sink(e)),e=>HF.fromReason(`InternalError`,e))),KF=(e,t=GF)=>Zm(function*(){let n=yield*px,r=yield*vx,i=yield*n.makeTempDirectoryScoped(),a=Object.create(null),o=/* @__PURE__ */ new Set,s=0;return yield*lx(e,e=>{if(e._tag===`Field`)return e.key in a?typeof a[e.key]==`string`?a[e.key]=[a[e.key],e.value]:a[e.key].push(e.value):a[e.key]=e.value,P;if(e.name===``)return P;let n=e,c=r.basename(n.name).slice(-128),l=r.join(i,c);for(;o.has(l);)l=r.join(i,`${s++}-${c}`);o.add(l);let u=new qF(n.key,n.name,n.contentType,l);return Array.isArray(a[e.key])?a[e.key].push(u):a[e.key]=[u],t(l,n)}),a}).pipe(gh(`PlatformError`,e=>F(HF.fromReason(`InternalError`,e))));var qF=class extends WF{_tag=`PersistedFile`;key;name;contentType;path;constructor(e,t,n,r){super(),this.key=e,this.name=t,this.contentType=n,this.path=r}toJSON(){return{_id:`@effect/platform/Multipart/Part`,_tag:`PersistedFile`,key:this.key,name:this.name,contentType:this.contentType,path:this.path}}};const JF=/*#__PURE__*/ ma(`effect/http/Multipart/MaxParts`,{defaultValue:()=>void 0}),YF=/*#__PURE__*/ ma(`effect/http/Multipart/MaxFieldSize`,{defaultValue:/*#__PURE__*/ T(/*#__PURE__*/ i_(10))}),XF=/*#__PURE__*/ ma(`effect/http/Multipart/MaxFileSize`,{defaultValue:()=>void 0}),ZF=/*#__PURE__*/ ma(`effect/http/Multipart/FieldMLine truncated
|
||||||
|
`,a),s=[];for(;o!==-1;){OL(o-a,t)&&r(t);let e=JSON.parse(n.slice(a,o));s.push(e),a=o+1,o=n.indexOf(`
|
||||||
|
`,a)}return n=n.slice(a),OL(n.length,t)&&r(t),s},encode:e=>{if(Array.isArray(e)){if(e.length===0)return;let t=``;for(let n=0;n<e.length;n++)t+=JSON.stringify(e[n])+`
|
||||||
|
`;return t}return JSON.stringify(e)+`
|
||||||
|
`}}}})})(),AL=e=>EL.of({contentType:e?.contentType??`application/json`,includesFraming:!1,codecFor:CL,makeUnsafe:()=>{let e=/* @__PURE__ */ new Map;return{decode:t=>jL(JSON.parse(typeof t==`string`?t:TL(t)),e),encode:t=>{let n=PL(t,e);return n&&JSON.stringify(n)}}}});function jL(e,t){if(Array.isArray(e)){let n={size:0,responses:/* @__PURE__ */ new Map},r=[];for(let i=0;i<e.length;i++){let a=ML(e[i]);r.push(a),a._tag===`Request`&&!a.isNotification&&(n.size++,t.set(a.id,n))}return r}return[ML(e)]}function ML(e){if(Object.hasOwn(e,`method`)){let t=e;if(je(t.id)&&t.method.startsWith(`@effect/rpc/`)){let e=t.method.slice(12),n=t.params?.requestId;return n===void 0?{_tag:e}:{_tag:e,requestId:n}}return{_tag:`Request`,id:t.id??``,tag:t.method,payload:t.params??null,headers:t.headers??[],...E(t,`id`)?{}:{isNotification:!0},...t.spanId?{traceId:t.traceId,spanId:t.spanId,sampled:t.sampled}:{}}}let t=e,n=Object.hasOwn(t,`error`);return n&&t.error&&t.error._tag===`Defect`?{_tag:`Defect`,defect:t.error.data}:Object.hasOwn(t,`chunk`)&&t.chunk===!0?{_tag:`Chunk`,requestId:t.id??``,values:t.result}:{_tag:`Exit`,requestId:t.id??``,exit:n&&t.error!=null?{_tag:`Failure`,cause:t.error._tag===`Cause`?t.error.data:[{_tag:`Die`,defect:t.error}]}:{_tag:`Success`,value:t.result}}}function NL(e,t){if(!(`requestId`in e))return FL(e);let n=t.get(e.requestId);return n?(t.delete(e.requestId),n.responses.set(e.requestId,e),n.size===n.responses.size?Array.from(n.responses.values(),FL):void 0):FL(e)}function PL(e,t){if(Array.isArray(e)===!1)return NL(e,t);if(e.length===0)return;let n=[];for(let r=0;r<e.length;r++){let i=NL(e[r],t);i!==void 0&&n.push(i)}if(n.length===0)return;if(n.length===1)return n[0];let r=[];for(let e=0;e<n.length;e++){let t=n[e];Array.isArray(t)?r.push(...t):r.push(t)}return r}function FL(e){switch(e._tag){case`Request`:return{jsonrpc:`2.0`,method:e.tag,params:e.payload,...e.isNotification?{}:{id:e.id},...e.headers?.length>0?{headers:e.headers}:{},traceId:e.traceId,spanId:e.spanId,sampled:e.sampled};case`Ping`:case`Pong`:case`Interrupt`:case`Ack`:case`Eof`:return{jsonrpc:`2.0`,method:`@effect/rpc/${e._tag}`,params:`requestId`in e?{requestId:e.requestId}:void 0};case`Chunk`:return{jsonrpc:`2.0`,chunk:!0,id:e.requestId,result:e.values};case`Exit`:{if(e.exit._tag===`Success`)return{jsonrpc:`2.0`,id:e.requestId??void 0,result:e.exit.value};let t=e.exit.cause.find(e=>e._tag===`Fail`),n=t?._tag===`Fail`?t.error:void 0;return{jsonrpc:`2.0`,id:e.requestId??void 0,error:e.exit._tag===`Failure`?{_tag:`Cause`,code:n&&E(n,`code`)?Number(n.code):0,message:n&&E(n,`message`)?n.message:JSON.stringify(e.exit.cause),data:e.exit.cause}:void 0}}case`Defect`:return{jsonrpc:`2.0`,id:IL,error:{_tag:`Defect`,code:1,message:`A defect occurred`,data:e.defect}};case`ClientProtocolError`:return{}}}const IL=-32603,LL=e=>op(EL)(AL(e)),RL=()=>e=>M(()=>{let t=Rv(1),n=[],r=(...e)=>Bh(t=>(n.push([e,t]),P));return L(e((...e)=>r(...e)),e=>({...e,run(e){return t.withPermits(1)(Zm(function*(){let t=r;r=e;for(let[t,r]of n)yield*Hh(M(()=>e(...t)),r);return n=[],yield*eg(Xm,()=>(r=t,P))}))}}))}),zL=e=>M(()=>{let t=/* @__PURE__ */ new Set,n=/* @__PURE__ */ new Map,r=/* @__PURE__ */ new Map,i=(e,t)=>Bh(r=>{let i=n.get(e);return i||(i=[],n.set(e,i)),i.push([t,r]),P});return L(e((e,t)=>{let n=r.get(e);return n?n(t):i(e,t)},t),e=>({...e,run(e,i){return Zm(function*(){t.add(e),r.set(e,i);let a=n.get(e);if(a){n.delete(e);for(let[e,t]of a)yield*Hh(M(()=>i(e)),t)}return yield*eg(Xm,()=>(t.delete(e),r.delete(e),P))})}}))});let BL=0;const VL=/*#__PURE__*/ z(function*(e,t){let n=t?.spanPrefix??`RpcClient`,r=t?.supportsAck??!0,i=t?.disableTracing??!1,a=t?.generateRequestId??(()=>BL++),o=yield*zh(),s=sa(o,Lf),c=/* @__PURE__ */ new Map,l=!1;yield*Hf(s,rh(e=>(l=!0,u(Of(e.id)))));let u=z(function*(e){for(let[t,n]of c)c.delete(t),n._tag===`Queue`?yield*e._tag===`Success`?hv(n.queue):pv(n.queue,e.cause):n.resume(e)}),d=e=>{let r=sL(e.successSchema),a=m(e);return(o,s)=>{let c=s?.headers?kD(s.headers):OD,l=s?.context??$i();if(!r){let r=t=>f(e,a,t,e.payloadSchema.make(o),c,l,s?.discard??!1);return i?r(void 0):dg(`${n}.${e._tag}`,{attributes:t.spanAttributes},r)}let u=p(e,a,e.payloadSchema.make(o),c,s?.streamBufferSize??16,l);return s?.asQueue?u:Ub(L(u,Bb))}},f=(e,n,r,i,o,s,u)=>rh(d=>{if(l)return ng;let f=a(),p=n(e=>t.onFromClient({message:e,context:s,discard:u}),{_tag:`Request`,id:f,tag:e._tag,payload:i,...r?{traceId:r.traceId,spanId:r.spanId,sampled:r.sampled}:{},headers:ND(d.getRef(GL),o)});if(u)return p;let m;return ig(Ym(t=>{let n={_tag:`Effect`,rpc:e,context:s,resume(e){t(e),m&&!m.pollUnsafe()&&d.currentDispatcher.scheduleTask(()=>{m.interruptUnsafe(d.id)},0)}};c.set(f,n),m=p.pipe(r?pg(r,{captureStackTrace:!1}):w,yg(d.context)),m.addObserver(e=>{if(e._tag===`Failure`)return t(e)})}),e=>(c.delete(f),sh(f_(m),h(f,Array.from(e),s))))}),p=z(function*(e,r,o,s,u,d){if(l)return yield*ng;let f=i?void 0:yield*ug(`${n}.${e._tag}`,{attributes:t.spanAttributes}),p=m_(),m=a(),g=oa(p.conte
Line truncated
|
||||||
|
/* istanbul ignore else */
|
||||||
|
if(t.exports={concat:i,mask:a,toArrayBuffer:s,toBuffer:c,unmask:o},!process.env.WS_NO_BUFFER_UTIL)try{let e=se(`bufferutil`);t.exports.mask=function(t,n,r,i,o){o<48?a(t,n,r,i,o):e.mask(t,n,r,i,o)},t.exports.unmask=function(t,n){t.length<32?o(t,n):e.unmask(t,n)}}catch{}})),SR=/* @__PURE__ */ x(((e,t)=>{let n=Symbol(`kDone`),r=Symbol(`kRun`);t.exports=class{constructor(e){this[n]=()=>{this.pending--,this[r]()},this.concurrency=e||1/0,this.jobs=[],this.pending=0}add(e){this.jobs.push(e),this[r]()}[r](){if(this.pending!==this.concurrency&&this.jobs.length){let e=this.jobs.shift();this.pending++,e(this[n])}}}})),CR=/* @__PURE__ */ x(((e,t)=>{let n=se(`zlib`),r=xR(),i=SR(),{kStatusCode:a}=bR(),o=Buffer[Symbol.species],s=Buffer.from([0,0,255,255]),c=Symbol(`permessage-deflate`),l=Symbol(`total-length`),u=Symbol(`callback`),d=Symbol(`buffers`),f=Symbol(`error`),p;t.exports=class{constructor(e){if(this._options=e||{},this._threshold=this._options.threshold===void 0?1024:this._options.threshold,this._maxPayload=this._options.maxPayload|0,this._isServer=!!this._options.isServer,this._deflate=null,this._inflate=null,this.params=null,!p){let e=this._options.concurrencyLimit===void 0?10:this._options.concurrencyLimit;p=new i(e)}}static get extensionName(){return`permessage-deflate`}offer(){let e={};return this._options.serverNoContextTakeover&&(e.server_no_context_takeover=!0),this._options.clientNoContextTakeover&&(e.client_no_context_takeover=!0),this._options.serverMaxWindowBits&&(e.server_max_window_bits=this._options.serverMaxWindowBits),this._options.clientMaxWindowBits?e.client_max_window_bits=this._options.clientMaxWindowBits:this._options.clientMaxWindowBits??(e.client_max_window_bits=!0),e}accept(e){return e=this.normalizeParams(e),this.params=this._isServer?this.acceptAsServer(e):this.acceptAsClient(e),this.params}cleanup(){if(this._inflate&&=(this._inflate.close(),null),this._deflate){let e=this._deflate[u];this._deflate.close(),this._deflate=null,e&&e(/* @__PURE__ */ Error(`The deflate stream was closed while data was being processed`))}}acceptAsServer(e){let t=this._options,n=e.find(e=>!(t.serverNoContextTakeover===!1&&e.server_no_context_takeover||e.server_max_window_bits&&(t.serverMaxWindowBits===!1||typeof t.serverMaxWindowBits==`number`&&t.serverMaxWindowBits>e.server_max_window_bits)||typeof t.clientMaxWindowBits==`number`&&(typeof e.client_max_window_bits==`number`?t.clientMaxWindowBits>e.client_max_window_bits:!e.client_max_window_bits)));if(!n)throw Error(`None of the extension offers can be accepted`);return t.serverNoContextTakeover&&(n.server_no_context_takeover=!0),t.clientNoContextTakeover&&(n.client_no_context_takeover=!0),typeof t.serverMaxWindowBits==`number`&&(n.server_max_window_bits=t.serverMaxWindowBits),typeof t.clientMaxWindowBits==`number`?n.client_max_window_bits=t.clientMaxWindowBits:(n.client_max_window_bits===!0||t.clientMaxWindowBits===!1)&&delete n.client_max_window_bits,n}acceptAsClient(e){let t=e[0];if(this._options.clientNoContextTakeover===!1&&t.client_no_context_takeover)throw Error(`Unexpected parameter "client_no_context_takeover"`);if(!t.client_max_window_bits)typeof this._options.clientMaxWindowBits==`number`&&(t.client_max_window_bits=this._options.clientMaxWindowBits);else if(this._options.clientMaxWindowBits===!1||typeof this._options.clientMaxWindowBits==`number`&&t.client_max_window_bits>this._options.clientMaxWindowBits)throw Error(`Unexpected or invalid parameter "client_max_window_bits"`);return t}normalizeParams(e){return e.forEach(e=>{Object.keys(e).forEach(t=>{let n=e[t];if(n.length>1)throw Error(`Parameter "${t}" must have only a single value`);if(n=n[0],t===`client_max_window_bits`){if(n!==!0){let e=+n;if(!Number.isInteger(e)||e<8||e>15)throw TypeError(`Invalid value for parameter "${t}": ${n}`);n=e}else if(!this._isServer)throw TypeError(`Invalid value for parameter "${t}": ${n}`)}else if(t===`server_max_window_bits`){let e=+n;if(!Number.isInteger(e)||e<8||e>15)throw TypeError(`Invalid value for parameter "${t}": ${n}`);n=e}else if(t===`client_no_context_takeover`||t===`server_no_context_takeover`){if(n!==!0)throw TypeError(`Invalid value for parameter "${t}": ${n}`)}else throw Error(`Unknown parameter "${t}"`);e[t]=n})}),e}decompress(e,t,n){p.add(r=>{this._decompress(e,t,(e,t)=>{r(),n(e,t)})})}compress(e,t,n){p.add(r=>{this._compress(e,t,(e,t)=>{r(),n(e,t)})})}_decompress(e,t,i){let a=this._isServer?`client`:`server`;if(!this._inflate){let e=`${a}_max_window_bits`,t=typeof this.params[e]==`number`?this.params[e]:n.Z_DEFAULT_WINDOWBITS;this._inflate=n.createInflateRaw({...this._options.zlibInflateOptions,windowBits:t}),this._inflate[c]=this,this._inflate[l]=0,this._inflate[d]=[],this._inflate.on(`error`,g),this._inflate.on(`data`,h)}this._inflate[u]=i,this._inflate.write(e),t&&this._inflate.write(s),this._inflate.flush(()=>{let e=this._inflate[f];if(e){this._inflate.close(),this._inflate=null,i(e);return}let n=r.concat(this._inflate[d],this._inflate[l]);tLine truncated
|
||||||
|
/* istanbul ignore else */
|
||||||
|
(g===void 0&&(g=Buffer.alloc(h)),r(g,0,h),_=0),n[0]=g[_++],n[1]=g[_++],n[2]=g[_++],n[3]=g[_++]),o=(n[0]|n[1]|n[2]|n[3])===0,a=6);let s;typeof e==`string`?(!t.mask||o)&&t[p]!==void 0?s=t[p]:(e=Buffer.from(e),s=e.length):(s=e.length,i=t.mask&&t.readOnly&&!o);let c=s;s>=65536?(a+=8,c=127):s>125&&(a+=2,c=126);let l=Buffer.allocUnsafe(i?s+a:a);return l[0]=t.fin?t.opcode|128:t.opcode,t.rsv1&&(l[0]|=64),l[1]=c,c===126?l.writeUInt16BE(s,2):c===127&&(l[2]=l[3]=0,l.writeUIntBE(s,4,6)),!t.mask||(l[1]|=128,l[a-4]=n[0],l[a-3]=n[1],l[a-2]=n[2],l[a-1]=n[3],o)?[l,e]:i?(d(e,n,l,a,s),[l]):(d(e,n,e,0,s),[l,e])}close(t,n,r,a){let s;if(t===void 0)s=o;else if(typeof t!=`number`||!u(t))throw TypeError(`First argument must be a valid error code number`);else if(n===void 0||!n.length)s=Buffer.allocUnsafe(2),s.writeUInt16BE(t,0);else{let e=Buffer.byteLength(n);if(e>123)throw RangeError(`The message must not be greater than 123 bytes`);if(s=Buffer.allocUnsafe(2+e),s.writeUInt16BE(t,0),typeof n==`string`)s.write(n,2);else if(i(n))s.set(n,2);else throw TypeError(`Second argument must be a string or a Uint8Array`)}let c={[p]:s.length,fin:!0,generateMask:this._generateMask,mask:r,maskBuffer:this._maskBuffer,opcode:8,readOnly:!1,rsv1:!1};this._state===0?this.sendFrame(e.frame(s,c),a):this.enqueue([this.dispatch,s,!1,c,a])}ping(t,n,r){let i,a;if(typeof t==`string`?(i=Buffer.byteLength(t),a=!1):l(t)?(i=t.size,a=!1):(t=f(t),i=t.length,a=f.readOnly),i>125)throw RangeError(`The data size must not be greater than 125 bytes`);let o={[p]:i,fin:!0,generateMask:this._generateMask,mask:n,maskBuffer:this._maskBuffer,opcode:9,readOnly:a,rsv1:!1};l(t)?this._state===0?this.getBlobData(t,!1,o,r):this.enqueue([this.getBlobData,t,!1,o,r]):this._state===0?this.sendFrame(e.frame(t,o),r):this.enqueue([this.dispatch,t,!1,o,r])}pong(t,n,r){let i,a;if(typeof t==`string`?(i=Buffer.byteLength(t),a=!1):l(t)?(i=t.size,a=!1):(t=f(t),i=t.length,a=f.readOnly),i>125)throw RangeError(`The data size must not be greater than 125 bytes`);let o={[p]:i,fin:!0,generateMask:this._generateMask,mask:n,maskBuffer:this._maskBuffer,opcode:10,readOnly:a,rsv1:!1};l(t)?this._state===0?this.getBlobData(t,!1,o,r):this.enqueue([this.getBlobData,t,!1,o,r]):this._state===0?this.sendFrame(e.frame(t,o),r):this.enqueue([this.dispatch,t,!1,o,r])}send(e,t,n){let r=this._extensions[a.extensionName],i=t.binary?2:1,o=t.compress,s,c;typeof e==`string`?(s=Buffer.byteLength(e),c=!1):l(e)?(s=e.size,c=!1):(e=f(e),s=e.length,c=f.readOnly),this._firstFragment?(this._firstFragment=!1,o&&r&&r.params[r._isServer?`server_no_context_takeover`:`client_no_context_takeover`]&&(o=s>=r._threshold),this._compress=o):(o=!1,i=0),t.fin&&(this._firstFragment=!0);let u={[p]:s,fin:t.fin,generateMask:this._generateMask,mask:t.mask,maskBuffer:this._maskBuffer,opcode:i,readOnly:c,rsv1:o};l(e)?this._state===0?this.getBlobData(e,this._compress,u,n):this.enqueue([this.getBlobData,e,this._compress,u,n]):this._state===0?this.dispatch(e,this._compress,u,n):this.enqueue([this.dispatch,e,this._compress,u,n])}getBlobData(t,n,r,i){this._bufferedBytes+=r[p],this._state=2,t.arrayBuffer().then(t=>{if(this._socket.destroyed){let e=/* @__PURE__ */ Error(`The socket was closed while the blob was being read`);process.nextTick(v,this,e,i);return}this._bufferedBytes-=r[p];let a=f(t);n?this.dispatch(a,n,r,i):(this._state=0,this.sendFrame(e.frame(a,r),i),this.dequeue())}).catch(e=>{process.nextTick(ee,this,e,i)})}dispatch(t,n,r,i){if(!n){this.sendFrame(e.frame(t,r),i);return}let o=this._extensions[a.extensionName];this._bufferedBytes+=r[p],this._state=1,o.compress(t,r.fin,(t,n)=>{if(this._socket.destroyed){let e=/* @__PURE__ */ Error(`The socket was closed while data was being compressed`);v(this,e,i);return}this._bufferedBytes-=r[p],this._state=0,r.readOnly=!1,this.sendFrame(e.frame(n,r),i),this.dequeue()})}dequeue(){for(;this._state===0&&this._queue.length;){let e=this._queue.shift();this._bufferedBytes-=e[3][p],Reflect.apply(e[0],this,e.slice(1))}}enqueue(e){this._bufferedBytes+=e[3][p],this._queue.push(e)}sendFrame(e,t){e.length===2?(this._socket.cork(),this._socket.write(e[0]),this._socket.write(e[1],t),this._socket.uncork()):this._socket.write(e[0],t)}};function v(e,t,n){typeof n==`function`&&n(t);for(let n=0;n<e._queue.length;n++){let r=e._queue[n],i=r[r.length-1];typeof i==`function`&&i(t)}}function ee(e,t,n){v(e,t,n),e.onerror(t)}})),DR=/* @__PURE__ */ x(((e,t)=>{let{kForOnEventAttribute:n,kListener:r}=bR(),i=Symbol(`kCode`),a=Symbol(`kData`),o=Symbol(`kError`),s=Symbol(`kMessage`),c=Symbol(`kReason`),l=Symbol(`kTarget`),u=Symbol(`kType`),d=Symbol(`kWasClean`);var f=class{constructor(e){this[l]=null,this[u]=e}get target(){return this[l]}get type(){return this[u]}};Object.defineProperty(f.prototype,"target",{enumerable:!0}),Object.defineProperty(f.prototype,"type",{enumerable:!0});var p=class extends f{constructor(e,t={}){super(e),this[i]=t.code===void 0?0:t.code,this[c]=t.reason===void 0?``:t.reason,this[d]=t.wasClean!==void 0&&t.wasClLine truncated
|
||||||
|
/* istanbul ignore next */
|
||||||
|
get onclose(){return null}
|
||||||
|
/* istanbul ignore next */
|
||||||
|
get onerror(){return null}
|
||||||
|
/* istanbul ignore next */
|
||||||
|
get onopen(){return null}
|
||||||
|
/* istanbul ignore next */
|
||||||
|
get onmessage(){return null}get protocol(){return this._protocol}get readyState(){return this._readyState}get url(){return this._url}setSocket(t,n,r){let i=new p({allowSynchronousEvents:r.allowSynchronousEvents,binaryType:this.binaryType,extensions:this._extensions,isServer:this._isServer,maxBufferedChunks:r.maxBufferedChunks,maxFragments:r.maxFragments,maxPayload:r.maxPayload,skipUTF8Validation:r.skipUTF8Validation}),a=new m(t,this._extensions,r.generateMask);this._receiver=i,this._sender=a,this._socket=t,i[ne]=this,a[ne]=this,t[ne]=this,i.on(`conclude`,ge),i.on(`drain`,_e),i.on(`error`,ve),i.on(`message`,be),i.on(`ping`,xe),i.on(`pong`,Se),a.onerror=we,t.setTimeout&&t.setTimeout(0),t.setNoDelay&&t.setNoDelay(),n.length>0&&t.unshift(n),t.on(`close`,Ee),t.on(`data`,De),t.on(`end`,Oe),t.on(`error`,ke),this._readyState=e.OPEN,this.emit(`open`)}emitClose(){if(!this._socket){this._readyState=e.CLOSED,this.emit(`close`,this._closeCode,this._closeMessage);return}this._extensions[f.extensionName]&&this._extensions[f.extensionName].cleanup(),this._receiver.removeAllListeners(),this._readyState=e.CLOSED,this.emit(`close`,this._closeCode,this._closeMessage)}close(t,n){if(this.readyState!==e.CLOSED){if(this.readyState===e.CONNECTING){me(this,this._req,`WebSocket was closed before the connection was established`);return}if(this.readyState===e.CLOSING){this._closeFrameSent&&(this._closeFrameReceived||this._receiver._writableState.errorEmitted)&&this._socket.end();return}this._readyState=e.CLOSING,this._sender.close(t,n,!this._isServer,e=>{e||(this._closeFrameSent=!0,(this._closeFrameReceived||this._receiver._writableState.errorEmitted)&&this._socket.end())}),Te(this)}}pause(){this.readyState!==e.CONNECTING&&this.readyState!==e.CLOSED&&(this._paused=!0,this._socket.pause())}ping(t,n,r){if(this.readyState===e.CONNECTING)throw Error(`WebSocket is not open: readyState 0 (CONNECTING)`);if(typeof t==`function`?(r=t,t=n=void 0):typeof n==`function`&&(r=n,n=void 0),typeof t==`number`&&(t=t.toString()),this.readyState!==e.OPEN){he(this,t,r);return}n===void 0&&(n=!this._isServer),this._sender.ping(t||v,n,r)}pong(t,n,r){if(this.readyState===e.CONNECTING)throw Error(`WebSocket is not open: readyState 0 (CONNECTING)`);if(typeof t==`function`?(r=t,t=n=void 0):typeof n==`function`&&(r=n,n=void 0),typeof t==`number`&&(t=t.toString()),this.readyState!==e.OPEN){he(this,t,r);return}n===void 0&&(n=!this._isServer),this._sender.pong(t||v,n,r)}resume(){this.readyState!==e.CONNECTING&&this.readyState!==e.CLOSED&&(this._paused=!1,this._receiver._writableState.needDrain||this._socket.resume())}send(t,n,r){if(this.readyState===e.CONNECTING)throw Error(`WebSocket is not open: readyState 0 (CONNECTING)`);if(typeof n==`function`&&(r=n,n={}),typeof t==`number`&&(t=t.toString()),this.readyState!==e.OPEN){he(this,t,r);return}let i={binary:typeof t!=`string`,mask:!this._isServer,compress:!0,fin:!0,...n};this._extensions[f.extensionName]||(i.compress=!1),this._sender.send(t||v,i,r)}terminate(){if(this.readyState!==e.CLOSED){if(this.readyState===e.CONNECTING){me(this,this._req,`WebSocket was closed before the connection was established`);return}this._socket&&(this._readyState=e.CLOSING,this._socket.destroy())}}};Object.defineProperty(T,"CONNECTING",{enumerable:!0,value:C.indexOf(`CONNECTING`)}),Object.defineProperty(T.prototype,"CONNECTING",{enumerable:!0,value:C.indexOf(`CONNECTING`)}),Object.defineProperty(T,"OPEN",{enumerable:!0,value:C.indexOf(`OPEN`)}),Object.defineProperty(T.prototype,"OPEN",{enumerable:!0,value:C.indexOf(`OPEN`)}),Object.defineProperty(T,"CLOSING",{enumerable:!0,value:C.indexOf(`CLOSING`)}),Object.defineProperty(T.prototype,"CLOSING",{enumerable:!0,value:C.indexOf(`CLOSING`)}),Object.defineProperty(T,"CLOSED",{enumerable:!0,value:C.indexOf(`CLOSED`)}),Object.defineProperty(T.prototype,"CLOSED",{enumerable:!0,value:C.indexOf(`CLOSED`)}),[`binaryType`,`bufferedAmount`,`extensions`,`isPaused`,`protocol`,`readyState`,`url`].forEach(e=>{Object.defineProperty(T.prototype,e,{enumerable:!0})}),[`open`,`error`,`close`,`message`].forEach(e=>{Object.defineProperty(T.prototype,`on${e}`,{enumerable:!0,get(){for(let t of this.listeners(e))if(t[y])return t[te];return null},set(t){for(let t of this.listeners(e))if(t[y]){this.removeListener(e,t);break}typeof t==`function`&&this.addEventListener(e,t,{[y]:!0})}})}),T.prototype.addEventListener=re,T.prototype.removeEventListener=ie,t.exports=T;function ue(e,t,n,a){let o={allowSynchronousEvents:!0,autoPong:!0,closeTimeout:_,protocolVersion:le[1],maxBufferedChunks:262144,maxFragments:16384,maxPayload:104857600,skipUTF8Validation:!1,perMessageDeflate:!0,followRedirects:!1,maxRedirects:10,...a,socketPath:void 0,hostname:void 0,protocol:void 0,timeout:void 0,method:`GET`,host:void 0,path:void 0,port:void 0};if(e._autoPong=o.autoPong,e._closeTimeout=o.closeTimeout,!le.includes(o.protocolVersion))throw RangeError(`Unsupported protocol version: ${o.protocolVersion} (supported versions: ${le.join(`, `)})`);lLine truncated
|
||||||
|
`).join(`\r
|
||||||
|
`)),i.removeListener(`error`,g),p.setSocket(i,c,{allowSynchronousEvents:this.options.allowSynchronousEvents,maxBufferedChunks:this.options.maxBufferedChunks,maxFragments:this.options.maxFragments,maxPayload:this.options.maxPayload,skipUTF8Validation:this.options.skipUTF8Validation}),this.clients&&(this.clients.add(p),p.on(`close`,()=>{this.clients.delete(p),this._shouldEmitClose&&!this.clients.size&&process.nextTick(h,this)})),l(p,r)}};function m(e,t){for(let n of Object.keys(t))e.on(n,t[n]);return function(){for(let n of Object.keys(t))e.removeListener(n,t[n])}}function h(e){e._state=2,e.emit(`close`)}function g(){this.destroy()}function _(e,t,n,i){n||=r.STATUS_CODES[t],i={Connection:`close`,"Content-Type":`text/html`,"Content-Length":Buffer.byteLength(n),...i},e.once(`finish`,e.destroy),e.end(`HTTP/1.1 ${t} ${r.STATUS_CODES[t]}\r\n`+Object.keys(i).map(e=>`${e}: ${i[e]}`).join(`\r
|
||||||
|
`)+`\r
|
||||||
|
\r
|
||||||
|
`+n)}function v(e,t,n,r,i,a){if(e.listenerCount(`wsClientError`)){let r=Error(i);Error.captureStackTrace(r,v),e.emit(`wsClientError`,r,n,t)}else _(n,r,i,a)}}));AR(),OR(),CR(),TR(),ER(),jR(),kR();var NR=/* @__PURE__ */ ae(MR(),1);const PR=e=>{switch(e){case`SHA-1`:return`sha1`;case`SHA-256`:return`sha256`;case`SHA-384`:return`sha384`;case`SHA-512`:return`sha512`}},FR=/* @__PURE__ */ op(_R,/* @__PURE__ */ vR({randomBytes:c.randomBytes,digest:(e,t)=>th({try:()=>Uint8Array.from(c.createHash(PR(e)).update(t).digest()),catch:e=>l_({module:`Crypto`,method:`digest`,_tag:`Unknown`,description:`Could not compute digest`,cause:e})})})),IR=e=>t=>u_({module:`FileSystem`,method:e,description:t.message??String(t)}),LR=(e,t)=>{let n=Number(e);if(!Number.isSafeInteger(n))throw RangeError(`${t} exceeds the safe integer range: ${e}`);return n},RR=e=>Hr(Lr(e),zg),zR=(e,t)=>th({try:()=>LR(e,`position`),catch:IR(t)}),BR=/*#__PURE__*/ (()=>{let e=/*#__PURE__*/ Mg(l.access,/*#__PURE__*/ OE(`FileSystem`,`access`),/*#__PURE__*/ IR(`access`));return(t,n)=>{let r=l.constants.F_OK;return n?.readable&&(r|=l.constants.R_OK),n?.writable&&(r|=l.constants.W_OK),e(t,r)}})(),VR=/*#__PURE__*/ (()=>{let e=/*#__PURE__*/ Mg(l.cp,/*#__PURE__*/ OE(`FileSystem`,`copy`),/*#__PURE__*/ IR(`copy`));return(t,n,r)=>e(t,n,{force:r?.overwrite??!1,preserveTimestamps:r?.preserveTimestamps??!1,recursive:!0})})(),HR=/*#__PURE__*/ (()=>{let e=/*#__PURE__*/ Mg(l.copyFile,/*#__PURE__*/ OE(`FileSystem`,`copyFile`),/*#__PURE__*/ IR(`copyFile`));return(t,n)=>e(t,n)})(),UR=/*#__PURE__*/ (()=>{let e=/*#__PURE__*/ Mg(l.chmod,/*#__PURE__*/ OE(`FileSystem`,`chmod`),/*#__PURE__*/ IR(`chmod`));return(t,n)=>e(t,n)})(),WR=/*#__PURE__*/ (()=>{let e=/*#__PURE__*/ Mg(l.chown,/*#__PURE__*/ OE(`FileSystem`,`chown`),/*#__PURE__*/ IR(`chown`));return(t,n,r)=>e(t,n,r)})(),GR=/*#__PURE__*/ (()=>{let e=/*#__PURE__*/ Mg(l.glob,/*#__PURE__*/ OE(`FileSystem`,`glob`),/*#__PURE__*/ IR(`glob`));return(t,n)=>e(t,{cwd:n?.root,exclude:n?.exclude})})(),KR=/*#__PURE__*/ (()=>{let e=/*#__PURE__*/ Mg(l.link,/*#__PURE__*/ OE(`FileSystem`,`link`),/*#__PURE__*/ IR(`link`));return(t,n)=>e(t,n)})(),qR=/*#__PURE__*/ (()=>{let e=/*#__PURE__*/ Mg(l.mkdir,/*#__PURE__*/ OE(`FileSystem`,`makeDirectory`),/*#__PURE__*/ IR(`makeDirectory`));return(t,n)=>e(t,{recursive:n?.recursive??!1,mode:n?.mode})})(),JR=e=>{let t=Mg(l.mkdtemp,OE(`FileSystem`,e),IR(e));return e=>M(()=>{let n=e?.prefix??``,r=typeof e?.directory==`string`?d.join(e.directory,`.`):u.tmpdir();return t(n?d.join(r,n):r+`/`)})},YR=/*#__PURE__*/ JR(`makeTempDirectory`),XR=e=>{let t=Mg(l.rm,OE(`FileSystem`,e),IR(e));return(e,n)=>t(e,{recursive:n?.recursive??!1,force:n?.force??!1})},ZR=/*#__PURE__*/ XR(`remove`),QR=/*#__PURE__*/ (()=>{let e=/*#__PURE__*/ JR(`makeTempDirectoryScoped`),t=/*#__PURE__*/ XR(`makeTempDirectoryScoped`);return n=>Yh(e(n),e=>xh(t(e,{recursive:!0})))})(),$R=/*#__PURE__*/ (e=>{let t=Mg(l.open,OE(`FileSystem`,e),IR(e)),n=Mg(l.close,OE(`FileSystem`,e),IR(e));return(e,r)=>he(Yh(t(e,r?.flag??`r`,r?.mode),e=>xh(n(e))),L(e=>ez(e,r?.flag?.startsWith(`a`)??!1)))})(`open`),ez=/*#__PURE__*/ (()=>{let e=e=>Mg(l.read,OE(`FileSystem`,e),IR(e)),t=/*#__PURE__*/ e(`read`),n=/*#__PURE__*/ e(`readAlloc`),r=/*#__PURE__*/ Mg(l.fstat,/*#__PURE__*/ OE(`FileSystem`,`stat`),/*#__PURE__*/ IR(`stat`)),i=/*#__PURE__*/ Mg(l.ftruncate,/*#__PURE__*/ OE(`FileSystem`,`truncate`),/*#__PURE__*/ IR(`truncate`)),a=/*#__PURE__*/ Mg(l.fsync,/*#__PURE__*/ OE(`FileSystem`,`sync`),/*#__PURE__*/ IR(`sync`)),o=e=>Mg(l.write,OE(`FileSystem`,e),IR(e)),s=/*#__PURE__*/ o(`write`),c=/*#__PURE__*/ o(`writeAll`);class u{[hx];fd;append;position=/*#__PURE__*/ BigInt(0);constructor(e,t){this[hx]=hx,this.fd=e,this.append=t}get stat(){return I(r(this.fd,{bigint:!0}),lz)}get sync(){return a(this.fd)}seek(e,t){return M(()=>{let n=t===`start`?e:this.position+e;return n<BigInt(0)?F(u_({module:`FileSystem`,method:`seek`,description:`Cannot seek before the start of the file`})):(this.position=n,j(n))})}read(e){return M(()=>{let n=this.position;return L(t(this.fd,{buffer:e,position:n}),e=>(this.position=n+BigInt(e),e))})}readAlloc(e){return M(()=>{try{if(!Number.isInteger(e)||e<0)throw RangeError(`size must be a non-negative integer`);let t=Buffer.allocUnsafeSlow(e),r=this.position;return L(n(this.fd,{buffer:t,position:r}),n=>{if(n===0)return D();if(this.position=r+BigInt(n),n===e)return O(t);let i=Buffer.allocUnsafeSlow(n);return t.copy(i,0,0,n),O(i)})}catch(e){return F(IR(`readAlloc`)(e))}})}truncate(e){return L(i(this.fd,e||void 0),()=>{if(!this.append){let t=BigInt(e??0);this.position>t&&(this.position=t)}})}write(e){return M(()=>{let t=this.position;return I(this.append?j(void 0):zR(t,`write`),n=>L(s(this.fd,e,void 0,void 0,n),e=>(this.append||(this.position=t+BigInt(e)),e)))})}writeAllChunk(e){return M(()=>{let t=this.position;return I(this.append?j(void 0):zR(t,`writeAll`),n=>I(c(this.fd,e,void 0,void 0,n),n=>n===0?F(l_({module:`FileSystem`,method:`writeAll`,_tag:`WriteZero`,pathOrDes
Line truncated
|
||||||
|
`)}`:``;return(this.command?`Unrecognized flag: ${this.option} in command ${this.command.join(` `)}`:`Unrecognized flag: ${this.option}`)+e}},uV=class extends (/*#__PURE__*/ IM(`${sV}/DuplicateOption`)(`DuplicateOption`,{option:V,parentCommand:V,childCommand:V})){[sV]=sV;get message(){return`Duplicate flag name "${this.option}" in parent command "${this.parentCommand}" and subcommand "${this.childCommand}". Parent will always claim this flag (Mode A semantics). Consider renaming one of them to avoid confusion.`}},dV=class extends (/*#__PURE__*/ IM(`${sV}/MissingOption`)(`MissingOption`,{option:V})){[sV]=sV;get message(){return`Missing required flag: --${this.option}`}},fV=class extends (/*#__PURE__*/ IM(`${sV}/MissingArgument`)(`MissingArgument`,{argument:V})){[sV]=sV;get message(){return`Missing required argument: ${this.argument}`}},pV=class extends (/*#__PURE__*/ IM(`${sV}/UnexpectedArgument`)(`UnexpectedArgument`,{arguments:/*#__PURE__*/ U(V)})){[sV]=sV;get message(){return`Unexpected positional ${this.arguments.length===1?`argument`:`arguments`}: ${this.arguments.map(e=>JSON.stringify(e)).join(`, `)}`}},mV=class extends (/*#__PURE__*/ IM(`${sV}/InvalidValue`)(`InvalidValue`,{option:V,value:V,expected:V,kind:/*#__PURE__*/ W([/*#__PURE__*/ B(`flag`),/*#__PURE__*/ B(`argument`)])})){[sV]=sV;get message(){let e=this.expected.startsWith(`Expected `)||this.expected.startsWith(`Expected:`)?this.expected:`Expected: ${this.expected}`;return this.kind===`argument`?`Invalid value for argument <${this.option}>: "${this.value}". ${e}`:this.value.length===0?`Missing value for flag --${this.option}. ${e}`:`Invalid value for flag --${this.option}: "${this.value}". ${e}`}},hV=class extends (/*#__PURE__*/ IM(`${sV}/UnknownSubcommand`)(`UnknownSubcommand`,{subcommand:V,parent:/*#__PURE__*/ pj(/*#__PURE__*/ U(V)),suggestions:/*#__PURE__*/ U(V)})){[sV]=sV;get message(){let e=this.suggestions.length>0?`\n\n Did you mean this?\n ${this.suggestions.join(`
|
||||||
|
`)}`:``;return this.parent?`Unknown subcommand "${this.subcommand}" for "${this.parent.join(` `)}"${e}`:`Unknown subcommand "${this.subcommand}"${e}`}};const gV=/*#__PURE__*/ W([lV,uV,dV,fV,pV,mV,hV,class extends (/*#__PURE__*/ IM(`${sV}/UserError`)(`UserError`,{cause:/*#__PURE__*/ uM(),userMessage:/*#__PURE__*/ fj(V)})){[sV]=sV;[gB]=!0;get message(){return this.userMessage?this.userMessage:typeof this.cause==`string`&&this.cause?this.cause:this.cause instanceof Error&&this.cause.message?this.cause.message:`An error occurred`}}]);var _V=class extends (/*#__PURE__*/ IM(`${sV}/ShowHelp`)(`ShowHelp`,{commandPath:/*#__PURE__*/ U(V),errors:/*#__PURE__*/ U(gV)})){[sV]=sV;[mB]=+!!this.errors.length;[gB]=!1;get message(){return`Help requested`}};const vV={"~effect/cli/Primitive":{_A:w}},yV=/*#__PURE__*/ tj(Bj),bV=/*#__PURE__*/ tj(Vj),xV=e=>e._tag===`Boolean`,SV=(e,t)=>Object.assign(Object.create(vV),{_tag:e,parse:t}),CV=(e,t)=>{let n=nj(zM(t));return SV(e,e=>R(n(e),e=>e.message))},wV=/*#__PURE__*/ CV(`Boolean`,Hj),TV=/*#__PURE__*/ CV(`Int`,oM),EV=/*#__PURE__*/ SV(`String`,e=>j(e)),DV=e=>{let t=new Map(e),n=e.map(([e])=>Ot(e)).join(` | `),r=SV(`Choice`,e=>t.has(e)?j(t.get(e)):F(n));return Object.assign(r,{choiceKeys:e.map(([e])=>e)})},OV=(e,t)=>{let n=SV(`Path`,z(function*(n){let r=yield*px,i=yield*vx,a=i.isAbsolute(n)?n:i.resolve(n),o=yield*R(r.exists(a),e=>`Failed to check path existence: ${e.message}`);if(t===!0&&!o)return yield*F(`Path does not exist: ${a}`);if(o&&e!==`either`){let t=yield*R(r.stat(a),e=>`Failed to stat path: ${e.message}`);if(e===`file`&&t.type!==`File`)return yield*F(`Path is not a file: ${a}`);if(e===`directory`&&t.type!==`Directory`)return yield*F(`Path is not a directory: ${a}`)}return a}));return Object.assign(n,{pathType:e})},kV=/*#__PURE__*/ SV(`KeyValuePair`,/*#__PURE__*/ z(function*(e){let t=e.indexOf(`=`);if(t===-1)return yield*F(`Invalid key=value format. Expected format: key=value, got: ${e}`);let n=e.slice(0,t),r=e.slice(t+1);return!n||!r?yield*F(`Invalid key=value format. Both key and value must be non-empty. Got: ${e}`):{[n]:r}})),AV=e=>{switch(e._tag){case`Boolean`:return`boolean`;case`String`:return`string`;case`Int`:return`integer`;case`Finite`:return`number`;case`Date`:return`date`;case`Path`:return`path`;case`Choice`:return`choice`;case`Redacted`:return`string`;case`FileText`:return`file`;case`FileParse`:return`file`;case`FileSchema`:return`file`;case`KeyValuePair`:return`key=value`;case`Never`:return`none`;default:return`value`}},jV=e=>e._tag===`Choice`?e.choiceKeys:void 0,MV=e=>e._tag===`Path`?e.pathType:void 0,NV=`\x1B[1m`,PV=`\x1B[4m`,FV=`\x1B[?25l`,IV=`\x1B[31m`,LV=`\x1B[32m`,RV=`\x1B[37m`,zV=`\x1B[90m`,BV=`\x1B[96m`,VV=(e,...t)=>`${t.flat().join(``)}${e}[0m`,HV=(...e)=>e,UV=(e,t)=>t===void 0?`[${Math.max(e+1,0)}G`:`[${t+1};${Math.max(e+1,0)}H`,WV=(e=1)=>`[${e}B`,GV=(e,t=0)=>{let n=``;return t<0&&(n+=`[${-t}A`),t>0&&(n+=`[${t}B`),e>0&&(n+=`[${e}C`),e<0&&(n+=`[${-e}D`),n},KV=e=>{let t=``;for(let n=0;n<e;n++)t+=`\x1B[2K`+(n<e-1?`\x1B[1A`:``);return e>0&&(t+=`\x1B[G`),t},qV={prefix:`?`,arrowUp:`↑`,arrowDown:`↓`,checkboxOn:`☒`,checkboxOff:`☐`,tick:`✔`,ellipsis:`…`,pointerSmall:`›`,pointer:`❯`,descriptionSeparator:`- `,passwordMask:`*`,toggleSeparator:`/`,primaryColor:BV,mutedColor:zV,successColor:LV,errorColor:IV,submittedColor:RV},JV={...qV,checkboxOn:`[*]`,checkboxOff:`[ ]`,tick:`√`,ellipsis:`...`,pointerSmall:`»`,pointer:`>`},YV=/*#__PURE__*/ ma(`effect/unstable/cli/Prompt/Theme`,{defaultValue:e=>({...process.platform===`win32`?JV:qV,...e})}),XV=e=>L(YV,t=>({...t,...e.theme})),ZV=e=>VV(e,NV),QV=(e,t)=>VV(e,HV(`\x1B[3m`,t)),$V=(e,...t)=>e.length===0?``:VV(e,...t),eH=(e,t)=>e.length===0?t:e+` `+t,tH=(e,t,n)=>{let r=Math.max(e.arrowUp.length,e.arrowDown.length);return t?e.arrowUp.padEnd(r):n?e.arrowDown.padEnd(r):` `.repeat(r)},nH=e=>{let t={initial:!1,...e,label:{confirm:`yes`,deny:`no`,...e.label},placeholder:{defaultConfirm:`(Y/n)`,defaultDeny:`(y/N)`,...e.placeholder}},n={value:t.initial};return rH(n,{render:DH(t),process:e=>AH(e,t.initial),clear:CH(t)})},rH=(e,...t)=>{let[n,r]=t.length===1?[void 0,t[0]]:[t[0],t[1]],i=Object.create(gH);return i._tag=`Loop`,i.initialState=e,i.render=r.render,i.process=r.process,i.clear=r.clear,i.events=n,i},iH=e=>{let t={initial:new globalThis.Date,dateMask:`YYYY-MM-DD HH:mm:ss`,validate:j,...e,locales:{...UH,...e.locales}},n=JH(t.dateMask,t.initial,t.locales),r={dateParts:n,typed:``,cursor:n.findIndex(e=>!e.isToken()),value:t.initial,error:D()};return rH(r,{render:WH(t),process:GH(t),clear:jH(t)})},aH=/*#__PURE__*/ C(2,(e,t)=>{let n=Object.create(gH);return n._tag=`OnSuccess`,n.prompt=e,n.onSuccess=t,n}),oH=e=>{let t={default:0,min:globalThis.Number.NEGATIVE_INFINITY,max:globalThis.Number.POSITIVE_INFINITY,incrementBy:1,decrementBy:1,precision:2,validate:e=>e<t.min?F(`${e} must be greater than or equal to ${t.min}`):e>t.max?F(`${e} must be less than or equal to ${t.max}`):j(e),...e},n=e.default===void 0?`Line truncated
|
||||||
|
`}),DH=e=>(t,n)=>q.$match(n,{Beep:()=>j(`\x07`),NextFrame:({state:t})=>TH(t,e),Submit:({value:t})=>EH(t,e)}),OH=/^y|t$/,kH=/^n|f$/,AH=(e,t)=>{let n=Ir(e.input,()=>``);return e.key.name===`enter`||e.key.name===`return`?j(q.Submit({value:t})):OH.test(n.toLowerCase())?j(q.Submit({value:!0})):kH.test(n.toLowerCase())?j(q.Submit({value:!1})):j(q.Beep())},jH=e=>z(function*(t,n){let r=yield*(yield*Zz).columns,i=yield*XV(e),a=Ni(t.dateParts,``,(e,t)=>e+t.toString()),o=PH(i.prefix,i.pointerSmall,a,e,{plain:!0}),s=Pr(t.error)?gi(t.error.value.split(SH),{onEmpty:()=>``,onNonEmpty:e=>eH(i.pointerSmall,e.join(`
|
||||||
|
`))}):``;return xH(o,r,s)+`\x1B[2K\x1B[G`}),MH=(e,t,n)=>{if(Pr(e.error)){let r=e.error.value.split(SH);if(yi(r)){let e=$V(t,n.errorColor),i=Mi(r,e=>QV(e,n.errorColor));return`\x1B[s
|
||||||
|
`+eH(e,i.join(`
|
||||||
|
`))+`\x1B[u`}}return``},NH=(e,t,n=!1)=>Ni(e.dateParts,``,(r,i,a)=>{let o=i.toString();if(a===e.cursor&&!n){let e=HV(PV,t.primaryColor);return r+VV(o,e)}return r+o}),PH=(e,t,n,r,i)=>aU(n,r.message,e,t,i),FH=/*#__PURE__*/ z(function*(e,t){let n=yield*XV(t),r=$V(n.prefix,n.primaryColor),i=$V(n.pointerSmall,n.mutedColor),a=NH(e,n),o=PH(r,i,a,t),s=MH(e,n.pointerSmall,n);return FV+o+s}),IH=/*#__PURE__*/ z(function*(e,t){let n=yield*XV(t),r=$V(n.tick,n.successColor),i=$V(n.ellipsis,n.mutedColor),a=NH(e,n,!0);return PH(r,i,a,t)+`
|
||||||
|
`}),LH=e=>(e.dateParts[e.cursor].increment(),q.NextFrame({state:{...e,typed:``}})),RH=e=>(e.dateParts[e.cursor].decrement(),q.NextFrame({state:{...e,typed:``}})),zH=e=>{let t=e.dateParts[e.cursor].previousPart();return Pr(t)?q.NextFrame({state:{...e,typed:``,cursor:e.dateParts.indexOf(t.value)}}):q.Beep()},BH=e=>{let t=e.dateParts[e.cursor].nextPart();return Pr(t)?q.NextFrame({state:{...e,typed:``,cursor:e.dateParts.indexOf(t.value)}}):q.Beep()},VH=e=>{let t=e.dateParts[e.cursor].nextPart(),n=Fr(t,{onNone:()=>e.dateParts.findIndex(e=>!e.isToken()),onSome:t=>e.dateParts.indexOf(t)});return q.NextFrame({state:{...e,typed:``,cursor:n}})},HH=(e,t)=>{if(/\d/.test(e)){let n=t.typed+e;return t.dateParts[t.cursor].setValue(n),q.NextFrame({state:{...t,typed:n}})}return q.Beep()},UH={months:[`January`,`February`,`March`,`April`,`May`,`June`,`July`,`August`,`September`,`October`,`November`,`December`],monthsShort:[`Jan`,`Feb`,`Mar`,`Apr`,`May`,`Jun`,`Jul`,`Aug`,`Sep`,`Oct`,`Nov`,`Dec`],weekdays:[`Sunday`,`Monday`,`Tuesday`,`Wednesday`,`Thursday`,`Friday`,`Saturday`],weekdaysShort:[`Sun`,`Mon`,`Tue`,`Wed`,`Thu`,`Fri`,`Sat`]},WH=e=>(t,n)=>q.$match(n,{Beep:()=>j(`\x07`),NextFrame:({state:t})=>FH(t,e),Submit:()=>IH(t,e)}),GH=e=>(t,n)=>{switch(t.key.name){case`left`:return j(zH(n));case`right`:return j(BH(n));case`k`:case`up`:return j(LH(n));case`j`:case`down`:return j(RH(n));case`tab`:return j(VH(n));case`enter`:case`return`:return Fh(e.validate(n.value),{onFailure:e=>q.NextFrame({state:{...n,error:O(e)}}),onSuccess:e=>q.Submit({value:e})});default:return j(HH(Ir(t.input,()=>``),n))}},KH=/\\(.)|"((?:\\["\\]|[^"])+)"|(D[Do]?|d{3,4}|d)|(M{1,4})|(YY(?:YY)?)|([aA])|([Hh]{1,2})|(m{1,2})|(s{1,2})|(S{1,4})|./g,qH={1:({token:e,...t})=>new XH({token:e.replace(/\\(.)/g,`$1`),...t}),2:e=>new tU(e),3:e=>new nU(e),4:e=>new rU(e),5:e=>new iU(e),6:e=>new eU(e),7:e=>new $H(e),8:e=>new QH(e),9:e=>new ZH(e)},JH=(e,t,n)=>{let r=[],i=null;for(;i=KH.exec(e);){let e=i.shift(),a=i.findIndex(e=>e!==void 0);if(a in qH){let o=i[a]||e;r.push(qH[a]({token:o,date:t,parts:r,locales:n}))}else r.push(new XH({token:i[a]||e,date:t,parts:r,locales:n}))}let a=r.reduce((e,t)=>{let n=e[e.length-1];return t.isToken()&&n!==void 0&&n.isToken()?n.setValue(t.token):e.push(t),e},Ai());return r.splice(0,r.length,...a),r};var YH=class{token;date;parts;locales;constructor(e){this.token=e.token,this.locales=e.locales,this.date=e.date||new globalThis.Date,this.parts=e.parts||[this]}isToken(){return!1}nextPart(){let e=Ir(Ei(this.parts,e=>e===this),()=>0);return Di(this.parts.slice(e+1),e=>!e.isToken())}previousPart(){let e=Ei(this.parts,e=>e===this);return Pr(e)?Oi(this.parts.slice(0,e.value),e=>!e.isToken()):D()}toString(){return globalThis.String(this.date)}},XH=class extends YH{increment(){}decrement(){}setValue(e){this.token+=e}isToken(){return!0}toString(){return this.token}},ZH=class extends YH{increment(){this.date.setMilliseconds(this.date.getMilliseconds()+1)}decrement(){this.date.setMilliseconds(this.date.getMilliseconds()-1)}setValue(e){this.date.setMilliseconds(globalThis.Number.parseInt(e.slice(-this.token.length)))}toString(){return`${this.date.getMilliseconds()}`.padStart(4,`0`).substring(0,this.token.length)}},QH=class extends YH{increment(){this.date.setSeconds(this.date.getSeconds()+1)}decrement(){this.date.setSeconds(this.date.getSeconds()-1)}setValue(e){this.date.setSeconds(globalThis.Number.parseInt(e.slice(-2)))}toString(){let e=`${this.date.getSeconds()}`;return this.token.length>1?e.padStart(2,`0`):e}},$H=class extends YH{increment(){this.date.setMinutes(this.date.getMinutes()+1)}decrement(){this.date.setMinutes(this.date.getMinutes()-1)}setValue(e){this.date.setMinutes(globalThis.Number.parseInt(e.slice(-2)))}toString(){let e=`${this.date.getMinutes()}`;return this.token.length>1?e.padStart(2,`0`):e}},eU=class extends YH{increment(){this.date.setHours(this.date.getHours()+1)}decrement(){this.date.setHours(this.date.getHours()-1)}setValue(e){this.date.setHours(globalThis.Number.parseInt(e.slice(-2)))}toString(){let e=/h/.test(this.token)?this.date.getHours()%12||12:this.date.getHours();return this.token.length>1?`${e}`.padStart(2,`0`):`${e}`}},tU=class extends YH{increment(){this.date.setDate(this.date.getDate()+1)}decrement(){this.date.setDate(this.date.getDate()-1)}setValue(e){this.date.setDate(globalThis.Number.parseInt(e.slice(-2)))}toString(){let e=this.date.getDate(),t=this.date.getDay();switch(this.token){case`DD`:return`${e}`.padStart(2,`0`);case`Do`:return`${e}${this.ordinalIndicator(e)}`;case`d`:return`${t+1}`;case`ddd`:return this.locales.weekdaysShort[t];case`dddd`:return this.locales.weekdays[t];default:return`${e}`}}ordinalIndicator(e){if(e>=11&&e<=13)return`th`;switch(e%10){case 1:return`st`;case 2:return`nd`;case 3:return`rd`;default:return`th`}}},nU=class extends YH{increment(){this.date.setMonth(this.date.getMonth()+1)}decrement(){this.date.setMonth(this.date.getMonth()-1)}setValue(e){let t=globalThis.Number.parseInt(e.slice(-2)
Line truncated
|
||||||
|
`))}})},oU=(e,t,n,r)=>{if(!e.disabled&&e.description&&t){let t=n.descriptionSeparator+e.description;return r?.plain===!0?t:VV(t,n.mutedColor)}return``},sU=e=>z(function*(t,n){let r=yield*(yield*Zz).columns,i=yield*XV(e),a=lU(t,i.pointerSmall,i,{plain:!0}),o=uU(t,i.prefix,i.pointerSmall,e,i,{plain:!0});return xH(o,r,a)+`\x1B[2K\x1B[G`}),cU=(e,t,n,r)=>{let i=e.value===``?``:`${e.value}`;if(t||r?.plain===!0)return i;let a=Pr(e.error)?n.errorColor:HV(PV,n.primaryColor);return VV(i,a)},lU=(e,t,n,r)=>Pr(e.error)?gi(e.error.value.split(SH),{onEmpty:()=>``,onNonEmpty:e=>{if(r?.plain===!0)return eH(t,e.join(`
|
||||||
|
`));let i=$V(t,n.errorColor),a=Mi(e,e=>QV(e,n.errorColor));return`\x1B[s
|
||||||
|
`+eH(i,a.join(`
|
||||||
|
`))+`\x1B[u`}}):``,uU=(e,t,n,r,i,a,o=!1)=>{let s=cU(e,o,i,a);return aU(s,r.message,t,n,a)},dU=/*#__PURE__*/ z(function*(e,t){let n=yield*XV(t),r=$V(n.prefix,n.primaryColor),i=$V(n.pointerSmall,n.mutedColor),a=lU(e,n.pointerSmall,n);return uU(e,r,i,t,n)+a}),fU=/*#__PURE__*/ z(function*(e,t){let n=yield*XV(t),r=$V(n.tick,n.successColor),i=$V(n.ellipsis,n.mutedColor);return uU(e,r,i,t,n,void 0,!0)+`
|
||||||
|
`}),pU=e=>{if(e.value.length<=0)return j(q.Beep());let t=e.value.slice(0,e.value.length-1);return j(q.NextFrame({state:{...e,value:t,error:D()}}))},mU=e=>j(q.NextFrame({state:{...e,cursor:0,value:``,error:D()}})),hU=(e,t)=>{if(t.value.length===0&&e===`-`)return j(q.NextFrame({state:{...t,value:`-`,error:D()}}));let n=globalThis.Number.parseInt(t.value+e);return globalThis.Number.isNaN(n)?j(q.Beep()):j(q.NextFrame({state:{...t,value:`${n}`,error:D()}}))},gU=(e,t)=>{if(e===`.`&&t.value.includes(`.`))return j(q.Beep());if(t.value.length===0&&e===`-`)return j(q.NextFrame({state:{...t,value:`-`,error:D()}}));let n=globalThis.Number.parseFloat(t.value+e);return globalThis.Number.isNaN(n)?j(q.Beep()):j(q.NextFrame({state:{...t,value:e===`.`?`${n}.`:t.value.includes(`.`)&&/^\d$/.test(e)?t.value+e:`${n}`,error:D()}}))},_U=e=>(t,n)=>q.$match(n,{Beep:()=>j(`\x07`),NextFrame:({state:t})=>dU(t,e),Submit:()=>fU(t,e)}),vU=e=>(t,n)=>{if(t.key.ctrl&&t.key.name===`u`)return mU(n);switch(t.key.name){case`backspace`:return pU(n);case`k`:case`up`:return j(q.NextFrame({state:{...n,value:n.value===``||n.value===`-`?`${e.incrementBy}`:`${globalThis.Number.parseInt(n.value)+e.incrementBy}`,error:D()}}));case`j`:case`down`:return j(q.NextFrame({state:{...n,value:n.value===``||n.value===`-`?`-${e.decrementBy}`:`${globalThis.Number.parseInt(n.value)-e.decrementBy}`,error:D()}}));case`enter`:case`return`:{let t=globalThis.Number.parseInt(n.value);return globalThis.Number.isNaN(t)?j(q.NextFrame({state:{...n,error:O(`Must provide an integer value`)}})):Fh(e.validate(t),{onFailure:e=>q.NextFrame({state:{...n,error:O(e)}}),onSuccess:e=>q.Submit({value:e})})}default:return hU(Ir(t.input,()=>``),n)}},yU=e=>(t,n)=>q.$match(n,{Beep:()=>j(`\x07`),NextFrame:({state:t})=>dU(t,e),Submit:()=>fU(t,e)}),bU=e=>(t,n)=>{if(t.key.ctrl&&t.key.name===`u`)return mU(n);switch(t.key.name){case`backspace`:return pU(n);case`k`:case`up`:return j(q.NextFrame({state:{...n,value:n.value===``||n.value===`-`?`${e.incrementBy}`:`${globalThis.Number.parseFloat(n.value)+e.incrementBy}`,error:D()}}));case`j`:case`down`:return j(q.NextFrame({state:{...n,value:n.value===``||n.value===`-`?`-${e.decrementBy}`:`${globalThis.Number.parseFloat(n.value)-e.decrementBy}`,error:D()}}));case`enter`:case`return`:{let t=globalThis.Number.parseFloat(n.value);return globalThis.Number.isNaN(t)?j(q.NextFrame({state:{...n,error:O(`Must provide a floating point value`)}})):I(N(()=>fm(t,e.precision)),t=>Fh(e.validate(t),{onFailure:e=>q.NextFrame({state:{...n,error:O(e)}}),onSuccess:e=>q.Submit({value:e})}))}default:return gU(Ir(t.input,()=>``),n)}},xU=(e,t,n,r)=>n.message===void 0?void 0:aU(``,n.message,e,t,r),SU=(e,t)=>e===void 0?t:e+`
|
||||||
|
`+t,CU=(e,t,n,r,i,a)=>{let o=tH(i,r===n.startIndex&&n.startIndex>0,r===n.endIndex-1&&n.endIndex<t.length);if(a?.plain===!0)return e===r?i.pointer+o:o+` `.repeat(i.pointer.length);if(t[r].disabled){let t=HV(NV,i.mutedColor);return e===r?$V(i.pointer,t)+o:o+` `.repeat(i.pointer.length)}return e===r?$V(i.pointer,i.primaryColor)+o:o+` `.repeat(i.pointer.length)},wU=(e,t,n,r)=>{if(r?.plain===!0)return e.title;let i=e.title;return t?e.disabled?VV(i,HV(PV,n.mutedColor)):VV(i,HV(PV,n.primaryColor)):e.disabled?VV(i,HV(`\x1B[9m`,n.mutedColor)):i},TU=(e,t,n,r)=>{let i=t.choices,a=sW(e,i.length,t.maxPerPage),o=[];for(let t=a.startIndex;t<a.endIndex;t++){let s=i[t],c=e===t,l=CU(e,i,a,t,n,r),u=wU(s,c,n,r),d=oU(s,c,n,r);o.push(l+u+` `+d)}return o.join(`
|
||||||
|
`)},EU=/*#__PURE__*/ z(function*(e,t){let n=yield*XV(t),r=TU(e,t,n),i=$V(n.prefix,n.primaryColor),a=$V(n.pointerSmall,n.mutedColor),o=xU(i,a,t);return FV+SU(o,r)}),DU=/*#__PURE__*/ z(function*(e,t){let n=yield*XV(t),r=t.choices[e].title,i=$V(n.tick,n.successColor),a=$V(n.ellipsis,n.mutedColor);return(xU(i,a,t)??i)+` `+VV(r,n.submittedColor)+`
|
||||||
|
`}),OU=(e,t)=>j(e===0?q.NextFrame({state:t.length-1}):q.NextFrame({state:e-1})),kU=(e,t)=>e===t.length-1?j(q.NextFrame({state:0})):j(q.NextFrame({state:e+1})),AU=(e,t)=>j(q.NextFrame({state:(e+1)%t.length})),jU=e=>(t,n)=>q.$match(n,{Beep:()=>j(`\x07`),NextFrame:({state:t})=>EU(t,e),Submit:()=>DU(t,e)}),MU=e=>z(function*(t,n){let r=yield*(yield*Zz).columns,i=yield*XV(e),a=xU(i.prefix,i.pointerSmall,e,{plain:!0}),o=TU(t,e,i,{plain:!0});return yH(SU(a,o),r)+`\x1B[2K\x1B[G`}),NU=e=>(t,n)=>{switch(t.key.name){case`k`:case`up`:return OU(n,e.choices);case`j`:case`down`:return kU(n,e.choices);case`tab`:return AU(n,e.choices);case`enter`:case`return`:{let t=e.choices[n];return t.disabled?j(q.Beep()):j(q.Submit({value:t.value}))}default:return j(q.Beep())}},PU=/*#__PURE__*/ z(function*(e,t){let n=yield*(yield*Zz).columns,r=yield*XV(t),i=IU(e,r.pointerSmall,r,{plain:!0});return xH(LU(e,r.prefix,r.pointerSmall,t,r,{plain:!0}),n,i)+`\x1B[2K\x1B[G`}),FU=(e,t,n,r,i)=>{let a=e.value;if(i?.plain===!0)switch(t.type){case`hidden`:return``;case`password`:return n.passwordMask.repeat(a.length);case`text`:return a}if(a.length===0)return``;let o=Pr(e.error)?n.errorColor:r?n.submittedColor:HV(PV,n.primaryColor);switch(t.type){case`hidden`:return``;case`password`:return $V(n.passwordMask.repeat(a.length),o);case`text`:return VV(a,o)}},IU=(e,t,n,r)=>Pr(e.error)?gi(e.error.value.split(SH),{onEmpty:()=>``,onNonEmpty:e=>{if(r?.plain===!0)return eH(t,e.join(`
|
||||||
|
`));let i=$V(t,n.errorColor),a=Mi(e,e=>QV(e,n.errorColor));return`\x1B[s
|
||||||
|
`+eH(i,a.join(`
|
||||||
|
`))+`\x1B[u`}}):``,LU=(e,t,n,r,i,a,o=!1)=>{let s=FU(e,r,i,o,a);return aU(s,r.message,t,n,a)},RU=/*#__PURE__*/ z(function*(e,t){let n=yield*XV(t),r=$V(n.prefix,n.primaryColor),i=$V(n.pointerSmall,n.mutedColor),a=LU(e,r,i,t,n),o=IU(e,n.pointerSmall,n),s=t.type===`password`?n.passwordMask.length:1,c=(e.cursor-e.value.length)*s;return a+o+GV(c)}),zU=/*#__PURE__*/ z(function*(e,t){let n=yield*XV(t),r=$V(n.tick,n.successColor),i=$V(n.ellipsis,n.mutedColor);return LU(e,r,i,t,n,void 0,!0)+`
|
||||||
|
`}),BU=e=>{if(e.cursor<=0)return j(q.Beep());let t=e.value.slice(0,e.cursor-1),n=e.value.slice(e.cursor),r=e.cursor-1,i=`${t}${n}`;return j(q.NextFrame({state:{...e,cursor:r,value:i,error:D()}}))},VU=e=>j(q.NextFrame({state:{...e,cursor:0,value:``,error:D()}})),HU=e=>{if(e.cursor<=0)return j(q.Beep());let t=e.cursor-1;return j(q.NextFrame({state:{...e,cursor:t,error:D()}}))},UU=e=>{if(e.cursor>=e.value.length)return j(q.Beep());let t=Math.min(e.cursor+1,e.value.length);return j(q.NextFrame({state:{...e,cursor:t,error:D()}}))},WU=e=>j(q.NextFrame({state:{...e,cursor:0,error:D()}})),GU=e=>j(q.NextFrame({state:{...e,cursor:e.value.length,error:D()}})),KU=(e,t)=>{if(e.value===t.default)return j(q.Beep());let n=e.value.length===0?t.default:e.value;return j(q.NextFrame({state:{...e,value:n,cursor:n.length,error:D()}}))},qU=(e,t)=>{let n=`${t.value.slice(0,t.cursor)}${e}${t.value.slice(t.cursor)}`,r=t.cursor+e.length;return j(q.NextFrame({state:{...t,cursor:r,value:n,error:D()}}))},JU=e=>(t,n)=>q.$match(n,{Beep:()=>j(`\x07`),NextFrame:({state:t})=>RU(t,e),Submit:()=>zU(t,e)}),YU=e=>(t,n)=>{if(t.key.ctrl)switch(t.key.name){case`u`:return VU(n);case`a`:return WU(n);case`e`:return GU(n);default:return j(q.Beep())}switch(t.key.name){case`backspace`:return BU(n);case`left`:return HU(n);case`right`:return UU(n);case`home`:return WU(n);case`end`:return GU(n);case`enter`:case`return`:{let t=n.value;return Fh(e.validate(t),{onFailure:e=>q.NextFrame({state:{...n,value:t,error:O(e)}}),onSuccess:e=>q.Submit({value:e})})}case`tab`:return KU(n,e);default:return qU(Ir(t.input,()=>``),n)}},XU=e=>(t,n)=>PU(t,e),ZU=(e,t)=>{let n={default:``,type:t,validate:j,...e},r={cursor:n.default.length,value:n.default,error:D()};return rH(r,{render:JU(n),process:YU(n),clear:XU(n)})},QU=/*#__PURE__*/ z(function*(e){let t=yield*(yield*Zz).columns,n=yield*XV(e),r=e.active+` `+eH(n.toggleSeparator,e.inactive),i=aU(r,e.message,n.prefix,n.pointerSmall,{plain:!0});return yH(i,t)+`\x1B[2K\x1B[G`}),$U=(e,t,n,r=!1)=>{let i=$V(n.toggleSeparator,n.mutedColor),a=HV(PV,r?n.submittedColor:n.primaryColor),o=e?t.inactive:VV(t.inactive,a);return(e?VV(t.active,a):t.active)+` `+eH(i,o)},eW=(e,t,n,r)=>aU(e,r.message,t,n),tW=/*#__PURE__*/ z(function*(e,t){let n=yield*XV(t),r=$V(n.prefix,n.primaryColor),i=$V(n.pointerSmall,n.mutedColor),a=$U(e,t,n),o=eW(a,r,i,t);return FV+o}),nW=/*#__PURE__*/ z(function*(e,t){let n=yield*XV(t),r=$V(n.tick,n.successColor),i=$V(n.ellipsis,n.mutedColor),a=$U(e,t,n,!0);return eW(a,r,i,t)+`
|
||||||
|
`}),rW=/*#__PURE__*/ j(/*#__PURE__*/ q.NextFrame({state:!0})),iW=/*#__PURE__*/ j(/*#__PURE__*/ q.NextFrame({state:!1})),aW=e=>(t,n)=>{switch(n._tag){case`Beep`:return j(`\x07`);case`NextFrame`:return tW(t,e);case`Submit`:return nW(t,e)}},oW=(e,t)=>{switch(e.key.name){case`0`:case`j`:case`delete`:case`right`:case`down`:return iW;case`1`:case`k`:case`left`:case`up`:return rW;case` `:case`tab`:return t?iW:rW;case`enter`:case`return`:return j(q.Submit({value:t}));default:return j(q.Beep())}},sW=(e,t,n)=>{let r=n===void 0?t:n,i=Math.min(t-r,e-Math.floor(r/2));i<0&&(i=0);let a=Math.min(i+r,t);return{startIndex:i,endIndex:a}},cW=`~effect/cli/Param`,lW=`flag`,uW={[cW]:{_A:w},pipe(){return S(this,arguments)}},dW=e=>E(e,cW),fW=e=>e.kind===`flag`,pW=e=>{let t=t=>e.kind===`argument`?PW(e.name,e.primitiveType,t):FW(e.name,e.primitiveType,t);return Object.setPrototypeOf({_tag:`Single`,...e,description:e.description??D(),aliases:e.aliases??[],hidden:e.hidden??!1,parse:t},uW)},mW=(e,t)=>pW({name:t,primitiveType:EV,kind:e}),hW=(e,t)=>pW({name:t,primitiveType:wV,kind:e}),gW=(e,t)=>pW({name:t,primitiveType:TV,kind:e}),_W=(e,t,n)=>pW({name:t,primitiveType:DV(n),kind:e}),vW=(e,t,n)=>{let r=n.map(e=>[e,e]);return _W(e,t,r)},yW=(e,t,n)=>pW({name:t,kind:e,primitiveType:OV(n?.pathType??`either`,n?.mustExist),typeName:n?.typeName}),bW=(e,t,n)=>yW(e,t,{pathType:`directory`,typeName:`directory`,mustExist:n?.mustExist}),xW=(e,t)=>TW(AW(pW({name:t,primitiveType:kV,kind:e}),{min:1}),e=>Object.fromEntries(e.flatMap(Object.entries))),SW=/^-+/,CW=/*#__PURE__*/ C(2,(e,t)=>zW(e,e=>pW({...e,aliases:[...e.aliases,t.replace(SW,``)]}))),wW=/*#__PURE__*/ C(2,(e,t)=>zW(e,e=>pW({...e,description:O(t)}))),TW=/*#__PURE__*/ C(2,(e,t)=>Object.assign(Object.create(uW),{_tag:`Map`,kind:e.kind,param:e,f:t,parse:n=>L(e.parse(n),([e,n])=>[e,t(n)])})),EW=(e,t,n=[])=>{let r=n.map(e=>()=>e().parse);return Object.assign(Object.create(uW),{_tag:`Transform`,kind:e.kind,param:e,alternatives:n,f:t,parse:t(e.parse,r)})},DW=/*#__PURE__*/ C(2,(e,t)=>EW(e,e=>n=>I(e(n),([e,n])=>t(n).pipe(L(t=>[e,t]))))),OW=e=>Object.assign(Object.create(uW),{_tag:`Optional`,kind:e.kind,param:e,parse:t=>e.parse(t).pipe(L(([e,t])=>[e,O(t)]),_h({MissingOption:()=>j([t.arguments,D()]),MissingArgument:()=>j([t.arguments,D()])}))}),kW=/*#__PURE__*/ C(2,(e,t)=>Um(t)?DW(OW(e),Fr({onNone:()=>t,onSome:j})):TW(OW(e),Ir(()=>t))),AW=(e,t)=>{let n=VW(e);return Object.assign(Object.create(uW),{_tag:`Variadic`,kind:e.kind,param:e,min:Rr(t?.min),max:Rr(t?.max),parse:r=>n.kind===`argument`?IW(e,n,r,t):LW(e,n,r,t)})},jW=/*#__PURE__*/ C(2,(e,t)=>zW(e,e=>pW({...e,typeName:t}))),MW=/*#__PURE__*/ C(2,(e,t)=>{let n=nj(t);return DW(e,t=>R(n(t),n=>{let r=VW(e);return new mV({option:r.name,value:globalThis.String(t),expected:`Schema validation failed: ${n.message}`,kind:r.kind})}))}),NW=/*#__PURE__*/ C(2,(e,t)=>EW(e,(e,t)=>n=>hh(e(n),()=>t[0]()(n)),[t])),PW=/*#__PURE__*/ z(function*(e,t,n){if(n.arguments.length===0)return yield*new fV({argument:e});let r=n.arguments[0],i=yield*R(t.parse(r),t=>new mV({option:e,value:r,expected:t,kind:`argument`}));return[n.arguments.slice(1),i]}),FW=/*#__PURE__*/ z(function*(e,t,n){let r=n.flags[e];if(r===void 0||r.length===0)return yield*new dV({option:e});let i=r[0],a=yield*R(t.parse(i),t=>new mV({option:e,value:i,expected:t,kind:`flag`}));return[n.arguments,a]}),IW=/*#__PURE__*/ z(function*(e,t,n,r){let i=[],a=r?.min??0,o=r?.max??1/0,s=0,c=n.arguments;for(;c.length>0&&s<o;){let[t,r]=yield*e.parse({flags:n.flags,arguments:c});i.push(r),c=t,s++}return s<a?yield*s===0?new fV({argument:t.name}):new mV({option:t.name,value:`${s} values`,expected:`at least ${a} value${a===1?``:`s`}`,kind:t.kind}):[c,i]}),LW=/*#__PURE__*/ z(function*(e,t,n,r){let i=[],a=[t.name,...t.aliases].flatMap(e=>n.flags[e]??[]),o=a.length;if(ke(r?.min)&&o<r.min)return yield*o===0?new dV({option:t.name}):new mV({option:t.name,value:`${o} occurrences`,expected:`at least ${r.min} value${r.min===1?``:`s`}`,kind:t.kind});if(ke(r?.max)&&o>r.max)return yield*new mV({option:t.name,value:`${o} occurrences`,expected:`at most ${r.max} value${r.max===1?``:`s`}`,kind:t.kind});for(let n of a){let[,r]=yield*e.parse({flags:{[t.name]:[n]},arguments:[]});i.push(r)}return[n.arguments,i]}),RW=(e,t)=>{let n=e;switch(n._tag){case`Single`:return t.Single(n);case`Map`:return t.Map(n);case`Transform`:return t.Transform(n);case`Optional`:return t.Optional(n);case`Variadic`:return t.Variadic(n)}},zW=(e,t)=>RW(e,{Single:e=>t(e),Map:e=>TW(zW(e.param,t),e.f),Transform:e=>EW(zW(e.param,t),e.f,e.alternatives.map(e=>()=>zW(e(),t))),Optional:e=>OW(zW(e.param,t)),Variadic:e=>AW(zW(e.param,t),{min:zr(e.min),max:zr(e.max)})}),BW=e=>RW(e,{Single:e=>[e],Map:e=>BW(e.param),Transform:e=>[...BW(e.param),...e.alternatives.flatMap(e=>BW(e()))],Optional:e=>BW(e.param),Variadic:e=>BW(e.param)}),VW=e=>{let t=BW(e);if(t.length===0)throw Error(`No Single param found in param structure`);if(t.length>1)throw Error(`Multiple Single params found: ${t.map(e=>e.namLine truncated
|
||||||
|
`)},formatVersion:(e,t)=>`${i.bold(e)} ${i.dim(`v`)}${i.bold(t)}`}},YW=e=>e.replace(/\u001B\[[0-9;]*m/g,``),XW=/*#__PURE__*/ new Intl.Segmenter(void 0,{granularity:`grapheme`}),ZW=/[\p{Mark}\p{Control}\p{Default_Ignorable_Code_Point}]/gu,QW=/\p{Emoji_Presentation}/u,$W=e=>e<4352?!1:e<=4447||e===9001||e===9002||e>=11904&&e<=12350||e>=12352&&e<=42191||e>=44032&&e<=55203||e>=63744&&e<=64255||e>=65040&&e<=65049||e>=65072&&e<=65135||e>=65280&&e<=65376||e>=65504&&e<=65510||e>=110592&&e<=111359||e>=127488&&e<=127569||e>=131072&&e<=262141,eG=e=>{let t=e.replace(ZW,``);return t.length===0?0:QW.test(e)||e.includes(`️`)||$W(t.codePointAt(0))?2:1},tG=e=>{let t=0;for(let{segment:n}of XW.segment(YW(e)))t+=eG(n);return t},nG=(e,t)=>{let n=tG(e),r=Math.max(0,t-n);return e+` `.repeat(r)},rG=(e,t)=>{let n=Math.max(...e.map(e=>tG(e.left)))+4,r=t===void 0?n:Math.min(n,t);return e.map(({left:e,right:t})=>` ${nG(e,Math.max(r,tG(e)+1))}${t}`).join(`
|
||||||
|
`)},iG=(e,t)=>t?`${e}, ${t}`:e,aG=(e,t)=>{let n=[];if(e.description&&(n.push(t.bold(`DESCRIPTION`)),n.push(` ${e.description}`),n.push(``)),n.push(t.bold(`USAGE`)),n.push(` ${t.cyan(e.usage)}`),n.push(``),e.args&&e.args.length>0){n.push(t.bold(`ARGUMENTS`));let r=e.args.map(e=>{let n=e.name;e.variadic&&(n+=`...`);let r=`${t.green(n)} ${t.dim(e.type)}`,i=e.required?``:t.dim(` (optional)`);return{left:r,right:Ir(e.description,()=>``)+i}});n.push(rG(r,25)),n.push(``)}if(e.flags.length>0){n.push(t.bold(`FLAGS`));let r=e.flags.map(e=>{let n=[];n.push(t.green(`--${e.name}`));for(let r of e.aliases)n.push(t.green(r));return{left:n.join(`, `)+(e.type===`boolean`?``:` ${t.dim(e.type)}`),right:Ir(e.description,()=>``)}});n.push(rG(r)),n.push(``)}if(e.globalFlags&&e.globalFlags.length>0){n.push(t.bold(`GLOBAL FLAGS`));let r=e.globalFlags.map(e=>{let n=[];n.push(t.green(`--${e.name}`));for(let r of e.aliases)n.push(t.green(r));return{left:n.join(`, `)+(e.type===`boolean`?``:` ${t.dim(e.type)}`),right:Ir(e.description,()=>``)}});n.push(rG(r)),n.push(``)}if(e.subcommands&&e.subcommands.length>0){let r=e.subcommands.find(e=>e.group===void 0);r&&(n.push(t.bold(`SUBCOMMANDS`)),n.push(rG(r.commands.map(e=>({left:t.cyan(iG(e.name,e.alias)),right:e.shortDescription??e.description})),20)),e.subcommands.length>1&&n.push(``));for(let r of e.subcommands)r.group!==void 0&&(n.push(t.bold(`${r.group}:`)),n.push(rG(r.commands.map(e=>({left:t.cyan(iG(e.name,e.alias)),right:e.shortDescription??e.description})),20)),n.push(``))}if(e.examples&&e.examples.length>0){n.push(t.bold(`EXAMPLES`));let r=!0,i=!1;for(let a of e.examples)a.description?(r||n.push(``),n.push(` ${t.dim(`# ${a.description}`)}`)):i&&n.push(``),n.push(` ${t.cyan(a.command)}`),r=!1,i=!!a.description;n.push(``)}return n[n.length-1]===``&&n.pop(),n.join(`
|
||||||
|
`)},oG=e=>e.replace(/'/g,`'\\''`),sG=e=>e.replace(/[^a-zA-Z0-9_]/g,`_`),cG=e=>{let t=[`--${e.name}`];for(let n of e.aliases)t.push(n.length===1?`-${n}`:`--${n}`);return e.type._tag===`Boolean`&&t.push(`--no-${e.name}`),t},lG=(e,t)=>{if(e.length===0)return;let n=e.map(cG);t.push(` local ${n.map((e,t)=>`_used_${t}=""`).join(` `)}`),t.push(` for ((i = 1; i < cword; i++)); do`),t.push(' case "${words[i]%%=*}" in'),n.forEach((e,n)=>{t.push(` ${e.join(`|`)}) _used_${n}=1 ;;`)}),t.push(` esac`),t.push(` done`),t.push(` local _filtered_flags=""`),n.forEach((e,n)=>{t.push(` [[ -n "$_used_${n}" ]] || _filtered_flags+=" ${e.join(` `)}"`)}),t.push(``)},uG=(e,t)=>{t.push(`${e}()`),t.push(`{`),t.push(` local _cur="$1" _word="$2"; shift 2`),t.push(``),t.push(' local _head="${_cur%"$_word"}"'),t.push(` local _open=""`),t.push(` case "$_head" in`),t.push(` *\\') _open="'" ;;`),t.push(` *\\") _open='"' ;;`),t.push(` esac`),t.push(``),t.push(` local _prefix="$_cur" _committed="$_head"`),t.push(" _prefix=${_prefix//\\\\/}; _prefix=${_prefix//\\\"/}; _prefix=${_prefix//\\'/}"),t.push(" _committed=${_committed//\\\\/}; _committed=${_committed//\\\"/}; _committed=${_committed//\\'/}"),t.push(``),t.push(` COMPREPLY=()`),t.push(` local _choice _rest _match`),t.push(` for _choice in "$@"; do`),t.push(` [[ "$_choice" == "$_prefix"* ]] || continue`),t.push(' _rest="${_choice#"$_committed"}"'),t.push(` case "$_open" in`),t.push(` "'")`),t.push(` if [[ "$_head" == "'" ]]; then`),t.push(" _match=${_rest//\\'/\\'\\\\\\'\\'}"),t.push(` else`),t.push(` [[ "$_rest" == *\\'* ]] && continue`),t.push(` _match="$_rest"`),t.push(` fi`),t.push(` ;;`),t.push(` '"')`),t.push(' _match="${_rest//\\\\/\\\\\\\\}"'),t.push(' _match="${_match//\\$/\\\\$}"'),t.push(' _match="${_match//\\`/\\\\\\`}"'),t.push(' _match="${_match//\\"/\\\\\\"}"'),t.push(` ;;`),t.push(` *)`),t.push(` printf -v _match '%q' "$_rest"`),t.push(` [[ -z "$_head" && "$_match" == '~'* ]] && _match="\\\\$_match"`),t.push(` ;;`),t.push(` esac`),t.push(` [[ -n "$_open" && "$_match" == *"$_open" ]] && _match+="$_open"`),t.push(` COMPREPLY+=("$_match")`),t.push(` done`),t.push(`}`),t.push(``)},dG=(e,t)=>`${e} "$cur" "$_comp_word" ${t.map(e=>`'${oG(e)}'`).join(` `)}`,fG=(e,t)=>{switch(e._tag){case`Boolean`:return;case`Choice`:return dG(t,e.values);case`Path`:return e.pathType===`directory`?`COMPREPLY=( $(compgen -d -- "$cur") )`:`COMPREPLY=( $(compgen -f -- "$cur") )`;default:return}},pG=(e,t)=>{switch(e._tag){case`Choice`:return dG(t,e.values);case`Path`:return e.pathType===`directory`?`COMPREPLY=( $(compgen -d -- "$cur") )`:`COMPREPLY=( $(compgen -f -- "$cur") )`;default:return}},mG=(e,t,n,r)=>{let i=[...t,e.name],a=`_${i.map(sG).join(`_`)}`;n.push(`${a}()`),n.push(`{`),n.push(` local cur prev words cword i`),n.push(t.length===0?` local _command_index=0`:` local _command_index="$1"`),n.push(` _init_completion -n "$COMP_WORDBREAKS" || return`),t.length===0&&n.push(` local _comp_word="$2"`),n.push(``);let o=e.flags.filter(e=>e.type._tag!==`Boolean`);if(o.length>0){n.push(` # Flag value completions`),n.push(` case "$prev" in`);for(let e of o){let t=[`--${e.name}`];for(let n of e.aliases)t.push(n.length===1?`-${n}`:`--${n}`);let i=fG(e.type,r);i&&(n.push(` ${t.join(`|`)})`),n.push(` ${i}`),n.push(` return`),n.push(` ;;`))}n.push(` esac`),n.push(``)}if(e.subcommands.length>0){n.push(` # Subcommand dispatch`),n.push(` local cmd _skip_next=0`),n.push(` for ((i = _command_index + 1; i < cword; i++)); do`),n.push(` if (( _skip_next )); then`),n.push(` _skip_next=0`),n.push(` continue`),n.push(` fi`),n.push(' case "${words[i]}" in');for(let t of e.flags){if(t.type._tag===`Boolean`)continue;let e=cG(t);n.push(` ${e.join(`|`)}) _skip_next=1 ;;`),n.push(` ${e.map(e=>`${e}=*`).join(`|`)}) ;;`)}for(let t of e.subcommands){let e=`_${[...i,t.name].map(sG).join(`_`)}`;n.push(` ${t.name})`),n.push(` ${e} "$i"`),n.push(` return`),n.push(` ;;`)}n.push(` esac`),n.push(` done`),n.push(``)}lG(e.flags,n),(e.flags.length>0||e.subcommands.length>0)&&(n.push(` # Complete flags (filtered) and subcommands`),n.push(` if [[ "$cur" == -* ]]; then`),e.flags.length>0&&n.push(` COMPREPLY=( $(compgen -W "$_filtered_flags" -- "$cur") )`),n.push(` return`),n.push(` fi`),n.push(``));let s=e.arguments.flatMap((e,t)=>{let n=pG(e.type,r);return n===void 0?[]:[{argument:e,completion:n,index:t}]});if(s.length>0){n.push(` # Positional argument completions`),n.push(` local _position=0 _skip_next=0 _end_of_options=0`),n.push(` for ((i = _command_index + 1; i < cword; i++)); do`),n.push(` if (( _skip_next )); then`),n.push(` _skip_next=0`),n.push(` continue`),n.push(` fi`),n.push(` if (( _end_of_options )); then`),n.push(` ((_position += 1)
Line truncated
|
||||||
|
`)},gG=e=>e.replace(/\\/g,`\\\\`).replace(/'/g,`\\'`),_G=e=>gG(e.replace(/[^A-Za-z0-9_.,/@%+-]/gu,`\\$&`)),vG=(e,t)=>{if(e.length===0)return t.length>0?`__fish_use_subcommand`:``;let n=e.map(e=>`__fish_seen_subcommand_from ${e}`).join(`; and `);return t.length>0?`${n}; and not __fish_seen_subcommand_from ${t.join(` `)}`:n},yG=e=>{let t=[];for(let n of e.aliases)n.length===1&&t.push(`-s ${n}`);t.push(e.name);for(let n of e.aliases)n.length>1&&t.push(n);return e.type._tag===`Boolean`&&t.push(`no-${e.name}`),`not __fish_contains_opt ${t.join(` `)}`},bG=e=>{let t=[`--${e.name}`];for(let n of e.aliases)t.push(n.length===1?`-${n}`:`--${n}`);return`begin; ${yG(e)}; or contains -- (commandline -poc)[-1] ${t.join(` `)}; end`},xG=e=>{let t=[`-l ${e.name}`];for(let n of e.aliases)n.length===1?t.push(`-s ${n}`):t.push(`-l ${n}`);e.description&&t.push(`-d '${gG(e.description)}'`);let n=SG(e.type);return n&&t.push(n),t},SG=e=>{switch(e._tag){case`Boolean`:return;case`Choice`:return`-r -f -a '${e.values.map(_G).join(` `)}'`;case`Path`:return e.pathType,`-r -F`;default:return`-r -f`}},CG=e=>{switch(e._tag){case`Choice`:return`-r -f -a '${e.values.map(_G).join(` `)}'`;case`Path`:return`-r -F`;default:return}},wG=(e,t,n,r)=>{let i=t.subcommands.map(e=>e.name),a=vG(n,i),o=a?`-n '${a}'`:``;if(!t.arguments.some(e=>e.type._tag===`Path`)){let t=[`complete -c ${e}`];o&&t.push(o),t.push(`-f`),r.push(t.join(` `))}for(let n of t.subcommands){let t=[`complete -c ${e}`];o&&t.push(o),t.push(`-f -a '${gG(n.name)}'`),n.description&&t.push(`-d '${gG(n.description)}'`),r.push(t.join(` `))}for(let n of t.flags){let t=n.type._tag===`Boolean`,i=t?yG(n):bG(n),o=a?`${a}; and ${i}`:i,s=o?`-n '${o}'`:``,c=[`complete -c ${e}`];if(s&&c.push(s),c.push(...xG(n)),r.push(c.join(` `)),t){let t=[`complete -c ${e}`];s&&t.push(s),t.push(`-l no-${n.name}`),n.description&&t.push(`-d '${gG(`Disable ${n.name}`)}'`),r.push(t.join(` `))}}if(t.flags.length>0){let n=`not string match -q -- "-*" (commandline -ct)`,i=a?`${a}; and ${n}`:n;for(let n of t.flags){let t=`${i}; and ${yG(n)}`,a=n.type._tag===`Boolean`,o=[`complete -c ${e}`];if(o.push(`-n '${t}'`),o.push(`-f -a '--${n.name}'`),n.description&&o.push(`-d '${gG(n.description)}'`),r.push(o.join(` `)),a){let i=[`complete -c ${e}`];i.push(`-n '${t}'`),i.push(`-f -a '--no-${n.name}'`),n.description&&i.push(`-d '${gG(`Disable ${n.name}`)}'`),r.push(i.join(` `))}}}for(let n of t.arguments){let t=CG(n.type);if(t){let i=[`complete -c ${e}`];o&&i.push(o),i.push(t),n.description&&i.push(`-d '${gG(n.description)}'`),r.push(i.join(` `))}}for(let i of t.subcommands)wG(e,i,[...n,i.name],r)},TG=(e,t)=>{let n=[];return n.push(`###-begin-${e}-completions-###`),n.push(`#`),n.push(`# Static completion script for Fish`),n.push(`#`),n.push(`# Installation:`),n.push(`# ${e} --completions fish > ~/.config/fish/completions/${e}.fish`),n.push(`#`),n.push(``),wG(e,t,[],n),n.push(``),n.push(`###-end-${e}-completions-###`),n.join(`
|
||||||
|
`)},EG=e=>e.replace(/\\/g,`\\\\`).replace(/'/g,`'\\''`).replace(/:/g,`\\:`),DG=e=>e.replace(/[^A-Za-z0-9_.,/@%+-]/gu,`\\$&`).replace(/'/g,`'\\''`),OG=e=>e.replace(/[^a-zA-Z0-9_]/g,`_`),kG=e=>{let t=[`--${e.name}`];for(let n of e.aliases)t.push(n.length===1?`-${n}`:`--${n}`);return e.type._tag===`Boolean`&&t.push(`--no-${e.name}`),t},AG=e=>{switch(e._tag){case`Boolean`:return``;case`Choice`:return`:value:(${e.values.map(DG).join(` `)})`;case`Path`:return e.pathType===`directory`?`:directory:_directories`:`:file:_files`;case`Int`:return`:integer:`;case`Finite`:return`:float:`;case`Date`:return`:date:`;default:return`:value:`}},jG=e=>{switch(e._tag){case`Choice`:return`(${e.values.map(DG).join(` `)})`;case`Path`:return e.pathType===`directory`?`_directories`:`_files`;default:return``}},MG=e=>{let t=[],n=e.description?`[${EG(e.description)}]`:``,r=AG(e.type),i=`(${kG(e).join(` `)})`;t.push(`'${i}--${e.name}${n}${r}'`);for(let a of e.aliases){let e=a.length===1?`-`:`--`;t.push(`'${i}${e}${a}${n}${r}'`)}if(e.type._tag===`Boolean`){let n=e.description?`[${EG(`Disable ${e.name}`)}]`:``;t.push(`'${i}--no-${e.name}${n}'`)}return t},NG=e=>{let t=e.description?EG(e.description):e.name,n=jG(e.type);return`'${e.variadic?`*`:``}:${t}:${n}'`},PG=(e,t,n)=>{let r=[...t,e.name],i=`_${r.map(OG).join(`_`)}`;if(n.push(`${i}() {`),e.subcommands.length>0){n.push(` local context state state_descr line`),n.push(` typeset -A opt_args`),n.push(``),n.push(` local -a commands`),n.push(` commands=(`);for(let t of e.subcommands){let e=t.description?EG(t.description):``;n.push(` '${t.name}:${e}'`)}n.push(` )`),n.push(``),n.push(` local -a specs`),n.push(` specs=(`);for(let t of e.flags)for(let e of MG(t))n.push(` ${e}`);if(e.arguments.length>0){n.push(` -`),n.push(` parent-arguments`);for(let t of e.arguments)n.push(` ${NG(t)}`);n.push(` -`),n.push(` subcommands`)}n.push(` '1:command:->command'`),n.push(` '*::arg:->args'`),n.push(` )`),n.push(``),n.push(' _arguments -C "${specs[@]}"'),n.push(``),n.push(` case "$state" in`),n.push(` command)`),n.push(` _describe -t commands 'commands' commands`),n.push(` ;;`),n.push(` args)`),n.push(` case "$words[1]" in`);for(let t of e.subcommands){let e=`_${[...r,t.name].map(OG).join(`_`)}`;n.push(` ${t.name})`),n.push(` ${e}`),n.push(` ;;`)}n.push(` esac`),n.push(` ;;`),n.push(` esac`)}else{let t=[];for(let n of e.flags)t.push(...MG(n));for(let n of e.arguments)t.push(NG(n));if(t.length>0){n.push(` local -a specs`),n.push(` specs=(`);for(let e of t)n.push(` ${e}`);n.push(` )`),n.push(' _arguments "${specs[@]}"')}}n.push(`}`),n.push(``);for(let t of e.subcommands)PG(t,r,n)},FG=(e,t)=>{let n=[],r=OG(e);return n.push(`#compdef ${e}`),n.push(`###-begin-${e}-completions-###`),n.push(`#`),n.push(`# Static completion script for Zsh`),n.push(`#`),n.push(`# Installation:`),n.push(`# ${e} --completions zsh > ~/.zsh/completions/_${e}`),n.push(`# then add ~/.zsh/completions to your fpath`),n.push(`#`),n.push(``),PG(t,[],n),n.push(`# Handle both direct invocation and autoload`),n.push('if [[ "${zsh_eval_context[-1]}" == "loadautofunc" ]]; then'),n.push(` _${r} "$@"`),n.push(`else`),n.push(` compdef _${r} ${e}`),n.push(`fi`),n.push(`###-end-${e}-completions-###`),n.join(`
|
||||||
|
`)},IG=(e,t,n)=>{switch(t){case`bash`:return hG(e,n);case`zsh`:return FG(e,n);case`fish`:return TG(e,n)}},LG=e=>mW(lW,e),RG=e=>hW(lW,e),zG=e=>gW(lW,e),BG=(e,t)=>_W(lW,e,t),VG=(e,t)=>vW(lW,e,t),HG=(e,t)=>bW(lW,e,t),UG=e=>xW(lW,e),WG=/*#__PURE__*/ C(2,(e,t)=>CW(e,t)),GG=/*#__PURE__*/ C(2,(e,t)=>wW(e,t)),KG=/*#__PURE__*/ C(2,(e,t)=>jW(e,t)),qG=e=>OW(e),JG=kW,YG=/*#__PURE__*/ C(2,(e,t)=>TW(e,t)),XG=/*#__PURE__*/ C(2,(e,t)=>NW(e,t)),ZG=/*#__PURE__*/ C(2,(e,t)=>MW(e,t)),QG=`~effect/cli/Command/Config/Internal`,$G=e=>{let t=[],n=[],r=[];function i(e){let t=Object.create(null);for(let n of Object.keys(e))t[n]=a(e[n]);return t}function a(e){if(Array.isArray(e))return{_tag:`Array`,children:e.map(e=>a(e))};if(dW(e)){let i=t.length;return t.push(e),e.kind===`argument`?r.push(e):n.push(e),{_tag:`Param`,index:i}}return{_tag:`Nested`,tree:i(e)}}return{[QG]:QG,flags:n,arguments:r,orderedParams:t,tree:i(e)}},eK=/*#__PURE__*/ $G({}),tK=(e,t)=>{let n={};for(let t of Object.keys(e))Kt(n,t,r(e[t]));return n;function r(e){switch(e._tag){case`Param`:return t[e.index];case`Array`:return e.children.map(e=>r(e));case`Nested`:return tK(e.tree,t)}}},nK=`~effect/cli/Command`,rK=e=>e,iK={.../*#__PURE__*/ Fi({label:`Command`,evaluate(){return rK(this).service}})},aK=e=>{let t=e.config,n=e.contextConfig??eK,r=e.service??Li(`~effect/cli/Command/${e.name}`),i=e.annotations??$i(),a=e.globalFlags??[],o=e.subcommands??[],s=(t,n)=>ke(e.handle)?e.handle(t,n):F(new _V({commandPath:n,errors:[]})),c=e.parse??cK(t),l=e.parseContext??cK(n,{allowLeftovers:!0}),u=n=>{let r=[],a=[];for(let e of t.arguments){let t=BW(e),n=HW(e);for(let e of t)r.push({name:e.name,type:e.typeName??AV(e.primitiveType),description:e.description,required:!n.isOptional&&(!n.isVariadic||Wr(n.variadicMin,e=>e>0)),variadic:n.isVariadic})}let s=n.length>0?n.join(` `):e.name;o.some(e=>e.commands.some(e=>!e.unlisted))&&(s+=` <subcommand>`),s+=` [flags]`;for(let e of r){let t=e.variadic?`<${e.name}...>`:`<${e.name}>`;s+=` ${e.required?t:`[${t}]`}`}for(let e of t.flags){let t=BW(e),n=HW(e);for(let e of t)e.hidden||a.push(oK(e,n))}let c=[];for(let e of o){let t=e.commands.filter(e=>!e.unlisted);t.length!==0&&c.push({group:e.group,commands:Mi(t,e=>({name:e.name,alias:e.alias,shortDescription:e.shortDescription,description:e.description??``}))})}let l=e.examples??[];return{description:e.description??``,usage:s,flags:a,annotations:i,...r.length>0&&{args:r},...c.length>0&&{subcommands:c},...l.length>0&&{examples:l}}};return Object.assign(Object.create(iK),{[nK]:nK,name:e.name,examples:e.examples??[],annotations:i,globalFlags:a,subcommands:o,unlisted:e.unlisted??!1,config:t,contextConfig:n,service:r,parse:c,parseContext:l,handle:s,buildHelpDoc:u,...ke(e.description)?{description:e.description}:{},...ke(e.shortDescription)?{shortDescription:e.shortDescription}:{},...ke(e.alias)?{alias:e.alias}:{}})},oK=(e,t)=>{let n=e.aliases.map(e=>e.length===1?`-${e}`:`--${e}`);return{name:e.name,aliases:n,type:e.typeName??AV(e.primitiveType),description:sK(e.description,jV(e.primitiveType)),required:e.primitiveType._tag!==`Boolean`&&!t.isOptional}},sK=(e,t)=>{if(t===void 0||t.length===0)return e;let n=`(choices: ${t.join(`, `)})`;return Fr(e,{onNone:()=>O(n),onSome:e=>O(`${e} ${n}`)})},cK=(e,t)=>z(function*(n){let r={flags:n.flags,arguments:n.arguments},[i,a]=yield*lK(r,e.orderedParams);return t?.allowLeftovers!==!0&&i.length>0?yield*new pV({arguments:i}):tK(e.tree,a)}),lK=/*#__PURE__*/ z(function*(e,t){let n=[],r=e.arguments;for(let i of t){let[t,a]=yield*i.parse({flags:e.flags,arguments:r});n.push(a),r=t}return[r,n]}),uK=e=>{switch(e.primitiveType._tag){case`Boolean`:return{_tag:`Boolean`};case`Int`:return{_tag:`Int`};case`Finite`:return{_tag:`Finite`};case`Date`:return{_tag:`Date`};case`Choice`:return{_tag:`Choice`,values:jV(e.primitiveType)??[]};case`Path`:return{_tag:`Path`,pathType:MV(e.primitiveType)??`either`};case`FileText`:case`FileParse`:case`FileSchema`:return{_tag:`Path`,pathType:`file`};default:return{_tag:`String`}}},dK=e=>{switch(e.primitiveType._tag){case`Int`:return{_tag:`Int`};case`Finite`:return{_tag:`Finite`};case`Date`:return{_tag:`Date`};case`Choice`:return{_tag:`Choice`,values:jV(e.primitiveType)??[]};case`Path`:return{_tag:`Path`,pathType:MV(e.primitiveType)??`either`};case`FileText`:case`FileParse`:case`FileSchema`:return{_tag:`Path`,pathType:`file`};default:return{_tag:`String`}}},fK=(e,t=[])=>{let n=rK(e),r=n.config,i=[],a=/* @__PURE__ */ new Set;for(let[e,n]of[...r.flags,...t].entries()){let t=BW(n);for(let n of t)n.kind===`flag`&&(n.hidden||e>=r.flags.length&&a.has(n.name)||(a.add(n.name),i.push({name:n.name,aliases:n.aliases,description:zr(n.description),type:uK(n)})))}let o=[];for(let e of r.arguments){let t=BW(e),n=HW(e);for(let e of t)e.kind===`argument`&&o.push({name:e.name,description:zr(e.description),required:!n.isOptional,variadic:n.isVariadic,type:dK(e)})}let s=[],c=[...t,...n.contextConfig.flags];for(let t of e.subcommands)for(let e of t.commands){if(e.unlisted)co
Line truncated
|
||||||
|
`),wq=e=>Eq(`Command ready`,e,BV,LV),Tq=()=>`\n${VV(`Wizard cancelled.`,IV)}`,Eq=(e,t,n,r=RV)=>{let i=Dq(t);return[VV(e,NV,r),...i.map(e=>VV(e,n)),``].join(`
|
||||||
|
`)},Dq=e=>{let t=` $ `,n=` `,r=e.map(Oq),i=[],a=t;for(let e of r){let r=a===t||a===n?``:` `;a.length+r.length+e.length>88&&a!==t?(i.push(`${a} \\`),a=`${n}${e}`):a+=`${r}${e}`}return a!==t&&i.push(a),i},Oq=e=>/^[A-Za-z0-9_./:@%+=,-]+$/.test(e)?e:`'${e.replaceAll(`'`,`'"'"'`)}'`,kq=(e,t,n)=>{let r=$G(t??{});return aK({name:e,config:r,...ke(n)?{handle:n}:{}})},Aq=/*#__PURE__*/ C(2,(e,t)=>aK({...rK(e),description:t})),jq=/*#__PURE__*/ C(2,(e,t)=>aK({...rK(e),examples:t})),Mq=(e,t,n,r)=>{let i=new Set(t),a=[],o=/* @__PURE__ */ new Set;for(let t of e){if(i.has(t))continue;let e=BW(t.flag);for(let t of e){let e=n[t.name];if(!e||e.length===0)continue;let i=`--${t.name}`;o.has(i)||(o.add(i),a.push(new lV({option:i,suggestions:[],command:r})))}}return a},Nq=(e,t,n)=>Zm(function*(){let{builtIns:r}=yield*kK,i=yield*KW,a=yield*yK(e,t.commandPath,r);yield*SB(i.formatHelpDoc(a)),n&&t.errors.length>0&&(yield*xB(i.formatErrors(t.errors)))}),Pq=e=>Zm(function*(){let t=yield*KW;yield*xB(t.formatError(e)),e[gB]=!1}),Fq=/*#__PURE__*/ C(2,(e,t)=>JL.use(({args:n})=>I(n,n=>Iq(e,t)(n)))),Iq=(e,t)=>{let n=rK(e);return z(function*(r){let{builtIns:i}=yield*kK,{tokens:a,trailingOperands:o}=jK(r),s=vK(e,i),c=s.flatMap(e=>BW(e.flag)),l=RK(c.filter(fW)),{flagMap:u,remainder:d,errors:f}=nq(a,e,l),p={flags:u,arguments:[]},m=yield*IK({tokens:d,trailingOperands:o},e),h=[e.name,...FK(m)],g={builtIns:i,command:e,commandPath:h,version:t.version},_=hK(e,h,i),v=Mq(s,_,u,h);if(v.length>0||f.length>0){let e=m.errors??[];return yield*new _V({commandPath:h,errors:[...f,...v,...e]})}for(let n of _){if(n._tag!==`Action`||!BW(n.flag).some(e=>{let t=u[e.name];return t!==void 0&&t.length>0}))continue;let[,i]=yield*n.flag.parse(p);if(n===TK)return yield*Zm(function*(){yield*SB(Cq(e.name,t.version,e.description));let n=[e.name,...r.filter(e=>e!==`--wizard`&&!e.startsWith(`--wizard=`))],i=yield*uq(e,{commandPath:h,prefix:n});yield*SB(wq(i.displayArgs)),(yield*uH(hH({message:`Run this command?`,initial:!0,active:`yes`,inactive:`no`})))&&(yield*SB(),yield*Iq(e,{...t,renderErrors:!1})(i.args.slice(1)))}).pipe(gh(`QuitError`,()=>SB(Tq())));yield*n.run(i,g);return}if(m.errors&&m.errors.length>0)return yield*new _V({commandPath:h,errors:m.errors});let ee=yield*lh(n.parse(m));if(ee._tag===`Failure`)return yield*new _V({commandPath:h,errors:[ee.failure]});let y=n.handle(ee.success,[e.name]),te=_.includes(DK)?(yield*DK.flag.parse(p))[1]:D();y=Gh(y,DK,te);for(let e of _){if(e._tag!==`Setting`||e===DK)continue;let[,t]=yield*e.flag.parse(p);y=Gh(y,e,t)}let b=Fr(te,{onNone:()=>$i(),onSome:e=>ta(Ff,e)});yield*Hh(y,b)},bh(e=>cV(e)&&e._tag===`ShowHelp`?Gr(e):Kr(e),n=>sh(Nq(e,n,t.renderErrors!==!1),F(n))),bh(e=>t.renderErrors!==!1&&cV(e)&&e._tag===`UserError`?Gr(e):Kr(e),e=>sh(Pq(e),F(e))),bh(e=>Xz(e)?Gr(e):Kr(e),e=>ng))},Lq=/*#__PURE__*/ H({method:Pj([`GET`,`POST`,`PATCH`,`PUT`,`DELETE`,`HEAD`,`OPTIONS`,`TRACE`]),url:V,urlParams:U(jj([V,V])),hash:pj(V),headers:Oj(V,W([V,EM(V)]))}).annotate({identifier:`HttpRequestDetails`}),Rq=/*#__PURE__*/ H({status:oM,headers:Oj(V,W([V,EM(V)]))}).annotate({identifier:`HttpResponseDetails`}),zq=`~effect/ai/AiError/Reason`,Bq=()=>Uq.pipe(Gj(j({})),qj(j({}))),Vq=e=>{let t=Ct(e),n={};for(let e in t){let r=t[e];Kt(n,e,yD(r)?r.toString():r)}return n};var Hq=class e extends (/*#__PURE__*/ FM(`effect/ai/AiError/NetworkError`)({_tag:/*#__PURE__*/ G(`NetworkError`),reason:/*#__PURE__*/ Pj([`TransportError`,`EncodeError`,`InvalidUrlError`]),request:Lq,description:/*#__PURE__*/ pj(V)})){[zq]=zq;get isRetryable(){return this.reason===`TransportError`}static fromRequestError(t){return new e({description:t.description,reason:t._tag,request:{hash:zr(t.request.hash),headers:Vq(t.request.headers),method:t.request.method,url:t.request.url,urlParams:Array.from(t.request.urlParams)}})}get message(){let e=`${this.request.method} ${this.request.url}`,t=this.description?`${this.reason}: ${this.description}`:`${this.reason}: A network error occurred.`;t+=` (${e})`;let n=``;switch(this.reason){case`EncodeError`:n+=`Check that the request body data is properly formatted and matches the expected content type.`;break;case`InvalidUrlError`:n+=`Verify that the URL format is correct and that all required parameters have been provided.`,n+=` Check for any special characters that may need encoding.`;break;case`TransportError`:n+=`Check your network connection and verify that the requested URL is accessible.`}return t+=`\n\n${n}`,t}};const Uq=/*#__PURE__*/ Oj(V,/*#__PURE__*/ Fj(HM)),Wq=/*#__PURE__*/ H({promptTokens:pj(oM),completionTokens:pj(oM),totalTokens:pj(oM)}).annotate({identifier:`UsageInfo`}),Gq=/*#__PURE__*/ H({request:Lq,response:pj(Rq),body:pj(V)}).annotate({identifier:`HttpContext`});var Kq=class extends (/*#__PURE__*/ FM(`effect/ai/AiError/RateLimitError`)({_tag:/*#__PURE__*/ G(`RateLimitError`),retryAfter:/*#__PURE__*/ pj(wM),metadata:/*#__PURE__*/ Bq(),http:/*#__PURE__*/ pj(Gq)})){[zq]=zq;get isRetryable(){return!0}get message(){let e=`Rate limit exceedLine truncated
|
||||||
|
`;let n=r.encode(e);return n===void 0?void 0:`${n}\n`}}}})},d0=e=>{let t=a0(e.protocols),n=nP({status:405,headers:{allow:`POST`}}),r=t=>p0(t,e.allowedOrigins)?j(n):j(nP({status:403})),i=hp(AI(`GET`,e.path,r),AI(`PUT`,e.path,r),AI(`PATCH`,e.path,r),AI(`DELETE`,e.path,r),AI(`OPTIONS`,e.path,r));return gp(c0(e),i).pipe(vp(f0(e)),vp(t),vp(LL()))},f0=e=>cp(tR)(Zm(function*(){let t=yield*r0,{httpEffect:n,protocol:r}=yield*nR();return yield*(yield*wI).add(`POST`,e.path,r=>{if(!p0(r,e.allowedOrigins))return j(nP({status:403}));if(m0(r.headers[`content-type`])[0]!==`application/json`)return j(nP({status:415}));let i=m0(r.headers.accept);if(!i.includes(`application/json`)||!i.includes(`text/event-stream`))return j(nP({status:406}));let a=r.headers[$1],o=r.headers[Q1],s=o===void 0?void 0:t.sessions.bySessionId.get(o);if(o!==void 0&&s===void 0)return j(nP({status:404}));let c=a!==void 0&&!t.protocolRegistry.protocols.some(e=>e.protocolVersion===a)||s?.protocol.transport.requiresVersionHeader===!0&&a!==s.protocol.protocolVersion,l=c?nP({status:400}):oP({jsonrpc:`2.0`,id:null,error:new eY({message:`Parse error`})});return r.text.pipe(I(nj(pM)),Rh({onFailure:()=>j(l),onSuccess:e=>{if(!Array.isArray(e)){let t=E(e,`id`),r=t&&(typeof e.id==`string`||typeof e.id==`number`)?e.id:null,i=E(e,`jsonrpc`)&&e.jsonrpc===`2.0`,a=!t||typeof e.id==`string`||typeof e.id==`number`,s=i&&a&&E(e,`method`)&&typeof e.method==`string`,l=t&&(typeof e.id==`string`||typeof e.id==`number`||e.id===null),u=E(e,`result`),d=E(e,`error`),f=i&&l&&u!==d,p=s&&g0(e);return!p&&c?j(nP({status:400})):!s&&!f?j(oP({jsonrpc:`2.0`,id:r,error:new tY({message:`Invalid Request`})})):p&&o!==void 0||!p&&s&&o===void 0?j(nP({status:400})):n}return c?j(nP({status:400})):e.length===0?j(oP({jsonrpc:`2.0`,id:null,error:new tY({message:`Invalid Request`})},{status:400})):e.some(g0)||s===void 0?j(nP({status:400})):s.protocol.transport.acceptsJsonRpcBatches?n:j(nP({status:400}))}}))}),r})),p0=(e,t)=>{let n=e.headers.origin;return n===void 0||(t??[]).includes(n)},m0=e=>e===void 0?[]:e.split(`,`).flatMap(e=>{let[t,...n]=e.split(`;`),r=n.map(e=>e.trim().toLowerCase()).find(e=>e.startsWith(`q=`));if(r!==void 0){let e=Number(r.slice(2));if(!Number.isFinite(e)||e<=0||e>1)return[]}return[t.trim().toLowerCase()]}),h0=/*#__PURE__*/ H({method:/*#__PURE__*/ B(`initialize`)}),g0=e=>Xr(rj(h0)(e)),_0=/*#__PURE__*/ H({params:/*#__PURE__*/ H({protocolVersion:V})}),v0=e=>{let t=rj(_0)(e);return Xr(t)?t.success.params.protocolVersion:void 0},y0=`Tool execution failed due to an internal server error.`,b0=e=>new EY({isError:!0,content:[{type:`text`,text:e}]}),x0=/*#__PURE__*/ z(function*(e){let t=yield*Z1,n=yield*e,r=yield*zh(),i=e=>Hh(XN(e),r);for(let e of Object.values(n.tools)){let a=e.annotations,o=ia(a,SJ),s=tj(e.failureSchema),c=yJ(e.successSchema),l=c.type===`object`?yield*nj(wY)(c).pipe(xh):void 0,u=yield*nj(wY)(vJ(e)).pipe(xh),d=new TY({name:e.name,description:_J(e),inputSchema:u,...l===void 0?{}:{outputSchema:l},annotations:{...da(e.annotations,xJ).pipe(Vr(e=>({title:e})),zr),readOnlyHint:sa(e.annotations,CJ),destructiveHint:sa(e.annotations,wJ),idempotentHint:sa(e.annotations,TJ),openWorldHint:sa(e.annotations,EJ)},_meta:o});yield*t.addTool({tool:d,annotations:a,handle(t){return n.handle(e.name,t??{}).pipe(Ub,ox(Xy()),I(ah),L(e=>new EY({isError:!1,structuredContent:J1(e.encodedResult),content:e.encodedResult===void 0?[]:[{type:`text`,text:JSON.stringify(e.encodedResult)}]})),Hh(r),Ch(Dg),vh(e=>{let t=Fp(e);if(Yr(t))return Ip(e)?fh(i(e),b0(y0)):Qm(t.failure);let n=t.success;if(lJ(n)){let t=n.reason;return t._tag===`ToolParameterValidationError`?F(new rY({message:t.message})):fh(i(e),b0(y0))}let r=s(n)&&n instanceof Error?n.message:y0;return fh(i(e),b0(r))}))}})}}),S0=function(){if(arguments.length===1){let e=arguments[0];return Zm(function*(){let t=yield*zh();yield*(yield*Z1).addResource({resource:new dY({...e,annotations:e}),handle:e.content.pipe(Hh(t),L(t=>D0(e.uri,t)),vh(e=>{let t=Bp(e)[0];return F(new iY({message:t.message}))})),annotations:e.annotations??$i()})})}let{params:e,routerPath:t,schema:n,uriPath:r}=w0(...arguments);return z(function*(i){let a=yield*zh(),o=yield*Z1,s=nj(n),c=new fY({...i,uriTemplate:r,annotations:i}),l=Object.create(null);for(let[t,n]of Object.entries(i.completion??{})){let r=oj(U(e[t]));l[t]=(e,t)=>n(e,t).pipe(I(r),L(e=>({completion:{values:e,total:e.length,hasMore:!1}})),vh(e=>{let t=Bp(e)[0];return F(new iY({message:t.message}))}),Hh(a))}yield*o.addResourceTemplate({template:c,routerPath:t,completions:l,annotations:i.annotations??$i(),handle:(e,t)=>s(t).pipe(R(e=>new rY({message:e.message})),I(t=>i.content(e,...t).pipe(L(t=>D0(e,t)),vh(e=>{let t=Bp(e)[0];return F(new iY({message:t.message}))}))),Hh(a))})})},C0=()=>{let e=HN({ignoreTrailingSlash:!0,ignoreDuplicateSlashes:!0,caseSensitive:!0});return{add:(t,n)=>{e.on(`GET`,`/${t}`,n)},find:t=>e.find(`GET`,`/${t}`)}},w0=(e,...t)=>{let n=e[0].replace(`:`,`::`),r=e[0],i=Object.create(null),a=jj([]);if(t.leng
Line truncated
|
||||||
|
`),n.push(`\r
|
||||||
|
`),this.doWrite(e,n,t)},e=>{throw this.fireError(e),e}))}async doWrite(e,t,n){try{return await this.writable.write(t.join(``),`ascii`),this.writable.write(n)}catch(t){return this.handleError(t,e),Promise.reject(t)}}handleError(e,t){this.errorCount++,this.fireError(e,t,this.errorCount)}end(){this.writable.end()}}})),J0=/* @__PURE__ */ x((e=>{Object.defineProperty(e,"__esModule",{value:!0}),e.AbstractMessageBuffer=void 0,e.AbstractMessageBuffer=class{_encoding;_chunks;_totalLength;constructor(e=`utf-8`){this._encoding=e,this._chunks=[],this._totalLength=0}get encoding(){return this._encoding}append(e){let t=typeof e==`string`?this.fromString(e,this._encoding):e;this._chunks.push(t),this._totalLength+=t.byteLength}tryReadHeaders(e=!1){if(this._chunks.length===0)return;let t=0,n=0,r=0,i=0;row:for(;n<this._chunks.length;){let e=this._chunks[n];for(r=0;r<e.length;){switch(e[r]){case 13:switch(t){case 0:t=1;break;case 2:t=3;break;default:t=0}break;case 10:switch(t){case 1:t=2;break;case 3:t=4,r++;break row;default:t=0}break;default:t=0}r++}i+=e.byteLength,n++}if(t!==4)return;let a=this._read(i+r),o=/* @__PURE__ */ new Map,s=this.toString(a,`ascii`).split(`\r
|
||||||
|
`);if(s.length<2)return o;for(let t=0;t<s.length-2;t++){let n=s[t],r=n.indexOf(`:`);if(r===-1)throw Error(`Message header must separate key and value using ':'\n${n}`);let i=n.substr(0,r),a=n.substr(r+1).trim();o.set(e?i.toLowerCase():i,a)}return o}tryReadBody(e){if(!(this._totalLength<e))return this._read(e)}get numberOfBytes(){return this._totalLength}_read(e){if(e===0)return this.emptyBuffer();if(e>this._totalLength)throw Error(`Cannot read so many bytes!`);if(this._chunks[0].byteLength===e){let t=this._chunks[0];return this._chunks.shift(),this._totalLength-=e,this.asNative(t)}if(this._chunks[0].byteLength>e){let t=this._chunks[0],n=this.asNative(t,e);return this._chunks[0]=t.slice(e),this._totalLength-=e,n}let t=this.allocNative(e),n=0;for(;e>0;){let r=this._chunks[0];if(r.byteLength>e){let i=r.slice(0,e);t.set(i,n),n+=e,this._chunks[0]=r.slice(e),this._totalLength-=e,e-=e}else t.set(r,n),n+=r.byteLength,this._chunks.shift(),this._totalLength-=r.byteLength,e-=r.byteLength}return t}}})),Y0=/* @__PURE__ */ x((e=>{var t=e&&e.__createBinding||(Object.create?(function(e,t,n,r){r===void 0&&(r=n);var i=Object.getOwnPropertyDescriptor(t,n);(!i||(`get`in i?!t.__esModule:i.writable||i.configurable))&&(i={enumerable:!0,get:function(){return t[n]}}),Object.defineProperty(e,r,i)}):(function(e,t,n,r){r===void 0&&(r=n),e[r]=t[n]})),n=e&&e.__setModuleDefault||(Object.create?(function(e,t){Object.defineProperty(e,"default",{enumerable:!0,value:t})}):function(e,t){e.default=t}),r=e&&e.__importStar||(function(){var e=function(t){return e=Object.getOwnPropertyNames||function(e){var t=[];for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&(t[t.length]=n);return t},e(t)};return function(r){if(r&&r.__esModule)return r;var i={};if(r!=null)for(var a=e(r),o=0;o<a.length;o++)a[o]!=="default"&&t(i,r,a[o]);return n(i,r),i}})(),i=e&&e.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(e,"__esModule",{value:!0}),e.ConnectionOptions=e.MessageStrategy=e.CancellationStrategy=e.CancellationSenderStrategy=e.CancellationReceiverStrategy=e.RequestCancellationReceiverStrategy=e.IdCancellationReceiverStrategy=e.ConnectionStrategy=e.ConnectionError=e.ConnectionErrors=e.LogTraceNotification=e.SetTraceNotification=e.TraceFormat=e.TraceValues=e.TraceValue=e.Trace=e.NullLogger=e.ProgressType=e.ProgressToken=void 0,e.createMessageConnection=ce;let a=i(V0()),o=r(L0()),s=R0(),c=z0(),l=H0(),u=U0();var d;(function(e){e.type=new s.NotificationType(`$/cancelRequest`)})(d||={});var f;(function(e){function t(e){return typeof e==`string`||typeof e==`number`}e.is=t})(f||(e.ProgressToken=f={}));var p;(function(e){e.type=new s.NotificationType(`$/progress`)})(p||={}),e.ProgressType=class{__;_pr;constructor(){}};var m;(function(e){function t(e){return o.func(e)}e.is=t})(m||={}),e.NullLogger=Object.freeze({error:()=>{},warn:()=>{},info:()=>{},log:()=>{}});var h;(function(e){e[e.Off=0]=`Off`,e[e.Messages=1]=`Messages`,e[e.Compact=2]=`Compact`,e[e.Verbose=3]=`Verbose`})(h||(e.Trace=h={}));var g;(function(e){e.Off=`off`,e.Messages=`messages`,e.Compact=`compact`,e.Verbose=`verbose`})(g||(e.TraceValue=g={})),e.TraceValues=g,(function(e){function t(t){if(!o.string(t))return e.Off;switch(t=t.toLowerCase(),t){case`off`:return e.Off;case`messages`:return e.Messages;case`compact`:return e.Compact;case`verbose`:return e.Verbose;default:return e.Off}}e.fromString=t;function n(t){switch(t){case e.Off:return`off`;case e.Messages:return`messages`;case e.Compact:return`compact`;case e.Verbose:return`verbose`;default:return`off`}}e.toString=n})(h||(e.Trace=h={}));var _;(function(e){e.Text=`text`,e.JSON=`json`})(_||(e.TraceFormat=_={})),(function(e){function t(t){return o.string(t)?(t=t.toLowerCase(),t===`json`?e.JSON:e.Text):e.Text}e.fromString=t})(_||(e.TraceFormat=_={}));var v;(function(e){e.type=new s.NotificationType(`$/setTrace`)})(v||(e.SetTraceNotification=v={}));var ee;(function(e){e.type=new s.NotificationType(`$/logTrace`)})(ee||(e.LogTraceNotification=ee={}));var y;(function(e){e[e.Closed=1]=`Closed`,e[e.Disposed=2]=`Disposed`,e[e.AlreadyListening=3]=`AlreadyListening`})(y||(e.ConnectionErrors=y={}));var te=class e extends Error{code;constructor(t,n){super(n),this.code=t,Object.setPrototypeOf(this,e.prototype)}};e.ConnectionError=te;var b;(function(e){function t(e){let t=e;return t&&o.func(t.cancelUndispatched)}e.is=t})(b||(e.ConnectionStrategy=b={}));var ne;(function(e){function t(e){let t=e;return t&&(t.kind===void 0||t.kind===`id`)&&o.func(t.createCancellationTokenSource)&&(t.dispose===void 0||o.func(t.dispose))}e.is=t})(ne||(e.IdCancellationReceiverStrategy=ne={}));var x;(function(e){function t(e){let t=e;return t&&t.kind===`request`&&o.func(t.createCancellationTokenSource)&&(t.dispose===void 0||o.func(t.dispose))}e.is=t})(x||(e.RequestCancellationReceiverStrategy=x={}));var re;(function(e){e.Message=Object.freeze({createCancellationTokenSource(e){return new u.CancellationTokenSource}});function t(e){return ne.is(e)||x
Line truncated
|
||||||
|
`,`\r
|
||||||
|
`,`\r`],(function(e){function t(e,t,n,r){return new j4(e,t,n,r)}e.create=t;function n(e){let t=e;return!!($.defined(t)&&$.string(t.uri)&&($.undefined(t.languageId)||$.string(t.languageId))&&$.uinteger(t.lineCount)&&$.func(t.getText)&&$.func(t.positionAt)&&$.func(t.offsetAt))}e.is=n;function r(e,t){let n=e.getText(),r=i(t,(e,t)=>{let n=e.range.start.line-t.range.start.line;return n===0?e.range.start.character-t.range.start.character:n}),a=n.length;for(let t=r.length-1;t>=0;t--){let i=r[t],o=e.offsetAt(i.range.start),s=e.offsetAt(i.range.end);if(s<=a)n=n.substring(0,o)+i.newText+n.substring(s,n.length);else throw Error(`Overlapping edit`);a=o}return n}e.applyEdits=r;function i(e,t){if(e.length<=1)return e;let n=e.length/2|0,r=e.slice(0,n),a=e.slice(n);i(r,t),i(a,t);let o=0,s=0,c=0;for(;o<r.length&&s<a.length;)t(r[o],a[s])<=0?e[c++]=r[o++]:e[c++]=a[s++];for(;o<r.length;)e[c++]=r[o++];for(;s<a.length;)e[c++]=a[s++];return e}})(A4||={}),j4=class{constructor(e,t,n,r){this._uri=e,this._languageId=t,this._version=n,this._content=r,this._lineOffsets=void 0}get uri(){return this._uri}get languageId(){return this._languageId}get version(){return this._version}getText(e){if(e){let t=this.offsetAt(e.start),n=this.offsetAt(e.end);return this._content.substring(t,n)}return this._content}update(e,t){this._content=e.text,this._version=t,this._lineOffsets=void 0}getLineOffsets(){if(this._lineOffsets===void 0){let e=[],t=this._content,n=!0;for(let r=0;r<t.length;r++){n&&=(e.push(r),!1);let i=t.charAt(r);n=i===`\r`||i===`
|
||||||
|
`,i===`\r`&&r+1<t.length&&t.charAt(r+1)===`
|
||||||
|
`&&r++}n&&t.length>0&&e.push(t.length),this._lineOffsets=e}return this._lineOffsets}positionAt(e){e=Math.max(Math.min(e,this._content.length),0);let t=this.getLineOffsets(),n=0,r=t.length;if(r===0)return n2.create(0,e);for(;n<r;){let i=Math.floor((n+r)/2);t[i]>e?r=i:n=i+1}let i=n-1;return n2.create(i,e-t[i])}offsetAt(e){let t=this.getLineOffsets();if(e.line>=t.length)return this._content.length;if(e.line<0)return 0;let n=t[e.line],r=e.line+1<t.length?t[e.line+1]:this._content.length;return Math.max(Math.min(n+e.character,r),n)}get lineCount(){return this.getLineOffsets().length}},(function(e){let t=Object.prototype.toString;function n(e){return e!==void 0}e.defined=n;function r(e){return e===void 0}e.undefined=r;function i(e){return e===!0||e===!1}e.boolean=i;function a(e){return t.call(e)===`[object String]`}e.string=a;function o(e){return t.call(e)===`[object Number]`}e.number=o;function s(e,n,r){return t.call(e)===`[object Number]`&&n<=e&&e<=r}e.numberRange=s;function c(e){return t.call(e)===`[object Number]`&&-2147483648<=e&&e<=2147483647}e.integer=c;function l(e){return t.call(e)===`[object Number]`&&0<=e&&e<=2147483647}e.uinteger=l;function u(e){return t.call(e)===`[object Function]`}e.func=u;function d(e){return typeof e==`object`&&!!e}e.objectLiteral=d;function f(e,t){return Array.isArray(e)&&e.every(t)}e.typedArray=f})($||={})})),N4=/* @__PURE__ */ x((e=>{Object.defineProperty(e,"__esModule",{value:!0}),e.CM=e.ProtocolNotificationType=e.ProtocolNotificationType0=e.ProtocolRequestType=e.ProtocolRequestType0=e.RegistrationType=e.MessageDirection=void 0;let t=X0();var n;(function(e){e.clientToServer=`clientToServer`,e.serverToClient=`serverToClient`,e.both=`both`})(n||(e.MessageDirection=n={})),e.RegistrationType=class{____;method;constructor(e){this.method=e}},e.ProtocolRequestType0=class extends t.RequestType0{__;___;____;_pr;constructor(e){super(e)}},e.ProtocolRequestType=class extends t.RequestType{__;___;____;_pr;constructor(e){super(e,t.ParameterStructures.byName)}},e.ProtocolNotificationType0=class extends t.NotificationType0{___;____;constructor(e){super(e)}},e.ProtocolNotificationType=class extends t.NotificationType{___;____;constructor(e){super(e,t.ParameterStructures.byName)}};var r;(function(e){function t(e,t){return{client:e,server:t}}e.create=t})(r||(e.CM=r={}))})),P4=/* @__PURE__ */ x((e=>{Object.defineProperty(e,"__esModule",{value:!0}),e.boolean=t,e.string=n,e.number=r,e.error=i,e.func=a,e.array=o,e.stringArray=s,e.typedArray=c,e.objectLiteral=l;function t(e){return e===!0||e===!1}function n(e){return typeof e==`string`||e instanceof String}function r(e){return typeof e==`number`||e instanceof Number}function i(e){return e instanceof Error}function a(e){return typeof e==`function`}function o(e){return Array.isArray(e)}function s(e){return o(e)&&e.every(e=>n(e))}function c(e,t){return Array.isArray(e)&&e.every(t)}function l(e){return typeof e==`object`&&!!e}})),F4=/* @__PURE__ */ x((e=>{Object.defineProperty(e,"__esModule",{value:!0}),e.ImplementationRequest=void 0;let t=N4();var n;(function(e){e.method=`textDocument/implementation`,e.messageDirection=t.MessageDirection.clientToServer,e.type=new t.ProtocolRequestType(e.method),e.capabilities=t.CM.create(`textDocument.implementation`,`implementationProvider`)})(n||(e.ImplementationRequest=n={}))})),I4=/* @__PURE__ */ x((e=>{Object.defineProperty(e,"__esModule",{value:!0}),e.TypeDefinitionRequest=void 0;let t=N4();var n;(function(e){e.method=`textDocument/typeDefinition`,e.messageDirection=t.MessageDirection.clientToServer,e.type=new t.ProtocolRequestType(e.method),e.capabilities=t.CM.create(`textDocument.typeDefinition`,`typeDefinitionProvider`)})(n||(e.TypeDefinitionRequest=n={}))})),L4=/* @__PURE__ */ x((e=>{Object.defineProperty(e,"__esModule",{value:!0}),e.DidChangeWorkspaceFoldersNotification=e.WorkspaceFoldersRequest=void 0;let t=N4();var n;(function(e){e.method=`workspace/workspaceFolders`,e.messageDirection=t.MessageDirection.serverToClient,e.type=new t.ProtocolRequestType0(e.method),e.capabilities=t.CM.create(`workspace.workspaceFolders`,`workspace.workspaceFolders`)})(n||(e.WorkspaceFoldersRequest=n={}));var r;(function(e){e.method=`workspace/didChangeWorkspaceFolders`,e.messageDirection=t.MessageDirection.clientToServer,e.type=new t.ProtocolNotificationType(e.method),e.capabilities=t.CM.create(void 0,`workspace.workspaceFolders.changeNotifications`)})(r||(e.DidChangeWorkspaceFoldersNotification=r={}))})),R4=/* @__PURE__ */ x((e=>{Object.defineProperty(e,"__esModule",{value:!0}),e.ConfigurationRequest=void 0;let t=N4();var n;(function(e){e.method=`workspace/configuration`,e.messageDirection=t.MessageDirection.serverToClient,e.type=new t.ProtocolRequestType(e.method),e.capabilities=t.CM.create(`workspace.configuration`,void 0)})(n||(e.ConfigurationRequest=n={}))})),z4=/* @__PURE__ */ x((e=>{Object.defineProperty(e,"__esModule",{value:!0}),e.ColorPresentationRequest=e.DocumentColorRequest=void 0;let t=N4();var n;(function(e){e
Line truncated
|
||||||
|
|
||||||
|
`):`kind`in e?e.value:`\`\`\`${e.language}\n${e.value}\n\`\`\``,d6=e=>Object.fromEntries(Object.entries(e).map(([e,t])=>[t,e[0]?.toLowerCase()+e.slice(1)])),f6=d6(l3.SymbolKind),p6=d6(l3.CompletionItemKind),m6=[`error`,`warning`,`information`,`hint`],h6=e=>new F3({method:e,message:`the language server does not support ${e}`}),g6=(e,t)=>e[t]?P:F(h6(t)),_6=Sg(`Editor.locate`)(function*(e){let t=yield*(yield*n6).open(e.file),n=e.line-1;if(n>=t.lineCount)return yield*new F3({method:`locate`,message:`${e.file} has only ${t.lineCount} lines`});let r=c6(t,n),i=e.column===void 0?e.symbol===void 0?Math.max(0,r.search(/\S/)):v6(r,e.symbol):e.column-1;return i<0?yield*new F3({method:`locate`,message:`"${e.symbol}" does not occur on line ${e.line}`}):{doc:t,position:{line:n,character:Math.min(i,r.length)},textDocument:{uri:t.uri}}}),v6=(e,t)=>{let n=t.replace(/[.*+?^${}()|[\]\\]/g,`\\$&`),r=e.search(RegExp(`(?<![\\p{L}\\p{N}_$])${n}(?![\\p{L}\\p{N}_$])`,`u`));return r>=0?r:e.indexOf(t)},y6=Sg(`Editor.locations`)(function*(e){let t=yield*n6,n=e.map(e=>`targetUri`in e?{uri:e.targetUri,range:e.targetSelectionRange}:e),r=new Map(yield*Gm([...new Set(n.map(e=>e.uri))],e=>t.peek(e).pipe(uh,L(t=>[e,zr(t)]))));return n.map(({uri:e,range:n})=>({file:t.file(e),...s6(n),preview:l6(r.get(e),n.start.line)}))}),b6=Sg(`Editor.workspaceEdit`)(function*(e,t){let n=yield*n6,r=[],i=[];for(let t of r6(e))l3.TextDocumentEdit.is(t)?r.push({file:n.file(t.textDocument.uri),edits:t.edits.flatMap(e=>`newText`in e?[{...s6(e.range),newText:e.newText}]:[])}):t.kind===`rename`?i.push(`rename ${n.file(t.oldUri)} -> ${n.file(t.newUri)}`):i.push(`${t.kind} ${n.file(t.uri)}`);return t&&(yield*n.applyEdit(e)),{applied:t,files:r,operations:i}}),x6=Sg(`Editor.hover`)(function*(e){let t=yield*I3,{textDocument:n,position:r}=yield*_6(e),i=yield*t.request(l3.HoverRequest.type,{textDocument:n,position:r});return i===null?null:{contents:u6(i.contents),range:i.range&&s6(i.range)}}),S6={definition:l3.DefinitionRequest.type,typeDefinition:l3.TypeDefinitionRequest.type,implementation:l3.ImplementationRequest.type,declaration:l3.DeclarationRequest.type},C6=Sg(`Editor.definition`)(function*({kind:e=`definition`,...t}){let n=yield*I3;yield*g6(n.capabilities,`${e}Provider`);let{textDocument:r,position:i}=yield*_6(t),a=yield*n.request(S6[e],{textDocument:r,position:i});return yield*y6(a===null?[]:Array.isArray(a)?a:[a])}),w6=Sg(`Editor.references`)(function*({includeDeclaration:e=!0,...t}){let n=yield*I3,{textDocument:r,position:i}=yield*_6(t),a=yield*n.request(l3.ReferencesRequest.type,{textDocument:r,position:i,context:{includeDeclaration:e}});return yield*y6(a??[])}),T6=Sg(`Editor.documentSymbols`)(function*({file:e}){let t=yield*I3,n=yield*(yield*n6).open(e),r=yield*t.request(l3.DocumentSymbolRequest.type,{textDocument:{uri:n.uri}}),i=e=>({name:e.name,kind:f6[e.kind]??`unknown`,detail:e.detail||void 0,...s6(e.range),children:e.children?.length?e.children.map(i):void 0});return(r??[]).map(e=>`range`in e?i(e):{name:e.name,kind:f6[e.kind]??`unknown`,container:e.containerName||void 0,...s6(e.location.range)})}),E6=Sg(`Editor.workspaceSymbols`)(function*({query:e,limit:t=50}){let n=yield*I3,r=yield*n6;return((yield*n.request(l3.WorkspaceSymbolRequest.type,{query:e}))??[]).slice(0,t).map(e=>({name:e.name,kind:f6[e.kind]??`unknown`,container:e.containerName||void 0,file:r.file(e.location.uri),...`range`in e.location?{line:e.location.range.start.line+1,column:e.location.range.start.character+1}:{}}))}),D6=Sg(`Editor.diagnostics`)(function*({file:e,severity:t=`hint`}){let n=yield*n6,r=yield*q3,i=e=>t=>({file:n.file(e),...s6(t.range),severity:m6[(t.severity??1)-1]??`error`,code:t.code===void 0?void 0:String(t.code),source:t.source,message:u6(t.message),related:t.relatedInformation?.map(e=>({file:n.file(e.location.uri),line:e.location.range.start.line+1,column:e.location.range.start.character+1,message:e.message}))}),a=e===void 0?(yield*r.all).flatMap(e=>e.diagnostics.map(i(e.uri))):yield*n.open(e).pipe(I(e=>L(r.forDocument(e),t=>t.map(i(e.uri))))),o=m6.indexOf(t);return a.filter(e=>m6.indexOf(e.severity)<=o).sort((e,t)=>e.file.localeCompare(t.file)||e.line-t.line||e.column-t.column)}),O6=Sg(`Editor.completions`)(function*({prefix:e,limit:t=20,...n}){let r=yield*I3,{textDocument:i,position:a}=yield*_6(n),o=yield*r.request(l3.CompletionRequest.type,{textDocument:i,position:a,context:{triggerKind:l3.CompletionTriggerKind.Invoked}}),s=Array.isArray(o)?o:o?.items??[],c=e?.toLowerCase();return s.filter(e=>c===void 0||(e.filterText??e.label).toLowerCase().startsWith(c)).sort((e,t)=>(e.sortText??e.label).localeCompare(t.sortText??t.label)).slice(0,t).map(e=>({label:e.label,kind:p6[e.kind??1]??`text`,detail:e.detail,documentation:e.documentation===void 0?void 0:u6(e.documentation)}))}),k6=Sg(`Editor.signatureHelp`)(function*(e){let t=yield*I3,{textDocument:n,position:r}=yield*_6(e),i=yield*t.request(l3.SignatureHelpRequest.type,{textDocument:n,position:r});return{active:i?.activeSignLine truncated
|
||||||
+28
-14
@@ -1,15 +1,24 @@
|
|||||||
{
|
{
|
||||||
"name": "lsp-mcp",
|
"name": "@opticlm/lsp-mcp",
|
||||||
"version": "1.0.0",
|
"version": "0.1.2",
|
||||||
"description": "",
|
"description": "MCP server exposing a Language Server's capabilities to agents as a headless editor",
|
||||||
"main": "index.js",
|
"type": "module",
|
||||||
|
"license": "MIT",
|
||||||
|
"files": [
|
||||||
|
"dist"
|
||||||
|
],
|
||||||
|
"bin": {
|
||||||
|
"lsp-mcp": "./dist/main.mjs"
|
||||||
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "echo \"Error: no test specified\" && exit 1",
|
"build": "tsdown",
|
||||||
|
"start": "node dist/main.mjs",
|
||||||
|
"dev": "node src/main.ts",
|
||||||
|
"check": "tsc",
|
||||||
|
"test": "vitest run",
|
||||||
|
"lint": "biome check --write --unsafe",
|
||||||
"prepare": "effect-tsgo patch --typescript --no-oxlint"
|
"prepare": "effect-tsgo patch --typescript --no-oxlint"
|
||||||
},
|
},
|
||||||
"keywords": [],
|
|
||||||
"author": "",
|
|
||||||
"license": "ISC",
|
|
||||||
"devEngines": {
|
"devEngines": {
|
||||||
"packageManager": {
|
"packageManager": {
|
||||||
"name": "pnpm",
|
"name": "pnpm",
|
||||||
@@ -18,14 +27,19 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"packageManager": "pnpm@12.4.2",
|
"packageManager": "pnpm@12.4.2",
|
||||||
"type": "module",
|
|
||||||
"dependencies": {
|
|
||||||
"@effect/platform-node": "4.0.0-rc.115",
|
|
||||||
"effect": "4.0.0-rc.115"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@biomejs/biome": "2.5.14",
|
"@biomejs/biome": "2.5.14",
|
||||||
|
"@effect/platform-node": "4.0.0-rc.115",
|
||||||
"@effect/tsgo": "0.45.0",
|
"@effect/tsgo": "0.45.0",
|
||||||
"typescript": "^7.0.2"
|
"@effect/vitest": "4.0.0-rc.115",
|
||||||
|
"@types/node": "^26.6.1",
|
||||||
|
"effect": "4.0.0-rc.115",
|
||||||
|
"tsdown": "0.23.0",
|
||||||
|
"typescript": "^7.0.2",
|
||||||
|
"vitest": "^5.0.1",
|
||||||
|
"vscode-languageserver": "^10.1.1",
|
||||||
|
"vscode-languageserver-protocol": "^3.18.3",
|
||||||
|
"vscode-languageserver-textdocument": "^1.0.14",
|
||||||
|
"vscode-uri": "^3.2.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Generated
+1154
-7
File diff suppressed because it is too large.
Load diff
+319
@@ -0,0 +1,319 @@
|
|||||||
|
/**
|
||||||
|
* The agent-facing vocabulary of the server.
|
||||||
|
*
|
||||||
|
* Everything an agent sends or receives is described here as `Schema`, so the
|
||||||
|
* same definitions drive MCP tool input/output schemas and the values the
|
||||||
|
* `lsp/` bridge produces. Positions are 1-based (line and column), the way
|
||||||
|
* agents and editors display them; the bridge converts to LSP's 0-based form.
|
||||||
|
*/
|
||||||
|
import { Option, Predicate, Schema, SchemaGetter } from "effect";
|
||||||
|
|
||||||
|
/** An optional field whose key is omitted, rather than set to `undefined`, in JSON output. */
|
||||||
|
const optional = <S extends Schema.Top>(schema: S) =>
|
||||||
|
Schema.optional(schema).pipe(
|
||||||
|
Schema.encodeTo(Schema.optionalKey(schema), {
|
||||||
|
decode: SchemaGetter.passthrough(),
|
||||||
|
encode: SchemaGetter.transformOptional(
|
||||||
|
Option.filter(Predicate.isNotUndefined),
|
||||||
|
),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
const positive = Schema.Int.check(Schema.isGreaterThanOrEqualTo(1));
|
||||||
|
|
||||||
|
export const File = Schema.String.annotate({
|
||||||
|
description: "File path, absolute or relative to the workspace root",
|
||||||
|
});
|
||||||
|
export const Line = positive.annotate({ description: "1-based line number" });
|
||||||
|
export const Column = positive.annotate({
|
||||||
|
description:
|
||||||
|
"1-based column, counted in UTF-16 code units (plain characters for ASCII)",
|
||||||
|
});
|
||||||
|
|
||||||
|
export const Where = Schema.Struct({
|
||||||
|
file: File,
|
||||||
|
line: Line,
|
||||||
|
column: optional(
|
||||||
|
positive.annotate({
|
||||||
|
description:
|
||||||
|
"1-based column. Omit to place the cursor on `symbol`, or on the first non-blank character of the line",
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
symbol: optional(
|
||||||
|
Schema.String.annotate({
|
||||||
|
description:
|
||||||
|
"Identifier on the line to place the cursor at; used when `column` is omitted",
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
});
|
||||||
|
export type Where = typeof Where.Type;
|
||||||
|
|
||||||
|
export const Range = Schema.Struct({
|
||||||
|
line: Line,
|
||||||
|
column: Column,
|
||||||
|
endLine: Line,
|
||||||
|
endColumn: Column,
|
||||||
|
});
|
||||||
|
export type Range = typeof Range.Type;
|
||||||
|
|
||||||
|
export const Location = Schema.Struct({
|
||||||
|
file: Schema.String,
|
||||||
|
...Range.fields,
|
||||||
|
preview: Schema.String.annotate({
|
||||||
|
description: "Trimmed source text of the starting line",
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
export type Location = typeof Location.Type;
|
||||||
|
|
||||||
|
export const Hover = Schema.Struct({
|
||||||
|
contents: Schema.String.annotate({ description: "Markdown" }),
|
||||||
|
range: optional(Range),
|
||||||
|
});
|
||||||
|
export type Hover = typeof Hover.Type;
|
||||||
|
|
||||||
|
export const GotoKind = Schema.Literals([
|
||||||
|
"definition",
|
||||||
|
"typeDefinition",
|
||||||
|
"implementation",
|
||||||
|
"declaration",
|
||||||
|
]);
|
||||||
|
export const Goto = Schema.Struct({
|
||||||
|
...Where.fields,
|
||||||
|
kind: optional(
|
||||||
|
GotoKind.annotate({
|
||||||
|
description: "What to navigate to (default: definition)",
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
});
|
||||||
|
export type Goto = typeof Goto.Type;
|
||||||
|
|
||||||
|
export const References = Schema.Struct({
|
||||||
|
...Where.fields,
|
||||||
|
includeDeclaration: optional(
|
||||||
|
Schema.Boolean.annotate({ description: "Default: true" }),
|
||||||
|
),
|
||||||
|
});
|
||||||
|
export type References = typeof References.Type;
|
||||||
|
|
||||||
|
export interface DocumentSymbol extends Range {
|
||||||
|
readonly name: string;
|
||||||
|
readonly kind: string;
|
||||||
|
readonly detail?: string | undefined;
|
||||||
|
readonly container?: string | undefined;
|
||||||
|
readonly children?: ReadonlyArray<DocumentSymbol> | undefined;
|
||||||
|
}
|
||||||
|
export const DocumentSymbol: Schema.Codec<DocumentSymbol> = Schema.Struct({
|
||||||
|
name: Schema.String,
|
||||||
|
kind: Schema.String,
|
||||||
|
detail: optional(Schema.String),
|
||||||
|
container: optional(Schema.String),
|
||||||
|
...Range.fields,
|
||||||
|
children: optional(
|
||||||
|
Schema.Array(
|
||||||
|
Schema.suspend((): Schema.Codec<DocumentSymbol> => DocumentSymbol),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const WorkspaceSymbol = Schema.Struct({
|
||||||
|
name: Schema.String,
|
||||||
|
kind: Schema.String,
|
||||||
|
container: optional(Schema.String),
|
||||||
|
file: Schema.String,
|
||||||
|
line: optional(Line),
|
||||||
|
column: optional(Column),
|
||||||
|
});
|
||||||
|
export type WorkspaceSymbol = typeof WorkspaceSymbol.Type;
|
||||||
|
|
||||||
|
export const WorkspaceQuery = Schema.Struct({
|
||||||
|
query: Schema.String.annotate({
|
||||||
|
description:
|
||||||
|
"Symbol name or fragment; matching is fuzzy and server-defined",
|
||||||
|
}),
|
||||||
|
limit: optional(positive.annotate({ description: "Default: 50" })),
|
||||||
|
});
|
||||||
|
export type WorkspaceQuery = typeof WorkspaceQuery.Type;
|
||||||
|
|
||||||
|
export const Severity = Schema.Literals([
|
||||||
|
"error",
|
||||||
|
"warning",
|
||||||
|
"information",
|
||||||
|
"hint",
|
||||||
|
]);
|
||||||
|
export type Severity = typeof Severity.Type;
|
||||||
|
|
||||||
|
export const Diagnostic = Schema.Struct({
|
||||||
|
file: Schema.String,
|
||||||
|
...Range.fields,
|
||||||
|
severity: Severity,
|
||||||
|
code: optional(Schema.String),
|
||||||
|
source: optional(Schema.String),
|
||||||
|
message: Schema.String,
|
||||||
|
related: optional(
|
||||||
|
Schema.Array(
|
||||||
|
Schema.Struct({
|
||||||
|
file: Schema.String,
|
||||||
|
line: Line,
|
||||||
|
column: Column,
|
||||||
|
message: Schema.String,
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
});
|
||||||
|
export type Diagnostic = typeof Diagnostic.Type;
|
||||||
|
|
||||||
|
export const DiagnosticsQuery = Schema.Struct({
|
||||||
|
file: optional(
|
||||||
|
File.annotate({
|
||||||
|
description:
|
||||||
|
"Restrict to one file; omit for every file the server has reported on",
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
severity: optional(
|
||||||
|
Severity.annotate({
|
||||||
|
description:
|
||||||
|
"Least severe level to include (default: hint, i.e. everything)",
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
});
|
||||||
|
export type DiagnosticsQuery = typeof DiagnosticsQuery.Type;
|
||||||
|
|
||||||
|
export const Completions = Schema.Struct({
|
||||||
|
...Where.fields,
|
||||||
|
prefix: optional(
|
||||||
|
Schema.String.annotate({
|
||||||
|
description: "Keep only items whose label starts with this",
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
limit: optional(positive.annotate({ description: "Default: 20" })),
|
||||||
|
});
|
||||||
|
export type Completions = typeof Completions.Type;
|
||||||
|
|
||||||
|
export const Completion = Schema.Struct({
|
||||||
|
label: Schema.String,
|
||||||
|
kind: Schema.String,
|
||||||
|
detail: optional(Schema.String),
|
||||||
|
documentation: optional(Schema.String),
|
||||||
|
});
|
||||||
|
export type Completion = typeof Completion.Type;
|
||||||
|
|
||||||
|
export const Signature = Schema.Struct({
|
||||||
|
label: Schema.String,
|
||||||
|
documentation: optional(Schema.String),
|
||||||
|
parameters: Schema.Array(Schema.String),
|
||||||
|
activeParameter: optional(Schema.Int),
|
||||||
|
});
|
||||||
|
export const SignatureHelp = Schema.Struct({
|
||||||
|
signatures: Schema.Array(Signature),
|
||||||
|
active: Schema.Int,
|
||||||
|
});
|
||||||
|
export type SignatureHelp = typeof SignatureHelp.Type;
|
||||||
|
|
||||||
|
export const TextEdit = Schema.Struct({
|
||||||
|
...Range.fields,
|
||||||
|
newText: Schema.String,
|
||||||
|
});
|
||||||
|
export const FileEdits = Schema.Struct({
|
||||||
|
file: Schema.String,
|
||||||
|
edits: Schema.Array(TextEdit),
|
||||||
|
});
|
||||||
|
export const WorkspaceEdit = Schema.Struct({
|
||||||
|
applied: Schema.Boolean.annotate({
|
||||||
|
description: "Whether the edits were written to disk",
|
||||||
|
}),
|
||||||
|
files: Schema.Array(FileEdits),
|
||||||
|
operations: Schema.Array(Schema.String).annotate({
|
||||||
|
description: "File creations, renames, deletions and executed commands",
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
export type WorkspaceEdit = typeof WorkspaceEdit.Type;
|
||||||
|
|
||||||
|
const apply = optional(
|
||||||
|
Schema.Boolean.annotate({
|
||||||
|
description:
|
||||||
|
"Write the resulting edits to disk (default: false, preview only)",
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
export const Rename = Schema.Struct({
|
||||||
|
...Where.fields,
|
||||||
|
newName: Schema.String,
|
||||||
|
apply,
|
||||||
|
});
|
||||||
|
export type Rename = typeof Rename.Type;
|
||||||
|
|
||||||
|
export const Selection = Schema.Struct({
|
||||||
|
file: File,
|
||||||
|
line: Line,
|
||||||
|
column: optional(Column.annotate({ description: "Default: start of line" })),
|
||||||
|
endLine: optional(Line.annotate({ description: "Default: same as line" })),
|
||||||
|
endColumn: optional(
|
||||||
|
Column.annotate({ description: "Default: end of endLine" }),
|
||||||
|
),
|
||||||
|
});
|
||||||
|
export type Selection = typeof Selection.Type;
|
||||||
|
|
||||||
|
export const CodeActions = Schema.Struct({
|
||||||
|
...Selection.fields,
|
||||||
|
kind: optional(
|
||||||
|
Schema.String.annotate({
|
||||||
|
description:
|
||||||
|
"Only actions of this kind prefix, e.g. quickfix, refactor, refactor.extract, source.organizeImports",
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
});
|
||||||
|
export type CodeActions = typeof CodeActions.Type;
|
||||||
|
|
||||||
|
export const CodeAction = Schema.Struct({
|
||||||
|
title: Schema.String,
|
||||||
|
kind: optional(Schema.String),
|
||||||
|
preferred: Schema.Boolean,
|
||||||
|
fixes: Schema.Array(Schema.String).annotate({
|
||||||
|
description: "Messages of the diagnostics this action addresses",
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
export type CodeAction = typeof CodeAction.Type;
|
||||||
|
|
||||||
|
export const ApplyCodeAction = Schema.Struct({
|
||||||
|
...CodeActions.fields,
|
||||||
|
title: Schema.String.annotate({
|
||||||
|
description:
|
||||||
|
"Exact title of an action returned by code_actions for the same selection",
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
export type ApplyCodeAction = typeof ApplyCodeAction.Type;
|
||||||
|
|
||||||
|
export const Format = Schema.Struct({
|
||||||
|
file: File,
|
||||||
|
tabSize: optional(positive.annotate({ description: "Default: 4" })),
|
||||||
|
insertSpaces: optional(
|
||||||
|
Schema.Boolean.annotate({ description: "Default: true" }),
|
||||||
|
),
|
||||||
|
apply,
|
||||||
|
});
|
||||||
|
export type Format = typeof Format.Type;
|
||||||
|
|
||||||
|
export const CallDirection = Schema.Literals(["incoming", "outgoing"]);
|
||||||
|
export const CallHierarchy = Schema.Struct({
|
||||||
|
...Where.fields,
|
||||||
|
direction: optional(
|
||||||
|
CallDirection.annotate({
|
||||||
|
description:
|
||||||
|
"incoming: who calls this; outgoing: what this calls (default: incoming)",
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
});
|
||||||
|
export type CallHierarchy = typeof CallHierarchy.Type;
|
||||||
|
|
||||||
|
export const Call = Schema.Struct({
|
||||||
|
name: Schema.String,
|
||||||
|
kind: Schema.String,
|
||||||
|
detail: optional(Schema.String),
|
||||||
|
file: Schema.String,
|
||||||
|
line: Line,
|
||||||
|
column: Column,
|
||||||
|
sites: Schema.Array(Schema.Struct({ line: Line, column: Column })).annotate({
|
||||||
|
description: "Call sites, located in the caller's file",
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
export type Call = typeof Call.Type;
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
/**
|
||||||
|
* MCP resources and notifications: the server's identity and capabilities,
|
||||||
|
* live diagnostics (subscribable), and the language server's log messages
|
||||||
|
* forwarded as MCP log notifications.
|
||||||
|
*/
|
||||||
|
import { Effect, Layer, Schema, Stream } from "effect";
|
||||||
|
import { McpSchema, McpServer } from "effect/unstable/ai";
|
||||||
|
import { Diagnostics } from "./lsp/Diagnostics.ts";
|
||||||
|
import { Documents } from "./lsp/Documents.ts";
|
||||||
|
import * as Editor from "./lsp/Editor.ts";
|
||||||
|
import { LanguageServer } from "./lsp/LanguageServer.ts";
|
||||||
|
|
||||||
|
const json = (value: unknown) => JSON.stringify(value, null, 2);
|
||||||
|
const diagnosticsUri = "lsp://diagnostics";
|
||||||
|
const fileDiagnosticsUri = (file: string) =>
|
||||||
|
`${diagnosticsUri}/${encodeURIComponent(file)}`;
|
||||||
|
|
||||||
|
export const layer = Layer.effectDiscard(
|
||||||
|
Effect.gen(function* () {
|
||||||
|
const lsp = yield* LanguageServer;
|
||||||
|
const docs = yield* Documents;
|
||||||
|
const diagnostics = yield* Diagnostics;
|
||||||
|
const mcp = yield* McpServer.McpServer;
|
||||||
|
|
||||||
|
yield* McpServer.registerResource({
|
||||||
|
uri: "lsp://server",
|
||||||
|
name: "Language server",
|
||||||
|
description:
|
||||||
|
"Name, version, workspace root and capabilities of the connected language server",
|
||||||
|
mimeType: "application/json",
|
||||||
|
content: Effect.succeed(
|
||||||
|
json({
|
||||||
|
...lsp.serverInfo,
|
||||||
|
root: lsp.root,
|
||||||
|
capabilities: lsp.capabilities,
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
});
|
||||||
|
|
||||||
|
yield* McpServer.registerResource({
|
||||||
|
uri: diagnosticsUri,
|
||||||
|
name: "Diagnostics",
|
||||||
|
description:
|
||||||
|
"All diagnostics the language server has reported. Subscribe to be told when they change.",
|
||||||
|
mimeType: "application/json",
|
||||||
|
content: Effect.map(Editor.diagnostics({}), json),
|
||||||
|
});
|
||||||
|
|
||||||
|
yield* McpServer.registerResource`lsp://diagnostics/${McpSchema.param("file", Schema.String)}`(
|
||||||
|
{
|
||||||
|
name: "File diagnostics",
|
||||||
|
description:
|
||||||
|
"Diagnostics of one file (path URL-encoded, relative to the workspace root)",
|
||||||
|
mimeType: "application/json",
|
||||||
|
completion: {
|
||||||
|
file: (input) =>
|
||||||
|
Effect.map(docs.opened, (uris) =>
|
||||||
|
uris.map(docs.file).filter((file) => file.startsWith(input)),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
content: (_, file) =>
|
||||||
|
Effect.map(
|
||||||
|
Editor.diagnostics({ file: decodeURIComponent(file) }),
|
||||||
|
json,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const updated = (uri: string) =>
|
||||||
|
mcp.notifications["notifications/resources/updated"]({ uri });
|
||||||
|
yield* diagnostics.changes.pipe(
|
||||||
|
Stream.runForEach((uri) =>
|
||||||
|
Effect.all([
|
||||||
|
updated(diagnosticsUri),
|
||||||
|
updated(fileDiagnosticsUri(docs.file(uri))),
|
||||||
|
]),
|
||||||
|
),
|
||||||
|
Effect.forkScoped,
|
||||||
|
);
|
||||||
|
|
||||||
|
yield* lsp.logs.pipe(
|
||||||
|
Stream.runForEach(({ level, message }) =>
|
||||||
|
mcp.notifications["notifications/message"]({
|
||||||
|
level,
|
||||||
|
logger: "lsp",
|
||||||
|
data: message,
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
Effect.forkScoped,
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
);
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
/**
|
||||||
|
* Wires the language server, the headless editor and the MCP server together.
|
||||||
|
*
|
||||||
|
* Build order matters: the language server initializes first (so tool
|
||||||
|
* registration can consult its capabilities), then the editor services, then
|
||||||
|
* the MCP transport, then tools and resources are registered.
|
||||||
|
*/
|
||||||
|
import { createServer } from "node:http";
|
||||||
|
import { NodeHttpServer } from "@effect/platform-node";
|
||||||
|
import { Effect, Layer } from "effect";
|
||||||
|
import { McpProtocol, McpServer } from "effect/unstable/ai";
|
||||||
|
import { HttpRouter } from "effect/unstable/http";
|
||||||
|
import { Diagnostics } from "./lsp/Diagnostics.ts";
|
||||||
|
import * as Documents from "./lsp/Documents.ts";
|
||||||
|
import * as LanguageServer from "./lsp/LanguageServer.ts";
|
||||||
|
import * as Resources from "./Resources.ts";
|
||||||
|
import * as Tools from "./Tools.ts";
|
||||||
|
|
||||||
|
export type Transport =
|
||||||
|
| { readonly _tag: "stdio" }
|
||||||
|
| { readonly _tag: "http"; readonly port: number };
|
||||||
|
|
||||||
|
export interface Options {
|
||||||
|
readonly transport: Transport;
|
||||||
|
readonly server: LanguageServer.Options;
|
||||||
|
readonly documents: Documents.Options;
|
||||||
|
}
|
||||||
|
|
||||||
|
const info = {
|
||||||
|
name: "lsp-mcp",
|
||||||
|
version: "0.1.2",
|
||||||
|
protocols: [
|
||||||
|
McpProtocol.v2025_11_25,
|
||||||
|
McpProtocol.v2025_06_18,
|
||||||
|
McpProtocol.v2025_03_26,
|
||||||
|
McpProtocol.v2024_11_05,
|
||||||
|
],
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
const transport = (transport: Transport) =>
|
||||||
|
transport._tag === "stdio"
|
||||||
|
? McpServer.layerStdio(info)
|
||||||
|
: HttpRouter.serve(McpServer.layerHttp({ ...info, path: "/mcp" }), {
|
||||||
|
disableLogger: true,
|
||||||
|
}).pipe(
|
||||||
|
Layer.provide(
|
||||||
|
NodeHttpServer.layer(createServer, { port: transport.port }),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
/** Registers the tools the connected server can serve. */
|
||||||
|
export const tools = Layer.effectDiscard(
|
||||||
|
Effect.gen(function* () {
|
||||||
|
const { capabilities } = yield* LanguageServer.LanguageServer;
|
||||||
|
yield* McpServer.registerToolkit(Tools.supportedBy(capabilities));
|
||||||
|
}),
|
||||||
|
).pipe(Layer.provide(Tools.layer));
|
||||||
|
|
||||||
|
export const layer = (options: Options) =>
|
||||||
|
Layer.mergeAll(tools, Resources.layer).pipe(
|
||||||
|
Layer.provide(transport(options.transport)),
|
||||||
|
Layer.provide(
|
||||||
|
Layer.mergeAll(
|
||||||
|
Documents.Documents.layer(options.documents),
|
||||||
|
Diagnostics.layer,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Layer.provide(LanguageServer.LanguageServer.layer(options.server)),
|
||||||
|
);
|
||||||
+167
@@ -0,0 +1,167 @@
|
|||||||
|
/**
|
||||||
|
* The MCP tools: one per editor operation, described for agents. Tools whose
|
||||||
|
* capability the connected language server lacks are not offered.
|
||||||
|
*/
|
||||||
|
import { Schema } from "effect";
|
||||||
|
import { Tool, Toolkit } from "effect/unstable/ai";
|
||||||
|
import type { ServerCapabilities } from "vscode-languageserver-protocol";
|
||||||
|
import { Diagnostics } from "./lsp/Diagnostics.ts";
|
||||||
|
import { DocumentError, Documents } from "./lsp/Documents.ts";
|
||||||
|
import * as Editor from "./lsp/Editor.ts";
|
||||||
|
import { LanguageServer, LspError } from "./lsp/LanguageServer.ts";
|
||||||
|
import * as Model from "./Model.ts";
|
||||||
|
|
||||||
|
const Failure = Schema.Union([LspError, DocumentError]);
|
||||||
|
|
||||||
|
const define = <
|
||||||
|
const Name extends string,
|
||||||
|
Parameters extends Schema.Constraint,
|
||||||
|
Success extends Schema.Constraint,
|
||||||
|
>(
|
||||||
|
name: Name,
|
||||||
|
options: {
|
||||||
|
readonly description: string;
|
||||||
|
readonly parameters: Parameters;
|
||||||
|
readonly success: Success;
|
||||||
|
readonly mutates?: boolean;
|
||||||
|
},
|
||||||
|
) =>
|
||||||
|
Tool.make(name, {
|
||||||
|
description: options.description,
|
||||||
|
parameters: options.parameters,
|
||||||
|
success: options.success,
|
||||||
|
failure: Failure,
|
||||||
|
dependencies: [LanguageServer, Documents, Diagnostics],
|
||||||
|
})
|
||||||
|
.annotate(Tool.Readonly, !options.mutates)
|
||||||
|
.annotate(Tool.Destructive, false)
|
||||||
|
.annotate(Tool.Idempotent, !options.mutates)
|
||||||
|
.annotate(Tool.OpenWorld, false);
|
||||||
|
|
||||||
|
const positions =
|
||||||
|
"Positions are 1-based lines and columns; instead of a column you may name the `symbol` on that line to point at.";
|
||||||
|
const preview =
|
||||||
|
"Without `apply` the edits are only previewed; with `apply: true` they are written to disk.";
|
||||||
|
|
||||||
|
export const Tools = Toolkit.make(
|
||||||
|
define("hover", {
|
||||||
|
description: `Type signature and documentation of the symbol at a position, as an editor shows on hover. ${positions}`,
|
||||||
|
parameters: Model.Where,
|
||||||
|
success: Schema.NullOr(Model.Hover),
|
||||||
|
}),
|
||||||
|
define("definition", {
|
||||||
|
description: `Where the symbol at a position is defined (or its type definition, implementations, or declaration). Returns locations with a preview of each target line. ${positions}`,
|
||||||
|
parameters: Model.Goto,
|
||||||
|
success: Schema.Array(Model.Location),
|
||||||
|
}),
|
||||||
|
define("references", {
|
||||||
|
description: `Every place the symbol at a position is used across the workspace, with a preview of each line. ${positions}`,
|
||||||
|
parameters: Model.References,
|
||||||
|
success: Schema.Array(Model.Location),
|
||||||
|
}),
|
||||||
|
define("document_symbols", {
|
||||||
|
description:
|
||||||
|
"Outline of a file: its functions, classes, methods, variables and so on, with their ranges, as a tree.",
|
||||||
|
parameters: Schema.Struct({ file: Model.File }),
|
||||||
|
success: Schema.Array(Model.DocumentSymbol),
|
||||||
|
}),
|
||||||
|
define("workspace_symbols", {
|
||||||
|
description:
|
||||||
|
"Find symbols by name across the whole workspace, including files you have not opened.",
|
||||||
|
parameters: Model.WorkspaceQuery,
|
||||||
|
success: Schema.Array(Model.WorkspaceSymbol),
|
||||||
|
}),
|
||||||
|
define("diagnostics", {
|
||||||
|
description:
|
||||||
|
"Errors, warnings and hints from the language server. Files are re-read from disk first, so call this right after editing to check your changes. Without `file`, returns everything reported so far.",
|
||||||
|
parameters: Model.DiagnosticsQuery,
|
||||||
|
success: Schema.Array(Model.Diagnostic),
|
||||||
|
}),
|
||||||
|
define("completions", {
|
||||||
|
description: `Completion items at a position: discover members, imports, or valid identifiers the server suggests. ${positions}`,
|
||||||
|
parameters: Model.Completions,
|
||||||
|
success: Schema.Array(Model.Completion),
|
||||||
|
}),
|
||||||
|
define("signature_help", {
|
||||||
|
description: `Signatures and parameters of the call surrounding a position. ${positions}`,
|
||||||
|
parameters: Model.Where,
|
||||||
|
success: Model.SignatureHelp,
|
||||||
|
}),
|
||||||
|
define("rename", {
|
||||||
|
description: `Rename the symbol at a position everywhere it is referenced, across files. ${positions} ${preview}`,
|
||||||
|
parameters: Model.Rename,
|
||||||
|
success: Model.WorkspaceEdit,
|
||||||
|
mutates: true,
|
||||||
|
}),
|
||||||
|
define("code_actions", {
|
||||||
|
description:
|
||||||
|
"Quick fixes and refactorings the server offers for a selection (whole lines unless columns are given), including fixes for the diagnostics there. Apply one with apply_code_action.",
|
||||||
|
parameters: Model.CodeActions,
|
||||||
|
success: Schema.Array(Model.CodeAction),
|
||||||
|
}),
|
||||||
|
define("apply_code_action", {
|
||||||
|
description:
|
||||||
|
"Apply a code action by title, for the same selection given to code_actions. Edits are written to disk.",
|
||||||
|
parameters: Model.ApplyCodeAction,
|
||||||
|
success: Model.WorkspaceEdit,
|
||||||
|
mutates: true,
|
||||||
|
}),
|
||||||
|
define("format", {
|
||||||
|
description: `Format a whole file with the language server's formatter. ${preview}`,
|
||||||
|
parameters: Model.Format,
|
||||||
|
success: Model.WorkspaceEdit,
|
||||||
|
mutates: true,
|
||||||
|
}),
|
||||||
|
define("call_hierarchy", {
|
||||||
|
description: `Callers of the function at a position (incoming) or the functions it calls (outgoing). ${positions}`,
|
||||||
|
parameters: Model.CallHierarchy,
|
||||||
|
success: Schema.Array(Model.Call),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
export const layer = Tools.toLayer({
|
||||||
|
hover: Editor.hover,
|
||||||
|
definition: Editor.definition,
|
||||||
|
references: Editor.references,
|
||||||
|
document_symbols: Editor.documentSymbols,
|
||||||
|
workspace_symbols: Editor.workspaceSymbols,
|
||||||
|
diagnostics: Editor.diagnostics,
|
||||||
|
completions: Editor.completions,
|
||||||
|
signature_help: Editor.signatureHelp,
|
||||||
|
rename: Editor.rename,
|
||||||
|
code_actions: Editor.codeActions,
|
||||||
|
apply_code_action: Editor.applyCodeAction,
|
||||||
|
format: Editor.format,
|
||||||
|
call_hierarchy: Editor.callHierarchy,
|
||||||
|
});
|
||||||
|
|
||||||
|
const requirements: Record<
|
||||||
|
keyof typeof Tools.tools,
|
||||||
|
(c: ServerCapabilities) => unknown
|
||||||
|
> = {
|
||||||
|
hover: (c) => c.hoverProvider,
|
||||||
|
definition: (c) =>
|
||||||
|
c.definitionProvider ??
|
||||||
|
c.typeDefinitionProvider ??
|
||||||
|
c.implementationProvider ??
|
||||||
|
c.declarationProvider,
|
||||||
|
references: (c) => c.referencesProvider,
|
||||||
|
document_symbols: (c) => c.documentSymbolProvider,
|
||||||
|
workspace_symbols: (c) => c.workspaceSymbolProvider,
|
||||||
|
diagnostics: () => true,
|
||||||
|
completions: (c) => c.completionProvider,
|
||||||
|
signature_help: (c) => c.signatureHelpProvider,
|
||||||
|
rename: (c) => c.renameProvider,
|
||||||
|
code_actions: (c) => c.codeActionProvider,
|
||||||
|
apply_code_action: (c) => c.codeActionProvider,
|
||||||
|
format: (c) => c.documentFormattingProvider,
|
||||||
|
call_hierarchy: (c) => c.callHierarchyProvider,
|
||||||
|
};
|
||||||
|
|
||||||
|
/** The subset of `Tools` the server can serve; handled by the same `layer`. */
|
||||||
|
export const supportedBy = (capabilities: ServerCapabilities): typeof Tools =>
|
||||||
|
Toolkit.make(
|
||||||
|
...Object.values(Tools.tools).filter((tool) =>
|
||||||
|
requirements[tool.name](capabilities),
|
||||||
|
),
|
||||||
|
) as typeof Tools;
|
||||||
@@ -0,0 +1,134 @@
|
|||||||
|
/**
|
||||||
|
* Diagnostics reconcile LSP's push model with the agent's pull model.
|
||||||
|
*
|
||||||
|
* Published diagnostics are accumulated per URI. Asking for a document's
|
||||||
|
* diagnostics either pulls them (when the server supports
|
||||||
|
* `textDocument/diagnostic`) or waits for the push that follows the document's
|
||||||
|
* latest version to settle, bounded by a timeout.
|
||||||
|
*/
|
||||||
|
import {
|
||||||
|
Context,
|
||||||
|
Effect,
|
||||||
|
HashMap,
|
||||||
|
Layer,
|
||||||
|
Option,
|
||||||
|
PubSub,
|
||||||
|
Ref,
|
||||||
|
Stream,
|
||||||
|
} from "effect";
|
||||||
|
import {
|
||||||
|
type Diagnostic,
|
||||||
|
DocumentDiagnosticReportKind,
|
||||||
|
DocumentDiagnosticRequest,
|
||||||
|
PublishDiagnosticsNotification,
|
||||||
|
type PublishDiagnosticsParams,
|
||||||
|
WorkspaceDiagnosticRequest,
|
||||||
|
} from "vscode-languageserver-protocol";
|
||||||
|
import type { TextDocument } from "vscode-languageserver-textdocument";
|
||||||
|
import { LanguageServer, type LspError } from "./LanguageServer.ts";
|
||||||
|
|
||||||
|
/** Quiet period after a publish before diagnostics count as settled. */
|
||||||
|
const settle = "250 millis";
|
||||||
|
/** How long to wait for a server that never publishes for a change. */
|
||||||
|
const patience = "5 seconds";
|
||||||
|
|
||||||
|
const make = Effect.gen(function* () {
|
||||||
|
const lsp = yield* LanguageServer;
|
||||||
|
const state = yield* Ref.make(
|
||||||
|
HashMap.empty<string, PublishDiagnosticsParams>(),
|
||||||
|
);
|
||||||
|
const updates = yield* PubSub.unbounded<string>();
|
||||||
|
const settled = new Map<string, number>();
|
||||||
|
const provider = lsp.capabilities.diagnosticProvider;
|
||||||
|
|
||||||
|
const publish = (params: PublishDiagnosticsParams) =>
|
||||||
|
Ref.update(state, HashMap.set(params.uri, params)).pipe(
|
||||||
|
Effect.andThen(PubSub.publish(updates, params.uri)),
|
||||||
|
);
|
||||||
|
yield* lsp
|
||||||
|
.notifications(PublishDiagnosticsNotification.type)
|
||||||
|
.pipe(Stream.runForEach(publish), Effect.forkScoped);
|
||||||
|
|
||||||
|
const current = (uri: string) =>
|
||||||
|
Ref.get(state).pipe(
|
||||||
|
Effect.map((map) => Option.getOrUndefined(HashMap.get(map, uri))),
|
||||||
|
);
|
||||||
|
|
||||||
|
const pull = Effect.fn("Diagnostics.pull")(function* (doc: TextDocument) {
|
||||||
|
const report = yield* lsp.request(DocumentDiagnosticRequest.type, {
|
||||||
|
textDocument: { uri: doc.uri },
|
||||||
|
});
|
||||||
|
if (report.kind === DocumentDiagnosticReportKind.Full) {
|
||||||
|
yield* publish({
|
||||||
|
uri: doc.uri,
|
||||||
|
version: doc.version,
|
||||||
|
diagnostics: report.items,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return (yield* current(doc.uri))?.diagnostics ?? [];
|
||||||
|
});
|
||||||
|
|
||||||
|
const awaitPush = Effect.fn("Diagnostics.awaitPush")(function* (
|
||||||
|
doc: TextDocument,
|
||||||
|
) {
|
||||||
|
if (settled.get(doc.uri) !== doc.version) {
|
||||||
|
yield* Effect.scoped(
|
||||||
|
Effect.gen(function* () {
|
||||||
|
const subscription = yield* PubSub.subscribe(updates);
|
||||||
|
const fresh = (yield* current(doc.uri))?.version === doc.version;
|
||||||
|
yield* Stream.fromSubscription(subscription).pipe(
|
||||||
|
Stream.filter((uri) => uri === doc.uri),
|
||||||
|
Stream.debounce(settle),
|
||||||
|
Stream.runHead,
|
||||||
|
Effect.timeoutOption(fresh ? settle : patience),
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
settled.set(doc.uri, doc.version);
|
||||||
|
}
|
||||||
|
return (yield* current(doc.uri))?.diagnostics ?? [];
|
||||||
|
});
|
||||||
|
|
||||||
|
const all = Effect.gen(function* () {
|
||||||
|
if (provider?.workspaceDiagnostics) {
|
||||||
|
const report = yield* lsp.request(WorkspaceDiagnosticRequest.type, {
|
||||||
|
previousResultIds: [],
|
||||||
|
});
|
||||||
|
for (const item of report.items) {
|
||||||
|
if (item.kind === DocumentDiagnosticReportKind.Full) {
|
||||||
|
yield* publish({
|
||||||
|
uri: item.uri,
|
||||||
|
diagnostics: item.items,
|
||||||
|
...(item.version === null ? {} : { version: item.version }),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Array.from(HashMap.values(yield* Ref.get(state)));
|
||||||
|
});
|
||||||
|
|
||||||
|
return Diagnostics.of({
|
||||||
|
all,
|
||||||
|
forDocument: provider ? pull : awaitPush,
|
||||||
|
changes: Stream.fromPubSub(updates),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
export class Diagnostics extends Context.Service<
|
||||||
|
Diagnostics,
|
||||||
|
{
|
||||||
|
/** Everything the server has reported, per URI. */
|
||||||
|
readonly all: Effect.Effect<
|
||||||
|
ReadonlyArray<PublishDiagnosticsParams>,
|
||||||
|
LspError
|
||||||
|
>;
|
||||||
|
/** Diagnostics matching the document's current version. */
|
||||||
|
readonly forDocument: (
|
||||||
|
doc: TextDocument,
|
||||||
|
) => Effect.Effect<ReadonlyArray<Diagnostic>, LspError>;
|
||||||
|
/** URIs whose diagnostics changed. */
|
||||||
|
readonly changes: Stream.Stream<string>;
|
||||||
|
}
|
||||||
|
>()("lsp-mcp/lsp/Diagnostics") {
|
||||||
|
static readonly layer = Layer.effect(Diagnostics, make);
|
||||||
|
}
|
||||||
@@ -0,0 +1,366 @@
|
|||||||
|
/**
|
||||||
|
* The headless editor's document store.
|
||||||
|
*
|
||||||
|
* Documents are opened in the language server on demand and kept in an LRU
|
||||||
|
* set; the least recently used one is closed when the capacity is exceeded.
|
||||||
|
* Every access re-checks the file on disk and, if it changed, pushes the new
|
||||||
|
* content as `didChange` + `didSave`, so edits agents make with other tools
|
||||||
|
* are visible to the server without any explicit synchronization step.
|
||||||
|
*/
|
||||||
|
import {
|
||||||
|
Context,
|
||||||
|
Duration,
|
||||||
|
Effect,
|
||||||
|
Exit,
|
||||||
|
FileSystem,
|
||||||
|
Layer,
|
||||||
|
Option,
|
||||||
|
Path,
|
||||||
|
type PlatformError,
|
||||||
|
Schema,
|
||||||
|
ScopedCache,
|
||||||
|
Semaphore,
|
||||||
|
} from "effect";
|
||||||
|
import {
|
||||||
|
ApplyWorkspaceEditRequest,
|
||||||
|
CreateFile,
|
||||||
|
DeleteFile,
|
||||||
|
DidChangeTextDocumentNotification,
|
||||||
|
DidCloseTextDocumentNotification,
|
||||||
|
DidOpenTextDocumentNotification,
|
||||||
|
DidSaveTextDocumentNotification,
|
||||||
|
RenameFile,
|
||||||
|
type TextDocumentEdit,
|
||||||
|
type TextEdit,
|
||||||
|
type WorkspaceEdit,
|
||||||
|
} from "vscode-languageserver-protocol";
|
||||||
|
import { TextDocument } from "vscode-languageserver-textdocument";
|
||||||
|
import { URI } from "vscode-uri";
|
||||||
|
import { LanguageServer } from "./LanguageServer.ts";
|
||||||
|
|
||||||
|
export interface Options {
|
||||||
|
/** How many documents stay open in the server before the least recently used is closed. */
|
||||||
|
readonly capacity: number;
|
||||||
|
/** Extra file extension (without dot) to LSP `languageId` mappings. */
|
||||||
|
readonly languages: Readonly<Record<string, string>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class DocumentError extends Schema.TaggedError<DocumentError>()(
|
||||||
|
"DocumentError",
|
||||||
|
{
|
||||||
|
file: Schema.String,
|
||||||
|
message: Schema.String,
|
||||||
|
},
|
||||||
|
) {}
|
||||||
|
|
||||||
|
export type Change = TextDocumentEdit | CreateFile | RenameFile | DeleteFile;
|
||||||
|
|
||||||
|
export class Documents extends Context.Service<
|
||||||
|
Documents,
|
||||||
|
{
|
||||||
|
/** URI for a path that is absolute or relative to the workspace root. */
|
||||||
|
readonly uri: (file: string) => string;
|
||||||
|
/** Display path for a URI: relative to the root when inside it. */
|
||||||
|
readonly file: (uri: string) => string;
|
||||||
|
/** Open (or refresh) a document in the server and return its current text. */
|
||||||
|
readonly open: (file: string) => Effect.Effect<TextDocument, DocumentError>;
|
||||||
|
/** Current content of a URI without opening it in the server. */
|
||||||
|
readonly peek: (uri: string) => Effect.Effect<TextDocument, DocumentError>;
|
||||||
|
/** Replace an open document's content on disk and in the server. */
|
||||||
|
readonly write: (
|
||||||
|
uri: string,
|
||||||
|
text: string,
|
||||||
|
) => Effect.Effect<void, DocumentError>;
|
||||||
|
/** Apply a server-provided edit to disk, in order, refusing stale versions. */
|
||||||
|
readonly applyEdit: (
|
||||||
|
edit: WorkspaceEdit,
|
||||||
|
) => Effect.Effect<void, DocumentError>;
|
||||||
|
readonly opened: Effect.Effect<ReadonlyArray<string>>;
|
||||||
|
}
|
||||||
|
>()("lsp-mcp/lsp/Documents") {
|
||||||
|
static readonly layer = (options: Options) =>
|
||||||
|
Layer.effect(Documents, make(options));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** The ordered list of changes described by a `WorkspaceEdit`. */
|
||||||
|
export const changes = (edit: WorkspaceEdit): ReadonlyArray<Change> =>
|
||||||
|
edit.documentChanges ??
|
||||||
|
Object.entries(edit.changes ?? {}).map(([uri, edits]) => ({
|
||||||
|
textDocument: { uri, version: null },
|
||||||
|
edits,
|
||||||
|
}));
|
||||||
|
|
||||||
|
const textEdits = (edit: TextDocumentEdit): Array<TextEdit> =>
|
||||||
|
edit.edits.filter((e): e is TextEdit => "newText" in e);
|
||||||
|
|
||||||
|
interface Entry {
|
||||||
|
readonly doc: TextDocument;
|
||||||
|
stamp: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const make = Effect.fn("Documents.make")(function* (options: Options) {
|
||||||
|
const fs = yield* FileSystem.FileSystem;
|
||||||
|
const path = yield* Path.Path;
|
||||||
|
const lsp = yield* LanguageServer;
|
||||||
|
const lock = yield* Semaphore.make(1);
|
||||||
|
const sync = lsp.capabilities.textDocumentSync;
|
||||||
|
const save = typeof sync === "object" ? sync.save : undefined;
|
||||||
|
|
||||||
|
const uri = (file: string) =>
|
||||||
|
URI.file(path.resolve(lsp.root, file)).toString();
|
||||||
|
const fsPath = (uri: string) => URI.parse(uri).fsPath;
|
||||||
|
const file = (uri: string) => {
|
||||||
|
const parsed = URI.parse(uri);
|
||||||
|
if (parsed.scheme !== "file") return uri;
|
||||||
|
const relative = path.relative(lsp.root, parsed.fsPath);
|
||||||
|
return relative.startsWith("..") || path.isAbsolute(relative)
|
||||||
|
? parsed.fsPath
|
||||||
|
: relative;
|
||||||
|
};
|
||||||
|
const io = (uri: string) => (error: PlatformError.PlatformError) =>
|
||||||
|
new DocumentError({ file: file(uri), message: error.message });
|
||||||
|
const read = (uri: string) =>
|
||||||
|
fs.readFileString(fsPath(uri)).pipe(Effect.mapError(io(uri)));
|
||||||
|
const stamp = (uri: string) =>
|
||||||
|
fs.stat(fsPath(uri)).pipe(
|
||||||
|
Effect.map(
|
||||||
|
(info) =>
|
||||||
|
`${Option.getOrUndefined(info.mtime)?.getTime()}:${info.size}`,
|
||||||
|
),
|
||||||
|
Effect.mapError(io(uri)),
|
||||||
|
);
|
||||||
|
|
||||||
|
const languageId = (uri: string) => {
|
||||||
|
const name = path.basename(fsPath(uri)).toLowerCase();
|
||||||
|
const ext = path.extname(name).slice(1);
|
||||||
|
return (
|
||||||
|
options.languages[ext] ?? languages[ext] ?? languages[name] ?? "plaintext"
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const cache = yield* ScopedCache.makeWith<string, Entry, DocumentError>({
|
||||||
|
capacity: options.capacity,
|
||||||
|
timeToLive: (exit) =>
|
||||||
|
Exit.isFailure(exit) ? Duration.zero : Duration.infinity,
|
||||||
|
lookup: (uri) =>
|
||||||
|
Effect.gen(function* () {
|
||||||
|
const [text, current] = yield* Effect.all([read(uri), stamp(uri)]);
|
||||||
|
const doc = TextDocument.create(uri, languageId(uri), 1, text);
|
||||||
|
yield* Effect.acquireRelease(
|
||||||
|
lsp.notify(DidOpenTextDocumentNotification.type, {
|
||||||
|
textDocument: {
|
||||||
|
uri,
|
||||||
|
languageId: doc.languageId,
|
||||||
|
version: doc.version,
|
||||||
|
text,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
() =>
|
||||||
|
lsp.notify(DidCloseTextDocumentNotification.type, {
|
||||||
|
textDocument: { uri },
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
return { doc, stamp: current } satisfies Entry;
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
const change = Effect.fn("Documents.change")(function* (
|
||||||
|
entry: Entry,
|
||||||
|
text: string,
|
||||||
|
) {
|
||||||
|
const { uri } = entry.doc;
|
||||||
|
const version = entry.doc.version + 1;
|
||||||
|
TextDocument.update(entry.doc, [{ text }], version);
|
||||||
|
yield* lsp.notify(DidChangeTextDocumentNotification.type, {
|
||||||
|
textDocument: { uri, version },
|
||||||
|
contentChanges: [{ text }],
|
||||||
|
});
|
||||||
|
if (save) {
|
||||||
|
yield* lsp.notify(DidSaveTextDocumentNotification.type, {
|
||||||
|
textDocument: { uri },
|
||||||
|
...(typeof save === "object" && save.includeText ? { text } : {}),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/** Bring an open document in line with the file on disk. */
|
||||||
|
const refresh = Effect.fn("Documents.refresh")(function* (uri: string) {
|
||||||
|
const entry = yield* ScopedCache.get(cache, uri);
|
||||||
|
const current = yield* stamp(uri).pipe(
|
||||||
|
Effect.tapError(() => ScopedCache.invalidate(cache, uri)),
|
||||||
|
);
|
||||||
|
if (current !== entry.stamp) {
|
||||||
|
const text = yield* read(uri);
|
||||||
|
entry.stamp = current;
|
||||||
|
if (text !== entry.doc.getText()) yield* change(entry, text);
|
||||||
|
}
|
||||||
|
return entry.doc;
|
||||||
|
});
|
||||||
|
|
||||||
|
const open = (file: string) => lock.withPermits(1)(refresh(uri(file)));
|
||||||
|
|
||||||
|
const peek = (uri: string) =>
|
||||||
|
Effect.flatMap(ScopedCache.has(cache, uri), (open) =>
|
||||||
|
open
|
||||||
|
? lock.withPermits(1)(refresh(uri))
|
||||||
|
: Effect.map(read(uri), (text) =>
|
||||||
|
TextDocument.create(uri, "", 0, text),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
/** Persist new content for an open document and tell the server. */
|
||||||
|
const store = Effect.fn("Documents.store")(function* (
|
||||||
|
uri: string,
|
||||||
|
text: string,
|
||||||
|
) {
|
||||||
|
const entry = yield* ScopedCache.get(cache, uri);
|
||||||
|
yield* fs.writeFileString(fsPath(uri), text).pipe(Effect.mapError(io(uri)));
|
||||||
|
entry.stamp = yield* stamp(uri);
|
||||||
|
if (text !== entry.doc.getText()) yield* change(entry, text);
|
||||||
|
});
|
||||||
|
|
||||||
|
const write = (uri: string, text: string) =>
|
||||||
|
lock.withPermits(1)(store(uri, text));
|
||||||
|
|
||||||
|
const forget = (uri: string) => ScopedCache.invalidate(cache, uri);
|
||||||
|
|
||||||
|
const apply = Effect.fn("Documents.applyChange")(function* (change: Change) {
|
||||||
|
if (CreateFile.is(change)) {
|
||||||
|
const target = fsPath(change.uri);
|
||||||
|
if (
|
||||||
|
change.options?.ignoreIfExists &&
|
||||||
|
(yield* fs.exists(target).pipe(Effect.mapError(io(change.uri))))
|
||||||
|
)
|
||||||
|
return;
|
||||||
|
yield* fs
|
||||||
|
.makeDirectory(path.dirname(target), { recursive: true })
|
||||||
|
.pipe(
|
||||||
|
Effect.andThen(fs.writeFileString(target, "")),
|
||||||
|
Effect.mapError(io(change.uri)),
|
||||||
|
);
|
||||||
|
} else if (RenameFile.is(change)) {
|
||||||
|
yield* Effect.all([forget(change.oldUri), forget(change.newUri)]);
|
||||||
|
yield* fs
|
||||||
|
.rename(fsPath(change.oldUri), fsPath(change.newUri))
|
||||||
|
.pipe(Effect.mapError(io(change.oldUri)));
|
||||||
|
} else if (DeleteFile.is(change)) {
|
||||||
|
yield* forget(change.uri);
|
||||||
|
yield* fs
|
||||||
|
.remove(fsPath(change.uri), {
|
||||||
|
recursive: change.options?.recursive ?? false,
|
||||||
|
})
|
||||||
|
.pipe(Effect.mapError(io(change.uri)));
|
||||||
|
} else {
|
||||||
|
const { uri, version } = change.textDocument;
|
||||||
|
const doc = yield* refresh(uri);
|
||||||
|
if (
|
||||||
|
version !== null &&
|
||||||
|
version !== undefined &&
|
||||||
|
version !== doc.version
|
||||||
|
) {
|
||||||
|
return yield* new DocumentError({
|
||||||
|
file: file(uri),
|
||||||
|
message: "document changed since the edit was computed",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
yield* store(uri, TextDocument.applyEdits(doc, textEdits(change)));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const applyEdit = (edit: WorkspaceEdit) =>
|
||||||
|
lock.withPermits(1)(
|
||||||
|
Effect.forEach(changes(edit), apply, { discard: true }),
|
||||||
|
);
|
||||||
|
|
||||||
|
yield* lsp.onRequest(ApplyWorkspaceEditRequest.type, ({ edit }) =>
|
||||||
|
applyEdit(edit).pipe(
|
||||||
|
Effect.as({ applied: true }),
|
||||||
|
Effect.catch((error) =>
|
||||||
|
Effect.succeed({ applied: false, failureReason: error.message }),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
return Documents.of({
|
||||||
|
uri,
|
||||||
|
file,
|
||||||
|
open,
|
||||||
|
peek,
|
||||||
|
write,
|
||||||
|
applyEdit,
|
||||||
|
opened: ScopedCache.keys(cache),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const languages: Record<string, string> = {
|
||||||
|
ts: "typescript",
|
||||||
|
mts: "typescript",
|
||||||
|
cts: "typescript",
|
||||||
|
tsx: "typescriptreact",
|
||||||
|
js: "javascript",
|
||||||
|
mjs: "javascript",
|
||||||
|
cjs: "javascript",
|
||||||
|
jsx: "javascriptreact",
|
||||||
|
json: "json",
|
||||||
|
jsonc: "jsonc",
|
||||||
|
py: "python",
|
||||||
|
pyi: "python",
|
||||||
|
rs: "rust",
|
||||||
|
go: "go",
|
||||||
|
c: "c",
|
||||||
|
h: "c",
|
||||||
|
cc: "cpp",
|
||||||
|
cpp: "cpp",
|
||||||
|
cxx: "cpp",
|
||||||
|
hpp: "cpp",
|
||||||
|
hh: "cpp",
|
||||||
|
java: "java",
|
||||||
|
kt: "kotlin",
|
||||||
|
kts: "kotlin",
|
||||||
|
scala: "scala",
|
||||||
|
cs: "csharp",
|
||||||
|
fs: "fsharp",
|
||||||
|
swift: "swift",
|
||||||
|
rb: "ruby",
|
||||||
|
php: "php",
|
||||||
|
lua: "lua",
|
||||||
|
zig: "zig",
|
||||||
|
hs: "haskell",
|
||||||
|
ml: "ocaml",
|
||||||
|
mli: "ocaml",
|
||||||
|
ex: "elixir",
|
||||||
|
exs: "elixir",
|
||||||
|
erl: "erlang",
|
||||||
|
clj: "clojure",
|
||||||
|
cljs: "clojurescript",
|
||||||
|
dart: "dart",
|
||||||
|
elm: "elm",
|
||||||
|
nix: "nix",
|
||||||
|
r: "r",
|
||||||
|
jl: "julia",
|
||||||
|
vue: "vue",
|
||||||
|
svelte: "svelte",
|
||||||
|
astro: "astro",
|
||||||
|
html: "html",
|
||||||
|
css: "css",
|
||||||
|
scss: "scss",
|
||||||
|
less: "less",
|
||||||
|
md: "markdown",
|
||||||
|
yaml: "yaml",
|
||||||
|
yml: "yaml",
|
||||||
|
toml: "toml",
|
||||||
|
xml: "xml",
|
||||||
|
sql: "sql",
|
||||||
|
sh: "shellscript",
|
||||||
|
bash: "shellscript",
|
||||||
|
zsh: "shellscript",
|
||||||
|
fish: "fish",
|
||||||
|
tf: "terraform",
|
||||||
|
proto: "proto",
|
||||||
|
graphql: "graphql",
|
||||||
|
gql: "graphql",
|
||||||
|
tex: "latex",
|
||||||
|
vim: "vim",
|
||||||
|
dockerfile: "dockerfile",
|
||||||
|
makefile: "makefile",
|
||||||
|
};
|
||||||
@@ -0,0 +1,608 @@
|
|||||||
|
/**
|
||||||
|
* The operations an agent performs on the headless editor.
|
||||||
|
*
|
||||||
|
* Each function resolves agent-facing input (paths, 1-based positions,
|
||||||
|
* symbols on a line) to LSP requests and presents the results in the
|
||||||
|
* `Model` vocabulary. These functions are the MCP tool handlers.
|
||||||
|
*/
|
||||||
|
import { Effect, Option } from "effect";
|
||||||
|
import {
|
||||||
|
CallHierarchyIncomingCallsRequest,
|
||||||
|
CallHierarchyOutgoingCallsRequest,
|
||||||
|
CallHierarchyPrepareRequest,
|
||||||
|
CodeAction,
|
||||||
|
CodeActionRequest,
|
||||||
|
CodeActionResolveRequest,
|
||||||
|
CodeActionTriggerKind,
|
||||||
|
type Command,
|
||||||
|
CompletionItemKind,
|
||||||
|
CompletionRequest,
|
||||||
|
CompletionTriggerKind,
|
||||||
|
DeclarationRequest,
|
||||||
|
DefinitionRequest,
|
||||||
|
type Diagnostic,
|
||||||
|
DocumentFormattingRequest,
|
||||||
|
type DocumentSymbol,
|
||||||
|
DocumentSymbolRequest,
|
||||||
|
ExecuteCommandRequest,
|
||||||
|
type Hover,
|
||||||
|
HoverRequest,
|
||||||
|
ImplementationRequest,
|
||||||
|
type Location,
|
||||||
|
type LocationLink,
|
||||||
|
type MarkupContent,
|
||||||
|
type Position,
|
||||||
|
type Range,
|
||||||
|
ReferencesRequest,
|
||||||
|
RenameRequest,
|
||||||
|
type ServerCapabilities,
|
||||||
|
SignatureHelpRequest,
|
||||||
|
SymbolKind,
|
||||||
|
TextDocumentEdit,
|
||||||
|
TypeDefinitionRequest,
|
||||||
|
type WorkspaceEdit,
|
||||||
|
WorkspaceSymbolRequest,
|
||||||
|
} from "vscode-languageserver-protocol";
|
||||||
|
import { TextDocument } from "vscode-languageserver-textdocument";
|
||||||
|
import type * as Model from "../Model.ts";
|
||||||
|
import { Diagnostics } from "./Diagnostics.ts";
|
||||||
|
import { changes, Documents } from "./Documents.ts";
|
||||||
|
import { LanguageServer, LspError } from "./LanguageServer.ts";
|
||||||
|
|
||||||
|
// --- Presentation ----------------------------------------------------------
|
||||||
|
|
||||||
|
const range = (r: Range): Model.Range => ({
|
||||||
|
line: r.start.line + 1,
|
||||||
|
column: r.start.character + 1,
|
||||||
|
endLine: r.end.line + 1,
|
||||||
|
endColumn: r.end.character + 1,
|
||||||
|
});
|
||||||
|
|
||||||
|
const lineText = (doc: TextDocument, line: number) =>
|
||||||
|
doc
|
||||||
|
.getText()
|
||||||
|
.slice(
|
||||||
|
doc.offsetAt({ line, character: 0 }),
|
||||||
|
doc.offsetAt({ line: line + 1, character: 0 }),
|
||||||
|
)
|
||||||
|
.replace(/\r?\n$/, "");
|
||||||
|
|
||||||
|
const preview = (doc: TextDocument | undefined, line: number) =>
|
||||||
|
doc === undefined ? "" : lineText(doc, line).trim();
|
||||||
|
|
||||||
|
const markup = (content: Hover["contents"] | MarkupContent | string): string =>
|
||||||
|
typeof content === "string"
|
||||||
|
? content
|
||||||
|
: Array.isArray(content)
|
||||||
|
? content.map(markup).join("\n\n")
|
||||||
|
: "kind" in content
|
||||||
|
? content.value
|
||||||
|
: `\`\`\`${content.language}\n${content.value}\n\`\`\``;
|
||||||
|
|
||||||
|
/** `{ Class: 5 }` → `{ 5: "class" }`, `{ TypeParameter: 26 }` → `{ 26: "typeParameter" }`. */
|
||||||
|
const kindNames = (kinds: Record<string, number>): Record<number, string> =>
|
||||||
|
Object.fromEntries(
|
||||||
|
Object.entries(kinds).map(([name, value]) => [
|
||||||
|
value,
|
||||||
|
name[0]?.toLowerCase() + name.slice(1),
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
const symbolKinds = kindNames(SymbolKind);
|
||||||
|
const completionKinds = kindNames(CompletionItemKind);
|
||||||
|
const severities = [
|
||||||
|
"error",
|
||||||
|
"warning",
|
||||||
|
"information",
|
||||||
|
"hint",
|
||||||
|
] as const satisfies ReadonlyArray<Model.Severity>;
|
||||||
|
|
||||||
|
// --- Resolution ------------------------------------------------------------
|
||||||
|
|
||||||
|
const unsupported = (feature: string) =>
|
||||||
|
new LspError({
|
||||||
|
method: feature,
|
||||||
|
message: `the language server does not support ${feature}`,
|
||||||
|
});
|
||||||
|
|
||||||
|
const ensure = (
|
||||||
|
capabilities: ServerCapabilities,
|
||||||
|
key: keyof ServerCapabilities,
|
||||||
|
) => (capabilities[key] ? Effect.void : Effect.fail(unsupported(key)));
|
||||||
|
|
||||||
|
/** Turn agent-facing `Where` into an LSP text document position. */
|
||||||
|
const locate = Effect.fn("Editor.locate")(function* (where: Model.Where) {
|
||||||
|
const docs = yield* Documents;
|
||||||
|
const doc = yield* docs.open(where.file);
|
||||||
|
const line = where.line - 1;
|
||||||
|
if (line >= doc.lineCount) {
|
||||||
|
return yield* new LspError({
|
||||||
|
method: "locate",
|
||||||
|
message: `${where.file} has only ${doc.lineCount} lines`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const text = lineText(doc, line);
|
||||||
|
const character =
|
||||||
|
where.column !== undefined
|
||||||
|
? where.column - 1
|
||||||
|
: where.symbol !== undefined
|
||||||
|
? findSymbol(text, where.symbol)
|
||||||
|
: Math.max(0, text.search(/\S/));
|
||||||
|
if (character < 0) {
|
||||||
|
return yield* new LspError({
|
||||||
|
method: "locate",
|
||||||
|
message: `"${where.symbol}" does not occur on line ${where.line}`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const position: Position = {
|
||||||
|
line,
|
||||||
|
character: Math.min(character, text.length),
|
||||||
|
};
|
||||||
|
return { doc, position, textDocument: { uri: doc.uri } };
|
||||||
|
});
|
||||||
|
|
||||||
|
/** Index of `symbol` as a whole word on the line, else its first occurrence, else -1. */
|
||||||
|
const findSymbol = (text: string, symbol: string) => {
|
||||||
|
const escaped = symbol.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
||||||
|
const index = text.search(
|
||||||
|
new RegExp(`(?<![\\p{L}\\p{N}_$])${escaped}(?![\\p{L}\\p{N}_$])`, "u"),
|
||||||
|
);
|
||||||
|
return index >= 0 ? index : text.indexOf(symbol);
|
||||||
|
};
|
||||||
|
|
||||||
|
const locations = Effect.fn("Editor.locations")(function* (
|
||||||
|
items: ReadonlyArray<Location | LocationLink>,
|
||||||
|
) {
|
||||||
|
const docs = yield* Documents;
|
||||||
|
const targets = items.map((item) =>
|
||||||
|
"targetUri" in item
|
||||||
|
? { uri: item.targetUri, range: item.targetSelectionRange }
|
||||||
|
: item,
|
||||||
|
);
|
||||||
|
const previews = new Map(
|
||||||
|
yield* Effect.forEach([...new Set(targets.map((t) => t.uri))], (uri) =>
|
||||||
|
docs.peek(uri).pipe(
|
||||||
|
Effect.option,
|
||||||
|
Effect.map((doc) => [uri, Option.getOrUndefined(doc)] as const),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
return targets.map(
|
||||||
|
({ uri, range: r }): Model.Location => ({
|
||||||
|
file: docs.file(uri),
|
||||||
|
...range(r),
|
||||||
|
preview: preview(previews.get(uri), r.start.line),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
const workspaceEdit = Effect.fn("Editor.workspaceEdit")(function* (
|
||||||
|
edit: WorkspaceEdit,
|
||||||
|
apply: boolean,
|
||||||
|
) {
|
||||||
|
const docs = yield* Documents;
|
||||||
|
const files: Array<typeof Model.FileEdits.Type> = [];
|
||||||
|
const operations: Array<string> = [];
|
||||||
|
for (const change of changes(edit)) {
|
||||||
|
if (TextDocumentEdit.is(change)) {
|
||||||
|
files.push({
|
||||||
|
file: docs.file(change.textDocument.uri),
|
||||||
|
edits: change.edits.flatMap((e) =>
|
||||||
|
"newText" in e ? [{ ...range(e.range), newText: e.newText }] : [],
|
||||||
|
),
|
||||||
|
});
|
||||||
|
} else if (change.kind === "rename") {
|
||||||
|
operations.push(
|
||||||
|
`rename ${docs.file(change.oldUri)} -> ${docs.file(change.newUri)}`,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
operations.push(`${change.kind} ${docs.file(change.uri)}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (apply) yield* docs.applyEdit(edit);
|
||||||
|
return { applied: apply, files, operations } satisfies Model.WorkspaceEdit;
|
||||||
|
});
|
||||||
|
|
||||||
|
// --- Operations ------------------------------------------------------------
|
||||||
|
|
||||||
|
export const hover = Effect.fn("Editor.hover")(function* (where: Model.Where) {
|
||||||
|
const lsp = yield* LanguageServer;
|
||||||
|
const { textDocument, position } = yield* locate(where);
|
||||||
|
const result = yield* lsp.request(HoverRequest.type, {
|
||||||
|
textDocument,
|
||||||
|
position,
|
||||||
|
});
|
||||||
|
return result === null
|
||||||
|
? null
|
||||||
|
: ({
|
||||||
|
contents: markup(result.contents),
|
||||||
|
range: result.range && range(result.range),
|
||||||
|
} satisfies Model.Hover);
|
||||||
|
});
|
||||||
|
|
||||||
|
const gotoRequests = {
|
||||||
|
definition: DefinitionRequest.type,
|
||||||
|
typeDefinition: TypeDefinitionRequest.type,
|
||||||
|
implementation: ImplementationRequest.type,
|
||||||
|
declaration: DeclarationRequest.type,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const definition = Effect.fn("Editor.definition")(function* ({
|
||||||
|
kind = "definition",
|
||||||
|
...where
|
||||||
|
}: Model.Goto) {
|
||||||
|
const lsp = yield* LanguageServer;
|
||||||
|
yield* ensure(lsp.capabilities, `${kind}Provider`);
|
||||||
|
const { textDocument, position } = yield* locate(where);
|
||||||
|
const result = yield* lsp.request(
|
||||||
|
gotoRequests[kind] as typeof DefinitionRequest.type,
|
||||||
|
{ textDocument, position },
|
||||||
|
);
|
||||||
|
return yield* locations(
|
||||||
|
result === null ? [] : Array.isArray(result) ? result : [result],
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
export const references = Effect.fn("Editor.references")(function* ({
|
||||||
|
includeDeclaration = true,
|
||||||
|
...where
|
||||||
|
}: Model.References) {
|
||||||
|
const lsp = yield* LanguageServer;
|
||||||
|
const { textDocument, position } = yield* locate(where);
|
||||||
|
const result = yield* lsp.request(ReferencesRequest.type, {
|
||||||
|
textDocument,
|
||||||
|
position,
|
||||||
|
context: { includeDeclaration },
|
||||||
|
});
|
||||||
|
return yield* locations(result ?? []);
|
||||||
|
});
|
||||||
|
|
||||||
|
export const documentSymbols = Effect.fn("Editor.documentSymbols")(function* ({
|
||||||
|
file,
|
||||||
|
}: {
|
||||||
|
readonly file: string;
|
||||||
|
}) {
|
||||||
|
const lsp = yield* LanguageServer;
|
||||||
|
const docs = yield* Documents;
|
||||||
|
const doc = yield* docs.open(file);
|
||||||
|
const result = yield* lsp.request(DocumentSymbolRequest.type, {
|
||||||
|
textDocument: { uri: doc.uri },
|
||||||
|
});
|
||||||
|
const present = (symbol: DocumentSymbol): Model.DocumentSymbol => ({
|
||||||
|
name: symbol.name,
|
||||||
|
kind: symbolKinds[symbol.kind] ?? "unknown",
|
||||||
|
detail: symbol.detail || undefined,
|
||||||
|
...range(symbol.range),
|
||||||
|
children: symbol.children?.length
|
||||||
|
? symbol.children.map(present)
|
||||||
|
: undefined,
|
||||||
|
});
|
||||||
|
return (result ?? []).map(
|
||||||
|
(symbol): Model.DocumentSymbol =>
|
||||||
|
"range" in symbol
|
||||||
|
? present(symbol)
|
||||||
|
: {
|
||||||
|
name: symbol.name,
|
||||||
|
kind: symbolKinds[symbol.kind] ?? "unknown",
|
||||||
|
container: symbol.containerName || undefined,
|
||||||
|
...range(symbol.location.range),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
export const workspaceSymbols = Effect.fn("Editor.workspaceSymbols")(
|
||||||
|
function* ({ query, limit = 50 }: Model.WorkspaceQuery) {
|
||||||
|
const lsp = yield* LanguageServer;
|
||||||
|
const docs = yield* Documents;
|
||||||
|
const result = yield* lsp.request(WorkspaceSymbolRequest.type, { query });
|
||||||
|
return (result ?? []).slice(0, limit).map(
|
||||||
|
(symbol): Model.WorkspaceSymbol => ({
|
||||||
|
name: symbol.name,
|
||||||
|
kind: symbolKinds[symbol.kind] ?? "unknown",
|
||||||
|
container: symbol.containerName || undefined,
|
||||||
|
file: docs.file(symbol.location.uri),
|
||||||
|
...("range" in symbol.location
|
||||||
|
? {
|
||||||
|
line: symbol.location.range.start.line + 1,
|
||||||
|
column: symbol.location.range.start.character + 1,
|
||||||
|
}
|
||||||
|
: {}),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
export const diagnostics = Effect.fn("Editor.diagnostics")(function* ({
|
||||||
|
file,
|
||||||
|
severity = "hint",
|
||||||
|
}: Model.DiagnosticsQuery) {
|
||||||
|
const docs = yield* Documents;
|
||||||
|
const diags = yield* Diagnostics;
|
||||||
|
const present =
|
||||||
|
(uri: string) =>
|
||||||
|
(d: Diagnostic): Model.Diagnostic => ({
|
||||||
|
file: docs.file(uri),
|
||||||
|
...range(d.range),
|
||||||
|
severity: severities[(d.severity ?? 1) - 1] ?? "error",
|
||||||
|
code: d.code === undefined ? undefined : String(d.code),
|
||||||
|
source: d.source,
|
||||||
|
message: markup(d.message),
|
||||||
|
related: d.relatedInformation?.map((r) => ({
|
||||||
|
file: docs.file(r.location.uri),
|
||||||
|
line: r.location.range.start.line + 1,
|
||||||
|
column: r.location.range.start.character + 1,
|
||||||
|
message: r.message,
|
||||||
|
})),
|
||||||
|
});
|
||||||
|
const items =
|
||||||
|
file === undefined
|
||||||
|
? (yield* diags.all).flatMap((p) => p.diagnostics.map(present(p.uri)))
|
||||||
|
: yield* docs
|
||||||
|
.open(file)
|
||||||
|
.pipe(
|
||||||
|
Effect.flatMap((doc) =>
|
||||||
|
Effect.map(diags.forDocument(doc), (ds) =>
|
||||||
|
ds.map(present(doc.uri)),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
const threshold = severities.indexOf(severity);
|
||||||
|
return items
|
||||||
|
.filter((d) => severities.indexOf(d.severity) <= threshold)
|
||||||
|
.sort(
|
||||||
|
(a, b) =>
|
||||||
|
a.file.localeCompare(b.file) || a.line - b.line || a.column - b.column,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
export const completions = Effect.fn("Editor.completions")(function* ({
|
||||||
|
prefix,
|
||||||
|
limit = 20,
|
||||||
|
...where
|
||||||
|
}: Model.Completions) {
|
||||||
|
const lsp = yield* LanguageServer;
|
||||||
|
const { textDocument, position } = yield* locate(where);
|
||||||
|
const result = yield* lsp.request(CompletionRequest.type, {
|
||||||
|
textDocument,
|
||||||
|
position,
|
||||||
|
context: { triggerKind: CompletionTriggerKind.Invoked },
|
||||||
|
});
|
||||||
|
const items = Array.isArray(result) ? result : (result?.items ?? []);
|
||||||
|
const lower = prefix?.toLowerCase();
|
||||||
|
return items
|
||||||
|
.filter(
|
||||||
|
(item) =>
|
||||||
|
lower === undefined ||
|
||||||
|
(item.filterText ?? item.label).toLowerCase().startsWith(lower),
|
||||||
|
)
|
||||||
|
.sort((a, b) =>
|
||||||
|
(a.sortText ?? a.label).localeCompare(b.sortText ?? b.label),
|
||||||
|
)
|
||||||
|
.slice(0, limit)
|
||||||
|
.map(
|
||||||
|
(item): Model.Completion => ({
|
||||||
|
label: item.label,
|
||||||
|
kind: completionKinds[item.kind ?? 1] ?? "text",
|
||||||
|
detail: item.detail,
|
||||||
|
documentation:
|
||||||
|
item.documentation === undefined
|
||||||
|
? undefined
|
||||||
|
: markup(item.documentation),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
export const signatureHelp = Effect.fn("Editor.signatureHelp")(function* (
|
||||||
|
where: Model.Where,
|
||||||
|
) {
|
||||||
|
const lsp = yield* LanguageServer;
|
||||||
|
const { textDocument, position } = yield* locate(where);
|
||||||
|
const result = yield* lsp.request(SignatureHelpRequest.type, {
|
||||||
|
textDocument,
|
||||||
|
position,
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
active: result?.activeSignature ?? 0,
|
||||||
|
signatures: (result?.signatures ?? []).map((s) => ({
|
||||||
|
label: s.label,
|
||||||
|
documentation:
|
||||||
|
s.documentation === undefined ? undefined : markup(s.documentation),
|
||||||
|
parameters: (s.parameters ?? []).map((p) =>
|
||||||
|
typeof p.label === "string"
|
||||||
|
? p.label
|
||||||
|
: s.label.slice(p.label[0], p.label[1]),
|
||||||
|
),
|
||||||
|
activeParameter:
|
||||||
|
s.activeParameter ?? result?.activeParameter ?? undefined,
|
||||||
|
})),
|
||||||
|
} satisfies Model.SignatureHelp;
|
||||||
|
});
|
||||||
|
|
||||||
|
export const rename = Effect.fn("Editor.rename")(function* ({
|
||||||
|
newName,
|
||||||
|
apply = false,
|
||||||
|
...where
|
||||||
|
}: Model.Rename) {
|
||||||
|
const lsp = yield* LanguageServer;
|
||||||
|
const { textDocument, position } = yield* locate(where);
|
||||||
|
const edit = yield* lsp.request(RenameRequest.type, {
|
||||||
|
textDocument,
|
||||||
|
position,
|
||||||
|
newName,
|
||||||
|
});
|
||||||
|
if (edit === null)
|
||||||
|
return yield* new LspError({
|
||||||
|
method: "rename",
|
||||||
|
message: "nothing to rename at this position",
|
||||||
|
});
|
||||||
|
return yield* workspaceEdit(edit, apply);
|
||||||
|
});
|
||||||
|
|
||||||
|
const selection = Effect.fn("Editor.selection")(function* ({
|
||||||
|
file,
|
||||||
|
line,
|
||||||
|
column,
|
||||||
|
endLine,
|
||||||
|
endColumn,
|
||||||
|
}: Model.Selection) {
|
||||||
|
const docs = yield* Documents;
|
||||||
|
const doc = yield* docs.open(file);
|
||||||
|
const end = (endLine ?? line) - 1;
|
||||||
|
const range: Range = {
|
||||||
|
start: { line: line - 1, character: column === undefined ? 0 : column - 1 },
|
||||||
|
end: {
|
||||||
|
line: end,
|
||||||
|
character:
|
||||||
|
endColumn === undefined ? lineText(doc, end).length : endColumn - 1,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
return { doc, textDocument: { uri: doc.uri }, range };
|
||||||
|
});
|
||||||
|
|
||||||
|
const overlaps = (a: Range, b: Range) =>
|
||||||
|
(a.start.line < b.end.line ||
|
||||||
|
(a.start.line === b.end.line && a.start.character <= b.end.character)) &&
|
||||||
|
(b.start.line < a.end.line ||
|
||||||
|
(b.start.line === a.end.line && b.start.character <= a.end.character));
|
||||||
|
|
||||||
|
const codeActionsAt = Effect.fn("Editor.codeActionsAt")(function* ({
|
||||||
|
kind,
|
||||||
|
...where
|
||||||
|
}: Model.CodeActions) {
|
||||||
|
const lsp = yield* LanguageServer;
|
||||||
|
const diags = yield* Diagnostics;
|
||||||
|
const { doc, textDocument, range } = yield* selection(where);
|
||||||
|
const diagnostics = (yield* diags.forDocument(doc)).filter((d) =>
|
||||||
|
overlaps(d.range, range),
|
||||||
|
);
|
||||||
|
const actions = yield* lsp.request(CodeActionRequest.type, {
|
||||||
|
textDocument,
|
||||||
|
range,
|
||||||
|
context: {
|
||||||
|
diagnostics,
|
||||||
|
triggerKind: CodeActionTriggerKind.Invoked,
|
||||||
|
...(kind === undefined ? {} : { only: [kind] }),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return actions ?? [];
|
||||||
|
});
|
||||||
|
|
||||||
|
export const codeActions = Effect.fn("Editor.codeActions")(function* (
|
||||||
|
params: Model.CodeActions,
|
||||||
|
) {
|
||||||
|
return (yield* codeActionsAt(params)).map(
|
||||||
|
(action): Model.CodeAction => ({
|
||||||
|
title: action.title,
|
||||||
|
kind: CodeAction.is(action) ? action.kind : undefined,
|
||||||
|
preferred: CodeAction.is(action) && action.isPreferred === true,
|
||||||
|
fixes: CodeAction.is(action)
|
||||||
|
? (action.diagnostics ?? []).map((d) => markup(d.message))
|
||||||
|
: [],
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
export const applyCodeAction = Effect.fn("Editor.applyCodeAction")(function* ({
|
||||||
|
title,
|
||||||
|
...params
|
||||||
|
}: Model.ApplyCodeAction) {
|
||||||
|
const lsp = yield* LanguageServer;
|
||||||
|
const action = (yield* codeActionsAt(params)).find((a) => a.title === title);
|
||||||
|
if (action === undefined)
|
||||||
|
return yield* new LspError({
|
||||||
|
method: "codeAction",
|
||||||
|
message: `no code action titled "${title}"`,
|
||||||
|
});
|
||||||
|
const execute = (command: Command) =>
|
||||||
|
lsp
|
||||||
|
.request(ExecuteCommandRequest.type, {
|
||||||
|
command: command.command,
|
||||||
|
...(command.arguments && { arguments: command.arguments }),
|
||||||
|
})
|
||||||
|
.pipe(Effect.as(`command ${command.command}`));
|
||||||
|
if (!CodeAction.is(action)) {
|
||||||
|
return {
|
||||||
|
applied: true,
|
||||||
|
files: [],
|
||||||
|
operations: [yield* execute(action)],
|
||||||
|
} satisfies Model.WorkspaceEdit;
|
||||||
|
}
|
||||||
|
const resolved =
|
||||||
|
action.edit === undefined &&
|
||||||
|
action.command === undefined &&
|
||||||
|
lsp.capabilities.codeActionProvider
|
||||||
|
? yield* lsp.request(CodeActionResolveRequest.type, action)
|
||||||
|
: action;
|
||||||
|
const result = yield* workspaceEdit(resolved.edit ?? {}, true);
|
||||||
|
const operations = resolved.command
|
||||||
|
? [...result.operations, yield* execute(resolved.command)]
|
||||||
|
: result.operations;
|
||||||
|
return { ...result, operations };
|
||||||
|
});
|
||||||
|
|
||||||
|
export const format = Effect.fn("Editor.format")(function* ({
|
||||||
|
file,
|
||||||
|
tabSize = 4,
|
||||||
|
insertSpaces = true,
|
||||||
|
apply = false,
|
||||||
|
}: Model.Format) {
|
||||||
|
const lsp = yield* LanguageServer;
|
||||||
|
const docs = yield* Documents;
|
||||||
|
const doc = yield* docs.open(file);
|
||||||
|
const edits =
|
||||||
|
(yield* lsp.request(DocumentFormattingRequest.type, {
|
||||||
|
textDocument: { uri: doc.uri },
|
||||||
|
options: { tabSize, insertSpaces },
|
||||||
|
})) ?? [];
|
||||||
|
if (apply && edits.length > 0)
|
||||||
|
yield* docs.write(doc.uri, TextDocument.applyEdits(doc, edits));
|
||||||
|
return {
|
||||||
|
applied: apply,
|
||||||
|
files: [
|
||||||
|
{
|
||||||
|
file: docs.file(doc.uri),
|
||||||
|
edits: edits.map((e) => ({ ...range(e.range), newText: e.newText })),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
operations: [],
|
||||||
|
} satisfies Model.WorkspaceEdit;
|
||||||
|
});
|
||||||
|
|
||||||
|
export const callHierarchy = Effect.fn("Editor.callHierarchy")(function* ({
|
||||||
|
direction = "incoming",
|
||||||
|
...where
|
||||||
|
}: Model.CallHierarchy) {
|
||||||
|
const lsp = yield* LanguageServer;
|
||||||
|
const docs = yield* Documents;
|
||||||
|
const { textDocument, position } = yield* locate(where);
|
||||||
|
const item = (yield* lsp.request(CallHierarchyPrepareRequest.type, {
|
||||||
|
textDocument,
|
||||||
|
position,
|
||||||
|
}))?.[0];
|
||||||
|
if (item === undefined) return [];
|
||||||
|
const calls =
|
||||||
|
direction === "incoming"
|
||||||
|
? (
|
||||||
|
(yield* lsp.request(CallHierarchyIncomingCallsRequest.type, {
|
||||||
|
item,
|
||||||
|
})) ?? []
|
||||||
|
).map((c) => ({ item: c.from, sites: c.fromRanges }))
|
||||||
|
: (
|
||||||
|
(yield* lsp.request(CallHierarchyOutgoingCallsRequest.type, {
|
||||||
|
item,
|
||||||
|
})) ?? []
|
||||||
|
).map((c) => ({ item: c.to, sites: c.fromRanges }));
|
||||||
|
return calls.map(
|
||||||
|
({ item, sites }): Model.Call => ({
|
||||||
|
name: item.name,
|
||||||
|
kind: symbolKinds[item.kind] ?? "unknown",
|
||||||
|
detail: item.detail,
|
||||||
|
file: docs.file(item.uri),
|
||||||
|
line: item.selectionRange.start.line + 1,
|
||||||
|
column: item.selectionRange.start.character + 1,
|
||||||
|
sites: sites.map((r) => ({
|
||||||
|
line: r.start.line + 1,
|
||||||
|
column: r.start.character + 1,
|
||||||
|
})),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
});
|
||||||
@@ -0,0 +1,671 @@
|
|||||||
|
/**
|
||||||
|
* A running language server: process, JSON-RPC connection and the LSP
|
||||||
|
* lifecycle, exposed as an Effect service.
|
||||||
|
*
|
||||||
|
* `request` waits for the server to become idle (no `$/progress` work in
|
||||||
|
* flight) and retries transient `ContentModified` / `ServerCancelled`
|
||||||
|
* failures, so callers can treat the server as if it were always ready.
|
||||||
|
*
|
||||||
|
* The process is supervised: when it exits unexpectedly it is started again
|
||||||
|
* according to the restart policy, the documents that were open are opened in
|
||||||
|
* the new process, and requests caught by the crash wait for it and retry.
|
||||||
|
* Once the policy gives up, every request fails with an `LspError` saying so.
|
||||||
|
*/
|
||||||
|
import { spawn } from "node:child_process";
|
||||||
|
import { PassThrough } from "node:stream";
|
||||||
|
import { NodeStream } from "@effect/platform-node";
|
||||||
|
import {
|
||||||
|
Cause,
|
||||||
|
Context,
|
||||||
|
Deferred,
|
||||||
|
Duration,
|
||||||
|
Effect,
|
||||||
|
FiberSet,
|
||||||
|
HashSet,
|
||||||
|
Layer,
|
||||||
|
PubSub,
|
||||||
|
Schedule,
|
||||||
|
Schema,
|
||||||
|
type Scope,
|
||||||
|
Stream,
|
||||||
|
SubscriptionRef,
|
||||||
|
} from "effect";
|
||||||
|
import {
|
||||||
|
CancellationTokenSource,
|
||||||
|
type ClientCapabilities,
|
||||||
|
type CompletionItemKind,
|
||||||
|
type ConfigurationParams,
|
||||||
|
ConfigurationRequest,
|
||||||
|
createMessageConnection,
|
||||||
|
DidChangeConfigurationNotification,
|
||||||
|
DidChangeTextDocumentNotification,
|
||||||
|
type DidChangeTextDocumentParams,
|
||||||
|
DidCloseTextDocumentNotification,
|
||||||
|
type DidCloseTextDocumentParams,
|
||||||
|
DidOpenTextDocumentNotification,
|
||||||
|
type DidOpenTextDocumentParams,
|
||||||
|
ErrorCodes,
|
||||||
|
ExitNotification,
|
||||||
|
type HandlerResult,
|
||||||
|
InitializedNotification,
|
||||||
|
InitializeRequest,
|
||||||
|
type InitializeResult,
|
||||||
|
LogMessageNotification,
|
||||||
|
type LSPAny,
|
||||||
|
LSPErrorCodes,
|
||||||
|
type MessageType,
|
||||||
|
type NotificationType,
|
||||||
|
RegistrationRequest,
|
||||||
|
type RequestType,
|
||||||
|
ResponseError,
|
||||||
|
type ServerCapabilities,
|
||||||
|
ShowMessageNotification,
|
||||||
|
ShutdownRequest,
|
||||||
|
StreamMessageReader,
|
||||||
|
StreamMessageWriter,
|
||||||
|
type SymbolKind,
|
||||||
|
type TextDocumentItem,
|
||||||
|
UnregistrationRequest,
|
||||||
|
WorkDoneProgressCreateRequest,
|
||||||
|
WorkspaceFoldersRequest,
|
||||||
|
} from "vscode-languageserver-protocol/node";
|
||||||
|
import { URI } from "vscode-uri";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* What to do when the server exits unexpectedly: never start it again, always
|
||||||
|
* start it again, or start it again but give up after this many crashes in a
|
||||||
|
* row (less than a minute apart).
|
||||||
|
*/
|
||||||
|
export type Restart = "never" | "always" | number;
|
||||||
|
|
||||||
|
export interface Options {
|
||||||
|
readonly command: string;
|
||||||
|
readonly args: ReadonlyArray<string>;
|
||||||
|
/** Absolute path of the workspace root. */
|
||||||
|
readonly root: string;
|
||||||
|
readonly initializationOptions?: unknown;
|
||||||
|
/** Served as `workspace/configuration` and pushed via `didChangeConfiguration`. */
|
||||||
|
readonly settings?: unknown;
|
||||||
|
/** Defaults to giving up after 3 crashes in a row. */
|
||||||
|
readonly restart?: Restart;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Failures worth retrying: the server was busy, or it went away and may come back. */
|
||||||
|
const transient: ReadonlySet<number> = new Set([
|
||||||
|
LSPErrorCodes.ContentModified,
|
||||||
|
LSPErrorCodes.ServerCancelled,
|
||||||
|
ErrorCodes.ConnectionInactive,
|
||||||
|
ErrorCodes.PendingResponseRejected,
|
||||||
|
ErrorCodes.MessageWriteError,
|
||||||
|
]);
|
||||||
|
|
||||||
|
export class LspError extends Schema.TaggedError<LspError>()("LspError", {
|
||||||
|
method: Schema.String,
|
||||||
|
message: Schema.String,
|
||||||
|
code: Schema.optional(Schema.Int),
|
||||||
|
}) {
|
||||||
|
get retryable() {
|
||||||
|
return this.code !== undefined && transient.has(this.code);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export type LogLevel = "error" | "warning" | "info" | "debug";
|
||||||
|
export interface LogMessage {
|
||||||
|
readonly level: LogLevel;
|
||||||
|
readonly message: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class LanguageServer extends Context.Service<
|
||||||
|
LanguageServer,
|
||||||
|
{
|
||||||
|
readonly root: string;
|
||||||
|
readonly capabilities: ServerCapabilities;
|
||||||
|
readonly serverInfo:
|
||||||
|
| { readonly name: string; readonly version?: string | undefined }
|
||||||
|
| undefined;
|
||||||
|
readonly request: <P, R>(
|
||||||
|
type: RequestType<P, R, unknown>,
|
||||||
|
params: P,
|
||||||
|
) => Effect.Effect<R, LspError>;
|
||||||
|
readonly notify: <P>(
|
||||||
|
type: NotificationType<P>,
|
||||||
|
params: P,
|
||||||
|
) => Effect.Effect<void>;
|
||||||
|
readonly notifications: <P>(type: NotificationType<P>) => Stream.Stream<P>;
|
||||||
|
/** Handle a request the server sends to us; unhandled methods answer `MethodNotFound`. */
|
||||||
|
readonly onRequest: <P, R>(
|
||||||
|
type: RequestType<P, R, unknown>,
|
||||||
|
handler: (params: P) => Effect.Effect<R, unknown>,
|
||||||
|
) => Effect.Effect<void, never, Scope.Scope>;
|
||||||
|
/** `window/logMessage` and `window/showMessage` traffic, plus restart announcements. */
|
||||||
|
readonly logs: Stream.Stream<LogMessage>;
|
||||||
|
}
|
||||||
|
>()("lsp-mcp/lsp/LanguageServer") {
|
||||||
|
static readonly layer = (options: Options) =>
|
||||||
|
Layer.effect(LanguageServer, make(options));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** One process and its connection; replaced on crash. */
|
||||||
|
interface Session {
|
||||||
|
readonly initialized: InitializeResult;
|
||||||
|
readonly exited: Deferred.Deferred<never, LspError>;
|
||||||
|
readonly send: <P, R>(
|
||||||
|
type: RequestType<P, R, unknown>,
|
||||||
|
params: P,
|
||||||
|
) => Effect.Effect<R, LspError>;
|
||||||
|
readonly notify: <P>(
|
||||||
|
type: NotificationType<P>,
|
||||||
|
params: P,
|
||||||
|
) => Effect.Effect<void, LspError>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const make = Effect.fn("LanguageServer.make")(function* (options: Options) {
|
||||||
|
const run = yield* FiberSet.makeRuntime<never>();
|
||||||
|
const runPromise = yield* FiberSet.makeRuntimePromise<never>();
|
||||||
|
const events = yield* PubSub.unbounded<{
|
||||||
|
readonly method: string;
|
||||||
|
readonly params: unknown;
|
||||||
|
}>();
|
||||||
|
const lifecycle = yield* PubSub.unbounded<LogMessage>();
|
||||||
|
const progress = yield* SubscriptionRef.make(
|
||||||
|
HashSet.empty<string | number>(),
|
||||||
|
);
|
||||||
|
const initialized = yield* Deferred.make<InitializeResult, LspError>();
|
||||||
|
/** The live session, or the one being started; failed once the policy gives up. */
|
||||||
|
let current = Deferred.makeUnsafe<Session, LspError>();
|
||||||
|
const session = Effect.suspend(() => Deferred.await(current));
|
||||||
|
/** What the server has been told is open, to open it again after a restart. */
|
||||||
|
const documents = new Map<string, TextDocumentItem>();
|
||||||
|
const limit =
|
||||||
|
options.restart === "never"
|
||||||
|
? 0
|
||||||
|
: options.restart === "always"
|
||||||
|
? Number.POSITIVE_INFINITY
|
||||||
|
: (options.restart ?? 3);
|
||||||
|
|
||||||
|
const rootUri = URI.file(options.root).toString();
|
||||||
|
const workspaceFolders = [
|
||||||
|
{ uri: rootUri, name: options.root.split(/[\\/]/).pop() ?? options.root },
|
||||||
|
];
|
||||||
|
const handlers = new Map<
|
||||||
|
string,
|
||||||
|
(params: unknown) => HandlerResult<unknown, unknown>
|
||||||
|
>([
|
||||||
|
[WorkDoneProgressCreateRequest.method, () => undefined],
|
||||||
|
[RegistrationRequest.method, () => undefined],
|
||||||
|
[UnregistrationRequest.method, () => undefined],
|
||||||
|
[
|
||||||
|
ConfigurationRequest.method,
|
||||||
|
(params) =>
|
||||||
|
(params as ConfigurationParams).items.map((item) =>
|
||||||
|
section(options.settings, item.section),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
[WorkspaceFoldersRequest.method, () => workspaceFolders],
|
||||||
|
]);
|
||||||
|
|
||||||
|
const failure = (method: string, error: unknown) =>
|
||||||
|
error instanceof ResponseError
|
||||||
|
? new LspError({ method, message: error.message, code: error.code })
|
||||||
|
: new LspError({
|
||||||
|
method,
|
||||||
|
message: error instanceof Error ? error.message : String(error),
|
||||||
|
});
|
||||||
|
|
||||||
|
const announce = (level: LogLevel, message: string) =>
|
||||||
|
Effect.andThen(
|
||||||
|
level === "error" ? Effect.logError(message) : Effect.logWarning(message),
|
||||||
|
PubSub.publish(lifecycle, { level, message }),
|
||||||
|
);
|
||||||
|
|
||||||
|
const start = Effect.fn("LanguageServer.start")(function* () {
|
||||||
|
const exited = yield* Deferred.make<never, LspError>();
|
||||||
|
const exit = (message: string) => {
|
||||||
|
if (Deferred.isDoneUnsafe(exited)) return;
|
||||||
|
// Whoever was using this session now waits for the next one; swap before
|
||||||
|
// signalling, as waiters resume synchronously and may give up right away.
|
||||||
|
if (Deferred.isDoneUnsafe(current)) current = Deferred.makeUnsafe();
|
||||||
|
Deferred.doneUnsafe(
|
||||||
|
exited,
|
||||||
|
Effect.fail(
|
||||||
|
new LspError({
|
||||||
|
method: "connection",
|
||||||
|
message,
|
||||||
|
code: ErrorCodes.ConnectionInactive,
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const child = yield* Effect.acquireRelease(
|
||||||
|
Effect.sync(() =>
|
||||||
|
spawn(options.command, options.args, {
|
||||||
|
cwd: options.root,
|
||||||
|
stdio: "pipe",
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
(child) =>
|
||||||
|
Effect.sync(() => void (child.exitCode === null && child.kill())),
|
||||||
|
);
|
||||||
|
child.on("error", (error) =>
|
||||||
|
exit(`failed to start ${options.command}: ${error.message}`),
|
||||||
|
);
|
||||||
|
child.on("exit", (code, signal) =>
|
||||||
|
exit(`${options.command} exited (${signal ?? code})`),
|
||||||
|
);
|
||||||
|
yield* NodeStream.fromReadable<Uint8Array, never>({
|
||||||
|
evaluate: () => child.stderr,
|
||||||
|
onError: () => undefined as never,
|
||||||
|
}).pipe(
|
||||||
|
Stream.decodeText(),
|
||||||
|
Stream.splitLines,
|
||||||
|
Stream.runForEach((line) => Effect.logDebug(line)),
|
||||||
|
Effect.annotateLogs("source", `${options.command}:stderr`),
|
||||||
|
Effect.forkScoped,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Writes go through a buffer: a broken pipe must end the session, not
|
||||||
|
// surface as a write failure (vscode-jsonrpc leaks those as unhandled rejections).
|
||||||
|
const input = new PassThrough();
|
||||||
|
input.pipe(child.stdin);
|
||||||
|
child.stdin.on("error", (error) =>
|
||||||
|
exit(`${options.command}: ${error.message}`),
|
||||||
|
);
|
||||||
|
const connection = createMessageConnection(
|
||||||
|
new StreamMessageReader(child.stdout),
|
||||||
|
new StreamMessageWriter(input),
|
||||||
|
{
|
||||||
|
error: (m) => run(Effect.logError(m)),
|
||||||
|
warn: (m) => run(Effect.logWarning(m)),
|
||||||
|
info: (m) => run(Effect.logInfo(m)),
|
||||||
|
log: (m) => run(Effect.logDebug(m)),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
yield* Effect.addFinalizer(() => Effect.sync(() => connection.dispose()));
|
||||||
|
connection.onClose(() => exit(`${options.command} closed the connection`));
|
||||||
|
connection.onNotification((method, params) => {
|
||||||
|
PubSub.publishUnsafe(events, { method, params });
|
||||||
|
});
|
||||||
|
connection.onUnhandledProgress(({ token, value }) =>
|
||||||
|
run(
|
||||||
|
SubscriptionRef.update(progress, (active) =>
|
||||||
|
value.kind === "begin"
|
||||||
|
? HashSet.add(active, token)
|
||||||
|
: value.kind === "end"
|
||||||
|
? HashSet.remove(active, token)
|
||||||
|
: active,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
connection.onRequest(
|
||||||
|
(method, params) =>
|
||||||
|
handlers.get(method)?.(params) ??
|
||||||
|
new ResponseError(ErrorCodes.MethodNotFound, `unhandled ${method}`),
|
||||||
|
);
|
||||||
|
connection.listen();
|
||||||
|
|
||||||
|
const send = <P, R>(type: RequestType<P, R, unknown>, params: P) =>
|
||||||
|
Effect.callback<R, LspError>((resume) => {
|
||||||
|
const cancellation = new CancellationTokenSource();
|
||||||
|
try {
|
||||||
|
connection
|
||||||
|
.sendRequest(type, params as never, cancellation.token)
|
||||||
|
.then(
|
||||||
|
(result) => resume(Effect.succeed(result)),
|
||||||
|
(error) => resume(Effect.fail(failure(type.method, error))),
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
resume(Effect.fail(failure(type.method, error)));
|
||||||
|
}
|
||||||
|
return Effect.ignore(Effect.try(() => cancellation.cancel()));
|
||||||
|
}).pipe(Effect.raceFirst(Deferred.await(exited)));
|
||||||
|
|
||||||
|
const notify = <P>(type: NotificationType<P>, params: P) =>
|
||||||
|
Effect.tryPromise({
|
||||||
|
try: () => connection.sendNotification(type, params as never),
|
||||||
|
catch: (error) => failure(type.method, error),
|
||||||
|
});
|
||||||
|
|
||||||
|
yield* SubscriptionRef.set(progress, HashSet.empty());
|
||||||
|
const initialized = yield* send(InitializeRequest.type, {
|
||||||
|
processId: process.pid,
|
||||||
|
clientInfo: { name: "lsp-mcp", version: "0.1.2" },
|
||||||
|
locale: "en",
|
||||||
|
rootPath: options.root,
|
||||||
|
rootUri,
|
||||||
|
workspaceFolders,
|
||||||
|
capabilities: clientCapabilities,
|
||||||
|
initializationOptions: options.initializationOptions as LSPAny,
|
||||||
|
});
|
||||||
|
yield* notify(InitializedNotification.type, {});
|
||||||
|
if (options.settings !== undefined) {
|
||||||
|
yield* notify(DidChangeConfigurationNotification.type, {
|
||||||
|
settings: options.settings as LSPAny,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
yield* Effect.forEach(
|
||||||
|
documents.values(),
|
||||||
|
(textDocument) =>
|
||||||
|
notify(DidOpenTextDocumentNotification.type, { textDocument }),
|
||||||
|
{ discard: true },
|
||||||
|
);
|
||||||
|
const shutdown = Effect.tryPromise(() =>
|
||||||
|
connection.sendRequest(ShutdownRequest.type),
|
||||||
|
).pipe(
|
||||||
|
Effect.timeout("3 seconds"),
|
||||||
|
Effect.andThen(
|
||||||
|
Effect.tryPromise(() =>
|
||||||
|
connection.sendNotification(ExitNotification.type),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Effect.andThen(Deferred.await(exited).pipe(Effect.timeout("2 seconds"))),
|
||||||
|
Effect.ignore,
|
||||||
|
);
|
||||||
|
yield* Effect.addFinalizer(() =>
|
||||||
|
Effect.suspend(() =>
|
||||||
|
Deferred.isDoneUnsafe(exited) ? Effect.void : shutdown,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
return { initialized, exited, send, notify } satisfies Session;
|
||||||
|
});
|
||||||
|
|
||||||
|
/** Runs one session until its process exits, and fails with the reason. */
|
||||||
|
const serve = Effect.scoped(
|
||||||
|
Effect.gen(function* () {
|
||||||
|
const session = yield* start().pipe(
|
||||||
|
Effect.tapError((error) => Deferred.fail(initialized, error)),
|
||||||
|
);
|
||||||
|
yield* Deferred.succeed(current, session);
|
||||||
|
yield* Deferred.succeed(initialized, session.initialized);
|
||||||
|
return yield* Deferred.await(session.exited);
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
const policy = restarts(limit).pipe(
|
||||||
|
// Only crashes are worth retrying; a server that fails to initialize is broken.
|
||||||
|
Schedule.while(({ input }) => input.code === ErrorCodes.ConnectionInactive),
|
||||||
|
Schedule.tap(({ input, output, duration }) =>
|
||||||
|
announce(
|
||||||
|
"warning",
|
||||||
|
`${input.message}; restarting in ${Duration.format(duration)}${
|
||||||
|
Number.isFinite(limit) ? ` (crash ${output} of ${limit})` : ""
|
||||||
|
}`,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
const giveUp = (error: LspError) => {
|
||||||
|
const reason =
|
||||||
|
error.code !== ErrorCodes.ConnectionInactive
|
||||||
|
? `restarting ${options.command} failed (${error.method}: ${error.message})`
|
||||||
|
: limit === 0
|
||||||
|
? `${error.message}; restarting is disabled`
|
||||||
|
: `${error.message}; that is ${limit} crash${limit === 1 ? "" : "es"} in a row`;
|
||||||
|
const message = `${reason}. Restart lsp-mcp to recover`;
|
||||||
|
return Effect.andThen(
|
||||||
|
announce("error", message),
|
||||||
|
Deferred.fail(current, new LspError({ method: "connection", message })),
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
yield* serve.pipe(Effect.retryOrElse(policy, giveUp), Effect.forkScoped);
|
||||||
|
const first = yield* Deferred.await(initialized);
|
||||||
|
|
||||||
|
const idle = SubscriptionRef.changes(progress).pipe(
|
||||||
|
Stream.filter(HashSet.isEmpty),
|
||||||
|
Stream.runHead,
|
||||||
|
Effect.timeoutOption("15 seconds"),
|
||||||
|
Effect.asVoid,
|
||||||
|
);
|
||||||
|
|
||||||
|
const request = <P, R>(type: RequestType<P, R, unknown>, params: P) =>
|
||||||
|
session.pipe(
|
||||||
|
Effect.tap(() => idle),
|
||||||
|
Effect.flatMap((session) => session.send(type, params)),
|
||||||
|
Effect.retry({
|
||||||
|
while: (error) => error.retryable,
|
||||||
|
schedule: Schedule.exponential("100 millis").pipe(
|
||||||
|
Schedule.upTo({ times: 5 }),
|
||||||
|
),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
const notify = <P>(type: NotificationType<P>, params: P) =>
|
||||||
|
Effect.sync(() => track(documents, type.method, params)).pipe(
|
||||||
|
Effect.andThen(session),
|
||||||
|
Effect.flatMap((session) => session.notify(type, params)),
|
||||||
|
Effect.catch((error) => Effect.logWarning(error.message)),
|
||||||
|
);
|
||||||
|
|
||||||
|
const notifications = <P>(type: NotificationType<P>): Stream.Stream<P> =>
|
||||||
|
Stream.fromPubSub(events).pipe(
|
||||||
|
Stream.filter((event) => event.method === type.method),
|
||||||
|
Stream.map((event) => event.params as P),
|
||||||
|
);
|
||||||
|
|
||||||
|
const onRequest = <P, R>(
|
||||||
|
type: RequestType<P, R, unknown>,
|
||||||
|
handler: (params: P) => Effect.Effect<R, unknown>,
|
||||||
|
) =>
|
||||||
|
Effect.acquireRelease(
|
||||||
|
Effect.sync(() =>
|
||||||
|
handlers.set(
|
||||||
|
type.method,
|
||||||
|
(params) =>
|
||||||
|
runPromise(
|
||||||
|
handler(params as P).pipe(
|
||||||
|
Effect.mapError((error) =>
|
||||||
|
error instanceof ResponseError
|
||||||
|
? error
|
||||||
|
: new ResponseError(
|
||||||
|
ErrorCodes.InternalError,
|
||||||
|
error instanceof Error ? error.message : String(error),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
) as Promise<never>,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
() => Effect.sync(() => handlers.delete(type.method)),
|
||||||
|
).pipe(Effect.asVoid);
|
||||||
|
|
||||||
|
const logs = Stream.merge(
|
||||||
|
notifications(LogMessageNotification.type),
|
||||||
|
notifications(ShowMessageNotification.type),
|
||||||
|
).pipe(
|
||||||
|
Stream.map(
|
||||||
|
({ type, message }): LogMessage => ({
|
||||||
|
level: logLevels[type] ?? "info",
|
||||||
|
message,
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
Stream.merge(Stream.fromPubSub(lifecycle)),
|
||||||
|
);
|
||||||
|
|
||||||
|
return LanguageServer.of({
|
||||||
|
root: options.root,
|
||||||
|
capabilities: first.capabilities,
|
||||||
|
serverInfo: first.serverInfo,
|
||||||
|
request,
|
||||||
|
notify,
|
||||||
|
notifications,
|
||||||
|
onRequest,
|
||||||
|
logs,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
/** A crash this long after the previous one starts a fresh streak. */
|
||||||
|
const quiet = Duration.minutes(1);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Restart delays: exponential from 500ms, capped at 30s, giving up once
|
||||||
|
* `limit` crashes happened in a row. The output is the length of the streak.
|
||||||
|
*/
|
||||||
|
const restarts = (limit: number) =>
|
||||||
|
Schedule.fromStepWithMetadata(
|
||||||
|
Effect.sync(() => {
|
||||||
|
let streak = 0;
|
||||||
|
return ({ elapsedSincePrevious }: Schedule.InputMetadata<LspError>) => {
|
||||||
|
streak =
|
||||||
|
elapsedSincePrevious > Duration.toMillis(quiet) ? 1 : streak + 1;
|
||||||
|
return streak > limit
|
||||||
|
? Cause.done(streak)
|
||||||
|
: Effect.succeed<[number, Duration.Duration]>([
|
||||||
|
streak,
|
||||||
|
Duration.millis(Math.min(500 * 2 ** (streak - 1), 30_000)),
|
||||||
|
]);
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
/** Keep the open-document bookkeeping in step with what we tell the server. */
|
||||||
|
const track = (
|
||||||
|
documents: Map<string, TextDocumentItem>,
|
||||||
|
method: string,
|
||||||
|
params: unknown,
|
||||||
|
) => {
|
||||||
|
switch (method) {
|
||||||
|
case DidOpenTextDocumentNotification.method: {
|
||||||
|
const { textDocument } = params as DidOpenTextDocumentParams;
|
||||||
|
documents.set(textDocument.uri, textDocument);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case DidChangeTextDocumentNotification.method: {
|
||||||
|
const { textDocument, contentChanges } =
|
||||||
|
params as DidChangeTextDocumentParams;
|
||||||
|
const open = documents.get(textDocument.uri);
|
||||||
|
const last = contentChanges.at(-1);
|
||||||
|
if (open && last && !("range" in last)) {
|
||||||
|
documents.set(textDocument.uri, {
|
||||||
|
...open,
|
||||||
|
version: textDocument.version,
|
||||||
|
text: last.text,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case DidCloseTextDocumentNotification.method:
|
||||||
|
documents.delete((params as DidCloseTextDocumentParams).textDocument.uri);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const logLevels: Record<MessageType, LogLevel> = {
|
||||||
|
1: "error",
|
||||||
|
2: "warning",
|
||||||
|
3: "info",
|
||||||
|
4: "debug",
|
||||||
|
5: "debug",
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Resolve a dotted `section` path inside the configured settings. */
|
||||||
|
const section = (settings: unknown, path: string | undefined): LSPAny =>
|
||||||
|
(path === undefined
|
||||||
|
? settings
|
||||||
|
: path
|
||||||
|
.split(".")
|
||||||
|
.reduce<unknown>(
|
||||||
|
(value, key) => (value as Record<string, unknown> | null)?.[key],
|
||||||
|
settings,
|
||||||
|
)) as LSPAny;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* What we tell the server we can do. Deliberately narrow: no dynamic
|
||||||
|
* registration, no snippets, no watched files (servers fall back to their
|
||||||
|
* own watchers), UTF-16 positions only.
|
||||||
|
*/
|
||||||
|
const clientCapabilities: ClientCapabilities = {
|
||||||
|
general: {
|
||||||
|
positionEncodings: ["utf-16"],
|
||||||
|
markdown: { parser: "marked", version: "1.1.0" },
|
||||||
|
},
|
||||||
|
workspace: {
|
||||||
|
applyEdit: true,
|
||||||
|
workspaceEdit: {
|
||||||
|
documentChanges: true,
|
||||||
|
resourceOperations: ["create", "rename", "delete"],
|
||||||
|
failureHandling: "abort",
|
||||||
|
},
|
||||||
|
configuration: true,
|
||||||
|
workspaceFolders: true,
|
||||||
|
symbol: { symbolKind: { valueSet: allSymbolKinds() } },
|
||||||
|
didChangeConfiguration: { dynamicRegistration: false },
|
||||||
|
didChangeWatchedFiles: { dynamicRegistration: false },
|
||||||
|
},
|
||||||
|
window: {
|
||||||
|
workDoneProgress: true,
|
||||||
|
showMessage: {},
|
||||||
|
showDocument: { support: false },
|
||||||
|
},
|
||||||
|
textDocument: {
|
||||||
|
synchronization: {
|
||||||
|
didSave: true,
|
||||||
|
willSave: false,
|
||||||
|
willSaveWaitUntil: false,
|
||||||
|
},
|
||||||
|
publishDiagnostics: {
|
||||||
|
relatedInformation: true,
|
||||||
|
versionSupport: true,
|
||||||
|
codeDescriptionSupport: true,
|
||||||
|
dataSupport: true,
|
||||||
|
},
|
||||||
|
diagnostic: { dynamicRegistration: false, relatedDocumentSupport: false },
|
||||||
|
hover: { contentFormat: ["markdown", "plaintext"] },
|
||||||
|
completion: {
|
||||||
|
completionItem: {
|
||||||
|
snippetSupport: false,
|
||||||
|
documentationFormat: ["markdown", "plaintext"],
|
||||||
|
deprecatedSupport: true,
|
||||||
|
},
|
||||||
|
completionItemKind: {
|
||||||
|
valueSet: Array.from(
|
||||||
|
{ length: 25 },
|
||||||
|
(_, i) => (i + 1) as CompletionItemKind,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
contextSupport: true,
|
||||||
|
},
|
||||||
|
signatureHelp: {
|
||||||
|
signatureInformation: {
|
||||||
|
documentationFormat: ["markdown", "plaintext"],
|
||||||
|
parameterInformation: { labelOffsetSupport: true },
|
||||||
|
activeParameterSupport: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
definition: { linkSupport: true },
|
||||||
|
typeDefinition: { linkSupport: true },
|
||||||
|
implementation: { linkSupport: true },
|
||||||
|
declaration: { linkSupport: true },
|
||||||
|
references: {},
|
||||||
|
documentSymbol: {
|
||||||
|
hierarchicalDocumentSymbolSupport: true,
|
||||||
|
symbolKind: { valueSet: allSymbolKinds() },
|
||||||
|
},
|
||||||
|
codeAction: {
|
||||||
|
codeActionLiteralSupport: {
|
||||||
|
codeActionKind: {
|
||||||
|
valueSet: [
|
||||||
|
"",
|
||||||
|
"quickfix",
|
||||||
|
"refactor",
|
||||||
|
"refactor.extract",
|
||||||
|
"refactor.inline",
|
||||||
|
"refactor.rewrite",
|
||||||
|
"source",
|
||||||
|
"source.organizeImports",
|
||||||
|
"source.fixAll",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
isPreferredSupport: true,
|
||||||
|
dataSupport: true,
|
||||||
|
resolveSupport: { properties: ["edit"] },
|
||||||
|
},
|
||||||
|
rename: { prepareSupport: true },
|
||||||
|
formatting: {},
|
||||||
|
callHierarchy: {},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
function allSymbolKinds() {
|
||||||
|
return Array.from({ length: 26 }, (_, i) => (i + 1) as SymbolKind);
|
||||||
|
}
|
||||||
+148
@@ -0,0 +1,148 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
/**
|
||||||
|
* CLI entry point.
|
||||||
|
*
|
||||||
|
* lsp-mcp [--stdio | --http <port>] [--root <dir>] -- <server> [args...]
|
||||||
|
*/
|
||||||
|
import { NodeRuntime, NodeServices } from "@effect/platform-node";
|
||||||
|
import { Cause, Effect, Layer, Option, Path, References, Schema } from "effect";
|
||||||
|
import { Argument, Command, Flag } from "effect/unstable/cli";
|
||||||
|
import * as Server from "./Server.ts";
|
||||||
|
|
||||||
|
const Json = Flag.withSchema(Schema.fromJsonString(Schema.Json));
|
||||||
|
const crashes = { message: "expected a whole number of crashes" };
|
||||||
|
const Restart = Schema.Union([
|
||||||
|
Schema.Literals(["never", "always"]),
|
||||||
|
Schema.NumberFromString.check(
|
||||||
|
Schema.isInt(crashes),
|
||||||
|
Schema.isGreaterThanOrEqualTo(0, crashes),
|
||||||
|
),
|
||||||
|
]);
|
||||||
|
|
||||||
|
const command = Command.make(
|
||||||
|
"lsp-mcp",
|
||||||
|
{
|
||||||
|
transport: Flag.Int("http").pipe(
|
||||||
|
Flag.withDescription(
|
||||||
|
"Serve MCP over Streamable HTTP at http://localhost:<port>/mcp",
|
||||||
|
),
|
||||||
|
Flag.map((port) => ({ _tag: "http", port }) as const),
|
||||||
|
Flag.orElse(() =>
|
||||||
|
Flag.Boolean("stdio").pipe(
|
||||||
|
Flag.withDescription("Serve MCP over stdio (the default)"),
|
||||||
|
Flag.withDefault(false),
|
||||||
|
Flag.map(() => ({ _tag: "stdio" }) as const),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
root: Flag.Directory("root", { mustExist: true }).pipe(
|
||||||
|
Flag.withAlias("r"),
|
||||||
|
Flag.withDescription("Workspace root handed to the language server"),
|
||||||
|
Flag.withDefault("."),
|
||||||
|
),
|
||||||
|
initOptions: Flag.String("init-options").pipe(
|
||||||
|
Json,
|
||||||
|
Flag.withDescription(
|
||||||
|
"JSON passed to the language server as initializationOptions",
|
||||||
|
),
|
||||||
|
Flag.optional,
|
||||||
|
),
|
||||||
|
settings: Flag.String("settings").pipe(
|
||||||
|
Json,
|
||||||
|
Flag.withDescription(
|
||||||
|
"JSON served as workspace/configuration and sent as didChangeConfiguration",
|
||||||
|
),
|
||||||
|
Flag.optional,
|
||||||
|
),
|
||||||
|
languages: Flag.KeyValuePair("language").pipe(
|
||||||
|
Flag.withDescription(
|
||||||
|
"Extra file extension to languageId mapping, e.g. --language vue=vue",
|
||||||
|
),
|
||||||
|
Flag.withDefault({}),
|
||||||
|
),
|
||||||
|
capacity: Flag.Int("open-documents").pipe(
|
||||||
|
Flag.withDescription(
|
||||||
|
"Documents kept open in the language server before the least recently used is closed",
|
||||||
|
),
|
||||||
|
Flag.withDefault(32),
|
||||||
|
),
|
||||||
|
restart: Flag.String("restart").pipe(
|
||||||
|
Flag.withSchema(Restart),
|
||||||
|
Flag.withMetavar("never|always|N"),
|
||||||
|
Flag.withDescription(
|
||||||
|
"When the language server crashes: never start it again, always start it again, or give up after N crashes in a row (default 3)",
|
||||||
|
),
|
||||||
|
Flag.withDefault(3),
|
||||||
|
),
|
||||||
|
command: Argument.String("server").pipe(
|
||||||
|
Argument.withDescription("Language server executable"),
|
||||||
|
),
|
||||||
|
args: Argument.String("args").pipe(
|
||||||
|
Argument.withDescription(
|
||||||
|
"Arguments for the language server; put `--` before them",
|
||||||
|
),
|
||||||
|
Argument.variadic(),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
Effect.fn(function* ({
|
||||||
|
transport,
|
||||||
|
root,
|
||||||
|
initOptions,
|
||||||
|
settings,
|
||||||
|
languages,
|
||||||
|
capacity,
|
||||||
|
restart,
|
||||||
|
command,
|
||||||
|
args,
|
||||||
|
}) {
|
||||||
|
const path = yield* Path.Path;
|
||||||
|
return yield* Layer.launch(
|
||||||
|
Server.layer({
|
||||||
|
transport,
|
||||||
|
server: {
|
||||||
|
// a relative executable path is relative to where lsp-mcp was started, not to the root
|
||||||
|
command: command.includes("/") ? path.resolve(command) : command,
|
||||||
|
args,
|
||||||
|
root: path.resolve(root),
|
||||||
|
initializationOptions: Option.getOrUndefined(initOptions),
|
||||||
|
settings: Option.getOrUndefined(settings),
|
||||||
|
restart,
|
||||||
|
},
|
||||||
|
documents: { capacity, languages },
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
).pipe(
|
||||||
|
Command.withDescription(
|
||||||
|
"Expose a Language Server to MCP clients as a headless editor",
|
||||||
|
),
|
||||||
|
Command.withExamples([
|
||||||
|
{
|
||||||
|
command: "lsp-mcp -- typescript-language-server --stdio",
|
||||||
|
description: "TypeScript over stdio",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
command: "lsp-mcp --http 3000 --root ./crate -- rust-analyzer",
|
||||||
|
description: "Rust over HTTP",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
command: `lsp-mcp --settings '{"gopls":{"staticcheck":true}}' -- gopls`,
|
||||||
|
description: "Go with workspace settings",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
command: "lsp-mcp --restart always -- clangd",
|
||||||
|
description: "Keep bringing the server back, however often it crashes",
|
||||||
|
},
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
|
||||||
|
command.pipe(
|
||||||
|
Command.run({ version: "0.1.2" }),
|
||||||
|
Effect.tapCause((cause) =>
|
||||||
|
Cause.hasInterruptsOnly(cause) ? Effect.void : Effect.logError(cause),
|
||||||
|
),
|
||||||
|
// stdout may be the MCP channel, so every log line goes to stderr
|
||||||
|
Effect.provideService(References.LogToStderr, true),
|
||||||
|
Effect.provide(NodeServices.layer),
|
||||||
|
NodeRuntime.runMain({ disableErrorReporting: true }),
|
||||||
|
);
|
||||||
@@ -0,0 +1,207 @@
|
|||||||
|
import { NodeServices } from "@effect/platform-node";
|
||||||
|
import { assert, describe, it } from "@effect/vitest";
|
||||||
|
import { Effect, FileSystem, Layer } from "effect";
|
||||||
|
import { NotificationType, RequestType } from "vscode-languageserver-protocol";
|
||||||
|
import { Diagnostics } from "../src/lsp/Diagnostics.ts";
|
||||||
|
import { Documents } from "../src/lsp/Documents.ts";
|
||||||
|
import * as Editor from "../src/lsp/Editor.ts";
|
||||||
|
import { LanguageServer } from "../src/lsp/LanguageServer.ts";
|
||||||
|
import { workspace } from "./fixtures/workspace.ts";
|
||||||
|
|
||||||
|
const State = new RequestType<null, { open: number; closes: number }, void>(
|
||||||
|
"fixture/state",
|
||||||
|
);
|
||||||
|
const Progress = new RequestType<number, string, void>("fixture/progress");
|
||||||
|
const Crash = new NotificationType<null>("fixture/crash");
|
||||||
|
|
||||||
|
const TestLayer = Layer.mergeAll(
|
||||||
|
Documents.layer({ capacity: 2, languages: {} }),
|
||||||
|
Diagnostics.layer,
|
||||||
|
).pipe(
|
||||||
|
Layer.provideMerge(
|
||||||
|
workspace({
|
||||||
|
"a.ts": "const alpha = 1\nexport const beta = alpha + alpha\n",
|
||||||
|
"b.ts": "// TODO fix\n",
|
||||||
|
"c.ts": "",
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
Layer.provideMerge(NodeServices.layer),
|
||||||
|
);
|
||||||
|
|
||||||
|
describe("Editor", () => {
|
||||||
|
it.layer(TestLayer, { excludeTestServices: true })(
|
||||||
|
"with the fixture server",
|
||||||
|
(it) => {
|
||||||
|
it.effect("addresses positions by 1-based line and symbol name", () =>
|
||||||
|
Effect.gen(function* () {
|
||||||
|
const byColumn = yield* Editor.hover({
|
||||||
|
file: "a.ts",
|
||||||
|
line: 2,
|
||||||
|
column: 22,
|
||||||
|
});
|
||||||
|
const bySymbol = yield* Editor.hover({
|
||||||
|
file: "a.ts",
|
||||||
|
line: 2,
|
||||||
|
symbol: "alpha",
|
||||||
|
});
|
||||||
|
assert.strictEqual(byColumn?.contents, "word **alpha**");
|
||||||
|
assert.strictEqual(bySymbol?.contents, "word **alpha**");
|
||||||
|
|
||||||
|
const refs = yield* Editor.references({
|
||||||
|
file: "a.ts",
|
||||||
|
line: 1,
|
||||||
|
symbol: "alpha",
|
||||||
|
});
|
||||||
|
assert.deepStrictEqual(
|
||||||
|
refs.map((r) => [r.line, r.column, r.preview]),
|
||||||
|
[
|
||||||
|
[1, 7, "const alpha = 1"],
|
||||||
|
[2, 21, "export const beta = alpha + alpha"],
|
||||||
|
[2, 29, "export const beta = alpha + alpha"],
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
it.effect("sees edits made on disk without being told", () =>
|
||||||
|
Effect.gen(function* () {
|
||||||
|
const fs = yield* FileSystem.FileSystem;
|
||||||
|
const docs = yield* Documents;
|
||||||
|
const lsp = yield* LanguageServer;
|
||||||
|
const before = yield* Editor.diagnostics({ file: "b.ts" });
|
||||||
|
assert.deepStrictEqual(
|
||||||
|
before.map((d) => [d.severity, d.line]),
|
||||||
|
[["warning", 1]],
|
||||||
|
);
|
||||||
|
|
||||||
|
yield* fs.writeFileString(
|
||||||
|
docs.uri("b.ts").replace("file://", ""),
|
||||||
|
"// done\n// TODO more\n",
|
||||||
|
);
|
||||||
|
const after = yield* Editor.diagnostics({ file: "b.ts" });
|
||||||
|
assert.deepStrictEqual(
|
||||||
|
after.map((d) => [d.severity, d.line]),
|
||||||
|
[["warning", 2]],
|
||||||
|
);
|
||||||
|
assert.strictEqual((yield* lsp.request(State, null)).open, 2);
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
it.effect("closes the least recently used document beyond capacity", () =>
|
||||||
|
Effect.gen(function* () {
|
||||||
|
const lsp = yield* LanguageServer;
|
||||||
|
const docs = yield* Documents;
|
||||||
|
yield* docs.open("a.ts");
|
||||||
|
yield* docs.open("b.ts");
|
||||||
|
yield* docs.open("c.ts");
|
||||||
|
yield* Effect.sleep("50 millis");
|
||||||
|
const state = yield* lsp.request(State, null);
|
||||||
|
assert.strictEqual(state.open, 2);
|
||||||
|
assert.isAtLeast(state.closes, 1);
|
||||||
|
assert.deepStrictEqual((yield* docs.opened).map(docs.file).sort(), [
|
||||||
|
"b.ts",
|
||||||
|
"c.ts",
|
||||||
|
]);
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
it.effect("previews a rename, and applies it only when asked", () =>
|
||||||
|
Effect.gen(function* () {
|
||||||
|
const docs = yield* Documents;
|
||||||
|
const preview = yield* Editor.rename({
|
||||||
|
file: "a.ts",
|
||||||
|
line: 1,
|
||||||
|
symbol: "alpha",
|
||||||
|
newName: "first",
|
||||||
|
});
|
||||||
|
assert.isFalse(preview.applied);
|
||||||
|
assert.strictEqual(preview.files[0]?.edits.length, 3);
|
||||||
|
assert.include((yield* docs.open("a.ts")).getText(), "alpha");
|
||||||
|
|
||||||
|
const applied = yield* Editor.rename({
|
||||||
|
file: "a.ts",
|
||||||
|
line: 1,
|
||||||
|
symbol: "alpha",
|
||||||
|
newName: "first",
|
||||||
|
apply: true,
|
||||||
|
});
|
||||||
|
assert.isTrue(applied.applied);
|
||||||
|
const text = (yield* docs.peek(docs.uri("a.ts"))).getText();
|
||||||
|
assert.strictEqual(
|
||||||
|
text,
|
||||||
|
"const first = 1\nexport const beta = first + first\n",
|
||||||
|
);
|
||||||
|
assert.strictEqual(
|
||||||
|
(yield* Editor.hover({ file: "a.ts", line: 1, symbol: "first" }))
|
||||||
|
?.contents,
|
||||||
|
"word **first**",
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
it.effect("waits for the server's background work before asking", () =>
|
||||||
|
Effect.gen(function* () {
|
||||||
|
const lsp = yield* LanguageServer;
|
||||||
|
yield* lsp.request(Progress, 300);
|
||||||
|
const started = Date.now();
|
||||||
|
const symbols = yield* Editor.documentSymbols({ file: "c.ts" });
|
||||||
|
assert.isAtLeast(Date.now() - started, 250);
|
||||||
|
assert.deepStrictEqual(symbols, []);
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
it.effect("explains what the server cannot do", () =>
|
||||||
|
Effect.gen(function* () {
|
||||||
|
const error = yield* Effect.flip(
|
||||||
|
Editor.definition({
|
||||||
|
file: "a.ts",
|
||||||
|
line: 1,
|
||||||
|
symbol: "alpha",
|
||||||
|
kind: "implementation",
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
assert.strictEqual(error._tag, "LspError");
|
||||||
|
assert.match(
|
||||||
|
error.message,
|
||||||
|
/does not support implementationProvider/,
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
it.layer(
|
||||||
|
Layer.mergeAll(
|
||||||
|
Documents.layer({ capacity: 2, languages: {} }),
|
||||||
|
Diagnostics.layer,
|
||||||
|
).pipe(
|
||||||
|
Layer.provideMerge(
|
||||||
|
workspace({ "a.ts": "const alpha = 1\n" }, { restart: 1 }),
|
||||||
|
),
|
||||||
|
Layer.provideMerge(NodeServices.layer),
|
||||||
|
),
|
||||||
|
{ excludeTestServices: true },
|
||||||
|
)("when the server crashes", (it) => {
|
||||||
|
it.effect(
|
||||||
|
"starts it again with the same documents open, until the policy gives up",
|
||||||
|
() =>
|
||||||
|
Effect.gen(function* () {
|
||||||
|
const lsp = yield* LanguageServer;
|
||||||
|
const hover = Editor.hover({
|
||||||
|
file: "a.ts",
|
||||||
|
line: 1,
|
||||||
|
symbol: "alpha",
|
||||||
|
});
|
||||||
|
yield* hover;
|
||||||
|
|
||||||
|
yield* lsp.notify(Crash, null);
|
||||||
|
assert.strictEqual((yield* hover)?.contents, "word **alpha**");
|
||||||
|
assert.strictEqual((yield* lsp.request(State, null)).open, 1);
|
||||||
|
|
||||||
|
yield* lsp.notify(Crash, null);
|
||||||
|
const error = yield* Effect.flip(hover);
|
||||||
|
assert.match(error.message, /1 crash in a row/);
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,144 @@
|
|||||||
|
import { NodeServices } from "@effect/platform-node";
|
||||||
|
import { assert, describe, it } from "@effect/vitest";
|
||||||
|
import { Effect, Layer } from "effect";
|
||||||
|
import { McpProtocol, McpSchema, McpServer } from "effect/unstable/ai";
|
||||||
|
import {
|
||||||
|
FetchHttpClient,
|
||||||
|
HttpClient,
|
||||||
|
HttpClientRequest,
|
||||||
|
HttpRouter,
|
||||||
|
} from "effect/unstable/http";
|
||||||
|
import { RpcClient, RpcSerialization } from "effect/unstable/rpc";
|
||||||
|
import { Diagnostics } from "../src/lsp/Diagnostics.ts";
|
||||||
|
import { Documents } from "../src/lsp/Documents.ts";
|
||||||
|
import * as Resources from "../src/Resources.ts";
|
||||||
|
import * as Server from "../src/Server.ts";
|
||||||
|
import { workspace } from "./fixtures/workspace.ts";
|
||||||
|
|
||||||
|
const mcp = McpServer.layerHttp({
|
||||||
|
name: "test",
|
||||||
|
version: "0.0.0",
|
||||||
|
path: "/mcp",
|
||||||
|
protocols: [McpProtocol.v2025_06_18],
|
||||||
|
});
|
||||||
|
|
||||||
|
const app = Layer.mergeAll(Server.tools, Resources.layer).pipe(
|
||||||
|
Layer.provide(mcp),
|
||||||
|
Layer.provide(
|
||||||
|
Layer.mergeAll(
|
||||||
|
Documents.layer({ capacity: 4, languages: {} }),
|
||||||
|
Diagnostics.layer,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Layer.provide(workspace({ "a.ts": "const alpha = 1\n" })),
|
||||||
|
Layer.provide(NodeServices.layer),
|
||||||
|
);
|
||||||
|
|
||||||
|
/** An MCP client talking to the app over in-memory HTTP. */
|
||||||
|
const client = Effect.gen(function* () {
|
||||||
|
const { dispose, handler } = HttpRouter.toWebHandler(app, {
|
||||||
|
disableLogger: true,
|
||||||
|
});
|
||||||
|
yield* Effect.addFinalizer(() => Effect.promise(dispose));
|
||||||
|
const headers = new Map<string, string>();
|
||||||
|
const fetch: typeof globalThis.fetch = Object.assign(
|
||||||
|
async (input: RequestInfo | URL, init?: RequestInit) => {
|
||||||
|
const request = new Request(input, init);
|
||||||
|
for (const [name, value] of headers) request.headers.set(name, value);
|
||||||
|
const response = await handler(request);
|
||||||
|
for (const name of ["Mcp-Session-Id", "Mcp-Protocol-Version"]) {
|
||||||
|
const value = response.headers.get(name);
|
||||||
|
if (value !== null) headers.set(name, value);
|
||||||
|
}
|
||||||
|
return response;
|
||||||
|
},
|
||||||
|
{ preconnect() {} },
|
||||||
|
);
|
||||||
|
const transport = RpcClient.layerProtocolHttp({
|
||||||
|
url: "http://localhost/mcp",
|
||||||
|
transformClient: HttpClient.mapRequest(
|
||||||
|
HttpClientRequest.setHeader(
|
||||||
|
"accept",
|
||||||
|
"application/json, text/event-stream",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
}).pipe(
|
||||||
|
Layer.provideMerge([
|
||||||
|
FetchHttpClient.layer,
|
||||||
|
RpcSerialization.layerJsonRpc(),
|
||||||
|
]),
|
||||||
|
Layer.provide(Layer.succeed(FetchHttpClient.Fetch, fetch)),
|
||||||
|
);
|
||||||
|
const client = yield* RpcClient.make(McpSchema.ClientRpcs).pipe(
|
||||||
|
Effect.provide(transport),
|
||||||
|
);
|
||||||
|
yield* client.initialize({
|
||||||
|
protocolVersion: "2025-06-18",
|
||||||
|
capabilities: {},
|
||||||
|
clientInfo: { name: "test", version: "0" },
|
||||||
|
});
|
||||||
|
return client;
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("MCP server", () => {
|
||||||
|
it.live(
|
||||||
|
"offers only the tools the language server supports, and serves them",
|
||||||
|
() =>
|
||||||
|
Effect.gen(function* () {
|
||||||
|
const mcp = yield* client;
|
||||||
|
const { tools } = yield* mcp["tools/list"]({});
|
||||||
|
assert.deepStrictEqual(tools.map((t) => t.name).sort(), [
|
||||||
|
"definition",
|
||||||
|
"diagnostics",
|
||||||
|
"document_symbols",
|
||||||
|
"hover",
|
||||||
|
"references",
|
||||||
|
"rename",
|
||||||
|
]);
|
||||||
|
assert.isTrue(
|
||||||
|
tools.find((t) => t.name === "hover")?.annotations?.readOnlyHint,
|
||||||
|
);
|
||||||
|
assert.isFalse(
|
||||||
|
tools.find((t) => t.name === "rename")?.annotations?.readOnlyHint,
|
||||||
|
);
|
||||||
|
|
||||||
|
const result = yield* mcp["tools/call"]({
|
||||||
|
name: "hover",
|
||||||
|
arguments: { file: "a.ts", line: 1, symbol: "alpha" },
|
||||||
|
});
|
||||||
|
assert.deepStrictEqual(result.structuredContent, {
|
||||||
|
contents: "word **alpha**",
|
||||||
|
});
|
||||||
|
|
||||||
|
const failure = yield* mcp["tools/call"]({
|
||||||
|
name: "hover",
|
||||||
|
arguments: { file: "missing.ts", line: 1 },
|
||||||
|
});
|
||||||
|
assert.isTrue(failure.isError);
|
||||||
|
assert.match(JSON.stringify(failure.content), /missing\.ts/);
|
||||||
|
}).pipe(Effect.scoped),
|
||||||
|
);
|
||||||
|
|
||||||
|
it.live("exposes the server and its diagnostics as resources", () =>
|
||||||
|
Effect.gen(function* () {
|
||||||
|
const mcp = yield* client;
|
||||||
|
const { resources } = yield* mcp["resources/list"]({});
|
||||||
|
assert.deepStrictEqual(resources.map((r) => r.uri).sort(), [
|
||||||
|
"lsp://diagnostics",
|
||||||
|
"lsp://server",
|
||||||
|
]);
|
||||||
|
const server = yield* mcp["resources/read"]({ uri: "lsp://server" });
|
||||||
|
const contents = server.contents[0];
|
||||||
|
assert.isTrue(
|
||||||
|
contents !== undefined &&
|
||||||
|
"text" in contents &&
|
||||||
|
JSON.parse(contents.text).name === "fixture",
|
||||||
|
);
|
||||||
|
const { resourceTemplates } = yield* mcp["resources/templates/list"]({});
|
||||||
|
assert.deepStrictEqual(
|
||||||
|
resourceTemplates.map((t) => t.uriTemplate),
|
||||||
|
["lsp://diagnostics/{file}"],
|
||||||
|
);
|
||||||
|
}).pipe(Effect.scoped),
|
||||||
|
);
|
||||||
|
});
|
||||||
Vendored
+162
@@ -0,0 +1,162 @@
|
|||||||
|
/**
|
||||||
|
* A miniature language server used by the tests. Words are symbols: hover
|
||||||
|
* describes the word under the cursor, definition is its first occurrence,
|
||||||
|
* references and rename cover every occurrence, and each line containing
|
||||||
|
* "TODO" yields a warning. `fixture/state` reports open/close counts and
|
||||||
|
* `fixture/crash` kills the process.
|
||||||
|
*/
|
||||||
|
import {
|
||||||
|
createConnection,
|
||||||
|
DiagnosticSeverity,
|
||||||
|
type Position,
|
||||||
|
TextDocumentSyncKind,
|
||||||
|
TextDocuments,
|
||||||
|
WorkDoneProgress,
|
||||||
|
} from "vscode-languageserver/node";
|
||||||
|
import { TextDocument } from "vscode-languageserver-textdocument";
|
||||||
|
|
||||||
|
const connection = createConnection(process.stdin, process.stdout);
|
||||||
|
const documents = new TextDocuments(TextDocument);
|
||||||
|
let closes = 0;
|
||||||
|
let progressToken = 0;
|
||||||
|
|
||||||
|
const wordAt = (doc: TextDocument, position: Position) => {
|
||||||
|
const text = doc.getText();
|
||||||
|
const offset = doc.offsetAt(position);
|
||||||
|
let start = offset;
|
||||||
|
let end = offset;
|
||||||
|
while (start > 0 && /\w/.test(text.charAt(start - 1))) start--;
|
||||||
|
while (end < text.length && /\w/.test(text.charAt(end))) end++;
|
||||||
|
return start === end ? undefined : text.slice(start, end);
|
||||||
|
};
|
||||||
|
|
||||||
|
const occurrences = (doc: TextDocument, word: string) => {
|
||||||
|
const text = doc.getText();
|
||||||
|
const ranges = [];
|
||||||
|
const pattern = new RegExp(`\\b${word}\\b`, "g");
|
||||||
|
for (const match of text.matchAll(pattern)) {
|
||||||
|
ranges.push({
|
||||||
|
start: doc.positionAt(match.index),
|
||||||
|
end: doc.positionAt(match.index + word.length),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return ranges;
|
||||||
|
};
|
||||||
|
|
||||||
|
documents.onDidClose(() => closes++);
|
||||||
|
documents.onDidChangeContent(({ document }) => {
|
||||||
|
const diagnostics = document
|
||||||
|
.getText()
|
||||||
|
.split("\n")
|
||||||
|
.flatMap((line, i) =>
|
||||||
|
line.includes("TODO")
|
||||||
|
? [
|
||||||
|
{
|
||||||
|
range: {
|
||||||
|
start: { line: i, character: 0 },
|
||||||
|
end: { line: i, character: line.length },
|
||||||
|
},
|
||||||
|
severity: DiagnosticSeverity.Warning,
|
||||||
|
message: "unresolved TODO",
|
||||||
|
source: "fixture",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
: [],
|
||||||
|
);
|
||||||
|
void connection.sendDiagnostics({
|
||||||
|
uri: document.uri,
|
||||||
|
version: document.version,
|
||||||
|
diagnostics,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
connection.onInitialize(() => ({
|
||||||
|
serverInfo: { name: "fixture", version: "1.0.0" },
|
||||||
|
capabilities: {
|
||||||
|
textDocumentSync: {
|
||||||
|
openClose: true,
|
||||||
|
change: TextDocumentSyncKind.Full,
|
||||||
|
save: { includeText: true },
|
||||||
|
},
|
||||||
|
hoverProvider: true,
|
||||||
|
definitionProvider: true,
|
||||||
|
referencesProvider: true,
|
||||||
|
renameProvider: true,
|
||||||
|
documentSymbolProvider: true,
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
connection.onHover(({ textDocument, position }) => {
|
||||||
|
const doc = documents.get(textDocument.uri);
|
||||||
|
const word = doc && wordAt(doc, position);
|
||||||
|
return word
|
||||||
|
? { contents: { kind: "markdown", value: `word **${word}**` } }
|
||||||
|
: null;
|
||||||
|
});
|
||||||
|
|
||||||
|
connection.onDefinition(({ textDocument, position }) => {
|
||||||
|
const doc = documents.get(textDocument.uri);
|
||||||
|
const word = doc && wordAt(doc, position);
|
||||||
|
const range = word && occurrences(doc, word)[0];
|
||||||
|
return range ? { uri: textDocument.uri, range } : null;
|
||||||
|
});
|
||||||
|
|
||||||
|
connection.onReferences(({ textDocument, position }) => {
|
||||||
|
const doc = documents.get(textDocument.uri);
|
||||||
|
const word = doc && wordAt(doc, position);
|
||||||
|
return word
|
||||||
|
? occurrences(doc, word).map((range) => ({ uri: textDocument.uri, range }))
|
||||||
|
: [];
|
||||||
|
});
|
||||||
|
|
||||||
|
connection.onRenameRequest(({ textDocument, position, newName }) => {
|
||||||
|
const doc = documents.get(textDocument.uri);
|
||||||
|
const word = doc && wordAt(doc, position);
|
||||||
|
if (!word) return null;
|
||||||
|
return {
|
||||||
|
documentChanges: [
|
||||||
|
{
|
||||||
|
textDocument: { uri: textDocument.uri, version: doc.version },
|
||||||
|
edits: occurrences(doc, word).map((range) => ({
|
||||||
|
range,
|
||||||
|
newText: newName,
|
||||||
|
})),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
connection.onDocumentSymbol(({ textDocument }) => {
|
||||||
|
const doc = documents.get(textDocument.uri);
|
||||||
|
if (!doc) return [];
|
||||||
|
const words = new Set(doc.getText().match(/\b[A-Za-z_]\w*\b/g) ?? []);
|
||||||
|
return [...words].flatMap((name) =>
|
||||||
|
occurrences(doc, name)
|
||||||
|
.slice(0, 1)
|
||||||
|
.map((range) => ({ name, kind: 13, range, selectionRange: range })),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
connection.onNotification("fixture/crash", () => process.exit(1));
|
||||||
|
connection.onRequest("fixture/state", () => ({
|
||||||
|
open: documents.all().length,
|
||||||
|
closes,
|
||||||
|
}));
|
||||||
|
connection.onRequest("fixture/progress", async (ms: number) => {
|
||||||
|
const token = `work-${progressToken++}`;
|
||||||
|
void connection.sendProgress(WorkDoneProgress.type, token, {
|
||||||
|
kind: "begin",
|
||||||
|
title: "indexing",
|
||||||
|
});
|
||||||
|
setTimeout(
|
||||||
|
() =>
|
||||||
|
void connection.sendProgress(WorkDoneProgress.type, token, {
|
||||||
|
kind: "end",
|
||||||
|
}),
|
||||||
|
ms,
|
||||||
|
);
|
||||||
|
return token;
|
||||||
|
});
|
||||||
|
|
||||||
|
documents.listen(connection);
|
||||||
|
connection.listen();
|
||||||
Vendored
+27
@@ -0,0 +1,27 @@
|
|||||||
|
import { fileURLToPath } from "node:url";
|
||||||
|
import { Effect, FileSystem, Layer, Path } from "effect";
|
||||||
|
import { LanguageServer, type Options } from "../../src/lsp/LanguageServer.ts";
|
||||||
|
|
||||||
|
const server = fileURLToPath(new URL("./server.ts", import.meta.url));
|
||||||
|
|
||||||
|
/** A temporary workspace with the given files, served by the fixture language server. */
|
||||||
|
export const workspace = (
|
||||||
|
files: Record<string, string>,
|
||||||
|
options?: Pick<Options, "restart">,
|
||||||
|
) =>
|
||||||
|
Layer.unwrap(
|
||||||
|
Effect.gen(function* () {
|
||||||
|
const fs = yield* FileSystem.FileSystem;
|
||||||
|
const path = yield* Path.Path;
|
||||||
|
const root = yield* fs.makeTempDirectoryScoped({ prefix: "lsp-mcp-" });
|
||||||
|
for (const [name, content] of Object.entries(files)) {
|
||||||
|
yield* fs.writeFileString(path.join(root, name), content);
|
||||||
|
}
|
||||||
|
return LanguageServer.layer({
|
||||||
|
command: process.execPath,
|
||||||
|
args: [server],
|
||||||
|
root,
|
||||||
|
...options,
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
);
|
||||||
+5
-28
@@ -2,50 +2,27 @@
|
|||||||
"$schema": "./node_modules/@effect/tsgo/schema.json",
|
"$schema": "./node_modules/@effect/tsgo/schema.json",
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"noEmit": true,
|
"noEmit": true,
|
||||||
// File Layout
|
|
||||||
// "rootDir": "./src",
|
|
||||||
// "outDir": "./dist",
|
|
||||||
|
|
||||||
// Environment Settings
|
|
||||||
// See also https://aka.ms/tsconfig/module
|
|
||||||
"module": "nodenext",
|
"module": "nodenext",
|
||||||
"target": "esnext",
|
"target": "esnext",
|
||||||
"types": [],
|
"types": ["node"],
|
||||||
// For nodejs:
|
"allowImportingTsExtensions": true,
|
||||||
// "lib": ["esnext"],
|
|
||||||
// "types": ["node"],
|
|
||||||
// and npm install -D @types/node
|
|
||||||
|
|
||||||
// Other Outputs
|
|
||||||
"sourceMap": true,
|
|
||||||
"declaration": true,
|
|
||||||
"declarationMap": true,
|
|
||||||
|
|
||||||
// Stricter Typechecking Options
|
|
||||||
"noUncheckedIndexedAccess": true,
|
"noUncheckedIndexedAccess": true,
|
||||||
"exactOptionalPropertyTypes": true,
|
"exactOptionalPropertyTypes": true,
|
||||||
|
|
||||||
// Style Options
|
|
||||||
"noImplicitReturns": true,
|
"noImplicitReturns": true,
|
||||||
"noImplicitOverride": true,
|
"noImplicitOverride": true,
|
||||||
"noUnusedLocals": true,
|
"noUnusedLocals": true,
|
||||||
"noUnusedParameters": true,
|
"noUnusedParameters": true,
|
||||||
"noFallthroughCasesInSwitch": true,
|
"noFallthroughCasesInSwitch": true,
|
||||||
"noPropertyAccessFromIndexSignature": true,
|
"noPropertyAccessFromIndexSignature": true,
|
||||||
|
|
||||||
// Recommended Options
|
|
||||||
"strict": true,
|
"strict": true,
|
||||||
"jsx": "react-jsx",
|
|
||||||
"verbatimModuleSyntax": true,
|
"verbatimModuleSyntax": true,
|
||||||
"isolatedModules": true,
|
"isolatedModules": true,
|
||||||
|
"erasableSyntaxOnly": true,
|
||||||
"noUncheckedSideEffectImports": true,
|
"noUncheckedSideEffectImports": true,
|
||||||
"moduleDetection": "force",
|
"moduleDetection": "force",
|
||||||
"skipLibCheck": true,
|
"skipLibCheck": true,
|
||||||
"plugins": [
|
"plugins": [{ "name": "@effect/language-service" }]
|
||||||
{
|
|
||||||
"name": "@effect/language-service"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
|
"include": ["src", "test", "vitest.config.ts"],
|
||||||
"exclude": ["node_modules", "repos"]
|
"exclude": ["node_modules", "repos"]
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
import { defineConfig } from "tsdown";
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
entry: "src/main.ts",
|
||||||
|
format: "esm",
|
||||||
|
outDir: "dist",
|
||||||
|
|
||||||
|
deps: {
|
||||||
|
alwaysBundle: ["*"],
|
||||||
|
},
|
||||||
|
|
||||||
|
treeshake: true,
|
||||||
|
minify: true,
|
||||||
|
sourcemap: false,
|
||||||
|
});
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
import { defineConfig } from "vitest/config";
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
test: {
|
||||||
|
include: ["test/**/*.test.ts"],
|
||||||
|
testTimeout: 20_000,
|
||||||
|
},
|
||||||
|
});
|
||||||
Reference in new issue
Block a user