fibrae 0.2.2 → 0.3.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 (119) hide show
  1. package/dist/cli/build.d.ts +34 -0
  2. package/dist/cli/build.js +92 -0
  3. package/dist/cli/build.js.map +1 -0
  4. package/dist/cli/cli.d.ts +10 -0
  5. package/dist/cli/cli.js +43 -0
  6. package/dist/cli/cli.js.map +1 -0
  7. package/dist/cli/config.d.ts +19 -0
  8. package/dist/cli/config.js +5 -0
  9. package/dist/cli/config.js.map +1 -0
  10. package/dist/cli/html.d.ts +13 -0
  11. package/dist/cli/html.js +101 -0
  12. package/dist/cli/html.js.map +1 -0
  13. package/dist/cli/index.d.ts +6 -0
  14. package/dist/cli/index.js +4 -0
  15. package/dist/cli/index.js.map +1 -0
  16. package/dist/cli/vite-plugin.d.ts +9 -0
  17. package/dist/cli/vite-plugin.js +143 -0
  18. package/dist/cli/vite-plugin.js.map +1 -0
  19. package/dist/components.d.ts +27 -29
  20. package/dist/components.js +32 -52
  21. package/dist/components.js.map +1 -1
  22. package/dist/core.js +7 -10
  23. package/dist/core.js.map +1 -1
  24. package/dist/dom.d.ts +13 -4
  25. package/dist/dom.js +91 -26
  26. package/dist/dom.js.map +1 -1
  27. package/dist/fiber-boundary.d.ts +39 -0
  28. package/dist/fiber-boundary.js +151 -0
  29. package/dist/fiber-boundary.js.map +1 -0
  30. package/dist/fiber-commit.d.ts +27 -0
  31. package/dist/fiber-commit.js +243 -0
  32. package/dist/fiber-commit.js.map +1 -0
  33. package/dist/fiber-render.d.ts +9 -9
  34. package/dist/fiber-render.js +165 -958
  35. package/dist/fiber-render.js.map +1 -1
  36. package/dist/fiber-tree.d.ts +77 -0
  37. package/dist/fiber-tree.js +152 -0
  38. package/dist/fiber-tree.js.map +1 -0
  39. package/dist/fiber-update.d.ts +46 -0
  40. package/dist/fiber-update.js +515 -0
  41. package/dist/fiber-update.js.map +1 -0
  42. package/dist/h.js.map +1 -1
  43. package/dist/index.d.ts +1 -1
  44. package/dist/index.js +1 -1
  45. package/dist/index.js.map +1 -1
  46. package/dist/jsx-runtime/index.d.ts +253 -2
  47. package/dist/live/atom.d.ts +31 -0
  48. package/dist/live/atom.js +33 -0
  49. package/dist/live/atom.js.map +1 -0
  50. package/dist/live/client.d.ts +50 -0
  51. package/dist/live/client.js +90 -0
  52. package/dist/live/client.js.map +1 -0
  53. package/dist/live/codec.d.ts +39 -0
  54. package/dist/live/codec.js +41 -0
  55. package/dist/live/codec.js.map +1 -0
  56. package/dist/live/config.d.ts +13 -0
  57. package/dist/live/config.js +11 -0
  58. package/dist/live/config.js.map +1 -0
  59. package/dist/live/index.d.ts +25 -0
  60. package/dist/live/index.js +19 -0
  61. package/dist/live/index.js.map +1 -0
  62. package/dist/live/server.d.ts +83 -0
  63. package/dist/live/server.js +106 -0
  64. package/dist/live/server.js.map +1 -0
  65. package/dist/live/sse-stream.d.ts +14 -0
  66. package/dist/live/sse-stream.js +30 -0
  67. package/dist/live/sse-stream.js.map +1 -0
  68. package/dist/live/types.d.ts +40 -0
  69. package/dist/live/types.js +20 -0
  70. package/dist/live/types.js.map +1 -0
  71. package/dist/router/History.d.ts +2 -2
  72. package/dist/router/History.js.map +1 -1
  73. package/dist/router/Link.d.ts +8 -4
  74. package/dist/router/Link.js +13 -34
  75. package/dist/router/Link.js.map +1 -1
  76. package/dist/router/Navigator.d.ts +12 -1
  77. package/dist/router/Navigator.js +31 -68
  78. package/dist/router/Navigator.js.map +1 -1
  79. package/dist/router/Route.d.ts +16 -3
  80. package/dist/router/Route.js +32 -25
  81. package/dist/router/Route.js.map +1 -1
  82. package/dist/router/Router.d.ts +27 -19
  83. package/dist/router/Router.js +78 -101
  84. package/dist/router/Router.js.map +1 -1
  85. package/dist/router/RouterBuilder.d.ts +106 -34
  86. package/dist/router/RouterBuilder.js +78 -39
  87. package/dist/router/RouterBuilder.js.map +1 -1
  88. package/dist/router/RouterOutlet.d.ts +1 -18
  89. package/dist/router/RouterOutlet.js +25 -8
  90. package/dist/router/RouterOutlet.js.map +1 -1
  91. package/dist/router/RouterState.d.ts +1 -1
  92. package/dist/router/index.d.ts +4 -4
  93. package/dist/router/index.js +4 -2
  94. package/dist/router/index.js.map +1 -1
  95. package/dist/router/utils.d.ts +36 -0
  96. package/dist/router/utils.js +48 -0
  97. package/dist/router/utils.js.map +1 -0
  98. package/dist/runtime.d.ts +10 -7
  99. package/dist/runtime.js +20 -2
  100. package/dist/runtime.js.map +1 -1
  101. package/dist/server.d.ts +2 -2
  102. package/dist/server.js +15 -29
  103. package/dist/server.js.map +1 -1
  104. package/dist/shared.d.ts +58 -59
  105. package/dist/shared.js +51 -13
  106. package/dist/shared.js.map +1 -1
  107. package/dist/tracking.d.ts +4 -3
  108. package/dist/tracking.js +6 -1
  109. package/dist/tracking.js.map +1 -1
  110. package/package.json +32 -7
  111. package/dist/hydration.d.ts +0 -30
  112. package/dist/hydration.js +0 -355
  113. package/dist/hydration.js.map +0 -1
  114. package/dist/render.d.ts +0 -19
  115. package/dist/render.js +0 -285
  116. package/dist/render.js.map +0 -1
  117. package/dist/scope-utils.d.ts +0 -14
  118. package/dist/scope-utils.js +0 -29
  119. package/dist/scope-utils.js.map +0 -1
