@browsersync/bslive 0.0.9 → 0.0.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/Cargo.toml +3 -2
  2. package/bslive/src/lib.rs +8 -3
  3. package/bsnext/src/main.rs +9 -3
  4. package/crates/bsnext_client/generated/dto.ts +33 -2
  5. package/crates/bsnext_client/generated/schema.ts +52 -2
  6. package/crates/bsnext_client/{dist → inject/dist}/index.js +105 -58
  7. package/crates/bsnext_client/inject/package.json +12 -0
  8. package/crates/bsnext_client/{ts → inject/src}/index.ts +2 -2
  9. package/crates/bsnext_client/package-lock.json +89 -3
  10. package/crates/bsnext_client/package.json +4 -2
  11. package/crates/bsnext_client/src/lib.rs +10 -0
  12. package/crates/bsnext_client/tsconfig.json +8 -2
  13. package/crates/bsnext_client/ui/dist/index.css +78 -0
  14. package/crates/bsnext_client/ui/dist/index.js +997 -0
  15. package/crates/bsnext_client/{index.html → ui/index.html} +8 -4
  16. package/crates/bsnext_client/ui/input.yml +19 -0
  17. package/crates/bsnext_client/ui/package.json +18 -0
  18. package/crates/bsnext_client/ui/src/components/bs-debug.ts +27 -0
  19. package/crates/bsnext_client/ui/src/components/bs-header.ts +33 -0
  20. package/crates/bsnext_client/ui/src/components/bs-icon.ts +101 -0
  21. package/crates/bsnext_client/ui/src/components/bs-server-detail.ts +21 -0
  22. package/crates/bsnext_client/ui/src/components/bs-server-identity.ts +24 -0
  23. package/crates/bsnext_client/ui/src/components/bs-server-list.ts +39 -0
  24. package/crates/bsnext_client/ui/src/index.ts +39 -0
  25. package/crates/bsnext_client/ui/styles/base.css.ts +17 -0
  26. package/crates/bsnext_client/ui/styles/reset.css +52 -0
  27. package/crates/bsnext_client/ui/styles/style.css +29 -0
  28. package/crates/bsnext_client/ui/svg/wordmark-white.svg +38 -0
  29. package/crates/bsnext_core/Cargo.toml +1 -0
  30. package/crates/bsnext_core/src/common_layers.rs +62 -0
  31. package/crates/bsnext_core/src/dir_loader.rs +1 -1
  32. package/crates/bsnext_core/src/dto.rs +62 -2
  33. package/crates/bsnext_core/src/lib.rs +1 -0
  34. package/crates/bsnext_core/src/not_found/mod.rs +0 -1
  35. package/crates/bsnext_core/src/not_found/not_found_service.rs +5 -11
  36. package/crates/bsnext_core/src/raw_loader.rs +4 -34
  37. package/crates/bsnext_core/src/server/handler_listen.rs +10 -4
  38. package/crates/bsnext_core/src/server/router/assets.rs +39 -0
  39. package/crates/bsnext_core/src/server/router/mod.rs +15 -4
  40. package/crates/bsnext_core/src/server/router/pub_api.rs +39 -0
  41. package/crates/bsnext_core/src/server/router/snapshots/bsnext_core__server__router__tests__test__handlers.snap +9 -0
  42. package/crates/bsnext_core/src/server/router/tests.rs +11 -6
  43. package/crates/bsnext_core/src/server/state.rs +4 -0
  44. package/crates/bsnext_core/src/servers_supervisor/get_servers_handler.rs +3 -2
  45. package/crates/bsnext_core/src/servers_supervisor/start_handler.rs +5 -1
  46. package/crates/bsnext_fs/src/actor.rs +2 -1
  47. package/crates/bsnext_fs/src/filter.rs +14 -5
  48. package/crates/bsnext_fs/src/test/mod.rs +1 -1
  49. package/crates/bsnext_fs/src/watch_path_handler.rs +1 -1
  50. package/crates/bsnext_fs/src/watcher.rs +2 -1
  51. package/crates/bsnext_input/Cargo.toml +3 -0
  52. package/crates/bsnext_input/src/input_test/mod.rs +34 -0
  53. package/crates/bsnext_input/src/input_test/snapshots/bsnext_input__input_test__deserialize_3_headers.snap +29 -0
  54. package/crates/bsnext_input/src/input_test/snapshots/bsnext_input__input_test__deserialize_3_headers_control.snap +25 -0
  55. package/crates/bsnext_input/src/route.rs +4 -0
  56. package/crates/bsnext_output/src/pretty.rs +1 -1
  57. package/crates/bsnext_system/Cargo.toml +1 -1
  58. package/crates/bsnext_system/src/args.rs +4 -0
  59. package/crates/bsnext_system/src/monitor_any_watchables.rs +20 -14
  60. package/crates/bsnext_system/src/start_kind/snapshots/bsnext_system__start_kind__start_from_paths__test__test-2.snap +2 -1
  61. package/crates/bsnext_system/src/start_kind/snapshots/bsnext_system__start_kind__start_from_paths__test__test.snap +2 -1
  62. package/crates/bsnext_tracing/src/lib.rs +42 -4
  63. package/examples/kitchen-sink/input.yml +1 -1
  64. package/package.json +17 -17
  65. package/crates/bsnext_client/style.css +0 -3
  66. package/crates/bsnext_core/src/not_found/not_found.html +0 -20
  67. package/crates/bsnext_core/src/not_found/route_list.rs +0 -49
  68. /package/crates/bsnext_client/{ts → inject/src}/console.ts +0 -0
