@zenithbuild/core 0.4.7 → 0.5.0-beta.2.1

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 (110) hide show
  1. package/CORE_CONTRACT.md +143 -0
  2. package/README.md +11 -31
  3. package/bin/zenith.js +68 -0
  4. package/package.json +40 -52
  5. package/src/config.js +134 -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 -335
  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 -82
  43. package/compiler/ir/types.ts +0 -162
  44. package/compiler/output/types.ts +0 -34
  45. package/compiler/parse/detectMapExpressions.ts +0 -102
  46. package/compiler/parse/parseScript.ts +0 -46
  47. package/compiler/parse/parseTemplate.ts +0 -599
  48. package/compiler/parse/parseZenFile.ts +0 -66
  49. package/compiler/parse/scriptAnalysis.ts +0 -91
  50. package/compiler/parse/trackLoopContext.ts +0 -82
  51. package/compiler/runtime/dataExposure.ts +0 -317
  52. package/compiler/runtime/generateDOM.ts +0 -246
  53. package/compiler/runtime/generateHydrationBundle.ts +0 -407
  54. package/compiler/runtime/hydration.ts +0 -309
  55. package/compiler/runtime/navigation.ts +0 -432
  56. package/compiler/runtime/thinRuntime.ts +0 -160
  57. package/compiler/runtime/transformIR.ts +0 -363
  58. package/compiler/runtime/wrapExpression.ts +0 -95
  59. package/compiler/runtime/wrapExpressionWithLoop.ts +0 -83
  60. package/compiler/spa-build.ts +0 -917
  61. package/compiler/ssg-build.ts +0 -422
  62. package/compiler/test/validate-test.ts +0 -104
  63. package/compiler/transform/classifyExpression.ts +0 -444
  64. package/compiler/transform/componentResolver.ts +0 -312
  65. package/compiler/transform/componentScriptTransformer.ts +0 -147
  66. package/compiler/transform/expressionTransformer.ts +0 -385
  67. package/compiler/transform/fragmentLowering.ts +0 -634
  68. package/compiler/transform/generateBindings.ts +0 -47
  69. package/compiler/transform/generateHTML.ts +0 -28
  70. package/compiler/transform/layoutProcessor.ts +0 -132
  71. package/compiler/transform/slotResolver.ts +0 -292
  72. package/compiler/transform/transformNode.ts +0 -126
  73. package/compiler/transform/transformTemplate.ts +0 -38
  74. package/compiler/validate/invariants.ts +0 -292
  75. package/compiler/validate/validateExpressions.ts +0 -168
  76. package/core/config/index.ts +0 -16
  77. package/core/config/loader.ts +0 -69
  78. package/core/config/types.ts +0 -89
  79. package/core/index.ts +0 -135
  80. package/core/lifecycle/index.ts +0 -49
  81. package/core/lifecycle/zen-mount.ts +0 -182
  82. package/core/lifecycle/zen-unmount.ts +0 -88
  83. package/core/plugins/index.ts +0 -7
  84. package/core/plugins/registry.ts +0 -81
  85. package/core/reactivity/index.ts +0 -54
  86. package/core/reactivity/tracking.ts +0 -167
  87. package/core/reactivity/zen-batch.ts +0 -57
  88. package/core/reactivity/zen-effect.ts +0 -139
  89. package/core/reactivity/zen-memo.ts +0 -146
  90. package/core/reactivity/zen-ref.ts +0 -52
  91. package/core/reactivity/zen-signal.ts +0 -121
  92. package/core/reactivity/zen-state.ts +0 -180
  93. package/core/reactivity/zen-untrack.ts +0 -44
  94. package/dist/cli.js +0 -11659
  95. package/dist/zen-build.js +0 -15633
  96. package/dist/zen-dev.js +0 -15633
  97. package/dist/zen-preview.js +0 -15633
  98. package/dist/zenith.js +0 -15633
  99. package/router/index.ts +0 -76
  100. package/router/manifest.ts +0 -314
  101. package/router/navigation/ZenLink.zen +0 -231
  102. package/router/navigation/index.ts +0 -78
  103. package/router/navigation/zen-link.ts +0 -584
  104. package/router/runtime.ts +0 -458
  105. package/router/types.ts +0 -168
  106. package/runtime/build.ts +0 -17
  107. package/runtime/bundle-generator.ts +0 -943
  108. package/runtime/client-runtime.ts +0 -549
  109. package/runtime/serve.ts +0 -93
  110. package/tsconfig.json +0 -28
