@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,476 @@
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.ProviderOutputTracker = void 0;
33
+ const Logger_1 = require("../utils/Logger");
34
+ const logger = Logger_1.LoggerFactory.getLogger('hooks');
35
+ /**
36
+ * ProviderOutputTracker - Tracks useInstance calls and their output dependencies
37
+ *
38
+ * Key Features:
39
+ * - Track which components use which provider instances
40
+ * - Detect when provider outputs change
41
+ * - Notify bound components of output changes
42
+ * - Event hook integration for tooling and debugging
43
+ * - REQ-6.2, 6.3, 6.4: Track output reads for automatic binding creation
44
+ */
45
+ class ProviderOutputTracker {
46
+ constructor(eventHooks) {
47
+ this.instanceBindings = new Map();
48
+ this.instanceOutputs = new Map();
49
+ // REQ-6.2, 6.3: Access tracking sessions for dependency analysis
50
+ this.activeSessions = new Map();
51
+ this.eventHooks = eventHooks;
52
+ }
53
+ /**
54
+ * Track a useInstance call - bind a fiber to a CloudDOM node
55
+ * This is called whenever useInstance is called in a component
56
+ */
57
+ trackInstance(node, fiber) {
58
+ // Initialize bindings for this instance if not exists
59
+ if (!this.instanceBindings.has(node.id)) {
60
+ this.instanceBindings.set(node.id, new Set());
61
+ }
62
+ // Add the fiber to the bindings
63
+ this.instanceBindings.get(node.id).add(fiber);
64
+ // Store initial outputs if available
65
+ if (node.outputs) {
66
+ this.instanceOutputs.set(node.id, { ...node.outputs });
67
+ }
68
+ }
69
+ /**
70
+ * Get all fibers bound to a specific instance
71
+ */
72
+ getBindingsForInstance(nodeId) {
73
+ return this.instanceBindings.get(nodeId) || new Set();
74
+ }
75
+ /**
76
+ * Update outputs for an instance and detect changes
77
+ * Returns array of output changes that occurred
78
+ */
79
+ updateInstanceOutputs(nodeId, newOutputs) {
80
+ const previousOutputs = this.instanceOutputs.get(nodeId) || {};
81
+ const changes = [];
82
+ const boundFibers = Array.from(this.getBindingsForInstance(nodeId));
83
+ // Check for changed outputs
84
+ for (const [outputKey, newValue] of Object.entries(newOutputs)) {
85
+ const previousValue = previousOutputs[outputKey];
86
+ if (previousValue !== newValue) {
87
+ changes.push({
88
+ nodeId,
89
+ outputKey,
90
+ previousValue,
91
+ newValue,
92
+ affectedFibers: boundFibers,
93
+ });
94
+ }
95
+ }
96
+ // Check for removed outputs
97
+ for (const [outputKey, previousValue] of Object.entries(previousOutputs)) {
98
+ if (!(outputKey in newOutputs)) {
99
+ changes.push({
100
+ nodeId,
101
+ outputKey,
102
+ previousValue,
103
+ newValue: undefined,
104
+ affectedFibers: boundFibers,
105
+ });
106
+ }
107
+ }
108
+ // Update stored outputs
109
+ this.instanceOutputs.set(nodeId, { ...newOutputs });
110
+ return changes;
111
+ }
112
+ /**
113
+ * Notify bound components of output changes
114
+ * This triggers re-renders for affected components
115
+ */
116
+ notifyOutputChanges(changes) {
117
+ for (const change of changes) {
118
+ const boundFibers = this.getBindingsForInstance(change.nodeId);
119
+ for (const fiber of boundFibers) {
120
+ try {
121
+ // Emit render start event for tooling
122
+ this.eventHooks?.onRenderStart(fiber);
123
+ // Mark fiber as needing re-render due to output change
124
+ if (!fiber.reactiveState) {
125
+ fiber.reactiveState = {
126
+ renderCount: 0,
127
+ isDirty: true,
128
+ updatePending: true,
129
+ lastRenderReason: 'output-update',
130
+ lastRenderTime: Date.now(),
131
+ };
132
+ }
133
+ else {
134
+ fiber.reactiveState.lastRenderReason = 'output-update';
135
+ fiber.reactiveState.lastRenderTime = Date.now();
136
+ fiber.reactiveState.isDirty = true;
137
+ fiber.reactiveState.updatePending = true;
138
+ }
139
+ }
140
+ catch (error) {
141
+ this.eventHooks?.onError(error, fiber);
142
+ }
143
+ }
144
+ }
145
+ }
146
+ /**
147
+ * Process a batch of CloudDOM nodes and detect all output changes
148
+ * This is typically called after deployment completes
149
+ */
150
+ processCloudDOMOutputs(nodes) {
151
+ const allChanges = [];
152
+ for (const node of nodes) {
153
+ if (node.outputs) {
154
+ const changes = this.updateInstanceOutputs(node.id, node.outputs);
155
+ allChanges.push(...changes);
156
+ }
157
+ // Process child nodes recursively
158
+ if (node.children && node.children.length > 0) {
159
+ const childChanges = this.processCloudDOMOutputs(node.children);
160
+ allChanges.push(...childChanges);
161
+ }
162
+ }
163
+ return allChanges;
164
+ }
165
+ /**
166
+ * Remove bindings for a specific fiber (cleanup)
167
+ */
168
+ removeBindingsForFiber(fiber) {
169
+ for (const [nodeId, bindings] of this.instanceBindings) {
170
+ bindings.delete(fiber);
171
+ // Clean up empty binding sets
172
+ if (bindings.size === 0) {
173
+ this.instanceBindings.delete(nodeId);
174
+ this.instanceOutputs.delete(nodeId);
175
+ }
176
+ }
177
+ }
178
+ /**
179
+ * Remove bindings for a specific instance (when resource is deleted)
180
+ */
181
+ removeBindingsForInstance(nodeId) {
182
+ this.instanceBindings.delete(nodeId);
183
+ this.instanceOutputs.delete(nodeId);
184
+ }
185
+ /**
186
+ * Validate bindings and remove invalid ones
187
+ * This should be called periodically to clean up stale bindings
188
+ */
189
+ validateBindings(validNodes) {
190
+ const invalidNodeIds = [];
191
+ for (const nodeId of this.instanceBindings.keys()) {
192
+ if (!validNodes.has(nodeId)) {
193
+ invalidNodeIds.push(nodeId);
194
+ }
195
+ }
196
+ // Remove invalid bindings
197
+ for (const nodeId of invalidNodeIds) {
198
+ this.removeBindingsForInstance(nodeId);
199
+ }
200
+ }
201
+ /**
202
+ * Get current outputs for an instance
203
+ */
204
+ getInstanceOutputs(nodeId) {
205
+ return this.instanceOutputs.get(nodeId);
206
+ }
207
+ /**
208
+ * Check if an instance has any bound components
209
+ */
210
+ hasBindings(nodeId) {
211
+ const bindings = this.instanceBindings.get(nodeId);
212
+ return bindings ? bindings.size > 0 : false;
213
+ }
214
+ /**
215
+ * Get all tracked instances
216
+ */
217
+ getTrackedInstances() {
218
+ return Array.from(this.instanceBindings.keys());
219
+ }
220
+ /**
221
+ * Get statistics about current bindings
222
+ */
223
+ getBindingStats() {
224
+ let totalBindings = 0;
225
+ for (const bindings of this.instanceBindings.values()) {
226
+ totalBindings += bindings.size;
227
+ }
228
+ return {
229
+ totalInstances: this.instanceBindings.size,
230
+ totalBindings,
231
+ instancesWithOutputs: this.instanceOutputs.size,
232
+ };
233
+ }
234
+ /**
235
+ * Get all bindings for debugging/inspection
236
+ */
237
+ getAllBindings() {
238
+ const result = new Map();
239
+ for (const [nodeId, bindings] of this.instanceBindings) {
240
+ result.set(nodeId, {
241
+ fibers: Array.from(bindings),
242
+ outputs: this.instanceOutputs.get(nodeId),
243
+ });
244
+ }
245
+ return result;
246
+ }
247
+ /**
248
+ * Clear all bindings (for testing/cleanup)
249
+ */
250
+ clearAllBindings() {
251
+ this.instanceBindings.clear();
252
+ this.instanceOutputs.clear();
253
+ }
254
+ /**
255
+ * Create a snapshot of current state for comparison
256
+ * Useful for detecting changes between deployments
257
+ */
258
+ createSnapshot() {
259
+ const bindingsSnapshot = new Map();
260
+ const outputsSnapshot = new Map();
261
+ // Create bindings snapshot with fiber paths instead of fiber objects
262
+ for (const [nodeId, fibers] of this.instanceBindings) {
263
+ bindingsSnapshot.set(nodeId, Array.from(fibers).map((f) => f.path.join('.')));
264
+ }
265
+ // Create outputs snapshot
266
+ for (const [nodeId, outputs] of this.instanceOutputs) {
267
+ outputsSnapshot.set(nodeId, { ...outputs });
268
+ }
269
+ return {
270
+ bindings: bindingsSnapshot,
271
+ outputs: outputsSnapshot,
272
+ };
273
+ }
274
+ /**
275
+ * Compare with a previous snapshot to detect changes
276
+ */
277
+ compareWithSnapshot(snapshot) {
278
+ const bindingChanges = {
279
+ added: [],
280
+ removed: [],
281
+ modified: [],
282
+ };
283
+ const outputChanges = [];
284
+ // Check for binding changes
285
+ const currentNodeIds = new Set(this.instanceBindings.keys());
286
+ const snapshotNodeIds = new Set(snapshot.bindings.keys());
287
+ // Added instances
288
+ for (const nodeId of currentNodeIds) {
289
+ if (!snapshotNodeIds.has(nodeId)) {
290
+ bindingChanges.added.push(nodeId);
291
+ }
292
+ }
293
+ // Removed instances
294
+ for (const nodeId of snapshotNodeIds) {
295
+ if (!currentNodeIds.has(nodeId)) {
296
+ bindingChanges.removed.push(nodeId);
297
+ }
298
+ }
299
+ // Modified instances (binding changes)
300
+ for (const nodeId of currentNodeIds) {
301
+ if (snapshotNodeIds.has(nodeId)) {
302
+ const currentPaths = Array.from(this.instanceBindings.get(nodeId)).map((f) => f.path.join('.'));
303
+ const snapshotPaths = snapshot.bindings.get(nodeId);
304
+ if (JSON.stringify(currentPaths.sort()) !== JSON.stringify(snapshotPaths.sort())) {
305
+ bindingChanges.modified.push(nodeId);
306
+ }
307
+ }
308
+ }
309
+ // Check for output changes
310
+ for (const nodeId of currentNodeIds) {
311
+ const currentOutputs = this.instanceOutputs.get(nodeId) || {};
312
+ const snapshotOutputs = snapshot.outputs.get(nodeId) || {};
313
+ const changes = this.compareOutputs(nodeId, snapshotOutputs, currentOutputs);
314
+ outputChanges.push(...changes);
315
+ }
316
+ return { bindingChanges, outputChanges };
317
+ }
318
+ /**
319
+ * Compare two output objects and return changes
320
+ */
321
+ compareOutputs(nodeId, previousOutputs, currentOutputs) {
322
+ const changes = [];
323
+ const boundFibers = Array.from(this.getBindingsForInstance(nodeId));
324
+ // Check for changed/added outputs
325
+ for (const [outputKey, currentValue] of Object.entries(currentOutputs)) {
326
+ const previousValue = previousOutputs[outputKey];
327
+ if (previousValue !== currentValue) {
328
+ changes.push({
329
+ nodeId,
330
+ outputKey,
331
+ previousValue,
332
+ newValue: currentValue,
333
+ affectedFibers: boundFibers,
334
+ });
335
+ }
336
+ }
337
+ // Check for removed outputs
338
+ for (const [outputKey, previousValue] of Object.entries(previousOutputs)) {
339
+ if (!(outputKey in currentOutputs)) {
340
+ changes.push({
341
+ nodeId,
342
+ outputKey,
343
+ previousValue,
344
+ newValue: undefined,
345
+ affectedFibers: boundFibers,
346
+ });
347
+ }
348
+ }
349
+ return changes;
350
+ }
351
+ /**
352
+ * Extract output references for automatic state binding
353
+ * Creates proxy objects that can be used to automatically bind state to outputs
354
+ */
355
+ extractOutputReferences(node) {
356
+ const outputReferences = {};
357
+ if (!node.outputs) {
358
+ return outputReferences;
359
+ }
360
+ // Create output reference objects for each output
361
+ for (const [outputKey, value] of Object.entries(node.outputs)) {
362
+ outputReferences[outputKey] = {
363
+ __providerOutput: {
364
+ nodeId: node.id,
365
+ outputKey,
366
+ value,
367
+ },
368
+ // Also include the actual value for direct access
369
+ valueOf: () => value,
370
+ toString: () => String(value),
371
+ // Make it behave like the actual value in most contexts
372
+ [Symbol.toPrimitive]: () => value,
373
+ };
374
+ }
375
+ return outputReferences;
376
+ }
377
+ /**
378
+ * Update outputs for a specific node and return changes
379
+ * This is an alias for updateInstanceOutputs for compatibility
380
+ */
381
+ updateNodeOutputs(nodeId, newOutputs) {
382
+ return this.updateInstanceOutputs(nodeId, newOutputs);
383
+ }
384
+ /**
385
+ * Process output changes and return affected fibers
386
+ * This is an alias for processCloudDOMOutputs for single node updates
387
+ */
388
+ processOutputChanges(changes) {
389
+ const affectedFibers = new Set();
390
+ for (const change of changes) {
391
+ change.affectedFibers.forEach((fiber) => affectedFibers.add(fiber));
392
+ }
393
+ // Notify about the changes
394
+ this.notifyOutputChanges(changes);
395
+ return affectedFibers;
396
+ }
397
+ /**
398
+ * Get current outputs for a node
399
+ * This is an alias for getInstanceOutputs for compatibility
400
+ */
401
+ getNodeOutputs(nodeId) {
402
+ return this.getInstanceOutputs(nodeId) || {};
403
+ }
404
+ /**
405
+ * Start an access tracking session for a fiber
406
+ * REQ-6.2, 6.3: Track which outputs are accessed during execution
407
+ *
408
+ * @param fiber - Fiber node to track
409
+ */
410
+ startAccessTracking(fiber) {
411
+ this.activeSessions.set(fiber, {
412
+ fiber,
413
+ startTime: Date.now(),
414
+ trackedOutputs: new Set(),
415
+ isActive: true,
416
+ });
417
+ logger.debug(`Started access tracking for ${fiber.path.join('.')}`);
418
+ }
419
+ /**
420
+ * End an access tracking session and return tracked outputs
421
+ * REQ-6.3, 6.4: Collect tracked outputs for binding creation
422
+ *
423
+ * @param fiber - Fiber node to end tracking for
424
+ * @returns Set of binding keys that were accessed
425
+ */
426
+ endAccessTracking(fiber) {
427
+ const session = this.activeSessions.get(fiber);
428
+ if (!session) {
429
+ return new Set();
430
+ }
431
+ session.isActive = false;
432
+ this.activeSessions.delete(fiber);
433
+ logger.debug(`Ended access tracking for ${fiber.path.join('.')}, tracked ${session.trackedOutputs.size} outputs`);
434
+ return session.trackedOutputs;
435
+ }
436
+ /**
437
+ * Track an output read during an active session
438
+ * REQ-6.2, 6.4: Record when outputs are accessed for binding creation
439
+ *
440
+ * @param nodeId - CloudDOM node ID
441
+ * @param outputKey - Output key that was accessed
442
+ * @param fiber - Fiber node that accessed the output
443
+ */
444
+ trackOutputRead(nodeId, outputKey, fiber) {
445
+ const session = this.activeSessions.get(fiber);
446
+ if (session?.isActive) {
447
+ // Generate binding key for this output
448
+ const bindingKey = `${nodeId}.${outputKey}`;
449
+ session.trackedOutputs.add(bindingKey);
450
+ logger.debug(`Tracked output read: ${bindingKey} by ${fiber.path.join('.')}`);
451
+ }
452
+ }
453
+ /**
454
+ * Check if a fiber has an active tracking session
455
+ *
456
+ * @param fiber - Fiber node to check
457
+ * @returns True if tracking is active for this fiber
458
+ */
459
+ isTrackingActive(fiber) {
460
+ const session = this.activeSessions.get(fiber);
461
+ return session?.isActive ?? false;
462
+ }
463
+ /**
464
+ * Get all active tracking sessions (for debugging)
465
+ */
466
+ getActiveSessions() {
467
+ return Array.from(this.activeSessions.values());
468
+ }
469
+ /**
470
+ * Clear all active tracking sessions (for cleanup/testing)
471
+ */
472
+ clearActiveSessions() {
473
+ this.activeSessions.clear();
474
+ }
475
+ }
476
+ exports.ProviderOutputTracker = ProviderOutputTracker;
@@ -0,0 +1,76 @@
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 } from './types';
31
+ /**
32
+ * Global queue for tracking fibers that need re-rendering
33
+ */
34
+ declare class ReactiveUpdateQueue {
35
+ private dirtyFibers;
36
+ private isProcessing;
37
+ /**
38
+ * Enqueue a fiber for re-rendering due to state change
39
+ * Uses Set to automatically deduplicate multiple setState calls
40
+ *
41
+ * @param fiber - Fiber node that needs re-rendering
42
+ */
43
+ enqueue(fiber: FiberNode): void;
44
+ /**
45
+ * Flush the queue and return all dirty fibers
46
+ * Clears the queue after returning
47
+ *
48
+ * @returns Array of dirty fibers that need re-rendering
49
+ */
50
+ flush(): FiberNode[];
51
+ /**
52
+ * Check if a fiber is in the queue
53
+ *
54
+ * @param fiber - Fiber to check
55
+ * @returns True if fiber is queued for re-render
56
+ */
57
+ has(fiber: FiberNode): boolean;
58
+ /**
59
+ * Get current queue size
60
+ *
61
+ * @returns Number of fibers in queue
62
+ */
63
+ size(): number;
64
+ /**
65
+ * Clear the queue without processing
66
+ * Used for cleanup or error recovery
67
+ */
68
+ clear(): void;
69
+ }
70
+ /**
71
+ * Get the global reactive update queue
72
+ *
73
+ * @returns Global ReactiveUpdateQueue instance
74
+ */
75
+ export declare function getReactiveUpdateQueue(): ReactiveUpdateQueue;
76
+ export {};
@@ -0,0 +1,121 @@
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.getReactiveUpdateQueue = getReactiveUpdateQueue;
33
+ /**
34
+ * ReactiveUpdateQueue - Tracks fibers that need re-rendering due to state changes
35
+ *
36
+ * This queue handles user-triggered state changes (via useState) separately from
37
+ * provider-driven output changes. It enables the reactivity phase to detect and
38
+ * re-render components that have internal state changes.
39
+ *
40
+ * REQ-4.4: Fiber dirty tracking for state-driven re-renders
41
+ */
42
+ const Logger_1 = require("../utils/Logger");
43
+ const logger = Logger_1.LoggerFactory.getLogger('hooks');
44
+ /**
45
+ * Global queue for tracking fibers that need re-rendering
46
+ */
47
+ class ReactiveUpdateQueue {
48
+ constructor() {
49
+ this.dirtyFibers = new Set();
50
+ this.isProcessing = false;
51
+ }
52
+ /**
53
+ * Enqueue a fiber for re-rendering due to state change
54
+ * Uses Set to automatically deduplicate multiple setState calls
55
+ *
56
+ * @param fiber - Fiber node that needs re-rendering
57
+ */
58
+ enqueue(fiber) {
59
+ if (this.isProcessing) {
60
+ // Don't enqueue during processing to avoid infinite loops
61
+ logger.debug(`Skipping enqueue during processing: ${fiber.path?.join('.')}`);
62
+ return;
63
+ }
64
+ this.dirtyFibers.add(fiber);
65
+ logger.debug(`Enqueued fiber: ${fiber.path?.join('.')} (queue size: ${this.dirtyFibers.size})`);
66
+ }
67
+ /**
68
+ * Flush the queue and return all dirty fibers
69
+ * Clears the queue after returning
70
+ *
71
+ * @returns Array of dirty fibers that need re-rendering
72
+ */
73
+ flush() {
74
+ if (this.isProcessing) {
75
+ logger.debug('Already processing, returning empty array');
76
+ return [];
77
+ }
78
+ this.isProcessing = true;
79
+ const fibers = Array.from(this.dirtyFibers);
80
+ this.dirtyFibers.clear();
81
+ this.isProcessing = false;
82
+ logger.debug(`Flushed ${fibers.length} dirty fibers`);
83
+ return fibers;
84
+ }
85
+ /**
86
+ * Check if a fiber is in the queue
87
+ *
88
+ * @param fiber - Fiber to check
89
+ * @returns True if fiber is queued for re-render
90
+ */
91
+ has(fiber) {
92
+ return this.dirtyFibers.has(fiber);
93
+ }
94
+ /**
95
+ * Get current queue size
96
+ *
97
+ * @returns Number of fibers in queue
98
+ */
99
+ size() {
100
+ return this.dirtyFibers.size;
101
+ }
102
+ /**
103
+ * Clear the queue without processing
104
+ * Used for cleanup or error recovery
105
+ */
106
+ clear() {
107
+ this.dirtyFibers.clear();
108
+ this.isProcessing = false;
109
+ logger.debug('Queue cleared');
110
+ }
111
+ }
112
+ // Global singleton instance
113
+ const globalQueue = new ReactiveUpdateQueue();
114
+ /**
115
+ * Get the global reactive update queue
116
+ *
117
+ * @returns Global ReactiveUpdateQueue instance
118
+ */
119
+ function getReactiveUpdateQueue() {
120
+ return globalQueue;
121
+ }