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

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 (234) hide show
  1. package/README.md +1 -1
  2. package/SKILL.md +65 -64
  3. package/dist/engine-wasm/index.js +4 -4
  4. package/dist/engine-wasm/wasm/lix_engine.d.ts +5 -5
  5. package/dist/engine-wasm/wasm/lix_engine.js +130 -118
  6. package/dist/engine-wasm/wasm/lix_engine.wasm +0 -0
  7. package/dist/engine-wasm/wasm/lix_engine.wasm.d.ts +9 -8
  8. package/dist/generated/builtin-schemas.d.ts +69 -69
  9. package/dist/generated/builtin-schemas.js +94 -94
  10. package/dist/open-lix.d.ts +33 -26
  11. package/dist/open-lix.js +10 -10
  12. package/dist/sqlite/index.js +86 -30
  13. package/dist-engine-src/README.md +3 -3
  14. package/dist-engine-src/src/backend/capabilities.rs +67 -0
  15. package/dist-engine-src/src/backend/conformance/baseline.rs +1127 -0
  16. package/dist-engine-src/src/backend/conformance/factory.rs +93 -0
  17. package/dist-engine-src/src/backend/conformance/failure_tests.rs +608 -0
  18. package/dist-engine-src/src/backend/conformance/fixtures.rs +26 -0
  19. package/dist-engine-src/src/backend/conformance/mod.rs +75 -0
  20. package/dist-engine-src/src/backend/conformance/model.rs +28 -0
  21. package/dist-engine-src/src/backend/conformance/model_based.rs +257 -0
  22. package/dist-engine-src/src/backend/conformance/persistence.rs +204 -0
  23. package/dist-engine-src/src/backend/conformance/projection.rs +21 -0
  24. package/dist-engine-src/src/backend/conformance/pushdown.rs +24 -0
  25. package/dist-engine-src/src/backend/conformance/runner.rs +90 -0
  26. package/dist-engine-src/src/backend/conformance/scan.rs +24 -0
  27. package/dist-engine-src/src/backend/conformance/write.rs +16 -0
  28. package/dist-engine-src/src/backend/error.rs +94 -0
  29. package/dist-engine-src/src/backend/in_memory.rs +670 -0
  30. package/dist-engine-src/src/backend/mod.rs +36 -9
  31. package/dist-engine-src/src/backend/predicate.rs +80 -0
  32. package/dist-engine-src/src/backend/traits.rs +260 -0
  33. package/dist-engine-src/src/backend/types.rs +224 -81
  34. package/dist-engine-src/src/binary_cas/context.rs +8 -8
  35. package/dist-engine-src/src/binary_cas/kv.rs +234 -259
  36. package/dist-engine-src/src/{version → branch}/context.rs +12 -12
  37. package/dist-engine-src/src/branch/lifecycle.rs +221 -0
  38. package/dist-engine-src/src/branch/mod.rs +13 -0
  39. package/dist-engine-src/src/branch/refs.rs +321 -0
  40. package/dist-engine-src/src/branch/stage_rows.rs +67 -0
  41. package/dist-engine-src/src/branch/types.rs +21 -0
  42. package/dist-engine-src/src/catalog/context.rs +18 -18
  43. package/dist-engine-src/src/catalog/snapshot.rs +8 -8
  44. package/dist-engine-src/src/changelog/bench_support.rs +785 -0
  45. package/dist-engine-src/src/changelog/change.rs +1 -0
  46. package/dist-engine-src/src/changelog/codec.rs +497 -0
  47. package/dist-engine-src/src/changelog/commit.rs +1 -0
  48. package/dist-engine-src/src/changelog/context.rs +1614 -0
  49. package/dist-engine-src/src/changelog/mod.rs +29 -0
  50. package/dist-engine-src/src/changelog/store.rs +163 -0
  51. package/dist-engine-src/src/changelog/test_support.rs +54 -0
  52. package/dist-engine-src/src/changelog/types.rs +213 -0
  53. package/dist-engine-src/src/commit_graph/context.rs +317 -274
  54. package/dist-engine-src/src/commit_graph/mod.rs +2 -4
  55. package/dist-engine-src/src/commit_graph/types.rs +22 -42
  56. package/dist-engine-src/src/commit_graph/walker.rs +133 -103
  57. package/dist-engine-src/src/common/error.rs +52 -18
  58. package/dist-engine-src/src/common/identity.rs +2 -2
  59. package/dist-engine-src/src/common/mod.rs +1 -1
  60. package/dist-engine-src/src/domain.rs +42 -46
  61. package/dist-engine-src/src/engine.rs +74 -96
  62. package/dist-engine-src/src/{entity_identity.rs → entity_pk.rs} +89 -92
  63. package/dist-engine-src/src/functions/context.rs +56 -52
  64. package/dist-engine-src/src/functions/state.rs +51 -52
  65. package/dist-engine-src/src/init.rs +288 -154
  66. package/dist-engine-src/src/json_store/context.rs +15 -266
  67. package/dist-engine-src/src/json_store/mod.rs +26 -0
  68. package/dist-engine-src/src/json_store/store.rs +103 -718
  69. package/dist-engine-src/src/json_store/types.rs +4 -9
  70. package/dist-engine-src/src/lib.rs +49 -19
  71. package/dist-engine-src/src/live_state/context.rs +654 -790
  72. package/dist-engine-src/src/live_state/mod.rs +9 -3
  73. package/dist-engine-src/src/live_state/overlay.rs +4 -4
  74. package/dist-engine-src/src/live_state/types.rs +30 -21
  75. package/dist-engine-src/src/live_state/visibility.rs +514 -71
  76. package/dist-engine-src/src/plugin/install.rs +48 -48
  77. package/dist-engine-src/src/plugin/manifest.rs +7 -7
  78. package/dist-engine-src/src/plugin/materializer.rs +0 -275
  79. package/dist-engine-src/src/plugin/plugin_manifest.json +4 -3
  80. package/dist-engine-src/src/schema/builtin/lix_binary_blob_ref.json +2 -2
  81. package/dist-engine-src/src/schema/builtin/lix_branch_descriptor.json +34 -0
  82. package/dist-engine-src/src/schema/builtin/lix_branch_ref.json +48 -0
  83. package/dist-engine-src/src/schema/builtin/lix_change.json +3 -3
  84. package/dist-engine-src/src/schema/builtin/lix_commit.json +1 -1
  85. package/dist-engine-src/src/schema/builtin/lix_label_assignment.json +6 -6
  86. package/dist-engine-src/src/schema/builtin/mod.rs +18 -20
  87. package/dist-engine-src/src/schema/compatibility.rs +11 -11
  88. package/dist-engine-src/src/schema/definition.json +2 -2
  89. package/dist-engine-src/src/schema/definition.rs +5 -5
  90. package/dist-engine-src/src/schema/key.rs +3 -3
  91. package/dist-engine-src/src/schema/mod.rs +1 -1
  92. package/dist-engine-src/src/schema/tests.rs +18 -18
  93. package/dist-engine-src/src/session/context.rs +803 -148
  94. package/dist-engine-src/src/session/create_branch.rs +94 -0
  95. package/dist-engine-src/src/session/execute.rs +223 -83
  96. package/dist-engine-src/src/session/merge/analysis.rs +9 -3
  97. package/dist-engine-src/src/session/merge/{version.rs → branch.rs} +119 -129
  98. package/dist-engine-src/src/session/merge/conflicts.rs +2 -2
  99. package/dist-engine-src/src/session/merge/mod.rs +5 -6
  100. package/dist-engine-src/src/session/merge/stats.rs +7 -11
  101. package/dist-engine-src/src/session/mod.rs +15 -12
  102. package/dist-engine-src/src/session/switch_branch.rs +113 -0
  103. package/dist-engine-src/src/session/transaction.rs +495 -14
  104. package/dist-engine-src/src/sql2/{classify.rs → bind/classify.rs} +3 -75
  105. package/dist-engine-src/src/sql2/bind/error.rs +5 -0
  106. package/dist-engine-src/src/sql2/bind/expr.rs +29 -0
  107. package/dist-engine-src/src/sql2/bind/mod.rs +12 -0
  108. package/dist-engine-src/src/sql2/{udfs/public_call.rs → bind/public_udf.rs} +71 -3
  109. package/dist-engine-src/src/sql2/bind/read.rs +65 -0
  110. package/dist-engine-src/src/sql2/bind/statement.rs +2236 -0
  111. package/dist-engine-src/src/sql2/bind/table.rs +273 -0
  112. package/dist-engine-src/src/sql2/bind/write.rs +86 -0
  113. package/dist-engine-src/src/sql2/branch_scope.rs +436 -0
  114. package/dist-engine-src/src/sql2/catalog/capability.rs +20 -0
  115. package/dist-engine-src/src/sql2/catalog/entity_surface.rs +296 -0
  116. package/dist-engine-src/src/sql2/catalog/mod.rs +15 -0
  117. package/dist-engine-src/src/sql2/catalog/registry.rs +556 -0
  118. package/dist-engine-src/src/sql2/catalog/schema.rs +88 -0
  119. package/dist-engine-src/src/sql2/catalog/surface.rs +41 -0
  120. package/dist-engine-src/src/sql2/change_materialization.rs +122 -0
  121. package/dist-engine-src/src/sql2/context.rs +36 -30
  122. package/dist-engine-src/src/sql2/error.rs +1 -1
  123. package/dist-engine-src/src/sql2/exec/bound_public_write.rs +1593 -0
  124. package/dist-engine-src/src/sql2/exec/datafusion.rs +5266 -0
  125. package/dist-engine-src/src/sql2/exec/fast_write.rs +82 -0
  126. package/dist-engine-src/src/sql2/exec/mod.rs +24 -0
  127. package/dist-engine-src/src/sql2/exec/write.rs +661 -0
  128. package/dist-engine-src/src/sql2/filesystem_planner.rs +72 -77
  129. package/dist-engine-src/src/sql2/filesystem_visibility.rs +21 -21
  130. package/dist-engine-src/src/sql2/history_projection.rs +8 -8
  131. package/dist-engine-src/src/sql2/history_route.rs +35 -31
  132. package/dist-engine-src/src/sql2/mod.rs +28 -23
  133. package/dist-engine-src/src/sql2/optimize/datafusion.rs +1 -0
  134. package/dist-engine-src/src/sql2/optimize/mod.rs +2 -0
  135. package/dist-engine-src/src/sql2/optimize/simple_write.rs +116 -0
  136. package/dist-engine-src/src/sql2/parse/mod.rs +69 -0
  137. package/dist-engine-src/src/sql2/parse/normalize.rs +1 -0
  138. package/dist-engine-src/src/sql2/plan/branch_scope.rs +24 -0
  139. package/dist-engine-src/src/sql2/plan/mod.rs +5 -0
  140. package/dist-engine-src/src/sql2/plan/predicate.rs +22 -0
  141. package/dist-engine-src/src/sql2/plan/write.rs +147 -0
  142. package/dist-engine-src/src/sql2/predicate_typecheck.rs +258 -0
  143. package/dist-engine-src/src/sql2/{version_provider.rs → providers/branch.rs} +218 -214
  144. package/dist-engine-src/src/sql2/{change_provider.rs → providers/change.rs} +156 -42
  145. package/dist-engine-src/src/sql2/{directory_provider.rs → providers/directory.rs} +291 -322
  146. package/dist-engine-src/src/sql2/{directory_history_provider.rs → providers/directory_history.rs} +56 -42
  147. package/dist-engine-src/src/sql2/providers/entity.rs +1484 -0
  148. package/dist-engine-src/src/sql2/{entity_history_provider.rs → providers/entity_history.rs} +43 -31
  149. package/dist-engine-src/src/sql2/{file_provider.rs → providers/file.rs} +323 -316
  150. package/dist-engine-src/src/sql2/{file_history_provider.rs → providers/file_history.rs} +60 -46
  151. package/dist-engine-src/src/sql2/{history_provider.rs → providers/history.rs} +46 -32
  152. package/dist-engine-src/src/sql2/{lix_state_provider.rs → providers/lix_state.rs} +359 -329
  153. package/dist-engine-src/src/sql2/providers/mod.rs +508 -0
  154. package/dist-engine-src/src/sql2/read_only.rs +2 -2
  155. package/dist-engine-src/src/sql2/session.rs +47 -96
  156. package/dist-engine-src/src/sql2/storage/constraints.rs +1 -0
  157. package/dist-engine-src/src/sql2/storage/mod.rs +1 -0
  158. package/dist-engine-src/src/sql2/test_support/differential.rs +712 -0
  159. package/dist-engine-src/src/sql2/test_support/generators.rs +354 -0
  160. package/dist-engine-src/src/sql2/test_support/mod.rs +2 -0
  161. package/dist-engine-src/src/sql2/udfs/{lix_active_version_commit_id.rs → lix_active_branch_commit_id.rs} +7 -7
  162. package/dist-engine-src/src/sql2/udfs/mod.rs +3 -6
  163. package/dist-engine-src/src/sql2/write_normalization.rs +45 -22
  164. package/dist-engine-src/src/storage/conformance.rs +399 -0
  165. package/dist-engine-src/src/storage/context.rs +552 -288
  166. package/dist-engine-src/src/storage/mod.rs +48 -10
  167. package/dist-engine-src/src/storage/point.rs +440 -0
  168. package/dist-engine-src/src/storage/read_scope.rs +43 -64
  169. package/dist-engine-src/src/storage/reader.rs +867 -0
  170. package/dist-engine-src/src/storage/scan.rs +784 -0
  171. package/dist-engine-src/src/storage/spaces.rs +236 -0
  172. package/dist-engine-src/src/storage/stats.rs +80 -0
  173. package/dist-engine-src/src/storage/write_set.rs +962 -0
  174. package/dist-engine-src/src/storage_bench.rs +136 -4828
  175. package/dist-engine-src/src/test_support.rs +360 -138
  176. package/dist-engine-src/src/tracked_state/bench_support.rs +394 -0
  177. package/dist-engine-src/src/tracked_state/codec.rs +155 -1057
  178. package/dist-engine-src/src/tracked_state/commit_root_rebuild.rs +358 -0
  179. package/dist-engine-src/src/tracked_state/context.rs +1927 -993
  180. package/dist-engine-src/src/tracked_state/diff.rs +1715 -261
  181. package/dist-engine-src/src/tracked_state/merge.rs +74 -88
  182. package/dist-engine-src/src/tracked_state/mod.rs +19 -16
  183. package/dist-engine-src/src/tracked_state/{materialization.rs → row_materialization.rs} +50 -178
  184. package/dist-engine-src/src/tracked_state/storage.rs +243 -191
  185. package/dist-engine-src/src/tracked_state/tree.rs +247 -371
  186. package/dist-engine-src/src/tracked_state/types.rs +49 -42
  187. package/dist-engine-src/src/transaction/bench_support.rs +407 -0
  188. package/dist-engine-src/src/transaction/commit.rs +821 -713
  189. package/dist-engine-src/src/transaction/context.rs +705 -600
  190. package/dist-engine-src/src/transaction/mod.rs +13 -2
  191. package/dist-engine-src/src/transaction/normalization.rs +63 -76
  192. package/dist-engine-src/src/transaction/prep.rs +13 -13
  193. package/dist-engine-src/src/transaction/schema_resolver.rs +19 -5
  194. package/dist-engine-src/src/transaction/staging.rs +228 -434
  195. package/dist-engine-src/src/transaction/types.rs +41 -98
  196. package/dist-engine-src/src/transaction/validation.rs +382 -446
  197. package/dist-engine-src/src/untracked_state/codec.rs +337 -29
  198. package/dist-engine-src/src/untracked_state/context.rs +7 -7
  199. package/dist-engine-src/src/untracked_state/materialization.rs +2 -2
  200. package/dist-engine-src/src/untracked_state/mod.rs +1 -1
  201. package/dist-engine-src/src/untracked_state/storage.rs +659 -157
  202. package/dist-engine-src/src/untracked_state/types.rs +21 -21
  203. package/package.json +71 -68
  204. package/dist-engine-src/src/backend/kv.rs +0 -358
  205. package/dist-engine-src/src/backend/testing.rs +0 -658
  206. package/dist-engine-src/src/commit_store/codec.rs +0 -887
  207. package/dist-engine-src/src/commit_store/context.rs +0 -944
  208. package/dist-engine-src/src/commit_store/materialization.rs +0 -84
  209. package/dist-engine-src/src/commit_store/mod.rs +0 -16
  210. package/dist-engine-src/src/commit_store/storage.rs +0 -600
  211. package/dist-engine-src/src/commit_store/types.rs +0 -215
  212. package/dist-engine-src/src/schema/builtin/lix_version_descriptor.json +0 -34
  213. package/dist-engine-src/src/schema/builtin/lix_version_ref.json +0 -48
  214. package/dist-engine-src/src/session/create_version.rs +0 -88
  215. package/dist-engine-src/src/session/merge/apply.rs +0 -23
  216. package/dist-engine-src/src/session/optimization9_sql2_bench.rs +0 -100
  217. package/dist-engine-src/src/session/switch_version.rs +0 -110
  218. package/dist-engine-src/src/sql2/entity_provider.rs +0 -3211
  219. package/dist-engine-src/src/sql2/execute.rs +0 -3533
  220. package/dist-engine-src/src/sql2/public_bind/assignment.rs +0 -46
  221. package/dist-engine-src/src/sql2/public_bind/capability.rs +0 -41
  222. package/dist-engine-src/src/sql2/public_bind/dml.rs +0 -172
  223. package/dist-engine-src/src/sql2/public_bind/mod.rs +0 -26
  224. package/dist-engine-src/src/sql2/public_bind/table.rs +0 -168
  225. package/dist-engine-src/src/sql2/version_scope.rs +0 -394
  226. package/dist-engine-src/src/storage/types.rs +0 -501
  227. package/dist-engine-src/src/tracked_state/by_file_index.rs +0 -98
  228. package/dist-engine-src/src/tracked_state/materializer.rs +0 -488
  229. package/dist-engine-src/src/transaction/live_state_overlay.rs +0 -35
  230. package/dist-engine-src/src/version/lifecycle.rs +0 -221
  231. package/dist-engine-src/src/version/mod.rs +0 -13
  232. package/dist-engine-src/src/version/refs.rs +0 -330
  233. package/dist-engine-src/src/version/stage_rows.rs +0 -67
  234. package/dist-engine-src/src/version/types.rs +0 -21
