@playfast/reform 1.1.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.
- package/README.md +85 -55
- package/package.json +17 -17
- package/src/boundary/boundary.test.ts +62 -16
- package/src/boundary/boundary.ts +141 -32
- package/src/calc/asyncCalc.invalidate.test.ts +516 -18
- package/src/calc/asyncCalc.test.ts +207 -175
- package/src/calc/asyncCalc.ts +168 -39
- package/src/calc/calc.test.ts +3 -5
- package/src/calc/calc.ts +44 -18
- package/src/calc/calcFamily.test.ts +80 -22
- package/src/calc/calcFamily.ts +56 -25
- package/src/calc/compose.ts +1 -14
- package/src/channel/channel.ts +128 -11
- package/src/compose/composition.test.ts +19 -0
- package/src/compose/composition.ts +346 -62
- package/src/compose/props.ts +13 -6
- package/src/compose/provide.ts +187 -46
- package/src/compose/slot.ts +156 -19
- package/src/compose/structure.test.ts +6 -3
- package/src/compose/structure.ts +106 -13
- package/src/compose/ui.test.ts +19 -3
- package/src/compose/ui.ts +147 -54
- package/src/compose/ui.typecheck.ts +40 -4
- package/src/definition/definition.ts +22 -9
- package/src/event/event.fromSource.test.ts +172 -0
- package/src/event/event.test.ts +10 -0
- package/src/event/event.ts +102 -18
- package/src/feature/feature.mount.test.ts +123 -56
- package/src/feature/feature.test.ts +67 -63
- package/src/feature/feature.ts +1317 -197
- package/src/feature/feature.typecheck.ts +253 -61
- package/src/graph/closure.ts +403 -0
- package/src/index.ts +171 -245
- package/src/internal/bucketCache.ts +39 -0
- package/src/internal/capture.ts +9 -4
- package/src/internal/env.ts +1 -3
- package/src/internal/errors.ts +21 -10
- package/src/internal/queryDriver.ts +120 -15
- package/src/internal/queryDriverStore.ts +8 -5
- package/src/internal/queryDriverTypes.ts +3 -1
- package/src/internal/reuse.test.ts +10 -3
- package/src/internal/reuse.ts +5 -2
- package/src/internal/scheduler.ts +4 -1
- package/src/internal/sources.ts +25 -11
- package/src/internal/track.ts +3 -2
- package/src/internal.ts +222 -0
- package/src/namespace/namespace.ts +46 -25
- package/src/procedure/procedure.ts +38 -11
- package/src/reducer/reducer.ts +78 -34
- package/src/remote/remoteState.test.ts +515 -339
- package/src/remote/remoteState.ts +572 -107
- package/src/remote/remoteState.typecheck.ts +47 -23
- package/src/runtime/appRuntime.activation.test.ts +100 -0
- package/src/runtime/appRuntime.test.ts +157 -105
- package/src/runtime/appRuntime.ts +394 -33
- package/src/runtime/eventBudget.test.ts +1 -4
- package/src/runtime/eventBudget.ts +9 -8
- package/src/runtime/hardening.test.ts +4 -9
- package/src/runtime/instrumentation.test.ts +174 -192
- package/src/runtime/instrumentation.ts +6 -11
- package/src/runtime/loop.test.ts +36 -0
- package/src/runtime/loop.ts +69 -40
- package/src/scene/featureScene.test.ts +4 -2
- package/src/scene/scene.ts +234 -64
- package/src/scene/seedScene.test.ts +69 -86
- package/src/state/state.nominal.typecheck.ts +68 -0
- package/src/state/state.test.ts +17 -0
- package/src/state/state.ts +79 -26
- package/src/state/stateFamily.test.ts +14 -0
- package/src/state/stateFamily.ts +55 -22
- package/src/state/stateGroup.ts +53 -17
- package/src/state/token.ts +40 -10
- package/src/synced/syncedStore.ts +46 -23
- package/src/testkit/flight.testkit.ts +75 -0
- package/src/wire/tree.test.ts +8 -4
- package/src/wire/triggers.test.ts +6 -2
- package/src/wire/triggers.ts +14 -10
- package/src/calc/asyncCalcDefinitions.ts +0 -48
- package/src/channel/procedureRegistry.ts +0 -90
- package/src/feature/featureBinding.ts +0 -48
- package/src/internal/ctx.ts +0 -9
- package/src/remote/remoteStateDefinition.ts +0 -135
- package/src/remote/remoteStateLayers.ts +0 -118
- package/src/remote/remoteStateLiveTypes.ts +0 -59
- package/src/remote/remoteStateSend.ts +0 -64
package/src/boundary/boundary.ts
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
|
-
import { Context, Data, Effect, Layer, MutableRef, Predicate } from 'effect'
|
|
1
|
+
import { Context, Data, Effect, Equal, Layer, MutableRef, Option, Predicate } from 'effect'
|
|
2
2
|
import type { Effect as EffectType } from 'effect/Effect'
|
|
3
3
|
import { type Manifest, yieldableClass } from '../definition/definition'
|
|
4
4
|
import { resolveScheduler } from '../internal/scheduler'
|
|
5
|
-
import { type InputStores,
|
|
5
|
+
import { type InputStores, wireSources } from '../internal/sources'
|
|
6
6
|
import { makeDerivedStore, type Store } from '../internal/store'
|
|
7
7
|
import { readTracked } from '../internal/track'
|
|
8
|
-
import { type
|
|
9
|
-
import type { AnyValue } from '../internal/variance'
|
|
8
|
+
import { type AnySource, type SourceCapture, type SourceValue } from '../state/token'
|
|
10
9
|
|
|
11
10
|
export interface BoundaryPending {
|
|
12
11
|
readonly _tag: 'Pending'
|
|
@@ -23,29 +22,73 @@ export interface BoundaryReady {
|
|
|
23
22
|
|
|
24
23
|
export type BoundaryState = BoundaryPending | BoundaryErrored | BoundaryReady
|
|
25
24
|
|
|
25
|
+
export const BoundaryStoreTypeId: unique symbol = Symbol.for('reform/BoundaryStore')
|
|
26
|
+
export type BoundaryStoreTypeId = typeof BoundaryStoreTypeId
|
|
27
|
+
|
|
28
|
+
export interface BoundaryStore<N extends string> {
|
|
29
|
+
readonly [BoundaryStoreTypeId]: N
|
|
30
|
+
}
|
|
31
|
+
|
|
26
32
|
const pendingArm: BoundaryState = { _tag: 'Pending' }
|
|
27
33
|
const readyArm: BoundaryState = { _tag: 'Ready' }
|
|
28
34
|
const erroredArm = (errors: ReadonlyArray<unknown>): BoundaryState =>
|
|
29
35
|
Data.struct({ _tag: 'Errored' as const, errors: Data.array(errors) })
|
|
30
36
|
|
|
31
|
-
export type AnyLifecycleSource =
|
|
37
|
+
export type AnyLifecycleSource = AnySource
|
|
38
|
+
|
|
39
|
+
declare const BoundaryLifecycleMismatchTypeId: unique symbol
|
|
40
|
+
|
|
41
|
+
interface BoundaryLifecycleMismatch<Value> {
|
|
42
|
+
readonly [BoundaryLifecycleMismatchTypeId]: Value
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
type CheckedLifecycleValue<Value> = Value extends { readonly _tag: 'Idle' | 'Loading' }
|
|
46
|
+
? Value
|
|
47
|
+
: Value extends {
|
|
48
|
+
readonly _tag: 'Success'
|
|
49
|
+
readonly value: infer _Success
|
|
50
|
+
readonly refetching: boolean
|
|
51
|
+
}
|
|
52
|
+
? Value
|
|
53
|
+
: Value extends {
|
|
54
|
+
readonly _tag: 'Error'
|
|
55
|
+
readonly error: infer _Failure
|
|
56
|
+
readonly refetching: boolean
|
|
57
|
+
}
|
|
58
|
+
? Value
|
|
59
|
+
: BoundaryLifecycleMismatch<Value>
|
|
60
|
+
|
|
61
|
+
type CheckedLifecycleSource<Source extends AnySource> = [SourceValue<Source>] extends [never]
|
|
62
|
+
? BoundaryLifecycleMismatch<Source>
|
|
63
|
+
: SourceValue<Source> extends CheckedLifecycleValue<SourceValue<Source>>
|
|
64
|
+
? Source
|
|
65
|
+
: BoundaryLifecycleMismatch<SourceValue<Source>>
|
|
66
|
+
|
|
67
|
+
type CheckedLifecycleSources<Over extends ReadonlyArray<AnyLifecycleSource>> = {
|
|
68
|
+
readonly [Index in keyof Over]: Over[Index] extends AnySource
|
|
69
|
+
? CheckedLifecycleSource<Over[Index]>
|
|
70
|
+
: BoundaryLifecycleMismatch<Over[Index]>
|
|
71
|
+
}
|
|
32
72
|
|
|
33
73
|
export interface BoundaryManifest<N extends string> extends Manifest {
|
|
34
74
|
readonly kind: 'Boundary'
|
|
35
75
|
readonly name: N
|
|
36
76
|
}
|
|
37
77
|
|
|
38
|
-
export interface BoundaryClass<
|
|
39
|
-
extends
|
|
78
|
+
export interface BoundaryClass<
|
|
79
|
+
N extends string,
|
|
80
|
+
out Over extends ReadonlyArray<AnyLifecycleSource>,
|
|
81
|
+
> extends EffectType<BoundaryState, never, BoundaryStore<N>> {
|
|
40
82
|
new (): {}
|
|
41
83
|
readonly manifest: BoundaryManifest<N>
|
|
42
|
-
readonly store: Context.Tag<
|
|
84
|
+
readonly store: Context.Tag<BoundaryStore<N>, Store<BoundaryState>>
|
|
43
85
|
readonly name: N
|
|
44
86
|
readonly over: Over
|
|
87
|
+
readonly capture: <Result>(visit: SourceCapture<Result>) => Result
|
|
45
88
|
}
|
|
46
89
|
|
|
47
90
|
export interface BoundaryConfig<Over extends ReadonlyArray<AnyLifecycleSource>> {
|
|
48
|
-
readonly over: Over
|
|
91
|
+
readonly over: Over & CheckedLifecycleSources<Over>
|
|
49
92
|
}
|
|
50
93
|
|
|
51
94
|
export interface BoundaryOptions {
|
|
@@ -57,54 +100,120 @@ export const make = <const N extends string, const Over extends ReadonlyArray<An
|
|
|
57
100
|
name: N,
|
|
58
101
|
config: BoundaryConfig<Over>,
|
|
59
102
|
): BoundaryClass<N, Over> => {
|
|
60
|
-
const store = Context.GenericTag<
|
|
103
|
+
const store = Context.GenericTag<BoundaryStore<N>, Store<BoundaryState>>(
|
|
61
104
|
`reform/boundary/${name}`,
|
|
62
105
|
)
|
|
63
106
|
const manifest: BoundaryManifest<N> = { kind: 'Boundary', name }
|
|
64
107
|
const read = Effect.flatMap(store, readTracked)
|
|
65
|
-
|
|
108
|
+
const boundary: BoundaryClass<N, Over> = yieldableClass(read, {
|
|
109
|
+
manifest,
|
|
110
|
+
store,
|
|
111
|
+
name,
|
|
112
|
+
over: config.over,
|
|
113
|
+
capture: <Result>(visit: SourceCapture<Result>): Result => visit(boundary),
|
|
114
|
+
})
|
|
115
|
+
return boundary
|
|
66
116
|
}
|
|
67
117
|
|
|
68
118
|
const tagOf = (arm: unknown): string =>
|
|
69
119
|
Predicate.hasProperty(arm, '_tag') && Predicate.isString(arm._tag) ? arm._tag : ''
|
|
70
|
-
const errorOf = (arm: unknown):
|
|
71
|
-
Predicate.hasProperty(arm, 'error') && tagOf(arm) === 'Error'
|
|
120
|
+
const errorOf = (arm: unknown): Option.Option<unknown> =>
|
|
121
|
+
Predicate.hasProperty(arm, 'error') && tagOf(arm) === 'Error'
|
|
122
|
+
? Option.some(arm.error)
|
|
123
|
+
: Option.none()
|
|
124
|
+
|
|
125
|
+
interface SlotUpdate {
|
|
126
|
+
readonly slot: number
|
|
127
|
+
readonly arm: unknown
|
|
128
|
+
}
|
|
72
129
|
|
|
73
130
|
export const live = <N extends string, Over extends ReadonlyArray<AnyLifecycleSource>>(
|
|
74
131
|
boundary: BoundaryClass<N, Over>,
|
|
75
132
|
options: BoundaryOptions = {},
|
|
76
|
-
): Layer.Layer<
|
|
133
|
+
): Layer.Layer<BoundaryStore<N>, never, InputStores<Over>> =>
|
|
77
134
|
Layer.scoped(
|
|
78
135
|
boundary.store,
|
|
79
136
|
Effect.gen(function* () {
|
|
80
137
|
const scheduler = yield* resolveScheduler
|
|
81
|
-
const
|
|
138
|
+
const sourceWires = yield* Effect.forEach(boundary.over, (source) => wireSources([source]))
|
|
139
|
+
const readSource = (source: (typeof sourceWires)[number]): unknown =>
|
|
140
|
+
source.keyOf(source.snapshot())[0]
|
|
141
|
+
const arms = sourceWires.map((source) => MutableRef.make(readSource(source)))
|
|
142
|
+
const loadingCount = MutableRef.make(
|
|
143
|
+
arms.reduce((count, arm) => count + (tagOf(MutableRef.get(arm)) === 'Loading' ? 1 : 0), 0),
|
|
144
|
+
)
|
|
145
|
+
const errorCount = MutableRef.make(
|
|
146
|
+
arms.reduce(
|
|
147
|
+
(count, arm) => count + (Option.isSome(errorOf(MutableRef.get(arm))) ? 1 : 0),
|
|
148
|
+
0,
|
|
149
|
+
),
|
|
150
|
+
)
|
|
151
|
+
const collectErrors = (): ReadonlyArray<unknown> =>
|
|
152
|
+
arms.flatMap((arm) => Option.toArray(errorOf(MutableRef.get(arm))))
|
|
153
|
+
const errorArm = MutableRef.make(erroredArm(collectErrors()))
|
|
154
|
+
const updateSlot = (update: SlotUpdate): void => {
|
|
155
|
+
const armRef = arms[update.slot]
|
|
156
|
+
if (armRef === undefined) {
|
|
157
|
+
return
|
|
158
|
+
}
|
|
159
|
+
const previous = MutableRef.get(armRef)
|
|
160
|
+
if (Equal.equals(previous, update.arm)) {
|
|
161
|
+
return
|
|
162
|
+
}
|
|
163
|
+
MutableRef.set(armRef, update.arm)
|
|
164
|
+
MutableRef.update(
|
|
165
|
+
loadingCount,
|
|
166
|
+
(count) =>
|
|
167
|
+
count +
|
|
168
|
+
(tagOf(update.arm) === 'Loading' ? 1 : 0) -
|
|
169
|
+
(tagOf(previous) === 'Loading' ? 1 : 0),
|
|
170
|
+
)
|
|
171
|
+
const previousError = errorOf(previous)
|
|
172
|
+
const nextError = errorOf(update.arm)
|
|
173
|
+
if (!Equal.equals(previousError, nextError)) {
|
|
174
|
+
MutableRef.update(
|
|
175
|
+
errorCount,
|
|
176
|
+
(count) =>
|
|
177
|
+
count + (Option.isSome(nextError) ? 1 : 0) - (Option.isSome(previousError) ? 1 : 0),
|
|
178
|
+
)
|
|
179
|
+
MutableRef.set(errorArm, erroredArm(collectErrors()))
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
const synchronize = (): void =>
|
|
183
|
+
sourceWires.forEach((source, slot) => updateSlot({ slot, arm: readSource(source) }))
|
|
82
184
|
|
|
83
185
|
const latched = MutableRef.make(false)
|
|
84
|
-
const
|
|
85
|
-
{ readonly key: ReadonlyArray<unknown>; readonly value: BoundaryState } | undefined
|
|
86
|
-
>(undefined)
|
|
186
|
+
const incremental = MutableRef.make(false)
|
|
87
187
|
const recompute = (): BoundaryState => {
|
|
88
188
|
if (options.once === true && MutableRef.get(latched)) {
|
|
89
189
|
return readyArm
|
|
90
190
|
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
if (prev !== undefined && sameKey(arms, prev.key)) {
|
|
94
|
-
return prev.value
|
|
191
|
+
if (!MutableRef.get(incremental)) {
|
|
192
|
+
synchronize()
|
|
95
193
|
}
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
?
|
|
100
|
-
:
|
|
101
|
-
? pendingArm
|
|
102
|
-
: readyArm
|
|
103
|
-
MutableRef.set(memo, { key: arms, value: mergedArm })
|
|
104
|
-
return mergedArm
|
|
194
|
+
return MutableRef.get(errorCount) > 0
|
|
195
|
+
? MutableRef.get(errorArm)
|
|
196
|
+
: MutableRef.get(loadingCount) > 0
|
|
197
|
+
? pendingArm
|
|
198
|
+
: readyArm
|
|
105
199
|
}
|
|
106
200
|
|
|
107
|
-
const
|
|
201
|
+
const subscribe = (onChange: () => void): (() => void) => {
|
|
202
|
+
const publish = (): void => {
|
|
203
|
+
MutableRef.set(incremental, true)
|
|
204
|
+
onChange()
|
|
205
|
+
MutableRef.set(incremental, false)
|
|
206
|
+
}
|
|
207
|
+
const settle = (): void => scheduler.schedule([publish])
|
|
208
|
+
const unsubscribes = sourceWires.map((source, sourceIndex) =>
|
|
209
|
+
source.subscribe(() => {
|
|
210
|
+
updateSlot({ slot: sourceIndex, arm: readSource(source) })
|
|
211
|
+
scheduler.schedule([settle])
|
|
212
|
+
}),
|
|
213
|
+
)
|
|
214
|
+
return () => unsubscribes.forEach((unsubscribe) => unsubscribe())
|
|
215
|
+
}
|
|
216
|
+
const derived = makeDerivedStore(recompute, subscribe, scheduler)
|
|
108
217
|
yield* Effect.addFinalizer(() => Effect.sync(derived.unsubscribe))
|
|
109
218
|
|
|
110
219
|
if (options.once === true) {
|