@feltdb/core 0.4.12 → 0.4.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 (164) hide show
  1. package/dist/cli/commands.js +5 -2
  2. package/dist/cli/index.js +1 -1
  3. package/dist/create/cli.js +16 -1
  4. package/dist/create/create.js +30 -16
  5. package/dist/create/docker-compose-generator.js +48 -9
  6. package/dist/create/package-versions.js +1 -1
  7. package/dist/create/server-source/Cargo.lock +2238 -0
  8. package/dist/create/server-source/Cargo.toml +7 -0
  9. package/dist/create/server-source/crates/feltdb/Cargo.lock +175 -0
  10. package/dist/create/server-source/crates/feltdb/Cargo.toml +79 -0
  11. package/dist/create/server-source/crates/feltdb/benches/baselines/gate-13-redux.json +370 -0
  12. package/dist/create/server-source/crates/feltdb/benches/gate13_baseline.rs +589 -0
  13. package/dist/create/server-source/crates/feltdb/benches/gate13_phase_7_1_release_economics.rs +259 -0
  14. package/dist/create/server-source/crates/feltdb/benches/gate_13_redux.rs +446 -0
  15. package/dist/create/server-source/crates/feltdb/benches/gate_13_regression_runner.rs +272 -0
  16. package/dist/create/server-source/crates/feltdb/benches/gate_14a_concurrent_writer_scaling.rs +378 -0
  17. package/dist/create/server-source/crates/feltdb/benches/gate_14a_production_admission_revalidation.rs +414 -0
  18. package/dist/create/server-source/crates/feltdb/benches/gate_14a_rc2_admission_contract.rs +486 -0
  19. package/dist/create/server-source/crates/feltdb/benches/gate_14a_rc_root_cause.rs +273 -0
  20. package/dist/create/server-source/crates/feltdb/benches/gate_14a_sync1_queued_prototype.rs +587 -0
  21. package/dist/create/server-source/crates/feltdb/benches/gate_14a_sync_economics.rs +513 -0
  22. package/dist/create/server-source/crates/feltdb/benches/gate_14b_causal_backlog_scaling.rs +395 -0
  23. package/dist/create/server-source/crates/feltdb/benches/gate_14c_replication_contract_test.rs +469 -0
  24. package/dist/create/server-source/crates/feltdb/benches/gate_14c_replication_scaling.rs +409 -0
  25. package/dist/create/server-source/crates/feltdb/benches/gate_14d_combined_dimension_scaling.rs +627 -0
  26. package/dist/create/server-source/crates/feltdb/benches/phase_7_1_2_optimization_benchmark.rs +383 -0
  27. package/dist/create/server-source/crates/feltdb/benches/phase_7_1_3_crossover_analysis.rs +298 -0
  28. package/dist/create/server-source/crates/feltdb/src/acceptance_tests.rs +1698 -0
  29. package/dist/create/server-source/crates/feltdb/src/acquisition.rs +286 -0
  30. package/dist/create/server-source/crates/feltdb/src/admission.rs +192 -0
  31. package/dist/create/server-source/crates/feltdb/src/admission_contract_tests.rs +477 -0
  32. package/dist/create/server-source/crates/feltdb/src/adversarial_transport.rs +566 -0
  33. package/dist/create/server-source/crates/feltdb/src/analytics.rs +475 -0
  34. package/dist/create/server-source/crates/feltdb/src/application.rs +2244 -0
  35. package/dist/create/server-source/crates/feltdb/src/application_runtime.rs +1070 -0
  36. package/dist/create/server-source/crates/feltdb/src/authorization.rs +1030 -0
  37. package/dist/create/server-source/crates/feltdb/src/bin/feltdb_node.rs +266 -0
  38. package/dist/create/server-source/crates/feltdb/src/capabilities/mod.rs +8 -0
  39. package/dist/create/server-source/crates/feltdb/src/capabilities/search.rs +418 -0
  40. package/dist/create/server-source/crates/feltdb/src/capabilities/vector.rs +482 -0
  41. package/dist/create/server-source/crates/feltdb/src/capability.rs +903 -0
  42. package/dist/create/server-source/crates/feltdb/src/cardinality_diagnostics.rs +261 -0
  43. package/dist/create/server-source/crates/feltdb/src/cardinality_endpoint.rs +78 -0
  44. package/dist/create/server-source/crates/feltdb/src/causal_dependency_barrier.rs +2214 -0
  45. package/dist/create/server-source/crates/feltdb/src/causal_dependency_barrier_phase_7_1.rs +194 -0
  46. package/dist/create/server-source/crates/feltdb/src/concurrency_fuzzing.rs +427 -0
  47. package/dist/create/server-source/crates/feltdb/src/consistency_contract.rs +453 -0
  48. package/dist/create/server-source/crates/feltdb/src/content_distribution.rs +465 -0
  49. package/dist/create/server-source/crates/feltdb/src/convergence.rs +618 -0
  50. package/dist/create/server-source/crates/feltdb/src/crash_atomic_boundary.rs +418 -0
  51. package/dist/create/server-source/crates/feltdb/src/crash_injection.rs +380 -0
  52. package/dist/create/server-source/crates/feltdb/src/crash_recovery_tests.rs +362 -0
  53. package/dist/create/server-source/crates/feltdb/src/cron.rs +294 -0
  54. package/dist/create/server-source/crates/feltdb/src/distributed_indexing.rs +474 -0
  55. package/dist/create/server-source/crates/feltdb/src/distributed_tests.rs +1003 -0
  56. package/dist/create/server-source/crates/feltdb/src/distributed_transactions.rs +536 -0
  57. package/dist/create/server-source/crates/feltdb/src/durability_guarantees.rs +364 -0
  58. package/dist/create/server-source/crates/feltdb/src/durable_dedup_set.rs +235 -0
  59. package/dist/create/server-source/crates/feltdb/src/durable_operation_log.rs +207 -0
  60. package/dist/create/server-source/crates/feltdb/src/durable_sync.rs +316 -0
  61. package/dist/create/server-source/crates/feltdb/src/execution.rs +426 -0
  62. package/dist/create/server-source/crates/feltdb/src/in_process_transport.rs +219 -0
  63. package/dist/create/server-source/crates/feltdb/src/indexing.rs +779 -0
  64. package/dist/create/server-source/crates/feltdb/src/lib.rs +2838 -0
  65. package/dist/create/server-source/crates/feltdb/src/materialization.rs +184 -0
  66. package/dist/create/server-source/crates/feltdb/src/metrics.rs +267 -0
  67. package/dist/create/server-source/crates/feltdb/src/multi_node_convergence.rs +311 -0
  68. package/dist/create/server-source/crates/feltdb/src/observability.rs +366 -0
  69. package/dist/create/server-source/crates/feltdb/src/operation.rs +251 -0
  70. package/dist/create/server-source/crates/feltdb/src/operation_algebra.rs +438 -0
  71. package/dist/create/server-source/crates/feltdb/src/operation_log.rs +344 -0
  72. package/dist/create/server-source/crates/feltdb/src/partition_reconciliation.rs +477 -0
  73. package/dist/create/server-source/crates/feltdb/src/peer_registry.rs +166 -0
  74. package/dist/create/server-source/crates/feltdb/src/permutation_scheduler.rs +261 -0
  75. package/dist/create/server-source/crates/feltdb/src/persistence_reality.rs +560 -0
  76. package/dist/create/server-source/crates/feltdb/src/phase1b_acceptance.rs +3226 -0
  77. package/dist/create/server-source/crates/feltdb/src/phase1c1_acceptance.rs +201 -0
  78. package/dist/create/server-source/crates/feltdb/src/phase1c2_acceptance.rs +263 -0
  79. package/dist/create/server-source/crates/feltdb/src/phase1c3_acceptance.rs +484 -0
  80. package/dist/create/server-source/crates/feltdb/src/phase1c_atomicity_proof.rs +216 -0
  81. package/dist/create/server-source/crates/feltdb/src/phase5_integration.rs +281 -0
  82. package/dist/create/server-source/crates/feltdb/src/phase5_scenarios.rs +323 -0
  83. package/dist/create/server-source/crates/feltdb/src/phase6_adversarial_scenarios.rs +573 -0
  84. package/dist/create/server-source/crates/feltdb/src/phase6_convergence_validator.rs +404 -0
  85. package/dist/create/server-source/crates/feltdb/src/phase6_persistence.rs +418 -0
  86. package/dist/create/server-source/crates/feltdb/src/phase_1c_real_tcp.rs +381 -0
  87. package/dist/create/server-source/crates/feltdb/src/phase_1c_three_node.rs +523 -0
  88. package/dist/create/server-source/crates/feltdb/src/phase_2a_failures.rs +334 -0
  89. package/dist/create/server-source/crates/feltdb/src/phase_2b_network.rs +306 -0
  90. package/dist/create/server-source/crates/feltdb/src/phase_2c_cascading.rs +355 -0
  91. package/dist/create/server-source/crates/feltdb/src/phase_3_durability.rs +395 -0
  92. package/dist/create/server-source/crates/feltdb/src/phase_4_baseline.rs +346 -0
  93. package/dist/create/server-source/crates/feltdb/src/phase_5_soak.rs +430 -0
  94. package/dist/create/server-source/crates/feltdb/src/production_api.rs +400 -0
  95. package/dist/create/server-source/crates/feltdb/src/provenance.rs +207 -0
  96. package/dist/create/server-source/crates/feltdb/src/query_performance.rs +217 -0
  97. package/dist/create/server-source/crates/feltdb/src/references.rs +404 -0
  98. package/dist/create/server-source/crates/feltdb/src/replay_fuzzing.rs +401 -0
  99. package/dist/create/server-source/crates/feltdb/src/replication_manager.rs +160 -0
  100. package/dist/create/server-source/crates/feltdb/src/replication_protocol.rs +132 -0
  101. package/dist/create/server-source/crates/feltdb/src/routing.rs +409 -0
  102. package/dist/create/server-source/crates/feltdb/src/sharding.rs +523 -0
  103. package/dist/create/server-source/crates/feltdb/src/state_contract.rs +3118 -0
  104. package/dist/create/server-source/crates/feltdb/src/state_hash.rs +291 -0
  105. package/dist/create/server-source/crates/feltdb/src/state_transition_store.rs +376 -0
  106. package/dist/create/server-source/crates/feltdb/src/storage.rs +267 -0
  107. package/dist/create/server-source/crates/feltdb/src/submission.rs +477 -0
  108. package/dist/create/server-source/crates/feltdb/src/sync.rs +483 -0
  109. package/dist/create/server-source/crates/feltdb/src/sync_contract.rs +822 -0
  110. package/dist/create/server-source/crates/feltdb/src/tcp_transport.rs +366 -0
  111. package/dist/create/server-source/crates/feltdb/src/transaction_api.rs +473 -0
  112. package/dist/create/server-source/crates/feltdb/src/transaction_invariants.rs +949 -0
  113. package/dist/create/server-source/crates/feltdb/src/transactions.rs +646 -0
  114. package/dist/create/server-source/crates/feltdb/src/trigger.rs +390 -0
  115. package/dist/create/server-source/crates/feltdb/src/worker_mesh.rs +928 -0
  116. package/dist/create/server-source/crates/feltdb/src/workflow.rs +713 -0
  117. package/dist/create/server-source/crates/feltdb/src/workflow_acceptance_tests.rs +510 -0
  118. package/dist/create/server-source/crates/feltdb/src/workflow_integration.rs +354 -0
  119. package/dist/create/server-source/crates/feltdb/src/workflow_runtime.rs +594 -0
  120. package/dist/create/server-source/crates/feltdb/src/workload.rs +1310 -0
  121. package/dist/create/server-source/crates/feltdb-server/Cargo.toml +23 -0
  122. package/dist/create/server-source/crates/feltdb-server/README.md +79 -0
  123. package/dist/create/server-source/crates/feltdb-server/src/app_state.rs +73 -0
  124. package/dist/create/server-source/crates/feltdb-server/src/application_contract.rs +425 -0
  125. package/dist/create/server-source/crates/feltdb-server/src/artifacts.rs +449 -0
  126. package/dist/create/server-source/crates/feltdb-server/src/audit.rs +59 -0
  127. package/dist/create/server-source/crates/feltdb-server/src/auth.rs +308 -0
  128. package/dist/create/server-source/crates/feltdb-server/src/authorization.rs +228 -0
  129. package/dist/create/server-source/crates/feltdb-server/src/backup.rs +416 -0
  130. package/dist/create/server-source/crates/feltdb-server/src/causal.rs +218 -0
  131. package/dist/create/server-source/crates/feltdb-server/src/certification.rs +223 -0
  132. package/dist/create/server-source/crates/feltdb-server/src/clock.rs +132 -0
  133. package/dist/create/server-source/crates/feltdb-server/src/cluster.rs +165 -0
  134. package/dist/create/server-source/crates/feltdb-server/src/connections.rs +1044 -0
  135. package/dist/create/server-source/crates/feltdb-server/src/content.rs +111 -0
  136. package/dist/create/server-source/crates/feltdb-server/src/identity.rs +250 -0
  137. package/dist/create/server-source/crates/feltdb-server/src/key_management.rs +78 -0
  138. package/dist/create/server-source/crates/feltdb-server/src/key_provider.rs +247 -0
  139. package/dist/create/server-source/crates/feltdb-server/src/leases.rs +123 -0
  140. package/dist/create/server-source/crates/feltdb-server/src/lib.rs +25 -0
  141. package/dist/create/server-source/crates/feltdb-server/src/main.rs +8715 -0
  142. package/dist/create/server-source/crates/feltdb-server/src/metrics.rs +97 -0
  143. package/dist/create/server-source/crates/feltdb-server/src/portable_bundle.rs +350 -0
  144. package/dist/create/server-source/crates/feltdb-server/src/principals.rs +510 -0
  145. package/dist/create/server-source/crates/feltdb-server/src/providers.rs +269 -0
  146. package/dist/create/server-source/crates/feltdb-server/src/releases.rs +2563 -0
  147. package/dist/create/server-source/crates/feltdb-server/src/sessions.rs +156 -0
  148. package/dist/create/server-source/crates/feltdb-server/src/tenancy.rs +1097 -0
  149. package/dist/create/server-source/crates/feltdb-server/src/versions.rs +264 -0
  150. package/dist/create/server-source/crates/feltdb-wasm/Cargo.toml +31 -0
  151. package/dist/create/server-source/crates/feltdb-wasm/src/lib.rs +1086 -0
  152. package/dist/create/server-source/crates/feltdb-wasm/test.db +0 -0
  153. package/dist/flowspec.d.ts +2 -0
  154. package/dist/flowspec.d.ts.map +1 -1
  155. package/dist/flowspec.js +28 -4
  156. package/dist/state-contract.d.ts +7 -1
  157. package/dist/state-contract.d.ts.map +1 -1
  158. package/dist/studio/components/ApplicationDesigner.d.ts.map +1 -1
  159. package/dist/studio/components/index.js +1 -1
  160. package/dist/studio/{components-Q0Nx6gLE.js → components-bHTARBin.js} +156 -46
  161. package/dist/studio/index.js +31 -28
  162. package/dist/studio-app/assets/{index-BHOmLZF-.js → index-BqKquLuU.js} +8 -8
  163. package/dist/studio-app/index.html +1 -1
  164. package/package.json +1 -1
