@zenithbuild/core 0.1.0

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 (101) hide show
  1. package/.eslintignore +15 -0
  2. package/.gitattributes +2 -0
  3. package/.github/ISSUE_TEMPLATE/compiler-errors-for-invalid-state-declarations.md +25 -0
  4. package/.github/ISSUE_TEMPLATE/new_ticket.yaml +34 -0
  5. package/.github/pull_request_template.md +15 -0
  6. package/.github/workflows/discord-changelog.yml +141 -0
  7. package/.github/workflows/discord-notify.yml +242 -0
  8. package/.github/workflows/discord-version.yml +195 -0
  9. package/.prettierignore +13 -0
  10. package/.prettierrc +21 -0
  11. package/.zen.d.ts +15 -0
  12. package/LICENSE +21 -0
  13. package/README.md +55 -0
  14. package/app/components/Button.zen +46 -0
  15. package/app/components/Link.zen +11 -0
  16. package/app/favicon.ico +0 -0
  17. package/app/layouts/Main.zen +59 -0
  18. package/app/pages/about.zen +23 -0
  19. package/app/pages/blog/[id].zen +53 -0
  20. package/app/pages/blog/index.zen +32 -0
  21. package/app/pages/dynamic-dx.zen +712 -0
  22. package/app/pages/dynamic-primitives.zen +453 -0
  23. package/app/pages/index.zen +154 -0
  24. package/app/pages/navigation-demo.zen +229 -0
  25. package/app/pages/posts/[...slug].zen +61 -0
  26. package/app/pages/primitives-demo.zen +273 -0
  27. package/assets/logos/0E3B5DDD-605C-4839-BB2E-DFCA8ADC9604.PNG +0 -0
  28. package/assets/logos/760971E5-79A1-44F9-90B9-925DF30F4278.PNG +0 -0
  29. package/assets/logos/8A06ED80-9ED2-4689-BCBD-13B2E95EE8E4.JPG +0 -0
  30. package/assets/logos/C691FF58-ED13-4E8D-B6A3-02E835849340.PNG +0 -0
  31. package/assets/logos/C691FF58-ED13-4E8D-B6A3-02E835849340.svg +601 -0
  32. package/assets/logos/README.md +54 -0
  33. package/assets/logos/zen.icns +0 -0
  34. package/bun.lock +39 -0
  35. package/compiler/README.md +380 -0
  36. package/compiler/errors/compilerError.ts +24 -0
  37. package/compiler/finalize/finalizeOutput.ts +163 -0
  38. package/compiler/finalize/generateFinalBundle.ts +82 -0
  39. package/compiler/index.ts +44 -0
  40. package/compiler/ir/types.ts +83 -0
  41. package/compiler/legacy/binding.ts +254 -0
  42. package/compiler/legacy/bindings.ts +338 -0
  43. package/compiler/legacy/component-process.ts +1208 -0
  44. package/compiler/legacy/component.ts +301 -0
  45. package/compiler/legacy/event.ts +50 -0
  46. package/compiler/legacy/expression.ts +1149 -0
  47. package/compiler/legacy/mutation.ts +280 -0
  48. package/compiler/legacy/parse.ts +299 -0
  49. package/compiler/legacy/split.ts +608 -0
  50. package/compiler/legacy/types.ts +32 -0
  51. package/compiler/output/types.ts +34 -0
  52. package/compiler/parse/detectMapExpressions.ts +102 -0
  53. package/compiler/parse/parseScript.ts +22 -0
  54. package/compiler/parse/parseTemplate.ts +425 -0
  55. package/compiler/parse/parseZenFile.ts +66 -0
  56. package/compiler/parse/trackLoopContext.ts +82 -0
  57. package/compiler/runtime/dataExposure.ts +291 -0
  58. package/compiler/runtime/generateDOM.ts +144 -0
  59. package/compiler/runtime/generateHydrationBundle.ts +383 -0
  60. package/compiler/runtime/hydration.ts +309 -0
  61. package/compiler/runtime/navigation.ts +432 -0
  62. package/compiler/runtime/thinRuntime.ts +160 -0
  63. package/compiler/runtime/transformIR.ts +256 -0
  64. package/compiler/runtime/wrapExpression.ts +84 -0
  65. package/compiler/runtime/wrapExpressionWithLoop.ts +77 -0
  66. package/compiler/spa-build.ts +1000 -0
  67. package/compiler/test/validate-test.ts +104 -0
  68. package/compiler/transform/generateBindings.ts +47 -0
  69. package/compiler/transform/generateHTML.ts +28 -0
  70. package/compiler/transform/transformNode.ts +126 -0
  71. package/compiler/transform/transformTemplate.ts +38 -0
  72. package/compiler/validate/validateExpressions.ts +168 -0
  73. package/core/index.ts +135 -0
  74. package/core/lifecycle/index.ts +49 -0
  75. package/core/lifecycle/zen-mount.ts +182 -0
  76. package/core/lifecycle/zen-unmount.ts +88 -0
  77. package/core/reactivity/index.ts +54 -0
  78. package/core/reactivity/tracking.ts +167 -0
  79. package/core/reactivity/zen-batch.ts +57 -0
  80. package/core/reactivity/zen-effect.ts +139 -0
  81. package/core/reactivity/zen-memo.ts +146 -0
  82. package/core/reactivity/zen-ref.ts +52 -0
  83. package/core/reactivity/zen-signal.ts +121 -0
  84. package/core/reactivity/zen-state.ts +180 -0
  85. package/core/reactivity/zen-untrack.ts +44 -0
  86. package/docs/COMMENTS.md +111 -0
  87. package/docs/COMMITS.md +36 -0
  88. package/docs/CONTRIBUTING.md +116 -0
  89. package/docs/STYLEGUIDE.md +62 -0
  90. package/package.json +44 -0
  91. package/router/index.ts +76 -0
  92. package/router/manifest.ts +314 -0
  93. package/router/navigation/ZenLink.zen +231 -0
  94. package/router/navigation/index.ts +78 -0
  95. package/router/navigation/zen-link.ts +584 -0
  96. package/router/runtime.ts +458 -0
  97. package/router/types.ts +168 -0
  98. package/runtime/build.ts +17 -0
  99. package/runtime/serve.ts +93 -0
  100. package/scripts/webhook-proxy.ts +213 -0
  101. package/tsconfig.json +28 -0
