feat: add tauri-schema-worker (#10871)

This commit is contained in:
Amr Bashir 2024-09-17 00:52:56 +03:00 committed by GitHub
parent ad294d274d
commit a1cce04260
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
20 changed files with 1082 additions and 44 deletions

View File

@ -23,14 +23,14 @@
"prepublish": [
"cargo install cargo-audit --features=fix",
{
"command": "cargo generate-lockfile",
"command": "echo '<details>\n<summary><em><h4>Cargo Audit</h4></em></summary>\n\n```'",
"dryRunCommand": true,
"runFromRoot": true,
"pipe": true
},
{
"command": "echo '<details>\n<summary><em><h4>Cargo Audit</h4></em></summary>\n\n```'",
"command": "cargo generate-lockfile",
"dryRunCommand": true,
"runFromRoot": true,
"pipe": true
},
{
@ -79,15 +79,15 @@
}
},
"prepublish": [
{
"command": "pnpm i --frozen-lockfile",
"dryRunCommand": true
},
{
"command": "echo '<details>\n<summary><em><h4>PNPM Audit</h4></em></summary>\n\n```'",
"dryRunCommand": true,
"pipe": true
},
{
"command": "pnpm i --frozen-lockfile",
"dryRunCommand": true
},
{
"command": "pnpm audit",
"dryRunCommand": true,
@ -191,16 +191,7 @@
"path": "./crates/tauri-build",
"manager": "rust",
"dependencies": ["tauri-codegen", "tauri-utils"],
"postversion": [
"node ../../.scripts/ci/sync-cli-metadata.js ${ pkg.pkg } ${ release.type }",
"cargo build --manifest-path ../tauri-schema-generator/Cargo.toml"
],
"assets": [
{
"path": "./crates/tauri-schema-generator/schemas/config.schema.json",
"name": "config.schema.json"
}
]
"postversion": "node ../../.scripts/ci/sync-cli-metadata.js ${ pkg.pkg } ${ release.type }"
},
"tauri": {
"path": "./crates/tauri",
@ -212,16 +203,16 @@
"tauri-runtime-wry",
"tauri-build"
],
"postversion": "node ../../.scripts/ci/sync-cli-metadata.js ${ pkg.pkg } ${ release.type }"
"postversion": [
"node ../../.scripts/ci/sync-cli-metadata.js ${ pkg.pkg } ${ release.type }",
"cargo build --manifest-path ../tauri-schema-generator/Cargo.toml"
]
},
"@tauri-apps/cli": {
"path": "./packages/cli",
"manager": "javascript",
"dependencies": ["tauri-cli"],
"postversion": [
"node ../../.scripts/ci/sync-cli-metadata.js ${ pkg.pkg } ${ release.type }",
"cargo build --manifest-path ../../crates/tauri-schema-generator/Cargo.toml"
],
"postversion": "node ../../.scripts/ci/sync-cli-metadata.js ${ pkg.pkg } ${ release.type }",
"prepublish": [],
"publish": [],
"postpublish": []
@ -230,10 +221,7 @@
"path": "./crates/tauri-cli",
"manager": "rust",
"dependencies": ["tauri-bundler", "tauri-utils", "tauri-macos-sign"],
"postversion": [
"cargo check",
"cargo build --manifest-path ../tauri-schema-generator/Cargo.toml"
]
"postversion": "cargo check"
},
"tauri-driver": {
"path": "./crates/tauri-driver",

View File

@ -0,0 +1,25 @@
# Copyright 2019-2024 Tauri Programme within The Commons Conservancy
# SPDX-License-Identifier: Apache-2.0
# SPDX-License-Identifier: MIT
name: deploy schema worker
on:
push:
branches:
- dev
paths:
- '.github/workflows/deploy-schema-worker.yml'
- 'crates/tauri-schema-worker/**'
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: cloudflare/wrangler-action@3
with:
command: deploy
workingDirectory: 'crates/tauri-schema-worker'
apiToken: ${{ secrets.SCHEMA_WORKER_CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.SCHEMA_WORKER_CLOUDFLARE_ACCOUNT_ID }}

View File

@ -1,7 +1,10 @@
/.changes
/audits
/.vscode
# change files are hand-written and shouldn't be formatted
/.changes/*
!/.changes/config.json
# dependcies and artifacts directories
node_modules/
target/
@ -25,6 +28,7 @@ crates/tauri-cli/templates
**/autogenerated/**/*.md
packages/cli/index.js
packages/cli/index.d.ts
crates/tauri-schema-worker/.wrangler
CHANGELOG.md
*schema.json

121
Cargo.lock generated
View File

@ -1205,6 +1205,16 @@ dependencies = [
"windows-sys 0.52.0",
]
[[package]]
name = "console_error_panic_hook"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc"
dependencies = [
"cfg-if",
"wasm-bindgen",
]
[[package]]
name = "const-oid"
version = "0.9.6"
@ -6450,6 +6460,28 @@ dependencies = [
"serde",
]
[[package]]
name = "serde-wasm-bindgen"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3b143e2833c57ab9ad3ea280d21fd34e285a42837aeb0ee301f4f41890fa00e"
dependencies = [
"js-sys",
"serde",
"wasm-bindgen",
]
[[package]]
name = "serde-wasm-bindgen"
version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8302e169f0eddcc139c70f139d19d6467353af16f9fce27e8c30158036a1e16b"
dependencies = [
"js-sys",
"serde",
"wasm-bindgen",
]
[[package]]
name = "serde_derive"
version = "1.0.209"
@ -7708,6 +7740,20 @@ dependencies = [
"url",
]
[[package]]
name = "tauri-schema-worker"
version = "0.0.0"
dependencies = [
"anyhow",
"axum",
"console_error_panic_hook",
"semver",
"serde",
"tower-service",
"worker",
"worker-macros",
]
[[package]]
name = "tauri-utils"
version = "1.6.0"
@ -9326,6 +9372,81 @@ version = "0.0.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904"
[[package]]
name = "worker"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3bd73bd2ea409ae91df99293cbed8b892d39c4c0df5039b646be7586df62c6b"
dependencies = [
"async-trait",
"axum",
"bytes",
"chrono",
"futures-channel",
"futures-util",
"http 1.1.0",
"http-body 1.0.1",
"js-sys",
"matchit",
"pin-project",
"serde",
"serde-wasm-bindgen 0.6.5",
"serde_json",
"serde_urlencoded",
"tokio",
"url",
"wasm-bindgen",
"wasm-bindgen-futures",
"wasm-streams",
"web-sys",
"worker-kv",
"worker-macros",
"worker-sys",
]
[[package]]
name = "worker-kv"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f06d4d1416a9f8346ee9123b0d9a11b3cfa38e6cfb5a139698017d1597c4d41"
dependencies = [
"js-sys",
"serde",
"serde-wasm-bindgen 0.5.0",
"serde_json",
"thiserror",
"wasm-bindgen",
"wasm-bindgen-futures",
]
[[package]]
name = "worker-macros"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0bbf47d65e77652febb28abedac18b317d8dfe4e57f0d8d9998c4e991fca8e23"
dependencies = [
"async-trait",
"proc-macro2",
"quote",
"syn 2.0.76",
"wasm-bindgen",
"wasm-bindgen-futures",
"wasm-bindgen-macro-support",
"worker-sys",
]
[[package]]
name = "worker-sys"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4fbb72a85a6509e5ac5dcd1361543468be089ff5ea5c932043b6d0aeac7b6a5"
dependencies = [
"cfg-if",
"js-sys",
"wasm-bindgen",
"web-sys",
]
[[package]]
name = "wry"
version = "0.43.1"

View File

@ -9,6 +9,7 @@ members = [
"crates/tauri-codegen",
"crates/tauri-plugin",
"crates/tauri-schema-generator",
"crates/tauri-schema-worker",
"crates/tauri-cli",
"crates/tauri-bundler",
"crates/tauri-macos-sign",
@ -50,6 +51,7 @@ codegen-units = 1
lto = true
incremental = false
opt-level = "s"
strip = true
# profiles for tauri-cli
[profile.dev.package.miniz_oxide]

View File

@ -1,5 +1,6 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "tauri-config-2.0.0-rc.14",
"title": "Config",
"description": "The Tauri configuration object.\n It is read from a file where you can define your frontend assets,\n configure the bundler and define a tray icon.\n\n The configuration file is generated by the\n [`tauri init`](https://tauri.app/v1/api/cli#init) command that lives in\n your Tauri application source directory (src-tauri).\n\n Once generated, you may modify it at will to customize your Tauri application.\n\n ## File Formats\n\n By default, the configuration is defined as a JSON file named `tauri.conf.json`.\n\n Tauri also supports JSON5 and TOML files via the `config-json5` and `config-toml` Cargo features, respectively.\n The JSON5 file name must be either `tauri.conf.json` or `tauri.conf.json5`.\n The TOML file name is `Tauri.toml`.\n\n ## Platform-Specific Configuration\n\n In addition to the default configuration file, Tauri can\n read a platform-specific configuration from `tauri.linux.conf.json`,\n `tauri.windows.conf.json`, `tauri.macos.conf.json`, `tauri.android.conf.json` and `tauri.ios.conf.json`\n (or `Tauri.linux.toml`, `Tauri.windows.toml`, `Tauri.macos.toml`, `Tauri.android.toml` and `Tauri.ios.toml` if the `Tauri.toml` format is used),\n which gets merged with the main configuration object.\n\n ## Configuration Structure\n\n The configuration is composed of the following objects:\n\n - [`app`](#appconfig): The Tauri configuration\n - [`build`](#buildconfig): The build configuration\n - [`bundle`](#bundleconfig): The bundle configurations\n - [`plugins`](#pluginconfig): The plugins configuration\n\n Example tauri.config.json file:\n\n ```json\n {\n \"productName\": \"tauri-app\",\n \"version\": \"0.1.0\",\n \"build\": {\n \"beforeBuildCommand\": \"\",\n \"beforeDevCommand\": \"\",\n \"devUrl\": \"../dist\",\n \"frontendDist\": \"../dist\"\n },\n \"app\": {\n \"security\": {\n \"csp\": null\n },\n \"windows\": [\n {\n \"fullscreen\": false,\n \"height\": 600,\n \"resizable\": true,\n \"title\": \"Tauri App\",\n \"width\": 800\n }\n ]\n },\n \"bundle\": {},\n \"plugins\": {}\n }\n ```",
"type": "object",

View File

@ -1,4 +1,5 @@
{
"$schema": "https://schema.tauri.app/config/2.0.0-rc",
"productName": "{{ app_name }}",
"version": "0.1.0",
"identifier": "com.tauri.dev",

View File

@ -4,6 +4,7 @@
use std::{error::Error, path::PathBuf};
use serde::Deserialize;
use tauri_utils::{
acl::{capability::Capability, Permission, Scopes},
config::Config,
@ -16,9 +17,13 @@ macro_rules! schema {
};
}
#[derive(Deserialize)]
pub struct VersionMetadata {
tauri: String,
}
pub fn main() -> Result<(), Box<dyn Error>> {
let schemas = [
schema!("config", Config),
schema!("capability", Capability),
schema!("permission", Permission),
schema!("scope", Scopes),
@ -32,10 +37,21 @@ pub fn main() -> Result<(), Box<dyn Error>> {
for (filename, schema) in schemas {
let schema = serde_json::to_string_pretty(&schema)?;
write_if_changed(schemas_dir.join(filename), &schema)?;
}
if filename.starts_with("config") {
write_if_changed(out.join("../tauri-cli/config.schema.json"), schema)?;
}
// write config schema file
{
let metadata = include_str!("../tauri-cli/metadata-v2.json");
let tauri_ver = serde_json::from_str::<VersionMetadata>(metadata)?.tauri;
// set id for generated schema
let (filename, mut config_schema) = schema!("config", Config);
let schema_metadata = config_schema.schema.metadata.as_mut().unwrap();
schema_metadata.id = Some(format!("tauri-config-{}", tauri_ver));
let config_schema = serde_json::to_string_pretty(&config_schema)?;
write_if_changed(schemas_dir.join(filename), &config_schema)?;
write_if_changed(out.join("../tauri-cli/config.schema.json"), config_schema)?;
}
Ok(())

View File

@ -1,5 +1,6 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "tauri-config-2.0.0-rc.14",
"title": "Config",
"description": "The Tauri configuration object.\n It is read from a file where you can define your frontend assets,\n configure the bundler and define a tray icon.\n\n The configuration file is generated by the\n [`tauri init`](https://tauri.app/v1/api/cli#init) command that lives in\n your Tauri application source directory (src-tauri).\n\n Once generated, you may modify it at will to customize your Tauri application.\n\n ## File Formats\n\n By default, the configuration is defined as a JSON file named `tauri.conf.json`.\n\n Tauri also supports JSON5 and TOML files via the `config-json5` and `config-toml` Cargo features, respectively.\n The JSON5 file name must be either `tauri.conf.json` or `tauri.conf.json5`.\n The TOML file name is `Tauri.toml`.\n\n ## Platform-Specific Configuration\n\n In addition to the default configuration file, Tauri can\n read a platform-specific configuration from `tauri.linux.conf.json`,\n `tauri.windows.conf.json`, `tauri.macos.conf.json`, `tauri.android.conf.json` and `tauri.ios.conf.json`\n (or `Tauri.linux.toml`, `Tauri.windows.toml`, `Tauri.macos.toml`, `Tauri.android.toml` and `Tauri.ios.toml` if the `Tauri.toml` format is used),\n which gets merged with the main configuration object.\n\n ## Configuration Structure\n\n The configuration is composed of the following objects:\n\n - [`app`](#appconfig): The Tauri configuration\n - [`build`](#buildconfig): The build configuration\n - [`bundle`](#bundleconfig): The bundle configurations\n - [`plugins`](#pluginconfig): The plugins configuration\n\n Example tauri.config.json file:\n\n ```json\n {\n \"productName\": \"tauri-app\",\n \"version\": \"0.1.0\",\n \"build\": {\n \"beforeBuildCommand\": \"\",\n \"beforeDevCommand\": \"\",\n \"devUrl\": \"../dist\",\n \"frontendDist\": \"../dist\"\n },\n \"app\": {\n \"security\": {\n \"csp\": null\n },\n \"windows\": [\n {\n \"fullscreen\": false,\n \"height\": 600,\n \"resizable\": true,\n \"title\": \"Tauri App\",\n \"width\": 800\n }\n ]\n },\n \"bundle\": {},\n \"plugins\": {}\n }\n ```",
"type": "object",

4
crates/tauri-schema-worker/.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
/target
/node_modules
/.wrangler
/build

View File

@ -0,0 +1,18 @@
[package]
name = "tauri-schema-worker"
version = "0.0.0"
edition = "2021"
publish = false
[lib]
crate-type = ["cdylib"]
[dependencies]
worker = { version = "0.3", features = ['http', 'axum'] }
worker-macros = { version = "0.3", features = ['http'] }
console_error_panic_hook = { version = "0.1" }
axum = { version = "0.7", default-features = false }
tower-service = "0.3"
semver = { version = "1.0", features = ["serde"] }
serde = { version = "1.0", features = ["derive"] }
anyhow = "1.0"

View File

@ -0,0 +1,3 @@
# schema.tauri.app worker
Source code for `https://schema.tauri.app` cloudflare worker.

View File

@ -0,0 +1,13 @@
{
"name": "tauri-schema-worker",
"version": "0.0.0",
"license": "Apache-2.0 OR MIT",
"private": "true",
"scripts": {
"deploy": "wrangler deploy",
"dev": "wrangler dev"
},
"devDependencies": {
"wrangler": "^3.73"
}
}

View File

@ -0,0 +1,174 @@
// Copyright 2019-2024 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
use anyhow::Context;
use axum::{
extract::Path,
http::{header, StatusCode},
response::Result,
routing::get,
Router,
};
use semver::{Version, VersionReq};
use serde::Deserialize;
use worker::*;
#[derive(Deserialize)]
pub struct CrateReleases {
pub versions: Vec<CrateRelease>,
}
#[derive(Debug, Deserialize)]
pub struct CrateRelease {
#[serde(alias = "num")]
pub version: Version,
pub yanked: Option<bool>,
}
#[derive(Deserialize)]
pub struct CrateMetadataFull {
#[serde(rename = "crate")]
pub crate_: CrateMetadata,
}
#[derive(Deserialize)]
pub struct CrateMetadata {
pub max_stable_version: Version,
}
const USERAGENT: &str = "tauri-schema-worker (contact@tauri.app)";
pub fn router() -> Router {
Router::new()
.route("/config", get(stable_schema))
.route("/config/latest", get(stable_schema))
.route("/config/stable", get(stable_schema))
.route("/config/next", get(next_schema)) // pre-releases versions, (rc, alpha and beta)
.route("/config/:version", get(schema_for_version))
}
async fn schema_for_version(Path(version): Path<String>) -> Result<String> {
try_schema_for_version(version)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))
.map_err(Into::into)
}
async fn stable_schema() -> Result<String> {
try_stable_schema()
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))
.map_err(Into::into)
}
async fn next_schema() -> Result<String> {
try_next_schema()
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))
.map_err(Into::into)
}
#[worker::send]
async fn try_schema_for_version(version: String) -> anyhow::Result<String> {
let version = version.parse::<VersionReq>()?;
let releases = crate_releases("tauri").await?;
if releases.is_empty() {
return try_stable_schema().await;
}
let Some(version) = releases.into_iter().find(|r| version.matches(&r.version)) else {
return try_stable_schema().await;
};
schema_file_for_version(version.version).await
}
#[worker::send]
async fn try_stable_schema() -> anyhow::Result<String> {
let max = stable_version("tauri").await?;
schema_file_for_version(max).await
}
#[worker::send]
async fn try_next_schema() -> anyhow::Result<String> {
let releases = crate_releases("tauri").await?;
let version = releases
.into_iter()
.filter(|r| !r.version.pre.is_empty())
.map(|r| r.version)
.max()
.context("Couldn't find latest pre-release")?;
schema_file_for_version(version).await
}
async fn schema_file_for_version(version: Version) -> anyhow::Result<String> {
let cache = Cache::open("schema".to_string()).await;
let cache_key = format!("https://scheam.tauri.app/config/{version}");
if let Some(mut cached) = cache.get(cache_key.clone(), true).await? {
console_log!("Serving schema for {version} from cache");
return cached.text().await.map_err(Into::into);
}
console_log!("Fetching schema for {version} from remote");
let path = if version.major >= 2 {
"crates/tauri-schema-generator/schemas/config.schema.json"
} else {
"core/tauri-config-schema/schema.json"
};
let url = format!("https://raw.githubusercontent.com/tauri-apps/tauri/tauri-v{version}/{path}");
let mut res = Fetch::Request(fetch_req(&url)?).send().await?;
cache.put(cache_key, res.cloned()?).await?;
res.text().await.map_err(Into::into)
}
async fn crate_releases(crate_: &str) -> anyhow::Result<Vec<CrateRelease>> {
let url = format!("https://crates.io/api/v1/crates/{crate_}/versions");
let mut res = Fetch::Request(fetch_req(&url)?).send().await?;
let versions: CrateReleases = res.json().await?;
let versions = versions.versions;
let flt = |r: &CrateRelease| r.yanked == Some(false);
Ok(versions.into_iter().filter(flt).collect())
}
async fn stable_version(crate_: &str) -> anyhow::Result<Version> {
let url = format!("https://crates.io/api/v1/crates/{crate_}");
let mut res = Fetch::Request(fetch_req(&url)?).send().await?;
let metadata: CrateMetadataFull = res.json().await?;
Ok(metadata.crate_.max_stable_version)
}
fn fetch_req(url: &str) -> anyhow::Result<worker::Request> {
let mut headers = Headers::new();
headers.append(header::USER_AGENT.as_str(), USERAGENT)?;
worker::Request::new_with_init(
url,
&RequestInit {
method: Method::Get,
headers,
cf: CfProperties {
cache_ttl: Some(86400),
cache_everything: Some(true),
cache_ttl_by_status: Some(
[
("200-299".to_string(), 86400),
("404".to_string(), 1),
("500-599".to_string(), 0),
]
.into(),
),
..Default::default()
},
..Default::default()
},
)
.map_err(Into::into)
}

View File

@ -0,0 +1,27 @@
// Copyright 2019-2024 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
use axum::{routing::get, Router};
use tower_service::Service;
use worker::*;
mod config;
#[worker::event(fetch)]
async fn main(
req: HttpRequest,
_env: Env,
_ctx: Context,
) -> worker::Result<axum::http::Response<axum::body::Body>> {
console_error_panic_hook::set_once();
Ok(router().call(req).await?)
}
fn router() -> Router {
Router::new().route("/", get(root)).merge(config::router())
}
async fn root() -> &'static str {
"tauri schema worker"
}

View File

@ -0,0 +1,11 @@
# Copyright 2019-2022 Tauri Programme within The Commons Conservancy
# SPDX-License-Identifier: Apache-2.0
# SPDX-License-Identifier: MIT
name = "tauri-schema"
main = "build/worker/shim.mjs"
compatibility_date = "2023-08-23"
send_metrics = false
[build]
command = "cargo install -q worker-build && worker-build --release"

View File

@ -19,7 +19,7 @@
"@unocss/extractor-svelte": "^0.61.0",
"unocss": "^0.61.0",
"@sveltejs/vite-plugin-svelte": "^3.1.1",
"svelte": "^4.2.18",
"svelte": "^4.2.19",
"vite": "^5.4.1"
}
}

View File

@ -19,11 +19,5 @@
"devDependencies": {
"prettier": "^3.3.3"
},
"packageManager": "pnpm@9.7.1",
"pnpm": {
"overrides": {
"micromatch@<4.0.8": ">=4.0.8",
"svelte@<4.2.19": ">=4.2.19"
}
}
"packageManager": "pnpm@9.9.0"
}

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,7 @@
packages:
- packages/api
- packages/cli
- crates/tauri-schema-worker
- examples/api
- examples/resources
- examples/file-associations