@nordcraft/core 1.0.95 → 1.0.96

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 (71) hide show
  1. package/dist/api/api.js +30 -10
  2. package/dist/api/api.js.map +1 -1
  3. package/dist/component/ToddleComponent.js +3 -3
  4. package/dist/component/ToddleComponent.js.map +1 -1
  5. package/dist/component/component.types.d.ts +3 -3
  6. package/dist/component/component.types.js.map +1 -1
  7. package/dist/formula/andFormula.d.ts +3 -0
  8. package/dist/formula/andFormula.js +25 -0
  9. package/dist/formula/andFormula.js.map +1 -0
  10. package/dist/formula/applyFormula.d.ts +2 -0
  11. package/dist/formula/applyFormula.js +49 -0
  12. package/dist/formula/applyFormula.js.map +1 -0
  13. package/dist/formula/arrayFormula.d.ts +2 -0
  14. package/dist/formula/arrayFormula.js +5 -0
  15. package/dist/formula/arrayFormula.js.map +1 -0
  16. package/dist/formula/formula.d.ts +8 -6
  17. package/dist/formula/formula.js +45 -172
  18. package/dist/formula/formula.js.map +1 -1
  19. package/dist/formula/formulaTypes.d.ts +1 -0
  20. package/dist/formula/formulaUtils.d.ts +1 -1
  21. package/dist/formula/formulaUtils.js.map +1 -1
  22. package/dist/formula/functionFormula.d.ts +2 -0
  23. package/dist/formula/functionFormula.js +88 -0
  24. package/dist/formula/functionFormula.js.map +1 -0
  25. package/dist/formula/objectFormula.d.ts +2 -0
  26. package/dist/formula/objectFormula.js +8 -0
  27. package/dist/formula/objectFormula.js.map +1 -0
  28. package/dist/formula/orFormula.d.ts +3 -0
  29. package/dist/formula/orFormula.js +27 -0
  30. package/dist/formula/orFormula.js.map +1 -0
  31. package/dist/formula/pathFormula.d.ts +2 -0
  32. package/dist/formula/pathFormula.js +13 -0
  33. package/dist/formula/pathFormula.js.map +1 -0
  34. package/dist/formula/recordFormula.d.ts +2 -0
  35. package/dist/formula/recordFormula.js +8 -0
  36. package/dist/formula/recordFormula.js.map +1 -0
  37. package/dist/formula/switchFormula.d.ts +3 -0
  38. package/dist/formula/switchFormula.js +40 -0
  39. package/dist/formula/switchFormula.js.map +1 -0
  40. package/dist/types.d.ts +1 -1
  41. package/dist/utils/measure.js +5 -0
  42. package/dist/utils/measure.js.map +1 -1
  43. package/package.json +1 -1
  44. package/src/api/api.test.ts +22 -22
  45. package/src/api/api.ts +36 -10
  46. package/src/component/ToddleComponent.ts +9 -7
  47. package/src/component/component.types.ts +5 -3
  48. package/src/formula/andFormula.test.ts +112 -0
  49. package/src/formula/andFormula.ts +33 -0
  50. package/src/formula/applyFormula.test.ts +151 -0
  51. package/src/formula/applyFormula.ts +72 -0
  52. package/src/formula/arrayFormula.test.ts +52 -0
  53. package/src/formula/arrayFormula.ts +14 -0
  54. package/src/formula/formula.ts +67 -206
  55. package/src/formula/formulaTypes.ts +5 -0
  56. package/src/formula/formulaUtils.ts +1 -1
  57. package/src/formula/functionFormula.test.ts +89 -0
  58. package/src/formula/functionFormula.ts +118 -0
  59. package/src/formula/objectFormula.test.ts +56 -0
  60. package/src/formula/objectFormula.ts +17 -0
  61. package/src/formula/orFormula.test.ts +113 -0
  62. package/src/formula/orFormula.ts +33 -0
  63. package/src/formula/pathFormula.test.ts +35 -0
  64. package/src/formula/pathFormula.ts +17 -0
  65. package/src/formula/recordFormula.test.ts +56 -0
  66. package/src/formula/recordFormula.ts +17 -0
  67. package/src/formula/switchFormula.test.ts +122 -0
  68. package/src/formula/switchFormula.ts +54 -0
  69. package/src/formula/testUtils.test.ts +68 -0
  70. package/src/types.ts +1 -1
  71. package/src/utils/measure.ts +6 -0
