@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,16 +1,115 @@
1
- use std::collections::BTreeSet;
1
+ use std::{collections::BTreeSet, fmt, ops::Deref, sync::Arc};
2
2
 
3
+ use crate::catalog::SchemaPlanId;
3
4
  use crate::entity_identity::EntityIdentity;
4
- use crate::live_state::LiveStateRow;
5
- use crate::tracked_state::TrackedStateRow;
5
+ use crate::json_store::JsonRef;
6
+ use crate::live_state::MaterializedLiveStateRow;
7
+ use crate::tracked_state::MaterializedTrackedStateRow;
6
8
  use crate::untracked_state::MaterializedUntrackedStateRow;
7
- use crate::RowMetadata;
9
+ use crate::LixError;
10
+ use serde::{Deserialize, Deserializer, Serialize, Serializer};
11
+ use serde_json::Value as JsonValue;
8
12
 
9
- /// Incoming state row before transaction hydration.
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.
10
108
  ///
11
- /// Write frontends produce this shape after decoding their own surface. The
12
- /// transaction later assigns generated fields and turns it into a
13
- /// `StagedStateRow`.
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.
14
113
  ///
15
114
  /// SQL providers stage semantic rows, not final storage rows. INSERT providers
16
115
  /// may omit defaulted snapshot fields and leave `entity_id` unset when the
@@ -20,14 +119,13 @@ use crate::RowMetadata;
20
119
  /// existing row. Raw `lix_state` snapshot updates are replacement writes, not
21
120
  /// implicit patches.
22
121
  #[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
