@mrclrchtr/supi-tree-sitter 4.8.0 → 4.10.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mrclrchtr/supi-tree-sitter",
3
- "version": "4.8.0",
3
+ "version": "4.10.0",
4
4
  "description": "Structural AST analysis for SuPi code intelligence",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -34,8 +34,8 @@
34
34
  ],
35
35
  "dependencies": {
36
36
  "web-tree-sitter": "^0.26.8",
37
- "@mrclrchtr/supi-code-runtime": "4.8.0",
38
- "@mrclrchtr/supi-core": "4.8.0"
37
+ "@mrclrchtr/supi-code-runtime": "4.10.0",
38
+ "@mrclrchtr/supi-core": "4.10.0"
39
39
  },
40
40
  "bundledDependencies": [
41
41
  "@mrclrchtr/supi-code-runtime",
@@ -49,6 +49,7 @@
49
49
  "./provider/tree-sitter-provider": "./src/provider/tree-sitter-provider.ts"
50
50
  },
51
51
  "scripts": {
52
+ "bench:structural": "vitest bench __tests__/benchmark/structural-baseline.bench.ts --run",
52
53
  "vendor:wasm": "node scripts/vendor-wasm.mjs",
53
54
  "check:wasm": "node scripts/vendor-wasm.mjs --check",
54
55
  "generate:kotlin-wasm": "node scripts/generate-kotlin-wasm.mjs",
@@ -6,7 +6,7 @@
6
6
  "releaseAsset": "https://github.com/fwcd/tree-sitter-kotlin/releases/download/0.3.8/tree-sitter-kotlin.wasm"
7
7
  },
8
8
  "generatedWith": {
9
- "treeSitterCli": "0.26.11"
9
+ "treeSitterCli": "0.26.12"
10
10
  },
11
11
  "sha256": "e08447e36ad5d86f39f91f13defce0636aea1490b8d65dafde6fa72649422061"
12
12
  }
@@ -5,7 +5,7 @@
5
5
  "repository": "https://github.com/derekstride/tree-sitter-sql"
6
6
  },
7
7
  "generatedWith": {
8
- "treeSitterCli": "0.26.11"
8
+ "treeSitterCli": "0.26.12"
9
9
  },
10
10
  "sha256": "9b93a15dc3eade66df4347731f2687b939daf987c8382660d6118e863604ab09",
11
11
  "trust": {
@@ -1,7 +1,9 @@
1
1
  // Tree-sitter runtime — parser management, parse, and query services.
2
2
 
3
+ import { createHash } from "node:crypto";
3
4
  import * as fs from "node:fs";
4
5
  import * as path from "node:path";
6
+ import { startDebugTimer } from "@mrclrchtr/supi-core/debug";
5
7
  import { resolveToolPath } from "@mrclrchtr/supi-core/path";
6
8
  import type { Language, Parser, Tree } from "web-tree-sitter";
7
9
  import { nodeToRange } from "../coordinates.ts";
@@ -126,11 +128,25 @@ export class TreeSitterRuntime {
126
128
  };
127
129
  }
128
130
 
131
+ const timer = startDebugTimer();
132
+ const parserState = this.parsers.has(grammarId)
133
+ ? "reused"
134
+ : this.parserPromises.has(grammarId)
135
+ ? "initializing"
136
+ : "cold";
137
+
129
138
  // Read the file
130
139
  let source: string;
131
140
  try {
132
141
  source = fs.readFileSync(resolvedPath, "utf-8");
133
142
  } catch (err: unknown) {
143
+ finishStructuralTiming(timer, {
144
+ operation: "parse",
145
+ grammar: grammarId,
146
+ parserState,
147
+ outcome: "file-access-error",
148
+ finalPhase: "file-read",
149
+ });
134
150
  const message = err instanceof Error ? err.message : "File could not be read";
135
151
  return {
136
152
  kind: "file-access-error",
@@ -138,15 +154,42 @@ export class TreeSitterRuntime {
138
154
  message,
139
155
  };
140
156
  }
157
+ timer.mark("file-read");
158
+
159
+ if (timer.enabled) {
160
+ try {
161
+ createHash("sha256").update(source).digest();
162
+ } catch {
163
+ // Timing-only hashing must not change structural behavior.
164
+ }
165
+ }
166
+ timer.mark("content-hash");
141
167
 
168
+ let parserReady = false;
142
169
  try {
143
170
  const entry = await this.ensureGrammarParser(grammarId);
171
+ timer.mark("parser-setup");
172
+ parserReady = true;
144
173
  const tree = entry.parser.parse(source);
174
+ finishStructuralTiming(timer, {
175
+ operation: "parse",
176
+ grammar: grammarId,
177
+ parserState,
178
+ outcome: "completed",
179
+ finalPhase: "parse",
180
+ });
145
181
  return {
146
182
  kind: "success",
147
183
  data: { tree: tree as Tree, source, resolvedPath, grammarId },
148
184
  };
149
185
  } catch (err: unknown) {
186
+ finishStructuralTiming(timer, {
187
+ operation: "parse",
188
+ grammar: grammarId,
189
+ parserState,
190
+ outcome: "runtime-error",
191
+ finalPhase: parserReady ? "parse" : "parser-setup",
192
+ });
150
193
  return { kind: "runtime-error", message: formatError(err, "Parser initialization failed") };
151
194
  }
152
195
  }