@@ -0,0 +1,72 @@
1
+ /* eslint-disable no-console */
2
+ import { measure } from '../utils/measure'
3
+ import {
4
+ applyFormula,
5
+ type ApplyOperation,
6
+ type FormulaContext,
7
+ } from './formula'
8
+
9
+ export const applyApplyFormula = (
10
+ formula: ApplyOperation,
11
+ ctx: FormulaContext,
12
+ ) => {
13
+ const componentFormula = ctx.component?.formulas?.[formula.name]
14
+ if (!componentFormula) {
15
+ if (ctx.env?.logErrors) {
16
+ console.log(
17
+ 'Component does not have a formula with the name ',
18
+ formula.name,
19
+ )
20
+ }
21
+ return null
22
+ }
23
+ const stopMeasure = measure(`Formula: ${componentFormula.name}`, {
24
+ formula,
25
+ component: ctx.component?.name,
26
+ })
27
+ const Input = Object.fromEntries(
28
+ (formula.arguments ?? []).map((arg, i) =>
29
+ arg.isFunction
30
+ ? [
31
+ arg.name,
32
+ (Args: any) =>
33
+ applyFormula(
34
+ arg.formula,
35
+ {
36
+ ...ctx,
37
+ data: {
38
+ ...ctx.data,
39
+ Args: ctx.data.Args
40
+ ? { ...Args, '@toddle.parent': ctx.data.Args }
41
+ : Args,
42
+ },
43
+ },
44
+ ['arguments', i],
45
+ ),
46
+ ]
47
+ : [arg.name, applyFormula(arg.formula, ctx, ['arguments', i])],
48
+ ),
49
+ )
50
+ const data = {
51
+ ...ctx.data,
52
+ Args: ctx.data.Args ? { ...Input, '@toddle.parent': ctx.data.Args } : Input,
53
+ }
54
+ const cache = ctx.formulaCache?.[formula.name]?.get(data)
55
+
56
+ if (cache?.hit) {
57
+ stopMeasure({ cache: 'hit' })
58
+ return cache.data
59
+ } else {
60
+ const result = applyFormula(
61
+ componentFormula.formula,
62
+ {
63
+ ...ctx,
64
+ data,
65
+ },
66
+ ['formula'],
67
+ )
68
+ ctx.formulaCache?.[formula.name]?.set(data, result)
69
+ stopMeasure({ cache: 'miss' })
70
+ return result
71
+ }
72
+ }
@@ -0,0 +1,52 @@
1
+ import { describe, expect, it } from 'bun:test'
2
+ import { applyArrayFormula } from './arrayFormula'
3
+ import type { ArrayOperation } from './formula'
4
+ import { valueFormula } from './formulaUtils'
5
+ import {
6
+ createTestFormulaContext,
7
+ createTestFormulaContextForAllPaths,
8
+ } from './testUtils.test'
9
+
10
+ describe('applyArrayFormula', () => {
11
+ it('returns an array with evaluated values', () => {
12
+ const formula: ArrayOperation = {
13
+ type: 'array',
14
+ arguments: [
15
+ { formula: valueFormula(1) },
16
+ { formula: valueFormula('hello') },
17
+ { formula: valueFormula(true) },
18
+ ],
19
+ }
20
+ const ctx = createTestFormulaContext()
21
+ expect(applyArrayFormula(formula, ctx)).toEqual([1, 'hello', true])
22
+ })
23
+
24
+ it('returns an empty array if arguments is empty', () => {
25
+ const formula: ArrayOperation = {
26
+ type: 'array',
27
+ arguments: [],
28
+ }
29
+ const ctx = createTestFormulaContext()
30
+ expect(applyArrayFormula(formula, ctx)).toEqual([])
31
+ })
32
+
33
+ it('evaluates all formulas and reports results in "report" mode', () => {
34
+ const formula: ArrayOperation = {
35
+ type: 'array',
36
+ arguments: [
37
+ { formula: valueFormula('foo') },
38
+ { formula: valueFormula('bar') },
39
+ ],
40
+ }
41
+ const results: Record<string, any> = {}
42
+ const ctx = createTestFormulaContextForAllPaths(
43
+ {},
44
+ (path, result) => (results[path.join('/')] = result),
45
+ )
46
+ expect(applyArrayFormula(formula, ctx)).toEqual(['foo', 'bar'])
47
+ expect(results).toMatchObject({
48
+ 'arguments/0/formula': 'foo',
49
+ 'arguments/1/formula': 'bar',
50
+ })
51
+ })
52
+ })
@@ -0,0 +1,14 @@
1
+ import {
2
+ applyFormula,
3
+ type ArrayOperation,
4
+ type FormulaContext,
5
+ } from './formula'
6
+
7
+ export const applyArrayFormula = (
8
+ formula: ArrayOperation,
9
+ ctx: FormulaContext,
10
+ ) => {
11
+ return (formula.arguments ?? []).map((entry, i) =>
12
+ applyFormula(entry.formula, ctx, ['arguments', i, 'formula']),
13
+ )
14
+ }
@@ -2,19 +2,31 @@
2
2
  import type { Component, ComponentData } from '../component/component.types'
