gitnexus 1.6.10 → 1.6.11-rc.2

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 (31) hide show
  1. package/dist/core/group/extractors/http-patterns/kotlin.js +522 -7
  2. package/dist/core/group/extractors/http-patterns/php.js +279 -11
  3. package/dist/core/index-freshness.d.ts +2 -2
  4. package/dist/core/index-freshness.js +13 -0
  5. package/dist/core/ingestion/parsing-processor.d.ts +2 -0
  6. package/dist/core/ingestion/parsing-processor.js +5 -0
  7. package/dist/core/ingestion/pipeline-phases/parse-impl.d.ts +3 -0
  8. package/dist/core/ingestion/pipeline-phases/parse-impl.js +7 -0
  9. package/dist/core/ingestion/pipeline-phases/parse.d.ts +4 -0
  10. package/dist/core/ingestion/pipeline.js +4 -1
  11. package/dist/core/ingestion/route-extractors/kotlin-const-resolver.d.ts +377 -0
  12. package/dist/core/ingestion/route-extractors/kotlin-const-resolver.js +1203 -0
  13. package/dist/core/ingestion/scope-extractor-bridge.js +8 -2
  14. package/dist/core/ingestion/scope-resolution/pipeline/phase.d.ts +2 -0
  15. package/dist/core/ingestion/scope-resolution/pipeline/phase.js +14 -2
  16. package/dist/core/ingestion/scope-resolution/pipeline/run.d.ts +2 -0
  17. package/dist/core/ingestion/scope-resolution/pipeline/run.js +11 -1
  18. package/dist/core/ingestion/scope-resolution/scope-extraction-failures.d.ts +14 -0
  19. package/dist/core/ingestion/scope-resolution/scope-extraction-failures.js +35 -0
  20. package/dist/core/ingestion/workers/parse-worker.d.ts +6 -0
  21. package/dist/core/ingestion/workers/parse-worker.js +7 -1
  22. package/dist/core/ingestion/workers/result-merge.js +4 -1
  23. package/dist/core/run-analyze.js +6 -0
  24. package/dist/mcp/local/local-backend.d.ts +2 -0
  25. package/dist/mcp/local/local-backend.js +29 -0
  26. package/dist/mcp/tools.js +6 -4
  27. package/dist/storage/parse-cache.js +6 -6
  28. package/dist/storage/repo-meta.d.ts +17 -1
  29. package/dist/storage/repo-meta.js +1 -1
  30. package/dist/types/pipeline.d.ts +4 -0
  31. package/package.json +1 -1
