@lix-js/sdk 0.6.0-preview.2 → 0.6.0-preview.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (165) hide show
  1. package/SKILL.md +4 -5
  2. package/dist/engine-wasm/wasm/lix_engine.js +1 -1
  3. package/dist/engine-wasm/wasm/lix_engine.wasm +0 -0
  4. package/dist/generated/builtin-schemas.d.ts +87 -162
  5. package/dist/generated/builtin-schemas.js +139 -236
  6. package/dist/open-lix.d.ts +1 -1
  7. package/dist-engine-src/src/binary_cas/types.rs +0 -6
  8. package/dist-engine-src/src/catalog/context.rs +412 -0
  9. package/dist-engine-src/src/catalog/mod.rs +10 -0
  10. package/dist-engine-src/src/catalog/schema.rs +4 -0
  11. package/dist-engine-src/src/catalog/snapshot.rs +1114 -0
  12. package/dist-engine-src/src/cel/mod.rs +1 -1
  13. package/dist-engine-src/src/cel/provider.rs +1 -1
  14. package/dist-engine-src/src/commit_graph/context.rs +328 -1015
  15. package/dist-engine-src/src/commit_graph/mod.rs +2 -3
  16. package/dist-engine-src/src/commit_graph/types.rs +7 -43
  17. package/dist-engine-src/src/commit_graph/walker.rs +57 -81
  18. package/dist-engine-src/src/commit_store/codec.rs +887 -0
  19. package/dist-engine-src/src/commit_store/context.rs +944 -0
  20. package/dist-engine-src/src/commit_store/materialization.rs +84 -0
  21. package/dist-engine-src/src/commit_store/mod.rs +16 -0
  22. package/dist-engine-src/src/commit_store/storage.rs +600 -0
  23. package/dist-engine-src/src/commit_store/types.rs +215 -0
  24. package/dist-engine-src/src/common/identity.rs +15 -5
  25. package/dist-engine-src/src/common/json_pointer.rs +67 -0
  26. package/dist-engine-src/src/common/metadata.rs +17 -12
  27. package/dist-engine-src/src/common/mod.rs +5 -5
  28. package/dist-engine-src/src/domain.rs +324 -0
  29. package/dist-engine-src/src/engine.rs +29 -43
  30. package/dist-engine-src/src/entity_identity.rs +238 -118
  31. package/dist-engine-src/src/functions/context.rs +17 -52
  32. package/dist-engine-src/src/functions/deterministic.rs +1 -1
  33. package/dist-engine-src/src/functions/mod.rs +1 -1
  34. package/dist-engine-src/src/functions/provider.rs +4 -4
  35. package/dist-engine-src/src/functions/state.rs +39 -66
  36. package/dist-engine-src/src/functions/types.rs +1 -1
  37. package/dist-engine-src/src/init.rs +204 -151
  38. package/dist-engine-src/src/json_store/context.rs +354 -60
  39. package/dist-engine-src/src/json_store/encoded.rs +6 -6
  40. package/dist-engine-src/src/json_store/mod.rs +4 -1
  41. package/dist-engine-src/src/json_store/store.rs +884 -11
  42. package/dist-engine-src/src/json_store/types.rs +166 -1
  43. package/dist-engine-src/src/lib.rs +10 -9
  44. package/dist-engine-src/src/live_state/context.rs +608 -830
  45. package/dist-engine-src/src/live_state/mod.rs +3 -3
  46. package/dist-engine-src/src/live_state/overlay.rs +7 -7
  47. package/dist-engine-src/src/live_state/reader.rs +5 -5
  48. package/dist-engine-src/src/live_state/types.rs +19 -36
  49. package/dist-engine-src/src/live_state/visibility.rs +19 -14
  50. package/dist-engine-src/src/plugin/archive.rs +3 -6
  51. package/dist-engine-src/src/plugin/install.rs +0 -18
  52. package/dist-engine-src/src/plugin/plugin_manifest.json +0 -1
  53. package/dist-engine-src/src/schema/annotations/defaults.rs +2 -7
  54. package/dist-engine-src/src/schema/builtin/lix_account.json +0 -1
  55. package/dist-engine-src/src/schema/builtin/lix_active_account.json +0 -1
  56. package/dist-engine-src/src/schema/builtin/lix_binary_blob_ref.json +0 -1
  57. package/dist-engine-src/src/schema/builtin/lix_change.json +11 -10
  58. package/dist-engine-src/src/schema/builtin/lix_change_author.json +0 -1
  59. package/dist-engine-src/src/schema/builtin/lix_commit.json +8 -46
  60. package/dist-engine-src/src/schema/builtin/lix_commit_edge.json +29 -22
  61. package/dist-engine-src/src/schema/builtin/lix_directory_descriptor.json +0 -1
  62. package/dist-engine-src/src/schema/builtin/lix_file_descriptor.json +0 -1
  63. package/dist-engine-src/src/schema/builtin/lix_key_value.json +0 -1
  64. package/dist-engine-src/src/schema/builtin/lix_label.json +10 -3
  65. package/dist-engine-src/src/schema/builtin/lix_label_assignment.json +74 -0
  66. package/dist-engine-src/src/schema/builtin/lix_registered_schema.json +2 -8
  67. package/dist-engine-src/src/schema/builtin/lix_version_descriptor.json +0 -1
  68. package/dist-engine-src/src/schema/builtin/lix_version_ref.json +0 -1
  69. package/dist-engine-src/src/schema/builtin/mod.rs +10 -59
  70. package/dist-engine-src/src/schema/compatibility.rs +787 -0
  71. package/dist-engine-src/src/schema/definition.json +47 -17
  72. package/dist-engine-src/src/schema/definition.rs +202 -96
  73. package/dist-engine-src/src/schema/key.rs +9 -77
  74. package/dist-engine-src/src/schema/mod.rs +4 -4
  75. package/dist-engine-src/src/schema/tests.rs +133 -92
  76. package/dist-engine-src/src/session/context.rs +40 -42
  77. package/dist-engine-src/src/session/create_version.rs +22 -14
  78. package/dist-engine-src/src/session/execute.rs +45 -14
  79. package/dist-engine-src/src/session/merge/apply.rs +4 -4
  80. package/dist-engine-src/src/session/merge/conflicts.rs +3 -2
  81. package/dist-engine-src/src/session/merge/stats.rs +1 -1
  82. package/dist-engine-src/src/session/merge/version.rs +35 -45
  83. package/dist-engine-src/src/session/mod.rs +4 -2
  84. package/dist-engine-src/src/session/optimization9_sql2_bench.rs +100 -0
  85. package/dist-engine-src/src/session/switch_version.rs +16 -28
  86. package/dist-engine-src/src/sql2/change_provider.rs +14 -20
  87. package/dist-engine-src/src/sql2/classify.rs +61 -26
  88. package/dist-engine-src/src/sql2/context.rs +22 -18
  89. package/dist-engine-src/src/sql2/directory_history_provider.rs +28 -20
  90. package/dist-engine-src/src/sql2/directory_provider.rs +131 -83
  91. package/dist-engine-src/src/sql2/entity_history_provider.rs +10 -14
  92. package/dist-engine-src/src/sql2/entity_provider.rs +680 -169
  93. package/dist-engine-src/src/sql2/error.rs +21 -1
  94. package/dist-engine-src/src/sql2/execute.rs +325 -264
  95. package/dist-engine-src/src/sql2/file_history_provider.rs +29 -21
  96. package/dist-engine-src/src/sql2/file_provider.rs +533 -108
  97. package/dist-engine-src/src/sql2/filesystem_planner.rs +58 -94
  98. package/dist-engine-src/src/sql2/filesystem_visibility.rs +37 -23
  99. package/dist-engine-src/src/sql2/history_projection.rs +3 -27
  100. package/dist-engine-src/src/sql2/history_provider.rs +11 -17
  101. package/dist-engine-src/src/sql2/history_route.rs +22 -8
  102. package/dist-engine-src/src/sql2/lix_state_provider.rs +178 -96
  103. package/dist-engine-src/src/sql2/mod.rs +6 -3
  104. package/dist-engine-src/src/sql2/predicate_typecheck.rs +246 -0
  105. package/dist-engine-src/src/sql2/public_bind/assignment.rs +46 -0
  106. package/dist-engine-src/src/sql2/public_bind/capability.rs +41 -0
  107. package/dist-engine-src/src/sql2/public_bind/dml.rs +166 -0
  108. package/dist-engine-src/src/sql2/public_bind/mod.rs +25 -0
  109. package/dist-engine-src/src/sql2/public_bind/table.rs +168 -0
  110. package/dist-engine-src/src/sql2/read_only.rs +10 -12
  111. package/dist-engine-src/src/sql2/session.rs +7 -10
  112. package/dist-engine-src/src/sql2/udfs/lix_timestamp.rs +76 -0
  113. package/dist-engine-src/src/sql2/udfs/mod.rs +8 -1
  114. package/dist-engine-src/src/sql2/udfs/public_call.rs +211 -0
  115. package/dist-engine-src/src/sql2/version_provider.rs +46 -31
  116. package/dist-engine-src/src/sql2/version_scope.rs +4 -4
  117. package/dist-engine-src/src/storage_bench.rs +1782 -325
  118. package/dist-engine-src/src/test_support.rs +183 -36
  119. package/dist-engine-src/src/tracked_state/by_file_index.rs +20 -24
  120. package/dist-engine-src/src/tracked_state/codec.rs +1519 -181
  121. package/dist-engine-src/src/tracked_state/context.rs +1155 -271
  122. package/dist-engine-src/src/tracked_state/diff.rs +249 -57
  123. package/dist-engine-src/src/tracked_state/materialization.rs +365 -103
  124. package/dist-engine-src/src/tracked_state/materializer.rs +488 -0
  125. package/dist-engine-src/src/tracked_state/merge.rs +37 -19
  126. package/dist-engine-src/src/tracked_state/mod.rs +8 -7
  127. package/dist-engine-src/src/tracked_state/storage.rs +138 -6
  128. package/dist-engine-src/src/tracked_state/tree.rs +695 -252
  129. package/dist-engine-src/src/tracked_state/types.rs +176 -6
  130. package/dist-engine-src/src/transaction/commit.rs +695 -435
  131. package/dist-engine-src/src/transaction/context.rs +551 -310
  132. package/dist-engine-src/src/transaction/live_state_overlay.rs +9 -8
  133. package/dist-engine-src/src/transaction/mod.rs +2 -0
  134. package/dist-engine-src/src/transaction/normalization.rs +311 -447
  135. package/dist-engine-src/src/transaction/prep.rs +37 -0
  136. package/dist-engine-src/src/transaction/schema_resolver.rs +93 -71
  137. package/dist-engine-src/src/transaction/staging.rs +701 -406
  138. package/dist-engine-src/src/transaction/types.rs +231 -122
  139. package/dist-engine-src/src/transaction/validation.rs +2717 -1698
  140. package/dist-engine-src/src/untracked_state/codec.rs +40 -96
  141. package/dist-engine-src/src/untracked_state/context.rs +21 -5
  142. package/dist-engine-src/src/untracked_state/materialization.rs +10 -104
  143. package/dist-engine-src/src/untracked_state/mod.rs +3 -5
  144. package/dist-engine-src/src/untracked_state/storage.rs +105 -57
  145. package/dist-engine-src/src/untracked_state/types.rs +63 -13
  146. package/dist-engine-src/src/version/context.rs +1 -13
  147. package/dist-engine-src/src/version/lifecycle.rs +221 -0
  148. package/dist-engine-src/src/version/mod.rs +3 -2
  149. package/dist-engine-src/src/version/refs.rs +12 -103
  150. package/dist-engine-src/src/version/stage_rows.rs +15 -19
  151. package/package.json +1 -1
  152. package/dist-engine-src/src/changelog/codec.rs +0 -321
  153. package/dist-engine-src/src/changelog/context.rs +0 -92
  154. package/dist-engine-src/src/changelog/materialization.rs +0 -121
  155. package/dist-engine-src/src/changelog/mod.rs +0 -13
  156. package/dist-engine-src/src/changelog/reader.rs +0 -20
  157. package/dist-engine-src/src/changelog/storage.rs +0 -220
  158. package/dist-engine-src/src/changelog/types.rs +0 -38
  159. package/dist-engine-src/src/schema/builtin/lix_change_set.json +0 -18
  160. package/dist-engine-src/src/schema/builtin/lix_change_set_element.json +0 -75
  161. package/dist-engine-src/src/schema/builtin/lix_entity_label.json +0 -63
  162. package/dist-engine-src/src/schema_registry.rs +0 -294
  163. package/dist-engine-src/src/sql2/commit_derived_provider.rs +0 -591
  164. package/dist-engine-src/src/tracked_state/rebuild.rs +0 -771
  165. package/dist-engine-src/src/tracked_state/tree_types.rs +0 -176
