@zenithbuild/core 0.4.6 → 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 (109) 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 -174
  38. package/compiler/discovery/layouts.ts +0 -61
  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 -81
  43. package/compiler/ir/types.ts +0 -150
  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 -591
  48. package/compiler/parse/parseZenFile.ts +0 -66
  49. package/compiler/parse/scriptAnalysis.ts +0 -83
  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 -343
  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 -289
  65. package/compiler/transform/expressionTransformer.ts +0 -385
  66. package/compiler/transform/fragmentLowering.ts +0 -634
  67. package/compiler/transform/generateBindings.ts +0 -47
  68. package/compiler/transform/generateHTML.ts +0 -28
  69. package/compiler/transform/layoutProcessor.ts +0 -132
  70. package/compiler/transform/slotResolver.ts +0 -292
  71. package/compiler/transform/transformNode.ts +0 -126
  72. package/compiler/transform/transformTemplate.ts +0 -38
  73. package/compiler/validate/invariants.ts +0 -292
  74. package/compiler/validate/validateExpressions.ts +0 -168
  75. package/core/config/index.ts +0 -16
  76. package/core/config/loader.ts +0 -69
  77. package/core/config/types.ts +0 -89
  78. package/core/index.ts +0 -135
  79. package/core/lifecycle/index.ts +0 -49
  80. package/core/lifecycle/zen-mount.ts +0 -182
  81. package/core/lifecycle/zen-unmount.ts +0 -88
  82. package/core/plugins/index.ts +0 -7
  83. package/core/plugins/registry.ts +0 -81
  84. package/core/reactivity/index.ts +0 -54
  85. package/core/reactivity/tracking.ts +0 -167
  86. package/core/reactivity/zen-batch.ts +0 -57
  87. package/core/reactivity/zen-effect.ts +0 -139
  88. package/core/reactivity/zen-memo.ts +0 -146
  89. package/core/reactivity/zen-ref.ts +0 -52
  90. package/core/reactivity/zen-signal.ts +0 -121
  91. package/core/reactivity/zen-state.ts +0 -180
  92. package/core/reactivity/zen-untrack.ts +0 -44
  93. package/dist/cli.js +0 -11653
  94. package/dist/zen-build.js +0 -15388
  95. package/dist/zen-dev.js +0 -15388
  96. package/dist/zen-preview.js +0 -15388
  97. package/dist/zenith.js +0 -15388
  98. package/router/index.ts +0 -76
  99. package/router/manifest.ts +0 -314
  100. package/router/navigation/ZenLink.zen +0 -231
  101. package/router/navigation/index.ts +0 -78
  102. package/router/navigation/zen-link.ts +0 -584
  103. package/router/runtime.ts +0 -458
  104. package/router/types.ts +0 -168
  105. package/runtime/build.ts +0 -17
  106. package/runtime/bundle-generator.ts +0 -800
  107. package/runtime/client-runtime.ts +0 -549
  108. package/runtime/serve.ts +0 -93
  109. 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
- }