@creact-labs/creact 0.1.0

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 (103) hide show
  1. package/LICENSE +212 -0
  2. package/README.md +379 -0
  3. package/dist/cli/commands/BuildCommand.d.ts +40 -0
  4. package/dist/cli/commands/BuildCommand.js +151 -0
  5. package/dist/cli/commands/DeployCommand.d.ts +38 -0
  6. package/dist/cli/commands/DeployCommand.js +194 -0
  7. package/dist/cli/commands/DevCommand.d.ts +52 -0
  8. package/dist/cli/commands/DevCommand.js +385 -0
  9. package/dist/cli/commands/PlanCommand.d.ts +39 -0
  10. package/dist/cli/commands/PlanCommand.js +164 -0
  11. package/dist/cli/commands/index.d.ts +36 -0
  12. package/dist/cli/commands/index.js +43 -0
  13. package/dist/cli/core/ArgumentParser.d.ts +46 -0
  14. package/dist/cli/core/ArgumentParser.js +127 -0
  15. package/dist/cli/core/BaseCommand.d.ts +75 -0
  16. package/dist/cli/core/BaseCommand.js +95 -0
  17. package/dist/cli/core/CLIContext.d.ts +68 -0
  18. package/dist/cli/core/CLIContext.js +183 -0
  19. package/dist/cli/core/CommandRegistry.d.ts +64 -0
  20. package/dist/cli/core/CommandRegistry.js +89 -0
  21. package/dist/cli/core/index.d.ts +36 -0
  22. package/dist/cli/core/index.js +43 -0
  23. package/dist/cli/index.d.ts +35 -0
  24. package/dist/cli/index.js +100 -0
  25. package/dist/cli/output.d.ts +204 -0
  26. package/dist/cli/output.js +437 -0
  27. package/dist/cli/utils.d.ts +59 -0
  28. package/dist/cli/utils.js +76 -0
  29. package/dist/context/createContext.d.ts +90 -0
  30. package/dist/context/createContext.js +113 -0
  31. package/dist/context/index.d.ts +30 -0
  32. package/dist/context/index.js +35 -0
  33. package/dist/core/CReact.d.ts +409 -0
  34. package/dist/core/CReact.js +1127 -0
  35. package/dist/core/CloudDOMBuilder.d.ts +429 -0
  36. package/dist/core/CloudDOMBuilder.js +1198 -0
  37. package/dist/core/ContextDependencyTracker.d.ts +165 -0
  38. package/dist/core/ContextDependencyTracker.js +448 -0
  39. package/dist/core/ErrorRecoveryManager.d.ts +145 -0
  40. package/dist/core/ErrorRecoveryManager.js +443 -0
  41. package/dist/core/EventBus.d.ts +91 -0
  42. package/dist/core/EventBus.js +185 -0
  43. package/dist/core/ProviderOutputTracker.d.ts +211 -0
  44. package/dist/core/ProviderOutputTracker.js +476 -0
  45. package/dist/core/ReactiveUpdateQueue.d.ts +76 -0
  46. package/dist/core/ReactiveUpdateQueue.js +121 -0
  47. package/dist/core/Reconciler.d.ts +415 -0
  48. package/dist/core/Reconciler.js +1037 -0
  49. package/dist/core/RenderScheduler.d.ts +153 -0
  50. package/dist/core/RenderScheduler.js +519 -0
  51. package/dist/core/Renderer.d.ts +276 -0
  52. package/dist/core/Renderer.js +791 -0
  53. package/dist/core/Runtime.d.ts +246 -0
  54. package/dist/core/Runtime.js +640 -0
  55. package/dist/core/StateBindingManager.d.ts +121 -0
  56. package/dist/core/StateBindingManager.js +309 -0
  57. package/dist/core/StateMachine.d.ts +424 -0
  58. package/dist/core/StateMachine.js +787 -0
  59. package/dist/core/StructuralChangeDetector.d.ts +140 -0
  60. package/dist/core/StructuralChangeDetector.js +363 -0
  61. package/dist/core/Validator.d.ts +127 -0
  62. package/dist/core/Validator.js +279 -0
  63. package/dist/core/errors.d.ts +153 -0
  64. package/dist/core/errors.js +202 -0
  65. package/dist/core/index.d.ts +38 -0
  66. package/dist/core/index.js +64 -0
  67. package/dist/core/types.d.ts +263 -0
  68. package/dist/core/types.js +48 -0
  69. package/dist/hooks/context.d.ts +147 -0
  70. package/dist/hooks/context.js +334 -0
  71. package/dist/hooks/useContext.d.ts +113 -0
  72. package/dist/hooks/useContext.js +169 -0
  73. package/dist/hooks/useEffect.d.ts +105 -0
  74. package/dist/hooks/useEffect.js +540 -0
  75. package/dist/hooks/useInstance.d.ts +139 -0
  76. package/dist/hooks/useInstance.js +441 -0
  77. package/dist/hooks/useState.d.ts +120 -0
  78. package/dist/hooks/useState.js +298 -0
  79. package/dist/index.d.ts +46 -0
  80. package/dist/index.js +70 -0
  81. package/dist/jsx.d.ts +64 -0
  82. package/dist/jsx.js +76 -0
  83. package/dist/providers/DummyBackendProvider.d.ts +193 -0
  84. package/dist/providers/DummyBackendProvider.js +189 -0
  85. package/dist/providers/DummyCloudProvider.d.ts +128 -0
  86. package/dist/providers/DummyCloudProvider.js +157 -0
  87. package/dist/providers/IBackendProvider.d.ts +177 -0
  88. package/dist/providers/IBackendProvider.js +31 -0
  89. package/dist/providers/ICloudProvider.d.ts +146 -0
  90. package/dist/providers/ICloudProvider.js +31 -0
  91. package/dist/providers/index.d.ts +31 -0
  92. package/dist/providers/index.js +31 -0
  93. package/dist/test-event-callbacks.d.ts +0 -0
  94. package/dist/test-event-callbacks.js +1 -0
  95. package/dist/utils/Logger.d.ts +144 -0
  96. package/dist/utils/Logger.js +220 -0
  97. package/dist/utils/Output.d.ts +161 -0
  98. package/dist/utils/Output.js +401 -0
  99. package/dist/utils/deepEqual.d.ts +71 -0
  100. package/dist/utils/deepEqual.js +276 -0
  101. package/dist/utils/naming.d.ts +241 -0
  102. package/dist/utils/naming.js +376 -0
  103. package/package.json +87 -0
