@lix-js/sdk 0.6.0-preview.4 → 0.6.0

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 (223) hide show
  1. package/README.md +76 -4
  2. package/dist/errors.d.ts +7 -0
  3. package/dist/errors.js +19 -0
  4. package/dist/index.d.ts +4 -5
  5. package/dist/index.js +3 -3
  6. package/dist/native.d.ts +1 -0
  7. package/dist/native.js +47 -0
  8. package/dist/open-lix.d.ts +39 -201
  9. package/dist/open-lix.js +59 -284
  10. package/dist/result.d.ts +18 -0
  11. package/dist/result.js +48 -0
  12. package/dist/types.d.ts +114 -1
  13. package/dist/value.d.ts +28 -0
  14. package/dist/value.js +245 -0
  15. package/package.json +20 -50
  16. package/SKILL.md +0 -506
  17. package/dist/builtin-schemas.d.ts +0 -1
  18. package/dist/builtin-schemas.js +0 -1
  19. package/dist/engine-wasm/index.d.ts +0 -87
  20. package/dist/engine-wasm/index.js +0 -339
  21. package/dist/engine-wasm/wasm/lix_engine.d.ts +0 -79
  22. package/dist/engine-wasm/wasm/lix_engine.js +0 -821
  23. package/dist/engine-wasm/wasm/lix_engine.wasm +0 -0
  24. package/dist/engine-wasm/wasm/lix_engine.wasm.d.ts +0 -26
  25. package/dist/generated/builtin-schemas.d.ts +0 -427
  26. package/dist/generated/builtin-schemas.js +0 -643
  27. package/dist/sqlite/index.d.ts +0 -12
  28. package/dist/sqlite/index.js +0 -303
  29. package/dist-engine-src/README.md +0 -18
  30. package/dist-engine-src/src/backend/kv.rs +0 -358
  31. package/dist-engine-src/src/backend/mod.rs +0 -12
  32. package/dist-engine-src/src/backend/testing.rs +0 -658
  33. package/dist-engine-src/src/backend/types.rs +0 -96
  34. package/dist-engine-src/src/binary_cas/chunking.rs +0 -31
  35. package/dist-engine-src/src/binary_cas/codec.rs +0 -346
  36. package/dist-engine-src/src/binary_cas/context.rs +0 -139
  37. package/dist-engine-src/src/binary_cas/kv.rs +0 -1063
  38. package/dist-engine-src/src/binary_cas/mod.rs +0 -11
  39. package/dist-engine-src/src/binary_cas/types.rs +0 -121
  40. package/dist-engine-src/src/catalog/context.rs +0 -412
  41. package/dist-engine-src/src/catalog/mod.rs +0 -10
  42. package/dist-engine-src/src/catalog/schema.rs +0 -4
  43. package/dist-engine-src/src/catalog/snapshot.rs +0 -1114
  44. package/dist-engine-src/src/cel/context.rs +0 -86
  45. package/dist-engine-src/src/cel/error.rs +0 -19
  46. package/dist-engine-src/src/cel/mod.rs +0 -8
  47. package/dist-engine-src/src/cel/provider.rs +0 -9
  48. package/dist-engine-src/src/cel/runtime.rs +0 -167
  49. package/dist-engine-src/src/cel/value.rs +0 -50
  50. package/dist-engine-src/src/commit_graph/context.rs +0 -901
  51. package/dist-engine-src/src/commit_graph/mod.rs +0 -11
  52. package/dist-engine-src/src/commit_graph/types.rs +0 -109
  53. package/dist-engine-src/src/commit_graph/walker.rs +0 -756
  54. package/dist-engine-src/src/commit_store/codec.rs +0 -887
  55. package/dist-engine-src/src/commit_store/context.rs +0 -944
  56. package/dist-engine-src/src/commit_store/materialization.rs +0 -84
  57. package/dist-engine-src/src/commit_store/mod.rs +0 -16
  58. package/dist-engine-src/src/commit_store/storage.rs +0 -600
  59. package/dist-engine-src/src/commit_store/types.rs +0 -215
  60. package/dist-engine-src/src/common/error.rs +0 -313
  61. package/dist-engine-src/src/common/fingerprint.rs +0 -3
  62. package/dist-engine-src/src/common/fs_path.rs +0 -1336
  63. package/dist-engine-src/src/common/identity.rs +0 -145
  64. package/dist-engine-src/src/common/json_pointer.rs +0 -67
  65. package/dist-engine-src/src/common/metadata.rs +0 -40
  66. package/dist-engine-src/src/common/mod.rs +0 -23
  67. package/dist-engine-src/src/common/types.rs +0 -105
  68. package/dist-engine-src/src/common/wire.rs +0 -222
  69. package/dist-engine-src/src/domain.rs +0 -324
  70. package/dist-engine-src/src/engine.rs +0 -225
  71. package/dist-engine-src/src/entity_identity.rs +0 -405
  72. package/dist-engine-src/src/functions/context.rs +0 -292
  73. package/dist-engine-src/src/functions/deterministic.rs +0 -113
  74. package/dist-engine-src/src/functions/mod.rs +0 -18
  75. package/dist-engine-src/src/functions/provider.rs +0 -130
  76. package/dist-engine-src/src/functions/state.rs +0 -336
  77. package/dist-engine-src/src/functions/types.rs +0 -37
  78. package/dist-engine-src/src/init.rs +0 -558
  79. package/dist-engine-src/src/json_store/compression.rs +0 -77
  80. package/dist-engine-src/src/json_store/context.rs +0 -423
  81. package/dist-engine-src/src/json_store/encoded.rs +0 -15
  82. package/dist-engine-src/src/json_store/mod.rs +0 -12
  83. package/dist-engine-src/src/json_store/store.rs +0 -1109
  84. package/dist-engine-src/src/json_store/types.rs +0 -217
  85. package/dist-engine-src/src/lib.rs +0 -62
  86. package/dist-engine-src/src/live_state/context.rs +0 -2019
  87. package/dist-engine-src/src/live_state/mod.rs +0 -15
  88. package/dist-engine-src/src/live_state/overlay.rs +0 -75
  89. package/dist-engine-src/src/live_state/reader.rs +0 -23
  90. package/dist-engine-src/src/live_state/types.rs +0 -222
  91. package/dist-engine-src/src/live_state/visibility.rs +0 -223
  92. package/dist-engine-src/src/plugin/archive.rs +0 -438
  93. package/dist-engine-src/src/plugin/component.rs +0 -183
  94. package/dist-engine-src/src/plugin/install.rs +0 -619
  95. package/dist-engine-src/src/plugin/manifest.rs +0 -516
  96. package/dist-engine-src/src/plugin/materializer.rs +0 -477
  97. package/dist-engine-src/src/plugin/mod.rs +0 -33
  98. package/dist-engine-src/src/plugin/plugin_manifest.json +0 -118
  99. package/dist-engine-src/src/plugin/storage.rs +0 -74
  100. package/dist-engine-src/src/schema/annotations/defaults.rs +0 -275
  101. package/dist-engine-src/src/schema/annotations/mod.rs +0 -1
  102. package/dist-engine-src/src/schema/builtin/lix_account.json +0 -21
  103. package/dist-engine-src/src/schema/builtin/lix_active_account.json +0 -29
  104. package/dist-engine-src/src/schema/builtin/lix_binary_blob_ref.json +0 -29
  105. package/dist-engine-src/src/schema/builtin/lix_change.json +0 -63
  106. package/dist-engine-src/src/schema/builtin/lix_change_author.json +0 -45
  107. package/dist-engine-src/src/schema/builtin/lix_commit.json +0 -24
  108. package/dist-engine-src/src/schema/builtin/lix_commit_edge.json +0 -53
  109. package/dist-engine-src/src/schema/builtin/lix_directory_descriptor.json +0 -52
  110. package/dist-engine-src/src/schema/builtin/lix_file_descriptor.json +0 -52
  111. package/dist-engine-src/src/schema/builtin/lix_key_value.json +0 -40
  112. package/dist-engine-src/src/schema/builtin/lix_label.json +0 -29
  113. package/dist-engine-src/src/schema/builtin/lix_label_assignment.json +0 -74
  114. package/dist-engine-src/src/schema/builtin/lix_registered_schema.json +0 -25
  115. package/dist-engine-src/src/schema/builtin/lix_version_descriptor.json +0 -34
  116. package/dist-engine-src/src/schema/builtin/lix_version_ref.json +0 -48
  117. package/dist-engine-src/src/schema/builtin/mod.rs +0 -222
  118. package/dist-engine-src/src/schema/compatibility.rs +0 -787
  119. package/dist-engine-src/src/schema/definition.json +0 -187
  120. package/dist-engine-src/src/schema/definition.rs +0 -742
  121. package/dist-engine-src/src/schema/key.rs +0 -138
  122. package/dist-engine-src/src/schema/mod.rs +0 -20
  123. package/dist-engine-src/src/schema/seed.rs +0 -14
  124. package/dist-engine-src/src/schema/tests.rs +0 -780
  125. package/dist-engine-src/src/session/context.rs +0 -404
  126. package/dist-engine-src/src/session/create_version.rs +0 -88
  127. package/dist-engine-src/src/session/execute.rs +0 -541
  128. package/dist-engine-src/src/session/merge/analysis.rs +0 -102
  129. package/dist-engine-src/src/session/merge/apply.rs +0 -23
  130. package/dist-engine-src/src/session/merge/conflicts.rs +0 -63
  131. package/dist-engine-src/src/session/merge/mod.rs +0 -11
  132. package/dist-engine-src/src/session/merge/stats.rs +0 -65
  133. package/dist-engine-src/src/session/merge/version.rs +0 -427
  134. package/dist-engine-src/src/session/mod.rs +0 -27
  135. package/dist-engine-src/src/session/optimization9_sql2_bench.rs +0 -100
  136. package/dist-engine-src/src/session/switch_version.rs +0 -110
  137. package/dist-engine-src/src/session/transaction.rs +0 -76
  138. package/dist-engine-src/src/sql2/change_provider.rs +0 -331
  139. package/dist-engine-src/src/sql2/classify.rs +0 -174
  140. package/dist-engine-src/src/sql2/context.rs +0 -311
  141. package/dist-engine-src/src/sql2/directory_history_provider.rs +0 -631
  142. package/dist-engine-src/src/sql2/directory_provider.rs +0 -2453
  143. package/dist-engine-src/src/sql2/dml.rs +0 -148
  144. package/dist-engine-src/src/sql2/entity_history_provider.rs +0 -440
  145. package/dist-engine-src/src/sql2/entity_provider.rs +0 -3211
  146. package/dist-engine-src/src/sql2/error.rs +0 -215
  147. package/dist-engine-src/src/sql2/execute.rs +0 -3533
  148. package/dist-engine-src/src/sql2/file_history_provider.rs +0 -910
  149. package/dist-engine-src/src/sql2/file_provider.rs +0 -3679
  150. package/dist-engine-src/src/sql2/filesystem_planner.rs +0 -1490
  151. package/dist-engine-src/src/sql2/filesystem_predicates.rs +0 -159
  152. package/dist-engine-src/src/sql2/filesystem_visibility.rs +0 -383
  153. package/dist-engine-src/src/sql2/history_projection.rs +0 -56
  154. package/dist-engine-src/src/sql2/history_provider.rs +0 -412
  155. package/dist-engine-src/src/sql2/history_route.rs +0 -657
  156. package/dist-engine-src/src/sql2/lix_state_provider.rs +0 -2512
  157. package/dist-engine-src/src/sql2/mod.rs +0 -47
  158. package/dist-engine-src/src/sql2/predicate_typecheck.rs +0 -246
  159. package/dist-engine-src/src/sql2/public_bind/assignment.rs +0 -46
  160. package/dist-engine-src/src/sql2/public_bind/capability.rs +0 -41
  161. package/dist-engine-src/src/sql2/public_bind/dml.rs +0 -172
  162. package/dist-engine-src/src/sql2/public_bind/mod.rs +0 -26
  163. package/dist-engine-src/src/sql2/public_bind/table.rs +0 -168
  164. package/dist-engine-src/src/sql2/read_only.rs +0 -63
  165. package/dist-engine-src/src/sql2/record_batch.rs +0 -17
  166. package/dist-engine-src/src/sql2/result_metadata.rs +0 -29
  167. package/dist-engine-src/src/sql2/runtime.rs +0 -60
  168. package/dist-engine-src/src/sql2/session.rs +0 -132
  169. package/dist-engine-src/src/sql2/udfs/common.rs +0 -295
  170. package/dist-engine-src/src/sql2/udfs/lix_active_version_commit_id.rs +0 -53
  171. package/dist-engine-src/src/sql2/udfs/lix_empty_blob.rs +0 -47
  172. package/dist-engine-src/src/sql2/udfs/lix_json.rs +0 -100
  173. package/dist-engine-src/src/sql2/udfs/lix_json_get.rs +0 -99
  174. package/dist-engine-src/src/sql2/udfs/lix_json_get_text.rs +0 -99
  175. package/dist-engine-src/src/sql2/udfs/lix_text_decode.rs +0 -82
  176. package/dist-engine-src/src/sql2/udfs/lix_text_encode.rs +0 -85
  177. package/dist-engine-src/src/sql2/udfs/lix_timestamp.rs +0 -76
  178. package/dist-engine-src/src/sql2/udfs/lix_uuid_v7.rs +0 -76
  179. package/dist-engine-src/src/sql2/udfs/mod.rs +0 -89
  180. package/dist-engine-src/src/sql2/udfs/public_call.rs +0 -238
  181. package/dist-engine-src/src/sql2/version_provider.rs +0 -1202
  182. package/dist-engine-src/src/sql2/version_scope.rs +0 -394
  183. package/dist-engine-src/src/sql2/write_normalization.rs +0 -345
  184. package/dist-engine-src/src/storage/context.rs +0 -356
  185. package/dist-engine-src/src/storage/mod.rs +0 -14
  186. package/dist-engine-src/src/storage/read_scope.rs +0 -88
  187. package/dist-engine-src/src/storage/types.rs +0 -501
  188. package/dist-engine-src/src/storage_bench.rs +0 -4863
  189. package/dist-engine-src/src/test_support.rs +0 -228
  190. package/dist-engine-src/src/tracked_state/by_file_index.rs +0 -98
  191. package/dist-engine-src/src/tracked_state/codec.rs +0 -2085
  192. package/dist-engine-src/src/tracked_state/context.rs +0 -1867
  193. package/dist-engine-src/src/tracked_state/diff.rs +0 -686
  194. package/dist-engine-src/src/tracked_state/materialization.rs +0 -403
  195. package/dist-engine-src/src/tracked_state/materializer.rs +0 -488
  196. package/dist-engine-src/src/tracked_state/merge.rs +0 -492
  197. package/dist-engine-src/src/tracked_state/mod.rs +0 -32
  198. package/dist-engine-src/src/tracked_state/storage.rs +0 -375
  199. package/dist-engine-src/src/tracked_state/tree.rs +0 -3187
  200. package/dist-engine-src/src/tracked_state/types.rs +0 -231
  201. package/dist-engine-src/src/transaction/commit.rs +0 -1484
  202. package/dist-engine-src/src/transaction/context.rs +0 -1548
  203. package/dist-engine-src/src/transaction/live_state_overlay.rs +0 -35
  204. package/dist-engine-src/src/transaction/mod.rs +0 -13
  205. package/dist-engine-src/src/transaction/normalization.rs +0 -890
  206. package/dist-engine-src/src/transaction/prep.rs +0 -37
  207. package/dist-engine-src/src/transaction/schema_resolver.rs +0 -149
  208. package/dist-engine-src/src/transaction/staging.rs +0 -1731
  209. package/dist-engine-src/src/transaction/types.rs +0 -460
  210. package/dist-engine-src/src/transaction/validation.rs +0 -5830
  211. package/dist-engine-src/src/untracked_state/codec.rs +0 -307
  212. package/dist-engine-src/src/untracked_state/context.rs +0 -98
  213. package/dist-engine-src/src/untracked_state/materialization.rs +0 -63
  214. package/dist-engine-src/src/untracked_state/mod.rs +0 -15
  215. package/dist-engine-src/src/untracked_state/storage.rs +0 -396
  216. package/dist-engine-src/src/untracked_state/types.rs +0 -146
  217. package/dist-engine-src/src/version/context.rs +0 -40
  218. package/dist-engine-src/src/version/lifecycle.rs +0 -221
  219. package/dist-engine-src/src/version/mod.rs +0 -13
  220. package/dist-engine-src/src/version/refs.rs +0 -330
  221. package/dist-engine-src/src/version/stage_rows.rs +0 -67
  222. package/dist-engine-src/src/version/types.rs +0 -21
  223. package/dist-engine-src/src/wasm/mod.rs +0 -60
