@telorun/ide-support 0.14.1 → 0.16.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 (85) hide show
  1. package/dist/cel/cursor-chain.d.ts +30 -0
  2. package/dist/cel/cursor-chain.d.ts.map +1 -0
  3. package/dist/cel/cursor-chain.js +30 -0
  4. package/dist/cel/symbols.d.ts +82 -0
  5. package/dist/cel/symbols.d.ts.map +1 -0
  6. package/dist/cel/symbols.js +147 -0
  7. package/dist/cel/tokens.d.ts +32 -0
  8. package/dist/cel/tokens.d.ts.map +1 -0
  9. package/dist/cel/tokens.js +162 -0
  10. package/dist/completions/build.d.ts +6 -2
  11. package/dist/completions/build.d.ts.map +1 -1
  12. package/dist/completions/build.js +52 -24
  13. package/dist/completions/call-inputs.d.ts +25 -0
  14. package/dist/completions/call-inputs.d.ts.map +1 -0
  15. package/dist/completions/call-inputs.js +78 -0
  16. package/dist/completions/cel-completions.d.ts +26 -0
  17. package/dist/completions/cel-completions.d.ts.map +1 -0
  18. package/dist/completions/cel-completions.js +78 -0
  19. package/dist/completions/detect-context.d.ts +47 -8
  20. package/dist/completions/detect-context.d.ts.map +1 -1
  21. package/dist/completions/detect-context.js +51 -15
  22. package/dist/completions/prop-keys.d.ts +5 -1
  23. package/dist/completions/prop-keys.d.ts.map +1 -1
  24. package/dist/completions/prop-keys.js +51 -3
  25. package/dist/completions/resolve-node.d.ts +9 -2
  26. package/dist/completions/resolve-node.d.ts.map +1 -1
  27. package/dist/completions/resolve-node.js +63 -21
  28. package/dist/definition/build-definition.d.ts +6 -2
  29. package/dist/definition/build-definition.d.ts.map +1 -1
  30. package/dist/definition/build-definition.js +16 -3
  31. package/dist/definition/locate-context-binding.d.ts +15 -0
  32. package/dist/definition/locate-context-binding.d.ts.map +1 -0
  33. package/dist/definition/locate-context-binding.js +35 -0
  34. package/dist/definition/locate-step.d.ts +13 -0
  35. package/dist/definition/locate-step.d.ts.map +1 -0
  36. package/dist/definition/locate-step.js +33 -0
  37. package/dist/definition/resolve-cel-target.d.ts +11 -1
  38. package/dist/definition/resolve-cel-target.d.ts.map +1 -1
  39. package/dist/definition/resolve-cel-target.js +14 -14
  40. package/dist/doc-identity.d.ts +17 -0
  41. package/dist/doc-identity.d.ts.map +1 -0
  42. package/dist/doc-identity.js +19 -0
  43. package/dist/hover/build-hover.d.ts +6 -2
  44. package/dist/hover/build-hover.d.ts.map +1 -1
  45. package/dist/hover/build-hover.js +64 -3
  46. package/dist/import-upgrades/build-import-upgrades.d.ts +57 -3
  47. package/dist/import-upgrades/build-import-upgrades.d.ts.map +1 -1
  48. package/dist/import-upgrades/build-import-upgrades.js +67 -10
  49. package/dist/import-upgrades/index.d.ts +5 -2
  50. package/dist/import-upgrades/index.d.ts.map +1 -1
  51. package/dist/import-upgrades/index.js +3 -1
  52. package/dist/import-upgrades/manifest-cache-url.d.ts +17 -0
  53. package/dist/import-upgrades/manifest-cache-url.d.ts.map +1 -0
  54. package/dist/import-upgrades/manifest-cache-url.js +30 -0
  55. package/dist/import-upgrades/version-compatibility.d.ts +92 -0
  56. package/dist/import-upgrades/version-compatibility.d.ts.map +1 -0
  57. package/dist/import-upgrades/version-compatibility.js +121 -0
  58. package/dist/semantic-tokens/build-semantic-tokens.d.ts +13 -8
  59. package/dist/semantic-tokens/build-semantic-tokens.d.ts.map +1 -1
  60. package/dist/semantic-tokens/build-semantic-tokens.js +81 -37
  61. package/dist/types.d.ts +25 -5
  62. package/dist/types.d.ts.map +1 -1
  63. package/dist/types.js +16 -2
  64. package/package.json +2 -2
  65. package/src/cel/cursor-chain.ts +58 -0
  66. package/src/cel/symbols.ts +189 -0
  67. package/src/cel/tokens.ts +169 -0
  68. package/src/completions/build.ts +85 -22
  69. package/src/completions/call-inputs.ts +92 -0
  70. package/src/completions/cel-completions.ts +108 -0
  71. package/src/completions/detect-context.ts +107 -13
  72. package/src/completions/prop-keys.ts +59 -2
  73. package/src/completions/resolve-node.ts +82 -17
  74. package/src/definition/build-definition.ts +30 -2
  75. package/src/definition/locate-context-binding.ts +53 -0
  76. package/src/definition/locate-step.ts +50 -0
  77. package/src/definition/resolve-cel-target.ts +25 -0
  78. package/src/doc-identity.ts +31 -0
  79. package/src/hover/build-hover.ts +67 -1
  80. package/src/import-upgrades/build-import-upgrades.ts +115 -11
  81. package/src/import-upgrades/index.ts +22 -1
  82. package/src/import-upgrades/manifest-cache-url.ts +40 -0
  83. package/src/import-upgrades/version-compatibility.ts +196 -0
  84. package/src/semantic-tokens/build-semantic-tokens.ts +84 -30
  85. package/src/types.ts +47 -6
