@@ -21,79 +21,85 @@ import { BASIC_FUNCTIONS } from "./basic_functions.js";
2121import { DataModel } from "../../state/data-model.js" ;
2222import { DataContext } from "../../rendering/data-context.js" ;
2323
24+ function invoke ( name : string , args : Record < string , any > , context : DataContext ) {
25+ const fn = BASIC_FUNCTIONS . find ( f => f . name === name ) ;
26+ if ( ! fn ) throw new Error ( `Function ${ name } not found` ) ;
27+ return fn . execute ( fn . schema . parse ( args ) , context ) ;
28+ }
29+
2430describe ( "BASIC_FUNCTIONS" , ( ) => {
2531 const dataModel = new DataModel ( { a : 10 , b : 20 } ) ;
26- const context = new DataContext ( dataModel , "/" ) ;
32+ const context = new DataContext ( dataModel , "/" , ( ) => null ) ;
2733
2834 describe ( "Arithmetic" , ( ) => {
2935 it ( "add" , ( ) => {
30- assert . strictEqual ( BASIC_FUNCTIONS . add ( { a : 1 , b : 2 } , context ) , 3 ) ;
31- assert . strictEqual ( BASIC_FUNCTIONS . add ( { a : "1" , b : "2" } , context ) , 3 ) ;
36+ assert . strictEqual ( invoke ( "add" , { a : 1 , b : 2 } , context ) , 3 ) ;
37+ assert . strictEqual ( invoke ( "add" , { a : "1" , b : "2" } , context ) , 3 ) ;
3238 } ) ;
3339 it ( "subtract" , ( ) => {
34- assert . strictEqual ( BASIC_FUNCTIONS . subtract ( { a : 5 , b : 3 } , context ) , 2 ) ;
40+ assert . strictEqual ( invoke ( "subtract" , { a : 5 , b : 3 } , context ) , 2 ) ;
3541 } ) ;
3642 it ( "multiply" , ( ) => {
37- assert . strictEqual ( BASIC_FUNCTIONS . multiply ( { a : 4 , b : 2 } , context ) , 8 ) ;
43+ assert . strictEqual ( invoke ( "multiply" , { a : 4 , b : 2 } , context ) , 8 ) ;
3844 } ) ;
3945 it ( "divide" , ( ) => {
40- assert . strictEqual ( BASIC_FUNCTIONS . divide ( { a : 10 , b : 2 } , context ) , 5 ) ;
46+ assert . strictEqual ( invoke ( "divide" , { a : 10 , b : 2 } , context ) , 5 ) ;
4147 assert . strictEqual (
42- BASIC_FUNCTIONS . divide ( { a : 10 , b : 0 } , context ) ,
48+ invoke ( "divide" , { a : 10 , b : 0 } , context ) ,
4349 Infinity ,
4450 ) ;
4551 assert . ok (
46- Number . isNaN ( BASIC_FUNCTIONS . divide ( { a : 10 , b : undefined } , context ) ) ,
52+ Number . isNaN ( invoke ( "divide" , { a : 10 , b : undefined } , context ) ) ,
4753 ) ;
4854 assert . ok (
49- Number . isNaN ( BASIC_FUNCTIONS . divide ( { a : undefined , b : 10 } , context ) ) ,
55+ Number . isNaN ( invoke ( "divide" , { a : undefined , b : 10 } , context ) ) ,
5056 ) ;
5157 assert . ok (
5258 Number . isNaN (
53- BASIC_FUNCTIONS . divide ( { a : undefined , b : undefined } , context ) ,
59+ invoke ( "divide" , { a : undefined , b : undefined } , context ) ,
5460 ) ,
5561 ) ;
5662 assert . ok (
57- Number . isNaN ( BASIC_FUNCTIONS . divide ( { a : 10 , b : null } , context ) ) ,
63+ Number . isNaN ( invoke ( "divide" , { a : 10 , b : null } , context ) ) ,
5864 ) ;
5965 assert . ok (
60- Number . isNaN ( BASIC_FUNCTIONS . divide ( { a : 10 , b : "invalid" } , context ) ) ,
66+ Number . isNaN ( invoke ( "divide" , { a : 10 , b : "invalid" } , context ) ) ,
6167 ) ;
62- assert . strictEqual ( BASIC_FUNCTIONS . divide ( { a : 10 , b : "2" } , context ) , 5 ) ;
68+ assert . strictEqual ( invoke ( "divide" , { a : 10 , b : "2" } , context ) , 5 ) ;
6369 assert . strictEqual (
64- BASIC_FUNCTIONS . divide ( { a : "10" , b : "2" } , context ) ,
70+ invoke ( "divide" , { a : "10" , b : "2" } , context ) ,
6571 5 ,
6672 ) ;
6773 } ) ;
6874 } ) ;
6975
7076 describe ( "Comparison" , ( ) => {
7177 it ( "equals" , ( ) => {
72- assert . strictEqual ( BASIC_FUNCTIONS . equals ( { a : 1 , b : 1 } , context ) , true ) ;
78+ assert . strictEqual ( invoke ( "equals" , { a : 1 , b : 1 } , context ) , true ) ;
7379 assert . strictEqual (
74- BASIC_FUNCTIONS . equals ( { a : 1 , b : 2 } , context ) ,
80+ invoke ( "equals" , { a : 1 , b : 2 } , context ) ,
7581 false ,
7682 ) ;
7783 } ) ;
7884 it ( "not_equals" , ( ) => {
7985 assert . strictEqual (
80- BASIC_FUNCTIONS . not_equals ( { a : 1 , b : 2 } , context ) ,
86+ invoke ( "not_equals" , { a : 1 , b : 2 } , context ) ,
8187 true ,
8288 ) ;
8389 } ) ;
8490 it ( "greater_than" , ( ) => {
8591 assert . strictEqual (
86- BASIC_FUNCTIONS . greater_than ( { a : 5 , b : 3 } , context ) ,
92+ invoke ( "greater_than" , { a : 5 , b : 3 } , context ) ,
8793 true ,
8894 ) ;
8995 assert . strictEqual (
90- BASIC_FUNCTIONS . greater_than ( { a : 3 , b : 5 } , context ) ,
96+ invoke ( "greater_than" , { a : 3 , b : 5 } , context ) ,
9197 false ,
9298 ) ;
9399 } ) ;
94100 it ( "less_than" , ( ) => {
95101 assert . strictEqual (
96- BASIC_FUNCTIONS . less_than ( { a : 3 , b : 5 } , context ) ,
102+ invoke ( "less_than" , { a : 3 , b : 5 } , context ) ,
97103 true ,
98104 ) ;
99105 } ) ;
@@ -103,49 +109,49 @@ describe("BASIC_FUNCTIONS", () => {
103109 it ( "and" , ( ) => {
104110 // Checks args['values'] array OR args['a'] && args['b'].
105111 assert . strictEqual (
106- BASIC_FUNCTIONS . and ( { values : [ true , true ] } , context ) ,
112+ invoke ( "and" , { values : [ true , true ] } , context ) ,
107113 true ,
108114 ) ;
109115 assert . strictEqual (
110- BASIC_FUNCTIONS . and ( { values : [ true , false ] } , context ) ,
116+ invoke ( "and" , { values : [ true , false ] } , context ) ,
111117 false ,
112118 ) ;
113119 assert . strictEqual (
114- BASIC_FUNCTIONS . and ( { a : true , b : true } , context ) ,
120+ invoke ( "and" , { a : true , b : true } , context ) ,
115121 true ,
116122 ) ;
117123 } ) ;
118124 it ( "or" , ( ) => {
119125 assert . strictEqual (
120- BASIC_FUNCTIONS . or ( { values : [ false , true ] } , context ) ,
126+ invoke ( "or" , { values : [ false , true ] } , context ) ,
121127 true ,
122128 ) ;
123129 assert . strictEqual (
124- BASIC_FUNCTIONS . or ( { values : [ false , false ] } , context ) ,
130+ invoke ( "or" , { values : [ false , false ] } , context ) ,
125131 false ,
126132 ) ;
127133 assert . strictEqual (
128- BASIC_FUNCTIONS . or ( { a : false , b : true } , context ) ,
134+ invoke ( "or" , { a : false , b : true } , context ) ,
129135 true ,
130136 ) ;
131137 } ) ;
132138 it ( "not" , ( ) => {
133- assert . strictEqual ( BASIC_FUNCTIONS . not ( { value : false } , context ) , true ) ;
134- assert . strictEqual ( BASIC_FUNCTIONS . not ( { value : true } , context ) , false ) ;
139+ assert . strictEqual ( invoke ( "not" , { value : false } , context ) , true ) ;
140+ assert . strictEqual ( invoke ( "not" , { value : true } , context ) , false ) ;
135141 } ) ;
136142 } ) ;
137143
138144 describe ( "String" , ( ) => {
139145 it ( "contains" , ( ) => {
140146 assert . strictEqual (
141- BASIC_FUNCTIONS . contains (
147+ invoke ( "contains" ,
142148 { string : "hello world" , substring : "world" } ,
143149 context ,
144150 ) ,
145151 true ,
146152 ) ;
147153 assert . strictEqual (
148- BASIC_FUNCTIONS . contains (
154+ invoke ( "contains" ,
149155 { string : "hello world" , substring : "foo" } ,
150156 context ,
151157 ) ,
@@ -154,13 +160,13 @@ describe("BASIC_FUNCTIONS", () => {
154160 } ) ;
155161 it ( "starts_with" , ( ) => {
156162 assert . strictEqual (
157- BASIC_FUNCTIONS . starts_with ( { string : "hello" , prefix : "he" } , context ) ,
163+ invoke ( "starts_with" , { string : "hello" , prefix : "he" } , context ) ,
158164 true ,
159165 ) ;
160166 } ) ;
161167 it ( "ends_with" , ( ) => {
162168 assert . strictEqual (
163- BASIC_FUNCTIONS . ends_with ( { string : "hello" , suffix : "lo" } , context ) ,
169+ invoke ( "ends_with" , { string : "hello" , suffix : "lo" } , context ) ,
164170 true ,
165171 ) ;
166172 } ) ;
@@ -169,74 +175,74 @@ describe("BASIC_FUNCTIONS", () => {
169175 describe ( "Validation" , ( ) => {
170176 it ( "required" , ( ) => {
171177 assert . strictEqual (
172- BASIC_FUNCTIONS . required ( { value : "a" } , context ) ,
178+ invoke ( "required" , { value : "a" } , context ) ,
173179 true ,
174180 ) ;
175181 assert . strictEqual (
176- BASIC_FUNCTIONS . required ( { value : "" } , context ) ,
182+ invoke ( "required" , { value : "" } , context ) ,
177183 false ,
178184 ) ;
179185 assert . strictEqual (
180- BASIC_FUNCTIONS . required ( { value : null } , context ) ,
186+ invoke ( "required" , { value : null } , context ) ,
181187 false ,
182188 ) ;
183189 } ) ;
184190
185191 it ( "length" , ( ) => {
186192 assert . strictEqual (
187- BASIC_FUNCTIONS . length ( { value : "abc" , min : 2 } , context ) ,
193+ invoke ( "length" , { value : "abc" , min : 2 } , context ) ,
188194 true ,
189195 ) ;
190196 assert . strictEqual (
191- BASIC_FUNCTIONS . length ( { value : "abc" , max : 2 } , context ) ,
197+ invoke ( "length" , { value : "abc" , max : 2 } , context ) ,
192198 false ,
193199 ) ;
194200 } ) ;
195201
196202 it ( "numeric" , ( ) => {
197203 assert . strictEqual (
198- BASIC_FUNCTIONS . numeric ( { value : 10 , min : 5 , max : 15 } , context ) ,
204+ invoke ( "numeric" , { value : 10 , min : 5 , max : 15 } , context ) ,
199205 true ,
200206 ) ;
201207 assert . strictEqual (
202- BASIC_FUNCTIONS . numeric ( { value : 3 , min : 5 } , context ) ,
208+ invoke ( "numeric" , { value : 3 , min : 5 } , context ) ,
203209 false ,
204210 ) ;
205211 } ) ;
206212
207213 it ( "email" , ( ) => {
208214 assert . strictEqual (
209- BASIC_FUNCTIONS . email ( { value : "test@example.com" } , context ) ,
215+ invoke ( "email" , { value : "test@example.com" } , context ) ,
210216 true ,
211217 ) ;
212218 assert . strictEqual (
213- BASIC_FUNCTIONS . email ( { value : "invalid" } , context ) ,
219+ invoke ( "email" , { value : "invalid" } , context ) ,
214220 false ,
215221 ) ;
216222 } ) ;
217223
218224 it ( "regex" , ( ) => {
219225 assert . strictEqual (
220- BASIC_FUNCTIONS . regex ( { value : "abc" , pattern : "^[a-z]+$" } , context ) ,
226+ invoke ( "regex" , { value : "abc" , pattern : "^[a-z]+$" } , context ) ,
221227 true ,
222228 ) ;
223229 assert . strictEqual (
224- BASIC_FUNCTIONS . regex ( { value : "123" , pattern : "^[a-z]+$" } , context ) ,
230+ invoke ( "regex" , { value : "123" , pattern : "^[a-z]+$" } , context ) ,
225231 false ,
226232 ) ;
227233 } ) ;
228234
229235 it ( "regex handles invalid pattern" , ( ) => {
230236 assert . strictEqual (
231- BASIC_FUNCTIONS . regex ( { value : "abc" , pattern : "[" } , context ) ,
237+ invoke ( "regex" , { value : "abc" , pattern : "[" } , context ) ,
232238 false , // fallback when regex throws
233239 ) ;
234240 } ) ;
235241 } ) ;
236242
237243 describe ( "Formatting" , ( ) => {
238244 it ( "formatString (static literal)" , ( _ , done ) => {
239- const result = BASIC_FUNCTIONS . formatString (
245+ const result = invoke ( "formatString" ,
240246 { value : "hello world" } ,
241247 context ,
242248 ) as import ( "@preact/signals-core" ) . Signal < string > ;
@@ -254,7 +260,7 @@ describe("BASIC_FUNCTIONS", () => {
254260
255261 it ( "formatString (with data binding)" , ( _ , done ) => {
256262 // Assuming dataModel has { "a": 10 } from setup
257- const result = BASIC_FUNCTIONS . formatString (
263+ const result = invoke ( "formatString" ,
258264 { value : "Value: ${a}" } ,
259265 context ,
260266 ) as import ( "@preact/signals-core" ) . Signal < string > ;
@@ -293,7 +299,7 @@ describe("BASIC_FUNCTIONS", () => {
293299 return null ;
294300 } ) ;
295301
296- const result = BASIC_FUNCTIONS . formatString (
302+ const result = invoke ( "formatString" ,
297303 { value : "Result: ${add(a: 5, b: 7)}" } ,
298304 ctxWithInvoker ,
299305 ) as import ( "@preact/signals-core" ) . Signal < string > ;
@@ -311,7 +317,7 @@ describe("BASIC_FUNCTIONS", () => {
311317
312318 it ( "formatNumber" , ( ) => {
313319 // Test basic output as Intl behavior varies by environment.
314- const result = BASIC_FUNCTIONS . formatNumber (
320+ const result = invoke ( "formatNumber" ,
315321 { value : 1234.56 , decimals : 1 } ,
316322 context ,
317323 ) ;
@@ -324,7 +330,7 @@ describe("BASIC_FUNCTIONS", () => {
324330 } ) ;
325331
326332 it ( "formatCurrency" , ( ) => {
327- const result = BASIC_FUNCTIONS . formatCurrency (
333+ const result = invoke ( "formatCurrency" ,
328334 { value : 1234.56 , currency : "USD" } ,
329335 context ,
330336 ) ;
@@ -334,30 +340,30 @@ describe("BASIC_FUNCTIONS", () => {
334340 } ) ;
335341
336342 it ( "formatDate" , ( ) => {
337- const result = BASIC_FUNCTIONS . formatDate (
343+ const result = invoke ( "formatDate" ,
338344 { value : "2025-01-01T00:00:00Z" } ,
339345 context ,
340346 ) ;
341347 assert . ok ( typeof result === "string" ) ;
342348 assert . ok ( result . length > 0 ) ;
343349
344- const resultISO = BASIC_FUNCTIONS . formatDate (
350+ const resultISO = invoke ( "formatDate" ,
345351 { value : "2025-01-01T00:00:00Z" , format : "ISO" } ,
346352 context ,
347353 ) ;
348354 assert . strictEqual ( resultISO , "2025-01-01T00:00:00.000Z" ) ;
349355 } ) ;
350356
351357 it ( "formatDate handles invalid dates" , ( ) => {
352- const result = BASIC_FUNCTIONS . formatDate (
358+ const result = invoke ( "formatDate" ,
353359 { value : "invalid-date" } ,
354360 context ,
355361 ) ;
356362 assert . strictEqual ( result , "" ) ;
357363 } ) ;
358364
359365 it ( "formatDate uses options properly" , ( ) => {
360- const result = BASIC_FUNCTIONS . formatDate (
366+ const result = invoke ( "formatDate" ,
361367 {
362368 value : "2025-01-01T00:00:00Z" ,
363369 options : { year : "numeric" , timeZone : "UTC" } ,
@@ -369,7 +375,7 @@ describe("BASIC_FUNCTIONS", () => {
369375 } ) ;
370376
371377 it ( "formatDate fallback on formatting error" , ( ) => {
372- const result = BASIC_FUNCTIONS . formatDate (
378+ const result = invoke ( "formatDate" ,
373379 { value : "2025-01-01T00:00:00Z" , locale : "invalid-locale-!!!11123" } ,
374380 context ,
375381 ) ;
@@ -378,7 +384,7 @@ describe("BASIC_FUNCTIONS", () => {
378384 } ) ;
379385
380386 it ( "formatCurrency fallback on formatting error" , ( ) => {
381- const result = BASIC_FUNCTIONS . formatCurrency (
387+ const result = invoke ( "formatCurrency" ,
382388 { value : 1234.56 , currency : "INVALID-CURRENCY" , decimals : 2 } ,
383389 context ,
384390 ) ;
@@ -388,14 +394,14 @@ describe("BASIC_FUNCTIONS", () => {
388394
389395 it ( "pluralize" , ( ) => {
390396 assert . strictEqual (
391- BASIC_FUNCTIONS . pluralize (
397+ invoke ( "pluralize" ,
392398 { value : 1 , one : "apple" , other : "apples" } ,
393399 context ,
394400 ) ,
395401 "apple" ,
396402 ) ;
397403 assert . strictEqual (
398- BASIC_FUNCTIONS . pluralize (
404+ invoke ( "pluralize" ,
399405 { value : 2 , one : "apple" , other : "apples" } ,
400406 context ,
401407 ) ,
@@ -416,11 +422,11 @@ describe("BASIC_FUNCTIONS", () => {
416422 } ;
417423
418424 try {
419- BASIC_FUNCTIONS . openUrl ( { url : "https://google.com" } , context ) ;
425+ invoke ( "openUrl" , { url : "https://google.com" } , context ) ;
420426 assert . strictEqual ( openedUrl , "https://google.com" ) ;
421427 } finally {
422428 ( global as any ) . window = originalWindow ;
423429 }
424430 } ) ;
425431 } ) ;
426- } ) ;
432+ } ) ;
0 commit comments