diff --git a/.gitignore b/.gitignore index 4e7f17f..3381f79 100644 --- a/.gitignore +++ b/.gitignore @@ -78,3 +78,4 @@ docker-compose.override.yml *.pid packs.examples/ +codex/ diff --git a/.semgrepignore b/.semgrepignore index bafa56d..a025079 100644 --- a/.semgrepignore +++ b/.semgrepignore @@ -2,6 +2,5 @@ target/ web/dist/ web/node_modules/ web/src/api/ -packs/ packs.dev/ packs.external/ diff --git a/crates/api/src/routes/packs.rs b/crates/api/src/routes/packs.rs index c5f74cc..d06e5d4 100644 --- a/crates/api/src/routes/packs.rs +++ b/crates/api/src/routes/packs.rs @@ -29,8 +29,8 @@ use crate::{ pack::{ BuildPackEnvsRequest, BuildPackEnvsResponse, CreatePackRequest, DownloadPacksRequest, DownloadPacksResponse, GetPackDependenciesRequest, GetPackDependenciesResponse, - InstallPackRequest, PackInstallResponse, PackResponse, PackSummary, - PackDescriptionPatch, PackWorkflowSyncResponse, PackWorkflowValidationResponse, + InstallPackRequest, PackDescriptionPatch, PackInstallResponse, PackResponse, + PackSummary, PackWorkflowSyncResponse, PackWorkflowValidationResponse, RegisterPackRequest, RegisterPacksRequest, RegisterPacksResponse, UpdatePackRequest, WorkflowSyncResult, }, diff --git a/crates/api/src/routes/triggers.rs b/crates/api/src/routes/triggers.rs index 70a1dd3..0b67309 100644 --- a/crates/api/src/routes/triggers.rs +++ b/crates/api/src/routes/triggers.rs @@ -26,8 +26,8 @@ use crate::{ common::{PaginatedResponse, PaginationParams}, trigger::{ CreateSensorRequest, CreateTriggerRequest, SensorJsonPatch, SensorResponse, - SensorSummary, TriggerJsonPatch, TriggerResponse, TriggerStringPatch, - TriggerSummary, UpdateSensorRequest, UpdateTriggerRequest, + SensorSummary, TriggerJsonPatch, TriggerResponse, TriggerStringPatch, TriggerSummary, + UpdateSensorRequest, UpdateTriggerRequest, }, ApiResponse, SuccessResponse, }, diff --git a/web/eslint.config.js b/web/eslint.config.js index 5e6b472..5b599ba 100644 --- a/web/eslint.config.js +++ b/web/eslint.config.js @@ -6,7 +6,7 @@ import tseslint from 'typescript-eslint' import { defineConfig, globalIgnores } from 'eslint/config' export default defineConfig([ - globalIgnores(['dist']), + globalIgnores(['dist', 'src/api']), { files: ['**/*.{ts,tsx}'], extends: [ diff --git a/web/src/components/forms/RuntimeForm.tsx b/web/src/components/forms/RuntimeForm.tsx index aa8147c..305dea7 100644 --- a/web/src/components/forms/RuntimeForm.tsx +++ b/web/src/components/forms/RuntimeForm.tsx @@ -1,4 +1,4 @@ -import { useEffect, useMemo, useState } from "react"; +import { useMemo, useState } from "react"; import { useNavigate } from "react-router-dom"; import { usePacks } from "@/hooks/usePacks"; import { useCreateRuntime, useUpdateRuntime } from "@/hooks/useRuntimes"; @@ -15,7 +15,15 @@ interface RuntimeFormProps { onCancel?: () => void; } -type JsonObject = Record; +type JsonValue = + | string + | number + | boolean + | null + | { [key: string]: JsonValue } + | JsonValue[]; +type JsonObject = { [key: string]: JsonValue }; +type NonNullJsonValue = Exclude; function prettyJson(value: unknown): string { return JSON.stringify(value ?? {}, null, 2); @@ -40,7 +48,11 @@ function validateObjectJson(label: string, raw: string): JsonObject { } } -function validateJsonValue(label: string, raw: string, required = true): any { +function validateJsonValue( + label: string, + raw: string, + required = true, +): NonNullJsonValue | null { if (!raw.trim()) { if (required) { throw new Error(`${label} is required`); @@ -49,7 +61,7 @@ function validateJsonValue(label: string, raw: string, required = true): any { } try { - return JSON.parse(raw) as any; + return JSON.parse(raw) as NonNullJsonValue; } catch { throw new Error(`${label} must be valid JSON`); } @@ -66,31 +78,23 @@ export default function RuntimeForm({ const createRuntime = useCreateRuntime(); const updateRuntime = useUpdateRuntime(); - const [ref, setRef] = useState(""); - const [packRef, setPackRef] = useState(""); - const [name, setName] = useState(""); - const [description, setDescription] = useState(""); - const [distributions, setDistributions] = useState("{}"); - const [installation, setInstallation] = useState(""); - const [executionConfig, setExecutionConfig] = useState("{}"); + const [ref, setRef] = useState(() => initialData?.ref ?? ""); + const [packRef, setPackRef] = useState(() => initialData?.pack_ref ?? ""); + const [name, setName] = useState(() => initialData?.name ?? ""); + const [description, setDescription] = useState( + () => initialData?.description ?? "", + ); + const [distributions, setDistributions] = useState(() => + prettyJson(initialData?.distributions ?? {}), + ); + const [installation, setInstallation] = useState(() => + initialData?.installation == null ? "" : prettyJson(initialData.installation), + ); + const [executionConfig, setExecutionConfig] = useState(() => + prettyJson(initialData?.execution_config ?? {}), + ); const [errors, setErrors] = useState>({}); - useEffect(() => { - if (!initialData) { - return; - } - - setRef(initialData.ref); - setPackRef(initialData.pack_ref ?? ""); - setName(initialData.name); - setDescription(initialData.description ?? ""); - setDistributions(prettyJson(initialData.distributions)); - setInstallation( - initialData.installation == null ? "" : prettyJson(initialData.installation), - ); - setExecutionConfig(prettyJson(initialData.execution_config)); - }, [initialData]); - const canEditRef = !isEditing; const isSubmitting = createRuntime.isPending || updateRuntime.isPending; const selectedPackExists = @@ -113,7 +117,7 @@ export default function RuntimeForm({ let parsedDistributions: JsonObject | undefined; let parsedExecutionConfig: JsonObject | undefined; - let parsedInstallation: any = null; + let parsedInstallation: NonNullJsonValue | null = null; try { parsedDistributions = validateObjectJson("Distributions", distributions); @@ -146,6 +150,11 @@ export default function RuntimeForm({ try { if (isEditing && initialData) { + const installationPatch = + installation.trim().length > 0 && parsedInstallation !== null + ? { op: NullableJsonPatch.op.SET, value: parsedInstallation } + : null; + await updateRuntime.mutateAsync({ ref: initialData.ref, data: { @@ -154,10 +163,7 @@ export default function RuntimeForm({ : null, name: name.trim(), distributions: parsedDistributions, - installation: - installation.trim().length > 0 - ? { op: NullableJsonPatch.op.SET, value: parsedInstallation } - : null, + installation: installationPatch, execution_config: parsedExecutionConfig, }, }); diff --git a/web/src/pages/runtimes/RuntimesPage.tsx b/web/src/pages/runtimes/RuntimesPage.tsx index b11a7f7..abde53a 100644 --- a/web/src/pages/runtimes/RuntimesPage.tsx +++ b/web/src/pages/runtimes/RuntimesPage.tsx @@ -1,4 +1,4 @@ -import { useEffect, useMemo, useState } from "react"; +import { useMemo, useState } from "react"; import { Link, useNavigate, useParams } from "react-router-dom"; import { Code2, Pencil, Plus, Search, Trash2, X } from "lucide-react"; import type { RuntimeSummary } from "@/api"; @@ -144,7 +144,7 @@ export default function RuntimesPage() { {ref === "new" ? ( ) : ref ? ( - + ) : (
@@ -169,10 +169,6 @@ function RuntimeDetail({ runtimeRef }: { runtimeRef: string }) { const deleteRuntime = useDeleteRuntime(); const [isEditing, setIsEditing] = useState(false); - useEffect(() => { - setIsEditing(false); - }, [runtimeRef]); - if (isLoading) { return (