@constela/runtime 0.19.6 → 0.19.7

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 (3) hide show
  1. package/dist/index.d.ts +484 -2
  2. package/dist/index.js +1929 -22
  3. package/package.json +1 -1
package/dist/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
- import { CompiledExpression, CompiledAction, CompiledNode, CompiledLocalAction, CompiledProgram } from '@constela/compiler';
1
+ import { CompiledExpression, CompiledAction, CompiledNode, CompiledLocalAction, CompiledProgram, CompiledIslandNode } from '@constela/compiler';
2
+ import { IslandStrategy, IslandStrategyOptions, ThemeConfig, ColorScheme, ThemeColors, ThemeFonts } from '@constela/core';
2
3
 
3
4
  interface Signal<T> {
4
5
  get(): T;
@@ -245,6 +246,187 @@ declare function createWebSocketConnection(url: string, handlers: WebSocketHandl
245
246
  */
246
247
  declare function createConnectionManager(): ConnectionManager;
247
248
 
249
+ /**
250
+ * SSE (Server-Sent Events) Connection Module
251
+ *
252
+ * This file provides the interface definitions and implementations
253
+ * for SSE connection management in Constela runtime.
254
+ *
255
+ * SSE is a one-way communication protocol from server to client,
256
+ * making it ideal for real-time updates, notifications, and streaming data.
257
+ */
258
+ /**
259
+ * SSE connection interface for managing server-sent events
260
+ * Note: SSE is one-way (server to client), so there is no send method
261
+ */
262
+ interface SSEConnection {
263
+ /**
264
+ * Close the SSE connection
265
+ */
266
+ close(): void;
267
+ /**
268
+ * Get the current connection state
269
+ * @returns The connection state
270
+ */
271
+ getState(): 'connecting' | 'open' | 'closed';
272
+ }
273
+ /**
274
+ * Event handlers for SSE connection events
275
+ */
276
+ interface SSEHandlers {
277
+ /** Called when connection is established */
278
+ onOpen?: () => Promise<void> | void;
279
+ /** Called when connection is closed */
280
+ onClose?: () => Promise<void> | void;
281
+ /** Called when an error occurs */
282
+ onError?: (error: Event) => Promise<void> | void;
283
+ /** Called when a message is received (JSON parsed if possible) */
284
+ onMessage?: (data: unknown, eventType: string) => Promise<void> | void;
285
+ }
286
+ /**
287
+ * SSE Connection manager for named connections
288
+ */
289
+ interface SSEConnectionManager {
290
+ /**
291
+ * Create a new named SSE connection
292
+ * If a connection with the same name exists, it will be closed first
293
+ */
294
+ create(name: string, url: string, handlers: SSEHandlers, eventTypes?: string[]): void;
295
+ /**
296
+ * Get a connection by name
297
+ * @returns The connection or undefined if not found
298
+ */
299
+ get(name: string): SSEConnection | undefined;
300
+ /**
301
+ * Close a named connection
302
+ * No-op if connection not found
303
+ */
304
+ close(name: string): void;
305
+ /**
306
+ * Close all connections
307
+ */
308
+ closeAll(): void;
309
+ }
310
+ /**
311
+ * Create an SSE connection with event handlers
312
+ *
313
+ * @param url - The SSE endpoint URL (e.g., "https://api.example.com/events")
314
+ * @param handlers - Event handlers for connection lifecycle
315
+ * @param eventTypes - Optional array of custom event types to listen for
316
+ * @returns An SSEConnection interface for managing the connection
317
+ */
318
+ declare function createSSEConnection(url: string, handlers: SSEHandlers, eventTypes?: string[]): SSEConnection;
319
+ /**
320
+ * Create an SSE connection manager for managing multiple named connections
321
+ *
322
+ * @returns An SSEConnectionManager interface
323
+ */
324
+ declare function createSSEConnectionManager(): SSEConnectionManager;
325
+
326
+ /**
327
+ * OptimisticManager - Manages optimistic UI updates with rollback support.
328
+ *
329
+ * TDD Stub: This file contains only type definitions and stub implementations
330
+ * to allow tests to run and fail at assertion level (Red phase).
331
+ */
332
+ /**
333
+ * Represents a pending optimistic update that can be confirmed or rejected.
334
+ */
335
+ interface PendingUpdate {
336
+ /** Unique identifier for this update */
337
+ id: string;
338
+ /** The state target being updated */
339
+ target: string;
340
+ /** Optional path for nested updates (e.g., ['items', 0, 'liked']) */
341
+ path: (string | number)[] | undefined;
342
+ /** The original value before optimistic update */
343
+ originalValue: unknown;
344
+ /** The optimistically applied value */
345
+ optimisticValue: unknown;
346
+ /** Timestamp when the update was applied */
347
+ timestamp: number;
348
+ }
349
+ /**
350
+ * Manager for optimistic UI updates with automatic rollback support.
351
+ */
352
+ interface OptimisticManager {
353
+ /**
354
+ * Apply an optimistic update immediately.
355
+ * @param target - The state target to update
356
+ * @param path - Optional path for nested updates
357
+ * @param value - The optimistic value to apply
358
+ * @param getState - Function to get current state
359
+ * @param setState - Function to set new state
360
+ * @returns Unique ID for this update
361
+ */
362
+ apply(target: string, path: (string | number)[] | undefined, value: unknown, getState: (target: string) => unknown, setState: (target: string, value: unknown) => void): string;
363
+ /**
364
+ * Confirm an optimistic update (server accepted).
365
+ * @param id - The update ID to confirm
366
+ * @returns true if update was found and confirmed, false otherwise
367
+ */
368
+ confirm(id: string): boolean;
369
+ /**
370
+ * Reject an optimistic update and rollback to original value.
371
+ * @param id - The update ID to reject
372
+ * @param getState - Function to get current state
373
+ * @param setState - Function to set new state
374
+ * @returns true if update was found and rejected, false otherwise
375
+ */
376
+ reject(id: string, getState: (target: string) => unknown, setState: (target: string, value: unknown) => void): boolean;
377
+ /**
378
+ * Get a pending update by ID.
379
+ * @param id - The update ID to look up
380
+ * @returns The pending update or undefined if not found
381
+ */
382
+ getPending(id: string): PendingUpdate | undefined;
383
+ /**
384
+ * Get all pending updates.
385
+ * @returns Array of all pending updates
386
+ */
387
+ getAllPending(): PendingUpdate[];
388
+ /**
389
+ * Set auto-rollback timeout for pending updates.
390
+ * @param ms - Timeout in milliseconds (0 to disable)
391
+ */
392
+ setAutoRollbackTimeout(ms: number): void;
393
+ /**
394
+ * Dispose the manager and clear all pending updates.
395
+ */
396
+ dispose(): void;
397
+ }
398
+ /**
399
+ * Create a new OptimisticManager instance.
400
+ */
401
+ declare function createOptimisticManager(): OptimisticManager;
402
+
403
+ /**
404
+ * Realtime Data Binding
405
+ *
406
+ * Automatically updates state when messages arrive from WebSocket/SSE connections.
407
+ */
408
+ interface BindingConfig {
409
+ connection: string;
410
+ eventType?: string;
411
+ target: string;
412
+ path?: (string | number)[];
413
+ transform?: (data: unknown) => unknown;
414
+ patch?: boolean;
415
+ }
416
+ type SetStateFn = (target: string, value: unknown, pathOrOptions?: (string | number)[] | {
417
+ patch: boolean;
418
+ }) => void;
419
+ interface BindingManager {
420
+ bind(config: BindingConfig, setState: SetStateFn): string;
421
+ unbind(id: string): boolean;
422
+ unbindByConnection(connection: string): void;
423
+ unbindByTarget(target: string): void;
424
+ handleMessage(connection: string, data: unknown, eventType?: string): void;
425
+ getBindings(): BindingConfig[];
426
+ dispose(): void;
427
+ }
428
+ declare function createBindingManager(): BindingManager;
429
+
248
430
  /**
249
431
  * Action Executor - Executes compiled action steps
250
432
  *
@@ -286,6 +468,9 @@ interface ActionContext {
286
468
  };
287
469
  imports?: Record<string, unknown>;
288
470
  connections?: ConnectionManager;
471
+ sse?: SSEConnectionManager;
472
+ optimistic?: OptimisticManager;
473
+ binding?: BindingManager;
289
474
  }
290
475
  declare function executeAction(action: CompiledAction | ExtendedAction, ctx: ActionContext): Promise<void>;
291
476
 
@@ -392,6 +577,191 @@ interface HydrateOptions {
392
577
  * @returns AppInstance for controlling the hydrated application
393
578
  */
394
579
  declare function hydrateApp(options: HydrateOptions): AppInstance;
580
+ /**
581
+ * Hydrates all islands in the application with their respective strategies.
582
+ * Returns a cleanup function that cleans up all island resources.
583
+ *
584
+ * @param program - The compiled program
585
+ * @param options - Options including the container element
586
+ * @returns Cleanup function to dispose all island hydrations
587
+ */
588
+ declare function hydrateAppWithIslands(program: CompiledProgram, options?: {
589
+ container?: HTMLElement;
590
+ }): () => void;
591
+
592
+ /**
593
+ * Island Hydration - Runtime Partial Hydration
594
+ *
595
+ * This module provides selective hydration for Islands Architecture,
596
+ * enabling fine-grained control over when interactive components become active.
597
+ */
598
+
599
+ interface IslandHydrationOptions {
600
+ element: HTMLElement;
601
+ id: string;
602
+ strategy: IslandStrategy;
603
+ strategyOptions?: IslandStrategyOptions;
604
+ state?: Record<string, unknown>;
605
+ program: CompiledProgram | CompiledIslandNode;
606
+ }
607
+ /**
608
+ * Hydrates an island based on the specified strategy.
609
+ * Returns a cleanup function to cancel pending hydration or cleanup resources.
610
+ */
611
+ declare function hydrateIsland(options: IslandHydrationOptions): () => void;
612
+ /**
613
+ * Detects islands in the DOM by looking for data attributes.
614
+ * Returns an array of island configurations extracted from the DOM.
615
+ */
616
+ declare function detectIslandsInDOM(container: HTMLElement): Array<{
617
+ element: HTMLElement;
618
+ id: string;
619
+ strategy: IslandStrategy;
620
+ strategyOptions?: IslandStrategyOptions;
621
+ state?: Record<string, unknown>;
622
+ }>;
623
+
624
+ /**
625
+ * Island Loader - Dynamic module loading for Islands Architecture
626
+ *
627
+ * This module provides lazy loading and caching for island bundles,
628
+ * enabling efficient code splitting for interactive components.
629
+ */
630
+ /**
631
+ * Options for creating an island loader.
632
+ */
633
+ interface IslandLoaderOptions {
634
+ /** Base path for island bundles (default: "/_constela/islands/") */
635
+ basePath?: string;
636
+ }
637
+ /**
638
+ * Loaded island module structure.
639
+ */
640
+ interface LoadedIslandModule {
641
+ /** The island definition */
642
+ island: unknown;
643
+ /** Island state definitions */
644
+ state: Record<string, {
645
+ type: string;
646
+ initial: unknown;
647
+ }>;
648
+ /** Island action definitions */
649
+ actions: Record<string, unknown>;
650
+ /** Default export (same as island) */
651
+ default: unknown;
652
+ }
653
+ /**
654
+ * Island loader interface.
655
+ */
656
+ interface IslandLoader {
657
+ /**
658
+ * Load an island module by ID.
659
+ * Returns a promise that resolves with the loaded module.
660
+ */
661
+ load(id: string): Promise<LoadedIslandModule>;
662
+ /**
663
+ * Preload an island module in the background.
664
+ * Fire-and-forget operation for warming the cache.
665
+ */
666
+ preload(id: string): void;
667
+ }
668
+ /**
669
+ * Create an island loader for dynamically loading island bundles.
670
+ *
671
+ * The loader provides:
672
+ * - Dynamic import of island bundles
673
+ * - Caching of loaded modules
674
+ * - Deduplication of concurrent requests
675
+ * - Preloading for performance optimization
676
+ *
677
+ * @param options - Loader options
678
+ * @returns Island loader instance
679
+ *
680
+ * @example
681
+ * ```typescript
682
+ * const loader = createIslandLoader();
683
+ *
684
+ * // Load an island when needed
685
+ * const module = await loader.load('counter-island');
686
+ *
687
+ * // Preload islands for faster subsequent loads
688
+ * loader.preload('modal-island');
689
+ * ```
690
+ */
691
+ declare function createIslandLoader(options?: IslandLoaderOptions): IslandLoader;
692
+
693
+ /**
694
+ * Reconnection Manager Module
695
+ *
696
+ * Provides automatic reconnection logic for WebSocket and SSE connections
697
+ * with configurable backoff strategies (exponential, linear, or none).
698
+ *
699
+ * Features:
700
+ * - Exponential backoff: delay = min(baseDelay * 2^attempt, maxDelay)
701
+ * - Linear backoff: delay = min(baseDelay * (attempt + 1), maxDelay)
702
+ * - Max retries limit with callback
703
+ * - Retry count reset on successful reconnection
704
+ * - Manual trigger reconnection
705
+ * - Timer cleanup on dispose
706
+ */
707
+ /**
708
+ * Reconnection policy configuration
709
+ */
710
+ interface ReconnectionPolicy {
711
+ /** Whether automatic reconnection is enabled */
712
+ enabled: boolean;
713
+ /** Backoff strategy: 'exponential' doubles delay, 'linear' adds baseDelay, 'none' disables */
714
+ strategy: 'exponential' | 'linear' | 'none';
715
+ /** Maximum number of reconnection attempts */
716
+ maxRetries: number;
717
+ /** Initial delay in milliseconds */
718
+ baseDelay: number;
719
+ /** Maximum delay cap in milliseconds */
720
+ maxDelay: number;
721
+ }
722
+ /**
723
+ * Interface for reconnectable connections (WebSocket, SSE)
724
+ */
725
+ interface Reconnectable {
726
+ /** Get current connection state */
727
+ getState(): string;
728
+ /** Close the connection */
729
+ close(): void;
730
+ }
731
+ /**
732
+ * Reconnection manager interface
733
+ */
734
+ interface ReconnectionManager {
735
+ /**
736
+ * Wrap a connection with automatic reconnection logic
737
+ * @param connection - The connection to wrap
738
+ * @param reconnectFn - Function that creates a new connection
739
+ * @param policy - Reconnection policy configuration
740
+ * @param onReconnect - Optional callback when reconnection succeeds
741
+ * @param onMaxRetriesReached - Optional callback when max retries reached
742
+ * @returns The wrapped connection
743
+ */
744
+ wrap<T extends Reconnectable>(connection: T, reconnectFn: () => T, policy: ReconnectionPolicy, onReconnect?: () => void, onMaxRetriesReached?: () => void): T;
745
+ /**
746
+ * Manually trigger reconnection for a connection
747
+ * @param connection - The connection to reconnect
748
+ */
749
+ triggerReconnect(connection: Reconnectable): void;
750
+ /**
751
+ * Stop reconnection attempts and cleanup for a specific connection
752
+ * @param connection - The connection to dispose
753
+ */
754
+ dispose(connection: Reconnectable): void;
755
+ /**
756
+ * Stop all reconnection attempts and cleanup all connections
757
+ */
758
+ disposeAll(): void;
759
+ }
760
+ /**
761
+ * Create a reconnection manager that handles automatic reconnection
762
+ * for WebSocket and SSE connections with configurable backoff strategies.
763
+ */
764
+ declare function createReconnectionManager(): ReconnectionManager;
395
765
 
396
766
  /**
397
767
  * HMR Client - WebSocket client for Hot Module Replacement
@@ -590,4 +960,116 @@ interface ErrorOverlay {
590
960
  */
591
961
  declare function createErrorOverlay(): ErrorOverlay;
592
962
 
593
- export { type ActionContext, type AppInstance, type Computed, type ConnectionManager, type ErrorInfo, type ErrorOverlay, type EvaluationContext, type HMRClient, type HMRClientOptions, type HMRHandler, type HMRHandlerOptions, type HydrateOptions, type RenderContext, type RouteContext, type Signal, type StateStore, type StylePreset, type TypedStateStore, type WebSocketConnection, type WebSocketHandlers, createApp, createComputed, createConnectionManager, createEffect, createErrorOverlay, createHMRClient, createHMRHandler, createSignal, createStateStore, createTypedStateStore, createWebSocketConnection, evaluate, evaluateStyle, executeAction, hydrateApp, render };
963
+ /**
964
+ * ThemeProvider - Runtime theme management for Constela UI
965
+ *
966
+ * Provides reactive theme state management with:
967
+ * - System theme detection via prefers-color-scheme
968
+ * - CSS variable application to :root
969
+ * - Dark class management on document.documentElement
970
+ * - Persistence via localStorage (optional cookies)
971
+ * - Subscription-based notifications
972
+ */
973
+
974
+ interface ThemeProviderOptions {
975
+ config?: ThemeConfig;
976
+ storageKey?: string;
977
+ useCookies?: boolean;
978
+ defaultMode?: ColorScheme;
979
+ }
980
+ interface ResolvedTheme {
981
+ resolvedMode: 'light' | 'dark';
982
+ selectedMode: ColorScheme;
983
+ colors: ThemeColors;
984
+ fonts: ThemeFonts;
985
+ }
986
+ interface ThemeProvider {
987
+ getTheme(): ResolvedTheme;
988
+ getMode(): ColorScheme;
989
+ setMode(mode: ColorScheme): void;
990
+ subscribe(fn: (theme: ResolvedTheme) => void): () => void;
991
+ setColors(colors: ThemeColors, mode?: 'light' | 'dark'): void;
992
+ destroy(): void;
993
+ }
994
+ /**
995
+ * Creates a ThemeProvider instance for managing application theme state.
996
+ */
997
+ declare function createThemeProvider(options?: ThemeProviderOptions): ThemeProvider;
998
+
999
+ /**
1000
+ * Island Prefetching - Optimized preloading for Islands Architecture
1001
+ *
1002
+ * This module provides prefetching strategies for island bundles,
1003
+ * enabling optimized loading based on user interaction patterns.
1004
+ */
1005
+
1006
+ declare const PREFETCH_STRATEGIES: readonly ["hover", "visible", "immediate"];
1007
+ type PrefetchStrategy = (typeof PREFETCH_STRATEGIES)[number];
1008
+ /**
1009
+ * Options for configuring prefetch behavior.
1010
+ */
1011
+ interface PrefetchOptions {
1012
+ /** The prefetch strategy to use */
1013
+ strategy: PrefetchStrategy;
1014
+ /** Intersection threshold for visible strategy (0-1) */
1015
+ threshold?: number;
1016
+ /** Root margin for visible strategy */
1017
+ rootMargin?: string;
1018
+ }
1019
+ /**
1020
+ * Prefetcher interface returned by createPrefetcher.
1021
+ */
1022
+ interface Prefetcher {
1023
+ /**
1024
+ * Prefetch an island when the element is hovered.
1025
+ * Returns a cleanup function to remove the event listener.
1026
+ */
1027
+ prefetchOnHover(element: HTMLElement, id: string): () => void;
1028
+ /**
1029
+ * Prefetch an island when the element becomes visible.
1030
+ * Returns a cleanup function to unobserve the element.
1031
+ */
1032
+ prefetchOnVisible(element: HTMLElement, id: string, options?: PrefetchOptions): () => void;
1033
+ }
1034
+ /**
1035
+ * Checks if a value is a valid PrefetchOptions object.
1036
+ */
1037
+ declare function isPrefetchOptions(value: unknown): value is PrefetchOptions;
1038
+ /**
1039
+ * Set the global island loader for prefetchIsland.
1040
+ * This is typically called during app initialization.
1041
+ */
1042
+ declare function setGlobalLoader(loader: IslandLoader): void;
1043
+ /**
1044
+ * Prefetch an island by ID.
1045
+ * Uses the global loader if available.
1046
+ *
1047
+ * @param id - The island ID to prefetch
1048
+ * @param options - Optional prefetch options
1049
+ */
1050
+ declare function prefetchIsland(id: string, options?: PrefetchOptions): void;
1051
+ /**
1052
+ * Create a prefetcher instance bound to a specific island loader.
1053
+ *
1054
+ * @param loader - The island loader to use for prefetching
1055
+ * @returns Prefetcher instance with prefetchOnHover and prefetchOnVisible methods
1056
+ *
1057
+ * @example
1058
+ * ```typescript
1059
+ * const loader = createIslandLoader();
1060
+ * const prefetcher = createPrefetcher(loader);
1061
+ *
1062
+ * // Prefetch on hover
1063
+ * const cleanup = prefetcher.prefetchOnHover(element, 'counter-island');
1064
+ *
1065
+ * // Prefetch when visible
1066
+ * const cleanup2 = prefetcher.prefetchOnVisible(element, 'chart-island', {
1067
+ * strategy: 'visible',
1068
+ * threshold: 0.5,
1069
+ * rootMargin: '100px',
1070
+ * });
1071
+ * ```
1072
+ */
1073
+ declare function createPrefetcher(loader: IslandLoader): Prefetcher;
1074
+
1075
+ export { type ActionContext, type AppInstance, type BindingConfig, type BindingManager, type Computed, type ConnectionManager, type ErrorInfo, type ErrorOverlay, type EvaluationContext, type HMRClient, type HMRClientOptions, type HMRHandler, type HMRHandlerOptions, type HydrateOptions, type IslandHydrationOptions, type IslandLoader, type IslandLoaderOptions, type LoadedIslandModule, type OptimisticManager, PREFETCH_STRATEGIES, type PendingUpdate, type PrefetchOptions, type PrefetchStrategy, type Prefetcher, type Reconnectable, type ReconnectionManager, type ReconnectionPolicy, type RenderContext, type ResolvedTheme, type RouteContext, type SSEConnection, type SSEConnectionManager, type SSEHandlers, type Signal, type StateStore, type StylePreset, type ThemeProvider, type ThemeProviderOptions, type TypedStateStore, type WebSocketConnection, type WebSocketHandlers, createApp, createBindingManager, createComputed, createConnectionManager, createEffect, createErrorOverlay, createHMRClient, createHMRHandler, createIslandLoader, createOptimisticManager, createPrefetcher, createReconnectionManager, createSSEConnection, createSSEConnectionManager, createSignal, createStateStore, createThemeProvider, createTypedStateStore, createWebSocketConnection, detectIslandsInDOM, evaluate, evaluateStyle, executeAction, hydrateApp, hydrateAppWithIslands, hydrateIsland, isPrefetchOptions, prefetchIsland, render, setGlobalLoader };