@feltdb/core 0.8.3 → 0.8.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/commands.js +4 -1
- package/dist/cli/provisioning-neutrality.js +79 -0
- package/dist/collection.d.ts +43 -1
- package/dist/collection.d.ts.map +1 -1
- package/dist/collection.js +192 -22
- package/dist/create/create.js +25 -21
- package/dist/create/managed-account.js +11 -0
- package/dist/create/package-versions.js +1 -1
- package/dist/create/server-source/Cargo.lock +165 -0
- package/dist/create/server-source/Cargo.toml +9 -0
- package/dist/create/server-source/crates/feltdb/Cargo.toml +3 -0
- package/dist/create/server-source/crates/feltdb/benches/gate13_baseline.rs +44 -44
- package/dist/create/server-source/crates/feltdb/benches/gate13_phase_7_1_release_economics.rs +12 -24
- package/dist/create/server-source/crates/feltdb/benches/gate_13_redux.rs +7 -13
- package/dist/create/server-source/crates/feltdb/benches/gate_13_regression_runner.rs +13 -10
- package/dist/create/server-source/crates/feltdb/benches/gate_14a_concurrent_writer_scaling.rs +12 -9
- package/dist/create/server-source/crates/feltdb/benches/gate_14a_production_admission_revalidation.rs +78 -25
- package/dist/create/server-source/crates/feltdb/benches/gate_14a_rc2_admission_contract.rs +16 -13
- package/dist/create/server-source/crates/feltdb/benches/gate_14a_rc_root_cause.rs +13 -5
- package/dist/create/server-source/crates/feltdb/benches/gate_14a_sync1_queued_prototype.rs +41 -22
- package/dist/create/server-source/crates/feltdb/benches/gate_14a_sync_economics.rs +33 -15
- package/dist/create/server-source/crates/feltdb/benches/gate_14b_causal_backlog_scaling.rs +100 -33
- package/dist/create/server-source/crates/feltdb/benches/gate_14c_replication_contract_test.rs +56 -20
- package/dist/create/server-source/crates/feltdb/benches/gate_14c_replication_scaling.rs +116 -41
- package/dist/create/server-source/crates/feltdb/benches/gate_14d_combined_dimension_scaling.rs +186 -55
- package/dist/create/server-source/crates/feltdb/benches/phase_7_1_2_optimization_benchmark.rs +64 -26
- package/dist/create/server-source/crates/feltdb/benches/phase_7_1_3_crossover_analysis.rs +46 -15
- package/dist/create/server-source/crates/feltdb/src/admission.rs +8 -15
- package/dist/create/server-source/crates/feltdb/src/admission_contract_tests.rs +43 -13
- package/dist/create/server-source/crates/feltdb/src/adversarial_transport.rs +15 -42
- package/dist/create/server-source/crates/feltdb/src/analytics.rs +65 -19
- package/dist/create/server-source/crates/feltdb/src/application.rs +113 -30
- package/dist/create/server-source/crates/feltdb/src/authorization_security_tests.rs +475 -140
- package/dist/create/server-source/crates/feltdb/src/cardinality_diagnostics.rs +17 -15
- package/dist/create/server-source/crates/feltdb/src/cardinality_endpoint.rs +0 -1
- package/dist/create/server-source/crates/feltdb/src/causal_backlog_bound.rs +59 -15
- package/dist/create/server-source/crates/feltdb/src/causal_dependency_barrier.rs +266 -114
- package/dist/create/server-source/crates/feltdb/src/causal_dependency_barrier_phase_7_1.rs +25 -7
- package/dist/create/server-source/crates/feltdb/src/concurrency_fuzzing.rs +10 -15
- package/dist/create/server-source/crates/feltdb/src/consistency_contract.rs +3 -11
- package/dist/create/server-source/crates/feltdb/src/crash_atomic_boundary.rs +14 -5
- package/dist/create/server-source/crates/feltdb/src/crash_injection.rs +21 -25
- package/dist/create/server-source/crates/feltdb/src/crash_recovery_tests.rs +14 -11
- package/dist/create/server-source/crates/feltdb/src/dedup_bound_investigation.rs +103 -22
- package/dist/create/server-source/crates/feltdb/src/distributed_indexing.rs +18 -15
- package/dist/create/server-source/crates/feltdb/src/durability_guarantees.rs +12 -8
- package/dist/create/server-source/crates/feltdb/src/durable_dedup_set.rs +1 -5
- package/dist/create/server-source/crates/feltdb/src/durable_operation_identity.rs +87 -23
- package/dist/create/server-source/crates/feltdb/src/durable_operation_log.rs +3 -7
- package/dist/create/server-source/crates/feltdb/src/durable_sync.rs +10 -9
- package/dist/create/server-source/crates/feltdb/src/equality_index.rs +595 -0
- package/dist/create/server-source/crates/feltdb/src/in_process_transport.rs +1 -6
- package/dist/create/server-source/crates/feltdb/src/indexing.rs +35 -38
- package/dist/create/server-source/crates/feltdb/src/lib.rs +1050 -117
- package/dist/create/server-source/crates/feltdb/src/managed_cas_tests.rs +4 -1
- package/dist/create/server-source/crates/feltdb/src/metrics.rs +0 -1
- package/dist/create/server-source/crates/feltdb/src/multi_node_convergence.rs +1 -2
- package/dist/create/server-source/crates/feltdb/src/multi_operation_transaction.rs +107 -30
- package/dist/create/server-source/crates/feltdb/src/observability.rs +19 -6
- package/dist/create/server-source/crates/feltdb/src/operation_algebra.rs +12 -11
- package/dist/create/server-source/crates/feltdb/src/operation_log.rs +9 -4
- package/dist/create/server-source/crates/feltdb/src/p1_application_atomicity.rs +65 -18
- package/dist/create/server-source/crates/feltdb/src/p1_atomicity_acceptance.rs +193 -57
- package/dist/create/server-source/crates/feltdb/src/partition_reconciliation.rs +37 -27
- package/dist/create/server-source/crates/feltdb/src/permutation_scheduler.rs +38 -10
- package/dist/create/server-source/crates/feltdb/src/persistence_reality.rs +20 -14
- package/dist/create/server-source/crates/feltdb/src/phase1b_acceptance.rs +394 -229
- package/dist/create/server-source/crates/feltdb/src/phase1c1_acceptance.rs +8 -6
- package/dist/create/server-source/crates/feltdb/src/phase1c2_acceptance.rs +11 -13
- package/dist/create/server-source/crates/feltdb/src/phase1c3_acceptance.rs +90 -72
- package/dist/create/server-source/crates/feltdb/src/phase1c_atomicity_proof.rs +3 -3
- package/dist/create/server-source/crates/feltdb/src/phase5_integration.rs +33 -11
- package/dist/create/server-source/crates/feltdb/src/phase5_scenarios.rs +6 -6
- package/dist/create/server-source/crates/feltdb/src/phase6_adversarial_scenarios.rs +14 -56
- package/dist/create/server-source/crates/feltdb/src/phase6_convergence_validator.rs +29 -27
- package/dist/create/server-source/crates/feltdb/src/phase6_persistence.rs +35 -17
- package/dist/create/server-source/crates/feltdb/src/phase_1c_real_tcp.rs +8 -2
- package/dist/create/server-source/crates/feltdb/src/phase_2a_failures.rs +59 -15
- package/dist/create/server-source/crates/feltdb/src/phase_2b_network.rs +70 -17
- package/dist/create/server-source/crates/feltdb/src/phase_2c_cascading.rs +23 -6
- package/dist/create/server-source/crates/feltdb/src/phase_3_durability.rs +12 -3
- package/dist/create/server-source/crates/feltdb/src/phase_4_baseline.rs +41 -11
- package/dist/create/server-source/crates/feltdb/src/phase_5_soak.rs +56 -25
- package/dist/create/server-source/crates/feltdb/src/policy_evaluation.rs +701 -245
- package/dist/create/server-source/crates/feltdb/src/production_api.rs +31 -13
- package/dist/create/server-source/crates/feltdb/src/query_execution_diagnostics.rs +126 -0
- package/dist/create/server-source/crates/feltdb/src/query_performance.rs +6 -8
- package/dist/create/server-source/crates/feltdb/src/replay_fuzzing.rs +5 -5
- package/dist/create/server-source/crates/feltdb/src/replica_acknowledgements.rs +48 -18
- package/dist/create/server-source/crates/feltdb/src/replica_membership.rs +30 -11
- package/dist/create/server-source/crates/feltdb/src/replication_manager.rs +6 -3
- package/dist/create/server-source/crates/feltdb/src/replication_protocol.rs +4 -3
- package/dist/create/server-source/crates/feltdb/src/sharding.rs +36 -10
- package/dist/create/server-source/crates/feltdb/src/state_conflict_contract.rs +516 -0
- package/dist/create/server-source/crates/feltdb/src/state_contract.rs +305 -6
- package/dist/create/server-source/crates/feltdb/src/state_diff_contract.rs +222 -0
- package/dist/create/server-source/crates/feltdb/src/state_facade.rs +82 -54
- package/dist/create/server-source/crates/feltdb/src/state_hash.rs +2 -2
- package/dist/create/server-source/crates/feltdb/src/state_model.rs +1514 -537
- package/dist/create/server-source/crates/feltdb/src/state_transition_store.rs +6 -3
- package/dist/create/server-source/crates/feltdb/src/state_trigger.rs +672 -0
- package/dist/create/server-source/crates/feltdb/src/submission.rs +5 -11
- package/dist/create/server-source/crates/feltdb/src/sync.rs +12 -0
- package/dist/create/server-source/crates/feltdb/src/tcp_transport.rs +6 -8
- package/dist/create/server-source/crates/feltdb/src/transaction_api.rs +24 -35
- package/dist/create/server-source/crates/feltdb/src/transaction_invariants.rs +24 -8
- package/dist/create/server-source/crates/feltdb/src/transaction_preconditions.rs +248 -59
- package/dist/create/server-source/crates/feltdb/src/transactions.rs +17 -20
- package/dist/create/server-source/crates/feltdb/src/trigger_contract.rs +749 -0
- package/dist/create/server-source/crates/feltdb/src/worker_mesh.rs +1 -0
- package/dist/create/server-source/crates/feltdb/src/workload.rs +512 -4
- package/dist/create/server-source/crates/feltdb/src/workload_diagnostics.rs +442 -0
- package/dist/create/server-source/crates/feltdb/tests/branching_evidence.rs +299 -0
- package/dist/create/server-source/crates/feltdb/tests/current_revision_authority_evidence.rs +288 -0
- package/dist/create/server-source/crates/feltdb/tests/durable_format_compatibility.rs +392 -0
- package/dist/create/server-source/crates/feltdb/tests/feltdb_state_boundary_tests.rs +436 -220
- package/dist/create/server-source/crates/feltdb/tests/fixtures/state_conflict_contract_corpus.json +1916 -0
- package/dist/create/server-source/crates/feltdb/tests/fixtures/state_diff_contract_corpus.json +1878 -0
- package/dist/create/server-source/crates/feltdb/tests/fixtures/trigger_contract_corpus.json +1862 -0
- package/dist/create/server-source/crates/feltdb/tests/pr34_query_collection.rs +234 -0
- package/dist/create/server-source/crates/feltdb/tests/pr35_equality_index.rs +947 -0
- package/dist/create/server-source/crates/feltdb/tests/pr7_self_authorization_proof.rs +5 -8
- package/dist/create/server-source/crates/feltdb/tests/pr8_vocabulary_assessment.rs +52 -44
- package/dist/create/server-source/crates/feltdb/tests/pr9_phase2_boundary_tests.rs +33 -16
- package/dist/create/server-source/crates/feltdb/tests/pr9_phase3a_path_a_tests.rs +22 -7
- package/dist/create/server-source/crates/feltdb/tests/pr9_phase3c_authorized_mutations.rs +41 -22
- package/dist/create/server-source/crates/feltdb/tests/pr9_phase3c_role_based_authorization.rs +25 -8
- package/dist/create/server-source/crates/feltdb/tests/pr9_phase3c_simple_auth_delete.rs +9 -6
- package/dist/create/server-source/crates/feltdb/tests/pr9_phase3c_team_delete_role_authorization.rs +120 -69
- package/dist/create/server-source/crates/feltdb/tests/pr9_teams_role_based_access.rs +21 -10
- package/dist/create/server-source/crates/feltdb/tests/production_readiness_contract.rs +1032 -0
- package/dist/create/server-source/crates/feltdb/tests/reconciliation_application.rs +868 -0
- package/dist/create/server-source/crates/feltdb/tests/reconciliation_wire_format_evidence.rs +221 -0
- package/dist/create/server-source/crates/feltdb/tests/resource_scoped_revisions.rs +338 -0
- package/dist/create/server-source/crates/feltdb/tests/revision_identity_contract.rs +1039 -0
- package/dist/create/server-source/crates/feltdb/tests/revision_model_decision.rs +739 -0
- package/dist/create/server-source/crates/feltdb/tests/revision_retention_boundary_evidence.rs +427 -0
- package/dist/create/server-source/crates/feltdb/tests/saas_authorization_integration.rs +3 -3
- package/dist/create/server-source/crates/feltdb/tests/saas_invitation_lifecycle.rs +25 -22
- package/dist/create/server-source/crates/feltdb/tests/state_conflict_contract_conformance.rs +1799 -0
- package/dist/create/server-source/crates/feltdb/tests/state_diff_contract_conformance.rs +1316 -0
- package/dist/create/server-source/crates/feltdb/tests/state_model_integration.rs +53 -61
- package/dist/create/server-source/crates/feltdb/tests/state_persistence_integration.rs +156 -61
- package/dist/create/server-source/crates/feltdb/tests/state_store_boundary_evidence.rs +299 -0
- package/dist/create/server-source/crates/feltdb/tests/sync_divergence_evidence.rs +255 -0
- package/dist/create/server-source/crates/feltdb/tests/three_way_input_boundary_evidence.rs +249 -0
- package/dist/create/server-source/crates/feltdb/tests/trigger_contract_conformance.rs +994 -0
- package/dist/create/server-source/crates/feltdb-server/src/app_state.rs +2 -1
- package/dist/create/server-source/crates/feltdb-server/src/audit.rs +1137 -29
- package/dist/create/server-source/crates/feltdb-server/src/auth.rs +164 -13
- package/dist/create/server-source/crates/feltdb-server/src/main.rs +800 -34
- package/dist/db.d.ts +33 -34
- package/dist/db.d.ts.map +1 -1
- package/dist/db.js +74 -20
- package/dist/deployment.d.ts +30 -0
- package/dist/deployment.d.ts.map +1 -0
- package/dist/deployment.js +130 -0
- package/dist/embedded-transaction.d.ts +22 -4
- package/dist/embedded-transaction.d.ts.map +1 -1
- package/dist/embedded-transaction.js +51 -5
- package/dist/feltdb.d.ts +14 -2
- package/dist/feltdb.d.ts.map +1 -1
- package/dist/file-db.js +1 -1
- package/dist/http-client.d.ts +14 -0
- package/dist/http-client.d.ts.map +1 -1
- package/dist/http-client.js +23 -5
- package/dist/http-db.d.ts +119 -1
- package/dist/http-db.d.ts.map +1 -1
- package/dist/http-db.js +346 -31
- package/dist/index-core.d.ts +2 -0
- package/dist/index-core.d.ts.map +1 -1
- package/dist/index-core.js +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -0
- package/dist/indexeddb-db.d.ts.map +1 -1
- package/dist/indexeddb-db.js +35 -21
- package/dist/managed-recovery.d.ts +192 -0
- package/dist/managed-recovery.d.ts.map +1 -0
- package/dist/managed-recovery.js +242 -0
- package/dist/memory-db.js +1 -1
- package/dist/studio-app/assets/{feltdb_wasm-DB8cX151.js → feltdb_wasm-DaNwCLRX.js} +1 -1
- package/dist/studio-app/assets/feltdb_wasm_bg-DnsHNv6g.wasm +0 -0
- package/dist/studio-app/assets/index-j8IlhNqJ.js +29 -0
- package/dist/studio-app/index.html +1 -1
- package/dist/transaction.d.ts +30 -0
- package/dist/transaction.d.ts.map +1 -1
- package/dist/transaction.js +41 -0
- package/dist/wasm/feltdb_wasm_bg.wasm +0 -0
- package/dist/workload.d.ts +2 -1
- package/dist/workload.d.ts.map +1 -1
- package/package.json +1 -1
- package/dist/studio-app/assets/feltdb_wasm_bg-ClhDHp0S.wasm +0 -0
- package/dist/studio-app/assets/index-B0k4UAlI.js +0 -29
|
@@ -14,8 +14,8 @@
|
|
|
14
14
|
#[cfg(test)]
|
|
15
15
|
mod integration_smoke_test {
|
|
16
16
|
use feltdb::{
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
ConflictClass, ConflictClassification, ReconciliationPlan, Relationship, SemanticDiff,
|
|
18
|
+
StateRevision, StateStore, StateTopology,
|
|
19
19
|
};
|
|
20
20
|
use serde_json::json;
|
|
21
21
|
|
|
@@ -25,13 +25,14 @@ mod integration_smoke_test {
|
|
|
25
25
|
let store = StateStore::new_volatile();
|
|
26
26
|
let initial_state = store
|
|
27
27
|
.create(
|
|
28
|
+
"docs:1",
|
|
28
29
|
r#"{"users":{"alice":{"balance":100},"bob":{"balance":50}}}"#.to_string(),
|
|
29
30
|
"initial-authority".to_string(),
|
|
30
31
|
)
|
|
31
32
|
.expect("Failed to create initial state");
|
|
32
33
|
|
|
33
34
|
assert!(store.exists(&initial_state.id));
|
|
34
|
-
assert_eq!(store.
|
|
35
|
+
assert_eq!(store.get(&initial_state.id).unwrap().id, initial_state.id);
|
|
35
36
|
|
|
36
37
|
println!("✓ Step 1: Created initial state ({})", initial_state.id);
|
|
37
38
|
|
|
@@ -44,13 +45,10 @@ mod integration_smoke_test {
|
|
|
44
45
|
)
|
|
45
46
|
.expect("Failed to create alice branch");
|
|
46
47
|
|
|
47
|
-
store
|
|
48
|
-
.create_branch("alice-branch".to_string(), alice_branch.id.clone())
|
|
49
|
-
.expect("Failed to create alice-branch");
|
|
50
|
-
|
|
51
48
|
println!(
|
|
52
49
|
"✓ Step 2: Created alice branch ({}), parent: {}",
|
|
53
|
-
alice_branch.id,
|
|
50
|
+
alice_branch.id,
|
|
51
|
+
alice_branch.parent().unwrap()
|
|
54
52
|
);
|
|
55
53
|
|
|
56
54
|
// Step 3: Create second branch from initial state (Bob's branch - modifies bob balance)
|
|
@@ -62,13 +60,10 @@ mod integration_smoke_test {
|
|
|
62
60
|
)
|
|
63
61
|
.expect("Failed to create bob branch");
|
|
64
62
|
|
|
65
|
-
store
|
|
66
|
-
.create_branch("bob-branch".to_string(), bob_branch.id.clone())
|
|
67
|
-
.expect("Failed to create bob-branch");
|
|
68
|
-
|
|
69
63
|
println!(
|
|
70
64
|
"✓ Step 3: Created bob branch ({}), parent: {}",
|
|
71
|
-
bob_branch.id,
|
|
65
|
+
bob_branch.id,
|
|
66
|
+
bob_branch.parent().unwrap()
|
|
72
67
|
);
|
|
73
68
|
|
|
74
69
|
// Step 4: Inspect topology
|
|
@@ -96,18 +91,15 @@ mod integration_smoke_test {
|
|
|
96
91
|
);
|
|
97
92
|
|
|
98
93
|
// Step 5: Compute semantic diffs
|
|
99
|
-
let initial_json =
|
|
100
|
-
r#"{"users":{"alice":{"balance":100},"bob":{"balance":50}}}"
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
r#"{"users":{"alice":{"balance":100},"bob":{"balance":150}}}"#,
|
|
109
|
-
)
|
|
110
|
-
.unwrap();
|
|
94
|
+
let initial_json =
|
|
95
|
+
serde_json::from_str(r#"{"users":{"alice":{"balance":100},"bob":{"balance":50}}}"#)
|
|
96
|
+
.unwrap();
|
|
97
|
+
let alice_json =
|
|
98
|
+
serde_json::from_str(r#"{"users":{"alice":{"balance":150},"bob":{"balance":50}}}"#)
|
|
99
|
+
.unwrap();
|
|
100
|
+
let bob_json =
|
|
101
|
+
serde_json::from_str(r#"{"users":{"alice":{"balance":100},"bob":{"balance":150}}}"#)
|
|
102
|
+
.unwrap();
|
|
111
103
|
|
|
112
104
|
let alice_diff = SemanticDiff::compute(&initial_json, &alice_json);
|
|
113
105
|
let bob_diff = SemanticDiff::compute(&initial_json, &bob_json);
|
|
@@ -120,18 +112,15 @@ mod integration_smoke_test {
|
|
|
120
112
|
println!(" - Bob's change: {:?}", bob_diff.changes[0]);
|
|
121
113
|
|
|
122
114
|
// Step 6: Classify conflicts
|
|
123
|
-
let conflict_class =
|
|
124
|
-
&initial_state,
|
|
125
|
-
&alice_branch,
|
|
126
|
-
&bob_branch,
|
|
127
|
-
);
|
|
115
|
+
let conflict_class =
|
|
116
|
+
ConflictClassification::classify(&initial_state, &alice_branch, &bob_branch);
|
|
128
117
|
|
|
129
118
|
// These changes are independent (different paths)
|
|
130
119
|
assert_eq!(conflict_class.overall, ConflictClass::Independent);
|
|
131
|
-
assert!(
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
120
|
+
assert!(conflict_class
|
|
121
|
+
.path_conflicts
|
|
122
|
+
.iter()
|
|
123
|
+
.all(|c| c.classification == ConflictClass::Independent));
|
|
135
124
|
|
|
136
125
|
println!("✓ Step 6: Classified conflicts as INDEPENDENT");
|
|
137
126
|
|
|
@@ -172,7 +161,10 @@ mod integration_smoke_test {
|
|
|
172
161
|
)
|
|
173
162
|
.expect("Failed to commit reconciled state");
|
|
174
163
|
|
|
175
|
-
println!(
|
|
164
|
+
println!(
|
|
165
|
+
"✓ Step 8: Materialized reconciled state ({})",
|
|
166
|
+
final_state.id
|
|
167
|
+
);
|
|
176
168
|
|
|
177
169
|
// Step 9: Verify state properties
|
|
178
170
|
let retrieved = store
|
|
@@ -207,6 +199,7 @@ mod integration_smoke_test {
|
|
|
207
199
|
let store = StateStore::new_volatile();
|
|
208
200
|
let initial = store
|
|
209
201
|
.create(
|
|
202
|
+
"docs:1",
|
|
210
203
|
r#"{"status":"initial"}"#.to_string(),
|
|
211
204
|
"auth".to_string(),
|
|
212
205
|
)
|
|
@@ -232,6 +225,7 @@ mod integration_smoke_test {
|
|
|
232
225
|
let store = StateStore::new_volatile();
|
|
233
226
|
let initial = store
|
|
234
227
|
.create(
|
|
228
|
+
"docs:1",
|
|
235
229
|
r#"{"value":"initial"}"#.to_string(),
|
|
236
230
|
"auth".to_string(),
|
|
237
231
|
)
|
|
@@ -262,12 +256,12 @@ mod integration_smoke_test {
|
|
|
262
256
|
// Test that unrelated branches are detected as such
|
|
263
257
|
let store1 = StateStore::new_volatile();
|
|
264
258
|
let state1 = store1
|
|
265
|
-
.create(r#"{"id":1}"#.to_string(), "auth1".to_string())
|
|
259
|
+
.create("docs:1", r#"{"id":1}"#.to_string(), "auth1".to_string())
|
|
266
260
|
.unwrap();
|
|
267
261
|
|
|
268
262
|
let store2 = StateStore::new_volatile();
|
|
269
263
|
let state2 = store2
|
|
270
|
-
.create(r#"{"id":2}"#.to_string(), "auth2".to_string())
|
|
264
|
+
.create("docs:1", r#"{"id":2}"#.to_string(), "auth2".to_string())
|
|
271
265
|
.unwrap();
|
|
272
266
|
|
|
273
267
|
let mut topology = StateTopology::new();
|
|
@@ -294,36 +288,34 @@ mod integration_smoke_test {
|
|
|
294
288
|
let diff2 = SemanticDiff::compute(&old, &new_string);
|
|
295
289
|
|
|
296
290
|
assert_eq!(diff1.changes.len(), 0, "No change for same representation");
|
|
297
|
-
assert_eq!(
|
|
291
|
+
assert_eq!(
|
|
292
|
+
diff2.changes.len(),
|
|
293
|
+
1,
|
|
294
|
+
"Change detected for different representation"
|
|
295
|
+
);
|
|
298
296
|
|
|
299
297
|
println!("✓ Representation sensitivity verified in diffs");
|
|
300
298
|
}
|
|
301
299
|
|
|
302
300
|
#[test]
|
|
303
|
-
fn
|
|
304
|
-
//
|
|
301
|
+
fn test_committing_a_child_does_not_disturb_its_parent() {
|
|
302
|
+
// The store keeps no pointer that a commit could advance, so a parent
|
|
303
|
+
// is exactly as retrievable after its child exists as before.
|
|
305
304
|
let store = StateStore::new_volatile();
|
|
306
305
|
let state1 = store
|
|
307
|
-
.create(r#"{"v":1}"#.to_string(), "auth".to_string())
|
|
306
|
+
.create("docs:1", r#"{"v":1}"#.to_string(), "auth".to_string())
|
|
308
307
|
.unwrap();
|
|
308
|
+
assert_eq!(store.get(&state1.id).unwrap().content, r#"{"v":1}"#);
|
|
309
309
|
|
|
310
|
-
let current_after_create = store.current().unwrap();
|
|
311
|
-
assert_eq!(current_after_create.id, state1.id);
|
|
312
|
-
|
|
313
|
-
// Branch from state1
|
|
314
310
|
let state2 = store
|
|
315
311
|
.commit(r#"{"v":2}"#.to_string(), &state1, "auth".to_string())
|
|
316
312
|
.unwrap();
|
|
317
313
|
|
|
318
|
-
store
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
// Current should still be state2 (because we committed it)
|
|
323
|
-
let current_after_branch = store.current().unwrap();
|
|
324
|
-
assert_eq!(current_after_branch.id, state2.id);
|
|
314
|
+
assert_eq!(store.get(&state1.id).unwrap().content, r#"{"v":1}"#);
|
|
315
|
+
assert_eq!(store.get(&state2.id).unwrap().content, r#"{"v":2}"#);
|
|
316
|
+
assert_eq!(store.parent(&state2.id).unwrap().id, state1.id);
|
|
325
317
|
|
|
326
|
-
println!("✓
|
|
318
|
+
println!("✓ A commit adds a revision and displaces nothing");
|
|
327
319
|
}
|
|
328
320
|
|
|
329
321
|
#[test]
|
|
@@ -331,11 +323,15 @@ mod integration_smoke_test {
|
|
|
331
323
|
// Verify that reconciliation requires explicit plan
|
|
332
324
|
let store = StateStore::new_volatile();
|
|
333
325
|
let initial = store
|
|
334
|
-
.create(r#"{"a":1,"b":2}"#.to_string(), "auth".to_string())
|
|
326
|
+
.create("docs:1", r#"{"a":1,"b":2}"#.to_string(), "auth".to_string())
|
|
335
327
|
.unwrap();
|
|
336
328
|
|
|
337
329
|
let left = store
|
|
338
|
-
.commit(
|
|
330
|
+
.commit(
|
|
331
|
+
r#"{"a":10,"b":2}"#.to_string(),
|
|
332
|
+
&initial,
|
|
333
|
+
"auth".to_string(),
|
|
334
|
+
)
|
|
339
335
|
.unwrap();
|
|
340
336
|
|
|
341
337
|
let right = StateRevision::child(
|
|
@@ -348,12 +344,8 @@ mod integration_smoke_test {
|
|
|
348
344
|
let classification = ConflictClassification::classify(&initial, &left, &right);
|
|
349
345
|
|
|
350
346
|
// Caller must supply explicit plan
|
|
351
|
-
let plan =
|
|
352
|
-
left.id.clone(),
|
|
353
|
-
right.id.clone(),
|
|
354
|
-
initial.id.clone(),
|
|
355
|
-
true,
|
|
356
|
-
);
|
|
347
|
+
let plan =
|
|
348
|
+
ReconciliationPlan::new(left.id.clone(), right.id.clone(), initial.id.clone(), true);
|
|
357
349
|
|
|
358
350
|
assert!(plan.validate());
|
|
359
351
|
assert!(
|
|
@@ -22,8 +22,12 @@ mod state_persistence_tests {
|
|
|
22
22
|
// Create initial state with FeltDB persistence
|
|
23
23
|
let db1 = feltdb::FeltDb::open(&temp_path).expect("open db");
|
|
24
24
|
let store1 = StateStore::with_feltdb(Arc::new(db1)).expect("create store");
|
|
25
|
-
let rev1 = store1
|
|
26
|
-
|
|
25
|
+
let rev1 = store1
|
|
26
|
+
.create("docs:1", r#"{"v":1}"#.to_string(), "auth".to_string())
|
|
27
|
+
.expect("create");
|
|
28
|
+
let rev2 = store1
|
|
29
|
+
.commit(r#"{"v":2}"#.to_string(), &rev1, "auth".to_string())
|
|
30
|
+
.expect("commit");
|
|
27
31
|
|
|
28
32
|
let id1 = rev1.id.clone();
|
|
29
33
|
let id2 = rev2.id.clone();
|
|
@@ -32,7 +36,10 @@ mod state_persistence_tests {
|
|
|
32
36
|
// Verify both revisions exist
|
|
33
37
|
assert!(store1.exists(&id1), "rev1 should exist");
|
|
34
38
|
assert!(store1.exists(&id2), "rev2 should exist");
|
|
35
|
-
assert_eq!(
|
|
39
|
+
assert_eq!(
|
|
40
|
+
store1.get(&id2).map(|r| r.id.as_hex().to_string()),
|
|
41
|
+
Some(id2_hex.clone())
|
|
42
|
+
);
|
|
36
43
|
|
|
37
44
|
// Drop store and database (simulate restart)
|
|
38
45
|
drop(store1);
|
|
@@ -44,7 +51,11 @@ mod state_persistence_tests {
|
|
|
44
51
|
// Verify all revisions still exist
|
|
45
52
|
assert!(store2.exists(&id1), "rev1 should survive restart");
|
|
46
53
|
assert!(store2.exists(&id2), "rev2 should survive restart");
|
|
47
|
-
assert_eq!(
|
|
54
|
+
assert_eq!(
|
|
55
|
+
store2.get(&id2).map(|r| r.id.as_hex().to_string()),
|
|
56
|
+
Some(id2_hex),
|
|
57
|
+
"the revision is retrievable by identity after restart"
|
|
58
|
+
);
|
|
48
59
|
|
|
49
60
|
// Verify content is correct
|
|
50
61
|
let recovered_rev2 = store2.get(&id2).expect("get rev2");
|
|
@@ -59,7 +70,13 @@ mod state_persistence_tests {
|
|
|
59
70
|
|
|
60
71
|
let db1 = feltdb::FeltDb::open(&temp_path).expect("open db");
|
|
61
72
|
let store1 = StateStore::with_feltdb(Arc::new(db1)).expect("create store");
|
|
62
|
-
let rev1 = store1
|
|
73
|
+
let rev1 = store1
|
|
74
|
+
.create(
|
|
75
|
+
"docs:1",
|
|
76
|
+
r#"{"fixed":"content"}"#.to_string(),
|
|
77
|
+
"auth".to_string(),
|
|
78
|
+
)
|
|
79
|
+
.expect("create");
|
|
63
80
|
let id1 = rev1.id.clone();
|
|
64
81
|
drop(store1);
|
|
65
82
|
|
|
@@ -67,9 +84,13 @@ mod state_persistence_tests {
|
|
|
67
84
|
let db2 = feltdb::FeltDb::open(&temp_path).expect("open db again");
|
|
68
85
|
let store2 = StateStore::with_feltdb(Arc::new(db2)).expect("recovery");
|
|
69
86
|
let recovered = store2.get(&id1).expect("get");
|
|
70
|
-
|
|
87
|
+
|
|
71
88
|
// Content hash must be the same for identical content
|
|
72
|
-
assert_eq!(
|
|
89
|
+
assert_eq!(
|
|
90
|
+
recovered.id.as_hex(),
|
|
91
|
+
id1.as_hex(),
|
|
92
|
+
"StateId should be deterministic"
|
|
93
|
+
);
|
|
73
94
|
}
|
|
74
95
|
|
|
75
96
|
#[test]
|
|
@@ -80,22 +101,28 @@ mod state_persistence_tests {
|
|
|
80
101
|
// Create store and initial state
|
|
81
102
|
let db1 = feltdb::FeltDb::open(&temp_path).expect("open db");
|
|
82
103
|
let store1 = StateStore::with_feltdb(Arc::new(db1)).expect("create store");
|
|
83
|
-
let rev1 = store1
|
|
84
|
-
|
|
104
|
+
let rev1 = store1
|
|
105
|
+
.create("docs:1", r#"{"v":1}"#.to_string(), "auth".to_string())
|
|
106
|
+
.expect("create");
|
|
107
|
+
|
|
85
108
|
let id1 = rev1.id.clone();
|
|
86
109
|
let id1_hex = id1.as_hex().to_string();
|
|
87
|
-
|
|
88
|
-
//
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
110
|
+
|
|
111
|
+
// There are no named branches. A revision is addressed by its own
|
|
112
|
+
// content-derived identity, which is what a name would have pointed at.
|
|
113
|
+
assert_eq!(
|
|
114
|
+
store1.get(&id1).map(|id| id.id.as_hex().to_string()),
|
|
115
|
+
Some(id1_hex.clone())
|
|
116
|
+
);
|
|
93
117
|
drop(store1);
|
|
94
118
|
|
|
95
|
-
// Recover and check branch
|
|
96
119
|
let db2 = feltdb::FeltDb::open(&temp_path).expect("open db again");
|
|
97
|
-
let store2 = StateStore::with_feltdb(Arc::new(db2)).expect("
|
|
98
|
-
assert_eq!(
|
|
120
|
+
let store2 = StateStore::with_feltdb(Arc::new(db2)).expect("reopen");
|
|
121
|
+
assert_eq!(
|
|
122
|
+
store2.get(&id1).map(|id| id.id.as_hex().to_string()),
|
|
123
|
+
Some(id1_hex),
|
|
124
|
+
"the revision survives restart under its own identity"
|
|
125
|
+
);
|
|
99
126
|
}
|
|
100
127
|
|
|
101
128
|
#[test]
|
|
@@ -105,14 +132,22 @@ mod state_persistence_tests {
|
|
|
105
132
|
|
|
106
133
|
// Create volatile store
|
|
107
134
|
let store1 = StateStore::new_volatile();
|
|
108
|
-
let rev1 = store1
|
|
135
|
+
let rev1 = store1
|
|
136
|
+
.create("docs:1", r#"{"v":1}"#.to_string(), "auth".to_string())
|
|
137
|
+
.expect("create");
|
|
109
138
|
drop(store1);
|
|
110
139
|
|
|
111
140
|
// Try to recover from disk (should be empty)
|
|
112
141
|
let db2 = feltdb::FeltDb::open(&temp_path).expect("open db");
|
|
113
142
|
let store2 = StateStore::with_feltdb(Arc::new(db2)).expect("create store");
|
|
114
|
-
assert!(
|
|
115
|
-
|
|
143
|
+
assert!(
|
|
144
|
+
!store2.exists(&rev1.id),
|
|
145
|
+
"volatile store should not persist"
|
|
146
|
+
);
|
|
147
|
+
assert!(
|
|
148
|
+
store2.get(&rev1.id).is_none(),
|
|
149
|
+
"nothing from the volatile store reached disk"
|
|
150
|
+
);
|
|
116
151
|
}
|
|
117
152
|
|
|
118
153
|
#[test]
|
|
@@ -123,10 +158,16 @@ mod state_persistence_tests {
|
|
|
123
158
|
// Create chain of revisions
|
|
124
159
|
let db1 = feltdb::FeltDb::open(&temp_path).expect("open db");
|
|
125
160
|
let store1 = StateStore::with_feltdb(Arc::new(db1)).expect("create store");
|
|
126
|
-
let rev1 = store1
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
161
|
+
let rev1 = store1
|
|
162
|
+
.create("docs:1", r#"{"v":1}"#.to_string(), "auth".to_string())
|
|
163
|
+
.expect("create");
|
|
164
|
+
let rev2 = store1
|
|
165
|
+
.commit(r#"{"v":2}"#.to_string(), &rev1, "auth".to_string())
|
|
166
|
+
.expect("commit");
|
|
167
|
+
let rev3 = store1
|
|
168
|
+
.commit(r#"{"v":3}"#.to_string(), &rev2, "auth".to_string())
|
|
169
|
+
.expect("commit");
|
|
170
|
+
|
|
130
171
|
let id1 = rev1.id.clone();
|
|
131
172
|
let id2 = rev2.id.clone();
|
|
132
173
|
let id3 = rev3.id.clone();
|
|
@@ -135,15 +176,26 @@ mod state_persistence_tests {
|
|
|
135
176
|
// Recover and verify parent pointers
|
|
136
177
|
let db2 = feltdb::FeltDb::open(&temp_path).expect("open db again");
|
|
137
178
|
let store2 = StateStore::with_feltdb(Arc::new(db2)).expect("recovery");
|
|
138
|
-
|
|
179
|
+
|
|
139
180
|
let recovered_rev3 = store2.get(&id3).expect("get rev3");
|
|
140
|
-
assert_eq!(
|
|
141
|
-
|
|
181
|
+
assert_eq!(
|
|
182
|
+
recovered_rev3.parent_id.as_ref().map(|id| id.as_hex()),
|
|
183
|
+
Some(id2.as_hex()),
|
|
184
|
+
"rev3 parent should be rev2"
|
|
185
|
+
);
|
|
186
|
+
|
|
142
187
|
let recovered_rev2 = store2.get(&id2).expect("get rev2");
|
|
143
|
-
assert_eq!(
|
|
144
|
-
|
|
188
|
+
assert_eq!(
|
|
189
|
+
recovered_rev2.parent_id.as_ref().map(|id| id.as_hex()),
|
|
190
|
+
Some(id1.as_hex()),
|
|
191
|
+
"rev2 parent should be rev1"
|
|
192
|
+
);
|
|
193
|
+
|
|
145
194
|
let recovered_rev1 = store2.get(&id1).expect("get rev1");
|
|
146
|
-
assert!(
|
|
195
|
+
assert!(
|
|
196
|
+
recovered_rev1.parent_id.is_none(),
|
|
197
|
+
"rev1 should have no parent"
|
|
198
|
+
);
|
|
147
199
|
}
|
|
148
200
|
|
|
149
201
|
#[test]
|
|
@@ -153,20 +205,30 @@ mod state_persistence_tests {
|
|
|
153
205
|
|
|
154
206
|
let db = feltdb::FeltDb::open(&temp_path).expect("open db");
|
|
155
207
|
let store = StateStore::with_feltdb(Arc::new(db)).expect("create store");
|
|
156
|
-
|
|
208
|
+
|
|
157
209
|
// Commit sequence
|
|
158
|
-
let rev1 = store
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
let
|
|
162
|
-
|
|
163
|
-
|
|
210
|
+
let rev1 = store
|
|
211
|
+
.create("docs:1", r#"{"step":1}"#.to_string(), "app".to_string())
|
|
212
|
+
.expect("1");
|
|
213
|
+
let rev2 = store
|
|
214
|
+
.commit(r#"{"step":2}"#.to_string(), &rev1, "app".to_string())
|
|
215
|
+
.expect("2");
|
|
216
|
+
let rev3 = store
|
|
217
|
+
.commit(r#"{"step":3}"#.to_string(), &rev2, "app".to_string())
|
|
218
|
+
.expect("3");
|
|
219
|
+
let rev4 = store
|
|
220
|
+
.commit(r#"{"step":4}"#.to_string(), &rev3, "app".to_string())
|
|
221
|
+
.expect("4");
|
|
222
|
+
let rev5 = store
|
|
223
|
+
.commit(r#"{"step":5}"#.to_string(), &rev4, "app".to_string())
|
|
224
|
+
.expect("5");
|
|
225
|
+
|
|
164
226
|
let id1 = rev1.id.clone();
|
|
165
227
|
let id2 = rev2.id.clone();
|
|
166
228
|
let id3 = rev3.id.clone();
|
|
167
229
|
let id4 = rev4.id.clone();
|
|
168
230
|
let id5 = rev5.id.clone();
|
|
169
|
-
|
|
231
|
+
|
|
170
232
|
// All should exist
|
|
171
233
|
assert!(store.exists(&id1));
|
|
172
234
|
assert!(store.exists(&id2));
|
|
@@ -174,7 +236,10 @@ mod state_persistence_tests {
|
|
|
174
236
|
assert!(store.exists(&id4));
|
|
175
237
|
assert!(store.exists(&id5));
|
|
176
238
|
let id5_hex = id5.as_hex().to_string();
|
|
177
|
-
assert_eq!(
|
|
239
|
+
assert_eq!(
|
|
240
|
+
store.get(&id5).map(|r| r.id.as_hex().to_string()),
|
|
241
|
+
Some(id5_hex)
|
|
242
|
+
);
|
|
178
243
|
}
|
|
179
244
|
|
|
180
245
|
#[test]
|
|
@@ -185,10 +250,13 @@ mod state_persistence_tests {
|
|
|
185
250
|
// Create and persist
|
|
186
251
|
let db1 = feltdb::FeltDb::open(&temp_path).expect("open db");
|
|
187
252
|
let store1 = StateStore::with_feltdb(Arc::new(db1)).expect("create store");
|
|
188
|
-
let rev1 = store1
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
253
|
+
let rev1 = store1
|
|
254
|
+
.create(
|
|
255
|
+
"docs:1",
|
|
256
|
+
r#"{"complex":{"nested":{"data":[1,2,3],"unicode":"αβγ"}}}"#.to_string(),
|
|
257
|
+
"auth".to_string(),
|
|
258
|
+
)
|
|
259
|
+
.expect("create");
|
|
192
260
|
let id1 = rev1.id.clone();
|
|
193
261
|
drop(store1);
|
|
194
262
|
|
|
@@ -196,7 +264,10 @@ mod state_persistence_tests {
|
|
|
196
264
|
let db2 = feltdb::FeltDb::open(&temp_path).expect("open db again");
|
|
197
265
|
let store2 = StateStore::with_feltdb(Arc::new(db2)).expect("recovery");
|
|
198
266
|
let recovered = store2.get(&id1).expect("get");
|
|
199
|
-
assert_eq!(
|
|
267
|
+
assert_eq!(
|
|
268
|
+
recovered.content,
|
|
269
|
+
r#"{"complex":{"nested":{"data":[1,2,3],"unicode":"αβγ"}}}"#
|
|
270
|
+
);
|
|
200
271
|
}
|
|
201
272
|
|
|
202
273
|
#[test]
|
|
@@ -206,7 +277,9 @@ mod state_persistence_tests {
|
|
|
206
277
|
|
|
207
278
|
let db = feltdb::FeltDb::open(&temp_path).expect("open db");
|
|
208
279
|
let store = StateStore::with_feltdb(Arc::new(db)).expect("create store");
|
|
209
|
-
let rev1 = store
|
|
280
|
+
let rev1 = store
|
|
281
|
+
.create("docs:1", r#"{"v":1}"#.to_string(), "auth".to_string())
|
|
282
|
+
.expect("create");
|
|
210
283
|
let id1 = rev1.id.clone();
|
|
211
284
|
|
|
212
285
|
// Clone the store for concurrent access
|
|
@@ -216,11 +289,19 @@ mod state_persistence_tests {
|
|
|
216
289
|
let id1_hex = id1.as_hex().to_string();
|
|
217
290
|
|
|
218
291
|
// Both can read
|
|
219
|
-
assert_eq!(
|
|
220
|
-
|
|
292
|
+
assert_eq!(
|
|
293
|
+
store_clone1.get(&id1).map(|r| r.id.as_hex().to_string()),
|
|
294
|
+
Some(id1_hex.clone())
|
|
295
|
+
);
|
|
296
|
+
assert_eq!(
|
|
297
|
+
store_clone2.get(&id1).map(|r| r.id.as_hex().to_string()),
|
|
298
|
+
Some(id1_hex)
|
|
299
|
+
);
|
|
221
300
|
|
|
222
301
|
// Commit from one
|
|
223
|
-
let rev2 = store_clone1
|
|
302
|
+
let rev2 = store_clone1
|
|
303
|
+
.commit(r#"{"v":2}"#.to_string(), &rev1, "auth".to_string())
|
|
304
|
+
.expect("commit");
|
|
224
305
|
let id2 = rev2.id.clone();
|
|
225
306
|
|
|
226
307
|
// Both should see the new revision
|
|
@@ -233,38 +314,52 @@ mod state_persistence_tests {
|
|
|
233
314
|
let temp_dir = TempDir::new().unwrap();
|
|
234
315
|
let temp_path = temp_dir.path().join("test.log");
|
|
235
316
|
|
|
317
|
+
// The caller carries the ids it cares about across restarts.
|
|
318
|
+
let rev2_id;
|
|
319
|
+
let rev3_id;
|
|
320
|
+
|
|
236
321
|
// Cycle 1: create initial state
|
|
237
322
|
{
|
|
238
323
|
let db = feltdb::FeltDb::open(&temp_path).expect("open db");
|
|
239
324
|
let store = StateStore::with_feltdb(Arc::new(db)).expect("create store");
|
|
240
|
-
let rev1 = store
|
|
241
|
-
|
|
325
|
+
let rev1 = store
|
|
326
|
+
.create("docs:1", r#"{"cycle":1}"#.to_string(), "auth".to_string())
|
|
327
|
+
.expect("create1");
|
|
328
|
+
rev2_id = store
|
|
329
|
+
.commit(r#"{"cycle":1.5}"#.to_string(), &rev1, "auth".to_string())
|
|
330
|
+
.expect("commit1")
|
|
331
|
+
.id;
|
|
242
332
|
}
|
|
243
333
|
|
|
244
|
-
// Cycle 2:
|
|
334
|
+
// Cycle 2: reopen and add more, starting from the id we carried
|
|
245
335
|
{
|
|
246
336
|
let db = feltdb::FeltDb::open(&temp_path).expect("open db");
|
|
247
337
|
let store = StateStore::with_feltdb(Arc::new(db)).expect("create store");
|
|
248
|
-
let rev2 = store.
|
|
249
|
-
|
|
338
|
+
let rev2 = store.get(&rev2_id).expect("rev2 survives");
|
|
339
|
+
rev3_id = store
|
|
340
|
+
.commit(r#"{"cycle":2}"#.to_string(), &rev2, "auth".to_string())
|
|
341
|
+
.expect("commit2")
|
|
342
|
+
.id;
|
|
250
343
|
}
|
|
251
344
|
|
|
252
345
|
// Cycle 3: verify all history
|
|
253
346
|
{
|
|
254
347
|
let db = feltdb::FeltDb::open(&temp_path).expect("open db");
|
|
255
348
|
let store = StateStore::with_feltdb(Arc::new(db)).expect("create store");
|
|
256
|
-
|
|
257
|
-
|
|
349
|
+
|
|
258
350
|
// Walk the chain backwards - count how many nodes we can reach
|
|
259
|
-
let mut rev = store.
|
|
260
|
-
let mut count = 1;
|
|
351
|
+
let mut rev = store.get(&rev3_id).expect("rev3 survives every cycle");
|
|
352
|
+
let mut count = 1; // Start with 1 for the current node
|
|
261
353
|
while let Some(parent_id) = &rev.parent_id {
|
|
262
354
|
rev = store.get(parent_id).expect("get parent");
|
|
263
355
|
count += 1;
|
|
264
356
|
}
|
|
265
|
-
|
|
266
|
-
assert!(
|
|
357
|
+
|
|
358
|
+
assert!(
|
|
359
|
+
count >= 3,
|
|
360
|
+
"should have chain of at least 3 revisions, got {}",
|
|
361
|
+
count
|
|
362
|
+
);
|
|
267
363
|
}
|
|
268
364
|
}
|
|
269
365
|
}
|
|
270
|
-
|