@descryy/runtime-identity-grounding 0.3.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.
Files changed (49) hide show
  1. package/dist/element-identity.d.ts +41 -78
  2. package/dist/element-identity.d.ts.map +1 -1
  3. package/dist/element-identity.js +69 -64
  4. package/dist/element-identity.js.map +1 -1
  5. package/dist/fiber-probe.d.ts +25 -45
  6. package/dist/fiber-probe.d.ts.map +1 -1
  7. package/dist/fiber-probe.js +45 -84
  8. package/dist/fiber-probe.js.map +1 -1
  9. package/dist/framework-aware-grounder.d.ts +35 -0
  10. package/dist/framework-aware-grounder.d.ts.map +1 -0
  11. package/dist/framework-aware-grounder.js +58 -0
  12. package/dist/framework-aware-grounder.js.map +1 -0
  13. package/dist/framework-detector.d.ts +46 -0
  14. package/dist/framework-detector.d.ts.map +1 -0
  15. package/dist/framework-detector.js +89 -0
  16. package/dist/framework-detector.js.map +1 -0
  17. package/dist/graph-join.d.ts +18 -49
  18. package/dist/graph-join.d.ts.map +1 -1
  19. package/dist/graph-join.js +17 -42
  20. package/dist/graph-join.js.map +1 -1
  21. package/dist/identity-grounder.d.ts +12 -31
  22. package/dist/identity-grounder.d.ts.map +1 -1
  23. package/dist/identity-grounder.js +13 -26
  24. package/dist/identity-grounder.js.map +1 -1
  25. package/dist/identity-grounding.d.ts +20 -39
  26. package/dist/identity-grounding.d.ts.map +1 -1
  27. package/dist/identity-grounding.js +27 -44
  28. package/dist/identity-grounding.js.map +1 -1
  29. package/dist/index.d.ts +11 -6
  30. package/dist/index.d.ts.map +1 -1
  31. package/dist/index.js +7 -6
  32. package/dist/index.js.map +1 -1
  33. package/dist/owner-stack.d.ts +36 -65
  34. package/dist/owner-stack.d.ts.map +1 -1
  35. package/dist/owner-stack.js +33 -62
  36. package/dist/owner-stack.js.map +1 -1
  37. package/dist/vue-identity-grounder.d.ts +23 -0
  38. package/dist/vue-identity-grounder.d.ts.map +1 -0
  39. package/dist/vue-identity-grounder.js +28 -0
  40. package/dist/vue-identity-grounder.js.map +1 -0
  41. package/dist/vue-identity-grounding.d.ts +16 -0
  42. package/dist/vue-identity-grounding.d.ts.map +1 -0
  43. package/dist/vue-identity-grounding.js +110 -0
  44. package/dist/vue-identity-grounding.js.map +1 -0
  45. package/dist/vue-probe.d.ts +49 -0
  46. package/dist/vue-probe.d.ts.map +1 -0
  47. package/dist/vue-probe.js +95 -0
  48. package/dist/vue-probe.js.map +1 -0
  49. package/package.json +5 -5
@@ -2,58 +2,38 @@
2
2
  * The page-side half: reads React's fiber bookkeeping off a real DOM node
3
3
  * and returns **raw facts only**.
4
4
  *
5
- * ## Read-only, and why that matters here
5
+ * Read-only, unlike `frontend-initiator-capture.ts` which concedes it
6
+ * wraps `window.fetch`. This module defines one function on `window` and
7
+ * reads properties React already put on DOM nodes and fiber objects —
8
+ * nothing wrapped, nothing patched, no application code path changes shape
9
+ * (same observation-only guarantee `Collector` states for attach mode).
6
10
  *
7
- * `frontend-initiator-capture.ts` (`@descryy/runtime-browser`) had to open
8
- * its own module comment by conceding that it *writes* — it wraps
9
- * `window.fetch` and adds a header to real outgoing requests, which is a
10
- * real observable side effect on the application under test. This module
11
- * has no such concession to make. It defines one function on `window` and
12
- * reads properties React already put on DOM nodes and on its own fiber
13
- * objects. Nothing is wrapped, nothing is patched, no application code path
14
- * changes shape because Descry is attached. That is the same
15
- * observation-only guarantee `Collector` states for attach mode
16
- * (`ATTACH_MODE_SCOPE_DISCLOSURE`), and it holds here without needing an
17
- * exception.
11
+ * Injected via `page.addInitScript` with a string body, the same mechanism
12
+ * `frontend-initiator-capture.ts` uses — no transpilation step needed. A
13
+ * string rather than a typed function because this repo's
14
+ * `tsconfig.base.json` has no DOM library, so `document`/`window` don't
15
+ * exist as types here; it keeps the "runs here" vs "runs there" boundary
16
+ * visible.
18
17
  *
19
- * ## Injected through the existing seam, not a second one
18
+ * `addInitScript` doesn't reach an already-loaded document — call
19
+ * `install()` before navigating, or a page reports `probe-not-installed`.
20
20
  *
