@zenithbuild/core 1.2.2 → 1.2.4

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 (83) hide show
  1. package/README.md +20 -19
  2. package/cli/commands/add.ts +2 -2
  3. package/cli/commands/build.ts +2 -3
  4. package/cli/commands/dev.ts +94 -74
  5. package/cli/commands/index.ts +1 -1
  6. package/cli/commands/preview.ts +1 -1
  7. package/cli/commands/remove.ts +2 -2
  8. package/cli/index.ts +1 -1
  9. package/cli/main.ts +1 -1
  10. package/cli/utils/logger.ts +1 -1
  11. package/cli/utils/plugin-manager.ts +1 -1
  12. package/cli/utils/project.ts +4 -4
  13. package/core/components/ErrorPage.zen +218 -0
  14. package/core/components/index.ts +15 -0
  15. package/core/config.ts +1 -0
  16. package/core/index.ts +29 -0
  17. package/dist/compiler-native-frej59m4.node +0 -0
  18. package/dist/core/compiler-native-frej59m4.node +0 -0
  19. package/dist/core/index.js +6293 -0
  20. package/dist/runtime/lifecycle/index.js +1 -0
  21. package/dist/runtime/reactivity/index.js +1 -0
  22. package/dist/zen-build.js +1 -20118
  23. package/dist/zen-dev.js +1 -20118
  24. package/dist/zen-preview.js +1 -20118
  25. package/dist/zenith.js +1 -20118
  26. package/package.json +11 -20
  27. package/compiler/README.md +0 -380
  28. package/compiler/build-analyzer.ts +0 -122
  29. package/compiler/css/index.ts +0 -317
  30. package/compiler/discovery/componentDiscovery.ts +0 -242
  31. package/compiler/discovery/layouts.ts +0 -70
  32. package/compiler/errors/compilerError.ts +0 -56
  33. package/compiler/finalize/finalizeOutput.ts +0 -192
  34. package/compiler/finalize/generateFinalBundle.ts +0 -82
  35. package/compiler/index.ts +0 -83
  36. package/compiler/ir/types.ts +0 -174
  37. package/compiler/output/types.ts +0 -48
  38. package/compiler/parse/detectMapExpressions.ts +0 -102
  39. package/compiler/parse/importTypes.ts +0 -78
  40. package/compiler/parse/parseImports.ts +0 -309
  41. package/compiler/parse/parseScript.ts +0 -46
  42. package/compiler/parse/parseTemplate.ts +0 -628
  43. package/compiler/parse/parseZenFile.ts +0 -66
  44. package/compiler/parse/scriptAnalysis.ts +0 -91
  45. package/compiler/parse/trackLoopContext.ts +0 -82
  46. package/compiler/runtime/dataExposure.ts +0 -332
  47. package/compiler/runtime/generateDOM.ts +0 -255
  48. package/compiler/runtime/generateHydrationBundle.ts +0 -407
  49. package/compiler/runtime/hydration.ts +0 -309
  50. package/compiler/runtime/navigation.ts +0 -432
  51. package/compiler/runtime/thinRuntime.ts +0 -160
  52. package/compiler/runtime/transformIR.ts +0 -406
  53. package/compiler/runtime/wrapExpression.ts +0 -114
  54. package/compiler/runtime/wrapExpressionWithLoop.ts +0 -97
  55. package/compiler/spa-build.ts +0 -917
  56. package/compiler/ssg-build.ts +0 -486
  57. package/compiler/test/component-stacking.test.ts +0 -365
  58. package/compiler/test/map-lowering.test.ts +0 -130
  59. package/compiler/test/validate-test.ts +0 -104
  60. package/compiler/transform/classifyExpression.ts +0 -444
  61. package/compiler/transform/componentResolver.ts +0 -350
  62. package/compiler/transform/componentScriptTransformer.ts +0 -303
  63. package/compiler/transform/expressionTransformer.ts +0 -385
  64. package/compiler/transform/fragmentLowering.ts +0 -819
  65. package/compiler/transform/generateBindings.ts +0 -68
  66. package/compiler/transform/generateHTML.ts +0 -28
  67. package/compiler/transform/layoutProcessor.ts +0 -132
  68. package/compiler/transform/slotResolver.ts +0 -292
  69. package/compiler/transform/transformNode.ts +0 -314
  70. package/compiler/transform/transformTemplate.ts +0 -38
  71. package/compiler/validate/invariants.ts +0 -292
  72. package/compiler/validate/validateExpressions.ts +0 -168
  73. package/core/config/index.ts +0 -18
  74. package/core/config/loader.ts +0 -69
  75. package/core/config/types.ts +0 -119
  76. package/core/plugins/bridge.ts +0 -193
  77. package/core/plugins/index.ts +0 -7
  78. package/core/plugins/registry.ts +0 -126
  79. package/dist/cli.js +0 -11675
  80. package/runtime/build.ts +0 -17
  81. package/runtime/bundle-generator.ts +0 -1266
  82. package/runtime/client-runtime.ts +0 -891
  83. package/runtime/serve.ts +0 -93