23
- pub(crate) struct StageRow {
122
+ pub(crate) struct TransactionWriteRow {
24
123
  pub(crate) entity_id: Option<EntityIdentity>,
25
124
  pub(crate) schema_key: String,
26
125
  pub(crate) file_id: Option<String>,
27
- pub(crate) snapshot_content: Option<String>,
28
- pub(crate) metadata: Option<RowMetadata>,
29
- pub(crate) origin: Option<StageRowOrigin>,
30
- pub(crate) schema_version: String,
126
+ pub(crate) snapshot: Option<TransactionJson>,
127
+ pub(crate) metadata: Option<TransactionJson>,
128
+ pub(crate) origin: Option<TransactionWriteOrigin>,
31
129
  pub(crate) created_at: Option<String>,
32
130
  pub(crate) updated_at: Option<String>,
33
131
  pub(crate) global: bool,
@@ -37,7 +135,7 @@ pub(crate) struct StageRow {
37
135
  pub(crate) version_id: String,
38
136
  }
39
137
 
40
- impl StageRow {
138
+ impl TransactionWriteRow {
41
139
  pub(crate) fn schema_scope_version_id(&self) -> &str {
42
140
  if self.global {
43
141
  crate::GLOBAL_VERSION_ID
@@ -54,14 +152,14 @@ impl StageRow {
54
152
  /// but error messages should stay in the vocabulary of the logical operation
55
153
  /// when the caller did not write the physical state schema directly.
56
154
  #[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
57
- pub(crate) struct StageRowOrigin {
155
+ pub(crate) struct TransactionWriteOrigin {
58
156
  pub(crate) surface: String,
59
- pub(crate) operation: StageWriteOperation,
157
+ pub(crate) operation: TransactionWriteOperation,
60
158
  pub(crate) primary_key: Option<LogicalPrimaryKey>,
61
159
  }
62
160
 
63
161
  #[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
64
- pub(crate) enum StageWriteOperation {
162
+ pub(crate) enum TransactionWriteOperation {
65
163
  Insert,
66
164
  Update,
67
165
  Delete,
@@ -73,9 +171,9 @@ pub(crate) struct LogicalPrimaryKey {
73
171
  pub(crate) values: Vec<String>,
74
172
  }
75
173
 
76
- /// Incoming file payload paired with staged filesystem rows.
174
+ /// Incoming file payload paired with transaction write rows.
77
175
  #[derive(Debug, Clone, PartialEq, Eq)]
78
- pub(crate) struct StageFileData {
176
+ pub(crate) struct TransactionFileData {
79
177
  pub(crate) file_id: String,
80
178
  pub(crate) version_id: String,
81
179
  pub(crate) untracked: bool,
@@ -88,55 +186,110 @@ pub(crate) struct StageFileData {
88
186
  /// changelog fact. The target commit references that existing change id and
89
187
  /// writes a target-version projection row without appending a copied change.
90
188
  #[derive(Debug, Clone, PartialEq, Eq)]
91
- pub(crate) struct StageAdoptedChange {
189
+ pub(crate) struct TransactionAdoptedChange {
92
190
  pub(crate) version_id: String,
93
191
  pub(crate) change_id: String,
94
- pub(crate) projected_row: TrackedStateRow,
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
+ },
95
211
  }
96
212
 
97
- /// One decoded write batch before transaction hydration.
213
+ /// One decoded write batch after semantic normalization and JSON preparation.
98
214
  #[derive(Debug, Clone, PartialEq, Eq)]
99
- pub(crate) enum StageWrite {
215
+ pub(crate) enum PreparedTransactionWrite {
100
216
  Rows {
101
- mode: StageWriteMode,
102
- rows: Vec<StageRow>,
217
+ mode: TransactionWriteMode,
218
+ rows: Vec<PreparedStateRow>,
103
219
  },
104
220
  RowsWithFileData {
105
- mode: StageWriteMode,
106
- rows: Vec<StageRow>,
107
- file_data: Vec<StageFileData>,
221
+ mode: TransactionWriteMode,
222
+ rows: Vec<PreparedStateRow>,
223
+ file_data: Vec<TransactionFileData>,
108
224
  count: u64,
109
225
  },
110
226
  AdoptedChanges {
111
- changes: Vec<StageAdoptedChange>,
227
+ rows: Vec<PreparedAdoptedStateRow>,
112
228
  },
113
229
  }
114
230
 
115
231
  #[derive(Debug, Clone, Copy, PartialEq, Eq)]
116
- pub(crate) enum StageWriteMode {
232
+ pub(crate) enum TransactionWriteMode {
117
233
  Insert,
118
234
  Replace,
119
235
  }
120
236
 
121
- /// Result returned after staging a decoded write batch.
237
+ /// Result returned after the transaction accepts a write batch.
122
238
  #[derive(Debug, Clone, PartialEq, Eq)]
123
- pub(crate) struct StageWriteOutcome {
239
+ pub(crate) struct TransactionWriteOutcome {
124
240
  pub(crate) count: u64,
125
241
  }
126
242
 
127
- /// Transaction-hydrated state row.
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.
128
277
  ///
129
- /// This is the row form owned by a write transaction after generated fields
130
- /// have been assigned but before the rows are durably flushed.
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.
131
283
  #[derive(Debug, Clone, PartialEq, Eq)]
132
- pub(crate) struct StagedStateRow {
284
+ pub(crate) struct PreparedStateRow {
285
+ pub(crate) schema_plan_id: SchemaPlanId,
286
+ pub(crate) facts: PreparedRowFacts,
133
287
  pub(crate) entity_id: EntityIdentity,
134
288
  pub(crate) schema_key: String,
135
289
  pub(crate) file_id: Option<String>,
136
- pub(crate) snapshot_content: Option<String>,
137
- pub(crate) metadata: Option<RowMetadata>,
138
- pub(crate) origin: Option<StageRowOrigin>,
139
- pub(crate) schema_version: String,
290
+ pub(crate) snapshot: Option<StageJson>,
291
+ pub(crate) metadata: Option<StageJson>,
292
+ pub(crate) origin: Option<TransactionWriteOrigin>,
140
293
  pub(crate) created_at: String,
141
294
  pub(crate) updated_at: String,
142
295
  pub(crate) global: bool,
@@ -146,25 +299,16 @@ pub(crate) struct StagedStateRow {
146
299
  pub(crate) version_id: String,
147
300
  }
148
301
 
149
- impl StagedStateRow {
150
- pub(crate) fn schema_scope_version_id(&self) -> &str {
151
- if self.global {
152
- crate::GLOBAL_VERSION_ID
153
- } else {
154
- self.version_id.as_str()
155
- }
156
- }
157
- }
158
-
159
302
  /// Transaction-hydrated projection for an adopted canonical change.
160
303
  #[derive(Debug, Clone, PartialEq, Eq)]
161
- pub(crate) struct StagedAdoptedStateRow {
304
+ pub(crate) struct PreparedAdoptedStateRow {
305
+ pub(crate) schema_plan_id: SchemaPlanId,
306
+ pub(crate) facts: PreparedRowFacts,
162
307
  pub(crate) entity_id: EntityIdentity,
163
308
  pub(crate) schema_key: String,
164
309
  pub(crate) file_id: Option<String>,
165
- pub(crate) snapshot_content: Option<String>,
166
- pub(crate) metadata: Option<RowMetadata>,
167
- pub(crate) schema_version: String,
310
+ pub(crate) snapshot: Option<StageJson>,
311
+ pub(crate) metadata: Option<StageJson>,
168
312
  pub(crate) created_at: String,
169
313
  pub(crate) updated_at: String,
170
314
  pub(crate) global: bool,
@@ -173,25 +317,16 @@ pub(crate) struct StagedAdoptedStateRow {
173
317
  pub(crate) version_id: String,
174
318
  }
175
319
 
176
- impl StagedAdoptedStateRow {
177
- pub(crate) fn schema_scope_version_id(&self) -> &str {
178
- if self.global {
179
- crate::GLOBAL_VERSION_ID
180
- } else {
181
- self.version_id.as_str()
182
- }
183
- }
184
- }
185
-
186
- impl From<StagedStateRow> for LiveStateRow {
187
- fn from(row: StagedStateRow) -> Self {
188
- LiveStateRow {
320
+ impl From<PreparedStateRow> for MaterializedLiveStateRow {
321
+ fn from(row: PreparedStateRow) -> Self {
322
+ let deleted = row.snapshot.is_none();
323
+ MaterializedLiveStateRow {
189
324
  entity_id: row.entity_id,
190
325
  schema_key: row.schema_key,
191
326
  file_id: row.file_id,
192
- snapshot_content: row.snapshot_content,
193
- metadata: row.metadata,
194
- schema_version: row.schema_version,
327
+ snapshot_content: row.snapshot.map(|snapshot| snapshot.materialize()),
328
+ metadata: row.metadata.map(|metadata| metadata.materialize()),
329
+ deleted,
195
330
  created_at: row.created_at,
196
331
  updated_at: row.updated_at,
197
332
  global: row.global,
@@ -203,15 +338,15 @@ impl From<StagedStateRow> for LiveStateRow {
203
338
  }
204
339
  }
205
340
 
206
- impl From<&StagedStateRow> for LiveStateRow {
207
- fn from(row: &StagedStateRow) -> Self {
208
- LiveStateRow {
341
+ impl From<&PreparedStateRow> for MaterializedLiveStateRow {
342
+ fn from(row: &PreparedStateRow) -> Self {
343
+ MaterializedLiveStateRow {
209
344
  entity_id: row.entity_id.clone(),
210
345
  schema_key: row.schema_key.clone(),
211
346
  file_id: row.file_id.clone(),
212
- snapshot_content: row.snapshot_content.clone(),
213
- metadata: row.metadata.clone(),
214
- schema_version: row.schema_version.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(),
215
350
  created_at: row.created_at.clone(),
216
351
  updated_at: row.updated_at.clone(),
217
352
  global: row.global,
@@ -223,15 +358,16 @@ impl From<&StagedStateRow> for LiveStateRow {
223
358
  }
224
359
  }
225
360
 
226
- impl From<StagedAdoptedStateRow> for LiveStateRow {
227
- fn from(row: StagedAdoptedStateRow) -> Self {
228
- LiveStateRow {
361
+ impl From<PreparedAdoptedStateRow> for MaterializedLiveStateRow {
362
+ fn from(row: PreparedAdoptedStateRow) -> Self {
363
+ let deleted = row.snapshot.is_none();
364
+ MaterializedLiveStateRow {
229
365
  entity_id: row.entity_id,
230
366
  schema_key: row.schema_key,
231
367
  file_id: row.file_id,
232
- snapshot_content: row.snapshot_content,
233
- metadata: row.metadata,
234
- schema_version: row.schema_version,
368
+ snapshot_content: row.snapshot.map(|snapshot| snapshot.materialize()),
369
+ metadata: row.metadata.map(|metadata| metadata.materialize()),
370
+ deleted,
235
371
  created_at: row.created_at,
236
372
  updated_at: row.updated_at,
237
373
  global: row.global,
@@ -243,36 +379,15 @@ impl From<StagedAdoptedStateRow> for LiveStateRow {
243
379
  }
244
380
  }
245
381
 
246
- impl From<&StagedAdoptedStateRow> for LiveStateRow {
247
- fn from(row: &StagedAdoptedStateRow) -> Self {
248
- LiveStateRow {
249
- entity_id: row.entity_id.clone(),
250
- schema_key: row.schema_key.clone(),
251
- file_id: row.file_id.clone(),
252
- snapshot_content: row.snapshot_content.clone(),
253
- metadata: row.metadata.clone(),
254
- schema_version: row.schema_version.clone(),
255
- created_at: row.created_at.clone(),
256
- updated_at: row.updated_at.clone(),
257
- global: row.global,
258
- change_id: Some(row.change_id.clone()),
259
- commit_id: Some(row.commit_id.clone()),
260
- untracked: false,
261
- version_id: row.version_id.clone(),
262
- }
263
- }
264
- }
265
-
266
- impl From<&StagedAdoptedStateRow> for StagedStateRow {
267
- fn from(row: &StagedAdoptedStateRow) -> Self {
268
- StagedStateRow {
382
+ impl From<&PreparedAdoptedStateRow> for MaterializedLiveStateRow {
383
+ fn from(row: &PreparedAdoptedStateRow) -> Self {
384
+ MaterializedLiveStateRow {
269
385
  entity_id: row.entity_id.clone(),
270
386
  schema_key: row.schema_key.clone(),
271
387
  file_id: row.file_id.clone(),
272
- snapshot_content: row.snapshot_content.clone(),
273
- metadata: row.metadata.clone(),
274
- origin: None,
275
- schema_version: row.schema_version.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(),
276
391
  created_at: row.created_at.clone(),
277
392
  updated_at: row.updated_at.clone(),
278
393
  global: row.global,
@@ -284,15 +399,16 @@ impl From<&StagedAdoptedStateRow> for StagedStateRow {
284
399
  }
285
400
  }
286
401
 
287
- impl From<StagedStateRow> for MaterializedUntrackedStateRow {
288
- fn from(row: StagedStateRow) -> Self {
402
+ impl From<PreparedStateRow> for MaterializedUntrackedStateRow {
403
+ fn from(row: PreparedStateRow) -> Self {
404
+ let deleted = row.snapshot.is_none();
289
405
  MaterializedUntrackedStateRow {
290
406
  entity_id: row.entity_id,
291
407
  schema_key: row.schema_key,
292
408
  file_id: row.file_id,
293
- snapshot_content: row.snapshot_content,
294
- metadata: row.metadata,
295
- schema_version: row.schema_version,
409
+ snapshot_content: row.snapshot.map(|snapshot| snapshot.materialize()),
410
+ metadata: row.metadata.map(|metadata| metadata.materialize()),
411
+ deleted,
296
412
  created_at: row.created_at,
297
413
  updated_at: row.updated_at,
298
414
  global: row.global,
@@ -310,23 +426,16 @@ impl From<StagedStateRow> for MaterializedUntrackedStateRow {
310
426
  pub(crate) struct StagedCommitMembers {
311
427
  pub(crate) commit_id: String,
312
428
  pub(crate) commit_change_id: String,
313
- pub(crate) change_set_id: String,
314
429
  pub(crate) created_at: String,
315
430
  pub(crate) change_ids: BTreeSet<String>,
316
431
  pub(crate) allow_empty: bool,
317
432
  }
318
433
 
319
434
  impl StagedCommitMembers {
320
- pub(crate) fn new(
321
- commit_id: String,
322
- commit_change_id: String,
323
- change_set_id: String,
324
- created_at: String,
325
- ) -> Self {
435
+ pub(crate) fn new(commit_id: String, commit_change_id: String, created_at: String) -> Self {
326
436
  Self {
327
437
  commit_id,
328
438
  commit_change_id,
329
- change_set_id,
330
439
  created_at,
331
440
  change_ids: BTreeSet::new(),
332
441
  allow_empty: false,