@@ -169,16 +212,27 @@ export class TreeSitterRuntime {
169
212
  const parseResult = await this.parseFile(filePath);
170
213
  if (parseResult.kind !== "success") return parseResult;
171
214
 
172
- const { tree, source } = parseResult.data;
215
+ const { grammarId, tree, source } = parseResult.data;
216
+ const timer = startDebugTimer();
217
+ let compiled = false;
173
218
 
174
219
  try {
175
- const entry = await this.ensureGrammarParser(parseResult.data.grammarId);
220
+ const entry = await this.ensureGrammarParser(grammarId);
176
221
  const mod = await this.ensureParserInit();
177
222
  let query: InstanceType<typeof mod.Query>;
178
223
 
179
224
  try {
180
225
  query = new mod.Query(entry.language, queryString);
226
+ compiled = true;
227
+ timer.mark("query-compilation");
181
228
  } catch (err: unknown) {
229
+ finishStructuralTiming(timer, {
230
+ operation: "query",
231
+ grammar: grammarId,
232
+ outcome: "validation-error",
233
+ captureCount: 0,
234
+ finalPhase: "query-compilation",
235
+ });
182
236
  return { kind: "validation-error", message: `Invalid query: ${formatError(err)}` };
183
237
  }
184
238
 
@@ -195,11 +249,25 @@ export class TreeSitterRuntime {
195
249
  });
196
250
  }
197
251
  }
252
+ finishStructuralTiming(timer, {
253
+ operation: "query",
254
+ grammar: grammarId,
255
+ outcome: "completed",
256
+ captureCount: captures.length,
257
+ finalPhase: "query-execution",
258
+ });
198
259
  return { kind: "success", data: captures };
199
260
  } finally {
200
261
  query.delete();
201
262
  }
202
263
  } catch (err: unknown) {
264
+ finishStructuralTiming(timer, {
265
+ operation: "query",
266
+ grammar: grammarId,
267
+ outcome: "runtime-error",
268
+ captureCount: 0,
269
+ finalPhase: compiled ? "query-execution" : "query-compilation",
270
+ });
203
271
  return { kind: "runtime-error", message: formatError(err, "Query execution failed") };
204
272
  } finally {
205
273
  tree.delete();
@@ -239,6 +307,42 @@ export class TreeSitterRuntime {
239
307
  /** Max query string length to prevent ReDoS via overly complex Tree-sitter patterns. */
240
308
  const MAX_QUERY_LENGTH = 10_000;
241
309
 
310
+ type StructuralTimer = ReturnType<typeof startDebugTimer>;
311
+ type ParserState = "cold" | "initializing" | "reused";
312
+
313
+ type StructuralTimingObservation =
314
+ | {
315
+ readonly operation: "parse";
316
+ readonly grammar: GrammarId;
317
+ readonly parserState: ParserState;
318
+ readonly outcome: "completed" | "file-access-error" | "runtime-error";
319
+ readonly finalPhase: "file-read" | "parse" | "parser-setup";
320
+ }
321
+ | {
322
+ readonly operation: "query";
323
+ readonly grammar: GrammarId;
324
+ readonly outcome: "completed" | "runtime-error" | "validation-error";
325
+ readonly captureCount: number;
326
+ readonly finalPhase: "query-compilation" | "query-execution";
327
+ };
328
+
329
+ function finishStructuralTiming(
330
+ timer: StructuralTimer,
331
+ observation: StructuralTimingObservation,
332
+ ): void {
333
+ const { finalPhase, ...data } = observation;
334
+ timer.finish(
335
+ () => ({
336
+ source: "tree-sitter",
337
+ level: "debug",
338
+ category: `structural.${data.operation}.timing`,
339
+ message: `Tree-sitter ${data.operation} ${data.outcome}`,
340
+ data: { ...data },
341
+ }),
342
+ finalPhase,
343
+ );
344
+ }
345
+
242
346
  /** Format errors with their cause chain's first message for user-facing tool output. */
243
347
  function formatError(err: unknown, fallback = "Operation failed"): string {
244
348
  if (!(err instanceof Error)) return String(err || fallback);