@gradio/client 0.16.0 → 0.18.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 (84) hide show
  1. package/CHANGELOG.md +57 -0
  2. package/README.md +49 -43
  3. package/dist/client.d.ts +63 -70
  4. package/dist/client.d.ts.map +1 -1
  5. package/dist/constants.d.ts +23 -0
  6. package/dist/constants.d.ts.map +1 -0
  7. package/dist/helpers/api_info.d.ts +25 -0
  8. package/dist/helpers/api_info.d.ts.map +1 -0
  9. package/dist/helpers/data.d.ts +8 -0
  10. package/dist/helpers/data.d.ts.map +1 -0
  11. package/dist/{utils.d.ts → helpers/init_helpers.d.ts} +6 -18
  12. package/dist/helpers/init_helpers.d.ts.map +1 -0
  13. package/dist/helpers/spaces.d.ts +7 -0
  14. package/dist/helpers/spaces.d.ts.map +1 -0
  15. package/dist/index.d.ts +8 -4
  16. package/dist/index.d.ts.map +1 -1
  17. package/dist/index.js +1628 -1398
  18. package/dist/test/handlers.d.ts +3 -0
  19. package/dist/test/handlers.d.ts.map +1 -0
  20. package/dist/test/mock_eventsource.d.ts +2 -0
  21. package/dist/test/mock_eventsource.d.ts.map +1 -0
  22. package/dist/test/server.d.ts +2 -0
  23. package/dist/test/server.d.ts.map +1 -0
  24. package/dist/test/test_data.d.ts +76 -0
  25. package/dist/test/test_data.d.ts.map +1 -0
  26. package/dist/types.d.ts +153 -49
  27. package/dist/types.d.ts.map +1 -1
  28. package/dist/upload.d.ts +2 -2
  29. package/dist/upload.d.ts.map +1 -1
  30. package/dist/utils/duplicate.d.ts +4 -0
  31. package/dist/utils/duplicate.d.ts.map +1 -0
  32. package/dist/utils/handle_blob.d.ts +4 -0
  33. package/dist/utils/handle_blob.d.ts.map +1 -0
  34. package/dist/utils/post_data.d.ts +4 -0
  35. package/dist/utils/post_data.d.ts.map +1 -0
  36. package/dist/utils/predict.d.ts +4 -0
  37. package/dist/utils/predict.d.ts.map +1 -0
  38. package/dist/utils/stream.d.ts +8 -0
  39. package/dist/utils/stream.d.ts.map +1 -0
  40. package/dist/utils/submit.d.ts +4 -0
  41. package/dist/utils/submit.d.ts.map +1 -0
  42. package/dist/utils/upload_files.d.ts +4 -0
  43. package/dist/utils/upload_files.d.ts.map +1 -0
  44. package/dist/utils/view_api.d.ts +3 -0
  45. package/dist/utils/view_api.d.ts.map +1 -0
  46. package/dist/{wrapper-6f348d45.js → wrapper-CviSselG.js} +259 -17
  47. package/package.json +10 -3
  48. package/src/client.ts +314 -1691
  49. package/src/constants.ts +27 -0
  50. package/src/globals.d.ts +2 -21
  51. package/src/helpers/api_info.ts +300 -0
  52. package/src/helpers/data.ts +133 -0
  53. package/src/helpers/init_helpers.ts +130 -0
  54. package/src/helpers/spaces.ts +197 -0
  55. package/src/index.ts +16 -10
  56. package/src/test/api_info.test.ts +456 -0
  57. package/src/test/data.test.ts +281 -0
  58. package/src/test/handlers.ts +438 -0
  59. package/src/test/init.test.ts +139 -0
  60. package/src/test/init_helpers.test.ts +94 -0
  61. package/src/test/mock_eventsource.ts +11 -0
  62. package/src/test/post_data.test.ts +45 -0
  63. package/src/test/server.ts +6 -0
  64. package/src/test/spaces.test.ts +145 -0
  65. package/src/test/stream.test.ts +67 -0
  66. package/src/test/test_data.ts +557 -0
  67. package/src/test/upload_files.test.ts +42 -0
  68. package/src/test/view_api.test.ts +53 -0
  69. package/src/types.ts +201 -59
  70. package/src/upload.ts +19 -15
  71. package/src/utils/duplicate.ts +104 -0
  72. package/src/utils/handle_blob.ts +47 -0
  73. package/src/utils/post_data.ts +37 -0
  74. package/src/utils/predict.ts +56 -0
  75. package/src/utils/stream.ts +175 -0
  76. package/src/utils/submit.ts +697 -0
  77. package/src/utils/upload_files.ts +51 -0
  78. package/src/utils/view_api.ts +67 -0
  79. package/src/vite-env.d.ts +1 -0
  80. package/tsconfig.json +15 -2
  81. package/vite.config.js +11 -17
  82. package/dist/utils.d.ts.map +0 -1
  83. package/src/client.node-test.ts +0 -172
  84. package/src/utils.ts +0 -314
