fibrae 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. package/dist/components.d.ts +40 -0
  2. package/dist/components.js +63 -0
  3. package/dist/components.js.map +1 -0
  4. package/dist/core.d.ts +25 -0
  5. package/dist/core.js +46 -0
  6. package/dist/core.js.map +1 -0
  7. package/dist/dom.d.ts +16 -0
  8. package/dist/dom.js +67 -0
  9. package/dist/dom.js.map +1 -0
  10. package/dist/fiber-render.d.ts +33 -0
  11. package/dist/fiber-render.js +1069 -0
  12. package/dist/fiber-render.js.map +1 -0
  13. package/dist/h.d.ts +19 -0
  14. package/dist/h.js +26 -0
  15. package/dist/h.js.map +1 -0
  16. package/dist/hydration.d.ts +30 -0
  17. package/dist/hydration.js +375 -0
  18. package/dist/hydration.js.map +1 -0
  19. package/dist/index.d.ts +26 -0
  20. package/dist/index.js +28 -0
  21. package/dist/index.js.map +1 -0
  22. package/dist/jsx-runtime/index.d.ts +29 -0
  23. package/dist/jsx-runtime/index.js +61 -0
  24. package/dist/jsx-runtime/index.js.map +1 -0
  25. package/dist/render.d.ts +19 -0
  26. package/dist/render.js +325 -0
  27. package/dist/render.js.map +1 -0
  28. package/dist/router/History.d.ts +129 -0
  29. package/dist/router/History.js +241 -0
  30. package/dist/router/History.js.map +1 -0
  31. package/dist/router/Link.d.ts +52 -0
  32. package/dist/router/Link.js +131 -0
  33. package/dist/router/Link.js.map +1 -0
  34. package/dist/router/Navigator.d.ts +108 -0
  35. package/dist/router/Navigator.js +225 -0
  36. package/dist/router/Navigator.js.map +1 -0
  37. package/dist/router/Route.d.ts +65 -0
  38. package/dist/router/Route.js +143 -0
  39. package/dist/router/Route.js.map +1 -0
  40. package/dist/router/Router.d.ts +167 -0
  41. package/dist/router/Router.js +328 -0
  42. package/dist/router/Router.js.map +1 -0
  43. package/dist/router/RouterBuilder.d.ts +128 -0
  44. package/dist/router/RouterBuilder.js +112 -0
  45. package/dist/router/RouterBuilder.js.map +1 -0
  46. package/dist/router/RouterOutlet.d.ts +57 -0
  47. package/dist/router/RouterOutlet.js +132 -0
  48. package/dist/router/RouterOutlet.js.map +1 -0
  49. package/dist/router/RouterState.d.ts +102 -0
  50. package/dist/router/RouterState.js +94 -0
  51. package/dist/router/RouterState.js.map +1 -0
  52. package/dist/router/index.d.ts +28 -0
  53. package/dist/router/index.js +31 -0
  54. package/dist/router/index.js.map +1 -0
  55. package/dist/runtime.d.ts +55 -0
  56. package/dist/runtime.js +68 -0
  57. package/dist/runtime.js.map +1 -0
  58. package/dist/scope-utils.d.ts +14 -0
  59. package/dist/scope-utils.js +29 -0
  60. package/dist/scope-utils.js.map +1 -0
  61. package/dist/server.d.ts +112 -0
  62. package/dist/server.js +313 -0
  63. package/dist/server.js.map +1 -0
  64. package/dist/shared.d.ts +136 -0
  65. package/dist/shared.js +53 -0
  66. package/dist/shared.js.map +1 -0
  67. package/dist/tracking.d.ts +23 -0
  68. package/dist/tracking.js +53 -0
  69. package/dist/tracking.js.map +1 -0
  70. package/package.json +62 -0
