feat(sdk): Phase 1 typed query foundation (gsd-sdk query) (#2118)
* feat(sdk): add typed query foundation and gsd-sdk query (Phase 1) Add sdk/src/query registry and handlers with tests, GSDQueryError, CLI query wiring, and supporting type/tool-scoping hooks. Update CHANGELOG. Vitest 4 constructor mock fixes in milestone-runner tests. Made-with: Cursor * chore: gitignore .cursor for local-only Cursor assets Made-with: Cursor * fix(sdk): harden query layer for PR review (paths, locks, CLI, ReDoS) - resolvePathUnderProject: realpath + relative containment for frontmatter and key_links - commitToSubrepo: path checks + sanitizeCommitMessage - statePlannedPhase: readModifyWriteStateMd (lock); MUTATION_COMMANDS + events - key_links: regexForKeyLinkPattern length/ReDoS guard; phase dirs: reject .. and separators - gsd-sdk: strip --pick before parseArgs; strict parser; QueryRegistry.commands() - progress: static GSDError import; tests updated Made-with: Cursor * feat(sdk): query follow-up — tests, QUERY-HANDLERS, registry, locks, intel depth Made-with: Cursor * docs(sdk): use ASCII punctuation in QUERY-HANDLERS.md Made-with: Cursor
This commit is contained in:
@@ -74,27 +74,32 @@ function hashFile(filePath: string): string | null {
|
||||
}
|
||||
}
|
||||
|
||||
function searchJsonEntries(data: unknown, term: string): unknown[] {
|
||||
/** Max recursion depth when walking JSON for intel queries (avoids stack overflow). */
|
||||
export const MAX_JSON_SEARCH_DEPTH = 48;
|
||||
|
||||
export function searchJsonEntries(data: unknown, term: string, depth = 0): unknown[] {
|
||||
const lowerTerm = term.toLowerCase();
|
||||
const results: unknown[] = [];
|
||||
if (depth > MAX_JSON_SEARCH_DEPTH) return results;
|
||||
if (!data || typeof data !== 'object') return results;
|
||||
|
||||
function matchesInValue(value: unknown): boolean {
|
||||
function matchesInValue(value: unknown, d: number): boolean {
|
||||
if (d > MAX_JSON_SEARCH_DEPTH) return false;
|
||||
if (typeof value === 'string') return value.toLowerCase().includes(lowerTerm);
|
||||
if (Array.isArray(value)) return value.some(v => matchesInValue(v));
|
||||
if (value && typeof value === 'object') return Object.values(value as object).some(v => matchesInValue(v));
|
||||
if (Array.isArray(value)) return value.some(v => matchesInValue(v, d + 1));
|
||||
if (value && typeof value === 'object') return Object.values(value as object).some(v => matchesInValue(v, d + 1));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Array.isArray(data)) {
|
||||
for (const entry of data) {
|
||||
if (matchesInValue(entry)) results.push(entry);
|
||||
if (matchesInValue(entry, depth + 1)) results.push(entry);
|
||||
}
|
||||
} else {
|
||||
for (const [, value] of Object.entries(data as object)) {
|
||||
if (Array.isArray(value)) {
|
||||
for (const entry of value) {
|
||||
if (matchesInValue(entry)) results.push(entry);
|
||||
if (matchesInValue(entry, depth + 1)) results.push(entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user