@@ -1,658 +0,0 @@
1
- use std::collections::BTreeMap;
2
- use std::sync::{Arc, Mutex};
3
-
4
- use async_trait::async_trait;
5
-
6
- use crate::backend::{
7
- Backend, BackendKvEntryPage, BackendKvExistsBatch, BackendKvExistsGroup, BackendKvGetRequest,
8
- BackendKvKeyPage, BackendKvScanRange, BackendKvScanRequest, BackendKvValueBatch,
9
- BackendKvValueGroup, BackendKvValuePage, BackendKvWriteBatch, BackendKvWriteStats,
10
- BackendReadTransaction, BackendWriteTransaction, BytePageBuilder,
11
- };
12
- use crate::LixError;
13
-
14
- type KvMap = BTreeMap<(String, Vec<u8>), Vec<u8>>;
15
-
16
- /// In-memory backend for unit tests that need backend KV semantics without SQL.
17
- ///
18
- /// SQL execution intentionally returns an error so new tests do not accidentally
19
- /// couple to raw SQL while exercising storage-facing APIs.
20
- #[derive(Debug, Clone, Default)]
21
- pub(crate) struct UnitTestBackend {
22
- kv: Arc<Mutex<KvMap>>,
23
- }
24
-
25
- impl UnitTestBackend {
26
- pub(crate) fn new() -> Self {
27
- Self::default()
28
- }
29
- }
30
-
31
- #[async_trait]
32
- impl Backend for UnitTestBackend {
33
- async fn begin_read_transaction(
34
- &self,
35
- ) -> Result<Box<dyn BackendReadTransaction + Send + Sync + 'static>, LixError> {
36
- let snapshot = self
37
- .kv
38
- .lock()
39
- .map_err(|_| lock_error("unit test backend kv"))?
40
- .clone();
41
- Ok(Box::new(UnitTestTransaction {
42
- parent: Arc::clone(&self.kv),
43
- kv: snapshot,
44
- }))
45
- }
46
-
47
- async fn begin_write_transaction(
48
- &self,
49
- ) -> Result<Box<dyn BackendWriteTransaction + Send + Sync + 'static>, LixError> {
50
- let snapshot = self
51
- .kv
52
- .lock()
53
- .map_err(|_| lock_error("unit test backend kv"))?
54
- .clone();
55
- Ok(Box::new(UnitTestTransaction {
56
- parent: Arc::clone(&self.kv),
57
- kv: snapshot,
58
- }))
59
- }
60
- }
61
-
62
- struct UnitTestTransaction {
63
- parent: Arc<Mutex<KvMap>>,
64
- kv: KvMap,
65
- }
66
-
67
- #[async_trait]
68
- impl BackendReadTransaction for UnitTestTransaction {
69
- async fn get_values(
70
- &mut self,
71
- request: BackendKvGetRequest,
72
- ) -> Result<BackendKvValueBatch, LixError> {
73
- let mut groups = Vec::with_capacity(request.groups.len());
74
- for group in request.groups {
75
- let namespace = group.namespace.clone();
76
- let mut values = BytePageBuilder::with_capacity(group.keys.len(), 0);
77
- let mut present = Vec::with_capacity(group.keys.len());
78
- for key in group.keys {
79
- if let Some(value) = self.kv.get(&(namespace.clone(), key)) {
80
- values.push(value);
81
- present.push(true);
82
- } else {
83
- values.push([]);
84
- present.push(false);
85
- }
86
- }
87
- groups.push(BackendKvValueGroup::new(
88
- namespace,
89
- values.finish(),
90
- present,
91
- ));
92
- }
93
- Ok(BackendKvValueBatch { groups })
94
- }
95
-
96
- async fn exists_many(
97
- &mut self,
98
- request: BackendKvGetRequest,
99
- ) -> Result<BackendKvExistsBatch, LixError> {
100
- let mut groups = Vec::with_capacity(request.groups.len());
101
- for group in request.groups {
102
- let namespace = group.namespace.clone();
103
- let exists = group
104
- .keys
105
- .into_iter()
106
- .map(|key| self.kv.contains_key(&(namespace.clone(), key)))
107
- .collect();
108
- groups.push(BackendKvExistsGroup { namespace, exists });
109
- }
110
- Ok(BackendKvExistsBatch { groups })
111
- }
112
-
113
- async fn scan_keys(
114
- &mut self,
115
- request: BackendKvScanRequest,
116
- ) -> Result<BackendKvKeyPage, LixError> {
117
- Ok(scan_map_keys(&self.kv, request))
118
- }
119
-
120
- async fn scan_values(
121
- &mut self,
122
- request: BackendKvScanRequest,
123
- ) -> Result<BackendKvValuePage, LixError> {
124
- Ok(scan_map_values(&self.kv, request))
125
- }
126
-
127
- async fn scan_entries(
128
- &mut self,
129
- request: BackendKvScanRequest,
130
- ) -> Result<BackendKvEntryPage, LixError> {
131
- Ok(scan_map_entries(&self.kv, request))
132
- }
133
-
134
- async fn rollback(self: Box<Self>) -> Result<(), LixError> {
135
- Ok(())
136
- }
137
- }
138
-
139
- #[async_trait]
140
- impl BackendWriteTransaction for UnitTestTransaction {
141
- async fn write_kv_batch(
142
- &mut self,
143
- batch: BackendKvWriteBatch,
144
- ) -> Result<BackendKvWriteStats, LixError> {
145
- let mut stats = BackendKvWriteStats::default();
146
- for group in batch.groups {
147
- let namespace = group.namespace().to_string();
148
- for index in 0..group.put_count() {
149
- let key = group.put_key(index).ok_or_else(|| {
150
- LixError::new("LIX_ERROR_UNKNOWN", "backend write batch missing put key")
151
- })?;
152
- let value = group.put_value(index).ok_or_else(|| {
153
- LixError::new("LIX_ERROR_UNKNOWN", "backend write batch missing put value")
154
- })?;
155
- stats.puts += 1;
156
- stats.bytes_written += key.len() + value.len();
157
- self.kv
158
- .insert((namespace.clone(), key.to_vec()), value.to_vec());
159
- }
160
- for index in 0..group.delete_count() {
161
- let key = group.delete_key(index).ok_or_else(|| {
162
- LixError::new(
163
- "LIX_ERROR_UNKNOWN",
164
- "backend write batch missing delete key",
165
- )
166
- })?;
167
- stats.deletes += 1;
168
- stats.bytes_written += key.len();
169
- self.kv.remove(&(namespace.clone(), key.to_vec()));
170
- }
171
- }
172
- Ok(stats)
173
- }
174
-
175
- async fn commit(self: Box<Self>) -> Result<(), LixError> {
176
- *self
177
- .parent
178
- .lock()
179
- .map_err(|_| lock_error("unit test backend kv"))? = self.kv;
180
- Ok(())
181
- }
182
- }
183
-
184
- #[async_trait]
185
- impl Backend for Arc<UnitTestBackend> {
186
- async fn begin_read_transaction(
187
- &self,
188
- ) -> Result<Box<dyn BackendReadTransaction + Send + Sync + 'static>, LixError> {
189
- self.as_ref().begin_read_transaction().await
190
- }
191
-
192
- async fn begin_write_transaction(
193
- &self,
194
- ) -> Result<Box<dyn BackendWriteTransaction + Send + Sync + 'static>, LixError> {
195
- self.as_ref().begin_write_transaction().await
196
- }
197
- }
198
-
199
- fn scan_pairs<'a>(
200
- kv: &'a KvMap,
201
- namespace: &str,
202
- range: &BackendKvScanRange,
203
- limit: Option<usize>,
204
- ) -> Vec<(&'a Vec<u8>, &'a Vec<u8>)> {
205
- let pairs = kv
206
- .iter()
207
- .filter(|((candidate_namespace, key), _)| {
208
- candidate_namespace == namespace && key_matches_range(key, range)
209
- })
210
- .collect::<Vec<_>>();
211
- let mut pairs = pairs;
212
- pairs.sort_by(|left, right| left.0 .1.cmp(&right.0 .1));
213
- if let Some(limit) = limit {
214
- pairs.truncate(limit);
215
- }
216
- pairs
217
- .into_iter()
218
- .map(|((_, key), value)| (key, value))
219
- .collect()
220
- }
221
-
222
- pub(crate) fn scan_map_keys(kv: &KvMap, request: BackendKvScanRequest) -> BackendKvKeyPage {
223
- let pairs = scan_filtered_pairs(kv, &request);
224
- let has_more = pairs.len() > request.limit;
225
- let mut keys = BytePageBuilder::with_capacity(request.limit.min(pairs.len()), 0);
226
- let mut resume_after = None;
227
- for (index, (key, _)) in pairs.into_iter().enumerate() {
228
- if index >= request.limit {
229
- break;
230
- }
231
- resume_after = Some(key.clone());
232
- keys.push(key);
233
- }
234
- let resume_after = has_more.then_some(resume_after).flatten();
235
- BackendKvKeyPage {
236
- keys: keys.finish(),
237
- resume_after,
238
- }
239
- }
240
-
241
- pub(crate) fn scan_map_values(kv: &KvMap, request: BackendKvScanRequest) -> BackendKvValuePage {
242
- let pairs = scan_filtered_pairs(kv, &request);
243
- let has_more = pairs.len() > request.limit;
244
- let mut values = BytePageBuilder::with_capacity(request.limit.min(pairs.len()), 0);
245
- let mut resume_after = None;
246
- for (index, (key, value)) in pairs.into_iter().enumerate() {
247
- if index >= request.limit {
248
- break;
249
- }
250
- resume_after = Some(key.clone());
251
- values.push(value);
252
- }
253
- let resume_after = has_more.then_some(resume_after).flatten();
254
- BackendKvValuePage {
255
- values: values.finish(),
256
- resume_after,
257
- }
258
- }
259
-
260
- pub(crate) fn scan_map_entries(kv: &KvMap, request: BackendKvScanRequest) -> BackendKvEntryPage {
261
- let pairs = scan_filtered_pairs(kv, &request);
262
- let has_more = pairs.len() > request.limit;
263
- let mut keys = BytePageBuilder::with_capacity(request.limit.min(pairs.len()), 0);
264
- let mut values = BytePageBuilder::with_capacity(request.limit.min(pairs.len()), 0);
265
- let mut resume_after = None;
266
- for (index, (key, value)) in pairs.into_iter().enumerate() {
267
- if index >= request.limit {
268
- break;
269
- }
270
- resume_after = Some(key.clone());
271
- keys.push(key);
272
- values.push(value);
273
- }
274
- let resume_after = has_more.then_some(resume_after).flatten();
275
- BackendKvEntryPage {
276
- keys: keys.finish(),
277
- values: values.finish(),
278
- resume_after,
279
- }
280
- }
281
-
282
- fn scan_filtered_pairs<'a>(
283
- kv: &'a KvMap,
284
- request: &BackendKvScanRequest,
285
- ) -> Vec<(&'a Vec<u8>, &'a Vec<u8>)> {
286
- let scan_limit = request
287
- .limit
288
- .checked_add(1 + usize::from(request.after.is_some()))
289
- .unwrap_or(request.limit);
290
- scan_pairs(kv, &request.namespace, &request.range, Some(scan_limit))
291
- .into_iter()
292
- .filter(|(key, _)| {
293
- request
294
- .after
295
- .as_deref()
296
- .is_none_or(|after| key.as_slice() > after)
297
- })
298
- .collect()
299
- }
300
-
301
- fn key_matches_range(key: &[u8], range: &BackendKvScanRange) -> bool {
302
- match range {
303
- BackendKvScanRange::Prefix(prefix) => key.starts_with(prefix),
304
- BackendKvScanRange::Range { start, end } => start.as_slice() <= key && key < end.as_slice(),
305
- }
306
- }
307
-
308
- fn lock_error(name: &str) -> LixError {
309
- LixError::new("LIX_ERROR_UNKNOWN", format!("{name} lock poisoned"))
310
- }
311
-
312
- #[cfg(test)]
313
- mod tests {
314
- use super::*;
315
- use crate::backend::{
316
- BackendKvGetGroup, BackendKvGetRequest, BackendKvScanRequest, BackendKvWriteBatch,
317
- BackendKvWriteGroup,
318
- };
319
-
320
- async fn put(
321
- transaction: &mut (dyn BackendWriteTransaction + Send + Sync),
322
- namespace: &str,
323
- key: &[u8],
324
- value: &[u8],
325
- ) {
326
- transaction
327
- .write_kv_batch(BackendKvWriteBatch {
328
- groups: {
329
- let mut group = BackendKvWriteGroup::new(namespace);
330
- group.put(key, value);
331
- vec![group]
332
- },
333
- })
334
- .await
335
- .expect("put should succeed");
336
- }
337
-
338
- async fn delete(
339
- transaction: &mut (dyn BackendWriteTransaction + Send + Sync),
340
- namespace: &str,
341
- key: &[u8],
342
- ) {
343
- transaction
344
- .write_kv_batch(BackendKvWriteBatch {
345
- groups: {
346
- let mut group = BackendKvWriteGroup::new(namespace);
347
- group.delete(key);
348
- vec![group]
349
- },
350
- })
351
- .await
352
- .expect("delete should succeed");
353
- }
354
-
355
- async fn get(backend: &UnitTestBackend, namespace: &str, key: &[u8]) -> Option<Vec<u8>> {
356
- let mut transaction = backend
357
- .begin_read_transaction()
358
- .await
359
- .expect("read transaction should open");
360
- let result = transaction
361
- .get_values(BackendKvGetRequest {
362
- groups: vec![BackendKvGetGroup {
363
- namespace: namespace.to_string(),
364
- keys: vec![key.to_vec()],
365
- }],
366
- })
367
- .await
368
- .expect("get should succeed");
369
- transaction
370
- .rollback()
371
- .await
372
- .expect("rollback should succeed");
373
- result
374
- .groups
375
- .into_iter()
376
- .next()
377
- .and_then(|group| group.value(0).flatten().map(<[u8]>::to_vec))
378
- }
379
-
380
- async fn scan(
381
- backend: &UnitTestBackend,
382
- namespace: &str,
383
- range: BackendKvScanRange,
384
- limit: usize,
385
- ) -> BackendKvEntryPage {
386
- let mut transaction = backend
387
- .begin_read_transaction()
388
- .await
389
- .expect("read transaction should open");
390
- let result = transaction
391
- .scan_entries(BackendKvScanRequest {
392
- namespace: namespace.to_string(),
393
- range,
394
- after: None,
395
- limit,
396
- })
397
- .await
398
- .expect("scan should succeed");
399
- transaction
400
- .rollback()
401
- .await
402
- .expect("rollback should succeed");
403
- result
404
- }
405
-
406
- fn assert_entries(page: &BackendKvEntryPage, expected: &[(&[u8], &[u8])]) {
407
- assert_eq!(page.len(), expected.len());
408
- for (index, (key, value)) in expected.iter().enumerate() {
409
- assert_eq!(page.key(index).expect("key exists"), *key);
410
- assert_eq!(page.value(index).expect("value exists"), *value);
411
- }
412
- }
413
-
414
- async fn scan_entries_request(
415
- backend: &UnitTestBackend,
416
- after: Option<&[u8]>,
417
- limit: usize,
418
- ) -> BackendKvEntryPage {
419
- let mut transaction = backend
420
- .begin_read_transaction()
421
- .await
422
- .expect("read transaction should open");
423
- let result = transaction
424
- .scan_entries(BackendKvScanRequest {
425
- namespace: "ns".to_string(),
426
- range: BackendKvScanRange::prefix(Vec::new()),
427
- after: after.map(Vec::from),
428
- limit,
429
- })
430
- .await
431
- .expect("scan should succeed");
432
- transaction
433
- .rollback()
434
- .await
435
- .expect("rollback should succeed");
436
- result
437
- }
438
-
439
- async fn scan_keys_request(
440
- backend: &UnitTestBackend,
441
- after: Option<&[u8]>,
442
- limit: usize,
443
- ) -> BackendKvKeyPage {
444
- let mut transaction = backend
445
- .begin_read_transaction()
446
- .await
447
- .expect("read transaction should open");
448
- let result = transaction
449
- .scan_keys(BackendKvScanRequest {
450
- namespace: "ns".to_string(),
451
- range: BackendKvScanRange::prefix(Vec::new()),
452
- after: after.map(Vec::from),
453
- limit,
454
- })
455
- .await
456
- .expect("scan should succeed");
457
- transaction
458
- .rollback()
459
- .await
460
- .expect("rollback should succeed");
461
- result
462
- }
463
-
464
- #[tokio::test]
465
- async fn committed_put_is_visible_to_backend_reads() {
466
- let backend = UnitTestBackend::new();
467
- let mut transaction = backend
468
- .begin_write_transaction()
469
- .await
470
- .expect("transaction should open");
471
- put(transaction.as_mut(), "live_state", b"key", b"value").await;
472
- transaction.commit().await.expect("commit should succeed");
473
-
474
- assert_eq!(
475
- get(&backend, "live_state", b"key").await,
476
- Some(b"value".to_vec())
477
- );
478
- }
479
-
480
- #[tokio::test]
481
- async fn rollback_discards_puts() {
482
- let backend = UnitTestBackend::new();
483
- let mut transaction = backend
484
- .begin_write_transaction()
485
- .await
486
- .expect("transaction should open");
487
- put(transaction.as_mut(), "live_state", b"key", b"value").await;
488
- transaction
489
- .rollback()
490
- .await
491
- .expect("rollback should succeed");
492
-
493
- assert_eq!(get(&backend, "live_state", b"key").await, None);
494
- }
495
-
496
- #[tokio::test]
497
- async fn close_is_idempotent_and_does_not_destroy_data() {
498
- let backend = UnitTestBackend::new();
499
- let mut transaction = backend
500
- .begin_write_transaction()
501
- .await
502
- .expect("transaction should open");
503
- put(transaction.as_mut(), "live_state", b"key", b"value").await;
504
- transaction.commit().await.expect("commit should succeed");
505
-
506
- backend.close().await.expect("first close should succeed");
507
- backend.close().await.expect("second close should succeed");
508
-
509
- assert_eq!(
510
- get(&backend, "live_state", b"key").await,
511
- Some(b"value".to_vec())
512
- );
513
- }
514
-
515
- #[tokio::test]
516
- async fn delete_removes_key_on_commit() {
517
- let backend = UnitTestBackend::new();
518
- let mut seed = backend
519
- .begin_write_transaction()
520
- .await
521
- .expect("seed transaction should open");
522
- put(seed.as_mut(), "live_state", b"key", b"value").await;
523
- seed.commit().await.expect("seed commit should succeed");
524
-
525
- let mut transaction = backend
526
- .begin_write_transaction()
527
- .await
528
- .expect("delete transaction should open");
529
- delete(transaction.as_mut(), "live_state", b"key").await;
530
- transaction.commit().await.expect("commit should succeed");
531
-
532
- assert_eq!(get(&backend, "live_state", b"key").await, None);
533
- }
534
-
535
- #[tokio::test]
536
- async fn prefix_scan_returns_lexicographic_order_with_limit() {
537
- let backend = UnitTestBackend::new();
538
- let mut transaction = backend
539
- .begin_write_transaction()
540
- .await
541
- .expect("transaction should open");
542
- put(transaction.as_mut(), "ns", b"b/2", b"2").await;
543
- put(transaction.as_mut(), "ns", b"a/2", b"2").await;
544
- put(transaction.as_mut(), "ns", b"a/1", b"1").await;
545
- put(transaction.as_mut(), "other", b"a/0", b"0").await;
546
- transaction.commit().await.unwrap();
547
-
548
- let pairs = scan(&backend, "ns", BackendKvScanRange::prefix(b"a/"), 1).await;
549
- assert_entries(&pairs, &[(b"a/1", b"1")]);
550
- }
551
-
552
- #[tokio::test]
553
- async fn scan_sets_resume_after_only_when_more_rows_exist() {
554
- let backend = UnitTestBackend::new();
555
- let mut transaction = backend
556
- .begin_write_transaction()
557
- .await
558
- .expect("transaction should open");
559
- put(transaction.as_mut(), "ns", b"a", b"1").await;
560
- put(transaction.as_mut(), "ns", b"b", b"2").await;
561
- put(transaction.as_mut(), "ns", b"c", b"3").await;
562
- transaction.commit().await.unwrap();
563
-
564
- let first_page = scan_entries_request(&backend, None, 2).await;
565
- assert_entries(&first_page, &[(b"a", b"1"), (b"b", b"2")]);
566
- assert_eq!(first_page.resume_after, Some(b"b".to_vec()));
567
-
568
- let second_page =
569
- scan_entries_request(&backend, first_page.resume_after.as_deref(), 2).await;
570
- assert_entries(&second_page, &[(b"c", b"3")]);
571
- assert_eq!(second_page.resume_after, None);
572
- }
573
-
574
- #[tokio::test]
575
- async fn scan_exact_page_size_has_no_resume_after() {
576
- let backend = UnitTestBackend::new();
577
- let mut transaction = backend
578
- .begin_write_transaction()
579
- .await
580
- .expect("transaction should open");
581
- put(transaction.as_mut(), "ns", b"a", b"1").await;
582
- put(transaction.as_mut(), "ns", b"b", b"2").await;
583
- transaction.commit().await.unwrap();
584
-
585
- let page = scan_entries_request(&backend, None, 2).await;
586
- assert_entries(&page, &[(b"a", b"1"), (b"b", b"2")]);
587
- assert_eq!(page.resume_after, None);
588
- }
589
-
590
- #[tokio::test]
591
- async fn key_only_scan_omits_values() {
592
- let backend = UnitTestBackend::new();
593
- let mut transaction = backend
594
- .begin_write_transaction()
595
- .await
596
- .expect("transaction should open");
597
- put(transaction.as_mut(), "ns", b"a", b"1").await;
598
- put(transaction.as_mut(), "ns", b"b", b"2").await;
599
- transaction.commit().await.unwrap();
600
-
601
- let page = scan_keys_request(&backend, None, 2).await;
602
- assert_eq!(page.keys.iter().collect::<Vec<_>>(), vec![b"a", b"b"]);
603
- assert_eq!(page.resume_after, None);
604
- }
605
-
606
- #[tokio::test]
607
- async fn existence_get_omits_values() {
608
- let backend = UnitTestBackend::new();
609
- let mut transaction = backend
610
- .begin_write_transaction()
611
- .await
612
- .expect("transaction should open");
613
- put(transaction.as_mut(), "ns", b"a", b"1").await;
614
- transaction.commit().await.unwrap();
615
-
616
- let mut transaction = backend
617
- .begin_read_transaction()
618
- .await
619
- .expect("read transaction should open");
620
- let result = transaction
621
- .exists_many(BackendKvGetRequest {
622
- groups: vec![BackendKvGetGroup {
623
- namespace: "ns".to_string(),
624
- keys: vec![b"a".to_vec(), b"missing".to_vec()],
625
- }],
626
- })
627
- .await
628
- .expect("existence get should succeed");
629
- transaction
630
- .rollback()
631
- .await
632
- .expect("rollback should succeed");
633
-
634
- assert_eq!(result.groups[0].exists, vec![true, false]);
635
- }
636
-
637
- #[tokio::test]
638
- async fn range_scan_is_half_open() {
639
- let backend = UnitTestBackend::new();
640
- let mut transaction = backend
641
- .begin_write_transaction()
642
- .await
643
- .expect("transaction should open");
644
- put(transaction.as_mut(), "ns", b"a", b"a").await;
645
- put(transaction.as_mut(), "ns", b"b", b"b").await;
646
- put(transaction.as_mut(), "ns", b"c", b"c").await;
647
- transaction.commit().await.unwrap();
648
-
649
- let pairs = scan(
650
- &backend,
651
- "ns",
652
- BackendKvScanRange::range(b"a", b"c"),
653
- usize::MAX,
654
- )
655
- .await;
656
- assert_entries(&pairs, &[(b"a", b"a"), (b"b", b"b")]);
657
- }
658
- }