@coherent.js/core 1.0.0 → 1.0.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/dist/index.js CHANGED
@@ -6517,7 +6517,7 @@ function deepClone2(obj) {
6517
6517
  }
6518
6518
  return cloned;
6519
6519
  }
6520
- var VERSION = true ? "1.0.0" : JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8")).version;
6520
+ var VERSION = true ? "1.0.1" : JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8")).version;
6521
6521
  var fp = {
6522
6522
  /**
6523
6523
  * Curried map: fp.map(fn)(array)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@coherent.js/core",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "Core runtime for Coherent.js (SSR framework).",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
package/types/index.d.ts CHANGED
@@ -5,8 +5,10 @@
5
5
  * @version 1.0.0-beta.1
6
6
  */
7
7
 
8
- // Re-export strict element types
8
+ // Re-export strict element types. `export *` re-exports without binding the
9
+ // names locally, so CoherentNode below needs its own import.
9
10
  export * from './elements';
11
+ import type { StrictCoherentElement } from './elements';
10
12
 
11
13
  // ============================================================================
12
14
  // Basic Types
@@ -762,16 +764,116 @@ export function createCacheManager(options?: CacheManagerOptions): CacheManager;
762
764
  // Component Cache (Additional)
763
765
  // ============================================================================
764
766
 
765
- /** Component cache class */
767
+ /** Options for {@link ComponentCache} */
768
+ export interface ComponentCacheOptions {
769
+ /** Entries retained before least-used eviction; defaults to `1000` */
770
+ maxSize?: number;
771
+ /** Entry lifetime in ms; defaults to `300000` */
772
+ defaultTTL?: number;
773
+ /** Expiry sweep interval in ms, or `0` to disable; defaults to `60000` */
774
+ cleanupInterval?: number;
775
+ /** Track hits, misses and evictions; defaults to `true` */
776
+ enableStats?: boolean;
777
+ [option: string]: unknown;
778
+ }
779
+
780
+ /** Per-entry overrides for {@link ComponentCache.set} */
781
+ export interface ComponentCacheEntryOptions {
782
+ /** Keys that invalidate this entry */
783
+ dependencies?: string[];
784
+ /** Lifetime in ms; defaults to the cache's `defaultTTL` */
785
+ ttl?: number;
786
+ /** Exempt from expiry */
787
+ persistent?: boolean;
788
+ }
789
+
790
+ export interface ComponentCacheStats {
791
+ size: number;
792
+ maxSize: number;
793
+ hits: number;
794
+ misses: number;
795
+ /** Percentage, as a fixed-2 string, or `0` before any request */
796
+ hitRate: string | number;
797
+ evictions: number;
798
+ cleanups: number;
799
+ invalidations: number;
800
+ dependencies: number;
801
+ /** Rough estimate in KB */
802
+ memoryUsage: number;
803
+ }
804
+
805
+ /**
806
+ * Caches rendered components with TTL, least-used eviction and
807
+ * dependency-based invalidation.
808
+ *
809
+ * `cache` is the backing Map, not a method — write through `set()`.
810
+ */
766
811
  export class ComponentCache {
767
- cache(key: string, component: CoherentNode): void;
768
- get(key: string): CoherentNode | undefined;
769
- invalidate(key: string): void;
770
- invalidateAll(): void;
812
+ constructor(options?: ComponentCacheOptions);
813
+
814
+ options: ComponentCacheOptions;
815
+ /** Backing store, keyed by cache key */
816
+ cache: Map<string, unknown>;
817
+ /** Dependency key to the cache keys that depend on it */
818
+ dependencies: Map<string, Set<string>>;
819
+
820
+ /** Derive a stable key from a component and its props */
821
+ generateKey(
822
+ component: unknown,
823
+ props?: Record<string, unknown>,
824
+ context?: Record<string, unknown>
825
+ ): string;
826
+
827
+ /**
828
+ * Read an entry, recording a hit or miss. Returns a deep clone, or `null`
829
+ * when absent or expired. Any `dependencies` passed are added to the entry.
830
+ */
831
+ get(key: string, dependencies?: string[]): CoherentNode | null;
832
+
833
+ /** Store an entry, evicting the least-used one when the cache is full */
834
+ set(key: string, component: CoherentNode, options?: ComponentCacheEntryOptions): boolean;
835
+
836
+ /** Whether a live, unexpired entry exists */
837
+ has(key: string): boolean;
838
+
839
+ /** Drop every entry depending on `dependency`; returns how many */
840
+ invalidate(dependency: string): number;
841
+
842
+ /** Drop every entry depending on any of `dependencies`; returns how many */
843
+ invalidateMultiple(dependencies: string[]): number;
844
+
845
+ /** Drop every entry */
846
+ clear(): void;
847
+
848
+ /** Drop expired entries; returns how many */
849
+ cleanup(): number;
850
+
851
+ getStats(): ComponentCacheStats;
852
+
853
+ /** Rough memory estimate in KB */
854
+ estimateMemoryUsage(): number;
855
+
856
+ /** Most-accessed entries, hottest first */
857
+ getHotComponents(limit?: number): Array<{
858
+ key: string;
859
+ accessCount: number;
860
+ component: CoherentNode;
861
+ dependencies: string[];
862
+ }>;
863
+
864
+ /** Tuning suggestions derived from the current stats */
865
+ getRecommendations(): Array<{
866
+ type: string;
867
+ message: string;
868
+ priority: 'HIGH' | 'MEDIUM' | 'LOW';
869
+ }>;
870
+
871
+ /** Stop the cleanup timer and drop every entry */
872
+ destroy(): void;
771
873
  }
772
874
 
773
875
  /** Create component cache */
774
- export function createComponentCache(options?: CacheManagerOptions): ComponentCache;
876
+ export function createComponentCache(options?: ComponentCacheOptions): ComponentCache;
775
877
 
776
878
  // ============================================================================
777
879
  // HTML Utilities
@@ -821,3 +923,497 @@ declare const coherent: {
821
923
  };
822
924
 
823
925
  export default coherent;
926
+
927
+ // ============================================================================
928
+ // Object Factory
929
+ // ============================================================================
930
+
931
+ /**
932
+ * Build a single-element node.
933
+ *
934
+ * @throws when `tag` is not a known HTML element.
935
+ */
936
+ export function createElement(tag: string, props?: Record<string, unknown>): CoherentNode;
937
+
938
+ /** Build a text node from any value. */
939
+ export function createTextNode(text: unknown): CoherentNode;
940
+
941
+ /**
942
+ * Shorthand element factories: `h.div({ text: 'hi' })` is
943
+ * `createElement('div', { text: 'hi' })`.
944
+ */
945
+ export const h: Record<string, (props?: Record<string, unknown>) => CoherentNode>;
946
+
947
+ // ============================================================================
948
+ // HTML Nesting Validation
949
+ // ============================================================================
950
+
951
+ /** Children the HTML spec forbids, keyed by parent tag. */
952
+ export const FORBIDDEN_CHILDREN: Record<string, Set<string>>;
953
+
954
+ /** Raised by {@link validateNesting} when `throwOnError` is set. */
955
+ export class HTMLNestingError extends Error {
956
+ constructor(message: string, context?: { parent?: string; child?: string; path?: string });
957
+ parent?: string;
958
+ child?: string;
959
+ path?: string;
960
+ }
961
+
962
+ /**
963
+ * Check one parent/child pair against {@link FORBIDDEN_CHILDREN}.
964
+ *
965
+ * Returns `true` when the nesting is legal. Otherwise warns (outside
966
+ * production) and returns `false`, or throws {@link HTMLNestingError} when
967
+ * `throwOnError` is set. Browsers silently reparent invalid nesting, which
968
+ * shows up later as a hydration mismatch.
969
+ */
970
+ export function validateNesting(
971
+ parentTag: string,
972
+ childTag: string,
973
+ path?: string,
974
+ options?: { warn?: boolean; throwOnError?: boolean }
975
+ ): boolean;
976
+
977
+ // ============================================================================
978
+ // Component Lifecycle
979
+ // ============================================================================
980
+
981
+ /** Lifecycle phase names. */
982
+ export const LIFECYCLE_PHASES: {
983
+ readonly BEFORE_CREATE: 'beforeCreate';
984
+ readonly CREATED: 'created';
985
+ readonly BEFORE_MOUNT: 'beforeMount';
986
+ readonly MOUNTED: 'mounted';
987
+ readonly BEFORE_UPDATE: 'beforeUpdate';
988
+ readonly UPDATED: 'updated';
989
+ readonly BEFORE_UNMOUNT: 'beforeUnmount';
990
+ readonly UNMOUNTED: 'unmounted';
991
+ readonly ERROR: '_error';
992
+ };
993
+
994
+ /** One of the {@link LIFECYCLE_PHASES} values. */
995
+ export type LifecyclePhase =
996
+ | 'beforeCreate'
997
+ | 'created'
998
+ | 'beforeMount'
999
+ | 'mounted'
1000
+ | 'beforeUpdate'
1001
+ | 'updated'
1002
+ | 'beforeUnmount'
1003
+ | 'unmounted'
1004
+ | '_error';
1005
+
1006
+ /**
1007
+ * Tracks one component's phase, hooks, state and cleanup.
1008
+ *
1009
+ * Timers, listeners and subscriptions registered through the instance are
1010
+ * released on unmount, so prefer them over the globals.
1011
+ */
1012
+ export class ComponentLifecycle {
1013
+ constructor(component: unknown, options?: Record<string, unknown>);
1014
+
1015
+ component: unknown;
1016
+ id: string;
1017
+ options: Record<string, unknown>;
1018
+ phase: LifecyclePhase | null;
1019
+ hooks: Map<string, Array<(...args: never[]) => unknown>>;
1020
+ state: Map<string, unknown>;
1021
+ props: Record<string, unknown>;
1022
+ context: Record<string, unknown>;
1023
+ isMounted: boolean;
1024
+ isDestroyed: boolean;
1025
+ children: Set<ComponentLifecycle>;
1026
+ parent: ComponentLifecycle | null;
1027
+
1028
+ /** Register a callback for a phase */
1029
+ hook(phase: LifecyclePhase, callback: (...args: never[]) => unknown): this;
1030
+
1031
+ addChild(child: ComponentLifecycle): void;
1032
+ removeChild(child: ComponentLifecycle): void;
1033
+
1034
+ /** Add a listener released on unmount */
1035
+ addEventListener(
1036
+ element: unknown,
1037
+ event: string,
1038
+ listener: (event: unknown) => void,
1039
+ options?: Record<string, unknown>
1040
+ ): void;
1041
+
1042
+ /** Register an unsubscribe called on unmount */
1043
+ addSubscription(unsubscribe: () => void): void;
1044
+
1045
+ /** `setTimeout` cleared on unmount */
1046
+ setTimeout(callback: () => void, delay: number): unknown;
1047
+ /** `setInterval` cleared on unmount */
1048
+ setInterval(callback: () => void, interval: number): unknown;
1049
+
1050
+ /** Counts of hooks, children, timers and subscriptions */
1051
+ getStats(): Record<string, unknown>;
1052
+ }
1053
+
1054
+ /**
1055
+ * Component-scoped event emitter used by the lifecycle system. Reachable
1056
+ * through {@link eventSystem}; the class itself is not exported.
1057
+ */
1058
+ declare class ComponentEventSystem {
1059
+ constructor();
1060
+ emit(eventName: string, data?: unknown, target?: unknown): unknown;
1061
+ on(eventName: string, handler: (event: unknown) => void, componentId?: string | null): () => void;
1062
+ off(eventName: string, handler: (event: unknown) => void, componentId?: string | null): void;
1063
+ once(eventName: string, handler: (event: unknown) => void, componentId?: string | null): () => void;
1064
+ /** Release every handler for a component */
1065
+ cleanup(componentId: string): void;
1066
+ getStats(): Record<string, unknown>;
1067
+ }
1068
+
1069
+ /** Process-wide {@link ComponentEventSystem}. */
1070
+ export const eventSystem: ComponentEventSystem;
1071
+
1072
+ /**
1073
+ * Build a hook registrar per lifecycle phase. Each registers against the
1074
+ * instance currently being created, and is a no-op outside that window.
1075
+ */
1076
+ export function createLifecycleHooks(): Record<LifecyclePhase, (callback: (...args: never[]) => unknown) => void>;
1077
+
1078
+ /** Shared {@link createLifecycleHooks} result. */
1079
+ export const useHooks: Record<LifecyclePhase, (callback: (...args: never[]) => unknown) => void>;
1080
+
1081
+ /** Helpers for reaching lifecycle instances from a component. */
1082
+ export const lifecycleUtils: {
1083
+ /** The instance attached to a component, if any */
1084
+ getLifecycle(component: unknown): ComponentLifecycle | undefined;
1085
+ /** Wrap a component with a lifecycle and expose its mount hooks */
1086
+ createWithLifecycle(
1087
+ component: unknown,
1088
+ options?: Record<string, unknown>
1089
+ ): {
1090
+ component: unknown;
1091
+ lifecycle: ComponentLifecycle;
1092
+ mount: (...args: never[]) => unknown;
1093
+ unmount: (...args: never[]) => unknown;
1094
+ update: (...args: never[]) => unknown;
1095
+ };
1096
+ /** Every live instance */
1097
+ getAllInstances(): ComponentLifecycle[];
1098
+ findById(id: string): ComponentLifecycle | undefined;
1099
+ /** Emit through {@link eventSystem}, targeting a component */
1100
+ emit(component: unknown, eventName: string, data?: unknown): unknown;
1101
+ /** Listen for a component's events; returns an unsubscribe function */
1102
+ listen(component: unknown, eventName: string, handler: (event: unknown) => void): () => void;
1103
+ };
1104
+
1105
+ /** Wrap a component so a {@link ComponentLifecycle} is created around renders. */
1106
+ export function withLifecycle(
1107
+ component: CoherentComponent | CoherentNode,
1108
+ options?: Record<string, unknown>
1109
+ ): (props?: Record<string, unknown>) => CoherentNode;
1110
+
1111
+ // ============================================================================
1112
+ // Error Boundaries
1113
+ // ============================================================================
1114
+
1115
+ export interface ErrorBoundaryOptions {
1116
+ /** Rendered instead of the children when a render throws */
1117
+ fallback?: CoherentNode | ((error: Error, reset: () => void) => CoherentNode);
1118
+ /** Notified when a render throws */
1119
+ onError?: (error: Error, errorInfo: Record<string, unknown>) => void;
1120
+ /** Clear the error when any of these values change */
1121
+ resetKeys?: unknown[];
1122
+ [option: string]: unknown;
1123
+ }
1124
+
1125
+ /** Wrap children so a render error shows a fallback instead of propagating. */
1126
+ export function createErrorBoundary(options?: ErrorBoundaryOptions): CoherentComponent;
1127
+
1128
+ /** Build a fallback node for an error boundary. */
1129
+ export function createErrorFallback(options?: Record<string, unknown>): CoherentNode;
1130
+
1131
+ /** Wrap components in an error boundary. */
1132
+ export function withErrorBoundary(
1133
+ options: ErrorBoundaryOptions,
1134
+ components: CoherentComponent | CoherentComponent[]
1135
+ ): CoherentComponent;
1136
+
1137
+ /** An error boundary that also catches rejections from async children. */
1138
+ export function createAsyncErrorBoundary(options?: ErrorBoundaryOptions): CoherentComponent;
1139
+
1140
+ export interface GlobalErrorHandlerOptions {
1141
+ /** Cap on retained errors */
1142
+ maxErrors?: number;
1143
+ /** Notified for every captured error */
1144
+ onError?: (error: Error, context: Record<string, unknown>) => void;
1145
+ [option: string]: unknown;
1146
+ }
1147
+
1148
+ /** Collects errors that escaped every boundary. */
1149
+ export class GlobalErrorHandler {
1150
+ constructor(options?: GlobalErrorHandlerOptions);
1151
+
1152
+ /** Record an error with context */
1153
+ captureError(error: Error, context?: Record<string, unknown>): void;
1154
+ /** Retained errors */
1155
+ getErrors(): Array<{ error: Error; context: Record<string, unknown>; timestamp: number }>;
1156
+ clearErrors(): void;
1157
+ /** Counts by type and recency */
1158
+ getStats(): Record<string, unknown>;
1159
+ enable(): void;
1160
+ disable(): void;
1161
+ }
1162
+
1163
+ /** Create a {@link GlobalErrorHandler}. */
1164
+ export function createGlobalErrorHandler(options?: GlobalErrorHandlerOptions): GlobalErrorHandler;
1165
+
1166
+ // ============================================================================
1167
+ // Event Bus
1168
+ // ============================================================================
1169
+
1170
+ /** Called when a subscribed event fires. */
1171
+ export type EventListener = (data: unknown, event: string) => unknown;
1172
+
1173
+ export interface EventListenerOptions {
1174
+ /** Higher runs first when `enablePriority` is set */
1175
+ priority?: number;
1176
+ /** Skip the listener when this returns false */
1177
+ condition?: (data: unknown) => boolean;
1178
+ [option: string]: unknown;
1179
+ }
1180
+
1181
+ export interface EventBusOptions {
1182
+ /** Log every emit; defaults to `false` */
1183
+ debug?: boolean;
1184
+ /** Track emit timings; defaults to `true` */
1185
+ performance?: boolean;
1186
+ /** Listeners per event before warning; defaults to `100` */
1187
+ maxListeners?: number;
1188
+ /** Allow `a:*` patterns; defaults to `true` */
1189
+ enableWildcards?: boolean;
1190
+ /** Allow async listeners; defaults to `true` */
1191
+ enableAsync?: boolean;
1192
+ /** Wildcard segment separator; defaults to `':'` */
1193
+ wildcardSeparator?: string;
1194
+ /** Honor listener priority; defaults to `true` */
1195
+ enablePriority?: boolean;
1196
+ defaultPriority?: number;
1197
+ errorHandler?: ((error: Error, event: string, data: unknown) => void) | null;
1198
+ filters?: {
1199
+ /** Only these events pass; `null` allows all */
1200
+ allowList?: string[] | null;
1201
+ blockList?: string[];
1202
+ };
1203
+ throttle?: {
1204
+ enabled?: boolean;
1205
+ defaultDelay?: number;
1206
+ events?: Record<string, number>;
1207
+ };
1208
+ batching?: {
1209
+ enabled?: boolean;
1210
+ maxBatchSize?: number;
1211
+ flushInterval?: number;
1212
+ };
1213
+ [option: string]: unknown;
1214
+ }
1215
+
1216
+ export interface EventBusStats {
1217
+ eventsEmitted: number;
1218
+ listenersExecuted: number;
1219
+ errorsOccurred: number;
1220
+ averageEmitTime: number;
1221
+ throttledEvents: number;
1222
+ filteredEvents: number;
1223
+ }
1224
+
1225
+ /**
1226
+ * Pub/sub bus with wildcards, priorities, middleware, throttling and
1227
+ * batching, plus a named-action registry for DOM handlers.
1228
+ */
1229
+ export class EventBus {
1230
+ constructor(options?: EventBusOptions);
1231
+
1232
+ options: EventBusOptions;
1233
+ listeners: Map<string, unknown[]>;
1234
+ actionHandlers: Map<string, (...args: never[]) => unknown>;
1235
+ middleware: Array<(event: string, data: unknown, next: () => void) => void>;
1236
+
1237
+ /** Add middleware run before listeners */
1238
+ use(middleware: (event: string, data: unknown, next: () => void) => void): this;
1239
+
1240
+ /** Emit, honoring batching and throttling */
1241
+ emit(event: string, data?: unknown): Promise<unknown>;
1242
+
1243
+ /** Emit immediately, ignoring batching and throttling */
1244
+ emitSync(event: string, data?: unknown): void;
1245
+
1246
+ /** Subscribe; returns the listener id for {@link EventBus.off} */
1247
+ on(event: string, listener: EventListener, options?: EventListenerOptions): string;
1248
+
1249
+ /** Subscribe for one emit */
1250
+ once(event: string, listener: EventListener, options?: EventListenerOptions): string;
1251
+
1252
+ /** Unsubscribe by listener id; `false` when not found */
1253
+ off(event: string, listenerId: string): boolean;
1254
+
1255
+ /** Drop every listener for an event, or for all events */
1256
+ removeAllListeners(event?: string): void;
1257
+
1258
+ /** Listeners matching an event, wildcards included */
1259
+ getEventListeners(event: string): unknown[];
1260
+
1261
+ /** Register a named action for DOM handlers */
1262
+ registerAction(action: string, handler: (...args: never[]) => unknown): void;
1263
+ /** Register several named actions */
1264
+ registerActions(actions: Record<string, (...args: never[]) => unknown>): void;
1265
+ getRegisteredActions(): string[];
1266
+ /** Invoke a registered action */
1267
+ handleAction(action: string, element?: unknown, event?: unknown, data?: unknown): unknown;
1268
+
1269
+ getStats(): EventBusStats;
1270
+ resetStats(): void;
1271
+
1272
+ /** Drop every listener, action and timer */
1273
+ destroy(): void;
1274
+ }
1275
+
1276
+ /** Create an {@link EventBus}. */
1277
+ export function createEventBus(options?: EventBusOptions): EventBus;
1278
+
1279
+ /** Process-wide {@link EventBus} backing the module-level helpers. */
1280
+ export const globalEventBus: EventBus;
1281
+
1282
+ /** {@link EventBus.emit} on {@link globalEventBus}. */
1283
+ export const emit: EventBus['emit'];
1284
+ /** {@link EventBus.emitSync} on {@link globalEventBus}. */
1285
+ export const emitSync: EventBus['emitSync'];
1286
+ /** {@link EventBus.on} on {@link globalEventBus}. */
1287
+ export const on: EventBus['on'];
1288
+ /** {@link EventBus.once} on {@link globalEventBus}. */
1289
+ export const once: EventBus['once'];
1290
+ /** {@link EventBus.off} on {@link globalEventBus}. */
1291
+ export const off: EventBus['off'];
1292
+ /** {@link EventBus.registerAction} on {@link globalEventBus}. */
1293
+ export const registerAction: EventBus['registerAction'];
1294
+ /** {@link EventBus.handleAction} on {@link globalEventBus}. */
1295
+ export const handleAction: EventBus['handleAction'];
1296
+
1297
+ // ============================================================================
1298
+ // Event System Integration
1299
+ // ============================================================================
1300
+
1301
+ /** Wire a component to an event bus. */
1302
+ export function withEventBus(
1303
+ options?: Record<string, unknown>
1304
+ ): (component: CoherentComponent) => CoherentComponent;
1305
+
1306
+ /** Give a component state that updates in response to bus events. */
1307
+ export function withEventState(
1308
+ initialState?: Record<string, unknown>,
1309
+ options?: Record<string, unknown>
1310
+ ): (component: CoherentComponent) => CoherentComponent;
1311
+
1312
+ /** Factories for the common data-action handler shapes. */
1313
+ export const createActionHandlers: Record<string, (...args: never[]) => unknown>;
1314
+
1315
+ /** Factories for the common DOM event handler shapes. */
1316
+ export const createEventHandlers: Record<string, (...args: never[]) => unknown>;
1317
+
1318
+ /** Wrap a component so its declared events are wired to the bus. */
1319
+ export function createEventComponent(
1320
+ component: CoherentComponent,
1321
+ options?: Record<string, unknown>
1322
+ ): CoherentComponent;
1323
+
1324
+ /**
1325
+ * Bridges DOM events to an {@link EventBus} through delegated listeners.
1326
+ *
1327
+ * Browser-only: constructing it outside a document is inert.
1328
+ */
1329
+ export class DOMEventIntegration {
1330
+ constructor(eventBus?: EventBus, options?: Record<string, unknown>);
1331
+
1332
+ /** Attach delegated listeners */
1333
+ initialize(root?: unknown): void;
1334
+ /** Detach every listener */
1335
+ destroy(): void;
1336
+ }
1337
+
1338
+ /** Process-wide {@link DOMEventIntegration} bound to {@link globalEventBus}. */
1339
+ export const globalDOMIntegration: DOMEventIntegration;
1340
+
1341
+ /** Initialize {@link globalDOMIntegration}. */
1342
+ export function initializeDOMIntegration(options?: Record<string, unknown>): DOMEventIntegration;
1343
+
1344
+ // ============================================================================
1345
+ // State Management
1346
+ // ============================================================================
1347
+
1348
+ export interface StateManagerConfig {
1349
+ initialState?: Record<string, unknown>;
1350
+ /** Reducers combined into a root reducer, keyed by state slice */
1351
+ reducers?: Record<string, (state: unknown, action: unknown) => unknown>;
1352
+ actions?: Record<string, (...args: never[]) => unknown>;
1353
+ middleware?: Array<(...args: never[]) => unknown>;
1354
+ /** Applied to the config before the manager is built */
1355
+ plugins?: Array<(config: Record<string, unknown>) => Record<string, unknown>>;
1356
+ }
1357
+
1358
+ /** Build a reducer-based state manager from slice reducers and actions. */
1359
+ export function createStateManager(config: StateManagerConfig): Record<string, unknown>;
1360
+
1361
+ /** Presets over `withState` for the common state shapes. */
1362
+ export const withStateUtils: {
1363
+ /** Component-local state */
1364
+ local(initialState: Record<string, unknown>): ReturnType<typeof withState>;
1365
+ /** State mirrored to localStorage under `key` */
1366
+ persistent(initialState: Record<string, unknown>, key: string): ReturnType<typeof withState>;
1367
+ /** State driven by a reducer */
1368
+ reducer(
1369
+ initialState: Record<string, unknown>,
1370
+ reducer: (state: unknown, action: unknown) => unknown,
1371
+ actions?: Record<string, (...args: never[]) => unknown>
1372
+ ): ReturnType<typeof withState>;
1373
+ [preset: string]: (...args: never[]) => unknown;
1374
+ };
1375
+
1376
+ // ============================================================================
1377
+ // Composition and Utilities
1378
+ // ============================================================================
1379
+
1380
+ /** Higher-order component factories. */
1381
+ export const hoc: {
1382
+ /** Merge extra props into a component */
1383
+ withProps(additionalProps: Record<string, unknown>): (component: CoherentComponent) => CoherentComponent;
1384
+ /** Render only when `condition` holds */
1385
+ withCondition(condition: (props: Record<string, unknown>) => boolean): (component: CoherentComponent) => CoherentComponent;
1386
+ /** Show a placeholder while `props.loading` is set */
1387
+ withLoading(loadingComponent: CoherentNode): (component: CoherentComponent) => CoherentComponent;
1388
+ /** Show a fallback when `props.error` is set */
1389
+ withError(errorComponent: CoherentNode): (component: CoherentComponent) => CoherentComponent;
1390
+ /** Cache renders by a derived key */
1391
+ withMemo(getMemoKey: (props: Record<string, unknown>) => string): (component: CoherentComponent) => CoherentComponent;
1392
+ };
1393
+
1394
+ /** Functional helpers. */
1395
+ export const fp: {
1396
+ /** Curried map: `fp.map(fn)(array)` */
1397
+ map<T, R>(fn: (value: T, index: number, array: T[]) => R): (array: T[]) => R[];
1398
+ };
1399
+
1400
+ /** Cache a component's renders, keyed by `keyGenerator`. */
1401
+ export function memoize<C extends CoherentComponent>(
1402
+ component: C,
1403
+ keyGenerator?: (props: Record<string, unknown>) => string,
1404
+ options?: ComponentCacheEntryOptions
1405
+ ): C;
1406
+
1407
+ /** Mark a component as an interactive island for client-side hydration. */
1408
+ export function Island(componentFn: CoherentComponent): CoherentComponent;
1409
+
1410
+ /** Shadow DOM helpers; browser-only. */
1411
+ export const shadowDOM: {
1412
+ isShadowDOMSupported(): boolean;
1413
+ createShadowComponent(
1414
+ element: unknown,
1415
+ componentDef: CoherentNode,
1416
+ options?: Record<string, unknown>
1417
+ ): unknown;
1418
+ renderWithBestEncapsulation(componentDef: CoherentNode, containerElement?: unknown): unknown;
1419
+ };