gitnexus 1.6.6-rc.135 → 1.6.6-rc.136

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.
@@ -33,7 +33,7 @@ import { getDartParser, getDartScopeQuery } from './query.js';
33
33
  import { recordCacheHit, recordCacheMiss } from './cache-stats.js';
34
34
  import { getTreeSitterBufferSize } from '../../constants.js';
35
35
  import { parseSourceSafe } from '../../../tree-sitter/safe-parse.js';
36
- import { DART_HERITAGE_PREFIX } from './interpret.js';
36
+ import { encodeMarker } from '../../utils/heritage-marker.js';
37
37
  import { DART_BUILT_INS } from './built-ins.js';
38
38
  const FUNCTION_DECL_TAGS = [
39
39
  '@declaration.function',
@@ -439,7 +439,7 @@ function emitHeritageMarkers(container, kind, className, out) {
439
439
  const c = container.namedChild(i);
440
440
  if (c === null || c.type !== 'type_identifier')
441
441
  continue;
442
- const payload = `${DART_HERITAGE_PREFIX}${kind}:${c.text}:${className}`;
442
+ const payload = encodeMarker('heritage', [kind, c.text, className]);
443
443
  out.push({ '@import.heritage': syntheticCapture('@import.heritage', c, payload) });
444
444
  }
445
445
  }
@@ -15,8 +15,10 @@
15
15
  */
16
16
  import type { CaptureMatch, ParsedImport, ParsedTypeBinding } from '../../../../_shared/index.js';
