@playfast/reform 1.0.1 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (99) hide show
  1. package/README.md +103 -56
  2. package/package.json +17 -17
  3. package/src/boundary/boundary.test.ts +63 -49
  4. package/src/boundary/boundary.ts +144 -113
  5. package/src/calc/asyncCalc.invalidate.test.ts +518 -32
  6. package/src/calc/asyncCalc.test.ts +207 -213
  7. package/src/calc/asyncCalc.ts +131 -154
  8. package/src/calc/asyncData.ts +0 -37
  9. package/src/calc/calc.test.ts +7 -33
  10. package/src/calc/calc.ts +44 -58
  11. package/src/calc/calcFamily.test.ts +80 -34
  12. package/src/calc/calcFamily.ts +54 -65
  13. package/src/calc/compose.test.ts +1 -12
  14. package/src/calc/compose.ts +1 -36
  15. package/src/calc/queryState.ts +0 -23
  16. package/src/channel/channel.ts +126 -111
  17. package/src/compose/composition.test.ts +19 -0
  18. package/src/compose/composition.ts +351 -127
  19. package/src/compose/host.ts +0 -6
  20. package/src/compose/props.ts +13 -12
  21. package/src/compose/provide.ts +189 -62
  22. package/src/compose/slot.ts +158 -49
  23. package/src/compose/structure.test.ts +6 -9
  24. package/src/compose/structure.ts +108 -79
  25. package/src/compose/ui.test.ts +19 -7
  26. package/src/compose/ui.ts +155 -156
  27. package/src/compose/ui.typecheck.ts +40 -18
  28. package/src/definition/definition.ts +22 -41
  29. package/src/event/event.fromSource.test.ts +172 -0
  30. package/src/event/event.test.ts +10 -3
  31. package/src/event/event.ts +102 -27
  32. package/src/event/eventGroup.ts +0 -1
  33. package/src/feature/feature.mount.test.ts +122 -43
  34. package/src/feature/feature.test.ts +46 -21
  35. package/src/feature/feature.ts +1347 -256
  36. package/src/feature/feature.typecheck.ts +273 -68
  37. package/src/graph/closure.ts +403 -0
  38. package/src/index.ts +68 -126
  39. package/src/internal/bucketCache.ts +39 -0
  40. package/src/internal/capture.ts +9 -24
  41. package/src/internal/env.ts +7 -0
  42. package/src/internal/errors.test.ts +0 -6
  43. package/src/internal/errors.ts +37 -60
  44. package/src/internal/inspect.test.ts +0 -6
  45. package/src/internal/inspect.ts +0 -12
  46. package/src/internal/queryDriver.ts +105 -201
  47. package/src/internal/queryDriverStore.ts +156 -0
  48. package/src/internal/queryDriverTypes.ts +59 -0
  49. package/src/internal/queryEvents.ts +0 -12
  50. package/src/internal/queryStore.ts +0 -14
  51. package/src/internal/reuse.test.ts +10 -14
  52. package/src/internal/reuse.ts +5 -30
  53. package/src/internal/scheduler.ts +4 -44
  54. package/src/internal/seeds.ts +0 -14
  55. package/src/internal/sources.ts +26 -49
  56. package/src/internal/stateRegistry.ts +0 -14
  57. package/src/internal/store.test.ts +1 -3
  58. package/src/internal/store.ts +0 -37
  59. package/src/internal/track.ts +3 -20
  60. package/src/internal/variance.ts +5 -0
  61. package/src/internal.ts +222 -0
  62. package/src/namespace/namespace.test.ts +46 -0
  63. package/src/namespace/namespace.ts +106 -0
  64. package/src/procedure/procedure.ts +40 -35
  65. package/src/reducer/reducer.ts +78 -52
  66. package/src/remote/remoteState.test.ts +590 -397
  67. package/src/remote/remoteState.ts +425 -347
  68. package/src/remote/remoteState.typecheck.ts +47 -56
  69. package/src/runtime/appRuntime.activation.test.ts +100 -0
  70. package/src/runtime/appRuntime.test.ts +249 -0
  71. package/src/runtime/appRuntime.ts +415 -97
  72. package/src/runtime/bus.ts +2 -15
  73. package/src/runtime/eventBudget.test.ts +152 -0
  74. package/src/runtime/eventBudget.ts +120 -0
  75. package/src/runtime/hardening.test.ts +73 -13
  76. package/src/runtime/instrumentation.test.ts +55 -52
  77. package/src/runtime/instrumentation.ts +6 -60
  78. package/src/runtime/loop.test.ts +36 -17
  79. package/src/runtime/loop.ts +87 -88
  80. package/src/runtime/queries.ts +0 -17
  81. package/src/scene/featureScene.test.ts +61 -0
  82. package/src/scene/scene.ts +247 -104
  83. package/src/scene/seedScene.test.ts +42 -79
  84. package/src/state/state.nominal.typecheck.ts +68 -0
  85. package/src/state/state.test.ts +17 -0
  86. package/src/state/state.ts +80 -46
  87. package/src/state/stateFamily.test.ts +16 -11
  88. package/src/state/stateFamily.ts +55 -65
  89. package/src/state/stateGroup.ts +53 -64
  90. package/src/state/token.ts +40 -23
  91. package/src/synced/syncedStore.ts +46 -58
  92. package/src/testkit/flight.testkit.ts +75 -0
  93. package/src/ui/node.ts +0 -6
  94. package/src/ui/trigger.ts +0 -5
  95. package/src/wire/tree.test.ts +8 -7
  96. package/src/wire/tree.ts +0 -39
  97. package/src/wire/triggers.test.ts +6 -6
  98. package/src/wire/triggers.ts +14 -32
  99. package/src/internal/ctx.ts +0 -16
