mirror of
https://github.com/gradio-app/gradio.git
synced 2025-01-18 10:44:33 +08:00
Gradio-lite (Gradio Wasm) (#4402)
This commit is contained in:
parent
3abad71ce4
commit
4fbdefe94e
@ -2,6 +2,7 @@
|
||||
**/pnpm-workspace.yaml
|
||||
**/js/app/dist/**
|
||||
**/client/js/dist/**
|
||||
**/js/lite/dist/**
|
||||
**/pnpm-lock.yaml
|
||||
**/js/plot/src/Plot.svelte
|
||||
**/.svelte-kit/**
|
||||
|
2
.github/workflows/ui.yml
vendored
2
.github/workflows/ui.yml
vendored
@ -27,6 +27,8 @@ jobs:
|
||||
always-install-pnpm: true
|
||||
- name: build client
|
||||
run: pnpm --filter @gradio/client build
|
||||
- name: build the wasm module
|
||||
run: pnpm --filter @gradio/wasm build
|
||||
- name: lint
|
||||
run: pnpm lint
|
||||
continue-on-error: true
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,2 +1,8 @@
|
||||
export { client, post_data, upload_files, duplicate } from "./client.js";
|
||||
export {
|
||||
client,
|
||||
post_data,
|
||||
upload_files,
|
||||
duplicate,
|
||||
api_factory
|
||||
} from "./client.js";
|
||||
export type { SpaceStatus } from "./types.js";
|
||||
|
@ -32,6 +32,7 @@ from gradio import (
|
||||
strings,
|
||||
themes,
|
||||
utils,
|
||||
wasm_utils,
|
||||
)
|
||||
from gradio.context import Context
|
||||
from gradio.deprecation import check_deprecated_parameters
|
||||
@ -739,7 +740,7 @@ class Blocks(BlockContext):
|
||||
self.root_path = ""
|
||||
self.root_urls = set()
|
||||
|
||||
if self.analytics_enabled:
|
||||
if not wasm_utils.IS_WASM and self.analytics_enabled:
|
||||
is_custom_theme = not any(
|
||||
self.theme.to_dict() == built_in_theme.to_dict()
|
||||
for built_in_theme in BUILT_IN_THEMES.values()
|
||||
@ -1772,15 +1773,37 @@ Received outputs:
|
||||
"Rerunning server... use `close()` to stop if you need to change `launch()` parameters.\n----"
|
||||
)
|
||||
else:
|
||||
server_name, server_port, local_url, app, server = networking.start_server(
|
||||
self,
|
||||
server_name,
|
||||
server_port,
|
||||
ssl_keyfile,
|
||||
ssl_certfile,
|
||||
ssl_keyfile_password,
|
||||
app_kwargs=app_kwargs,
|
||||
)
|
||||
if wasm_utils.IS_WASM:
|
||||
server_name = "xxx"
|
||||
server_port = 99999
|
||||
local_url = ""
|
||||
server = None
|
||||
|
||||
# In the Wasm environment, we only need the app object
|
||||
# which the frontend app will directly communicate with through the Worker API,
|
||||
# and we don't need to start a server.
|
||||
# So we just create the app object and register it here,
|
||||
# and avoid using `networking.start_server` that would start a server that don't work in the Wasm env.
|
||||
from gradio.routes import App
|
||||
|
||||
app = App.create_app(self, app_kwargs=app_kwargs)
|
||||
wasm_utils.register_app(app)
|
||||
else:
|
||||
(
|
||||
server_name,
|
||||
server_port,
|
||||
local_url,
|
||||
app,
|
||||
server,
|
||||
) = networking.start_server(
|
||||
self,
|
||||
server_name,
|
||||
server_port,
|
||||
ssl_keyfile,
|
||||
ssl_certfile,
|
||||
ssl_keyfile_password,
|
||||
app_kwargs=app_kwargs,
|
||||
)
|
||||
self.server_name = server_name
|
||||
self.local_url = local_url
|
||||
self.server_port = server_port
|
||||
@ -1802,7 +1825,11 @@ Received outputs:
|
||||
|
||||
# Cannot run async functions in background other than app's scope.
|
||||
# Workaround by triggering the app endpoint
|
||||
requests.get(f"{self.local_url}startup-events", verify=ssl_verify)
|
||||
if not wasm_utils.IS_WASM:
|
||||
requests.get(f"{self.local_url}startup-events", verify=ssl_verify)
|
||||
|
||||
if wasm_utils.IS_WASM:
|
||||
return TupleNoPrint((self.server_app, self.local_url, self.share_url))
|
||||
|
||||
utils.launch_counter()
|
||||
|
||||
@ -2037,7 +2064,8 @@ Received outputs:
|
||||
try:
|
||||
if self.enable_queue:
|
||||
self._queue.close()
|
||||
self.server.close()
|
||||
if self.server:
|
||||
self.server.close()
|
||||
self.is_running = False
|
||||
# So that the startup events (starting the queue)
|
||||
# happen the next time the app is launched
|
||||
@ -2056,7 +2084,8 @@ Received outputs:
|
||||
time.sleep(0.1)
|
||||
except (KeyboardInterrupt, OSError):
|
||||
print("Keyboard interruption in main thread... closing server.")
|
||||
self.server.close()
|
||||
if self.server:
|
||||
self.server.close()
|
||||
for tunnel in CURRENT_TUNNELS:
|
||||
tunnel.kill()
|
||||
|
||||
|
@ -7,16 +7,19 @@ import warnings
|
||||
from pathlib import Path
|
||||
from typing import Callable, Literal
|
||||
|
||||
from ffmpy import FFmpeg
|
||||
from gradio_client import utils as client_utils
|
||||
from gradio_client.data_classes import FileData
|
||||
from gradio_client.documentation import document, set_documentation_group
|
||||
from gradio_client.serializing import VideoSerializable
|
||||
|
||||
from gradio import processing_utils, utils
|
||||
from gradio import processing_utils, utils, wasm_utils
|
||||
from gradio.components.base import IOComponent, _Keywords
|
||||
from gradio.events import Changeable, Clearable, Playable, Recordable, Uploadable
|
||||
|
||||
if not wasm_utils.IS_WASM:
|
||||
# TODO: Support ffmpeg on Wasm
|
||||
from ffmpy import FFmpeg
|
||||
|
||||
set_documentation_group("component")
|
||||
|
||||
|
||||
@ -204,6 +207,10 @@ class Video(
|
||||
)
|
||||
if Path(output_file_name).exists():
|
||||
return output_file_name
|
||||
if wasm_utils.IS_WASM:
|
||||
raise wasm_utils.WasmUnsupportedError(
|
||||
"Video formatting is not supported in the Wasm mode."
|
||||
)
|
||||
ff = FFmpeg(
|
||||
inputs={str(file_name): None},
|
||||
outputs={output_file_name: output_options},
|
||||
@ -212,6 +219,10 @@ class Video(
|
||||
return output_file_name
|
||||
elif not self.include_audio:
|
||||
output_file_name = str(file_name.with_name(f"muted_{file_name.name}"))
|
||||
if wasm_utils.IS_WASM:
|
||||
raise wasm_utils.WasmUnsupportedError(
|
||||
"include_audio=False is not supported in the Wasm mode."
|
||||
)
|
||||
ff = FFmpeg(
|
||||
inputs={str(file_name): None},
|
||||
outputs={output_file_name: ["-an"]},
|
||||
@ -301,6 +312,10 @@ class Video(
|
||||
# selected format
|
||||
returned_format = video.split(".")[-1].lower()
|
||||
if self.format is not None and returned_format != self.format:
|
||||
if wasm_utils.IS_WASM:
|
||||
raise wasm_utils.WasmUnsupportedError(
|
||||
"Returning a video in a different format is not supported in the Wasm mode."
|
||||
)
|
||||
output_file_name = video[0 : video.rindex(".") + 1] + self.format
|
||||
ff = FFmpeg(
|
||||
inputs={video: None},
|
||||
|
@ -11,10 +11,15 @@ from io import BytesIO
|
||||
from pathlib import Path
|
||||
|
||||
import numpy as np
|
||||
from ffmpy import FFmpeg, FFprobe, FFRuntimeError
|
||||
from gradio_client import utils as client_utils
|
||||
from PIL import Image, ImageOps, PngImagePlugin
|
||||
|
||||
from gradio import wasm_utils
|
||||
|
||||
if not wasm_utils.IS_WASM:
|
||||
# TODO: Support ffmpeg on Wasm
|
||||
from ffmpy import FFmpeg, FFprobe, FFRuntimeError
|
||||
|
||||
with warnings.catch_warnings():
|
||||
warnings.simplefilter("ignore") # Ignore pydub warning if ffmpeg is not installed
|
||||
from pydub import AudioSegment
|
||||
@ -478,6 +483,10 @@ def _convert(image, dtype, force_copy=False, uniform=False):
|
||||
|
||||
|
||||
def ffmpeg_installed() -> bool:
|
||||
if wasm_utils.IS_WASM:
|
||||
# TODO: Support ffmpeg in WASM
|
||||
return False
|
||||
|
||||
return shutil.which("ffmpeg") is not None
|
||||
|
||||
|
||||
|
@ -42,7 +42,7 @@ from starlette.websockets import WebSocketState
|
||||
|
||||
import gradio
|
||||
import gradio.ranged_response as ranged_response
|
||||
from gradio import utils
|
||||
from gradio import utils, wasm_utils
|
||||
from gradio.context import Context
|
||||
from gradio.data_classes import PredictBody, ResetBody
|
||||
from gradio.exceptions import Error
|
||||
@ -167,16 +167,18 @@ class App(FastAPI):
|
||||
blocks: gradio.Blocks, app_kwargs: Dict[str, Any] | None = None
|
||||
) -> App:
|
||||
app_kwargs = app_kwargs or {}
|
||||
app_kwargs.setdefault("default_response_class", ORJSONResponse)
|
||||
if not wasm_utils.IS_WASM:
|
||||
app_kwargs.setdefault("default_response_class", ORJSONResponse)
|
||||
app = App(**app_kwargs)
|
||||
app.configure_app(blocks)
|
||||
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=["*"],
|
||||
allow_methods=["*"],
|
||||
allow_headers=["*"],
|
||||
)
|
||||
if not wasm_utils.IS_WASM:
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=["*"],
|
||||
allow_methods=["*"],
|
||||
allow_headers=["*"],
|
||||
)
|
||||
|
||||
@app.get("/user")
|
||||
@app.get("/user/")
|
||||
|
@ -4,6 +4,8 @@ from typing import Dict
|
||||
|
||||
import requests
|
||||
|
||||
from gradio import wasm_utils
|
||||
|
||||
MESSAGING_API_ENDPOINT = "https://api.gradio.app/gradio-messaging/en"
|
||||
|
||||
en = {
|
||||
@ -41,5 +43,5 @@ def get_updated_messaging(en: Dict):
|
||||
pass
|
||||
|
||||
|
||||
if os.getenv("GRADIO_ANALYTICS_ENABLED", "True") == "True":
|
||||
if os.getenv("GRADIO_ANALYTICS_ENABLED", "True") == "True" and not wasm_utils.IS_WASM:
|
||||
threading.Thread(target=get_updated_messaging, args=(en,)).start()
|
||||
|
24
gradio/wasm_utils.py
Normal file
24
gradio/wasm_utils.py
Normal file
@ -0,0 +1,24 @@
|
||||
import sys
|
||||
|
||||
# See https://pyodide.org/en/stable/usage/faq.html#how-to-detect-that-code-is-run-with-pyodide
|
||||
IS_WASM = sys.platform == "emscripten"
|
||||
|
||||
|
||||
class WasmUnsupportedError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
app = None
|
||||
|
||||
|
||||
# `register_app` and `get_registered_app` are used
|
||||
# for the Wasm worker to get a reference to
|
||||
# the Gradio's FastAPI app instance (`app`).
|
||||
def register_app(_app):
|
||||
global app
|
||||
app = _app
|
||||
|
||||
|
||||
def get_registered_app():
|
||||
global app
|
||||
return app
|
43
js/app/lite.html
Normal file
43
js/app/lite.html
Normal file
@ -0,0 +1,43 @@
|
||||
<!DOCTYPE html>
|
||||
<!-- An entrypoint for the Wasm version development -->
|
||||
<html
|
||||
lang="en"
|
||||
style="
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
min-height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
"
|
||||
>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta
|
||||
name="viewport"
|
||||
content="width=device-width, initial-scale=1, shrink-to-fit=no, maximum-scale=1"
|
||||
/>
|
||||
|
||||
<script type="module" src="./src/lite/index.ts"></script>
|
||||
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link
|
||||
rel="preconnect"
|
||||
href="https://fonts.gstatic.com"
|
||||
crossorigin="anonymous"
|
||||
/>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/iframe-resizer/4.3.1/iframeResizer.contentWindow.min.js"></script>
|
||||
</head>
|
||||
|
||||
<body
|
||||
style="
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-grow: 1;
|
||||
"
|
||||
>
|
||||
<div id="gradio-app"></div>
|
||||
</body>
|
||||
</html>
|
@ -5,9 +5,14 @@
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite --port 9876",
|
||||
"dev:lite": "vite --port 9876 --mode development:lite",
|
||||
"build:cdn": "vite build --mode production:cdn --emptyOutDir",
|
||||
"build:website": "vite build --mode production:website --emptyOutDir",
|
||||
"build:local": "vite build --mode production:local --emptyOutDir",
|
||||
"pybuild:gradio": "cd ../../ && rm -rf gradio/templates/frontend && python3 -m build",
|
||||
"pybuild:gradio-client": "cd ../../client/python && python3 -m build",
|
||||
"pybuild": "run-p pybuild:*",
|
||||
"build:lite": "pnpm pybuild && vite build --mode production:lite --emptyOutDir",
|
||||
"preview": "vite preview",
|
||||
"test:snapshot": "pnpm exec playwright test snapshots/ --config=../../.config/playwright.config.js",
|
||||
"test:browser": "pnpm exec playwright test test/ --config=../../.config/playwright.config.js",
|
||||
@ -42,6 +47,7 @@
|
||||
"@gradio/upload-button": "workspace:^0.0.1",
|
||||
"@gradio/utils": "workspace:^0.0.1",
|
||||
"@gradio/video": "workspace:^0.0.1",
|
||||
"@gradio/wasm": "workspace:^0.0.1",
|
||||
"@playwright/test": "^1.35.1",
|
||||
"d3-dsv": "^3.0.1",
|
||||
"mime-types": "^2.1.34",
|
||||
|
@ -1,6 +1,6 @@
|
||||
<script context="module" lang="ts">
|
||||
import { writable } from "svelte/store";
|
||||
import { mount_css } from "./css";
|
||||
import { mount_css as default_mount_css } from "./css";
|
||||
|
||||
import type {
|
||||
ComponentMeta,
|
||||
@ -58,8 +58,8 @@
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import { onMount } from "svelte";
|
||||
import { client, SpaceStatus } from "@gradio/client";
|
||||
import { onMount, setContext } from "svelte";
|
||||
import type { api_factory, SpaceStatus } from "@gradio/client";
|
||||
|
||||
import Embed from "./Embed.svelte";
|
||||
import type { ThemeMode } from "./components/types";
|
||||
@ -76,6 +76,11 @@
|
||||
export let info: boolean;
|
||||
export let eager: boolean;
|
||||
|
||||
// These utilities are exported to be injectable for the Wasm version.
|
||||
export let mount_css: typeof default_mount_css = default_mount_css;
|
||||
export let client: ReturnType<typeof api_factory>["client"];
|
||||
export let upload_files: ReturnType<typeof api_factory>["upload_files"];
|
||||
|
||||
export let space: string | null;
|
||||
export let host: string | null;
|
||||
export let src: string | null;
|
||||
@ -216,6 +221,8 @@
|
||||
}
|
||||
});
|
||||
|
||||
setContext("upload_files", upload_files);
|
||||
|
||||
$: loader_status =
|
||||
!ready && status.load_status !== "error"
|
||||
? "pending"
|
||||
|
@ -1,11 +1,11 @@
|
||||
<script lang="ts">
|
||||
import { createEventDispatcher } from "svelte";
|
||||
import { createEventDispatcher, getContext } from "svelte";
|
||||
import { File as FileComponent, FileUpload } from "@gradio/file";
|
||||
import { blobToBase64, FileData } from "@gradio/upload";
|
||||
import { normalise_file } from "@gradio/upload";
|
||||
import { Block } from "@gradio/atoms";
|
||||
import UploadText from "../UploadText.svelte";
|
||||
import { upload_files } from "@gradio/client";
|
||||
import { upload_files as default_upload_files } from "@gradio/client";
|
||||
|
||||
import StatusTracker from "../StatusTracker/StatusTracker.svelte";
|
||||
import type { LoadingStatus } from "../StatusTracker/types";
|
||||
@ -31,6 +31,10 @@
|
||||
export let scale: number | null = null;
|
||||
export let min_width: number | undefined = undefined;
|
||||
|
||||
const upload_files =
|
||||
getContext<typeof default_upload_files>("upload_files") ??
|
||||
default_upload_files;
|
||||
|
||||
$: _value = normalise_file(value, root, root_url);
|
||||
|
||||
let dragging = false;
|
||||
|
@ -1,8 +1,8 @@
|
||||
<script lang="ts">
|
||||
import { createEventDispatcher, tick } from "svelte";
|
||||
import { createEventDispatcher, tick, getContext } from "svelte";
|
||||
import type { FileData } from "@gradio/upload";
|
||||
import { UploadButton } from "@gradio/upload-button";
|
||||
import { upload_files } from "@gradio/client";
|
||||
import { upload_files as default_upload_files } from "@gradio/client";
|
||||
import { blobToBase64 } from "@gradio/upload";
|
||||
import { _ } from "svelte-i18n";
|
||||
|
||||
@ -20,6 +20,10 @@
|
||||
export let mode: "static" | "dynamic" = "dynamic";
|
||||
export let variant: "primary" | "secondary" | "stop" = "secondary";
|
||||
|
||||
const upload_files =
|
||||
getContext<typeof default_upload_files>("upload_files") ??
|
||||
default_upload_files;
|
||||
|
||||
async function handle_upload({ detail }: CustomEvent<FileData>) {
|
||||
value = detail;
|
||||
await tick();
|
||||
|
36
js/app/src/lite/css.ts
Normal file
36
js/app/src/lite/css.ts
Normal file
@ -0,0 +1,36 @@
|
||||
import type { WorkerProxy } from "@gradio/wasm";
|
||||
import { is_self_origin } from "./url";
|
||||
import { mount_css as default_mount_css } from "../css";
|
||||
|
||||
export async function wasm_proxied_mount_css(
|
||||
worker_proxy: WorkerProxy,
|
||||
url_string: string,
|
||||
target: HTMLElement
|
||||
) {
|
||||
const request = new Request(url_string); // Resolve a relative URL.
|
||||
const url = new URL(request.url);
|
||||
|
||||
if (!is_self_origin(url)) {
|
||||
// Fallback to the default implementation for external resources.
|
||||
return default_mount_css(url_string, target);
|
||||
}
|
||||
|
||||
const response = await worker_proxy.httpRequest({
|
||||
method: "GET",
|
||||
path: url.pathname,
|
||||
query_string: "",
|
||||
headers: {}
|
||||
});
|
||||
const css = new TextDecoder().decode(response.body);
|
||||
|
||||
const existing_link = document.querySelector(
|
||||
`style[data-wasm-path='${url_string}']`
|
||||
);
|
||||
if (existing_link) return;
|
||||
|
||||
const style = document.createElement("style");
|
||||
style.setAttribute("data-wasm-path", url_string);
|
||||
style.textContent = css;
|
||||
// @ts-ignore
|
||||
target.appendChild(style);
|
||||
}
|
54
js/app/src/lite/fetch.ts
Normal file
54
js/app/src/lite/fetch.ts
Normal file
@ -0,0 +1,54 @@
|
||||
import type { WorkerProxy } from "@gradio/wasm";
|
||||
import { is_self_origin } from "./url";
|
||||
|
||||
/**
|
||||
* A fetch() function that proxies HTTP requests to the worker,
|
||||
* which also falls back to the original fetch() for external resource requests.
|
||||
*/
|
||||
export async function wasm_proxied_fetch(
|
||||
workerProxy: WorkerProxy,
|
||||
input: RequestInfo | URL,
|
||||
init?: RequestInit
|
||||
): Promise<Response> {
|
||||
console.debug("wasm_proxied_fetch", input, init);
|
||||
|
||||
const request = new Request(input, init);
|
||||
|
||||
const url = new URL(request.url);
|
||||
|
||||
if (!is_self_origin(url)) {
|
||||
console.debug("Fallback to original fetch");
|
||||
return fetch(input, init);
|
||||
}
|
||||
|
||||
const method = request.method;
|
||||
if (
|
||||
method !== "GET" &&
|
||||
method !== "POST" &&
|
||||
method !== "PUT" &&
|
||||
method !== "DELETE"
|
||||
) {
|
||||
throw new Error(`Unsupported method: ${method}`);
|
||||
}
|
||||
|
||||
const headers: Parameters<WorkerProxy["httpRequest"]>[0]["headers"] = {};
|
||||
request.headers.forEach((value, key) => {
|
||||
headers[key] = value;
|
||||
});
|
||||
|
||||
const bodyArrayBuffer = await new Response(request.body).arrayBuffer();
|
||||
const body: Parameters<WorkerProxy["httpRequest"]>[0]["body"] =
|
||||
new Uint8Array(bodyArrayBuffer);
|
||||
|
||||
const response = await workerProxy.httpRequest({
|
||||
path: url.pathname,
|
||||
query_string: url.search,
|
||||
method,
|
||||
headers,
|
||||
body
|
||||
});
|
||||
return new Response(response.body, {
|
||||
status: response.status,
|
||||
headers: new Headers(response.headers)
|
||||
});
|
||||
}
|
162
js/app/src/lite/index.ts
Normal file
162
js/app/src/lite/index.ts
Normal file
@ -0,0 +1,162 @@
|
||||
import "@gradio/theme";
|
||||
import { WorkerProxy } from "@gradio/wasm";
|
||||
import { api_factory } from "@gradio/client";
|
||||
import { wasm_proxied_fetch } from "./fetch";
|
||||
import { wasm_proxied_mount_css } from "./css";
|
||||
import type { mount_css } from "../css";
|
||||
import Index from "../Index.svelte";
|
||||
import type { ThemeMode } from "../components/types";
|
||||
|
||||
// These imports are aliased at built time with Vite. See the `resolve.alias` config in `vite.config.ts`.
|
||||
import gradioWheel from "gradio.whl";
|
||||
import gradioClientWheel from "gradio_client.whl";
|
||||
|
||||
declare let GRADIO_VERSION: string;
|
||||
|
||||
// NOTE: The following line has been copied from `main.ts`.
|
||||
// In `main.ts`, which is the normal Gradio app entry point,
|
||||
// the string literal "__ENTRY_CSS__" will be replaced with the actual CSS file path
|
||||
// by the Vite plugin `handle_ce_css` in `build_plugins.ts`,
|
||||
// and the CSS file will be dynamically loaded at runtime
|
||||
// as the file path (the `ENTRY_CSS` variable) will be passed to `mount_css()`.
|
||||
// This mechanism has been introduced in https://github.com/gradio-app/gradio/pull/1444
|
||||
// to make Gradio work as a Web Component library
|
||||
// with which users can use Gradio by loading only one JS file,
|
||||
// without a link tag referring to the CSS file.
|
||||
// However, we don't rely on this mechanism here to make things simpler by leaving the Vite plugins as is,
|
||||
// because it will be refactored in the near future.
|
||||
// As a result, the users of the Wasm app will have to load the CSS file manually.
|
||||
// const ENTRY_CSS = "__ENTRY_CSS__";
|
||||
|
||||
interface Options {
|
||||
target: HTMLElement;
|
||||
pyCode: string;
|
||||
info: boolean;
|
||||
container: boolean;
|
||||
isEmbed: boolean;
|
||||
initialHeight?: string;
|
||||
eager: boolean;
|
||||
themeMode: ThemeMode | null;
|
||||
autoScroll: boolean;
|
||||
controlPageTitle: boolean;
|
||||
appMode: boolean;
|
||||
}
|
||||
export async function create(options: Options) {
|
||||
// TODO: Runtime type validation for options.
|
||||
|
||||
const observer = new MutationObserver(() => {
|
||||
document.body.style.padding = "0";
|
||||
});
|
||||
|
||||
observer.observe(options.target, { childList: true });
|
||||
|
||||
const worker_proxy = new WorkerProxy({
|
||||
gradioWheelUrl: gradioWheel,
|
||||
gradioClientWheelUrl: gradioClientWheel,
|
||||
requirements: []
|
||||
});
|
||||
|
||||
// Internally, the execution of `runPythonAsync()` is queued
|
||||
// and its promise will be resolved after the Pyodide is loaded and the worker initialization is done
|
||||
// (see the await in the `onmessage` callback in the webworker code)
|
||||
// So we don't await this promise because we want to mount the `Index` immediately and start the app initialization asynchronously.
|
||||
worker_proxy.runPythonAsync(options.pyCode);
|
||||
|
||||
const overridden_fetch: typeof fetch = (input, init?) => {
|
||||
return wasm_proxied_fetch(worker_proxy, input, init);
|
||||
};
|
||||
const { client, upload_files } = api_factory(overridden_fetch);
|
||||
const overridden_mount_css: typeof mount_css = async (url, target) => {
|
||||
return wasm_proxied_mount_css(worker_proxy, url, target);
|
||||
};
|
||||
|
||||
const app = new Index({
|
||||
target: options.target,
|
||||
props: {
|
||||
// embed source
|
||||
space: null,
|
||||
src: null,
|
||||
host: null,
|
||||
// embed info
|
||||
info: options.info,
|
||||
container: options.container,
|
||||
is_embed: options.isEmbed,
|
||||
initial_height: options.initialHeight ?? "300px", // default: 300px
|
||||
eager: options.eager,
|
||||
// gradio meta info
|
||||
version: GRADIO_VERSION,
|
||||
theme_mode: options.themeMode,
|
||||
// misc global behaviour
|
||||
autoscroll: options.autoScroll,
|
||||
control_page_title: options.controlPageTitle,
|
||||
// for gradio docs
|
||||
// TODO: Remove -- i think this is just for autoscroll behavhiour, app vs embeds
|
||||
app_mode: options.appMode,
|
||||
// For Wasm mode
|
||||
client,
|
||||
upload_files,
|
||||
mount_css: overridden_mount_css
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* I'm not sure if this is a correct way to export functions from a bundle created with Vite.
|
||||
* However, at least, the library mode (https://vitejs.dev/guide/build.html#library-mode)
|
||||
* with an exported function (`export async function create()`) didn't work for our case.
|
||||
* In library mode with the `build.lib.entry = (this file)` config,
|
||||
* Vite creates a bundle exporting the functions from this file, which looks nice,
|
||||
* however, it inevitably enables inlining of all the static file assets,
|
||||
* while we need to disable inlining for the wheel files to pass their URLs to `micropip.install()`.
|
||||
*
|
||||
* > If you specify build.lib, build.assetsInlineLimit will be ignored and assets will always be inlined, regardless of file size or being a Git LFS placeholder.
|
||||
* > https://vitejs.dev/config/build-options.html#build-assetsinlinelimit
|
||||
*
|
||||
* There is an open issue about this: https://github.com/vitejs/vite/issues/4454
|
||||
*
|
||||
* FYI, stlite (https://github.com/whitphx/stlite) uses Webpack,
|
||||
* which supports bundling libraries that export entities to the global scope and disabling assets inlining
|
||||
* (https://webpack.js.org/guides/author-libraries/).
|
||||
*/
|
||||
// @ts-ignore
|
||||
globalThis.createGradioApp = create;
|
||||
|
||||
declare let BUILD_MODE: string;
|
||||
if (BUILD_MODE === "dev") {
|
||||
create({
|
||||
target: document.getElementById("gradio-app")!,
|
||||
pyCode: `
|
||||
import gradio as gr
|
||||
|
||||
def greet(name):
|
||||
return "Hello " + name + "!"
|
||||
|
||||
def upload_file(files):
|
||||
file_paths = [file.name for file in files]
|
||||
return file_paths
|
||||
|
||||
with gr.Blocks() as demo:
|
||||
name = gr.Textbox(label="Name")
|
||||
output = gr.Textbox(label="Output Box")
|
||||
greet_btn = gr.Button("Greet")
|
||||
greet_btn.click(fn=greet, inputs=name, outputs=output, api_name="greet")
|
||||
|
||||
gr.File()
|
||||
|
||||
file_output = gr.File()
|
||||
upload_button = gr.UploadButton("Click to Upload a File", file_types=["image", "video"], file_count="multiple")
|
||||
upload_button.upload(upload_file, upload_button, file_output)
|
||||
|
||||
demo.launch()
|
||||
`,
|
||||
info: true,
|
||||
container: true,
|
||||
isEmbed: false,
|
||||
initialHeight: "300px",
|
||||
eager: false,
|
||||
themeMode: null,
|
||||
autoScroll: false,
|
||||
controlPageTitle: false,
|
||||
appMode: true
|
||||
});
|
||||
}
|
6
js/app/src/lite/url.ts
Normal file
6
js/app/src/lite/url.ts
Normal file
@ -0,0 +1,6 @@
|
||||
export function is_self_origin(url: URL): boolean {
|
||||
return (
|
||||
url.origin === window.location.origin ||
|
||||
url.origin === "http://localhost:7860" // Ref: https://github.com/gradio-app/gradio/blob/v3.32.0/js/app/src/Index.svelte#L194
|
||||
);
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
import "@gradio/theme";
|
||||
import { client, upload_files } from "@gradio/client";
|
||||
import { mount_css } from "./css";
|
||||
import Index from "./Index.svelte";
|
||||
import type { ThemeMode } from "./components/types";
|
||||
@ -93,6 +94,9 @@ function create_custom_element() {
|
||||
// misc global behaviour
|
||||
autoscroll: this.autoscroll === "true" ? true : false,
|
||||
control_page_title: this.control_page_title === "true" ? true : false,
|
||||
// injectables
|
||||
client,
|
||||
upload_files,
|
||||
// for gradio docs
|
||||
// TODO: Remove -- i think this is just for autoscroll behavhiour, app vs embeds
|
||||
app_mode: window.__gradio_mode__ === "app"
|
||||
@ -154,6 +158,9 @@ function create_custom_element() {
|
||||
autoscroll: this.autoscroll === "true" ? true : false,
|
||||
control_page_title:
|
||||
this.control_page_title === "true" ? true : false,
|
||||
// injectables
|
||||
client,
|
||||
upload_files,
|
||||
// for gradio docs
|
||||
// TODO: Remove -- i think this is just for autoscroll behavhiour, app vs embeds
|
||||
app_mode: window.__gradio_mode__ === "app"
|
||||
|
6
js/app/src/vite-env-override.d.ts
vendored
Normal file
6
js/app/src/vite-env-override.d.ts
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
// See https://vitejs.dev/guide/features.html#client-types
|
||||
|
||||
declare module "*.whl" {
|
||||
const content: string;
|
||||
export default content;
|
||||
}
|
@ -7,14 +7,21 @@ import global_data from "@csstools/postcss-global-data";
|
||||
// @ts-ignore
|
||||
import prefixer from "postcss-prefix-selector";
|
||||
import { readFileSync } from "fs";
|
||||
import { join } from "path";
|
||||
import { resolve } from "path";
|
||||
|
||||
const version_path = join(__dirname, "..", "..", "gradio", "version.txt");
|
||||
const theme_token_path = join(__dirname, "..", "theme", "src", "tokens.css");
|
||||
const version_path = resolve(__dirname, "../../gradio/version.txt");
|
||||
const theme_token_path = resolve(__dirname, "../theme/src/tokens.css");
|
||||
const version_raw = readFileSync(version_path, { encoding: "utf-8" }).trim();
|
||||
const version = version_raw.replace(/\./g, "-");
|
||||
|
||||
const version = readFileSync(version_path, { encoding: "utf-8" })
|
||||
.trim()
|
||||
.replace(/\./g, "-");
|
||||
const client_version_path = resolve(
|
||||
__dirname,
|
||||
"../../client/python/gradio_client/version.txt"
|
||||
);
|
||||
const client_version_raw = readFileSync(client_version_path, {
|
||||
encoding: "utf-8"
|
||||
}).trim();
|
||||
const client_version = client_version_raw.replace(/\./g, "-");
|
||||
|
||||
import {
|
||||
inject_ejs,
|
||||
@ -36,21 +43,51 @@ export default defineConfig(({ mode }) => {
|
||||
const production =
|
||||
mode === "production:cdn" ||
|
||||
mode === "production:local" ||
|
||||
mode === "production:website";
|
||||
mode === "production:website" ||
|
||||
mode === "production:lite";
|
||||
const is_cdn = mode === "production:cdn" || mode === "production:website";
|
||||
const is_lite = mode.endsWith(":lite");
|
||||
|
||||
return {
|
||||
base: is_cdn ? CDN_URL : "./",
|
||||
|
||||
server: {
|
||||
port: 9876
|
||||
port: 9876,
|
||||
open: is_lite ? "/lite.html" : "/"
|
||||
},
|
||||
|
||||
build: {
|
||||
sourcemap: true,
|
||||
target: "esnext",
|
||||
minify: production,
|
||||
outDir: `../../gradio/templates/${is_cdn ? "cdn" : "frontend"}`
|
||||
outDir: is_lite
|
||||
? resolve(__dirname, "../lite/dist")
|
||||
: `../../gradio/templates/${is_cdn ? "cdn" : "frontend"}`,
|
||||
// To build Gradio-lite as a library, we can't use the library mode
|
||||
// like `lib: is_lite && {}`
|
||||
// because it inevitably enables inlining of all the static file assets,
|
||||
// while we need to disable inlining for the wheel files to pass their URLs to `micropip.install()`.
|
||||
// So we build it as an app and only use the bundled JS and CSS files as library assets, ignoring the HTML file.
|
||||
// See also `lite.ts` about it.
|
||||
rollupOptions: is_lite && {
|
||||
input: "./lite.html",
|
||||
output: {
|
||||
// To use it as a library, we don't add the hash to the file name.
|
||||
entryFileNames: "lite.js",
|
||||
assetFileNames: (file) => {
|
||||
if (file.name?.endsWith(".whl")) {
|
||||
// Python wheel files must follow the naming rules to be installed, so adding a hash to the name is not allowed.
|
||||
return `assets/[name].[ext]`;
|
||||
}
|
||||
if (file.name === "lite.css") {
|
||||
// To use it as a library, we don't add the hash to the file name.
|
||||
return `[name].[ext]`;
|
||||
} else {
|
||||
return `assets/[name]-[hash].[ext]`;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
define: {
|
||||
BUILD_MODE: production ? JSON.stringify("prod") : JSON.stringify("dev"),
|
||||
@ -114,6 +151,20 @@ export default defineConfig(({ mode }) => {
|
||||
? ["**/*.node-test.{js,mjs,cjs,ts,mts,cts,jsx,tsx}"]
|
||||
: ["**/*.test.{js,mjs,cjs,ts,mts,cts,jsx,tsx}"],
|
||||
globals: true
|
||||
}
|
||||
},
|
||||
resolve: {
|
||||
alias: {
|
||||
// For the Wasm app to import the wheel file URLs.
|
||||
"gradio.whl": resolve(
|
||||
__dirname,
|
||||
`../../dist/gradio-${version_raw}-py3-none-any.whl`
|
||||
),
|
||||
"gradio_client.whl": resolve(
|
||||
__dirname,
|
||||
`../../client/python/dist/gradio_client-${client_version_raw}-py3-none-any.whl`
|
||||
)
|
||||
}
|
||||
},
|
||||
assetsInclude: ["**/*.whl"] // To pass URLs of built wheel files to the Wasm worker.
|
||||
};
|
||||
});
|
||||
|
72
js/lite/index.html
Normal file
72
js/lite/index.html
Normal file
@ -0,0 +1,72 @@
|
||||
<!DOCTYPE html>
|
||||
<!-- A demo HTML file to test the bundled JS and CSS files -->
|
||||
<html
|
||||
lang="en"
|
||||
style="
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
min-height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
"
|
||||
>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta
|
||||
name="viewport"
|
||||
content="width=device-width, initial-scale=1, shrink-to-fit=no, maximum-scale=1"
|
||||
/>
|
||||
|
||||
<script type="module" crossorigin src="./dist/lite.js"></script>
|
||||
<link rel="stylesheet" href="./dist/lite.css" />
|
||||
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link
|
||||
rel="preconnect"
|
||||
href="https://fonts.gstatic.com"
|
||||
crossorigin="anonymous"
|
||||
/>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/iframe-resizer/4.3.1/iframeResizer.contentWindow.min.js"></script>
|
||||
</head>
|
||||
|
||||
<body
|
||||
style="
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-grow: 1;
|
||||
"
|
||||
>
|
||||
<div id="gradio-app"></div>
|
||||
</body>
|
||||
|
||||
<script>
|
||||
window.addEventListener("DOMContentLoaded", function () {
|
||||
// <script type="module" /> is loaded asynchronously, so we need to wait for it to load before we can use it.
|
||||
createGradioApp({
|
||||
target: document.getElementById("gradio-app"),
|
||||
pyCode: `
|
||||
import gradio as gr
|
||||
|
||||
def greet(name):
|
||||
return "Hello " + name + "!"
|
||||
|
||||
demo = gr.Interface(fn=greet, inputs="text", outputs="text")
|
||||
|
||||
demo.launch()
|
||||
`,
|
||||
info: true,
|
||||
container: true,
|
||||
isEmbed: false,
|
||||
initialHeight: "300px",
|
||||
eager: false,
|
||||
themeMode: null,
|
||||
autoScroll: false,
|
||||
controlPageTitle: false,
|
||||
appMode: true
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</html>
|
22
js/wasm/package.json
Normal file
22
js/wasm/package.json
Normal file
@ -0,0 +1,22 @@
|
||||
{
|
||||
"name": "@gradio/wasm",
|
||||
"version": "0.0.1",
|
||||
"description": "Gradio Wasm package",
|
||||
"type": "module",
|
||||
"main": "dist/index.js",
|
||||
"private": true,
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"scripts": {
|
||||
"start:client": "tsc -w --incremental",
|
||||
"start:worker": "vite build --config vite.worker.config.js --watch --emptyOutDir=false",
|
||||
"start": "run-p start:*",
|
||||
"build:client": "tsc",
|
||||
"build:worker": "vite build --config vite.worker.config.js",
|
||||
"build": "run-s build:worker build:client"
|
||||
},
|
||||
"devDependencies": {
|
||||
"pyodide": "^0.23.2"
|
||||
}
|
||||
}
|
1
js/wasm/src/index.ts
Normal file
1
js/wasm/src/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export { WorkerProxy } from "./worker-proxy";
|
62
js/wasm/src/message-types.ts
Normal file
62
js/wasm/src/message-types.ts
Normal file
@ -0,0 +1,62 @@
|
||||
export interface HttpRequest {
|
||||
method: "GET" | "POST" | "PUT" | "DELETE";
|
||||
path: string;
|
||||
query_string: string;
|
||||
headers: Record<string, string>;
|
||||
body?: Uint8Array;
|
||||
}
|
||||
|
||||
export interface HttpResponse {
|
||||
status: number;
|
||||
headers: Record<string, string>;
|
||||
body: Uint8Array;
|
||||
}
|
||||
|
||||
export interface InMessageBase {
|
||||
type: string;
|
||||
data: unknown;
|
||||
}
|
||||
|
||||
export interface InMessageInit extends InMessageBase {
|
||||
type: "init";
|
||||
data: {
|
||||
gradioWheelUrl: string;
|
||||
gradioClientWheelUrl: string;
|
||||
requirements: string[];
|
||||
};
|
||||
}
|
||||
export interface InMessageRunPython extends InMessageBase {
|
||||
type: "run-python";
|
||||
data: {
|
||||
code: string;
|
||||
};
|
||||
}
|
||||
export interface InMessageHttpRequest extends InMessageBase {
|
||||
type: "http-request";
|
||||
data: {
|
||||
request: HttpRequest;
|
||||
};
|
||||
}
|
||||
|
||||
export interface InMessageEcho extends InMessageBase {
|
||||
// For debug
|
||||
type: "echo";
|
||||
data: unknown;
|
||||
}
|
||||
|
||||
export type InMessage =
|
||||
| InMessageInit
|
||||
| InMessageRunPython
|
||||
| InMessageHttpRequest
|
||||
| InMessageEcho;
|
||||
|
||||
export interface ReplyMessageSuccess<T = unknown> {
|
||||
type: "reply:success";
|
||||
data: T;
|
||||
}
|
||||
export interface ReplyMessageError {
|
||||
type: "reply:error";
|
||||
error: Error;
|
||||
}
|
||||
|
||||
export type ReplyMessage = ReplyMessageSuccess | ReplyMessageError;
|
2
js/wasm/src/webworker/declarations.d.ts
vendored
Normal file
2
js/wasm/src/webworker/declarations.d.ts
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
// Declarations for the WebWorker files where some variables are dynamically loaded through importScript.
|
||||
declare let loadPyodide: any;
|
135
js/wasm/src/webworker/http.ts
Normal file
135
js/wasm/src/webworker/http.ts
Normal file
@ -0,0 +1,135 @@
|
||||
import type { PyProxy } from "pyodide/ffi";
|
||||
import type { HttpRequest, HttpResponse } from "../message-types";
|
||||
|
||||
// Inspired by https://github.com/rstudio/shinylive/blob/v0.1.2/src/messageporthttp.ts
|
||||
|
||||
// A reference to an ASGI application instance in Python
|
||||
// Ref: https://asgi.readthedocs.io/en/latest/specs/main.html#applications
|
||||
type ASGIApplication = (
|
||||
scope: Record<string, unknown>,
|
||||
receive: () => Promise<ReceiveEvent>,
|
||||
send: (event: PyProxy) => Promise<void>
|
||||
) => Promise<void>;
|
||||
|
||||
type ReceiveEvent = RequestReceiveEvent | DisconnectReceiveEvent;
|
||||
// https://asgi.readthedocs.io/en/latest/specs/www.html#request-receive-event
|
||||
interface RequestReceiveEvent {
|
||||
type: "http.request";
|
||||
body?: Uint8Array; // `bytes` in Python
|
||||
more_body: boolean;
|
||||
}
|
||||
// https://asgi.readthedocs.io/en/latest/specs/www.html#disconnect-receive-event
|
||||
interface DisconnectReceiveEvent {
|
||||
type: "http.disconnect";
|
||||
}
|
||||
|
||||
type SendEvent = ResponseStartSendEvent | ResponseBodySendEvent;
|
||||
// https://asgi.readthedocs.io/en/latest/specs/www.html#response-start-send-event
|
||||
interface ResponseStartSendEvent {
|
||||
type: "http.response.start";
|
||||
status: number;
|
||||
headers: Iterable<[Uint8Array, Uint8Array]>;
|
||||
trailers: boolean;
|
||||
}
|
||||
// https://asgi.readthedocs.io/en/latest/specs/www.html#response-body-send-event
|
||||
interface ResponseBodySendEvent {
|
||||
type: "http.response.body";
|
||||
body: Uint8Array; // `bytes` in Python
|
||||
more_body: boolean;
|
||||
}
|
||||
|
||||
function headersToASGI(
|
||||
headers: HttpRequest["headers"]
|
||||
): Array<[string, string]> {
|
||||
const result: Array<[string, string]> = [];
|
||||
for (const [key, value] of Object.entries(headers)) {
|
||||
result.push([key, value]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
export function uint8ArrayToString(buf: Uint8Array): string {
|
||||
let result = "";
|
||||
for (let i = 0; i < buf.length; i++) {
|
||||
result += String.fromCharCode(buf[i]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function asgiHeadersToRecord(headers: any): Record<string, string> {
|
||||
headers = headers.map(([key, val]: [Uint8Array, Uint8Array]) => {
|
||||
return [uint8ArrayToString(key), uint8ArrayToString(val)];
|
||||
});
|
||||
return Object.fromEntries(headers);
|
||||
}
|
||||
|
||||
export const makeHttpRequest = (
|
||||
asgiApp: ASGIApplication,
|
||||
request: HttpRequest
|
||||
): Promise<HttpResponse> =>
|
||||
new Promise((resolve, reject) => {
|
||||
let sent = false;
|
||||
async function receiveFromJs(): Promise<ReceiveEvent> {
|
||||
if (sent) {
|
||||
// NOTE: I implemented this block just referring to the spec. However, it is not reached in practice so it's not combat-proven.
|
||||
return {
|
||||
type: "http.disconnect"
|
||||
};
|
||||
}
|
||||
|
||||
const event: RequestReceiveEvent = {
|
||||
type: "http.request",
|
||||
more_body: false
|
||||
};
|
||||
if (request.body) {
|
||||
event.body = request.body;
|
||||
}
|
||||
|
||||
console.debug("receive", event);
|
||||
sent = true;
|
||||
return event;
|
||||
}
|
||||
|
||||
let status: number;
|
||||
let headers: { [key: string]: string };
|
||||
let body: Uint8Array = new Uint8Array();
|
||||
async function sendToJs(proxiedEvent: PyProxy): Promise<void> {
|
||||
const event = Object.fromEntries(proxiedEvent.toJs()) as SendEvent;
|
||||
console.debug("send", event);
|
||||
if (event.type === "http.response.start") {
|
||||
status = event.status;
|
||||
headers = asgiHeadersToRecord(event.headers);
|
||||
} else if (event.type === "http.response.body") {
|
||||
body = new Uint8Array([...body, ...event.body]);
|
||||
if (!event.more_body) {
|
||||
const response: HttpResponse = {
|
||||
status,
|
||||
headers,
|
||||
body
|
||||
};
|
||||
console.debug("HTTP response", response);
|
||||
resolve(response);
|
||||
}
|
||||
} else {
|
||||
throw new Error(`Unhandled ASGI event: ${JSON.stringify(event)}`);
|
||||
}
|
||||
}
|
||||
|
||||
// https://asgi.readthedocs.io/en/latest/specs/www.html#http-connection-scope
|
||||
const scope = {
|
||||
type: "http",
|
||||
asgi: {
|
||||
version: "3.0",
|
||||
spec_version: "2.1"
|
||||
},
|
||||
http_version: "1.1",
|
||||
scheme: "http",
|
||||
method: request.method,
|
||||
path: request.path,
|
||||
query_string: request.query_string,
|
||||
root_path: "",
|
||||
headers: headersToASGI(request.headers)
|
||||
};
|
||||
|
||||
asgiApp(scope, receiveFromJs, sendToJs);
|
||||
});
|
205
js/wasm/src/webworker/index.ts
Normal file
205
js/wasm/src/webworker/index.ts
Normal file
@ -0,0 +1,205 @@
|
||||
/// <reference lib="webworker" />
|
||||
|
||||
import type { PyodideInterface } from "pyodide";
|
||||
import type {
|
||||
InMessage,
|
||||
ReplyMessageError,
|
||||
ReplyMessageSuccess
|
||||
} from "../message-types";
|
||||
import { makeHttpRequest } from "./http";
|
||||
|
||||
importScripts("https://cdn.jsdelivr.net/pyodide/v0.23.2/full/pyodide.js");
|
||||
|
||||
let pyodide: PyodideInterface;
|
||||
|
||||
let pyodideReadyPromise: undefined | Promise<void> = undefined;
|
||||
|
||||
let call_asgi_app_from_js: (
|
||||
scope: unknown,
|
||||
receive: Function,
|
||||
send: Function
|
||||
) => Promise<void>;
|
||||
|
||||
interface InitOptions {
|
||||
gradioWheelUrl: string;
|
||||
gradioClientWheelUrl: string;
|
||||
requirements: string[];
|
||||
}
|
||||
async function loadPyodideAndPackages(options: InitOptions) {
|
||||
console.debug("Loading Pyodide.");
|
||||
pyodide = await loadPyodide({
|
||||
stdout: console.log,
|
||||
stderr: console.error
|
||||
});
|
||||
console.debug("Pyodide is loaded.");
|
||||
|
||||
console.debug("Loading micropip");
|
||||
await pyodide.loadPackage("micropip");
|
||||
const micropip = pyodide.pyimport("micropip");
|
||||
console.debug("micropip is loaded.");
|
||||
|
||||
const gradioWheelUrls = [
|
||||
options.gradioWheelUrl,
|
||||
options.gradioClientWheelUrl
|
||||
];
|
||||
console.debug("Loading Gradio wheels.", gradioWheelUrls);
|
||||
await micropip.add_mock_package("ffmpy", "0.3.0");
|
||||
await micropip.add_mock_package("orjson", "3.8.12");
|
||||
await micropip.add_mock_package("aiohttp", "3.8.4");
|
||||
await micropip.add_mock_package("multidict", "4.7.6");
|
||||
await pyodide.loadPackage(["ssl", "distutils", "setuptools"]);
|
||||
await micropip.install(["markdown-it-py~=2.2.0"]); // On 3rd June 2023, markdown-it-py 3.0.0 has been released. The `gradio` package depends on its `>=2.0.0` version so its 3.x will be resolved. However, it conflicts with `mdit-py-plugins`'s dependency `markdown-it-py >=1.0.0,<3.0.0` and micropip currently can't resolve it. So we explicitly install the compatible version of the library here.
|
||||
await micropip.install.callKwargs(gradioWheelUrls, {
|
||||
keep_going: true
|
||||
});
|
||||
console.debug("Gradio wheels are loaded.");
|
||||
|
||||
console.debug("Install packages.", options.requirements);
|
||||
await micropip.install.callKwargs(options.requirements, { keep_going: true });
|
||||
console.debug("Packages are installed.");
|
||||
|
||||
console.debug("Mock os module methods.");
|
||||
// `os.link` is used in `aiofiles` (https://github.com/Tinche/aiofiles/blob/v23.1.0/src/aiofiles/os.py#L31),
|
||||
// which is imported from `gradio.ranged_response` (https://github.com/gradio-app/gradio/blob/v3.32.0/gradio/ranged_response.py#L12).
|
||||
// However, it's not available on Wasm.
|
||||
await pyodide.runPythonAsync(`
|
||||
import os
|
||||
|
||||
os.link = lambda src, dst: None
|
||||
`);
|
||||
console.debug("os module methods are mocked.");
|
||||
|
||||
console.debug("Import gradio package.");
|
||||
// Importing the gradio package takes a long time, so we do it separately.
|
||||
// This is necessary for accurate performance profiling.
|
||||
await pyodide.runPythonAsync(`import gradio`);
|
||||
console.debug("gradio package is imported.");
|
||||
|
||||
console.debug("Define a ASGI wrapper function.");
|
||||
// TODO: Unlike Streamlit, user's code is executed in the global scope,
|
||||
// so we should not define this function in the global scope.
|
||||
await pyodide.runPythonAsync(`
|
||||
# Based on Shiny's App.call_pyodide().
|
||||
# https://github.com/rstudio/py-shiny/blob/v0.3.3/shiny/_app.py#L224-L258
|
||||
async def _call_asgi_app_from_js(scope, receive, send):
|
||||
# TODO: Pretty sure there are objects that need to be destroy()'d here?
|
||||
scope = scope.to_py()
|
||||
|
||||
# ASGI requires some values to be byte strings, not character strings. Those are
|
||||
# not that easy to create in JavaScript, so we let the JS side pass us strings
|
||||
# and we convert them to bytes here.
|
||||
if "headers" in scope:
|
||||
# JS doesn't have \`bytes\` so we pass as strings and convert here
|
||||
scope["headers"] = [
|
||||
[value.encode("latin-1") for value in header]
|
||||
for header in scope["headers"]
|
||||
]
|
||||
if "query_string" in scope and scope["query_string"]:
|
||||
scope["query_string"] = scope["query_string"].encode("latin-1")
|
||||
if "raw_path" in scope and scope["raw_path"]:
|
||||
scope["raw_path"] = scope["raw_path"].encode("latin-1")
|
||||
|
||||
async def rcv():
|
||||
event = await receive()
|
||||
return event.to_py()
|
||||
|
||||
async def snd(event):
|
||||
await send(event)
|
||||
|
||||
app = gradio.wasm_utils.get_registered_app()
|
||||
if app is None:
|
||||
raise RuntimeError("Gradio app has not been launched.")
|
||||
|
||||
await app(scope, rcv, snd)
|
||||
`);
|
||||
call_asgi_app_from_js = pyodide.globals.get("_call_asgi_app_from_js");
|
||||
console.debug("The ASGI wrapper function is defined.");
|
||||
|
||||
console.debug("Mock async libraries.");
|
||||
// FastAPI uses `anyio.to_thread.run_sync` internally which, however, doesn't work in Wasm environments where the `threading` module is not supported.
|
||||
// So we mock `anyio.to_thread.run_sync` here not to use threads.
|
||||
await pyodide.runPythonAsync(`
|
||||
async def mocked_anyio_to_thread_run_sync(func, *args, cancellable=False, limiter=None):
|
||||
return func(*args)
|
||||
|
||||
import anyio.to_thread
|
||||
anyio.to_thread.run_sync = mocked_anyio_to_thread_run_sync
|
||||
`);
|
||||
console.debug("Async libraries are mocked.");
|
||||
|
||||
console.debug("Set matplotlib backend.");
|
||||
// Ref: https://github.com/streamlit/streamlit/blob/1.22.0/lib/streamlit/web/bootstrap.py#L111
|
||||
// This backend setting is required to use matplotlib in Wasm environment.
|
||||
await pyodide.runPythonAsync(`
|
||||
import matplotlib
|
||||
matplotlib.use("agg")
|
||||
`);
|
||||
console.debug("matplotlib backend is set.");
|
||||
}
|
||||
|
||||
self.onmessage = async (event: MessageEvent<InMessage>) => {
|
||||
const msg = event.data;
|
||||
console.debug("worker.onmessage", msg);
|
||||
|
||||
const messagePort = event.ports[0];
|
||||
|
||||
try {
|
||||
if (msg.type === "init") {
|
||||
pyodideReadyPromise = loadPyodideAndPackages({
|
||||
gradioWheelUrl: msg.data.gradioWheelUrl,
|
||||
gradioClientWheelUrl: msg.data.gradioClientWheelUrl,
|
||||
requirements: msg.data.requirements
|
||||
});
|
||||
|
||||
const replyMessage: ReplyMessageSuccess = {
|
||||
type: "reply:success",
|
||||
data: null
|
||||
};
|
||||
messagePort.postMessage(replyMessage);
|
||||
}
|
||||
|
||||
if (pyodideReadyPromise == null) {
|
||||
throw new Error("Pyodide Initialization is not started.");
|
||||
}
|
||||
|
||||
await pyodideReadyPromise;
|
||||
|
||||
switch (msg.type) {
|
||||
case "echo": {
|
||||
const replyMessage: ReplyMessageSuccess = {
|
||||
type: "reply:success",
|
||||
data: msg.data
|
||||
};
|
||||
messagePort.postMessage(replyMessage);
|
||||
break;
|
||||
}
|
||||
case "run-python": {
|
||||
await pyodide.runPythonAsync(msg.data.code);
|
||||
const replyMessage: ReplyMessageSuccess = {
|
||||
type: "reply:success",
|
||||
data: null // We don't send back the execution result because it's not needed for our purpose, and sometimes the result is of type `pyodide.ffi.PyProxy` which cannot be cloned across threads and causes an error.
|
||||
};
|
||||
messagePort.postMessage(replyMessage);
|
||||
break;
|
||||
}
|
||||
case "http-request": {
|
||||
const request = msg.data.request;
|
||||
const response = await makeHttpRequest(call_asgi_app_from_js, request);
|
||||
const replyMessage: ReplyMessageSuccess = {
|
||||
type: "reply:success",
|
||||
data: {
|
||||
response
|
||||
}
|
||||
};
|
||||
messagePort.postMessage(replyMessage);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
const replyMessage: ReplyMessageError = {
|
||||
type: "reply:error",
|
||||
error: error as Error
|
||||
};
|
||||
messagePort.postMessage(replyMessage);
|
||||
}
|
||||
};
|
104
js/wasm/src/worker-proxy.ts
Normal file
104
js/wasm/src/worker-proxy.ts
Normal file
@ -0,0 +1,104 @@
|
||||
import type {
|
||||
HttpRequest,
|
||||
HttpResponse,
|
||||
InMessage,
|
||||
ReplyMessage
|
||||
} from "./message-types";
|
||||
|
||||
export interface WorkerProxyOptions {
|
||||
gradioWheelUrl: string;
|
||||
gradioClientWheelUrl: string;
|
||||
requirements: string[];
|
||||
}
|
||||
|
||||
export class WorkerProxy {
|
||||
private worker: Worker;
|
||||
|
||||
constructor(options: WorkerProxyOptions) {
|
||||
console.debug("WorkerProxy.constructor(): Create a new worker.");
|
||||
// Loading a worker here relies on Vite's support for WebWorkers (https://vitejs.dev/guide/features.html#web-workers),
|
||||
// assuming that this module is imported from the Gradio frontend (`@gradio/app`), which is bundled with Vite.
|
||||
this.worker = new Worker(new URL("./webworker.js", import.meta.url));
|
||||
|
||||
this.postMessageAsync({
|
||||
type: "init",
|
||||
data: {
|
||||
gradioWheelUrl: options.gradioWheelUrl,
|
||||
gradioClientWheelUrl: options.gradioClientWheelUrl,
|
||||
requirements: options.requirements
|
||||
}
|
||||
}).then(() => {
|
||||
console.debug("WorkerProxy.constructor(): Initialization is done.");
|
||||
});
|
||||
}
|
||||
|
||||
public async runPythonAsync(code: string): Promise<void> {
|
||||
await this.postMessageAsync({
|
||||
type: "run-python",
|
||||
data: {
|
||||
code
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// A wrapper for this.worker.postMessage(). Unlike that function, which
|
||||
// returns void immediately, this function returns a promise, which resolves
|
||||
// when a ReplyMessage is received from the worker.
|
||||
// The original implementation is in https://github.com/rstudio/shinylive/blob/v0.1.2/src/pyodide-proxy.ts#L404-L418
|
||||
private postMessageAsync(msg: InMessage): Promise<unknown> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const channel = new MessageChannel();
|
||||
|
||||
channel.port1.onmessage = (e) => {
|
||||
channel.port1.close();
|
||||
const msg = e.data as ReplyMessage;
|
||||
if (msg.type === "reply:error") {
|
||||
reject(msg.error);
|
||||
return;
|
||||
}
|
||||
|
||||
resolve(msg.data);
|
||||
};
|
||||
|
||||
this.worker.postMessage(msg, [channel.port2]);
|
||||
});
|
||||
}
|
||||
|
||||
public async httpRequest(request: HttpRequest): Promise<HttpResponse> {
|
||||
console.debug("WorkerProxy.httpRequest()", request);
|
||||
const result = await this.postMessageAsync({
|
||||
type: "http-request",
|
||||
data: {
|
||||
request
|
||||
}
|
||||
});
|
||||
const response = (result as { response: HttpResponse }).response;
|
||||
|
||||
if (Math.floor(response.status / 100) !== 2) {
|
||||
let bodyText: string;
|
||||
let bodyJson: unknown;
|
||||
try {
|
||||
bodyText = new TextDecoder().decode(response.body);
|
||||
} catch (e) {
|
||||
bodyText = "(failed to decode body)";
|
||||
}
|
||||
try {
|
||||
bodyJson = JSON.parse(bodyText);
|
||||
} catch (e) {
|
||||
bodyJson = "(failed to parse body as JSON)";
|
||||
}
|
||||
console.error("Wasm HTTP error", {
|
||||
request,
|
||||
response,
|
||||
bodyText,
|
||||
bodyJson
|
||||
});
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
public terminate(): void {
|
||||
this.worker.terminate();
|
||||
}
|
||||
}
|
105
js/wasm/tsconfig.json
Normal file
105
js/wasm/tsconfig.json
Normal file
@ -0,0 +1,105 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
/* Visit https://aka.ms/tsconfig to read more about this file */
|
||||
|
||||
/* Projects */
|
||||
// "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */
|
||||
// "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
|
||||
// "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */
|
||||
// "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */
|
||||
// "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
|
||||
// "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
|
||||
|
||||
/* Language and Environment */
|
||||
"target": "es2016" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */,
|
||||
// "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
|
||||
// "jsx": "preserve", /* Specify what JSX code is generated. */
|
||||
// "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */
|
||||
// "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
|
||||
// "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */
|
||||
// "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
|
||||
// "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */
|
||||
// "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */
|
||||
// "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
|
||||
// "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
|
||||
// "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
|
||||
|
||||
/* Modules */
|
||||
"module": "ESNext" /* Specify what module code is generated. */,
|
||||
// "rootDir": "./", /* Specify the root folder within your source files. */
|
||||
// "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */
|
||||
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
|
||||
// "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
|
||||
// "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
|
||||
// "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */
|
||||
// "types": [], /* Specify type package names to be included without being referenced in a source file. */
|
||||
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
|
||||
// "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */
|
||||
// "resolveJsonModule": true, /* Enable importing .json files. */
|
||||
// "noResolve": true, /* Disallow 'import's, 'require's or '<reference>'s from expanding the number of files TypeScript should add to a project. */
|
||||
|
||||
/* JavaScript Support */
|
||||
// "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */
|
||||
// "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */
|
||||
// "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */
|
||||
|
||||
/* Emit */
|
||||
"declaration": true /* Generate .d.ts files from TypeScript and JavaScript files in your project. */,
|
||||
"declarationMap": true /* Create sourcemaps for d.ts files. */,
|
||||
// "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
|
||||
// "sourceMap": true, /* Create source map files for emitted JavaScript files. */
|
||||
// "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */
|
||||
"outDir": "./dist" /* Specify an output folder for all emitted files. */,
|
||||
// "removeComments": true, /* Disable emitting comments. */
|
||||
// "noEmit": true, /* Disable emitting files from a compilation. */
|
||||
// "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
|
||||
// "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */
|
||||
// "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
|
||||
// "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */
|
||||
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
|
||||
// "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
|
||||
// "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */
|
||||
// "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
|
||||
// "newLine": "crlf", /* Set the newline character for emitting files. */
|
||||
// "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */
|
||||
// "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */
|
||||
// "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
|
||||
// "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */
|
||||
// "declarationDir": "./", /* Specify the output directory for generated declaration files. */
|
||||
// "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */
|
||||
|
||||
/* Interop Constraints */
|
||||
// "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
|
||||
// "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
|
||||
"esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */,
|
||||
// "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
|
||||
"forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */,
|
||||
|
||||
/* Type Checking */
|
||||
"strict": true /* Enable all strict type-checking options. */,
|
||||
// "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */
|
||||
// "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */
|
||||
// "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
|
||||
// "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */
|
||||
// "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
|
||||
// "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */
|
||||
// "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */
|
||||
// "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
|
||||
// "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */
|
||||
// "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */
|
||||
// "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
|
||||
// "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
|
||||
// "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
|
||||
// "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */
|
||||
// "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
|
||||
// "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */
|
||||
// "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
|
||||
// "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
|
||||
|
||||
/* Completeness */
|
||||
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
|
||||
"skipLibCheck": true /* Skip type checking all .d.ts files. */
|
||||
},
|
||||
"include": ["src/**/*"],
|
||||
"exclude": ["src/webworker/**/*"] // The worker code is bundled by Vite separately. See its config file.
|
||||
}
|
34
js/wasm/vite.worker.config.js
Normal file
34
js/wasm/vite.worker.config.js
Normal file
@ -0,0 +1,34 @@
|
||||
import path from "path";
|
||||
import { defineConfig } from "vite";
|
||||
|
||||
/**
|
||||
* We bundle the worker file before packaging, while other files are only TS-transpiled.
|
||||
* The consumer of this package, `@gradio/app`, will be bundled with Vite,
|
||||
* and Vite only supports module-type WebWorkers (`new Worker("...", { type: "module" })`) to handle `import` in the worker file,
|
||||
* because in the dev mode it doesn't bundle the worker file and just relies on the browser's native support for module-type workers to resolve the imports.
|
||||
* However, we need to use `importScripts()` in the worker to load Pyodide from the CDN, which is only supported by classic WebWorkers (`new Worker("...")`),
|
||||
* while we still want to use `import` in the worker to modularize the code.
|
||||
* So, we bundle the worker file to resolve `import`s here before exporting, preserving `importScripts()` in the bundled file,
|
||||
* and load the bundled worker file on `@gradio/app` as a classic WebWorker.
|
||||
*
|
||||
* Note: We tried the following approaches, but they failed:
|
||||
* 1. Just TS-transpile the worker file like other files into `worker.js`, and use it like `new Worker("worker.js")`.
|
||||
* It failed because `tsc` reserves `importScripts()` and also appends `export {};` to the end of the file to specify it as a module (`https://github.com/microsoft/TypeScript/issues/41513`),
|
||||
* however, `importScripts()` is only supported by classic WebWorkers, and `export {};` is not supported by classic WebWorkers.
|
||||
* 2. Use ESM import instead of `importScripts()`, which is (experimentally?) supported by Pyodide since v0.20.0 (https://pyodide.org/en/stable/project/changelog.html#javascript-package),
|
||||
* using `import { loadPyodide } from "https://cdn.jsdelivr.net/pyodide/v0.23.2/full/pyodide.js";` in the worker file, instead of `importScripts(...)`.
|
||||
* It was successful in the dev mode, but failed in the prod mode, which has this problem: https://github.com/pyodide/pyodide/issues/2217#issuecomment-1328344562.
|
||||
*/
|
||||
|
||||
export default defineConfig({
|
||||
build: {
|
||||
outDir: "dist",
|
||||
rollupOptions: {
|
||||
input: path.join(__dirname, "src/webworker/index.ts"),
|
||||
// Ref: https://github.com/rollup/rollup/issues/2616#issuecomment-1431551704
|
||||
output: {
|
||||
entryFileNames: "webworker.js"
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
215
pnpm-lock.yaml
215
pnpm-lock.yaml
@ -46,10 +46,10 @@ importers:
|
||||
version: 20.3.1
|
||||
'@typescript-eslint/eslint-plugin':
|
||||
specifier: ^5.60.0
|
||||
version: 5.60.0(@typescript-eslint/parser@5.60.0)(eslint@8.43.0)(typescript@5.1.3)
|
||||
version: 5.60.0(@typescript-eslint/parser@5.60.0)(eslint@8.43.0)(typescript@5.0.4)
|
||||
'@typescript-eslint/parser':
|
||||
specifier: ^5.60.0
|
||||
version: 5.60.0(eslint@8.43.0)(typescript@5.1.3)
|
||||
version: 5.60.0(eslint@8.43.0)(typescript@5.0.4)
|
||||
autoprefixer:
|
||||
specifier: ^10.4.4
|
||||
version: 10.4.4(postcss@8.4.6)
|
||||
@ -76,7 +76,7 @@ importers:
|
||||
version: 4.1.5
|
||||
msw:
|
||||
specifier: ^1.0.0
|
||||
version: 1.0.0(typescript@5.1.3)
|
||||
version: 1.0.0(typescript@5.0.4)
|
||||
node-html-parser:
|
||||
specifier: ^6.0.0
|
||||
version: 6.0.0
|
||||
@ -133,16 +133,16 @@ importers:
|
||||
version: 3.6.0(svelte@3.59.1)
|
||||
svelte-preprocess:
|
||||
specifier: ^5.0.3
|
||||
version: 5.0.3(postcss@8.4.6)(svelte@3.59.1)(typescript@5.1.3)
|
||||
version: 5.0.3(postcss@8.4.6)(svelte@3.59.1)(typescript@5.0.4)
|
||||
tailwindcss:
|
||||
specifier: ^3.1.6
|
||||
version: 3.1.6(postcss@8.4.6)
|
||||
tinyspy:
|
||||
specifier: ^2.0.0
|
||||
version: 2.1.1
|
||||
version: 2.0.0
|
||||
typescript:
|
||||
specifier: ^5.0.0
|
||||
version: 5.1.3
|
||||
version: 5.0.4
|
||||
vite:
|
||||
specifier: ^4.3.9
|
||||
version: 4.3.9(@types/node@20.3.1)
|
||||
@ -301,6 +301,9 @@ importers:
|
||||
'@gradio/video':
|
||||
specifier: workspace:^0.0.1
|
||||
version: link:../video
|
||||
'@gradio/wasm':
|
||||
specifier: workspace:^0.0.1
|
||||
version: link:../wasm
|
||||
'@playwright/test':
|
||||
specifier: ^1.35.1
|
||||
version: 1.35.1
|
||||
@ -723,6 +726,12 @@ importers:
|
||||
specifier: workspace:^0.0.1
|
||||
version: link:../upload
|
||||
|
||||
js/wasm:
|
||||
devDependencies:
|
||||
pyodide:
|
||||
specifier: ^0.23.2
|
||||
version: 0.23.2
|
||||
|
||||
packages:
|
||||
|
||||
/@adobe/css-tools@4.2.0:
|
||||
@ -1548,13 +1557,13 @@ packages:
|
||||
resolution: {integrity: sha512-EBikYFp2JCdIfGEb5G9dyCkTGDmC57KSHhRQOC3aYxoPWVZvfWCDjZwkGYHN7Lis/fmuWl906bnNTJifDQ3sXw==}
|
||||
dependencies:
|
||||
'@formatjs/intl-localematcher': 0.2.25
|
||||
tslib: 2.5.3
|
||||
tslib: 2.4.0
|
||||
dev: false
|
||||
|
||||
/@formatjs/fast-memoize@1.2.1:
|
||||
resolution: {integrity: sha512-Rg0e76nomkz3vF9IPlKeV+Qynok0r7YZjL6syLz4/urSg0IbjPZCB/iYUMNsYA643gh4mgrX3T7KEIFIxJBQeg==}
|
||||
dependencies:
|
||||
tslib: 2.5.3
|
||||
tslib: 2.4.0
|
||||
dev: false
|
||||
|
||||
/@formatjs/icu-messageformat-parser@2.1.0:
|
||||
@ -1562,20 +1571,20 @@ packages:
|
||||
dependencies:
|
||||
'@formatjs/ecma402-abstract': 1.11.4
|
||||
'@formatjs/icu-skeleton-parser': 1.3.6
|
||||
tslib: 2.5.3
|
||||
tslib: 2.4.0
|
||||
dev: false
|
||||
|
||||
/@formatjs/icu-skeleton-parser@1.3.6:
|
||||
resolution: {integrity: sha512-I96mOxvml/YLrwU2Txnd4klA7V8fRhb6JG/4hm3VMNmeJo1F03IpV2L3wWt7EweqNLES59SZ4d6hVOPCSf80Bg==}
|
||||
dependencies:
|
||||
'@formatjs/ecma402-abstract': 1.11.4
|
||||
tslib: 2.5.3
|
||||
tslib: 2.4.0
|
||||
dev: false
|
||||
|
||||
/@formatjs/intl-localematcher@0.2.25:
|
||||
resolution: {integrity: sha512-YmLcX70BxoSopLFdLr1Ds99NdlTI2oWoLbaUW2M406lxOIPzE1KQhRz2fPUkq34xVZQaihCoU29h0KK7An3bhA==}
|
||||
dependencies:
|
||||
tslib: 2.5.3
|
||||
tslib: 2.4.0
|
||||
dev: false
|
||||
|
||||
/@humanwhocodes/config-array@0.11.10:
|
||||
@ -2184,7 +2193,7 @@ packages:
|
||||
'@types/yargs-parser': 21.0.0
|
||||
dev: false
|
||||
|
||||
/@typescript-eslint/eslint-plugin@5.60.0(@typescript-eslint/parser@5.60.0)(eslint@8.43.0)(typescript@5.1.3):
|
||||
/@typescript-eslint/eslint-plugin@5.60.0(@typescript-eslint/parser@5.60.0)(eslint@8.43.0)(typescript@5.0.4):
|
||||
resolution: {integrity: sha512-78B+anHLF1TI8Jn/cD0Q00TBYdMgjdOn980JfAVa9yw5sop8nyTfVOQAv6LWywkOGLclDBtv5z3oxN4w7jxyNg==}
|
||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||
peerDependencies:
|
||||
@ -2196,23 +2205,23 @@ packages:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@eslint-community/regexpp': 4.5.1
|
||||
'@typescript-eslint/parser': 5.60.0(eslint@8.43.0)(typescript@5.1.3)
|
||||
'@typescript-eslint/parser': 5.60.0(eslint@8.43.0)(typescript@5.0.4)
|
||||
'@typescript-eslint/scope-manager': 5.60.0
|
||||
'@typescript-eslint/type-utils': 5.60.0(eslint@8.43.0)(typescript@5.1.3)
|
||||
'@typescript-eslint/utils': 5.60.0(eslint@8.43.0)(typescript@5.1.3)
|
||||
'@typescript-eslint/type-utils': 5.60.0(eslint@8.43.0)(typescript@5.0.4)
|
||||
'@typescript-eslint/utils': 5.60.0(eslint@8.43.0)(typescript@5.0.4)
|
||||
debug: 4.3.4
|
||||
eslint: 8.43.0
|
||||
grapheme-splitter: 1.0.4
|
||||
ignore: 5.2.4
|
||||
natural-compare-lite: 1.4.0
|
||||
semver: 7.3.8
|
||||
tsutils: 3.21.0(typescript@5.1.3)
|
||||
typescript: 5.1.3
|
||||
tsutils: 3.21.0(typescript@5.0.4)
|
||||
typescript: 5.0.4
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
dev: false
|
||||
|
||||
/@typescript-eslint/parser@5.60.0(eslint@8.43.0)(typescript@5.1.3):
|
||||
/@typescript-eslint/parser@5.60.0(eslint@8.43.0)(typescript@5.0.4):
|
||||
resolution: {integrity: sha512-jBONcBsDJ9UoTWrARkRRCgDz6wUggmH5RpQVlt7BimSwaTkTjwypGzKORXbR4/2Hqjk9hgwlon2rVQAjWNpkyQ==}
|
||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||
peerDependencies:
|
||||
@ -2224,10 +2233,10 @@ packages:
|
||||
dependencies:
|
||||
'@typescript-eslint/scope-manager': 5.60.0
|
||||
'@typescript-eslint/types': 5.60.0
|
||||
'@typescript-eslint/typescript-estree': 5.60.0(typescript@5.1.3)
|
||||
'@typescript-eslint/typescript-estree': 5.60.0(typescript@5.0.4)
|
||||
debug: 4.3.4
|
||||
eslint: 8.43.0
|
||||
typescript: 5.1.3
|
||||
typescript: 5.0.4
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
dev: false
|
||||
@ -2240,7 +2249,7 @@ packages:
|
||||
'@typescript-eslint/visitor-keys': 5.60.0
|
||||
dev: false
|
||||
|
||||
/@typescript-eslint/type-utils@5.60.0(eslint@8.43.0)(typescript@5.1.3):
|
||||
/@typescript-eslint/type-utils@5.60.0(eslint@8.43.0)(typescript@5.0.4):
|
||||
resolution: {integrity: sha512-X7NsRQddORMYRFH7FWo6sA9Y/zbJ8s1x1RIAtnlj6YprbToTiQnM6vxcMu7iYhdunmoC0rUWlca13D5DVHkK2g==}
|
||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||
peerDependencies:
|
||||
@ -2250,12 +2259,12 @@ packages:
|
||||
typescript:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@typescript-eslint/typescript-estree': 5.60.0(typescript@5.1.3)
|
||||
'@typescript-eslint/utils': 5.60.0(eslint@8.43.0)(typescript@5.1.3)
|
||||
'@typescript-eslint/typescript-estree': 5.60.0(typescript@5.0.4)
|
||||
'@typescript-eslint/utils': 5.60.0(eslint@8.43.0)(typescript@5.0.4)
|
||||
debug: 4.3.4
|
||||
eslint: 8.43.0
|
||||
tsutils: 3.21.0(typescript@5.1.3)
|
||||
typescript: 5.1.3
|
||||
tsutils: 3.21.0(typescript@5.0.4)
|
||||
typescript: 5.0.4
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
dev: false
|
||||
@ -2265,7 +2274,7 @@ packages:
|
||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||
dev: false
|
||||
|
||||
/@typescript-eslint/typescript-estree@5.60.0(typescript@5.1.3):
|
||||
/@typescript-eslint/typescript-estree@5.60.0(typescript@5.0.4):
|
||||
resolution: {integrity: sha512-R43thAuwarC99SnvrBmh26tc7F6sPa2B3evkXp/8q954kYL6Ro56AwASYWtEEi+4j09GbiNAHqYwNNZuNlARGQ==}
|
||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||
peerDependencies:
|
||||
@ -2280,13 +2289,13 @@ packages:
|
||||
globby: 11.1.0
|
||||
is-glob: 4.0.3
|
||||
semver: 7.3.8
|
||||
tsutils: 3.21.0(typescript@5.1.3)
|
||||
typescript: 5.1.3
|
||||
tsutils: 3.21.0(typescript@5.0.4)
|
||||
typescript: 5.0.4
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
dev: false
|
||||
|
||||
/@typescript-eslint/utils@5.60.0(eslint@8.43.0)(typescript@5.1.3):
|
||||
/@typescript-eslint/utils@5.60.0(eslint@8.43.0)(typescript@5.0.4):
|
||||
resolution: {integrity: sha512-ba51uMqDtfLQ5+xHtwlO84vkdjrqNzOnqrnwbMHMRY8Tqeme8C2Q8Fc7LajfGR+e3/4LoYiWXUM6BpIIbHJ4hQ==}
|
||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||
peerDependencies:
|
||||
@ -2297,7 +2306,7 @@ packages:
|
||||
'@types/semver': 7.5.0
|
||||
'@typescript-eslint/scope-manager': 5.60.0
|
||||
'@typescript-eslint/types': 5.60.0
|
||||
'@typescript-eslint/typescript-estree': 5.60.0(typescript@5.1.3)
|
||||
'@typescript-eslint/typescript-estree': 5.60.0(typescript@5.0.4)
|
||||
eslint: 8.43.0
|
||||
eslint-scope: 5.1.1
|
||||
semver: 7.3.8
|
||||
@ -2534,7 +2543,7 @@ packages:
|
||||
engines: {node: '>=12.20.1'}
|
||||
dependencies:
|
||||
'@babel/runtime': 7.21.0
|
||||
tslib: 2.5.3
|
||||
tslib: 2.4.0
|
||||
dev: false
|
||||
|
||||
/autoprefixer@10.4.4(postcss@8.4.6):
|
||||
@ -2592,6 +2601,10 @@ packages:
|
||||
/balanced-match@1.0.2:
|
||||
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
|
||||
|
||||
/base-64@1.0.0:
|
||||
resolution: {integrity: sha512-kwDPIFCGx0NZHog36dj+tHiwP4QMzsZ3AgMViUBKI0+V5n4U0ufTCUMhnQ04diaRI8EX/QcPfql7zlhZ7j4zgg==}
|
||||
dev: true
|
||||
|
||||
/base64-js@1.5.1:
|
||||
resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
|
||||
dev: false
|
||||
@ -2646,7 +2659,7 @@ packages:
|
||||
dependencies:
|
||||
'@babel/runtime': 7.21.0
|
||||
fast-unique-numbers: 6.0.21
|
||||
tslib: 2.5.3
|
||||
tslib: 2.4.0
|
||||
worker-factory: 6.0.69
|
||||
dev: false
|
||||
|
||||
@ -2678,7 +2691,6 @@ packages:
|
||||
requiresBuild: true
|
||||
dependencies:
|
||||
node-gyp-build: 4.6.0
|
||||
dev: false
|
||||
|
||||
/busboy@1.6.0:
|
||||
resolution: {integrity: sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==}
|
||||
@ -2955,7 +2967,7 @@ packages:
|
||||
'@babel/runtime': 7.21.0
|
||||
dashify: 2.0.0
|
||||
indefinite-article: 0.0.2
|
||||
tslib: 2.5.3
|
||||
tslib: 2.4.0
|
||||
dev: false
|
||||
|
||||
/concat-map@0.0.1:
|
||||
@ -2971,7 +2983,7 @@ packages:
|
||||
js-string-escape: 1.0.1
|
||||
lodash: 4.17.21
|
||||
md5-hex: 3.0.1
|
||||
semver: 7.4.0
|
||||
semver: 7.3.8
|
||||
well-known-symbols: 2.0.0
|
||||
dev: false
|
||||
|
||||
@ -3938,7 +3950,7 @@ packages:
|
||||
engines: {node: '>=12.20.1'}
|
||||
dependencies:
|
||||
'@babel/runtime': 7.21.0
|
||||
tslib: 2.5.3
|
||||
tslib: 2.4.0
|
||||
dev: false
|
||||
|
||||
/fastq@1.13.0:
|
||||
@ -4131,7 +4143,7 @@ packages:
|
||||
fs.realpath: 1.0.0
|
||||
inflight: 1.0.6
|
||||
inherits: 2.0.4
|
||||
minimatch: 3.1.2
|
||||
minimatch: 3.0.4
|
||||
once: 1.4.0
|
||||
path-is-absolute: 1.0.1
|
||||
|
||||
@ -4394,7 +4406,7 @@ packages:
|
||||
'@formatjs/ecma402-abstract': 1.11.4
|
||||
'@formatjs/fast-memoize': 1.2.1
|
||||
'@formatjs/icu-messageformat-parser': 2.1.0
|
||||
tslib: 2.5.3
|
||||
tslib: 2.4.0
|
||||
dev: false
|
||||
|
||||
/is-arguments@1.1.1:
|
||||
@ -4717,7 +4729,7 @@ packages:
|
||||
whatwg-encoding: 2.0.0
|
||||
whatwg-mimetype: 3.0.0
|
||||
whatwg-url: 12.0.1
|
||||
ws: 8.13.0
|
||||
ws: 8.13.0(bufferutil@4.0.7)
|
||||
xml-name-validator: 4.0.0
|
||||
transitivePeerDependencies:
|
||||
- bufferutil
|
||||
@ -4950,7 +4962,7 @@ packages:
|
||||
broker-factory: 3.0.68
|
||||
fast-unique-numbers: 6.0.21
|
||||
media-encoder-host-worker: 9.0.70
|
||||
tslib: 2.5.3
|
||||
tslib: 2.4.0
|
||||
dev: false
|
||||
|
||||
/media-encoder-host-worker@9.0.70:
|
||||
@ -4958,7 +4970,7 @@ packages:
|
||||
dependencies:
|
||||
'@babel/runtime': 7.21.0
|
||||
extendable-media-recorder-wav-encoder-broker: 7.0.70
|
||||
tslib: 2.5.3
|
||||
tslib: 2.4.0
|
||||
worker-factory: 6.0.69
|
||||
dev: false
|
||||
|
||||
@ -4968,7 +4980,7 @@ packages:
|
||||
'@babel/runtime': 7.21.0
|
||||
media-encoder-host-broker: 7.0.70
|
||||
media-encoder-host-worker: 9.0.70
|
||||
tslib: 2.5.3
|
||||
tslib: 2.4.0
|
||||
dev: false
|
||||
|
||||
/memoizee@0.4.15:
|
||||
@ -5053,12 +5065,12 @@ packages:
|
||||
resolution: {integrity: sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==}
|
||||
dependencies:
|
||||
brace-expansion: 1.1.11
|
||||
dev: false
|
||||
|
||||
/minimatch@3.1.2:
|
||||
resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
|
||||
dependencies:
|
||||
brace-expansion: 1.1.11
|
||||
dev: false
|
||||
|
||||
/minimist-options@4.1.0:
|
||||
resolution: {integrity: sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==}
|
||||
@ -5103,7 +5115,7 @@ packages:
|
||||
/ms@2.1.2:
|
||||
resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==}
|
||||
|
||||
/msw@1.0.0(typescript@5.1.3):
|
||||
/msw@1.0.0(typescript@5.0.4):
|
||||
resolution: {integrity: sha512-8QVa1RAN/Nzbn/tKmtimJ+b2M1QZOMdETQW7/1TmBOZ4w+wJojfxuh1Hj5J4FYdBgZWW/TK4CABUOlOM4OjTOA==}
|
||||
engines: {node: '>=14'}
|
||||
hasBin: true
|
||||
@ -5132,7 +5144,7 @@ packages:
|
||||
path-to-regexp: 6.2.1
|
||||
strict-event-emitter: 0.4.6
|
||||
type-fest: 2.19.0
|
||||
typescript: 5.1.3
|
||||
typescript: 5.0.4
|
||||
yargs: 17.6.2
|
||||
transitivePeerDependencies:
|
||||
- encoding
|
||||
@ -5144,7 +5156,7 @@ packages:
|
||||
engines: {node: '>=12.20.1'}
|
||||
dependencies:
|
||||
'@babel/runtime': 7.21.0
|
||||
tslib: 2.5.3
|
||||
tslib: 2.4.0
|
||||
dev: false
|
||||
|
||||
/mute-stream@0.0.8:
|
||||
@ -5193,12 +5205,10 @@ packages:
|
||||
optional: true
|
||||
dependencies:
|
||||
whatwg-url: 5.0.0
|
||||
dev: false
|
||||
|
||||
/node-gyp-build@4.6.0:
|
||||
resolution: {integrity: sha512-NTZVKn9IylLwUzaKjkas1e4u2DLNcV4rdYagA4PWdPwW87Bi7z+BznyKSRwS/761tV/lzCGXplWsiaMjLqP2zQ==}
|
||||
hasBin: true
|
||||
dev: false
|
||||
|
||||
/node-html-parser@6.0.0:
|
||||
resolution: {integrity: sha512-o4vS5Jm7ZdV5WN4/jHmCEVJOpm4exLCeXOcZnNzXi0BGv0AS8FsGwyQ4k0Ujmui1NMQs6qsTy+amjjpr9rmz4Q==}
|
||||
@ -5848,6 +5858,18 @@ packages:
|
||||
engines: {node: '>=6'}
|
||||
dev: false
|
||||
|
||||
/pyodide@0.23.2:
|
||||
resolution: {integrity: sha512-GK4YDZVgzfAXK/7X0IiCI+142k0Ah/HwYTzDHtG8zC47dflWYuPozeFbOngShuL1M9Un5sCmHFqiH3boxJv0pQ==}
|
||||
dependencies:
|
||||
base-64: 1.0.0
|
||||
node-fetch: 2.6.7
|
||||
ws: 8.13.0(bufferutil@4.0.7)
|
||||
transitivePeerDependencies:
|
||||
- bufferutil
|
||||
- encoding
|
||||
- utf-8-validate
|
||||
dev: true
|
||||
|
||||
/querystringify@2.2.0:
|
||||
resolution: {integrity: sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==}
|
||||
dev: false
|
||||
@ -5936,7 +5958,7 @@ packages:
|
||||
resolution: {integrity: sha512-5QTJKukH8JcQR1f2FqZsQ1QD2aoc6/+tM0WPv8sqEI4THzbiMfH4VuWF3BfdL2F9mRjLo81nFC9OShCj87wMhg==}
|
||||
dependencies:
|
||||
'@babel/runtime': 7.21.0
|
||||
tslib: 2.5.3
|
||||
tslib: 2.4.0
|
||||
dev: false
|
||||
|
||||
/recorder-audio-worklet@5.1.29:
|
||||
@ -5948,7 +5970,7 @@ packages:
|
||||
recorder-audio-worklet-processor: 4.2.15
|
||||
standardized-audio-context: 25.3.32
|
||||
subscribable-things: 2.1.7
|
||||
tslib: 2.5.3
|
||||
tslib: 2.4.0
|
||||
worker-factory: 6.0.69
|
||||
dev: false
|
||||
|
||||
@ -6074,7 +6096,7 @@ packages:
|
||||
/rxjs@7.8.0:
|
||||
resolution: {integrity: sha512-F2+gxDshqmIub1KdvZkaEfGDwLNpPvk9Fs6LD/MyQxNgMds/WH9OdDDXOmxUZpME+iSK3rQCctkL0DYyytUqMg==}
|
||||
dependencies:
|
||||
tslib: 2.5.3
|
||||
tslib: 2.4.0
|
||||
dev: false
|
||||
|
||||
/sade@1.8.1:
|
||||
@ -6299,7 +6321,7 @@ packages:
|
||||
dependencies:
|
||||
'@babel/runtime': 7.21.0
|
||||
automation-events: 4.0.21
|
||||
tslib: 2.5.3
|
||||
tslib: 2.4.0
|
||||
dev: false
|
||||
|
||||
/std-env@3.3.2:
|
||||
@ -6428,7 +6450,7 @@ packages:
|
||||
dependencies:
|
||||
'@babel/runtime': 7.21.0
|
||||
rxjs-interop: 2.0.0
|
||||
tslib: 2.5.3
|
||||
tslib: 2.4.0
|
||||
dev: false
|
||||
|
||||
/supports-color@5.5.0:
|
||||
@ -6651,7 +6673,7 @@ packages:
|
||||
typescript: 4.9.5
|
||||
dev: false
|
||||
|
||||
/svelte-preprocess@5.0.3(postcss@8.4.6)(svelte@3.59.1)(typescript@5.1.3):
|
||||
/svelte-preprocess@5.0.3(postcss@8.4.6)(svelte@3.59.1)(typescript@5.0.4):
|
||||
resolution: {integrity: sha512-GrHF1rusdJVbOZOwgPWtpqmaexkydznKzy5qIC2FabgpFyKN57bjMUUUqPRfbBXK5igiEWn1uO/DXsa2vJ5VHA==}
|
||||
engines: {node: '>= 14.10.0'}
|
||||
requiresBuild: true
|
||||
@ -6696,7 +6718,7 @@ packages:
|
||||
sorcery: 0.11.0
|
||||
strip-indent: 3.0.0
|
||||
svelte: 3.59.1
|
||||
typescript: 5.1.3
|
||||
typescript: 5.0.4
|
||||
dev: false
|
||||
|
||||
/svelte-range-slider-pips@2.0.2:
|
||||
@ -6813,6 +6835,11 @@ packages:
|
||||
engines: {node: '>=14.0.0'}
|
||||
dev: false
|
||||
|
||||
/tinyspy@2.0.0:
|
||||
resolution: {integrity: sha512-B9wP6IgqmgNTDffygA716cr+PrW51LZc22qFs7+Aur0t73gqf3vNwwlwdcnE1AcqusK6V4R4+5jQ69nIQDiJiw==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
dev: false
|
||||
|
||||
/tinyspy@2.1.1:
|
||||
resolution: {integrity: sha512-XPJL2uSzcOyBMky6OFrusqWlzfFrXtE0hPuMgW8A2HmaqrPo4ZQHRN/V0QXN3FSjKxpsbRrFc5LI7KOwBsT1/w==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
@ -6854,7 +6881,6 @@ packages:
|
||||
|
||||
/tr46@0.0.3:
|
||||
resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==}
|
||||
dev: false
|
||||
|
||||
/tr46@4.1.1:
|
||||
resolution: {integrity: sha512-2lv/66T7e5yNyhAAC4NaKe5nVavzuGJQVVtRYLyQ2OI8tsJ61PMLlelehb0wi2Hx6+hT/OJUWZcw8MjlSRnxvw==}
|
||||
@ -6887,14 +6913,14 @@ packages:
|
||||
resolution: {integrity: sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w==}
|
||||
dev: false
|
||||
|
||||
/tsutils@3.21.0(typescript@5.1.3):
|
||||
/tsutils@3.21.0(typescript@5.0.4):
|
||||
resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==}
|
||||
engines: {node: '>= 6'}
|
||||
peerDependencies:
|
||||
typescript: '>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta'
|
||||
dependencies:
|
||||
tslib: 1.14.1
|
||||
typescript: 5.1.3
|
||||
typescript: 5.0.4
|
||||
dev: false
|
||||
|
||||
/tty-table@4.2.1:
|
||||
@ -6978,13 +7004,6 @@ packages:
|
||||
resolution: {integrity: sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw==}
|
||||
engines: {node: '>=12.20'}
|
||||
hasBin: true
|
||||
dev: true
|
||||
|
||||
/typescript@5.1.3:
|
||||
resolution: {integrity: sha512-XH627E9vkeqhlZFQuL+UsyAXEnibT0kWR2FWONlr4sTjvxyJYnyefgrkyECLzM5NenmKzRAy2rR/OlYLA1HkZw==}
|
||||
engines: {node: '>=14.17'}
|
||||
hasBin: true
|
||||
dev: false
|
||||
|
||||
/ufo@1.1.1:
|
||||
resolution: {integrity: sha512-MvlCc4GHrmZdAllBc0iUDowff36Q9Ndw/UzqmEKyrfSzokTd9ZCy1i+IIk5hrYKkjoYVQyNbrw7/F8XJ2rEwTg==}
|
||||
@ -7068,7 +7087,7 @@ packages:
|
||||
dependencies:
|
||||
d3-array: 3.1.1
|
||||
vega-dataflow: 5.7.4
|
||||
vega-util: 1.17.2
|
||||
vega-util: 1.17.0
|
||||
transitivePeerDependencies:
|
||||
- encoding
|
||||
dev: false
|
||||
@ -7078,7 +7097,7 @@ packages:
|
||||
dependencies:
|
||||
vega-format: 1.1.0
|
||||
vega-loader: 4.5.0
|
||||
vega-util: 1.17.2
|
||||
vega-util: 1.17.0
|
||||
transitivePeerDependencies:
|
||||
- encoding
|
||||
dev: false
|
||||
@ -7110,7 +7129,7 @@ packages:
|
||||
d3-interpolate: 3.0.1
|
||||
vega-dataflow: 5.7.4
|
||||
vega-scale: 7.2.0
|
||||
vega-util: 1.17.2
|
||||
vega-util: 1.17.0
|
||||
transitivePeerDependencies:
|
||||
- encoding
|
||||
dev: false
|
||||
@ -7123,7 +7142,7 @@ packages:
|
||||
resolution: {integrity: sha512-y5+c2frq0tGwJ7vYXzZcfVcIRF/QGfhf2e+bV1Z0iQs+M2lI1II1GPDdmOcMKimpoCVp/D61KUJDIGE1DSmk2w==}
|
||||
dependencies:
|
||||
'@types/estree': 0.0.50
|
||||
vega-util: 1.17.2
|
||||
vega-util: 1.17.0
|
||||
dev: false
|
||||
|
||||
/vega-force@4.1.0:
|
||||
@ -7131,7 +7150,7 @@ packages:
|
||||
dependencies:
|
||||
d3-force: 3.0.0
|
||||
vega-dataflow: 5.7.4
|
||||
vega-util: 1.17.2
|
||||
vega-util: 1.17.0
|
||||
transitivePeerDependencies:
|
||||
- encoding
|
||||
dev: false
|
||||
@ -7143,7 +7162,7 @@ packages:
|
||||
d3-format: 3.1.0
|
||||
d3-time-format: 4.1.0
|
||||
vega-time: 2.1.0
|
||||
vega-util: 1.17.2
|
||||
vega-util: 1.17.0
|
||||
dev: false
|
||||
|
||||
/vega-functions@5.13.0:
|
||||
@ -7159,7 +7178,7 @@ packages:
|
||||
vega-selections: 5.4.0
|
||||
vega-statistics: 1.8.0
|
||||
vega-time: 2.1.0
|
||||
vega-util: 1.17.2
|
||||
vega-util: 1.17.0
|
||||
transitivePeerDependencies:
|
||||
- encoding
|
||||
dev: false
|
||||
@ -7174,7 +7193,7 @@ packages:
|
||||
vega-dataflow: 5.7.4
|
||||
vega-projection: 1.5.0
|
||||
vega-statistics: 1.8.0
|
||||
vega-util: 1.17.2
|
||||
vega-util: 1.17.0
|
||||
transitivePeerDependencies:
|
||||
- encoding
|
||||
dev: false
|
||||
@ -7184,7 +7203,7 @@ packages:
|
||||
dependencies:
|
||||
d3-hierarchy: 3.1.2
|
||||
vega-dataflow: 5.7.4
|
||||
vega-util: 1.17.2
|
||||
vega-util: 1.17.0
|
||||
transitivePeerDependencies:
|
||||
- encoding
|
||||
dev: false
|
||||
@ -7199,7 +7218,7 @@ packages:
|
||||
vega-canvas: 1.2.6
|
||||
vega-dataflow: 5.7.4
|
||||
vega-scenegraph: 4.10.1
|
||||
vega-util: 1.17.2
|
||||
vega-util: 1.17.0
|
||||
transitivePeerDependencies:
|
||||
- encoding
|
||||
dev: false
|
||||
@ -7231,7 +7250,7 @@ packages:
|
||||
node-fetch: 2.6.7
|
||||
topojson-client: 3.1.0
|
||||
vega-format: 1.1.0
|
||||
vega-util: 1.17.2
|
||||
vega-util: 1.17.0
|
||||
transitivePeerDependencies:
|
||||
- encoding
|
||||
dev: false
|
||||
@ -7243,7 +7262,7 @@ packages:
|
||||
vega-event-selector: 3.0.0
|
||||
vega-functions: 5.13.0
|
||||
vega-scale: 7.2.0
|
||||
vega-util: 1.17.2
|
||||
vega-util: 1.17.0
|
||||
transitivePeerDependencies:
|
||||
- encoding
|
||||
dev: false
|
||||
@ -7261,7 +7280,7 @@ packages:
|
||||
d3-array: 3.1.1
|
||||
vega-dataflow: 5.7.4
|
||||
vega-statistics: 1.8.0
|
||||
vega-util: 1.17.2
|
||||
vega-util: 1.17.0
|
||||
transitivePeerDependencies:
|
||||
- encoding
|
||||
dev: false
|
||||
@ -7270,7 +7289,7 @@ packages:
|
||||
resolution: {integrity: sha512-gE+sO2IfxMUpV0RkFeQVnHdmPy3K7LjHakISZgUGsDI/ZFs9y+HhBf8KTGSL5pcZPtQsZh3GBQ0UonqL1mp9PA==}
|
||||
dependencies:
|
||||
vega-dataflow: 5.7.4
|
||||
vega-util: 1.17.2
|
||||
vega-util: 1.17.0
|
||||
transitivePeerDependencies:
|
||||
- encoding
|
||||
dev: false
|
||||
@ -7282,7 +7301,7 @@ packages:
|
||||
d3-interpolate: 3.0.1
|
||||
d3-scale: 4.0.2
|
||||
vega-time: 2.1.0
|
||||
vega-util: 1.17.2
|
||||
vega-util: 1.17.0
|
||||
dev: false
|
||||
|
||||
/vega-scenegraph@4.10.1:
|
||||
@ -7293,7 +7312,7 @@ packages:
|
||||
vega-canvas: 1.2.6
|
||||
vega-loader: 4.5.0
|
||||
vega-scale: 7.2.0
|
||||
vega-util: 1.17.2
|
||||
vega-util: 1.17.0
|
||||
transitivePeerDependencies:
|
||||
- encoding
|
||||
dev: false
|
||||
@ -7307,7 +7326,7 @@ packages:
|
||||
dependencies:
|
||||
d3-array: 3.1.1
|
||||
vega-expression: 5.0.0
|
||||
vega-util: 1.17.2
|
||||
vega-util: 1.17.0
|
||||
dev: false
|
||||
|
||||
/vega-statistics@1.8.0:
|
||||
@ -7331,7 +7350,7 @@ packages:
|
||||
dependencies:
|
||||
d3-array: 3.1.1
|
||||
d3-time: 3.0.0
|
||||
vega-util: 1.17.2
|
||||
vega-util: 1.17.0
|
||||
dev: false
|
||||
|
||||
/vega-tooltip@0.32.0:
|
||||
@ -7347,7 +7366,7 @@ packages:
|
||||
vega-dataflow: 5.7.4
|
||||
vega-statistics: 1.8.0
|
||||
vega-time: 2.1.0
|
||||
vega-util: 1.17.2
|
||||
vega-util: 1.17.0
|
||||
transitivePeerDependencies:
|
||||
- encoding
|
||||
dev: false
|
||||
@ -7357,7 +7376,7 @@ packages:
|
||||
dependencies:
|
||||
vega-event-selector: 3.0.0
|
||||
vega-expression: 5.0.0
|
||||
vega-util: 1.17.2
|
||||
vega-util: 1.17.0
|
||||
dev: false
|
||||
|
||||
/vega-util@1.17.0:
|
||||
@ -7373,7 +7392,7 @@ packages:
|
||||
dependencies:
|
||||
vega-dataflow: 5.7.4
|
||||
vega-scenegraph: 4.10.1
|
||||
vega-util: 1.17.2
|
||||
vega-util: 1.17.0
|
||||
transitivePeerDependencies:
|
||||
- encoding
|
||||
dev: false
|
||||
@ -7388,7 +7407,7 @@ packages:
|
||||
vega-functions: 5.13.0
|
||||
vega-runtime: 6.1.3
|
||||
vega-scenegraph: 4.10.1
|
||||
vega-util: 1.17.2
|
||||
vega-util: 1.17.0
|
||||
transitivePeerDependencies:
|
||||
- encoding
|
||||
dev: false
|
||||
@ -7398,7 +7417,7 @@ packages:
|
||||
dependencies:
|
||||
d3-delaunay: 6.0.2
|
||||
vega-dataflow: 5.7.4
|
||||
vega-util: 1.17.2
|
||||
vega-util: 1.17.0
|
||||
transitivePeerDependencies:
|
||||
- encoding
|
||||
dev: false
|
||||
@ -7410,7 +7429,7 @@ packages:
|
||||
vega-dataflow: 5.7.4
|
||||
vega-scale: 7.2.0
|
||||
vega-statistics: 1.8.0
|
||||
vega-util: 1.17.2
|
||||
vega-util: 1.17.0
|
||||
transitivePeerDependencies:
|
||||
- encoding
|
||||
dev: false
|
||||
@ -7651,7 +7670,6 @@ packages:
|
||||
|
||||
/webidl-conversions@3.0.1:
|
||||
resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==}
|
||||
dev: false
|
||||
|
||||
/webidl-conversions@7.0.0:
|
||||
resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==}
|
||||
@ -7688,7 +7706,6 @@ packages:
|
||||
dependencies:
|
||||
tr46: 0.0.3
|
||||
webidl-conversions: 3.0.1
|
||||
dev: false
|
||||
|
||||
/which-boxed-primitive@1.0.2:
|
||||
resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==}
|
||||
@ -7759,7 +7776,7 @@ packages:
|
||||
'@babel/runtime': 7.21.0
|
||||
compilerr: 9.0.21
|
||||
fast-unique-numbers: 6.0.21
|
||||
tslib: 2.5.3
|
||||
tslib: 2.4.0
|
||||
dev: false
|
||||
|
||||
/wrap-ansi@6.2.0:
|
||||
@ -7783,19 +7800,6 @@ packages:
|
||||
/wrappy@1.0.2:
|
||||
resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
|
||||
|
||||
/ws@8.13.0:
|
||||
resolution: {integrity: sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==}
|
||||
engines: {node: '>=10.0.0'}
|
||||
peerDependencies:
|
||||
bufferutil: ^4.0.1
|
||||
utf-8-validate: '>=5.0.2'
|
||||
peerDependenciesMeta:
|
||||
bufferutil:
|
||||
optional: true
|
||||
utf-8-validate:
|
||||
optional: true
|
||||
dev: false
|
||||
|
||||
/ws@8.13.0(bufferutil@4.0.7):
|
||||
resolution: {integrity: sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==}
|
||||
engines: {node: '>=10.0.0'}
|
||||
@ -7809,7 +7813,6 @@ packages:
|
||||
optional: true
|
||||
dependencies:
|
||||
bufferutil: 4.0.7
|
||||
dev: false
|
||||
|
||||
/xml-name-validator@4.0.0:
|
||||
resolution: {integrity: sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==}
|
||||
|
@ -1,3 +1,3 @@
|
||||
packages:
|
||||
- 'js/*'
|
||||
- 'client/js'
|
||||
- 'client/js'
|
||||
|
Loading…
Reference in New Issue
Block a user