@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,153 @@
1
+ /**
2
+
3
+ * Licensed under the Apache License, Version 2.0 (the "License");
4
+
5
+ * you may not use this file except in compliance with the License.
6
+
7
+ * You may obtain a copy of the License at
8
+
9
+ *
10
+
11
+ * http://www.apache.org/licenses/LICENSE-2.0
12
+
13
+ *
14
+
15
+ * Unless required by applicable law or agreed to in writing, software
16
+
17
+ * distributed under the License is distributed on an "AS IS" BASIS,
18
+
19
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20
+
21
+ * See the License for the specific language governing permissions and
22
+
23
+ * limitations under the License.
24
+
25
+ *
26
+
27
+ * Copyright 2025 Daniel Coutinho Ribeiro
28
+
29
+ */
30
+ import { FiberNode, ReRenderReason, CReactEvents } from './types';
31
+ /**
32
+ * RenderScheduler - Manages batched re-rendering of components
33
+ *
34
+ * Provides:
35
+ * - Batched re-rendering to avoid excessive renders
36
+ * - Circular dependency detection
37
+ * - Performance safeguards and rate limiting
38
+ * - Event hook integration for tooling
39
+ */
40
+ export declare class RenderScheduler {
41
+ private pendingReRenders;
42
+ private batchTimeout;
43
+ private eventHooks?;
44
+ private renderChain;
45
+ private maxRenderDepth;
46
+ private rateLimitMap;
47
+ private maxRendersPerSecond;
48
+ private errorRecoveryManager;
49
+ private failedRenders;
50
+ private maxRetries;
51
+ private backoffMultiplier;
52
+ private baseBackoffMs;
53
+ constructor(eventHooks?: CReactEvents);
54
+ /**
55
+ * Schedule a component for re-rendering with mark-and-sweep model
56
+ * Uses batching to avoid excessive renders and prevents duplicate scheduling
57
+ */
58
+ schedule(fiber: FiberNode, reason: ReRenderReason, contextId?: symbol): void;
59
+ /**
60
+ * Schedule the next batch of re-renders
61
+ * Uses setTimeout to batch multiple changes in the same tick
62
+ */
63
+ private scheduleBatch;
64
+ /**
65
+ * Execute all pending re-renders in a batch with error handling and recovery
66
+ */
67
+ private flushBatch;
68
+ /**
69
+ * Execute re-renders for the given fibers
70
+ * Integrates with the Renderer to perform actual re-rendering
71
+ */
72
+ private executeReRenders;
73
+ /**
74
+ * Detect circular dependencies in the render chain
75
+ */
76
+ private detectCircularDependencies;
77
+ /**
78
+ * Check if a fiber has circular dependencies using DFS
79
+ */
80
+ private hasCycle;
81
+ /**
82
+ * Sort fibers by dependency order (dependencies before dependents)
83
+ */
84
+ private sortByDependencies;
85
+ /**
86
+ * Check if a fiber is rate limited
87
+ */
88
+ private isRateLimited;
89
+ /**
90
+ * Get pending re-renders (for testing/debugging)
91
+ */
92
+ getPendingReRenders(): Set<FiberNode>;
93
+ /**
94
+ * Clear all pending re-renders (for testing/cleanup)
95
+ */
96
+ clearPending(): void;
97
+ /**
98
+ * Set performance limits (for testing/configuration)
99
+ */
100
+ setLimits(maxRenderDepth: number, maxRendersPerSecond: number): void;
101
+ /**
102
+ * Create snapshots of fiber states for rollback
103
+ */
104
+ private createFiberSnapshots;
105
+ /**
106
+ * Filter out fibers that have failed too many times
107
+ */
108
+ private filterEligibleFibers;
109
+ /**
110
+ * Execute re-render for a single fiber
111
+ */
112
+ private executeReRender;
113
+ /**
114
+ * Record a failure for a fiber
115
+ */
116
+ private recordFailure;
117
+ /**
118
+ * Clear failure record for a fiber (after successful render)
119
+ */
120
+ private clearFailureRecord;
121
+ /**
122
+ * Handle partial failures with graceful degradation
123
+ */
124
+ private handlePartialFailures;
125
+ /**
126
+ * Determine if successful renders should be rolled back due to dependency failures
127
+ */
128
+ private shouldRollbackSuccessfulRenders;
129
+ /**
130
+ * Rollback specific fibers to their snapshots
131
+ */
132
+ private rollbackFibers;
133
+ /**
134
+ * Rollback all changes (critical error recovery)
135
+ */
136
+ private rollbackAllChanges;
137
+ /**
138
+ * Schedule retries for failed renders with exponential backoff
139
+ */
140
+ private scheduleRetries;
141
+ /**
142
+ * Get failure statistics (for monitoring/debugging)
143
+ */
144
+ getFailureStats(): {
145
+ totalFailures: number;
146
+ fibersWithFailures: number;
147
+ averageFailureCount: number;
148
+ };
149
+ /**
150
+ * Clear all failure records (for testing/cleanup)
151
+ */
152
+ clearFailureRecords(): void;
153
+ }
@@ -0,0 +1,519 @@
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.RenderScheduler = void 0;
33
+ const ErrorRecoveryManager_1 = require("./ErrorRecoveryManager");
34
+ const errors_1 = require("./errors");
35
+ const CReact_1 = require("./CReact");
36
+ const Logger_1 = require("../utils/Logger");
37
+ const logger = Logger_1.LoggerFactory.getLogger('renderer');
38
+ /**
39
+ * RenderScheduler - Manages batched re-rendering of components
40
+ *
41
+ * Provides:
42
+ * - Batched re-rendering to avoid excessive renders
43
+ * - Circular dependency detection
44
+ * - Performance safeguards and rate limiting
45
+ * - Event hook integration for tooling
46
+ */
47
+ class RenderScheduler {
48
+ constructor(eventHooks) {
49
+ this.pendingReRenders = new Set();
50
+ this.batchTimeout = null;
51
+ this.renderChain = [];
52
+ this.maxRenderDepth = 50; // Prevent infinite loops
53
+ this.rateLimitMap = new Map();
54
+ this.maxRendersPerSecond = 10;
55
+ // Error handling and recovery
56
+ this.failedRenders = new Map();
57
+ this.maxRetries = 3;
58
+ this.backoffMultiplier = 2;
59
+ this.baseBackoffMs = 100;
60
+ this.eventHooks = eventHooks;
61
+ this.errorRecoveryManager = new ErrorRecoveryManager_1.ErrorRecoveryManager(eventHooks);
62
+ }
63
+ /**
64
+ * Schedule a component for re-rendering with mark-and-sweep model
65
+ * Uses batching to avoid excessive renders and prevents duplicate scheduling
66
+ */
67
+ schedule(fiber, reason, contextId) {
68
+ // Rate limiting check
69
+ if (this.isRateLimited(fiber)) {
70
+ logger.warn(`Rate limiting re-render for component at path: ${fiber.path.join('.')}`);
71
+ return;
72
+ }
73
+ // Initialize reactive state if needed
74
+ if (!fiber.reactiveState) {
75
+ fiber.reactiveState = {
76
+ renderCount: 0,
77
+ isDirty: false,
78
+ updatePending: false,
79
+ };
80
+ }
81
+ // Mark-and-sweep: avoid duplicate scheduling
82
+ if (fiber.reactiveState.updatePending) {
83
+ // Update context tracking for debugging
84
+ if (contextId && reason === 'context-change') {
85
+ if (!fiber.reactiveState.pendingContexts) {
86
+ fiber.reactiveState.pendingContexts = new Set();
87
+ }
88
+ fiber.reactiveState.pendingContexts.add(contextId);
89
+ }
90
+ return; // Already scheduled, skip duplicate
91
+ }
92
+ // Mark as pending update
93
+ fiber.reactiveState.updatePending = true;
94
+ fiber.reactiveState.lastRenderReason = reason;
95
+ fiber.reactiveState.lastRenderTime = Date.now();
96
+ fiber.reactiveState.isDirty = true;
97
+ // Track context for debugging
98
+ if (contextId && reason === 'context-change') {
99
+ fiber.reactiveState.pendingContexts = new Set([contextId]);
100
+ }
101
+ // Emit telemetry hook
102
+ this.eventHooks?.onFiberReRenderScheduled?.(fiber, reason, contextId);
103
+ this.pendingReRenders.add(fiber);
104
+ this.scheduleBatch();
105
+ }
106
+ /**
107
+ * Schedule the next batch of re-renders
108
+ * Uses setTimeout to batch multiple changes in the same tick
109
+ */
110
+ scheduleBatch() {
111
+ if (this.batchTimeout)
112
+ return;
113
+ this.batchTimeout = setTimeout(() => {
114
+ this.flushBatch();
115
+ }, 0); // Next tick batching
116
+ }
117
+ /**
118
+ * Execute all pending re-renders in a batch with error handling and recovery
119
+ */
120
+ async flushBatch() {
121
+ const fibers = Array.from(this.pendingReRenders);
122
+ this.pendingReRenders.clear();
123
+ this.batchTimeout = null;
124
+ if (fibers.length === 0)
125
+ return;
126
+ // Create snapshots for rollback
127
+ const fiberSnapshots = this.createFiberSnapshots(fibers);
128
+ const successfulRenders = [];
129
+ const failedRenders = [];
130
+ try {
131
+ // Detect circular dependencies
132
+ this.detectCircularDependencies(fibers);
133
+ // Filter out fibers that have failed too many times
134
+ const eligibleFibers = this.filterEligibleFibers(fibers);
135
+ if (eligibleFibers.length === 0) {
136
+ logger.warn('All fibers filtered out due to repeated failures');
137
+ return;
138
+ }
139
+ // Sort by dependency order (dependents after dependencies)
140
+ const sortedFibers = this.sortByDependencies(eligibleFibers);
141
+ // Execute re-renders with individual error isolation
142
+ for (const fiber of sortedFibers) {
143
+ try {
144
+ // Emit render start event
145
+ this.eventHooks?.onRenderStart(fiber);
146
+ this.renderChain.push(fiber);
147
+ // Execute single fiber re-render
148
+ await this.executeReRender(fiber);
149
+ // Mark as successful
150
+ successfulRenders.push(fiber);
151
+ this.clearFailureRecord(fiber);
152
+ // Update reactive state
153
+ if (fiber.reactiveState) {
154
+ fiber.reactiveState.renderCount++;
155
+ fiber.reactiveState.isDirty = false;
156
+ }
157
+ // Emit render complete event
158
+ this.eventHooks?.onRenderComplete(fiber);
159
+ }
160
+ catch (error) {
161
+ // Record failure
162
+ this.recordFailure(fiber, error);
163
+ failedRenders.push({ fiber, error: error });
164
+ // Emit error event
165
+ this.eventHooks?.onError(error, fiber);
166
+ // Continue with other fibers (error isolation)
167
+ logger.warn(`Fiber render failed: ${fiber.path.join('.')}, continuing with others`);
168
+ }
169
+ }
170
+ // Handle partial failures with graceful degradation
171
+ if (failedRenders.length > 0) {
172
+ await this.handlePartialFailures(failedRenders, successfulRenders, fiberSnapshots);
173
+ }
174
+ // Clear render chain
175
+ this.renderChain = [];
176
+ }
177
+ catch (error) {
178
+ // Critical error - rollback all changes
179
+ logger.error('Critical error during batch render, rolling back all changes');
180
+ await this.rollbackAllChanges(fiberSnapshots);
181
+ this.eventHooks?.onError(error);
182
+ throw error;
183
+ }
184
+ }
185
+ /**
186
+ * Execute re-renders for the given fibers
187
+ * Integrates with the Renderer to perform actual re-rendering
188
+ */
189
+ async executeReRenders(fibers) {
190
+ if (fibers.length === 0)
191
+ return;
192
+ try {
193
+ // Get the CReact instance to access the renderer
194
+ const creact = (0, CReact_1.getCReactInstance)();
195
+ if (!creact) {
196
+ logger.warn('No CReact instance available for re-rendering');
197
+ return;
198
+ }
199
+ // Use the CReact rerender method to perform actual re-rendering
200
+ await creact.rerender('default', fibers);
201
+ // Update render count for successfully rendered fibers
202
+ for (const fiber of fibers) {
203
+ if (fiber.reactiveState) {
204
+ fiber.reactiveState.renderCount++;
205
+ fiber.reactiveState.isDirty = false;
206
+ }
207
+ }
208
+ }
209
+ catch (error) {
210
+ logger.error('Failed to execute re-renders:', error);
211
+ throw error;
212
+ }
213
+ }
214
+ /**
215
+ * Detect circular dependencies in the render chain
216
+ */
217
+ detectCircularDependencies(fibers) {
218
+ const visited = new Set();
219
+ const recursionStack = new Set();
220
+ for (const fiber of fibers) {
221
+ if (this.hasCycle(fiber, visited, recursionStack)) {
222
+ const cyclePath = Array.from(recursionStack).map((f) => f.path.join('.'));
223
+ throw new errors_1.CircularDependencyError(`Circular dependency detected in re-render chain: ${cyclePath.join(' -> ')}`, cyclePath);
224
+ }
225
+ }
226
+ // Check render chain depth
227
+ if (this.renderChain.length > this.maxRenderDepth) {
228
+ const chainPath = this.renderChain.map((f) => f.path.join('.')).join(' -> ');
229
+ throw new Error(`Maximum render depth exceeded (${this.maxRenderDepth}): ${chainPath}`);
230
+ }
231
+ }
232
+ /**
233
+ * Check if a fiber has circular dependencies using DFS
234
+ */
235
+ hasCycle(fiber, visited, recursionStack) {
236
+ if (recursionStack.has(fiber)) {
237
+ return true; // Back edge found - cycle detected
238
+ }
239
+ if (visited.has(fiber)) {
240
+ return false; // Already processed
241
+ }
242
+ visited.add(fiber);
243
+ recursionStack.add(fiber);
244
+ // Check dependencies
245
+ if (fiber.dependencies) {
246
+ for (const dependency of Array.from(fiber.dependencies)) {
247
+ if (this.hasCycle(dependency, visited, recursionStack)) {
248
+ return true;
249
+ }
250
+ }
251
+ }
252
+ recursionStack.delete(fiber);
253
+ return false;
254
+ }
255
+ /**
256
+ * Sort fibers by dependency order (dependencies before dependents)
257
+ */
258
+ sortByDependencies(fibers) {
259
+ const sorted = [];
260
+ const visited = new Set();
261
+ const temp = new Set();
262
+ const visit = (fiber) => {
263
+ if (temp.has(fiber)) {
264
+ throw new Error(`Circular dependency detected during sort: ${fiber.path.join('.')}`);
265
+ }
266
+ if (visited.has(fiber)) {
267
+ return;
268
+ }
269
+ temp.add(fiber);
270
+ // Visit dependencies first
271
+ if (fiber.dependencies) {
272
+ for (const dependency of Array.from(fiber.dependencies)) {
273
+ if (fibers.includes(dependency)) {
274
+ visit(dependency);
275
+ }
276
+ }
277
+ }
278
+ temp.delete(fiber);
279
+ visited.add(fiber);
280
+ sorted.push(fiber);
281
+ };
282
+ for (const fiber of fibers) {
283
+ if (!visited.has(fiber)) {
284
+ visit(fiber);
285
+ }
286
+ }
287
+ return sorted;
288
+ }
289
+ /**
290
+ * Check if a fiber is rate limited
291
+ */
292
+ isRateLimited(fiber) {
293
+ const now = Date.now();
294
+ const lastRenderTime = this.rateLimitMap.get(fiber) || 0;
295
+ const timeSinceLastRender = now - lastRenderTime;
296
+ const minInterval = 1000 / this.maxRendersPerSecond; // ms between renders
297
+ if (timeSinceLastRender < minInterval) {
298
+ return true;
299
+ }
300
+ this.rateLimitMap.set(fiber, now);
301
+ return false;
302
+ }
303
+ /**
304
+ * Get pending re-renders (for testing/debugging)
305
+ */
306
+ getPendingReRenders() {
307
+ return new Set(this.pendingReRenders);
308
+ }
309
+ /**
310
+ * Clear all pending re-renders (for testing/cleanup)
311
+ */
312
+ clearPending() {
313
+ if (this.batchTimeout) {
314
+ clearTimeout(this.batchTimeout);
315
+ this.batchTimeout = null;
316
+ }
317
+ this.pendingReRenders.clear();
318
+ this.renderChain = [];
319
+ // Clear updatePending flags from all fibers
320
+ Array.from(this.pendingReRenders).forEach((fiber) => {
321
+ if (fiber.reactiveState) {
322
+ fiber.reactiveState.updatePending = false;
323
+ fiber.reactiveState.pendingContexts?.clear();
324
+ }
325
+ });
326
+ }
327
+ /**
328
+ * Set performance limits (for testing/configuration)
329
+ */
330
+ setLimits(maxRenderDepth, maxRendersPerSecond) {
331
+ this.maxRenderDepth = maxRenderDepth;
332
+ this.maxRendersPerSecond = maxRendersPerSecond;
333
+ }
334
+ // Error Handling and Recovery Methods
335
+ /**
336
+ * Create snapshots of fiber states for rollback
337
+ */
338
+ createFiberSnapshots(fibers) {
339
+ const snapshots = new Map();
340
+ fibers.forEach((fiber) => {
341
+ snapshots.set(fiber, {
342
+ hooks: fiber.hooks ? [...fiber.hooks] : undefined,
343
+ state: fiber.state ? { ...fiber.state } : undefined,
344
+ reactiveState: fiber.reactiveState ? { ...fiber.reactiveState } : undefined,
345
+ cloudDOMNodes: fiber.cloudDOMNodes ? [...fiber.cloudDOMNodes] : undefined,
346
+ });
347
+ });
348
+ return snapshots;
349
+ }
350
+ /**
351
+ * Filter out fibers that have failed too many times
352
+ */
353
+ filterEligibleFibers(fibers) {
354
+ const now = Date.now();
355
+ return fibers.filter((fiber) => {
356
+ const failureRecord = this.failedRenders.get(fiber);
357
+ if (!failureRecord) {
358
+ return true; // Never failed, eligible
359
+ }
360
+ if (failureRecord.count >= this.maxRetries) {
361
+ // Check if enough time has passed for retry with backoff
362
+ const backoffTime = this.baseBackoffMs * Math.pow(this.backoffMultiplier, failureRecord.count - 1);
363
+ const timeSinceLastFailure = now - failureRecord.lastFailure;
364
+ if (timeSinceLastFailure < backoffTime) {
365
+ logger.warn(`Fiber ${fiber.path.join('.')} still in backoff period`);
366
+ return false;
367
+ }
368
+ }
369
+ return true;
370
+ });
371
+ }
372
+ /**
373
+ * Execute re-render for a single fiber
374
+ */
375
+ async executeReRender(fiber) {
376
+ try {
377
+ // Get the CReact instance to access the renderer
378
+ const creact = (0, CReact_1.getCReactInstance)();
379
+ if (!creact) {
380
+ logger.warn('No CReact instance available for re-rendering');
381
+ return;
382
+ }
383
+ // Note: Previously skipped re-renders during initial build, but now that
384
+ // CloudDOMBuilder properly handles re-render scenarios, we allow all re-renders
385
+ // Use selective re-rendering to avoid rebuilding the entire tree
386
+ const renderer = creact.renderer;
387
+ if (renderer && renderer.reRenderComponents) {
388
+ renderer.reRenderComponents([fiber], 'manual');
389
+ }
390
+ else {
391
+ logger.warn('Renderer not available for selective re-rendering');
392
+ }
393
+ // Update render count
394
+ if (fiber.reactiveState) {
395
+ fiber.reactiveState.renderCount++;
396
+ fiber.reactiveState.isDirty = false;
397
+ }
398
+ }
399
+ catch (error) {
400
+ logger.error('Failed to execute re-render for fiber:', fiber.path.join('.'), error);
401
+ throw error;
402
+ }
403
+ }
404
+ /**
405
+ * Record a failure for a fiber
406
+ */
407
+ recordFailure(fiber, error) {
408
+ const existing = this.failedRenders.get(fiber);
409
+ if (existing) {
410
+ existing.count++;
411
+ existing.lastFailure = Date.now();
412
+ existing.error = error;
413
+ }
414
+ else {
415
+ this.failedRenders.set(fiber, {
416
+ count: 1,
417
+ lastFailure: Date.now(),
418
+ error,
419
+ });
420
+ }
421
+ }
422
+ /**
423
+ * Clear failure record for a fiber (after successful render)
424
+ */
425
+ clearFailureRecord(fiber) {
426
+ this.failedRenders.delete(fiber);
427
+ }
428
+ /**
429
+ * Handle partial failures with graceful degradation
430
+ */
431
+ async handlePartialFailures(failedRenders, successfulRenders, fiberSnapshots) {
432
+ logger.warn(`Partial failure: ${failedRenders.length} failed, ${successfulRenders.length} succeeded`);
433
+ // Check if we should rollback successful renders due to dependencies
434
+ const shouldRollback = this.shouldRollbackSuccessfulRenders(failedRenders, successfulRenders);
435
+ if (shouldRollback) {
436
+ logger.warn('Rolling back successful renders due to dependency failures');
437
+ await this.rollbackFibers(successfulRenders, fiberSnapshots);
438
+ }
439
+ else {
440
+ logger.info('Continuing with partial success (graceful degradation)');
441
+ }
442
+ // Schedule retry for failed renders with backoff
443
+ this.scheduleRetries(failedRenders);
444
+ }
445
+ /**
446
+ * Determine if successful renders should be rolled back due to dependency failures
447
+ */
448
+ shouldRollbackSuccessfulRenders(failedRenders, successfulRenders) {
449
+ // Check if any successful render depends on a failed render
450
+ for (const successfulFiber of successfulRenders) {
451
+ if (successfulFiber.dependencies) {
452
+ const dependencyArray = Array.from(successfulFiber.dependencies);
453
+ for (const dependency of dependencyArray) {
454
+ if (failedRenders.some((failed) => failed.fiber === dependency)) {
455
+ return true; // Successful fiber depends on failed fiber
456
+ }
457
+ }
458
+ }
459
+ }
460
+ return false;
461
+ }
462
+ /**
463
+ * Rollback specific fibers to their snapshots
464
+ */
465
+ async rollbackFibers(fibers, snapshots) {
466
+ for (const fiber of fibers) {
467
+ const snapshot = snapshots.get(fiber);
468
+ if (snapshot) {
469
+ // Restore fiber state
470
+ fiber.hooks = snapshot.hooks ? [...snapshot.hooks] : undefined;
471
+ fiber.state = snapshot.state ? { ...snapshot.state } : undefined;
472
+ fiber.reactiveState = snapshot.reactiveState ? { ...snapshot.reactiveState } : undefined;
473
+ fiber.cloudDOMNodes = snapshot.cloudDOMNodes ? [...snapshot.cloudDOMNodes] : undefined;
474
+ }
475
+ }
476
+ }
477
+ /**
478
+ * Rollback all changes (critical error recovery)
479
+ */
480
+ async rollbackAllChanges(snapshots) {
481
+ const fibers = Array.from(snapshots.keys());
482
+ await this.rollbackFibers(fibers, snapshots);
483
+ }
484
+ /**
485
+ * Schedule retries for failed renders with exponential backoff
486
+ */
487
+ scheduleRetries(failedRenders) {
488
+ for (const { fiber } of failedRenders) {
489
+ const failureRecord = this.failedRenders.get(fiber);
490
+ if (failureRecord && failureRecord.count < this.maxRetries) {
491
+ const backoffTime = this.baseBackoffMs * Math.pow(this.backoffMultiplier, failureRecord.count);
492
+ setTimeout(() => {
493
+ logger.info(`Retrying failed render for ${fiber.path.join('.')}`);
494
+ this.schedule(fiber, 'manual'); // Retry with manual reason
495
+ }, backoffTime);
496
+ }
497
+ }
498
+ }
499
+ /**
500
+ * Get failure statistics (for monitoring/debugging)
501
+ */
502
+ getFailureStats() {
503
+ const totalFailures = Array.from(this.failedRenders.values()).reduce((sum, record) => sum + record.count, 0);
504
+ const fibersWithFailures = this.failedRenders.size;
505
+ const averageFailureCount = fibersWithFailures > 0 ? totalFailures / fibersWithFailures : 0;
506
+ return {
507
+ totalFailures,
508
+ fibersWithFailures,
509
+ averageFailureCount,
510
+ };
511
+ }
512
+ /**
513
+ * Clear all failure records (for testing/cleanup)
514
+ */
515
+ clearFailureRecords() {
516
+ this.failedRenders.clear();
517
+ }
518
+ }
519
+ exports.RenderScheduler = RenderScheduler;