@telorun/ide-support 0.13.3 → 0.14.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 (36) hide show
  1. package/dist/cel-chain.d.ts +36 -0
  2. package/dist/cel-chain.d.ts.map +1 -0
  3. package/dist/cel-chain.js +77 -0
  4. package/dist/completions/detect-context.d.ts +5 -5
  5. package/dist/completions/detect-context.d.ts.map +1 -1
  6. package/dist/completions/detect-context.js +72 -5
  7. package/dist/completions/prop-keys.d.ts +1 -1
  8. package/dist/completions/prop-keys.d.ts.map +1 -1
  9. package/dist/completions/prop-keys.js +18 -1
  10. package/dist/definition/resolve-cel-target.d.ts.map +1 -1
  11. package/dist/definition/resolve-cel-target.js +1 -60
  12. package/dist/index.d.ts +1 -0
  13. package/dist/index.d.ts.map +1 -1
  14. package/dist/index.js +1 -0
  15. package/dist/rename/build-rename.d.ts +39 -0
  16. package/dist/rename/build-rename.d.ts.map +1 -0
  17. package/dist/rename/build-rename.js +407 -0
  18. package/dist/rename/find-sites.d.ts +49 -0
  19. package/dist/rename/find-sites.d.ts.map +1 -0
  20. package/dist/rename/find-sites.js +202 -0
  21. package/dist/rename/index.d.ts +3 -0
  22. package/dist/rename/index.d.ts.map +1 -0
  23. package/dist/rename/index.js +1 -0
  24. package/dist/rename/types.d.ts +55 -0
  25. package/dist/rename/types.d.ts.map +1 -0
  26. package/dist/rename/types.js +1 -0
  27. package/package.json +2 -2
  28. package/src/cel-chain.ts +86 -0
  29. package/src/completions/detect-context.ts +73 -6
  30. package/src/completions/prop-keys.ts +23 -2
  31. package/src/definition/resolve-cel-target.ts +1 -68
  32. package/src/index.ts +1 -0
  33. package/src/rename/build-rename.ts +513 -0
  34. package/src/rename/find-sites.ts +215 -0
  35. package/src/rename/index.ts +9 -0
  36. package/src/rename/types.ts +51 -0
