@getforma/core 0.3.0 → 0.6.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.
Files changed (46) hide show
  1. package/README.md +201 -13
  2. package/dist/{chunk-KX5WRZH7.js → chunk-DPSAVBCP.js} +3 -4
  3. package/dist/chunk-DPSAVBCP.js.map +1 -0
  4. package/dist/{chunk-FPSLC62A.cjs → chunk-KH3F5NRU.cjs} +2 -4
  5. package/dist/chunk-KH3F5NRU.cjs.map +1 -0
  6. package/dist/{chunk-VKKPX4YU.js → chunk-TKGUHASG.js} +79 -19
  7. package/dist/chunk-TKGUHASG.js.map +1 -0
  8. package/dist/{chunk-VP5EOGM5.cjs → chunk-YSKF3VRA.cjs} +87 -26
  9. package/dist/chunk-YSKF3VRA.cjs.map +1 -0
  10. package/dist/forma-runtime-csp.js +2 -0
  11. package/dist/forma-runtime.js +2 -0
  12. package/dist/formajs-runtime-hardened.global.js +1 -1
  13. package/dist/formajs-runtime-hardened.global.js.map +1 -1
  14. package/dist/formajs-runtime.global.js +1 -1
  15. package/dist/formajs-runtime.global.js.map +1 -1
  16. package/dist/formajs.global.js +1 -1
  17. package/dist/formajs.global.js.map +1 -1
  18. package/dist/index.cjs +154 -96
  19. package/dist/index.cjs.map +1 -1
  20. package/dist/index.d.cts +83 -35
  21. package/dist/index.d.ts +83 -35
  22. package/dist/index.js +99 -40
  23. package/dist/index.js.map +1 -1
  24. package/dist/runtime-hardened.cjs +224 -60
  25. package/dist/runtime-hardened.cjs.map +1 -1
  26. package/dist/runtime-hardened.d.cts +86 -0
  27. package/dist/runtime-hardened.d.ts +86 -0
  28. package/dist/runtime-hardened.js +225 -61
  29. package/dist/runtime-hardened.js.map +1 -1
  30. package/dist/runtime.cjs +248 -83
  31. package/dist/runtime.cjs.map +1 -1
  32. package/dist/runtime.js +226 -61
  33. package/dist/runtime.js.map +1 -1
  34. package/dist/ssr/index.cjs +69 -73
  35. package/dist/ssr/index.cjs.map +1 -1
  36. package/dist/ssr/index.d.cts +4 -0
  37. package/dist/ssr/index.d.ts +4 -0
  38. package/dist/ssr/index.js +69 -73
  39. package/dist/ssr/index.js.map +1 -1
  40. package/dist/tc39-compat.cjs +3 -3
  41. package/dist/tc39-compat.js +1 -1
  42. package/package.json +24 -3
  43. package/dist/chunk-FPSLC62A.cjs.map +0 -1
  44. package/dist/chunk-KX5WRZH7.js.map +0 -1
  45. package/dist/chunk-VKKPX4YU.js.map +0 -1
  46. package/dist/chunk-VP5EOGM5.cjs.map +0 -1
package/dist/index.d.cts CHANGED
@@ -16,6 +16,11 @@ declare function createEffect(fn: () => void | (() => void)): () => void;
16
16
  /**
17
17
  * Create a lazy, cached computed value.
18
18
  *
19
+ * Note: Unlike SolidJS's createComputed (which is an eager synchronous
20
+ * side effect), this is a lazy cached derivation — equivalent to
21
+ * SolidJS's createMemo. Both createComputed and createMemo in FormaJS
22
+ * are identical.
23
+ *
19
24
  * ```ts
20
25
  * const [count, setCount] = createSignal(0);
21
26
  * const doubled = createComputed(() => count() * 2);
@@ -39,6 +44,11 @@ declare function createComputed<T>(fn: () => T): () => T;
39
44
  * Create a memoized computed value.
40
45
  * Identical to `createComputed` — provided for React/SolidJS familiarity.
41
46
  *
47
+ * Note: Unlike SolidJS's createComputed (which is an eager synchronous
48
+ * side effect), both createComputed and createMemo in FormaJS are lazy
49
+ * cached derivations — equivalent to SolidJS's createMemo. They are
50
+ * identical.
51
+ *
42
52
  * The computation runs lazily and caches the result. It only recomputes
43
53
  * when a signal it reads during computation changes.
44
54
  *
@@ -380,6 +390,7 @@ declare function cleanup(el: Element): void;
380
390
  * )
381
391
  * ```
382
392
  */
