@lix-js/sdk 0.6.0-preview.1 → 0.6.0-preview.3

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 (205) hide show
  1. package/SKILL.md +304 -320
  2. package/dist/engine-wasm/wasm/lix_engine.d.ts +5 -0
  3. package/dist/engine-wasm/wasm/lix_engine.js +9 -13
  4. package/dist/engine-wasm/wasm/lix_engine.wasm +0 -0
  5. package/dist/engine-wasm/wasm/lix_engine.wasm.d.ts +1 -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 +103 -14
  9. package/dist/open-lix.js +3 -0
  10. package/dist/sqlite/index.js +99 -22
  11. package/dist-engine-src/README.md +18 -0
  12. package/dist-engine-src/src/backend/kv.rs +358 -0
  13. package/dist-engine-src/src/backend/mod.rs +12 -0
  14. package/dist-engine-src/src/backend/testing.rs +658 -0
  15. package/dist-engine-src/src/backend/types.rs +96 -0
  16. package/dist-engine-src/src/binary_cas/chunking.rs +31 -0
  17. package/dist-engine-src/src/binary_cas/codec.rs +346 -0
  18. package/dist-engine-src/src/binary_cas/context.rs +139 -0
  19. package/dist-engine-src/src/binary_cas/kv.rs +1063 -0
  20. package/dist-engine-src/src/binary_cas/mod.rs +11 -0
  21. package/dist-engine-src/src/binary_cas/types.rs +121 -0
  22. package/dist-engine-src/src/catalog/context.rs +412 -0
  23. package/dist-engine-src/src/catalog/mod.rs +10 -0
  24. package/dist-engine-src/src/catalog/schema.rs +4 -0
  25. package/dist-engine-src/src/catalog/snapshot.rs +1114 -0
  26. package/dist-engine-src/src/cel/context.rs +86 -0
  27. package/dist-engine-src/src/cel/error.rs +19 -0
  28. package/dist-engine-src/src/cel/mod.rs +8 -0
  29. package/dist-engine-src/src/cel/provider.rs +9 -0
  30. package/dist-engine-src/src/cel/runtime.rs +167 -0
  31. package/dist-engine-src/src/cel/value.rs +50 -0
  32. package/dist-engine-src/src/commit_graph/context.rs +901 -0
  33. package/dist-engine-src/src/commit_graph/mod.rs +11 -0
  34. package/dist-engine-src/src/commit_graph/types.rs +109 -0
  35. package/dist-engine-src/src/commit_graph/walker.rs +756 -0
  36. package/dist-engine-src/src/commit_store/codec.rs +887 -0
  37. package/dist-engine-src/src/commit_store/context.rs +944 -0
  38. package/dist-engine-src/src/commit_store/materialization.rs +84 -0
  39. package/dist-engine-src/src/commit_store/mod.rs +16 -0
  40. package/dist-engine-src/src/commit_store/storage.rs +600 -0
  41. package/dist-engine-src/src/commit_store/types.rs +215 -0
  42. package/dist-engine-src/src/common/error.rs +313 -0
  43. package/dist-engine-src/src/common/fingerprint.rs +3 -0
  44. package/dist-engine-src/src/common/fs_path.rs +1336 -0
  45. package/dist-engine-src/src/common/identity.rs +145 -0
  46. package/dist-engine-src/src/common/json_pointer.rs +67 -0
  47. package/dist-engine-src/src/common/metadata.rs +40 -0
  48. package/dist-engine-src/src/common/mod.rs +23 -0
  49. package/dist-engine-src/src/common/types.rs +105 -0
  50. package/dist-engine-src/src/common/wire.rs +222 -0
  51. package/dist-engine-src/src/domain.rs +324 -0
  52. package/dist-engine-src/src/engine.rs +225 -0
  53. package/dist-engine-src/src/entity_identity.rs +405 -0
  54. package/dist-engine-src/src/functions/context.rs +292 -0
  55. package/dist-engine-src/src/functions/deterministic.rs +113 -0
  56. package/dist-engine-src/src/functions/mod.rs +18 -0
  57. package/dist-engine-src/src/functions/provider.rs +130 -0
  58. package/dist-engine-src/src/functions/state.rs +336 -0
  59. package/dist-engine-src/src/functions/types.rs +37 -0
  60. package/dist-engine-src/src/init.rs +558 -0
  61. package/dist-engine-src/src/json_store/compression.rs +77 -0
  62. package/dist-engine-src/src/json_store/context.rs +423 -0
  63. package/dist-engine-src/src/json_store/encoded.rs +15 -0
  64. package/dist-engine-src/src/json_store/mod.rs +12 -0
  65. package/dist-engine-src/src/json_store/store.rs +1109 -0
  66. package/dist-engine-src/src/json_store/types.rs +217 -0
  67. package/dist-engine-src/src/lib.rs +62 -0
  68. package/dist-engine-src/src/live_state/context.rs +2019 -0
  69. package/dist-engine-src/src/live_state/mod.rs +15 -0
  70. package/dist-engine-src/src/live_state/overlay.rs +75 -0
  71. package/dist-engine-src/src/live_state/reader.rs +23 -0
  72. package/dist-engine-src/src/live_state/types.rs +222 -0
  73. package/dist-engine-src/src/live_state/visibility.rs +223 -0
  74. package/dist-engine-src/src/plugin/archive.rs +438 -0
  75. package/dist-engine-src/src/plugin/component.rs +183 -0
  76. package/dist-engine-src/src/plugin/install.rs +619 -0
  77. package/dist-engine-src/src/plugin/manifest.rs +516 -0
  78. package/dist-engine-src/src/plugin/materializer.rs +477 -0
  79. package/dist-engine-src/src/plugin/mod.rs +33 -0
  80. package/dist-engine-src/src/plugin/plugin_manifest.json +118 -0
  81. package/dist-engine-src/src/plugin/storage.rs +74 -0
  82. package/dist-engine-src/src/schema/annotations/defaults.rs +275 -0
  83. package/dist-engine-src/src/schema/annotations/mod.rs +1 -0
  84. package/dist-engine-src/src/schema/builtin/lix_account.json +21 -0
  85. package/dist-engine-src/src/schema/builtin/lix_active_account.json +29 -0
  86. package/dist-engine-src/src/schema/builtin/lix_binary_blob_ref.json +29 -0
  87. package/dist-engine-src/src/schema/builtin/lix_change.json +63 -0
  88. package/dist-engine-src/src/schema/builtin/lix_change_author.json +45 -0
  89. package/dist-engine-src/src/schema/builtin/lix_commit.json +24 -0
  90. package/dist-engine-src/src/schema/builtin/lix_commit_edge.json +53 -0
  91. package/dist-engine-src/src/schema/builtin/lix_directory_descriptor.json +52 -0
  92. package/dist-engine-src/src/schema/builtin/lix_file_descriptor.json +52 -0
  93. package/dist-engine-src/src/schema/builtin/lix_key_value.json +40 -0
  94. package/dist-engine-src/src/schema/builtin/lix_label.json +29 -0
  95. package/dist-engine-src/src/schema/builtin/lix_label_assignment.json +74 -0
  96. package/dist-engine-src/src/schema/builtin/lix_registered_schema.json +25 -0
  97. package/dist-engine-src/src/schema/builtin/lix_version_descriptor.json +34 -0
  98. package/dist-engine-src/src/schema/builtin/lix_version_ref.json +48 -0
  99. package/dist-engine-src/src/schema/builtin/mod.rs +222 -0
  100. package/dist-engine-src/src/schema/compatibility.rs +787 -0
  101. package/dist-engine-src/src/schema/definition.json +187 -0
  102. package/dist-engine-src/src/schema/definition.rs +742 -0
  103. package/dist-engine-src/src/schema/key.rs +138 -0
  104. package/dist-engine-src/src/schema/mod.rs +20 -0
  105. package/dist-engine-src/src/schema/seed.rs +14 -0
  106. package/dist-engine-src/src/schema/tests.rs +780 -0
  107. package/dist-engine-src/src/session/context.rs +364 -0
  108. package/dist-engine-src/src/session/create_version.rs +88 -0
  109. package/dist-engine-src/src/session/execute.rs +478 -0
  110. package/dist-engine-src/src/session/merge/analysis.rs +102 -0
  111. package/dist-engine-src/src/session/merge/apply.rs +23 -0
  112. package/dist-engine-src/src/session/merge/conflicts.rs +63 -0
  113. package/dist-engine-src/src/session/merge/mod.rs +11 -0
  114. package/dist-engine-src/src/session/merge/stats.rs +65 -0
  115. package/dist-engine-src/src/session/merge/version.rs +427 -0
  116. package/dist-engine-src/src/session/mod.rs +27 -0
  117. package/dist-engine-src/src/session/optimization9_sql2_bench.rs +100 -0
  118. package/dist-engine-src/src/session/switch_version.rs +109 -0
  119. package/dist-engine-src/src/sql2/change_provider.rs +331 -0
  120. package/dist-engine-src/src/sql2/classify.rs +182 -0
  121. package/dist-engine-src/src/sql2/context.rs +311 -0
  122. package/dist-engine-src/src/sql2/directory_history_provider.rs +631 -0
  123. package/dist-engine-src/src/sql2/directory_provider.rs +2453 -0
  124. package/dist-engine-src/src/sql2/dml.rs +148 -0
  125. package/dist-engine-src/src/sql2/entity_history_provider.rs +440 -0
  126. package/dist-engine-src/src/sql2/entity_provider.rs +3211 -0
  127. package/dist-engine-src/src/sql2/error.rs +216 -0
  128. package/dist-engine-src/src/sql2/execute.rs +3440 -0
  129. package/dist-engine-src/src/sql2/file_history_provider.rs +910 -0
  130. package/dist-engine-src/src/sql2/file_provider.rs +3679 -0
  131. package/dist-engine-src/src/sql2/filesystem_planner.rs +1490 -0
  132. package/dist-engine-src/src/sql2/filesystem_predicates.rs +159 -0
  133. package/dist-engine-src/src/sql2/filesystem_visibility.rs +383 -0
  134. package/dist-engine-src/src/sql2/history_projection.rs +56 -0
  135. package/dist-engine-src/src/sql2/history_provider.rs +412 -0
  136. package/dist-engine-src/src/sql2/history_route.rs +657 -0
  137. package/dist-engine-src/src/sql2/lix_state_provider.rs +2512 -0
  138. package/dist-engine-src/src/sql2/mod.rs +46 -0
  139. package/dist-engine-src/src/sql2/predicate_typecheck.rs +246 -0
  140. package/dist-engine-src/src/sql2/public_bind/assignment.rs +46 -0
  141. package/dist-engine-src/src/sql2/public_bind/capability.rs +41 -0
  142. package/dist-engine-src/src/sql2/public_bind/dml.rs +166 -0
  143. package/dist-engine-src/src/sql2/public_bind/mod.rs +25 -0
  144. package/dist-engine-src/src/sql2/public_bind/table.rs +168 -0
  145. package/dist-engine-src/src/sql2/read_only.rs +63 -0
  146. package/dist-engine-src/src/sql2/record_batch.rs +17 -0
  147. package/dist-engine-src/src/sql2/result_metadata.rs +29 -0
  148. package/dist-engine-src/src/sql2/runtime.rs +60 -0
  149. package/dist-engine-src/src/sql2/session.rs +132 -0
  150. package/dist-engine-src/src/sql2/udfs/common.rs +295 -0
  151. package/dist-engine-src/src/sql2/udfs/lix_active_version_commit_id.rs +53 -0
  152. package/dist-engine-src/src/sql2/udfs/lix_empty_blob.rs +47 -0
  153. package/dist-engine-src/src/sql2/udfs/lix_json.rs +100 -0
  154. package/dist-engine-src/src/sql2/udfs/lix_json_get.rs +99 -0
  155. package/dist-engine-src/src/sql2/udfs/lix_json_get_text.rs +99 -0
  156. package/dist-engine-src/src/sql2/udfs/lix_text_decode.rs +82 -0
  157. package/dist-engine-src/src/sql2/udfs/lix_text_encode.rs +85 -0
  158. package/dist-engine-src/src/sql2/udfs/lix_timestamp.rs +76 -0
  159. package/dist-engine-src/src/sql2/udfs/lix_uuid_v7.rs +76 -0
  160. package/dist-engine-src/src/sql2/udfs/mod.rs +89 -0
  161. package/dist-engine-src/src/sql2/udfs/public_call.rs +211 -0
  162. package/dist-engine-src/src/sql2/version_provider.rs +1202 -0
  163. package/dist-engine-src/src/sql2/version_scope.rs +394 -0
  164. package/dist-engine-src/src/sql2/write_normalization.rs +345 -0
  165. package/dist-engine-src/src/storage/context.rs +356 -0
  166. package/dist-engine-src/src/storage/mod.rs +14 -0
  167. package/dist-engine-src/src/storage/read_scope.rs +88 -0
  168. package/dist-engine-src/src/storage/types.rs +501 -0
  169. package/dist-engine-src/src/storage_bench.rs +4863 -0
  170. package/dist-engine-src/src/test_support.rs +228 -0
  171. package/dist-engine-src/src/tracked_state/by_file_index.rs +98 -0
  172. package/dist-engine-src/src/tracked_state/codec.rs +2085 -0
  173. package/dist-engine-src/src/tracked_state/context.rs +1867 -0
  174. package/dist-engine-src/src/tracked_state/diff.rs +686 -0
  175. package/dist-engine-src/src/tracked_state/materialization.rs +403 -0
  176. package/dist-engine-src/src/tracked_state/materializer.rs +488 -0
  177. package/dist-engine-src/src/tracked_state/merge.rs +492 -0
  178. package/dist-engine-src/src/tracked_state/mod.rs +32 -0
  179. package/dist-engine-src/src/tracked_state/storage.rs +375 -0
  180. package/dist-engine-src/src/tracked_state/tree.rs +3187 -0
  181. package/dist-engine-src/src/tracked_state/types.rs +231 -0
  182. package/dist-engine-src/src/transaction/commit.rs +1484 -0
  183. package/dist-engine-src/src/transaction/context.rs +1548 -0
  184. package/dist-engine-src/src/transaction/live_state_overlay.rs +35 -0
  185. package/dist-engine-src/src/transaction/mod.rs +13 -0
  186. package/dist-engine-src/src/transaction/normalization.rs +890 -0
  187. package/dist-engine-src/src/transaction/prep.rs +37 -0
  188. package/dist-engine-src/src/transaction/schema_resolver.rs +149 -0
  189. package/dist-engine-src/src/transaction/staging.rs +1731 -0
  190. package/dist-engine-src/src/transaction/types.rs +460 -0
  191. package/dist-engine-src/src/transaction/validation.rs +5830 -0
  192. package/dist-engine-src/src/untracked_state/codec.rs +307 -0
  193. package/dist-engine-src/src/untracked_state/context.rs +98 -0
  194. package/dist-engine-src/src/untracked_state/materialization.rs +63 -0
  195. package/dist-engine-src/src/untracked_state/mod.rs +15 -0
  196. package/dist-engine-src/src/untracked_state/storage.rs +396 -0
  197. package/dist-engine-src/src/untracked_state/types.rs +146 -0
  198. package/dist-engine-src/src/version/context.rs +40 -0
  199. package/dist-engine-src/src/version/lifecycle.rs +221 -0
  200. package/dist-engine-src/src/version/mod.rs +13 -0
  201. package/dist-engine-src/src/version/refs.rs +330 -0
  202. package/dist-engine-src/src/version/stage_rows.rs +67 -0
  203. package/dist-engine-src/src/version/types.rs +21 -0
  204. package/dist-engine-src/src/wasm/mod.rs +60 -0
  205. package/package.json +68 -64
