@zenithbuild/core 0.6.2 → 0.6.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 (112) hide show
  1. package/CORE_CONTRACT.md +145 -0
  2. package/README.md +14 -29
  3. package/bin/zenith.js +89 -0
  4. package/package.json +39 -56
  5. package/src/config.js +136 -0
  6. package/src/core-template.js +30 -0
  7. package/src/errors.js +54 -0
  8. package/src/guards.js +61 -0
  9. package/src/hash.js +52 -0
  10. package/src/index.js +26 -0
  11. package/src/ir/index.js +1 -0
  12. package/src/order.js +69 -0
  13. package/src/path.js +131 -0
  14. package/src/schema.js +28 -0
  15. package/src/version.js +67 -0
  16. package/bin/zen-build.ts +0 -2
  17. package/bin/zen-dev.ts +0 -2
  18. package/bin/zen-preview.ts +0 -2
  19. package/bin/zenith.ts +0 -2
  20. package/cli/commands/add.ts +0 -37
  21. package/cli/commands/build.ts +0 -37
  22. package/cli/commands/create.ts +0 -702
  23. package/cli/commands/dev.ts +0 -388
  24. package/cli/commands/index.ts +0 -112
  25. package/cli/commands/preview.ts +0 -62
  26. package/cli/commands/remove.ts +0 -33
  27. package/cli/index.ts +0 -10
  28. package/cli/main.ts +0 -101
  29. package/cli/utils/branding.ts +0 -178
  30. package/cli/utils/content.ts +0 -112
  31. package/cli/utils/logger.ts +0 -46
  32. package/cli/utils/plugin-manager.ts +0 -114
  33. package/cli/utils/project.ts +0 -77
  34. package/compiler/README.md +0 -380
  35. package/compiler/build-analyzer.ts +0 -122
  36. package/compiler/css/index.ts +0 -317
  37. package/compiler/discovery/componentDiscovery.ts +0 -178
  38. package/compiler/discovery/layouts.ts +0 -70
  39. package/compiler/errors/compilerError.ts +0 -56
  40. package/compiler/finalize/finalizeOutput.ts +0 -192
  41. package/compiler/finalize/generateFinalBundle.ts +0 -82
  42. package/compiler/index.ts +0 -83
  43. package/compiler/ir/types.ts +0 -174
  44. package/compiler/output/types.ts +0 -34
  45. package/compiler/parse/detectMapExpressions.ts +0 -102
  46. package/compiler/parse/importTypes.ts +0 -78
  47. package/compiler/parse/parseImports.ts +0 -309
  48. package/compiler/parse/parseScript.ts +0 -46
  49. package/compiler/parse/parseTemplate.ts +0 -599
  50. package/compiler/parse/parseZenFile.ts +0 -66
  51. package/compiler/parse/scriptAnalysis.ts +0 -91
  52. package/compiler/parse/trackLoopContext.ts +0 -82
  53. package/compiler/runtime/dataExposure.ts +0 -317
  54. package/compiler/runtime/generateDOM.ts +0 -246
  55. package/compiler/runtime/generateHydrationBundle.ts +0 -407
  56. package/compiler/runtime/hydration.ts +0 -309
  57. package/compiler/runtime/navigation.ts +0 -432
  58. package/compiler/runtime/thinRuntime.ts +0 -160
  59. package/compiler/runtime/transformIR.ts +0 -370
  60. package/compiler/runtime/wrapExpression.ts +0 -95
  61. package/compiler/runtime/wrapExpressionWithLoop.ts +0 -83
  62. package/compiler/spa-build.ts +0 -917
  63. package/compiler/ssg-build.ts +0 -422
  64. package/compiler/test/validate-test.ts +0 -104
  65. package/compiler/transform/classifyExpression.ts +0 -444
  66. package/compiler/transform/componentResolver.ts +0 -312
  67. package/compiler/transform/componentScriptTransformer.ts +0 -303
  68. package/compiler/transform/expressionTransformer.ts +0 -385
  69. package/compiler/transform/fragmentLowering.ts +0 -634
  70. package/compiler/transform/generateBindings.ts +0 -47
  71. package/compiler/transform/generateHTML.ts +0 -28
  72. package/compiler/transform/layoutProcessor.ts +0 -132
  73. package/compiler/transform/slotResolver.ts +0 -292
  74. package/compiler/transform/transformNode.ts +0 -126
  75. package/compiler/transform/transformTemplate.ts +0 -38
  76. package/compiler/validate/invariants.ts +0 -292
  77. package/compiler/validate/validateExpressions.ts +0 -168
  78. package/core/config/index.ts +0 -16
  79. package/core/config/loader.ts +0 -69
  80. package/core/config/types.ts +0 -89
  81. package/core/index.ts +0 -135
  82. package/core/lifecycle/index.ts +0 -49
  83. package/core/lifecycle/zen-mount.ts +0 -182
  84. package/core/lifecycle/zen-unmount.ts +0 -88
  85. package/core/plugins/index.ts +0 -7
  86. package/core/plugins/registry.ts +0 -81
  87. package/core/reactivity/index.ts +0 -54
  88. package/core/reactivity/tracking.ts +0 -167
  89. package/core/reactivity/zen-batch.ts +0 -57
  90. package/core/reactivity/zen-effect.ts +0 -139
  91. package/core/reactivity/zen-memo.ts +0 -146
  92. package/core/reactivity/zen-ref.ts +0 -52
  93. package/core/reactivity/zen-signal.ts +0 -121
  94. package/core/reactivity/zen-state.ts +0 -180
  95. package/core/reactivity/zen-untrack.ts +0 -44
  96. package/dist/cli.js +0 -11665
  97. package/dist/zen-build.js +0 -21172
  98. package/dist/zen-dev.js +0 -21172
  99. package/dist/zen-preview.js +0 -21172
  100. package/dist/zenith.js +0 -21172
  101. package/router/index.ts +0 -28
  102. package/router/manifest.ts +0 -314
  103. package/router/navigation/ZenLink.zen +0 -231
  104. package/router/navigation/index.ts +0 -78
  105. package/router/navigation/zen-link.ts +0 -584
  106. package/router/runtime.ts +0 -458
  107. package/router/types.ts +0 -168
  108. package/runtime/build.ts +0 -17
  109. package/runtime/bundle-generator.ts +0 -1247
  110. package/runtime/client-runtime.ts +0 -549
  111. package/runtime/serve.ts +0 -93
  112. package/tsconfig.json +0 -28
