@descryy/runtime-identity-grounding 0.2.0 → 0.4.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/element-identity.d.ts +41 -78
- package/dist/element-identity.d.ts.map +1 -1
- package/dist/element-identity.js +69 -64
- package/dist/element-identity.js.map +1 -1
- package/dist/fiber-probe.d.ts +25 -45
- package/dist/fiber-probe.d.ts.map +1 -1
- package/dist/fiber-probe.js +45 -84
- package/dist/fiber-probe.js.map +1 -1
- package/dist/framework-aware-grounder.d.ts +35 -0
- package/dist/framework-aware-grounder.d.ts.map +1 -0
- package/dist/framework-aware-grounder.js +58 -0
- package/dist/framework-aware-grounder.js.map +1 -0
- package/dist/framework-detector.d.ts +46 -0
- package/dist/framework-detector.d.ts.map +1 -0
- package/dist/framework-detector.js +89 -0
- package/dist/framework-detector.js.map +1 -0
- package/dist/graph-join.d.ts +18 -49
- package/dist/graph-join.d.ts.map +1 -1
- package/dist/graph-join.js +17 -42
- package/dist/graph-join.js.map +1 -1
- package/dist/identity-grounder.d.ts +12 -31
- package/dist/identity-grounder.d.ts.map +1 -1
- package/dist/identity-grounder.js +13 -26
- package/dist/identity-grounder.js.map +1 -1
- package/dist/identity-grounding.d.ts +20 -39
- package/dist/identity-grounding.d.ts.map +1 -1
- package/dist/identity-grounding.js +27 -44
- package/dist/identity-grounding.js.map +1 -1
- package/dist/index.d.ts +11 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -6
- package/dist/index.js.map +1 -1
- package/dist/owner-stack.d.ts +36 -65
- package/dist/owner-stack.d.ts.map +1 -1
- package/dist/owner-stack.js +33 -62
- package/dist/owner-stack.js.map +1 -1
- package/dist/vue-identity-grounder.d.ts +23 -0
- package/dist/vue-identity-grounder.d.ts.map +1 -0
- package/dist/vue-identity-grounder.js +28 -0
- package/dist/vue-identity-grounder.js.map +1 -0
- package/dist/vue-identity-grounding.d.ts +16 -0
- package/dist/vue-identity-grounding.d.ts.map +1 -0
- package/dist/vue-identity-grounding.js +110 -0
- package/dist/vue-identity-grounding.js.map +1 -0
- package/dist/vue-probe.d.ts +49 -0
- package/dist/vue-probe.d.ts.map +1 -0
- package/dist/vue-probe.js +95 -0
- package/dist/vue-probe.js.map +1 -0
- package/package.json +5 -5
package/dist/owner-stack.js
CHANGED
|
@@ -1,12 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* React 19's replacement for `_debugSource`, and the
|
|
3
|
-
* needs
|
|
2
|
+
* React 19's replacement for `_debugSource`, and the rule this package
|
|
3
|
+
* needs to read it.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* Measured directly, in a real Chromium, against React 18.3.1 and 19.2.8
|
|
8
|
-
* development bundles of the same authored source
|
|
9
|
-
* (`fixture-apps/react-identity`):
|
|
5
|
+
* Measured in a real Chromium, React 18.3.1 vs 19.2.8 dev bundles of the
|
|
6
|
+
* same source (`fixture-apps/react-identity`):
|
|
10
7
|
*
|
|
11
8
|
* | fiber field | React 18.3.1 dev | React 19.2.8 dev | either, production |
|
|
12
9
|
* | --- | --- | --- | --- |
|
|
@@ -15,63 +12,40 @@
|
|
|
15
12
|
* | `_debugOwner` | present | present | absent |
|
|
16
13
|
* | `type.name` | authored | authored | minified |
|
|
17
14
|
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
* not cosmetic: one is the application telling us where it came from, the
|
|
25
|
-
* other is us translating through a file that can be missing or stale.
|
|
26
|
-
*
|
|
27
|
-
* ## The rule: drop React's own element-creation frame, and only that
|
|
28
|
-
*
|
|
29
|
-
* The captured stack's innermost frame is React's own JSX entry point:
|
|
30
|
-
*
|
|
31
|
-
* ```text
|
|
32
|
-
* Error: react-stack-top-frame
|
|
33
|
-
* at exports.jsxDEV (http://…/dev.js:21690:32)
|
|
34
|
-
* at PayButton (http://…/dev.js:21714:62) <- the site we want
|
|
35
|
-
* at Object.react_stack_bottom_frame (…)
|
|
36
|
-
* ```
|
|
37
|
-
*
|
|
38
|
-
* The second frame is the JSX call site inside the owning component, which
|
|
39
|
-
* is exactly the fact React 18's `_debugSource` carries. Verified against
|
|
40
|
-
* the fixture: React 18 reports `src/app.jsx:20`, and mapping React 19's
|
|
41
|
-
* second frame through the bundle's source map lands on `src/app.jsx:20`
|
|
42
|
-
* as well. `react-fixtures.test.ts` asserts that equality rather than
|
|
43
|
-
* asserting each major separately, because two independently-written
|
|
44
|
-
* expectations can both be wrong in the same direction.
|
|
15
|
+
* React 19 didn't move the source location, it **deleted** it and left a
|
|
16
|
+
* captured stack whose frames point into *compiled* output — costing a
|
|
17
|
+
* source-map resolution React 18 doesn't need, and reporting a different
|
|
18
|
+
* `SourceLocation.reliability` (`self-contained` vs `side-artifact-resolved`):
|
|
19
|
+
* one is the app telling us where it came from, the other is us
|
|
20
|
+
* translating through a file that can be missing or stale.
|
|
45
21
|
*
|
|
46
|
-
*
|
|
22
|
+
* The rule: drop React's own element-creation frame (the stack's innermost
|
|
23
|
+
* frame, e.g. `exports.jsxDEV`), and only that — the second frame is the
|
|
24
|
+
* JSX call site inside the owning component, the same fact React 18's
|
|
25
|
+
* `_debugSource` carries. Verified against the fixture: both majors land on
|
|
26
|
+
* `src/app.jsx:20` (`react-fixtures.test.ts` asserts the equality, not each
|
|
27
|
+
* major separately, since two independent expectations can be wrong the
|
|
28
|
+
* same way).
|
|
47
29
|
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
* The rule used instead is by name, against a closed list of React's own
|
|
57
|
-
* element-creation entry points, and it **refuses** when the innermost
|
|
58
|
-
* frame is not one of them. A refusal here costs a source location; a
|
|
59
|
-
* positional guess that is off by one costs a confident wrong file and
|
|
60
|
-
* line, attributed to a component that did not create the element. Rule 2.
|
|
30
|
+
* Not `stripInitiatorWrapperFrame`'s positional rule: nothing here captured
|
|
31
|
+
* this stack, React did, so "deterministically innermost" doesn't apply —
|
|
32
|
+
* and `createV8StackTraceParser` omits unparseable frames, so "first frame
|
|
33
|
+
* of the parsed trace" isn't reliably "first frame of the raw text" either.
|
|
34
|
+
* Instead: match by name against a closed list of React's element-creation
|
|
35
|
+
* entry points, and **refuse** when the innermost frame isn't one of them.
|
|
36
|
+
* A refusal costs a source location; an off-by-one positional guess costs a
|
|
37
|
+
* confident wrong file/line attributed to the wrong component (rule 2).
|
|
61
38
|
*/
|
|
62
39
|
/**
|
|
63
40
|
* React's own element-creation entry points, matched against the **last
|
|
64
|
-
* dotted segment** of a frame's function name so
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
* `
|
|
69
|
-
* production build has no `_debugStack` to reach this code with);
|
|
70
|
-
* `createElement` / `createElementWithValidation` the classic runtime's.
|
|
41
|
+
* dotted segment** of a frame's function name so `exports.jsxDEV` and bare
|
|
42
|
+
* `jsxDEV` are the same frame. `jsxDEV`/`jsxDEVImpl` are the automatic
|
|
43
|
+
* runtime's dev entry; `jsx`/`jsxs` its production ones (listed for
|
|
44
|
+
* completeness — production has no `_debugStack` to reach this code with);
|
|
45
|
+
* `createElement`/`createElementWithValidation` the classic runtime's.
|
|
71
46
|
*
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
* which is exactly the thing that may not resolve.
|
|
47
|
+
* A closed list, not a "lives inside react-dom" heuristic — that needs a
|
|
48
|
+
* resolved path, exactly the thing that may not resolve.
|
|
75
49
|
*/
|
|
76
50
|
export const REACT_ELEMENT_CREATION_FRAMES = [
|
|
77
51
|
"jsxDEV",
|
|
@@ -86,10 +60,7 @@ function lastSegment(functionName) {
|
|
|
86
60
|
const parts = functionName.split(".");
|
|
87
61
|
return parts[parts.length - 1] ?? functionName;
|
|
88
62
|
}
|
|
89
|
-
/**
|
|
90
|
-
* Index of the frame that is the JSX call site inside the owning component,
|
|
91
|
-
* or a refusal naming why the stack could not be read that way.
|
|
92
|
-
*/
|
|
63
|
+
/** Index of the frame that is the JSX call site inside the owning component, or a refusal naming why the stack couldn't be read that way. */
|
|
93
64
|
export function selectOwnerFrame(stack) {
|
|
94
65
|
const first = stack.frames[0];
|
|
95
66
|
if (first === undefined) {
|
package/dist/owner-stack.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"owner-stack.js","sourceRoot":"","sources":["../src/owner-stack.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"owner-stack.js","sourceRoot":"","sources":["../src/owner-stack.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AAIH;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,6BAA6B,GAAG;IAC3C,QAAQ;IACR,YAAY;IACZ,mBAAmB;IACnB,KAAK;IACL,MAAM;IACN,eAAe;IACf,6BAA6B;CACrB,CAAC;AAMX,SAAS,WAAW,CAAC,YAAoB;IACvC,MAAM,KAAK,GAAG,YAAY,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACtC,OAAO,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,IAAI,YAAY,CAAC;AACjD,CAAC;AAED,6IAA6I;AAC7I,MAAM,UAAU,gBAAgB,CAAC,KAAiB;IAChD,MAAM,KAAK,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;IAC9B,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QACxB,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,yFAAyF,EAAE,CAAC;IAC1H,CAAC;IAED,MAAM,YAAY,GAAG,KAAK,CAAC,QAAQ,CAAC,YAAY,CAAC;IACjD,IAAI,YAAY,KAAK,IAAI,EAAE,CAAC;QAC1B,OAAO;YACL,EAAE,EAAE,KAAK;YACT,MAAM,EACJ,wGAAwG;gBACxG,uGAAuG;gBACvG,iFAAiF;SACpF,CAAC;IACJ,CAAC;IAED,IAAI,CAAE,6BAAmD,CAAC,QAAQ,CAAC,WAAW,CAAC,YAAY,CAAC,CAAC,EAAE,CAAC;QAC9F,OAAO;YACL,EAAE,EAAE,KAAK;YACT,MAAM,EACJ,uDAAuD,YAAY,qCAAqC;gBACxG,kCAAkC,6BAA6B,CAAC,IAAI,CAAC,IAAI,CAAC,kCAAkC;gBAC5G,sGAAsG;gBACtG,yBAAyB;SAC5B,CAAC;IACJ,CAAC;IAED,IAAI,KAAK,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC5B,OAAO;YACL,EAAE,EAAE,KAAK;YACT,MAAM,EACJ,2GAA2G;gBAC3G,+FAA+F;SAClG,CAAC;IACJ,CAAC;IAED,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC;AAChC,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The composed, page-driving surface for Vue — install once, then ask about
|
|
3
|
+
* a selector. Mirrors `identity-grounder.ts`'s shape exactly (`install`/
|
|
4
|
+
* `ground`) so a caller consuming both frameworks can hold one interface
|
|
5
|
+
* and switch grounders per page, without a framework-specific branch
|
|
6
|
+
* anywhere above this module.
|
|
7
|
+
*
|
|
8
|
+
* Nothing to compose here beyond the probe and the decision layer — Vue's
|
|
9
|
+
* answer needs no source-map resolver, no stack parser, no `resolveSourceRoot`
|
|
10
|
+
* (`vue-identity-grounding.ts`'s own header says why: file-level only, by
|
|
11
|
+
* construction). That is the whole reason this file is a fifth of
|
|
12
|
+
* `identity-grounder.ts`'s size rather than a second copy of it.
|
|
13
|
+
*/
|
|
14
|
+
import type { Page } from "playwright-core";
|
|
15
|
+
import type { ElementIdentity } from "./element-identity.ts";
|
|
16
|
+
export interface VueIdentityGrounder {
|
|
17
|
+
/** Installs the page-side reader into every document this page loads from now on. Call before navigating — a page navigated first reports `probe-not-installed`. */
|
|
18
|
+
install(page: Page): Promise<void>;
|
|
19
|
+
/** Resolves one on-screen element to the component that produced it, and to that component's authored file where it can be established. Never throws for a routine unavailability; refuses by name instead. */
|
|
20
|
+
ground(page: Page, selector: string): Promise<ElementIdentity>;
|
|
21
|
+
}
|
|
22
|
+
export declare function createVueIdentityGrounder(): VueIdentityGrounder;
|
|
23
|
+
//# sourceMappingURL=vue-identity-grounder.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vue-identity-grounder.d.ts","sourceRoot":"","sources":["../src/vue-identity-grounder.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAE5C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAI7D,MAAM,WAAW,mBAAmB;IAClC,oKAAoK;IACpK,OAAO,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACnC,+MAA+M;IAC/M,MAAM,CAAC,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC,CAAC;CAChE;AAED,wBAAgB,yBAAyB,IAAI,mBAAmB,CAW/D"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The composed, page-driving surface for Vue — install once, then ask about
|
|
3
|
+
* a selector. Mirrors `identity-grounder.ts`'s shape exactly (`install`/
|
|
4
|
+
* `ground`) so a caller consuming both frameworks can hold one interface
|
|
5
|
+
* and switch grounders per page, without a framework-specific branch
|
|
6
|
+
* anywhere above this module.
|
|
7
|
+
*
|
|
8
|
+
* Nothing to compose here beyond the probe and the decision layer — Vue's
|
|
9
|
+
* answer needs no source-map resolver, no stack parser, no `resolveSourceRoot`
|
|
10
|
+
* (`vue-identity-grounding.ts`'s own header says why: file-level only, by
|
|
11
|
+
* construction). That is the whole reason this file is a fifth of
|
|
12
|
+
* `identity-grounder.ts`'s size rather than a second copy of it.
|
|
13
|
+
*/
|
|
14
|
+
import { createVueIdentityGroundingProbe } from "./vue-probe.js";
|
|
15
|
+
import { groundFromVueProbe } from "./vue-identity-grounding.js";
|
|
16
|
+
export function createVueIdentityGrounder() {
|
|
17
|
+
const probe = createVueIdentityGroundingProbe();
|
|
18
|
+
return {
|
|
19
|
+
install(page) {
|
|
20
|
+
return probe.install(page);
|
|
21
|
+
},
|
|
22
|
+
async ground(page, selector) {
|
|
23
|
+
const raw = await probe.probe(page, selector);
|
|
24
|
+
return groundFromVueProbe(raw, selector);
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=vue-identity-grounder.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vue-identity-grounder.js","sourceRoot":"","sources":["../src/vue-identity-grounder.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAKH,OAAO,EAAE,+BAA+B,EAAE,MAAM,gBAAgB,CAAC;AACjE,OAAO,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AASjE,MAAM,UAAU,yBAAyB;IACvC,MAAM,KAAK,GAAG,+BAA+B,EAAE,CAAC;IAChD,OAAO;QACL,OAAO,CAAC,IAAU;YAChB,OAAO,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QAC7B,CAAC;QACD,KAAK,CAAC,MAAM,CAAC,IAAU,EAAE,QAAgB;YACvC,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,KAAK,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;YAC9C,OAAO,kBAAkB,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;QAC3C,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The decision layer for Vue: raw probe facts in, one `ElementIdentity` out
|
|
3
|
+
* — same contract `identity-grounding.ts`'s `groundFromProbe` holds for
|
|
4
|
+
* React, on the same output vocabulary, so a caller that already branches
|
|
5
|
+
* on `ElementIdentity.outcome` needs no second code path to consume this.
|
|
6
|
+
*
|
|
7
|
+
* Simpler than the React decision layer by construction, not by omission:
|
|
8
|
+
* Vue's runtime gives this package one fact per element (`type.__file`,
|
|
9
|
+
* file-level, no line) rather than React 18's file+line or React 19's
|
|
10
|
+
* compiled-stack-needing-resolution — there is no owner-stack equivalent to
|
|
11
|
+
* inject a resolver for, so this function takes no second argument.
|
|
12
|
+
*/
|
|
13
|
+
import { type ElementIdentity } from "./element-identity.ts";
|
|
14
|
+
import type { RawVueProbe } from "./vue-probe.ts";
|
|
15
|
+
export declare function groundFromVueProbe(probe: RawVueProbe, selector: string): ElementIdentity;
|
|
16
|
+
//# sourceMappingURL=vue-identity-grounding.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vue-identity-grounding.d.ts","sourceRoot":"","sources":["../src/vue-identity-grounding.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAiB,KAAK,eAAe,EAA0B,MAAM,uBAAuB,CAAC;AACpG,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAElD,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,GAAG,eAAe,CAkHxF"}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The decision layer for Vue: raw probe facts in, one `ElementIdentity` out
|
|
3
|
+
* — same contract `identity-grounding.ts`'s `groundFromProbe` holds for
|
|
4
|
+
* React, on the same output vocabulary, so a caller that already branches
|
|
5
|
+
* on `ElementIdentity.outcome` needs no second code path to consume this.
|
|
6
|
+
*
|
|
7
|
+
* Simpler than the React decision layer by construction, not by omission:
|
|
8
|
+
* Vue's runtime gives this package one fact per element (`type.__file`,
|
|
9
|
+
* file-level, no line) rather than React 18's file+line or React 19's
|
|
10
|
+
* compiled-stack-needing-resolution — there is no owner-stack equivalent to
|
|
11
|
+
* inject a resolver for, so this function takes no second argument.
|
|
12
|
+
*/
|
|
13
|
+
import { capabilityFor } from "./element-identity.js";
|
|
14
|
+
export function groundFromVueProbe(probe, selector) {
|
|
15
|
+
if (probe.status === "probe-not-installed") {
|
|
16
|
+
return {
|
|
17
|
+
outcome: "refused",
|
|
18
|
+
selector,
|
|
19
|
+
fiberDistance: 0,
|
|
20
|
+
refusal: "probe-not-installed",
|
|
21
|
+
observedTypeName: null,
|
|
22
|
+
reason: "the Vue identity-grounding reader is not present in this document. `page.addInitScript` only affects " +
|
|
23
|
+
"documents loaded after it is registered, so `install()` must be called before navigating.",
|
|
24
|
+
capability: capabilityFor("unavailable", "the page-side reader was never installed in this document; nothing was inspected."),
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
if (probe.status === "element-not-found") {
|
|
28
|
+
return {
|
|
29
|
+
outcome: "refused",
|
|
30
|
+
selector,
|
|
31
|
+
fiberDistance: 0,
|
|
32
|
+
refusal: "element-not-found",
|
|
33
|
+
observedTypeName: null,
|
|
34
|
+
reason: `no element matched "${selector}" in the page's current document.`,
|
|
35
|
+
capability: capabilityFor("unavailable", "the selector matched no element; there was nothing to ground."),
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
if (probe.status === "no-marker") {
|
|
39
|
+
return {
|
|
40
|
+
outcome: "refused",
|
|
41
|
+
selector,
|
|
42
|
+
fiberDistance: 0,
|
|
43
|
+
refusal: "vue-component-marker-absent",
|
|
44
|
+
observedTypeName: null,
|
|
45
|
+
reason: `neither the element matched by "${selector}" nor any of its ancestors carries a __vueParentComponent ` +
|
|
46
|
+
`marker (${probe.ownKeys.length === 0 ? "the element has no own enumerable keys at all" : `own keys: ${probe.ownKeys.join(", ")}`}). ` +
|
|
47
|
+
"Measured against a real Vue 3 build (fixture-apps/vue-identity): this page was not produced by Vue at " +
|
|
48
|
+
"all, OR it was produced by a Vue build where this marker was compiled out — Vue does not distinguish " +
|
|
49
|
+
"the two the way React's still-present-but-stripped production fiber does, so both readings are named " +
|
|
50
|
+
"together rather than one being guessed.",
|
|
51
|
+
capability: capabilityFor("unavailable", "no __vueParentComponent marker on the element or any ancestor."),
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
const { name, file } = probe.component;
|
|
55
|
+
if (name === null) {
|
|
56
|
+
return {
|
|
57
|
+
outcome: "refused",
|
|
58
|
+
selector,
|
|
59
|
+
fiberDistance: probe.componentDistance,
|
|
60
|
+
refusal: "anonymous-component",
|
|
61
|
+
observedTypeName: null,
|
|
62
|
+
reason: "a Vue component instance was found and its options object carries no `name` — an anonymous component " +
|
|
63
|
+
"definition. Refused rather than reported as nameless: there is no symbol for `resolveSymbolNode` to " +
|
|
64
|
+
"look up and no name a reader could act on.",
|
|
65
|
+
capability: capabilityFor("unavailable", "the owning component has no `name`."),
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
const component = { name, nameSource: "vue-options-name", kind: "vue-options" };
|
|
69
|
+
if (file === null) {
|
|
70
|
+
return {
|
|
71
|
+
outcome: "component-only",
|
|
72
|
+
selector,
|
|
73
|
+
fiberDistance: probe.componentDistance,
|
|
74
|
+
component,
|
|
75
|
+
componentSource: "vue-parent-component",
|
|
76
|
+
// Vue's instance tree carries no runtime-observable creation-vs-render
|
|
77
|
+
// split the way React's `_debugOwner` vs `return` chain does — see
|
|
78
|
+
// `COMPONENT_SOURCES`'s own doc. Never a guessed second answer.
|
|
79
|
+
renderedBy: null,
|
|
80
|
+
reason: `the component "${name}" was found and it carries no \`__file\` — either it was not compiled by a tool ` +
|
|
81
|
+
"that writes one (`@vitejs/plugin-vue`/`vue-loader`'s devtools convention), or that tool's dev-only " +
|
|
82
|
+
"annotation was stripped for this build. The component is named; its file is not.",
|
|
83
|
+
capability: capabilityFor("degraded", "no `__file` on the component's options object."),
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
return {
|
|
87
|
+
outcome: "grounded",
|
|
88
|
+
selector,
|
|
89
|
+
fiberDistance: probe.componentDistance,
|
|
90
|
+
component,
|
|
91
|
+
componentSource: "vue-parent-component",
|
|
92
|
+
renderedBy: null,
|
|
93
|
+
sourceLocation: {
|
|
94
|
+
file,
|
|
95
|
+
// Vue's runtime records no creation line anywhere this package can
|
|
96
|
+
// read — not a missed case, the real ceiling of what `__file` alone
|
|
97
|
+
// can answer. See `vue-options-file`'s own doc.
|
|
98
|
+
line: null,
|
|
99
|
+
column: null,
|
|
100
|
+
functionName: name,
|
|
101
|
+
reliability: "self-contained",
|
|
102
|
+
resolvedVia: null,
|
|
103
|
+
},
|
|
104
|
+
authoredSource: true,
|
|
105
|
+
mechanism: "vue-options-file",
|
|
106
|
+
capability: capabilityFor("degraded", "`__file` named the authored file, but Vue's runtime records no creation line to pair with it, so the " +
|
|
107
|
+
"answer is file-level rather than line-level."),
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
//# sourceMappingURL=vue-identity-grounding.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vue-identity-grounding.js","sourceRoot":"","sources":["../src/vue-identity-grounding.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The page-side half of Vue support: reads the marker Vue itself puts on a
|
|
3
|
+
* DOM node and returns **raw facts only** — same discipline as
|
|
4
|
+
* `fiber-probe.ts`, and deliberately a separate module rather than a branch
|
|
5
|
+
* inside it, so the React reader's own tests and this one never share a
|
|
6
|
+
* probe function that could silently start doing both jobs at once.
|
|
7
|
+
*
|
|
8
|
+
* `el.__vueParentComponent` is Vue 3's own internal marker, set when the
|
|
9
|
+
* element is mounted — measured (see `fixture-apps/vue-identity`) present
|
|
10
|
+
* on an unminified dev-mode bundle and **absent entirely** on a minified
|
|
11
|
+
* bundle with devtools support compiled out. That is a real difference from
|
|
12
|
+
* React: a React production fiber is still there, just stripped of
|
|
13
|
+
* `_debug*` fields, so "no fiber" and "production build" are
|
|
14
|
+
* distinguishable there. Here they are not — see
|
|
15
|
+
* `vue-component-marker-absent`'s own doc in `element-identity.ts`.
|
|
16
|
+
*
|
|
17
|
+
* Injected the same way `fiber-probe.ts` is: `page.addInitScript` with a
|
|
18
|
+
* string body (no DOM lib in this repo's `tsconfig.base.json`), installed
|
|
19
|
+
* before the first navigation or the page reports `probe-not-installed`.
|
|
20
|
+
*/
|
|
21
|
+
import type { Page } from "playwright-core";
|
|
22
|
+
export declare const VUE_IDENTITY_GROUNDING_BINDING = "__descryVueIdentityGrounding";
|
|
23
|
+
export interface RawVueComponentDescriptor {
|
|
24
|
+
readonly name: string | null;
|
|
25
|
+
/** `type.__file`, verbatim — the marker `@vitejs/plugin-vue`/`vue-loader` write for their own "open in editor" devtools feature. Null when the options object carries none. */
|
|
26
|
+
readonly file: string | null;
|
|
27
|
+
}
|
|
28
|
+
export type RawVueProbe = {
|
|
29
|
+
readonly status: "probe-not-installed";
|
|
30
|
+
} | {
|
|
31
|
+
readonly status: "element-not-found";
|
|
32
|
+
readonly selector: string;
|
|
33
|
+
} | {
|
|
34
|
+
readonly status: "no-marker";
|
|
35
|
+
readonly selector: string;
|
|
36
|
+
/** Capped own-keys list, same audit purpose `fiber-probe.ts`'s carries. */
|
|
37
|
+
readonly ownKeys: readonly string[];
|
|
38
|
+
} | {
|
|
39
|
+
readonly status: "component";
|
|
40
|
+
readonly selector: string;
|
|
41
|
+
readonly componentDistance: number;
|
|
42
|
+
readonly component: RawVueComponentDescriptor;
|
|
43
|
+
};
|
|
44
|
+
export interface VueIdentityGroundingProbe {
|
|
45
|
+
install(page: Page): Promise<void>;
|
|
46
|
+
probe(page: Page, selector: string): Promise<RawVueProbe>;
|
|
47
|
+
}
|
|
48
|
+
export declare function createVueIdentityGroundingProbe(): VueIdentityGroundingProbe;
|
|
49
|
+
//# sourceMappingURL=vue-probe.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vue-probe.d.ts","sourceRoot":"","sources":["../src/vue-probe.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAE5C,eAAO,MAAM,8BAA8B,iCAAiC,CAAC;AAE7E,MAAM,WAAW,yBAAyB;IACxC,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,+KAA+K;IAC/K,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B;AAED,MAAM,MAAM,WAAW,GACnB;IAAE,QAAQ,CAAC,MAAM,EAAE,qBAAqB,CAAA;CAAE,GAC1C;IAAE,QAAQ,CAAC,MAAM,EAAE,mBAAmB,CAAC;IAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GACnE;IACE,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC;IAC7B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,2EAA2E;IAC3E,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;CACrC,GACD;IACE,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC;IAC7B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAC;IACnC,QAAQ,CAAC,SAAS,EAAE,yBAAyB,CAAC;CAC/C,CAAC;AAEN,MAAM,WAAW,yBAAyB;IACxC,OAAO,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACnC,KAAK,CAAC,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;CAC3D;AAED,wBAAgB,+BAA+B,IAAI,yBAAyB,CAS3E"}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The page-side half of Vue support: reads the marker Vue itself puts on a
|
|
3
|
+
* DOM node and returns **raw facts only** — same discipline as
|
|
4
|
+
* `fiber-probe.ts`, and deliberately a separate module rather than a branch
|
|
5
|
+
* inside it, so the React reader's own tests and this one never share a
|
|
6
|
+
* probe function that could silently start doing both jobs at once.
|
|
7
|
+
*
|
|
8
|
+
* `el.__vueParentComponent` is Vue 3's own internal marker, set when the
|
|
9
|
+
* element is mounted — measured (see `fixture-apps/vue-identity`) present
|
|
10
|
+
* on an unminified dev-mode bundle and **absent entirely** on a minified
|
|
11
|
+
* bundle with devtools support compiled out. That is a real difference from
|
|
12
|
+
* React: a React production fiber is still there, just stripped of
|
|
13
|
+
* `_debug*` fields, so "no fiber" and "production build" are
|
|
14
|
+
* distinguishable there. Here they are not — see
|
|
15
|
+
* `vue-component-marker-absent`'s own doc in `element-identity.ts`.
|
|
16
|
+
*
|
|
17
|
+
* Injected the same way `fiber-probe.ts` is: `page.addInitScript` with a
|
|
18
|
+
* string body (no DOM lib in this repo's `tsconfig.base.json`), installed
|
|
19
|
+
* before the first navigation or the page reports `probe-not-installed`.
|
|
20
|
+
*/
|
|
21
|
+
export const VUE_IDENTITY_GROUNDING_BINDING = "__descryVueIdentityGrounding";
|
|
22
|
+
export function createVueIdentityGroundingProbe() {
|
|
23
|
+
return {
|
|
24
|
+
async install(page) {
|
|
25
|
+
await page.addInitScript({ content: probeScriptSource() });
|
|
26
|
+
},
|
|
27
|
+
probe(page, selector) {
|
|
28
|
+
return page.evaluate(probeCallSource(selector));
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
/** Self-invoking, selector serialised in — same reasoning as `fiber-probe.ts`'s `probeCallSource`. */
|
|
33
|
+
function probeCallSource(selector) {
|
|
34
|
+
return `(() => {
|
|
35
|
+
const grounding = window[${JSON.stringify(VUE_IDENTITY_GROUNDING_BINDING)}];
|
|
36
|
+
if (grounding === undefined || typeof grounding.probe !== "function") {
|
|
37
|
+
return { status: "probe-not-installed" };
|
|
38
|
+
}
|
|
39
|
+
return grounding.probe(${JSON.stringify(selector)});
|
|
40
|
+
})()`;
|
|
41
|
+
}
|
|
42
|
+
/** Cap on the reported own-keys list — same bound `fiber-probe.ts` uses, same reason. */
|
|
43
|
+
const MAX_REPORTED_OWN_KEYS = 32;
|
|
44
|
+
function probeScriptSource() {
|
|
45
|
+
return `(() => {
|
|
46
|
+
function describeInstance(instance) {
|
|
47
|
+
const type = instance && instance.type;
|
|
48
|
+
if (type === null || type === undefined) return { name: null, file: null };
|
|
49
|
+
return {
|
|
50
|
+
name: typeof type.name === "string" && type.name.length > 0 ? type.name : null,
|
|
51
|
+
file: typeof type.__file === "string" && type.__file.length > 0 ? type.__file : null,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
window[${JSON.stringify(VUE_IDENTITY_GROUNDING_BINDING)}] = {
|
|
56
|
+
probe: function (selector) {
|
|
57
|
+
let element;
|
|
58
|
+
try {
|
|
59
|
+
element = document.querySelector(selector);
|
|
60
|
+
} catch (_error) {
|
|
61
|
+
return { status: "element-not-found", selector: selector };
|
|
62
|
+
}
|
|
63
|
+
if (element === null) return { status: "element-not-found", selector: selector };
|
|
64
|
+
|
|
65
|
+
let node = element;
|
|
66
|
+
let distance = 0;
|
|
67
|
+
let instance = null;
|
|
68
|
+
while (node !== null && node !== undefined) {
|
|
69
|
+
if (node.__vueParentComponent !== undefined && node.__vueParentComponent !== null) {
|
|
70
|
+
instance = node.__vueParentComponent;
|
|
71
|
+
break;
|
|
72
|
+
}
|
|
73
|
+
node = node.parentElement;
|
|
74
|
+
distance++;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (instance === null) {
|
|
78
|
+
return {
|
|
79
|
+
status: "no-marker",
|
|
80
|
+
selector: selector,
|
|
81
|
+
ownKeys: Object.keys(element).slice(0, ${MAX_REPORTED_OWN_KEYS}),
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return {
|
|
86
|
+
status: "component",
|
|
87
|
+
selector: selector,
|
|
88
|
+
componentDistance: distance,
|
|
89
|
+
component: describeInstance(instance),
|
|
90
|
+
};
|
|
91
|
+
},
|
|
92
|
+
};
|
|
93
|
+
})();`;
|
|
94
|
+
}
|
|
95
|
+
//# sourceMappingURL=vue-probe.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vue-probe.js","sourceRoot":"","sources":["../src/vue-probe.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAIH,MAAM,CAAC,MAAM,8BAA8B,GAAG,8BAA8B,CAAC;AA6B7E,MAAM,UAAU,+BAA+B;IAC7C,OAAO;QACL,KAAK,CAAC,OAAO,CAAC,IAAU;YACtB,MAAM,IAAI,CAAC,aAAa,CAAC,EAAE,OAAO,EAAE,iBAAiB,EAAE,EAAE,CAAC,CAAC;QAC7D,CAAC;QACD,KAAK,CAAC,IAAU,EAAE,QAAgB;YAChC,OAAO,IAAI,CAAC,QAAQ,CAAc,eAAe,CAAC,QAAQ,CAAC,CAAC,CAAC;QAC/D,CAAC;KACF,CAAC;AACJ,CAAC;AAED,sGAAsG;AACtG,SAAS,eAAe,CAAC,QAAgB;IACvC,OAAO;6BACoB,IAAI,CAAC,SAAS,CAAC,8BAA8B,CAAC;;;;2BAIhD,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC;KAC9C,CAAC;AACN,CAAC;AAED,yFAAyF;AACzF,MAAM,qBAAqB,GAAG,EAAE,CAAC;AAEjC,SAAS,iBAAiB;IACxB,OAAO;;;;;;;;;;WAUE,IAAI,CAAC,SAAS,CAAC,8BAA8B,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;mDA0BN,qBAAqB;;;;;;;;;;;;MAYlE,CAAC;AACP,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@descryy/runtime-identity-grounding",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"description": "Identity grounding: resolves an on-screen DOM element back to the React component that produced it, and to that component's authored file and line. Injects through @descryy/runtime-browser's existing page.addInitScript seam; refuses by name on a production
|
|
5
|
+
"description": "Identity grounding: resolves an on-screen DOM element back to the React or Vue component that produced it, and to that component's authored file (and, for React, line). createFrameworkAwareGrounder detects which framework actually rendered the page (React's fiber marker, then Vue's component marker, else a named refusal) and grounds through the matching mechanism -- never a default to React. Injects through @descryy/runtime-browser's existing page.addInitScript seam; refuses by name on a production build rather than guessing. Consumed by descry-core's @descryy/mcp (packages/mcp/src/browser/playwright-driver.ts) and descry-desktop's @descry/session (packages/session/src/pipeline/browser-drive.ts).",
|
|
6
6
|
"license": "UNLICENSED",
|
|
7
7
|
"engines": {
|
|
8
8
|
"node": ">=22.5"
|
|
@@ -24,9 +24,9 @@
|
|
|
24
24
|
"build": "tsc -b"
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"@descryy/runtime-contracts": "0.
|
|
28
|
-
"@descryy/runtime-adapter-typescript": "0.
|
|
29
|
-
"@descryy/runtime-browser": "0.
|
|
27
|
+
"@descryy/runtime-contracts": "0.3.3",
|
|
28
|
+
"@descryy/runtime-adapter-typescript": "0.3.3",
|
|
29
|
+
"@descryy/runtime-browser": "0.4.4",
|
|
30
30
|
"playwright-core": "^1.62.1"
|
|
31
31
|
},
|
|
32
32
|
"devDependencies": {
|