@@ -13,12 +13,262 @@ export declare function jsx(type: JSXType, props: PropsWithChildren<{
13
13
  export declare const jsxs: typeof jsx;
14
14
  export declare const jsxDEV: typeof jsx;
15
15
  export declare const h: typeof jsx;
16
+ /**
17
+ * Map DOM event names (lowercase) to camelCase prop names.
18
+ * Handles compound words: "keydown" → "KeyDown", "pointerenter" → "PointerEnter", etc.
19
+ */
20
+ type CamelCaseEventName = {
21
+ keydown: "KeyDown";
22
+ keyup: "KeyUp";
23
+ keypress: "KeyPress";
24
+ mousedown: "MouseDown";
25
+ mouseup: "MouseUp";
26
+ mouseover: "MouseOver";
27
+ mouseout: "MouseOut";
28
+ mouseenter: "MouseEnter";
29
+ mouseleave: "MouseLeave";
30
+ mousemove: "MouseMove";
31
+ dblclick: "DblClick";
32
+ pointerdown: "PointerDown";
33
+ pointerup: "PointerUp";
34
+ pointermove: "PointerMove";
35
+ pointerover: "PointerOver";
36
+ pointerout: "PointerOut";
37
+ pointerenter: "PointerEnter";
38
+ pointerleave: "PointerLeave";
39
+ pointercancel: "PointerCancel";
40
+ gotpointercapture: "GotPointerCapture";
41
+ lostpointercapture: "LostPointerCapture";
42
+ touchstart: "TouchStart";
43
+ touchend: "TouchEnd";
44
+ touchmove: "TouchMove";
45
+ touchcancel: "TouchCancel";
46
+ focusin: "FocusIn";
47
+ focusout: "FocusOut";
48
+ dragenter: "DragEnter";
49
+ dragleave: "DragLeave";
50
+ dragover: "DragOver";
51
+ dragstart: "DragStart";
52
+ dragend: "DragEnd";
53
+ animationstart: "AnimationStart";
54
+ animationend: "AnimationEnd";
55
+ animationiteration: "AnimationIteration";
56
+ animationcancel: "AnimationCancel";
57
+ transitionstart: "TransitionStart";
58
+ transitionend: "TransitionEnd";
59
+ transitionrun: "TransitionRun";
60
+ transitioncancel: "TransitionCancel";
61
+ compositionstart: "CompositionStart";
62
+ compositionend: "CompositionEnd";
63
+ compositionupdate: "CompositionUpdate";
64
+ selectstart: "SelectStart";
65
+ selectionchange: "SelectionChange";
66
+ canplay: "CanPlay";
67
+ canplaythrough: "CanPlayThrough";
68
+ durationchange: "DurationChange";
69
+ loadeddata: "LoadedData";
70
+ loadedmetadata: "LoadedMetadata";
71
+ loadstart: "LoadStart";
72
+ ratechange: "RateChange";
73
+ timeupdate: "TimeUpdate";
74
+ volumechange: "VolumeChange";
75
+ contextmenu: "ContextMenu";
76
+ beforeinput: "BeforeInput";
77
+ fullscreenchange: "FullscreenChange";
78
+ fullscreenerror: "FullscreenError";
79
+ };
80
+ /**
81
+ * Convert a DOM event name to its `on`-prefixed camelCase form.
82
+ * Uses CamelCaseEventName for compound words, Capitalize for simple ones.
83
+ */
84
+ type OnEventName<K extends string> = K extends keyof CamelCaseEventName ? `on${CamelCaseEventName[K]}` : `on${Capitalize<K>}`;
85
+ /**
86
+ * Event handler return type — handlers can return void, an Effect (forked
87
+ * with app context via runForkWithRuntime), or a Promise.
88
+ */
89
+ type EventHandlerReturn = void | import("effect/Effect").Effect<any, any, any>;
90
+ /**
91
+ * Event handler props derived from the DOM's own HTMLElementEventMap.
92
+ * Accepts both camelCase (onClick) and lowercase (onclick) since the runtime
93
+ * lowercases all event prop names before calling addEventListener.
94
+ */
95
+ type EventHandlerProps = {
96
+ [K in keyof HTMLElementEventMap as OnEventName<K & string>]?: (event: HTMLElementEventMap[K]) => EventHandlerReturn;
97
+ } & {
98
+ [K in keyof HTMLElementEventMap as `on${K & string}`]?: (event: HTMLElementEventMap[K]) => EventHandlerReturn;
99
+ };
100
+ /**
101
+ * For form elements (input, select, textarea), narrow event.target and
102
+ * event.currentTarget to the specific element type so users don't need casts.
103
+ */
104
+ type NarrowedEventHandlers<E extends HTMLElement> = {
105
+ onInput?: (event: Event & {
106
+ target: E;
107
+ currentTarget: E;
108
+ }) => EventHandlerReturn;
109
+ oninput?: (event: Event & {
110
+ target: E;
111
+ currentTarget: E;
112
+ }) => EventHandlerReturn;
113
+ onChange?: (event: Event & {
114
+ target: E;
115
+ currentTarget: E;
116
+ }) => EventHandlerReturn;
117
+ onchange?: (event: Event & {
118
+ target: E;
119
+ currentTarget: E;
120
+ }) => EventHandlerReturn;
121
+ onFocus?: (event: FocusEvent & {
122
+ target: E;
123
+ currentTarget: E;
124
+ }) => EventHandlerReturn;
125
+ onfocus?: (event: FocusEvent & {
126
+ target: E;
127
+ currentTarget: E;
128
+ }) => EventHandlerReturn;
129
+ onBlur?: (event: FocusEvent & {
130
+ target: E;
131
+ currentTarget: E;
132
+ }) => EventHandlerReturn;
133
+ onblur?: (event: FocusEvent & {
134
+ target: E;
135
+ currentTarget: E;
136
+ }) => EventHandlerReturn;
137
+ };
138
+ /**
139
+ * Global HTML attributes derived from the DOM HTMLElement interface.
140
+ * Using native types prevents false negatives (e.g. missing `role`).
141
+ */
142
+ type NativeGlobalAttrs = Partial<Pick<HTMLElement, "accessKey" | "autocapitalize" | "autofocus" | "contentEditable" | "dir" | "draggable" | "enterKeyHint" | "hidden" | "id" | "inert" | "inputMode" | "lang" | "nonce" | "popover" | "role" | "slot" | "spellcheck" | "tabIndex" | "title" | "translate">>;
143
+ /**
144
+ * Common HTML attributes shared across all elements.
145
+ * Combines fibrae-specific props with native global HTML attributes.
146
+ */
147
+ type BaseHTMLProps = NativeGlobalAttrs & {
148
+ key?: string | number;
149
+ ref?: ((el: HTMLElement) => void) | {
150
+ current: HTMLElement | null;
151
+ };
152
+ /** Accepts CSS string or object (native DOM uses CSSStyleDeclaration) */
153
+ style?: string | Record<string, string | number>;
154
+ class?: string;
155
+ className?: string;
156
+ children?: VChild;
157
+ /** Set raw HTML content. Replaces children — use with care. */
158
+ dangerouslySetInnerHTML?: string;
159
+ [key: `data-${string}`]: unknown;
160
+ [key: `aria-${string}`]: unknown;
161
+ };
162
+ /**
163
+ * Event names that get narrowed per-element (excluded from base EventHandlerProps).
164
+ */
165
+ type NarrowedEventNames = keyof NarrowedEventHandlers<HTMLElement>;
166
+ /**
167
+ * Props for a specific HTML element, combining base attributes,
168
+ * typed event handlers, and narrowed form-element events.
169
+ * Narrowed events override the generic ones to avoid union parameter types.
170
+ */
171
+ type HTMLElementProps<E extends HTMLElement> = BaseHTMLProps & Omit<EventHandlerProps, NarrowedEventNames> & NarrowedEventHandlers<E>;
172
+ /**
173
+ * Element-specific attribute types for elements with unique properties.
174
+ */
175
+ type InputAttrs = {
176
+ type?: string;
177
+ value?: string | number;
178
+ checked?: boolean;
179
+ placeholder?: string;
180
+ disabled?: boolean;
181
+ name?: string;
182
+ min?: string | number;
183
+ max?: string | number;
184
+ step?: string | number;
185
+ pattern?: string;
186
+ required?: boolean;
187
+ readOnly?: boolean;
188
+ autoFocus?: boolean;
189
+ autoComplete?: string;
190
+ multiple?: boolean;
191
+ accept?: string;
192
+ };
193
+ type SelectAttrs = {
194
+ value?: string | number;
195
+ multiple?: boolean;
196
+ disabled?: boolean;
197
+ name?: string;
198
+ required?: boolean;
199
+ };
200
+ type TextareaAttrs = {
201
+ value?: string;
202
+ placeholder?: string;
203
+ disabled?: boolean;
204
+ name?: string;
205
+ rows?: number;
206
+ cols?: number;
207
+ required?: boolean;
208
+ readOnly?: boolean;
209
+ };
210
+ type OptionAttrs = {
211
+ value?: string | number;
212
+ selected?: boolean;
213
+ disabled?: boolean;
214
+ label?: string;
215
+ };
216
+ type AnchorAttrs = {
217
+ href?: string;
218
+ target?: string;
219
+ rel?: string;
220
+ download?: string | boolean;
221
+ };
222
+ type ImgAttrs = {
223
+ src?: string;
224
+ alt?: string;
225
+ width?: string | number;
226
+ height?: string | number;
227
+ loading?: "lazy" | "eager";
228
+ crossOrigin?: string;
229
+ };
230
+ type FormAttrs = {
231
+ action?: string;
232
+ method?: string;
233
+ encType?: string;
234
+ noValidate?: boolean;
235
+ };
236
+ type ButtonAttrs = {
237
+ type?: "button" | "submit" | "reset";
238
+ disabled?: boolean;
239
+ name?: string;
240
+ value?: string;
241
+ };
242
+ type LabelAttrs = {
243
+ htmlFor?: string;
244
+ for?: string;
245
+ };
246
+ /**
247
+ * Map of elements that need extra attribute types beyond the base.
248
+ */
249
+ type SpecificElements = {
250
+ input: HTMLElementProps<HTMLInputElement> & InputAttrs;
251
+ select: HTMLElementProps<HTMLSelectElement> & SelectAttrs;
252
+ textarea: HTMLElementProps<HTMLTextAreaElement> & TextareaAttrs;
253
+ option: HTMLElementProps<HTMLOptionElement> & OptionAttrs;
254
+ a: HTMLElementProps<HTMLAnchorElement> & AnchorAttrs;
255
+ img: HTMLElementProps<HTMLImageElement> & ImgAttrs;
256
+ form: HTMLElementProps<HTMLFormElement> & FormAttrs;
257
+ button: HTMLElementProps<HTMLButtonElement> & ButtonAttrs;
258
+ label: HTMLElementProps<HTMLLabelElement> & LabelAttrs;
259
+ };
260
+ /**
261
+ * All remaining HTML elements get base props + typed events.
262
+ * Derived from TypeScript's own HTMLElementTagNameMap (~140 elements).
263
+ */
264
+ type GenericElements = {
265
+ [K in Exclude<keyof HTMLElementTagNameMap, keyof SpecificElements>]: HTMLElementProps<HTMLElementTagNameMap[K]>;
266
+ };
16
267
  declare global {
17
268
  namespace JSX {
18
269
  type Element = VElement;
19
270
  type ElementType = keyof IntrinsicElements | ((props: any) => VNode);
20
- interface IntrinsicElements {
21
- [elemName: string]: Record<string, unknown>;
271
+ interface IntrinsicElements extends SpecificElements, GenericElements {
22
272
  }
23
273
  interface IntrinsicAttributes {
24
274
  key?: string | number;
@@ -28,3 +278,4 @@ declare global {
28
278
  }
29
279
  }
30
280
  }
281
+ export {};
@@ -0,0 +1,31 @@
1
+ import { Atom, Result } from "@effect-atom/atom";
2
+ import type * as Schema from "effect/Schema";
3
+ /** Metadata attached to live atoms for runtime discovery. */
4
+ export interface LiveMeta<A, I> {
5
+ readonly event: string;
6
+ readonly schema: Schema.Schema<A, I>;
7
+ }
8
+ /** A live atom — an atom backed by a remote SSE source. */
9
+ export type LiveAtom<A, I = A> = Atom.Writable<Result.Result<A>> & {
10
+ readonly _live: LiveMeta<A, I>;
11
+ };
12
+ /** Check if an atom is a live atom. */
13
+ export declare const isLiveAtom: (atom: Atom.Atom<any>) => atom is LiveAtom<any, any>;
14
+ /**
15
+ * Create a live atom — an atom whose value is synced from a server SSE source.
16
+ *
17
+ * The atom's type is `Result<A>`:
18
+ * - `Result.initial()` before SSE connects
19
+ * - `Result.success(value)` on each event
20
+ *
21
+ * The atom is automatically serializable for SSR hydration.
22
+ *
23
+ * @example
24
+ * ```ts
25
+ * const ClockAtom = live("clock", { schema: Schema.String })
26
+ * ```
27
+ */
28
+ export declare const live: <A, I>(event: string, options: {
29
+ readonly schema: Schema.Schema<A, I, never>;
30
+ readonly key?: string | undefined;
31
+ }) => LiveAtom<A, I>;
@@ -0,0 +1,33 @@
1
+ import { Atom, Result } from "@effect-atom/atom";
2
+ /** Check if an atom is a live atom. */
3
+ export const isLiveAtom = (atom) => "_live" in atom && atom._live != null;
4
+ /**
5
+ * Create a live atom — an atom whose value is synced from a server SSE source.
6
+ *
7
+ * The atom's type is `Result<A>`:
8
+ * - `Result.initial()` before SSE connects
9
+ * - `Result.success(value)` on each event
10
+ *
11
+ * The atom is automatically serializable for SSR hydration.
12
+ *
13
+ * @example
14
+ * ```ts
15
+ * const ClockAtom = live("clock", { schema: Schema.String })
16
+ * ```
17
+ */
18
+ export const live = (event, options) => {
19
+ // Create a plain writable atom with Result.initial() as default.
20
+ // The actual SSE stream subscription is wired up by the render
21
+ // system when it detects a live atom via LiveConfig.
22
+ const atom = Atom.make(Result.initial());
23
+ // Mark as serializable for SSR hydration
24
+ const serialized = Atom.serializable(atom, {
25
+ key: options.key ?? event,
26
+ schema: Result.Schema({ success: options.schema }),
27
+ });
28
+ // Attach live metadata
29
+ return Object.assign(serialized, {
30
+ _live: { event, schema: options.schema },
31
+ });
32
+ };
33
+ //# sourceMappingURL=atom.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"atom.js","sourceRoot":"","sources":["../../src/live/atom.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAcjD,uCAAuC;AACvC,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,IAAoB,EAAyB,EAAE,CACxE,OAAO,IAAI,IAAI,IAAK,IAAY,CAAC,KAAK,IAAI,IAAI,CAAC;AAEjD;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,MAAM,IAAI,GAAG,CAClB,KAAa,EACb,OAGC,EACe,EAAE;IAClB,iEAAiE;IACjE,+DAA+D;IAC/D,qDAAqD;IACrD,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAmB,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC;IAE3D,yCAAyC;IACzC,MAAM,UAAU,GAAG,IAAI,CAAC,YAAY,CAAC,IAAI,EAAE;QACzC,GAAG,EAAE,OAAO,CAAC,GAAG,IAAI,KAAK;QACzB,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,MAAM,EAAE,CAAQ;KAC1D,CAAC,CAAC;IAEH,uBAAuB;IACvB,OAAO,MAAM,CAAC,MAAM,CAAC,UAAU,EAAE;QAC/B,KAAK,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAoB;KAC3D,CAAQ,CAAC;AACZ,CAAC,CAAC"}
@@ -0,0 +1,50 @@
1
+ /**
2
+ * @since 1.0.0
3
+ * Client-side LiveSync — connects to SSE endpoint and syncs atoms.
4
+ */
5
+ import * as Effect from "effect/Effect";
6
+ import { Registry as AtomRegistry } from "@effect-atom/atom";
7
+ import { ComponentScope } from "../shared.js";
8
+ import type { LiveChannel } from "./types.js";
9
+ /**
10
+ * Options for `connect()`.
11
+ *
12
+ * @since 1.0.0
13
+ */
14
+ export interface ConnectOptions {
15
+ /** SSE endpoint URL (e.g. "/api/live"). */
16
+ readonly url: string;
17
+ /** Whether to send cookies cross-origin. Required for cookie-based auth. */
18
+ readonly withCredentials?: boolean;
19
+ }
20
+ /**
21
+ * Connect to a LiveSync SSE endpoint and sync a single channel's atom.
22
+ *
23
+ * @deprecated Live atoms auto-connect via LiveConfig — just read the atom.
24
+ * Must be called inside a fibrae component — uses `ComponentScope` for
25
+ * automatic EventSource cleanup on unmount, and `AtomRegistry` to set
26
+ * the atom value on each event.
27
+ *
28
+ * Decode errors are logged via Effect and skipped (no crash).
29
+ *
30
+ * @example
31
+ * ```ts
32
+ * const LiveLights = () =>
33
+ * Effect.gen(function* () {
34
+ * yield* LiveSync.connect(LightsChannel, { url: "/api/live" })
35
+ * return <span class="live-dot" />
36
+ * })
37
+ * ```
38
+ *
39
+ * @since 1.0.0
40
+ */
41
+ export declare const connect: <A, I>(channel: LiveChannel<A, I>, options: ConnectOptions) => Effect.Effect<void, never, AtomRegistry.AtomRegistry | ComponentScope>;
42
+ /**
43
+ * Connect to a multiplexed SSE endpoint and sync multiple channels' atoms.
44
+ *
45
+ * @deprecated Live atoms auto-connect via LiveConfig — just read the atoms.
46
+ * Single EventSource, dispatches to correct atom by event name.
47
+ *
48
+ * @since 1.0.0
49
+ */
50
+ export declare const connectGroup: (channels: readonly LiveChannel<any, any>[], options: ConnectOptions) => Effect.Effect<void, never, AtomRegistry.AtomRegistry | ComponentScope>;
@@ -0,0 +1,90 @@
1
+ /**
2
+ * @since 1.0.0
3
+ * Client-side LiveSync — connects to SSE endpoint and syncs atoms.
4
+ */
5
+ import * as Effect from "effect/Effect";
6
+ import * as Either from "effect/Either";
7
+ import * as Runtime from "effect/Runtime";
8
+ import * as Scope from "effect/Scope";
9
+ import * as Schema from "effect/Schema";
10
+ import { Registry as AtomRegistry } from "@effect-atom/atom";
11
+ import { ComponentScope } from "../shared.js";
12
+ /**
13
+ * Connect to a LiveSync SSE endpoint and sync a single channel's atom.
14
+ *
15
+ * @deprecated Live atoms auto-connect via LiveConfig — just read the atom.
16
+ * Must be called inside a fibrae component — uses `ComponentScope` for
17
+ * automatic EventSource cleanup on unmount, and `AtomRegistry` to set
18
+ * the atom value on each event.
19
+ *
20
+ * Decode errors are logged via Effect and skipped (no crash).
21
+ *
22
+ * @example
23
+ * ```ts
24
+ * const LiveLights = () =>
25
+ * Effect.gen(function* () {
26
+ * yield* LiveSync.connect(LightsChannel, { url: "/api/live" })
27
+ * return <span class="live-dot" />
28
+ * })
29
+ * ```
30
+ *
31
+ * @since 1.0.0
32
+ */
33
+ export const connect = (channel, options) => Effect.gen(function* () {
34
+ // Skip during SSR
35
+ if (typeof window === "undefined")
36
+ return;
37
+ const registry = yield* AtomRegistry.AtomRegistry;
38
+ const { scope } = yield* ComponentScope;
39
+ const rt = yield* Effect.runtime();
40
+ const run = Runtime.runSync(rt);
41
+ const decode = Schema.decodeUnknownEither(Schema.parseJson(channel.schema));
42
+ const es = new EventSource(options.url, { withCredentials: options.withCredentials ?? false });
43
+ es.addEventListener(channel.name, (e) => {
44
+ const result = decode(e.data);
45
+ if (Either.isRight(result)) {
46
+ registry.set(channel.atom, result.right);
47
+ }
48
+ else {
49
+ run(Effect.logWarning("LiveSync decode error").pipe(Effect.annotateLogs({ channel: channel.name, error: String(result.left) })));
50
+ }
51
+ });
52
+ es.onerror = () => {
53
+ run(Effect.logWarning("LiveSync connection error").pipe(Effect.annotateLogs("channel", channel.name)));
54
+ };
55
+ yield* Scope.addFinalizer(scope, Effect.sync(() => es.close()));
56
+ });
57
+ /**
58
+ * Connect to a multiplexed SSE endpoint and sync multiple channels' atoms.
59
+ *
60
+ * @deprecated Live atoms auto-connect via LiveConfig — just read the atoms.
61
+ * Single EventSource, dispatches to correct atom by event name.
62
+ *
63
+ * @since 1.0.0
64
+ */
65
+ export const connectGroup = (channels, options) => Effect.gen(function* () {
66
+ if (typeof window === "undefined")
67
+ return;
68
+ const registry = yield* AtomRegistry.AtomRegistry;
69
+ const { scope } = yield* ComponentScope;
70
+ const rt = yield* Effect.runtime();
71
+ const run = Runtime.runSync(rt);
72
+ const es = new EventSource(options.url, { withCredentials: options.withCredentials ?? false });
73
+ for (const ch of channels) {
74
+ const decode = Schema.decodeUnknownEither(Schema.parseJson(ch.schema));
75
+ es.addEventListener(ch.name, (e) => {
76
+ const result = decode(e.data);
77
+ if (Either.isRight(result)) {
78
+ registry.set(ch.atom, result.right);
79
+ }
80
+ else {
81
+ run(Effect.logWarning("LiveSync decode error").pipe(Effect.annotateLogs({ channel: ch.name, error: String(result.left) })));
82
+ }
83
+ });
84
+ }
85
+ es.onerror = () => {
86
+ run(Effect.logWarning("LiveSync connection error").pipe(Effect.annotateLogs("channels", channels.map((c) => c.name).join(","))));
87
+ };
88
+ yield* Scope.addFinalizer(scope, Effect.sync(() => es.close()));
89
+ });
90
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/live/client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * @since 1.0.0
3
+ * SSE encode/decode helpers for LiveSync.
4
+ */
5
+ /**
6
+ * Encode an SSE event as a Uint8Array.
7
+ *
8
+ * Format: `[id: <id>\n]event: <name>\ndata: <json>\n\n`
9
+ *
10
+ * @since 1.0.0
11
+ */
12
+ export declare const encodeSSE: (name: string, data: unknown, id?: string | undefined) => Uint8Array<ArrayBufferLike>;
13
+ /**
14
+ * Encode an SSE comment (keepalive).
15
+ *
16
+ * Format: `: <text>\n\n`
17
+ *
18
+ * @since 1.0.0
19
+ */
20
+ export declare const encodeComment: (text: string) => Uint8Array<ArrayBufferLike>;
21
+ /**
22
+ * Encode an SSE retry directive.
23
+ *
24
+ * Tells the client how many milliseconds to wait before reconnecting.
25
+ *
26
+ * Format: `retry: <millis>\n\n`
27
+ *
28
+ * @since 1.0.0
29
+ */
30
+ export declare const encodeRetry: (millis: number) => Uint8Array<ArrayBufferLike>;
31
+ /**
32
+ * Standard SSE response headers.
33
+ *
34
+ * @since 1.0.0
35
+ */
36
+ export declare const SSE_HEADERS: {
37
+ readonly "Cache-Control": "no-cache";
38
+ readonly Connection: "keep-alive";
39
+ };
@@ -0,0 +1,41 @@
1
+ /**
2
+ * @since 1.0.0
3
+ * SSE encode/decode helpers for LiveSync.
4
+ */
5
+ const encoder = new TextEncoder();
6
+ /**
7
+ * Encode an SSE event as a Uint8Array.
8
+ *
9
+ * Format: `[id: <id>\n]event: <name>\ndata: <json>\n\n`
10
+ *
11
+ * @since 1.0.0
12
+ */
13
+ export const encodeSSE = (name, data, id) => encoder.encode((id !== undefined ? `id: ${id}\n` : "") + `event: ${name}\ndata: ${JSON.stringify(data)}\n\n`);
14
+ /**
15
+ * Encode an SSE comment (keepalive).
16
+ *
17
+ * Format: `: <text>\n\n`
18
+ *
19
+ * @since 1.0.0
20
+ */
21
+ export const encodeComment = (text) => encoder.encode(`: ${text}\n\n`);
22
+ /**
23
+ * Encode an SSE retry directive.
24
+ *
25
+ * Tells the client how many milliseconds to wait before reconnecting.
26
+ *
27
+ * Format: `retry: <millis>\n\n`
28
+ *
29
+ * @since 1.0.0
30
+ */
31
+ export const encodeRetry = (millis) => encoder.encode(`retry: ${millis}\n\n`);
32
+ /**
33
+ * Standard SSE response headers.
34
+ *
35
+ * @since 1.0.0
36
+ */
37
+ export const SSE_HEADERS = {
38
+ "Cache-Control": "no-cache",
39
+ Connection: "keep-alive",
40
+ };
41
+ //# sourceMappingURL=codec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codec.js","sourceRoot":"","sources":["../../src/live/codec.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,OAAO,GAAG,IAAI,WAAW,EAAE,CAAC;AAElC;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,IAAY,EAAE,IAAa,EAAE,EAAW,EAAc,EAAE,CAChF,OAAO,CAAC,MAAM,CACZ,CAAC,EAAE,KAAK,SAAS,CAAC,CAAC,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,UAAU,IAAI,WAAW,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,MAAM,CAC9F,CAAC;AAEJ;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,IAAY,EAAc,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,IAAI,MAAM,CAAC,CAAC;AAE3F;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,MAAc,EAAc,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,UAAU,MAAM,MAAM,CAAC,CAAC;AAElG;;;;GAIG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG;IACzB,eAAe,EAAE,UAAU;IAC3B,UAAU,EAAE,YAAY;CAChB,CAAC"}
@@ -0,0 +1,13 @@
1
+ import * as Context from "effect/Context";
2
+ export interface LiveConfigShape {
3
+ readonly baseUrl: string;
4
+ readonly channels?: Record<string, string>;
5
+ readonly withCredentials?: boolean;
6
+ }
7
+ declare const LiveConfig_base: Context.TagClass<LiveConfig, "fibrae/LiveConfig", LiveConfigShape>;
8
+ export declare class LiveConfig extends LiveConfig_base {
9
+ static make(options: LiveConfigShape): LiveConfigShape;
10
+ /** Resolve the SSE URL for a given event name. */
11
+ static resolve(config: LiveConfigShape, event: string): string;
12
+ }
13
+ export {};
@@ -0,0 +1,11 @@
1
+ import * as Context from "effect/Context";
2
+ export class LiveConfig extends Context.Tag("fibrae/LiveConfig")() {
3
+ static make(options) {
4
+ return options;
5
+ }
6
+ /** Resolve the SSE URL for a given event name. */
7
+ static resolve(config, event) {
8
+ return config.channels?.[event] ?? config.baseUrl;
9
+ }
10
+ }
11
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/live/config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,OAAO,MAAM,gBAAgB,CAAC;AAQ1C,MAAM,OAAO,UAAW,SAAQ,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC,EAA+B;IAC7F,MAAM,CAAC,IAAI,CAAC,OAAwB;QAClC,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,kDAAkD;IAClD,MAAM,CAAC,OAAO,CAAC,MAAuB,EAAE,KAAa;QACnD,OAAO,MAAM,CAAC,QAAQ,EAAE,CAAC,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC;IACpD,CAAC;CACF"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * @since 1.0.0
3
+ * fibrae/live — first-class server↔client atom sync over SSE.
4
+ *
5
+ * @example
6
+ * ```ts
7
+ * import { LiveSync } from "fibrae/live"
8
+ * ```
9
+ */
10
+ export { LiveConfig } from "./config.js";
11
+ export type { LiveConfigShape } from "./config.js";
12
+ /** @deprecated Use `live()` to create live atoms instead. */
13
+ export { channel } from "./types.js";
14
+ /** @deprecated Use `LiveAtom` type instead. */
15
+ export type { LiveChannel } from "./types.js";
16
+ export { encodeSSE, encodeComment, encodeRetry, SSE_HEADERS } from "./codec.js";
17
+ export { serve, serveGroup } from "./server.js";
18
+ export type { ServeOptions, ServeGroupChannelOptions } from "./server.js";
19
+ /** @deprecated Live atoms auto-connect via LiveConfig. */
20
+ export { connect, connectGroup } from "./client.js";
21
+ /** @deprecated Live atoms auto-connect via LiveConfig. */
22
+ export type { ConnectOptions } from "./client.js";
23
+ export { sseStream } from "./sse-stream.js";
24
+ export { live, isLiveAtom } from "./atom.js";
25
+ export type { LiveAtom, LiveMeta } from "./atom.js";
@@ -0,0 +1,19 @@
1
+ /**
2
+ * @since 1.0.0
3
+ * fibrae/live — first-class server↔client atom sync over SSE.
4
+ *
5
+ * @example
6
+ * ```ts
7
+ * import { LiveSync } from "fibrae/live"
8
+ * ```
9
+ */
10
+ export { LiveConfig } from "./config.js";
11
+ /** @deprecated Use `live()` to create live atoms instead. */
12
+ export { channel } from "./types.js";
13
+ export { encodeSSE, encodeComment, encodeRetry, SSE_HEADERS } from "./codec.js";
14
+ export { serve, serveGroup } from "./server.js";
15
+ /** @deprecated Live atoms auto-connect via LiveConfig. */
16
+ export { connect, connectGroup } from "./client.js";
17
+ export { sseStream } from "./sse-stream.js";
18
+ export { live, isLiveAtom } from "./atom.js";
19
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/live/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,6DAA6D;AAC7D,OAAO,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAGrC,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAChF,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEhD,0DAA0D;AAC1D,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAGpD,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC"}