@@ -10,18 +10,15 @@ use crate::common::{
10
10
  parent_directory_path, stable_content_fingerprint_hex, ParsedFilePath,
11
11
  };
12
12
  use crate::entity_identity::EntityIdentity;
13
- use crate::live_state::LiveStateRow;
14
- use crate::{LixError, RowMetadata};
13
+ use crate::live_state::MaterializedLiveStateRow;
14
+ use crate::LixError;
15
15
 
16
16
  use super::filesystem_visibility::VisibleFilesystem;
17
- use crate::transaction::types::{StageFileData, StageRow};
17
+ use crate::transaction::types::{TransactionFileData, TransactionJson, TransactionWriteRow};
18
18
 
19
19
  pub(crate) const FILE_DESCRIPTOR_SCHEMA_KEY: &str = "lix_file_descriptor";
20
- pub(crate) const FILE_DESCRIPTOR_SCHEMA_VERSION: &str = "1";
21
20
  pub(crate) const DIRECTORY_DESCRIPTOR_SCHEMA_KEY: &str = "lix_directory_descriptor";
22
- pub(crate) const DIRECTORY_DESCRIPTOR_SCHEMA_VERSION: &str = "1";
23
21
  pub(crate) const BLOB_REF_SCHEMA_KEY: &str = "lix_binary_blob_ref";
