@liteforge/runtime 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.
@@ -0,0 +1,295 @@
1
+ /**
2
+ * LiteForge createComponent
3
+ *
4
+ * The central factory for creating reactive components with full lifecycle support.
5
+ *
6
+ * Lifecycle Flow:
7
+ * 1. setup() - Synchronous, create local signals
8
+ * 2. placeholder - Shown immediately (if load exists)
9
+ * 3. load() - Async data fetching
10
+ * 4. component() - Render (only when load resolved)
11
+ * 5. mounted() - After DOM insertion
12
+ * 6. destroyed() - On unmount
13
+ */
14
+ import { generateDebugId, emitComponentMount, emitComponentUnmount, } from '@liteforge/core';
15
+ import { use, withContext } from './context.js';
16
+ // Track parent component for debug hierarchy
17
+ let currentParentComponentId;
18
+ // ============================================================================
19
+ // Component State Enum
20
+ // ============================================================================
21
+ var ComponentState;
22
+ (function (ComponentState) {
23
+ ComponentState[ComponentState["Created"] = 0] = "Created";
24
+ ComponentState[ComponentState["Loading"] = 1] = "Loading";
25
+ ComponentState[ComponentState["Loaded"] = 2] = "Loaded";
26
+ ComponentState[ComponentState["Error"] = 3] = "Error";
27
+ ComponentState[ComponentState["Mounted"] = 4] = "Mounted";
28
+ ComponentState[ComponentState["Unmounted"] = 5] = "Unmounted";
29
+ })(ComponentState || (ComponentState = {}));
30
+ // Implementation
31
+ export function createComponent(definition) {
32
+ const factory = (inputProps) => {
33
+ return createComponentInstance(definition, inputProps);
34
+ };
35
+ // Mark as LiteForge component for detection
36
+ factory.__liteforge_component = true;
37
+ return factory;
38
+ }
39
+ /**
40
+ * Create an instance of a component with full lifecycle management.
41
+ */
42
+ function createComponentInstance(definition, inputProps) {
43
+ // Debug info - extract component name from definition
44
+ // Priority: 1. explicit name property, 2. component function name (if meaningful), 3. fallback
45
+ // Skip generic names like 'component' or empty strings
46
+ const funcName = definition.component.name;
47
+ const componentName = definition.name
48
+ ?? (funcName && funcName !== 'component' && funcName !== '' ? funcName : 'Component');
49
+ const componentId = generateDebugId(componentName);
50
+ const parentId = currentParentComponentId;
51
+ // State
52
+ let state = ComponentState.Created;
53
+ let currentNode = null;
54
+ let parentElement = null;
55
+ let beforeNode = null;
56
+ let props = resolveProps(definition.props, inputProps);
57
+ let setupResult;
58
+ let loadedData;
59
+ let mountedCleanup;
60
+ const hasProvide = Boolean(definition.provide);
61
+ let provideContext;
62
+ // ========================================
63
+ // Phase 1: Setup (synchronous)
64
+ // ========================================
65
+ if (definition.setup) {
66
+ setupResult = definition.setup({ props, use });
67
+ }
68
+ // ========================================
69
+ // Lifecycle Methods
70
+ // ========================================
71
+ function mount(parent, before = null) {
72
+ if (state === ComponentState.Unmounted) {
73
+ throw new Error('Cannot mount an unmounted component');
74
+ }
75
+ parentElement = parent;
76
+ beforeNode = before;
77
+ // Set up provide context if defined
78
+ if (hasProvide && definition.provide) {
79
+ provideContext = definition.provide({ use });
80
+ }
81
+ // Track parent component for nested components
82
+ const previousParentId = currentParentComponentId;
83
+ currentParentComponentId = componentId;
84
+ // Run within context scope
85
+ const mountWithContext = () => {
86
+ if (definition.load) {
87
+ // Has async loading - show placeholder first
88
+ state = ComponentState.Loading;
89
+ renderPlaceholder();
90
+ startLoading();
91
+ }
92
+ else {
93
+ // No loading - render directly
94
+ state = ComponentState.Loaded;
95
+ loadedData = undefined;
96
+ renderComponent();
97
+ }
98
+ };
99
+ if (hasProvide && provideContext) {
100
+ withContext(provideContext, mountWithContext);
101
+ }
102
+ else {
103
+ mountWithContext();
104
+ }
105
+ // Restore parent component tracking
106
+ currentParentComponentId = previousParentId;
107
+ // Emit mount event (zero cost if debug not enabled)
108
+ emitComponentMount(componentId, componentName, parentId);
109
+ }
110
+ function unmount() {
111
+ if (state === ComponentState.Unmounted)
112
+ return;
113
+ // Run destroyed callback
114
+ if (definition.destroyed) {
115
+ definition.destroyed({ props, setup: setupResult });
116
+ }
117
+ // Run mounted cleanup
118
+ if (mountedCleanup) {
119
+ mountedCleanup();
120
+ mountedCleanup = undefined;
121
+ }
122
+ // Remove from DOM
123
+ if (currentNode && currentNode.parentNode) {
124
+ currentNode.parentNode.removeChild(currentNode);
125
+ }
126
+ currentNode = null;
127
+ parentElement = null;
128
+ state = ComponentState.Unmounted;
129
+ // Emit unmount event (zero cost if debug not enabled)
130
+ emitComponentUnmount(componentId, componentName);
131
+ }
132
+ function getNode() {
133
+ return currentNode;
134
+ }
135
+ function updateProps(newProps) {
136
+ props = resolveProps(definition.props, newProps);
137
+ // In a full implementation, this would trigger a re-render
138
+ // For now, props are static after initial render
139
+ }
140
+ // ========================================
141
+ // Internal Methods
142
+ // ========================================
143
+ function renderPlaceholder() {
144
+ if (!parentElement)
145
+ return;
146
+ let placeholderNode;
147
+ if (definition.placeholder) {
148
+ placeholderNode = definition.placeholder({ props });
149
+ }
150
+ else {
151
+ // Default placeholder is an empty comment node
152
+ placeholderNode = document.createComment('loading');
153
+ }
154
+ insertNode(placeholderNode);
155
+ }
156
+ function renderComponent() {
157
+ if (!parentElement)
158
+ return;
159
+ if (state === ComponentState.Unmounted)
160
+ return;
161
+ const render = () => {
162
+ const node = definition.component({
163
+ props,
164
+ data: loadedData,
165
+ setup: setupResult,
166
+ use,
167
+ });
168
+ insertNode(node);
169
+ state = ComponentState.Mounted;
170
+ // Phase 4: mounted callback
171
+ if (definition.mounted && currentNode instanceof Element) {
172
+ mountedCleanup = definition.mounted({
173
+ el: currentNode,
174
+ props,
175
+ data: loadedData,
176
+ setup: setupResult,
177
+ use,
178
+ });
179
+ }
180
+ };
181
+ if (hasProvide && provideContext) {
182
+ withContext(provideContext, render);
183
+ }
184
+ else {
185
+ render();
186
+ }
187
+ }
188
+ function renderError(error) {
189
+ if (!parentElement)
190
+ return;
191
+ if (state === ComponentState.Unmounted)
192
+ return;
193
+ state = ComponentState.Error;
194
+ if (definition.error) {
195
+ const errorNode = definition.error({
196
+ props,
197
+ error,
198
+ retry: () => {
199
+ // Re-attempt loading
200
+ state = ComponentState.Loading;
201
+ renderPlaceholder();
202
+ startLoading();
203
+ },
204
+ });
205
+ insertNode(errorNode);
206
+ }
207
+ else {
208
+ // Default error: show a text node with the error message
209
+ const errorNode = document.createTextNode(`Error: ${error.message}`);
210
+ insertNode(errorNode);
211
+ }
212
+ }
213
+ function startLoading() {
214
+ if (!definition.load)
215
+ return;
216
+ definition
217
+ .load({
218
+ props,
219
+ setup: setupResult,
220
+ use,
221
+ })
222
+ .then((data) => {
223
+ if (state === ComponentState.Unmounted)
224
+ return;
225
+ loadedData = data;
226
+ state = ComponentState.Loaded;
227
+ renderComponent();
228
+ })
229
+ .catch((error) => {
230
+ if (state === ComponentState.Unmounted)
231
+ return;
232
+ renderError(error instanceof Error ? error : new Error(String(error)));
233
+ });
234
+ }
235
+ function insertNode(newNode) {
236
+ if (!parentElement)
237
+ return;
238
+ // Remove old node if exists
239
+ if (currentNode && currentNode.parentNode) {
240
+ currentNode.parentNode.removeChild(currentNode);
241
+ }
242
+ // Insert new node
243
+ if (beforeNode) {
244
+ parentElement.insertBefore(newNode, beforeNode);
245
+ }
246
+ else {
247
+ parentElement.appendChild(newNode);
248
+ }
249
+ currentNode = newNode;
250
+ }
251
+ return {
252
+ mount,
253
+ unmount,
254
+ getNode,
255
+ updateProps,
256
+ };
257
+ }
258
+ // ============================================================================
259
+ // Prop Resolution
260
+ // ============================================================================
261
+ /**
262
+ * Resolve props with defaults and type checking.
263
+ */
264
+ function resolveProps(propsSchema, inputProps) {
265
+ if (!propsSchema) {
266
+ return inputProps;
267
+ }
268
+ const resolved = { ...inputProps };
269
+ for (const [key, propDef] of Object.entries(propsSchema)) {
270
+ if (!(key in resolved) || resolved[key] === undefined) {
271
+ // Apply default if available
272
+ if ('default' in propDef) {
273
+ resolved[key] =
274
+ typeof propDef.default === 'function'
275
+ ? propDef.default()
276
+ : propDef.default;
277
+ }
278
+ else if (propDef.required) {
279
+ console.warn(`Required prop "${key}" is missing`);
280
+ }
281
+ }
282
+ }
283
+ return resolved;
284
+ }
285
+ // ============================================================================
286
+ // Utility: Check if value is a component factory
287
+ // ============================================================================
288
+ /**
289
+ * Check if a value is a LiteForge component factory.
290
+ */
291
+ export function isComponentFactory(value) {
292
+ return (typeof value === 'function' &&
293
+ value.__liteforge_component === true);
294
+ }
295
+ //# sourceMappingURL=component.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"component.js","sourceRoot":"","sources":["../src/component.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,66 @@
1
+ /**
2
+ * LiteForge Context System
3
+ *
4
+ * Provides a scoped context chain with no provider components.
5
+ * Context flows from app → parent components → child components.
6
+ */
7
+ import type { ContextValues, UseFn } from './types.js';
8
+ /**
9
+ * Get a value from the context chain.
10
+ * Searches from the current scope up to the app root.
11
+ *
12
+ * @param key - The context key to look up
13
+ * @returns The value associated with the key
14
+ * @throws Error if key is not found in any scope
15
+ */
16
+ export declare function use<T = unknown>(key: string): T;
17
+ /**
18
+ * Check if a context key exists without throwing.
19
+ *
20
+ * @param key - The context key to check
21
+ * @returns True if the key exists in any scope
22
+ */
23
+ export declare function hasContext(key: string): boolean;
24
+ /**
25
+ * Push a new context scope onto the stack.
26
+ * Used when entering a component with `provide`.
27
+ *
28
+ * @param values - The context values to add to the scope
29
+ */
30
+ export declare function pushContext(values: ContextValues): void;
31
+ /**
32
+ * Pop the current context scope from the stack.
33
+ * Used when exiting a component with `provide`.
34
+ */
35
+ export declare function popContext(): void;
36
+ /**
37
+ * Initialize the app-level context (bottom of stack).
38
+ * Should only be called once by createApp().
39
+ *
40
+ * @param values - The app-level context values
41
+ */
42
+ export declare function initAppContext(values: ContextValues): void;
43
+ /**
44
+ * Clear all context (used for cleanup/testing).
45
+ */
46
+ export declare function clearContext(): void;
47
+ /**
48
+ * Get the current context depth (for debugging/testing).
49
+ */
50
+ export declare function getContextDepth(): number;
51
+ /**
52
+ * Run a function with a temporary context scope.
53
+ * The scope is automatically popped after the function completes.
54
+ *
55
+ * @param values - The context values for this scope
56
+ * @param fn - The function to run within the scope
57
+ * @returns The return value of the function
58
+ */
59
+ export declare function withContext<T>(values: ContextValues, fn: () => T): T;
60
+ /**
61
+ * Create a use function bound to the current context state.
62
+ * This captures the current stack depth so the function works correctly
63
+ * even if called later when the context has changed.
64
+ */
65
+ export declare function createBoundUse(): UseFn;
66
+ //# sourceMappingURL=context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAiBvD;;;;;;;GAOG;AACH,wBAAgB,GAAG,CAAC,CAAC,GAAG,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,CAAC,CAU/C;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAQ/C;AAMD;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,aAAa,GAAG,IAAI,CAEvD;AAED;;;GAGG;AACH,wBAAgB,UAAU,IAAI,IAAI,CAEjC;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,aAAa,GAAG,IAAI,CAI1D;AAED;;GAEG;AACH,wBAAgB,YAAY,IAAI,IAAI,CAEnC;AAED;;GAEG;AACH,wBAAgB,eAAe,IAAI,MAAM,CAExC;AAED;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE,MAAM,EAAE,aAAa,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC,CAOpE;AAED;;;;GAIG;AACH,wBAAgB,cAAc,IAAI,KAAK,CAItC"}
@@ -0,0 +1,121 @@
1
+ /**
2
+ * LiteForge Context System
3
+ *
4
+ * Provides a scoped context chain with no provider components.
5
+ * Context flows from app → parent components → child components.
6
+ */
7
+ // ============================================================================
8
+ // Context Stack
9
+ // ============================================================================
10
+ /**
11
+ * Stack of context scopes. Each scope is a map of key → value.
12
+ * The bottom of the stack is the app-level context.
13
+ * Components can push their own scope when they have `provide`.
14
+ */
15
+ const contextStack = [];
16
+ // ============================================================================
17
+ // Public API
18
+ // ============================================================================
19
+ /**
20
+ * Get a value from the context chain.
21
+ * Searches from the current scope up to the app root.
22
+ *
23
+ * @param key - The context key to look up
24
+ * @returns The value associated with the key
25
+ * @throws Error if key is not found in any scope
26
+ */
27
+ export function use(key) {
28
+ // Search from top of stack (innermost scope) to bottom (app scope)
29
+ for (let i = contextStack.length - 1; i >= 0; i--) {
30
+ const scope = contextStack[i];
31
+ if (scope && key in scope) {
32
+ return scope[key];
33
+ }
34
+ }
35
+ throw new Error(`Context key "${key}" not found. Make sure it's provided in createApp() or a parent component's provide().`);
36
+ }
37
+ /**
38
+ * Check if a context key exists without throwing.
39
+ *
40
+ * @param key - The context key to check
41
+ * @returns True if the key exists in any scope
42
+ */
43
+ export function hasContext(key) {
44
+ for (let i = contextStack.length - 1; i >= 0; i--) {
45
+ const scope = contextStack[i];
46
+ if (scope && key in scope) {
47
+ return true;
48
+ }
49
+ }
50
+ return false;
51
+ }
52
+ // ============================================================================
53
+ // Internal API (used by createComponent and createApp)
54
+ // ============================================================================
55
+ /**
56
+ * Push a new context scope onto the stack.
57
+ * Used when entering a component with `provide`.
58
+ *
59
+ * @param values - The context values to add to the scope
60
+ */
61
+ export function pushContext(values) {
62
+ contextStack.push(values);
63
+ }
64
+ /**
65
+ * Pop the current context scope from the stack.
66
+ * Used when exiting a component with `provide`.
67
+ */
68
+ export function popContext() {
69
+ contextStack.pop();
70
+ }
71
+ /**
72
+ * Initialize the app-level context (bottom of stack).
73
+ * Should only be called once by createApp().
74
+ *
75
+ * @param values - The app-level context values
76
+ */
77
+ export function initAppContext(values) {
78
+ // Clear any existing context and set the app context as the base
79
+ contextStack.length = 0;
80
+ contextStack.push(values);
81
+ }
82
+ /**
83
+ * Clear all context (used for cleanup/testing).
84
+ */
85
+ export function clearContext() {
86
+ contextStack.length = 0;
87
+ }
88
+ /**
89
+ * Get the current context depth (for debugging/testing).
90
+ */
91
+ export function getContextDepth() {
92
+ return contextStack.length;
93
+ }
94
+ /**
95
+ * Run a function with a temporary context scope.
96
+ * The scope is automatically popped after the function completes.
97
+ *
98
+ * @param values - The context values for this scope
99
+ * @param fn - The function to run within the scope
100
+ * @returns The return value of the function
101
+ */
102
+ export function withContext(values, fn) {
103
+ pushContext(values);
104
+ try {
105
+ return fn();
106
+ }
107
+ finally {
108
+ popContext();
109
+ }
110
+ }
111
+ /**
112
+ * Create a use function bound to the current context state.
113
+ * This captures the current stack depth so the function works correctly
114
+ * even if called later when the context has changed.
115
+ */
116
+ export function createBoundUse() {
117
+ // The use function always reads from the current stack,
118
+ // which includes any scopes pushed by parent components
119
+ return use;
120
+ }
121
+ //# sourceMappingURL=context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.js","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,+EAA+E;AAC/E,gBAAgB;AAChB,+EAA+E;AAE/E;;;;GAIG;AACH,MAAM,YAAY,GAAoB,EAAE,CAAC;AAEzC,+EAA+E;AAC/E,aAAa;AACb,+EAA+E;AAE/E;;;;;;;GAOG;AACH,MAAM,UAAU,GAAG,CAAc,GAAW;IAC1C,mEAAmE;IACnE,KAAK,IAAI,CAAC,GAAG,YAAY,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QAClD,MAAM,KAAK,GAAG,YAAY,CAAC,CAAC,CAAC,CAAC;QAC9B,IAAI,KAAK,IAAI,GAAG,IAAI,KAAK,EAAE,CAAC;YAC1B,OAAO,KAAK,CAAC,GAAG,CAAM,CAAC;QACzB,CAAC;IACH,CAAC;IAED,MAAM,IAAI,KAAK,CAAC,gBAAgB,GAAG,wFAAwF,CAAC,CAAC;AAC/H,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,UAAU,CAAC,GAAW;IACpC,KAAK,IAAI,CAAC,GAAG,YAAY,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QAClD,MAAM,KAAK,GAAG,YAAY,CAAC,CAAC,CAAC,CAAC;QAC9B,IAAI,KAAK,IAAI,GAAG,IAAI,KAAK,EAAE,CAAC;YAC1B,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,+EAA+E;AAC/E,uDAAuD;AACvD,+EAA+E;AAE/E;;;;;GAKG;AACH,MAAM,UAAU,WAAW,CAAC,MAAqB;IAC/C,YAAY,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;AAC5B,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,UAAU;IACxB,YAAY,CAAC,GAAG,EAAE,CAAC;AACrB,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAAC,MAAqB;IAClD,iEAAiE;IACjE,YAAY,CAAC,MAAM,GAAG,CAAC,CAAC;IACxB,YAAY,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;AAC5B,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,YAAY;IAC1B,YAAY,CAAC,MAAM,GAAG,CAAC,CAAC;AAC1B,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,eAAe;IAC7B,OAAO,YAAY,CAAC,MAAM,CAAC;AAC7B,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,WAAW,CAAI,MAAqB,EAAE,EAAW;IAC/D,WAAW,CAAC,MAAM,CAAC,CAAC;IACpB,IAAI,CAAC;QACH,OAAO,EAAE,EAAE,CAAC;IACd,CAAC;YAAS,CAAC;QACT,UAAU,EAAE,CAAC;IACf,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,cAAc;IAC5B,wDAAwD;IACxD,wDAAwD;IACxD,OAAO,GAAY,CAAC;AACtB,CAAC"}
@@ -0,0 +1,119 @@
1
+ /**
2
+ * LiteForge Control Flow Components
3
+ *
4
+ * Components for conditional rendering and list iteration.
5
+ * These handle DOM insertion/removal reactively based on signals.
6
+ */
7
+ import type { ComponentFactory, RenderFunction } from './types.js';
8
+ /**
9
+ * Config for Show component (internal use).
10
+ * Use ShowProps<T> from types.ts for external typing.
11
+ */
12
+ export interface ShowConfig<T = unknown> {
13
+ when: (() => T) | T;
14
+ fallback?: () => Node;
15
+ children: (value: NonNullable<T>) => Node;
16
+ }
17
+ export declare function Show<T>(config: ShowConfig<T>): Node;
18
+ /**
19
+ * Config for For component (internal use).
20
+ * Use ForProps<T> from types.ts for external typing.
21
+ */
22
+ export interface ForConfig<T> {
23
+ each: (() => ReadonlyArray<T>) | (() => T[]) | ReadonlyArray<T> | T[];
24
+ key?: keyof T | ((item: T, index: number) => string | number);
25
+ children: (item: T, index: number) => Node;
26
+ fallback?: () => Node;
27
+ }
28
+ /**
29
+ * Render a list of items with keyed reconciliation.
30
+ *
31
+ * @typeParam T - The item type, inferred from the `each` array
32
+ *
33
+ * @example
34
+ * ```ts
35
+ * For({
36
+ * each: () => users(),
37
+ * key: 'id',
38
+ * children: (user, index) => {
39
+ * return document.createTextNode(`${index}: ${user.name}`);
40
+ * },
41
+ * fallback: () => document.createTextNode('No users'),
42
+ * })
43
+ * ```
44
+ */
45
+ export declare function For<T>(config: ForConfig<T>): Node;
46
+ /**
47
+ * A single case in a Switch statement.
48
+ */
49
+ export interface MatchCase {
50
+ when: (() => boolean) | boolean;
51
+ render: () => Node;
52
+ }
53
+ /**
54
+ * Config for Switch component (internal use).
55
+ * Use SwitchProps from types.ts for external typing.
56
+ */
57
+ export interface SwitchConfig {
58
+ fallback?: () => Node;
59
+ children: Array<MatchCase>;
60
+ }
61
+ /**
62
+ * Config for Match helper.
63
+ */
64
+ export interface MatchConfig {
65
+ when: (() => boolean) | boolean;
66
+ children: () => Node;
67
+ }
68
+ /**
69
+ * Render the first matching case, or fallback.
70
+ *
71
+ * @example
72
+ * ```ts
73
+ * Switch({
74
+ * fallback: () => document.createTextNode('Unknown'),
75
+ * children: [
76
+ * Match({ when: () => status() === 'loading', children: () => Spinner() }),
77
+ * Match({ when: () => status() === 'error', children: () => ErrorView() }),
78
+ * Match({ when: () => status() === 'success', children: () => SuccessView() }),
79
+ * ],
80
+ * })
81
+ * ```
82
+ */
83
+ export declare function Switch(config: SwitchConfig): Node;
84
+ /**
85
+ * Helper to create a Match case for Switch.
86
+ *
87
+ * @example
88
+ * ```ts
89
+ * Match({
90
+ * when: () => status() === 'active',
91
+ * children: () => <span>Active</span>,
92
+ * })
93
+ * ```
94
+ */
95
+ export declare function Match(config: MatchConfig): MatchCase;
96
+ /**
97
+ * Configuration for the Dynamic component.
98
+ * The component prop is a getter that returns either:
99
+ * - RenderFunction: A simple () => Node function
100
+ * - ComponentFactory: A LiteForge component from createComponent()
101
+ * - null: No component to render
102
+ */
103
+ export interface DynamicConfig<P extends Record<string, unknown>> {
104
+ component: () => RenderFunction | ComponentFactory<P> | null;
105
+ props?: P | (() => P);
106
+ }
107
+ /**
108
+ * Dynamically render a component based on a signal.
109
+ *
110
+ * @example
111
+ * ```ts
112
+ * Dynamic({
113
+ * component: () => currentView(),
114
+ * props: () => ({ id: currentId() }),
115
+ * })
116
+ * ```
117
+ */
118
+ export declare function Dynamic<P extends Record<string, unknown>>(config: DynamicConfig<P>): Node;
119
+ //# sourceMappingURL=control-flow.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"control-flow.d.ts","sourceRoot":"","sources":["../src/control-flow.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAqB,gBAAgB,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAsCtF;;;GAGG;AACH,MAAM,WAAW,UAAU,CAAC,CAAC,GAAG,OAAO;IACrC,IAAI,EAAE,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB,QAAQ,EAAE,CAAC,KAAK,EAAE,WAAW,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC;CAC3C;AA6BD,wBAAgB,IAAI,CAAC,CAAC,EAAE,MAAM,EAAE,UAAU,CAAC,CAAC,CAAC,GAAG,IAAI,CA0EnD;AAMD;;;GAGG;AACH,MAAM,WAAW,SAAS,CAAC,CAAC;IAC1B,IAAI,EAAE,CAAC,MAAM,aAAa,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,GAAG,aAAa,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC;IACtE,GAAG,CAAC,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,KAAK,MAAM,GAAG,MAAM,CAAC,CAAC;IAC9D,QAAQ,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3C,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;CACvB;AASD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,GAAG,CAAC,CAAC,EAAE,MAAM,EAAE,SAAS,CAAC,CAAC,CAAC,GAAG,IAAI,CAyJjD;AAMD;;GAEG;AACH,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,CAAC,MAAM,OAAO,CAAC,GAAG,OAAO,CAAC;IAChC,MAAM,EAAE,MAAM,IAAI,CAAC;CACpB;AAED;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;CAC5B;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,CAAC,MAAM,OAAO,CAAC,GAAG,OAAO,CAAC;IAChC,QAAQ,EAAE,MAAM,IAAI,CAAC;CACtB;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,MAAM,CAAC,MAAM,EAAE,YAAY,GAAG,IAAI,CA6EjD;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,KAAK,CAAC,MAAM,EAAE,WAAW,GAAG,SAAS,CAEpD;AAMD;;;;;;GAMG;AACH,MAAM,WAAW,aAAa,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAC9D,SAAS,EAAE,MAAM,cAAc,GAAG,gBAAgB,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;IAC7D,KAAK,CAAC,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC;CACvB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,OAAO,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,EAAE,aAAa,CAAC,CAAC,CAAC,GAAG,IAAI,CA2FzF"}