@gradio/client 2.0.4 → 2.2.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.
@@ -11,11 +11,64 @@ import { config_response, endpoint_info } from "./test_data";
11
11
  import { BlobRef, Command } from "../types";
12
12
  import { FileData } from "../upload";
13
13
 
14
- const IS_NODE = process.env.TEST_MODE === "node";
14
+ const IS_NODE =
15
+ typeof process !== "undefined" && process.env.TEST_MODE === "node";
16
+
17
+ class FakeBuffer {
18
+ static from(data: string) {
19
+ return new Blob([data]);
20
+ }
21
+
22
+ static isBuffer() {
23
+ return false;
24
+ }
25
+
26
+ static isEncoding() {
27
+ return false;
28
+ }
29
+
30
+ static byteLength() {
31
+ return 0;
32
+ }
33
+
34
+ static concat() {
35
+ return new Blob([]);
36
+ }
37
+
38
+ static compare() {
39
+ return 0;
40
+ }
41
+
42
+ static alloc() {
43
+ return new Blob([]);
44
+ }
45
+
46
+ static allocUnsafe() {
47
+ return new Blob([]);
48
+ }
49
+
50
+ static allocUnsafeSlow() {
51
+ return new Blob([]);
52
+ }
53
+
54
+ static poolSize = 0;
55
+
56
+ static of() {
57
+ return new Blob([]);
58
+ }
59
+
60
+ equals(other: Blob) {
61
+ return this.toString() === other.toString();
62
+ }
63
+ }
64
+
65
+ if (!IS_NODE) {
66
+ globalThis.Buffer = FakeBuffer as unknown as BufferConstructor;
67
+ }
15
68
 