@@ -0,0 +1 @@
1
+ {"version":3,"file":"call-inputs.d.ts","sourceRoot":"","sources":["../../src/completions/call-inputs.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,EAGL,KAAK,gBAAgB,EACrB,KAAK,gBAAgB,EAEtB,MAAM,mBAAmB,CAAC;AAe3B;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAC1B,QAAQ,EAAE,gBAAgB,GAAG,SAAS,EACtC,QAAQ,EAAE,gBAAgB,GAAG,SAAS,EACtC,OAAO,EAAE,MAAM,EACf,YAAY,EAAE,MAAM,GAAG,SAAS,EAChC,YAAY,EAAE,MAAM,GACnB,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,SAAS,CA0CjC"}
@@ -0,0 +1,78 @@
1
+ /**
2
+ * The arguments a call site declares.
3
+ *
4
+ * A slot that transfers control names its argument slot on its own `x-telo-ref`
5
+ * — `inputs: /inputs`, a JSON Pointer relative to the object enclosing the slot.
6
+ * That is the only thing tying an `inputs:` map to the resource it is arguments
7
+ * FOR: the map itself is an open object, and the reference sits in a sibling
8
+ * field whose name no walker may assume.
9
+ *
10
+ * Reading the pointer here means completion offers exactly the keys the invoked
11
+ * target declares — resolved through the shared contract resolver, so they are
12
+ * the keys `telo check` validates that call against and the kernel binds at
13
+ * dispatch, instance declaration first.
14
+ */
15
+ import { navigateConcretePath, readRefSlot, } from "@telorun/analyzer";
16
+ import { navigateSchema } from "./detect-context.js";
17
+ /** Resolve a JSON Pointer that is a plain property path (`/inputs`) into path
18
+ * segments. Pointers here address a sibling FIELD, never an array element, so
19
+ * anything else is left alone rather than guessed at. */
20
+ function pointerSegments(pointer) {
21
+ if (!pointer.startsWith("/"))
22
+ return undefined;
23
+ const segments = pointer
24
+ .slice(1)
25
+ .split("/")
26
+ .map((s) => s.replace(/~1/g, "/").replace(/~0/g, "~"));
27
+ return segments.every((s) => s.length > 0 && !/^\d+$/.test(s)) ? segments : undefined;
28
+ }
29
+ /**
30
+ * The declared input contract of the call whose argument slot is at
31
+ * `concretePath`, or undefined when this path is not one.
32
+ *
33
+ * Both halves have to line up: the enclosing object's schema must declare a ref
34
+ * slot whose `inputs` pointer names this field, and the manifest must fill that
35
+ * ref. Either missing means there is no call here to take arguments for.
36
+ */
37
+ export function callInputsAt(registry, analysis, docKind, resourceName, concretePath) {
38
+ if (!registry || !analysis || !concretePath)
39
+ return undefined;
40
+ const resource = analysis.resourceFor(docKind, resourceName);
41
+ if (!resource)
42
+ return undefined;
43
+ const definition = registry.resolveDefinition(docKind);
44
+ if (!definition?.schema)
45
+ return undefined;
46
+ const segments = concretePath.split(".").map((seg) => seg.replace(/\[\d+\]$/, ""));
47
+ // The pointer is relative to the object ENCLOSING the annotated slot, and it
48
+ // may name a nested field (`/handler/inputs`), so every prefix of this path is
49
+ // a candidate enclosing object — not just the immediate parent. Trying them
50
+ // longest-first keeps the nearest enclosing declaration winning.
51
+ const concreteSegments = concretePath.split(".");
52
+ for (let depth = segments.length - 1; depth >= 0; depth--) {
53
+ const enclosing = segments.slice(0, depth);
54
+ const tail = segments.slice(depth);
55
+ const enclosingSchema = navigateSchema(definition.schema,
56
+ // Schema navigation is index-free; the concrete path is not.
57
+ enclosing.filter(Boolean), (from) => registry.resolveSchemaFrom(from, docKind));
58
+ const properties = enclosingSchema?.properties;
59
+ if (!properties)
60
+ continue;
61
+ for (const [siblingName, siblingSchema] of Object.entries(properties)) {
62
+ const slot = readRefSlot(siblingSchema);
63
+ if (!slot?.inputs)
64
+ continue;
65
+ const pointed = pointerSegments(slot.inputs);
66
+ if (!pointed || pointed.length !== tail.length)
67
+ continue;
68
+ if (!pointed.every((seg, i) => seg === tail[i]))
69
+ continue;
70
+ const refPath = [...concreteSegments.slice(0, depth), siblingName].filter(Boolean).join(".");
71
+ const ref = navigateConcretePath(resource, refPath);
72
+ if (!ref || typeof ref !== "object" || !ref.name)
73
+ continue;
74
+ return analysis.contractFor(ref, "inputType");
75
+ }
76
+ }
77
+ return undefined;
78
+ }
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Completion inside a CEL body.
3
+ *
4
+ * Every candidate comes from the scope the analyzer resolved for this exact
5
+ * site, so the list is a claim that what it offers will type-check — not a
6
+ * separate model of what CEL sees. Where the scope declares nothing (an open
7
+ * node, a permissive contract, a live value) nothing is offered, which is the
8
+ * honest answer rather than a guess.
9
+ */
10
+ import type { CelScopeQuery, CelSegment } from "@telorun/analyzer";
11
+ import type { CompletionResult } from "../types.js";
12
+ /** The resource a cursor's document addresses. */
13
+ export interface CelCompletionTarget {
14
+ kind?: string;
15
+ name?: string;
16
+ }
17
+ /**
18
+ * Candidates for the cursor inside `segment`.
19
+ *
20
+ * A member position (`req.|`) offers only what the prefix declares — no
21
+ * functions, since a receiver-style call is rare next to a field access and
22
+ * mixing them buries the fields. A root position offers the scope's names
23
+ * first, then the global functions the environment declares.
24
+ */
25
+ export declare function celCompletions(text: string, segment: CelSegment, offset: number, concretePath: string, target: CelCompletionTarget, query: CelScopeQuery | undefined): CompletionResult[];
26
+ //# sourceMappingURL=cel-completions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cel-completions.d.ts","sourceRoot":"","sources":["../../src/completions/cel-completions.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,KAAK,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACnE,OAAO,KAAK,EAAE,gBAAgB,EAAgB,MAAM,aAAa,CAAC;AAIlE,kDAAkD;AAClD,MAAM,WAAW,mBAAmB;IAClC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAqBD;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAC5B,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,UAAU,EACnB,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,MAAM,EACpB,MAAM,EAAE,mBAAmB,EAC3B,KAAK,EAAE,aAAa,GAAG,SAAS,GAC/B,gBAAgB,EAAE,CAqDpB"}
@@ -0,0 +1,78 @@
1
+ import { celCursorChain } from "../cel/cursor-chain.js";
2
+ import { celFunctions, celMemberSymbols, celRootSymbols } from "../cel/symbols.js";
3
+ /** Markdown listing every overload, with the function's own description above
4
+ * it. A single-signature function needs no list — its one signature is already
5
+ * on the detail line. */
6
+ function describeOverloads(signatures, description) {
7
+ if (signatures.length <= 1)
8
+ return description;
9
+ const list = signatures.map((s) => `- \`${s}\``).join("\n");
10
+ return description ? `${description}\n\n${list}` : list;
11
+ }
12
+ function toResult(symbol, replaceRange) {
13
+ return {
14
+ label: symbol.name,
15
+ kind: "property",
16
+ detail: symbol.type,
17
+ documentation: symbol.description,
18
+ replaceRange,
19
+ };
20
+ }
21
+ /**
22
+ * Candidates for the cursor inside `segment`.
23
+ *
24
+ * A member position (`req.|`) offers only what the prefix declares — no
25
+ * functions, since a receiver-style call is rare next to a field access and
26
+ * mixing them buries the fields. A root position offers the scope's names
27
+ * first, then the global functions the environment declares.
28
+ */
29
+ export function celCompletions(text, segment, offset, concretePath, target, query) {
30
+ if (!query)
31
+ return [];
32
+ const resource = query.resourceFor(target.kind, target.name);
33
+ if (!resource)
34
+ return [];
35
+ const scope = query.scopeAt(resource, concretePath);
36
+ const chain = celCursorChain(text, segment, offset);
37
+ const prefix = chain?.prefix ?? [];
38
+ if (chain?.member) {
39
+ return celMemberSymbols(scope, prefix).map((s) => toResult(s));
40
+ }
41
+ const roots = celRootSymbols(scope);
42
+ const results = roots.map((s) => toResult(s));
43
+ // A CEL type name (`double`, `int`, `string`) is registered as a variable of
44
+ // type `type` AND as the conversion function of the same name. Both are real,
45
+ // but two identical labels are two things an author cannot choose between, so
46
+ // the callable form wins the slot and says it also names the type — that is
47
+ // the form written at a root position.
48
+ const typeNames = new Set(roots.filter((s) => s.type === "type").map((s) => s.name));
49
+ // Global functions only: a receiver-style one (`string.startsWith`) is
50
+ // reachable through a member position, where the receiver's type is known.
51
+ for (const fn of celFunctions(scope)) {
52
+ if (fn.receiverType)
53
+ continue;
54
+ const alsoAType = typeNames.has(fn.name);
55
+ if (alsoAType) {
56
+ const at = results.findIndex((r) => r.label === fn.name);
57
+ if (at >= 0)
58
+ results.splice(at, 1);
59
+ }
60
+ results.push({
61
+ label: fn.name,
62
+ kind: "value",
63
+ // One candidate per function, so the extra overloads are reported IN it
64
+ // rather than as repeated labels: the count on the detail line, the
65
+ // signatures themselves in the documentation.
66
+ detail: fn.signatures.length > 1
67
+ ? `${fn.signatures[0]} (+${fn.signatures.length - 1} overloads)`
68
+ : fn.signatures[0],
69
+ documentation: describeOverloads(fn.signatures, alsoAType
70
+ ? [fn.description, `Also names the CEL type \`${fn.name}\`.`].filter(Boolean).join("\n\n")
71
+ : fn.description),
72
+ // Sorted after the scope's own names: a variable is what an author is
73
+ // reaching for at a root position far more often than a built-in.
74
+ sortText: `z${fn.name}`,
75
+ });
76
+ }
77
+ return results;
78
+ }
@@ -1,4 +1,4 @@
1
- import { type AstDocument } from "@telorun/analyzer";
1
+ import { type AstDocument, type CelSegment } from "@telorun/analyzer";
2
2
  import type { ReplaceRange } from "../types.js";
3
3
  export type { ReplaceRange };
4
4
  export type CompletionCtx = {
@@ -19,6 +19,11 @@ export type CompletionCtx = {
19
19
  type: "prop-key";
20
20
  docKind: string;
21
21
  yamlPath: string[];
22
+ /** The same location with sequence indices kept, so a caller can find the
23
+ * manifest node this slot sits in — what resolving an enclosing call's
24
+ * reference needs. */
25
+ concretePath: string;
26
+ docIndex: number;
22
27
  existingKeys: Set<string>;
23
28
  } | {
24
29
  /** Cursor sits on the value of an object-form ref's `name:` field
@@ -42,14 +47,48 @@ export type CompletionCtx = {
42
47
  prefix: string;
43
48
  /** Full source range of the value being completed. */
44
49
  replaceRange: ReplaceRange;
50
+ } | {
51
+ /** Cursor sits on an ordinary field VALUE whose schema declares the values
52
+ * it may take — `enum` (closed) or `examples` (open). Untargeted on
53
+ * purpose: every value slot resolves here and the ones declaring neither
54
+ * simply offer nothing. */
55
+ type: "value-suggestions";
56
+ docKind: string;
57
+ /** Path from the document root to the field, so its schema can be found. */
58
+ yamlPath: string[];
59
+ replaceRange: ReplaceRange;
60
+ } | {
61
+ /** Cursor sits inside a CEL body — closed or still open (`!cel "req.|`).
62
+ * What completes is decided by the scope the analyzer resolves for this
63
+ * site, so the host must supply a `CelScopeQuery`; without one the
64
+ * candidate list would be a guess rather than a claim about what
65
+ * `telo check` accepts, and nothing is offered. */
66
+ type: "cel";
67
+ docKind?: string;
68
+ /** Which `---` document the cursor is in, so the host can name the
69
+ * resource this expression belongs to. */
70
+ docIndex: number;
71
+ /** The site's address with sequence indices kept — what the scope is
72
+ * resolved at. */
73
+ concretePath: string;
74
+ segment: CelSegment;
75
+ /** Cursor as a document offset. */
76
+ offset: number;
45
77
  };
78
+ /**
79
+ * Resolves an `x-telo-schema-from` annotation to the schema it derives. Supplied
80
+ * by the caller because the anchor is alias-qualified and only the registry can
81
+ * resolve it in the declaring kind's module scope.
82
+ */
83
+ export type SchemaFromResolver = (schemaFrom: string) => Record<string, any> | undefined;
46
84
  /** Navigate a JSON Schema hierarchy following `path`, auto-descending into
47
- * array items, peeling `anyOf` / `oneOf` branches and following document-local
48
- * `$ref`s. When multiple peeled branches define `properties`, returns a
49
- * synthetic node whose `properties` is the union (first-wins on key collision)
50
- * and whose `required` is the intersection — enough for propKeyCompletions to
51
- * surface every key a value at this slot can legally carry. */
52
- export declare function navigateSchema(schema: Record<string, any>, path: string[]): Record<string, any> | undefined;
85
+ * array items, peeling `anyOf` / `oneOf` branches, following document-local
86
+ * `$ref`s and expanding `x-telo-schema-from` slots. When multiple peeled
87
+ * branches define `properties`, returns a synthetic node whose `properties` is
88
+ * the union (first-wins on key collision) and whose `required` is the
89
+ * intersection — enough for propKeyCompletions to surface every key a value at
90
+ * this slot can legally carry. */
91
+ export declare function navigateSchema(schema: Record<string, any>, path: string[], schemaFrom?: SchemaFromResolver): Record<string, any> | undefined;
53
92
  /** Walks up and down from `cursorLine` looking for a sibling line at the
54
93
  * exact same indent whose key is `kind`. The value of the first such line
55
94
  * is returned (alias form, e.g. `"Sql.Connection"`). Used by ref-name
@@ -64,7 +103,7 @@ export declare function navigateSchema(schema: Record<string, any>, path: string
64
103
  * the first branch. The analyzer's accessor unions a `kind:` list and the
65
104
  * `anyOf` / `oneOf` branches alike, so completion now offers what the slot
66
105
  * actually takes. */
67
- export declare function lookupRefConstraints(definitionSchema: Record<string, any>, yamlPath: string[]): string[];
106
+ export declare function lookupRefConstraints(definitionSchema: Record<string, any>, yamlPath: string[], schemaFrom?: SchemaFromResolver): string[];
68
107
  /** Derive a `CompletionCtx` from the AST-resolved cursor (Approach B). The
69
108
  * structural resolution lives in `resolveNodeAtPosition`; this only maps a
70
109
  * resolved slot onto the completion the editor should offer. `docs` lets a
@@ -1 +1 @@
1
- {"version":3,"file":"detect-context.d.ts","sourceRoot":"","sources":["../../src/completions/detect-context.ts"],"names":[],"mappings":"AAAA,OAAO,EAA8C,KAAK,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACjG,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAGhD,YAAY,EAAE,YAAY,EAAE,CAAC;AAE7B,MAAM,MAAM,aAAa,GACrB;IACE,IAAI,EAAE,MAAM,CAAC;IACb;;;uEAGmE;IACnE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB;;+BAE2B;IAC3B,YAAY,EAAE,YAAY,CAAC;CAC5B,GACD;IAAE,IAAI,EAAE,YAAY,CAAA;CAAE,GACtB;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,EAAE,CAAC;IAAC,YAAY,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;CAAE,GACpF;IACE;;;gDAG4C;IAC5C,IAAI,EAAE,UAAU,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB;6DACyD;IACzD,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,8DAA8D;IAC9D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,YAAY,CAAC;CAC5B,GACD;IACE,IAAI,EAAE,aAAa,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,sDAAsD;IACtD,MAAM,EAAE,MAAM,CAAC;IACf,sDAAsD;IACtD,YAAY,EAAE,YAAY,CAAC;CAC5B,CAAC;AAyEN;;;;;gEAKgE;AAChE,wBAAgB,cAAc,CAC5B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC3B,IAAI,EAAE,MAAM,EAAE,GACb,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,SAAS,CA6CjC;AAwCD;;;;;wDAKwD;AACxD;;;;;;;sBAOsB;AACtB,wBAAgB,oBAAoB,CAClC,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EACrC,QAAQ,EAAE,MAAM,EAAE,GACjB,MAAM,EAAE,CAIV;AAED;;;;oBAIoB;AACpB,wBAAgB,aAAa,CAC3B,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,MAAM,EACjB,IAAI,CAAC,EAAE,WAAW,EAAE,GACnB,aAAa,GAAG,SAAS,CAsE3B"}
1
+ {"version":3,"file":"detect-context.d.ts","sourceRoot":"","sources":["../../src/completions/detect-context.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,WAAW,EAChB,KAAK,UAAU,EAChB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAGhD,YAAY,EAAE,YAAY,EAAE,CAAC;AAE7B,MAAM,MAAM,aAAa,GACrB;IACE,IAAI,EAAE,MAAM,CAAC;IACb;;;uEAGmE;IACnE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB;;+BAE2B;IAC3B,YAAY,EAAE,YAAY,CAAC;CAC5B,GACD;IAAE,IAAI,EAAE,YAAY,CAAA;CAAE,GACtB;IACE,IAAI,EAAE,UAAU,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB;;2BAEuB;IACvB,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CAC3B,GACD;IACE;;;gDAG4C;IAC5C,IAAI,EAAE,UAAU,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB;6DACyD;IACzD,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,8DAA8D;IAC9D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,YAAY,CAAC;CAC5B,GACD;IACE,IAAI,EAAE,aAAa,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,sDAAsD;IACtD,MAAM,EAAE,MAAM,CAAC;IACf,sDAAsD;IACtD,YAAY,EAAE,YAAY,CAAC;CAC5B,GACD;IACE;;;gCAG4B;IAC5B,IAAI,EAAE,mBAAmB,CAAC;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,4EAA4E;IAC5E,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,YAAY,EAAE,YAAY,CAAC;CAC5B,GACD;IACE;;;;wDAIoD;IACpD,IAAI,EAAE,KAAK,CAAC;IACZ,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;+CAC2C;IAC3C,QAAQ,EAAE,MAAM,CAAC;IACjB;uBACmB;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,UAAU,CAAC;IACpB,mCAAmC;IACnC,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAyEN;;;;GAIG;AACH,MAAM,MAAM,kBAAkB,GAAG,CAAC,UAAU,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,SAAS,CAAC;AA0BzF;;;;;;mCAMmC;AACnC,wBAAgB,cAAc,CAC5B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC3B,IAAI,EAAE,MAAM,EAAE,EACd,UAAU,CAAC,EAAE,kBAAkB,GAC9B,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,SAAS,CA6CjC;AAwCD;;;;;wDAKwD;AACxD;;;;;;;sBAOsB;AACtB,wBAAgB,oBAAoB,CAClC,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EACrC,QAAQ,EAAE,MAAM,EAAE,EAClB,UAAU,CAAC,EAAE,kBAAkB,GAC9B,MAAM,EAAE,CAIV;AAED;;;;oBAIoB;AACpB,wBAAgB,aAAa,CAC3B,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,MAAM,EACjB,IAAI,CAAC,EAAE,WAAW,EAAE,GACnB,aAAa,GAAG,SAAS,CAqF3B"}
@@ -1,4 +1,4 @@
1
- import { parseToAst, readRefSlot, refSlotAnnotation } from "@telorun/analyzer";
1
+ import { parseToAst, readRefSlot, refSlotAnnotation, } from "@telorun/analyzer";
2
2
  import { resolveNodeAtPosition } from "./resolve-node.js";
3
3
  /** Returns every schema branch reachable from `node` after peeling `anyOf` /
4
4
  * `oneOf` recursively. A branch with no combinators is its own only entry.
@@ -72,16 +72,39 @@ function resolveLocalRef(node, root) {
72
72
  }
73
73
  return current;
74
74
  }
75
+ /**
76
+ * Expand a node whose shape comes from a sibling kind's schema.
77
+ *
78
+ * A slot annotated `x-telo-schema-from` declares NO `properties` of its own — an
79
+ * `Http.Api` route's `request:` is exactly this — so every walk that reads
80
+ * `properties` finds an empty node and silently offers nothing. The derived
81
+ * schema is merged UNDER whatever the slot itself declared, so a slot that adds
82
+ * a title or narrows a field keeps winning.
83
+ */
84
+ function resolveSchemaFrom(node, resolve) {
85
+ const from = node?.["x-telo-schema-from"];
86
+ if (!node || !resolve || typeof from !== "string")
87
+ return node;
88
+ const derived = resolve(from);
89
+ if (!derived)
90
+ return node;
91
+ return {
92
+ ...derived,
93
+ ...node,
94
+ properties: { ...(derived.properties ?? {}), ...(node.properties ?? {}) },
95
+ };
96
+ }
75
97
  /** Navigate a JSON Schema hierarchy following `path`, auto-descending into
76
- * array items, peeling `anyOf` / `oneOf` branches and following document-local
77
- * `$ref`s. When multiple peeled branches define `properties`, returns a
78
- * synthetic node whose `properties` is the union (first-wins on key collision)
79
- * and whose `required` is the intersection — enough for propKeyCompletions to
80
- * surface every key a value at this slot can legally carry. */
81
- export function navigateSchema(schema, path) {
98
+ * array items, peeling `anyOf` / `oneOf` branches, following document-local
99
+ * `$ref`s and expanding `x-telo-schema-from` slots. When multiple peeled
100
+ * branches define `properties`, returns a synthetic node whose `properties` is
101
+ * the union (first-wins on key collision) and whose `required` is the
102
+ * intersection — enough for propKeyCompletions to surface every key a value at
103
+ * this slot can legally carry. */
104
+ export function navigateSchema(schema, path, schemaFrom) {
82
105
  let current = schema;
83
106
  for (const segment of path) {
84
- current = resolveLocalRef(current, schema);
107
+ current = resolveSchemaFrom(resolveLocalRef(current, schema), schemaFrom);
85
108
  if (!current)
86
109
  return undefined;
87
110
  const candidates = peelCombinators(current).flatMap((node) => {
@@ -118,7 +141,7 @@ export function navigateSchema(schema, path) {
118
141
  return undefined;
119
142
  current = next;
120
143
  }
121
- current = resolveLocalRef(current, schema);
144
+ current = resolveSchemaFrom(resolveLocalRef(current, schema), schemaFrom);
122
145
  if (!current)
123
146
  return undefined;
124
147
  // Auto-descend through a trailing array at the leaf (e.g. cursor inside `mounts:` items)
@@ -176,8 +199,8 @@ function unionLeaves(parent, leaves) {
176
199
  * the first branch. The analyzer's accessor unions a `kind:` list and the
177
200
  * `anyOf` / `oneOf` branches alike, so completion now offers what the slot
178
201
  * actually takes. */
179
- export function lookupRefConstraints(definitionSchema, yamlPath) {
180
- const node = navigateSchema(definitionSchema, yamlPath);
202
+ export function lookupRefConstraints(definitionSchema, yamlPath, schemaFrom) {
203
+ const node = navigateSchema(definitionSchema, yamlPath, schemaFrom);
181
204
  if (!node)
182
205
  return [];
183
206
  return readRefSlot(node)?.kinds ?? [];
@@ -193,10 +216,18 @@ export function detectContext(text, line, character, docs) {
193
216
  return undefined;
194
217
  const { docKind } = resolved;
195
218
  if (resolved.slot === "value") {
196
- // Inside a CEL body — structural completion does not apply (a future
197
- // CEL-completion feature consumes `resolved.cel`).
198
- if (resolved.cel)
199
- return undefined;
219
+ // Inside a CEL body — structural completion does not apply; what completes
220
+ // are the names the expression may use.
221
+ if (resolved.cel) {
222
+ return {
223
+ type: "cel",
224
+ docKind,
225
+ docIndex: resolved.docIndex,
226
+ concretePath: resolved.concretePath ?? "",
227
+ segment: resolved.cel.segment,
228
+ offset: resolved.cel.offset,
229
+ };
230
+ }
200
231
  const replaceRange = resolved.replaceRange;
201
232
  if (!replaceRange)
202
233
  return undefined;
@@ -237,6 +268,9 @@ export function detectContext(text, line, character, docs) {
237
268
  return { type: "field-value", docKind, field: "import-source", prefix, replaceRange };
238
269
  }
239
270
  }
271
+ if (docKind) {
272
+ return { type: "value-suggestions", docKind, yamlPath: resolved.path, replaceRange };
273
+ }
240
274
  return undefined;
241
275
  }
242
276
  // Key position (existing key, blank line, or trailing indent). Complete
@@ -251,6 +285,8 @@ export function detectContext(text, line, character, docs) {
251
285
  type: "prop-key",
252
286
  docKind: scopeKind,
253
287
  yamlPath: resolved.path.slice(resolved.resourceDepth ?? 0),
288
+ concretePath: resolved.concretePath ?? "",
289
+ docIndex: resolved.docIndex,
254
290
  existingKeys: resolved.existingKeys ?? new Set(),
255
291
  };
256
292
  }
@@ -1,4 +1,8 @@
1
1
  import { type AnalysisRegistry } from "@telorun/analyzer";
2
2
  import type { CompletionResult } from "../types.js";
3
- export declare function propKeyCompletions(kind: string, yamlPath: string[], existingKeys: Set<string>, registry: AnalysisRegistry | undefined): CompletionResult[];
3
+ export declare function propKeyCompletions(kind: string, yamlPath: string[], existingKeys: Set<string>, registry: AnalysisRegistry | undefined,
4
+ /** The arguments the enclosing call declares, when this path IS that call's
5
+ * argument slot. Resolved by the caller, which is the side holding the
6
+ * manifest the reference names. */
7
+ callInputs?: Record<string, any>): CompletionResult[];
4
8
  //# sourceMappingURL=prop-keys.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"prop-keys.d.ts","sourceRoot":"","sources":["../../src/completions/prop-keys.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,gBAAgB,EACtB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAiBpD,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAAE,EAClB,YAAY,EAAE,GAAG,CAAC,MAAM,CAAC,EACzB,QAAQ,EAAE,gBAAgB,GAAG,SAAS,GACrC,gBAAgB,EAAE,CAkCpB"}
1
+ {"version":3,"file":"prop-keys.d.ts","sourceRoot":"","sources":["../../src/completions/prop-keys.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,gBAAgB,EACtB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAiBpD,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAAE,EAClB,YAAY,EAAE,GAAG,CAAC,MAAM,CAAC,EACzB,QAAQ,EAAE,gBAAgB,GAAG,SAAS;AACtC;;oCAEoC;AACpC,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAC/B,gBAAgB,EAAE,CAoDpB"}
@@ -13,7 +13,14 @@ const ROOT_IMPLICIT_PROPS = {
13
13
  description: "Resource metadata (name, namespace, version).",
14
14
  },
15
15
  };
16
- export function propKeyCompletions(kind, yamlPath, existingKeys, registry) {
16
+ export function propKeyCompletions(kind, yamlPath, existingKeys, registry,
17
+ /** The arguments the enclosing call declares, when this path IS that call's
18
+ * argument slot. Resolved by the caller, which is the side holding the
19
+ * manifest the reference names. */
20
+ callInputs) {
21
+ if (callInputs?.properties) {
22
+ return buildItems(callInputs.properties, existingKeys, new Set(Array.isArray(callInputs.required) ? callInputs.required : []));
23
+ }
17
24
  if (!registry)
18
25
  return [];
19
26
  const definition = registry.resolveDefinition(kind);
@@ -28,8 +35,17 @@ export function propKeyCompletions(kind, yamlPath, existingKeys, registry) {
28
35
  }
29
36
  const targetSchema = yamlPath.length === 0
30
37
  ? definition.schema
31
- : navigateSchema(definition.schema, yamlPath);
38
+ : navigateSchema(definition.schema, yamlPath, (from) => registry.resolveSchemaFrom(from, kind));
39
+ // A NAME-KEYED map declares its keys nowhere in `properties` — they are the
40
+ // author's own (a media type, a header name). `propertyNames` is JSON
41
+ // Schema's own vocabulary for what they may be, and it carries the open/closed
42
+ // distinction already: `enum` constrains, `examples` only suggests. So an open
43
+ // list of known values needs no annotation and no analyzer-side knowledge of
44
+ // any domain.
45
+ const keySuggestions = propertyNameSuggestions(targetSchema, existingKeys);
32
46
  if (!targetSchema?.properties) {
47
+ if (keySuggestions.length > 0)
48
+ return keySuggestions;
33
49
  if (yamlPath.length === 0) {
34
50
  return buildItems(ROOT_IMPLICIT_PROPS, existingKeys, new Set());
35
51
  }
@@ -39,7 +55,39 @@ export function propKeyCompletions(kind, yamlPath, existingKeys, registry) {
39
55
  const properties = yamlPath.length === 0
40
56
  ? { ...ROOT_IMPLICIT_PROPS, ...targetSchema.properties }
41
57
  : { ...targetSchema.properties, ...annotationKeys(targetSchema) };
42
- return buildItems(properties, existingKeys, required);
58
+ return [...keySuggestions, ...buildItems(properties, existingKeys, required)];
59
+ }
60
+ /**
61
+ * Key candidates a map-valued node declares through `propertyNames`.
62
+ *
63
+ * `enum` is a closed set and `examples` an open one — suggestions with no
64
+ * validation effect, which is exactly "these are the known values, others are
65
+ * allowed". Both are stock JSON Schema, so nothing here knows what a media type
66
+ * is and any name-keyed field gains the same behaviour by declaring it.
67
+ */
68
+ function propertyNameSuggestions(schema, existingKeys) {
69
+ const names = schema?.propertyNames;
70
+ if (!names || typeof names !== "object")
71
+ return [];
72
+ const closed = Array.isArray(names.enum) ? names.enum : undefined;
73
+ const open = Array.isArray(names.examples) ? names.examples : [];
74
+ const values = closed ?? open;
75
+ const out = [];
76
+ for (const value of values) {
77
+ if (typeof value !== "string" || existingKeys.has(value))
78
+ continue;
79
+ out.push({
80
+ label: value,
81
+ kind: "enumMember",
82
+ insertText: `${value}: $0`,
83
+ snippet: true,
84
+ detail: closed ? names.title ?? "allowed key" : names.title ?? "known key",
85
+ // Ahead of any structural key at the same level: at a name-keyed slot the
86
+ // author is choosing one of these, not adding a sibling field.
87
+ sortText: `0_${value}`,
88
+ });
89
+ }
90
+ return out;
43
91
  }
44
92
  /**
45
93
  * The `x-telo-*` vocabulary, offered only inside a KIND's own schema.
@@ -41,8 +41,15 @@ export interface ResolvedCursor {
41
41
  /** Key slot: number of `path` segments that reach `resourceKind`'s map, so
42
42
  * the schema-relative path is `path.slice(resourceDepth)`. */
43
43
  resourceDepth?: number;
44
- /** Set when the cursor sits inside a CEL body (closed or open). Populated for
45
- * a future CEL-completion feature; this refactor does not consume it. */
44
+ /** The path with sequence INDICES kept (`routes[0].handler.url`),
45
+ * as distinct from `path`, through which arrays are transparent. This is the
46
+ * address the analyzer speaks — an `x-telo-context` scope, an error-bearing
47
+ * region and a step's identity are all resolved per item — so it is what a
48
+ * CEL site is looked up by. */
49
+ concretePath?: string;
50
+ /** Set when the cursor sits inside a CEL body (closed or open). The CEL
51
+ * segment plus the cursor's document offset — what completion and hover
52
+ * hit-test the expression's chain against. */
46
53
  cel?: {
47
54
  segment: CelSegment;
48
55
  offset: number;
@@ -1 +1 @@
1
- {"version":3,"file":"resolve-node.d.ts","sourceRoot":"","sources":["../../src/completions/resolve-node.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,WAAW,EAChB,KAAK,MAAM,EACX,KAAK,OAAO,EAEZ,KAAK,UAAU,EACf,KAAK,QAAQ,EACd,MAAM,mBAAmB,CAAC;AAE3B;;;;0CAI0C;AAC1C,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB;;;sCAGkC;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,sEAAsE;IACtE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,KAAK,GAAG,OAAO,CAAC;IACtB;4DACwD;IACxD,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE;QAAE,KAAK,EAAE,QAAQ,CAAC;QAAC,GAAG,EAAE,QAAQ,CAAA;KAAE,CAAC;IAClD,8DAA8D;IAC9D,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;gFAC4E;IAC5E,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B;gCAC4B;IAC5B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;iDAC6C;IAC7C,YAAY,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC3B;;yCAEqC;IACrC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;mEAC+D;IAC/D,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;8EAC0E;IAC1E,GAAG,CAAC,EAAE;QAAE,OAAO,EAAE,UAAU,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;CAC/C;AAMD,wBAAgB,YAAY,CAAC,IAAI,EAAE,OAAO,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAG1E;AAwPD;8DAC8D;AAC9D,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,WAAW,EAAE,EACnB,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,MAAM,GAChB,cAAc,GAAG,SAAS,CAgG5B"}
1
+ {"version":3,"file":"resolve-node.d.ts","sourceRoot":"","sources":["../../src/completions/resolve-node.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,WAAW,EAChB,KAAK,MAAM,EACX,KAAK,OAAO,EAEZ,KAAK,UAAU,EACf,KAAK,QAAQ,EACd,MAAM,mBAAmB,CAAC;AAE3B;;;;0CAI0C;AAC1C,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB;;;sCAGkC;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,sEAAsE;IACtE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,KAAK,GAAG,OAAO,CAAC;IACtB;4DACwD;IACxD,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE;QAAE,KAAK,EAAE,QAAQ,CAAC;QAAC,GAAG,EAAE,QAAQ,CAAA;KAAE,CAAC;IAClD,8DAA8D;IAC9D,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;gFAC4E;IAC5E,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B;gCAC4B;IAC5B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;iDAC6C;IAC7C,YAAY,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC3B;;yCAEqC;IACrC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;mEAC+D;IAC/D,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;;oCAIgC;IAChC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;mDAE+C;IAC/C,GAAG,CAAC,EAAE;QAAE,OAAO,EAAE,UAAU,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;CAC/C;AAMD,wBAAgB,YAAY,CAAC,IAAI,EAAE,OAAO,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAG1E;AA8SD;8DAC8D;AAC9D,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,WAAW,EAAE,EACnB,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,MAAM,GAChB,cAAc,GAAG,SAAS,CAoG5B"}