@creact-labs/creact 0.1.8 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (146) hide show
  1. package/README.md +85 -22
  2. package/dist/cli.d.ts +11 -0
  3. package/dist/cli.js +88 -0
  4. package/dist/index.d.ts +19 -44
  5. package/dist/index.js +20 -68
  6. package/dist/jsx/index.d.ts +2 -0
  7. package/dist/jsx/index.js +1 -0
  8. package/dist/jsx/jsx-dev-runtime.d.ts +4 -0
  9. package/dist/jsx/jsx-dev-runtime.js +4 -0
  10. package/dist/jsx/jsx-runtime.d.ts +38 -0
  11. package/dist/jsx/jsx-runtime.js +38 -0
  12. package/dist/jsx/types.d.ts +12 -0
  13. package/dist/jsx/types.js +4 -0
  14. package/dist/primitives/context.d.ts +34 -0
  15. package/dist/primitives/context.js +63 -0
  16. package/dist/primitives/index.d.ts +3 -0
  17. package/dist/primitives/index.js +3 -0
  18. package/dist/primitives/instance.d.ts +72 -0
  19. package/dist/primitives/instance.js +235 -0
  20. package/dist/primitives/store.d.ts +22 -0
  21. package/dist/primitives/store.js +97 -0
  22. package/dist/provider/backend.d.ts +110 -0
  23. package/dist/provider/backend.js +37 -0
  24. package/dist/provider/interface.d.ts +48 -0
  25. package/dist/provider/interface.js +39 -0
  26. package/dist/reactive/effect.d.ts +11 -0
  27. package/dist/reactive/effect.js +42 -0
  28. package/dist/reactive/index.d.ts +3 -0
  29. package/dist/reactive/index.js +3 -0
  30. package/dist/reactive/signal.d.ts +32 -0
  31. package/dist/reactive/signal.js +60 -0
  32. package/dist/reactive/tracking.d.ts +41 -0
  33. package/dist/reactive/tracking.js +161 -0
  34. package/dist/runtime/fiber.d.ts +21 -0
  35. package/dist/runtime/fiber.js +16 -0
  36. package/dist/runtime/index.d.ts +4 -0
  37. package/dist/runtime/index.js +4 -0
  38. package/dist/runtime/reconcile.d.ts +66 -0
  39. package/dist/runtime/reconcile.js +210 -0
  40. package/dist/runtime/render.d.ts +42 -0
  41. package/dist/runtime/render.js +231 -0
  42. package/dist/runtime/run.d.ts +119 -0
  43. package/dist/runtime/run.js +334 -0
  44. package/dist/runtime/state-machine.d.ts +95 -0
  45. package/dist/runtime/state-machine.js +209 -0
  46. package/dist/types.d.ts +13 -0
  47. package/dist/types.js +4 -0
  48. package/package.json +29 -24
  49. package/dist/cli/commands/BuildCommand.d.ts +0 -40
  50. package/dist/cli/commands/BuildCommand.js +0 -151
  51. package/dist/cli/commands/DeployCommand.d.ts +0 -38
  52. package/dist/cli/commands/DeployCommand.js +0 -194
  53. package/dist/cli/commands/DevCommand.d.ts +0 -52
  54. package/dist/cli/commands/DevCommand.js +0 -394
  55. package/dist/cli/commands/PlanCommand.d.ts +0 -39
  56. package/dist/cli/commands/PlanCommand.js +0 -164
  57. package/dist/cli/commands/index.d.ts +0 -36
  58. package/dist/cli/commands/index.js +0 -43
  59. package/dist/cli/core/ArgumentParser.d.ts +0 -46
  60. package/dist/cli/core/ArgumentParser.js +0 -127
  61. package/dist/cli/core/BaseCommand.d.ts +0 -75
  62. package/dist/cli/core/BaseCommand.js +0 -95
  63. package/dist/cli/core/CLIContext.d.ts +0 -68
  64. package/dist/cli/core/CLIContext.js +0 -183
  65. package/dist/cli/core/CommandRegistry.d.ts +0 -64
  66. package/dist/cli/core/CommandRegistry.js +0 -89
  67. package/dist/cli/core/index.d.ts +0 -36
  68. package/dist/cli/core/index.js +0 -43
  69. package/dist/cli/index.d.ts +0 -35
  70. package/dist/cli/index.js +0 -100
  71. package/dist/cli/output.d.ts +0 -204
  72. package/dist/cli/output.js +0 -437
  73. package/dist/cli/utils.d.ts +0 -59
  74. package/dist/cli/utils.js +0 -76
  75. package/dist/context/createContext.d.ts +0 -90
  76. package/dist/context/createContext.js +0 -113
  77. package/dist/context/index.d.ts +0 -30
  78. package/dist/context/index.js +0 -35
  79. package/dist/core/CReact.d.ts +0 -409
  80. package/dist/core/CReact.js +0 -1151
  81. package/dist/core/CloudDOMBuilder.d.ts +0 -447
  82. package/dist/core/CloudDOMBuilder.js +0 -1234
  83. package/dist/core/ContextDependencyTracker.d.ts +0 -165
  84. package/dist/core/ContextDependencyTracker.js +0 -448
  85. package/dist/core/ErrorRecoveryManager.d.ts +0 -145
  86. package/dist/core/ErrorRecoveryManager.js +0 -443
  87. package/dist/core/EventBus.d.ts +0 -91
  88. package/dist/core/EventBus.js +0 -185
  89. package/dist/core/ProviderOutputTracker.d.ts +0 -211
  90. package/dist/core/ProviderOutputTracker.js +0 -476
  91. package/dist/core/ReactiveUpdateQueue.d.ts +0 -76
  92. package/dist/core/ReactiveUpdateQueue.js +0 -121
  93. package/dist/core/Reconciler.d.ts +0 -415
  94. package/dist/core/Reconciler.js +0 -1044
  95. package/dist/core/RenderScheduler.d.ts +0 -153
  96. package/dist/core/RenderScheduler.js +0 -519
  97. package/dist/core/Renderer.d.ts +0 -336
  98. package/dist/core/Renderer.js +0 -944
  99. package/dist/core/Runtime.d.ts +0 -246
  100. package/dist/core/Runtime.js +0 -640
  101. package/dist/core/StateBindingManager.d.ts +0 -121
  102. package/dist/core/StateBindingManager.js +0 -309
  103. package/dist/core/StateMachine.d.ts +0 -441
  104. package/dist/core/StateMachine.js +0 -883
  105. package/dist/core/StructuralChangeDetector.d.ts +0 -140
  106. package/dist/core/StructuralChangeDetector.js +0 -363
  107. package/dist/core/Validator.d.ts +0 -127
  108. package/dist/core/Validator.js +0 -279
  109. package/dist/core/errors.d.ts +0 -153
  110. package/dist/core/errors.js +0 -202
  111. package/dist/core/index.d.ts +0 -38
  112. package/dist/core/index.js +0 -64
  113. package/dist/core/types.d.ts +0 -265
  114. package/dist/core/types.js +0 -48
  115. package/dist/hooks/context.d.ts +0 -147
  116. package/dist/hooks/context.js +0 -334
  117. package/dist/hooks/useContext.d.ts +0 -113
  118. package/dist/hooks/useContext.js +0 -169
  119. package/dist/hooks/useEffect.d.ts +0 -105
  120. package/dist/hooks/useEffect.js +0 -540
  121. package/dist/hooks/useInstance.d.ts +0 -139
  122. package/dist/hooks/useInstance.js +0 -455
  123. package/dist/hooks/useState.d.ts +0 -120
  124. package/dist/hooks/useState.js +0 -298
  125. package/dist/jsx.d.ts +0 -143
  126. package/dist/jsx.js +0 -76
  127. package/dist/providers/DummyBackendProvider.d.ts +0 -193
  128. package/dist/providers/DummyBackendProvider.js +0 -189
  129. package/dist/providers/DummyCloudProvider.d.ts +0 -128
  130. package/dist/providers/DummyCloudProvider.js +0 -157
  131. package/dist/providers/IBackendProvider.d.ts +0 -177
  132. package/dist/providers/IBackendProvider.js +0 -31
  133. package/dist/providers/ICloudProvider.d.ts +0 -230
  134. package/dist/providers/ICloudProvider.js +0 -31
  135. package/dist/providers/index.d.ts +0 -31
  136. package/dist/providers/index.js +0 -31
  137. package/dist/test-event-callbacks.d.ts +0 -0
  138. package/dist/test-event-callbacks.js +0 -1
  139. package/dist/utils/Logger.d.ts +0 -144
  140. package/dist/utils/Logger.js +0 -220
  141. package/dist/utils/Output.d.ts +0 -161
  142. package/dist/utils/Output.js +0 -401
  143. package/dist/utils/deepEqual.d.ts +0 -71
  144. package/dist/utils/deepEqual.js +0 -276
  145. package/dist/utils/naming.d.ts +0 -241
  146. package/dist/utils/naming.js +0 -376
