@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,375 @@
1
+ use std::collections::HashMap;
2
+
3
+ use crate::json_store::JsonStoreContext;
4
+ use crate::storage::{KvGetGroup, KvGetRequest, StorageReader, StorageWriteSet};
5
+ use crate::tracked_state::codec::PendingChunkWrite;
6
+ use crate::tracked_state::types::{
7
+ TrackedStateDeltaEntry, TrackedStateDeltaRef, TrackedStateRootId, TRACKED_STATE_HASH_BYTES,
8
+ };
9
+ use crate::LixError;
10
+
11
+ pub(crate) const TRACKED_STATE_CHUNK_NAMESPACE: &'static str = "tracked_state.tree.chunk";
12
+ pub(crate) const TRACKED_STATE_ROOT_NAMESPACE: &'static str = "tracked_state.tree.root";
13
+ pub(crate) const TRACKED_STATE_BY_FILE_ROOT_NAMESPACE: &'static str =
14
+ "tracked_state.tree.root.by_file";
15
+ pub(crate) const TRACKED_STATE_DELTA_PACK_NAMESPACE: &'static str = "tracked_state.delta_pack";
16
+
17
+ async fn get_one(
18
+ store: &mut (impl StorageReader + ?Sized),
19
+ namespace: &str,
20
+ key: Vec<u8>,
21
+ ) -> Result<Option<Vec<u8>>, LixError> {
22
+ Ok(store
23
+ .get_values(KvGetRequest {
24
+ groups: vec![KvGetGroup {
25
+ namespace: namespace.to_string(),
26
+ keys: vec![key],
27
+ }],
28
+ })
29
+ .await?
30
+ .groups
31
+ .into_iter()
32
+ .next()
33
+ .and_then(|group| group.single_value_owned()))
34
+ }
35
+
36
+ pub(crate) async fn load_root(
37
+ store: &mut (impl StorageReader + ?Sized),
38
+ commit_id: &str,
39
+ ) -> Result<Option<TrackedStateRootId>, LixError> {
40
+ let Some(bytes) = get_one(
41
+ store,
42
+ TRACKED_STATE_ROOT_NAMESPACE,
43
+ commit_id.as_bytes().to_vec(),
44
+ )
45
+ .await?
46
+ else {
47
+ return Ok(None);
48
+ };
49
+ TrackedStateRootId::from_slice(&bytes).map(Some)
50
+ }
51
+
52
+ pub(crate) fn stage_root(
53
+ writes: &mut StorageWriteSet,
54
+ commit_id: &str,
55
+ root_id: &TrackedStateRootId,
56
+ ) {
57
+ writes.put(
58
+ TRACKED_STATE_ROOT_NAMESPACE,
59
+ commit_id.as_bytes().to_vec(),
60
+ root_id.as_bytes().to_vec(),
61
+ );
62
+ }
63
+
64
+ pub(crate) async fn load_by_file_root(
65
+ store: &mut (impl StorageReader + ?Sized),
66
+ commit_id: &str,
67
+ ) -> Result<Option<TrackedStateRootId>, LixError> {
68
+ let Some(bytes) = get_one(
69
+ store,
70
+ TRACKED_STATE_BY_FILE_ROOT_NAMESPACE,
71
+ commit_id.as_bytes().to_vec(),
72
+ )
73
+ .await?
74
+ else {
75
+ return Ok(None);
76
+ };
77
+ TrackedStateRootId::from_slice(&bytes).map(Some)
78
+ }
79
+
80
+ pub(crate) fn stage_by_file_root(
81
+ writes: &mut StorageWriteSet,
82
+ commit_id: &str,
83
+ root_id: &TrackedStateRootId,
84
+ ) {
85
+ writes.put(
86
+ TRACKED_STATE_BY_FILE_ROOT_NAMESPACE,
87
+ commit_id.as_bytes().to_vec(),
88
+ root_id.as_bytes().to_vec(),
89
+ );
90
+ }
91
+
92
+ pub(crate) async fn load_delta_pack(
93
+ store: &mut (impl StorageReader + ?Sized),
94
+ commit_id: &str,
95
+ ) -> Result<Option<Vec<TrackedStateDeltaEntry>>, LixError> {
96
+ let json_store = JsonStoreContext::new();
97
+ let result = store
98
+ .get_values(KvGetRequest {
99
+ groups: vec![
100
+ KvGetGroup {
101
+ namespace: TRACKED_STATE_DELTA_PACK_NAMESPACE.to_string(),
102
+ keys: vec![commit_id.as_bytes().to_vec()],
103
+ },
104
+ json_store.commit_pack_get_group(commit_id, 0),
105
+ ],
106
+ })
107
+ .await?;
108
+ let mut groups = result.groups.into_iter();
109
+ let delta_group = groups.next().ok_or_else(|| {
110
+ LixError::new(
111
+ LixError::CODE_INTERNAL_ERROR,
112
+ "tracked-state delta pack load returned no delta result group",
113
+ )
114
+ })?;
115
+ let json_pack_group = groups.next().ok_or_else(|| {
116
+ LixError::new(
117
+ LixError::CODE_INTERNAL_ERROR,
118
+ "tracked-state delta pack load returned no JSON pack result group",
119
+ )
120
+ })?;
121
+ let Some(bytes) = delta_group.single_value_owned() else {
122
+ return Ok(None);
123
+ };
124
+ let pack_refs = if crate::tracked_state::codec::delta_pack_uses_json_pack_indexes(&bytes)? {
125
+ json_pack_group
126
+ .single_value_owned()
127
+ .map(|bytes| json_store.decode_pack_refs(&bytes))
128
+ .transpose()?
129
+ } else {
130
+ None
131
+ };
132
+ let (stored_commit_id, entries) =
133
+ crate::tracked_state::codec::decode_delta_pack(&bytes, pack_refs.as_deref())?;
134
+ if stored_commit_id != commit_id {
135
+ return Err(LixError::new(
136
+ LixError::CODE_INTERNAL_ERROR,
137
+ format!(
138
+ "tracked-state delta pack identity mismatch: expected '{commit_id}', got '{stored_commit_id}'"
139
+ ),
140
+ ));
141
+ }
142
+ Ok(Some(entries))
143
+ }
144
+
145
+ pub(crate) async fn delta_pack_exists(
146
+ store: &mut (impl StorageReader + ?Sized),
147
+ commit_id: &str,
148
+ ) -> Result<bool, LixError> {
149
+ let result = store
150
+ .exists_many(KvGetRequest {
151
+ groups: vec![KvGetGroup {
152
+ namespace: TRACKED_STATE_DELTA_PACK_NAMESPACE.to_string(),
153
+ keys: vec![commit_id.as_bytes().to_vec()],
154
+ }],
155
+ })
156
+ .await?;
157
+ let group = result.groups.into_iter().next().ok_or_else(|| {
158
+ LixError::new(
159
+ LixError::CODE_INTERNAL_ERROR,
160
+ "tracked-state delta pack existence check returned no result group",
161
+ )
162
+ })?;
163
+ group.exists.into_iter().next().ok_or_else(|| {
164
+ LixError::new(
165
+ LixError::CODE_INTERNAL_ERROR,
166
+ "tracked-state delta pack existence check returned no result",
167
+ )
168
+ })
169
+ }
170
+
171
+ pub(crate) fn stage_delta_pack_refs(
172
+ writes: &mut StorageWriteSet,
173
+ commit_id: &str,
174
+ deltas: &[TrackedStateDeltaRef<'_>],
175
+ ) -> Result<(), LixError> {
176
+ writes.put(
177
+ TRACKED_STATE_DELTA_PACK_NAMESPACE,
178
+ commit_id.as_bytes().to_vec(),
179
+ crate::tracked_state::codec::encode_delta_pack_refs(commit_id, deltas)?,
180
+ );
181
+ Ok(())
182
+ }
183
+
184
+ pub(crate) struct DeltaJsonPackIndexesRef<'a> {
185
+ pub(crate) commit_id: &'a str,
186
+ pub(crate) pack_id: u32,
187
+ pub(crate) indexes: &'a std::collections::HashMap<[u8; TRACKED_STATE_HASH_BYTES], usize>,
188
+ }
189
+
190
+ pub(crate) fn stage_delta_pack_refs_with_json_pack_indexes(
191
+ writes: &mut StorageWriteSet,
192
+ commit_id: &str,
193
+ deltas: &[TrackedStateDeltaRef<'_>],
194
+ json_pack_indexes: DeltaJsonPackIndexesRef<'_>,
195
+ ) -> Result<(), LixError> {
196
+ if json_pack_indexes.commit_id != commit_id {
197
+ return Err(LixError::new(
198
+ LixError::CODE_INTERNAL_ERROR,
199
+ format!(
200
+ "tracked-state delta JSON pack indexes for '{}' cannot encode delta pack '{}'",
201
+ json_pack_indexes.commit_id, commit_id
202
+ ),
203
+ ));
204
+ }
205
+ if json_pack_indexes.pack_id != 0 {
206
+ return Err(LixError::new(
207
+ LixError::CODE_INTERNAL_ERROR,
208
+ format!(
209
+ "tracked-state delta JSON pack indexes only support pack 0, got pack {}",
210
+ json_pack_indexes.pack_id
211
+ ),
212
+ ));
213
+ }
214
+ if json_pack_indexes.indexes.is_empty() {
215
+ return stage_delta_pack_refs(writes, commit_id, deltas);
216
+ }
217
+ writes.put(
218
+ TRACKED_STATE_DELTA_PACK_NAMESPACE,
219
+ commit_id.as_bytes().to_vec(),
220
+ crate::tracked_state::codec::encode_delta_pack_refs_with_json_pack_indexes(
221
+ commit_id,
222
+ deltas,
223
+ Some(json_pack_indexes.indexes),
224
+ )?,
225
+ );
226
+ Ok(())
227
+ }
228
+
229
+ pub(crate) async fn read_chunk(
230
+ store: &mut (impl StorageReader + ?Sized),
231
+ hash: &[u8; TRACKED_STATE_HASH_BYTES],
232
+ ) -> Result<Option<Vec<u8>>, LixError> {
233
+ get_one(store, TRACKED_STATE_CHUNK_NAMESPACE, hash.to_vec()).await
234
+ }
235
+
236
+ pub(crate) fn verify_chunk_hash(
237
+ expected: &[u8; TRACKED_STATE_HASH_BYTES],
238
+ bytes: &[u8],
239
+ ) -> Result<(), LixError> {
240
+ let actual = crate::tracked_state::codec::hash_bytes(bytes);
241
+ if &actual != expected {
242
+ return Err(LixError::new(
243
+ "LIX_ERROR_UNKNOWN",
244
+ "tracked-state chunk hash mismatch",
245
+ ));
246
+ }
247
+ Ok(())
248
+ }
249
+
250
+ pub(crate) fn stage_chunks(writes: &mut StorageWriteSet, chunks: &[PendingChunkWrite]) {
251
+ for chunk in chunks {
252
+ writes.put(
253
+ TRACKED_STATE_CHUNK_NAMESPACE,
254
+ chunk.hash.to_vec(),
255
+ chunk.data.clone(),
256
+ );
257
+ }
258
+ }
259
+
260
+ #[allow(dead_code)]
261
+ #[derive(Debug, Default)]
262
+ pub(crate) struct TrackedStateChunkOverlay {
263
+ chunks: HashMap<[u8; TRACKED_STATE_HASH_BYTES], Vec<u8>>,
264
+ }
265
+
266
+ impl TrackedStateChunkOverlay {
267
+ pub(crate) fn new() -> Self {
268
+ Self::default()
269
+ }
270
+
271
+ pub(crate) async fn read_chunk(
272
+ &self,
273
+ store: &mut (impl StorageReader + ?Sized),
274
+ hash: &[u8; TRACKED_STATE_HASH_BYTES],
275
+ ) -> Result<Option<Vec<u8>>, LixError> {
276
+ if let Some(bytes) = self.chunks.get(hash) {
277
+ return Ok(Some(bytes.clone()));
278
+ }
279
+ read_chunk(store, hash).await
280
+ }
281
+
282
+ pub(crate) fn stage_chunks(
283
+ &mut self,
284
+ writes: &mut StorageWriteSet,
285
+ chunks: &[PendingChunkWrite],
286
+ ) {
287
+ for chunk in chunks {
288
+ self.chunks.insert(chunk.hash, chunk.data.clone());
289
+ }
290
+ stage_chunks(writes, chunks);
291
+ }
292
+ }
293
+
294
+ #[cfg(test)]
295
+ mod tests {
296
+ use std::fs;
297
+ use std::path::{Path, PathBuf};
298
+
299
+ #[test]
300
+ fn production_tracked_state_sources_do_not_call_storage_batch_writer() {
301
+ let tracked_state_dir = Path::new(env!("CARGO_MANIFEST_DIR")).join("src/tracked_state");
302
+ let forbidden = ["write", "kv", "batch"].join("_");
303
+
304
+ for path in rust_sources(&tracked_state_dir) {
305
+ let source =
306
+ fs::read_to_string(&path).expect("tracked_state source should be readable");
307
+ for (line_number, line) in production_lines(&source) {
308
+ assert!(
309
+ !line.contains(&forbidden),
310
+ "production tracked_state source must stage into StorageWriteSet instead of calling {forbidden}: {}:{}",
311
+ path.display(),
312
+ line_number
313
+ );
314
+ }
315
+ }
316
+ }
317
+
318
+ fn rust_sources(dir: &Path) -> Vec<PathBuf> {
319
+ let mut sources = Vec::new();
320
+ for entry in fs::read_dir(dir).expect("tracked_state source dir should be readable") {
321
+ let path = entry
322
+ .expect("tracked_state source entry should be readable")
323
+ .path();
324
+ if path.is_dir() {
325
+ sources.extend(rust_sources(&path));
326
+ } else if path.extension().and_then(|extension| extension.to_str()) == Some("rs") {
327
+ sources.push(path);
328
+ }
329
+ }
330
+ sources
331
+ }
332
+
333
+ fn production_lines(source: &str) -> Vec<(usize, &str)> {
334
+ let mut lines = Vec::new();
335
+ let mut skipping_cfg_test_item = false;
336
+ let mut pending_cfg_test = false;
337
+ let mut item_started = false;
338
+ let mut brace_depth = 0i32;
339
+
340
+ for (index, line) in source.lines().enumerate() {
341
+ let trimmed = line.trim();
342
+ if trimmed == "#[cfg(test)]" {
343
+ pending_cfg_test = true;
344
+ continue;
345
+ }
346
+
347
+ if pending_cfg_test || skipping_cfg_test_item {
348
+ if pending_cfg_test && !item_started && trimmed.ends_with(';') {
349
+ pending_cfg_test = false;
350
+ continue;
351
+ }
352
+ let opens = line.matches('{').count() as i32;
353
+ let closes = line.matches('}').count() as i32;
354
+ if opens > 0 {
355
+ item_started = true;
356
+ skipping_cfg_test_item = true;
357
+ }
358
+ if item_started {
359
+ brace_depth += opens - closes;
360
+ if brace_depth <= 0 {
361
+ pending_cfg_test = false;
362
+ skipping_cfg_test_item = false;
363
+ item_started = false;
364
+ brace_depth = 0;
365
+ }
366
+ }
367
+ continue;
368
+ }
369
+
370
+ lines.push((index + 1, line));
371
+ }
372
+
373
+ lines
374
+ }
375
+ }