@@ -1,365 +0,0 @@
1
- /**
2
- * Component Stacking Test
3
- *
4
- * Tests that multiple sibling components and multiple instances
5
- * of the same component render correctly.
6
- */
7
-
8
- import { compileZenSource } from '../index'
9
- import { discoverComponents, type ComponentMetadata } from '../discovery/componentDiscovery'
10
- import { resolveComponentsInIR } from '../transform/componentResolver'
11
- import { parseTemplate } from '../parse/parseTemplate'
12
- import * as fs from 'fs'
13
- import * as path from 'path'
14
-
15
- // Test 1: Multiple sibling components
16
- async function testMultipleSiblingComponents() {
17
- console.log('\n=== Test 1: Multiple Sibling Components ===\n')
18
-
19
- // Create mock components
20
- const mockComponents = new Map<string, ComponentMetadata>()
21
-
22
- mockComponents.set('Header', {
23
- name: 'Header',
24
- path: '/mock/Header.zen',
25
- template: '<header class="test-header">Header Content</header>',
26
- nodes: [{
27
- type: 'element',
28
- tag: 'header',
29
- attributes: [{ name: 'class', value: 'test-header', location: { line: 1, column: 1 } }],
30
- children: [{ type: 'text', value: 'Header Content', location: { line: 1, column: 1 } }],
31
- location: { line: 1, column: 1 }
32
- }],
33
- slots: [],
34
- props: [],
35
- styles: [],
36
- script: null,
37
- scriptAttributes: null,
38
- hasScript: false,
39
- hasStyles: false
40
- })
41
-
42
- mockComponents.set('Hero', {
43
- name: 'Hero',
44
- path: '/mock/Hero.zen',
45
- template: '<section class="test-hero">Hero Content</section>',
46
- nodes: [{
47
- type: 'element',
48
- tag: 'section',
49
- attributes: [{ name: 'class', value: 'test-hero', location: { line: 1, column: 1 } }],
50
- children: [{ type: 'text', value: 'Hero Content', location: { line: 1, column: 1 } }],
51
- location: { line: 1, column: 1 }
52
- }],
53
- slots: [],
54
- props: [],
55
- styles: [],
56
- script: null,
57
- scriptAttributes: null,
58
- hasScript: false,
59
- hasStyles: false
60
- })
61
-
62
- mockComponents.set('Footer', {
63
- name: 'Footer',
64
- path: '/mock/Footer.zen',
65
- template: '<footer class="test-footer">Footer Content</footer>',
66
- nodes: [{
67
- type: 'element',
68
- tag: 'footer',
69
- attributes: [{ name: 'class', value: 'test-footer', location: { line: 1, column: 1 } }],
70
- children: [{ type: 'text', value: 'Footer Content', location: { line: 1, column: 1 } }],
71
- location: { line: 1, column: 1 }
72
- }],
73
- slots: [],
74
- props: [],
75
- styles: [],
76
- script: null,
77
- scriptAttributes: null,
78
- hasScript: false,
79
- hasStyles: false
80
- })
81
-
82
- // Test source with multiple sibling components
83
- const testSource = `
84
- <script setup="ts">
85
- </script>
86
- <div class="page">
87
- <Header />
88
- <Hero />
89
- <Footer />
90
- </div>
91
- `
92
-
93
- // Parse the template
94
- const template = parseTemplate(testSource, 'test.zen')
95
-
96
- console.log('Parsed nodes:', JSON.stringify(template.nodes, null, 2))
97
- console.log('\nComponent nodes found:')
98
-
99
- // Find all component nodes
100
- function findComponents(nodes: any[], depth = 0): void {
101
- for (const node of nodes) {
102
- if (node.type === 'component') {
103
- console.log(`${' '.repeat(depth)}Component: ${node.name}`)
104
- } else if (node.type === 'element') {
105
- console.log(`${' '.repeat(depth)}Element: <${node.tag}>`)
106
- if (node.children) {
107
- findComponents(node.children, depth + 1)
108
- }
109
- }
110
- }
111
- }
112
-
113
- findComponents(template.nodes)
114
-
115
- // Create IR
116
- const ir = {
117
- filePath: 'test.zen',
118
- template,
119
- script: { raw: '', attributes: {} },
120
- styles: []
121
- }
122
-
123
- // Resolve components
124
- const resolvedIR = resolveComponentsInIR(ir, mockComponents)
125
-
126
- console.log('\nResolved nodes:', JSON.stringify(resolvedIR.template.nodes, null, 2))
127
-
128
- // Count how many test-* classes appear in the resolved HTML
129
- const jsonStr = JSON.stringify(resolvedIR.template.nodes)
130
- const countHeaders = (jsonStr.match(/"value":"test-header"/g) || []).length
131
- const countHeros = (jsonStr.match(/"value":"test-hero"/g) || []).length
132
- const countFooters = (jsonStr.match(/"value":"test-footer"/g) || []).length
133
-
134
- console.log(`\nResults:`)
135
- console.log(` Headers: ${countHeaders} (expected: 1)`)
136
- console.log(` Heros: ${countHeros} (expected: 1)`)
137
- console.log(` Footers: ${countFooters} (expected: 1)`)
138
-
139
- const passed = countHeaders === 1 && countHeros === 1 && countFooters === 1
140
- console.log(`\n${passed ? '✅ PASSED' : '❌ FAILED'}: Multiple sibling components`)
141
-
142
- return passed
143
- }
144
-
145
- // Test 2: Multiple instances of the same component
146
- async function testMultipleInstances() {
147
- console.log('\n=== Test 2: Multiple Instances of Same Component ===\n')
148
-
149
- const mockComponents = new Map<string, ComponentMetadata>()
150
-
151
- mockComponents.set('Card', {
152
- name: 'Card',
153
- path: '/mock/Card.zen',
154
- template: '<div class="card">Card Content</div>',
155
- nodes: [{
156
- type: 'element',
157
- tag: 'div',
158
- attributes: [{ name: 'class', value: 'card', location: { line: 1, column: 1 } }],
159
- children: [{ type: 'text', value: 'Card Content', location: { line: 1, column: 1 } }],
160
- location: { line: 1, column: 1 }
161
- }],
162
- slots: [],
163
- props: [],
164
- styles: [],
165
- script: null,
166
- scriptAttributes: null,
167
- hasScript: false,
168
- hasStyles: false
169
- })
170
-
171
- const testSource = `
172
- <script setup="ts">
173
- </script>
174
- <div class="grid">
175
- <Card />
176
- <Card />
177
- <Card />
178
- </div>
179
- `
180
-
181
- const template = parseTemplate(testSource, 'test.zen')
182
-
183
- console.log('Component nodes found:')
184
-
185
- function countComponents(nodes: any[]): number {
186
- let count = 0
187
- for (const node of nodes) {
188
- if (node.type === 'component') {
189
- console.log(` Found component: ${node.name}`)
190
- count++
191
- } else if (node.type === 'element' && node.children) {
192
- count += countComponents(node.children)
193
- }
194
- }
195
- return count
196
- }
197
-
198
- const componentCount = countComponents(template.nodes)
199
- console.log(`Total component nodes before resolution: ${componentCount}`)
200
-
201
- const ir = {
202
- filePath: 'test.zen',
203
- template,
204
- script: { raw: '', attributes: {} },
205
- styles: []
206
- }
207
-
208
- const resolvedIR = resolveComponentsInIR(ir, mockComponents)
209
-
210
- // Count card divs in resolved output - search for the attribute value
211
- const jsonStr = JSON.stringify(resolvedIR.template.nodes)
212
- const cardCount = (jsonStr.match(/"value":"card"/g) || []).length
213
-
214
- console.log(`\nResolved JSON (first 500 chars): ${jsonStr.substring(0, 500)}...`)
215
- console.log(`\nCard divs in resolved output: ${cardCount} (expected: 3)`)
216
-
217
- const passed = cardCount === 3
218
- console.log(`\n${passed ? '✅ PASSED' : '❌ FAILED'}: Multiple instances of same component`)
219
-
220
- return passed
221
- }
222
-
223
- // Test 3: Nested components
224
- async function testNestedComponents() {
225
- console.log('\n=== Test 3: Nested Components ===\n')
226
-
227
- const mockComponents = new Map<string, ComponentMetadata>()
228
-
229
- mockComponents.set('Outer', {
230
- name: 'Outer',
231
- path: '/mock/Outer.zen',
232
- template: '<div class="outer"><slot /></div>',
233
- nodes: [{
234
- type: 'element',
235
- tag: 'div',
236
- attributes: [{ name: 'class', value: 'outer', location: { line: 1, column: 1 } }],
237
- children: [{
238
- type: 'element',
239
- tag: 'slot',
240
- attributes: [],
241
- children: [],
242
- location: { line: 1, column: 1 }
243
- }],
244
- location: { line: 1, column: 1 }
245
- }],
246
- slots: [{ name: null, location: { line: 1, column: 1 } }],
247
- props: [],
248
- styles: [],
249
- script: null,
250
- scriptAttributes: null,
251
- hasScript: false,
252
- hasStyles: false
253
- })
254
-
255
- mockComponents.set('Inner', {
256
- name: 'Inner',
257
- path: '/mock/Inner.zen',
258
- template: '<span class="inner">Inner Content</span>',
259
- nodes: [{
260
- type: 'element',
261
- tag: 'span',
262
- attributes: [{ name: 'class', value: 'inner', location: { line: 1, column: 1 } }],
263
- children: [{ type: 'text', value: 'Inner Content', location: { line: 1, column: 1 } }],
264
- location: { line: 1, column: 1 }
265
- }],
266
- slots: [],
267
- props: [],
268
- styles: [],
269
- script: null,
270
- scriptAttributes: null,
271
- hasScript: false,
272
- hasStyles: false
273
- })
274
-
275
- const testSource = `
276
- <script setup="ts">
277
- </script>
278
- <Outer>
279
- <Inner />
280
- </Outer>
281
- `
282
-
283
- const template = parseTemplate(testSource, 'test.zen')
284
-
285
- const ir = {
286
- filePath: 'test.zen',
287
- template,
288
- script: { raw: '', attributes: {} },
289
- styles: []
290
- }
291
-
292
- const resolvedIR = resolveComponentsInIR(ir, mockComponents)
293
-
294
- const jsonStr = JSON.stringify(resolvedIR.template.nodes)
295
- const hasOuter = jsonStr.includes('"value":"outer"')
296
- const hasInner = jsonStr.includes('"value":"inner"')
297
-
298
- console.log(`Resolved JSON (first 500 chars): ${jsonStr.substring(0, 500)}...`)
299
- console.log(`Outer div present: ${hasOuter}`)
300
- console.log(`Inner span present: ${hasInner}`)
301
-
302
- const passed = hasOuter && hasInner
303
- console.log(`\n${passed ? '✅ PASSED' : '❌ FAILED'}: Nested components`)
304
-
305
- return passed
306
- }
307
-
308
- // Test 4: Auto-import naming (filename-based)
309
- async function testAutoImportNaming() {
310
- console.log('\n=== Test 4: Auto-Import Naming ===\n')
311
-
312
- // Test the naming algorithm directly
313
- // With the new convention, component name = filename (subdirectories are ignored)
314
- const testCases = [
315
- { input: 'components/Header.zen', expected: 'Header' },
316
- { input: 'components/globals/Header.zen', expected: 'Header' }, // Filename only!
317
- { input: 'components/ui/buttons/Primary.zen', expected: 'Primary' },
318
- { input: 'components/sections/HeroSection.zen', expected: 'HeroSection' },
319
- { input: 'components/ui-kit/Button.zen', expected: 'Button' },
320
- ]
321
-
322
- let passed = true
323
-
324
- for (const tc of testCases) {
325
- // Component name is just the filename without .zen extension
326
- const result = path.basename(tc.input, '.zen')
327
-
328
- const match = result === tc.expected
329
- console.log(` ${match ? '✓' : '✗'} "${tc.input}" → "${result}" (expected: "${tc.expected}")`)
330
-
331
- if (!match) passed = false
332
- }
333
-
334
- console.log(`\n${passed ? '✅ PASSED' : '❌ FAILED'}: Auto-import naming`)
335
-
336
- return passed
337
- }
338
-
339
- // Run all tests
340
- async function runTests() {
341
- console.log('╔════════════════════════════════════════════╗')
342
- console.log('║ Component Stacking Tests ║')
343
- console.log('╚════════════════════════════════════════════╝')
344
-
345
- const results = []
346
-
347
- results.push(await testMultipleSiblingComponents())
348
- results.push(await testMultipleInstances())
349
- results.push(await testNestedComponents())
350
- results.push(await testAutoImportNaming())
351
-
352
- console.log('\n════════════════════════════════════════════')
353
- console.log('Summary:')
354
- console.log(` Total: ${results.length}`)
355
- console.log(` Passed: ${results.filter(r => r).length}`)
356
- console.log(` Failed: ${results.filter(r => !r).length}`)
357
- console.log('════════════════════════════════════════════\n')
358
-
359
- process.exit(results.every(r => r) ? 0 : 1)
360
- }
361
-
362
- runTests().catch(err => {
363
- console.error('Test error:', err)
364
- process.exit(1)
365
- })
@@ -1,130 +0,0 @@
1
- import { parseTemplate } from '../parse/parseTemplate'
2
- import { transformTemplate } from '../transform/transformTemplate'
3
- import { lowerFragments } from '../transform/fragmentLowering'
4
- import { transformNode } from '../transform/transformNode'
5
- import type { ZenIR, TemplateNode, ExpressionNode, LoopFragmentNode } from '../ir/types'
6
-
7
- async function testMapLowering() {
8
- console.log('--- Testing JSX .map() Lowering ---')
9
-
10
- const source = `
11
- <div>
12
- {items.map((item, index) => (
13
- <li key={index} class={item.active ? 'active' : ''}>
14
- {item.text}
15
- </li>
16
- ))}
17
- </div>
18
- `
19
-
20
- try {
21
- const template = parseTemplate(source, 'test.zen')
22
-
23
- // Recursive search for LoopFragmentNode
24
- function findNode(nodes: TemplateNode[], type: string): any {
25
- for (const node of nodes) {
26
- if (node.type === type) return node
27
- if ('children' in node) {
28
- const found = findNode(node.children, type)
29
- if (found) return found
30
- }
31
- if (node.type === 'loop-fragment') {
32
- const found = findNode(node.body, type)
33
- if (found) return found
34
- }
35
- if (node.type === 'conditional-fragment') {
36
- const foundC = findNode(node.consequent, type)
37
- if (foundC) return foundC
38
- const foundA = findNode(node.alternate, type)
39
- if (foundA) return foundA
40
- }
41
- }
42
- return null
43
- }
44
-
45
- const loopNode = findNode(template.nodes, 'loop-fragment') as LoopFragmentNode
46
-
47
- if (!loopNode) {
48
- console.log('Template structure:', JSON.stringify(template.nodes, null, 2))
49
- throw new Error('LoopFragmentNode not found in template')
50
- }
51
-
52
- console.assert(loopNode.source.startsWith('expr_'), 'Loop source should be an expression ID')
53
- const sourceExpr = template.expressions.find(e => e.id === loopNode.source)
54
- console.assert(sourceExpr?.code === 'items', `Expected source code "items", got "${sourceExpr?.code}"`)
55
-
56
- // Check if inner expressions are registered
57
- const li = loopNode.body.find(n => n.type === 'element' && n.tag === 'li') as any
58
- const classAttr = li?.attributes.find((a: any) => a.name === 'class')
59
- console.assert(classAttr?.value.id.startsWith('expr_'), 'Inner attribute expression should be an ID')
60
-
61
- const textExpr = li?.children.find((n: any) => n.type === 'expression') as ExpressionNode
62
- console.assert(textExpr?.expression.startsWith('expr_'), 'Inner text expression should be an ID')
63
-
64
- // Final transformation
65
- const ir: ZenIR = {
66
- filePath: 'test.zen',
67
- template,
68
- script: null,
69
- styles: []
70
- }
71
-
72
- const compiled = transformTemplate(ir)
73
- console.assert(compiled.html.includes('data-zen-loop="loop_0"'), 'HTML should contain loop marker')
74
- console.assert(compiled.html.includes(`data-zen-source="${loopNode.source}"`), 'HTML should contain source expression ID')
75
-
76
- console.log('✅ JSX .map() lowering test passed')
77
- } catch (e) {
78
- console.error('❌ JSX .map() lowering test failed:', e)
79
- process.exit(1)
80
- }
81
- }
82
-
83
- async function testInvariantEnforcement() {
84
- console.log('--- Testing INV-EXPR-REG-001 Invariant Enforcement ---')
85
-
86
- const expressions: any[] = []
87
- const badNode: ExpressionNode = {
88
- type: 'expression',
89
- expression: 'raw.code.here', // Should be expr_N
90
- location: { line: 1, column: 1 }
91
- }
92
-
93
- try {
94
- // We need to call lowerFragments but it calls assertNoRawExpressions
95
- lowerFragments([badNode], 'test.zen', expressions)
96
- console.error('❌ Invariant enforcement failed to catch raw expression')
97
- process.exit(1)
98
- } catch (e: any) {
99
- console.assert(e.name === 'InvariantError', 'Expected InvariantError')
100
- console.assert(e.message.includes('INV-EXPR-REG-001'), 'Expected INV-EXPR-REG-001 in error message')
101
- console.log('✅ Invariant enforcement caught raw expression')
102
- }
103
-
104
- // Test transformNode safety
105
- try {
106
- const expressionsList: any[] = []
107
- const badLoopNode: any = {
108
- type: 'loop-fragment',
109
- source: 'raw_source',
110
- itemVar: 'item',
111
- body: [],
112
- location: { line: 1, column: 1 }
113
- }
114
- transformNode(badLoopNode, expressionsList)
115
- console.error('❌ transformNode failed to catch raw loop source')
116
- process.exit(1)
117
- } catch (e: any) {
118
- console.assert(e.name === 'InvariantError', 'Expected InvariantError in transformNode')
119
- console.assert(e.message.includes('Raw loop source found'), 'Expected Loop source error')
120
- console.log('✅ transformNode caught raw loop source')
121
- }
122
- }
123
-
124
- async function runTests() {
125
- await testMapLowering()
126
- await testInvariantEnforcement()
127
- console.log('--- All tests completed successfully ---')
128
- }
129
-
130
- runTests()
@@ -1,104 +0,0 @@
1
- /**
2
- * Test Cases for Expression Validation
3
- *
4
- * Phase 8/9/10: Tests that invalid expressions fail the build
5
- */
6
-
7
- import { validateExpressions, validateExpressionsOrThrow } from '../validate/validateExpressions'
8
- import type { ExpressionIR } from '../ir/types'
9
-
10
- /**
11
- * Test valid expressions
12
- */
13
- function testValidExpressions() {
14
- const validExpressions: ExpressionIR[] = [
15
- {
16
- id: 'expr_0',
17
- code: 'user.name',
18
- location: { line: 10, column: 5 }
19
- },
20
- {
21
- id: 'expr_1',
22
- code: 'count + 1',
23
- location: { line: 11, column: 8 }
24
- },
25
- {
26
- id: 'expr_2',
27
- code: 'isActive ? "on" : "off"',
28
- location: { line: 12, column: 12 }
29
- }
30
- ]
31
-
32
- const result = validateExpressions(validExpressions, 'test.zen')
33
- console.assert(result.valid === true, 'Valid expressions should pass validation')
34
- console.assert(result.errors.length === 0, 'Valid expressions should have no errors')
35
- console.log('✅ Valid expressions test passed')
36
- }
37
-
38
- /**
39
- * Test invalid expressions
40
- */
41
- function testInvalidExpressions() {
42
- const invalidExpressions: ExpressionIR[] = [
43
- {
44
- id: 'expr_0',
45
- code: 'user.name}', // Mismatched brace
46
- location: { line: 10, column: 5 }
47
- }
48
- ]
49
-
50
- const result = validateExpressions(invalidExpressions, 'test.zen')
51
- console.assert(result.valid === false, 'Invalid expressions should fail validation')
52
- console.assert(result.errors.length > 0, 'Invalid expressions should have errors')
53
- console.log('✅ Invalid expressions test passed')
54
- }
55
-
56
- /**
57
- * Test unsafe code detection
58
- */
59
- function testUnsafeCode() {
60
- const unsafeExpressions: ExpressionIR[] = [
61
- {
62
- id: 'expr_0',
63
- code: 'eval("alert(1)")',
64
- location: { line: 10, column: 5 }
65
- }
66
- ]
67
-
68
- const result = validateExpressions(unsafeExpressions, 'test.zen')
69
- console.assert(result.valid === false, 'Unsafe code should fail validation')
70
- console.assert(result.errors.length > 0, 'Unsafe code should have errors')
71
- console.log('✅ Unsafe code detection test passed')
72
- }
73
-
74
- /**
75
- * Test validateExpressionsOrThrow
76
- */
77
- function testThrowOnInvalid() {
78
- const invalidExpressions: ExpressionIR[] = [
79
- {
80
- id: 'expr_0',
81
- code: 'user.name}', // Mismatched brace
82
- location: { line: 10, column: 5 }
83
- }
84
- ]
85
-
86
- try {
87
- validateExpressionsOrThrow(invalidExpressions, 'test.zen')
88
- console.assert(false, 'Should have thrown on invalid expressions')
89
- } catch (error) {
90
- console.assert(error instanceof Error, 'Should throw Error')
91
- console.log('✅ Throw on invalid expressions test passed')
92
- }
93
- }
94
-
95
- // Run tests
96
- if (require.main === module) {
97
- console.log('Running validation tests...')
98
- testValidExpressions()
99
- testInvalidExpressions()
100
- testUnsafeCode()
101
- testThrowOnInvalid()
102
- console.log('✅ All validation tests passed!')
103
- }
104
-