@@ -0,0 +1,112 @@
1
+ /**
2
+ * Server-side rendering for Fibrae
3
+ *
4
+ * Renders VElement trees to HTML strings for SSR.
5
+ * Integrates with @effect-atom/atom's Hydration module for state serialization.
6
+ *
7
+ * Key design decisions (see docs/ssr-hydration-design.md):
8
+ * - Streams restart on client (no server continuation)
9
+ * - Atoms must use Atom.serializable() for state transfer
10
+ * - Same components work on server and client
11
+ */
12
+ import * as Effect from "effect/Effect";
13
+ import { Atom, Registry as AtomRegistry, Hydration } from "@effect-atom/atom";
14
+ import { type VElement } from "./shared.js";
15
+ /**
16
+ * Result of renderToString - HTML plus serialized state
17
+ *
18
+ * The dehydratedState can be serialized and embedded in your HTML however
19
+ * you prefer (script tag, data attribute, separate endpoint, etc.).
20
+ * On the client, pass this state to render() via the initialState option.
21
+ */
22
+ export interface RenderResult {
23
+ readonly html: string;
24
+ readonly dehydratedState: ReadonlyArray<Hydration.DehydratedAtom>;
25
+ }
26
+ /**
27
+ * Options for SSR rendering
28
+ */
29
+ export interface RenderOptions {
30
+ /**
31
+ * Initial atom values to use during rendering.
32
+ * These will be set on the registry before rendering begins.
33
+ */
34
+ readonly initialValues?: Iterable<readonly [Atom.Atom<unknown>, unknown]>;
35
+ }
36
+ /**
37
+ * Create a synchronous AtomRegistry layer for SSR.
38
+ * Uses synchronous task scheduling since we're not in a browser.
39
+ */
40
+ declare const SSRAtomRegistryLayer: import("effect/Layer").Layer<AtomRegistry.AtomRegistry, never, never>;
41
+ /**
42
+ * Exported for SSR scenarios that need to compose with other layers.
43
+ * Use this when you need to provide additional services (e.g., Navigator, RouterHandlers)
44
+ * alongside the AtomRegistry.
45
+ */
46
+ export { SSRAtomRegistryLayer };
47
+ /**
48
+ * Render a VElement tree to an HTML string with serialized state.
49
+ *
50
+ * Returns the HTML and the dehydrated atom state array. You can serialize
51
+ * and embed the state in your HTML however you prefer (script tag, data
52
+ * attribute, separate endpoint, etc.).
53
+ *
54
+ * On the client, pass this state to render() via the initialState option
55
+ * to hydrate atom values before DOM hydration.
56
+ *
57
+ * Note: Atoms must use `Atom.serializable({ key, schema })` to be included
58
+ * in the dehydrated state.
59
+ *
60
+ * @example
61
+ * ```typescript
62
+ * import { renderToString } from "fibrae/server";
63
+ *
64
+ * const program = Effect.gen(function* () {
65
+ * const { html, dehydratedState } = yield* renderToString(<App />);
66
+ *
67
+ * // Embed state however you prefer
68
+ * const page = `
69
+ * <!DOCTYPE html>
70
+ * <html>
71
+ * <body>
72
+ * <div id="root">${html}</div>
73
+ * <script>window.__STATE__ = ${JSON.stringify(dehydratedState)}</script>
74
+ * <script src="/client.js"></script>
75
+ * </body>
76
+ * </html>
77
+ * `;
78
+ * return page;
79
+ * });
80
+ * ```
81
+ */
82
+ export declare const renderToString: (element: VElement, _options?: RenderOptions) => Effect.Effect<RenderResult, unknown, never>;
83
+ /**
84
+ * Render a VElement tree to HTML, requiring AtomRegistry and any other
85
+ * services the component tree needs.
86
+ *
87
+ * Use this when your components require additional services (Navigator, RouterHandlers, etc.)
88
+ * that you want to provide yourself.
89
+ *
90
+ * @example
91
+ * ```typescript
92
+ * import { renderToStringWith, SSRAtomRegistryLayer } from "fibrae/server";
93
+ *
94
+ * const program = Effect.gen(function* () {
95
+ * const { html, dehydratedState } = yield* renderToStringWith(<App />);
96
+ * return { html, dehydratedState };
97
+ * });
98
+ *
99
+ * // Run with composed layers
100
+ * const result = Effect.runPromise(
101
+ * program.pipe(
102
+ * Effect.provide(Layer.mergeAll(
103
+ * SSRAtomRegistryLayer,
104
+ * navigatorLayer,
105
+ * routerHandlersLayer
106
+ * ))
107
+ * )
108
+ * );
109
+ * ```
110
+ */
111
+ export declare const renderToStringWith: <R>(element: VElement, _options?: RenderOptions) => Effect.Effect<RenderResult, unknown, AtomRegistry.AtomRegistry | R>;
112
+ export { Hydration };
package/dist/server.js ADDED
@@ -0,0 +1,313 @@
1
+ /**
2
+ * Server-side rendering for Fibrae
3
+ *
4
+ * Renders VElement trees to HTML strings for SSR.
5
+ * Integrates with @effect-atom/atom's Hydration module for state serialization.
6
+ *
7
+ * Key design decisions (see docs/ssr-hydration-design.md):
8
+ * - Streams restart on client (no server continuation)
9
+ * - Atoms must use Atom.serializable() for state transfer
10
+ * - Same components work on server and client
11
+ */
12
+ import * as Effect from "effect/Effect";
13
+ import * as Stream from "effect/Stream";
14
+ import * as Option from "effect/Option";
15
+ import * as Deferred from "effect/Deferred";
16
+ import { Atom, Registry as AtomRegistry, Hydration, } from "@effect-atom/atom";
17
+ import { isStream, isProperty, } from "./shared.js";
18
+ // =============================================================================
19
+ // SSR Registry Layer
20
+ // =============================================================================
21
+ /**
22
+ * Create a synchronous AtomRegistry layer for SSR.
23
+ * Uses synchronous task scheduling since we're not in a browser.
24
+ */
25
+ const SSRAtomRegistryLayer = AtomRegistry.layerOptions({
26
+ scheduleTask: (f) => f(),
27
+ });
28
+ /**
29
+ * Exported for SSR scenarios that need to compose with other layers.
30
+ * Use this when you need to provide additional services (e.g., Navigator, RouterHandlers)
31
+ * alongside the AtomRegistry.
32
+ */
33
+ export { SSRAtomRegistryLayer };
34
+ // =============================================================================
35
+ // HTML Escaping
36
+ // =============================================================================
37
+ const escapeHtml = (str) => {
38
+ return str
39
+ .replace(/&/g, "&amp;")
40
+ .replace(/</g, "&lt;")
41
+ .replace(/>/g, "&gt;")
42
+ .replace(/"/g, "&quot;")
43
+ .replace(/'/g, "&#039;");
44
+ };
45
+ // =============================================================================
46
+ // Attribute Rendering
47
+ // =============================================================================
48
+ /**
49
+ * Convert a prop name to its HTML attribute name
50
+ */
51
+ const propToAttr = (prop) => {
52
+ // Handle className -> class
53
+ if (prop === "className")
54
+ return "class";
55
+ // Handle htmlFor -> for
56
+ if (prop === "htmlFor")
57
+ return "for";
58
+ // Convert camelCase to kebab-case for data-* and aria-*
59
+ if (prop.startsWith("data") || prop.startsWith("aria")) {
60
+ return prop.replace(/([A-Z])/g, "-$1").toLowerCase();
61
+ }
62
+ return prop;
63
+ };
64
+ /**
65
+ * Render a single attribute to string
66
+ */
67
+ const renderAttribute = (name, value) => {
68
+ if (value === true) {
69
+ return ` ${name}`;
70
+ }
71
+ if (value === false || value === null || value === undefined) {
72
+ return "";
73
+ }
74
+ return ` ${name}="${escapeHtml(String(value))}"`;
75
+ };
76
+ /**
77
+ * Render all props as HTML attributes
78
+ */
79
+ const renderAttributes = (props) => {
80
+ let attrs = "";
81
+ for (const [key, value] of Object.entries(props)) {
82
+ if (isProperty(key)) {
83
+ attrs += renderAttribute(propToAttr(key), value);
84
+ }
85
+ }
86
+ return attrs;
87
+ };
88
+ // =============================================================================
89
+ // Void Elements (self-closing)
90
+ // =============================================================================
91
+ const VOID_ELEMENTS = new Set([
92
+ "area", "base", "br", "col", "embed", "hr", "img", "input",
93
+ "link", "meta", "param", "source", "track", "wbr",
94
+ ]);
95
+ // =============================================================================
96
+ // Core Rendering
97
+ // =============================================================================
98
+ /**
99
+ * Check if a type is a function component
100
+ */
101
+ const isFunctionComponent = (type) => typeof type === "function";
102
+ /**
103
+ * Check if a type is a host element (string tag)
104
+ */
105
+ const isHostElement = (type) => typeof type === "string";
106
+ /**
107
+ * Render a VElement to HTML string.
108
+ * This is an Effect that requires AtomRegistry.
109
+ */
110
+ const renderVElementToString = (vElement) => Effect.gen(function* () {
111
+ const type = vElement.type;
112
+ if (isFunctionComponent(type)) {
113
+ // Invoke the component, catching synchronous throws
114
+ const outputEffect = Effect.try({
115
+ try: () => type(vElement.props),
116
+ catch: (e) => e,
117
+ });
118
+ const output = yield* outputEffect;
119
+ // Normalize to get the VElement
120
+ let childVElement;
121
+ if (isStream(output)) {
122
+ // For streams, take first emission
123
+ const first = yield* Stream.runHead(output);
124
+ if (Option.isNone(first)) {
125
+ return ""; // Empty stream
126
+ }
127
+ childVElement = first.value;
128
+ }
129
+ else if (Effect.isEffect(output)) {
130
+ // Await the effect
131
+ childVElement = yield* output;
132
+ }
133
+ else {
134
+ // Plain VElement
135
+ childVElement = output;
136
+ }
137
+ // Recursively render the result
138
+ return yield* renderVElementToString(childVElement);
139
+ }
140
+ else if (type === "TEXT_ELEMENT") {
141
+ // Text node - escape and return
142
+ return escapeHtml(String(vElement.props.nodeValue ?? ""));
143
+ }
144
+ else if (type === "FRAGMENT") {
145
+ // Fragment - render children directly
146
+ const children = vElement.props.children ?? [];
147
+ let html = "";
148
+ for (const child of children) {
149
+ html += yield* renderVElementToString(child);
150
+ }
151
+ return html;
152
+ }
153
+ else if (type === "ERROR_BOUNDARY") {
154
+ // Error boundary - try to render children, catch errors and render fallback
155
+ const fallback = vElement.props.fallback;
156
+ const children = vElement.props.children;
157
+ const result = yield* Effect.either(Effect.gen(function* () {
158
+ let html = "";
159
+ for (const child of children) {
160
+ html += yield* renderVElementToString(child);
161
+ }
162
+ return html;
163
+ }));
164
+ if (result._tag === "Left") {
165
+ // Error occurred - render fallback
166
+ const onError = vElement.props.onError;
167
+ onError?.(result.left);
168
+ return yield* renderVElementToString(fallback);
169
+ }
170
+ return result.right;
171
+ }
172
+ else if (type === "SUSPENSE") {
173
+ // Suspense boundary - race child rendering against timeout
174
+ // Phase 5: If children complete first → resolved marker
175
+ // If timeout fires first → fallback marker
176
+ const fallback = vElement.props.fallback;
177
+ const threshold = vElement.props.threshold ?? 100;
178
+ const children = vElement.props.children;
179
+ // Create a Deferred to signal when children complete
180
+ const childrenComplete = yield* Deferred.make();
181
+ // Fork: render children to string
182
+ yield* Effect.fork(Effect.gen(function* () {
183
+ let childrenHtml = "";
184
+ for (const child of children) {
185
+ childrenHtml += yield* renderVElementToString(child);
186
+ }
187
+ yield* Deferred.succeed(childrenComplete, childrenHtml);
188
+ }).pipe(Effect.catchAll((e) => Deferred.fail(childrenComplete, e))));
189
+ // Race: children completing vs timeout
190
+ const result = yield* Effect.race(Deferred.await(childrenComplete).pipe(Effect.map((html) => ({ type: "resolved", html }))), Effect.sleep(`${threshold} millis`).pipe(Effect.as({ type: "timeout" })));
191
+ if (result.type === "resolved") {
192
+ // Children completed before timeout - render with resolved marker
193
+ return `<!--fibrae:sus:resolved-->${result.html}<!--/fibrae:sus-->`;
194
+ }
195
+ else {
196
+ // Timeout fired first - render fallback with fallback marker
197
+ const fallbackHtml = yield* renderVElementToString(fallback);
198
+ return `<!--fibrae:sus:fallback-->${fallbackHtml}<!--/fibrae:sus-->`;
199
+ }
200
+ }
201
+ else if (isHostElement(type)) {
202
+ // Regular HTML element
203
+ const attrs = renderAttributes(vElement.props);
204
+ // Add data-key for keyed elements (needed for hydration)
205
+ const key = vElement.props.key;
206
+ const keyAttr = key != null ? ` data-key="${escapeHtml(String(key))}"` : "";
207
+ if (VOID_ELEMENTS.has(type)) {
208
+ return `<${type}${attrs}${keyAttr} />`;
209
+ }
210
+ // Render children with text node markers between adjacent text nodes
211
+ // This preserves text node boundaries for hydration (React's approach)
212
+ const children = vElement.props.children ?? [];
213
+ let childrenHtml = "";
214
+ let prevWasText = false;
215
+ for (const child of children) {
216
+ const isText = child.type === "TEXT_ELEMENT";
217
+ // Insert marker between adjacent text nodes to preserve boundaries
218
+ if (prevWasText && isText) {
219
+ childrenHtml += "<!--fibrae:$-->";
220
+ }
221
+ childrenHtml += yield* renderVElementToString(child);
222
+ prevWasText = isText;
223
+ }
224
+ return `<${type}${attrs}${keyAttr}>${childrenHtml}</${type}>`;
225
+ }
226
+ // Unknown type
227
+ return "";
228
+ });
229
+ // =============================================================================
230
+ // Public API
231
+ // =============================================================================
232
+ /**
233
+ * Render a VElement tree to an HTML string with serialized state.
234
+ *
235
+ * Returns the HTML and the dehydrated atom state array. You can serialize
236
+ * and embed the state in your HTML however you prefer (script tag, data
237
+ * attribute, separate endpoint, etc.).
238
+ *
239
+ * On the client, pass this state to render() via the initialState option
240
+ * to hydrate atom values before DOM hydration.
241
+ *
242
+ * Note: Atoms must use `Atom.serializable({ key, schema })` to be included
243
+ * in the dehydrated state.
244
+ *
245
+ * @example
246
+ * ```typescript
247
+ * import { renderToString } from "fibrae/server";
248
+ *
249
+ * const program = Effect.gen(function* () {
250
+ * const { html, dehydratedState } = yield* renderToString(<App />);
251
+ *
252
+ * // Embed state however you prefer
253
+ * const page = `
254
+ * <!DOCTYPE html>
255
+ * <html>
256
+ * <body>
257
+ * <div id="root">${html}</div>
258
+ * <script>window.__STATE__ = ${JSON.stringify(dehydratedState)}</script>
259
+ * <script src="/client.js"></script>
260
+ * </body>
261
+ * </html>
262
+ * `;
263
+ * return page;
264
+ * });
265
+ * ```
266
+ */
267
+ export const renderToString = (element, _options) => Effect.gen(function* () {
268
+ const registry = yield* AtomRegistry.AtomRegistry;
269
+ // Render the element
270
+ const html = yield* renderVElementToString(element);
271
+ // Dehydrate the registry state
272
+ const dehydratedState = Hydration.dehydrate(registry);
273
+ return { html, dehydratedState };
274
+ }).pipe(Effect.provide(SSRAtomRegistryLayer));
275
+ /**
276
+ * Render a VElement tree to HTML, requiring AtomRegistry and any other
277
+ * services the component tree needs.
278
+ *
279
+ * Use this when your components require additional services (Navigator, RouterHandlers, etc.)
280
+ * that you want to provide yourself.
281
+ *
282
+ * @example
283
+ * ```typescript
284
+ * import { renderToStringWith, SSRAtomRegistryLayer } from "fibrae/server";
285
+ *
286
+ * const program = Effect.gen(function* () {
287
+ * const { html, dehydratedState } = yield* renderToStringWith(<App />);
288
+ * return { html, dehydratedState };
289
+ * });
290
+ *
291
+ * // Run with composed layers
292
+ * const result = Effect.runPromise(
293
+ * program.pipe(
294
+ * Effect.provide(Layer.mergeAll(
295
+ * SSRAtomRegistryLayer,
296
+ * navigatorLayer,
297
+ * routerHandlersLayer
298
+ * ))
299
+ * )
300
+ * );
301
+ * ```
302
+ */
303
+ export const renderToStringWith = (element, _options) => Effect.gen(function* () {
304
+ const registry = yield* AtomRegistry.AtomRegistry;
305
+ // Render the element
306
+ const html = yield* renderVElementToString(element);
307
+ // Dehydrate the registry state
308
+ const dehydratedState = Hydration.dehydrate(registry);
309
+ return { html, dehydratedState };
310
+ });
311
+ // Re-export Hydration for convenience
312
+ export { Hydration };
313
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,136 @@
1
+ import * as Option from "effect/Option";
2
+ import * as Stream from "effect/Stream";
3
+ import * as Effect from "effect/Effect";
4
+ import * as Scope from "effect/Scope";
5
+ import * as Deferred from "effect/Deferred";
6
+ import * as Context from "effect/Context";
7
+ import { Atom as BaseAtom } from "@effect-atom/atom";
8
+ /**
9
+ * Primitive element types: HTML tags, text nodes, fragments, error boundary, or suspense
10
+ */
11
+ export type Primitive = keyof HTMLElementTagNameMap | "TEXT_ELEMENT" | "FRAGMENT" | "ERROR_BOUNDARY" | "SUSPENSE";
12
+ /**
13
+ * Element type can be a primitive or a component function
14
+ * Components can return VElement, Effect, or Stream
15
+ */
16
+ export type ElementType<Props = {}> = Primitive | ((props: Props) => VElement | Stream.Stream<VElement, any, any> | Effect.Effect<VElement, any, any>);
17
+ /**
18
+ * Virtual element representation - the core unit of the virtual DOM
19
+ */
20
+ export interface VElement {
21
+ type: ElementType;
22
+ props: {
23
+ [key: string]: unknown;
24
+ children?: VElement[];
25
+ };
26
+ }
27
+ /**
28
+ * Mutable reference to a fiber for component instances
29
+ */
30
+ export type FiberRef = {
31
+ current: Fiber;
32
+ };
33
+ /**
34
+ * Suspense boundary configuration
35
+ *
36
+ * Supports optimistic rendering: try children first, show fallback if they take
37
+ * too long. Suspended fibers are "parked" to continue processing in background.
38
+ */
39
+ export type SuspenseConfig = {
40
+ fallback: VElement;
41
+ threshold: number;
42
+ showingFallback: boolean;
43
+ /** Reference to the original child fiber that's still processing in background */
44
+ parkedFiber: Option.Option<Fiber>;
45
+ /** Deferred that signals when parked fiber completes first render */
46
+ parkedComplete: Option.Option<Deferred.Deferred<void>>;
47
+ };
48
+ /**
49
+ * Error boundary configuration
50
+ */
51
+ export type ErrorBoundaryConfig = {
52
+ fallback: VElement;
53
+ onError?: (cause: unknown) => void;
54
+ hasError: boolean;
55
+ };
56
+ /**
57
+ * Fiber node - represents a unit of work in the reconciliation tree
58
+ * Contains all state needed for rendering, effects, and diffing
59
+ */
60
+ export interface Fiber {
61
+ type: Option.Option<ElementType>;
62
+ props: {
63
+ [key: string]: unknown;
64
+ children?: VElement[];
65
+ };
66
+ dom: Option.Option<Node>;
67
+ parent: Option.Option<Fiber>;
68
+ child: Option.Option<Fiber>;
69
+ sibling: Option.Option<Fiber>;
70
+ alternate: Option.Option<Fiber>;
71
+ effectTag: Option.Option<"UPDATE" | "PLACEMENT" | "DELETION">;
72
+ componentScope: Option.Option<Scope.Scope>;
73
+ accessedAtoms: Option.Option<Set<BaseAtom.Atom<any>>>;
74
+ latestStreamValue: Option.Option<VElement>;
75
+ childFirstCommitDeferred: Option.Option<Deferred.Deferred<void>>;
76
+ fiberRef: Option.Option<FiberRef>;
77
+ isMultiEmissionStream: boolean;
78
+ errorBoundary: Option.Option<ErrorBoundaryConfig>;
79
+ suspense: Option.Option<SuspenseConfig>;
80
+ /** Context captured during render phase, used for event handlers in commit phase */
81
+ renderContext: Option.Option<Context.Context<unknown>>;
82
+ /** True if this fiber is parked (suspended) - scope should not be closed on deletion */
83
+ isParked: boolean;
84
+ /** True when fiber is being restored from parked state - skip component re-execution */
85
+ isUnparking: boolean;
86
+ }
87
+ /**
88
+ * Helper to check if a key is an event handler
89
+ */
90
+ export declare const isEvent: (key: string) => boolean;
91
+ /**
92
+ * Helper to check if a key is a regular property (not children, ref, key, or event)
93
+ */
94
+ export declare const isProperty: (key: string) => boolean;
95
+ /**
96
+ * Check if an element type is a primitive (string) or component (function)
97
+ */
98
+ export declare const isPrimitive: (type: ElementType) => type is Primitive;
99
+ /**
100
+ * Check if element type is a component function
101
+ */
102
+ export declare const isComponent: (type: ElementType) => type is (props: {}) => VElement | Stream.Stream<VElement, any, any> | Effect.Effect<VElement, any, any>;
103
+ export declare const isStream: (value: unknown) => value is Stream.Stream<any, any, any>;
104
+ declare const HydrationMismatch_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").Equals<A, {}> extends true ? void : { readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }) => import("effect/Cause").YieldableError & {
105
+ readonly _tag: "HydrationMismatch";
106
+ } & Readonly<A>;
107
+ /**
108
+ * Error thrown when DOM structure doesn't match VElement tree during hydration.
109
+ *
110
+ * This is a tagged error that can be caught via Effect.catchTag("HydrationMismatch", ...).
111
+ *
112
+ * Structural mismatches (tag name, child count) indicate a bug where server and client
113
+ * rendered different component trees.
114
+ *
115
+ * @example
116
+ * ```typescript
117
+ * yield* render(<App />, container, { initialState }).pipe(
118
+ * Effect.catchTag("HydrationMismatch", (err) => {
119
+ * console.error(`Hydration failed at ${err.path}: expected ${err.expected}, got ${err.actual}`);
120
+ * // Fallback: clear container and do fresh render
121
+ * container.innerHTML = "";
122
+ * return render(<App />, container);
123
+ * })
124
+ * );
125
+ * ```
126
+ */
127
+ export declare class HydrationMismatch extends HydrationMismatch_base<{
128
+ /** What the VElement tree expected (e.g., "div", "3 children") */
129
+ readonly expected: string;
130
+ /** What the DOM actually had (e.g., "span", "2 children") */
131
+ readonly actual: string;
132
+ /** Human-readable path to the mismatch location (e.g., "div > ul > li:2") */
133
+ readonly path: string;
134
+ }> {
135
+ }
136
+ export {};
package/dist/shared.js ADDED
@@ -0,0 +1,53 @@
1
+ import * as Option from "effect/Option";
2
+ import * as Stream from "effect/Stream";
3
+ import * as Effect from "effect/Effect";
4
+ import * as Scope from "effect/Scope";
5
+ import * as Deferred from "effect/Deferred";
6
+ import * as Data from "effect/Data";
7
+ import * as Context from "effect/Context";
8
+ import { Atom as BaseAtom } from "@effect-atom/atom";
9
+ /**
10
+ * Helper to check if a key is an event handler
11
+ */
12
+ export const isEvent = (key) => key.startsWith("on");
13
+ /**
14
+ * Helper to check if a key is a regular property (not children, ref, key, or event)
15
+ */
16
+ export const isProperty = (key) => key !== "children" && key !== "ref" && key !== "key" && !isEvent(key);
17
+ /**
18
+ * Check if an element type is a primitive (string) or component (function)
19
+ */
20
+ export const isPrimitive = (type) => typeof type === "string";
21
+ /**
22
+ * Check if element type is a component function
23
+ */
24
+ export const isComponent = (type) => typeof type === "function";
25
+ export const isStream = (value) => (typeof value === "object" &&
26
+ value !== null &&
27
+ Stream.StreamTypeId in value);
28
+ // =============================================================================
29
+ // Hydration Errors
30
+ // =============================================================================
31
+ /**
32
+ * Error thrown when DOM structure doesn't match VElement tree during hydration.
33
+ *
34
+ * This is a tagged error that can be caught via Effect.catchTag("HydrationMismatch", ...).
35
+ *
36
+ * Structural mismatches (tag name, child count) indicate a bug where server and client
37
+ * rendered different component trees.
38
+ *
39
+ * @example
40
+ * ```typescript
41
+ * yield* render(<App />, container, { initialState }).pipe(
42
+ * Effect.catchTag("HydrationMismatch", (err) => {
43
+ * console.error(`Hydration failed at ${err.path}: expected ${err.expected}, got ${err.actual}`);
44
+ * // Fallback: clear container and do fresh render
45
+ * container.innerHTML = "";
46
+ * return render(<App />, container);
47
+ * })
48
+ * );
49
+ * ```
50
+ */
51
+ export class HydrationMismatch extends Data.TaggedError("HydrationMismatch") {
52
+ }
53
+ //# sourceMappingURL=shared.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shared.js","sourceRoot":"","sources":["../src/shared.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,KAAK,MAAM,cAAc,CAAC;AACtC,OAAO,KAAK,QAAQ,MAAM,iBAAiB,CAAC;AAC5C,OAAO,KAAK,IAAI,MAAM,aAAa,CAAC;AACpC,OAAO,KAAK,OAAO,MAAM,gBAAgB,CAAC;AAC1C,OAAO,EAAE,IAAI,IAAI,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAwFrD;;GAEG;AACH,MAAM,CAAC,MAAM,OAAO,GAAG,CAAC,GAAW,EAAE,EAAE,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;AAE7D;;GAEG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,GAAW,EAAE,EAAE,CACxC,GAAG,KAAK,UAAU,IAAI,GAAG,KAAK,KAAK,IAAI,GAAG,KAAK,KAAK,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;AAExE;;GAEG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,IAAiB,EAAqB,EAAE,CAClE,OAAO,IAAI,KAAK,QAAQ,CAAC;AAE3B;;GAEG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,IAAiB,EAAE,EAAE,CAC/C,OAAO,IAAI,KAAK,UAAU,CAAC;AAE7B,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,KAAc,EAAyC,EAAE,CAAC,CACjF,OAAO,KAAK,KAAK,QAAQ;IACzB,KAAK,KAAK,IAAI;IACd,MAAM,CAAC,YAAY,IAAI,KAAK,CAC7B,CAAC;AAEF,gFAAgF;AAChF,mBAAmB;AACnB,gFAAgF;AAEhF;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,OAAO,iBAAkB,SAAQ,IAAI,CAAC,WAAW,CAAC,mBAAmB,CAOzE;CAAG"}
@@ -0,0 +1,23 @@
1
+ import * as Effect from "effect/Effect";
2
+ import * as Stream from "effect/Stream";
3
+ import * as Scope from "effect/Scope";
4
+ import { Atom, Registry as AtomRegistry } from "@effect-atom/atom";
5
+ import { type VElement } from "./shared.js";
6
+ import { FibraeRuntime } from "./runtime.js";
7
+ /**
8
+ * Normalize component output to a Stream.
9
+ * Components can return VElement, Effect<VElement, E>, or Stream<VElement, E>.
10
+ * Error type is preserved through the conversion.
11
+ */
12
+ export declare const normalizeToStream: <E>(value: VElement | Effect.Effect<VElement, E, never> | Stream.Stream<VElement, E, never>) => Stream.Stream<VElement, E, never>;
13
+ /**
14
+ * Create a tracking registry that records which atoms are accessed
15
+ */
16
+ export declare const makeTrackingRegistry: (realRegistry: AtomRegistry.Registry, accessedAtoms: Set<Atom.Atom<unknown>>) => AtomRegistry.Registry;
17
+ /**
18
+ * Subscribe to atom changes for reactivity.
19
+ * Uses registry.subscribe directly (like atom-react) instead of streams.
20
+ * This is simpler and more efficient - subscriptions are synchronous
21
+ * and cleanup is handled via scope finalizers.
22
+ */
23
+ export declare const subscribeToAtoms: (atoms: Set<Atom.Atom<unknown>>, onUpdate: () => void, runtime: FibraeRuntime, scope: Scope.Scope.Closeable) => Effect.Effect<void, never, never>;