feat: Add hardened error handling and retries (#2451)

- Closes #1830
 - Closes #2099
This commit is contained in:
Dermot Duffy
2026-06-30 17:45:12 -07:00
committed by dermotduffy
parent 4bc787e2b7
commit 47bcce93d3
182 changed files with 7877 additions and 4043 deletions
+5 -3
View File
@@ -136,10 +136,12 @@ export type NotificationControl = z.infer<typeof notificationControlSchema>;
const notificationSchema = z.object({
heading: notificationDetailSchema.optional(),
controls: notificationControlSchema.array().optional(),
details: notificationDetailSchema.array().optional(),
text: z.string().optional(),
body: notificationDetailSchema.optional(),
metadata: notificationDetailSchema.array().optional(),
context: z.string().array().optional(),
link: linkSchema.optional(),
in_progress: z.boolean().optional(),
controls: notificationControlSchema.array().optional(),
});
export type Notification = z.infer<typeof notificationSchema>;
+23 -9
View File
@@ -86,15 +86,29 @@ const castConfigDefault = {
method: 'standard' as const,
};
const castSchema = z.object({
method: z.enum(['standard', 'dashboard']).default(castConfigDefault.method).optional(),
dashboard: z
.object({
dashboard_path: z.string().optional(),
view_path: z.string().optional(),
})
.optional(),
});
const castSchema = z
.object({
method: z
.enum(['standard', 'dashboard'])
.default(castConfigDefault.method)
.optional(),
dashboard: z
.object({
dashboard_path: z.string().optional(),
view_path: z.string().optional(),
})
.optional(),
})
.refine(
(cast) =>
cast.method !== 'dashboard' ||
(cast.dashboard?.dashboard_path && cast.dashboard?.view_path),
{
message:
'dashboard_path and view_path are required when cast method is "dashboard"',
path: ['dashboard'],
},
);
// *************************************************************************
// Camera Configuration
+1
View File
@@ -3,6 +3,7 @@ import { STATUS_BAR_PRIORITY_DEFAULT, STATUS_BAR_PRIORITY_MAX } from './const';
export const statusBarItemBaseSchema = z.object({
enabled: z.boolean().default(true).optional(),
permanent: z.boolean().default(false).optional(),
priority: z
.number()
.min(0)
+19 -16
View File
@@ -16,21 +16,34 @@ const STATUS_BAR_POSITIONS = ['top', 'bottom'] as const;
const statusBarItemDefault = {
priority: STATUS_BAR_PRIORITY_DEFAULT,
enabled: true,
permanent: false,
};
// Issues share a single status-bar item config so per-issue granularity
// doesn't leak into the schema/editor surface. Defaults to permanent so the
// status bar stays visible while any issue is active, even in popup mode.
const statusBarIssuesItemDefault = {
...statusBarItemDefault,
permanent: true,
};
// Extend the base schema so the `permanent: true` default survives a user
// override that touches only one sibling field (e.g. `issues.enabled: true`).
// With the item-level default alone, zod would reparse with the base schema's
// field-level `permanent: false` and silently flip the behavior.
const statusBarIssuesItemSchema = statusBarItemBaseSchema.extend({
permanent: z.boolean().default(true).optional(),
});
export const statusBarConfigDefault = {
height: 40,
items: {
engine: statusBarItemDefault,
issues: statusBarIssuesItemDefault,
resolution: statusBarItemDefault,
severity: statusBarItemDefault,
technology: statusBarItemDefault,
title: statusBarItemDefault,
// Problems.
problem_config_upgrade: statusBarItemDefault,
problem_legacy_resource: statusBarItemDefault,
problem_stream_not_loading: statusBarItemDefault,
},
position: 'bottom' as const,
style: 'popup' as const,
@@ -50,6 +63,7 @@ export const statusBarConfigSchema = z
items: z
.object({
engine: statusBarItemBaseSchema.default(statusBarConfigDefault.items.engine),
issues: statusBarIssuesItemSchema.default(statusBarConfigDefault.items.issues),
resolution: statusBarItemBaseSchema.default(
statusBarConfigDefault.items.resolution,
),
@@ -58,17 +72,6 @@ export const statusBarConfigSchema = z
statusBarConfigDefault.items.technology,
),
title: statusBarItemBaseSchema.default(statusBarConfigDefault.items.title),
// Problems.
problem_config_upgrade: statusBarItemBaseSchema.default(
statusBarConfigDefault.items.problem_config_upgrade,
),
problem_legacy_resource: statusBarItemBaseSchema.default(
statusBarConfigDefault.items.problem_legacy_resource,
),
problem_stream_not_loading: statusBarItemBaseSchema.default(
statusBarConfigDefault.items.problem_stream_not_loading,
),
})
.default(statusBarConfigDefault.items),
})
+14
View File
@@ -74,6 +74,10 @@ export const viewConfigDefault = {
untrigger_force_seconds: 0,
},
keyboard_shortcuts: keyboardShortcutsDefault,
issues: {
interaction_mode: 'all' as const,
retry_seconds: 'auto' as const,
},
};
const interactionModeSchema = z
@@ -146,6 +150,16 @@ export const viewConfigSchema = z
keyboard_shortcuts: keyboardShortcutsSchema.default(
viewConfigDefault.keyboard_shortcuts,
),
// See: https://github.com/dermotduffy/advanced-camera-card/issues/2099
issues: z
.object({
interaction_mode: interactionModeSchema,
retry_seconds: z
.union([z.literal('auto'), z.number().min(0)])
.default(viewConfigDefault.issues.retry_seconds),
})
.default(viewConfigDefault.issues),
})
.extend(actionsSchema.shape)
.default(viewConfigDefault);