@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,423 @@
1
+ use crate::json_store::store;
2
+ use crate::json_store::types::{
3
+ JsonLoadBatch, JsonLoadRequestRef, JsonProjection, JsonProjectionBatch,
4
+ JsonProjectionLoadRequestRef, JsonRef, JsonValueBatch, JsonWritePlacementRef,
5
+ NormalizedJsonRef,
6
+ };
7
+ use crate::storage::{KvGetGroup, StorageReader, StorageWriteSet};
8
+ use crate::LixError;
9
+ use std::collections::{HashMap, HashSet};
10
+
11
+ const PACK_LOCAL_MAX_JSON_BYTES: usize = 64 * 1024;
12
+
13
+ #[derive(Debug, Clone, Copy)]
14
+ pub(crate) struct JsonStoreContext;
15
+
16
+ impl JsonStoreContext {
17
+ pub(crate) fn new() -> Self {
18
+ Self
19
+ }
20
+
21
+ pub(crate) fn reader<S>(&self, store: S) -> JsonStoreReader<S>
22
+ where
23
+ S: StorageReader,
24
+ {
25
+ JsonStoreReader { store }
26
+ }
27
+
28
+ pub(crate) fn writer(&self) -> JsonStoreWriter {
29
+ JsonStoreWriter::new()
30
+ }
31
+
32
+ pub(crate) async fn load_bytes_many(
33
+ &self,
34
+ store: &mut impl StorageReader,
35
+ request: JsonLoadRequestRef<'_>,
36
+ ) -> Result<JsonLoadBatch, LixError> {
37
+ store::load_json_bytes_many_in_scope(store, request.refs, request.scope)
38
+ .await
39
+ .map(JsonLoadBatch::new)
40
+ }
41
+
42
+ pub(crate) fn commit_pack_get_group(&self, commit_id: &str, pack_id: u32) -> KvGetGroup {
43
+ KvGetGroup {
44
+ namespace: store::JSON_PACK_NAMESPACE.to_string(),
45
+ keys: vec![store::pack_key(commit_id, pack_id)],
46
+ }
47
+ }
48
+
49
+ pub(crate) fn decode_pack_refs(&self, bytes: &[u8]) -> Result<Vec<JsonRef>, LixError> {
50
+ store::decode_json_pack_refs(bytes)
51
+ }
52
+ }
53
+
54
+ pub(crate) struct JsonStoreReader<S> {
55
+ store: S,
56
+ }
57
+
58
+ impl<S> Clone for JsonStoreReader<S>
59
+ where
60
+ S: Clone,
61
+ {
62
+ fn clone(&self) -> Self {
63
+ Self {
64
+ store: self.store.clone(),
65
+ }
66
+ }
67
+ }
68
+
69
+ impl<S> JsonStoreReader<S>
70
+ where
71
+ S: StorageReader,
72
+ {
73
+ pub(crate) async fn load_bytes_many(
74
+ &mut self,
75
+ request: JsonLoadRequestRef<'_>,
76
+ ) -> Result<JsonLoadBatch, LixError> {
77
+ store::load_json_bytes_many_in_scope(&mut self.store, request.refs, request.scope)
78
+ .await
79
+ .map(JsonLoadBatch::new)
80
+ }
81
+
82
+ pub(crate) async fn load_values_many(
83
+ &mut self,
84
+ request: JsonLoadRequestRef<'_>,
85
+ ) -> Result<JsonValueBatch, LixError> {
86
+ let refs = request.refs;
87
+ let values = self
88
+ .load_bytes_many(request)
89
+ .await?
90
+ .into_values()
91
+ .into_iter()
92
+ .enumerate()
93
+ .map(|(index, bytes)| match bytes {
94
+ Some(bytes) => serde_json::from_slice(&bytes).map(Some).map_err(|error| {
95
+ LixError::new(
96
+ "LIX_ERROR_UNKNOWN",
97
+ format!(
98
+ "json ref '{}' is invalid JSON: {error}",
99
+ refs[index].to_hex()
100
+ ),
101
+ )
102
+ }),
103
+ None => Ok(None),
104
+ })
105
+ .collect::<Result<Vec<_>, _>>()?;
106
+ Ok(JsonValueBatch::new(values))
107
+ }
108
+
109
+ pub(crate) async fn load_projections_many(
110
+ &mut self,
111
+ request: JsonProjectionLoadRequestRef<'_>,
112
+ ) -> Result<JsonProjectionBatch, LixError> {
113
+ let values = self
114
+ .load_values_many(JsonLoadRequestRef {
115
+ refs: request.refs,
116
+ scope: request.scope,
117
+ })
118
+ .await?
119
+ .into_values()
120
+ .into_iter()
121
+ .map(|value| {
122
+ value.map(|value| {
123
+ JsonProjection::new(
124
+ request
125
+ .paths
126
+ .iter()
127
+ .map(|path| value.pointer(path.as_str()).cloned())
128
+ .collect(),
129
+ )
130
+ })
131
+ })
132
+ .collect();
133
+ Ok(JsonProjectionBatch::new(values))
134
+ }
135
+ }
136
+
137
+ pub(crate) struct JsonStoreWriter;
138
+
139
+ #[derive(Debug, Clone, Default)]
140
+ pub(crate) struct JsonStageBatchReport {
141
+ pub(crate) refs: Vec<JsonRef>,
142
+ pub(crate) pack_indexes: HashMap<[u8; 32], usize>,
143
+ }
144
+
145
+ impl JsonStoreWriter {
146
+ fn new() -> Self {
147
+ Self
148
+ }
149
+
150
+ pub(crate) fn stage_batch<'a>(
151
+ &mut self,
152
+ writes: &mut StorageWriteSet,
153
+ placement: JsonWritePlacementRef<'a>,
154
+ payloads: impl IntoIterator<Item = NormalizedJsonRef<'a>>,
155
+ ) -> Result<Vec<JsonRef>, LixError> {
156
+ self.stage_batch_report(writes, placement, payloads)
157
+ .map(|report| report.refs)
158
+ }
159
+
160
+ pub(crate) fn stage_batch_report<'a>(
161
+ &mut self,
162
+ writes: &mut StorageWriteSet,
163
+ placement: JsonWritePlacementRef<'a>,
164
+ payloads: impl IntoIterator<Item = NormalizedJsonRef<'a>>,
165
+ ) -> Result<JsonStageBatchReport, LixError> {
166
+ let mut unique_encoded = Vec::new();
167
+ let mut order = Vec::new();
168
+ let mut seen = HashSet::new();
169
+ for payload in payloads {
170
+ let encoded = match payload.trusted_json_ref() {
171
+ Some(json_ref) => store::encode_json_str_with_ref(payload.normalized(), json_ref)?,
172
+ None => store::encode_json_str(payload.normalized())?,
173
+ };
174
+ let hash: [u8; 32] = encoded
175
+ .json_ref
176
+ .as_hash_bytes()
177
+ .try_into()
178
+ .expect("json ref hash is fixed size");
179
+ #[cfg(feature = "storage-benches")]
180
+ crate::storage_bench::record_json_store_stage_bytes(hash);
181
+ order.push(encoded.json_ref);
182
+ if seen.insert(hash) {
183
+ unique_encoded.push(encoded);
184
+ }
185
+ }
186
+
187
+ let pack_local = matches!(placement, JsonWritePlacementRef::CommitPack { .. });
188
+ let mut pack_indexes = HashMap::new();
189
+ if let JsonWritePlacementRef::CommitPack { commit_id, pack_id } = placement {
190
+ let pack_entries = unique_encoded
191
+ .iter()
192
+ .filter(|encoded| encoded.uncompressed_len <= PACK_LOCAL_MAX_JSON_BYTES)
193
+ .collect::<Vec<_>>();
194
+ for (index, encoded) in pack_entries.iter().enumerate() {
195
+ pack_indexes.insert(*encoded.json_ref.as_hash_array(), index);
196
+ }
197
+ if !pack_entries.is_empty() {
198
+ let encoded_pack = store::encode_json_pack(&pack_entries)?;
199
+ writes.put(
200
+ store::JSON_PACK_NAMESPACE,
201
+ store::pack_key(commit_id, pack_id),
202
+ encoded_pack,
203
+ );
204
+ }
205
+ }
206
+
207
+ for encoded in &unique_encoded {
208
+ if pack_local && encoded.uncompressed_len <= PACK_LOCAL_MAX_JSON_BYTES {
209
+ continue;
210
+ }
211
+ writes.put(
212
+ store::JSON_NAMESPACE,
213
+ encoded.json_ref.as_hash_bytes().to_vec(),
214
+ store::encode_direct_json_payload(encoded),
215
+ );
216
+ }
217
+
218
+ Ok(JsonStageBatchReport {
219
+ refs: order,
220
+ pack_indexes,
221
+ })
222
+ }
223
+ }
224
+
225
+ #[cfg(test)]
226
+ mod tests {
227
+ use std::sync::Arc;
228
+
229
+ use super::*;
230
+ use crate::backend::testing::UnitTestBackend;
231
+ use crate::json_store::types::JsonReadScopeRef;
232
+ use crate::storage::StorageContext;
233
+
234
+ #[tokio::test]
235
+ async fn commit_local_batch_writes_pack_without_direct_rows() {
236
+ let storage = StorageContext::new(Arc::new(UnitTestBackend::new()));
237
+ let context = JsonStoreContext::new();
238
+ let first = "{\"value\":\"first\"}";
239
+ let second = "{\"value\":\"second\"}";
240
+
241
+ let mut transaction = storage
242
+ .begin_write_transaction()
243
+ .await
244
+ .expect("transaction should open");
245
+ let mut writes = StorageWriteSet::new();
246
+ context
247
+ .writer()
248
+ .stage_batch(
249
+ &mut writes,
250
+ JsonWritePlacementRef::CommitPack {
251
+ commit_id: "commit-a",
252
+ pack_id: 0,
253
+ },
254
+ [
255
+ NormalizedJsonRef::new(first),
256
+ NormalizedJsonRef::new(second),
257
+ ],
258
+ )
259
+ .expect("json pack should stage");
260
+ writes
261
+ .apply(&mut transaction.as_mut())
262
+ .await
263
+ .expect("json pack should apply");
264
+ transaction
265
+ .commit()
266
+ .await
267
+ .expect("transaction should commit");
268
+
269
+ let refs = [
270
+ JsonRef::for_content(first.as_bytes()),
271
+ JsonRef::for_content(second.as_bytes()),
272
+ ];
273
+ let unknown = context
274
+ .reader(storage.clone())
275
+ .load_bytes_many(JsonLoadRequestRef {
276
+ refs: &refs,
277
+ scope: JsonReadScopeRef::OutOfBand,
278
+ })
279
+ .await
280
+ .expect("unknown load should check direct rows");
281
+ assert_eq!(unknown.into_values(), vec![None, None]);
282
+
283
+ let pack_ids = [0];
284
+ let packed = context
285
+ .reader(storage.clone())
286
+ .load_bytes_many(JsonLoadRequestRef {
287
+ refs: &refs,
288
+ scope: JsonReadScopeRef::CommitPacks {
289
+ commit_id: "commit-a",
290
+ pack_ids: &pack_ids,
291
+ },
292
+ })
293
+ .await
294
+ .expect("packed load should hydrate");
295
+ assert_eq!(
296
+ packed.into_values(),
297
+ vec![
298
+ Some(first.as_bytes().to_vec()),
299
+ Some(second.as_bytes().to_vec())
300
+ ]
301
+ );
302
+ }
303
+
304
+ #[tokio::test]
305
+ async fn commit_local_batch_dedupes_pack_payloads_but_returns_request_order() {
306
+ let storage = StorageContext::new(Arc::new(UnitTestBackend::new()));
307
+ let context = JsonStoreContext::new();
308
+ let first = "{\"value\":\"first\"}";
309
+ let second = "{\"value\":\"second\"}";
310
+
311
+ let mut transaction = storage
312
+ .begin_write_transaction()
313
+ .await
314
+ .expect("transaction should open");
315
+ let mut writes = StorageWriteSet::new();
316
+ let staged_refs = context
317
+ .writer()
318
+ .stage_batch(
319
+ &mut writes,
320
+ JsonWritePlacementRef::CommitPack {
321
+ commit_id: "commit-a",
322
+ pack_id: 0,
323
+ },
324
+ [
325
+ NormalizedJsonRef::new(first),
326
+ NormalizedJsonRef::new(first),
327
+ NormalizedJsonRef::new(second),
328
+ ],
329
+ )
330
+ .expect("json pack should stage");
331
+ writes
332
+ .apply(&mut transaction.as_mut())
333
+ .await
334
+ .expect("json pack should apply");
335
+ transaction
336
+ .commit()
337
+ .await
338
+ .expect("transaction should commit");
339
+
340
+ let first_ref = JsonRef::for_content(first.as_bytes());
341
+ let second_ref = JsonRef::for_content(second.as_bytes());
342
+ assert_eq!(staged_refs, vec![first_ref, first_ref, second_ref]);
343
+
344
+ let refs = [first_ref, second_ref];
345
+ let unknown = context
346
+ .reader(storage.clone())
347
+ .load_bytes_many(JsonLoadRequestRef {
348
+ refs: &refs,
349
+ scope: JsonReadScopeRef::OutOfBand,
350
+ })
351
+ .await
352
+ .expect("unknown load should check direct rows");
353
+ assert_eq!(unknown.into_values(), vec![None, None]);
354
+
355
+ let pack_ids = [0];
356
+ let packed = context
357
+ .reader(storage.clone())
358
+ .load_bytes_many(JsonLoadRequestRef {
359
+ refs: &refs,
360
+ scope: JsonReadScopeRef::CommitPacks {
361
+ commit_id: "commit-a",
362
+ pack_ids: &pack_ids,
363
+ },
364
+ })
365
+ .await
366
+ .expect("packed load should hydrate");
367
+ assert_eq!(
368
+ packed.into_values(),
369
+ vec![
370
+ Some(first.as_bytes().to_vec()),
371
+ Some(second.as_bytes().to_vec())
372
+ ]
373
+ );
374
+ }
375
+
376
+ #[tokio::test]
377
+ async fn commit_local_batch_accepts_trusted_prehashed_payload() {
378
+ let storage = StorageContext::new(Arc::new(UnitTestBackend::new()));
379
+ let context = JsonStoreContext::new();
380
+ let json = "{\"value\":\"prehashed\"}";
381
+ let json_ref = JsonRef::for_content(json.as_bytes());
382
+
383
+ let mut transaction = storage
384
+ .begin_write_transaction()
385
+ .await
386
+ .expect("transaction should open");
387
+ let mut writes = StorageWriteSet::new();
388
+ let refs = context
389
+ .writer()
390
+ .stage_batch(
391
+ &mut writes,
392
+ JsonWritePlacementRef::CommitPack {
393
+ commit_id: "commit-a",
394
+ pack_id: 0,
395
+ },
396
+ [NormalizedJsonRef::trusted_prehashed(json, json_ref)],
397
+ )
398
+ .expect("prehashed json should stage");
399
+ assert_eq!(refs, vec![json_ref]);
400
+ writes
401
+ .apply(&mut transaction.as_mut())
402
+ .await
403
+ .expect("json pack should apply");
404
+ transaction
405
+ .commit()
406
+ .await
407
+ .expect("transaction should commit");
408
+
409
+ let pack_ids = [0];
410
+ let packed = context
411
+ .reader(storage.clone())
412
+ .load_bytes_many(JsonLoadRequestRef {
413
+ refs: &refs,
414
+ scope: JsonReadScopeRef::CommitPacks {
415
+ commit_id: "commit-a",
416
+ pack_ids: &pack_ids,
417
+ },
418
+ })
419
+ .await
420
+ .expect("prehashed payload should hydrate");
421
+ assert_eq!(packed.into_values(), vec![Some(json.as_bytes().to_vec())]);
422
+ }
423
+ }
@@ -0,0 +1,15 @@
1
+ use crate::json_store::types::JsonRef;
2
+ use std::borrow::Cow;
3
+
4
+ #[derive(Debug, Clone, Copy, PartialEq, Eq)]
5
+ pub(crate) enum JsonCodec {
6
+ Raw,
7
+ Zstd,
8
+ }
9
+
10
+ pub(crate) struct EncodedJson<'a> {
11
+ pub(crate) json_ref: JsonRef,
12
+ pub(crate) codec: JsonCodec,
13
+ pub(crate) uncompressed_len: usize,
14
+ pub(crate) data: Cow<'a, [u8]>,
15
+ }
@@ -0,0 +1,12 @@
1
+ pub(crate) mod compression;
2
+ pub(crate) mod context;
3
+ mod encoded;
4
+ pub(crate) mod store;
5
+ pub(crate) mod types;
6
+
7
+ #[allow(unused_imports)]
8
+ pub(crate) use context::{JsonStoreContext, JsonStoreReader, JsonStoreWriter};
9
+ pub(crate) use types::{
10
+ JsonLoadRequestRef, JsonReadScopeRef, JsonRef, JsonWritePlacementRef, NormalizedJson,
11
+ NormalizedJsonRef,
12
+ };