@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
@@ -0,0 +1,145 @@
1
+ # CORE_CONTRACT.md — Deterministic Utility Substrate
2
+
3
+ Canonical public docs: `../zenith-docs/documentation/contracts/core-contract.md`
4
+
5
+ > **This document is a legal boundary.**
6
+ > Core is a shared utility layer. It contains no business logic,
7
+ > no routing, no DOM, no framework behavior. Pure helper substrate only.
8
+ >
9
+ > **Standalone test:** If `zenith-core` were published alone on npm,
10
+ > it must make sense as a generic deterministic utility library.
11
+
12
+ ## Status: FROZEN (V0)
13
+
14
+ ---
15
+
16
+ ## 1. Core Identity
17
+
18
+ Core provides deterministic **transforms**, **formatting**, and **schema validation**.
19
+
20
+ **Core provides:**
21
+ - Deterministic transforms: `hash`, `normalizePath`, `sortRoutes`, `parseConfig`, `parseSemver`
22
+ - Deterministic formatting: `formatError`
23
+ - Deterministic schema validation: `validateConfigSchema`, `validateRouteParams`
24
+
25
+ **Core does NOT:**
26
+ - Scan repositories
27
+ - Enforce cross-layer behavior
28
+ - Know about router semantics
29
+ - Know about bundler internals
30
+ - Police other layers' architecture
31
+
32
+ ---
33
+
34
+ ## 2. Allowed Modules
35
+
36
+ | Module | Purpose |
37
+ |---|---|
38
+ | `config.js` | Load + validate config schema |
39
+ | `path.js` | Normalize paths + `[param]` → `:param` |
40
+ | `order.js` | Static-first stable sort |
41
+ | `hash.js` | SHA-256 content hashing |
42
+ | `errors.js` | Error factory + prefixing |
43
+ | `version.js` | SemVer parsing + major compatibility |
44
+ | `guards.js` | Small pure validation helpers |
45
+ | `index.js` | Re-exports |
46
+
47
+ ---
48
+
49
+ ## 3. Determinism Guarantees
50
+
51
+ | Rule | Guarantee |
52
+ |---|---|
53
+ | Hashing | Same input → same hash, cross-platform |
54
+ | Ordering | Stable sort: static first, dynamic after, alpha tiebreak |
55
+ | Paths | Normalized separators (`/`), consistent param format |
56
+ | Config | Missing keys → explicit defaults, unknown keys → throw |
57
+ | Errors | Consistent format: `[Zenith:MODULE] message` |
58
+
59
+ ---
60
+
61
+ ## 4. Explicit Prohibitions
62
+
63
+ Core source **must never**:
64
+
65
+ 1. Import from `@zenithbuild/compiler`, `@zenithbuild/bundler`, `@zenithbuild/runtime`, or `@zenithbuild/router`
66
+ 2. Reference `window`, `document`, `navigator`, or any browser API
67
+ 3. Use `eval()`, `new Function()`, or `document.write()`
68
+ 4. Perform build orchestration of any kind
69
+ 5. Access the filesystem **except when explicitly loading `zenith.config.js`**
70
+ 6. Mutate global state
71
+ 7. Contain preset/mode logic (`basic`, `router`, `fullstack` belong in `create-zenith`)
72
+ 8. Initiate version checks against other packages (other layers call core's utility)
73
+
74
+ ---
75
+
76
+ ## 5. Hash Contract
77
+
78
+ - Algorithm: **SHA-256** via `node:crypto`
79
+ - Output: **hex string**
80
+ - Input normalization: path separators → `/`, trailing newlines stripped
81
+
82
+ > **Critical rule:** Hash algorithm must match bundler's algorithm exactly.
83
+ > If bundler changes hash algorithm, core must change in lockstep.
84
+
85
+ ---
86
+
87
+ ## 6. Config Schema (V0)
88
+
89
+ ```js
90
+ // zenith.config.js
91
+ export default {
92
+ router: false, // boolean — opt-in client router
93
+ outDir: 'dist', // string — output directory
94
+ pagesDir: 'pages' // string — pages directory
95
+ }
96
+ ```
97
+
98
+ | Key | Type | Default | Validation |
99
+ |---|---|---|---|
100
+ | `router` | `boolean` | `false` | Must be boolean |
101
+ | `outDir` | `string` | `'dist'` | Non-empty string |
102
+ | `pagesDir` | `string` | `'pages'` | Non-empty string |
103
+
104
+ Unknown keys → throw `[Zenith:Config] Unknown key: "foo"`.
105
+
106
+ **No other keys for V0.** No `mode`, `target`, `presets`, `experimental`, `base`, `assetsDir`.
107
+
108
+ ---
109
+
110
+ ## 7. Version Compatibility API
111
+
112
+ ```js
113
+ validateCompatibility(coreVersion, otherVersion)
114
+ // Throws if major versions differ
115
+ // Warns if minor versions differ by > 1
116
+ ```
117
+
118
+ **Direction of control:** Other layers call this function.
119
+ Core never imports other packages to auto-check.
120
+
121
+ ---
122
+
123
+ ## 8. Guard Helpers
124
+
125
+ Guards are **small pure validation helpers**:
126
+
127
+ ```js
128
+ containsForbiddenPattern(source, patterns) // returns boolean
129
+ validateRouteParams(routePath) // throws on repeated params
130
+ validateConfigSchema(config) // throws on unknown keys / wrong types
131
+ ```
132
+
133
+ Guards do NOT:
134
+ - Scan entire repositories
135
+ - Enforce architectural decisions
136
+ - Assert that other layers used sorting correctly
137
+
138
+ ---
139
+
140
+ ## 9. Dependency Rules
141
+
142
+ - **Zero dependencies on other Zenith packages**
143
+ - Zero runtime npm dependencies
144
+ - Dev dependencies: Jest only
145
+ - Pure ESM, Node.js built-ins only
package/README.md CHANGED
@@ -1,39 +1,24 @@
1
- # @zenithbuild/core
1
+ # `@zenithbuild/core`
2
2
 
3
- The heart of the Zenith framework. High-performance reactive runtime, compiler, and build primitives.
3
+ This is the **sole public entrypoint** for the Zenith framework.
4
4
 
5
- ## Overview
5
+ ## Canonical Docs
6
6
 
7
- Zenith is a modern reactive web framework designed for maximum performance and developer experience. The core package contains the essential building blocks:
8
- - **Compiler**: Transforms `.zen` files into optimized JavaScript.
9
- - **Runtime**: A lightweight, efficient reactive system for the browser.
10
- - **Router**: Lightweight client-side routing.
11
- - **Build Primitives**: Tools for dev servers and production builds.
7
+ - Core contract: `../zenith-docs/documentation/contracts/core-contract.md`
8
+ - Zenith contract: `../zenith-docs/documentation/zenith-contract.md`
12
9
 
13
- ## Key Components
10
+ Use this package directly in your projects to run Zenith utilities and access the official APIs.
14
11
 
15
- ### 1. Compiler (`/compiler`)
16
- The Zenith compiler handles parsing and transforming Single File Components (`.zen`). It leverages `parse5` for robust HTML parsing and generates highly optimized render functions.
12
+ ## Installation
17
13
 
18
- ### 2. Runtime (`/runtime`)
19
- Our runtime is designed to be minimal. It manages the reactive cycle, efficient DOM updates, and lifecycle management (like `zenOnMount`).
20
-
21
- ### 3. Server (`/bin/zen-dev`, `/bin/zen-build`)
22
- Low-level binaries for orchestrating development and production environments.
23
-
24
- ## Architecture
25
-
26
- Zenith follows a "Compiler-First" philosophy. We shift as much work as possible to build time, keeping the client-side bundle lean and fast.
27
-
28
- ## Usage (Internal)
14
+ ```bash
15
+ npm install @zenithbuild/core
16
+ ```
29
17
 
30
- This package is typically consumed by the Zenith CLI and other ecosystem tools.
18
+ ## Running the CLI
31
19
 
32
- ```typescript
33
- import { compile } from '@zenithbuild/core/compiler';
34
- // ... compile logic
35
- ```
20
+ The `.bin` wrapper is exposed as `zenith`. Running `npx zenith dev` or `npx zenith build` executes the corresponding framework command.
36
21
 
37
- ## License
22
+ ## Internal Dependencies
38
23
 
39
- MIT
24
+ For developers: DO NOT import any other `@zenithbuild/*` packages directly (e.g. `@zenithbuild/compiler`, `@zenithbuild/runtime`, `@zenithbuild/cli`). All needed features are either opaque implementation details or safely exposed through exports here. Direct access of inner implementation details is strictly forbidden.
package/bin/zenith.js ADDED
@@ -0,0 +1,89 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { readFileSync } from 'node:fs';
4
+ import { dirname, join } from 'node:path';
5
+ import { fileURLToPath, pathToFileURL } from 'node:url';
6
+ import { createRequire } from 'node:module';
7
+
8
+ const __filename = fileURLToPath(import.meta.url);
9
+ const __dirname = dirname(__filename);
10
+ const require = createRequire(import.meta.url);
11
+ const cwdRequire = createRequire(join(process.cwd(), 'package.json'));
12
+
13
+ function resolveInternal(specifier) {
14
+ try {
15
+ return cwdRequire.resolve(specifier);
16
+ } catch {
17
+ return require.resolve(specifier);
18
+ }
19
+ }
20
+
21
+ // Version mismatch check
22
+ const corePkgPath = join(__dirname, '../package.json');
23
+ const corePkg = JSON.parse(readFileSync(corePkgPath, 'utf-8'));
24
+ const args = process.argv.slice(2);
25
+
26
+ if (args.includes('--version') || args.includes('-v')) {
27
+ console.log(`zenith ${corePkg.version}`);
28
+ process.exit(0);
29
+ }
30
+
31
+ if (args.includes('--help') || args.includes('-h')) {
32
+ const { cli } = await import(pathToFileURL(resolveInternal('@zenithbuild/cli')).href);
33
+ await cli(args);
34
+ process.exit(0);
35
+ }
36
+
37
+ const expectedInternals = [
38
+ '@zenithbuild/cli',
39
+ '@zenithbuild/compiler',
40
+ '@zenithbuild/runtime',
41
+ '@zenithbuild/router',
42
+ '@zenithbuild/bundler'
43
+ ];
44
+
45
+ let hasMismatch = false;
46
+
47
+ for (const internal of expectedInternals) {
48
+ const expectedVersion = corePkg.dependencies[internal];
49
+ if (!expectedVersion) continue;
50
+
51
+ try {
52
+ const entryPath = resolveInternal(internal);
53
+
54
+ // Walk up to find the package's package.json
55
+ let currentDir = dirname(entryPath);
56
+ let pkgVersion = null;
57
+
58
+ while (currentDir !== '/' && currentDir !== '.') {
59
+ try {
60
+ const pkgTxt = readFileSync(join(currentDir, 'package.json'), 'utf-8');
61
+ const pkg = JSON.parse(pkgTxt);
62
+ if (pkg.name === internal) {
63
+ pkgVersion = pkg.version;
64
+ break;
65
+ }
66
+ } catch (e) {
67
+ // Ignored, continue walking up
68
+ }
69
+ currentDir = dirname(currentDir);
70
+ }
71
+
72
+ if (pkgVersion && pkgVersion !== expectedVersion) {
73
+ console.error(`Version mismatch: ${internal} is version ${pkgVersion} but @zenithbuild/core expects exactly ${expectedVersion}`);
74
+ hasMismatch = true;
75
+ } else if (!pkgVersion) {
76
+ // In a strict mode, failing to find package.json might be treated as a failure.
77
+ }
78
+ } catch (err) {
79
+ console.error(`Version mismatch check failed: Could not resolve internal dependency ${internal}`);
80
+ hasMismatch = true;
81
+ }
82
+ }
83
+
84
+ if (hasMismatch) {
85
+ process.exit(1);
86
+ }
87
+
88
+ // Proceed to hand off execution to the underlying CLI implementation
89
+ import(pathToFileURL(resolveInternal('@zenithbuild/cli')).href);
package/package.json CHANGED
@@ -1,72 +1,55 @@
1
1
  {
2
2
  "name": "@zenithbuild/core",
3
- "version": "0.6.2",
4
- "description": "Core library for the Zenith framework",
3
+ "version": "0.6.4",
4
+ "description": "Deterministic utility substrate for the Zenith framework",
5
5
  "license": "MIT",
6
6
  "type": "module",
7
- "main": "./index.ts",
7
+ "main": "./src/index.js",
8
8
  "bin": {
9
- "zenith": "./dist/zenith.js",
10
- "zenith-dev": "./dist/zen-dev.js",
11
- "zen-dev": "./dist/zen-dev.js",
12
- "zen-build": "./dist/zen-build.js",
13
- "zen-preview": "./dist/zen-preview.js"
9
+ "zenith": "bin/zenith.js"
10
+ },
11
+ "exports": {
12
+ ".": "./src/index.js",
13
+ "./config": "./src/config.js",
14
+ "./path": "./src/path.js",
15
+ "./order": "./src/order.js",
16
+ "./hash": "./src/hash.js",
17
+ "./errors": "./src/errors.js",
18
+ "./version": "./src/version.js",
19
+ "./guards": "./src/guards.js",
20
+ "./schema": "./src/schema.js",
21
+ "./core-template": "./src/core-template.js",
22
+ "./ir": "./src/ir/index.js"
14
23
  },
15
24
  "files": [
25
+ "src",
16
26
  "bin",
17
- "cli",
18
- "compiler",
19
- "core",
20
- "router",
21
- "runtime",
22
- "dist",
23
- "index.ts",
24
- "tsconfig.json"
27
+ "CORE_CONTRACT.md",
28
+ "README.md",
29
+ "contracts"
25
30
  ],
26
- "exports": {
27
- ".": "./index.ts",
28
- "./compiler": "./compiler/index.ts",
29
- "./config": "./core/config/index.ts",
30
- "./core": "./core/index.ts",
31
- "./router": "./router/index.ts",
32
- "./runtime": "./runtime/index.ts"
31
+ "publishConfig": {
32
+ "access": "public"
33
33
  },
34
- "keywords": [
35
- "zenith",
36
- "core",
37
- "framework"
38
- ],
39
- "author": "Zenith Team",
40
- "repository": {
41
- "type": "git",
42
- "url": "git@github.com:zenithbuild/zenith-core.git"
34
+ "dependencies": {
35
+ "@zenithbuild/cli": "0.6.4",
36
+ "@zenithbuild/compiler": "0.6.4",
37
+ "@zenithbuild/runtime": "0.6.4",
38
+ "@zenithbuild/router": "0.6.4",
39
+ "@zenithbuild/bundler": "0.6.4"
43
40
  },
44
41
  "scripts": {
45
- "dev": "bun dev",
46
- "build": "bun build",
47
- "start": "bun run build && bun run dev",
48
- "build:cli": "bun build bin/zenith.ts bin/zen-dev.ts bin/zen-build.ts bin/zen-preview.ts --outdir dist --target bun --bundle && for f in dist/*.js; do echo '#!/usr/bin/env bun' | cat - \"$f\" > \"$f.tmp\" && mv \"$f.tmp\" \"$f\" && chmod +x \"$f\"; done",
49
- "format": "prettier --write \"**/*.ts\"",
50
- "format:check": "prettier --check \"**/*.ts\""
42
+ "test": "npm run contract:deps && npm run contract:scan && npm run contract:api && npm run contract:template && npm run contract:golden",
43
+ "test:legacy": "NODE_OPTIONS=--experimental-vm-modules jest --config jest.config.js",
44
+ "contract:deps": "node dependency_contract.spec.js",
45
+ "contract:scan": "node contract-scan.mjs",
46
+ "contract:api": "node core_api_lock.spec.js",
47
+ "contract:template": "node template-contract.spec.js",
48
+ "contract:golden": "node golden-contract.spec.js"
51
49
  },
52
- "private": false,
53
50
  "devDependencies": {
54
- "@types/bun": "latest",
55
- "@types/node": "latest",
56
- "prettier": "^3.7.4"
57
- },
58
- "peerDependencies": {
59
- "typescript": "^5"
51
+ "@jest/globals": "^30.2.0",
52
+ "jest": "^30.2.0"
60
53
  },
61
- "dependencies": {
62
- "@zenithbuild/router": "latest",
63
- "@types/acorn": "^6.0.4",
64
- "@types/marked": "^6.0.0",
65
- "@types/parse5": "^7.0.0",
66
- "acorn": "^8.15.0",
67
- "es-module-lexer": "^2.0.0",
68
- "marked": "^17.0.1",
69
- "parse5": "^8.0.0",
70
- "picocolors": "^1.1.1"
71
- }
54
+ "private": false
72
55
  }
package/src/config.js ADDED
@@ -0,0 +1,136 @@
1
+ // ---------------------------------------------------------------------------
2
+ // config.js — Zenith Core V0
3
+ // ---------------------------------------------------------------------------
4
+ // Load and validate zenith.config.js. This is the ONLY module in core
5
+ // that touches the filesystem (via dynamic import).
6
+ //
7
+ // V0 Schema: { router, outDir, pagesDir }
8
+ // Unknown keys → throw.
9
+ // ---------------------------------------------------------------------------
10
+
11
+ import { join } from 'node:path';
12
+ import { pathToFileURL } from 'node:url';
13
+
14
+ /** V0 config defaults */
15
+ const DEFAULTS = {
16
+ router: false,
17
+ embeddedMarkupExpressions: false,
18
+ types: true,
19
+ typescriptDefault: true,
20
+ outDir: 'dist',
21
+ pagesDir: 'pages',
22
+ experimental: {},
23
+ strictDomLints: false
24
+ };
25
+
26
+ /** Allowed keys and their expected types */
27
+ const SCHEMA = {
28
+ router: 'boolean',
29
+ embeddedMarkupExpressions: 'boolean',
30
+ types: 'boolean',
31
+ typescriptDefault: 'boolean',
32
+ outDir: 'string',
33
+ pagesDir: 'string',
34
+ experimental: 'object',
35
+ strictDomLints: 'boolean'
36
+ };
37
+
38
+ /**
39
+ * Validate a config object against the V0 schema.
40
+ * Throws on unknown keys or wrong types.
41
+ *
42
+ * @param {object} config
43
+ * @returns {object} Normalized config with defaults applied
44
+ */
45
+ export function validateConfig(config) {
46
+ if (config === null || config === undefined) {
47
+ return { ...DEFAULTS };
48
+ }
49
+
50
+ if (typeof config !== 'object' || Array.isArray(config)) {
51
+ throw new Error('[Zenith:Config] Config must be a plain object');
52
+ }
53
+
54
+ // Check for unknown keys
55
+ for (const key of Object.keys(config)) {
56
+ if (!(key in SCHEMA)) {
57
+ throw new Error(`[Zenith:Config] Unknown key: "${key}"`);
58
+ }
59
+ }
60
+
61
+ // Validate types and apply defaults
62
+ const result = { ...DEFAULTS };
63
+
64
+ for (const [key, expectedType] of Object.entries(SCHEMA)) {
65
+ if (key in config) {
66
+ const value = config[key];
67
+ if (typeof value !== expectedType) {
68
+ throw new Error(
69
+ `[Zenith:Config] Key "${key}" must be ${expectedType}, got ${typeof value}`
70
+ );
71
+ }
72
+ if (expectedType === 'string' && value.trim() === '') {
73
+ throw new Error(
74
+ `[Zenith:Config] Key "${key}" must be a non-empty string`
75
+ );
76
+ }
77
+ if (key === 'experimental' && value) {
78
+ if (typeof value !== 'object' || Array.isArray(value)) {
79
+ throw new Error(`[Zenith:Config] Key "experimental" must be a plain object`);
80
+ }
81
+ const expDefaults = { ...DEFAULTS.experimental };
82
+ for (const expKey of Object.keys(value)) {
83
+ if (!(expKey in expDefaults)) {
84
+ throw new Error(`[Zenith:Config] Unknown experimental key: "${expKey}"`);
85
+ }
86
+ if (typeof value[expKey] !== typeof expDefaults[expKey]) {
87
+ throw new Error(`[Zenith:Config] Experimental key "${expKey}" must be ${typeof expDefaults[expKey]}`);
88
+ }
89
+ expDefaults[expKey] = value[expKey];
90
+ }
91
+ result[key] = expDefaults;
92
+ continue;
93
+ }
94
+ result[key] = value;
95
+ }
96
+ }
97
+
98
+ return result;
99
+ }
100
+
101
+ /**
102
+ * Load zenith.config.js from a project root.
103
+ * Returns validated config with defaults applied.
104
+ *
105
+ * @param {string} projectRoot
106
+ * @returns {Promise<object>}
107
+ */
108
+ export async function loadConfig(projectRoot) {
109
+ const configPath = join(projectRoot, 'zenith.config.js');
110
+
111
+ try {
112
+ const url = pathToFileURL(configPath).href;
113
+ const mod = await import(url);
114
+ const raw = mod.default || mod;
115
+ return validateConfig(raw);
116
+ } catch (err) {
117
+ // File not found — return defaults
118
+ if (
119
+ err.code === 'ERR_MODULE_NOT_FOUND' ||
120
+ err.code === 'ENOENT' ||
121
+ err.message?.includes('Cannot find module') ||
122
+ err.message?.includes('ENOENT')
123
+ ) {
124
+ return { ...DEFAULTS };
125
+ }
126
+ throw err;
127
+ }
128
+ }
129
+
130
+ /**
131
+ * Return the default config.
132
+ * @returns {object}
133
+ */
134
+ export function getDefaults() {
135
+ return { ...DEFAULTS };
136
+ }
@@ -0,0 +1,30 @@
1
+ // ---------------------------------------------------------------------------
2
+ // core-template.js — source template for emitted core asset
3
+ // ---------------------------------------------------------------------------
4
+
5
+ function assertRuntimeImport(runtimeImport) {
6
+ if (typeof runtimeImport !== 'string' || runtimeImport.trim().length === 0) {
7
+ throw new Error('[Zenith Core] coreModuleSource(runtimeImport) requires non-empty runtimeImport');
8
+ }
9
+ }
10
+
11
+ export function coreModuleSource(runtimeImport) {
12
+ assertRuntimeImport(runtimeImport);
13
+ const runtimeImportLiteral = JSON.stringify(runtimeImport);
14
+
15
+ return [
16
+ `import { signal, state, zeneffect, zenEffect as __zenithZenEffect, zenMount as __zenithZenMount } from ${runtimeImportLiteral};`,
17
+ '',
18
+ 'export const zenSignal = signal;',
19
+ 'export const zenState = state;',
20
+ 'export const zenEffect = __zenithZenEffect;',
21
+ 'export const zenMount = __zenithZenMount;',
22
+ '',
23
+ 'export function zenOnMount(callback) {',
24
+ ' return __zenithZenMount(callback);',
25
+ '}',
26
+ '',
27
+ 'export { signal, state, zeneffect };',
28
+ ''
29
+ ].join('\n');
30
+ }
package/src/errors.js ADDED
@@ -0,0 +1,54 @@
1
+ // ---------------------------------------------------------------------------
2
+ // errors.js — Zenith Core V0
3
+ // ---------------------------------------------------------------------------
4
+ // Shared error formatting. All Zenith errors use the format:
5
+ // [Zenith:MODULE] message
6
+ //
7
+ // Pure factory functions. No side effects.
8
+ // ---------------------------------------------------------------------------
9
+
10
+ /**
11
+ * Create a Zenith error with standard formatting.
12
+ *
13
+ * @param {string} module Module name (e.g. 'Config', 'Path', 'Build')
14
+ * @param {string} message Error message
15
+ * @returns {Error}
16
+ */
17
+ export function createError(module, message) {
18
+ const err = new Error(`[Zenith:${module}] ${message}`);
19
+ err.zenithModule = module;
20
+ return err;
21
+ }
22
+
23
+ /**
24
+ * Format an error message with the Zenith prefix.
25
+ *
26
+ * @param {string} module
27
+ * @param {string} message
28
+ * @returns {string}
29
+ */
30
+ export function formatError(module, message) {
31
+ return `[Zenith:${module}] ${message}`;
32
+ }
33
+
34
+ /**
35
+ * Check if an error is a Zenith error.
36
+ *
37
+ * @param {Error} err
38
+ * @returns {boolean}
39
+ */
40
+ export function isZenithError(err) {
41
+ return err instanceof Error && typeof err.zenithModule === 'string';
42
+ }
43
+
44
+ /**
45
+ * Predefined error codes for common situations.
46
+ */
47
+ export const ErrorCodes = {
48
+ CONFIG_UNKNOWN_KEY: 'CONFIG_UNKNOWN_KEY',
49
+ CONFIG_INVALID_TYPE: 'CONFIG_INVALID_TYPE',
50
+ CONFIG_EMPTY_VALUE: 'CONFIG_EMPTY_VALUE',
51
+ PATH_REPEATED_PARAM: 'PATH_REPEATED_PARAM',
52
+ VERSION_INCOMPATIBLE: 'VERSION_INCOMPATIBLE',
53
+ GUARD_VIOLATION: 'GUARD_VIOLATION'
54
+ };
package/src/guards.js ADDED
@@ -0,0 +1,61 @@
1
+ // ---------------------------------------------------------------------------
2
+ // guards.js — Zenith Core V0
3
+ // ---------------------------------------------------------------------------
4
+ // Small pure validation helpers. These provide primitives —
5
+ // they do NOT scan repos, enforce architecture, or police other layers.
6
+ // ---------------------------------------------------------------------------
7
+
8
+ /**
9
+ * Check if source contains any of the given forbidden patterns.
10
+ *
11
+ * @param {string} source Source code text
12
+ * @param {string[]} patterns Patterns to check for
13
+ * @returns {string[]} Array of matched patterns (empty if none)
14
+ */
15
+ export function containsForbiddenPattern(source, patterns) {
16
+ const found = [];
17
+ for (const pattern of patterns) {
18
+ if (source.includes(pattern)) {
19
+ found.push(pattern);
20
+ }
21
+ }
22
+ return found;
23
+ }
24
+
25
+ /**
26
+ * Validate that a route path has no repeated parameter names.
27
+ * Re-exported from path.js for convenience.
28
+ *
29
+ * @param {string} routePath
30
+ */
31
+ export { validateRouteParams } from './path.js';
32
+
33
+ /**
34
+ * Validate a config object against the V0 schema.
35
+ * Re-exported from config.js for convenience.
36
+ *
37
+ * @param {object} config
38
+ * @returns {object}
39
+ */
40
+ export { validateConfig as validateConfigSchema } from './config.js';
41
+
42
+ /**
43
+ * Default forbidden patterns for Zenith source code.
44
+ */
45
+ export const FORBIDDEN_PATTERNS = [
46
+ 'eval(',
47
+ 'new Function(',
48
+ 'new Function (',
49
+ 'document.write('
50
+ ];
51
+
52
+ /**
53
+ * Default browser globals that should not appear in Node-only code.
54
+ */
55
+ export const BROWSER_GLOBALS = [
56
+ 'window',
57
+ 'document',
58
+ 'navigator',
59
+ 'localStorage',
60
+ 'sessionStorage'
61
+ ];