@gradio/client 0.1.2 → 0.1.4

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/src/client.ts CHANGED
@@ -45,7 +45,7 @@ type client_return = {
45
45
  data?: unknown[],
46
46
  event_data?: unknown
47
47
  ) => SubmitReturn;
48
- view_api: (c?: Config) => Promise<Record<string, any>>;
48
+ view_api: (c?: Config) => Promise<ApiInfo<JsApiData>>;
49
49
  };
50
50
 
51
51
  type SubmitReturn = {
@@ -58,62 +58,8 @@ type SubmitReturn = {
58
58
  const QUEUE_FULL_MSG = "This application is too busy. Keep trying!";
59
59
  const BROKEN_CONNECTION_MSG = "Connection errored out.";
60
60
 
61
- export async function post_data(
62
- url: string,
63
- body: unknown,
64
- token?: `hf_${string}`
65
- ): Promise<[PostResponse, number]> {
66
- const headers: {
67
- Authorization?: string;
68
- "Content-Type": "application/json";
69
- } = { "Content-Type": "application/json" };
70
- if (token) {
71
- headers.Authorization = `Bearer ${token}`;
72
- }
73
- try {
74
- var response = await fetch(url, {
75
- method: "POST",
76
- body: JSON.stringify(body),
77
- headers
78
- });
79
- } catch (e) {
80
- return [{ error: BROKEN_CONNECTION_MSG }, 500];
81
- }
82
- const output: PostResponse = await response.json();
83
- return [output, response.status];
84
- }
85
-
86
61
  export let NodeBlob;
87
62
 
88
- export async function upload_files(
89
- root: string,
90
- files: Array<File>,
91
- token?: `hf_${string}`
92
- ): Promise<UploadResponse> {
93
- const headers: {
94
- Authorization?: string;
95
- } = {};
96
- if (token) {
97
- headers.Authorization = `Bearer ${token}`;
98
- }
99
-
100
- const formData = new FormData();
101
- files.forEach((file) => {
102
- formData.append("files", file);
103
- });
104
- try {
105
- var response = await fetch(`${root}/upload`, {
106
- method: "POST",
107
- body: formData,
108
- headers
109
- });
110
- } catch (e) {
111
- return { error: BROKEN_CONNECTION_MSG };
112
- }
113
- const output: UploadResponse["files"] = await response.json();
114
- return { files: output };
115
- }
116
-
117
63
  export async function duplicate(
118
64
  app_reference: string,
119
65
  options: {
@@ -197,453 +143,526 @@ export async function duplicate(
197
143
  }
198
144
  }
199
145
 
200
- export async function client(
201
- app_reference: string,
202
- options: {
203
- hf_token?: `hf_${string}`;
204
- status_callback?: SpaceStatusCallback;
205
- normalise_files?: boolean;
206
- } = { normalise_files: true }
207
- ): Promise<client_return> {
208
- return new Promise(async (res) => {
209
- const { status_callback, hf_token, normalise_files } = options;
210
- const return_obj = {
211
- predict,
212
- submit,
213
- view_api
214
- // duplicate
215
- };
146
+ /**
147
+ * We need to inject a customized fetch implementation for the Wasm version.
148
+ */
149
+ export function api_factory(fetch_implementation: typeof fetch) {
150
+ return { post_data, upload_files, client, handle_blob };
151
+
152
+ async function post_data(
153
+ url: string,
154
+ body: unknown,
155
+ token?: `hf_${string}`
156
+ ): Promise<[PostResponse, number]> {
157
+ const headers: {
158
+ Authorization?: string;
159
+ "Content-Type": "application/json";
160
+ } = { "Content-Type": "application/json" };
161
+ if (token) {
162
+ headers.Authorization = `Bearer ${token}`;
163
+ }
164
+ try {
165
+ var response = await fetch_implementation(url, {
166
+ method: "POST",
167
+ body: JSON.stringify(body),
168
+ headers
169
+ });
170
+ } catch (e) {
171
+ return [{ error: BROKEN_CONNECTION_MSG }, 500];
172
+ }
173
+ const output: PostResponse = await response.json();
174
+ return [output, response.status];
175
+ }
216
176
 
217
- let transform_files = normalise_files ?? true;
218
- if (typeof window === "undefined" || !("WebSocket" in window)) {
219
- const ws = await import("ws");
220
- NodeBlob = (await import("node:buffer")).Blob;
221
- //@ts-ignore
222
- global.WebSocket = ws.WebSocket;
177
+ async function upload_files(
178
+ root: string,
179
+ files: Array<File>,
180
+ token?: `hf_${string}`
181
+ ): Promise<UploadResponse> {
182
+ const headers: {
183
+ Authorization?: string;
184
+ } = {};
185
+ if (token) {
186
+ headers.Authorization = `Bearer ${token}`;
223
187
  }
224
188
 
225
- const { ws_protocol, http_protocol, host, space_id } =
226
- await process_endpoint(app_reference, hf_token);
189
+ const formData = new FormData();
190
+ files.forEach((file) => {
191
+ formData.append("files", file);
192
+ });
193
+ try {
194
+ var response = await fetch_implementation(`${root}/upload`, {
195
+ method: "POST",
196
+ body: formData,
197
+ headers
198
+ });
199
+ } catch (e) {
200
+ return { error: BROKEN_CONNECTION_MSG };
201
+ }
202
+ const output: UploadResponse["files"] = await response.json();
203
+ return { files: output };
204
+ }
205
+
206
+ async function client(
207
+ app_reference: string,
208
+ options: {
209
+ hf_token?: `hf_${string}`;
210
+ status_callback?: SpaceStatusCallback;
211
+ normalise_files?: boolean;
212
+ } = { normalise_files: true }
213
+ ): Promise<client_return> {
214
+ return new Promise(async (res) => {
215
+ const { status_callback, hf_token, normalise_files } = options;
216
+ const return_obj = {
217
+ predict,
218
+ submit,
219
+ view_api
220
+ // duplicate
221
+ };
227
222
 
228
- const session_hash = Math.random().toString(36).substring(2);
229
- const last_status: Record<string, Status["stage"]> = {};
230
- let config: Config;
231
- let api_map: Record<string, number> = {};
223
+ const transform_files = normalise_files ?? true;
224
+ if (typeof window === "undefined" || !("WebSocket" in window)) {
225
+ const ws = await import("ws");
226
+ NodeBlob = (await import("node:buffer")).Blob;
227
+ //@ts-ignore
228
+ global.WebSocket = ws.WebSocket;
229
+ }
232
230
 
233
- let jwt: false | string = false;
231
+ const { ws_protocol, http_protocol, host, space_id } =
232
+ await process_endpoint(app_reference, hf_token);
234
233
 
235
- if (hf_token && space_id) {
236
- jwt = await get_jwt(space_id, hf_token);
237
- }
234
+ const session_hash = Math.random().toString(36).substring(2);
235
+ const last_status: Record<string, Status["stage"]> = {};
236
+ let config: Config;
237
+ let api_map: Record<string, number> = {};
238
238
 
239
- async function config_success(_config: Config) {
240
- config = _config;
241
- api_map = map_names_to_ids(_config?.dependencies || []);
242
- try {
243
- api = await view_api(config);
244
- } catch (e) {
245
- console.error(`Could not get api details: ${e.message}`);
239
+ let jwt: false | string = false;
240
+
241
+ if (hf_token && space_id) {
242
+ jwt = await get_jwt(space_id, hf_token);
246
243
  }
247
244
 
248
- return {
249
- config,
250
- ...return_obj
251
- };
252
- }
253
- let api;
254
- async function handle_space_sucess(status: SpaceStatus) {
255
- if (status_callback) status_callback(status);
256
- if (status.status === "running")
245
+ async function config_success(_config: Config) {
246
+ config = _config;
247
+ api_map = map_names_to_ids(_config?.dependencies || []);
257
248
  try {
258
- config = await resolve_config(`${http_protocol}//${host}`, hf_token);
259
-
260
- const _config = await config_success(config);
261
- res(_config);
249
+ api = await view_api(config);
262
250
  } catch (e) {
263
- if (status_callback) {
264
- status_callback({
265
- status: "error",
266
- message: "Could not load this space.",
267
- load_status: "error",
268
- detail: "NOT_FOUND"
269
- });
270
- }
251
+ console.error(`Could not get api details: ${e.message}`);
271
252
  }
272
- }
273
-
274
- try {
275
- config = await resolve_config(`${http_protocol}//${host}`, hf_token);
276
253
 
277
- const _config = await config_success(config);
278
- res(_config);
279
- } catch (e) {
280
- if (space_id) {
281
- check_space_status(
282
- space_id,
283
- RE_SPACE_NAME.test(space_id) ? "space_name" : "subdomain",
284
- handle_space_sucess
285
- );
286
- } else {
287
- if (status_callback)
288
- status_callback({
289
- status: "error",
290
- message: "Could not load this space.",
291
- load_status: "error",
292
- detail: "NOT_FOUND"
293
- });
254
+ return {
255
+ config,
256
+ ...return_obj
257
+ };
294
258
  }
295
- }
259
+ let api: ApiInfo<JsApiData>;
260
+ async function handle_space_sucess(status: SpaceStatus) {
261
+ if (status_callback) status_callback(status);
262
+ if (status.status === "running")
263
+ try {
264
+ config = await resolve_config(
265
+ fetch_implementation,
266
+ `${http_protocol}//${host}`,
267
+ hf_token
268
+ );
296
269
 
297
- /**
298
- * Run a prediction.
299
- * @param endpoint - The prediction endpoint to use.
300
- * @param status_callback - A function that is called with the current status of the prediction immediately and every time it updates.
301
- * @return Returns the data for the prediction or an error message.
302
- */
303
- function predict(endpoint: string, data: unknown[], event_data?: unknown) {
304
- let data_returned = false;
305
- let status_complete = false;
306
- return new Promise((res, rej) => {
307
- const app = submit(endpoint, data, event_data);
308
-
309
- app
310
- .on("data", (d) => {
311
- data_returned = true;
312
- if (status_complete) {
313
- app.destroy();
314
- }
315
- res(d);
316
- })
317
- .on("status", (status) => {
318
- if (status.stage === "error") rej(status);
319
- if (status.stage === "complete" && data_returned) {
320
- app.destroy();
321
- }
322
- if (status.stage === "complete") {
323
- status_complete = true;
270
+ const _config = await config_success(config);
271
+ res(_config);
272
+ } catch (e) {
273
+ console.error(e);
274
+ if (status_callback) {
275
+ status_callback({
276
+ status: "error",
277
+ message: "Could not load this space.",
278
+ load_status: "error",
279
+ detail: "NOT_FOUND"
280
+ });
324
281
  }
325
- });
326
- });
327
- }
282
+ }
283
+ }
328
284
 
329
- function submit(
330
- endpoint: string | number,
331
- data: unknown[],
332
- event_data?: unknown
333
- ): SubmitReturn {
334
- let fn_index: number;
335
- let api_info;
336
-
337
- if (typeof endpoint === "number") {
338
- fn_index = endpoint;
339
- api_info = api.unnamed_endpoints[fn_index];
340
- } else {
341
- const trimmed_endpoint = endpoint.replace(/^\//, "");
285
+ try {
286
+ config = await resolve_config(
287
+ fetch_implementation,
288
+ `${http_protocol}//${host}`,
289
+ hf_token
290
+ );
342
291
 
343
- fn_index = api_map[trimmed_endpoint];
344
- api_info = api.named_endpoints[endpoint.trim()];
292
+ const _config = await config_success(config);
293
+ res(_config);
294
+ } catch (e) {
295
+ console.error(e);
296
+ if (space_id) {
297
+ check_space_status(
298
+ space_id,
299
+ RE_SPACE_NAME.test(space_id) ? "space_name" : "subdomain",
300
+ handle_space_sucess
301
+ );
302
+ } else {
303
+ if (status_callback)
304
+ status_callback({
305
+ status: "error",
306
+ message: "Could not load this space.",
307
+ load_status: "error",
308
+ detail: "NOT_FOUND"
309
+ });
310
+ }
345
311
  }
346
312
 
347
- if (typeof fn_index !== "number") {
348
- throw new Error(
349
- "There is no endpoint matching that name of fn_index matching that number."
350
- );
313
+ /**
314
+ * Run a prediction.
315
+ * @param endpoint - The prediction endpoint to use.
316
+ * @param status_callback - A function that is called with the current status of the prediction immediately and every time it updates.
317
+ * @return Returns the data for the prediction or an error message.
318
+ */
319
+ function predict(
320
+ endpoint: string,
321
+ data: unknown[],
322
+ event_data?: unknown
323
+ ) {
324
+ let data_returned = false;
325
+ let status_complete = false;
326
+ return new Promise((res, rej) => {
327
+ const app = submit(endpoint, data, event_data);
328
+
329
+ app
330
+ .on("data", (d) => {
331
+ data_returned = true;
332
+ if (status_complete) {
333
+ app.destroy();
334
+ }
335
+ res(d);
336
+ })
337
+ .on("status", (status) => {
338
+ if (status.stage === "error") rej(status);
339
+ if (status.stage === "complete" && data_returned) {
340
+ app.destroy();
341
+ }
342
+ if (status.stage === "complete") {
343
+ status_complete = true;
344
+ }
345
+ });
346
+ });
351
347
  }
352
348
 
353
- let websocket: WebSocket;
349
+ function submit(
350
+ endpoint: string | number,
351
+ data: unknown[],
352
+ event_data?: unknown
353
+ ): SubmitReturn {
354
+ let fn_index: number;
355
+ let api_info;
356
+
357
+ if (typeof endpoint === "number") {
358
+ fn_index = endpoint;
359
+ api_info = api.unnamed_endpoints[fn_index];
360
+ } else {
361
+ const trimmed_endpoint = endpoint.replace(/^\//, "");
354
362
 
355
- const _endpoint = typeof endpoint === "number" ? "/predict" : endpoint;
356
- let payload: Payload;
357
- let complete: false | Record<string, any> = false;
358
- const listener_map: ListenerMap<EventType> = {};
363
+ fn_index = api_map[trimmed_endpoint];
364
+ api_info = api.named_endpoints[endpoint.trim()];
365
+ }
359
366
 
360
- //@ts-ignore
361
- handle_blob(
362
- `${http_protocol}//${host + config.path}`,
363
- data,
364
- api_info,
365
- hf_token
366
- ).then((_payload) => {
367
- payload = { data: _payload || [], event_data, fn_index };
368
- if (skip_queue(fn_index, config)) {
369
- fire_event({
370
- type: "status",
371
- endpoint: _endpoint,
372
- stage: "pending",
373
- queue: false,
374
- fn_index,
375
- time: new Date()
376
- });
367
+ if (typeof fn_index !== "number") {
368
+ throw new Error(
369
+ "There is no endpoint matching that name of fn_index matching that number."
370
+ );
371
+ }
377
372
 
378
- post_data(
379
- `${http_protocol}//${host + config.path}/run${
380
- _endpoint.startsWith("/") ? _endpoint : `/${_endpoint}`
381
- }`,
382
- {
383
- ...payload,
384
- session_hash
385
- },
386
- hf_token
387
- )
388
- .then(([output, status_code]) => {
389
- const data = transform_files
390
- ? transform_output(
391
- output.data,
392
- api_info,
393
- config.root,
394
- config.root_url
395
- )
396
- : output.data;
397
- if (status_code == 200) {
398
- fire_event({
399
- type: "data",
400
- endpoint: _endpoint,
401
- fn_index,
402
- data: data,
403
- time: new Date()
404
- });
373
+ let websocket: WebSocket;
374
+
375
+ const _endpoint = typeof endpoint === "number" ? "/predict" : endpoint;
376
+ let payload: Payload;
377
+ let complete: false | Record<string, any> = false;
378
+ const listener_map: ListenerMap<EventType> = {};
379
+
380
+ handle_blob(
381
+ `${http_protocol}//${host + config.path}`,
382
+ data,
383
+ api_info,
384
+ hf_token
385
+ ).then((_payload) => {
386
+ payload = { data: _payload || [], event_data, fn_index };
387
+ if (skip_queue(fn_index, config)) {
388
+ fire_event({
389
+ type: "status",
390
+ endpoint: _endpoint,
391
+ stage: "pending",
392
+ queue: false,
393
+ fn_index,
394
+ time: new Date()
395
+ });
405
396
 
406
- fire_event({
407
- type: "status",
408
- endpoint: _endpoint,
409
- fn_index,
410
- stage: "complete",
411
- eta: output.average_duration,
412
- queue: false,
413
- time: new Date()
414
- });
415
- } else {
397
+ post_data(
398
+ `${http_protocol}//${host + config.path}/run${
399
+ _endpoint.startsWith("/") ? _endpoint : `/${_endpoint}`
400
+ }`,
401
+ {
402
+ ...payload,
403
+ session_hash
404
+ },
405
+ hf_token
406
+ )
407
+ .then(([output, status_code]) => {
408
+ const data = transform_files
409
+ ? transform_output(
410
+ output.data,
411
+ api_info,
412
+ config.root,
413
+ config.root_url
414
+ )
415
+ : output.data;
416
+ if (status_code == 200) {
417
+ fire_event({
418
+ type: "data",
419
+ endpoint: _endpoint,
420
+ fn_index,
421
+ data: data,
422
+ time: new Date()
423
+ });
424
+
425
+ fire_event({
426
+ type: "status",
427
+ endpoint: _endpoint,
428
+ fn_index,
429
+ stage: "complete",
430
+ eta: output.average_duration,
431
+ queue: false,
432
+ time: new Date()
433
+ });
434
+ } else {
435
+ fire_event({
436
+ type: "status",
437
+ stage: "error",
438
+ endpoint: _endpoint,
439
+ fn_index,
440
+ message: output.error,
441
+ queue: false,
442
+ time: new Date()
443
+ });
444
+ }
445
+ })
446
+ .catch((e) => {
416
447
  fire_event({
417
448
  type: "status",
418
449
  stage: "error",
450
+ message: e.message,
419
451
  endpoint: _endpoint,
420
452
  fn_index,
421
- message: output.error,
422
453
  queue: false,
423
454
  time: new Date()
424
455
  });
425
- }
426
- })
427
- .catch((e) => {
428
- fire_event({
429
- type: "status",
430
- stage: "error",
431
- message: e.message,
432
- endpoint: _endpoint,
433
- fn_index,
434
- queue: false,
435
- time: new Date()
436
456
  });
457
+ } else {
458
+ fire_event({
459
+ type: "status",
460
+ stage: "pending",
461
+ queue: true,
462
+ endpoint: _endpoint,
463
+ fn_index,
464
+ time: new Date()
437
465
  });
438
- } else {
439
- fire_event({
440
- type: "status",
441
- stage: "pending",
442
- queue: true,
443
- endpoint: _endpoint,
444
- fn_index,
445
- time: new Date()
446
- });
447
-
448
- let url = new URL(`${ws_protocol}://${host}${config.path}
449
- /queue/join`);
450
466
 
451
- if (jwt) {
452
- url.searchParams.set("__sign", jwt);
453
- }
467
+ let url = new URL(`${ws_protocol}://${host}${config.path}
468
+ /queue/join`);
454
469
 
455
- websocket = new WebSocket(url);
456
-
457
- websocket.onclose = (evt) => {
458
- if (!evt.wasClean) {
459
- fire_event({
460
- type: "status",
461
- stage: "error",
462
- message: BROKEN_CONNECTION_MSG,
463
- queue: true,
464
- endpoint: _endpoint,
465
- fn_index,
466
- time: new Date()
467
- });
470
+ if (jwt) {
471
+ url.searchParams.set("__sign", jwt);
468
472
  }
469
- };
470
473
 
471
- websocket.onmessage = function (event) {
472
- const _data = JSON.parse(event.data);
473
- const { type, status, data } = handle_message(
474
- _data,
475
- last_status[fn_index]
476
- );
474
+ websocket = new WebSocket(url);
477
475
 
478
- if (type === "update" && status && !complete) {
479
- // call 'status' listeners
480
- fire_event({
481
- type: "status",
482
- endpoint: _endpoint,
483
- fn_index,
484
- time: new Date(),
485
- ...status
486
- });
487
- if (status.stage === "error") {
488
- websocket.close();
476
+ websocket.onclose = (evt) => {
477
+ if (!evt.wasClean) {
478
+ fire_event({
479
+ type: "status",
480
+ stage: "error",
481
+ message: BROKEN_CONNECTION_MSG,
482
+ queue: true,
483
+ endpoint: _endpoint,
484
+ fn_index,
485
+ time: new Date()
486
+ });
489
487
  }
490
- } else if (type === "hash") {
491
- websocket.send(JSON.stringify({ fn_index, session_hash }));
492
- return;
493
- } else if (type === "data") {
494
- websocket.send(JSON.stringify({ ...payload, session_hash }));
495
- } else if (type === "complete") {
496
- complete = status;
497
- } else if (type === "generating") {
498
- fire_event({
499
- type: "status",
500
- time: new Date(),
501
- ...status,
502
- stage: status?.stage!,
503
- queue: true,
504
- endpoint: _endpoint,
505
- fn_index
506
- });
507
- }
508
- if (data) {
509
- fire_event({
510
- type: "data",
511
- time: new Date(),
512
- data: transform_files
513
- ? transform_output(
514
- data.data,
515
- api_info,
516
- config.root,
517
- config.root_url
518
- )
519
- : data.data,
520
- endpoint: _endpoint,
521
- fn_index
522
- });
488
+ };
489
+
490
+ websocket.onmessage = function (event) {
491
+ const _data = JSON.parse(event.data);
492
+ const { type, status, data } = handle_message(
493
+ _data,
494
+ last_status[fn_index]
495
+ );
523
496
 
524
- if (complete) {
497
+ if (type === "update" && status && !complete) {
498
+ // call 'status' listeners
499
+ fire_event({
500
+ type: "status",
501
+ endpoint: _endpoint,
502
+ fn_index,
503
+ time: new Date(),
504
+ ...status
505
+ });
506
+ if (status.stage === "error") {
507
+ websocket.close();
508
+ }
509
+ } else if (type === "hash") {
510
+ websocket.send(JSON.stringify({ fn_index, session_hash }));
511
+ return;
512
+ } else if (type === "data") {
513
+ websocket.send(JSON.stringify({ ...payload, session_hash }));
514
+ } else if (type === "complete") {
515
+ complete = status;
516
+ } else if (type === "generating") {
525
517
  fire_event({
526
518
  type: "status",
527
519
  time: new Date(),
528
- ...complete,
520
+ ...status,
529
521
  stage: status?.stage!,
530
522
  queue: true,
531
523
  endpoint: _endpoint,
532
524
  fn_index
533
525
  });
534
- websocket.close();
535
526
  }
536
- }
537
- };
527
+ if (data) {
528
+ fire_event({
529
+ type: "data",
530
+ time: new Date(),
531
+ data: transform_files
532
+ ? transform_output(
533
+ data.data,
534
+ api_info,
535
+ config.root,
536
+ config.root_url
537
+ )
538
+ : data.data,
539
+ endpoint: _endpoint,
540
+ fn_index
541
+ });
538
542
 
539
- // different ws contract for gradio versions older than 3.6.0
540
- //@ts-ignore
541
- if (semiver(config.version || "2.0.0", "3.6") < 0) {
542
- addEventListener("open", () =>
543
- websocket.send(JSON.stringify({ hash: session_hash }))
544
- );
543
+ if (complete) {
544
+ fire_event({
545
+ type: "status",
546
+ time: new Date(),
547
+ ...complete,
548
+ stage: status?.stage!,
549
+ queue: true,
550
+ endpoint: _endpoint,
551
+ fn_index
552
+ });
553
+ websocket.close();
554
+ }
555
+ }
556
+ };
557
+
558
+ // different ws contract for gradio versions older than 3.6.0
559
+ //@ts-ignore
560
+ if (semiver(config.version || "2.0.0", "3.6") < 0) {
561
+ addEventListener("open", () =>
562
+ websocket.send(JSON.stringify({ hash: session_hash }))
563
+ );
564
+ }
545
565
  }
546
- }
547
- });
566
+ });
548
567
 
549
- function fire_event<K extends EventType>(event: Event<K>) {
550
- const narrowed_listener_map: ListenerMap<K> = listener_map;
551
- let listeners = narrowed_listener_map[event.type] || [];
552
- listeners?.forEach((l) => l(event));
553
- }
568
+ function fire_event<K extends EventType>(event: Event<K>) {
569
+ const narrowed_listener_map: ListenerMap<K> = listener_map;
570
+ const listeners = narrowed_listener_map[event.type] || [];
571
+ listeners?.forEach((l) => l(event));
572
+ }
554
573
 
555
- function on<K extends EventType>(
556
- eventType: K,
557
- listener: EventListener<K>
558
- ) {
559
- const narrowed_listener_map: ListenerMap<K> = listener_map;
560
- let listeners = narrowed_listener_map[eventType] || [];
561
- narrowed_listener_map[eventType] = listeners;
562
- listeners?.push(listener);
574
+ function on<K extends EventType>(
575
+ eventType: K,
576
+ listener: EventListener<K>
577
+ ) {
578
+ const narrowed_listener_map: ListenerMap<K> = listener_map;
579
+ const listeners = narrowed_listener_map[eventType] || [];
580
+ narrowed_listener_map[eventType] = listeners;
581
+ listeners?.push(listener);
563
582
 
564
- return { on, off, cancel, destroy };
565
- }
583
+ return { on, off, cancel, destroy };
584
+ }
566
585
 
567
- function off<K extends EventType>(
568
- eventType: K,
569
- listener: EventListener<K>
570
- ) {
571
- const narrowed_listener_map: ListenerMap<K> = listener_map;
572
- let listeners = narrowed_listener_map[eventType] || [];
573
- listeners = listeners?.filter((l) => l !== listener);
574
- narrowed_listener_map[eventType] = listeners;
586
+ function off<K extends EventType>(
587
+ eventType: K,
588
+ listener: EventListener<K>
589
+ ) {
590
+ const narrowed_listener_map: ListenerMap<K> = listener_map;
591
+ let listeners = narrowed_listener_map[eventType] || [];
592
+ listeners = listeners?.filter((l) => l !== listener);
593
+ narrowed_listener_map[eventType] = listeners;
575
594
 
576
- return { on, off, cancel, destroy };
577
- }
595
+ return { on, off, cancel, destroy };
596
+ }
578
597
 
579
- async function cancel() {
580
- const _status: Status = {
581
- stage: "complete",
582
- queue: false,
583
- time: new Date()
584
- };
585
- complete = _status;
586
- fire_event({
587
- ..._status,
588
- type: "status",
589
- endpoint: _endpoint,
590
- fn_index: fn_index
591
- });
598
+ async function cancel() {
599
+ const _status: Status = {
600
+ stage: "complete",
601
+ queue: false,
602
+ time: new Date()
603
+ };
604
+ complete = _status;
605
+ fire_event({
606
+ ..._status,
607
+ type: "status",
608
+ endpoint: _endpoint,
609
+ fn_index: fn_index
610
+ });
592
611
 
593
- if (websocket && websocket.readyState === 0) {
594
- websocket.addEventListener("open", () => {
612
+ if (websocket && websocket.readyState === 0) {
613
+ websocket.addEventListener("open", () => {
614
+ websocket.close();
615
+ });
616
+ } else {
595
617
  websocket.close();
596
- });
597
- } else {
598
- websocket.close();
599
- }
618
+ }
600
619
 
601
- try {
602
- await fetch(`${http_protocol}//${host + config.path}/reset`, {
603
- headers: { "Content-Type": "application/json" },
604
- method: "POST",
605
- body: JSON.stringify({ fn_index, session_hash })
606
- });
607
- } catch (e) {
608
- console.warn(
609
- "The `/reset` endpoint could not be called. Subsequent endpoint results may be unreliable."
610
- );
620
+ try {
621
+ await fetch_implementation(
622
+ `${http_protocol}//${host + config.path}/reset`,
623
+ {
624
+ headers: { "Content-Type": "application/json" },
625
+ method: "POST",
626
+ body: JSON.stringify({ fn_index, session_hash })
627
+ }
628
+ );
629
+ } catch (e) {
630
+ console.warn(
631
+ "The `/reset` endpoint could not be called. Subsequent endpoint results may be unreliable."
632
+ );
633
+ }
611
634
  }
612
- }
613
635
 
614
- function destroy() {
615
- for (const event_type in listener_map) {
616
- listener_map[event_type as "data" | "status"].forEach((fn) => {
617
- off(event_type as "data" | "status", fn);
618
- });
636
+ function destroy() {
637
+ for (const event_type in listener_map) {
638
+ listener_map[event_type as "data" | "status"].forEach((fn) => {
639
+ off(event_type as "data" | "status", fn);
640
+ });
641
+ }
619
642
  }
643
+
644
+ return {
645
+ on,
646
+ off,
647
+ cancel,
648
+ destroy
649
+ };
620
650
  }
621
651
 
622
- return {
623
- on,
624
- off,
625
- cancel,
626
- destroy
627
- };
628
- }
652
+ async function view_api(config?: Config): Promise<ApiInfo<JsApiData>> {
653
+ if (api) return api;
629
654
 
630
- async function view_api(
631
- config?: Config
632
- ): Promise<ApiInfo<JsApiData> | [{ error: string }, 500]> {
633
- if (api) return api;
634
-
635
- const headers: {
636
- Authorization?: string;
637
- "Content-Type": "application/json";
638
- } = { "Content-Type": "application/json" };
639
- if (hf_token) {
640
- headers.Authorization = `Bearer ${hf_token}`;
641
- }
642
- try {
655
+ const headers: {
656
+ Authorization?: string;
657
+ "Content-Type": "application/json";
658
+ } = { "Content-Type": "application/json" };
659
+ if (hf_token) {
660
+ headers.Authorization = `Bearer ${hf_token}`;
661
+ }
643
662
  let response: Response;
644
663
  // @ts-ignore
645
664
  if (semiver(config.version || "2.0.0", "3.30") < 0) {
646
- response = await fetch(
665
+ response = await fetch_implementation(
647
666
  "https://gradio-space-api-fetcher-v2.hf.space/api",
648
667
  {
649
668
  method: "POST",
@@ -655,11 +674,15 @@ export async function client(
655
674
  }
656
675
  );
657
676
  } else {
658
- response = await fetch(`${config.root}/info`, {
677
+ response = await fetch_implementation(`${config.root}/info`, {
659
678
  headers
660
679
  });
661
680
  }
662
681
 
682
+ if (!response.ok) {
683
+ throw new Error(BROKEN_CONNECTION_MSG);
684
+ }
685
+
663
686
  let api_info = (await response.json()) as
664
687
  | ApiInfo<ApiData>
665
688
  | { api: ApiInfo<ApiData> };
@@ -676,20 +699,66 @@ export async function client(
676
699
 
677
700
  const x = transform_api_info(api_info, config, api_map);
678
701
  return x;
679
- } catch (e) {
680
- return [{ error: BROKEN_CONNECTION_MSG }, 500];
681
702
  }
682
- }
683
- });
703
+ });
704
+ }
705
+
706
+ async function handle_blob(
707
+ endpoint: string,
708
+ data: unknown[],
709
+ api_info,
710
+ token?: `hf_${string}`
711
+ ): Promise<unknown[]> {
712
+ const blob_refs = await walk_and_store_blobs(
713
+ data,
714
+ undefined,
715
+ [],
716
+ true,
717
+ api_info
718
+ );
719
+
720
+ return Promise.all(
721
+ blob_refs.map(async ({ path, blob, data, type }) => {
722
+ if (blob) {
723
+ const file_url = (await upload_files(endpoint, [blob], token))
724
+ .files[0];
725
+ return { path, file_url, type };
726
+ } else {
727
+ return { path, base64: data, type };
728
+ }
729
+ })
730
+ ).then((r) => {
731
+ r.forEach(({ path, file_url, base64, type }) => {
732
+ if (base64) {
733
+ update_object(data, base64, path);
734
+ } else if (type === "Gallery") {
735
+ update_object(data, file_url, path);
736
+ } else if (file_url) {
737
+ const o = {
738
+ is_file: true,
739
+ name: `${file_url}`,
740
+ data: null
741
+ // orig_name: "file.csv"
742
+ };
743
+ update_object(data, o, path);
744
+ }
745
+ });
746
+
747
+ return data;
748
+ });
749
+ }
684
750
  }
685
751
 
752
+ export const { post_data, upload_files, client, handle_blob } =
753
+ api_factory(fetch);
754
+
686
755
  function transform_output(
687
756
  data: any[],
688
757
  api_info: any,
689
758
  root_url: string,
690
759
  remote_url?: string
691
760
  ): unknown[] {
692
- let transformed_data = data.map((d, i) => {
761
+ return data.map((d, i) => {
693
762
  if (api_info.returns?.[i]?.component === "File") {
694
763
  return normalise_file(d, root_url, remote_url);
695
764
  } else if (api_info.returns?.[i]?.component === "Gallery") {
@@ -704,8 +773,6 @@ function transform_output(
704
773
  return d;
705
774
  }
706
775
  });
707
-
708
- return transformed_data;
709
776
  }
710
777
 
711
778
  function normalise_file(
@@ -750,7 +817,7 @@ function normalise_file(
750
817
  if (!root_url) {
751
818
  file.data = root + "/file=" + file.name;
752
819
  } else {
753
- file.data = "/proxy=" + root_url + "/file=" + file.name;
820
+ file.data = "/proxy=" + root_url + "file=" + file.name;
754
821
  }
755
822
  }
756
823
  return file;
@@ -907,55 +974,6 @@ async function get_jwt(
907
974
  }
908
975
  }
909
976
 
910
- export async function handle_blob(
911
- endpoint: string,
912
- data: unknown[],
913
- api_info,
914
- token?: `hf_${string}`
915
- ): Promise<unknown[]> {
916
- const blob_refs = await walk_and_store_blobs(
917
- data,
918
- undefined,
919
- [],
920
- true,
921
- api_info
922
- );
923
-
924
- return new Promise((res) => {
925
- Promise.all(
926
- blob_refs.map(async ({ path, blob, data, type }) => {
927
- if (blob) {
928
- const file_url = (await upload_files(endpoint, [blob], token))
929
- .files[0];
930
- return { path, file_url, type };
931
- } else {
932
- return { path, base64: data, type };
933
- }
934
- })
935
- )
936
- .then((r) => {
937
- r.forEach(({ path, file_url, base64, type }) => {
938
- if (base64) {
939
- update_object(data, base64, path);
940
- } else if (type === "Gallery") {
941
- update_object(data, file_url, path);
942
- } else if (file_url) {
943
- const o = {
944
- is_file: true,
945
- name: `${file_url}`,
946
- data: null
947
- // orig_name: "file.csv"
948
- };
949
- update_object(data, o, path);
950
- }
951
- });
952
-
953
- res(data);
954
- })
955
- .catch(console.log);
956
- });
957
- }
958
-
959
977
  function update_object(object, newValue, stack) {
960
978
  while (stack.length > 1) {
961
979
  object = object[stack.shift()];
@@ -1061,6 +1079,7 @@ function skip_queue(id: number, config: Config) {
1061
1079
  }
1062
1080
 
1063
1081
  async function resolve_config(
1082
+ fetch_implementation: typeof fetch,
1064
1083
  endpoint?: string,
1065
1084
  token?: `hf_${string}`
1066
1085
  ): Promise<Config> {
@@ -1078,7 +1097,9 @@ async function resolve_config(
1078
1097
  config.root = endpoint + config.root;
1079
1098
  return { ...config, path: path };
1080
1099
  } else if (endpoint) {
1081
- let response = await fetch(`${endpoint}/config`, { headers });
1100
+ let response = await fetch_implementation(`${endpoint}/config`, {
1101
+ headers
1102
+ });
1082
1103
 
1083
1104
  if (response.status === 200) {
1084
1105
  const config = await response.json();
@@ -1139,9 +1160,18 @@ async function check_space_status(
1139
1160
 
1140
1161
  setTimeout(() => {
1141
1162
  check_space_status(id, type, status_callback);
1142
- }, 1000);
1163
+ }, 1000); // poll for status
1164
+ break;
1165
+ case "PAUSED":
1166
+ status_callback({
1167
+ status: "paused",
1168
+ load_status: "error",
1169
+ message:
1170
+ "This space has been paused by the author. If you would like to try this demo, consider duplicating the space.",
1171
+ detail: stage,
1172
+ discussions_enabled: await discussions_enabled(space_name)
1173
+ });
1143
1174
  break;
1144
- // poll for status
1145
1175
  case "RUNNING":
1146
1176
  case "RUNNING_BUILDING":
1147
1177
  status_callback({