@ggui-ai/ui-gen 0.25.0 → 0.26.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/README.md +2 -2
- package/dist/{adapter-Cg_rkpGq.d.ts → adapter-BJwn74rx.d.ts} +49 -1
- package/dist/adapters/generation-dispatch.d.ts +8 -5
- package/dist/adapters/generation-dispatch.js +1118 -193
- package/dist/adapters/generation-dispatch.js.map +1 -1
- package/dist/adapters/index.js +27 -6
- package/dist/adapters/index.js.map +1 -1
- package/dist/advanced/index.d.ts +10 -9
- package/dist/advanced/index.js +1126 -202
- package/dist/advanced/index.js.map +1 -1
- package/dist/blueprint-validator.js +416 -92
- package/dist/blueprint-validator.js.map +1 -1
- package/dist/boilerplate.d.ts +2 -2
- package/dist/boilerplate.js +10 -3
- package/dist/boilerplate.js.map +1 -1
- package/dist/check/index.d.ts +3 -2
- package/dist/check/index.js +9 -2
- package/dist/check/index.js.map +1 -1
- package/dist/{create-ui-generator-DFnnArrv.d.ts → create-ui-generator-N4gRXpcL.d.ts} +3 -4
- package/dist/{design-mode-DLufhrNp.d.ts → design-mode-DGWN_Gkm.d.ts} +1 -1
- package/dist/{dispatch-CStl9eAw.d.ts → dispatch-BUoXRj_r.d.ts} +2 -2
- package/dist/evaluation/axis-checks/index.d.ts +5 -4
- package/dist/evaluation/axis-checks/index.js.map +1 -1
- package/dist/evaluation/axis-checks/registry.d.ts +4 -3
- package/dist/evaluation/index.d.ts +107 -539
- package/dist/evaluation/index.js +552 -55
- package/dist/evaluation/index.js.map +1 -1
- package/dist/evaluation/types.d.ts +17 -0
- package/dist/evaluation/types.js.map +1 -1
- package/dist/{generate-9IeNT4ZY.d.ts → generate-tSO_shQ0.d.ts} +1 -1
- package/dist/harness/check/runtime-render/index.d.ts +5 -5
- package/dist/harness/check/runtime-render/index.js +416 -92
- package/dist/harness/check/runtime-render/index.js.map +1 -1
- package/dist/harness/check/runtime-render/render-check-worker.js +393 -88
- package/dist/harness/check/runtime-render/render-check-worker.js.map +1 -1
- package/dist/harness/index.d.ts +4 -5
- package/dist/harness/index.js +36 -5
- package/dist/harness/index.js.map +1 -1
- package/dist/harness/prompts.d.ts +2 -2
- package/dist/harness/prompts.js +10 -1
- package/dist/harness/prompts.js.map +1 -1
- package/dist/harness/result-types.d.ts +17 -3
- package/dist/harness/result-types.js.map +1 -1
- package/dist/harness/runtime.d.ts +14 -6
- package/dist/harness/runtime.js +11 -4
- package/dist/harness/runtime.js.map +1 -1
- package/dist/harness/types-public.d.ts +4 -2
- package/dist/harness/types-public.js +10 -3
- package/dist/harness/types-public.js.map +1 -1
- package/dist/harness/validator-trace-sink.d.ts +4 -3
- package/dist/index.d.ts +46 -11
- package/dist/index.js +1128 -200
- package/dist/index.js.map +1 -1
- package/dist/patch.d.ts +3 -3
- package/dist/{prompts-YU9piOgs.d.ts → prompts-BhNUuRoZ.d.ts} +1 -1
- package/dist/tools/render-check-worker.js +8 -1
- package/dist/tools/render-check-worker.js.map +1 -1
- package/dist/{types-public-BJTLpzWn.d.ts → types-public-B7wn4ce5.d.ts} +384 -3
- package/dist/validation/index.d.ts +1 -1
- package/dist/validation/index.js +1 -1
- package/dist/validation/index.js.map +1 -1
- package/dist/visual-evaluator-C9L-o6ap.d.ts +639 -0
- package/dist/workflows.d.ts +3 -3
- package/package.json +12 -12
- package/src/adapters/generation-dispatch.ts +2 -0
- package/src/boilerplate/free-design-prompt.ts +3 -3
- package/src/boilerplate/hard-sections.ts +1 -1
- package/src/boilerplate/templates/layouts/chat-mobile.tsx.tmpl +3 -1
- package/src/boilerplate/templates/layouts/chat-universal.tsx.tmpl +3 -1
- package/src/boilerplate/templates/layouts/fullscreen-desktop.tsx.tmpl +3 -1
- package/src/boilerplate/templates/layouts/fullscreen-universal.tsx.tmpl +3 -1
- package/src/check/run-tier0.ts +6 -3
- package/src/contract-context.ts +1 -1
- package/src/evaluation/cost-tracker.ts +54 -6
- package/src/evaluation/criteria/edge-probe.ts +286 -0
- package/src/evaluation/criteria/edge.ts +202 -0
- package/src/evaluation/criteria/resolve.ts +55 -7
- package/src/evaluation/index.ts +5 -1
- package/src/evaluation/judge-spend.ts +48 -0
- package/src/evaluation/types-public.ts +91 -0
- package/src/evaluation/types.ts +10 -0
- package/src/evaluation/visual-evaluator.ts +236 -40
- package/src/harness/check/runtime-render/adapter.ts +18 -0
- package/src/harness/check/runtime-render/probe-pending.ts +111 -0
- package/src/harness/check/runtime-render/render-check-host.ts +10 -6
- package/src/harness/check/runtime-render/render-check-worker.ts +6 -3
- package/src/harness/check/runtime-render/render-check.ts +417 -133
- package/src/harness/check/runtime-render/walk.ts +200 -0
- package/src/harness/coding/assemble-result.ts +2 -0
- package/src/harness/coding/generate-task-runner.ts +27 -0
- package/src/harness/coding/init-session.ts +5 -3
- package/src/harness/coding/run-coding-turn.ts +5 -1
- package/src/harness/coding/run-eval-round.ts +63 -3
- package/src/harness/in-loop-evaluation.ts +39 -0
- package/src/harness/llm-router.ts +58 -1
- package/src/harness/result-types.ts +9 -0
- package/src/harness/runtime.ts +9 -0
- package/src/harness/types-public.ts +10 -1
- package/src/index.ts +5 -0
- package/src/internal/verdict-line.ts +33 -0
- package/src/taught-tokens.ts +32 -0
- package/src/tools/render-check-worker.ts +2 -1
- package/src/tools/render-check.ts +8 -7
- package/src/validation/primitives.ts +1 -1
- package/dist/llm-router-nfO7wPec.d.ts +0 -267
- package/dist/{bank-CT4eEkta.d.ts → bank-Ctdp3FUA.d.ts} +3 -3
|
@@ -17,6 +17,22 @@ var __export = (target, all) => {
|
|
|
17
17
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
18
18
|
};
|
|
19
19
|
|
|
20
|
+
// src/internal/verdict-line.ts
|
|
21
|
+
function parseVerdictLine(stdout) {
|
|
22
|
+
const lines = stdout.split("\n");
|
|
23
|
+
for (let i = lines.length - 1; i >= 0; i -= 1) {
|
|
24
|
+
const line = lines[i];
|
|
25
|
+
if (line.startsWith(`${VERDICT_PREFIX} `)) return JSON.parse(line.slice(VERDICT_PREFIX.length + 1));
|
|
26
|
+
}
|
|
27
|
+
return void 0;
|
|
28
|
+
}
|
|
29
|
+
var VERDICT_PREFIX;
|
|
30
|
+
var init_verdict_line = __esm({
|
|
31
|
+
"src/internal/verdict-line.ts"() {
|
|
32
|
+
VERDICT_PREFIX = "@@ggui-render-check-verdict@@";
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
|
|
20
36
|
// src/harness/check/runtime-render/render-check-host.ts
|
|
21
37
|
var render_check_host_exports = {};
|
|
22
38
|
__export(render_check_host_exports, {
|
|
@@ -124,18 +140,19 @@ function mapSandboxResultToCheckResult(result, t0, boundMs = DEFAULT_RENDER_CHEC
|
|
|
124
140
|
t0
|
|
125
141
|
);
|
|
126
142
|
}
|
|
127
|
-
|
|
128
|
-
if (stdout.length === 0) {
|
|
129
|
-
return unverified("render check worker exited without producing a verdict.", t0);
|
|
130
|
-
}
|
|
143
|
+
let verdict;
|
|
131
144
|
try {
|
|
132
|
-
|
|
145
|
+
verdict = parseVerdictLine(result.stdout);
|
|
133
146
|
} catch (err) {
|
|
134
147
|
return unverified(
|
|
135
148
|
`render check worker verdict was not valid JSON \u2014 ${err instanceof Error ? err.message : String(err)}`,
|
|
136
149
|
t0
|
|
137
150
|
);
|
|
138
151
|
}
|
|
152
|
+
if (verdict === void 0) {
|
|
153
|
+
return unverified("render check worker exited without producing a verdict.", t0);
|
|
154
|
+
}
|
|
155
|
+
return verdict;
|
|
139
156
|
}
|
|
140
157
|
async function runRenderCheckViaWorker(input, options = {}) {
|
|
141
158
|
const t0 = Date.now();
|
|
@@ -154,7 +171,9 @@ async function runRenderCheckViaWorker(input, options = {}) {
|
|
|
154
171
|
gracePeriodMs: bounds.gracePeriodMs,
|
|
155
172
|
maxStdoutBytes: CHECK_STDOUT_CAP,
|
|
156
173
|
nodeHeapMb: bounds.heapMb,
|
|
157
|
-
|
|
174
|
+
// ggui#1398: the check learns its own deadline, so its report-only
|
|
175
|
+
// pending phase can step aside instead of pushing it past the bound.
|
|
176
|
+
stdin: JSON.stringify({ ...input, deadlineAt: t0 + bounds.timeoutMs }),
|
|
158
177
|
// NODE_ENV steers React's production vs development build — keep
|
|
159
178
|
// parity with the caller; everything else stays on the sandbox's
|
|
160
179
|
// default allowlist.
|
|
@@ -171,6 +190,7 @@ async function runRenderCheckViaWorker(input, options = {}) {
|
|
|
171
190
|
var DEFAULT_RENDER_CHECK_HOST_BOUNDS, CHECK_STDOUT_CAP, WORKER_BASENAME;
|
|
172
191
|
var init_render_check_host = __esm({
|
|
173
192
|
"src/harness/check/runtime-render/render-check-host.ts"() {
|
|
193
|
+
init_verdict_line();
|
|
174
194
|
DEFAULT_RENDER_CHECK_HOST_BOUNDS = {
|
|
175
195
|
timeoutMs: 3e4,
|
|
176
196
|
heapMb: 512,
|
|
@@ -698,6 +718,169 @@ function editableSurface(el) {
|
|
|
698
718
|
function parentLink(node) {
|
|
699
719
|
return node;
|
|
700
720
|
}
|
|
721
|
+
|
|
722
|
+
// src/harness/check/runtime-render/probe-pending.ts
|
|
723
|
+
function createProbePendingSource() {
|
|
724
|
+
const pending = /* @__PURE__ */ new Set();
|
|
725
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
726
|
+
const notify = () => {
|
|
727
|
+
for (const listener of [...listeners]) listener();
|
|
728
|
+
};
|
|
729
|
+
return {
|
|
730
|
+
isPending: (actionName) => pending.has(actionName),
|
|
731
|
+
subscribe: (listener) => {
|
|
732
|
+
listeners.add(listener);
|
|
733
|
+
return () => {
|
|
734
|
+
listeners.delete(listener);
|
|
735
|
+
};
|
|
736
|
+
},
|
|
737
|
+
mark: (actionName) => {
|
|
738
|
+
pending.add(actionName);
|
|
739
|
+
notify();
|
|
740
|
+
},
|
|
741
|
+
clear: () => {
|
|
742
|
+
if (pending.size === 0) return;
|
|
743
|
+
pending.clear();
|
|
744
|
+
notify();
|
|
745
|
+
}
|
|
746
|
+
};
|
|
747
|
+
}
|
|
748
|
+
function lookOf(el) {
|
|
749
|
+
return {
|
|
750
|
+
connected: el.isConnected !== false,
|
|
751
|
+
// React renders `disabled={true}` as `disabled=""`, so presence is the test.
|
|
752
|
+
disabled: el.getAttribute("disabled") !== null || el.getAttribute("aria-disabled") === "true",
|
|
753
|
+
ariaBusy: el.getAttribute("aria-busy") === "true",
|
|
754
|
+
text: el.tagName?.toLowerCase() === "input" ? String(typeof el.value === "string" ? el.value : el.getAttribute("value") ?? "") : el.textContent ?? ""
|
|
755
|
+
};
|
|
756
|
+
}
|
|
757
|
+
async function settlesWithin(ms, work) {
|
|
758
|
+
let timer;
|
|
759
|
+
const expired = new Promise((resolve) => {
|
|
760
|
+
timer = setTimeout(() => resolve(false), Math.max(0, ms));
|
|
761
|
+
});
|
|
762
|
+
try {
|
|
763
|
+
return await Promise.race([work.then(() => true), expired]);
|
|
764
|
+
} finally {
|
|
765
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
function lookChanged(before, after) {
|
|
769
|
+
if (before.connected && !after.connected) return true;
|
|
770
|
+
if (!before.disabled && after.disabled) return true;
|
|
771
|
+
if (!before.ariaBusy && after.ariaBusy) return true;
|
|
772
|
+
return before.text.trim() !== after.text.trim();
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
// src/harness/check/runtime-render/walk.ts
|
|
776
|
+
var LAST_PRESSED_KEPT = 4;
|
|
777
|
+
function isDisabled(el) {
|
|
778
|
+
return el.getAttribute("disabled") !== null || el.getAttribute("aria-disabled") === "true";
|
|
779
|
+
}
|
|
780
|
+
function labelOf(el) {
|
|
781
|
+
const named = el.getAttribute("aria-label") ?? el.getAttribute("placeholder");
|
|
782
|
+
return (named ?? el.textContent ?? "").replace(/\s+/g, " ").trim().slice(0, 60);
|
|
783
|
+
}
|
|
784
|
+
function screenFingerprint(container) {
|
|
785
|
+
const parts = [];
|
|
786
|
+
for (const el of container.querySelectorAll(
|
|
787
|
+
'button, [role="button"], a[href], input, select, textarea, form'
|
|
788
|
+
)) {
|
|
789
|
+
parts.push(
|
|
790
|
+
[el.tagName.toLowerCase(), el.getAttribute("type") ?? "", labelOf(el), isDisabled(el) ? "disabled" : ""].join("|")
|
|
791
|
+
);
|
|
792
|
+
}
|
|
793
|
+
return parts.join("\n");
|
|
794
|
+
}
|
|
795
|
+
function keyed(controls, describe) {
|
|
796
|
+
const seen = /* @__PURE__ */ new Map();
|
|
797
|
+
const out = /* @__PURE__ */ new Map();
|
|
798
|
+
for (const c of controls) {
|
|
799
|
+
const d = describe(c.element);
|
|
800
|
+
const n = seen.get(d) ?? 0;
|
|
801
|
+
seen.set(d, n + 1);
|
|
802
|
+
out.set(`${d}#${n}`, c);
|
|
803
|
+
}
|
|
804
|
+
return out;
|
|
805
|
+
}
|
|
806
|
+
function firstStepToUnpressed(screens, from) {
|
|
807
|
+
const firstStep = /* @__PURE__ */ new Map([[from, void 0]]);
|
|
808
|
+
const queue = [from];
|
|
809
|
+
while (queue.length > 0) {
|
|
810
|
+
const at = queue.shift();
|
|
811
|
+
const screen = screens.get(at);
|
|
812
|
+
if (screen === void 0) continue;
|
|
813
|
+
if (at !== from && screen.order.some((k) => !screen.led.has(k))) return firstStep.get(at);
|
|
814
|
+
for (const [key, to] of screen.led) {
|
|
815
|
+
if (to === at || firstStep.has(to)) continue;
|
|
816
|
+
firstStep.set(to, firstStep.get(at) ?? key);
|
|
817
|
+
queue.push(to);
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
return void 0;
|
|
821
|
+
}
|
|
822
|
+
async function walkToAction(input) {
|
|
823
|
+
const lastPressed = [];
|
|
824
|
+
const screenTexts = [];
|
|
825
|
+
const screens = /* @__PURE__ */ new Map();
|
|
826
|
+
let presses = 0;
|
|
827
|
+
let namedControlSeen = false;
|
|
828
|
+
const outcome = (stoppedBy, extra = {}) => ({
|
|
829
|
+
fired: false,
|
|
830
|
+
presses,
|
|
831
|
+
screens: screens.size,
|
|
832
|
+
stoppedBy,
|
|
833
|
+
namedControlSeen,
|
|
834
|
+
lastPressed: [...lastPressed],
|
|
835
|
+
screenTexts: [...screenTexts],
|
|
836
|
+
...extra
|
|
837
|
+
});
|
|
838
|
+
const mounted = await input.mount();
|
|
839
|
+
if (mounted === void 0) return outcome("no-mount");
|
|
840
|
+
const { container } = mounted;
|
|
841
|
+
const arrive = () => {
|
|
842
|
+
mounted.prime();
|
|
843
|
+
const controls = keyed(input.controls(container), input.describe);
|
|
844
|
+
if ([...controls.values()].some((c) => c.namesAction)) namedControlSeen = true;
|
|
845
|
+
const id = screenFingerprint(container);
|
|
846
|
+
if (!screens.has(id)) {
|
|
847
|
+
screens.set(id, { order: [...controls.keys()], led: /* @__PURE__ */ new Map() });
|
|
848
|
+
screenTexts.push(container.textContent ?? "");
|
|
849
|
+
}
|
|
850
|
+
return { id, controls };
|
|
851
|
+
};
|
|
852
|
+
let here = arrive();
|
|
853
|
+
const first = here.id;
|
|
854
|
+
for (; ; ) {
|
|
855
|
+
const screen = screens.get(here.id);
|
|
856
|
+
const onOpenFirstScreen = here.id === first && screen.order.some((k) => !screen.led.has(k));
|
|
857
|
+
if (!onOpenFirstScreen) {
|
|
858
|
+
if (presses >= input.maxPresses) return outcome("press-cap");
|
|
859
|
+
if (Date.now() >= input.stopAt) return outcome("deadline");
|
|
860
|
+
}
|
|
861
|
+
const key = screen.order.find((k) => !screen.led.has(k)) ?? firstStepToUnpressed(screens, here.id);
|
|
862
|
+
if (key === void 0) return outcome("explored");
|
|
863
|
+
const control = here.controls.get(key);
|
|
864
|
+
if (control === void 0) {
|
|
865
|
+
screen.led.set(key, here.id);
|
|
866
|
+
continue;
|
|
867
|
+
}
|
|
868
|
+
presses += 1;
|
|
869
|
+
lastPressed.push(input.describe(control.element));
|
|
870
|
+
if (lastPressed.length > LAST_PRESSED_KEPT) lastPressed.shift();
|
|
871
|
+
if (await input.press(control)) {
|
|
872
|
+
return outcome("dispatched", { fired: true, element: control.element, container });
|
|
873
|
+
}
|
|
874
|
+
here = arrive();
|
|
875
|
+
if (!screen.led.has(key)) screen.led.set(key, here.id);
|
|
876
|
+
}
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
// src/harness/check/runtime-render/render-check.ts
|
|
880
|
+
var PENDING_PHASE_RESERVE_MS = 5e3;
|
|
881
|
+
var WALK_MAX_PRESSES = 60;
|
|
882
|
+
var WALK_ACTION_CAP_MS = 3e3;
|
|
883
|
+
var WALK_DEADLINE_MARGIN_MS = 2e3;
|
|
701
884
|
async function runRenderCheck(input, options = {}) {
|
|
702
885
|
const g = hostGlobals();
|
|
703
886
|
if ("window" in g && "document" in g) {
|
|
@@ -763,7 +946,9 @@ async function runRenderCheckInProcess(input) {
|
|
|
763
946
|
const wireConfig = createProbeWireConfig(probe);
|
|
764
947
|
uninstallSpy = probe.installPostMessageSpy();
|
|
765
948
|
installProductionActShim();
|
|
766
|
-
const { render, cleanup } = await import('@testing-library/react');
|
|
949
|
+
const { render, cleanup, act } = await import('@testing-library/react');
|
|
950
|
+
const { ActionPendingContext } = await import('@ggui-ai/wire/internal');
|
|
951
|
+
const pendingSource = createProbePendingSource();
|
|
767
952
|
const userEventModule = await import('@testing-library/user-event');
|
|
768
953
|
const userEvent = userEventModule.default ?? userEventModule;
|
|
769
954
|
const originalConsoleError = console.error;
|
|
@@ -800,7 +985,8 @@ async function runRenderCheckInProcess(input) {
|
|
|
800
985
|
};
|
|
801
986
|
const boundaryRef = {
|
|
802
987
|
stack: null,
|
|
803
|
-
error: null
|
|
988
|
+
error: null,
|
|
989
|
+
catches: 0
|
|
804
990
|
};
|
|
805
991
|
class ProbeErrorBoundary extends React.Component {
|
|
806
992
|
state = { caught: false };
|
|
@@ -808,6 +994,7 @@ async function runRenderCheckInProcess(input) {
|
|
|
808
994
|
return { caught: true };
|
|
809
995
|
}
|
|
810
996
|
componentDidCatch(error, errorInfo) {
|
|
997
|
+
boundaryRef.catches += 1;
|
|
811
998
|
if (errorInfo.componentStack && !boundaryRef.stack) {
|
|
812
999
|
boundaryRef.stack = errorInfo.componentStack;
|
|
813
1000
|
}
|
|
@@ -820,19 +1007,19 @@ async function runRenderCheckInProcess(input) {
|
|
|
820
1007
|
return this.props.children;
|
|
821
1008
|
}
|
|
822
1009
|
}
|
|
1010
|
+
const cardTree = () => React.createElement(GguiWireProvider, {
|
|
1011
|
+
config: wireConfig,
|
|
1012
|
+
children: React.createElement(ProbeErrorBoundary, {
|
|
1013
|
+
children: React.createElement(ActionPendingContext.Provider, {
|
|
1014
|
+
value: pendingSource,
|
|
1015
|
+
children: React.createElement(Component, input.mockupProps)
|
|
1016
|
+
})
|
|
1017
|
+
})
|
|
1018
|
+
});
|
|
823
1019
|
let renderResult;
|
|
824
1020
|
const RENDER_TIMEOUT_MS = 5e3;
|
|
825
1021
|
try {
|
|
826
|
-
const renderPromise = Promise.resolve().then(
|
|
827
|
-
() => render(
|
|
828
|
-
React.createElement(GguiWireProvider, {
|
|
829
|
-
config: wireConfig,
|
|
830
|
-
children: React.createElement(ProbeErrorBoundary, {
|
|
831
|
-
children: React.createElement(Component, input.mockupProps)
|
|
832
|
-
})
|
|
833
|
-
})
|
|
834
|
-
)
|
|
835
|
-
);
|
|
1022
|
+
const renderPromise = Promise.resolve().then(() => render(cardTree()));
|
|
836
1023
|
const timeoutPromise = new Promise((_, reject) => {
|
|
837
1024
|
setTimeout(
|
|
838
1025
|
() => reject(
|
|
@@ -884,6 +1071,9 @@ async function runRenderCheckInProcess(input) {
|
|
|
884
1071
|
const user = buildClickUser(userEvent, hostGlobals().document);
|
|
885
1072
|
let actionsChecked = 0;
|
|
886
1073
|
let streamsChecked = 0;
|
|
1074
|
+
let pendingTally;
|
|
1075
|
+
const pendingTargets = [];
|
|
1076
|
+
const walkedTexts = [];
|
|
887
1077
|
try {
|
|
888
1078
|
let inputPriming;
|
|
889
1079
|
try {
|
|
@@ -900,15 +1090,42 @@ async function runRenderCheckInProcess(input) {
|
|
|
900
1090
|
hookName: "useAction",
|
|
901
1091
|
hookArg: name
|
|
902
1092
|
});
|
|
1093
|
+
const walkPriming = {};
|
|
1094
|
+
const mount = async () => {
|
|
1095
|
+
let mounted;
|
|
1096
|
+
try {
|
|
1097
|
+
mounted = render(cardTree());
|
|
1098
|
+
} catch {
|
|
1099
|
+
return void 0;
|
|
1100
|
+
}
|
|
1101
|
+
await flushPromises();
|
|
1102
|
+
const root = mounted.container;
|
|
1103
|
+
return {
|
|
1104
|
+
container: toMinimalElement(root),
|
|
1105
|
+
prime: () => {
|
|
1106
|
+
try {
|
|
1107
|
+
primeInputs(root);
|
|
1108
|
+
} catch (err) {
|
|
1109
|
+
walkPriming.error ??= err instanceof Error ? err.message : String(err);
|
|
1110
|
+
}
|
|
1111
|
+
}
|
|
1112
|
+
};
|
|
1113
|
+
};
|
|
903
1114
|
const issue = await checkActionWiring({
|
|
904
|
-
|
|
1115
|
+
mount,
|
|
905
1116
|
actionName: name,
|
|
906
1117
|
actionLabel: entry.label,
|
|
907
1118
|
wiring,
|
|
908
1119
|
probe,
|
|
909
|
-
user
|
|
1120
|
+
user,
|
|
1121
|
+
pendingTargets,
|
|
1122
|
+
stopAt: walkStopAt(input.deadlineAt),
|
|
1123
|
+
walkedTexts
|
|
910
1124
|
});
|
|
911
|
-
if (issue)
|
|
1125
|
+
if (issue) {
|
|
1126
|
+
const priming = "primed" in inputPriming && walkPriming.error !== void 0 ? { error: `on a screen the action walk reached: ${walkPriming.error}` } : inputPriming;
|
|
1127
|
+
issues.push({ ...issue, diagnostics: { ...issue.diagnostics, inputPriming: priming } });
|
|
1128
|
+
}
|
|
912
1129
|
}
|
|
913
1130
|
}
|
|
914
1131
|
if (input.contract?.actionSpec) {
|
|
@@ -930,6 +1147,7 @@ async function runRenderCheckInProcess(input) {
|
|
|
930
1147
|
if (input.contract?.propsSpec) {
|
|
931
1148
|
for (const issue of checkPropCoverage({
|
|
932
1149
|
container,
|
|
1150
|
+
walkedTexts,
|
|
933
1151
|
propsSpec: input.contract.propsSpec,
|
|
934
1152
|
mockupProps: input.mockupProps
|
|
935
1153
|
})) {
|
|
@@ -1035,10 +1253,21 @@ async function runRenderCheckInProcess(input) {
|
|
|
1035
1253
|
}
|
|
1036
1254
|
}
|
|
1037
1255
|
}
|
|
1256
|
+
const timeLeft = input.deadlineAt === void 0 ? Number.POSITIVE_INFINITY : input.deadlineAt - Date.now();
|
|
1257
|
+
if (input.contract?.actionSpec && timeLeft >= PENDING_PHASE_RESERVE_MS) {
|
|
1258
|
+
pendingTally = await runPendingPhase({
|
|
1259
|
+
targets: pendingTargets,
|
|
1260
|
+
source: pendingSource,
|
|
1261
|
+
act,
|
|
1262
|
+
catches: () => boundaryRef.catches,
|
|
1263
|
+
actions: Object.entries(input.contract.actionSpec).map(([name, entry]) => ({ name, label: entry.label })),
|
|
1264
|
+
...input.deadlineAt !== void 0 ? { deadlineAt: input.deadlineAt } : {}
|
|
1265
|
+
});
|
|
1266
|
+
}
|
|
1038
1267
|
} finally {
|
|
1039
1268
|
cleanup();
|
|
1040
1269
|
}
|
|
1041
|
-
return finalize(issues, t0, actionsChecked, streamsChecked);
|
|
1270
|
+
return finalize(issues, t0, actionsChecked, streamsChecked, pendingTally);
|
|
1042
1271
|
} finally {
|
|
1043
1272
|
try {
|
|
1044
1273
|
uninstallSpy?.();
|
|
@@ -1250,7 +1479,7 @@ async function setupHappyDom() {
|
|
|
1250
1479
|
delete g.IS_REACT_ACT_ENVIRONMENT;
|
|
1251
1480
|
};
|
|
1252
1481
|
}
|
|
1253
|
-
function finalize(issues, t0, actionsChecked, streamsChecked) {
|
|
1482
|
+
function finalize(issues, t0, actionsChecked, streamsChecked, pendingTally) {
|
|
1254
1483
|
const ok = !issues.some((i) => i.outcome === "failed");
|
|
1255
1484
|
return {
|
|
1256
1485
|
ok,
|
|
@@ -1258,12 +1487,86 @@ function finalize(issues, t0, actionsChecked, streamsChecked) {
|
|
|
1258
1487
|
stats: {
|
|
1259
1488
|
actionsChecked,
|
|
1260
1489
|
streamsChecked,
|
|
1261
|
-
renderMs: Date.now() - t0
|
|
1490
|
+
renderMs: Date.now() - t0,
|
|
1491
|
+
...pendingTally !== void 0 ? {
|
|
1492
|
+
pendingAffordance: {
|
|
1493
|
+
dispatched: pendingTally.dispatched,
|
|
1494
|
+
visible: pendingTally.visible,
|
|
1495
|
+
missing: [...pendingTally.missing],
|
|
1496
|
+
gone: [...pendingTally.gone]
|
|
1497
|
+
}
|
|
1498
|
+
} : {}
|
|
1262
1499
|
}
|
|
1263
1500
|
};
|
|
1264
1501
|
}
|
|
1502
|
+
var PENDING_TARGET_CAP_MS = 2e3;
|
|
1503
|
+
var PENDING_PHASE_TAIL_MS = 1e3;
|
|
1504
|
+
async function runPendingPhase(input) {
|
|
1505
|
+
const tally = { dispatched: 0, visible: 0, missing: [], gone: [] };
|
|
1506
|
+
let stopped = false;
|
|
1507
|
+
for (const target of input.targets) {
|
|
1508
|
+
const budget = Math.min(
|
|
1509
|
+
PENDING_TARGET_CAP_MS,
|
|
1510
|
+
input.deadlineAt === void 0 ? Number.POSITIVE_INFINITY : input.deadlineAt - Date.now() - PENDING_PHASE_TAIL_MS
|
|
1511
|
+
);
|
|
1512
|
+
const found = stopped || budget <= 0 ? void 0 : pendingControlOf(target, input.actions);
|
|
1513
|
+
if (found === void 0) {
|
|
1514
|
+
tally.gone.push(target.actionName);
|
|
1515
|
+
if (budget <= 0) stopped = true;
|
|
1516
|
+
continue;
|
|
1517
|
+
}
|
|
1518
|
+
const el = controlToRead(found, target);
|
|
1519
|
+
const before = lookOf(el);
|
|
1520
|
+
const caughtBefore = input.catches();
|
|
1521
|
+
const settled = await settlesWithin(
|
|
1522
|
+
budget,
|
|
1523
|
+
(async () => {
|
|
1524
|
+
await input.act(async () => {
|
|
1525
|
+
input.source.mark(target.actionName);
|
|
1526
|
+
});
|
|
1527
|
+
await flushPromises();
|
|
1528
|
+
})()
|
|
1529
|
+
);
|
|
1530
|
+
const crashed = input.catches() !== caughtBefore;
|
|
1531
|
+
tally.dispatched += 1;
|
|
1532
|
+
if (settled && !crashed && lookChanged(before, lookOf(el))) tally.visible += 1;
|
|
1533
|
+
else tally.missing.push(target.actionName);
|
|
1534
|
+
if (!settled) {
|
|
1535
|
+
stopped = true;
|
|
1536
|
+
continue;
|
|
1537
|
+
}
|
|
1538
|
+
await input.act(async () => {
|
|
1539
|
+
input.source.clear();
|
|
1540
|
+
});
|
|
1541
|
+
await flushPromises();
|
|
1542
|
+
if (crashed) stopped = true;
|
|
1543
|
+
}
|
|
1544
|
+
return tally;
|
|
1545
|
+
}
|
|
1546
|
+
function pendingControlOf(target, actions) {
|
|
1547
|
+
const movedToAnother = !matchesActionIdentity(target.element, target.actionName, target.actionLabel) && actions.some((a) => a.name !== target.actionName && matchesActionIdentity(target.element, a.name, a.label));
|
|
1548
|
+
if (lookOf(target.element).connected && !movedToAnother) return target.element;
|
|
1549
|
+
if (target.wiringKind !== "click") return void 0;
|
|
1550
|
+
return findActionElements(target.container, target.actionName, target.actionLabel, { identityOnly: true })[0];
|
|
1551
|
+
}
|
|
1552
|
+
function controlToRead(el, target) {
|
|
1553
|
+
if (el.tagName.toLowerCase() !== "form") return el;
|
|
1554
|
+
for (const c of el.querySelectorAll('button, input[type="submit"], input[type="button"], [role="button"]')) {
|
|
1555
|
+
if (matchesActionIdentity(c, target.actionName, target.actionLabel)) return c;
|
|
1556
|
+
}
|
|
1557
|
+
for (const submitter of el.querySelectorAll(
|
|
1558
|
+
'button[type="submit"], input[type="submit"], button:not([type])'
|
|
1559
|
+
)) {
|
|
1560
|
+
return submitter;
|
|
1561
|
+
}
|
|
1562
|
+
return el;
|
|
1563
|
+
}
|
|
1564
|
+
function walkStopAt(deadlineAt) {
|
|
1565
|
+
const cap = Date.now() + WALK_ACTION_CAP_MS;
|
|
1566
|
+
return deadlineAt === void 0 ? cap : Math.min(cap, deadlineAt - PENDING_PHASE_RESERVE_MS - WALK_DEADLINE_MARGIN_MS);
|
|
1567
|
+
}
|
|
1265
1568
|
async function checkActionWiring(input) {
|
|
1266
|
-
const {
|
|
1569
|
+
const { actionName, actionLabel, wiring, probe, user } = input;
|
|
1267
1570
|
const baseDiagnostics = {
|
|
1268
1571
|
observedJsxElements: wiring.observedJsxElements,
|
|
1269
1572
|
observedNativeProps: wiring.observedNativeProps,
|
|
@@ -1287,25 +1590,56 @@ async function checkActionWiring(input) {
|
|
|
1287
1590
|
diagnostics: baseDiagnostics
|
|
1288
1591
|
};
|
|
1289
1592
|
}
|
|
1290
|
-
const
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1593
|
+
const kind = wiring.kind;
|
|
1594
|
+
const walk = await walkToAction({
|
|
1595
|
+
mount: input.mount,
|
|
1596
|
+
controls: (container) => actionControls(container, kind, actionName, actionLabel),
|
|
1597
|
+
press: async (control) => {
|
|
1598
|
+
const before = probe.getFireLog().length;
|
|
1599
|
+
try {
|
|
1600
|
+
await dispatchTrigger(control.element, control.how, user);
|
|
1601
|
+
} catch {
|
|
1602
|
+
return false;
|
|
1603
|
+
}
|
|
1604
|
+
await flushPromises();
|
|
1605
|
+
return probe.getFireLog().slice(before).some((e) => e.kind === "action.fired" && e.name === actionName);
|
|
1606
|
+
},
|
|
1607
|
+
describe: describeElement,
|
|
1608
|
+
stopAt: input.stopAt,
|
|
1609
|
+
maxPresses: WALK_MAX_PRESSES
|
|
1298
1610
|
});
|
|
1299
|
-
|
|
1611
|
+
input.walkedTexts.push(...walk.screenTexts);
|
|
1612
|
+
if (walk.fired) {
|
|
1613
|
+
if (input.pendingTargets && walk.element && walk.container && (kind === "click" || kind === "submit")) {
|
|
1614
|
+
input.pendingTargets.push({
|
|
1615
|
+
actionName,
|
|
1616
|
+
actionLabel,
|
|
1617
|
+
wiringKind: kind,
|
|
1618
|
+
element: walk.element,
|
|
1619
|
+
container: walk.container
|
|
1620
|
+
});
|
|
1621
|
+
}
|
|
1622
|
+
return null;
|
|
1623
|
+
}
|
|
1624
|
+
const allFired = probe.getFireLog().filter((e) => e.kind === "action.fired").map((e) => e.name);
|
|
1625
|
+
const otherActionsFired = Array.from(new Set(allFired)).filter((n) => n !== actionName);
|
|
1626
|
+
const stoppedBy = walk.stoppedBy === "dispatched" ? "explored" : walk.stoppedBy;
|
|
1300
1627
|
return {
|
|
1301
1628
|
check: "action-wiring",
|
|
1302
1629
|
outcome: "unverified",
|
|
1303
1630
|
subject: actionName,
|
|
1304
|
-
reason: `Source confirms ${
|
|
1305
|
-
elementHint:
|
|
1631
|
+
reason: `Source confirms ${kind} wiring exists for action '${actionName}', but synthetic ${kind} did not dispatch it. Likely cause: conditional rendering, required input/form fill, or a multi-step interaction the static probe cannot complete.`,
|
|
1632
|
+
...walk.presses === 0 && stoppedBy === "explored" ? { elementHint: `No ${kind}-eligible element found in DOM` } : {},
|
|
1306
1633
|
diagnostics: {
|
|
1307
1634
|
...baseDiagnostics,
|
|
1308
|
-
actionsFiredFromClicks:
|
|
1635
|
+
actionsFiredFromClicks: otherActionsFired,
|
|
1636
|
+
walk: {
|
|
1637
|
+
presses: walk.presses,
|
|
1638
|
+
screens: walk.screens,
|
|
1639
|
+
stoppedBy,
|
|
1640
|
+
namedControlSeen: walk.namedControlSeen,
|
|
1641
|
+
lastPressed: walk.lastPressed
|
|
1642
|
+
}
|
|
1309
1643
|
}
|
|
1310
1644
|
};
|
|
1311
1645
|
}
|
|
@@ -1373,32 +1707,6 @@ async function checkSelectionIdentity(input) {
|
|
|
1373
1707
|
}
|
|
1374
1708
|
return null;
|
|
1375
1709
|
}
|
|
1376
|
-
async function simulateAndCheck(input) {
|
|
1377
|
-
const { container, user, probe, wiringKind, actionName, actionLabel, eventKind } = input;
|
|
1378
|
-
const candidates = findCandidateElements(container, wiringKind, actionName, actionLabel);
|
|
1379
|
-
if (candidates.length === 0) {
|
|
1380
|
-
return { fired: false, attemptedHint: `No ${wiringKind}-eligible element found in DOM` };
|
|
1381
|
-
}
|
|
1382
|
-
for (const el of candidates) {
|
|
1383
|
-
const before = probe.getFireLog().length;
|
|
1384
|
-
try {
|
|
1385
|
-
await dispatchTrigger(el, wiringKind, user);
|
|
1386
|
-
} catch {
|
|
1387
|
-
continue;
|
|
1388
|
-
}
|
|
1389
|
-
await flushPromises();
|
|
1390
|
-
const newEvents = probe.getFireLog().slice(before);
|
|
1391
|
-
const matched = newEvents.some((e) => e.kind === eventKind && e.name === actionName);
|
|
1392
|
-
if (matched) return { fired: true };
|
|
1393
|
-
}
|
|
1394
|
-
const allFired = probe.getFireLog().filter((e) => e.kind === "action.fired").map((e) => e.name);
|
|
1395
|
-
const otherActionsFired = Array.from(new Set(allFired)).filter((n) => n !== actionName);
|
|
1396
|
-
return {
|
|
1397
|
-
fired: false,
|
|
1398
|
-
attemptedHint: candidates.length ? describeElement(candidates[0]) : void 0,
|
|
1399
|
-
otherActionsFired
|
|
1400
|
-
};
|
|
1401
|
-
}
|
|
1402
1710
|
async function dispatchTrigger(el, kind, user) {
|
|
1403
1711
|
switch (kind) {
|
|
1404
1712
|
case "click":
|
|
@@ -1484,40 +1792,40 @@ function closestForm(el) {
|
|
|
1484
1792
|
}
|
|
1485
1793
|
return null;
|
|
1486
1794
|
}
|
|
1487
|
-
function
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
const all = container.querySelectorAll(
|
|
1493
|
-
'form, button[type="submit"], input[type="submit"]'
|
|
1494
|
-
);
|
|
1495
|
-
return Array.from(all);
|
|
1496
|
-
}
|
|
1497
|
-
case "change": {
|
|
1498
|
-
const all = container.querySelectorAll("select, input, textarea");
|
|
1499
|
-
return Array.from(all);
|
|
1500
|
-
}
|
|
1501
|
-
case "keyboard-enter":
|
|
1502
|
-
return findActionElements(container, name, label);
|
|
1795
|
+
function actionControls(container, kind, name, label) {
|
|
1796
|
+
const clickables = findActionElements(container, name, label);
|
|
1797
|
+
const named = (el) => matchesActionIdentity(el, name, label);
|
|
1798
|
+
if (kind === "click" || kind === "keyboard-enter") {
|
|
1799
|
+
return clickables.map((element) => ({ element, namesAction: named(element), how: kind }));
|
|
1503
1800
|
}
|
|
1801
|
+
const eligible = Array.from(
|
|
1802
|
+
container.querySelectorAll(
|
|
1803
|
+
kind === "submit" ? 'form, button[type="submit"], input[type="submit"]' : "select, input, textarea"
|
|
1804
|
+
)
|
|
1805
|
+
);
|
|
1806
|
+
const seen = new Set(eligible);
|
|
1807
|
+
return [
|
|
1808
|
+
...eligible.map((element) => ({ element, namesAction: named(element), how: kind })),
|
|
1809
|
+
...clickables.filter((el) => !seen.has(el)).map((element) => ({ element, namesAction: named(element), how: "click" }))
|
|
1810
|
+
];
|
|
1504
1811
|
}
|
|
1505
1812
|
function checkPropCoverage(input) {
|
|
1506
|
-
const { container, propsSpec, mockupProps } = input;
|
|
1813
|
+
const { container, walkedTexts, propsSpec, mockupProps } = input;
|
|
1507
1814
|
const issues = [];
|
|
1508
|
-
const
|
|
1815
|
+
const texts = [container.textContent ?? "", ...walkedTexts];
|
|
1509
1816
|
for (const [propName, entry] of Object.entries(propsSpec.properties)) {
|
|
1510
1817
|
if (!entry.required) continue;
|
|
1511
1818
|
const value = mockupProps[propName];
|
|
1512
1819
|
if (value === void 0 || value === null) continue;
|
|
1513
1820
|
const marker = pickScalarMarker(value);
|
|
1514
1821
|
if (marker === null) continue;
|
|
1515
|
-
if (!text.includes(marker)) {
|
|
1822
|
+
if (!texts.some((text) => text.includes(marker))) {
|
|
1823
|
+
const where = walkedTexts.length > 0 ? ", on the first screen or any screen the action walk reached" : "";
|
|
1516
1824
|
issues.push({
|
|
1517
1825
|
check: "prop-coverage",
|
|
1518
1826
|
outcome: "unverified",
|
|
1519
1827
|
subject: propName,
|
|
1520
|
-
reason: `Required prop '${propName}' value (${JSON.stringify(marker).slice(0, 60)}) not visible in rendered DOM`
|
|
1828
|
+
reason: `Required prop '${propName}' value (${JSON.stringify(marker).slice(0, 60)}) not visible in rendered DOM${where}`
|
|
1521
1829
|
});
|
|
1522
1830
|
}
|
|
1523
1831
|
}
|
|
@@ -1557,26 +1865,28 @@ async function checkStreamRerender(input) {
|
|
|
1557
1865
|
}
|
|
1558
1866
|
return null;
|
|
1559
1867
|
}
|
|
1560
|
-
function
|
|
1868
|
+
function matchesActionIdentity(el, name, label) {
|
|
1869
|
+
const norm = (s) => s.toLowerCase().replace(/[\s_-]+/g, "");
|
|
1870
|
+
const aria = (el.getAttribute("aria-label") ?? "").toLowerCase();
|
|
1871
|
+
const txt = (el.textContent ?? "").toLowerCase();
|
|
1872
|
+
const dataAction = (el.getAttribute("data-action") ?? "").toLowerCase();
|
|
1873
|
+
return aria.includes(label.toLowerCase()) || txt.includes(label.toLowerCase()) || norm(aria).includes(norm(name)) || norm(txt).includes(norm(name)) || dataAction === name.toLowerCase() || norm(txt).includes(norm(label));
|
|
1874
|
+
}
|
|
1875
|
+
function findActionElements(container, name, label, options = {}) {
|
|
1561
1876
|
const candidates = [];
|
|
1562
1877
|
const seen = /* @__PURE__ */ new Set();
|
|
1563
|
-
const norm = (s) => s.toLowerCase().replace(/[\s_-]+/g, "");
|
|
1564
|
-
const nameKey = norm(name);
|
|
1565
|
-
const labelKey = norm(label);
|
|
1566
1878
|
const allClickable = container.querySelectorAll(
|
|
1567
1879
|
'button, [role="button"], input[type="submit"], input[type="button"], a[href]'
|
|
1568
1880
|
);
|
|
1569
1881
|
for (const el of allClickable) {
|
|
1570
|
-
|
|
1571
|
-
const txt = (el.textContent ?? "").toLowerCase();
|
|
1572
|
-
const dataAction = (el.getAttribute("data-action") ?? "").toLowerCase();
|
|
1573
|
-
if (aria.includes(label.toLowerCase()) || txt.includes(label.toLowerCase()) || norm(aria).includes(nameKey) || norm(txt).includes(nameKey) || dataAction === name.toLowerCase() || norm(txt).includes(labelKey)) {
|
|
1882
|
+
if (matchesActionIdentity(el, name, label)) {
|
|
1574
1883
|
if (!seen.has(el)) {
|
|
1575
1884
|
seen.add(el);
|
|
1576
1885
|
candidates.push(el);
|
|
1577
1886
|
}
|
|
1578
1887
|
}
|
|
1579
1888
|
}
|
|
1889
|
+
if (options.identityOnly) return candidates;
|
|
1580
1890
|
for (const el of allClickable) {
|
|
1581
1891
|
if (!seen.has(el)) {
|
|
1582
1892
|
seen.add(el);
|
|
@@ -1847,7 +2157,9 @@ function createRuntimeRenderCheck(config = {}) {
|
|
|
1847
2157
|
elapsedMs: Date.now() - t0,
|
|
1848
2158
|
...queuedField(queuedMs),
|
|
1849
2159
|
renderMs: result.stats.renderMs,
|
|
1850
|
-
...load
|
|
2160
|
+
...load,
|
|
2161
|
+
// ggui#1398: report-only — carried to the probe's meta for the bench, never an issue.
|
|
2162
|
+
...result.stats.pendingAffordance !== void 0 ? { pendingAffordance: result.stats.pendingAffordance } : {}
|
|
1851
2163
|
};
|
|
1852
2164
|
}
|
|
1853
2165
|
};
|
|
@@ -1901,6 +2213,18 @@ function toEvalIssue(issue) {
|
|
|
1901
2213
|
"primed" in diag.inputPriming ? `input priming: ${diag.inputPriming.primed} control(s) filled before the click` : `input priming failed: ${diag.inputPriming.error}`
|
|
1902
2214
|
);
|
|
1903
2215
|
}
|
|
2216
|
+
if (diag?.walk) {
|
|
2217
|
+
const w = diag.walk;
|
|
2218
|
+
const stopped = {
|
|
2219
|
+
explored: "every reachable control pressed",
|
|
2220
|
+
"press-cap": "stopped at the press cap",
|
|
2221
|
+
deadline: "stopped at the time budget",
|
|
2222
|
+
"no-mount": "the card could not be mounted for the walk"
|
|
2223
|
+
}[w.stoppedBy];
|
|
2224
|
+
const named = w.namedControlSeen ? "a control naming the action rendered, but pressing it did not dispatch the action" : "no screen rendered a control naming the action";
|
|
2225
|
+
const last = w.lastPressed.length ? `; last pressed: ${w.lastPressed.join(", ")}` : "";
|
|
2226
|
+
diagParts.push(`walk: ${w.presses} press(es) across ${w.screens} screen(s), ${stopped}; ${named}${last}`);
|
|
2227
|
+
}
|
|
1904
2228
|
const diagSuffix = diagParts.length ? ` [observed: ${diagParts.join("; ")}]` : "";
|
|
1905
2229
|
switch (issue.check) {
|
|
1906
2230
|
case "render-no-throw":
|