@@ -1,422 +0,0 @@
1
- /**
2
- * Zenith SSG Build System
3
- *
4
- * SSG-first (Static Site Generation) build system that outputs:
5
- * - Per-page HTML files: dist/{route}/index.html
6
- * - Shared runtime: dist/assets/bundle.js
7
- * - Global styles: dist/assets/styles.css
8
- * - Page-specific JS only for pages needing hydration: dist/assets/page_{name}.js
9
- *
10
- * Static pages get pure HTML+CSS, no JavaScript.
11
- * Hydrated pages reference the shared bundle.js and their page-specific JS.
12
- */
13
-
14
- import fs from "fs"
15
- import path from "path"
16
- import { compileZenSource } from "./index"
17
- import { discoverLayouts } from "./discovery/layouts"
18
- import { processLayout } from "./transform/layoutProcessor"
19
- import { discoverPages, generateRouteDefinition } from "@zenithbuild/router/manifest"
20
- import { analyzePageSource, getAnalysisSummary, getBuildOutputType, type PageAnalysis } from "./build-analyzer"
21
- import { generateBundleJS } from "../runtime/bundle-generator"
22
- import { loadContent } from "../cli/utils/content"
23
- import { compileCss, resolveGlobalsCss } from "./css"
24
-
25
- // ============================================
26
- // Types
27
- // ============================================
28
-
29
- interface CompiledPage {
30
- /** Route path like "/" or "/about" or "/blog/:id" */
31
- routePath: string
32
- /** Original file path */
33
- filePath: string
34
- /** Compiled HTML content */
35
- html: string
36
- /** Page-specific JavaScript (empty if static) */
37
- pageScript: string
38
- /** Page styles */
39
- styles: string[]
40
- /** Route score for matching priority */
41
- score: number
42
- /** Dynamic route parameter names */
43
- paramNames: string[]
44
- /** Build analysis result */
45
- analysis: PageAnalysis
46
- /** Output directory relative to dist/ */
47
- outputDir: string
48
- }
49
-
50
- export interface SSGBuildOptions {
51
- /** Pages directory (e.g., app/pages) */
52
- pagesDir: string
53
- /** Output directory (e.g., app/dist) */
54
- outDir: string
55
- /** Base directory for components/layouts (e.g., app/) */
56
- baseDir?: string
57
- /** Include source maps */
58
- sourceMaps?: boolean
59
- }
60
-
61
- // ============================================
62
- // Page Compilation
63
- // ============================================
64
-
65
- /**
66
- * Compile a single page file for SSG output
67
- */
68
- async function compilePage(
69
- pagePath: string,
70
- pagesDir: string,
71
- baseDir: string = process.cwd()
72
- ): Promise<CompiledPage> {
73
- const source = fs.readFileSync(pagePath, 'utf-8')
74
-
75
- // Analyze page requirements
76
- const analysis = analyzePageSource(source)
77
-
78
- // Discover layouts
79
- const layoutsDir = path.join(baseDir, 'layouts')
80
- const layouts = discoverLayouts(layoutsDir)
81
-
82
- // Process with layout if one is used
83
- let processedSource = source
84
- const layoutToUse = layouts.get('DefaultLayout')
85
-
86
- if (layoutToUse) {
87
- processedSource = processLayout(source, layoutToUse)
88
- }
89
-
90
- // Compile with new pipeline
91
- const result = await compileZenSource(processedSource, pagePath)
92
-
93
- if (!result.finalized) {
94
- throw new Error(`Compilation failed for ${pagePath}: No finalized output`)
95
- }
96
-
97
- // Extract compiled output
98
- const html = result.finalized.html
99
- const js = result.finalized.js || ''
100
- const styles = result.finalized.styles || []
101
-
102
- // Generate route definition
103
- const routeDef = generateRouteDefinition(pagePath, pagesDir)
104
-
105
- // Determine output directory from route path
106
- // "/" -> "index", "/about" -> "about", "/blog/post" -> "blog/post"
107
- let outputDir = routeDef.path === '/' ? 'index' : routeDef.path.replace(/^\//, '')
108
-
109
- // Handle dynamic routes - they'll be placeholders for now
110
- // [id] segments become _id_ for folder names
111
- outputDir = outputDir.replace(/\[([^\]]+)\]/g, '_$1_')
112
-
113
- return {
114
- routePath: routeDef.path,
115
- filePath: pagePath,
116
- html,
117
- pageScript: analysis.needsHydration ? js : '',
118
- styles,
119
- score: routeDef.score,
120
- paramNames: routeDef.paramNames,
121
- analysis,
122
- outputDir
123
- }
124
- }
125
-
126
- // ============================================
127
- // HTML Generation
128
- // ============================================
129
-
130
- /**
131
- * Generate the final HTML for a page
132
- * Static pages: no JS references
133
- * Hydrated pages: bundle.js + page-specific JS
134
- */
135
- function generatePageHTML(page: CompiledPage, globalStyles: string, contentData: any): string {
136
- const { html, styles, analysis, routePath, pageScript } = page
137
-
138
- // Combine styles
139
- const pageStyles = styles.join('\n')
140
- const allStyles = globalStyles + '\n' + pageStyles
141
-
142
- // Build script tags only if needed
143
- let scriptTags = ''
144
- if (analysis.needsHydration) {
145
- scriptTags = `
146
- <script>window.__ZENITH_CONTENT__ = ${JSON.stringify(contentData)};</script>
147
- <script src="/assets/bundle.js"></script>`
148
-
149
- if (pageScript) {
150
- // Generate a safe filename from route path
151
- const pageJsName = routePath === '/'
152
- ? 'page_index.js'
153
- : `page_${routePath.replace(/^\//, '').replace(/\//g, '_')}.js`
154
- scriptTags += `
155
- <script src="/assets/${pageJsName}"></script>`
156
- }
157
- }
158
-
159
- // Check if HTML already has full document structure
160
- const hasHtmlTag = /<html[^>]*>/i.test(html)
161
-
162
- if (hasHtmlTag) {
163
- // HTML already has structure from layout - inject styles and scripts
164
- let finalHtml = html
165
-
166
- // Inject styles into <head> if not already there
167
- if (!/<style[^>]*>/.test(finalHtml)) {
168
- finalHtml = finalHtml.replace(
169
- '</head>',
170
- ` <style>\n${allStyles}\n </style>\n</head>`
171
- )
172
- }
173
-
174
- // Inject scripts before </body>
175
- if (scriptTags) {
176
- finalHtml = finalHtml.replace(
177
- '</body>',
178
- `${scriptTags}\n</body>`
179
- )
180
- }
181
-
182
- return finalHtml
183
- }
184
-
185
- // Generate full HTML document for pages without layout
186
- return `<!DOCTYPE html>
187
- <html lang="en">
188
- <head>
189
- <meta charset="UTF-8">
190
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
191
- <title>Zenith App</title>
192
- <style>
193
- ${allStyles}
194
- </style>
195
- </head>
196
- <body>
197
- ${html}${scriptTags}
198
- </body>
199
- </html>`
200
- }
201
-
202
- // ============================================
203
- // Asset Generation
204
- // ============================================
205
-
206
- /**
207
- * Generate page-specific JavaScript
208
- */
209
- function generatePageJS(page: CompiledPage): string {
210
- if (!page.pageScript) return ''
211
-
212
- // Wrap in IIFE for isolation
213
- return `// Zenith Page: ${page.routePath}
214
- (function() {
215
- 'use strict';
216
-
217
- ${page.pageScript}
218
-
219
- // Trigger hydration after DOM is ready
220
- if (document.readyState === 'loading') {
221
- document.addEventListener('DOMContentLoaded', function() {
222
- if (window.__zenith && window.__zenith.triggerMount) {
223
- window.__zenith.triggerMount();
224
- }
225
- });
226
- } else {
227
- if (window.__zenith && window.__zenith.triggerMount) {
228
- window.__zenith.triggerMount();
229
- }
230
- }
231
- })();
232
- `
233
- }
234
-
235
- // ============================================
236
- // Main Build Function
237
- // ============================================
238
-
239
- /**
240
- * Build all pages using SSG approach
241
- */
242
- export async function buildSSG(options: SSGBuildOptions): Promise<void> {
243
- const { pagesDir, outDir, baseDir = path.dirname(pagesDir) } = options
244
- const contentDir = path.join(baseDir, 'content')
245
- const contentData = loadContent(contentDir)
246
-
247
- console.log('🔨 Zenith SSG Build')
248
- console.log(` Pages: ${pagesDir}`)
249
- console.log(` Output: ${outDir}`)
250
- console.log('')
251
-
252
- // Clean and create output directory
253
- if (fs.existsSync(outDir)) {
254
- fs.rmSync(outDir, { recursive: true, force: true })
255
- }
256
- fs.mkdirSync(outDir, { recursive: true })
257
- fs.mkdirSync(path.join(outDir, 'assets'), { recursive: true })
258
-
259
- // Discover pages
260
- const pageFiles = discoverPages(pagesDir)
261
-
262
- if (pageFiles.length === 0) {
263
- console.warn('⚠️ No pages found in', pagesDir)
264
- return
265
- }
266
-
267
- console.log(`📄 Found ${pageFiles.length} page(s)`)
268
-
269
- // Compile all pages
270
- const compiledPages: CompiledPage[] = []
271
- let hasHydratedPages = false
272
-
273
- for (const pageFile of pageFiles) {
274
- const relativePath = path.relative(pagesDir, pageFile)
275
- console.log(` Compiling: ${relativePath}`)
276
-
277
- try {
278
- const compiled = await compilePage(pageFile, pagesDir, baseDir)
279
- compiledPages.push(compiled)
280
-
281
- if (compiled.analysis.needsHydration) {
282
- hasHydratedPages = true
283
- }
284
-
285
- const outputType = getBuildOutputType(compiled.analysis)
286
- const summary = getAnalysisSummary(compiled.analysis)
287
- console.log(` → ${outputType.toUpperCase()} [${summary}]`)
288
- } catch (error: any) {
289
- console.error(` ❌ Error: ${error.message}`)
290
- throw error
291
- }
292
- }
293
-
294
- console.log('')
295
-
296
- // Compile global styles (Tailwind CSS)
297
- let globalStyles = ''
298
- const globalsCssPath = resolveGlobalsCss(baseDir)
299
- if (globalsCssPath) {
300
- console.log('📦 Compiling CSS:', path.relative(baseDir, globalsCssPath))
301
- const cssOutputPath = path.join(outDir, 'assets', 'styles.css')
302
- const result = compileCss({
303
- input: globalsCssPath,
304
- output: cssOutputPath,
305
- minify: true
306
- })
307
- if (result.success) {
308
- globalStyles = result.css
309
- console.log(`📦 Generated assets/styles.css (${result.duration}ms)`)
310
- } else {
311
- console.error('❌ CSS compilation failed:', result.error)
312
- }
313
- }
314
-
315
- // Write bundle.js if any pages need hydration
316
- if (hasHydratedPages) {
317
- const bundleJS = generateBundleJS()
318
- fs.writeFileSync(path.join(outDir, 'assets', 'bundle.js'), bundleJS)
319
- console.log('📦 Generated assets/bundle.js')
320
- }
321
-
322
- // Write each page
323
- for (const page of compiledPages) {
324
- // Create output directory
325
- const pageOutDir = path.join(outDir, page.outputDir)
326
- fs.mkdirSync(pageOutDir, { recursive: true })
327
-
328
- // Generate and write HTML
329
- const html = generatePageHTML(page, globalStyles, contentData)
330
- fs.writeFileSync(path.join(pageOutDir, 'index.html'), html)
331
-
332
- // Write page-specific JS if needed
333
- if (page.pageScript) {
334
- const pageJsName = page.routePath === '/'
335
- ? 'page_index.js'
336
- : `page_${page.routePath.replace(/^\//, '').replace(/\//g, '_')}.js`
337
- const pageJS = generatePageJS(page)
338
- fs.writeFileSync(path.join(outDir, 'assets', pageJsName), pageJS)
339
- }
340
-
341
- console.log(`✅ ${page.outputDir}/index.html`)
342
- }
343
-
344
- // Copy favicon if exists
345
- const faviconPath = path.join(baseDir, 'favicon.ico')
346
- if (fs.existsSync(faviconPath)) {
347
- fs.copyFileSync(faviconPath, path.join(outDir, 'favicon.ico'))
348
- console.log('📦 Copied favicon.ico')
349
- }
350
-
351
- // Generate 404 page
352
- const custom404Candidates = ['404.zen', '+404.zen', 'not-found.zen']
353
- let has404 = false
354
-
355
- for (const candidate of custom404Candidates) {
356
- const custom404Path = path.join(pagesDir, candidate)
357
- if (fs.existsSync(custom404Path)) {
358
- try {
359
- const compiled = await compilePage(custom404Path, pagesDir, baseDir)
360
- const html = generatePageHTML(compiled, globalStyles, contentData)
361
- fs.writeFileSync(path.join(outDir, '404.html'), html)
362
- console.log('📦 Generated 404.html (custom)')
363
- has404 = true
364
- if (compiled.pageScript) {
365
- const pageJS = generatePageJS(compiled)
366
- fs.writeFileSync(path.join(outDir, 'assets', 'page_404.js'), pageJS)
367
- }
368
- } catch (error: any) {
369
- console.warn(` ⚠️ Could not compile ${candidate}: ${error.message}`)
370
- }
371
- break
372
- }
373
- }
374
-
375
- if (!has404) {
376
- const default404HTML = `<!DOCTYPE html>
377
- <html lang="en">
378
- <head>
379
- <meta charset="UTF-8">
380
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
381
- <title>Page Not Found | Zenith</title>
382
- <style>
383
- * { box-sizing: border-box; margin: 0; padding: 0; }
384
- body { font-family: system-ui, sans-serif; background: linear-gradient(135deg, #0f172a 0%, #1e293b 100%); color: #f1f5f9; min-height: 100vh; display: flex; align-items: center; justify-content: center; }
385
- .container { text-align: center; padding: 2rem; }
386
- .error-code { font-size: 8rem; font-weight: 800; background: linear-gradient(135deg, #3b82f6, #06b6d4); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text; line-height: 1; margin-bottom: 1rem; }
387
- h1 { font-size: 1.5rem; font-weight: 600; margin-bottom: 1rem; color: #e2e8f0; }
388
- .message { color: #94a3b8; margin-bottom: 2rem; }
389
- a { display: inline-block; background: linear-gradient(135deg, #3b82f6, #2563eb); color: white; text-decoration: none; padding: 0.75rem 1.5rem; border-radius: 8px; font-weight: 500; }
390
- </style>
391
- </head>
392
- <body>
393
- <div class="container">
394
- <div class="error-code">404</div>
395
- <h1>Page Not Found</h1>
396
- <p class="message">The page you're looking for doesn't exist.</p>
397
- <a href="/">← Go Home</a>
398
- </div>
399
- </body>
400
- </html>`
401
- fs.writeFileSync(path.join(outDir, '404.html'), default404HTML)
402
- console.log('📦 Generated 404.html (default)')
403
- }
404
-
405
- // Summary
406
- console.log('')
407
- console.log('✨ Build complete!')
408
- console.log(` Static pages: ${compiledPages.filter(p => p.analysis.isStatic).length}`)
409
- console.log(` Hydrated pages: ${compiledPages.filter(p => p.analysis.needsHydration).length}`)
410
- console.log(` SSR pages: ${compiledPages.filter(p => p.analysis.needsSSR).length}`)
411
- console.log('')
412
-
413
- // Route manifest
414
- console.log('📍 Routes:')
415
- for (const page of compiledPages.sort((a, b) => b.score - a.score)) {
416
- const type = getBuildOutputType(page.analysis)
417
- console.log(` ${page.routePath.padEnd(20)} → ${page.outputDir}/index.html (${type})`)
418
- }
419
- }
420
-
421
- // Legacy export for backwards compatibility
422
- export { buildSSG as buildSPA }
@@ -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
-