@feltdb/core 0.5.7 → 0.6.1

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 (49) hide show
  1. package/dist/cli/commands.js +110 -5
  2. package/dist/cli/index.js +1 -1
  3. package/dist/cli/workspace-integration.js +127 -0
  4. package/dist/create/cli.js +1 -1
  5. package/dist/create/create.js +21 -0
  6. package/dist/create/package-versions.js +1 -1
  7. package/dist/create/server-source/Cargo.lock +10 -0
  8. package/dist/create/server-source/crates/feltdb-server/Cargo.toml +1 -0
  9. package/dist/create/server-source/crates/feltdb-server/src/app_state.rs +5 -1
  10. package/dist/create/server-source/crates/feltdb-server/src/authenticated_principal.rs +273 -0
  11. package/dist/create/server-source/crates/feltdb-server/src/certification_harness.rs +528 -0
  12. package/dist/create/server-source/crates/feltdb-server/src/delegation_token.rs +472 -0
  13. package/dist/create/server-source/crates/feltdb-server/src/durable_operations.rs +992 -0
  14. package/dist/create/server-source/crates/feltdb-server/src/lib.rs +9 -0
  15. package/dist/create/server-source/crates/feltdb-server/src/main.rs +157 -9
  16. package/dist/create/server-source/crates/feltdb-server/src/managed_diagnostics.rs +413 -0
  17. package/dist/create/server-source/crates/feltdb-server/src/membership_policy.rs +488 -0
  18. package/dist/create/server-source/crates/feltdb-server/src/snapshot_cursor.rs +378 -0
  19. package/dist/create/server-source/crates/feltdb-server/src/tenancy.rs +49 -0
  20. package/dist/create/server-source/crates/feltdb-server/src/tenant_policies.rs +525 -0
  21. package/dist/create/server-source/crates/feltdb-server/src/transaction_recovery.rs +461 -0
  22. package/dist/create/template/dot-feltdb-README.md +58 -0
  23. package/dist/create/workspace-initialization.js +77 -0
  24. package/dist/index.d.ts +6 -0
  25. package/dist/index.d.ts.map +1 -1
  26. package/dist/index.js +6 -0
  27. package/dist/studio-app/assets/{feltdb_wasm-h9mxesnH.js → feltdb_wasm-B4wq4mqp.js} +1 -1
  28. package/dist/studio-app/assets/feltdb_wasm_bg-Ceyi7l21.wasm +0 -0
  29. package/dist/studio-app/assets/{index-B_EMnTaE.js → index-LQmvJSq6.js} +2 -2
  30. package/dist/studio-app/index.html +1 -1
  31. package/dist/telemetry.js +1 -1
  32. package/dist/wasm/feltdb_wasm_bg.wasm +0 -0
  33. package/dist/workspace/development-node.d.ts +42 -0
  34. package/dist/workspace/development-node.d.ts.map +1 -0
  35. package/dist/workspace/development-node.js +208 -0
  36. package/dist/workspace/index.d.ts +20 -0
  37. package/dist/workspace/index.d.ts.map +1 -0
  38. package/dist/workspace/index.js +16 -0
  39. package/dist/workspace/workspace-connection.d.ts +174 -0
  40. package/dist/workspace/workspace-connection.d.ts.map +1 -0
  41. package/dist/workspace/workspace-connection.js +290 -0
  42. package/dist/workspace/workspace-identity.d.ts +13 -0
  43. package/dist/workspace/workspace-identity.d.ts.map +1 -0
  44. package/dist/workspace/workspace-identity.js +70 -0
  45. package/dist/workspace/workspace-types.d.ts +82 -0
  46. package/dist/workspace/workspace-types.d.ts.map +1 -0
  47. package/dist/workspace/workspace-types.js +7 -0
  48. package/package.json +5 -1
  49. package/dist/studio-app/assets/feltdb_wasm_bg-B8U4A1n1.wasm +0 -0
