@gradio/client 0.19.3 → 0.20.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.
- package/CHANGELOG.md +20 -0
- package/README.md +8 -1
- package/dist/client.d.ts +4 -0
- package/dist/client.d.ts.map +1 -1
- package/dist/constants.d.ts +4 -0
- package/dist/constants.d.ts.map +1 -1
- package/dist/helpers/api_info.d.ts +1 -0
- package/dist/helpers/api_info.d.ts.map +1 -1
- package/dist/helpers/data.d.ts.map +1 -1
- package/dist/helpers/init_helpers.d.ts +4 -1
- package/dist/helpers/init_helpers.d.ts.map +1 -1
- package/dist/index.js +236 -75
- package/dist/test/handlers.d.ts.map +1 -1
- package/dist/test/test_data.d.ts.map +1 -1
- package/dist/types.d.ts +6 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/duplicate.d.ts.map +1 -1
- package/dist/utils/post_data.d.ts.map +1 -1
- package/dist/utils/predict.d.ts.map +1 -1
- package/dist/utils/submit.d.ts.map +1 -1
- package/dist/utils/upload_files.d.ts.map +1 -1
- package/dist/utils/view_api.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/client.ts +70 -28
- package/src/constants.ts +5 -0
- package/src/helpers/api_info.ts +44 -17
- package/src/helpers/data.ts +9 -22
- package/src/helpers/init_helpers.ts +98 -9
- package/src/test/api_info.test.ts +69 -4
- package/src/test/data.test.ts +13 -16
- package/src/test/handlers.ts +249 -2
- package/src/test/init.test.ts +2 -2
- package/src/test/init_helpers.test.ts +53 -1
- package/src/test/test_data.ts +3 -0
- package/src/types.ts +6 -0
- package/src/utils/duplicate.ts +27 -2
- package/src/utils/post_data.ts +2 -1
- package/src/utils/predict.ts +4 -2
- package/src/utils/submit.ts +42 -9
- package/src/utils/upload_files.ts +2 -1
- package/src/utils/view_api.ts +7 -4
@@ -1,16 +1,22 @@
|
|
1
|
-
import {
|
1
|
+
import {
|
2
|
+
INVALID_URL_MSG,
|
3
|
+
QUEUE_FULL_MSG,
|
4
|
+
SPACE_METADATA_ERROR_MSG
|
5
|
+
} from "../constants";
|
2
6
|
import { beforeAll, afterEach, afterAll, it, expect, describe } from "vitest";
|
3
7
|
import {
|
4
8
|
handle_message,
|
5
9
|
get_description,
|
6
10
|
get_type,
|
7
11
|
process_endpoint,
|
12
|
+
join_urls,
|
8
13
|
map_data_to_params
|
9
14
|
} from "../helpers/api_info";
|
10
15
|
import { initialise_server } from "./server";
|
11
16
|
import { transformed_api_info } from "./test_data";
|
12
17
|
|
13
18
|
const server = initialise_server();
|
19
|
+
const IS_NODE = process.env.TEST_MODE === "node";
|
14
20
|
|
15
21
|
beforeAll(() => server.listen());
|
16
22
|
afterEach(() => server.resetHandlers());
|
@@ -435,9 +441,7 @@ describe("process_endpoint", () => {
|
|
435
441
|
try {
|
436
442
|
await process_endpoint(app_reference, hf_token);
|
437
443
|
} catch (error) {
|
438
|
-
expect(error.message).toEqual(
|
439
|
-
SPACE_METADATA_ERROR_MSG + "Unexpected end of JSON input"
|
440
|
-
);
|
444
|
+
expect(error.message).toEqual(SPACE_METADATA_ERROR_MSG);
|
441
445
|
}
|
442
446
|
});
|
443
447
|
|
@@ -455,6 +459,67 @@ describe("process_endpoint", () => {
|
|
455
459
|
const result = await process_endpoint("hmb/hello_world");
|
456
460
|
expect(result).toEqual(expected);
|
457
461
|
});
|
462
|
+
|
463
|
+
it("processes local server URLs correctly", async () => {
|
464
|
+
const local_url = "http://localhost:7860/gradio";
|
465
|
+
const response_local_url = await process_endpoint(local_url);
|
466
|
+
expect(response_local_url.space_id).toBe(false);
|
467
|
+
expect(response_local_url.host).toBe("localhost:7860/gradio");
|
468
|
+
|
469
|
+
const local_url_2 = "http://localhost:7860/gradio/";
|
470
|
+
const response_local_url_2 = await process_endpoint(local_url_2);
|
471
|
+
expect(response_local_url_2.space_id).toBe(false);
|
472
|
+
expect(response_local_url_2.host).toBe("localhost:7860/gradio");
|
473
|
+
});
|
474
|
+
|
475
|
+
it("handles hugging face space references", async () => {
|
476
|
+
const space_id = "hmb/hello_world";
|
477
|
+
|
478
|
+
const response = await process_endpoint(space_id);
|
479
|
+
expect(response.space_id).toBe(space_id);
|
480
|
+
expect(response.host).toContain("hf.space");
|
481
|
+
});
|
482
|
+
|
483
|
+
it("handles hugging face domain URLs", async () => {
|
484
|
+
const app_reference = "https://hmb-hello-world.hf.space/";
|
485
|
+
const response = await process_endpoint(app_reference);
|
486
|
+
expect(response.space_id).toBe("hmb-hello-world");
|
487
|
+
expect(response.host).toBe("hmb-hello-world.hf.space");
|
488
|
+
});
|
489
|
+
});
|
490
|
+
|
491
|
+
describe("join_urls", () => {
|
492
|
+
it("joins URLs correctly", () => {
|
493
|
+
expect(join_urls("http://localhost:7860", "/gradio")).toBe(
|
494
|
+
"http://localhost:7860/gradio"
|
495
|
+
);
|
496
|
+
expect(join_urls("http://localhost:7860/", "/gradio")).toBe(
|
497
|
+
"http://localhost:7860/gradio"
|
498
|
+
);
|
499
|
+
expect(join_urls("http://localhost:7860", "app/", "/gradio")).toBe(
|
500
|
+
"http://localhost:7860/app/gradio"
|
501
|
+
);
|
502
|
+
expect(join_urls("http://localhost:7860/", "/app/", "/gradio/")).toBe(
|
503
|
+
"http://localhost:7860/app/gradio/"
|
504
|
+
);
|
505
|
+
|
506
|
+
expect(join_urls("http://127.0.0.1:8000/app", "/config")).toBe(
|
507
|
+
"http://127.0.0.1:8000/app/config"
|
508
|
+
);
|
509
|
+
|
510
|
+
expect(join_urls("http://127.0.0.1:8000/app/gradio", "/config")).toBe(
|
511
|
+
"http://127.0.0.1:8000/app/gradio/config"
|
512
|
+
);
|
513
|
+
});
|
514
|
+
it("throws an error when the URLs are not valid", () => {
|
515
|
+
expect(() => join_urls("localhost:7860", "/gradio")).toThrowError(
|
516
|
+
INVALID_URL_MSG
|
517
|
+
);
|
518
|
+
|
519
|
+
expect(() => join_urls("localhost:7860", "/gradio", "app")).toThrowError(
|
520
|
+
INVALID_URL_MSG
|
521
|
+
);
|
522
|
+
});
|
458
523
|
});
|
459
524
|
|
460
525
|
describe("map_data_params", () => {
|
package/src/test/data.test.ts
CHANGED
@@ -43,6 +43,15 @@ describe("walk_and_store_blobs", () => {
|
|
43
43
|
expect(parts[0].blob).toBe(false);
|
44
44
|
});
|
45
45
|
|
46
|
+
it("should handle arrays", async () => {
|
47
|
+
const image = new Blob([]);
|
48
|
+
const parts = await walk_and_store_blobs([image]);
|
49
|
+
|
50
|
+
expect(parts).toHaveLength(1);
|
51
|
+
expect(parts[0].blob).toBeInstanceOf(NodeBlob);
|
52
|
+
expect(parts[0].path).toEqual(["0"]);
|
53
|
+
});
|
54
|
+
|
46
55
|
it("should handle deep structures", async () => {
|
47
56
|
const image = new Blob([]);
|
48
57
|
const parts = await walk_and_store_blobs({ a: { b: { data: { image } } } });
|
@@ -142,18 +151,6 @@ describe("walk_and_store_blobs", () => {
|
|
142
151
|
expect(parts[0].path).toEqual(["a", "b", "data", "image"]);
|
143
152
|
expect(parts[0].blob).toBeInstanceOf(NodeBlob);
|
144
153
|
});
|
145
|
-
|
146
|
-
it("should convert an object with primitive values to BlobRefs", async () => {
|
147
|
-
const param = {
|
148
|
-
test: "test"
|
149
|
-
};
|
150
|
-
const parts = await walk_and_store_blobs(param);
|
151
|
-
|
152
|
-
expect(parts).toHaveLength(1);
|
153
|
-
expect(parts[0].path).toEqual([]);
|
154
|
-
expect(parts[0].blob).toBeInstanceOf(NodeBlob);
|
155
|
-
expect(parts[0].type).toEqual("object");
|
156
|
-
});
|
157
154
|
});
|
158
155
|
describe("update_object", () => {
|
159
156
|
it("should update the value of a nested property", () => {
|
@@ -198,7 +195,7 @@ describe("skip_queue", () => {
|
|
198
195
|
|
199
196
|
it("should not skip queue when global and dependency queue is enabled", () => {
|
200
197
|
config.enable_queue = true;
|
201
|
-
config.dependencies
|
198
|
+
config.dependencies.find((dep) => dep.id === id)!.queue = true;
|
202
199
|
|
203
200
|
const result = skip_queue(id, config_response);
|
204
201
|
|
@@ -207,7 +204,7 @@ describe("skip_queue", () => {
|
|
207
204
|
|
208
205
|
it("should not skip queue when global queue is disabled and dependency queue is enabled", () => {
|
209
206
|
config.enable_queue = false;
|
210
|
-
config.dependencies
|
207
|
+
config.dependencies.find((dep) => dep.id === id)!.queue = true;
|
211
208
|
|
212
209
|
const result = skip_queue(id, config_response);
|
213
210
|
|
@@ -216,7 +213,7 @@ describe("skip_queue", () => {
|
|
216
213
|
|
217
214
|
it("should should skip queue when global queue and dependency queue is disabled", () => {
|
218
215
|
config.enable_queue = false;
|
219
|
-
config.dependencies
|
216
|
+
config.dependencies.find((dep) => dep.id === id)!.queue = false;
|
220
217
|
|
221
218
|
const result = skip_queue(id, config_response);
|
222
219
|
|
@@ -225,7 +222,7 @@ describe("skip_queue", () => {
|
|
225
222
|
|
226
223
|
it("should should skip queue when global queue is enabled and dependency queue is disabled", () => {
|
227
224
|
config.enable_queue = true;
|
228
|
-
config.dependencies
|
225
|
+
config.dependencies.find((dep) => dep.id === id)!.queue = false;
|
229
226
|
|
230
227
|
const result = skip_queue(id, config_response);
|
231
228
|
|
package/src/test/handlers.ts
CHANGED
@@ -6,7 +6,8 @@ import {
|
|
6
6
|
RUNTIME_URL,
|
7
7
|
SLEEPTIME_URL,
|
8
8
|
UPLOAD_URL,
|
9
|
-
BROKEN_CONNECTION_MSG
|
9
|
+
BROKEN_CONNECTION_MSG,
|
10
|
+
LOGIN_URL
|
10
11
|
} from "../constants";
|
11
12
|
import {
|
12
13
|
response_api_info,
|
@@ -22,16 +23,24 @@ const root_url = "https://huggingface.co";
|
|
22
23
|
|
23
24
|
const direct_space_url = "https://hmb-hello-world.hf.space";
|
24
25
|
const private_space_url = "https://hmb-secret-world.hf.space";
|
26
|
+
const private_auth_space_url = "https://hmb-private-auth-space.hf.space";
|
25
27
|
|
26
28
|
const server_error_space_url = "https://hmb-server-error.hf.space";
|
27
29
|
const upload_server_test_space_url = "https://hmb-server-test.hf.space";
|
28
|
-
const
|
30
|
+
const auth_app_space_url = "https://hmb-auth-space.hf.space";
|
31
|
+
const unauth_app_space_url = "https://hmb-unauth-space.hf.space";
|
32
|
+
const invalid_auth_space_url = "https://hmb-invalid-auth-space.hf.space";
|
29
33
|
|
34
|
+
const server_error_reference = "hmb/server_error";
|
30
35
|
const app_reference = "hmb/hello_world";
|
31
36
|
const broken_app_reference = "hmb/bye_world";
|
32
37
|
const duplicate_app_reference = "gradio/hello_world";
|
33
38
|
const private_app_reference = "hmb/secret_world";
|
34
39
|
const server_test_app_reference = "hmb/server_test";
|
40
|
+
const auth_app_reference = "hmb/auth_space";
|
41
|
+
const unauth_app_reference = "hmb/unauth_space";
|
42
|
+
const invalid_auth_app_reference = "hmb/invalid_auth_space";
|
43
|
+
const private_auth_app_reference = "hmb/private_auth_space";
|
35
44
|
|
36
45
|
export const handlers: RequestHandler[] = [
|
37
46
|
// /host requests
|
@@ -58,6 +67,23 @@ export const handlers: RequestHandler[] = [
|
|
58
67
|
}
|
59
68
|
});
|
60
69
|
}),
|
70
|
+
http.get(
|
71
|
+
`${root_url}/api/spaces/${private_auth_app_reference}/${HOST_URL}`,
|
72
|
+
() => {
|
73
|
+
return new HttpResponse(
|
74
|
+
JSON.stringify({
|
75
|
+
subdomain: "hmb-private-auth-space",
|
76
|
+
host: "https://hmb-private-auth-space.hf.space"
|
77
|
+
}),
|
78
|
+
{
|
79
|
+
status: 200,
|
80
|
+
headers: {
|
81
|
+
"Content-Type": "application/json"
|
82
|
+
}
|
83
|
+
}
|
84
|
+
);
|
85
|
+
}
|
86
|
+
),
|
61
87
|
http.get(
|
62
88
|
`${root_url}/api/spaces/${private_app_reference}/${HOST_URL}`,
|
63
89
|
({ request }) => {
|
@@ -120,6 +146,68 @@ export const handlers: RequestHandler[] = [
|
|
120
146
|
);
|
121
147
|
}
|
122
148
|
),
|
149
|
+
http.get(`${root_url}/api/spaces/${auth_app_reference}/${HOST_URL}`, () => {
|
150
|
+
return new HttpResponse(
|
151
|
+
JSON.stringify({
|
152
|
+
subdomain: "hmb-auth-space",
|
153
|
+
host: "https://hmb-auth-space.hf.space"
|
154
|
+
}),
|
155
|
+
{
|
156
|
+
status: 200,
|
157
|
+
headers: {
|
158
|
+
"Content-Type": "application/json"
|
159
|
+
}
|
160
|
+
}
|
161
|
+
);
|
162
|
+
}),
|
163
|
+
http.get(
|
164
|
+
`${root_url}/api/spaces/${invalid_auth_app_reference}/${HOST_URL}`,
|
165
|
+
() => {
|
166
|
+
return new HttpResponse(
|
167
|
+
JSON.stringify({
|
168
|
+
subdomain: "hmb-invalid-auth-space",
|
169
|
+
host: "https://hmb-invalid-auth-space.hf.space"
|
170
|
+
}),
|
171
|
+
{
|
172
|
+
status: 200,
|
173
|
+
headers: {
|
174
|
+
"Content-Type": "application/json"
|
175
|
+
}
|
176
|
+
}
|
177
|
+
);
|
178
|
+
}
|
179
|
+
),
|
180
|
+
http.get(
|
181
|
+
`${root_url}/api/spaces/${duplicate_app_reference}/${HOST_URL}`,
|
182
|
+
() => {
|
183
|
+
return new HttpResponse(
|
184
|
+
JSON.stringify({
|
185
|
+
subdomain: "gradio-hello-world",
|
186
|
+
host: "https://gradio-hello-world.hf.space"
|
187
|
+
}),
|
188
|
+
{
|
189
|
+
status: 200,
|
190
|
+
headers: {
|
191
|
+
"Content-Type": "application/json"
|
192
|
+
}
|
193
|
+
}
|
194
|
+
);
|
195
|
+
}
|
196
|
+
),
|
197
|
+
http.get(`${root_url}/api/spaces/${unauth_app_reference}/${HOST_URL}`, () => {
|
198
|
+
return new HttpResponse(
|
199
|
+
JSON.stringify({
|
200
|
+
subdomain: "hmb-unath-space",
|
201
|
+
host: "https://hmb-unauth-space.hf.space"
|
202
|
+
}),
|
203
|
+
{
|
204
|
+
status: 200,
|
205
|
+
headers: {
|
206
|
+
"Content-Type": "application/json"
|
207
|
+
}
|
208
|
+
}
|
209
|
+
);
|
210
|
+
}),
|
123
211
|
// /info requests
|
124
212
|
http.get(`${direct_space_url}/${API_INFO_URL}`, () => {
|
125
213
|
return new HttpResponse(JSON.stringify(response_api_info), {
|
@@ -153,6 +241,22 @@ export const handlers: RequestHandler[] = [
|
|
153
241
|
}
|
154
242
|
});
|
155
243
|
}),
|
244
|
+
http.get(`${auth_app_space_url}/${API_INFO_URL}`, async () => {
|
245
|
+
return new HttpResponse(JSON.stringify(response_api_info), {
|
246
|
+
status: 200,
|
247
|
+
headers: {
|
248
|
+
"Content-Type": "application/json"
|
249
|
+
}
|
250
|
+
});
|
251
|
+
}),
|
252
|
+
http.get(`${private_auth_space_url}/${API_INFO_URL}`, async () => {
|
253
|
+
return new HttpResponse(JSON.stringify(response_api_info), {
|
254
|
+
status: 200,
|
255
|
+
headers: {
|
256
|
+
"Content-Type": "application/json"
|
257
|
+
}
|
258
|
+
});
|
259
|
+
}),
|
156
260
|
// /config requests
|
157
261
|
http.get(`${direct_space_url}/${CONFIG_URL}`, () => {
|
158
262
|
return new HttpResponse(JSON.stringify(config_response), {
|
@@ -190,6 +294,20 @@ export const handlers: RequestHandler[] = [
|
|
190
294
|
}
|
191
295
|
);
|
192
296
|
}),
|
297
|
+
http.get(`${private_auth_space_url}/${CONFIG_URL}`, () => {
|
298
|
+
return new HttpResponse(
|
299
|
+
JSON.stringify({
|
300
|
+
...config_response,
|
301
|
+
root: "https://hmb-private-auth-space.hf.space"
|
302
|
+
}),
|
303
|
+
{
|
304
|
+
status: 200,
|
305
|
+
headers: {
|
306
|
+
"Content-Type": "application/json"
|
307
|
+
}
|
308
|
+
}
|
309
|
+
);
|
310
|
+
}),
|
193
311
|
http.get(`${direct_space_url}/${CONFIG_URL}`, () => {
|
194
312
|
return new HttpResponse(JSON.stringify(config_response), {
|
195
313
|
status: 500,
|
@@ -206,6 +324,42 @@ export const handlers: RequestHandler[] = [
|
|
206
324
|
}
|
207
325
|
});
|
208
326
|
}),
|
327
|
+
http.get(`${invalid_auth_space_url}/${CONFIG_URL}`, () => {
|
328
|
+
return new HttpResponse(JSON.stringify({ detail: "Unauthorized" }), {
|
329
|
+
status: 401,
|
330
|
+
headers: {
|
331
|
+
"Content-Type": "application/json"
|
332
|
+
}
|
333
|
+
});
|
334
|
+
}),
|
335
|
+
http.get(`${auth_app_space_url}/${CONFIG_URL}`, ({ request }) => {
|
336
|
+
return new HttpResponse(
|
337
|
+
JSON.stringify({
|
338
|
+
...config_response,
|
339
|
+
root: "https://hmb-auth-space.hf.space",
|
340
|
+
space_id: "hmb/auth_space"
|
341
|
+
}),
|
342
|
+
{
|
343
|
+
status: 200,
|
344
|
+
headers: {
|
345
|
+
"Content-Type": "application/json"
|
346
|
+
}
|
347
|
+
}
|
348
|
+
);
|
349
|
+
}),
|
350
|
+
http.get(`${unauth_app_space_url}/${CONFIG_URL}`, () => {
|
351
|
+
return new HttpResponse(
|
352
|
+
JSON.stringify({
|
353
|
+
detail: "Unauthorized"
|
354
|
+
}),
|
355
|
+
{
|
356
|
+
status: 401,
|
357
|
+
headers: {
|
358
|
+
"Content-Type": "application/json"
|
359
|
+
}
|
360
|
+
}
|
361
|
+
);
|
362
|
+
}),
|
209
363
|
// /whoami requests
|
210
364
|
http.get(`${root_url}/api/whoami-v2`, () => {
|
211
365
|
return new HttpResponse(JSON.stringify(whoami_response), {
|
@@ -387,6 +541,20 @@ export const handlers: RequestHandler[] = [
|
|
387
541
|
}
|
388
542
|
});
|
389
543
|
}),
|
544
|
+
http.get(`${root_url}/api/spaces/${unauth_app_reference}`, () => {
|
545
|
+
return new HttpResponse(
|
546
|
+
JSON.stringify({
|
547
|
+
id: unauth_app_reference,
|
548
|
+
runtime: { ...runtime_response }
|
549
|
+
}),
|
550
|
+
{
|
551
|
+
status: 200,
|
552
|
+
headers: {
|
553
|
+
"Content-Type": "application/json"
|
554
|
+
}
|
555
|
+
}
|
556
|
+
);
|
557
|
+
}),
|
390
558
|
// jwt requests
|
391
559
|
http.get(`${root_url}/api/spaces/${app_reference}/jwt`, () => {
|
392
560
|
return new HttpResponse(
|
@@ -434,5 +602,84 @@ export const handlers: RequestHandler[] = [
|
|
434
602
|
"Content-Type": "application/json"
|
435
603
|
}
|
436
604
|
});
|
605
|
+
}),
|
606
|
+
// login requests
|
607
|
+
http.post(`${auth_app_space_url}/${LOGIN_URL}`, async ({ request }) => {
|
608
|
+
let username;
|
609
|
+
let password;
|
610
|
+
|
611
|
+
await request.formData().then((data) => {
|
612
|
+
username = data.get("username");
|
613
|
+
password = data.get("password");
|
614
|
+
});
|
615
|
+
|
616
|
+
if (username === "admin" && password === "pass1234") {
|
617
|
+
return new HttpResponse(
|
618
|
+
JSON.stringify({
|
619
|
+
success: true
|
620
|
+
}),
|
621
|
+
{
|
622
|
+
status: 200,
|
623
|
+
headers: {
|
624
|
+
"Content-Type": "application/json",
|
625
|
+
"Set-Cookie":
|
626
|
+
"access-token-123=abc; HttpOnly; Path=/; SameSite=none; Secure",
|
627
|
+
// @ts-ignore - multiple Set-Cookie headers are returned
|
628
|
+
"Set-Cookie":
|
629
|
+
"access-token-unsecure-123=abc; HttpOnly; Path=/; SameSite=none; Secure"
|
630
|
+
}
|
631
|
+
}
|
632
|
+
);
|
633
|
+
}
|
634
|
+
|
635
|
+
return new HttpResponse(null, {
|
636
|
+
status: 401,
|
637
|
+
headers: {
|
638
|
+
"Content-Type": "application/json"
|
639
|
+
}
|
640
|
+
});
|
641
|
+
}),
|
642
|
+
http.post(`${invalid_auth_space_url}/${LOGIN_URL}`, async () => {
|
643
|
+
return new HttpResponse(null, {
|
644
|
+
status: 401,
|
645
|
+
headers: {
|
646
|
+
"Content-Type": "application/json"
|
647
|
+
}
|
648
|
+
});
|
649
|
+
}),
|
650
|
+
http.post(`${private_auth_space_url}/${LOGIN_URL}`, async ({ request }) => {
|
651
|
+
let username;
|
652
|
+
let password;
|
653
|
+
|
654
|
+
await request.formData().then((data) => {
|
655
|
+
username = data.get("username");
|
656
|
+
password = data.get("password");
|
657
|
+
});
|
658
|
+
|
659
|
+
if (username === "admin" && password === "pass1234") {
|
660
|
+
return new HttpResponse(
|
661
|
+
JSON.stringify({
|
662
|
+
success: true
|
663
|
+
}),
|
664
|
+
{
|
665
|
+
status: 200,
|
666
|
+
headers: {
|
667
|
+
"Content-Type": "application/json",
|
668
|
+
"Set-Cookie":
|
669
|
+
"access-token-123=abc; HttpOnly; Path=/; SameSite=none; Secure",
|
670
|
+
// @ts-ignore - multiple Set-Cookie headers are returned
|
671
|
+
"Set-Cookie":
|
672
|
+
"access-token-unsecure-123=abc; HttpOnly; Path=/; SameSite=none; Secure"
|
673
|
+
}
|
674
|
+
}
|
675
|
+
);
|
676
|
+
}
|
677
|
+
|
678
|
+
return new HttpResponse(null, {
|
679
|
+
status: 401,
|
680
|
+
headers: {
|
681
|
+
"Content-Type": "application/json"
|
682
|
+
}
|
683
|
+
});
|
437
684
|
})
|
438
685
|
];
|
package/src/test/init.test.ts
CHANGED
@@ -67,7 +67,7 @@ describe("Client class", () => {
|
|
67
67
|
Client.connect("hmb/secret_world", {
|
68
68
|
hf_token: "hf_bad_token"
|
69
69
|
})
|
70
|
-
).rejects.
|
70
|
+
).rejects.toThrowError(SPACE_METADATA_ERROR_MSG);
|
71
71
|
});
|
72
72
|
|
73
73
|
test("viewing the api info of a running app", async () => {
|
@@ -77,7 +77,7 @@ describe("Client class", () => {
|
|
77
77
|
|
78
78
|
test("viewing the api info of a non-existent app", async () => {
|
79
79
|
const app = Client.connect(broken_app_reference);
|
80
|
-
await expect(app).rejects.
|
80
|
+
await expect(app).rejects.toThrowError();
|
81
81
|
});
|
82
82
|
});
|
83
83
|
|
@@ -1,10 +1,13 @@
|
|
1
1
|
import {
|
2
2
|
resolve_root,
|
3
3
|
get_jwt,
|
4
|
-
determine_protocol
|
4
|
+
determine_protocol,
|
5
|
+
parse_and_set_cookies
|
5
6
|
} from "../helpers/init_helpers";
|
6
7
|
import { initialise_server } from "./server";
|
7
8
|
import { beforeAll, afterEach, afterAll, it, expect, describe } from "vitest";
|
9
|
+
import { Client } from "../client";
|
10
|
+
import { INVALID_CREDENTIALS_MSG, MISSING_CREDENTIALS_MSG } from "../constants";
|
8
11
|
|
9
12
|
const server = initialise_server();
|
10
13
|
|
@@ -92,3 +95,52 @@ describe("determine_protocol", () => {
|
|
92
95
|
});
|
93
96
|
});
|
94
97
|
});
|
98
|
+
|
99
|
+
describe("parse_and_set_cookies", () => {
|
100
|
+
it("should return an empty array when the cookie header is empty", () => {
|
101
|
+
const cookie_header = "";
|
102
|
+
const result = parse_and_set_cookies(cookie_header);
|
103
|
+
expect(result).toEqual([]);
|
104
|
+
});
|
105
|
+
|
106
|
+
it("should parse the cookie header and return an array of cookies", () => {
|
107
|
+
const cookie_header = "access-token-123=abc;access-token-unsecured-456=def";
|
108
|
+
const result = parse_and_set_cookies(cookie_header);
|
109
|
+
expect(result).toEqual(["access-token-123=abc"]);
|
110
|
+
});
|
111
|
+
});
|
112
|
+
|
113
|
+
describe("resolve_cookies", () => {
|
114
|
+
it("should set the cookies when correct auth credentials are provided", async () => {
|
115
|
+
const client = await Client.connect("hmb/auth_space", {
|
116
|
+
auth: ["admin", "pass1234"]
|
117
|
+
});
|
118
|
+
|
119
|
+
const api = client.view_api();
|
120
|
+
expect((await api).named_endpoints["/predict"]).toBeDefined();
|
121
|
+
});
|
122
|
+
|
123
|
+
it("should connect to a private and authenticated space", async () => {
|
124
|
+
const client = await Client.connect("hmb/private_auth_space", {
|
125
|
+
hf_token: "hf_123",
|
126
|
+
auth: ["admin", "pass1234"]
|
127
|
+
});
|
128
|
+
|
129
|
+
const api = client.view_api();
|
130
|
+
expect((await api).named_endpoints["/predict"]).toBeDefined();
|
131
|
+
});
|
132
|
+
|
133
|
+
it("should not set the cookies when auth credentials are invalid", async () => {
|
134
|
+
await expect(
|
135
|
+
Client.connect("hmb/invalid_auth_space", {
|
136
|
+
auth: ["admin", "wrong_password"]
|
137
|
+
})
|
138
|
+
).rejects.toThrowError(INVALID_CREDENTIALS_MSG);
|
139
|
+
});
|
140
|
+
|
141
|
+
it("should not set the cookies when auth option is not provided in an auth space", async () => {
|
142
|
+
await expect(Client.connect("hmb/unauth_space")).rejects.toThrowError(
|
143
|
+
MISSING_CREDENTIALS_MSG
|
144
|
+
);
|
145
|
+
});
|
146
|
+
});
|
package/src/test/test_data.ts
CHANGED
@@ -376,6 +376,7 @@ export const config_response: Config = {
|
|
376
376
|
},
|
377
377
|
dependencies: [
|
378
378
|
{
|
379
|
+
id: 0,
|
379
380
|
targets: [
|
380
381
|
[9, "click"],
|
381
382
|
[1, "submit"]
|
@@ -404,6 +405,7 @@ export const config_response: Config = {
|
|
404
405
|
zerogpu: false
|
405
406
|
},
|
406
407
|
{
|
408
|
+
id: 1,
|
407
409
|
targets: [[8, "click"]],
|
408
410
|
inputs: [],
|
409
411
|
outputs: [1, 2],
|
@@ -429,6 +431,7 @@ export const config_response: Config = {
|
|
429
431
|
zerogpu: false
|
430
432
|
},
|
431
433
|
{
|
434
|
+
id: 2,
|
432
435
|
targets: [[8, "click"]],
|
433
436
|
inputs: [],
|
434
437
|
outputs: [5],
|
package/src/types.ts
CHANGED
@@ -154,6 +154,7 @@ export interface Config {
|
|
154
154
|
}
|
155
155
|
|
156
156
|
export interface Dependency {
|
157
|
+
id: number;
|
157
158
|
targets: [number, string][];
|
158
159
|
inputs: number[];
|
159
160
|
outputs: number[];
|
@@ -179,6 +180,7 @@ export interface Dependency {
|
|
179
180
|
final_event: Payload | null;
|
180
181
|
show_api: boolean;
|
181
182
|
zerogpu?: boolean;
|
183
|
+
rendered_in: number | null;
|
182
184
|
}
|
183
185
|
|
184
186
|
export interface DependencyTypes {
|
@@ -215,6 +217,7 @@ export interface DuplicateOptions extends ClientOptions {
|
|
215
217
|
export interface ClientOptions {
|
216
218
|
hf_token?: `hf_${string}`;
|
217
219
|
status_callback?: SpaceStatusCallback | null;
|
220
|
+
auth?: [string, string] | null;
|
218
221
|
}
|
219
222
|
|
220
223
|
export interface FileData {
|
@@ -255,6 +258,8 @@ export interface RenderMessage {
|
|
255
258
|
data: {
|
256
259
|
components: any[];
|
257
260
|
layout: any;
|
261
|
+
dependencies: Dependency[];
|
262
|
+
render_id: number;
|
258
263
|
};
|
259
264
|
}
|
260
265
|
|
@@ -276,4 +281,5 @@ export interface Status {
|
|
276
281
|
desc: string | null;
|
277
282
|
}[];
|
278
283
|
time?: Date;
|
284
|
+
changed_state_ids?: number[];
|
279
285
|
}
|
package/src/utils/duplicate.ts
CHANGED
@@ -6,12 +6,17 @@ import {
|
|
6
6
|
import type { DuplicateOptions } from "../types";
|
7
7
|
import { Client } from "../client";
|
8
8
|
import { SPACE_METADATA_ERROR_MSG } from "../constants";
|
9
|
+
import {
|
10
|
+
get_cookie_header,
|
11
|
+
parse_and_set_cookies
|
12
|
+
} from "../helpers/init_helpers";
|
13
|
+
import { process_endpoint } from "../helpers/api_info";
|
9
14
|
|
10
15
|
export async function duplicate(
|
11
16
|
app_reference: string,
|
12
17
|
options: DuplicateOptions
|
13
18
|
): Promise<Client> {
|
14
|
-
const { hf_token, private: _private, hardware, timeout } = options;
|
19
|
+
const { hf_token, private: _private, hardware, timeout, auth } = options;
|
15
20
|
|
16
21
|
if (hardware && !hardware_types.includes(hardware)) {
|
17
22
|
throw new Error(
|
@@ -20,9 +25,29 @@ export async function duplicate(
|
|
20
25
|
.join(",")}.`
|
21
26
|
);
|
22
27
|
}
|
28
|
+
|
29
|
+
const { http_protocol, host } = await process_endpoint(
|
30
|
+
app_reference,
|
31
|
+
hf_token
|
32
|
+
);
|
33
|
+
|
34
|
+
let cookies: string[] | null = null;
|
35
|
+
|
36
|
+
if (auth) {
|
37
|
+
const cookie_header = await get_cookie_header(
|
38
|
+
http_protocol,
|
39
|
+
host,
|
40
|
+
auth,
|
41
|
+
fetch
|
42
|
+
);
|
43
|
+
|
44
|
+
if (cookie_header) cookies = parse_and_set_cookies(cookie_header);
|
45
|
+
}
|
46
|
+
|
23
47
|
const headers = {
|
24
48
|
Authorization: `Bearer ${hf_token}`,
|
25
|
-
"Content-Type": "application/json"
|
49
|
+
"Content-Type": "application/json",
|
50
|
+
...(cookies ? { Cookie: cookies.join("; ") } : {})
|
26
51
|
};
|
27
52
|
|
28
53
|
const user = (
|
package/src/utils/post_data.ts
CHANGED
@@ -19,7 +19,8 @@ export async function post_data(
|
|
19
19
|
var response = await this.fetch(url, {
|
20
20
|
method: "POST",
|
21
21
|
body: JSON.stringify(body),
|
22
|
-
headers: { ...headers, ...additional_headers }
|
22
|
+
headers: { ...headers, ...additional_headers },
|
23
|
+
credentials: "include"
|
23
24
|
});
|
24
25
|
} catch (e) {
|
25
26
|
return [{ error: BROKEN_CONNECTION_MSG }, 500];
|