@@ -0,0 +1,104 @@
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
+
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Generate Bindings
3
+ *
4
+ * This module is handled by transformNode, but kept here for future extensibility
5
+ */
6
+
7
+ import type { Binding } from '../output/types'
8
+
9
+ /**
10
+ * Validate bindings structure
11
+ */
12
+ export function validateBindings(bindings: Binding[]): void {
13
+ for (const binding of bindings) {
14
+ if (!binding.id || !binding.type || !binding.target || !binding.expression) {
15
+ throw new Error(`Invalid binding: ${JSON.stringify(binding)}`)
16
+ }
17
+
18
+ if (binding.type !== 'text' && binding.type !== 'attribute') {
19
+ throw new Error(`Invalid binding type: ${binding.type}`)
20
+ }
21
+
22
+ if (binding.type === 'text' && binding.target !== 'data-zen-text') {
23
+ throw new Error(`Text binding must have target 'data-zen-text', got: ${binding.target}`)
24
+ }
25
+
26
+ if (binding.type === 'attribute' && !binding.target.startsWith('data-zen-attr-')) {
27
+ // This is handled in transformNode, but validate here too
28
+ // Actually, the target should be the attribute name (e.g., "class")
29
+ // and we prepend "data-zen-attr-" when generating HTML
30
+ // So this validation is correct
31
+ }
32
+ }
33
+ }
34
+
35
+ /**
36
+ * Sort bindings by location for deterministic output
37
+ */
38
+ export function sortBindings(bindings: Binding[]): Binding[] {
39
+ return [...bindings].sort((a, b) => {
40
+ if (!a.location || !b.location) return 0
41
+ if (a.location.line !== b.location.line) {
42
+ return a.location.line - b.location.line
43
+ }
44
+ return a.location.column - b.location.column
45
+ })
46
+ }
47
+
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Generate Static HTML from Transformed Nodes
3
+ *
4
+ * This generates pure HTML with no expressions or runtime code
5
+ */
6
+
7
+ import type { TemplateNode } from '../ir/types'
8
+ import { transformNode } from './transformNode'
9
+
10
+ /**
11
+ * Generate HTML string from template nodes
12
+ */
13
+ export function generateHTML(
14
+ nodes: TemplateNode[],
15
+ expressions: any[]
16
+ ): { html: string; bindings: any[] } {
17
+ let html = ''
18
+ const allBindings: any[] = []
19
+
20
+ for (const node of nodes) {
21
+ const { html: nodeHtml, bindings } = transformNode(node, expressions)
22
+ html += nodeHtml
23
+ allBindings.push(...bindings)
24
+ }
25
+
26
+ return { html, bindings: allBindings }
27
+ }
28
+
@@ -0,0 +1,126 @@
1
+ /**
2
+ * Transform Template Nodes
3
+ *
4
+ * Transforms IR nodes into HTML strings and collects bindings
5
+ */
6
+
7
+ import type { TemplateNode, ElementNode, TextNode, ExpressionNode, ExpressionIR, LoopContext } from '../ir/types'
8
+ import type { Binding } from '../output/types'
9
+
10
+ let bindingIdCounter = 0
11
+
12
+ function generateBindingId(): string {
13
+ return `exp_${bindingIdCounter++}`
14
+ }
15
+
16
+ /**
17
+ * Transform a template node to HTML and collect bindings
18
+ * Phase 7: Supports loop context propagation for map expressions
19
+ */
20
+ export function transformNode(
21
+ node: TemplateNode,
22
+ expressions: ExpressionIR[],
23
+ parentLoopContext?: LoopContext // Phase 7: Loop context from parent map expressions
24
+ ): { html: string; bindings: Binding[] } {
25
+ const bindings: Binding[] = []
26
+
27
+ function transform(node: TemplateNode, loopContext?: LoopContext): string {
28
+ switch (node.type) {
29
+ case 'text':
30
+ return escapeHtml((node as TextNode).value)
31
+
32
+ case 'expression': {
33
+ const exprNode = node as ExpressionNode
34
+ // Find the expression in the expressions array
35
+ const expr = expressions.find(e => e.id === exprNode.expression)
36
+ if (!expr) {
37
+ throw new Error(`Expression ${exprNode.expression} not found`)
38
+ }
39
+
40
+ const bindingId = generateBindingId()
41
+ // Phase 7: Use loop context from ExpressionNode if available, otherwise use passed context
42
+ const activeLoopContext = exprNode.loopContext || loopContext
43
+
44
+ bindings.push({
45
+ id: bindingId,
46
+ type: 'text',
47
+ target: 'data-zen-text',
48
+ expression: expr.code,
49
+ location: expr.location,
50
+ loopContext: activeLoopContext // Phase 7: Attach loop context to binding
51
+ })
52
+
53
+ return `<span data-zen-text="${bindingId}"></span>`
54
+ }
55
+
56
+ case 'element': {
57
+ const elNode = node as ElementNode
58
+ const tag = elNode.tag
59
+
60
+ // Build attributes
61
+ const attrs: string[] = []
62
+ for (const attr of elNode.attributes) {
63
+ if (typeof attr.value === 'string') {
64
+ // Static attribute
65
+ const value = escapeHtml(attr.value)
66
+ attrs.push(`${attr.name}="${value}"`)
67
+ } else {
68
+ // Expression attribute
69
+ const expr = attr.value as ExpressionIR
70
+ const bindingId = generateBindingId()
71
+ // Phase 7: Use loop context from AttributeIR if available, otherwise use element's loop context
72
+ const activeLoopContext = attr.loopContext || loopContext
73
+
74
+ bindings.push({
75
+ id: bindingId,
76
+ type: 'attribute',
77
+ target: attr.name, // e.g., "class", "style"
78
+ expression: expr.code,
79
+ location: expr.location,
80
+ loopContext: activeLoopContext // Phase 7: Attach loop context to binding
81
+ })
82
+
83
+ // Use data-zen-attr-{name} for attribute expressions
84
+ attrs.push(`data-zen-attr-${attr.name}="${bindingId}"`)
85
+ }
86
+ }
87
+
88
+ const attrStr = attrs.length > 0 ? ' ' + attrs.join(' ') : ''
89
+
90
+ // Phase 7: Use loop context from ElementNode if available, otherwise use passed context
91
+ const activeLoopContext = elNode.loopContext || loopContext
92
+
93
+ // Transform children
94
+ const childrenHtml = elNode.children.map(child => transform(child, activeLoopContext)).join('')
95
+
96
+ // Self-closing tags
97
+ const voidElements = new Set([
98
+ 'area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input',
99
+ 'link', 'meta', 'param', 'source', 'track', 'wbr'
100
+ ])
101
+
102
+ if (voidElements.has(tag.toLowerCase()) && childrenHtml === '') {
103
+ return `<${tag}${attrStr} />`
104
+ }
105
+
106
+ return `<${tag}${attrStr}>${childrenHtml}</${tag}>`
107
+ }
108
+ }
109
+ }
110
+
111
+ const html = transform(node, parentLoopContext)
112
+ return { html, bindings }
113
+ }
114
+
115
+ /**
116
+ * Escape HTML special characters
117
+ */
118
+ function escapeHtml(text: string): string {
119
+ return text
120
+ .replace(/&/g, '&amp;')
121
+ .replace(/</g, '&lt;')
122
+ .replace(/>/g, '&gt;')
123
+ .replace(/"/g, '&quot;')
124
+ .replace(/'/g, '&#39;')
125
+ }
126
+
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Transform Template IR to Compiled Template
3
+ *
4
+ * Phase 2: Transform IR → Static HTML + Runtime Bindings
5
+ */
6
+
7
+ import type { ZenIR } from '../ir/types'
8
+ import type { CompiledTemplate } from '../output/types'
9
+ import { generateHTML } from './generateHTML'
10
+ import { validateBindings, sortBindings } from './generateBindings'
11
+
12
+ /**
13
+ * Transform a ZenIR into CompiledTemplate
14
+ */
15
+ export function transformTemplate(ir: ZenIR): CompiledTemplate {
16
+ // Generate HTML and collect bindings
17
+ const { html, bindings } = generateHTML(ir.template.nodes, ir.template.expressions)
18
+
19
+ // Validate bindings
20
+ validateBindings(bindings)
21
+
22
+ // Sort bindings by location for deterministic output
23
+ const sortedBindings = sortBindings(bindings)
24
+
25
+ // Extract scripts (raw content, pass through)
26
+ const scripts = ir.script ? ir.script.raw : null
27
+
28
+ // Extract styles (raw content, pass through)
29
+ const styles = ir.styles.map(s => s.raw)
30
+
31
+ return {
32
+ html,
33
+ bindings: sortedBindings,
34
+ scripts,
35
+ styles
36
+ }
37
+ }
38
+
@@ -0,0 +1,168 @@
1
+ /**
2
+ * Expression Validation
3
+ *
4
+ * Phase 8/9/10: Compile-time validation of all expressions
5
+ *
6
+ * Ensures all expressions are valid JavaScript and will not cause runtime errors.
7
+ * Build fails immediately if any expression is invalid.
8
+ */
9
+
10
+ import type { ExpressionIR } from '../ir/types'
11
+ import { CompilerError } from '../errors/compilerError'
12
+
13
+ /**
14
+ * Validation result
15
+ */
16
+ export interface ValidationResult {
17
+ valid: boolean
18
+ errors: CompilerError[]
19
+ }
20
+
21
+ /**
22
+ * Validate all expressions in the IR
23
+ *
24
+ * @param expressions - Array of expressions to validate
25
+ * @param filePath - Source file path for error reporting
26
+ * @returns Validation result with errors
27
+ */
28
+ export function validateExpressions(
29
+ expressions: ExpressionIR[],
30
+ filePath: string
31
+ ): ValidationResult {
32
+ const errors: CompilerError[] = []
33
+
34
+ for (const expr of expressions) {
35
+ const exprErrors = validateSingleExpression(expr, filePath)
36
+ errors.push(...exprErrors)
37
+ }
38
+
39
+ return {
40
+ valid: errors.length === 0,
41
+ errors
42
+ }
43
+ }
44
+
45
+ /**
46
+ * Validate a single expression
47
+ */
48
+ function validateSingleExpression(
49
+ expr: ExpressionIR,
50
+ filePath: string
51
+ ): CompilerError[] {
52
+ const errors: CompilerError[] = []
53
+ const { id, code, location } = expr
54
+
55
+ // Basic syntax validation using a safe approach
56
+ // We don't execute the code, just validate syntax
57
+ // Note: Expressions may contain JSX/HTML syntax (e.g., condition && <element>)
58
+ // which is not valid JavaScript but is valid in our expression language.
59
+ // We skip strict JavaScript validation for expressions that contain JSX.
60
+
61
+ const hasJSX = /<[a-zA-Z]/.test(code) || /\/>/.test(code)
62
+
63
+ if (!hasJSX) {
64
+ // Only validate JavaScript syntax if there's no JSX
65
+ // Note: Using Function constructor here is for syntax validation only (compile-time)
66
+ // This is safe because:
67
+ // 1. It's only called at compile time, not runtime
68
+ // 2. We're only checking syntax, not executing the code
69
+ // 3. The actual runtime uses pre-compiled functions, not Function constructor
70
+ try {
71
+ // Use Function constructor to validate syntax (doesn't execute)
72
+ // This is compile-time only - runtime never uses Function constructor
73
+ new Function('state', 'loaderData', 'props', 'stores', `return ${code}`)
74
+ } catch (error: any) {
75
+ errors.push(
76
+ new CompilerError(
77
+ `Invalid expression syntax: ${code}\n${error.message}`,
78
+ filePath,
79
+ location.line,
80
+ location.column
81
+ )
82
+ )
83
+ }
84
+ }
85
+ // If hasJSX, we skip JavaScript validation - JSX syntax is handled by the parser/runtime
86
+
87
+ // Check for dangerous patterns
88
+ if (code.includes('eval(') || code.includes('Function(') || code.includes('with (')) {
89
+ errors.push(
90
+ new CompilerError(
91
+ `Expression contains unsafe code: ${code}`,
92
+ filePath,
93
+ location.line,
94
+ location.column
95
+ )
96
+ )
97
+ }
98
+
99
+ // Check for undefined global references (basic heuristic)
100
+ // This is a simple check - can be enhanced with AST parsing
101
+ const globalPattern = /\b(window|document|console|globalThis)\./g
102
+ const matches = code.match(globalPattern)
103
+ if (matches && matches.length > 0) {
104
+ // Warn but don't fail - some global access might be intentional
105
+ // In a stricter mode, we could fail here
106
+ }
107
+
108
+ // Check for common syntax errors
109
+ const openBraces = (code.match(/\{/g) || []).length
110
+ const closeBraces = (code.match(/\}/g) || []).length
111
+ const openParens = (code.match(/\(/g) || []).length
112
+ const closeParens = (code.match(/\)/g) || []).length
113
+ const openBrackets = (code.match(/\[/g) || []).length
114
+ const closeBrackets = (code.match(/\]/g) || []).length
115
+
116
+ if (openBraces !== closeBraces) {
117
+ errors.push(
118
+ new CompilerError(
119
+ `Mismatched braces in expression: ${code}`,
120
+ filePath,
121
+ location.line,
122
+ location.column
123
+ )
124
+ )
125
+ }
126
+
127
+ if (openParens !== closeParens) {
128
+ errors.push(
129
+ new CompilerError(
130
+ `Mismatched parentheses in expression: ${code}`,
131
+ filePath,
132
+ location.line,
133
+ location.column
134
+ )
135
+ )
136
+ }
137
+
138
+ if (openBrackets !== closeBrackets) {
139
+ errors.push(
140
+ new CompilerError(
141
+ `Mismatched brackets in expression: ${code}`,
142
+ filePath,
143
+ location.line,
144
+ location.column
145
+ )
146
+ )
147
+ }
148
+
149
+ return errors
150
+ }
151
+
152
+ /**
153
+ * Validate and throw if invalid
154
+ *
155
+ * @throws CompilerError if any expression is invalid
156
+ */
157
+ export function validateExpressionsOrThrow(
158
+ expressions: ExpressionIR[],
159
+ filePath: string
160
+ ): void {
161
+ const result = validateExpressions(expressions, filePath)
162
+
163
+ if (!result.valid && result.errors.length > 0) {
164
+ // Throw the first error (can be enhanced to collect all)
165
+ throw result.errors[0]
166
+ }
167
+ }
168
+
package/core/index.ts ADDED
@@ -0,0 +1,135 @@
1
+ /**
2
+ * Zenith Core Runtime
3
+ *
4
+ * This is the foundational layer of the Zenith framework, providing:
5
+ * - Reactive primitives (signals, state, effects, memos)
6
+ * - Lifecycle hooks (onMount, onUnmount)
7
+ *
8
+ * Design principles:
9
+ * - Auto-tracked reactivity (no dependency arrays)
10
+ * - No VDOM or render loops
11
+ * - Runtime-agnostic (works in browser, SSR, tests)
12
+ * - Hybrid naming: internal `zen*` + public clean names
13
+ *
14
+ * @example
15
+ * ```ts
16
+ * // Using clean names (recommended for application code)
17
+ * import { signal, effect, onMount } from 'zenith/core'
18
+ *
19
+ * const count = signal(0)
20
+ *
21
+ * effect(() => {
22
+ * console.log('Count:', count())
23
+ * })
24
+ *
25
+ * onMount(() => {
26
+ * console.log('Mounted!')
27
+ * })
28
+ * ```
29
+ *
30
+ * @example
31
+ * ```ts
32
+ * // Using explicit zen* names (for library/internal code)
33
+ * import { zenSignal, zenEffect, zenOnMount } from 'zenith/core'
34
+ *
35
+ * const count = zenSignal(0)
36
+ * zenEffect(() => console.log(count()))
37
+ * zenOnMount(() => console.log('Ready'))
38
+ * ```
39
+ *
40
+ * @example
41
+ * ```ts
42
+ * // For navigation, import from router
43
+ * import { navigate, isActive } from 'zenith/router'
44
+ *
45
+ * navigate('/about')
46
+ * if (isActive('/blog')) {
47
+ * // Handle active state
48
+ * }
49
+ * ```
50
+ */
51
+
52
+ // ============================================
53
+ // Reactivity Primitives
54
+ // ============================================
55
+
56
+ // Explicit zen* exports (internal naming)
57
+ export {
58
+ zenSignal,
59
+ zenState,
60
+ zenEffect,
61
+ zenMemo,
62
+ zenRef,
63
+ zenBatch,
64
+ zenUntrack
65
+ } from './reactivity'
66
+
67
+ // Types
68
+ export type {
69
+ Signal,
70
+ Memo,
71
+ Ref,
72
+ EffectFn,
73
+ DisposeFn,
74
+ Subscriber,
75
+ TrackingContext
76
+ } from './reactivity'
77
+
78
+ // Clean name exports (public DX)
79
+ export {
80
+ signal,
81
+ state,
82
+ effect,
83
+ memo,
84
+ ref,
85
+ batch,
86
+ untrack
87
+ } from './reactivity'
88
+
89
+ // Internal tracking utilities (advanced use)
90
+ export {
91
+ trackDependency,
92
+ notifySubscribers,
93
+ getCurrentContext,
94
+ pushContext,
95
+ popContext,
96
+ cleanupContext,
97
+ runUntracked,
98
+ startBatch,
99
+ endBatch,
100
+ isBatching
101
+ } from './reactivity'
102
+
103
+ // ============================================
104
+ // Lifecycle Hooks
105
+ // ============================================
106
+
107
+ // Explicit zen* exports (internal naming)
108
+ export {
109
+ zenOnMount,
110
+ zenOnUnmount
111
+ } from './lifecycle'
112
+
113
+ // Clean name exports (public DX)
114
+ export {
115
+ onMount,
116
+ onUnmount
117
+ } from './lifecycle'
118
+
119
+ // Types
120
+ export type {
121
+ MountCallback,
122
+ UnmountCallback
123
+ } from './lifecycle'
124
+
125
+ // Internal lifecycle utilities (for component system)
126
+ export {
127
+ triggerMount,
128
+ triggerUnmount,
129
+ executeUnmountCallbacks,
130
+ getIsMounted,
131
+ getUnmountCallbackCount,
132
+ resetMountState,
133
+ resetUnmountState
134
+ } from './lifecycle'
135
+
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Zenith Lifecycle Hooks
3
+ *
4
+ * This module exports lifecycle hooks for component mount/unmount events.
5
+ * These are effect wrappers that integrate with the component lifecycle system.
6
+ *
7
+ * Exports both explicit `zen*` names (internal) and clean aliases (public DX).
8
+ */
9
+
10
+ // Import lifecycle hooks
11
+ import {
12
+ zenOnMount as _zenOnMount,
13
+ triggerMount,
14
+ triggerUnmount,
15
+ getIsMounted,
16
+ resetMountState,
17
+ type MountCallback
18
+ } from './zen-mount'
19
+
20
+ import {
21
+ zenOnUnmount as _zenOnUnmount,
22
+ executeUnmountCallbacks,
23
+ getUnmountCallbackCount,
24
+ resetUnmountState,
25
+ type UnmountCallback
26
+ } from './zen-unmount'
27
+
28
+ // Re-export with explicit names
29
+ export const zenOnMount = _zenOnMount
30
+ export const zenOnUnmount = _zenOnUnmount
31
+
32
+ // Re-export utilities
33
+ export {
34
+ triggerMount,
35
+ triggerUnmount,
36
+ getIsMounted,
37
+ resetMountState,
38
+ executeUnmountCallbacks,
39
+ getUnmountCallbackCount,
40
+ resetUnmountState
41
+ }
42
+
43
+ // Re-export types
44
+ export type { MountCallback, UnmountCallback }
45
+
46
+ // Public DX aliases - clean names
47
+ export const onMount = _zenOnMount
48
+ export const onUnmount = _zenOnUnmount
49
+