393
+ declare function h(tag: (props: Record<string, unknown>) => unknown, props?: Record<string, unknown> | null, ...children: unknown[]): Node;
383
394
  declare function h(tag: typeof Fragment, props?: null, ...children: unknown[]): DocumentFragment;
384
395
  declare function h(tag: string, props?: Record<string, unknown> | null, ...children: unknown[]): HTMLElement;
385
396
  /**
@@ -473,12 +484,6 @@ interface CreateListOptions {
473
484
  */
474
485
  updateOnItemChange?: 'none' | 'rerender';
475
486
  }
476
- /**
477
- * Find the longest increasing subsequence.
478
- * Returns indices into the input array.
479
- * O(n log n) time, O(n) space.
480
- */
481
- declare function longestIncreasingSubsequence(arr: number[]): number[];
482
487
  /**
483
488
  * Reconcile a DOM parent's children to match a new array of items.
484
489
  * Uses keyed reconciliation with LIS for minimum DOM operations.
@@ -712,8 +717,20 @@ declare function hydrateIsland(component: () => unknown, target: Element): Eleme
712
717
  * loads props (inline or script_tag), and hydrates each island inside
713
718
  * an independent createRoot scope with try/catch error isolation.
714
719
  */
715
- /** Function that creates a component's DOM tree (same for CSR and hydration). */
716
- type IslandHydrateFn = (props: Record<string, unknown> | null) => unknown;
720
+ /**
721
+ * Function that hydrates an island.
722
+ *
723
+ * @param el The root element of the island (`[data-forma-island]`).
724
+ * Useful for layout measurement, focus management, CSS class
725
+ * toggling, third-party library init, or reading extra `data-*`
726
+ * attributes from the server-rendered shell.
727
+ * @param props Parsed props from `data-forma-props` (inline or script block),
728
+ * or `null` if no props were provided.
729
+ * @returns A component tree (from `h()` calls) for descriptor-based
730
+ * hydration, or `undefined` for imperative islands that set up
731
+ * their own effects.
732
+ */
733
+ type IslandHydrateFn = (el: HTMLElement, props: Record<string, unknown> | null) => unknown;
717
734
  /**
718
735
  * Discover and activate all SSR-rendered islands on the page.
719
736
  *
@@ -723,6 +740,21 @@ type IslandHydrateFn = (props: Record<string, unknown> | null) => unknown;
723
740
  * @param registry Map of component names to hydration functions.
724
741
  */
725
742
  declare function activateIslands(registry: Record<string, IslandHydrateFn>): void;
743
+ /**
744
+ * Dispose a single island, tearing down its reactive root and all effects.
745
+ *
746
+ * Safe to call multiple times (idempotent). Sets `data-forma-status` to
747
+ * `"disposed"` so the island can be distinguished from active/error states.
748
+ */
749
+ declare function deactivateIsland(el: HTMLElement): void;
750
+ /**
751
+ * Dispose ALL active islands under a root element (or the whole document).
752
+ *
753
+ * Use this when swapping module content — e.g., replacing the contents of
754
+ * a `<forma-stage>` Shadow DOM during AI generation. Prevents leaked effects
755
+ * and event listeners from accumulating across swaps.
756
+ */
757
+ declare function deactivateAllIslands(root?: Element | Document): void;
726
758
 
727
759
  /**
728
760
  * Forma Component - Define
@@ -924,6 +956,8 @@ interface PersistOptions<T> {
924
956
  serialize?: (v: T) => string;
925
957
  /** Custom deserializer. Defaults to JSON.parse. */
926
958
  deserialize?: (s: string) => T;
959
+ /** Optional validator — return true if the deserialized value is valid. Rejects corrupt/tampered data. */
960
+ validate?: (v: unknown) => v is T;
927
961
  }
928
962
  /**
929
963
  * Persist a signal's value to storage.
@@ -940,14 +974,14 @@ interface PersistOptions<T> {
940
974
  */
941
975
  declare function persist<T>(source: [get: () => T, set: (v: T) => void], key: string, options?: PersistOptions<T>): void;
942
976
 