package/src/hash.js ADDED
@@ -0,0 +1,52 @@
1
+ // ---------------------------------------------------------------------------
2
+ // hash.js — Zenith Core V0
3
+ // ---------------------------------------------------------------------------
4
+ // Deterministic content hashing. SHA-256, hex output.
5
+ //
6
+ // CRITICAL: This algorithm must match the bundler's hashing exactly.
7
+ // If bundler changes hash algorithm, core must change in lockstep.
8
+ //
9
+ // Input normalization:
10
+ // - Path separators → /
11
+ // - Trailing newlines stripped
12
+ // ---------------------------------------------------------------------------
13
+
14
+ import { createHash } from 'node:crypto';
15
+
16
+ /**
17
+ * Hash a string using SHA-256. Returns hex digest.
18
+ *
19
+ * @param {string} content
20
+ * @returns {string}
21
+ */
22
+ export function hash(content) {
23
+ const normalized = _normalizeInput(content);
24
+ return createHash('sha256').update(normalized).digest('hex');
25
+ }
26
+
27
+ /**
28
+ * Hash a string and return a truncated hash (first N characters).
29
+ * Useful for content-hashed filenames.
30
+ *
31
+ * @param {string} content
32
+ * @param {number} [length=8]
33
+ * @returns {string}
34
+ */
35
+ export function hashShort(content, length = 8) {
36
+ return hash(content).slice(0, length);
37
+ }
38
+
39
+ /**
40
+ * Normalize input for deterministic hashing.
41
+ * - Replaces backslashes with forward slashes
42
+ * - Strips trailing newlines
43
+ *
44
+ * @param {string} content
45
+ * @returns {string}
46
+ */
47
+ function _normalizeInput(content) {
48
+ let result = content.replace(/\\/g, '/');
49
+ // Strip trailing newlines
50
+ result = result.replace(/\n+$/, '');
51
+ return result;
52
+ }
package/src/index.js ADDED
@@ -0,0 +1,26 @@
1
+ // ---------------------------------------------------------------------------
2
+ // index.js — Zenith Core V0
3
+ // ---------------------------------------------------------------------------
4
+ // Public API. Re-exports frozen module namespaces only.
5
+ // ---------------------------------------------------------------------------
6
+
7
+ export { validateConfig, loadConfig, getDefaults } from './config.js';
8
+ export {
9
+ normalizeSeparators,
10
+ fileToRoute,
11
+ extractParams,
12
+ isDynamic,
13
+ validateRouteParams,
14
+ canonicalize
15
+ } from './path.js';
16
+ export { sortRoutes, sortAlpha, isCorrectOrder } from './order.js';
17
+ export { hash, hashShort } from './hash.js';
18
+ export { createError, formatError, isZenithError, ErrorCodes } from './errors.js';
19
+ export { parseSemver, formatVersion, validateCompatibility } from './version.js';
20
+ export { IR_VERSION, IR_SCHEMA } from './schema.js';
21
+ export { coreModuleSource } from './core-template.js';
22
+ export {
23
+ containsForbiddenPattern,
24
+ FORBIDDEN_PATTERNS,
25
+ BROWSER_GLOBALS
26
+ } from './guards.js';
@@ -0,0 +1 @@
1
+ export { IR_VERSION, IR_SCHEMA } from '../schema.js';
package/src/order.js ADDED
@@ -0,0 +1,69 @@
1
+ // ---------------------------------------------------------------------------
2
+ // order.js — Zenith Core V0
3
+ // ---------------------------------------------------------------------------
4
+ // Deterministic ordering utilities. Pure transforms.
5
+ //
6
+ // Sorting contract:
7
+ // 1. Static routes first, dynamic routes second
8
+ // 2. Alphabetical tiebreak within each group
9
+ // 3. Stable across runs
10
+ // ---------------------------------------------------------------------------
11
+
12
+ /**
13
+ * Check if a route path contains dynamic segments (starts with :).
14
+ *
15
+ * @param {string} routePath
16
+ * @returns {boolean}
17
+ */
18
+ function _isDynamic(routePath) {
19
+ return routePath.includes(':');
20
+ }
21
+
22
+ /**
23
+ * Sort route entries deterministically.
24
+ *
25
+ * Rules:
26
+ * 1. Static routes before dynamic routes
27
+ * 2. Alphabetical (lexicographic) within each group
28
+ *
29
+ * Entries must have a `path` property.
30
+ *
31
+ * @param {{ path: string }[]} entries
32
+ * @returns {{ path: string }[]}
33
+ */
34
+ export function sortRoutes(entries) {
35
+ return [...entries].sort((a, b) => {
36
+ const aDynamic = _isDynamic(a.path);
37
+ const bDynamic = _isDynamic(b.path);
38
+
39
+ // Static before dynamic
40
+ if (!aDynamic && bDynamic) return -1;
41
+ if (aDynamic && !bDynamic) return 1;
42
+
43
+ // Alphabetical tiebreak
44
+ return a.path.localeCompare(b.path);
45
+ });
46
+ }
47
+
48
+ /**
49
+ * Sort an array of strings deterministically.
50
+ * Simple alphabetical sort with stable ordering.
51
+ *
52
+ * @param {string[]} items
53
+ * @returns {string[]}
54
+ */
55
+ export function sortAlpha(items) {
56
+ return [...items].sort((a, b) => a.localeCompare(b));
57
+ }
58
+
59
+ /**
60
+ * Check if an array of route entries is in correct deterministic order.
61
+ * Does not throw — returns boolean.
62
+ *
63
+ * @param {{ path: string }[]} entries
64
+ * @returns {boolean}
65
+ */
66
+ export function isCorrectOrder(entries) {
67
+ const sorted = sortRoutes(entries);
68
+ return entries.every((entry, i) => entry.path === sorted[i].path);
69
+ }
package/src/path.js ADDED
@@ -0,0 +1,131 @@
1
+ // ---------------------------------------------------------------------------
2
+ // path.js — Zenith Core V0
3
+ // ---------------------------------------------------------------------------
4
+ // Path normalization utilities. Pure transforms — no filesystem access.
5
+ //
6
+ // - Normalizes separators (\ → /)
7
+ // - Converts [param] → :param
8
+ // - Extracts params from route strings
9
+ // - Validates no repeated param names
10
+ // - Strips file extensions
11
+ // ---------------------------------------------------------------------------
12
+
13
+ /**
14
+ * Normalize path separators to forward slashes.
15
+ *
16
+ * @param {string} filePath
17
+ * @returns {string}
18
+ */
19
+ export function normalizeSeparators(filePath) {
20
+ return filePath.replace(/\\/g, '/');
21
+ }
22
+
23
+ /**
24
+ * Convert a file-system path to a route path.
25
+ *
26
+ * Rules:
27
+ * - Normalize separators
28
+ * - Strip the extension
29
+ * - Convert [param] → :param
30
+ * - index files → parent path
31
+ * - Ensure leading /
32
+ *
33
+ * @param {string} filePath Relative path from pages dir (e.g. "users/[id].zen")
34
+ * @param {string} [extension='.zen']
35
+ * @returns {string}
36
+ */
37
+ export function fileToRoute(filePath, extension = '.zen') {
38
+ let route = normalizeSeparators(filePath);
39
+
40
+ // Strip extension
41
+ if (route.endsWith(extension)) {
42
+ route = route.slice(0, -extension.length);
43
+ }
44
+
45
+ // Convert [param] → :param
46
+ route = route.replace(/\[([^\]]+)\]/g, ':$1');
47
+
48
+ // Handle index → parent
49
+ if (route === 'index') {
50
+ return '/';
51
+ }
52
+ if (route.endsWith('/index')) {
53
+ route = route.slice(0, -'/index'.length);
54
+ }
55
+
56
+ // Ensure leading /
57
+ if (!route.startsWith('/')) {
58
+ route = '/' + route;
59
+ }
60
+
61
+ return route;
62
+ }
63
+
64
+ /**
65
+ * Extract parameter names from a route path.
66
+ *
67
+ * @param {string} routePath e.g. "/users/:id/posts/:postId"
68
+ * @returns {string[]} e.g. ["id", "postId"]
69
+ */
70
+ export function extractParams(routePath) {
71
+ const params = [];
72
+ const segments = routePath.split('/');
73
+ for (const segment of segments) {
74
+ if (segment.startsWith(':')) {
75
+ params.push(segment.slice(1));
76
+ }
77
+ }
78
+ return params;
79
+ }
80
+
81
+ /**
82
+ * Check if a route path contains dynamic segments.
83
+ *
84
+ * @param {string} routePath
85
+ * @returns {boolean}
86
+ */
87
+ export function isDynamic(routePath) {
88
+ return routePath.includes(':');
89
+ }
90
+
91
+ /**
92
+ * Validate that a route path has no repeated parameter names.
93
+ * Throws on violation.
94
+ *
95
+ * @param {string} routePath
96
+ */
97
+ export function validateRouteParams(routePath) {
98
+ const params = extractParams(routePath);
99
+ const seen = new Set();
100
+ for (const param of params) {
101
+ if (seen.has(param)) {
102
+ throw new Error(
103
+ `[Zenith:Path] Repeated parameter name ":${param}" in route "${routePath}"`
104
+ );
105
+ }
106
+ seen.add(param);
107
+ }
108
+ }
109
+
110
+ /**
111
+ * Canonicalize a route path.
112
+ * Strips trailing slashes (except root), normalizes separators.
113
+ *
114
+ * @param {string} routePath
115
+ * @returns {string}
116
+ */
117
+ export function canonicalize(routePath) {
118
+ let path = normalizeSeparators(routePath);
119
+
120
+ // Remove trailing slash (unless root)
121
+ if (path.length > 1 && path.endsWith('/')) {
122
+ path = path.slice(0, -1);
123
+ }
124
+
125
+ // Ensure leading slash
126
+ if (!path.startsWith('/')) {
127
+ path = '/' + path;
128
+ }
129
+
130
+ return path;
131
+ }
package/src/schema.js ADDED
@@ -0,0 +1,28 @@
1
+ // ---------------------------------------------------------------------------
2
+ // schema.js — Zenith Core IR authority
3
+ // ---------------------------------------------------------------------------
4
+
5
+ export const IR_VERSION = 1;
6
+
7
+ export const IR_SCHEMA = Object.freeze({
8
+ version: IR_VERSION,
9
+ requiredTopLevelKeys: Object.freeze([
10
+ 'ir_version',
11
+ 'graph_hash',
12
+ 'graph_edges',
13
+ 'graph_nodes',
14
+ 'html',
15
+ 'expressions',
16
+ 'hoisted',
17
+ 'components_scripts',
18
+ 'component_instances',
19
+ 'imports',
20
+ 'modules',
21
+ 'prerender',
22
+ 'signals',
23
+ 'expression_bindings',
24
+ 'marker_bindings',
25
+ 'event_bindings',
26
+ 'style_blocks'
27
+ ])
28
+ });
package/src/version.js ADDED
@@ -0,0 +1,67 @@
1
+ // ---------------------------------------------------------------------------
2
+ // version.js — Zenith Core V0
3
+ // ---------------------------------------------------------------------------
4
+ // SemVer parsing and compatibility validation.
5
+ //
6
+ // Direction of control: Other layers call core's version utility.
7
+ // Core never imports other packages to auto-check.
8
+ // ---------------------------------------------------------------------------
9
+
10
+ /**
11
+ * Parse a SemVer string into components.
12
+ *
13
+ * @param {string} version e.g. "1.2.3"
14
+ * @returns {{ major: number, minor: number, patch: number }}
15
+ */
16
+ export function parseSemver(version) {
17
+ const cleaned = version.replace(/^v/, '');
18
+ const match = cleaned.match(/^(\d+)\.(\d+)\.(\d+)/);
19
+ if (!match) {
20
+ throw new Error(`[Zenith:Version] Invalid semver: "${version}"`);
21
+ }
22
+ return {
23
+ major: parseInt(match[1], 10),
24
+ minor: parseInt(match[2], 10),
25
+ patch: parseInt(match[3], 10)
26
+ };
27
+ }
28
+
29
+ /**
30
+ * Format a version object back to a string.
31
+ *
32
+ * @param {{ major: number, minor: number, patch: number }} ver
33
+ * @returns {string}
34
+ */
35
+ export function formatVersion(ver) {
36
+ return `${ver.major}.${ver.minor}.${ver.patch}`;
37
+ }
38
+
39
+ /**
40
+ * Validate compatibility between two versions.
41
+ *
42
+ * Rules:
43
+ * - Major versions must match (throws if different)
44
+ * - Minor version difference > 1 produces a warning (returns warning string)
45
+ * - Otherwise returns null (compatible)
46
+ *
47
+ * @param {string} coreVersion
48
+ * @param {string} otherVersion
49
+ * @returns {string|null} Warning message or null if fully compatible
50
+ */
51
+ export function validateCompatibility(coreVersion, otherVersion) {
52
+ const core = parseSemver(coreVersion);
53
+ const other = parseSemver(otherVersion);
54
+
55
+ if (core.major !== other.major) {
56
+ throw new Error(
57
+ `[Zenith:Version] Incompatible major versions: core=${formatVersion(core)}, other=${formatVersion(other)}`
58
+ );
59
+ }
60
+
61
+ const minorDiff = Math.abs(core.minor - other.minor);
62
+ if (minorDiff > 1) {
63
+ return `[Zenith:Version] Minor version drift: core=${formatVersion(core)}, other=${formatVersion(other)} (diff=${minorDiff})`;
64
+ }
65
+
66
+ return null;
67
+ }
package/bin/zen-build.ts DELETED
@@ -1,2 +0,0 @@
1
- import { runCLI } from '../cli/main'
2
- runCLI({ defaultCommand: 'build' })
package/bin/zen-dev.ts DELETED
@@ -1,2 +0,0 @@
1
- import { runCLI } from '../cli/main'
2
- runCLI({ defaultCommand: 'dev' })
@@ -1,2 +0,0 @@
1
- import { runCLI } from '../cli/main'
2
- runCLI({ defaultCommand: 'preview' })
package/bin/zenith.ts DELETED
@@ -1,2 +0,0 @@
1
- import { runCLI } from '../cli/main'
2
- runCLI()
@@ -1,37 +0,0 @@
1
- /**
2
- * @zenith/cli - Add Command
3
- *
4
- * Registers a plugin in the project
5
- */
6
-
7
- import { requireProject } from '../utils/project'
8
- import { addPlugin, hasPlugin } from '../utils/plugin-manager'
9
- import * as logger from '../utils/logger'
10
-
11
- export interface AddOptions {
12
- options?: Record<string, unknown>
13
- }
14
-
15
- export async function add(pluginName: string, options: AddOptions = {}): Promise<void> {
16
- requireProject()
17
-
18
- logger.header('Add Plugin')
19
-
20
- if (!pluginName) {
21
- logger.error('Plugin name required. Usage: zenith add <plugin>')
22
- process.exit(1)
23
- }
24
-
25
- if (hasPlugin(pluginName)) {
26
- logger.warn(`Plugin "${pluginName}" is already registered`)
27
- return
28
- }
29
-
30
- const success = addPlugin(pluginName, options.options)
31
-
32
- if (success) {
33
- logger.info(`Plugin "${pluginName}" has been registered.`)
34
- logger.info('Note: You may need to install the package manually:')
35
- logger.log(` bun add @zenith/plugin-${pluginName}`)
36
- }
37
- }
@@ -1,37 +0,0 @@
1
- /**
2
- * @zenith/cli - Build Command
3
- *
4
- * Builds the application for production using SSG.
5
- */
6
-
7
- import path from 'path'
8
- import { requireProject } from '../utils/project'
9
- import * as logger from '../utils/logger'
10
- import { buildSSG } from '../../compiler/ssg-build'
11
-
12
- export interface BuildOptions {
13
- outDir?: string
14
- }
15
-
16
- export async function build(options: BuildOptions = {}): Promise<void> {
17
- const project = requireProject()
18
- const outDir = options.outDir || project.distDir
19
-
20
- logger.header('Zenith Build')
21
- logger.log(`Source: ${project.pagesDir}`)
22
- logger.log(`Output: ${outDir}`)
23
-
24
- try {
25
- buildSSG({
26
- pagesDir: project.pagesDir,
27
- outDir: outDir,
28
- baseDir: project.root
29
- })
30
- logger.success('Build complete!')
31
-
32
- } catch (err: unknown) {
33
- const message = err instanceof Error ? err.message : String(err)
34
- logger.error(`Build failed: ${message}`)
35
- process.exit(1)
36
- }
37
- }