21
- * `page.addInitScript` with a string body — exactly the mechanism
22
- * `frontend-initiator-capture.ts` already uses, for the same reason it
23
- * gives: the script runs as written in the page's own JS engine and needs
24
- * no transpilation step of its own. It is a string rather than a typed
25
- * function because this repo's `tsconfig.base.json` compiles with `lib:
26
- * ["ES2023"]` and no DOM library at all, so `document` and `window` do not
27
- * exist as types in this package. Writing the page code as a string is not
28
- * a shortcut around that; it is the same choice the sibling module made,
29
- * and it keeps the boundary between "runs here" and "runs there" visible.
30
- *
31
- * `addInitScript` does not reach a document that has already loaded. Call
32
- * `install()` before navigating, like every other collector in
33
- * `@descryy/runtime-browser` — a page navigated first reports
34
- * `probe-not-installed`, by name, rather than silently returning nothing.
35
- *
36
- * ## Raw facts only — no decisions
37
- *
38
- * Everything this returns is a JSON-serialisable transcript of what was on
39
- * the fiber. Deciding what it *means* (is this a production build? does
40
- * this owner stack point at authored source?) happens in Node, in
41
- * `identity-grounding.ts`, where it can be unit-tested without a browser
42
- * and where the source-map machinery this repo already owns is reachable.
43
- * The page-side script cannot resolve a source map and must not try.
21
+ * Raw facts only, no decisions: everything returned is a JSON-serialisable
22
+ * transcript. Deciding what it *means* happens in Node
23
+ * (`identity-grounding.ts`), unit-testable without a browser. The
24
+ * page-side script cannot resolve a source map and must not try.
44
25
  */
45
26
  /** The single global this installs. One name, one function, no other footprint on the page. */
46
27
  export const IDENTITY_GROUNDING_BINDING = "__descryIdentityGrounding";
47
28
  /**
48
29
  * The two key prefixes React has used to hang a fiber off a host DOM node:
49
- * `__reactFiber$` since React 17, `__reactInternalInstance$` before that.
50
- * Both are checked because both are cheap to check and a wrong "not React"
51
- * answer on a React 16 page would be a silent, permanent blind spot.
30
+ * `__reactFiber$` since React 17, `__reactInternalInstance$` before. Both
31
+ * checked because both are cheap and a wrong "not React" on a React 16 page
32
+ * would be a silent, permanent blind spot.
52
33
  *
53
- * Measured present as `__reactFiber$<random>` on React 18.3.1 and 19.2.8
54
- * alike; `__reactInternalInstance$` is carried on the documented shape of
55
- * React 16 and is **not** covered by this repo's fixtures, which is stated
56
- * here rather than implied by the code.
34
+ * Measured present as `__reactFiber$<random>` on React 18.3.1 and 19.2.8;
35
+ * `__reactInternalInstance$` matches React 16's documented shape but is
36
+ * NOT covered by this repo's fixtures.
57
37
  */
58
38
  export const FIBER_KEY_PREFIXES = ["__reactFiber$", "__reactInternalInstance$"];
59
39
  export function createIdentityGroundingProbe() {
@@ -67,22 +47,15 @@ export function createIdentityGroundingProbe() {
67
47
  };
68
48
  }
69
49
  /**
70
- * The call side, also a string — and a **self-invoking** one, with the
71
- * selector serialised into it rather than passed as an `evaluate` argument.
72
- *
73
- * Measured, not stylistic: Playwright's string form of `page.evaluate`
74
- * evaluates the text as an expression, and passing a separate `arg`
75
- * alongside a string body returned `undefined` here rather than calling the
76
- * function expression with it. A self-invoking expression has one
77
- * unambiguous meaning in both directions, and `JSON.stringify` on the
78
- * selector is the same escaping this file already uses for the binding
79
- * name, so a selector containing quotes cannot break out of the string.
50
+ * The call side, also a string — **self-invoking**, with the selector
51
+ * serialised into it rather than passed as an `evaluate` argument. Measured,
52
+ * not stylistic: a separate `arg` alongside a string body returned
53
+ * `undefined` here instead of calling the expression with it.
54
+ * `JSON.stringify` on the selector prevents a quote from breaking out.
80
55
  *
81
- * The `probe-not-installed` branch lives here rather than in the injected
82
- * script for the obvious reason that a script which never ran cannot report
83
- * that it never ran. This is the only place that distinction can be drawn,
84
- * and drawing it is what keeps "Descry was not attached to this document"
85
- * from being reported as "this page is not React".
56
+ * The `probe-not-installed` branch lives here, not in the injected script —
57
+ * a script that never ran can't report that it never ran. This is the only
58
+ * place that distinction can be drawn.
86
59
  */
87
60
  function probeCallSource(selector) {
88
61
  return `(() => {
@@ -93,34 +66,23 @@ function probeCallSource(selector) {
93
66
  return grounding.probe(${JSON.stringify(selector)});
94
67
  })()`;
95
68
  }
96
- /** Cap on the key list carried by a `no-fiber` result. A `<div>` has few own keys; a pathological one should not become an unbounded payload. */
69
+ /** Cap on the key list carried by a `no-fiber` result — a pathological element shouldn't become an unbounded payload. */
97
70
  const MAX_REPORTED_OWN_KEYS = 32;
98
71
  /**
99
72
  * The injected reader.
100
73
  *
101
- * ## Why component detection reads the *type shape* and not `fiber.tag`
102
- *
103
- * The obvious implementation is a set of numeric fiber tags —
104
- * `0 = FunctionComponent`, `1 = ClassComponent`, `11 = ForwardRef` and so
105
- * on. That table is a private React implementation detail with no
106
- * compatibility guarantee across majors, and this module has to work
107
- * against at least two majors whose fiber internals already differ in the
108
- * ways this package exists to handle. A table that is wrong on some future
109
- * React does not fail loudly — it silently classifies a real component as
110
- * "not a component" and the answer degrades to a refusal nobody can
111
- * explain.
112
- *
113
- * `fiber.type` is far more stable and is directly observable: a string is a
114
- * host element, a function is a function or class component, an object with
115
- * a `render` is a `forwardRef`, an object with a `type` is a `memo`. That
116
- * is what is used. `tag` is still reported, verbatim, for audit.
117
- *
118
- * ## Everything degrades to null, nothing throws
74
+ * Component detection reads the *type shape*, not `fiber.tag` — the
75
+ * obvious numeric-tag table (`0 = FunctionComponent`, ...) is a private
76
+ * React implementation detail with no cross-major guarantee, and a table
77
+ * wrong on some future React silently misclassifies a component as "not a
78
+ * component" rather than failing loudly. `fiber.type` is stable and
79
+ * observable instead: a string is a host element, a function a
80
+ * function/class component, an object with `render` a `forwardRef`, an
81
+ * object with `type` a `memo`. `tag` is still reported verbatim for audit.
119
82
  *
120
- * A property access that finds an unexpected shape yields `null`, and the
121
- * whole probe body is wrapped so that a genuinely unexpected page cannot
122
- * turn a read-only observation into a page error the application would
123
- * then have to survive.
83
+ * Everything degrades to null, nothing throws — an unexpected shape yields
84
+ * `null`, and the whole probe body is wrapped so a read-only observation
85
+ * can't become a page error the app has to survive.
124
86
  */
125
87
  function probeScriptSource() {
126
88
  return `(() => {
@@ -214,9 +176,8 @@ function probeScriptSource() {
214
176
  try {
215
177
  element = document.querySelector(selector);
216
178
  } catch (_error) {
217
- // An invalid selector is the caller's mistake, and it is not a
218
- // statement about the page. Reported as "matched nothing", which is
219
- // what actually happened here.
179
+ // An invalid selector is the caller's mistake, not a statement
180
+ // about the page — reported as "matched nothing".
220
181
  return { status: "element-not-found", selector: selector };
221
182
  }
222
183
  if (element === null) return { status: "element-not-found", selector: selector };
@@ -1 +1 @@
1
- {"version":3,"file":"fiber-probe.js","sourceRoot":"","sources":["../src/fiber-probe.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AAMH,+FAA+F;AAC/F,MAAM,CAAC,MAAM,0BAA0B,GAAG,2BAA2B,CAAC;AAEtE;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,eAAe,EAAE,0BAA0B,CAAU,CAAC;AAuDzF,MAAM,UAAU,4BAA4B;IAC1C,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,CAAgB,eAAe,CAAC,QAAQ,CAAC,CAAC,CAAC;QACjE,CAAC;KACF,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,SAAS,eAAe,CAAC,QAAgB;IACvC,OAAO;6BACoB,IAAI,CAAC,SAAS,CAAC,0BAA0B,CAAC;;;;2BAI5C,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC;KAC9C,CAAC;AACN,CAAC;AAED,iJAAiJ;AACjJ,MAAM,qBAAqB,GAAG,EAAE,CAAC;AAEjC;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,SAAS,iBAAiB;IACxB,OAAO;+BACsB,IAAI,CAAC,SAAS,CAAC,kBAAkB,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;WAoFtD,IAAI,CAAC,SAAS,CAAC,0BAA0B,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;mDA6BF,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;MA2BlE,CAAC;AACP,CAAC"}
1
+ {"version":3,"file":"fiber-probe.js","sourceRoot":"","sources":["../src/fiber-probe.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAMH,+FAA+F;AAC/F,MAAM,CAAC,MAAM,0BAA0B,GAAG,2BAA2B,CAAC;AAEtE;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,eAAe,EAAE,0BAA0B,CAAU,CAAC;AAuDzF,MAAM,UAAU,4BAA4B;IAC1C,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,CAAgB,eAAe,CAAC,QAAQ,CAAC,CAAC,CAAC;QACjE,CAAC;KACF,CAAC;AACJ,CAAC;AAED;;;;;;;;;;GAUG;AACH,SAAS,eAAe,CAAC,QAAgB;IACvC,OAAO;6BACoB,IAAI,CAAC,SAAS,CAAC,0BAA0B,CAAC;;;;2BAI5C,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC;KAC9C,CAAC;AACN,CAAC;AAED,yHAAyH;AACzH,MAAM,qBAAqB,GAAG,EAAE,CAAC;AAEjC;;;;;;;;;;;;;;;GAeG;AACH,SAAS,iBAAiB;IACxB,OAAO;+BACsB,IAAI,CAAC,SAAS,CAAC,kBAAkB,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;WAoFtD,IAAI,CAAC,SAAS,CAAC,0BAA0B,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;mDA4BF,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;MA2BlE,CAAC;AACP,CAAC"}
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Detect, then ground with the matching framework's grounder -- the single
3
+ * seam both surfaces (`descry-core`'s `playwright-driver.ts`, the desktop's
4
+ * `browser-drive.ts`) are meant to call instead of hardcoding
5
+ * `createIdentityGrounder` (React). Composes `createFrameworkDetector`
6
+ * (`framework-detector.ts`) with `createIdentityGrounder` (React,
7
+ * `identity-grounder.ts`) and `createVueIdentityGrounder` (Vue,
8
+ * `vue-identity-grounder.ts`); never defaults to either when detection
9
+ * fails.
10
+ *
11
+ * `install()` installs all three page-side probes -- the detector's and
12
+ * both frameworks' -- before first navigation, required by
13
+ * `page.addInitScript`'s contract regardless of which framework the page
14
+ * turns out to be, since detection cannot run until the document is live.
15
+ * Installing a probe that never fires costs nothing beyond the one
16
+ * `addInitScript` registration.
17
+ *
18
+ * `ground()` re-detects on every call rather than caching the framework
19
+ * after the first success: a session's page can navigate to a different
20
+ * app mid-session (a browser session's grounder is created once and reused
21
+ * across the whole session, both in `playwright-driver.ts`'s long-lived
22
+ * session and in the desktop's drive), and a framework cached from page 1
23
+ * would silently misground page 2 rather than re-deciding honestly.
24
+ */
25
+ import type { Page } from "playwright-core";
26
+ import type { ElementIdentity } from "./element-identity.ts";
27
+ import { type IdentityGrounderOptions } from "./identity-grounder.ts";
28
+ export interface FrameworkAwareGrounder {
29
+ /** Installs every page-side probe (detection, React, Vue) into every document this page loads from now on. Call before navigating -- same `addInitScript` contract every probe in this package carries. */
30
+ install(page: Page): Promise<void>;
31
+ /** Detects the page's framework, then resolves one on-screen element through that framework's own grounder. Refuses by name (`framework-not-detected`) rather than guessing when neither framework's marker is present. */
32
+ ground(page: Page, selector: string): Promise<ElementIdentity>;
33
+ }
34
+ export declare function createFrameworkAwareGrounder(options?: IdentityGrounderOptions): FrameworkAwareGrounder;
35
+ //# sourceMappingURL=framework-aware-grounder.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"framework-aware-grounder.d.ts","sourceRoot":"","sources":["../src/framework-aware-grounder.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAE5C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAG7D,OAAO,EAAiD,KAAK,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AAGrH,MAAM,WAAW,sBAAsB;IACrC,2MAA2M;IAC3M,OAAO,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACnC,2NAA2N;IAC3N,MAAM,CAAC,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC,CAAC;CAChE;AAED,wBAAgB,4BAA4B,CAAC,OAAO,GAAE,uBAA4B,GAAG,sBAAsB,CA6B1G"}
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Detect, then ground with the matching framework's grounder -- the single
3
+ * seam both surfaces (`descry-core`'s `playwright-driver.ts`, the desktop's
4
+ * `browser-drive.ts`) are meant to call instead of hardcoding
5
+ * `createIdentityGrounder` (React). Composes `createFrameworkDetector`
6
+ * (`framework-detector.ts`) with `createIdentityGrounder` (React,
7
+ * `identity-grounder.ts`) and `createVueIdentityGrounder` (Vue,
8
+ * `vue-identity-grounder.ts`); never defaults to either when detection
9
+ * fails.
10
+ *
11
+ * `install()` installs all three page-side probes -- the detector's and
12
+ * both frameworks' -- before first navigation, required by
13
+ * `page.addInitScript`'s contract regardless of which framework the page
14
+ * turns out to be, since detection cannot run until the document is live.
15
+ * Installing a probe that never fires costs nothing beyond the one
16
+ * `addInitScript` registration.
17
+ *
18
+ * `ground()` re-detects on every call rather than caching the framework
19
+ * after the first success: a session's page can navigate to a different
20
+ * app mid-session (a browser session's grounder is created once and reused
21
+ * across the whole session, both in `playwright-driver.ts`'s long-lived
22
+ * session and in the desktop's drive), and a framework cached from page 1
23
+ * would silently misground page 2 rather than re-deciding honestly.
24
+ */
25
+ import { capabilityFor } from "./element-identity.js";
26
+ import { createFrameworkDetector } from "./framework-detector.js";
27
+ import { createIdentityGrounder } from "./identity-grounder.js";
28
+ import { createVueIdentityGrounder } from "./vue-identity-grounder.js";
29
+ export function createFrameworkAwareGrounder(options = {}) {
30
+ const detector = createFrameworkDetector();
31
+ const reactGrounder = createIdentityGrounder(options);
32
+ const vueGrounder = createVueIdentityGrounder();
33
+ return {
34
+ async install(page) {
35
+ // Order doesn't matter -- addInitScript registrations are independent
36
+ // and all three must land before the first navigation.
37
+ await detector.install(page);
38
+ await reactGrounder.install(page);
39
+ await vueGrounder.install(page);
40
+ },
41
+ async ground(page, selector) {
42
+ const result = await detector.detect(page);
43
+ if (result.outcome === "detected") {
44
+ return result.framework === "react" ? reactGrounder.ground(page, selector) : vueGrounder.ground(page, selector);
45
+ }
46
+ return {
47
+ selector,
48
+ fiberDistance: 0,
49
+ outcome: "refused",
50
+ refusal: "framework-not-detected",
51
+ reason: result.reason,
52
+ capability: capabilityFor("unavailable", result.reason),
53
+ observedTypeName: null,
54
+ };
55
+ },
56
+ };
57
+ }
58
+ //# sourceMappingURL=framework-aware-grounder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"framework-aware-grounder.js","sourceRoot":"","sources":["../src/framework-aware-grounder.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAKH,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,EAAE,uBAAuB,EAAiC,MAAM,yBAAyB,CAAC;AACjG,OAAO,EAAE,sBAAsB,EAAuD,MAAM,wBAAwB,CAAC;AACrH,OAAO,EAAE,yBAAyB,EAA4B,MAAM,4BAA4B,CAAC;AASjG,MAAM,UAAU,4BAA4B,CAAC,UAAmC,EAAE;IAChF,MAAM,QAAQ,GAAG,uBAAuB,EAAE,CAAC;IAC3C,MAAM,aAAa,GAAqB,sBAAsB,CAAC,OAAO,CAAC,CAAC;IACxE,MAAM,WAAW,GAAwB,yBAAyB,EAAE,CAAC;IAErE,OAAO;QACL,KAAK,CAAC,OAAO,CAAC,IAAU;YACtB,sEAAsE;YACtE,uDAAuD;YACvD,MAAM,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;YAC7B,MAAM,aAAa,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;YAClC,MAAM,WAAW,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QAClC,CAAC;QACD,KAAK,CAAC,MAAM,CAAC,IAAU,EAAE,QAAgB;YACvC,MAAM,MAAM,GAA6B,MAAM,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;YACrE,IAAI,MAAM,CAAC,OAAO,KAAK,UAAU,EAAE,CAAC;gBAClC,OAAO,MAAM,CAAC,SAAS,KAAK,OAAO,CAAC,CAAC,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,MAAM,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;YAClH,CAAC;YACD,OAAO;gBACL,QAAQ;gBACR,aAAa,EAAE,CAAC;gBAChB,OAAO,EAAE,SAAS;gBAClB,OAAO,EAAE,wBAAwB;gBACjC,MAAM,EAAE,MAAM,CAAC,MAAM;gBACrB,UAAU,EAAE,aAAa,CAAC,aAAa,EAAE,MAAM,CAAC,MAAM,CAAC;gBACvD,gBAAgB,EAAE,IAAI;aACvB,CAAC;QACJ,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Which framework rendered this page, decided by scanning the live document
3
+ * for the marker each framework's own probe already reads: React's fiber
4
+ * key (`fiber-probe.ts`'s `FIBER_KEY_PREFIXES`), then Vue's
5
+ * `__vueParentComponent` (`vue-probe.ts`). Neither found is a named
6
+ * refusal -- never a default to React.
7
+ *
8
+ * This is the one place both surfaces (`descry-core`'s
9
+ * `packages/mcp/src/browser/playwright-driver.ts` and `descry-desktop`'s
10
+ * `packages/session/src/pipeline/browser-drive.ts`) are meant to call, so
11
+ * detection is decided once and the two cannot drift on how they decide.
12
+ * See `framework-aware-grounder.ts` for the composed grounder that uses
13
+ * this to pick between `createIdentityGrounder` (React) and
14
+ * `createVueIdentityGrounder` (Vue).
15
+ *
16
+ * Page-wide, not selector-scoped, deliberately: the element a caller wants
17
+ * grounded may sit outside the framework's own mount root (a static
18
+ * wrapper around an app), and a selector-scoped scan would misreport "not
19
+ * React" for a real React page just because the queried node happens to sit
20
+ * outside it. Scanning the whole document answers the honest, broader
21
+ * question -- "did this framework render anything on this page at all" --
22
+ * which is what deciding *which grounder to use* needs; the per-selector
23
+ * probes answer the narrower one ("did it render *this* element") once a
24
+ * grounder has already been chosen.
25
+ */
26
+ import type { Page } from "playwright-core";
27
+ /** The single global this installs. One name, one function, no other footprint on the page -- same discipline as `fiber-probe.ts`'s and `vue-probe.ts`'s own bindings. */
28
+ export declare const FRAMEWORK_DETECTION_BINDING = "__descryFrameworkDetection";
29
+ /** Checked in this order, always: React's fiber marker, then Vue's component marker. Never the reverse, and never a default. */
30
+ export declare const DETECTABLE_FRAMEWORKS: readonly ["react", "vue"];
31
+ export type DetectableFramework = (typeof DETECTABLE_FRAMEWORKS)[number];
32
+ export type FrameworkDetectionResult = {
33
+ readonly outcome: "detected";
34
+ readonly framework: DetectableFramework;
35
+ } | {
36
+ readonly outcome: "refused";
37
+ readonly reason: string;
38
+ };
39
+ export interface FrameworkDetector {
40
+ /** Installs the page-side reader into every document this page loads from now on. Call before navigating -- a page navigated first reports a `probe-not-installed`-shaped refusal, same contract `fiber-probe.ts`'s `install` documents. */
41
+ install(page: Page): Promise<void>;
42
+ /** Scans the page's current document and names which framework rendered it, or refuses by name. Never throws for a routine unavailability. */
43
+ detect(page: Page): Promise<FrameworkDetectionResult>;
44
+ }
45
+ export declare function createFrameworkDetector(): FrameworkDetector;
46
+ //# sourceMappingURL=framework-detector.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"framework-detector.d.ts","sourceRoot":"","sources":["../src/framework-detector.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAI5C,0KAA0K;AAC1K,eAAO,MAAM,2BAA2B,+BAA+B,CAAC;AAExE,gIAAgI;AAChI,eAAO,MAAM,qBAAqB,2BAA4B,CAAC;AAC/D,MAAM,MAAM,mBAAmB,GAAG,CAAC,OAAO,qBAAqB,CAAC,CAAC,MAAM,CAAC,CAAC;AAEzE,MAAM,MAAM,wBAAwB,GAChC;IAAE,QAAQ,CAAC,OAAO,EAAE,UAAU,CAAC;IAAC,QAAQ,CAAC,SAAS,EAAE,mBAAmB,CAAA;CAAE,GACzE;IAAE,QAAQ,CAAC,OAAO,EAAE,SAAS,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAE7D,MAAM,WAAW,iBAAiB;IAChC,4OAA4O;IAC5O,OAAO,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACnC,8IAA8I;IAC9I,MAAM,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,wBAAwB,CAAC,CAAC;CACvD;AAED,wBAAgB,uBAAuB,IAAI,iBAAiB,CAS3D"}
@@ -0,0 +1,89 @@
1
+ /**
2
+ * Which framework rendered this page, decided by scanning the live document
3
+ * for the marker each framework's own probe already reads: React's fiber
4
+ * key (`fiber-probe.ts`'s `FIBER_KEY_PREFIXES`), then Vue's
5
+ * `__vueParentComponent` (`vue-probe.ts`). Neither found is a named
6
+ * refusal -- never a default to React.
7
+ *
8
+ * This is the one place both surfaces (`descry-core`'s
9
+ * `packages/mcp/src/browser/playwright-driver.ts` and `descry-desktop`'s
10
+ * `packages/session/src/pipeline/browser-drive.ts`) are meant to call, so
11
+ * detection is decided once and the two cannot drift on how they decide.
12
+ * See `framework-aware-grounder.ts` for the composed grounder that uses
13
+ * this to pick between `createIdentityGrounder` (React) and
14
+ * `createVueIdentityGrounder` (Vue).
15
+ *
16
+ * Page-wide, not selector-scoped, deliberately: the element a caller wants
17
+ * grounded may sit outside the framework's own mount root (a static
18
+ * wrapper around an app), and a selector-scoped scan would misreport "not
19
+ * React" for a real React page just because the queried node happens to sit
20
+ * outside it. Scanning the whole document answers the honest, broader
21
+ * question -- "did this framework render anything on this page at all" --
22
+ * which is what deciding *which grounder to use* needs; the per-selector
23
+ * probes answer the narrower one ("did it render *this* element") once a
24
+ * grounder has already been chosen.
25
+ */
26
+ import { FIBER_KEY_PREFIXES } from "./fiber-probe.js";
27
+ /** The single global this installs. One name, one function, no other footprint on the page -- same discipline as `fiber-probe.ts`'s and `vue-probe.ts`'s own bindings. */
28
+ export const FRAMEWORK_DETECTION_BINDING = "__descryFrameworkDetection";
29
+ /** Checked in this order, always: React's fiber marker, then Vue's component marker. Never the reverse, and never a default. */
30
+ export const DETECTABLE_FRAMEWORKS = ["react", "vue"];
31
+ export function createFrameworkDetector() {
32
+ return {
33
+ async install(page) {
34
+ await page.addInitScript({ content: probeScriptSource() });
35
+ },
36
+ detect(page) {
37
+ return page.evaluate(callSource());
38
+ },
39
+ };
40
+ }
41
+ /** Self-invoking, no arguments needed -- unlike the per-element probes there is no selector to serialise in. The `probe-not-installed` branch lives here, not in the injected script, for the same reason `fiber-probe.ts`'s `probeCallSource` gives: a script that never ran cannot report that it never ran. */
42
+ function callSource() {
43
+ return `(() => {
44
+ const detection = window[${JSON.stringify(FRAMEWORK_DETECTION_BINDING)}];
45
+ if (detection === undefined || typeof detection.detect !== "function") {
46
+ return {
47
+ outcome: "refused",
48
+ reason: "the framework-detection probe was never installed (install() must run before the page's first navigation)",
49
+ };
50
+ }
51
+ return detection.detect();
52
+ })()`;
53
+ }
54
+ function probeScriptSource() {
55
+ return `(() => {
56
+ const FIBER_KEY_PREFIXES = ${JSON.stringify(FIBER_KEY_PREFIXES)};
57
+
58
+ function hasReactFiberKey(element) {
59
+ const keys = Object.keys(element);
60
+ for (let i = 0; i < keys.length; i++) {
61
+ for (let p = 0; p < FIBER_KEY_PREFIXES.length; p++) {
62
+ if (keys[i].indexOf(FIBER_KEY_PREFIXES[p]) === 0) return true;
63
+ }
64
+ }
65
+ return false;
66
+ }
67
+
68
+ function hasVueMarker(element) {
69
+ return element.__vueParentComponent !== undefined && element.__vueParentComponent !== null;
70
+ }
71
+
72
+ window[${JSON.stringify(FRAMEWORK_DETECTION_BINDING)}] = {
73
+ detect: function () {
74
+ const all = document.querySelectorAll("*");
75
+ for (let i = 0; i < all.length; i++) {
76
+ if (hasReactFiberKey(all[i])) return { outcome: "detected", framework: "react" };
77
+ }
78
+ for (let i = 0; i < all.length; i++) {
79
+ if (hasVueMarker(all[i])) return { outcome: "detected", framework: "vue" };
80
+ }
81
+ return {
82
+ outcome: "refused",
83
+ reason: "neither a React fiber key nor a Vue __vueParentComponent marker was found anywhere in the document",
84
+ };
85
+ },
86
+ };
87
+ })();`;
88
+ }
89
+ //# sourceMappingURL=framework-detector.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"framework-detector.js","sourceRoot":"","sources":["../src/framework-detector.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAIH,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAEtD,0KAA0K;AAC1K,MAAM,CAAC,MAAM,2BAA2B,GAAG,4BAA4B,CAAC;AAExE,gIAAgI;AAChI,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,OAAO,EAAE,KAAK,CAAU,CAAC;AAc/D,MAAM,UAAU,uBAAuB;IACrC,OAAO;QACL,KAAK,CAAC,OAAO,CAAC,IAAU;YACtB,MAAM,IAAI,CAAC,aAAa,CAAC,EAAE,OAAO,EAAE,iBAAiB,EAAE,EAAE,CAAC,CAAC;QAC7D,CAAC;QACD,MAAM,CAAC,IAAU;YACf,OAAO,IAAI,CAAC,QAAQ,CAA2B,UAAU,EAAE,CAAC,CAAC;QAC/D,CAAC;KACF,CAAC;AACJ,CAAC;AAED,kTAAkT;AAClT,SAAS,UAAU;IACjB,OAAO;6BACoB,IAAI,CAAC,SAAS,CAAC,2BAA2B,CAAC;;;;;;;;KAQnE,CAAC;AACN,CAAC;AAED,SAAS,iBAAiB;IACxB,OAAO;+BACsB,IAAI,CAAC,SAAS,CAAC,kBAAkB,CAAC;;;;;;;;;;;;;;;;WAgBtD,IAAI,CAAC,SAAS,CAAC,2BAA2B,CAAC;;;;;;;;;;;;;;;MAehD,CAAC;AACP,CAAC"}
@@ -1,61 +1,30 @@
1
1
  /**
2
- * The join to `descry-core`'s graph — as a **hand-off**, not a second
3
- * resolver.
2
+ * The join to `descry-core`'s graph — a **hand-off**, not a second
3
+ * resolver. `resolveSymbolNode` already does the whole job of turning
4
+ * `{ file, symbolName, line }` into a `FUNCTION`/`CLASS` node — refusing
5
+ * ambiguity, checking the line against the node's range, translating path
6
+ * languages, requiring `repo` since there's no honest default. This
7
+ * function's entire contract: hand it exactly that input, or hand it
8
+ * nothing.
4
9
  *
5
- * `@descryy/runtime-graph-correlator`'s `resolveSymbolNode` already does the
6
- * whole job of turning `{ file, symbolName, line }` into a `FUNCTION`/
7
- * `CLASS` node, and everything hard about that job is in the parts this
8
- * function must not duplicate: refusing ambiguity instead of ranking
9
- * candidates, checking the claimed line against the node's own range and
10
- * reporting `line-mismatch` distinctly, translating between the absolute /
11
- * repo-relative / relative-to-launch-cwd path languages, and requiring
12
- * `repo` because `FUNCTION` is repo-scoped and there is no honest default.
13
- * A copy of any of that here would be a copy that drifts.
10
+ * No new node type, no new edge type — resolves to a `FUNCTION`/`CLASS`
11
+ * node an adapter already produced. `descry-core`'s frozen 15/15 vocabulary
12
+ * is untouched.
14
13
  *
15
- * So this function's entire contract is: hand `resolveSymbolNode` exactly
16
- * the input it asks for, or hand it nothing.
17
- *
18
- * ## No new node type, no new edge type
19
- *
20
- * The component resolves to a `FUNCTION` or `CLASS` node that an adapter
21
- * already produced — `descry-core`'s frozen 15 node types and 15 edge types
22
- * are untouched. There is no `COMPONENT_INSTANCE`, no `RENDERS` edge, and
23
- * nothing here asks for one. (`COMPONENT` is already one of the 15, and a
24
- * React component that an adapter emitted as one is found by the same
25
- * name+file lookup.)
26
- *
27
- * ## Why a compiled location is withheld rather than passed through
28
- *
29
- * `resolveSymbolNode` would take a bundle path quite happily and report
30
- * `not-found` for it, which reads to a consumer as *"that component is not
31
- * in the graph"* — the exact misleading refusal `symbol.ts`'s own header
32
- * describes and fixed for absolute paths. A React 19 answer with
33
- * `authoredSource: false` names `dist/dev.js`, a file no adapter ever
34
- * indexed and never will. Withholding it keeps the honest statement —
35
- * *"we know the component, we could not reach authored source"* — inside
36
- * `ElementIdentity`, where the capability status explains it, instead of
37
- * laundering it into a graph miss.
14
+ * A compiled location is withheld rather than passed through:
15
+ * `resolveSymbolNode` would report `not-found` for a bundle path, reading
16
+ * as "not in the graph" — the exact misleading refusal `symbol.ts` fixed
17
+ * for absolute paths. Withholding it keeps the honest statement ("we know
18
+ * the component, couldn't reach authored source") inside `ElementIdentity`,
19
+ * not laundered into a graph miss.
38
20
  */
39
21
  import type { ElementIdentity } from "./element-identity.ts";
40
- /**
41
- * Structurally `resolveSymbolNode`'s own `location` parameter. Deliberately
42
- * declared with `line` optional rather than nullable, because that is the
43
- * shape that function accepts; `graph-join.test.ts` asserts the
44
- * compatibility against the real signature at compile time, so a change on
45
- * either side breaks the build rather than being discovered at runtime.
46
- */
22
+ /** Structurally `resolveSymbolNode`'s own `location` parameter — `line` optional, not nullable, matching that function's shape. `graph-join.test.ts` asserts the compatibility at compile time. */
47
23
  export interface GraphSymbolLocation {
48
24
  readonly file: string;
49
25
  readonly symbolName: string;
50
26
  readonly line?: number;
51
27
  }
52
- /**
53
- * The graph lookup this identity supports, or `null` when it supports none.
54
- *
55
- * `null` for every outcome except a `grounded` one that reached authored
56
- * source with a real file — including `component-only`, which knows the
57
- * component's name and nothing about where it lives, and including a
58
- * `grounded` answer whose location is compiled output.
59
- */
28
+ /** The graph lookup this identity supports, or `null` when it supports none — only a `grounded` outcome with authored source and a real file. */
60
29
  export declare function symbolLocationForGraph(identity: ElementIdentity): GraphSymbolLocation | null;
61
30
  //# sourceMappingURL=graph-join.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"graph-join.d.ts","sourceRoot":"","sources":["../src/graph-join.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAE7D;;;;;;GAMG;AACH,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,eAAe,GAAG,mBAAmB,GAAG,IAAI,CAU5F"}
1
+ {"version":3,"file":"graph-join.d.ts","sourceRoot":"","sources":["../src/graph-join.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAE7D,mMAAmM;AACnM,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,iJAAiJ;AACjJ,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,eAAe,GAAG,mBAAmB,GAAG,IAAI,CAU5F"}
@@ -1,49 +1,24 @@
1
1
  /**
2
- * The join to `descry-core`'s graph — as a **hand-off**, not a second
3
- * resolver.
2
+ * The join to `descry-core`'s graph — a **hand-off**, not a second
3
+ * resolver. `resolveSymbolNode` already does the whole job of turning
4
+ * `{ file, symbolName, line }` into a `FUNCTION`/`CLASS` node — refusing
5
+ * ambiguity, checking the line against the node's range, translating path
6
+ * languages, requiring `repo` since there's no honest default. This
7
+ * function's entire contract: hand it exactly that input, or hand it
8
+ * nothing.
4
9
  *
5
- * `@descryy/runtime-graph-correlator`'s `resolveSymbolNode` already does the
6
- * whole job of turning `{ file, symbolName, line }` into a `FUNCTION`/
7
- * `CLASS` node, and everything hard about that job is in the parts this
8
- * function must not duplicate: refusing ambiguity instead of ranking
9
- * candidates, checking the claimed line against the node's own range and
10
- * reporting `line-mismatch` distinctly, translating between the absolute /
11
- * repo-relative / relative-to-launch-cwd path languages, and requiring
12
- * `repo` because `FUNCTION` is repo-scoped and there is no honest default.
13
- * A copy of any of that here would be a copy that drifts.
10
+ * No new node type, no new edge type — resolves to a `FUNCTION`/`CLASS`
11
+ * node an adapter already produced. `descry-core`'s frozen 15/15 vocabulary
12
+ * is untouched.
14
13
  *
15
- * So this function's entire contract is: hand `resolveSymbolNode` exactly
16
- * the input it asks for, or hand it nothing.
17
- *
18
- * ## No new node type, no new edge type
19
- *
20
- * The component resolves to a `FUNCTION` or `CLASS` node that an adapter
21
- * already produced — `descry-core`'s frozen 15 node types and 15 edge types
22
- * are untouched. There is no `COMPONENT_INSTANCE`, no `RENDERS` edge, and
23
- * nothing here asks for one. (`COMPONENT` is already one of the 15, and a
24
- * React component that an adapter emitted as one is found by the same
25
- * name+file lookup.)
26
- *
27
- * ## Why a compiled location is withheld rather than passed through
28
- *
29
- * `resolveSymbolNode` would take a bundle path quite happily and report
30
- * `not-found` for it, which reads to a consumer as *"that component is not
31
- * in the graph"* — the exact misleading refusal `symbol.ts`'s own header
32
- * describes and fixed for absolute paths. A React 19 answer with
33
- * `authoredSource: false` names `dist/dev.js`, a file no adapter ever
34
- * indexed and never will. Withholding it keeps the honest statement —
35
- * *"we know the component, we could not reach authored source"* — inside
36
- * `ElementIdentity`, where the capability status explains it, instead of
37
- * laundering it into a graph miss.
38
- */
39
- /**
40
- * The graph lookup this identity supports, or `null` when it supports none.
41
- *
42
- * `null` for every outcome except a `grounded` one that reached authored
43
- * source with a real file — including `component-only`, which knows the
44
- * component's name and nothing about where it lives, and including a
45
- * `grounded` answer whose location is compiled output.
14
+ * A compiled location is withheld rather than passed through:
15
+ * `resolveSymbolNode` would report `not-found` for a bundle path, reading
16
+ * as "not in the graph" — the exact misleading refusal `symbol.ts` fixed
17
+ * for absolute paths. Withholding it keeps the honest statement ("we know
18
+ * the component, couldn't reach authored source") inside `ElementIdentity`,
19
+ * not laundered into a graph miss.
46
20
  */
21
+ /** The graph lookup this identity supports, or `null` when it supports none — only a `grounded` outcome with authored source and a real file. */
47
22
  export function symbolLocationForGraph(identity) {
48
23
  if (identity.outcome !== "grounded")
49
24
  return null;
@@ -1 +1 @@
1
- {"version":3,"file":"graph-join.js","sourceRoot":"","sources":["../src/graph-join.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AAiBH;;;;;;;GAOG;AACH,MAAM,UAAU,sBAAsB,CAAC,QAAyB;IAC9D,IAAI,QAAQ,CAAC,OAAO,KAAK,UAAU;QAAE,OAAO,IAAI,CAAC;IACjD,IAAI,CAAC,QAAQ,CAAC,cAAc;QAAE,OAAO,IAAI,CAAC;IAE1C,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,QAAQ,CAAC,cAAc,CAAC;IAC/C,IAAI,IAAI,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IAE/B,OAAO,IAAI,KAAK,IAAI;QAClB,CAAC,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,QAAQ,CAAC,SAAS,CAAC,IAAI,EAAE;QAC/C,CAAC,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,QAAQ,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC;AAC1D,CAAC"}
1
+ {"version":3,"file":"graph-join.js","sourceRoot":"","sources":["../src/graph-join.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAWH,iJAAiJ;AACjJ,MAAM,UAAU,sBAAsB,CAAC,QAAyB;IAC9D,IAAI,QAAQ,CAAC,OAAO,KAAK,UAAU;QAAE,OAAO,IAAI,CAAC;IACjD,IAAI,CAAC,QAAQ,CAAC,cAAc;QAAE,OAAO,IAAI,CAAC;IAE1C,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,QAAQ,CAAC,cAAc,CAAC;IAC/C,IAAI,IAAI,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IAE/B,OAAO,IAAI,KAAK,IAAI;QAClB,CAAC,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,QAAQ,CAAC,SAAS,CAAC,IAAI,EAAE;QAC/C,CAAC,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,QAAQ,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC;AAC1D,CAAC"}