@zap-js/server 0.0.2 → 0.0.5
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.
- package/index.js +18 -14
- package/package.json +4 -9
- package/src/bin/zap.rs +0 -154
- package/src/config.rs +0 -253
- package/src/connection_pool.rs +0 -404
- package/src/error.rs +0 -380
- package/src/handler.rs +0 -89
- package/src/ipc.js +0 -10
- package/src/ipc.rs +0 -499
- package/src/lib.rs +0 -433
- package/src/metrics.rs +0 -264
- package/src/proxy.rs +0 -436
- package/src/reliability.rs +0 -917
- package/src/request.rs +0 -60
- package/src/request_id.rs +0 -97
- package/src/response.rs +0 -182
- package/src/rpc.js +0 -14
- package/src/server.rs +0 -597
- package/src/static.rs +0 -572
- package/src/types.js +0 -21
- package/src/utils.rs +0 -18
- package/src/websocket.rs +0 -429
package/src/request.rs
DELETED
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
//! Request types and utilities for ZapServer
|
|
2
|
-
|
|
3
|
-
use std::collections::HashMap;
|
|
4
|
-
use zap_core::{Request, Method};
|
|
5
|
-
|
|
6
|
-
/// Request data that can be owned and moved between threads
|
|
7
|
-
#[derive(Debug, Clone)]
|
|
8
|
-
pub struct RequestData {
|
|
9
|
-
pub method: Method,
|
|
10
|
-
pub path: String,
|
|
11
|
-
pub path_only: String,
|
|
12
|
-
pub version: String,
|
|
13
|
-
pub headers: HashMap<String, String>,
|
|
14
|
-
pub body: Vec<u8>,
|
|
15
|
-
pub params: HashMap<String, String>,
|
|
16
|
-
pub query: HashMap<String, String>,
|
|
17
|
-
pub cookies: HashMap<String, String>,
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
impl RequestData {
|
|
21
|
-
/// Create RequestData from a borrowed Request
|
|
22
|
-
pub fn from_request(req: &Request) -> Self {
|
|
23
|
-
Self {
|
|
24
|
-
method: req.method(),
|
|
25
|
-
path: req.path().to_string(),
|
|
26
|
-
path_only: req.path_only().to_string(),
|
|
27
|
-
version: req.version().to_string(),
|
|
28
|
-
headers: req.headers().iter().map(|(k, v)| (k.to_string(), v.to_string())).collect(),
|
|
29
|
-
body: req.body().to_vec(),
|
|
30
|
-
params: req.params().iter().map(|(k, v)| (k.to_string(), v.to_string())).collect(),
|
|
31
|
-
query: req.query_params().into_iter().map(|(k, v)| (k.to_string(), v.to_string())).collect(),
|
|
32
|
-
cookies: req.cookies().into_iter().map(|(k, v)| (k.to_string(), v.to_string())).collect(),
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
/// Get parameter by name
|
|
37
|
-
pub fn param(&self, name: &str) -> Option<&str> {
|
|
38
|
-
self.params.get(name).map(|s| s.as_str())
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/// Get query parameter by name
|
|
42
|
-
pub fn query(&self, name: &str) -> Option<&str> {
|
|
43
|
-
self.query.get(name).map(|s| s.as_str())
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/// Get header by name
|
|
47
|
-
pub fn header(&self, name: &str) -> Option<&str> {
|
|
48
|
-
self.headers.get(name).map(|s| s.as_str())
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/// Get cookie by name
|
|
52
|
-
pub fn cookie(&self, name: &str) -> Option<&str> {
|
|
53
|
-
self.cookies.get(name).map(|s| s.as_str())
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
/// Get body as string
|
|
57
|
-
pub fn body_string(&self) -> Result<String, std::string::FromUtf8Error> {
|
|
58
|
-
String::from_utf8(self.body.clone())
|
|
59
|
-
}
|
|
60
|
-
}
|
package/src/request_id.rs
DELETED
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
//! Request ID generation and extraction for request correlation
|
|
2
|
-
//!
|
|
3
|
-
//! Provides unique identifiers for each request that flow through
|
|
4
|
-
//! the entire system (Rust server -> IPC -> TypeScript handlers).
|
|
5
|
-
//! This enables end-to-end request tracing in logs and metrics.
|
|
6
|
-
|
|
7
|
-
use uuid::Uuid;
|
|
8
|
-
|
|
9
|
-
/// Standard header name for request ID
|
|
10
|
-
pub const REQUEST_ID_HEADER: &str = "x-request-id";
|
|
11
|
-
|
|
12
|
-
/// Generate a new UUID v4 request ID
|
|
13
|
-
#[inline]
|
|
14
|
-
pub fn generate() -> String {
|
|
15
|
-
Uuid::new_v4().to_string()
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
/// Extract request ID from headers or generate a new one
|
|
19
|
-
///
|
|
20
|
-
/// Checks for both lowercase and mixed-case header names.
|
|
21
|
-
pub fn get_or_generate(headers: &std::collections::HashMap<String, String>) -> String {
|
|
22
|
-
// Check lowercase first (normalized headers)
|
|
23
|
-
if let Some(id) = headers.get(REQUEST_ID_HEADER) {
|
|
24
|
-
if !id.is_empty() {
|
|
25
|
-
return id.clone();
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
// Check mixed-case variant
|
|
30
|
-
if let Some(id) = headers.get("X-Request-ID") {
|
|
31
|
-
if !id.is_empty() {
|
|
32
|
-
return id.clone();
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
// Check another common variant
|
|
37
|
-
if let Some(id) = headers.get("X-Request-Id") {
|
|
38
|
-
if !id.is_empty() {
|
|
39
|
-
return id.clone();
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
generate()
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/// Validate that a request ID looks reasonable
|
|
47
|
-
/// (not empty, not too long, alphanumeric + hyphens)
|
|
48
|
-
pub fn is_valid(id: &str) -> bool {
|
|
49
|
-
if id.is_empty() || id.len() > 128 {
|
|
50
|
-
return false;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
id.chars()
|
|
54
|
-
.all(|c| c.is_ascii_alphanumeric() || c == '-' || c == '_')
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
#[cfg(test)]
|
|
58
|
-
mod tests {
|
|
59
|
-
use super::*;
|
|
60
|
-
|
|
61
|
-
#[test]
|
|
62
|
-
fn test_generate_unique() {
|
|
63
|
-
let id1 = generate();
|
|
64
|
-
let id2 = generate();
|
|
65
|
-
assert_ne!(id1, id2);
|
|
66
|
-
assert!(is_valid(&id1));
|
|
67
|
-
assert!(is_valid(&id2));
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
#[test]
|
|
71
|
-
fn test_get_or_generate_extracts() {
|
|
72
|
-
let mut headers = std::collections::HashMap::new();
|
|
73
|
-
headers.insert("x-request-id".to_string(), "test-123".to_string());
|
|
74
|
-
|
|
75
|
-
let id = get_or_generate(&headers);
|
|
76
|
-
assert_eq!(id, "test-123");
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
#[test]
|
|
80
|
-
fn test_get_or_generate_creates() {
|
|
81
|
-
let headers = std::collections::HashMap::new();
|
|
82
|
-
let id = get_or_generate(&headers);
|
|
83
|
-
assert!(is_valid(&id));
|
|
84
|
-
// Should be UUID format
|
|
85
|
-
assert!(id.contains('-'));
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
#[test]
|
|
89
|
-
fn test_is_valid() {
|
|
90
|
-
assert!(is_valid("abc-123"));
|
|
91
|
-
assert!(is_valid("550e8400-e29b-41d4-a716-446655440000"));
|
|
92
|
-
assert!(is_valid("request_123"));
|
|
93
|
-
assert!(!is_valid(""));
|
|
94
|
-
assert!(!is_valid(&"a".repeat(200)));
|
|
95
|
-
assert!(!is_valid("test@123")); // @ not allowed
|
|
96
|
-
}
|
|
97
|
-
}
|
package/src/response.rs
DELETED
|
@@ -1,182 +0,0 @@
|
|
|
1
|
-
//! Response types and utilities for ZapServer
|
|
2
|
-
|
|
3
|
-
use std::collections::HashMap;
|
|
4
|
-
use std::path::PathBuf;
|
|
5
|
-
|
|
6
|
-
use bytes::Bytes;
|
|
7
|
-
use serde::Serialize;
|
|
8
|
-
|
|
9
|
-
use zap_core::{Response, StatusCode, ResponseBody};
|
|
10
|
-
|
|
11
|
-
/// Streaming response data
|
|
12
|
-
#[derive(Debug)]
|
|
13
|
-
pub struct StreamingResponse {
|
|
14
|
-
/// HTTP status code
|
|
15
|
-
pub status: u16,
|
|
16
|
-
/// Response headers
|
|
17
|
-
pub headers: HashMap<String, String>,
|
|
18
|
-
/// Collected body chunks (base64 decoded)
|
|
19
|
-
pub chunks: Vec<Vec<u8>>,
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
impl StreamingResponse {
|
|
23
|
-
/// Create a new streaming response
|
|
24
|
-
pub fn new(status: u16, headers: HashMap<String, String>) -> Self {
|
|
25
|
-
Self {
|
|
26
|
-
status,
|
|
27
|
-
headers,
|
|
28
|
-
chunks: Vec::new(),
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/// Add a chunk to the response
|
|
33
|
-
pub fn add_chunk(&mut self, data: Vec<u8>) {
|
|
34
|
-
self.chunks.push(data);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/// Get the complete body as bytes
|
|
38
|
-
pub fn body_bytes(&self) -> Vec<u8> {
|
|
39
|
-
let total_len: usize = self.chunks.iter().map(|c| c.len()).sum();
|
|
40
|
-
let mut body = Vec::with_capacity(total_len);
|
|
41
|
-
for chunk in &self.chunks {
|
|
42
|
-
body.extend_from_slice(chunk);
|
|
43
|
-
}
|
|
44
|
-
body
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
/// Get the complete body as a string (lossy conversion)
|
|
48
|
-
pub fn body_string(&self) -> String {
|
|
49
|
-
String::from_utf8_lossy(&self.body_bytes()).to_string()
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
/// Zap response types with auto-serialization
|
|
54
|
-
#[derive(Debug)]
|
|
55
|
-
pub enum ZapResponse {
|
|
56
|
-
/// Plain text response
|
|
57
|
-
Text(String),
|
|
58
|
-
/// HTML response
|
|
59
|
-
Html(String),
|
|
60
|
-
/// JSON response (auto-serialized)
|
|
61
|
-
Json(serde_json::Value),
|
|
62
|
-
/// JSON response with custom status code
|
|
63
|
-
JsonWithStatus(serde_json::Value, u16),
|
|
64
|
-
/// Binary response
|
|
65
|
-
Bytes(Bytes),
|
|
66
|
-
/// File response
|
|
67
|
-
File(PathBuf),
|
|
68
|
-
/// Custom response with full control
|
|
69
|
-
Custom(Response),
|
|
70
|
-
/// Redirect response
|
|
71
|
-
Redirect(String),
|
|
72
|
-
/// Empty response with status code
|
|
73
|
-
Status(StatusCode),
|
|
74
|
-
/// Streaming response (collected chunks)
|
|
75
|
-
Stream(StreamingResponse),
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
/// JSON response wrapper for auto-serialization
|
|
79
|
-
#[derive(Debug)]
|
|
80
|
-
pub struct Json<T>(pub T);
|
|
81
|
-
|
|
82
|
-
impl<T: Serialize> From<Json<T>> for ZapResponse {
|
|
83
|
-
fn from(json: Json<T>) -> Self {
|
|
84
|
-
match serde_json::to_value(json.0) {
|
|
85
|
-
Ok(value) => ZapResponse::Json(value),
|
|
86
|
-
Err(_) => ZapResponse::Custom(
|
|
87
|
-
Response::internal_server_error("Failed to serialize JSON"),
|
|
88
|
-
),
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
impl ZapResponse {
|
|
94
|
-
/// Convert ZapResponse to hyper Response
|
|
95
|
-
pub fn to_hyper_response(&self) -> hyper::Response<String> {
|
|
96
|
-
match self {
|
|
97
|
-
ZapResponse::Text(text) => hyper::Response::builder()
|
|
98
|
-
.status(200)
|
|
99
|
-
.header("Content-Type", "text/plain; charset=utf-8")
|
|
100
|
-
.body(text.clone())
|
|
101
|
-
.unwrap(),
|
|
102
|
-
ZapResponse::Html(html) => hyper::Response::builder()
|
|
103
|
-
.status(200)
|
|
104
|
-
.header("Content-Type", "text/html; charset=utf-8")
|
|
105
|
-
.body(html.clone())
|
|
106
|
-
.unwrap(),
|
|
107
|
-
ZapResponse::Json(json) => {
|
|
108
|
-
let body = serde_json::to_string(json).unwrap_or_else(|_| {
|
|
109
|
-
r#"{"error": "Failed to serialize JSON"}"#.to_string()
|
|
110
|
-
});
|
|
111
|
-
hyper::Response::builder()
|
|
112
|
-
.status(200)
|
|
113
|
-
.header("Content-Type", "application/json")
|
|
114
|
-
.body(body)
|
|
115
|
-
.unwrap()
|
|
116
|
-
}
|
|
117
|
-
ZapResponse::JsonWithStatus(json, status) => {
|
|
118
|
-
let body = serde_json::to_string(json).unwrap_or_else(|_| {
|
|
119
|
-
r#"{"error": "Failed to serialize JSON"}"#.to_string()
|
|
120
|
-
});
|
|
121
|
-
hyper::Response::builder()
|
|
122
|
-
.status(*status)
|
|
123
|
-
.header("Content-Type", "application/json")
|
|
124
|
-
.body(body)
|
|
125
|
-
.unwrap()
|
|
126
|
-
}
|
|
127
|
-
ZapResponse::Bytes(bytes) => hyper::Response::builder()
|
|
128
|
-
.status(200)
|
|
129
|
-
.header("Content-Type", "application/octet-stream")
|
|
130
|
-
.body(String::from_utf8_lossy(bytes).to_string())
|
|
131
|
-
.unwrap(),
|
|
132
|
-
ZapResponse::Custom(response) => {
|
|
133
|
-
let status = response.status.as_u16();
|
|
134
|
-
let mut builder = hyper::Response::builder().status(status);
|
|
135
|
-
|
|
136
|
-
for (key, value) in &response.headers {
|
|
137
|
-
builder = builder.header(key, value);
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
let body = match &response.body {
|
|
141
|
-
ResponseBody::Empty => String::new(),
|
|
142
|
-
ResponseBody::Text(text) => text.clone(),
|
|
143
|
-
ResponseBody::Bytes(bytes) => {
|
|
144
|
-
String::from_utf8_lossy(bytes).to_string()
|
|
145
|
-
}
|
|
146
|
-
};
|
|
147
|
-
|
|
148
|
-
builder.body(body).unwrap()
|
|
149
|
-
}
|
|
150
|
-
ZapResponse::Redirect(location) => hyper::Response::builder()
|
|
151
|
-
.status(302)
|
|
152
|
-
.header("Location", location)
|
|
153
|
-
.body(String::new())
|
|
154
|
-
.unwrap(),
|
|
155
|
-
ZapResponse::Status(status) => hyper::Response::builder()
|
|
156
|
-
.status(status.as_u16())
|
|
157
|
-
.body(String::new())
|
|
158
|
-
.unwrap(),
|
|
159
|
-
ZapResponse::File(_path) => {
|
|
160
|
-
// File serving would be implemented here
|
|
161
|
-
// For now, return not implemented
|
|
162
|
-
hyper::Response::builder()
|
|
163
|
-
.status(501)
|
|
164
|
-
.body("File serving not yet implemented".to_string())
|
|
165
|
-
.unwrap()
|
|
166
|
-
}
|
|
167
|
-
ZapResponse::Stream(stream_response) => {
|
|
168
|
-
let mut builder = hyper::Response::builder()
|
|
169
|
-
.status(stream_response.status);
|
|
170
|
-
|
|
171
|
-
// Add all headers from the streaming response
|
|
172
|
-
for (key, value) in &stream_response.headers {
|
|
173
|
-
builder = builder.header(key, value);
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
// Convert chunks to body
|
|
177
|
-
let body = stream_response.body_string();
|
|
178
|
-
builder.body(body).unwrap()
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
}
|
package/src/rpc.js
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @zap-js/server/rpc
|
|
3
|
-
*
|
|
4
|
-
* RPC client for server communication
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { rpcCall } from '../../client/internal/runtime/src/rpc-client.js';
|
|
8
|
-
|
|
9
|
-
// RPC object with call method
|
|
10
|
-
const rpc = {
|
|
11
|
-
call: rpcCall,
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
export default rpc;
|