@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.
Files changed (169) hide show
  1. package/SKILL.md +46 -8
  2. package/dist/engine-wasm/wasm/lix_engine.d.ts +25 -1
  3. package/dist/engine-wasm/wasm/lix_engine.js +60 -2
  4. package/dist/engine-wasm/wasm/lix_engine.wasm +0 -0
  5. package/dist/engine-wasm/wasm/lix_engine.wasm.d.ts +5 -0
  6. package/dist/generated/builtin-schemas.d.ts +87 -162
  7. package/dist/generated/builtin-schemas.js +139 -236
  8. package/dist/open-lix.d.ts +10 -3
  9. package/dist/open-lix.js +39 -0
  10. package/dist-engine-src/src/binary_cas/types.rs +0 -6
  11. package/dist-engine-src/src/catalog/context.rs +412 -0
  12. package/dist-engine-src/src/catalog/mod.rs +10 -0
  13. package/dist-engine-src/src/catalog/schema.rs +4 -0
  14. package/dist-engine-src/src/catalog/snapshot.rs +1114 -0
  15. package/dist-engine-src/src/cel/mod.rs +1 -1
  16. package/dist-engine-src/src/cel/provider.rs +1 -1
  17. package/dist-engine-src/src/commit_graph/context.rs +328 -1015
  18. package/dist-engine-src/src/commit_graph/mod.rs +2 -3
  19. package/dist-engine-src/src/commit_graph/types.rs +7 -43
  20. package/dist-engine-src/src/commit_graph/walker.rs +57 -81
  21. package/dist-engine-src/src/commit_store/codec.rs +887 -0
  22. package/dist-engine-src/src/commit_store/context.rs +944 -0
  23. package/dist-engine-src/src/commit_store/materialization.rs +84 -0
  24. package/dist-engine-src/src/commit_store/mod.rs +16 -0
  25. package/dist-engine-src/src/commit_store/storage.rs +600 -0
  26. package/dist-engine-src/src/commit_store/types.rs +215 -0
  27. package/dist-engine-src/src/common/identity.rs +15 -5
  28. package/dist-engine-src/src/common/json_pointer.rs +67 -0
  29. package/dist-engine-src/src/common/metadata.rs +17 -12
  30. package/dist-engine-src/src/common/mod.rs +5 -5
  31. package/dist-engine-src/src/domain.rs +324 -0
  32. package/dist-engine-src/src/engine.rs +29 -43
  33. package/dist-engine-src/src/entity_identity.rs +238 -118
  34. package/dist-engine-src/src/functions/context.rs +17 -52
  35. package/dist-engine-src/src/functions/deterministic.rs +1 -1
  36. package/dist-engine-src/src/functions/mod.rs +1 -1
  37. package/dist-engine-src/src/functions/provider.rs +4 -4
  38. package/dist-engine-src/src/functions/state.rs +39 -66
  39. package/dist-engine-src/src/functions/types.rs +1 -1
  40. package/dist-engine-src/src/init.rs +204 -151
  41. package/dist-engine-src/src/json_store/context.rs +354 -60
  42. package/dist-engine-src/src/json_store/encoded.rs +6 -6
  43. package/dist-engine-src/src/json_store/mod.rs +4 -1
  44. package/dist-engine-src/src/json_store/store.rs +884 -11
  45. package/dist-engine-src/src/json_store/types.rs +166 -1
  46. package/dist-engine-src/src/lib.rs +11 -10
  47. package/dist-engine-src/src/live_state/context.rs +608 -830
  48. package/dist-engine-src/src/live_state/mod.rs +3 -3
  49. package/dist-engine-src/src/live_state/overlay.rs +7 -7
  50. package/dist-engine-src/src/live_state/reader.rs +5 -5
  51. package/dist-engine-src/src/live_state/types.rs +19 -36
  52. package/dist-engine-src/src/live_state/visibility.rs +19 -14
  53. package/dist-engine-src/src/plugin/archive.rs +3 -6
  54. package/dist-engine-src/src/plugin/install.rs +0 -18
  55. package/dist-engine-src/src/plugin/plugin_manifest.json +0 -1
  56. package/dist-engine-src/src/schema/annotations/defaults.rs +2 -7
  57. package/dist-engine-src/src/schema/builtin/lix_account.json +0 -1
  58. package/dist-engine-src/src/schema/builtin/lix_active_account.json +0 -1
  59. package/dist-engine-src/src/schema/builtin/lix_binary_blob_ref.json +0 -1
  60. package/dist-engine-src/src/schema/builtin/lix_change.json +11 -10
  61. package/dist-engine-src/src/schema/builtin/lix_change_author.json +0 -1
  62. package/dist-engine-src/src/schema/builtin/lix_commit.json +8 -46
  63. package/dist-engine-src/src/schema/builtin/lix_commit_edge.json +29 -22
  64. package/dist-engine-src/src/schema/builtin/lix_directory_descriptor.json +0 -1
  65. package/dist-engine-src/src/schema/builtin/lix_file_descriptor.json +0 -1
  66. package/dist-engine-src/src/schema/builtin/lix_key_value.json +0 -1
  67. package/dist-engine-src/src/schema/builtin/lix_label.json +10 -3
  68. package/dist-engine-src/src/schema/builtin/lix_label_assignment.json +74 -0
  69. package/dist-engine-src/src/schema/builtin/lix_registered_schema.json +2 -8
  70. package/dist-engine-src/src/schema/builtin/lix_version_descriptor.json +0 -1
  71. package/dist-engine-src/src/schema/builtin/lix_version_ref.json +0 -1
  72. package/dist-engine-src/src/schema/builtin/mod.rs +10 -59
  73. package/dist-engine-src/src/schema/compatibility.rs +787 -0
  74. package/dist-engine-src/src/schema/definition.json +47 -17
  75. package/dist-engine-src/src/schema/definition.rs +202 -96
  76. package/dist-engine-src/src/schema/key.rs +9 -77
  77. package/dist-engine-src/src/schema/mod.rs +4 -4
  78. package/dist-engine-src/src/schema/tests.rs +133 -92
  79. package/dist-engine-src/src/session/context.rs +86 -48
  80. package/dist-engine-src/src/session/create_version.rs +22 -14
  81. package/dist-engine-src/src/session/execute.rs +117 -23
  82. package/dist-engine-src/src/session/merge/apply.rs +4 -4
  83. package/dist-engine-src/src/session/merge/conflicts.rs +3 -2
  84. package/dist-engine-src/src/session/merge/stats.rs +1 -1
  85. package/dist-engine-src/src/session/merge/version.rs +35 -45
  86. package/dist-engine-src/src/session/mod.rs +9 -7
  87. package/dist-engine-src/src/session/optimization9_sql2_bench.rs +100 -0
  88. package/dist-engine-src/src/session/switch_version.rs +17 -28
  89. package/dist-engine-src/src/session/transaction.rs +76 -0
  90. package/dist-engine-src/src/sql2/change_provider.rs +14 -20
  91. package/dist-engine-src/src/sql2/classify.rs +75 -48
  92. package/dist-engine-src/src/sql2/context.rs +22 -18
  93. package/dist-engine-src/src/sql2/directory_history_provider.rs +28 -20
  94. package/dist-engine-src/src/sql2/directory_provider.rs +131 -83
  95. package/dist-engine-src/src/sql2/entity_history_provider.rs +10 -14
  96. package/dist-engine-src/src/sql2/entity_provider.rs +680 -169
  97. package/dist-engine-src/src/sql2/error.rs +24 -5
  98. package/dist-engine-src/src/sql2/execute.rs +426 -272
  99. package/dist-engine-src/src/sql2/file_history_provider.rs +29 -21
  100. package/dist-engine-src/src/sql2/file_provider.rs +533 -108
  101. package/dist-engine-src/src/sql2/filesystem_planner.rs +58 -94
  102. package/dist-engine-src/src/sql2/filesystem_visibility.rs +37 -23
  103. package/dist-engine-src/src/sql2/history_projection.rs +3 -27
  104. package/dist-engine-src/src/sql2/history_provider.rs +11 -17
  105. package/dist-engine-src/src/sql2/history_route.rs +22 -8
  106. package/dist-engine-src/src/sql2/lix_state_provider.rs +178 -96
  107. package/dist-engine-src/src/sql2/mod.rs +8 -4
  108. package/dist-engine-src/src/sql2/predicate_typecheck.rs +246 -0
  109. package/dist-engine-src/src/sql2/public_bind/assignment.rs +46 -0
  110. package/dist-engine-src/src/sql2/public_bind/capability.rs +41 -0
  111. package/dist-engine-src/src/sql2/public_bind/dml.rs +172 -0
  112. package/dist-engine-src/src/sql2/public_bind/mod.rs +26 -0
  113. package/dist-engine-src/src/sql2/public_bind/table.rs +168 -0
  114. package/dist-engine-src/src/sql2/read_only.rs +10 -12
  115. package/dist-engine-src/src/sql2/session.rs +7 -10
  116. package/dist-engine-src/src/sql2/udfs/lix_timestamp.rs +76 -0
  117. package/dist-engine-src/src/sql2/udfs/mod.rs +8 -1
  118. package/dist-engine-src/src/sql2/udfs/public_call.rs +238 -0
  119. package/dist-engine-src/src/sql2/version_provider.rs +46 -31
  120. package/dist-engine-src/src/sql2/version_scope.rs +4 -4
  121. package/dist-engine-src/src/storage_bench.rs +1782 -325
  122. package/dist-engine-src/src/test_support.rs +183 -36
  123. package/dist-engine-src/src/tracked_state/by_file_index.rs +20 -24
  124. package/dist-engine-src/src/tracked_state/codec.rs +1519 -181
  125. package/dist-engine-src/src/tracked_state/context.rs +1155 -271
  126. package/dist-engine-src/src/tracked_state/diff.rs +249 -57
  127. package/dist-engine-src/src/tracked_state/materialization.rs +365 -103
  128. package/dist-engine-src/src/tracked_state/materializer.rs +488 -0
  129. package/dist-engine-src/src/tracked_state/merge.rs +37 -19
  130. package/dist-engine-src/src/tracked_state/mod.rs +8 -7
  131. package/dist-engine-src/src/tracked_state/storage.rs +138 -6
  132. package/dist-engine-src/src/tracked_state/tree.rs +695 -252
  133. package/dist-engine-src/src/tracked_state/types.rs +176 -6
  134. package/dist-engine-src/src/transaction/commit.rs +695 -435
  135. package/dist-engine-src/src/transaction/context.rs +551 -310
  136. package/dist-engine-src/src/transaction/live_state_overlay.rs +9 -8
  137. package/dist-engine-src/src/transaction/mod.rs +2 -0
  138. package/dist-engine-src/src/transaction/normalization.rs +311 -447
  139. package/dist-engine-src/src/transaction/prep.rs +37 -0
  140. package/dist-engine-src/src/transaction/schema_resolver.rs +93 -71
  141. package/dist-engine-src/src/transaction/staging.rs +701 -406
  142. package/dist-engine-src/src/transaction/types.rs +231 -122
  143. package/dist-engine-src/src/transaction/validation.rs +2717 -1698
  144. package/dist-engine-src/src/untracked_state/codec.rs +40 -96
  145. package/dist-engine-src/src/untracked_state/context.rs +21 -5
  146. package/dist-engine-src/src/untracked_state/materialization.rs +10 -104
  147. package/dist-engine-src/src/untracked_state/mod.rs +3 -5
  148. package/dist-engine-src/src/untracked_state/storage.rs +105 -57
  149. package/dist-engine-src/src/untracked_state/types.rs +63 -13
  150. package/dist-engine-src/src/version/context.rs +1 -13
  151. package/dist-engine-src/src/version/lifecycle.rs +221 -0
  152. package/dist-engine-src/src/version/mod.rs +3 -2
  153. package/dist-engine-src/src/version/refs.rs +12 -103
  154. package/dist-engine-src/src/version/stage_rows.rs +15 -19
  155. package/package.json +1 -1
  156. package/dist-engine-src/src/changelog/codec.rs +0 -321
  157. package/dist-engine-src/src/changelog/context.rs +0 -92
  158. package/dist-engine-src/src/changelog/materialization.rs +0 -121
  159. package/dist-engine-src/src/changelog/mod.rs +0 -13
  160. package/dist-engine-src/src/changelog/reader.rs +0 -20
  161. package/dist-engine-src/src/changelog/storage.rs +0 -220
  162. package/dist-engine-src/src/changelog/types.rs +0 -38
  163. package/dist-engine-src/src/schema/builtin/lix_change_set.json +0 -18
  164. package/dist-engine-src/src/schema/builtin/lix_change_set_element.json +0 -75
  165. package/dist-engine-src/src/schema/builtin/lix_entity_label.json +0 -63
  166. package/dist-engine-src/src/schema_registry.rs +0 -294
  167. package/dist-engine-src/src/sql2/commit_derived_provider.rs +0 -591
  168. package/dist-engine-src/src/tracked_state/rebuild.rs +0 -771
  169. package/dist-engine-src/src/tracked_state/tree_types.rs +0 -176