24
- pub(crate) const BLOB_REF_SCHEMA_VERSION: &str = "1";
25
22
 
26
23
  /// Planned filesystem write output after SQL surface columns have been lowered
27
24
  /// into state rows and optional file payload writes.
@@ -31,8 +28,8 @@ pub(crate) const BLOB_REF_SCHEMA_VERSION: &str = "1";
31
28
  /// filesystem write surface.
32
29
  #[derive(Debug, Clone, PartialEq, Eq, Default)]
33
30
  pub(crate) struct FilesystemWritePlan {
34
- pub(crate) rows: Vec<StageRow>,
35
- pub(crate) file_data: Vec<StageFileData>,
31
+ pub(crate) rows: Vec<TransactionWriteRow>,
32
+ pub(crate) file_data: Vec<TransactionFileData>,
36
33
  pub(crate) count: u64,
37
34
  }
38
35
 
@@ -40,7 +37,7 @@ pub(crate) struct FilesystemWritePlan {
40
37
  /// and the surface delete has been lowered into tombstone state rows.
41
38
  #[derive(Debug, Clone, PartialEq, Eq, Default)]
42
39
  pub(crate) struct FilesystemDeletePlan {
43
- pub(crate) rows: Vec<StageRow>,
40
+ pub(crate) rows: Vec<TransactionWriteRow>,
44
41
  pub(crate) count: u64,
45
42
  }
46
43
 
@@ -51,7 +48,7 @@ pub(crate) struct FilesystemRowContext {
51
48
  pub(crate) global: bool,
52
49
  pub(crate) untracked: bool,
53
50
  pub(crate) file_id: Option<String>,
54
- pub(crate) metadata: Option<RowMetadata>,
51
+ pub(crate) metadata: Option<TransactionJson>,
55
52
  }
56
53
 
57
54
  impl FilesystemRowContext {
@@ -224,7 +221,7 @@ impl DirectoryPathResolver {
224
221
  context: FilesystemRowContext,
225
222
  hidden: bool,
226
223
  generate_directory_id: &mut dyn FnMut() -> String,
227
- ) -> Result<Vec<StageRow>, LixError> {
224
+ ) -> Result<Vec<TransactionWriteRow>, LixError> {
228
225
  self.ensure_directory_path_with_leaf_id(
229
226
  directory_path,
230
227
  None,
@@ -241,7 +238,7 @@ impl DirectoryPathResolver {
241
238
  context: FilesystemRowContext,
242
239
  hidden: bool,
243
240
  generate_directory_id: &mut dyn FnMut() -> String,
244
- ) -> Result<Vec<StageRow>, LixError> {
241
+ ) -> Result<Vec<TransactionWriteRow>, LixError> {
245
242
  self.plan_directory_path(
246
243
  directory_path,
247
244
  leaf_id,
@@ -259,7 +256,7 @@ impl DirectoryPathResolver {
259
256
  context: FilesystemRowContext,
260
257
  hidden: bool,
261
258
  generate_directory_id: &mut dyn FnMut() -> String,
262
- ) -> Result<Vec<StageRow>, LixError> {
259
+ ) -> Result<Vec<TransactionWriteRow>, LixError> {
263
260
  self.plan_directory_path(
264
261
  directory_path,
265
262
  leaf_id,
@@ -278,7 +275,7 @@ impl DirectoryPathResolver {
278
275
  hidden: bool,
279
276
  generate_directory_id: &mut dyn FnMut() -> String,
280
277
  reject_existing_leaf: bool,
281
- ) -> Result<Vec<StageRow>, LixError> {
278
+ ) -> Result<Vec<TransactionWriteRow>, LixError> {
282
279
  let directory_path = normalize_directory_path(directory_path)?;
283
280
  if directory_path == "/" {
284
281
  if reject_existing_leaf {
@@ -402,7 +399,7 @@ fn filesystem_namespace_conflict_error(
402
399
  )
403
400
  }
404
401
 
405
- pub(crate) fn directory_descriptor_row(input: DirectoryDescriptorRowInput) -> StageRow {
402
+ pub(crate) fn directory_descriptor_row(input: DirectoryDescriptorRowInput) -> TransactionWriteRow {
406
403
  directory_descriptor_write_row(DirectoryDescriptorWriteIntent {
407
404
  id: Some(input.id),
408
405
  parent_id: input.parent_id,
@@ -412,7 +409,7 @@ pub(crate) fn directory_descriptor_row(input: DirectoryDescriptorRowInput) -> St
412
409
  })
413
410
  }
414
411
 
415
- pub(crate) fn file_descriptor_row(input: FileDescriptorRowInput) -> StageRow {
412
+ pub(crate) fn file_descriptor_row(input: FileDescriptorRowInput) -> TransactionWriteRow {
416
413
  file_descriptor_write_row(FileDescriptorWriteIntent {
417
414
  id: Some(input.id),
418
415
  directory_id: input.directory_id,
@@ -422,7 +419,9 @@ pub(crate) fn file_descriptor_row(input: FileDescriptorRowInput) -> StageRow {
422
419
  })
423
420
  }
424
421
 
425
- pub(crate) fn directory_descriptor_write_row(input: DirectoryDescriptorWriteIntent) -> StageRow {
422
+ pub(crate) fn directory_descriptor_write_row(
423
+ input: DirectoryDescriptorWriteIntent,
424
+ ) -> TransactionWriteRow {
426
425
  let mut snapshot = JsonMap::new();
427
426
  if let Some(id) = input.id.as_ref() {
428
427
  snapshot.insert("id".to_string(), JsonValue::String(id.clone()));
@@ -443,13 +442,12 @@ pub(crate) fn directory_descriptor_write_row(input: DirectoryDescriptorWriteInte
443
442
  partial_state_row(
444
443
  input.id,
445
444
  DIRECTORY_DESCRIPTOR_SCHEMA_KEY,
446
- DIRECTORY_DESCRIPTOR_SCHEMA_VERSION.to_string(),
447
- Some(JsonValue::Object(snapshot).to_string()),
445
+ Some(JsonValue::Object(snapshot)),
448
446
  input.context,
449
447
  )
450
448
  }
451
449
 
452
- pub(crate) fn file_descriptor_write_row(input: FileDescriptorWriteIntent) -> StageRow {
450
+ pub(crate) fn file_descriptor_write_row(input: FileDescriptorWriteIntent) -> TransactionWriteRow {
453
451
  let mut snapshot = JsonMap::new();
454
452
  if let Some(id) = input.id.as_ref() {
455
453
  snapshot.insert("id".to_string(), JsonValue::String(id.clone()));
@@ -470,13 +468,12 @@ pub(crate) fn file_descriptor_write_row(input: FileDescriptorWriteIntent) -> Sta
470
468
  partial_state_row(
471
469
  input.id,
472
470
  FILE_DESCRIPTOR_SCHEMA_KEY,
473
- FILE_DESCRIPTOR_SCHEMA_VERSION.to_string(),
474
- Some(JsonValue::Object(snapshot).to_string()),
471
+ Some(JsonValue::Object(snapshot)),
475
472
  input.context,
476
473
  )
477
474
  }
478
475
 
479
- pub(crate) fn blob_ref_row(input: BlobRefRowInput) -> Result<StageRow, LixError> {
476
+ pub(crate) fn blob_ref_row(input: BlobRefRowInput) -> Result<TransactionWriteRow, LixError> {
480
477
  let size_bytes = u64::try_from(input.data.len()).map_err(|_| {
481
478
  LixError::new(
482
479
  "LIX_ERROR_UNKNOWN",
@@ -486,18 +483,16 @@ pub(crate) fn blob_ref_row(input: BlobRefRowInput) -> Result<StageRow, LixError>
486
483
  ),
487
484
  )
488
485
  })?;
489
- let snapshot_content = json!({
486
+ let snapshot = json!({
490
487
  "id": input.file_id.clone(),
491
488
  "blob_hash": stable_content_fingerprint_hex(&input.data),
492
489
  "size_bytes": size_bytes,
493
- })
494
- .to_string();
490
+ });
495
491
 
496
492
  Ok(state_row(
497
493
  input.file_id.clone(),
498
494
  BLOB_REF_SCHEMA_KEY,
499
- BLOB_REF_SCHEMA_VERSION.to_string(),
500
- Some(snapshot_content),
495
+ Some(snapshot),
501
496
  FilesystemRowContext {
502
497
  file_id: Some(input.file_id),
503
498
  ..input.context
@@ -549,7 +544,7 @@ pub(crate) fn plan_file_path_write(
549
544
  ..input.context.clone()
550
545
  },
551
546
  })?);
552
- file_data.push(StageFileData {
547
+ file_data.push(TransactionFileData {
553
548
  file_id,
554
549
  version_id: input.context.version_id,
555
550
  untracked: input.context.untracked,
@@ -617,7 +612,6 @@ pub(crate) fn plan_file_delete(input: FileDeleteInput) -> FilesystemDeletePlan {
617
612
  let mut rows = vec![tombstone_row(
618
613
  input.file_id.clone(),
619
614
  FILE_DESCRIPTOR_SCHEMA_KEY,
620
- FILE_DESCRIPTOR_SCHEMA_VERSION.to_string(),
621
615
  FilesystemRowContext {
622
616
  file_id: None,
623
617
  ..input.context.clone()
@@ -628,7 +622,6 @@ pub(crate) fn plan_file_delete(input: FileDeleteInput) -> FilesystemDeletePlan {
628
622
  rows.push(tombstone_row(
629
623
  input.file_id.clone(),
630
624
  BLOB_REF_SCHEMA_KEY,
631
- BLOB_REF_SCHEMA_VERSION.to_string(),
632
625
  FilesystemRowContext {
633
626
  file_id: Some(input.file_id),
634
627
  metadata: None,
@@ -645,7 +638,6 @@ pub(crate) fn plan_directory_delete(input: DirectoryDeleteInput) -> FilesystemDe
645
638
  rows: vec![tombstone_row(
646
639
  input.directory_id,
647
640
  DIRECTORY_DESCRIPTOR_SCHEMA_KEY,
648
- DIRECTORY_DESCRIPTOR_SCHEMA_VERSION.to_string(),
649
641
  FilesystemRowContext {
650
642
  file_id: None,
651
643
  ..input.context
@@ -675,7 +667,7 @@ pub(crate) fn plan_recursive_directory_delete(
675
667
  }
676
668
 
677
669
  pub(crate) fn directory_path_resolvers_from_state_rows(
678
- rows: Vec<LiveStateRow>,
670
+ rows: Vec<MaterializedLiveStateRow>,
679
671
  ) -> Result<BTreeMap<String, DirectoryPathResolver>, LixError> {
680
672
  let mut directory_rows = BTreeMap::<String, BTreeMap<String, DirectoryDescriptorSeed>>::new();
681
673
  let mut file_rows = BTreeMap::<String, Vec<(Option<String>, String, String)>>::new();
@@ -814,37 +806,26 @@ fn directory_parent_cycle_error(directory_id: &str) -> LixError {
814
806
  fn state_row(
815
807
  entity_id: String,
816
808
  schema_key: &str,
817
- schema_version: String,
818
- snapshot_content: Option<String>,
809
+ snapshot: Option<JsonValue>,
819
810
  context: FilesystemRowContext,
820
- ) -> StageRow {
821
- partial_state_row(
822
- Some(entity_id),
823
- schema_key,
824
- schema_version,
825
- snapshot_content,
826
- context,
827
- )
811
+ ) -> TransactionWriteRow {
812
+ partial_state_row(Some(entity_id), schema_key, snapshot, context)
828
813
  }
829
814
 
830
815
  fn partial_state_row(
831
816
  entity_id: Option<String>,
832
817
  schema_key: &str,
833
- schema_version: String,
834
- snapshot_content: Option<String>,
818
+ snapshot: Option<JsonValue>,
835
819
  context: FilesystemRowContext,
836
- ) -> StageRow {
837
- StageRow {
838
- entity_id: entity_id.map(|entity_id| {
839
- EntityIdentity::from_string(&entity_id)
840
- .expect("filesystem entity id should decode as entity identity")
841
- }),
820
+ ) -> TransactionWriteRow {
821
+ let snapshot = snapshot.map(TransactionJson::from_value_unchecked);
822
+ TransactionWriteRow {
823
+ entity_id: entity_id.map(EntityIdentity::single),
842
824
  schema_key: schema_key.to_string(),
843
825
  file_id: context.file_id,
844
- snapshot_content,
826
+ snapshot,
845
827
  metadata: context.metadata,
846
828
  origin: None,
847
- schema_version,
848
829
  created_at: None,
849
830
  updated_at: None,
850
831
  global: context.global,
@@ -858,17 +839,16 @@ fn partial_state_row(
858
839
  fn tombstone_row(
859
840
  entity_id: String,
860
841
  schema_key: &str,
861
- schema_version: String,
862
842
  context: FilesystemRowContext,
863
- ) -> StageRow {
864
- state_row(entity_id, schema_key, schema_version, None, context)
843
+ ) -> TransactionWriteRow {
844
+ state_row(entity_id, schema_key, None, context)
865
845
  }
866
846
 
867
847
  fn collect_recursive_directory_delete(
868
848
  directory_id: &str,
869
849
  visible_filesystem: &VisibleFilesystem,
870
850
  context: &FilesystemRowContext,
871
- rows: &mut Vec<StageRow>,
851
+ rows: &mut Vec<TransactionWriteRow>,
872
852
  count: &mut u64,
873
853
  ) {
874
854
  if let Some(child_ids) = visible_filesystem
@@ -920,7 +900,7 @@ mod tests {
920
900
  use crate::sql2::filesystem_visibility::{
921
901
  VisibleBlobRef, VisibleDirectory, VisibleFile, VisibleFilesystem,
922
902
  };
923
- use crate::{entity_identity::EntityIdentity, live_state::LiveStateRow};
903
+ use crate::{entity_identity::EntityIdentity, live_state::MaterializedLiveStateRow};
924
904
 
925
905
  fn test_id_generator(ids: &'static [&'static str]) -> impl FnMut() -> String {
926
906
  let mut ids = ids.iter();
@@ -942,10 +922,8 @@ mod tests {
942
922
  Some(&crate::entity_identity::EntityIdentity::single("dir-docs"))
943
923
  );
944
924
  assert_eq!(row.schema_key, "lix_directory_descriptor");
945
- assert_eq!(row.schema_version.as_str(), "1");
946
925
  assert_eq!(row.version_id, "version-a");
947
- let snapshot: JsonValue =
948
- serde_json::from_str(row.snapshot_content.as_deref().unwrap()).unwrap();
926
+ let snapshot: JsonValue = row.snapshot.as_ref().unwrap().value().clone();
949
927
  assert_eq!(snapshot["id"], "dir-docs");
950
928
  assert_eq!(snapshot["parent_id"], JsonValue::Null);
951
929
  assert_eq!(snapshot["name"], "docs");
@@ -969,9 +947,7 @@ mod tests {
969
947
  ))
970
948
  );
971
949
  assert_eq!(row.schema_key, "lix_file_descriptor");
972
- assert_eq!(row.schema_version.as_str(), "1");
973
- let snapshot: JsonValue =
974
- serde_json::from_str(row.snapshot_content.as_deref().unwrap()).unwrap();
950
+ let snapshot: JsonValue = row.snapshot.as_ref().unwrap().value().clone();
975
951
  assert_eq!(snapshot["directory_id"], "dir-docs");
976
952
  assert_eq!(snapshot["name"], "readme.md");
977
953
  }
@@ -993,9 +969,7 @@ mod tests {
993
969
  );
994
970
  assert_eq!(row.file_id.as_deref(), Some("file-readme"));
995
971
  assert_eq!(row.schema_key, "lix_binary_blob_ref");
996
- assert_eq!(row.schema_version.as_str(), "1");
997
- let snapshot: JsonValue =
998
- serde_json::from_str(row.snapshot_content.as_deref().unwrap()).unwrap();
972
+ let snapshot: JsonValue = row.snapshot.as_ref().unwrap().value().clone();
999
973
  assert_eq!(snapshot["id"], "file-readme");
1000
974
  assert_eq!(snapshot["size_bytes"], 5);
1001
975
  assert!(snapshot["blob_hash"]
@@ -1025,8 +999,7 @@ mod tests {
1025
999
  Some("dir-generated-nested")
1026
1000
  );
1027
1001
 
1028
- let snapshot: JsonValue =
1029
- serde_json::from_str(rows[0].snapshot_content.as_deref().unwrap()).unwrap();
1002
+ let snapshot: JsonValue = rows[0].snapshot.as_ref().unwrap().value().clone();
1030
1003
  assert_eq!(snapshot["id"], "dir-generated-nested");
1031
1004
  assert_eq!(snapshot["parent_id"], "dir-docs");
1032
1005
  assert_eq!(snapshot["name"], "nested");
@@ -1057,8 +1030,7 @@ mod tests {
1057
1030
  .expect("nested directory should plan");
1058
1031
 
1059
1032
  assert_eq!(nested_rows.len(), 1);
1060
- let snapshot: JsonValue =
1061
- serde_json::from_str(nested_rows[0].snapshot_content.as_deref().unwrap()).unwrap();
1033
+ let snapshot: JsonValue = nested_rows[0].snapshot.as_ref().unwrap().value().clone();
1062
1034
  assert_eq!(snapshot["id"], "dir-generated-nested");
1063
1035
  assert_eq!(snapshot["parent_id"], "dir-generated-docs");
1064
1036
  assert_eq!(snapshot["name"], "nested");
@@ -1089,8 +1061,7 @@ mod tests {
1089
1061
  Some("dir-nested")
1090
1062
  );
1091
1063
 
1092
- let snapshot: JsonValue =
1093
- serde_json::from_str(rows[1].snapshot_content.as_deref().unwrap()).unwrap();
1064
+ let snapshot: JsonValue = rows[1].snapshot.as_ref().unwrap().value().clone();
1094
1065
  assert_eq!(snapshot["id"], "dir-nested");
1095
1066
  assert_eq!(snapshot["parent_id"], "dir-generated-docs");
1096
1067
  assert_eq!(snapshot["name"], "nested");
@@ -1163,8 +1134,7 @@ mod tests {
1163
1134
  .iter()
1164
1135
  .find(|row| row.schema_key == "lix_file_descriptor")
1165
1136
  .expect("file descriptor row should be planned");
1166
- let snapshot: JsonValue =
1167
- serde_json::from_str(file_row.snapshot_content.as_deref().unwrap()).unwrap();
1137
+ let snapshot: JsonValue = file_row.snapshot.as_ref().unwrap().value().clone();
1168
1138
  assert_eq!(snapshot["id"], "file-readme");
1169
1139
  assert_eq!(snapshot["directory_id"], "dir-generated-guides");
1170
1140
  assert_eq!(snapshot["name"], "readme.md");
@@ -1204,8 +1174,7 @@ mod tests {
1204
1174
  .iter()
1205
1175
  .find(|row| row.schema_key == "lix_file_descriptor")
1206
1176
  .expect("file descriptor row should be planned");
1207
- let snapshot: JsonValue =
1208
- serde_json::from_str(file_row.snapshot_content.as_deref().unwrap()).unwrap();
1177
+ let snapshot: JsonValue = file_row.snapshot.as_ref().unwrap().value().clone();
1209
1178
  assert_eq!(snapshot["directory_id"], "dir-guides");
1210
1179
  }
1211
1180
 
@@ -1234,8 +1203,7 @@ mod tests {
1234
1203
  .iter()
1235
1204
  .all(|row| row.schema_key != "lix_binary_blob_ref"));
1236
1205
 
1237
- let snapshot: JsonValue =
1238
- serde_json::from_str(plan.rows[0].snapshot_content.as_deref().unwrap()).unwrap();
1206
+ let snapshot: JsonValue = plan.rows[0].snapshot.as_ref().unwrap().value().clone();
1239
1207
  assert_eq!(snapshot["id"], "file-readme");
1240
1208
  assert_eq!(snapshot["directory_id"], "dir-docs");
1241
1209
  assert_eq!(snapshot["name"], "renamed.md");
@@ -1278,8 +1246,7 @@ mod tests {
1278
1246
  .iter()
1279
1247
  .find(|row| row.schema_key == "lix_file_descriptor")
1280
1248
  .expect("file descriptor row should be planned");
1281
- let snapshot: JsonValue =
1282
- serde_json::from_str(file_row.snapshot_content.as_deref().unwrap()).unwrap();
1249
+ let snapshot: JsonValue = file_row.snapshot.as_ref().unwrap().value().clone();
1283
1250
  assert_eq!(snapshot["directory_id"], "dir-generated-guides");
1284
1251
  assert_eq!(snapshot["name"], "readme.md");
1285
1252
  assert_eq!(snapshot["hidden"], true);
@@ -1338,8 +1305,7 @@ mod tests {
1338
1305
  ))
1339
1306
  );
1340
1307
  assert_eq!(descriptor.file_id, None);
1341
- assert_eq!(descriptor.snapshot_content, None);
1342
- assert_eq!(descriptor.schema_version.as_str(), "1");
1308
+ assert_eq!(descriptor.snapshot, None);
1343
1309
 
1344
1310
  let blob_ref = plan
1345
1311
  .rows
@@ -1353,8 +1319,7 @@ mod tests {
1353
1319
  ))
1354
1320
  );
1355
1321
  assert_eq!(blob_ref.file_id.as_deref(), Some("file-readme"));
1356
- assert_eq!(blob_ref.snapshot_content, None);
1357
- assert_eq!(blob_ref.schema_version.as_str(), "1");
1322
+ assert_eq!(blob_ref.snapshot, None);
1358
1323
  }
1359
1324
 
1360
1325
  #[test]
@@ -1368,7 +1333,7 @@ mod tests {
1368
1333
  assert_eq!(plan.count, 1);
1369
1334
  assert_eq!(plan.rows.len(), 1);
1370
1335
  assert_eq!(plan.rows[0].schema_key, "lix_file_descriptor");
1371
- assert_eq!(plan.rows[0].snapshot_content, None);
1336
+ assert_eq!(plan.rows[0].snapshot, None);
1372
1337
  }
1373
1338
 
1374
1339
  #[test]
@@ -1386,8 +1351,7 @@ mod tests {
1386
1351
  );
1387
1352
  assert_eq!(plan.rows[0].schema_key, "lix_directory_descriptor");
1388
1353
  assert_eq!(plan.rows[0].file_id, None);
1389
- assert_eq!(plan.rows[0].snapshot_content, None);
1390
- assert_eq!(plan.rows[0].schema_version.as_str(), "1");
1354
+ assert_eq!(plan.rows[0].snapshot, None);
1391
1355
  }
1392
1356
 
1393
1357
  #[test]
@@ -1447,7 +1411,7 @@ mod tests {
1447
1411
  row.entity_id
1448
1412
  .as_ref()
1449
1413
  .expect("planned recursive delete row should carry entity_id")
1450
- .as_string()
1414
+ .as_single_string_owned()
1451
1415
  .expect("planned recursive delete row should project entity_id"),
1452
1416
  )
1453
1417
  })
@@ -1460,7 +1424,7 @@ mod tests {
1460
1424
  ("lix_directory_descriptor", "dir-docs".to_string()),
1461
1425
  ]
1462
1426
  );
1463
- assert!(plan.rows.iter().all(|row| row.snapshot_content.is_none()));
1427
+ assert!(plan.rows.iter().all(|row| row.snapshot.is_none()));
1464
1428
  }
1465
1429
 
1466
1430
  fn visible_directory(
@@ -1506,14 +1470,14 @@ mod tests {
1506
1470
  entity_id: &str,
1507
1471
  version_id: &str,
1508
1472
  snapshot_content: &str,
1509
- ) -> LiveStateRow {
1510
- LiveStateRow {
1511
- entity_id: EntityIdentity::from_string(entity_id).expect("entity id should decode"),
1473
+ ) -> MaterializedLiveStateRow {
1474
+ MaterializedLiveStateRow {
1475
+ entity_id: EntityIdentity::single(entity_id),
1512
1476
  schema_key: "lix_directory_descriptor".to_string(),
1513
1477
  file_id: None,
1514
1478
  snapshot_content: Some(snapshot_content.to_string()),
1515
1479
  metadata: None,
1516
- schema_version: "1".to_string(),
1480
+ deleted: false,
1517
1481
  version_id: version_id.to_string(),
1518
1482
  change_id: Some(format!("change-{entity_id}")),
1519
1483
  commit_id: Some(format!("commit-{entity_id}")),
@@ -5,7 +5,7 @@ use std::sync::Arc;
5
5
 
6
6
  use serde::Deserialize;
7
7
 
8
- use crate::live_state::LiveStateRow;
8
+ use crate::live_state::MaterializedLiveStateRow;
9
9
  use crate::live_state::{LiveStateFilter, LiveStateReader, LiveStateScanRequest};
10
10
  use crate::LixError;
11
11
 
@@ -16,7 +16,7 @@ use super::filesystem_planner::{
16
16
 
17
17
  /// Execution-visible filesystem metadata decoded from live-state rows.
18
18
  ///
19
- /// The helper intentionally depends only on `LiveStateReader`. In engine2
19
+ /// The helper intentionally depends only on `LiveStateReader`. In engine
20
20
  /// write execution that context may include staged rows, so filesystem planning
21
21
  /// sees pending writes without reaching into write-execution internals.
22
22
  #[derive(Debug, Clone, PartialEq, Eq, Default)]
@@ -53,7 +53,7 @@ impl VisibleFilesystem {
53
53
 
54
54
  /// Builds filesystem lookup indexes from rows that are already known to be
55
55
  /// transaction-visible.
56
- pub(crate) fn from_live_rows(rows: Vec<LiveStateRow>) -> Result<Self, LixError> {
56
+ pub(crate) fn from_live_rows(rows: Vec<MaterializedLiveStateRow>) -> Result<Self, LixError> {
57
57
  let mut visible = Self::default();
58
58
 
59
59
  for row in rows {
@@ -74,7 +74,7 @@ impl VisibleFilesystem {
74
74
  parent_id: snapshot.parent_id,
75
75
  name: snapshot.name,
76
76
  hidden: snapshot.hidden.unwrap_or(false),
77
- context: filesystem_row_context(&row),
77
+ context: filesystem_row_context(&row)?,
78
78
  };
79
79
  visible
80
80
  .directory_children_by_parent_id
@@ -98,7 +98,7 @@ impl VisibleFilesystem {
98
98
  directory_id: snapshot.directory_id,
99
99
  name: snapshot.name,
100
100
  hidden: snapshot.hidden,
101
- context: filesystem_row_context(&row),
101
+ context: filesystem_row_context(&row)?,
102
102
  };
103
103
  visible
104
104
  .files_by_directory_id
@@ -120,7 +120,7 @@ impl VisibleFilesystem {
120
120
  file_id: snapshot.id,
121
121
  blob_hash: snapshot.blob_hash,
122
122
  size_bytes: snapshot.size_bytes,
123
- context: filesystem_row_context(&row),
123
+ context: filesystem_row_context(&row)?,
124
124
  },
125
125
  );
126
126
  }
@@ -181,21 +181,36 @@ struct BlobRefSnapshot {
181
181
  size_bytes: Option<u64>,
182
182
  }
183
183
 
184
- fn filesystem_row_context(row: &LiveStateRow) -> FilesystemRowContext {
185
- FilesystemRowContext {
184
+ fn filesystem_row_context(
185
+ row: &MaterializedLiveStateRow,
186
+ ) -> Result<FilesystemRowContext, LixError> {
187
+ Ok(FilesystemRowContext {
186
188
  version_id: row.version_id.clone(),
187
189
  global: row.global,
188
190
  untracked: row.untracked,
189
191
  file_id: row.file_id.clone(),
190
- metadata: row.metadata.clone(),
191
- }
192
+ metadata: row
193
+ .metadata
194
+ .as_deref()
195
+ .map(|metadata| {
196
+ crate::parse_row_metadata_value(metadata, "filesystem row metadata").and_then(
197
+ |metadata| {
198
+ crate::transaction::types::TransactionJson::from_value(
199
+ metadata,
200
+ "filesystem row metadata",
201
+ )
202
+ },
203
+ )
204
+ })
205
+ .transpose()?,
206
+ })
192
207
  }
193
208
 
194
209
  #[cfg(test)]
195
210
  mod tests {
196
211
  use async_trait::async_trait;
197
212
 
198
- use crate::live_state::LiveStateRow;
213
+ use crate::live_state::MaterializedLiveStateRow;
199
214
  use crate::live_state::{LiveStateReader, LiveStateRowRequest, LiveStateScanRequest};
200
215
  use crate::LixError;
201
216
 
@@ -281,12 +296,12 @@ mod tests {
281
296
  assert_eq!(blob_ref.size_bytes, Some(5));
282
297
  }
283
298
 
284
- fn live_state(rows: Vec<LiveStateRow>) -> std::sync::Arc<dyn LiveStateReader> {
299
+ fn live_state(rows: Vec<MaterializedLiveStateRow>) -> std::sync::Arc<dyn LiveStateReader> {
285
300
  std::sync::Arc::new(RowsLiveStateReader { rows })
286
301
  }
287
302
 
288
303
  struct RowsLiveStateReader {
289
- rows: Vec<LiveStateRow>,
304
+ rows: Vec<MaterializedLiveStateRow>,
290
305
  }
291
306
 
292
307
  #[async_trait]
@@ -294,7 +309,7 @@ mod tests {
294
309
  async fn scan_rows(
295
310
  &self,
296
311
  request: &LiveStateScanRequest,
297
- ) -> Result<Vec<LiveStateRow>, LixError> {
312
+ ) -> Result<Vec<MaterializedLiveStateRow>, LixError> {
298
313
  Ok(self
299
314
  .rows
300
315
  .iter()
@@ -311,12 +326,12 @@ mod tests {
311
326
  async fn load_row(
312
327
  &self,
313
328
  _request: &LiveStateRowRequest,
314
- ) -> Result<Option<LiveStateRow>, LixError> {
329
+ ) -> Result<Option<MaterializedLiveStateRow>, LixError> {
315
330
  Ok(None)
316
331
  }
317
332
  }
318
333
 
319
- fn directory_row(entity_id: &str, snapshot_content: &str) -> LiveStateRow {
334
+ fn directory_row(entity_id: &str, snapshot_content: &str) -> MaterializedLiveStateRow {
320
335
  live_row(
321
336
  entity_id,
322
337
  DIRECTORY_DESCRIPTOR_SCHEMA_KEY,
@@ -325,7 +340,7 @@ mod tests {
325
340
  )
326
341
  }
327
342
 
328
- fn file_row(entity_id: &str, snapshot_content: &str) -> LiveStateRow {
343
+ fn file_row(entity_id: &str, snapshot_content: &str) -> MaterializedLiveStateRow {
329
344
  live_row(
330
345
  entity_id,
331
346
  FILE_DESCRIPTOR_SCHEMA_KEY,
@@ -334,7 +349,7 @@ mod tests {
334
349
  )
335
350
  }
336
351
 
337
- fn blob_ref_row(entity_id: &str, snapshot_content: &str) -> LiveStateRow {
352
+ fn blob_ref_row(entity_id: &str, snapshot_content: &str) -> MaterializedLiveStateRow {
338
353
  live_row(
339
354
  entity_id,
340
355
  BLOB_REF_SCHEMA_KEY,
@@ -348,15 +363,14 @@ mod tests {
348
363
  schema_key: &str,
349
364
  file_id: Option<String>,
350
365
  snapshot_content: &str,
351
- ) -> LiveStateRow {
352
- LiveStateRow {
353
- entity_id: crate::entity_identity::EntityIdentity::from_string(entity_id)
354
- .expect("entity id should decode"),
366
+ ) -> MaterializedLiveStateRow {
367
+ MaterializedLiveStateRow {
368
+ entity_id: crate::entity_identity::EntityIdentity::single(entity_id),
355
369
  schema_key: schema_key.to_string(),
356
370
  file_id,
357
371
  snapshot_content: Some(snapshot_content.to_string()),
358
372
  metadata: None,
359
- schema_version: "1".to_string(),
373
+ deleted: false,
360
374
  version_id: "version-a".to_string(),
361
375
  change_id: Some(format!("change-{entity_id}")),
362
376
  commit_id: Some(format!("commit-{entity_id}")),
@@ -1,6 +1,6 @@
1
1
  use serde_json::Value as JsonValue;
2
2
 
3
- use crate::entity_identity::{EntityIdentity, EntityIdentityPart};
3
+ use crate::entity_identity::EntityIdentity;
4
4
  use crate::LixError;
5
5
 
6
6
  /// Shared projection contract for typed history views.
@@ -44,7 +44,7 @@ fn primary_key_tombstone_value(
44
44
  return Ok(None);
45
45
  };
46
46
 
47
- let identity = EntityIdentity::from_string(entity_id).map_err(|error| {
47
+ let identity = EntityIdentity::from_json_array_text(entity_id).map_err(|error| {
48
48
  LixError::unknown(format!(
49
49
  "failed to decode history tombstone entity identity: {error}"
50
50
  ))
@@ -52,29 +52,5 @@ fn primary_key_tombstone_value(
52
52
  Ok(identity
53
53
  .parts
54
54
  .get(part_index)
55
- .map(entity_identity_part_json_value))
56
- }
57
-
58
- fn entity_identity_part_json_value(part: &EntityIdentityPart) -> JsonValue {
59
- match part {
60
- EntityIdentityPart::String(value) => JsonValue::String(value.clone()),
61
- EntityIdentityPart::Bool(value) => JsonValue::Bool(*value),
62
- EntityIdentityPart::Number(value) => value
63
- .parse::<i64>()
64
- .map(|value| JsonValue::Number(value.into()))
65
- .or_else(|_| {
66
- value
67
- .parse::<u64>()
68
- .map(|value| JsonValue::Number(value.into()))
69
- })
70
- .ok()
71
- .or_else(|| {
72
- value
73
- .parse::<f64>()
74
- .ok()
75
- .and_then(serde_json::Number::from_f64)
76
- .map(JsonValue::Number)
77
- })
78
- .unwrap_or_else(|| JsonValue::String(value.clone())),
79
- }
55
+ .map(|part| JsonValue::String(part.clone())))
80
56
  }