@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.
- package/dist/cjs/errors.js +23 -0
- package/dist/cjs/index.js +292 -0
- package/dist/cjs/package.json +4 -0
- package/dist/dts/errors.d.ts +24 -0
- package/dist/dts/errors.d.ts.map +1 -0
- package/dist/dts/index.d.ts +71 -0
- package/dist/dts/index.d.ts.map +1 -0
- package/dist/esm/errors.js +19 -0
- package/dist/esm/errors.js.map +1 -0
- package/dist/esm/index.js +288 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/package.json +4 -0
- package/package.json +29 -0
|
@@ -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,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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"}
|
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
|
+
}
|