@feltdb/core 0.12.1 → 0.13.0

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 (139) hide show
  1. package/dist/build-identity.json +13 -4
  2. package/dist/cli/commands.js +96 -45
  3. package/dist/cli/workspace-integration.js +8 -0
  4. package/dist/collection.d.ts +41 -4
  5. package/dist/collection.d.ts.map +1 -1
  6. package/dist/collection.js +116 -47
  7. package/dist/composition.d.ts.map +1 -1
  8. package/dist/composition.js +11 -1
  9. package/dist/create/build-identity.json +13 -4
  10. package/dist/create/cli.js +49 -40
  11. package/dist/create/create.js +32 -19
  12. package/dist/create/package-versions.js +1 -1
  13. package/dist/create/server-source/Cargo.lock +18 -3
  14. package/dist/create/server-source/Cargo.toml +4 -2
  15. package/dist/create/server-source/crates/feltdb/Cargo.toml +20 -0
  16. package/dist/create/server-source/crates/feltdb/LICENSE +21 -0
  17. package/dist/create/server-source/crates/feltdb/README.md +75 -0
  18. package/dist/create/server-source/crates/feltdb/src/agent_state.rs +320 -0
  19. package/dist/create/server-source/crates/feltdb/src/agent_transition_contract.rs +1545 -0
  20. package/dist/create/server-source/crates/feltdb/src/authority.rs +145 -15
  21. package/dist/create/server-source/crates/feltdb/src/lib.rs +227 -23
  22. package/dist/create/server-source/crates/feltdb/src/state_model.rs +1 -5
  23. package/dist/create/server-source/crates/feltdb/src/worker_mesh.rs +204 -1
  24. package/dist/create/server-source/crates/feltdb/src/workload.rs +685 -53
  25. package/dist/create/server-source/crates/feltdb/tests/agent_state_native.rs +1188 -0
  26. package/dist/create/server-source/crates/feltdb/tests/agent_transition_contract_conformance.rs +2884 -0
  27. package/dist/create/server-source/crates/feltdb/tests/authority_transaction_identity.rs +534 -0
  28. package/dist/create/server-source/crates/feltdb/tests/fixtures/agent_transition_corpus.json +23340 -0
  29. package/dist/create/server-source/crates/feltdb/tests/production_readiness_contract.rs +18 -12
  30. package/dist/create/server-source/crates/feltdb/tests/workload_envelope_contract.rs +17 -5
  31. package/dist/create/server-source/crates/feltdb-client/Cargo.toml +29 -0
  32. package/dist/create/server-source/crates/feltdb-client/LICENSE +21 -0
  33. package/dist/create/server-source/crates/feltdb-client/README.md +70 -0
  34. package/dist/create/server-source/crates/feltdb-client/src/application.rs +527 -0
  35. package/dist/create/server-source/crates/feltdb-client/src/authority.rs +679 -0
  36. package/dist/create/server-source/crates/feltdb-client/src/control.rs +292 -0
  37. package/dist/create/server-source/crates/feltdb-client/src/error.rs +227 -0
  38. package/dist/create/server-source/crates/feltdb-client/src/events.rs +146 -0
  39. package/dist/create/server-source/crates/feltdb-client/src/lib.rs +466 -0
  40. package/dist/create/server-source/crates/feltdb-client/tests/fixtures/README.md +4 -0
  41. package/dist/create/server-source/crates/feltdb-client/tests/fixtures/private-ca.pem +20 -0
  42. package/dist/create/server-source/crates/feltdb-client/tests/fixtures/server-key.pem +28 -0
  43. package/dist/create/server-source/crates/feltdb-client/tests/fixtures/server.pem +20 -0
  44. package/dist/create/server-source/crates/feltdb-client/tests/private_ca.rs +95 -0
  45. package/dist/create/server-source/crates/feltdb-server/Cargo.toml +20 -1
  46. package/dist/create/server-source/crates/feltdb-server/LICENSE +21 -0
  47. package/dist/create/server-source/crates/feltdb-server/README.md +12 -1
  48. package/dist/create/server-source/crates/feltdb-server/src/agent_execution.rs +511 -0
  49. package/dist/create/server-source/crates/feltdb-server/src/auth.rs +28 -0
  50. package/dist/create/server-source/crates/feltdb-server/src/backup.rs +6 -2
  51. package/dist/create/server-source/crates/feltdb-server/src/lib.rs +21 -0
  52. package/dist/create/server-source/crates/feltdb-server/src/main.rs +845 -138
  53. package/dist/create/server-source/crates/feltdb-server/tests/rust_client.rs +683 -0
  54. package/dist/create/server-source/crates/feltdb-wasm/Cargo.toml +3 -1
  55. package/dist/create/setup-summary.js +40 -0
  56. package/dist/create/template/default-project/README.md +12 -7
  57. package/dist/create/template/default-project/src/pages/Dashboard.tsx +1 -1
  58. package/dist/create/template/dot-feltdb-README.md +3 -3
  59. package/dist/db.d.ts +27 -1
  60. package/dist/db.d.ts.map +1 -1
  61. package/dist/db.js +60 -21
  62. package/dist/development-runtime-bridge.d.ts +20 -0
  63. package/dist/development-runtime-bridge.d.ts.map +1 -1
  64. package/dist/development-runtime-bridge.js +74 -4
  65. package/dist/embedded-transaction.d.ts +75 -8
  66. package/dist/embedded-transaction.d.ts.map +1 -1
  67. package/dist/embedded-transaction.js +124 -18
  68. package/dist/error-codes.d.ts +2 -0
  69. package/dist/error-codes.d.ts.map +1 -1
  70. package/dist/error-codes.js +2 -0
  71. package/dist/feltdb.d.ts +1 -11
  72. package/dist/feltdb.d.ts.map +1 -1
  73. package/dist/file-db.d.ts +6 -0
  74. package/dist/file-db.d.ts.map +1 -1
  75. package/dist/file-db.js +43 -11
  76. package/dist/http-db.d.ts +13 -0
  77. package/dist/http-db.d.ts.map +1 -1
  78. package/dist/http-db.js +33 -4
  79. package/dist/indexeddb-db.d.ts.map +1 -1
  80. package/dist/indexeddb-db.js +65 -41
  81. package/dist/memory-db.d.ts.map +1 -1
  82. package/dist/memory-db.js +29 -8
  83. package/dist/studio/{KeyManagementPanel-DZuSeWBK.js → KeyManagementPanel-skUNnKRw.js} +36 -28
  84. package/dist/studio/app.d.ts +8 -1
  85. package/dist/studio/app.d.ts.map +1 -1
  86. package/dist/studio/components/KeyManagementPanel.d.ts.map +1 -1
  87. package/dist/studio/components/KeyManagementPanel.js +1 -1
  88. package/dist/studio/components/RouteErrorBoundary.d.ts +5 -0
  89. package/dist/studio/components/RouteErrorBoundary.d.ts.map +1 -0
  90. package/dist/studio/components/index.js +2 -2
  91. package/dist/studio/{components-Bivm2lX6.js → components-BSGmHfif.js} +6 -6
  92. package/dist/studio/control-plane/Agents.d.ts.map +1 -1
  93. package/dist/studio/control-plane/Data.d.ts.map +1 -1
  94. package/dist/studio/control-plane/Instance.d.ts +4 -1
  95. package/dist/studio/control-plane/Instance.d.ts.map +1 -1
  96. package/dist/studio/control-plane/Operations.d.ts +4 -1
  97. package/dist/studio/control-plane/Operations.d.ts.map +1 -1
  98. package/dist/studio/control-plane/Overview.d.ts.map +1 -1
  99. package/dist/studio/control-plane/Schema.d.ts.map +1 -1
  100. package/dist/studio/control-plane/context.d.ts +4 -1
  101. package/dist/studio/control-plane/context.d.ts.map +1 -1
  102. package/dist/studio/control-plane/index.js +2 -2
  103. package/dist/studio/control-plane/primitives.d.ts +24 -0
  104. package/dist/studio/control-plane/primitives.d.ts.map +1 -1
  105. package/dist/studio/{control-plane-CLm69XwX.js → control-plane-CaBA7Rgv.js} +1374 -1298
  106. package/dist/studio/index.d.ts +1 -0
  107. package/dist/studio/index.d.ts.map +1 -1
  108. package/dist/studio/index.js +415 -237
  109. package/dist/studio/navigation.d.ts +57 -0
  110. package/dist/studio/navigation.d.ts.map +1 -0
  111. package/dist/studio/studio.css +1 -1
  112. package/dist/studio/utils/control-plane-api.d.ts +2 -0
  113. package/dist/studio/utils/control-plane-api.d.ts.map +1 -1
  114. package/dist/studio/utils/control-plane-api.js +9 -4
  115. package/dist/studio/utils/service-api.d.ts.map +1 -1
  116. package/dist/studio/utils/service-api.js +5 -4
  117. package/dist/studio-app/assets/{feltdb_wasm-DACcD-zs.js → feltdb_wasm-3hDngl1h.js} +1 -1
  118. package/dist/studio-app/assets/feltdb_wasm_bg-CFQT4gCy.wasm +0 -0
  119. package/dist/studio-app/assets/index-Dql1hS5m.js +30 -0
  120. package/dist/studio-app/assets/{index-CiC8VY-a.css → index-vi21oEQa.css} +1 -1
  121. package/dist/studio-app/index.html +2 -2
  122. package/dist/telemetry.d.ts +1 -0
  123. package/dist/telemetry.d.ts.map +1 -1
  124. package/dist/telemetry.js +4 -3
  125. package/dist/transaction-identity.d.ts +62 -0
  126. package/dist/transaction-identity.d.ts.map +1 -0
  127. package/dist/transaction-identity.js +138 -0
  128. package/dist/transaction.d.ts +50 -5
  129. package/dist/transaction.d.ts.map +1 -1
  130. package/dist/transaction.js +28 -0
  131. package/dist/wasm/feltdb_wasm_bg.wasm +0 -0
  132. package/dist/wasm/package.json +1 -1
  133. package/dist/workspace/local-development-authority.d.ts +19 -0
  134. package/dist/workspace/local-development-authority.d.ts.map +1 -1
  135. package/dist/workspace/local-development-authority.js +335 -15
  136. package/package.json +1 -1
  137. package/dist/create/server-source/crates/feltdb-wasm/test.db +0 -0
  138. package/dist/studio-app/assets/feltdb_wasm_bg-BejThWID.wasm +0 -0
  139. package/dist/studio-app/assets/index-CoZaxnlm.js +0 -30