@@ -0,0 +1,791 @@
1
+ "use strict";
2
+ /**
3
+
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+
6
+ * you may not use this file except in compliance with the License.
7
+
8
+ * You may obtain a copy of the License at
9
+
10
+ *
11
+
12
+ * http://www.apache.org/licenses/LICENSE-2.0
13
+
14
+ *
15
+
16
+ * Unless required by applicable law or agreed to in writing, software
17
+
18
+ * distributed under the License is distributed on an "AS IS" BASIS,
19
+
20
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
21
+
22
+ * See the License for the specific language governing permissions and
23
+
24
+ * limitations under the License.
25
+
26
+ *
27
+
28
+ * Copyright 2025 Daniel Coutinho Ribeiro
29
+
30
+ */
31
+ Object.defineProperty(exports, "__esModule", { value: true });
32
+ exports.Renderer = void 0;
33
+ const useInstance_1 = require("../hooks/useInstance");
34
+ const context_1 = require("../hooks/context");
35
+ const naming_1 = require("../utils/naming");
36
+ const Logger_1 = require("../utils/Logger");
37
+ const logger = Logger_1.LoggerFactory.getLogger('renderer');
38
+ /**
39
+ * Renderer transforms JSX into a Fiber tree
40
+ *
41
+ * The Fiber tree is an intermediate representation that includes:
42
+ * - All components (containers + resources)
43
+ * - Hierarchical paths for identity tracking
44
+ * - Props and children resolved recursively
45
+ *
46
+ * Enhanced with context reactivity:
47
+ * - Tracks context provider value changes
48
+ * - Integrates with ContextDependencyTracker for selective re-rendering
49
+ * - Detects when context values change and triggers appropriate re-renders
50
+ *
51
+ * No dependencies injected - pure transformation logic
52
+ */
53
+ class Renderer {
54
+ constructor() {
55
+ this.currentFiber = null;
56
+ this.currentPath = [];
57
+ // Structural change detection
58
+ this.previousStructures = new WeakMap();
59
+ }
60
+ /**
61
+ * Render JSX to Fiber tree
62
+ *
63
+ * @param jsx - JSX element to render
64
+ * @returns Root Fiber node
65
+ */
66
+ render(jsx) {
67
+ // Run rendering within AsyncLocalStorage context for thread safety
68
+ return (0, context_1.runWithHookContext)(() => {
69
+ try {
70
+ this.currentPath = [];
71
+ this.currentFiber = this.renderElement(jsx, [], 0);
72
+ return this.currentFiber;
73
+ }
74
+ finally {
75
+ // Clear context stacks to prevent memory leaks
76
+ // This ensures context stacks are always cleared after each render cycle
77
+ (0, context_1.clearContextStacks)();
78
+ }
79
+ });
80
+ }
81
+ /**
82
+ * Recursively render a JSX element to a Fiber node
83
+ *
84
+ * @param element - JSX element
85
+ * @param parentPath - Path from parent
86
+ * @param siblingIndex - Sibling index for automatic key generation (default 0)
87
+ * @returns Fiber node
88
+ */
89
+ renderElement(element, parentPath, siblingIndex = 0) {
90
+ if (!element || typeof element !== 'object') {
91
+ throw new Error('Invalid JSX element');
92
+ }
93
+ const { type, props, key } = element;
94
+ if (!type) {
95
+ throw new Error('JSX element missing type');
96
+ }
97
+ // Ensure props is always an object (handle null/undefined)
98
+ const safeProps = props || {};
99
+ // Generate path for this node with sibling index support
100
+ const nodeName = (0, naming_1.getNodeName)(type, safeProps, key, siblingIndex);
101
+ const path = [...parentPath, nodeName];
102
+ // Create Fiber node for component execution
103
+ const fiber = {
104
+ type,
105
+ props: { ...safeProps },
106
+ children: [],
107
+ path,
108
+ key: key !== undefined ? (typeof key === 'symbol' ? nodeName : key.toString()) : undefined,
109
+ cloudDOMNodes: [], // Will be populated by useInstance
110
+ };
111
+ // Check if this is a Context Provider and handle value changes
112
+ const isProvider = typeof type === 'function' && type._isContextProvider;
113
+ if (isProvider) {
114
+ const contextId = type._contextId;
115
+ const newValue = safeProps.value;
116
+ // Detect context value changes and schedule re-renders if needed
117
+ this.handleContextProviderValueChange(contextId, newValue);
118
+ // Push value onto stack for child components
119
+ (0, context_1.pushContextValue)(contextId, newValue);
120
+ }
121
+ // Set context and execute component function to get children
122
+ (0, context_1.setRenderContext)(fiber, path);
123
+ (0, useInstance_1.resetConstructCounts)(fiber); // Reset construct call counts for this component
124
+ const children = this.executeComponent(type, safeProps, path);
125
+ (0, context_1.clearRenderContext)();
126
+ // Recursively render children
127
+ if (children) {
128
+ fiber.children = this.renderChildren(children, path);
129
+ }
130
+ // Check for structural changes after component execution
131
+ this.handleStructuralChanges(fiber);
132
+ // Pop context value from stack after rendering children
133
+ if (isProvider) {
134
+ const contextId = type._contextId;
135
+ (0, context_1.popContextValue)(contextId);
136
+ }
137
+ return fiber;
138
+ }
139
+ /**
140
+ * Execute a component function to resolve its children
141
+ *
142
+ * Note: setRenderContext should already be called by renderComponent before this
143
+ *
144
+ * @param type - Component type (function or class)
145
+ * @param props - Props to pass
146
+ * @param path - Current path (for context)
147
+ * @returns Children elements
148
+ */
149
+ executeComponent(type, props, path) {
150
+ // Store current path for hooks to access
151
+ const previousPath = this.currentPath;
152
+ this.currentPath = path;
153
+ try {
154
+ // Handle Fragment (symbol type)
155
+ if (typeof type === 'symbol') {
156
+ // Fragments just pass through their children
157
+ return props.children;
158
+ }
159
+ // Handle function components
160
+ if (typeof type === 'function') {
161
+ // Check if it's a class component
162
+ if (type.prototype && type.prototype.isReactComponent) {
163
+ const instance = new type(props);
164
+ return instance.render();
165
+ }
166
+ // Function component
167
+ return type(props);
168
+ }
169
+ // Handle intrinsic elements (strings like 'div')
170
+ if (typeof type === 'string') {
171
+ return props.children;
172
+ }
173
+ throw new Error(`Unknown component type: ${typeof type === 'symbol' ? 'Symbol' : type}`);
174
+ }
175
+ finally {
176
+ // Clear rendering context for hooks
177
+ (0, context_1.clearRenderContext)();
178
+ // Restore previous path
179
+ this.currentPath = previousPath;
180
+ // Copy CloudDOM nodes from temp fiber to actual fiber (will be done in renderElement)
181
+ // This is handled by returning the tempFiber's cloudDOMNodes
182
+ }
183
+ }
184
+ /**
185
+ * Render children elements
186
+ *
187
+ * @param children - Children to render (can be array, single element, or null)
188
+ * @param parentPath - Path from parent
189
+ * @returns Array of Fiber nodes
190
+ */
191
+ renderChildren(children, parentPath) {
192
+ if (!children) {
193
+ return [];
194
+ }
195
+ // Debug logging for array handling
196
+ if (Array.isArray(children) && children.length > 0) {
197
+ const hasNestedArrays = children.some(child => Array.isArray(child));
198
+ if (hasNestedArrays) {
199
+ logger.debug(`[renderChildren] Nested arrays detected at ${parentPath.join('.')}`);
200
+ logger.debug(` Before flat: ${children.length} items`);
201
+ logger.debug(` After flat: ${children.flat().length} items`);
202
+ }
203
+ }
204
+ // Normalize to array and flatten nested arrays (handles .map() results)
205
+ const childArray = Array.isArray(children) ? children.flat() : [children];
206
+ // Filter out null/undefined/boolean values
207
+ const validChildren = childArray.filter((child) => child !== null && child !== undefined && typeof child !== 'boolean');
208
+ // Track sibling counts by component type for automatic key generation
209
+ const siblingCounts = new Map();
210
+ // Track which types have been warned about (only warn once per type per render)
211
+ const warnedTypes = new Set();
212
+ // Render each child
213
+ return validChildren
214
+ .map((child) => {
215
+ // Handle text nodes (strings/numbers)
216
+ if (typeof child === 'string' || typeof child === 'number') {
217
+ // Skip text nodes in infrastructure (they don't make sense)
218
+ return null;
219
+ }
220
+ // Render JSX element
221
+ if (typeof child === 'object' && child.type) {
222
+ // Track sibling index for this type
223
+ const currentIndex = siblingCounts.get(child.type) || 0;
224
+ siblingCounts.set(child.type, currentIndex + 1);
225
+ // Emit warning when multiple siblings of same type without explicit keys
226
+ // This detects potential .map() usage without keys
227
+ if (currentIndex > 0 && !child.key && !warnedTypes.has(child.type)) {
228
+ warnedTypes.add(child.type);
229
+ const typeName = this.getTypeName(child.type);
230
+ const pathStr = parentPath.join('.');
231
+ logger.warn(`Warning: Multiple children of type "${typeName}" ` +
232
+ `without explicit keys at path "${pathStr}". ` +
233
+ `Consider adding a "key" prop for better reconciliation.`);
234
+ }
235
+ // Pass sibling index to renderElement
236
+ return this.renderElement(child, parentPath, currentIndex);
237
+ }
238
+ return null;
239
+ })
240
+ .filter((node) => node !== null);
241
+ }
242
+ /**
243
+ * Get a human-readable type name for warning messages
244
+ *
245
+ * @param type - Component type
246
+ * @returns Type name string
247
+ */
248
+ getTypeName(type) {
249
+ if (typeof type === 'function') {
250
+ return type.displayName || type.name || 'anonymous';
251
+ }
252
+ if (typeof type === 'string') {
253
+ return type;
254
+ }
255
+ if (typeof type === 'symbol') {
256
+ return 'Fragment';
257
+ }
258
+ return 'unknown';
259
+ }
260
+ /**
261
+ * Get current path (for hooks to access)
262
+ */
263
+ getCurrentPath() {
264
+ return [...this.currentPath];
265
+ }
266
+ /**
267
+ * Get current Fiber for post-deployment effects
268
+ *
269
+ * @returns Current Fiber node or null
270
+ */
271
+ getCurrentFiber() {
272
+ return this.currentFiber;
273
+ }
274
+ /**
275
+ * Set the RenderScheduler for selective re-rendering integration
276
+ *
277
+ * @param scheduler - RenderScheduler instance
278
+ */
279
+ setRenderScheduler(scheduler) {
280
+ this.renderScheduler = scheduler;
281
+ }
282
+ /**
283
+ * Set the ContextDependencyTracker for context reactivity
284
+ *
285
+ * @param tracker - ContextDependencyTracker instance
286
+ */
287
+ setContextDependencyTracker(tracker) {
288
+ this.contextDependencyTracker = tracker;
289
+ }
290
+ /**
291
+ * Set structural change detector for topology change detection
292
+ * @param detector - StructuralChangeDetector instance
293
+ */
294
+ setStructuralChangeDetector(detector) {
295
+ this.structuralChangeDetector = detector;
296
+ }
297
+ /**
298
+ * Handle context provider value changes
299
+ * Called when a context provider's value prop changes
300
+ *
301
+ * @param contextId - Context identifier
302
+ * @param newValue - New context value
303
+ */
304
+ handleContextProviderValueChange(contextId, newValue) {
305
+ if (this.contextDependencyTracker) {
306
+ try {
307
+ // Update context value and get affected fibers
308
+ const affectedFibers = this.contextDependencyTracker.updateContextValue(contextId, newValue);
309
+ // Schedule re-renders for affected components
310
+ if (this.renderScheduler && affectedFibers.length > 0) {
311
+ affectedFibers.forEach((fiber) => {
312
+ this.renderScheduler.schedule(fiber, 'context-change');
313
+ });
314
+ }
315
+ }
316
+ catch (error) {
317
+ logger.warn('Context provider value change handling failed:', error);
318
+ }
319
+ }
320
+ }
321
+ /**
322
+ * Detect structural changes in a component
323
+ * Compares the current structure with the previous render
324
+ *
325
+ * @param fiber - Fiber node to check for structural changes
326
+ * @returns True if structural changes were detected
327
+ */
328
+ detectStructuralChanges(fiber) {
329
+ // Generate structural signature for current render
330
+ const currentStructure = this.generateStructuralSignature(fiber);
331
+ // Get previous structure
332
+ const previousStructure = this.previousStructures.get(fiber);
333
+ // Store current structure for next comparison
334
+ this.previousStructures.set(fiber, currentStructure);
335
+ // If no previous structure, this is the first render (not a change)
336
+ if (!previousStructure) {
337
+ return false;
338
+ }
339
+ // Compare structures
340
+ const hasChanged = previousStructure !== currentStructure;
341
+ if (hasChanged) {
342
+ logger.debug(`Structural change detected in ${fiber.path.join('.')}:`);
343
+ logger.debug(` Previous: ${previousStructure}`);
344
+ logger.debug(` Current: ${currentStructure}`);
345
+ }
346
+ return hasChanged;
347
+ }
348
+ /**
349
+ * Generate a structural signature for a fiber node
350
+ * This captures the essential structure that affects CloudDOM generation
351
+ *
352
+ * @param fiber - Fiber node to generate signature for
353
+ * @returns Structural signature string
354
+ */
355
+ generateStructuralSignature(fiber) {
356
+ const parts = [];
357
+ // Include component type
358
+ const typeName = fiber.type?.name || (typeof fiber.type === 'symbol' ? 'fragment' : fiber.type);
359
+ parts.push(`type:${typeName}`);
360
+ // Include number and types of CloudDOM nodes (useInstance calls)
361
+ if (fiber.cloudDOMNodes && fiber.cloudDOMNodes.length > 0) {
362
+ const nodeSignatures = fiber.cloudDOMNodes
363
+ .map((node) => `${node.construct?.name || 'unknown'}:${node.id}`)
364
+ .sort(); // Sort for consistent ordering
365
+ parts.push(`nodes:[${nodeSignatures.join(',')}]`);
366
+ }
367
+ else {
368
+ parts.push('nodes:[]');
369
+ }
370
+ // Include number of children and their types
371
+ if (fiber.children && fiber.children.length > 0) {
372
+ const childTypes = fiber.children
373
+ .map((child) => child.type?.name ||
374
+ (typeof child.type === 'symbol' ? 'fragment' : child.type) ||
375
+ 'unknown')
376
+ .sort(); // Sort for consistent ordering
377
+ parts.push(`children:[${childTypes.join(',')}]`);
378
+ }
379
+ else {
380
+ parts.push('children:[]');
381
+ }
382
+ // Include key if present (affects identity)
383
+ if (fiber.key) {
384
+ const keyValue = typeof fiber.key === 'symbol' ? 'fragment' : fiber.key;
385
+ parts.push(`key:${keyValue}`);
386
+ }
387
+ return parts.join('|');
388
+ }
389
+ /**
390
+ * Check for structural changes and schedule re-renders if needed
391
+ * Called during component re-execution
392
+ *
393
+ * @param fiber - Fiber node to check
394
+ */
395
+ handleStructuralChanges(fiber) {
396
+ if (this.detectStructuralChanges(fiber)) {
397
+ // Schedule re-render for structural change
398
+ if (this.renderScheduler) {
399
+ this.renderScheduler.schedule(fiber, 'structural-change');
400
+ }
401
+ // Also check if any dependent components need re-rendering
402
+ if (fiber.dependents) {
403
+ Array.from(fiber.dependents).forEach((dependent) => {
404
+ if (this.renderScheduler) {
405
+ this.renderScheduler.schedule(dependent, 'structural-change');
406
+ }
407
+ });
408
+ }
409
+ }
410
+ }
411
+ /**
412
+ * Handle structural changes between old and new fiber trees
413
+ * Called during re-rendering to detect topology changes
414
+ *
415
+ * @param oldFiber - Previous fiber tree
416
+ * @param newFiber - New fiber tree after re-render
417
+ */
418
+ handleStructuralChangesComparison(oldFiber, newFiber) {
419
+ if (!this.structuralChangeDetector) {
420
+ return;
421
+ }
422
+ try {
423
+ // Extract CloudDOM from both fiber trees for comparison
424
+ const oldCloudDOM = this.extractCloudDOMFromFiber(oldFiber);
425
+ const newCloudDOM = this.extractCloudDOMFromFiber(newFiber);
426
+ // Detect structural changes
427
+ const changes = this.structuralChangeDetector.detectStructuralChanges(oldCloudDOM, newCloudDOM, newFiber);
428
+ if (changes.length > 0) {
429
+ // Trigger re-renders for affected components
430
+ this.structuralChangeDetector.triggerStructuralReRenders(changes, this.renderScheduler);
431
+ }
432
+ }
433
+ catch (error) {
434
+ logger.error('Error handling structural changes:', error);
435
+ }
436
+ }
437
+ /**
438
+ * Extract CloudDOM nodes from a fiber tree for structural comparison
439
+ *
440
+ * @param fiber - Fiber tree to extract CloudDOM from
441
+ * @returns Array of CloudDOM nodes
442
+ */
443
+ extractCloudDOMFromFiber(fiber) {
444
+ const cloudDOMNodes = [];
445
+ const walkFiber = (currentFiber) => {
446
+ // Check for cloudDOMNodes array
447
+ if (currentFiber.cloudDOMNodes &&
448
+ Array.isArray(currentFiber.cloudDOMNodes)) {
449
+ cloudDOMNodes.push(...currentFiber.cloudDOMNodes);
450
+ }
451
+ // Legacy cloudDOMNode removed - only use cloudDOMNodes array
452
+ // Recursively walk children
453
+ if (currentFiber.children && currentFiber.children.length > 0) {
454
+ for (const child of currentFiber.children) {
455
+ walkFiber(child);
456
+ }
457
+ }
458
+ };
459
+ walkFiber(fiber);
460
+ return cloudDOMNodes;
461
+ }
462
+ /**
463
+ * Re-render specific components with selective updates
464
+ * This method re-executes only the specified components and their children
465
+ *
466
+ * @param components - Array of fiber nodes to re-render
467
+ * @param reason - Reason for the re-render
468
+ * @returns Updated root fiber node
469
+ */
470
+ reRenderComponents(components, reason) {
471
+ if (components.length === 0) {
472
+ throw new Error('[Renderer] No components provided for re-rendering');
473
+ }
474
+ return (0, context_1.runWithHookContext)(() => {
475
+ try {
476
+ // Find the root component to determine the full tree structure
477
+ const rootComponent = this.findRootComponent(components);
478
+ if (!rootComponent) {
479
+ throw new Error('[Renderer] Could not determine root component for re-rendering');
480
+ }
481
+ // Track dependencies during re-render
482
+ this.trackDependenciesDuringRender(components);
483
+ // Selectively re-render the components
484
+ const updatedRoot = this.selectiveReRender(rootComponent, new Set(components), reason);
485
+ // Detect structural changes if this is a structural re-render
486
+ if (reason === 'structural-change' && this.structuralChangeDetector) {
487
+ this.handleStructuralChangesComparison(rootComponent, updatedRoot);
488
+ }
489
+ // Update current fiber reference
490
+ this.currentFiber = updatedRoot;
491
+ return updatedRoot;
492
+ }
493
+ finally {
494
+ // Clear context stacks to prevent memory leaks
495
+ (0, context_1.clearContextStacks)();
496
+ }
497
+ });
498
+ }
499
+ /**
500
+ * Find components that depend on changed state/outputs
501
+ * This method traverses the fiber tree to find dependent components
502
+ *
503
+ * @param changedFiber - Fiber node that changed
504
+ * @returns Set of fiber nodes that depend on the changed fiber
505
+ */
506
+ findDependentComponents(changedFiber) {
507
+ const dependents = new Set();
508
+ // If the fiber has explicit dependents, add them
509
+ if (changedFiber.dependents) {
510
+ Array.from(changedFiber.dependents).forEach((dependent) => dependents.add(dependent));
511
+ }
512
+ // Traverse the tree to find implicit dependencies
513
+ if (this.currentFiber) {
514
+ this.findDependentsRecursive(this.currentFiber, changedFiber, dependents);
515
+ }
516
+ return dependents;
517
+ }
518
+ /**
519
+ * Track component dependencies during render
520
+ * This builds the dependency graph for selective re-rendering
521
+ *
522
+ * @param components - Components being rendered
523
+ */
524
+ trackDependenciesDuringRender(components) {
525
+ for (const component of components) {
526
+ // Initialize dependency tracking if not present
527
+ if (!component.dependencies) {
528
+ component.dependencies = new Set();
529
+ }
530
+ if (!component.dependents) {
531
+ component.dependents = new Set();
532
+ }
533
+ // Track dependencies based on context usage, state bindings, etc.
534
+ this.buildDependencyGraph(component);
535
+ }
536
+ }
537
+ /**
538
+ * Build dependency graph for a component
539
+ * This analyzes the component's usage patterns to determine dependencies
540
+ *
541
+ * @param component - Component to analyze
542
+ */
543
+ buildDependencyGraph(component) {
544
+ // Track context dependencies
545
+ const contextDependencies = this.contextDependencyTracker.getFiberContexts(component);
546
+ contextDependencies.forEach((contextId) => {
547
+ // Find context provider components and add as dependencies
548
+ const providerFiber = this.findContextProvider(contextId);
549
+ if (providerFiber && providerFiber !== component) {
550
+ if (!component.dependencies) {
551
+ component.dependencies = new Set();
552
+ }
553
+ component.dependencies.add(providerFiber);
554
+ if (!providerFiber.dependents) {
555
+ providerFiber.dependents = new Set();
556
+ }
557
+ providerFiber.dependents.add(component);
558
+ }
559
+ });
560
+ // Track state binding dependencies
561
+ // (This would integrate with StateBindingManager)
562
+ // Track instance output dependencies
563
+ // (This would integrate with ProviderOutputTracker)
564
+ // Basic parent-child dependencies
565
+ if (component.children) {
566
+ component.children.forEach((child) => {
567
+ // Child depends on parent
568
+ if (!child.dependencies) {
569
+ child.dependencies = new Set();
570
+ }
571
+ child.dependencies.add(component);
572
+ // Parent has child as dependent
573
+ if (!component.dependents) {
574
+ component.dependents = new Set();
575
+ }
576
+ component.dependents.add(child);
577
+ });
578
+ }
579
+ }
580
+ /**
581
+ * Find the context provider fiber for a given context ID
582
+ * This traverses up the fiber tree to find the provider
583
+ *
584
+ * @param contextId - Context identifier to find provider for
585
+ * @returns Provider fiber node or null if not found
586
+ */
587
+ findContextProvider(contextId) {
588
+ // This is a simplified implementation
589
+ // In a full implementation, we would traverse the fiber tree
590
+ // to find the actual provider component
591
+ if (!this.currentFiber) {
592
+ return null;
593
+ }
594
+ // For now, we'll do a simple search through the current fiber tree
595
+ return this.searchForProvider(this.currentFiber, contextId);
596
+ }
597
+ /**
598
+ * Recursively search for a context provider in the fiber tree
599
+ *
600
+ * @param fiber - Current fiber to search
601
+ * @param contextId - Context ID to find
602
+ * @returns Provider fiber or null
603
+ */
604
+ searchForProvider(fiber, contextId) {
605
+ // Check if this fiber is a provider for the context
606
+ const isProvider = typeof fiber.type === 'function' &&
607
+ fiber.type._isContextProvider &&
608
+ fiber.type._contextId === contextId;
609
+ if (isProvider) {
610
+ return fiber;
611
+ }
612
+ // Search children
613
+ if (fiber.children) {
614
+ for (const child of fiber.children) {
615
+ const result = this.searchForProvider(child, contextId);
616
+ if (result) {
617
+ return result;
618
+ }
619
+ }
620
+ }
621
+ return null;
622
+ }
623
+ /**
624
+ * Perform selective re-render of specific components
625
+ * Only re-executes components that need updates
626
+ *
627
+ * @param rootFiber - Root fiber node
628
+ * @param componentsToReRender - Set of components that need re-rendering
629
+ * @param reason - Reason for re-render
630
+ * @returns Updated fiber tree
631
+ */
632
+ selectiveReRender(rootFiber, componentsToReRender, reason) {
633
+ // Create a copy of the root fiber for selective updates
634
+ const updatedFiber = this.cloneFiberForUpdate(rootFiber);
635
+ // Recursively update only the components that need re-rendering
636
+ this.selectiveReRenderRecursive(updatedFiber, componentsToReRender, reason, []);
637
+ return updatedFiber;
638
+ }
639
+ /**
640
+ * Recursively perform selective re-rendering
641
+ *
642
+ * @param fiber - Current fiber node
643
+ * @param componentsToReRender - Set of components to re-render
644
+ * @param reason - Reason for re-render
645
+ * @param currentPath - Current path in the tree
646
+ */
647
+ selectiveReRenderRecursive(fiber, componentsToReRender, reason, currentPath) {
648
+ // Check if this component needs re-rendering
649
+ const needsReRender = this.shouldReRenderComponent(fiber, componentsToReRender);
650
+ if (needsReRender) {
651
+ // Update reactive state
652
+ if (!fiber.reactiveState) {
653
+ fiber.reactiveState = {
654
+ renderCount: 0,
655
+ isDirty: false,
656
+ updatePending: false,
657
+ };
658
+ }
659
+ fiber.reactiveState.lastRenderReason = reason;
660
+ fiber.reactiveState.lastRenderTime = Date.now();
661
+ fiber.reactiveState.renderCount++;
662
+ fiber.reactiveState.isDirty = false;
663
+ fiber.reactiveState.updatePending = false;
664
+ // Re-execute the component
665
+ this.reExecuteComponent(fiber, currentPath);
666
+ }
667
+ // Recursively process children
668
+ if (fiber.children) {
669
+ fiber.children.forEach((child) => {
670
+ const childPath = [...currentPath, child.path[child.path.length - 1]];
671
+ this.selectiveReRenderRecursive(child, componentsToReRender, reason, childPath);
672
+ });
673
+ }
674
+ }
675
+ /**
676
+ * Determine if a component should be re-rendered
677
+ *
678
+ * @param fiber - Fiber node to check
679
+ * @param componentsToReRender - Set of components marked for re-rendering
680
+ * @returns True if component should be re-rendered
681
+ */
682
+ shouldReRenderComponent(fiber, componentsToReRender) {
683
+ // Direct match
684
+ if (componentsToReRender.has(fiber)) {
685
+ return true;
686
+ }
687
+ // Check if any dependencies need re-rendering
688
+ if (fiber.dependencies) {
689
+ const dependencyArray = Array.from(fiber.dependencies);
690
+ for (const dependency of dependencyArray) {
691
+ if (componentsToReRender.has(dependency)) {
692
+ return true;
693
+ }
694
+ }
695
+ }
696
+ // Check reactive state
697
+ if (fiber.reactiveState?.isDirty) {
698
+ return true;
699
+ }
700
+ return false;
701
+ }
702
+ /**
703
+ * Re-execute a component function
704
+ *
705
+ * @param fiber - Fiber node to re-execute
706
+ * @param currentPath - Current path in the tree
707
+ */
708
+ reExecuteComponent(fiber, currentPath) {
709
+ // Set up rendering context
710
+ (0, context_1.setRenderContext)(fiber, fiber.path);
711
+ (0, useInstance_1.resetConstructCounts)(fiber);
712
+ try {
713
+ // Re-execute the component
714
+ const children = this.executeComponent(fiber.type, fiber.props, fiber.path);
715
+ // Update children if they changed
716
+ if (children) {
717
+ fiber.children = this.renderChildren(children, fiber.path);
718
+ }
719
+ // Check for structural changes after re-execution
720
+ this.handleStructuralChanges(fiber);
721
+ }
722
+ finally {
723
+ // Clean up context
724
+ (0, context_1.clearRenderContext)();
725
+ }
726
+ }
727
+ /**
728
+ * Find the root component from a set of components
729
+ *
730
+ * @param components - Array of components
731
+ * @returns Root component or null
732
+ */
733
+ findRootComponent(components) {
734
+ // Find the component with the shortest path (closest to root)
735
+ let rootComponent = null;
736
+ let shortestPathLength = Infinity;
737
+ for (const component of components) {
738
+ if (component.path.length < shortestPathLength) {
739
+ shortestPathLength = component.path.length;
740
+ rootComponent = component;
741
+ }
742
+ }
743
+ return rootComponent;
744
+ }
745
+ /**
746
+ * Recursively find dependent components
747
+ *
748
+ * @param currentFiber - Current fiber being examined
749
+ * @param changedFiber - Fiber that changed
750
+ * @param dependents - Set to collect dependents
751
+ */
752
+ findDependentsRecursive(currentFiber, changedFiber, dependents) {
753
+ // Check if current fiber depends on changed fiber
754
+ if (currentFiber.dependencies?.has(changedFiber)) {
755
+ dependents.add(currentFiber);
756
+ }
757
+ // Recursively check children
758
+ if (currentFiber.children) {
759
+ currentFiber.children.forEach((child) => {
760
+ this.findDependentsRecursive(child, changedFiber, dependents);
761
+ });
762
+ }
763
+ }
764
+ /**
765
+ * Set StateBindingManager for context dependency tracker integration
766
+ */
767
+ setStateBindingManager(stateBindingManager) {
768
+ this.contextDependencyTracker.setStateBindingManager(stateBindingManager);
769
+ }
770
+ /**
771
+ * Clone a fiber node for selective updates
772
+ *
773
+ * @param fiber - Fiber to clone
774
+ * @returns Cloned fiber
775
+ */
776
+ cloneFiberForUpdate(fiber) {
777
+ return {
778
+ ...fiber,
779
+ props: { ...fiber.props },
780
+ children: fiber.children
781
+ ? fiber.children.map((child) => this.cloneFiberForUpdate(child))
782
+ : [],
783
+ hooks: fiber.hooks ? [...fiber.hooks] : undefined,
784
+ state: fiber.state ? { ...fiber.state } : undefined,
785
+ reactiveState: fiber.reactiveState ? { ...fiber.reactiveState } : undefined,
786
+ dependencies: fiber.dependencies ? new Set(fiber.dependencies) : undefined,
787
+ dependents: fiber.dependents ? new Set(fiber.dependents) : undefined,
788
+ };
789
+ }
790
+ }
791
+ exports.Renderer = Renderer;