mirror of
https://github.com/tauri-apps/plugins-workspace.git
synced 2026-06-06 13:53:54 +02:00
copy plugin sources
This commit is contained in:
@@ -0,0 +1,19 @@
|
||||
[package]
|
||||
name = "tauri-plugin-websocket"
|
||||
version = "0.1.0"
|
||||
edition.workspace = true
|
||||
authors.workspace = true
|
||||
license.workspace = true
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
serde.workspace = true
|
||||
serde_json.workspace = true
|
||||
tauri.workspace = true
|
||||
log.workspace = true
|
||||
thiserror.workspace = true
|
||||
rand = "0.8"
|
||||
futures-util = "0.3"
|
||||
tokio = { version = "1.17", features = ["net", "sync"] }
|
||||
tokio-tungstenite = { version = "0.17", features = ["native-tls"] }
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
var d=Object.defineProperty;var e=(c,a)=>{for(var b in a)d(c,b,{get:a[b],enumerable:!0});};
|
||||
|
||||
var f={};e(f,{convertFileSrc:()=>w,invoke:()=>c,transformCallback:()=>s});function u(){return window.crypto.getRandomValues(new Uint32Array(1))[0]}function s(e,r=!1){let n=u(),t=`_${n}`;return Object.defineProperty(window,t,{value:o=>(r&&Reflect.deleteProperty(window,t),e==null?void 0:e(o)),writable:!1,configurable:!0}),n}async function c(e,r={}){return new Promise((n,t)=>{let o=s(i=>{n(i),Reflect.deleteProperty(window,`_${a}`);},!0),a=s(i=>{t(i),Reflect.deleteProperty(window,`_${o}`);},!0);window.__TAURI_IPC__({cmd:e,callback:o,error:a,...r});})}function w(e,r="asset"){let n=encodeURIComponent(e);return navigator.userAgent.includes("Windows")?`https://${r}.localhost/${n}`:`${r}://localhost/${n}`}
|
||||
|
||||
class WebSocket {
|
||||
constructor(id, listeners) {
|
||||
this.id = id;
|
||||
this.listeners = listeners;
|
||||
}
|
||||
static async connect(url, options) {
|
||||
const listeners = [];
|
||||
const handler = (message) => {
|
||||
listeners.forEach((l) => l(message));
|
||||
};
|
||||
return await c("plugin:websocket|connect", {
|
||||
url,
|
||||
callbackFunction: s(handler),
|
||||
options,
|
||||
}).then((id) => new WebSocket(id, listeners));
|
||||
}
|
||||
addListener(cb) {
|
||||
this.listeners.push(cb);
|
||||
}
|
||||
async send(message) {
|
||||
let m;
|
||||
if (typeof message === "string") {
|
||||
m = { type: "Text", data: message };
|
||||
}
|
||||
else if (typeof message === "object" && "type" in message) {
|
||||
m = message;
|
||||
}
|
||||
else if (Array.isArray(message)) {
|
||||
m = { type: "Binary", data: message };
|
||||
}
|
||||
else {
|
||||
throw new Error("invalid `message` type, expected a `{ type: string, data: any }` object, a string or a numeric array");
|
||||
}
|
||||
return await c("plugin:websocket|send", {
|
||||
id: this.id,
|
||||
message: m,
|
||||
});
|
||||
}
|
||||
async disconnect() {
|
||||
return await this.send({
|
||||
type: "Close",
|
||||
data: {
|
||||
code: 1000,
|
||||
reason: "Disconnected by client",
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export { WebSocket as default };
|
||||
//# sourceMappingURL=index.min.js.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"index.min.js","sources":["../../../../node_modules/.pnpm/@tauri-apps+api@1.2.0/node_modules/@tauri-apps/api/chunk-FEIY7W7S.js","../../../../node_modules/.pnpm/@tauri-apps+api@1.2.0/node_modules/@tauri-apps/api/chunk-RCPA6UVN.js","../index.ts"],"sourcesContent":["var d=Object.defineProperty;var e=(c,a)=>{for(var b in a)d(c,b,{get:a[b],enumerable:!0})};export{e as a};\n","import{a as d}from\"./chunk-FEIY7W7S.js\";var f={};d(f,{convertFileSrc:()=>w,invoke:()=>c,transformCallback:()=>s});function u(){return window.crypto.getRandomValues(new Uint32Array(1))[0]}function s(e,r=!1){let n=u(),t=`_${n}`;return Object.defineProperty(window,t,{value:o=>(r&&Reflect.deleteProperty(window,t),e==null?void 0:e(o)),writable:!1,configurable:!0}),n}async function c(e,r={}){return new Promise((n,t)=>{let o=s(i=>{n(i),Reflect.deleteProperty(window,`_${a}`)},!0),a=s(i=>{t(i),Reflect.deleteProperty(window,`_${o}`)},!0);window.__TAURI_IPC__({cmd:e,callback:o,error:a,...r})})}function w(e,r=\"asset\"){let n=encodeURIComponent(e);return navigator.userAgent.includes(\"Windows\")?`https://${r}.localhost/${n}`:`${r}://localhost/${n}`}export{s as a,c as b,w as c,f as d};\n",null],"names":["d","invoke","transformCallback"],"mappings":"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"}
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
import { invoke, transformCallback } from '@tauri-apps/api/tauri';
|
||||
|
||||
class WebSocket {
|
||||
constructor(id, listeners) {
|
||||
this.id = id;
|
||||
this.listeners = listeners;
|
||||
}
|
||||
static async connect(url, options) {
|
||||
const listeners = [];
|
||||
const handler = (message) => {
|
||||
listeners.forEach((l) => l(message));
|
||||
};
|
||||
return await invoke("plugin:websocket|connect", {
|
||||
url,
|
||||
callbackFunction: transformCallback(handler),
|
||||
options,
|
||||
}).then((id) => new WebSocket(id, listeners));
|
||||
}
|
||||
addListener(cb) {
|
||||
this.listeners.push(cb);
|
||||
}
|
||||
async send(message) {
|
||||
let m;
|
||||
if (typeof message === "string") {
|
||||
m = { type: "Text", data: message };
|
||||
}
|
||||
else if (typeof message === "object" && "type" in message) {
|
||||
m = message;
|
||||
}
|
||||
else if (Array.isArray(message)) {
|
||||
m = { type: "Binary", data: message };
|
||||
}
|
||||
else {
|
||||
throw new Error("invalid `message` type, expected a `{ type: string, data: any }` object, a string or a numeric array");
|
||||
}
|
||||
return await invoke("plugin:websocket|send", {
|
||||
id: this.id,
|
||||
message: m,
|
||||
});
|
||||
}
|
||||
async disconnect() {
|
||||
return await this.send({
|
||||
type: "Close",
|
||||
data: {
|
||||
code: 1000,
|
||||
reason: "Disconnected by client",
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export { WebSocket as default };
|
||||
//# sourceMappingURL=index.mjs.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"index.mjs","sources":["../index.ts"],"sourcesContent":[null],"names":[],"mappings":";;AAmBc,MAAO,SAAS,CAAA;IAI5B,WAAY,CAAA,EAAU,EAAE,SAAwC,EAAA;AAC9D,QAAA,IAAI,CAAC,EAAE,GAAG,EAAE,CAAC;AACb,QAAA,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;KAC5B;AAED,IAAA,aAAa,OAAO,CAAC,GAAW,EAAE,OAAiB,EAAA;QACjD,MAAM,SAAS,GAAkC,EAAE,CAAC;AACpD,QAAA,MAAM,OAAO,GAAG,CAAC,OAAgB,KAAU;AACzC,YAAA,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;AACvC,SAAC,CAAC;AAEF,QAAA,OAAO,MAAM,MAAM,CAAS,0BAA0B,EAAE;YACtD,GAAG;AACH,YAAA,gBAAgB,EAAE,iBAAiB,CAAC,OAAO,CAAC;YAC5C,OAAO;AACR,SAAA,CAAC,CAAC,IAAI,CAAC,CAAC,EAAE,KAAK,IAAI,SAAS,CAAC,EAAE,EAAE,SAAS,CAAC,CAAC,CAAC;KAC/C;AAED,IAAA,WAAW,CAAC,EAA0B,EAAA;AACpC,QAAA,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;KACzB;IAED,MAAM,IAAI,CAAC,OAAoC,EAAA;AAC7C,QAAA,IAAI,CAAU,CAAC;AACf,QAAA,IAAI,OAAO,OAAO,KAAK,QAAQ,EAAE;YAC/B,CAAC,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;AACrC,SAAA;aAAM,IAAI,OAAO,OAAO,KAAK,QAAQ,IAAI,MAAM,IAAI,OAAO,EAAE;YAC3D,CAAC,GAAG,OAAO,CAAC;AACb,SAAA;AAAM,aAAA,IAAI,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE;YACjC,CAAC,GAAG,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;AACvC,SAAA;AAAM,aAAA;AACL,YAAA,MAAM,IAAI,KAAK,CACb,sGAAsG,CACvG,CAAC;AACH,SAAA;AACD,QAAA,OAAO,MAAM,MAAM,CAAC,uBAAuB,EAAE;YAC3C,EAAE,EAAE,IAAI,CAAC,EAAE;AACX,YAAA,OAAO,EAAE,CAAC;AACX,SAAA,CAAC,CAAC;KACJ;AAED,IAAA,MAAM,UAAU,GAAA;AACd,QAAA,OAAO,MAAM,IAAI,CAAC,IAAI,CAAC;AACrB,YAAA,IAAI,EAAE,OAAO;AACb,YAAA,IAAI,EAAE;AACJ,gBAAA,IAAI,EAAE,IAAI;AACV,gBAAA,MAAM,EAAE,wBAAwB;AACjC,aAAA;AACF,SAAA,CAAC,CAAC;KACJ;AACF;;;;"}
|
||||
@@ -0,0 +1,74 @@
|
||||
import { invoke, transformCallback } from "@tauri-apps/api/tauri";
|
||||
|
||||
export interface MessageKind<T, D> {
|
||||
type: T;
|
||||
data: D;
|
||||
}
|
||||
|
||||
export interface CloseFrame {
|
||||
code: number;
|
||||
reason: string;
|
||||
}
|
||||
|
||||
export type Message =
|
||||
| MessageKind<"Text", string>
|
||||
| MessageKind<"Binary", number[]>
|
||||
| MessageKind<"Ping", number[]>
|
||||
| MessageKind<"Pong", number[]>
|
||||
| MessageKind<"Close", CloseFrame | null>;
|
||||
|
||||
export default class WebSocket {
|
||||
id: number;
|
||||
private readonly listeners: Array<(arg: Message) => void>;
|
||||
|
||||
constructor(id: number, listeners: Array<(arg: Message) => void>) {
|
||||
this.id = id;
|
||||
this.listeners = listeners;
|
||||
}
|
||||
|
||||
static async connect(url: string, options?: unknown): Promise<WebSocket> {
|
||||
const listeners: Array<(arg: Message) => void> = [];
|
||||
const handler = (message: Message): void => {
|
||||
listeners.forEach((l) => l(message));
|
||||
};
|
||||
|
||||
return await invoke<number>("plugin:websocket|connect", {
|
||||
url,
|
||||
callbackFunction: transformCallback(handler),
|
||||
options,
|
||||
}).then((id) => new WebSocket(id, listeners));
|
||||
}
|
||||
|
||||
addListener(cb: (arg: Message) => void): void {
|
||||
this.listeners.push(cb);
|
||||
}
|
||||
|
||||
async send(message: Message | string | number[]): Promise<void> {
|
||||
let m: Message;
|
||||
if (typeof message === "string") {
|
||||
m = { type: "Text", data: message };
|
||||
} else if (typeof message === "object" && "type" in message) {
|
||||
m = message;
|
||||
} else if (Array.isArray(message)) {
|
||||
m = { type: "Binary", data: message };
|
||||
} else {
|
||||
throw new Error(
|
||||
"invalid `message` type, expected a `{ type: string, data: any }` object, a string or a numeric array"
|
||||
);
|
||||
}
|
||||
return await invoke("plugin:websocket|send", {
|
||||
id: this.id,
|
||||
message: m,
|
||||
});
|
||||
}
|
||||
|
||||
async disconnect(): Promise<void> {
|
||||
return await this.send({
|
||||
type: "Close",
|
||||
data: {
|
||||
code: 1000,
|
||||
reason: "Disconnected by client",
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"name": "tauri-plugin-websocket-api",
|
||||
"version": "0.0.0",
|
||||
"license": "MIT or APACHE-2.0",
|
||||
"type": "module",
|
||||
"browser": "dist/index.min.js",
|
||||
"module": "dist/index.mjs",
|
||||
"types": "dist/index.d.ts",
|
||||
"exports": {
|
||||
"import": "./dist/index.mjs",
|
||||
"types": "./dist/index.d.ts",
|
||||
"browser": "./dist/index.min.js"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "rollup -c"
|
||||
},
|
||||
"files": [
|
||||
"dist",
|
||||
"!dist/**/*.map",
|
||||
"README.md",
|
||||
"LICENSE"
|
||||
],
|
||||
"devDependencies": {
|
||||
"tslib": "^2.4.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@tauri-apps/api": "^1.2.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
import { readFileSync } from "fs";
|
||||
|
||||
import { createConfig } from "../../../shared/rollup.config.mjs";
|
||||
|
||||
export default createConfig({
|
||||
pkg: JSON.parse(
|
||||
readFileSync(new URL("./package.json", import.meta.url), "utf8")
|
||||
),
|
||||
external: [/^@tauri-apps\/api/],
|
||||
});
|
||||
@@ -0,0 +1 @@
|
||||
../../../shared/tsconfig.json
|
||||
@@ -0,0 +1,189 @@
|
||||
use futures_util::{stream::SplitSink, SinkExt, StreamExt};
|
||||
use serde::{ser::Serializer, Deserialize, Serialize};
|
||||
use serde_json::Value as JsonValue;
|
||||
use tauri::{
|
||||
api::ipc::{format_callback, CallbackFn},
|
||||
plugin::Plugin,
|
||||
AppHandle, Invoke, Manager, Runtime, State, Window,
|
||||
};
|
||||
use tokio::{net::TcpStream, sync::Mutex};
|
||||
use tokio_tungstenite::{
|
||||
connect_async_with_config,
|
||||
tungstenite::{
|
||||
protocol::{CloseFrame as ProtocolCloseFrame, WebSocketConfig},
|
||||
Message,
|
||||
},
|
||||
MaybeTlsStream, WebSocketStream,
|
||||
};
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
type Id = u32;
|
||||
type WebSocket = WebSocketStream<MaybeTlsStream<TcpStream>>;
|
||||
type WebSocketWriter = SplitSink<WebSocket, Message>;
|
||||
type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
enum Error {
|
||||
#[error(transparent)]
|
||||
Websocket(#[from] tokio_tungstenite::tungstenite::Error),
|
||||
#[error("connection not found for the given id: {0}")]
|
||||
ConnectionNotFound(Id),
|
||||
}
|
||||
|
||||
impl Serialize for Error {
|
||||
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
serializer.serialize_str(self.to_string().as_str())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct ConnectionManager(Mutex<HashMap<Id, WebSocketWriter>>);
|
||||
|
||||
#[derive(Default, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ConnectionConfig {
|
||||
pub max_send_queue: Option<usize>,
|
||||
pub max_message_size: Option<usize>,
|
||||
pub max_frame_size: Option<usize>,
|
||||
pub accept_unmasked_frames: bool,
|
||||
}
|
||||
|
||||
impl From<ConnectionConfig> for WebSocketConfig {
|
||||
fn from(config: ConnectionConfig) -> Self {
|
||||
Self {
|
||||
max_send_queue: config.max_send_queue,
|
||||
max_message_size: config.max_message_size,
|
||||
max_frame_size: config.max_frame_size,
|
||||
accept_unmasked_frames: config.accept_unmasked_frames,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
struct CloseFrame {
|
||||
pub code: u16,
|
||||
pub reason: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
#[serde(tag = "type", content = "data")]
|
||||
enum WebSocketMessage {
|
||||
Text(String),
|
||||
Binary(Vec<u8>),
|
||||
Ping(Vec<u8>),
|
||||
Pong(Vec<u8>),
|
||||
Close(Option<CloseFrame>),
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
fn connect<R: Runtime>(
|
||||
window: Window<R>,
|
||||
url: String,
|
||||
callback_function: CallbackFn,
|
||||
config: Option<ConnectionConfig>,
|
||||
) -> Result<Id> {
|
||||
let id = rand::random();
|
||||
let (ws_stream, _) =
|
||||
tauri::async_runtime::block_on(connect_async_with_config(url, config.map(Into::into)))?;
|
||||
|
||||
tauri::async_runtime::spawn(async move {
|
||||
let (write, read) = ws_stream.split();
|
||||
let manager = window.state::<ConnectionManager>();
|
||||
manager.0.lock().await.insert(id, write);
|
||||
read.for_each(move |message| {
|
||||
let window_ = window.clone();
|
||||
async move {
|
||||
if let Ok(Message::Close(_)) = message {
|
||||
let manager = window_.state::<ConnectionManager>();
|
||||
manager.0.lock().await.remove(&id);
|
||||
}
|
||||
|
||||
let response = match message {
|
||||
Ok(Message::Text(t)) => {
|
||||
serde_json::to_value(WebSocketMessage::Text(t)).unwrap()
|
||||
}
|
||||
Ok(Message::Binary(t)) => {
|
||||
serde_json::to_value(WebSocketMessage::Binary(t)).unwrap()
|
||||
}
|
||||
Ok(Message::Ping(t)) => {
|
||||
serde_json::to_value(WebSocketMessage::Ping(t)).unwrap()
|
||||
}
|
||||
Ok(Message::Pong(t)) => {
|
||||
serde_json::to_value(WebSocketMessage::Pong(t)).unwrap()
|
||||
}
|
||||
Ok(Message::Close(t)) => {
|
||||
serde_json::to_value(WebSocketMessage::Close(t.map(|v| CloseFrame {
|
||||
code: v.code.into(),
|
||||
reason: v.reason.into_owned(),
|
||||
})))
|
||||
.unwrap()
|
||||
}
|
||||
Ok(Message::Frame(_)) => serde_json::Value::Null, // This value can't be recieved.
|
||||
Err(e) => serde_json::to_value(Error::from(e)).unwrap(),
|
||||
};
|
||||
let js = format_callback(callback_function, &response)
|
||||
.expect("unable to serialize websocket message");
|
||||
let _ = window_.eval(js.as_str());
|
||||
}
|
||||
})
|
||||
.await;
|
||||
});
|
||||
|
||||
Ok(id)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
async fn send(
|
||||
manager: State<'_, ConnectionManager>,
|
||||
id: Id,
|
||||
message: WebSocketMessage,
|
||||
) -> Result<()> {
|
||||
if let Some(write) = manager.0.lock().await.get_mut(&id) {
|
||||
write
|
||||
.send(match message {
|
||||
WebSocketMessage::Text(t) => Message::Text(t),
|
||||
WebSocketMessage::Binary(t) => Message::Binary(t),
|
||||
WebSocketMessage::Ping(t) => Message::Ping(t),
|
||||
WebSocketMessage::Pong(t) => Message::Pong(t),
|
||||
WebSocketMessage::Close(t) => Message::Close(t.map(|v| ProtocolCloseFrame {
|
||||
code: v.code.into(),
|
||||
reason: std::borrow::Cow::Owned(v.reason),
|
||||
})),
|
||||
})
|
||||
.await?;
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Error::ConnectionNotFound(id))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TauriWebsocket<R: Runtime> {
|
||||
invoke_handler: Box<dyn Fn(Invoke<R>) + Send + Sync>,
|
||||
}
|
||||
|
||||
impl<R: Runtime> Default for TauriWebsocket<R> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
invoke_handler: Box::new(tauri::generate_handler![connect, send]),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: Runtime> Plugin<R> for TauriWebsocket<R> {
|
||||
fn name(&self) -> &'static str {
|
||||
"websocket"
|
||||
}
|
||||
|
||||
fn initialize(&mut self, app: &AppHandle<R>, _config: JsonValue) -> tauri::plugin::Result<()> {
|
||||
app.manage(ConnectionManager::default());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn extend_api(&mut self, invoke: Invoke<R>) {
|
||||
(self.invoke_handler)(invoke)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user