fibrae 0.1.3 → 0.2.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.
package/dist/shared.d.ts CHANGED
@@ -4,7 +4,62 @@ import * as Effect from "effect/Effect";
4
4
  import * as Scope from "effect/Scope";
5
5
  import * as Deferred from "effect/Deferred";
6
6
  import * as Context from "effect/Context";
7
+ import type * as Cause from "effect/Cause";
8
+ import type * as Types from "effect/Types";
7
9
  import { Atom as BaseAtom } from "@effect-atom/atom";
10
+ export type { Cause, Types };
11
+ declare const ComponentScope_base: Context.TagClass<ComponentScope, "fibrae/ComponentScope", {
12
+ scope: Scope.Scope;
13
+ mounted: Deferred.Deferred<void>;
14
+ }>;
15
+ /**
16
+ * Service tag for accessing the current component's scope and mount signal.
17
+ *
18
+ * Provides:
19
+ * - `scope`: Register cleanup logic that runs when the component unmounts
20
+ * - `mounted`: Deferred that resolves after the component's DOM subtree commits
21
+ *
22
+ * @example
23
+ * ```tsx
24
+ * const JsonEditor = () =>
25
+ * Effect.gen(function* () {
26
+ * const { scope, mounted } = yield* ComponentScope;
27
+ * const containerRef = { current: null as HTMLDivElement | null };
28
+ *
29
+ * // Fork an effect that waits for mount, then initializes
30
+ * yield* pipe(
31
+ * Effect.gen(function* () {
32
+ * yield* Deferred.await(mounted); // Wait for DOM to be ready
33
+ * const editor = monaco.create(containerRef.current!);
34
+ * yield* Scope.addFinalizer(scope, Effect.sync(() => editor.dispose()));
35
+ * }),
36
+ * Effect.forkScoped,
37
+ * Scope.extend(scope)
38
+ * );
39
+ *
40
+ * return <div ref={el => containerRef.current = el} />;
41
+ * });
42
+ * ```
43
+ *
44
+ * For simple cleanup without waiting for mount:
45
+ *
46
+ * @example
47
+ * ```tsx
48
+ * const JsonEditor = () =>
49
+ * Effect.gen(function* () {
50
+ * const { scope } = yield* ComponentScope;
51
+ *
52
+ * // Register cleanup that runs on unmount
53
+ * yield* Scope.addFinalizer(scope, Effect.sync(() => {
54
+ * console.log("Editor unmounted");
55
+ * }));
56
+ *
57
+ * return <div />;
58
+ * });
59
+ * ```
60
+ */
61
+ export declare class ComponentScope extends ComponentScope_base {
62
+ }
8
63
  /**
9
64
  * Primitive element types: HTML tags, text nodes, fragments, suspense, or boundary
10
65
  */