16
69
  describe("walk_and_store_blobs", () => {
17
- it("should convert a Buffer to a Blob", async () => {
18
- const buffer = Buffer.from("test data");
70
+ it.skipIf(!IS_NODE)("should convert a Buffer to a Blob", async () => {
71
+ const buffer = globalThis.Buffer.from("test data");
19
72
  const parts = await walk_and_store_blobs(buffer, "text");
20
73
 
21
74
  expect(parts).toHaveLength(1);
@@ -74,75 +127,87 @@ describe("walk_and_store_blobs", () => {
74
127
  expect(parts[0].blob).toBeInstanceOf(Blob);
75
128
  });
76
129
 
77
- it("should handle deep structures with arrays (with equality check)", async () => {
78
- const image = new Blob([]);
79
-
80
- const obj = {
81
- a: [
82
- {
83
- b: [
84
- {
85
- data: [[image], image, [image, [image]]]
86
- }
87
- ]
130
+ it.skipIf(!IS_NODE)(
131
+ "should handle deep structures with arrays (with equality check)",
132
+ async () => {
133
+ const image = new Blob([]);
134
+
135
+ const obj = {
136
+ a: [
137
+ {
138
+ b: [
139
+ {
140
+ data: [[image], image, [image, [image]]]
141
+ }
142
+ ]
143
+ }
144
+ ]
145
+ };
146
+ const parts = await walk_and_store_blobs(obj);
147
+
148
+ async function map_path(obj: Record<string, any>, parts: BlobRef[]) {
149
+ const { path, blob } = parts[parts.length - 1];
150
+ let ref = obj;
151
+ path.forEach((p) => (ref = ref[p]));
152
+
153
+ // since ref is a Blob and blob is a Blob, we deep equal check the two buffers instead
154
+ if (ref instanceof Blob && blob instanceof Blob) {
155
+ const refBuffer = Buffer.from(await ref.arrayBuffer());
156
+ const blobBuffer = Buffer.from(await blob.arrayBuffer());
157
+ return refBuffer.equals(blobBuffer);
88
158
  }
89
- ]
90
- };
91
- const parts = await walk_and_store_blobs(obj);
92
-
93
- async function map_path(obj: Record<string, any>, parts: BlobRef[]) {
94
- const { path, blob } = parts[parts.length - 1];
95
- let ref = obj;
96
- path.forEach((p) => (ref = ref[p]));
97
-
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) {
100
- const refBuffer = Buffer.from(await ref.arrayBuffer());
101
- const blobBuffer = Buffer.from(await blob.arrayBuffer());
102
- return refBuffer.equals(blobBuffer);
159
+
160
+ return ref === blob;
103
161
  }
104
162
 
105
- return ref === blob;
163
+ expect(parts[0].blob).toBeInstanceOf(Blob);
164
+ expect(map_path(obj, parts)).toBeTruthy();
106
165
  }
166
+ );
107
167
 
108
- expect(parts[0].blob).toBeInstanceOf(Blob);
109
- expect(map_path(obj, parts)).toBeTruthy();
110
- });
111
-
112
- it("should handle buffer instances and return a BlobRef", async () => {
113
- const buffer = Buffer.from("test");
114
- const parts = await walk_and_store_blobs(buffer, undefined, ["blob"]);
115
-
116
- expect(parts).toHaveLength(1);
117
- expect(parts[0].blob).toBeInstanceOf(Blob);
118
- expect(parts[0].path).toEqual(["blob"]);
119
- });
168
+ it.skipIf(!IS_NODE)(
169
+ "should handle buffer instances and return a BlobRef",
170
+ async () => {
171
+ const buffer = Buffer.from("test");
172
+ const parts = await walk_and_store_blobs(buffer, undefined, ["blob"]);
120
173
 
121
- it("should handle buffer instances with a path and return a BlobRef with the path", async () => {
122
- const buffer = Buffer.from("test data");
123
- const parts = await walk_and_store_blobs(buffer);
174
+ expect(parts).toHaveLength(1);
175
+ expect(parts[0].blob).toBeInstanceOf(Blob);
176
+ expect(parts[0].path).toEqual(["blob"]);
177
+ }
178
+ );
124
179
 
125
- expect(parts).toHaveLength(1);
126
- expect(parts[0].path).toEqual([]);
127
- expect(parts[0].blob).toBeInstanceOf(Blob);
128
- });
180
+ it.skipIf(!IS_NODE)(
181
+ "should handle buffer instances with a path and return a BlobRef with the path",
182
+ async () => {
183
+ const buffer = Buffer.from("test data");
184
+ const parts = await walk_and_store_blobs(buffer);
129
185
 
130
- it("should convert an object with deep structures to BlobRefs", async () => {
131
- const param = {
132
- a: {
133
- b: {
134
- data: {
135
- image: Buffer.from("test image")
186
+ expect(parts).toHaveLength(1);
187
+ expect(parts[0].path).toEqual([]);
188
+ expect(parts[0].blob).toBeInstanceOf(Blob);
189
+ }
190
+ );
191
+
192
+ it.skipIf(!IS_NODE)(
193
+ "should convert an object with deep structures to BlobRefs",
194
+ async () => {
195
+ const param = {
196
+ a: {
197
+ b: {
198
+ data: {
199
+ image: Buffer.from("test image")
200
+ }
136
201
  }
137
202
  }
138
- }
139
- };
140
- const parts = await walk_and_store_blobs(param);
203
+ };
204
+ const parts = await walk_and_store_blobs(param);
141
205
 
142
- expect(parts).toHaveLength(1);
143
- expect(parts[0].path).toEqual(["a", "b", "data", "image"]);
144
- expect(parts[0].blob).toBeInstanceOf(Blob);
145
- });
206
+ expect(parts).toHaveLength(1);
207
+ expect(parts[0].path).toEqual(["a", "b", "data", "image"]);
208
+ expect(parts[0].blob).toBeInstanceOf(Blob);
209
+ }
210
+ );
146
211
  });
147
212
  describe("update_object", () => {
148
213
  it("should update the value of a nested property", () => {
@@ -227,46 +292,50 @@ describe("post_message", () => {
227
292
  vi.restoreAllMocks();
228
293
  });
229
294
 
230
- it("should send a message to the parent window and resolve with received data", async () => {
231
- const test_data = { key: "value" };
232
- const test_origin = "https://huggingface.co";
233
-
234
- const post_message_mock = vi.fn();
295
+ it.skipIf(IS_NODE)(
296
+ "should send a message to the parent window and resolve with received data",
297
+ async () => {
298
+ const test_data = { key: "value" };
299
+ const test_origin = "https://huggingface.co";
300
+
301
+ // Create a mock for window.parent.postMessage that we'll spy on
302
+ const post_message_spy = vi
303
+ .spyOn(window.parent, "postMessage")
304
+ .mockImplementation(() => {});
305
+
306
+ // Mock MessageChannel
307
+ const original_message_channel = globalThis.MessageChannel;
308
+ const mock_port1 = {
309
+ onmessage: null as unknown as (event: { data: any }) => void,
310
+ close: vi.fn()
311
+ };
312
+ const mock_port2 = {};
235
313
 
236
- global.window = {
237
- // @ts-ignore
238
- parent: {
239
- postMessage: post_message_mock
314
+ class MockMessageChannel {
315
+ port1 = mock_port1;
316
+ port2 = mock_port2;
240
317
  }
241
- };
242
318
 
243
- const message_channel_mock = {
244
- port1: {
245
- onmessage: (handler) => {
246
- onmessage = handler;
247
- },
248
- close: vi.fn()
249
- },
250
- port2: {}
251
- };
319
+ // Replace MessageChannel with our mock version
320
+ globalThis.MessageChannel = MockMessageChannel as any;
252
321
 
253
- vi.stubGlobal("MessageChannel", function () {
254
- this.port1 = message_channel_mock.port1;
255
- this.port2 = message_channel_mock.port2;
256
- return this;
257
- });
322
+ const promise = post_message(test_data, test_origin);
258
323
 
259
- const promise = post_message(test_data, test_origin);
324
+ // Simulate receiving a message back
325
+ if (mock_port1.onmessage) {
326
+ mock_port1.onmessage({ data: test_data } as any);
327
+ }
260
328
 
261
- if (message_channel_mock.port1.onmessage) {
262
- message_channel_mock.port1.onmessage({ data: test_data });
263
- }
329
+ await expect(promise).resolves.toEqual(test_data);
330
+ expect(post_message_spy).toHaveBeenCalledWith(test_data, test_origin, [
331
+ mock_port2
332
+ ]);
264
333
 
265
- await expect(promise).resolves.toEqual(test_data);
266
- expect(post_message_mock).toHaveBeenCalledWith(test_data, test_origin, [
267
- message_channel_mock.port2
268
- ]);
269
- });
334
+ // Restore original MessageChannel
335
+ globalThis.MessageChannel = original_message_channel;
336
+ post_message_spy.mockRestore();
337
+ }
338
+ );
270
339
  });
271
340
 
272
341
  describe("handle_file", () => {
@@ -277,31 +346,38 @@ describe("handle_file", () => {
277
346
  expect(result).toBe(blob);
278
347
  });
279
348
 
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;
349
+ it.skipIf(!IS_NODE)(
350
+ "should handle a Buffer object and return it as a blob",
351
+ () => {
352
+ const buffer = Buffer.from("test data");
353
+ const result = handle_file(buffer) as FileData;
354
+ expect(result).toBeInstanceOf(Blob);
300
355
  }
301
- const file = new File(["test image"], "test.png", { type: "image/png" });
302
- const result = handle_file(file) as FileData;
303
- expect(result).toBeInstanceOf(Blob);
304
- });
356
+ );
357
+
358
+ it.skipIf(!IS_NODE)(
359
+ "should handle a local file path and return a Command object",
360
+ () => {
361
+ const file_path = "./owl.png";
362
+ const result = handle_file(file_path) as Command;
363
+ expect(result).toBeInstanceOf(Command);
364
+ expect(result).toEqual({
365
+ type: "command",
366
+ command: "upload_file",
367
+ meta: { path: "./owl.png", name: "./owl.png", orig_path: "./owl.png" },
368
+ fileData: undefined
369
+ });
370
+ }
371
+ );
372
+
373
+ it.skipIf(IS_NODE)(
374
+ "should handle a File object and return it as FileData",
375
+ () => {
376
+ const file = new File(["test image"], "test.png", { type: "image/png" });
377
+ const result = handle_file(file) as FileData;
378
+ expect(result).toBeInstanceOf(Blob);
379
+ }
380
+ );
305
381
 
306
382
  it("should throw an error for invalid input", () => {
307
383
  const invalid_input = 123;
@@ -630,9 +630,6 @@ export const handlers: RequestHandler[] = [
630
630
  status: 200,
631
631
  headers: {
632
632
  "Content-Type": "application/json",
633
- "Set-Cookie":
634
- "access-token-123=abc; HttpOnly; Path=/; SameSite=none; Secure",
635
- // @ts-ignore - multiple Set-Cookie headers are returned
636
633
  "Set-Cookie":
637
634
  "access-token-unsecure-123=abc; HttpOnly; Path=/; SameSite=none; Secure"
638
635
  }
@@ -673,9 +670,6 @@ export const handlers: RequestHandler[] = [
673
670
  status: 200,
674
671
  headers: {
675
672
  "Content-Type": "application/json",
676
- "Set-Cookie":
677
- "access-token-123=abc; HttpOnly; Path=/; SameSite=none; Secure",
678
- // @ts-ignore - multiple Set-Cookie headers are returned
679
673
  "Set-Cookie":
680
674
  "access-token-unsecure-123=abc; HttpOnly; Path=/; SameSite=none; Secure"
681
675
  }
@@ -22,11 +22,14 @@ const broken_app_reference = "hmb/bye_world";
22
22
  const direct_app_reference = "https://hmb-hello-world.hf.space";
23
23
  const secret_direct_app_reference = "https://hmb-secret-world.hf.space";
24
24
 
25
- const server = initialise_server();
25
+ let server: Awaited<ReturnType<typeof initialise_server>>;
26
26
 
27
- beforeAll(() => server.listen());
27
+ beforeAll(async () => {
28
+ server = await initialise_server();
29
+ await server.start({ quiet: true });
30
+ });
28
31
  afterEach(() => server.resetHandlers());
29
- afterAll(() => server.close());
32
+ afterAll(() => server.stop());
30
33
 
31
34
  describe("Client class", () => {
32
35
  describe("initialisation", () => {
@@ -9,11 +9,14 @@ import { beforeAll, afterEach, afterAll, it, expect, describe } from "vitest";
9
9
  import { Client } from "../client";
10
10
  import { INVALID_CREDENTIALS_MSG, MISSING_CREDENTIALS_MSG } from "../constants";
11
11
 
12
- const server = initialise_server();
12
+ let server: Awaited<ReturnType<typeof initialise_server>>;
13
13
 
14
- beforeAll(() => server.listen());
14
+ beforeAll(async () => {
15
+ server = await initialise_server();
16
+ await server.start({ quiet: true });
17
+ });
15
18
  afterEach(() => server.resetHandlers());
16
- afterAll(() => server.close());
19
+ afterAll(() => server.stop());
17
20
 
18
21
  describe("resolve_root", () => {
19
22
  it('should return the base URL if the root path starts with "http://"', () => {
@@ -102,7 +105,7 @@ describe("resolve_cookies", () => {
102
105
 
103
106
  it("should connect to a private and authenticated space", async () => {
104
107
  const client = await Client.connect("hmb/private_auth_space", {
105
- hf_token: "hf_123",
108
+ token: "hf_123",
106
109
  auth: ["admin", "pass1234"]
107
110
  });
108
111
 
@@ -1,6 +1,6 @@
1
1
  import { vi } from "vitest";
2
2
 
3
- if (process.env.TEST_MODE !== "node") {
3
+ if (import.meta.env.TEST_MODE !== "node") {
4
4
  Object.defineProperty(window, "EventSource", {
5
5
  writable: true,
6
6
  value: vi.fn().mockImplementation(() => ({
@@ -2,12 +2,16 @@ import { Client } from "../client";
2
2
 
3
3
  import { initialise_server } from "./server";
4
4
  import { BROKEN_CONNECTION_MSG } from "../constants";
5
- const server = initialise_server();
6
5
  import { beforeAll, afterEach, afterAll, it, expect, describe } from "vitest";
7
6
 
8
- beforeAll(() => server.listen());
7
+ let server: Awaited<ReturnType<typeof initialise_server>>;
8
+
9
+ beforeAll(async () => {
10
+ server = await initialise_server();
11
+ await server.start({ quiet: true });
12
+ });
9
13
  afterEach(() => server.resetHandlers());
10
- afterAll(() => server.close());
14
+ afterAll(() => server.stop());
11
15
 
12
16
  describe("post_data", () => {
13
17
  it("should send a POST request with the correct headers and body", async () => {
@@ -1,6 +1,31 @@
1
- import { setupServer } from "msw/node";
2
1
  import { handlers } from "./handlers";
2
+ // import type { StartOptions } from 'msw';
3
+ import type { SetupWorker, StartOptions } from "msw/browser";
3
4
 
4
- export function initialise_server(): any {
5
- return setupServer(...handlers);
5
+ const IS_NODE =
6
+ typeof process !== "undefined" && process.env.TEST_MODE === "node";
7
+
8
+ interface MockServer {
9
+ start: (opts: StartOptions) => void | ReturnType<SetupWorker["start"]>;
10
+ stop: () => void | Promise<void>;
11
+ resetHandlers: (...handlers: any[]) => void;
12
+ }
13
+
14
+ export async function initialise_server(): Promise<MockServer> {
15
+ if (IS_NODE) {
16
+ const { setupServer } = await import("msw/node");
17
+ const server = setupServer(...handlers);
18
+ return {
19
+ start: (opts: StartOptions) => server.listen(opts),
20
+ stop: () => server.close(),
21
+ resetHandlers: (...h) => server.resetHandlers(...h)
22
+ };
23
+ }
24
+ const { setupWorker } = await import("msw/browser");
25
+ const worker = setupWorker(...handlers);
26
+ return {
27
+ start: (opts: StartOptions) => worker.start(opts),
28
+ stop: () => worker.stop(),
29
+ resetHandlers: (...h) => worker.resetHandlers(...h)
30
+ };
6
31
  }
@@ -11,11 +11,14 @@ import { initialise_server } from "./server";
11
11
  import { hardware_sleeptime_response } from "./test_data";
12
12
  import { vi } from "vitest";
13
13
 
14
- const server = initialise_server();
14
+ let server: Awaited<ReturnType<typeof initialise_server>>;
15
15
 
16
- beforeAll(() => server.listen());
16
+ beforeAll(async () => {
17
+ server = await initialise_server();
18
+ await server.start({ quiet: true });
19
+ });
17
20
  afterEach(() => server.resetHandlers());
18
- afterAll(() => server.close());
21
+ afterAll(() => server.stop());
19
22
 
20
23
  describe("set_space_timeout", () => {
21
24
  it("should set the sleep timeout for a space", async () => {
@@ -14,11 +14,14 @@ import {
14
14
  beforeEach
15
15
  } from "vitest";
16
16
 
17
- const server = initialise_server();
17
+ let server: Awaited<ReturnType<typeof initialise_server>>;
18
18
 
19
- beforeAll(() => server.listen());
19
+ beforeAll(async () => {
20
+ server = await initialise_server();
21
+ await server.start({ quiet: true });
22
+ });
20
23
  afterEach(() => server.resetHandlers());
21
- afterAll(() => server.close());
24
+ afterAll(() => server.stop());
22
25
 
23
26
  describe("open_stream", () => {
24
27
  let app: Client;
@@ -37,10 +40,10 @@ describe("open_stream", () => {
37
40
  vi.clearAllMocks();
38
41
  });
39
42
 
40
- it("should throw an error if config is not defined", () => {
43
+ it("should throw an error if config is not defined", async () => {
41
44
  app.config = undefined;
42
45
 
43
- expect(async () => {
46
+ await expect(async () => {
44
47
  await app.open_stream();
45
48
  }).rejects.toThrow("Could not resolve app config");
46
49
  });
@@ -60,22 +63,25 @@ describe("open_stream", () => {
60
63
  throw new Error("stream instance is not defined");
61
64
  }
62
65
 
63
- const onMessageCallback = app.stream_instance.onmessage.bind(app);
64
- const onErrorCallback = app.stream_instance.onerror.bind(app);
65
-
66
66
  const message = { msg: "hello jerry" };
67
67
 
68
- onMessageCallback({ data: JSON.stringify(message) });
68
+ app.stream_instance.onmessage({
69
+ data: JSON.stringify(message)
70
+ } as MessageEvent);
69
71
  expect(app.stream_status.open).toBe(true);
70
72
 
71
73
  expect(app.event_callbacks).toEqual({});
72
74
  expect(app.pending_stream_messages).toEqual({});
73
75
 
74
76
  const close_stream_message = { msg: "close_stream" };
75
- onMessageCallback({ data: JSON.stringify(close_stream_message) });
77
+ app.stream_instance.onmessage({
78
+ data: JSON.stringify(close_stream_message)
79
+ } as MessageEvent);
76
80
  expect(app.stream_status.open).toBe(false);
77
81
 
78
- onErrorCallback({ data: JSON.stringify("404") });
82
+ app.stream_instance.onerror({
83
+ data: JSON.stringify("404")
84
+ } as MessageEvent);
79
85
  expect(app.stream_status.open).toBe(false);
80
86
  });
81
87
  });
@@ -3,18 +3,21 @@ import { describe, it, expect, afterEach, beforeAll, afterAll } from "vitest";
3
3
  import { Client } from "..";
4
4
  import { initialise_server } from "./server";
5
5
 
6
- const server = initialise_server();
6
+ let server: Awaited<ReturnType<typeof initialise_server>>;
7
7
 
8
- beforeAll(() => server.listen());
8
+ beforeAll(async () => {
9
+ server = await initialise_server();
10
+ await server.start({ quiet: true });
11
+ });
9
12
  afterEach(() => server.resetHandlers());
10
- afterAll(() => server.close());
13
+ afterAll(() => server.stop());
11
14
 
12
15
  describe("upload_files", () => {
13
16
  it("should upload files successfully", async () => {
14
17
  const root_url = "https://hmb-hello-world.hf.space";
15
18
 
16
19
  const client = await Client.connect("hmb/hello_world", {
17
- hf_token: "hf_token"
20
+ token: "hf_token"
18
21
  });
19
22
 
20
23
  const files = [new Blob([], { type: "image/jpeg" })];
@@ -8,11 +8,14 @@ const app_reference = "hmb/hello_world";
8
8
  const secret_app_reference = "hmb/secret_world";
9
9
  const secret_direct_app_reference = "https://hmb-secret-world.hf.space";
10
10
 
11
- const server = initialise_server();
11
+ let server: Awaited<ReturnType<typeof initialise_server>>;
12
12
 
13
- beforeAll(() => server.listen());
13
+ beforeAll(async () => {
14
+ server = await initialise_server();
15
+ await server.start({ quiet: true });
16
+ });
14
17
  afterEach(() => server.resetHandlers());
15
- afterAll(() => server.close());
18
+ afterAll(() => server.stop());
16
19
 
17
20
  describe("view_api", () => {
18
21
  test("viewing the api of a running, public app", async () => {
package/src/types.ts CHANGED
@@ -402,6 +402,9 @@ export interface Status {
402
402
  changed_state_ids?: number[];
403
403
  time_limit?: number;
404
404
  session_not_found?: boolean;
405
+ used_cache?: "full" | "partial" | null;
406
+ cache_duration?: number;
407
+ avg_time?: number;
405
408
  }
406
409
 
407
410
  export interface StatusMessage extends Status {