@wrongstack/tools 1.0.10 → 1.0.12

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 (80) hide show
  1. package/dist/builtin.js +17 -17
  2. package/dist/{chunk-6I6RXQFG.js → chunk-2BCRQAIW.js} +9 -9
  3. package/dist/{chunk-XRNN44QY.js → chunk-2IRHFHZY.js} +235 -71
  4. package/dist/{chunk-5RBUYFRV.js → chunk-4IMNVQRB.js} +8 -2
  5. package/dist/{chunk-NEYUPZL6.js → chunk-6D3UIQMF.js} +651 -303
  6. package/dist/chunk-BJ5LXLKE.js +351 -0
  7. package/dist/{chunk-C6ZOXNXV.js → chunk-CSS42GRP.js} +2 -2
  8. package/dist/{chunk-SKRMUHTU.js → chunk-HSLDY55L.js} +41 -8
  9. package/dist/{chunk-ENE22I5W.js → chunk-K37AKCTN.js} +884 -362
  10. package/dist/{chunk-V25Q6TQ7.js → chunk-K3VX6V5P.js} +3 -3
  11. package/dist/chunk-OIFY4XNP.js +855 -0
  12. package/dist/{chunk-OUERRFEM.js → chunk-OMQDKZOT.js} +17 -8
  13. package/dist/{chunk-7GOE2ZTZ.js → chunk-OMWC2OGT.js} +2 -2
  14. package/dist/{chunk-IE4KDAOB.js → chunk-OYQE2UNT.js} +3 -3
  15. package/dist/chunk-R3HR3QI3.js +97 -0
  16. package/dist/{chunk-XPVYZ2S6.js → chunk-RYLMSGR5.js} +2 -2
  17. package/dist/{chunk-HYHE4VNA.js → chunk-UMOP3VGK.js} +79 -45
  18. package/dist/{chunk-FVSMTEO3.js → chunk-VMNWQI4V.js} +2 -2
  19. package/dist/{chunk-2UVAIS5O.js → chunk-YPSVPCIE.js} +2 -2
  20. package/dist/{chunk-PWZUHZP7.js → chunk-Z77XMU3U.js} +2 -2
  21. package/dist/codebase-index/ast-invariant-engine.d.ts +19 -2
  22. package/dist/codebase-index/atlas-projection.d.ts +19 -0
  23. package/dist/codebase-index/background-indexer.d.ts +14 -0
  24. package/dist/codebase-index/circuit-breaker.d.ts +7 -0
  25. package/dist/codebase-index/codebase-ast-replace-tool.d.ts +1 -1
  26. package/dist/codebase-index/codebase-impact-analysis-tool.d.ts +6 -0
  27. package/dist/codebase-index/codebase-invariant-check-tool.d.ts +2 -0
  28. package/dist/codebase-index/codebase-targeted-test-tool.d.ts +26 -0
  29. package/dist/codebase-index/dead-code-scan.d.ts +4 -0
  30. package/dist/codebase-index/go-parser.d.ts +23 -3
  31. package/dist/codebase-index/graph-rank-pass.d.ts +10 -0
  32. package/dist/codebase-index/graph-rank.d.ts +8 -0
  33. package/dist/codebase-index/index.d.ts +1 -1
  34. package/dist/codebase-index/index.js +9 -9
  35. package/dist/codebase-index/lsp-kind.d.ts +3 -1
  36. package/dist/codebase-index/module-resolver.d.ts +21 -1
  37. package/dist/codebase-index/parser-batch.d.ts +6 -8
  38. package/dist/codebase-index/parser-output.d.ts +5 -1
  39. package/dist/codebase-index/parser-worker-script.js +4 -4
  40. package/dist/codebase-index/project-server-framing.d.ts +1 -17
  41. package/dist/codebase-index/project-server-watcher.d.ts +6 -0
  42. package/dist/codebase-index/project-server.js +72 -26
  43. package/dist/codebase-index/py-parser.d.ts +4 -4
  44. package/dist/codebase-index/rs-parser.d.ts +7 -0
  45. package/dist/codebase-index/toolchain-scripts.d.ts +64 -0
  46. package/dist/codebase-index/tree-sitter/queries.d.ts +14 -0
  47. package/dist/codebase-index/tree-sitter/util.d.ts +6 -1
  48. package/dist/codebase-index/tree-sitter-parser.d.ts +7 -0
  49. package/dist/codebase-index/worker.js +23 -16
  50. package/dist/codebase-index/writer-refs.d.ts +17 -2
  51. package/dist/codebase-index/writer-search-helpers.d.ts +9 -0
  52. package/dist/codebase-index/writer.d.ts +20 -1
  53. package/dist/edit.js +8 -8
  54. package/dist/{generic-parser-N2PLGBC2.js → generic-parser-IVWLFKMR.js} +2 -2
  55. package/dist/go-parser-LWV3L5V7.js +197 -0
  56. package/dist/index.js +19 -19
  57. package/dist/json-parser-PERV2JHX.js +204 -0
  58. package/dist/pack.js +18 -18
  59. package/dist/parser-dispatch-ZSWCQB6M.js +14 -0
  60. package/dist/patch.js +8 -8
  61. package/dist/{py-parser-4PFZYAJ7.js → py-parser-B6D6RGH7.js} +4 -4
  62. package/dist/read.js +8 -8
  63. package/dist/replace.js +8 -8
  64. package/dist/rs-parser-L7SP4GWJ.js +182 -0
  65. package/dist/test.d.ts +1 -0
  66. package/dist/test.js +1 -1
  67. package/dist/tool-tier.js +19 -19
  68. package/dist/{tree-sitter-parser-GILYQLS4.js → tree-sitter-parser-KU6JF3ZC.js} +6 -4
  69. package/dist/{ts-parser-7W26YB26.js → ts-parser-PML6QIH7.js} +55 -23
  70. package/dist/typecheck.js +1 -1
  71. package/dist/write.js +8 -8
  72. package/dist/{yaml-parser-DFBC5FWH.js → yaml-parser-7BJN544B.js} +41 -6
  73. package/package.json +5 -5
  74. package/dist/chunk-4CE3RKEP.js +0 -390
  75. package/dist/chunk-KHUCKLOR.js +0 -899
  76. package/dist/chunk-ZKC5UNGD.js +0 -167
  77. package/dist/go-parser-Z4H6SR3R.js +0 -474
  78. package/dist/json-parser-IJD77VL4.js +0 -256
  79. package/dist/parser-dispatch-WG4DNBHH.js +0 -14
  80. package/dist/rs-parser-GIWPUA3T.js +0 -81
