mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-11-13 12:13:42 +01:00
Move the RPC wire schema into a Tauri-free crate (#553)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
93001e3da7
commit
4838353585
29 files changed
+1156
-995
No files matched your search
Generated
+17
@@ -11035,6 +11035,7 @@ dependencies = [
|
||||
"yaak-models",
|
||||
"yaak-plugins",
|
||||
"yaak-rpc",
|
||||
"yaak-rpc-schema",
|
||||
"yaak-sse",
|
||||
"yaak-sync",
|
||||
"yaak-system-appearance",
|
||||
@@ -11372,6 +11373,22 @@ dependencies = [
|
||||
"ts-rs",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "yaak-rpc-schema"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"ts-rs",
|
||||
"yaak-git",
|
||||
"yaak-grpc",
|
||||
"yaak-models",
|
||||
"yaak-plugins",
|
||||
"yaak-sse",
|
||||
"yaak-sync",
|
||||
"yaak-templates",
|
||||
"yaak-ws",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "yaak-sse"
|
||||
version = "0.1.0"
|
||||
|
||||
@@ -5,6 +5,7 @@ members = [
|
||||
# Common/foundation crates
|
||||
"crates/common/yaak-database",
|
||||
"crates/common/yaak-rpc",
|
||||
"crates/common/yaak-rpc-schema",
|
||||
# Shared crates (no Tauri dependency)
|
||||
"crates/yaak-core",
|
||||
"crates/yaak-common",
|
||||
@@ -63,6 +64,7 @@ ts-rs = "11.1.0"
|
||||
# Internal crates - common/foundation
|
||||
yaak-database = { path = "crates/common/yaak-database" }
|
||||
yaak-rpc = { path = "crates/common/yaak-rpc" }
|
||||
yaak-rpc-schema = { path = "crates/common/yaak-rpc-schema" }
|
||||
|
||||
# Internal crates - shared
|
||||
yaak-core = { path = "crates/yaak-core" }
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { RpcPayload } from "@yaakapp-internal/platform";
|
||||
import { platform } from "@yaakapp-internal/platform";
|
||||
import type { RpcSchema } from "@yaakapp-internal/tauri-client";
|
||||
import type { RpcSchema } from "@yaakapp-internal/rpc-schema";
|
||||
|
||||
/**
|
||||
* Every backend command the app can call: the generated wire schema, one field
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { HttpRequest } from "@yaakapp-internal/models";
|
||||
import type { EphemeralHttpResponse } from "@yaakapp-internal/tauri-client";
|
||||
import type { EphemeralHttpResponse } from "@yaakapp-internal/rpc-schema";
|
||||
import { getActiveCookieJar } from "../hooks/useActiveCookieJar";
|
||||
import { rpc } from "./rpc";
|
||||
|
||||
|
||||
@@ -73,6 +73,7 @@ url = "2"
|
||||
tokio-util = { version = "0.7", features = ["codec"] }
|
||||
ts-rs = { workspace = true }
|
||||
yaak-rpc = { workspace = true }
|
||||
yaak-rpc-schema = { workspace = true }
|
||||
uuid = "1.12.1"
|
||||
yaak-api = { workspace = true }
|
||||
yaak-common = { workspace = true }
|
||||
|
||||
-4
@@ -1,7 +1,5 @@
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
|
||||
export type GitWatchResult = { unlistenEvent: string, };
|
||||
|
||||
export type PluginUpdateInfo = { name: string, currentVersion: string, latestVersion: string, };
|
||||
|
||||
export type PluginUpdateNotification = { updateCount: number, plugins: Array<PluginUpdateInfo>, };
|
||||
@@ -12,8 +10,6 @@ export type UpdateResponse = { "type": "ack" } | { "type": "action", action: Upd
|
||||
|
||||
export type UpdateResponseAction = "install" | "skip";
|
||||
|
||||
export type WatchResult = { unlistenEvent: string, };
|
||||
|
||||
export type YaakNotification = { timestamp: string, timeout: number | null, id: string, title: string | null, message: string, color: string | null, action: YaakNotificationAction | null, };
|
||||
|
||||
export type YaakNotificationAction = { label: string, url: string, };
|
||||
@@ -1,4 +1,4 @@
|
||||
// ts-rs owns bindings/index.ts and rewrites it on export, so this hand-written
|
||||
// entry point is where the generated files come together.
|
||||
export * from "./bindings/gen_rpc";
|
||||
// ts-rs owns bindings/index.ts and rewrites it on export. What remains here
|
||||
// after the RPC schema moved to @yaakapp-internal/rpc-schema is the
|
||||
// desktop-only surface: updater and notification types.
|
||||
export * from "./bindings/index";
|
||||
@@ -2,7 +2,6 @@ use crate::error::{Error, Result};
|
||||
use chrono::Utc;
|
||||
use log::{debug, error, warn};
|
||||
use notify::Watcher;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::path::Path;
|
||||
use std::sync::mpsc;
|
||||
use std::time::Duration;
|
||||
@@ -10,18 +9,11 @@ use tauri::{AppHandle, Listener, Runtime};
|
||||
use tokio::select;
|
||||
use tokio::sync::watch;
|
||||
use tokio::time::sleep;
|
||||
use ts_rs::TS;
|
||||
use yaak_git::{GitWorktreeStatus, git_path_is_ignored, git_repository_paths, git_worktree_status};
|
||||
use yaak_rpc_schema::GitWatchResult;
|
||||
|
||||
const GIT_STATUS_COALESCE_WINDOW: Duration = Duration::from_millis(250);
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "index.ts")]
|
||||
pub(crate) struct GitWatchResult {
|
||||
unlisten_event: String,
|
||||
}
|
||||
|
||||
pub(crate) async fn watch_git_worktree_status<R, F>(
|
||||
app_handle: AppHandle<R>,
|
||||
dir: &Path,
|
||||
|
||||
@@ -8,7 +8,6 @@ use crate::import::{import_data, import_url};
|
||||
use crate::models_ext::{BlobManagerExt, QueryManagerExt};
|
||||
use crate::notifications::YaakNotifier;
|
||||
use crate::render::{render_grpc_request, render_json_value, render_template};
|
||||
use crate::rpc_ext::EphemeralHttpResponse;
|
||||
use crate::updates::{UpdateMode, UpdateTrigger, YaakUpdater};
|
||||
use crate::uri_scheme::handle_deep_link;
|
||||
use error::Result as YaakResult;
|
||||
@@ -57,6 +56,7 @@ use yaak_plugins::events::{
|
||||
use yaak_plugins::manager::PluginManager;
|
||||
use yaak_plugins::plugin_meta::{PluginMetadata, get_plugin_meta};
|
||||
use yaak_plugins::template_callback::PluginTemplateCallback;
|
||||
use yaak_rpc_schema::{AppMetaData, EphemeralHttpResponse};
|
||||
use yaak_sse::sse::ServerSentEvent;
|
||||
use yaak_tauri_utils::window::WorkspaceWindowTrait;
|
||||
use yaak_templates::format_json::format_json;
|
||||
@@ -184,22 +184,6 @@ impl<R: Runtime> PluginContextExt<R> for WebviewWindow<R> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(serde::Serialize, ts_rs::TS)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "gen_rpc.ts")]
|
||||
pub struct AppMetaData {
|
||||
is_dev: bool,
|
||||
version: String,
|
||||
cli_version: Option<String>,
|
||||
name: String,
|
||||
app_data_dir: String,
|
||||
app_log_dir: String,
|
||||
vendored_plugin_dir: String,
|
||||
default_project_dir: String,
|
||||
feature_updater: bool,
|
||||
feature_license: bool,
|
||||
}
|
||||
|
||||
async fn cmd_metadata<R: Runtime>(app_handle: AppHandle<R>) -> YaakResult<AppMetaData> {
|
||||
let app_data_dir = app_handle.path().app_data_dir()?;
|
||||
let app_log_dir = app_handle.path().app_log_dir()?;
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -6,11 +6,10 @@ use crate::error::Result;
|
||||
use crate::models_ext::{BlobManagerExt, QueryManagerExt};
|
||||
use chrono::Utc;
|
||||
use log::warn;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::path::Path;
|
||||
use tauri::{AppHandle, Listener, Runtime};
|
||||
use tokio::sync::watch;
|
||||
use ts_rs::TS;
|
||||
use yaak_rpc_schema::WatchResult;
|
||||
use yaak_sync::error::Error::InvalidSyncDirectory;
|
||||
use yaak_sync::sync::{
|
||||
FsCandidate, SyncOp, apply_sync_ops, apply_sync_state_ops, compute_sync_ops, get_db_candidates,
|
||||
@@ -57,13 +56,6 @@ pub(crate) async fn cmd_sync_apply<R: Runtime>(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "index.ts")]
|
||||
pub(crate) struct WatchResult {
|
||||
unlisten_event: String,
|
||||
}
|
||||
|
||||
pub(crate) async fn sync_watch<R, F>(
|
||||
app_handle: AppHandle<R>,
|
||||
sync_dir: &Path,
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
[package]
|
||||
name = "yaak-rpc-schema"
|
||||
version = "0.0.0"
|
||||
edition = "2024"
|
||||
authors = ["Gregory Schier"]
|
||||
publish = false
|
||||
|
||||
[dependencies]
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
ts-rs = { workspace = true }
|
||||
yaak-git = { workspace = true }
|
||||
yaak-grpc = { workspace = true }
|
||||
yaak-models = { workspace = true }
|
||||
yaak-plugins = { workspace = true }
|
||||
yaak-sse = { workspace = true }
|
||||
yaak-sync = { workspace = true }
|
||||
yaak-templates = { workspace = true }
|
||||
yaak-ws = { workspace = true }
|
||||
@@ -0,0 +1,44 @@
|
||||
# yaak-rpc-schema
|
||||
|
||||
The wire schema for the app's RPC surface: every command name, its request
|
||||
payload, and its response type, declared once.
|
||||
|
||||
Every host that serves the Yaak UI — the desktop app today, the browser bridge
|
||||
and anything after it — imports these types and implements the commands against
|
||||
them. That is what keeps a request's shape from drifting between hosts, and it
|
||||
is why the TypeScript bindings (`bindings/gen_rpc.ts`, exposed to the frontend
|
||||
as `@yaakapp-internal/rpc-schema`) are generated from one place.
|
||||
|
||||
Nothing here depends on Tauri or on any host. Request structs are plain data,
|
||||
and so are the few response types declared here rather than in an engine crate.
|
||||
Command *bodies* live with the host that runs them.
|
||||
|
||||
## Adding a command
|
||||
|
||||
1. Add its request struct and an entry in `with_commands!` in `src/lib.rs`.
|
||||
2. Write the adapter in each host — the desktop's live in
|
||||
`crates-tauri/yaak-app-client/src/rpc_ext.rs`. A host that does not support
|
||||
the command still has to say so; a missing adapter fails to compile.
|
||||
3. Regenerate the bindings: `cargo test -p yaak-rpc-schema` writes
|
||||
`bindings/gen_rpc.ts`, which is committed.
|
||||
|
||||
## How hosts consume the list
|
||||
|
||||
`with_commands!` takes the name of a `macro_rules!` macro and calls it with the
|
||||
full `name(Req) -> Res` list. Each host writes a small macro that receives that
|
||||
list and builds its router:
|
||||
|
||||
```rust
|
||||
macro_rules! register_commands {
|
||||
( $( $name:ident ( $req:ty ) -> $res:ty ),* $(,)? ) => {
|
||||
pub fn build_router() -> RpcRouter<MyCtx> {
|
||||
let mut router = RpcRouter::new();
|
||||
$( router.register(stringify!($name), rpc_handler_async!($name)); )*
|
||||
router
|
||||
}
|
||||
};
|
||||
}
|
||||
yaak_rpc_schema::with_commands!(register_commands);
|
||||
```
|
||||
|
||||
The schema decides *what* commands exist; the host decides *how* each one runs.
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
crates-tauri/yaak-app-client/bindings/gen_rpc.ts → crates/common/yaak-rpc-schema/bindings/gen_rpc.ts
Generated
+11
-5
@@ -7,10 +7,12 @@ import type { AnyModel, GraphQlIntrospection, GrpcEvent, HttpRequest, HttpReques
|
||||
import type { PluginMetadata } from "./gen_search";
|
||||
import type { SyncOp } from "./gen_sync";
|
||||
import type { BatchUpsertResult } from "./gen_util";
|
||||
import type { GitWatchResult, WatchResult } from "./index";
|
||||
import type { Tokens } from "./parser";
|
||||
import type { ServerSentEvent } from "./sse";
|
||||
|
||||
/**
|
||||
* What the frontend learns about the host it is talking to.
|
||||
*/
|
||||
export type AppMetaData = { isDev: boolean, version: string, cliVersion: string | null, name: string, appDataDir: string, appLogDir: string, vendoredPluginDir: string, defaultProjectDir: string, featureUpdater: boolean, featureLicense: boolean, };
|
||||
|
||||
export type CmdCallFolderActionReq = { req: CallFolderActionRequest, };
|
||||
@@ -222,6 +224,12 @@ export type CmdWsSendReq = { connectionId: string, environmentId: string | null,
|
||||
*/
|
||||
export type EphemeralHttpResponse = { response: HttpResponse, body: Array<number>, };
|
||||
|
||||
/**
|
||||
* Returned by the two watch commands: the name of the event to emit to stop
|
||||
* the watcher.
|
||||
*/
|
||||
export type GitWatchResult = { unlistenEvent: string, };
|
||||
|
||||
export type ModelsDeleteReq = { model: AnyModel, };
|
||||
|
||||
export type ModelsDuplicateReq = { modelType: string, modelId: string, };
|
||||
@@ -240,8 +248,6 @@ export type ModelsWebsocketEventsReq = { connectionId: string, };
|
||||
|
||||
export type ModelsWorkspaceModelsReq = { workspaceId: string | null, };
|
||||
|
||||
/**
|
||||
* The wire schema, exported to TypeScript. Field name = command name,
|
||||
* tuple = (request payload, response payload).
|
||||
*/
|
||||
export type RpcSchema = { cmd_metadata: [CmdMetadataReq, AppMetaData], cmd_template_tokens_to_string: [CmdTemplateTokensToStringReq, string], cmd_render_template: [CmdRenderTemplateReq, string], cmd_send_feedback: [CmdSendFeedbackReq, null], cmd_dismiss_notification: [CmdDismissNotificationReq, null], cmd_grpc_reflect: [CmdGrpcReflectReq, Array<ServiceDefinition>], cmd_grpc_go: [CmdGrpcGoReq, string], cmd_restart: [CmdRestartReq, null], cmd_send_ephemeral_request: [CmdSendEphemeralRequestReq, EphemeralHttpResponse], cmd_format_json: [CmdFormatJsonReq, string], cmd_format_graphql: [CmdFormatGraphqlReq, string], cmd_http_response_body: [CmdHttpResponseBodyReq, FilterResponse], cmd_http_response_body_path: [CmdHttpResponseBodyPathReq, string | null], cmd_http_request_body: [CmdHttpRequestBodyReq, Array<number> | null], cmd_get_sse_events: [CmdGetSseEventsReq, Array<ServerSentEvent>], cmd_get_http_response_events: [CmdGetHttpResponseEventsReq, Array<HttpResponseEvent>], cmd_import_data: [CmdImportDataReq, BatchUpsertResult], cmd_import_url: [CmdImportUrlReq, BatchUpsertResult], cmd_http_request_actions: [CmdHttpRequestActionsReq, Array<GetHttpRequestActionsResponse>], cmd_websocket_request_actions: [CmdWebsocketRequestActionsReq, Array<GetWebsocketRequestActionsResponse>], cmd_call_websocket_request_action: [CmdCallWebsocketRequestActionReq, null], cmd_workspace_actions: [CmdWorkspaceActionsReq, Array<GetWorkspaceActionsResponse>], cmd_call_workspace_action: [CmdCallWorkspaceActionReq, null], cmd_folder_actions: [CmdFolderActionsReq, Array<GetFolderActionsResponse>], cmd_call_folder_action: [CmdCallFolderActionReq, null], cmd_grpc_request_actions: [CmdGrpcRequestActionsReq, Array<GetGrpcRequestActionsResponse>], cmd_template_function_summaries: [CmdTemplateFunctionSummariesReq, Array<GetTemplateFunctionSummaryResponse>], cmd_template_function_config: [CmdTemplateFunctionConfigReq, GetTemplateFunctionConfigResponse], cmd_get_http_authentication_summaries: [CmdGetHttpAuthenticationSummariesReq, Array<GetHttpAuthenticationSummaryResponse>], cmd_get_http_authentication_config: [CmdGetHttpAuthenticationConfigReq, GetHttpAuthenticationConfigResponse], cmd_call_http_request_action: [CmdCallHttpRequestActionReq, null], cmd_call_grpc_request_action: [CmdCallGrpcRequestActionReq, null], cmd_call_http_authentication_action: [CmdCallHttpAuthenticationActionReq, null], cmd_curl_to_request: [CmdCurlToRequestReq, HttpRequest], cmd_export_data: [CmdExportDataReq, null], cmd_save_base64_to_binary: [CmdSaveBase64ToBinaryReq, null], cmd_save_response: [CmdSaveResponseReq, null], cmd_send_http_request: [CmdSendHttpRequestReq, HttpResponse], cmd_reload_plugins: [CmdReloadPluginsReq, Array<[string, string]>], cmd_plugin_info: [CmdPluginInfoReq, PluginMetadata], cmd_delete_all_grpc_connections: [CmdDeleteAllGrpcConnectionsReq, null], cmd_delete_send_history: [CmdDeleteSendHistoryReq, null], cmd_delete_all_http_responses: [CmdDeleteAllHttpResponsesReq, null], cmd_get_workspace_meta: [CmdGetWorkspaceMetaReq, WorkspaceMeta], cmd_new_child_window: [CmdNewChildWindowReq, null], cmd_new_main_window: [CmdNewMainWindowReq, null], cmd_check_for_updates: [CmdCheckForUpdatesReq, boolean], cmd_decrypt_template: [CmdDecryptTemplateReq, string], cmd_secure_template: [CmdSecureTemplateReq, string], cmd_get_themes: [CmdGetThemesReq, Array<GetThemesResponse>], cmd_enable_encryption: [CmdEnableEncryptionReq, null], cmd_reveal_workspace_key: [CmdRevealWorkspaceKeyReq, string], cmd_set_workspace_key: [CmdSetWorkspaceKeyReq, null], cmd_disable_encryption: [CmdDisableEncryptionReq, null], cmd_default_headers: [CmdDefaultHeadersReq, Array<HttpRequestHeader>], models_upsert: [ModelsUpsertReq, string], models_delete: [ModelsDeleteReq, string], models_duplicate: [ModelsDuplicateReq, string], models_websocket_events: [ModelsWebsocketEventsReq, Array<WebsocketEvent>], models_grpc_events: [ModelsGrpcEventsReq, Array<GrpcEvent>], models_get_settings: [ModelsGetSettingsReq, Settings], models_get_graphql_introspection: [ModelsGetGraphqlIntrospectionReq, GraphQlIntrospection | null], models_upsert_graphql_introspection: [ModelsUpsertGraphqlIntrospectionReq, GraphQlIntrospection], models_workspace_models: [ModelsWorkspaceModelsReq, string], cmd_git_checkout: [CmdGitCheckoutReq, string], cmd_git_branch: [CmdGitBranchReq, null], cmd_git_delete_branch: [CmdGitDeleteBranchReq, BranchDeleteResult], cmd_git_delete_remote_branch: [CmdGitDeleteRemoteBranchReq, null], cmd_git_merge_branch: [CmdGitMergeBranchReq, null], cmd_git_rename_branch: [CmdGitRenameBranchReq, null], cmd_git_status: [CmdGitStatusReq, GitStatusSummary], cmd_git_branch_info: [CmdGitBranchInfoReq, GitBranchInfo], cmd_git_worktree_status: [CmdGitWorktreeStatusReq, GitWorktreeStatus], cmd_git_log: [CmdGitLogReq, Array<GitCommit>], cmd_git_log_for_file: [CmdGitLogForFileReq, Array<GitCommit>], cmd_git_file_diff_for_commit: [CmdGitFileDiffForCommitReq, GitFileDiff], cmd_git_initialize: [CmdGitInitializeReq, nuLine truncated
|
||||
|
||||
export type WatchResult = { unlistenEvent: string, };
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
Generated
File renamed without changes.
Generated
File renamed without changes.
@@ -0,0 +1,4 @@
|
||||
// The RPC wire schema, generated by ts-rs from the Rust declarations in
|
||||
// src/lib.rs. `RpcSchema` maps every command name to its (request, response)
|
||||
// pair; the app's `rpc()` helper derives its command union from it.
|
||||
export * from "./bindings/gen_rpc";
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"name": "@yaakapp-internal/rpc-schema",
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"main": "index.ts"
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,5 +1,5 @@
|
||||
import { platform } from "@yaakapp-internal/platform";
|
||||
import type { WatchResult } from "@yaakapp-internal/tauri-client";
|
||||
import type { WatchResult } from "@yaakapp-internal/rpc-schema";
|
||||
import { SyncOp } from "./bindings/gen_sync";
|
||||
import { WatchEvent } from "./bindings/gen_watch";
|
||||
|
||||
|
||||
@@ -58,6 +58,7 @@
|
||||
"crates-tauri/yaak-fonts",
|
||||
"crates-tauri/yaak-license",
|
||||
"crates-tauri/yaak-mac-window",
|
||||
"crates/common/yaak-rpc-schema",
|
||||
"crates/yaak-crypto",
|
||||
"crates/yaak-git",
|
||||
"crates/yaak-models",
|
||||
|
||||
Reference in new issue
Block a user