@zolomedia/bifrost-client 1.7.74

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 (140) hide show
  1. package/L1_Foundation/L1_Foundation.js +13 -0
  2. package/L1_Foundation/bootstrap/bootstrap.js +11 -0
  3. package/L1_Foundation/bootstrap/bootstrap_hooks.js +123 -0
  4. package/L1_Foundation/bootstrap/bootstrap_index.js +15 -0
  5. package/L1_Foundation/bootstrap/bootstrap_logger.js +135 -0
  6. package/L1_Foundation/bootstrap/cdn_loader.js +217 -0
  7. package/L1_Foundation/bootstrap/module_registry.js +102 -0
  8. package/L1_Foundation/bootstrap/prism_loader.js +164 -0
  9. package/L1_Foundation/config/client_config.js +110 -0
  10. package/L1_Foundation/config/config.js +7 -0
  11. package/L1_Foundation/connection/connection.js +8 -0
  12. package/L1_Foundation/connection/websocket_connection.js +122 -0
  13. package/L1_Foundation/constants/bifrost_constants.js +284 -0
  14. package/L1_Foundation/constants/constants.js +7 -0
  15. package/L1_Foundation/logger/logger.js +10 -0
  16. package/L2_Handling/L2_Handling.js +15 -0
  17. package/L2_Handling/cache/cache.js +22 -0
  18. package/L2_Handling/cache/cache_constants.js +69 -0
  19. package/L2_Handling/cache/orchestration/cache_manager.js +299 -0
  20. package/L2_Handling/cache/orchestration/cache_orchestrator.js +260 -0
  21. package/L2_Handling/cache/orchestration/orchestration.js +12 -0
  22. package/L2_Handling/cache/storage/session_manager.js +289 -0
  23. package/L2_Handling/cache/storage/storage.js +10 -0
  24. package/L2_Handling/cache/storage/storage_manager.js +590 -0
  25. package/L2_Handling/display/composite/composite.js +13 -0
  26. package/L2_Handling/display/composite/dashboard_renderer.js +221 -0
  27. package/L2_Handling/display/composite/swiper_renderer.js +564 -0
  28. package/L2_Handling/display/composite/terminal_renderer.js +922 -0
  29. package/L2_Handling/display/composite/wizard_conditional_renderer.js +274 -0
  30. package/L2_Handling/display/display.js +30 -0
  31. package/L2_Handling/display/feedback/feedback.js +11 -0
  32. package/L2_Handling/display/feedback/progressbar_renderer.js +418 -0
  33. package/L2_Handling/display/feedback/spinner_renderer.js +246 -0
  34. package/L2_Handling/display/inputs/button_renderer.js +634 -0
  35. package/L2_Handling/display/inputs/form_renderer.js +583 -0
  36. package/L2_Handling/display/inputs/input_renderer.js +658 -0
  37. package/L2_Handling/display/inputs/inputs.js +12 -0
  38. package/L2_Handling/display/navigation/menu_renderer.js +206 -0
  39. package/L2_Handling/display/navigation/navigation.js +11 -0
  40. package/L2_Handling/display/navigation/navigation_renderer.js +703 -0
  41. package/L2_Handling/display/orchestration/orchestration.js +11 -0
  42. package/L2_Handling/display/orchestration/renderer.js +430 -0
  43. package/L2_Handling/display/orchestration/zdisplay_orchestrator.js +1759 -0
  44. package/L2_Handling/display/outputs/alert_renderer.js +161 -0
  45. package/L2_Handling/display/outputs/audio_renderer.js +94 -0
  46. package/L2_Handling/display/outputs/card_renderer.js +229 -0
  47. package/L2_Handling/display/outputs/code_renderer.js +66 -0
  48. package/L2_Handling/display/outputs/dl_renderer.js +131 -0
  49. package/L2_Handling/display/outputs/header_renderer.js +162 -0
  50. package/L2_Handling/display/outputs/icon_renderer.js +107 -0
  51. package/L2_Handling/display/outputs/image_renderer.js +145 -0
  52. package/L2_Handling/display/outputs/list_renderer.js +190 -0
  53. package/L2_Handling/display/outputs/outputs.js +19 -0
  54. package/L2_Handling/display/outputs/table_renderer.js +765 -0
  55. package/L2_Handling/display/outputs/text_renderer.js +818 -0
  56. package/L2_Handling/display/outputs/typography_renderer.js +293 -0
  57. package/L2_Handling/display/outputs/video_renderer.js +116 -0
  58. package/L2_Handling/display/primitives/document_structure_primitives.js +319 -0
  59. package/L2_Handling/display/primitives/form_primitives.js +526 -0
  60. package/L2_Handling/display/primitives/generic_containers.js +109 -0
  61. package/L2_Handling/display/primitives/interactive_primitives.js +305 -0
  62. package/L2_Handling/display/primitives/link_primitives.js +552 -0
  63. package/L2_Handling/display/primitives/lists_primitives.js +262 -0
  64. package/L2_Handling/display/primitives/media_primitives.js +383 -0
  65. package/L2_Handling/display/primitives/primitives.js +19 -0
  66. package/L2_Handling/display/primitives/semantic_element_primitive.js +226 -0
  67. package/L2_Handling/display/primitives/table_primitives.js +528 -0
  68. package/L2_Handling/display/primitives/typography_primitives.js +175 -0
  69. package/L2_Handling/display/specialized/input_request_renderer.js +467 -0
  70. package/L2_Handling/display/specialized/specialized.js +10 -0
  71. package/L2_Handling/hooks/hooks.js +9 -0
  72. package/L2_Handling/hooks/menu_integration.js +57 -0
  73. package/L2_Handling/hooks/widget_hook_manager.js +292 -0
  74. package/L2_Handling/message/message.js +8 -0
  75. package/L2_Handling/message/message_handler.js +701 -0
  76. package/L2_Handling/navigation/navigation.js +8 -0
  77. package/L2_Handling/navigation/navigation_manager.js +403 -0
  78. package/L2_Handling/zhooks/features/cache_live.js +287 -0
  79. package/L2_Handling/zhooks/features/crumbs_live.js +292 -0
  80. package/L2_Handling/zhooks/zhooks_manager.js +65 -0
  81. package/L2_Handling/zvaf/zvaf.js +8 -0
  82. package/L2_Handling/zvaf/zvaf_manager.js +334 -0
  83. package/L3_Abstraction/L3_Abstraction.js +12 -0
  84. package/L3_Abstraction/orchestrator/container_unwrapper.js +101 -0
  85. package/L3_Abstraction/orchestrator/group_renderer.js +698 -0
  86. package/L3_Abstraction/orchestrator/input_event_handler.js +797 -0
  87. package/L3_Abstraction/orchestrator/metadata_processor.js +249 -0
  88. package/L3_Abstraction/orchestrator/navbar_builder.js +201 -0
  89. package/L3_Abstraction/orchestrator/orchestrator.js +13 -0
  90. package/L3_Abstraction/orchestrator/wizard_gate_handler.js +360 -0
  91. package/L3_Abstraction/renderer/renderer.js +1 -0
  92. package/L3_Abstraction/session/session.js +1 -0
  93. package/L4_Orchestration/L4_Orchestration.js +11 -0
  94. package/L4_Orchestration/client/client.js +1 -0
  95. package/L4_Orchestration/facade/facade.js +9 -0
  96. package/L4_Orchestration/facade/manager_registry.js +118 -0
  97. package/L4_Orchestration/facade/renderer_registry.js +274 -0
  98. package/L4_Orchestration/lifecycle/asset_loader.js +255 -0
  99. package/L4_Orchestration/lifecycle/initializer.js +135 -0
  100. package/L4_Orchestration/lifecycle/lifecycle.js +8 -0
  101. package/L4_Orchestration/rendering/facade.js +94 -0
  102. package/L4_Orchestration/rendering/rendering.js +7 -0
  103. package/LICENSE +21 -0
  104. package/README.md +82 -0
  105. package/bifrost_client.js +204 -0
  106. package/bifrost_core.js +1686 -0
  107. package/docs/ARCHITECTURE.md +111 -0
  108. package/docs/PROTOCOL.md +106 -0
  109. package/docs/RENDERERS.md +101 -0
  110. package/docs/SECURITY.md +92 -0
  111. package/package.json +24 -0
  112. package/syntax/prism-zconfig.js +41 -0
  113. package/syntax/prism-zenv.js +69 -0
  114. package/syntax/prism-zolo-theme.css +288 -0
  115. package/syntax/prism-zolo.js +380 -0
  116. package/syntax/prism-zschema.js +38 -0
  117. package/syntax/prism-zspark.js +25 -0
  118. package/syntax/prism-zui.js +68 -0
  119. package/zSys/accessibility/accessibility.js +10 -0
  120. package/zSys/accessibility/emoji_accessibility.js +173 -0
  121. package/zSys/dom/block_utils.js +122 -0
  122. package/zSys/dom/container_utils.js +370 -0
  123. package/zSys/dom/dom.js +13 -0
  124. package/zSys/dom/dom_utils.js +328 -0
  125. package/zSys/dom/encoding_utils.js +117 -0
  126. package/zSys/dom/style_utils.js +71 -0
  127. package/zSys/errors/error_display.js +299 -0
  128. package/zSys/errors/errors.js +10 -0
  129. package/zSys/theme/color_utils.js +274 -0
  130. package/zSys/theme/dark_mode_utils.js +272 -0
  131. package/zSys/theme/size_utils.js +256 -0
  132. package/zSys/theme/spacing_utils.js +405 -0
  133. package/zSys/theme/theme.js +14 -0
  134. package/zSys/theme/zbase.css +1735 -0
  135. package/zSys/theme/zbase_inject.js +161 -0
  136. package/zSys/theme/ztheme_utils.js +305 -0
  137. package/zSys/validation/error_boundary.js +201 -0
  138. package/zSys/validation/validation.js +11 -0
  139. package/zSys/validation/validation_utils.js +238 -0
  140. package/zSys/zSys.js +14 -0
