Skip to content

Commit

Permalink
Add eventsource polyfill for Node.js and browser environments (#8118)
Browse files Browse the repository at this point in the history
* add msw setup and initialisation tests

* add changeset

* add eventsource polyfill for node and browser envs

* add changeset

* add changeset

* config tweak

* types

* update eventsource usage

* add changeset

* add walk_and_store_blobs improvements and add tests

* add changeset

* api_info tests

* add direct space URL link tests

* fix tests

* add view_api tests

* add post_message test

* tweak

* add spaces tests

* jwt and protocol tests

* add post_data tests

* test tweaks

* dynamically import eventsource

* revet eventsource imports

* add node test

* lockfile

* add client test in root pkg file

* lcokfile

* remove eventsource from js/app

* add changeset

* remove ts ignore

* move eventsource polyfill to eventsource factory

* add changeset

* tweak

---------

Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>
  • Loading branch information
2 people authored and dawoodkhan82 committed May 6, 2024
1 parent 8e0d09d commit 1d7fc6f
Show file tree
Hide file tree
Showing 16 changed files with 79 additions and 14 deletions.
7 changes: 7 additions & 0 deletions .changeset/red-colts-burn.md
@@ -0,0 +1,7 @@
---
"@gradio/client": patch
"@gradio/upload": patch
"gradio": patch
---

fix:Add eventsource polyfill for Node.js and browser environments
7 changes: 6 additions & 1 deletion client/js/package.json
Expand Up @@ -13,7 +13,9 @@
"./package.json": "./package.json"
},
"dependencies": {
"@types/eventsource": "^1.1.15",
"bufferutil": "^4.0.7",
"eventsource": "^2.0.2",
"msw": "^2.2.1",
"semiver": "^1.1.0",
"typescript": "^5.0.0",
Expand All @@ -26,7 +28,10 @@
"scripts": {
"bundle": "vite build --ssr",
"generate_types": "tsc",
"build": "pnpm bundle && pnpm generate_types"
"build": "pnpm bundle && pnpm generate_types",
"test": "pnpm test:client && pnpm test:client:node",
"test:client": "vitest run -c vite.config.js",
"test:client:node": "TEST_MODE=node vitest run -c vite.config.js"
},
"engines": {
"node": ">=18.0.0"
Expand Down
15 changes: 11 additions & 4 deletions client/js/src/client.ts
Expand Up @@ -62,12 +62,19 @@ export class Client {
return fetch(input, init);
}

eventSource_factory(url: URL): EventSource {
if (typeof window !== undefined && typeof EventSource !== "undefined") {
eventSource_factory(url: URL): EventSource | null {
if (typeof window === "undefined" || typeof EventSource === "undefined") {
import("eventsource")
.then((EventSourceModule) => {
return new EventSourceModule.default(url.toString());
})
.catch((error) =>
console.error("Failed to load EventSource module:", error)
);
} else {
return new EventSource(url.toString());
}
// @ts-ignore
return null; // todo: polyfill eventsource for node envs
return null;
}

view_api: () => Promise<ApiInfo<JsApiData>>;
Expand Down
1 change: 1 addition & 0 deletions client/js/src/test/api_info.test.ts
@@ -1,4 +1,5 @@
import { QUEUE_FULL_MSG, SPACE_METADATA_ERROR_MSG } from "../constants";
import { beforeAll, afterEach, afterAll, it, expect, describe } from "vitest";
import {
handle_message,
get_description,
Expand Down
2 changes: 1 addition & 1 deletion client/js/src/test/data.test.ts
@@ -1,4 +1,4 @@
import { describe, it, expect, vi } from "vitest";
import { describe, it, expect, vi, afterEach } from "vitest";
import {
update_object,
walk_and_store_blobs,
Expand Down
9 changes: 9 additions & 0 deletions client/js/src/test/handlers.ts
Expand Up @@ -425,5 +425,14 @@ export const handlers: RequestHandler[] = [
"Content-Type": "application/json"
}
});
}),
// heartbeat requests
http.get(`*/heartbeat/*`, () => {
return new HttpResponse(null, {
status: 200,
headers: {
"Content-Type": "application/json"
}
});
})
];
1 change: 1 addition & 0 deletions client/js/src/test/init_helpers.test.ts
Expand Up @@ -4,6 +4,7 @@ import {
determine_protocol
} from "../helpers/init_helpers";
import { initialise_server } from "./server";
import { beforeAll, afterEach, afterAll, it, expect, describe } from "vitest";

const server = initialise_server();

Expand Down
1 change: 1 addition & 0 deletions client/js/src/test/post_data.test.ts
Expand Up @@ -3,6 +3,7 @@ import { Client } from "../client";
import { initialise_server } from "./server";
import { BROKEN_CONNECTION_MSG } from "../constants";
const server = initialise_server();
import { beforeAll, afterEach, afterAll, it, expect, describe } from "vitest";

beforeAll(() => server.listen());
afterEach(() => server.resetHandlers());
Expand Down
1 change: 1 addition & 0 deletions client/js/src/test/spaces.test.ts
Expand Up @@ -5,6 +5,7 @@ import {
set_space_timeout,
check_space_status
} from "../helpers/spaces";
import { beforeAll, afterEach, afterAll, it, expect, describe } from "vitest";

import { initialise_server } from "./server";
import { hardware_sleeptime_response } from "./test_data";
Expand Down
2 changes: 1 addition & 1 deletion client/js/src/test/upload_files.test.ts
@@ -1,4 +1,4 @@
import { describe, it, expect, afterEach } from "vitest";
import { describe, it, expect, afterEach, beforeAll, afterAll } from "vitest";

import { Client } from "..";
import { initialise_server } from "./server";
Expand Down
11 changes: 8 additions & 3 deletions client/js/src/utils/stream.ts
Expand Up @@ -28,7 +28,7 @@ export function open_stream(this: Client): void {
throw new Error("Cannot connect to sse endpoint: " + url.toString());
}

event_source.onmessage = async function (event) {
event_source.onmessage = async function (event: MessageEvent) {
let _data = JSON.parse(event.data);
if (_data.msg === "close_stream") {
close_stream(stream_status, event_source);
Expand All @@ -53,8 +53,13 @@ export function open_stream(this: Client): void {
close_stream(stream_status, event_source);
}
}
let fn = event_callbacks[event_id];
window.setTimeout(fn, 0, _data); // need to do this to put the event on the end of the event loop, so the browser can refresh between callbacks and not freeze in case of quick generations. See https://github.com/gradio-app/gradio/pull/7055
let fn: (data: any) => void = event_callbacks[event_id];

if (typeof window !== "undefined") {
window.setTimeout(fn, 0, _data); // need to do this to put the event on the end of the event loop, so the browser can refresh between callbacks and not freeze in case of quick generations. See https://github.com/gradio-app/gradio/pull/7055
} else {
setImmediate(fn, _data);
}
} else {
if (!pending_stream_messages[event_id]) {
pending_stream_messages[event_id] = [];
Expand Down
8 changes: 6 additions & 2 deletions client/js/src/utils/submit.ts
Expand Up @@ -376,7 +376,7 @@ export function submit(
);
}

event_source.onmessage = async function (event) {
event_source.onmessage = async function (event: MessageEvent) {
const _data = JSON.parse(event.data);
const { type, status, data } = handle_message(
_data,
Expand Down Expand Up @@ -476,7 +476,11 @@ export function submit(
fn_index,
time: new Date()
});
let hostname = window.location.hostname;
let hostname = "";
if (typeof window !== "undefined") {
hostname = window?.location?.hostname;
}

let hfhubdev = "dev.spaces.huggingface.tech";
const origin = hostname.includes(".dev.")
? `https://moon-${hostname.split(".")[1]}.${hfhubdev}`
Expand Down
7 changes: 7 additions & 0 deletions client/js/vite.config.js
@@ -1,6 +1,8 @@
import { defineConfig } from "vite";
import { svelte } from "@sveltejs/vite-plugin-svelte";

const TEST_MODE = process.env.TEST_MODE || "happy-dom";

export default defineConfig({
build: {
lib: {
Expand All @@ -17,6 +19,11 @@ export default defineConfig({
},
plugins: [svelte()],

mode: process.env.MODE || "development",
test: {
include: ["./src/test/*.test.*"],
environment: TEST_MODE
},
ssr: {
target: "node",
format: "esm",
Expand Down
2 changes: 1 addition & 1 deletion js/upload/src/UploadProgress.svelte
Expand Up @@ -50,7 +50,7 @@
const _data = JSON.parse(event.data);
if (!progress) progress = true;
if (_data.msg === "done") {
event_source.close();
event_source?.close();
dispatch("done");
} else {
current_file_upload = _data;
Expand Down
2 changes: 1 addition & 1 deletion package.json
Expand Up @@ -15,7 +15,7 @@
"ts:check": "svelte-check --tsconfig tsconfig.json --threshold error",
"test": "pnpm --filter @gradio/client build && vitest dev --config .config/vitest.config.ts",
"test:run": "pnpm --filter @gradio/client build && vitest run --config .config/vitest.config.ts --reporter=verbose",
"test:node": "TEST_MODE=node pnpm vitest run --config .config/vitest.config.ts",
"test:client": "pnpm --filter=@gradio/client test",
"test:browser": "pnpm --filter @gradio/app test:browser",
"test:browser:reload": "CUSTOM_TEST=1 pnpm --filter @gradio/app test:browser:reload",
"test:browser:full": "run-s build test:browser",
Expand Down
17 changes: 17 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 1d7fc6f

Please sign in to comment.