From b4ae8e91347ec6e48f528cdc5d52a81b6df68ad8 Mon Sep 17 00:00:00 2001 From: John Dvorak Date: Thu, 21 May 2026 20:13:57 -0700 Subject: [PATCH] fix: children($var) domain compilation and resolver support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add children branch in convertDomain() alongside descendants — children() now correctly maps to parentVar: '$parent' instead of falling through to the default branch which reversed the parentVar/selector mapping. - SelectorDomainResolver now handles parentVar domains with no CSS selector filter (children domain collects all registered subject IDs, then filters by parent via ancestor index). - All 1072 tests pass across 14 packages. --- packages/imhotep-dsl/src/compiler.ts | 2 +- packages/imhotep-playwright/src/extraction.ts | 24 ++++++++++++++----- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/packages/imhotep-dsl/src/compiler.ts b/packages/imhotep-dsl/src/compiler.ts index d82c1f6..47212dc 100644 --- a/packages/imhotep-dsl/src/compiler.ts +++ b/packages/imhotep-dsl/src/compiler.ts @@ -938,7 +938,7 @@ function convertDomain(domain: DslDomainRef): DomainRef { // Descendant domains use the first argument as parent and second as filter: // descendants($card, '.title') => parentVar: '$card', selector: '.title' - if (domain.kind === 'descendants') { + if (domain.kind === 'descendants' || domain.kind === 'children') { const parentVar = selectorFromVar ?? domain.selector const selector = extraArgFromVar ?? (domain as any).extraArg return { diff --git a/packages/imhotep-playwright/src/extraction.ts b/packages/imhotep-playwright/src/extraction.ts index f09ee82..6cef112 100644 --- a/packages/imhotep-playwright/src/extraction.ts +++ b/packages/imhotep-playwright/src/extraction.ts @@ -1021,26 +1021,38 @@ export class SelectorDomainResolver implements DomainResolver { }) } + private allRegisteredSubjectIds(): number[] { + const seen = new Set() + for (const d of this.domains.values()) { + for (const id of d.subjectIds) { + seen.add(id) + } + } + return Array.from(seen) + } + resolve(domain: DomainRef, env?: BindingEnv): DomainValue | undefined { if (domain.parentVar) { if (!env || !this.ancestorIndex) return undefined const parentId = env.lookup(domain.parentVar) if (parentId === undefined) return undefined - const selector = domain.selector ?? domain.domain - const globalDomain = this.domains.get(selector) - if (!globalDomain) return undefined + const selector = domain.selector + const candidateIds = selector + ? this.domains.get(selector)?.subjectIds + : this.allRegisteredSubjectIds() + if (!candidateIds || candidateIds.length === 0) return undefined const filtered: number[] = [] - for (const id of globalDomain.subjectIds) { + for (const id of candidateIds) { const ancestors = this.ancestorIndex.get(id) if (ancestors?.has(parentId)) { filtered.push(id) } } - const key = `descendants_p${parentId}_${selector}` + const key = `descendants_p${parentId}_${selector ?? '*'}` return { domainId: `dom_${key}`, subjectIds: new Uint32Array(filtered), - provenance: `descendants(parentId=${parentId}, ${selector})`, + provenance: `descendants(parentId=${parentId}, ${selector ?? '*'})`, closed: true, } }