@weftlet/toolkit 0.6.1 → 0.7.1
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/README.md +191 -18
- package/THIRD_PARTY_NOTICES.md +3 -1
- package/dist/application/application.d.ts +10 -0
- package/dist/application/application.js +20 -0
- package/dist/application/document-code-bindings.d.ts +11 -0
- package/dist/application/document-code-bindings.js +25 -0
- package/dist/application/document-obligations.d.ts +8 -0
- package/dist/application/document-obligations.js +11 -0
- package/dist/application/draft-facts.d.ts +3 -0
- package/dist/application/draft-facts.js +37 -0
- package/dist/application/extraction-identity.d.ts +2 -2
- package/dist/application/extraction-identity.js +7 -1
- package/dist/application/extractor-results.d.ts +6 -0
- package/dist/application/extractor-results.js +6 -0
- package/dist/application/hydrator.js +38 -18
- package/dist/application/mapping-proposals.d.ts +108 -0
- package/dist/application/mapping-proposals.js +142 -0
- package/dist/application/mapping-service.d.ts +2 -0
- package/dist/application/mapping-service.js +5 -0
- package/dist/application/overlay.js +43 -29
- package/dist/application/saved-code-coverage.d.ts +40 -0
- package/dist/application/saved-code-coverage.js +20 -0
- package/dist/application/saved-compatibility.d.ts +38 -0
- package/dist/application/saved-compatibility.js +20 -0
- package/dist/application/saved-evidence.d.ts +17 -11
- package/dist/application/saved-evidence.js +44 -40
- package/dist/application/saved-exploration.d.ts +57 -0
- package/dist/application/saved-exploration.js +21 -0
- package/dist/application/saved-paths.d.ts +58 -0
- package/dist/application/saved-paths.js +20 -0
- package/dist/application/semantic-run.d.ts +1 -1
- package/dist/extraction/call-syntax.d.ts +18 -0
- package/dist/extraction/call-syntax.js +61 -0
- package/dist/extraction/canonical-document-links.d.ts +5 -1
- package/dist/extraction/canonical-document-links.js +4 -4
- package/dist/extraction/code-inventory.d.ts +6 -0
- package/dist/extraction/code-inventory.js +26 -0
- package/dist/extraction/document-code-bindings.d.ts +14 -0
- package/dist/extraction/document-code-bindings.js +121 -0
- package/dist/extraction/document-obligations.d.ts +12 -0
- package/dist/extraction/document-obligations.js +112 -0
- package/dist/extraction/mapping-candidates.d.ts +13 -0
- package/dist/extraction/mapping-candidates.js +46 -0
- package/dist/extraction/mapping-output.d.ts +75 -0
- package/dist/extraction/mapping-output.js +53 -0
- package/dist/extraction/mapping-plan.d.ts +7 -0
- package/dist/extraction/mapping-plan.js +117 -0
- package/dist/extraction/mapping-prompt.d.ts +2 -0
- package/dist/extraction/mapping-prompt.js +17 -0
- package/dist/extraction/mapping-provider.d.ts +15 -0
- package/dist/extraction/mapping-provider.js +60 -0
- package/dist/extraction/mapping-request.d.ts +3 -0
- package/dist/extraction/mapping-request.js +32 -0
- package/dist/extraction/ontology.d.ts +2 -0
- package/dist/extraction/ontology.js +28 -13
- package/dist/extraction/routing.js +5 -20
- package/dist/extraction/scip-occurrences.d.ts +3 -0
- package/dist/extraction/scip-occurrences.js +102 -0
- package/dist/extraction/scip-ranges.d.ts +6 -0
- package/dist/extraction/scip-ranges.js +31 -0
- package/dist/extraction/scip-runtime.d.ts +39 -0
- package/dist/extraction/scip-runtime.js +8 -0
- package/dist/extraction/scip-symbols.d.ts +21 -0
- package/dist/extraction/scip-symbols.js +91 -0
- package/dist/extraction/semantic.d.ts +1 -1
- package/dist/extraction/semantic.js +5 -17
- package/dist/extraction/static-calls.d.ts +10 -0
- package/dist/extraction/static-calls.js +133 -0
- package/dist/extraction/static.d.ts +3 -20
- package/dist/extraction/static.js +8 -84
- package/dist/extraction/symbol-resolution.d.ts +12 -0
- package/dist/extraction/symbol-resolution.js +51 -0
- package/dist/extraction/symbol-selectors.d.ts +11 -0
- package/dist/extraction/symbol-selectors.js +51 -0
- package/dist/graph/binding-rdf.d.ts +7 -0
- package/dist/graph/binding-rdf.js +109 -0
- package/dist/graph/call-rdf.d.ts +4 -0
- package/dist/graph/call-rdf.js +94 -0
- package/dist/graph/call-shapes.d.ts +2 -0
- package/dist/graph/call-shapes.js +28 -0
- package/dist/graph/code-coverage-projection.d.ts +9 -0
- package/dist/graph/code-coverage-projection.js +117 -0
- package/dist/graph/code-coverage-renderer.d.ts +2 -0
- package/dist/graph/code-coverage-renderer.js +15 -0
- package/dist/graph/code-coverage-request.d.ts +6 -0
- package/dist/graph/code-coverage-request.js +19 -0
- package/dist/graph/code-coverage.d.ts +6 -0
- package/dist/graph/code-coverage.js +54 -0
- package/dist/graph/code-description.d.ts +4 -0
- package/dist/graph/code-description.js +24 -0
- package/dist/graph/code-rdf.d.ts +6 -0
- package/dist/graph/code-rdf.js +79 -0
- package/dist/graph/compatibility-context.d.ts +2 -0
- package/dist/graph/compatibility-context.js +18 -0
- package/dist/graph/compatibility-renderer.d.ts +2 -0
- package/dist/graph/compatibility-renderer.js +45 -0
- package/dist/graph/compatibility-request.d.ts +3 -0
- package/dist/graph/compatibility-request.js +18 -0
- package/dist/graph/compatibility.d.ts +7 -0
- package/dist/graph/compatibility.js +67 -0
- package/dist/graph/constraint-comparison.d.ts +7 -0
- package/dist/graph/constraint-comparison.js +49 -0
- package/dist/graph/coverage.d.ts +2 -2
- package/dist/graph/coverage.js +8 -3
- package/dist/graph/diagnostics.d.ts +1 -1
- package/dist/graph/diagnostics.js +10 -6
- package/dist/graph/evidence-description.d.ts +3 -0
- package/dist/graph/evidence-description.js +13 -0
- package/dist/graph/identity-preflight.js +1 -1
- package/dist/graph/neighborhood-renderer.d.ts +2 -0
- package/dist/graph/neighborhood-renderer.js +38 -0
- package/dist/graph/neighborhood-request.d.ts +9 -0
- package/dist/graph/neighborhood-request.js +37 -0
- package/dist/graph/neighborhood.d.ts +8 -0
- package/dist/graph/neighborhood.js +100 -0
- package/dist/graph/obligation-rdf.d.ts +5 -0
- package/dist/graph/obligation-rdf.js +67 -0
- package/dist/graph/obligation-shapes.d.ts +2 -0
- package/dist/graph/obligation-shapes.js +32 -0
- package/dist/graph/partitioned-shacl.d.ts +14 -0
- package/dist/graph/partitioned-shacl.js +66 -0
- package/dist/graph/path-request.d.ts +11 -0
- package/dist/graph/path-request.js +24 -0
- package/dist/graph/path-search.d.ts +45 -0
- package/dist/graph/path-search.js +101 -0
- package/dist/graph/paths-renderer.d.ts +2 -0
- package/dist/graph/paths-renderer.js +36 -0
- package/dist/graph/paths.d.ts +7 -0
- package/dist/graph/paths.js +74 -0
- package/dist/graph/query.js +23 -6
- package/dist/graph/rdf-partitions.d.ts +24 -0
- package/dist/graph/rdf-partitions.js +170 -0
- package/dist/graph/rdf-projection.d.ts +7 -0
- package/dist/graph/rdf-projection.js +27 -0
- package/dist/graph/rdf-records.d.ts +12 -0
- package/dist/graph/rdf-records.js +90 -0
- package/dist/graph/rdf.d.ts +10 -10
- package/dist/graph/rdf.js +84 -89
- package/dist/graph/shacl-shapes.js +70 -0
- package/dist/graph/snapshot-context.d.ts +3 -0
- package/dist/graph/snapshot-context.js +8 -0
- package/dist/graph/validation-progress.d.ts +4 -1
- package/dist/index.d.ts +23 -0
- package/dist/index.js +22 -0
- package/dist/infrastructure/json-digest.d.ts +3 -0
- package/dist/infrastructure/json-digest.js +3 -0
- package/dist/infrastructure/unique-json.d.ts +2 -0
- package/dist/infrastructure/unique-json.js +20 -0
- package/dist/interfaces/cli.js +33 -1
- package/dist/interfaces/code-coverage.d.ts +46 -0
- package/dist/interfaces/code-coverage.js +18 -0
- package/dist/interfaces/graph-compatibility.d.ts +35 -0
- package/dist/interfaces/graph-compatibility.js +21 -0
- package/dist/interfaces/graph-exploration.d.ts +59 -0
- package/dist/interfaces/graph-exploration.js +27 -0
- package/dist/interfaces/graph-paths.d.ts +77 -0
- package/dist/interfaces/graph-paths.js +31 -0
- package/dist/interfaces/graph-query-args.d.ts +2 -0
- package/dist/interfaces/graph-query-args.js +25 -0
- package/dist/interfaces/mapping-proposals.d.ts +204 -0
- package/dist/interfaces/mapping-proposals.js +74 -0
- package/dist/interfaces/mcp.js +31 -3
- package/dist/storage/mapping-proposals.d.ts +9 -0
- package/dist/storage/mapping-proposals.js +42 -0
- package/dist/storage/storage.d.ts +3 -0
- package/dist/storage/storage.js +37 -13
- package/dist/storage/streaming-json.d.ts +5 -0
- package/dist/storage/streaming-json.js +45 -0
- package/dist/visualization/evidence-sources.d.ts +6 -0
- package/dist/visualization/evidence-sources.js +19 -0
- package/dist/visualization/viewer-interaction.d.ts +5 -8
- package/dist/visualization/viewer-interaction.js +46 -44
- package/dist/visualization/visualization.js +14 -5
- package/docs/1.0-WORK-QUEUE.md +48 -0
- package/docs/ADR-029-exact-graph-neighborhoods.md +70 -0
- package/docs/ADR-030-bounded-evidence-paths.md +97 -0
- package/docs/ADR-031-code-extractor-selection.md +91 -0
- package/docs/ADR-032-scip-code-evidence.md +73 -0
- package/docs/ADR-033-document-symbol-bindings.md +83 -0
- package/docs/ADR-034-mapping-gaps.md +80 -0
- package/docs/ADR-035-static-call-evidence.md +78 -0
- package/docs/ADR-036-contract-obligation-declarations.md +124 -0
- package/docs/ADR-037-selected-contract-compatibility.md +93 -0
- package/docs/ADR-038-monorepo-code-coverage.md +105 -0
- package/docs/ADR-039-mapping-proposals.md +119 -0
- package/docs/ADR-040-bounded-graph-io.md +90 -0
- package/docs/GRAPHIFY-EVALUATION.md +21 -4
- package/docs/PRE-1.0-BASELINE.md +49 -2
- package/docs/RELEASE-1.0-PLAN.md +14 -2
- package/docs/RELEASING.md +3 -3
- package/docs/ROADMAP.md +27 -6
- package/docs/VALIDATION.md +521 -14
- package/docs/benchmarks/large-graph-2026-11-02/result.json +156 -0
- package/docs/extraction-manifest.json +266 -23
- package/package.json +10 -4
- package/templates/context-graph-development/SKILL.md +30 -1
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { mkdtemp, readFile, rm, stat, writeFile } from 'node:fs/promises';
|
|
2
|
+
import { tmpdir } from 'node:os';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { resolveConsumerModule } from '../infrastructure/module-resolution.js';
|
|
5
|
+
import { sha256, stableJson } from '../infrastructure/util.js';
|
|
6
|
+
import { prepareCodexAuthentication } from './codex-auth.js';
|
|
7
|
+
import { resolveCodexInvocation } from './codex-command.js';
|
|
8
|
+
import { runSemanticProcess, SemanticProviderError } from './semantic-process.js';
|
|
9
|
+
import { mappingOutputSchema } from './mapping-output.js';
|
|
10
|
+
import { mappingPrompt } from './mapping-prompt.js';
|
|
11
|
+
import { mappingVersion } from './mapping-request.js';
|
|
12
|
+
export const mappingProviderIdentity = (config) => sha256(stableJson({ version: mappingVersion, provider: config.provider,
|
|
13
|
+
model: config.model, reasoningEffort: config.reasoningEffort, providerModule: config.providerModule,
|
|
14
|
+
providerIdentity: config.providerIdentity, authMode: config.auth?.mode ?? 'chatgpt' }));
|
|
15
|
+
/** Same Codex authentication choice as extraction; one bounded request, with no automatic retry. */
|
|
16
|
+
export function createMappingProvider(config, repositoryRoot, runtime = {}) {
|
|
17
|
+
const identity = mappingProviderIdentity(config);
|
|
18
|
+
return { identity, async propose(input, options) {
|
|
19
|
+
if (config.provider === 'module') {
|
|
20
|
+
let factory;
|
|
21
|
+
try {
|
|
22
|
+
factory = (await import(resolveConsumerModule(config.providerModule, repositoryRoot))).createMappingProposalProvider;
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
throw new SemanticProviderError('unavailable', 'Mapping provider module could not be loaded');
|
|
26
|
+
}
|
|
27
|
+
if (typeof factory !== 'function')
|
|
28
|
+
throw new SemanticProviderError('unavailable', 'Module must export createMappingProposalProvider(config); semantic providers are not implicitly compatible');
|
|
29
|
+
const provider = await factory(config);
|
|
30
|
+
if (provider?.identity !== config.providerIdentity || typeof provider.propose !== 'function')
|
|
31
|
+
throw new SemanticProviderError('unavailable', 'Mapping provider identity/contract mismatch');
|
|
32
|
+
options.signal.throwIfAborted();
|
|
33
|
+
return provider.propose(input, options);
|
|
34
|
+
}
|
|
35
|
+
const command = 'codex';
|
|
36
|
+
const env = await (runtime.authenticate ?? prepareCodexAuthentication)(config.auth, process.env, command, options);
|
|
37
|
+
const invocation = await (runtime.invocation ?? resolveCodexInvocation)(command, env, process.platform, options);
|
|
38
|
+
options.signal.throwIfAborted();
|
|
39
|
+
const root = await mkdtemp(join(tmpdir(), 'weftlet-mapping-'));
|
|
40
|
+
try {
|
|
41
|
+
const schema = join(root, 'schema.json'), output = join(root, 'output.json');
|
|
42
|
+
await writeFile(schema, JSON.stringify(mappingOutputSchema));
|
|
43
|
+
const args = ['exec', '--ephemeral', '--ignore-user-config', '--ignore-rules', '--skip-git-repo-check',
|
|
44
|
+
'--disable', 'shell_tool', '--cd', root, '--sandbox', 'read-only', '--model', config.model,
|
|
45
|
+
'-c', `model_reasoning_effort="${config.reasoningEffort}"`, '--output-schema', schema, '--output-last-message', output, '-'];
|
|
46
|
+
await (runtime.run ?? runSemanticProcess)(invocation.command, [...invocation.prefixArgs, ...args], root, mappingPrompt(input), env, options);
|
|
47
|
+
if ((await stat(output)).size > 131_072)
|
|
48
|
+
throw new SemanticProviderError('invalid-output', 'Mapping output exceeds 128 KiB');
|
|
49
|
+
try {
|
|
50
|
+
return JSON.parse(await readFile(output, 'utf8'));
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
throw new SemanticProviderError('invalid-output', 'Mapping output is not JSON');
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
finally {
|
|
57
|
+
await rm(root, { recursive: true, force: true });
|
|
58
|
+
}
|
|
59
|
+
} };
|
|
60
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export const mappingVersion = 'mapping-proposal-v1';
|
|
2
|
+
export function parseMappingRequest(value) {
|
|
3
|
+
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
4
|
+
throw Error('Mapping request must be an object');
|
|
5
|
+
const item = value;
|
|
6
|
+
if (Object.keys(item).some(key => !['snapshotId', 'ruleId', 'documentPath', 'files', 'maxCandidates', 'maxInputBytes'].includes(key)))
|
|
7
|
+
throw Error('Unknown mapping request field');
|
|
8
|
+
const text = (value, max) => {
|
|
9
|
+
if (typeof value !== 'string' || !value.trim() || value.length > max)
|
|
10
|
+
throw Error('Invalid mapping request string');
|
|
11
|
+
return value;
|
|
12
|
+
};
|
|
13
|
+
const path = (value) => {
|
|
14
|
+
const s = text(value, 8192);
|
|
15
|
+
if (/[:\\\x00-\x1f\x7f]/.test(s) || s.split('/').some(part => !part || ['.', '..', '.git'].includes(part)))
|
|
16
|
+
throw Error('Mapping paths must be repository relative regular source paths');
|
|
17
|
+
return s;
|
|
18
|
+
};
|
|
19
|
+
const limit = (key, fallback, max) => {
|
|
20
|
+
const n = item[key] === undefined ? fallback : item[key];
|
|
21
|
+
if (!Number.isSafeInteger(n) || Number(n) < 1 || Number(n) > max)
|
|
22
|
+
throw Error(`Invalid ${key}; maximum ${max}`);
|
|
23
|
+
return Number(n);
|
|
24
|
+
};
|
|
25
|
+
if (!Array.isArray(item.files) || !item.files.length || item.files.length > 4)
|
|
26
|
+
throw Error('Select between one and four mapping files');
|
|
27
|
+
const files = item.files.map(path).sort();
|
|
28
|
+
if (new Set(files).size !== files.length)
|
|
29
|
+
throw Error('Duplicate mapping file');
|
|
30
|
+
return { snapshotId: text(item.snapshotId, 256), ruleId: text(item.ruleId, 8192), documentPath: path(item.documentPath), files,
|
|
31
|
+
maxCandidates: limit('maxCandidates', 8, 20), maxInputBytes: limit('maxInputBytes', 32_768, 131_072) };
|
|
32
|
+
}
|
|
@@ -29,4 +29,6 @@ export declare class DirectRdfOntologyCompiler implements OntologyCompiler {
|
|
|
29
29
|
constructor(storageRoot: string);
|
|
30
30
|
compile(snapshotId: string, artifacts: readonly SemanticArtifact[]): Promise<OntologyCompileResult>;
|
|
31
31
|
}
|
|
32
|
+
/** Exact legacy stableJson([{content, path}, ...]) digest, including UTF-8 decoding/escaping. */
|
|
33
|
+
export declare function digestRdfFiles(root: string, files: readonly string[]): Promise<string>;
|
|
32
34
|
export {};
|
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
// Modified for standalone extraction; see docs/extraction-manifest.json.
|
|
2
|
-
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { createHash, randomUUID } from 'node:crypto';
|
|
3
|
+
import { createReadStream, createWriteStream } from 'node:fs';
|
|
4
|
+
import { pipeline } from 'node:stream/promises';
|
|
3
5
|
import { withCommittedTree } from "../repository/committed-tree.js";
|
|
4
6
|
import { DataFactory, Store, Writer } from 'n3';
|
|
5
7
|
import { CG, nodeIri } from "../graph/rdf.js";
|
|
6
|
-
import { cp, mkdir, readdir,
|
|
8
|
+
import { cp, mkdir, readdir, rm, writeFile } from 'node:fs/promises';
|
|
7
9
|
import { execFile } from 'node:child_process';
|
|
8
10
|
import { promisify } from 'node:util';
|
|
9
11
|
import { dirname, join, relative, resolve, sep } from 'node:path';
|
|
10
12
|
import { runProcess } from "../infrastructure/process.js";
|
|
11
|
-
import { sha256, stableJson } from "../infrastructure/util.js";
|
|
12
13
|
const probeDocker = async (cwd) => {
|
|
13
14
|
const { stdout } = await promisify(execFile)('docker', ['info', '--format', '{{.OSType}}'], {
|
|
14
15
|
cwd,
|
|
@@ -130,11 +131,7 @@ export class OpenCaesarOntologyCompiler {
|
|
|
130
131
|
const rdfFiles = await listRdfFiles(rdfRoot);
|
|
131
132
|
if (rdfFiles.length === 0)
|
|
132
133
|
throw new Error('openCAESAR produced no RDF files');
|
|
133
|
-
|
|
134
|
-
path: relative(rdfRoot, path),
|
|
135
|
-
content: await readFile(path, 'utf8'),
|
|
136
|
-
})));
|
|
137
|
-
return { root: rdfRoot, rdfFiles, digest: sha256(stableJson(digestParts)) };
|
|
134
|
+
return { root: rdfRoot, rdfFiles, digest: await digestRdfFiles(rdfRoot, rdfFiles) };
|
|
138
135
|
}
|
|
139
136
|
}
|
|
140
137
|
/** Default compiler preserves asserted semantics without Java, Docker, or an OWL reasoner. */
|
|
@@ -152,13 +149,31 @@ export class DirectRdfOntologyCompiler {
|
|
|
152
149
|
}
|
|
153
150
|
}
|
|
154
151
|
const writer = new Writer({ format: 'N-Triples' });
|
|
155
|
-
|
|
156
|
-
const text = await new Promise((accept, reject) => writer.end((error, value) => error ? reject(error) : accept(value)));
|
|
157
|
-
const content = text.split('\n').filter(Boolean).sort().join('\n') + '\n';
|
|
152
|
+
const lines = Array.from(dataset, quad => writer.quadToString(quad.subject, quad.predicate, quad.object, quad.graph)).sort();
|
|
158
153
|
const root = join(this.storageRoot, 'work', `rdf-${randomUUID()}`);
|
|
159
154
|
await mkdir(root, { recursive: true });
|
|
160
155
|
const path = join(root, 'semantic.nt');
|
|
161
|
-
|
|
162
|
-
|
|
156
|
+
const digest = createHash('sha256');
|
|
157
|
+
function* content() {
|
|
158
|
+
// The legacy empty dataset encodes a single newline too.
|
|
159
|
+
for (const line of lines.length ? lines : ['\n']) {
|
|
160
|
+
digest.update(line);
|
|
161
|
+
yield line;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
await pipeline(content(), createWriteStream(path, { flags: 'wx' }));
|
|
165
|
+
return { root, rdfFiles: [path], digest: digest.digest('hex') };
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
/** Exact legacy stableJson([{content, path}, ...]) digest, including UTF-8 decoding/escaping. */
|
|
169
|
+
export async function digestRdfFiles(root, files) {
|
|
170
|
+
const hash = createHash('sha256').update('[');
|
|
171
|
+
for (const [index, path] of files.entries()) {
|
|
172
|
+
hash.update(`${index ? ',' : ''}{"content":"`);
|
|
173
|
+
for await (const chunk of createReadStream(path, { encoding: 'utf8' })) {
|
|
174
|
+
hash.update(JSON.stringify(chunk).slice(1, -1));
|
|
175
|
+
}
|
|
176
|
+
hash.update(`","path":${JSON.stringify(relative(root, path))}}`);
|
|
163
177
|
}
|
|
178
|
+
return hash.update(']').digest('hex');
|
|
164
179
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { posix } from 'node:path';
|
|
2
|
+
import { parseUniqueJson as uniqueJson } from '../infrastructure/unique-json.js';
|
|
2
3
|
import { repositoryFileNode as fileNode } from '@weftlet/core';
|
|
3
4
|
import { sha256, stableJson } from '../infrastructure/util.js';
|
|
4
5
|
export const routingExtractorIdentity = { id: 'routing-declaration-extractor', version: '1.1.0' };
|
|
@@ -20,29 +21,13 @@ function repositoryPath(value) {
|
|
|
20
21
|
}
|
|
21
22
|
/** JSON.parse alone silently accepts duplicate keys, which would hide conflicting declarations. */
|
|
22
23
|
function parseUniqueJson(content, path) {
|
|
23
|
-
let parsed;
|
|
24
24
|
try {
|
|
25
|
-
|
|
25
|
+
return uniqueJson(content);
|
|
26
26
|
}
|
|
27
|
-
catch {
|
|
28
|
-
|
|
27
|
+
catch (error) {
|
|
28
|
+
const reason = error instanceof Error && error.message === 'Duplicate JSON key' ? 'duplicate JSON key' : 'malformed JSON';
|
|
29
|
+
throw new Error(`Invalid routing source ${path}: ${reason}`);
|
|
29
30
|
}
|
|
30
|
-
const objects = [];
|
|
31
|
-
for (const token of content.matchAll(/"(?:\\.|[^"\\])*"|[{}\[\]]/g)) {
|
|
32
|
-
const value = token[0];
|
|
33
|
-
if (value === '{' || value === '[')
|
|
34
|
-
objects.push(value === '{' ? new Set() : undefined);
|
|
35
|
-
else if (value === '}' || value === ']')
|
|
36
|
-
objects.pop();
|
|
37
|
-
else if (/^\s*:/.test(content.slice(token.index + value.length))) {
|
|
38
|
-
const keys = objects.at(-1);
|
|
39
|
-
const key = JSON.parse(value);
|
|
40
|
-
if (keys.has(key))
|
|
41
|
-
throw new Error(`Invalid routing source ${path}: duplicate JSON key ${key}`);
|
|
42
|
-
keys.add(key);
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
return parsed;
|
|
46
31
|
}
|
|
47
32
|
function parseRoutes(content, path) {
|
|
48
33
|
if (Buffer.byteLength(content) > 65_536)
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { type ExtractionContext, type ExtractorIdentity, type GraphFact } from '@weftlet/core';
|
|
2
|
+
import type { ScipIndex } from './scip-runtime.js';
|
|
3
|
+
export declare function extractCodeOccurrences(index: ScipIndex, sources: ReadonlyMap<string, string>, context: ExtractionContext, indexDigest: string, identity: ExtractorIdentity): readonly GraphFact[];
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { codeSymbolNode, repositoryFileNode } from '@weftlet/core';
|
|
2
|
+
import { sha256, stableJson } from '../infrastructure/util.js';
|
|
3
|
+
import { comparePosition, containsRange, rangeText, scipRange } from './scip-ranges.js';
|
|
4
|
+
function readSites(index, sources, commitSha) {
|
|
5
|
+
const result = [];
|
|
6
|
+
for (const document of index.documents) {
|
|
7
|
+
const path = document.relative_path.replaceAll('\\', '/');
|
|
8
|
+
if (!path || path.startsWith('/') || /^[A-Za-z]:/.test(path) ||
|
|
9
|
+
path.split('/').some(part => !part || part === '.' || part === '..'))
|
|
10
|
+
throw new Error('Invalid SCIP repository path');
|
|
11
|
+
const content = sources.get(path);
|
|
12
|
+
if (content === undefined)
|
|
13
|
+
throw new Error(`Missing committed SCIP source: ${path}`);
|
|
14
|
+
const lines = content.split('\n'), digest = sha256(content);
|
|
15
|
+
for (const occurrence of document.occurrences) {
|
|
16
|
+
if (!occurrence.symbol)
|
|
17
|
+
continue;
|
|
18
|
+
if (!Number.isSafeInteger(occurrence.symbol_roles) || occurrence.symbol_roles < 0)
|
|
19
|
+
throw new Error('Invalid SCIP symbol roles');
|
|
20
|
+
const range = scipRange(occurrence.range, lines, path);
|
|
21
|
+
const definition = (occurrence.symbol_roles & 1) !== 0;
|
|
22
|
+
const declarationRange = definition && occurrence.enclosing_range?.length
|
|
23
|
+
? scipRange(occurrence.enclosing_range, lines, path) : undefined;
|
|
24
|
+
// Native extents may cover an arrow initializer after its variable name.
|
|
25
|
+
// Preserve both ranges; containment of the name is not a SCIP invariant.
|
|
26
|
+
result.push({ key: stableJson([occurrence.symbol.startsWith('local ') ? path : '', occurrence.symbol]),
|
|
27
|
+
nativeSymbol: occurrence.symbol, nativeRoles: occurrence.symbol_roles, definition,
|
|
28
|
+
label: rangeText(range, lines), span: { path, ...range, digest, commitSha },
|
|
29
|
+
...(declarationRange ? { declarationRange } : {}) });
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return result;
|
|
33
|
+
}
|
|
34
|
+
function symbolsFor(sites, context, indexDigest) {
|
|
35
|
+
const grouped = new Map();
|
|
36
|
+
for (const site of sites) {
|
|
37
|
+
const group = grouped.get(site.key) ?? [];
|
|
38
|
+
group.push(site);
|
|
39
|
+
grouped.set(site.key, group);
|
|
40
|
+
}
|
|
41
|
+
return new Map([...grouped].map(([key, group]) => {
|
|
42
|
+
const definitions = group.filter(site => site.definition);
|
|
43
|
+
const first = group[0], local = first.nativeSymbol.startsWith('local ');
|
|
44
|
+
const labels = definitions.map(site => site.label).filter(Boolean).sort();
|
|
45
|
+
const node = codeSymbolNode({ schemaVersion: 1, provider: 'scip-typescript', repositoryId: context.repositoryId,
|
|
46
|
+
nativeSymbol: first.nativeSymbol, identityScope: local ? 'index-document' : 'repository',
|
|
47
|
+
...(local ? { documentPath: first.span.path, indexDigest } : {}),
|
|
48
|
+
resolution: definitions.length === 0 ? 'no-indexed-definition' : definitions.every(site => /\.d\.[cm]?ts$/.test(site.span.path))
|
|
49
|
+
? 'declaration-only' : 'indexed-definition' }, labels[0] ?? first.nativeSymbol);
|
|
50
|
+
return [key, node];
|
|
51
|
+
}));
|
|
52
|
+
}
|
|
53
|
+
/** Sweep sorted sites against active native declaration extents, not every declaration in the file. */
|
|
54
|
+
function enclosures(sites, symbols) {
|
|
55
|
+
const result = new Map();
|
|
56
|
+
const files = new Map();
|
|
57
|
+
for (const site of sites) {
|
|
58
|
+
const group = files.get(site.span.path) ?? [];
|
|
59
|
+
group.push(site);
|
|
60
|
+
files.set(site.span.path, group);
|
|
61
|
+
}
|
|
62
|
+
for (const group of files.values()) {
|
|
63
|
+
const ordered = [...group].sort((a, b) => comparePosition(a.span.startLine, a.span.startColumn, b.span.startLine, b.span.startColumn));
|
|
64
|
+
const scopes = group.filter(site => site.declarationRange).sort((a, b) => comparePosition(a.declarationRange.startLine, a.declarationRange.startColumn, b.declarationRange.startLine, b.declarationRange.startColumn));
|
|
65
|
+
let cursor = 0;
|
|
66
|
+
let active = [];
|
|
67
|
+
for (const site of ordered) {
|
|
68
|
+
while (cursor < scopes.length && comparePosition(scopes[cursor].declarationRange.startLine, scopes[cursor].declarationRange.startColumn, site.span.startLine, site.span.startColumn) <= 0)
|
|
69
|
+
active.push(scopes[cursor++]);
|
|
70
|
+
active = active.filter(scope => comparePosition(scope.declarationRange.endLine, scope.declarationRange.endColumn, site.span.startLine, site.span.startColumn) >= 0);
|
|
71
|
+
// A recursive reference is enclosed by its own symbol; its definition is not self-enclosed.
|
|
72
|
+
const candidates = active.filter(scope => !(site.definition && scope.key === site.key) && containsRange(scope.declarationRange, site.span));
|
|
73
|
+
const narrowest = candidates.filter(scope => !candidates.some(other => containsRange(scope.declarationRange, other.declarationRange) && !containsRange(other.declarationRange, scope.declarationRange)));
|
|
74
|
+
const ids = [...new Set(narrowest.map(scope => symbols.get(scope.key).id))];
|
|
75
|
+
result.set(site, ids.length === 1 ? { enclosure: 'native-range', enclosingSymbolId: ids[0] }
|
|
76
|
+
: { enclosure: ids.length ? 'ambiguous' : 'file' });
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return result;
|
|
80
|
+
}
|
|
81
|
+
export function extractCodeOccurrences(index, sources, context, indexDigest, identity) {
|
|
82
|
+
const sites = readSites(index, sources, context.commitSha);
|
|
83
|
+
const symbols = symbolsFor(sites, context, indexDigest);
|
|
84
|
+
const byId = new Map([...symbols.values()].map(symbol => [symbol.id, symbol]));
|
|
85
|
+
const scopes = enclosures(sites, symbols);
|
|
86
|
+
const facts = sites.map(site => {
|
|
87
|
+
const symbol = symbols.get(site.key);
|
|
88
|
+
const scope = scopes.get(site);
|
|
89
|
+
const file = repositoryFileNode(site.span.path);
|
|
90
|
+
const codeOccurrence = { schemaVersion: 1, role: site.definition ? 'definition' : 'reference',
|
|
91
|
+
locationKind: site.span.startLine === site.span.endLine && site.span.startColumn === site.span.endColumn ? 'zero-width-anchor' : 'name',
|
|
92
|
+
nativeRoles: site.nativeRoles, ...scope, ...(site.declarationRange ? { declarationRange: site.declarationRange } : {}) };
|
|
93
|
+
const fact = {
|
|
94
|
+
subject: site.definition ? symbol : scope.enclosingSymbolId ? byId.get(scope.enclosingSymbolId) : file,
|
|
95
|
+
predicate: site.definition ? 'definedBy' : 'references', object: site.definition ? file : symbol,
|
|
96
|
+
provenance: site.span, codeOccurrence, extractor: identity, authority: 'implemented', extractionReview: 'deterministic', confidence: 1,
|
|
97
|
+
};
|
|
98
|
+
return { ...fact, id: sha256(stableJson(fact)), snapshotId: context.snapshotId };
|
|
99
|
+
});
|
|
100
|
+
// Duplicate native entries at the identical target/range/role add no distinct evidence.
|
|
101
|
+
return [...new Map(facts.map(fact => [fact.id, fact])).values()].sort((a, b) => a.id.localeCompare(b.id));
|
|
102
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { CodeRange } from '@weftlet/core';
|
|
2
|
+
export declare function comparePosition(line: number, column: number, otherLine: number, otherColumn: number): number;
|
|
3
|
+
export declare function containsRange(outer: CodeRange, inner: CodeRange): boolean;
|
|
4
|
+
/** SCIP TypeScript uses zero-based UTF-16 positions; graph citations are one-based. */
|
|
5
|
+
export declare function scipRange(raw: readonly number[], lines: readonly string[], path: string): CodeRange;
|
|
6
|
+
export declare function rangeText(range: CodeRange, lines: readonly string[]): string;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export function comparePosition(line, column, otherLine, otherColumn) {
|
|
2
|
+
return line - otherLine || column - otherColumn;
|
|
3
|
+
}
|
|
4
|
+
export function containsRange(outer, inner) {
|
|
5
|
+
return comparePosition(outer.startLine, outer.startColumn, inner.startLine, inner.startColumn) <= 0 &&
|
|
6
|
+
comparePosition(outer.endLine, outer.endColumn, inner.endLine, inner.endColumn) >= 0;
|
|
7
|
+
}
|
|
8
|
+
/** SCIP TypeScript uses zero-based UTF-16 positions; graph citations are one-based. */
|
|
9
|
+
export function scipRange(raw, lines, path) {
|
|
10
|
+
if ((raw.length !== 3 && raw.length !== 4) || raw.some(value => !Number.isSafeInteger(value) || value < 0)) {
|
|
11
|
+
throw new Error(`Invalid SCIP range in ${path}`);
|
|
12
|
+
}
|
|
13
|
+
const startLine = raw[0] + 1;
|
|
14
|
+
const endLine = (raw.length === 3 ? raw[0] : raw[2]) + 1;
|
|
15
|
+
// TypeScript's file reader strips a leading UTF-8 BOM. Citations address the
|
|
16
|
+
// original committed text, whose first line retains that UTF-16 code unit.
|
|
17
|
+
const firstLineOffset = lines[0]?.startsWith('\uFEFF') ? 1 : 0;
|
|
18
|
+
const startColumn = raw[1] + 1 + (startLine === 1 ? firstLineOffset : 0);
|
|
19
|
+
const endColumn = raw.at(-1) + 1 + (endLine === 1 ? firstLineOffset : 0);
|
|
20
|
+
if (startLine > lines.length || endLine > lines.length || startColumn > lines[startLine - 1].length + 1 ||
|
|
21
|
+
endColumn > lines[endLine - 1].length + 1 || comparePosition(startLine, startColumn, endLine, endColumn) > 0) {
|
|
22
|
+
throw new Error(`SCIP range is outside committed source in ${path}`);
|
|
23
|
+
}
|
|
24
|
+
return { startLine, startColumn, endLine, endColumn, columnEncoding: 'utf-16' };
|
|
25
|
+
}
|
|
26
|
+
export function rangeText(range, lines) {
|
|
27
|
+
if (range.startLine === range.endLine)
|
|
28
|
+
return lines[range.startLine - 1].slice(range.startColumn - 1, range.endColumn - 1);
|
|
29
|
+
return [lines[range.startLine - 1].slice(range.startColumn - 1),
|
|
30
|
+
...lines.slice(range.startLine, range.endLine - 1), lines[range.endLine - 1].slice(0, range.endColumn - 1)].join('\n');
|
|
31
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
export interface ScipOccurrence {
|
|
2
|
+
readonly range: readonly number[];
|
|
3
|
+
readonly symbol: string;
|
|
4
|
+
readonly symbol_roles: number;
|
|
5
|
+
readonly enclosing_range?: readonly number[];
|
|
6
|
+
}
|
|
7
|
+
export interface ScipDocument {
|
|
8
|
+
readonly relative_path: string;
|
|
9
|
+
readonly occurrences: readonly ScipOccurrence[];
|
|
10
|
+
readonly symbols: readonly ScipSymbolInformation[];
|
|
11
|
+
}
|
|
12
|
+
interface ScipSymbolInformation {
|
|
13
|
+
documentation: string[];
|
|
14
|
+
}
|
|
15
|
+
export interface ScipIndex {
|
|
16
|
+
readonly documents: readonly ScipDocument[];
|
|
17
|
+
readonly external_symbols: readonly ScipSymbolInformation[];
|
|
18
|
+
readonly metadata?: {
|
|
19
|
+
project_root: string;
|
|
20
|
+
};
|
|
21
|
+
serializeBinary(): Uint8Array;
|
|
22
|
+
}
|
|
23
|
+
interface ScipRuntime {
|
|
24
|
+
readonly scip: {
|
|
25
|
+
readonly Index: {
|
|
26
|
+
deserializeBinary(bytes: Uint8Array): ScipIndex;
|
|
27
|
+
};
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
export declare function loadScipRuntime(): ScipRuntime;
|
|
31
|
+
export interface ScipIndexResult {
|
|
32
|
+
readonly path: string;
|
|
33
|
+
readonly digest: string;
|
|
34
|
+
readonly mode: 'full' | 'package';
|
|
35
|
+
readonly version: string;
|
|
36
|
+
/** Exact document paths observed in the normalized index, including zero-occurrence documents. */
|
|
37
|
+
readonly indexedPaths?: readonly string[];
|
|
38
|
+
}
|
|
39
|
+
export {};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
// Modified for standalone extraction; see docs/extraction-manifest.json.
|
|
2
|
+
import { createRequire } from 'node:module';
|
|
3
|
+
import { dirname, join } from 'node:path';
|
|
4
|
+
export function loadScipRuntime() {
|
|
5
|
+
const require = createRequire(import.meta.url);
|
|
6
|
+
const main = require.resolve('@sourcegraph/scip-typescript');
|
|
7
|
+
return require(join(dirname(main), 'scip.js'));
|
|
8
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type ExtractionContext, type FactExtractor, type GraphFact } from '@weftlet/core';
|
|
2
|
+
import type { GitRepository } from '../repository/git.js';
|
|
3
|
+
import type { ScipIndexResult } from './scip-runtime.js';
|
|
4
|
+
export declare const scipSymbolExtractorIdentity: {
|
|
5
|
+
readonly id: "scip-symbol-extractor";
|
|
6
|
+
readonly version: "2.1.0";
|
|
7
|
+
};
|
|
8
|
+
export declare class ScipSymbolExtractor implements FactExtractor {
|
|
9
|
+
private readonly repository;
|
|
10
|
+
private readonly index;
|
|
11
|
+
readonly identity: {
|
|
12
|
+
readonly id: "scip-symbol-extractor";
|
|
13
|
+
readonly version: "2.1.0";
|
|
14
|
+
};
|
|
15
|
+
constructor(repository: GitRepository, index: ScipIndexResult);
|
|
16
|
+
extract(context: ExtractionContext): Promise<readonly GraphFact[]>;
|
|
17
|
+
extractWithDiagnostics(context: ExtractionContext): Promise<{
|
|
18
|
+
facts: GraphFact[];
|
|
19
|
+
diagnostics: import("@weftlet/core").GraphDiagnostic[];
|
|
20
|
+
}>;
|
|
21
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
// Modified for standalone extraction; see docs/extraction-manifest.json.
|
|
2
|
+
import { readFile } from 'node:fs/promises';
|
|
3
|
+
import { repositoryFileNode } from '@weftlet/core';
|
|
4
|
+
import { loadScipRuntime } from './scip-runtime.js';
|
|
5
|
+
import { extractCodeOccurrences } from './scip-occurrences.js';
|
|
6
|
+
import { extractStaticCalls } from './static-calls.js';
|
|
7
|
+
import { sha256, stableJson } from '../infrastructure/util.js';
|
|
8
|
+
export const scipSymbolExtractorIdentity = { id: 'scip-symbol-extractor', version: '2.1.0' };
|
|
9
|
+
export class ScipSymbolExtractor {
|
|
10
|
+
repository;
|
|
11
|
+
index;
|
|
12
|
+
identity = scipSymbolExtractorIdentity;
|
|
13
|
+
constructor(repository, index) {
|
|
14
|
+
this.repository = repository;
|
|
15
|
+
this.index = index;
|
|
16
|
+
}
|
|
17
|
+
async extract(context) {
|
|
18
|
+
return (await this.extractWithDiagnostics(context)).facts;
|
|
19
|
+
}
|
|
20
|
+
async extractWithDiagnostics(context) {
|
|
21
|
+
const parsed = loadScipRuntime().scip.Index.deserializeBinary(await readFile(this.index.path));
|
|
22
|
+
const symbols = new Map();
|
|
23
|
+
const sources = new Map();
|
|
24
|
+
for (const document of parsed.documents) {
|
|
25
|
+
const path = document.relative_path.replace(/\\/g, '/');
|
|
26
|
+
const content = await this.repository.readFileAt(context.commitSha, path);
|
|
27
|
+
sources.set(path, content);
|
|
28
|
+
const digest = sha256(content);
|
|
29
|
+
const component = repositoryFileNode(path);
|
|
30
|
+
for (const occurrence of document.occurrences) {
|
|
31
|
+
if (!occurrence.symbol || occurrence.symbol.startsWith('local '))
|
|
32
|
+
continue;
|
|
33
|
+
const definition = (occurrence.symbol_roles & 1) === 1;
|
|
34
|
+
const sites = symbols.get(occurrence.symbol) ?? {
|
|
35
|
+
definitions: new Map(),
|
|
36
|
+
references: new Map(),
|
|
37
|
+
};
|
|
38
|
+
symbols.set(occurrence.symbol, sites);
|
|
39
|
+
const target = definition ? sites.definitions : sites.references;
|
|
40
|
+
if (target.has(path))
|
|
41
|
+
continue;
|
|
42
|
+
target.set(path, {
|
|
43
|
+
path,
|
|
44
|
+
line: (occurrence.range[0] ?? 0) + 1,
|
|
45
|
+
digest,
|
|
46
|
+
component,
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
const facts = [];
|
|
51
|
+
for (const [symbolText, sites] of [...symbols.entries()].sort(([left], [right]) => left.localeCompare(right))) {
|
|
52
|
+
const definitionPaths = new Set(sites.definitions.keys());
|
|
53
|
+
const downstream = [...sites.references.values()].filter((site) => !definitionPaths.has(site.path));
|
|
54
|
+
if (sites.definitions.size === 0 || downstream.length === 0)
|
|
55
|
+
continue;
|
|
56
|
+
const symbol = {
|
|
57
|
+
id: `symbol:scip:${sha256(symbolText)}`,
|
|
58
|
+
kind: 'Contract',
|
|
59
|
+
label: symbolText,
|
|
60
|
+
attributes: { scipIndexDigest: this.index.digest },
|
|
61
|
+
};
|
|
62
|
+
const relationships = [
|
|
63
|
+
...[...sites.definitions.values()].map((site) => ({ site, definition: true })),
|
|
64
|
+
...downstream.map((site) => ({ site, definition: false })),
|
|
65
|
+
];
|
|
66
|
+
for (const { site, definition } of relationships) {
|
|
67
|
+
const relation = definition ? 'definition' : 'reference';
|
|
68
|
+
const provenance = {
|
|
69
|
+
path: site.path,
|
|
70
|
+
startLine: site.line,
|
|
71
|
+
endLine: site.line,
|
|
72
|
+
digest: site.digest,
|
|
73
|
+
commitSha: context.commitSha,
|
|
74
|
+
};
|
|
75
|
+
facts.push({
|
|
76
|
+
id: sha256(stableJson({ relation, path: site.path, symbol: symbolText, provenance })),
|
|
77
|
+
subject: site.component,
|
|
78
|
+
predicate: definition ? 'contains' : 'consumes',
|
|
79
|
+
object: symbol,
|
|
80
|
+
provenance,
|
|
81
|
+
extractor: this.identity,
|
|
82
|
+
confidence: 1,
|
|
83
|
+
snapshotId: context.snapshotId,
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
const occurrences = extractCodeOccurrences(parsed, sources, context, this.index.digest, this.identity);
|
|
88
|
+
const calls = extractStaticCalls(sources, occurrences, context);
|
|
89
|
+
return { facts: [...facts, ...occurrences, ...calls.facts], diagnostics: calls.diagnostics };
|
|
90
|
+
}
|
|
91
|
+
}
|
|
@@ -14,7 +14,7 @@ export interface ExtractedAssertion {
|
|
|
14
14
|
readonly kind: GraphNodeKind;
|
|
15
15
|
readonly label: string;
|
|
16
16
|
};
|
|
17
|
-
readonly predicate: GraphPredicate
|
|
17
|
+
readonly predicate: Exclude<GraphPredicate, 'calls'>;
|
|
18
18
|
readonly object: {
|
|
19
19
|
readonly id: string;
|
|
20
20
|
readonly kind: GraphNodeKind;
|
|
@@ -7,6 +7,9 @@ export { semanticCommandEnvironment } from './codex-auth.js';
|
|
|
7
7
|
import { resolveCodexInvocation } from './codex-command.js';
|
|
8
8
|
import { runSemanticProcess, SemanticProviderError } from "./semantic-process.js";
|
|
9
9
|
export { SemanticProviderError } from "./semantic-process.js";
|
|
10
|
+
import { graphPredicates } from "@weftlet/core";
|
|
11
|
+
// Calls require indexed syntax/proofs. Keep the prose schema and its cache identity unchanged.
|
|
12
|
+
const predicates = graphPredicates.filter(predicate => predicate !== 'calls');
|
|
10
13
|
import { sha256, stableJson } from "../infrastructure/util.js";
|
|
11
14
|
import { canonicalizeRepositoryDocumentNode } from './repository-nodes.js';
|
|
12
15
|
import { semanticArtifactKey, semanticArtifactOutputDigest, semanticProviderIdentity } from './semantic-identity.js';
|
|
@@ -79,6 +82,8 @@ function renderOml(key, assertions) {
|
|
|
79
82
|
const instances = [...nodes.entries()]
|
|
80
83
|
.sort(([left], [right]) => left.localeCompare(right))
|
|
81
84
|
.map(([id, node]) => {
|
|
85
|
+
if (node.kind === 'CodeSymbol')
|
|
86
|
+
throw new Error('Code symbols require deterministic code extraction');
|
|
82
87
|
const local = localNames.get(id);
|
|
83
88
|
const properties = [
|
|
84
89
|
` cg:hasIdentifier "${omlEscape(node.id)}"`,
|
|
@@ -119,23 +124,6 @@ const kinds = [
|
|
|
119
124
|
'PersistenceModel',
|
|
120
125
|
'Scope',
|
|
121
126
|
];
|
|
122
|
-
const predicates = [
|
|
123
|
-
'realizes',
|
|
124
|
-
'implementedBy',
|
|
125
|
-
'consumes',
|
|
126
|
-
'emits',
|
|
127
|
-
'persistsTo',
|
|
128
|
-
'governedBy',
|
|
129
|
-
'definedBy',
|
|
130
|
-
'verifiedBy',
|
|
131
|
-
'operationallyOwnedBy',
|
|
132
|
-
'decisionAuthority',
|
|
133
|
-
'routesThrough',
|
|
134
|
-
'authorizedBy',
|
|
135
|
-
'sourcedFrom',
|
|
136
|
-
'references',
|
|
137
|
-
'contains',
|
|
138
|
-
];
|
|
139
127
|
export function semanticOutputSchema(lineCount) {
|
|
140
128
|
const node = {
|
|
141
129
|
type: 'object',
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type ExtractionContext, type GraphDiagnostic, type GraphFact } from '@weftlet/core';
|
|
2
|
+
export declare const staticCallIdentity: {
|
|
3
|
+
readonly id: "typescript-call-extractor";
|
|
4
|
+
readonly version: "1.0.0";
|
|
5
|
+
};
|
|
6
|
+
/** Parse only the exact committed documents already indexed by SCIP. No second resolver or name guessing. */
|
|
7
|
+
export declare function extractStaticCalls(sources: ReadonlyMap<string, string>, codeFacts: readonly GraphFact[], context: ExtractionContext): {
|
|
8
|
+
facts: GraphFact[];
|
|
9
|
+
diagnostics: GraphDiagnostic[];
|
|
10
|
+
};
|