@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,139 @@
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 { CloudDOMNode } from '../core/types';
31
+ import { ProviderOutputTracker } from '../core/ProviderOutputTracker';
32
+ /**
33
+ * Set the ProviderOutputTracker instance (for testing/injection)
34
+ * @internal
35
+ */
36
+ export declare function setProviderOutputTracker(tracker: ProviderOutputTracker): void;
37
+ /**
38
+ * Get the ProviderOutputTracker instance (for external access)
39
+ * @internal
40
+ */
41
+ export declare function getProviderOutputTrackerInstance(): ProviderOutputTracker;
42
+ /**
43
+ * Set the current rendering context
44
+ * Called by Renderer before executing a component
45
+ *
46
+ * @internal
47
+ */
48
+ export declare function setInstanceRenderContext(fiber: any, path: string[]): void;
49
+ /**
50
+ * Clear the current rendering context
51
+ * Called by Renderer after component execution
52
+ *
53
+ * @internal
54
+ */
55
+ export declare function clearInstanceRenderContext(): void;
56
+ /**
57
+ * Set previous outputs map for restoration during render
58
+ * Called by CReact before rendering
59
+ *
60
+ * @internal
61
+ */
62
+ export declare function setPreviousOutputs(outputsMap: Map<string, Record<string, any>> | null): void;
63
+ /**
64
+ * useInstance hook - Register an infrastructure resource (React-like API)
65
+ *
66
+ * This hook creates a CloudDOM node and attaches it to the current Fiber.
67
+ * It must be called during component rendering (inside a component function).
68
+ *
69
+ * Automatically extracts event callbacks (onDeploy, onError, onDestroy) from
70
+ * component props and attaches them to the CloudDOM node for lifecycle events.
71
+ *
72
+ * REQ-01: JSX → CloudDOM rendering
73
+ * REQ-04: Resource creation via hooks (React-like API)
74
+ * REQ-2.3: CloudDOM event callbacks for deployment lifecycle
75
+ *
76
+ * @param construct - Constructor/class for the infrastructure resource
77
+ * @param props - Properties/configuration for the resource (may include `key` and event callbacks)
78
+ * @returns Reference to the created CloudDOM node
79
+ *
80
+ * @example
81
+ * ```tsx
82
+ * // With event callbacks (extracted from component props)
83
+ * function MyDatabase({ onDeploy, onError }) {
84
+ * const db = useInstance(Database, {
85
+ * name: 'my-db',
86
+ * size: '100GB'
87
+ * });
88
+ * // onDeploy and onError are automatically extracted from component props
89
+ * return <></>;
90
+ * }
91
+ *
92
+ * // Usage with event callbacks
93
+ * <MyDatabase
94
+ * onDeploy={(ctx) => logger.info('Database deployed:', ctx.resourceId)}
95
+ * onError={(ctx, err) => logger.error('Database failed:', err)}
96
+ * />
97
+ *
98
+ * // Without event callbacks (normal usage)
99
+ * function MyBucket() {
100
+ * const bucket = useInstance(S3Bucket, {
101
+ * bucketName: 'my-assets'
102
+ * });
103
+ * return <></>;
104
+ * }
105
+ * ```
106
+ */
107
+ export declare function useInstance<T = any>(construct: new (...args: any[]) => T, props: Record<string, any>): CloudDOMNode;
108
+ /**
109
+ * Reset construct call counts for a fiber
110
+ * Called by Renderer before executing a component
111
+ *
112
+ * @internal
113
+ */
114
+ export declare function resetConstructCounts(fiber: any): void;
115
+ /**
116
+ * Get the current rendering path
117
+ * Useful for debugging and testing
118
+ *
119
+ * @internal
120
+ */
121
+ export declare function getCurrentPath(): string[];
122
+ /**
123
+ * Check if currently rendering
124
+ * Useful for validation and testing
125
+ *
126
+ * @internal
127
+ */
128
+ export declare function isRendering(): boolean;
129
+ /**
130
+ * Update outputs for a CloudDOM node and notify bound components
131
+ * This is called after deployment when provider outputs are available
132
+ * @internal
133
+ */
134
+ export declare function updateNodeOutputs(nodeId: string, newOutputs: Record<string, any>): void;
135
+ /**
136
+ * Get current outputs for a CloudDOM node
137
+ * @internal
138
+ */
139
+ export declare function getNodeOutputs(nodeId: string): Record<string, any>;
@@ -0,0 +1,441 @@
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.setProviderOutputTracker = setProviderOutputTracker;
33
+ exports.getProviderOutputTrackerInstance = getProviderOutputTrackerInstance;
34
+ exports.setInstanceRenderContext = setInstanceRenderContext;
35
+ exports.clearInstanceRenderContext = clearInstanceRenderContext;
36
+ exports.setPreviousOutputs = setPreviousOutputs;
37
+ exports.useInstance = useInstance;
38
+ exports.resetConstructCounts = resetConstructCounts;
39
+ exports.getCurrentPath = getCurrentPath;
40
+ exports.isRendering = isRendering;
41
+ exports.updateNodeOutputs = updateNodeOutputs;
42
+ exports.getNodeOutputs = getNodeOutputs;
43
+ const naming_1 = require("../utils/naming");
44
+ const ProviderOutputTracker_1 = require("../core/ProviderOutputTracker");
45
+ const context_1 = require("./context");
46
+ const Logger_1 = require("../utils/Logger");
47
+ const logger = Logger_1.LoggerFactory.getLogger('hooks');
48
+ // Global ProviderOutputTracker instance
49
+ let providerOutputTracker = null;
50
+ /**
51
+ * Get or create the global ProviderOutputTracker instance
52
+ * @internal
53
+ */
54
+ function getProviderOutputTracker() {
55
+ if (!providerOutputTracker) {
56
+ providerOutputTracker = new ProviderOutputTracker_1.ProviderOutputTracker();
57
+ }
58
+ return providerOutputTracker;
59
+ }
60
+ /**
61
+ * Set the ProviderOutputTracker instance (for testing/injection)
62
+ * @internal
63
+ */
64
+ function setProviderOutputTracker(tracker) {
65
+ providerOutputTracker = tracker;
66
+ }
67
+ /**
68
+ * Get the ProviderOutputTracker instance (for external access)
69
+ * @internal
70
+ */
71
+ function getProviderOutputTrackerInstance() {
72
+ return getProviderOutputTracker();
73
+ }
74
+ /**
75
+ * Create a placeholder node when dependencies are undefined
76
+ * This node won't be included in CloudDOM but allows the component to continue rendering
77
+ * All output accesses return undefined
78
+ *
79
+ * @internal
80
+ */
81
+ function createPlaceholderNode() {
82
+ const placeholderNode = {
83
+ id: '__placeholder__',
84
+ path: ['__placeholder__'],
85
+ construct: class Placeholder {
86
+ },
87
+ constructType: 'Placeholder',
88
+ props: {},
89
+ children: [],
90
+ outputs: {},
91
+ };
92
+ // Return a proxy that always returns undefined for outputs
93
+ return new Proxy(placeholderNode, {
94
+ get(target, prop) {
95
+ if (prop === 'outputs') {
96
+ return new Proxy({}, {
97
+ get() {
98
+ return undefined;
99
+ },
100
+ });
101
+ }
102
+ return Reflect.get(target, prop);
103
+ },
104
+ });
105
+ }
106
+ /**
107
+ * Create an enhanced CloudDOM node with output reference capabilities
108
+ * This allows automatic binding when outputs are used in useState
109
+ * The proxy dynamically reads from the node's current outputs for reactivity
110
+ *
111
+ * REQ-2.1, 2.4, 2.5: Enhanced proxy that always reads live values and tracks access
112
+ *
113
+ * @internal
114
+ */
115
+ function createEnhancedNode(node, fiber) {
116
+ // Create a proxy that intercepts property access to provide reactive output access
117
+ return new Proxy(node, {
118
+ get(target, prop, receiver) {
119
+ // Special handling for 'outputs' property to ensure reactivity
120
+ if (prop === 'outputs') {
121
+ // Return a proxy for the outputs object that tracks reads
122
+ return new Proxy(target.outputs || {}, {
123
+ get(outputsTarget, outputKey) {
124
+ if (typeof outputKey === 'string') {
125
+ // REQ-2.1: Always read from current target.outputs (live values)
126
+ const currentValue = target.outputs?.[outputKey];
127
+ // REQ-2.4, 2.5: Track this output read for binding creation
128
+ if (currentValue !== undefined) {
129
+ const tracker = getProviderOutputTracker();
130
+ tracker.trackOutputRead(target.id, outputKey, fiber);
131
+ logger.debug(`Tracked output read: ${target.id}.${outputKey} = ${currentValue}`);
132
+ }
133
+ // REQ-2.2: Return undefined gracefully if not populated
134
+ return currentValue;
135
+ }
136
+ return Reflect.get(outputsTarget, outputKey);
137
+ },
138
+ has(outputsTarget, outputKey) {
139
+ // Check if output exists in current target.outputs
140
+ return (typeof outputKey === 'string' &&
141
+ target.outputs !== undefined &&
142
+ outputKey in target.outputs);
143
+ },
144
+ ownKeys(outputsTarget) {
145
+ // Return keys from current target.outputs
146
+ return target.outputs ? Object.keys(target.outputs) : [];
147
+ },
148
+ });
149
+ }
150
+ // For all other properties, return the original value
151
+ return Reflect.get(target, prop, receiver);
152
+ },
153
+ has(target, prop) {
154
+ // Standard property checks
155
+ return Reflect.has(target, prop);
156
+ },
157
+ ownKeys(target) {
158
+ // Return original keys
159
+ return Reflect.ownKeys(target);
160
+ },
161
+ });
162
+ }
163
+ /**
164
+ * Set the current rendering context
165
+ * Called by Renderer before executing a component
166
+ *
167
+ * @internal
168
+ */
169
+ function setInstanceRenderContext(fiber, path) {
170
+ (0, context_1.setRenderContext)(fiber, path);
171
+ }
172
+ /**
173
+ * Clear the current rendering context
174
+ * Called by Renderer after component execution
175
+ *
176
+ * @internal
177
+ */
178
+ function clearInstanceRenderContext() {
179
+ (0, context_1.clearRenderContext)();
180
+ }
181
+ /**
182
+ * Set previous outputs map for restoration during render
183
+ * Called by CReact before rendering
184
+ *
185
+ * @internal
186
+ */
187
+ function setPreviousOutputs(outputsMap) {
188
+ (0, context_1.setPreviousOutputs)(outputsMap);
189
+ }
190
+ /**
191
+ * Track construct call counts per component for auto-ID generation
192
+ * Maps component fiber to construct type to call count
193
+ */
194
+ const constructCallCounts = new WeakMap();
195
+ /**
196
+ * useInstance hook - Register an infrastructure resource (React-like API)
197
+ *
198
+ * This hook creates a CloudDOM node and attaches it to the current Fiber.
199
+ * It must be called during component rendering (inside a component function).
200
+ *
201
+ * Automatically extracts event callbacks (onDeploy, onError, onDestroy) from
202
+ * component props and attaches them to the CloudDOM node for lifecycle events.
203
+ *
204
+ * REQ-01: JSX → CloudDOM rendering
205
+ * REQ-04: Resource creation via hooks (React-like API)
206
+ * REQ-2.3: CloudDOM event callbacks for deployment lifecycle
207
+ *
208
+ * @param construct - Constructor/class for the infrastructure resource
209
+ * @param props - Properties/configuration for the resource (may include `key` and event callbacks)
210
+ * @returns Reference to the created CloudDOM node
211
+ *
212
+ * @example
213
+ * ```tsx
214
+ * // With event callbacks (extracted from component props)
215
+ * function MyDatabase({ onDeploy, onError }) {
216
+ * const db = useInstance(Database, {
217
+ * name: 'my-db',
218
+ * size: '100GB'
219
+ * });
220
+ * // onDeploy and onError are automatically extracted from component props
221
+ * return <></>;
222
+ * }
223
+ *
224
+ * // Usage with event callbacks
225
+ * <MyDatabase
226
+ * onDeploy={(ctx) => logger.info('Database deployed:', ctx.resourceId)}
227
+ * onError={(ctx, err) => logger.error('Database failed:', err)}
228
+ * />
229
+ *
230
+ * // Without event callbacks (normal usage)
231
+ * function MyBucket() {
232
+ * const bucket = useInstance(S3Bucket, {
233
+ * bucketName: 'my-assets'
234
+ * });
235
+ * return <></>;
236
+ * }
237
+ * ```
238
+ */
239
+ function useInstance(construct, props) {
240
+ // Use consolidated hook context
241
+ const context = (0, context_1.requireHookContext)();
242
+ const currentFiber = context.currentFiber; // Non-null assertion safe due to requireHookContext validation
243
+ const { currentPath, previousOutputsMap } = context;
244
+ // Get hook index for this useInstance call (instance-specific)
245
+ const hookIndex = (0, context_1.incrementHookIndex)('instance');
246
+ // Extract key from props (React-like)
247
+ const { key, ...restProps } = props;
248
+ // Check for undefined dependencies - enforce deployment order
249
+ // If any prop value is undefined, don't create the node yet
250
+ // This ensures resources are only created when their dependencies are available
251
+ const hasUndefinedDeps = Object.values(restProps).some((v) => v === undefined);
252
+ if (hasUndefinedDeps) {
253
+ logger.info(`[Deployment Order] Skipping resource creation - undefined dependencies detected`);
254
+ logger.info(` Construct: ${construct.name}`);
255
+ logger.info(` Props with undefined values:`, Object.entries(restProps)
256
+ .filter(([_, v]) => v === undefined)
257
+ .map(([k]) => k));
258
+ // Don't attach anything to fiber - this resource will be skipped entirely
259
+ // Return a placeholder node that won't be included in CloudDOM
260
+ // The proxy will return undefined for all output accesses
261
+ return createPlaceholderNode();
262
+ }
263
+ // Remove undefined values from props to match JSON serialization behavior
264
+ // When CloudDOM is saved to backend, JSON.stringify strips undefined values
265
+ // This ensures consistent comparison after deserialization
266
+ const cleanProps = {};
267
+ for (const [k, v] of Object.entries(restProps)) {
268
+ if (v !== undefined) {
269
+ cleanProps[k] = v;
270
+ }
271
+ }
272
+ // Extract event callbacks from current component's props (not useInstance props)
273
+ const componentProps = currentFiber.props || {};
274
+ const eventCallbacks = {};
275
+ // Extract onDeploy callback
276
+ if (typeof componentProps.onDeploy === 'function') {
277
+ eventCallbacks.onDeploy = componentProps.onDeploy;
278
+ }
279
+ // Extract onError callback
280
+ if (typeof componentProps.onError === 'function') {
281
+ eventCallbacks.onError = componentProps.onError;
282
+ }
283
+ // Extract onDestroy callback
284
+ if (typeof componentProps.onDestroy === 'function') {
285
+ eventCallbacks.onDestroy = componentProps.onDestroy;
286
+ }
287
+ // Generate ID from key or construct type
288
+ // Priority: explicit key from useInstance props > component fiber key > auto-generated
289
+ let id;
290
+ if (key !== undefined) {
291
+ // Use explicit key if provided in useInstance props
292
+ id = String(key);
293
+ }
294
+ else {
295
+ // Auto-generate ID from construct type name (kebab-case)
296
+ const constructName = (0, naming_1.toKebabCase)(construct.name);
297
+ // Track call count for this construct type in this component
298
+ if (!constructCallCounts.has(currentFiber)) {
299
+ constructCallCounts.set(currentFiber, new Map());
300
+ }
301
+ const counts = constructCallCounts.get(currentFiber);
302
+ const count = counts.get(construct) || 0;
303
+ counts.set(construct, count + 1);
304
+ // Append index if multiple calls with same type
305
+ if (count > 0) {
306
+ id = `${constructName}-${count}`;
307
+ }
308
+ else {
309
+ id = constructName;
310
+ }
311
+ // If component has a key (from JSX), prepend it to make resources unique per component instance
312
+ if (currentFiber.key) {
313
+ id = `${currentFiber.key}-${id}`;
314
+ }
315
+ }
316
+ // Generate full resource ID from current path
317
+ // Example: ['registry', 'service'] + 'bucket' → 'registry.service.bucket'
318
+ const fullPath = [...currentPath, id];
319
+ const resourceId = (0, naming_1.generateResourceId)(fullPath);
320
+ // Generate stable constructType identifier
321
+ // This is a serializable string that uniquely identifies the construct type
322
+ const constructType = construct.name || 'UnknownConstruct';
323
+ // Create CloudDOM node
324
+ const node = {
325
+ id: resourceId,
326
+ path: fullPath,
327
+ construct,
328
+ constructType, // Serializable construct type identifier
329
+ props: cleanProps, // Use cleaned props (no undefined values, no key)
330
+ children: [],
331
+ outputs: {}, // Will be populated during materialization
332
+ eventCallbacks: Object.keys(eventCallbacks).length > 0 ? eventCallbacks : undefined,
333
+ };
334
+ // Restore outputs from previous state if available
335
+ logger.debug(`Checking for outputs: ${resourceId}, map has ${previousOutputsMap?.size || 0} entries`);
336
+ if (previousOutputsMap && previousOutputsMap.has(resourceId)) {
337
+ node.outputs = { ...previousOutputsMap.get(resourceId) };
338
+ logger.debug(`✓ Restored outputs for: ${resourceId}`, node.outputs);
339
+ }
340
+ else {
341
+ logger.debug(`✗ No outputs found for: ${resourceId}`);
342
+ if (previousOutputsMap) {
343
+ logger.debug(` Available keys:`, Array.from(previousOutputsMap.keys()));
344
+ }
345
+ }
346
+ // Attach node to current Fiber
347
+ // The Fiber stores all CloudDOM nodes created during its execution
348
+ if (!currentFiber.cloudDOMNodes) {
349
+ currentFiber.cloudDOMNodes = [];
350
+ }
351
+ // Check if a node with this ID already exists (from previous render)
352
+ // If so, update it instead of creating a duplicate
353
+ const existingNodeIndex = currentFiber.cloudDOMNodes.findIndex((n) => n.id === resourceId);
354
+ if (existingNodeIndex >= 0) {
355
+ logger.debug(`Updating existing node: ${resourceId}`);
356
+ currentFiber.cloudDOMNodes[existingNodeIndex] = node;
357
+ }
358
+ else {
359
+ logger.debug(`Creating new node: ${resourceId}`);
360
+ currentFiber.cloudDOMNodes.push(node);
361
+ }
362
+ // Track this instance with ProviderOutputTracker for output change detection
363
+ const outputTracker = getProviderOutputTracker();
364
+ outputTracker.trackInstance(node, currentFiber);
365
+ // REQ-2.3, 6.1: Enhance the node with proxy that reads live values and tracks access
366
+ // No longer creating stale outputReferences - proxy reads directly from target.outputs
367
+ const enhancedNode = createEnhancedNode(node, currentFiber);
368
+ // Debug logging
369
+ logger.debug(`Created and tracked instance: ${resourceId}`);
370
+ logger.debug(`Node outputs at creation: ${JSON.stringify(node.outputs || {})}`);
371
+ // Return reference to the enhanced node
372
+ // This allows components to reference the resource and its outputs
373
+ // REQ-2.5: Multiple accesses within same render cycle are consistent
374
+ return enhancedNode;
375
+ }
376
+ /**
377
+ * Reset construct call counts for a fiber
378
+ * Called by Renderer before executing a component
379
+ *
380
+ * @internal
381
+ */
382
+ function resetConstructCounts(fiber) {
383
+ constructCallCounts.delete(fiber);
384
+ }
385
+ /**
386
+ * Get the current rendering path
387
+ * Useful for debugging and testing
388
+ *
389
+ * @internal
390
+ */
391
+ function getCurrentPath() {
392
+ try {
393
+ const context = (0, context_1.requireHookContext)();
394
+ return [...context.currentPath];
395
+ }
396
+ catch {
397
+ return [];
398
+ }
399
+ }
400
+ /**
401
+ * Check if currently rendering
402
+ * Useful for validation and testing
403
+ *
404
+ * @internal
405
+ */
406
+ function isRendering() {
407
+ try {
408
+ const context = (0, context_1.requireHookContext)();
409
+ return context.currentFiber !== null;
410
+ }
411
+ catch {
412
+ return false;
413
+ }
414
+ }
415
+ /**
416
+ * Update outputs for a CloudDOM node and notify bound components
417
+ * This is called after deployment when provider outputs are available
418
+ * @internal
419
+ */
420
+ function updateNodeOutputs(nodeId, newOutputs) {
421
+ const outputTracker = getProviderOutputTracker();
422
+ const changes = outputTracker.updateNodeOutputs(nodeId, newOutputs);
423
+ if (changes.length > 0) {
424
+ // Process the changes and get affected fibers
425
+ const affectedFibers = outputTracker.processOutputChanges(changes);
426
+ logger.debug(`Output changes detected for ${nodeId}:`, {
427
+ changes: changes.length,
428
+ affectedFibers: affectedFibers.size,
429
+ });
430
+ // Note: The actual re-rendering will be handled by the RenderScheduler
431
+ // when it's integrated with the CReact main orchestrator
432
+ }
433
+ }
434
+ /**
435
+ * Get current outputs for a CloudDOM node
436
+ * @internal
437
+ */
438
+ function getNodeOutputs(nodeId) {
439
+ const outputTracker = getProviderOutputTracker();
440
+ return outputTracker.getNodeOutputs(nodeId);
441
+ }
@@ -0,0 +1,120 @@
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 { StateBindingManager } from '../core/StateBindingManager';
31
+ import { FiberNode } from '../core/types';
32
+ /**
33
+ * Set the StateBindingManager instance (for testing/injection)
34
+ * @internal
35
+ */
36
+ export declare function setStateBindingManager(manager: StateBindingManager): void;
37
+ /**
38
+ * Get the StateBindingManager instance (for external access)
39
+ * @internal
40
+ */
41
+ export declare function getStateBindingManagerInstance(): StateBindingManager;
42
+ /**
43
+ * Set the current rendering context for useState
44
+ * Called by Renderer before executing a component
45
+ *
46
+ * @internal
47
+ */
48
+ export declare function setStateRenderContext(fiber: FiberNode, path?: string[]): void;
49
+ /**
50
+ * Clear the current rendering context for useState
51
+ * Called by Renderer after component execution
52
+ *
53
+ * @internal
54
+ */
55
+ export declare function clearStateRenderContext(): void;
56
+ /**
57
+ * useState hook - Declare component outputs (NOT reactive state)
58
+ *
59
+ * This hook declares outputs that will be persisted across build/deploy cycles.
60
+ * Unlike React's useState, this does NOT trigger re-renders.
61
+ *
62
+ * Mimics React's useState API with hooks array pattern for multiple calls.
63
+ *
64
+ * REQ-02: Stack Context (declarative outputs)
65
+ *
66
+ * Semantics:
67
+ * - `useState(initialValue)` - Declares a single output value
68
+ * - `setState(value)` during build - Updates the output value for build-time enrichment
69
+ * - `setState(value)` during deploy - Updates persisted output after provider materialization
70
+ * - NOT a render trigger - it's a persistent output update mechanism
71
+ * - Supports multiple useState calls per component (like React)
72
+ *
73
+ * Key difference from React:
74
+ * - React: `setState()` causes re-render in memory
75
+ * - CReact: `setState()` updates persisted outputs for next cycle
76
+ *
77
+ * @param initialValue - Initial output value to declare
78
+ * @returns Tuple of [state, setState] where setState updates the output value
79
+ *
80
+ * @example
81
+ * ```tsx
82
+ * function CDNStack({ children }) {
83
+ * const distribution = useInstance('cdn', CloudFrontDistribution, { ... });
84
+ *
85
+ * // Multiple useState calls (like React)
86
+ * const [distributionId, setDistributionId] = useState<string>();
87
+ * const [distributionDomain, setDistributionDomain] = useState<string>();
88
+ * const [distributionArn, setDistributionArn] = useState<string>();
89
+ *
90
+ * // Optional: Enrich outputs after async materialization
91
+ * useEffect(async () => {
92
+ * const actualDomain = await distribution.getDomain();
93
+ * setDistributionDomain(actualDomain);
94
+ * }, [distribution]);
95
+ *
96
+ * // Aggregate outputs for StackContext
97
+ * const outputs = {
98
+ * distributionId,
99
+ * distributionDomain,
100
+ * distributionArn,
101
+ * };
102
+ *
103
+ * return <StackContext.Provider value={outputs}>{children}</StackContext.Provider>;
104
+ * }
105
+ * ```
106
+ */
107
+ export declare function useState<T = undefined>(initialValue?: T): [T, (value: T | ((prev: T) => T)) => void];
108
+ /**
109
+ * Check if useState context is available
110
+ * Useful for validation and testing
111
+ *
112
+ * @internal
113
+ */
114
+ export declare function hasStateContext(): boolean;
115
+ /**
116
+ * Get the current state from Fiber (for testing)
117
+ *
118
+ * @internal
119
+ */
120
+ export declare function getCurrentState(): Record<string, any> | undefined;