create-feltdb 0.9.0 → 0.10.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 (29) hide show
  1. package/dist/package-versions.js +1 -1
  2. package/dist/server-source/crates/feltdb/src/acceptance_tests.rs +8 -1
  3. package/dist/server-source/crates/feltdb/src/application.rs +16 -0
  4. package/dist/server-source/crates/feltdb/src/bin/feltdb_node.rs +21 -22
  5. package/dist/server-source/crates/feltdb/src/lib.rs +451 -33
  6. package/dist/server-source/crates/feltdb/src/state_contract.rs +48 -3
  7. package/dist/server-source/crates/feltdb/src/state_model.rs +569 -4
  8. package/dist/server-source/crates/feltdb/src/state_trigger.rs +1 -0
  9. package/dist/server-source/crates/feltdb/src/versioned_object_substrate.rs +708 -0
  10. package/dist/server-source/crates/feltdb/src/worker_mesh.rs +1 -0
  11. package/dist/server-source/crates/feltdb/src/workload.rs +480 -44
  12. package/dist/server-source/crates/feltdb/tests/generalized_version_control_evidence.rs +123 -0
  13. package/dist/server-source/crates/feltdb/tests/generalized_version_control_substrate_completion.rs +281 -0
  14. package/dist/server-source/crates/feltdb/tests/production_readiness_contract.rs +15 -3
  15. package/dist/server-source/crates/feltdb/tests/revision_retention_boundary_evidence.rs +28 -18
  16. package/dist/server-source/crates/feltdb/tests/storage_independent_versioned_object_substrate.rs +328 -0
  17. package/dist/server-source/crates/feltdb/tests/workload_envelope_contract.rs +20 -3
  18. package/dist/server-source/crates/feltdb-server/src/app_state.rs +14 -4
  19. package/dist/server-source/crates/feltdb-server/src/backup.rs +0 -4
  20. package/dist/server-source/crates/feltdb-server/src/canonical_principals.rs +1093 -0
  21. package/dist/server-source/crates/feltdb-server/src/causal.rs +905 -144
  22. package/dist/server-source/crates/feltdb-server/src/connections.rs +9 -6
  23. package/dist/server-source/crates/feltdb-server/src/control_plane.rs +90 -0
  24. package/dist/server-source/crates/feltdb-server/src/lib.rs +1 -0
  25. package/dist/server-source/crates/feltdb-server/src/main.rs +443 -77
  26. package/dist/server-source/crates/feltdb-server/src/principals.rs +9 -501
  27. package/dist/server-source/crates/feltdb-server/src/tenancy.rs +22 -0
  28. package/dist/server-source/crates/feltdb-server/tests/principal_tenant_migration.rs +247 -0
  29. package/package.json +1 -1
@@ -1,4 +1,4 @@
1
1
  // One release train keeps generated applications installable. The repository
2
2
  // validation script checks these values against every workspace manifest.
3
- export const FELTDB_PACKAGE_VERSION = '0.9.0';
3
+ export const FELTDB_PACKAGE_VERSION = '0.10.0';
4
4
  export const feltdbPackageRange = `^${FELTDB_PACKAGE_VERSION}`;
@@ -1460,9 +1460,12 @@ mod convergence_acceptance_tests {
1460
1460
  use crate::FeltDb;
1461
1461
  use serde::{Deserialize, Serialize};
1462
1462
  use std::path::PathBuf;
1463
+ use std::sync::atomic::{AtomicU64, Ordering};
1463
1464
  use std::time::SystemTime;
1464
1465
  use std::time::UNIX_EPOCH;
1465
1466
 
1467
+ static TEMP_FILE_SEQUENCE: AtomicU64 = AtomicU64::new(0);
1468
+
1466
1469
  #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1467
1470
  struct Task {
1468
1471
  title: String,
@@ -1475,7 +1478,11 @@ mod convergence_acceptance_tests {
1475
1478
  .duration_since(UNIX_EPOCH)
1476
1479
  .expect("time")
1477
1480
  .as_nanos();
1478
- std::env::temp_dir().join(format!("feltdb-convergence-test-{nanos}.data"))
1481
+ let sequence = TEMP_FILE_SEQUENCE.fetch_add(1, Ordering::Relaxed);
1482
+ std::env::temp_dir().join(format!(
1483
+ "feltdb-convergence-test-{}-{nanos}-{sequence}.data",
1484
+ std::process::id()
1485
+ ))
1479
1486
  }
1480
1487
 
1481
1488
  /// Test convergence: Two instances make concurrent mutations on same key
@@ -1203,6 +1203,22 @@ impl ApplicationStore {
1203
1203
  records: Arc::new(RwLock::new(records)),
1204
1204
  })
1205
1205
  }