17
17
  /** Marker prefix carried on a side-effect `ParsedImport.targetRaw` for
18
- * `implements`/`with` heritage, consumed by `emitDartHeritageEdges`. */
19
- export declare const DART_HERITAGE_PREFIX = "__heritage__:";
18
+ * `implements`/`with` heritage, consumed by `emitDartHeritageEdges`. Aliased to
19
+ * the shared codec prefix (#1994) so the Dart wire prefix has a single source of
20
+ * truth and cannot desync from `encodeMarker`/`decodeMarker`. */
21
+ export declare const DART_HERITAGE_PREFIX: string;
20
22
  export declare function interpretDartImport(captures: CaptureMatch): ParsedImport | null;
21
23
  export declare function normalizeDartType(text: string): string;
22
24
  export declare function interpretDartTypeBinding(captures: CaptureMatch): ParsedTypeBinding | null;
@@ -13,9 +13,12 @@
13
13
  * normalizing the Dart type (strip nullable `?`, unwrap single-arg
14
14
  * container generics like `Future<X>`/`List<X>`, drop library prefixes).
15
15
  */
16
+ import { HERITAGE_MARKER_PREFIX } from '../../utils/heritage-marker.js';
16
17
  /** Marker prefix carried on a side-effect `ParsedImport.targetRaw` for
17
- * `implements`/`with` heritage, consumed by `emitDartHeritageEdges`. */
18
- export const DART_HERITAGE_PREFIX = '__heritage__:';
18
+ * `implements`/`with` heritage, consumed by `emitDartHeritageEdges`. Aliased to
19
+ * the shared codec prefix (#1994) so the Dart wire prefix has a single source of
20
+ * truth and cannot desync from `encodeMarker`/`decodeMarker`. */
21
+ export const DART_HERITAGE_PREFIX = HERITAGE_MARKER_PREFIX;
19
22
  function stripQuotes(s) {
20
23
  return s.replace(/^['"]|['"]$/g, '');
21
24
  }
@@ -31,7 +31,8 @@ import { populateClassOwnedMembers, isClassLike } from '../../scope-resolution/s
31
31
  import { resolveDefGraphId } from '../../scope-resolution/graph-bridge/ids.js';
32
32
  import { generateId } from '../../../../lib/utils.js';
33
33
  import { dartProvider } from '../dart.js';
34
- import { dartArityCompatibility, dartMergeBindings, resolveDartImportTarget, DART_HERITAGE_PREFIX, } from './index.js';
34
+ import { dartArityCompatibility, dartMergeBindings, resolveDartImportTarget } from './index.js';
35
+ import { decodeMarker } from '../../utils/heritage-marker.js';
35
36
  import { expandDartWildcardNames } from './expand-wildcards.js';
36
37
  /**
37
38
  * Resolve a class simple name to a graph id with FILE AFFINITY: prefer a
@@ -88,9 +89,12 @@ function emitDartHeritageEdges(graph, parsedFiles, nodeLookup) {
88
89
  for (const parsed of parsedFiles) {
89
90
  for (const imp of parsed.parsedImports) {
90
91
  const raw = imp.targetRaw;
91
- if (typeof raw !== 'string' || !raw.startsWith(DART_HERITAGE_PREFIX))
92
+ if (typeof raw !== 'string')
92
93
  continue;
93
- const parts = raw.slice(DART_HERITAGE_PREFIX.length).split(':');
94
+ const decoded = decodeMarker(raw);
95
+ if (decoded?.kind !== 'heritage')
96
+ continue;
97
+ const parts = decoded.fields;
94
98
  if (parts.length < 3)
95
99
  continue;
96
100
  const [kind, baseName, childName] = parts;
@@ -5,6 +5,7 @@ import { synthesizeRubyReceiverBinding, findEnclosingClassOrModule } from './rec
5
5
  import { getTreeSitterBufferSize } from '../../constants.js';
6
6
  import { parseSourceSafe } from '../../../tree-sitter/safe-parse.js';
7
7
  import { splitQualifiedName } from '../../utils/qualified-name.js';
8
+ import { encodeMarker } from '../../utils/heritage-marker.js';
8
9
  const FUNCTION_NODE_TYPES = ['method', 'singleton_method'];
9
10
  const HERITAGE_CALL_NAMES = new Set(['include', 'extend', 'prepend']);
10
11
  const ATTR_CALL_NAMES = new Set([
@@ -159,7 +160,7 @@ export function emitRubyScopeCaptures(sourceText, _filePath, cachedTree) {
159
160
  out.push({
160
161
  '@import.statement': grouped['@reference.call.free'],
161
162
  '@import.kind': syntheticCapture('@import.kind', callNode, 'namespace'),
162
- '@import.source': syntheticCapture('@import.source', callNode, `__heritage__:${callName}:${mixinName}:${ownerName}`),
163
+ '@import.source': syntheticCapture('@import.source', callNode, encodeMarker('heritage', [callName, mixinName, ownerName])),
163
164
  '@import.name': syntheticCapture('@import.name', callNode, mixinName),
164
165
  });
165
166
  }
@@ -188,7 +189,7 @@ export function emitRubyScopeCaptures(sourceText, _filePath, cachedTree) {
188
189
  out.push({
189
190
  '@import.statement': grouped['@reference.call.free'],
190
191
  '@import.kind': syntheticCapture('@import.kind', callNode, 'namespace'),
191
- '@import.source': syntheticCapture('@import.source', callNode, `__property__:${callName}:${propName}:${ownerName}`),
192
+ '@import.source': syntheticCapture('@import.source', callNode, encodeMarker('property', [callName, propName, ownerName])),
192
193
  '@import.name': syntheticCapture('@import.name', callNode, propName),
193
194
  });
194
195
  // Emit a property declaration so the property flows into
@@ -8,6 +8,7 @@
8
8
  */
9
9
  import { resolveRubyImportInternal } from '../../import-resolvers/ruby.js';
10
10
  import { buildSuffixIndex } from '../../import-resolvers/utils.js';
11
+ import { isHeritageMarker } from '../../utils/heritage-marker.js';
11
12
  // ─── resolveRubyImportTarget ──────────────────────────────────────────────
12
13
  /**
13
14
  * ScopeResolver-shaped adapter:
@@ -25,7 +26,7 @@ import { buildSuffixIndex } from '../../import-resolvers/utils.js';
25
26
  export function resolveRubyImportTarget(targetRaw, fromFile, allFilePaths, _resolutionConfig) {
26
27
  if (!targetRaw)
27
28
  return null;
28
- if (targetRaw.startsWith('__heritage__:') || targetRaw.startsWith('__property__:'))
29
+ if (isHeritageMarker(targetRaw))
29
30
  return null;
30
31
  const fromNormalized = fromFile.replace(/\\/g, '/');
31
32
  const fromDir = fromNormalized.includes('/')
@@ -1,3 +1,4 @@
1
+ import { isHeritageMarker } from '../../utils/heritage-marker.js';
1
2
  // ─── interpretImport ──────────────────────────────────────────────────────
2
3
  /**
3
4
  * Interpret a pre-decomposed Ruby import capture into a `ParsedImport`.
@@ -18,7 +19,7 @@ export function interpretRubyImport(captures) {
18
19
  return null;
19
20
  // Heritage-encoded imports (__heritage__:include:Serializable:User)
20
21
  // are stored as namespace imports so emitHeritageEdges can read them.
21
- if (source.startsWith('__heritage__:') || source.startsWith('__property__:')) {
22
+ if (isHeritageMarker(source)) {
22
23
  const name = captures['@import.name']?.text ?? source;
23
24
  return { kind: 'namespace', localName: name, importedName: name, targetRaw: source };
24
25
  }
@@ -5,8 +5,7 @@ import { rubyArityCompatibility, rubyMergeBindings, resolveRubyImportTarget } fr
5
5
  import { populateClassOwnedMembers, isClassLike } from '../../scope-resolution/scope/walkers.js';
6
6
  import { resolveDefGraphId } from '../../scope-resolution/graph-bridge/ids.js';
7
7
  import { generateId } from '../../../../lib/utils.js';
8
- const HERITAGE_PREFIX = '__heritage__:';
9
- const PROPERTY_PREFIX = '__property__:';
8
+ import { decodeMarker } from '../../utils/heritage-marker.js';
10
9
  /**
11
10
  * #1991: resolve a BARE mixin reference (`include Loggable`) to a nested module by
12
11
  * the INCLUDING class's lexical scope — Ruby looks up a constant in the innermost
@@ -71,9 +70,10 @@ function emitRubyMixinEdges(graph, parsedFiles, nodeLookup) {
71
70
  }
72
71
  for (const parsed of parsedFiles) {
73
72
  for (const imp of parsed.parsedImports) {
74
- if (!imp.targetRaw.startsWith(HERITAGE_PREFIX))
73
+ const decoded = decodeMarker(imp.targetRaw);
74
+ if (decoded?.kind !== 'heritage')
75
75
  continue;
76
- const parts = imp.targetRaw.slice(HERITAGE_PREFIX.length).split(':');
76
+ const parts = decoded.fields;
77
77
  if (parts.length < 3)
78
78
  continue;
79
79
  const [kind, mixinName, className] = parts;
@@ -114,9 +114,10 @@ function emitRubyMixinEdges(graph, parsedFiles, nodeLookup) {
114
114
  }
115
115
  for (const parsed of parsedFiles) {
116
116
  for (const imp of parsed.parsedImports) {
117
- if (!imp.targetRaw.startsWith(PROPERTY_PREFIX))
117
+ const decoded = decodeMarker(imp.targetRaw);
118
+ if (decoded?.kind !== 'property')
118
119
  continue;
119
- const parts = imp.targetRaw.slice(PROPERTY_PREFIX.length).split(':');
120
+ const parts = decoded.fields;
120
121
  if (parts.length < 3)
121
122
  continue;
122
123
  const [_attrKind, propName, className] = parts;
@@ -0,0 +1,33 @@
1
+ /**
2
+ * #1994: shared codec for the synthetic `__heritage__:` / `__property__:` import
3
+ * markers used by the Ruby and Dart scope resolvers to carry side-effect facts
4
+ * (mixin includes, attr_accessor properties) through the import channel. Both
5
+ * languages share the exact `':'`-delimited wire format, so a single encode/decode
6
+ * pair removes the per-site hand-rolled string handling that produced the #1981
7
+ * edge-drop. Language-NEUTRAL — keyed only on the literal prefixes; no provider
8
+ * branching belongs here.
9
+ */
10
+ export type MarkerKind = 'heritage' | 'property';
11
+ export declare const HERITAGE_MARKER_PREFIX: string;
12
+ export declare const PROPERTY_MARKER_PREFIX: string;
13
+ /**
14
+ * Build a marker string `<prefix><field>:<field>:...`. The `':'` delimiter IS the
15
+ * wire format, so a field that itself contains `':'` is structurally invalid and
16
+ * THROWS — callers must pre-normalize colon-bearing values (e.g. a qualified mixin
17
+ * arg `Outer::Mixin` → `Outer.Mixin`). This makes the #1981 silent edge-drop a
18
+ * loud failure instead.
19
+ */
20
+ export declare function encodeMarker(kind: MarkerKind, fields: readonly string[]): string;
21
+ /**
22
+ * Parse a marker string back into its kind + positional fields, or `null` if `raw`
23
+ * is not a marker. Mirrors the historical `slice(PREFIX.length).split(':')`.
24
+ */
25
+ export declare function decodeMarker(raw: string): {
26
+ kind: MarkerKind;
27
+ fields: string[];
28
+ } | null;
29
+ /**
30
+ * True if `raw` is a synthetic heritage/property marker — exactly the prior
31
+ * `startsWith('__heritage__:') || startsWith('__property__:')` pair.
32
+ */
33
+ export declare function isHeritageMarker(raw: string): boolean;
@@ -0,0 +1,41 @@
1
+ const PREFIX_BY_KIND = {
2
+ heritage: '__heritage__:',
3
+ property: '__property__:',
4
+ };
5
+ export const HERITAGE_MARKER_PREFIX = PREFIX_BY_KIND.heritage;
6
+ export const PROPERTY_MARKER_PREFIX = PREFIX_BY_KIND.property;
7
+ /**
8
+ * Build a marker string `<prefix><field>:<field>:...`. The `':'` delimiter IS the
9
+ * wire format, so a field that itself contains `':'` is structurally invalid and
10
+ * THROWS — callers must pre-normalize colon-bearing values (e.g. a qualified mixin
11
+ * arg `Outer::Mixin` → `Outer.Mixin`). This makes the #1981 silent edge-drop a
12
+ * loud failure instead.
13
+ */
14
+ export function encodeMarker(kind, fields) {
15
+ for (const field of fields) {
16
+ if (field.includes(':')) {
17
+ throw new Error(`encodeMarker: field "${field}" contains the ':' delimiter; normalize it before encoding`);
18
+ }
19
+ }
20
+ return PREFIX_BY_KIND[kind] + fields.join(':');
21
+ }
22
+ /**
23
+ * Parse a marker string back into its kind + positional fields, or `null` if `raw`
24
+ * is not a marker. Mirrors the historical `slice(PREFIX.length).split(':')`.
25
+ */
26
+ export function decodeMarker(raw) {
27
+ if (raw.startsWith(PREFIX_BY_KIND.heritage)) {
28
+ return { kind: 'heritage', fields: raw.slice(PREFIX_BY_KIND.heritage.length).split(':') };
29
+ }
30
+ if (raw.startsWith(PREFIX_BY_KIND.property)) {
31
+ return { kind: 'property', fields: raw.slice(PREFIX_BY_KIND.property.length).split(':') };
32
+ }
33
+ return null;
34
+ }
35
+ /**
36
+ * True if `raw` is a synthetic heritage/property marker — exactly the prior
37
+ * `startsWith('__heritage__:') || startsWith('__property__:')` pair.
38
+ */
39
+ export function isHeritageMarker(raw) {
40
+ return raw.startsWith(PREFIX_BY_KIND.heritage) || raw.startsWith(PREFIX_BY_KIND.property);
41
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gitnexus",
3
- "version": "1.6.6-rc.135",
3
+ "version": "1.6.6-rc.136",
4
4
  "description": "Graph-powered code intelligence for AI agents. Index any codebase, query via MCP or CLI.",
5
5
  "author": "Abhigyan Patwari",
6
6
  "license": "PolyForm-Noncommercial-1.0.0",