dsh-code-index 0.10.1 → 0.11.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/CHANGELOG.md +22 -2
- package/README.md +65 -11
- package/README.zh.md +65 -11
- package/dist/client.js +641 -46
- package/dist/client.js.map +1 -1
- package/dist/index.d.ts +91 -3
- package/dist/index.js +245 -3
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/scripts/impact-xray-benchmark.mjs +30 -0
- package/scripts/impact-xray-demo.mjs +23 -0
- package/scripts/impact-xray-poc.mjs +27 -0
- package/scripts/impact-xray-rc-benchmark.mjs +77 -0
- package/scripts/process-impact-xray-demo.mjs +29 -0
- package/scripts/release-smoke.mjs +62 -1
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-code-index",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.11.0",
|
|
4
|
+
"description": "Impact X-Ray for DSH: inspect potential code-change impact, provenance and real source evidence before AI edits.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
7
7
|
"types": "./dist/index.d.ts",
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/** Measured local observations, not a performance promise. Run after build. */
|
|
2
|
+
import { mkdir, mkdtemp, writeFile } from 'node:fs/promises'
|
|
3
|
+
import { execFileSync } from 'node:child_process'
|
|
4
|
+
import path from 'node:path'
|
|
5
|
+
import { fileURLToPath, pathToFileURL } from 'node:url'
|
|
6
|
+
if (process.argv[2] !== '--worker') {
|
|
7
|
+
const rows = []
|
|
8
|
+
const baseline = process.argv[2] === '--baseline' ? path.resolve(process.argv[3]) : null
|
|
9
|
+
for (const files of [6, 100, 600]) rows.push(JSON.parse(execFileSync(process.execPath, [fileURLToPath(import.meta.url), '--worker', String(files), ...(baseline ? [baseline] : [])], { encoding: 'utf8' })))
|
|
10
|
+
console.log(JSON.stringify({ method: 'Three isolated Node processes; generated real files; explicit file selection, not a Git-change or production-repository benchmark; one cold index and three warm analysis/projection samples per size', node: process.version, rows }, null, 2))
|
|
11
|
+
} else {
|
|
12
|
+
const { buildIndex, buildChangeContext, buildImpactXray } = await import(process.argv[4] ? pathToFileURL(process.argv[4]).href : '../dist/index.js')
|
|
13
|
+
const count = Number(process.argv[3])
|
|
14
|
+
await mkdir('.dsh-code-index/impact-xray-mvp', { recursive: true })
|
|
15
|
+
const root = await mkdtemp(path.resolve('.dsh-code-index/impact-xray-mvp/bench-'))
|
|
16
|
+
await mkdir(path.join(root, '.git'))
|
|
17
|
+
await writeFile(path.join(root, 'core.ts'), 'export function normalize(input: string) { return input.trim() }\n')
|
|
18
|
+
for (let i = 1; i < count; i++) await writeFile(path.join(root, `caller${i}.ts`), `import { normalize } from './core'\nexport function caller${i}() { return normalize(' value ') }\nexport function entry${i}() { return caller${i}() }\n`)
|
|
19
|
+
const start = performance.now(), index = await buildIndex(root), indexMs = performance.now() - start
|
|
20
|
+
const samples = []
|
|
21
|
+
for (let i = 0; i < 3; i++) {
|
|
22
|
+
const cpu = process.cpuUsage(), before = performance.now()
|
|
23
|
+
const result = await buildChangeContext(index, { kind: 'files', files: ['core.ts'] }, { maxImpact: 200, maxPaths: 50 })
|
|
24
|
+
const analysisMs = performance.now() - before, project = performance.now(), view = buildImpactXray ? await buildImpactXray(index, result) : null
|
|
25
|
+
const cpuUsed = process.cpuUsage(cpu)
|
|
26
|
+
samples.push({ analysisMs, projectionMs: view ? performance.now() - project : null, cpuMs: (cpuUsed.user + cpuUsed.system) / 1000,
|
|
27
|
+
nodes: view?.nodes.length ?? 0, edges: view?.edges.length ?? 0, metadataBytes: view ? Buffer.byteLength(JSON.stringify(view)) : 0, truncated: view?.truncated ?? Object.values(result.truncated).some(Boolean) })
|
|
28
|
+
}
|
|
29
|
+
console.log(JSON.stringify({ files: index.files.length, symbols: index.files.reduce((n, f) => n + f.symbols.length, 0), indexMs, samples, rssBytes: process.memoryUsage().rss, processPeakRssBytes: process.resourceUsage().maxRSS * 1024 }))
|
|
30
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/** Reproducible real JavaScript repository for the Desktop demo. */
|
|
2
|
+
import { access, mkdir, writeFile } from 'node:fs/promises'
|
|
3
|
+
import { execFileSync } from 'node:child_process'
|
|
4
|
+
import path from 'node:path'
|
|
5
|
+
const root = path.resolve(process.argv[2] ?? '.dsh-code-index/impact-xray-mvp/demo-repo')
|
|
6
|
+
try { await access(root); throw Error(`Refusing to overwrite ${root}`) } catch (e) { if (e.code !== 'ENOENT') throw e }
|
|
7
|
+
await mkdir(path.join(root, 'src'), { recursive: true }); await mkdir(path.join(root, 'tests'))
|
|
8
|
+
const files = {
|
|
9
|
+
'package.json': JSON.stringify({ name: 'impact-xray-demo', private: true, type: 'module', scripts: { test: 'node --test' } }, null, 2) + '\n',
|
|
10
|
+
'src/core.js': 'export function normalize(input) {\n return input.trim()\n}\nexport function localUsage() { return normalize(" local ") }\n',
|
|
11
|
+
'src/startup.js': 'import { normalize } from "./core.js"\nexport function startup() { return normalize(" startup ") }\n',
|
|
12
|
+
'src/scoped.js': 'import * as core from "./core.js"\nexport function scopedUsage() { return core.normalize(" scoped ") }\n',
|
|
13
|
+
'src/loose.js': '// Deliberately unresolved name: a candidate, not a verified executable path.\nexport function looseUsage() { return normalize(" loose ") }\n',
|
|
14
|
+
'src/main.js': 'import { startup } from "./startup.js"\nexport function main() { return startup() }\n',
|
|
15
|
+
'tests/core.test.js': 'import test from "node:test"\nimport assert from "node:assert/strict"\nimport { normalize } from "../src/core.js"\nexport function checkNormalize() { return normalize(" test ") }\ntest("normalization preserves letter case", () => assert.equal(checkNormalize(), "test"))\n',
|
|
16
|
+
}
|
|
17
|
+
for (const [file, text] of Object.entries(files)) await writeFile(path.join(root, file), text)
|
|
18
|
+
const git = (...args) => execFileSync('git', args, { cwd: root, encoding: 'utf8' }).trim()
|
|
19
|
+
git('init', '-q'); git('add', '.'); git('-c', 'user.name=Impact X-Ray Demo', '-c', 'user.email=demo@example.invalid', 'commit', '-qm', 'real demo baseline')
|
|
20
|
+
await writeFile(path.join(root, 'src/core.js'), files['src/core.js'].replace('input.trim()', 'input.trim().toUpperCase()'))
|
|
21
|
+
console.log(JSON.stringify({ root, base: git('rev-parse', 'HEAD'), request: { repoRoot: root, outputFormat: 'xray' },
|
|
22
|
+
test: 'node --test (baseline passes; current case-conversion edit intentionally fails)',
|
|
23
|
+
uncertainty: 'looseUsage intentionally has no import; its name-only edge is a low-confidence candidate.' }, null, 2))
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/** Create a fresh, real Git fixture; no host/profile changes or service. */
|
|
2
|
+
import { mkdir, writeFile, access } from 'node:fs/promises'
|
|
3
|
+
import { execFileSync } from 'node:child_process'
|
|
4
|
+
import path from 'node:path'
|
|
5
|
+
|
|
6
|
+
const root = path.resolve(process.argv[2] ?? '.dsh-code-index/impact-xray-poc/fixture')
|
|
7
|
+
try { await access(root); throw Error(`Fixture directory already exists: ${root}`) }
|
|
8
|
+
catch (error) { if (error.code !== 'ENOENT') throw error }
|
|
9
|
+
await mkdir(path.join(root, 'src'), { recursive: true })
|
|
10
|
+
await mkdir(path.join(root, 'tests'))
|
|
11
|
+
const files = {
|
|
12
|
+
'src/core.ts': 'export function normalize(input: string) {\n return input.trim()\n}\nexport function localUsage() { return normalize(" local ") }\n',
|
|
13
|
+
'src/startup.ts': 'import { normalize } from "./core"\nexport function startup() { return normalize(" startup ") }\n',
|
|
14
|
+
'src/scoped.ts': 'import * as core from "./core"\nexport function scopedUsage() { return core.normalize(" scoped ") }\n',
|
|
15
|
+
'src/loose.ts': '// Deliberately unresolved: a low-confidence name match, not an executable import.\nexport function looseUsage() { return normalize(" loose ") }\n',
|
|
16
|
+
'src/main.ts': 'import { startup } from "./startup"\nexport function main() { return startup() }\n',
|
|
17
|
+
'tests/core.test.ts': 'import { normalize } from "../src/core"\nexport function checkNormalize() { return normalize(" test ") === "test" }\n',
|
|
18
|
+
}
|
|
19
|
+
for (const [file, text] of Object.entries(files)) await writeFile(path.join(root, file), text)
|
|
20
|
+
const git = (...args) => execFileSync('git', args, { cwd: root, encoding: 'utf8' }).trim()
|
|
21
|
+
git('init', '-q')
|
|
22
|
+
git('add', 'src', 'tests')
|
|
23
|
+
git('-c', 'user.name=Impact X-Ray PoC', '-c', 'user.email=poc@example.invalid', 'commit', '-qm', 'fixture baseline')
|
|
24
|
+
await writeFile(path.join(root, 'src/core.ts'), files['src/core.ts'].replace('input.trim()', 'input.trim().toUpperCase()'))
|
|
25
|
+
console.log(JSON.stringify({ root, base: git('rev-parse', 'HEAD'), changed: 'src/core.ts:2',
|
|
26
|
+
request: { repoRoot: root, outputFormat: 'xray' },
|
|
27
|
+
notes: 'Tiny analysis fixture. loose.ts intentionally has no import; the test file is a lead, not a coverage claim.' }, null, 2))
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/** Stage-separated observations; never a speed or memory guarantee. No forced GC. */
|
|
2
|
+
import { readFile, mkdir, writeFile } from 'node:fs/promises'
|
|
3
|
+
import { execFileSync } from 'node:child_process'
|
|
4
|
+
import { createHash } from 'node:crypto'
|
|
5
|
+
import path from 'node:path'
|
|
6
|
+
import os from 'node:os'
|
|
7
|
+
import { fileURLToPath, pathToFileURL } from 'node:url'
|
|
8
|
+
const sha = value => createHash('sha256').update(value).digest('hex')
|
|
9
|
+
const script = fileURLToPath(import.meta.url)
|
|
10
|
+
if (process.argv[2] === '--worker') {
|
|
11
|
+
const [moduleFile, root, file, mode] = process.argv.slice(3)
|
|
12
|
+
const api = await import(pathToFileURL(moduleFile).href)
|
|
13
|
+
const stages = []
|
|
14
|
+
const capture = (stage, ms = 0) => stages.push({ stage, ms, ...process.memoryUsage(), peakRssBytes: process.resourceUsage().maxRSS * 1024 })
|
|
15
|
+
capture('module-loaded')
|
|
16
|
+
let start = performance.now()
|
|
17
|
+
const index = await api.buildIndex(root)
|
|
18
|
+
capture('cold-index', performance.now() - start)
|
|
19
|
+
if (process.env.XRAY_BENCH_SETTLE_MS) {
|
|
20
|
+
await new Promise(resolve => setTimeout(resolve, Number(process.env.XRAY_BENCH_SETTLE_MS)))
|
|
21
|
+
capture('after-index-idle', Number(process.env.XRAY_BENCH_SETTLE_MS))
|
|
22
|
+
}
|
|
23
|
+
const samples = []
|
|
24
|
+
for (let i = 0; i < 3; i++) {
|
|
25
|
+
start = performance.now()
|
|
26
|
+
const result = await api.buildChangeContext(index, { kind: 'files', files: [file] }, { maxImpact: 200, maxPaths: 50 })
|
|
27
|
+
capture(`analysis-${i + 1}`, performance.now() - start)
|
|
28
|
+
const analysisSha256 = sha(JSON.stringify(result))
|
|
29
|
+
let view
|
|
30
|
+
if (mode === 'xray') {
|
|
31
|
+
start = performance.now()
|
|
32
|
+
view = await api.buildImpactXray(index, result)
|
|
33
|
+
capture(`projection-${i + 1}`, performance.now() - start)
|
|
34
|
+
}
|
|
35
|
+
samples.push({ analysisSha256, changed: result.changed.length, callers: result.directCallers.length, impact: result.impact.length,
|
|
36
|
+
tests: result.tests.length, nodes: view?.nodes.length, edges: view?.edges.length,
|
|
37
|
+
metadataBytes: view ? Buffer.byteLength(JSON.stringify(view)) : 0 })
|
|
38
|
+
}
|
|
39
|
+
console.log(JSON.stringify({ mode, files: index.files.length, symbols: index.files.reduce((n, f) => n + f.symbols.length, 0),
|
|
40
|
+
indexedSourceBytes: (await Promise.all(index.files.map(f => readFile(path.join(root, f.path))))).reduce((n, b) => n + b.length, 0), stages, samples }))
|
|
41
|
+
} else {
|
|
42
|
+
const [baseline, mediumRoot, mediumFile = 'lib/utils.js', repeats = '3'] = process.argv.slice(2)
|
|
43
|
+
if (!baseline || !mediumRoot) throw Error('Usage: node scripts/impact-xray-rc-benchmark.mjs BASELINE_DIST MEDIUM_REPO [FILE] [REPEATS]')
|
|
44
|
+
const scratch = path.resolve('.dsh-code-index/impact-xray-rc')
|
|
45
|
+
const tiny = path.join(scratch, 'six-files')
|
|
46
|
+
await mkdir(tiny, { recursive: true })
|
|
47
|
+
await mkdir(path.join(tiny, '.git'), { recursive: true })
|
|
48
|
+
await writeFile(path.join(tiny, 'core.ts'), 'export function normalize(input: string) { return input.trim() }\n')
|
|
49
|
+
for (let i = 1; i < 6; i++) await writeFile(path.join(tiny, `caller${i}.ts`), `import { normalize } from './core'\nexport function caller${i}() { return normalize(' value ') }\nexport function entry${i}() { return caller${i}() }\n`)
|
|
50
|
+
const variants = [{ name: 'v0.10.2-analysis', module: path.resolve(baseline), mode: 'analysis' },
|
|
51
|
+
{ name: 'candidate-analysis', module: path.resolve('dist/index.js'), mode: 'analysis' },
|
|
52
|
+
{ name: 'candidate-xray', module: path.resolve('dist/index.js'), mode: 'xray' }]
|
|
53
|
+
const observations = []
|
|
54
|
+
for (const [fixture, root, file] of [['six-files', tiny, 'core.ts'], ['express-v5.1.0', path.resolve(mediumRoot), mediumFile]]) {
|
|
55
|
+
for (let repeat = 0; repeat < Number(repeats); repeat++) {
|
|
56
|
+
// Rotate order to reduce systematic order / machine-temperature bias.
|
|
57
|
+
const order = [...variants.slice(repeat % 3), ...variants.slice(0, repeat % 3)]
|
|
58
|
+
for (const v of order) {
|
|
59
|
+
const result = JSON.parse(execFileSync(process.execPath, [script, '--worker', v.module, root, file, v.mode], { encoding: 'utf8', maxBuffer: 4e6 }))
|
|
60
|
+
observations.push({ fixture, repeat: repeat + 1, variant: v.name, ...result })
|
|
61
|
+
await writeFile(path.join(scratch, 'performance-progress.json'), JSON.stringify(observations, null, 2) + '\n')
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
const byFixture = new Map()
|
|
66
|
+
for (const row of observations) {
|
|
67
|
+
const digests = row.samples.map(s => s.analysisSha256)
|
|
68
|
+
const expected = byFixture.get(row.fixture) ?? digests[0]
|
|
69
|
+
if (digests.some(d => d !== expected)) throw Error(`Analysis output changed: ${row.fixture}`)
|
|
70
|
+
byFixture.set(row.fixture, expected)
|
|
71
|
+
}
|
|
72
|
+
console.log(JSON.stringify({ node: process.version, platform: process.platform, cpu: os.cpus()[0]?.model, physicalMemoryBytes: os.totalmem(),
|
|
73
|
+
method: '18 isolated processes, three rotated repeats per fixture/variant. Identical files and explicit file selection, no fabricated diff. Cold index then three analysis samples; projection only in xray variant. Same analysis output hashes required. Process high-water RSS is cumulative, not a per-stage allocation metric. No forced GC; retained RSS is not proof of a leak.',
|
|
74
|
+
indexIdleMs: Number(process.env.XRAY_BENCH_SETTLE_MS ?? 0),
|
|
75
|
+
medium: { origin: execFileSync('git', ['-C', mediumRoot, 'remote', 'get-url', 'origin'], { encoding: 'utf8' }).trim(), commit: execFileSync('git', ['-C', mediumRoot, 'rev-parse', 'HEAD'], { encoding: 'utf8' }).trim(), selectedFile: mediumFile },
|
|
76
|
+
engineSources: await Promise.all(['src/buildIndex.ts', 'src/extract.ts', 'src/refgraph.ts', 'src/change-context.ts'].map(async file => ({ file, sha256: sha(await readFile(file)) }))), observations }, null, 2))
|
|
77
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/** Trim/crop authentic Desktop footage. No generated UI, overlays or source text. */
|
|
2
|
+
import { spawnSync } from 'node:child_process'
|
|
3
|
+
import { readFile, stat, writeFile } from 'node:fs/promises'
|
|
4
|
+
import { createHash } from 'node:crypto'
|
|
5
|
+
import path from 'node:path'
|
|
6
|
+
const [input, startArg, durationArg, crop = '-'] = process.argv.slice(2)
|
|
7
|
+
const start = Number(startArg), duration = Number(durationArg)
|
|
8
|
+
if (!input || !Number.isFinite(start) || start < 0 || !Number.isFinite(duration) || duration < 20 || duration > 30 ||
|
|
9
|
+
crop !== '-' && !/^\d+:\d+:\d+:\d+$/.test(crop)) throw Error('Usage: node scripts/process-impact-xray-demo.mjs INPUT START_SECONDS DURATION_20_TO_30 [WIDTH:HEIGHT:X:Y]. Set FFMPEG to an existing ffmpeg binary if needed.')
|
|
10
|
+
const ffmpeg = process.env.FFMPEG || 'ffmpeg'
|
|
11
|
+
const output = 'assets/impact-xray-mvp'
|
|
12
|
+
const filters = [...(crop === '-' ? [] : [`crop=${crop}`]), "scale='min(1180,iw)':-2"]
|
|
13
|
+
function run(args) {
|
|
14
|
+
const r = spawnSync(ffmpeg, ['-hide_banner', '-y', ...args], { encoding: 'utf8' })
|
|
15
|
+
if (r.status !== 0) throw Error(r.stderr || String(r.error))
|
|
16
|
+
return r.stderr
|
|
17
|
+
}
|
|
18
|
+
const log = run(['-ss', String(start), '-i', input, '-t', String(duration), '-vf', filters.join(','), '-an', '-r', '30', '-c:v', 'libx264', '-crf', '20', '-pix_fmt', 'yuv420p', '-movflags', '+faststart', `${output}.mp4`])
|
|
19
|
+
if (!/frame=\s*[1-9]/.test(log)) throw Error('No footage encoded')
|
|
20
|
+
run(['-i', `${output}.mp4`, '-vf', "fps=10,scale='min(1000,iw)':-2,split[a][b];[a]palettegen=max_colors=128[p];[b][p]paletteuse=dither=bayer:bayer_scale=3", '-loop', '0', `${output}.gif`])
|
|
21
|
+
run(['-ss', '8', '-i', `${output}.mp4`, '-frames:v', '1', `${output}-poster.png`])
|
|
22
|
+
const sha256 = async p => createHash('sha256').update(await readFile(p)).digest('hex')
|
|
23
|
+
const files = []
|
|
24
|
+
for (const suffix of ['mp4', 'gif', 'poster.png']) {
|
|
25
|
+
const file = suffix === 'poster.png' ? `${output}-poster.png` : `${output}.${suffix}`
|
|
26
|
+
files.push({ file, bytes: (await stat(file)).size, sha256: await sha256(file) })
|
|
27
|
+
}
|
|
28
|
+
await writeFile(`${output}-media.json`, JSON.stringify({ method: 'A chronological excerpt of actual DSH Desktop screen recording; rectangular crop/scale, frame-rate conversion, palette quantization and audio removal only. No fabricated UI, text, screenshots or latency claim.', raw: { filename: path.basename(input), sha256: await sha256(input) }, excerpt: { startSeconds: start, durationSeconds: duration, crop, speed: 1 }, files }, null, 2) + '\n')
|
|
29
|
+
console.log(JSON.stringify(files, null, 2))
|
|
@@ -219,6 +219,23 @@ try {
|
|
|
219
219
|
writeFileSync(path.join(arkRoot, 'Broken.ets'), '@Component\nstruct Broken { build() {')
|
|
220
220
|
assert.match(await invoke('code_index', { repoRoot: arkRoot }), /index incomplete:/)
|
|
221
221
|
|
|
222
|
+
// Issue #5: exercise actual installed Rust signatures, not source imports.
|
|
223
|
+
const rustRoot = path.join(smokeDir, 'rust-repo')
|
|
224
|
+
mkdirSync(path.join(rustRoot, '.git'), { recursive: true })
|
|
225
|
+
writeFileSync(path.join(rustRoot, 'repro.rs'), [
|
|
226
|
+
'pub struct PublicOne { pub field: u32 }',
|
|
227
|
+
'struct PrivateOne { field: u32 }',
|
|
228
|
+
'pub enum PublicEnum { A }',
|
|
229
|
+
'enum PrivateEnum { A }',
|
|
230
|
+
'pub trait PublicTrait { fn run(&self); }',
|
|
231
|
+
'trait PrivateTrait { fn run(&self); }',
|
|
232
|
+
'pub fn public_function(a: u32) -> u32 { a }',
|
|
233
|
+
'fn private_function(a: u32) -> u32 { a }',
|
|
234
|
+
'pub(crate) struct CrateOnly;',
|
|
235
|
+
'pub(in crate) enum Restricted { A }',
|
|
236
|
+
'fn caller() { let value = PublicOne { field: public_function(1) }; }',
|
|
237
|
+
].join('\n'))
|
|
238
|
+
|
|
222
239
|
for (const dispose of disposers.reverse()) dispose()
|
|
223
240
|
|
|
224
241
|
const compactRegistered = []
|
|
@@ -272,12 +289,56 @@ try {
|
|
|
272
289
|
assert.equal(typeof result.value, outputFormat === 'pack' ? 'object' : 'string')
|
|
273
290
|
if (outputFormat === 'pack') assert.ok(JSON.stringify(result.value).length <= 5000)
|
|
274
291
|
}
|
|
292
|
+
await nativeInvoke('code_index', { action: 'build', repoRoot: rustRoot })
|
|
293
|
+
const rustSymbols = await nativeInvoke('code_symbols', { repoRoot: rustRoot })
|
|
294
|
+
assert.ok(rustSymbols.value.some(symbol => symbol.name === 'CrateOnly' && symbol.signature === 'CrateOnly' && !symbol.exported))
|
|
295
|
+
assert.ok(rustSymbols.value.some(symbol => symbol.name === 'Restricted' && symbol.signature === 'Restricted' && !symbol.exported))
|
|
296
|
+
const rustMap = await nativeInvoke('code_map', { repoRoot: rustRoot })
|
|
297
|
+
for (const name of ['PublicOne', 'PublicEnum', 'PublicTrait']) {
|
|
298
|
+
const search = await nativeInvoke('code_search', { query: name, repoRoot: rustRoot })
|
|
299
|
+
assert.equal(search.value[0]?.name, name)
|
|
300
|
+
assert.equal(search.value[0]?.signature, name)
|
|
301
|
+
assert.match(JSON.stringify(search.content), new RegExp(name))
|
|
302
|
+
assert.match(rustMap.value, new RegExp(name))
|
|
303
|
+
const refs = await nativeInvoke('code_refs', { symbol: name, repoRoot: rustRoot })
|
|
304
|
+
assert.match(refs.value, new RegExp(`${name} — repro.rs:`))
|
|
305
|
+
assert.match(refs.value, /callers \(0\)/) // no invented struct-literal/type-use edges
|
|
306
|
+
}
|
|
307
|
+
const rustFunction = await nativeInvoke('code_refs', { symbol: 'public_function', repoRoot: rustRoot })
|
|
308
|
+
assert.match(rustFunction.value, /callers \(1\)/)
|
|
309
|
+
const xrayRoot = path.join(smokeDir, 'xray-demo')
|
|
310
|
+
execFileSync(process.execPath, [path.join(smokeDir, 'node_modules', packageJson.name, 'scripts/impact-xray-demo.mjs'), xrayRoot], { encoding: 'utf8' })
|
|
311
|
+
const legacyChange = await nativeInvoke('code_change_context', { repoRoot: xrayRoot })
|
|
312
|
+
const xray = await nativeInvoke('code_change_context', { repoRoot: xrayRoot, outputFormat: 'xray' })
|
|
313
|
+
assert.equal(typeof legacyChange.value, 'string')
|
|
314
|
+
assert.deepEqual(xray.content, legacyChange.content)
|
|
315
|
+
assert.equal(xray.meta.card, 'impact-xray'); assert.equal(xray.meta.version, 2)
|
|
316
|
+
assert.equal(xray.meta.edges.filter(e => e.kind === 'call').length, 6)
|
|
317
|
+
assert.equal(xray.meta.edges.filter(e => e.kind === 'import').length, 3)
|
|
318
|
+
assert.equal(xray.meta.tests.length, 1)
|
|
319
|
+
assert.deepEqual(new Set(xray.meta.edges.map(e => e.resolution)), new Set(['exact', 'import-scoped', 'name-only']))
|
|
320
|
+
assert.ok(xray.meta.paths.some(p => p.edgeIds.length === 2 && p.resolution === 'exact' && !p.omitted))
|
|
321
|
+
assert.ok(Buffer.byteLength(JSON.stringify(xray.meta)) <= plugin.XRAY_LIMITS.metadataBytes)
|
|
322
|
+
for (const item of [...xray.meta.nodes, ...xray.meta.edges]) {
|
|
323
|
+
const e = item.evidence
|
|
324
|
+
assert.equal(typeof e.code, 'string')
|
|
325
|
+
const lines = readFileSync(path.join(xrayRoot, e.file), 'utf8').split(/\r?\n/)
|
|
326
|
+
assert.equal(e.code, lines.slice(e.startLine - 1, e.startLine - 1 + e.code.split('\n').length).join('\n'))
|
|
327
|
+
}
|
|
328
|
+
console.log('Impact X-Ray installed runtime: default text, call/import/test/path provenance and source windows passed')
|
|
329
|
+
for (const outputFormat of ['text', 'pack']) {
|
|
330
|
+
const result = await nativeInvoke('code_context', {
|
|
331
|
+
task: 'Explain PublicOne', repoRoot: rustRoot, outputFormat, budgetChars: 5000,
|
|
332
|
+
})
|
|
333
|
+
assert.match(JSON.stringify(result.content), /PublicOne/)
|
|
334
|
+
if (outputFormat === 'pack') assert.ok(result.value.items.some(item => item.name === 'PublicOne' && item.code?.includes('pub struct PublicOne')))
|
|
335
|
+
}
|
|
275
336
|
} finally {
|
|
276
337
|
for (const dispose of nativeDisposers.reverse()) dispose()
|
|
277
338
|
await nativeContext.fiber.dispose()
|
|
278
339
|
}
|
|
279
340
|
console.log(`release smoke passed for ${packageJson.name}@${packageJson.version} with dsh-tools@${toolsVersion}`)
|
|
280
341
|
} finally {
|
|
281
|
-
rmSync(smokeDir, { recursive: true, force: true })
|
|
342
|
+
rmSync(smokeDir, { recursive: true, force: true, maxRetries: 3, retryDelay: 100 })
|
|
282
343
|
if (!suppliedTarball) rmSync(tarball, { force: true })
|
|
283
344
|
}
|