@@ -6,8 +6,67 @@ use std::path::Path;
6
6
 
7
7
  use crate::server::handler_change::{Change, ChangeKind};
8
8
  use bsnext_fs::Debounce;
9
+ use bsnext_input::route::{DirRoute, ProxyRoute, Route, RouteKind};
9
10
  use typeshare::typeshare;
10
11
 
12
+ #[typeshare]
13
+ #[derive(Debug, serde::Serialize)]
14
+ pub struct ServerDesc {
15
+ pub routes: Vec<RouteDTO>,
16
+ pub id: String,
17
+ }
18
+
19
+ #[typeshare]
20
+ #[derive(Debug, serde::Serialize)]
21
+ pub struct RouteDTO {
22
+ pub path: String,
23
+ pub kind: RouteKindDTO,
24
+ }
25
+
26
+ impl From<Route> for RouteDTO {
27
+ fn from(value: Route) -> Self {
28
+ Self {
29
+ path: value.path.to_owned(),
30
+ kind: RouteKindDTO::from(value.kind),
31
+ }
32
+ }
33
+ }
34
+ impl From<&Route> for RouteDTO {
35
+ fn from(value: &Route) -> Self {
36
+ Self {
37
+ path: value.path.to_owned(),
38
+ kind: RouteKindDTO::from(value.kind.clone()),
39
+ }
40
+ }
41
+ }
42
+
43
+ #[typeshare]
44
+ #[derive(Debug, serde::Serialize)]
45
+ #[serde(tag = "kind", content = "payload")]
46
+ pub enum RouteKindDTO {
47
+ Html { html: String },
48
+ Json { json_str: String },
49
+ Raw { raw: String },
50
+ Sse { sse: String },
51
+ Proxy { proxy: String },
52
+ Dir { dir: String },
53
+ }
54
+
55
+ impl From<RouteKind> for RouteKindDTO {
56
+ fn from(value: RouteKind) -> Self {
57
+ match value {
58
+ RouteKind::Html { html } => RouteKindDTO::Html { html },
59
+ RouteKind::Json { json } => RouteKindDTO::Json {
60
+ json_str: serde_json::to_string(&json).expect("unreachable"),
61
+ },
62
+ RouteKind::Raw { raw } => RouteKindDTO::Raw { raw },
63
+ RouteKind::Sse { sse } => RouteKindDTO::Sse { sse },
64
+ RouteKind::Proxy(ProxyRoute { proxy }) => RouteKindDTO::Proxy { proxy },
65
+ RouteKind::Dir(DirRoute { dir }) => RouteKindDTO::Dir { dir },
66
+ }
67
+ }
68
+ }
69
+
11
70
  #[typeshare]
12
71
  #[derive(Debug, serde::Serialize)]
