@geode/opengeodeweb-front 10.35.0 → 10.36.0-rc.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.build/opengeodeweb_front_schemas.js +200 -21
- package/.build/server/utils/typed_handler.js +63 -0
- package/.build/shared/utils/validate_schema.js +2 -1
- package/app/components/CrsSelector.vue +5 -5
- package/app/components/ExtensionSelector.vue +6 -12
- package/app/components/FileSelector.vue +2 -4
- package/app/components/MissingFilesSelector.vue +1 -1
- package/app/components/ObjectSelector.vue +1 -1
- package/app/components/ThresholdFilter.vue +1 -1
- package/app/components/Viewer/Generic/Model/BlocksOptions.vue +1 -1
- package/app/components/Viewer/Generic/Model/CornersOptions.vue +1 -1
- package/app/components/Viewer/Generic/Model/LinesOptions.vue +1 -1
- package/app/components/Viewer/Generic/Model/SurfacesOptions.vue +1 -1
- package/app/components/Viewer/Options/ColoringTypeSelector.vue +1 -1
- package/app/components/Viewer/Options/TextureItem.vue +5 -7
- package/app/composables/global_attribute_style.ts +1 -1
- package/app/composables/project_manager.ts +1 -1
- package/app/stores/app.ts +19 -17
- package/app/stores/back.ts +27 -21
- package/app/stores/cloud.ts +1 -1
- package/app/stores/infra.ts +1 -1
- package/app/stores/viewer.ts +4 -6
- package/app/utils/extension.ts +4 -4
- package/app/utils/import_workflow.ts +1 -1
- package/internal/stores/hybrid_viewer/base64.ts +6 -0
- package/internal/stores/hybrid_viewer/scene.ts +5 -1
- package/internal/utils/api_fetch.ts +9 -8
- package/opengeodeweb_front_schemas.json +252 -19
- package/opengeodeweb_front_typed_schemas.d.ts +225 -0
- package/opengeodeweb_front_typed_schemas.js +4 -0
- package/package.json +7 -5
- package/server/api/cloud/extensions/run.post.ts +9 -21
- package/server/api/cloud/extensions/schemas/run.json +42 -3
- package/server/api/local/app/kill.post.ts +4 -2
- package/server/api/local/app/project_folder_path.post.ts +6 -22
- package/server/api/local/app/run_back.post.ts +8 -23
- package/server/api/local/app/run_viewer.post.ts +8 -23
- package/server/api/local/app/schemas/kill.json +7 -1
- package/server/api/local/app/schemas/project_folder_path.json +19 -2
- package/server/api/local/app/schemas/run_back.json +31 -4
- package/server/api/local/app/schemas/run_viewer.json +31 -4
- package/server/api/local/extensions/kill.post.ts +8 -23
- package/server/api/local/extensions/run.post.ts +8 -22
- package/server/api/local/extensions/schemas/kill.json +20 -4
- package/server/api/local/extensions/schemas/run.json +42 -3
- package/server/api/local/extensions/schemas/upload.json +11 -1
- package/server/api/local/extensions/upload.put.ts +37 -29
- package/server/api/microservice/app/get_is_app_ready.get.ts +7 -13
- package/server/api/microservice/app/schemas/get_is_app_ready.json +14 -1
- package/server/api/microservice/app/schemas/set_app_base_url.json +17 -2
- package/server/api/microservice/app/schemas/set_back_base_url.json +17 -2
- package/server/api/microservice/app/schemas/set_is_app_ready.json +17 -2
- package/server/api/microservice/app/schemas/set_viewer_base_url.json +17 -2
- package/server/api/microservice/app/set_app_base_url.post.ts +8 -17
- package/server/api/microservice/app/set_back_base_url.post.ts +8 -16
- package/server/api/microservice/app/set_is_app_ready.post.ts +8 -17
- package/server/api/microservice/app/set_viewer_base_url.post.ts +8 -16
- package/server/api/microservice/extensions/download.post.ts +8 -21
- package/server/api/microservice/extensions/schemas/download.json +21 -4
- package/server/utils/typed_handler.ts +114 -0
- package/shared/utils/types.ts +18 -3
- package/shared/utils/validate_schema.ts +1 -1
package/app/stores/app.ts
CHANGED
|
@@ -8,10 +8,15 @@ import { upload_file } from "@ogw_internal/utils/upload_file.js";
|
|
|
8
8
|
import { useAppExtensions } from "./app_helpers/extension.js";
|
|
9
9
|
import { useInfraStore } from "@ogw_front/stores/infra";
|
|
10
10
|
|
|
11
|
-
import type {
|
|
11
|
+
import type {
|
|
12
|
+
JsonRpcSchema,
|
|
13
|
+
ParamsOf,
|
|
14
|
+
RequestHandlers,
|
|
15
|
+
ResponseOf,
|
|
16
|
+
} from "@ogw_shared/utils/types.js";
|
|
12
17
|
import type { StateTree } from "pinia";
|
|
13
18
|
|
|
14
|
-
import opengeodeweb_front_schemas from "@geode/opengeodeweb-front/
|
|
19
|
+
import opengeodeweb_front_schemas from "@geode/opengeodeweb-front/opengeodeweb_front_typed_schemas.js";
|
|
15
20
|
|
|
16
21
|
// The `share` defineStore option (used by every store in this codebase) is implemented by a runtime pinia plugin outside this package's type surface; this augmentation only teaches the type checker about the option shape already used at each defineStore call site.
|
|
17
22
|
declare module "pinia" {
|
|
@@ -170,29 +175,30 @@ export const useAppStore = defineStore("app", () => {
|
|
|
170
175
|
return result;
|
|
171
176
|
}
|
|
172
177
|
|
|
173
|
-
// `
|
|
178
|
+
// The response type comes from the schema's generated `response` type, asserted at the single `as ResponseOf<Schema>` boundary below: the server checks its responses against that schema outside production.
|
|
174
179
|
// oxlint-disable-next-line unicorn/consistent-function-scoping
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
+
async function request<Schema extends JsonRpcSchema>(
|
|
181
|
+
{ schema, params }: { schema: Schema; params?: ParamsOf<Schema> },
|
|
182
|
+
callbacks: RequestHandlers<ResponseOf<Schema>> = {},
|
|
183
|
+
): Promise<ResponseOf<Schema>> {
|
|
184
|
+
const rpc_schema: JsonRpcSchema = schema;
|
|
180
185
|
const result = await api_fetch(
|
|
181
186
|
{ $id: "app", base_url: base_url.value, start_request, stop_request },
|
|
182
187
|
// The app store is only ever used with HTTP ("front") schemas, which always carry `methods`; the wider JsonRpcSchema param above is kept as-is to match this action's public signature (e.g. relayed from get_version-style callers that only know about the shared, looser schema shape).
|
|
183
188
|
// oxlint-disable-next-line no-unsafe-type-assertion -- narrowing optional `methods` to required is safe here; see comment above.
|
|
184
|
-
{ schema:
|
|
189
|
+
{ schema: rpc_schema as JsonRpcSchema & { methods: string[] }, params: params ?? {} },
|
|
185
190
|
{
|
|
186
191
|
...callbacks,
|
|
187
192
|
response_function: async (response: unknown) => {
|
|
188
193
|
if (callbacks.response_function) {
|
|
189
|
-
|
|
194
|
+
// oxlint-disable-next-line no-unsafe-type-assertion -- this is the trusted API boundary; see comment above.
|
|
195
|
+
await callbacks.response_function(response as ResponseOf<Schema>);
|
|
190
196
|
}
|
|
191
197
|
},
|
|
192
198
|
},
|
|
193
199
|
);
|
|
194
200
|
// oxlint-disable-next-line no-unsafe-type-assertion -- this is the trusted API boundary; see comment above.
|
|
195
|
-
return result as
|
|
201
|
+
return result as ResponseOf<Schema>;
|
|
196
202
|
}
|
|
197
203
|
|
|
198
204
|
const projectFolderPath = ref("");
|
|
@@ -204,12 +210,8 @@ export const useAppStore = defineStore("app", () => {
|
|
|
204
210
|
const result = await request(
|
|
205
211
|
{ schema, params },
|
|
206
212
|
{
|
|
207
|
-
response_function: (response
|
|
208
|
-
|
|
209
|
-
const { projectFolderPath: newProjectFolderPath } = response as {
|
|
210
|
-
projectFolderPath: string;
|
|
211
|
-
};
|
|
212
|
-
projectFolderPath.value = newProjectFolderPath;
|
|
213
|
+
response_function: (response) => {
|
|
214
|
+
projectFolderPath.value = response.projectFolderPath;
|
|
213
215
|
},
|
|
214
216
|
},
|
|
215
217
|
);
|
package/app/stores/back.ts
CHANGED
|
@@ -1,15 +1,20 @@
|
|
|
1
1
|
import { getRestApiPort, getRestApiProtocol, isCloudMode } from "@ogw_front/utils/stores";
|
|
2
2
|
import { Status } from "@ogw_front/utils/status";
|
|
3
3
|
import { api_fetch } from "@ogw_internal/utils/api_fetch";
|
|
4
|
-
import back_schemas from "@geode/opengeodeweb-back/
|
|
4
|
+
import back_schemas from "@geode/opengeodeweb-back/opengeodeweb_back_typed_schemas.js";
|
|
5
5
|
import { upload_file } from "@ogw_internal/utils/upload_file.js";
|
|
6
6
|
import { useAppStore } from "@ogw_front/stores/app";
|
|
7
7
|
import { useFeedbackStore } from "@ogw_front/stores/feedback";
|
|
8
8
|
import { useInfraStore } from "@ogw_front/stores/infra";
|
|
9
9
|
|
|
10
|
-
import type {
|
|
10
|
+
import type {
|
|
11
|
+
JsonRpcSchema,
|
|
12
|
+
ParamsOf,
|
|
13
|
+
RequestHandlers,
|
|
14
|
+
ResponseOf,
|
|
15
|
+
} from "@ogw_shared/utils/types.js";
|
|
11
16
|
|
|
12
|
-
import opengeodeweb_front_schemas from "@geode/opengeodeweb-front/
|
|
17
|
+
import opengeodeweb_front_schemas from "@geode/opengeodeweb-front/opengeodeweb_front_typed_schemas.js";
|
|
13
18
|
|
|
14
19
|
const MILLISECONDS_IN_SECOND = 1000;
|
|
15
20
|
const DEFAULT_PING_INTERVAL_SECONDS = 10;
|
|
@@ -81,7 +86,7 @@ export const useBackStore = defineStore("back", {
|
|
|
81
86
|
stop_request() {
|
|
82
87
|
this.request_counter -= 1;
|
|
83
88
|
},
|
|
84
|
-
async launch(args:
|
|
89
|
+
async launch(args: { projectFolderPath: string }) {
|
|
85
90
|
const appStore = useAppStore();
|
|
86
91
|
const { COMMAND_BACK, NUXT_ROOT_PATH } = useRuntimeConfig().public;
|
|
87
92
|
const schema = opengeodeweb_front_schemas.api.local.app.run_back;
|
|
@@ -90,15 +95,8 @@ export const useBackStore = defineStore("back", {
|
|
|
90
95
|
const result = await appStore.request(
|
|
91
96
|
{ schema, params },
|
|
92
97
|
{
|
|
93
|
-
response_function: (response
|
|
94
|
-
|
|
95
|
-
typeof response === "object" &&
|
|
96
|
-
response !== null &&
|
|
97
|
-
"port" in response &&
|
|
98
|
-
(typeof response.port === "string" || typeof response.port === "number")
|
|
99
|
-
) {
|
|
100
|
-
this.default_local_port = String(response.port);
|
|
101
|
-
}
|
|
98
|
+
response_function: (response) => {
|
|
99
|
+
this.default_local_port = String(response.port);
|
|
102
100
|
},
|
|
103
101
|
},
|
|
104
102
|
);
|
|
@@ -108,27 +106,35 @@ export const useBackStore = defineStore("back", {
|
|
|
108
106
|
this.set_ping();
|
|
109
107
|
await Promise.resolve();
|
|
110
108
|
},
|
|
111
|
-
async request(
|
|
112
|
-
{ schema, params
|
|
113
|
-
callbacks: RequestHandlers = {},
|
|
114
|
-
) {
|
|
109
|
+
async request<Schema extends JsonRpcSchema>(
|
|
110
|
+
{ schema, params }: { schema: Schema; params?: ParamsOf<Schema> },
|
|
111
|
+
callbacks: RequestHandlers<ResponseOf<Schema>> = {},
|
|
112
|
+
): Promise<ResponseOf<Schema>> {
|
|
113
|
+
const rpc_schema: JsonRpcSchema = schema;
|
|
115
114
|
const result = await api_fetch(
|
|
116
115
|
this,
|
|
117
116
|
// The back store is only ever used with HTTP ("front"/"back") schemas,
|
|
118
117
|
// Which always carry `methods`; the wider JsonRpcSchema param above is
|
|
119
118
|
// Kept as-is to match this action's public signature.
|
|
120
|
-
|
|
121
|
-
|
|
119
|
+
{
|
|
120
|
+
// oxlint-disable-next-line typescript/no-unsafe-type-assertion
|
|
121
|
+
schema: rpc_schema as JsonRpcSchema & { methods: string[] },
|
|
122
|
+
params: params ?? {},
|
|
123
|
+
headers: {},
|
|
124
|
+
},
|
|
122
125
|
{
|
|
123
126
|
...callbacks,
|
|
124
127
|
response_function: async (response: unknown) => {
|
|
125
128
|
if (callbacks.response_function) {
|
|
126
|
-
|
|
129
|
+
// The back validates its responses against the schema `response` it was generated from.
|
|
130
|
+
// oxlint-disable-next-line typescript/no-unsafe-type-assertion
|
|
131
|
+
await callbacks.response_function(response as ResponseOf<Schema>);
|
|
127
132
|
}
|
|
128
133
|
},
|
|
129
134
|
},
|
|
130
135
|
);
|
|
131
|
-
|
|
136
|
+
// oxlint-disable-next-line typescript/no-unsafe-type-assertion
|
|
137
|
+
return result as ResponseOf<Schema>;
|
|
132
138
|
},
|
|
133
139
|
async upload(file: File, callbacks: RequestHandlers = {}) {
|
|
134
140
|
const schema = back_schemas.opengeodeweb_back.upload_file;
|
package/app/stores/cloud.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { clearCloudUrlParam, getCloudUrlParam } from "@ogw_front/utils/cloud";
|
|
|
2
2
|
import type { RunCloudResponse } from "@geode/cloud-api/types";
|
|
3
3
|
import { Status } from "@ogw_front/utils/status";
|
|
4
4
|
import { api_fetch } from "@ogw_internal/utils/api_fetch";
|
|
5
|
-
import back_schemas from "@geode/opengeodeweb-back/
|
|
5
|
+
import back_schemas from "@geode/opengeodeweb-back/opengeodeweb_back_typed_schemas.js";
|
|
6
6
|
import cloud_api_schemas from "@geode/cloud-api/cloud_api_schemas.json";
|
|
7
7
|
import { setAppBaseUrl } from "@ogw_shared/scripts";
|
|
8
8
|
import { useAPIStore } from "@ogw_front/stores/api";
|
package/app/stores/infra.ts
CHANGED
|
@@ -107,6 +107,6 @@ export interface Microservice {
|
|
|
107
107
|
$id: string;
|
|
108
108
|
status?: string;
|
|
109
109
|
is_busy?: boolean;
|
|
110
|
-
launch?: (params:
|
|
110
|
+
launch?: (params: { projectFolderPath: string }) => Promise<unknown>;
|
|
111
111
|
connect: () => Promise<void>;
|
|
112
112
|
}
|
package/app/stores/viewer.ts
CHANGED
|
@@ -4,7 +4,7 @@ import "@kitware/vtk.js/Rendering/OpenGL/Profiles/Geometry";
|
|
|
4
4
|
import type { Vector3 } from "@kitware/vtk.js/types";
|
|
5
5
|
import { connectImageStream } from "@kitware/vtk.js/Rendering/Misc/RemoteView";
|
|
6
6
|
import { initWebSocketClient } from "@ogw_internal/utils/ws_client";
|
|
7
|
-
import opengeodeweb_front_schemas from "@geode/opengeodeweb-front/
|
|
7
|
+
import opengeodeweb_front_schemas from "@geode/opengeodeweb-front/opengeodeweb_front_typed_schemas.js";
|
|
8
8
|
import opengeodeweb_viewer_schemas from "@geode/opengeodeweb-viewer/opengeodeweb_viewer_schemas.json" with { type: "json" };
|
|
9
9
|
|
|
10
10
|
// Local imports
|
|
@@ -150,7 +150,7 @@ export const useViewerStore = defineStore(
|
|
|
150
150
|
}
|
|
151
151
|
});
|
|
152
152
|
}
|
|
153
|
-
async function launch(args: Readonly<{ projectFolderPath
|
|
153
|
+
async function launch(args: Readonly<{ projectFolderPath: string }>): Promise<unknown> {
|
|
154
154
|
const appStore = useAppStore();
|
|
155
155
|
const { COMMAND_VIEWER, NUXT_ROOT_PATH } = useRuntimeConfig().public;
|
|
156
156
|
const schema = opengeodeweb_front_schemas.api.local.app.run_viewer;
|
|
@@ -165,10 +165,8 @@ export const useViewerStore = defineStore(
|
|
|
165
165
|
params,
|
|
166
166
|
},
|
|
167
167
|
{
|
|
168
|
-
response_function: (response
|
|
169
|
-
|
|
170
|
-
const { port: viewerPort } = response as { port: string };
|
|
171
|
-
default_local_port.value = viewerPort;
|
|
168
|
+
response_function: (response) => {
|
|
169
|
+
default_local_port.value = String(response.port);
|
|
172
170
|
},
|
|
173
171
|
},
|
|
174
172
|
);
|
package/app/utils/extension.ts
CHANGED
|
@@ -7,14 +7,14 @@ import type { RegisterableStore, useAppStore } from "@ogw_front/stores/app";
|
|
|
7
7
|
import type { Microservice } from "@ogw_front/stores/infra";
|
|
8
8
|
import { consola } from "consola";
|
|
9
9
|
import { isCloudMode } from "@ogw_front/utils/stores";
|
|
10
|
-
import opengeodeweb_front_schemas from "@geode/opengeodeweb-front/
|
|
10
|
+
import opengeodeweb_front_schemas from "@geode/opengeodeweb-front/opengeodeweb_front_typed_schemas.js";
|
|
11
11
|
|
|
12
12
|
interface ExtensionDescriptor {
|
|
13
13
|
id: string;
|
|
14
14
|
name: string;
|
|
15
15
|
version: string;
|
|
16
16
|
frontendContent: string;
|
|
17
|
-
port:
|
|
17
|
+
port: number;
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
interface DownloadExtensionParams {
|
|
@@ -48,7 +48,7 @@ async function runExtensions(): Promise<{ extensionsArray: ExtensionDescriptor[]
|
|
|
48
48
|
projectFolderPath,
|
|
49
49
|
projectName,
|
|
50
50
|
};
|
|
51
|
-
const result = await appStore.request
|
|
51
|
+
const result = await appStore.request({
|
|
52
52
|
schema,
|
|
53
53
|
params,
|
|
54
54
|
});
|
|
@@ -92,7 +92,7 @@ async function registerRunningExtensions(): Promise<RegisteredExtension[]> {
|
|
|
92
92
|
type: "application/javascript",
|
|
93
93
|
});
|
|
94
94
|
const blobUrl = URL.createObjectURL(blob);
|
|
95
|
-
const extensionModule = await appStore.loadExtension(blobUrl, port);
|
|
95
|
+
const extensionModule = await appStore.loadExtension(blobUrl, String(port));
|
|
96
96
|
consola.info("[ExtensionManager] Extension loaded:", id);
|
|
97
97
|
const storeFactory = extensionModule.metadata.store;
|
|
98
98
|
const store = storeFactory();
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// oxlint-disable promise/prefer-await-to-then
|
|
2
2
|
// Third party imports
|
|
3
|
-
import back_schemas from "@geode/opengeodeweb-back/
|
|
3
|
+
import back_schemas from "@geode/opengeodeweb-back/opengeodeweb_back_typed_schemas.js";
|
|
4
4
|
import { consola } from "consola";
|
|
5
5
|
|
|
6
6
|
// Local imports
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
// Decodes a base64 string (the back's binary_light_viewable) into raw bytes for vtk.js readers.
|
|
2
|
+
// Uint8Array.fromBase64 would do this natively but is unavailable in Node 22, which runs the tests.
|
|
3
|
+
export function base64ToArrayBuffer(b64: string): ArrayBuffer {
|
|
4
|
+
// Atob returns a "binary string": one char per byte, so each code point is the byte value.
|
|
5
|
+
return Uint8Array.from(atob(b64), (char) => char.codePointAt(0) ?? 0).buffer;
|
|
6
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { HybridDb, vtkActor as VtkActorInstance } from "./vtk_types";
|
|
2
2
|
import { requireRenderWindow, useHybridViewerCore } from "./core";
|
|
3
3
|
import { ACTOR_COLOR } from "./constants";
|
|
4
|
+
import { base64ToArrayBuffer } from "./base64";
|
|
4
5
|
import { useDataStore } from "@ogw_front/stores/data";
|
|
5
6
|
import { useViewerStore } from "@ogw_front/stores/viewer";
|
|
6
7
|
import viewer_schemas from "@geode/opengeodeweb-viewer/opengeodeweb_viewer_schemas.json";
|
|
@@ -25,8 +26,11 @@ const useHybridViewerScene = createSharedComposable(() => {
|
|
|
25
26
|
return;
|
|
26
27
|
}
|
|
27
28
|
const value = await dataStore.item(id);
|
|
29
|
+
if (value.binary_light_viewable === undefined) {
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
28
32
|
const reader = vtkXMLPolyDataReader();
|
|
29
|
-
reader.parseAsArrayBuffer(
|
|
33
|
+
reader.parseAsArrayBuffer(base64ToArrayBuffer(value.binary_light_viewable));
|
|
30
34
|
const actor = vtkActor();
|
|
31
35
|
const mapper = vtkMapper();
|
|
32
36
|
// oxlint-disable-next-line no-unsafe-type-assertion -- vtk.js's algorithm interface types getOutputData as `any`; this reader always produces polydata.
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { JsonRpcSchema, RequestHandlersWithValidation } from "@ogw_shared/utils/types.js";
|
|
2
2
|
import { endRequestLog, startRequestLog } from "@ogw_front/utils/log";
|
|
3
|
+
import type { ErrorResponse } from "@geode/opengeodeweb-back/opengeodeweb_back_typed_schemas.js";
|
|
3
4
|
import { consola } from "consola";
|
|
4
5
|
import { fetchSchema } from "@ogw_shared/utils/fetch_schema";
|
|
5
6
|
import { useFeedbackStore } from "@ogw_front/stores/feedback";
|
|
@@ -15,7 +16,7 @@ interface Microservice {
|
|
|
15
16
|
interface ApiFetchParams {
|
|
16
17
|
// This function always forwards this schema to fetchSchema, which requires the HTTP-flavored `methods` array (as opposed to the websocket `rpc` field).
|
|
17
18
|
schema: JsonRpcSchema & { methods: string[] };
|
|
18
|
-
params?:
|
|
19
|
+
params?: object;
|
|
19
20
|
headers?: Record<string, string>;
|
|
20
21
|
}
|
|
21
22
|
|
|
@@ -26,11 +27,9 @@ interface FetchErrorLike {
|
|
|
26
27
|
stack?: string;
|
|
27
28
|
}
|
|
28
29
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
description?: string;
|
|
33
|
-
}
|
|
30
|
+
type FetchErrorResponseLike = { status?: number } & Partial<
|
|
31
|
+
Pick<ErrorResponse, "name" | "description">
|
|
32
|
+
>;
|
|
34
33
|
|
|
35
34
|
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
36
35
|
return typeof value === "object" && value !== null;
|
|
@@ -51,10 +50,12 @@ function toFetchErrorResponseLike(response: unknown): FetchErrorResponseLike {
|
|
|
51
50
|
if (!isRecord(response)) {
|
|
52
51
|
return {};
|
|
53
52
|
}
|
|
53
|
+
// Ofetch puts the parsed body (an ErrorResponse for Flask microservices) in `_data`
|
|
54
|
+
const body = isRecord(response._data) ? response._data : response;
|
|
54
55
|
return {
|
|
55
56
|
status: typeof response.status === "number" ? response.status : undefined,
|
|
56
|
-
name: typeof
|
|
57
|
-
description: typeof
|
|
57
|
+
name: typeof body.name === "string" ? body.name : undefined,
|
|
58
|
+
description: typeof body.description === "string" ? body.description : undefined,
|
|
58
59
|
};
|
|
59
60
|
}
|
|
60
61
|
|