@@ -1,121 +0,0 @@
1
- use crate::changelog::{CanonicalChange, MaterializedCanonicalChange};
2
- use crate::json_store::{JsonRef, JsonStoreReader, JsonStoreWriter};
3
- use crate::storage::{StorageReader, StorageWriteSet};
4
- use crate::{serialize_row_metadata, validate_row_metadata, LixError, RowMetadata};
5
-
6
- pub(crate) fn canonicalize_materialized_change(
7
- writes: &mut StorageWriteSet,
8
- json_writer: &mut JsonStoreWriter,
9
- change: &MaterializedCanonicalChange,
10
- ) -> Result<CanonicalChange, LixError> {
11
- let snapshot_ref =
12
- stage_optional_json(writes, json_writer, change.snapshot_content.as_deref())?;
13
- let metadata_ref = stage_optional_metadata(writes, json_writer, change.metadata.as_ref())?;
14
- Ok(CanonicalChange {
15
- id: change.id.clone(),
16
- entity_id: change.entity_id.clone(),
17
- schema_key: change.schema_key.clone(),
18
- schema_version: change.schema_version.clone(),
19
- file_id: change.file_id.clone(),
20
- snapshot_ref,
21
- metadata_ref,
22
- created_at: change.created_at.clone(),
23
- })
24
- }
25
-
26
- pub(crate) async fn materialize_change<S>(
27
- json_reader: &mut JsonStoreReader<S>,
28
- change: CanonicalChange,
29
- ) -> Result<MaterializedCanonicalChange, LixError>
30
- where
31
- S: StorageReader,
32
- {
33
- let snapshot_content =
34
- load_optional_json(json_reader, change.snapshot_ref.as_ref(), "snapshot_ref").await?;
35
- let metadata = load_optional_metadata(json_reader, change.metadata_ref.as_ref()).await?;
36
- Ok(MaterializedCanonicalChange {
37
- id: change.id,
38
- entity_id: change.entity_id,
39
- schema_key: change.schema_key,
40
- schema_version: change.schema_version,
41
- file_id: change.file_id,
42
- snapshot_content,
43
- metadata,
44
- created_at: change.created_at,
45
- })
46
- }
47
-
48
- fn stage_optional_json(
49
- writes: &mut StorageWriteSet,
50
- json_writer: &mut JsonStoreWriter,
51
- value: Option<&str>,
52
- ) -> Result<Option<JsonRef>, LixError> {
53
- let Some(value) = value else {
54
- return Ok(None);
55
- };
56
- json_writer.stage_bytes(writes, value.as_bytes()).map(Some)
57
- }
58
-
59
- fn stage_optional_metadata(
60
- writes: &mut StorageWriteSet,
61
- json_writer: &mut JsonStoreWriter,
62
- value: Option<&RowMetadata>,
63
- ) -> Result<Option<JsonRef>, LixError> {
64
- let Some(value) = value else {
65
- return Ok(None);
66
- };
67
- let serialized = serialize_row_metadata(value);
68
- json_writer
69
- .stage_bytes(writes, serialized.as_bytes())
70
- .map(Some)
71
- }
72
-
73
- async fn load_optional_metadata<S>(
74
- json_reader: &mut JsonStoreReader<S>,
75
- json_ref: Option<&JsonRef>,
76
- ) -> Result<Option<RowMetadata>, LixError>
77
- where
78
- S: StorageReader,
79
- {
80
- let Some(json) = load_optional_json(json_reader, json_ref, "metadata_ref").await? else {
81
- return Ok(None);
82
- };
83
- let metadata = serde_json::from_str::<RowMetadata>(&json).map_err(|error| {
84
- LixError::new(
85
- "LIX_ERROR_INVALID_JSON",
86
- format!("metadata_ref is invalid JSON: {error}"),
87
- )
88
- })?;
89
- validate_row_metadata(metadata, "metadata_ref").map(Some)
90
- }
91
-
92
- async fn load_optional_json<S>(
93
- json_reader: &mut JsonStoreReader<S>,
94
- json_ref: Option<&JsonRef>,
95
- field: &str,
96
- ) -> Result<Option<String>, LixError>
97
- where
98
- S: StorageReader,
99
- {
100
- let Some(json_ref) = json_ref else {
101
- return Ok(None);
102
- };
103
- let bytes = json_reader.load_bytes(json_ref).await?.ok_or_else(|| {
104
- LixError::new(
105
- "LIX_ERROR_UNKNOWN",
106
- format!(
107
- "changelog {field} '{}' is missing from json_store",
108
- json_ref.to_hex()
109
- ),
110
- )
111
- })?;
112
- String::from_utf8(bytes).map(Some).map_err(|error| {
113
- LixError::new(
114
- "LIX_ERROR_UNKNOWN",
115
- format!(
116
- "changelog {field} '{}' is not valid UTF-8 JSON bytes: {error}",
117
- json_ref.to_hex()
118
- ),
119
- )
120
- })
121
- }
@@ -1,13 +0,0 @@
1
- pub(crate) mod codec;
2
- mod context;
3
- mod materialization;
4
- mod reader;
5
- mod storage;
6
- mod types;
7
-
8
- #[allow(unused_imports)]
9
- pub(crate) use context::{ChangelogContext, ChangelogStoreReader, ChangelogWriter};
10
- pub(crate) use materialization::{canonicalize_materialized_change, materialize_change};
11
- pub(crate) use reader::ChangelogReader;
12
- #[allow(unused_imports)]
13
- pub(crate) use types::{CanonicalChange, ChangelogScanRequest, MaterializedCanonicalChange};
@@ -1,20 +0,0 @@
1
- use async_trait::async_trait;
2
-
3
- use crate::changelog::{CanonicalChange, ChangelogScanRequest};
4
- use crate::LixError;
5
-
6
- /// Read side for immutable changelog facts.
7
- ///
8
- /// SQL providers and commit-graph readers depend on this role instead of
9
- /// knowing which KV store backs the changelog for the current execution.
10
- #[async_trait]
11
- pub(crate) trait ChangelogReader: Send + Sync {
12
- #[allow(dead_code)]
13
- async fn load_change(&self, change_id: &str) -> Result<Option<CanonicalChange>, LixError>;
14
-
15
- #[allow(dead_code)]
16
- async fn scan_changes(
17
- &self,
18
- request: &ChangelogScanRequest,
19
- ) -> Result<Vec<CanonicalChange>, LixError>;
20
- }
@@ -1,220 +0,0 @@
1
- use crate::changelog::codec::{decode_change, encode_change};
2
- use crate::changelog::{CanonicalChange, ChangelogScanRequest};
3
- use crate::storage::KvScanRange;
4
- use crate::storage::{KvGetGroup, KvGetRequest, KvScanRequest, StorageReader, StorageWriteSet};
5
- use crate::LixError;
6
-
7
- const CHANGELOG_CHANGE_NAMESPACE: &str = "changelog.change";
8
-
9
- pub(crate) async fn load_change(
10
- store: &mut impl StorageReader,
11
- change_id: &str,
12
- ) -> Result<Option<CanonicalChange>, LixError> {
13
- let bytes = store
14
- .get_values(KvGetRequest {
15
- groups: vec![KvGetGroup {
16
- namespace: CHANGELOG_CHANGE_NAMESPACE.to_string(),
17
- keys: vec![encode_change_key(change_id)],
18
- }],
19
- })
20
- .await?
21
- .groups
22
- .into_iter()
23
- .next()
24
- .and_then(|group| group.single_value_owned());
25
- let Some(bytes) = bytes else {
26
- return Ok(None);
27
- };
28
- decode_change(&bytes).map(Some)
29
- }
30
-
31
- pub(crate) async fn scan_changes(
32
- store: &mut impl StorageReader,
33
- request: &ChangelogScanRequest,
34
- ) -> Result<Vec<CanonicalChange>, LixError> {
35
- // TODO(engine2): scan by a durable append sequence instead of change id.
36
- // This first index is enough for exact lookup and deterministic debug scans.
37
- let page = store
38
- .scan_values(KvScanRequest {
39
- namespace: CHANGELOG_CHANGE_NAMESPACE.to_string(),
40
- range: KvScanRange::prefix(Vec::new()),
41
- after: None,
42
- limit: request.limit.unwrap_or(usize::MAX),
43
- })
44
- .await?;
45
- page.values.iter().map(decode_change).collect()
46
- }
47
-
48
- pub(crate) fn stage_changes(
49
- writes: &mut StorageWriteSet,
50
- changes: &[CanonicalChange],
51
- ) -> Result<(), LixError> {
52
- for change in changes {
53
- writes.put(
54
- CHANGELOG_CHANGE_NAMESPACE,
55
- encode_change_key(&change.id),
56
- encode_change(change)?,
57
- );
58
- }
59
- Ok(())
60
- }
61
-
62
- fn encode_change_key(change_id: &str) -> Vec<u8> {
63
- change_id.as_bytes().to_vec()
64
- }
65
-
66
- #[cfg(test)]
67
- mod tests {
68
- use std::sync::Arc;
69
-
70
- use crate::backend::testing::UnitTestBackend;
71
- use crate::changelog::{
72
- canonicalize_materialized_change, materialize_change, ChangelogContext,
73
- ChangelogScanRequest, MaterializedCanonicalChange,
74
- };
75
- use crate::json_store::JsonStoreContext;
76
- use crate::storage::{StorageContext, StorageWriteSet, StorageWriteTransaction};
77
-
78
- use super::*;
79
-
80
- #[tokio::test]
81
- async fn append_and_load_change_roundtrips() {
82
- let storage = StorageContext::new(Arc::new(UnitTestBackend::new()));
83
- let changelog = ChangelogContext::new();
84
- let change = test_change("change-1");
85
-
86
- let mut tx = storage
87
- .begin_write_transaction()
88
- .await
89
- .expect("transaction should open");
90
- append_test_changes(&changelog, &mut tx, std::slice::from_ref(&change)).await;
91
- tx.commit().await.expect("commit should succeed");
92
-
93
- let loaded = load_test_change(&changelog, storage, "change-1").await;
94
- assert_eq!(loaded, Some(change));
95
- }
96
-
97
- #[tokio::test]
98
- async fn append_and_load_composite_entity_identity_roundtrips() {
99
- let storage = StorageContext::new(Arc::new(UnitTestBackend::new()));
100
- let changelog = ChangelogContext::new();
101
- let mut change = test_change("change-composite");
102
- change.entity_id = crate::entity_identity::EntityIdentity::tuple(vec![
103
- crate::entity_identity::EntityIdentityPart::String("entity".to_string()),
104
- crate::entity_identity::EntityIdentityPart::Number("7".to_string()),
105
- crate::entity_identity::EntityIdentityPart::Bool(true),
106
- ])
107
- .expect("composite identity should be valid");
108
-
109
- let mut tx = storage
110
- .begin_write_transaction()
111
- .await
112
- .expect("transaction should open");
113
- append_test_changes(&changelog, &mut tx, std::slice::from_ref(&change)).await;
114
- tx.commit().await.expect("commit should succeed");
115
-
116
- let loaded = load_test_change(&changelog, storage, "change-composite").await;
117
- assert_eq!(loaded, Some(change));
118
- }
119
-
120
- #[test]
121
- fn decode_rejects_non_flatbuffer_bytes() {
122
- let error = decode_change(br#"{"id":"change-json"}"#)
123
- .expect_err("json changelog payloads are not accepted after the hard cut");
124
- assert!(
125
- error
126
- .message
127
- .contains("invalid FlatBuffers file identifier"),
128
- "unexpected error: {error:?}"
129
- );
130
- }
131
-
132
- #[tokio::test]
133
- async fn scan_changes_respects_limit() {
134
- let storage = StorageContext::new(Arc::new(UnitTestBackend::new()));
135
- let changelog = ChangelogContext::new();
136
- let mut tx = storage
137
- .begin_write_transaction()
138
- .await
139
- .expect("transaction should open");
140
- append_test_changes(
141
- &changelog,
142
- &mut tx,
143
- &[test_change("change-1"), test_change("change-2")],
144
- )
145
- .await;
146
- tx.commit().await.expect("commit should succeed");
147
-
148
- let canonical_changes = {
149
- let reader = changelog.reader(storage.clone());
150
- reader
151
- .scan_changes(&ChangelogScanRequest { limit: Some(1) })
152
- .await
153
- }
154
- .expect("scan should succeed");
155
- let mut json_reader = JsonStoreContext::new().reader(storage);
156
- let mut changes = Vec::new();
157
- for change in canonical_changes {
158
- changes.push(
159
- materialize_change(&mut json_reader, change)
160
- .await
161
- .expect("change should materialize"),
162
- );
163
- }
164
- assert_eq!(changes, vec![test_change("change-1")]);
165
- }
166
-
167
- fn test_change(id: &str) -> MaterializedCanonicalChange {
168
- MaterializedCanonicalChange {
169
- id: id.to_string(),
170
- entity_id: crate::entity_identity::EntityIdentity::single("entity-1"),
171
- schema_key: "test_schema".to_string(),
172
- schema_version: "1".to_string(),
173
- file_id: None,
174
- snapshot_content: Some("{\"value\":1}".to_string()),
175
- metadata: None,
176
- created_at: "2026-01-01T00:00:00Z".to_string(),
177
- }
178
- }
179
-
180
- async fn append_test_changes(
181
- changelog: &ChangelogContext,
182
- tx: &mut Box<dyn StorageWriteTransaction + Send + Sync + 'static>,
183
- changes: &[MaterializedCanonicalChange],
184
- ) {
185
- let mut writes = StorageWriteSet::new();
186
- let mut json_writer = JsonStoreContext::new().writer();
187
- let canonical_changes = changes
188
- .iter()
189
- .map(|change| canonicalize_materialized_change(&mut writes, &mut json_writer, change))
190
- .collect::<Result<Vec<_>, _>>()
191
- .expect("changes should canonicalize");
192
- let mut writer = changelog.writer(&mut writes);
193
- writer
194
- .stage_changes(&canonical_changes)
195
- .expect("append should succeed");
196
- writes
197
- .apply(&mut tx.as_mut())
198
- .await
199
- .expect("writes should apply");
200
- }
201
-
202
- async fn load_test_change(
203
- changelog: &ChangelogContext,
204
- storage: StorageContext,
205
- change_id: &str,
206
- ) -> Option<MaterializedCanonicalChange> {
207
- let canonical = {
208
- let reader = changelog.reader(storage.clone());
209
- reader
210
- .load_change(change_id)
211
- .await
212
- .expect("load should succeed")
213
- }?;
214
- let mut json_reader = JsonStoreContext::new().reader(storage);
215
- materialize_change(&mut json_reader, canonical)
216
- .await
217
- .map(Some)
218
- .expect("change should materialize")
219
- }
220
- }
@@ -1,38 +0,0 @@
1
- use crate::entity_identity::EntityIdentity;
2
- use crate::json_store::JsonRef;
3
- use crate::RowMetadata;
4
-
5
- /// Immutable canonical change fact stored in the changelog.
6
- #[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
7
- pub(crate) struct CanonicalChange {
8
- pub(crate) id: String,
9
- pub(crate) entity_id: EntityIdentity,
10
- pub(crate) schema_key: String,
11
- pub(crate) schema_version: String,
12
- pub(crate) file_id: Option<String>,
13
- pub(crate) snapshot_ref: Option<JsonRef>,
14
- pub(crate) metadata_ref: Option<JsonRef>,
15
- pub(crate) created_at: String,
16
- }
17
-
18
- /// Boundary shape for callers that still work with materialized JSON payloads.
19
- #[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
20
- pub(crate) struct MaterializedCanonicalChange {
21
- pub(crate) id: String,
22
- pub(crate) entity_id: EntityIdentity,
23
- pub(crate) schema_key: String,
24
- pub(crate) schema_version: String,
25
- pub(crate) file_id: Option<String>,
26
- pub(crate) snapshot_content: Option<String>,
27
- pub(crate) metadata: Option<RowMetadata>,
28
- pub(crate) created_at: String,
29
- }
30
-
31
- /// Minimal changelog scan request.
32
- ///
33
- /// TODO(engine2): add filters and append-order cursors once changelog storage
34
- /// has real append sequence keys.
35
- #[derive(Debug, Clone, Default, PartialEq, Eq)]
36
- pub(crate) struct ChangelogScanRequest {
37
- pub(crate) limit: Option<usize>,
38
- }
@@ -1,18 +0,0 @@
1
- {
2
- "x-lix-key": "lix_change_set",
3
- "x-lix-version": "1",
4
- "x-lix-primary-key": [
5
- "/id"
6
- ],
7
- "type": "object",
8
- "properties": {
9
- "id": {
10
- "type": "string",
11
- "x-lix-default": "lix_uuid_v7()"
12
- }
13
- },
14
- "required": [
15
- "id"
16
- ],
17
- "additionalProperties": false
18
- }
@@ -1,75 +0,0 @@
1
- {
2
- "x-lix-key": "lix_change_set_element",
3
- "x-lix-version": "1",
4
- "description": "Derived relational index mapping a change_set to the canonical changes introduced by its commit relative to the commit's first parent. Rebuildable from `lix_commit.change_ids` plus the referenced `lix_change` rows; the unique constraint enforces at most one change per (entity, schema, file) tuple within a single change_set.",
5
- "x-lix-primary-key": [
6
- "/change_set_id",
7
- "/change_id"
8
- ],
9
- "x-lix-unique": [
10
- [
11
- "/change_set_id",
12
- "/entity_id",
13
- "/schema_key",
14
- "/file_id"
15
- ]
16
- ],
17
- "x-lix-foreign-keys": [
18
- {
19
- "properties": [
20
- "/change_set_id"
21
- ],
22
- "references": {
23
- "schemaKey": "lix_change_set",
24
- "properties": [
25
- "/id"
26
- ]
27
- }
28
- },
29
- {
30
- "properties": [
31
- "/change_id"
32
- ],
33
- "references": {
34
- "schemaKey": "lix_change",
35
- "properties": [
36
- "/id"
37
- ]
38
- }
39
- }
40
- ],
41
- "type": "object",
42
- "properties": {
43
- "change_set_id": {
44
- "type": "string",
45
- "description": "The change_set this element belongs to (references `lix_change_set.id`)."
46
- },
47
- "change_id": {
48
- "type": "string",
49
- "description": "The canonical change introduced/adopted by the change_set's commit (references `lix_change.id`)."
50
- },
51
- "entity_id": {
52
- "type": "string",
53
- "description": "Denormalized from the referenced `lix_change` for fast entity-scoped queries and to anchor the unique constraint."
54
- },
55
- "schema_key": {
56
- "type": "string",
57
- "description": "Denormalized schema identifier from the referenced `lix_change`; part of the per-change_set uniqueness tuple."
58
- },
59
- "file_id": {
60
- "type": [
61
- "string",
62
- "null"
63
- ],
64
- "description": "Denormalized file scope from the referenced `lix_change`; NULL for engine-internal entities. Part of the per-change_set uniqueness tuple."
65
- }
66
- },
67
- "required": [
68
- "change_set_id",
69
- "change_id",
70
- "entity_id",
71
- "schema_key",
72
- "file_id"
73
- ],
74
- "additionalProperties": false
75
- }
@@ -1,63 +0,0 @@
1
- {
2
- "x-lix-key": "lix_entity_label",
3
- "x-lix-version": "1",
4
- "x-lix-primary-key": [
5
- "/entity_id",
6
- "/schema_key",
7
- "/file_id",
8
- "/label_id"
9
- ],
10
- "x-lix-foreign-keys": [
11
- {
12
- "properties": [
13
- "/entity_id",
14
- "/schema_key",
15
- "/file_id"
16
- ],
17
- "references": {
18
- "schemaKey": "lix_state",
19
- "properties": [
20
- "/entity_id",
21
- "/schema_key",
22
- "/file_id"
23
- ]
24
- }
25
- },
26
- {
27
- "properties": [
28
- "/label_id"
29
- ],
30
- "references": {
31
- "schemaKey": "lix_label",
32
- "properties": [
33
- "/id"
34
- ]
35
- }
36
- }
37
- ],
38
- "type": "object",
39
- "properties": {
40
- "entity_id": {
41
- "type": "string"
42
- },
43
- "schema_key": {
44
- "type": "string"
45
- },
46
- "file_id": {
47
- "type": [
48
- "string",
49
- "null"
50
- ]
51
- },
52
- "label_id": {
53
- "type": "string"
54
- }
55
- },
56
- "required": [
57
- "entity_id",
58
- "schema_key",
59
- "file_id",
60
- "label_id"
61
- ],
62
- "additionalProperties": false
63
- }