@systemfsoftware/effect-atom-react 0.5.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/index.mjs ADDED
@@ -0,0 +1,614 @@
1
+ import * as Atom from "@systemfsoftware/effect-atom/Atom";
2
+ import * as AtomRegistry from "@systemfsoftware/effect-atom/Registry";
3
+ import * as AsyncResult from "@systemfsoftware/effect-atom/Result";
4
+ import * as Cause from "effect/Cause";
5
+ import "effect/Effect";
6
+ import * as React from "react";
7
+ import * as Scheduler from "scheduler";
8
+ import * as Hydration from "@systemfsoftware/effect-atom/Hydration";
9
+ //#region src/RegistryContext.ts
10
+ /**
11
+ * React context and provider for the Atom registry used by Effect Atom hooks.
12
+ * The registry stores atom values, schedules update work, and cleans up unused
13
+ * atoms. Sharing one registry through React context lets components in the same
14
+ * subtree read and write the same atom state.
15
+ *
16
+ * @since 4.0.0
17
+ */
18
+ /**
19
+ * Schedules Atom registry work with React's scheduler at low priority and
20
+ * returns a cancellation function for the scheduled task.
21
+ *
22
+ * @category context
23
+ * @since 4.0.0
24
+ */
25
+ function scheduleTask(f) {
26
+ const node = Scheduler.unstable_scheduleCallback(Scheduler.unstable_LowPriority, f);
27
+ return () => Scheduler.unstable_cancelCallback(node);
28
+ }
29
+ /**
30
+ * Provides a React context that supplies the `AtomRegistry` used by Atom hooks and
31
+ * hydration helpers, defaulting to a standalone registry when no provider is
32
+ * present.
33
+ *
34
+ * **When to use**
35
+ *
36
+ * Use to supply an existing `AtomRegistry` through React context when hooks or
37
+ * hydration helpers need to share registry state that is managed outside
38
+ * `RegistryProvider`.
39
+ *
40
+ * @see {@link RegistryProvider} for creating and providing a registry for a React subtree
41
+ *
42
+ * @category context
43
+ * @since 4.0.0
44
+ */
45
+ const RegistryContext = React.createContext(AtomRegistry.make({
46
+ scheduleTask,
47
+ defaultIdleTTL: 400
48
+ }));
49
+ /**
50
+ * Provides a stable `AtomRegistry` to a React subtree, optionally seeding
51
+ * initial atom values and overriding registry scheduling or idle settings.
52
+ *
53
+ * **When to use**
54
+ *
55
+ * Use to scope atom state, scheduling, and idle cleanup to a React subtree.
56
+ *
57
+ * **Details**
58
+ *
59
+ * The provider creates one `AtomRegistry` with `AtomRegistry.make`, passes it
60
+ * through `RegistryContext.Provider`, and forwards `initialValues`,
61
+ * `scheduleTask`, `timeoutResolution`, and `defaultIdleTTL` only when that
62
+ * registry is created.
63
+ *
64
+ * **Gotchas**
65
+ *
66
+ * Option changes after the first render do not rebuild the registry. When the
67
+ * provider unmounts, registry disposal is delayed briefly and canceled if the
68
+ * provider remounts before the timeout fires.
69
+ *
70
+ * @see {@link RegistryContext} for the React context supplied by this provider
71
+ *
72
+ * @category context
73
+ * @since 4.0.0
74
+ */
75
+ const RegistryProvider = (options) => {
76
+ const ref = React.useRef(null);
77
+ if (ref.current === null) ref.current = { registry: AtomRegistry.make({
78
+ scheduleTask: options.scheduleTask ?? scheduleTask,
79
+ initialValues: options.initialValues,
80
+ timeoutResolution: options.timeoutResolution,
81
+ defaultIdleTTL: options.defaultIdleTTL
82
+ }) };
83
+ React.useEffect(() => {
84
+ const current = ref.current;
85
+ if (current?.timeout !== void 0) clearTimeout(current.timeout);
86
+ return () => {
87
+ if (ref.current === null) return;
88
+ ref.current.timeout = setTimeout(() => {
89
+ ref.current?.registry.dispose();
90
+ ref.current = null;
91
+ }, 500);
92
+ };
93
+ }, [ref]);
94
+ return React.createElement(RegistryContext.Provider, { value: ref.current.registry }, options?.children);
95
+ };
96
+ //#endregion
97
+ //#region src/Hooks.ts
98
+ /**
99
+ * React hooks for working with Effect atoms from components. The hooks read,
100
+ * write, mount, refresh, and subscribe to atoms from `RegistryContext`, handle
101
+ * `AsyncResult` atoms with React Suspense, and expose helpers for reading and
102
+ * deriving `AtomRef` values.
103
+ *
104
+ * @since 4.0.0
105
+ */
106
+ function useStore(registry, atom) {
107
+ const subscribe = React.useMemo(() => (onStoreChange) => registry.subscribe(atom, () => onStoreChange()), [registry, atom]);
108
+ return React.useSyncExternalStore(subscribe, () => registry.get(atom), () => Atom.getServerValue(atom, registry));
109
+ }
110
+ const initialValuesSet = /* @__PURE__ */ new WeakMap();
111
+ /**
112
+ * Seeds initial atom values in the current React atom registry.
113
+ *
114
+ * **When to use**
115
+ *
116
+ * Use to seed atom values from a React component after the current registry
117
+ * already exists.
118
+ *
119
+ * **Gotchas**
120
+ *
121
+ * Each atom is initialized at most once for a given registry by this hook, so
122
+ * later calls for the same atom in that registry are ignored.
123
+ *
124
+ * @category hooks
125
+ * @since 4.0.0
126
+ */
127
+ const useAtomInitialValues = (initialValues) => {
128
+ const registry = React.useContext(RegistryContext);
129
+ let set = initialValuesSet.get(registry);
130
+ if (set === void 0) {
131
+ set = /* @__PURE__ */ new WeakSet();
132
+ initialValuesSet.set(registry, set);
133
+ }
134
+ for (const [atom, value] of initialValues) if (!set.has(atom)) {
135
+ set.add(atom);
136
+ registry.setInitialValue(atom, value);
137
+ }
138
+ };
139
+ /**
140
+ * Subscribes to an atom in the current React registry and returns its current
141
+ * value, optionally mapped through a selector.
142
+ *
143
+ * **When to use**
144
+ *
145
+ * Use when a React component needs to render from an atom value without also
146
+ * returning a setter.
147
+ *
148
+ * **Details**
149
+ *
150
+ * When a selector is provided, the hook maps the atom before subscribing so the
151
+ * component reads the selected value from the current `RegistryContext`.
152
+ *
153
+ * @see {@link useAtom} for reading and updating a writable atom from one component
154
+ * @see {@link useAtomRef} for reading an `AtomRef` directly
155
+ *
156
+ * @category hooks
157
+ * @since 4.0.0
158
+ */
159
+ const useAtomValue = (atom, f) => {
160
+ const registry = React.useContext(RegistryContext);
161
+ if (f) return useStore(registry, React.useMemo(() => Atom.map(atom, f), [atom, f]));
162
+ return useStore(registry, atom);
163
+ };
164
+ function mountAtom(registry, atom) {
165
+ React.useEffect(() => registry.mount(atom), [atom, registry]);
166
+ }
167
+ /**
168
+ * Mounts an atom in the current React registry for the lifetime of the
169
+ * component.
170
+ *
171
+ * **When to use**
172
+ *
173
+ * Use to keep an atom mounted from a React component without reading, writing,
174
+ * or refreshing it.
175
+ *
176
+ * **Details**
177
+ *
178
+ * The hook uses the current `RegistryContext` and releases the mount through
179
+ * React effect cleanup when the component unmounts or when the registry or atom
180
+ * dependency changes.
181
+ *
182
+ * @see {@link useAtomSet} for mounting a writable atom while returning a setter
183
+ * @see {@link useAtomRefresh} for mounting an atom while returning a refresh callback
184
+ *
185
+ * @category hooks
186
+ * @since 4.0.0
187
+ */
188
+ const useAtomMount = (atom) => {
189
+ mountAtom(React.useContext(RegistryContext), atom);
190
+ };
191
+ /**
192
+ * Mounts a writable atom and returns a setter without subscribing to its value.
193
+ *
194
+ * **When to use**
195
+ *
196
+ * Use when a React component needs to update a writable atom without rendering
197
+ * from that atom's value.
198
+ *
199
+ * The hook mounts the atom and returns a setter that writes a complete value.
200
+ * For updates computed from the current value use `useAtomUpdate`.
201
+ *
202
+ * @see {@link useAtom} for reading and updating the same writable atom
203
+ * @see {@link useAtomSetResult} for a setter that resolves once the write settles
204
+ * @see {@link useAtomUpdate} for a setter that applies an updater function
205
+ *
206
+ * @category hooks
207
+ * @since 4.0.0
208
+ */
209
+ const useAtomSet = (atom) => {
210
+ const registry = React.useContext(RegistryContext);
211
+ mountAtom(registry, atom);
212
+ return React.useCallback((value) => {
213
+ registry.set(atom, value);
214
+ }, [registry, atom]);
215
+ };
216
+ /**
217
+ * Mounts a writable `AsyncResult` atom and returns a setter whose returned
218
+ * effect resolves to the settled success value.
219
+ *
220
+ * **When to use**
221
+ *
222
+ * Use when a component writes to an `AsyncResult` atom and needs to know when
223
+ * the write has been applied, so a save button can show a confirming state or
224
+ * report a failure.
225
+ *
226
+ * The hook mounts the atom and returns a setter that writes a new value and
227
+ * returns the effect of the atom leaving its initial state, failing with the
228
+ * write result's cause when the write fails.
229
+ *
230
+ * @see {@link useAtomSet} for writing without waiting for settlement
231
+ *
232
+ * @category hooks
233
+ * @since 4.0.0
234
+ */
235
+ const useAtomSetResult = (atom) => {
236
+ const registry = React.useContext(RegistryContext);
237
+ mountAtom(registry, atom);
238
+ return React.useCallback((value) => {
239
+ registry.set(atom, value);
240
+ return AtomRegistry.getResult(registry, atom, { suspendOnWaiting: true });
241
+ }, [registry, atom]);
242
+ };
243
+ /**
244
+ * Mounts a writable atom and returns an updater that applies a function to the
245
+ * current value.
246
+ *
247
+ * **When to use**
248
+ *
249
+ * Use when a component needs to update a writable atom from its current value,
250
+ * such as incrementing a counter, without subscribing to the atom.
251
+ *
252
+ * @see {@link useAtomSet} for writing a complete value
253
+ *
254
+ * @category hooks
255
+ * @since 4.0.0
256
+ */
257
+ const useAtomUpdate = (atom) => {
258
+ const registry = React.useContext(RegistryContext);
259
+ mountAtom(registry, atom);
260
+ return React.useCallback((f) => {
261
+ registry.update(atom, f);
262
+ }, [registry, atom]);
263
+ };
264
+ /**
265
+ * Mounts an atom and returns a callback that refreshes it in the current React
266
+ * registry.
267
+ *
268
+ * **When to use**
269
+ *
270
+ * Use to expose a React callback that requests a refresh for an atom without
271
+ * reading or writing its value.
272
+ *
273
+ * **Details**
274
+ *
275
+ * The hook uses the current `RegistryContext`, mounts the atom for the
276
+ * component lifetime, and returns a callback that calls `registry.refresh`.
277
+ *
278
+ * @see {@link useAtomMount} for mounting an atom without returning a refresh callback
279
+ *
280
+ * @category hooks
281
+ * @since 4.0.0
282
+ */
283
+ const useAtomRefresh = (atom) => {
284
+ const registry = React.useContext(RegistryContext);
285
+ mountAtom(registry, atom);
286
+ return React.useCallback(() => {
287
+ registry.refresh(atom);
288
+ }, [registry, atom]);
289
+ };
290
+ /**
291
+ * Subscribes to a writable atom and returns its current value together with a
292
+ * setter for updating it.
293
+ *
294
+ * **When to use**
295
+ *
296
+ * Use when a React component needs both to render the current value of a
297
+ * writable atom and update it from the same component.
298
+ *
299
+ * @see {@link useAtomValue} for subscribing to an atom without a setter
300
+ * @see {@link useAtomSet} for updating a writable atom without subscribing to its value
301
+ *
302
+ * @category hooks
303
+ * @since 4.0.0
304
+ */
305
+ const useAtom = (atom) => {
306
+ const registry = React.useContext(RegistryContext);
307
+ return [useStore(registry, atom), React.useCallback((value) => registry.set(atom, value), [registry, atom])];
308
+ };
309
+ const atomPromiseMap = {
310
+ suspendOnWaiting: /* @__PURE__ */ new WeakMap(),
311
+ default: /* @__PURE__ */ new WeakMap()
312
+ };
313
+ function atomToPromise(registry, atom, suspendOnWaiting) {
314
+ const registries = suspendOnWaiting ? atomPromiseMap.suspendOnWaiting : atomPromiseMap.default;
315
+ let map = registries.get(registry);
316
+ if (map === void 0) {
317
+ map = /* @__PURE__ */ new WeakMap();
318
+ registries.set(registry, map);
319
+ }
320
+ const cached = map.get(atom);
321
+ if (cached !== void 0) return cached;
322
+ const { promise, resolve } = Promise.withResolvers();
323
+ let settled = false;
324
+ const dispose = registry.subscribe(atom, (result) => {
325
+ if (settled || AsyncResult.isInitial(result) || suspendOnWaiting && result.waiting) return;
326
+ settled = true;
327
+ dispose();
328
+ resolve();
329
+ map.delete(atom);
330
+ });
331
+ map.set(atom, promise);
332
+ return promise;
333
+ }
334
+ function atomResultOrSuspend(registry, atom, suspendOnWaiting) {
335
+ const value = useStore(registry, atom);
336
+ if (AsyncResult.isInitial(value) || suspendOnWaiting && value.waiting) throw atomToPromise(registry, atom, suspendOnWaiting);
337
+ return value;
338
+ }
339
+ /**
340
+ * Reads an `AsyncResult` atom through React Suspense, suspending while the
341
+ * result is initial or configured as waiting.
342
+ *
343
+ * **When to use**
344
+ *
345
+ * Use when a React component should render only after an `AsyncResult` atom has
346
+ * left its initial state, with loading delegated to a Suspense boundary.
347
+ *
348
+ * **Details**
349
+ *
350
+ * `suspendOnWaiting` defaults to `false`. When `includeFailure` is `true`, a
351
+ * failure result is returned instead of being thrown.
352
+ *
353
+ * **Gotchas**
354
+ *
355
+ * Without `includeFailure`, failure results are thrown with
356
+ * `Cause.squash(result.cause)`, so callers need an error boundary for failures.
357
+ *
358
+ * @see {@link useAtomValue} for reading the raw `AsyncResult` value without Suspense
359
+ *
360
+ * @category hooks
361
+ * @since 4.0.0
362
+ */
363
+ const useAtomSuspense = (atom, options) => {
364
+ const result = atomResultOrSuspend(React.useContext(RegistryContext), atom, options?.suspendOnWaiting ?? false);
365
+ if (AsyncResult.isFailure(result)) {
366
+ if (options?.includeFailure) return result;
367
+ throw Cause.squash(result.cause);
368
+ }
369
+ return result;
370
+ };
371
+ /**
372
+ * Subscribes a callback to an atom in the current React registry for the
373
+ * component lifetime.
374
+ *
375
+ * **When to use**
376
+ *
377
+ * Use when a React component needs to run a callback for atom changes without
378
+ * reading the atom value during render.
379
+ *
380
+ * **Details**
381
+ *
382
+ * The subscription is installed in a React effect and cleaned up on unmount or
383
+ * dependency change. When `options.immediate` is enabled, the callback receives
384
+ * the current value when the effect subscribes.
385
+ *
386
+ * @see {@link useAtomValue} for reading an atom value during render instead of running a callback
387
+ *
388
+ * @category hooks
389
+ * @since 4.0.0
390
+ */
391
+ const useAtomSubscribe = (atom, f, options) => {
392
+ const registry = React.useContext(RegistryContext);
393
+ const fRef = React.useRef(f);
394
+ fRef.current = f;
395
+ React.useEffect(() => registry.subscribe(atom, (value) => fRef.current(value), options), [
396
+ registry,
397
+ atom,
398
+ options?.immediate
399
+ ]);
400
+ };
401
+ /**
402
+ * Subscribes to an atom ref and returns its latest value.
403
+ *
404
+ * **When to use**
405
+ *
406
+ * Use when a React component should render from an `AtomRef.ReadonlyRef`
407
+ * directly instead of reading an atom through the current registry.
408
+ *
409
+ * **Details**
410
+ *
411
+ * The hook subscribes with `ref.subscribe`, triggers re-renders through React
412
+ * state, and returns the current `ref.value`.
413
+ *
414
+ * @see {@link useAtomValue} for reading an `Atom` from the current registry
415
+ * @see {@link useAtomRefPropValue} for reading a property ref value
416
+ *
417
+ * @category hooks
418
+ * @since 4.0.0
419
+ */
420
+ const useAtomRef = (ref) => {
421
+ const [, setValue] = React.useState(ref.value);
422
+ React.useEffect(() => ref.subscribe(setValue), [ref]);
423
+ return ref.value;
424
+ };
425
+ /**
426
+ * Returns a memoized atom ref for a property of another atom ref.
427
+ *
428
+ * **When to use**
429
+ *
430
+ * Use to derive an `AtomRef` for one property of an object-shaped atom ref.
431
+ *
432
+ * **Details**
433
+ *
434
+ * The hook memoizes `ref.prop(prop)` for the `[ref, prop]` dependency pair and
435
+ * returns the property ref so callers can read, set, update, or subscribe to
436
+ * that nested property.
437
+ *
438
+ * @see {@link useAtomRef} for subscribing to an atom ref value
439
+ * @see {@link useAtomRefPropValue} for subscribing directly to a property value
440
+ *
441
+ * @category hooks
442
+ * @since 4.0.0
443
+ */
444
+ const useAtomRefProp = (ref, prop) => React.useMemo(() => ref.prop(prop), [ref, prop]);
445
+ /**
446
+ * Subscribes to a property ref derived from an atom ref and returns its current
447
+ * value.
448
+ *
449
+ * **When to use**
450
+ *
451
+ * Use when a React component needs only the current value of one property from
452
+ * an object-shaped `AtomRef`.
453
+ *
454
+ * **Details**
455
+ *
456
+ * The hook composes `useAtomRefProp(ref, prop)` with `useAtomRef`, so the
457
+ * property ref is memoized for the `[ref, prop]` pair and then subscribed
458
+ * through `ref.subscribe`.
459
+ *
460
+ * @see {@link useAtomRefProp} for returning the property ref directly
461
+ * @see {@link useAtomRef} for subscribing to a whole atom ref value
462
+ *
463
+ * @category hooks
464
+ * @since 4.0.0
465
+ */
466
+ const useAtomRefPropValue = (ref, prop) => useAtomRef(useAtomRefProp(ref, prop));
467
+ //#endregion
468
+ //#region src/ReactHydration.ts
469
+ /**
470
+ * React helpers for applying dehydrated Effect Atom state to a React subtree.
471
+ * The `HydrationBoundary` component reads the nearest `RegistryContext`,
472
+ * hydrates new Atom values before children render, and delays updates for
473
+ * existing Atom values until after commit so React transitions do not update
474
+ * the current UI too early.
475
+ *
476
+ * @since 4.0.0
477
+ */
478
+ /**
479
+ * Provides a React hydration boundary that loads dehydrated Atom values into
480
+ * the current Atom registry.
481
+ *
482
+ * **When to use**
483
+ *
484
+ * Use to apply dehydrated Atom state to a React subtree that reads from the
485
+ * nearest `RegistryContext`.
486
+ *
487
+ * **Details**
488
+ *
489
+ * New Atom values are hydrated during render so descendants can read them
490
+ * immediately, while values for existing Atoms are deferred until after commit
491
+ * so transition data does not update the current UI before React accepts it.
492
+ *
493
+ * @see {@link Hydration.dehydrate} for producing dehydrated Atom state
494
+ * @see {@link Hydration.hydrate} for lower-level non-React hydration
495
+ *
496
+ * @category components
497
+ * @since 4.0.0
498
+ */
499
+ const HydrationBoundary = ({ children, state }) => {
500
+ const registry = React.useContext(RegistryContext);
501
+ const hydrationQueue = React.useMemo(() => {
502
+ if (state) {
503
+ const dehydratedAtoms = Array.from(state);
504
+ const nodes = registry.getNodes();
505
+ const newDehydratedAtoms = [];
506
+ const existingDehydratedAtoms = [];
507
+ for (const dehydratedAtom of dehydratedAtoms) if (!nodes.get(dehydratedAtom.key)) newDehydratedAtoms.push(dehydratedAtom);
508
+ else existingDehydratedAtoms.push(dehydratedAtom);
509
+ if (newDehydratedAtoms.length > 0) Hydration.hydrate(registry, newDehydratedAtoms);
510
+ if (existingDehydratedAtoms.length > 0) return existingDehydratedAtoms;
511
+ }
512
+ }, [registry, state]);
513
+ React.useEffect(() => {
514
+ if (hydrationQueue) Hydration.hydrate(registry, hydrationQueue);
515
+ }, [registry, hydrationQueue]);
516
+ return React.createElement(React.Fragment, {}, children);
517
+ };
518
+ //#endregion
519
+ //#region src/ScopedAtom.ts
520
+ /**
521
+ * React helpers for creating Atom instances that belong to one component
522
+ * subtree. `make` returns a scoped atom with a provider, context, and `use`
523
+ * accessor. Each provider creates its own Atom once, so different subtrees can
524
+ * use the same scoped atom definition without sharing state.
525
+ *
526
+ * @since 4.0.0
527
+ */
528
+ /**
529
+ * Type identifier for ScopedAtom.
530
+ *
531
+ * **Details**
532
+ *
533
+ * Used as the computed property key and marker value stored on `ScopedAtom`
534
+ * objects.
535
+ *
536
+ * @category type IDs
537
+ * @since 4.0.0
538
+ */
539
+ const TypeId = "~@effect/atom-react/ScopedAtom";
540
+ /**
541
+ * Creates a ScopedAtom from a factory function.
542
+ *
543
+ * **When to use**
544
+ *
545
+ * Use to create an atom instance that is owned by a React provider and scoped
546
+ * to a component subtree.
547
+ *
548
+ * **Details**
549
+ *
550
+ * The returned scoped atom includes a `Provider`, `Context`, and `use`
551
+ * accessor. The provider creates the atom once for its lifetime, passing the
552
+ * `value` prop to the factory when the scoped atom expects input.
553
+ *
554
+ * **Gotchas**
555
+ *
556
+ * `use` must run under the matching provider. Changing the provider `value`
557
+ * prop after mount does not recreate the atom.
558
+ *
559
+ * **Example** (Creating a scoped atom with input)
560
+ *
561
+ * ```ts import.meta.vitest
562
+ * import { make, useAtomValue } from "@effect/atom-react"
563
+ * import { Atom } from "effect/unstable/reactivity"
564
+ * import * as React from "react"
565
+ * import { renderToStaticMarkup } from "react-dom/server"
566
+ *
567
+ * const User = make((name: string) => Atom.make(name))
568
+ *
569
+ * function UserName() {
570
+ * const atom = User.use()
571
+ * const value = useAtomValue(atom)
572
+ * return React.createElement("span", null, value)
573
+ * }
574
+ *
575
+ * export function App() {
576
+ * return React.createElement(
577
+ * User.Provider,
578
+ * { value: "Ada" },
579
+ * React.createElement(UserName)
580
+ * )
581
+ * }
582
+ *
583
+ * renderToStaticMarkup(React.createElement(App)) // => "<span>Ada</span>"
584
+ * ```
585
+ *
586
+ * @category constructors
587
+ * @since 4.0.0
588
+ */
589
+ const make = (f) => {
590
+ const Context = React.createContext(void 0);
591
+ const use = () => {
592
+ const atom = React.useContext(Context);
593
+ if (atom === void 0) throw new Error("ScopedAtom used outside of its Provider");
594
+ return atom;
595
+ };
596
+ const hasNoParameters = (factory) => factory.length === 0;
597
+ const Provider = (props) => {
598
+ const atom = React.useRef(null);
599
+ if (atom.current === null) {
600
+ if (hasNoParameters(f)) atom.current = f();
601
+ else if (props.value !== void 0) atom.current = f(props.value);
602
+ else throw new Error("ScopedAtom Provider requires a value");
603
+ }
604
+ return React.createElement(Context.Provider, { value: atom.current }, props.children);
605
+ };
606
+ return {
607
+ [TypeId]: TypeId,
608
+ use,
609
+ Provider,
610
+ Context
611
+ };
612
+ };
613
+ //#endregion
614
+ export { HydrationBoundary, RegistryContext, RegistryProvider, TypeId, make, scheduleTask, useAtom, useAtomInitialValues, useAtomMount, useAtomRef, useAtomRefProp, useAtomRefPropValue, useAtomRefresh, useAtomSet, useAtomSetResult, useAtomSubscribe, useAtomSuspense, useAtomUpdate, useAtomValue };
package/package.json ADDED
@@ -0,0 +1,84 @@
1
+ {
2
+ "name": "@systemfsoftware/effect-atom-react",
3
+ "license": "Apache-2.0",
4
+ "version": "0.5.0",
5
+ "author": "Ryan Lee <drdgvhbh@gmail.com>",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/systemfsoftware/systemfsoftware.git",
9
+ "directory": "packages/effect-atom/atom-react"
10
+ },
11
+ "homepage": "https://github.com/systemfsoftware/systemfsoftware/tree/main/packages/effect-atom/atom-react#readme",
12
+ "bugs": "https://github.com/systemfsoftware/systemfsoftware/issues",
13
+ "description": "React bindings for effect-atom — forked under systemfsoftware from tim-smart/effect-atom.",
14
+ "keywords": [
15
+ "effect",
16
+ "effect-ts",
17
+ "atom",
18
+ "react",
19
+ "reactive",
20
+ "state"
21
+ ],
22
+ "type": "module",
23
+ "exports": {
24
+ ".": {
25
+ "types": "./dist/index.d.ts",
26
+ "default": "./dist/index.mjs"
27
+ },
28
+ "./package.json": "./package.json"
29
+ },
30
+ "files": [
31
+ "dist"
32
+ ],
33
+ "dependencies": {
34
+ "@systemfsoftware/effect-atom": "^0.5.3"
35
+ },
36
+ "peerDependencies": {
37
+ "effect": "4.0.0-rc.108",
38
+ "react": "^19.2.8",
39
+ "react-dom": "^19.2.8",
40
+ "scheduler": "*"
41
+ },
42
+ "devDependencies": {
43
+ "@effect/vitest": "4.0.0-rc.108",
44
+ "@systemfsoftware/arethetypeswrong-cli": "^1.1.1",
45
+ "@testing-library/dom": "^10.4.1",
46
+ "@testing-library/react": "^16.3.2",
47
+ "@types/node": "^24",
48
+ "@types/react": "^19.2.18",
49
+ "@types/react-dom": "^19.2.4",
50
+ "@types/scheduler": "^0.26.0",
51
+ "@vitest/browser": "4.1.10",
52
+ "@vitest/browser-playwright": "4.1.10",
53
+ "@vitest/coverage-istanbul": "4.1.10",
54
+ "effect": "4.0.0-rc.108",
55
+ "oxlint": "^1.77.0",
56
+ "playwright": "1.62.1",
57
+ "react": "^19.2.8",
58
+ "react-dom": "^19.2.8",
59
+ "react-error-boundary": "^6.1.2",
60
+ "rimraf": "^6.1.3",
61
+ "scheduler": "^0.27.0",
62
+ "tsdown": "^0.22.14",
63
+ "typescript": "^7",
64
+ "vitest": "^4.1.10",
65
+ "@systemfsoftware/oxlint-config": "^0.1.0",
66
+ "@systemfsoftware/tsconfig": "^1.3.1",
67
+ "@systemfsoftware/vitest-config": "^0.1.0",
68
+ "@systemfsoftware/effect-gherkin-spec": "^0.5.1"
69
+ },
70
+ "publishConfig": {
71
+ "provenance": true
72
+ },
73
+ "scripts": {
74
+ "clean": "rimraf dist",
75
+ "build": "tsdown && pnpm dts:check",
76
+ "typecheck": "tsc --noEmit --incremental",
77
+ "test": "vitest run --passWithNoTests",
78
+ "test:watch": "vitest",
79
+ "lint": "f=${OXLINT_FORMAT:-${AGENT:+agent}}; oxlint . --config oxlint.config.ts --format=${f:-default}",
80
+ "lint:tsgo": "effect-tsgo diagnostics --project tsconfig.json --format ${TSGO_FORMAT:-text}",
81
+ "attw": "attw --pack .",
82
+ "dts:check": "tsc --noEmit -p tsconfig.dts.json"
83
+ }
84
+ }