1206
+
1207
+ /// Return every tenant scope asserted by canonical application definition
1208
+ /// records for one stable application identity. Callers must reject zero
1209
+ /// or multiple distinct tenants; ordering is never ownership evidence.
1210
+ pub fn tenant_ownership_candidates(&self, application_id: &str) -> Vec<String> {
1211
+ let Ok(records) = self.records.read() else {
1212
+ return Vec::new();
1213
+ };
1214
+ records
1215
+ .revisions
1216
+ .iter()
1217
+ .map(|value| (&value.application_id, &value.tenant_id))
1218
+ .filter(|(candidate, _)| candidate.as_str() == application_id)
1219
+ .map(|(_, tenant)| tenant.clone())
1220
+ .collect()
1221
+ }
1206
1222
  fn persist(&self, r: &RevisionRecords) -> Result<(), String> {
1207
1223
  if let Some(parent) = self.path.parent() {
1208
1224
  fs::create_dir_all(parent).map_err(|e| e.to_string())?;
@@ -744,11 +744,12 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
744
744
  link.peer.clone(),
745
745
  envelope.envelope_id.sequence,
746
746
  );
747
- if link.io.send(message.clone()).await.is_ok() {
748
- delivered.push(link.peer.clone());
749
- } else if connect_with_retry(link, 3).await
750
- && link.io.send(message).await.is_ok()
751
- {
747
+ // Forwarding, like re-sending, is an explicit
748
+ // recovery operation. Prefer a fresh connection so
749
+ // a peer restart cannot leave this write accepted
750
+ // by a stale local socket and delivered nowhere.
751
+ let _ = connect_with_retry(link, 3).await;
752
+ if link.io.send(message).await.is_ok() {
752
753
  delivered.push(link.peer.clone());
753
754
  }
754
755
  }
@@ -793,19 +794,16 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
793
794
  link.peer.clone(),
794
795
  envelope.envelope_id.sequence,
795
796
  );
796
- // The peer this is aimed at may have restarted
797
- // since the link was established, which is the
798
- // whole point of re-sending. Reconnect and retry,
799
- // as `execute-tx` does.
800
- match link.io.send(message.clone()).await {
801
- Ok(()) => delivered.push(link.peer.clone()),
802
- Err(_) => {
803
- if connect_with_retry(link, 3).await
804
- && link.io.send(message).await.is_ok()
805
- {
806
- delivered.push(link.peer.clone());
807
- }
808
- }
797
+ // Re-sending is the recovery primitive used after a
798
+ // peer restart. A write to the old TCP socket can
799
+ // still return success after the peer has exited,
800
+ // because that only confirms acceptance by the
801
+ // local kernel. Try to establish a fresh connection
802
+ // first. If the peer never restarted, the connect
803
+ // attempt leaves the established stream untouched.
804
+ let _ = connect_with_retry(link, 3).await;
805
+ if link.io.send(message).await.is_ok() {
806
+ delivered.push(link.peer.clone());
809
807
  }
810
808
  }
811
809
  println!(
@@ -961,12 +959,13 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
961
959
 
962
960
  let mut delivered = Vec::new();
963
961
  for link in &send_links {
962
+ // Acknowledgements are durable recovery evidence and are
963
+ // commonly rebroadcast after a peer restart. Refresh the
964
+ // connection first for the same reason as explicit
965
+ // envelope replay.
966
+ let _ = connect_with_retry(link, 3).await;
964
967
  if link.io.send_acknowledgement(ack.clone()).await.is_ok() {
965
968
  delivered.push(link.peer.clone());
966
- } else if connect_with_retry(link, 3).await
967
- && link.io.send_acknowledgement(ack.clone()).await.is_ok()
968
- {
969
- delivered.push(link.peer.clone());
970
969
  }
971
970
  }
972
971
  println!(