@@ -0,0 +1,36 @@
1
+ import type { CelNode } from "@telorun/analyzer";
2
+ /** One identifier of a dotted CEL chain, with the span a cursor hit-tests
3
+ * against and a rename replaces. */
4
+ export interface ChainPart {
5
+ name: string;
6
+ range: [number, number];
7
+ }
8
+ /**
9
+ * Flatten `a.b.c` into its identifiers. Returns undefined as soon as the chain
10
+ * is rooted in something other than a plain identifier (a call, an index), so a
11
+ * navigable — or renameable — prefix is never invented out of a computed
12
+ * expression.
13
+ */
14
+ export declare function flattenChain(node: CelNode): ChainPart[] | undefined;
15
+ /**
16
+ * A node's children.
17
+ *
18
+ * Exhaustive by construction: a new `CelNode` variant fails the build here
19
+ * rather than silently going unwalked, so the analyzer's node model and the
20
+ * walks over it cannot drift apart unnoticed. That matters twice over — for
21
+ * go-to-definition an unwalked variant is a missed jump, for a rename it is a
22
+ * reference left pointing at the old name.
23
+ */
24
+ export declare function celChildren(node: CelNode): CelNode[];
25
+ /** Every node of the tree, outermost first. */
26
+ export declare function walkCel(node: CelNode, visit: (node: CelNode) => void): void;
27
+ /**
28
+ * The dotted chain under `offset`, and which of its identifiers was hit. The
29
+ * walk is outermost-first so the longest chain wins — `resources.store.conn`
30
+ * resolves as one chain rather than as its `resources.store` prefix.
31
+ */
32
+ export declare function chainAt(node: CelNode, offset: number): {
33
+ parts: ChainPart[];
34
+ index: number;
35
+ } | undefined;
36
+ //# sourceMappingURL=cel-chain.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cel-chain.d.ts","sourceRoot":"","sources":["../src/cel-chain.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAEjD;qCACqC;AACrC,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACzB;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,OAAO,GAAG,SAAS,EAAE,GAAG,SAAS,CAKnE;AAED;;;;;;;;GAQG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,EAAE,CA0BpD;AAED,+CAA+C;AAC/C,wBAAgB,OAAO,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,GAAG,IAAI,CAG3E;AAED;;;;GAIG;AACH,wBAAgB,OAAO,CACrB,IAAI,EAAE,OAAO,EACb,MAAM,EAAE,MAAM,GACb;IAAE,KAAK,EAAE,SAAS,EAAE,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAYnD"}
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Flatten `a.b.c` into its identifiers. Returns undefined as soon as the chain
3
+ * is rooted in something other than a plain identifier (a call, an index), so a
4
+ * navigable — or renameable — prefix is never invented out of a computed
5
+ * expression.
6
+ */
7
+ export function flattenChain(node) {
8
+ if (node.kind === "ident")
9
+ return [{ name: node.name, range: node.range }];
10
+ if (node.kind !== "member")
11
+ return undefined;
12
+ const head = flattenChain(node.target);
13
+ return head ? [...head, { name: node.property, range: node.propertyRange }] : undefined;
14
+ }
15
+ /**
16
+ * A node's children.
17
+ *
18
+ * Exhaustive by construction: a new `CelNode` variant fails the build here
19
+ * rather than silently going unwalked, so the analyzer's node model and the
20
+ * walks over it cannot drift apart unnoticed. That matters twice over — for
21
+ * go-to-definition an unwalked variant is a missed jump, for a rename it is a
22
+ * reference left pointing at the old name.
23
+ */
24
+ export function celChildren(node) {
25
+ switch (node.kind) {
26
+ case "literal":
27
+ case "ident":
28
+ return [];
29
+ case "member":
30
+ return [node.target];
31
+ case "index":
32
+ return [node.target, node.index];
33
+ case "call":
34
+ return node.args;
35
+ case "methodCall":
36
+ return [node.receiver, ...node.args];
37
+ case "list":
38
+ return node.items;
39
+ case "map":
40
+ return node.entries.flatMap((e) => [e.key, e.value]);
41
+ case "ternary":
42
+ return [node.cond, node.then, node.else];
43
+ case "unary":
44
+ return [node.operand];
45
+ case "binary":
46
+ return [node.left, node.right];
47
+ }
48
+ const unhandled = node;
49
+ throw new Error(`Unhandled CEL node: ${JSON.stringify(unhandled)}`);
50
+ }
51
+ /** Every node of the tree, outermost first. */
52
+ export function walkCel(node, visit) {
53
+ visit(node);
54
+ for (const child of celChildren(node))
55
+ walkCel(child, visit);
56
+ }
57
+ /**
58
+ * The dotted chain under `offset`, and which of its identifiers was hit. The
59
+ * walk is outermost-first so the longest chain wins — `resources.store.conn`
60
+ * resolves as one chain rather than as its `resources.store` prefix.
61
+ */
62
+ export function chainAt(node, offset) {
63
+ if (offset < node.range[0] || offset > node.range[1])
64
+ return undefined;
65
+ const parts = flattenChain(node);
66
+ if (parts) {
67
+ const index = parts.findIndex((p) => offset >= p.range[0] && offset <= p.range[1]);
68
+ if (index >= 0)
69
+ return { parts, index };
70
+ }
71
+ for (const child of celChildren(node)) {
72
+ const hit = chainAt(child, offset);
73
+ if (hit)
74
+ return hit;
75
+ }
76
+ return undefined;
77
+ }
@@ -44,11 +44,11 @@ export type CompletionCtx = {
44
44
  replaceRange: ReplaceRange;
45
45
  };
46
46
  /** Navigate a JSON Schema hierarchy following `path`, auto-descending into
47
- * array items and peeling `anyOf` / `oneOf` branches. When multiple peeled
48
- * branches define `properties`, returns a synthetic node whose `properties`
49
- * is the union (first-wins on key collision) and whose `required` is the
50
- * intersection — enough for propKeyCompletions to surface every key a value
51
- * at this slot can legally carry. */
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
52
  export declare function navigateSchema(schema: Record<string, any>, path: string[]): Record<string, any> | undefined;
