@hyperframes/inspector 0.8.181 → 0.8.183
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 +2 -5
- package/dist/inspection.js +1 -1
- package/dist/live.js +168 -32
- package/dist/service.js +2774 -4216
- package/dist/types/core/src/mediaTiming.d.ts +1 -41
- package/dist/types/inspector/src/live.d.ts +1 -1
- package/dist/types/inspector/src/mapInspection.d.ts +30 -14
- package/dist/types/inspector/src/service.d.ts +40 -14
- package/dist/types/inspector/src/sources.d.ts +11 -2
- package/package.json +4 -3
package/README.md
CHANGED
|
@@ -42,7 +42,7 @@ The package ships the capability, not agent tools: a harness defines its own too
|
|
|
42
42
|
|
|
43
43
|
## Output
|
|
44
44
|
|
|
45
|
-
The default JSON is capped at 12,000 UTF-8 bytes, including metadata. `
|
|
45
|
+
The default JSON is capped at 12,000 UTF-8 bytes, including metadata. Each snippet's `id` names its file and its start and end lines (`file:start-end`); a snippet also carries its kinds, **unmodified full source lines**, and whether that exact block occurs only once in the file. When a query names more than one target, `targets` gives the input target indices it serves. No pretty-printing or ellipses are inserted into code. Adjacent/overlapping blocks merge across targets.
|
|
46
46
|
|
|
47
47
|
```json
|
|
48
48
|
{
|
|
@@ -51,9 +51,6 @@ The default JSON is capped at 12,000 UTF-8 bytes, including metadata. `snippets`
|
|
|
51
51
|
"snippets": [
|
|
52
52
|
{
|
|
53
53
|
"id": "scene.html:20-24",
|
|
54
|
-
"file": "scene.html",
|
|
55
|
-
"startLine": 20,
|
|
56
|
-
"endLine": 24,
|
|
57
54
|
"kind": ["animation creation"],
|
|
58
55
|
"targets": [0, 1],
|
|
59
56
|
"code": "the exact original source lines, including whitespace",
|
|
@@ -65,7 +62,7 @@ The default JSON is capped at 12,000 UTF-8 bytes, including metadata. `snippets`
|
|
|
65
62
|
|
|
66
63
|
This illustration omits metadata. Actual files in the implementation report show the full output.
|
|
67
64
|
|
|
68
|
-
Shared helpers appear once with all **queried** targets using them. `coTargets` names observed additional DOM targets of the same tween; it does not claim to enumerate all separate calls to a helper. Creation statements and authored caller statements are mapped; small named helper functions are returned whole. Variable assignments are not a stack/data-flow history. A missing source origin increments `unmappedAnimations`, rather than guessing an editable location.
|
|
65
|
+
Shared helpers appear once with all **queried** targets using them. `coTargets` names up to three observed additional DOM targets of the same tween, prioritizing elements outside the query, and `coTargetsOmitted` counts the rest; it does not claim to enumerate all separate calls to a helper. Creation statements and authored caller statements are mapped; small named helper functions are returned whole. Variable assignments are not a stack/data-flow history. A missing source origin increments `unmappedAnimations`, rather than guessing an editable location.
|
|
69
66
|
|
|
70
67
|
HTML snippets are uniquely identified authored opening tags (their full source lines); dynamically generated markup may have no authored tag match. CSS is an authored-selector candidate scan, not proof of active cascade, media-query state, or inherited styles. In particular, a class reused in different scoped scene styles can have multiple candidate source rules.
|
|
71
68
|
|
package/dist/inspection.js
CHANGED
|
@@ -1394,7 +1394,7 @@
|
|
|
1394
1394
|
return timeAtSourceTime(playbackRate, Math.max(0, sourceDuration - mediaStart));
|
|
1395
1395
|
}
|
|
1396
1396
|
|
|
1397
|
-
// ../
|
|
1397
|
+
// ../parsers/src/mediaTiming.ts
|
|
1398
1398
|
var MEDIA_START_BASIS_ATTR = "data-hf-media-start-basis";
|
|
1399
1399
|
function readMediaStartBasis(value) {
|
|
1400
1400
|
return value?.trim().toLowerCase() === "global" ? "global" : "local";
|
package/dist/live.js
CHANGED
|
@@ -2,7 +2,7 @@ import { createRequire } from "node:module"; const require = createRequire(impor
|
|
|
2
2
|
|
|
3
3
|
// src/live.ts
|
|
4
4
|
import { randomUUID } from "node:crypto";
|
|
5
|
-
import { readFileSync as readFileSync2, realpathSync } from "node:fs";
|
|
5
|
+
import { readFileSync as readFileSync2, realpathSync as realpathSync2 } from "node:fs";
|
|
6
6
|
|
|
7
7
|
// src/authoredState.ts
|
|
8
8
|
var PLAIN_CLASS = /^[a-zA-Z_-][\w-]*$/;
|
|
@@ -459,10 +459,11 @@ function formatElementMap(snapshot, { file = "index.html" } = {}) {
|
|
|
459
459
|
}
|
|
460
460
|
|
|
461
461
|
// src/sources.ts
|
|
462
|
-
import { closeSync, fstatSync, openSync, readFileSync, readdirSync } from "node:fs";
|
|
463
|
-
import { extname, join, relative } from "node:path";
|
|
462
|
+
import { closeSync, fstatSync, openSync, readFileSync, readdirSync, realpathSync } from "node:fs";
|
|
463
|
+
import { extname, join, posix, relative, resolve } from "node:path";
|
|
464
464
|
import { createHash as createHash2 } from "node:crypto";
|
|
465
465
|
import { parse as parse2 } from "acorn";
|
|
466
|
+
import { Parser } from "htmlparser2";
|
|
466
467
|
import postcss from "postcss";
|
|
467
468
|
import { TraceMap, originalPositionFor } from "@jridgewell/trace-mapping";
|
|
468
469
|
|
|
@@ -556,6 +557,14 @@ function occurrences(text, needle) {
|
|
|
556
557
|
}
|
|
557
558
|
return hits;
|
|
558
559
|
}
|
|
560
|
+
function realPath(path) {
|
|
561
|
+
try {
|
|
562
|
+
return realpathSync(path);
|
|
563
|
+
} catch {
|
|
564
|
+
return resolve(path);
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
var SHOWN_WHOLE = 2e3;
|
|
559
568
|
var excluded = /* @__PURE__ */ new Set([...PREVIEW_IGNORED_DIRS, ".agents", ".claude", "evidence", "snapshots"]);
|
|
560
569
|
function sourceFiles(project) {
|
|
561
570
|
const files = [];
|
|
@@ -649,7 +658,24 @@ function rawTextElements(text, tag) {
|
|
|
649
658
|
}
|
|
650
659
|
return elements;
|
|
651
660
|
}
|
|
661
|
+
function nesting(text) {
|
|
662
|
+
const parents = /* @__PURE__ */ new Map();
|
|
663
|
+
const open = [];
|
|
664
|
+
const parser = new Parser({
|
|
665
|
+
onopentag() {
|
|
666
|
+
parents.set(parser.startIndex, open.at(-1));
|
|
667
|
+
open.push(parser.startIndex);
|
|
668
|
+
},
|
|
669
|
+
onclosetag() {
|
|
670
|
+
open.pop();
|
|
671
|
+
}
|
|
672
|
+
});
|
|
673
|
+
parser.write(text);
|
|
674
|
+
parser.end();
|
|
675
|
+
return parents;
|
|
676
|
+
}
|
|
652
677
|
function htmlTagsOf(file, text) {
|
|
678
|
+
const parents = nesting(text);
|
|
653
679
|
return [...text.matchAll(/<([a-z][\w-]*)\b[^>]*>/gi)].map((m) => {
|
|
654
680
|
const attr = (name) => {
|
|
655
681
|
const quoted = new RegExp(`\\s${name}\\s*=\\s*["']([^"']*)["']`, "i").exec(m[0]);
|
|
@@ -663,7 +689,11 @@ function htmlTagsOf(file, text) {
|
|
|
663
689
|
tag: m[1].toLowerCase(),
|
|
664
690
|
id: attr("id"),
|
|
665
691
|
hfId: attr("data-hf-id"),
|
|
666
|
-
classes: (attr("class") ?? "").split(/\s+/).filter(Boolean)
|
|
692
|
+
classes: (attr("class") ?? "").split(/\s+/).filter(Boolean),
|
|
693
|
+
compositionId: attr("data-composition-id"),
|
|
694
|
+
compositionSrc: attr("data-composition-src"),
|
|
695
|
+
inMarkup: parents.has(m.index),
|
|
696
|
+
parent: parents.get(m.index)
|
|
667
697
|
};
|
|
668
698
|
});
|
|
669
699
|
}
|
|
@@ -680,11 +710,14 @@ function authoredHtml(project) {
|
|
|
680
710
|
return html;
|
|
681
711
|
}
|
|
682
712
|
var Sources = class {
|
|
683
|
-
constructor(project) {
|
|
713
|
+
constructor(project, served) {
|
|
684
714
|
this.project = project;
|
|
715
|
+
const root = realPath(project);
|
|
716
|
+
const bundled = served && new Set([...served].map((path) => relative(root, realPath(path))));
|
|
685
717
|
for (const path of sourceFiles(project)) {
|
|
686
718
|
const file = relative(project, path);
|
|
687
719
|
if (/(?:^|\/)(?:vendor|lib)\//.test(file)) continue;
|
|
720
|
+
if (bundled && !bundled.has(file)) continue;
|
|
688
721
|
const fd = openSync(path, "r");
|
|
689
722
|
let text;
|
|
690
723
|
try {
|
|
@@ -767,12 +800,69 @@ var Sources = class {
|
|
|
767
800
|
};
|
|
768
801
|
}
|
|
769
802
|
markup(element, target) {
|
|
803
|
+
const tag = this.markupTag(element);
|
|
804
|
+
return tag ? this.slice(tag.file, tag.start, tag.end, "html opening tag", target) : null;
|
|
805
|
+
}
|
|
806
|
+
markupTag(element) {
|
|
770
807
|
const sameTag = this.htmlTags.filter((tag) => tag.tag === element.tag);
|
|
771
808
|
const byHfId = element.hfId ? sameTag.filter((tag) => tag.hfId === element.hfId) : [];
|
|
772
809
|
const tags = byHfId.length ? byHfId : sameTag.filter(
|
|
773
810
|
(tag) => element.id ? tag.id === element.id : element.classes.length > 0 && element.classes.every((c) => tag.classes.includes(c))
|
|
774
811
|
);
|
|
775
|
-
return tags.length === 1 ?
|
|
812
|
+
return tags.length === 1 ? tags[0] : null;
|
|
813
|
+
}
|
|
814
|
+
nestingIndex;
|
|
815
|
+
get nested() {
|
|
816
|
+
if (this.nestingIndex) return this.nestingIndex;
|
|
817
|
+
const tagAt = new Map(this.htmlTags.map((t) => [`${t.file}:${t.start}`, t]));
|
|
818
|
+
const hostsOf = /* @__PURE__ */ new Map();
|
|
819
|
+
for (const host of this.htmlTags.filter((t) => t.inMarkup && t.compositionSrc)) {
|
|
820
|
+
const file = this.mountedFile(host);
|
|
821
|
+
if (file) hostsOf.set(file, [...hostsOf.get(file) ?? [], host]);
|
|
822
|
+
}
|
|
823
|
+
return this.nestingIndex = { tagAt, hostsOf };
|
|
824
|
+
}
|
|
825
|
+
fileByPath;
|
|
826
|
+
mountedFile(host) {
|
|
827
|
+
const files = this.fileByPath ??= new Map(
|
|
828
|
+
[...this.files.keys()].map((file) => [file.replace(/\\/g, "/"), file])
|
|
829
|
+
);
|
|
830
|
+
const src = (host.compositionSrc ?? "").split(/[?#]/)[0].replace(/\\/g, "/");
|
|
831
|
+
const path = [
|
|
832
|
+
posix.normalize(src.replace(/^\.?\//, "")),
|
|
833
|
+
posix.join(posix.dirname(host.file.replace(/\\/g, "/")), src)
|
|
834
|
+
].find((file) => files.has(file));
|
|
835
|
+
return path === void 0 ? void 0 : files.get(path);
|
|
836
|
+
}
|
|
837
|
+
compositions(tag, target) {
|
|
838
|
+
const { tagAt, hostsOf } = this.nested;
|
|
839
|
+
const shown2 = /* @__PURE__ */ new Set([tag]);
|
|
840
|
+
const result = [];
|
|
841
|
+
const show = (t, kind) => {
|
|
842
|
+
if (shown2.has(t)) return false;
|
|
843
|
+
shown2.add(t);
|
|
844
|
+
const snippet = this.slice(t.file, t.start, t.end, kind, target);
|
|
845
|
+
if (snippet) result.push(snippet);
|
|
846
|
+
return true;
|
|
847
|
+
};
|
|
848
|
+
const mounted = tag.compositionSrc && this.mountedFile(tag);
|
|
849
|
+
const root = mounted && this.htmlTags.find(
|
|
850
|
+
(t) => t.file === mounted && t.inMarkup && t.compositionId && (!tag.compositionId || t.compositionId === tag.compositionId)
|
|
851
|
+
);
|
|
852
|
+
if (root) show(root, "composition root");
|
|
853
|
+
const parentOf = (t) => t.parent === void 0 ? void 0 : tagAt.get(`${t.file}:${t.parent}`);
|
|
854
|
+
const climb = [{ tag, candidate: false }];
|
|
855
|
+
for (let from = climb.shift(); from; from = climb.shift()) {
|
|
856
|
+
for (let at2 = parentOf(from.tag); at2; at2 = parentOf(at2))
|
|
857
|
+
if (at2.compositionId)
|
|
858
|
+
show(at2, from.candidate ? "composition root candidate" : "composition root");
|
|
859
|
+
const hosts = hostsOf.get(from.tag.file) ?? [];
|
|
860
|
+
const candidate = from.candidate || hosts.length > 1;
|
|
861
|
+
for (const host of hosts)
|
|
862
|
+
if (show(host, candidate ? "composition host candidate" : "composition host"))
|
|
863
|
+
climb.push({ tag: host, candidate });
|
|
864
|
+
}
|
|
865
|
+
return result;
|
|
776
866
|
}
|
|
777
867
|
mapper(html, provenance) {
|
|
778
868
|
const htmlLines = starts(html);
|
|
@@ -822,17 +912,20 @@ var Sources = class {
|
|
|
822
912
|
const enclosing = hit.chunk.statements.filter((s) => hit.local >= s.start && hit.local < s.end).sort((a, b) => a.end - a.start - (b.end - b.start))[0];
|
|
823
913
|
if (!enclosing) continue;
|
|
824
914
|
const helper = hit.chunk.statements.filter(
|
|
825
|
-
(s) => s.type === "FunctionDeclaration" && hit.local >= s.start && hit.local < s.end && s.end - s.start <=
|
|
915
|
+
(s) => s.type === "FunctionDeclaration" && hit.local >= s.start && hit.local < s.end && s.end - s.start <= SHOWN_WHOLE
|
|
826
916
|
).sort((a, b) => a.end - a.start - (b.end - b.start))[0];
|
|
827
|
-
const
|
|
917
|
+
const caller = result.length > 0;
|
|
918
|
+
const callLine = caller && !helper && enclosing.end - enclosing.start > SHOWN_WHOLE;
|
|
919
|
+
const range = callLine ? { start: hit.local, end: hit.local + 1 } : helper ?? enclosing;
|
|
828
920
|
const snippet = this.slice(
|
|
829
921
|
hit.chunk.file,
|
|
830
922
|
hit.chunk.offset + range.start,
|
|
831
923
|
hit.chunk.offset + range.end,
|
|
832
|
-
|
|
924
|
+
caller ? "animation caller" : "animation creation",
|
|
833
925
|
target
|
|
834
926
|
);
|
|
835
927
|
if (snippet) result.push(snippet);
|
|
928
|
+
if (callLine) continue;
|
|
836
929
|
for (const dependency of hit.chunk.dependencies(range)) {
|
|
837
930
|
const definition = this.slice(
|
|
838
931
|
hit.chunk.file,
|
|
@@ -849,9 +942,6 @@ var Sources = class {
|
|
|
849
942
|
};
|
|
850
943
|
}
|
|
851
944
|
};
|
|
852
|
-
function markupSnippet(sources, element, target) {
|
|
853
|
-
return sources.markup(element, target);
|
|
854
|
-
}
|
|
855
945
|
function mergeSnippets(snippets, sources) {
|
|
856
946
|
const sorted = snippets.sort((a, b) => a.file.localeCompare(b.file) || a.startLine - b.startLine);
|
|
857
947
|
const output = [];
|
|
@@ -874,16 +964,31 @@ function mergeSnippets(snippets, sources) {
|
|
|
874
964
|
}
|
|
875
965
|
|
|
876
966
|
// src/mapInspection.ts
|
|
877
|
-
var
|
|
878
|
-
"
|
|
879
|
-
"
|
|
880
|
-
"
|
|
881
|
-
"
|
|
967
|
+
var KEPT_FIRST_UNDER_BUDGET = [
|
|
968
|
+
"animation creation",
|
|
969
|
+
"animation caller",
|
|
970
|
+
"timeline attachment",
|
|
971
|
+
"html opening tag",
|
|
972
|
+
"animation source dependency",
|
|
973
|
+
"matched CSS candidate"
|
|
882
974
|
];
|
|
975
|
+
var rank = (kind) => {
|
|
976
|
+
const at2 = KEPT_FIRST_UNDER_BUDGET.indexOf(kind);
|
|
977
|
+
return at2 === -1 ? KEPT_FIRST_UNDER_BUDGET.length : at2;
|
|
978
|
+
};
|
|
979
|
+
var priority = (snippet) => Math.min(...snippet.kind.map(rank));
|
|
980
|
+
var byPosition = (a, b) => a.file.localeCompare(b.file) || a.startLine - b.startLine;
|
|
981
|
+
var CO_TARGETS_SHOWN = 3;
|
|
982
|
+
function shown(snippet, targetCount) {
|
|
983
|
+
const { id, kind, code, uniqueInFile, targets } = snippet;
|
|
984
|
+
return targetCount === 1 ? { id, kind, code, uniqueInFile } : { id, kind, targets, code, uniqueInFile };
|
|
985
|
+
}
|
|
883
986
|
function mapInspection(raw, request, context) {
|
|
884
987
|
const { sources, map, catalog, generation, sourceVersion, reused, start, visualState } = context;
|
|
885
988
|
const maxBytes = request.maxBytes ?? 12e3;
|
|
886
989
|
const collected = [];
|
|
990
|
+
const compositions = [];
|
|
991
|
+
const queriedRefs = new Set(raw.results.flatMap((result) => result.elements.map((el) => el.ref)));
|
|
887
992
|
const results = raw.results.map((result, index) => {
|
|
888
993
|
let unmappedAnimations = 0;
|
|
889
994
|
for (const animation of result.animations) {
|
|
@@ -896,8 +1001,11 @@ function mapInspection(raw, request, context) {
|
|
|
896
1001
|
);
|
|
897
1002
|
}
|
|
898
1003
|
for (const el of result.elements) {
|
|
899
|
-
const
|
|
1004
|
+
const tag = sources.markupTag(el);
|
|
1005
|
+
if (!tag) continue;
|
|
1006
|
+
const snippet = sources.slice(tag.file, tag.start, tag.end, "html opening tag", index);
|
|
900
1007
|
if (snippet) collected.push(snippet);
|
|
1008
|
+
compositions.push(...sources.compositions(tag, index));
|
|
901
1009
|
}
|
|
902
1010
|
for (const id of result.css) {
|
|
903
1011
|
const rule = sources.cssRules.find((rule2) => rule2.id === id);
|
|
@@ -912,30 +1020,35 @@ function mapInspection(raw, request, context) {
|
|
|
912
1020
|
if (snippet) collected.push(snippet);
|
|
913
1021
|
}
|
|
914
1022
|
}
|
|
1023
|
+
const coTargets = [
|
|
1024
|
+
...new Map(
|
|
1025
|
+
result.animations.flatMap((animation) => animation.targets).filter((element) => !result.elements.some((own) => own.ref === element.ref)).map((element) => [element.ref, element])
|
|
1026
|
+
).values()
|
|
1027
|
+
].sort((a, b) => Number(queriedRefs.has(a.ref)) - Number(queriedRefs.has(b.ref)));
|
|
915
1028
|
return {
|
|
916
1029
|
target: index,
|
|
917
1030
|
status: result.status,
|
|
918
1031
|
candidates: result.candidates,
|
|
919
1032
|
candidatesOmitted: 0,
|
|
920
1033
|
animations: result.animations.length,
|
|
921
|
-
coTargets:
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
].slice(0, 10),
|
|
1034
|
+
coTargets: coTargets.slice(0, CO_TARGETS_SHOWN),
|
|
1035
|
+
...coTargets.length > CO_TARGETS_SHOWN && {
|
|
1036
|
+
coTargetsOmitted: coTargets.length - CO_TARGETS_SHOWN
|
|
1037
|
+
},
|
|
926
1038
|
unmappedAnimations,
|
|
927
1039
|
scopeTruncated: result.truncated
|
|
928
1040
|
};
|
|
929
1041
|
});
|
|
930
|
-
const merged = mergeSnippets(collected, sources);
|
|
1042
|
+
const merged = withCompositionsLast(mergeSnippets(collected, sources), compositions, sources);
|
|
931
1043
|
for (const snippet of merged) catalog.set(snippet.id, snippet);
|
|
932
|
-
let selected = merged;
|
|
1044
|
+
let selected = [...merged].sort((a, b) => priority(a) - priority(b));
|
|
933
1045
|
if (request.snippets)
|
|
934
1046
|
selected = request.snippets.map((id) => {
|
|
935
1047
|
const snippet = catalog.get(id);
|
|
936
1048
|
if (!snippet) throw new Error(`unknown_snippet: ${id}`);
|
|
937
1049
|
return snippet;
|
|
938
1050
|
});
|
|
1051
|
+
const candidateContext = {};
|
|
939
1052
|
const response = {
|
|
940
1053
|
generation,
|
|
941
1054
|
visualState,
|
|
@@ -946,7 +1059,7 @@ function mapInspection(raw, request, context) {
|
|
|
946
1059
|
omitted: [],
|
|
947
1060
|
omittedCount: 0,
|
|
948
1061
|
nextOffset: null,
|
|
949
|
-
|
|
1062
|
+
...candidateContext,
|
|
950
1063
|
recordLimitReached: raw.recordLimitReached,
|
|
951
1064
|
hooked: raw.hooked,
|
|
952
1065
|
debugAvailable: true,
|
|
@@ -967,23 +1080,46 @@ function mapInspection(raw, request, context) {
|
|
|
967
1080
|
}
|
|
968
1081
|
};
|
|
969
1082
|
trimCandidates(maxBytes / 2, 5);
|
|
1083
|
+
const targetCount = request.targets.length;
|
|
1084
|
+
const kept = [];
|
|
970
1085
|
for (let i = offset; i < selected.length; i++) {
|
|
971
|
-
response.snippets.push(selected[i]);
|
|
1086
|
+
response.snippets.push(shown(selected[i], targetCount));
|
|
1087
|
+
const priorCoverage = response.coverage;
|
|
1088
|
+
if (response.coverage === void 0 && selected[i].kind.some(
|
|
1089
|
+
(kind) => kind.startsWith("composition ") && kind.endsWith(" candidate")
|
|
1090
|
+
))
|
|
1091
|
+
response.coverage = [
|
|
1092
|
+
"Composition candidates are alternative source mounts and their enclosing roots, not a verified runtime instance chain; all alternatives remain available within the snippet budget and pagination."
|
|
1093
|
+
];
|
|
972
1094
|
if (size() > maxBytes - 700) {
|
|
973
1095
|
response.snippets.pop();
|
|
1096
|
+
response.coverage = priorCoverage;
|
|
974
1097
|
response.omittedCount = selected.length - i;
|
|
975
1098
|
response.omitted = selected.slice(i, i + 8).map((item) => ({ id: item.id, bytes: Buffer.byteLength(item.code) }));
|
|
976
1099
|
const nothingFit = response.snippets.length === 0;
|
|
977
1100
|
response.nextOffset = nothingFit ? i + 1 < selected.length ? i + 1 : null : i;
|
|
978
1101
|
break;
|
|
979
1102
|
}
|
|
1103
|
+
kept.push(selected[i]);
|
|
980
1104
|
}
|
|
1105
|
+
if (!request.snippets)
|
|
1106
|
+
response.snippets = kept.sort(byPosition).map((snippet) => shown(snippet, targetCount));
|
|
981
1107
|
trimCandidates(maxBytes, 0);
|
|
982
1108
|
if (Buffer.byteLength(JSON.stringify(response)) > maxBytes)
|
|
983
1109
|
throw new Error("metadata_exceeds_budget: use fewer targets or increase maxBytes");
|
|
984
1110
|
if (request.debug) return { ...response, debug: raw };
|
|
985
1111
|
return response;
|
|
986
1112
|
}
|
|
1113
|
+
function withCompositionsLast(merged, compositions, sources) {
|
|
1114
|
+
const covered = (s) => merged.some((m) => m.file === s.file && m.startLine <= s.startLine && m.endLine >= s.endLine);
|
|
1115
|
+
return [
|
|
1116
|
+
...merged,
|
|
1117
|
+
...mergeSnippets(
|
|
1118
|
+
compositions.filter((s) => !covered(s)),
|
|
1119
|
+
sources
|
|
1120
|
+
)
|
|
1121
|
+
];
|
|
1122
|
+
}
|
|
987
1123
|
|
|
988
1124
|
// src/pageQueries.ts
|
|
989
1125
|
function queryPage(query) {
|
|
@@ -1118,8 +1254,8 @@ var located = (sources) => onePerSourceLine(sources).map((s) => `${s.file}:${s.s
|
|
|
1118
1254
|
function describeValue(value) {
|
|
1119
1255
|
if (value.kind === "unknown") return "?";
|
|
1120
1256
|
if (value.kind === "expression" && value.value === "custom function") return "custom function";
|
|
1121
|
-
const
|
|
1122
|
-
return `${
|
|
1257
|
+
const shown2 = typeof value.value === "number" ? Number(value.value.toFixed(4)) : value.value;
|
|
1258
|
+
return `${shown2}${value.kind === "literal" ? "" : ` (${value.kind})`}`;
|
|
1123
1259
|
}
|
|
1124
1260
|
function describeValues(values) {
|
|
1125
1261
|
return ` authored: ${values.properties.map((p) => `${p.property}: ${describeValue(p.from)} \u2192 ${describeValue(p.to)}`).join("; ")}; ease: ${describeValue(values.ease)}`;
|
|
@@ -1334,8 +1470,8 @@ function assertGeneration(requested, current) {
|
|
|
1334
1470
|
|
|
1335
1471
|
// src/live.ts
|
|
1336
1472
|
var runtime = () => readFileSync2(new URL(`./inspection.${"js"}`, import.meta.url), "utf8");
|
|
1337
|
-
function prepareLiveInspection(projectDir, html, provenance, sourceVersion) {
|
|
1338
|
-
const project =
|
|
1473
|
+
function prepareLiveInspection(projectDir, html, provenance, sourceVersion, reads) {
|
|
1474
|
+
const project = realpathSync2(projectDir);
|
|
1339
1475
|
if (fingerprint2(project) !== sourceVersion) throw new Error("source_changed_during_load");
|
|
1340
1476
|
if (!/<head\b[^>]*>/i.test(html)) throw new Error("preview_head_missing");
|
|
1341
1477
|
const generation = randomUUID();
|
|
@@ -1353,7 +1489,7 @@ ${runtime()}`.replace(/<\/script/gi, "<\\/script");
|
|
|
1353
1489
|
pageGeneration: generation,
|
|
1354
1490
|
sourceContext() {
|
|
1355
1491
|
if (!context) {
|
|
1356
|
-
const sources = new Sources(project);
|
|
1492
|
+
const sources = new Sources(project, reads);
|
|
1357
1493
|
context = { sources, map: sources.mapper(document2, [...provenance]) };
|
|
1358
1494
|
}
|
|
1359
1495
|
return context;
|