@@ -0,0 +1,324 @@
1
+ use crate::entity_identity::EntityIdentity;
2
+ use crate::live_state::MaterializedLiveStateRow;
3
+ use crate::{NullableKeyFilter, GLOBAL_VERSION_ID};
4
+
5
+ /// Validation/storage coordinate for repository facts.
6
+ ///
7
+ /// A domain is the complete scope in which a row identity is meaningful:
8
+ /// version, durability, and file scope. Projection methods on this type are
9
+ /// deliberately named so callers cannot silently erase part of the coordinate.
10
+ #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
11
+ pub(crate) struct Domain {
12
+ version_id: String,
13
+ untracked: bool,
14
+ file_scope: DomainFileScope,
15
+ }
16
+
17
+ impl Domain {
18
+ pub(crate) fn exact_file(
19
+ version_id: impl Into<String>,
20
+ untracked: bool,
21
+ file_id: Option<String>,
22
+ ) -> Self {
23
+ Self {
24
+ version_id: version_id.into(),
25
+ untracked,
26
+ file_scope: DomainFileScope::Exact(file_id),
27
+ }
28
+ }
29
+
30
+ pub(crate) fn any_file(version_id: impl Into<String>, untracked: bool) -> Self {
31
+ Self {
32
+ version_id: version_id.into(),
33
+ untracked,
34
+ file_scope: DomainFileScope::Any,
35
+ }
36
+ }
37
+
38
+ pub(crate) fn schema_catalog(version_id: impl Into<String>, untracked: bool) -> Self {
39
+ Self::any_file(version_id, untracked)
40
+ }
41
+
42
+ pub(crate) fn for_live_row(row: &MaterializedLiveStateRow) -> Self {
43
+ Self::exact_file(row.version_id.clone(), row.untracked, row.file_id.clone())
44
+ }
45
+
46
+ pub(crate) fn schema_catalog_domain(&self) -> Self {
47
+ // Schema definitions are version + durability scoped. They are not
48
+ // owned by a data file, so schema catalog lookup deliberately erases
49
+ // row file scope into `Any`.
50
+ Self::schema_catalog(self.version_id.clone(), self.untracked)
51
+ }
52
+
53
+ pub(crate) fn version_id(&self) -> &str {
54
+ &self.version_id
55
+ }
56
+
57
+ pub(crate) fn untracked(&self) -> bool {
58
+ self.untracked
59
+ }
60
+
61
+ pub(crate) fn fingerprint_component(&self) -> String {
62
+ let file_scope = match &self.file_scope {
63
+ DomainFileScope::Any => "*".to_string(),
64
+ DomainFileScope::Exact(Some(file_id)) => format!("={file_id}"),
65
+ DomainFileScope::Exact(None) => "=".to_string(),
66
+ };
67
+ format!("{}|{}|{}", self.version_id, self.untracked, file_scope)
68
+ }
69
+
70
+ #[cfg(test)]
71
+ pub(crate) fn file_scope(&self) -> &DomainFileScope {
72
+ &self.file_scope
73
+ }
74
+
75
+ pub(crate) fn is_exact_file(&self, file_id: &Option<String>) -> bool {
76
+ matches!(&self.file_scope, DomainFileScope::Exact(exact) if exact == file_id)
77
+ }
78
+
79
+ pub(crate) fn with_untracked(&self, untracked: bool) -> Self {
80
+ Self {
81
+ version_id: self.version_id.clone(),
82
+ untracked,
83
+ file_scope: self.file_scope.clone(),
84
+ }
85
+ }
86
+
87
+ pub(crate) fn with_file_scope(&self, file_scope: DomainFileScope) -> Self {
88
+ Self {
89
+ version_id: self.version_id.clone(),
90
+ untracked: self.untracked,
91
+ file_scope,
92
+ }
93
+ }
94
+
95
+ pub(crate) fn with_exact_file_scope(&self, file_id: Option<String>) -> Self {
96
+ self.with_file_scope(DomainFileScope::Exact(file_id))
97
+ }
98
+
99
+ pub(crate) fn file_filters(&self) -> Vec<NullableKeyFilter<String>> {
100
+ match &self.file_scope {
101
+ DomainFileScope::Any => Vec::new(),
102
+ DomainFileScope::Exact(file_id) => vec![nullable_filter_from_option(file_id)],
103
+ }
104
+ }
105
+
106
+ pub(crate) fn contains(&self, row: &MaterializedLiveStateRow) -> bool {
107
+ row.version_id == self.version_id
108
+ && row.untracked == self.untracked
109
+ && committed_row_is_exact_version_scoped(row, &self.version_id)
110
+ && match &self.file_scope {
111
+ DomainFileScope::Any => true,
112
+ DomainFileScope::Exact(file_id) => row.file_id == *file_id,
113
+ }
114
+ }
115
+
116
+ fn reachable_target_domains(&self) -> Vec<Self> {
117
+ if self.untracked {
118
+ vec![self.with_untracked(false), self.clone()]
119
+ } else {
120
+ vec![self.clone()]
121
+ }
122
+ }
123
+
124
+ fn source_domains_that_can_reach(&self) -> Vec<Self> {
125
+ if self.untracked {
126
+ vec![self.clone()]
127
+ } else {
128
+ vec![self.clone(), self.with_untracked(true)]
129
+ }
130
+ }
131
+
132
+ fn can_reach(&self, target: &Self) -> bool {
133
+ self.version_id == target.version_id
134
+ && self.file_scope == target.file_scope
135
+ && (self.untracked || !target.untracked)
136
+ }
137
+
138
+ pub(crate) fn schema_catalog_domains(&self) -> Vec<Self> {
139
+ self.schema_catalog_domain().reachable_target_domains()
140
+ }
141
+
142
+ pub(crate) fn fk_target_domains(&self) -> Vec<Self> {
143
+ self.reachable_target_domains()
144
+ }
145
+
146
+ pub(crate) fn fk_source_domains_for_target(&self) -> Vec<Self> {
147
+ self.source_domains_that_can_reach()
148
+ }
149
+
150
+ pub(crate) fn file_owner_domains(&self) -> Vec<Self> {
151
+ self.reachable_target_domains()
152
+ }
153
+
154
+ pub(crate) fn directory_parent_domains(&self) -> Vec<Self> {
155
+ self.reachable_target_domains()
156
+ }
157
+
158
+ pub(crate) fn version_descriptor_domains_for_ref_delete(&self) -> Vec<Self> {
159
+ self.source_domains_that_can_reach()
160
+ }
161
+
162
+ pub(crate) fn file_scoped_row_domains_for_file_descriptor_delete(&self) -> Vec<Self> {
163
+ self.source_domains_that_can_reach()
164
+ }
165
+
166
+ pub(crate) fn validation_scope_contains_constraint_domain(&self, target: &Self) -> bool {
167
+ self.can_reach(target)
168
+ }
169
+
170
+ pub(crate) fn tombstone_domain_affects_validation_scope(
171
+ &self,
172
+ validation_scope: &Self,
173
+ ) -> bool {
174
+ self.can_reach(validation_scope)
175
+ }
176
+ }
177
+
178
+ #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
179
+ pub(crate) enum DomainFileScope {
180
+ Any,
181
+ Exact(Option<String>),
182
+ }
183
+
184
+ #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
185
+ pub(crate) struct DomainRowIdentity {
186
+ domain: Domain,
187
+ schema_key: String,
188
+ entity_id: EntityIdentity,
189
+ }
190
+
191
+ impl DomainRowIdentity {
192
+ pub(crate) fn new(
193
+ domain: Domain,
194
+ schema_key: impl Into<String>,
195
+ entity_id: EntityIdentity,
196
+ ) -> Self {
197
+ Self {
198
+ domain,
199
+ schema_key: schema_key.into(),
200
+ entity_id,
201
+ }
202
+ }
203
+
204
+ pub(crate) fn from_live_row(row: &MaterializedLiveStateRow) -> Self {
205
+ Self::new(
206
+ Domain::for_live_row(row),
207
+ row.schema_key.clone(),
208
+ row.entity_id.clone(),
209
+ )
210
+ }
211
+
212
+ pub(crate) fn in_domain(
213
+ domain: Domain,
214
+ schema_key: impl Into<String>,
215
+ entity_id: EntityIdentity,
216
+ ) -> Self {
217
+ Self::new(domain, schema_key, entity_id)
218
+ }
219
+
220
+ #[cfg(test)]
221
+ pub(crate) fn exact(
222
+ version_id: impl Into<String>,
223
+ untracked: bool,
224
+ file_id: Option<String>,
225
+ schema_key: impl Into<String>,
226
+ entity_id: EntityIdentity,
227
+ ) -> Self {
228
+ Self::new(
229
+ Domain::exact_file(version_id, untracked, file_id),
230
+ schema_key,
231
+ entity_id,
232
+ )
233
+ }
234
+
235
+ pub(crate) fn with_domain(&self, domain: Domain) -> Self {
236
+ Self {
237
+ domain,
238
+ schema_key: self.schema_key.clone(),
239
+ entity_id: self.entity_id.clone(),
240
+ }
241
+ }
242
+
243
+ pub(crate) fn domain(&self) -> &Domain {
244
+ &self.domain
245
+ }
246
+
247
+ pub(crate) fn schema_key(&self) -> &str {
248
+ &self.schema_key
249
+ }
250
+
251
+ pub(crate) fn schema_key_owned(&self) -> String {
252
+ self.schema_key.clone()
253
+ }
254
+
255
+ pub(crate) fn entity_id(&self) -> &EntityIdentity {
256
+ &self.entity_id
257
+ }
258
+
259
+ pub(crate) fn entity_id_owned(&self) -> EntityIdentity {
260
+ self.entity_id.clone()
261
+ }
262
+
263
+ pub(crate) fn matches_parts(
264
+ &self,
265
+ domain: &Domain,
266
+ schema_key: &str,
267
+ entity_id: &EntityIdentity,
268
+ ) -> bool {
269
+ &self.domain == domain && self.schema_key == schema_key && &self.entity_id == entity_id
270
+ }
271
+
272
+ pub(crate) fn reachable_target_identities(&self) -> Vec<Self> {
273
+ self.domain
274
+ .fk_target_domains()
275
+ .into_iter()
276
+ .map(|domain| self.with_domain(domain))
277
+ .collect()
278
+ }
279
+
280
+ pub(crate) fn source_identities_that_can_reach(&self) -> Vec<Self> {
281
+ self.domain
282
+ .fk_source_domains_for_target()
283
+ .into_iter()
284
+ .map(|domain| self.with_domain(domain))
285
+ .collect()
286
+ }
287
+ }
288
+
289
+ #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
290
+ pub(crate) struct DomainSchemaIdentity {
291
+ domain: Domain,
292
+ schema_key: String,
293
+ }
294
+
295
+ impl DomainSchemaIdentity {
296
+ pub(crate) fn new(domain: Domain, schema_key: impl Into<String>) -> Self {
297
+ Self {
298
+ domain: domain.schema_catalog_domain(),
299
+ schema_key: schema_key.into(),
300
+ }
301
+ }
302
+
303
+ pub(crate) fn fingerprint_component(&self) -> String {
304
+ format!(
305
+ "{}|{}",
306
+ self.domain.fingerprint_component(),
307
+ self.schema_key
308
+ )
309
+ }
310
+ }
311
+
312
+ pub(crate) fn committed_row_is_exact_version_scoped(
313
+ row: &MaterializedLiveStateRow,
314
+ version_id: &str,
315
+ ) -> bool {
316
+ row.version_id == version_id && row.global == (row.version_id == GLOBAL_VERSION_ID)
317
+ }
318
+
319
+ fn nullable_filter_from_option(value: &Option<String>) -> NullableKeyFilter<String> {
320
+ match value {
321
+ Some(value) => NullableKeyFilter::Value(value.clone()),
322
+ None => NullableKeyFilter::Null,
323
+ }
324
+ }
@@ -0,0 +1,225 @@
1
+ use std::sync::Arc;
2
+
3
+ use crate::binary_cas::BinaryCasContext;
4
+ use crate::catalog::CatalogContext;
5
+ use crate::commit_graph::CommitGraphContext;
6
+ use crate::commit_store::CommitStoreContext;
7
+ use crate::entity_identity::EntityIdentity;
8
+ use crate::init::InitReceipt;
9
+ use crate::live_state::LiveStateContext;
10
+ use crate::live_state::LiveStateRowRequest;
11
+ use crate::session::SessionContext;
12
+ use crate::storage::{StorageContext, StorageWriteSet};
13
+ use crate::tracked_state::TrackedStateContext;
14
+ use crate::untracked_state::UntrackedStateContext;
15
+ use crate::version::{VersionContext, VersionRefReader};
16
+ use crate::GLOBAL_VERSION_ID;
17
+ use crate::{Backend, LixError, NullableKeyFilter};
18
+
19
+ #[derive(Clone)]
20
+ pub struct Engine {
21
+ storage: StorageContext,
22
+ tracked_state: Arc<TrackedStateContext>,
23
+ live_state: Arc<LiveStateContext>,
24
+ version_ctx: Arc<VersionContext>,
25
+ binary_cas: Arc<BinaryCasContext>,
26
+ commit_store: Arc<CommitStoreContext>,
27
+ catalog_context: Arc<CatalogContext>,
28
+ }
29
+
30
+ impl Engine {
31
+ /// Seeds an empty backend with the engine repository bootstrap facts.
32
+ ///
33
+ /// Initialization is a storage lifecycle operation, separate from runtime
34
+ /// construction. Call this before `Engine::new(...)` for a brand-new
35
+ /// backend.
36
+ pub async fn initialize(
37
+ backend: Box<dyn Backend + Send + Sync>,
38
+ ) -> Result<InitReceipt, LixError> {
39
+ let backend: Arc<dyn Backend + Send + Sync> = Arc::from(backend);
40
+ let storage = StorageContext::new(backend);
41
+ let commit_store = CommitStoreContext::new();
42
+
43
+ crate::init::initialize(
44
+ storage,
45
+ &commit_store,
46
+ &TrackedStateContext::new(),
47
+ &UntrackedStateContext::new(),
48
+ )
49
+ .await
50
+ }
51
+
52
+ /// Creates a clean DataFusion-first engine over an initialized backend.
53
+ ///
54
+ /// SessionContext, execution, and transaction overlays are layered below the
55
+ /// instance instead of being hidden behind a legacy boot path.
56
+ pub async fn new(backend: Box<dyn Backend + Send + Sync>) -> Result<Self, LixError> {
57
+ let backend: Arc<dyn Backend + Send + Sync> = Arc::from(backend);
58
+ let storage = StorageContext::new(backend);
59
+
60
+ let tracked_state = Arc::new(TrackedStateContext::new());
61
+ let untracked_state = Arc::new(UntrackedStateContext::new());
62
+ let commit_store = Arc::new(CommitStoreContext::new());
63
+ let commit_graph = CommitGraphContext::new();
64
+ let live_state = Arc::new(LiveStateContext::new(
65
+ tracked_state.as_ref().clone(),
66
+ *untracked_state,
67
+ commit_graph,
68
+ ));
69
+ let version_ctx = Arc::new(VersionContext::new(Arc::clone(&untracked_state)));
70
+ assert_initialized(storage.clone(), live_state.as_ref()).await?;
71
+
72
+ // SessionContext::execute later projects these stable state contexts into one
73
+ // execution-scoped SQL context, optionally wrapped by a transaction
74
+ // overlay for writes.
75
+
76
+ Ok(Self {
77
+ binary_cas: Arc::new(BinaryCasContext::new()),
78
+ commit_store,
79
+ storage,
80
+ tracked_state,
81
+ live_state,
82
+ version_ctx,
83
+ catalog_context: Arc::new(CatalogContext::new()),
84
+ })
85
+ }
86
+
87
+ pub(crate) fn storage(&self) -> StorageContext {
88
+ self.storage.clone()
89
+ }
90
+
91
+ /// Loads the current commit head for a version.
92
+ ///
93
+ /// This is the public engine-level form of the typed `version_ref` context:
94
+ /// callers should not need to know that version heads are represented as
95
+ /// untracked `lix_version_ref` rows in live_state.
96
+ pub async fn load_version_head_commit_id(
97
+ &self,
98
+ version_id: &str,
99
+ ) -> Result<Option<String>, LixError> {
100
+ let mut transaction = self.storage.begin_read_transaction().await?;
101
+ let result = self
102
+ .version_ctx
103
+ .ref_reader(transaction.as_mut())
104
+ .load_head_commit_id(version_id)
105
+ .await;
106
+ match result {
107
+ Ok(result) => {
108
+ transaction.rollback().await?;
109
+ Ok(result)
110
+ }
111
+ Err(error) => {
112
+ let _ = transaction.rollback().await;
113
+ Err(error)
114
+ }
115
+ }
116
+ }
117
+
118
+ pub async fn open_session(
119
+ &self,
120
+ active_version_id: impl Into<String>,
121
+ ) -> Result<SessionContext, LixError> {
122
+ SessionContext::open(
123
+ active_version_id.into(),
124
+ self.storage(),
125
+ Arc::clone(&self.live_state),
126
+ Arc::clone(&self.tracked_state),
127
+ Arc::clone(&self.binary_cas),
128
+ Arc::clone(&self.commit_store),
129
+ Arc::clone(&self.version_ctx),
130
+ Arc::clone(&self.catalog_context),
131
+ )
132
+ .await
133
+ }
134
+
135
+ pub async fn open_workspace_session(&self) -> Result<SessionContext, LixError> {
136
+ SessionContext::open_workspace(
137
+ self.storage(),
138
+ Arc::clone(&self.live_state),
139
+ Arc::clone(&self.tracked_state),
140
+ Arc::clone(&self.binary_cas),
141
+ Arc::clone(&self.commit_store),
142
+ Arc::clone(&self.version_ctx),
143
+ Arc::clone(&self.catalog_context),
144
+ )
145
+ .await
146
+ }
147
+
148
+ /// Materializes the tracked serving projection root for one version from commit_store.
149
+ ///
150
+ /// This is intentionally an engine-level operation: callers should not need
151
+ /// to know which KV namespaces back changelog, commit graph, or tracked
152
+ /// state. The current version head is read from the live-state facade so
153
+ /// materialization uses the same moving-ref visibility as normal execution.
154
+ pub async fn rebuild_tracked_state_for_version(
155
+ &self,
156
+ version_id: &str,
157
+ ) -> Result<(), LixError> {
158
+ let head_commit_id = self
159
+ .load_version_head_commit_id(version_id)
160
+ .await?
161
+ .ok_or_else(|| {
162
+ LixError::version_not_found(
163
+ version_id.to_string(),
164
+ "rebuild_tracked_state_for_version",
165
+ "target",
166
+ )
167
+ })?;
168
+ let storage = self.storage();
169
+ let mut transaction = storage.begin_write_transaction().await?;
170
+ let mut writes = StorageWriteSet::new();
171
+ let materialize_result = self
172
+ .tracked_state
173
+ .materializer(
174
+ transaction.as_mut(),
175
+ &mut writes,
176
+ self.commit_store.as_ref(),
177
+ )
178
+ .materialize_root_at(&head_commit_id)
179
+ .await;
180
+ if let Err(error) = materialize_result {
181
+ let _ = transaction.rollback().await;
182
+ return Err(error);
183
+ }
184
+ if let Err(error) = writes.apply(&mut transaction.as_mut()).await {
185
+ let _ = transaction.rollback().await;
186
+ return Err(error);
187
+ }
188
+ transaction.commit().await
189
+ }
190
+ }
191
+
192
+ async fn assert_initialized(
193
+ storage: StorageContext,
194
+ live_state: &LiveStateContext,
195
+ ) -> Result<(), LixError> {
196
+ let mut transaction = storage.begin_read_transaction().await?;
197
+ let reader = live_state.reader(transaction.as_mut());
198
+ let result = reader
199
+ .load_row(&LiveStateRowRequest {
200
+ schema_key: "lix_key_value".to_string(),
201
+ version_id: GLOBAL_VERSION_ID.to_string(),
202
+ entity_id: EntityIdentity::single("lix_id"),
203
+ file_id: NullableKeyFilter::Null,
204
+ })
205
+ .await;
206
+ let initialized = match result {
207
+ Ok(row) => {
208
+ transaction.rollback().await?;
209
+ row.is_some()
210
+ }
211
+ Err(error) => {
212
+ let _ = transaction.rollback().await;
213
+ return Err(error);
214
+ }
215
+ };
216
+
217
+ if initialized {
218
+ return Ok(());
219
+ }
220
+
221
+ Err(LixError::new(
222
+ "LIX_ERROR_NOT_INITIALIZED",
223
+ "engine backend is not initialized; call Engine::initialize(...) before Engine::new(...)",
224
+ ))
225
+ }