@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
@@ -1,800 +0,0 @@
1
- /**
2
- * Zenith Bundle Generator
3
- *
4
- * Generates the shared client runtime bundle that gets served as:
5
- * - /assets/bundle.js in production
6
- * - /runtime.js in development
7
- *
8
- * This is a cacheable, versioned file that contains:
9
- * - Reactivity primitives (zenSignal, zenState, zenEffect, etc.)
10
- * - Lifecycle hooks (zenOnMount, zenOnUnmount)
11
- * - Hydration functions (zenithHydrate)
12
- * - Event binding utilities
13
- */
14
-
15
- /**
16
- * Generate the complete client runtime bundle
17
- * This is served as an external JS file, not inlined
18
- */
19
- export function generateBundleJS(): string {
20
- return `/*!
21
- * Zenith Runtime v0.1.0
22
- * Shared client-side runtime for hydration and reactivity
23
- */
24
- (function(global) {
25
- 'use strict';
26
-
27
- // ============================================
28
- // Dependency Tracking System
29
- // ============================================
30
-
31
- let currentEffect = null;
32
- const effectStack = [];
33
- let batchDepth = 0;
34
- const pendingEffects = new Set();
35
-
36
- function pushContext(effect) {
37
- effectStack.push(currentEffect);
38
- currentEffect = effect;
39
- }
40
-
41
- function popContext() {
42
- currentEffect = effectStack.pop() || null;
43
- }
44
-
45
- function trackDependency(subscribers) {
46
- if (currentEffect) {
47
- subscribers.add(currentEffect);
48
- currentEffect.dependencies.add(subscribers);
49
- }
50
- }
51
-
52
- function notifySubscribers(subscribers) {
53
- const effects = [...subscribers];
54
- for (const effect of effects) {
55
- if (batchDepth > 0) {
56
- pendingEffects.add(effect);
57
- } else {
58
- effect.run();
59
- }
60
- }
61
- }
62
-
63
- function cleanupEffect(effect) {
64
- for (const deps of effect.dependencies) {
65
- deps.delete(effect);
66
- }
67
- effect.dependencies.clear();
68
- }
69
-
70
- // ============================================
71
- // zenSignal - Atomic reactive value
72
- // ============================================
73
-
74
- function zenSignal(initialValue) {
75
- let value = initialValue;
76
- const subscribers = new Set();
77
-
78
- function signal(newValue) {
79
- if (arguments.length === 0) {
80
- trackDependency(subscribers);
81
- return value;
82
- }
83
- if (newValue !== value) {
84
- value = newValue;
85
- notifySubscribers(subscribers);
86
- }
87
- return value;
88
- }
89
-
90
- return signal;
91
- }
92
-
93
- // ============================================
94
- // zenState - Deep reactive object with Proxy
95
- // ============================================
96
-
97
- function zenState(initialObj) {
98
- const subscribers = new Map();
99
-
100
- function getSubscribers(path) {
101
- if (!subscribers.has(path)) {
102
- subscribers.set(path, new Set());
103
- }
104
- return subscribers.get(path);
105
- }
106
-
107
- function createProxy(obj, path) {
108
- path = path || '';
109
- if (typeof obj !== 'object' || obj === null) return obj;
110
-
111
- return new Proxy(obj, {
112
- get: function(target, prop) {
113
- const propPath = path ? path + '.' + String(prop) : String(prop);
114
- trackDependency(getSubscribers(propPath));
115
- const value = target[prop];
116
- if (typeof value === 'object' && value !== null) {
117
- return createProxy(value, propPath);
118
- }
119
- return value;
120
- },
121
- set: function(target, prop, value) {
122
- const propPath = path ? path + '.' + String(prop) : String(prop);
123
- target[prop] = value;
124
- notifySubscribers(getSubscribers(propPath));
125
- if (path) {
126
- notifySubscribers(getSubscribers(path));
127
- }
128
- return true;
129
- }
130
- });
131
- }
132
-
133
- return createProxy(initialObj);
134
- }
135
-
136
- // ============================================
137
- // zenEffect - Auto-tracked side effect
138
- // ============================================
139
-
140
- function zenEffect(fn) {
141
- const effect = {
142
- fn: fn,
143
- dependencies: new Set(),
144
- run: function() {
145
- cleanupEffect(this);
146
- pushContext(this);
147
- try {
148
- this.fn();
149
- } finally {
150
- popContext();
151
- }
152
- },
153
- dispose: function() {
154
- cleanupEffect(this);
155
- }
156
- };
157
-
158
- effect.run();
159
- return function() { effect.dispose(); };
160
- }
161
-
162
- // ============================================
163
- // zenMemo - Cached computed value
164
- // ============================================
165
-
166
- function zenMemo(fn) {
167
- let cachedValue;
168
- let dirty = true;
169
- const subscribers = new Set();
170
-
171
- const effect = {
172
- dependencies: new Set(),
173
- run: function() {
174
- dirty = true;
175
- notifySubscribers(subscribers);
176
- }
177
- };
178
-
179
- function compute() {
180
- if (dirty) {
181
- cleanupEffect(effect);
182
- pushContext(effect);
183
- try {
184
- cachedValue = fn();
185
- dirty = false;
186
- } finally {
187
- popContext();
188
- }
189
- }
190
- trackDependency(subscribers);
191
- return cachedValue;
192
- }
193
-
194
- return compute;
195
- }
196
-
197
- // ============================================
198
- // zenRef - Non-reactive mutable container
199
- // ============================================
200
-
201
- function zenRef(initialValue) {
202
- return { current: initialValue !== undefined ? initialValue : null };
203
- }
204
-
205
- // ============================================
206
- // zenBatch - Batch updates
207
- // ============================================
208
-
209
- function zenBatch(fn) {
210
- batchDepth++;
211
- try {
212
- fn();
213
- } finally {
214
- batchDepth--;
215
- if (batchDepth === 0) {
216
- const effects = [...pendingEffects];
217
- pendingEffects.clear();
218
- for (const effect of effects) {
219
- effect.run();
220
- }
221
- }
222
- }
223
- }
224
-
225
- // ============================================
226
- // zenUntrack - Read without tracking
227
- // ============================================
228
-
229
- function zenUntrack(fn) {
230
- const prevEffect = currentEffect;
231
- currentEffect = null;
232
- try {
233
- return fn();
234
- } finally {
235
- currentEffect = prevEffect;
236
- }
237
- }
238
-
239
- // ============================================
240
- // Lifecycle Hooks
241
- // ============================================
242
-
243
- const mountCallbacks = [];
244
- const unmountCallbacks = [];
245
- let isMounted = false;
246
-
247
- function zenOnMount(fn) {
248
- if (isMounted) {
249
- const cleanup = fn();
250
- if (typeof cleanup === 'function') {
251
- unmountCallbacks.push(cleanup);
252
- }
253
- } else {
254
- mountCallbacks.push(fn);
255
- }
256
- }
257
-
258
- function zenOnUnmount(fn) {
259
- unmountCallbacks.push(fn);
260
- }
261
-
262
- function triggerMount() {
263
- isMounted = true;
264
- for (let i = 0; i < mountCallbacks.length; i++) {
265
- try {
266
- const cleanup = mountCallbacks[i]();
267
- if (typeof cleanup === 'function') {
268
- unmountCallbacks.push(cleanup);
269
- }
270
- } catch(e) {
271
- console.error('[Zenith] Mount error:', e);
272
- }
273
- }
274
- mountCallbacks.length = 0;
275
- }
276
-
277
- function triggerUnmount() {
278
- isMounted = false;
279
- for (let i = 0; i < unmountCallbacks.length; i++) {
280
- try { unmountCallbacks[i](); } catch(e) { console.error('[Zenith] Unmount error:', e); }
281
- }
282
- unmountCallbacks.length = 0;
283
- }
284
-
285
- // ============================================
286
- // Expression Registry & Hydration
287
- // ============================================
288
-
289
- const expressionRegistry = new Map();
290
-
291
- function registerExpression(id, fn) {
292
- expressionRegistry.set(id, fn);
293
- }
294
-
295
- function getExpression(id) {
296
- return expressionRegistry.get(id);
297
- }
298
-
299
- function updateNode(node, exprId, pageState) {
300
- const expr = getExpression(exprId);
301
- if (!expr) return;
302
-
303
- zenEffect(function() {
304
- const result = expr(pageState);
305
-
306
- if (node.hasAttribute('data-zen-text')) {
307
- // Handle complex text/children results
308
- if (result === null || result === undefined || result === false) {
309
- node.textContent = '';
310
- } else if (typeof result === 'string') {
311
- if (result.trim().startsWith('<') && result.trim().endsWith('>')) {
312
- node.innerHTML = result;
313
- } else {
314
- node.textContent = result;
315
- }
316
- } else if (result instanceof Node) {
317
- node.innerHTML = '';
318
- node.appendChild(result);
319
- } else if (Array.isArray(result)) {
320
- node.innerHTML = '';
321
- const fragment = document.createDocumentFragment();
322
- result.flat(Infinity).forEach(item => {
323
- if (item instanceof Node) fragment.appendChild(item);
324
- else if (item != null && item !== false) fragment.appendChild(document.createTextNode(String(item)));
325
- });
326
- node.appendChild(fragment);
327
- } else {
328
- node.textContent = String(result);
329
- }
330
- } else {
331
- // Attribute update
332
- const attrNames = ['class', 'style', 'src', 'href', 'disabled', 'checked'];
333
- for (const attr of attrNames) {
334
- if (node.hasAttribute('data-zen-attr-' + attr)) {
335
- if (attr === 'class' || attr === 'className') {
336
- node.className = String(result || '');
337
- } else if (attr === 'disabled' || attr === 'checked') {
338
- if (result) node.setAttribute(attr, '');
339
- else node.removeAttribute(attr);
340
- } else {
341
- if (result != null && result !== false) node.setAttribute(attr, String(result));
342
- else node.removeAttribute(attr);
343
- }
344
- }
345
- }
346
- }
347
- });
348
- }
349
-
350
- /**
351
- * Hydrate a page with reactive bindings
352
- * Called after page HTML is in DOM
353
- */
354
- function zenithHydrate(pageState, container) {
355
- container = container || document;
356
-
357
- // Find all text expression placeholders
358
- const textNodes = container.querySelectorAll('[data-zen-text]');
359
- textNodes.forEach(el => updateNode(el, el.getAttribute('data-zen-text'), pageState));
360
-
361
- // Find all attribute expression placeholders
362
- const attrNodes = container.querySelectorAll('[data-zen-attr-class], [data-zen-attr-style], [data-zen-attr-src], [data-zen-attr-href]');
363
- attrNodes.forEach(el => {
364
- const attrMatch = Array.from(el.attributes).find(a => a.name.startsWith('data-zen-attr-'));
365
- if (attrMatch) updateNode(el, attrMatch.value, pageState);
366
- });
367
-
368
- // Wire up event handlers
369
- const eventTypes = ['click', 'change', 'input', 'submit', 'focus', 'blur', 'keyup', 'keydown'];
370
- eventTypes.forEach(eventType => {
371
- const elements = container.querySelectorAll('[data-zen-' + eventType + ']');
372
- elements.forEach(el => {
373
- const handlerName = el.getAttribute('data-zen-' + eventType);
374
- if (handlerName && (global[handlerName] || getExpression(handlerName))) {
375
- el.addEventListener(eventType, function(e) {
376
- const handler = global[handlerName] || getExpression(handlerName);
377
- if (typeof handler === 'function') handler(e, el);
378
- });
379
- }
380
- });
381
- });
382
-
383
- // Trigger mount
384
- triggerMount();
385
- }
386
-
387
- // ============================================
388
- // zenith:content - Content Engine
389
- // ============================================
390
-
391
- const schemaRegistry = new Map();
392
- const builtInEnhancers = {
393
- readTime: (item) => {
394
- const wordsPerMinute = 200;
395
- const text = item.content || '';
396
- const wordCount = text.split(/\\s+/).length;
397
- const minutes = Math.ceil(wordCount / wordsPerMinute);
398
- return Object.assign({}, item, { readTime: minutes + ' min' });
399
- },
400
- wordCount: (item) => {
401
- const text = item.content || '';
402
- const wordCount = text.split(/\\s+/).length;
403
- return Object.assign({}, item, { wordCount: wordCount });
404
- }
405
- };
406
-
407
- async function applyEnhancers(item, enhancers) {
408
- let enrichedItem = Object.assign({}, item);
409
- for (const enhancer of enhancers) {
410
- if (typeof enhancer === 'string') {
411
- const fn = builtInEnhancers[enhancer];
412
- if (fn) enrichedItem = await fn(enrichedItem);
413
- } else if (typeof enhancer === 'function') {
414
- enrichedItem = await enhancer(enrichedItem);
415
- }
416
- }
417
- return enrichedItem;
418
- }
419
-
420
- class ZenCollection {
421
- constructor(items) {
422
- this.items = [...items];
423
- this.filters = [];
424
- this.sortField = null;
425
- this.sortOrder = 'desc';
426
- this.limitCount = null;
427
- this.selectedFields = null;
428
- this.enhancers = [];
429
- this._groupByFolder = false;
430
- }
431
- where(fn) { this.filters.push(fn); return this; }
432
- sortBy(field, order = 'desc') { this.sortField = field; this.sortOrder = order; return this; }
433
- limit(n) { this.limitCount = n; return this; }
434
- fields(f) { this.selectedFields = f; return this; }
435
- enhanceWith(e) { this.enhancers.push(e); return this; }
436
- groupByFolder() { this._groupByFolder = true; return this; }
437
- get() {
438
- let results = [...this.items];
439
- for (const filter of this.filters) results = results.filter(filter);
440
- if (this.sortField) {
441
- results.sort((a, b) => {
442
- const valA = a[this.sortField];
443
- const valB = b[this.sortField];
444
- if (valA < valB) return this.sortOrder === 'asc' ? -1 : 1;
445
- if (valA > valB) return this.sortOrder === 'asc' ? 1 : -1;
446
- return 0;
447
- });
448
- }
449
- if (this.limitCount !== null) results = results.slice(0, this.limitCount);
450
-
451
- // Apply enhancers synchronously if possible
452
- if (this.enhancers.length > 0) {
453
- results = results.map(item => {
454
- let enrichedItem = Object.assign({}, item);
455
- for (const enhancer of this.enhancers) {
456
- if (typeof enhancer === 'string') {
457
- const fn = builtInEnhancers[enhancer];
458
- if (fn) enrichedItem = fn(enrichedItem);
459
- } else if (typeof enhancer === 'function') {
460
- enrichedItem = enhancer(enrichedItem);
461
- }
462
- }
463
- return enrichedItem;
464
- });
465
- }
466
-
467
- if (this.selectedFields) {
468
- results = results.map(item => {
469
- const newItem = {};
470
- this.selectedFields.forEach(f => { newItem[f] = item[f]; });
471
- return newItem;
472
- });
473
- }
474
-
475
- // Group by folder if requested
476
- if (this._groupByFolder) {
477
- const groups = {};
478
- const groupOrder = [];
479
- for (const item of results) {
480
- // Extract folder from slug (e.g., "getting-started/installation" -> "getting-started")
481
- const slug = item.slug || item.id || '';
482
- const parts = slug.split('/');
483
- const folder = parts.length > 1 ? parts[0] : 'root';
484
-
485
- if (!groups[folder]) {
486
- groups[folder] = {
487
- id: folder,
488
- title: folder.split('-').map(w => w.charAt(0).toUpperCase() + w.slice(1)).join(' '),
489
- items: []
490
- };
491
- groupOrder.push(folder);
492
- }
493
- groups[folder].items.push(item);
494
- }
495
- return groupOrder.map(f => groups[f]);
496
- }
497
-
498
- return results;
499
- }
500
- }
501
-
502
- function defineSchema(name, schema) { schemaRegistry.set(name, schema); }
503
-
504
- function zenCollection(collectionName) {
505
- const data = (global.__ZENITH_CONTENT__ && global.__ZENITH_CONTENT__[collectionName]) || [];
506
- return new ZenCollection(data);
507
- }
508
-
509
- // ============================================
510
- // useZenOrder - Documentation ordering & navigation
511
- // ============================================
512
-
513
- function slugify(text) {
514
- return String(text || '')
515
- .toLowerCase()
516
- .replace(/[^\\w\\s-]/g, '')
517
- .replace(/\\s+/g, '-')
518
- .replace(/-+/g, '-')
519
- .trim();
520
- }
521
-
522
- function getDocSlug(doc) {
523
- const slugOrId = String(doc.slug || doc.id || '');
524
- const parts = slugOrId.split('/');
525
- const filename = parts[parts.length - 1];
526
- return filename ? slugify(filename) : slugify(doc.title || 'untitled');
527
- }
528
-
529
- function processRawSections(rawSections) {
530
- const sections = (rawSections || []).map(function(rawSection) {
531
- const sectionSlug = slugify(rawSection.title || rawSection.id || 'section');
532
- const items = (rawSection.items || []).map(function(item) {
533
- return Object.assign({}, item, {
534
- slug: getDocSlug(item),
535
- sectionSlug: sectionSlug,
536
- isIntro: item.intro === true || (item.tags && item.tags.includes && item.tags.includes('intro'))
537
- });
538
- });
539
-
540
- // Sort items: intro first, then order, then alphabetical
541
- items.sort(function(a, b) {
542
- if (a.isIntro && !b.isIntro) return -1;
543
- if (!a.isIntro && b.isIntro) return 1;
544
- if (a.order !== undefined && b.order !== undefined) return a.order - b.order;
545
- if (a.order !== undefined) return -1;
546
- if (b.order !== undefined) return 1;
547
- return (a.title || '').localeCompare(b.title || '');
548
- });
549
-
550
- return {
551
- id: rawSection.id || sectionSlug,
552
- title: rawSection.title || 'Untitled',
553
- slug: sectionSlug,
554
- order: rawSection.order !== undefined ? rawSection.order : (rawSection.meta && rawSection.meta.order),
555
- hasIntro: items.some(function(i) { return i.isIntro; }),
556
- items: items
557
- };
558
- });
559
-
560
- // Sort sections: order → hasIntro → alphabetical
561
- sections.sort(function(a, b) {
562
- if (a.order !== undefined && b.order !== undefined) return a.order - b.order;
563
- if (a.order !== undefined) return -1;
564
- if (b.order !== undefined) return 1;
565
- if (a.hasIntro && !b.hasIntro) return -1;
566
- if (!a.hasIntro && b.hasIntro) return 1;
567
- return a.title.localeCompare(b.title);
568
- });
569
-
570
- return sections;
571
- }
572
-
573
- function createZenOrder(rawSections) {
574
- const sections = processRawSections(rawSections);
575
-
576
- return {
577
- sections: sections,
578
- selectedSection: sections[0] || null,
579
- selectedDoc: sections[0] && sections[0].items[0] || null,
580
-
581
- getSectionBySlug: function(sectionSlug) {
582
- return sections.find(function(s) { return s.slug === sectionSlug; }) || null;
583
- },
584
-
585
- getDocBySlug: function(sectionSlug, docSlug) {
586
- var section = sections.find(function(s) { return s.slug === sectionSlug; });
587
- if (!section) return null;
588
- return section.items.find(function(d) { return d.slug === docSlug; }) || null;
589
- },
590
-
591
- getNextDoc: function(currentDoc) {
592
- if (!currentDoc) return null;
593
- var currentSection = sections.find(function(s) { return s.slug === currentDoc.sectionSlug; });
594
- if (!currentSection) return null;
595
- var idx = currentSection.items.findIndex(function(d) { return d.slug === currentDoc.slug; });
596
- if (idx < currentSection.items.length - 1) return currentSection.items[idx + 1];
597
- var secIdx = sections.findIndex(function(s) { return s.slug === currentSection.slug; });
598
- if (secIdx < sections.length - 1) return sections[secIdx + 1].items[0] || null;
599
- return null;
600
- },
601
-
602
- getPrevDoc: function(currentDoc) {
603
- if (!currentDoc) return null;
604
- var currentSection = sections.find(function(s) { return s.slug === currentDoc.sectionSlug; });
605
- if (!currentSection) return null;
606
- var idx = currentSection.items.findIndex(function(d) { return d.slug === currentDoc.slug; });
607
- if (idx > 0) return currentSection.items[idx - 1];
608
- var secIdx = sections.findIndex(function(s) { return s.slug === currentSection.slug; });
609
- if (secIdx > 0) {
610
- var prev = sections[secIdx - 1];
611
- return prev.items[prev.items.length - 1] || null;
612
- }
613
- return null;
614
- },
615
-
616
- buildDocUrl: function(sectionSlug, docSlug) {
617
- if (!docSlug || docSlug === 'index') return '/documentation/' + sectionSlug;
618
- return '/documentation/' + sectionSlug + '/' + docSlug;
619
- }
620
- };
621
- }
622
-
623
- // Virtual DOM Helper for JSX-style expressions
624
- function h(tag, props, children) {
625
- const el = document.createElement(tag);
626
- if (props) {
627
- for (const [key, value] of Object.entries(props)) {
628
- if (key.startsWith('on') && typeof value === 'function') {
629
- el.addEventListener(key.slice(2).toLowerCase(), value);
630
- } else if (key === 'class' || key === 'className') {
631
- el.className = String(value || '');
632
- } else if (key === 'style' && typeof value === 'object') {
633
- Object.assign(el.style, value);
634
- } else if (value != null && value !== false) {
635
- el.setAttribute(key, String(value));
636
- }
637
- }
638
- }
639
- if (children != null && children !== false) {
640
- // Flatten nested arrays (from .map() calls)
641
- const childrenArray = Array.isArray(children) ? children.flat(Infinity) : [children];
642
- for (const child of childrenArray) {
643
- // Skip null, undefined, and false
644
- if (child == null || child === false) continue;
645
-
646
- if (typeof child === 'string') {
647
- // Check if string looks like HTML
648
- if (child.trim().startsWith('<') && child.trim().endsWith('>')) {
649
- // Render as HTML
650
- const wrapper = document.createElement('div');
651
- wrapper.innerHTML = child;
652
- while (wrapper.firstChild) {
653
- el.appendChild(wrapper.firstChild);
654
- }
655
- } else {
656
- el.appendChild(document.createTextNode(child));
657
- }
658
- } else if (typeof child === 'number') {
659
- el.appendChild(document.createTextNode(String(child)));
660
- } else if (child instanceof Node) {
661
- el.appendChild(child);
662
- } else if (Array.isArray(child)) {
663
- // Handle nested arrays (shouldn't happen after flat() but just in case)
664
- for (const c of child) {
665
- if (c instanceof Node) el.appendChild(c);
666
- else if (c != null && c !== false) el.appendChild(document.createTextNode(String(c)));
667
- }
668
- }
669
- }
670
- }
671
- return el;
672
- }
673
-
674
- // ============================================
675
- // Export to window.__zenith
676
- // ============================================
677
-
678
- global.__zenith = {
679
- // Reactivity primitives
680
- signal: zenSignal,
681
- state: zenState,
682
- effect: zenEffect,
683
- memo: zenMemo,
684
- ref: zenRef,
685
- batch: zenBatch,
686
- untrack: zenUntrack,
687
- // zenith:content
688
- defineSchema: defineSchema,
689
- zenCollection: zenCollection,
690
- // useZenOrder hook
691
- createZenOrder: createZenOrder,
692
- processRawSections: processRawSections,
693
- slugify: slugify,
694
- // Virtual DOM helper for JSX
695
- h: h,
696
- // Lifecycle
697
- onMount: zenOnMount,
698
- onUnmount: zenOnUnmount,
699
- // Internal hooks
700
- triggerMount: triggerMount,
701
- triggerUnmount: triggerUnmount,
702
- // Hydration
703
- hydrate: zenithHydrate,
704
- registerExpression: registerExpression,
705
- getExpression: getExpression
706
- };
707
-
708
- // Expose with zen* prefix for direct usage
709
- global.zenSignal = zenSignal;
710
- global.zenState = zenState;
711
- global.zenEffect = zenEffect;
712
- global.zenMemo = zenMemo;
713
- global.zenRef = zenRef;
714
- global.zenBatch = zenBatch;
715
- global.zenUntrack = zenUntrack;
716
- global.zenOnMount = zenOnMount;
717
- global.zenOnUnmount = zenOnUnmount;
718
- global.zenithHydrate = zenithHydrate;
719
-
720
- // Clean aliases
721
- global.signal = zenSignal;
722
- global.state = zenState;
723
- global.effect = zenEffect;
724
- global.memo = zenMemo;
725
- global.ref = zenRef;
726
- global.batch = zenBatch;
727
- global.untrack = zenUntrack;
728
- global.onMount = zenOnMount;
729
- global.onUnmount = zenOnUnmount;
730
-
731
- // useZenOrder hook exports
732
- global.createZenOrder = createZenOrder;
733
- global.processRawSections = processRawSections;
734
- global.slugify = slugify;
735
-
736
- // ============================================
737
- // HMR Client (Development Only)
738
- // ============================================
739
-
740
- if (typeof window !== 'undefined' && (location.hostname === 'localhost' || location.hostname === '127.0.0.1')) {
741
- let socket;
742
- function connectHMR() {
743
- const protocol = location.protocol === 'https:' ? 'wss:' : 'ws:';
744
- socket = new WebSocket(protocol + '//' + location.host + '/hmr');
745
-
746
- socket.onmessage = function(event) {
747
- try {
748
- const data = JSON.parse(event.data);
749
- if (data.type === 'reload') {
750
- console.log('[Zenith] HMR: Reloading page...');
751
- location.reload();
752
- } else if (data.type === 'style-update') {
753
- console.log('[Zenith] HMR: Updating style ' + data.url);
754
- const links = document.querySelectorAll('link[rel="stylesheet"]');
755
- for (let i = 0; i < links.length; i++) {
756
- const link = links[i];
757
- const url = new URL(link.href);
758
- if (url.pathname === data.url) {
759
- link.href = data.url + '?t=' + Date.now();
760
- break;
761
- }
762
- }
763
- }
764
- } catch (e) {
765
- console.error('[Zenith] HMR Error:', e);
766
- }
767
- };
768
-
769
- socket.onclose = function() {
770
- console.log('[Zenith] HMR: Connection closed. Retrying in 2s...');
771
- setTimeout(connectHMR, 2000);
772
- };
773
- }
774
-
775
- // Connect unless explicitly disabled
776
- if (!window.__ZENITH_NO_HMR__) {
777
- connectHMR();
778
- }
779
- }
780
-
781
- })(typeof window !== 'undefined' ? window : this);
782
- `
783
- }
784
-
785
- /**
786
- * Generate a minified version of the bundle
787
- * For production builds
788
- */
789
- export function generateMinifiedBundleJS(): string {
790
- // For now, return non-minified
791
- // TODO: Add minification via terser or similar
792
- return generateBundleJS()
793
- }
794
-
795
- /**
796
- * Get bundle version for cache busting
797
- */
798
- export function getBundleVersion(): string {
799
- return '0.1.0'
800
- }