@lix-js/sdk 0.6.0-preview.1 → 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 (205) hide show
  1. package/SKILL.md +304 -320
  2. package/dist/engine-wasm/wasm/lix_engine.d.ts +5 -0
  3. package/dist/engine-wasm/wasm/lix_engine.js +9 -13
  4. package/dist/engine-wasm/wasm/lix_engine.wasm +0 -0
  5. package/dist/engine-wasm/wasm/lix_engine.wasm.d.ts +1 -0
  6. package/dist/generated/builtin-schemas.d.ts +87 -162
  7. package/dist/generated/builtin-schemas.js +139 -236
  8. package/dist/open-lix.d.ts +103 -14
  9. package/dist/open-lix.js +3 -0
  10. package/dist/sqlite/index.js +99 -22
  11. package/dist-engine-src/README.md +18 -0
  12. package/dist-engine-src/src/backend/kv.rs +358 -0
  13. package/dist-engine-src/src/backend/mod.rs +12 -0
  14. package/dist-engine-src/src/backend/testing.rs +658 -0
  15. package/dist-engine-src/src/backend/types.rs +96 -0
  16. package/dist-engine-src/src/binary_cas/chunking.rs +31 -0
  17. package/dist-engine-src/src/binary_cas/codec.rs +346 -0
  18. package/dist-engine-src/src/binary_cas/context.rs +139 -0
  19. package/dist-engine-src/src/binary_cas/kv.rs +1063 -0
  20. package/dist-engine-src/src/binary_cas/mod.rs +11 -0
  21. package/dist-engine-src/src/binary_cas/types.rs +121 -0
  22. package/dist-engine-src/src/catalog/context.rs +412 -0
  23. package/dist-engine-src/src/catalog/mod.rs +10 -0
  24. package/dist-engine-src/src/catalog/schema.rs +4 -0
  25. package/dist-engine-src/src/catalog/snapshot.rs +1114 -0
  26. package/dist-engine-src/src/cel/context.rs +86 -0
  27. package/dist-engine-src/src/cel/error.rs +19 -0
  28. package/dist-engine-src/src/cel/mod.rs +8 -0
  29. package/dist-engine-src/src/cel/provider.rs +9 -0
  30. package/dist-engine-src/src/cel/runtime.rs +167 -0
  31. package/dist-engine-src/src/cel/value.rs +50 -0
  32. package/dist-engine-src/src/commit_graph/context.rs +901 -0
  33. package/dist-engine-src/src/commit_graph/mod.rs +11 -0
  34. package/dist-engine-src/src/commit_graph/types.rs +109 -0
  35. package/dist-engine-src/src/commit_graph/walker.rs +756 -0
  36. package/dist-engine-src/src/commit_store/codec.rs +887 -0
  37. package/dist-engine-src/src/commit_store/context.rs +944 -0
  38. package/dist-engine-src/src/commit_store/materialization.rs +84 -0
  39. package/dist-engine-src/src/commit_store/mod.rs +16 -0
  40. package/dist-engine-src/src/commit_store/storage.rs +600 -0
  41. package/dist-engine-src/src/commit_store/types.rs +215 -0
  42. package/dist-engine-src/src/common/error.rs +313 -0
  43. package/dist-engine-src/src/common/fingerprint.rs +3 -0
  44. package/dist-engine-src/src/common/fs_path.rs +1336 -0
  45. package/dist-engine-src/src/common/identity.rs +145 -0
  46. package/dist-engine-src/src/common/json_pointer.rs +67 -0
  47. package/dist-engine-src/src/common/metadata.rs +40 -0
  48. package/dist-engine-src/src/common/mod.rs +23 -0
  49. package/dist-engine-src/src/common/types.rs +105 -0
  50. package/dist-engine-src/src/common/wire.rs +222 -0
  51. package/dist-engine-src/src/domain.rs +324 -0
  52. package/dist-engine-src/src/engine.rs +225 -0
  53. package/dist-engine-src/src/entity_identity.rs +405 -0
  54. package/dist-engine-src/src/functions/context.rs +292 -0
  55. package/dist-engine-src/src/functions/deterministic.rs +113 -0
  56. package/dist-engine-src/src/functions/mod.rs +18 -0
  57. package/dist-engine-src/src/functions/provider.rs +130 -0
  58. package/dist-engine-src/src/functions/state.rs +336 -0
  59. package/dist-engine-src/src/functions/types.rs +37 -0
  60. package/dist-engine-src/src/init.rs +558 -0
  61. package/dist-engine-src/src/json_store/compression.rs +77 -0
  62. package/dist-engine-src/src/json_store/context.rs +423 -0
  63. package/dist-engine-src/src/json_store/encoded.rs +15 -0
  64. package/dist-engine-src/src/json_store/mod.rs +12 -0
  65. package/dist-engine-src/src/json_store/store.rs +1109 -0
  66. package/dist-engine-src/src/json_store/types.rs +217 -0
  67. package/dist-engine-src/src/lib.rs +62 -0
  68. package/dist-engine-src/src/live_state/context.rs +2019 -0
  69. package/dist-engine-src/src/live_state/mod.rs +15 -0
  70. package/dist-engine-src/src/live_state/overlay.rs +75 -0
  71. package/dist-engine-src/src/live_state/reader.rs +23 -0
  72. package/dist-engine-src/src/live_state/types.rs +222 -0
  73. package/dist-engine-src/src/live_state/visibility.rs +223 -0
  74. package/dist-engine-src/src/plugin/archive.rs +438 -0
  75. package/dist-engine-src/src/plugin/component.rs +183 -0
  76. package/dist-engine-src/src/plugin/install.rs +619 -0
  77. package/dist-engine-src/src/plugin/manifest.rs +516 -0
  78. package/dist-engine-src/src/plugin/materializer.rs +477 -0
  79. package/dist-engine-src/src/plugin/mod.rs +33 -0
  80. package/dist-engine-src/src/plugin/plugin_manifest.json +118 -0
  81. package/dist-engine-src/src/plugin/storage.rs +74 -0
  82. package/dist-engine-src/src/schema/annotations/defaults.rs +275 -0
  83. package/dist-engine-src/src/schema/annotations/mod.rs +1 -0
  84. package/dist-engine-src/src/schema/builtin/lix_account.json +21 -0
  85. package/dist-engine-src/src/schema/builtin/lix_active_account.json +29 -0
  86. package/dist-engine-src/src/schema/builtin/lix_binary_blob_ref.json +29 -0
  87. package/dist-engine-src/src/schema/builtin/lix_change.json +63 -0
  88. package/dist-engine-src/src/schema/builtin/lix_change_author.json +45 -0
  89. package/dist-engine-src/src/schema/builtin/lix_commit.json +24 -0
  90. package/dist-engine-src/src/schema/builtin/lix_commit_edge.json +53 -0
  91. package/dist-engine-src/src/schema/builtin/lix_directory_descriptor.json +52 -0
  92. package/dist-engine-src/src/schema/builtin/lix_file_descriptor.json +52 -0
  93. package/dist-engine-src/src/schema/builtin/lix_key_value.json +40 -0
  94. package/dist-engine-src/src/schema/builtin/lix_label.json +29 -0
  95. package/dist-engine-src/src/schema/builtin/lix_label_assignment.json +74 -0
  96. package/dist-engine-src/src/schema/builtin/lix_registered_schema.json +25 -0
  97. package/dist-engine-src/src/schema/builtin/lix_version_descriptor.json +34 -0
  98. package/dist-engine-src/src/schema/builtin/lix_version_ref.json +48 -0
  99. package/dist-engine-src/src/schema/builtin/mod.rs +222 -0
  100. package/dist-engine-src/src/schema/compatibility.rs +787 -0
  101. package/dist-engine-src/src/schema/definition.json +187 -0
  102. package/dist-engine-src/src/schema/definition.rs +742 -0
  103. package/dist-engine-src/src/schema/key.rs +138 -0
  104. package/dist-engine-src/src/schema/mod.rs +20 -0
  105. package/dist-engine-src/src/schema/seed.rs +14 -0
  106. package/dist-engine-src/src/schema/tests.rs +780 -0
  107. package/dist-engine-src/src/session/context.rs +364 -0
  108. package/dist-engine-src/src/session/create_version.rs +88 -0
  109. package/dist-engine-src/src/session/execute.rs +478 -0
  110. package/dist-engine-src/src/session/merge/analysis.rs +102 -0
  111. package/dist-engine-src/src/session/merge/apply.rs +23 -0
  112. package/dist-engine-src/src/session/merge/conflicts.rs +63 -0
  113. package/dist-engine-src/src/session/merge/mod.rs +11 -0
  114. package/dist-engine-src/src/session/merge/stats.rs +65 -0
  115. package/dist-engine-src/src/session/merge/version.rs +427 -0
  116. package/dist-engine-src/src/session/mod.rs +27 -0
  117. package/dist-engine-src/src/session/optimization9_sql2_bench.rs +100 -0
  118. package/dist-engine-src/src/session/switch_version.rs +109 -0
  119. package/dist-engine-src/src/sql2/change_provider.rs +331 -0
  120. package/dist-engine-src/src/sql2/classify.rs +182 -0
  121. package/dist-engine-src/src/sql2/context.rs +311 -0
  122. package/dist-engine-src/src/sql2/directory_history_provider.rs +631 -0
  123. package/dist-engine-src/src/sql2/directory_provider.rs +2453 -0
  124. package/dist-engine-src/src/sql2/dml.rs +148 -0
  125. package/dist-engine-src/src/sql2/entity_history_provider.rs +440 -0
  126. package/dist-engine-src/src/sql2/entity_provider.rs +3211 -0
  127. package/dist-engine-src/src/sql2/error.rs +216 -0
  128. package/dist-engine-src/src/sql2/execute.rs +3440 -0
  129. package/dist-engine-src/src/sql2/file_history_provider.rs +910 -0
  130. package/dist-engine-src/src/sql2/file_provider.rs +3679 -0
  131. package/dist-engine-src/src/sql2/filesystem_planner.rs +1490 -0
  132. package/dist-engine-src/src/sql2/filesystem_predicates.rs +159 -0
  133. package/dist-engine-src/src/sql2/filesystem_visibility.rs +383 -0
  134. package/dist-engine-src/src/sql2/history_projection.rs +56 -0
  135. package/dist-engine-src/src/sql2/history_provider.rs +412 -0
  136. package/dist-engine-src/src/sql2/history_route.rs +657 -0
  137. package/dist-engine-src/src/sql2/lix_state_provider.rs +2512 -0
  138. package/dist-engine-src/src/sql2/mod.rs +46 -0
  139. package/dist-engine-src/src/sql2/predicate_typecheck.rs +246 -0
  140. package/dist-engine-src/src/sql2/public_bind/assignment.rs +46 -0
  141. package/dist-engine-src/src/sql2/public_bind/capability.rs +41 -0
  142. package/dist-engine-src/src/sql2/public_bind/dml.rs +166 -0
  143. package/dist-engine-src/src/sql2/public_bind/mod.rs +25 -0
  144. package/dist-engine-src/src/sql2/public_bind/table.rs +168 -0
  145. package/dist-engine-src/src/sql2/read_only.rs +63 -0
  146. package/dist-engine-src/src/sql2/record_batch.rs +17 -0
  147. package/dist-engine-src/src/sql2/result_metadata.rs +29 -0
  148. package/dist-engine-src/src/sql2/runtime.rs +60 -0
  149. package/dist-engine-src/src/sql2/session.rs +132 -0
  150. package/dist-engine-src/src/sql2/udfs/common.rs +295 -0
  151. package/dist-engine-src/src/sql2/udfs/lix_active_version_commit_id.rs +53 -0
  152. package/dist-engine-src/src/sql2/udfs/lix_empty_blob.rs +47 -0
  153. package/dist-engine-src/src/sql2/udfs/lix_json.rs +100 -0
  154. package/dist-engine-src/src/sql2/udfs/lix_json_get.rs +99 -0
  155. package/dist-engine-src/src/sql2/udfs/lix_json_get_text.rs +99 -0
  156. package/dist-engine-src/src/sql2/udfs/lix_text_decode.rs +82 -0
  157. package/dist-engine-src/src/sql2/udfs/lix_text_encode.rs +85 -0
  158. package/dist-engine-src/src/sql2/udfs/lix_timestamp.rs +76 -0
  159. package/dist-engine-src/src/sql2/udfs/lix_uuid_v7.rs +76 -0
  160. package/dist-engine-src/src/sql2/udfs/mod.rs +89 -0
  161. package/dist-engine-src/src/sql2/udfs/public_call.rs +211 -0
  162. package/dist-engine-src/src/sql2/version_provider.rs +1202 -0
  163. package/dist-engine-src/src/sql2/version_scope.rs +394 -0
  164. package/dist-engine-src/src/sql2/write_normalization.rs +345 -0
  165. package/dist-engine-src/src/storage/context.rs +356 -0
  166. package/dist-engine-src/src/storage/mod.rs +14 -0
  167. package/dist-engine-src/src/storage/read_scope.rs +88 -0
  168. package/dist-engine-src/src/storage/types.rs +501 -0
  169. package/dist-engine-src/src/storage_bench.rs +4863 -0
  170. package/dist-engine-src/src/test_support.rs +228 -0
  171. package/dist-engine-src/src/tracked_state/by_file_index.rs +98 -0
  172. package/dist-engine-src/src/tracked_state/codec.rs +2085 -0
  173. package/dist-engine-src/src/tracked_state/context.rs +1867 -0
  174. package/dist-engine-src/src/tracked_state/diff.rs +686 -0
  175. package/dist-engine-src/src/tracked_state/materialization.rs +403 -0
  176. package/dist-engine-src/src/tracked_state/materializer.rs +488 -0
  177. package/dist-engine-src/src/tracked_state/merge.rs +492 -0
  178. package/dist-engine-src/src/tracked_state/mod.rs +32 -0
  179. package/dist-engine-src/src/tracked_state/storage.rs +375 -0
  180. package/dist-engine-src/src/tracked_state/tree.rs +3187 -0
  181. package/dist-engine-src/src/tracked_state/types.rs +231 -0
  182. package/dist-engine-src/src/transaction/commit.rs +1484 -0
  183. package/dist-engine-src/src/transaction/context.rs +1548 -0
  184. package/dist-engine-src/src/transaction/live_state_overlay.rs +35 -0
  185. package/dist-engine-src/src/transaction/mod.rs +13 -0
  186. package/dist-engine-src/src/transaction/normalization.rs +890 -0
  187. package/dist-engine-src/src/transaction/prep.rs +37 -0
  188. package/dist-engine-src/src/transaction/schema_resolver.rs +149 -0
  189. package/dist-engine-src/src/transaction/staging.rs +1731 -0
  190. package/dist-engine-src/src/transaction/types.rs +460 -0
  191. package/dist-engine-src/src/transaction/validation.rs +5830 -0
  192. package/dist-engine-src/src/untracked_state/codec.rs +307 -0
  193. package/dist-engine-src/src/untracked_state/context.rs +98 -0
  194. package/dist-engine-src/src/untracked_state/materialization.rs +63 -0
  195. package/dist-engine-src/src/untracked_state/mod.rs +15 -0
  196. package/dist-engine-src/src/untracked_state/storage.rs +396 -0
  197. package/dist-engine-src/src/untracked_state/types.rs +146 -0
  198. package/dist-engine-src/src/version/context.rs +40 -0
  199. package/dist-engine-src/src/version/lifecycle.rs +221 -0
  200. package/dist-engine-src/src/version/mod.rs +13 -0
  201. package/dist-engine-src/src/version/refs.rs +330 -0
  202. package/dist-engine-src/src/version/stage_rows.rs +67 -0
  203. package/dist-engine-src/src/version/types.rs +21 -0
  204. package/dist-engine-src/src/wasm/mod.rs +60 -0
  205. package/package.json +68 -64
