41 lines
3.1 KiB
TypeScript
41 lines
3.1 KiB
TypeScript
import { z } from "zod";
|
|
import type { FrontendActionManifest } from "./types.js";
|
|
|
|
type Definition = { manifest: FrontendActionManifest; inputSchema: z.ZodTypeAny; outputSchema: z.ZodTypeAny };
|
|
const zoomInput = z.object({ x: z.number().finite(), y: z.number().finite(), source_crs: z.enum(["EPSG:3857", "EPSG:4326"]).optional(), zoom: z.number().finite().optional(), duration_ms: z.number().finite().nonnegative().optional() }).strict();
|
|
const id = z.string().trim().min(1).max(256);
|
|
const isoTime = z.string().datetime({ offset: true });
|
|
const timeRange = {
|
|
start_time: isoTime.optional(),
|
|
end_time: isoTime.optional(),
|
|
};
|
|
const requiredTimeRange = {
|
|
start_time: isoTime,
|
|
end_time: isoTime,
|
|
};
|
|
const locateInput = z.object({
|
|
ids: z.array(id).min(1).max(100),
|
|
feature_type: z.enum(["junction", "pipe", "valve", "reservoir", "pump", "tank", "conduit", "orifice", "outfall"]),
|
|
}).strict();
|
|
const historyInput = z.object({
|
|
feature_infos: z.array(z.tuple([id, id])).min(1).max(100),
|
|
data_type: z.enum(["realtime", "scheme", "none"]),
|
|
...requiredTimeRange,
|
|
}).strict().refine((value) => Date.parse(value.start_time) < Date.parse(value.end_time), "start_time must precede end_time");
|
|
const scadaInput = z.object({
|
|
device_id: id.optional(),
|
|
device_ids: z.array(id).min(1).max(100).optional(),
|
|
...timeRange,
|
|
}).strict().refine((value) => Boolean(value.device_id) !== Boolean(value.device_ids), "provide exactly one of device_id or device_ids")
|
|
.refine((value) => !value.start_time || !value.end_time || Date.parse(value.start_time) <= Date.parse(value.end_time), "start_time must not be after end_time");
|
|
const definitions: Definition[] = [
|
|
{ manifest: { id: "zoom_to_map", version: "frontend-action@1", risk: "view", timeoutMs: 10_000, supportedSurfaces: ["map_overlay"] }, inputSchema: zoomInput, outputSchema: z.object({}).passthrough() },
|
|
{ manifest: { id: "locate_features", version: "frontend-action@1", risk: "view", timeoutMs: 15_000, supportedSurfaces: ["map_overlay"] }, inputSchema: locateInput, outputSchema: z.object({ locatedIds: z.array(id), missingIds: z.array(id), featureType: id, bounds: z.tuple([z.number(), z.number(), z.number(), z.number()]) }).strict() },
|
|
{ manifest: { id: "view_history", version: "frontend-action@1", risk: "view", timeoutMs: 10_000, supportedSurfaces: ["side_panel", "canvas"] }, inputSchema: historyInput, outputSchema: z.object({ component: z.literal("HistoryPanel"), rendered: z.literal(true), itemCount: z.number().int().nonnegative() }).strict() },
|
|
{ manifest: { id: "view_scada", version: "frontend-action@1", risk: "view", timeoutMs: 10_000, supportedSurfaces: ["side_panel", "canvas"] }, inputSchema: scadaInput, outputSchema: z.object({ component: z.literal("ScadaPanel"), rendered: z.literal(true), itemCount: z.number().int().nonnegative() }).strict() },
|
|
];
|
|
|
|
export const frontendActionRegistry = definitions.map((item) => item.manifest);
|
|
export const getFrontendActionDefinition = (name: string) => definitions.find((item) => item.manifest.id === name);
|
|
export const frontendActionRegistryResponse = { schema_version: "frontend-action-registry@1" as const, actions: frontendActionRegistry };
|