@oscharko-dev/keiko-workspace 0.2.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/dist/.tsbuildinfo +1 -0
- package/dist/binaryDetect.d.ts +6 -0
- package/dist/binaryDetect.d.ts.map +1 -0
- package/dist/binaryDetect.js +20 -0
- package/dist/contextPack.d.ts +24 -0
- package/dist/contextPack.d.ts.map +1 -0
- package/dist/contextPack.js +118 -0
- package/dist/detect.d.ts +5 -0
- package/dist/detect.d.ts.map +1 -0
- package/dist/detect.js +144 -0
- package/dist/discovery.d.ts +10 -0
- package/dist/discovery.d.ts.map +1 -0
- package/dist/discovery.js +199 -0
- package/dist/document-extraction.d.ts +44 -0
- package/dist/document-extraction.d.ts.map +1 -0
- package/dist/document-extraction.js +372 -0
- package/dist/errors.d.ts +3 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +4 -0
- package/dist/fs.d.ts +25 -0
- package/dist/fs.d.ts.map +1 -0
- package/dist/fs.js +69 -0
- package/dist/gitHistory.d.ts +3 -0
- package/dist/gitHistory.d.ts.map +1 -0
- package/dist/gitHistory.js +317 -0
- package/dist/ignore.d.ts +15 -0
- package/dist/ignore.d.ts.map +1 -0
- package/dist/ignore.js +248 -0
- package/dist/importGraph.d.ts +3 -0
- package/dist/importGraph.d.ts.map +1 -0
- package/dist/importGraph.js +131 -0
- package/dist/index.d.ts +27 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +25 -0
- package/dist/paths.d.ts +3 -0
- package/dist/paths.d.ts.map +1 -0
- package/dist/paths.js +38 -0
- package/dist/realpath.d.ts +9 -0
- package/dist/realpath.d.ts.map +1 -0
- package/dist/realpath.js +72 -0
- package/dist/repoSearch.d.ts +46 -0
- package/dist/repoSearch.d.ts.map +1 -0
- package/dist/repoSearch.js +350 -0
- package/dist/repoSearchEntries.d.ts +15 -0
- package/dist/repoSearchEntries.d.ts.map +1 -0
- package/dist/repoSearchEntries.js +106 -0
- package/dist/repoSearchLineSelection.d.ts +18 -0
- package/dist/repoSearchLineSelection.d.ts.map +1 -0
- package/dist/repoSearchLineSelection.js +43 -0
- package/dist/repoSearchMatchers.d.ts +8 -0
- package/dist/repoSearchMatchers.d.ts.map +1 -0
- package/dist/repoSearchMatchers.js +414 -0
- package/dist/repoSearchPolicy.d.ts +34 -0
- package/dist/repoSearchPolicy.d.ts.map +1 -0
- package/dist/repoSearchPolicy.js +342 -0
- package/dist/repoSearchRegexSafety.d.ts +2 -0
- package/dist/repoSearchRegexSafety.d.ts.map +1 -0
- package/dist/repoSearchRegexSafety.js +15 -0
- package/dist/repoSearchScan.d.ts +62 -0
- package/dist/repoSearchScan.d.ts.map +1 -0
- package/dist/repoSearchScan.js +292 -0
- package/dist/retrieval.d.ts +10 -0
- package/dist/retrieval.d.ts.map +1 -0
- package/dist/retrieval.js +74 -0
- package/dist/stableId.d.ts +4 -0
- package/dist/stableId.d.ts.map +1 -0
- package/dist/stableId.js +49 -0
- package/dist/structuralAdapters.d.ts +27 -0
- package/dist/structuralAdapters.d.ts.map +1 -0
- package/dist/structuralAdapters.js +87 -0
- package/dist/summary.d.ts +4 -0
- package/dist/summary.d.ts.map +1 -0
- package/dist/summary.js +54 -0
- package/dist/testSourcePairing.d.ts +3 -0
- package/dist/testSourcePairing.d.ts.map +1 -0
- package/dist/testSourcePairing.js +179 -0
- package/dist/types.d.ts +3 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +4 -0
- package/dist/version.d.ts +2 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +4 -0
- package/package.json +35 -0
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
// Test/source pairing adapter (Epic #177, Issue #180). Deterministic, pure-JS mapper that,
|
|
2
|
+
// given a workspace-relative path or a bare symbol name, locates the paired test (or paired
|
|
3
|
+
// source) file using fixed name conventions. Output normalized to EvidenceAtom via the shared
|
|
4
|
+
// buildAtom helper. Stays within ADR-0019 rule 3b: imports only @oscharko-dev/keiko-contracts,
|
|
5
|
+
// sibling workspace modules, and Node stdlib (node:crypto).
|
|
6
|
+
import { createHash } from "node:crypto";
|
|
7
|
+
import { resolveWithinWorkspace } from "./paths.js";
|
|
8
|
+
import { assertContainedRealPath } from "./realpath.js";
|
|
9
|
+
import { buildAtom, gatherCandidates } from "./repoSearchScan.js";
|
|
10
|
+
// Canonical fingerprint shared by every structural adapter: SHA-256({kind,text}) → 16 hex chars.
|
|
11
|
+
function queryFingerprint(query) {
|
|
12
|
+
const canonical = JSON.stringify({ kind: query.kind, text: query.text });
|
|
13
|
+
return createHash("sha256").update(canonical).digest("hex").slice(0, 16);
|
|
14
|
+
}
|
|
15
|
+
const PATH_EXT_RE = /\.(?:ts|tsx|js|jsx|mjs|cjs|mts|cts)$/;
|
|
16
|
+
const TEST_MARKER_RE = /\.(?:test|spec)\.(?:ts|tsx|js|jsx|mjs|cjs|mts|cts)$/;
|
|
17
|
+
function looksLikePath(text) {
|
|
18
|
+
return PATH_EXT_RE.test(text);
|
|
19
|
+
}
|
|
20
|
+
function extractExtension(path) {
|
|
21
|
+
const match = PATH_EXT_RE.exec(path);
|
|
22
|
+
if (match === null) {
|
|
23
|
+
return undefined;
|
|
24
|
+
}
|
|
25
|
+
return { stem: path.slice(0, match.index), ext: match[0] };
|
|
26
|
+
}
|
|
27
|
+
function basenameOf(stem) {
|
|
28
|
+
const slash = stem.lastIndexOf("/");
|
|
29
|
+
if (slash === -1) {
|
|
30
|
+
return { dir: "", base: stem };
|
|
31
|
+
}
|
|
32
|
+
return { dir: stem.slice(0, slash), base: stem.slice(slash + 1) };
|
|
33
|
+
}
|
|
34
|
+
// Map a source-shaped path to a prioritized list of candidate test paths.
|
|
35
|
+
function candidateTestsFor(path) {
|
|
36
|
+
const parts = extractExtension(path);
|
|
37
|
+
if (parts === undefined) {
|
|
38
|
+
return [];
|
|
39
|
+
}
|
|
40
|
+
const { stem, ext } = parts;
|
|
41
|
+
const { dir, base } = basenameOf(stem);
|
|
42
|
+
const out = [];
|
|
43
|
+
if (stem.startsWith("src/")) {
|
|
44
|
+
out.push(`tests/${stem.slice(4)}.test${ext}`);
|
|
45
|
+
}
|
|
46
|
+
out.push(`${stem}.test${ext}`);
|
|
47
|
+
out.push(`${stem}.spec${ext}`);
|
|
48
|
+
const nestedDir = dir === "" ? "__tests__" : `${dir}/__tests__`;
|
|
49
|
+
out.push(`${nestedDir}/${base}.test${ext}`);
|
|
50
|
+
return out;
|
|
51
|
+
}
|
|
52
|
+
// Map a test-shaped path back to its paired source path. The marker (".test." / ".spec.")
|
|
53
|
+
// and trailing extension are stripped to recover the stem; conventions are inverted to
|
|
54
|
+
// produce candidates in priority order.
|
|
55
|
+
function candidateSourcesFor(path) {
|
|
56
|
+
const match = TEST_MARKER_RE.exec(path);
|
|
57
|
+
if (match === null) {
|
|
58
|
+
return [];
|
|
59
|
+
}
|
|
60
|
+
const stem = path.slice(0, match.index);
|
|
61
|
+
const extWithDot = match[0].slice(match[0].lastIndexOf("."));
|
|
62
|
+
const { dir, base } = basenameOf(stem);
|
|
63
|
+
const out = [];
|
|
64
|
+
if (stem.startsWith("tests/")) {
|
|
65
|
+
out.push(`src/${stem.slice(6)}${extWithDot}`);
|
|
66
|
+
}
|
|
67
|
+
if (dir.endsWith("/__tests__")) {
|
|
68
|
+
out.push(`${dir.slice(0, -"/__tests__".length)}/${base}${extWithDot}`);
|
|
69
|
+
}
|
|
70
|
+
else if (dir === "__tests__") {
|
|
71
|
+
out.push(`${base}${extWithDot}`);
|
|
72
|
+
}
|
|
73
|
+
out.push(`${stem}${extWithDot}`);
|
|
74
|
+
return Array.from(new Set(out));
|
|
75
|
+
}
|
|
76
|
+
function isAllowedPath(ctx, relativePath) {
|
|
77
|
+
return ctx.allowedPaths === undefined || ctx.allowedPaths.has(relativePath);
|
|
78
|
+
}
|
|
79
|
+
function firstExistingPair(ctx, candidates) {
|
|
80
|
+
for (const candidate of candidates) {
|
|
81
|
+
if (!isAllowedPath(ctx, candidate)) {
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
const abs = resolveWithinWorkspace(ctx.scope.workspace.root, candidate);
|
|
85
|
+
assertContainedRealPath(ctx.fs, ctx.scope.workspace.root, abs, "scope");
|
|
86
|
+
if (ctx.fs.exists(abs)) {
|
|
87
|
+
return candidate;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return undefined;
|
|
91
|
+
}
|
|
92
|
+
function emitPairAtom(ctx, pairedPath) {
|
|
93
|
+
return buildAtom({
|
|
94
|
+
scopeId: ctx.scope.scopeId,
|
|
95
|
+
scopePath: pairedPath,
|
|
96
|
+
lineRange: undefined,
|
|
97
|
+
provenanceKind: "structural",
|
|
98
|
+
tool: "test-source-pairing",
|
|
99
|
+
queryFingerprint: ctx.fingerprint,
|
|
100
|
+
score: 0.8,
|
|
101
|
+
emittedAtMs: ctx.nowMs(),
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
function pairForPath(ctx, path) {
|
|
105
|
+
const isTest = TEST_MARKER_RE.test(path);
|
|
106
|
+
const candidates = isTest ? candidateSourcesFor(path) : candidateTestsFor(path);
|
|
107
|
+
const found = firstExistingPair(ctx, candidates);
|
|
108
|
+
return found === undefined ? undefined : emitPairAtom(ctx, found);
|
|
109
|
+
}
|
|
110
|
+
function pathsForSymbol(ctx, symbol, limits) {
|
|
111
|
+
const lowered = symbol.toLowerCase();
|
|
112
|
+
const out = [];
|
|
113
|
+
const files = ctx.allowedPaths === undefined
|
|
114
|
+
? gatherCandidates(ctx.scope, limits, ctx.fs).files.map((file) => file.relativePath)
|
|
115
|
+
: [...ctx.allowedPaths];
|
|
116
|
+
for (const relativePath of files) {
|
|
117
|
+
const parts = extractExtension(relativePath);
|
|
118
|
+
if (parts === undefined) {
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
121
|
+
const base = basenameOf(parts.stem).base.replace(/\.(?:test|spec)$/, "");
|
|
122
|
+
if (base.toLowerCase() === lowered) {
|
|
123
|
+
out.push(relativePath);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return out;
|
|
127
|
+
}
|
|
128
|
+
function allowedPathsForScope(scope, limits, fs) {
|
|
129
|
+
if (scope.relativePaths.length === 0) {
|
|
130
|
+
return undefined;
|
|
131
|
+
}
|
|
132
|
+
return new Set(gatherCandidates(scope, limits, fs).files.map((file) => file.relativePath));
|
|
133
|
+
}
|
|
134
|
+
function inputsForQuery(ctx, query, limits) {
|
|
135
|
+
if (!looksLikePath(query.text)) {
|
|
136
|
+
return pathsForSymbol(ctx, query.text, limits);
|
|
137
|
+
}
|
|
138
|
+
return isAllowedPath(ctx, query.text) ? [query.text] : [];
|
|
139
|
+
}
|
|
140
|
+
export const testSourcePairingAdapter = {
|
|
141
|
+
name: "test-source-pairing",
|
|
142
|
+
isAvailable: () => Promise.resolve(true),
|
|
143
|
+
lookup: (scope, query, limits, fs, deps) => {
|
|
144
|
+
// Wrap the synchronous body so that throws from assertContainedRealPath surface as a
|
|
145
|
+
// rejected Promise (which the registry runner and `await expect(...).rejects.…` tests
|
|
146
|
+
// can observe). Without this wrapper the throw escapes the call site before the Promise
|
|
147
|
+
// is built.
|
|
148
|
+
try {
|
|
149
|
+
return Promise.resolve(runLookup(scope, query, limits, fs, deps));
|
|
150
|
+
}
|
|
151
|
+
catch (err) {
|
|
152
|
+
return Promise.reject(err instanceof Error ? err : new Error(String(err)));
|
|
153
|
+
}
|
|
154
|
+
},
|
|
155
|
+
};
|
|
156
|
+
function runLookup(scope, query, limits, fs, deps) {
|
|
157
|
+
if (query.kind !== "natural-language" && query.kind !== "exact-symbol") {
|
|
158
|
+
return [];
|
|
159
|
+
}
|
|
160
|
+
const ctx = {
|
|
161
|
+
scope,
|
|
162
|
+
fs,
|
|
163
|
+
nowMs: deps?.nowMs ?? Date.now,
|
|
164
|
+
fingerprint: queryFingerprint(query),
|
|
165
|
+
allowedPaths: allowedPathsForScope(scope, limits, fs),
|
|
166
|
+
};
|
|
167
|
+
const inputs = inputsForQuery(ctx, query, limits);
|
|
168
|
+
const atoms = [];
|
|
169
|
+
for (const input of inputs) {
|
|
170
|
+
if (atoms.length >= limits.maxMatchesReturned) {
|
|
171
|
+
break;
|
|
172
|
+
}
|
|
173
|
+
const atom = pairForPath(ctx, input);
|
|
174
|
+
if (atom !== undefined) {
|
|
175
|
+
atoms.push(atom);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
return atoms;
|
|
179
|
+
}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export type { WorkspaceLanguage, TestFramework, WorkspaceInfo, DiscoveredFile, DiscoveryOptions, DiscoveryStats, ReadOptions, FileContent, SelectionReason, ContextRequest, ContextEntry, ContextPack, ContextEntrySummary, ContextPackSummary, WorkspaceSummary, AuditEntry, AuditSummary, } from "@oscharko-dev/keiko-contracts";
|
|
2
|
+
export { DEFAULT_DISCOVERY_OPTIONS, DEFAULT_READ_OPTIONS, SELECTION_REASON_PRIORITY, DEFAULT_CONTEXT_REQUEST, } from "@oscharko-dev/keiko-contracts";
|
|
3
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAIA,YAAY,EACV,iBAAiB,EACjB,aAAa,EACb,aAAa,EACb,cAAc,EACd,gBAAgB,EAChB,cAAc,EACd,WAAW,EACX,WAAW,EACX,eAAe,EACf,cAAc,EACd,YAAY,EACZ,WAAW,EACX,mBAAmB,EACnB,kBAAkB,EAClB,gBAAgB,EAChB,UAAU,EACV,YAAY,GACb,MAAM,+BAA+B,CAAC;AACvC,OAAO,EACL,yBAAyB,EACzB,oBAAoB,EACpB,yBAAyB,EACzB,uBAAuB,GACxB,MAAM,+BAA+B,CAAC"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
// Re-export shim: workspace contract types live in @oscharko-dev/keiko-contracts (issue #158).
|
|
2
|
+
// `verbatimModuleSyntax` is on, so type-only names use `export type` and value-emitting frozen
|
|
3
|
+
// tables use `export`.
|
|
4
|
+
export { DEFAULT_DISCOVERY_OPTIONS, DEFAULT_READ_OPTIONS, SELECTION_REASON_PRIORITY, DEFAULT_CONTEXT_REQUEST, } from "@oscharko-dev/keiko-contracts";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,uBAAuB,EAAG,OAAgB,CAAC"}
|
package/dist/version.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@oscharko-dev/keiko-workspace",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"license": "Apache-2.0",
|
|
6
|
+
"description": "Internal workspace package: Keiko repository context and workspace access — detection, safe reads, path containment, deny/ignore rules, context packs, retrieval, and summaries. Not published independently.",
|
|
7
|
+
"main": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.js"
|
|
13
|
+
},
|
|
14
|
+
"./internal/fs": {
|
|
15
|
+
"types": "./dist/fs.d.ts",
|
|
16
|
+
"import": "./dist/fs.js"
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"scripts": {
|
|
20
|
+
"build": "tsc -b tsconfig.json",
|
|
21
|
+
"typecheck": "tsc -b tsconfig.json",
|
|
22
|
+
"test": "vitest run"
|
|
23
|
+
},
|
|
24
|
+
"files": [
|
|
25
|
+
"dist"
|
|
26
|
+
],
|
|
27
|
+
"sideEffects": false,
|
|
28
|
+
"engines": {
|
|
29
|
+
"node": ">=22"
|
|
30
|
+
},
|
|
31
|
+
"dependencies": {
|
|
32
|
+
"@oscharko-dev/keiko-contracts": "0.2.0",
|
|
33
|
+
"@oscharko-dev/keiko-security": "0.2.0"
|
|
34
|
+
}
|
|
35
|
+
}
|