@@ -0,0 +1,855 @@
1
+ // src/codebase-index/parser-output.ts
2
+ function toIndexSymbols(raw, file, lang) {
3
+ return raw.map((s) => {
4
+ const signature = s.signature.slice(0, 500);
5
+ return {
6
+ id: 0,
7
+ lang,
8
+ kind: s.kind,
9
+ name: s.name,
10
+ file,
11
+ line: s.line,
12
+ col: s.col,
13
+ signature,
14
+ docComment: s.doc,
15
+ scope: s.scope,
16
+ text: [s.name, signature, s.doc].filter(Boolean).join(" ")
17
+ };
18
+ });
19
+ }
20
+ var CALL_TYPES = /* @__PURE__ */ new Set([
21
+ "call",
22
+ "type_ref",
23
+ "inherit",
24
+ "implement",
25
+ "import"
26
+ ]);
27
+ function coerceSymbols(value) {
28
+ if (!Array.isArray(value)) return [];
29
+ return value.flatMap((entry) => {
30
+ const candidate = entry;
31
+ if (typeof candidate.name !== "string" || typeof candidate.kind !== "string") return [];
32
+ return [
33
+ {
34
+ name: candidate.name,
35
+ kind: candidate.kind,
36
+ line: typeof candidate.line === "number" ? candidate.line : 1,
37
+ col: typeof candidate.col === "number" ? candidate.col : 0,
38
+ signature: typeof candidate.signature === "string" ? candidate.signature : "",
39
+ scope: typeof candidate.scope === "string" ? candidate.scope : "",
40
+ doc: typeof candidate.doc === "string" ? candidate.doc : ""
41
+ }
42
+ ];
43
+ });
44
+ }
45
+ function coerceRefs(value, lang) {
46
+ if (!Array.isArray(value)) return [];
47
+ return value.flatMap((entry) => {
48
+ const candidate = entry;
49
+ if (typeof candidate.toName !== "string" || !candidate.toName) return [];
50
+ if (typeof candidate.callType !== "string" || !CALL_TYPES.has(candidate.callType)) return [];
51
+ const module = typeof candidate.module === "string" && candidate.module ? candidate.module : void 0;
52
+ return [
53
+ {
54
+ fromId: 0,
55
+ toName: candidate.toName,
56
+ callType: candidate.callType,
57
+ line: typeof candidate.line === "number" ? candidate.line : 1,
58
+ lang,
59
+ module
60
+ }
61
+ ];
62
+ });
63
+ }
64
+ function parseParserOutput(stdout, lang) {
65
+ const trimmed = stdout.trim();
66
+ if (!trimmed) return { symbols: [], refs: [] };
67
+ let parsed;
68
+ try {
69
+ parsed = JSON.parse(trimmed);
70
+ } catch {
71
+ return { symbols: [], refs: [] };
72
+ }
73
+ if (Array.isArray(parsed)) return { symbols: coerceSymbols(parsed), refs: [] };
74
+ const record = parsed;
75
+ return {
76
+ symbols: coerceSymbols(record.symbols),
77
+ refs: dedupeRefs(coerceRefs(record.refs, lang))
78
+ };
79
+ }
80
+ function parseParserBatchOutput(stdout, lang) {
81
+ const trimmed = stdout.trim();
82
+ if (!trimmed) return [];
83
+ let parsed;
84
+ try {
85
+ parsed = JSON.parse(trimmed);
86
+ } catch {
87
+ return [];
88
+ }
89
+ if (!parsed || typeof parsed !== "object") return [];
90
+ const results = parsed.results;
91
+ if (!Array.isArray(results)) return [];
92
+ return results.flatMap((entry) => {
93
+ if (typeof entry !== "object" || entry === null || Array.isArray(entry)) return [];
94
+ const candidate = entry;
95
+ if (typeof candidate.file !== "string" || !candidate.file) return [];
96
+ return [
97
+ {
98
+ file: candidate.file,
99
+ error: typeof candidate.error === "string" && candidate.error ? candidate.error : void 0,
100
+ symbols: coerceSymbols(candidate.symbols),
101
+ refs: dedupeRefs(coerceRefs(candidate.refs, lang))
102
+ }
103
+ ];
104
+ });
105
+ }
106
+ function dedupeRefs(refs) {
107
+ const seen = /* @__PURE__ */ new Set();
108
+ return refs.filter((ref) => {
109
+ const key = `${ref.toName}:${ref.callType}:${ref.line}:${ref.module ?? ""}`;
110
+ if (seen.has(key)) return false;
111
+ seen.add(key);
112
+ return true;
113
+ });
114
+ }
115
+
116
+ // src/codebase-index/perf-metrics.ts
117
+ var counters = {
118
+ filesystemBytesRead: 0,
119
+ filesystemReadCount: 0,
120
+ parserGateWaitMs: 0,
121
+ parserGateCalls: 0,
122
+ parserSubprocessCount: 0,
123
+ ipcPendingPeak: 0,
124
+ writeQueueWaitMs: 0,
125
+ writeQueueCalls: 0
126
+ };
127
+ function resetCodebaseIndexPerfMetrics() {
128
+ for (const key of Object.keys(counters)) {
129
+ counters[key] = 0;
130
+ }
131
+ }
132
+ function getCodebaseIndexPerfSnapshot() {
133
+ return { ...counters };
134
+ }
135
+ function recordFilesystemRead(bytes) {
136
+ counters.filesystemBytesRead += Math.max(0, bytes);
137
+ counters.filesystemReadCount++;
138
+ }
139
+ function recordParserGateWait(waitMs) {
140
+ counters.parserGateWaitMs += Math.max(0, waitMs);
141
+ counters.parserGateCalls++;
142
+ }
143
+ function recordParserSubprocess() {
144
+ counters.parserSubprocessCount++;
145
+ }
146
+ function recordIpcPending(depth) {
147
+ counters.ipcPendingPeak = Math.max(counters.ipcPendingPeak, depth);
148
+ }
149
+ function recordWriteQueueWait(waitMs) {
150
+ counters.writeQueueWaitMs += Math.max(0, waitMs);
151
+ counters.writeQueueCalls++;
152
+ }
153
+
154
+ // src/codebase-index/spawn-gate.ts
155
+ var chain = Promise.resolve();
156
+ function withSpawnGate(fn) {
157
+ const queuedAt = performance.now();
158
+ const run = chain.then(
159
+ () => {
160
+ recordParserGateWait(performance.now() - queuedAt);
161
+ return fn();
162
+ },
163
+ () => {
164
+ recordParserGateWait(performance.now() - queuedAt);
165
+ return fn();
166
+ }
167
+ );
168
+ chain = run.then(
169
+ () => void 0,
170
+ () => void 0
171
+ );
172
+ return run;
173
+ }
174
+
175
+ // src/codebase-index/toolchain-scripts.ts
176
+ import { spawn } from "node:child_process";
177
+ import * as fsSync from "node:fs";
178
+ import * as fs from "node:fs/promises";
179
+ import * as os from "node:os";
180
+ import * as path from "node:path";
181
+ import { StringDecoder } from "node:string_decoder";
182
+ var GO_CORE = `
183
+ package main
184
+
185
+ import (
186
+ "encoding/json"
187
+ "fmt"
188
+ "go/ast"
189
+ "go/parser"
190
+ "go/token"
191
+ "io"
192
+ "os"
193
+ "strconv"
194
+ "strings"
195
+ )
196
+
197
+ type Sym struct {
198
+ Name string \`json:"name"\`
199
+ Kind string \`json:"kind"\`
200
+ Line int \`json:"line"\`
201
+ Col int \`json:"col"\`
202
+ Signature string \`json:"signature"\`
203
+ Scope string \`json:"scope"\`
204
+ Doc string \`json:"doc"\`
205
+ }
206
+
207
+ // Ref is a cross-reference emitted alongside the symbols, so one run yields
208
+ // both. Module is the import path for CallType "import", else empty.
209
+ type Ref struct {
210
+ ToName string \`json:"toName"\`
211
+ CallType string \`json:"callType"\`
212
+ Line int \`json:"line"\`
213
+ Module string \`json:"module"\`
214
+ }
215
+
216
+ func extract(src []byte) ([]Sym, []Ref, error) {
217
+ fset := token.NewFileSet()
218
+ node, err := parser.ParseFile(fset, "src.go", src, parser.ParseComments)
219
+ if err != nil {
220
+ return nil, nil, err
221
+ }
222
+ pkg := node.Name.Name
223
+ // token columns are 1-based; the index schema's are 0-based.
224
+ at := func(p token.Pos) (int, int) {
225
+ pos := fset.Position(p)
226
+ return pos.Line, pos.Column - 1
227
+ }
228
+
229
+ syms := []Sym{}
230
+ for _, decl := range node.Decls {
231
+ switch d := decl.(type) {
232
+ case *ast.FuncDecl:
233
+ line, col := at(d.Pos())
234
+ kind := "function"
235
+ scope := pkg + "." + d.Name.Name
236
+ if d.Recv != nil && len(d.Recv.List) > 0 {
237
+ kind = "method"
238
+ scope = pkg + "." + recvTypeName(d.Recv.List[0].Type) + "." + d.Name.Name
239
+ }
240
+ syms = append(syms, Sym{Name: d.Name.Name, Kind: kind, Line: line, Col: col, Signature: formatFuncSig(d), Scope: scope, Doc: firstDocLine(d.Doc)})
241
+
242
+ case *ast.GenDecl:
243
+ // An ungrouped declaration carries its comment on the GenDecl; a
244
+ // grouped one's doc describes the group, not each member.
245
+ var declDoc *ast.CommentGroup
246
+ if !d.Lparen.IsValid() {
247
+ declDoc = d.Doc
248
+ }
249
+ for _, spec := range d.Specs {
250
+ switch s := spec.(type) {
251
+ case *ast.TypeSpec:
252
+ line, col := at(s.Pos())
253
+ sig := "type " + s.Name.Name + formatTypeParams(s.TypeParams)
254
+ if s.Assign.IsValid() {
255
+ sig += " ="
256
+ }
257
+ sig += " " + formatType(s.Type)
258
+ syms = append(syms, Sym{Name: s.Name.Name, Kind: "type", Line: line, Col: col, Signature: sig, Scope: pkg, Doc: firstDocLine(s.Doc, declDoc)})
259
+ case *ast.ValueSpec:
260
+ kind := "var"
261
+ if d.Tok == token.CONST {
262
+ kind = "const"
263
+ }
264
+ for _, n := range s.Names {
265
+ line, col := at(n.Pos())
266
+ sig := kind + " " + n.Name
267
+ if s.Type != nil {
268
+ sig += " " + formatType(s.Type)
269
+ }
270
+ syms = append(syms, Sym{Name: n.Name, Kind: kind, Line: line, Col: col, Signature: sig, Scope: pkg, Doc: firstDocLine(s.Doc, declDoc)})
271
+ }
272
+ }
273
+ }
274
+ }
275
+ }
276
+
277
+ refs := []Ref{}
278
+ ast.Inspect(node, func(n ast.Node) bool {
279
+ switch expr := n.(type) {
280
+ case *ast.CallExpr:
281
+ line := fset.Position(expr.Pos()).Line
282
+ switch fun := expr.Fun.(type) {
283
+ case *ast.Ident:
284
+ refs = append(refs, Ref{ToName: fun.Name, CallType: "call", Line: line})
285
+ case *ast.SelectorExpr:
286
+ // The selected name (\`Join\` of \`filepath.Join\`) is the declared
287
+ // symbol name, so it resolves like the TS and Python call refs.
288
+ refs = append(refs, Ref{ToName: fun.Sel.Name, CallType: "call", Line: line})
289
+ }
290
+ case *ast.ImportSpec:
291
+ if expr.Path != nil {
292
+ if importPath, uerr := strconv.Unquote(expr.Path.Value); uerr == nil {
293
+ line := fset.Position(expr.Pos()).Line
294
+ // A Go import names a package; its last path segment is the
295
+ // name it is referenced by.
296
+ name := importPath
297
+ if idx := strings.LastIndex(importPath, "/"); idx >= 0 {
298
+ name = importPath[idx+1:]
299
+ }
300
+ refs = append(refs, Ref{ToName: name, CallType: "import", Line: line, Module: importPath})
301
+ }
302
+ }
303
+ }
304
+ return true
305
+ })
306
+ return syms, refs, nil
307
+ }
308
+
309
+ func firstDocLine(groups ...*ast.CommentGroup) string {
310
+ for _, g := range groups {
311
+ if g == nil {
312
+ continue
313
+ }
314
+ for _, line := range strings.Split(g.Text(), "\\n") {
315
+ if t := strings.TrimSpace(line); t != "" {
316
+ if r := []rune(t); len(r) > 200 {
317
+ t = string(r[:200])
318
+ }
319
+ return t
320
+ }
321
+ }
322
+ }
323
+ return ""
324
+ }
325
+
326
+ // recvTypeName unwraps pointers, parens and generic instantiation:
327
+ // \`(l *List[T])\` is a method of List, not of "?".
328
+ func recvTypeName(t ast.Expr) string {
329
+ switch v := t.(type) {
330
+ case *ast.Ident:
331
+ return v.Name
332
+ case *ast.StarExpr:
333
+ return recvTypeName(v.X)
334
+ case *ast.ParenExpr:
335
+ return recvTypeName(v.X)
336
+ case *ast.IndexExpr:
337
+ return recvTypeName(v.X)
338
+ case *ast.IndexListExpr:
339
+ return recvTypeName(v.X)
340
+ default:
341
+ return "?"
342
+ }
343
+ }
344
+
345
+ func formatFuncSig(d *ast.FuncDecl) string {
346
+ sig := "func "
347
+ if d.Recv != nil && len(d.Recv.List) > 0 {
348
+ sig += formatFieldList(d.Recv.List) + " "
349
+ }
350
+ return sig + d.Name.Name + formatTypeParams(d.Type.TypeParams) + formatFuncType(d.Type)
351
+ }
352
+
353
+ func fieldsOf(fl *ast.FieldList) []*ast.Field {
354
+ if fl == nil {
355
+ return nil
356
+ }
357
+ return fl.List
358
+ }
359
+
360
+ func formatFuncType(f *ast.FuncType) string {
361
+ out := formatFieldList(fieldsOf(f.Params))
362
+ results := fieldsOf(f.Results)
363
+ if len(results) == 1 && len(results[0].Names) == 0 {
364
+ out += " " + formatType(results[0].Type)
365
+ } else if len(results) > 0 {
366
+ out += " " + formatFieldList(results)
367
+ }
368
+ return out
369
+ }
370
+
371
+ // formatField keeps every name of a shared-type field: \`a, b int\`.
372
+ func formatField(f *ast.Field) string {
373
+ t := formatType(f.Type)
374
+ if len(f.Names) == 0 {
375
+ return t
376
+ }
377
+ names := make([]string, len(f.Names))
378
+ for i, n := range f.Names {
379
+ names[i] = n.Name
380
+ }
381
+ return strings.Join(names, ", ") + " " + t
382
+ }
383
+
384
+ func formatFieldList(fields []*ast.Field) string {
385
+ parts := make([]string, len(fields))
386
+ for i, f := range fields {
387
+ parts[i] = formatField(f)
388
+ }
389
+ return "(" + strings.Join(parts, ", ") + ")"
390
+ }
391
+
392
+ // formatFields renders struct fields and interface members.
393
+ func formatFields(fields []*ast.Field) string {
394
+ parts := make([]string, len(fields))
395
+ for i, f := range fields {
396
+ if ft, ok := f.Type.(*ast.FuncType); ok && len(f.Names) == 1 {
397
+ parts[i] = f.Names[0].Name + formatFuncType(ft)
398
+ } else {
399
+ parts[i] = formatField(f)
400
+ }
401
+ }
402
+ return strings.Join(parts, "; ")
403
+ }
404
+
405
+ func formatTypeParams(tp *ast.FieldList) string {
406
+ if tp == nil || len(tp.List) == 0 {
407
+ return ""
408
+ }
409
+ parts := make([]string, len(tp.List))
410
+ for i, p := range tp.List {
411
+ parts[i] = formatField(p)
412
+ }
413
+ return "[" + strings.Join(parts, ", ") + "]"
414
+ }
415
+
416
+ func formatType(t ast.Expr) string {
417
+ if t == nil {
418
+ return "?"
419
+ }
420
+ switch v := t.(type) {
421
+ case *ast.Ident:
422
+ return v.Name
423
+ case *ast.SelectorExpr:
424
+ return formatType(v.X) + "." + v.Sel.Name
425
+ case *ast.StarExpr:
426
+ return "*" + formatType(v.X)
427
+ case *ast.ParenExpr:
428
+ return "(" + formatType(v.X) + ")"
429
+ case *ast.Ellipsis:
430
+ if v.Elt == nil {
431
+ return "..."
432
+ }
433
+ return "..." + formatType(v.Elt)
434
+ case *ast.ArrayType:
435
+ if v.Len == nil {
436
+ return "[]" + formatType(v.Elt)
437
+ }
438
+ return "[" + formatType(v.Len) + "]" + formatType(v.Elt)
439
+ case *ast.MapType:
440
+ return "map[" + formatType(v.Key) + "]" + formatType(v.Value)
441
+ case *ast.ChanType:
442
+ switch v.Dir {
443
+ case ast.SEND:
444
+ return "chan<- " + formatType(v.Value)
445
+ case ast.RECV:
446
+ return "<-chan " + formatType(v.Value)
447
+ default:
448
+ return "chan " + formatType(v.Value)
449
+ }
450
+ case *ast.InterfaceType:
451
+ if len(fieldsOf(v.Methods)) == 0 {
452
+ return "interface{}"
453
+ }
454
+ return "interface{ " + formatFields(v.Methods.List) + " }"
455
+ case *ast.StructType:
456
+ if len(fieldsOf(v.Fields)) == 0 {
457
+ return "struct{}"
458
+ }
459
+ return "struct{ " + formatFields(v.Fields.List) + " }"
460
+ case *ast.FuncType:
461
+ return "func" + formatFuncType(v)
462
+ case *ast.UnaryExpr:
463
+ return v.Op.String() + formatType(v.X)
464
+ case *ast.BinaryExpr:
465
+ return formatType(v.X) + " " + v.Op.String() + " " + formatType(v.Y)
466
+ case *ast.BasicLit:
467
+ return v.Value
468
+ case *ast.IndexExpr:
469
+ return formatType(v.X) + "[" + formatType(v.Index) + "]"
470
+ case *ast.IndexListExpr:
471
+ args := make([]string, len(v.Indices))
472
+ for i, idx := range v.Indices {
473
+ args[i] = formatType(idx)
474
+ }
475
+ return formatType(v.X) + "[" + strings.Join(args, ", ") + "]"
476
+ default:
477
+ return "?"
478
+ }
479
+ }
480
+ `;
481
+ var GO_PARSE_SCRIPT = `${GO_CORE}
482
+ type Result struct {
483
+ Symbols []Sym \`json:"symbols"\`
484
+ Refs []Ref \`json:"refs"\`
485
+ }
486
+
487
+ func main() {
488
+ empty := \`{"symbols":[],"refs":[]}\`
489
+ src, err := io.ReadAll(os.Stdin)
490
+ if err != nil {
491
+ fmt.Print(empty)
492
+ return
493
+ }
494
+ syms, refs, err := extract(src)
495
+ if err != nil {
496
+ fmt.Print(empty)
497
+ return
498
+ }
499
+ data, err := json.Marshal(Result{Symbols: syms, Refs: refs})
500
+ if err != nil {
501
+ fmt.Print(empty)
502
+ return
503
+ }
504
+ fmt.Print(string(data))
505
+ }
506
+ `;
507
+ var GO_BATCH_SCRIPT = `${GO_CORE}
508
+ type FileResult struct {
509
+ File string \`json:"file"\`
510
+ Error string \`json:"error,omitempty"\`
511
+ Symbols []Sym \`json:"symbols"\`
512
+ Refs []Ref \`json:"refs"\`
513
+ }
514
+
515
+ type BatchResult struct {
516
+ Version int \`json:"version"\`
517
+ Results []FileResult \`json:"results"\`
518
+ }
519
+
520
+ type inputFile struct {
521
+ File string \`json:"file"\`
522
+ Content string \`json:"content"\`
523
+ }
524
+
525
+ func main() {
526
+ var inputs []inputFile
527
+ raw, err := io.ReadAll(os.Stdin)
528
+ if err == nil {
529
+ err = json.Unmarshal(raw, &inputs)
530
+ }
531
+ results := make([]FileResult, 0, len(inputs))
532
+ if err == nil {
533
+ for _, in := range inputs {
534
+ res := FileResult{File: in.File, Symbols: []Sym{}, Refs: []Ref{}}
535
+ if syms, refs, perr := extract([]byte(in.Content)); perr != nil {
536
+ res.Error = perr.Error()
537
+ } else {
538
+ res.Symbols, res.Refs = syms, refs
539
+ }
540
+ results = append(results, res)
541
+ }
542
+ }
543
+ data, merr := json.Marshal(BatchResult{Version: 1, Results: results})
544
+ if merr != nil {
545
+ fmt.Print(\`{"version":1,"results":[]}\`)
546
+ return
547
+ }
548
+ fmt.Print(string(data))
549
+ }
550
+ `;
551
+ var PY_CORE = `import ast, json, sys
552
+
553
+ # Decorators whose function is an attribute accessor, by last dotted segment.
554
+ PROPERTY_DECORATORS = ("property", "cached_property", "abstractproperty", "setter", "getter", "deleter")
555
+
556
+ def get_name(node):
557
+ if isinstance(node, ast.Name):
558
+ return node.id
559
+ if isinstance(node, ast.Attribute):
560
+ return get_name(node.value) + "." + node.attr
561
+ if isinstance(node, ast.Subscript):
562
+ return get_name(node.value)
563
+ if isinstance(node, ast.Call):
564
+ return get_name(node.func)
565
+ if isinstance(node, ast.Constant):
566
+ return str(node.value)
567
+ return ""
568
+
569
+ def leaf_name(node):
570
+ # Declared name of the callee: \`join\` of \`os.path.join\`. Matches how the
571
+ # TypeScript and Go extractors record call refs.
572
+ if isinstance(node, ast.Attribute):
573
+ return node.attr
574
+ if isinstance(node, ast.Name):
575
+ return node.id
576
+ return get_name(node).split(".")[-1]
577
+
578
+ def is_private(name):
579
+ return name.startswith("__") and not name.endswith("__")
580
+
581
+ def first_doc_line(node):
582
+ try:
583
+ doc = ast.get_docstring(node)
584
+ except Exception:
585
+ return ""
586
+ for line in (doc or "").splitlines():
587
+ line = line.strip()
588
+ if line:
589
+ return line[:200]
590
+ return ""
591
+
592
+ def module_name_of(path):
593
+ base = path.replace("\\\\", "/").rsplit("/", 1)[-1]
594
+ return base.rsplit(".", 1)[0] if "." in base else base
595
+
596
+ def read_stdin_text():
597
+ # Decode explicitly: sys.stdin uses the locale code page on Windows
598
+ # (cp1254, cp1252, ...), which mangles or rejects the UTF-8 we are sent.
599
+ return sys.stdin.buffer.read().decode("utf-8", "replace")
600
+
601
+ def parse_source(text, name):
602
+ # ast.parse rejects a leading U+FEFF in a str source.
603
+ if text.startswith("\\ufeff"):
604
+ text = text[1:]
605
+ return ast.parse(text, filename=name or "<module>")
606
+
607
+ def format_args(args):
608
+ parts = [a.arg for a in list(getattr(args, "posonlyargs", [])) + list(args.args)]
609
+ if args.vararg is not None:
610
+ parts.append("*" + args.vararg.arg)
611
+ elif args.kwonlyargs:
612
+ parts.append("*")
613
+ parts.extend(a.arg for a in args.kwonlyargs)
614
+ if args.kwarg is not None:
615
+ parts.append("**" + args.kwarg.arg)
616
+ return ", ".join(parts)
617
+
618
+ def extract(tree, module_name):
619
+ syms = []
620
+ refs = []
621
+ scope = [module_name]
622
+
623
+ def add(name, kind, line, col, signature, sym_scope, doc=""):
624
+ syms.append({
625
+ "name": name, "kind": kind, "line": line, "col": col,
626
+ "signature": signature, "scope": sym_scope, "doc": doc,
627
+ })
628
+
629
+ def qualified(name):
630
+ return ".".join(scope) + "." + name
631
+
632
+ # Kinds are the index schema's (class/function/method/property/var/const/
633
+ # type). Imports are refs, not symbols: an import symbol per file made
634
+ # every imported name look like a definition in every importing module.
635
+ class Visitor(ast.NodeVisitor):
636
+ in_class = False
637
+
638
+ def visit_ClassDef(self, node):
639
+ bases = [get_name(b) for b in node.bases]
640
+ sig = "class " + node.name + ("(" + ", ".join(bases) + ")" if bases else "") + ": ..."
641
+ add(node.name, "class", node.lineno, node.col_offset, sig, qualified(node.name), first_doc_line(node))
642
+ scope.append(node.name)
643
+ outer = self.in_class
644
+ self.in_class = True
645
+ try:
646
+ self.generic_visit(node)
647
+ finally:
648
+ self.in_class = outer
649
+ scope.pop()
650
+
651
+ def visit_FunctionDef(self, node):
652
+ kind = "method" if self.in_class else "function"
653
+ for dec in node.decorator_list:
654
+ if get_name(dec).rsplit(".", 1)[-1] in PROPERTY_DECORATORS:
655
+ kind = "property"
656
+ prefix = "async def " if isinstance(node, ast.AsyncFunctionDef) else "def "
657
+ sig = prefix + node.name + "(" + format_args(node.args) + ")"
658
+ returns = get_name(node.returns) if node.returns is not None else ""
659
+ if returns:
660
+ sig += " -> " + returns
661
+ add(node.name, kind, node.lineno, node.col_offset, sig, qualified(node.name), first_doc_line(node))
662
+ # Bodies are not visited: function locals are not index symbols.
663
+
664
+ visit_AsyncFunctionDef = visit_FunctionDef
665
+
666
+ def add_target(self, target, line, annotation=None, has_value=True):
667
+ if isinstance(target, ast.Name):
668
+ if is_private(target.id):
669
+ return
670
+ kind = "const" if target.id.isupper() else "var"
671
+ if annotation is not None:
672
+ sig = target.id + ": " + get_name(annotation) + (" = ..." if has_value else "")
673
+ else:
674
+ sig = target.id + " = ..."
675
+ add(target.id, kind, line, target.col_offset, sig, ".".join(scope))
676
+ elif isinstance(target, (ast.Tuple, ast.List)):
677
+ for elt in target.elts:
678
+ self.add_target(elt, line)
679
+ elif isinstance(target, ast.Starred):
680
+ self.add_target(target.value, line)
681
+
682
+ def visit_Assign(self, node):
683
+ for target in node.targets:
684
+ self.add_target(target, node.lineno)
685
+
686
+ def visit_AnnAssign(self, node):
687
+ self.add_target(node.target, node.lineno, node.annotation, node.value is not None)
688
+
689
+ def visit_TypeAlias(self, node):
690
+ if isinstance(node.name, ast.Name):
691
+ add(node.name.id, "type", node.lineno, node.col_offset, "type " + node.name.id + " = ...", ".".join(scope))
692
+
693
+ Visitor().visit(tree)
694
+
695
+ # Refs need a full walk: the visitor deliberately skips function bodies,
696
+ # which is exactly where the calls are.
697
+ for node in ast.walk(tree):
698
+ if isinstance(node, ast.Call):
699
+ name = leaf_name(node.func)
700
+ if name:
701
+ refs.append({"toName": name, "callType": "call", "line": node.lineno})
702
+ elif isinstance(node, ast.Import):
703
+ for alias in node.names:
704
+ refs.append({
705
+ "toName": alias.name.split(".")[-1], "callType": "import",
706
+ "line": node.lineno, "module": alias.name,
707
+ })
708
+ elif isinstance(node, ast.ImportFrom):
709
+ # PEP 328: keep the leading dots so the resolver can walk up from
710
+ # the importing file's package.
711
+ module = ("." * (node.level or 0)) + (node.module or "")
712
+ for alias in node.names:
713
+ refs.append({
714
+ "toName": alias.name, "callType": "import",
715
+ "line": node.lineno, "module": module,
716
+ })
717
+ elif isinstance(node, ast.ClassDef):
718
+ for base in node.bases:
719
+ name = leaf_name(base)
720
+ if name:
721
+ refs.append({"toName": name, "callType": "inherit", "line": node.lineno})
722
+
723
+ return syms, refs
724
+ `;
725
+ var PY_PARSE_SCRIPT = `${PY_CORE}
726
+ def main():
727
+ name = sys.argv[1] if len(sys.argv) > 1 else "module.py"
728
+ try:
729
+ syms, refs = extract(parse_source(read_stdin_text(), name), module_name_of(name))
730
+ except Exception:
731
+ syms, refs = [], []
732
+ print(json.dumps({"symbols": syms, "refs": refs}))
733
+
734
+ main()
735
+ `;
736
+ var PY_BATCH_SCRIPT = `${PY_CORE}
737
+ def main():
738
+ try:
739
+ inputs = json.loads(read_stdin_text())
740
+ except Exception:
741
+ print(json.dumps({"version": 1, "results": []}))
742
+ return
743
+ results = []
744
+ for entry in inputs:
745
+ name = entry.get("file", "")
746
+ result = {"file": name, "symbols": [], "refs": []}
747
+ try:
748
+ syms, refs = extract(parse_source(entry.get("content", ""), name), module_name_of(name))
749
+ result["symbols"] = syms
750
+ result["refs"] = refs
751
+ except Exception as e:
752
+ result["error"] = str(e) or type(e).__name__
753
+ results.append(result)
754
+ print(json.dumps({"version": 1, "results": results}))
755
+
756
+ main()
757
+ `;
758
+ var scriptPaths = /* @__PURE__ */ new Map();
759
+ function privateScriptPath(prefix, fileName, script) {
760
+ const key = `${prefix}\0${fileName}`;
761
+ const cached = scriptPaths.get(key);
762
+ if (cached) return cached;
763
+ const pending = writePrivateScript(prefix, fileName, script);
764
+ scriptPaths.set(key, pending);
765
+ pending.catch(() => {
766
+ if (scriptPaths.get(key) === pending) scriptPaths.delete(key);
767
+ });
768
+ return pending;
769
+ }
770
+ async function writePrivateScript(prefix, fileName, script) {
771
+ const dir = await fs.mkdtemp(path.join(os.tmpdir(), prefix));
772
+ const scriptPath = path.join(dir, fileName);
773
+ await fs.writeFile(scriptPath, script, { encoding: "utf8", flag: "wx" });
774
+ process.once("exit", () => {
775
+ try {
776
+ fsSync.rmSync(dir, { recursive: true, force: true });
777
+ } catch {
778
+ }
779
+ });
780
+ return scriptPath;
781
+ }
782
+ function goSpawnOptions(scriptPath) {
783
+ return {
784
+ cwd: path.dirname(scriptPath),
785
+ env: { ...process.env, GOTOOLCHAIN: "local", GOWORK: "off" }
786
+ };
787
+ }
788
+ function pySpawnOptions(scriptPath) {
789
+ return { cwd: path.dirname(scriptPath) };
790
+ }
791
+ function runToolchainChild(binary, args, stdinPayload, timeoutMs, options) {
792
+ return new Promise((resolve) => {
793
+ let settled = false;
794
+ let stdout = "";
795
+ let proc;
796
+ try {
797
+ recordParserSubprocess();
798
+ proc = spawn(binary, args, {
799
+ cwd: options.cwd,
800
+ env: options.env ?? process.env,
801
+ stdio: ["pipe", "pipe", "pipe"],
802
+ windowsHide: true
803
+ });
804
+ } catch {
805
+ resolve(null);
806
+ return;
807
+ }
808
+ const finish = (value) => {
809
+ if (settled) return;
810
+ settled = true;
811
+ clearTimeout(timer);
812
+ resolve(value);
813
+ };
814
+ const timer = setTimeout(() => {
815
+ proc.kill("SIGKILL");
816
+ finish(null);
817
+ }, timeoutMs);
818
+ timer.unref?.();
819
+ proc.on("error", () => finish(null));
820
+ const stdoutDecoder = new StringDecoder("utf8");
821
+ proc.stdout?.on("data", (chunk) => {
822
+ stdout += stdoutDecoder.write(chunk);
823
+ });
824
+ proc.stderr?.resume();
825
+ proc.stdin?.on("error", () => {
826
+ });
827
+ proc.stdin?.write(stdinPayload);
828
+ proc.stdin?.end();
829
+ proc.on("close", (code) => {
830
+ stdout += stdoutDecoder.end();
831
+ finish({ code, stdout });
832
+ });
833
+ });
834
+ }
835
+
836
+ export {
837
+ toIndexSymbols,
838
+ parseParserOutput,
839
+ parseParserBatchOutput,
840
+ resetCodebaseIndexPerfMetrics,
841
+ getCodebaseIndexPerfSnapshot,
842
+ recordFilesystemRead,
843
+ recordIpcPending,
844
+ recordWriteQueueWait,
845
+ withSpawnGate,
846
+ GO_PARSE_SCRIPT,
847
+ GO_BATCH_SCRIPT,
848
+ PY_PARSE_SCRIPT,
849
+ PY_BATCH_SCRIPT,
850
+ privateScriptPath,
851
+ goSpawnOptions,
852
+ pySpawnOptions,
853
+ runToolchainChild
854
+ };
855
+ //# sourceMappingURL=chunk-OIFY4XNP.js.map