@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.
Files changed (41) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/README.md +8 -1
  3. package/dist/client.d.ts +4 -0
  4. package/dist/client.d.ts.map +1 -1
  5. package/dist/constants.d.ts +4 -0
  6. package/dist/constants.d.ts.map +1 -1
  7. package/dist/helpers/api_info.d.ts +1 -0
  8. package/dist/helpers/api_info.d.ts.map +1 -1
  9. package/dist/helpers/data.d.ts.map +1 -1
  10. package/dist/helpers/init_helpers.d.ts +4 -1
  11. package/dist/helpers/init_helpers.d.ts.map +1 -1
  12. package/dist/index.js +236 -75
  13. package/dist/test/handlers.d.ts.map +1 -1
  14. package/dist/test/test_data.d.ts.map +1 -1
  15. package/dist/types.d.ts +6 -0
  16. package/dist/types.d.ts.map +1 -1
  17. package/dist/utils/duplicate.d.ts.map +1 -1
  18. package/dist/utils/post_data.d.ts.map +1 -1
  19. package/dist/utils/predict.d.ts.map +1 -1
  20. package/dist/utils/submit.d.ts.map +1 -1
  21. package/dist/utils/upload_files.d.ts.map +1 -1
  22. package/dist/utils/view_api.d.ts.map +1 -1
  23. package/package.json +1 -1
  24. package/src/client.ts +70 -28
  25. package/src/constants.ts +5 -0
  26. package/src/helpers/api_info.ts +44 -17
  27. package/src/helpers/data.ts +9 -22
  28. package/src/helpers/init_helpers.ts +98 -9
  29. package/src/test/api_info.test.ts +69 -4
  30. package/src/test/data.test.ts +13 -16
  31. package/src/test/handlers.ts +249 -2
  32. package/src/test/init.test.ts +2 -2
  33. package/src/test/init_helpers.test.ts +53 -1
  34. package/src/test/test_data.ts +3 -0
  35. package/src/types.ts +6 -0
  36. package/src/utils/duplicate.ts +27 -2
  37. package/src/utils/post_data.ts +2 -1
  38. package/src/utils/predict.ts +4 -2
  39. package/src/utils/submit.ts +42 -9
  40. package/src/utils/upload_files.ts +2 -1
  41. package/src/utils/view_api.ts +7 -4
@@ -1,16 +1,22 @@
1
- import { QUEUE_FULL_MSG, SPACE_METADATA_ERROR_MSG } from "../constants";
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", () => {
@@ -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[id].queue = true;
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[id].queue = true;
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[id].queue = false;
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[id].queue = false;
225
+ config.dependencies.find((dep) => dep.id === id)!.queue = false;
229
226
 
230
227
  const result = skip_queue(id, config_response);
231
228
 
@@ -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 server_error_reference = "hmb/server_error";
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
  ];
@@ -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.toThrow(CONFIG_ERROR_MSG);
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.toThrow(CONFIG_ERROR_MSG);
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
+ });
@@ -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
  }
@@ -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 = (
@@ -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];