@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.
- package/README.md +85 -22
- package/dist/cli.d.ts +11 -0
- package/dist/cli.js +88 -0
- package/dist/index.d.ts +19 -44
- package/dist/index.js +20 -68
- package/dist/jsx/index.d.ts +2 -0
- package/dist/jsx/index.js +1 -0
- package/dist/jsx/jsx-dev-runtime.d.ts +4 -0
- package/dist/jsx/jsx-dev-runtime.js +4 -0
- package/dist/jsx/jsx-runtime.d.ts +38 -0
- package/dist/jsx/jsx-runtime.js +38 -0
- package/dist/jsx/types.d.ts +12 -0
- package/dist/jsx/types.js +4 -0
- package/dist/primitives/context.d.ts +34 -0
- package/dist/primitives/context.js +63 -0
- package/dist/primitives/index.d.ts +3 -0
- package/dist/primitives/index.js +3 -0
- package/dist/primitives/instance.d.ts +72 -0
- package/dist/primitives/instance.js +235 -0
- package/dist/primitives/store.d.ts +22 -0
- package/dist/primitives/store.js +97 -0
- package/dist/provider/backend.d.ts +110 -0
- package/dist/provider/backend.js +37 -0
- package/dist/provider/interface.d.ts +48 -0
- package/dist/provider/interface.js +39 -0
- package/dist/reactive/effect.d.ts +11 -0
- package/dist/reactive/effect.js +42 -0
- package/dist/reactive/index.d.ts +3 -0
- package/dist/reactive/index.js +3 -0
- package/dist/reactive/signal.d.ts +32 -0
- package/dist/reactive/signal.js +60 -0
- package/dist/reactive/tracking.d.ts +41 -0
- package/dist/reactive/tracking.js +161 -0
- package/dist/runtime/fiber.d.ts +21 -0
- package/dist/runtime/fiber.js +16 -0
- package/dist/runtime/index.d.ts +4 -0
- package/dist/runtime/index.js +4 -0
- package/dist/runtime/reconcile.d.ts +66 -0
- package/dist/runtime/reconcile.js +210 -0
- package/dist/runtime/render.d.ts +42 -0
- package/dist/runtime/render.js +231 -0
- package/dist/runtime/run.d.ts +119 -0
- package/dist/runtime/run.js +334 -0
- package/dist/runtime/state-machine.d.ts +95 -0
- package/dist/runtime/state-machine.js +209 -0
- package/dist/types.d.ts +13 -0
- package/dist/types.js +4 -0
- package/package.json +29 -24
- package/dist/cli/commands/BuildCommand.d.ts +0 -40
- package/dist/cli/commands/BuildCommand.js +0 -151
- package/dist/cli/commands/DeployCommand.d.ts +0 -38
- package/dist/cli/commands/DeployCommand.js +0 -194
- package/dist/cli/commands/DevCommand.d.ts +0 -52
- package/dist/cli/commands/DevCommand.js +0 -394
- package/dist/cli/commands/PlanCommand.d.ts +0 -39
- package/dist/cli/commands/PlanCommand.js +0 -164
- package/dist/cli/commands/index.d.ts +0 -36
- package/dist/cli/commands/index.js +0 -43
- package/dist/cli/core/ArgumentParser.d.ts +0 -46
- package/dist/cli/core/ArgumentParser.js +0 -127
- package/dist/cli/core/BaseCommand.d.ts +0 -75
- package/dist/cli/core/BaseCommand.js +0 -95
- package/dist/cli/core/CLIContext.d.ts +0 -68
- package/dist/cli/core/CLIContext.js +0 -183
- package/dist/cli/core/CommandRegistry.d.ts +0 -64
- package/dist/cli/core/CommandRegistry.js +0 -89
- package/dist/cli/core/index.d.ts +0 -36
- package/dist/cli/core/index.js +0 -43
- package/dist/cli/index.d.ts +0 -35
- package/dist/cli/index.js +0 -100
- package/dist/cli/output.d.ts +0 -204
- package/dist/cli/output.js +0 -437
- package/dist/cli/utils.d.ts +0 -59
- package/dist/cli/utils.js +0 -76
- package/dist/context/createContext.d.ts +0 -90
- package/dist/context/createContext.js +0 -113
- package/dist/context/index.d.ts +0 -30
- package/dist/context/index.js +0 -35
- package/dist/core/CReact.d.ts +0 -409
- package/dist/core/CReact.js +0 -1151
- package/dist/core/CloudDOMBuilder.d.ts +0 -447
- package/dist/core/CloudDOMBuilder.js +0 -1234
- package/dist/core/ContextDependencyTracker.d.ts +0 -165
- package/dist/core/ContextDependencyTracker.js +0 -448
- package/dist/core/ErrorRecoveryManager.d.ts +0 -145
- package/dist/core/ErrorRecoveryManager.js +0 -443
- package/dist/core/EventBus.d.ts +0 -91
- package/dist/core/EventBus.js +0 -185
- package/dist/core/ProviderOutputTracker.d.ts +0 -211
- package/dist/core/ProviderOutputTracker.js +0 -476
- package/dist/core/ReactiveUpdateQueue.d.ts +0 -76
- package/dist/core/ReactiveUpdateQueue.js +0 -121
- package/dist/core/Reconciler.d.ts +0 -415
- package/dist/core/Reconciler.js +0 -1044
- package/dist/core/RenderScheduler.d.ts +0 -153
- package/dist/core/RenderScheduler.js +0 -519
- package/dist/core/Renderer.d.ts +0 -336
- package/dist/core/Renderer.js +0 -944
- package/dist/core/Runtime.d.ts +0 -246
- package/dist/core/Runtime.js +0 -640
- package/dist/core/StateBindingManager.d.ts +0 -121
- package/dist/core/StateBindingManager.js +0 -309
- package/dist/core/StateMachine.d.ts +0 -441
- package/dist/core/StateMachine.js +0 -883
- package/dist/core/StructuralChangeDetector.d.ts +0 -140
- package/dist/core/StructuralChangeDetector.js +0 -363
- package/dist/core/Validator.d.ts +0 -127
- package/dist/core/Validator.js +0 -279
- package/dist/core/errors.d.ts +0 -153
- package/dist/core/errors.js +0 -202
- package/dist/core/index.d.ts +0 -38
- package/dist/core/index.js +0 -64
- package/dist/core/types.d.ts +0 -265
- package/dist/core/types.js +0 -48
- package/dist/hooks/context.d.ts +0 -147
- package/dist/hooks/context.js +0 -334
- package/dist/hooks/useContext.d.ts +0 -113
- package/dist/hooks/useContext.js +0 -169
- package/dist/hooks/useEffect.d.ts +0 -105
- package/dist/hooks/useEffect.js +0 -540
- package/dist/hooks/useInstance.d.ts +0 -139
- package/dist/hooks/useInstance.js +0 -455
- package/dist/hooks/useState.d.ts +0 -120
- package/dist/hooks/useState.js +0 -298
- package/dist/jsx.d.ts +0 -143
- package/dist/jsx.js +0 -76
- package/dist/providers/DummyBackendProvider.d.ts +0 -193
- package/dist/providers/DummyBackendProvider.js +0 -189
- package/dist/providers/DummyCloudProvider.d.ts +0 -128
- package/dist/providers/DummyCloudProvider.js +0 -157
- package/dist/providers/IBackendProvider.d.ts +0 -177
- package/dist/providers/IBackendProvider.js +0 -31
- package/dist/providers/ICloudProvider.d.ts +0 -230
- package/dist/providers/ICloudProvider.js +0 -31
- package/dist/providers/index.d.ts +0 -31
- package/dist/providers/index.js +0 -31
- package/dist/test-event-callbacks.d.ts +0 -0
- package/dist/test-event-callbacks.js +0 -1
- package/dist/utils/Logger.d.ts +0 -144
- package/dist/utils/Logger.js +0 -220
- package/dist/utils/Output.d.ts +0 -161
- package/dist/utils/Output.js +0 -401
- package/dist/utils/deepEqual.d.ts +0 -71
- package/dist/utils/deepEqual.js +0 -276
- package/dist/utils/naming.d.ts +0 -241
- 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;
|
package/dist/hooks/useEffect.js
DELETED
|
@@ -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
|
-
}
|