@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,274 @@
1
+ /**
2
+ * L4_Orchestration/facade/renderer_registry.js
3
+ *
4
+ * Renderer Registry - Centralized Lazy Loading for All Renderers
5
+ *
6
+ * Consolidates 16 individual _ensure*Renderer() methods from bifrost_client.js
7
+ * into a single registry-based loader.
8
+ *
9
+ * Extracted from bifrost_client.js (Task 0, Step 1.2)
10
+ *
11
+ * @module facade/renderer_registry
12
+ * @layer L4 (Orchestration)
13
+ */
14
+
15
+ /**
16
+ * Renderer Registry - Maps renderer types to their module paths and classes
17
+ *
18
+ * TODO: Tiered initialization for publish-time performance
19
+ * ──────────────────────────────────────────────────────────
20
+ * Add a `priority` field to each entry:
21
+ * - 'critical' → load before WebSocket connects (blocks first paint)
22
+ * - 'deferred' → load after first chunk renders (requestIdleCallback / post-paint hook)
23
+ *
24
+ * Critical tier (must exist before first chunk renders):
25
+ * typography, text, header (header_renderer if separate), list
26
+ *
27
+ * Deferred tier (never in a typical first chunk):
28
+ * code, card, button, table, dl, image, icon, navigation,
29
+ * dashboard, swiper, terminal, spinner, progressBar, form, menu
30
+ *
31
+ * Implementation sketch:
32
+ * 1. Add `priority: 'critical' | 'deferred'` to each entry below
33
+ * 2. In asset_loader.js (or initializer.js), call
34
+ * rendererRegistry.preloadTier('critical') before WS connect
35
+ * 3. After first-paint signal (first WebSocket chunk ACK), call
36
+ * rendererRegistry.preloadTier('deferred') via requestIdleCallback
37
+ * 4. ensureRenderer() stays unchanged — cache hit on any pre-loaded renderer
38
+ *
39
+ * Expected gain: time-to-interactive drops from ~40 module fetches to ~4-5
40
+ * before first content paint, remainder loads invisibly in background.
41
+ */
42
+ export const RENDERER_REGISTRY = {
43
+ // Outputs - Typography & Text
44
+ typography: {
45
+ path: 'L2_Handling/display/outputs/typography_renderer.js',
46
+ className: 'TypographyRenderer',
47
+ isDefault: true,
48
+ passClient: false
49
+ },
50
+ text: {
51
+ path: 'L2_Handling/display/outputs/text_renderer.js',
52
+ className: 'TextRenderer',
53
+ isDefault: true,
54
+ passClient: false
55
+ },
56
+ code: {
57
+ path: 'L2_Handling/display/outputs/code_renderer.js',
58
+ className: 'CodeRenderer',
59
+ isDefault: true,
60
+ passClient: false
61
+ },
62
+
63
+ // Outputs - Cards & Buttons
64
+ card: {
65
+ path: 'L2_Handling/display/outputs/card_renderer.js',
66
+ className: 'CardRenderer',
67
+ isDefault: true,
68
+ passClient: false
69
+ },
70
+ button: {
71
+ path: 'L2_Handling/display/inputs/button_renderer.js',
72
+ className: 'ButtonRenderer',
73
+ isDefault: true,
74
+ passClient: true // ButtonRenderer needs client for event handling
75
+ },
76
+
77
+ // Outputs - Data Display
78
+ table: {
79
+ path: 'L2_Handling/display/outputs/table_renderer.js',
80
+ className: 'TableRenderer',
81
+ isDefault: true,
82
+ passClient: false
83
+ },
84
+ list: {
85
+ path: 'L2_Handling/display/outputs/list_renderer.js',
86
+ className: 'ListRenderer',
87
+ isDefault: true,
88
+ passClient: true // ListRenderer needs client for nested rendering
89
+ },
90
+ dl: {
91
+ path: 'L2_Handling/display/outputs/dl_renderer.js',
92
+ className: 'DLRenderer',
93
+ isDefault: false,
94
+ passClient: false,
95
+ useModuleRegistry: true // Uses MODULE_REGISTRY for loading
96
+ },
97
+
98
+ // Outputs - Media
99
+ image: {
100
+ path: 'L2_Handling/display/outputs/image_renderer.js',
101
+ className: 'ImageRenderer',
102
+ isDefault: true,
103
+ passClient: false
104
+ },
105
+ video: {
106
+ path: 'L2_Handling/display/outputs/video_renderer.js',
107
+ className: 'VideoRenderer',
108
+ isDefault: true,
109
+ passClient: false
110
+ },
111
+ audio: {
112
+ path: 'L2_Handling/display/outputs/audio_renderer.js',
113
+ className: 'AudioRenderer',
114
+ isDefault: true,
115
+ passClient: false
116
+ },
117
+ icon: {
118
+ path: 'L2_Handling/display/outputs/icon_renderer.js',
119
+ className: 'IconRenderer',
120
+ isDefault: true,
121
+ passClient: false
122
+ },
123
+
124
+ // Outputs - Navigation
125
+ navigation: {
126
+ path: 'L2_Handling/display/outputs/navigation_renderer.js',
127
+ className: 'NavigationRenderer',
128
+ isDefault: false,
129
+ passClient: true, // NavigationRenderer needs client for link primitives
130
+ useModuleRegistry: true
131
+ },
132
+
133
+ // Composite - Complex Components
134
+ dashboard: {
135
+ path: 'L2_Handling/display/composite/dashboard_renderer.js',
136
+ className: 'DashboardRenderer',
137
+ isDefault: true,
138
+ passClient: true // DashboardRenderer needs client for nested rendering
139
+ },
140
+ swiper: {
141
+ path: 'L2_Handling/display/composite/swiper_renderer.js',
142
+ className: 'SwiperRenderer',
143
+ isDefault: true,
144
+ passClient: false
145
+ },
146
+ terminal: {
147
+ path: 'L2_Handling/display/composite/terminal_renderer.js',
148
+ className: 'TerminalRenderer',
149
+ isDefault: true,
150
+ passClient: true, // TerminalRenderer needs client for execution
151
+ exposeToWindow: true // Expose to window._TerminalRenderer for message handler
152
+ },
153
+
154
+ // Feedback - UI State
155
+ spinner: {
156
+ path: 'L2_Handling/display/feedback/spinner_renderer.js',
157
+ className: 'SpinnerRenderer',
158
+ isDefault: true,
159
+ passClient: false
160
+ },
161
+ progressBar: {
162
+ path: 'L2_Handling/display/feedback/progressbar_renderer.js',
163
+ className: 'ProgressBarRenderer',
164
+ isDefault: true,
165
+ passClient: false
166
+ },
167
+
168
+ // Inputs - Forms
169
+ form: {
170
+ path: 'L2_Handling/display/inputs/form_renderer.js',
171
+ className: 'FormRenderer',
172
+ isDefault: false,
173
+ passClient: true, // FormRenderer needs client for form handling
174
+ useModuleRegistry: true
175
+ },
176
+ menu: {
177
+ path: 'L2_Handling/display/navigation/menu_renderer.js',
178
+ className: 'MenuRenderer',
179
+ isDefault: false,
180
+ passClient: true, // MenuRenderer needs client for menu interactions
181
+ useModuleRegistry: true
182
+ }
183
+ };
184
+
185
+ /**
186
+ * RendererRegistry - Centralized renderer loading and caching
187
+ */
188
+ export class RendererRegistry {
189
+ constructor(client) {
190
+ this.client = client;
191
+ this.logger = client.logger;
192
+ this.baseUrl = client._baseUrl;
193
+ this.renderers = {}; // Cache for loaded renderers
194
+ }
195
+
196
+ /**
197
+ * Ensure a renderer is loaded and cached
198
+ * @param {string} type - Renderer type (e.g., 'typography', 'button', 'table')
199
+ * @returns {Promise<Object>} Renderer instance
200
+ */
201
+ async ensureRenderer(type) {
202
+ // Return cached renderer if already loaded
203
+ if (this.renderers[type]) {
204
+ return this.renderers[type];
205
+ }
206
+
207
+ // Get renderer config from registry
208
+ const config = RENDERER_REGISTRY[type];
209
+ if (!config) {
210
+ throw new Error(`Unknown renderer type: ${type}`);
211
+ }
212
+
213
+ // Load renderer module
214
+ let RendererClass;
215
+ if (config.useModuleRegistry) {
216
+ // Use client's _loadModule for MODULE_REGISTRY lookup
217
+ const module = await this.client._loadModule(type === 'dl' ? 'dl_renderer' :
218
+ type === 'navigation' ? 'navigation_renderer' :
219
+ type === 'form' ? 'form_renderer' :
220
+ type === 'menu' ? 'menu_renderer' : type);
221
+ RendererClass = module[config.className];
222
+ } else {
223
+ // Direct import
224
+ const fullPath = `${this.baseUrl}${config.path}`;
225
+ const module = await import(fullPath);
226
+ RendererClass = config.isDefault ? module.default : module[config.className];
227
+ }
228
+
229
+ // Instantiate renderer
230
+ const args = config.passClient ? [this.logger, this.client] : [this.logger];
231
+ const renderer = new RendererClass(...args);
232
+
233
+ // Expose to window if needed (for TerminalRenderer)
234
+ if (config.exposeToWindow) {
235
+ window[`_${config.className}`] = RendererClass;
236
+ }
237
+
238
+ // Cache and return
239
+ this.renderers[type] = renderer;
240
+ this.logger.debug(`${config.className} loaded via registry`);
241
+ return renderer;
242
+ }
243
+
244
+ /**
245
+ * Get a cached renderer (throws if not loaded)
246
+ * @param {string} type - Renderer type
247
+ * @returns {Object} Renderer instance
248
+ */
249
+ getRenderer(type) {
250
+ const renderer = this.renderers[type];
251
+ if (!renderer) {
252
+ throw new Error(`Renderer not loaded: ${type}`);
253
+ }
254
+ return renderer;
255
+ }
256
+
257
+ /**
258
+ * Check if a renderer is loaded
259
+ * @param {string} type - Renderer type
260
+ * @returns {boolean}
261
+ */
262
+ hasRenderer(type) {
263
+ return !!this.renderers[type];
264
+ }
265
+
266
+ /**
267
+ * Preload multiple renderers in parallel
268
+ * @param {string[]} types - Array of renderer types
269
+ * @returns {Promise<void>}
270
+ */
271
+ async preloadRenderers(types) {
272
+ await Promise.all(types.map(type => this.ensureRenderer(type)));
273
+ }
274
+ }
@@ -0,0 +1,255 @@
1
+ /**
2
+ * L4_Orchestration/lifecycle/asset_loader.js
3
+ *
4
+ * Asset Loading Orchestrator
5
+ *
6
+ * Manages loading of external assets and libraries:
7
+ * - Prism.js syntax highlighting (core + languages + .zolo variants)
8
+ * - _zScripts from YAML metadata (plugin scripts)
9
+ *
10
+ * Extracted from bifrost_client.js (Phase 5.4)
11
+ *
12
+ * TODO: Tiered renderer initialization for publish-time performance
13
+ * ──────────────────────────────────────────────────────────────────
14
+ * This is the right place to orchestrate the two-phase load sequence:
15
+ *
16
+ * Phase 1 — before WebSocket connect (called from initializer.js):
17
+ * await client.rendererRegistry.preloadTier('critical')
18
+ * // loads: typography, text, list — ~4 files, unblocks first paint
19
+ *
20
+ * Phase 2 — after first chunk renders (post-paint, background):
21
+ * const afterFirstPaint = () => {
22
+ * requestIdleCallback(() => client.rendererRegistry.preloadTier('deferred'));
23
+ * };
24
+ * // hook into message_handler.js first-chunk-rendered signal, or
25
+ * // use a one-shot WebSocket message ACK as the trigger
26
+ *
27
+ * Prerequisite: add `priority: 'critical' | 'deferred'` to each entry
28
+ * in renderer_registry.js (see TODO there), then add preloadTier() to
29
+ * RendererRegistry that filters by priority and calls ensureRenderer().
30
+ *
31
+ * No changes to ensureRenderer() needed — cache hit on pre-loaded renderers
32
+ * means existing lazy-load call sites stay identical.
33
+ */
34
+
35
+ export class AssetLoader {
36
+ constructor(client) {
37
+ this.client = client;
38
+ this.logger = client.logger;
39
+ }
40
+
41
+ /**
42
+ * Load _zScripts from YAML metadata (plugin scripts)
43
+ * Resolves plugin references: &.plugin_name → /plugins/plugin_name.js
44
+ */
45
+ loadZScripts() {
46
+ if (typeof document === 'undefined') {
47
+ return;
48
+ }
49
+
50
+ // Extract zScripts from zuiConfig.zMeta (renamed from _zScripts in v1.7.26)
51
+ const zScripts = this.client.zuiConfig?.zMeta?.zScripts || this.client.zuiConfig?.zMeta?._zScripts || [];
52
+
53
+ if (!Array.isArray(zScripts) || zScripts.length === 0) {
54
+ this.logger.debug('[AssetLoader] No zScripts found in YAML metadata');
55
+ return;
56
+ }
57
+
58
+ this.logger.debug('[AssetLoader] Loading %s _zScripts from YAML', zScripts.length);
59
+
60
+ zScripts.forEach(scriptRef => {
61
+ // Resolve plugin reference: &.plugin_name → /plugins/plugin_name.js
62
+ let scriptUrl = scriptRef;
63
+ if (scriptRef.startsWith('&.')) {
64
+ const pluginName = scriptRef.substring(2);
65
+ scriptUrl = `/plugins/${pluginName}.js`;
66
+ this.logger.debug('[AssetLoader] Resolving plugin: %s → %s', scriptRef, scriptUrl);
67
+ }
68
+
69
+ // Check if script already loaded
70
+ if (!document.querySelector(`script[src="${scriptUrl}"]`)) {
71
+ const script = document.createElement('script');
72
+ script.src = scriptUrl;
73
+ script.async = true;
74
+ script.onload = () => {
75
+ this.logger.debug('[AssetLoader] Loaded _zScript: %s', scriptUrl);
76
+ };
77
+ script.onerror = () => {
78
+ this.logger.error('[AssetLoader] Failed to load _zScript: %s', scriptUrl);
79
+ };
80
+ document.head.appendChild(script);
81
+ } else {
82
+ this.logger.debug('[AssetLoader] _zScript already loaded: %s', scriptUrl);
83
+ }
84
+ });
85
+ }
86
+
87
+ /**
88
+ * Load Prism.js from CDN for syntax highlighting
89
+ * Complex sequential loading: core → components → .zolo languages
90
+ */
91
+ loadPrismJS() {
92
+ if (typeof document === 'undefined') {
93
+ return;
94
+ }
95
+
96
+ this.logger.debug('[AssetLoader] Loading Prism.js...');
97
+
98
+ const prismCDN = 'https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0';
99
+ const prismTheme = `${prismCDN}/themes/prism-tomorrow.min.css`;
100
+
101
+ // Check if Prism CSS already loaded
102
+ if (!document.querySelector(`link[href="${prismTheme}"]`)) {
103
+ const link = document.createElement('link');
104
+ link.rel = 'stylesheet';
105
+ link.href = prismTheme;
106
+ document.head.appendChild(link);
107
+ this.logger.debug('[AssetLoader] Prism CSS loaded (prism-tomorrow)');
108
+ } else {
109
+ this.logger.debug('[AssetLoader] Prism CSS already loaded');
110
+ }
111
+
112
+ // Load custom .zolo theme overrides — bundled WITH the client (syntax/),
113
+ // resolved relative to this module so the zlsp palette ships built-in.
114
+ const zoloTheme = new URL('../../syntax/prism-zolo-theme.css', import.meta.url).href;
115
+ if (!document.querySelector(`link[href="${zoloTheme}"]`)) {
116
+ const link = document.createElement('link');
117
+ link.rel = 'stylesheet';
118
+ link.href = zoloTheme;
119
+ document.head.appendChild(link);
120
+ this.logger.debug('[AssetLoader] Prism .zolo custom theme loaded');
121
+ }
122
+
123
+ // Load Prism core + common languages
124
+ const scripts = [
125
+ { src: `${prismCDN}/prism.min.js`, name: 'core' },
126
+ { src: `${prismCDN}/components/prism-markup.min.js`, name: 'markup' },
127
+ { src: `${prismCDN}/components/prism-css.min.js`, name: 'css' },
128
+ { src: `${prismCDN}/components/prism-javascript.min.js`, name: 'javascript' },
129
+ { src: `${prismCDN}/components/prism-python.min.js`, name: 'python' },
130
+ { src: `${prismCDN}/components/prism-bash.min.js`, name: 'bash' },
131
+ { src: `${prismCDN}/components/prism-yaml.min.js`, name: 'yaml' }
132
+ ];
133
+
134
+ // Load scripts sequentially (Prism components depend on core)
135
+ const loadScript = (scriptInfo, index) => {
136
+ // Check if script already loaded
137
+ if (document.querySelector(`script[src="${scriptInfo.src}"]`)) {
138
+ // Already loaded, continue to next
139
+ this.logger.debug(`[AssetLoader] Prism ${scriptInfo.name} already loaded`);
140
+ if (index < scripts.length - 1) {
141
+ loadScript(scripts[index + 1], index + 1);
142
+ } else {
143
+ this.logger.debug('[AssetLoader] All Prism.js scripts already loaded');
144
+ this.loadPrismZolo();
145
+ }
146
+ return;
147
+ }
148
+
149
+ const script = document.createElement('script');
150
+ script.src = scriptInfo.src;
151
+ script.onload = () => {
152
+ // Load next script in sequence (silent)
153
+ if (index < scripts.length - 1) {
154
+ loadScript(scripts[index + 1], index + 1);
155
+ } else {
156
+ // Load custom .zolo language definition
157
+ this.loadPrismZolo();
158
+ }
159
+ };
160
+ script.onerror = () => {
161
+ this.logger.warn(`[AssetLoader] Failed to load Prism ${scriptInfo.name}`);
162
+ };
163
+ document.head.appendChild(script);
164
+ };
165
+
166
+ // Start loading chain
167
+ loadScript(scripts[0], 0);
168
+ }
169
+
170
+ /**
171
+ * Load custom .zolo language definitions for Prism.js
172
+ * Loads 6 .zolo variants: zolo, zspark, zui, zschema, zconfig, zenv
173
+ */
174
+ loadPrismZolo() {
175
+ if (typeof document === 'undefined') {
176
+ return;
177
+ }
178
+
179
+ this.logger.debug('[AssetLoader] Loading custom .zolo languages...');
180
+
181
+ // Keep dependency order deterministic: extensions rely on base "zolo".
182
+ const zoloLanguages = ['zolo', 'zspark', 'zui', 'zschema', 'zconfig', 'zenv'];
183
+ const totalLanguages = zoloLanguages.length;
184
+
185
+ // zolo grammars ship WITH the client (syntax/), not the host app. Resolve
186
+ // them relative to this module so every zApp gets zolo highlighting for
187
+ // free — no per-app /static/js/prism-*.js copies required.
188
+ const syntaxBase = new URL('../../syntax/', import.meta.url).href;
189
+
190
+ const alreadyLoaded = zoloLanguages.every((lang) => window.Prism?.languages?.[lang]);
191
+ if (alreadyLoaded) {
192
+ this.logger.debug('[AssetLoader] Prism .zolo languages already loaded');
193
+ return;
194
+ }
195
+
196
+ const finishLoad = () => {
197
+ this.logger.debug('[AssetLoader] Prism.js loaded (7 languages + %s .zolo variants)', totalLanguages);
198
+
199
+ // Rehighlight ALL code blocks rendered before grammars loaded — covers
200
+ // zolo variants AND stock languages (bash, python, …) from zMD fences.
201
+ if (window.Prism) {
202
+ const codeBlocks = document.querySelectorAll('pre code[class*="language-"]');
203
+ if (codeBlocks.length > 0) {
204
+ this.logger.debug(`[AssetLoader] Rehighlighting ${codeBlocks.length} code blocks`);
205
+ codeBlocks.forEach(block => {
206
+ Prism.highlightElement(block);
207
+ });
208
+ }
209
+ }
210
+ };
211
+
212
+ const loadLanguageSequentially = (index) => {
213
+ if (index >= totalLanguages) {
214
+ finishLoad();
215
+ return;
216
+ }
217
+
218
+ const lang = zoloLanguages[index];
219
+ const path = `${syntaxBase}prism-${lang}.js`;
220
+
221
+ // If language is already registered, continue.
222
+ if (window.Prism?.languages?.[lang]) {
223
+ this.logger.debug(`[AssetLoader] Prism ${lang} already registered`);
224
+ loadLanguageSequentially(index + 1);
225
+ return;
226
+ }
227
+
228
+ // Reuse existing script tag if present but language not yet available.
229
+ const existingScript = document.querySelector(`script[src="${path}"]`);
230
+ if (existingScript) {
231
+ existingScript.addEventListener('load', () => {
232
+ this.logger.debug(`[AssetLoader] Loaded Prism language ${index + 1}/${totalLanguages}: ${path}`);
233
+ loadLanguageSequentially(index + 1);
234
+ }, { once: true });
235
+ existingScript.addEventListener('error', () => {
236
+ this.logger.error(`[AssetLoader] Failed to load Prism language: ${path}`);
237
+ }, { once: true });
238
+ return;
239
+ }
240
+
241
+ const script = document.createElement('script');
242
+ script.src = path;
243
+ script.onload = () => {
244
+ this.logger.debug(`[AssetLoader] Loaded Prism language ${index + 1}/${totalLanguages}: ${path}`);
245
+ loadLanguageSequentially(index + 1);
246
+ };
247
+ script.onerror = () => {
248
+ this.logger.error(`[AssetLoader] Failed to load Prism language: ${path}`);
249
+ };
250
+ document.head.appendChild(script);
251
+ };
252
+
253
+ loadLanguageSequentially(0);
254
+ }
255
+ }
@@ -0,0 +1,135 @@
1
+ /**
2
+ * L4_Orchestration/lifecycle/initializer.js
3
+ *
4
+ * Initialization Orchestrator
5
+ *
6
+ * Coordinates the initialization sequence for BifrostClient:
7
+ * - zVaF elements (connection badge, navbar, content area)
8
+ * - Client-side navigation setup
9
+ * - Widget hooks registration
10
+ * - Cache hooks registration
11
+ *
12
+ * Extracted from bifrost_client.js (Phase 5.2)
13
+ */
14
+
15
+ export class Initializer {
16
+ constructor(client) {
17
+ this.client = client;
18
+ this.logger = client.logger;
19
+ }
20
+
21
+ /**
22
+ * Register default widget hooks (backward compatibility)
23
+ * Widget hooks are now registered in WidgetHookManager
24
+ */
25
+ registerDefaultWidgetHooks() {
26
+ // Widget hooks are now registered in the widget handler
27
+ // This method is kept for backward compatibility
28
+ }
29
+
30
+ /**
31
+ * Register cache-related hooks (onConnectionInfo, onDisconnected, onConnected)
32
+ */
33
+ async registerCacheHooks() {
34
+ await this.client._ensureCacheManager();
35
+ return this.client.cacheManager.registerCacheHooks();
36
+ }
37
+
38
+ /**
39
+ * Disable all forms during offline mode
40
+ */
41
+ async disableForms() {
42
+ await this.client._ensureCacheManager();
43
+ return this.client.cacheManager.disableForms();
44
+ }
45
+
46
+ /**
47
+ * Enable all forms when back online
48
+ */
49
+ async enableForms() {
50
+ await this.client._ensureCacheManager();
51
+ return this.client.cacheManager.enableForms();
52
+ }
53
+
54
+ /**
55
+ * Initialize zVaF elements (connection badges, navbar, content area)
56
+ * HTML structure (declared in zVaF.html):
57
+ * <zBifrostBadge></zBifrostBadge> ← Dynamic, always fresh
58
+ * <zNavBar></zNavBar> ← Dynamic, RBAC-aware
59
+ * <zVaF>...</zVaF> ← Cacheable content area
60
+ */
61
+ async initZVaFElements() {
62
+ await this.client._ensureZVaFManager();
63
+ return this.client.zvafManager.initZVaFElements();
64
+ }
65
+
66
+ /**
67
+ * Populate connection badge content
68
+ */
69
+ async populateConnectionBadge() {
70
+ await this.client._ensureZVaFManager();
71
+ return this.client.zvafManager.populateConnectionBadge();
72
+ }
73
+
74
+ /**
75
+ * Update badge state
76
+ * @param {string} state - 'connecting', 'connected', 'disconnected', 'error'
77
+ */
78
+ async updateBadgeState(state) {
79
+ await this.client._ensureZVaFManager();
80
+ return this.client.zvafManager.updateBadgeState(state);
81
+ }
82
+
83
+ /**
84
+ * Update badge with bifrost render status (Rendering k/N → connected)
85
+ * @param {Object} opts - { current, total, done }
86
+ */
87
+ async updateRenderState(opts) {
88
+ await this.client._ensureZVaFManager();
89
+ return this.client.zvafManager.updateRenderState(opts);
90
+ }
91
+
92
+ /**
93
+ * Populate navbar from embedded config
94
+ * Uses zuiConfig from server, fetches fresh on auth change
95
+ */
96
+ async populateNavBar() {
97
+ await this.client._ensureZVaFManager();
98
+ return this.client.zvafManager.populateNavBar();
99
+ }
100
+
101
+ /**
102
+ * Fetch fresh navbar from API and populate (used after auth state changes)
103
+ */
104
+ async fetchAndPopulateNavBar(navHtmlFromServer = null) {
105
+ await this.client._ensureZVaFManager();
106
+ return this.client.zvafManager.fetchAndPopulateNavBar(navHtmlFromServer);
107
+ }
108
+
109
+ /**
110
+ * Enable client-side navigation (SPA-style) for navbar links
111
+ * Intercepts clicks to prevent full page reloads and uses WebSocket instead
112
+ */
113
+ async enableClientSideNavigation() {
114
+ await this.client._ensureNavigationManager();
115
+ return this.client.navigationManager.enableClientSideNavigation();
116
+ }
117
+
118
+ /**
119
+ * Navigate to a route via WebSocket (client-side navigation)
120
+ * @param {string} routePath - Path to navigate to (e.g., '/zAbout', '/zAccount')
121
+ * @param {Object} options - Navigation options
122
+ */
123
+ async navigateToRoute(routePath, options = {}) {
124
+ await this.client._ensureNavigationManager();
125
+ return this.client.navigationManager.navigateToRoute(routePath, options);
126
+ }
127
+
128
+ /**
129
+ * Freeze the page currently on screen into the trail (offline-browse).
130
+ */
131
+ async snapshotCurrentPage() {
132
+ await this.client._ensureNavigationManager();
133
+ return this.client.navigationManager.snapshotCurrentPage();
134
+ }
135
+ }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * L4_Orchestration/lifecycle/lifecycle.js
3
+ *
4
+ * Lifecycle Management Barrel
5
+ */
6
+
7
+ export { Initializer } from './initializer.js';
8
+ export { AssetLoader } from './asset_loader.js';