@zap-js/server 0.0.2 → 0.0.4

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/src/error.rs DELETED
@@ -1,380 +0,0 @@
1
- //! Comprehensive error handling with proper context and recovery
2
- //!
3
- //! Type-safe error handling throughout the application with proper
4
- //! error propagation and context preservation.
5
- //!
6
- //! ## Error Structure
7
- //! Each error has:
8
- //! - A machine-readable **code** (e.g., "HANDLER_ERROR")
9
- //! - A human-readable **message**
10
- //! - An HTTP **status code**
11
- //! - A unique **digest** for log correlation
12
- //! - Optional **details** for additional context
13
-
14
- use serde::{Deserialize, Serialize};
15
- use std::io;
16
- use thiserror::Error;
17
- use uuid::Uuid;
18
-
19
- /// Zap error type covering all possible failure modes
20
- #[derive(Debug, Error)]
21
- pub enum ZapError {
22
- /// HTTP server errors
23
- #[error("HTTP error: {message}")]
24
- Http {
25
- message: String,
26
- #[source]
27
- source: Option<Box<dyn std::error::Error + Send + Sync>>,
28
- },
29
-
30
- /// Route not found (404)
31
- #[error("Route not found: {path}")]
32
- RouteNotFound { path: String },
33
-
34
- /// Handler execution errors
35
- #[error("Handler error: {message}")]
36
- Handler {
37
- message: String,
38
- handler_id: Option<String>,
39
- },
40
-
41
- /// IPC/Socket errors
42
- #[error("IPC error: {message}")]
43
- Ipc { message: String },
44
-
45
- /// Configuration errors
46
- #[error("Configuration error: {message}")]
47
- Config { message: String },
48
-
49
- /// I/O errors
50
- #[error("I/O error: {0}")]
51
- Io(#[from] io::Error),
52
-
53
- /// Serialization errors
54
- #[error("Serialization error: {0}")]
55
- Serialization(#[from] serde_json::Error),
56
-
57
- /// Validation errors (400)
58
- #[error("Validation error: {message}")]
59
- Validation {
60
- message: String,
61
- field: Option<String>,
62
- },
63
-
64
- /// Authentication required (401)
65
- #[error("Authentication required: {message}")]
66
- Unauthorized { message: String },
67
-
68
- /// Access forbidden (403)
69
- #[error("Access forbidden: {message}")]
70
- Forbidden { message: String },
71
-
72
- /// Timeout errors (408/504)
73
- #[error("Timeout: {message}")]
74
- Timeout { message: String, timeout_ms: u64 },
75
-
76
- /// Rate limit exceeded (429)
77
- #[error("Rate limit exceeded")]
78
- RateLimited { retry_after_secs: u64 },
79
-
80
- /// Invalid state
81
- #[error("Invalid state: {0}")]
82
- InvalidState(String),
83
-
84
- /// Internal error (500)
85
- #[error("Internal error: {0}")]
86
- Internal(String),
87
-
88
- /// WebSocket errors
89
- #[error("WebSocket error: {message}")]
90
- WebSocket { message: String },
91
- }
92
-
93
- impl ZapError {
94
- /// Get the machine-readable error code
95
- pub fn code(&self) -> &'static str {
96
- match self {
97
- ZapError::Http { .. } => "HTTP_ERROR",
98
- ZapError::RouteNotFound { .. } => "ROUTE_NOT_FOUND",
99
- ZapError::Handler { .. } => "HANDLER_ERROR",
100
- ZapError::Ipc { .. } => "IPC_ERROR",
101
- ZapError::Config { .. } => "CONFIG_ERROR",
102
- ZapError::Io(_) => "IO_ERROR",
103
- ZapError::Serialization(_) => "SERIALIZATION_ERROR",
104
- ZapError::Validation { .. } => "VALIDATION_ERROR",
105
- ZapError::Unauthorized { .. } => "UNAUTHORIZED",
106
- ZapError::Forbidden { .. } => "FORBIDDEN",
107
- ZapError::Timeout { .. } => "TIMEOUT",
108
- ZapError::RateLimited { .. } => "RATE_LIMITED",
109
- ZapError::InvalidState(_) => "INVALID_STATE",
110
- ZapError::Internal(_) => "INTERNAL_ERROR",
111
- ZapError::WebSocket { .. } => "WEBSOCKET_ERROR",
112
- }
113
- }
114
-
115
- /// Get the appropriate HTTP status code
116
- pub fn status_code(&self) -> u16 {
117
- match self {
118
- ZapError::Http { .. } => 500,
119
- ZapError::RouteNotFound { .. } => 404,
120
- ZapError::Handler { .. } => 500,
121
- ZapError::Ipc { .. } => 502,
122
- ZapError::Config { .. } => 500,
123
- ZapError::Io(_) => 500,
124
- ZapError::Serialization(_) => 400,
125
- ZapError::Validation { .. } => 400,
126
- ZapError::Unauthorized { .. } => 401,
127
- ZapError::Forbidden { .. } => 403,
128
- ZapError::Timeout { .. } => 504,
129
- ZapError::RateLimited { .. } => 429,
130
- ZapError::InvalidState(_) => 500,
131
- ZapError::Internal(_) => 500,
132
- ZapError::WebSocket { .. } => 500,
133
- }
134
- }
135
-
136
- /// Convert to a structured error response
137
- pub fn to_error_response(&self) -> ErrorResponse {
138
- let digest = Uuid::new_v4().to_string();
139
-
140
- ErrorResponse {
141
- error: true,
142
- code: self.code().to_string(),
143
- message: self.to_string(),
144
- status: self.status_code(),
145
- digest,
146
- details: self.details(),
147
- }
148
- }
149
-
150
- /// Get additional error-specific details
151
- fn details(&self) -> Option<serde_json::Value> {
152
- match self {
153
- ZapError::Validation { field, .. } => {
154
- field.as_ref().map(|f| serde_json::json!({ "field": f }))
155
- }
156
- ZapError::RateLimited { retry_after_secs } => {
157
- Some(serde_json::json!({ "retryAfter": retry_after_secs }))
158
- }
159
- ZapError::Timeout { timeout_ms, .. } => {
160
- Some(serde_json::json!({ "timeoutMs": timeout_ms }))
161
- }
162
- ZapError::RouteNotFound { path } => Some(serde_json::json!({ "path": path })),
163
- ZapError::Handler { handler_id, .. } => {
164
- handler_id.as_ref().map(|id| serde_json::json!({ "handlerId": id }))
165
- }
166
- _ => None,
167
- }
168
- }
169
-
170
- // Convenience constructors
171
-
172
- /// Create an HTTP error
173
- pub fn http(message: impl Into<String>) -> Self {
174
- ZapError::Http {
175
- message: message.into(),
176
- source: None,
177
- }
178
- }
179
-
180
- /// Create a route not found error
181
- pub fn route_not_found(path: impl Into<String>) -> Self {
182
- ZapError::RouteNotFound { path: path.into() }
183
- }
184
-
185
- /// Create a handler error
186
- pub fn handler(message: impl Into<String>) -> Self {
187
- ZapError::Handler {
188
- message: message.into(),
189
- handler_id: None,
190
- }
191
- }
192
-
193
- /// Create a handler error with handler ID
194
- pub fn handler_with_id(message: impl Into<String>, handler_id: impl Into<String>) -> Self {
195
- ZapError::Handler {
196
- message: message.into(),
197
- handler_id: Some(handler_id.into()),
198
- }
199
- }
200
-
201
- /// Create an IPC error
202
- pub fn ipc(message: impl Into<String>) -> Self {
203
- ZapError::Ipc {
204
- message: message.into(),
205
- }
206
- }
207
-
208
- /// Create a config error
209
- pub fn config(message: impl Into<String>) -> Self {
210
- ZapError::Config {
211
- message: message.into(),
212
- }
213
- }
214
-
215
- /// Create a validation error
216
- pub fn validation(message: impl Into<String>) -> Self {
217
- ZapError::Validation {
218
- message: message.into(),
219
- field: None,
220
- }
221
- }
222
-
223
- /// Create a validation error with field name
224
- pub fn validation_field(message: impl Into<String>, field: impl Into<String>) -> Self {
225
- ZapError::Validation {
226
- message: message.into(),
227
- field: Some(field.into()),
228
- }
229
- }
230
-
231
- /// Create an unauthorized error
232
- pub fn unauthorized(message: impl Into<String>) -> Self {
233
- ZapError::Unauthorized {
234
- message: message.into(),
235
- }
236
- }
237
-
238
- /// Create a forbidden error
239
- pub fn forbidden(message: impl Into<String>) -> Self {
240
- ZapError::Forbidden {
241
- message: message.into(),
242
- }
243
- }
244
-
245
- /// Create a timeout error
246
- pub fn timeout(message: impl Into<String>, timeout_ms: u64) -> Self {
247
- ZapError::Timeout {
248
- message: message.into(),
249
- timeout_ms,
250
- }
251
- }
252
-
253
- /// Create a rate limited error
254
- pub fn rate_limited(retry_after_secs: u64) -> Self {
255
- ZapError::RateLimited { retry_after_secs }
256
- }
257
-
258
- /// Create a WebSocket error
259
- pub fn websocket(message: impl Into<String>) -> Self {
260
- ZapError::WebSocket {
261
- message: message.into(),
262
- }
263
- }
264
- }
265
-
266
- impl From<String> for ZapError {
267
- fn from(msg: String) -> Self {
268
- Self::Internal(msg)
269
- }
270
- }
271
-
272
- impl From<&str> for ZapError {
273
- fn from(msg: &str) -> Self {
274
- Self::Internal(msg.to_string())
275
- }
276
- }
277
-
278
- /// Structured error response for JSON serialization
279
- #[derive(Debug, Clone, Serialize, Deserialize)]
280
- pub struct ErrorResponse {
281
- /// Always true for error responses
282
- pub error: bool,
283
-
284
- /// Machine-readable error code (e.g., "HANDLER_ERROR")
285
- pub code: String,
286
-
287
- /// Human-readable error message
288
- pub message: String,
289
-
290
- /// HTTP status code
291
- pub status: u16,
292
-
293
- /// Unique error identifier for log correlation
294
- pub digest: String,
295
-
296
- /// Additional error-specific details
297
- #[serde(skip_serializing_if = "Option::is_none")]
298
- pub details: Option<serde_json::Value>,
299
- }
300
-
301
- impl ErrorResponse {
302
- /// Create a new error response
303
- pub fn new(code: impl Into<String>, message: impl Into<String>, status: u16) -> Self {
304
- Self {
305
- error: true,
306
- code: code.into(),
307
- message: message.into(),
308
- status,
309
- digest: Uuid::new_v4().to_string(),
310
- details: None,
311
- }
312
- }
313
-
314
- /// Add details to the error response
315
- pub fn with_details(mut self, details: serde_json::Value) -> Self {
316
- self.details = Some(details);
317
- self
318
- }
319
-
320
- /// Convert to JSON string
321
- pub fn to_json(&self) -> String {
322
- serde_json::to_string(self).unwrap_or_else(|_| {
323
- format!(
324
- r#"{{"error":true,"code":"{}","message":"{}","status":{},"digest":"{}"}}"#,
325
- self.code, self.message, self.status, self.digest
326
- )
327
- })
328
- }
329
- }
330
-
331
- /// Convenient Result type for Zap operations
332
- pub type ZapResult<T> = Result<T, ZapError>;
333
-
334
- #[cfg(test)]
335
- mod tests {
336
- use super::*;
337
-
338
- #[test]
339
- fn test_error_codes() {
340
- assert_eq!(ZapError::route_not_found("/test").code(), "ROUTE_NOT_FOUND");
341
- assert_eq!(ZapError::handler("test").code(), "HANDLER_ERROR");
342
- assert_eq!(ZapError::validation("test").code(), "VALIDATION_ERROR");
343
- assert_eq!(ZapError::rate_limited(60).code(), "RATE_LIMITED");
344
- }
345
-
346
- #[test]
347
- fn test_status_codes() {
348
- assert_eq!(ZapError::route_not_found("/test").status_code(), 404);
349
- assert_eq!(ZapError::validation("test").status_code(), 400);
350
- assert_eq!(ZapError::unauthorized("test").status_code(), 401);
351
- assert_eq!(ZapError::forbidden("test").status_code(), 403);
352
- assert_eq!(ZapError::rate_limited(60).status_code(), 429);
353
- assert_eq!(ZapError::timeout("test", 5000).status_code(), 504);
354
- }
355
-
356
- #[test]
357
- fn test_error_response() {
358
- let error = ZapError::validation_field("Invalid email", "email");
359
- let response = error.to_error_response();
360
-
361
- assert!(response.error);
362
- assert_eq!(response.code, "VALIDATION_ERROR");
363
- assert_eq!(response.status, 400);
364
- assert!(!response.digest.is_empty());
365
- assert!(response.details.is_some());
366
-
367
- let details = response.details.unwrap();
368
- assert_eq!(details["field"], "email");
369
- }
370
-
371
- #[test]
372
- fn test_error_response_json() {
373
- let response = ErrorResponse::new("TEST_ERROR", "Test message", 500);
374
- let json = response.to_json();
375
-
376
- assert!(json.contains("TEST_ERROR"));
377
- assert!(json.contains("Test message"));
378
- assert!(json.contains("500"));
379
- }
380
- }
package/src/handler.rs DELETED
@@ -1,89 +0,0 @@
1
- //! Handler traits and implementations for ZapServer
2
-
3
- use std::future::Future;
4
- use std::pin::Pin;
5
-
6
- use crate::error::ZapError;
7
- use crate::response::ZapResponse;
8
- use zap_core::Request;
9
- use crate::request::RequestData;
10
-
11
- /// Handler trait for request processing
12
- pub trait Handler {
13
- /// Handle the request and return a response
14
- fn handle<'a>(
15
- &'a self,
16
- req: Request<'a>,
17
- ) -> Pin<Box<dyn Future<Output = Result<ZapResponse, ZapError>> + Send + 'a>>;
18
- }
19
-
20
- /// Implement Handler for simple closures that return strings
21
- impl<F> Handler for F
22
- where
23
- F: Fn() -> &'static str + Send + Sync,
24
- {
25
- fn handle<'a>(
26
- &'a self,
27
- _req: Request<'a>,
28
- ) -> Pin<Box<dyn Future<Output = Result<ZapResponse, ZapError>> + Send + 'a>> {
29
- let response = self();
30
- Box::pin(async move { Ok(ZapResponse::Text(response.to_string())) })
31
- }
32
- }
33
-
34
- /// Simple handler that returns a ZapResponse
35
- pub struct SimpleHandler<F> {
36
- func: F,
37
- }
38
-
39
- impl<F> SimpleHandler<F> {
40
- pub fn new(func: F) -> Self {
41
- Self { func }
42
- }
43
- }
44
-
45
- impl<F> Handler for SimpleHandler<F>
46
- where
47
- F: Fn() -> String + Send + Sync,
48
- {
49
- fn handle<'a>(
50
- &'a self,
51
- _req: Request<'a>,
52
- ) -> Pin<Box<dyn Future<Output = Result<ZapResponse, ZapError>> + Send + 'a>> {
53
- let response = (self.func)();
54
- Box::pin(async move { Ok(ZapResponse::Text(response)) })
55
- }
56
- }
57
-
58
- /// Async handler wrapper
59
- pub struct AsyncHandler<F> {
60
- func: F,
61
- }
62
-
63
- impl<F> AsyncHandler<F> {
64
- pub fn new(func: F) -> Self {
65
- Self { func }
66
- }
67
- }
68
-
69
- impl<F, Fut> Handler for AsyncHandler<F>
70
- where
71
- F: Fn(RequestData) -> Fut + Send + Sync,
72
- Fut: Future<Output = ZapResponse> + Send,
73
- {
74
- fn handle<'a>(
75
- &'a self,
76
- req: Request<'a>,
77
- ) -> Pin<Box<dyn Future<Output = Result<ZapResponse, ZapError>> + Send + 'a>> {
78
- // Extract request data that can be moved
79
- let req_data = RequestData::from_request(&req);
80
-
81
- Box::pin(async move {
82
- let response = (self.func)(req_data).await;
83
- Ok(response)
84
- })
85
- }
86
- }
87
-
88
- /// Type alias for boxed async handlers
89
- pub type BoxedHandler = Box<dyn Handler + Send + Sync>;
package/src/ipc.js DELETED
@@ -1,10 +0,0 @@
1
- /**
2
- * @zap-js/server/ipc
3
- *
4
- * IPC client for advanced server communication
5
- */
6
-
7
- import { IpcClient } from '../../client/internal/runtime/src/ipc-client.js';
8
-
9
- // Export IPC client directly
10
- export default IpcClient;