@telorun/editor-protocol 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +17 -0
- package/README.md +197 -0
- package/dist/index.d.ts +159 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +34 -0
- package/editor-protocol-schema.json +373 -0
- package/package.json +50 -0
- package/src/index.ts +186 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# SUSTAINABLE USE LICENSE (Fair-code)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 CodeNet Sp. z o.o.
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to use, copy, modify, and distribute the Software for any purpose—including commercial purposes—subject to the following conditions:
|
|
6
|
+
|
|
7
|
+
1. ANTI-COMPETITION RESTRICTION: The Software may not be provided to third parties as a managed service, commercial SaaS (Software-as-a-Service), PaaS (Platform-as-a-Service), BaaS (Backend-as-a-Service), or similar offering where the primary value provided to the user is the functionality of the Software itself, without a separate commercial license from the copyright holder.
|
|
8
|
+
|
|
9
|
+
2. PERMITTED COMMERCIAL USE: You are free to use the Software to build, host, and monetize your own commercial applications, products, and services, provided such use does not violate Clause 1.
|
|
10
|
+
|
|
11
|
+
3. ATTRIBUTION: This copyright notice and license must be included in all copies or substantial portions of the Software.
|
|
12
|
+
|
|
13
|
+
4. CONTRIBUTIONS: Contributions to the Software are welcome and encouraged. By contributing, you agree that your contributions may be incorporated into the Software and distributed under this license.
|
|
14
|
+
|
|
15
|
+
5. DISCLAIMER: 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.
|
|
16
|
+
|
|
17
|
+
For commercial licensing, managed hosting exemptions, or enterprise inquiries, please contact <contact@codenet.pl>.
|
package/README.md
ADDED
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
# @telorun/editor-protocol
|
|
2
|
+
|
|
3
|
+
The contract between an **editor host** (VS Code, telo studio, any LSP client)
|
|
4
|
+
and a **telo language-server engine** — the part of the conversation LSP itself
|
|
5
|
+
does not define. An editor edits a module against one chosen telo version; that
|
|
6
|
+
version's engine produces every diagnostic and language feature, and the host
|
|
7
|
+
supplies everything the engine cannot do itself: files, imports, the hub.
|
|
8
|
+
|
|
9
|
+
This README is the specification. The TypeScript types in `src/index.ts` and
|
|
10
|
+
[`editor-protocol-schema.json`](./editor-protocol-schema.json) (for hosts written
|
|
11
|
+
in other languages) are its projections; the package has no dependencies.
|
|
12
|
+
|
|
13
|
+
## The engine
|
|
14
|
+
|
|
15
|
+
Telo `X` publishes its engine as `@telorun/language-server@X`: one
|
|
16
|
+
self-contained ES module, `dist/language-server.mjs`, with no imports. Its
|
|
17
|
+
`package.json` declares `teloEditorProtocol`, the protocol generation the engine
|
|
18
|
+
speaks — a host offers only versions whose generation it speaks.
|
|
19
|
+
|
|
20
|
+
The module exports one entry:
|
|
21
|
+
|
|
22
|
+
```ts
|
|
23
|
+
serve(port: { postMessage(message): void; addEventListener("message", (event) => void): void }): void
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
`port` carries **LSP JSON-RPC message objects** (not framed text) in both
|
|
27
|
+
directions; the engine reads each message from `event.data`. A Web Worker scope,
|
|
28
|
+
a `MessagePort` and a Node `parentPort` adapted to `addEventListener` all fit.
|
|
29
|
+
The host loads the module into a worker, calls `serve(self)` there, and speaks
|
|
30
|
+
LSP to the worker.
|
|
31
|
+
|
|
32
|
+
**The engine performs no I/O of its own** — no `fetch`, no filesystem. Every
|
|
33
|
+
byte it reads arrives through the `telo/*` requests below, which is what lets one
|
|
34
|
+
build run in a browser, a desktop app and an extension host alike, and what
|
|
35
|
+
leaves transports, credentials, caches and integrity checks with the host.
|
|
36
|
+
|
|
37
|
+
## Handshake
|
|
38
|
+
|
|
39
|
+
The host sends a standard `initialize`. The engine answers:
|
|
40
|
+
|
|
41
|
+
- `serverInfo: { name: "telo", version: "<identity>" }` — the engine's
|
|
42
|
+
**identity**. For a release build it is `X`, the telo version the engine
|
|
43
|
+
implements (the surface generation `requires: telo:` ranges are written
|
|
44
|
+
against) and exactly the version of the `@telorun/language-server` package it
|
|
45
|
+
was published as. A build made while that release is still pending — a
|
|
46
|
+
development checkout, a main-branch deployment — reports `X+unreleased`: it
|
|
47
|
+
implements `X` but is not the `X` that will be published, so it never shares
|
|
48
|
+
an identity with a published engine. A host orders identities and tests them
|
|
49
|
+
against `requires: telo:` intervals by semver precedence (build metadata
|
|
50
|
+
ignored), and tells engines apart by the whole string.
|
|
51
|
+
- `capabilities.experimental.telo.protocol: 1` — the generation it speaks.
|
|
52
|
+
- The standard capabilities it serves: full text sync, completion, hover,
|
|
53
|
+
definition, rename with `prepareRename`, signature help, `semanticTokens/full`
|
|
54
|
+
(the legend is in the capabilities), `codeAction` (quick fixes),
|
|
55
|
+
`codeLens`, and `workspace/executeCommand` for the commands below.
|
|
56
|
+
|
|
57
|
+
A host verifies every handshake: `serverInfo.name` is `telo`, the protocol is a
|
|
58
|
+
generation the host speaks, and an engine the host downloaded as version `X`
|
|
59
|
+
reports exactly `X`. An engine failing any of these is refused, not run.
|
|
60
|
+
|
|
61
|
+
The engine reads the client's `workspace.codeLens.refreshSupport`,
|
|
62
|
+
`workspace.semanticTokens.refreshSupport` and
|
|
63
|
+
`workspace.didChangeWatchedFiles.dynamicRegistration` capabilities; with the
|
|
64
|
+
last it registers a watcher for `**/*.yaml` and re-analyses what a changed file
|
|
65
|
+
feeds.
|
|
66
|
+
|
|
67
|
+
## URIs
|
|
68
|
+
|
|
69
|
+
Documents are `file:` URIs; a remote module is named by its import source as
|
|
70
|
+
written (`oci://host/repo@1.2.0`, `https://…`). One `file:` location has many
|
|
71
|
+
spellings, so the protocol fixes **one canonical form** and identity is
|
|
72
|
+
equality of canonical forms:
|
|
73
|
+
|
|
74
|
+
- there is no query and no fragment;
|
|
75
|
+
- a local file has an empty authority (`file:///…`; `file://localhost/…` is the
|
|
76
|
+
same location), and a file on a UNC share has the share's host as its
|
|
77
|
+
authority, ASCII-lowercased and percent-encoded like a path segment —
|
|
78
|
+
`\\ServerA\share\x` is `file://servera/share/x`, and the four-slash
|
|
79
|
+
spelling `file:////ServerA/share/x` folds to that same form;
|
|
80
|
+
- the path is its UTF-8 bytes, each percent-encoded except the unreserved
|
|
81
|
+
characters `A–Z a–z 0–9 - . _ ~` and `/`, with uppercase hex digits —
|
|
82
|
+
`My Project (copy)` is `My%20Project%20%28copy%29`;
|
|
83
|
+
- a local Windows drive is written `/<lowercase letter>%3A/` — `C:\Users\u` is
|
|
84
|
+
`file:///c%3A/Users/u`; the drive rule does not apply under a UNC host.
|
|
85
|
+
|
|
86
|
+
An engine emits every `file:` URI in canonical form. A host compares URIs only
|
|
87
|
+
by their canonical forms — a document the editor opened under another spelling
|
|
88
|
+
of the same location is the same document — and does not rewrite the URIs in
|
|
89
|
+
the messages it passes along.
|
|
90
|
+
|
|
91
|
+
## Host-served requests (engine → host)
|
|
92
|
+
|
|
93
|
+
A relative path is resolved against the **directory of the file** its `base`
|
|
94
|
+
names.
|
|
95
|
+
|
|
96
|
+
| Method | Params | Result |
|
|
97
|
+
| --- | --- | --- |
|
|
98
|
+
| `telo/read` | `{ uri }` | `{ uri, text }` — the text and the **canonical** location it was read from: a directory resolves to its `telo.yaml` (`TELO_MODULE_FILENAME`), an `oci://` tag to the reference the host resolved — or `null` when nothing exists at `uri`. |
|
|
99
|
+
| `telo/exists` | `{ base, relative }` | `boolean` — whether a file or directory exists there. |
|
|
100
|
+
| `telo/listDirectory` | `{ uri }` | `[{ name, kind }]`, or `null` when `uri` names nothing or a file. `kind` is `file`, `directory`, `symlink` or `other`, read without following links — a symbolic link is `symlink` whatever it points at. |
|
|
101
|
+
| `telo/hub/searchRefs` | `{ query }` | `[{ ref, latestVersion, description? }]` — refs the configured hub matches `query` against. |
|
|
102
|
+
| `telo/hub/listVersions` | `{ ref }` | `[{ version, integrity? }]`, newest first; `[]` for a ref the hub does not track. |
|
|
103
|
+
|
|
104
|
+
Any other failure — an unreachable registry, a hub that did not answer, a
|
|
105
|
+
permission error — is a **JSON-RPC error** carrying its reason, never a silent
|
|
106
|
+
`null`: the engine reports it (a load failure becomes a diagnostic; a failed hub
|
|
107
|
+
lookup is logged through `window/logMessage` and, where a user asked for it,
|
|
108
|
+
shown). Hub lookups feed completion and upgrade lenses only.
|
|
109
|
+
|
|
110
|
+
The engine expands `include:` globs itself, by walking `telo/listDirectory`
|
|
111
|
+
from the including file's directory — collecting `file` entries and descending
|
|
112
|
+
into `directory` ones, never through a link, as the kernel does — so a host
|
|
113
|
+
serves directories, never patterns, and every host matches a glob exactly as the
|
|
114
|
+
kernel does. `telo/read` and `telo/exists` do follow links.
|
|
115
|
+
|
|
116
|
+
Remote modules are read through `telo/read` like any document: the host owns
|
|
117
|
+
the transport (`oci://`, `https://`, a manifest cache) and any integrity check.
|
|
118
|
+
A candidate version an import upgrade considers is read as its versioned ref
|
|
119
|
+
(`oci://host/repo@1.3.0`).
|
|
120
|
+
|
|
121
|
+
## `telo/requirements` (engine → host notification)
|
|
122
|
+
|
|
123
|
+
Sent after each analysis of an **owner module** — a `telo.yaml`, or a manifest
|
|
124
|
+
analysed standalone:
|
|
125
|
+
|
|
126
|
+
```json
|
|
127
|
+
{
|
|
128
|
+
"owner": "file:///ws/app/telo.yaml",
|
|
129
|
+
"documents": ["file:///ws/app/telo.yaml", "file:///ws/app/routes.yaml"],
|
|
130
|
+
"ranges": [
|
|
131
|
+
{ "module": "file:///ws/app/telo.yaml", "text": ">=0.100.0",
|
|
132
|
+
"interval": { "min": { "version": "0.100.0", "inclusive": true } } },
|
|
133
|
+
{ "module": "oci://ghcr.io/telorun/sql@0.25.0", "text": ">=0.90.0 <0.120.0",
|
|
134
|
+
"interval": { "min": { "version": "0.90.0", "inclusive": true },
|
|
135
|
+
"max": { "version": "0.120.0", "inclusive": false } } }
|
|
136
|
+
]
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
- `owner` — the owner module's URI.
|
|
141
|
+
- `documents` — the owner and every partial it includes. A host routes these
|
|
142
|
+
documents to the engine it chose for `owner`.
|
|
143
|
+
- `ranges` — every `requires: telo:` range in the owner's import closure, one
|
|
144
|
+
per declaring module. The owner declares one exactly when an entry's `module`
|
|
145
|
+
equals `owner`. `interval` is the range reduced to its edges by the engine,
|
|
146
|
+
through the analyzer's own range reader; an absent edge is open. A host only
|
|
147
|
+
compares plain versions against it — it never parses a range.
|
|
148
|
+
|
|
149
|
+
A range the analyzer refuses as malformed is reported as a diagnostic and does
|
|
150
|
+
not appear here.
|
|
151
|
+
|
|
152
|
+
## `Diagnostic.data`
|
|
153
|
+
|
|
154
|
+
Every diagnostic the engine publishes may carry:
|
|
155
|
+
|
|
156
|
+
```ts
|
|
157
|
+
{ fix?: { replacement: string; tag?: "ref" | "cel" | "module-path" },
|
|
158
|
+
resource?: { kind: string; name: string },
|
|
159
|
+
path?: string }
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
`fix` is a mechanically applicable repair — the whole corrected value at the
|
|
163
|
+
diagnostic's range, written behind the YAML tag `tag` when present. A host
|
|
164
|
+
returns the diagnostic unchanged in a `textDocument/codeAction` request's
|
|
165
|
+
context, and the engine answers with the quick fix that applies it. `resource`
|
|
166
|
+
and `path` locate the offending value inside a manifest (a host may use them to
|
|
167
|
+
place a diagnostic on a form field). `Diagnostic.tags` carries LSP's
|
|
168
|
+
`Deprecated` / `Unnecessary`.
|
|
169
|
+
|
|
170
|
+
## Commands
|
|
171
|
+
|
|
172
|
+
| Command | Arguments | Effect |
|
|
173
|
+
| --- | --- | --- |
|
|
174
|
+
| `telo.upgradeImport` | `[{ uri, aliases }]` | Recomputes the document's import upgrades and applies those of `aliases` through `workspace/applyEdit`. |
|
|
175
|
+
| `telo.upgradeAllImports` | `[{ uri, aliases }]` | Same, for every alias the summary lens named. |
|
|
176
|
+
| `telo.refreshImportUpgrades` | — | Drops cached version lists and verdicts, then `workspace/codeLens/refresh`. |
|
|
177
|
+
|
|
178
|
+
The engine's code lenses over an `imports:` block name the first two.
|
|
179
|
+
|
|
180
|
+
## Generations
|
|
181
|
+
|
|
182
|
+
`TELO_EDITOR_PROTOCOL` is the generation this package describes (`1`).
|
|
183
|
+
**Additions within a generation are negotiated by capability**: a new request, a
|
|
184
|
+
new optional field or a new command is advertised by the side that has it and
|
|
185
|
+
used only when advertised, so an older host and a newer engine (or the reverse)
|
|
186
|
+
keep working. **An incompatible change bumps the generation**: a host speaking
|
|
187
|
+
generation `N` offers only engines whose `teloEditorProtocol` is `N`, and an
|
|
188
|
+
engine refuses nothing — it simply is not selected.
|
|
189
|
+
|
|
190
|
+
**A receiver must ignore what it does not know**, so that an addition within a
|
|
191
|
+
generation never breaks the other side: a member it does not know is ignored,
|
|
192
|
+
and an enumeration value it does not know is read as that enumeration's
|
|
193
|
+
least-capable value — a `telo/listDirectory` entry `kind` as `other` (neither
|
|
194
|
+
read as a file nor descended into), a `Diagnostic.data.fix.tag` as no fix
|
|
195
|
+
offered. `editor-protocol-schema.json` is written the same way: its objects are
|
|
196
|
+
open and its enumerations admit any string, so a message carrying a later
|
|
197
|
+
addition still validates.
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
/** The protocol generation this package describes. Additions within a
|
|
2
|
+
* generation are negotiated by capability; an incompatible change bumps it. */
|
|
3
|
+
export declare const TELO_EDITOR_PROTOCOL = 1;
|
|
4
|
+
/** What an engine advertises under `ServerCapabilities.experimental`. */
|
|
5
|
+
export interface TeloExperimentalCapabilities {
|
|
6
|
+
telo: TeloProtocolCapability;
|
|
7
|
+
}
|
|
8
|
+
export interface TeloProtocolCapability {
|
|
9
|
+
/** The generation the engine speaks. */
|
|
10
|
+
protocol: number;
|
|
11
|
+
}
|
|
12
|
+
/** The file a module directory is read as: `telo/read` of a directory answers
|
|
13
|
+
* with this file inside it. */
|
|
14
|
+
export declare const TELO_MODULE_FILENAME = "telo.yaml";
|
|
15
|
+
/** `initialize`'s `serverInfo.name` for every telo engine. Its
|
|
16
|
+
* `serverInfo.version` is the engine's identity: the telo version `X` for a
|
|
17
|
+
* release build, `X+unreleased` for a build made while that release is still
|
|
18
|
+
* pending. */
|
|
19
|
+
export declare const TELO_SERVER_NAME = "telo";
|
|
20
|
+
/** Method names of the requests an engine sends and a host serves, and of the
|
|
21
|
+
* notification an engine sends. */
|
|
22
|
+
export declare const TeloMethod: {
|
|
23
|
+
readonly read: "telo/read";
|
|
24
|
+
readonly exists: "telo/exists";
|
|
25
|
+
readonly listDirectory: "telo/listDirectory";
|
|
26
|
+
readonly hubSearchRefs: "telo/hub/searchRefs";
|
|
27
|
+
readonly hubListVersions: "telo/hub/listVersions";
|
|
28
|
+
readonly requirements: "telo/requirements";
|
|
29
|
+
};
|
|
30
|
+
export type TeloMethod = (typeof TeloMethod)[keyof typeof TeloMethod];
|
|
31
|
+
/** `telo/read`: the text at `uri`. `uri` is a document URI (`file:///…`) or an
|
|
32
|
+
* import source (`oci://host/repo@1.2.0`, `https://…`); the host reads it
|
|
33
|
+
* through whatever transport it owns. */
|
|
34
|
+
export interface ReadParams {
|
|
35
|
+
uri: string;
|
|
36
|
+
}
|
|
37
|
+
/** The text read, with the CANONICAL location it was read from — a directory
|
|
38
|
+
* resolves to its {@link TELO_MODULE_FILENAME}, an `oci://` tag to the reference the host
|
|
39
|
+
* resolved. `null` when nothing exists at `uri`; any other failure is a
|
|
40
|
+
* JSON-RPC error carrying its reason. */
|
|
41
|
+
export type ReadResult = {
|
|
42
|
+
uri: string;
|
|
43
|
+
text: string;
|
|
44
|
+
} | null;
|
|
45
|
+
/** `telo/exists`: whether a file or directory exists at `relative`, resolved
|
|
46
|
+
* against the DIRECTORY of the file `base` names. */
|
|
47
|
+
export interface ExistsParams {
|
|
48
|
+
base: string;
|
|
49
|
+
relative: string;
|
|
50
|
+
}
|
|
51
|
+
export type ExistsResult = boolean;
|
|
52
|
+
/** `telo/listDirectory`: the entries of the directory at `uri`. */
|
|
53
|
+
export interface ListDirectoryParams {
|
|
54
|
+
uri: string;
|
|
55
|
+
}
|
|
56
|
+
/** What a directory entry is, read WITHOUT following a link: a symbolic link
|
|
57
|
+
* is `symlink` whatever it points at. A receiver reads a kind it does not know
|
|
58
|
+
* as `other`. */
|
|
59
|
+
export type DirectoryEntryKind = "file" | "directory" | "symlink" | "other";
|
|
60
|
+
export interface DirectoryEntry {
|
|
61
|
+
name: string;
|
|
62
|
+
kind: DirectoryEntryKind;
|
|
63
|
+
}
|
|
64
|
+
/** `null` when `uri` names nothing or names a file. */
|
|
65
|
+
export type ListDirectoryResult = DirectoryEntry[] | null;
|
|
66
|
+
/** `telo/hub/searchRefs`: module refs the configured telo hub matches `query`
|
|
67
|
+
* against (a substring over every registered ref). */
|
|
68
|
+
export interface SearchRefsParams {
|
|
69
|
+
query: string;
|
|
70
|
+
}
|
|
71
|
+
export interface HubRef {
|
|
72
|
+
ref: string;
|
|
73
|
+
latestVersion: string;
|
|
74
|
+
description?: string;
|
|
75
|
+
}
|
|
76
|
+
export type SearchRefsResult = HubRef[];
|
|
77
|
+
/** `telo/hub/listVersions`: every version the hub tracks for a location ref,
|
|
78
|
+
* newest first, each with its import pin when the hub has one. `[]` for a ref
|
|
79
|
+
* the hub does not track; an unreachable hub is a JSON-RPC error. */
|
|
80
|
+
export interface ListVersionsParams {
|
|
81
|
+
ref: string;
|
|
82
|
+
}
|
|
83
|
+
export interface ModuleVersion {
|
|
84
|
+
version: string;
|
|
85
|
+
/** Canonical `sha256-<base64url>` pin. */
|
|
86
|
+
integrity?: string;
|
|
87
|
+
}
|
|
88
|
+
export type ListVersionsResult = ModuleVersion[];
|
|
89
|
+
/** Params and result of every host-served request, by method. */
|
|
90
|
+
export interface TeloHostRequests {
|
|
91
|
+
"telo/read": [ReadParams, ReadResult];
|
|
92
|
+
"telo/exists": [ExistsParams, ExistsResult];
|
|
93
|
+
"telo/listDirectory": [ListDirectoryParams, ListDirectoryResult];
|
|
94
|
+
"telo/hub/searchRefs": [SearchRefsParams, SearchRefsResult];
|
|
95
|
+
"telo/hub/listVersions": [ListVersionsParams, ListVersionsResult];
|
|
96
|
+
}
|
|
97
|
+
/** One edge of a version interval. */
|
|
98
|
+
export interface VersionBound {
|
|
99
|
+
version: string;
|
|
100
|
+
inclusive: boolean;
|
|
101
|
+
}
|
|
102
|
+
/** A `requires: telo:` range reduced to its edges; an absent edge is open. */
|
|
103
|
+
export interface VersionInterval {
|
|
104
|
+
min?: VersionBound;
|
|
105
|
+
max?: VersionBound;
|
|
106
|
+
}
|
|
107
|
+
export interface RequirementsRange {
|
|
108
|
+
/** URI of the module document declaring the range. */
|
|
109
|
+
module: string;
|
|
110
|
+
/** The range as written. */
|
|
111
|
+
text: string;
|
|
112
|
+
interval: VersionInterval;
|
|
113
|
+
}
|
|
114
|
+
/** `telo/requirements`: sent after each analysis of an owner module. */
|
|
115
|
+
export interface RequirementsParams {
|
|
116
|
+
/** URI of the owner module's `telo.yaml` (or standalone manifest). */
|
|
117
|
+
owner: string;
|
|
118
|
+
/** URIs of the owner's member documents — the owner and every partial it
|
|
119
|
+
* includes. A host routes these documents to the engine chosen for `owner`. */
|
|
120
|
+
documents: string[];
|
|
121
|
+
/** Every `requires: telo:` range in the owner's import closure. The owner
|
|
122
|
+
* itself declares one exactly when an entry's `module` equals `owner`. */
|
|
123
|
+
ranges: RequirementsRange[];
|
|
124
|
+
}
|
|
125
|
+
/** The YAML tags a repair may write. A receiver that does not know a tag
|
|
126
|
+
* offers no fix for it. */
|
|
127
|
+
export type DiagnosticFixTag = "ref" | "cel" | "module-path";
|
|
128
|
+
/** A mechanically applicable repair: the whole corrected value at the
|
|
129
|
+
* diagnostic's range, written behind `tag` when one is present. */
|
|
130
|
+
export interface DiagnosticFix {
|
|
131
|
+
replacement: string;
|
|
132
|
+
tag?: DiagnosticFixTag;
|
|
133
|
+
}
|
|
134
|
+
/** What an engine puts in `Diagnostic.data`. A host hands the diagnostic back
|
|
135
|
+
* unchanged in `codeAction`'s context, which is how a fix round-trips. */
|
|
136
|
+
export interface TeloDiagnosticData {
|
|
137
|
+
fix?: DiagnosticFix;
|
|
138
|
+
/** The resource the diagnostic is pinned to. */
|
|
139
|
+
resource?: {
|
|
140
|
+
kind: string;
|
|
141
|
+
name: string;
|
|
142
|
+
};
|
|
143
|
+
/** Dotted path of the offending value within that resource. */
|
|
144
|
+
path?: string;
|
|
145
|
+
}
|
|
146
|
+
/** `workspace/executeCommand` commands an engine serves. The two upgrade
|
|
147
|
+
* commands take `[{ uri, aliases }]` and apply through `workspace/applyEdit`;
|
|
148
|
+
* refresh takes no argument. */
|
|
149
|
+
export declare const TeloCommand: {
|
|
150
|
+
readonly upgradeImport: "telo.upgradeImport";
|
|
151
|
+
readonly upgradeAllImports: "telo.upgradeAllImports";
|
|
152
|
+
readonly refreshImportUpgrades: "telo.refreshImportUpgrades";
|
|
153
|
+
};
|
|
154
|
+
export type TeloCommand = (typeof TeloCommand)[keyof typeof TeloCommand];
|
|
155
|
+
export interface UpgradeImportsArguments {
|
|
156
|
+
uri: string;
|
|
157
|
+
aliases: string[];
|
|
158
|
+
}
|
|
159
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
// The Telo editor protocol: what an editor host and a telo language-server
|
|
2
|
+
// engine say to each other beyond LSP itself. Zero dependencies and no runtime
|
|
3
|
+
// beyond constants, so a host of any telo version — and a host in any language,
|
|
4
|
+
// through `editor-protocol-schema.json` — can speak it. `README.md` is the spec.
|
|
5
|
+
/** The protocol generation this package describes. Additions within a
|
|
6
|
+
* generation are negotiated by capability; an incompatible change bumps it. */
|
|
7
|
+
export const TELO_EDITOR_PROTOCOL = 1;
|
|
8
|
+
/** The file a module directory is read as: `telo/read` of a directory answers
|
|
9
|
+
* with this file inside it. */
|
|
10
|
+
export const TELO_MODULE_FILENAME = "telo.yaml";
|
|
11
|
+
/** `initialize`'s `serverInfo.name` for every telo engine. Its
|
|
12
|
+
* `serverInfo.version` is the engine's identity: the telo version `X` for a
|
|
13
|
+
* release build, `X+unreleased` for a build made while that release is still
|
|
14
|
+
* pending. */
|
|
15
|
+
export const TELO_SERVER_NAME = "telo";
|
|
16
|
+
/** Method names of the requests an engine sends and a host serves, and of the
|
|
17
|
+
* notification an engine sends. */
|
|
18
|
+
export const TeloMethod = {
|
|
19
|
+
read: "telo/read",
|
|
20
|
+
exists: "telo/exists",
|
|
21
|
+
listDirectory: "telo/listDirectory",
|
|
22
|
+
hubSearchRefs: "telo/hub/searchRefs",
|
|
23
|
+
hubListVersions: "telo/hub/listVersions",
|
|
24
|
+
requirements: "telo/requirements",
|
|
25
|
+
};
|
|
26
|
+
// --- Commands --------------------------------------------------------------
|
|
27
|
+
/** `workspace/executeCommand` commands an engine serves. The two upgrade
|
|
28
|
+
* commands take `[{ uri, aliases }]` and apply through `workspace/applyEdit`;
|
|
29
|
+
* refresh takes no argument. */
|
|
30
|
+
export const TeloCommand = {
|
|
31
|
+
upgradeImport: "telo.upgradeImport",
|
|
32
|
+
upgradeAllImports: "telo.upgradeAllImports",
|
|
33
|
+
refreshImportUpgrades: "telo.refreshImportUpgrades",
|
|
34
|
+
};
|
|
@@ -0,0 +1,373 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"$id": "https://telo.run/schemas/editor-protocol.json",
|
|
4
|
+
"title": "Telo editor protocol, generation 1",
|
|
5
|
+
"description": "The messages an editor host and a telo language-server engine exchange beyond LSP itself. Each host-served request (engine to host) has a `<Name>Params` and a `<Name>Result` definition; `RequirementsParams` is the engine-to-host `telo/requirements` notification; `TeloDiagnosticData` is what an engine puts in `Diagnostic.data`; `ExperimentalCapabilities` is what an engine advertises under `ServerCapabilities.experimental`. `x-telo-methods` maps each method name to its definitions. Objects are open and enumerations list the values known to this generation: a receiver ignores a member it does not know and reads an unknown enumeration value as its least-capable value (README.md, Generations). See README.md for the handshake and the generation rule.",
|
|
6
|
+
"x-telo-methods": {
|
|
7
|
+
"telo/read": {
|
|
8
|
+
"params": "#/$defs/ReadParams",
|
|
9
|
+
"result": "#/$defs/ReadResult"
|
|
10
|
+
},
|
|
11
|
+
"telo/exists": {
|
|
12
|
+
"params": "#/$defs/ExistsParams",
|
|
13
|
+
"result": "#/$defs/ExistsResult"
|
|
14
|
+
},
|
|
15
|
+
"telo/listDirectory": {
|
|
16
|
+
"params": "#/$defs/ListDirectoryParams",
|
|
17
|
+
"result": "#/$defs/ListDirectoryResult"
|
|
18
|
+
},
|
|
19
|
+
"telo/hub/searchRefs": {
|
|
20
|
+
"params": "#/$defs/SearchRefsParams",
|
|
21
|
+
"result": "#/$defs/SearchRefsResult"
|
|
22
|
+
},
|
|
23
|
+
"telo/hub/listVersions": {
|
|
24
|
+
"params": "#/$defs/ListVersionsParams",
|
|
25
|
+
"result": "#/$defs/ListVersionsResult"
|
|
26
|
+
},
|
|
27
|
+
"telo/requirements": {
|
|
28
|
+
"params": "#/$defs/RequirementsParams"
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
"$defs": {
|
|
32
|
+
"ExperimentalCapabilities": {
|
|
33
|
+
"type": "object",
|
|
34
|
+
"required": [
|
|
35
|
+
"telo"
|
|
36
|
+
],
|
|
37
|
+
"properties": {
|
|
38
|
+
"telo": {
|
|
39
|
+
"type": "object",
|
|
40
|
+
"required": [
|
|
41
|
+
"protocol"
|
|
42
|
+
],
|
|
43
|
+
"properties": {
|
|
44
|
+
"protocol": {
|
|
45
|
+
"type": "integer",
|
|
46
|
+
"minimum": 1,
|
|
47
|
+
"description": "The protocol generation the engine speaks."
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
"ReadParams": {
|
|
54
|
+
"type": "object",
|
|
55
|
+
"required": [
|
|
56
|
+
"uri"
|
|
57
|
+
],
|
|
58
|
+
"properties": {
|
|
59
|
+
"uri": {
|
|
60
|
+
"type": "string",
|
|
61
|
+
"description": "A document URI (`file:///…`) or an import source (`oci://…`, `https://…`)."
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
"ReadResult": {
|
|
66
|
+
"description": "The text and the canonical location it was read from (a directory resolves to its `telo.yaml`), or null when nothing exists at the URI.",
|
|
67
|
+
"oneOf": [
|
|
68
|
+
{
|
|
69
|
+
"type": "object",
|
|
70
|
+
"required": [
|
|
71
|
+
"uri",
|
|
72
|
+
"text"
|
|
73
|
+
],
|
|
74
|
+
"properties": {
|
|
75
|
+
"uri": {
|
|
76
|
+
"type": "string"
|
|
77
|
+
},
|
|
78
|
+
"text": {
|
|
79
|
+
"type": "string"
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
"type": "null"
|
|
85
|
+
}
|
|
86
|
+
]
|
|
87
|
+
},
|
|
88
|
+
"ExistsParams": {
|
|
89
|
+
"type": "object",
|
|
90
|
+
"required": [
|
|
91
|
+
"base",
|
|
92
|
+
"relative"
|
|
93
|
+
],
|
|
94
|
+
"properties": {
|
|
95
|
+
"base": {
|
|
96
|
+
"type": "string",
|
|
97
|
+
"description": "URI of a file; `relative` resolves against its directory."
|
|
98
|
+
},
|
|
99
|
+
"relative": {
|
|
100
|
+
"type": "string"
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
"ExistsResult": {
|
|
105
|
+
"type": "boolean"
|
|
106
|
+
},
|
|
107
|
+
"ListDirectoryParams": {
|
|
108
|
+
"type": "object",
|
|
109
|
+
"required": [
|
|
110
|
+
"uri"
|
|
111
|
+
],
|
|
112
|
+
"properties": {
|
|
113
|
+
"uri": {
|
|
114
|
+
"type": "string"
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
},
|
|
118
|
+
"DirectoryEntry": {
|
|
119
|
+
"type": "object",
|
|
120
|
+
"required": [
|
|
121
|
+
"name",
|
|
122
|
+
"kind"
|
|
123
|
+
],
|
|
124
|
+
"properties": {
|
|
125
|
+
"name": {
|
|
126
|
+
"type": "string"
|
|
127
|
+
},
|
|
128
|
+
"kind": {
|
|
129
|
+
"description": "What the entry is, read without following a link: a symbolic link is `symlink` whatever it points at. A receiver reads a kind it does not know as `other`.",
|
|
130
|
+
"anyOf": [
|
|
131
|
+
{
|
|
132
|
+
"enum": [
|
|
133
|
+
"file",
|
|
134
|
+
"directory",
|
|
135
|
+
"symlink",
|
|
136
|
+
"other"
|
|
137
|
+
]
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
"type": "string"
|
|
141
|
+
}
|
|
142
|
+
]
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
},
|
|
146
|
+
"ListDirectoryResult": {
|
|
147
|
+
"description": "The entries, or null when the URI names nothing or names a file.",
|
|
148
|
+
"oneOf": [
|
|
149
|
+
{
|
|
150
|
+
"type": "array",
|
|
151
|
+
"items": {
|
|
152
|
+
"$ref": "#/$defs/DirectoryEntry"
|
|
153
|
+
}
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
"type": "null"
|
|
157
|
+
}
|
|
158
|
+
]
|
|
159
|
+
},
|
|
160
|
+
"SearchRefsParams": {
|
|
161
|
+
"type": "object",
|
|
162
|
+
"required": [
|
|
163
|
+
"query"
|
|
164
|
+
],
|
|
165
|
+
"properties": {
|
|
166
|
+
"query": {
|
|
167
|
+
"type": "string"
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
},
|
|
171
|
+
"HubRef": {
|
|
172
|
+
"type": "object",
|
|
173
|
+
"required": [
|
|
174
|
+
"ref",
|
|
175
|
+
"latestVersion"
|
|
176
|
+
],
|
|
177
|
+
"properties": {
|
|
178
|
+
"ref": {
|
|
179
|
+
"type": "string"
|
|
180
|
+
},
|
|
181
|
+
"latestVersion": {
|
|
182
|
+
"type": "string"
|
|
183
|
+
},
|
|
184
|
+
"description": {
|
|
185
|
+
"type": "string"
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
},
|
|
189
|
+
"SearchRefsResult": {
|
|
190
|
+
"type": "array",
|
|
191
|
+
"items": {
|
|
192
|
+
"$ref": "#/$defs/HubRef"
|
|
193
|
+
}
|
|
194
|
+
},
|
|
195
|
+
"ListVersionsParams": {
|
|
196
|
+
"type": "object",
|
|
197
|
+
"required": [
|
|
198
|
+
"ref"
|
|
199
|
+
],
|
|
200
|
+
"properties": {
|
|
201
|
+
"ref": {
|
|
202
|
+
"type": "string"
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
},
|
|
206
|
+
"ModuleVersion": {
|
|
207
|
+
"type": "object",
|
|
208
|
+
"required": [
|
|
209
|
+
"version"
|
|
210
|
+
],
|
|
211
|
+
"properties": {
|
|
212
|
+
"version": {
|
|
213
|
+
"type": "string"
|
|
214
|
+
},
|
|
215
|
+
"integrity": {
|
|
216
|
+
"type": "string",
|
|
217
|
+
"pattern": "^sha256-[A-Za-z0-9_-]{43}$",
|
|
218
|
+
"description": "Canonical import pin: `sha256-` and 43 base64url characters."
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
},
|
|
222
|
+
"ListVersionsResult": {
|
|
223
|
+
"description": "Newest first; empty for a ref the hub does not track.",
|
|
224
|
+
"type": "array",
|
|
225
|
+
"items": {
|
|
226
|
+
"$ref": "#/$defs/ModuleVersion"
|
|
227
|
+
}
|
|
228
|
+
},
|
|
229
|
+
"VersionBound": {
|
|
230
|
+
"type": "object",
|
|
231
|
+
"required": [
|
|
232
|
+
"version",
|
|
233
|
+
"inclusive"
|
|
234
|
+
],
|
|
235
|
+
"properties": {
|
|
236
|
+
"version": {
|
|
237
|
+
"type": "string"
|
|
238
|
+
},
|
|
239
|
+
"inclusive": {
|
|
240
|
+
"type": "boolean"
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
},
|
|
244
|
+
"VersionInterval": {
|
|
245
|
+
"description": "A `requires: telo:` range reduced to its edges; an absent edge is open.",
|
|
246
|
+
"type": "object",
|
|
247
|
+
"properties": {
|
|
248
|
+
"min": {
|
|
249
|
+
"$ref": "#/$defs/VersionBound"
|
|
250
|
+
},
|
|
251
|
+
"max": {
|
|
252
|
+
"$ref": "#/$defs/VersionBound"
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
},
|
|
256
|
+
"RequirementsRange": {
|
|
257
|
+
"type": "object",
|
|
258
|
+
"required": [
|
|
259
|
+
"module",
|
|
260
|
+
"text",
|
|
261
|
+
"interval"
|
|
262
|
+
],
|
|
263
|
+
"properties": {
|
|
264
|
+
"module": {
|
|
265
|
+
"type": "string",
|
|
266
|
+
"description": "URI of the module document declaring the range."
|
|
267
|
+
},
|
|
268
|
+
"text": {
|
|
269
|
+
"type": "string",
|
|
270
|
+
"description": "The range as written."
|
|
271
|
+
},
|
|
272
|
+
"interval": {
|
|
273
|
+
"$ref": "#/$defs/VersionInterval"
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
},
|
|
277
|
+
"RequirementsParams": {
|
|
278
|
+
"type": "object",
|
|
279
|
+
"required": [
|
|
280
|
+
"owner",
|
|
281
|
+
"documents",
|
|
282
|
+
"ranges"
|
|
283
|
+
],
|
|
284
|
+
"properties": {
|
|
285
|
+
"owner": {
|
|
286
|
+
"type": "string"
|
|
287
|
+
},
|
|
288
|
+
"documents": {
|
|
289
|
+
"type": "array",
|
|
290
|
+
"items": {
|
|
291
|
+
"type": "string"
|
|
292
|
+
}
|
|
293
|
+
},
|
|
294
|
+
"ranges": {
|
|
295
|
+
"type": "array",
|
|
296
|
+
"items": {
|
|
297
|
+
"$ref": "#/$defs/RequirementsRange"
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
},
|
|
302
|
+
"DiagnosticFix": {
|
|
303
|
+
"type": "object",
|
|
304
|
+
"required": [
|
|
305
|
+
"replacement"
|
|
306
|
+
],
|
|
307
|
+
"properties": {
|
|
308
|
+
"replacement": {
|
|
309
|
+
"type": "string"
|
|
310
|
+
},
|
|
311
|
+
"tag": {
|
|
312
|
+
"description": "The YAML tag the replacement is written behind. A receiver that does not know a tag offers no fix for it.",
|
|
313
|
+
"anyOf": [
|
|
314
|
+
{
|
|
315
|
+
"enum": [
|
|
316
|
+
"ref",
|
|
317
|
+
"cel",
|
|
318
|
+
"module-path"
|
|
319
|
+
]
|
|
320
|
+
},
|
|
321
|
+
{
|
|
322
|
+
"type": "string"
|
|
323
|
+
}
|
|
324
|
+
]
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
},
|
|
328
|
+
"TeloDiagnosticData": {
|
|
329
|
+
"type": "object",
|
|
330
|
+
"properties": {
|
|
331
|
+
"fix": {
|
|
332
|
+
"$ref": "#/$defs/DiagnosticFix"
|
|
333
|
+
},
|
|
334
|
+
"resource": {
|
|
335
|
+
"type": "object",
|
|
336
|
+
"required": [
|
|
337
|
+
"kind",
|
|
338
|
+
"name"
|
|
339
|
+
],
|
|
340
|
+
"properties": {
|
|
341
|
+
"kind": {
|
|
342
|
+
"type": "string"
|
|
343
|
+
},
|
|
344
|
+
"name": {
|
|
345
|
+
"type": "string"
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
},
|
|
349
|
+
"path": {
|
|
350
|
+
"type": "string"
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
},
|
|
354
|
+
"UpgradeImportsArguments": {
|
|
355
|
+
"type": "object",
|
|
356
|
+
"required": [
|
|
357
|
+
"uri",
|
|
358
|
+
"aliases"
|
|
359
|
+
],
|
|
360
|
+
"properties": {
|
|
361
|
+
"uri": {
|
|
362
|
+
"type": "string"
|
|
363
|
+
},
|
|
364
|
+
"aliases": {
|
|
365
|
+
"type": "array",
|
|
366
|
+
"items": {
|
|
367
|
+
"type": "string"
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@telorun/editor-protocol",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "The Telo editor protocol: the contract between an editor host and a telo language-server engine — the telo/* requests, the telo/requirements notification and the protocol generation.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"telo",
|
|
7
|
+
"lsp",
|
|
8
|
+
"language-server",
|
|
9
|
+
"editor",
|
|
10
|
+
"protocol"
|
|
11
|
+
],
|
|
12
|
+
"author": "Bartosz Pasiński <bartosz.pasinski@codenet.pl>",
|
|
13
|
+
"license": "SEE LICENSE IN LICENSE",
|
|
14
|
+
"repository": {
|
|
15
|
+
"type": "git",
|
|
16
|
+
"url": "git+https://github.com/telorun/telo.git",
|
|
17
|
+
"directory": "packages/editor-protocol"
|
|
18
|
+
},
|
|
19
|
+
"homepage": "https://github.com/telorun/telo#readme",
|
|
20
|
+
"bugs": {
|
|
21
|
+
"url": "https://github.com/telorun/telo/issues"
|
|
22
|
+
},
|
|
23
|
+
"type": "module",
|
|
24
|
+
"main": "./dist/index.js",
|
|
25
|
+
"exports": {
|
|
26
|
+
".": {
|
|
27
|
+
"source": "./src/index.ts",
|
|
28
|
+
"types": "./dist/index.d.ts",
|
|
29
|
+
"bun": "./src/index.ts",
|
|
30
|
+
"import": "./dist/index.js",
|
|
31
|
+
"default": "./dist/index.js"
|
|
32
|
+
},
|
|
33
|
+
"./editor-protocol-schema.json": "./editor-protocol-schema.json",
|
|
34
|
+
"./package.json": "./package.json"
|
|
35
|
+
},
|
|
36
|
+
"files": [
|
|
37
|
+
"dist/**",
|
|
38
|
+
"src/**",
|
|
39
|
+
"editor-protocol-schema.json"
|
|
40
|
+
],
|
|
41
|
+
"devDependencies": {
|
|
42
|
+
"ajv": "^8.17.1",
|
|
43
|
+
"typescript": "^5.0.0",
|
|
44
|
+
"vitest": "^2.1.8"
|
|
45
|
+
},
|
|
46
|
+
"scripts": {
|
|
47
|
+
"build": "tsc -p tsconfig.lib.json",
|
|
48
|
+
"test": "vitest run"
|
|
49
|
+
}
|
|
50
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
// The Telo editor protocol: what an editor host and a telo language-server
|
|
2
|
+
// engine say to each other beyond LSP itself. Zero dependencies and no runtime
|
|
3
|
+
// beyond constants, so a host of any telo version — and a host in any language,
|
|
4
|
+
// through `editor-protocol-schema.json` — can speak it. `README.md` is the spec.
|
|
5
|
+
|
|
6
|
+
/** The protocol generation this package describes. Additions within a
|
|
7
|
+
* generation are negotiated by capability; an incompatible change bumps it. */
|
|
8
|
+
export const TELO_EDITOR_PROTOCOL = 1;
|
|
9
|
+
|
|
10
|
+
/** What an engine advertises under `ServerCapabilities.experimental`. */
|
|
11
|
+
export interface TeloExperimentalCapabilities {
|
|
12
|
+
telo: TeloProtocolCapability;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface TeloProtocolCapability {
|
|
16
|
+
/** The generation the engine speaks. */
|
|
17
|
+
protocol: number;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** The file a module directory is read as: `telo/read` of a directory answers
|
|
21
|
+
* with this file inside it. */
|
|
22
|
+
export const TELO_MODULE_FILENAME = "telo.yaml";
|
|
23
|
+
|
|
24
|
+
/** `initialize`'s `serverInfo.name` for every telo engine. Its
|
|
25
|
+
* `serverInfo.version` is the engine's identity: the telo version `X` for a
|
|
26
|
+
* release build, `X+unreleased` for a build made while that release is still
|
|
27
|
+
* pending. */
|
|
28
|
+
export const TELO_SERVER_NAME = "telo";
|
|
29
|
+
|
|
30
|
+
/** Method names of the requests an engine sends and a host serves, and of the
|
|
31
|
+
* notification an engine sends. */
|
|
32
|
+
export const TeloMethod = {
|
|
33
|
+
read: "telo/read",
|
|
34
|
+
exists: "telo/exists",
|
|
35
|
+
listDirectory: "telo/listDirectory",
|
|
36
|
+
hubSearchRefs: "telo/hub/searchRefs",
|
|
37
|
+
hubListVersions: "telo/hub/listVersions",
|
|
38
|
+
requirements: "telo/requirements",
|
|
39
|
+
} as const;
|
|
40
|
+
export type TeloMethod = (typeof TeloMethod)[keyof typeof TeloMethod];
|
|
41
|
+
|
|
42
|
+
// --- Host-served requests (engine → host) ---------------------------------
|
|
43
|
+
|
|
44
|
+
/** `telo/read`: the text at `uri`. `uri` is a document URI (`file:///…`) or an
|
|
45
|
+
* import source (`oci://host/repo@1.2.0`, `https://…`); the host reads it
|
|
46
|
+
* through whatever transport it owns. */
|
|
47
|
+
export interface ReadParams {
|
|
48
|
+
uri: string;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** The text read, with the CANONICAL location it was read from — a directory
|
|
52
|
+
* resolves to its {@link TELO_MODULE_FILENAME}, an `oci://` tag to the reference the host
|
|
53
|
+
* resolved. `null` when nothing exists at `uri`; any other failure is a
|
|
54
|
+
* JSON-RPC error carrying its reason. */
|
|
55
|
+
export type ReadResult = { uri: string; text: string } | null;
|
|
56
|
+
|
|
57
|
+
/** `telo/exists`: whether a file or directory exists at `relative`, resolved
|
|
58
|
+
* against the DIRECTORY of the file `base` names. */
|
|
59
|
+
export interface ExistsParams {
|
|
60
|
+
base: string;
|
|
61
|
+
relative: string;
|
|
62
|
+
}
|
|
63
|
+
export type ExistsResult = boolean;
|
|
64
|
+
|
|
65
|
+
/** `telo/listDirectory`: the entries of the directory at `uri`. */
|
|
66
|
+
export interface ListDirectoryParams {
|
|
67
|
+
uri: string;
|
|
68
|
+
}
|
|
69
|
+
/** What a directory entry is, read WITHOUT following a link: a symbolic link
|
|
70
|
+
* is `symlink` whatever it points at. A receiver reads a kind it does not know
|
|
71
|
+
* as `other`. */
|
|
72
|
+
export type DirectoryEntryKind = "file" | "directory" | "symlink" | "other";
|
|
73
|
+
export interface DirectoryEntry {
|
|
74
|
+
name: string;
|
|
75
|
+
kind: DirectoryEntryKind;
|
|
76
|
+
}
|
|
77
|
+
/** `null` when `uri` names nothing or names a file. */
|
|
78
|
+
export type ListDirectoryResult = DirectoryEntry[] | null;
|
|
79
|
+
|
|
80
|
+
/** `telo/hub/searchRefs`: module refs the configured telo hub matches `query`
|
|
81
|
+
* against (a substring over every registered ref). */
|
|
82
|
+
export interface SearchRefsParams {
|
|
83
|
+
query: string;
|
|
84
|
+
}
|
|
85
|
+
export interface HubRef {
|
|
86
|
+
ref: string;
|
|
87
|
+
latestVersion: string;
|
|
88
|
+
description?: string;
|
|
89
|
+
}
|
|
90
|
+
export type SearchRefsResult = HubRef[];
|
|
91
|
+
|
|
92
|
+
/** `telo/hub/listVersions`: every version the hub tracks for a location ref,
|
|
93
|
+
* newest first, each with its import pin when the hub has one. `[]` for a ref
|
|
94
|
+
* the hub does not track; an unreachable hub is a JSON-RPC error. */
|
|
95
|
+
export interface ListVersionsParams {
|
|
96
|
+
ref: string;
|
|
97
|
+
}
|
|
98
|
+
export interface ModuleVersion {
|
|
99
|
+
version: string;
|
|
100
|
+
/** Canonical `sha256-<base64url>` pin. */
|
|
101
|
+
integrity?: string;
|
|
102
|
+
}
|
|
103
|
+
export type ListVersionsResult = ModuleVersion[];
|
|
104
|
+
|
|
105
|
+
/** Params and result of every host-served request, by method. */
|
|
106
|
+
export interface TeloHostRequests {
|
|
107
|
+
"telo/read": [ReadParams, ReadResult];
|
|
108
|
+
"telo/exists": [ExistsParams, ExistsResult];
|
|
109
|
+
"telo/listDirectory": [ListDirectoryParams, ListDirectoryResult];
|
|
110
|
+
"telo/hub/searchRefs": [SearchRefsParams, SearchRefsResult];
|
|
111
|
+
"telo/hub/listVersions": [ListVersionsParams, ListVersionsResult];
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// --- Engine → host notification -------------------------------------------
|
|
115
|
+
|
|
116
|
+
/** One edge of a version interval. */
|
|
117
|
+
export interface VersionBound {
|
|
118
|
+
version: string;
|
|
119
|
+
inclusive: boolean;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/** A `requires: telo:` range reduced to its edges; an absent edge is open. */
|
|
123
|
+
export interface VersionInterval {
|
|
124
|
+
min?: VersionBound;
|
|
125
|
+
max?: VersionBound;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export interface RequirementsRange {
|
|
129
|
+
/** URI of the module document declaring the range. */
|
|
130
|
+
module: string;
|
|
131
|
+
/** The range as written. */
|
|
132
|
+
text: string;
|
|
133
|
+
interval: VersionInterval;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/** `telo/requirements`: sent after each analysis of an owner module. */
|
|
137
|
+
export interface RequirementsParams {
|
|
138
|
+
/** URI of the owner module's `telo.yaml` (or standalone manifest). */
|
|
139
|
+
owner: string;
|
|
140
|
+
/** URIs of the owner's member documents — the owner and every partial it
|
|
141
|
+
* includes. A host routes these documents to the engine chosen for `owner`. */
|
|
142
|
+
documents: string[];
|
|
143
|
+
/** Every `requires: telo:` range in the owner's import closure. The owner
|
|
144
|
+
* itself declares one exactly when an entry's `module` equals `owner`. */
|
|
145
|
+
ranges: RequirementsRange[];
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// --- Diagnostic.data -------------------------------------------------------
|
|
149
|
+
|
|
150
|
+
/** The YAML tags a repair may write. A receiver that does not know a tag
|
|
151
|
+
* offers no fix for it. */
|
|
152
|
+
export type DiagnosticFixTag = "ref" | "cel" | "module-path";
|
|
153
|
+
|
|
154
|
+
/** A mechanically applicable repair: the whole corrected value at the
|
|
155
|
+
* diagnostic's range, written behind `tag` when one is present. */
|
|
156
|
+
export interface DiagnosticFix {
|
|
157
|
+
replacement: string;
|
|
158
|
+
tag?: DiagnosticFixTag;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/** What an engine puts in `Diagnostic.data`. A host hands the diagnostic back
|
|
162
|
+
* unchanged in `codeAction`'s context, which is how a fix round-trips. */
|
|
163
|
+
export interface TeloDiagnosticData {
|
|
164
|
+
fix?: DiagnosticFix;
|
|
165
|
+
/** The resource the diagnostic is pinned to. */
|
|
166
|
+
resource?: { kind: string; name: string };
|
|
167
|
+
/** Dotted path of the offending value within that resource. */
|
|
168
|
+
path?: string;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// --- Commands --------------------------------------------------------------
|
|
172
|
+
|
|
173
|
+
/** `workspace/executeCommand` commands an engine serves. The two upgrade
|
|
174
|
+
* commands take `[{ uri, aliases }]` and apply through `workspace/applyEdit`;
|
|
175
|
+
* refresh takes no argument. */
|
|
176
|
+
export const TeloCommand = {
|
|
177
|
+
upgradeImport: "telo.upgradeImport",
|
|
178
|
+
upgradeAllImports: "telo.upgradeAllImports",
|
|
179
|
+
refreshImportUpgrades: "telo.refreshImportUpgrades",
|
|
180
|
+
} as const;
|
|
181
|
+
export type TeloCommand = (typeof TeloCommand)[keyof typeof TeloCommand];
|
|
182
|
+
|
|
183
|
+
export interface UpgradeImportsArguments {
|
|
184
|
+
uri: string;
|
|
185
|
+
aliases: string[];
|
|
186
|
+
}
|