@@ -9,60 +9,70 @@ import {
9
9
  } from '../internal/sources'
10
10
  import { resolveScheduler } from '../internal/scheduler'
11
11
  import { resolveInstrumentation, stateUpdateHook } from '../runtime/instrumentation'
12
+ import { reuse } from '../internal/reuse'
12
13
  import { makeDerivedStore, type Store } from '../internal/store'
13
14
  import { readTracked } from '../internal/track'
14
15
  import { type FamilyOptions, type FamilyStore } from '../state/stateFamily'
15
16
  import { type AnySource } from '../state/token'
16
17
 
17
- // `CalcFamily` is `Calc`'s keyed sibling, modeled on `StateFamily`: one derived,
18
- // memoized store per key over a SHARED set of inputs. Where a monolithic calc
19
- // rebuilds its whole output on any change (waking every consumer), a family
20
- // member recomputes lazily and notifies only when ITS slice of the projection
21
- // moves (the derived store's Equal gate) — per-row/per-group UI subscribes per
22
- // key and unrelated changes never wake it. Members are pure projections; a
23
- // Reducer cannot target a CalcFamily (its config requires a `StateFamily`
24
- // manifest), so calc purity holds structurally.
18
+ export interface CalcFamilySchemaReflection {
19
+ readonly ast: Schema.Schema<unknown, unknown, unknown>['ast']
20
+ }
25
21
 