943
- interface EventBus<T extends Record<string, any>> {
977
+ interface EventBus<T extends Record<string, unknown>> {
944
978
  on<K extends keyof T>(event: K, handler: (payload: T[K]) => void): () => void;
945
979
  once<K extends keyof T>(event: K, handler: (payload: T[K]) => void): () => void;
946
980
  emit<K extends keyof T>(event: K, payload: T[K]): void;
947
981
  off<K extends keyof T>(event: K, handler: (payload: T[K]) => void): void;
948
982
  clear(): void;
949
983
  }
950
- declare function createBus<T extends Record<string, any> = Record<string, any>>(): EventBus<T>;
984
+ declare function createBus<T extends Record<string, unknown> = Record<string, unknown>>(): EventBus<T>;
951
985
 
952
986
  declare function delegate<K extends keyof HTMLElementEventMap>(container: HTMLElement | Document, selector: string, event: K, handler: (e: HTMLElementEventMap[K], matchedEl: HTMLElement) => void, options?: AddEventListenerOptions): () => void;
953
987
 
@@ -967,7 +1001,22 @@ declare function toggleClass(el: HTMLElement, className: string, force?: boolean
967
1001
  declare function setStyle(el: HTMLElement, styles: Partial<CSSStyleDeclaration>): void;
968
1002
  declare function setAttr(el: HTMLElement, attrs: Record<string, string | boolean | null>): void;
969
1003
  declare function setText(el: HTMLElement, text: string): void;
1004
+ /**
1005
+ * Set raw HTML on an element. **No sanitization is performed.**
1006
+ *
1007
+ * Prefer `setText()` for user-controlled content. Only use this when you
1008
+ * trust the HTML source (e.g., server-rendered markup you control).
1009
+ *
1010
+ * @deprecated Use `setHTMLUnsafe` instead — renamed to signal risk.
1011
+ */
970
1012
  declare function setHTML(el: HTMLElement, html: string): void;
1013
+ /**
1014
+ * Set raw HTML on an element. **No sanitization is performed.**
1015
+ *
1016
+ * Prefer `setText()` for user-controlled content. Only use this when you
1017
+ * trust the HTML source (e.g., server-rendered markup you control).
1018
+ */
1019
+ declare function setHTMLUnsafe(el: HTMLElement, html: string): void;
971
1020
 
972
1021
  declare function closest<T extends HTMLElement = HTMLElement>(el: HTMLElement, selector: string): T | null;
973
1022
  declare function children<T extends HTMLElement = HTMLElement>(el: HTMLElement, selector?: string): T[];
@@ -980,22 +1029,26 @@ declare function onResize(el: HTMLElement, handler: (entry: ResizeObserverEntry)
980
1029
  declare function onIntersect(el: HTMLElement, handler: (entry: IntersectionObserverEntry) => void, options?: IntersectionObserverInit): () => void;
981
1030
  declare function onMutation(el: HTMLElement, handler: (mutations: MutationRecord[]) => void, options?: MutationObserverInit): () => void;
982
1031
 
983
- /**
984
- * Forma Storage - Local
985
- *
986
- * Typed localStorage wrapper with graceful error handling.
987
- * Zero dependencies — native browser APIs only.
988
- */
989
- interface TypedStorage$1<T> {
1032
+ interface TypedStorage<T> {
990
1033
  get(): T | null;
991
1034
  set(value: T): void;
992
1035
  remove(): void;
993
1036
  key: string;
994
1037
  }
995
- interface StorageOptions$1<T> {
1038
+ interface StorageOptions<T> {
996
1039
  serialize?: (v: T) => string;
997
1040
  deserialize?: (s: string) => T;
1041
+ /** Optional validator — return true if the deserialized value is valid. */
1042
+ validate?: (v: unknown) => v is T;
998
1043
  }
1044
+
1045
+ /**
1046
+ * Forma Storage - Local
1047
+ *
1048
+ * Typed localStorage wrapper with graceful error handling.
1049
+ * Zero dependencies — native browser APIs only.
1050
+ */
1051
+
999
1052
  /**
1000
1053
  * Create a typed localStorage wrapper for the given key.
1001
1054
  *
@@ -1006,7 +1059,7 @@ interface StorageOptions$1<T> {
1006
1059
  * store.remove();
1007
1060
  * ```
1008
1061
  */
1009
- declare function createLocalStorage<T>(key: string, options?: StorageOptions$1<T>): TypedStorage$1<T>;
1062
+ declare function createLocalStorage<T>(key: string, options?: StorageOptions<T>): TypedStorage<T>;
1010
1063
 
1011
1064
  /**
1012
1065
  * Forma Storage - Session
@@ -1014,16 +1067,7 @@ declare function createLocalStorage<T>(key: string, options?: StorageOptions$1<T
1014
1067
  * Typed sessionStorage wrapper with graceful error handling.
1015
1068
  * Zero dependencies — native browser APIs only.
1016
1069
  */
1017
- interface TypedStorage<T> {
1018
- get(): T | null;
1019
- set(value: T): void;
1020
- remove(): void;
1021
- key: string;
1022
- }
1023
- interface StorageOptions<T> {
1024
- serialize?: (v: T) => string;
1025
- deserialize?: (s: string) => T;
1026
- }
1070
+
1027
1071
  /**
1028
1072
  * Create a typed sessionStorage wrapper for the given key.
1029
1073
  *
@@ -1106,9 +1150,11 @@ declare function fetchJSON<T>(url: string, options?: RequestInit): Promise<T>;
1106
1150
  * Reactive SSE wrapper with signal integration.
1107
1151
  * Zero dependencies — native browser APIs only.
1108
1152
  */
1109
- interface SSEOptions {
1153
+ interface SSEOptions<T = unknown> {
1110
1154
  withCredentials?: boolean;
1111
1155
  headers?: Record<string, string>;
1156
+ /** Custom parser for incoming messages. Defaults to JSON.parse with raw data fallback. */
1157
+ parse?: (data: string) => T;
1112
1158
  }
1113
1159
  interface SSEConnection<T = unknown> {
1114
1160
  data: () => T | null;
@@ -1128,7 +1174,7 @@ interface SSEConnection<T = unknown> {
1128
1174
  * });
1129
1175
  * ```
1130
1176
  */
1131
- declare function createSSE<T = unknown>(url: string, options?: SSEOptions): SSEConnection<T>;
1177
+ declare function createSSE<T = unknown>(url: string, options?: SSEOptions<T>): SSEConnection<T>;
1132
1178
 
1133
1179
  /**
1134
1180
  * Forma HTTP - WebSocket
@@ -1137,11 +1183,13 @@ declare function createSSE<T = unknown>(url: string, options?: SSEOptions): SSEC
1137
1183
  * Zero dependencies — native browser APIs only.
1138
1184
  */
1139
1185
  type WSStatus = 'connecting' | 'open' | 'closed' | 'error';
1140
- interface WSOptions {
1186
+ interface WSOptions<TReceive = unknown> {
1141
1187
  protocols?: string | string[];
1142
1188
  reconnect?: boolean;
1143
1189
  reconnectInterval?: number;
1144
1190
  maxReconnects?: number;
1191
+ /** Custom parser for incoming messages. Defaults to JSON.parse with raw data fallback. */
1192
+ parse?: (data: string) => TReceive;
1145
1193
  }
1146
1194
  interface WSConnection<TSend = unknown, TReceive = unknown> {
1147
1195
  data: () => TReceive | null;
@@ -1162,7 +1210,7 @@ interface WSConnection<TSend = unknown, TReceive = unknown> {
1162
1210
  * });
1163
1211
  * ```
1164
1212
  */
1165
- declare function createWebSocket<TSend = unknown, TReceive = unknown>(url: string, options?: WSOptions): WSConnection<TSend, TReceive>;
1213
+ declare function createWebSocket<TSend = unknown, TReceive = unknown>(url: string, options?: WSOptions<TReceive>): WSConnection<TSend, TReceive>;
1166
1214
 
1167
1215
  /**
1168
1216
  * FormaJS Server - Action
@@ -1327,7 +1375,7 @@ declare function withRevalidation<T>(data: T, revalidate: Record<string, unknown
1327
1375
  * @param endpoint - The RPC endpoint path (e.g. "/rpc/createTodo_a1b2c3")
1328
1376
  * @returns An async function that sends args to the server and returns the result
1329
1377
  */
1330
- declare function $$serverFunction<T extends (...args: any[]) => Promise<any>>(endpoint: string): T;
1378
+ declare function $$serverFunction<T extends (...args: unknown[]) => Promise<unknown>>(endpoint: string): T;
1331
1379
 
1332
1380
  /**
1333
1381
  * FormaJS Server - RPC Handler
@@ -1391,4 +1439,4 @@ declare function renderLocal(slotsJson: string): Promise<string>;
1391
1439
  /** Fragment render (single island) via WASM. */
1392
1440
  declare function renderIsland(slotsJson: string, islandId: number): Promise<string>;
1393
1441
 
1394
- export { $, $$, $$serverFunction, type Action, type ActionOptions, Fragment, type IslandHydrateFn, type MutationResponse, type RPCRequest, type RPCResponse, activateIslands, addClass, applyRevalidation, batch, children, cleanup, closest, createAction, createBus, createComputed, createContext, createEffect, createErrorBoundary, createFetch, createHistory, createIndexedDB, createList, createLocalStorage, createMemo, createPortal, createRPCMiddleware, createReducer, createRef, createResource, createRoot, createSSE, createSessionStorage, createShow, createStore, createSuspense, createSwitch, createText, createWebSocket, defineComponent, delegate, disposeComponent, enableAutoRevalidation, fetchJSON, fragment, getRegisteredEndpoints, getServerFunction, h, handleRPC, hydrateIsland, inject, longestIncreasingSubsequence, mount, nextSibling, on, onCleanup, onError, onIntersect, onKey, onMount, onMutation, onResize, onUnmount, parent, persist, prevSibling, provide, reconcileList, registerResource, registerServerFunction, removeClass, renderIsland, renderLocal, setAttr, setHTML, setStyle, setText, siblings, toggleClass, trackDisposer, unprovide, unregisterResource, untrack, withRevalidation };
1442
+ export { $, $$, $$serverFunction, type Action, type ActionOptions, Fragment, type IslandHydrateFn, type MutationResponse, type RPCRequest, type RPCResponse, activateIslands, addClass, applyRevalidation, batch, children, cleanup, closest, createAction, createBus, createComputed, createContext, createEffect, createErrorBoundary, createFetch, createHistory, createIndexedDB, createList, createLocalStorage, createMemo, createPortal, createRPCMiddleware, createReducer, createRef, createResource, createRoot, createSSE, createSessionStorage, createShow, createStore, createSuspense, createSwitch, createText, createWebSocket, deactivateAllIslands, deactivateIsland, defineComponent, delegate, disposeComponent, enableAutoRevalidation, fetchJSON, fragment, getRegisteredEndpoints, getServerFunction, h, handleRPC, hydrateIsland, inject, mount, nextSibling, on, onCleanup, onError, onIntersect, onKey, onMount, onMutation, onResize, onUnmount, parent, persist, prevSibling, provide, reconcileList, registerResource, registerServerFunction, removeClass, renderIsland, renderLocal, setAttr, setHTML, setHTMLUnsafe, setStyle, setText, siblings, toggleClass, trackDisposer, unprovide, unregisterResource, untrack, withRevalidation };
package/dist/index.d.ts CHANGED
@@ -16,6 +16,11 @@ declare function createEffect(fn: () => void | (() => void)): () => void;
16
16
  /**
17
17
  * Create a lazy, cached computed value.
18
18
  *
19
+ * Note: Unlike SolidJS's createComputed (which is an eager synchronous
20
+ * side effect), this is a lazy cached derivation — equivalent to
21
+ * SolidJS's createMemo. Both createComputed and createMemo in FormaJS
22
+ * are identical.
23
+ *
19
24
  * ```ts
20
25
  * const [count, setCount] = createSignal(0);
21
26
  * const doubled = createComputed(() => count() * 2);
@@ -39,6 +44,11 @@ declare function createComputed<T>(fn: () => T): () => T;
39
44
  * Create a memoized computed value.
40
45
  * Identical to `createComputed` — provided for React/SolidJS familiarity.
41
46
  *
47
+ * Note: Unlike SolidJS's createComputed (which is an eager synchronous
48
+ * side effect), both createComputed and createMemo in FormaJS are lazy
49
+ * cached derivations — equivalent to SolidJS's createMemo. They are
50
+ * identical.
51
+ *
42
52
  * The computation runs lazily and caches the result. It only recomputes
43
53
  * when a signal it reads during computation changes.
44
54
  *
@@ -380,6 +390,7 @@ declare function cleanup(el: Element): void;
380
390
  * )
381
391
  * ```
382
392
  */
393
+ declare function h(tag: (props: Record<string, unknown>) => unknown, props?: Record<string, unknown> | null, ...children: unknown[]): Node;
383
394
  declare function h(tag: typeof Fragment, props?: null, ...children: unknown[]): DocumentFragment;
384
395
  declare function h(tag: string, props?: Record<string, unknown> | null, ...children: unknown[]): HTMLElement;
385
396
  /**
@@ -473,12 +484,6 @@ interface CreateListOptions {
473
484
  */
474
485
  updateOnItemChange?: 'none' | 'rerender';
475
486
  }
476
- /**
477
- * Find the longest increasing subsequence.
478
- * Returns indices into the input array.
479
- * O(n log n) time, O(n) space.
480
- */
481
- declare function longestIncreasingSubsequence(arr: number[]): number[];
482
487
  /**
483
488
  * Reconcile a DOM parent's children to match a new array of items.
484
489
  * Uses keyed reconciliation with LIS for minimum DOM operations.
@@ -712,8 +717,20 @@ declare function hydrateIsland(component: () => unknown, target: Element): Eleme
712
717
  * loads props (inline or script_tag), and hydrates each island inside
713
718
  * an independent createRoot scope with try/catch error isolation.
714
719
  */
715
- /** Function that creates a component's DOM tree (same for CSR and hydration). */
716
- type IslandHydrateFn = (props: Record<string, unknown> | null) => unknown;
720
+ /**
721
+ * Function that hydrates an island.
722
+ *
723
+ * @param el The root element of the island (`[data-forma-island]`).
724
+ * Useful for layout measurement, focus management, CSS class
725
+ * toggling, third-party library init, or reading extra `data-*`
726
+ * attributes from the server-rendered shell.
727
+ * @param props Parsed props from `data-forma-props` (inline or script block),
728
+ * or `null` if no props were provided.
729
+ * @returns A component tree (from `h()` calls) for descriptor-based
730
+ * hydration, or `undefined` for imperative islands that set up
731
+ * their own effects.
732
+ */
733
+ type IslandHydrateFn = (el: HTMLElement, props: Record<string, unknown> | null) => unknown;
717
734
  /**
718
735
  * Discover and activate all SSR-rendered islands on the page.
719
736
  *
@@ -723,6 +740,21 @@ type IslandHydrateFn = (props: Record<string, unknown> | null) => unknown;
723
740
  * @param registry Map of component names to hydration functions.
724
741
  */
725
742
  declare function activateIslands(registry: Record<string, IslandHydrateFn>): void;
743
+ /**
744
+ * Dispose a single island, tearing down its reactive root and all effects.
745
+ *
746
+ * Safe to call multiple times (idempotent). Sets `data-forma-status` to
747
+ * `"disposed"` so the island can be distinguished from active/error states.
748
+ */
749
+ declare function deactivateIsland(el: HTMLElement): void;
750
+ /**
751
+ * Dispose ALL active islands under a root element (or the whole document).
752
+ *
753
+ * Use this when swapping module content — e.g., replacing the contents of
754
+ * a `<forma-stage>` Shadow DOM during AI generation. Prevents leaked effects
755
+ * and event listeners from accumulating across swaps.
756
+ */
757
+ declare function deactivateAllIslands(root?: Element | Document): void;
726
758
 
727
759
  /**
728
760
  * Forma Component - Define
@@ -924,6 +956,8 @@ interface PersistOptions<T> {
924
956
  serialize?: (v: T) => string;
925
957
  /** Custom deserializer. Defaults to JSON.parse. */
926
958
  deserialize?: (s: string) => T;
959
+ /** Optional validator — return true if the deserialized value is valid. Rejects corrupt/tampered data. */
960
+ validate?: (v: unknown) => v is T;
927
961
  }
928
962
  /**
929
963
  * Persist a signal's value to storage.
@@ -940,14 +974,14 @@ interface PersistOptions<T> {
940
974
  */
941
975
  declare function persist<T>(source: [get: () => T, set: (v: T) => void], key: string, options?: PersistOptions<T>): void;
942
976
 
943
- interface EventBus<T extends Record<string, any>> {
977
+ interface EventBus<T extends Record<string, unknown>> {
944
978
  on<K extends keyof T>(event: K, handler: (payload: T[K]) => void): () => void;
945
979
  once<K extends keyof T>(event: K, handler: (payload: T[K]) => void): () => void;
946
980
  emit<K extends keyof T>(event: K, payload: T[K]): void;
947
981
  off<K extends keyof T>(event: K, handler: (payload: T[K]) => void): void;
948
982
  clear(): void;
949
983
  }
950
- declare function createBus<T extends Record<string, any> = Record<string, any>>(): EventBus<T>;
984
+ declare function createBus<T extends Record<string, unknown> = Record<string, unknown>>(): EventBus<T>;
951
985
 
952
986
  declare function delegate<K extends keyof HTMLElementEventMap>(container: HTMLElement | Document, selector: string, event: K, handler: (e: HTMLElementEventMap[K], matchedEl: HTMLElement) => void, options?: AddEventListenerOptions): () => void;
953
987
 
@@ -967,7 +1001,22 @@ declare function toggleClass(el: HTMLElement, className: string, force?: boolean
967
1001
  declare function setStyle(el: HTMLElement, styles: Partial<CSSStyleDeclaration>): void;
968
1002
  declare function setAttr(el: HTMLElement, attrs: Record<string, string | boolean | null>): void;
969
1003
  declare function setText(el: HTMLElement, text: string): void;
1004
+ /**
1005
+ * Set raw HTML on an element. **No sanitization is performed.**
1006
+ *
1007
+ * Prefer `setText()` for user-controlled content. Only use this when you
1008
+ * trust the HTML source (e.g., server-rendered markup you control).
1009
+ *
1010
+ * @deprecated Use `setHTMLUnsafe` instead — renamed to signal risk.
1011
+ */
970
1012
  declare function setHTML(el: HTMLElement, html: string): void;
1013
+ /**
1014
+ * Set raw HTML on an element. **No sanitization is performed.**
1015
+ *
1016
+ * Prefer `setText()` for user-controlled content. Only use this when you
1017
+ * trust the HTML source (e.g., server-rendered markup you control).
1018
+ */
1019
+ declare function setHTMLUnsafe(el: HTMLElement, html: string): void;
971
1020
 
972
1021
  declare function closest<T extends HTMLElement = HTMLElement>(el: HTMLElement, selector: string): T | null;
973
1022
  declare function children<T extends HTMLElement = HTMLElement>(el: HTMLElement, selector?: string): T[];
@@ -980,22 +1029,26 @@ declare function onResize(el: HTMLElement, handler: (entry: ResizeObserverEntry)
980
1029
  declare function onIntersect(el: HTMLElement, handler: (entry: IntersectionObserverEntry) => void, options?: IntersectionObserverInit): () => void;
981
1030
  declare function onMutation(el: HTMLElement, handler: (mutations: MutationRecord[]) => void, options?: MutationObserverInit): () => void;
982
1031
 
983
- /**
984
- * Forma Storage - Local
985
- *
986
- * Typed localStorage wrapper with graceful error handling.
987
- * Zero dependencies — native browser APIs only.
988
- */
989
- interface TypedStorage$1<T> {
1032
+ interface TypedStorage<T> {
990
1033
  get(): T | null;
991
1034
  set(value: T): void;
992
1035
  remove(): void;
993
1036
  key: string;
994
1037
  }
995
- interface StorageOptions$1<T> {
1038
+ interface StorageOptions<T> {
996
1039
  serialize?: (v: T) => string;
997
1040
  deserialize?: (s: string) => T;
1041
+ /** Optional validator — return true if the deserialized value is valid. */
1042
+ validate?: (v: unknown) => v is T;
998
1043
  }
1044
+
1045
+ /**
1046
+ * Forma Storage - Local
1047
+ *
1048
+ * Typed localStorage wrapper with graceful error handling.
1049
+ * Zero dependencies — native browser APIs only.
1050
+ */
1051
+
999
1052
  /**
1000
1053
  * Create a typed localStorage wrapper for the given key.
1001
1054
  *
@@ -1006,7 +1059,7 @@ interface StorageOptions$1<T> {
1006
1059
  * store.remove();
1007
1060
  * ```
1008
1061
  */
1009
- declare function createLocalStorage<T>(key: string, options?: StorageOptions$1<T>): TypedStorage$1<T>;
1062
+ declare function createLocalStorage<T>(key: string, options?: StorageOptions<T>): TypedStorage<T>;
1010
1063
 
1011
1064
  /**
1012
1065
  * Forma Storage - Session
@@ -1014,16 +1067,7 @@ declare function createLocalStorage<T>(key: string, options?: StorageOptions$1<T
1014
1067
  * Typed sessionStorage wrapper with graceful error handling.
1015
1068
  * Zero dependencies — native browser APIs only.
1016
1069
  */
1017
- interface TypedStorage<T> {
1018
- get(): T | null;
1019
- set(value: T): void;
1020
- remove(): void;
1021
- key: string;
1022
- }
1023
- interface StorageOptions<T> {
1024
- serialize?: (v: T) => string;
1025
- deserialize?: (s: string) => T;
1026
- }
1070
+
1027
1071
  /**
1028
1072
  * Create a typed sessionStorage wrapper for the given key.
1029
1073
  *
@@ -1106,9 +1150,11 @@ declare function fetchJSON<T>(url: string, options?: RequestInit): Promise<T>;
1106
1150
  * Reactive SSE wrapper with signal integration.
1107
1151
  * Zero dependencies — native browser APIs only.
1108
1152
  */
1109
- interface SSEOptions {
1153
+ interface SSEOptions<T = unknown> {
1110
1154
  withCredentials?: boolean;
1111
1155
  headers?: Record<string, string>;
1156
+ /** Custom parser for incoming messages. Defaults to JSON.parse with raw data fallback. */
1157
+ parse?: (data: string) => T;
1112
1158
  }
1113
1159
  interface SSEConnection<T = unknown> {
1114
1160
  data: () => T | null;
@@ -1128,7 +1174,7 @@ interface SSEConnection<T = unknown> {
1128
1174
  * });
1129
1175
  * ```
1130
1176
  */
1131
- declare function createSSE<T = unknown>(url: string, options?: SSEOptions): SSEConnection<T>;
1177
+ declare function createSSE<T = unknown>(url: string, options?: SSEOptions<T>): SSEConnection<T>;
1132
1178
 
1133
1179
  /**
1134
1180
  * Forma HTTP - WebSocket
@@ -1137,11 +1183,13 @@ declare function createSSE<T = unknown>(url: string, options?: SSEOptions): SSEC
1137
1183
  * Zero dependencies — native browser APIs only.
1138
1184
  */
1139
1185
  type WSStatus = 'connecting' | 'open' | 'closed' | 'error';
1140
- interface WSOptions {
1186
+ interface WSOptions<TReceive = unknown> {
1141
1187
  protocols?: string | string[];
1142
1188
  reconnect?: boolean;
1143
1189
  reconnectInterval?: number;
1144
1190
  maxReconnects?: number;
1191
+ /** Custom parser for incoming messages. Defaults to JSON.parse with raw data fallback. */
1192
+ parse?: (data: string) => TReceive;
1145
1193
  }
1146
1194
  interface WSConnection<TSend = unknown, TReceive = unknown> {
1147
1195
  data: () => TReceive | null;
@@ -1162,7 +1210,7 @@ interface WSConnection<TSend = unknown, TReceive = unknown> {
1162
1210
  * });
1163
1211
  * ```
1164
1212
  */
1165
- declare function createWebSocket<TSend = unknown, TReceive = unknown>(url: string, options?: WSOptions): WSConnection<TSend, TReceive>;
1213
+ declare function createWebSocket<TSend = unknown, TReceive = unknown>(url: string, options?: WSOptions<TReceive>): WSConnection<TSend, TReceive>;
1166
1214
 
1167
1215
  /**
1168
1216
  * FormaJS Server - Action
@@ -1327,7 +1375,7 @@ declare function withRevalidation<T>(data: T, revalidate: Record<string, unknown
1327
1375
  * @param endpoint - The RPC endpoint path (e.g. "/rpc/createTodo_a1b2c3")
1328
1376
  * @returns An async function that sends args to the server and returns the result
1329
1377
  */
1330
- declare function $$serverFunction<T extends (...args: any[]) => Promise<any>>(endpoint: string): T;
1378
+ declare function $$serverFunction<T extends (...args: unknown[]) => Promise<unknown>>(endpoint: string): T;
1331
1379
 
1332
1380
  /**
1333
1381
  * FormaJS Server - RPC Handler
@@ -1391,4 +1439,4 @@ declare function renderLocal(slotsJson: string): Promise<string>;
1391
1439
  /** Fragment render (single island) via WASM. */
1392
1440
  declare function renderIsland(slotsJson: string, islandId: number): Promise<string>;
1393
1441
 
1394
- export { $, $$, $$serverFunction, type Action, type ActionOptions, Fragment, type IslandHydrateFn, type MutationResponse, type RPCRequest, type RPCResponse, activateIslands, addClass, applyRevalidation, batch, children, cleanup, closest, createAction, createBus, createComputed, createContext, createEffect, createErrorBoundary, createFetch, createHistory, createIndexedDB, createList, createLocalStorage, createMemo, createPortal, createRPCMiddleware, createReducer, createRef, createResource, createRoot, createSSE, createSessionStorage, createShow, createStore, createSuspense, createSwitch, createText, createWebSocket, defineComponent, delegate, disposeComponent, enableAutoRevalidation, fetchJSON, fragment, getRegisteredEndpoints, getServerFunction, h, handleRPC, hydrateIsland, inject, longestIncreasingSubsequence, mount, nextSibling, on, onCleanup, onError, onIntersect, onKey, onMount, onMutation, onResize, onUnmount, parent, persist, prevSibling, provide, reconcileList, registerResource, registerServerFunction, removeClass, renderIsland, renderLocal, setAttr, setHTML, setStyle, setText, siblings, toggleClass, trackDisposer, unprovide, unregisterResource, untrack, withRevalidation };
1442
+ export { $, $$, $$serverFunction, type Action, type ActionOptions, Fragment, type IslandHydrateFn, type MutationResponse, type RPCRequest, type RPCResponse, activateIslands, addClass, applyRevalidation, batch, children, cleanup, closest, createAction, createBus, createComputed, createContext, createEffect, createErrorBoundary, createFetch, createHistory, createIndexedDB, createList, createLocalStorage, createMemo, createPortal, createRPCMiddleware, createReducer, createRef, createResource, createRoot, createSSE, createSessionStorage, createShow, createStore, createSuspense, createSwitch, createText, createWebSocket, deactivateAllIslands, deactivateIsland, defineComponent, delegate, disposeComponent, enableAutoRevalidation, fetchJSON, fragment, getRegisteredEndpoints, getServerFunction, h, handleRPC, hydrateIsland, inject, mount, nextSibling, on, onCleanup, onError, onIntersect, onKey, onMount, onMutation, onResize, onUnmount, parent, persist, prevSibling, provide, reconcileList, registerResource, registerServerFunction, removeClass, renderIsland, renderLocal, setAttr, setHTML, setHTMLUnsafe, setStyle, setText, siblings, toggleClass, trackDisposer, unprovide, unregisterResource, untrack, withRevalidation };