@@ -0,0 +1,86 @@
1
+ use cel::Context;
2
+ use serde_json::{Map as JsonMap, Value as JsonValue};
3
+
4
+ use crate::LixError;
5
+
6
+ use super::provider::CelFunctionProvider;
7
+ use super::value::json_to_cel;
8
+
9
+ pub(crate) fn build_context_with_functions<P>(
10
+ variables: &JsonMap<String, JsonValue>,
11
+ functions: P,
12
+ ) -> Result<Context<'static>, LixError>
13
+ where
14
+ P: CelFunctionProvider,
15
+ {
16
+ let mut context = Context::default();
17
+
18
+ let uuid_functions = functions.clone();
19
+ context.add_function("lix_uuid_v7", move || uuid_functions.call_uuid_v7());
20
+ let timestamp_functions = functions.clone();
21
+ context.add_function("lix_timestamp", move || {
22
+ timestamp_functions.call_timestamp()
23
+ });
24
+
25
+ for (name, value) in variables {
26
+ let cel_value = json_to_cel(value)?;
27
+ context.add_variable_from_value(name.clone(), cel_value);
28
+ }
29
+
30
+ Ok(context)
31
+ }
32
+
33
+ #[cfg(test)]
34
+ mod tests {
35
+ use super::build_context_with_functions;
36
+ use crate::cel::CelFunctionProvider;
37
+ use cel::Program;
38
+ use serde_json::Map as JsonMap;
39
+
40
+ #[test]
41
+ fn registers_lix_uuid_v7_function() {
42
+ let context = build_context_with_functions(&JsonMap::new(), fixed_functions())
43
+ .expect("build context");
44
+ let program = Program::compile("lix_uuid_v7()").expect("compile CEL");
45
+ let value = program.execute(&context).expect("execute CEL");
46
+ let as_json = value.json().expect("to json");
47
+ assert!(as_json.as_str().is_some());
48
+ }
49
+
50
+ #[test]
51
+ fn errors_on_unknown_variables() {
52
+ let context = build_context_with_functions(&JsonMap::new(), fixed_functions())
53
+ .expect("build context");
54
+ let program = Program::compile("missing_var == null").expect("compile CEL");
55
+ let err = program
56
+ .execute(&context)
57
+ .expect_err("execute CEL should fail");
58
+ assert!(err.to_string().contains("Undeclared reference"));
59
+ }
60
+
61
+ #[derive(Clone)]
62
+ struct FixedFunctions;
63
+
64
+ impl CelFunctionProvider for FixedFunctions {
65
+ fn call_uuid_v7(&self) -> String {
66
+ "uuid-fixed".to_string()
67
+ }
68
+
69
+ fn call_timestamp(&self) -> String {
70
+ "1970-01-01T00:00:00.000Z".to_string()
71
+ }
72
+ }
73
+
74
+ fn fixed_functions() -> FixedFunctions {
75
+ FixedFunctions
76
+ }
77
+
78
+ #[test]
79
+ fn uses_supplied_function_provider() {
80
+ let context = build_context_with_functions(&JsonMap::new(), fixed_functions())
81
+ .expect("build context");
82
+ let program = Program::compile("lix_uuid_v7()").expect("compile CEL");
83
+ let value = program.execute(&context).expect("execute CEL");
84
+ assert_eq!(value.json().expect("to json").as_str(), Some("uuid-fixed"));
85
+ }
86
+ }
@@ -0,0 +1,19 @@
1
+ use crate::LixError;
2
+
3
+ pub(crate) fn cel_parse_error(expression: &str, error: impl std::fmt::Display) -> LixError {
4
+ LixError {
5
+ code: "LIX_ERROR_UNKNOWN".to_string(),
6
+ message: format!("failed to parse CEL expression '{expression}': {error}"),
7
+ hint: None,
8
+ details: None,
9
+ }
10
+ }
11
+
12
+ pub(crate) fn cel_runtime_error(expression: &str, error: impl std::fmt::Display) -> LixError {
13
+ LixError {
14
+ code: "LIX_ERROR_UNKNOWN".to_string(),
15
+ message: format!("failed to evaluate CEL expression '{expression}': {error}"),
16
+ hint: None,
17
+ details: None,
18
+ }
19
+ }
@@ -0,0 +1,8 @@
1
+ mod context;
2
+ mod error;
3
+ mod provider;
4
+ mod runtime;
5
+ mod value;
6
+
7
+ pub(crate) use provider::CelFunctionProvider;
8
+ pub(crate) use runtime::shared_runtime;
@@ -0,0 +1,9 @@
1
+ /// Function source available to CEL expressions.
2
+ ///
3
+ /// CEL is shared infrastructure for schema expressions. It should not depend
4
+ /// on engine1 or engine runtime traits directly; callers adapt their own
5
+ /// execution-scoped function provider to this small boundary.
6
+ pub(crate) trait CelFunctionProvider: Clone + Send + Sync + 'static {
7
+ fn call_uuid_v7(&self) -> String;
8
+ fn call_timestamp(&self) -> String;
9
+ }
@@ -0,0 +1,167 @@
1
+ use std::collections::HashMap;
2
+ use std::sync::{Arc, OnceLock, RwLock};
3
+
4
+ use cel::Program;
5
+ use serde_json::{Map as JsonMap, Value as JsonValue};
6
+
7
+ use crate::LixError;
8
+
9
+ use super::context::build_context_with_functions;
10
+ use super::error::{cel_parse_error, cel_runtime_error};
11
+ use super::provider::CelFunctionProvider;
12
+ use super::value::cel_to_json;
13
+
14
+ #[derive(Debug)]
15
+ struct CompiledProgram {
16
+ program: Program,
17
+ }
18
+
19
+ #[derive(Default)]
20
+ pub struct CelEvaluator {
21
+ programs: RwLock<HashMap<String, Arc<CompiledProgram>>>,
22
+ }
23
+
24
+ impl CelEvaluator {
25
+ pub fn new() -> Self {
26
+ Self::default()
27
+ }
28
+
29
+ pub fn evaluate_with_functions<P>(
30
+ &self,
31
+ expression: &str,
32
+ variables: &JsonMap<String, JsonValue>,
33
+ functions: P,
34
+ ) -> Result<JsonValue, LixError>
35
+ where
36
+ P: CelFunctionProvider,
37
+ {
38
+ let compiled = self.compile(expression)?;
39
+ let context = build_context_with_functions(variables, functions)?;
40
+ let value = compiled
41
+ .program
42
+ .execute(&context)
43
+ .map_err(|error| cel_runtime_error(expression, error))?;
44
+ cel_to_json(&value)
45
+ }
46
+
47
+ fn compile(&self, expression: &str) -> Result<Arc<CompiledProgram>, LixError> {
48
+ if let Some(existing) = self.programs.read().unwrap().get(expression).cloned() {
49
+ return Ok(existing);
50
+ }
51
+
52
+ let program =
53
+ Program::compile(expression).map_err(|error| cel_parse_error(expression, error))?;
54
+ let compiled = Arc::new(CompiledProgram { program });
55
+
56
+ self.programs
57
+ .write()
58
+ .unwrap()
59
+ .insert(expression.to_string(), compiled.clone());
60
+
61
+ Ok(compiled)
62
+ }
63
+ }
64
+
65
+ pub(crate) fn shared_runtime() -> &'static CelEvaluator {
66
+ static SHARED_RUNTIME: OnceLock<CelEvaluator> = OnceLock::new();
67
+ SHARED_RUNTIME.get_or_init(CelEvaluator::new)
68
+ }
69
+
70
+ #[cfg(test)]
71
+ mod tests {
72
+ use super::CelEvaluator;
73
+ use crate::cel::CelFunctionProvider;
74
+ use serde_json::{json, Map as JsonMap, Value as JsonValue};
75
+
76
+ #[derive(Clone)]
77
+ struct FixedFunctions;
78
+
79
+ impl CelFunctionProvider for FixedFunctions {
80
+ fn call_uuid_v7(&self) -> String {
81
+ "uuid-fixed".to_string()
82
+ }
83
+
84
+ fn call_timestamp(&self) -> String {
85
+ "1970-01-01T00:00:00.000Z".to_string()
86
+ }
87
+ }
88
+
89
+ fn fixed_functions() -> FixedFunctions {
90
+ FixedFunctions
91
+ }
92
+
93
+ #[test]
94
+ fn evaluates_basic_expressions() {
95
+ let evaluator = CelEvaluator::new();
96
+ let value = evaluator
97
+ .evaluate_with_functions("'open'", &JsonMap::new(), fixed_functions())
98
+ .expect("evaluate CEL");
99
+ assert_eq!(value, JsonValue::String("open".to_string()));
100
+ }
101
+
102
+ #[test]
103
+ fn evaluates_with_variables() {
104
+ let evaluator = CelEvaluator::new();
105
+ let mut context = JsonMap::new();
106
+ context.insert("name".to_string(), JsonValue::String("sample".to_string()));
107
+ let value = evaluator
108
+ .evaluate_with_functions("name + '-slug'", &context, fixed_functions())
109
+ .expect("evaluate CEL");
110
+ assert_eq!(value, JsonValue::String("sample-slug".to_string()));
111
+ }
112
+
113
+ #[test]
114
+ fn reports_parse_errors() {
115
+ let evaluator = CelEvaluator::new();
116
+ let err = evaluator
117
+ .evaluate_with_functions("lix_uuid_v7(", &JsonMap::new(), fixed_functions())
118
+ .expect_err("expected parse error");
119
+ assert!(err.to_string().contains("failed to parse CEL expression"));
120
+ }
121
+
122
+ #[test]
123
+ fn reports_runtime_errors() {
124
+ let evaluator = CelEvaluator::new();
125
+ let err = evaluator
126
+ .evaluate_with_functions("1 / 0", &JsonMap::new(), fixed_functions())
127
+ .expect_err("expected runtime error");
128
+ assert!(err
129
+ .to_string()
130
+ .contains("failed to evaluate CEL expression"));
131
+ }
132
+
133
+ #[test]
134
+ fn supports_function_calls() {
135
+ let evaluator = CelEvaluator::new();
136
+ let value = evaluator
137
+ .evaluate_with_functions("lix_timestamp()", &JsonMap::new(), fixed_functions())
138
+ .expect("evaluate CEL");
139
+ assert_eq!(value.as_str(), Some("1970-01-01T00:00:00.000Z"));
140
+ }
141
+
142
+ #[test]
143
+ fn caches_compiled_programs() {
144
+ let evaluator = CelEvaluator::new();
145
+ let mut context = JsonMap::new();
146
+ context.insert("name".to_string(), json!("x"));
147
+
148
+ let _ = evaluator
149
+ .evaluate_with_functions("name + '-slug'", &context, fixed_functions())
150
+ .expect("first evaluation");
151
+ let _ = evaluator
152
+ .evaluate_with_functions("name + '-slug'", &context, fixed_functions())
153
+ .expect("second evaluation");
154
+
155
+ let size = evaluator.programs.read().unwrap().len();
156
+ assert_eq!(size, 1);
157
+ }
158
+
159
+ #[test]
160
+ fn errors_on_unknown_variable() {
161
+ let evaluator = CelEvaluator::new();
162
+ let err = evaluator
163
+ .evaluate_with_functions("missing_var + '-slug'", &JsonMap::new(), fixed_functions())
164
+ .expect_err("expected unknown variable error");
165
+ assert!(err.to_string().contains("Undeclared reference"));
166
+ }
167
+ }
@@ -0,0 +1,50 @@
1
+ use cel::Value as CelValue;
2
+ use serde_json::Value as JsonValue;
3
+
4
+ use crate::LixError;
5
+
6
+ pub fn json_to_cel(value: &JsonValue) -> Result<CelValue, LixError> {
7
+ cel::to_value(value).map_err(|err| LixError {
8
+ code: "LIX_ERROR_UNKNOWN".to_string(),
9
+ message: format!("failed to convert JSON value to CEL value: {err}"),
10
+ hint: None,
11
+ details: None,
12
+ })
13
+ }
14
+
15
+ pub fn cel_to_json(value: &CelValue) -> Result<JsonValue, LixError> {
16
+ value.json().map_err(|err| LixError {
17
+ code: "LIX_ERROR_UNKNOWN".to_string(),
18
+ message: format!("failed to convert CEL value to JSON value: {err}"),
19
+ hint: None,
20
+ details: None,
21
+ })
22
+ }
23
+
24
+ #[cfg(test)]
25
+ mod tests {
26
+ use super::{cel_to_json, json_to_cel};
27
+ use serde_json::json;
28
+
29
+ #[test]
30
+ fn converts_json_scalars() {
31
+ let value = json!("hello");
32
+ let cel = json_to_cel(&value).expect("convert to CEL");
33
+ let roundtrip = cel_to_json(&cel).expect("convert to JSON");
34
+ assert_eq!(roundtrip, value);
35
+ }
36
+
37
+ #[test]
38
+ fn converts_json_objects_and_arrays() {
39
+ let value = json!({
40
+ "name": "Ada",
41
+ "flags": [true, false],
42
+ "meta": {
43
+ "count": 1
44
+ }
45
+ });
46
+ let cel = json_to_cel(&value).expect("convert to CEL");
47
+ let roundtrip = cel_to_json(&cel).expect("convert to JSON");
48
+ assert_eq!(roundtrip, value);
49
+ }
50
+ }