Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -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": {
Expand Down Expand Up @@ -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",
Expand Down
25 changes: 15 additions & 10 deletions src/lib/api-dispatch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down Expand Up @@ -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(
Expand All @@ -745,7 +751,7 @@ export function uploadEntityScript(
entityId: string,
form: FormData
) {
const body = form as unknown as Record<string, unknown>;
const body = form as unknown as ScriptUploadBody;
const bodySerializer = (value: unknown) => value as FormData;
switch (entityType) {
case 'apps':
Expand Down Expand Up @@ -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<string, never>` 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,
Expand All @@ -795,7 +800,7 @@ export function startScriptExecution(
scriptId: string,
request: StartScriptExecutionRequest
) {
const body = request as unknown as Record<string, never>;
const body = request;
switch (entityType) {
case 'apps':
return client.POST('/apps/{app_id}/scripts/{script_id}/executions', {
Expand Down
Loading