@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,460 @@
1
+ use std::{collections::BTreeSet, fmt, ops::Deref, sync::Arc};
2
+
3
+ use crate::catalog::SchemaPlanId;
4
+ use crate::entity_identity::EntityIdentity;
5
+ use crate::json_store::JsonRef;
6
+ use crate::live_state::MaterializedLiveStateRow;
7
+ use crate::tracked_state::MaterializedTrackedStateRow;
8
+ use crate::untracked_state::MaterializedUntrackedStateRow;
9
+ use crate::LixError;
10
+ use serde::{Deserialize, Deserializer, Serialize, Serializer};
11
+ use serde_json::Value as JsonValue;
12
+
13
+ #[derive(Debug, Clone, PartialEq, Eq)]
14
+ pub(crate) struct TransactionJson {
15
+ value: Arc<JsonValue>,
16
+ normalized: Arc<str>,
17
+ }
18
+
19
+ impl TransactionJson {
20
+ pub(crate) fn from_value(value: JsonValue, context: &str) -> Result<Self, LixError> {
21
+ let normalized: Arc<str> = serde_json::to_string(&value)
22
+ .map_err(|error| {
23
+ LixError::new(
24
+ LixError::CODE_UNKNOWN,
25
+ format!("{context} failed to serialize as normalized JSON: {error}"),
26
+ )
27
+ })?
28
+ .into();
29
+ Ok(Self {
30
+ value: Arc::new(value),
31
+ normalized,
32
+ })
33
+ }
34
+
35
+ pub(crate) fn from_value_unchecked(value: JsonValue) -> Self {
36
+ Self::from_value(value, "transaction JSON")
37
+ .expect("serializing serde_json::Value should not fail")
38
+ }
39
+
40
+ #[cfg(test)]
41
+ pub(crate) fn from_value_for_test(value: JsonValue) -> Self {
42
+ Self::from_value(value, "test transaction JSON").expect("test JSON should normalize")
43
+ }
44
+
45
+ pub(crate) fn from_parts(value: Arc<JsonValue>, normalized: Arc<str>) -> Self {
46
+ Self { value, normalized }
47
+ }
48
+
49
+ pub(crate) fn value(&self) -> &JsonValue {
50
+ self.value.as_ref()
51
+ }
52
+
53
+ pub(crate) fn normalized(&self) -> &str {
54
+ self.normalized.as_ref()
55
+ }
56
+
57
+ pub(crate) fn into_parts(self) -> (Arc<JsonValue>, Arc<str>) {
58
+ (self.value, self.normalized)
59
+ }
60
+ }
61
+
62
+ impl Deref for TransactionJson {
63
+ type Target = JsonValue;
64
+
65
+ fn deref(&self) -> &Self::Target {
66
+ self.value()
67
+ }
68
+ }
69
+
70
+ impl PartialEq<JsonValue> for TransactionJson {
71
+ fn eq(&self, other: &JsonValue) -> bool {
72
+ self.value() == other
73
+ }
74
+ }
75
+
76
+ impl PartialEq<TransactionJson> for JsonValue {
77
+ fn eq(&self, other: &TransactionJson) -> bool {
78
+ self == other.value()
79
+ }
80
+ }
81
+
82
+ impl fmt::Display for TransactionJson {
83
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
84
+ f.write_str(self.normalized())
85
+ }
86
+ }
87
+
88
+ impl Serialize for TransactionJson {
89
+ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
90
+ where
91
+ S: Serializer,
92
+ {
93
+ self.value.serialize(serializer)
94
+ }
95
+ }
96
+
97
+ impl<'de> Deserialize<'de> for TransactionJson {
98
+ fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
99
+ where
100
+ D: Deserializer<'de>,
101
+ {
102
+ let value = JsonValue::deserialize(deserializer)?;
103
+ Self::from_value(value, "transaction JSON").map_err(serde::de::Error::custom)
104
+ }
105
+ }
106
+
107
+ /// State row accepted at the transaction write boundary.
108
+ ///
109
+ /// External SQL/provider code must parse any textual JSON before constructing
110
+ /// this type. The transaction receives `TransactionJson`, applies schema
111
+ /// defaults and identity derivation, then prepares JSON refs in
112
+ /// `PreparedStateRow` without serializing already-normalized JSON again.
113
+ ///
114
+ /// SQL providers stage semantic rows, not final storage rows. INSERT providers
115
+ /// may omit defaulted snapshot fields and leave `entity_id` unset when the
116
+ /// target schema has an `x-lix-primary-key`; transaction normalization applies
117
+ /// schema defaults and derives the final identity. Typed UPDATE providers must
118
+ /// stage full rewritten snapshots after applying column assignments to the
119
+ /// existing row. Raw `lix_state` snapshot updates are replacement writes, not
120
+ /// implicit patches.
121
+ #[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
122
+ pub(crate) struct TransactionWriteRow {
123
+ pub(crate) entity_id: Option<EntityIdentity>,
124
+ pub(crate) schema_key: String,
125
+ pub(crate) file_id: Option<String>,
126
+ pub(crate) snapshot: Option<TransactionJson>,
127
+ pub(crate) metadata: Option<TransactionJson>,
128
+ pub(crate) origin: Option<TransactionWriteOrigin>,
129
+ pub(crate) created_at: Option<String>,
130
+ pub(crate) updated_at: Option<String>,
131
+ pub(crate) global: bool,
132
+ pub(crate) change_id: Option<String>,
133
+ pub(crate) commit_id: Option<String>,
134
+ pub(crate) untracked: bool,
135
+ pub(crate) version_id: String,
136
+ }
137
+
138
+ impl TransactionWriteRow {
139
+ pub(crate) fn schema_scope_version_id(&self) -> &str {
140
+ if self.global {
141
+ crate::GLOBAL_VERSION_ID
142
+ } else {
143
+ self.version_id.as_str()
144
+ }
145
+ }
146
+ }
147
+
148
+ /// User-facing write operation that produced one physical staged row.
149
+ ///
150
+ /// Composite SQL surfaces such as `lix_file` lower one logical row into
151
+ /// multiple state rows. The transaction layer owns final constraint validation,
152
+ /// but error messages should stay in the vocabulary of the logical operation
153
+ /// when the caller did not write the physical state schema directly.
154
+ #[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
155
+ pub(crate) struct TransactionWriteOrigin {
156
+ pub(crate) surface: String,
157
+ pub(crate) operation: TransactionWriteOperation,
158
+ pub(crate) primary_key: Option<LogicalPrimaryKey>,
159
+ }
160
+
161
+ #[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
162
+ pub(crate) enum TransactionWriteOperation {
163
+ Insert,
164
+ Update,
165
+ Delete,
166
+ }
167
+
168
+ #[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
169
+ pub(crate) struct LogicalPrimaryKey {
170
+ pub(crate) columns: Vec<String>,
171
+ pub(crate) values: Vec<String>,
172
+ }
173
+
174
+ /// Incoming file payload paired with transaction write rows.
175
+ #[derive(Debug, Clone, PartialEq, Eq)]
176
+ pub(crate) struct TransactionFileData {
177
+ pub(crate) file_id: String,
178
+ pub(crate) version_id: String,
179
+ pub(crate) untracked: bool,
180
+ pub(crate) data: Vec<u8>,
181
+ }
182
+
183
+ /// Existing canonical change adopted into another version's tracked projection.
184
+ ///
185
+ /// Merges use this path when the source side already owns the canonical
186
+ /// changelog fact. The target commit references that existing change id and
187
+ /// writes a target-version projection row without appending a copied change.
188
+ #[derive(Debug, Clone, PartialEq, Eq)]
189
+ pub(crate) struct TransactionAdoptedChange {
190
+ pub(crate) version_id: String,
191
+ pub(crate) change_id: String,
192
+ pub(crate) projected_row: MaterializedTrackedStateRow,
193
+ }
194
+
195
+ /// One decoded write batch accepted by the transaction boundary.
196
+ #[derive(Debug, Clone, PartialEq, Eq)]
197
+ pub(crate) enum TransactionWrite {
198
+ Rows {
199
+ mode: TransactionWriteMode,
200
+ rows: Vec<TransactionWriteRow>,
201
+ },
202
+ RowsWithFileData {
203
+ mode: TransactionWriteMode,
204
+ rows: Vec<TransactionWriteRow>,
205
+ file_data: Vec<TransactionFileData>,
206
+ count: u64,
207
+ },
208
+ AdoptedChanges {
209
+ changes: Vec<TransactionAdoptedChange>,
210
+ },
211
+ }
212
+
213
+ /// One decoded write batch after semantic normalization and JSON preparation.
214
+ #[derive(Debug, Clone, PartialEq, Eq)]
215
+ pub(crate) enum PreparedTransactionWrite {
216
+ Rows {
217
+ mode: TransactionWriteMode,
218
+ rows: Vec<PreparedStateRow>,
219
+ },
220
+ RowsWithFileData {
221
+ mode: TransactionWriteMode,
222
+ rows: Vec<PreparedStateRow>,
223
+ file_data: Vec<TransactionFileData>,
224
+ count: u64,
225
+ },
226
+ AdoptedChanges {
227
+ rows: Vec<PreparedAdoptedStateRow>,
228
+ },
229
+ }
230
+
231
+ #[derive(Debug, Clone, Copy, PartialEq, Eq)]
232
+ pub(crate) enum TransactionWriteMode {
233
+ Insert,
234
+ Replace,
235
+ }
236
+
237
+ /// Result returned after the transaction accepts a write batch.
238
+ #[derive(Debug, Clone, PartialEq, Eq)]
239
+ pub(crate) struct TransactionWriteOutcome {
240
+ pub(crate) count: u64,
241
+ }
242
+
243
+ #[derive(Debug, Clone, PartialEq, Eq)]
244
+ pub(crate) struct StageJson {
245
+ pub(crate) value: Arc<serde_json::Value>,
246
+ pub(crate) normalized: Arc<str>,
247
+ pub(crate) json_ref: JsonRef,
248
+ }
249
+
250
+ impl StageJson {
251
+ pub(crate) fn materialize(&self) -> String {
252
+ self.normalized.as_ref().to_string()
253
+ }
254
+ }
255
+
256
+ pub(crate) fn stage_json_from_value(
257
+ value: TransactionJson,
258
+ _context: &str,
259
+ ) -> Result<StageJson, LixError> {
260
+ let (value, normalized) = value.into_parts();
261
+ let json_ref = JsonRef::for_content(normalized.as_bytes());
262
+ Ok(StageJson {
263
+ value,
264
+ normalized,
265
+ json_ref,
266
+ })
267
+ }
268
+
269
+ #[derive(Debug, Clone, Default, PartialEq, Eq)]
270
+ pub(crate) struct PreparedRowFacts {
271
+ /// Placeholder for the next cut: row-derived constraint facts will be
272
+ /// computed once during normalization and consumed by validation.
273
+ pub(crate) _sealed: (),
274
+ }
275
+
276
+ /// Prepared state row owned by the transaction write buffer.
277
+ ///
278
+ /// This is the first boundary that owns `StageJson`: JSON has been normalized
279
+ /// and assigned a content-addressed `JsonRef`. Durable placement belongs to the
280
+ /// JSON store at batch staging time, not row preparation time.
281
+ /// Storage owners must receive only the ref-backed row forms derived from this
282
+ /// type.
283
+ #[derive(Debug, Clone, PartialEq, Eq)]
284
+ pub(crate) struct PreparedStateRow {
285
+ pub(crate) schema_plan_id: SchemaPlanId,
286
+ pub(crate) facts: PreparedRowFacts,
287
+ pub(crate) entity_id: EntityIdentity,
288
+ pub(crate) schema_key: String,
289
+ pub(crate) file_id: Option<String>,
290
+ pub(crate) snapshot: Option<StageJson>,
291
+ pub(crate) metadata: Option<StageJson>,
292
+ pub(crate) origin: Option<TransactionWriteOrigin>,
293
+ pub(crate) created_at: String,
294
+ pub(crate) updated_at: String,
295
+ pub(crate) global: bool,
296
+ pub(crate) change_id: Option<String>,
297
+ pub(crate) commit_id: Option<String>,
298
+ pub(crate) untracked: bool,
299
+ pub(crate) version_id: String,
300
+ }
301
+
302
+ /// Transaction-hydrated projection for an adopted canonical change.
303
+ #[derive(Debug, Clone, PartialEq, Eq)]
304
+ pub(crate) struct PreparedAdoptedStateRow {
305
+ pub(crate) schema_plan_id: SchemaPlanId,
306
+ pub(crate) facts: PreparedRowFacts,
307
+ pub(crate) entity_id: EntityIdentity,
308
+ pub(crate) schema_key: String,
309
+ pub(crate) file_id: Option<String>,
310
+ pub(crate) snapshot: Option<StageJson>,
311
+ pub(crate) metadata: Option<StageJson>,
312
+ pub(crate) created_at: String,
313
+ pub(crate) updated_at: String,
314
+ pub(crate) global: bool,
315
+ pub(crate) change_id: String,
316
+ pub(crate) commit_id: String,
317
+ pub(crate) version_id: String,
318
+ }
319
+
320
+ impl From<PreparedStateRow> for MaterializedLiveStateRow {
321
+ fn from(row: PreparedStateRow) -> Self {
322
+ let deleted = row.snapshot.is_none();
323
+ MaterializedLiveStateRow {
324
+ entity_id: row.entity_id,
325
+ schema_key: row.schema_key,
326
+ file_id: row.file_id,
327
+ snapshot_content: row.snapshot.map(|snapshot| snapshot.materialize()),
328
+ metadata: row.metadata.map(|metadata| metadata.materialize()),
329
+ deleted,
330
+ created_at: row.created_at,
331
+ updated_at: row.updated_at,
332
+ global: row.global,
333
+ change_id: row.change_id,
334
+ commit_id: row.commit_id,
335
+ untracked: row.untracked,
336
+ version_id: row.version_id,
337
+ }
338
+ }
339
+ }
340
+
341
+ impl From<&PreparedStateRow> for MaterializedLiveStateRow {
342
+ fn from(row: &PreparedStateRow) -> Self {
343
+ MaterializedLiveStateRow {
344
+ entity_id: row.entity_id.clone(),
345
+ schema_key: row.schema_key.clone(),
346
+ file_id: row.file_id.clone(),
347
+ snapshot_content: row.snapshot.as_ref().map(StageJson::materialize),
348
+ metadata: row.metadata.as_ref().map(StageJson::materialize),
349
+ deleted: row.snapshot.is_none(),
350
+ created_at: row.created_at.clone(),
351
+ updated_at: row.updated_at.clone(),
352
+ global: row.global,
353
+ change_id: row.change_id.clone(),
354
+ commit_id: row.commit_id.clone(),
355
+ untracked: row.untracked,
356
+ version_id: row.version_id.clone(),
357
+ }
358
+ }
359
+ }
360
+
361
+ impl From<PreparedAdoptedStateRow> for MaterializedLiveStateRow {
362
+ fn from(row: PreparedAdoptedStateRow) -> Self {
363
+ let deleted = row.snapshot.is_none();
364
+ MaterializedLiveStateRow {
365
+ entity_id: row.entity_id,
366
+ schema_key: row.schema_key,
367
+ file_id: row.file_id,
368
+ snapshot_content: row.snapshot.map(|snapshot| snapshot.materialize()),
369
+ metadata: row.metadata.map(|metadata| metadata.materialize()),
370
+ deleted,
371
+ created_at: row.created_at,
372
+ updated_at: row.updated_at,
373
+ global: row.global,
374
+ change_id: Some(row.change_id),
375
+ commit_id: Some(row.commit_id),
376
+ untracked: false,
377
+ version_id: row.version_id,
378
+ }
379
+ }
380
+ }
381
+
382
+ impl From<&PreparedAdoptedStateRow> for MaterializedLiveStateRow {
383
+ fn from(row: &PreparedAdoptedStateRow) -> Self {
384
+ MaterializedLiveStateRow {
385
+ entity_id: row.entity_id.clone(),
386
+ schema_key: row.schema_key.clone(),
387
+ file_id: row.file_id.clone(),
388
+ snapshot_content: row.snapshot.as_ref().map(StageJson::materialize),
389
+ metadata: row.metadata.as_ref().map(StageJson::materialize),
390
+ deleted: row.snapshot.is_none(),
391
+ created_at: row.created_at.clone(),
392
+ updated_at: row.updated_at.clone(),
393
+ global: row.global,
394
+ change_id: Some(row.change_id.clone()),
395
+ commit_id: Some(row.commit_id.clone()),
396
+ untracked: false,
397
+ version_id: row.version_id.clone(),
398
+ }
399
+ }
400
+ }
401
+
402
+ impl From<PreparedStateRow> for MaterializedUntrackedStateRow {
403
+ fn from(row: PreparedStateRow) -> Self {
404
+ let deleted = row.snapshot.is_none();
405
+ MaterializedUntrackedStateRow {
406
+ entity_id: row.entity_id,
407
+ schema_key: row.schema_key,
408
+ file_id: row.file_id,
409
+ snapshot_content: row.snapshot.map(|snapshot| snapshot.materialize()),
410
+ metadata: row.metadata.map(|metadata| metadata.materialize()),
411
+ deleted,
412
+ created_at: row.created_at,
413
+ updated_at: row.updated_at,
414
+ global: row.global,
415
+ version_id: row.version_id,
416
+ }
417
+ }
418
+ }
419
+
420
+ /// Transaction-local introduced-change membership accumulated while rows are staged.
421
+ ///
422
+ /// Final commit row materialization owns commit ids, parent heads, and commit
423
+ /// row timestamps. Staging only tracks which hydrated tracked changes the
424
+ /// future commit introduces for a version.
425
+ #[derive(Debug, Clone, Default, PartialEq, Eq)]
426
+ pub(crate) struct StagedCommitMembers {
427
+ pub(crate) commit_id: String,
428
+ pub(crate) commit_change_id: String,
429
+ pub(crate) created_at: String,
430
+ pub(crate) change_ids: BTreeSet<String>,
431
+ pub(crate) allow_empty: bool,
432
+ }
433
+
434
+ impl StagedCommitMembers {
435
+ pub(crate) fn new(commit_id: String, commit_change_id: String, created_at: String) -> Self {
436
+ Self {
437
+ commit_id,
438
+ commit_change_id,
439
+ created_at,
440
+ change_ids: BTreeSet::new(),
441
+ allow_empty: false,
442
+ }
443
+ }
444
+
445
+ pub(crate) fn add_change_id(&mut self, change_id: String) {
446
+ self.change_ids.insert(change_id);
447
+ }
448
+
449
+ pub(crate) fn remove_change_id(&mut self, change_id: &str) {
450
+ self.change_ids.remove(change_id);
451
+ }
452
+
453
+ pub(crate) fn is_empty(&self) -> bool {
454
+ self.change_ids.is_empty()
455
+ }
456
+
457
+ pub(crate) fn allow_empty(&mut self) {
458
+ self.allow_empty = true;
459
+ }
460
+ }