@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,944 +0,0 @@
1
- use crate::commit_store::{
2
- Change, ChangeIndexEntry, ChangeLocator, ChangeRef, ChangeScanRequest, Commit, CommitDraftRef,
3
- LocatedChange, StagedCommitStoreCommit,
4
- };
5
- use crate::storage::{StorageReader, StorageWriteSet};
6
- use crate::LixError;
7
- use std::collections::{BTreeMap, BTreeSet};
8
- use tokio::sync::Mutex;
9
-
10
- /// Canonical physical storage boundary for commits and their changes.
11
- #[derive(Clone, Copy, Debug, Default)]
12
- pub(crate) struct CommitStoreContext;
13
-
14
- impl CommitStoreContext {
15
- pub(crate) fn new() -> Self {
16
- Self
17
- }
18
-
19
- /// Creates a commit-store writer over read visibility and a pending write set.
20
- pub(crate) fn writer<'a, S>(
21
- &self,
22
- store: &'a mut S,
23
- writes: &'a mut StorageWriteSet,
24
- ) -> CommitStoreWriter<'a, S>
25
- where
26
- S: StorageReader + ?Sized,
27
- {
28
- CommitStoreWriter { store, writes }
29
- }
30
-
31
- /// Creates a commit-store reader over a storage snapshot or transaction.
32
- pub(crate) fn reader<S>(&self, store: S) -> CommitStoreReader<S>
33
- where
34
- S: StorageReader,
35
- {
36
- CommitStoreReader {
37
- store: Mutex::new(store),
38
- }
39
- }
40
-
41
- pub(crate) async fn load_commit_from(
42
- &self,
43
- store: &mut (impl StorageReader + ?Sized),
44
- commit_id: &str,
45
- ) -> Result<Option<Commit>, LixError> {
46
- crate::commit_store::storage::load_commit(store, commit_id).await
47
- }
48
-
49
- pub(crate) async fn load_change_pack_from(
50
- &self,
51
- store: &mut (impl StorageReader + ?Sized),
52
- commit_id: &str,
53
- pack_id: u32,
54
- ) -> Result<Option<Vec<Change>>, LixError> {
55
- crate::commit_store::storage::load_change_pack(store, commit_id, pack_id).await
56
- }
57
-
58
- pub(crate) async fn load_membership_pack_from(
59
- &self,
60
- store: &mut (impl StorageReader + ?Sized),
61
- commit_id: &str,
62
- pack_id: u32,
63
- ) -> Result<Option<Vec<ChangeLocator>>, LixError> {
64
- crate::commit_store::storage::load_membership_pack(store, commit_id, pack_id).await
65
- }
66
- }
67
-
68
- /// Commit-store reader over a storage snapshot or transaction.
69
- pub(crate) struct CommitStoreReader<S> {
70
- store: Mutex<S>,
71
- }
72
-
73
- impl<S> CommitStoreReader<S>
74
- where
75
- S: StorageReader,
76
- {
77
- pub(crate) async fn load_change_index_entries(
78
- &self,
79
- change_ids: &[String],
80
- ) -> Result<Vec<Option<crate::commit_store::ChangeIndexEntry>>, LixError> {
81
- crate::commit_store::storage::load_change_index_entries(
82
- &mut *self.store.lock().await,
83
- change_ids,
84
- )
85
- .await
86
- }
87
-
88
- pub(crate) async fn load_commit(
89
- &self,
90
- commit_id: &str,
91
- ) -> Result<Option<crate::commit_store::Commit>, LixError> {
92
- crate::commit_store::storage::load_commit(&mut *self.store.lock().await, commit_id).await
93
- }
94
-
95
- pub(crate) async fn scan_commits(&self) -> Result<Vec<crate::commit_store::Commit>, LixError> {
96
- crate::commit_store::storage::scan_commits(&mut *self.store.lock().await).await
97
- }
98
-
99
- pub(crate) async fn load_change_pack(
100
- &self,
101
- commit_id: &str,
102
- pack_id: u32,
103
- ) -> Result<Option<Vec<crate::commit_store::Change>>, LixError> {
104
- crate::commit_store::storage::load_change_pack(
105
- &mut *self.store.lock().await,
106
- commit_id,
107
- pack_id,
108
- )
109
- .await
110
- }
111
-
112
- pub(crate) async fn load_membership_pack(
113
- &self,
114
- commit_id: &str,
115
- pack_id: u32,
116
- ) -> Result<Option<Vec<crate::commit_store::ChangeLocator>>, LixError> {
117
- crate::commit_store::storage::load_membership_pack(
118
- &mut *self.store.lock().await,
119
- commit_id,
120
- pack_id,
121
- )
122
- .await
123
- }
124
-
125
- pub(crate) async fn load_changes(
126
- &self,
127
- change_ids: &[String],
128
- ) -> Result<Vec<Option<crate::commit_store::Change>>, LixError> {
129
- if change_ids.is_empty() {
130
- return Ok(Vec::new());
131
- }
132
-
133
- let mut store = self.store.lock().await;
134
- let entries =
135
- crate::commit_store::storage::load_change_index_entries(&mut *store, change_ids)
136
- .await?;
137
- let mut changes = Vec::with_capacity(entries.len());
138
- let mut commits_by_id = BTreeMap::new();
139
- let mut packs_by_locator = BTreeMap::new();
140
- for (change_id, entry) in change_ids.iter().zip(entries) {
141
- changes.push(match entry {
142
- Some(ChangeIndexEntry::CommitHeader { commit_id, .. }) => {
143
- if !commits_by_id.contains_key(&commit_id) {
144
- let commit =
145
- crate::commit_store::storage::load_commit(&mut *store, &commit_id)
146
- .await?;
147
- commits_by_id.insert(commit_id.clone(), commit);
148
- }
149
- commits_by_id
150
- .get(&commit_id)
151
- .cloned()
152
- .flatten()
153
- .map(commit_header_change)
154
- }
155
- Some(ChangeIndexEntry::PackedChange { locator }) => Some(
156
- load_change_by_locator_cached(
157
- &mut *store,
158
- &mut packs_by_locator,
159
- &locator,
160
- change_id,
161
- )
162
- .await?,
163
- ),
164
- None => None,
165
- });
166
- }
167
- Ok(changes)
168
- }
169
-
170
- pub(crate) async fn load_located_changes(
171
- &self,
172
- change_ids: &[String],
173
- ) -> Result<Vec<Option<LocatedChange>>, LixError> {
174
- if change_ids.is_empty() {
175
- return Ok(Vec::new());
176
- }
177
-
178
- let mut store = self.store.lock().await;
179
- let entries =
180
- crate::commit_store::storage::load_change_index_entries(&mut *store, change_ids)
181
- .await?;
182
- let mut changes = Vec::with_capacity(entries.len());
183
- let mut commits_by_id = BTreeMap::new();
184
- let mut packs_by_locator = BTreeMap::new();
185
- for (change_id, entry) in change_ids.iter().zip(entries) {
186
- changes.push(match entry {
187
- Some(ChangeIndexEntry::CommitHeader { commit_id, .. }) => {
188
- if !commits_by_id.contains_key(&commit_id) {
189
- let commit =
190
- crate::commit_store::storage::load_commit(&mut *store, &commit_id)
191
- .await?;
192
- commits_by_id.insert(commit_id.clone(), commit);
193
- }
194
- commits_by_id
195
- .get(&commit_id)
196
- .cloned()
197
- .flatten()
198
- .map(|commit| located_commit_header_change(commit, 0))
199
- }
200
- Some(ChangeIndexEntry::PackedChange { locator }) => Some(LocatedChange {
201
- record: load_change_by_locator_cached(
202
- &mut *store,
203
- &mut packs_by_locator,
204
- &locator,
205
- change_id,
206
- )
207
- .await?,
208
- source_commit_id: locator.source_commit_id,
209
- source_pack_id: locator.source_pack_id,
210
- }),
211
- None => None,
212
- });
213
- }
214
- Ok(changes)
215
- }
216
-
217
- pub(crate) async fn load_commit_changes(
218
- &self,
219
- commit_id: &str,
220
- ) -> Result<Vec<crate::commit_store::Change>, LixError> {
221
- let mut store = self.store.lock().await;
222
- let Some(commit) =
223
- crate::commit_store::storage::load_commit(&mut *store, commit_id).await?
224
- else {
225
- return Ok(Vec::new());
226
- };
227
-
228
- let mut changes = Vec::new();
229
- for pack_id in 0..commit.change_pack_count {
230
- let Some(mut pack_changes) =
231
- crate::commit_store::storage::load_change_pack(&mut *store, commit_id, pack_id)
232
- .await?
233
- else {
234
- return Err(missing_pack_error("change", commit_id, pack_id));
235
- };
236
- changes.append(&mut pack_changes);
237
- }
238
-
239
- for pack_id in 0..commit.membership_pack_count {
240
- let Some(locators) =
241
- crate::commit_store::storage::load_membership_pack(&mut *store, commit_id, pack_id)
242
- .await?
243
- else {
244
- return Err(missing_pack_error("membership", commit_id, pack_id));
245
- };
246
- for locator in locators {
247
- let change =
248
- load_change_by_locator(&mut *store, &locator, &locator.change_id).await?;
249
- changes.push(change);
250
- }
251
- }
252
-
253
- Ok(changes)
254
- }
255
-
256
- pub(crate) async fn scan_changes(
257
- &self,
258
- request: &ChangeScanRequest,
259
- ) -> Result<Vec<LocatedChange>, LixError> {
260
- scan_changes_from_commit_store(&mut *self.store.lock().await, request).await
261
- }
262
- }
263
-
264
- /// Commit-store writer over read visibility and a transaction-local write set.
265
- pub(crate) struct CommitStoreWriter<'a, S: ?Sized> {
266
- store: &'a mut S,
267
- writes: &'a mut StorageWriteSet,
268
- }
269
-
270
- struct PendingCommitDraft<'a> {
271
- commit: CommitDraftRef<'a>,
272
- authored_changes: Vec<ChangeRef<'a>>,
273
- adopted_changes: Vec<ChangeRef<'a>>,
274
- }
275
-
276
- impl<S> CommitStoreWriter<'_, S>
277
- where
278
- S: StorageReader + ?Sized,
279
- {
280
- /// Validates and stages canonical commit-store writes for complete commits.
281
- ///
282
- /// Callers provide logical commit facts and borrowed change facts. The
283
- /// commit store owns change-id uniqueness, adoption resolution, pack
284
- /// locators, and physical namespace writes.
285
- pub(crate) async fn stage_commit_draft<'a>(
286
- &mut self,
287
- commit: CommitDraftRef<'a>,
288
- authored_changes: Vec<ChangeRef<'a>>,
289
- adopted_changes: Vec<ChangeRef<'a>>,
290
- ) -> Result<StagedCommitStoreCommit, LixError> {
291
- let mut staged = self
292
- .stage_commit_drafts([(commit, authored_changes, adopted_changes)])
293
- .await?;
294
- staged.pop().ok_or_else(|| {
295
- LixError::new(
296
- LixError::CODE_INTERNAL_ERROR,
297
- "commit-store staged no result for one commit draft",
298
- )
299
- })
300
- }
301
-
302
- /// Validates and stages a tracked commit whose authored rows will be stored
303
- /// in the tracked-state delta pack instead of a duplicate commit-store pack.
304
- pub(crate) async fn stage_tracked_commit_draft<'a>(
305
- &mut self,
306
- commit: CommitDraftRef<'a>,
307
- authored_changes: Vec<ChangeRef<'a>>,
308
- adopted_changes: Vec<ChangeRef<'a>>,
309
- ) -> Result<StagedCommitStoreCommit, LixError> {
310
- let mut staged = self
311
- .stage_tracked_commit_drafts([(commit, authored_changes, adopted_changes)])
312
- .await?;
313
- staged.pop().ok_or_else(|| {
314
- LixError::new(
315
- LixError::CODE_INTERNAL_ERROR,
316
- "commit-store staged no result for one tracked commit draft",
317
- )
318
- })
319
- }
320
-
321
- /// Validates and stages multiple commit drafts as one commit-store batch.
322
- pub(crate) async fn stage_commit_drafts<'a>(
323
- &mut self,
324
- commits: impl IntoIterator<Item = (CommitDraftRef<'a>, Vec<ChangeRef<'a>>, Vec<ChangeRef<'a>>)>,
325
- ) -> Result<Vec<StagedCommitStoreCommit>, LixError> {
326
- self.stage_commit_drafts_with_authored_pack(commits, true)
327
- .await
328
- }
329
-
330
- /// Validates and stages multiple tracked commit drafts whose authored rows
331
- /// will be stored in tracked-state delta packs.
332
- pub(crate) async fn stage_tracked_commit_drafts<'a>(
333
- &mut self,
334
- commits: impl IntoIterator<Item = (CommitDraftRef<'a>, Vec<ChangeRef<'a>>, Vec<ChangeRef<'a>>)>,
335
- ) -> Result<Vec<StagedCommitStoreCommit>, LixError> {
336
- self.stage_commit_drafts_with_authored_pack(commits, false)
337
- .await
338
- }
339
-
340
- async fn stage_commit_drafts_with_authored_pack<'a>(
341
- &mut self,
342
- commits: impl IntoIterator<Item = (CommitDraftRef<'a>, Vec<ChangeRef<'a>>, Vec<ChangeRef<'a>>)>,
343
- write_authored_change_pack: bool,
344
- ) -> Result<Vec<StagedCommitStoreCommit>, LixError> {
345
- let commits = commits
346
- .into_iter()
347
- .map(
348
- |(commit, authored_changes, adopted_changes)| PendingCommitDraft {
349
- commit,
350
- authored_changes,
351
- adopted_changes,
352
- },
353
- )
354
- .collect::<Vec<_>>();
355
- let adopted_locators = validate_stage_commits(self.store, &commits).await?;
356
- let mut staged = Vec::with_capacity(commits.len());
357
- for commit in commits {
358
- let mut adopted_changes = Vec::with_capacity(commit.adopted_changes.len());
359
- for change in &commit.adopted_changes {
360
- let Some(locator) = adopted_locators.get(change.id) else {
361
- return Err(LixError::new(
362
- LixError::CODE_INTERNAL_ERROR,
363
- format!(
364
- "validated adopted commit-store change id '{}' has no locator",
365
- change.id
366
- ),
367
- ));
368
- };
369
- adopted_changes.push(locator.clone());
370
- }
371
- staged.push(if write_authored_change_pack {
372
- crate::commit_store::storage::stage_commit(
373
- self.writes,
374
- commit.commit,
375
- commit.authored_changes,
376
- adopted_changes,
377
- )?
378
- } else {
379
- crate::commit_store::storage::stage_commit_with_external_authored_pack(
380
- self.writes,
381
- commit.commit,
382
- commit.authored_changes,
383
- adopted_changes,
384
- )?
385
- });
386
- }
387
- Ok(staged)
388
- }
389
- }
390
-
391
- async fn validate_stage_commits<'a>(
392
- store: &mut (impl StorageReader + ?Sized),
393
- commits: &[PendingCommitDraft<'a>],
394
- ) -> Result<BTreeMap<&'a str, ChangeLocator>, LixError> {
395
- validate_new_changes_absent(store, commits).await?;
396
- validate_adopted_changes_present(store, commits).await
397
- }
398
-
399
- async fn scan_changes_from_commit_store(
400
- store: &mut (impl StorageReader + ?Sized),
401
- request: &ChangeScanRequest,
402
- ) -> Result<Vec<LocatedChange>, LixError> {
403
- let limit = request.limit.unwrap_or(usize::MAX);
404
- let commits = crate::commit_store::storage::scan_commits(store).await?;
405
- let mut changes = Vec::new();
406
- for commit in commits {
407
- if changes.len() >= limit {
408
- break;
409
- }
410
- for pack_id in 0..commit.change_pack_count {
411
- if changes.len() >= limit {
412
- break;
413
- }
414
- let Some(mut pack_changes) =
415
- crate::commit_store::storage::load_change_pack(store, &commit.id, pack_id).await?
416
- else {
417
- return Err(missing_pack_error("change", &commit.id, pack_id));
418
- };
419
- let remaining = limit - changes.len();
420
- if pack_changes.len() > remaining {
421
- pack_changes.truncate(remaining);
422
- }
423
- changes.extend(pack_changes.into_iter().map(|record| LocatedChange {
424
- record,
425
- source_commit_id: commit.id.clone(),
426
- source_pack_id: pack_id,
427
- }));
428
- }
429
- if changes.len() < limit {
430
- changes.push(located_commit_header_change(commit, 0));
431
- }
432
- }
433
- Ok(changes)
434
- }
435
-
436
- async fn load_change_by_locator(
437
- store: &mut (impl StorageReader + ?Sized),
438
- locator: &ChangeLocator,
439
- expected_change_id: &str,
440
- ) -> Result<Change, LixError> {
441
- let Some(changes) = crate::commit_store::storage::load_change_pack(
442
- store,
443
- &locator.source_commit_id,
444
- locator.source_pack_id,
445
- )
446
- .await?
447
- else {
448
- return Err(missing_pack_error(
449
- "change",
450
- &locator.source_commit_id,
451
- locator.source_pack_id,
452
- ));
453
- };
454
- let change = changes
455
- .get(usize::try_from(locator.source_ordinal).map_err(|_| {
456
- LixError::new(
457
- LixError::CODE_INTERNAL_ERROR,
458
- "commit-store change locator ordinal does not fit usize",
459
- )
460
- })?)
461
- .ok_or_else(|| {
462
- LixError::new(
463
- LixError::CODE_INTERNAL_ERROR,
464
- format!(
465
- "commit-store change locator for '{}' points past pack '{}' in commit '{}'",
466
- expected_change_id, locator.source_pack_id, locator.source_commit_id
467
- ),
468
- )
469
- })?;
470
- if change.id != expected_change_id || change.id != locator.change_id {
471
- return Err(LixError::new(
472
- LixError::CODE_INTERNAL_ERROR,
473
- format!(
474
- "commit-store change locator expected '{}' but found '{}'",
475
- expected_change_id, change.id
476
- ),
477
- ));
478
- }
479
- Ok(change.clone())
480
- }
481
-
482
- async fn load_change_by_locator_cached(
483
- store: &mut (impl StorageReader + ?Sized),
484
- packs_by_locator: &mut BTreeMap<(String, u32), Vec<Change>>,
485
- locator: &ChangeLocator,
486
- expected_change_id: &str,
487
- ) -> Result<Change, LixError> {
488
- let key = (locator.source_commit_id.clone(), locator.source_pack_id);
489
- if !packs_by_locator.contains_key(&key) {
490
- let Some(changes) = crate::commit_store::storage::load_change_pack(
491
- store,
492
- &locator.source_commit_id,
493
- locator.source_pack_id,
494
- )
495
- .await?
496
- else {
497
- return Err(missing_pack_error(
498
- "change",
499
- &locator.source_commit_id,
500
- locator.source_pack_id,
501
- ));
502
- };
503
- packs_by_locator.insert(key.clone(), changes);
504
- }
505
- let changes = packs_by_locator.get(&key).ok_or_else(|| {
506
- LixError::new(
507
- LixError::CODE_INTERNAL_ERROR,
508
- "commit-store change pack cache lost a loaded pack",
509
- )
510
- })?;
511
- let change = changes
512
- .get(usize::try_from(locator.source_ordinal).map_err(|_| {
513
- LixError::new(
514
- LixError::CODE_INTERNAL_ERROR,
515
- "commit-store change locator ordinal does not fit usize",
516
- )
517
- })?)
518
- .ok_or_else(|| {
519
- LixError::new(
520
- LixError::CODE_INTERNAL_ERROR,
521
- format!(
522
- "commit-store change locator for '{}' points past pack '{}' in commit '{}'",
523
- expected_change_id, locator.source_pack_id, locator.source_commit_id
524
- ),
525
- )
526
- })?;
527
- if change.id != expected_change_id || change.id != locator.change_id {
528
- return Err(LixError::new(
529
- LixError::CODE_INTERNAL_ERROR,
530
- format!(
531
- "commit-store change locator expected '{}' but found '{}'",
532
- expected_change_id, change.id
533
- ),
534
- ));
535
- }
536
- Ok(change.clone())
537
- }
538
-
539
- fn commit_header_change(commit: Commit) -> Change {
540
- Change {
541
- id: commit.change_id,
542
- entity_id: crate::entity_identity::EntityIdentity::single(commit.id),
543
- schema_key: "lix_commit".to_string(),
544
- file_id: None,
545
- snapshot_ref: None,
546
- metadata_ref: None,
547
- created_at: commit.created_at,
548
- }
549
- }
550
-
551
- fn located_commit_header_change(commit: Commit, source_pack_id: u32) -> LocatedChange {
552
- let source_commit_id = commit.id.clone();
553
- LocatedChange {
554
- record: commit_header_change(commit),
555
- source_commit_id,
556
- source_pack_id,
557
- }
558
- }
559
-
560
- fn missing_pack_error(label: &str, commit_id: &str, pack_id: u32) -> LixError {
561
- LixError::new(
562
- LixError::CODE_INTERNAL_ERROR,
563
- format!("commit-store missing {label} pack ({commit_id}, {pack_id})"),
564
- )
565
- }
566
-
567
- async fn validate_new_changes_absent<'a>(
568
- store: &mut (impl StorageReader + ?Sized),
569
- commits: &[PendingCommitDraft<'a>],
570
- ) -> Result<(), LixError> {
571
- let mut change_ids = Vec::new();
572
- let mut seen_change_ids = BTreeSet::new();
573
- for commit in commits {
574
- if !seen_change_ids.insert(commit.commit.change_id) {
575
- return Err(duplicate_change_id_error(commit.commit.change_id));
576
- }
577
- change_ids.push(commit.commit.change_id.to_string());
578
- for change in &commit.authored_changes {
579
- if !seen_change_ids.insert(change.id) {
580
- return Err(duplicate_change_id_error(change.id));
581
- }
582
- change_ids.push(change.id.to_string());
583
- }
584
- }
585
-
586
- let reader = CommitStoreContext::new().reader(&mut *store);
587
- let existing_changes = reader.load_change_index_entries(&change_ids).await?;
588
- for (change_id, existing) in change_ids.iter().zip(existing_changes) {
589
- if existing.is_some() {
590
- return Err(LixError::new(
591
- LixError::CODE_UNIQUE,
592
- format!("commit-store change id '{}' already exists", change_id),
593
- ));
594
- }
595
- }
596
- Ok(())
597
- }
598
-
599
- async fn validate_adopted_changes_present<'a>(
600
- store: &mut (impl StorageReader + ?Sized),
601
- commits: &[PendingCommitDraft<'a>],
602
- ) -> Result<BTreeMap<&'a str, ChangeLocator>, LixError> {
603
- let mut expected_changes = Vec::new();
604
- let mut seen_change_ids = BTreeSet::new();
605
- for commit in commits {
606
- for change in &commit.adopted_changes {
607
- if !seen_change_ids.insert(change.id) {
608
- return Err(LixError::new(
609
- LixError::CODE_UNIQUE,
610
- format!(
611
- "adopted commit-store change id '{}' appears more than once in the same transaction",
612
- change.id
613
- ),
614
- ));
615
- }
616
- expected_changes.push(*change);
617
- }
618
- }
619
- if expected_changes.is_empty() {
620
- return Ok(BTreeMap::new());
621
- }
622
-
623
- let change_ids = expected_changes
624
- .iter()
625
- .map(|change| change.id.to_string())
626
- .collect::<Vec<_>>();
627
- let reader = CommitStoreContext::new().reader(&mut *store);
628
- let existing_entries = reader.load_change_index_entries(&change_ids).await?;
629
- let mut locators_by_change_id = BTreeMap::new();
630
- for (expected, existing) in expected_changes.into_iter().zip(existing_entries) {
631
- match existing {
632
- Some(ChangeIndexEntry::PackedChange { locator }) => {
633
- let existing_change = load_packed_change(&reader, &locator, expected.id).await?;
634
- if !change_matches_ref(&existing_change, expected) {
635
- let entity_id = existing_change
636
- .entity_id
637
- .as_json_array_text()
638
- .unwrap_or_else(|_| "<invalid entity_id>".to_string());
639
- return Err(LixError::new(
640
- LixError::CODE_UNIQUE,
641
- format!(
642
- "adopted commit-store change id '{}' exists with different content for schema '{}' entity '{}'",
643
- expected.id, existing_change.schema_key, entity_id
644
- ),
645
- ));
646
- }
647
- locators_by_change_id.insert(expected.id, locator);
648
- }
649
- Some(ChangeIndexEntry::CommitHeader { .. }) => {
650
- return Err(LixError::new(
651
- LixError::CODE_INTERNAL_ERROR,
652
- format!(
653
- "adopted commit-store change id '{}' resolves to a commit header, not a packed state change",
654
- expected.id
655
- ),
656
- ));
657
- }
658
- None => {
659
- return Err(LixError::new(
660
- LixError::CODE_INTERNAL_ERROR,
661
- format!(
662
- "adopted commit-store change id '{}' does not exist",
663
- expected.id
664
- ),
665
- ));
666
- }
667
- }
668
- }
669
- Ok(locators_by_change_id)
670
- }
671
-
672
- async fn load_packed_change<S>(
673
- reader: &CommitStoreReader<S>,
674
- locator: &ChangeLocator,
675
- expected_change_id: &str,
676
- ) -> Result<Change, LixError>
677
- where
678
- S: StorageReader,
679
- {
680
- let pack = reader
681
- .load_change_pack(&locator.source_commit_id, locator.source_pack_id)
682
- .await?
683
- .ok_or_else(|| {
684
- LixError::new(
685
- LixError::CODE_INTERNAL_ERROR,
686
- format!(
687
- "commit-store change pack '{}:{}' for change '{}' is missing",
688
- locator.source_commit_id, locator.source_pack_id, expected_change_id
689
- ),
690
- )
691
- })?;
692
- let change = pack
693
- .get(usize::try_from(locator.source_ordinal).map_err(|_| {
694
- LixError::new(
695
- LixError::CODE_INTERNAL_ERROR,
696
- "commit-store change locator ordinal exceeds usize",
697
- )
698
- })?)
699
- .ok_or_else(|| {
700
- LixError::new(
701
- LixError::CODE_INTERNAL_ERROR,
702
- format!(
703
- "commit-store change locator '{}' points past pack length",
704
- expected_change_id
705
- ),
706
- )
707
- })?
708
- .clone();
709
- if change.id != expected_change_id {
710
- return Err(LixError::new(
711
- LixError::CODE_INTERNAL_ERROR,
712
- format!(
713
- "commit-store change locator expected '{}' but loaded '{}'",
714
- expected_change_id, change.id
715
- ),
716
- ));
717
- }
718
- Ok(change)
719
- }
720
-
721
- fn change_matches_ref(change: &Change, expected: ChangeRef<'_>) -> bool {
722
- change.id == expected.id
723
- && &change.entity_id == expected.entity_id
724
- && change.schema_key == expected.schema_key
725
- && change.file_id.as_deref() == expected.file_id
726
- && change.snapshot_ref.as_ref() == expected.snapshot_ref
727
- && change.metadata_ref.as_ref() == expected.metadata_ref
728
- && change.created_at == expected.created_at
729
- }
730
-
731
- fn duplicate_change_id_error(change_id: &str) -> LixError {
732
- LixError::new(
733
- LixError::CODE_UNIQUE,
734
- format!(
735
- "commit-store change id '{}' appears more than once in the same transaction",
736
- change_id
737
- ),
738
- )
739
- }
740
-
741
- #[cfg(test)]
742
- mod tests {
743
- use std::sync::Arc;
744
-
745
- use crate::backend::testing::UnitTestBackend;
746
- use crate::commit_store::{
747
- ChangeIndexEntry, ChangeLocator, CommitDraftRef, CommitStoreContext,
748
- };
749
- use crate::entity_identity::EntityIdentity;
750
- use crate::json_store::JsonRef;
751
- use crate::storage::{StorageContext, StorageWriteSet, StorageWriteTransaction};
752
-
753
- use super::*;
754
-
755
- #[tokio::test]
756
- async fn load_changes_materializes_commit_header_and_packed_change() {
757
- let storage = StorageContext::new(Arc::new(UnitTestBackend::new()));
758
- let mut transaction = storage
759
- .begin_write_transaction()
760
- .await
761
- .expect("transaction should open");
762
- let mut writes = StorageWriteSet::new();
763
- let parent_ids = vec!["parent-1".to_string()];
764
- let author_account_ids = vec!["author-1".to_string()];
765
- let commit_id = "commit-1".to_string();
766
- let commit_change_id = "commit-change-1".to_string();
767
- let authored_change = test_change("change-1");
768
-
769
- CommitStoreContext::new()
770
- .writer(transaction.as_mut(), &mut writes)
771
- .stage_commit_draft(
772
- CommitDraftRef {
773
- id: &commit_id,
774
- change_id: &commit_change_id,
775
- parent_ids: &parent_ids,
776
- author_account_ids: &author_account_ids,
777
- created_at: "2026-01-01T00:00:00Z",
778
- },
779
- vec![authored_change.as_ref()],
780
- Vec::new(),
781
- )
782
- .await
783
- .expect("commit should stage");
784
- writes
785
- .apply(&mut transaction.as_mut())
786
- .await
787
- .expect("writes should apply");
788
- transaction.commit().await.expect("commit should persist");
789
-
790
- let reader = CommitStoreContext::new().reader(storage.clone());
791
- let index_entries = reader
792
- .load_change_index_entries(&[
793
- commit_change_id.clone(),
794
- authored_change.id.clone(),
795
- "missing-change".to_string(),
796
- ])
797
- .await
798
- .expect("index entries should load");
799
- assert_eq!(
800
- index_entries,
801
- vec![
802
- Some(ChangeIndexEntry::CommitHeader {
803
- commit_id: commit_id.clone(),
804
- change_id: commit_change_id.clone(),
805
- }),
806
- Some(ChangeIndexEntry::PackedChange {
807
- locator: ChangeLocator {
808
- source_commit_id: commit_id.clone(),
809
- source_pack_id: 0,
810
- source_ordinal: 0,
811
- change_id: authored_change.id.clone(),
812
- },
813
- }),
814
- None,
815
- ]
816
- );
817
-
818
- let changes = reader
819
- .load_changes(&[
820
- commit_change_id.clone(),
821
- authored_change.id.clone(),
822
- "missing-change".to_string(),
823
- ])
824
- .await
825
- .expect("changes should load");
826
- assert_eq!(changes.len(), 3);
827
-
828
- let header_change = changes[0]
829
- .as_ref()
830
- .expect("commit-header change should materialize");
831
- assert_eq!(header_change.id, commit_change_id);
832
- assert_eq!(header_change.entity_id, EntityIdentity::single(&commit_id));
833
- assert_eq!(header_change.schema_key, "lix_commit");
834
- assert_eq!(header_change.file_id, None);
835
- assert_eq!(header_change.snapshot_ref, None);
836
- assert_eq!(header_change.metadata_ref, None);
837
- assert_eq!(header_change.created_at, "2026-01-01T00:00:00Z");
838
-
839
- assert_eq!(
840
- changes[1]
841
- .as_ref()
842
- .expect("packed change should decode from change pack"),
843
- &authored_change
844
- );
845
- assert_eq!(changes[2], None);
846
- }
847
-
848
- #[tokio::test]
849
- async fn load_commit_changes_returns_equivalent_authored_and_adopted_changes() {
850
- let storage = StorageContext::new(Arc::new(UnitTestBackend::new()));
851
- let authored_change = test_change("shared-change-1");
852
-
853
- stage_test_commit(
854
- storage.clone(),
855
- "source-commit",
856
- "source-commit-change",
857
- vec![authored_change.as_ref()],
858
- Vec::new(),
859
- )
860
- .await;
861
- stage_test_commit(
862
- storage.clone(),
863
- "adopting-commit",
864
- "adopting-commit-change",
865
- Vec::new(),
866
- vec![authored_change.as_ref()],
867
- )
868
- .await;
869
-
870
- let reader = CommitStoreContext::new().reader(storage.clone());
871
- let source_changes = reader
872
- .load_commit_changes("source-commit")
873
- .await
874
- .expect("source commit changes should load");
875
- let adopting_changes = reader
876
- .load_commit_changes("adopting-commit")
877
- .await
878
- .expect("adopting commit changes should load");
879
-
880
- assert_eq!(source_changes, vec![authored_change.clone()]);
881
- assert_eq!(adopting_changes, source_changes);
882
- assert_eq!(
883
- reader
884
- .load_membership_pack("adopting-commit", 0)
885
- .await
886
- .expect("membership pack should load"),
887
- Some(vec![ChangeLocator {
888
- source_commit_id: "source-commit".to_string(),
889
- source_pack_id: 0,
890
- source_ordinal: 0,
891
- change_id: authored_change.id.clone(),
892
- }])
893
- );
894
- }
895
-
896
- async fn stage_test_commit(
897
- storage: StorageContext,
898
- commit_id: &str,
899
- commit_change_id: &str,
900
- authored_changes: Vec<ChangeRef<'_>>,
901
- adopted_changes: Vec<ChangeRef<'_>>,
902
- ) {
903
- let mut transaction = storage
904
- .begin_write_transaction()
905
- .await
906
- .expect("transaction should open");
907
- let mut writes = StorageWriteSet::new();
908
- let parent_ids = Vec::new();
909
- let author_account_ids = Vec::new();
910
-
911
- CommitStoreContext::new()
912
- .writer(transaction.as_mut(), &mut writes)
913
- .stage_commit_draft(
914
- CommitDraftRef {
915
- id: commit_id,
916
- change_id: commit_change_id,
917
- parent_ids: &parent_ids,
918
- author_account_ids: &author_account_ids,
919
- created_at: "2026-01-01T00:00:00Z",
920
- },
921
- authored_changes,
922
- adopted_changes,
923
- )
924
- .await
925
- .expect("commit should stage");
926
- writes
927
- .apply(&mut transaction.as_mut())
928
- .await
929
- .expect("writes should apply");
930
- transaction.commit().await.expect("commit should persist");
931
- }
932
-
933
- fn test_change(id: &str) -> Change {
934
- Change {
935
- id: id.to_string(),
936
- entity_id: EntityIdentity::single("entity-1"),
937
- schema_key: "test_schema".to_string(),
938
- file_id: Some("file-1".to_string()),
939
- snapshot_ref: Some(JsonRef::from_hash_bytes([1; 32])),
940
- metadata_ref: Some(JsonRef::from_hash_bytes([2; 32])),
941
- created_at: "2026-01-02T00:00:00Z".to_string(),
942
- }
943
- }
944
- }