3
3
  import type {
4
4
  CustomFormulaHandler,
5
- FormulaHandler,
6
5
  FormulaLookup,
7
6
  NordcraftMetadata,
8
7
  Nullable,
9
8
  Runtime,
10
- Toddle,
11
9
  } from '../types'
12
- import { measure } from '../utils/measure'
13
- import { isDefined, toBoolean } from '../utils/util'
14
- import { type PluginFormula, type ToddleFormula } from './formulaTypes'
10
+ import { isDefined } from '../utils/util'
11
+ import { applyAndFormula, applyEvaluateAllAndFormula } from './andFormula'
12
+ import { applyApplyFormula } from './applyFormula'
13
+ import { applyArrayFormula } from './arrayFormula'
14
+ import {
15
+ type FormulaEvaluationReporter,
16
+ type PluginFormula,
17
+ type ToddleFormula,
18
+ } from './formulaTypes'
19
+ import { applyFunctionFormula } from './functionFormula'
20
+ import { applyObjectFormula } from './objectFormula'
21
+ import { applyEvaluateAllOrFormula, applyOrFormula } from './orFormula'
22
+ import { applyPathFormula } from './pathFormula'
23
+ import { applyRecordFormula } from './recordFormula'
24
+ import {
25
+ applyEvaluateAllSwitchFormula,
26
+ applySwitchFormula,
27
+ } from './switchFormula'
15
28
 
16
29
  // Define the some objects types as union of ServerSide and ClientSide runtime types as applyFormula is used in both
17
- declare const document: Document | undefined
18
30
  type ShadowRoot = DocumentFragment
19
31
 
