@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,105 +0,0 @@
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
- /**
31
- * Effect function type - runs after deployment (supports async)
32
- */
33
- export type EffectCallback = () => void | (() => void) | Promise<void | (() => void)>;
34
- /**
35
- * Dependency array type - same as React
36
- */
37
- export type DependencyList = ReadonlyArray<any>;
38
- /**
39
- * useEffect hook - Infrastructure lifecycle management with reactive capabilities
40
- *
41
- * This hook manages side effects that run during infrastructure lifecycle events.
42
- * It integrates with the reactive system to respond to provider output changes.
43
- *
44
- * Infrastructure Lifecycle:
45
- * 1. **Build Phase**: Component renders, useEffect is registered
46
- * 2. **Deploy Phase**: Resources are materialized by cloud provider
47
- * 3. **Post-Deploy Phase**: useEffect callbacks run with real resource outputs
48
- * 4. **Reactive Phase**: Effects re-run when dependencies change (including provider outputs)
49
- * 5. **Destroy Phase**: Cleanup functions are called before stack destruction
50
- *
51
- * Behavior based on dependencies:
52
- * - **No dependencies (undefined)**: Runs on every deploy, cleanup on destroy
53
- * - **Empty dependencies ([])**: Runs once on first deploy, cleanup on destroy
54
- * - **With dependencies**: Runs when dependencies change, cleanup before re-run or destroy
55
- *
56
- * @param effect - Effect callback that runs after deployment
57
- * @param deps - Dependency array (same semantics as React)
58
- *
59
- * @example
60
- * ```tsx
61
- * function DatabaseStack() {
62
- * const [dbUrl, setDbUrl] = useState<string>();
63
- * const db = useInstance(PostgresDatabase, { name: 'my-db' });
64
- *
65
- * // Runs on every deploy, cleanup on destroy
66
- * useEffect(() => {
67
- * logger.info('Database deployed');
68
- * return () => logger.info('Database destroyed');
69
- * });
70
- *
71
- * // Runs when db.outputs.connectionUrl changes
72
- * useEffect(() => {
73
- * if (db.outputs?.connectionUrl) {
74
- * setDbUrl(db.outputs.connectionUrl);
75
- * }
76
- * }, [db.outputs?.connectionUrl]);
77
- *
78
- * return <DatabaseContext.Provider value={{ dbUrl }}>{children}</DatabaseContext.Provider>;
79
- * }
80
- * ```
81
- */
82
- export declare function useEffect(effect: EffectCallback, deps?: DependencyList): void;
83
- /**
84
- * Execute effects for a Fiber node after deployment
85
- * Called by CReact core after successful deployment
86
- *
87
- * @internal
88
- */
89
- export declare function executeEffects(fiber: any, changedOutputs?: Set<string>): void;
90
- /**
91
- * Execute effects when provider outputs change (reactive execution)
92
- * Called by the reactive system when bound outputs change
93
- *
94
- * @param fiber - Fiber node containing effects
95
- * @param changedOutputs - Set of output keys that changed
96
- * @internal
97
- */
98
- export declare function executeEffectsOnOutputChange(fiber: any, changedOutputs: Set<string>): void;
99
- /**
100
- * Cleanup all effects for a Fiber node before stack destroy
101
- * Called by CReact core before stack destruction
102
- *
103
- * @internal
104
- */
105
- export declare function cleanupEffects(fiber: any): void;
@@ -1,540 +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.useEffect = useEffect;
33
- exports.executeEffects = executeEffects;
34
- exports.executeEffectsOnOutputChange = executeEffectsOnOutputChange;
35
- exports.cleanupEffects = cleanupEffects;
36
- // REQ-03: useEffect hook for infrastructure lifecycle management
37
- // This hook manages side effects that run between deployment cycles
38
- const context_1 = require("./context");
39
- const naming_1 = require("../utils/naming");
40
- const useInstance_1 = require("./useInstance");
41
- const Logger_1 = require("../utils/Logger");
42
- const logger = Logger_1.LoggerFactory.getLogger('hooks');
43
- /**
44
- * useEffect hook - Infrastructure lifecycle management with reactive capabilities
45
- *
46
- * This hook manages side effects that run during infrastructure lifecycle events.
47
- * It integrates with the reactive system to respond to provider output changes.
48
- *
49
- * Infrastructure Lifecycle:
50
- * 1. **Build Phase**: Component renders, useEffect is registered
51
- * 2. **Deploy Phase**: Resources are materialized by cloud provider
52
- * 3. **Post-Deploy Phase**: useEffect callbacks run with real resource outputs
53
- * 4. **Reactive Phase**: Effects re-run when dependencies change (including provider outputs)
54
- * 5. **Destroy Phase**: Cleanup functions are called before stack destruction
55
- *
56
- * Behavior based on dependencies:
57
- * - **No dependencies (undefined)**: Runs on every deploy, cleanup on destroy
58
- * - **Empty dependencies ([])**: Runs once on first deploy, cleanup on destroy
59
- * - **With dependencies**: Runs when dependencies change, cleanup before re-run or destroy
60
- *
61
- * @param effect - Effect callback that runs after deployment
62
- * @param deps - Dependency array (same semantics as React)
63
- *
64
- * @example
65
- * ```tsx
66
- * function DatabaseStack() {
67
- * const [dbUrl, setDbUrl] = useState<string>();
68
- * const db = useInstance(PostgresDatabase, { name: 'my-db' });
69
- *
70
- * // Runs on every deploy, cleanup on destroy
71
- * useEffect(() => {
72
- * logger.info('Database deployed');
73
- * return () => logger.info('Database destroyed');
74
- * });
75
- *
76
- * // Runs when db.outputs.connectionUrl changes
77
- * useEffect(() => {
78
- * if (db.outputs?.connectionUrl) {
79
- * setDbUrl(db.outputs.connectionUrl);
80
- * }
81
- * }, [db.outputs?.connectionUrl]);
82
- *
83
- * return <DatabaseContext.Provider value={{ dbUrl }}>{children}</DatabaseContext.Provider>;
84
- * }
85
- * ```
86
- */
87
- function useEffect(effect, deps) {
88
- // Use consolidated hook context
89
- const context = (0, context_1.requireHookContext)();
90
- const currentFiber = context.currentFiber; // Non-null assertion safe due to requireHookContext validation
91
- // Initialize effects array in Fiber node if not already present
92
- if (!currentFiber.effects) {
93
- currentFiber.effects = [];
94
- }
95
- // Get current hook index and increment for next call (effect-specific)
96
- const currentHookIndex = (0, context_1.incrementHookIndex)('effect');
97
- // Analyze dependencies for provider output tracking
98
- const boundOutputs = new Set();
99
- const isReactive = deps !== undefined && deps.length > 0;
100
- if (isReactive && deps) {
101
- // REQ-5.2, 5.3, 5.4: Start access tracking session before evaluating dependencies
102
- // This will track which outputs are actually accessed during dependency evaluation
103
- const outputTracker = (0, useInstance_1.getProviderOutputTrackerInstance)();
104
- if (outputTracker) {
105
- outputTracker.startAccessTracking(currentFiber);
106
- if (process.env.CREACT_DEBUG === 'true') {
107
- logger.debug(`Started access tracking for dependency evaluation`);
108
- }
109
- }
110
- // REQ-5.3, 5.4: Access dependencies to trigger proxy tracking
111
- // This will record which outputs are actually read
112
- for (const dep of deps) {
113
- // Simply accessing the dependency triggers the proxy tracking
114
- // The proxy in useInstance will call outputTracker.trackOutputRead()
115
- void dep; // Access the dependency to trigger tracking
116
- }
117
- // REQ-5.3, 5.4: End access tracking session and collect tracked outputs
118
- if (outputTracker) {
119
- const trackedOutputs = outputTracker.endAccessTracking(currentFiber);
120
- // Add tracked outputs to boundOutputs
121
- trackedOutputs.forEach((bindingKey) => {
122
- boundOutputs.add(bindingKey);
123
- });
124
- if (process.env.CREACT_DEBUG === 'true') {
125
- logger.debug(`Tracked ${trackedOutputs.size} output accesses during dependency evaluation`);
126
- if (trackedOutputs.size > 0) {
127
- logger.debug(`Tracked outputs: ${Array.from(trackedOutputs).join(', ')}`);
128
- }
129
- }
130
- }
131
- // Track which CloudDOM nodes are referenced in this effect
132
- const referencedNodes = new Set();
133
- // Get current fiber's CloudDOM nodes for reference tracking
134
- const fiberCloudDOMNodes = currentFiber.cloudDOMNodes || [];
135
- const nodeMap = new Map();
136
- // Build a map from node objects to their IDs
137
- for (const node of fiberCloudDOMNodes) {
138
- nodeMap.set(node, node.id);
139
- }
140
- // Analyze dependencies to find CloudDOM node references
141
- for (const dep of deps) {
142
- if (dep && typeof dep === 'object') {
143
- // Check if dependency is a provider output reference
144
- if (dep.__providerOutput) {
145
- const bindingKey = (0, naming_1.generateBindingKey)(dep.__providerOutput.nodeId, dep.__providerOutput.outputKey);
146
- boundOutputs.add(bindingKey);
147
- if (process.env.CREACT_DEBUG === 'true') {
148
- logger.debug(`Detected provider output dependency: ${bindingKey}`);
149
- }
150
- }
151
- // Check if dependency is a CloudDOM output reference
152
- else if (dep.__cloudDOMOutput) {
153
- const bindingKey = (0, naming_1.generateBindingKey)(dep.__cloudDOMOutput.nodeId, dep.__cloudDOMOutput.outputKey);
154
- boundOutputs.add(bindingKey);
155
- if (process.env.CREACT_DEBUG === 'true') {
156
- logger.debug(`Detected CloudDOM output dependency: ${bindingKey}`);
157
- }
158
- }
159
- // Check if dependency is a CloudDOM node (from useInstance)
160
- else if (nodeMap.has(dep)) {
161
- const nodeId = nodeMap.get(dep);
162
- referencedNodes.add(nodeId);
163
- if (process.env.CREACT_DEBUG === 'true') {
164
- logger.debug(`Detected CloudDOM node reference: ${nodeId}`);
165
- }
166
- }
167
- }
168
- }
169
- // For referenced nodes, bind to all their current and future outputs
170
- for (const nodeId of referencedNodes) {
171
- const node = fiberCloudDOMNodes.find((n) => n.id === nodeId);
172
- if (node) {
173
- // REQ-5.1, 5.2: Only bind to specific outputs that are actually accessed
174
- // No wildcard bindings - precise tracking only
175
- if (node.outputs) {
176
- for (const outputKey of Object.keys(node.outputs)) {
177
- const bindingKey = (0, naming_1.generateBindingKey)(nodeId, outputKey);
178
- boundOutputs.add(bindingKey);
179
- }
180
- }
181
- }
182
- }
183
- if (process.env.CREACT_DEBUG === 'true' && boundOutputs.size > 0) {
184
- logger.debug(`Bound to ${boundOutputs.size} output patterns: ${Array.from(boundOutputs).join(', ')}`);
185
- }
186
- }
187
- // Generate dependency hash for change detection
188
- const depsHash = deps ? generateDependencyHash(deps) : undefined;
189
- // Generate unique effect ID for debugging and orchestrator logs
190
- const effectId = `${currentFiber.path.join('.')}:${currentHookIndex}`;
191
- // REQ-5.1, 5.5: Create enhanced dependency comparison method
192
- const hasDepChanged = function (newDeps) {
193
- if (!this.lastDepsValues)
194
- return true;
195
- if (this.lastDepsValues.length !== newDeps.length)
196
- return true;
197
- return this.lastDepsValues.some((prev, i) => {
198
- const curr = newDeps[i];
199
- // REQ-5.5: Handle provider outputs specially
200
- if (isProviderOutput(prev) && isProviderOutput(curr)) {
201
- // Compare by nodeId and outputKey, not by value
202
- return (prev.__providerOutput.nodeId !== curr.__providerOutput.nodeId ||
203
- prev.__providerOutput.outputKey !== curr.__providerOutput.outputKey);
204
- }
205
- // Handle CloudDOM outputs
206
- if (isCloudDOMOutput(prev) && isCloudDOMOutput(curr)) {
207
- return (prev.__cloudDOMOutput.nodeId !== curr.__cloudDOMOutput.nodeId ||
208
- prev.__cloudDOMOutput.outputKey !== curr.__cloudDOMOutput.outputKey);
209
- }
210
- // Standard comparison for other values
211
- return prev !== curr;
212
- });
213
- };
214
- // Store effect data in Fiber node
215
- const effectHook = {
216
- callback: effect,
217
- deps,
218
- hasRun: false,
219
- boundOutputs: boundOutputs.size > 0 ? boundOutputs : undefined,
220
- lastDepsHash: depsHash,
221
- lastDepsValues: deps ? [...deps] : undefined, // REQ-5.5: Store actual values
222
- isReactive,
223
- effectId,
224
- hasDepChanged, // REQ-5.1: Attach comparison method
225
- };
226
- currentFiber.effects[currentHookIndex] = effectHook;
227
- // Register effect with reactive system if it has bound outputs
228
- if (boundOutputs.size > 0) {
229
- registerEffectWithReactiveSystem(currentFiber, currentHookIndex, boundOutputs);
230
- }
231
- }
232
- /**
233
- * Check if a value is a provider output
234
- * REQ-5.5: Helper for enhanced dependency comparison
235
- */
236
- function isProviderOutput(value) {
237
- return value && typeof value === 'object' && value.__providerOutput;
238
- }
239
- /**
240
- * Check if a value is a CloudDOM output
241
- * REQ-5.5: Helper for enhanced dependency comparison
242
- */
243
- function isCloudDOMOutput(value) {
244
- return value && typeof value === 'object' && value.__cloudDOMOutput;
245
- }
246
- /**
247
- * Generate a hash of dependencies for change detection using consistent naming
248
- */
249
- function generateDependencyHash(deps) {
250
- try {
251
- return JSON.stringify(deps.map((dep) => {
252
- if (dep && typeof dep === 'object') {
253
- // For provider outputs, use binding key for stable representation
254
- if (dep.__providerOutput) {
255
- const bindingKey = (0, naming_1.generateBindingKey)(dep.__providerOutput.nodeId, dep.__providerOutput.outputKey);
256
- return `__providerOutput:${bindingKey}`;
257
- }
258
- if (dep.__cloudDOMOutput) {
259
- const bindingKey = (0, naming_1.generateBindingKey)(dep.__cloudDOMOutput.nodeId, dep.__cloudDOMOutput.outputKey);
260
- return `__cloudDOMOutput:${bindingKey}`;
261
- }
262
- // For other objects, try to create a stable hash
263
- return JSON.stringify(dep);
264
- }
265
- return dep;
266
- }));
267
- }
268
- catch (error) {
269
- // Fallback for non-serializable dependencies
270
- return `hash_error_${Date.now()}`;
271
- }
272
- }
273
- /**
274
- * Register effect with reactive system for output change notifications
275
- * Uses binding keys for consistent output tracking
276
- */
277
- function registerEffectWithReactiveSystem(fiber, effectIndex, boundOutputs) {
278
- // This would integrate with the reactive system to notify when outputs change
279
- // For now, we store the binding information in the fiber for later use
280
- if (!fiber.effectBindings) {
281
- fiber.effectBindings = new Map();
282
- }
283
- const bindingKeys = Array.from(boundOutputs);
284
- fiber.effectBindings.set(effectIndex, {
285
- boundOutputs: bindingKeys, // These are already binding keys from generateBindingKey
286
- registeredAt: Date.now(),
287
- });
288
- if (process.env.CREACT_DEBUG === 'true') {
289
- logger.debug(`Registered effect ${effectIndex} with reactive system for outputs: ${bindingKeys.join(', ')}`);
290
- }
291
- }
292
- /**
293
- * Check if effect dependencies have changed, including provider outputs
294
- * REQ-5.1, 5.5: Use enhanced dependency comparison
295
- */
296
- function hasEffectDependenciesChanged(current, previous, changedOutputs) {
297
- // Always run on first execution
298
- if (!previous || !current.hasRun) {
299
- return true;
300
- }
301
- // No dependencies - run every deployment
302
- if (current.deps === undefined) {
303
- return true;
304
- }
305
- // Empty dependencies - run only once
306
- if (current.deps.length === 0) {
307
- return false;
308
- }
309
- // Check if any bound outputs have changed (using binding keys)
310
- if (current.boundOutputs && changedOutputs) {
311
- for (const boundOutputKey of current.boundOutputs) {
312
- if (changedOutputs.has(boundOutputKey)) {
313
- if (process.env.CREACT_DEBUG === 'true') {
314
- logger.debug(`Effect dependency changed: ${boundOutputKey}`);
315
- }
316
- return true;
317
- }
318
- }
319
- }
320
- // REQ-5.1, 5.5: Use enhanced dependency comparison if available
321
- if (current.hasDepChanged && current.deps) {
322
- return current.hasDepChanged([...current.deps]);
323
- }
324
- // Fallback: Compare dependency hash
325
- if (current.lastDepsHash !== previous.lastDepsHash) {
326
- return true;
327
- }
328
- // Fallback: Direct comparison
329
- if (!previous.deps || previous.deps.length !== current.deps.length) {
330
- return true;
331
- }
332
- for (let i = 0; i < current.deps.length; i++) {
333
- if (current.deps[i] !== previous.deps[i]) {
334
- return true;
335
- }
336
- }
337
- return false;
338
- }
339
- /**
340
- * Execute effects for a Fiber node after deployment
341
- * Called by CReact core after successful deployment
342
- *
343
- * @internal
344
- */
345
- function executeEffects(fiber, changedOutputs) {
346
- if (process.env.CREACT_DEBUG === 'true') {
347
- logger.debug(`executeEffects called for fiber: ${fiber.path?.join('.')}`);
348
- logger.debug(`Fiber has effects: ${!!fiber.effects}, length: ${fiber.effects?.length || 0}`);
349
- if (changedOutputs && changedOutputs.size > 0) {
350
- logger.debug(`Changed outputs: ${Array.from(changedOutputs).join(', ')}`);
351
- }
352
- }
353
- if (!fiber.effects || !Array.isArray(fiber.effects)) {
354
- if (process.env.CREACT_DEBUG === 'true') {
355
- logger.debug(`No effects found for fiber: ${fiber.path?.join('.')}`);
356
- }
357
- return;
358
- }
359
- if (process.env.CREACT_DEBUG === 'true') {
360
- logger.debug(`Executing ${fiber.effects.length} effects for fiber: ${fiber.path?.join('.')}`);
361
- }
362
- for (let i = 0; i < fiber.effects.length; i++) {
363
- const effectHook = fiber.effects[i];
364
- if (!effectHook) {
365
- if (process.env.CREACT_DEBUG === 'true') {
366
- logger.debug(`Effect ${i} is null/undefined`);
367
- }
368
- continue;
369
- }
370
- // Check if effect should run based on dependencies and output changes
371
- const shouldRun = hasEffectDependenciesChanged(effectHook, fiber.previousEffects?.[i], changedOutputs);
372
- logger.debug(`Effect ${i} should run: ${shouldRun}`);
373
- if (effectHook.boundOutputs && effectHook.boundOutputs.size > 0) {
374
- logger.debug(`Effect ${i} bound to outputs: ${Array.from(effectHook.boundOutputs).join(', ')}`);
375
- }
376
- if (shouldRun) {
377
- // Run cleanup from previous effect if it exists
378
- if (effectHook.cleanup) {
379
- try {
380
- if (process.env.CREACT_DEBUG === 'true') {
381
- logger.debug(`Running cleanup for effect ${i}`);
382
- }
383
- effectHook.cleanup();
384
- effectHook.cleanup = undefined; // Clear cleanup after running
385
- }
386
- catch (error) {
387
- logger.error('Cleanup error:', error);
388
- }
389
- }
390
- // Run the effect with async support and tracing
391
- try {
392
- if (process.env.CREACT_DEBUG === 'true') {
393
- logger.debug(`Running effect ${effectHook.effectId || i}`);
394
- }
395
- const result = effectHook.callback();
396
- // Handle async effects
397
- if (result instanceof Promise) {
398
- result
399
- .then((cleanup) => {
400
- if (typeof cleanup === 'function') {
401
- effectHook.cleanup = cleanup;
402
- }
403
- if (process.env.CREACT_DEBUG === 'true') {
404
- logger.debug(`Async effect ${effectHook.effectId || i} completed successfully`);
405
- }
406
- })
407
- .catch((error) => {
408
- logger.error(`Async effect ${effectHook.effectId || i} error:`, error);
409
- });
410
- }
411
- else {
412
- // Handle sync effects
413
- if (typeof result === 'function') {
414
- effectHook.cleanup = result;
415
- }
416
- if (process.env.CREACT_DEBUG === 'true') {
417
- logger.debug(`Sync effect ${effectHook.effectId || i} completed successfully`);
418
- }
419
- }
420
- effectHook.hasRun = true;
421
- // REQ-5.5: Update dependency values and hash for next comparison
422
- if (effectHook.deps) {
423
- effectHook.lastDepsValues = [...effectHook.deps];
424
- effectHook.lastDepsHash = generateDependencyHash(effectHook.deps);
425
- }
426
- }
427
- catch (error) {
428
- logger.error(`Effect ${effectHook.effectId || i} error:`, error);
429
- }
430
- }
431
- }
432
- // Store current effects as previous for next deployment
433
- fiber.previousEffects = [...fiber.effects];
434
- }
435
- /**
436
- * Execute effects when provider outputs change (reactive execution)
437
- * Called by the reactive system when bound outputs change
438
- *
439
- * @param fiber - Fiber node containing effects
440
- * @param changedOutputs - Set of output keys that changed
441
- * @internal
442
- */
443
- function executeEffectsOnOutputChange(fiber, changedOutputs) {
444
- if (!fiber.effects || !Array.isArray(fiber.effects)) {
445
- return;
446
- }
447
- if (process.env.CREACT_DEBUG === 'true') {
448
- logger.debug(`Executing effects on output change for fiber: ${fiber.path?.join('.')}`);
449
- logger.debug(`Changed outputs: ${Array.from(changedOutputs).join(', ')}`);
450
- }
451
- for (let i = 0; i < fiber.effects.length; i++) {
452
- const effectHook = fiber.effects[i];
453
- if (!effectHook || !effectHook.isReactive) {
454
- continue; // Skip non-reactive effects
455
- }
456
- // Check if this effect is bound to any of the changed outputs
457
- // REQ-5.2, 5.4: Only check specific bindings, no wildcard matching
458
- if (effectHook.boundOutputs) {
459
- const hasChangedDependency = Array.from(effectHook.boundOutputs).some((boundOutput) => {
460
- // Direct match only - no wildcard patterns
461
- return changedOutputs.has(boundOutput);
462
- });
463
- if (hasChangedDependency) {
464
- // Run cleanup from previous effect if it exists
465
- if (effectHook.cleanup) {
466
- try {
467
- if (process.env.CREACT_DEBUG === 'true') {
468
- logger.debug(`Running cleanup for reactive effect ${i}`);
469
- }
470
- effectHook.cleanup();
471
- effectHook.cleanup = undefined;
472
- }
473
- catch (error) {
474
- logger.error('Reactive cleanup error:', error);
475
- }
476
- }
477
- // Run the reactive effect with async support and tracing
478
- try {
479
- if (process.env.CREACT_DEBUG === 'true') {
480
- logger.debug(`Running reactive effect ${effectHook.effectId || i}`);
481
- }
482
- const result = effectHook.callback();
483
- // Handle async reactive effects
484
- if (result instanceof Promise) {
485
- result
486
- .then((cleanup) => {
487
- if (typeof cleanup === 'function') {
488
- effectHook.cleanup = cleanup;
489
- }
490
- if (process.env.CREACT_DEBUG === 'true') {
491
- logger.debug(`Async reactive effect ${effectHook.effectId || i} completed successfully`);
492
- }
493
- })
494
- .catch((error) => {
495
- logger.error(`Async reactive effect ${effectHook.effectId || i} error:`, error);
496
- });
497
- }
498
- else {
499
- // Handle sync reactive effects
500
- if (typeof result === 'function') {
501
- effectHook.cleanup = result;
502
- }
503
- if (process.env.CREACT_DEBUG === 'true') {
504
- logger.debug(`Sync reactive effect ${effectHook.effectId || i} completed successfully`);
505
- }
506
- }
507
- // REQ-5.5: Update dependency values and hash
508
- if (effectHook.deps) {
509
- effectHook.lastDepsValues = [...effectHook.deps];
510
- effectHook.lastDepsHash = generateDependencyHash(effectHook.deps);
511
- }
512
- }
513
- catch (error) {
514
- logger.error(`Reactive effect ${effectHook.effectId || i} error:`, error);
515
- }
516
- }
517
- }
518
- }
519
- }
520
- /**
521
- * Cleanup all effects for a Fiber node before stack destroy
522
- * Called by CReact core before stack destruction
523
- *
524
- * @internal
525
- */
526
- function cleanupEffects(fiber) {
527
- if (!fiber.effects || !Array.isArray(fiber.effects)) {
528
- return;
529
- }
530
- for (const effectHook of fiber.effects) {
531
- if (effectHook?.cleanup) {
532
- try {
533
- effectHook.cleanup();
534
- }
535
- catch (error) {
536
- logger.error('Cleanup error:', error);
537
- }
538
- }
539
- }
540
- }