@@ -0,0 +1,1086 @@
1
+ use feltdb::FeltDb;
2
+ use serde_json::Value;
3
+ use std::sync::Arc;
4
+ use wasm_bindgen::prelude::*;
5
+
6
+ /// Canonicalizes a typed application manifest using the same Rust implementation
7
+ /// used by the server. JavaScript never defines revision identity independently.
8
+ #[wasm_bindgen]
9
+ pub fn canonicalize_application_manifest(input: &str) -> std::result::Result<String, JsValue> {
10
+ let manifest: feltdb::application::ApplicationManifest =
11
+ serde_json::from_str(input).map_err(|error| JsValue::from_str(&error.to_string()))?;
12
+ let bytes = feltdb::application::canonical_bytes(&manifest)
13
+ .map_err(|error| JsValue::from_str(&error))?;
14
+ String::from_utf8(bytes).map_err(|error| JsValue::from_str(&error.to_string()))
15
+ }
16
+
17
+ #[wasm_bindgen]
18
+ pub fn hash_application_manifest(input: &str) -> std::result::Result<String, JsValue> {
19
+ let manifest: feltdb::application::ApplicationManifest =
20
+ serde_json::from_str(input).map_err(|error| JsValue::from_str(&error.to_string()))?;
21
+ feltdb::application::manifest_hash(&manifest).map_err(|error| JsValue::from_str(&error))
22
+ }
23
+
24
+ #[wasm_bindgen]
25
+ pub fn resolve_application_runtime(
26
+ revision: &str,
27
+ environment: &str,
28
+ ) -> std::result::Result<String, JsValue> {
29
+ let revision: feltdb::application::ApplicationRevision =
30
+ serde_json::from_str(revision).map_err(|error| JsValue::from_str(&error.to_string()))?;
31
+ let contract =
32
+ feltdb::application_runtime::resolve_runtime(&revision, environment, &Default::default())
33
+ .map_err(|report| {
34
+ JsValue::from_str(
35
+ &serde_json::to_string(&report)
36
+ .unwrap_or_else(|_| "runtime resolution failed".into()),
37
+ )
38
+ })?;
39
+ serde_json::to_string(&contract).map_err(|error| JsValue::from_str(&error.to_string()))
40
+ }
41
+
42
+ #[wasm_bindgen]
43
+ pub fn hash_application_runtime(
44
+ revision: &str,
45
+ environment: &str,
46
+ ) -> std::result::Result<String, JsValue> {
47
+ let revision: feltdb::application::ApplicationRevision =
48
+ serde_json::from_str(revision).map_err(|error| JsValue::from_str(&error.to_string()))?;
49
+ let contract =
50
+ feltdb::application_runtime::resolve_runtime(&revision, environment, &Default::default())
51
+ .map_err(|report| {
52
+ JsValue::from_str(
53
+ &serde_json::to_string(&report)
54
+ .unwrap_or_else(|_| "runtime resolution failed".into()),
55
+ )
56
+ })?;
57
+ feltdb::application_runtime::runtime_hash(&contract).map_err(|error| JsValue::from_str(&error))
58
+ }
59
+
60
+ #[wasm_bindgen]
61
+ pub fn diff_application_revisions(
62
+ from: Option<String>,
63
+ to: &str,
64
+ ) -> std::result::Result<String, JsValue> {
65
+ let before = from
66
+ .as_deref()
67
+ .map(serde_json::from_str::<feltdb::application::ApplicationRevision>)
68
+ .transpose()
69
+ .map_err(|error| JsValue::from_str(&error.to_string()))?;
70
+ let after: feltdb::application::ApplicationRevision =
71
+ serde_json::from_str(to).map_err(|error| JsValue::from_str(&error.to_string()))?;
72
+ let diff = feltdb::application::application_revision_diff(before.as_ref(), &after)
73
+ .map_err(|error| JsValue::from_str(&error))?;
74
+ serde_json::to_string(&diff).map_err(|error| JsValue::from_str(&error.to_string()))
75
+ }
76
+
77
+ #[wasm_bindgen]
78
+ pub fn validate_state_schema(schema: &str) -> std::result::Result<String, JsValue> {
79
+ let schema: feltdb::state_contract::StateSchema =
80
+ serde_json::from_str(schema).map_err(|e| JsValue::from_str(&e.to_string()))?;
81
+ serde_json::to_string(&feltdb::state_contract::validate_schema(&schema))
82
+ .map_err(|e| JsValue::from_str(&e.to_string()))
83
+ }
84
+ #[wasm_bindgen]
85
+ pub fn hash_state_schema(schema: &str) -> std::result::Result<String, JsValue> {
86
+ let schema: feltdb::state_contract::StateSchema =
87
+ serde_json::from_str(schema).map_err(|e| JsValue::from_str(&e.to_string()))?;
88
+ feltdb::state_contract::schema_hash(&schema).map_err(|e| JsValue::from_str(&e))
89
+ }
90
+ #[wasm_bindgen]
91
+ pub fn compare_state_schemas(before: &str, after: &str) -> std::result::Result<String, JsValue> {
92
+ let before: feltdb::state_contract::StateSchema =
93
+ serde_json::from_str(before).map_err(|e| JsValue::from_str(&e.to_string()))?;
94
+ let after: feltdb::state_contract::StateSchema =
95
+ serde_json::from_str(after).map_err(|e| JsValue::from_str(&e.to_string()))?;
96
+ serde_json::to_string(&feltdb::state_contract::compare_schemas(&before, &after))
97
+ .map_err(|e| JsValue::from_str(&e.to_string()))
98
+ }
99
+
100
+ /// Verify a portable signed grant with the exact Rust canonicalization used by
101
+ /// the server. The verification key is provisioned with the offline app shell.
102
+ #[wasm_bindgen]
103
+ pub fn verify_signed_grant(
104
+ signed_grant: &str,
105
+ key_id: &str,
106
+ verification_key: &str,
107
+ request: &str,
108
+ grant_chain: &str,
109
+ revocations: &str,
110
+ ) -> std::result::Result<String, JsValue> {
111
+ use feltdb::authorization::{AuthorizationRequest, GrantSigner, RevocationState, SignedGrant};
112
+ use std::collections::BTreeMap;
113
+ let signed: SignedGrant =
114
+ serde_json::from_str(signed_grant).map_err(|e| JsValue::from_str(&e.to_string()))?;
115
+ let request: AuthorizationRequest =
116
+ serde_json::from_str(request).map_err(|e| JsValue::from_str(&e.to_string()))?;
117
+ let grants: BTreeMap<String, SignedGrant> =
118
+ serde_json::from_str(grant_chain).map_err(|e| JsValue::from_str(&e.to_string()))?;
119
+ let revoked: RevocationState =
120
+ serde_json::from_str(revocations).map_err(|e| JsValue::from_str(&e.to_string()))?;
121
+ let signer = GrantSigner::verifier_hex(key_id, verification_key)
122
+ .map_err(|e| JsValue::from_str(&e.to_string()))?;
123
+ feltdb::authorization::verify_grant(&signed, &signer, &request, &grants, &revoked).map_err(
124
+ |e| JsValue::from_str(&serde_json::to_string(&e).unwrap_or_else(|_| e.to_string())),
125
+ )?;
126
+ Ok(signed
127
+ .grant
128
+ .digest()
129
+ .map_err(|e| JsValue::from_str(&e.to_string()))?)
130
+ }
131
+
132
+ #[wasm_bindgen]
133
+ pub fn authorize_offline(
134
+ signed_grant: &str,
135
+ key_id: &str,
136
+ verification_key: &str,
137
+ request: &str,
138
+ grant_chain: &str,
139
+ revocations: &str,
140
+ ) -> std::result::Result<String, JsValue> {
141
+ use feltdb::authorization::{AuthorizationRequest, GrantSigner, RevocationState, SignedGrant};
142
+ use std::collections::BTreeMap;
143
+ let signed: SignedGrant =
144
+ serde_json::from_str(signed_grant).map_err(|e| JsValue::from_str(&e.to_string()))?;
145
+ let request: AuthorizationRequest =
146
+ serde_json::from_str(request).map_err(|e| JsValue::from_str(&e.to_string()))?;
147
+ let grants: BTreeMap<String, SignedGrant> =
148
+ serde_json::from_str(grant_chain).map_err(|e| JsValue::from_str(&e.to_string()))?;
149
+ let revoked: RevocationState =
150
+ serde_json::from_str(revocations).map_err(|e| JsValue::from_str(&e.to_string()))?;
151
+ let signer = GrantSigner::verifier_hex(key_id, verification_key)
152
+ .map_err(|e| JsValue::from_str(&e.to_string()))?;
153
+ serde_json::to_string(&feltdb::authorization::authorize(
154
+ &request,
155
+ Some(&signed),
156
+ &signer,
157
+ &grants,
158
+ &revoked,
159
+ ))
160
+ .map_err(|e| JsValue::from_str(&e.to_string()))
161
+ }
162
+
163
+ #[wasm_bindgen]
164
+ pub fn validate_sync_operation(operation: &str) -> std::result::Result<String, JsValue> {
165
+ let operation: feltdb::sync_contract::SyncOperation =
166
+ serde_json::from_str(operation).map_err(|e| JsValue::from_str(&e.to_string()))?;
167
+ operation.validate_identity().map_err(|e| {
168
+ JsValue::from_str(&serde_json::to_string(&e).unwrap_or_else(|_| e.to_string()))
169
+ })?;
170
+ Ok(operation.hash())
171
+ }
172
+
173
+ #[wasm_bindgen]
174
+ pub fn apply_sync_result(
175
+ local_runtime: &str,
176
+ result: &str,
177
+ ) -> std::result::Result<String, JsValue> {
178
+ use feltdb::sync_contract::{OutboxStatus, SyncResult};
179
+ use std::collections::BTreeMap;
180
+ let mut outbox: BTreeMap<String, OutboxStatus> =
181
+ serde_json::from_str(local_runtime).map_err(|e| JsValue::from_str(&e.to_string()))?;
182
+ let result: SyncResult =
183
+ serde_json::from_str(result).map_err(|e| JsValue::from_str(&e.to_string()))?;
184
+ for ack in result.acknowledgements {
185
+ outbox.insert(ack.operation_id, OutboxStatus::Acknowledged);
186
+ }
187
+ for rejection in result.rejections {
188
+ outbox.insert(
189
+ rejection.operation_id,
190
+ if rejection.retryable {
191
+ OutboxStatus::Retryable
192
+ } else {
193
+ OutboxStatus::Rejected
194
+ },
195
+ );
196
+ }
197
+ for conflict in result.conflicts {
198
+ outbox.insert(conflict.operation_id, OutboxStatus::Conflicted);
199
+ }
200
+ serde_json::to_string(&outbox).map_err(|e| JsValue::from_str(&e.to_string()))
201
+ }
202
+
203
+ #[wasm_bindgen]
204
+ pub fn validate_workload_transition(from: &str, to: &str) -> std::result::Result<bool, JsValue> {
205
+ let from: feltdb::workload::WorkloadState = serde_json::from_str(&format!("\"{from}\""))
206
+ .map_err(|e| JsValue::from_str(&e.to_string()))?;
207
+ let to: feltdb::workload::WorkloadState = serde_json::from_str(&format!("\"{to}\""))
208
+ .map_err(|e| JsValue::from_str(&e.to_string()))?;
209
+ Ok(feltdb::workload::can_transition(from, to))
210
+ }
211
+
212
+ #[wasm_bindgen]
213
+ pub fn validate_worker_transition(from: &str, to: &str) -> std::result::Result<bool, JsValue> {
214
+ let from: feltdb::worker_mesh::WorkerLifecycle =
215
+ serde_json::from_str(&format!("\"{from}\""))
216
+ .map_err(|e| JsValue::from_str(&e.to_string()))?;
217
+ let to: feltdb::worker_mesh::WorkerLifecycle = serde_json::from_str(&format!("\"{to}\""))
218
+ .map_err(|e| JsValue::from_str(&e.to_string()))?;
219
+ Ok(feltdb::worker_mesh::worker_transition(from, to))
220
+ }
221
+
222
+ #[wasm_bindgen]
223
+ pub fn validate_mesh_claim(
224
+ claim: &str,
225
+ worker: &str,
226
+ workload: &str,
227
+ now: i64,
228
+ ) -> std::result::Result<(), JsValue> {
229
+ let claim: feltdb::worker_mesh::MeshExecutionClaim =
230
+ serde_json::from_str(claim).map_err(|e| JsValue::from_str(&e.to_string()))?;
231
+ let worker: feltdb::worker_mesh::Worker =
232
+ serde_json::from_str(worker).map_err(|e| JsValue::from_str(&e.to_string()))?;
233
+ let workload: feltdb::workload::Workload =
234
+ serde_json::from_str(workload).map_err(|e| JsValue::from_str(&e.to_string()))?;
235
+ feltdb::worker_mesh::validate_mesh_claim(&claim, &worker, &workload, now).map_err(|e| {
236
+ JsValue::from_str(&serde_json::to_string(&e).unwrap_or_else(|_| e.to_string()))
237
+ })
238
+ }
239
+
240
+ /// Wrapper around FeltDb for JavaScript interop
241
+ #[wasm_bindgen]
242
+ pub struct JsDb {
243
+ db: Arc<FeltDb>,
244
+ }
245
+
246
+ /// Result type for WASM operations
247
+ #[wasm_bindgen]
248
+ pub struct JsResult {
249
+ success: bool,
250
+ data: Option<String>,
251
+ error: Option<String>,
252
+ }
253
+
254
+ #[wasm_bindgen]
255
+ impl JsResult {
256
+ #[wasm_bindgen(getter)]
257
+ pub fn success(&self) -> bool {
258
+ self.success
259
+ }
260
+
261
+ #[wasm_bindgen(getter)]
262
+ pub fn data(&self) -> Option<String> {
263
+ self.data.clone()
264
+ }
265
+
266
+ #[wasm_bindgen(getter)]
267
+ pub fn error(&self) -> Option<String> {
268
+ self.error.clone()
269
+ }
270
+ }
271
+
272
+ fn feltdb_error_to_js(err: feltdb::FlowError) -> JsValue {
273
+ JsValue::from_str(&format!("FeltDB error: {}", err))
274
+ }
275
+
276
+ /// Opens or creates a FeltDB database
277
+ ///
278
+ /// # Arguments
279
+ /// * `path` - Path to the database file
280
+ ///
281
+ /// # Returns
282
+ /// JsDb instance or error
283
+ #[wasm_bindgen]
284
+ pub fn open(path: &str) -> std::result::Result<JsDb, JsValue> {
285
+ let db = FeltDb::open(path).map_err(feltdb_error_to_js)?;
286
+ Ok(JsDb { db: Arc::new(db) })
287
+ }
288
+
289
+ #[wasm_bindgen]
290
+ impl JsDb {
291
+ pub fn execute_canonical_query(
292
+ &self,
293
+ schema: &str,
294
+ authorization: &str,
295
+ state_namespace: &str,
296
+ query: &str,
297
+ ) -> JsResult {
298
+ let result = (|| {
299
+ let schema: feltdb::state_contract::StateSchema =
300
+ serde_json::from_str(schema).map_err(|e| e.to_string())?;
301
+ let authorization: feltdb::state_contract::AuthorizationContext =
302
+ serde_json::from_str(authorization).map_err(|e| e.to_string())?;
303
+ let query: feltdb::state_contract::CanonicalQuery =
304
+ serde_json::from_str(query).map_err(|e| e.to_string())?;
305
+ let context = feltdb::state_contract::begin_read(
306
+ &self.db,
307
+ &schema,
308
+ state_namespace,
309
+ authorization,
310
+ )
311
+ .map_err(|e| serde_json::to_string(&e).unwrap_or(e.message))?;
312
+ feltdb::state_contract::execute_query(&self.db, &schema, &context, &query)
313
+ .map_err(|e| serde_json::to_string(&e).unwrap_or(e.message))
314
+ })()
315
+ .and_then(|v| serde_json::to_string(&v).map_err(|e| e.to_string()));
316
+ match result {
317
+ Ok(data) => JsResult {
318
+ success: true,
319
+ data: Some(data),
320
+ error: None,
321
+ },
322
+ Err(error) => JsResult {
323
+ success: false,
324
+ data: None,
325
+ error: Some(error),
326
+ },
327
+ }
328
+ }
329
+ pub fn execute_canonical_transaction(&self, schema: &str, request: &str) -> JsResult {
330
+ let result = (|| {
331
+ let schema: feltdb::state_contract::StateSchema =
332
+ serde_json::from_str(schema).map_err(|e| e.to_string())?;
333
+ let request: feltdb::state_contract::TransactionRequest =
334
+ serde_json::from_str(request).map_err(|e| e.to_string())?;
335
+ feltdb::state_contract::execute_transaction(&self.db, &schema, &request)
336
+ .map_err(|e| serde_json::to_string(&e).unwrap_or(e.message))
337
+ })()
338
+ .and_then(|v| serde_json::to_string(&v).map_err(|e| e.to_string()));
339
+ match result {
340
+ Ok(data) => JsResult {
341
+ success: true,
342
+ data: Some(data),
343
+ error: None,
344
+ },
345
+ Err(error) => JsResult {
346
+ success: false,
347
+ data: None,
348
+ error: Some(error),
349
+ },
350
+ }
351
+ }
352
+ /// Insert a record into the database
353
+ ///
354
+ /// # Arguments
355
+ /// * `key` - Key for the record (e.g., "users:123")
356
+ /// * `value` - JSON object as a string
357
+ ///
358
+ /// # Returns
359
+ /// JsResult with success status
360
+ pub fn insert(&self, key: &str, value: &str) -> JsResult {
361
+ match serde_json::from_str::<Value>(value) {
362
+ Ok(json_value) => match self.db.insert(key, &json_value) {
363
+ Ok(_) => JsResult {
364
+ success: true,
365
+ data: Some(key.to_string()),
366
+ error: None,
367
+ },
368
+ Err(e) => JsResult {
369
+ success: false,
370
+ data: None,
371
+ error: Some(format!("Insert failed: {}", e)),
372
+ },
373
+ },
374
+ Err(e) => JsResult {
375
+ success: false,
376
+ data: None,
377
+ error: Some(format!("Invalid JSON: {}", e)),
378
+ },
379
+ }
380
+ }
381
+
382
+ /// Update an existing record through the canonical update operation.
383
+ pub fn update(&self, key: &str, value: &str) -> JsResult {
384
+ match serde_json::from_str::<Value>(value) {
385
+ Ok(json_value) => match self.db.update(key, &json_value) {
386
+ Ok(_) => JsResult {
387
+ success: true,
388
+ data: Some(key.to_string()),
389
+ error: None,
390
+ },
391
+ Err(e) => JsResult {
392
+ success: false,
393
+ data: None,
394
+ error: Some(format!("Update failed: {}", e)),
395
+ },
396
+ },
397
+ Err(e) => JsResult {
398
+ success: false,
399
+ data: None,
400
+ error: Some(format!("Invalid JSON: {}", e)),
401
+ },
402
+ }
403
+ }
404
+
405
+ /// Delete a record from the database.
406
+ pub fn delete(&self, key: &str) -> JsResult {
407
+ match self.db.delete(key) {
408
+ Ok(_) => JsResult {
409
+ success: true,
410
+ data: Some(key.to_string()),
411
+ error: None,
412
+ },
413
+ Err(e) => JsResult {
414
+ success: false,
415
+ data: None,
416
+ error: Some(format!("Delete failed: {}", e)),
417
+ },
418
+ }
419
+ }
420
+
421
+ /// Retrieve a record from the database
422
+ ///
423
+ /// # Arguments
424
+ /// * `key` - Key for the record
425
+ ///
426
+ /// # Returns
427
+ /// JSON string of the record or error
428
+ pub fn get(&self, key: &str) -> JsResult {
429
+ match self.db.get::<Value>(key) {
430
+ Ok(Some(value)) => JsResult {
431
+ success: true,
432
+ data: Some(value.to_string()),
433
+ error: None,
434
+ },
435
+ Ok(None) => JsResult {
436
+ success: true,
437
+ data: None,
438
+ error: None,
439
+ },
440
+ Err(e) => JsResult {
441
+ success: false,
442
+ data: None,
443
+ error: Some(format!("Get failed: {}", e)),
444
+ },
445
+ }
446
+ }
447
+
448
+ /// Query records in the database with a predicate
449
+ ///
450
+ /// For now, this returns all records of a given type namespace as a simplified implementation.
451
+ /// The full closure-based predicate from Rust cannot be directly represented in WASM bindings.
452
+ ///
453
+ /// # Arguments
454
+ /// * `capability` - Capability/namespace to query (e.g., "users")
455
+ ///
456
+ /// # Returns
457
+ /// JSON array string of matching records
458
+ pub fn query(&self, capability: &str) -> JsResult {
459
+ // Get the capability and find all records in it
460
+ let cap = self.db.capability(capability);
461
+ match cap.find::<Value, _>(|_| true) {
462
+ Ok(values) => {
463
+ let json_array = Value::Array(values);
464
+ JsResult {
465
+ success: true,
466
+ data: Some(json_array.to_string()),
467
+ error: None,
468
+ }
469
+ }
470
+ Err(e) => JsResult {
471
+ success: false,
472
+ data: None,
473
+ error: Some(format!("Query failed: {}", e)),
474
+ },
475
+ }
476
+ }
477
+
478
+ /// Get all records in a capability namespace
479
+ ///
480
+ /// # Arguments
481
+ /// * `capability` - Capability/namespace name
482
+ ///
483
+ /// # Returns
484
+ /// JSON array string of all records in the capability
485
+ pub fn get_capability_records(&self, capability: &str) -> JsResult {
486
+ let cap = self.db.capability(capability);
487
+ match cap.find::<Value, _>(|_| true) {
488
+ Ok(values) => {
489
+ let json_array = Value::Array(values);
490
+ JsResult {
491
+ success: true,
492
+ data: Some(json_array.to_string()),
493
+ error: None,
494
+ }
495
+ }
496
+ Err(e) => JsResult {
497
+ success: false,
498
+ data: None,
499
+ error: Some(format!("Get capability records failed: {}", e)),
500
+ },
501
+ }
502
+ }
503
+
504
+ /// Check if a type is auto-indexed
505
+ ///
506
+ /// # Returns
507
+ /// Whether the default (generic JSON) type is auto-indexed
508
+ pub fn is_auto_indexed(&self) -> bool {
509
+ self.db.is_auto_indexed::<Value>()
510
+ }
511
+
512
+ /// Get current sync state information
513
+ ///
514
+ /// # Returns
515
+ /// JSON string containing sync state metrics
516
+ pub fn sync_info(&self) -> JsResult {
517
+ match self.db.sync_info() {
518
+ Ok(sync_state) => match serde_json::to_string(&sync_state) {
519
+ Ok(json) => JsResult {
520
+ success: true,
521
+ data: Some(json),
522
+ error: None,
523
+ },
524
+ Err(e) => JsResult {
525
+ success: false,
526
+ data: None,
527
+ error: Some(format!("Serialization failed: {}", e)),
528
+ },
529
+ },
530
+ Err(e) => JsResult {
531
+ success: false,
532
+ data: None,
533
+ error: Some(format!("Sync info failed: {}", e)),
534
+ },
535
+ }
536
+ }
537
+
538
+ /// Add a peer for synchronization
539
+ ///
540
+ /// # Arguments
541
+ /// * `peer_id` - Unique identifier for the peer
542
+ ///
543
+ /// # Returns
544
+ /// JsResult with success status
545
+ pub fn add_sync_peer(&self, peer_id: &str) -> JsResult {
546
+ match self.db.add_sync_peer(peer_id.to_string()) {
547
+ Ok(_) => JsResult {
548
+ success: true,
549
+ data: Some(format!("Added peer: {}", peer_id)),
550
+ error: None,
551
+ },
552
+ Err(e) => JsResult {
553
+ success: false,
554
+ data: None,
555
+ error: Some(format!("Add peer failed: {}", e)),
556
+ },
557
+ }
558
+ }
559
+
560
+ /// Remove a peer from synchronization
561
+ ///
562
+ /// # Arguments
563
+ /// * `peer_id` - Unique identifier for the peer
564
+ ///
565
+ /// # Returns
566
+ /// JsResult with success status
567
+ pub fn remove_sync_peer(&self, peer_id: &str) -> JsResult {
568
+ match self.db.remove_sync_peer(peer_id) {
569
+ Ok(_) => JsResult {
570
+ success: true,
571
+ data: Some(format!("Removed peer: {}", peer_id)),
572
+ error: None,
573
+ },
574
+ Err(e) => JsResult {
575
+ success: false,
576
+ data: None,
577
+ error: Some(format!("Remove peer failed: {}", e)),
578
+ },
579
+ }
580
+ }
581
+
582
+ /// Get pending operations for a peer since a given sequence
583
+ ///
584
+ /// # Arguments
585
+ /// * `peer_id` - Unique identifier for the peer
586
+ /// * `since_sequence` - Get operations after this sequence number
587
+ ///
588
+ /// # Returns
589
+ /// JSON array of operations as a string
590
+ pub fn get_pending_for_peer(&self, peer_id: &str, since_sequence: u64) -> JsResult {
591
+ match self.db.get_pending_for_peer(peer_id, since_sequence) {
592
+ Ok(operations) => match serde_json::to_string(&operations) {
593
+ Ok(json) => JsResult {
594
+ success: true,
595
+ data: Some(json),
596
+ error: None,
597
+ },
598
+ Err(e) => JsResult {
599
+ success: false,
600
+ data: None,
601
+ error: Some(format!("Serialization failed: {}", e)),
602
+ },
603
+ },
604
+ Err(e) => JsResult {
605
+ success: false,
606
+ data: None,
607
+ error: Some(format!("Get pending failed: {}", e)),
608
+ },
609
+ }
610
+ }
611
+
612
+ /// Acknowledge receipt of operations from a peer
613
+ ///
614
+ /// # Arguments
615
+ /// * `peer_id` - Unique identifier for the peer
616
+ /// * `sequence` - Acknowledge operations up to this sequence number
617
+ ///
618
+ /// # Returns
619
+ /// JsResult with success status
620
+ pub fn acknowledge_peer_operations(&self, peer_id: &str, sequence: u64) -> JsResult {
621
+ match self
622
+ .db
623
+ .acknowledge_peer_operations(peer_id.to_string(), sequence)
624
+ {
625
+ Ok(_) => JsResult {
626
+ success: true,
627
+ data: Some(format!("Acknowledged sequence {}", sequence)),
628
+ error: None,
629
+ },
630
+ Err(e) => JsResult {
631
+ success: false,
632
+ data: None,
633
+ error: Some(format!("Acknowledgement failed: {}", e)),
634
+ },
635
+ }
636
+ }
637
+
638
+ /// Get the instance ID for this FeltDB instance
639
+ ///
640
+ /// # Returns
641
+ /// The unique instance ID as a string
642
+ pub fn instance_id(&self) -> String {
643
+ self.db
644
+ .instance_id()
645
+ .unwrap_or_else(|_| "unknown".to_string())
646
+ }
647
+
648
+ /// Get the current sequence number for this instance
649
+ ///
650
+ /// # Returns
651
+ /// The current sequence number
652
+ pub fn get_sequence(&self) -> u64 {
653
+ self.db.sequence().unwrap_or(0)
654
+ }
655
+ }
656
+
657
+ // Note: Live queries require async/await support which requires special handling in WASM.
658
+ // For now, the synchronous query methods above are sufficient for frontend integration.
659
+ // Live query subscriptions can be added using a message-passing pattern or WebWorkers.
660
+
661
+ #[wasm_bindgen]
662
+ extern "C" {
663
+ #[wasm_bindgen(js_namespace = console)]
664
+ fn log(s: &str);
665
+ }
666
+
667
+ fn canonical_json(value: &serde_json::Value) -> serde_json::Value {
668
+ match value {
669
+ serde_json::Value::Object(map) => serde_json::Value::Object(
670
+ map.iter()
671
+ .map(|(k, v)| (k.clone(), canonical_json(v)))
672
+ .collect::<std::collections::BTreeMap<_, _>>()
673
+ .into_iter()
674
+ .collect(),
675
+ ),
676
+ serde_json::Value::Array(values) => {
677
+ serde_json::Value::Array(values.iter().map(canonical_json).collect())
678
+ }
679
+ value => value.clone(),
680
+ }
681
+ }
682
+ #[wasm_bindgen]
683
+ pub fn canonical_bundle_hash(bundle_json: &str) -> Result<String, JsValue> {
684
+ use sha2::{Digest, Sha256};
685
+ let value: serde_json::Value =
686
+ serde_json::from_str(bundle_json).map_err(|e| JsValue::from_str(&e.to_string()))?;
687
+ let bytes = serde_json::to_vec(&canonical_json(&value))
688
+ .map_err(|e| JsValue::from_str(&e.to_string()))?;
689
+ Ok(format!("sha256:{:x}", Sha256::digest(bytes)))
690
+ }
691
+ #[wasm_bindgen]
692
+ pub fn verify_bundle_integrity(bundle_json: &str) -> Result<bool, JsValue> {
693
+ use sha2::{Digest, Sha256};
694
+ let value: serde_json::Value =
695
+ serde_json::from_str(bundle_json).map_err(|e| JsValue::from_str(&e.to_string()))?;
696
+ if value.get("format").and_then(|v| v.as_str()) != Some("feltdb-bundle/v1") {
697
+ return Ok(false);
698
+ }
699
+ let checksums = value
700
+ .get("checksums")
701
+ .ok_or_else(|| JsValue::from_str("checksums missing"))?;
702
+ let bytes = serde_json::to_vec(&canonical_json(checksums))
703
+ .map_err(|e| JsValue::from_str(&e.to_string()))?;
704
+ let root = format!("sha256:{:x}", Sha256::digest(bytes));
705
+ Ok(value
706
+ .pointer("/bundle/integrity_root")
707
+ .and_then(|v| v.as_str())
708
+ == Some(&root))
709
+ }
710
+
711
+ /// High-performance index manager for collections.
712
+ /// Wraps Rust IndexManager for optimal query performance.
713
+ #[wasm_bindgen]
714
+ pub struct WasmIndexManager {
715
+ manager: feltdb::IndexManager,
716
+ }
717
+
718
+ #[wasm_bindgen]
719
+ impl WasmIndexManager {
720
+ #[wasm_bindgen(constructor)]
721
+ pub fn new() -> WasmIndexManager {
722
+ WasmIndexManager {
723
+ manager: feltdb::IndexManager::new(),
724
+ }
725
+ }
726
+
727
+ /// Create a new index on the collection.
728
+ #[wasm_bindgen]
729
+ pub fn create_index(&mut self, config: &str) -> Result<(), JsValue> {
730
+ let index_config: feltdb::IndexConfig =
731
+ serde_json::from_str(config).map_err(|e| JsValue::from_str(&e.to_string()))?;
732
+ self.manager.create_index(index_config)
733
+ .map_err(|e| JsValue::from_str(&e))
734
+ }
735
+
736
+ /// Update a record's index entries.
737
+ #[wasm_bindgen]
738
+ pub fn update_record(
739
+ &mut self,
740
+ record_id: &str,
741
+ old_value: Option<String>,
742
+ new_value: &str,
743
+ ) -> Result<(), JsValue> {
744
+ self.manager
745
+ .update_record(record_id, old_value.as_deref(), new_value)
746
+ .map_err(|e| JsValue::from_str(&e))
747
+ }
748
+
749
+ /// Query an index.
750
+ #[wasm_bindgen]
751
+ pub fn query_index(
752
+ &mut self,
753
+ index_name: &str,
754
+ operator: &str,
755
+ value: &str,
756
+ ) -> Result<String, JsValue> {
757
+ let results = self.manager
758
+ .query_index(index_name, operator, value)
759
+ .map_err(|e| JsValue::from_str(&e))?;
760
+ serde_json::to_string(&results).map_err(|e| JsValue::from_str(&e.to_string()))
761
+ }
762
+
763
+ /// List all indexes.
764
+ #[wasm_bindgen]
765
+ pub fn list_indexes(&self) -> Result<String, JsValue> {
766
+ let indexes = self.manager.list_indexes();
767
+ serde_json::to_string(&indexes).map_err(|e| JsValue::from_str(&e.to_string()))
768
+ }
769
+
770
+ /// Get statistics for an index.
771
+ #[wasm_bindgen]
772
+ pub fn get_stats(&self, name: &str) -> Result<String, JsValue> {
773
+ let stats = self.manager
774
+ .get_stats(name)
775
+ .ok_or_else(|| JsValue::from_str("Index not found"))?;
776
+ serde_json::to_string(&stats).map_err(|e| JsValue::from_str(&e.to_string()))
777
+ }
778
+ }
779
+
780
+ /// Shard manager for horizontal scaling.
781
+ /// Distributes data across shards and handles rebalancing.
782
+ #[wasm_bindgen]
783
+ pub struct WasmShardManager {
784
+ manager: feltdb::ShardManager,
785
+ }
786
+
787
+ #[wasm_bindgen]
788
+ impl WasmShardManager {
789
+ #[wasm_bindgen(constructor)]
790
+ pub fn new(shard_field: &str, strategy: &str) -> WasmShardManager {
791
+ let sharding_key = feltdb::ShardKey {
792
+ field: shard_field.to_string(),
793
+ hash_function: "xxhash".to_string(),
794
+ };
795
+
796
+ let strat = match strategy {
797
+ "range" => feltdb::ShardingStrategy::RangeBasedHash,
798
+ "modulo" => feltdb::ShardingStrategy::KeyModulo,
799
+ _ => feltdb::ShardingStrategy::ConsistentHash,
800
+ };
801
+
802
+ WasmShardManager {
803
+ manager: feltdb::ShardManager::new(sharding_key, strat),
804
+ }
805
+ }
806
+
807
+ /// Initialize shards
808
+ #[wasm_bindgen]
809
+ pub fn initialize_shards(&mut self, shard_ids_json: &str) -> Result<(), JsValue> {
810
+ let shard_ids: Vec<String> = serde_json::from_str(shard_ids_json)
811
+ .map_err(|e| JsValue::from_str(&e.to_string()))?;
812
+
813
+ let shard_ids: Vec<_> = shard_ids
814
+ .into_iter()
815
+ .map(|id| feltdb::ShardId(id))
816
+ .collect();
817
+
818
+ self.manager
819
+ .initialize_shards(shard_ids, std::collections::HashMap::new());
820
+ Ok(())
821
+ }
822
+
823
+ /// Get shard for a key
824
+ #[wasm_bindgen]
825
+ pub fn get_shard_for_key(&self, key: &str) -> Result<String, JsValue> {
826
+ let shard = self
827
+ .manager
828
+ .get_shard_for_key(key)
829
+ .ok_or_else(|| JsValue::from_str("No shard found for key"))?;
830
+ Ok(shard.shard_id.0.clone())
831
+ }
832
+
833
+ /// Record a shard operation
834
+ #[wasm_bindgen]
835
+ pub fn record_shard_operation(
836
+ &mut self,
837
+ shard_id: &str,
838
+ operation: &str,
839
+ time_ms: f64,
840
+ records_affected: u32,
841
+ size_bytes: u64,
842
+ ) {
843
+ self.manager.record_shard_operation(
844
+ &feltdb::ShardId(shard_id.to_string()),
845
+ operation,
846
+ time_ms,
847
+ records_affected as usize,
848
+ size_bytes,
849
+ );
850
+ }
851
+
852
+ /// Get all shard metrics
853
+ #[wasm_bindgen]
854
+ pub fn get_all_metrics(&self) -> Result<String, JsValue> {
855
+ let metrics = self.manager.get_all_metrics();
856
+ serde_json::to_string(&metrics).map_err(|e| JsValue::from_str(&e.to_string()))
857
+ }
858
+
859
+ /// Detect hotspots
860
+ #[wasm_bindgen]
861
+ pub fn detect_hotspots(&self) -> Result<String, JsValue> {
862
+ let hotspots = self.manager.detect_hotspots();
863
+ serde_json::to_string(&hotspots).map_err(|e| JsValue::from_str(&e.to_string()))
864
+ }
865
+
866
+ /// Generate rebalance operations
867
+ #[wasm_bindgen]
868
+ pub fn generate_rebalance_ops(&self) -> Result<String, JsValue> {
869
+ let ops = self.manager.generate_rebalance_ops();
870
+ serde_json::to_string(&ops).map_err(|e| JsValue::from_str(&e.to_string()))
871
+ }
872
+
873
+ /// Get distribution summary
874
+ #[wasm_bindgen]
875
+ pub fn get_distribution_summary(&self) -> Result<String, JsValue> {
876
+ let summary = self.manager.get_distribution_summary();
877
+ serde_json::to_string(&summary).map_err(|e| JsValue::from_str(&e.to_string()))
878
+ }
879
+ }
880
+
881
+ /// Distributed index manager for multi-node synchronization.
882
+ /// Coordinates index replication, consistency, and conflict resolution.
883
+ #[wasm_bindgen]
884
+ pub struct WasmDistributedIndexManager {
885
+ manager: feltdb::DistributedIndexManager,
886
+ }
887
+
888
+ #[wasm_bindgen]
889
+ impl WasmDistributedIndexManager {
890
+ #[wasm_bindgen(constructor)]
891
+ pub fn new(peer_id: &str) -> WasmDistributedIndexManager {
892
+ WasmDistributedIndexManager {
893
+ manager: feltdb::DistributedIndexManager::new(peer_id.to_string()),
894
+ }
895
+ }
896
+
897
+ /// Register a peer for replication
898
+ #[wasm_bindgen]
899
+ pub fn register_peer(&mut self, peer_id: &str) {
900
+ self.manager.register_peer(peer_id.to_string());
901
+ }
902
+
903
+ /// Create a replicated index
904
+ #[wasm_bindgen]
905
+ pub fn create_replicated_index(
906
+ &mut self,
907
+ name: &str,
908
+ field: &str,
909
+ index_type: &str,
910
+ ) -> Result<(), JsValue> {
911
+ self.manager
912
+ .create_replicated_index(name.to_string(), field.to_string(), index_type.to_string())
913
+ .map_err(|e| JsValue::from_str(&e))
914
+ }
915
+
916
+ /// Get replication status for an index
917
+ #[wasm_bindgen]
918
+ pub fn get_replication_status(&self, index_name: &str) -> Result<String, JsValue> {
919
+ let status = self
920
+ .manager
921
+ .get_replication_status(index_name)
922
+ .ok_or_else(|| JsValue::from_str("Index not found"))?;
923
+ serde_json::to_string(&status).map_err(|e| JsValue::from_str(&e.to_string()))
924
+ }
925
+
926
+ /// Get list of peers needing replication for an index
927
+ #[wasm_bindgen]
928
+ pub fn get_replication_targets(&self, index_name: &str) -> Result<String, JsValue> {
929
+ let targets = self.manager.get_replication_targets(index_name);
930
+ serde_json::to_string(&targets).map_err(|e| JsValue::from_str(&e.to_string()))
931
+ }
932
+
933
+ /// Detect version conflicts
934
+ #[wasm_bindgen]
935
+ pub fn detect_conflicts(&self, index_name: &str) -> Result<String, JsValue> {
936
+ let conflicts = self.manager.detect_conflicts(index_name);
937
+ serde_json::to_string(&conflicts).map_err(|e| JsValue::from_str(&e.to_string()))
938
+ }
939
+
940
+ /// Merge concurrent versions
941
+ #[wasm_bindgen]
942
+ pub fn merge_versions(&mut self, index_name: &str) -> Result<(), JsValue> {
943
+ self.manager
944
+ .merge_versions(index_name)
945
+ .map_err(|e| JsValue::from_str(&e))
946
+ }
947
+
948
+ /// Set peer reachability
949
+ #[wasm_bindgen]
950
+ pub fn set_peer_reachable(&mut self, peer_id: &str, reachable: bool) {
951
+ self.manager.set_peer_reachable(peer_id, reachable);
952
+ }
953
+
954
+ /// Get overall synchronization status
955
+ #[wasm_bindgen]
956
+ pub fn get_sync_status(&self) -> Result<String, JsValue> {
957
+ let status = self.manager.get_sync_status();
958
+ serde_json::to_string(&status).map_err(|e| JsValue::from_str(&e.to_string()))
959
+ }
960
+ }
961
+
962
+ /// High-performance analytics engine for index monitoring.
963
+ /// Tracks metrics, generates reports, and provides tuning recommendations.
964
+ #[wasm_bindgen]
965
+ pub struct WasmAnalytics {
966
+ analytics: feltdb::analytics::IndexAnalytics,
967
+ }
968
+
969
+ #[wasm_bindgen]
970
+ impl WasmAnalytics {
971
+ #[wasm_bindgen(constructor)]
972
+ pub fn new() -> WasmAnalytics {
973
+ WasmAnalytics {
974
+ analytics: feltdb::analytics::IndexAnalytics::new(),
975
+ }
976
+ }
977
+
978
+ /// Record an index usage event.
979
+ #[wasm_bindgen]
980
+ pub fn record_index_usage(&mut self, index_name: &str, operation: &str, time_ms: f64) {
981
+ self.analytics.record_index_usage(index_name, operation, time_ms);
982
+ }
983
+
984
+ /// Record a query execution with performance metrics.
985
+ #[wasm_bindgen]
986
+ pub fn record_query(
987
+ &mut self,
988
+ fields_json: &str,
989
+ operator: &str,
990
+ execution_time_ms: f64,
991
+ rows_scanned: u64,
992
+ result_count: u64,
993
+ index_used: Option<String>,
994
+ ) -> Result<(), JsValue> {
995
+ let fields: Vec<String> = serde_json::from_str(fields_json)
996
+ .map_err(|e| JsValue::from_str(&e.to_string()))?;
997
+ self.analytics.record_query(
998
+ fields,
999
+ operator.to_string(),
1000
+ execution_time_ms,
1001
+ rows_scanned,
1002
+ result_count,
1003
+ index_used,
1004
+ );
1005
+ Ok(())
1006
+ }
1007
+
1008
+ /// Get metrics for a specific index.
1009
+ #[wasm_bindgen]
1010
+ pub fn get_index_metrics(&self, index_name: &str) -> Result<String, JsValue> {
1011
+ let metrics = self
1012
+ .analytics
1013
+ .get_index_metrics(index_name)
1014
+ .ok_or_else(|| JsValue::from_str("Index not found"))?;
1015
+ serde_json::to_string(&metrics).map_err(|e| JsValue::from_str(&e.to_string()))
1016
+ }
1017
+
1018
+ /// Get metrics for all indexes.
1019
+ #[wasm_bindgen]
1020
+ pub fn get_all_metrics(&self) -> Result<String, JsValue> {
1021
+ let metrics = self.analytics.get_all_metrics();
1022
+ serde_json::to_string(&metrics).map_err(|e| JsValue::from_str(&e.to_string()))
1023
+ }
1024
+
1025
+ /// Get slowest queries.
1026
+ #[wasm_bindgen]
1027
+ pub fn get_slowest_queries(&self, limit: usize) -> Result<String, JsValue> {
1028
+ let traces = self.analytics.get_slowest_queries(limit);
1029
+ serde_json::to_string(&traces).map_err(|e| JsValue::from_str(&e.to_string()))
1030
+ }
1031
+
1032
+ /// Get most frequently accessed fields.
1033
+ #[wasm_bindgen]
1034
+ pub fn get_frequent_fields(&self, limit: usize) -> Result<String, JsValue> {
1035
+ let fields = self.analytics.get_frequent_fields(limit);
1036
+ serde_json::to_string(&fields).map_err(|e| JsValue::from_str(&e.to_string()))
1037
+ }
1038
+
1039
+ /// Generate performance report for a collection.
1040
+ #[wasm_bindgen]
1041
+ pub fn generate_report(&self, collection_name: &str, period: &str) -> Result<String, JsValue> {
1042
+ let report = self.analytics.generate_report(collection_name, period);
1043
+ serde_json::to_string(&report).map_err(|e| JsValue::from_str(&e.to_string()))
1044
+ }
1045
+
1046
+ /// Reset all collected metrics.
1047
+ #[wasm_bindgen]
1048
+ pub fn reset(&mut self) {
1049
+ self.analytics.reset();
1050
+ }
1051
+ }
1052
+
1053
+ #[cfg(test)]
1054
+ mod tests {
1055
+ use super::*;
1056
+ use std::fs;
1057
+
1058
+ #[test]
1059
+ fn test_wasm_open() {
1060
+ let test_path = "test_wasm.db";
1061
+ let _db = open(test_path).expect("open should succeed");
1062
+ assert!(std::path::Path::new(test_path).exists());
1063
+ let _ = fs::remove_file(test_path);
1064
+ }
1065
+ #[test]
1066
+ fn wasm_schema_hash_uses_the_canonical_rust_contract() {
1067
+ let schema = feltdb::state_contract::StateSchema {
1068
+ contract_version: 1,
1069
+ schema_version: 2,
1070
+ application_id: "app".into(),
1071
+ revision_id: "rev".into(),
1072
+ collections: vec![],
1073
+ };
1074
+ let json = serde_json::to_string(&schema).unwrap();
1075
+ assert_eq!(
1076
+ hash_state_schema(&json).unwrap(),
1077
+ feltdb::state_contract::schema_hash(&schema).unwrap()
1078
+ );
1079
+ let validation = validate_state_schema(&json).unwrap();
1080
+ assert!(
1081
+ serde_json::from_str::<feltdb::state_contract::SchemaValidation>(&validation)
1082
+ .unwrap()
1083
+ .valid
1084
+ );
1085
+ }
1086
+ }