@@ -0,0 +1,413 @@
1
+ use serde::{Deserialize, Serialize};
2
+ use std::collections::HashMap;
3
+
4
+ /// Engine version for FeltDB.
5
+ pub const ENGINE_VERSION: &str = "0.2.0";
6
+
7
+ /// API protocol version for compatibility tracking.
8
+ pub const PROTOCOL_VERSION: u32 = 1;
9
+
10
+ /// Storage format identifier.
11
+ pub const STORAGE_FORMAT: &str = "feltdb-v2";
12
+
13
+ /// Environment type.
14
+ #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
15
+ #[serde(rename_all = "lowercase")]
16
+ pub enum Environment {
17
+ Dev,
18
+ Staging,
19
+ Prod,
20
+ }
21
+
22
+ /// Health status for subsystems.
23
+ #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
24
+ #[serde(rename_all = "lowercase")]
25
+ pub enum HealthStatus {
26
+ Healthy,
27
+ Degraded,
28
+ Unhealthy,
29
+ }
30
+
31
+ impl HealthStatus {
32
+ pub fn is_healthy(&self) -> bool {
33
+ *self == HealthStatus::Healthy
34
+ }
35
+ }
36
+
37
+ /// Recovery status and health.
38
+ #[derive(Debug, Clone, Serialize, Deserialize)]
39
+ pub struct RecoveryStatus {
40
+ /// Overall recovery system health.
41
+ pub status: HealthStatus,
42
+
43
+ /// Transaction recovery health.
44
+ pub transaction_recovery: HealthStatus,
45
+
46
+ /// Durable operation recovery health.
47
+ pub durable_operations: HealthStatus,
48
+
49
+ /// Last recovery check timestamp (unix seconds).
50
+ pub last_check_at: u64,
51
+
52
+ /// Details about any issues (empty if healthy).
53
+ pub issues: Vec<String>,
54
+
55
+ /// Metrics: count of operations awaiting recovery.
56
+ pub pending_recovery_count: usize,
57
+
58
+ /// Metrics: count of failed recovery attempts (recent).
59
+ pub failed_recovery_attempts: usize,
60
+ }
61
+
62
+ impl RecoveryStatus {
63
+ pub fn is_healthy(&self) -> bool {
64
+ self.status.is_healthy()
65
+ }
66
+ }
67
+
68
+ /// Diagnostic information about server health and status.
69
+ #[derive(Debug, Clone, Serialize, Deserialize)]
70
+ pub struct DiagnosticsReport {
71
+ /// FeltDB engine version.
72
+ pub engine_version: String,
73
+
74
+ /// Git commit hash of this deployment.
75
+ pub server_commit: String,
76
+
77
+ /// API protocol version.
78
+ pub protocol_version: u32,
79
+
80
+ /// Storage format identifier.
81
+ pub storage_format: String,
82
+
83
+ /// Deployment environment.
84
+ pub environment: Environment,
85
+
86
+ /// Overall server health status.
87
+ pub status: HealthStatus,
88
+
89
+ /// Recovery system status and health.
90
+ pub recovery: RecoveryStatus,
91
+
92
+ /// Uptime in seconds since server start.
93
+ pub uptime_seconds: u64,
94
+
95
+ /// Timestamp of this report (unix seconds).
96
+ pub reported_at: u64,
97
+
98
+ /// Optional detailed metrics by subsystem.
99
+ pub subsystems: HashMap<String, SubsystemMetrics>,
100
+ }
101
+
102
+ /// Metrics for a single subsystem.
103
+ #[derive(Debug, Clone, Serialize, Deserialize)]
104
+ pub struct SubsystemMetrics {
105
+ pub name: String,
106
+ pub status: HealthStatus,
107
+ pub request_count: u64,
108
+ pub error_count: u64,
109
+ pub avg_latency_ms: f64,
110
+ pub details: HashMap<String, serde_json::Value>,
111
+ }
112
+
113
+ /// Diagnostics collector and reporter.
114
+ pub struct DiagnosticsCollector {
115
+ engine_version: String,
116
+ server_commit: String,
117
+ protocol_version: u32,
118
+ storage_format: String,
119
+ environment: Environment,
120
+ start_time: u64,
121
+ }
122
+
123
+ impl DiagnosticsCollector {
124
+ /// Creates a new diagnostics collector.
125
+ pub fn new(
126
+ server_commit: String,
127
+ environment: Environment,
128
+ ) -> Self {
129
+ Self {
130
+ engine_version: ENGINE_VERSION.to_string(),
131
+ server_commit,
132
+ protocol_version: PROTOCOL_VERSION,
133
+ storage_format: STORAGE_FORMAT.to_string(),
134
+ environment,
135
+ start_time: now(),
136
+ }
137
+ }
138
+
139
+ /// Generates a diagnostics report.
140
+ pub fn collect_report(
141
+ &self,
142
+ recovery_status: RecoveryStatus,
143
+ ) -> DiagnosticsReport {
144
+ let uptime = now() - self.start_time;
145
+ let overall_status = if recovery_status.status.is_healthy() {
146
+ HealthStatus::Healthy
147
+ } else {
148
+ HealthStatus::Degraded
149
+ };
150
+
151
+ DiagnosticsReport {
152
+ engine_version: self.engine_version.clone(),
153
+ server_commit: self.server_commit.clone(),
154
+ protocol_version: self.protocol_version,
155
+ storage_format: self.storage_format.clone(),
156
+ environment: self.environment,
157
+ status: overall_status,
158
+ recovery: recovery_status,
159
+ uptime_seconds: uptime,
160
+ reported_at: now(),
161
+ subsystems: HashMap::new(),
162
+ }
163
+ }
164
+
165
+ /// Adds subsystem metrics to the report.
166
+ pub fn add_subsystem_metrics(
167
+ report: &mut DiagnosticsReport,
168
+ name: String,
169
+ status: HealthStatus,
170
+ request_count: u64,
171
+ error_count: u64,
172
+ avg_latency_ms: f64,
173
+ details: HashMap<String, serde_json::Value>,
174
+ ) {
175
+ report.subsystems.insert(
176
+ name.clone(),
177
+ SubsystemMetrics {
178
+ name,
179
+ status,
180
+ request_count,
181
+ error_count,
182
+ avg_latency_ms,
183
+ details,
184
+ },
185
+ );
186
+ }
187
+ }
188
+
189
+ impl Default for RecoveryStatus {
190
+ fn default() -> Self {
191
+ Self {
192
+ status: HealthStatus::Healthy,
193
+ transaction_recovery: HealthStatus::Healthy,
194
+ durable_operations: HealthStatus::Healthy,
195
+ last_check_at: now(),
196
+ issues: vec![],
197
+ pending_recovery_count: 0,
198
+ failed_recovery_attempts: 0,
199
+ }
200
+ }
201
+ }
202
+
203
+ /// Builder for RecoveryStatus for easy construction.
204
+ pub struct RecoveryStatusBuilder {
205
+ status: HealthStatus,
206
+ transaction_recovery: HealthStatus,
207
+ durable_operations: HealthStatus,
208
+ issues: Vec<String>,
209
+ pending_recovery_count: usize,
210
+ failed_recovery_attempts: usize,
211
+ }
212
+
213
+ impl RecoveryStatusBuilder {
214
+ pub fn new() -> Self {
215
+ Self {
216
+ status: HealthStatus::Healthy,
217
+ transaction_recovery: HealthStatus::Healthy,
218
+ durable_operations: HealthStatus::Healthy,
219
+ issues: vec![],
220
+ pending_recovery_count: 0,
221
+ failed_recovery_attempts: 0,
222
+ }
223
+ }
224
+
225
+ pub fn with_transaction_recovery(mut self, status: HealthStatus) -> Self {
226
+ self.transaction_recovery = status;
227
+ self
228
+ }
229
+
230
+ pub fn with_durable_operations(mut self, status: HealthStatus) -> Self {
231
+ self.durable_operations = status;
232
+ self
233
+ }
234
+
235
+ pub fn with_issue(mut self, issue: String) -> Self {
236
+ self.issues.push(issue);
237
+ if self.status == HealthStatus::Healthy {
238
+ self.status = HealthStatus::Degraded;
239
+ }
240
+ self
241
+ }
242
+
243
+ pub fn with_pending_recovery_count(mut self, count: usize) -> Self {
244
+ self.pending_recovery_count = count;
245
+ self
246
+ }
247
+
248
+ pub fn with_failed_attempts(mut self, count: usize) -> Self {
249
+ self.failed_recovery_attempts = count;
250
+ self
251
+ }
252
+
253
+ pub fn build(mut self) -> RecoveryStatus {
254
+ // Determine overall status from subsystems
255
+ if self.transaction_recovery != HealthStatus::Healthy
256
+ || self.durable_operations != HealthStatus::Healthy
257
+ {
258
+ self.status = HealthStatus::Degraded;
259
+ }
260
+
261
+ RecoveryStatus {
262
+ status: self.status,
263
+ transaction_recovery: self.transaction_recovery,
264
+ durable_operations: self.durable_operations,
265
+ last_check_at: now(),
266
+ issues: self.issues,
267
+ pending_recovery_count: self.pending_recovery_count,
268
+ failed_recovery_attempts: self.failed_recovery_attempts,
269
+ }
270
+ }
271
+ }
272
+
273
+ impl Default for RecoveryStatusBuilder {
274
+ fn default() -> Self {
275
+ Self::new()
276
+ }
277
+ }
278
+
279
+ fn now() -> u64 {
280
+ std::time::SystemTime::now()
281
+ .duration_since(std::time::UNIX_EPOCH)
282
+ .unwrap_or_default()
283
+ .as_secs()
284
+ }
285
+
286
+ #[cfg(test)]
287
+ mod tests {
288
+ use super::*;
289
+
290
+ #[test]
291
+ fn create_diagnostics_collector() {
292
+ let collector = DiagnosticsCollector::new(
293
+ "abc123def456".to_string(),
294
+ Environment::Prod,
295
+ );
296
+
297
+ assert_eq!(collector.engine_version, ENGINE_VERSION);
298
+ assert_eq!(collector.protocol_version, PROTOCOL_VERSION);
299
+ assert_eq!(collector.storage_format, STORAGE_FORMAT);
300
+ assert_eq!(collector.environment, Environment::Prod);
301
+ }
302
+
303
+ #[test]
304
+ fn collect_report_includes_uptime() {
305
+ let collector = DiagnosticsCollector::new(
306
+ "abc123def456".to_string(),
307
+ Environment::Staging,
308
+ );
309
+
310
+ let recovery_status = RecoveryStatus::default();
311
+ let report = collector.collect_report(recovery_status);
312
+
313
+ assert_eq!(report.engine_version, ENGINE_VERSION);
314
+ let _uptime = report.uptime_seconds;
315
+ }
316
+
317
+ #[test]
318
+ fn recovery_status_builder() {
319
+ let recovery = RecoveryStatusBuilder::new()
320
+ .with_transaction_recovery(HealthStatus::Healthy)
321
+ .with_durable_operations(HealthStatus::Degraded)
322
+ .with_issue("durable operations slow".to_string())
323
+ .with_pending_recovery_count(5)
324
+ .build();
325
+
326
+ assert_eq!(recovery.transaction_recovery, HealthStatus::Healthy);
327
+ assert_eq!(recovery.durable_operations, HealthStatus::Degraded);
328
+ assert_eq!(recovery.status, HealthStatus::Degraded);
329
+ assert_eq!(recovery.pending_recovery_count, 5);
330
+ assert!(recovery.issues.contains(&"durable operations slow".to_string()));
331
+ }
332
+
333
+ #[test]
334
+ fn health_status_propagates_to_overall() {
335
+ let recovery = RecoveryStatusBuilder::new()
336
+ .with_transaction_recovery(HealthStatus::Unhealthy)
337
+ .build();
338
+
339
+ assert_eq!(recovery.status, HealthStatus::Degraded);
340
+ }
341
+
342
+ #[test]
343
+ fn environment_serialization() {
344
+ let envs = vec![
345
+ Environment::Dev,
346
+ Environment::Staging,
347
+ Environment::Prod,
348
+ ];
349
+
350
+ for env in envs {
351
+ let json = serde_json::to_string(&env).expect("serialize failed");
352
+ let deserialized: Environment =
353
+ serde_json::from_str(&json).expect("deserialize failed");
354
+ assert_eq!(env, deserialized);
355
+ }
356
+ }
357
+
358
+ #[test]
359
+ fn diagnostics_report_serialization() {
360
+ let collector = DiagnosticsCollector::new(
361
+ "abc123def456".to_string(),
362
+ Environment::Prod,
363
+ );
364
+
365
+ let recovery_status = RecoveryStatusBuilder::new()
366
+ .with_pending_recovery_count(3)
367
+ .build();
368
+
369
+ let report = collector.collect_report(recovery_status);
370
+ let json = serde_json::to_string(&report).expect("serialize failed");
371
+ let deserialized: DiagnosticsReport =
372
+ serde_json::from_str(&json).expect("deserialize failed");
373
+
374
+ assert_eq!(deserialized.engine_version, report.engine_version);
375
+ assert_eq!(deserialized.server_commit, report.server_commit);
376
+ assert_eq!(deserialized.recovery.pending_recovery_count, 3);
377
+ }
378
+
379
+ #[test]
380
+ fn add_subsystem_metrics() {
381
+ let collector = DiagnosticsCollector::new(
382
+ "abc123def456".to_string(),
383
+ Environment::Dev,
384
+ );
385
+
386
+ let mut report = collector.collect_report(RecoveryStatus::default());
387
+ let mut details = HashMap::new();
388
+ details.insert("cached_items".to_string(), serde_json::json!(1024));
389
+
390
+ DiagnosticsCollector::add_subsystem_metrics(
391
+ &mut report,
392
+ "cache".to_string(),
393
+ HealthStatus::Healthy,
394
+ 10000,
395
+ 5,
396
+ 1.2,
397
+ details,
398
+ );
399
+
400
+ assert!(report.subsystems.contains_key("cache"));
401
+ let cache_metrics = &report.subsystems["cache"];
402
+ assert_eq!(cache_metrics.request_count, 10000);
403
+ assert_eq!(cache_metrics.error_count, 5);
404
+ assert_eq!(cache_metrics.avg_latency_ms, 1.2);
405
+ }
406
+
407
+ #[test]
408
+ fn default_recovery_status_is_healthy() {
409
+ let recovery = RecoveryStatus::default();
410
+ assert_eq!(recovery.status, HealthStatus::Healthy);
411
+ assert!(recovery.is_healthy());
412
+ }
413
+ }