@@ -85,6 +140,8 @@ export interface Fiber {
85
140
  alternate: Option.Option<Fiber>;
86
141
  effectTag: Option.Option<"UPDATE" | "PLACEMENT" | "DELETION">;
87
142
  componentScope: Option.Option<Scope.Scope>;
143
+ /** Deferred that resolves after this component's DOM subtree commits */
144
+ mountedDeferred: Option.Option<Deferred.Deferred<void>>;
88
145
  accessedAtoms: Option.Option<Set<BaseAtom.Atom<any>>>;
89
146
  latestStreamValue: Option.Option<VElement>;
90
147
  childFirstCommitDeferred: Option.Option<Deferred.Deferred<void>>;
@@ -117,7 +174,7 @@ export declare const isPrimitive: (type: ElementType) => type is Primitive;
117
174
  */
118
175
  export declare const isComponent: (type: ElementType) => type is (props: {}) => VNode;
119
176
  export declare const isStream: (value: unknown) => value is Stream.Stream<any, any, any>;
120
- 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 & {
177
+ declare const HydrationMismatch_base: new <A extends Record<string, any> = {}>(args: Types.Equals<A, {}> extends true ? void : { readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }) => Cause.YieldableError & {
121
178
  readonly _tag: "HydrationMismatch";
122
179
  } & Readonly<A>;
123
180
  /**
@@ -149,7 +206,7 @@ export declare class HydrationMismatch extends HydrationMismatch_base<{
149
206
  readonly path: string;
150
207
  }> {
151
208
  }
152
- declare const RenderError_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 & {
209
+ declare const RenderError_base: new <A extends Record<string, any> = {}>(args: Types.Equals<A, {}> extends true ? void : { readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }) => Cause.YieldableError & {
153
210
  readonly _tag: "RenderError";
154
211
  } & Readonly<A>;
155
212
  /**
@@ -171,7 +228,7 @@ export declare class RenderError extends RenderError_base<{
171
228
  readonly componentName?: string;
172
229
  }> {
173
230
  }
174
- declare const StreamError_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 & {
231
+ declare const StreamError_base: new <A extends Record<string, any> = {}>(args: Types.Equals<A, {}> extends true ? void : { readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }) => Cause.YieldableError & {
175
232
  readonly _tag: "StreamError";
176
233
  } & Readonly<A>;
177
234
  /**
@@ -188,7 +245,7 @@ export declare class StreamError extends StreamError_base<{
188
245
  readonly phase: "before-first-emission" | "after-first-emission";
189
246
  }> {
190
247
  }
191
- declare const EventHandlerError_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 & {
248
+ declare const EventHandlerError_base: new <A extends Record<string, any> = {}>(args: Types.Equals<A, {}> extends true ? void : { readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }) => Cause.YieldableError & {
192
249
  readonly _tag: "EventHandlerError";
193
250
  } & Readonly<A>;
194
251
  /**
@@ -208,4 +265,3 @@ export declare class EventHandlerError extends EventHandlerError_base<{
208
265
  * Used with ErrorBoundary's onError handlers for typed error matching.
209
266
  */
210
267
  export type ComponentError = RenderError | StreamError | EventHandlerError;
211
- export {};
package/dist/shared.js CHANGED
@@ -6,6 +6,57 @@ import * as Deferred from "effect/Deferred";
6
6
  import * as Data from "effect/Data";
7
7
  import * as Context from "effect/Context";
8
8
  import { Atom as BaseAtom } from "@effect-atom/atom";
9
+ // =============================================================================
10
+ // Component Scope Service
11
+ // =============================================================================
12
+ /**
13
+ * Service tag for accessing the current component's scope and mount signal.
14
+ *
15
+ * Provides:
16
+ * - `scope`: Register cleanup logic that runs when the component unmounts
17
+ * - `mounted`: Deferred that resolves after the component's DOM subtree commits
18
+ *
19
+ * @example
20
+ * ```tsx
21
+ * const JsonEditor = () =>
22
+ * Effect.gen(function* () {
23
+ * const { scope, mounted } = yield* ComponentScope;
24
+ * const containerRef = { current: null as HTMLDivElement | null };
25
+ *
26
+ * // Fork an effect that waits for mount, then initializes
27
+ * yield* pipe(
28
+ * Effect.gen(function* () {
29
+ * yield* Deferred.await(mounted); // Wait for DOM to be ready
30
+ * const editor = monaco.create(containerRef.current!);
31
+ * yield* Scope.addFinalizer(scope, Effect.sync(() => editor.dispose()));
32
+ * }),
33
+ * Effect.forkScoped,
34
+ * Scope.extend(scope)
35
+ * );
36
+ *
37
+ * return <div ref={el => containerRef.current = el} />;
38
+ * });
39
+ * ```
40
+ *
41
+ * For simple cleanup without waiting for mount:
42
+ *
43
+ * @example
44
+ * ```tsx
45
+ * const JsonEditor = () =>
46
+ * Effect.gen(function* () {
47
+ * const { scope } = yield* ComponentScope;
48
+ *
49
+ * // Register cleanup that runs on unmount
50
+ * yield* Scope.addFinalizer(scope, Effect.sync(() => {
51
+ * console.log("Editor unmounted");
52
+ * }));
53
+ *
54
+ * return <div />;
55
+ * });
56
+ * ```
57
+ */
58
+ export class ComponentScope extends Context.Tag("fibrae/ComponentScope")() {
59
+ }
9
60
  /**
10
61
  * Helper to check if a key is an event handler
11
62
  */
@@ -1 +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;AA0HrD;;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,CAAC,OAAO,IAAI,KAAK,QAAQ,CAAC;AAE9F;;GAEG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,IAAiB,EAAE,EAAE,CAAC,OAAO,IAAI,KAAK,UAAU,CAAC;AAE7E,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,KAAc,EAAyC,EAAE,CAChF,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,MAAM,CAAC,YAAY,IAAI,KAAK,CAAC;AAE9E,gFAAgF;AAChF,mBAAmB;AACnB,gFAAgF;AAEhF;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,OAAO,iBAAkB,SAAQ,IAAI,CAAC,WAAW,CAAC,mBAAmB,CAOzE;CAAG;AAEL,gFAAgF;AAChF,mBAAmB;AACnB,gFAAgF;AAEhF;;;;;;;;;;;GAWG;AACH,MAAM,OAAO,WAAY,SAAQ,IAAI,CAAC,WAAW,CAAC,aAAa,CAK7D;CAAG;AAEL;;;;;;GAMG;AACH,MAAM,OAAO,WAAY,SAAQ,IAAI,CAAC,WAAW,CAAC,aAAa,CAK7D;CAAG;AAEL;;;;GAIG;AACH,MAAM,OAAO,iBAAkB,SAAQ,IAAI,CAAC,WAAW,CAAC,mBAAmB,CAKzE;CAAG"}
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;AAI1C,OAAO,EAAE,IAAI,IAAI,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAKrD,gFAAgF;AAChF,0BAA0B;AAC1B,gFAAgF;AAEhF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6CG;AACH,MAAM,OAAO,cAAe,SAAQ,OAAO,CAAC,GAAG,CAAC,uBAAuB,CAAC,EAGrE;CAAG;AA4HN;;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,CAAC,OAAO,IAAI,KAAK,QAAQ,CAAC;AAE9F;;GAEG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,IAAiB,EAAE,EAAE,CAAC,OAAO,IAAI,KAAK,UAAU,CAAC;AAE7E,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,KAAc,EAAyC,EAAE,CAChF,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,MAAM,CAAC,YAAY,IAAI,KAAK,CAAC;AAE9E,gFAAgF;AAChF,mBAAmB;AACnB,gFAAgF;AAEhF;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,OAAO,iBAAkB,SAAQ,IAAI,CAAC,WAAW,CAAC,mBAAmB,CAOzE;CAAG;AAEL,gFAAgF;AAChF,mBAAmB;AACnB,gFAAgF;AAEhF;;;;;;;;;;;GAWG;AACH,MAAM,OAAO,WAAY,SAAQ,IAAI,CAAC,WAAW,CAAC,aAAa,CAK7D;CAAG;AAEL;;;;;;GAMG;AACH,MAAM,OAAO,WAAY,SAAQ,IAAI,CAAC,WAAW,CAAC,aAAa,CAK7D;CAAG;AAEL;;;;GAIG;AACH,MAAM,OAAO,iBAAkB,SAAQ,IAAI,CAAC,WAAW,CAAC,mBAAmB,CAKzE;CAAG"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fibrae",
3
- "version": "0.1.3",
3
+ "version": "0.2.0",
4
4
  "description": "Effect-first JSX renderer with automatic reactivity",
5
5
  "license": "MIT",
6
6
  "repository": {