@gradio/client 0.20.0 → 1.1.0

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.
Files changed (48) hide show
  1. package/CHANGELOG.md +201 -0
  2. package/README.md +21 -36
  3. package/dist/client.d.ts +6 -7
  4. package/dist/client.d.ts.map +1 -1
  5. package/dist/constants.d.ts +3 -0
  6. package/dist/constants.d.ts.map +1 -1
  7. package/dist/helpers/data.d.ts +15 -1
  8. package/dist/helpers/data.d.ts.map +1 -1
  9. package/dist/helpers/init_helpers.d.ts.map +1 -1
  10. package/dist/helpers/spaces.d.ts.map +1 -1
  11. package/dist/index.d.ts +2 -1
  12. package/dist/index.d.ts.map +1 -1
  13. package/dist/index.js +496 -114
  14. package/dist/test/handlers.d.ts +1 -0
  15. package/dist/test/handlers.d.ts.map +1 -1
  16. package/dist/test/test_data.d.ts.map +1 -1
  17. package/dist/types.d.ts +93 -24
  18. package/dist/types.d.ts.map +1 -1
  19. package/dist/utils/handle_blob.d.ts +2 -1
  20. package/dist/utils/handle_blob.d.ts.map +1 -1
  21. package/dist/utils/predict.d.ts +2 -2
  22. package/dist/utils/predict.d.ts.map +1 -1
  23. package/dist/utils/stream.d.ts +2 -1
  24. package/dist/utils/stream.d.ts.map +1 -1
  25. package/dist/utils/submit.d.ts +2 -2
  26. package/dist/utils/submit.d.ts.map +1 -1
  27. package/index.html +39 -0
  28. package/package.json +5 -2
  29. package/src/client.ts +40 -35
  30. package/src/constants.ts +4 -0
  31. package/src/helpers/api_info.ts +1 -1
  32. package/src/helpers/data.ts +124 -10
  33. package/src/helpers/init_helpers.ts +5 -0
  34. package/src/helpers/spaces.ts +2 -1
  35. package/src/index.ts +6 -1
  36. package/src/test/api_info.test.ts +0 -1
  37. package/src/test/data.test.ts +201 -26
  38. package/src/test/handlers.ts +9 -1
  39. package/src/test/stream.test.ts +12 -10
  40. package/src/test/test_data.ts +8 -5
  41. package/src/test/upload_files.test.ts +1 -1
  42. package/src/types.ts +110 -26
  43. package/src/utils/handle_blob.ts +91 -1
  44. package/src/utils/predict.ts +15 -16
  45. package/src/utils/stream.ts +66 -13
  46. package/src/utils/submit.ts +156 -63
  47. package/src/utils/upload_files.ts +1 -1
  48. package/vite.config.js +37 -24
