@telorun/ide-support 0.23.0 → 0.102.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.
Files changed (42) hide show
  1. package/dist/completions/build.d.ts.map +1 -1
  2. package/dist/completions/build.js +7 -0
  3. package/dist/completions/detect-context.d.ts +33 -0
  4. package/dist/completions/detect-context.d.ts.map +1 -1
  5. package/dist/completions/detect-context.js +52 -0
  6. package/dist/completions/module-file-completions.d.ts +18 -0
  7. package/dist/completions/module-file-completions.d.ts.map +1 -0
  8. package/dist/completions/module-file-completions.js +59 -0
  9. package/dist/completions/resolve-node.d.ts +23 -5
  10. package/dist/completions/resolve-node.d.ts.map +1 -1
  11. package/dist/completions/resolve-node.js +54 -14
  12. package/dist/completions/value-tag-completions.d.ts +23 -0
  13. package/dist/completions/value-tag-completions.d.ts.map +1 -0
  14. package/dist/completions/value-tag-completions.js +39 -0
  15. package/dist/import-upgrades/build-import-upgrades.d.ts +3 -3
  16. package/dist/import-upgrades/build-import-upgrades.d.ts.map +1 -1
  17. package/dist/import-upgrades/index.d.ts +0 -1
  18. package/dist/import-upgrades/index.d.ts.map +1 -1
  19. package/dist/import-upgrades/index.js +0 -1
  20. package/dist/index.d.ts +1 -0
  21. package/dist/index.d.ts.map +1 -1
  22. package/dist/index.js +2 -0
  23. package/dist/types.d.ts +13 -1
  24. package/dist/types.d.ts.map +1 -1
  25. package/dist/value-tags/offered-value-tags.d.ts +56 -0
  26. package/dist/value-tags/offered-value-tags.d.ts.map +1 -0
  27. package/dist/value-tags/offered-value-tags.js +92 -0
  28. package/package.json +3 -2
  29. package/src/completions/build.ts +6 -0
  30. package/src/completions/detect-context.ts +84 -0
  31. package/src/completions/module-file-completions.ts +64 -0
  32. package/src/completions/resolve-node.ts +87 -19
  33. package/src/completions/value-tag-completions.ts +55 -0
  34. package/src/import-upgrades/build-import-upgrades.ts +3 -3
  35. package/src/import-upgrades/index.ts +0 -1
  36. package/src/index.ts +2 -0
  37. package/src/types.ts +22 -1
  38. package/src/value-tags/offered-value-tags.ts +127 -0
  39. package/dist/import-upgrades/parse-module-versions.d.ts +0 -19
  40. package/dist/import-upgrades/parse-module-versions.d.ts.map +0 -1
  41. package/dist/import-upgrades/parse-module-versions.js +0 -29
  42. package/src/import-upgrades/parse-module-versions.ts +0 -30
