diff --git a/package-lock.json b/package-lock.json index 779d3af..2a83984 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "ros2_medkit_web_ui", - "version": "0.6.0", + "version": "0.7.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "ros2_medkit_web_ui", - "version": "0.6.0", + "version": "0.7.0", "license": "Apache-2.0", "dependencies": { "@codemirror/lang-python": "^6.2.1", @@ -18,7 +18,7 @@ "@radix-ui/react-slot": "^1.2.3", "@radix-ui/react-switch": "^1.2.6", "@radix-ui/react-tooltip": "^1.2.8", - "@selfpatch/ros2-medkit-client-ts": "^0.6.0", + "@selfpatch/ros2-medkit-client-ts": "^0.7.0", "@tailwindcss/vite": "^4.1.14", "@uiw/react-codemirror": "^4.25.11", "class-variance-authority": "^0.7.1", @@ -3425,9 +3425,9 @@ ] }, "node_modules/@selfpatch/ros2-medkit-client-ts": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/@selfpatch/ros2-medkit-client-ts/-/ros2-medkit-client-ts-0.6.0.tgz", - "integrity": "sha512-eeF3aFm4SJz+8zYBIukSZgG+9nQja2iJqIxfuKIjhBS6y04scpVSdCMMvYJJJBjoo+1KbgxWd017WwNgj7LHew==", + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@selfpatch/ros2-medkit-client-ts/-/ros2-medkit-client-ts-0.7.0.tgz", + "integrity": "sha512-tf7rxO6Haw/sgMQZEDCQn/7QcoEGHJmhK8xVZ2PMW4iux8ZEHIfJcFsnlje6nYPvtOghxzKnMzr1D0V2Wj70hQ==", "license": "Apache-2.0", "engines": { "node": ">=18" diff --git a/package.json b/package.json index 4bede9e..d60a6cc 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "ros2_medkit_web_ui", "private": true, - "version": "0.6.0", + "version": "0.7.0", "type": "module", "description": "Simple web UI for browsing SOVD entity trees via discovery endpoints", "repository": { @@ -42,7 +42,7 @@ "@radix-ui/react-slot": "^1.2.3", "@radix-ui/react-switch": "^1.2.6", "@radix-ui/react-tooltip": "^1.2.8", - "@selfpatch/ros2-medkit-client-ts": "^0.6.0", + "@selfpatch/ros2-medkit-client-ts": "^0.7.0", "@tailwindcss/vite": "^4.1.14", "@uiw/react-codemirror": "^4.25.11", "class-variance-authority": "^0.7.1", diff --git a/src/lib/api-dispatch.ts b/src/lib/api-dispatch.ts index f38183e..29c1fb7 100644 --- a/src/lib/api-dispatch.ts +++ b/src/lib/api-dispatch.ts @@ -21,7 +21,7 @@ * typed path based on the entity type string. */ -import type { MedkitClient } from '@selfpatch/ros2-medkit-client-ts'; +import type { MedkitClient, paths } from '@selfpatch/ros2-medkit-client-ts'; import type { SovdResourceEntityType, LifecycleAction, ScriptEntityType, StartScriptExecutionRequest } from './types'; import type { LogsQueryParams, LogsConfiguration } from './log-types'; @@ -730,13 +730,19 @@ export function getEntityScript( } } +/** The upload body both script paths declare: a required `file` part plus optional `metadata`. */ +type ScriptUploadBody = NonNullable< + paths['/apps/{app_id}/scripts']['post']['requestBody'] +>['content']['multipart/form-data']; + /** * Multipart upload. * - * The spec declares the body as `{type: object, additionalProperties: true}`, so - * the generated type is `{ [key: string]: unknown }` and FormData (a DOM interface) - * is not assignable to it. bodySerializer returns the FormData unchanged so fetch - * sets Content-Type with the multipart boundary itself - the gateway rejects the + * FormData is a DOM interface, so it is never assignable to the generated body + * object however the spec describes it - hence the cast, which names the real + * body type so a change to the declared parts is a type error here rather than + * a runtime 400. bodySerializer returns the FormData unchanged so fetch sets + * Content-Type with the multipart boundary itself - the gateway rejects the * request without it. */ export function uploadEntityScript( @@ -745,7 +751,7 @@ export function uploadEntityScript( entityId: string, form: FormData ) { - const body = form as unknown as Record; + const body = form as unknown as ScriptUploadBody; const bodySerializer = (value: unknown) => value as FormData; switch (entityType) { case 'apps': @@ -784,9 +790,8 @@ export function deleteEntityScript( /** * Start an execution. * - * The spec declares this request body as a bare `type: object`, so the generated - * type is `Record` and any real body fails the type check. The cast - * keeps the runtime payload correct; removing it requires a spec fix in the gateway. + * StartScriptExecutionRequest mirrors the generated ScriptExecutionRequest, so the + * body passes straight through and a divergence between the two shows up here. */ export function startScriptExecution( client: MedkitClient, @@ -795,7 +800,7 @@ export function startScriptExecution( scriptId: string, request: StartScriptExecutionRequest ) { - const body = request as unknown as Record; + const body = request; switch (entityType) { case 'apps': return client.POST('/apps/{app_id}/scripts/{script_id}/executions', {