13
72
  pub struct ServersStarted {
@@ -157,12 +216,13 @@ pub struct ServerChangeSet {
157
216
  #[typeshare::typeshare]
158
217
  #[derive(Debug, Clone, serde::Serialize, serde::Deserialize, MessageResponse)]
159
218
  pub struct GetServersMessageResponse {
160
- pub servers: Vec<ServersDTO>,
219
+ pub servers: Vec<ServerDTO>,
161
220
  }
162
221
 
163
222
  #[typeshare::typeshare]
164
223
  #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
165
- pub struct ServersDTO {
224
+ pub struct ServerDTO {
225
+ pub id: String,
166
226
  pub identity: IdentityDTO,
167
227
  pub socket_addr: String,
168
228
  }
@@ -1,6 +1,7 @@
1
1
  pub mod server;
2
2
  pub mod servers_supervisor;
3
3
 
4
+ pub mod common_layers;
4
5
  pub mod dir_loader;
5
6
  pub mod dto;
6
7
  pub mod handlers;
@@ -1,2 +1 @@
1
1
  pub mod not_found_service;
2
- pub mod route_list;
@@ -1,6 +1,5 @@
1
- use axum::extract::{Request, State};
1
+ use axum::extract::Request;
2
2
  use axum::middleware::Next;
3
- use std::sync::Arc;
4
3
 
5
4
  use axum::response::{IntoResponse, Response};
6
5
 
@@ -9,15 +8,11 @@ use http::{HeaderValue, StatusCode};
9
8
  use mime_guess::mime;
10
9
 
11
10
  use crate::handlers::proxy::AnyAppError;
12
- use crate::not_found::route_list::create_route_list_html;
13
- use crate::server::state::ServerState;
11
+
12
+ use bsnext_client::html_with_base;
14
13
  use tracing::{span, Level};
15
14
 
16
- pub async fn not_found_loader(
17
- State(state): State<Arc<ServerState>>,
18
- req: Request,
19
- next: Next,
20
- ) -> Result<Response, AnyAppError> {
15
+ pub async fn not_found_loader(req: Request, next: Next) -> Result<Response, AnyAppError> {
21
16
  let span = span!(parent: None, Level::INFO, "not_found", path = req.uri().path());
22
17
  let _guard = span.enter();
23
18
 
@@ -26,8 +21,7 @@ pub async fn not_found_loader(
26
21
  return Ok(r);
27
22
  };
28
23
 
29
- let routes = state.routes.read().await;
30
- let markup = create_route_list_html(&routes);
24
+ let markup = html_with_base("/__bs_assets/ui/");
31
25
 
32
26
  Ok((
33
27
  StatusCode::NOT_FOUND,
@@ -11,17 +11,18 @@ use crate::meta::MetaData;
11
11
  use crate::server::state::ServerState;
12
12
  use axum::body::Body;
13
13
  use axum::response::sse::Event;
14
- use bsnext_input::route::{CorsOpts, DelayKind, DelayOpts, DirRoute, ProxyRoute, Route, RouteKind};
14
+ use bsnext_input::route::{DirRoute, ProxyRoute, RouteKind};
15
15
  use bsnext_resp::response_modifications_layer;
16
16
  use bytes::Bytes;
17
17
  use http::StatusCode;
18
18
  use http_body_util::BodyExt;
19
19
  use std::sync::Arc;
20
20
  use std::time::Duration;
21
- use tokio::time::sleep;
21
+
22
22
  use tokio_stream::StreamExt;
23
23
  use tower::ServiceExt;
24
- use tower_http::cors::CorsLayer;
24
+
25
+ use crate::common_layers::add_route_layers;
25
26
  use tracing::{span, Level};
26
27
 
27
28
  // use futures_util::stream::{self, Stream};
@@ -145,37 +146,6 @@ async fn tag_raw(req: Request, next: Next) -> Result<impl IntoResponse, (StatusC
145
146
  Ok(Response::from_parts(parts, body))
146
147
  }
147
148
 
148
- pub fn add_route_layers(app: Router, route: &Route) -> Router {
149
- let mut app = app;
150
-
151
- if route
152
- .cors_opts
153
- .as_ref()
154
- .is_some_and(|v| *v == CorsOpts::Cors(true))
155
- {
156
- tracing::trace!(to = route.path, "adding permissive cors");
157
- app = app.layer(CorsLayer::permissive());
158
- }
159
-
160
- if let Some(DelayOpts::Delay(DelayKind::Ms(ms))) = route.delay_opts.as_ref() {
161
- tracing::trace!(to = route.path, ?ms, "adding delay");
162
- let ms = *ms;
163
- app = app.layer(middleware::from_fn(
164
- move |req: Request, next: Next| async move {
165
- let res = next.run(req).await;
166
- sleep(Duration::from_millis(ms)).await;
167
- Ok::<_, Infallible>(res)
168
- },
169
- ));
170
- }
171
-
172
- // if route.opts.as_ref().is_some_and(|v| v.buff) {
173
- // app = app.layer(middleware::from_fn(print_request_response));
174
- // }
175
-
176
- app
177
- }
178
-
179
149
  #[allow(dead_code)]
180
150
  async fn print_request_response(
181
151
  req: Request,
@@ -2,7 +2,8 @@ use crate::server::actor::ServerActor;
2
2
  use crate::server::error::ServerError;
3
3
  use crate::server::router::make_router;
4
4
  use crate::server::state::ServerState;
5
- use actix::AsyncContext;
5
+ use crate::servers_supervisor::actor::ServersSupervisor;
6
+ use actix::{Addr, AsyncContext};
6
7
  use actix_rt::Arbiter;
7
8
  use bsnext_input::server_config::Identity;
8
9
  use std::future::Future;
@@ -14,13 +15,15 @@ use tokio::sync::{oneshot, RwLock};
14
15
 
15
16
  #[derive(actix::Message)]
16
17
  #[rtype(result = "Result<(SocketAddr, actix::Addr<ServerActor>), ServerError>")]
17
- pub struct Listen;
18
+ pub struct Listen {
19
+ pub(crate) parent: Addr<ServersSupervisor>,
20
+ }
18
21
 
19
22
  impl actix::Handler<Listen> for ServerActor {
20
23
  type Result =
21
24
  Pin<Box<dyn Future<Output = Result<(SocketAddr, actix::Addr<ServerActor>), ServerError>>>>;
22
25
 
23
- fn handle(&mut self, _msg: Listen, ctx: &mut Self::Context) -> Self::Result {
26
+ fn handle(&mut self, msg: Listen, ctx: &mut Self::Context) -> Self::Result {
24
27
  let identity = self.config.identity.clone();
25
28
  tracing::trace!("actor started for {:?}", identity);
26
29
  let (send_complete, handle, client_sender) = self.install_signals();
@@ -29,7 +32,10 @@ impl actix::Handler<Listen> for ServerActor {
29
32
  let h2 = handle.clone();
30
33
 
31
34
  let app_state = Arc::new(ServerState {
35
+ // parent: ,
32
36
  routes: Arc::new(RwLock::new(self.config.routes.clone())),
37
+ id: self.config.identity.as_id(),
38
+ parent: Some(msg.parent.clone()),
33
39
  client_sender: Arc::new(client_sender),
34
40
  });
35
41
 
@@ -103,7 +109,7 @@ impl actix::Handler<Listen> for ServerActor {
103
109
  listening = h2.listening() => {
104
110
  match listening {
105
111
  Some(socket_addr) => {
106
- tracing::debug!("box::pin was listening...");
112
+ tracing::debug!("{} listening...", socket_addr);
107
113
  Ok((socket_addr, self_addr.clone()))
108
114
  }
109
115
  None => {
@@ -0,0 +1,39 @@
1
+ use crate::server::state::ServerState;
2
+ use axum::response::IntoResponse;
3
+ use axum::routing::get;
4
+ use axum::Router;
5
+ use bsnext_client::{UI_CSS, UI_JS};
6
+ use http::header::CONTENT_TYPE;
7
+ use http::HeaderValue;
8
+ use mime_guess::mime;
9
+ use std::sync::Arc;
10
+
11
+ pub fn pub_ui_assets(_: Arc<ServerState>) -> Router<Arc<ServerState>> {
12
+ Router::new()
13
+ .route(
14
+ "/dist/index.js",
15
+ get(|| async {
16
+ (
17
+ [(
18
+ CONTENT_TYPE,
19
+ HeaderValue::from_static(mime::APPLICATION_JAVASCRIPT.as_ref()),
20
+ )],
21
+ UI_JS,
22
+ )
23
+ .into_response()
24
+ }),
25
+ )
26
+ .route(
27
+ "/dist/index.css",
28
+ get(|| async {
29
+ (
30
+ [(
31
+ CONTENT_TYPE,
32
+ HeaderValue::from_static(mime::TEXT_CSS.as_ref()),
33
+ )],
34
+ UI_CSS,
35
+ )
36
+ .into_response()
37
+ }),
38
+ )
39
+ }
@@ -21,15 +21,20 @@ use tower::ServiceBuilder;
21
21
  use tower_http::catch_panic::CatchPanicLayer;
22
22
 
23
23
  use crate::dir_loader::serve_dir_loader;
24
+ use crate::dto::{RouteDTO, ServerDesc};
24
25
  use crate::meta::MetaData;
25
26
  use crate::not_found::not_found_service::not_found_loader;
26
- use crate::not_found::route_list::create_route_list_html;
27
27
  use crate::raw_loader::raw_loader;
28
+ use crate::server::router::assets::pub_ui_assets;
29
+ use crate::server::router::pub_api::pub_api;
28
30
  use crate::server::state::ServerState;
29
31
  use crate::ws::ws_handler;
32
+ use bsnext_client::html_with_base;
30
33
  use tower_http::timeout::TimeoutLayer;
31
34
  use tower_http::trace::TraceLayer;
32
35
 
36
+ mod assets;
37
+ mod pub_api;
33
38
  mod tests;
34
39
 
35
40
  pub fn make_router(state: &Arc<ServerState>) -> Router {
@@ -49,7 +54,11 @@ pub fn built_ins(state: Arc<ServerState>) -> Router {
49
54
  async fn handler(State(app): State<Arc<ServerState>>, _uri: Uri) -> impl IntoResponse {
50
55
  // let v = app.lock().await;
51
56
  let routes = app.routes.read().await;
52
- let markup = create_route_list_html(&routes);
57
+ let _dto = ServerDesc {
58
+ routes: routes.iter().map(RouteDTO::from).collect(),
59
+ id: app.id.to_string(),
60
+ };
61
+ let markup = html_with_base("/__bs_assets/ui/");
53
62
  (
54
63
  [(
55
64
  CONTENT_TYPE,
@@ -60,7 +69,7 @@ pub fn built_ins(state: Arc<ServerState>) -> Router {
60
69
  .into_response()
61
70
  }
62
71
  async fn js_handler(_uri: Uri) -> impl IntoResponse {
63
- let markup = include_str!("../../../../bsnext_client/dist/index.js");
72
+ let markup = include_str!("../../../../bsnext_client/inject/dist/index.js");
64
73
  (
65
74
  [(
66
75
  CONTENT_TYPE,
@@ -71,8 +80,10 @@ pub fn built_ins(state: Arc<ServerState>) -> Router {
71
80
  .into_response()
72
81
  }
73
82
 
74
- route("/__bsnext", get(handler))
83
+ route("/__bslive", get(handler))
75
84
  .route("/__bs_js", get(js_handler))
85
+ .nest("/__bs_api", pub_api(state.clone()))
86
+ .nest("/__bs_assets/ui", pub_ui_assets(state.clone()))
76
87
  .route("/__bs_ws", get(ws_handler))
77
88
  .with_state(state.clone())
78
89
  }
@@ -0,0 +1,39 @@
1
+ use crate::dto::{RouteDTO, ServerDesc};
2
+ use crate::server::state::ServerState;
3
+ use crate::servers_supervisor::get_servers_handler::GetServersMessage;
4
+ use axum::extract::State;
5
+ use axum::response::IntoResponse;
6
+ use axum::routing::get;
7
+ use axum::{Json, Router};
8
+ use http::{StatusCode, Uri};
9
+ use std::sync::Arc;
10
+
11
+ async fn all_servers_handler(State(app): State<Arc<ServerState>>, _uri: Uri) -> impl IntoResponse {
12
+ match &app.parent {
13
+ None => unreachable!("cannot get here"),
14
+ Some(parent) => match parent.send(GetServersMessage).await {
15
+ Ok(servers) => Json(servers).into_response(),
16
+ Err(err) => (
17
+ StatusCode::INTERNAL_SERVER_ERROR,
18
+ format!("Could not fetch servers: {}", err),
19
+ )
20
+ .into_response(),
21
+ },
22
+ }
23
+ }
24
+
25
+ async fn get_current_server(State(app): State<Arc<ServerState>>, _uri: Uri) -> impl IntoResponse {
26
+ let routes = app.routes.read().await;
27
+ let dto = ServerDesc {
28
+ routes: routes.iter().map(RouteDTO::from).collect(),
29
+ id: app.id.to_string(),
30
+ };
31
+ Json(dto)
32
+ }
33
+
34
+ pub fn pub_api(state: Arc<ServerState>) -> Router<Arc<ServerState>> {
35
+ Router::new()
36
+ .route("/servers", get(all_servers_handler))
37
+ .route("/me", get(get_current_server))
38
+ .with_state(state.clone())
39
+ }
@@ -0,0 +1,9 @@
1
+ ---
2
+ source: crates/bsnext_core/src/server/router/tests.rs
3
+ expression: res.headers()
4
+ ---
5
+ {
6
+ "content-type": "text/html; charset=utf-8",
7
+ "content-length": "4",
8
+ "a": "b",
9
+ }
@@ -1,6 +1,5 @@
1
1
  #[cfg(test)]
2
2
  mod test {
3
-
4
3
  use crate::dto::ClientEvent;
5
4
  use crate::server::router::make_router;
6
5
  use crate::server::state::ServerState;
@@ -10,6 +9,7 @@ mod test {
10
9
  use bsnext_input::route::{CorsOpts, Route, RouteKind};
11
10
  use bsnext_input::server_config::{Identity, ServerConfig};
12
11
  use http::HeaderValue;
12
+ use std::collections::BTreeMap;
13
13
  use std::sync::Arc;
14
14
  use tokio::sync::RwLock;
15
15
  use tower::ServiceExt;
@@ -19,6 +19,8 @@ mod test {
19
19
  let (sender, _) = tokio::sync::broadcast::channel::<ClientEvent>(10);
20
20
  ServerState {
21
21
  routes: Arc::new(RwLock::new(val.routes.clone())),
22
+ id: val.identity.as_id(),
23
+ parent: None,
22
24
  client_sender: Arc::new(sender),
23
25
  }
24
26
  }
@@ -35,6 +37,8 @@ mod test {
35
37
 
36
38
  #[tokio::test]
37
39
  async fn test_handlers() -> Result<(), anyhow::Error> {
40
+ let headers: BTreeMap<String, String> =
41
+ [("a".to_string(), "b".to_string())].into_iter().collect();
38
42
  let state: ServerState = ServerConfig {
39
43
  identity: Identity::Address {
40
44
  bind_address: "127.0.0.1:3000".to_string(),
@@ -42,6 +46,7 @@ mod test {
42
46
  routes: vec![Route {
43
47
  path: "/hello".to_string(),
44
48
  kind: RouteKind::html("🐥"),
49
+ headers: Some(headers),
45
50
  ..Default::default()
46
51
  }],
47
52
  ..Default::default()
@@ -52,7 +57,7 @@ mod test {
52
57
  let req = Request::get("/hello").body(Body::empty()).unwrap();
53
58
  let res = app.oneshot(req).await.unwrap();
54
59
 
55
- assert_eq!(res.headers().get("content-length").unwrap(), "4");
60
+ insta::assert_debug_snapshot!(res.headers());
56
61
  assert_eq!(
57
62
  res.headers().get("content-type").unwrap(),
58
63
  "text/html; charset=utf-8"
@@ -164,7 +169,7 @@ mod test {
164
169
 
165
170
  let body = to_resp_body(res).await;
166
171
 
167
- assert!(body.contains("<title>BSNEXT</title>"));
172
+ assert!(body.contains("<title>Browsersync LIVE</title>"));
168
173
  assert_eq!(status, 404);
169
174
  Ok(())
170
175
  }
@@ -185,7 +190,7 @@ mod test {
185
190
  .into();
186
191
 
187
192
  let app = make_router(&Arc::new(state));
188
- let req = Request::get("/__bsnext").body(Body::empty()).unwrap();
193
+ let req = Request::get("/__bslive").body(Body::empty()).unwrap();
189
194
  let res = app.oneshot(req).await.unwrap();
190
195
  let status = res.status().as_u16();
191
196
 
@@ -196,8 +201,8 @@ mod test {
196
201
 
197
202
  let body = to_resp_body(res).await;
198
203
 
199
- assert!(body.contains("<title>BSNEXT</title>"));
200
- assert!(body.contains("<code>/abc</code>"));
204
+ assert!(body.contains("<title>Browsersync LIVE</title>"));
205
+ assert!(body.contains("<base href=\"/__bs_assets/ui/\" />"));
201
206
  assert_eq!(status, 200);
202
207
  Ok(())
203
208
  }
@@ -1,4 +1,6 @@
1
1
  use crate::dto::ClientEvent;
2
+ use crate::servers_supervisor::actor::ServersSupervisor;
3
+ use actix::Addr;
2
4
  use bsnext_input::route::Route;
3
5
  use std::fmt::Formatter;
4
6
  use std::sync::Arc;
@@ -7,6 +9,8 @@ use tokio::sync::{broadcast, RwLock};
7
9
  #[derive(Clone)]
8
10
  pub struct ServerState {
9
11
  pub routes: Arc<RwLock<Vec<Route>>>,
12
+ pub id: u64,
13
+ pub parent: Option<Addr<ServersSupervisor>>,
10
14
  pub client_sender: Arc<broadcast::Sender<ClientEvent>>,
11
15
  }
12
16
 
@@ -1,4 +1,4 @@
1
- use crate::dto::{GetServersMessageResponse, ServersDTO};
1
+ use crate::dto::{GetServersMessageResponse, ServerDTO};
2
2
  use crate::servers_supervisor::actor::ServersSupervisor;
3
3
 
4
4
  #[derive(actix::Message)]
@@ -13,7 +13,8 @@ impl actix::Handler<GetServersMessage> for ServersSupervisor {
13
13
  servers: self
14
14
  .handlers
15
15
  .iter()
16
- .map(|(identity, child_handler)| ServersDTO {
16
+ .map(|(identity, child_handler)| ServerDTO {
17
+ id: identity.as_id().to_string(),
17
18
  identity: identity.into(),
18
19
  socket_addr: child_handler.socket_addr.to_string(),
19
20
  })
@@ -31,7 +31,11 @@ impl actix::Handler<StartMessage> for ServersSupervisor {
31
31
  let server = ServerActor::new_from_config(server_config.clone());
32
32
  let actor_addr = server.start();
33
33
  let c = server_config.clone();
34
- actor_addr.send(Listen).map(|r| (r, c))
34
+ actor_addr
35
+ .send(Listen {
36
+ parent: self_addr.clone(),
37
+ })
38
+ .map(|r| (r, c))
35
39
  })
36
40
  .collect::<Vec<_>>();
37
41
 
@@ -14,7 +14,7 @@ use tokio::sync::{broadcast, mpsc};
14
14
  use tokio_stream::wrappers::ReceiverStream;
15
15
 
16
16
  pub struct FsWatcher {
17
- pub watcher: Option<notify::FsEventWatcher>,
17
+ pub watcher: Option<notify::RecommendedWatcher>,
18
18
  raw_fs_stream: Arc<broadcast::Sender<InnerChangeEvent>>,
19
19
  pub receivers: Vec<Recipient<FsEvent>>,
20
20
  pub ctx: FsEventContext,
@@ -50,6 +50,7 @@ impl FsWatcher {
50
50
  }
51
51
 
52
52
  pub fn with_filter(&mut self, f: Filter) {
53
+ tracing::debug!("adding filter {:?}", f);
53
54
  self.filters.push(f)
54
55
  }
55
56
 
@@ -12,13 +12,22 @@ impl PathFilter for Filter {
12
12
  fn filter(&self, pd: &PathDescription) -> bool {
13
13
  match self {
14
14
  Filter::None => false,
15
- Filter::Extension { ext } => pd
16
- .absolute
17
- .extension()
18
- .is_some_and(|x| x.to_string_lossy() == *ext),
15
+ Filter::Extension { ext } => {
16
+ tracing::trace!("testing extension `{:?}` against `{}`", pd, ext);
17
+ pd.absolute
18
+ .extension()
19
+ .is_some_and(|x| x.to_string_lossy() == *ext)
20
+ }
19
21
  Filter::Glob { glob } => {
20
22
  let target = pd.relative.unwrap_or(pd.absolute);
21
- let did_match = glob_match(glob, target.to_string_lossy().to_string().as_str());
23
+ let compare = target.to_string_lossy().to_string();
24
+ let did_match = glob_match(glob, &compare);
25
+ tracing::trace!(
26
+ "testing glob `{}` against `{}`: {}",
27
+ glob,
28
+ compare.as_str(),
29
+ did_match
30
+ );
22
31
  did_match
23
32
  }
24
33
  }
@@ -131,7 +131,7 @@ async fn test_single_file_impl() -> Result<(), Box<dyn std::error::Error>> {
131
131
  let tc = TestCase::new(Debounce::trailing_ms(10), None);
132
132
  tc.watch().await;
133
133
  tc.write_file("test_file.txt").await;
134
- let events = tc.get_events_after(Duration::from_millis(300)).await;
134
+ let events = tc.get_events_after(Duration::from_millis(500)).await;
135
135
  assert_eq!(events.len(), 2);
136
136
  assert_eq!(
137
137
  matches!(events.get(0).unwrap().kind, FsEventKind::PathAdded(..)),
@@ -29,7 +29,7 @@ impl Handler<RequestWatchPath> for FsWatcher {
29
29
 
30
30
  tracing::debug!(path = ?msg.path, "👀 watching! {} receivers", self.receivers.len());
31
31
  tracing::debug!(?self.cwd);
32
- tracing::debug!(?self.receivers);
32
+ tracing::debug!("{} receivers", self.receivers.len());
33
33
 
34
34
  let matched = msg.path == self.cwd;
35
35
 
@@ -13,7 +13,7 @@ use tokio::sync::broadcast;
13
13
  pub fn create_watcher(
14
14
  sender: Arc<broadcast::Sender<InnerChangeEvent>>,
15
15
  cwd: &Path,
16
- ) -> notify::Result<notify::FsEventWatcher> {
16
+ ) -> notify::Result<notify::RecommendedWatcher> {
17
17
  let cwd_c = cwd.to_owned();
18
18
  notify::recommended_watcher(move |res: Result<notify::Event, _>| {
19
19
  let span = tracing::span!(tracing::Level::TRACE, "raw");
@@ -247,6 +247,7 @@ fn is_auto_excluded<P: AsRef<Path>>(cwd: &P, subject: &P) -> bool {
247
247
  ".vscode",
248
248
  ".idea",
249
249
  ".sass-cache",
250
+ "bslive.log",
250
251
  ]
251
252
  .into_iter()
252
253
  .map(OsStr::new)
@@ -20,3 +20,6 @@ tracing = { workspace = true }
20
20
  thiserror = { workspace = true }
21
21
  typeshare = { workspace = true }
22
22
  nom = "7.1.3"
23
+
24
+ [dev-dependencies]
25
+ insta = { workspace = true }
@@ -62,6 +62,40 @@ fn test_deserialize_cors_false() {
62
62
  assert_eq!(opts, CorsOpts::Cors(false));
63
63
  }
64
64
 
65
+ #[test]
66
+ fn test_deserialize_3_headers_control() {
67
+ #[derive(serde::Deserialize, serde::Serialize, Debug)]
68
+ struct Config {
69
+ pub items: Vec<Route>,
70
+ }
71
+
72
+ let input = r#"
73
+ items:
74
+ - path: /api
75
+ json: [1,2]
76
+ "#;
77
+ let c: Config = serde_yaml::from_str(input).unwrap();
78
+ insta::assert_debug_snapshot!(c)
79
+ }
80
+
81
+ #[test]
82
+ fn test_deserialize_3_headers() {
83
+ #[derive(serde::Deserialize, serde::Serialize, Debug)]
84
+ struct Config {
85
+ pub items: Vec<Route>,
86
+ }
87
+
88
+ let input = r#"
89
+ items:
90
+ - path: /api
91
+ json: [1,2]
92
+ headers:
93
+ a: b
94
+ "#;
95
+ let c: Config = serde_yaml::from_str(input).unwrap();
96
+ insta::assert_debug_snapshot!(c)
97
+ }
98
+
65
99
  #[test]
66
100
  fn test_deserialize_3() {
67
101
  let input = r#"
@@ -0,0 +1,29 @@
1
+ ---
2
+ source: crates/bsnext_input/src/input_test/mod.rs
3
+ expression: c
4
+ ---
5
+ Config {
6
+ items: [
7
+ Route {
8
+ path: "/api",
9
+ cors_opts: None,
10
+ delay_opts: None,
11
+ watch_opts: Bool(
12
+ true,
13
+ ),
14
+ kind: Json {
15
+ json: JsonWrapper(
16
+ Array [
17
+ Number(1),
18
+ Number(2),
19
+ ],
20
+ ),
21
+ },
22
+ headers: Some(
23
+ {
24
+ "a": "b",
25
+ },
26
+ ),
27
+ },
28
+ ],
29
+ }