Merge pull request 'push-xsnmptvkprqt' (#13) from push-xsnmptvkprqt into main

Reviewed-on: #13
This commit was merged in pull request #13.
This commit is contained in:
2025-10-06 11:47:18 +02:00
1255 changed files with 1622 additions and 545346 deletions

141
Cargo.lock generated
View File

@@ -7,7 +7,9 @@ name = "PMOMusic"
version = "0.1.0"
dependencies = [
"axum",
"pmoapp",
"pmoconfig",
"pmoserver",
"pmoupnp",
"serde_json",
"tokio",
@@ -467,41 +469,6 @@ dependencies = [
"typenum",
]
[[package]]
name = "darling"
version = "0.20.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee"
dependencies = [
"darling_core",
"darling_macro",
]
[[package]]
name = "darling_core"
version = "0.20.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e"
dependencies = [
"fnv",
"ident_case",
"proc-macro2",
"quote",
"strsim",
"syn",
]
[[package]]
name = "darling_macro"
version = "0.20.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead"
dependencies = [
"darling_core",
"quote",
"syn",
]
[[package]]
name = "derive_arbitrary"
version = "1.4.2"
@@ -1129,12 +1096,6 @@ dependencies = [
"zerovec",
]
[[package]]
name = "ident_case"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
[[package]]
name = "idna"
version = "1.1.0"
@@ -1483,6 +1444,13 @@ version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
[[package]]
name = "pmoapp"
version = "0.1.0"
dependencies = [
"rust-embed",
]
[[package]]
name = "pmoconfig"
version = "0.1.0"
@@ -1511,41 +1479,50 @@ dependencies = [
]
[[package]]
name = "pmoupnp"
name = "pmoserver"
version = "0.1.0"
dependencies = [
"anyhow",
"async-stream",
"axum",
"axum-embed",
"axum-server",
"futures",
"futures-util",
"pmoconfig",
"pmoupnp",
"rust-embed",
"serde",
"serde_json",
"tokio",
"tokio-stream",
"tracing",
"tracing-subscriber",
"utoipa",
"utoipa-swagger-ui",
]
[[package]]
name = "pmoupnp"
version = "0.1.0"
dependencies = [
"axum",
"base64",
"bevy_reflect",
"bevy_reflect_derive",
"chrono",
"futures",
"futures-util",
"get_if_addrs",
"hex",
"log",
"once_cell",
"parking_lot",
"pmoconfig",
"pmodidl",
"reqwest",
"rust-embed",
"serde",
"serde_json",
"thiserror",
"tokio",
"tokio-stream",
"tracing",
"tracing-subscriber",
"url",
"utoipa",
"utoipa-swagger-ui",
"uuid",
"validator",
"xmltree",
]
@@ -1580,28 +1557,6 @@ dependencies = [
"zerovec",
]
[[package]]
name = "proc-macro-error-attr2"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5"
dependencies = [
"proc-macro2",
"quote",
]
[[package]]
name = "proc-macro-error2"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802"
dependencies = [
"proc-macro-error-attr2",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "proc-macro2"
version = "1.0.101"
@@ -2050,12 +2005,6 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
[[package]]
name = "strsim"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
[[package]]
name = "subtle"
version = "2.6.1"
@@ -2492,36 +2441,6 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "validator"
version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43fb22e1a008ece370ce08a3e9e4447a910e92621bb49b85d6e48a45397e7cfa"
dependencies = [
"idna",
"once_cell",
"regex",
"serde",
"serde_derive",
"serde_json",
"url",
"validator_derive",
]
[[package]]
name = "validator_derive"
version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7df16e474ef958526d1205f6dda359fdfab79d9aa6d54bafcb92dcd07673dca"
dependencies = [
"darling",
"once_cell",
"proc-macro-error2",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "valuable"
version = "0.1.1"

View File

@@ -1,3 +1,3 @@
[workspace]
resolver = "3"
members = ["PMOMusic", "pmoupnp","pmoconfig", "pmoutils", "pmodidl"]
members = ["PMOMusic", "pmoupnp","pmoconfig", "pmoutils", "pmodidl", "pmoserver", "pmoapp"]

View File

@@ -2,7 +2,7 @@
# Variables de configuration
CARGO = cargo
NPM = npm
WEBAPP_DIR = pmoupnp/webapp
WEBAPP_DIR = pmoapp/webapp
DIST_DIR = $(WEBAPP_DIR)/dist
RUST_TARGET = target/release
DOC_DIR = target/doc
@@ -29,13 +29,13 @@ build: webapp release
@echo "$(GREEN)✓ Build complet terminé$(NC)"
## release: Compile le binaire Rust en mode release
release:
release: webapp
@echo "$(YELLOW)→ Compilation Rust (release)...$(NC)"
$(CARGO) build --release
@echo "$(GREEN)✓ Binaire disponible : $(RUST_TARGET)/$(BINARY_NAME)$(NC)"
## debug: Compile le binaire Rust en mode debug
debug:
debug: webapp
@echo "$(YELLOW)→ Compilation Rust (debug)...$(NC)"
$(CARGO) build
@echo "$(GREEN)✓ Binaire disponible : target/debug/$(BINARY_NAME)$(NC)"

View File

@@ -6,6 +6,8 @@ edition = "2024"
[dependencies]
pmoconfig = { path = "../pmoconfig" }
pmoupnp = { path = "../pmoupnp"}
pmoserver = { path = "../pmoserver" }
pmoapp = { path = "../pmoapp" }
tokio = { version = "1.35", features = ["rt-multi-thread", "macros", "sync", "time","signal"] }

View File

@@ -1,6 +1,9 @@
use pmoupnp::{mediarenderer::avtransport::{actions::SETAVTRANSPORTURI, AVTTRANSPORT}, server::{
logs::{log_dump, log_sse, LogState, SseLayer}, ServerBuilder, Webapp
}, UpnpObject}; // ton module pmoupnp::server
use pmoupnp::{mediarenderer::avtransport::AVTTRANSPORT, UpnpObject};
use pmoserver::{
logs::{log_dump, log_sse, LogState, SseLayer},
ServerBuilder
};
use pmoapp::Webapp;
use tracing_subscriber::Registry;
use tracing_subscriber::prelude::*;
use tracing::info;

7
pmoapp/Cargo.toml Normal file
View File

@@ -0,0 +1,7 @@
[package]
name = "pmoapp"
version = "0.1.0"
edition = "2021"
[dependencies]
rust-embed = "8.5.0"

51
pmoapp/src/lib.rs Normal file
View File

@@ -0,0 +1,51 @@
//! # pmoapp - Application web UPnP pour PMOMusic
//!
//! Cette crate fournit l'application web frontend pour le contrôle UPnP,
//! intégrée via RustEmbed pour être servie par pmoserver.
//!
//! ## Fonctionnalités
//!
//! - 📦 **Frontend intégré** : Application web compilée et embarquée dans le binaire
//! - 🎨 **Interface de contrôle** : UI pour gérer les devices UPnP MediaRenderer
//! - 🚀 **Zero configuration** : Pas besoin de servir des fichiers statiques séparés
//!
//! ## Utilisation
//!
//! ```rust,no_run
//! use pmoapp::Webapp;
//! use pmoserver::ServerBuilder;
//!
//! # async fn example() {
//! let mut server = ServerBuilder::new("MyApp").build();
//! server.add_spa::<Webapp>("/app").await;
//! # }
//! ```
//!
//! ## Structure
//!
//! La webapp est construite avec Vite et Vue.js, et les fichiers statiques
//! sont embarqués dans le binaire au moment de la compilation via `RustEmbed`.
use rust_embed::RustEmbed;
/// Structure représentant l'application web embarquée.
///
/// Cette structure utilise `RustEmbed` pour inclure tous les fichiers
/// du répertoire `webapp/dist` dans le binaire au moment de la compilation.
///
/// ## Exemple
///
/// ```rust,no_run
/// use pmoapp::Webapp;
/// use pmoserver::ServerBuilder;
///
/// # async fn example() {
/// let mut server = ServerBuilder::new("MyApp").build();
///
/// // Ajouter la webapp comme SPA sur le chemin /app
/// server.add_spa::<Webapp>("/app").await;
/// # }
/// ```
#[derive(RustEmbed, Clone)]
#[folder = "webapp/dist"]
pub struct Webapp;

View File

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

Before

Width:  |  Height:  |  Size: 496 B

After

Width:  |  Height:  |  Size: 496 B

View File

@@ -87,11 +87,41 @@ function formatTimestamp(timestamp) {
}
function renderMarkdown(text) {
// Convertir markdown en HTML et nettoyer pour la sécurité
const rawHtml = marked.parse(text, { async: false })
// ÉTAPE 1 : Pré-processing pour détecter et protéger le XML
let processedText = text
// Détecter si le message contient du XML
// Pattern : cherche <?xml ou des balises XML racine communes (scpd, root, service, etc.)
const hasXml = /<\?xml|<(scpd|root|service|device|actionList|stateVariable)[>\s]/i.test(text)
if (hasXml) {
// Extraire tout ce qui ressemble à du XML (du <?xml ou première balise jusqu'à la fin)
const xmlStartMatch = text.match(/<\?xml[\s\S]*$/)
if (xmlStartMatch) {
const xmlContent = xmlStartMatch[0]
const beforeXml = text.substring(0, text.indexOf(xmlContent))
// Créer le texte avec le XML dans un bloc de code
processedText = beforeXml + '\n```xml\n' + xmlContent + '\n```\n'
} else {
// Fallback : chercher une balise racine XML
const xmlMatch = text.match(/<([a-zA-Z][a-zA-Z0-9:-]*)[>\s][\s\S]*/)
if (xmlMatch) {
const xmlContent = xmlMatch[0]
const beforeXml = text.substring(0, text.indexOf(xmlContent))
processedText = beforeXml + '\n```xml\n' + xmlContent + '\n```\n'
}
}
}
// ÉTAPE 2 : Convertir markdown en HTML
const rawHtml = marked.parse(processedText, { async: false })
// ÉTAPE 3 : Nettoyer pour la sécurité
return DOMPurify.sanitize(rawHtml, {
ALLOWED_TAGS: ['strong', 'em', 'code', 'pre', 'a', 'ul', 'ol', 'li', 'p', 'br'],
ALLOWED_ATTR: ['href', 'target']
ALLOWED_TAGS: ['strong', 'em', 'code', 'pre', 'a', 'ul', 'ol', 'li', 'p', 'br', 'span'],
ALLOWED_ATTR: ['href', 'target', 'class']
})
}
@@ -425,16 +455,46 @@ button.active {
.markdown-content :deep(pre) {
background: #2d2d30;
padding: 0.5rem;
padding: 0.75rem;
border-radius: 4px;
overflow-x: auto;
margin: 0.25rem 0;
margin: 0.5rem 0;
border: 1px solid #3e3e42;
max-height: 400px;
overflow-y: auto;
}
.markdown-content :deep(pre code) {
background: transparent;
padding: 0;
color: #d4d4d4;
font-size: 0.85em;
line-height: 1.5;
display: block;
}
/* Coloration pour les blocs XML */
.markdown-content :deep(pre code.language-xml) {
color: #ce9178;
}
/* Scrollbar pour les blocs de code longs */
.markdown-content :deep(pre)::-webkit-scrollbar {
width: 8px;
height: 8px;
}
.markdown-content :deep(pre)::-webkit-scrollbar-track {
background: #1e1e1e;
}
.markdown-content :deep(pre)::-webkit-scrollbar-thumb {
background: #424242;
border-radius: 4px;
}
.markdown-content :deep(pre)::-webkit-scrollbar-thumb:hover {
background: #4e4e4e;
}
.markdown-content :deep(strong) {

27
pmoserver/Cargo.toml Normal file
View File

@@ -0,0 +1,27 @@
[package]
name = "pmoserver"
version = "0.1.0"
edition = "2024"
[dependencies]
pmoconfig = { path = "../pmoconfig" }
axum = "0.8.4"
tokio = { version = "1.35", features = ["rt-multi-thread", "macros", "sync", "time", "signal"] }
tokio-stream = "0.1"
futures-util = "0.3"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["fmt", "env-filter"] }
futures = "0.3"
async-stream = "0.3.6"
axum-server = "0.7.2"
axum-embed = "0.1.0"
rust-embed = "8.7.2"
utoipa = { version = "5.4.0", features = ["axum_extras"] }
utoipa-swagger-ui = { version = "9.0.2", features = ["axum"] }
[dependencies.pmoupnp]
path = "../pmoupnp"
default-features = false

79
pmoserver/src/lib.rs Normal file
View File

@@ -0,0 +1,79 @@
//! # pmoserver - Serveur web haut niveau basé sur Axum
//!
//! Cette crate fournit une abstraction simple et ergonomique pour créer des serveurs HTTP
//! avec Axum, spécialement conçue pour les applications UPnP et les serveurs multimédia.
//!
//! ## Fonctionnalités
//!
//! - 🚀 **API de haut niveau** : Interface simple pour créer des serveurs HTTP avec Axum
//! - 🎯 **Support UPnP** : Implémentation du trait `UpnpServer` pour connecter des devices UPnP
//! - 📡 **Server-Sent Events (SSE)** : Support intégré pour les logs en temps réel via SSE
//! - ⚛️ **Applications SPA** : Support pour servir des applications Single Page (Vue.js, React, etc.)
//! - 📁 **Fichiers statiques** : Serve de fichiers statiques avec `RustEmbed`
//! - 🔀 **Redirections** : Support pour les redirections HTTP
//! - 📚 **Documentation OpenAPI** : Génération automatique de Swagger UI
//! - ⚡ **Arrêt gracieux** : Gestion propre de l'arrêt sur Ctrl+C
//!
//! ## Architecture
//!
//! La crate est organisée en plusieurs modules :
//!
//! - [`server`] : Implémentation du serveur principal et du builder
//! - [`logs`] : Système de logs SSE pour monitoring en temps réel
//! - `upnp_impl` : Implémentation du trait `pmoupnp::UpnpServer` (privé)
//!
//! ## Exemple d'utilisation
//!
//! ```rust,no_run
//! use pmoserver::{ServerBuilder, logs::{LogState, SseLayer}};
//! use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
//!
//! #[tokio::main]
//! async fn main() {
//! // Configuration des logs avec SSE
//! let log_state = LogState::new();
//! tracing_subscriber::registry()
//! .with(SseLayer::new(log_state.clone()))
//! .init();
//!
//! // Création et démarrage du serveur
//! let mut server = ServerBuilder::new("MyServer")
//! .http_port(8080)
//! .build();
//!
//! // Ajout d'une route JSON
//! server.add_route("/api/status", || async {
//! serde_json::json!({"status": "ok"})
//! }).await;
//!
//! // Démarrage
//! server.start().await;
//! }
//! ```
//!
//! ## Intégration UPnP
//!
//! Le serveur implémente automatiquement le trait `pmoupnp::UpnpServer`, permettant
//! de connecter des devices UPnP :
//!
//! ```rust,no_run
//! use pmoupnp::{UpnpServer, mediarenderer::device::MEDIA_RENDERER};
//! use pmoupnp::devices::DeviceInstance;
//! use pmoserver::ServerBuilder;
//! use std::sync::Arc;
//!
//! # async fn example() {
//! let mut server = ServerBuilder::new("MediaRenderer").build();
//! let device = Arc::new(DeviceInstance::new(&MEDIA_RENDERER));
//!
//! // Le device enregistre automatiquement ses routes
//! device.register_urls(&mut server).await;
//! # }
//! ```
pub mod server;
pub mod logs;
mod upnp_impl;
pub use server::{Server, ServerBuilder, ServerInfo};
pub use logs::{LogState, SseLayer, log_sse, log_dump};

View File

@@ -13,8 +13,6 @@
//! - 📚 **Documentation API** : OpenAPI/Swagger automatique avec `add_openapi()`
//! - ⚡ **Gestion gracieuse** : Arrêt propre sur Ctrl+C
pub mod logs;
use axum::handler::Handler;
use axum::response::Redirect;
use axum::routing::get;
@@ -25,8 +23,7 @@ use rust_embed::RustEmbed;
use serde::Serialize;
use std::{net::SocketAddr, sync::Arc};
use tokio::{signal, sync::RwLock, task::JoinHandle};
use tracing::{info, warn, debug, error};
use utoipa::OpenApi;
use tracing::info;
use utoipa_swagger_ui::SwaggerUi;
/// Info serveur sérialisable
@@ -50,10 +47,6 @@ pub struct Server {
join_handle: Option<JoinHandle<()>>,
}
#[derive(RustEmbed, Clone)]
#[folder = "webapp/dist"]
pub struct Webapp;
impl Server {
/// Crée une nouvelle instance de serveur
///

View File

@@ -0,0 +1,94 @@
//! Implémentation du trait UpnpServer pour le serveur pmoserver
//!
//! Ce module fournit l'implémentation du trait [`pmoupnp::UpnpServer`] pour
//! le [`Server`](crate::server::Server) de pmoserver, permettant aux devices
//! et services UPnP d'enregistrer automatiquement leurs endpoints HTTP.
//!
//! ## Architecture
//!
//! L'implémentation fait le pont entre :
//! - Les pointeurs de fonction du trait `UpnpServer` (agnostiques du framework web)
//! - Les handlers Axum (spécifiques à l'implémentation `pmoserver`)
//!
//! Chaque méthode du trait crée un wrapper qui :
//! 1. Convertit les pointeurs de fonction en closures compatibles Axum
//! 2. Délègue l'enregistrement aux méthodes internes du `Server`
//! 3. Retourne une future qui se résout une fois le handler enregistré
//!
//! ## Exemple d'utilisation
//!
//! ```rust,no_run
//! use pmoupnp::{UpnpServer, mediarenderer::device::MEDIA_RENDERER};
//! use pmoupnp::devices::DeviceInstance;
//! use pmoserver::ServerBuilder;
//! use std::sync::Arc;
//!
//! # async fn example() {
//! let mut server = ServerBuilder::new("MyRenderer").build();
//! let device = Arc::new(DeviceInstance::new(&MEDIA_RENDERER));
//!
//! // Le trait UpnpServer est automatiquement disponible
//! device.register_urls(&mut server).await;
//! # }
//! ```
use crate::server::Server;
use pmoupnp::{UpnpServer, server::{Response, HeaderMap, Request}};
use std::future::Future;
use std::pin::Pin;
use axum::extract::State;
impl UpnpServer for Server {
fn add_handler<F, Fut>(&mut self, path: &str, handler: F) -> Pin<Box<dyn Future<Output = ()> + Send + '_>>
where
F: Fn() -> Fut + Send + Sync + 'static + Clone,
Fut: Future<Output = Response> + Send + 'static,
{
let path = path.to_string();
Box::pin(async move {
Self::add_handler(self, &path, handler).await;
})
}
fn add_post_handler_with_state<S>(
&mut self,
path: &str,
handler: fn(State<S>, String) -> Pin<Box<dyn Future<Output = Response> + Send>>,
state: S,
) -> Pin<Box<dyn Future<Output = ()> + Send + '_>>
where
S: Clone + Send + Sync + 'static,
{
let path = path.to_string();
// Créer un wrapper qui convertit le fn pointer en handler Axum
let wrapper = move |State(s): State<S>, body: String| -> Pin<Box<dyn Future<Output = Response> + Send>> {
handler(State(s), body)
};
Box::pin(async move {
Self::add_post_handler_with_state(self, &path, wrapper, state).await;
})
}
fn add_handler_with_state<S>(
&mut self,
path: &str,
handler: fn(State<S>, HeaderMap, Request) -> Pin<Box<dyn Future<Output = Response> + Send>>,
state: S,
) -> Pin<Box<dyn Future<Output = ()> + Send + '_>>
where
S: Clone + Send + Sync + 'static,
{
let path = path.to_string();
// Créer un wrapper qui convertit le fn pointer en handler Axum
let wrapper = move |State(s): State<S>, headers: HeaderMap, req: Request| -> Pin<Box<dyn Future<Output = Response> + Send>> {
handler(State(s), headers, req)
};
Box::pin(async move {
Self::add_handler_with_state(self, &path, wrapper, state).await;
})
}
}

View File

@@ -13,28 +13,14 @@ hex = "0.4.3"
base64 = "0.22.1"
thiserror = "2.0.16"
xmltree = "0.11.0"
get_if_addrs = "0.5.3"
axum = "0.8.4"
tokio = { version = "1.35", features = ["rt-multi-thread", "macros", "sync", "time", "signal"] }
tokio-stream = "0.1"
futures-util = "0.3"
tokio = { version = "1.35", features = ["rt-multi-thread", "macros", "sync"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
chrono = { version = "0.4.42", features = ["serde"] }
log = "0.4.28"
once_cell = "1.20"
parking_lot = "0.12"
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["fmt", "env-filter"] }
futures = "0.3"
async-stream = "0.3.6"
axum-server = "0.7.2"
axum-embed = "0.1.0"
rust-embed = "8.7.2"
anyhow = "1.0"
utoipa = { version = "5.4.0", features = ["axum_extras"] }
utoipa-swagger-ui = { version = "9.0.2", features = ["axum"] }
validator = { version = "0.20.0", features = ["derive"] }
bevy_reflect = "0.17.1"
bevy_reflect_derive = "0.17.1"
reqwest = "0.12.23"

View File

@@ -0,0 +1,309 @@
//! Définition du modèle Device UPnP.
use std::sync::{Arc, RwLock};
use std::collections::HashMap;
use crate::{
UpnpTyped, UpnpObjectType,
services::Service,
};
use super::errors::DeviceError;
/// Modèle d'un device UPnP.
///
/// Représente la définition d'un device selon la spécification UPnP Device Architecture.
/// Un device peut contenir plusieurs services et éventuellement des sous-devices.
#[derive(Debug)]
pub struct Device {
/// Métadonnées de l'objet
object: UpnpObjectType,
/// Type de device UPnP (ex: "MediaRenderer", "MediaServer")
device_type: String,
/// Version du device
version: u8,
/// Nom convivial du device
friendly_name: String,
/// Fabricant
manufacturer: String,
/// URL du fabricant
manufacturer_url: Option<String>,
/// Description du modèle
model_description: Option<String>,
/// Nom du modèle
model_name: String,
/// Numéro du modèle
model_number: Option<String>,
/// URL du modèle
model_url: Option<String>,
/// Numéro de série
serial_number: Option<String>,
/// UDN (Unique Device Name) - sera généré à l'instance
udn_prefix: String,
/// UPC (Universal Product Code)
upc: Option<String>,
/// URL de l'icône
icon_url: Option<String>,
/// URL de présentation
presentation_url: Option<String>,
/// Services du device
services: RwLock<HashMap<String, Arc<Service>>>,
/// Sous-devices (embedded devices)
devices: RwLock<HashMap<String, Arc<Device>>>,
}
impl Clone for Device {
fn clone(&self) -> Self {
Self {
object: self.object.clone(),
device_type: self.device_type.clone(),
version: self.version,
friendly_name: self.friendly_name.clone(),
manufacturer: self.manufacturer.clone(),
manufacturer_url: self.manufacturer_url.clone(),
model_description: self.model_description.clone(),
model_name: self.model_name.clone(),
model_number: self.model_number.clone(),
model_url: self.model_url.clone(),
serial_number: self.serial_number.clone(),
udn_prefix: self.udn_prefix.clone(),
upc: self.upc.clone(),
icon_url: self.icon_url.clone(),
presentation_url: self.presentation_url.clone(),
services: RwLock::new(self.services.read().unwrap().clone()),
devices: RwLock::new(self.devices.read().unwrap().clone()),
}
}
}
impl Device {
/// Crée un nouveau modèle de device.
///
/// # Arguments
///
/// * `name` - Nom unique du device
/// * `device_type` - Type UPnP du device
/// * `friendly_name` - Nom convivial pour l'utilisateur
pub fn new(name: String, device_type: String, friendly_name: String) -> Self {
Self {
object: UpnpObjectType {
name: name.clone(),
object_type: "Device".to_string(),
},
device_type,
version: 1,
friendly_name,
manufacturer: "PMOMusic".to_string(),
manufacturer_url: None,
model_description: None,
model_name: name.clone(),
model_number: None,
model_url: None,
serial_number: None,
udn_prefix: "pmomusic".to_string(),
upc: None,
icon_url: None,
presentation_url: None,
services: RwLock::new(HashMap::new()),
devices: RwLock::new(HashMap::new()),
}
}
/// Retourne le type de device UPnP.
///
/// Format: `urn:schemas-upnp-org:device:{type}:{version}`
pub fn device_type(&self) -> String {
format!("urn:schemas-upnp-org:device:{}:{}", self.device_type, self.version)
}
/// Définit la version du device.
pub fn set_version(&mut self, version: u8) -> Result<(), DeviceError> {
if version == 0 {
return Err(DeviceError::InvalidVersion);
}
self.version = version;
Ok(())
}
/// Retourne la version du device.
pub fn version(&self) -> u8 {
self.version
}
/// Définit le fabricant.
pub fn set_manufacturer(&mut self, manufacturer: String) {
self.manufacturer = manufacturer;
}
/// Définit l'URL du fabricant.
pub fn set_manufacturer_url(&mut self, url: String) {
self.manufacturer_url = Some(url);
}
/// Définit la description du modèle.
pub fn set_model_description(&mut self, description: String) {
self.model_description = Some(description);
}
/// Définit le nom du modèle.
pub fn set_model_name(&mut self, name: String) {
self.model_name = name;
}
/// Définit le numéro du modèle.
pub fn set_model_number(&mut self, number: String) {
self.model_number = Some(number);
}
/// Définit le numéro de série.
pub fn set_serial_number(&mut self, serial: String) {
self.serial_number = Some(serial);
}
/// Définit le préfixe UDN.
pub fn set_udn_prefix(&mut self, prefix: String) {
self.udn_prefix = prefix;
}
/// Retourne le préfixe UDN.
pub fn udn_prefix(&self) -> &str {
&self.udn_prefix
}
/// Définit l'URL de présentation.
pub fn set_presentation_url(&mut self, url: String) {
self.presentation_url = Some(url);
}
/// Ajoute un service au device.
///
/// # Errors
///
/// Retourne une erreur si un service avec le même nom existe déjà.
pub fn add_service(&self, service: Arc<Service>) -> Result<(), DeviceError> {
let mut services = self.services.write().unwrap();
let name = service.get_name().to_string();
if services.contains_key(&name) {
return Err(DeviceError::ServiceAlreadyExists(name));
}
services.insert(name, service);
Ok(())
}
/// Retourne tous les services.
pub fn services(&self) -> Vec<Arc<Service>> {
self.services.read().unwrap().values().cloned().collect()
}
/// Retourne un service par nom.
pub fn get_service(&self, name: &str) -> Option<Arc<Service>> {
self.services.read().unwrap().get(name).cloned()
}
/// Ajoute un sous-device.
///
/// # Errors
///
/// Retourne une erreur si un device avec le même nom existe déjà.
pub fn add_device(&self, device: Arc<Device>) -> Result<(), DeviceError> {
let mut devices = self.devices.write().unwrap();
let name = device.get_name().to_string();
if devices.contains_key(&name) {
return Err(DeviceError::DeviceAlreadyExists(name));
}
devices.insert(name, device);
Ok(())
}
/// Retourne tous les sous-devices.
pub fn devices(&self) -> Vec<Arc<Device>> {
self.devices.read().unwrap().values().cloned().collect()
}
/// Retourne le nom convivial.
pub fn friendly_name(&self) -> &str {
&self.friendly_name
}
/// Retourne le fabricant.
pub fn manufacturer(&self) -> &str {
&self.manufacturer
}
/// Retourne le nom du modèle.
pub fn model_name(&self) -> &str {
&self.model_name
}
/// Retourne la description du modèle.
pub fn model_description(&self) -> Option<&str> {
self.model_description.as_deref()
}
/// Retourne l'URL de présentation.
pub fn presentation_url(&self) -> Option<&str> {
self.presentation_url.as_deref()
}
/// Retourne l'URL du fabricant.
pub fn manufacturer_url(&self) -> Option<&str> {
self.manufacturer_url.as_deref()
}
/// Retourne le numéro du modèle.
pub fn model_number(&self) -> Option<&str> {
self.model_number.as_deref()
}
/// Retourne l'URL du modèle.
pub fn model_url(&self) -> Option<&str> {
self.model_url.as_deref()
}
/// Retourne le numéro de série.
pub fn serial_number(&self) -> Option<&str> {
self.serial_number.as_deref()
}
/// Retourne l'UPC.
pub fn upc(&self) -> Option<&str> {
self.upc.as_deref()
}
/// Retourne l'URL de l'icône.
pub fn icon_url(&self) -> Option<&str> {
self.icon_url.as_deref()
}
}
impl std::fmt::Display for Device {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "Device({}:{})", self.get_name(), self.version)
}
}
impl UpnpTyped for Device {
fn as_upnp_object_type(&self) -> &UpnpObjectType {
&self.object
}
}

View File

@@ -0,0 +1,330 @@
//! Implémentation de DeviceInstance.
use std::{
collections::HashMap,
sync::{Arc, RwLock},
};
use axum::{
http::StatusCode,
response::{IntoResponse, Response},
};
use tracing::info;
use xmltree::{Element, XMLNode, EmitterConfig};
use crate::{
devices::{Device, errors::DeviceError},
services::ServiceInstance,
UpnpObject, UpnpInstance, UpnpTyped, UpnpTypedInstance, UpnpObjectType,
};
/// Instance d'un device UPnP.
///
/// Représente une instance concrète d'un device UPnP, avec ses services instanciés
/// et son UDN unique.
pub struct DeviceInstance {
/// Métadonnées de l'objet
object: UpnpObjectType,
/// Référence vers le modèle
model: Arc<Device>,
/// UDN unique pour cette instance
udn: String,
/// URL de base du serveur
server_base_url: String,
/// Instances de services
services: RwLock<HashMap<String, Arc<ServiceInstance>>>,
/// Instances de sous-devices
devices: RwLock<HashMap<String, Arc<DeviceInstance>>>,
}
impl Clone for DeviceInstance {
fn clone(&self) -> Self {
Self {
object: self.object.clone(),
model: Arc::clone(&self.model),
udn: self.udn.clone(),
server_base_url: self.server_base_url.clone(),
services: RwLock::new(self.services.read().unwrap().clone()),
devices: RwLock::new(self.devices.read().unwrap().clone()),
}
}
}
impl std::fmt::Debug for DeviceInstance {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("DeviceInstance")
.field("object", &self.object)
.field("udn", &self.udn)
.field("server_base_url", &self.server_base_url)
.field("services", &self.services)
.field("devices", &self.devices)
.finish()
}
}
impl UpnpTyped for DeviceInstance {
fn as_upnp_object_type(&self) -> &UpnpObjectType {
&self.object
}
}
impl UpnpInstance for DeviceInstance {
type Model = Device;
fn new(model: &Device) -> Self {
// Obtenir ou créer un UDN persistant via la configuration
let device_name = model.get_name();
let udn = if let Ok(config_udn) = pmoconfig::get_config().get_device_udn("mediarenderer", device_name) {
config_udn
} else {
// Fallback : générer un UDN
tracing::warn!("Failed to get/save UDN from config, using generated UUID");
format!("uuid:{}_{}", model.udn_prefix(), uuid::Uuid::new_v4())
};
Self {
object: UpnpObjectType {
name: model.get_name().to_string(),
object_type: "DeviceInstance".to_string(),
},
model: Arc::new(model.clone()),
udn,
server_base_url: "http://localhost:8080".to_string(),
services: RwLock::new(HashMap::new()),
devices: RwLock::new(HashMap::new()),
}
}
}
impl UpnpTypedInstance for DeviceInstance {
fn get_model(&self) -> &Self::Model {
&self.model
}
}
impl UpnpObject for DeviceInstance {
fn to_xml_element(&self) -> Element {
let mut elem = Element::new("device");
// deviceType
let mut device_type = Element::new("deviceType");
device_type.children.push(XMLNode::Text(self.model.device_type()));
elem.children.push(XMLNode::Element(device_type));
// friendlyName
let mut friendly_name = Element::new("friendlyName");
friendly_name.children.push(XMLNode::Text(self.model.friendly_name().to_string()));
elem.children.push(XMLNode::Element(friendly_name));
// manufacturer
let mut manufacturer = Element::new("manufacturer");
manufacturer.children.push(XMLNode::Text(self.model.manufacturer().to_string()));
elem.children.push(XMLNode::Element(manufacturer));
// modelName
let mut model_name = Element::new("modelName");
model_name.children.push(XMLNode::Text(self.model.model_name().to_string()));
elem.children.push(XMLNode::Element(model_name));
// UDN
let mut udn = Element::new("UDN");
udn.children.push(XMLNode::Text(self.udn.clone()));
elem.children.push(XMLNode::Element(udn));
// serviceList
let services = self.services.read().unwrap();
if !services.is_empty() {
let mut service_list = Element::new("serviceList");
for service in services.values() {
service_list.children.push(XMLNode::Element(service.to_xml_element()));
}
elem.children.push(XMLNode::Element(service_list));
}
// deviceList (sous-devices)
let devices = self.devices.read().unwrap();
if !devices.is_empty() {
let mut device_list = Element::new("deviceList");
for device in devices.values() {
device_list.children.push(XMLNode::Element(device.to_xml_element()));
}
elem.children.push(XMLNode::Element(device_list));
}
// presentationURL
if let Some(url) = self.model.presentation_url() {
let mut presentation_url = Element::new("presentationURL");
presentation_url.children.push(XMLNode::Text(url.to_string()));
elem.children.push(XMLNode::Element(presentation_url));
}
elem
}
}
impl DeviceInstance {
/// Définit l'URL de base du serveur.
pub fn set_server_base_url(&mut self, url: String) {
self.server_base_url = url;
}
/// Retourne l'UDN du device.
pub fn udn(&self) -> &str {
&self.udn
}
/// Retourne l'URL de base du serveur (protocole + host + port).
pub fn base_url(&self) -> &str {
&self.server_base_url
}
/// Retourne la route du device (chemin relatif).
pub fn route(&self) -> String {
format!("/device/{}", self.get_name())
}
/// Retourne la route de description du device.
pub fn description_route(&self) -> String {
format!("{}/desc.xml", self.route())
}
/// Ajoute une instance de service au device.
///
/// Cette méthode configure automatiquement le service pour qu'il connaisse
/// son device parent.
///
/// # Errors
///
/// Retourne une erreur si un service avec le même nom existe déjà.
pub fn add_service(self: &Arc<Self>, service: Arc<ServiceInstance>) -> Result<(), DeviceError> {
let mut services = self.services.write().unwrap();
let name = service.get_name().to_string();
if services.contains_key(&name) {
return Err(DeviceError::ServiceAlreadyExists(name));
}
// Configurer le service pour qu'il connaisse son device parent
service.set_device(Arc::clone(self));
services.insert(name, service);
Ok(())
}
/// Retourne tous les services.
pub fn services(&self) -> Vec<Arc<ServiceInstance>> {
self.services.read().unwrap().values().cloned().collect()
}
/// Retourne un service par nom.
pub fn get_service(&self, name: &str) -> Option<Arc<ServiceInstance>> {
self.services.read().unwrap().get(name).cloned()
}
/// Ajoute une instance de sous-device.
pub fn add_device(&self, device: Arc<DeviceInstance>) -> Result<(), DeviceError> {
let mut devices = self.devices.write().unwrap();
let name = device.get_name().to_string();
if devices.contains_key(&name) {
return Err(DeviceError::DeviceAlreadyExists(name));
}
devices.insert(name, device);
Ok(())
}
/// Retourne tous les sous-devices.
pub fn devices(&self) -> Vec<Arc<DeviceInstance>> {
self.devices.read().unwrap().values().cloned().collect()
}
/// Enregistre toutes les URLs du device et de ses services dans le serveur.
pub fn register_urls<'a, S: crate::UpnpServer + ?Sized>(&'a self, server: &'a mut S) -> std::pin::Pin<Box<dyn std::future::Future<Output = Result<(), DeviceError>> + 'a>> {
Box::pin(async move {
info!(
"✅ Device description for {} available at: {}{}",
self.get_name(),
self.base_url(),
self.description_route(),
);
// Handler pour la description du device
let instance_desc = self.clone();
server.add_handler(&self.description_route(), move || {
let instance = instance_desc.clone();
async move { instance.description_handler().await }
}).await;
// Enregistrer les services
for service in self.services() {
service.register_urls(server).await
.map_err(|e| DeviceError::UrlRegistrationError(e.to_string()))?;
}
// Enregistrer les sous-devices
for device in self.devices() {
device.register_urls(server).await?;
}
Ok(())
})
}
/// Génère l'élément XML de description du device.
pub fn description_element(&self) -> Element {
let mut root = Element::new("root");
root.attributes.insert(
"xmlns".to_string(),
"urn:schemas-upnp-org:device-1-0".to_string(),
);
// specVersion
let mut spec = Element::new("specVersion");
let mut major = Element::new("major");
major.children.push(XMLNode::Text("1".to_string()));
spec.children.push(XMLNode::Element(major));
let mut minor = Element::new("minor");
minor.children.push(XMLNode::Text("0".to_string()));
spec.children.push(XMLNode::Element(minor));
root.children.push(XMLNode::Element(spec));
// device
root.children.push(XMLNode::Element(self.to_xml_element()));
root
}
/// Handler HTTP pour la description du device.
async fn description_handler(&self) -> Response {
let elem = self.description_element();
let config = EmitterConfig::new()
.perform_indent(true)
.indent_string(" ");
let mut xml_output = Vec::new();
if let Err(e) = elem.write_with_config(&mut xml_output, config) {
tracing::error!("Failed to serialize device description XML: {}", e);
return StatusCode::INTERNAL_SERVER_ERROR.into_response();
}
let mut xml = String::from_utf8_lossy(&xml_output).to_string();
// Ajouter l'en-tête XML
xml.insert_str(0, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
(
StatusCode::OK,
[(axum::http::header::CONTENT_TYPE, "text/xml; charset=\"utf-8\"")],
xml,
).into_response()
}
}

View File

@@ -0,0 +1,118 @@
//! Implémentation des traits UPnP pour Device.
use xmltree::{Element, XMLNode};
use crate::{
devices::{Device, DeviceInstance},
UpnpObject, UpnpModel,
};
impl UpnpObject for Device {
fn to_xml_element(&self) -> Element {
let mut elem = Element::new("device");
// deviceType
let mut device_type = Element::new("deviceType");
device_type.children.push(XMLNode::Text(self.device_type()));
elem.children.push(XMLNode::Element(device_type));
// friendlyName
let mut friendly_name = Element::new("friendlyName");
friendly_name.children.push(XMLNode::Text(self.friendly_name().to_string()));
elem.children.push(XMLNode::Element(friendly_name));
// manufacturer
let mut manufacturer = Element::new("manufacturer");
manufacturer.children.push(XMLNode::Text(self.manufacturer().to_string()));
elem.children.push(XMLNode::Element(manufacturer));
// manufacturerURL (optionnel)
if let Some(url) = self.manufacturer_url() {
let mut manufacturer_url = Element::new("manufacturerURL");
manufacturer_url.children.push(XMLNode::Text(url.to_string()));
elem.children.push(XMLNode::Element(manufacturer_url));
}
// modelDescription (optionnel)
if let Some(desc) = self.model_description() {
let mut model_description = Element::new("modelDescription");
model_description.children.push(XMLNode::Text(desc.to_string()));
elem.children.push(XMLNode::Element(model_description));
}
// modelName
let mut model_name = Element::new("modelName");
model_name.children.push(XMLNode::Text(self.model_name().to_string()));
elem.children.push(XMLNode::Element(model_name));
// modelNumber (optionnel)
if let Some(number) = self.model_number() {
let mut model_number = Element::new("modelNumber");
model_number.children.push(XMLNode::Text(number.to_string()));
elem.children.push(XMLNode::Element(model_number));
}
// modelURL (optionnel)
if let Some(url) = self.model_url() {
let mut model_url = Element::new("modelURL");
model_url.children.push(XMLNode::Text(url.to_string()));
elem.children.push(XMLNode::Element(model_url));
}
// serialNumber (optionnel)
if let Some(serial) = self.serial_number() {
let mut serial_number = Element::new("serialNumber");
serial_number.children.push(XMLNode::Text(serial.to_string()));
elem.children.push(XMLNode::Element(serial_number));
}
// UPC (optionnel)
if let Some(upc) = self.upc() {
let mut upc_elem = Element::new("UPC");
upc_elem.children.push(XMLNode::Text(upc.to_string()));
elem.children.push(XMLNode::Element(upc_elem));
}
// iconList (optionnel)
if let Some(icon_url) = self.icon_url() {
let mut icon_list = Element::new("iconList");
let mut icon = Element::new("icon");
let mut mimetype = Element::new("mimetype");
mimetype.children.push(XMLNode::Text("image/png".to_string()));
icon.children.push(XMLNode::Element(mimetype));
let mut width = Element::new("width");
width.children.push(XMLNode::Text("48".to_string()));
icon.children.push(XMLNode::Element(width));
let mut height = Element::new("height");
height.children.push(XMLNode::Text("48".to_string()));
icon.children.push(XMLNode::Element(height));
let mut depth = Element::new("depth");
depth.children.push(XMLNode::Text("24".to_string()));
icon.children.push(XMLNode::Element(depth));
let mut url = Element::new("url");
url.children.push(XMLNode::Text(icon_url.to_string()));
icon.children.push(XMLNode::Element(url));
icon_list.children.push(XMLNode::Element(icon));
elem.children.push(XMLNode::Element(icon_list));
}
// presentationURL (optionnel)
if let Some(url) = self.presentation_url() {
let mut presentation_url = Element::new("presentationURL");
presentation_url.children.push(XMLNode::Text(url.to_string()));
elem.children.push(XMLNode::Element(presentation_url));
}
elem
}
}
impl UpnpModel for Device {
type Instance = DeviceInstance;
}

View File

@@ -0,0 +1,23 @@
//! Erreurs relatives aux devices UPnP.
use thiserror::Error;
/// Erreurs liées aux devices UPnP.
#[derive(Error, Debug)]
pub enum DeviceError {
/// Service déjà existant
#[error("Service '{0}' already exists in device")]
ServiceAlreadyExists(String),
/// Device déjà existant
#[error("Device '{0}' already exists")]
DeviceAlreadyExists(String),
/// Version invalide
#[error("Device version must be > 0")]
InvalidVersion,
/// Erreur d'enregistrement d'URL
#[error("Failed to register URL: {0}")]
UrlRegistrationError(String),
}

View File

@@ -0,0 +1,40 @@
//! Module pour les devices UPnP.
//!
//! Ce module fournit les structures et fonctionnalites pour creer et gerer
//! des devices UPnP selon la specification UPnP Device Architecture.
//!
//! # Architecture
//!
//! - [`Device`] : Modele d'un device UPnP
//! - [`DeviceInstance`] : Instance concrete d'un device
//! - [`DeviceError`](errors::DeviceError) : Erreurs liees aux devices
//!
//! # Exemple
//!
//! ```ignore
//! use pmoupnp::devices::Device;
//! use pmoupnp::services::Service;
//! use std::sync::Arc;
//!
//! // Creer un device MediaRenderer
//! let mut device = Device::new(
//! "MediaRenderer".to_string(),
//! "MediaRenderer".to_string(),
//! "PMOMusic Renderer".to_string()
//! );
//!
//! // Ajouter des services
//! let avtransport = Arc::new(Service::new("AVTransport".to_string()));
//! device.add_service(avtransport).unwrap();
//!
//! // Creer une instance
//! let instance = device.create_instance();
//! ```
mod device;
mod device_instance;
mod device_methods;
pub mod errors;
pub use device::Device;
pub use device_instance::DeviceInstance;

View File

@@ -2,6 +2,7 @@ mod object_trait;
mod object_set;
pub mod actions;
pub mod devices;
pub mod mediarenderer;
pub mod server;
pub mod services;
@@ -9,6 +10,9 @@ pub mod state_variables;
pub mod value_ranges;
pub mod variable_types;
// Re-exports
pub use server::UpnpServer;
use std::{collections::HashMap, sync::Arc};

View File

@@ -0,0 +1,69 @@
//! Définition du device MediaRenderer.
use once_cell::sync::Lazy;
use std::sync::Arc;
use crate::{
devices::Device,
mediarenderer::{
avtransport::AVTTRANSPORT,
renderingcontrol::RENDERINGCONTROL,
connectionmanager::CONNECTIONMANAGER,
},
};
/// Device MediaRenderer UPnP.
///
/// MediaRenderer audio-only conforme UPnP AV Architecture 1.0.
///
/// # Services inclus
///
/// - **AVTransport:1** : Contrôle de la lecture
/// - **RenderingControl:1** : Contrôle du volume et du mute
/// - **ConnectionManager:1** : Gestion des connexions
///
/// # Spécifications
///
/// - Device Type : `urn:schemas-upnp-org:device:MediaRenderer:1`
/// - Version : 1
/// - Manufacturer : PMOMusic
/// - Model : PMOMusic Audio Renderer
///
/// # Exemple
///
/// ```ignore
/// use pmoupnp::mediarenderer::MEDIA_RENDERER;
/// use pmoupnp::UpnpModel;
///
/// // Créer une instance du renderer
/// let renderer_instance = MEDIA_RENDERER.create_instance();
///
/// // Accéder aux services
/// if let Some(avtransport) = renderer_instance.get_service("AVTransport") {
/// // Contrôler la lecture...
/// }
/// ```
pub static MEDIA_RENDERER: Lazy<Arc<Device>> = Lazy::new(|| {
let mut device = Device::new(
"MediaRenderer".to_string(),
"MediaRenderer".to_string(),
"PMOMusic Audio Renderer".to_string(),
);
device.set_manufacturer("PMOMusic".to_string());
device.set_model_name("PMOMusic Audio Renderer".to_string());
device.set_model_description("UPnP AV MediaRenderer for audio streaming".to_string());
device.set_udn_prefix("pmomusic".to_string());
// Ajouter les trois services obligatoires
device.add_service(Arc::clone(&AVTTRANSPORT))
.expect("Failed to add AVTransport service");
device.add_service(Arc::clone(&RENDERINGCONTROL))
.expect("Failed to add RenderingControl service");
device.add_service(Arc::clone(&CONNECTIONMANAGER))
.expect("Failed to add ConnectionManager service");
Arc::new(device)
});

View File

@@ -1,3 +1,35 @@
//! Module MediaRenderer UPnP.
//!
//! Ce module implémente un MediaRenderer UPnP audio-only conforme à la spécification
//! UPnP AV Architecture. Un MediaRenderer permet de recevoir et lire du contenu audio
//! depuis un serveur UPnP (MediaServer).
//!
//! # Architecture
//!
//! Le MediaRenderer est composé de trois services obligatoires :
//!
//! - **AVTransport** : Contrôle de la lecture (play, pause, stop, seek, etc.)
//! - **RenderingControl** : Contrôle du volume et du mute
//! - **ConnectionManager** : Gestion des connexions et des protocoles supportés
//!
//! # Device UPnP
//!
//! - Type : `urn:schemas-upnp-org:device:MediaRenderer:1`
//! - Services : AVTransport:1, RenderingControl:1, ConnectionManager:1
//!
//! # Utilisation
//!
//! ```ignore
//! use pmoupnp::mediarenderer::MEDIA_RENDERER;
//!
//! // Le device est déjà configuré avec tous ses services
//! let renderer = MEDIA_RENDERER.clone();
//! let instance = renderer.create_instance();
//! ```
pub mod avtransport;
pub mod connectionmanager;
pub mod renderingcontrol;
pub mod device;
pub use device::MEDIA_RENDERER;

262
pmoupnp/src/server.rs Normal file
View File

@@ -0,0 +1,262 @@
//! Trait pour les serveurs UPnP
//!
//! Ce module définit le trait [`UpnpServer`] qui permet de connecter
//! des devices UPnP à n'importe quelle implémentation de serveur web.
//!
//! ## Architecture
//!
//! Le trait `UpnpServer` définit une interface minimale permettant aux devices
//! et services UPnP d'enregistrer leurs endpoints HTTP sans dépendre d'une
//! implémentation de serveur spécifique.
//!
//! ## Séparation des responsabilités
//!
//! - **pmoupnp** : Définit le trait `UpnpServer` et l'utilise via des contraintes génériques
//! - **pmoserver** : Fournit une implémentation concrète basée sur Axum
//! - **Autres crates** : Peuvent fournir leurs propres implémentations (actix-web, warp, etc.)
//!
//! ## Exemple d'utilisation
//!
//! ```rust,no_run
//! use pmoupnp::{UpnpServer, devices::{Device, DeviceInstance}};
//! use std::sync::Arc;
//!
//! # async fn example<S: UpnpServer>(mut server: S) {
//! // Créer un device
//! let device = Device::new(
//! "MyDevice".to_string(),
//! "MyDeviceType".to_string(),
//! "Friendly Name".to_string(),
//! );
//! let device_instance = Arc::new(DeviceInstance::new(&device));
//!
//! // Le device enregistre automatiquement ses routes UPnP
//! device_instance.register_urls(&mut server).await;
//! # }
//! ```
//!
//! ## Implémentation
//!
//! Pour implémenter ce trait, votre serveur doit fournir trois méthodes
//! pour enregistrer des handlers HTTP asynchrones :
//!
//! ```rust,no_run
//! use pmoupnp::UpnpServer;
//! use std::future::Future;
//! use std::pin::Pin;
//!
//! struct MyServer {
//! // votre implémentation
//! }
//!
//! impl UpnpServer for MyServer {
//! fn add_handler<F, Fut>(&mut self, path: &str, handler: F)
//! -> Pin<Box<dyn Future<Output = ()> + Send + '_>>
//! where
//! F: Fn() -> Fut + Send + Sync + 'static + Clone,
//! Fut: Future<Output = pmoupnp::server::Response> + Send + 'static,
//! {
//! // Enregistrer le handler pour GET requests
//! # todo!()
//! }
//!
//! fn add_post_handler_with_state<S>(
//! &mut self,
//! path: &str,
//! handler: fn(axum::extract::State<S>, String)
//! -> Pin<Box<dyn Future<Output = pmoupnp::server::Response> + Send>>,
//! state: S,
//! ) -> Pin<Box<dyn Future<Output = ()> + Send + '_>>
//! where
//! S: Clone + Send + Sync + 'static,
//! {
//! // Enregistrer le handler pour POST avec body
//! # todo!()
//! }
//!
//! fn add_handler_with_state<S>(
//! &mut self,
//! path: &str,
//! handler: fn(axum::extract::State<S>,
//! pmoupnp::server::HeaderMap,
//! pmoupnp::server::Request)
//! -> Pin<Box<dyn Future<Output = pmoupnp::server::Response> + Send>>,
//! state: S,
//! ) -> Pin<Box<dyn Future<Output = ()> + Send + '_>>
//! where
//! S: Clone + Send + Sync + 'static,
//! {
//! // Enregistrer le handler avec accès complet à la requête
//! # todo!()
//! }
//! }
//! ```
use std::future::Future;
use std::pin::Pin;
/// Type alias pour la réponse HTTP (basé sur Axum).
///
/// Utilisé pour éviter une dépendance directe sur axum dans les signatures de trait,
/// tout en restant compatible avec les types Axum.
pub type Response = axum::response::Response;
/// Type alias pour les en-têtes HTTP (basé sur Axum).
pub type HeaderMap = axum::http::HeaderMap;
/// Type alias pour la requête HTTP (basé sur Axum).
pub type Request = axum::extract::Request<axum::body::Body>;
/// Trait pour les serveurs compatibles UPnP.
///
/// Ce trait définit l'interface minimale qu'un serveur web doit implémenter
/// pour supporter l'enregistrement automatique des endpoints UPnP par les
/// [`DeviceInstance`](crate::devices::DeviceInstance) et
/// [`ServiceInstance`](crate::services::ServiceInstance).
///
/// ## Contraintes
///
/// - `Send + Sync` : Le serveur doit être partageable entre threads
///
/// ## Méthodes
///
/// Les trois méthodes permettent d'enregistrer différents types de handlers :
///
/// 1. **`add_handler`** : Handler GET simple sans état
/// 2. **`add_post_handler_with_state`** : Handler POST avec état et body texte (pour SOAP)
/// 3. **`add_handler_with_state`** : Handler générique avec accès complet (pour SUBSCRIBE/UNSUBSCRIBE)
///
/// ## Implémentations
///
/// - **pmoserver::Server** : Implémentation basée sur Axum (fournie par la crate `pmoserver`)
pub trait UpnpServer: Send + Sync {
/// Ajoute un handler GET pour un chemin donné.
///
/// Utilisé principalement pour servir les descripteurs XML des devices et services.
///
/// # Arguments
///
/// * `path` - Le chemin HTTP (ex: `/device/MediaRenderer/description.xml`)
/// * `handler` - Une closure asynchrone qui génère la réponse
///
/// # Retour
///
/// Une future qui se résout quand le handler est enregistré.
///
/// # Exemple
///
/// ```rust,no_run
/// use pmoupnp::UpnpServer;
/// use axum::response::IntoResponse;
///
/// # async fn example<S: UpnpServer>(mut server: S) {
/// server.add_handler("/description.xml", || async {
/// "<?xml version=\"1.0\"?><root></root>".into_response()
/// }).await;
/// # }
/// ```
fn add_handler<F, Fut>(&mut self, path: &str, handler: F) -> Pin<Box<dyn Future<Output = ()> + Send + '_>>
where
F: Fn() -> Fut + Send + Sync + 'static + Clone,
Fut: Future<Output = Response> + Send + 'static;
/// Ajoute un handler POST avec état pour un chemin donné.
///
/// Utilisé pour les endpoints de contrôle SOAP des services UPnP.
///
/// # Arguments
///
/// * `path` - Le chemin HTTP (ex: `/service/AVTransport/control`)
/// * `handler` - Un pointeur de fonction qui traite la requête SOAP
/// * `state` - L'état partagé (typiquement une `ServiceInstance`)
///
/// # Retour
///
/// Une future qui se résout quand le handler est enregistré.
///
/// # Exemple
///
/// ```rust,no_run
/// use pmoupnp::{UpnpServer, server::Response};
/// use axum::extract::State;
/// use std::pin::Pin;
/// use std::future::Future;
///
/// fn soap_handler(
/// State(service): State<String>,
/// body: String,
/// ) -> Pin<Box<dyn Future<Output = Response> + Send>> {
/// Box::pin(async move {
/// // Traiter la requête SOAP
/// axum::response::Response::default()
/// })
/// }
///
/// # async fn example<S: UpnpServer>(mut server: S) {
/// server.add_post_handler_with_state(
/// "/control",
/// soap_handler,
/// "ServiceName".to_string(),
/// ).await;
/// # }
/// ```
fn add_post_handler_with_state<S>(
&mut self,
path: &str,
handler: fn(axum::extract::State<S>, String) -> Pin<Box<dyn Future<Output = Response> + Send>>,
state: S,
) -> Pin<Box<dyn Future<Output = ()> + Send + '_>>
where
S: Clone + Send + Sync + 'static;
/// Ajoute un handler avec état et accès complet à la requête.
///
/// Utilisé pour les endpoints d'événements (SUBSCRIBE/UNSUBSCRIBE) qui nécessitent
/// un accès aux en-têtes HTTP et à la méthode HTTP.
///
/// # Arguments
///
/// * `path` - Le chemin HTTP (ex: `/service/AVTransport/event`)
/// * `handler` - Un pointeur de fonction avec accès complet à la requête
/// * `state` - L'état partagé (typiquement une `ServiceInstance`)
///
/// # Retour
///
/// Une future qui se résout quand le handler est enregistré.
///
/// # Exemple
///
/// ```rust,no_run
/// use pmoupnp::{UpnpServer, server::{Response, HeaderMap, Request}};
/// use axum::extract::State;
/// use std::pin::Pin;
/// use std::future::Future;
///
/// fn event_handler(
/// State(service): State<String>,
/// headers: HeaderMap,
/// req: Request,
/// ) -> Pin<Box<dyn Future<Output = Response> + Send>> {
/// Box::pin(async move {
/// // Traiter SUBSCRIBE/UNSUBSCRIBE
/// axum::response::Response::default()
/// })
/// }
///
/// # async fn example<S: UpnpServer>(mut server: S) {
/// server.add_handler_with_state(
/// "/event",
/// event_handler,
/// "ServiceName".to_string(),
/// ).await;
/// # }
/// ```
fn add_handler_with_state<S>(
&mut self,
path: &str,
handler: fn(axum::extract::State<S>, HeaderMap, Request) -> Pin<Box<dyn Future<Output = Response> + Send>>,
state: S,
) -> Pin<Box<dyn Future<Output = ()> + Send + '_>>
where
S: Clone + Send + Sync + 'static;
}

View File

@@ -400,15 +400,15 @@ impl Service {
format!("/service/{}", self.name())
}
pub fn scpd_url(&self) -> String {
pub fn scpd_route(&self) -> String {
format!("{}/desc.xml", self.service_base_url())
}
pub fn control_url(&self) -> String {
pub fn control_route(&self) -> String {
format!("{}/control", self.service_base_url())
}
pub fn event_url(&self) -> String {
pub fn event_route(&self) -> String {
format!("{}/event", self.service_base_url())
}

View File

@@ -4,6 +4,8 @@ use std::{
collections::HashMap,
sync::{Arc, Mutex, RwLock},
time::Duration,
pin::Pin,
future::Future,
};
use axum::{
extract::{Request, State},
@@ -19,6 +21,7 @@ use crate::{
services::{Service, ServiceError},
actions::{ActionInstance, ActionInstanceSet},
state_variables::{StateVarInstance, StateVarInstanceSet, UpnpVariable},
devices::DeviceInstance,
UpnpObject, UpnpInstance, UpnpTyped, UpnpTypedInstance, UpnpObjectType,
};
@@ -74,8 +77,8 @@ pub struct ServiceInstance {
/// Identifiant du service
identifier: String,
/// Device parent (optionnel)
device: Option<Arc<DeviceStub>>,
/// Device parent (optionnel) - utilisé via interior mutability
device: Arc<RwLock<Option<Arc<DeviceInstance>>>>,
/// Variables d'état instanciées
statevariables: StateVarInstanceSet,
@@ -93,31 +96,6 @@ pub struct ServiceInstance {
seqid: Arc<Mutex<HashMap<String, u32>>>,
}
// Stub temporaire pour DeviceInstance
// TODO: Remplacer par la vraie implémentation quand le module devices sera créé
#[derive(Debug, Clone)]
pub struct DeviceStub {
name: String,
udn: String,
}
impl DeviceStub {
pub fn name(&self) -> &str {
&self.name
}
pub fn base_route(&self) -> String {
format!("/device/{}", self.name)
}
pub fn udn(&self) -> &str {
&self.udn
}
pub fn server_base_url(&self) -> String {
"http://localhost:8080".to_string()
}
}
impl std::fmt::Debug for ServiceInstance {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
@@ -195,7 +173,7 @@ impl UpnpInstance for ServiceInstance {
},
model: Arc::new(model.clone()),
identifier: model.identifier().to_string(),
device: None,
device: Arc::new(RwLock::new(None)),
statevariables,
actions,
subscribers: Arc::new(RwLock::new(HashMap::new())),
@@ -224,15 +202,15 @@ impl UpnpObject for ServiceInstance {
elem.children.push(XMLNode::Element(service_id));
let mut scpd_url = Element::new("SCPDURL");
scpd_url.children.push(XMLNode::Text(self.scpd_url()));
scpd_url.children.push(XMLNode::Text(self.scpd_route()));
elem.children.push(XMLNode::Element(scpd_url));
let mut control_url = Element::new("controlURL");
control_url.children.push(XMLNode::Text(self.control_url()));
control_url.children.push(XMLNode::Text(self.control_route()));
elem.children.push(XMLNode::Element(control_url));
let mut event_sub_url = Element::new("eventSubURL");
event_sub_url.children.push(XMLNode::Text(self.event_sub_url()));
event_sub_url.children.push(XMLNode::Text(self.event_route()));
elem.children.push(XMLNode::Element(event_sub_url));
elem
@@ -269,32 +247,43 @@ impl ServiceInstance {
self.actions.get_by_name(name)
}
/// Retourne la route de base du service.
pub fn base_route(&self) -> String {
match &self.device {
Some(device) => format!("{}/service/{}", device.base_route(), self.get_name()),
/// Définit le device parent pour ce service.
///
/// Cette méthode doit être appelée après la création du service instance
/// pour établir la relation avec le device parent.
pub fn set_device(&self, device: Arc<DeviceInstance>) {
let mut dev = self.device.write().unwrap();
*dev = Some(device);
}
/// Retourne la route du service (chemin relatif).
pub fn route(&self) -> String {
let device = self.device.read().unwrap();
match device.as_ref() {
Some(device) => format!("{}/service/{}", device.route(), self.get_name()),
None => format!("/service/{}", self.get_name()),
}
}
/// Retourne l'URL de contrôle SOAP.
pub fn control_url(&self) -> String {
format!("{}/control", self.base_route())
/// Retourne la route de contrôle SOAP.
pub fn control_route(&self) -> String {
format!("{}/control", self.route())
}
/// Retourne l'URL de souscription aux événements.
pub fn event_sub_url(&self) -> String {
format!("{}/event", self.base_route())
/// Retourne la route de souscription aux événements.
pub fn event_route(&self) -> String {
format!("{}/event", self.route())
}
/// Retourne l'URL de la description SCPD.
pub fn scpd_url(&self) -> String {
format!("{}/desc.xml", self.base_route())
/// Retourne la route de la description SCPD.
pub fn scpd_route(&self) -> String {
format!("{}/desc.xml", self.route())
}
/// Retourne l'USN (Unique Service Name).
pub fn usn(&self) -> String {
match &self.device {
let device = self.device.read().unwrap();
match device.as_ref() {
Some(device) => format!("uuid:{}::urn:{}", device.udn(), self.service_type()),
None => format!("uuid::urn:{}", self.service_type()),
}
@@ -310,23 +299,28 @@ impl ServiceInstance {
&self.actions
}
/// Enregistre les routes UPnP dans le serveur Axum.
/// Enregistre les routes UPnP dans le serveur.
///
/// # Errors
///
/// Retourne une erreur si l'enregistrement des routes échoue.
pub async fn register_urls(&self, server: &mut crate::server::Server) -> Result<(), ServiceError> {
pub async fn register_urls<S: crate::UpnpServer + ?Sized>(&self, server: &mut S) -> Result<(), ServiceError> {
let device = self.device.read().unwrap();
let device_name = device.as_ref().map(|d| d.get_name().clone()).unwrap_or_else(|| "unknown".to_string());
let server_url = device.as_ref().map(|d| d.base_url().to_string()).unwrap_or_default();
drop(device);
info!(
"✅ Service description for {}:{} available at : {}{}",
self.device.as_ref().map(|d| d.name()).unwrap_or("unknown"),
device_name,
self.get_name(),
self.device.as_ref().map(|d| d.server_base_url()).unwrap_or_default(),
self.scpd_url(),
server_url,
self.scpd_route(),
);
// Handler SCPD
let instance_scpd = self.clone();
server.add_handler(&self.scpd_url(), move || {
server.add_handler(&self.scpd_route(), move || {
let instance = instance_scpd.clone();
async move { instance.scpd_handler().await }
}).await;
@@ -334,7 +328,7 @@ impl ServiceInstance {
// Handler control
let instance_control = self.clone();
server.add_post_handler_with_state(
&self.control_url(),
&self.control_route(),
control_handler,
instance_control,
).await;
@@ -342,7 +336,7 @@ impl ServiceInstance {
// Handler événements
let instance_event = self.clone();
server.add_handler_with_state(
&self.event_sub_url(),
&self.event_route(),
event_sub_handler,
instance_event,
).await;
@@ -574,11 +568,12 @@ impl ServiceInstance {
}
/// Handler Axum pour les événements (SUBSCRIBE/UNSUBSCRIBE).
async fn event_sub_handler(
fn event_sub_handler(
State(instance): State<ServiceInstance>,
headers: HeaderMap,
req: Request<Body>,
) -> Response {
) -> Pin<Box<dyn Future<Output = Response> + Send>> {
Box::pin(async move {
info!("📡 Event Subscription request for {}", instance.get_name());
let method = req.method().as_str();
@@ -641,13 +636,15 @@ async fn event_sub_handler(
StatusCode::METHOD_NOT_ALLOWED.into_response()
}
}
})
}
/// Handler Axum pour le contrôle SOAP.
async fn control_handler(
fn control_handler(
State(instance): State<ServiceInstance>,
body: String,
) -> Response {
_body: String,
) -> Pin<Box<dyn Future<Output = Response> + Send>> {
Box::pin(async move {
info!("📡 Control request for {}", instance.get_name());
// TODO: Parser le SOAP et appeler l'action correspondante
@@ -669,6 +666,7 @@ async fn control_handler(
[(axum::http::header::CONTENT_TYPE, "text/xml; charset=\"utf-8\"")],
response_xml,
).into_response()
})
}
#[cfg(test)]
@@ -690,10 +688,10 @@ mod tests {
let service = Service::new("AVTransport".to_string());
let instance = ServiceInstance::new(&service);
assert_eq!(instance.base_route(), "/service/AVTransport");
assert_eq!(instance.control_url(), "/service/AVTransport/control");
assert_eq!(instance.event_sub_url(), "/service/AVTransport/event");
assert_eq!(instance.scpd_url(), "/service/AVTransport/desc.xml");
assert_eq!(instance.route(), "/service/AVTransport");
assert_eq!(instance.control_route(), "/service/AVTransport/control");
assert_eq!(instance.event_route(), "/service/AVTransport/event");
assert_eq!(instance.scpd_route(), "/service/AVTransport/desc.xml");
}
#[test]

View File

@@ -35,17 +35,17 @@ impl UpnpObject for Service {
// SCPDURL
let mut SCPDURL = Element::new("SCPDURL");
SCPDURL.children.push(XMLNode::Text(self.scpd_url()));
SCPDURL.children.push(XMLNode::Text(self.scpd_route()));
elem.children.push(XMLNode::Element(SCPDURL));
// controlURL
let mut controlURL = Element::new("controlURL");
controlURL.children.push(XMLNode::Text(self.control_url()));
controlURL.children.push(XMLNode::Text(self.control_route()));
elem.children.push(XMLNode::Element(controlURL));
// SCPDURL
// eventSubURL
let mut eventSubURL = Element::new("eventSubURL");
eventSubURL.children.push(XMLNode::Text(self.event_url()));
eventSubURL.children.push(XMLNode::Text(self.event_route()));
elem.children.push(XMLNode::Element(eventSubURL));
elem

View File

@@ -1 +0,0 @@
../esbuild/bin/esbuild

View File

@@ -1 +0,0 @@
../marked/bin/marked.js

View File

@@ -1 +0,0 @@
../nanoid/bin/nanoid.cjs

View File

@@ -1 +0,0 @@
../@babel/parser/bin/babel-parser.js

View File

@@ -1 +0,0 @@
../rollup/dist/bin/rollup

View File

@@ -1 +0,0 @@
../typescript/bin/tsc

View File

@@ -1 +0,0 @@
../typescript/bin/tsserver

View File

@@ -1 +0,0 @@
../vite/bin/vite.js

View File

@@ -1 +0,0 @@
../vue-tsc/bin/vue-tsc.js

File diff suppressed because it is too large Load Diff

View File

@@ -1 +0,0 @@
{"root":["../../src/main.ts","../../src/shims-vue.d.ts","../../src/router/index.ts","../../src/app.vue","../../src/components/helloworld.vue","../../src/components/logview.vue"],"version":"5.8.3"}

View File

@@ -1 +0,0 @@
{"root":["../../vite.config.ts"],"version":"5.8.3"}

View File

@@ -1,15 +0,0 @@
{
"hash": "55ffb9fd",
"configHash": "38b44685",
"lockfileHash": "67a231c1",
"browserHash": "0b84b257",
"optimized": {
"vue": {
"src": "../../vue/dist/vue.runtime.esm-bundler.js",
"file": "vue.js",
"fileHash": "6eecd6fc",
"needsInterop": false
}
},
"chunks": {}
}

View File

@@ -1,3 +0,0 @@
{
"type": "module"
}

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@@ -1,137 +0,0 @@
// @ts-nocheck
export {};
; declare global {
const __VLS_directiveBindingRestFields: { instance: null, oldValue: null, modifiers: any, dir: any };
const __VLS_unref: typeof import('vue').unref;
const __VLS_placeholder: any;
type __VLS_NativeElements = __VLS_SpreadMerge<SVGElementTagNameMap, HTMLElementTagNameMap>;
type __VLS_IntrinsicElements = import('vue/jsx-runtime').JSX.IntrinsicElements;
type __VLS_Element = import('vue/jsx-runtime').JSX.Element;
type __VLS_GlobalComponents = import('vue').GlobalComponents;
type __VLS_GlobalDirectives = import('vue').GlobalDirectives;
type __VLS_IsAny<T> = 0 extends 1 & T ? true : false;
type __VLS_PickNotAny<A, B> = __VLS_IsAny<A> extends true ? B : A;
type __VLS_SpreadMerge<A, B> = Omit<A, keyof B> & B;
type __VLS_WithComponent<N0 extends string, LocalComponents, Self, N1 extends string, N2 extends string, N3 extends string> =
N1 extends keyof LocalComponents ? { [K in N0]: LocalComponents[N1] } :
N2 extends keyof LocalComponents ? { [K in N0]: LocalComponents[N2] } :
N3 extends keyof LocalComponents ? { [K in N0]: LocalComponents[N3] } :
Self extends object ? { [K in N0]: Self } :
N1 extends keyof __VLS_GlobalComponents ? { [K in N0]: __VLS_GlobalComponents[N1] } :
N2 extends keyof __VLS_GlobalComponents ? { [K in N0]: __VLS_GlobalComponents[N2] } :
N3 extends keyof __VLS_GlobalComponents ? { [K in N0]: __VLS_GlobalComponents[N3] } :
{};
type __VLS_FunctionalComponentCtx<T, K> = __VLS_PickNotAny<'__ctx' extends keyof __VLS_PickNotAny<K, {}>
? K extends { __ctx?: infer Ctx } ? NonNullable<Ctx> : never : any
, T extends (props: any, ctx: infer Ctx) => any ? Ctx : any
>;
type __VLS_FunctionalComponentProps<T, K> = '__ctx' extends keyof __VLS_PickNotAny<K, {}>
? K extends { __ctx?: { props?: infer P } } ? NonNullable<P> : never
: T extends (props: infer P, ...args: any) => any ? P
: {};
type __VLS_FunctionalComponent<T> = (props: (T extends { $props: infer Props } ? Props : {}) & Record<string, unknown>, ctx?: any) => __VLS_Element & {
__ctx?: {
attrs?: any;
slots?: T extends { $slots: infer Slots } ? Slots : Record<string, any>;
emit?: T extends { $emit: infer Emit } ? Emit : {};
props?: (T extends { $props: infer Props } ? Props : {}) & Record<string, unknown>;
expose?: (exposed: T) => void;
};
};
type __VLS_IsFunction<T, K> = K extends keyof T
? __VLS_IsAny<T[K]> extends false
? unknown extends T[K]
? false
: true
: false
: false;
type __VLS_NormalizeComponentEvent<
Props,
Emits,
onEvent extends keyof Props,
Event extends keyof Emits,
CamelizedEvent extends keyof Emits,
> = __VLS_IsFunction<Props, onEvent> extends true
? Props
: __VLS_IsFunction<Emits, Event> extends true
? { [K in onEvent]?: Emits[Event] }
: __VLS_IsFunction<Emits, CamelizedEvent> extends true
? { [K in onEvent]?: Emits[CamelizedEvent] }
: Props;
// fix https://github.com/vuejs/language-tools/issues/926
type __VLS_UnionToIntersection<U> = (U extends unknown ? (arg: U) => unknown : never) extends ((arg: infer P) => unknown) ? P : never;
type __VLS_OverloadUnionInner<T, U = unknown> = U & T extends (...args: infer A) => infer R
? U extends T
? never
: __VLS_OverloadUnionInner<T, Pick<T, keyof T> & U & ((...args: A) => R)> | ((...args: A) => R)
: never;
type __VLS_OverloadUnion<T> = Exclude<
__VLS_OverloadUnionInner<(() => never) & T>,
T extends () => never ? never : () => never
>;
type __VLS_ConstructorOverloads<T> = __VLS_OverloadUnion<T> extends infer F
? F extends (event: infer E, ...args: infer A) => any
? { [K in E & string]: (...args: A) => void; }
: never
: never;
type __VLS_NormalizeEmits<T> = __VLS_PrettifyGlobal<
__VLS_UnionToIntersection<
__VLS_ConstructorOverloads<T> & {
[K in keyof T]: T[K] extends any[] ? { (...args: T[K]): void } : never
}
>
>;
type __VLS_EmitsToProps<T> = __VLS_PrettifyGlobal<{
[K in string & keyof T as `on${Capitalize<K>}`]?:
(...args: T[K] extends (...args: infer P) => any ? P : T[K] extends null ? any[] : never) => any;
}>;
type __VLS_ResolveEmits<
Comp,
Emits,
TypeEmits = {},
NormalizedEmits = __VLS_NormalizeEmits<Emits> extends infer E ? string extends keyof E ? {} : E : never,
> = __VLS_SpreadMerge<NormalizedEmits, TypeEmits>;
type __VLS_ResolveDirectives<T> = {
[K in keyof T & string as `v${Capitalize<K>}`]: T[K];
};
type __VLS_PrettifyGlobal<T> = { [K in keyof T as K]: T[K]; } & {};
type __VLS_WithDefaultsGlobal<P, D> = {
[K in keyof P as K extends keyof D ? K : never]-?: P[K];
} & {
[K in keyof P as K extends keyof D ? never : K]: P[K];
};
type __VLS_UseTemplateRef<T> = Readonly<import('vue').ShallowRef<T | null>>;
type __VLS_ProxyRefs<T> = import('vue').ShallowUnwrapRef<T>;
function __VLS_getVForSourceType<T extends number | string | any[] | Iterable<any>>(source: T): [
item: T extends number ? number
: T extends string ? string
: T extends any[] ? T[number]
: T extends Iterable<infer T1> ? T1
: any,
index: number,
][];
function __VLS_getVForSourceType<T>(source: T): [
item: T[keyof T],
key: keyof T,
index: number,
][];
function __VLS_getSlotParameters<S, D extends S>(slot: S, decl?: D):
D extends (...args: infer P) => any ? P : any[];
function __VLS_asFunctionalDirective<T>(dir: T): T extends import('vue').ObjectDirective
? NonNullable<T['created' | 'beforeMount' | 'mounted' | 'beforeUpdate' | 'updated' | 'beforeUnmount' | 'unmounted']>
: T extends (...args: any) => any
? T
: (arg1: unknown, arg2: unknown, arg3: unknown, arg4: unknown) => void;
function __VLS_asFunctionalComponent<T, K = T extends new (...args: any) => any ? InstanceType<T> : unknown>(t: T, instance?: K):
T extends new (...args: any) => any ? __VLS_FunctionalComponent<K>
: T extends () => any ? (props: {}, ctx?: any) => ReturnType<T>
: T extends (...args: any) => any ? T
: __VLS_FunctionalComponent<{}>;
function __VLS_functionalComponentArgsRest<T extends (...args: any) => any>(t: T): 2 extends Parameters<T>['length'] ? [any] : [];
function __VLS_asFunctionalElement<T>(tag: T, endTag?: T): (attrs: T & Record<string, unknown>) => void;
function __VLS_asFunctionalSlot<S>(slot: S): S extends () => infer R ? (props: {}) => R : NonNullable<S>;
function __VLS_tryAsConstant<const T>(t: T): T;
}

View File

@@ -1,137 +0,0 @@
// @ts-nocheck
export {};
; declare global {
const __VLS_directiveBindingRestFields: { instance: null, oldValue: null, modifiers: any, dir: any };
const __VLS_unref: typeof import('vue').unref;
const __VLS_placeholder: any;
type __VLS_NativeElements = __VLS_SpreadMerge<SVGElementTagNameMap, HTMLElementTagNameMap>;
type __VLS_IntrinsicElements = import('vue/jsx-runtime').JSX.IntrinsicElements;
type __VLS_Element = import('vue/jsx-runtime').JSX.Element;
type __VLS_GlobalComponents = import('vue').GlobalComponents;
type __VLS_GlobalDirectives = import('vue').GlobalDirectives;
type __VLS_IsAny<T> = 0 extends 1 & T ? true : false;
type __VLS_PickNotAny<A, B> = __VLS_IsAny<A> extends true ? B : A;
type __VLS_SpreadMerge<A, B> = Omit<A, keyof B> & B;
type __VLS_WithComponent<N0 extends string, LocalComponents, Self, N1 extends string, N2 extends string, N3 extends string> =
N1 extends keyof LocalComponents ? { [K in N0]: LocalComponents[N1] } :
N2 extends keyof LocalComponents ? { [K in N0]: LocalComponents[N2] } :
N3 extends keyof LocalComponents ? { [K in N0]: LocalComponents[N3] } :
Self extends object ? { [K in N0]: Self } :
N1 extends keyof __VLS_GlobalComponents ? { [K in N0]: __VLS_GlobalComponents[N1] } :
N2 extends keyof __VLS_GlobalComponents ? { [K in N0]: __VLS_GlobalComponents[N2] } :
N3 extends keyof __VLS_GlobalComponents ? { [K in N0]: __VLS_GlobalComponents[N3] } :
{};
type __VLS_FunctionalComponentCtx<T, K> = __VLS_PickNotAny<'__ctx' extends keyof __VLS_PickNotAny<K, {}>
? K extends { __ctx?: infer Ctx } ? NonNullable<Ctx> : never : any
, T extends (props: any, ctx: infer Ctx) => any ? Ctx : any
>;
type __VLS_FunctionalComponentProps<T, K> = '__ctx' extends keyof __VLS_PickNotAny<K, {}>
? K extends { __ctx?: { props?: infer P } } ? NonNullable<P> : never
: T extends (props: infer P, ...args: any) => any ? P
: {};
type __VLS_FunctionalComponent<T> = (props: (T extends { $props: infer Props } ? Props : {}) & Record<string, unknown>, ctx?: any) => __VLS_Element & {
__ctx?: {
attrs?: any;
slots?: T extends { $slots: infer Slots } ? Slots : Record<string, any>;
emit?: T extends { $emit: infer Emit } ? Emit : {};
props?: (T extends { $props: infer Props } ? Props : {}) & Record<string, unknown>;
expose?: (exposed: T) => void;
};
};
type __VLS_IsFunction<T, K> = K extends keyof T
? __VLS_IsAny<T[K]> extends false
? unknown extends T[K]
? false
: true
: false
: false;
type __VLS_NormalizeComponentEvent<
Props,
Emits,
onEvent extends keyof Props,
Event extends keyof Emits,
CamelizedEvent extends keyof Emits,
> = __VLS_IsFunction<Props, onEvent> extends true
? Props
: __VLS_IsFunction<Emits, Event> extends true
? { [K in onEvent]?: Emits[Event] }
: __VLS_IsFunction<Emits, CamelizedEvent> extends true
? { [K in onEvent]?: Emits[CamelizedEvent] }
: Props;
// fix https://github.com/vuejs/language-tools/issues/926
type __VLS_UnionToIntersection<U> = (U extends unknown ? (arg: U) => unknown : never) extends ((arg: infer P) => unknown) ? P : never;
type __VLS_OverloadUnionInner<T, U = unknown> = U & T extends (...args: infer A) => infer R
? U extends T
? never
: __VLS_OverloadUnionInner<T, Pick<T, keyof T> & U & ((...args: A) => R)> | ((...args: A) => R)
: never;
type __VLS_OverloadUnion<T> = Exclude<
__VLS_OverloadUnionInner<(() => never) & T>,
T extends () => never ? never : () => never
>;
type __VLS_ConstructorOverloads<T> = __VLS_OverloadUnion<T> extends infer F
? F extends (event: infer E, ...args: infer A) => any
? { [K in E & string]: (...args: A) => void; }
: never
: never;
type __VLS_NormalizeEmits<T> = __VLS_PrettifyGlobal<
__VLS_UnionToIntersection<
__VLS_ConstructorOverloads<T> & {
[K in keyof T]: T[K] extends any[] ? { (...args: T[K]): void } : never
}
>
>;
type __VLS_EmitsToProps<T> = __VLS_PrettifyGlobal<{
[K in string & keyof T as `on${Capitalize<K>}`]?:
(...args: T[K] extends (...args: infer P) => any ? P : T[K] extends null ? any[] : never) => any;
}>;
type __VLS_ResolveEmits<
Comp,
Emits,
TypeEmits = Comp extends { __typeEmits?: infer T } ? unknown extends T ? {} : import('vue').ShortEmitsToObject<T> : {},
NormalizedEmits = __VLS_NormalizeEmits<Emits> extends infer E ? string extends keyof E ? {} : E : never,
> = __VLS_SpreadMerge<NormalizedEmits, TypeEmits>;
type __VLS_ResolveDirectives<T> = {
[K in keyof T & string as `v${Capitalize<K>}`]: T[K];
};
type __VLS_PrettifyGlobal<T> = { [K in keyof T as K]: T[K]; } & {};
type __VLS_WithDefaultsGlobal<P, D> = {
[K in keyof P as K extends keyof D ? K : never]-?: P[K];
} & {
[K in keyof P as K extends keyof D ? never : K]: P[K];
};
type __VLS_UseTemplateRef<T> = Readonly<import('vue').ShallowRef<T | null>>;
type __VLS_ProxyRefs<T> = import('vue').ShallowUnwrapRef<T>;
function __VLS_getVForSourceType<T extends number | string | any[] | Iterable<any>>(source: T): [
item: T extends number ? number
: T extends string ? string
: T extends any[] ? T[number]
: T extends Iterable<infer T1> ? T1
: any,
index: number,
][];
function __VLS_getVForSourceType<T>(source: T): [
item: T[keyof T],
key: keyof T,
index: number,
][];
function __VLS_getSlotParameters<S, D extends S>(slot: S, decl?: D):
D extends (...args: infer P) => any ? P : any[];
function __VLS_asFunctionalDirective<T>(dir: T): T extends import('vue').ObjectDirective
? NonNullable<T['created' | 'beforeMount' | 'mounted' | 'beforeUpdate' | 'updated' | 'beforeUnmount' | 'unmounted']>
: T extends (...args: any) => any
? T
: (arg1: unknown, arg2: unknown, arg3: unknown, arg4: unknown) => void;
function __VLS_asFunctionalComponent<T, K = T extends new (...args: any) => any ? InstanceType<T> : unknown>(t: T, instance?: K):
T extends new (...args: any) => any ? __VLS_FunctionalComponent<K>
: T extends () => any ? (props: {}, ctx?: any) => ReturnType<T>
: T extends (...args: any) => any ? T
: __VLS_FunctionalComponent<{}>;
function __VLS_functionalComponentArgsRest<T extends (...args: any) => any>(t: T): 2 extends Parameters<T>['length'] ? [any] : [];
function __VLS_asFunctionalElement<T>(tag: T, endTag?: T): (attrs: T & Record<string, unknown>) => void;
function __VLS_asFunctionalSlot<S>(slot: S): S extends () => infer R ? (props: {}) => R : NonNullable<S>;
function __VLS_tryAsConstant<const T>(t: T): T;
}

View File

@@ -1,22 +0,0 @@
MIT License
Copyright (c) 2014-present Sebastian McKenzie and other contributors
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -1,19 +0,0 @@
# @babel/helper-string-parser
> A utility package to parse strings
See our website [@babel/helper-string-parser](https://babeljs.io/docs/babel-helper-string-parser) for more information.
## Install
Using npm:
```sh
npm install --save @babel/helper-string-parser
```
or using yarn:
```sh
yarn add @babel/helper-string-parser
```

View File

@@ -1,295 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.readCodePoint = readCodePoint;
exports.readInt = readInt;
exports.readStringContents = readStringContents;
var _isDigit = function isDigit(code) {
return code >= 48 && code <= 57;
};
const forbiddenNumericSeparatorSiblings = {
decBinOct: new Set([46, 66, 69, 79, 95, 98, 101, 111]),
hex: new Set([46, 88, 95, 120])
};
const isAllowedNumericSeparatorSibling = {
bin: ch => ch === 48 || ch === 49,
oct: ch => ch >= 48 && ch <= 55,
dec: ch => ch >= 48 && ch <= 57,
hex: ch => ch >= 48 && ch <= 57 || ch >= 65 && ch <= 70 || ch >= 97 && ch <= 102
};
function readStringContents(type, input, pos, lineStart, curLine, errors) {
const initialPos = pos;
const initialLineStart = lineStart;
const initialCurLine = curLine;
let out = "";
let firstInvalidLoc = null;
let chunkStart = pos;
const {
length
} = input;
for (;;) {
if (pos >= length) {
errors.unterminated(initialPos, initialLineStart, initialCurLine);
out += input.slice(chunkStart, pos);
break;
}
const ch = input.charCodeAt(pos);
if (isStringEnd(type, ch, input, pos)) {
out += input.slice(chunkStart, pos);
break;
}
if (ch === 92) {
out += input.slice(chunkStart, pos);
const res = readEscapedChar(input, pos, lineStart, curLine, type === "template", errors);
if (res.ch === null && !firstInvalidLoc) {
firstInvalidLoc = {
pos,
lineStart,
curLine
};
} else {
out += res.ch;
}
({
pos,
lineStart,
curLine
} = res);
chunkStart = pos;
} else if (ch === 8232 || ch === 8233) {
++pos;
++curLine;
lineStart = pos;
} else if (ch === 10 || ch === 13) {
if (type === "template") {
out += input.slice(chunkStart, pos) + "\n";
++pos;
if (ch === 13 && input.charCodeAt(pos) === 10) {
++pos;
}
++curLine;
chunkStart = lineStart = pos;
} else {
errors.unterminated(initialPos, initialLineStart, initialCurLine);
}
} else {
++pos;
}
}
return {
pos,
str: out,
firstInvalidLoc,
lineStart,
curLine,
containsInvalid: !!firstInvalidLoc
};
}
function isStringEnd(type, ch, input, pos) {
if (type === "template") {
return ch === 96 || ch === 36 && input.charCodeAt(pos + 1) === 123;
}
return ch === (type === "double" ? 34 : 39);
}
function readEscapedChar(input, pos, lineStart, curLine, inTemplate, errors) {
const throwOnInvalid = !inTemplate;
pos++;
const res = ch => ({
pos,
ch,
lineStart,
curLine
});
const ch = input.charCodeAt(pos++);
switch (ch) {
case 110:
return res("\n");
case 114:
return res("\r");
case 120:
{
let code;
({
code,
pos
} = readHexChar(input, pos, lineStart, curLine, 2, false, throwOnInvalid, errors));
return res(code === null ? null : String.fromCharCode(code));
}
case 117:
{
let code;
({
code,
pos
} = readCodePoint(input, pos, lineStart, curLine, throwOnInvalid, errors));
return res(code === null ? null : String.fromCodePoint(code));
}
case 116:
return res("\t");
case 98:
return res("\b");
case 118:
return res("\u000b");
case 102:
return res("\f");
case 13:
if (input.charCodeAt(pos) === 10) {
++pos;
}
case 10:
lineStart = pos;
++curLine;
case 8232:
case 8233:
return res("");
case 56:
case 57:
if (inTemplate) {
return res(null);
} else {
errors.strictNumericEscape(pos - 1, lineStart, curLine);
}
default:
if (ch >= 48 && ch <= 55) {
const startPos = pos - 1;
const match = /^[0-7]+/.exec(input.slice(startPos, pos + 2));
let octalStr = match[0];
let octal = parseInt(octalStr, 8);
if (octal > 255) {
octalStr = octalStr.slice(0, -1);
octal = parseInt(octalStr, 8);
}
pos += octalStr.length - 1;
const next = input.charCodeAt(pos);
if (octalStr !== "0" || next === 56 || next === 57) {
if (inTemplate) {
return res(null);
} else {
errors.strictNumericEscape(startPos, lineStart, curLine);
}
}
return res(String.fromCharCode(octal));
}
return res(String.fromCharCode(ch));
}
}
function readHexChar(input, pos, lineStart, curLine, len, forceLen, throwOnInvalid, errors) {
const initialPos = pos;
let n;
({
n,
pos
} = readInt(input, pos, lineStart, curLine, 16, len, forceLen, false, errors, !throwOnInvalid));
if (n === null) {
if (throwOnInvalid) {
errors.invalidEscapeSequence(initialPos, lineStart, curLine);
} else {
pos = initialPos - 1;
}
}
return {
code: n,
pos
};
}
function readInt(input, pos, lineStart, curLine, radix, len, forceLen, allowNumSeparator, errors, bailOnError) {
const start = pos;
const forbiddenSiblings = radix === 16 ? forbiddenNumericSeparatorSiblings.hex : forbiddenNumericSeparatorSiblings.decBinOct;
const isAllowedSibling = radix === 16 ? isAllowedNumericSeparatorSibling.hex : radix === 10 ? isAllowedNumericSeparatorSibling.dec : radix === 8 ? isAllowedNumericSeparatorSibling.oct : isAllowedNumericSeparatorSibling.bin;
let invalid = false;
let total = 0;
for (let i = 0, e = len == null ? Infinity : len; i < e; ++i) {
const code = input.charCodeAt(pos);
let val;
if (code === 95 && allowNumSeparator !== "bail") {
const prev = input.charCodeAt(pos - 1);
const next = input.charCodeAt(pos + 1);
if (!allowNumSeparator) {
if (bailOnError) return {
n: null,
pos
};
errors.numericSeparatorInEscapeSequence(pos, lineStart, curLine);
} else if (Number.isNaN(next) || !isAllowedSibling(next) || forbiddenSiblings.has(prev) || forbiddenSiblings.has(next)) {
if (bailOnError) return {
n: null,
pos
};
errors.unexpectedNumericSeparator(pos, lineStart, curLine);
}
++pos;
continue;
}
if (code >= 97) {
val = code - 97 + 10;
} else if (code >= 65) {
val = code - 65 + 10;
} else if (_isDigit(code)) {
val = code - 48;
} else {
val = Infinity;
}
if (val >= radix) {
if (val <= 9 && bailOnError) {
return {
n: null,
pos
};
} else if (val <= 9 && errors.invalidDigit(pos, lineStart, curLine, radix)) {
val = 0;
} else if (forceLen) {
val = 0;
invalid = true;
} else {
break;
}
}
++pos;
total = total * radix + val;
}
if (pos === start || len != null && pos - start !== len || invalid) {
return {
n: null,
pos
};
}
return {
n: total,
pos
};
}
function readCodePoint(input, pos, lineStart, curLine, throwOnInvalid, errors) {
const ch = input.charCodeAt(pos);
let code;
if (ch === 123) {
++pos;
({
code,
pos
} = readHexChar(input, pos, lineStart, curLine, input.indexOf("}", pos) - pos, true, throwOnInvalid, errors));
++pos;
if (code !== null && code > 0x10ffff) {
if (throwOnInvalid) {
errors.invalidCodePoint(pos, lineStart, curLine);
} else {
return {
code: null,
pos
};
}
}
} else {
({
code,
pos
} = readHexChar(input, pos, lineStart, curLine, 4, false, throwOnInvalid, errors));
}
return {
code,
pos
};
}
//# sourceMappingURL=index.js.map

File diff suppressed because one or more lines are too long

View File

@@ -1,31 +0,0 @@
{
"name": "@babel/helper-string-parser",
"version": "7.27.1",
"description": "A utility package to parse strings",
"repository": {
"type": "git",
"url": "https://github.com/babel/babel.git",
"directory": "packages/babel-helper-string-parser"
},
"homepage": "https://babel.dev/docs/en/next/babel-helper-string-parser",
"license": "MIT",
"publishConfig": {
"access": "public"
},
"main": "./lib/index.js",
"devDependencies": {
"charcodes": "^0.2.0"
},
"engines": {
"node": ">=6.9.0"
},
"author": "The Babel Team (https://babel.dev/team)",
"exports": {
".": {
"types": "./lib/index.d.ts",
"default": "./lib/index.js"
},
"./package.json": "./package.json"
},
"type": "commonjs"
}

View File

@@ -1,22 +0,0 @@
MIT License
Copyright (c) 2014-present Sebastian McKenzie and other contributors
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -1,19 +0,0 @@
# @babel/helper-validator-identifier
> Validate identifier/keywords name
See our website [@babel/helper-validator-identifier](https://babeljs.io/docs/babel-helper-validator-identifier) for more information.
## Install
Using npm:
```sh
npm install --save @babel/helper-validator-identifier
```
or using yarn:
```sh
yarn add @babel/helper-validator-identifier
```

View File

@@ -1,70 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.isIdentifierChar = isIdentifierChar;
exports.isIdentifierName = isIdentifierName;
exports.isIdentifierStart = isIdentifierStart;
let nonASCIIidentifierStartChars = "\xaa\xb5\xba\xc0-\xd6\xd8-\xf6\xf8-\u02c1\u02c6-\u02d1\u02e0-\u02e4\u02ec\u02ee\u0370-\u0374\u0376\u0377\u037a-\u037d\u037f\u0386\u0388-\u038a\u038c\u038e-\u03a1\u03a3-\u03f5\u03f7-\u0481\u048a-\u052f\u0531-\u0556\u0559\u0560-\u0588\u05d0-\u05ea\u05ef-\u05f2\u0620-\u064a\u066e\u066f\u0671-\u06d3\u06d5\u06e5\u06e6\u06ee\u06ef\u06fa-\u06fc\u06ff\u0710\u0712-\u072f\u074d-\u07a5\u07b1\u07ca-\u07ea\u07f4\u07f5\u07fa\u0800-\u0815\u081a\u0824\u0828\u0840-\u0858\u0860-\u086a\u0870-\u0887\u0889-\u088e\u08a0-\u08c9\u0904-\u0939\u093d\u0950\u0958-\u0961\u0971-\u0980\u0985-\u098c\u098f\u0990\u0993-\u09a8\u09aa-\u09b0\u09b2\u09b6-\u09b9\u09bd\u09ce\u09dc\u09dd\u09df-\u09e1\u09f0\u09f1\u09fc\u0a05-\u0a0a\u0a0f\u0a10\u0a13-\u0a28\u0a2a-\u0a30\u0a32\u0a33\u0a35\u0a36\u0a38\u0a39\u0a59-\u0a5c\u0a5e\u0a72-\u0a74\u0a85-\u0a8d\u0a8f-\u0a91\u0a93-\u0aa8\u0aaa-\u0ab0\u0ab2\u0ab3\u0ab5-\u0ab9\u0abd\u0ad0\u0ae0\u0ae1\u0af9\u0b05-\u0b0c\u0b0f\u0b10\u0b13-\u0b28\u0b2a-\u0b30\u0b32\u0b33\u0b35-\u0b39\u0b3d\u0b5c\u0b5d\u0b5f-\u0b61\u0b71\u0b83\u0b85-\u0b8a\u0b8e-\u0b90\u0b92-\u0b95\u0b99\u0b9a\u0b9c\u0b9e\u0b9f\u0ba3\u0ba4\u0ba8-\u0baa\u0bae-\u0bb9\u0bd0\u0c05-\u0c0c\u0c0e-\u0c10\u0c12-\u0c28\u0c2a-\u0c39\u0c3d\u0c58-\u0c5a\u0c5d\u0c60\u0c61\u0c80\u0c85-\u0c8c\u0c8e-\u0c90\u0c92-\u0ca8\u0caa-\u0cb3\u0cb5-\u0cb9\u0cbd\u0cdd\u0cde\u0ce0\u0ce1\u0cf1\u0cf2\u0d04-\u0d0c\u0d0e-\u0d10\u0d12-\u0d3a\u0d3d\u0d4e\u0d54-\u0d56\u0d5f-\u0d61\u0d7a-\u0d7f\u0d85-\u0d96\u0d9a-\u0db1\u0db3-\u0dbb\u0dbd\u0dc0-\u0dc6\u0e01-\u0e30\u0e32\u0e33\u0e40-\u0e46\u0e81\u0e82\u0e84\u0e86-\u0e8a\u0e8c-\u0ea3\u0ea5\u0ea7-\u0eb0\u0eb2\u0eb3\u0ebd\u0ec0-\u0ec4\u0ec6\u0edc-\u0edf\u0f00\u0f40-\u0f47\u0f49-\u0f6c\u0f88-\u0f8c\u1000-\u102a\u103f\u1050-\u1055\u105a-\u105d\u1061\u1065\u1066\u106e-\u1070\u1075-\u1081\u108e\u10a0-\u10c5\u10c7\u10cd\u10d0-\u10fa\u10fc-\u1248\u124a-\u124d\u1250-\u1256\u1258\u125a-\u125d\u1260-\u1288\u128a-\u128d\u1290-\u12b0\u12b2-\u12b5\u12b8-\u12be\u12c0\u12c2-\u12c5\u12c8-\u12d6\u12d8-\u1310\u1312-\u1315\u1318-\u135a\u1380-\u138f\u13a0-\u13f5\u13f8-\u13fd\u1401-\u166c\u166f-\u167f\u1681-\u169a\u16a0-\u16ea\u16ee-\u16f8\u1700-\u1711\u171f-\u1731\u1740-\u1751\u1760-\u176c\u176e-\u1770\u1780-\u17b3\u17d7\u17dc\u1820-\u1878\u1880-\u18a8\u18aa\u18b0-\u18f5\u1900-\u191e\u1950-\u196d\u1970-\u1974\u1980-\u19ab\u19b0-\u19c9\u1a00-\u1a16\u1a20-\u1a54\u1aa7\u1b05-\u1b33\u1b45-\u1b4c\u1b83-\u1ba0\u1bae\u1baf\u1bba-\u1be5\u1c00-\u1c23\u1c4d-\u1c4f\u1c5a-\u1c7d\u1c80-\u1c8a\u1c90-\u1cba\u1cbd-\u1cbf\u1ce9-\u1cec\u1cee-\u1cf3\u1cf5\u1cf6\u1cfa\u1d00-\u1dbf\u1e00-\u1f15\u1f18-\u1f1d\u1f20-\u1f45\u1f48-\u1f4d\u1f50-\u1f57\u1f59\u1f5b\u1f5d\u1f5f-\u1f7d\u1f80-\u1fb4\u1fb6-\u1fbc\u1fbe\u1fc2-\u1fc4\u1fc6-\u1fcc\u1fd0-\u1fd3\u1fd6-\u1fdb\u1fe0-\u1fec\u1ff2-\u1ff4\u1ff6-\u1ffc\u2071\u207f\u2090-\u209c\u2102\u2107\u210a-\u2113\u2115\u2118-\u211d\u2124\u2126\u2128\u212a-\u2139\u213c-\u213f\u2145-\u2149\u214e\u2160-\u2188\u2c00-\u2ce4\u2ceb-\u2cee\u2cf2\u2cf3\u2d00-\u2d25\u2d27\u2d2d\u2d30-\u2d67\u2d6f\u2d80-\u2d96\u2da0-\u2da6\u2da8-\u2dae\u2db0-\u2db6\u2db8-\u2dbe\u2dc0-\u2dc6\u2dc8-\u2dce\u2dd0-\u2dd6\u2dd8-\u2dde\u3005-\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303c\u3041-\u3096\u309b-\u309f\u30a1-\u30fa\u30fc-\u30ff\u3105-\u312f\u3131-\u318e\u31a0-\u31bf\u31f0-\u31ff\u3400-\u4dbf\u4e00-\ua48c\ua4d0-\ua4fd\ua500-\ua60c\ua610-\ua61f\ua62a\ua62b\ua640-\ua66e\ua67f-\ua69d\ua6a0-\ua6ef\ua717-\ua71f\ua722-\ua788\ua78b-\ua7cd\ua7d0\ua7d1\ua7d3\ua7d5-\ua7dc\ua7f2-\ua801\ua803-\ua805\ua807-\ua80a\ua80c-\ua822\ua840-\ua873\ua882-\ua8b3\ua8f2-\ua8f7\ua8fb\ua8fd\ua8fe\ua90a-\ua925\ua930-\ua946\ua960-\ua97c\ua984-\ua9b2\ua9cf\ua9e0-\ua9e4\ua9e6-\ua9ef\ua9fa-\ua9fe\uaa00-\uaa28\uaa40-\uaa42\uaa44-\uaa4b\uaa60-\uaa76\uaa7a\uaa7e-\uaaaf\uaab1\uaab5\uaab6\uaab9-\uaabd\uaac0\uaac2\uaadb-\uaadd\uaae0-\uaaea\uaaf2-\uaaf4\uab01-\uab06\uab09-\uab0e\uab11-\uab16\uab20-\uab26\uab28-\uab2e\uab30-\uab5a\uab5c-\uab69\uab70-\uabe2\uac00-\ud7a3\ud7b0-\ud7c6\ud7cb-\ud7fb\uf900-\ufa6d\ufa70-\ufad9\ufb00-\ufb06\ufb13-\ufb17\ufb1d\ufb1f-\ufb28\ufb2a-\ufb36\ufb38-\ufb3c\ufb3e\ufb40\ufb41\ufb43\ufb44\ufb46-\ufbb1\ufbd3-\ufd3d\ufd50-\ufd8f\ufd92-\ufdc7\ufdf0-\ufdfb\ufe70-\ufe74\ufe76-\ufefc\uff21-\uff3a\uff41-\uff5a\uff66-\uffbe\uffc2-\uffc7\uffca-\uffcf\uffd2-\uffd7\uffda-\uffdc";
let nonASCIIidentifierChars = "\xb7\u0300-\u036f\u0387\u0483-\u0487\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u0610-\u061a\u064b-\u0669\u0670\u06d6-\u06dc\u06df-\u06e4\u06e7\u06e8\u06ea-\u06ed\u06f0-\u06f9\u0711\u0730-\u074a\u07a6-\u07b0\u07c0-\u07c9\u07eb-\u07f3\u07fd\u0816-\u0819\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0859-\u085b\u0897-\u089f\u08ca-\u08e1\u08e3-\u0903\u093a-\u093c\u093e-\u094f\u0951-\u0957\u0962\u0963\u0966-\u096f\u0981-\u0983\u09bc\u09be-\u09c4\u09c7\u09c8\u09cb-\u09cd\u09d7\u09e2\u09e3\u09e6-\u09ef\u09fe\u0a01-\u0a03\u0a3c\u0a3e-\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a66-\u0a71\u0a75\u0a81-\u0a83\u0abc\u0abe-\u0ac5\u0ac7-\u0ac9\u0acb-\u0acd\u0ae2\u0ae3\u0ae6-\u0aef\u0afa-\u0aff\u0b01-\u0b03\u0b3c\u0b3e-\u0b44\u0b47\u0b48\u0b4b-\u0b4d\u0b55-\u0b57\u0b62\u0b63\u0b66-\u0b6f\u0b82\u0bbe-\u0bc2\u0bc6-\u0bc8\u0bca-\u0bcd\u0bd7\u0be6-\u0bef\u0c00-\u0c04\u0c3c\u0c3e-\u0c44\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c62\u0c63\u0c66-\u0c6f\u0c81-\u0c83\u0cbc\u0cbe-\u0cc4\u0cc6-\u0cc8\u0cca-\u0ccd\u0cd5\u0cd6\u0ce2\u0ce3\u0ce6-\u0cef\u0cf3\u0d00-\u0d03\u0d3b\u0d3c\u0d3e-\u0d44\u0d46-\u0d48\u0d4a-\u0d4d\u0d57\u0d62\u0d63\u0d66-\u0d6f\u0d81-\u0d83\u0dca\u0dcf-\u0dd4\u0dd6\u0dd8-\u0ddf\u0de6-\u0def\u0df2\u0df3\u0e31\u0e34-\u0e3a\u0e47-\u0e4e\u0e50-\u0e59\u0eb1\u0eb4-\u0ebc\u0ec8-\u0ece\u0ed0-\u0ed9\u0f18\u0f19\u0f20-\u0f29\u0f35\u0f37\u0f39\u0f3e\u0f3f\u0f71-\u0f84\u0f86\u0f87\u0f8d-\u0f97\u0f99-\u0fbc\u0fc6\u102b-\u103e\u1040-\u1049\u1056-\u1059\u105e-\u1060\u1062-\u1064\u1067-\u106d\u1071-\u1074\u1082-\u108d\u108f-\u109d\u135d-\u135f\u1369-\u1371\u1712-\u1715\u1732-\u1734\u1752\u1753\u1772\u1773\u17b4-\u17d3\u17dd\u17e0-\u17e9\u180b-\u180d\u180f-\u1819\u18a9\u1920-\u192b\u1930-\u193b\u1946-\u194f\u19d0-\u19da\u1a17-\u1a1b\u1a55-\u1a5e\u1a60-\u1a7c\u1a7f-\u1a89\u1a90-\u1a99\u1ab0-\u1abd\u1abf-\u1ace\u1b00-\u1b04\u1b34-\u1b44\u1b50-\u1b59\u1b6b-\u1b73\u1b80-\u1b82\u1ba1-\u1bad\u1bb0-\u1bb9\u1be6-\u1bf3\u1c24-\u1c37\u1c40-\u1c49\u1c50-\u1c59\u1cd0-\u1cd2\u1cd4-\u1ce8\u1ced\u1cf4\u1cf7-\u1cf9\u1dc0-\u1dff\u200c\u200d\u203f\u2040\u2054\u20d0-\u20dc\u20e1\u20e5-\u20f0\u2cef-\u2cf1\u2d7f\u2de0-\u2dff\u302a-\u302f\u3099\u309a\u30fb\ua620-\ua629\ua66f\ua674-\ua67d\ua69e\ua69f\ua6f0\ua6f1\ua802\ua806\ua80b\ua823-\ua827\ua82c\ua880\ua881\ua8b4-\ua8c5\ua8d0-\ua8d9\ua8e0-\ua8f1\ua8ff-\ua909\ua926-\ua92d\ua947-\ua953\ua980-\ua983\ua9b3-\ua9c0\ua9d0-\ua9d9\ua9e5\ua9f0-\ua9f9\uaa29-\uaa36\uaa43\uaa4c\uaa4d\uaa50-\uaa59\uaa7b-\uaa7d\uaab0\uaab2-\uaab4\uaab7\uaab8\uaabe\uaabf\uaac1\uaaeb-\uaaef\uaaf5\uaaf6\uabe3-\uabea\uabec\uabed\uabf0-\uabf9\ufb1e\ufe00-\ufe0f\ufe20-\ufe2f\ufe33\ufe34\ufe4d-\ufe4f\uff10-\uff19\uff3f\uff65";
const nonASCIIidentifierStart = new RegExp("[" + nonASCIIidentifierStartChars + "]");
const nonASCIIidentifier = new RegExp("[" + nonASCIIidentifierStartChars + nonASCIIidentifierChars + "]");
nonASCIIidentifierStartChars = nonASCIIidentifierChars = null;
const astralIdentifierStartCodes = [0, 11, 2, 25, 2, 18, 2, 1, 2, 14, 3, 13, 35, 122, 70, 52, 268, 28, 4, 48, 48, 31, 14, 29, 6, 37, 11, 29, 3, 35, 5, 7, 2, 4, 43, 157, 19, 35, 5, 35, 5, 39, 9, 51, 13, 10, 2, 14, 2, 6, 2, 1, 2, 10, 2, 14, 2, 6, 2, 1, 4, 51, 13, 310, 10, 21, 11, 7, 25, 5, 2, 41, 2, 8, 70, 5, 3, 0, 2, 43, 2, 1, 4, 0, 3, 22, 11, 22, 10, 30, 66, 18, 2, 1, 11, 21, 11, 25, 71, 55, 7, 1, 65, 0, 16, 3, 2, 2, 2, 28, 43, 28, 4, 28, 36, 7, 2, 27, 28, 53, 11, 21, 11, 18, 14, 17, 111, 72, 56, 50, 14, 50, 14, 35, 39, 27, 10, 22, 251, 41, 7, 1, 17, 2, 60, 28, 11, 0, 9, 21, 43, 17, 47, 20, 28, 22, 13, 52, 58, 1, 3, 0, 14, 44, 33, 24, 27, 35, 30, 0, 3, 0, 9, 34, 4, 0, 13, 47, 15, 3, 22, 0, 2, 0, 36, 17, 2, 24, 20, 1, 64, 6, 2, 0, 2, 3, 2, 14, 2, 9, 8, 46, 39, 7, 3, 1, 3, 21, 2, 6, 2, 1, 2, 4, 4, 0, 19, 0, 13, 4, 31, 9, 2, 0, 3, 0, 2, 37, 2, 0, 26, 0, 2, 0, 45, 52, 19, 3, 21, 2, 31, 47, 21, 1, 2, 0, 185, 46, 42, 3, 37, 47, 21, 0, 60, 42, 14, 0, 72, 26, 38, 6, 186, 43, 117, 63, 32, 7, 3, 0, 3, 7, 2, 1, 2, 23, 16, 0, 2, 0, 95, 7, 3, 38, 17, 0, 2, 0, 29, 0, 11, 39, 8, 0, 22, 0, 12, 45, 20, 0, 19, 72, 200, 32, 32, 8, 2, 36, 18, 0, 50, 29, 113, 6, 2, 1, 2, 37, 22, 0, 26, 5, 2, 1, 2, 31, 15, 0, 328, 18, 16, 0, 2, 12, 2, 33, 125, 0, 80, 921, 103, 110, 18, 195, 2637, 96, 16, 1071, 18, 5, 26, 3994, 6, 582, 6842, 29, 1763, 568, 8, 30, 18, 78, 18, 29, 19, 47, 17, 3, 32, 20, 6, 18, 433, 44, 212, 63, 129, 74, 6, 0, 67, 12, 65, 1, 2, 0, 29, 6135, 9, 1237, 42, 9, 8936, 3, 2, 6, 2, 1, 2, 290, 16, 0, 30, 2, 3, 0, 15, 3, 9, 395, 2309, 106, 6, 12, 4, 8, 8, 9, 5991, 84, 2, 70, 2, 1, 3, 0, 3, 1, 3, 3, 2, 11, 2, 0, 2, 6, 2, 64, 2, 3, 3, 7, 2, 6, 2, 27, 2, 3, 2, 4, 2, 0, 4, 6, 2, 339, 3, 24, 2, 24, 2, 30, 2, 24, 2, 30, 2, 24, 2, 30, 2, 24, 2, 30, 2, 24, 2, 7, 1845, 30, 7, 5, 262, 61, 147, 44, 11, 6, 17, 0, 322, 29, 19, 43, 485, 27, 229, 29, 3, 0, 496, 6, 2, 3, 2, 1, 2, 14, 2, 196, 60, 67, 8, 0, 1205, 3, 2, 26, 2, 1, 2, 0, 3, 0, 2, 9, 2, 3, 2, 0, 2, 0, 7, 0, 5, 0, 2, 0, 2, 0, 2, 2, 2, 1, 2, 0, 3, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 1, 2, 0, 3, 3, 2, 6, 2, 3, 2, 3, 2, 0, 2, 9, 2, 16, 6, 2, 2, 4, 2, 16, 4421, 42719, 33, 4153, 7, 221, 3, 5761, 15, 7472, 16, 621, 2467, 541, 1507, 4938, 6, 4191];
const astralIdentifierCodes = [509, 0, 227, 0, 150, 4, 294, 9, 1368, 2, 2, 1, 6, 3, 41, 2, 5, 0, 166, 1, 574, 3, 9, 9, 7, 9, 32, 4, 318, 1, 80, 3, 71, 10, 50, 3, 123, 2, 54, 14, 32, 10, 3, 1, 11, 3, 46, 10, 8, 0, 46, 9, 7, 2, 37, 13, 2, 9, 6, 1, 45, 0, 13, 2, 49, 13, 9, 3, 2, 11, 83, 11, 7, 0, 3, 0, 158, 11, 6, 9, 7, 3, 56, 1, 2, 6, 3, 1, 3, 2, 10, 0, 11, 1, 3, 6, 4, 4, 68, 8, 2, 0, 3, 0, 2, 3, 2, 4, 2, 0, 15, 1, 83, 17, 10, 9, 5, 0, 82, 19, 13, 9, 214, 6, 3, 8, 28, 1, 83, 16, 16, 9, 82, 12, 9, 9, 7, 19, 58, 14, 5, 9, 243, 14, 166, 9, 71, 5, 2, 1, 3, 3, 2, 0, 2, 1, 13, 9, 120, 6, 3, 6, 4, 0, 29, 9, 41, 6, 2, 3, 9, 0, 10, 10, 47, 15, 343, 9, 54, 7, 2, 7, 17, 9, 57, 21, 2, 13, 123, 5, 4, 0, 2, 1, 2, 6, 2, 0, 9, 9, 49, 4, 2, 1, 2, 4, 9, 9, 330, 3, 10, 1, 2, 0, 49, 6, 4, 4, 14, 10, 5350, 0, 7, 14, 11465, 27, 2343, 9, 87, 9, 39, 4, 60, 6, 26, 9, 535, 9, 470, 0, 2, 54, 8, 3, 82, 0, 12, 1, 19628, 1, 4178, 9, 519, 45, 3, 22, 543, 4, 4, 5, 9, 7, 3, 6, 31, 3, 149, 2, 1418, 49, 513, 54, 5, 49, 9, 0, 15, 0, 23, 4, 2, 14, 1361, 6, 2, 16, 3, 6, 2, 1, 2, 4, 101, 0, 161, 6, 10, 9, 357, 0, 62, 13, 499, 13, 245, 1, 2, 9, 726, 6, 110, 6, 6, 9, 4759, 9, 787719, 239];
function isInAstralSet(code, set) {
let pos = 0x10000;
for (let i = 0, length = set.length; i < length; i += 2) {
pos += set[i];
if (pos > code) return false;
pos += set[i + 1];
if (pos >= code) return true;
}
return false;
}
function isIdentifierStart(code) {
if (code < 65) return code === 36;
if (code <= 90) return true;
if (code < 97) return code === 95;
if (code <= 122) return true;
if (code <= 0xffff) {
return code >= 0xaa && nonASCIIidentifierStart.test(String.fromCharCode(code));
}
return isInAstralSet(code, astralIdentifierStartCodes);
}
function isIdentifierChar(code) {
if (code < 48) return code === 36;
if (code < 58) return true;
if (code < 65) return false;
if (code <= 90) return true;
if (code < 97) return code === 95;
if (code <= 122) return true;
if (code <= 0xffff) {
return code >= 0xaa && nonASCIIidentifier.test(String.fromCharCode(code));
}
return isInAstralSet(code, astralIdentifierStartCodes) || isInAstralSet(code, astralIdentifierCodes);
}
function isIdentifierName(name) {
let isFirst = true;
for (let i = 0; i < name.length; i++) {
let cp = name.charCodeAt(i);
if ((cp & 0xfc00) === 0xd800 && i + 1 < name.length) {
const trail = name.charCodeAt(++i);
if ((trail & 0xfc00) === 0xdc00) {
cp = 0x10000 + ((cp & 0x3ff) << 10) + (trail & 0x3ff);
}
}
if (isFirst) {
isFirst = false;
if (!isIdentifierStart(cp)) {
return false;
}
} else if (!isIdentifierChar(cp)) {
return false;
}
}
return !isFirst;
}
//# sourceMappingURL=identifier.js.map

File diff suppressed because one or more lines are too long

View File

@@ -1,57 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
Object.defineProperty(exports, "isIdentifierChar", {
enumerable: true,
get: function () {
return _identifier.isIdentifierChar;
}
});
Object.defineProperty(exports, "isIdentifierName", {
enumerable: true,
get: function () {
return _identifier.isIdentifierName;
}
});
Object.defineProperty(exports, "isIdentifierStart", {
enumerable: true,
get: function () {
return _identifier.isIdentifierStart;
}
});
Object.defineProperty(exports, "isKeyword", {
enumerable: true,
get: function () {
return _keyword.isKeyword;
}
});
Object.defineProperty(exports, "isReservedWord", {
enumerable: true,
get: function () {
return _keyword.isReservedWord;
}
});
Object.defineProperty(exports, "isStrictBindOnlyReservedWord", {
enumerable: true,
get: function () {
return _keyword.isStrictBindOnlyReservedWord;
}
});
Object.defineProperty(exports, "isStrictBindReservedWord", {
enumerable: true,
get: function () {
return _keyword.isStrictBindReservedWord;
}
});
Object.defineProperty(exports, "isStrictReservedWord", {
enumerable: true,
get: function () {
return _keyword.isStrictReservedWord;
}
});
var _identifier = require("./identifier.js");
var _keyword = require("./keyword.js");
//# sourceMappingURL=index.js.map

View File

@@ -1 +0,0 @@
{"version":3,"names":["_identifier","require","_keyword"],"sources":["../src/index.ts"],"sourcesContent":["export {\n isIdentifierName,\n isIdentifierChar,\n isIdentifierStart,\n} from \"./identifier.ts\";\nexport {\n isReservedWord,\n isStrictBindOnlyReservedWord,\n isStrictBindReservedWord,\n isStrictReservedWord,\n isKeyword,\n} from \"./keyword.ts\";\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,IAAAA,WAAA,GAAAC,OAAA;AAKA,IAAAC,QAAA,GAAAD,OAAA","ignoreList":[]}

View File

@@ -1,35 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.isKeyword = isKeyword;
exports.isReservedWord = isReservedWord;
exports.isStrictBindOnlyReservedWord = isStrictBindOnlyReservedWord;
exports.isStrictBindReservedWord = isStrictBindReservedWord;
exports.isStrictReservedWord = isStrictReservedWord;
const reservedWords = {
keyword: ["break", "case", "catch", "continue", "debugger", "default", "do", "else", "finally", "for", "function", "if", "return", "switch", "throw", "try", "var", "const", "while", "with", "new", "this", "super", "class", "extends", "export", "import", "null", "true", "false", "in", "instanceof", "typeof", "void", "delete"],
strict: ["implements", "interface", "let", "package", "private", "protected", "public", "static", "yield"],
strictBind: ["eval", "arguments"]
};
const keywords = new Set(reservedWords.keyword);
const reservedWordsStrictSet = new Set(reservedWords.strict);
const reservedWordsStrictBindSet = new Set(reservedWords.strictBind);
function isReservedWord(word, inModule) {
return inModule && word === "await" || word === "enum";
}
function isStrictReservedWord(word, inModule) {
return isReservedWord(word, inModule) || reservedWordsStrictSet.has(word);
}
function isStrictBindOnlyReservedWord(word) {
return reservedWordsStrictBindSet.has(word);
}
function isStrictBindReservedWord(word, inModule) {
return isStrictReservedWord(word, inModule) || isStrictBindOnlyReservedWord(word);
}
function isKeyword(word) {
return keywords.has(word);
}
//# sourceMappingURL=keyword.js.map

View File

@@ -1 +0,0 @@
{"version":3,"names":["reservedWords","keyword","strict","strictBind","keywords","Set","reservedWordsStrictSet","reservedWordsStrictBindSet","isReservedWord","word","inModule","isStrictReservedWord","has","isStrictBindOnlyReservedWord","isStrictBindReservedWord","isKeyword"],"sources":["../src/keyword.ts"],"sourcesContent":["const reservedWords = {\n keyword: [\n \"break\",\n \"case\",\n \"catch\",\n \"continue\",\n \"debugger\",\n \"default\",\n \"do\",\n \"else\",\n \"finally\",\n \"for\",\n \"function\",\n \"if\",\n \"return\",\n \"switch\",\n \"throw\",\n \"try\",\n \"var\",\n \"const\",\n \"while\",\n \"with\",\n \"new\",\n \"this\",\n \"super\",\n \"class\",\n \"extends\",\n \"export\",\n \"import\",\n \"null\",\n \"true\",\n \"false\",\n \"in\",\n \"instanceof\",\n \"typeof\",\n \"void\",\n \"delete\",\n ],\n strict: [\n \"implements\",\n \"interface\",\n \"let\",\n \"package\",\n \"private\",\n \"protected\",\n \"public\",\n \"static\",\n \"yield\",\n ],\n strictBind: [\"eval\", \"arguments\"],\n};\nconst keywords = new Set(reservedWords.keyword);\nconst reservedWordsStrictSet = new Set(reservedWords.strict);\nconst reservedWordsStrictBindSet = new Set(reservedWords.strictBind);\n\n/**\n * Checks if word is a reserved word in non-strict mode\n */\nexport function isReservedWord(word: string, inModule: boolean): boolean {\n return (inModule && word === \"await\") || word === \"enum\";\n}\n\n/**\n * Checks if word is a reserved word in non-binding strict mode\n *\n * Includes non-strict reserved words\n */\nexport function isStrictReservedWord(word: string, inModule: boolean): boolean {\n return isReservedWord(word, inModule) || reservedWordsStrictSet.has(word);\n}\n\n/**\n * Checks if word is a reserved word in binding strict mode, but it is allowed as\n * a normal identifier.\n */\nexport function isStrictBindOnlyReservedWord(word: string): boolean {\n return reservedWordsStrictBindSet.has(word);\n}\n\n/**\n * Checks if word is a reserved word in binding strict mode\n *\n * Includes non-strict reserved words and non-binding strict reserved words\n */\nexport function isStrictBindReservedWord(\n word: string,\n inModule: boolean,\n): boolean {\n return (\n isStrictReservedWord(word, inModule) || isStrictBindOnlyReservedWord(word)\n );\n}\n\nexport function isKeyword(word: string): boolean {\n return keywords.has(word);\n}\n"],"mappings":";;;;;;;;;;AAAA,MAAMA,aAAa,GAAG;EACpBC,OAAO,EAAE,CACP,OAAO,EACP,MAAM,EACN,OAAO,EACP,UAAU,EACV,UAAU,EACV,SAAS,EACT,IAAI,EACJ,MAAM,EACN,SAAS,EACT,KAAK,EACL,UAAU,EACV,IAAI,EACJ,QAAQ,EACR,QAAQ,EACR,OAAO,EACP,KAAK,EACL,KAAK,EACL,OAAO,EACP,OAAO,EACP,MAAM,EACN,KAAK,EACL,MAAM,EACN,OAAO,EACP,OAAO,EACP,SAAS,EACT,QAAQ,EACR,QAAQ,EACR,MAAM,EACN,MAAM,EACN,OAAO,EACP,IAAI,EACJ,YAAY,EACZ,QAAQ,EACR,MAAM,EACN,QAAQ,CACT;EACDC,MAAM,EAAE,CACN,YAAY,EACZ,WAAW,EACX,KAAK,EACL,SAAS,EACT,SAAS,EACT,WAAW,EACX,QAAQ,EACR,QAAQ,EACR,OAAO,CACR;EACDC,UAAU,EAAE,CAAC,MAAM,EAAE,WAAW;AAClC,CAAC;AACD,MAAMC,QAAQ,GAAG,IAAIC,GAAG,CAACL,aAAa,CAACC,OAAO,CAAC;AAC/C,MAAMK,sBAAsB,GAAG,IAAID,GAAG,CAACL,aAAa,CAACE,MAAM,CAAC;AAC5D,MAAMK,0BAA0B,GAAG,IAAIF,GAAG,CAACL,aAAa,CAACG,UAAU,CAAC;AAK7D,SAASK,cAAcA,CAACC,IAAY,EAAEC,QAAiB,EAAW;EACvE,OAAQA,QAAQ,IAAID,IAAI,KAAK,OAAO,IAAKA,IAAI,KAAK,MAAM;AAC1D;AAOO,SAASE,oBAAoBA,CAACF,IAAY,EAAEC,QAAiB,EAAW;EAC7E,OAAOF,cAAc,CAACC,IAAI,EAAEC,QAAQ,CAAC,IAAIJ,sBAAsB,CAACM,GAAG,CAACH,IAAI,CAAC;AAC3E;AAMO,SAASI,4BAA4BA,CAACJ,IAAY,EAAW;EAClE,OAAOF,0BAA0B,CAACK,GAAG,CAACH,IAAI,CAAC;AAC7C;AAOO,SAASK,wBAAwBA,CACtCL,IAAY,EACZC,QAAiB,EACR;EACT,OACEC,oBAAoB,CAACF,IAAI,EAAEC,QAAQ,CAAC,IAAIG,4BAA4B,CAACJ,IAAI,CAAC;AAE9E;AAEO,SAASM,SAASA,CAACN,IAAY,EAAW;EAC/C,OAAOL,QAAQ,CAACQ,GAAG,CAACH,IAAI,CAAC;AAC3B","ignoreList":[]}

View File

@@ -1,31 +0,0 @@
{
"name": "@babel/helper-validator-identifier",
"version": "7.27.1",
"description": "Validate identifier/keywords name",
"repository": {
"type": "git",
"url": "https://github.com/babel/babel.git",
"directory": "packages/babel-helper-validator-identifier"
},
"license": "MIT",
"publishConfig": {
"access": "public"
},
"main": "./lib/index.js",
"exports": {
".": {
"types": "./lib/index.d.ts",
"default": "./lib/index.js"
},
"./package.json": "./package.json"
},
"devDependencies": {
"@unicode/unicode-16.0.0": "^1.0.0",
"charcodes": "^0.2.0"
},
"engines": {
"node": ">=6.9.0"
},
"author": "The Babel Team (https://babel.dev/team)",
"type": "commonjs"
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,19 +0,0 @@
Copyright (C) 2012-2014 by various contributors (see AUTHORS)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@@ -1,19 +0,0 @@
# @babel/parser
> A JavaScript parser
See our website [@babel/parser](https://babeljs.io/docs/babel-parser) for more information or the [issues](https://github.com/babel/babel/issues?utf8=%E2%9C%93&q=is%3Aissue+label%3A%22pkg%3A%20parser%22+is%3Aopen) associated with this package.
## Install
Using npm:
```sh
npm install --save-dev @babel/parser
```
or using yarn:
```sh
yarn add @babel/parser --dev
```

View File

@@ -1,15 +0,0 @@
#!/usr/bin/env node
/* eslint-disable no-var, unicorn/prefer-node-protocol */
var parser = require("..");
var fs = require("fs");
var filename = process.argv[2];
if (!filename) {
console.error("no filename specified");
} else {
var file = fs.readFileSync(filename, "utf8");
var ast = parser.parse(file);
console.log(JSON.stringify(ast, null, " "));
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,50 +0,0 @@
{
"name": "@babel/parser",
"version": "7.28.4",
"description": "A JavaScript parser",
"author": "The Babel Team (https://babel.dev/team)",
"homepage": "https://babel.dev/docs/en/next/babel-parser",
"bugs": "https://github.com/babel/babel/issues?utf8=%E2%9C%93&q=is%3Aissue+label%3A%22pkg%3A+parser+%28babylon%29%22+is%3Aopen",
"license": "MIT",
"publishConfig": {
"access": "public"
},
"keywords": [
"babel",
"javascript",
"parser",
"tc39",
"ecmascript",
"@babel/parser"
],
"repository": {
"type": "git",
"url": "https://github.com/babel/babel.git",
"directory": "packages/babel-parser"
},
"main": "./lib/index.js",
"types": "./typings/babel-parser.d.ts",
"files": [
"bin",
"lib",
"typings/babel-parser.d.ts",
"index.cjs"
],
"engines": {
"node": ">=6.0.0"
},
"# dependencies": "This package doesn't actually have runtime dependencies. @babel/types is only needed for type definitions.",
"dependencies": {
"@babel/types": "^7.28.4"
},
"devDependencies": {
"@babel/code-frame": "^7.27.1",
"@babel/helper-check-duplicate-nodes": "^7.27.1",
"@babel/helper-fixtures": "^7.28.0",
"@babel/helper-string-parser": "^7.27.1",
"@babel/helper-validator-identifier": "^7.27.1",
"charcodes": "^0.2.0"
},
"bin": "./bin/babel-parser.js",
"type": "commonjs"
}

View File

@@ -1,239 +0,0 @@
// This file is auto-generated! Do not modify it directly.
// Run `yarn gulp bundle-dts` to re-generate it.
/* eslint-disable @typescript-eslint/consistent-type-imports, @typescript-eslint/no-redundant-type-constituents */
import { File, Expression } from '@babel/types';
type BABEL_8_BREAKING = false;
type IF_BABEL_7<V> = false extends BABEL_8_BREAKING ? V : never;
type Plugin$1 =
| "asyncDoExpressions"
| IF_BABEL_7<"asyncGenerators">
| IF_BABEL_7<"bigInt">
| IF_BABEL_7<"classPrivateMethods">
| IF_BABEL_7<"classPrivateProperties">
| IF_BABEL_7<"classProperties">
| IF_BABEL_7<"classStaticBlock">
| IF_BABEL_7<"decimal">
| "decorators-legacy"
| "deferredImportEvaluation"
| "decoratorAutoAccessors"
| "destructuringPrivate"
| "deprecatedImportAssert"
| "doExpressions"
| IF_BABEL_7<"dynamicImport">
| IF_BABEL_7<"explicitResourceManagement">
| "exportDefaultFrom"
| IF_BABEL_7<"exportNamespaceFrom">
| "flow"
| "flowComments"
| "functionBind"
| "functionSent"
| "importMeta"
| "jsx"
| IF_BABEL_7<"jsonStrings">
| IF_BABEL_7<"logicalAssignment">
| IF_BABEL_7<"importAssertions">
| IF_BABEL_7<"importReflection">
| "moduleBlocks"
| IF_BABEL_7<"moduleStringNames">
| IF_BABEL_7<"nullishCoalescingOperator">
| IF_BABEL_7<"numericSeparator">
| IF_BABEL_7<"objectRestSpread">
| IF_BABEL_7<"optionalCatchBinding">
| IF_BABEL_7<"optionalChaining">
| "partialApplication"
| "placeholders"
| IF_BABEL_7<"privateIn">
| IF_BABEL_7<"regexpUnicodeSets">
| "sourcePhaseImports"
| "throwExpressions"
| IF_BABEL_7<"topLevelAwait">
| "v8intrinsic"
| ParserPluginWithOptions[0];
type ParserPluginWithOptions =
| ["decorators", DecoratorsPluginOptions]
| ["discardBinding", { syntaxType: "void" }]
| ["estree", { classFeatures?: boolean }]
| IF_BABEL_7<["importAttributes", { deprecatedAssertSyntax: boolean }]>
| IF_BABEL_7<["moduleAttributes", { version: "may-2020" }]>
| ["optionalChainingAssign", { version: "2023-07" }]
| ["pipelineOperator", PipelineOperatorPluginOptions]
| ["recordAndTuple", RecordAndTuplePluginOptions]
| ["flow", FlowPluginOptions]
| ["typescript", TypeScriptPluginOptions];
type PluginConfig = Plugin$1 | ParserPluginWithOptions;
interface DecoratorsPluginOptions {
decoratorsBeforeExport?: boolean;
allowCallParenthesized?: boolean;
}
interface PipelineOperatorPluginOptions {
proposal: BABEL_8_BREAKING extends false
? "minimal" | "fsharp" | "hack" | "smart"
: "fsharp" | "hack";
topicToken?: "%" | "#" | "@@" | "^^" | "^";
}
interface RecordAndTuplePluginOptions {
syntaxType: "bar" | "hash";
}
type FlowPluginOptions = BABEL_8_BREAKING extends true
? {
all?: boolean;
enums?: boolean;
}
: {
all?: boolean;
};
interface TypeScriptPluginOptions {
dts?: boolean;
disallowAmbiguousJSXLike?: boolean;
}
type Plugin = PluginConfig;
type SourceType = "script" | "commonjs" | "module" | "unambiguous";
interface Options {
/**
* By default, import and export declarations can only appear at a program's top level.
* Setting this option to true allows them anywhere where a statement is allowed.
*/
allowImportExportEverywhere?: boolean;
/**
* By default, await use is not allowed outside of an async function.
* Set this to true to accept such code.
*/
allowAwaitOutsideFunction?: boolean;
/**
* By default, a return statement at the top level raises an error.
* Set this to true to accept such code.
*/
allowReturnOutsideFunction?: boolean;
/**
* By default, new.target use is not allowed outside of a function or class.
* Set this to true to accept such code.
*/
allowNewTargetOutsideFunction?: boolean;
allowSuperOutsideMethod?: boolean;
/**
* By default, exported identifiers must refer to a declared variable.
* Set this to true to allow export statements to reference undeclared variables.
*/
allowUndeclaredExports?: boolean;
/**
* By default, yield use is not allowed outside of a generator function.
* Set this to true to accept such code.
*/
allowYieldOutsideFunction?: boolean;
/**
* By default, Babel parser JavaScript code according to Annex B syntax.
* Set this to `false` to disable such behavior.
*/
annexB?: boolean;
/**
* By default, Babel attaches comments to adjacent AST nodes.
* When this option is set to false, comments are not attached.
* It can provide up to 30% performance improvement when the input code has many comments.
* @babel/eslint-parser will set it for you.
* It is not recommended to use attachComment: false with Babel transform,
* as doing so removes all the comments in output code, and renders annotations such as
* /* istanbul ignore next *\/ nonfunctional.
*/
attachComment?: boolean;
/**
* By default, Babel always throws an error when it finds some invalid code.
* When this option is set to true, it will store the parsing error and
* try to continue parsing the invalid input file.
*/
errorRecovery?: boolean;
/**
* Indicate the mode the code should be parsed in.
* Can be one of "script", "commonjs", "module", or "unambiguous". Defaults to "script".
* "unambiguous" will make @babel/parser attempt to guess, based on the presence
* of ES6 import or export statements.
* Files with ES6 imports and exports are considered "module" and are otherwise "script".
*
* Use "commonjs" to parse code that is intended to be run in a CommonJS environment such as Node.js.
*/
sourceType?: SourceType;
/**
* Correlate output AST nodes with their source filename.
* Useful when generating code and source maps from the ASTs of multiple input files.
*/
sourceFilename?: string;
/**
* By default, all source indexes start from 0.
* You can provide a start index to alternatively start with.
* Useful for integration with other source tools.
*/
startIndex?: number;
/**
* By default, the first line of code parsed is treated as line 1.
* You can provide a line number to alternatively start with.
* Useful for integration with other source tools.
*/
startLine?: number;
/**
* By default, the parsed code is treated as if it starts from line 1, column 0.
* You can provide a column number to alternatively start with.
* Useful for integration with other source tools.
*/
startColumn?: number;
/**
* Array containing the plugins that you want to enable.
*/
plugins?: Plugin[];
/**
* Should the parser work in strict mode.
* Defaults to true if sourceType === 'module'. Otherwise, false.
*/
strictMode?: boolean;
/**
* Adds a ranges property to each node: [node.start, node.end]
*/
ranges?: boolean;
/**
* Adds all parsed tokens to a tokens property on the File node.
*/
tokens?: boolean;
/**
* By default, the parser adds information about parentheses by setting
* `extra.parenthesized` to `true` as needed.
* When this option is `true` the parser creates `ParenthesizedExpression`
* AST nodes instead of using the `extra` property.
*/
createParenthesizedExpressions?: boolean;
/**
* The default is false in Babel 7 and true in Babel 8
* Set this to true to parse it as an `ImportExpression` node.
* Otherwise `import(foo)` is parsed as `CallExpression(Import, [Identifier(foo)])`.
*/
createImportExpressions?: boolean;
}
type ParserOptions = Partial<Options>;
interface ParseError {
code: string;
reasonCode: string;
}
type ParseResult<Result extends File | Expression = File> = Result & {
errors: null | ParseError[];
};
/**
* Parse the provided code as an entire ECMAScript program.
*/
declare function parse(input: string, options?: ParserOptions): ParseResult<File>;
declare function parseExpression(input: string, options?: ParserOptions): ParseResult<Expression>;
declare const tokTypes: {
// todo(flow->ts) real token type
[name: string]: any;
};
export { DecoratorsPluginOptions, FlowPluginOptions, ParseError, ParseResult, ParserOptions, PluginConfig as ParserPlugin, ParserPluginWithOptions, PipelineOperatorPluginOptions, RecordAndTuplePluginOptions, TypeScriptPluginOptions, parse, parseExpression, tokTypes };

View File

@@ -1,22 +0,0 @@
MIT License
Copyright (c) 2014-present Sebastian McKenzie and other contributors
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -1,19 +0,0 @@
# @babel/types
> Babel Types is a Lodash-esque utility library for AST nodes
See our website [@babel/types](https://babeljs.io/docs/babel-types) for more information or the [issues](https://github.com/babel/babel/issues?utf8=%E2%9C%93&q=is%3Aissue+label%3A%22pkg%3A%20types%22+is%3Aopen) associated with this package.
## Install
Using npm:
```sh
npm install --save-dev @babel/types
```
or using yarn:
```sh
yarn add @babel/types --dev
```

View File

@@ -1,16 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = assertNode;
var _isNode = require("../validators/isNode.js");
function assertNode(node) {
if (!(0, _isNode.default)(node)) {
var _node$type;
const type = (_node$type = node == null ? void 0 : node.type) != null ? _node$type : JSON.stringify(node);
throw new TypeError(`Not a valid node of type "${type}"`);
}
}
//# sourceMappingURL=assertNode.js.map

View File

@@ -1 +0,0 @@
{"version":3,"names":["_isNode","require","assertNode","node","isNode","_node$type","type","JSON","stringify","TypeError"],"sources":["../../src/asserts/assertNode.ts"],"sourcesContent":["import isNode from \"../validators/isNode.ts\";\nimport type * as t from \"../index.ts\";\n\nexport default function assertNode(node?: any): asserts node is t.Node {\n if (!isNode(node)) {\n const type = node?.type ?? JSON.stringify(node);\n throw new TypeError(`Not a valid node of type \"${type}\"`);\n }\n}\n"],"mappings":";;;;;;AAAA,IAAAA,OAAA,GAAAC,OAAA;AAGe,SAASC,UAAUA,CAACC,IAAU,EAA0B;EACrE,IAAI,CAAC,IAAAC,eAAM,EAACD,IAAI,CAAC,EAAE;IAAA,IAAAE,UAAA;IACjB,MAAMC,IAAI,IAAAD,UAAA,GAAGF,IAAI,oBAAJA,IAAI,CAAEG,IAAI,YAAAD,UAAA,GAAIE,IAAI,CAACC,SAAS,CAACL,IAAI,CAAC;IAC/C,MAAM,IAAIM,SAAS,CAAC,6BAA6BH,IAAI,GAAG,CAAC;EAC3D;AACF","ignoreList":[]}

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@@ -1,3 +0,0 @@
"use strict";
//# sourceMappingURL=index.js.map

File diff suppressed because one or more lines are too long

View File

@@ -1,18 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = createFlowUnionType;
var _index = require("../generated/index.js");
var _removeTypeDuplicates = require("../../modifications/flow/removeTypeDuplicates.js");
function createFlowUnionType(types) {
const flattened = (0, _removeTypeDuplicates.default)(types);
if (flattened.length === 1) {
return flattened[0];
} else {
return (0, _index.unionTypeAnnotation)(flattened);
}
}
//# sourceMappingURL=createFlowUnionType.js.map

View File

@@ -1 +0,0 @@
{"version":3,"names":["_index","require","_removeTypeDuplicates","createFlowUnionType","types","flattened","removeTypeDuplicates","length","unionTypeAnnotation"],"sources":["../../../src/builders/flow/createFlowUnionType.ts"],"sourcesContent":["import { unionTypeAnnotation } from \"../generated/index.ts\";\nimport removeTypeDuplicates from \"../../modifications/flow/removeTypeDuplicates.ts\";\nimport type * as t from \"../../index.ts\";\n\n/**\n * Takes an array of `types` and flattens them, removing duplicates and\n * returns a `UnionTypeAnnotation` node containing them.\n */\nexport default function createFlowUnionType<T extends t.FlowType>(\n types: [T] | Array<T>,\n): T | t.UnionTypeAnnotation {\n const flattened = removeTypeDuplicates(types);\n\n if (flattened.length === 1) {\n return flattened[0] as T;\n } else {\n return unionTypeAnnotation(flattened);\n }\n}\n"],"mappings":";;;;;;AAAA,IAAAA,MAAA,GAAAC,OAAA;AACA,IAAAC,qBAAA,GAAAD,OAAA;AAOe,SAASE,mBAAmBA,CACzCC,KAAqB,EACM;EAC3B,MAAMC,SAAS,GAAG,IAAAC,6BAAoB,EAACF,KAAK,CAAC;EAE7C,IAAIC,SAAS,CAACE,MAAM,KAAK,CAAC,EAAE;IAC1B,OAAOF,SAAS,CAAC,CAAC,CAAC;EACrB,CAAC,MAAM;IACL,OAAO,IAAAG,0BAAmB,EAACH,SAAS,CAAC;EACvC;AACF","ignoreList":[]}

View File

@@ -1,31 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _index = require("../generated/index.js");
var _default = exports.default = createTypeAnnotationBasedOnTypeof;
function createTypeAnnotationBasedOnTypeof(type) {
switch (type) {
case "string":
return (0, _index.stringTypeAnnotation)();
case "number":
return (0, _index.numberTypeAnnotation)();
case "undefined":
return (0, _index.voidTypeAnnotation)();
case "boolean":
return (0, _index.booleanTypeAnnotation)();
case "function":
return (0, _index.genericTypeAnnotation)((0, _index.identifier)("Function"));
case "object":
return (0, _index.genericTypeAnnotation)((0, _index.identifier)("Object"));
case "symbol":
return (0, _index.genericTypeAnnotation)((0, _index.identifier)("Symbol"));
case "bigint":
return (0, _index.anyTypeAnnotation)();
}
throw new Error("Invalid typeof value: " + type);
}
//# sourceMappingURL=createTypeAnnotationBasedOnTypeof.js.map

View File

@@ -1 +0,0 @@
{"version":3,"names":["_index","require","_default","exports","default","createTypeAnnotationBasedOnTypeof","type","stringTypeAnnotation","numberTypeAnnotation","voidTypeAnnotation","booleanTypeAnnotation","genericTypeAnnotation","identifier","anyTypeAnnotation","Error"],"sources":["../../../src/builders/flow/createTypeAnnotationBasedOnTypeof.ts"],"sourcesContent":["import {\n anyTypeAnnotation,\n stringTypeAnnotation,\n numberTypeAnnotation,\n voidTypeAnnotation,\n booleanTypeAnnotation,\n genericTypeAnnotation,\n identifier,\n} from \"../generated/index.ts\";\nimport type * as t from \"../../index.ts\";\n\nexport default createTypeAnnotationBasedOnTypeof as {\n (type: \"string\"): t.StringTypeAnnotation;\n (type: \"number\"): t.NumberTypeAnnotation;\n (type: \"undefined\"): t.VoidTypeAnnotation;\n (type: \"boolean\"): t.BooleanTypeAnnotation;\n (type: \"function\"): t.GenericTypeAnnotation;\n (type: \"object\"): t.GenericTypeAnnotation;\n (type: \"symbol\"): t.GenericTypeAnnotation;\n (type: \"bigint\"): t.AnyTypeAnnotation;\n};\n\n/**\n * Create a type annotation based on typeof expression.\n */\nfunction createTypeAnnotationBasedOnTypeof(type: string): t.FlowType {\n switch (type) {\n case \"string\":\n return stringTypeAnnotation();\n case \"number\":\n return numberTypeAnnotation();\n case \"undefined\":\n return voidTypeAnnotation();\n case \"boolean\":\n return booleanTypeAnnotation();\n case \"function\":\n return genericTypeAnnotation(identifier(\"Function\"));\n case \"object\":\n return genericTypeAnnotation(identifier(\"Object\"));\n case \"symbol\":\n return genericTypeAnnotation(identifier(\"Symbol\"));\n case \"bigint\":\n // todo: use BigInt annotation when Flow supports BigInt\n // https://github.com/facebook/flow/issues/6639\n return anyTypeAnnotation();\n }\n throw new Error(\"Invalid typeof value: \" + type);\n}\n"],"mappings":";;;;;;AAAA,IAAAA,MAAA,GAAAC,OAAA;AAQ+B,IAAAC,QAAA,GAAAC,OAAA,CAAAC,OAAA,GAGhBC,iCAAiC;AAchD,SAASA,iCAAiCA,CAACC,IAAY,EAAc;EACnE,QAAQA,IAAI;IACV,KAAK,QAAQ;MACX,OAAO,IAAAC,2BAAoB,EAAC,CAAC;IAC/B,KAAK,QAAQ;MACX,OAAO,IAAAC,2BAAoB,EAAC,CAAC;IAC/B,KAAK,WAAW;MACd,OAAO,IAAAC,yBAAkB,EAAC,CAAC;IAC7B,KAAK,SAAS;MACZ,OAAO,IAAAC,4BAAqB,EAAC,CAAC;IAChC,KAAK,UAAU;MACb,OAAO,IAAAC,4BAAqB,EAAC,IAAAC,iBAAU,EAAC,UAAU,CAAC,CAAC;IACtD,KAAK,QAAQ;MACX,OAAO,IAAAD,4BAAqB,EAAC,IAAAC,iBAAU,EAAC,QAAQ,CAAC,CAAC;IACpD,KAAK,QAAQ;MACX,OAAO,IAAAD,4BAAqB,EAAC,IAAAC,iBAAU,EAAC,QAAQ,CAAC,CAAC;IACpD,KAAK,QAAQ;MAGX,OAAO,IAAAC,wBAAiB,EAAC,CAAC;EAC9B;EACA,MAAM,IAAIC,KAAK,CAAC,wBAAwB,GAAGR,IAAI,CAAC;AAClD","ignoreList":[]}

View File

@@ -1,29 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
var _lowercase = require("./lowercase.js");
Object.keys(_lowercase).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
if (key in exports && exports[key] === _lowercase[key]) return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function () {
return _lowercase[key];
}
});
});
var _uppercase = require("./uppercase.js");
Object.keys(_uppercase).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
if (key in exports && exports[key] === _uppercase[key]) return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function () {
return _uppercase[key];
}
});
});
//# sourceMappingURL=index.js.map

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

Some files were not shown because too many files have changed in this diff Show More