@nordcraft/core 1.0.94 → 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 (78) hide show
  1. package/dist/api/LegacyToddleApi.js +2 -2
  2. package/dist/api/LegacyToddleApi.js.map +1 -1
  3. package/dist/api/ToddleApiV2.js +2 -2
  4. package/dist/api/ToddleApiV2.js.map +1 -1
  5. package/dist/api/api.js +30 -10
  6. package/dist/api/api.js.map +1 -1
  7. package/dist/component/ToddleComponent.js +3 -3
  8. package/dist/component/ToddleComponent.js.map +1 -1
  9. package/dist/component/component.types.d.ts +3 -3
  10. package/dist/component/component.types.js.map +1 -1
  11. package/dist/formula/andFormula.d.ts +3 -0
  12. package/dist/formula/andFormula.js +25 -0
  13. package/dist/formula/andFormula.js.map +1 -0
  14. package/dist/formula/applyFormula.d.ts +2 -0
  15. package/dist/formula/applyFormula.js +49 -0
  16. package/dist/formula/applyFormula.js.map +1 -0
  17. package/dist/formula/arrayFormula.d.ts +2 -0
  18. package/dist/formula/arrayFormula.js +5 -0
  19. package/dist/formula/arrayFormula.js.map +1 -0
  20. package/dist/formula/formula.d.ts +19 -17
  21. package/dist/formula/formula.js +45 -172
  22. package/dist/formula/formula.js.map +1 -1
  23. package/dist/formula/formulaTypes.d.ts +1 -0
  24. package/dist/formula/formulaUtils.d.ts +1 -1
  25. package/dist/formula/formulaUtils.js +2 -2
  26. package/dist/formula/formulaUtils.js.map +1 -1
  27. package/dist/formula/functionFormula.d.ts +2 -0
  28. package/dist/formula/functionFormula.js +88 -0
  29. package/dist/formula/functionFormula.js.map +1 -0
  30. package/dist/formula/objectFormula.d.ts +2 -0
  31. package/dist/formula/objectFormula.js +8 -0
  32. package/dist/formula/objectFormula.js.map +1 -0
  33. package/dist/formula/orFormula.d.ts +3 -0
  34. package/dist/formula/orFormula.js +27 -0
  35. package/dist/formula/orFormula.js.map +1 -0
  36. package/dist/formula/pathFormula.d.ts +2 -0
  37. package/dist/formula/pathFormula.js +13 -0
  38. package/dist/formula/pathFormula.js.map +1 -0
  39. package/dist/formula/recordFormula.d.ts +2 -0
  40. package/dist/formula/recordFormula.js +8 -0
  41. package/dist/formula/recordFormula.js.map +1 -0
  42. package/dist/formula/switchFormula.d.ts +3 -0
  43. package/dist/formula/switchFormula.js +40 -0
  44. package/dist/formula/switchFormula.js.map +1 -0
  45. package/dist/types.d.ts +1 -1
  46. package/dist/utils/measure.js +5 -0
  47. package/dist/utils/measure.js.map +1 -1
  48. package/package.json +1 -1
  49. package/src/api/LegacyToddleApi.ts +2 -2
  50. package/src/api/ToddleApiV2.ts +2 -2
  51. package/src/api/api.test.ts +22 -22
  52. package/src/api/api.ts +36 -10
  53. package/src/component/ToddleComponent.ts +9 -7
  54. package/src/component/component.types.ts +5 -3
  55. package/src/formula/andFormula.test.ts +112 -0
  56. package/src/formula/andFormula.ts +33 -0
  57. package/src/formula/applyFormula.test.ts +151 -0
  58. package/src/formula/applyFormula.ts +72 -0
  59. package/src/formula/arrayFormula.test.ts +52 -0
  60. package/src/formula/arrayFormula.ts +14 -0
  61. package/src/formula/formula.ts +79 -214
  62. package/src/formula/formulaTypes.ts +5 -0
  63. package/src/formula/formulaUtils.ts +3 -3
  64. package/src/formula/functionFormula.test.ts +89 -0
  65. package/src/formula/functionFormula.ts +118 -0
  66. package/src/formula/objectFormula.test.ts +56 -0
  67. package/src/formula/objectFormula.ts +17 -0
  68. package/src/formula/orFormula.test.ts +113 -0
  69. package/src/formula/orFormula.ts +33 -0
  70. package/src/formula/pathFormula.test.ts +35 -0
  71. package/src/formula/pathFormula.ts +17 -0
  72. package/src/formula/recordFormula.test.ts +56 -0
  73. package/src/formula/recordFormula.ts +17 -0
  74. package/src/formula/switchFormula.test.ts +122 -0
  75. package/src/formula/switchFormula.ts +54 -0
  76. package/src/formula/testUtils.test.ts +68 -0
  77. package/src/types.ts +1 -1
  78. package/src/utils/measure.ts +6 -0
