@lix-js/sdk 0.6.0-preview.2 → 0.6.0-preview.4
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/SKILL.md +46 -8
- package/dist/engine-wasm/wasm/lix_engine.d.ts +25 -1
- package/dist/engine-wasm/wasm/lix_engine.js +60 -2
- package/dist/engine-wasm/wasm/lix_engine.wasm +0 -0
- package/dist/engine-wasm/wasm/lix_engine.wasm.d.ts +5 -0
- package/dist/generated/builtin-schemas.d.ts +87 -162
- package/dist/generated/builtin-schemas.js +139 -236
- package/dist/open-lix.d.ts +10 -3
- package/dist/open-lix.js +39 -0
- package/dist-engine-src/src/binary_cas/types.rs +0 -6
- package/dist-engine-src/src/catalog/context.rs +412 -0
- package/dist-engine-src/src/catalog/mod.rs +10 -0
- package/dist-engine-src/src/catalog/schema.rs +4 -0
- package/dist-engine-src/src/catalog/snapshot.rs +1114 -0
- package/dist-engine-src/src/cel/mod.rs +1 -1
- package/dist-engine-src/src/cel/provider.rs +1 -1
- package/dist-engine-src/src/commit_graph/context.rs +328 -1015
- package/dist-engine-src/src/commit_graph/mod.rs +2 -3
- package/dist-engine-src/src/commit_graph/types.rs +7 -43
- package/dist-engine-src/src/commit_graph/walker.rs +57 -81
- package/dist-engine-src/src/commit_store/codec.rs +887 -0
- package/dist-engine-src/src/commit_store/context.rs +944 -0
- package/dist-engine-src/src/commit_store/materialization.rs +84 -0
- package/dist-engine-src/src/commit_store/mod.rs +16 -0
- package/dist-engine-src/src/commit_store/storage.rs +600 -0
- package/dist-engine-src/src/commit_store/types.rs +215 -0
- package/dist-engine-src/src/common/identity.rs +15 -5
- package/dist-engine-src/src/common/json_pointer.rs +67 -0
- package/dist-engine-src/src/common/metadata.rs +17 -12
- package/dist-engine-src/src/common/mod.rs +5 -5
- package/dist-engine-src/src/domain.rs +324 -0
- package/dist-engine-src/src/engine.rs +29 -43
- package/dist-engine-src/src/entity_identity.rs +238 -118
- package/dist-engine-src/src/functions/context.rs +17 -52
- package/dist-engine-src/src/functions/deterministic.rs +1 -1
- package/dist-engine-src/src/functions/mod.rs +1 -1
- package/dist-engine-src/src/functions/provider.rs +4 -4
- package/dist-engine-src/src/functions/state.rs +39 -66
- package/dist-engine-src/src/functions/types.rs +1 -1
- package/dist-engine-src/src/init.rs +204 -151
- package/dist-engine-src/src/json_store/context.rs +354 -60
- package/dist-engine-src/src/json_store/encoded.rs +6 -6
- package/dist-engine-src/src/json_store/mod.rs +4 -1
- package/dist-engine-src/src/json_store/store.rs +884 -11
- package/dist-engine-src/src/json_store/types.rs +166 -1
- package/dist-engine-src/src/lib.rs +11 -10
- package/dist-engine-src/src/live_state/context.rs +608 -830
- package/dist-engine-src/src/live_state/mod.rs +3 -3
- package/dist-engine-src/src/live_state/overlay.rs +7 -7
- package/dist-engine-src/src/live_state/reader.rs +5 -5
- package/dist-engine-src/src/live_state/types.rs +19 -36
- package/dist-engine-src/src/live_state/visibility.rs +19 -14
- package/dist-engine-src/src/plugin/archive.rs +3 -6
- package/dist-engine-src/src/plugin/install.rs +0 -18
- package/dist-engine-src/src/plugin/plugin_manifest.json +0 -1
- package/dist-engine-src/src/schema/annotations/defaults.rs +2 -7
- package/dist-engine-src/src/schema/builtin/lix_account.json +0 -1
- package/dist-engine-src/src/schema/builtin/lix_active_account.json +0 -1
- package/dist-engine-src/src/schema/builtin/lix_binary_blob_ref.json +0 -1
- package/dist-engine-src/src/schema/builtin/lix_change.json +11 -10
- package/dist-engine-src/src/schema/builtin/lix_change_author.json +0 -1
- package/dist-engine-src/src/schema/builtin/lix_commit.json +8 -46
- package/dist-engine-src/src/schema/builtin/lix_commit_edge.json +29 -22
- package/dist-engine-src/src/schema/builtin/lix_directory_descriptor.json +0 -1
- package/dist-engine-src/src/schema/builtin/lix_file_descriptor.json +0 -1
- package/dist-engine-src/src/schema/builtin/lix_key_value.json +0 -1
- package/dist-engine-src/src/schema/builtin/lix_label.json +10 -3
- package/dist-engine-src/src/schema/builtin/lix_label_assignment.json +74 -0
- package/dist-engine-src/src/schema/builtin/lix_registered_schema.json +2 -8
- package/dist-engine-src/src/schema/builtin/lix_version_descriptor.json +0 -1
- package/dist-engine-src/src/schema/builtin/lix_version_ref.json +0 -1
- package/dist-engine-src/src/schema/builtin/mod.rs +10 -59
- package/dist-engine-src/src/schema/compatibility.rs +787 -0
- package/dist-engine-src/src/schema/definition.json +47 -17
- package/dist-engine-src/src/schema/definition.rs +202 -96
- package/dist-engine-src/src/schema/key.rs +9 -77
- package/dist-engine-src/src/schema/mod.rs +4 -4
- package/dist-engine-src/src/schema/tests.rs +133 -92
- package/dist-engine-src/src/session/context.rs +86 -48
- package/dist-engine-src/src/session/create_version.rs +22 -14
- package/dist-engine-src/src/session/execute.rs +117 -23
- package/dist-engine-src/src/session/merge/apply.rs +4 -4
- package/dist-engine-src/src/session/merge/conflicts.rs +3 -2
- package/dist-engine-src/src/session/merge/stats.rs +1 -1
- package/dist-engine-src/src/session/merge/version.rs +35 -45
- package/dist-engine-src/src/session/mod.rs +9 -7
- package/dist-engine-src/src/session/optimization9_sql2_bench.rs +100 -0
- package/dist-engine-src/src/session/switch_version.rs +17 -28
- package/dist-engine-src/src/session/transaction.rs +76 -0
- package/dist-engine-src/src/sql2/change_provider.rs +14 -20
- package/dist-engine-src/src/sql2/classify.rs +75 -48
- package/dist-engine-src/src/sql2/context.rs +22 -18
- package/dist-engine-src/src/sql2/directory_history_provider.rs +28 -20
- package/dist-engine-src/src/sql2/directory_provider.rs +131 -83
- package/dist-engine-src/src/sql2/entity_history_provider.rs +10 -14
- package/dist-engine-src/src/sql2/entity_provider.rs +680 -169
- package/dist-engine-src/src/sql2/error.rs +24 -5
- package/dist-engine-src/src/sql2/execute.rs +426 -272
- package/dist-engine-src/src/sql2/file_history_provider.rs +29 -21
- package/dist-engine-src/src/sql2/file_provider.rs +533 -108
- package/dist-engine-src/src/sql2/filesystem_planner.rs +58 -94
- package/dist-engine-src/src/sql2/filesystem_visibility.rs +37 -23
- package/dist-engine-src/src/sql2/history_projection.rs +3 -27
- package/dist-engine-src/src/sql2/history_provider.rs +11 -17
- package/dist-engine-src/src/sql2/history_route.rs +22 -8
- package/dist-engine-src/src/sql2/lix_state_provider.rs +178 -96
- package/dist-engine-src/src/sql2/mod.rs +8 -4
- package/dist-engine-src/src/sql2/predicate_typecheck.rs +246 -0
- package/dist-engine-src/src/sql2/public_bind/assignment.rs +46 -0
- package/dist-engine-src/src/sql2/public_bind/capability.rs +41 -0
- package/dist-engine-src/src/sql2/public_bind/dml.rs +172 -0
- package/dist-engine-src/src/sql2/public_bind/mod.rs +26 -0
- package/dist-engine-src/src/sql2/public_bind/table.rs +168 -0
- package/dist-engine-src/src/sql2/read_only.rs +10 -12
- package/dist-engine-src/src/sql2/session.rs +7 -10
- package/dist-engine-src/src/sql2/udfs/lix_timestamp.rs +76 -0
- package/dist-engine-src/src/sql2/udfs/mod.rs +8 -1
- package/dist-engine-src/src/sql2/udfs/public_call.rs +238 -0
- package/dist-engine-src/src/sql2/version_provider.rs +46 -31
- package/dist-engine-src/src/sql2/version_scope.rs +4 -4
- package/dist-engine-src/src/storage_bench.rs +1782 -325
- package/dist-engine-src/src/test_support.rs +183 -36
- package/dist-engine-src/src/tracked_state/by_file_index.rs +20 -24
- package/dist-engine-src/src/tracked_state/codec.rs +1519 -181
- package/dist-engine-src/src/tracked_state/context.rs +1155 -271
- package/dist-engine-src/src/tracked_state/diff.rs +249 -57
- package/dist-engine-src/src/tracked_state/materialization.rs +365 -103
- package/dist-engine-src/src/tracked_state/materializer.rs +488 -0
- package/dist-engine-src/src/tracked_state/merge.rs +37 -19
- package/dist-engine-src/src/tracked_state/mod.rs +8 -7
- package/dist-engine-src/src/tracked_state/storage.rs +138 -6
- package/dist-engine-src/src/tracked_state/tree.rs +695 -252
- package/dist-engine-src/src/tracked_state/types.rs +176 -6
- package/dist-engine-src/src/transaction/commit.rs +695 -435
- package/dist-engine-src/src/transaction/context.rs +551 -310
- package/dist-engine-src/src/transaction/live_state_overlay.rs +9 -8
- package/dist-engine-src/src/transaction/mod.rs +2 -0
- package/dist-engine-src/src/transaction/normalization.rs +311 -447
- package/dist-engine-src/src/transaction/prep.rs +37 -0
- package/dist-engine-src/src/transaction/schema_resolver.rs +93 -71
- package/dist-engine-src/src/transaction/staging.rs +701 -406
- package/dist-engine-src/src/transaction/types.rs +231 -122
- package/dist-engine-src/src/transaction/validation.rs +2717 -1698
- package/dist-engine-src/src/untracked_state/codec.rs +40 -96
- package/dist-engine-src/src/untracked_state/context.rs +21 -5
- package/dist-engine-src/src/untracked_state/materialization.rs +10 -104
- package/dist-engine-src/src/untracked_state/mod.rs +3 -5
- package/dist-engine-src/src/untracked_state/storage.rs +105 -57
- package/dist-engine-src/src/untracked_state/types.rs +63 -13
- package/dist-engine-src/src/version/context.rs +1 -13
- package/dist-engine-src/src/version/lifecycle.rs +221 -0
- package/dist-engine-src/src/version/mod.rs +3 -2
- package/dist-engine-src/src/version/refs.rs +12 -103
- package/dist-engine-src/src/version/stage_rows.rs +15 -19
- package/package.json +1 -1
- package/dist-engine-src/src/changelog/codec.rs +0 -321
- package/dist-engine-src/src/changelog/context.rs +0 -92
- package/dist-engine-src/src/changelog/materialization.rs +0 -121
- package/dist-engine-src/src/changelog/mod.rs +0 -13
- package/dist-engine-src/src/changelog/reader.rs +0 -20
- package/dist-engine-src/src/changelog/storage.rs +0 -220
- package/dist-engine-src/src/changelog/types.rs +0 -38
- package/dist-engine-src/src/schema/builtin/lix_change_set.json +0 -18
- package/dist-engine-src/src/schema/builtin/lix_change_set_element.json +0 -75
- package/dist-engine-src/src/schema/builtin/lix_entity_label.json +0 -63
- package/dist-engine-src/src/schema_registry.rs +0 -294
- package/dist-engine-src/src/sql2/commit_derived_provider.rs +0 -591
- package/dist-engine-src/src/tracked_state/rebuild.rs +0 -771
- package/dist-engine-src/src/tracked_state/tree_types.rs +0 -176
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
use crate::entity_identity::EntityIdentity;
|
|
2
|
-
use crate::tracked_state::
|
|
3
|
-
use crate::tracked_state::{
|
|
2
|
+
use crate::tracked_state::types::TrackedStateTreeScanRequest;
|
|
3
|
+
use crate::tracked_state::{
|
|
4
|
+
MaterializedTrackedStateRow, TrackedStateFilter, TrackedStateStoreReader,
|
|
5
|
+
};
|
|
4
6
|
use crate::LixError;
|
|
5
7
|
|
|
6
8
|
/// Filter for comparing two tracked-state commit roots.
|
|
@@ -24,12 +26,12 @@ pub(crate) struct TrackedStateDiffEntry {
|
|
|
24
26
|
///
|
|
25
27
|
/// This can be a tombstone. Callers that need user-visible semantics
|
|
26
28
|
/// should use `visible_before()` instead of inspecting this directly.
|
|
27
|
-
pub(crate) before: Option<
|
|
29
|
+
pub(crate) before: Option<MaterializedTrackedStateRow>,
|
|
28
30
|
/// Raw row in the right root.
|
|
29
31
|
///
|
|
30
32
|
/// This can be a tombstone. Keeping the raw tombstone is what lets merge
|
|
31
33
|
/// apply deletes without reloading the source root.
|
|
32
|
-
pub(crate) after: Option<
|
|
34
|
+
pub(crate) after: Option<MaterializedTrackedStateRow>,
|
|
33
35
|
}
|
|
34
36
|
|
|
35
37
|
/// Root-local tracked-state identity.
|
|
@@ -61,19 +63,35 @@ where
|
|
|
61
63
|
S: crate::storage::StorageReader,
|
|
62
64
|
{
|
|
63
65
|
let scan_request = scan_request_for_diff(request);
|
|
64
|
-
let
|
|
65
|
-
for tree_entry in reader
|
|
66
|
+
let tree_entries = reader
|
|
66
67
|
.diff_tree_entries_at_commits(left_commit_id, right_commit_id, &scan_request)
|
|
67
|
-
.await
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
};
|
|
68
|
+
.await?;
|
|
69
|
+
let mut before_entries = Vec::new();
|
|
70
|
+
let mut after_entries = Vec::new();
|
|
71
|
+
let mut pending_entries = Vec::with_capacity(tree_entries.len());
|
|
72
|
+
for tree_entry in tree_entries {
|
|
73
|
+
let before_index = tree_entry.before.map(|entry| {
|
|
74
|
+
let index = before_entries.len();
|
|
75
|
+
before_entries.push(entry);
|
|
76
|
+
index
|
|
77
|
+
});
|
|
78
|
+
let after_index = tree_entry.after.map(|entry| {
|
|
79
|
+
let index = after_entries.len();
|
|
80
|
+
after_entries.push(entry);
|
|
81
|
+
index
|
|
82
|
+
});
|
|
83
|
+
pending_entries.push(PendingDiffEntry {
|
|
84
|
+
before_index,
|
|
85
|
+
after_index,
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
let before_rows = reader.materialize_tree_values(before_entries).await?;
|
|
90
|
+
let after_rows = reader.materialize_tree_values(after_entries).await?;
|
|
91
|
+
let mut entries = Vec::new();
|
|
92
|
+
for pending_entry in pending_entries {
|
|
93
|
+
let before = materialized_row_at(pending_entry.before_index, &before_rows)?;
|
|
94
|
+
let after = materialized_row_at(pending_entry.after_index, &after_rows)?;
|
|
77
95
|
let identity = match before.as_ref().or(after.as_ref()) {
|
|
78
96
|
Some(row) => TrackedStateDiffIdentity::from_row(row)?,
|
|
79
97
|
None => continue,
|
|
@@ -87,6 +105,26 @@ where
|
|
|
87
105
|
Ok(TrackedStateDiff { entries })
|
|
88
106
|
}
|
|
89
107
|
|
|
108
|
+
fn materialized_row_at(
|
|
109
|
+
index: Option<usize>,
|
|
110
|
+
rows: &[MaterializedTrackedStateRow],
|
|
111
|
+
) -> Result<Option<MaterializedTrackedStateRow>, LixError> {
|
|
112
|
+
let Some(index) = index else {
|
|
113
|
+
return Ok(None);
|
|
114
|
+
};
|
|
115
|
+
rows.get(index).cloned().map(Some).ok_or_else(|| {
|
|
116
|
+
LixError::new(
|
|
117
|
+
LixError::CODE_INTERNAL_ERROR,
|
|
118
|
+
"tracked_state diff materialization returned fewer rows than planned",
|
|
119
|
+
)
|
|
120
|
+
})
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
struct PendingDiffEntry {
|
|
124
|
+
before_index: Option<usize>,
|
|
125
|
+
after_index: Option<usize>,
|
|
126
|
+
}
|
|
127
|
+
|
|
90
128
|
fn scan_request_for_diff(request: &TrackedStateDiffRequest) -> TrackedStateTreeScanRequest {
|
|
91
129
|
let mut filter = request.filter.clone();
|
|
92
130
|
filter.include_tombstones = true;
|
|
@@ -94,15 +132,15 @@ fn scan_request_for_diff(request: &TrackedStateDiffRequest) -> TrackedStateTreeS
|
|
|
94
132
|
schema_keys: filter.schema_keys,
|
|
95
133
|
entity_ids: filter.entity_ids,
|
|
96
134
|
file_ids: filter.file_ids,
|
|
97
|
-
include_tombstones:
|
|
135
|
+
include_tombstones: true,
|
|
98
136
|
limit: None,
|
|
99
137
|
}
|
|
100
138
|
}
|
|
101
139
|
|
|
102
140
|
fn classify_diff(
|
|
103
141
|
identity: TrackedStateDiffIdentity,
|
|
104
|
-
before: Option<
|
|
105
|
-
after: Option<
|
|
142
|
+
before: Option<MaterializedTrackedStateRow>,
|
|
143
|
+
after: Option<MaterializedTrackedStateRow>,
|
|
106
144
|
) -> Option<TrackedStateDiffEntry> {
|
|
107
145
|
match (is_live_row(before.as_ref()), is_live_row(after.as_ref())) {
|
|
108
146
|
(None, None) => None,
|
|
@@ -128,18 +166,19 @@ fn classify_diff(
|
|
|
128
166
|
}
|
|
129
167
|
}
|
|
130
168
|
|
|
131
|
-
fn is_live_row(row: Option<&
|
|
169
|
+
fn is_live_row(row: Option<&MaterializedTrackedStateRow>) -> Option<&MaterializedTrackedStateRow> {
|
|
132
170
|
row.filter(|row| row.snapshot_content.is_some())
|
|
133
171
|
}
|
|
134
172
|
|
|
135
|
-
fn tracked_row_payload_eq(
|
|
136
|
-
left
|
|
137
|
-
|
|
138
|
-
|
|
173
|
+
fn tracked_row_payload_eq(
|
|
174
|
+
left: &MaterializedTrackedStateRow,
|
|
175
|
+
right: &MaterializedTrackedStateRow,
|
|
176
|
+
) -> bool {
|
|
177
|
+
left.snapshot_content == right.snapshot_content && left.metadata == right.metadata
|
|
139
178
|
}
|
|
140
179
|
|
|
141
180
|
impl TrackedStateDiffIdentity {
|
|
142
|
-
fn from_row(row: &
|
|
181
|
+
fn from_row(row: &MaterializedTrackedStateRow) -> Result<Self, LixError> {
|
|
143
182
|
Ok(Self {
|
|
144
183
|
schema_key: row.schema_key.clone(),
|
|
145
184
|
entity_id: row.entity_id.clone(),
|
|
@@ -160,14 +199,14 @@ impl TrackedStateDiffEntry {
|
|
|
160
199
|
}
|
|
161
200
|
|
|
162
201
|
#[cfg(test)]
|
|
163
|
-
pub(crate) fn visible_before(&self) -> Option<&
|
|
202
|
+
pub(crate) fn visible_before(&self) -> Option<&MaterializedTrackedStateRow> {
|
|
164
203
|
self.before
|
|
165
204
|
.as_ref()
|
|
166
205
|
.filter(|row| row.snapshot_content.is_some())
|
|
167
206
|
}
|
|
168
207
|
|
|
169
208
|
#[cfg(test)]
|
|
170
|
-
pub(crate) fn visible_after(&self) -> Option<&
|
|
209
|
+
pub(crate) fn visible_after(&self) -> Option<&MaterializedTrackedStateRow> {
|
|
171
210
|
self.after
|
|
172
211
|
.as_ref()
|
|
173
212
|
.filter(|row| row.snapshot_content.is_some())
|
|
@@ -180,8 +219,7 @@ mod tests {
|
|
|
180
219
|
|
|
181
220
|
use super::*;
|
|
182
221
|
use crate::backend::testing::UnitTestBackend;
|
|
183
|
-
use crate::
|
|
184
|
-
use crate::storage::{StorageContext, StorageWriteSet, StorageWriteTransaction};
|
|
222
|
+
use crate::storage::{StorageContext, StorageWriteTransaction};
|
|
185
223
|
use crate::tracked_state::TrackedStateContext;
|
|
186
224
|
use crate::NullableKeyFilter;
|
|
187
225
|
|
|
@@ -373,6 +411,133 @@ mod tests {
|
|
|
373
411
|
);
|
|
374
412
|
}
|
|
375
413
|
|
|
414
|
+
#[tokio::test]
|
|
415
|
+
async fn diff_commits_between_delta_parent_and_child_reports_suffix_rows() {
|
|
416
|
+
let backend = Arc::new(UnitTestBackend::new());
|
|
417
|
+
let storage = StorageContext::new(backend.clone());
|
|
418
|
+
let tracked_state = TrackedStateContext::new();
|
|
419
|
+
let mut tx = storage
|
|
420
|
+
.begin_write_transaction()
|
|
421
|
+
.await
|
|
422
|
+
.expect("transaction should open");
|
|
423
|
+
write_root_for_test(
|
|
424
|
+
tx.as_mut(),
|
|
425
|
+
&tracked_state,
|
|
426
|
+
"parent",
|
|
427
|
+
None,
|
|
428
|
+
&[
|
|
429
|
+
row_with_value("entity-a", None, "parent-a", "before"),
|
|
430
|
+
row_with_value("entity-b", None, "parent-b", "same"),
|
|
431
|
+
],
|
|
432
|
+
)
|
|
433
|
+
.await
|
|
434
|
+
.expect("parent should write");
|
|
435
|
+
write_root_for_test(
|
|
436
|
+
tx.as_mut(),
|
|
437
|
+
&tracked_state,
|
|
438
|
+
"child",
|
|
439
|
+
Some("parent"),
|
|
440
|
+
&[row_with_value("entity-a", None, "child-a", "after")],
|
|
441
|
+
)
|
|
442
|
+
.await
|
|
443
|
+
.expect("child should write");
|
|
444
|
+
tx.commit().await.expect("transaction should commit");
|
|
445
|
+
|
|
446
|
+
let diff = tracked_state
|
|
447
|
+
.reader(storage)
|
|
448
|
+
.diff_commits("parent", "child", &TrackedStateDiffRequest::default())
|
|
449
|
+
.await
|
|
450
|
+
.expect("diff should load");
|
|
451
|
+
|
|
452
|
+
assert_eq!(
|
|
453
|
+
kinds(&diff),
|
|
454
|
+
vec![("entity-a".to_string(), TrackedStateDiffKind::Modified)]
|
|
455
|
+
);
|
|
456
|
+
assert_eq!(
|
|
457
|
+
diff.entries[0]
|
|
458
|
+
.before
|
|
459
|
+
.as_ref()
|
|
460
|
+
.and_then(|row| row.snapshot_content.as_deref()),
|
|
461
|
+
Some("{\"value\":\"before\"}")
|
|
462
|
+
);
|
|
463
|
+
assert_eq!(
|
|
464
|
+
diff.entries[0]
|
|
465
|
+
.after
|
|
466
|
+
.as_ref()
|
|
467
|
+
.and_then(|row| row.snapshot_content.as_deref()),
|
|
468
|
+
Some("{\"value\":\"after\"}")
|
|
469
|
+
);
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
#[tokio::test]
|
|
473
|
+
async fn diff_commits_between_delta_child_and_parent_reports_reverse_suffix_rows() {
|
|
474
|
+
let (storage, tracked_state) = seed_parent_child_delta(
|
|
475
|
+
&[
|
|
476
|
+
row_with_value("entity-a", None, "parent-a", "before"),
|
|
477
|
+
row_with_value("entity-b", None, "parent-b", "same"),
|
|
478
|
+
],
|
|
479
|
+
&[row_with_value("entity-a", None, "child-a", "after")],
|
|
480
|
+
)
|
|
481
|
+
.await;
|
|
482
|
+
|
|
483
|
+
let diff = tracked_state
|
|
484
|
+
.reader(storage)
|
|
485
|
+
.diff_commits("child", "parent", &TrackedStateDiffRequest::default())
|
|
486
|
+
.await
|
|
487
|
+
.expect("diff should load");
|
|
488
|
+
|
|
489
|
+
assert_eq!(
|
|
490
|
+
kinds(&diff),
|
|
491
|
+
vec![("entity-a".to_string(), TrackedStateDiffKind::Modified)]
|
|
492
|
+
);
|
|
493
|
+
assert_eq!(
|
|
494
|
+
diff.entries[0]
|
|
495
|
+
.before
|
|
496
|
+
.as_ref()
|
|
497
|
+
.and_then(|row| row.snapshot_content.as_deref()),
|
|
498
|
+
Some("{\"value\":\"after\"}")
|
|
499
|
+
);
|
|
500
|
+
assert_eq!(
|
|
501
|
+
diff.entries[0]
|
|
502
|
+
.after
|
|
503
|
+
.as_ref()
|
|
504
|
+
.and_then(|row| row.snapshot_content.as_deref()),
|
|
505
|
+
Some("{\"value\":\"before\"}")
|
|
506
|
+
);
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
#[tokio::test]
|
|
510
|
+
async fn diff_commits_between_delta_parent_and_child_preserves_suffix_tombstones() {
|
|
511
|
+
let (storage, tracked_state) = seed_parent_child_delta(
|
|
512
|
+
&[
|
|
513
|
+
row_with_value("entity-a", None, "parent-a", "before"),
|
|
514
|
+
row_with_value("entity-b", None, "parent-b", "same"),
|
|
515
|
+
],
|
|
516
|
+
&[tombstone("entity-a", None, "child-delete")],
|
|
517
|
+
)
|
|
518
|
+
.await;
|
|
519
|
+
|
|
520
|
+
let diff = tracked_state
|
|
521
|
+
.reader(storage)
|
|
522
|
+
.diff_commits("parent", "child", &TrackedStateDiffRequest::default())
|
|
523
|
+
.await
|
|
524
|
+
.expect("diff should load");
|
|
525
|
+
|
|
526
|
+
assert_eq!(
|
|
527
|
+
kinds(&diff),
|
|
528
|
+
vec![("entity-a".to_string(), TrackedStateDiffKind::Removed)]
|
|
529
|
+
);
|
|
530
|
+
assert!(diff.entries[0].before_is_live());
|
|
531
|
+
assert!(!diff.entries[0].after_is_live());
|
|
532
|
+
assert_eq!(
|
|
533
|
+
diff.entries[0]
|
|
534
|
+
.after
|
|
535
|
+
.as_ref()
|
|
536
|
+
.map(|row| row.change_id.as_str()),
|
|
537
|
+
Some("child-delete")
|
|
538
|
+
);
|
|
539
|
+
}
|
|
540
|
+
|
|
376
541
|
async fn diff(
|
|
377
542
|
storage: StorageContext,
|
|
378
543
|
tracked_state: &TrackedStateContext,
|
|
@@ -385,8 +550,8 @@ mod tests {
|
|
|
385
550
|
}
|
|
386
551
|
|
|
387
552
|
async fn seed_roots(
|
|
388
|
-
left_rows: &[
|
|
389
|
-
right_rows: &[
|
|
553
|
+
left_rows: &[MaterializedTrackedStateRow],
|
|
554
|
+
right_rows: &[MaterializedTrackedStateRow],
|
|
390
555
|
) -> (StorageContext, TrackedStateContext) {
|
|
391
556
|
let backend = Arc::new(UnitTestBackend::new());
|
|
392
557
|
let storage = StorageContext::new(backend.clone());
|
|
@@ -405,30 +570,48 @@ mod tests {
|
|
|
405
570
|
(storage, tracked_state)
|
|
406
571
|
}
|
|
407
572
|
|
|
573
|
+
async fn seed_parent_child_delta(
|
|
574
|
+
parent_rows: &[MaterializedTrackedStateRow],
|
|
575
|
+
child_rows: &[MaterializedTrackedStateRow],
|
|
576
|
+
) -> (StorageContext, TrackedStateContext) {
|
|
577
|
+
let backend = Arc::new(UnitTestBackend::new());
|
|
578
|
+
let storage = StorageContext::new(backend.clone());
|
|
579
|
+
let tracked_state = TrackedStateContext::new();
|
|
580
|
+
let mut tx = storage
|
|
581
|
+
.begin_write_transaction()
|
|
582
|
+
.await
|
|
583
|
+
.expect("transaction should open");
|
|
584
|
+
write_root_for_test(tx.as_mut(), &tracked_state, "parent", None, parent_rows)
|
|
585
|
+
.await
|
|
586
|
+
.expect("parent should write");
|
|
587
|
+
write_root_for_test(
|
|
588
|
+
tx.as_mut(),
|
|
589
|
+
&tracked_state,
|
|
590
|
+
"child",
|
|
591
|
+
Some("parent"),
|
|
592
|
+
child_rows,
|
|
593
|
+
)
|
|
594
|
+
.await
|
|
595
|
+
.expect("child should write");
|
|
596
|
+
tx.commit().await.expect("transaction should commit");
|
|
597
|
+
(storage, tracked_state)
|
|
598
|
+
}
|
|
599
|
+
|
|
408
600
|
async fn write_root_for_test(
|
|
409
601
|
tx: &mut dyn StorageWriteTransaction,
|
|
410
602
|
tracked_state: &TrackedStateContext,
|
|
411
603
|
commit_id: &str,
|
|
412
604
|
parent_commit_id: Option<&str>,
|
|
413
|
-
rows: &[
|
|
605
|
+
rows: &[MaterializedTrackedStateRow],
|
|
414
606
|
) -> Result<(), LixError> {
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
&mut json_writer,
|
|
424
|
-
commit_id,
|
|
425
|
-
parent_commit_id,
|
|
426
|
-
rows,
|
|
427
|
-
)
|
|
428
|
-
.await?;
|
|
429
|
-
}
|
|
430
|
-
writes.apply(tx).await?;
|
|
431
|
-
Ok(())
|
|
607
|
+
crate::test_support::stage_tracked_root_from_materialized(
|
|
608
|
+
tx,
|
|
609
|
+
tracked_state,
|
|
610
|
+
commit_id,
|
|
611
|
+
parent_commit_id,
|
|
612
|
+
rows,
|
|
613
|
+
)
|
|
614
|
+
.await
|
|
432
615
|
}
|
|
433
616
|
|
|
434
617
|
fn kinds(diff: &TrackedStateDiff) -> Vec<(String, TrackedStateDiffKind)> {
|
|
@@ -436,20 +619,29 @@ mod tests {
|
|
|
436
619
|
.iter()
|
|
437
620
|
.map(|entry| {
|
|
438
621
|
(
|
|
439
|
-
entry
|
|
622
|
+
entry
|
|
623
|
+
.identity
|
|
624
|
+
.entity_id
|
|
625
|
+
.as_single_string_owned()
|
|
626
|
+
.expect("identity"),
|
|
440
627
|
entry.kind,
|
|
441
628
|
)
|
|
442
629
|
})
|
|
443
630
|
.collect()
|
|
444
631
|
}
|
|
445
632
|
|
|
446
|
-
fn tombstone(
|
|
633
|
+
fn tombstone(
|
|
634
|
+
entity_id: &str,
|
|
635
|
+
file_id: Option<&str>,
|
|
636
|
+
change_id: &str,
|
|
637
|
+
) -> MaterializedTrackedStateRow {
|
|
447
638
|
let mut row = row(entity_id, file_id, change_id);
|
|
448
639
|
row.snapshot_content = None;
|
|
640
|
+
row.deleted = true;
|
|
449
641
|
row
|
|
450
642
|
}
|
|
451
643
|
|
|
452
|
-
fn row(entity_id: &str, file_id: Option<&str>, change_id: &str) ->
|
|
644
|
+
fn row(entity_id: &str, file_id: Option<&str>, change_id: &str) -> MaterializedTrackedStateRow {
|
|
453
645
|
row_with_schema(entity_id, file_id, "test_schema", change_id)
|
|
454
646
|
}
|
|
455
647
|
|
|
@@ -458,7 +650,7 @@ mod tests {
|
|
|
458
650
|
file_id: Option<&str>,
|
|
459
651
|
schema_key: &str,
|
|
460
652
|
change_id: &str,
|
|
461
|
-
) ->
|
|
653
|
+
) -> MaterializedTrackedStateRow {
|
|
462
654
|
row_with_schema_and_value(entity_id, file_id, schema_key, change_id, "value")
|
|
463
655
|
}
|
|
464
656
|
|
|
@@ -467,7 +659,7 @@ mod tests {
|
|
|
467
659
|
file_id: Option<&str>,
|
|
468
660
|
change_id: &str,
|
|
469
661
|
value: &str,
|
|
470
|
-
) ->
|
|
662
|
+
) -> MaterializedTrackedStateRow {
|
|
471
663
|
row_with_schema_and_value(entity_id, file_id, "test_schema", change_id, value)
|
|
472
664
|
}
|
|
473
665
|
|
|
@@ -477,14 +669,14 @@ mod tests {
|
|
|
477
669
|
schema_key: &str,
|
|
478
670
|
change_id: &str,
|
|
479
671
|
value: &str,
|
|
480
|
-
) ->
|
|
481
|
-
|
|
672
|
+
) -> MaterializedTrackedStateRow {
|
|
673
|
+
MaterializedTrackedStateRow {
|
|
482
674
|
entity_id: EntityIdentity::single(entity_id),
|
|
483
675
|
schema_key: schema_key.to_string(),
|
|
484
676
|
file_id: file_id.map(str::to_string),
|
|
485
677
|
snapshot_content: Some(format!("{{\"value\":\"{value}\"}}")),
|
|
486
678
|
metadata: None,
|
|
487
|
-
|
|
679
|
+
deleted: false,
|
|
488
680
|
created_at: "2026-01-01T00:00:00Z".to_string(),
|
|
489
681
|
updated_at: "2026-01-01T00:00:00Z".to_string(),
|
|
490
682
|
change_id: change_id.to_string(),
|