@@ -1020,19 +1020,25 @@ fn retention_cost_grows_with_the_window_and_inflates_the_log() {
1020
1020
 
1021
1021
  // Compare the best of repeated independent runs. The workspace executes
1022
1022
  // other crash/soak processes concurrently, so a single wall-clock sample
1023
- // can measure scheduler delay rather than retention work. Taking the
1024
- // minimum preserves the envelope claim while filtering that one-sided
1025
- // noise; every sample still performs the complete durable workload.
1026
- fn sample(run_once: impl Fn() -> (std::time::Duration, u64)) -> (std::time::Duration, u64) {
1027
- (0..3)
1028
- .map(|_| run_once())
1029
- .min_by_key(|(elapsed, _)| *elapsed)
1030
- .unwrap()
1023
+ // can measure scheduler delay rather than retention work. The minimum
1024
+ // filters that one-sided noise, but only if every configuration is sampled
1025
+ // across the same stretch of time: sampling them one after another lets a
1026
+ // busy sibling inflate one configuration's every sample and not another's.
1027
+ // So each round runs all three, and each keeps its own best. Every sample
1028
+ // still performs the complete durable workload.
1029
+ let mut none = (std::time::Duration::MAX, 0u64);
1030
+ let mut small = (std::time::Duration::MAX, 0u64);
1031
+ let mut large = std::time::Duration::MAX;
1032
+ for _ in 0..9 {
1033
+ let sample = run(None);
1034
+ if sample.0 < none.0 { none = sample; }
1035
+ let sample = run(Some(5));
1036
+ if sample.0 < small.0 { small = sample; }
1037
+ large = large.min(run(Some(50)).0);
1031
1038
  }
1032
-
1033
- let (none_time, none_bytes) = sample(|| run(None));
1034
- let (small_time, small_bytes) = sample(|| run(Some(5)));
1035
- let (large_time, _) = sample(|| run(Some(50)));
1039
+ let (none_time, none_bytes) = none;
1040
+ let (small_time, small_bytes) = small;
1041
+ let large_time = large;
1036
1042
 
1037
1043
  assert!(
1038
1044
  large_time > small_time,
@@ -215,14 +215,26 @@ fn a_barrier_per_write_costs_more_than_none() {
215
215
  let directory = TempDir::new().unwrap();
216
216
  let workload = WORKLOADS[0];
217
217
 
218
- let flushed = run(directory.path(), workload, DurabilityMode::Flushed);
218
+ // A stall can only invert this ordering by inflating the cheap, flushed
219
+ // run, so take the best of several of those against one synced run. The
220
+ // minimum is the cost of the mode with scheduler contention taken out;
221
+ // repeating the slow synced run would only add load to sibling timing
222
+ // tests in this concurrently-run binary.
219
223
  let synced = run(directory.path(), workload, DurabilityMode::Synced);
224
+ let flushed = run(directory.path(), workload, DurabilityMode::Flushed);
225
+ let mut best_flushed = flushed.elapsed;
226
+ for sample in 0..4 {
227
+ // A fresh log per sample: a reused path would append to its history.
228
+ let sample_directory = directory.path().join(format!("flushed-{sample}"));
229
+ std::fs::create_dir_all(&sample_directory).unwrap();
230
+ let repeat = run(&sample_directory, workload, DurabilityMode::Flushed);
231
+ best_flushed = best_flushed.min(repeat.elapsed);
232
+ }
220
233
 
221
234
  assert!(
222
- synced.elapsed > flushed.elapsed,
223
- "a barrier per write must cost something: synced {:?} vs flushed {:?}",
224
- synced.elapsed,
225
- flushed.elapsed
235
+ synced.elapsed > best_flushed,
236
+ "a barrier per write must cost something: synced {:?} vs flushed {best_flushed:?}",
237
+ synced.elapsed
226
238
  );
227
239
 
228
240
  // Durability is the only thing that changed. The database is identical.
@@ -0,0 +1,29 @@
1
+ [package]
2
+ name = "feltdb-client"
3
+ version.workspace = true
4
+ edition.workspace = true
5
+ # The oldest toolchain CI builds and tests this workspace with (.github/workflows/ci.yml).
6
+ rust-version = "1.96"
7
+ description = "Native async Rust client for a FeltDB server: records, atomic and idempotent transactions, compare-and-set, bounded queries, read-basis fences, events, and the canonical application API"
8
+ license = "MIT"
9
+ repository = "https://github.com/rkendel1/flow_db"
10
+ readme = "README.md"
11
+ keywords = ["database", "client", "transactions", "feltdb", "http"]
12
+ categories = ["database", "api-bindings", "asynchronous"]
13
+ exclude = ["tests/", "target/"]
14
+
15
+ [dependencies]
16
+ # The wire types are the server's own: the request and response documents are defined once, in
17
+ # `feltdb`, and both sides serialize the same structs. Exact, because the modules they come from are
18
+ # not yet covered by `feltdb`'s compatibility policy; the workspace releases both at one version.
19
+ feltdb = { version = "=0.2.2", path = "../feltdb" }
20
+ reqwest = { version = "0.12", default-features = false, features = ["json", "rustls-tls"] }
21
+ serde = { version = "1", features = ["derive"] }
22
+ serde_json = "1"
23
+ uuid = { version = "1.6", features = ["v4"] }
24
+
25
+ [dev-dependencies]
26
+ serde_json = "1"
27
+ tokio = { version = "1", features = ["io-util", "macros", "net", "rt-multi-thread"] }
28
+ # A TLS server for the private-CA test; the same rustls reqwest uses.
29
+ tokio-rustls = { version = "0.26", default-features = false, features = ["ring", "tls12"] }
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 FeltDB Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,70 @@
1
+ # feltdb-client
2
+
3
+ A native async Rust client for a [FeltDB](https://github.com/rkendel1/flow_db) server: the Rust
4
+ counterpart of `@feltdb/core`'s server runtime. It holds no state of its own; the server is the
5
+ authority for every read and write.
6
+
7
+ ```toml
8
+ [dependencies]
9
+ feltdb-client = "0.2"
10
+ serde_json = "1"
11
+ tokio = { version = "1", features = ["macros", "rt-multi-thread"] }
12
+ ```
13
+
14
+ ```rust
15
+ use feltdb_client::{Client, Operation, Precondition, Transaction};
16
+ use serde_json::json;
17
+
18
+ #[tokio::main]
19
+ async fn main() -> feltdb_client::Result<()> {
20
+ let client = Client::builder("http://127.0.0.1:7700")
21
+ .token(std::env::var("FELTDB_TOKEN").unwrap_or_default())
22
+ .build()?;
23
+
24
+ // One atomic commit. The id makes a retry after an uncertain outcome safe.
25
+ let tx = Transaction::new("ship-o-1")
26
+ .operation(Operation::put("orders", "o-1", json!({ "status": "shipped" })).expect_version(1))
27
+ .operation(Operation::create("audit", "a-1", json!({ "order": "o-1" })))
28
+ .precondition(Precondition::absent("locks", "o-1"));
29
+ match client.commit(&tx).await {
30
+ Ok(receipt) => println!("committed at revision {}", receipt.commit_revision),
31
+ Err(error) if error.is_conflict() => println!("lost a race: {:?}", error.precondition_failure()),
32
+ Err(error) if error.is_outcome_unknown() => { /* resubmit `tx` unchanged */ }
33
+ Err(error) => return Err(error),
34
+ }
35
+ Ok(())
36
+ }
37
+ ```
38
+
39
+ ## What it covers
40
+
41
+ | Server surface | Client |
42
+ | --- | --- |
43
+ | Records: `GET/POST /collections/…`, `PATCH`, `DELETE` | `get`, `list`, `create`, `update`, `delete` |
44
+ | Atomic, idempotent transactions with preconditions and fences: `POST /transactions` | `commit` with `Transaction`, `Operation`, `Precondition` |
45
+ | Read bases: `POST /transactions/read-basis` | `read_basis`, then `Transaction::fence` |
46
+ | Compare-and-set: `POST /collections/{c}/{id}/cas` | `compare_and_set` → `CasOutcome` |
47
+ | Bounded query: `POST /query` | `query` with `BoundedQuery` |
48
+ | Committed revision: `GET /revision` | `revision` |
49
+ | Live events: `GET /events` (SSE) | `events` → `EventStream` |
50
+ | Canonical application API under `/v1` | `client.application(id)`: `discover`, `query`, `commit` (per-operation `if_version` via `update_if_version`/`delete_if_version`), `read_basis`, `events`, `replay`, `provenance`, `sign_in`/`sign_up`/`session`/`sign_out` |
51
+ | Committed state version: `GET /v1/state/version` | `application.state_version()` |
52
+ | Control plane: `/api/tenants`, `/api/applications/…` drafts, revisions, promotion; `POST /admin/backups` | `client.control()`: `create_tenant`, `create_application`, `create_draft`, `update_draft`, `validate_draft`, `commit_draft`, `revision`, `promote`, `backup` |
53
+ | Health: `GET /health` | `health` |
54
+ | Capability discovery | `capabilities` |
55
+ | Anything else | `request(Method, path, body)` |
56
+
57
+ Errors carry the server's stable `code`, its `request_id`, and for a refused transaction the
58
+ structured `PreconditionFailure`. The canonical query and transaction documents are `feltdb`'s
59
+ own types (re-exported under `feltdb_client::wire`), so the client and server serialize the same
60
+ structs.
61
+
62
+ A server behind a private CA is trusted with
63
+ `Client::builder(url).add_root_certificate_pem(pem)`, in addition to the bundled public roots; verification is never
64
+ disabled.
65
+
66
+ The event stream does not reconnect by itself; reopen it from `EventStream::last_event_id()`.
67
+ For an embedded store in your own process, use the [`feltdb`](https://crates.io/crates/feltdb)
68
+ crate instead; to run the server, `cargo install feltdb-server`.
69
+
70
+ License: MIT.