@@ -0,0 +1,377 @@
1
+ /**
2
+ * Kotlin binding for the language-agnostic constant resolver (#2391 core).
3
+ *
4
+ * Supplies the two Kotlin-specific pieces — {@link resolveKotlinImport} (import
5
+ * specifier → file, honoring JVM package rules) and
6
+ * {@link extractKotlinModuleConstants} (tree → {@link ModuleConstants}) — plus
7
+ * the folding entry points {@link resolveKotlinConstant} and
8
+ * {@link foldKotlinOperands}, so callers stay language-oblivious.
9
+ *
10
+ * WHAT IS ACTUALLY SHARED WITH THE AGNOSTIC CORE. One value —
11
+ * {@link MAX_FOLD_LENGTH} — and five types. The core's own `resolveConstant` /
12
+ * `resolveOperands` are NOT called: the fold state machine below (cycle guard,
13
+ * success memo, depth caps, operand concatenation — roughly 200 of this file's
14
+ * lines) is a local fork, close enough to `java-const-resolver.ts`'s already
15
+ * forked copy that the two read as the same code with the language name
16
+ * swapped.
17
+ *
18
+ * That fork is a consequence, not an oversight. The core keys its maps by
19
+ * SIMPLE name, and a Kotlin operand can be a QUALIFIED reference at any
20
+ * position (`X = ApiPaths.Y + "/tail"`); handed to the core, `ApiPaths.Y` misses
21
+ * every map and floors the whole chain to null — see {@link computeKotlinFold},
22
+ * which resolves operands through the qualified-aware walk for exactly this
23
+ * reason. The import chase is Kotlin-specific too: a member import is spelled
24
+ * identically to a type import, so {@link resolveImportedName} has to try both
25
+ * readings, and the core exposes no hook for that. Java forked first on the same
26
+ * grounds. Teaching the core qualified names, and retiring both copies against
27
+ * it, is the standing follow-up; until then the honest description of this file
28
+ * is "a second fork", not "a binding over a shared fold".
29
+ *
30
+ * Kotlin shares the JVM package/import model with Java, so this binding mirrors
31
+ * `java-const-resolver.ts` in structure, naming and skip-floor discipline. The
32
+ * four places Kotlin genuinely differs are handled explicitly, not translated:
33
+ *
34
+ * 1. **Where a constant can live.** Java has one carrier (`static final` on a
35
+ * type). Kotlin has three: a top-level `const val`/`val`, a member of an
36
+ * `object`, and a member of a `companion object` — the last is referenced
37
+ * through its ENCLOSING class (`Holder.NAME`), not through `Companion`.
38
+ * 2. **No `String` type gate.** Kotlin infers property types, so
39
+ * `const val ORDERS = "/orders"` carries no type node to check. The
40
+ * initializer decides: anything {@link parseKotlinConstOperands} cannot fold
41
+ * to a string (a number, a call, a template) drops the constant.
42
+ * 3. **File names and directories are free.** `object ApiPaths` may live in
43
+ * `Constants.kt`, and a file's `package` need not match the directory it
44
+ * sits in, so a `<package>/<Name>.kt` PATH lookup is a convention and not a
45
+ * rule. The authority is each file's DECLARED `package`, which
46
+ * {@link extractKotlinModuleConstants} records and {@link resolveKotlinImport}
47
+ * requires an exact match on; the path is only a tie-break among files that
48
+ * already declare the right package.
49
+ * 4. **Member imports are unmarked.** Java spells them `import static a.b.C.F`;
50
+ * Kotlin writes `import a.b.C.F`, which is byte-identical to a type import
51
+ * of a class `F` in package `a.b.C`. Nothing in the syntax says which, so
52
+ * the fold tries both readings (see `resolveImportedName`) instead of
53
+ * guessing from casing.
54
+ * 5. **Any identifier may be backtick-quoted.** `` package com.example.`api` ``
55
+ * and `package com.example.api` are the SAME package to the compiler, and a
56
+ * keyword segment (`` com.example.`fun` ``) can only be spelled the quoted
57
+ * way. The grammar keeps the backticks in the node text, so every identifier is
58
+ * read through {@link unquoteKotlinIdentifier} before it becomes a map key
59
+ * or a lookup name — see that function for what a verbatim comparison cost.
60
+ *
61
+ * THREE PLACES THIS BINDING NO LONGER MIRRORS JAVA, each because the mirrored
62
+ * behavior was wrong rather than merely different, and each open as a Java
63
+ * follow-up rather than fixed here:
64
+ *
65
+ * * `java-const-resolver.ts` flattens nested types into one file-level
66
+ * namespace and argues the collision away — "qualified refs carry the class
67
+ * name, so nesting only matters for same-name fields, which flatten
68
+ * last-wins". The argument does not hold: the collision is one level BELOW
69
+ * the qualification, in the initializer, so a fully qualified `A.ROUTE` whose
70
+ * initializer names a bare sibling `BASE` still resolves through whichever
71
+ * same-named sibling was walked last. {@link extractKotlinModuleConstants}
72
+ * keys by Kotlin's own visibility instead.
73
+ * * Java's import resolution can lean on the `<package>/<Name>.java` layout the
74
+ * language enforces. Kotlin's cannot, and inferring the package from the path
75
+ * lets a path-suffix twin outrank the real declaration — so
76
+ * {@link resolveKotlinImport} reads the declared `package` instead.
77
+ * * Java's fold entry points take a file and a name, because a Java `static
78
+ * final` reachable by simple name is reachable that way from anywhere in the
79
+ * file. A Kotlin COMPANION member is not: it is bound unqualified only inside
80
+ * its enclosing class body. {@link foldKotlinOperands} therefore also takes
81
+ * the enclosing type chain of the reference site, which is what lets the
82
+ * binding answer a bare reference by Kotlin's scoping rather than by "whoever
83
+ * was walked last" — see {@link qualifyKotlinRefInEnclosingTypes}.
84
+ *
85
+ * Constant shapes this binding harvests:
86
+ *
87
+ * const val TOP_LEVEL = "/api/v1" // file top level
88
+ * object ApiPaths { // object member
89
+ * const val BASE = "/api/v1"
90
+ * val ORDERS = BASE + "/orders"
91
+ * }
92
+ * class Holder { companion object { const val H = "/h" } } // → Holder.H
93
+ *
94
+ * Reference shapes at annotation sites this binding resolves:
95
+ * @PostMapping(ApiPaths.ORDERS) // qualified
96
+ * @PostMapping(com.example.app.api.ApiPaths.ORDERS) // FQN-qualified
97
+ * @PostMapping(ORDERS) // single-name import
98
+ * @PostMapping(ApiPaths.BASE + "/orders") // inline concat
99
+ *
100
+ * Which ANNOTATIONS count as routes is a separate question this module has no
101
+ * say in — `spring-shared.ts` owns that map. Folding and annotation recognition
102
+ * compose; neither implies the other.
103
+ *
104
+ * Keying (parity with the Java and Python bindings): the repo map is keyed by
105
+ * unique POSIX file path, and an import that cannot be pinned to exactly one
106
+ * file returns null (skip floor), never a wrong path. A missing route is a
107
+ * missing fact; a wrongly folded one is a false edge in the graph. "Exactly one
108
+ * file" is decided from the DECLARED package, not from the path: a path is a
109
+ * repository-layout accident that any decoy directory can imitate, whereas the
110
+ * `package` header is the declaration the compiler itself resolves against.
111
+ *
112
+ * POSIX keys are a PRECONDITION this module cannot check cheaply, so it is
113
+ * enforced at the one boundary that produces them: `http-patterns/kotlin.ts`
114
+ * normalizes separators on both the write side (the `prepareRepo` map keys) and
115
+ * the read side (`scan`'s `fileRel`). It has to, because the orchestrator's file
116
+ * list comes from glob v13, which has no `posix: true` and joins with the
117
+ * platform separator — so on Windows the keys arrive backslashed and every
118
+ * `<pkg>/<Name>.kt` test in {@link resolveKotlinImport} would miss, silently
119
+ * disabling cross-file folding on that platform alone. Normalizing INSIDE this
120
+ * module instead cannot work: the resolver returns the key it matched, and a
121
+ * normalized return value would then miss in a map that was never normalized.
122
+ *
123
+ * WHERE THIS IS WIRED. Java reaches its binding from BOTH layers: the group
124
+ * extractor (`group/extractors/http-patterns/java.ts`) and the ingestion
125
+ * provider (`languages/java.ts`, via `extractModuleConstants` +
126
+ * `foldRoutePathOperands`). Kotlin is wired into the GROUP layer only, because
127
+ * the ingestion fold in `pipeline-phases/parse-impl.ts` runs exclusively over
128
+ * `decoratorRoutes` — and `languages/kotlin.ts` declares no
129
+ * `extractDecoratorRoutes`, since the ingestion Spring extractor (`spring.ts`)
130
+ * is bound to `tree-sitter-java` and its node types. Declaring the constant
131
+ * hooks on the Kotlin provider today would harvest a map on every Kotlin file
132
+ * that nothing consumes. An ingestion-side Kotlin route extractor is the
133
+ * prerequisite; when it lands, this binding is what its provider hooks should
134
+ * point at, and no change here is needed.
135
+ */
136
+ import type Parser from 'tree-sitter';
137
+ import { type ModuleConstants, type Operand, type RepoConstants } from './constant-resolver.js';
138
+ export type { ImportBinding, ModuleConstants, Operand, RepoConstants, } from './constant-resolver.js';
139
+ /**
140
+ * What {@link extractKotlinModuleConstants} returns: the agnostic
141
+ * {@link ModuleConstants} plus the one piece of per-file metadata JVM import
142
+ * resolution cannot be honest without — the file's declared `package`.
143
+ *
144
+ * Deliberately a KOTLIN-LOCAL widening rather than a field on the shared type.
145
+ * `ModuleConstants` is consumed by the Java, JS and Python bindings too, and
146
+ * none of them needs this: Python resolves imports from the module path, and
147
+ * Java's `package` is already pinned by the `<package>/<Name>.java` rule the
148
+ * language enforces. Adding a required field there would force three unrelated
149
+ * bindings to fill it in; adding an optional one would put a Kotlin-shaped hole
150
+ * in a type whose whole point is language neutrality.
151
+ *
152
+ * Read the metadata through {@link declaredPackageOf} and
153
+ * {@link unfoldableDeclarationsOf}, never by field access: a
154
+ * {@link RepoConstants} is typed over the agnostic shape, so an entry that some
155
+ * other producer put there carries no package and must be REJECTED as a
156
+ * candidate rather than silently treated as the default package. Missing
157
+ * unfoldable-declaration metadata instead means "none known", preserving the
158
+ * agnostic entry's existing behavior.
159
+ */
160
+ export interface KotlinModuleConstants extends ModuleConstants {
161
+ /** The file's declared `package`, or `''` for the default package. */
162
+ readonly packageName: string;
163
+ /** Declaration keys whose initializer cannot be folded. */
164
+ readonly unfoldableDeclarations: ReadonlySet<string>;
165
+ }
166
+ /**
167
+ * Kotlin declaration keys known to exist but not fold, or an empty set when
168
+ * `mc` came from another language binding.
169
+ */
170
+ export declare function unfoldableDeclarationsOf(mc: ModuleConstants | undefined): ReadonlySet<string>;
171
+ /**
172
+ * The name a backtick-quoted Kotlin identifier denotes: `` `api` `` → `api`.
173
+ *
174
+ * Quotes are spelling, not part of the name. tree-sitter-kotlin keeps them in
175
+ * node text, so every identifier that becomes a map key or lookup is read
176
+ * through here. Applied per dot-separated segment — a quoted identifier cannot
177
+ * contain `.`. Both the declaration side ({@link declaredPackage}) and the
178
+ * import side ({@link resolveKotlinImport}) are normalized, because either may
179
+ * carry the quotes while the other spells the same name plainly.
180
+ */
181
+ export declare function unquoteKotlinIdentifier(text: string): string;
182
+ export declare function isKotlinConstantFile(source: string): boolean;
183
+ /** Files and declaration ownership for one exact Kotlin package. */
184
+ export interface KotlinPackageConstants {
185
+ readonly files: readonly string[];
186
+ /** Unique declaring file, or null when the package declares the name twice. */
187
+ readonly declarers: ReadonlyMap<string, string | null>;
188
+ }
189
+ /** One exact package-qualified declaration and its in-file lookup key. */
190
+ interface KotlinImportTarget {
191
+ readonly fileKey: string;
192
+ readonly localName: string;
193
+ }
194
+ /**
195
+ * Repo-wide projections reused by every fold in one extraction run.
196
+ *
197
+ * `repo` includes importing files overlaid by `scan`; `constantKeys` and
198
+ * `byPackage` include only files that define a foldable or explicitly
199
+ * unfoldable declaration, preserving import ambiguity semantics.
200
+ */
201
+ export interface KotlinConstantIndex {
202
+ readonly repo: RepoConstants;
203
+ readonly constantKeys: ReadonlySet<string>;
204
+ readonly byPackage: ReadonlyMap<string, KotlinPackageConstants>;
205
+ /** Exact FQN → unique file/local key, or null when the FQN is duplicated. */
206
+ readonly byFqn: ReadonlyMap<string, KotlinImportTarget | null>;
207
+ }
208
+ /** Build the immutable import projections once for a repo constant map. */
209
+ export declare function buildKotlinConstantIndex(repo: RepoConstants): KotlinConstantIndex;
210
+ /**
211
+ * Add one scan-time file without rebuilding the base index when it only imports
212
+ * constants. A newly discovered declaration is rare and rebuilds once for that
213
+ * file's scan, never once per route.
214
+ */
215
+ export declare function overlayKotlinConstantIndex(index: KotlinConstantIndex, fileKey: string, mc: ModuleConstants): KotlinConstantIndex;
216
+ /**
217
+ * Map a fully-qualified import specifier to the unique file key it refers to, or
218
+ * null when it cannot be pinned to exactly one file.
219
+ *
220
+ * A specifier is split at its last dot into the package it names and the
221
+ * declaration inside it (`com.example.app.api` + `ApiPaths`). Resolution then
222
+ * runs in three steps, all of them "unique or nothing":
223
+ *
224
+ * 0. **Declared package** — only files whose `package` header is EXACTLY the
225
+ * sought package can carry the declaration (compared after
226
+ * {@link unquoteKotlinIdentifier}, since backtick quoting is spelling and
227
+ * not identity). This is the authority, and it
228
+ * is checked first. Kotlin does not require a file's directory to match its
229
+ * package, so the reverse test — "does this path end with the package?" —
230
+ * answers a different question, one any decoy directory can satisfy: a file
231
+ * at `src/x/com/example/api/ApiPaths.kt` declaring `package x.com.example.api`
232
+ * is not `com.example.api.ApiPaths` and must never be folded as it, and a
233
+ * path-suffix test also lets a root-level `package data` be impersonated by
234
+ * `…/com/example/data/`. An entry with no recorded package is rejected, not
235
+ * assumed to be the default package.
236
+ * 1. **Declared name** — when exactly one file declares the sought name, use
237
+ * it. When two do, the FQN itself is duplicated in the repository and names
238
+ * no single declaration, so return null. This is the general form of the
239
+ * same-FQN check step 2 could only make for files that happen to follow the
240
+ * file-name convention, and it is what stops a `src/test/…` copy of a
241
+ * production constant from being folded into a production route.
242
+ * 2. **File named after the declaration** — when declaration metadata found no
243
+ * owner, try the package-matching file ending
244
+ * `com/example/app/api/ApiPaths.kt`. Kotlin does not require this (`object
245
+ * ApiPaths` may live in `Constants.kt`), so it is only a fallback candidate;
246
+ * the subsequent map lookup must still prove that it carries the value.
247
+ * 3. **Sole file in the package** — when declaration metadata cannot identify
248
+ * the name, use the unique package-matching candidate. The set passed in
249
+ * contains files with foldable or explicitly unfoldable declarations, so
250
+ * unrelated files cannot create ambiguity once step 1 identifies a unique
251
+ * declarer. With 2+ unidentified candidates it returns null.
252
+ *
253
+ * Steps 2 and 3 can still hand back a file that does not declare the wanted name
254
+ * (its package is right and it is the only candidate, but the name lives
255
+ * elsewhere or nowhere). That remains safe by construction: the fold looks the
256
+ * name up in that file's map, misses, and returns null.
257
+ *
258
+ * A "nearest shared directory" tie-break is deliberately NOT applied when a step
259
+ * has several candidates, for the reason the Java binding records: the JVM
260
+ * resolves duplicate FQNs by classpath order, not directory proximity, so a test
261
+ * fixture copy sitting closer in the tree can outrank the real dependency and
262
+ * yield a silently wrong literal. In a resolver whose whole contract is
263
+ * skip-or-correct, a plausible guess is the one answer that cannot be allowed.
264
+ *
265
+ * This can no longer be typed as the agnostic {@link ModuleConstants} consumer's
266
+ * `ImportResolver`, whose signature carries only file KEYS: deciding a candidate
267
+ * on its declared package needs the map those keys index. Nothing is lost — the
268
+ * core's own fold is not used here either (see the module header), and the
269
+ * alternative is a resolver that must guess from a path.
270
+ */
271
+ export declare function resolveKotlinImport(_importingFileKey: string, rawModuleSpec: string, candidateKeys: ReadonlySet<string>, repo: RepoConstants): string | null;
272
+ /** Indexed equivalent of {@link resolveKotlinImport}, with identical fallbacks. */
273
+ export declare function resolveKotlinImportWithIndex(rawModuleSpec: string, index: KotlinConstantIndex): string | null;
274
+ /**
275
+ * Parse a Kotlin constant initializer (or an inline annotation argument) into an
276
+ * operand list, or null when it is not a foldable string expression. Handles a
277
+ * bare string literal, a bare identifier (`X = Y`), a qualified reference
278
+ * (`X = ApiPaths.Y` — recorded as ONE ref named `ApiPaths.Y`), and
279
+ * left-associative `+` chains of the three. Everything else — numbers, calls,
280
+ * `when`/`if` expressions, templates, `buildString` — returns null, which makes
281
+ * the constant unresolvable (→ skip floor), never a wrong value.
282
+ *
283
+ * A chain nests: tree-sitter-kotlin parses `A + B + C` as
284
+ * `additive_expression(additive_expression(A, B), C)`, so every node here has
285
+ * exactly two operands and arbitrary-length chains fold by recursion. The same
286
+ * node type also carries `-`, which is not a string operation, so a `+` token
287
+ * must be present.
288
+ *
289
+ * A PARENTHESIZED operand (`(A + B) + "/c"`) is deliberately NOT unwrapped,
290
+ * matching `parseJavaConstOperands`, which has no parenthesis arm either. The
291
+ * shape is vanishingly rare in a route annotation and the cost of omitting it is
292
+ * a skipped route, not a wrong one; adding it to both bindings at once is the
293
+ * only way to keep them in parity, so it is left to a follow-up.
294
+ */
295
+ export declare function parseKotlinConstOperands(node: Parser.SyntaxNode | null | undefined, depth?: number): Operand[] | null;
296
+ /**
297
+ * Extract the declared package, file-level string constants and import bindings
298
+ * of one parsed Kotlin file into the {@link KotlinModuleConstants} shape the
299
+ * resolver consumes.
300
+ *
301
+ * Constants come from the three carriers Kotlin allows a caller to reach without
302
+ * an instance: file top level, `object` members, and `companion object` members.
303
+ * A `val` in a plain class or interface body is per-instance or abstract and is
304
+ * NOT collected — the Kotlin analogue of Java's `static final` requirement. `var`
305
+ * is rejected outright.
306
+ *
307
+ * KEYS FOLLOW KOTLIN'S OWN VISIBILITY, not a flattened namespace. Every constant
308
+ * is recorded under `<DeclaringType>.<NAME>`, the spelling a qualified reference
309
+ * uses, with a companion member keyed under its ENCLOSING CLASS (`Holder.NAME`)
310
+ * because that is how Kotlin source refers to it — `Companion` never appears in
311
+ * a reference. The SIMPLE name is recorded only for a TOP-LEVEL `val`, the one
312
+ * carrier whose bare binding really does span the file. A member of a named
313
+ * `object` gets no bare key, because `BASE` alone does not name `A.BASE` from
314
+ * anywhere outside `object A`'s own body. Writing one anyway (as this binding
315
+ * and the Java one both used to) fabricates a binding the language does not
316
+ * have, and a fabricated key outranks the genuine `import com.example.api.Paths.ORDERS`
317
+ * that {@link computeKotlinFold} consults only after literals and expressions.
318
+ *
319
+ * A COMPANION member gets no bare key either: it is bound unqualified inside
320
+ * its enclosing class body and nowhere else. {@link qualifyKotlinRefInEnclosingTypes}
321
+ * rewrites a bare name to `<EnclosingType>.<NAME>` when an enclosing type
322
+ * declares it, so the companion wins inside its own class and loses everywhere
323
+ * else.
324
+ *
325
+ * An initializer that names a SIBLING is resolved the same way, against its own
326
+ * scope chain, innermost first, before the file level: inside
327
+ * `object A { const val BASE = "/right"; const val ROUTE = BASE + "/m" }` the
328
+ * operand `BASE` is rewritten to `A.BASE`. Collecting every declaration before
329
+ * recording any keeps that independent of declaration order.
330
+ *
331
+ * A TOP-LEVEL initializer has an EMPTY scope chain, so its bare operands stay
332
+ * bare and resolve at file level — they must not pick up a companion key.
333
+ *
334
+ * A non-foldable rebind (`X = compute()`) DROPS X to unresolvable rather than
335
+ * leaving a stale literal — and drops a same-named import with it whenever the
336
+ * declaration shadows that import ANYWHERE (top level, or a companion inside its
337
+ * class). The import map has no scopes, so a companion's shadow is applied
338
+ * file-wide: the conservative direction, costing a route rather than publishing
339
+ * the imported value at a reference the compiler binds to the unfoldable member.
340
+ * An `object` member shadows nothing and must leave the import alone.
341
+ */
342
+ export declare function extractKotlinModuleConstants(tree: Parser.Tree): KotlinModuleConstants;
343
+ /**
344
+ * Resolve a single Kotlin constant referenced in `fileKey` to its literal string
345
+ * value, folding `+` concatenation and following import chains via
346
+ * {@link resolveKotlinImport}, or null when it cannot be fully folded.
347
+ *
348
+ * `name` may be simple (`ORDERS`, resolved via a single-name import or a
349
+ * same-file constant) or qualified (`ApiPaths.ORDERS`, resolved via the type
350
+ * import plus the target file's qualified alias).
351
+ */
352
+ export declare function resolveKotlinConstant(fileKey: string, name: string, repo: RepoConstants, depth?: number, index?: KotlinConstantIndex): string | null;
353
+ /**
354
+ * Fold an inline operand list (e.g. `ApiPaths.BASE + "/orders"`) against
355
+ * `fileKey`, or null when any piece is unresolvable (skip floor).
356
+ *
357
+ * `enclosingTypes` is the chain of type declarations the REFERENCE sits inside
358
+ * (innermost first), and it is applied to the entry operands only — everything
359
+ * deeper is either already qualified by
360
+ * {@link extractKotlinModuleConstants} against its own declaring scope, or lives
361
+ * in another file where this chain means nothing. Passing it empty answers
362
+ * "what does this name mean at file level", which is the right question for a
363
+ * reference outside any type and the only one a caller without position
364
+ * information can honestly ask.
365
+ *
366
+ * An empty result is a SUCCESS, not a skip. `const val ROOT = ""` folds to `""`,
367
+ * which `joinPath` then resolves against the class-level prefix exactly as it
368
+ * resolves the literal `@GetMapping("")` — both mean "the prefix itself", the
369
+ * Spring idiom for a collection root. Collapsing it into `null` would make a
370
+ * resolved-empty path indistinguishable from an unresolvable one — the skip
371
+ * floor is reserved for "could not fold", and nothing else in the resolver
372
+ * conflates the two: {@link resolveKotlinConstant} returns `''` for an empty
373
+ * constant, and `resolveOperands` in the shared core returns its fold
374
+ * unfiltered. Matches `foldJavaOperands`, so the two JVM bindings do not
375
+ * diverge on the same input.
376
+ */
377
+ export declare function foldKotlinOperands(fileKey: string, operands: readonly Operand[], repo: RepoConstants, enclosingTypes?: readonly string[], index?: KotlinConstantIndex): string | null;