@@ -42,9 +42,9 @@ export interface ModuleVersion {
42
42
  *
43
43
  * Deliberately NOT `adapter.listVersionsForRef`, which answers `string[]`:
44
44
  * completion offers names, an upgrade writes a pin, so the two want different
45
- * things from one route. A host backed by the hub fetches
46
- * `GET /module/versions` and passes the body through
47
- * {@link parseModuleVersions}. */
45
+ * things from one route. A host backed by the hub reads
46
+ * `GET /module/versions`, carrying an `integrity` only when it is a canonical
47
+ * pin. */
48
48
  export type ModuleVersionLookup = (baseRef: string) => Promise<ModuleVersion[]>;
49
49
 
50
50
  /** Everything an upgrade needs from its host: what versions exist, and whether
@@ -14,7 +14,6 @@ export type {
14
14
  ModuleVersionLookup,
15
15
  } from "./build-import-upgrades.js";
16
16
  export { moduleManifestCacheUrl } from "./manifest-cache-url.js";
17
- export { parseModuleVersions } from "./parse-module-versions.js";
18
17
  export { findImportEntries } from "./find-import-entries.js";
19
18
  export type {
20
19
  ImportEntry,
package/src/index.ts CHANGED
@@ -8,6 +8,8 @@ export * from "./rename/index.js";
8
8
  export * from "./signature-help/index.js";
9
9
  export * from "./import-upgrades/index.js";
10
10
  export * from "./workspace/index.js";
11
+ // Which YAML tags a field takes — shared so every host offers the same set.
12
+ export { offeredValueTags, valueTag, type ValueTag } from "./value-tags/offered-value-tags.js";
11
13
  // The repo's single CEL-tree walk. Exported because every host that has to
12
14
  // answer "where is this name read" needs it and a second copy would be a second
13
15
  // answer — the editor asks it before deleting a resource.
package/src/types.ts CHANGED
@@ -20,7 +20,15 @@ import type {
20
20
  Range,
21
21
  } from "@telorun/analyzer";
22
22
 
23
- export type CompletionKind = "class" | "enumMember" | "property" | "folder" | "module" | "value";
23
+ export type CompletionKind =
24
+ | "class"
25
+ | "enumMember"
26
+ | "property"
27
+ | "folder"
28
+ | "file"
29
+ | "module"
30
+ | "value"
31
+ | "keyword";
24
32
 
25
33
  /** A source span the host replaces wholesale when a completion is accepted. */
26
34
  export interface ReplaceRange {
@@ -45,6 +53,9 @@ export interface CompletionResult {
45
53
  * containing non-word characters (`/`, `@`, `.`). A zero-width range is a
46
54
  * pure insert. */
47
55
  replaceRange?: ReplaceRange;
56
+ /** The host reopens completion once this item is accepted — what comes next
57
+ * has completions of its own (a tag's value, a directory's entries). */
58
+ retrigger?: boolean;
48
59
  }
49
60
 
50
61
  /** Rendered hover for the symbol under the cursor. `contents` is GitHub-flavored
@@ -172,6 +183,16 @@ export interface IdeEnvironmentAdapter {
172
183
  * (`GET /module/versions?ref=`). The browser cannot call OCI `tags/list`;
173
184
  * the hub holds them from ingest. */
174
185
  listVersionsForRef(ref: string): Promise<string[]>;
186
+ /** Entries of `relPath`, resolved against the ROOT of the module the manifest
187
+ * belongs to — the directory every `!include-*` / `!module-path` path is
188
+ * measured from, which for a partial is not the partial's own directory.
189
+ * Returns [] if the path doesn't exist or isn't a directory. */
190
+ listModuleEntries(relPath: string): Promise<ModuleEntry[]>;
191
+ }
192
+
193
+ export interface ModuleEntry {
194
+ name: string;
195
+ directory: boolean;
175
196
  }
176
197
 
177
198
  export interface NormalizedDiagnostic {
@@ -0,0 +1,127 @@
1
+ import { checkSchemaCompatibility } from "@telorun/analyzer";
2
+ import { builtinEngines, producedTypeOf } from "@telorun/templating";
3
+
4
+ /**
5
+ * A YAML tag an author may write on a value, and what it means to write one.
6
+ *
7
+ * The split with `@telorun/templating` is deliberate. An engine declares what a
8
+ * tag PRODUCES (`producedType()`) and where its CEL is (`expressionRegions`);
9
+ * this declares how the tag is PRESENTED to an author, which is editor knowledge
10
+ * no engine should carry. Applicability is derived from the engine's own
11
+ * declaration rather than from a list of names here — so the table says how to
12
+ * describe a tag, never which tags fit where.
13
+ *
14
+ * Shared by every editor host (VS Code completion, studio's source view and
15
+ * schema form), so the hosts cannot disagree about which tags a field takes.
16
+ */
17
+ export interface ValueTag {
18
+ /** Engine name, which is the YAML tag without its `!`. */
19
+ id: string;
20
+ /** How the tag is written. */
21
+ label: string;
22
+ /** One line on what the tag does. */
23
+ hint: string;
24
+ /** Only meaningful where the slot is EVALUATED — the tag decides what
25
+ * evaluation does with the value (`!cel` supplies the expression, `!literal`
26
+ * opts out of interpolation), so outside such a field it says nothing the
27
+ * plain value does not. An embed is the other case: it supplies a value, and
28
+ * evaluation was never involved. */
29
+ requiresEvalSlot?: boolean;
30
+ /** Set when the scalar under the tag is a module-root-relative location of
31
+ * something that ships with the module, saying what it may name. */
32
+ names?: "file" | "file-or-directory";
33
+ }
34
+
35
+ /**
36
+ * The tags an author may write, by engine name.
37
+ *
38
+ * `!ref` is absent on purpose: it names a RESOURCE rather than producing a
39
+ * value, so it belongs to a reference slot, never to a value one. `!sql` is
40
+ * absent until a host can edit it as SQL — a plain text box would be the wrong
41
+ * widget, and the hosts offer one set. An engine with no entry is simply not
42
+ * offered, which is the safe direction.
43
+ */
44
+ const AUTHORABLE: Record<string, Omit<ValueTag, "id">> = {
45
+ cel: {
46
+ label: "!cel",
47
+ hint: "A CEL expression, evaluated against this field's scope.",
48
+ requiresEvalSlot: true,
49
+ },
50
+ interpolate: {
51
+ label: "!interpolate",
52
+ hint: "Text with `${{ }}` holes, each a CEL expression; always a string.",
53
+ requiresEvalSlot: true,
54
+ },
55
+ literal: {
56
+ label: "!literal",
57
+ hint: "Opaque text. `${{ }}` inside it is not interpolated.",
58
+ requiresEvalSlot: true,
59
+ },
60
+ "include-text": {
61
+ label: "!include-text",
62
+ hint: "Contents of a file shipped with this module, as text.",
63
+ names: "file",
64
+ },
65
+ "include-bytes": {
66
+ label: "!include-bytes",
67
+ hint: "Contents of a file shipped with this module, as raw bytes.",
68
+ names: "file",
69
+ },
70
+ "module-path": {
71
+ label: "!module-path",
72
+ hint: "Location of a file or directory shipped with this module.",
73
+ names: "file-or-directory",
74
+ },
75
+ };
76
+
77
+ /** The authorable tag an engine name denotes, or undefined for a tag no host
78
+ * offers (`!ref`, `!sql`, an unknown one). */
79
+ export function valueTag(id: string): ValueTag | undefined {
80
+ const entry = AUTHORABLE[id];
81
+ return entry ? { id, ...entry } : undefined;
82
+ }
83
+
84
+ /**
85
+ * The tags offerable at one field.
86
+ *
87
+ * Two rules, both read off the engine rather than off its name:
88
+ *
89
+ * - CAN its value satisfy the slot? A tag that declares a produced type is
90
+ * offered only where that type fits. This is what puts `!include-bytes` on a
91
+ * `Telo.Bytes` slot and keeps it off a string one — and what keeps
92
+ * `!literal`, which is always text, off a boolean predicate. Checked with
93
+ * the analyzer's own comparator so the editor and `telo check` agree about
94
+ * what fits. A tag declaring no produced type (`!cel`) produces whatever the
95
+ * slot says and constrains nothing here.
96
+ * - Is it MEANINGFUL here? A tag that decides what evaluation does with the
97
+ * value needs a slot that is evaluated at all: outside one, `!cel` is a
98
+ * value the runtime never evaluates (`CEL_IN_NON_EVAL_FIELD`), and
99
+ * `!literal` suppresses an interpolation that was never going to happen.
100
+ *
101
+ * `prop` undefined is a field with no declared schema, which constrains
102
+ * nothing. `evalMode` undefined means no rule decides whether the field is
103
+ * evaluated, so the second question is not asked.
104
+ */
105
+ export function offeredValueTags(
106
+ prop: Record<string, unknown> | undefined,
107
+ evalMode: "compile" | "runtime" | null | undefined,
108
+ ): ValueTag[] {
109
+ const out: ValueTag[] = [];
110
+ for (const engine of builtinEngines) {
111
+ const tag = valueTag(engine.name);
112
+ if (!tag) continue;
113
+ const produced = producedTypeOf(engine.name);
114
+ const fitsSlot = produced && prop ? producedFits(produced, prop) : true;
115
+ const meaningful = tag.requiresEvalSlot && evalMode !== undefined ? evalMode !== null : true;
116
+ if (fitsSlot && meaningful) out.push(tag);
117
+ }
118
+ return out;
119
+ }
120
+
121
+ /** Whether a tag's produced type satisfies the slot's declared one. An
122
+ * undeclared slot accepts anything — it constrains nothing, so nothing about
123
+ * the value can contradict it. A union declares through its branches. */
124
+ function producedFits(produced: Record<string, unknown>, prop: Record<string, unknown>): boolean {
125
+ if (!prop.type && !prop["x-telo-type"] && !prop.anyOf && !prop.oneOf) return true;
126
+ return checkSchemaCompatibility(produced, prop).compatible;
127
+ }
@@ -1,19 +0,0 @@
1
- import type { ModuleVersion } from "./build-import-upgrades.js";
2
- /**
3
- * Read the hub's `GET /module/versions` payload into {@link ModuleVersion}s,
4
- * newest first (the route's own ordering, preserved).
5
- *
6
- * Pure — the host owns the `fetch`, as everything else in this package does.
7
- * It lives here rather than in each host because the shape is this package's:
8
- * three hosts parsing the same route by hand is how one of them was left
9
- * filtering for strings after the route started returning objects, which
10
- * TypeScript could not catch behind the response cast and which surfaced only
11
- * as a silently empty version list.
12
- *
13
- * Tolerant by design: an entry with no usable `version` is dropped rather than
14
- * throwing, and an `integrity` that is not a canonical `sha256-<base64url>` is
15
- * discarded rather than carried — a pin is spliced into the author's YAML, so a
16
- * malformed one has to become "no pin", never corrupt text.
17
- */
18
- export declare function parseModuleVersions(payload: unknown): ModuleVersion[];
19
- //# sourceMappingURL=parse-module-versions.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"parse-module-versions.d.ts","sourceRoot":"","sources":["../../src/import-upgrades/parse-module-versions.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAEhE;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,GAAG,aAAa,EAAE,CAUrE"}
@@ -1,29 +0,0 @@
1
- import { isCanonicalIntegrity } from "@telorun/analyzer";
2
- /**
3
- * Read the hub's `GET /module/versions` payload into {@link ModuleVersion}s,
4
- * newest first (the route's own ordering, preserved).
5
- *
6
- * Pure — the host owns the `fetch`, as everything else in this package does.
7
- * It lives here rather than in each host because the shape is this package's:
8
- * three hosts parsing the same route by hand is how one of them was left
9
- * filtering for strings after the route started returning objects, which
10
- * TypeScript could not catch behind the response cast and which surfaced only
11
- * as a silently empty version list.
12
- *
13
- * Tolerant by design: an entry with no usable `version` is dropped rather than
14
- * throwing, and an `integrity` that is not a canonical `sha256-<base64url>` is
15
- * discarded rather than carried — a pin is spliced into the author's YAML, so a
16
- * malformed one has to become "no pin", never corrupt text.
17
- */
18
- export function parseModuleVersions(payload) {
19
- const versions = payload?.versions;
20
- if (!Array.isArray(versions))
21
- return [];
22
- return versions.flatMap((entry) => {
23
- const version = entry?.version;
24
- if (typeof version !== "string" || version === "")
25
- return [];
26
- const integrity = entry.integrity;
27
- return [isCanonicalIntegrity(integrity) ? { version, integrity } : { version }];
28
- });
29
- }
@@ -1,30 +0,0 @@
1
- import { isCanonicalIntegrity } from "@telorun/analyzer";
2
- import type { ModuleVersion } from "./build-import-upgrades.js";
3
-
4
- /**
5
- * Read the hub's `GET /module/versions` payload into {@link ModuleVersion}s,
6
- * newest first (the route's own ordering, preserved).
7
- *
8
- * Pure — the host owns the `fetch`, as everything else in this package does.
9
- * It lives here rather than in each host because the shape is this package's:
10
- * three hosts parsing the same route by hand is how one of them was left
11
- * filtering for strings after the route started returning objects, which
12
- * TypeScript could not catch behind the response cast and which surfaced only
13
- * as a silently empty version list.
14
- *
15
- * Tolerant by design: an entry with no usable `version` is dropped rather than
16
- * throwing, and an `integrity` that is not a canonical `sha256-<base64url>` is
17
- * discarded rather than carried — a pin is spliced into the author's YAML, so a
18
- * malformed one has to become "no pin", never corrupt text.
19
- */
20
- export function parseModuleVersions(payload: unknown): ModuleVersion[] {
21
- const versions = (payload as { versions?: unknown } | null | undefined)?.versions;
22
- if (!Array.isArray(versions)) return [];
23
-
24
- return versions.flatMap((entry) => {
25
- const version = (entry as { version?: unknown } | null)?.version;
26
- if (typeof version !== "string" || version === "") return [];
27
- const integrity = (entry as { integrity?: unknown }).integrity;
28
- return [isCanonicalIntegrity(integrity) ? { version, integrity } : { version }];
29
- });
30
- }