@qontinui/ui-bridge-native 0.6.16 → 0.7.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/index.js +310 -24
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +310 -24
- package/dist/index.mjs.map +1 -1
- package/dist/observation/index.d.ts +12 -0
- package/dist/observation/index.d.ts.map +1 -0
- package/dist/observation/observation.d.ts +233 -0
- package/dist/observation/observation.d.ts.map +1 -0
- package/dist/react/index.js +310 -24
- package/dist/react/index.js.map +1 -1
- package/dist/react/index.mjs +310 -24
- package/dist/react/index.mjs.map +1 -1
- package/dist/server/handlers.d.ts.map +1 -1
- package/dist/server/index.d.ts +2 -0
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +314 -21
- package/dist/server/index.js.map +1 -1
- package/dist/server/index.mjs +308 -22
- package/dist/server/index.mjs.map +1 -1
- package/dist/server/page-health.d.ts +50 -7
- package/dist/server/page-health.d.ts.map +1 -1
- package/dist/server/types.d.ts +5 -3
- package/dist/server/types.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Observation envelope, vendored from `@qontinui/ui-bridge`.
|
|
3
|
+
*
|
|
4
|
+
* `./observation.ts` is a GENERATED copy of the canonical
|
|
5
|
+
* `packages/ui-bridge/src/observation/observation.ts` (see
|
|
6
|
+
* `scripts/sync-observation.mjs`); CI fails if the two drift. This package
|
|
7
|
+
* imports it by relative path so it takes no runtime dependency on
|
|
8
|
+
* `@qontinui/ui-bridge` (an optional peer here) and needs no package-`exports`
|
|
9
|
+
* support from Metro.
|
|
10
|
+
*/
|
|
11
|
+
export { Observation, ObservationError, UNKNOWN_CODES, isUnknownCode, type UnknownCode, type ObservationStatus, type ObservationProducer, type ObservationCoverage, type UnmeasuredDimension, type ObservationCache, type ObservationProvenance, type ObservationProvenanceInit, type ObservationUnknown, type ObservationTime, type MeasuredObservation, type AbsentObservation, type UnknownObservation, } from './observation';
|
|
12
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/observation/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EACL,WAAW,EACX,gBAAgB,EAChB,aAAa,EACb,aAAa,EACb,KAAK,WAAW,EAChB,KAAK,iBAAiB,EACtB,KAAK,mBAAmB,EACxB,KAAK,mBAAmB,EACxB,KAAK,mBAAmB,EACxB,KAAK,gBAAgB,EACrB,KAAK,qBAAqB,EAC1B,KAAK,yBAAyB,EAC9B,KAAK,kBAAkB,EACvB,KAAK,eAAe,EACpB,KAAK,mBAAmB,EACxB,KAAK,iBAAiB,EACtB,KAAK,kBAAkB,GACxB,MAAM,eAAe,CAAC"}
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Observation envelope — the shape every UI Bridge observation surface answers
|
|
3
|
+
* in: WHAT was seen (`measured`), that nothing was there (`absent`), or that the
|
|
4
|
+
* producer could not look (`unknown`) — and, in every case, where the answer
|
|
5
|
+
* came from (`provenance`).
|
|
6
|
+
*
|
|
7
|
+
* Why it exists: an empty findings list used to mean either "the page has
|
|
8
|
+
* nothing there" or "the producer never looked", byte-identically, and only the
|
|
9
|
+
* first is a statement about the page. `page-health` rendered a missing
|
|
10
|
+
* `elements` key as a CRITICAL blank page; an agent reading that went looking
|
|
11
|
+
* for a bug no evidence pointed at.
|
|
12
|
+
*
|
|
13
|
+
* This is the TypeScript twin of the canonical Rust type
|
|
14
|
+
* `qontinui-schemas/rust-vision-core/src/observation.rs`. `@qontinui/ui-bridge`
|
|
15
|
+
* is a standalone package and takes no dependency on qontinui-schemas; it is
|
|
16
|
+
* held to the Rust canon by SDK-captured fixtures the schemas crate parses
|
|
17
|
+
* strictly (plan 2026-09-20-ui-bridge-observations-…, D2). The two MUST stay
|
|
18
|
+
* structurally identical on the wire:
|
|
19
|
+
*
|
|
20
|
+
* ```jsonc
|
|
21
|
+
* {
|
|
22
|
+
* "status": "measured" | "absent" | "unknown", // always present
|
|
23
|
+
* "value": <T>, // present iff measured
|
|
24
|
+
* "unknown": { "code": <UnknownCode>, "detail": "…" }, // present iff unknown
|
|
25
|
+
* "provenance": { // always present, EVERY key always present
|
|
26
|
+
* "producer": { "id": "…", "version": "…" },
|
|
27
|
+
* "observedAt": "<RFC3339>" | null,
|
|
28
|
+
* "evaluatedAt": "<RFC3339>",
|
|
29
|
+
* "coverage": { "considered": n, "measured": n,
|
|
30
|
+
* "unmeasured": [{ "dimension": "…", "count": n, "code": <UnknownCode> }] },
|
|
31
|
+
* "confidence": <0..1> | null,
|
|
32
|
+
* "cache": { "hit": bool, "storedAt": "<RFC3339>" | null, "keyInputs": ["…"] } | null,
|
|
33
|
+
* "source": { … } | null
|
|
34
|
+
* }
|
|
35
|
+
* }
|
|
36
|
+
* ```
|
|
37
|
+
*
|
|
38
|
+
* `null` in `provenance` is a STATEMENT, never an omission: `confidence: null`
|
|
39
|
+
* says "a deduction, not an estimate", `cache: null` says "this producer has no
|
|
40
|
+
* cache", `observedAt: null` says "no sample was taken". A key is therefore
|
|
41
|
+
* never left out, and the constructors below are the only way to build one.
|
|
42
|
+
*
|
|
43
|
+
* The envelope adds no score and no verdict — `healthy`/`unhealthy` never
|
|
44
|
+
* appears in it.
|
|
45
|
+
*/
|
|
46
|
+
/**
|
|
47
|
+
* Why a producer could not answer. A CLOSED set: every `unknown` observation
|
|
48
|
+
* carries exactly one of these, so an agent branches on the code rather than
|
|
49
|
+
* parsing English out of `detail`.
|
|
50
|
+
*/
|
|
51
|
+
export type UnknownCode =
|
|
52
|
+
/** The bridge/app could not be reached. */
|
|
53
|
+
'app_unreachable'
|
|
54
|
+
/** The running build does not serve this capability (e.g. an SDK route that needs a runner). */
|
|
55
|
+
| 'capability_unavailable_in_build'
|
|
56
|
+
/** The producer ran and errored (transport, HTTP status, timeout, thrown exception). */
|
|
57
|
+
| 'producer_failed'
|
|
58
|
+
/** The producer did not run — e.g. zero registered components, nothing to look at yet. */
|
|
59
|
+
| 'producer_not_run'
|
|
60
|
+
/** A required input was absent (no `elements` key, no bbox, no frame). */
|
|
61
|
+
| 'input_missing'
|
|
62
|
+
/** Observations existed but all fell under the caller's confidence floor. */
|
|
63
|
+
| 'below_confidence_floor'
|
|
64
|
+
/** A model reply could not be parsed/validated. */
|
|
65
|
+
| 'model_reply_unparseable'
|
|
66
|
+
/** The question needs more than one frame (e.g. `animation_settled`). */
|
|
67
|
+
| 'needs_multi_frame_input'
|
|
68
|
+
/** The input is known to be stale. */
|
|
69
|
+
| 'stale_input';
|
|
70
|
+
/** Every {@link UnknownCode}, in the canonical (wire-contract) order. */
|
|
71
|
+
export declare const UNKNOWN_CODES: readonly UnknownCode[];
|
|
72
|
+
/** Type guard: is `value` one of the closed {@link UnknownCode} members? */
|
|
73
|
+
export declare function isUnknownCode(value: unknown): value is UnknownCode;
|
|
74
|
+
/** Which producer answered, and at what version. */
|
|
75
|
+
export interface ObservationProducer {
|
|
76
|
+
/** e.g. `sdk/page-health`, `runner/vision-extract`, `vision-core/layout`. */
|
|
77
|
+
id: string;
|
|
78
|
+
/** The producing package's or crate's version. */
|
|
79
|
+
version: string;
|
|
80
|
+
}
|
|
81
|
+
/** One dimension the producer could NOT measure, how many items it affected, and why. */
|
|
82
|
+
export interface UnmeasuredDimension {
|
|
83
|
+
/** What went unmeasured, e.g. `geometry`, `text`. */
|
|
84
|
+
dimension: string;
|
|
85
|
+
/** How many considered items lacked it. */
|
|
86
|
+
count: number;
|
|
87
|
+
code: UnknownCode;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* How much of what the producer looked at it could actually measure. A
|
|
91
|
+
* non-empty `unmeasured` makes a `measured` observation DEGRADED (legal) and an
|
|
92
|
+
* `absent` one ILLEGAL; so does `considered === 0` for `absent`.
|
|
93
|
+
*/
|
|
94
|
+
export interface ObservationCoverage {
|
|
95
|
+
/** Items the producer considered. */
|
|
96
|
+
considered: number;
|
|
97
|
+
/** Items it could measure in full. */
|
|
98
|
+
measured: number;
|
|
99
|
+
/** Named dimensions it could not measure. Always present — `[]` is the statement "full coverage". */
|
|
100
|
+
unmeasured: UnmeasuredDimension[];
|
|
101
|
+
}
|
|
102
|
+
/** Provenance of a cached answer. Every key always present. */
|
|
103
|
+
export interface ObservationCache {
|
|
104
|
+
/** Whether this answer was served from the cache. */
|
|
105
|
+
hit: boolean;
|
|
106
|
+
/** When the served entry was stored. `null` on a miss or when the cache keeps no timestamp. */
|
|
107
|
+
storedAt: string | null;
|
|
108
|
+
/** The inputs the cache key was derived from, so a reader can judge what a hit is keyed on. */
|
|
109
|
+
keyInputs: string[];
|
|
110
|
+
}
|
|
111
|
+
/** Where an {@link Observation} came from. EVERY key is always present. */
|
|
112
|
+
export interface ObservationProvenance {
|
|
113
|
+
producer: ObservationProducer;
|
|
114
|
+
/** When the underlying state was sampled (snapshot time / frame capture). `null` = no sample was taken. */
|
|
115
|
+
observedAt: string | null;
|
|
116
|
+
/** When the producer ran. Never null. */
|
|
117
|
+
evaluatedAt: string;
|
|
118
|
+
coverage: ObservationCoverage;
|
|
119
|
+
/** Finite, in `[0, 1]`, or `null` = "a deduction, not an estimate". */
|
|
120
|
+
confidence: number | null;
|
|
121
|
+
/** `null` = this producer has no cache. */
|
|
122
|
+
cache: ObservationCache | null;
|
|
123
|
+
/** Source attribution as a JSON object, or `null` = the producer has none to give. */
|
|
124
|
+
source: Record<string, unknown> | null;
|
|
125
|
+
}
|
|
126
|
+
/** Why a producer could not answer. */
|
|
127
|
+
export interface ObservationUnknown {
|
|
128
|
+
code: UnknownCode;
|
|
129
|
+
/** Human prose. Never the only carrier of the reason — `code` is. */
|
|
130
|
+
detail: string;
|
|
131
|
+
}
|
|
132
|
+
/** The three answers, as the `status` tag spells them on the wire. */
|
|
133
|
+
export type ObservationStatus = 'measured' | 'absent' | 'unknown';
|
|
134
|
+
/** The producer looked and saw `value`. */
|
|
135
|
+
export interface MeasuredObservation<T> {
|
|
136
|
+
status: 'measured';
|
|
137
|
+
value: T;
|
|
138
|
+
provenance: ObservationProvenance;
|
|
139
|
+
}
|
|
140
|
+
/** The producer looked with FULL coverage and found nothing. */
|
|
141
|
+
export interface AbsentObservation {
|
|
142
|
+
status: 'absent';
|
|
143
|
+
provenance: ObservationProvenance;
|
|
144
|
+
}
|
|
145
|
+
/** The producer could not look. Carries no `value`. */
|
|
146
|
+
export interface UnknownObservation {
|
|
147
|
+
status: 'unknown';
|
|
148
|
+
unknown: ObservationUnknown;
|
|
149
|
+
provenance: ObservationProvenance;
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* The envelope. A discriminated union on `status`, so there is no `measured`
|
|
153
|
+
* without a `value`, no `unknown` without a `code`, and no `value` beside an
|
|
154
|
+
* `absent` — at compile time. The one invariant spanning the state AND the
|
|
155
|
+
* provenance (`absent` only over full coverage of at least one considered
|
|
156
|
+
* item) is enforced by
|
|
157
|
+
* {@link Observation.absent}.
|
|
158
|
+
*/
|
|
159
|
+
export type Observation<T> = MeasuredObservation<T> | AbsentObservation | UnknownObservation;
|
|
160
|
+
/** Thrown when a constructor is asked for an illegal envelope. */
|
|
161
|
+
export declare class ObservationError extends Error {
|
|
162
|
+
constructor(message: string);
|
|
163
|
+
}
|
|
164
|
+
/** A timestamp in any of the forms producers hold one in. */
|
|
165
|
+
export type ObservationTime = string | number | Date;
|
|
166
|
+
/** Inputs for {@link Observation.provenance}. Omitted optional keys become the explicit `null` statements. */
|
|
167
|
+
export interface ObservationProvenanceInit {
|
|
168
|
+
producer: ObservationProducer;
|
|
169
|
+
/** When the underlying state was sampled. Omitted / `null` = no sample was taken. */
|
|
170
|
+
observedAt?: ObservationTime | null;
|
|
171
|
+
/** When the producer ran. Defaults to now. */
|
|
172
|
+
evaluatedAt?: ObservationTime;
|
|
173
|
+
/** Defaults to `{ considered: 0, measured: 0, unmeasured: [] }`. */
|
|
174
|
+
coverage?: ObservationCoverage;
|
|
175
|
+
/** Omitted / `null` / non-finite = a deduction, not an estimate. */
|
|
176
|
+
confidence?: number | null;
|
|
177
|
+
/** Omitted / `null` = this producer has no cache. */
|
|
178
|
+
cache?: {
|
|
179
|
+
hit: boolean;
|
|
180
|
+
storedAt: ObservationTime | null;
|
|
181
|
+
keyInputs: string[];
|
|
182
|
+
} | null;
|
|
183
|
+
/** Omitted / `null` = no source attribution. */
|
|
184
|
+
source?: Record<string, unknown> | null;
|
|
185
|
+
}
|
|
186
|
+
/** Full coverage of `n` considered items. */
|
|
187
|
+
declare function fullCoverage(n: number): ObservationCoverage;
|
|
188
|
+
declare function provenance(init: ObservationProvenanceInit): ObservationProvenance;
|
|
189
|
+
/**
|
|
190
|
+
* The producer looked and saw `value`. THROWS {@link ObservationError} on
|
|
191
|
+
* `undefined`: a measured observation whose `value` key would vanish from
|
|
192
|
+
* the wire is exactly the missing-key shape the Rust canon refuses.
|
|
193
|
+
*/
|
|
194
|
+
declare function measured<T>(value: T, prov: ObservationProvenance): Observation<T>;
|
|
195
|
+
/**
|
|
196
|
+
* The producer looked and found nothing. "absent" means the producer
|
|
197
|
+
* considered at least one item, measured every one of them, and found
|
|
198
|
+
* nothing. THROWS {@link ObservationError}, checked in the Rust canon's order:
|
|
199
|
+
*
|
|
200
|
+
* 1. `coverage.unmeasured` is non-empty — never asserted over a region the
|
|
201
|
+
* producer did not measure;
|
|
202
|
+
* 2. `coverage.considered` is 0 — never asserted over nothing looked at (a
|
|
203
|
+
* producer with nothing to consider answers `unknown`, e.g.
|
|
204
|
+
* `producer_not_run` / `input_missing`);
|
|
205
|
+
* 3. `coverage.measured !== coverage.considered` — a partial look is
|
|
206
|
+
* `measured` with its gaps in `coverage.unmeasured`, never `absent`.
|
|
207
|
+
*/
|
|
208
|
+
declare function absent<T = never>(prov: ObservationProvenance): Observation<T>;
|
|
209
|
+
declare function unknown<T = never>(code: UnknownCode, detail: string, prov: ObservationProvenance): Observation<T>;
|
|
210
|
+
/**
|
|
211
|
+
* Constructors for the envelope. `Observation.measured` / `.absent` /
|
|
212
|
+
* `.unknown` are the ONLY way to build one; `.provenance` builds the block
|
|
213
|
+
* with every nullable key stated explicitly.
|
|
214
|
+
*
|
|
215
|
+
* @example
|
|
216
|
+
* ```ts
|
|
217
|
+
* // `Observation` is exported by the package that ships this module.
|
|
218
|
+
* const prov = Observation.provenance({
|
|
219
|
+
* producer: { id: 'sdk/page-health', version: '0.29.0' },
|
|
220
|
+
* observedAt: Date.now(),
|
|
221
|
+
* });
|
|
222
|
+
* const obs = Observation.unknown('input_missing', 'no elements key', prov);
|
|
223
|
+
* ```
|
|
224
|
+
*/
|
|
225
|
+
export declare const Observation: {
|
|
226
|
+
readonly provenance: typeof provenance;
|
|
227
|
+
readonly fullCoverage: typeof fullCoverage;
|
|
228
|
+
readonly measured: typeof measured;
|
|
229
|
+
readonly absent: typeof absent;
|
|
230
|
+
readonly unknown: typeof unknown;
|
|
231
|
+
};
|
|
232
|
+
export {};
|
|
233
|
+
//# sourceMappingURL=observation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"observation.d.ts","sourceRoot":"","sources":["../../src/observation/observation.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AAMH;;;;GAIG;AACH,MAAM,MAAM,WAAW;AACrB,2CAA2C;AACzC,iBAAiB;AACnB,gGAAgG;GAC9F,iCAAiC;AACnC,wFAAwF;GACtF,iBAAiB;AACnB,0FAA0F;GACxF,kBAAkB;AACpB,0EAA0E;GACxE,eAAe;AACjB,6EAA6E;GAC3E,wBAAwB;AAC1B,mDAAmD;GACjD,yBAAyB;AAC3B,yEAAyE;GACvE,yBAAyB;AAC3B,sCAAsC;GACpC,aAAa,CAAC;AAElB,yEAAyE;AACzE,eAAO,MAAM,aAAa,EAAE,SAAS,WAAW,EAUtC,CAAC;AAEX,4EAA4E;AAC5E,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,WAAW,CAElE;AAMD,oDAAoD;AACpD,MAAM,WAAW,mBAAmB;IAClC,6EAA6E;IAC7E,EAAE,EAAE,MAAM,CAAC;IACX,kDAAkD;IAClD,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,yFAAyF;AACzF,MAAM,WAAW,mBAAmB;IAClC,qDAAqD;IACrD,SAAS,EAAE,MAAM,CAAC;IAClB,2CAA2C;IAC3C,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,WAAW,CAAC;CACnB;AAED;;;;GAIG;AACH,MAAM,WAAW,mBAAmB;IAClC,qCAAqC;IACrC,UAAU,EAAE,MAAM,CAAC;IACnB,sCAAsC;IACtC,QAAQ,EAAE,MAAM,CAAC;IACjB,qGAAqG;IACrG,UAAU,EAAE,mBAAmB,EAAE,CAAC;CACnC;AAED,+DAA+D;AAC/D,MAAM,WAAW,gBAAgB;IAC/B,qDAAqD;IACrD,GAAG,EAAE,OAAO,CAAC;IACb,+FAA+F;IAC/F,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,+FAA+F;IAC/F,SAAS,EAAE,MAAM,EAAE,CAAC;CACrB;AAED,2EAA2E;AAC3E,MAAM,WAAW,qBAAqB;IACpC,QAAQ,EAAE,mBAAmB,CAAC;IAC9B,2GAA2G;IAC3G,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,yCAAyC;IACzC,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,mBAAmB,CAAC;IAC9B,uEAAuE;IACvE,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,2CAA2C;IAC3C,KAAK,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAC/B,sFAAsF;IACtF,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;CACxC;AAED,uCAAuC;AACvC,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,WAAW,CAAC;IAClB,qEAAqE;IACrE,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,sEAAsE;AACtE,MAAM,MAAM,iBAAiB,GAAG,UAAU,GAAG,QAAQ,GAAG,SAAS,CAAC;AAElE,2CAA2C;AAC3C,MAAM,WAAW,mBAAmB,CAAC,CAAC;IACpC,MAAM,EAAE,UAAU,CAAC;IACnB,KAAK,EAAE,CAAC,CAAC;IACT,UAAU,EAAE,qBAAqB,CAAC;CACnC;AAED,gEAAgE;AAChE,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,QAAQ,CAAC;IACjB,UAAU,EAAE,qBAAqB,CAAC;CACnC;AAED,uDAAuD;AACvD,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,SAAS,CAAC;IAClB,OAAO,EAAE,kBAAkB,CAAC;IAC5B,UAAU,EAAE,qBAAqB,CAAC;CACnC;AAED;;;;;;;GAOG;AACH,MAAM,MAAM,WAAW,CAAC,CAAC,IAAI,mBAAmB,CAAC,CAAC,CAAC,GAAG,iBAAiB,GAAG,kBAAkB,CAAC;AAM7F,kEAAkE;AAClE,qBAAa,gBAAiB,SAAQ,KAAK;gBAC7B,OAAO,EAAE,MAAM;CAI5B;AAED,6DAA6D;AAC7D,MAAM,MAAM,eAAe,GAAG,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;AAgBrD,8GAA8G;AAC9G,MAAM,WAAW,yBAAyB;IACxC,QAAQ,EAAE,mBAAmB,CAAC;IAC9B,qFAAqF;IACrF,UAAU,CAAC,EAAE,eAAe,GAAG,IAAI,CAAC;IACpC,8CAA8C;IAC9C,WAAW,CAAC,EAAE,eAAe,CAAC;IAC9B,oEAAoE;IACpE,QAAQ,CAAC,EAAE,mBAAmB,CAAC;IAC/B,oEAAoE;IACpE,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,qDAAqD;IACrD,KAAK,CAAC,EAAE;QAAE,GAAG,EAAE,OAAO,CAAC;QAAC,QAAQ,EAAE,eAAe,GAAG,IAAI,CAAC;QAAC,SAAS,EAAE,MAAM,EAAE,CAAA;KAAE,GAAG,IAAI,CAAC;IACvF,gDAAgD;IAChD,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;CACzC;AAED,6CAA6C;AAC7C,iBAAS,YAAY,CAAC,CAAC,EAAE,MAAM,GAAG,mBAAmB,CAEpD;AAuHD,iBAAS,UAAU,CAAC,IAAI,EAAE,yBAAyB,GAAG,qBAAqB,CAsC1E;AAED;;;;GAIG;AACH,iBAAS,QAAQ,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,qBAAqB,GAAG,WAAW,CAAC,CAAC,CAAC,CAQ1E;AAED;;;;;;;;;;;;GAYG;AACH,iBAAS,MAAM,CAAC,CAAC,GAAG,KAAK,EAAE,IAAI,EAAE,qBAAqB,GAAG,WAAW,CAAC,CAAC,CAAC,CAuBtE;AAED,iBAAS,OAAO,CAAC,CAAC,GAAG,KAAK,EACxB,IAAI,EAAE,WAAW,EACjB,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,qBAAqB,GAC1B,WAAW,CAAC,CAAC,CAAC,CAShB;AAED;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,WAAW;;;;;;CAMd,CAAC"}
|
package/dist/react/index.js
CHANGED
|
@@ -2381,7 +2381,7 @@ var DefaultNativeActionExecutor = class {
|
|
|
2381
2381
|
if (rect && clip && !isEmptyRect(clip) && rect.right > rect.left && rect.bottom > rect.top && rect.top >= clip.top && rect.left >= clip.left && rect.bottom <= clip.bottom && rect.right <= clip.right) {
|
|
2382
2382
|
return { alreadyVisible: true, scrolled: false };
|
|
2383
2383
|
}
|
|
2384
|
-
const
|
|
2384
|
+
const measured2 = await new Promise(
|
|
2385
2385
|
(resolve, reject) => {
|
|
2386
2386
|
let settled = false;
|
|
2387
2387
|
const timer = setTimeout(() => {
|
|
@@ -2418,13 +2418,13 @@ var DefaultNativeActionExecutor = class {
|
|
|
2418
2418
|
}
|
|
2419
2419
|
}
|
|
2420
2420
|
);
|
|
2421
|
-
if (ancestorRect && Number.isFinite(
|
|
2421
|
+
if (ancestorRect && Number.isFinite(measured2.x) && Number.isFinite(measured2.width) && measured2.x + measured2.width > ancestorRect.right - ancestorRect.left + 1) {
|
|
2422
2422
|
throw new NativeActionError(
|
|
2423
2423
|
`scrollIntoView is not supported on element "${element.id}": it is laid out beyond the horizontal extent of its scroll ancestor "${ancestorId}" (sideways overflow, a horizontal container, or a nested horizontal container \u2014 declare that inner container as its scrollAncestorId); only vertical scrolling is supported`,
|
|
2424
2424
|
"NOT_SUPPORTED"
|
|
2425
2425
|
);
|
|
2426
2426
|
}
|
|
2427
|
-
container.scrollTo({ y: Math.max(0,
|
|
2427
|
+
container.scrollTo({ y: Math.max(0, measured2.y - padding), animated: false });
|
|
2428
2428
|
return { alreadyVisible: false, scrolled: true };
|
|
2429
2429
|
}
|
|
2430
2430
|
/**
|
|
@@ -3161,7 +3161,219 @@ function createDesignHandlers(registry, viewportProvider) {
|
|
|
3161
3161
|
};
|
|
3162
3162
|
}
|
|
3163
3163
|
|
|
3164
|
+
// src/observation/observation.ts
|
|
3165
|
+
var UNKNOWN_CODES = [
|
|
3166
|
+
"app_unreachable",
|
|
3167
|
+
"capability_unavailable_in_build",
|
|
3168
|
+
"producer_failed",
|
|
3169
|
+
"producer_not_run",
|
|
3170
|
+
"input_missing",
|
|
3171
|
+
"below_confidence_floor",
|
|
3172
|
+
"model_reply_unparseable",
|
|
3173
|
+
"needs_multi_frame_input",
|
|
3174
|
+
"stale_input"
|
|
3175
|
+
];
|
|
3176
|
+
function isUnknownCode(value) {
|
|
3177
|
+
return typeof value === "string" && UNKNOWN_CODES.includes(value);
|
|
3178
|
+
}
|
|
3179
|
+
var ObservationError = class extends Error {
|
|
3180
|
+
constructor(message) {
|
|
3181
|
+
super(message);
|
|
3182
|
+
this.name = "ObservationError";
|
|
3183
|
+
}
|
|
3184
|
+
};
|
|
3185
|
+
function toRfc3339(t) {
|
|
3186
|
+
const d = t instanceof Date ? t : new Date(t);
|
|
3187
|
+
if (Number.isNaN(d.getTime())) {
|
|
3188
|
+
throw new ObservationError(`not a valid timestamp: ${String(t)}`);
|
|
3189
|
+
}
|
|
3190
|
+
return d.toISOString();
|
|
3191
|
+
}
|
|
3192
|
+
function normalizeConfidence(c) {
|
|
3193
|
+
if (c === null || c === void 0 || !Number.isFinite(c)) return null;
|
|
3194
|
+
return Math.min(1, Math.max(0, c));
|
|
3195
|
+
}
|
|
3196
|
+
function fullCoverage(n) {
|
|
3197
|
+
return { considered: n, measured: n, unmeasured: [] };
|
|
3198
|
+
}
|
|
3199
|
+
function assertCount(value, what) {
|
|
3200
|
+
if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 0) {
|
|
3201
|
+
throw new ObservationError(`${what} must be a non-negative integer, got ${String(value)}`);
|
|
3202
|
+
}
|
|
3203
|
+
}
|
|
3204
|
+
function assertCoverage(coverage) {
|
|
3205
|
+
if (!coverage || typeof coverage !== "object") {
|
|
3206
|
+
throw new ObservationError("coverage must be an object");
|
|
3207
|
+
}
|
|
3208
|
+
assertCount(coverage.considered, "coverage.considered");
|
|
3209
|
+
assertCount(coverage.measured, "coverage.measured");
|
|
3210
|
+
if (!Array.isArray(coverage.unmeasured)) {
|
|
3211
|
+
throw new ObservationError("coverage.unmeasured must be an array");
|
|
3212
|
+
}
|
|
3213
|
+
coverage.unmeasured.forEach((u, i) => {
|
|
3214
|
+
if (!u || typeof u.dimension !== "string") {
|
|
3215
|
+
throw new ObservationError(`coverage.unmeasured[${i}].dimension must be a string`);
|
|
3216
|
+
}
|
|
3217
|
+
assertCount(u.count, `coverage.unmeasured[${i}].count`);
|
|
3218
|
+
if (!isUnknownCode(u.code)) {
|
|
3219
|
+
throw new ObservationError(
|
|
3220
|
+
`coverage.unmeasured[${i}].code is not an UnknownCode: ${String(u.code)}`
|
|
3221
|
+
);
|
|
3222
|
+
}
|
|
3223
|
+
});
|
|
3224
|
+
}
|
|
3225
|
+
function assertSource(source) {
|
|
3226
|
+
if (source === null) return;
|
|
3227
|
+
if (typeof source !== "object" || Array.isArray(source)) {
|
|
3228
|
+
throw new ObservationError("source must be a JSON object or null");
|
|
3229
|
+
}
|
|
3230
|
+
}
|
|
3231
|
+
var RFC3339 = /^(\d{4})-(\d{2})-(\d{2})[Tt ](\d{2}):(\d{2}):(\d{2})(\.\d+)?([Zz]|[+-](\d{2}):(\d{2}))$/;
|
|
3232
|
+
function isRfc3339(value) {
|
|
3233
|
+
if (typeof value !== "string") return false;
|
|
3234
|
+
const m = RFC3339.exec(value);
|
|
3235
|
+
if (!m) return false;
|
|
3236
|
+
const [y, mo, d, h, mi, s] = [m[1], m[2], m[3], m[4], m[5], m[6]].map(Number);
|
|
3237
|
+
if (mo < 1 || mo > 12 || d < 1 || h > 23 || mi > 59 || s > 60) return false;
|
|
3238
|
+
const leap = y % 4 === 0 && y % 100 !== 0 || y % 400 === 0;
|
|
3239
|
+
const daysInMonth = [31, leap ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][mo - 1];
|
|
3240
|
+
if (d > daysInMonth) return false;
|
|
3241
|
+
if (m[9] !== void 0 && (Number(m[9]) > 23 || Number(m[10]) > 59)) return false;
|
|
3242
|
+
return true;
|
|
3243
|
+
}
|
|
3244
|
+
function assertProvenance(prov) {
|
|
3245
|
+
if (!prov || typeof prov !== "object") throw new ObservationError("provenance is required");
|
|
3246
|
+
if (!prov.producer || typeof prov.producer.id !== "string" || typeof prov.producer.version !== "string") {
|
|
3247
|
+
throw new ObservationError("producer must carry a string id and version");
|
|
3248
|
+
}
|
|
3249
|
+
if (!isRfc3339(prov.evaluatedAt)) {
|
|
3250
|
+
throw new ObservationError(
|
|
3251
|
+
`evaluatedAt must be an RFC3339 date-time, got ${String(prov.evaluatedAt)}`
|
|
3252
|
+
);
|
|
3253
|
+
}
|
|
3254
|
+
if (prov.observedAt !== null && !isRfc3339(prov.observedAt)) {
|
|
3255
|
+
throw new ObservationError(
|
|
3256
|
+
`observedAt must be an RFC3339 date-time or null, got ${String(prov.observedAt)}`
|
|
3257
|
+
);
|
|
3258
|
+
}
|
|
3259
|
+
assertCoverage(prov.coverage);
|
|
3260
|
+
if (prov.confidence !== null && (typeof prov.confidence !== "number" || !Number.isFinite(prov.confidence) || prov.confidence < 0 || prov.confidence > 1)) {
|
|
3261
|
+
throw new ObservationError(
|
|
3262
|
+
`confidence must be in [0, 1] or null, got ${String(prov.confidence)}`
|
|
3263
|
+
);
|
|
3264
|
+
}
|
|
3265
|
+
if (prov.cache !== null) {
|
|
3266
|
+
if (typeof prov.cache !== "object" || typeof prov.cache.hit !== "boolean" || !Array.isArray(prov.cache.keyInputs) || !prov.cache.keyInputs.every((k) => typeof k === "string")) {
|
|
3267
|
+
throw new ObservationError(
|
|
3268
|
+
"cache must be { hit: boolean, storedAt, keyInputs: string[] } or null"
|
|
3269
|
+
);
|
|
3270
|
+
}
|
|
3271
|
+
if (prov.cache.storedAt !== null && !isRfc3339(prov.cache.storedAt)) {
|
|
3272
|
+
throw new ObservationError(
|
|
3273
|
+
`cache.storedAt must be an RFC3339 date-time or null, got ${String(prov.cache.storedAt)}`
|
|
3274
|
+
);
|
|
3275
|
+
}
|
|
3276
|
+
}
|
|
3277
|
+
assertSource(prov.source);
|
|
3278
|
+
}
|
|
3279
|
+
function provenance(init) {
|
|
3280
|
+
if (!init.producer || typeof init.producer.id !== "string" || typeof init.producer.version !== "string") {
|
|
3281
|
+
throw new ObservationError("producer must carry a string id and version");
|
|
3282
|
+
}
|
|
3283
|
+
const coverage = init.coverage ?? fullCoverage(0);
|
|
3284
|
+
assertCoverage(coverage);
|
|
3285
|
+
const source = init.source ?? null;
|
|
3286
|
+
assertSource(source);
|
|
3287
|
+
const built = {
|
|
3288
|
+
producer: { id: init.producer.id, version: init.producer.version },
|
|
3289
|
+
observedAt: init.observedAt === void 0 || init.observedAt === null ? null : toRfc3339(init.observedAt),
|
|
3290
|
+
evaluatedAt: toRfc3339(init.evaluatedAt ?? /* @__PURE__ */ new Date()),
|
|
3291
|
+
coverage: {
|
|
3292
|
+
considered: coverage.considered,
|
|
3293
|
+
measured: coverage.measured,
|
|
3294
|
+
unmeasured: coverage.unmeasured.map((u) => ({
|
|
3295
|
+
dimension: u.dimension,
|
|
3296
|
+
count: u.count,
|
|
3297
|
+
code: u.code
|
|
3298
|
+
}))
|
|
3299
|
+
},
|
|
3300
|
+
confidence: normalizeConfidence(init.confidence),
|
|
3301
|
+
cache: init.cache ? {
|
|
3302
|
+
hit: init.cache.hit,
|
|
3303
|
+
storedAt: init.cache.storedAt === null ? null : toRfc3339(init.cache.storedAt),
|
|
3304
|
+
keyInputs: [...init.cache.keyInputs]
|
|
3305
|
+
} : null,
|
|
3306
|
+
source
|
|
3307
|
+
};
|
|
3308
|
+
assertProvenance(built);
|
|
3309
|
+
return built;
|
|
3310
|
+
}
|
|
3311
|
+
function measured(value, prov) {
|
|
3312
|
+
if (value === void 0) {
|
|
3313
|
+
throw new ObservationError(
|
|
3314
|
+
'measured requires a value; report "nothing there" as absent and "could not look" as unknown'
|
|
3315
|
+
);
|
|
3316
|
+
}
|
|
3317
|
+
assertProvenance(prov);
|
|
3318
|
+
return { status: "measured", value, provenance: prov };
|
|
3319
|
+
}
|
|
3320
|
+
function absent(prov) {
|
|
3321
|
+
assertProvenance(prov);
|
|
3322
|
+
const { considered, measured: measured2, unmeasured } = prov.coverage;
|
|
3323
|
+
if (unmeasured.length > 0) {
|
|
3324
|
+
const dims = unmeasured.map((u) => `${u.dimension} x${u.count} (${u.code})`).join(", ");
|
|
3325
|
+
throw new ObservationError(
|
|
3326
|
+
`cannot assert absent over unmeasured coverage: ${dims}. Report the observation as measured (degraded) or unknown instead.`
|
|
3327
|
+
);
|
|
3328
|
+
}
|
|
3329
|
+
if (considered === 0) {
|
|
3330
|
+
throw new ObservationError(
|
|
3331
|
+
"cannot assert absent over zero considered items: the producer looked at nothing. Report the observation as unknown instead."
|
|
3332
|
+
);
|
|
3333
|
+
}
|
|
3334
|
+
if (measured2 !== considered) {
|
|
3335
|
+
throw new ObservationError(
|
|
3336
|
+
`an \`absent\` observation must have measured every considered item (measured ${measured2} of ${considered}); a partial look is \`measured\` with its gaps in \`coverage.unmeasured\`, never \`absent\``
|
|
3337
|
+
);
|
|
3338
|
+
}
|
|
3339
|
+
return { status: "absent", provenance: prov };
|
|
3340
|
+
}
|
|
3341
|
+
function unknown(code, detail, prov) {
|
|
3342
|
+
if (!isUnknownCode(code)) {
|
|
3343
|
+
throw new ObservationError(`not an UnknownCode: ${String(code)}`);
|
|
3344
|
+
}
|
|
3345
|
+
if (typeof detail !== "string") {
|
|
3346
|
+
throw new ObservationError("unknown requires a string detail");
|
|
3347
|
+
}
|
|
3348
|
+
assertProvenance(prov);
|
|
3349
|
+
return { status: "unknown", unknown: { code, detail }, provenance: prov };
|
|
3350
|
+
}
|
|
3351
|
+
var Observation = {
|
|
3352
|
+
provenance,
|
|
3353
|
+
fullCoverage,
|
|
3354
|
+
measured,
|
|
3355
|
+
absent,
|
|
3356
|
+
unknown
|
|
3357
|
+
};
|
|
3358
|
+
|
|
3164
3359
|
// src/server/page-health.ts
|
|
3360
|
+
var NATIVE_PAGE_HEALTH_PRODUCER_ID = "sdk-native/page-health";
|
|
3361
|
+
function defaultProducer() {
|
|
3362
|
+
return {
|
|
3363
|
+
id: NATIVE_PAGE_HEALTH_PRODUCER_ID,
|
|
3364
|
+
version: "0.7.0"
|
|
3365
|
+
};
|
|
3366
|
+
}
|
|
3367
|
+
function pageHealthUnknown(code, detail, options = {}) {
|
|
3368
|
+
return Observation.unknown(
|
|
3369
|
+
code,
|
|
3370
|
+
detail,
|
|
3371
|
+
Observation.provenance({
|
|
3372
|
+
producer: options.producer ?? defaultProducer(),
|
|
3373
|
+
evaluatedAt: options.evaluatedAt
|
|
3374
|
+
})
|
|
3375
|
+
);
|
|
3376
|
+
}
|
|
3165
3377
|
var GRID_SIZE = 20;
|
|
3166
3378
|
var NAV_TYPES = /* @__PURE__ */ new Set(["button", "heading", "badge", "status-message"]);
|
|
3167
3379
|
var SKIP_TEXT_TYPES = /* @__PURE__ */ new Set(["button", "link", "tab", "menuitem"]);
|
|
@@ -3230,13 +3442,66 @@ function normalizeRect(state, viewport) {
|
|
|
3230
3442
|
height: layout.height / viewport.height
|
|
3231
3443
|
};
|
|
3232
3444
|
}
|
|
3233
|
-
function diagnosePageHealth(
|
|
3445
|
+
function diagnosePageHealth(input, options = {}) {
|
|
3446
|
+
const producer = options.producer ?? defaultProducer();
|
|
3447
|
+
try {
|
|
3448
|
+
return observe(input, producer, options.evaluatedAt);
|
|
3449
|
+
} catch (err) {
|
|
3450
|
+
return Observation.unknown(
|
|
3451
|
+
"producer_failed",
|
|
3452
|
+
`page-health producer threw: ${err instanceof Error ? err.message : String(err)}`,
|
|
3453
|
+
Observation.provenance({ producer })
|
|
3454
|
+
);
|
|
3455
|
+
}
|
|
3456
|
+
}
|
|
3457
|
+
function observe(input, producer, evaluatedAt) {
|
|
3458
|
+
const base = {
|
|
3459
|
+
producer,
|
|
3460
|
+
observedAt: input.observedAt,
|
|
3461
|
+
evaluatedAt
|
|
3462
|
+
};
|
|
3463
|
+
if (!Array.isArray(input.elements)) {
|
|
3464
|
+
return Observation.unknown(
|
|
3465
|
+
"input_missing",
|
|
3466
|
+
"the registry handed over no `elements` array, so there was nothing to analyze; this says nothing about the screen itself",
|
|
3467
|
+
Observation.provenance({ ...base, observedAt: null })
|
|
3468
|
+
);
|
|
3469
|
+
}
|
|
3470
|
+
const elements = input.elements;
|
|
3471
|
+
const viewport = input.viewport;
|
|
3472
|
+
if (!viewport || !(viewport.width > 0) || !(viewport.height > 0)) {
|
|
3473
|
+
return Observation.unknown(
|
|
3474
|
+
"input_missing",
|
|
3475
|
+
viewport ? `the device viewport is degenerate (${viewport.width}x${viewport.height}), so no element can be placed on screen \u2014 pass \`body.viewport\` or wire \`viewportProvider\`` : "the device viewport is unknown, so no element can be placed on screen \u2014 pass `body.viewport` or wire `viewportProvider`",
|
|
3476
|
+
Observation.provenance(base)
|
|
3477
|
+
);
|
|
3478
|
+
}
|
|
3479
|
+
if (elements.length === 0 && (input.registeredComponents ?? 0) === 0) {
|
|
3480
|
+
return Observation.unknown(
|
|
3481
|
+
"producer_not_run",
|
|
3482
|
+
"the registry reports zero elements and zero components \u2014 nothing has registered yet, so there was nothing to look at",
|
|
3483
|
+
Observation.provenance(base)
|
|
3484
|
+
);
|
|
3485
|
+
}
|
|
3486
|
+
return analyze(elements, viewport, base);
|
|
3487
|
+
}
|
|
3488
|
+
function analyze(elements, viewport, base) {
|
|
3234
3489
|
const findings = [];
|
|
3235
|
-
const
|
|
3236
|
-
|
|
3237
|
-
rect
|
|
3238
|
-
|
|
3239
|
-
|
|
3490
|
+
const considered = elements.filter((el) => el.state.visible);
|
|
3491
|
+
const visible = considered.flatMap((el) => {
|
|
3492
|
+
const rect = normalizeRect(el.state, viewport);
|
|
3493
|
+
return rect ? [{ el, rect }] : [];
|
|
3494
|
+
});
|
|
3495
|
+
const withoutGeometry = considered.length - visible.length;
|
|
3496
|
+
const unmeasured = withoutGeometry > 0 ? [{ dimension: "geometry", count: withoutGeometry, code: "input_missing" }] : [];
|
|
3497
|
+
const coverage = { considered: considered.length, measured: visible.length, unmeasured };
|
|
3498
|
+
if (considered.length > 0 && visible.length === 0) {
|
|
3499
|
+
return Observation.unknown(
|
|
3500
|
+
"input_missing",
|
|
3501
|
+
`all ${considered.length} visible element(s) have no measured layout yet, so spatial coverage, layout regions and visual anomalies cannot be measured`,
|
|
3502
|
+
Observation.provenance({ ...base, coverage })
|
|
3503
|
+
);
|
|
3504
|
+
}
|
|
3240
3505
|
const grid = Array.from(
|
|
3241
3506
|
{ length: GRID_SIZE },
|
|
3242
3507
|
() => Array.from({ length: GRID_SIZE }, () => false)
|
|
@@ -3391,13 +3656,17 @@ function diagnosePageHealth(elements, viewport) {
|
|
|
3391
3656
|
const heatmap = grid.map(
|
|
3392
3657
|
(row) => row.map((cell) => cell ? "#" : ".").join("")
|
|
3393
3658
|
);
|
|
3394
|
-
return
|
|
3395
|
-
|
|
3396
|
-
|
|
3397
|
-
|
|
3398
|
-
|
|
3399
|
-
|
|
3400
|
-
|
|
3659
|
+
return Observation.measured(
|
|
3660
|
+
{
|
|
3661
|
+
summary: worstSeverity(findings),
|
|
3662
|
+
findings,
|
|
3663
|
+
heatmap,
|
|
3664
|
+
element_count: elements.length,
|
|
3665
|
+
visible_count: visible.length
|
|
3666
|
+
},
|
|
3667
|
+
// A deduction over the registry, not an estimate.
|
|
3668
|
+
Observation.provenance({ ...base, coverage })
|
|
3669
|
+
);
|
|
3401
3670
|
}
|
|
3402
3671
|
|
|
3403
3672
|
// src/server/handlers.ts
|
|
@@ -3845,13 +4114,30 @@ function createServerHandlers(registry, executor, config) {
|
|
|
3845
4114
|
},
|
|
3846
4115
|
getPageHealth: async (ctx) => {
|
|
3847
4116
|
const bodyViewport = ctx.body?.viewport;
|
|
3848
|
-
|
|
3849
|
-
|
|
3850
|
-
|
|
3851
|
-
|
|
3852
|
-
|
|
3853
|
-
|
|
3854
|
-
|
|
4117
|
+
let viewport;
|
|
4118
|
+
try {
|
|
4119
|
+
viewport = bodyViewport ?? config?.viewportProvider?.() ?? null;
|
|
4120
|
+
} catch {
|
|
4121
|
+
viewport = null;
|
|
4122
|
+
}
|
|
4123
|
+
const observedAt = Date.now();
|
|
4124
|
+
let elements;
|
|
4125
|
+
let registeredComponents;
|
|
4126
|
+
try {
|
|
4127
|
+
elements = registry.getAllElements().map((e) => ({
|
|
4128
|
+
type: e.type,
|
|
4129
|
+
state: e.getState()
|
|
4130
|
+
}));
|
|
4131
|
+
registeredComponents = registry.getAllComponents().length;
|
|
4132
|
+
} catch (err) {
|
|
4133
|
+
return success2(
|
|
4134
|
+
pageHealthUnknown(
|
|
4135
|
+
"producer_failed",
|
|
4136
|
+
`could not read the registry: ${err instanceof Error ? err.message : String(err)}`
|
|
4137
|
+
)
|
|
4138
|
+
);
|
|
4139
|
+
}
|
|
4140
|
+
return success2(diagnosePageHealth({ elements, registeredComponents, viewport, observedAt }));
|
|
3855
4141
|
},
|
|
3856
4142
|
// Workflows
|
|
3857
4143
|
getWorkflows: async () => {
|
|
@@ -4355,7 +4641,7 @@ function createServerHandlers(registry, executor, config) {
|
|
|
4355
4641
|
};
|
|
4356
4642
|
if (config?.appInfo) {
|
|
4357
4643
|
response.uiBridge = {
|
|
4358
|
-
version: "0.
|
|
4644
|
+
version: "0.7.0",
|
|
4359
4645
|
...config.appInfo,
|
|
4360
4646
|
capabilities: ["elements", "components", "actions", "design"],
|
|
4361
4647
|
elementCount: stats.elements,
|