@playfast/react 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.UnknownSlot = exports.ComposeOutsideProvider = void 0;
4
+ const effect_1 = require("effect");
5
+ /**
6
+ * Tagged errors for the React host's render-time boundaries. Thrown (React has
7
+ * no Effect error channel during render), but tagged and structured so they are
8
+ * never bare `Error`s.
9
+ */
10
+ /** `<Compose>` was used without a surrounding `<ReformProvider>` (no runtime). */
11
+ class ComposeOutsideProvider extends effect_1.Data.TaggedError('reform/react/ComposeOutsideProvider') {
12
+ get message() {
13
+ return 'reform: <Compose> must be rendered inside a <ReformProvider> (use mount)';
14
+ }
15
+ }
16
+ exports.ComposeOutsideProvider = ComposeOutsideProvider;
17
+ /** A composition was asked for a slot name its manifest does not declare. */
18
+ class UnknownSlot extends effect_1.Data.TaggedError('reform/react/UnknownSlot') {
19
+ get message() {
20
+ return `reform: composition '${this.composition}' has no slot '${this.slot}'`;
21
+ }
22
+ }
23
+ exports.UnknownSlot = UnknownSlot;
@@ -0,0 +1,292 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Reform = exports.Compose = exports.ReformProvider = void 0;
4
+ const effect_1 = require("effect");
5
+ const react_1 = require("react");
6
+ const reform_1 = require("reform");
7
+ const errors_js_1 = require("./errors.js");
8
+ const RuntimeContext = (0, react_1.createContext)(null);
9
+ const ReformProvider = (props) => (0, react_1.createElement)(RuntimeContext.Provider, { value: props.runtime }, props.children);
10
+ exports.ReformProvider = ReformProvider;
11
+ const useReform = () => {
12
+ const runtime = (0, react_1.useContext)(RuntimeContext);
13
+ if (runtime === null) {
14
+ throw new errors_js_1.ComposeOutsideProvider();
15
+ }
16
+ return runtime;
17
+ };
18
+ /**
19
+ * Build the slot bindings for one composition instance: each slot name maps to a
20
+ * *stable* React component (cached) that renders the wired child, so children
21
+ * reconcile across re-renders instead of remounting.
22
+ */
23
+ const makeSlotHost = (comp, reform) => {
24
+ const declared = comp.manifest.slots ?? {};
25
+ const cache = new Map();
26
+ return {
27
+ slot: (name) => {
28
+ const cached = cache.get(name);
29
+ if (cached !== undefined)
30
+ return cached;
31
+ const slotClass = declared[name];
32
+ if (slotClass === undefined) {
33
+ throw new errors_js_1.UnknownSlot({ composition: comp.manifest.name, slot: name });
34
+ }
35
+ const child = reform.read(slotClass.tag);
36
+ // A slot filled by a lazy feature holds a `FeatureBinding` — render it through
37
+ // `FeatureHost`, which drives the load/mount lifecycle and paints the
38
+ // placeholders. A plain composition (a normal fill or a default feature) goes
39
+ // straight through `Compose`.
40
+ const component = (0, reform_1.isFeatureBinding)(child)
41
+ ? (childProps) => (0, react_1.createElement)(FeatureHost, { binding: child, props: childProps, reform })
42
+ : (childProps) => (0, react_1.createElement)(exports.Compose, { comp: child, props: childProps });
43
+ cache.set(name, component);
44
+ return component;
45
+ },
46
+ };
47
+ };
48
+ /**
49
+ * Render one composition. Each React render runs the composition's synchronous
50
+ * `render(env)`, recording the state slices it reads; the component is driven by
51
+ * `useSyncExternalStore` whose snapshot is the sum of those slices' versions.
52
+ *
53
+ * Using `useSyncExternalStore` (rather than a manual `useReducer` + post-commit
54
+ * `subscribe`) is what makes the bridge correct under React 19: its snapshot is
55
+ * re-read right after subscribe, so a slice that changes in the gap between
56
+ * render and subscription forces a re-render instead of being silently dropped,
57
+ * and concurrent renders can't tear. Dependencies are dynamic (a family key can
58
+ * appear/disappear between renders), so the live subscription is re-pointed at
59
+ * the latest tracked set after every commit.
60
+ */
61
+ const ComposeImpl = ({ comp, props }) => {
62
+ const reform = useReform();
63
+ const service = (0, react_1.useMemo)(() => reform.read(comp.tag), [reform, comp]);
64
+ const slots = (0, react_1.useMemo)(() => makeSlotHost(comp, reform), [reform, comp]);
65
+ const tracked = (0, react_1.useRef)(new Set());
66
+ const sub = (0, react_1.useRef)({ cb: null, live: new Map(), snapshot: 0, dirty: true });
67
+ // Point the live subscription at the currently-tracked set by DELTA: subscribe
68
+ // only newly-tracked sources, unsubscribe only dropped ones. An unchanged dep
69
+ // set (the common case) does two cheap `has` scans and zero subscribe/
70
+ // unsubscribe calls, instead of tearing down and rebuilding every slice's
71
+ // subscription on every commit. Marking `dirty` forces the next snapshot to
72
+ // recompute from live versions, which is what catches a slice that changed in
73
+ // the gap between render and the post-commit re-point.
74
+ const repoint = (0, react_1.useCallback)(() => {
75
+ const { cb, live } = sub.current;
76
+ if (cb === null)
77
+ return;
78
+ const next = tracked.current;
79
+ for (const source of next) {
80
+ if (!live.has(source))
81
+ live.set(source, source.subscribe(cb));
82
+ }
83
+ for (const [source, off] of live) {
84
+ if (!next.has(source)) {
85
+ off();
86
+ live.delete(source);
87
+ }
88
+ }
89
+ sub.current.dirty = true;
90
+ }, []);
91
+ const subscribe = (0, react_1.useCallback)((onStoreChange) => {
92
+ // A slice notification marks the snapshot dirty before waking React, so the
93
+ // next `getSnapshot` recomputes once and then serves O(1) until the next change.
94
+ sub.current.cb = () => {
95
+ sub.current.dirty = true;
96
+ onStoreChange();
97
+ };
98
+ repoint();
99
+ return () => {
100
+ for (const off of sub.current.live.values())
101
+ off();
102
+ sub.current = { cb: null, live: new Map(), snapshot: 0, dirty: true };
103
+ };
104
+ }, [repoint]);
105
+ // Snapshot = number of tracked slices + sum of their versions. It moves on any
106
+ // dependency change (so React re-renders) and is stable otherwise (so React
107
+ // bails out). Recomputed from live versions only when `dirty` (a notification or
108
+ // a re-point) — O(1) on the hot path where React polls an unchanged snapshot —
109
+ // and crucially still reflects changes that land between render and subscribe,
110
+ // because the post-commit re-point sets `dirty`.
111
+ const getSnapshot = (0, react_1.useCallback)(() => {
112
+ const current = sub.current;
113
+ if (current.dirty) {
114
+ let snapshot = tracked.current.size;
115
+ for (const source of tracked.current)
116
+ snapshot = (snapshot + source.getVersion()) >>> 0;
117
+ current.snapshot = snapshot;
118
+ current.dirty = false;
119
+ }
120
+ return current.snapshot;
121
+ }, []);
122
+ (0, react_1.useSyncExternalStore)(subscribe, getSnapshot, getSnapshot);
123
+ // Run the logic, collecting this render's dependencies.
124
+ const deps = new Set();
125
+ const env = { props, tracker: { add: (source) => deps.add(source) }, slots };
126
+ const node = reform.runRender(reform_1.Composition.render(service, env));
127
+ tracked.current = deps;
128
+ // Re-point the live subscription at this render's dep set after commit.
129
+ (0, react_1.useEffect)(() => {
130
+ repoint();
131
+ });
132
+ return node;
133
+ };
134
+ /**
135
+ * Shallow value-equality over a composition's prop record. A composition's only
136
+ * reactive inputs are (a) its props and (b) the store slices it reads — and (b)
137
+ * re-renders this node directly through its own `useSyncExternalStore`
138
+ * subscription, independent of the parent. So a child can safely skip a
139
+ * parent-driven re-render whenever its props are unchanged: that's exactly what
140
+ * this comparison decides.
141
+ */
142
+ const shallowEqualProps = (a, b) => {
143
+ if (Object.is(a, b))
144
+ return true;
145
+ if (typeof a !== 'object' || a === null || typeof b !== 'object' || b === null)
146
+ return false;
147
+ const ak = Object.keys(a);
148
+ const bk = Object.keys(b);
149
+ if (ak.length !== bk.length)
150
+ return false;
151
+ for (const key of ak) {
152
+ if (!Object.is(a[key], b[key])) {
153
+ return false;
154
+ }
155
+ }
156
+ return true;
157
+ };
158
+ /**
159
+ * The memo boundary that stops a parent's re-render from cascading into every
160
+ * descendant. Without it, a slice change that re-renders a parent re-runs the
161
+ * synchronous `render(env)` of all its children even when their inputs never
162
+ * moved — turning fine-grained per-slice reactivity into O(subtree) work on the
163
+ * way down. With it, a child re-renders only when its own props change (compared
164
+ * here) or its own tracked slices change (its `useSyncExternalStore`
165
+ * subscription), giving O(changed nodes) updates. `comp` is a stable class; the
166
+ * inner `props` object is rebuilt by the parent each render, so it's compared by
167
+ * value, not reference — pass referentially-stable props (e.g. ids, not freshly
168
+ * derived objects) to keep the boundary effective for large lists.
169
+ */
170
+ exports.Compose = (0, react_1.memo)(ComposeImpl, (a, b) => a.comp === b.comp && shallowEqualProps(a.props, b.props));
171
+ /**
172
+ * Host a lazy feature in a slot. On mount it loads + builds the feature against the
173
+ * parent runtime (sharing its engine), painting `placeholder.loading` meanwhile and
174
+ * `placeholder.failed` (with a `retry`) on failure. Once live it renders the
175
+ * feature's composition under a nested provider over the child runtime, so the
176
+ * feature's own stores/composition resolve from its built context while shared
177
+ * reads and dispatch flow through the one engine. Unmounting disposes the feature.
178
+ *
179
+ * Placeholders render through the *parent* provider (their `.live` is eager, in the
180
+ * parent runtime); only the live composition uses the child runtime.
181
+ */
182
+ const FeatureHost = ({ binding, props, reform, }) => {
183
+ const [state, setState] = (0, react_1.useState)({ _tag: 'Loading' });
184
+ // Bumping `attempt` re-runs the mount effect — that's how `retry` works.
185
+ const [attempt, setAttempt] = (0, react_1.useState)(0);
186
+ (0, react_1.useEffect)(() => {
187
+ const alive = { current: true };
188
+ setState({ _tag: 'Loading' });
189
+ const dispose = reform.mountFeature(binding, {
190
+ onLive: (runtime) => {
191
+ if (alive.current)
192
+ setState({ _tag: 'Live', runtime });
193
+ },
194
+ onFailed: (error) => {
195
+ if (alive.current)
196
+ setState({ _tag: 'Failed', error });
197
+ },
198
+ });
199
+ return () => {
200
+ alive.current = false;
201
+ dispose();
202
+ };
203
+ }, [reform, binding, attempt]);
204
+ if (state._tag === 'Live') {
205
+ return (0, react_1.createElement)(exports.ReformProvider, {
206
+ runtime: state.runtime,
207
+ children: (0, react_1.createElement)(exports.Compose, { comp: binding.composition, props }),
208
+ });
209
+ }
210
+ if (state._tag === 'Failed') {
211
+ if (binding.placeholder === undefined)
212
+ return null;
213
+ const retry = () => setAttempt((n) => n + 1);
214
+ return (0, react_1.createElement)(exports.Compose, {
215
+ comp: binding.placeholder.failed,
216
+ props: { error: state.error, retry },
217
+ });
218
+ }
219
+ return binding.placeholder === undefined
220
+ ? null
221
+ : (0, react_1.createElement)(exports.Compose, { comp: binding.placeholder.loading, props });
222
+ };
223
+ /**
224
+ * Build the concrete `ReformRuntime` over a `ManagedRuntime` made from a scene's
225
+ * closed layers. The mounted runtime provides every reform tag, so reading any
226
+ * one needs no further context; we erase the requirement the generic tag carries.
227
+ * Shared by the DOM host (`Reform`) and the React-Native root.
228
+ */
229
+ const buildRuntime = (provide) => {
230
+ // A scene always provides at least one layer; merge them into the app layer.
231
+ const layer = provide.reduce((a, b) => effect_1.Layer.merge(a, b));
232
+ const runtime = effect_1.ManagedRuntime.make(layer);
233
+ // The full app context (all services), captured once. A feature's `.live` layer
234
+ // is built against this so it shares the one engine (bus + registries). Typed as
235
+ // the host's `MountedServices` subset; at runtime it carries every service.
236
+ const rootContext = runtime.runSync(effect_1.Effect.context());
237
+ // A child runtime over a feature's merged context (feature services + the shared
238
+ // engine). Reads resolve from that context; renders run on the parent runtime
239
+ // (the captured render Effect is self-contained); dispatch shares the one bus.
240
+ const makeChildRuntime = (context) => ({
241
+ read: (tag) => effect_1.Context.unsafeGet(context, tag),
242
+ runRender: (effect) => runtime.runSync(effect),
243
+ dispatch: (priority, event) => {
244
+ runtime.runFork((0, reform_1.publish)(priority, event));
245
+ },
246
+ mountFeature: (binding, handlers) => mountFeatureOnto(binding, handlers),
247
+ });
248
+ const mountFeatureOnto = (binding, handlers) => {
249
+ const scope = effect_1.Effect.runSync(effect_1.Scope.make());
250
+ runtime.runFork((0, reform_1.mountFeature)(binding, rootContext).pipe(effect_1.Effect.provideService(effect_1.Scope.Scope, scope), effect_1.Effect.matchCause({
251
+ onFailure: (cause) => handlers.onFailed(effect_1.Option.getOrElse(effect_1.Cause.failureOption(cause), () => new reform_1.FeatureLoadFailed({ cause }))),
252
+ onSuccess: (context) => handlers.onLive(makeChildRuntime(context)),
253
+ })));
254
+ return () => {
255
+ runtime.runFork(effect_1.Scope.close(scope, effect_1.Exit.succeed(undefined)));
256
+ };
257
+ };
258
+ const reform = {
259
+ read: (tag) => runtime.runSync(tag),
260
+ runRender: (effect) => runtime.runSync(effect),
261
+ dispatch: (priority, event) => {
262
+ runtime.runFork((0, reform_1.publish)(priority, event));
263
+ },
264
+ mountFeature: (binding, handlers) => mountFeatureOnto(binding, handlers),
265
+ };
266
+ return { reform, dispose: () => void runtime.dispose() };
267
+ };
268
+ /**
269
+ * The application entry point: host a scene as a live React tree. Builds the one
270
+ * `ManagedRuntime` from the scene's closed layers (memoised per scene), renders
271
+ * its composition, and disposes the runtime when the scene changes or the tree
272
+ * unmounts. The app owns the React root — `createRoot(el).render(<Reform scene={…} />)`.
273
+ *
274
+ * Boot events fire from a committed effect (not the render body): the app layer's
275
+ * procedure fibers subscribe to the `Bus` as the runtime builds, and dispatching
276
+ * boot only after commit guarantees those subscribers exist, so an event like
277
+ * `RequestedTodos` is never dropped into an empty bus. The effect is keyed on the
278
+ * built runtime, so a new scene re-boots against its fresh runtime.
279
+ */
280
+ const Reform = ({ scene }) => {
281
+ const runtime = (0, react_1.useMemo)(() => buildRuntime(scene.provide), [scene]);
282
+ (0, react_1.useEffect)(() => {
283
+ for (const event of scene.boot ?? [])
284
+ runtime.reform.dispatch('High', event);
285
+ return runtime.dispose;
286
+ }, [runtime, scene]);
287
+ return (0, react_1.createElement)(exports.ReformProvider, {
288
+ runtime: runtime.reform,
289
+ children: (0, react_1.createElement)(exports.Compose, { comp: scene.composition, props: {} }),
290
+ });
291
+ };
292
+ exports.Reform = Reform;
@@ -0,0 +1,4 @@
1
+ {
2
+ "type": "commonjs",
3
+ "sideEffects": []
4
+ }
@@ -0,0 +1,24 @@
1
+ declare const ComposeOutsideProvider_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
2
+ readonly _tag: "reform/react/ComposeOutsideProvider";
3
+ } & Readonly<A>;
4
+ /**
5
+ * Tagged errors for the React host's render-time boundaries. Thrown (React has
6
+ * no Effect error channel during render), but tagged and structured so they are
7
+ * never bare `Error`s.
8
+ */
9
+ /** `<Compose>` was used without a surrounding `<ReformProvider>` (no runtime). */
10
+ export declare class ComposeOutsideProvider extends ComposeOutsideProvider_base<{}> {
11
+ get message(): string;
12
+ }
13
+ declare const UnknownSlot_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
14
+ readonly _tag: "reform/react/UnknownSlot";
15
+ } & Readonly<A>;
16
+ /** A composition was asked for a slot name its manifest does not declare. */
17
+ export declare class UnknownSlot extends UnknownSlot_base<{
18
+ readonly composition: string;
19
+ readonly slot: string;
20
+ }> {
21
+ get message(): string;
22
+ }
23
+ export {};
24
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/errors.ts"],"names":[],"mappings":";;;AAEA;;;;GAIG;AAEH,kFAAkF;AAClF,qBAAa,sBAAuB,SAAQ,4BAAwD,EAAE,CAAC;IACrG,IAAa,OAAO,IAAI,MAAM,CAE7B;CACF;;;;AAED,6EAA6E;AAC7E,qBAAa,WAAY,SAAQ,iBAA6C;IAC5E,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;IAC5B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CACtB,CAAC;IACA,IAAa,OAAO,IAAI,MAAM,CAE7B;CACF"}
@@ -0,0 +1,71 @@
1
+ import { Context, Effect } from 'effect';
2
+ import { type ReactNode } from 'react';
3
+ import { FeatureLoadFailed } from 'reform';
4
+ import type { CompositionClass, FeatureBinding, Node, Priority, Scene, Tagged } from 'reform';
5
+ /**
6
+ * The capabilities a host needs from a built reform runtime. A concrete one is
7
+ * built by `buildRuntime` (below) over a `ManagedRuntime`; `Compose` depends
8
+ * only on this interface.
9
+ */
10
+ export interface ReformRuntime {
11
+ /**
12
+ * Resolve any reform DI tag (a composition's mounted logic, a slot's wired
13
+ * child) from the app context. The mounted runtime provides every reform
14
+ * service, so this never fails for tags from the same app.
15
+ */
16
+ read<A>(tag: Context.Tag<A, A>): A;
17
+ /** Run a fully-provided, synchronous render to its node (D2). */
18
+ runRender(effect: Effect.Effect<Node, never, never>): Node;
19
+ /** Dispatch a runtime/boot event onto the bus. */
20
+ dispatch(priority: Priority, event: Tagged): void;
21
+ /**
22
+ * Mount a feature against this runtime: load its module (lazy = dynamic import),
23
+ * build its `.live` layer on a fresh scope sharing this runtime's engine, and
24
+ * call `onLive` with a child runtime that reads the feature's composition/stores
25
+ * (and the shared services), or `onFailed` with a typed `FeatureLoadFailed`.
26
+ * Returns a disposer that closes the feature's scope — stopping its fibers and
27
+ * reclaiming its reducers/stores. Used by `FeatureHost` for lazy slots.
28
+ */
29
+ mountFeature(binding: FeatureBinding, handlers: {
30
+ readonly onLive: (runtime: ReformRuntime) => void;
31
+ readonly onFailed: (error: FeatureLoadFailed) => void;
32
+ }): () => void;
33
+ }
34
+ export declare const ReformProvider: (props: {
35
+ readonly runtime: ReformRuntime;
36
+ readonly children: ReactNode;
37
+ }) => ReactNode;
38
+ interface ComposeProps {
39
+ readonly comp: CompositionClass<unknown>;
40
+ readonly props: unknown;
41
+ }
42
+ /**
43
+ * The memo boundary that stops a parent's re-render from cascading into every
44
+ * descendant. Without it, a slice change that re-renders a parent re-runs the
45
+ * synchronous `render(env)` of all its children even when their inputs never
46
+ * moved — turning fine-grained per-slice reactivity into O(subtree) work on the
47
+ * way down. With it, a child re-renders only when its own props change (compared
48
+ * here) or its own tracked slices change (its `useSyncExternalStore`
49
+ * subscription), giving O(changed nodes) updates. `comp` is a stable class; the
50
+ * inner `props` object is rebuilt by the parent each render, so it's compared by
51
+ * value, not reference — pass referentially-stable props (e.g. ids, not freshly
52
+ * derived objects) to keep the boundary effective for large lists.
53
+ */
54
+ export declare const Compose: (props: ComposeProps) => ReactNode;
55
+ /**
56
+ * The application entry point: host a scene as a live React tree. Builds the one
57
+ * `ManagedRuntime` from the scene's closed layers (memoised per scene), renders
58
+ * its composition, and disposes the runtime when the scene changes or the tree
59
+ * unmounts. The app owns the React root — `createRoot(el).render(<Reform scene={…} />)`.
60
+ *
61
+ * Boot events fire from a committed effect (not the render body): the app layer's
62
+ * procedure fibers subscribe to the `Bus` as the runtime builds, and dispatching
63
+ * boot only after commit guarantees those subscribers exist, so an event like
64
+ * `RequestedTodos` is never dropped into an empty bus. The effect is keyed on the
65
+ * built runtime, so a new scene re-boots against its fresh runtime.
66
+ */
67
+ export declare const Reform: ({ scene }: {
68
+ readonly scene: Scene;
69
+ }) => ReactNode;
70
+ export {};
71
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAS,OAAO,EAAE,MAAM,EAA8C,MAAM,QAAQ,CAAA;AAC3F,OAAO,EAIL,KAAK,SAAS,EAQf,MAAM,OAAO,CAAA;AACd,OAAO,EAEL,iBAAiB,EAIlB,MAAM,QAAQ,CAAA;AAEf,OAAO,KAAK,EACV,gBAAgB,EAChB,cAAc,EAEd,IAAI,EACJ,QAAQ,EAER,KAAK,EAGL,MAAM,EACP,MAAM,QAAQ,CAAA;AASf;;;;GAIG;AACH,MAAM,WAAW,aAAa;IAC5B;;;;OAIG;IACH,IAAI,CAAC,CAAC,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,CAAA;IAClC,iEAAiE;IACjE,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,CAAC,GAAG,IAAI,CAAA;IAC1D,kDAAkD;IAClD,QAAQ,CAAC,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IACjD;;;;;;;OAOG;IACH,YAAY,CACV,OAAO,EAAE,cAAc,EACvB,QAAQ,EAAE;QACR,QAAQ,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,IAAI,CAAA;QACjD,QAAQ,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,iBAAiB,KAAK,IAAI,CAAA;KACtD,GACA,MAAM,IAAI,CAAA;CACd;AAID,eAAO,MAAM,cAAc,GAAI,OAAO;IACpC,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAA;IAC/B,QAAQ,CAAC,QAAQ,EAAE,SAAS,CAAA;CAC7B,KAAG,SAA6F,CAAA;AAyCjG,UAAU,YAAY;IACpB,QAAQ,CAAC,IAAI,EAAE,gBAAgB,CAAC,OAAO,CAAC,CAAA;IACxC,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAA;CACxB;AAgID;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,OAAO,EAGf,CAAC,KAAK,EAAE,YAAY,KAAK,SAAS,CAAA;AA+IvC;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,MAAM,GAAI,WAAW;IAAE,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAA;CAAE,KAAG,SAU7D,CAAA"}
@@ -0,0 +1,19 @@
1
+ import { Data } from 'effect';
2
+ /**
3
+ * Tagged errors for the React host's render-time boundaries. Thrown (React has
4
+ * no Effect error channel during render), but tagged and structured so they are
5
+ * never bare `Error`s.
6
+ */
7
+ /** `<Compose>` was used without a surrounding `<ReformProvider>` (no runtime). */
8
+ export class ComposeOutsideProvider extends Data.TaggedError('reform/react/ComposeOutsideProvider') {
9
+ get message() {
10
+ return 'reform: <Compose> must be rendered inside a <ReformProvider> (use mount)';
11
+ }
12
+ }
13
+ /** A composition was asked for a slot name its manifest does not declare. */
14
+ export class UnknownSlot extends Data.TaggedError('reform/react/UnknownSlot') {
15
+ get message() {
16
+ return `reform: composition '${this.composition}' has no slot '${this.slot}'`;
17
+ }
18
+ }
19
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAA;AAE7B;;;;GAIG;AAEH,kFAAkF;AAClF,MAAM,OAAO,sBAAuB,SAAQ,IAAI,CAAC,WAAW,CAAC,qCAAqC,CAAK;IACrG,IAAa,OAAO;QAClB,OAAO,0EAA0E,CAAA;IACnF,CAAC;CACF;AAED,6EAA6E;AAC7E,MAAM,OAAO,WAAY,SAAQ,IAAI,CAAC,WAAW,CAAC,0BAA0B,CAG1E;IACA,IAAa,OAAO;QAClB,OAAO,wBAAwB,IAAI,CAAC,WAAW,kBAAkB,IAAI,CAAC,IAAI,GAAG,CAAA;IAC/E,CAAC;CACF"}
@@ -0,0 +1,288 @@
1
+ import { Cause, Context, Effect, Exit, Layer, ManagedRuntime, Option, Scope } from 'effect';
2
+ import { createContext, createElement, memo, useCallback, useContext, useEffect, useMemo, useRef, useState, useSyncExternalStore, } from 'react';
3
+ import { Composition, FeatureLoadFailed, isFeatureBinding, mountFeature as mountFeatureContext, publish, } from 'reform';
4
+ import { ComposeOutsideProvider, UnknownSlot } from './errors.js';
5
+ const RuntimeContext = createContext(null);
6
+ export const ReformProvider = (props) => createElement(RuntimeContext.Provider, { value: props.runtime }, props.children);
7
+ const useReform = () => {
8
+ const runtime = useContext(RuntimeContext);
9
+ if (runtime === null) {
10
+ throw new ComposeOutsideProvider();
11
+ }
12
+ return runtime;
13
+ };
14
+ /**
15
+ * Build the slot bindings for one composition instance: each slot name maps to a
16
+ * *stable* React component (cached) that renders the wired child, so children
17
+ * reconcile across re-renders instead of remounting.
18
+ */
19
+ const makeSlotHost = (comp, reform) => {
20
+ const declared = comp.manifest.slots ?? {};
21
+ const cache = new Map();
22
+ return {
23
+ slot: (name) => {
24
+ const cached = cache.get(name);
25
+ if (cached !== undefined)
26
+ return cached;
27
+ const slotClass = declared[name];
28
+ if (slotClass === undefined) {
29
+ throw new UnknownSlot({ composition: comp.manifest.name, slot: name });
30
+ }
31
+ const child = reform.read(slotClass.tag);
32
+ // A slot filled by a lazy feature holds a `FeatureBinding` — render it through
33
+ // `FeatureHost`, which drives the load/mount lifecycle and paints the
34
+ // placeholders. A plain composition (a normal fill or a default feature) goes
35
+ // straight through `Compose`.
36
+ const component = isFeatureBinding(child)
37
+ ? (childProps) => createElement(FeatureHost, { binding: child, props: childProps, reform })
38
+ : (childProps) => createElement(Compose, { comp: child, props: childProps });
39
+ cache.set(name, component);
40
+ return component;
41
+ },
42
+ };
43
+ };
44
+ /**
45
+ * Render one composition. Each React render runs the composition's synchronous
46
+ * `render(env)`, recording the state slices it reads; the component is driven by
47
+ * `useSyncExternalStore` whose snapshot is the sum of those slices' versions.
48
+ *
49
+ * Using `useSyncExternalStore` (rather than a manual `useReducer` + post-commit
50
+ * `subscribe`) is what makes the bridge correct under React 19: its snapshot is
51
+ * re-read right after subscribe, so a slice that changes in the gap between
52
+ * render and subscription forces a re-render instead of being silently dropped,
53
+ * and concurrent renders can't tear. Dependencies are dynamic (a family key can
54
+ * appear/disappear between renders), so the live subscription is re-pointed at
55
+ * the latest tracked set after every commit.
56
+ */
57
+ const ComposeImpl = ({ comp, props }) => {
58
+ const reform = useReform();
59
+ const service = useMemo(() => reform.read(comp.tag), [reform, comp]);
60
+ const slots = useMemo(() => makeSlotHost(comp, reform), [reform, comp]);
61
+ const tracked = useRef(new Set());
62
+ const sub = useRef({ cb: null, live: new Map(), snapshot: 0, dirty: true });
63
+ // Point the live subscription at the currently-tracked set by DELTA: subscribe
64
+ // only newly-tracked sources, unsubscribe only dropped ones. An unchanged dep
65
+ // set (the common case) does two cheap `has` scans and zero subscribe/
66
+ // unsubscribe calls, instead of tearing down and rebuilding every slice's
67
+ // subscription on every commit. Marking `dirty` forces the next snapshot to
68
+ // recompute from live versions, which is what catches a slice that changed in
69
+ // the gap between render and the post-commit re-point.
70
+ const repoint = useCallback(() => {
71
+ const { cb, live } = sub.current;
72
+ if (cb === null)
73
+ return;
74
+ const next = tracked.current;
75
+ for (const source of next) {
76
+ if (!live.has(source))
77
+ live.set(source, source.subscribe(cb));
78
+ }
79
+ for (const [source, off] of live) {
80
+ if (!next.has(source)) {
81
+ off();
82
+ live.delete(source);
83
+ }
84
+ }
85
+ sub.current.dirty = true;
86
+ }, []);
87
+ const subscribe = useCallback((onStoreChange) => {
88
+ // A slice notification marks the snapshot dirty before waking React, so the
89
+ // next `getSnapshot` recomputes once and then serves O(1) until the next change.
90
+ sub.current.cb = () => {
91
+ sub.current.dirty = true;
92
+ onStoreChange();
93
+ };
94
+ repoint();
95
+ return () => {
96
+ for (const off of sub.current.live.values())
97
+ off();
98
+ sub.current = { cb: null, live: new Map(), snapshot: 0, dirty: true };
99
+ };
100
+ }, [repoint]);
101
+ // Snapshot = number of tracked slices + sum of their versions. It moves on any
102
+ // dependency change (so React re-renders) and is stable otherwise (so React
103
+ // bails out). Recomputed from live versions only when `dirty` (a notification or
104
+ // a re-point) — O(1) on the hot path where React polls an unchanged snapshot —
105
+ // and crucially still reflects changes that land between render and subscribe,
106
+ // because the post-commit re-point sets `dirty`.
107
+ const getSnapshot = useCallback(() => {
108
+ const current = sub.current;
109
+ if (current.dirty) {
110
+ let snapshot = tracked.current.size;
111
+ for (const source of tracked.current)
112
+ snapshot = (snapshot + source.getVersion()) >>> 0;
113
+ current.snapshot = snapshot;
114
+ current.dirty = false;
115
+ }
116
+ return current.snapshot;
117
+ }, []);
118
+ useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
119
+ // Run the logic, collecting this render's dependencies.
120
+ const deps = new Set();
121
+ const env = { props, tracker: { add: (source) => deps.add(source) }, slots };
122
+ const node = reform.runRender(Composition.render(service, env));
123
+ tracked.current = deps;
124
+ // Re-point the live subscription at this render's dep set after commit.
125
+ useEffect(() => {
126
+ repoint();
127
+ });
128
+ return node;
129
+ };
130
+ /**
131
+ * Shallow value-equality over a composition's prop record. A composition's only
132
+ * reactive inputs are (a) its props and (b) the store slices it reads — and (b)
133
+ * re-renders this node directly through its own `useSyncExternalStore`
134
+ * subscription, independent of the parent. So a child can safely skip a
135
+ * parent-driven re-render whenever its props are unchanged: that's exactly what
136
+ * this comparison decides.
137
+ */
138
+ const shallowEqualProps = (a, b) => {
139
+ if (Object.is(a, b))
140
+ return true;
141
+ if (typeof a !== 'object' || a === null || typeof b !== 'object' || b === null)
142
+ return false;
143
+ const ak = Object.keys(a);
144
+ const bk = Object.keys(b);
145
+ if (ak.length !== bk.length)
146
+ return false;
147
+ for (const key of ak) {
148
+ if (!Object.is(a[key], b[key])) {
149
+ return false;
150
+ }
151
+ }
152
+ return true;
153
+ };
154
+ /**
155
+ * The memo boundary that stops a parent's re-render from cascading into every
156
+ * descendant. Without it, a slice change that re-renders a parent re-runs the
157
+ * synchronous `render(env)` of all its children even when their inputs never
158
+ * moved — turning fine-grained per-slice reactivity into O(subtree) work on the
159
+ * way down. With it, a child re-renders only when its own props change (compared
160
+ * here) or its own tracked slices change (its `useSyncExternalStore`
161
+ * subscription), giving O(changed nodes) updates. `comp` is a stable class; the
162
+ * inner `props` object is rebuilt by the parent each render, so it's compared by
163
+ * value, not reference — pass referentially-stable props (e.g. ids, not freshly
164
+ * derived objects) to keep the boundary effective for large lists.
165
+ */
166
+ export const Compose = memo(ComposeImpl, (a, b) => a.comp === b.comp && shallowEqualProps(a.props, b.props));
167
+ /**
168
+ * Host a lazy feature in a slot. On mount it loads + builds the feature against the
169
+ * parent runtime (sharing its engine), painting `placeholder.loading` meanwhile and
170
+ * `placeholder.failed` (with a `retry`) on failure. Once live it renders the
171
+ * feature's composition under a nested provider over the child runtime, so the
172
+ * feature's own stores/composition resolve from its built context while shared
173
+ * reads and dispatch flow through the one engine. Unmounting disposes the feature.
174
+ *
175
+ * Placeholders render through the *parent* provider (their `.live` is eager, in the
176
+ * parent runtime); only the live composition uses the child runtime.
177
+ */
178
+ const FeatureHost = ({ binding, props, reform, }) => {
179
+ const [state, setState] = useState({ _tag: 'Loading' });
180
+ // Bumping `attempt` re-runs the mount effect — that's how `retry` works.
181
+ const [attempt, setAttempt] = useState(0);
182
+ useEffect(() => {
183
+ const alive = { current: true };
184
+ setState({ _tag: 'Loading' });
185
+ const dispose = reform.mountFeature(binding, {
186
+ onLive: (runtime) => {
187
+ if (alive.current)
188
+ setState({ _tag: 'Live', runtime });
189
+ },
190
+ onFailed: (error) => {
191
+ if (alive.current)
192
+ setState({ _tag: 'Failed', error });
193
+ },
194
+ });
195
+ return () => {
196
+ alive.current = false;
197
+ dispose();
198
+ };
199
+ }, [reform, binding, attempt]);
200
+ if (state._tag === 'Live') {
201
+ return createElement(ReformProvider, {
202
+ runtime: state.runtime,
203
+ children: createElement(Compose, { comp: binding.composition, props }),
204
+ });
205
+ }
206
+ if (state._tag === 'Failed') {
207
+ if (binding.placeholder === undefined)
208
+ return null;
209
+ const retry = () => setAttempt((n) => n + 1);
210
+ return createElement(Compose, {
211
+ comp: binding.placeholder.failed,
212
+ props: { error: state.error, retry },
213
+ });
214
+ }
215
+ return binding.placeholder === undefined
216
+ ? null
217
+ : createElement(Compose, { comp: binding.placeholder.loading, props });
218
+ };
219
+ /**
220
+ * Build the concrete `ReformRuntime` over a `ManagedRuntime` made from a scene's
221
+ * closed layers. The mounted runtime provides every reform tag, so reading any
222
+ * one needs no further context; we erase the requirement the generic tag carries.
223
+ * Shared by the DOM host (`Reform`) and the React-Native root.
224
+ */
225
+ const buildRuntime = (provide) => {
226
+ // A scene always provides at least one layer; merge them into the app layer.
227
+ const layer = provide.reduce((a, b) => Layer.merge(a, b));
228
+ const runtime = ManagedRuntime.make(layer);
229
+ // The full app context (all services), captured once. A feature's `.live` layer
230
+ // is built against this so it shares the one engine (bus + registries). Typed as
231
+ // the host's `MountedServices` subset; at runtime it carries every service.
232
+ const rootContext = runtime.runSync(Effect.context());
233
+ // A child runtime over a feature's merged context (feature services + the shared
234
+ // engine). Reads resolve from that context; renders run on the parent runtime
235
+ // (the captured render Effect is self-contained); dispatch shares the one bus.
236
+ const makeChildRuntime = (context) => ({
237
+ read: (tag) => Context.unsafeGet(context, tag),
238
+ runRender: (effect) => runtime.runSync(effect),
239
+ dispatch: (priority, event) => {
240
+ runtime.runFork(publish(priority, event));
241
+ },
242
+ mountFeature: (binding, handlers) => mountFeatureOnto(binding, handlers),
243
+ });
244
+ const mountFeatureOnto = (binding, handlers) => {
245
+ const scope = Effect.runSync(Scope.make());
246
+ runtime.runFork(mountFeatureContext(binding, rootContext).pipe(Effect.provideService(Scope.Scope, scope), Effect.matchCause({
247
+ onFailure: (cause) => handlers.onFailed(Option.getOrElse(Cause.failureOption(cause), () => new FeatureLoadFailed({ cause }))),
248
+ onSuccess: (context) => handlers.onLive(makeChildRuntime(context)),
249
+ })));
250
+ return () => {
251
+ runtime.runFork(Scope.close(scope, Exit.succeed(undefined)));
252
+ };
253
+ };
254
+ const reform = {
255
+ read: (tag) => runtime.runSync(tag),
256
+ runRender: (effect) => runtime.runSync(effect),
257
+ dispatch: (priority, event) => {
258
+ runtime.runFork(publish(priority, event));
259
+ },
260
+ mountFeature: (binding, handlers) => mountFeatureOnto(binding, handlers),
261
+ };
262
+ return { reform, dispose: () => void runtime.dispose() };
263
+ };
264
+ /**
265
+ * The application entry point: host a scene as a live React tree. Builds the one
266
+ * `ManagedRuntime` from the scene's closed layers (memoised per scene), renders
267
+ * its composition, and disposes the runtime when the scene changes or the tree
268
+ * unmounts. The app owns the React root — `createRoot(el).render(<Reform scene={…} />)`.
269
+ *
270
+ * Boot events fire from a committed effect (not the render body): the app layer's
271
+ * procedure fibers subscribe to the `Bus` as the runtime builds, and dispatching
272
+ * boot only after commit guarantees those subscribers exist, so an event like
273
+ * `RequestedTodos` is never dropped into an empty bus. The effect is keyed on the
274
+ * built runtime, so a new scene re-boots against its fresh runtime.
275
+ */
276
+ export const Reform = ({ scene }) => {
277
+ const runtime = useMemo(() => buildRuntime(scene.provide), [scene]);
278
+ useEffect(() => {
279
+ for (const event of scene.boot ?? [])
280
+ runtime.reform.dispatch('High', event);
281
+ return runtime.dispose;
282
+ }, [runtime, scene]);
283
+ return createElement(ReformProvider, {
284
+ runtime: runtime.reform,
285
+ children: createElement(Compose, { comp: scene.composition, props: {} }),
286
+ });
287
+ };
288
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ {
2
+ "type": "module",
3
+ "sideEffects": []
4
+ }
package/package.json ADDED
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "@playfast/react",
3
+ "version": "0.0.1",
4
+ "type": "module",
5
+ "sideEffects": [],
6
+ "license": "MIT",
7
+ "module": "./dist/esm/index.js",
8
+ "types": "./dist/dts/index.d.ts",
9
+ "main": "./dist/cjs/index.js",
10
+ "exports": {
11
+ "./package.json": "./package.json",
12
+ ".": {
13
+ "types": "./dist/dts/index.d.ts",
14
+ "import": "./dist/esm/index.js",
15
+ "default": "./dist/cjs/index.js"
16
+ }
17
+ },
18
+ "files": [
19
+ "dist"
20
+ ],
21
+ "publishConfig": {
22
+ "access": "public"
23
+ },
24
+ "peerDependencies": {
25
+ "effect": "*",
26
+ "react": "^19.0.0",
27
+ "@playfast/reform": "^0.0.1"
28
+ }
29
+ }