@@ -1,67 +1,66 @@
1
1
  use std::sync::Arc;
2
2
 
3
- use crate::commit_store::{Change, CommitDraftRef, CommitStoreContext};
4
- use crate::json_store::{
5
- JsonStoreContext, JsonWritePlacementRef, NormalizedJson, NormalizedJsonRef,
3
+ use std::collections::BTreeMap;
4
+
5
+ use crate::branch::BranchContext;
6
+ use crate::changelog::{
7
+ ChangeLoadRequest, ChangeRecord, ChangelogAppend, ChangelogContext, ChangelogReader,
8
+ ChangelogWriter, CommitChangeRef, CommitChangeRefSet, CommitRecord,
6
9
  };
10
+ use crate::json_store::{JsonRef, JsonStoreContext, JsonWritePlacementRef, NormalizedJsonRef};
7
11
  use crate::storage::StorageContext;
12
+ use crate::storage::StorageRead;
8
13
  use crate::storage::StorageWriteSet;
9
- use crate::storage::StorageWriteTransaction;
10
14
  use crate::tracked_state::{
11
15
  MaterializedTrackedStateRow, TrackedStateContext, TrackedStateDeltaRef,
12
16
  };
13
- use crate::transaction::prepare_version_ref_row;
17
+ use crate::transaction::prepare_branch_ref_row;
14
18
  use crate::untracked_state::{
15
19
  MaterializedUntrackedStateRow, UntrackedStateContext, UntrackedStateRow,
16
20
  };
17
- use crate::version::VersionContext;
18
21
 
19
22
  fn prepare_json_ref(value: &str) -> crate::json_store::JsonRef {
20
23
  crate::json_store::JsonRef::for_content(value.as_bytes())
21
24
  }
22
- use crate::GLOBAL_VERSION_ID;
25
+ use crate::GLOBAL_BRANCH_ID;
23
26
 
24
27
  pub(crate) const TEST_EMPTY_ROOT_COMMIT_ID: &str = "test-empty-root";
25
28
  const TEST_TIMESTAMP: &str = "1970-01-01T00:00:00.000Z";
26
29
 
27
- /// Seeds a version head and matching tracked root for unit tests.
30
+ /// Seeds a branch head and matching tracked root for unit tests.
28
31
  ///
29
- /// A version ref that points at a commit without a tracked root is invalid for
30
- /// the serving projection. This helper keeps that invariant in one place while
32
+ /// A branch ref that points at a commit without a tracked root is invalid for
33
+ /// the serving state. This helper keeps that invariant in one place while
31
34
  /// still letting low-level tests use synthetic commit ids.
32
- pub(crate) async fn seed_version_head(storage: StorageContext, version_id: &str, commit_id: &str) {
33
- seed_version_head_with_rows(storage, version_id, commit_id, &[]).await;
35
+ pub(crate) async fn seed_branch_head(storage: StorageContext, branch_id: &str, commit_id: &str) {
36
+ seed_branch_head_with_rows(storage, branch_id, commit_id, &[]).await;
34
37
  }
35
38
 
36
- /// Seeds the global version head to an empty tracked root for unit tests.
37
- pub(crate) async fn seed_global_version_head(storage: StorageContext) {
38
- seed_version_head(storage, GLOBAL_VERSION_ID, TEST_EMPTY_ROOT_COMMIT_ID).await;
39
+ /// Seeds the global branch head to an empty tracked root for unit tests.
40
+ pub(crate) async fn seed_global_branch_head(storage: StorageContext) {
41
+ seed_branch_head(storage, GLOBAL_BRANCH_ID, TEST_EMPTY_ROOT_COMMIT_ID).await;
39
42
  }
40
43
 
41
- /// Seeds a version head and writes the tracked root contents for its commit.
42
- pub(crate) async fn seed_version_head_with_rows(
44
+ /// Seeds a branch head and writes the tracked root contents for its commit.
45
+ pub(crate) async fn seed_branch_head_with_rows(
43
46
  storage: StorageContext,
44
- version_id: &str,
47
+ branch_id: &str,
45
48
  commit_id: &str,
46
49
  rows: &[MaterializedTrackedStateRow],
47
50
  ) {
48
- let mut transaction = storage
49
- .begin_write_transaction()
50
- .await
51
- .expect("seed transaction should open");
52
- let version_ctx = VersionContext::new(Arc::new(UntrackedStateContext::new()));
51
+ let mut read = storage
52
+ .begin_read(crate::storage::StorageReadOptions::default())
53
+ .expect("seed read should open");
54
+ let branch_ctx = BranchContext::new(Arc::new(UntrackedStateContext::new()));
53
55
  let mut writes = StorageWriteSet::new();
54
- let canonical_row = prepare_version_ref_row(version_id, commit_id, TEST_TIMESTAMP)
55
- .expect("version ref should canonicalize");
56
- version_ctx
56
+ let canonical_row = prepare_branch_ref_row(branch_id, commit_id, TEST_TIMESTAMP)
57
+ .expect("branch ref should canonicalize");
58
+ branch_ctx
57
59
  .stage_canonical_ref_rows(&mut writes, &[canonical_row.row])
58
- .expect("version ref should stage");
59
- writes
60
- .apply(&mut transaction.as_mut())
61
- .await
62
- .expect("version ref should write");
60
+ .expect("branch ref should stage");
63
61
  stage_tracked_root_from_materialized(
64
- transaction.as_mut(),
62
+ &mut read,
63
+ &mut writes,
65
64
  &TrackedStateContext::new(),
66
65
  commit_id,
67
66
  None,
@@ -69,121 +68,359 @@ pub(crate) async fn seed_version_head_with_rows(
69
68
  )
70
69
  .await
71
70
  .expect("tracked root should write");
72
- transaction.commit().await.expect("seed should commit");
71
+ storage
72
+ .commit_write_set(writes, crate::storage::StorageWriteOptions::default())
73
+ .expect("seed should commit");
73
74
  }
74
75
 
75
76
  pub(crate) async fn stage_tracked_root_from_materialized(
76
- transaction: &mut dyn StorageWriteTransaction,
77
+ read: &mut (impl StorageRead + Send + Sync + ?Sized),
78
+ writes: &mut StorageWriteSet,
77
79
  tracked_state: &TrackedStateContext,
78
80
  commit_id: &str,
79
81
  parent_commit_id: Option<&str>,
80
82
  rows: &[MaterializedTrackedStateRow],
81
83
  ) -> Result<(), crate::LixError> {
82
- let mut writes = StorageWriteSet::new();
83
84
  let changes = rows
84
85
  .iter()
85
86
  .map(tracked_change_from_materialized)
86
87
  .collect::<Result<Vec<_>, _>>()?;
87
- let json_payloads = materialized_tracked_json_payloads(rows);
88
- JsonStoreContext::new().writer().stage_batch(
89
- &mut writes,
90
- JsonWritePlacementRef::CommitPack {
91
- commit_id,
92
- pack_id: 0,
93
- },
94
- json_payloads
95
- .iter()
96
- .map(|json| NormalizedJsonRef::from(json)),
97
- )?;
98
-
99
88
  let parent_ids = parent_commit_id
100
89
  .map(|parent| vec![parent.to_string()])
101
90
  .unwrap_or_default();
102
91
  let commit_change_id = format!("{commit_id}:commit");
103
- let commit = CommitDraftRef {
104
- id: commit_id,
105
- change_id: &commit_change_id,
106
- parent_ids: &parent_ids,
107
- author_account_ids: &[],
108
- created_at: rows
109
- .first()
110
- .map(|row| row.updated_at.as_str())
111
- .unwrap_or(TEST_TIMESTAMP),
112
- };
113
- let commit_store = CommitStoreContext::new();
114
- let change_ids = changes
92
+ let staged = stage_test_changelog_commit(
93
+ read,
94
+ writes,
95
+ commit_id,
96
+ &commit_change_id,
97
+ &parent_ids,
98
+ rows,
99
+ &changes,
100
+ )
101
+ .await?;
102
+ let commit_snapshot = commit_row_snapshot_content(commit_id)?;
103
+ let commit_snapshot_ref = JsonRef::for_content(commit_snapshot.as_bytes());
104
+ let commit_entity_pk = crate::entity_pk::EntityPk::single(commit_id);
105
+ let mut deltas = staged
106
+ .change_commit_ids
107
+ .iter()
108
+ .map(|(row_index, change_commit_id)| {
109
+ let change = &changes[*row_index];
110
+ let row = &rows[*row_index];
111
+ TrackedStateDeltaRef {
112
+ schema_key: &change.schema_key,
113
+ file_id: change.file_id.as_deref(),
114
+ entity_pk: &change.entity_pk,
115
+ change_id: &change.change_id,
116
+ commit_id: change_commit_id,
117
+ snapshot_ref: change.snapshot_ref.as_ref(),
118
+ metadata_ref: change.metadata_ref.as_ref(),
119
+ deleted: change.snapshot_ref.is_none(),
120
+ created_at: &row.created_at,
121
+ updated_at: &row.updated_at,
122
+ }
123
+ })
124
+ .collect::<Vec<_>>();
125
+ deltas.push(TrackedStateDeltaRef {
126
+ schema_key: "lix_commit",
127
+ file_id: None,
128
+ entity_pk: &commit_entity_pk,
129
+ change_id: &staged.commit_change_id,
130
+ commit_id,
131
+ snapshot_ref: Some(&commit_snapshot_ref),
132
+ metadata_ref: None,
133
+ deleted: false,
134
+ created_at: &staged.commit_created_at,
135
+ updated_at: &staged.commit_created_at,
136
+ });
137
+ tracked_state
138
+ .writer(read, writes)
139
+ .stage_commit_root(commit_id, parent_commit_id, deltas)
140
+ .await?;
141
+ Ok(())
142
+ }
143
+
144
+ pub(crate) async fn stage_tracked_root_from_materialized_with_parents(
145
+ read: &mut (impl StorageRead + Send + Sync + ?Sized),
146
+ writes: &mut StorageWriteSet,
147
+ tracked_state: &TrackedStateContext,
148
+ commit_id: &str,
149
+ parent_ids: &[String],
150
+ commit_root_parent_commit_id: Option<&str>,
151
+ rows: &[MaterializedTrackedStateRow],
152
+ ) -> Result<(), crate::LixError> {
153
+ let changes = rows
154
+ .iter()
155
+ .map(tracked_change_from_materialized)
156
+ .collect::<Result<Vec<_>, _>>()?;
157
+ let commit_change_id = format!("{commit_id}:commit");
158
+ let staged = stage_test_changelog_commit(
159
+ read,
160
+ writes,
161
+ commit_id,
162
+ &commit_change_id,
163
+ parent_ids,
164
+ rows,
165
+ &changes,
166
+ )
167
+ .await?;
168
+ let commit_snapshot = commit_row_snapshot_content(commit_id)?;
169
+ let commit_snapshot_ref = JsonRef::for_content(commit_snapshot.as_bytes());
170
+ let commit_entity_pk = crate::entity_pk::EntityPk::single(commit_id);
171
+ let mut deltas = staged
172
+ .change_commit_ids
115
173
  .iter()
116
- .map(|change| change.id.clone())
174
+ .map(|(row_index, change_commit_id)| {
175
+ let change = &changes[*row_index];
176
+ let row = &rows[*row_index];
177
+ TrackedStateDeltaRef {
178
+ schema_key: &change.schema_key,
179
+ file_id: change.file_id.as_deref(),
180
+ entity_pk: &change.entity_pk,
181
+ change_id: &change.change_id,
182
+ commit_id: change_commit_id,
183
+ snapshot_ref: change.snapshot_ref.as_ref(),
184
+ metadata_ref: change.metadata_ref.as_ref(),
185
+ deleted: change.snapshot_ref.is_none(),
186
+ created_at: &row.created_at,
187
+ updated_at: &row.updated_at,
188
+ }
189
+ })
117
190
  .collect::<Vec<_>>();
118
- let existing_changes = commit_store
119
- .reader(&mut *transaction)
120
- .load_change_index_entries(&change_ids)
191
+ deltas.push(TrackedStateDeltaRef {
192
+ schema_key: "lix_commit",
193
+ file_id: None,
194
+ entity_pk: &commit_entity_pk,
195
+ change_id: &staged.commit_change_id,
196
+ commit_id,
197
+ snapshot_ref: Some(&commit_snapshot_ref),
198
+ metadata_ref: None,
199
+ deleted: false,
200
+ created_at: &staged.commit_created_at,
201
+ updated_at: &staged.commit_created_at,
202
+ });
203
+ tracked_state
204
+ .writer(read, writes)
205
+ .stage_commit_root(commit_id, commit_root_parent_commit_id, deltas)
121
206
  .await?;
122
- let mut authored_changes = Vec::new();
123
- let mut authored_created_at = Vec::new();
124
- let mut authored_updated_at = Vec::new();
125
- let mut adopted_changes = Vec::new();
126
- let mut adopted_created_at = Vec::new();
127
- let mut adopted_updated_at = Vec::new();
128
- for ((change, row), existing) in changes.iter().zip(rows).zip(existing_changes) {
129
- if existing.is_some() {
130
- adopted_changes.push(change.as_ref());
131
- adopted_created_at.push(row.created_at.as_str());
132
- adopted_updated_at.push(row.updated_at.as_str());
133
- } else {
134
- authored_changes.push(change.as_ref());
135
- authored_created_at.push(row.created_at.as_str());
136
- authored_updated_at.push(row.updated_at.as_str());
207
+ Ok(())
208
+ }
209
+
210
+ pub(crate) async fn stage_empty_changelog_commit(
211
+ read: &mut (impl StorageRead + Send + Sync + ?Sized),
212
+ writes: &mut StorageWriteSet,
213
+ commit_id: &str,
214
+ parent_commit_id: Option<&str>,
215
+ ) -> Result<(), crate::LixError> {
216
+ let parent_ids = parent_commit_id
217
+ .map(|parent| vec![parent.to_string()])
218
+ .unwrap_or_default();
219
+ let commit_change_id = format!("{commit_id}:commit");
220
+ stage_test_changelog_commit(
221
+ read,
222
+ writes,
223
+ commit_id,
224
+ &commit_change_id,
225
+ &parent_ids,
226
+ &[],
227
+ &[],
228
+ )
229
+ .await?;
230
+ Ok(())
231
+ }
232
+
233
+ pub(crate) async fn stage_empty_changelog_commit_with_parents(
234
+ read: &mut (impl StorageRead + Send + Sync + ?Sized),
235
+ writes: &mut StorageWriteSet,
236
+ commit_id: &str,
237
+ parent_ids: &[String],
238
+ ) -> Result<(), crate::LixError> {
239
+ let commit_change_id = format!("{commit_id}:commit");
240
+ stage_test_changelog_commit(
241
+ read,
242
+ writes,
243
+ commit_id,
244
+ &commit_change_id,
245
+ parent_ids,
246
+ &[],
247
+ &[],
248
+ )
249
+ .await?;
250
+ Ok(())
251
+ }
252
+
253
+ async fn stage_test_changelog_commit(
254
+ mut read: &mut (impl StorageRead + Send + Sync + ?Sized),
255
+ writes: &mut StorageWriteSet,
256
+ commit_id: &str,
257
+ commit_change_id: &str,
258
+ parent_ids: &[String],
259
+ rows: &[MaterializedTrackedStateRow],
260
+ changes: &[ChangeRecord],
261
+ ) -> Result<TestStagedChangelogCommit, crate::LixError> {
262
+ let winner_indices = final_state_row_winner_indices(rows)?;
263
+ let winner_change_ids = winner_indices
264
+ .iter()
265
+ .map(|&index| changes[index].change_id.clone())
266
+ .collect::<Vec<_>>();
267
+ let existing_change_ids = load_existing_changelog_change_ids(read, &winner_change_ids).await?;
268
+ let mut append = ChangelogAppend::default();
269
+ let mut refs = Vec::new();
270
+ let mut change_commit_ids = Vec::new();
271
+ let mut json_payloads = Vec::new();
272
+ let mut seen_json_refs = std::collections::BTreeSet::new();
273
+ for &row_index in &winner_indices {
274
+ let row = &rows[row_index];
275
+ let change = &changes[row_index];
276
+ if !existing_change_ids.contains(&change.change_id) {
277
+ for (json_ref, payload) in json_payloads_from_materialized(row) {
278
+ if seen_json_refs.insert(json_ref.as_hash_bytes().to_vec()) {
279
+ json_payloads.push((json_ref, payload));
280
+ }
281
+ }
282
+ append.changes.push(change.clone());
137
283
  }
284
+ refs.push(commit_change_ref_from_change(change));
285
+ change_commit_ids.push((row_index, row.commit_id.clone()));
286
+ }
287
+ let commit_snapshot = commit_row_snapshot_content(commit_id)?;
288
+ let commit_snapshot_ref = JsonRef::for_content(commit_snapshot.as_bytes());
289
+ json_payloads.push((commit_snapshot_ref, commit_snapshot));
290
+ stage_json_payloads(writes, &json_payloads)?;
291
+ let created_at = rows
292
+ .first()
293
+ .map(|row| row.created_at.clone())
294
+ .unwrap_or_else(|| TEST_TIMESTAMP.to_string());
295
+ append.commits.push(CommitRecord {
296
+ format_version: 1,
297
+ commit_id: commit_id.to_string(),
298
+ parent_commit_ids: parent_ids.to_vec(),
299
+ change_id: commit_change_id.to_string(),
300
+ author_account_ids: Vec::new(),
301
+ created_at: created_at.clone(),
302
+ });
303
+ append.commit_change_refs.push(CommitChangeRefSet {
304
+ commit_id: commit_id.to_string(),
305
+ entries: refs,
306
+ });
307
+ let mut writer = ChangelogContext::new().writer(&mut read, writes);
308
+ writer.stage_append(append).await?;
309
+ change_commit_ids.sort_by_key(|(row_index, _)| *row_index);
310
+ Ok(TestStagedChangelogCommit {
311
+ change_commit_ids,
312
+ commit_change_id: commit_change_id.to_string(),
313
+ commit_created_at: created_at,
314
+ })
315
+ }
316
+
317
+ fn commit_row_snapshot_content(commit_id: &str) -> Result<String, crate::LixError> {
318
+ serde_json::to_string(&serde_json::json!({
319
+ "id": commit_id,
320
+ }))
321
+ .map_err(|error| {
322
+ crate::LixError::new(
323
+ crate::LixError::CODE_INTERNAL_ERROR,
324
+ format!("failed to encode lix_commit snapshot: {error}"),
325
+ )
326
+ })
327
+ }
328
+
329
+ struct TestStagedChangelogCommit {
330
+ change_commit_ids: Vec<(usize, String)>,
331
+ commit_change_id: String,
332
+ commit_created_at: String,
333
+ }
334
+
335
+ async fn load_existing_changelog_change_ids(
336
+ read: &mut (impl StorageRead + Send + Sync + ?Sized),
337
+ change_ids: &[String],
338
+ ) -> Result<std::collections::BTreeSet<String>, crate::LixError> {
339
+ if change_ids.is_empty() {
340
+ return Ok(std::collections::BTreeSet::new());
138
341
  }
139
- let staged = commit_store
140
- .writer(&mut *transaction, &mut writes)
141
- .stage_tracked_commit_draft(commit, authored_changes.clone(), adopted_changes.clone())
342
+ let mut unique = change_ids.to_vec();
343
+ unique.sort();
344
+ unique.dedup();
345
+ let mut reader = ChangelogContext::new().reader(&mut *read);
346
+ let batch = reader
347
+ .load_changes(ChangeLoadRequest {
348
+ change_ids: &unique,
349
+ })
142
350
  .await?;
143
- let mut deltas = Vec::with_capacity(changes.len());
144
- deltas.extend(
145
- authored_changes
146
- .iter()
147
- .zip(&staged.authored_locators)
148
- .zip(authored_created_at)
149
- .zip(authored_updated_at)
150
- .map(
151
- |(((change, locator), created_at), updated_at)| TrackedStateDeltaRef {
152
- change: *change,
153
- locator: locator.as_ref(),
154
- created_at,
155
- updated_at,
156
- },
157
- ),
158
- );
159
- deltas.extend(
160
- adopted_changes
161
- .iter()
162
- .zip(&staged.adopted_locators)
163
- .zip(adopted_created_at)
164
- .zip(adopted_updated_at)
165
- .map(
166
- |(((change, locator), created_at), updated_at)| TrackedStateDeltaRef {
167
- change: *change,
168
- locator: locator.as_ref(),
169
- created_at,
170
- updated_at,
171
- },
351
+ Ok(unique
352
+ .into_iter()
353
+ .zip(batch.entries)
354
+ .filter_map(|(change_id, entry)| entry.map(|_| change_id))
355
+ .collect())
356
+ }
357
+
358
+ fn final_state_row_winner_indices(
359
+ rows: &[MaterializedTrackedStateRow],
360
+ ) -> Result<Vec<usize>, crate::LixError> {
361
+ let mut winners =
362
+ BTreeMap::<(String, Option<String>, crate::entity_pk::EntityPk), usize>::new();
363
+ for (index, row) in rows.iter().enumerate() {
364
+ winners.insert(
365
+ (
366
+ row.schema_key.clone(),
367
+ row.file_id.clone(),
368
+ row.entity_pk.clone(),
172
369
  ),
173
- );
174
- tracked_state
175
- .writer(&mut *transaction, &mut writes)
176
- .stage_delta(commit_id, parent_commit_id, &deltas)
177
- .await?;
178
- writes.apply(&mut *transaction).await.map(|_| ())
370
+ index,
371
+ );
372
+ }
373
+ let mut indices = winners.into_values().collect::<Vec<_>>();
374
+ indices.sort_unstable();
375
+ Ok(indices)
376
+ }
377
+
378
+ fn json_payloads_from_materialized(
379
+ row: &MaterializedTrackedStateRow,
380
+ ) -> Vec<(crate::json_store::JsonRef, String)> {
381
+ let mut payloads = Vec::new();
382
+ if let Some(snapshot) = row.snapshot_content.as_deref() {
383
+ payloads.push((prepare_json_ref(snapshot), snapshot.to_string()));
384
+ }
385
+ if let Some(metadata) = row.metadata.as_ref() {
386
+ let serialized = crate::serialize_row_metadata(metadata);
387
+ payloads.push((prepare_json_ref(&serialized), serialized));
388
+ }
389
+ payloads
390
+ }
391
+
392
+ fn stage_json_payloads(
393
+ writes: &mut StorageWriteSet,
394
+ payloads: &[(crate::json_store::JsonRef, String)],
395
+ ) -> Result<(), crate::LixError> {
396
+ let payloads = payloads
397
+ .iter()
398
+ .map(|(json_ref, payload)| NormalizedJsonRef::trusted_prehashed(payload, *json_ref))
399
+ .collect::<Vec<_>>();
400
+ JsonStoreContext::new().writer().stage_batch(
401
+ writes,
402
+ JsonWritePlacementRef::OutOfBand,
403
+ payloads,
404
+ )?;
405
+ Ok(())
406
+ }
407
+
408
+ fn commit_change_ref_from_change(change: &ChangeRecord) -> CommitChangeRef {
409
+ CommitChangeRef {
410
+ schema_key: change.schema_key.clone(),
411
+ file_id: change.file_id.clone(),
412
+ entity_pk: change.entity_pk.clone(),
413
+ change_id: change.change_id.clone(),
414
+ }
179
415
  }
180
416
 
181
417
  pub(crate) fn tracked_change_from_materialized(
182
418
  row: &MaterializedTrackedStateRow,
183
- ) -> Result<Change, crate::LixError> {
184
- Ok(Change {
185
- id: row.change_id.clone(),
186
- entity_id: row.entity_id.clone(),
419
+ ) -> Result<ChangeRecord, crate::LixError> {
420
+ Ok(ChangeRecord {
421
+ format_version: 1,
422
+ change_id: row.change_id.clone(),
423
+ entity_pk: row.entity_pk.clone(),
187
424
  schema_key: row.schema_key.clone(),
188
425
  file_id: row.file_id.clone(),
189
426
  snapshot_ref: row.snapshot_content.as_deref().map(prepare_json_ref),
@@ -191,31 +428,16 @@ pub(crate) fn tracked_change_from_materialized(
191
428
  let serialized = crate::serialize_row_metadata(value);
192
429
  prepare_json_ref(&serialized)
193
430
  }),
194
- created_at: row.created_at.clone(),
431
+ created_at: row.updated_at.clone(),
195
432
  })
196
433
  }
197
434
 
198
- fn materialized_tracked_json_payloads(rows: &[MaterializedTrackedStateRow]) -> Vec<NormalizedJson> {
199
- let mut payloads = Vec::new();
200
- for row in rows {
201
- if let Some(snapshot) = row.snapshot_content.as_deref() {
202
- payloads.push(NormalizedJson::from_arc_unchecked(Arc::from(snapshot)));
203
- }
204
- if let Some(metadata) = row.metadata.as_ref() {
205
- payloads.push(NormalizedJson::from_arc_unchecked(Arc::from(
206
- crate::serialize_row_metadata(metadata),
207
- )));
208
- }
209
- }
210
- payloads
211
- }
212
-
213
435
  pub(crate) fn untracked_state_row_from_materialized(
214
436
  _writes: &mut StorageWriteSet,
215
437
  row: &MaterializedUntrackedStateRow,
216
438
  ) -> Result<UntrackedStateRow, crate::LixError> {
217
439
  Ok(UntrackedStateRow {
218
- entity_id: row.entity_id.clone(),
440
+ entity_pk: row.entity_pk.clone(),
219
441
  schema_key: row.schema_key.clone(),
220
442
  file_id: row.file_id.clone(),
221
443
  snapshot_content: row.snapshot_content.clone(),
@@ -223,6 +445,6 @@ pub(crate) fn untracked_state_row_from_materialized(
223
445
  created_at: row.created_at.clone(),
224
446
  updated_at: row.updated_at.clone(),
225
447
  global: row.global,
226
- version_id: row.version_id.clone(),
448
+ branch_id: row.branch_id.clone(),
227
449
  })
228
450
  }