@@ -0,0 +1,139 @@
1
+ import {
2
+ describe,
3
+ beforeAll,
4
+ afterEach,
5
+ afterAll,
6
+ test,
7
+ expect,
8
+ vi
9
+ } from "vitest";
10
+
11
+ import { Client, client, duplicate } from "..";
12
+ import { transformed_api_info, config_response } from "./test_data";
13
+ import { initialise_server } from "./server";
14
+ import { CONFIG_ERROR_MSG, SPACE_METADATA_ERROR_MSG } from "../constants";
15
+
16
+ const app_reference = "hmb/hello_world";
17
+ const broken_app_reference = "hmb/bye_world";
18
+ const direct_app_reference = "https://hmb-hello-world.hf.space";
19
+ const secret_direct_app_reference = "https://hmb-secret-world.hf.space";
20
+
21
+ const server = initialise_server();
22
+
23
+ beforeAll(() => server.listen());
24
+ afterEach(() => server.resetHandlers());
25
+ afterAll(() => server.close());
26
+
27
+ describe("Client class", () => {
28
+ describe("initialisation", () => {
29
+ test("backwards compatibility of client using deprecated syntax", async () => {
30
+ const app = await client(app_reference);
31
+ expect(app.config).toEqual(config_response);
32
+ });
33
+ test("connecting to a running app with a space reference", async () => {
34
+ const app = await Client.connect(app_reference);
35
+ expect(app.config).toEqual(config_response);
36
+ });
37
+
38
+ test("connecting to a running app with a direct app URL", async () => {
39
+ const app = await Client.connect(direct_app_reference);
40
+ expect(app.config).toEqual(config_response);
41
+ });
42
+
43
+ test("connecting successfully to a private running app with a space reference", async () => {
44
+ const app = await Client.connect("hmb/secret_world", {
45
+ hf_token: "hf_123"
46
+ });
47
+
48
+ expect(app.config).toEqual({
49
+ ...config_response,
50
+ root: "https://hmb-secret-world.hf.space"
51
+ });
52
+ });
53
+
54
+ test("connecting successfully to a private running app with a direct app URL ", async () => {
55
+ const app = await Client.connect(secret_direct_app_reference, {
56
+ hf_token: "hf_123"
57
+ });
58
+
59
+ expect(app.config).toEqual({
60
+ ...config_response,
61
+ root: "https://hmb-secret-world.hf.space"
62
+ });
63
+ });
64
+
65
+ test("unsuccessfully attempting to connect to a private running app", async () => {
66
+ await expect(
67
+ Client.connect("hmb/secret_world", {
68
+ hf_token: "hf_bad_token"
69
+ })
70
+ ).rejects.toThrow(CONFIG_ERROR_MSG);
71
+ });
72
+
73
+ test("viewing the api info of a running app", async () => {
74
+ const app = await Client.connect(app_reference);
75
+ expect(await app.view_api()).toEqual(transformed_api_info);
76
+ });
77
+
78
+ test("viewing the api info of a non-existent app", async () => {
79
+ const app = Client.connect(broken_app_reference);
80
+ await expect(app).rejects.toThrow(CONFIG_ERROR_MSG);
81
+ });
82
+ });
83
+
84
+ describe("duplicate", () => {
85
+ test("backwards compatibility of duplicate using deprecated syntax", async () => {
86
+ const app = await duplicate("gradio/hello_world", {
87
+ hf_token: "hf_123",
88
+ private: true,
89
+ hardware: "cpu-basic"
90
+ });
91
+
92
+ expect(app.config).toEqual(config_response);
93
+ });
94
+
95
+ test("creating a duplicate of a running app", async () => {
96
+ const duplicate = await Client.duplicate("gradio/hello_world", {
97
+ hf_token: "hf_123",
98
+ private: true,
99
+ hardware: "cpu-basic"
100
+ });
101
+
102
+ expect(duplicate.config).toEqual(config_response);
103
+ });
104
+
105
+ test("creating a duplicate of a running app without a token", async () => {
106
+ const duplicate = Client.duplicate("gradio/hello_world", {
107
+ private: true,
108
+ hardware: "cpu-basic"
109
+ });
110
+
111
+ await expect(duplicate).rejects.toThrow("Error: Unauthorized");
112
+ });
113
+
114
+ test("creating a duplicate of a broken app", async () => {
115
+ const duplicate = Client.duplicate(broken_app_reference);
116
+
117
+ await expect(duplicate).rejects.toThrow(SPACE_METADATA_ERROR_MSG);
118
+ });
119
+ });
120
+
121
+ describe("overriding the Client class", () => {
122
+ test("overriding methods on the Client class", async () => {
123
+ const mocked_fetch = vi.fn(
124
+ (input: RequestInfo | URL, init?: RequestInit): Promise<Response> => {
125
+ return Promise.resolve(
126
+ new Response(JSON.stringify({ data: "test" }))
127
+ );
128
+ }
129
+ );
130
+
131
+ class CustomClient extends Client {
132
+ fetch = mocked_fetch;
133
+ }
134
+
135
+ await CustomClient.connect("hmb/hello_world");
136
+ expect(mocked_fetch).toHaveBeenCalled();
137
+ });
138
+ });
139
+ });
@@ -0,0 +1,94 @@
1
+ import {
2
+ resolve_root,
3
+ get_jwt,
4
+ determine_protocol
5
+ } from "../helpers/init_helpers";
6
+ import { initialise_server } from "./server";
7
+ import { beforeAll, afterEach, afterAll, it, expect, describe } from "vitest";
8
+
9
+ const server = initialise_server();
10
+
11
+ beforeAll(() => server.listen());
12
+ afterEach(() => server.resetHandlers());
13
+ afterAll(() => server.close());
14
+
15
+ describe("resolve_root", () => {
16
+ it('should return the base URL if the root path starts with "http://"', () => {
17
+ const base_url = "https://huggingface.co";
18
+ const root_path = "https://hmb-hello-world.hf.space";
19
+ const prioritize_base = true;
20
+ const result = resolve_root(base_url, root_path, prioritize_base);
21
+ expect(result).toBe(base_url);
22
+ });
23
+
24
+ it('should return the base URL if the root path starts with "https://"', () => {
25
+ const base_url = "https://huggingface.co";
26
+ const root_path = "https://hmb-hello-world.hf.space";
27
+ const prioritize_base = true;
28
+ const result = resolve_root(base_url, root_path, prioritize_base);
29
+ expect(result).toBe(base_url);
30
+ });
31
+ });
32
+
33
+ describe("get_jwt", () => {
34
+ it("should return a valid JWT token when the API call is successful", async () => {
35
+ const space = "hmb/hello_world";
36
+ const token = "hf_123";
37
+ const expected_jwt = "jwt_123";
38
+
39
+ const result = await get_jwt(space, token);
40
+
41
+ expect(result).toBe(expected_jwt);
42
+ });
43
+
44
+ it("should return false when the API call fails", async () => {
45
+ const space = "hmb/bye_world";
46
+ const token = "hf_123";
47
+
48
+ const result = await get_jwt(space, token);
49
+
50
+ expect(result).toBe(false);
51
+ });
52
+ });
53
+
54
+ describe("determine_protocol", () => {
55
+ it('should return the correct protocols and host when the endpoint starts with "http"', () => {
56
+ const endpoint = "http://huggingface.co";
57
+ const result = determine_protocol(endpoint);
58
+ expect(result).toEqual({
59
+ ws_protocol: "ws",
60
+ http_protocol: "http:",
61
+ host: "huggingface.co"
62
+ });
63
+ });
64
+
65
+ it('should return the correct protocols and host when the endpoint starts with "https"', () => {
66
+ const endpoint = "https://huggingface.co";
67
+ const result = determine_protocol(endpoint);
68
+ expect(result).toEqual({
69
+ ws_protocol: "wss",
70
+ http_protocol: "https:",
71
+ host: "huggingface.co"
72
+ });
73
+ });
74
+
75
+ it('should return the correct protocols and host when the endpoint starts with "file"', () => {
76
+ const endpoint = "file:///path/to/app.html";
77
+ const result = determine_protocol(endpoint);
78
+ expect(result).toEqual({
79
+ ws_protocol: "ws",
80
+ http_protocol: "http:",
81
+ host: "lite.local"
82
+ });
83
+ });
84
+
85
+ it('should return the default protocols and host when the endpoint does not start with "http" or "file"', () => {
86
+ const endpoint = "huggingface.co";
87
+ const result = determine_protocol(endpoint);
88
+ expect(result).toEqual({
89
+ ws_protocol: "wss",
90
+ http_protocol: "https:",
91
+ host: "huggingface.co"
92
+ });
93
+ });
94
+ });
@@ -0,0 +1,11 @@
1
+ import { vi } from "vitest";
2
+
3
+ Object.defineProperty(window, "EventSource", {
4
+ writable: true,
5
+ value: vi.fn().mockImplementation(() => ({
6
+ close: vi.fn(() => {}),
7
+ addEventListener: vi.fn(),
8
+ onmessage: vi.fn((_event: MessageEvent) => {}),
9
+ onerror: vi.fn((_event: Event) => {})
10
+ }))
11
+ });
@@ -0,0 +1,45 @@
1
+ import { Client } from "../client";
2
+
3
+ import { initialise_server } from "./server";
4
+ import { BROKEN_CONNECTION_MSG } from "../constants";
5
+ const server = initialise_server();
6
+ import { beforeAll, afterEach, afterAll, it, expect, describe } from "vitest";
7
+
8
+ beforeAll(() => server.listen());
9
+ afterEach(() => server.resetHandlers());
10
+ afterAll(() => server.close());
11
+
12
+ describe("post_data", () => {
13
+ it("should send a POST request with the correct headers and body", async () => {
14
+ const app = await Client.connect("hmb/hello_world");
15
+ const config = app.config;
16
+ const url = config?.root;
17
+ const body = { data: "test" };
18
+
19
+ if (!url) {
20
+ throw new Error("No URL provided");
21
+ }
22
+
23
+ const [response, status] = await app.post_data(url, body);
24
+
25
+ expect(response).toEqual({});
26
+ expect(status).toBe(200);
27
+ });
28
+
29
+ it("should handle network errors", async () => {
30
+ const app = await Client.connect("hmb/secret_world", {
31
+ hf_token: "hf_123"
32
+ });
33
+
34
+ const url = "https://hmb-secret-world.hf.space";
35
+
36
+ if (!url) {
37
+ throw new Error("No URL provided");
38
+ }
39
+
40
+ const [response, status] = await app.post_data(url, {});
41
+
42
+ expect(response).toEqual(BROKEN_CONNECTION_MSG);
43
+ expect(status).toBe(500);
44
+ });
45
+ });
@@ -0,0 +1,6 @@
1
+ import { setupServer } from "msw/node";
2
+ import { handlers } from "./handlers";
3
+
4
+ export function initialise_server(): any {
5
+ return setupServer(...handlers);
6
+ }
@@ -0,0 +1,145 @@
1
+ import { SPACE_STATUS_ERROR_MSG } from "../constants";
2
+ import {
3
+ discussions_enabled,
4
+ get_space_hardware,
5
+ set_space_timeout,
6
+ check_space_status
7
+ } from "../helpers/spaces";
8
+ import { beforeAll, afterEach, afterAll, it, expect, describe } from "vitest";
9
+
10
+ import { initialise_server } from "./server";
11
+ import { hardware_sleeptime_response } from "./test_data";
12
+ import { vi } from "vitest";
13
+
14
+ const server = initialise_server();
15
+
16
+ beforeAll(() => server.listen());
17
+ afterEach(() => server.resetHandlers());
18
+ afterAll(() => server.close());
19
+
20
+ describe("set_space_timeout", () => {
21
+ it("should set the sleep timeout for a space", async () => {
22
+ const space_id = "hmb/hello_world";
23
+ const timeout = 60;
24
+ const hf_token = "hf_123";
25
+
26
+ const response = await set_space_timeout(space_id, timeout, hf_token);
27
+
28
+ expect(response).toEqual(hardware_sleeptime_response);
29
+ });
30
+
31
+ it("should throw an error if the fetch call fails", async () => {
32
+ const space_id = "hmb/server_test";
33
+ const timeout = 60;
34
+ const hf_token = "hf_123";
35
+
36
+ await expect(
37
+ set_space_timeout(space_id, timeout, hf_token)
38
+ ).rejects.toThrow(
39
+ "Could not set sleep timeout on duplicated Space. Please visit *ADD HF LINK TO SETTINGS* to set a timeout manually to reduce billing charges."
40
+ );
41
+ });
42
+ });
43
+
44
+ describe("get_space_hardware", () => {
45
+ it("should return the current hardware for a space", async () => {
46
+ const space_id = "hmb/hello_world";
47
+ const hf_token = "hf_123";
48
+
49
+ const hardware = await get_space_hardware(space_id, hf_token);
50
+ expect(hardware).toEqual(hardware_sleeptime_response.hardware.current);
51
+ });
52
+
53
+ it("should throw an error if the fetch call fails", async () => {
54
+ const space_id = "hmb/bye_world";
55
+
56
+ await expect(get_space_hardware(space_id)).rejects.toThrow(
57
+ "Space hardware could not be obtained."
58
+ );
59
+ });
60
+ });
61
+
62
+ describe("discussions_enabled", () => {
63
+ it("should return true if discussions are enabled for the space", async () => {
64
+ const space_id = "hmb/hello_world";
65
+ const result = await discussions_enabled(space_id);
66
+ expect(result).toBe(true);
67
+ });
68
+
69
+ it("should return false if discussions are disabled for the space", async () => {
70
+ const space_id = "hmb/bye_world";
71
+ const result = await discussions_enabled(space_id);
72
+ expect(result).toBe(false);
73
+ });
74
+ });
75
+
76
+ describe("check_space_status", () => {
77
+ const status_callback = vi.fn();
78
+
79
+ it("should handle a successful response with RUNNING stage", async () => {
80
+ const id = "hmb/hello_world";
81
+ const type = "space_name";
82
+
83
+ await check_space_status(id, type, status_callback);
84
+ expect(status_callback).toHaveBeenCalledWith({
85
+ status: "running",
86
+ load_status: "complete",
87
+ message: "",
88
+ detail: "RUNNING"
89
+ });
90
+ });
91
+
92
+ it("should handle a successful response with PAUSED stage", async () => {
93
+ const id = "hmb/paused_space";
94
+ const type = "space_name";
95
+
96
+ await check_space_status(id, type, status_callback);
97
+ expect(status_callback).toHaveBeenCalledWith({
98
+ status: "paused",
99
+ load_status: "error",
100
+ message:
101
+ "This space has been paused by the author. If you would like to try this demo, consider duplicating the space.",
102
+ detail: "PAUSED",
103
+ discussions_enabled: true
104
+ });
105
+ });
106
+
107
+ it("should handle a successful response with BUILDING stage", async () => {
108
+ const id = "hmb/building_space";
109
+ const type = "space_name";
110
+
111
+ await check_space_status(id, type, status_callback);
112
+ expect(status_callback).toHaveBeenCalledWith({
113
+ status: "building",
114
+ load_status: "pending",
115
+ message: "Space is building...",
116
+ detail: "BUILDING"
117
+ });
118
+ });
119
+
120
+ it("should handle a successful response with STOPPED stage", async () => {
121
+ const id = "hmb/stopped_space";
122
+ const type = "space_name";
123
+
124
+ await check_space_status(id, type, status_callback);
125
+ expect(status_callback).toHaveBeenCalledWith({
126
+ status: "sleeping",
127
+ load_status: "pending",
128
+ message: "Space is asleep. Waking it up...",
129
+ detail: "STOPPED"
130
+ });
131
+ });
132
+
133
+ it("should handle a failed response", async () => {
134
+ const id = "hmb/failed_space";
135
+ const type = "space_name";
136
+
137
+ await check_space_status(id, type, status_callback);
138
+ expect(status_callback).toHaveBeenCalledWith({
139
+ status: "error",
140
+ load_status: "error",
141
+ message: SPACE_STATUS_ERROR_MSG,
142
+ detail: "NOT_FOUND"
143
+ });
144
+ });
145
+ });
@@ -0,0 +1,67 @@
1
+ import { vi } from "vitest";
2
+ import { Client } from "../client";
3
+ import { initialise_server } from "./server";
4
+
5
+ import { describe, it, expect, afterEach } from "vitest";
6
+ import "./mock_eventsource.ts";
7
+
8
+ const server = initialise_server();
9
+
10
+ beforeAll(() => server.listen());
11
+ afterEach(() => server.resetHandlers());
12
+ afterAll(() => server.close());
13
+
14
+ describe("open_stream", () => {
15
+ let mock_eventsource: any;
16
+ let app: any;
17
+
18
+ beforeEach(async () => {
19
+ app = await Client.connect("hmb/hello_world");
20
+ app.stream_factory = vi.fn().mockImplementation(() => {
21
+ mock_eventsource = new EventSource("");
22
+ return mock_eventsource;
23
+ });
24
+ });
25
+
26
+ afterEach(() => {
27
+ vi.clearAllMocks();
28
+ });
29
+
30
+ it("should throw an error if config is not defined", () => {
31
+ app.config = undefined;
32
+
33
+ expect(() => {
34
+ app.open_stream();
35
+ }).toThrow("Could not resolve app config");
36
+ });
37
+
38
+ it("should connect to the SSE endpoint and handle messages", async () => {
39
+ app.open_stream();
40
+
41
+ const eventsource_mock_call = app.stream_factory.mock.calls[0][0];
42
+
43
+ expect(eventsource_mock_call.href).toMatch(
44
+ /https:\/\/hmb-hello-world\.hf\.space\/queue\/data\?session_hash/
45
+ );
46
+
47
+ expect(app.stream_factory).toHaveBeenCalledWith(eventsource_mock_call);
48
+
49
+ const onMessageCallback = mock_eventsource.onmessage;
50
+ const onErrorCallback = mock_eventsource.onerror;
51
+
52
+ const message = { msg: "hello jerry" };
53
+
54
+ onMessageCallback({ data: JSON.stringify(message) });
55
+ expect(app.stream_status.open).toBe(true);
56
+
57
+ expect(app.event_callbacks).toEqual({});
58
+ expect(app.pending_stream_messages).toEqual({});
59
+
60
+ const close_stream_message = { msg: "close_stream" };
61
+ onMessageCallback({ data: JSON.stringify(close_stream_message) });
62
+ expect(app.stream_status.open).toBe(false);
63
+
64
+ onErrorCallback({ data: JSON.stringify("404") });
65
+ expect(app.stream_status.open).toBe(false);
66
+ });
67
+ });