53
53
  /** Walks up and down from `cursorLine` looking for a sibling line at the
54
54
  * exact same indent whose key is `kind`. The value of the first such line
@@ -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;AAuBN;;;;;sCAKsC;AACtC,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,CA4BjC;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,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"}
@@ -24,15 +24,66 @@ function peelCombinators(node) {
24
24
  visit(node);
25
25
  return out;
26
26
  }
27
+ /** One JSON Pointer segment: RFC 6901 escapes, then percent-decoding, which a
28
+ * pointer carried in a URI fragment is subject to. A malformed escape is
29
+ * returned raw rather than thrown — `decodeURIComponent` raises `URIError`, and
30
+ * a stray `%` in someone's `$defs` key must not take completion down. */
31
+ function decodePointerSegment(segment) {
32
+ const unescaped = segment.replace(/~1/g, "/").replace(/~0/g, "~");
33
+ try {
34
+ return decodeURIComponent(unescaped);
35
+ }
36
+ catch {
37
+ return unescaped;
38
+ }
39
+ }
40
+ /**
41
+ * Follow a document-local `$ref` (`#/$defs/<Name>`) against the schema root.
42
+ *
43
+ * The one reference form that occurs inside a kind schema, and the one the
44
+ * editor's resolver accepts — a schema-valued slot points at the hoisted
45
+ * `JsonSchema7` / `KindSchema` fragment this way, and the fragment points at
46
+ * itself to describe a nested schema. Without the hop, completion stopped dead
47
+ * at the first key of every `schema:` / `status:` block.
48
+ *
49
+ * A chain ends at a REPEATED pointer, which is what bounds the walk: a
50
+ * self-referential fragment is the normal case here, so a cycle must degrade to
51
+ * "no completion" rather than hang the editor.
52
+ */
53
+ function resolveLocalRef(node, root) {
54
+ let current = node;
55
+ const seen = new Set();
56
+ while (current && typeof current.$ref === "string" && current.$ref.startsWith("#/")) {
57
+ const pointer = current.$ref;
58
+ if (seen.has(pointer))
59
+ return undefined;
60
+ seen.add(pointer);
61
+ let target = root;
62
+ for (const segment of pointer.slice(2).split("/")) {
63
+ target = target?.[decodePointerSegment(segment)];
64
+ }
65
+ if (!target || typeof target !== "object")
66
+ return undefined;
67
+ // Keep whatever the slot declared beside the `$ref` (its title, its
68
+ // `x-telo-fragment` stamp) — that is what tells a consumer WHICH shape it
69
+ // pointed at, and draft-07 drops it at the validation layer only.
70
+ const { $ref: _, ...siblings } = current;
71
+ current = { ...target, ...siblings };
72
+ }
73
+ return current;
74
+ }
27
75
  /** Navigate a JSON Schema hierarchy following `path`, auto-descending into
28
- * array items and peeling `anyOf` / `oneOf` branches. When multiple peeled
29
- * branches define `properties`, returns a synthetic node whose `properties`
30
- * is the union (first-wins on key collision) and whose `required` is the
31
- * intersection — enough for propKeyCompletions to surface every key a value
32
- * at this slot can legally carry. */
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. */
33
81
  export function navigateSchema(schema, path) {
34
82
  let current = schema;
35
83
  for (const segment of path) {
84
+ current = resolveLocalRef(current, schema);
85
+ if (!current)
86
+ return undefined;
36
87
  const candidates = peelCombinators(current).flatMap((node) => {
37
88
  const expanded = [];
38
89
  let cur = node;
@@ -50,10 +101,26 @@ export function navigateSchema(schema, path) {
50
101
  break;
51
102
  }
52
103
  }
104
+ // A map-valued node (a schema's `properties:`, a kind's name-keyed field)
105
+ // names its entries nowhere, so every segment lands on
106
+ // `additionalProperties`. Without this the walk stopped one level into a
107
+ // `schema:` block — at exactly the field the author is writing.
108
+ if (!next) {
109
+ for (const cand of candidates) {
110
+ const additional = cand.additionalProperties;
111
+ if (additional && typeof additional === "object") {
112
+ next = additional;
113
+ break;
114
+ }
115
+ }
116
+ }
53
117
  if (!next)
54
118
  return undefined;
55
119
  current = next;
56
120
  }
121
+ current = resolveLocalRef(current, schema);
122
+ if (!current)
123
+ return undefined;
57
124
  // Auto-descend through a trailing array at the leaf (e.g. cursor inside `mounts:` items)
58
125
  while (current.type === "array" && current.items) {
59
126
  current = current.items;
@@ -1,4 +1,4 @@
1
- import type { AnalysisRegistry } from "@telorun/analyzer";
1
+ import { type AnalysisRegistry } from "@telorun/analyzer";
2
2
  import type { CompletionResult } from "../types.js";
3
3
  export declare function propKeyCompletions(kind: string, yamlPath: string[], existingKeys: Set<string>, registry: AnalysisRegistry | undefined): CompletionResult[];
4
4
  //# 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,KAAK,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAC1D,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,GACrC,gBAAgB,EAAE,CAkCpB"}
@@ -1,3 +1,4 @@
1
+ import { manifestFragmentOf, TELO_SCHEMA_ANNOTATIONS, } from "@telorun/analyzer";
1
2
  import { navigateSchema } from "./detect-context.js";
2
3
  /** Kernel-implicit fields. Every Telo resource declares its `kind` and a
3
4
  * `metadata` object; the analyzer's schema validator injects them when
@@ -37,9 +38,25 @@ export function propKeyCompletions(kind, yamlPath, existingKeys, registry) {
37
38
  const required = new Set(Array.isArray(targetSchema.required) ? targetSchema.required : []);
38
39
  const properties = yamlPath.length === 0
39
40
  ? { ...ROOT_IMPLICIT_PROPS, ...targetSchema.properties }
40
- : targetSchema.properties;
41
+ : { ...targetSchema.properties, ...annotationKeys(targetSchema) };
41
42
  return buildItems(properties, existingKeys, required);
42
43
  }
44
+ /**
45
+ * The `x-telo-*` vocabulary, offered only inside a KIND's own schema.
46
+ *
47
+ * Read off the fragment stamp rather than the position: a kind schema nests, so
48
+ * "am I inside one" is a fact about the node, not about how deep the cursor
49
+ * sits. A plain data schema — an `inputType:`, a `status:` block — stamps
50
+ * `JsonSchema7` and gets nothing, because an annotation there configures a slot
51
+ * that does not exist.
52
+ *
53
+ * The vocabulary lives on the analyzer side and never enters a manifest: a
54
+ * `properties` map holding a literal `x-telo-ref` key reads to the annotation
55
+ * walkers as an annotated node.
56
+ */
57
+ function annotationKeys(node) {
58
+ return manifestFragmentOf(node) === "KindSchema" ? TELO_SCHEMA_ANNOTATIONS : {};
59
+ }
43
60
  function buildItems(properties, existingKeys, required) {
44
61
  const items = [];
45
62
  for (const [prop, propSchema] of Object.entries(properties)) {
@@ -1 +1 @@
1
- {"version":3,"file":"resolve-cel-target.d.ts","sourceRoot":"","sources":["../../src/definition/resolve-cel-target.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,UAAU,EACf,KAAK,WAAW,EAChB,KAAK,YAAY,EAClB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAwGpD;;;;;;;;;;;;oBAYoB;AACpB,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,WAAW,EAClB,aAAa,EAAE,YAAY,EAC3B,OAAO,EAAE,UAAU,EACnB,MAAM,EAAE,MAAM,GACb,gBAAgB,GAAG,SAAS,CAqB9B"}
1
+ {"version":3,"file":"resolve-cel-target.d.ts","sourceRoot":"","sources":["../../src/definition/resolve-cel-target.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,UAAU,EACf,KAAK,WAAW,EAChB,KAAK,YAAY,EAClB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAqCpD;;;;;;;;;;;;oBAYoB;AACpB,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,WAAW,EAClB,aAAa,EAAE,YAAY,EAC3B,OAAO,EAAE,UAAU,EACnB,MAAM,EAAE,MAAM,GACb,gBAAgB,GAAG,SAAS,CAqB9B"}
@@ -1,69 +1,10 @@
1
1
  import { CelParseError, } from "@telorun/analyzer";
2
2
  import { locateImport, locateModuleDocKey, locateResource, moduleFiles, } from "./manifest-navigation.js";
3
3
  import { resolveExportedResource } from "./resolve-export-chain.js";
4
+ import { chainAt } from "../cel-chain.js";
4
5
  /** Root CEL scopes whose members are declared as a block on the module doc, so
5
6
  * `variables.port` navigates to `variables:` and then to its `port:` entry. */
6
7
  const DECLARATION_SCOPES = new Set(["variables", "secrets", "ports"]);
7
- /** Flatten `a.b.c` into its identifiers. Returns undefined as soon as the chain
8
- * is rooted in something other than a plain identifier (a call, an index), so a
9
- * navigable prefix is never invented out of a computed expression. */
10
- function flattenChain(node) {
11
- if (node.kind === "ident")
12
- return [{ name: node.name, range: node.range }];
13
- if (node.kind !== "member")
14
- return undefined;
15
- const head = flattenChain(node.target);
16
- return head ? [...head, { name: node.property, range: node.propertyRange }] : undefined;
17
- }
18
- /** Exhaustive by construction: a new `CelNode` variant fails the build here
19
- * rather than silently going unwalked, so the analyzer's node model and this
20
- * walk cannot drift apart unnoticed. */
21
- function celChildren(node) {
22
- switch (node.kind) {
23
- case "literal":
24
- case "ident":
25
- return [];
26
- case "member":
27
- return [node.target];
28
- case "index":
29
- return [node.target, node.index];
30
- case "call":
31
- return node.args;
32
- case "methodCall":
33
- return [node.receiver, ...node.args];
34
- case "list":
35
- return node.items;
36
- case "map":
37
- return node.entries.flatMap((e) => [e.key, e.value]);
38
- case "ternary":
39
- return [node.cond, node.then, node.else];
40
- case "unary":
41
- return [node.operand];
42
- case "binary":
43
- return [node.left, node.right];
44
- }
45
- const unhandled = node;
46
- throw new Error(`Unhandled CEL node: ${JSON.stringify(unhandled)}`);
47
- }
48
- /** The dotted chain under `offset`, and which of its identifiers was hit. The
49
- * walk is outermost-first so the longest chain wins — `resources.Store.conn`
50
- * resolves as one chain rather than as its `resources.Store` prefix. */
51
- function chainAt(node, offset) {
52
- if (offset < node.range[0] || offset > node.range[1])
53
- return undefined;
54
- const parts = flattenChain(node);
55
- if (parts) {
56
- const index = parts.findIndex((p) => offset >= p.range[0] && offset <= p.range[1]);
57
- if (index >= 0)
58
- return { parts, index };
59
- }
60
- for (const child of celChildren(node)) {
61
- const hit = chainAt(child, offset);
62
- if (hit)
63
- return hit;
64
- }
65
- return undefined;
66
- }
67
8
  /** `resources.<name>` is a local instance; `resources.<Alias>.<name>` is an
68
9
  * imported module's exported one. A local name wins over an import alias, so a
69
10
  * deeper access on a local instance (`resources.db.url`) reads as a field of
package/dist/index.d.ts CHANGED
@@ -4,5 +4,6 @@ export * from "./diagnostics/index.js";
4
4
  export * from "./hover/index.js";
5
5
  export * from "./semantic-tokens/index.js";
6
6
  export * from "./definition/index.js";
7
+ export * from "./rename/index.js";
7
8
  export * from "./import-upgrades/index.js";
8
9
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,wBAAwB,CAAC;AACvC,cAAc,wBAAwB,CAAC;AACvC,cAAc,kBAAkB,CAAC;AACjC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,uBAAuB,CAAC;AACtC,cAAc,4BAA4B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,wBAAwB,CAAC;AACvC,cAAc,wBAAwB,CAAC;AACvC,cAAc,kBAAkB,CAAC;AACjC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,uBAAuB,CAAC;AACtC,cAAc,mBAAmB,CAAC;AAClC,cAAc,4BAA4B,CAAC"}
package/dist/index.js CHANGED
@@ -4,4 +4,5 @@ export * from "./diagnostics/index.js";
4
4
  export * from "./hover/index.js";
5
5
  export * from "./semantic-tokens/index.js";
6
6
  export * from "./definition/index.js";
7
+ export * from "./rename/index.js";
7
8
  export * from "./import-upgrades/index.js";
@@ -0,0 +1,39 @@
1
+ import { type AstDocument, type LoadedGraph } from "@telorun/analyzer";
2
+ import type { RenamePreparation, RenameResult } from "./types.js";
3
+ /**
4
+ * Rename a name and every reference to it.
5
+ *
6
+ * **This is a refactor, not a fix**, and the distinction is the reason it lives
7
+ * here rather than behind a `DiagnosticFix`. A fix is a whole-value replacement
8
+ * for ONE node, verified by the diagnostic that produced it; a rename is only
9
+ * correct when every reference moves with it, which means the operation's unit
10
+ * is the reference graph, not the node. Renaming `metadata.name` alone leaves
11
+ * every `!ref`, `resources.<name>` and `steps.<name>.result` pointing at a name
12
+ * that no longer exists — so a rename offered as a quick fix would break the
13
+ * file it claimed to repair.
14
+ *
15
+ * **Three renameable surfaces, and every other one is an explicit refusal.**
16
+ * What they have in common is that their reference set is *enumerable from this
17
+ * workspace*: a resource instance, a step, and a `variables:` / `secrets:` /
18
+ * `ports:` key are all module-local. A kind name, a module name and an import
19
+ * alias are not supported yet — their references reach schema annotations
20
+ * (`x-telo-ref`, `extends`, `exports.kinds`) as alias-qualified halves of a
21
+ * larger value, which is a materially bigger surface than a bare identifier and
22
+ * wants its own pass.
23
+ *
24
+ * **A name in `exports.resources` is refused outright**, and that is the load-
25
+ * bearing refusal. Such a name is the library's public ABI: a consumer writes
26
+ * `!ref Alias.name` and reads `resources.Alias.name`, in files this workspace
27
+ * may not contain and, for a published consumer, cannot. Renaming it is a
28
+ * breaking change to be *versioned*, not an edit to be applied — and a rename
29
+ * box that silently ships one would be the worst available framing.
30
+ *
31
+ * **Ambiguity is refused rather than guessed.** A name declared twice in reach
32
+ * (a `with:`-scoped resource shadowing a module-level one, two steps in one
33
+ * resource sharing a spelling) has references that resolve to different
34
+ * declarations, and no edit set is right for both.
35
+ */
36
+ export declare function prepareRename(text: string, line: number, character: number, graph: LoadedGraph, currentFilePath: string, docs?: AstDocument[]): RenamePreparation;
37
+ /** Prepare, validate the new name, then collect every edit. */
38
+ export declare function buildRename(text: string, line: number, character: number, newName: string, graph: LoadedGraph, currentFilePath: string, docs?: AstDocument[]): RenameResult;
39
+ //# sourceMappingURL=build-rename.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"build-rename.d.ts","sourceRoot":"","sources":["../../src/rename/build-rename.ts"],"names":[],"mappings":"AAAA,OAAO,EAML,KAAK,WAAW,EAGhB,KAAK,WAAW,EAGjB,MAAM,mBAAmB,CAAC;AAa3B,OAAO,KAAK,EAGV,iBAAiB,EACjB,YAAY,EAEb,MAAM,YAAY,CAAC;AAEpB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,wBAAgB,aAAa,CAC3B,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,WAAW,EAClB,eAAe,EAAE,MAAM,EACvB,IAAI,CAAC,EAAE,WAAW,EAAE,GACnB,iBAAiB,CAsBnB;AAED,+DAA+D;AAC/D,wBAAgB,WAAW,CACzB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,WAAW,EAClB,eAAe,EAAE,MAAM,EACvB,IAAI,CAAC,EAAE,WAAW,EAAE,GACnB,YAAY,CA0Bd"}