@@ -1,944 +0,0 @@
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
- // Find the root component to determine the full tree structure
476
- const rootComponent = this.findRootComponent(components);
477
- if (!rootComponent) {
478
- throw new Error('[Renderer] Could not determine root component for re-rendering');
479
- }
480
- // Track dependencies during re-render
481
- this.trackDependenciesDuringRender(components);
482
- // Selectively re-render the components
483
- const updatedRoot = this.selectiveReRender(rootComponent, new Set(components), reason);
484
- // Detect structural changes if this is a structural re-render
485
- if (reason === 'structural-change' && this.structuralChangeDetector) {
486
- this.handleStructuralChangesComparison(rootComponent, updatedRoot);
487
- }
488
- // Update current fiber reference
489
- this.currentFiber = updatedRoot;
490
- return updatedRoot;
491
- });
492
- }
493
- /**
494
- * Find components that depend on changed state/outputs
495
- * This method traverses the fiber tree to find dependent components
496
- *
497
- * @param changedFiber - Fiber node that changed
498
- * @returns Set of fiber nodes that depend on the changed fiber
499
- */
500
- findDependentComponents(changedFiber) {
501
- const dependents = new Set();
502
- // If the fiber has explicit dependents, add them
503
- if (changedFiber.dependents) {
504
- Array.from(changedFiber.dependents).forEach((dependent) => dependents.add(dependent));
505
- }
506
- // Traverse the tree to find implicit dependencies
507
- if (this.currentFiber) {
508
- this.findDependentsRecursive(this.currentFiber, changedFiber, dependents);
509
- }
510
- return dependents;
511
- }
512
- /**
513
- * Track component dependencies during render
514
- * This builds the dependency graph for selective re-rendering
515
- *
516
- * @param components - Components being rendered
517
- */
518
- trackDependenciesDuringRender(components) {
519
- for (const component of components) {
520
- // Initialize dependency tracking if not present
521
- if (!component.dependencies) {
522
- component.dependencies = new Set();
523
- }
524
- if (!component.dependents) {
525
- component.dependents = new Set();
526
- }
527
- // Track dependencies based on context usage, state bindings, etc.
528
- this.buildDependencyGraph(component);
529
- }
530
- }
531
- /**
532
- * Build dependency graph for a component
533
- * This analyzes the component's usage patterns to determine dependencies
534
- *
535
- * @param component - Component to analyze
536
- */
537
- buildDependencyGraph(component) {
538
- // Track context dependencies
539
- const contextDependencies = this.contextDependencyTracker.getFiberContexts(component);
540
- contextDependencies.forEach((contextId) => {
541
- // Find context provider components and add as dependencies
542
- const providerFiber = this.findContextProvider(contextId);
543
- if (providerFiber && providerFiber !== component) {
544
- if (!component.dependencies) {
545
- component.dependencies = new Set();
546
- }
547
- component.dependencies.add(providerFiber);
548
- if (!providerFiber.dependents) {
549
- providerFiber.dependents = new Set();
550
- }
551
- providerFiber.dependents.add(component);
552
- }
553
- });
554
- // Track state binding dependencies
555
- // (This would integrate with StateBindingManager)
556
- // Track instance output dependencies
557
- // (This would integrate with ProviderOutputTracker)
558
- // Basic parent-child dependencies
559
- if (component.children) {
560
- component.children.forEach((child) => {
561
- // Child depends on parent
562
- if (!child.dependencies) {
563
- child.dependencies = new Set();
564
- }
565
- child.dependencies.add(component);
566
- // Parent has child as dependent
567
- if (!component.dependents) {
568
- component.dependents = new Set();
569
- }
570
- component.dependents.add(child);
571
- });
572
- }
573
- }
574
- /**
575
- * Find the context provider fiber for a given context ID
576
- * This traverses up the fiber tree to find the provider
577
- *
578
- * @param contextId - Context identifier to find provider for
579
- * @returns Provider fiber node or null if not found
580
- */
581
- findContextProvider(contextId) {
582
- // This is a simplified implementation
583
- // In a full implementation, we would traverse the fiber tree
584
- // to find the actual provider component
585
- if (!this.currentFiber) {
586
- return null;
587
- }
588
- // For now, we'll do a simple search through the current fiber tree
589
- return this.searchForProvider(this.currentFiber, contextId);
590
- }
591
- /**
592
- * Recursively search for a context provider in the fiber tree
593
- *
594
- * @param fiber - Current fiber to search
595
- * @param contextId - Context ID to find
596
- * @returns Provider fiber or null
597
- */
598
- searchForProvider(fiber, contextId) {
599
- // Check if this fiber is a provider for the context
600
- const isProvider = typeof fiber.type === 'function' &&
601
- fiber.type._isContextProvider &&
602
- fiber.type._contextId === contextId;
603
- if (isProvider) {
604
- return fiber;
605
- }
606
- // Search children
607
- if (fiber.children) {
608
- for (const child of fiber.children) {
609
- const result = this.searchForProvider(child, contextId);
610
- if (result) {
611
- return result;
612
- }
613
- }
614
- }
615
- return null;
616
- }
617
- /**
618
- * Perform selective re-render of specific components
619
- * Only re-executes components that need updates
620
- *
621
- * @param rootFiber - Root fiber node
622
- * @param componentsToReRender - Set of components that need re-rendering
623
- * @param reason - Reason for re-render
624
- * @returns Updated fiber tree
625
- */
626
- selectiveReRender(rootFiber, componentsToReRender, reason) {
627
- // Create a copy of the root fiber for selective updates
628
- const updatedFiber = this.cloneFiberForUpdate(rootFiber);
629
- // Recursively update only the components that need re-rendering
630
- this.selectiveReRenderRecursive(updatedFiber, componentsToReRender, reason, []);
631
- return updatedFiber;
632
- }
633
- /**
634
- * Recursively perform selective re-rendering
635
- *
636
- * @param fiber - Current fiber node
637
- * @param componentsToReRender - Set of components to re-render
638
- * @param reason - Reason for re-render
639
- * @param currentPath - Current path in the tree
640
- */
641
- selectiveReRenderRecursive(fiber, componentsToReRender, reason, currentPath) {
642
- // Check if this component needs re-rendering
643
- // Match by path instead of object identity (fibers are cloned)
644
- const needsReRender = this.shouldReRenderComponentByPath(fiber, componentsToReRender);
645
- if (needsReRender) {
646
- // Update reactive state
647
- if (!fiber.reactiveState) {
648
- fiber.reactiveState = {
649
- renderCount: 0,
650
- isDirty: false,
651
- updatePending: false,
652
- };
653
- }
654
- fiber.reactiveState.lastRenderReason = reason;
655
- fiber.reactiveState.lastRenderTime = Date.now();
656
- fiber.reactiveState.renderCount++;
657
- fiber.reactiveState.isDirty = false;
658
- fiber.reactiveState.updatePending = false;
659
- // Re-execute the component
660
- this.reExecuteComponent(fiber, currentPath);
661
- }
662
- // Recursively process children
663
- if (fiber.children) {
664
- fiber.children.forEach((child) => {
665
- const childPath = [...currentPath, child.path[child.path.length - 1]];
666
- this.selectiveReRenderRecursive(child, componentsToReRender, reason, childPath);
667
- });
668
- }
669
- }
670
- /**
671
- * Check if component should re-render by matching paths
672
- * This works with cloned fibers where object identity doesn't match
673
- *
674
- * @param fiber - Current fiber to check
675
- * @param componentsToReRender - Set of original fibers to re-render
676
- * @returns True if this fiber's path matches any in the set
677
- */
678
- shouldReRenderComponentByPath(fiber, componentsToReRender) {
679
- // Match by path (works with cloned fibers)
680
- const fiberPath = fiber.path?.join('.') || '';
681
- for (const componentToReRender of componentsToReRender) {
682
- const targetPath = componentToReRender.path?.join('.') || '';
683
- if (fiberPath === targetPath) {
684
- return true;
685
- }
686
- }
687
- return false;
688
- }
689
- /**
690
- * Determine if a component should be re-rendered
691
- *
692
- * @param fiber - Fiber node to check
693
- * @param componentsToReRender - Set of components marked for re-rendering
694
- * @returns True if component should be re-rendered
695
- */
696
- shouldReRenderComponent(fiber, componentsToReRender) {
697
- // Direct match
698
- if (componentsToReRender.has(fiber)) {
699
- return true;
700
- }
701
- // Check if any dependencies need re-rendering
702
- if (fiber.dependencies) {
703
- const dependencyArray = Array.from(fiber.dependencies);
704
- for (const dependency of dependencyArray) {
705
- if (componentsToReRender.has(dependency)) {
706
- return true;
707
- }
708
- }
709
- }
710
- // Check reactive state
711
- if (fiber.reactiveState?.isDirty) {
712
- return true;
713
- }
714
- return false;
715
- }
716
- /**
717
- * Re-execute a component function
718
- *
719
- * @param fiber - Fiber node to re-execute
720
- * @param currentPath - Current path in the tree
721
- */
722
- reExecuteComponent(fiber, currentPath) {
723
- // CRITICAL: Restore context stack from parent providers before re-executing
724
- this.restoreContextStackForFiber(fiber);
725
- // Set up rendering context
726
- (0, context_1.setRenderContext)(fiber, fiber.path);
727
- (0, useInstance_1.resetConstructCounts)(fiber);
728
- try {
729
- // Re-execute the component
730
- const children = this.executeComponent(fiber.type, fiber.props, fiber.path);
731
- // Update children if they changed
732
- if (children) {
733
- fiber.children = this.renderChildren(children, fiber.path);
734
- }
735
- // Check for structural changes after re-execution
736
- this.handleStructuralChanges(fiber);
737
- }
738
- finally {
739
- // Clean up context
740
- (0, context_1.clearRenderContext)();
741
- }
742
- }
743
- /**
744
- * Restore context stack for a fiber by traversing parent providers
745
- * This ensures useContext() gets the correct values during re-renders
746
- *
747
- * @param fiber - Fiber node to restore context for
748
- */
749
- restoreContextStackForFiber(fiber) {
750
- // Find all ancestor providers up to the root
751
- const ancestorProviders = this.findAncestorProviders(fiber);
752
- // Push context values onto the stack in order (root to leaf)
753
- for (const providerFiber of ancestorProviders) {
754
- const contextId = providerFiber.type._contextId;
755
- const contextValue = providerFiber.props.value;
756
- (0, context_1.pushContextValue)(contextId, contextValue);
757
- }
758
- }
759
- /**
760
- * Find all ancestor context providers for a fiber
761
- * Returns providers in order from root to the fiber's parent
762
- *
763
- * @param fiber - Fiber node to find ancestors for
764
- * @returns Array of provider fibers (root to leaf order)
765
- */
766
- findAncestorProviders(fiber) {
767
- if (!this.currentFiber) {
768
- return [];
769
- }
770
- const providers = [];
771
- this.findAncestorProvidersRecursive(this.currentFiber, fiber, providers, []);
772
- return providers;
773
- }
774
- /**
775
- * Recursively search for ancestor providers
776
- *
777
- * @param currentFiber - Current fiber being examined
778
- * @param targetFiber - Target fiber we're looking for ancestors of
779
- * @param providers - Array to collect providers
780
- * @param path - Current path of provider fibers
781
- * @returns True if targetFiber was found in this subtree
782
- */
783
- findAncestorProvidersRecursive(currentFiber, targetFiber, providers, path) {
784
- // Check if we found the target by comparing paths (works with cloned fibers)
785
- const currentPath = currentFiber.path?.join('.') || '';
786
- const targetPath = targetFiber.path?.join('.') || '';
787
- if (currentPath === targetPath) {
788
- // Add all providers in the path
789
- providers.push(...path);
790
- return true;
791
- }
792
- // Check if this is a provider
793
- const isProvider = typeof currentFiber.type === 'function' && currentFiber.type._isContextProvider;
794
- // Search children
795
- if (currentFiber.children) {
796
- for (const child of currentFiber.children) {
797
- // Add this fiber to path if it's a provider
798
- const newPath = isProvider ? [...path, currentFiber] : path;
799
- if (this.findAncestorProvidersRecursive(child, targetFiber, providers, newPath)) {
800
- return true;
801
- }
802
- }
803
- }
804
- return false;
805
- }
806
- /**
807
- * Find the root component from a set of components
808
- *
809
- * CRITICAL FIX: Find common ancestor instead of just shortest path
810
- * When multiple components have the same path length (e.g., multi-environment apps),
811
- * we need to find their common ancestor, not just pick the first one.
812
- *
813
- * @param components - Array of components
814
- * @returns Root component or null
815
- */
816
- findRootComponent(components) {
817
- if (components.length === 0)
818
- return null;
819
- if (components.length === 1) {
820
- // Single component - traverse up to find actual root
821
- return this.findActualRoot(components[0]);
822
- }
823
- // Find common ancestor by comparing paths
824
- const paths = components.map(c => c.path || []);
825
- const commonPath = this.findCommonPathPrefix(paths);
826
- // Find the fiber at the common path
827
- return this.findFiberByPath(this.currentFiber, commonPath);
828
- }
829
- /**
830
- * Find common path prefix among multiple paths
831
- *
832
- * @param paths - Array of path arrays
833
- * @returns Common prefix path
834
- */
835
- findCommonPathPrefix(paths) {
836
- if (paths.length === 0)
837
- return [];
838
- const shortest = paths.reduce((a, b) => a.length <= b.length ? a : b);
839
- const commonPath = [];
840
- for (let i = 0; i < shortest.length; i++) {
841
- const segment = shortest[i];
842
- if (paths.every(path => path[i] === segment)) {
843
- commonPath.push(segment);
844
- }
845
- else {
846
- break;
847
- }
848
- }
849
- return commonPath;
850
- }
851
- /**
852
- * Find fiber by path in the fiber tree
853
- *
854
- * @param fiber - Root fiber to search from
855
- * @param targetPath - Path to find
856
- * @returns Fiber at path or null
857
- */
858
- findFiberByPath(fiber, targetPath) {
859
- if (!fiber)
860
- return null;
861
- const fiberPath = fiber.path?.join('.') || '';
862
- const target = targetPath.join('.');
863
- if (fiberPath === target)
864
- return fiber;
865
- if (fiber.children) {
866
- for (const child of fiber.children) {
867
- const result = this.findFiberByPath(child, targetPath);
868
- if (result)
869
- return result;
870
- }
871
- }
872
- return null;
873
- }
874
- /**
875
- * Find actual root by traversing up the fiber tree
876
- *
877
- * @param fiber - Starting fiber
878
- * @returns Root fiber
879
- */
880
- findActualRoot(fiber) {
881
- // Traverse up to find the actual root
882
- let current = fiber;
883
- while (current.path && current.path.length > 1) {
884
- // Try to find parent in currentFiber tree
885
- const parentPath = current.path.slice(0, -1);
886
- const parent = this.findFiberByPath(this.currentFiber, parentPath);
887
- if (parent) {
888
- current = parent;
889
- }
890
- else {
891
- break;
892
- }
893
- }
894
- return current;
895
- }
896
- /**
897
- * Recursively find dependent components
898
- *
899
- * @param currentFiber - Current fiber being examined
900
- * @param changedFiber - Fiber that changed
901
- * @param dependents - Set to collect dependents
902
- */
903
- findDependentsRecursive(currentFiber, changedFiber, dependents) {
904
- // Check if current fiber depends on changed fiber
905
- if (currentFiber.dependencies?.has(changedFiber)) {
906
- dependents.add(currentFiber);
907
- }
908
- // Recursively check children
909
- if (currentFiber.children) {
910
- currentFiber.children.forEach((child) => {
911
- this.findDependentsRecursive(child, changedFiber, dependents);
912
- });
913
- }
914
- }
915
- /**
916
- * Set StateBindingManager for context dependency tracker integration
917
- */
918
- setStateBindingManager(stateBindingManager) {
919
- this.contextDependencyTracker.setStateBindingManager(stateBindingManager);
920
- }
921
- /**
922
- * Clone a fiber node for selective updates
923
- *
924
- * @param fiber - Fiber to clone
925
- * @returns Cloned fiber
926
- */
927
- cloneFiberForUpdate(fiber) {
928
- return {
929
- ...fiber,
930
- props: { ...fiber.props },
931
- children: fiber.children
932
- ? fiber.children.map((child) => this.cloneFiberForUpdate(child))
933
- : [],
934
- hooks: fiber.hooks ? [...fiber.hooks] : undefined,
935
- state: fiber.state ? { ...fiber.state } : undefined,
936
- reactiveState: fiber.reactiveState ? { ...fiber.reactiveState } : undefined,
937
- dependencies: fiber.dependencies ? new Set(fiber.dependencies) : undefined,
938
- dependents: fiber.dependents ? new Set(fiber.dependents) : undefined,
939
- cloudDOMNodes: fiber.cloudDOMNodes ? [...fiber.cloudDOMNodes] : undefined,
940
- effectBindings: fiber.effectBindings ? new Map(fiber.effectBindings) : undefined,
941
- };
942
- }
943
- }
944
- exports.Renderer = Renderer;