20
32
  interface BaseOperation extends NordcraftMetadata {
@@ -23,7 +35,7 @@ interface BaseOperation extends NordcraftMetadata {
23
35
 
24
36
  export interface PathOperation extends BaseOperation {
25
37
  type: 'path'
26
- path: string[]
38
+ path: Array<string | number>
27
39
  }
28
40
 
29
41
  export interface FunctionArgument {
@@ -79,7 +91,13 @@ export interface ValueOperation extends BaseOperation {
79
91
  value: ValueOperationValue
80
92
  }
81
93
 
82
- export type ValueOperationValue = string | number | boolean | null | object
94
+ export type ValueOperationValue =
95
+ | string
96
+ | number
97
+ | boolean
98
+ | null
99
+ | object
100
+ | undefined
83
101
 
84
102
  export interface SwitchOperation extends BaseOperation {
85
103
  type: 'switch'
@@ -104,7 +122,7 @@ export type Formula =
104
122
  | ValueOperation
105
123
  | ApplyOperation
106
124
 
107
- export type FormulaContext = {
125
+ export interface FormulaContext {
108
126
  component: Component | undefined
109
127
  formulaCache?: Nullable<
110
128
  Record<
@@ -123,6 +141,8 @@ export type FormulaContext = {
123
141
  getCustomFormula: CustomFormulaHandler
124
142
  errors: Error[]
125
143
  }
144
+ jsonPath?: Array<string | number> | undefined
145
+ reportFormulaEvaluation?: FormulaEvaluationReporter | undefined
126
146
  env: ToddleEnv | undefined
127
147
  }
128
148
 
@@ -185,222 +205,61 @@ export const isToddleFormula = <Handler>(
185
205
 
186
206
  export function applyFormula(
187
207
  formula: Formula | string | number | undefined | null | boolean,
188
- ctx: FormulaContext,
208
+ _ctx: FormulaContext,
209
+ extendedPath?: Array<string | number> | undefined,
189
210
  ): any {
211
+ const path = [...(_ctx?.jsonPath ?? []), ...(extendedPath ?? [])]
212
+ const ctx = { ..._ctx, jsonPath: path }
213
+ const report = (value: any, p: Array<string | number> = path) => {
214
+ ctx?.reportFormulaEvaluation?.(p, value)
215
+ return value
216
+ }
217
+
190
218
  if (!isFormula(formula)) {
191
- return formula
219
+ return report(formula)
192
220
  }
193
221
  try {
194
222
  switch (formula.type) {
195
- case 'value':
196
- return formula.value
223
+ case 'value': {
224
+ return report(formula.value)
225
+ }
197
226
  case 'path': {
198
- let input: any = ctx.data
199
- for (const key of formula.path) {
200
- if (input && typeof input === 'object') {
201
- input = input[key]
202
- } else {
203
- return null
204
- }
205
- }
206
-
207
- return input
227
+ return report(applyPathFormula(formula, ctx.data))
208
228
  }
209
229
  case 'switch': {
210
- for (const branch of formula.cases ?? []) {
211
- if (toBoolean(applyFormula(branch.condition, ctx))) {
212
- return applyFormula(branch.formula, ctx)
213
- }
230
+ if (ctx.reportFormulaEvaluation) {
231
+ return report(applyEvaluateAllSwitchFormula(formula, ctx))
214
232
  }
215
- return applyFormula(formula.default, ctx)
233
+ return applySwitchFormula(formula, ctx)
216
234
  }
217
235
  case 'or': {
218
- for (const entry of formula.arguments ?? []) {
219
- if (toBoolean(applyFormula(entry.formula, ctx))) {
220
- return true
221
- }
236
+ if (ctx.reportFormulaEvaluation) {
237
+ return report(applyEvaluateAllOrFormula(formula, ctx))
222
238
  }
223
- return false
239
+ return applyOrFormula(formula, ctx)
224
240
  }
225
241
  case 'and': {
226
- for (const entry of formula.arguments ?? []) {
227
- if (!toBoolean(applyFormula(entry.formula, ctx))) {
228
- return false
229
- }
242
+ if (ctx.reportFormulaEvaluation) {
243
+ return report(applyEvaluateAllAndFormula(formula, ctx))
230
244
  }
231
- return true
245
+ return applyAndFormula(formula, ctx)
246
+ }
247
+ case 'object': {
248
+ return report(applyObjectFormula(formula, ctx))
249
+ }
250
+ case 'record': {
251
+ // object used to be called record, there are still examples in the wild.
252
+ return report(applyRecordFormula(formula, ctx))
253
+ }
254
+ case 'array': {
255
+ return report(applyArrayFormula(formula, ctx))
232
256
  }
233
257
  case 'function': {
234
- const stopMeasure = measure(`Formula: ${formula.name}`, {
235
- formula,
236
- component: ctx.component?.name,
237
- })
238
- const packageName = formula.package ?? ctx.package ?? undefined
239
- const newFunc = (
240
- ctx.toddle ??
241
- ((globalThis as any).toddle as Toddle<unknown, unknown> | undefined)
242
- )?.getCustomFormula(formula.name, packageName)
243
- if (isDefined(newFunc)) {
244
- ctx.package = packageName
245
- const args = (formula.arguments ?? []).reduce<
246
- Record<string, unknown>
247
- >(
248
- (args, arg, i) => ({
249
- ...args,
250
- [arg.name ?? `${i}`]: arg.isFunction
251
- ? (Args: any) =>
252
- applyFormula(arg.formula, {
253
- ...ctx,
254
- data: {
255
- ...ctx.data,
256
- Args: ctx.data.Args
257
- ? { ...Args, '@toddle.parent': ctx.data.Args }
258
- : Args,
259
- },
260
- })
261
- : applyFormula(arg.formula, ctx),
262
- }),
263
- {},
264
- )
265
- try {
266
- if (isToddleFormula(newFunc)) {
267
- return applyFormula(newFunc.formula, {
268
- ...ctx,
269
- data: { ...ctx.data, Args: args },
270
- })
271
- } else {
272
- return newFunc.handler(args, {
273
- root: ctx.root ?? document,
274
- env: ctx.env,
275
- } as any)
276
- }
277
- } catch (e) {
278
- ctx.toddle.errors.push(e as Error)
279
- if (ctx.env?.logErrors) {
280
- console.error(e)
281
- }
282
- return null
283
- } finally {
284
- stopMeasure()
285
- }
286
- } else {
287
- // Lookup legacy formula
288
- const legacyFunc: FormulaHandler | undefined = (
289
- ctx.toddle ??
290
- ((globalThis as any).toddle as Toddle<unknown, unknown>)
291
- ).getFormula(formula.name)
292
- if (typeof legacyFunc === 'function') {
293
- const args = (formula.arguments ?? []).map((arg) =>
294
- arg.isFunction
295
- ? (Args: any) =>
296
- applyFormula(arg.formula, {
297
- ...ctx,
298
- data: {
299
- ...ctx.data,
300
- Args: ctx.data.Args
301
- ? { ...Args, '@toddle.parent': ctx.data.Args }
302
- : Args,
303
- },
304
- })
305
- : applyFormula(arg.formula, ctx),
306
- )
307
- try {
308
- return legacyFunc(args, ctx as any)
309
- } catch (e) {
310
- ctx.toddle.errors.push(e as Error)
311
- if (ctx.env?.logErrors) {
312
- console.error(e)
313
- }
314
- return null
315
- } finally {
316
- stopMeasure()
317
- }
318
- }
319
- }
320
- if (ctx.env?.logErrors) {
321
- console.error(
322
- `Could not find formula ${formula.name} in package ${
323
- packageName ?? ''
324
- }`,
325
- formula,
326
- )
327
- }
328
- return null
258
+ return report(applyFunctionFormula(formula, ctx))
329
259
  }
330
- case 'object':
331
- return Object.fromEntries(
332
- formula.arguments?.map((entry) => [
333
- entry.name,
334
- applyFormula(entry.formula, ctx),
335
- ]) ?? [],
336
- )
337
- case 'record': // object used to be called record, there are still examples in the wild.
338
- return Object.fromEntries(
339
- (formula.entries ?? []).map((entry) => [
340
- entry.name,
341
- applyFormula(entry.formula, ctx),
342
- ]),
343
- )
344
- case 'array':
345
- return (formula.arguments ?? []).map((entry) =>
346
- applyFormula(entry.formula, ctx),
347
- )
348
260
  case 'apply': {
349
- const componentFormula = ctx.component?.formulas?.[formula.name]
350
- if (!componentFormula) {
351
- if (ctx.env?.logErrors) {
352
- console.log(
353
- 'Component does not have a formula with the name ',
354
- formula.name,
355
- )
356
- }
357
- return null
358
- }
359
- const stopMeasure = measure(`Formula: ${componentFormula.name}`, {
360
- formula,
361
- component: ctx.component?.name,
362
- })
363
- const Input = Object.fromEntries(
364
- (formula.arguments ?? []).map((arg) =>
365
- arg.isFunction
366
- ? [
367
- arg.name,
368
- (Args: any) =>
369
- applyFormula(arg.formula, {
370
- ...ctx,
371
- data: {
372
- ...ctx.data,
373
- Args: ctx.data.Args
374
- ? { ...Args, '@toddle.parent': ctx.data.Args }
375
- : Args,
376
- },
377
- }),
378
- ]
379
- : [arg.name, applyFormula(arg.formula, ctx)],
380
- ),
381
- )
382
- const data = {
383
- ...ctx.data,
384
- Args: ctx.data.Args
385
- ? { ...Input, '@toddle.parent': ctx.data.Args }
386
- : Input,
387
- }
388
- const cache = ctx.formulaCache?.[formula.name]?.get(data)
389
-
390
- if (cache?.hit) {
391
- stopMeasure({ cache: 'hit' })
392
- return cache.data
393
- } else {
394
- const result = applyFormula(componentFormula.formula, {
395
- ...ctx,
396
- data,
397
- })
398
- ctx.formulaCache?.[formula.name]?.set(data, result)
399
- stopMeasure({ cache: 'miss' })
400
- return result
401
- }
261
+ return report(applyApplyFormula(formula, ctx))
402
262
  }
403
-
404
263
  default:
405
264
  if (ctx.env?.logErrors) {
406
265
  console.error('Could not recognize formula', formula)
@@ -410,6 +269,8 @@ export function applyFormula(
410
269
  if (ctx.env?.logErrors) {
411
270
  console.error(e)
412
271
  }
413
- return null
272
+ return report(null)
414
273
  }
274
+
275
+ return report(undefined)
415
276
  }
@@ -38,3 +38,8 @@ export interface GlobalFormulas<Handler = string | Function> {
38
38
  >
39
39
  >
40
40
  }
41
+
42
+ export type FormulaEvaluationReporter = (
43
+ path: Array<string | number>,
44
+ result: any,
45
+ ) => void
@@ -11,7 +11,7 @@ import { isFormula, isToddleFormula } from './formula'
11
11
  import type { GlobalFormulas } from './formulaTypes'
12
12
 
13
13
  export const valueFormula = (
14
- value: string | number | boolean | null | object,
14
+ value: string | number | boolean | null | object | undefined,
15
15
  ): ValueOperation => ({
16
16
  type: 'value',
17
17
  value,
@@ -0,0 +1,89 @@
1
+ import { describe, expect, it } from 'bun:test'
2
+ import type { FormulaHandlerV2 } from '../types'
3
+ import type { FunctionOperation } from './formula'
4
+ import type { PluginFormula } from './formulaTypes'
5
+ import { valueFormula } from './formulaUtils'
6
+ import { applyFunctionFormula } from './functionFormula'
7
+ import { createTestFormulaContext } from './testUtils.test'
8
+
9
+ const contextWithCustomFormulas = (
10
+ formulas: Record<string, PluginFormula<FormulaHandlerV2>>,
11
+ ) => {
12
+ return {
13
+ ...createTestFormulaContext(),
14
+ toddle: {
15
+ ...createTestFormulaContext().toddle,
16
+ getCustomFormula: (name: string) => formulas[name],
17
+ },
18
+ }
19
+ }
20
+
21
+ describe('applyFunctionFormula', () => {
22
+ it('returns the value of a simple function formula', () => {
23
+ const ctx = contextWithCustomFormulas({
24
+ identity: {
25
+ version: 2,
26
+ name: 'identity',
27
+ handler: () => 42,
28
+ arguments: [],
29
+ },
30
+ })
31
+ expect(
32
+ applyFunctionFormula(
33
+ { type: 'function', name: 'identity', arguments: [] },
34
+ ctx,
35
+ ),
36
+ ).toBe(42)
37
+ })
38
+
39
+ it('returns null if the function is not found', () => {
40
+ const formula: FunctionOperation = {
41
+ type: 'function',
42
+ name: 'missing',
43
+ arguments: [{ name: 'x', formula: valueFormula(1) }],
44
+ }
45
+ const ctx = contextWithCustomFormulas({})
46
+ expect(applyFunctionFormula(formula, ctx)).toBeNull()
47
+ })
48
+
49
+ it('calls isFunction arguments as functions', () => {
50
+ const formula: FunctionOperation = {
51
+ type: 'function',
52
+ name: 'fn',
53
+ arguments: [
54
+ {
55
+ name: 'fn',
56
+ isFunction: true,
57
+ formula: valueFormula('called'),
58
+ },
59
+ ],
60
+ }
61
+ const ctx = contextWithCustomFormulas({
62
+ fn: {
63
+ version: 2,
64
+ name: 'fn',
65
+ handler: ({ fn }) => {
66
+ return (fn as any)()
67
+ },
68
+ arguments: [{ name: 'fn' }],
69
+ },
70
+ })
71
+ expect(applyFunctionFormula(formula, ctx)).toBe('called')
72
+ })
73
+
74
+ it('returns the result of a ToddleFormula', () => {
75
+ const formula: FunctionOperation = {
76
+ type: 'function',
77
+ name: 'projectFormula',
78
+ arguments: [],
79
+ }
80
+ const ctx = contextWithCustomFormulas({
81
+ projectFormula: {
82
+ name: 'projectFormula',
83
+ arguments: [],
84
+ formula: valueFormula([1, 2, 3]),
85
+ },
86
+ })
87
+ expect(applyFunctionFormula(formula, ctx)).toEqual([1, 2, 3])
88
+ })
89
+ })