@@ -1,12 +1,18 @@
1
- import { NodeBlob } from "../client";
2
- import type {
3
- ApiData,
4
- BlobRef,
5
- Config,
6
- EndpointInfo,
7
- JsApiData,
8
- DataType
1
+ import {
2
+ type ApiData,
3
+ type BlobRef,
4
+ type Config,
5
+ type EndpointInfo,
6
+ type JsApiData,
7
+ type DataType,
8
+ Command,
9
+ type Dependency,
10
+ type ComponentMeta
9
11
  } from "../types";
12
+ import { FileData } from "../upload";
13
+
14
+ const is_node =
15
+ typeof process !== "undefined" && process.versions && process.versions.node;
10
16
 
11
17
  export function update_object(
12
18
  object: { [x: string]: any },
@@ -64,11 +70,10 @@ export async function walk_and_store_blobs(
64
70
  (globalThis.Buffer && data instanceof globalThis.Buffer) ||
65
71
  data instanceof Blob
66
72
  ) {
67
- const is_image = type === "Image";
68
73
  return [
69
74
  {
70
75
  path: path,
71
- blob: is_image ? false : new NodeBlob([data]),
76
+ blob: new Blob([data]),
72
77
  type
73
78
  }
74
79
  ];
@@ -118,3 +123,112 @@ export function post_message<Res = any>(
118
123
  window.parent.postMessage(message, origin, [channel.port2]);
119
124
  });
120
125
  }
126
+
127
+ export function handle_file(
128
+ file_or_url: File | string | Blob | Buffer
129
+ ): FileData | Blob | Command {
130
+ if (typeof file_or_url === "string") {
131
+ if (
132
+ file_or_url.startsWith("http://") ||
133
+ file_or_url.startsWith("https://")
134
+ ) {
135
+ return {
136
+ path: file_or_url,
137
+ url: file_or_url,
138
+ orig_name: file_or_url.split("/").pop() ?? "unknown",
139
+ meta: { _type: "gradio.FileData" }
140
+ };
141
+ }
142
+
143
+ if (is_node) {
144
+ // Handle local file paths
145
+ return new Command("upload_file", {
146
+ path: file_or_url,
147
+ name: file_or_url,
148
+ orig_path: file_or_url
149
+ });
150
+ }
151
+ } else if (typeof File !== "undefined" && file_or_url instanceof File) {
152
+ return {
153
+ path: file_or_url instanceof File ? file_or_url.name : "blob",
154
+ orig_name: file_or_url instanceof File ? file_or_url.name : "unknown",
155
+ // @ts-ignore
156
+ blob: file_or_url instanceof File ? file_or_url : new Blob([file_or_url]),
157
+ size:
158
+ file_or_url instanceof Blob
159
+ ? file_or_url.size
160
+ : Buffer.byteLength(file_or_url as Buffer),
161
+ mime_type:
162
+ file_or_url instanceof File
163
+ ? file_or_url.type
164
+ : "application/octet-stream", // Default MIME type for buffers
165
+ meta: { _type: "gradio.FileData" }
166
+ };
167
+ } else if (file_or_url instanceof Buffer) {
168
+ return new Blob([file_or_url]);
169
+ } else if (file_or_url instanceof Blob) {
170
+ return file_or_url;
171
+ }
172
+ throw new Error(
173
+ "Invalid input: must be a URL, File, Blob, or Buffer object."
174
+ );
175
+ }
176
+
177
+ /**
178
+ * Handles the payload by filtering out state inputs and returning an array of resolved payload values.
179
+ * We send null values for state inputs to the server, but we don't want to include them in the resolved payload.
180
+ *
181
+ * @param resolved_payload - The resolved payload values received from the client or the server
182
+ * @param dependency - The dependency object.
183
+ * @param components - The array of component metadata.
184
+ * @param with_null_state - Optional. Specifies whether to include null values for state inputs. Default is false.
185
+ * @returns An array of resolved payload values, filtered based on the dependency and component metadata.
186
+ */
187
+ export function handle_payload(
188
+ resolved_payload: unknown[],
189
+ dependency: Dependency,
190
+ components: ComponentMeta[],
191
+ type: "input" | "output",
192
+ with_null_state = false
193
+ ): unknown[] {
194
+ if (type === "input" && !with_null_state) {
195
+ throw new Error("Invalid code path. Cannot skip state inputs for input.");
196
+ }
197
+ // data comes from the server with null state values so we skip
198
+ if (type === "output" && with_null_state) {
199
+ return resolved_payload;
200
+ }
201
+
202
+ let updated_payload: unknown[] = [];
203
+ let payload_index = 0;
204
+ for (let i = 0; i < dependency.inputs.length; i++) {
205
+ const input_id = dependency.inputs[i];
206
+ const component = components.find((c) => c.id === input_id);
207
+
208
+ if (component?.type === "state") {
209
+ // input + with_null_state needs us to fill state with null values
210
+ if (with_null_state) {
211
+ if (resolved_payload.length === dependency.inputs.length) {
212
+ const value = resolved_payload[payload_index];
213
+ updated_payload.push(value);
214
+ payload_index++;
215
+ } else {
216
+ updated_payload.push(null);
217
+ }
218
+ } else {
219
+ // this is output & !with_null_state, we skip state inputs
220
+ // the server payload always comes with null state values so we move along the payload index
221
+ payload_index++;
222
+ continue;
223
+ }
224
+ // input & !with_null_state isn't a case we care about, server needs null
225
+ continue;
226
+ } else {
227
+ const value = resolved_payload[payload_index];
228
+ updated_payload.push(value);
229
+ payload_index++;
230
+ }
231
+ }
232
+
233
+ return updated_payload;
234
+ }
@@ -101,6 +101,11 @@ export async function resolve_config(
101
101
  let config = await response.json();
102
102
  config.path = config.path ?? "";
103
103
  config.root = endpoint;
104
+ config.dependencies?.forEach((dep: any, i: number) => {
105
+ if (dep.id === undefined) {
106
+ dep.id = i;
107
+ }
108
+ });
104
109
  return config;
105
110
  } else if (response?.status === 401) {
106
111
  throw new Error(UNAUTHORIZED_MSG);
@@ -106,9 +106,10 @@ export async function discussions_enabled(space_id: string): Promise<boolean> {
106
106
  method: "HEAD"
107
107
  }
108
108
  );
109
+
109
110
  const error = r.headers.get("x-error-message");
110
111
 
111
- if (error && RE_DISABLED_DISCUSSION.test(error)) return false;
112
+ if (!r.ok || (error && RE_DISABLED_DISCUSSION.test(error))) return false;
112
113
  return true;
113
114
  } catch (e) {
114
115
  return false;
package/src/index.ts CHANGED
@@ -4,12 +4,17 @@ export { predict } from "./utils/predict";
4
4
  export { submit } from "./utils/submit";
5
5
  export { upload_files } from "./utils/upload_files";
6
6
  export { FileData, upload, prepare_files } from "./upload";
7
+ export { handle_file } from "./helpers/data";
7
8
 
8
9
  export type {
9
10
  SpaceStatus,
11
+ StatusMessage,
10
12
  Status,
11
13
  client_return,
12
- UploadResponse
14
+ UploadResponse,
15
+ RenderMessage,
16
+ LogMessage,
17
+ Payload
13
18
  } from "./types";
14
19
 
15
20
  // todo: remove in @gradio/client v1.0
@@ -16,7 +16,6 @@ import { initialise_server } from "./server";
16
16
  import { transformed_api_info } from "./test_data";
17
17
 
18
18
  const server = initialise_server();
19
- const IS_NODE = process.env.TEST_MODE === "node";
20
19
 
21
20
  beforeAll(() => server.listen());
22
21
  afterEach(() => server.resetHandlers());
@@ -3,11 +3,15 @@ import {
3
3
  update_object,
4
4
  walk_and_store_blobs,
5
5
  skip_queue,
6
- post_message
6
+ post_message,
7
+ handle_file,
8
+ handle_payload
7
9
  } from "../helpers/data";
8
- import { NodeBlob } from "../client";
9
10
  import { config_response, endpoint_info } from "./test_data";
10
- import { BlobRef } from "../types";
11
+ import { BlobRef, Command } from "../types";
12
+ import { FileData } from "../upload";
13
+
14
+ const IS_NODE = process.env.TEST_MODE === "node";
11
15
 
12
16
  describe("walk_and_store_blobs", () => {
13
17
  it("should convert a Buffer to a Blob", async () => {
@@ -15,7 +19,7 @@ describe("walk_and_store_blobs", () => {
15
19
  const parts = await walk_and_store_blobs(buffer, "text");
16
20
 
17
21
  expect(parts).toHaveLength(1);
18
- expect(parts[0].blob).toBeInstanceOf(NodeBlob);
22
+ expect(parts[0].blob).toBeInstanceOf(Blob);
19
23
  });
20
24
 
21
25
  it("should return a Blob when passed a Blob", async () => {
@@ -28,19 +32,7 @@ describe("walk_and_store_blobs", () => {
28
32
  endpoint_info
29
33
  );
30
34
 
31
- expect(parts[0].blob).toBeInstanceOf(NodeBlob);
32
- });
33
-
34
- it("should return blob: false when passed an image", async () => {
35
- const blob = new Blob([]);
36
- const parts = await walk_and_store_blobs(
37
- blob,
38
- "Image",
39
- [],
40
- true,
41
- endpoint_info
42
- );
43
- expect(parts[0].blob).toBe(false);
35
+ expect(parts[0].blob).toBeInstanceOf(Blob);
44
36
  });
45
37
 
46
38
  it("should handle arrays", async () => {
@@ -48,7 +40,7 @@ describe("walk_and_store_blobs", () => {
48
40
  const parts = await walk_and_store_blobs([image]);
49
41
 
50
42
  expect(parts).toHaveLength(1);
51
- expect(parts[0].blob).toBeInstanceOf(NodeBlob);
43
+ expect(parts[0].blob).toBeInstanceOf(Blob);
52
44
  expect(parts[0].path).toEqual(["0"]);
53
45
  });
54
46
 
@@ -57,7 +49,7 @@ describe("walk_and_store_blobs", () => {
57
49
  const parts = await walk_and_store_blobs({ a: { b: { data: { image } } } });
58
50
 
59
51
  expect(parts).toHaveLength(1);
60
- expect(parts[0].blob).toBeInstanceOf(NodeBlob);
52
+ expect(parts[0].blob).toBeInstanceOf(Blob);
61
53
  expect(parts[0].path).toEqual(["a", "b", "data", "image"]);
62
54
  });
63
55
 
@@ -79,7 +71,7 @@ describe("walk_and_store_blobs", () => {
79
71
  ]
80
72
  });
81
73
 
82
- expect(parts[0].blob).toBeInstanceOf(NodeBlob);
74
+ expect(parts[0].blob).toBeInstanceOf(Blob);
83
75
  });
84
76
 
85
77
  it("should handle deep structures with arrays (with equality check)", async () => {
@@ -103,8 +95,8 @@ describe("walk_and_store_blobs", () => {
103
95
  let ref = obj;
104
96
  path.forEach((p) => (ref = ref[p]));
105
97
 
106
- // since ref is a Blob and blob is a NodeBlob, we deep equal check the two buffers instead
107
- if (ref instanceof Blob && blob instanceof NodeBlob) {
98
+ // since ref is a Blob and blob is a Blob, we deep equal check the two buffers instead
99
+ if (ref instanceof Blob && blob instanceof Blob) {
108
100
  const refBuffer = Buffer.from(await ref.arrayBuffer());
109
101
  const blobBuffer = Buffer.from(await blob.arrayBuffer());
110
102
  return refBuffer.equals(blobBuffer);
@@ -113,7 +105,7 @@ describe("walk_and_store_blobs", () => {
113
105
  return ref === blob;
114
106
  }
115
107
 
116
- expect(parts[0].blob).toBeInstanceOf(NodeBlob);
108
+ expect(parts[0].blob).toBeInstanceOf(Blob);
117
109
  expect(map_path(obj, parts)).toBeTruthy();
118
110
  });
119
111
 
@@ -122,7 +114,7 @@ describe("walk_and_store_blobs", () => {
122
114
  const parts = await walk_and_store_blobs(buffer, undefined, ["blob"]);
123
115
 
124
116
  expect(parts).toHaveLength(1);
125
- expect(parts[0].blob).toBeInstanceOf(NodeBlob);
117
+ expect(parts[0].blob).toBeInstanceOf(Blob);
126
118
  expect(parts[0].path).toEqual(["blob"]);
127
119
  });
128
120
 
@@ -132,7 +124,7 @@ describe("walk_and_store_blobs", () => {
132
124
 
133
125
  expect(parts).toHaveLength(1);
134
126
  expect(parts[0].path).toEqual([]);
135
- expect(parts[0].blob).toBeInstanceOf(NodeBlob);
127
+ expect(parts[0].blob).toBeInstanceOf(Blob);
136
128
  });
137
129
 
138
130
  it("should convert an object with deep structures to BlobRefs", async () => {
@@ -149,7 +141,7 @@ describe("walk_and_store_blobs", () => {
149
141
 
150
142
  expect(parts).toHaveLength(1);
151
143
  expect(parts[0].path).toEqual(["a", "b", "data", "image"]);
152
- expect(parts[0].blob).toBeInstanceOf(NodeBlob);
144
+ expect(parts[0].blob).toBeInstanceOf(Blob);
153
145
  });
154
146
  });
155
147
  describe("update_object", () => {
@@ -276,3 +268,186 @@ describe("post_message", () => {
276
268
  ]);
277
269
  });
278
270
  });
271
+
272
+ describe("handle_file", () => {
273
+ it("should handle a Blob object and return the blob", () => {
274
+ const blob = new Blob(["test data"], { type: "image/png" });
275
+ const result = handle_file(blob) as FileData;
276
+
277
+ expect(result).toBe(blob);
278
+ });
279
+
280
+ it("should handle a Buffer object and return it as a blob", () => {
281
+ const buffer = Buffer.from("test data");
282
+ const result = handle_file(buffer) as FileData;
283
+ expect(result).toBeInstanceOf(Blob);
284
+ });
285
+ it("should handle a local file path and return a Command object", () => {
286
+ const file_path = "./owl.png";
287
+ const result = handle_file(file_path) as Command;
288
+ expect(result).toBeInstanceOf(Command);
289
+ expect(result).toEqual({
290
+ type: "command",
291
+ command: "upload_file",
292
+ meta: { path: "./owl.png", name: "./owl.png", orig_path: "./owl.png" },
293
+ fileData: undefined
294
+ });
295
+ });
296
+
297
+ it("should handle a File object and return it as FileData", () => {
298
+ if (IS_NODE) {
299
+ return;
300
+ }
301
+ const file = new File(["test image"], "test.png", { type: "image/png" });
302
+ const result = handle_file(file) as FileData;
303
+ expect(result.path).toBe("test.png");
304
+ expect(result.orig_name).toBe("test.png");
305
+ expect(result.blob).toBeInstanceOf(Blob);
306
+ expect(result.size).toBe(file.size);
307
+ expect(result.mime_type).toBe("image/png");
308
+ expect(result.meta).toEqual({ _type: "gradio.FileData" });
309
+ });
310
+
311
+ it("should throw an error for invalid input", () => {
312
+ const invalid_input = 123;
313
+
314
+ expect(() => {
315
+ // @ts-ignore
316
+ handle_file(invalid_input);
317
+ }).toThrowError(
318
+ "Invalid input: must be a URL, File, Blob, or Buffer object."
319
+ );
320
+ });
321
+ });
322
+
323
+ describe("handle_payload", () => {
324
+ it("should return an input payload with null in place of `state` when with_null_state is true", () => {
325
+ const resolved_payload = [2];
326
+ const dependency = {
327
+ inputs: [1, 2]
328
+ };
329
+ const components = [
330
+ { id: 1, type: "number" },
331
+ { id: 2, type: "state" }
332
+ ];
333
+ const with_null_state = true;
334
+ const result = handle_payload(
335
+ resolved_payload,
336
+ // @ts-ignore
337
+ dependency,
338
+ components,
339
+ "input",
340
+ with_null_state
341
+ );
342
+ expect(result).toEqual([2, null]);
343
+ });
344
+ it("should return an input payload with null in place of two `state` components when with_null_state is true", () => {
345
+ const resolved_payload = ["hello", "goodbye"];
346
+ const dependency = {
347
+ inputs: [1, 2, 3, 4]
348
+ };
349
+ const components = [
350
+ { id: 1, type: "textbox" },
351
+ { id: 2, type: "state" },
352
+ { id: 3, type: "textbox" },
353
+ { id: 4, type: "state" }
354
+ ];
355
+ const with_null_state = true;
356
+ const result = handle_payload(
357
+ resolved_payload,
358
+ // @ts-ignore
359
+ dependency,
360
+ components,
361
+ "input",
362
+ with_null_state
363
+ );
364
+ expect(result).toEqual(["hello", null, "goodbye", null]);
365
+ });
366
+
367
+ it("should return an output payload without the state component value when with_null_state is false", () => {
368
+ const resolved_payload = ["hello", null];
369
+ const dependency = {
370
+ inputs: [2, 3]
371
+ };
372
+ const components = [
373
+ { id: 2, type: "textbox" },
374
+ { id: 3, type: "state" }
375
+ ];
376
+ const with_null_state = false;
377
+ const result = handle_payload(
378
+ resolved_payload,
379
+ // @ts-ignore
380
+ dependency,
381
+ components,
382
+ "output",
383
+ with_null_state
384
+ );
385
+ expect(result).toEqual(["hello"]);
386
+ });
387
+
388
+ it("should return an ouput payload without the two state component values when with_null_state is false", () => {
389
+ const resolved_payload = ["hello", null, "world", null];
390
+ const dependency = {
391
+ inputs: [2, 3, 4, 5]
392
+ };
393
+ const components = [
394
+ { id: 2, type: "textbox" },
395
+ { id: 3, type: "state" },
396
+ { id: 4, type: "textbox" },
397
+ { id: 5, type: "state" }
398
+ ];
399
+ const with_null_state = false;
400
+ const result = handle_payload(
401
+ resolved_payload,
402
+ // @ts-ignore
403
+ dependency,
404
+ components,
405
+ "output",
406
+ with_null_state
407
+ );
408
+ expect(result).toEqual(["hello", "world"]);
409
+ });
410
+
411
+ it("should return an ouput payload with the two state component values when with_null_state is true", () => {
412
+ const resolved_payload = ["hello", null, "world", null];
413
+ const dependency = {
414
+ inputs: [2, 3, 4, 5]
415
+ };
416
+ const components = [
417
+ { id: 2, type: "textbox" },
418
+ { id: 3, type: "state" },
419
+ { id: 4, type: "textbox" },
420
+ { id: 5, type: "state" }
421
+ ];
422
+ const with_null_state = true;
423
+ const result = handle_payload(
424
+ resolved_payload,
425
+ // @ts-ignore
426
+ dependency,
427
+ components,
428
+ "output",
429
+ with_null_state
430
+ );
431
+ expect(result).toEqual(["hello", null, "world", null]);
432
+ });
433
+
434
+ it("should return the same payload where no state components are defined", () => {
435
+ const resolved_payload = ["hello", "world"];
436
+ const dependency = {
437
+ inputs: [2, 3]
438
+ };
439
+ const components = [
440
+ { id: 2, type: "textbox" },
441
+ { id: 3, type: "textbox" }
442
+ ];
443
+ const with_null_state = true;
444
+ const result = handle_payload(
445
+ resolved_payload,
446
+ // @ts-ignore
447
+ dependency,
448
+ components,
449
+ with_null_state
450
+ );
451
+ expect(result).toEqual(["hello", "world"]);
452
+ });
453
+ });
@@ -21,7 +21,7 @@ import {
21
21
 
22
22
  const root_url = "https://huggingface.co";
23
23
 
24
- const direct_space_url = "https://hmb-hello-world.hf.space";
24
+ export const direct_space_url = "https://hmb-hello-world.hf.space";
25
25
  const private_space_url = "https://hmb-secret-world.hf.space";
26
26
  const private_auth_space_url = "https://hmb-private-auth-space.hf.space";
27
27
 
@@ -431,6 +431,14 @@ export const handlers: RequestHandler[] = [
431
431
  });
432
432
  }),
433
433
  // queue requests
434
+ http.get(`${direct_space_url}/queue/data`, () => {
435
+ return new HttpResponse(JSON.stringify({ event_id: "123" }), {
436
+ status: 200,
437
+ headers: {
438
+ "Content-Type": "application/json"
439
+ }
440
+ });
441
+ }),
434
442
  http.post(`${direct_space_url}/queue/join`, () => {
435
443
  return new HttpResponse(JSON.stringify({ event_id: "123" }), {
436
444
  status: 200,
@@ -1,6 +1,8 @@
1
1
  import { vi, type Mock } from "vitest";
2
2
  import { Client } from "../client";
3
+ import { readable_stream } from "../utils/stream";
3
4
  import { initialise_server } from "./server";
5
+ import { direct_space_url } from "./handlers.ts";
4
6
 
5
7
  import {
6
8
  describe,
@@ -11,27 +13,23 @@ import {
11
13
  afterAll,
12
14
  beforeEach
13
15
  } from "vitest";
14
- import "./mock_eventsource.ts";
15
- import NodeEventSource from "eventsource";
16
16
 
17
17
  const server = initialise_server();
18
- const IS_NODE = process.env.TEST_MODE === "node";
19
18
 
20
19
  beforeAll(() => server.listen());
21
20
  afterEach(() => server.resetHandlers());
22
21
  afterAll(() => server.close());
23
22
 
24
23
  describe("open_stream", () => {
25
- let mock_eventsource: any;
26
24
  let app: Client;
27
25
 
28
26
  beforeEach(async () => {
29
27
  app = await Client.connect("hmb/hello_world");
30
28
  app.stream = vi.fn().mockImplementation(() => {
31
- mock_eventsource = IS_NODE
32
- ? new NodeEventSource("")
33
- : new EventSource("");
34
- return mock_eventsource;
29
+ app.stream_instance = readable_stream(
30
+ new URL(`${direct_space_url}/queue/data`)
31
+ );
32
+ return app.stream_instance;
35
33
  });
36
34
  });
37
35
 
@@ -58,8 +56,12 @@ describe("open_stream", () => {
58
56
 
59
57
  expect(app.stream).toHaveBeenCalledWith(eventsource_mock_call);
60
58
 
61
- const onMessageCallback = mock_eventsource.onmessage;
62
- const onErrorCallback = mock_eventsource.onerror;
59
+ if (!app.stream_instance?.onmessage || !app.stream_instance?.onerror) {
60
+ throw new Error("stream instance is not defined");
61
+ }
62
+
63
+ const onMessageCallback = app.stream_instance.onmessage.bind(app);
64
+ const onErrorCallback = app.stream_instance.onerror.bind(app);
63
65
 
64
66
  const message = { msg: "hello jerry" };
65
67
 
@@ -46,7 +46,7 @@ export const transformed_api_info: ApiInfo<ApiData> = {
46
46
  component: "Textbox"
47
47
  }
48
48
  ],
49
- type: { continuous: false, generator: false }
49
+ type: { continuous: false, generator: false, cancel: false }
50
50
  }
51
51
  },
52
52
  unnamed_endpoints: {
@@ -68,7 +68,7 @@ export const transformed_api_info: ApiInfo<ApiData> = {
68
68
  component: "Textbox"
69
69
  }
70
70
  ],
71
- type: { continuous: false, generator: false }
71
+ type: { continuous: false, generator: false, cancel: false }
72
72
  }
73
73
  }
74
74
  };
@@ -395,7 +395,8 @@ export const config_response: Config = {
395
395
  cancels: [],
396
396
  types: {
397
397
  continuous: false,
398
- generator: false
398
+ generator: false,
399
+ cancel: false
399
400
  },
400
401
  collects_event_data: false,
401
402
  trigger_after: null,
@@ -421,7 +422,8 @@ export const config_response: Config = {
421
422
  cancels: [],
422
423
  types: {
423
424
  continuous: false,
424
- generator: false
425
+ generator: false,
426
+ cancel: false
425
427
  },
426
428
  collects_event_data: false,
427
429
  trigger_after: null,
@@ -447,7 +449,8 @@ export const config_response: Config = {
447
449
  cancels: [],
448
450
  types: {
449
451
  continuous: false,
450
- generator: false
452
+ generator: false,
453
+ cancel: false
451
454
  },
452
455
  collects_event_data: false,
453
456
  trigger_after: null,
@@ -29,7 +29,7 @@ describe("upload_files", () => {
29
29
  expect(response.files[0]).toBe("lion.jpg");
30
30
  });
31
31
 
32
- it("should handle a server error when connected to a running app and uploading files", async () => {
32
+ it.skip("should handle a server error when connected to a running app and uploading files", async () => {
33
33
  const client = await Client.connect("hmb/server_test");
34
34
 
35
35
  const root_url = "https://hmb-server-test.hf.space";