@@ -0,0 +1,161 @@
1
+ /**
2
+ * zbase_inject.js — zbifrost-client JS behaviors (tabs, zTheme)
3
+ *
4
+ * Called once during bifrost_core.js startup:
5
+ * import { injectZBase } from `${BASE_URL}zSys/theme/zbase_inject.js`;
6
+ * await injectZBase(BASE_URL);
7
+ *
8
+ * CSS is now injected server-side by route_dispatcher.py as a synchronous
9
+ * <link rel="stylesheet"> in <head> — no async fetch, no timing races.
10
+ * This module only exposes window.zTheme (tabs, list-group behaviors).
11
+ */
12
+
13
+ // ── Tab behavior ──────────────────────────────────────────────────────────────
14
+
15
+ function _showTab(trigger) {
16
+ const targetSelector = trigger.getAttribute('data-bs-target') || trigger.getAttribute('href');
17
+ if (!targetSelector) return;
18
+
19
+ const targetPane = document.querySelector(targetSelector);
20
+ if (!targetPane) return;
21
+
22
+ const tabContent = targetPane.closest('.zTab-content');
23
+ const nav = trigger.closest('.zNav, [role="tablist"]');
24
+
25
+ // Deactivate all triggers in the same nav group
26
+ const allTriggers = nav
27
+ ? nav.querySelectorAll('[data-bs-toggle="tab"]')
28
+ : [trigger];
29
+
30
+ allTriggers.forEach(t => {
31
+ t.classList.remove('zActive');
32
+ t.setAttribute('aria-selected', 'false');
33
+ t.setAttribute('tabindex', '-1');
34
+ });
35
+
36
+ // Activate the clicked trigger
37
+ trigger.classList.add('zActive');
38
+ trigger.setAttribute('aria-selected', 'true');
39
+ trigger.removeAttribute('tabindex');
40
+
41
+ // Hide all panes in the same tab-content
42
+ if (tabContent) {
43
+ tabContent.querySelectorAll('.zTab-pane').forEach(p => {
44
+ p.classList.remove('zActive', 'zShow');
45
+ });
46
+ }
47
+
48
+ // Show target pane
49
+ if (targetPane.classList.contains('zFade')) {
50
+ targetPane.classList.add('zActive');
51
+ requestAnimationFrame(() => targetPane.classList.add('zShow'));
52
+ } else {
53
+ targetPane.classList.add('zActive');
54
+ }
55
+
56
+ // Fire custom event for lazy-loading hooks
57
+ trigger.dispatchEvent(new CustomEvent('zTabShown', {
58
+ bubbles: true,
59
+ detail: { trigger, pane: targetPane }
60
+ }));
61
+ }
62
+
63
+ function initTabs() {
64
+ const triggers = document.querySelectorAll('[data-bs-toggle="tab"]');
65
+ if (triggers.length === 0) return;
66
+
67
+ triggers.forEach(trigger => {
68
+ if (trigger._zTabInited) return;
69
+ trigger._zTabInited = true;
70
+
71
+ trigger.setAttribute('role', 'tab');
72
+ if (trigger.classList.contains('zActive')) {
73
+ trigger.setAttribute('aria-selected', 'true');
74
+ } else {
75
+ trigger.setAttribute('aria-selected', 'false');
76
+ trigger.setAttribute('tabindex', '-1');
77
+ }
78
+
79
+ trigger.addEventListener('click', e => {
80
+ e.preventDefault();
81
+ if (!trigger.classList.contains('zActive')) _showTab(trigger);
82
+ });
83
+ });
84
+ }
85
+
86
+ // ── List-group tab behavior ───────────────────────────────────────────────────
87
+
88
+ function initListGroup() {
89
+ const triggers = document.querySelectorAll('[data-bs-toggle="list"]');
90
+ if (triggers.length === 0) return;
91
+
92
+ triggers.forEach(trigger => {
93
+ if (trigger._zListInited) return;
94
+ trigger._zListInited = true;
95
+
96
+ trigger.setAttribute('aria-selected', trigger.classList.contains('zActive') ? 'true' : 'false');
97
+
98
+ trigger.addEventListener('click', e => {
99
+ e.preventDefault();
100
+ if (trigger.classList.contains('zActive')) return;
101
+
102
+ const targetId = trigger.getAttribute('href') || trigger.getAttribute('data-bs-target');
103
+ const targetPane = targetId ? document.querySelector(targetId) : null;
104
+ if (!targetPane) return;
105
+
106
+ const listGroup = trigger.closest('.zList-group');
107
+ const tabContent = targetPane.closest('.zTab-content');
108
+
109
+ if (listGroup) {
110
+ listGroup.querySelectorAll('.zList-group-item').forEach(item => {
111
+ item.classList.remove('zActive');
112
+ item.setAttribute('aria-selected', 'false');
113
+ });
114
+ }
115
+ trigger.classList.add('zActive');
116
+ trigger.setAttribute('aria-selected', 'true');
117
+
118
+ if (tabContent) {
119
+ tabContent.querySelectorAll('.zTab-pane').forEach(p => p.classList.remove('zActive', 'zShow'));
120
+ }
121
+
122
+ if (targetPane.classList.contains('zFade')) {
123
+ targetPane.classList.add('zActive');
124
+ requestAnimationFrame(() => targetPane.classList.add('zShow'));
125
+ } else {
126
+ targetPane.classList.add('zActive');
127
+ }
128
+
129
+ trigger.dispatchEvent(new CustomEvent('zTabShown', {
130
+ bubbles: true,
131
+ detail: { trigger, pane: targetPane }
132
+ }));
133
+ });
134
+ });
135
+ }
136
+
137
+ // ── window.zTheme public API ──────────────────────────────────────────────────
138
+
139
+ function _exposeWindowZTheme() {
140
+ if (typeof window === 'undefined') return;
141
+ if (window.zTheme?._zbifrost) return; // already set by this module
142
+
143
+ window.zTheme = {
144
+ _zbifrost: true, // sentinel: sourced from zbifrost-client, not external CDN
145
+ initTabs,
146
+ initListGroup,
147
+ };
148
+ }
149
+
150
+ // ── Entry point ───────────────────────────────────────────────────────────────
151
+
152
+ /**
153
+ * Inject structural CSS and expose window.zTheme.
154
+ * Called once from bifrost_core.js during _onConnect().
155
+ *
156
+ * @param {string} baseUrl - CDN base URL (same as bifrost_core.js BASE_URL)
157
+ */
158
+ export async function injectZBase(_baseUrl) {
159
+ _exposeWindowZTheme();
160
+ // CSS is injected server-side — nothing async to do here.
161
+ }
@@ -0,0 +1,305 @@
1
+ /**
2
+ *
3
+ * zTheme Utilities - zCLI to zTheme Class Mappings
4
+ *
5
+ *
6
+ * Pure functions for mapping zCLI color/style names to zTheme CSS classes.
7
+ * Uses lookup tables for O(1) performance and consistency.
8
+ *
9
+ * @module utils/ztheme_utils
10
+ * @layer 2
11
+ * @pattern Pure Functions
12
+ *
13
+ * Dependencies:
14
+ * - Layer 0: None (pure JavaScript)
15
+ *
16
+ * Exports:
17
+ * - getButtonColorClass: Map zCLI color to zTheme button class
18
+ * - getButtonSizeClass: Map size to zTheme button size class
19
+ * - getButtonStyleClass: Map style variant to zTheme button style class
20
+ * - getAlertColorClass: Map event type to zTheme alert class
21
+ * - getBadgeColorClass: Map zCLI color to zTheme badge class
22
+ * - getTextColorClass: Map zCLI color to zTheme text color class
23
+ * - isValidZThemeClass: Validate if string is valid zTheme class
24
+ * - indentToHeaderTag: Convert indent level to HTML header tag
25
+ *
26
+ * Example:
27
+ * ```javascript
28
+ * import { getButtonColorClass, getButtonSizeClass } from './ztheme_utils.js';
29
+ *
30
+ * const colorClass = getButtonColorClass('primary'); // 'zBtnPrimary'
31
+ * const sizeClass = getButtonSizeClass('lg'); // 'zBtn-lg'
32
+ * ```
33
+ */
34
+
35
+ //
36
+ // Color Mappings (zCLI → zTheme)
37
+ //
38
+
39
+ /**
40
+ * Button color mapping: zCLI color names → zTheme button classes
41
+ * @const {Object<string, string>}
42
+ */
43
+ const BUTTON_COLOR_MAP = {
44
+ 'primary': 'zBtn-primary',
45
+ 'secondary': 'zBtn-secondary',
46
+ 'success': 'zBtn-success',
47
+ 'danger': 'zBtn-danger',
48
+ 'warning': 'zBtn-warning',
49
+ 'info': 'zBtn-info',
50
+ 'light': 'zBtn-light',
51
+ 'dark': 'zBtn-dark',
52
+ 'link': 'zBtn-link'
53
+ };
54
+
55
+ /**
56
+ * Alert color mapping: zCLI event types → zTheme zSignal classes
57
+ * Returns ONLY the color-specific class (base zSignal class added by renderer)
58
+ * @const {Object<string, string>}
59
+ */
60
+ const ALERT_COLOR_MAP = {
61
+ 'error': 'zSignal-error',
62
+ 'warning': 'zSignal-warning',
63
+ 'success': 'zSignal-success',
64
+ 'info': 'zSignal-info',
65
+ 'danger': 'zSignal-error', // danger → error (zTheme uses error)
66
+ 'primary': 'zSignal-primary',
67
+ 'secondary': 'zSignal-secondary',
68
+ 'light': 'zSignal-light',
69
+ 'dark': 'zSignal-dark'
70
+ };
71
+
72
+ /**
73
+ * Badge color mapping: zCLI colors → zTheme badge classes
74
+ * @const {Object<string, string>}
75
+ */
76
+ const BADGE_COLOR_MAP = {
77
+ 'primary': 'zBadge zBgPrimary',
78
+ 'secondary': 'zBadge zBgSecondary',
79
+ 'success': 'zBadge zBgSuccess',
80
+ 'danger': 'zBadge zBgDanger',
81
+ 'warning': 'zBadge zBgWarning',
82
+ 'info': 'zBadge zBgInfo',
83
+ 'light': 'zBadge zBgLight',
84
+ 'dark': 'zBadge zBgDark'
85
+ };
86
+
87
+ /**
88
+ * Text color mapping: zCLI colors → zTheme text color classes
89
+ * @const {Object<string, string>}
90
+ */
91
+ const TEXT_COLOR_MAP = {
92
+ 'primary': 'zText-primary',
93
+ 'secondary': 'zText-secondary',
94
+ 'success': 'zText-success',
95
+ 'danger': 'zText-danger',
96
+ 'warning': 'zText-warning',
97
+ 'info': 'zText-info',
98
+ 'light': 'zText-light',
99
+ 'dark': 'zText-dark',
100
+ 'muted': 'zText-muted',
101
+ 'white': 'zText-white'
102
+ };
103
+
104
+ //
105
+ // Button Utilities
106
+ //
107
+
108
+ /**
109
+ * Get zTheme button color class from zCLI color name
110
+ *
111
+ * @param {string} zColor - zCLI color name (primary, danger, success, etc)
112
+ * @returns {string} zTheme button class (zBtn-primary, zBtn-danger, etc)
113
+ *
114
+ * @example
115
+ * getButtonColorClass('primary') // 'zBtn-primary'
116
+ * getButtonColorClass('DANGER') // 'zBtn-danger' (case-insensitive)
117
+ * getButtonColorClass('invalid') // 'zBtn-primary' (fallback)
118
+ * getButtonColorClass(null) // 'zBtn-primary' (fallback)
119
+ */
120
+ export function getButtonColorClass(zColor) {
121
+ const normalized = zColor?.toLowerCase() || 'primary';
122
+ return BUTTON_COLOR_MAP[normalized] || 'zBtn-primary';
123
+ }
124
+
125
+ /**
126
+ * Get zTheme button size class
127
+ *
128
+ * @param {string} size - Size (sm, md, lg)
129
+ * @returns {string} zTheme size class (zBtn-sm, zBtn-lg, or empty string for md)
130
+ *
131
+ * @example
132
+ * getButtonSizeClass('sm') // 'zBtn-sm'
133
+ * getButtonSizeClass('md') // '' (default, no class needed)
134
+ * getButtonSizeClass('lg') // 'zBtn-lg'
135
+ * getButtonSizeClass(null) // '' (default)
136
+ */
137
+ export function getButtonSizeClass(size) {
138
+ const normalized = size?.toLowerCase() || 'md';
139
+
140
+ const SIZE_MAP = {
141
+ 'sm': 'zBtn-sm',
142
+ 'small': 'zBtn-sm',
143
+ 'md': '',
144
+ 'medium': '',
145
+ 'lg': 'zBtn-lg',
146
+ 'large': 'zBtn-lg'
147
+ };
148
+
149
+ return SIZE_MAP[normalized] || '';
150
+ }
151
+
152
+ /**
153
+ * Get zTheme button style variant class
154
+ *
155
+ * NOTE: For outline buttons, use getButtonOutlineClass() instead.
156
+ * This only handles non-color-specific styles.
157
+ *
158
+ * @param {string} style - Style variant (default, link)
159
+ * @returns {string} zTheme style class or empty for default
160
+ *
161
+ * @example
162
+ * getButtonStyleClass('default') // '' (no class needed)
163
+ * getButtonStyleClass('link') // 'zBtn-link'
164
+ */
165
+ export function getButtonStyleClass(style) {
166
+ const normalized = style?.toLowerCase() || 'default';
167
+
168
+ const STYLE_MAP = {
169
+ 'default': '',
170
+ 'solid': '',
171
+ 'link': 'zBtn-link'
172
+ };
173
+
174
+ return STYLE_MAP[normalized] || '';
175
+ }
176
+
177
+ /**
178
+ * Get zTheme outline button class (color-specific)
179
+ *
180
+ * @param {string} zColor - zCLI color name
181
+ * @returns {string} zTheme outline button class (e.g., 'zBtn-outline-primary')
182
+ *
183
+ * @example
184
+ * getButtonOutlineClass('primary') // 'zBtn-outline-primary'
185
+ * getButtonOutlineClass('danger') // 'zBtn-outline-danger'
186
+ */
187
+ export function getButtonOutlineClass(zColor) {
188
+ const normalized = zColor?.toLowerCase() || 'primary';
189
+ return `zBtn-outline-${normalized}`;
190
+ }
191
+
192
+ //
193
+ // Alert Utilities
194
+ //
195
+
196
+ /**
197
+ * Get zTheme signal color class from zCLI event type
198
+ *
199
+ * Returns ONLY the color-specific class (e.g., 'zSignal-success').
200
+ * The base 'zSignal' class should be added separately by the renderer.
201
+ *
202
+ * @param {string} eventType - zCLI event type (error, warning, success, info)
203
+ * @returns {string} zTheme signal color class (e.g., 'zSignal-success')
204
+ *
205
+ * @example
206
+ * getAlertColorClass('error') // 'zSignal-error'
207
+ * getAlertColorClass('warning') // 'zSignal-warning'
208
+ * getAlertColorClass('success') // 'zSignal-success'
209
+ * getAlertColorClass('info') // 'zSignal-info'
210
+ */
211
+ export function getAlertColorClass(eventType) {
212
+ const normalized = eventType?.toLowerCase() || 'info';
213
+ return ALERT_COLOR_MAP[normalized] || 'zSignal-info';
214
+ }
215
+
216
+ //
217
+ // Badge Utilities
218
+ //
219
+
220
+ /**
221
+ * Get zTheme badge color classes from zCLI color
222
+ *
223
+ * @param {string} zColor - zCLI color name
224
+ * @returns {string} zTheme badge classes (e.g., 'zBadge zBgPrimary')
225
+ *
226
+ * @example
227
+ * getBadgeColorClass('primary') // 'zBadge zBgPrimary'
228
+ * getBadgeColorClass('danger') // 'zBadge zBgDanger'
229
+ */
230
+ export function getBadgeColorClass(zColor) {
231
+ const normalized = zColor?.toLowerCase() || 'primary';
232
+ return BADGE_COLOR_MAP[normalized] || 'zBadge zBgPrimary';
233
+ }
234
+
235
+ //
236
+ // Text Utilities
237
+ //
238
+
239
+ /**
240
+ * Get zTheme text color class from zCLI color
241
+ *
242
+ * @param {string} zColor - zCLI color name
243
+ * @returns {string} zTheme text color class (e.g., 'zText-primary')
244
+ *
245
+ * @example
246
+ * getTextColorClass('primary') // 'zText-primary'
247
+ * getTextColorClass('danger') // 'zText-danger'
248
+ * getTextColorClass('muted') // 'zText-muted'
249
+ */
250
+ export function getTextColorClass(zColor) {
251
+ const normalized = zColor?.toLowerCase() || 'dark';
252
+ return TEXT_COLOR_MAP[normalized] || 'zText-dark';
253
+ }
254
+
255
+ //
256
+ // Typography Utilities
257
+ //
258
+
259
+ /**
260
+ * Convert indent level to HTML header tag
261
+ *
262
+ * NOTE: indent 0 is reserved for special cases (flush/hero layouts).
263
+ * This function maps indent 1-6 directly to h1-h6 semantic HTML tags.
264
+ *
265
+ * @param {number} indent - Indent level (1-6, where 1=h1, 6=h6)
266
+ * @returns {string} HTML tag name ('h1' through 'h6')
267
+ *
268
+ * @example
269
+ * indentToHeaderTag(1) // 'h1'
270
+ * indentToHeaderTag(2) // 'h2'
271
+ * indentToHeaderTag(6) // 'h6'
272
+ * indentToHeaderTag(10) // 'h6' (capped at h6)
273
+ */
274
+ export function indentToHeaderTag(indent) {
275
+ // Clamp indent between 1 and 6 (indent 0 reserved for special cases)
276
+ const level = Math.max(1, Math.min(6, parseInt(indent) || 1));
277
+ return `h${level}`;
278
+ }
279
+
280
+ //
281
+ // Validation Utilities
282
+ //
283
+
284
+ /**
285
+ * Validate if a string is a valid zTheme class name
286
+ *
287
+ * @param {string} className - Class name to validate
288
+ * @returns {boolean} True if valid zTheme class
289
+ *
290
+ * @example
291
+ * isValidZThemeClass('zBtn') // true
292
+ * isValidZThemeClass('zBtnPrimary') // true
293
+ * isValidZThemeClass('zAlert') // true
294
+ * isValidZThemeClass('not-ztheme') // false
295
+ * isValidZThemeClass('btn-primary') // false
296
+ */
297
+ export function isValidZThemeClass(className) {
298
+ if (!className || typeof className !== 'string') {
299
+ return false;
300
+ }
301
+
302
+ // zTheme classes start with 'z' followed by uppercase letter
303
+ return /^z[A-Z]/.test(className);
304
+ }
305
+
@@ -0,0 +1,201 @@
1
+ /**
2
+ *
3
+ * Error Boundary Utility - Standardized Error Handling for Renderers
4
+ *
5
+ *
6
+ * Provides error boundary wrappers for renderer methods to ensure
7
+ * graceful degradation when rendering fails.
8
+ */
9
+
10
+ // ─────────────────────────────────────────────────────────────────
11
+ // Imports
12
+ // ─────────────────────────────────────────────────────────────────
13
+
14
+ // Layer 2: Utilities
15
+ import { createElement } from '../dom/dom_utils.js';
16
+
17
+ /**
18
+ * Create a fallback error UI element
19
+ * @param {Object} errorInfo - Error information
20
+ * @param {Error} errorInfo.error - The error that occurred
21
+ * @param {string} errorInfo.component - Component name that failed
22
+ * @param {Object} errorInfo.data - Data that was being rendered
23
+ * @returns {HTMLElement} Error display element
24
+ */
25
+ export function createErrorFallback(errorInfo) {
26
+ const { error, component = 'Component', data: _data = {} } = errorInfo;
27
+
28
+ const container = createElement('div');
29
+ container.className = 'bifrost-render-error';
30
+ container.style.cssText = `
31
+ background: #fee;
32
+ border-left: 4px solid #c33;
33
+ padding: 1rem;
34
+ margin: 0.5rem 0;
35
+ border-radius: 4px;
36
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
37
+ font-size: 0.875rem;
38
+ `;
39
+
40
+ const title = createElement('div');
41
+ title.style.cssText = 'font-weight: 600; color: #c33; margin-bottom: 0.5rem;';
42
+ title.textContent = `[WARN] ${component} Render Error`;
43
+
44
+ const message = createElement('div');
45
+ message.style.cssText = 'color: #666; margin-bottom: 0.5rem;';
46
+ message.textContent = error.message || 'An error occurred while rendering this component';
47
+
48
+ const details = createElement('details');
49
+ details.style.cssText = 'margin-top: 0.5rem;';
50
+
51
+ const summary = createElement('summary');
52
+ summary.style.cssText = 'cursor: pointer; color: #999; font-size: 0.75rem;';
53
+ summary.textContent = 'Show technical details';
54
+
55
+ const stack = createElement('pre');
56
+ stack.style.cssText = `
57
+ margin-top: 0.5rem;
58
+ padding: 0.5rem;
59
+ background: #f9f9f9;
60
+ border-radius: 4px;
61
+ overflow-x: auto;
62
+ font-size: 0.75rem;
63
+ color: #666;
64
+ `;
65
+ stack.textContent = error.stack || 'No stack trace available';
66
+
67
+ details.appendChild(summary);
68
+ details.appendChild(stack);
69
+
70
+ container.appendChild(title);
71
+ container.appendChild(message);
72
+ container.appendChild(details);
73
+
74
+ return container;
75
+ }
76
+
77
+ /**
78
+ * Wrap a renderer method with error boundary
79
+ * @param {Function} renderFn - The render function to wrap
80
+ * @param {Object} options - Options for error handling
81
+ * @param {string} options.component - Component name for error messages
82
+ * @param {Function} options.logger - Logger instance
83
+ * @param {Function} options.onError - Optional error handler callback
84
+ * @param {boolean} options.throwOnError - Whether to re-throw errors (default: false)
85
+ * @returns {Function} Wrapped render function
86
+ */
87
+ export function withErrorBoundary(renderFn, options = {}) {
88
+ const {
89
+ component = 'Unknown',
90
+ logger = console,
91
+ onError = null,
92
+ throwOnError = false
93
+ } = options;
94
+
95
+ return function wrappedRender(...args) {
96
+ try {
97
+ const result = renderFn.apply(this, args);
98
+
99
+ // Handle async render functions
100
+ if (result && typeof result.then === 'function') {
101
+ return result.catch(error => {
102
+ logger.error(`[${component}] Async render error:`, error);
103
+
104
+ if (onError) {
105
+ try {
106
+ onError(error, args);
107
+ } catch (handlerError) {
108
+ logger.error(`[${component}] Error in error handler:`, handlerError);
109
+ }
110
+ }
111
+
112
+ if (throwOnError) {
113
+ throw error;
114
+ }
115
+
116
+ // Return fallback UI for async errors
117
+ return createErrorFallback({
118
+ error,
119
+ component,
120
+ data: args[0]
121
+ });
122
+ });
123
+ }
124
+
125
+ return result;
126
+
127
+ } catch (error) {
128
+ logger.error(`[${component}] Render error:`, error);
129
+
130
+ if (onError) {
131
+ try {
132
+ onError(error, args);
133
+ } catch (handlerError) {
134
+ logger.error(`[${component}] Error in error handler:`, handlerError);
135
+ }
136
+ }
137
+
138
+ if (throwOnError) {
139
+ throw error;
140
+ }
141
+
142
+ // Return fallback UI for sync errors
143
+ return createErrorFallback({
144
+ error,
145
+ component,
146
+ data: args[0]
147
+ });
148
+ }
149
+ };
150
+ }
151
+
152
+ /**
153
+ * Create a safe wrapper for renderer classes
154
+ * @param {Class} RendererClass - The renderer class to wrap
155
+ * @param {Object} options - Options for error handling
156
+ * @returns {Class} Wrapped renderer class
157
+ */
158
+ export function createSafeRenderer(RendererClass, options = {}) {
159
+ return class SafeRenderer extends RendererClass {
160
+ constructor(...args) {
161
+ super(...args);
162
+
163
+ // Wrap render method if it exists
164
+ if (typeof this.render === 'function') {
165
+ const originalRender = this.render.bind(this);
166
+ this.render = withErrorBoundary(originalRender, {
167
+ component: RendererClass.name || 'Renderer',
168
+ logger: this.logger || console,
169
+ ...options
170
+ });
171
+ }
172
+
173
+ // Wrap renderHTML method if it exists
174
+ if (typeof this.renderHTML === 'function') {
175
+ const originalRenderHTML = this.renderHTML.bind(this);
176
+ this.renderHTML = withErrorBoundary(originalRenderHTML, {
177
+ component: `${RendererClass.name || 'Renderer'}.renderHTML`,
178
+ logger: this.logger || console,
179
+ ...options
180
+ });
181
+ }
182
+ }
183
+ };
184
+ }
185
+
186
+ /**
187
+ * Decorator for renderer methods (for future use with decorators proposal)
188
+ * Usage: @errorBoundary({ component: 'MyRenderer' })
189
+ */
190
+ export function errorBoundary(options = {}) {
191
+ return function decorator(target, propertyKey, descriptor) {
192
+ const originalMethod = descriptor.value;
193
+
194
+ descriptor.value = withErrorBoundary(originalMethod, {
195
+ component: `${target.constructor.name}.${propertyKey}`,
196
+ ...options
197
+ });
198
+
199
+ return descriptor;
200
+ };
201
+ }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * zSys/validation - Validation Utilities Barrel
3
+ *
4
+ * Validation and error boundary utilities.
5
+ *
6
+ * @module zSys/validation
7
+ * @layer zSys (System Utilities)
8
+ */
9
+
10
+ export * from './error_boundary.js';
11
+ export * from './validation_utils.js';