26
- export interface CalcFamilyManifest<N extends string, K, Out> extends Manifest {
22
+ export interface CalcFamilyManifest<N extends string> extends Manifest {
27
23
  readonly kind: 'CalcFamily'
28
24
  readonly name: N
29
- readonly key: Schema.Schema<K, any>
30
- readonly output: Schema.Schema<Out, any>
25
+ readonly key: CalcFamilySchemaReflection
26
+ readonly output: CalcFamilySchemaReflection
27
+ }
28
+
29
+ export const CalcFamilyStoreTypeId: unique symbol = Symbol.for('reform/CalcFamilyStore')
30
+ export type CalcFamilyStoreTypeId = typeof CalcFamilyStoreTypeId
31
+
32
+ export interface CalcFamilyStore<N extends string, in out K, in out Out> extends FamilyStore<
33
+ K,
34
+ Out
35
+ > {
36
+ readonly [CalcFamilyStoreTypeId]: N
31
37
  }
32
38
 
33
39
  export interface CalcFamilyClass<
34
- out N extends string,
35
- out Inputs extends ReadonlyArray<AnySource>,
36
- in out K,
37
- in out Out,
40
+ N extends string,
41
+ Inputs extends ReadonlyArray<AnySource>,
42
+ K,
43
+ Out,
38
44
  > {
39
- /** Instance carries the key phantom so `Family['Key']` resolves in key types. */
40
45
  new (): { readonly Key: K }
41
- readonly manifest: CalcFamilyManifest<N, K, Out>
42
- readonly store: Context.Tag<FamilyStore<K, Out>, FamilyStore<K, Out>>
43
- /** Marker data read by `CalcFamily.live` and the editor's graph builder. */
46
+ readonly manifest: CalcFamilyManifest<N>
47
+ readonly store: Context.Tag<CalcFamilyStore<N, K, Out>, FamilyStore<K, Out>>
44
48
  readonly inputs: Inputs
45
49
  }
46
50
 
47
- export interface CalcFamilyConfig<Inputs extends ReadonlyArray<AnySource>, K, Out> {
48
- readonly key: Schema.Schema<K, any>
51
+ export interface CalcFamilyConfig<
52
+ Inputs extends ReadonlyArray<AnySource>,
53
+ KeySchema extends Schema.Schema.AnyNoContext,
54
+ OutputSchema extends Schema.Schema.AnyNoContext,
55
+ > {
56
+ readonly key: KeySchema
49
57
  readonly inputs: Inputs
50
- readonly output: Schema.Schema<Out, any>
58
+ readonly output: OutputSchema
51
59
  }
52
60
 
53
- /**
54
- * Define a keyed derived value: per-key memoized projections over shared
55
- * inputs. The *definition* fixes the key/output schemas and the inputs; the
56
- * *implementation* (the per-key projection) is supplied by `CalcFamily.live`.
57
- */
58
- export const make = <const N extends string, const Inputs extends ReadonlyArray<AnySource>, K, Out>(
61
+ export const make = <
62
+ const N extends string,
63
+ const Inputs extends ReadonlyArray<AnySource>,
64
+ KeySchema extends Schema.Schema.AnyNoContext,
65
+ OutputSchema extends Schema.Schema.AnyNoContext,
66
+ >(
59
67
  name: N,
60
- config: CalcFamilyConfig<Inputs, K, Out>,
61
- ): CalcFamilyClass<N, Inputs, K, Out> => {
62
- const store = Context.GenericTag<FamilyStore<K, Out>, FamilyStore<K, Out>>(
68
+ config: CalcFamilyConfig<Inputs, KeySchema, OutputSchema>,
69
+ ): CalcFamilyClass<N, Inputs, Schema.Schema.Type<KeySchema>, Schema.Schema.Type<OutputSchema>> => {
70
+ type K = Schema.Schema.Type<KeySchema>
71
+ type Out = Schema.Schema.Type<OutputSchema>
72
+ const store = Context.GenericTag<CalcFamilyStore<N, K, Out>, FamilyStore<K, Out>>(
63
73
  `reform/calcFamily/${name}`,
64
74
  )
65
- const manifest: CalcFamilyManifest<N, K, Out> = {
75
+ const manifest: CalcFamilyManifest<N> = {
66
76
  kind: 'CalcFamily',
67
77
  name,
68
78
  key: config.key,
@@ -75,54 +85,33 @@ export const make = <const N extends string, const Inputs extends ReadonlyArray<
75
85
  })
76
86
  }
77
87
 
78
- /**
79
- * Read one member by key — `CalcFamily.read(GroupProjection, id)`. Inside a
80
- * render it subscribes exactly that member's store (unrelated members never
81
- * wake the subtree); elsewhere it just snapshots — the same one-read-API
82
- * contract as `StateFamily.read`.
83
- */
84
88
  export const read = <N extends string, Inputs extends ReadonlyArray<AnySource>, K, Out>(
85
89
  family: CalcFamilyClass<N, Inputs, K, Out>,
86
90
  key: K,
87
- ): Effect.Effect<Out, never, FamilyStore<K, Out>> =>
91
+ ): Effect.Effect<Out, never, CalcFamilyStore<N, K, Out>> =>
88
92
  Effect.flatMap(family.store, (familyStore) => readTracked(familyStore.at(key)))
89
93
 
90
- // Boundary config object: optional fields are the framework's public, JSON-like
91
- // definition surface (`ExternalApi` postfix exempts them from `no-optional-fields`).
92
- export interface CalcFamilyLiveOptionsExternalApi<Inputs extends ReadonlyArray<AnySource>>
93
- extends FamilyOptions {
94
- /**
95
- * Family-wide invalidation key over the SHARED inputs (the member key plays
96
- * no part — it is fixed per member). Same contract as `Calc`'s.
97
- */
94
+ export interface CalcFamilyLiveOptionsExternalApi<
95
+ Inputs extends ReadonlyArray<AnySource>,
96
+ > extends FamilyOptions {
98
97
  readonly invalidateBy?: InvalidateBy<Inputs>
98
+ readonly reuse?: boolean
99
99
  }
100
100
 
101
- /**
102
- * Wire the per-key projection. `compute` is curried — `(key) => (inputs) =>
103
- * Out` — so per-key construction (closing over key-derived constants) runs once
104
- * per member while the inner projection runs per recompute. Each member is a
105
- * lazy derived store over the shared wired sources: memoized on the family's
106
- * invalidation key, notifying only when its own output moves. Unlike a
107
- * `StateFamily` entry, a member holds an upstream subscription, so every
108
- * removal path (forget / clear / eviction / the layer finalizer) releases it.
109
- */
110
101
  export const live = <N extends string, Inputs extends ReadonlyArray<AnySource>, K, Out>(
111
102
  family: CalcFamilyClass<N, Inputs, K, Out>,
112
103
  compute: (key: K) => (inputs: InputsObject<Inputs>) => Out,
113
104
  options: CalcFamilyLiveOptionsExternalApi<Inputs> = {},
114
- ): Layer.Layer<FamilyStore<K, Out>, never, InputStores<Inputs>> =>
105
+ ): Layer.Layer<CalcFamilyStore<N, K, Out>, never, InputStores<Inputs>> =>
115
106
  Layer.scoped(
116
107
  family.store,
117
108
  Effect.gen(function* () {
118
109
  const scheduler = yield* resolveScheduler
119
110
  const instrumentation = yield* resolveInstrumentation
120
111
  const onOutputChange = stateUpdateHook(instrumentation, family.manifest.name)
121
- // Wired ONCE for the whole family — members share the sensing surface.
122
112
  const sources = yield* wireSources(family.inputs, options.invalidateBy)
123
113
  const entries = new Map<K, { readonly store: Store<Out>; readonly unsubscribe: () => void }>()
124
114
  const evictWhenUnused = options.evictWhenUnused === true
125
- // Live subscriber count per key — maintained only when eviction is on.
126
115
  const subscribers = new Map<K, number>()
127
116
 
128
117
  const dropEntry = (key: K): void => {
@@ -134,8 +123,9 @@ export const live = <N extends string, Inputs extends ReadonlyArray<AnySource>,
134
123
  subscribers.delete(key)
135
124
  }
136
125
 
137
- // One member: the same memo block as `Calc.live`, with the key closed over.
138
- const createMember = (key: K): { readonly store: Store<Out>; readonly unsubscribe: () => void } => {
126
+ const createMember = (
127
+ key: K,
128
+ ): { readonly store: Store<Out>; readonly unsubscribe: () => void } => {
139
129
  const body = compute(key)
140
130
  const memo = MutableRef.make<
141
131
  { readonly key: ReadonlyArray<unknown>; readonly output: Out } | undefined
@@ -148,7 +138,9 @@ export const live = <N extends string, Inputs extends ReadonlyArray<AnySource>,
148
138
  return prev.output
149
139
  }
150
140
  const end = instrumentation.calcRecomputed(family.manifest.name)
151
- const output = body(args)
141
+ const fresh = body(args)
142
+ const output =
143
+ options.reuse === true && prev !== undefined ? reuse(prev.output, fresh) : fresh
152
144
  end()
153
145
  MutableRef.set(memo, { key: memoKey, output })
154
146
  return output
@@ -156,9 +148,6 @@ export const live = <N extends string, Inputs extends ReadonlyArray<AnySource>,
156
148
  return makeDerivedStore(recompute, sources.subscribe, scheduler, onOutputChange)
157
149
  }
158
150
 
159
- // The `StateFamily` ref-count idiom: evict on the next microtask once a
160
- // key falls idle (a same-commit re-subscribe cancels it) — here eviction
161
- // also releases the member's upstream subscription via `dropEntry`.
162
151
  const refCounted = (key: K, store: Store<Out>): Store<Out> => ({
163
152
  ...store,
164
153
  subscribe: (listener) => {
@@ -2,10 +2,6 @@ import { expect, it } from '@effect/vitest'
2
2
  import { Effect, Layer, Schema as S } from 'effect'
3
3
  import { Calc, composeCalcs, State, StateGroup } from '../index'
4
4
 
5
- // `composeCalcs` wires a calc-on-calc chain in dependency order. The shape under
6
- // test is the one that defeats a flat `Layer.mergeAll`: a linear chain where each
7
- // calc's only input is the previous calc — count → Doubled → PlusOne → Tripled.
8
-
9
5
  it.live('composeCalcs wires a linear calc chain; the result needs only the base state', () => {
10
6
  class Count extends State.make('count', S.Number) {}
11
7
  class Inputs extends StateGroup.make(Count) {}
@@ -22,23 +18,16 @@ it.live('composeCalcs wires a linear calc chain; the result needs only the base
22
18
  class Tripled extends Calc.make('Tripled', { inputs: [PlusOne], output: S.Number }) {}
23
19
  const TripledLive = Calc.live(Tripled, ({ PlusOne }) => PlusOne * 3)
24
20
 
25
- // Leaf-first, then upstreams in dependency order. The composed layer's only
26
- // remaining requirement is the base `count` store — which the type system
27
- // proves by letting us provide *only* the state group below.
28
- // The chain's only remaining requirement is the base `count` store — provide
29
- // (and expose, via provideMerge) just the state group and everything builds.
30
21
  const Chain = composeCalcs(TripledLive, PlusOneLive, DoubledLive)
31
22
  const TestLayer = Chain.pipe(Layer.provideMerge(StateGroup.live(Inputs, { count: 5 })))
32
23
 
33
24
  return Effect.gen(function* () {
34
25
  const source = yield* StateGroup.select(Inputs, 'count').store
35
26
  const tripled = yield* Tripled.store
36
- // count 5 → Doubled 10 → PlusOne 11 → Tripled 33
37
27
  expect(tripled.get()).toBe(33)
38
28
 
39
29
  source.set(2)
40
30
  yield* Effect.yieldNow()
41
- // count 2 → Doubled 4 → PlusOne 5 → Tripled 15
42
31
  expect(tripled.get()).toBe(15)
43
32
  }).pipe(Effect.provide(TestLayer))
44
33
  })
@@ -63,6 +52,6 @@ it.live('composeCalcs(leaf, upstream) matches the hand-written provideMerge chai
63
52
 
64
53
  return Effect.gen(function* () {
65
54
  const derived = yield* PlusDoubled.store
66
- expect(derived.get()).toBe(15) // 5 + (5*2), same as the manual-pipe test
55
+ expect(derived.get()).toBe(15)
67
56
  }).pipe(Effect.provide(TestLayer))
68
57
  })
@@ -1,25 +1,5 @@
1
1
  import { Layer } from 'effect'
2
2
 
3
- // Wiring a calc-on-calc chain (`FilteredAgents` ← `AllAgents` ← `AgentsQuery`)
4
- // is the one calc-graph shape that trips people up: each upstream calc's store
5
- // must be *provided into* the downstream live, in dependency order. A flat
6
- // `Layer.mergeAll(FilteredLive, AllLive, QueryLive)` does NOT do this — `mergeAll`
7
- // merges outputs but never provides members to one another, so the upstream
8
- // calc stores stay as unmet `Store<…>` requirements (the TS2322 the migration
9
- // hit). The correct shape is `leaf.pipe(provideMerge(u1), provideMerge(u2), …)`;
10
- // `composeCalcs` is that fold under one name so the right thing is the easy thing.
11
-
12
- /**
13
- * Compose a calc-dependency chain: provide each upstream calc's `.live` layer
14
- * into the downstream one, nearest dependency first. The result exposes every
15
- * calc store in the chain and requires only the chain's *external* inputs (the
16
- * base state stores) — never the intermediate calc stores.
17
- *
18
- * Order is leaf-first, then its upstreams in dependency order:
19
- * `composeCalcs(FilteredLive, AllLive, QueryLive)` ≡
20
- * `FilteredLive.pipe(Layer.provideMerge(AllLive), Layer.provideMerge(QueryLive))`.
21
- * For chains deeper than three upstreams, nest `composeCalcs`.
22
- */
23
3
  export function composeCalcs<LOut, LErr, LIn, AOut, AErr, AIn>(
24
4
  leaf: Layer.Layer<LOut, LErr, LIn>,
25
5
  upstream: Layer.Layer<AOut, AErr, AIn>,
@@ -33,20 +13,7 @@ export function composeCalcs<LOut, LErr, LIn, AOut, AErr, AIn, BOut, BErr, BIn>(
33
13
  LErr | AErr | BErr,
34
14
  BIn | Exclude<AIn | Exclude<LIn, AOut>, BOut>
35
15
  >
36
- export function composeCalcs<
37
- LOut,
38
- LErr,
39
- LIn,
40
- AOut,
41
- AErr,
42
- AIn,
43
- BOut,
44
- BErr,
45
- BIn,
46
- COut,
47
- CErr,
48
- CIn,
49
- >(
16
+ export function composeCalcs<LOut, LErr, LIn, AOut, AErr, AIn, BOut, BErr, BIn, COut, CErr, CIn>(
50
17
  leaf: Layer.Layer<LOut, LErr, LIn>,
51
18
  upstream1: Layer.Layer<AOut, AErr, AIn>,
52
19
  upstream2: Layer.Layer<BOut, BErr, BIn>,
@@ -60,8 +27,6 @@ export function composeCalcs(
60
27
  leaf: Layer.Layer<unknown, unknown, unknown>,
61
28
  ...upstream: ReadonlyArray<Layer.Layer<unknown, unknown, unknown>>
62
29
  ): Layer.Layer<unknown, unknown, unknown> {
63
- // Fold leaf-first: each upstream is provided into the accumulated downstream,
64
- // so dependency order is exactly the argument order.
65
30
  return upstream.reduce<Layer.Layer<unknown, unknown, unknown>>(
66
31
  (downstream, upstreamLayer) => Layer.provideMerge(downstream, upstreamLayer),
67
32
  leaf,
@@ -1,26 +1,13 @@
1
1
  import { Option } from 'effect'
2
2
  import { type AnyAsyncData, AsyncData } from './asyncData'
3
3
 
4
- // `QueryState` is the data-oriented value an `AsyncCalc` is driven by: the four
5
- // axes of a query, stored *flat and independent* so each can move on its own.
6
- // `data`/`error` survive a refetch (stale-while-revalidate for free); `isFetching`
7
- // (activity) and `isStale` (freshness) are orthogonal to both — which is exactly
8
- // what lets `invalidate` (flip `isStale`) and `refetch` (drive `isFetching`) be
9
- // separate operations. The matchable `AsyncData` union is *derived* from this
10
- // (`toAsyncData`), so view code and `RemoteState` keep their arm dispatch.
11
-
12
4
  export interface QueryState<A, E> {
13
- /** Last resolved value — kept across a refetch. */
14
5
  readonly data: Option.Option<A>
15
- /** Last failure — kept across a refetch. */
16
6
  readonly error: Option.Option<E>
17
- /** Activity axis: a run is in flight. */
18
7
  readonly isFetching: boolean
19
- /** Freshness axis: the value has been marked invalid (drives auto-refetch). */
20
8
  readonly isStale: boolean
21
9
  }
22
10
 
23
- /** The empty cell. `fetching` seeds the first-kick `Loading` for an enabled query. */
24
11
  export const empty = (fetching: boolean): QueryState<never, never> => ({
25
12
  data: Option.none(),
26
13
  error: Option.none(),
@@ -28,15 +15,6 @@ export const empty = (fetching: boolean): QueryState<never, never> => ({
28
15
  isStale: false,
29
16
  })
30
17
 
31
- /**
32
- * Project the flat state onto the public `AsyncData` union (the view contract) —
33
- * the single `Match`-able view of a query, so call sites keep `Match.tag`-ing the
34
- * arms. `error` wins over `data` so a failed refetch surfaces the `Error` arm,
35
- * matching the pre-`QueryState` driver; `isFetching` becomes the arm's
36
- * `refetching` flag. The data-less case is `Loading` while fetching (or for a
37
- * non-gated query, which always fetches) and `Idle` only for a switched-off
38
- * gateable query.
39
- */
40
18
  export const toAsyncData = <A, E>(state: QueryState<A, E>, gated: boolean): AnyAsyncData<A, E> =>
41
19
  Option.match(state.error, {
42
20
  onSome: (error) => AsyncData.error(error, state.isFetching),
@@ -47,6 +25,5 @@ export const toAsyncData = <A, E>(state: QueryState<A, E>, gated: boolean): AnyA
47
25
  }),
48
26
  })
49
27
 
50
- /** `Loading` in the React-Query sense: fetching with nothing to show yet. */
51
28
  export const isLoading = <A, E>(state: QueryState<A, E>): boolean =>
52
29
  state.isFetching && Option.isNone(state.data)