c5eebe6beb
Phase 0 of the Rust extraction-kernel migration (docs/design/ rust-kernel-migration-plan.md, now checked in with §3a recording the shipped state): - codegraph-kernel/ napi-rs crate: extractFile(path, content, language) → five flat buffers (meta/nodes/edges/refs/arena), one JS boundary crossing per file. Node ids computed Rust-side, byte-identical to generateNodeId (pinned by test vector). Reserved per-node metrics slot for the Arc 3.2 code-metrics work. - Generic .scm-driven emitter (@def.<kind>/@name/@ref.<kind> captures, byte-range scope stack → ::-joined qualified names, contains edges, refs attributed to the innermost enclosing symbol). Seed TS/JS queries are smoke-level; R2 replaces them with the full port. - Routing seam in extractFromSource with per-file wasm fallback. DEFAULT_ROUTED is empty — no behavior change until a language passes its equivalence gate (R3). Dev opt-in: CODEGRAPH_KERNEL_LANGS. Kill switch: CODEGRAPH_KERNEL=0. Loader verifies ABI + kind tables before routing; EDGE_KINDS became a runtime array because kind order is now wire contract. - Grammar-source parity: vendored TS/TSX/JS wasm grammars built from the exact crate revisions (tree-sitter-typescript v0.23.2, tree-sitter-javascript v0.25.0, checked-in parser.c, ts-cli 0.25.10) — the tree-sitter-wasms builds were 2023-era, which the new kernel-grammar-parity test caught on day one. Production TS/JS parsing gets 2.5 years of grammar fixes; full suite green (2456 tests). - Build/release wiring: scripts/build-kernel.sh + npm run build:kernel; release.yml kernel prebuild matrix (continue-on-error — the kernel is optional everywhere, bundles fall back to the wasm path); bundles stage lib/kernel/codegraph-kernel.node; release job runs the kernel suites with CODEGRAPH_KERNEL_EXPECT=1. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
54 lines
1.6 KiB
Rust
54 lines
1.6 KiB
Rust
//! Node-ID generation — MUST produce byte-identical output to
|
|
//! `generateNodeId` in `src/extraction/tree-sitter-helpers.ts`:
|
|
//!
|
|
//! `${kind}:${sha256(`${filePath}:${kind}:${name}:${line}`).hex[0..32]}`
|
|
//!
|
|
//! and the file-node special case in `TreeSitterExtractor.extract()`:
|
|
//!
|
|
//! `file:${filePath}`
|
|
//!
|
|
//! Node identity is how the wasm path and the kernel path agree on the same
|
|
//! graph — a drift here breaks every edge. Pinned by the node-id parity test
|
|
//! in `__tests__/kernel-scaffold.test.ts`.
|
|
|
|
use sha2::{Digest, Sha256};
|
|
|
|
pub fn node_id(file_path: &str, kind: &str, name: &str, line: u32) -> String {
|
|
let mut hasher = Sha256::new();
|
|
hasher.update(file_path.as_bytes());
|
|
hasher.update(b":");
|
|
hasher.update(kind.as_bytes());
|
|
hasher.update(b":");
|
|
hasher.update(name.as_bytes());
|
|
hasher.update(b":");
|
|
hasher.update(line.to_string().as_bytes());
|
|
let digest = hasher.finalize();
|
|
// 32 hex chars = first 16 bytes.
|
|
let mut hex = String::with_capacity(kind.len() + 1 + 32);
|
|
hex.push_str(kind);
|
|
hex.push(':');
|
|
for b in &digest[..16] {
|
|
hex.push_str(&format!("{b:02x}"));
|
|
}
|
|
hex
|
|
}
|
|
|
|
pub fn file_node_id(file_path: &str) -> String {
|
|
format!("file:{file_path}")
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn matches_known_ts_output() {
|
|
// Pinned vector: node -e "crypto.createHash('sha256')
|
|
// .update('src/a.ts:function:foo:3').digest('hex').substring(0,32)"
|
|
assert_eq!(
|
|
node_id("src/a.ts", "function", "foo", 3),
|
|
"function:bfb15544fed707794274a5c61006ea7b"
|
|
);
|
|
}
|
|
}
|