mirror of
https://github.com/priyanshujain/sanderling.git
synced 2026-10-04 20:17:09 +00:00
feat(verifier): track extractor value transitions
Cache each extractor's prior and current JSON-encoded value during PushSnapshot; expose ChangedExtractors to surface per-step diffs the runner can emit into the trace. The first observation flushes every non-null extractor as a change so the inspect UI shows initial state breadcrumbs alongside later transitions.
This commit is contained in:
1 parent
07c292913c
commit
419a2d564e
2 files changed
+54
No files matched your search
@@ -45,3 +45,11 @@ type Exception struct {
|
|||||||
StackTrace string
|
StackTrace string
|
||||||
UnixMillis int64
|
UnixMillis int64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ExtractorChange records a single extractor's value transition across one
|
||||||
|
// step. Used to surface "what changed at this step" breadcrumbs at violation
|
||||||
|
// markers in the inspect UI.
|
||||||
|
type ExtractorChange struct {
|
||||||
|
Prev []byte
|
||||||
|
Curr []byte
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
package verifier
|
package verifier
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
@@ -242,10 +243,55 @@ func (v *Verifier) PushSnapshot(input SnapshotInput) error {
|
|||||||
return fmt.Errorf("extractor %d: %w", index, err)
|
return fmt.Errorf("extractor %d: %w", index, err)
|
||||||
}
|
}
|
||||||
_ = extractor.handle.Set("current", newValue)
|
_ = extractor.handle.Set("current", newValue)
|
||||||
|
extractor.prev = extractor.curr
|
||||||
|
extractor.curr = encodeExtractorValue(newValue)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// encodeExtractorValue produces a stable JSON encoding of an extractor's
|
||||||
|
// current value for diff comparison. goja values that don't survive Export
|
||||||
|
// (e.g. wrapped host functions) yield nil; callers treat nil as "unknown" and
|
||||||
|
// emit no diff entry.
|
||||||
|
func encodeExtractorValue(value goja.Value) []byte {
|
||||||
|
if value == nil || goja.IsUndefined(value) || goja.IsNull(value) {
|
||||||
|
return []byte("null")
|
||||||
|
}
|
||||||
|
exported := value.Export()
|
||||||
|
body, err := json.Marshal(exported)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return body
|
||||||
|
}
|
||||||
|
|
||||||
|
// ChangedExtractors returns the named extractors whose value changed between
|
||||||
|
// the prior PushSnapshot and the current one. The map is keyed by extractor
|
||||||
|
// name; unnamed extractors (extractor_N fallback) are included so the inspect
|
||||||
|
// UI can still display them under a numeric label. The very first snapshot
|
||||||
|
// emits every non-null extractor as a change (Prev=null, Curr=current) since
|
||||||
|
// the runner can otherwise misread "no diff yet" as "nothing initialized".
|
||||||
|
func (v *Verifier) ChangedExtractors() map[string]ExtractorChange {
|
||||||
|
changes := map[string]ExtractorChange{}
|
||||||
|
for _, extractor := range v.extractors {
|
||||||
|
if extractor.curr == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
prev := extractor.prev
|
||||||
|
if prev == nil {
|
||||||
|
prev = []byte("null")
|
||||||
|
}
|
||||||
|
if bytes.Equal(prev, extractor.curr) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
changes[extractor.name] = ExtractorChange{
|
||||||
|
Prev: append([]byte(nil), prev...),
|
||||||
|
Curr: append([]byte(nil), extractor.curr...),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return changes
|
||||||
|
}
|
||||||
|
|
||||||
// OverrideExtractorValues replaces each extractor's `current` slot with a
|
// OverrideExtractorValues replaces each extractor's `current` slot with a
|
||||||
// caller-supplied value, keyed by registration index. Used by the web tick
|
// caller-supplied value, keyed by registration index. Used by the web tick
|
||||||
// path so extractor bodies that ran in V8 (against the real DOM) drive the
|
// path so extractor bodies that ran in V8 (against the real DOM) drive the
|
||||||
|
|||||||
Reference in new issue
Block a user