diff --git a/.gitignore b/.gitignore
index 5b5848d..2b0376b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -76,4 +76,22 @@ crashlytics-build.properties
.DS_Store
# Ignore Assets/Vuplex because it's paid asset
-/[Aa]ssets/Vuplex/
\ No newline at end of file
+/[Aa]ssets/Vuplex/
+
+# Node.js
+node_modules/
+dist/
+*.tgz
+
+# IDE
+.vscode/
+.idea/
+
+# Debug logs
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+
+# Local development
+.env
+.env.local
\ No newline at end of file
diff --git a/Assets/Scenes/SampleScene.unity b/Assets/Scenes/SampleScene.unity
index 5b1c1f9..e883b8a 100644
--- a/Assets/Scenes/SampleScene.unity
+++ b/Assets/Scenes/SampleScene.unity
@@ -513,11 +513,11 @@ PrefabInstance:
objectReference: {fileID: 0}
- target: {fileID: 224179541092979990, guid: 3e10f3b6a733e441c8352020cff2f1f6, type: 3}
propertyPath: m_AnchorMax.x
- value: 1
+ value: 0.5
objectReference: {fileID: 0}
- target: {fileID: 224179541092979990, guid: 3e10f3b6a733e441c8352020cff2f1f6, type: 3}
propertyPath: m_AnchorMax.y
- value: 1
+ value: 0.5
objectReference: {fileID: 0}
- target: {fileID: 224179541092979990, guid: 3e10f3b6a733e441c8352020cff2f1f6, type: 3}
propertyPath: m_AnchorMin.x
@@ -529,11 +529,11 @@ PrefabInstance:
objectReference: {fileID: 0}
- target: {fileID: 224179541092979990, guid: 3e10f3b6a733e441c8352020cff2f1f6, type: 3}
propertyPath: m_SizeDelta.x
- value: 0
+ value: 160
objectReference: {fileID: 0}
- target: {fileID: 224179541092979990, guid: 3e10f3b6a733e441c8352020cff2f1f6, type: 3}
propertyPath: m_SizeDelta.y
- value: 0
+ value: 340
objectReference: {fileID: 0}
- target: {fileID: 224179541092979990, guid: 3e10f3b6a733e441c8352020cff2f1f6, type: 3}
propertyPath: m_LocalPosition.x
@@ -565,11 +565,11 @@ PrefabInstance:
objectReference: {fileID: 0}
- target: {fileID: 224179541092979990, guid: 3e10f3b6a733e441c8352020cff2f1f6, type: 3}
propertyPath: m_AnchoredPosition.x
- value: 0
+ value: 80
objectReference: {fileID: 0}
- target: {fileID: 224179541092979990, guid: 3e10f3b6a733e441c8352020cff2f1f6, type: 3}
propertyPath: m_AnchoredPosition.y
- value: 0
+ value: 170
objectReference: {fileID: 0}
- target: {fileID: 224179541092979990, guid: 3e10f3b6a733e441c8352020cff2f1f6, type: 3}
propertyPath: m_LocalEulerAnglesHint.x
diff --git a/README.md b/README.md
index 2a7cf1b..be3614b 100644
--- a/README.md
+++ b/README.md
@@ -55,6 +55,24 @@ flowchart LR
With WebView RPC, method calls between C# and JavaScript behave like regular function calls, significantly reducing the need for repetitive JSON parsing and bridge implementations. This structure becomes even more maintainable as the project scales.
+## Examples
+### Unity Example
+Clone this whole repository.
+> [!NOTE]
+> Require [Viewplex Webview Asset(paid asset)](https://assetstore.unity.com/publishers/40309?locale=ko-KR&srsltid=AfmBOoqtnjTpJ-pw_5iGoS88XRtGX-tY2eJmP86PYoYCOhxrvz1OXRaJ) to run sample.
+### Javascript Example
+[webview_rpc_sample](https://github.com/kwan3854/Unity-WebViewRpc/tree/main/webview_rpc_sample)
+```bash
+# 1. Move to sample directory
+cd webview_rpc_sample
+
+# 2. Install dependencies
+npm install
+
+# 3. Build project
+npm run build
+```
+
## Installation
### Adding WebView RPC to a Unity Project
@@ -84,9 +102,22 @@ With WebView RPC, method calls between C# and JavaScript behave like regular fun
```
### Adding WebView Library
+#### Install
+```bash
+npm install app-webview-rpc
+```
-- WebView RPC is not distributed as a package.
-- Add the files under [Unity-WebViewRpc/WebViewRpcJS/RuntimeLibrary](https://github.com/kwan3854/Unity-WebViewRpc/tree/main/WebViewRpcJS/RuntimeLibrary) to your JavaScript project.
+#### Usage
+```javascript
+import { VuplexBridge, WebViewRpcClient, WebViewRpcServer } from 'app-webview-rpc';
+
+// RPC client
+const bridge = new VuplexBridge();
+const rpcClient = new WebViewRpcClient(bridge);
+
+// RPC server
+const rpcServer = new WebViewRpcServer(bridge);
+```
### Installing the protobuf Compiler
diff --git a/README_ko.md b/README_ko.md
index efdf3d7..bd5f804 100644
--- a/README_ko.md
+++ b/README_ko.md
@@ -49,6 +49,24 @@ flowchart LR
이처럼 WebView RPC를 사용하면 C#과 JavaScript 간 상호 호출이 단순 함수 호출처럼 동작하며, 반복되는 JSON 파싱/Bridge 처리를 크게 줄일 수 있습니다.
프로젝트 규모가 커질수록 이 구조가 훨씬 간결하고 유지보수하기 쉬워집니다.
+## 샘플 프로젝트 제공
+### Unity Example
+Clone this whole repository.
+> [!NOTE]
+> 샘플을 실행하기 위해서는 [Viewplex Webview Asset(유료)](https://assetstore.unity.com/publishers/40309?locale=ko-KR&srsltid=AfmBOoqtnjTpJ-pw_5iGoS88XRtGX-tY2eJmP86PYoYCOhxrvz1OXRaJ)이 필요합니다.
+### Javascript Example
+[webview_rpc_sample](https://github.com/kwan3854/Unity-WebViewRpc/tree/main/webview_rpc_sample)
+```bash
+# 1. Move to sample directory
+cd webview_rpc_sample
+
+# 2. Install dependencies
+npm install
+
+# 3. Build project
+npm run build
+```
+
## 설치
### 유니티 프로젝트에 WebView RPC 추가하기
1. Nuget 패키지 매니저를 통해 `Protobuf` 패키지를 설치합니다.
diff --git a/webview_rpc_runtime_library/.npmignore b/webview_rpc_runtime_library/.npmignore
new file mode 100644
index 0000000..aa2eec3
--- /dev/null
+++ b/webview_rpc_runtime_library/.npmignore
@@ -0,0 +1,3 @@
+src/
+tsconfig*.json
+.gitignore
\ No newline at end of file
diff --git a/webview_rpc_runtime_library/README.md b/webview_rpc_runtime_library/README.md
new file mode 100644
index 0000000..be3614b
--- /dev/null
+++ b/webview_rpc_runtime_library/README.md
@@ -0,0 +1,523 @@
+[English](README.md) | [Korean](README_ko.md)
+
+[](https://openupm.com/packages/com.kwanjoong.webviewrpc/)
+[](LICENSE.md)
+
+# Unity WebView RPC
+
+Unity WebView RPC provides an abstraction layer that allows communication between the Unity client (C#) and WebView (HTML, JS) using protobuf, similar to gRPC.
+It extends the traditional `JavaScript bridge` communication method to work similarly to a Remote Procedure Call (RPC).
+To avoid dependency on a specific WebView library, it provides a Bridge interface so that communication can be achieved with the same code, regardless of the WebView library used.
+
+## Architecture
+
+WebView RPC simplifies the workflow compared to the traditional `JavaScript bridge` method.
+
+```mermaid
+flowchart LR
+ subgraph Traditional Method
+ direction LR
+ A[Unity C#] <--> B[Data Class
& Manual Method Implementation]
+ B <--> C[JSON Parser]
+ C <--> D[JavaScript Bridge]
+ D <--> E[JSON Parser]
+ E <--> F[Data Class
& Manual Method Implementation]
+ F <--> G[JavaScript WebView]
+ end
+
+ subgraph WebView RPC Method
+ direction LR
+ H[Unity C#
Direct Method Call] <--> I[Magic Space]
+ I <--> J[JavaScript
Direct Method Call]
+ end
+```
+
+### Internal Implementation
+
+Internally, WebView RPC is structured as follows:
+
+```mermaid
+flowchart LR
+ A[Unity C# Direct Call] <--> B[protobuf-generated C# Code] <--> C[protobuf Serializer/Deserializer] <--> D[Base64 Serializer/Deserializer] <--> E[JavaScript Bridge] <--> F[Base64 Serializer/Deserializer] <--> G[protobuf Serializer/Deserializer] <--> H[protobuf-generated JavaScript Code] <--> I[JavaScript Direct Call]
+```
+
+1. **Unity C# Direct Call**
+ - Calls an RPC interface function like a regular method in Unity.
+2. **protobuf-generated C# Code**
+ - Auto-generated C# wrapper/stub from the proto definition.
+ - RPC methods and data structures are based on protobuf.
+3. **Base64 Serializer + JavaScript Bridge**
+ - Converts raw byte data to Base64 before sending it through the WebView (browser).
+ - JavaScript receives the same format.
+4. **protobuf-generated JavaScript Code**
+ - Auto-generated JavaScript code from the same proto definition.
+ - Deserializes the serialized data from C# and directly calls JavaScript methods.
+
+With WebView RPC, method calls between C# and JavaScript behave like regular function calls, significantly reducing the need for repetitive JSON parsing and bridge implementations. This structure becomes even more maintainable as the project scales.
+
+## Examples
+### Unity Example
+Clone this whole repository.
+> [!NOTE]
+> Require [Viewplex Webview Asset(paid asset)](https://assetstore.unity.com/publishers/40309?locale=ko-KR&srsltid=AfmBOoqtnjTpJ-pw_5iGoS88XRtGX-tY2eJmP86PYoYCOhxrvz1OXRaJ) to run sample.
+### Javascript Example
+[webview_rpc_sample](https://github.com/kwan3854/Unity-WebViewRpc/tree/main/webview_rpc_sample)
+```bash
+# 1. Move to sample directory
+cd webview_rpc_sample
+
+# 2. Install dependencies
+npm install
+
+# 3. Build project
+npm run build
+```
+
+## Installation
+
+### Adding WebView RPC to a Unity Project
+
+1. Install the `Protobuf` package via NuGet Package Manager.
+2. Install the WebViewRpc package either via Package Manager or OpenUPM.
+
+- Add to `Packages/manifest.json`:
+
+ ```json
+ {
+ "dependencies": {
+ "com.kwanjoong.webviewrpc": "https://github.com/kwan3854/Unity-WebViewRpc.git?path=/Packages/WebviewRpc"
+ }
+ }
+ ```
+
+- Or via Package Manager:
+
+ 1. `Window` → `Package Manager` → `Add package from git URL...`
+ 2. Enter: `https://github.com/kwan3854/Unity-WebViewRpc.git?path=/Packages/WebviewRpc`
+
+- Or via OpenUPM:
+
+ ```bash
+ openupm add com.kwanjoong.webviewrpc
+ ```
+
+### Adding WebView Library
+#### Install
+```bash
+npm install app-webview-rpc
+```
+
+#### Usage
+```javascript
+import { VuplexBridge, WebViewRpcClient, WebViewRpcServer } from 'app-webview-rpc';
+
+// RPC client
+const bridge = new VuplexBridge();
+const rpcClient = new WebViewRpcClient(bridge);
+
+// RPC server
+const rpcServer = new WebViewRpcServer(bridge);
+```
+
+### Installing the protobuf Compiler
+
+#### Convert protobuf files to C# and JavaScript using `protoc`.
+
+**Mac**
+
+```bash
+brew install protobuf
+protoc --version # Ensure compiler version is 3+
+```
+
+**Windows**
+
+```bash
+winget install protobuf
+protoc --version # Ensure compiler version is 3+
+```
+
+**Linux**
+
+```bash
+apt install -y protobuf-compiler
+protoc --version # Ensure compiler version is 3+
+```
+
+### Installing the WebView RPC Code Generator
+
+Download the latest release from the [WebViewRPC Code Generator repository](https://github.com/kwan3854/ProtocGenWebviewRpc).
+
+- **Windows**: `protoc-gen-webviewrpc.exe`
+- **Mac**: `protoc-gen-webviewrpc`
+- **Linux**: Not provided (requires manual build).
+
+## Quick Start
+
+HelloWorld is a simple RPC service that receives a `HelloRequest` message and returns a `HelloResponse` message. In this example, we will implement HelloWorld and verify communication between the Unity client and the WebView client.
+
+The HelloWorld service takes a `HelloRequest` and returns a `HelloResponse`. First, let’s look at the example where the C# side acts as the server and the JavaScript side acts as the client.
+
+### Defining the protobuf File
+
+- protobuf is used to define the request and response formats of the service.
+- When the Unity client and the WebView have items to communicate, define the protobuf through discussion.
+- The following example is the `HelloWorld.proto` file, defining `HelloRequest`, `HelloResponse`, and the `HelloService` service.
+- In this example, the client side (JavaScript) calls the `SayHello` method, and the server side (C#) implements the `SayHello` method to process the request and return a response.
+
+```protobuf
+syntax = "proto3";
+
+package helloworld;
+
+// (Can be used as the namespace when generated in C#)
+option csharp_namespace = "HelloWorld";
+
+// Request message
+message HelloRequest {
+ string name = 1;
+}
+
+// Response message
+message HelloResponse {
+ string greeting = 1;
+}
+
+// Simple example service
+service HelloService {
+ // [one-way] Request -> Response
+ rpc SayHello (HelloRequest) returns (HelloResponse);
+}
+```
+
+### Generating C# and JavaScript from protobuf
+
+- We use the `protoc` compiler to convert the protobuf file into C# and JavaScript.
+- The `protoc` compiler transforms protobuf files into C# and JavaScript.
+- A [customized code generator](https://github.com/kwan3854/ProtocGenWebviewRpc) for WebView RPC is also available.
+- Run the following commands to generate C# and JavaScript code from the protobuf file.
+
+#### C# Common Code Generation (used by both server and client)
+
+```bash
+protoc -I. --csharp_out=. HelloWorld.proto
+
+// This produces HelloWorld.cs.
+```
+
+#### C# Server Code Generation
+
+```bash
+protoc \
+ --plugin=protoc-gen-webviewrpc=./protoc-gen-webviewrpc \
+ --webviewrpc_out=cs_server:. \
+ -I. HelloWorld.proto
+
+// This produces HelloWorld_HelloServiceBase.cs.
+```
+
+#### JavaScript Common Code Generation (for both client and server)
+
+> [!IMPORTANT]
+> [pbjs library is required.](https://www.npmjs.com/package/pbjs)
+
+```bash
+npx pbjs HelloWorld.proto --es6 hello_world.js
+
+// This produces hello_world.js.
+// Recommend setting the output filename to the same name as the service defined in the protobuf file.
+```
+
+#### JavaScript Client Code Generation
+
+```bash
+protoc \
+ --plugin=protoc-gen-webviewrpc=./protoc-gen-webviewrpc \
+ --webviewrpc_out=js_client:. \
+ -I. HelloWorld.proto
+
+// This produces HelloWorld_HelloServiceClient.js.
+```
+
+### Adding the Generated Code to Each Project
+
+- Add the generated code to each respective project.
+- You can use a GitHub action so that code is automatically generated and added to your project.
+
+### Implementing Bridge Code
+
+- The bridge code mediates communication between C# and JavaScript.
+- WebViewRpc is abstracted so it can be used with any WebView library.
+- Implement the bridge code according to your chosen WebView library.
+- Below is an example using Viewplex’s CanvasWebViewPrefab.
+
+```csharp
+using System;
+using Vuplex.WebView;
+using WebViewRPC;
+
+public class ViewplexWebViewBridge : IWebViewBridge
+{
+ public event Action OnMessageReceived;
+ private readonly CanvasWebViewPrefab _webViewPrefab;
+
+ public ViewplexWebViewBridge(CanvasWebViewPrefab webViewPrefab)
+ {
+ _webViewPrefab = webViewPrefab;
+
+ _webViewPrefab.WebView.MessageEmitted += (sender, args) =>
+ {
+ OnMessageReceived?.Invoke(args.Value);
+ };
+ }
+
+ public void SendMessageToWeb(string message)
+ {
+ _webViewPrefab.WebView.PostMessage(message);
+ }
+}
+```
+
+```javascript
+export class VuplexBridge {
+ constructor() {
+ this._onMessageCallback = null;
+ this._isVuplexReady = false;
+ this._pendingMessages = [];
+
+ // 1) If window.vuplex already exists, use it immediately
+ if (window.vuplex) {
+ this._isVuplexReady = true;
+ } else {
+ // Otherwise, wait for the 'vuplexready' event
+ window.addEventListener('vuplexready', () => {
+ this._isVuplexReady = true;
+ // Send all pending messages
+ for (const msg of this._pendingMessages) {
+ window.vuplex.postMessage(msg);
+ }
+ this._pendingMessages = [];
+ });
+ }
+
+ // 2) C# -> JS messages: "vuplexmessage" event
+ // event.value contains the string (sent by C# PostMessage)
+ window.addEventListener('vuplexmessage', event => {
+ const base64Str = event.value; // Typically Base64
+ if (this._onMessageCallback) {
+ this._onMessageCallback(base64Str);
+ }
+ });
+ }
+
+ /**
+ * JS -> C#: sends string (base64Str)
+ */
+ sendMessage(base64Str) {
+ // Vuplex serializes JS objects to JSON,
+ // but if we pass a string, it sends the string as is.
+ if (this._isVuplexReady && window.vuplex) {
+ window.vuplex.postMessage(base64Str);
+ } else {
+ // If vuplex isn’t ready yet, store messages in a queue
+ this._pendingMessages.push(base64Str);
+ }
+ }
+
+ /**
+ * onMessage(cb): registers a callback to receive strings from C#
+ */
+ onMessage(cb) {
+ this._onMessageCallback = cb;
+ }
+}
+```
+
+### Writing C# Server and JavaScript Client Code
+
+```csharp
+public class WebViewRpcTester : MonoBehaviour
+{
+ [SerializeField] private CanvasWebViewPrefab webViewPrefab;
+
+ private async void Start()
+ {
+ await InitializeWebView(webViewPrefab);
+
+ // Create the bridge
+ var bridge = new ViewplexWebViewBridge(webViewPrefab);
+ // Create the server
+ var server = new WebViewRPC.WebViewRpcServer(bridge)
+ {
+ Services =
+ {
+ // Bind HelloService
+ HelloService.BindService(new HelloWorldService()),
+ // Add other services if necessary
+ }
+ };
+
+ // Start the server
+ server.Start();
+ }
+
+ private async Task InitializeWebView(CanvasWebViewPrefab webView)
+ {
+ // Example uses Viewplex’s CanvasWebViewPrefab
+ await webView.WaitUntilInitialized();
+ webView.WebView.LoadUrl("http://localhost:8081");
+ await webView.WebView.WaitForNextPageLoadToFinish();
+ }
+}
+```
+
+```csharp
+using HelloWorld;
+using UnityEngine;
+
+namespace SampleRpc
+{
+ // Inherit HelloWorldService and implement the SayHello method.
+ // HelloWorldService is generated from HelloWorld.proto.
+ public class HelloWorldService : HelloServiceBase
+ {
+ public override HelloResponse SayHello(HelloRequest request)
+ {
+ Debug.Log($"Received request: {request.Name}");
+ return new HelloResponse()
+ {
+ // Process the request and return a response
+ Greeting = $"Hello, {request.Name}!"
+ };
+ }
+ }
+}
+```
+
+```javascript
+// 1) Create a bridge
+const bridge = new VuplexBridge();
+// 2) Create an RpcClient
+const rpcClient = new WebViewRpcClient(bridge);
+// 3) Create a HelloServiceClient
+const helloClient = new HelloServiceClient(rpcClient);
+
+document.getElementById('btnSayHello').addEventListener('click', async () => {
+ try {
+ const reqObj = { name: "Hello World! From WebView" };
+ console.log("Request to Unity: ", reqObj);
+
+ const resp = await helloClient.SayHello(reqObj);
+ console.log("Response from Unity: ", resp.greeting);
+ } catch (err) {
+ console.error("Error: ", err);
+ }
+});
+```
+
+### Running the Example
+
+- Run the `WebViewRpcTester` script in Unity, and open the WebView.
+- When you click the button in the WebView, Unity processes the request via `HelloService` and returns a response.
+
+### The Opposite Case (C# Client, JavaScript Server)
+
+- The reverse scenario can be implemented in the same way.
+- Since the common code is already generated, generate C# client code and JavaScript server code.
+
+#### Generating C# Client Code
+
+```bash
+protoc \
+ --plugin=protoc-gen-webviewrpc=./protoc-gen-webviewrpc \
+ --webviewrpc_out=cs_client:. \
+ -I. HelloWorld.proto
+```
+
+#### Generating JavaScript Server Code
+
+```bash
+protoc \
+ --plugin=protoc-gen-webviewrpc=./protoc-gen-webviewrpc \
+ --webviewrpc_out=js_server:. \
+ -I. HelloWorld.proto
+```
+
+#### Writing C# Client Code
+
+```csharp
+public class WebViewRpcTester : MonoBehaviour
+{
+ [SerializeField] private CanvasWebViewPrefab webViewPrefab;
+
+ private void Awake()
+ {
+ Web.ClearAllData();
+ }
+
+ private async void Start()
+ {
+ await InitializeWebView(webViewPrefab);
+
+ // Create the bridge
+ var bridge = new ViewplexWebViewBridge(webViewPrefab);
+ // Create an RpcClient
+ var rpcClient = new WebViewRPC.WebViewRpcClient(bridge);
+ // Create a HelloServiceClient
+ var client = new HelloServiceClient(rpcClient);
+
+ // Send a request
+ var response = await client.SayHello(new HelloRequest()
+ {
+ Name = "World"
+ });
+
+ // Check the response
+ Debug.Log($"Received response: {response.Greeting}");
+ }
+
+ private async Task InitializeWebView(CanvasWebViewPrefab webView)
+ {
+ await webView.WaitUntilInitialized();
+ webView.WebView.LoadUrl("http://localhost:8081");
+ await webView.WebView.WaitForNextPageLoadToFinish();
+ }
+}
+```
+
+#### Writing JavaScript Server Code
+
+```javascript
+// 1) Create a bridge
+const bridge = new VuplexBridge();
+// 2) Create an RpcServer
+const rpcServer = new WebViewRpcServer(bridge);
+// 3) Create a service implementation
+const impl = new MyHelloServiceImpl();
+// 4) Bind the service
+const def = HelloService.bindService(impl);
+// 5) Register the service
+rpcServer.services.push(def);
+// 6) Start the server
+rpcServer.start();
+```
+
+```javascript
+import { HelloServiceBase } from "./HelloWorld_HelloServiceBase.js";
+
+// Inherit HelloServiceBase from the auto-generated HelloWorld_HelloServiceBase.js
+export class MyHelloServiceImpl extends HelloServiceBase {
+ SayHello(requestObj) {
+ // Check the incoming request
+ console.log("JS Server received: ", requestObj);
+
+ // Process the request and return a response
+ return {
+ greeting: "Hello from JS! I got your message: " + requestObj.name
+ };
+ }
+}
+```
+
+## License
+This project is licensed under the MIT License - see the [LICENSE.md](LICENSE.md) file for details.
\ No newline at end of file
diff --git a/webview_rpc_runtime_library/README_ko.md b/webview_rpc_runtime_library/README_ko.md
new file mode 100644
index 0000000..bd5f804
--- /dev/null
+++ b/webview_rpc_runtime_library/README_ko.md
@@ -0,0 +1,466 @@
+[English](README.md) | [Korean](README_ko.md)
+
+[](https://openupm.com/packages/com.kwanjoong.webviewrpc/)
+[](LICENSE.md)
+# Unity WebView RPC
+유니티 클라이언트(C#)과 웹뷰(HTML, JS)간의 통신에 protobuf를 사용한 grpc와 유사하게 사용할 수 있도록 추상화 레이어를 제공합니다.
+기존의 통신방식인 `javascript bridge` 방식을 확장하여 RPC(Remote Procedure Call) 방식과 유사하게 사용할 수 있도록 구현하였습니다.
+특정 웹뷰 라이브러리에 종속되지 않도록 Bridge 인터페이스를 제공하여, 어떤 웹뷰 라이브러리를 사용하더라도 동일한 코드로 통신할 수 있도록 설계하였습니다.
+
+## 구조도
+WebView RPC는 기존의 `javascript bridge` 방식과 비교하여 작업 흐름이 단순해졌습니다.
+```mermaid
+flowchart LR
+ subgraph 기존 방식
+ direction LR
+ A[Unity C#] <--> B[데이터 클래스
및 메서드
수동 구현]
+ B <--> C[JSON 파서]
+ C <--> D[Javascript Bridge]
+ D <--> E[JSON 파서]
+ E <--> F[데이터 클래스
및 메서드
수동 구현]
+ F <--> G[Javascript 웹뷰]
+ end
+
+ subgraph WebView RPC 방식
+ direction LR
+ H[Unity C#
메서드 직접 호출] <--> I[마법의 공간]
+ I <--> J[JavaScript
메서드 직접 호출]
+ end
+```
+
+### 내부 구현
+WebView RPC는 내부적으로는 다음과 같은 구조로 구현되어 있습니다.
+```mermaid
+flowchart LR
+ A[Unity C# 코드
직접 호출] <--> B[protobuf로
생성된 C# 코드] <--> C[protobuf
시리얼/디시리얼라이저] <--> D[Base64
시리얼/디시리얼라이저] <--> E[JavaScript Bridge] <--> F[Base64
시리얼/디시리얼라이저] <--> G[protobuf
시리얼/디시리얼라이저] <--> H[protobuf로
생성된 JavaScript 코드] <--> I[JavaScript
코드 직접 호출]
+```
+1. Unity C# 코드 직접 호출
+ - Unity 쪽에서 RPC 인터페이스 함수를 일반 메서드처럼 호출
+2. protobuf로 생성된 C# 코드
+ - Proto 정의 파일을 바탕으로 자동 생성된 C# 래퍼/스텁
+ - RPC 메서드 및 데이터 구조가 protobuf 기반
+3. Base64 시리얼라이저 + JavaScript Bridge
+ - 실제 바이트 데이터를 Base64로 변환하여 WebView(브라우저)로 전송
+ - JavaScript 측에서도 동일 포맷으로 수신
+4. protobuf로 생성된 JavaScript 코드
+ - 동일한 Proto 정의를 사용해 자동 생성된 JS 코드
+ - C#에서 넘어온 직렬화 데이터를 역직렬화한 뒤, JS 메서드를 직접 호출
+
+이처럼 WebView RPC를 사용하면 C#과 JavaScript 간 상호 호출이 단순 함수 호출처럼 동작하며, 반복되는 JSON 파싱/Bridge 처리를 크게 줄일 수 있습니다.
+프로젝트 규모가 커질수록 이 구조가 훨씬 간결하고 유지보수하기 쉬워집니다.
+
+## 샘플 프로젝트 제공
+### Unity Example
+Clone this whole repository.
+> [!NOTE]
+> 샘플을 실행하기 위해서는 [Viewplex Webview Asset(유료)](https://assetstore.unity.com/publishers/40309?locale=ko-KR&srsltid=AfmBOoqtnjTpJ-pw_5iGoS88XRtGX-tY2eJmP86PYoYCOhxrvz1OXRaJ)이 필요합니다.
+### Javascript Example
+[webview_rpc_sample](https://github.com/kwan3854/Unity-WebViewRpc/tree/main/webview_rpc_sample)
+```bash
+# 1. Move to sample directory
+cd webview_rpc_sample
+
+# 2. Install dependencies
+npm install
+
+# 3. Build project
+npm run build
+```
+
+## 설치
+### 유니티 프로젝트에 WebView RPC 추가하기
+1. Nuget 패키지 매니저를 통해 `Protobuf` 패키지를 설치합니다.
+2. 패키지 매니저를 통해 설치하거나 OpenUpm을 통해 WebViewRpc 패키지를 설치합니다.
+- `Packages/manifest.json`
+ ```json
+ {
+ "dependencies": {
+ "com.kwanjoong.webviewrpc": "https://github.com/kwan3854/Unity-WebViewRpc.git?path=/Packages/WebviewRpc"
+ }
+ }
+ ```
+- 또는 Package Manager에서
+ 1. `Window` → `Package Manager` → `Add package from git URL...`
+ 2. `https://github.com/kwan3854/Unity-WebViewRpc.git?path=/Packages/WebviewRpc` 입력
+- 또는 OpenUpm에서
+ ```bash
+ openupm add com.kwanjoong.webviewrpc
+ ```
+### 웹뷰 라이브러리 추가하기
+- WebView RPC는 패키지로 배포되지 않습니다.
+- [Unity-WebViewRpc/WebViewRpcJS/RuntimeLibrary](https://github.com/kwan3854/Unity-WebViewRpc/tree/main/WebViewRpcJS/RuntimeLibrary) 하위의 파일들을 javascript 프로젝트에 추가합니다.
+
+### protobuf 파일 생성기 설치
+#### protobuf 파일을 C#과 javascript로 변환하기 위해 `protoc` 컴파일러를 사용합니다.
+**Mac**
+```bash
+brew install protobuf
+protoc --version # Ensure compiler version is 3+
+```
+**Windows**
+```bash
+winget install protobuf
+protoc --version # Ensure compiler version is 3+
+```
+
+**Linux**
+```bash
+apt install -y protobuf-compiler
+protoc --version # Ensure compiler version is 3+
+```
+
+#### WebView RPC 코드 생성기 설치
+[WebViewRPC 코드 생성기 레포지토리의 Release 페이지](https://github.com/kwan3854/ProtocGenWebviewRpc)에서 최신 릴리즈를 다운로드 받습니다.
+- Windows: `protoc-gen-webviewrpc.exe`
+- Mac: `protoc-gen-webviewrpc`
+- Linux: 제공하지 않습니다.(직접 빌드 필요)
+
+
+## 빠른 시작
+HelloWorld 라는 간단한 RPC 서비스를 구현하고, 유니티 클라이언트와 웹뷰 클라이언트 간의 통신을 확인해보겠습니다.
+
+HelloWorld 서비스는 `HelloRequest` 메시지를 받아 `HelloResponse` 메시지를 반환하는 간단한 RPC 서비스입니다.
+
+우선, C#측이 서버 역할을 하고, javascript 쪽이 클라이언트 역할을 하는 예시를 살펴봅시다.
+
+### protobuf 정의 하기
+- protobuf는 서비스의 요청과 응답 형삭을 정의하는데 사용됩니다.
+- 유니티 클라이언트와 웹뷰가 통신할 사항이 생겼을 때 회의를 통해 protobuf를 정의합시다.
+- 아래의 예시는 `HelloWorld.proto` 파일로, `HelloRequest`, `HelloResponse` 메시지와 `HelloService` 서비스를 정의하였습니다.
+- RPC를 요청하는 클라이언트 측(이번예시에서는 javascript)이 SayHello 메서드를 호출하면, 서버 측(이번예시에서는 C#)에서는 SayHello 메서드를 구현하여 요청을 처리하고 응답을 반환합니다.
+
+**HelloWorld.proto**
+```protobuf
+syntax = "proto3";
+
+package helloworld;
+
+// (C#에서 생성될 때 네임스페이스로 쓰일 수 있음)
+option csharp_namespace = "HelloWorld";
+
+// 요청 메시지
+message HelloRequest {
+ string name = 1;
+}
+
+// 응답 메시지
+message HelloResponse {
+ string greeting = 1;
+}
+
+// 간단한 서비스 예시
+service HelloService {
+ // [단방향] Request -> Response
+ rpc SayHello (HelloRequest) returns (HelloResponse);
+}
+```
+### protobuf 파일을 C#과 javascript로 변환하기
+- protobuf 파일을 C#과 javascript로 변환하기 위해 `protoc` 컴파일러를 사용합니다.
+- `protoc` 컴파일러는 protobuf 파일을 C#과 javascript로 변환하는데 사용됩니다.
+- WebView RPC를 위한 [별도의 커스터마이징 된 코드 생성기](https://github.com/kwan3854/ProtocGenWebviewRpc)가 준비되어 있습니다.
+- 아래의 명령어를 통해 protobuf 파일을 C#과 javascript로 변환합니다.
+
+**C# 공용 코드 생성하기 (C#측이 서버가 되든, 클라이언트가 되든 공용으로 사용되는 코드)**
+```bash
+protoc -I. --csharp_out=. HelloWorld.proto
+
+// 결과물로 HelloWorld.cs 파일이 생성됩니다.
+```
+
+**C# 서버 코드 생성하기**
+```bash
+protoc \
+ --plugin=protoc-gen-webviewrpc=./protoc-gen-webviewrpc \
+ --webviewrpc_out=cs_server:. \
+ -I. HelloWorld.proto
+
+// 결과물로 HelloWorld_HelloServiceBase.cs 파일이 생성됩니다.
+```
+
+**javascript 공용 코드 생성하기 (javascript측이 클라이언트가 되든, 서버가 되든 공용으로 사용되는 코드)**
+> [!IMPORTANT]
+> [pbjs 라이브러리가 필요합니다.](https://www.npmjs.com/package/pbjs)
+```bash
+npx pbjs HelloWorld.proto --es6 hello_world.js
+
+// 결과물로 hello_world.js 파일이 생성됩니다.
+// 결과물 이름은 proto 파일에서 Service 이름을 따서 생성하는 것을 권장합니다.
+```
+
+**javascript 클라이언트 코드 생성하기**
+```bash
+protoc \
+ --plugin=protoc-gen-webviewrpc=./protoc-gen-webviewrpc \
+ --webviewrpc_out=js_client:. \
+ -I. HelloWorld.proto
+
+// 결과물로 HelloWorld_HelloServiceClient.js 파일이 생성됩니다.
+```
+
+### 생성된 코드를 각자의 프로젝트에 추가하기
+- 생성된 코드를 각자의 프로젝트에 추가합니다.
+- Github action을 통해 자동으로 생성된 코드가 생성되고 프로젝트에 추가되도록 할 수 있습니다.
+
+### 브릿지 코드 구현하기
+- 브릿지 코드는 C#과 javascript 간의 통신을 중계하는 역할을 합니다.
+- 어떠한 웹뷰 라이브러리를 사용하더라도 WebViewRpc 를 사용할 수 있도록 구현이 추상화되어있습니다.
+- 각자의 웹뷰 라이브러리에 맞는 브릿지 코드를 구현합니다.
+- 아래는 Viewplex의 CanvasWebViewPrefab을 사용하는 예시입니다.
+
+```csharp
+using System;
+using Vuplex.WebView;
+using WebViewRPC;
+
+public class ViewplexWebViewBridge : IWebViewBridge
+{
+ public event Action OnMessageReceived;
+ private readonly CanvasWebViewPrefab _webViewPrefab;
+
+ public ViewplexWebViewBridge(CanvasWebViewPrefab webViewPrefab)
+ {
+ _webViewPrefab = webViewPrefab;
+
+ _webViewPrefab.WebView.MessageEmitted += (sender, args) =>
+ {
+ OnMessageReceived?.Invoke(args.Value);
+ };
+ }
+
+ public void SendMessageToWeb(string message)
+ {
+ _webViewPrefab.WebView.PostMessage(message);
+ }
+}
+```
+```javascript
+export class VuplexBridge {
+ constructor() {
+ this._onMessageCallback = null;
+ this._isVuplexReady = false;
+ this._pendingMessages = [];
+
+ // 1) 만약 window.vuplex가 이미 있으면 바로 사용
+ if (window.vuplex) {
+ this._isVuplexReady = true;
+ } else {
+ // 아직 window.vuplex가 없으므로 'vuplexready' 이벤트 대기
+ window.addEventListener('vuplexready', () => {
+ this._isVuplexReady = true;
+ // 대기중이던 메시지들을 모두 보냄
+ for (const msg of this._pendingMessages) {
+ window.vuplex.postMessage(msg);
+ }
+ this._pendingMessages = [];
+ });
+ }
+
+ // 2) C# -> JS 메시지: "vuplexmessage" 이벤트
+ // event.value 에 문자열이 들어있음 (C# PostMessage로 보낸)
+ window.addEventListener('vuplexmessage', event => {
+ const base64Str = event.value; // 보통 Base64
+ if (this._onMessageCallback) {
+ this._onMessageCallback(base64Str);
+ }
+ });
+ }
+
+ /**
+ * JS -> C# 문자열 전송 (base64Str)
+ */
+ sendMessage(base64Str) {
+ // Vuplex는 전달된 인자가 JS object이면 JSON 직렬화해 보내지만,
+ // 우리가 'string'을 넘기면 'string' 그대로 보냄.
+ if (this._isVuplexReady && window.vuplex) {
+ window.vuplex.postMessage(base64Str);
+ } else {
+ // 아직 vuplex가 준비 안됐으면 대기열에 저장
+ this._pendingMessages.push(base64Str);
+ }
+ }
+
+ /**
+ * onMessage(cb): JS가 C#으로부터 문자열을 받을 때 콜백 등록
+ */
+ onMessage(cb) {
+ this._onMessageCallback = cb;
+ }
+}
+```
+
+### C# 서버와 javascript 클라이언트 코드 작성하기
+
+```csharp
+public class WebViewRpcTester : MonoBehaviour
+{
+ [SerializeField] private CanvasWebViewPrefab webViewPrefab;
+
+ private async void Start()
+ {
+ await InitializeWebView(webViewPrefab);
+
+ // 브릿지 생성
+ var bridge = new ViewplexWebViewBridge(webViewPrefab);
+ // 서버 생성
+ var server = new WebViewRPC.WebViewRpcServer(bridge)
+ {
+ Services =
+ {
+ // HelloService를 바인딩
+ HelloService.BindService(new HelloWorldService()),
+ // 다른 서비스도 추가 가능
+ }
+ };
+
+ // 서버 시작
+ server.Start();
+ }
+
+ private async Task InitializeWebView(CanvasWebViewPrefab webView)
+ {
+ // 예시로 사용한 WebViewPrefab은 Viewplex의 CanvasWebViewPrefab을 사용하였습니다.
+ await webView.WaitUntilInitialized();
+ webView.WebView.LoadUrl("http://localhost:8081");
+ await webView.WebView.WaitForNextPageLoadToFinish();
+ }
+}
+```
+```csharp
+using HelloWorld;
+using UnityEngine;
+
+namespace SampleRpc
+{
+ // HelloWorldService를 상속받아 SayHello 메서드를 구현
+ // HelloWorldService는 HelloWorld.proto에서 생성된 코드입니다.
+ public class HelloWorldService : HelloServiceBase
+ {
+ public override HelloResponse SayHello(HelloRequest request)
+ {
+ Debug.Log($"Received request: {request.Name}");
+ return new HelloResponse()
+ {
+ // 요청을 받아 응답을 반환
+ Greeting = $"Hello, {request.Name}!"
+ };
+ }
+ }
+}
+```
+```javascript
+// 1) 브리지 생성
+const bridge = new VuplexBridge();
+// 2) RpcClient 생성
+const rpcClient = new WebViewRpcClient(bridge);
+// 3) HelloServiceClient
+const helloClient = new HelloServiceClient(rpcClient);
+
+document.getElementById('btnSayHello').addEventListener('click', async () => {
+ try {
+ const reqObj = {name: "Hello World! From WebView"};
+ console.log("Request to Unity: ", reqObj);
+
+ const resp = await helloClient.SayHello(reqObj);
+ console.log("Response from Unity: ", resp.greeting);
+ } catch (err) {
+ console.error("Error: ", err);
+ }
+});
+```
+### 실행하기
+- 유니티에서 WebViewRpcTester 스크립트를 실행하고, 웹뷰를 띄웁니다.
+- 웹뷰에서 버튼을 클릭하면, 유니티에서 HelloService를 통해 요청을 처리하고 응답을 반환합니다.
+
+### 반대의 경우 (C# 클라이언트, javascript 서버)
+- 반대의 경우도 동일하게 구현이 가능합니다.
+- 이미 공용 코드가 생성되어 있으므로, C# 클라이언트와 javascript 서버 코드를 생성 합니다.
+
+**C# 클라이언트 코드 생성하기**
+```bash
+protoc \
+ --plugin=protoc-gen-webviewrpc=./protoc-gen-webviewrpc \
+ --webviewrpc_out=cs_client:. \
+ -I. HelloWorld.proto
+```
+**javascript 서버 코드 생성하기**
+```bash
+protoc \
+ --plugin=protoc-gen-webviewrpc=./protoc-gen-webviewrpc \
+ --webviewrpc_out=js_server:. \
+ -I. HelloWorld.proto
+```
+
+**C# 클라이언트 코드 작성하기**
+```csharp
+public class WebViewRpcTester : MonoBehaviour
+{
+ [SerializeField] private CanvasWebViewPrefab webViewPrefab;
+
+ private void Awake()
+ {
+ Web.ClearAllData();
+ }
+
+ private async void Start()
+ {
+ await InitializeWebView(webViewPrefab);
+
+ // 브릿지 생성
+ var bridge = new ViewplexWebViewBridge(webViewPrefab);
+ // RpcClient 생성
+ var rpcClient = new WebViewRPC.WebViewRpcClient(bridge);
+ // HelloServiceClient 생성
+ var client = new HelloServiceClient(rpcClient);
+
+ // 요청 보내기
+ var response = await client.SayHello(new HelloRequest()
+ {
+ Name = "World"
+ });
+
+ // 응답 확인
+ Debug.Log($"Received response: {response.Greeting}");
+ }
+
+ private async Task InitializeWebView(CanvasWebViewPrefab webView)
+ {
+ await webView.WaitUntilInitialized();
+ webView.WebView.LoadUrl("http://localhost:8081");
+ await webView.WebView.WaitForNextPageLoadToFinish();
+ }
+}
+```
+
+**javascript 서버 코드 작성하기**
+```javascript
+// 1) 브리지 생성
+const bridge = new VuplexBridge();
+// 2) RpcServer 생성
+const rpcServer = new WebViewRpcServer(bridge);
+// 3) 서비스 구현체 생성
+const impl = new MyHelloServiceImpl();
+// 4) 서비스 바인딩
+const def = HelloService.bindService(impl);
+// 5) 서비스 등록
+rpcServer.services.push(def);
+// 6) 서버 시작
+rpcServer.start();
+```
+```javascript
+import { HelloServiceBase } from "./HelloWorld_HelloServiceBase.js";
+
+// 자동 생성된 HelloWorld_HelloServiceBase.js의 HelloServiceBase를 상속받아 구현
+export class MyHelloServiceImpl extends HelloServiceBase {
+ SayHello(requestObj) {
+ // 받은 요청을 확인
+ console.log("JS Server received: ", requestObj);
+
+ // 요청을 받아 응답을 반환
+ return { greeting: "Hello from JS! I got your message: " + requestObj.name };
+ }
+}
+
+```
+
+## License
+This project is licensed under the MIT License - see the [LICENSE.md](LICENSE.md) file for details.
\ No newline at end of file
diff --git a/webview_rpc_runtime_library/package-lock.json b/webview_rpc_runtime_library/package-lock.json
new file mode 100644
index 0000000..a167e0f
--- /dev/null
+++ b/webview_rpc_runtime_library/package-lock.json
@@ -0,0 +1,517 @@
+{
+ "name": "app-webview-rpc",
+ "version": "0.1.1",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "app-webview-rpc",
+ "version": "0.1.1",
+ "license": "MIT",
+ "devDependencies": {
+ "rimraf": "^5.0.0",
+ "typescript": "^5.0.0"
+ }
+ },
+ "node_modules/@isaacs/cliui": {
+ "version": "8.0.2",
+ "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
+ "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==",
+ "dev": true,
+ "dependencies": {
+ "string-width": "^5.1.2",
+ "string-width-cjs": "npm:string-width@^4.2.0",
+ "strip-ansi": "^7.0.1",
+ "strip-ansi-cjs": "npm:strip-ansi@^6.0.1",
+ "wrap-ansi": "^8.1.0",
+ "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@pkgjs/parseargs": {
+ "version": "0.11.0",
+ "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
+ "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==",
+ "dev": true,
+ "optional": true,
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/ansi-regex": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz",
+ "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==",
+ "dev": true,
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-regex?sponsor=1"
+ }
+ },
+ "node_modules/ansi-styles": {
+ "version": "6.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz",
+ "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==",
+ "dev": true,
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/balanced-match": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+ "dev": true
+ },
+ "node_modules/brace-expansion": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+ "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+ "dev": true,
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
+ },
+ "node_modules/cross-spawn": {
+ "version": "7.0.6",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
+ "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
+ "dev": true,
+ "dependencies": {
+ "path-key": "^3.1.0",
+ "shebang-command": "^2.0.0",
+ "which": "^2.0.1"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/eastasianwidth": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
+ "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==",
+ "dev": true
+ },
+ "node_modules/emoji-regex": {
+ "version": "9.2.2",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
+ "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
+ "dev": true
+ },
+ "node_modules/foreground-child": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz",
+ "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==",
+ "dev": true,
+ "dependencies": {
+ "cross-spawn": "^7.0.0",
+ "signal-exit": "^4.0.1"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/glob": {
+ "version": "10.4.5",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz",
+ "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==",
+ "dev": true,
+ "dependencies": {
+ "foreground-child": "^3.1.0",
+ "jackspeak": "^3.1.2",
+ "minimatch": "^9.0.4",
+ "minipass": "^7.1.2",
+ "package-json-from-dist": "^1.0.0",
+ "path-scurry": "^1.11.1"
+ },
+ "bin": {
+ "glob": "dist/esm/bin.mjs"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/is-fullwidth-code-point": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/isexe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
+ "dev": true
+ },
+ "node_modules/jackspeak": {
+ "version": "3.4.3",
+ "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz",
+ "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==",
+ "dev": true,
+ "dependencies": {
+ "@isaacs/cliui": "^8.0.2"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ },
+ "optionalDependencies": {
+ "@pkgjs/parseargs": "^0.11.0"
+ }
+ },
+ "node_modules/lru-cache": {
+ "version": "10.4.3",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz",
+ "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
+ "dev": true
+ },
+ "node_modules/minimatch": {
+ "version": "9.0.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
+ "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
+ "dev": true,
+ "dependencies": {
+ "brace-expansion": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/minipass": {
+ "version": "7.1.2",
+ "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz",
+ "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==",
+ "dev": true,
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ }
+ },
+ "node_modules/package-json-from-dist": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz",
+ "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==",
+ "dev": true
+ },
+ "node_modules/path-key": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
+ "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/path-scurry": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz",
+ "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==",
+ "dev": true,
+ "dependencies": {
+ "lru-cache": "^10.2.0",
+ "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/rimraf": {
+ "version": "5.0.10",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.10.tgz",
+ "integrity": "sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==",
+ "dev": true,
+ "dependencies": {
+ "glob": "^10.3.7"
+ },
+ "bin": {
+ "rimraf": "dist/esm/bin.mjs"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/shebang-command": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
+ "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+ "dev": true,
+ "dependencies": {
+ "shebang-regex": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/shebang-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
+ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/signal-exit": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
+ "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
+ "dev": true,
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/string-width": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
+ "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==",
+ "dev": true,
+ "dependencies": {
+ "eastasianwidth": "^0.2.0",
+ "emoji-regex": "^9.2.2",
+ "strip-ansi": "^7.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/string-width-cjs": {
+ "name": "string-width",
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "dev": true,
+ "dependencies": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/string-width-cjs/node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/string-width-cjs/node_modules/emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "dev": true
+ },
+ "node_modules/string-width-cjs/node_modules/strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "dev": true,
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/strip-ansi": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
+ "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
+ "dev": true,
+ "dependencies": {
+ "ansi-regex": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/strip-ansi?sponsor=1"
+ }
+ },
+ "node_modules/strip-ansi-cjs": {
+ "name": "strip-ansi",
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "dev": true,
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/strip-ansi-cjs/node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/typescript": {
+ "version": "5.7.3",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.3.tgz",
+ "integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==",
+ "dev": true,
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=14.17"
+ }
+ },
+ "node_modules/which": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+ "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+ "dev": true,
+ "dependencies": {
+ "isexe": "^2.0.0"
+ },
+ "bin": {
+ "node-which": "bin/node-which"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/wrap-ansi": {
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz",
+ "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^6.1.0",
+ "string-width": "^5.0.1",
+ "strip-ansi": "^7.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
+ "node_modules/wrap-ansi-cjs": {
+ "name": "wrap-ansi",
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+ "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
+ "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "dev": true
+ },
+ "node_modules/wrap-ansi-cjs/node_modules/string-width": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "dev": true,
+ "dependencies": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "dev": true,
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ }
+ }
+}
diff --git a/webview_rpc_runtime_library/package.json b/webview_rpc_runtime_library/package.json
new file mode 100644
index 0000000..24961e0
--- /dev/null
+++ b/webview_rpc_runtime_library/package.json
@@ -0,0 +1,38 @@
+{
+ "name": "app-webview-rpc",
+ "version": "0.1.1",
+ "type": "module",
+ "description": "WebView RPC provides an abstraction layer that allows communication between the App (e.g. Unity C#) and WebView (HTML, JS) using protobuf, similar to gRPC.",
+ "main": "./dist/cjs/index.js",
+ "module": "./dist/esm/index.js",
+ "exports": {
+ ".": {
+ "require": "./dist/cjs/index.js",
+ "import": "./dist/esm/index.js"
+ }
+ },
+ "files": [
+ "dist",
+ "src"
+ ],
+ "scripts": {
+ "build": "rimraf dist && npm run build:esm && npm run build:cjs",
+ "build:esm": "tsc -p tsconfig.json",
+ "build:cjs": "tsc -p tsconfig-cjs.json"
+ },
+ "devDependencies": {
+ "rimraf": "^5.0.0",
+ "typescript": "^5.0.0"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/kwan3854/Unity-WebViewRpc.git",
+ "directory": "webview_rpc_runtime_library"
+ },
+ "author": "Kwanjoong Lee",
+ "license": "MIT",
+ "bugs": {
+ "url": "https://github.com/kwan3854/Unity-WebViewRpc/issues"
+ },
+ "homepage": "https://github.com/kwan3854/Unity-WebViewRpc#readme"
+}
diff --git a/WebViewRpcJS/unity_bridge.js b/webview_rpc_runtime_library/src/bridges/viewplex_bridge.js
similarity index 92%
rename from WebViewRpcJS/unity_bridge.js
rename to webview_rpc_runtime_library/src/bridges/viewplex_bridge.js
index ccc2db5..aae2901 100644
--- a/WebViewRpcJS/unity_bridge.js
+++ b/webview_rpc_runtime_library/src/bridges/viewplex_bridge.js
@@ -1,5 +1,9 @@
export class VuplexBridge {
constructor() {
+ if (typeof window === 'undefined') {
+ throw new Error('VuplexBridge requires browser environment');
+ }
+
this._onMessageCallback = null;
this._isVuplexReady = false;
this._pendingMessages = [];
diff --git a/WebViewRpcJS/RuntimeLibrary/rpc_envelope.js b/webview_rpc_runtime_library/src/core/rpc_envelope.js
similarity index 100%
rename from WebViewRpcJS/RuntimeLibrary/rpc_envelope.js
rename to webview_rpc_runtime_library/src/core/rpc_envelope.js
diff --git a/WebViewRpcJS/RuntimeLibrary/service_definition.js b/webview_rpc_runtime_library/src/core/service_definition.js
similarity index 100%
rename from WebViewRpcJS/RuntimeLibrary/service_definition.js
rename to webview_rpc_runtime_library/src/core/service_definition.js
diff --git a/WebViewRpcJS/RuntimeLibrary/webview_rpc_client.js b/webview_rpc_runtime_library/src/core/webview_rpc_client.js
similarity index 100%
rename from WebViewRpcJS/RuntimeLibrary/webview_rpc_client.js
rename to webview_rpc_runtime_library/src/core/webview_rpc_client.js
diff --git a/WebViewRpcJS/RuntimeLibrary/webview_rpc_server.js b/webview_rpc_runtime_library/src/core/webview_rpc_server.js
similarity index 100%
rename from WebViewRpcJS/RuntimeLibrary/webview_rpc_server.js
rename to webview_rpc_runtime_library/src/core/webview_rpc_server.js
diff --git a/WebViewRpcJS/RuntimeLibrary/webview_rpc_utils.js b/webview_rpc_runtime_library/src/core/webview_rpc_utils.js
similarity index 72%
rename from WebViewRpcJS/RuntimeLibrary/webview_rpc_utils.js
rename to webview_rpc_runtime_library/src/core/webview_rpc_utils.js
index 078098e..b6bab5e 100644
--- a/WebViewRpcJS/RuntimeLibrary/webview_rpc_utils.js
+++ b/webview_rpc_runtime_library/src/core/webview_rpc_utils.js
@@ -18,21 +18,29 @@ export function encodeEnvelopeToBase64(envObj) {
}
/** Uint8Array -> Base64 (browser JS) */
+let isNode = typeof window === 'undefined';
+
export function base64FromBytes(u8arr) {
+ if (isNode) {
+ return Buffer.from(u8arr).toString('base64');
+ } else {
let binary = "";
for (let i = 0; i < u8arr.length; i++) {
- binary += String.fromCharCode(u8arr[i]);
+ binary += String.fromCharCode(u8arr[i]);
}
return btoa(binary);
+ }
}
-/** Base64 -> Uint8Array (browser JS) */
export function base64ToBytes(b64) {
+ if (isNode) {
+ return Buffer.from(b64, 'base64');
+ } else {
const bin = atob(b64);
- const len = bin.length;
- const u8 = new Uint8Array(len);
- for (let i = 0; i < len; i++) {
- u8[i] = bin.charCodeAt(i);
+ const u8 = new Uint8Array(bin.length);
+ for (let i = 0; i < bin.length; i++) {
+ u8[i] = bin.charCodeAt(i);
}
return u8;
+ }
}
diff --git a/webview_rpc_runtime_library/src/index.js b/webview_rpc_runtime_library/src/index.js
new file mode 100644
index 0000000..c7b2244
--- /dev/null
+++ b/webview_rpc_runtime_library/src/index.js
@@ -0,0 +1,4 @@
+export { VuplexBridge } from './bridges/viewplex_bridge.js';
+export { WebViewRpcClient } from './core/webview_rpc_client.js';
+export { WebViewRpcServer } from './core/webview_rpc_server.js';
+export { ServiceDefinition } from './core/service_definition.js';
\ No newline at end of file
diff --git a/webview_rpc_runtime_library/tsconfig-cjs.json b/webview_rpc_runtime_library/tsconfig-cjs.json
new file mode 100644
index 0000000..fbd0775
--- /dev/null
+++ b/webview_rpc_runtime_library/tsconfig-cjs.json
@@ -0,0 +1,12 @@
+// tsconfig-cjs.json (CommonJS용)
+{
+ "compilerOptions": {
+ "target": "es2018",
+ "module": "commonjs",
+ "moduleResolution": "node",
+ "outDir": "./dist/cjs",
+ "allowJs": true,
+ "declaration": true
+ },
+ "include": ["src/**/*"]
+}
\ No newline at end of file
diff --git a/webview_rpc_runtime_library/tsconfig.json b/webview_rpc_runtime_library/tsconfig.json
new file mode 100644
index 0000000..cf1290e
--- /dev/null
+++ b/webview_rpc_runtime_library/tsconfig.json
@@ -0,0 +1,12 @@
+// tsconfig.json (ESM용)
+{
+ "compilerOptions": {
+ "target": "es2018",
+ "module": "esnext",
+ "moduleResolution": "node",
+ "outDir": "./dist/esm",
+ "allowJs": true,
+ "declaration": true
+ },
+ "include": ["src/**/*"]
+}
\ No newline at end of file
diff --git a/webview_rpc_sample/index.html b/webview_rpc_sample/index.html
new file mode 100644
index 0000000..96465cf
--- /dev/null
+++ b/webview_rpc_sample/index.html
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+ Unity WebViewRPC Test
+
+
+
+ Unity WebViewRPC Test
+
+
+
+
+
\ No newline at end of file
diff --git a/webview_rpc_sample/package-lock.json b/webview_rpc_sample/package-lock.json
new file mode 100644
index 0000000..8794128
--- /dev/null
+++ b/webview_rpc_sample/package-lock.json
@@ -0,0 +1,1384 @@
+{
+ "name": "webview_rpc_sample",
+ "version": "1.0.0",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "webview_rpc_sample",
+ "version": "1.0.0",
+ "license": "ISC",
+ "dependencies": {
+ "app-webview-rpc": "^0.1.0"
+ },
+ "devDependencies": {
+ "webpack": "^5.98.0",
+ "webpack-cli": "^6.0.1"
+ }
+ },
+ "node_modules/@discoveryjs/json-ext": {
+ "version": "0.6.3",
+ "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.6.3.tgz",
+ "integrity": "sha512-4B4OijXeVNOPZlYA2oEwWOTkzyltLao+xbotHQeqN++Rv27Y6s818+n2Qkp8q+Fxhn0t/5lA5X1Mxktud8eayQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=14.17.0"
+ }
+ },
+ "node_modules/@jridgewell/gen-mapping": {
+ "version": "0.3.8",
+ "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz",
+ "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==",
+ "dev": true,
+ "dependencies": {
+ "@jridgewell/set-array": "^1.2.1",
+ "@jridgewell/sourcemap-codec": "^1.4.10",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/resolve-uri": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
+ "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/set-array": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz",
+ "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/source-map": {
+ "version": "0.3.6",
+ "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz",
+ "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==",
+ "dev": true,
+ "dependencies": {
+ "@jridgewell/gen-mapping": "^0.3.5",
+ "@jridgewell/trace-mapping": "^0.3.25"
+ }
+ },
+ "node_modules/@jridgewell/sourcemap-codec": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz",
+ "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==",
+ "dev": true
+ },
+ "node_modules/@jridgewell/trace-mapping": {
+ "version": "0.3.25",
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz",
+ "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==",
+ "dev": true,
+ "dependencies": {
+ "@jridgewell/resolve-uri": "^3.1.0",
+ "@jridgewell/sourcemap-codec": "^1.4.14"
+ }
+ },
+ "node_modules/@types/eslint": {
+ "version": "9.6.1",
+ "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz",
+ "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==",
+ "dev": true,
+ "dependencies": {
+ "@types/estree": "*",
+ "@types/json-schema": "*"
+ }
+ },
+ "node_modules/@types/eslint-scope": {
+ "version": "3.7.7",
+ "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz",
+ "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==",
+ "dev": true,
+ "dependencies": {
+ "@types/eslint": "*",
+ "@types/estree": "*"
+ }
+ },
+ "node_modules/@types/estree": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz",
+ "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==",
+ "dev": true
+ },
+ "node_modules/@types/json-schema": {
+ "version": "7.0.15",
+ "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
+ "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==",
+ "dev": true
+ },
+ "node_modules/@types/node": {
+ "version": "22.13.4",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.4.tgz",
+ "integrity": "sha512-ywP2X0DYtX3y08eFVx5fNIw7/uIv8hYUKgXoK8oayJlLnKcRfEYCxWMVE1XagUdVtCJlZT1AU4LXEABW+L1Peg==",
+ "dev": true,
+ "dependencies": {
+ "undici-types": "~6.20.0"
+ }
+ },
+ "node_modules/@webassemblyjs/ast": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz",
+ "integrity": "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==",
+ "dev": true,
+ "dependencies": {
+ "@webassemblyjs/helper-numbers": "1.13.2",
+ "@webassemblyjs/helper-wasm-bytecode": "1.13.2"
+ }
+ },
+ "node_modules/@webassemblyjs/floating-point-hex-parser": {
+ "version": "1.13.2",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz",
+ "integrity": "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==",
+ "dev": true
+ },
+ "node_modules/@webassemblyjs/helper-api-error": {
+ "version": "1.13.2",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz",
+ "integrity": "sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==",
+ "dev": true
+ },
+ "node_modules/@webassemblyjs/helper-buffer": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz",
+ "integrity": "sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==",
+ "dev": true
+ },
+ "node_modules/@webassemblyjs/helper-numbers": {
+ "version": "1.13.2",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz",
+ "integrity": "sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==",
+ "dev": true,
+ "dependencies": {
+ "@webassemblyjs/floating-point-hex-parser": "1.13.2",
+ "@webassemblyjs/helper-api-error": "1.13.2",
+ "@xtuc/long": "4.2.2"
+ }
+ },
+ "node_modules/@webassemblyjs/helper-wasm-bytecode": {
+ "version": "1.13.2",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz",
+ "integrity": "sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==",
+ "dev": true
+ },
+ "node_modules/@webassemblyjs/helper-wasm-section": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz",
+ "integrity": "sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==",
+ "dev": true,
+ "dependencies": {
+ "@webassemblyjs/ast": "1.14.1",
+ "@webassemblyjs/helper-buffer": "1.14.1",
+ "@webassemblyjs/helper-wasm-bytecode": "1.13.2",
+ "@webassemblyjs/wasm-gen": "1.14.1"
+ }
+ },
+ "node_modules/@webassemblyjs/ieee754": {
+ "version": "1.13.2",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz",
+ "integrity": "sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==",
+ "dev": true,
+ "dependencies": {
+ "@xtuc/ieee754": "^1.2.0"
+ }
+ },
+ "node_modules/@webassemblyjs/leb128": {
+ "version": "1.13.2",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.13.2.tgz",
+ "integrity": "sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==",
+ "dev": true,
+ "dependencies": {
+ "@xtuc/long": "4.2.2"
+ }
+ },
+ "node_modules/@webassemblyjs/utf8": {
+ "version": "1.13.2",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.13.2.tgz",
+ "integrity": "sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==",
+ "dev": true
+ },
+ "node_modules/@webassemblyjs/wasm-edit": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz",
+ "integrity": "sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==",
+ "dev": true,
+ "dependencies": {
+ "@webassemblyjs/ast": "1.14.1",
+ "@webassemblyjs/helper-buffer": "1.14.1",
+ "@webassemblyjs/helper-wasm-bytecode": "1.13.2",
+ "@webassemblyjs/helper-wasm-section": "1.14.1",
+ "@webassemblyjs/wasm-gen": "1.14.1",
+ "@webassemblyjs/wasm-opt": "1.14.1",
+ "@webassemblyjs/wasm-parser": "1.14.1",
+ "@webassemblyjs/wast-printer": "1.14.1"
+ }
+ },
+ "node_modules/@webassemblyjs/wasm-gen": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz",
+ "integrity": "sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==",
+ "dev": true,
+ "dependencies": {
+ "@webassemblyjs/ast": "1.14.1",
+ "@webassemblyjs/helper-wasm-bytecode": "1.13.2",
+ "@webassemblyjs/ieee754": "1.13.2",
+ "@webassemblyjs/leb128": "1.13.2",
+ "@webassemblyjs/utf8": "1.13.2"
+ }
+ },
+ "node_modules/@webassemblyjs/wasm-opt": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz",
+ "integrity": "sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==",
+ "dev": true,
+ "dependencies": {
+ "@webassemblyjs/ast": "1.14.1",
+ "@webassemblyjs/helper-buffer": "1.14.1",
+ "@webassemblyjs/wasm-gen": "1.14.1",
+ "@webassemblyjs/wasm-parser": "1.14.1"
+ }
+ },
+ "node_modules/@webassemblyjs/wasm-parser": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz",
+ "integrity": "sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==",
+ "dev": true,
+ "dependencies": {
+ "@webassemblyjs/ast": "1.14.1",
+ "@webassemblyjs/helper-api-error": "1.13.2",
+ "@webassemblyjs/helper-wasm-bytecode": "1.13.2",
+ "@webassemblyjs/ieee754": "1.13.2",
+ "@webassemblyjs/leb128": "1.13.2",
+ "@webassemblyjs/utf8": "1.13.2"
+ }
+ },
+ "node_modules/@webassemblyjs/wast-printer": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz",
+ "integrity": "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==",
+ "dev": true,
+ "dependencies": {
+ "@webassemblyjs/ast": "1.14.1",
+ "@xtuc/long": "4.2.2"
+ }
+ },
+ "node_modules/@webpack-cli/configtest": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-3.0.1.tgz",
+ "integrity": "sha512-u8d0pJ5YFgneF/GuvEiDA61Tf1VDomHHYMjv/wc9XzYj7nopltpG96nXN5dJRstxZhcNpV1g+nT6CydO7pHbjA==",
+ "dev": true,
+ "engines": {
+ "node": ">=18.12.0"
+ },
+ "peerDependencies": {
+ "webpack": "^5.82.0",
+ "webpack-cli": "6.x.x"
+ }
+ },
+ "node_modules/@webpack-cli/info": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-3.0.1.tgz",
+ "integrity": "sha512-coEmDzc2u/ffMvuW9aCjoRzNSPDl/XLuhPdlFRpT9tZHmJ/039az33CE7uH+8s0uL1j5ZNtfdv0HkfaKRBGJsQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=18.12.0"
+ },
+ "peerDependencies": {
+ "webpack": "^5.82.0",
+ "webpack-cli": "6.x.x"
+ }
+ },
+ "node_modules/@webpack-cli/serve": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-3.0.1.tgz",
+ "integrity": "sha512-sbgw03xQaCLiT6gcY/6u3qBDn01CWw/nbaXl3gTdTFuJJ75Gffv3E3DBpgvY2fkkrdS1fpjaXNOmJlnbtKauKg==",
+ "dev": true,
+ "engines": {
+ "node": ">=18.12.0"
+ },
+ "peerDependencies": {
+ "webpack": "^5.82.0",
+ "webpack-cli": "6.x.x"
+ },
+ "peerDependenciesMeta": {
+ "webpack-dev-server": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@xtuc/ieee754": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz",
+ "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==",
+ "dev": true
+ },
+ "node_modules/@xtuc/long": {
+ "version": "4.2.2",
+ "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz",
+ "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==",
+ "dev": true
+ },
+ "node_modules/acorn": {
+ "version": "8.14.0",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz",
+ "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==",
+ "dev": true,
+ "bin": {
+ "acorn": "bin/acorn"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/ajv": {
+ "version": "8.17.1",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
+ "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
+ "dev": true,
+ "dependencies": {
+ "fast-deep-equal": "^3.1.3",
+ "fast-uri": "^3.0.1",
+ "json-schema-traverse": "^1.0.0",
+ "require-from-string": "^2.0.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
+ }
+ },
+ "node_modules/ajv-formats": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz",
+ "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==",
+ "dev": true,
+ "dependencies": {
+ "ajv": "^8.0.0"
+ },
+ "peerDependencies": {
+ "ajv": "^8.0.0"
+ },
+ "peerDependenciesMeta": {
+ "ajv": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/ajv-keywords": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz",
+ "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==",
+ "dev": true,
+ "dependencies": {
+ "fast-deep-equal": "^3.1.3"
+ },
+ "peerDependencies": {
+ "ajv": "^8.8.2"
+ }
+ },
+ "node_modules/app-webview-rpc": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/app-webview-rpc/-/app-webview-rpc-0.1.0.tgz",
+ "integrity": "sha512-TdfpZXCF+HT8HKJbteNQMKrWjdBCjUL4Lsl/4aJMvl+W6xecfXIshuozrwZI+uEz8LLzTeP4nkTkzWm2bruHSQ=="
+ },
+ "node_modules/browserslist": {
+ "version": "4.24.4",
+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz",
+ "integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "dependencies": {
+ "caniuse-lite": "^1.0.30001688",
+ "electron-to-chromium": "^1.5.73",
+ "node-releases": "^2.0.19",
+ "update-browserslist-db": "^1.1.1"
+ },
+ "bin": {
+ "browserslist": "cli.js"
+ },
+ "engines": {
+ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
+ }
+ },
+ "node_modules/buffer-from": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
+ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
+ "dev": true
+ },
+ "node_modules/caniuse-lite": {
+ "version": "1.0.30001699",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001699.tgz",
+ "integrity": "sha512-b+uH5BakXZ9Do9iK+CkDmctUSEqZl+SP056vc5usa0PL+ev5OHw003rZXcnjNDv3L8P5j6rwT6C0BPKSikW08w==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/caniuse-lite"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ]
+ },
+ "node_modules/chrome-trace-event": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz",
+ "integrity": "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.0"
+ }
+ },
+ "node_modules/clone-deep": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz",
+ "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==",
+ "dev": true,
+ "dependencies": {
+ "is-plain-object": "^2.0.4",
+ "kind-of": "^6.0.2",
+ "shallow-clone": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/colorette": {
+ "version": "2.0.20",
+ "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz",
+ "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==",
+ "dev": true
+ },
+ "node_modules/commander": {
+ "version": "2.20.3",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
+ "dev": true
+ },
+ "node_modules/cross-spawn": {
+ "version": "7.0.6",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
+ "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
+ "dev": true,
+ "dependencies": {
+ "path-key": "^3.1.0",
+ "shebang-command": "^2.0.0",
+ "which": "^2.0.1"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/electron-to-chromium": {
+ "version": "1.5.99",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.99.tgz",
+ "integrity": "sha512-77c/+fCyL2U+aOyqfIFi89wYLBeSTCs55xCZL0oFH0KjqsvSvyh6AdQ+UIl1vgpnQQE6g+/KK8hOIupH6VwPtg==",
+ "dev": true
+ },
+ "node_modules/enhanced-resolve": {
+ "version": "5.18.1",
+ "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.1.tgz",
+ "integrity": "sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==",
+ "dev": true,
+ "dependencies": {
+ "graceful-fs": "^4.2.4",
+ "tapable": "^2.2.0"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "node_modules/envinfo": {
+ "version": "7.14.0",
+ "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.14.0.tgz",
+ "integrity": "sha512-CO40UI41xDQzhLB1hWyqUKgFhs250pNcGbyGKe1l/e4FSaI/+YE4IMG76GDt0In67WLPACIITC+sOi08x4wIvg==",
+ "dev": true,
+ "bin": {
+ "envinfo": "dist/cli.js"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/es-module-lexer": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.6.0.tgz",
+ "integrity": "sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ==",
+ "dev": true
+ },
+ "node_modules/escalade": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
+ "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/eslint-scope": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz",
+ "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==",
+ "dev": true,
+ "dependencies": {
+ "esrecurse": "^4.3.0",
+ "estraverse": "^4.1.1"
+ },
+ "engines": {
+ "node": ">=8.0.0"
+ }
+ },
+ "node_modules/esrecurse": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
+ "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
+ "dev": true,
+ "dependencies": {
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/esrecurse/node_modules/estraverse": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+ "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+ "dev": true,
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/estraverse": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz",
+ "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==",
+ "dev": true,
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/events": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
+ "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.8.x"
+ }
+ },
+ "node_modules/fast-deep-equal": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+ "dev": true
+ },
+ "node_modules/fast-uri": {
+ "version": "3.0.6",
+ "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.6.tgz",
+ "integrity": "sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/fastify"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/fastify"
+ }
+ ]
+ },
+ "node_modules/fastest-levenshtein": {
+ "version": "1.0.16",
+ "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz",
+ "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==",
+ "dev": true,
+ "engines": {
+ "node": ">= 4.9.1"
+ }
+ },
+ "node_modules/find-up": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
+ "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
+ "dev": true,
+ "dependencies": {
+ "locate-path": "^5.0.0",
+ "path-exists": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/flat": {
+ "version": "5.0.2",
+ "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz",
+ "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==",
+ "dev": true,
+ "bin": {
+ "flat": "cli.js"
+ }
+ },
+ "node_modules/function-bind": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
+ "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
+ "dev": true,
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/glob-to-regexp": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz",
+ "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==",
+ "dev": true
+ },
+ "node_modules/graceful-fs": {
+ "version": "4.2.11",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
+ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
+ "dev": true
+ },
+ "node_modules/has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/hasown": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
+ "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
+ "dev": true,
+ "dependencies": {
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/import-local": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz",
+ "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==",
+ "dev": true,
+ "dependencies": {
+ "pkg-dir": "^4.2.0",
+ "resolve-cwd": "^3.0.0"
+ },
+ "bin": {
+ "import-local-fixture": "fixtures/cli.js"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/interpret": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/interpret/-/interpret-3.1.1.tgz",
+ "integrity": "sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "node_modules/is-core-module": {
+ "version": "2.16.1",
+ "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz",
+ "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==",
+ "dev": true,
+ "dependencies": {
+ "hasown": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-plain-object": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
+ "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==",
+ "dev": true,
+ "dependencies": {
+ "isobject": "^3.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/isexe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
+ "dev": true
+ },
+ "node_modules/isobject": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
+ "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/jest-worker": {
+ "version": "27.5.1",
+ "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz",
+ "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==",
+ "dev": true,
+ "dependencies": {
+ "@types/node": "*",
+ "merge-stream": "^2.0.0",
+ "supports-color": "^8.0.0"
+ },
+ "engines": {
+ "node": ">= 10.13.0"
+ }
+ },
+ "node_modules/json-parse-even-better-errors": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
+ "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==",
+ "dev": true
+ },
+ "node_modules/json-schema-traverse": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
+ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
+ "dev": true
+ },
+ "node_modules/kind-of": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
+ "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/loader-runner": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz",
+ "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.11.5"
+ }
+ },
+ "node_modules/locate-path": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
+ "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
+ "dev": true,
+ "dependencies": {
+ "p-locate": "^4.1.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/merge-stream": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
+ "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==",
+ "dev": true
+ },
+ "node_modules/mime-db": {
+ "version": "1.52.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+ "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mime-types": {
+ "version": "2.1.35",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+ "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+ "dev": true,
+ "dependencies": {
+ "mime-db": "1.52.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/neo-async": {
+ "version": "2.6.2",
+ "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz",
+ "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==",
+ "dev": true
+ },
+ "node_modules/node-releases": {
+ "version": "2.0.19",
+ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz",
+ "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==",
+ "dev": true
+ },
+ "node_modules/p-limit": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
+ "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
+ "dev": true,
+ "dependencies": {
+ "p-try": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/p-locate": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
+ "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
+ "dev": true,
+ "dependencies": {
+ "p-limit": "^2.2.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/p-try": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
+ "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/path-exists": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+ "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/path-key": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
+ "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/path-parse": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
+ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
+ "dev": true
+ },
+ "node_modules/picocolors": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
+ "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
+ "dev": true
+ },
+ "node_modules/pkg-dir": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz",
+ "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==",
+ "dev": true,
+ "dependencies": {
+ "find-up": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/randombytes": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
+ "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
+ "dev": true,
+ "dependencies": {
+ "safe-buffer": "^5.1.0"
+ }
+ },
+ "node_modules/rechoir": {
+ "version": "0.8.0",
+ "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.8.0.tgz",
+ "integrity": "sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==",
+ "dev": true,
+ "dependencies": {
+ "resolve": "^1.20.0"
+ },
+ "engines": {
+ "node": ">= 10.13.0"
+ }
+ },
+ "node_modules/require-from-string": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
+ "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/resolve": {
+ "version": "1.22.10",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz",
+ "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==",
+ "dev": true,
+ "dependencies": {
+ "is-core-module": "^2.16.0",
+ "path-parse": "^1.0.7",
+ "supports-preserve-symlinks-flag": "^1.0.0"
+ },
+ "bin": {
+ "resolve": "bin/resolve"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/resolve-cwd": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz",
+ "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==",
+ "dev": true,
+ "dependencies": {
+ "resolve-from": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/resolve-from": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz",
+ "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/safe-buffer": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ]
+ },
+ "node_modules/schema-utils": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.0.tgz",
+ "integrity": "sha512-Gf9qqc58SpCA/xdziiHz35F4GNIWYWZrEshUc/G/r5BnLph6xpKuLeoJoQuj5WfBIx/eQLf+hmVPYHaxJu7V2g==",
+ "dev": true,
+ "dependencies": {
+ "@types/json-schema": "^7.0.9",
+ "ajv": "^8.9.0",
+ "ajv-formats": "^2.1.1",
+ "ajv-keywords": "^5.1.0"
+ },
+ "engines": {
+ "node": ">= 10.13.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/webpack"
+ }
+ },
+ "node_modules/serialize-javascript": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz",
+ "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==",
+ "dev": true,
+ "dependencies": {
+ "randombytes": "^2.1.0"
+ }
+ },
+ "node_modules/shallow-clone": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz",
+ "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==",
+ "dev": true,
+ "dependencies": {
+ "kind-of": "^6.0.2"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/shebang-command": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
+ "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+ "dev": true,
+ "dependencies": {
+ "shebang-regex": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/shebang-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
+ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/source-map-support": {
+ "version": "0.5.21",
+ "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
+ "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
+ "dev": true,
+ "dependencies": {
+ "buffer-from": "^1.0.0",
+ "source-map": "^0.6.0"
+ }
+ },
+ "node_modules/supports-color": {
+ "version": "8.1.1",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
+ "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
+ "dev": true,
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/supports-color?sponsor=1"
+ }
+ },
+ "node_modules/supports-preserve-symlinks-flag": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
+ "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/tapable": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz",
+ "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/terser": {
+ "version": "5.39.0",
+ "resolved": "https://registry.npmjs.org/terser/-/terser-5.39.0.tgz",
+ "integrity": "sha512-LBAhFyLho16harJoWMg/nZsQYgTrg5jXOn2nCYjRUcZZEdE3qa2zb8QEDRUGVZBW4rlazf2fxkg8tztybTaqWw==",
+ "dev": true,
+ "dependencies": {
+ "@jridgewell/source-map": "^0.3.3",
+ "acorn": "^8.8.2",
+ "commander": "^2.20.0",
+ "source-map-support": "~0.5.20"
+ },
+ "bin": {
+ "terser": "bin/terser"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/terser-webpack-plugin": {
+ "version": "5.3.11",
+ "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.11.tgz",
+ "integrity": "sha512-RVCsMfuD0+cTt3EwX8hSl2Ks56EbFHWmhluwcqoPKtBnfjiT6olaq7PRIRfhyU8nnC2MrnDrBLfrD/RGE+cVXQ==",
+ "dev": true,
+ "dependencies": {
+ "@jridgewell/trace-mapping": "^0.3.25",
+ "jest-worker": "^27.4.5",
+ "schema-utils": "^4.3.0",
+ "serialize-javascript": "^6.0.2",
+ "terser": "^5.31.1"
+ },
+ "engines": {
+ "node": ">= 10.13.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/webpack"
+ },
+ "peerDependencies": {
+ "webpack": "^5.1.0"
+ },
+ "peerDependenciesMeta": {
+ "@swc/core": {
+ "optional": true
+ },
+ "esbuild": {
+ "optional": true
+ },
+ "uglify-js": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/undici-types": {
+ "version": "6.20.0",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz",
+ "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==",
+ "dev": true
+ },
+ "node_modules/update-browserslist-db": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.2.tgz",
+ "integrity": "sha512-PPypAm5qvlD7XMZC3BujecnaOxwhrtoFR+Dqkk5Aa/6DssiH0ibKoketaj9w8LP7Bont1rYeoV5plxD7RTEPRg==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "dependencies": {
+ "escalade": "^3.2.0",
+ "picocolors": "^1.1.1"
+ },
+ "bin": {
+ "update-browserslist-db": "cli.js"
+ },
+ "peerDependencies": {
+ "browserslist": ">= 4.21.0"
+ }
+ },
+ "node_modules/watchpack": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.2.tgz",
+ "integrity": "sha512-TnbFSbcOCcDgjZ4piURLCbJ3nJhznVh9kw6F6iokjiFPl8ONxe9A6nMDVXDiNbrSfLILs6vB07F7wLBrwPYzJw==",
+ "dev": true,
+ "dependencies": {
+ "glob-to-regexp": "^0.4.1",
+ "graceful-fs": "^4.1.2"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "node_modules/webpack": {
+ "version": "5.98.0",
+ "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.98.0.tgz",
+ "integrity": "sha512-UFynvx+gM44Gv9qFgj0acCQK2VE1CtdfwFdimkapco3hlPCJ/zeq73n2yVKimVbtm+TnApIugGhLJnkU6gjYXA==",
+ "dev": true,
+ "dependencies": {
+ "@types/eslint-scope": "^3.7.7",
+ "@types/estree": "^1.0.6",
+ "@webassemblyjs/ast": "^1.14.1",
+ "@webassemblyjs/wasm-edit": "^1.14.1",
+ "@webassemblyjs/wasm-parser": "^1.14.1",
+ "acorn": "^8.14.0",
+ "browserslist": "^4.24.0",
+ "chrome-trace-event": "^1.0.2",
+ "enhanced-resolve": "^5.17.1",
+ "es-module-lexer": "^1.2.1",
+ "eslint-scope": "5.1.1",
+ "events": "^3.2.0",
+ "glob-to-regexp": "^0.4.1",
+ "graceful-fs": "^4.2.11",
+ "json-parse-even-better-errors": "^2.3.1",
+ "loader-runner": "^4.2.0",
+ "mime-types": "^2.1.27",
+ "neo-async": "^2.6.2",
+ "schema-utils": "^4.3.0",
+ "tapable": "^2.1.1",
+ "terser-webpack-plugin": "^5.3.11",
+ "watchpack": "^2.4.1",
+ "webpack-sources": "^3.2.3"
+ },
+ "bin": {
+ "webpack": "bin/webpack.js"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/webpack"
+ },
+ "peerDependenciesMeta": {
+ "webpack-cli": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/webpack-cli": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-6.0.1.tgz",
+ "integrity": "sha512-MfwFQ6SfwinsUVi0rNJm7rHZ31GyTcpVE5pgVA3hwFRb7COD4TzjUUwhGWKfO50+xdc2MQPuEBBJoqIMGt3JDw==",
+ "dev": true,
+ "dependencies": {
+ "@discoveryjs/json-ext": "^0.6.1",
+ "@webpack-cli/configtest": "^3.0.1",
+ "@webpack-cli/info": "^3.0.1",
+ "@webpack-cli/serve": "^3.0.1",
+ "colorette": "^2.0.14",
+ "commander": "^12.1.0",
+ "cross-spawn": "^7.0.3",
+ "envinfo": "^7.14.0",
+ "fastest-levenshtein": "^1.0.12",
+ "import-local": "^3.0.2",
+ "interpret": "^3.1.1",
+ "rechoir": "^0.8.0",
+ "webpack-merge": "^6.0.1"
+ },
+ "bin": {
+ "webpack-cli": "bin/cli.js"
+ },
+ "engines": {
+ "node": ">=18.12.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/webpack"
+ },
+ "peerDependencies": {
+ "webpack": "^5.82.0"
+ },
+ "peerDependenciesMeta": {
+ "webpack-bundle-analyzer": {
+ "optional": true
+ },
+ "webpack-dev-server": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/webpack-cli/node_modules/commander": {
+ "version": "12.1.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz",
+ "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==",
+ "dev": true,
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/webpack-merge": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-6.0.1.tgz",
+ "integrity": "sha512-hXXvrjtx2PLYx4qruKl+kyRSLc52V+cCvMxRjmKwoA+CBbbF5GfIBtR6kCvl0fYGqTUPKB+1ktVmTHqMOzgCBg==",
+ "dev": true,
+ "dependencies": {
+ "clone-deep": "^4.0.1",
+ "flat": "^5.0.2",
+ "wildcard": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/webpack-sources": {
+ "version": "3.2.3",
+ "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz",
+ "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==",
+ "dev": true,
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "node_modules/which": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+ "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+ "dev": true,
+ "dependencies": {
+ "isexe": "^2.0.0"
+ },
+ "bin": {
+ "node-which": "bin/node-which"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/wildcard": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.1.tgz",
+ "integrity": "sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==",
+ "dev": true
+ }
+ }
+}
diff --git a/webview_rpc_sample/package.json b/webview_rpc_sample/package.json
new file mode 100644
index 0000000..2edb2c9
--- /dev/null
+++ b/webview_rpc_sample/package.json
@@ -0,0 +1,20 @@
+{
+ "name": "webview_rpc_sample",
+ "version": "1.0.0",
+ "main": "index.js",
+ "scripts": {
+ "build": "webpack",
+ "test": "echo \"Error: no test specified\" && exit 1"
+ },
+ "keywords": [],
+ "author": "",
+ "license": "MIT",
+ "description": "",
+ "devDependencies": {
+ "webpack": "^5.98.0",
+ "webpack-cli": "^6.0.1"
+ },
+ "dependencies": {
+ "app-webview-rpc": "^0.1.0"
+ }
+}
\ No newline at end of file
diff --git a/WebViewRpcJS/HelloWorld_HelloServiceBase.js b/webview_rpc_sample/src/HelloWorld_HelloServiceBase.js
similarity index 100%
rename from WebViewRpcJS/HelloWorld_HelloServiceBase.js
rename to webview_rpc_sample/src/HelloWorld_HelloServiceBase.js
diff --git a/WebViewRpcJS/HelloWorld_HelloServiceClient.js b/webview_rpc_sample/src/HelloWorld_HelloServiceClient.js
similarity index 100%
rename from WebViewRpcJS/HelloWorld_HelloServiceClient.js
rename to webview_rpc_sample/src/HelloWorld_HelloServiceClient.js
diff --git a/WebViewRpcJS/MyHelloServiceImpl.js b/webview_rpc_sample/src/MyHelloServiceImpl.js
similarity index 100%
rename from WebViewRpcJS/MyHelloServiceImpl.js
rename to webview_rpc_sample/src/MyHelloServiceImpl.js
diff --git a/WebViewRpcJS/hello_world.js b/webview_rpc_sample/src/hello_world.js
similarity index 100%
rename from WebViewRpcJS/hello_world.js
rename to webview_rpc_sample/src/hello_world.js
diff --git a/WebViewRpcJS/index.html b/webview_rpc_sample/src/index.js
similarity index 58%
rename from WebViewRpcJS/index.html
rename to webview_rpc_sample/src/index.js
index f9825f1..3821e5c 100644
--- a/WebViewRpcJS/index.html
+++ b/webview_rpc_sample/src/index.js
@@ -1,37 +1,25 @@
-
-
-
-
- Unity WebViewRPC Test
-
-
-Unity WebViewRPC Test
-
-
-
-
-
-
+});
\ No newline at end of file
diff --git a/webview_rpc_sample/webpack.config.js b/webview_rpc_sample/webpack.config.js
new file mode 100644
index 0000000..a63cd17
--- /dev/null
+++ b/webview_rpc_sample/webpack.config.js
@@ -0,0 +1,8 @@
+module.exports = {
+ entry: './src/index.js',
+ output: {
+ filename: 'bundle.js',
+ path: __dirname + '/dist'
+ },
+ mode: 'development'
+};
\ No newline at end of file