@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,84 +0,0 @@
1
- use crate::commit_store::{LocatedChange, MaterializedChange};
2
- use crate::json_store::{JsonLoadRequestRef, JsonReadScopeRef, JsonRef, JsonStoreReader};
3
- use crate::storage::StorageReader;
4
- use crate::{parse_row_metadata, LixError};
5
-
6
- pub(crate) async fn materialize_change<S>(
7
- json_reader: &mut JsonStoreReader<S>,
8
- located: LocatedChange,
9
- ) -> Result<MaterializedChange, LixError>
10
- where
11
- S: StorageReader,
12
- {
13
- let change = located.record;
14
- let pack_ids = [located.source_pack_id];
15
- let scope = JsonReadScopeRef::CommitPacks {
16
- commit_id: &located.source_commit_id,
17
- pack_ids: &pack_ids,
18
- };
19
- let snapshot_content = load_optional_json_text(
20
- json_reader,
21
- change.snapshot_ref.as_ref(),
22
- scope,
23
- "snapshot_ref",
24
- )
25
- .await?;
26
- let metadata = match load_optional_json_text(
27
- json_reader,
28
- change.metadata_ref.as_ref(),
29
- scope,
30
- "metadata_ref",
31
- )
32
- .await?
33
- {
34
- Some(value) => Some(parse_row_metadata(
35
- &value,
36
- "commit_store change metadata_ref",
37
- )?),
38
- None => None,
39
- };
40
- Ok(MaterializedChange {
41
- id: change.id,
42
- entity_id: change.entity_id,
43
- schema_key: change.schema_key,
44
- file_id: change.file_id,
45
- snapshot_content,
46
- metadata,
47
- created_at: change.created_at,
48
- })
49
- }
50
-
51
- async fn load_optional_json_text<S>(
52
- json_reader: &mut JsonStoreReader<S>,
53
- json_ref: Option<&JsonRef>,
54
- scope: JsonReadScopeRef<'_>,
55
- field: &str,
56
- ) -> Result<Option<String>, LixError>
57
- where
58
- S: StorageReader,
59
- {
60
- let Some(json_ref) = json_ref else {
61
- return Ok(None);
62
- };
63
- let batch = json_reader
64
- .load_bytes_many(JsonLoadRequestRef {
65
- refs: std::slice::from_ref(json_ref),
66
- scope,
67
- })
68
- .await?;
69
- let Some(bytes) = batch.into_values().into_iter().next().flatten() else {
70
- return Err(LixError::new(
71
- LixError::CODE_INTERNAL_ERROR,
72
- format!(
73
- "commit_store change {field} '{}' is missing",
74
- json_ref.to_hex()
75
- ),
76
- ));
77
- };
78
- String::from_utf8(bytes).map(Some).map_err(|error| {
79
- LixError::new(
80
- LixError::CODE_INTERNAL_ERROR,
81
- format!("commit_store change {field} is not UTF-8 JSON: {error}"),
82
- )
83
- })
84
- }
@@ -1,16 +0,0 @@
1
- pub(crate) mod codec;
2
- mod context;
3
- mod materialization;
4
- pub(crate) mod storage;
5
- mod types;
6
-
7
- #[allow(unused_imports)]
8
- pub(crate) use context::{CommitStoreContext, CommitStoreReader, CommitStoreWriter};
9
- #[allow(unused_imports)]
10
- pub(crate) use materialization::materialize_change;
11
- #[allow(unused_imports)]
12
- pub(crate) use types::{
13
- Change, ChangeIndexEntry, ChangeLocator, ChangeLocatorRef, ChangePack, ChangePackView,
14
- ChangeRef, ChangeScanRequest, Commit, CommitDraftRef, LocatedChange, MaterializedChange,
15
- MembershipPack, MembershipPackView, StagedCommitStoreCommit, StoredCommitRef,
16
- };
@@ -1,600 +0,0 @@
1
- use crate::commit_store::{
2
- Change, ChangeIndexEntry, ChangeLocator, ChangeRef, Commit, CommitDraftRef,
3
- StagedCommitStoreCommit, StoredCommitRef,
4
- };
5
- use crate::storage::{
6
- KvGetGroup, KvGetRequest, KvScanRange, KvScanRequest, StorageReader, StorageWriteSet,
7
- };
8
- use crate::LixError;
9
- use std::collections::{BTreeMap, BTreeSet};
10
-
11
- pub(crate) const COMMIT_NAMESPACE: &str = "commit_store.commit";
12
- pub(crate) const CHANGE_PACK_NAMESPACE: &str = "commit_store.change_pack";
13
- pub(crate) const MEMBERSHIP_PACK_NAMESPACE: &str = "commit_store.membership_pack";
14
-
15
- const SINGLE_PACK_ID: u32 = 0;
16
-
17
- pub(crate) fn stage_commit(
18
- writes: &mut StorageWriteSet,
19
- commit: CommitDraftRef<'_>,
20
- authored_changes: Vec<ChangeRef<'_>>,
21
- adopted_changes: Vec<ChangeLocator>,
22
- ) -> Result<StagedCommitStoreCommit, LixError> {
23
- stage_commit_with_authored_pack(writes, commit, authored_changes, adopted_changes, true)
24
- }
25
-
26
- pub(crate) fn stage_commit_with_external_authored_pack(
27
- writes: &mut StorageWriteSet,
28
- commit: CommitDraftRef<'_>,
29
- authored_changes: Vec<ChangeRef<'_>>,
30
- adopted_changes: Vec<ChangeLocator>,
31
- ) -> Result<StagedCommitStoreCommit, LixError> {
32
- stage_commit_with_authored_pack(writes, commit, authored_changes, adopted_changes, false)
33
- }
34
-
35
- fn stage_commit_with_authored_pack(
36
- writes: &mut StorageWriteSet,
37
- commit: CommitDraftRef<'_>,
38
- authored_changes: Vec<ChangeRef<'_>>,
39
- adopted_changes: Vec<ChangeLocator>,
40
- write_authored_change_pack: bool,
41
- ) -> Result<StagedCommitStoreCommit, LixError> {
42
- let stored_commit = StoredCommitRef {
43
- id: commit.id,
44
- change_id: commit.change_id,
45
- parent_ids: commit.parent_ids,
46
- author_account_ids: commit.author_account_ids,
47
- created_at: commit.created_at,
48
- change_pack_count: if authored_changes.is_empty() { 0 } else { 1 },
49
- membership_pack_count: if adopted_changes.is_empty() { 0 } else { 1 },
50
- };
51
-
52
- writes.put(
53
- COMMIT_NAMESPACE,
54
- commit_key(commit.id),
55
- crate::commit_store::codec::encode_commit_ref(stored_commit)?,
56
- );
57
-
58
- let mut authored_locators = Vec::with_capacity(authored_changes.len());
59
- if !authored_changes.is_empty() {
60
- if write_authored_change_pack {
61
- writes.put(
62
- CHANGE_PACK_NAMESPACE,
63
- pack_key(commit.id, SINGLE_PACK_ID)?,
64
- crate::commit_store::codec::encode_change_pack(
65
- commit.id,
66
- SINGLE_PACK_ID,
67
- &authored_changes,
68
- )?,
69
- );
70
- }
71
- for (source_ordinal, change) in authored_changes.iter().enumerate() {
72
- authored_locators.push(ChangeLocator {
73
- source_commit_id: commit.id.to_string(),
74
- source_pack_id: SINGLE_PACK_ID,
75
- source_ordinal: u32::try_from(source_ordinal).map_err(|_| {
76
- LixError::new(
77
- LixError::CODE_INTERNAL_ERROR,
78
- "commit-store change pack ordinal exceeds u32",
79
- )
80
- })?,
81
- change_id: change.id.to_string(),
82
- });
83
- }
84
- }
85
-
86
- if !adopted_changes.is_empty() {
87
- writes.put(
88
- MEMBERSHIP_PACK_NAMESPACE,
89
- pack_key(commit.id, SINGLE_PACK_ID)?,
90
- crate::commit_store::codec::encode_membership_pack(
91
- commit.id,
92
- SINGLE_PACK_ID,
93
- adopted_changes.iter().map(ChangeLocator::as_ref),
94
- )?,
95
- );
96
- }
97
-
98
- Ok(StagedCommitStoreCommit {
99
- authored_locators,
100
- adopted_locators: adopted_changes,
101
- })
102
- }
103
-
104
- pub(crate) async fn load_commit(
105
- store: &mut (impl StorageReader + ?Sized),
106
- commit_id: &str,
107
- ) -> Result<Option<Commit>, LixError> {
108
- let Some(bytes) = get_one(store, COMMIT_NAMESPACE, commit_key(commit_id)).await? else {
109
- return Ok(None);
110
- };
111
- crate::commit_store::codec::decode_commit(&bytes).map(Some)
112
- }
113
-
114
- pub(crate) async fn scan_commits(
115
- store: &mut (impl StorageReader + ?Sized),
116
- ) -> Result<Vec<Commit>, LixError> {
117
- let page = store
118
- .scan_values(KvScanRequest {
119
- namespace: COMMIT_NAMESPACE.to_string(),
120
- range: KvScanRange::prefix(Vec::new()),
121
- after: None,
122
- limit: usize::MAX,
123
- })
124
- .await?;
125
- page.values
126
- .iter()
127
- .map(|bytes| crate::commit_store::codec::decode_commit(bytes))
128
- .collect()
129
- }
130
-
131
- pub(crate) async fn load_change_pack(
132
- store: &mut (impl StorageReader + ?Sized),
133
- commit_id: &str,
134
- pack_id: u32,
135
- ) -> Result<Option<Vec<Change>>, LixError> {
136
- let Some(bytes) = get_one(store, CHANGE_PACK_NAMESPACE, pack_key(commit_id, pack_id)?).await?
137
- else {
138
- return load_tracked_authored_change_pack(store, commit_id, pack_id).await;
139
- };
140
- let (stored_commit_id, stored_pack_id, changes) =
141
- crate::commit_store::codec::decode_change_pack(&bytes)?;
142
- ensure_pack_identity(
143
- "change pack",
144
- commit_id,
145
- pack_id,
146
- &stored_commit_id,
147
- stored_pack_id,
148
- )?;
149
- Ok(Some(changes))
150
- }
151
-
152
- pub(crate) async fn load_tracked_authored_change_pack(
153
- store: &mut (impl StorageReader + ?Sized),
154
- commit_id: &str,
155
- pack_id: u32,
156
- ) -> Result<Option<Vec<Change>>, LixError> {
157
- let Some(delta_entries) = crate::tracked_state::load_delta_pack(store, commit_id).await? else {
158
- return Ok(None);
159
- };
160
- let mut changes_by_ordinal = BTreeMap::<u32, Change>::new();
161
- for delta in delta_entries {
162
- let locator = &delta.value.change_locator;
163
- if locator.source_commit_id != commit_id || locator.source_pack_id != pack_id {
164
- continue;
165
- }
166
- let ordinal = locator.source_ordinal;
167
- let change = Change {
168
- id: locator.change_id.clone(),
169
- entity_id: delta.key.entity_id,
170
- schema_key: delta.key.schema_key,
171
- file_id: delta.key.file_id,
172
- snapshot_ref: delta.value.snapshot_ref,
173
- metadata_ref: delta.value.metadata_ref,
174
- created_at: delta.value.updated_at,
175
- };
176
- if changes_by_ordinal.insert(ordinal, change).is_some() {
177
- return Err(LixError::new(
178
- LixError::CODE_INTERNAL_ERROR,
179
- format!(
180
- "tracked authored change pack ({commit_id}, {pack_id}) has duplicate ordinal {ordinal}"
181
- ),
182
- ));
183
- }
184
- }
185
- if changes_by_ordinal.is_empty() {
186
- return Ok(None);
187
- }
188
- let mut changes = Vec::with_capacity(changes_by_ordinal.len());
189
- for (expected_ordinal, (ordinal, change)) in (0u32..).zip(changes_by_ordinal) {
190
- if ordinal != expected_ordinal {
191
- return Err(LixError::new(
192
- LixError::CODE_INTERNAL_ERROR,
193
- format!(
194
- "tracked authored change pack ({commit_id}, {pack_id}) is missing ordinal {expected_ordinal}"
195
- ),
196
- ));
197
- }
198
- changes.push(change);
199
- }
200
- Ok(Some(changes))
201
- }
202
-
203
- pub(crate) async fn load_membership_pack(
204
- store: &mut (impl StorageReader + ?Sized),
205
- commit_id: &str,
206
- pack_id: u32,
207
- ) -> Result<Option<Vec<ChangeLocator>>, LixError> {
208
- let Some(bytes) = get_one(
209
- store,
210
- MEMBERSHIP_PACK_NAMESPACE,
211
- pack_key(commit_id, pack_id)?,
212
- )
213
- .await?
214
- else {
215
- return Ok(None);
216
- };
217
- let (stored_commit_id, stored_pack_id, members) =
218
- crate::commit_store::codec::decode_membership_pack(&bytes)?;
219
- ensure_pack_identity(
220
- "membership pack",
221
- commit_id,
222
- pack_id,
223
- &stored_commit_id,
224
- stored_pack_id,
225
- )?;
226
- Ok(Some(members))
227
- }
228
-
229
- pub(crate) async fn load_change_index_entries(
230
- store: &mut (impl StorageReader + ?Sized),
231
- change_ids: &[String],
232
- ) -> Result<Vec<Option<ChangeIndexEntry>>, LixError> {
233
- if change_ids.is_empty() {
234
- return Ok(Vec::new());
235
- }
236
-
237
- let mut unresolved = change_ids.iter().cloned().collect::<BTreeSet<_>>();
238
- let mut entries_by_change_id = BTreeMap::new();
239
- let commits = scan_commits(store).await?;
240
- for commit in commits {
241
- if unresolved.remove(&commit.change_id) {
242
- entries_by_change_id.insert(
243
- commit.change_id.clone(),
244
- ChangeIndexEntry::CommitHeader {
245
- commit_id: commit.id.clone(),
246
- change_id: commit.change_id.clone(),
247
- },
248
- );
249
- }
250
- if unresolved.is_empty() {
251
- break;
252
- }
253
-
254
- for pack_id in 0..commit.change_pack_count {
255
- let Some(changes) = load_change_pack(store, &commit.id, pack_id).await? else {
256
- return Err(LixError::new(
257
- LixError::CODE_INTERNAL_ERROR,
258
- format!(
259
- "commit-store missing change pack ({}, {pack_id})",
260
- commit.id
261
- ),
262
- ));
263
- };
264
- for (source_ordinal, change) in changes.iter().enumerate() {
265
- if !unresolved.remove(&change.id) {
266
- continue;
267
- }
268
- entries_by_change_id.insert(
269
- change.id.clone(),
270
- ChangeIndexEntry::PackedChange {
271
- locator: ChangeLocator {
272
- source_commit_id: commit.id.clone(),
273
- source_pack_id: pack_id,
274
- source_ordinal: u32::try_from(source_ordinal).map_err(|_| {
275
- LixError::new(
276
- LixError::CODE_INTERNAL_ERROR,
277
- "commit-store change pack ordinal exceeds u32",
278
- )
279
- })?,
280
- change_id: change.id.clone(),
281
- },
282
- },
283
- );
284
- if unresolved.is_empty() {
285
- break;
286
- }
287
- }
288
- if unresolved.is_empty() {
289
- break;
290
- }
291
- }
292
- if unresolved.is_empty() {
293
- break;
294
- }
295
- }
296
-
297
- Ok(change_ids
298
- .iter()
299
- .map(|change_id| entries_by_change_id.get(change_id).cloned())
300
- .collect())
301
- }
302
-
303
- async fn get_one(
304
- store: &mut (impl StorageReader + ?Sized),
305
- namespace: &str,
306
- key: Vec<u8>,
307
- ) -> Result<Option<Vec<u8>>, LixError> {
308
- Ok(store
309
- .get_values(KvGetRequest {
310
- groups: vec![KvGetGroup {
311
- namespace: namespace.to_string(),
312
- keys: vec![key],
313
- }],
314
- })
315
- .await?
316
- .groups
317
- .into_iter()
318
- .next()
319
- .and_then(|group| group.single_value_owned()))
320
- }
321
-
322
- fn ensure_pack_identity(
323
- label: &str,
324
- expected_commit_id: &str,
325
- expected_pack_id: u32,
326
- actual_commit_id: &str,
327
- actual_pack_id: u32,
328
- ) -> Result<(), LixError> {
329
- if actual_commit_id != expected_commit_id || actual_pack_id != expected_pack_id {
330
- return Err(LixError::new(
331
- LixError::CODE_INTERNAL_ERROR,
332
- format!(
333
- "commit-store {label} identity mismatch: expected ({expected_commit_id}, {expected_pack_id}), got ({actual_commit_id}, {actual_pack_id})"
334
- ),
335
- ));
336
- }
337
- Ok(())
338
- }
339
-
340
- fn commit_key(commit_id: &str) -> Vec<u8> {
341
- commit_id.as_bytes().to_vec()
342
- }
343
-
344
- fn pack_key(commit_id: &str, pack_id: u32) -> Result<Vec<u8>, LixError> {
345
- let commit_id_len = u32::try_from(commit_id.len()).map_err(|_| {
346
- LixError::new(
347
- LixError::CODE_INTERNAL_ERROR,
348
- "commit-store pack key commit id exceeds u32 length",
349
- )
350
- })?;
351
- let mut key = Vec::with_capacity(8 + commit_id.len());
352
- key.extend_from_slice(&commit_id_len.to_be_bytes());
353
- key.extend_from_slice(commit_id.as_bytes());
354
- key.extend_from_slice(&pack_id.to_be_bytes());
355
- Ok(key)
356
- }
357
-
358
- #[cfg(test)]
359
- mod tests {
360
- use std::sync::Arc;
361
-
362
- use crate::backend::testing::UnitTestBackend;
363
- use crate::commit_store::CommitDraftRef;
364
- use crate::entity_identity::EntityIdentity;
365
- use crate::json_store::JsonRef;
366
- use crate::storage::{StorageContext, StorageWriteTransaction};
367
- use crate::tracked_state::{TrackedStateContext, TrackedStateDeltaRef};
368
-
369
- use super::*;
370
-
371
- #[tokio::test]
372
- async fn stage_commit_writes_all_commit_store_namespaces() {
373
- let storage = StorageContext::new(Arc::new(UnitTestBackend::new()));
374
- let mut tx = storage
375
- .begin_write_transaction()
376
- .await
377
- .expect("transaction should open");
378
- let mut writes = StorageWriteSet::new();
379
- let commit = test_commit();
380
- let change = test_change("change-1");
381
- let adopted = ChangeLocator {
382
- source_commit_id: "source-commit".to_string(),
383
- source_pack_id: 3,
384
- source_ordinal: 7,
385
- change_id: "adopted-change".to_string(),
386
- };
387
-
388
- let staged = stage_commit(
389
- &mut writes,
390
- CommitDraftRef {
391
- id: &commit.id,
392
- change_id: &commit.change_id,
393
- parent_ids: &commit.parent_ids,
394
- author_account_ids: &commit.author_account_ids,
395
- created_at: &commit.created_at,
396
- },
397
- vec![change.as_ref()],
398
- vec![adopted.clone()],
399
- )
400
- .expect("commit should stage");
401
- writes
402
- .apply(&mut tx.as_mut())
403
- .await
404
- .expect("writes should apply");
405
- tx.commit().await.expect("commit should succeed");
406
-
407
- assert_eq!(
408
- staged.authored_locators,
409
- vec![ChangeLocator {
410
- source_commit_id: "commit-1".to_string(),
411
- source_pack_id: 0,
412
- source_ordinal: 0,
413
- change_id: "change-1".to_string(),
414
- }]
415
- );
416
- assert_eq!(staged.adopted_locators, vec![adopted.clone()]);
417
-
418
- let mut reader = storage.clone();
419
- assert_eq!(
420
- load_commit(&mut reader, "commit-1")
421
- .await
422
- .expect("commit should load"),
423
- Some(commit)
424
- );
425
- assert_eq!(
426
- load_change_pack(&mut reader, "commit-1", 0)
427
- .await
428
- .expect("change pack should load"),
429
- Some(vec![change])
430
- );
431
- assert_eq!(
432
- load_membership_pack(&mut reader, "commit-1", 0)
433
- .await
434
- .expect("membership pack should load"),
435
- Some(vec![adopted])
436
- );
437
-
438
- let index_entries = load_change_index_entries(
439
- &mut reader,
440
- &["commit-change-1".to_string(), "change-1".to_string()],
441
- )
442
- .await
443
- .expect("index entries should load");
444
- assert_eq!(
445
- index_entries,
446
- vec![
447
- Some(ChangeIndexEntry::CommitHeader {
448
- commit_id: "commit-1".to_string(),
449
- change_id: "commit-change-1".to_string(),
450
- }),
451
- Some(ChangeIndexEntry::PackedChange {
452
- locator: ChangeLocator {
453
- source_commit_id: "commit-1".to_string(),
454
- source_pack_id: 0,
455
- source_ordinal: 0,
456
- change_id: "change-1".to_string(),
457
- },
458
- }),
459
- ]
460
- );
461
- }
462
-
463
- #[tokio::test]
464
- async fn tracked_commit_change_pack_loads_from_delta_pack() {
465
- let storage = StorageContext::new(Arc::new(UnitTestBackend::new()));
466
- let mut tx = storage
467
- .begin_write_transaction()
468
- .await
469
- .expect("transaction should open");
470
- let mut writes = StorageWriteSet::new();
471
- let commit = test_commit();
472
- let change = test_change("change-1");
473
-
474
- let staged = stage_commit_with_external_authored_pack(
475
- &mut writes,
476
- CommitDraftRef {
477
- id: &commit.id,
478
- change_id: &commit.change_id,
479
- parent_ids: &commit.parent_ids,
480
- author_account_ids: &commit.author_account_ids,
481
- created_at: &commit.created_at,
482
- },
483
- vec![change.as_ref()],
484
- Vec::new(),
485
- )
486
- .expect("tracked commit should stage");
487
- let deltas = [TrackedStateDeltaRef {
488
- change: change.as_ref(),
489
- locator: staged.authored_locators[0].as_ref(),
490
- created_at: "2026-01-01T00:00:00Z",
491
- updated_at: "2026-01-02T00:00:00Z",
492
- }];
493
- TrackedStateContext::new()
494
- .writer(&mut tx.as_mut(), &mut writes)
495
- .stage_delta(&commit.id, None, &deltas)
496
- .await
497
- .expect("tracked delta should stage");
498
- writes
499
- .apply(&mut tx.as_mut())
500
- .await
501
- .expect("writes should apply");
502
- tx.commit().await.expect("commit should succeed");
503
-
504
- let mut reader = storage.clone();
505
- assert_eq!(
506
- get_one(
507
- &mut reader,
508
- CHANGE_PACK_NAMESPACE,
509
- pack_key("commit-1", 0).unwrap()
510
- )
511
- .await
512
- .expect("direct change pack lookup should succeed"),
513
- None
514
- );
515
- assert_eq!(
516
- load_change_pack(&mut reader, "commit-1", 0)
517
- .await
518
- .expect("tracked change pack should load"),
519
- Some(vec![Change {
520
- created_at: "2026-01-02T00:00:00Z".to_string(),
521
- ..change.clone()
522
- }])
523
- );
524
- assert_eq!(
525
- load_change_index_entries(&mut reader, &["change-1".to_string()])
526
- .await
527
- .expect("index entries should load"),
528
- vec![Some(ChangeIndexEntry::PackedChange {
529
- locator: staged.authored_locators[0].clone(),
530
- })]
531
- );
532
- }
533
-
534
- #[tokio::test]
535
- async fn tracked_commit_change_pack_rejects_sparse_delta_ordinals() {
536
- let storage = StorageContext::new(Arc::new(UnitTestBackend::new()));
537
- let mut tx = storage
538
- .begin_write_transaction()
539
- .await
540
- .expect("transaction should open");
541
- let mut writes = StorageWriteSet::new();
542
- let commit = test_commit();
543
- let change = test_change("change-1");
544
- let sparse_locator = ChangeLocator {
545
- source_commit_id: commit.id.clone(),
546
- source_pack_id: 0,
547
- source_ordinal: 1,
548
- change_id: change.id.clone(),
549
- };
550
- let deltas = [TrackedStateDeltaRef {
551
- change: change.as_ref(),
552
- locator: sparse_locator.as_ref(),
553
- created_at: "2026-01-01T00:00:00Z",
554
- updated_at: "2026-01-02T00:00:00Z",
555
- }];
556
- TrackedStateContext::new()
557
- .writer(&mut tx.as_mut(), &mut writes)
558
- .stage_delta(&commit.id, None, &deltas)
559
- .await
560
- .expect("tracked delta should stage");
561
- writes
562
- .apply(&mut tx.as_mut())
563
- .await
564
- .expect("writes should apply");
565
- tx.commit().await.expect("commit should succeed");
566
-
567
- let mut reader = storage.clone();
568
- let error = load_change_pack(&mut reader, "commit-1", 0)
569
- .await
570
- .expect_err("sparse tracked authored ordinals should reject");
571
- assert!(
572
- error.to_string().contains("missing ordinal 0"),
573
- "error should mention missing ordinal: {error}"
574
- );
575
- }
576
-
577
- fn test_commit() -> Commit {
578
- Commit {
579
- id: "commit-1".to_string(),
580
- change_id: "commit-change-1".to_string(),
581
- parent_ids: vec!["parent-1".to_string()],
582
- author_account_ids: Vec::new(),
583
- created_at: "2026-01-01T00:00:00Z".to_string(),
584
- change_pack_count: 1,
585
- membership_pack_count: 1,
586
- }
587
- }
588
-
589
- fn test_change(id: &str) -> Change {
590
- Change {
591
- id: id.to_string(),
592
- entity_id: EntityIdentity::single("entity-1"),
593
- schema_key: "test_schema".to_string(),
594
- file_id: None,
595
- snapshot_ref: Some(JsonRef::from_hash_bytes([1; 32])),
596
- metadata_ref: None,
597
- created_at: "2026-01-01T00:00:00Z".to_string(),
598
- }
599
- }
600
- }