@@ -0,0 +1,122 @@
1
+ import { describe, expect, it } from 'bun:test'
2
+ import { applyFormula, type SwitchOperation } from './formula'
3
+ import { valueFormula } from './formulaUtils'
4
+ import {
5
+ applyEvaluateAllSwitchFormula,
6
+ applySwitchFormula,
7
+ } from './switchFormula'
8
+ import {
9
+ createTestFormulaContext,
10
+ createTestFormulaContextForAllPaths,
11
+ } from './testUtils.test'
12
+
13
+ describe('applySwitchFormula', () => {
14
+ it('returns the first matching case value', () => {
15
+ const formula: SwitchOperation = {
16
+ type: 'switch',
17
+ cases: [
18
+ { condition: valueFormula(false), formula: valueFormula('no') },
19
+ { condition: valueFormula(true), formula: valueFormula('yes') },
20
+ {
21
+ condition: valueFormula(true),
22
+ formula: valueFormula('should not match'),
23
+ },
24
+ ],
25
+ default: valueFormula('default'),
26
+ }
27
+ const ctx = createTestFormulaContext()
28
+ expect(applySwitchFormula(formula, ctx)).toBe('yes')
29
+ })
30
+
31
+ it('returns the default value if no case matches', () => {
32
+ const formula: SwitchOperation = {
33
+ type: 'switch',
34
+ cases: [
35
+ { condition: valueFormula(false), formula: valueFormula('no') },
36
+ { condition: valueFormula(false), formula: valueFormula('nope') },
37
+ ],
38
+ default: valueFormula('default'),
39
+ }
40
+ const ctx = createTestFormulaContext()
41
+ expect(applySwitchFormula(formula, ctx)).toBe('default')
42
+ })
43
+
44
+ it('evaluates conditions using context data', () => {
45
+ const formula: SwitchOperation = {
46
+ type: 'switch',
47
+ cases: [
48
+ {
49
+ condition: { type: 'path', path: ['foo'] },
50
+ formula: valueFormula('foo matched'),
51
+ },
52
+ { condition: valueFormula(true), formula: valueFormula('fallback') },
53
+ ],
54
+ default: valueFormula('default'),
55
+ }
56
+ const ctx = createTestFormulaContext({ foo: true })
57
+ expect(applySwitchFormula(formula, ctx)).toBe('foo matched')
58
+ })
59
+ })
60
+
61
+ describe('applyEvaluateAllSwitchFormula', () => {
62
+ it('returns the first matching case value, but evaluates all cases and default', () => {
63
+ const formula: SwitchOperation = {
64
+ type: 'switch',
65
+ cases: [
66
+ { condition: valueFormula(false), formula: valueFormula('no') },
67
+ { condition: valueFormula(true), formula: valueFormula('yes') },
68
+ {
69
+ condition: valueFormula(true),
70
+ formula: valueFormula('should not match'),
71
+ },
72
+ ],
73
+ default: valueFormula('default'),
74
+ }
75
+ const results: Record<string, any> = {}
76
+ const ctx = createTestFormulaContextForAllPaths(
77
+ {},
78
+ (path, result) => (results[path.join('/')] = result),
79
+ )
80
+ // We use applyFormula directly since it will gather results in EVALUATE ALL PATHS mode
81
+ const result = applyFormula(formula, ctx, [])
82
+ expect(result).toBe('yes')
83
+ expect(results).toMatchObject({
84
+ 'cases/0/condition': false,
85
+ 'cases/0/formula': 'no',
86
+ 'cases/1/condition': true,
87
+ 'cases/1/formula': 'yes',
88
+ 'cases/2/condition': true,
89
+ 'cases/2/formula': 'should not match',
90
+ default: 'default',
91
+ })
92
+ })
93
+
94
+ it('returns the default value if no case matches', () => {
95
+ const formula: SwitchOperation = {
96
+ type: 'switch',
97
+ cases: [
98
+ { condition: valueFormula(false), formula: valueFormula('no') },
99
+ { condition: valueFormula(false), formula: valueFormula('nope') },
100
+ ],
101
+ default: valueFormula('default'),
102
+ }
103
+ const ctx = createTestFormulaContext()
104
+ expect(applyEvaluateAllSwitchFormula(formula, ctx)).toBe('default')
105
+ })
106
+
107
+ it('evaluates conditions using context data', () => {
108
+ const formula: SwitchOperation = {
109
+ type: 'switch',
110
+ cases: [
111
+ {
112
+ condition: { type: 'path', path: ['foo'] },
113
+ formula: valueFormula('foo matched'),
114
+ },
115
+ { condition: valueFormula(true), formula: valueFormula('fallback') },
116
+ ],
117
+ default: valueFormula('default'),
118
+ }
119
+ const ctx = createTestFormulaContext({ foo: true })
120
+ expect(applyEvaluateAllSwitchFormula(formula, ctx)).toBe('foo matched')
121
+ })
122
+ })
@@ -0,0 +1,54 @@
1
+ import { toBoolean } from '../utils/util'
2
+ import {
3
+ applyFormula,
4
+ type FormulaContext,
5
+ type SwitchOperation,
6
+ } from './formula'
7
+
8
+ export const applySwitchFormula = (
9
+ formula: SwitchOperation,
10
+ ctx: FormulaContext,
11
+ ) => {
12
+ // Evaluates cases until one matches
13
+ for (let i = 0; i < (formula.cases ?? []).length; i++) {
14
+ const switchCase = (formula.cases ?? [])[i]
15
+ if (
16
+ toBoolean(
17
+ applyFormula(switchCase?.condition, ctx, ['cases', i, 'condition']),
18
+ )
19
+ ) {
20
+ return applyFormula(switchCase?.formula, ctx, ['cases', i, 'formula'])
21
+ }
22
+ }
23
+ return applyFormula(formula.default, ctx, ['default'])
24
+ }
25
+
26
+ export const applyEvaluateAllSwitchFormula = (
27
+ formula: SwitchOperation,
28
+ ctx: FormulaContext,
29
+ ) => {
30
+ // Evaluate all cases and the default, but only returns the first matching case or the default
31
+ let switchResult: { match: true; value: any } | null = null
32
+ for (let i = 0; i < (formula.cases ?? []).length; i++) {
33
+ const switchCase = (formula.cases ?? [])[i]
34
+ const conditionValue = applyFormula(switchCase?.condition, ctx, [
35
+ 'cases',
36
+ i,
37
+ 'condition',
38
+ ])
39
+ const formulaValue = applyFormula(switchCase?.formula, ctx, [
40
+ 'cases',
41
+ i,
42
+ 'formula',
43
+ ])
44
+ if (toBoolean(conditionValue) && switchResult === null) {
45
+ switchResult = { match: true, value: formulaValue }
46
+ }
47
+ }
48
+ const defaultValue = applyFormula(formula.default, ctx, ['default'])
49
+ if (switchResult !== null) {
50
+ return switchResult.value
51
+ } else {
52
+ return defaultValue
53
+ }
54
+ }
@@ -0,0 +1,68 @@
1
+ import type { FormulaContext } from './formula'
2
+ import type { FormulaEvaluationReporter } from './formulaTypes'
3
+
4
+ // Register all core formulas globally for tests
5
+ if (!(globalThis as any).__CORE_FORMULAS__) {
6
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
7
+ const libFormulas = require('../../../lib/dist/formulas')
8
+ ;(globalThis as any).__CORE_FORMULAS__ = Object.fromEntries(
9
+ Object.entries(libFormulas).map(([name, module]) => [
10
+ '@toddle/' + name,
11
+ ((module as any).default ?? module) as any,
12
+ ]),
13
+ )
14
+ }
15
+
16
+ // Helper to create a minimal context
17
+ export const createTestFormulaContext = (data: any = {}): FormulaContext => {
18
+ const coreFormulas = (globalThis as any).__CORE_FORMULAS__ as
19
+ | Record<string, any>
20
+ | undefined
21
+ return {
22
+ component: undefined,
23
+ formulaCache: {},
24
+ data,
25
+ root: {} as Document,
26
+ package: undefined,
27
+ toddle: {
28
+ getFormula: (name: string) => coreFormulas?.[name],
29
+ getCustomFormula: () => undefined,
30
+ errors: [],
31
+ },
32
+ env: {
33
+ logErrors: true,
34
+ runtime: 'page',
35
+ branchName: 'main',
36
+ isServer: false,
37
+ request: undefined,
38
+ },
39
+ reportFormulaEvaluation: undefined,
40
+ jsonPath: [],
41
+ }
42
+ }
43
+
44
+ // Helper to create a minimal context when evaluating all paths
45
+ export const createTestFormulaContextForAllPaths = (
46
+ data: any = {},
47
+ formulaEvaluationReporter: FormulaEvaluationReporter,
48
+ ): FormulaContext => ({
49
+ component: undefined,
50
+ formulaCache: {},
51
+ data,
52
+ root: undefined,
53
+ package: undefined,
54
+ toddle: {
55
+ getFormula: (name: string) => coreFormulas[name],
56
+ getCustomFormula: () => undefined,
57
+ errors: [],
58
+ },
59
+ env: {
60
+ logErrors: true,
61
+ runtime: 'page',
62
+ branchName: 'main',
63
+ isServer: false,
64
+ request: undefined,
65
+ },
66
+ jsonPath: [],
67
+ reportFormulaEvaluation: formulaEvaluationReporter,
68
+ })
package/src/types.ts CHANGED
@@ -85,7 +85,7 @@ export type ArgumentInputDataFunction = (
85
85
 
86
86
  export type CustomFormulaHandler = (
87
87
  name: string,
88
- packageName: string | undefined,
88
+ packageName: string | null | undefined,
89
89
  ) => PluginFormula<FormulaHandlerV2> | undefined
90
90
 
91
91
  export type FormulaLookup = (name: string) => FormulaHandler | undefined
@@ -31,7 +31,13 @@ export const measure = (key: string, details: Record<string, unknown>) => {
31
31
  const start = performance.now()
32
32
  STACK.push(key)
33
33
 
34
+ let _stopped = false
34
35
  return (extraDetails?: Record<string, unknown>) => {
36
+ if (_stopped) {
37
+ return
38
+ }
39
+
40
+ _stopped = true
35
41
  const end = performance.now()
36
42
  const mergedDetails = extraDetails
37
43
  ? { ...details, ...extraDetails }