@celestia-island/hikari 0.55.166 → 0.55.167
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/package.json
CHANGED
|
@@ -80,6 +80,17 @@ export const HkErrorReportingOverlay = defineComponent({
|
|
|
80
80
|
if (!err) return null;
|
|
81
81
|
const options = getErrorReportingOptions();
|
|
82
82
|
|
|
83
|
+
// Host-owned card: everything inside the takeover is the host's —
|
|
84
|
+
// content, actions, dismissal. The host keeps the capture channels
|
|
85
|
+
// and the crash-surviving mount; only the card is swapped.
|
|
86
|
+
if (options.landing) {
|
|
87
|
+
return (
|
|
88
|
+
<div class="hk-error-reporting-overlay" role="alertdialog" aria-live="assertive" aria-modal="true">
|
|
89
|
+
{options.landing(err)}
|
|
90
|
+
</div>
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
|
|
83
94
|
const description = options.describe
|
|
84
95
|
? options.describe(err)
|
|
85
96
|
: err.message || t("hikari::errors.unexpectedDesc", "An unhandled error occurred");
|
|
@@ -207,4 +207,75 @@ describe("createErrorReporting", () => {
|
|
|
207
207
|
clearGlobalError();
|
|
208
208
|
expect(overlayHost()).toBeNull();
|
|
209
209
|
});
|
|
210
|
+
|
|
211
|
+
it("renders the host landing card instead of the built-in one", async () => {
|
|
212
|
+
vi.spyOn(console, "error").mockImplementation(() => {});
|
|
213
|
+
installPlugin({
|
|
214
|
+
landing: (err) =>
|
|
215
|
+
h("div", { class: "host-card", "data-code": err.name }, [
|
|
216
|
+
h("p", { class: "host-card__msg" }, err.message),
|
|
217
|
+
h(
|
|
218
|
+
"button",
|
|
219
|
+
{ class: "host-card__dismiss", onClick: () => clearGlobalError() },
|
|
220
|
+
"Dismiss",
|
|
221
|
+
),
|
|
222
|
+
]),
|
|
223
|
+
});
|
|
224
|
+
reportGlobalError(new TypeError("host boom"));
|
|
225
|
+
await nextTick();
|
|
226
|
+
|
|
227
|
+
const host = overlayHost();
|
|
228
|
+
expect(host).not.toBeNull();
|
|
229
|
+
// The built-in card is fully replaced by the host's.
|
|
230
|
+
expect(host!.querySelector(".hk-error-landing")).toBeNull();
|
|
231
|
+
expect(host!.querySelector(".host-card")).not.toBeNull();
|
|
232
|
+
expect(host!.querySelector(".host-card")!.getAttribute("data-code")).toBe("TypeError");
|
|
233
|
+
expect(host!.querySelector(".host-card__msg")!.textContent).toBe("host boom");
|
|
234
|
+
// The host owns the actions: the built-in Home/Retry pair is gone.
|
|
235
|
+
expect(host!.textContent).not.toContain("Back to home");
|
|
236
|
+
expect(host!.textContent).not.toContain("Retry");
|
|
237
|
+
// The takeover shell stays (fixed, alert-dialog semantics).
|
|
238
|
+
expect(host!.querySelector(".hk-error-reporting-overlay")!.getAttribute("role")).toBe(
|
|
239
|
+
"alertdialog",
|
|
240
|
+
);
|
|
241
|
+
// The host card rides INSIDE the takeover shell (same fixed z-band), not
|
|
242
|
+
// beside it: a sibling placement would float outside --hk-z-error. The
|
|
243
|
+
// quadrant of these assertions that pins "same shell" is the ancestor
|
|
244
|
+
// relationship — descendants of the shell, not just of the data node.
|
|
245
|
+
expect(host!.querySelector(".hk-error-reporting-overlay .host-card")).not.toBeNull();
|
|
246
|
+
// The host's own dismissal tears the takeover down.
|
|
247
|
+
(host!.querySelector(".host-card__dismiss") as HTMLButtonElement).click();
|
|
248
|
+
await nextTick();
|
|
249
|
+
expect(overlayHost()).toBeNull();
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
it("lets landing step over title/describe and the built-in actions", async () => {
|
|
253
|
+
vi.spyOn(console, "error").mockImplementation(() => {});
|
|
254
|
+
installPlugin({
|
|
255
|
+
title: "ignored headline",
|
|
256
|
+
describe: () => "ignored description",
|
|
257
|
+
homeHref: "/should-not-be-used",
|
|
258
|
+
onHome: vi.fn(),
|
|
259
|
+
onRetry: vi.fn(),
|
|
260
|
+
landing: (err) =>
|
|
261
|
+
h("div", { class: "host-card", "data-msg": err.message }, [
|
|
262
|
+
h("p", { class: "host-card__msg" }, err.message),
|
|
263
|
+
]),
|
|
264
|
+
});
|
|
265
|
+
reportGlobalError(new TypeError("own content"));
|
|
266
|
+
await nextTick();
|
|
267
|
+
|
|
268
|
+
const host = overlayHost()!;
|
|
269
|
+
// The host's card won outright; the built-in card (and its translated
|
|
270
|
+
// headline / description) never mounted.
|
|
271
|
+
expect(host.querySelector(".hk-error-landing")).toBeNull();
|
|
272
|
+
expect(host.querySelector(".host-card__msg")!.textContent).toBe("own content");
|
|
273
|
+
expect(host.textContent).not.toContain("ignored headline");
|
|
274
|
+
expect(host.textContent).not.toContain("ignored description");
|
|
275
|
+
expect(host.textContent).not.toContain("Back to home");
|
|
276
|
+
expect(host.textContent).not.toContain("Retry");
|
|
277
|
+
// The host card still receives the raw accepted record, not a
|
|
278
|
+
// title/describe-shaped one — the overrides never leak in.
|
|
279
|
+
expect(host.querySelector(".host-card")!.getAttribute("data-msg")).toBe("own content");
|
|
280
|
+
});
|
|
210
281
|
});
|
|
@@ -32,6 +32,11 @@ export { HkErrorReportingOverlay } from "./HkErrorReportingOverlay";
|
|
|
32
32
|
* with Home / Retry actions. Because the overlay lives on its own tiny app
|
|
33
33
|
* instance, it still shows when the host tree itself is what crashed.
|
|
34
34
|
*
|
|
35
|
+
* Hosts whose error landing carries app-specific semantics pass
|
|
36
|
+
* `options.landing(err)`: the takeover then renders the host's card inside
|
|
37
|
+
* the same crash-surviving shell — capture channels and the takeover
|
|
38
|
+
* guarantee stay, the built-in card and its actions step aside.
|
|
39
|
+
*
|
|
35
40
|
* Any pre-existing `app.config.errorHandler` is chained, not discarded.
|
|
36
41
|
* All capture channels are optional; `shouldReport` filters, `onError` is
|
|
37
42
|
* the telemetry hook.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ref, type Ref } from "vue";
|
|
1
|
+
import { ref, type Ref, type VNodeChild } from "vue";
|
|
2
2
|
|
|
3
3
|
import { reportHkRuntime, type HkRuntimeHandle } from "../runtime/registry";
|
|
4
4
|
|
|
@@ -17,11 +17,23 @@ export interface HkReportedError {
|
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
export interface HkErrorReportingOptions {
|
|
20
|
-
/** Headline override; defaults to `hikari::errors.defaultTitle`.
|
|
20
|
+
/** Headline override; defaults to `hikari::errors.defaultTitle`.
|
|
21
|
+
* Ignored when `landing` is set (the host owns the whole card). */
|
|
21
22
|
title?: string;
|
|
22
23
|
/** Description builder; defaults to the error's message, falling back to
|
|
23
|
-
* `hikari::errors.unexpectedDesc` when the error carries none.
|
|
24
|
+
* `hikari::errors.unexpectedDesc` when the error carries none.
|
|
25
|
+
* Ignored when `landing` is set. */
|
|
24
26
|
describe?: (err: HkReportedError) => string;
|
|
27
|
+
/** Host-provided takeover card — the unification hatch for hosts whose
|
|
28
|
+
* error landing carries app-specific semantics (localized per-code
|
|
29
|
+
* headlines, raw wire payloads, a Dismiss action). When set, the overlay
|
|
30
|
+
* renders `landing(err)` INSTEAD of the built-in HkErrorLanding, inside
|
|
31
|
+
* the same crash-surviving host (dedicated body app, --hk-z-error): the
|
|
32
|
+
* host keeps every capture channel and the takeover guarantee and owns
|
|
33
|
+
* the card's content and actions outright — `title`, `describe`,
|
|
34
|
+
* `homeHref`, `onHome` and `onRetry` are all ignored. Resolve app-shaped
|
|
35
|
+
* payloads from `err.error` (e.g. an Error subclass carrying them). */
|
|
36
|
+
landing?: (err: HkReportedError) => VNodeChild;
|
|
25
37
|
/** Target of the "back to home" action. `false` hides the button.
|
|
26
38
|
* Ignored when `onHome` is set. Default `/`. */
|
|
27
39
|
homeHref?: string | false;
|