Improve error location reporting (w/fixed zod).
This commit is contained in:
+1
-1
@@ -21,7 +21,7 @@
|
|||||||
"home-assistant-js-websocket": "^5.11.1",
|
"home-assistant-js-websocket": "^5.11.1",
|
||||||
"lit": "^2.0.2",
|
"lit": "^2.0.2",
|
||||||
"screenfull": "^5.1.0",
|
"screenfull": "^5.1.0",
|
||||||
"zod": "^3.10.0-beta.1"
|
"zod": "^3.10.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.15.8",
|
"@babel/core": "^7.15.8",
|
||||||
|
|||||||
+42
-5
@@ -18,6 +18,7 @@ import {
|
|||||||
handleAction,
|
handleAction,
|
||||||
} from 'custom-card-helpers';
|
} from 'custom-card-helpers';
|
||||||
import screenfull from 'screenfull';
|
import screenfull from 'screenfull';
|
||||||
|
import { z } from 'zod';
|
||||||
|
|
||||||
import { entitySchema, frigateCardConfigSchema } from './types';
|
import { entitySchema, frigateCardConfigSchema } from './types';
|
||||||
import type {
|
import type {
|
||||||
@@ -271,6 +272,42 @@ export class FrigateCard extends LitElement {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected _getParseErrorPaths<T>(error: z.ZodError<T>): string[] {
|
||||||
|
/* Zod errors involving unions are complex, as Zod may not be able to tell
|
||||||
|
* where the 'real' error is vs simply a union option not matching. This
|
||||||
|
* function finds all ZodError "issues" that don't have an error with 'type'
|
||||||
|
* in that object ('type' is the union discriminator for picture elements,
|
||||||
|
* the major union in the schema). An array of human-readable error
|
||||||
|
* locations is returned, or an empty list if none is available. None being
|
||||||
|
* available suggests the configuration has an error, but we can't tell
|
||||||
|
* exactly why (or rather Zod simply says it doesn't match any of the
|
||||||
|
* available unions). This usually suggests the user specified an incorrect
|
||||||
|
* type name entirely. */
|
||||||
|
let contenders: string[] = [];
|
||||||
|
if (error && error.issues) {
|
||||||
|
for (let i = 0; i < error.issues.length; i++) {
|
||||||
|
const issue = error.issues[i];
|
||||||
|
if (issue.code == 'invalid_union') {
|
||||||
|
const unionErrors = (issue as z.ZodInvalidUnionIssue).unionErrors;
|
||||||
|
for (let j = 0; j < unionErrors.length; j++) {
|
||||||
|
const nestedErrors = this._getParseErrorPaths(unionErrors[j]);
|
||||||
|
if (nestedErrors.length) {
|
||||||
|
contenders = contenders.concat(nestedErrors);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (issue.code == 'invalid_type') {
|
||||||
|
if (issue.path[issue.path.length - 1] == 'type') {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
contenders.push(this._getParseErrorPathString(issue.path));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return contenders;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert an array of strings and indices into a more human readable string,
|
||||||
|
// e.g. [a, 1, b, 2] => 'a[1] -> b[2]'
|
||||||
protected _getParseErrorPathString(path: (string | number)[]): string {
|
protected _getParseErrorPathString(path: (string | number)[]): string {
|
||||||
let out = '';
|
let out = '';
|
||||||
for (let i = 0; i < path.length; i++) {
|
for (let i = 0; i < path.length; i++) {
|
||||||
@@ -294,12 +331,12 @@ export class FrigateCard extends LitElement {
|
|||||||
|
|
||||||
const parseResult = frigateCardConfigSchema.safeParse(inputConfig);
|
const parseResult = frigateCardConfigSchema.safeParse(inputConfig);
|
||||||
if (!parseResult.success) {
|
if (!parseResult.success) {
|
||||||
let hint = '';
|
const hint = this._getParseErrorPaths(parseResult.error);
|
||||||
if (parseResult.error && parseResult.error.issues) {
|
|
||||||
hint = this._getParseErrorPathString(parseResult.error.issues[0].path);
|
|
||||||
}
|
|
||||||
throw new Error(
|
throw new Error(
|
||||||
localize('error.invalid_configuration') + (hint ? `: ${hint}` : ''),
|
`${localize('error.invalid_configuration')}: ` +
|
||||||
|
(hint.length
|
||||||
|
? JSON.stringify(hint, null, ' ')
|
||||||
|
: localize('error.invalid_configuration_no_hint')),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
const config = parseResult.data;
|
const config = parseResult.data;
|
||||||
|
|||||||
@@ -97,6 +97,7 @@
|
|||||||
"could_not_resolve": "Could not resolve media URL",
|
"could_not_resolve": "Could not resolve media URL",
|
||||||
"no_live_camera": "The camera_entity parameter must be set and valid for this live provider",
|
"no_live_camera": "The camera_entity parameter must be set and valid for this live provider",
|
||||||
"invalid_configuration": "Invalid configuration",
|
"invalid_configuration": "Invalid configuration",
|
||||||
|
"invalid_configuration_no_hint": "No location hint available (bad or missing type?)",
|
||||||
"missing_webrtc": "WebRTC component not found",
|
"missing_webrtc": "WebRTC component not found",
|
||||||
"no_frigate_camera_name": "Cannot autodetect Frigate camera name, you need to either set camera_entity and / or frigate_camera_name",
|
"no_frigate_camera_name": "Cannot autodetect Frigate camera name, you need to either set camera_entity and / or frigate_camera_name",
|
||||||
"could_not_render_elements": "Could not render picture elements",
|
"could_not_render_elements": "Could not render picture elements",
|
||||||
|
|||||||
+9
-1
@@ -195,7 +195,15 @@ const conditionalSchema = z.object({
|
|||||||
// https://www.home-assistant.io/lovelace/picture-elements/#custom-elements
|
// https://www.home-assistant.io/lovelace/picture-elements/#custom-elements
|
||||||
const customSchema = z.object({
|
const customSchema = z.object({
|
||||||
// Insist that Frigate card custom elements are handled by other schemas.
|
// Insist that Frigate card custom elements are handled by other schemas.
|
||||||
type: z.string().regex(/^custom:(?!frigate-card).+/),
|
type: z.string().superRefine((val, ctx) => {
|
||||||
|
if (!val.match(/^custom:(?!frigate-card).+/)) {
|
||||||
|
ctx.addIssue({
|
||||||
|
code: z.ZodIssueCode.invalid_type,
|
||||||
|
expected: "string",
|
||||||
|
received: "string",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
}).passthrough();
|
}).passthrough();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user