@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,113 +0,0 @@
1
- use crate::functions::FunctionProvider;
2
-
3
- const DETERMINISTIC_UUID_COUNTER_MASK: u64 = 0x0000_FFFF_FFFF_FFFF;
4
-
5
- /// Deterministic function provider for engine execution.
6
- ///
7
- /// The provider is pure runtime state: it does not load or persist the sequence
8
- /// itself. Session/transaction code owns that boundary so tests can decide when
9
- /// deterministic state is read and written.
10
- #[derive(Debug, Clone)]
11
- pub(crate) struct DeterministicFunctionProvider {
12
- next_sequence: i64,
13
- timestamp_shuffle: bool,
14
- highest_seen: Option<i64>,
15
- }
16
-
17
- impl DeterministicFunctionProvider {
18
- pub(crate) fn new(next_sequence: i64, timestamp_shuffle: bool) -> Self {
19
- Self {
20
- next_sequence,
21
- timestamp_shuffle,
22
- highest_seen: None,
23
- }
24
- }
25
-
26
- pub(crate) fn highest_seen(&self) -> Option<i64> {
27
- self.highest_seen
28
- }
29
-
30
- fn take_sequence(&mut self) -> i64 {
31
- let current = self.next_sequence;
32
- self.next_sequence += 1;
33
- self.highest_seen = Some(current);
34
- current
35
- }
36
- }
37
-
38
- impl FunctionProvider for DeterministicFunctionProvider {
39
- fn uuid_v7(&mut self) -> String {
40
- let counter = self.take_sequence();
41
- let counter_bits = (counter as u64) & DETERMINISTIC_UUID_COUNTER_MASK;
42
- format!("01920000-0000-7000-8000-{counter_bits:012x}")
43
- }
44
-
45
- fn timestamp(&mut self) -> String {
46
- let counter = self.take_sequence();
47
- let millis = if self.timestamp_shuffle {
48
- shuffled_timestamp_millis(counter)
49
- } else {
50
- counter
51
- };
52
- let dt = chrono::DateTime::<chrono::Utc>::from_timestamp_millis(millis)
53
- .unwrap_or(chrono::DateTime::<chrono::Utc>::UNIX_EPOCH);
54
- dt.to_rfc3339_opts(chrono::SecondsFormat::Millis, true)
55
- }
56
-
57
- fn deterministic_sequence_persist_highest_seen(&self) -> Option<i64> {
58
- self.highest_seen()
59
- }
60
- }
61
-
62
- fn shuffled_timestamp_millis(counter: i64) -> i64 {
63
- const WINDOW: i64 = 1000;
64
- const MULTIPLIER: i64 = 733;
65
- const OFFSET: i64 = 271;
66
-
67
- let cycle = counter.div_euclid(WINDOW);
68
- let within = counter.rem_euclid(WINDOW);
69
- let shuffled = (within * MULTIPLIER + OFFSET).rem_euclid(WINDOW);
70
- cycle * WINDOW + shuffled
71
- }
72
-
73
- #[cfg(test)]
74
- mod tests {
75
- use super::*;
76
- use crate::functions::DeterministicSequence;
77
-
78
- #[test]
79
- fn deterministic_uuid_uses_sequence_counter() {
80
- let mut provider = DeterministicFunctionProvider::new(0, false);
81
-
82
- assert_eq!(provider.uuid_v7(), "01920000-0000-7000-8000-000000000000");
83
- assert_eq!(provider.uuid_v7(), "01920000-0000-7000-8000-000000000001");
84
- assert_eq!(provider.highest_seen(), Some(1));
85
- }
86
-
87
- #[test]
88
- fn deterministic_timestamp_uses_sequence_counter() {
89
- let mut provider = DeterministicFunctionProvider::new(1, false);
90
-
91
- assert_eq!(provider.timestamp(), "1970-01-01T00:00:00.001Z");
92
- assert_eq!(provider.highest_seen(), Some(1));
93
- }
94
-
95
- #[test]
96
- fn deterministic_timestamp_shuffle_can_be_non_monotonic() {
97
- let mut provider = DeterministicFunctionProvider::new(0, true);
98
- let first = provider.timestamp();
99
- let second = provider.timestamp();
100
-
101
- assert!(second < first);
102
- assert_eq!(provider.highest_seen(), Some(1));
103
- }
104
-
105
- #[test]
106
- fn deterministic_sequence_can_start_after_persisted_highest_seen() {
107
- let sequence = DeterministicSequence { highest_seen: 41 };
108
- let mut provider = DeterministicFunctionProvider::new(sequence.next_sequence(), false);
109
-
110
- assert_eq!(provider.uuid_v7(), "01920000-0000-7000-8000-00000000002a");
111
- assert_eq!(provider.highest_seen(), Some(42));
112
- }
113
- }
@@ -1,18 +0,0 @@
1
- //! Engine runtime function boundary.
2
- //!
3
- //! Sessions prepare one function context per execution. SQL, providers, and
4
- //! transaction staging receive only a function provider; deterministic mode is
5
- //! resolved privately inside this module.
6
-
7
- mod context;
8
- mod deterministic;
9
- mod provider;
10
- mod state;
11
- mod types;
12
-
13
- pub(crate) use context::FunctionContext;
14
- pub(crate) use deterministic::DeterministicFunctionProvider;
15
- pub(crate) use provider::{
16
- FunctionProvider, FunctionProviderHandle, SharedFunctionProvider, SystemFunctionProvider,
17
- };
18
- pub(crate) use types::{DeterministicMode, DeterministicSequence};
@@ -1,130 +0,0 @@
1
- use std::sync::{Arc, Mutex};
2
-
3
- use crate::cel::CelFunctionProvider;
4
-
5
- /// Engine-owned runtime function provider trait.
6
- pub(crate) trait FunctionProvider: Send {
7
- fn uuid_v7(&mut self) -> String;
8
- fn timestamp(&mut self) -> String;
9
-
10
- fn deterministic_sequence_persist_highest_seen(&self) -> Option<i64> {
11
- None
12
- }
13
- }
14
-
15
- pub(crate) type FunctionProviderHandle = SharedFunctionProvider<Box<dyn FunctionProvider + Send>>;
16
-
17
- /// Shareable function provider used across SQL planning, UDFs, and staging.
18
- pub(crate) struct SharedFunctionProvider<P> {
19
- inner: Arc<Mutex<P>>,
20
- }
21
-
22
- impl<P> Clone for SharedFunctionProvider<P> {
23
- fn clone(&self) -> Self {
24
- Self {
25
- inner: Arc::clone(&self.inner),
26
- }
27
- }
28
- }
29
-
30
- impl<P> SharedFunctionProvider<P> {
31
- pub(crate) fn new(provider: P) -> Self {
32
- Self {
33
- inner: Arc::new(Mutex::new(provider)),
34
- }
35
- }
36
-
37
- fn with_lock<R>(&self, f: impl FnOnce(&P) -> R) -> R {
38
- let guard = self
39
- .inner
40
- .lock()
41
- .expect("engine function provider mutex poisoned");
42
- f(&guard)
43
- }
44
-
45
- fn with_lock_mut<R>(&self, f: impl FnOnce(&mut P) -> R) -> R {
46
- let mut guard = self
47
- .inner
48
- .lock()
49
- .expect("engine function provider mutex poisoned");
50
- f(&mut guard)
51
- }
52
- }
53
-
54
- impl<P> SharedFunctionProvider<P>
55
- where
56
- P: FunctionProvider,
57
- {
58
- pub(crate) fn call_uuid_v7(&self) -> String {
59
- self.with_lock_mut(|provider| provider.uuid_v7())
60
- }
61
-
62
- pub(crate) fn call_timestamp(&self) -> String {
63
- self.with_lock_mut(|provider| provider.timestamp())
64
- }
65
-
66
- pub(crate) fn deterministic_sequence_persist_highest_seen(&self) -> Option<i64> {
67
- self.with_lock(|provider| provider.deterministic_sequence_persist_highest_seen())
68
- }
69
- }
70
-
71
- impl<P> CelFunctionProvider for SharedFunctionProvider<P>
72
- where
73
- P: FunctionProvider + Send + 'static,
74
- {
75
- fn call_uuid_v7(&self) -> String {
76
- SharedFunctionProvider::call_uuid_v7(self)
77
- }
78
-
79
- fn call_timestamp(&self) -> String {
80
- SharedFunctionProvider::call_timestamp(self)
81
- }
82
- }
83
-
84
- impl<P> FunctionProvider for SharedFunctionProvider<P>
85
- where
86
- P: FunctionProvider,
87
- {
88
- fn uuid_v7(&mut self) -> String {
89
- self.call_uuid_v7()
90
- }
91
-
92
- fn timestamp(&mut self) -> String {
93
- self.call_timestamp()
94
- }
95
-
96
- fn deterministic_sequence_persist_highest_seen(&self) -> Option<i64> {
97
- SharedFunctionProvider::deterministic_sequence_persist_highest_seen(self)
98
- }
99
- }
100
-
101
- impl<T> FunctionProvider for Box<T>
102
- where
103
- T: FunctionProvider + ?Sized,
104
- {
105
- fn uuid_v7(&mut self) -> String {
106
- (**self).uuid_v7()
107
- }
108
-
109
- fn timestamp(&mut self) -> String {
110
- (**self).timestamp()
111
- }
112
-
113
- fn deterministic_sequence_persist_highest_seen(&self) -> Option<i64> {
114
- (**self).deterministic_sequence_persist_highest_seen()
115
- }
116
- }
117
-
118
- /// System-backed engine function provider.
119
- #[derive(Debug, Default, Clone, Copy)]
120
- pub(crate) struct SystemFunctionProvider;
121
-
122
- impl FunctionProvider for SystemFunctionProvider {
123
- fn uuid_v7(&mut self) -> String {
124
- uuid::Uuid::now_v7().to_string()
125
- }
126
-
127
- fn timestamp(&mut self) -> String {
128
- chrono::Utc::now().to_rfc3339_opts(chrono::SecondsFormat::Millis, true)
129
- }
130
- }
@@ -1,336 +0,0 @@
1
- use serde_json::Value as JsonValue;
2
- use std::sync::Arc;
3
-
4
- use crate::entity_identity::EntityIdentity;
5
- use crate::functions::{DeterministicMode, DeterministicSequence};
6
- use crate::json_store::NormalizedJson;
7
- use crate::live_state::{LiveStateReader, LiveStateRowRequest, MaterializedLiveStateRow};
8
- use crate::storage::StorageWriteSet;
9
- use crate::untracked_state::UntrackedStateContext;
10
- use crate::untracked_state::UntrackedStateRow;
11
- use crate::GLOBAL_VERSION_ID;
12
- use crate::{LixError, NullableKeyFilter};
13
-
14
- pub(crate) const DETERMINISTIC_MODE_KEY: &str = "lix_deterministic_mode";
15
- pub(crate) const DETERMINISTIC_SEQUENCE_KEY: &str = "lix_deterministic_sequence_number";
16
-
17
- const KEY_VALUE_SCHEMA_KEY: &str = "lix_key_value";
18
-
19
- /// Loads deterministic-mode settings from visible live state.
20
- ///
21
- /// Missing mode means deterministic execution is disabled. Malformed mode rows
22
- /// are errors because they would make runtime function behavior ambiguous.
23
- pub(crate) async fn load_mode(
24
- live_state: &dyn LiveStateReader,
25
- ) -> Result<DeterministicMode, LixError> {
26
- let Some(row) = load_key_value_row(live_state, DETERMINISTIC_MODE_KEY).await? else {
27
- return Ok(DeterministicMode::disabled());
28
- };
29
- let value = key_value_payload(&row, DETERMINISTIC_MODE_KEY)?;
30
- parse_mode_value(value)
31
- }
32
-
33
- /// Loads the persisted deterministic sequence position.
34
- ///
35
- /// Missing sequence means no deterministic values have been produced yet, so
36
- /// execution starts at sequence zero.
37
- pub(crate) async fn load_sequence(
38
- live_state: &dyn LiveStateReader,
39
- ) -> Result<DeterministicSequence, LixError> {
40
- let Some(row) = load_key_value_row(live_state, DETERMINISTIC_SEQUENCE_KEY).await? else {
41
- return Ok(DeterministicSequence::uninitialized());
42
- };
43
- let value = key_value_payload(&row, DETERMINISTIC_SEQUENCE_KEY)?;
44
- parse_sequence_value(value)
45
- }
46
-
47
- /// Persists the highest deterministic sequence value used by an execution.
48
- ///
49
- /// The row is untracked global `lix_key_value` state: it is durable local
50
- /// runtime state, not a changelog fact.
51
- pub(crate) async fn stage_sequence(
52
- writes: &mut StorageWriteSet,
53
- sequence: DeterministicSequence,
54
- timestamp: &str,
55
- ) -> Result<(), LixError> {
56
- let snapshot_content = serde_json::to_string(&serde_json::json!({
57
- "key": DETERMINISTIC_SEQUENCE_KEY,
58
- "value": sequence.highest_seen,
59
- }))
60
- .map_err(|error| {
61
- LixError::new(
62
- "LIX_ERROR_UNKNOWN",
63
- format!("deterministic sequence snapshot serialization failed: {error}"),
64
- )
65
- })?;
66
- let snapshot = NormalizedJson::from_arc_unchecked(Arc::from(snapshot_content.as_str()));
67
- let row =
68
- deterministic_key_value_row(DETERMINISTIC_SEQUENCE_KEY, snapshot.as_str(), timestamp)?;
69
- UntrackedStateContext::new()
70
- .writer(writes)
71
- .stage_rows(std::iter::once(row.as_ref()))
72
- }
73
-
74
- async fn load_key_value_row(
75
- live_state: &dyn LiveStateReader,
76
- key: &str,
77
- ) -> Result<Option<MaterializedLiveStateRow>, LixError> {
78
- live_state
79
- .load_row(&LiveStateRowRequest {
80
- schema_key: KEY_VALUE_SCHEMA_KEY.to_string(),
81
- version_id: GLOBAL_VERSION_ID.to_string(),
82
- entity_id: EntityIdentity::single(key),
83
- file_id: NullableKeyFilter::Null,
84
- })
85
- .await
86
- }
87
-
88
- fn key_value_payload(row: &MaterializedLiveStateRow, key: &str) -> Result<JsonValue, LixError> {
89
- let snapshot_content = row.snapshot_content.as_deref().ok_or_else(|| {
90
- LixError::new(
91
- "LIX_ERROR_UNKNOWN",
92
- format!("deterministic key-value row '{key}' is missing snapshot_content"),
93
- )
94
- })?;
95
- let snapshot = serde_json::from_str::<JsonValue>(snapshot_content).map_err(|error| {
96
- LixError::new(
97
- "LIX_ERROR_UNKNOWN",
98
- format!("deterministic key-value row '{key}' has invalid JSON: {error}"),
99
- )
100
- })?;
101
- let stored_key = snapshot.get("key").and_then(JsonValue::as_str);
102
- if stored_key != Some(key) {
103
- return Err(LixError::new(
104
- "LIX_ERROR_UNKNOWN",
105
- format!("deterministic key-value row '{key}' has mismatched key field"),
106
- ));
107
- }
108
- snapshot.get("value").cloned().ok_or_else(|| {
109
- LixError::new(
110
- "LIX_ERROR_UNKNOWN",
111
- format!("deterministic key-value row '{key}' is missing value"),
112
- )
113
- })
114
- }
115
-
116
- fn parse_mode_value(value: JsonValue) -> Result<DeterministicMode, LixError> {
117
- let Some(object) = value.as_object() else {
118
- return Err(LixError::new(
119
- "LIX_ERROR_UNKNOWN",
120
- "deterministic mode value must be an object",
121
- ));
122
- };
123
-
124
- let enabled = object
125
- .get("enabled")
126
- .and_then(JsonValue::as_bool)
127
- .unwrap_or(false);
128
- if !enabled {
129
- return Ok(DeterministicMode::disabled());
130
- }
131
- let timestamp_shuffle = object
132
- .get("timestamp_shuffle")
133
- .and_then(JsonValue::as_bool)
134
- .unwrap_or(false);
135
- Ok(DeterministicMode {
136
- enabled,
137
- timestamp_shuffle,
138
- })
139
- }
140
-
141
- fn parse_sequence_value(value: JsonValue) -> Result<DeterministicSequence, LixError> {
142
- let Some(highest_seen) = value.as_i64() else {
143
- return Err(LixError::new(
144
- "LIX_ERROR_UNKNOWN",
145
- "deterministic sequence value must be an integer",
146
- ));
147
- };
148
- Ok(DeterministicSequence { highest_seen })
149
- }
150
-
151
- fn deterministic_key_value_row(
152
- key: &str,
153
- snapshot_content: &str,
154
- timestamp: &str,
155
- ) -> Result<UntrackedStateRow, LixError> {
156
- Ok(UntrackedStateRow {
157
- entity_id: crate::entity_identity::EntityIdentity::single(key),
158
- schema_key: KEY_VALUE_SCHEMA_KEY.to_string(),
159
- file_id: None,
160
- snapshot_content: Some(snapshot_content.to_string()),
161
- metadata: None,
162
- created_at: timestamp.to_string(),
163
- updated_at: timestamp.to_string(),
164
- global: true,
165
- version_id: GLOBAL_VERSION_ID.to_string(),
166
- })
167
- }
168
-
169
- #[cfg(test)]
170
- mod tests {
171
- use std::sync::Arc;
172
-
173
- use crate::backend::testing::UnitTestBackend;
174
- use crate::live_state::{LiveStateContext, LiveStateRowRequest};
175
- use crate::storage::StorageContext;
176
-
177
- use super::*;
178
-
179
- fn live_state_context() -> LiveStateContext {
180
- LiveStateContext::new(
181
- crate::tracked_state::TrackedStateContext::new(),
182
- crate::untracked_state::UntrackedStateContext::new(),
183
- crate::commit_graph::CommitGraphContext::new(),
184
- )
185
- }
186
-
187
- #[tokio::test]
188
- async fn missing_mode_is_disabled() {
189
- let backend = Arc::new(UnitTestBackend::new());
190
- let storage = StorageContext::new(backend.clone());
191
- let live_state = live_state_context();
192
- let reader = live_state.reader(storage.clone());
193
-
194
- let mode = load_mode(&reader)
195
- .await
196
- .expect("missing mode should decode");
197
-
198
- assert_eq!(mode, DeterministicMode::disabled());
199
- }
200
-
201
- #[tokio::test]
202
- async fn valid_mode_decodes_flags() {
203
- let backend = Arc::new(UnitTestBackend::new());
204
- let storage = StorageContext::new(backend.clone());
205
- let live_state = live_state_context();
206
- crate::test_support::seed_global_version_head(storage.clone()).await;
207
- write_test_key_value(
208
- storage.clone(),
209
- DETERMINISTIC_MODE_KEY,
210
- serde_json::json!({
211
- "enabled": true,
212
- "timestamp_shuffle": true,
213
- }),
214
- )
215
- .await;
216
-
217
- let reader = live_state.reader(storage.clone());
218
- let mode = load_mode(&reader).await.expect("valid mode should decode");
219
-
220
- assert_eq!(
221
- mode,
222
- DeterministicMode {
223
- enabled: true,
224
- timestamp_shuffle: true,
225
- }
226
- );
227
- }
228
-
229
- #[tokio::test]
230
- async fn missing_sequence_is_uninitialized() {
231
- let backend = Arc::new(UnitTestBackend::new());
232
- let storage = StorageContext::new(backend.clone());
233
- let live_state = live_state_context();
234
- let reader = live_state.reader(storage.clone());
235
-
236
- let sequence = load_sequence(&reader)
237
- .await
238
- .expect("missing sequence should decode");
239
-
240
- assert_eq!(sequence, DeterministicSequence::uninitialized());
241
- }
242
-
243
- #[tokio::test]
244
- async fn valid_sequence_decodes_highest_seen() {
245
- let backend = Arc::new(UnitTestBackend::new());
246
- let storage = StorageContext::new(backend.clone());
247
- let live_state = live_state_context();
248
- crate::test_support::seed_global_version_head(storage.clone()).await;
249
- write_test_key_value(
250
- storage.clone(),
251
- DETERMINISTIC_SEQUENCE_KEY,
252
- serde_json::json!(41),
253
- )
254
- .await;
255
-
256
- let reader = live_state.reader(storage.clone());
257
- let sequence = load_sequence(&reader)
258
- .await
259
- .expect("valid sequence should decode");
260
-
261
- assert_eq!(sequence, DeterministicSequence { highest_seen: 41 });
262
- assert_eq!(sequence.next_sequence(), 42);
263
- }
264
-
265
- #[tokio::test]
266
- async fn write_sequence_persists_untracked_global_key_value() {
267
- let backend = Arc::new(UnitTestBackend::new());
268
- let storage = StorageContext::new(backend.clone());
269
- let live_state = live_state_context();
270
- crate::test_support::seed_global_version_head(storage.clone()).await;
271
- let mut tx = storage
272
- .begin_write_transaction()
273
- .await
274
- .expect("transaction should open");
275
-
276
- let mut writes = StorageWriteSet::new();
277
- stage_sequence(
278
- &mut writes,
279
- DeterministicSequence { highest_seen: 7 },
280
- "1970-01-01T00:00:00.000Z",
281
- )
282
- .await
283
- .expect("sequence should stage");
284
- writes
285
- .apply(&mut tx.as_mut())
286
- .await
287
- .expect("sequence should apply");
288
- tx.commit().await.expect("transaction should commit");
289
-
290
- let reader = live_state.reader(storage.clone());
291
- let row = reader
292
- .load_row(&LiveStateRowRequest {
293
- schema_key: KEY_VALUE_SCHEMA_KEY.to_string(),
294
- version_id: GLOBAL_VERSION_ID.to_string(),
295
- entity_id: crate::entity_identity::EntityIdentity::single(
296
- DETERMINISTIC_SEQUENCE_KEY,
297
- ),
298
- file_id: NullableKeyFilter::Null,
299
- })
300
- .await
301
- .expect("sequence row should load")
302
- .expect("sequence row should exist");
303
- assert!(row.untracked);
304
- assert!(row.global);
305
- assert_eq!(row.change_id, None);
306
- assert_eq!(row.commit_id, None);
307
- assert_eq!(
308
- row.snapshot_content.as_deref(),
309
- Some("{\"key\":\"lix_deterministic_sequence_number\",\"value\":7}")
310
- );
311
- }
312
-
313
- async fn write_test_key_value(storage: StorageContext, key: &str, value: JsonValue) {
314
- let mut tx = storage
315
- .begin_write_transaction()
316
- .await
317
- .expect("transaction should open");
318
- let snapshot_content = serde_json::to_string(&serde_json::json!({
319
- "key": key,
320
- "value": value,
321
- }))
322
- .expect("snapshot should serialize");
323
- let mut writes = StorageWriteSet::new();
324
- let row = deterministic_key_value_row(key, &snapshot_content, "1970-01-01T00:00:00.000Z")
325
- .expect("test key-value should canonicalize");
326
- UntrackedStateContext::new()
327
- .writer(&mut writes)
328
- .stage_rows(std::iter::once(row.as_ref()))
329
- .expect("test key-value should stage");
330
- writes
331
- .apply(&mut tx.as_mut())
332
- .await
333
- .expect("test key-value should apply");
334
- tx.commit().await.expect("transaction should commit");
335
- }
336
- }
@@ -1,37 +0,0 @@
1
- /// Decoded deterministic-mode setting.
2
- ///
3
- /// Storage can decide where this setting lives. The type only describes the
4
- /// behavior engine should apply while preparing runtime functions.
5
- #[derive(Debug, Clone, Copy, PartialEq, Eq)]
6
- pub(crate) struct DeterministicMode {
7
- pub(crate) enabled: bool,
8
- pub(crate) timestamp_shuffle: bool,
9
- }
10
-
11
- impl DeterministicMode {
12
- pub(crate) fn disabled() -> Self {
13
- Self {
14
- enabled: false,
15
- timestamp_shuffle: false,
16
- }
17
- }
18
- }
19
-
20
- /// Persisted deterministic sequence position.
21
- ///
22
- /// `highest_seen` is the last sequence value returned by the runtime provider.
23
- /// The next deterministic execution starts at `highest_seen + 1`.
24
- #[derive(Debug, Clone, Copy, PartialEq, Eq)]
25
- pub(crate) struct DeterministicSequence {
26
- pub(crate) highest_seen: i64,
27
- }
28
-
29
- impl DeterministicSequence {
30
- pub(crate) fn uninitialized() -> Self {
31
- Self { highest_seen: -1 }
32
- }
33
-
34
- pub(crate) fn next_sequence(self) -> i64 {
35
- self.highest_seen + 1
36
- }
37
- }