@playfast/reform 1.2.0 → 1.3.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/boundary/boundary.d.ts +64 -0
- package/dist/boundary/boundary.d.ts.map +1 -0
- package/dist/boundary/boundary.js +102 -0
- package/dist/boundary/boundary.js.map +1 -0
- package/dist/calc/asyncCalc.d.ts +21 -0
- package/dist/calc/asyncCalc.d.ts.map +1 -0
- package/dist/calc/asyncCalc.js +90 -0
- package/dist/calc/asyncCalc.js.map +1 -0
- package/dist/calc/asyncCalcTypes.d.ts +78 -0
- package/dist/calc/asyncCalcTypes.d.ts.map +1 -0
- package/dist/calc/asyncCalcTypes.js +2 -0
- package/dist/calc/asyncCalcTypes.js.map +1 -0
- package/dist/calc/asyncData.d.ts +29 -0
- package/dist/calc/asyncData.d.ts.map +1 -0
- package/dist/calc/asyncData.js +21 -0
- package/dist/calc/asyncData.js.map +1 -0
- package/dist/calc/calc.d.ts +39 -0
- package/dist/calc/calc.d.ts.map +1 -0
- package/dist/calc/calc.js +48 -0
- package/dist/calc/calc.js.map +1 -0
- package/dist/calc/calcFamily.d.ts +40 -0
- package/dist/calc/calcFamily.d.ts.map +1 -0
- package/dist/calc/calcFamily.js +113 -0
- package/dist/calc/calcFamily.js.map +1 -0
- package/dist/calc/compose.d.ts +5 -0
- package/dist/calc/compose.d.ts.map +1 -0
- package/dist/calc/compose.js +5 -0
- package/dist/calc/compose.js.map +1 -0
- package/dist/calc/queryState.d.ts +12 -0
- package/dist/calc/queryState.d.ts.map +1 -0
- package/dist/calc/queryState.js +17 -0
- package/dist/calc/queryState.js.map +1 -0
- package/dist/channel/channel.d.ts +51 -0
- package/dist/channel/channel.d.ts.map +1 -0
- package/dist/channel/channel.js +86 -0
- package/dist/channel/channel.js.map +1 -0
- package/dist/channel/procedures.d.ts +24 -0
- package/dist/channel/procedures.d.ts.map +1 -0
- package/dist/channel/procedures.js +82 -0
- package/dist/channel/procedures.js.map +1 -0
- package/dist/compose/composition.d.ts +12 -0
- package/dist/compose/composition.d.ts.map +1 -0
- package/dist/compose/composition.js +56 -0
- package/dist/compose/composition.js.map +1 -0
- package/dist/compose/compositionTypes.d.ts +127 -0
- package/dist/compose/compositionTypes.d.ts.map +1 -0
- package/dist/compose/compositionTypes.js +2 -0
- package/dist/compose/compositionTypes.js.map +1 -0
- package/dist/compose/host.d.ts +6 -0
- package/dist/compose/host.d.ts.map +1 -0
- package/dist/compose/host.js +2 -0
- package/dist/compose/host.js.map +1 -0
- package/dist/compose/props.d.ts +10 -0
- package/dist/compose/props.d.ts.map +1 -0
- package/dist/compose/props.js +6 -0
- package/dist/compose/props.js.map +1 -0
- package/dist/compose/provide.d.ts +18 -0
- package/dist/compose/provide.d.ts.map +1 -0
- package/dist/compose/provide.js +49 -0
- package/dist/compose/provide.js.map +1 -0
- package/dist/compose/slot.d.ts +70 -0
- package/dist/compose/slot.d.ts.map +1 -0
- package/dist/compose/slot.js +26 -0
- package/dist/compose/slot.js.map +1 -0
- package/dist/compose/structure.d.ts +51 -0
- package/dist/compose/structure.d.ts.map +1 -0
- package/dist/compose/structure.js +92 -0
- package/dist/compose/structure.js.map +1 -0
- package/dist/compose/ui.d.ts +112 -0
- package/dist/compose/ui.d.ts.map +1 -0
- package/dist/compose/ui.js +47 -0
- package/dist/compose/ui.js.map +1 -0
- package/dist/definition/definition.d.ts +11 -0
- package/dist/definition/definition.d.ts.map +1 -0
- package/dist/definition/definition.js +35 -0
- package/dist/definition/definition.js.map +1 -0
- package/dist/event/event.d.ts +52 -0
- package/dist/event/event.d.ts.map +1 -0
- package/dist/event/event.js +67 -0
- package/dist/event/event.js.map +1 -0
- package/dist/event/eventGroup.d.ts +8 -0
- package/dist/event/eventGroup.d.ts.map +1 -0
- package/dist/event/eventGroup.js +3 -0
- package/dist/event/eventGroup.js.map +1 -0
- package/dist/feature/feature.d.ts +27 -0
- package/dist/feature/feature.d.ts.map +1 -0
- package/dist/feature/feature.js +20 -0
- package/dist/feature/feature.js.map +1 -0
- package/dist/feature/featureBinding.d.ts +85 -0
- package/dist/feature/featureBinding.d.ts.map +1 -0
- package/dist/feature/featureBinding.js +10 -0
- package/dist/feature/featureBinding.js.map +1 -0
- package/dist/feature/featureClass.d.ts +10 -0
- package/dist/feature/featureClass.d.ts.map +1 -0
- package/dist/feature/featureClass.js +2 -0
- package/dist/feature/featureClass.js.map +1 -0
- package/dist/feature/featureConfig.d.ts +86 -0
- package/dist/feature/featureConfig.d.ts.map +1 -0
- package/dist/feature/featureConfig.js +13 -0
- package/dist/feature/featureConfig.js.map +1 -0
- package/dist/feature/featureFactory.d.ts +15 -0
- package/dist/feature/featureFactory.d.ts.map +1 -0
- package/dist/feature/featureFactory.js +59 -0
- package/dist/feature/featureFactory.js.map +1 -0
- package/dist/feature/featureModule.d.ts +28 -0
- package/dist/feature/featureModule.d.ts.map +1 -0
- package/dist/feature/featureModule.js +33 -0
- package/dist/feature/featureModule.js.map +1 -0
- package/dist/feature/featureNativeTree.d.ts +26 -0
- package/dist/feature/featureNativeTree.d.ts.map +1 -0
- package/dist/feature/featureNativeTree.js +2 -0
- package/dist/feature/featureNativeTree.js.map +1 -0
- package/dist/feature/featureRequirement.d.ts +43 -0
- package/dist/feature/featureRequirement.d.ts.map +1 -0
- package/dist/feature/featureRequirement.js +6 -0
- package/dist/feature/featureRequirement.js.map +1 -0
- package/dist/feature/featureVariants.d.ts +9 -0
- package/dist/feature/featureVariants.d.ts.map +1 -0
- package/dist/feature/featureVariants.js +59 -0
- package/dist/feature/featureVariants.js.map +1 -0
- package/dist/feature/mountFeature.d.ts +10 -0
- package/dist/feature/mountFeature.d.ts.map +1 -0
- package/dist/feature/mountFeature.js +14 -0
- package/dist/feature/mountFeature.js.map +1 -0
- package/dist/graph/closure.d.ts +77 -0
- package/dist/graph/closure.d.ts.map +1 -0
- package/dist/graph/closure.js +4 -0
- package/dist/graph/closure.js.map +1 -0
- package/dist/graph/services.d.ts +30 -0
- package/dist/graph/services.d.ts.map +1 -0
- package/dist/graph/services.js +2 -0
- package/dist/graph/services.js.map +1 -0
- package/dist/graph/summary.d.ts +60 -0
- package/dist/graph/summary.d.ts.map +1 -0
- package/dist/graph/summary.js +7 -0
- package/dist/graph/summary.js.map +1 -0
- package/dist/index.d.ts +51 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +34 -0
- package/dist/index.js.map +1 -0
- package/dist/internal/bucketCache.d.ts +19 -0
- package/dist/internal/bucketCache.d.ts.map +1 -0
- package/dist/internal/bucketCache.js +20 -0
- package/dist/internal/bucketCache.js.map +1 -0
- package/dist/internal/capture.d.ts +17 -0
- package/dist/internal/capture.d.ts.map +1 -0
- package/dist/internal/capture.js +5 -0
- package/dist/internal/capture.js.map +1 -0
- package/dist/internal/env.d.ts +2 -0
- package/dist/internal/env.d.ts.map +1 -0
- package/dist/internal/env.js +5 -0
- package/dist/internal/env.js.map +1 -0
- package/dist/internal/errors.d.ts +48 -0
- package/dist/internal/errors.d.ts.map +1 -0
- package/dist/internal/errors.js +69 -0
- package/dist/internal/errors.js.map +1 -0
- package/dist/internal/inspect.d.ts +5 -0
- package/dist/internal/inspect.d.ts.map +1 -0
- package/dist/internal/inspect.js +20 -0
- package/dist/internal/inspect.js.map +1 -0
- package/dist/internal/queryDriver.d.ts +7 -0
- package/dist/internal/queryDriver.d.ts.map +1 -0
- package/dist/internal/queryDriver.js +198 -0
- package/dist/internal/queryDriver.js.map +1 -0
- package/dist/internal/queryDriverHydrate.d.ts +17 -0
- package/dist/internal/queryDriverHydrate.d.ts.map +1 -0
- package/dist/internal/queryDriverHydrate.js +19 -0
- package/dist/internal/queryDriverHydrate.js.map +1 -0
- package/dist/internal/queryDriverStore.d.ts +54 -0
- package/dist/internal/queryDriverStore.d.ts.map +1 -0
- package/dist/internal/queryDriverStore.js +77 -0
- package/dist/internal/queryDriverStore.js.map +1 -0
- package/dist/internal/queryDriverTypes.d.ts +41 -0
- package/dist/internal/queryDriverTypes.d.ts.map +1 -0
- package/dist/internal/queryDriverTypes.js +9 -0
- package/dist/internal/queryDriverTypes.js.map +1 -0
- package/dist/internal/queryEvents.d.ts +12 -0
- package/dist/internal/queryEvents.d.ts.map +1 -0
- package/dist/internal/queryEvents.js +11 -0
- package/dist/internal/queryEvents.js.map +1 -0
- package/dist/internal/queryStore.d.ts +13 -0
- package/dist/internal/queryStore.d.ts.map +1 -0
- package/dist/internal/queryStore.js +11 -0
- package/dist/internal/queryStore.js.map +1 -0
- package/dist/internal/reuse.d.ts +2 -0
- package/dist/internal/reuse.d.ts.map +1 -0
- package/dist/internal/reuse.js +55 -0
- package/dist/internal/reuse.js.map +1 -0
- package/dist/internal/scheduler.d.ts +13 -0
- package/dist/internal/scheduler.d.ts.map +1 -0
- package/dist/internal/scheduler.js +38 -0
- package/dist/internal/scheduler.js.map +1 -0
- package/dist/internal/seeds.d.ts +3 -0
- package/dist/internal/seeds.d.ts.map +1 -0
- package/dist/internal/seeds.js +3 -0
- package/dist/internal/seeds.js.map +1 -0
- package/dist/internal/sources.d.ts +20 -0
- package/dist/internal/sources.d.ts.map +1 -0
- package/dist/internal/sources.js +39 -0
- package/dist/internal/sources.js.map +1 -0
- package/dist/internal/stateRegistry.d.ts +2 -0
- package/dist/internal/stateRegistry.d.ts.map +1 -0
- package/dist/internal/stateRegistry.js +15 -0
- package/dist/internal/stateRegistry.js.map +1 -0
- package/dist/internal/store.d.ts +15 -0
- package/dist/internal/store.d.ts.map +1 -0
- package/dist/internal/store.js +61 -0
- package/dist/internal/store.js.map +1 -0
- package/dist/internal/track.d.ts +15 -0
- package/dist/internal/track.d.ts.map +1 -0
- package/dist/internal/track.js +11 -0
- package/dist/internal/track.js.map +1 -0
- package/dist/internal/variance.d.ts +4 -0
- package/dist/internal/variance.d.ts.map +1 -0
- package/dist/internal/variance.js +2 -0
- package/dist/internal/variance.js.map +1 -0
- package/dist/internal.d.ts +42 -0
- package/dist/internal.d.ts.map +1 -0
- package/dist/internal.js +58 -0
- package/dist/internal.js.map +1 -0
- package/dist/namespace/namespace.d.ts +24 -0
- package/dist/namespace/namespace.d.ts.map +1 -0
- package/dist/namespace/namespace.js +20 -0
- package/dist/namespace/namespace.js.map +1 -0
- package/dist/procedure/procedure.d.ts +32 -0
- package/dist/procedure/procedure.d.ts.map +1 -0
- package/dist/procedure/procedure.js +34 -0
- package/dist/procedure/procedure.js.map +1 -0
- package/dist/reducer/reducer.d.ts +35 -0
- package/dist/reducer/reducer.d.ts.map +1 -0
- package/dist/reducer/reducer.js +73 -0
- package/dist/reducer/reducer.js.map +1 -0
- package/dist/remote/pendingQueue.d.ts +20 -0
- package/dist/remote/pendingQueue.d.ts.map +1 -0
- package/dist/remote/pendingQueue.js +113 -0
- package/dist/remote/pendingQueue.js.map +1 -0
- package/dist/remote/remoteState.d.ts +13 -0
- package/dist/remote/remoteState.d.ts.map +1 -0
- package/dist/remote/remoteState.js +176 -0
- package/dist/remote/remoteState.js.map +1 -0
- package/dist/remote/remoteStateMake.d.ts +8 -0
- package/dist/remote/remoteStateMake.d.ts.map +1 -0
- package/dist/remote/remoteStateMake.js +46 -0
- package/dist/remote/remoteStateMake.js.map +1 -0
- package/dist/remote/remoteStateSend.d.ts +41 -0
- package/dist/remote/remoteStateSend.d.ts.map +1 -0
- package/dist/remote/remoteStateSend.js +67 -0
- package/dist/remote/remoteStateSend.js.map +1 -0
- package/dist/remote/remoteStateTypes.d.ts +103 -0
- package/dist/remote/remoteStateTypes.d.ts.map +1 -0
- package/dist/remote/remoteStateTypes.js +4 -0
- package/dist/remote/remoteStateTypes.js.map +1 -0
- package/dist/runtime/appRuntime.d.ts +10 -0
- package/dist/runtime/appRuntime.d.ts.map +1 -0
- package/dist/runtime/appRuntime.js +6 -0
- package/dist/runtime/appRuntime.js.map +1 -0
- package/dist/runtime/bus.d.ts +17 -0
- package/dist/runtime/bus.d.ts.map +1 -0
- package/dist/runtime/bus.js +12 -0
- package/dist/runtime/bus.js.map +1 -0
- package/dist/runtime/capturedAppRuntime.d.ts +7 -0
- package/dist/runtime/capturedAppRuntime.d.ts.map +1 -0
- package/dist/runtime/capturedAppRuntime.js +151 -0
- package/dist/runtime/capturedAppRuntime.js.map +1 -0
- package/dist/runtime/eventBudget.d.ts +32 -0
- package/dist/runtime/eventBudget.d.ts.map +1 -0
- package/dist/runtime/eventBudget.js +67 -0
- package/dist/runtime/eventBudget.js.map +1 -0
- package/dist/runtime/featureMount.d.ts +16 -0
- package/dist/runtime/featureMount.d.ts.map +1 -0
- package/dist/runtime/featureMount.js +54 -0
- package/dist/runtime/featureMount.js.map +1 -0
- package/dist/runtime/instrumentation.d.ts +18 -0
- package/dist/runtime/instrumentation.d.ts.map +1 -0
- package/dist/runtime/instrumentation.js +16 -0
- package/dist/runtime/instrumentation.js.map +1 -0
- package/dist/runtime/loop.d.ts +32 -0
- package/dist/runtime/loop.d.ts.map +1 -0
- package/dist/runtime/loop.js +128 -0
- package/dist/runtime/loop.js.map +1 -0
- package/dist/runtime/queries.d.ts +20 -0
- package/dist/runtime/queries.d.ts.map +1 -0
- package/dist/runtime/queries.js +21 -0
- package/dist/runtime/queries.js.map +1 -0
- package/dist/runtime/runtimeHandle.d.ts +56 -0
- package/dist/runtime/runtimeHandle.d.ts.map +1 -0
- package/dist/runtime/runtimeHandle.js +2 -0
- package/dist/runtime/runtimeHandle.js.map +1 -0
- package/dist/scene/scene.d.ts +55 -0
- package/dist/scene/scene.d.ts.map +1 -0
- package/dist/scene/scene.js +74 -0
- package/dist/scene/scene.js.map +1 -0
- package/dist/state/state.d.ts +54 -0
- package/dist/state/state.d.ts.map +1 -0
- package/dist/state/state.js +41 -0
- package/dist/state/state.js.map +1 -0
- package/dist/state/stateFamily.d.ts +59 -0
- package/dist/state/stateFamily.d.ts.map +1 -0
- package/dist/state/stateFamily.js +91 -0
- package/dist/state/stateFamily.js.map +1 -0
- package/dist/state/stateGroup.d.ts +36 -0
- package/dist/state/stateGroup.d.ts.map +1 -0
- package/dist/state/stateGroup.js +33 -0
- package/dist/state/stateGroup.js.map +1 -0
- package/dist/state/token.d.ts +35 -0
- package/dist/state/token.d.ts.map +1 -0
- package/dist/state/token.js +18 -0
- package/dist/state/token.js.map +1 -0
- package/dist/synced/syncedStore.d.ts +44 -0
- package/dist/synced/syncedStore.d.ts.map +1 -0
- package/dist/synced/syncedStore.js +32 -0
- package/dist/synced/syncedStore.js.map +1 -0
- package/dist/testkit/flight.testkit.d.ts +18 -0
- package/dist/testkit/flight.testkit.d.ts.map +1 -0
- package/dist/testkit/flight.testkit.js +45 -0
- package/dist/testkit/flight.testkit.js.map +1 -0
- package/dist/ui/node.d.ts +3 -0
- package/dist/ui/node.d.ts.map +1 -0
- package/dist/ui/node.js +2 -0
- package/dist/ui/node.js.map +1 -0
- package/dist/ui/trigger.d.ts +2 -0
- package/dist/ui/trigger.d.ts.map +1 -0
- package/dist/ui/trigger.js +2 -0
- package/dist/ui/trigger.js.map +1 -0
- package/dist/wire/tree.d.ts +32 -0
- package/dist/wire/tree.d.ts.map +1 -0
- package/dist/wire/tree.js +59 -0
- package/dist/wire/tree.js.map +1 -0
- package/dist/wire/triggers.d.ts +25 -0
- package/dist/wire/triggers.d.ts.map +1 -0
- package/dist/wire/triggers.js +31 -0
- package/dist/wire/triggers.js.map +1 -0
- package/package.json +17 -5
- package/src/boundary/boundary.test.ts +26 -23
- package/src/calc/asyncCalc.invalidate.test.ts +258 -55
- package/src/calc/asyncCalc.test.ts +225 -85
- package/src/calc/asyncCalc.ts +27 -141
- package/src/calc/asyncCalcTypes.test.ts +64 -0
- package/src/calc/asyncCalcTypes.ts +139 -0
- package/src/calc/calc.test.ts +10 -11
- package/src/calc/calcFamily.test.ts +12 -12
- package/src/channel/channel.ts +8 -106
- package/src/channel/procedures.ts +108 -0
- package/src/compose/composition.ts +49 -246
- package/src/compose/compositionTypes.ts +249 -0
- package/src/event/event.fromSource.test.ts +12 -12
- package/src/feature/feature.ts +50 -1210
- package/src/feature/feature.typecheck.ts +2 -61
- package/src/feature/featureBinding.ts +158 -0
- package/src/feature/featureClass.ts +162 -0
- package/src/feature/featureConfig.ts +192 -0
- package/src/feature/featureFactory.ts +163 -0
- package/src/feature/featureModule.ts +136 -0
- package/src/feature/featureModule.typecheck.ts +63 -0
- package/src/feature/featureNativeTree.ts +152 -0
- package/src/feature/featureRequirement.ts +82 -0
- package/src/feature/featureVariants.ts +234 -0
- package/src/feature/mountFeature.ts +54 -0
- package/src/graph/closure.ts +45 -226
- package/src/graph/services.ts +95 -0
- package/src/graph/summary.ts +134 -0
- package/src/internal/queryDriver.ts +20 -89
- package/src/internal/queryDriverHydrate.ts +45 -0
- package/src/internal/queryDriverTypes.ts +8 -10
- package/src/internal/store.test.ts +27 -6
- package/src/remote/pendingQueue.ts +145 -0
- package/src/remote/remoteState.test.ts +28 -22
- package/src/remote/remoteState.ts +63 -489
- package/src/remote/remoteStateMake.ts +97 -0
- package/src/remote/remoteStateSend.ts +122 -0
- package/src/remote/remoteStateTypes.ts +155 -0
- package/src/runtime/appRuntime.activation.test.ts +9 -5
- package/src/runtime/appRuntime.test.ts +40 -16
- package/src/runtime/appRuntime.ts +17 -472
- package/src/runtime/capturedAppRuntime.ts +274 -0
- package/src/runtime/eventBudget.test.ts +38 -8
- package/src/runtime/featureMount.ts +94 -0
- package/src/runtime/hardening.test.ts +18 -4
- package/src/runtime/instrumentation.test.ts +49 -12
- package/src/runtime/loop.test.ts +51 -10
- package/src/runtime/runtimeHandle.ts +124 -0
- package/src/state/stateFamily.test.ts +25 -8
- package/src/testkit/flight.testkit.ts +9 -0
package/src/runtime/loop.test.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { expect, it } from '@effect/vitest'
|
|
2
2
|
import { Duration, Effect, Layer, Schema as S, TestClock } from 'effect'
|
|
3
3
|
import { Channel, Engine, Event, Procedure, publish, Reducer, State } from '../index'
|
|
4
|
+
import { makeFlightGate, until } from '../testkit/flight.testkit'
|
|
4
5
|
|
|
5
6
|
it.live('a frame of events drains in one pass with a single notification flush', () => {
|
|
6
7
|
class Counter extends State.make('counter', S.Number) {}
|
|
@@ -22,7 +23,11 @@ it.live('a frame of events drains in one pass with a single notification flush',
|
|
|
22
23
|
yield* publish('Normal', Event.construct(Bumped, { to: 1 }))
|
|
23
24
|
yield* publish('Normal', Event.construct(Bumped, { to: 2 }))
|
|
24
25
|
yield* publish('Normal', Event.construct(Bumped, { to: 3 }))
|
|
25
|
-
|
|
26
|
+
// The flush is the frame's last act, so one notification means the fold is done.
|
|
27
|
+
yield* until(
|
|
28
|
+
() => notifications.n,
|
|
29
|
+
(n) => n >= 1,
|
|
30
|
+
)
|
|
26
31
|
|
|
27
32
|
expect(store.get()).toBe(3)
|
|
28
33
|
expect(notifications.n).toBe(1)
|
|
@@ -34,11 +39,12 @@ it.live('an exclusive channel serializes procedures that share it', () => {
|
|
|
34
39
|
class StartB extends Event.make('StartB', S.Struct({})) {}
|
|
35
40
|
class Exclusive extends Channel.make('Exclusive', { policy: { _tag: 'exclusive' } }) {}
|
|
36
41
|
|
|
42
|
+
const gate = makeFlightGate()
|
|
37
43
|
const concurrency = { active: 0, max: 0 }
|
|
38
44
|
const guarded = function* () {
|
|
39
45
|
concurrency.active += 1
|
|
40
46
|
concurrency.max = Math.max(concurrency.max, concurrency.active)
|
|
41
|
-
yield*
|
|
47
|
+
yield* gate.through(Effect.void)
|
|
42
48
|
concurrency.active -= 1
|
|
43
49
|
}
|
|
44
50
|
|
|
@@ -52,9 +58,20 @@ it.live('an exclusive channel serializes procedures that share it', () => {
|
|
|
52
58
|
)
|
|
53
59
|
|
|
54
60
|
return Effect.gen(function* () {
|
|
61
|
+
// Parking the first run IS the overlap: a shared lane that is not exclusive
|
|
62
|
+
// would start the second procedure while the first sits at the gate.
|
|
63
|
+
yield* gate.hold
|
|
55
64
|
yield* publish('Normal', Event.construct(StartA, {}))
|
|
56
65
|
yield* publish('Normal', Event.construct(StartB, {}))
|
|
57
|
-
yield*
|
|
66
|
+
yield* gate.awaitEntry(1)
|
|
67
|
+
expect(gate.entered()).toBe(1)
|
|
68
|
+
|
|
69
|
+
yield* gate.release
|
|
70
|
+
yield* gate.awaitEntry(2)
|
|
71
|
+
yield* until(
|
|
72
|
+
() => concurrency.active,
|
|
73
|
+
(active) => active === 0,
|
|
74
|
+
)
|
|
58
75
|
expect(concurrency.max).toBe(1)
|
|
59
76
|
}).pipe(Effect.provide(TestLayer))
|
|
60
77
|
})
|
|
@@ -79,7 +96,11 @@ it.live('two procedures sharing a channel each run once for one shared event', (
|
|
|
79
96
|
|
|
80
97
|
return Effect.gen(function* () {
|
|
81
98
|
yield* publish('High', Event.construct(Shared, {}))
|
|
82
|
-
yield*
|
|
99
|
+
yield* until(
|
|
100
|
+
() => runs.a + runs.b,
|
|
101
|
+
(total) => total >= 2,
|
|
102
|
+
)
|
|
103
|
+
// A duplicated offer would land in the same frame and double one of them.
|
|
83
104
|
expect(runs.a).toBe(1)
|
|
84
105
|
expect(runs.b).toBe(1)
|
|
85
106
|
}).pipe(Effect.provide(TestLayer))
|
|
@@ -126,10 +147,12 @@ it.live('a reducer ignores events outside its declared set (no-op)', () => {
|
|
|
126
147
|
return Effect.gen(function* () {
|
|
127
148
|
const store = yield* Counter.store
|
|
128
149
|
yield* publish('Normal', Event.construct(Unrelated, {}))
|
|
129
|
-
yield* Effect.sleep(Duration.millis(10))
|
|
130
|
-
expect(store.get()).toBe(0)
|
|
131
150
|
yield* publish('Normal', Event.construct(Bump, {}))
|
|
132
|
-
yield*
|
|
151
|
+
yield* until(
|
|
152
|
+
() => store.get(),
|
|
153
|
+
(n) => n > 0,
|
|
154
|
+
)
|
|
155
|
+
// Both events fold in one frame: a reducer that also took Unrelated reads 2.
|
|
133
156
|
expect(store.get()).toBe(1)
|
|
134
157
|
}).pipe(Effect.provide(TestLayer))
|
|
135
158
|
})
|
|
@@ -166,6 +189,7 @@ it.live('a fold returning the previous reference keeps the exact store value', (
|
|
|
166
189
|
S.TaggedStruct('Open', { revision: S.Number }),
|
|
167
190
|
)
|
|
168
191
|
class ViewerState extends State.make('viewer', Viewer) {}
|
|
192
|
+
class Marker extends State.make('marker', S.Number) {}
|
|
169
193
|
class Poked extends Event.make('Poked', S.Struct({})) {}
|
|
170
194
|
class PokeReducer extends Reducer.make('PokeReducer', {
|
|
171
195
|
states: [ViewerState],
|
|
@@ -175,20 +199,37 @@ it.live('a fold returning the previous reference keeps the exact store value', (
|
|
|
175
199
|
const PokeReducerLive = Reducer.live(PokeReducer, (viewer) =>
|
|
176
200
|
viewer._tag === 'Open' ? { ...viewer, revision: viewer.revision + 1 } : viewer,
|
|
177
201
|
)
|
|
202
|
+
class MarkReducer extends Reducer.make('MarkReducer', {
|
|
203
|
+
states: [Marker],
|
|
204
|
+
events: [Poked],
|
|
205
|
+
}) {}
|
|
206
|
+
const MarkReducerLive = Reducer.live(MarkReducer, (n) => n + 1)
|
|
178
207
|
|
|
179
|
-
const TestLayer = PokeReducerLive.pipe(
|
|
180
|
-
Layer.provideMerge(
|
|
208
|
+
const TestLayer = Layer.mergeAll(PokeReducerLive, MarkReducerLive).pipe(
|
|
209
|
+
Layer.provideMerge(
|
|
210
|
+
Layer.mergeAll(State.live(ViewerState, { _tag: 'Closed' }), State.live(Marker, 0), Engine),
|
|
211
|
+
),
|
|
181
212
|
)
|
|
182
213
|
|
|
183
214
|
return Effect.gen(function* () {
|
|
184
215
|
const store = yield* ViewerState.store
|
|
216
|
+
const marker = yield* Marker.store
|
|
185
217
|
const before = store.get()
|
|
186
218
|
const notifications = { n: 0 }
|
|
219
|
+
const flushed = { n: 0 }
|
|
187
220
|
store.subscribe(() => {
|
|
188
221
|
notifications.n += 1
|
|
189
222
|
})
|
|
223
|
+
marker.subscribe(() => {
|
|
224
|
+
flushed.n += 1
|
|
225
|
+
})
|
|
190
226
|
yield* publish('Normal', Event.construct(Poked, {}))
|
|
191
|
-
|
|
227
|
+
// The marker folds on the same event and shares the scheduler's flush: once
|
|
228
|
+
// its listener fires, a viewer notification would have run in that batch too.
|
|
229
|
+
yield* until(
|
|
230
|
+
() => flushed.n,
|
|
231
|
+
(n) => n >= 1,
|
|
232
|
+
)
|
|
192
233
|
// Reference identity is the repaint signal: an unchanged fold must not be
|
|
193
234
|
// re-decoded into a fresh, structurally-equal value that wakes subscribers.
|
|
194
235
|
expect(store.get()).toBe(before)
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import type { Context, Effect, Option } from 'effect'
|
|
2
|
+
import type {
|
|
3
|
+
CompositionActivation,
|
|
4
|
+
CompositionId,
|
|
5
|
+
RequiredSlotIdentities,
|
|
6
|
+
} from '../compose/composition'
|
|
7
|
+
import type { UiContract } from '../compose/ui'
|
|
8
|
+
import type {
|
|
9
|
+
AnyFeatureBinding,
|
|
10
|
+
CapturedFeatureBinding,
|
|
11
|
+
FeatureBinding,
|
|
12
|
+
FeatureRequirement,
|
|
13
|
+
} from '../feature/feature'
|
|
14
|
+
import type {
|
|
15
|
+
AnyFeatureGraphSummary,
|
|
16
|
+
FeatureSummaryCompositionIdentity,
|
|
17
|
+
FeatureSummaryContract,
|
|
18
|
+
FeatureSummaryInput,
|
|
19
|
+
FeatureSummaryMountProps,
|
|
20
|
+
FeatureSummaryName,
|
|
21
|
+
FeatureSummaryOpenServices,
|
|
22
|
+
FeatureSummaryUsesInput,
|
|
23
|
+
} from '../graph/closure'
|
|
24
|
+
import type { FeatureLoadFailed } from '../internal/errors'
|
|
25
|
+
import type { Node } from '../ui/node'
|
|
26
|
+
import type { Priority, Tagged } from './bus'
|
|
27
|
+
import type { Instrumentation } from './instrumentation'
|
|
28
|
+
import type { EngineServices } from './loop'
|
|
29
|
+
|
|
30
|
+
export const UnspecifiedRuntimeServicesTypeId: unique symbol = Symbol.for(
|
|
31
|
+
'reform/UnspecifiedRuntimeServices',
|
|
32
|
+
)
|
|
33
|
+
export type UnspecifiedRuntimeServicesTypeId = typeof UnspecifiedRuntimeServicesTypeId
|
|
34
|
+
export interface UnspecifiedRuntimeServices {
|
|
35
|
+
readonly [UnspecifiedRuntimeServicesTypeId]: UnspecifiedRuntimeServicesTypeId
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export type FeatureMountHandlersExternalApi<UsesInput extends boolean, Input, Services> = {
|
|
39
|
+
readonly onLive: (runtime: RuntimeHandle<Services>) => void
|
|
40
|
+
readonly onFailed: (error: FeatureLoadFailed) => void
|
|
41
|
+
} & (UsesInput extends true ? { readonly props: Input } : { readonly props?: never })
|
|
42
|
+
|
|
43
|
+
export type FeatureMountHandlers<
|
|
44
|
+
UsesInput extends boolean,
|
|
45
|
+
Input,
|
|
46
|
+
Services,
|
|
47
|
+
> = FeatureMountHandlersExternalApi<UsesInput, Input, Services>
|
|
48
|
+
|
|
49
|
+
export type RuntimeFeatureAvailability<Services, OpenServices> = [
|
|
50
|
+
EngineServices | OpenServices,
|
|
51
|
+
] extends [Services]
|
|
52
|
+
? object
|
|
53
|
+
: {
|
|
54
|
+
readonly missingFeatureServices: Exclude<EngineServices | OpenServices, Services>
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export interface AnyFeatureMountHandlers {
|
|
58
|
+
readonly props: unknown
|
|
59
|
+
readonly onLive: <Services>(runtime: RuntimeHandle<Services>) => void
|
|
60
|
+
readonly onFailed: (error: FeatureLoadFailed) => void
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export interface CapturedFeatureMountHandlers<
|
|
64
|
+
Props,
|
|
65
|
+
HostServices,
|
|
66
|
+
FeatureServices,
|
|
67
|
+
C extends UiContract,
|
|
68
|
+
N extends string,
|
|
69
|
+
Identity,
|
|
70
|
+
> {
|
|
71
|
+
readonly props: Props
|
|
72
|
+
readonly onLive: (
|
|
73
|
+
runtime: RuntimeHandle<
|
|
74
|
+
HostServices | FeatureServices | CompositionId<N, Identity, RequiredSlotIdentities<C>>
|
|
75
|
+
>,
|
|
76
|
+
) => void
|
|
77
|
+
readonly onFailed: (error: FeatureLoadFailed) => void
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export interface RuntimeHandle<Services = UnspecifiedRuntimeServices> {
|
|
81
|
+
read<Identifier extends Services, Service>(tag: Context.Tag<Identifier, Service>): Service
|
|
82
|
+
readOption<Identifier, Service>(tag: Context.Tag<Identifier, Service>): Option.Option<Service>
|
|
83
|
+
runRender(effect: Effect.Effect<Node, never, never>): Node
|
|
84
|
+
dispatch(priority: Priority, event: Tagged): void
|
|
85
|
+
mountScoped(activation: CompositionActivation): () => void
|
|
86
|
+
mountFeature<
|
|
87
|
+
Summary extends AnyFeatureGraphSummary,
|
|
88
|
+
ROut,
|
|
89
|
+
const DirectRequires extends ReadonlyArray<FeatureRequirement>,
|
|
90
|
+
>(
|
|
91
|
+
binding: [EngineServices | FeatureSummaryOpenServices<Summary>] extends [Services]
|
|
92
|
+
? FeatureBinding<Summary, ROut, DirectRequires>
|
|
93
|
+
: never,
|
|
94
|
+
handlers: FeatureMountHandlers<
|
|
95
|
+
FeatureSummaryUsesInput<Summary>,
|
|
96
|
+
FeatureSummaryInput<Summary>,
|
|
97
|
+
Services | ROut
|
|
98
|
+
>,
|
|
99
|
+
): () => void
|
|
100
|
+
mountCapturedFeature<
|
|
101
|
+
Summary extends AnyFeatureGraphSummary,
|
|
102
|
+
ROut,
|
|
103
|
+
const DirectRequires extends ReadonlyArray<FeatureRequirement>,
|
|
104
|
+
>(
|
|
105
|
+
binding: CapturedFeatureBinding<Summary, ROut, DirectRequires> &
|
|
106
|
+
RuntimeFeatureAvailability<Services, FeatureSummaryOpenServices<Summary>>,
|
|
107
|
+
handlers: CapturedFeatureMountHandlers<
|
|
108
|
+
FeatureSummaryMountProps<Summary>,
|
|
109
|
+
Services,
|
|
110
|
+
ROut,
|
|
111
|
+
FeatureSummaryContract<Summary>,
|
|
112
|
+
FeatureSummaryName<Summary>,
|
|
113
|
+
FeatureSummaryCompositionIdentity<Summary>
|
|
114
|
+
>,
|
|
115
|
+
): () => void
|
|
116
|
+
mountAnyFeature(binding: AnyFeatureBinding, handlers: AnyFeatureMountHandlers): () => void
|
|
117
|
+
readonly instrumentation: Instrumentation
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export interface AppRuntime<Services = UnspecifiedRuntimeServices> extends RuntimeHandle<Services> {
|
|
121
|
+
boot(): void
|
|
122
|
+
dispose(): void
|
|
123
|
+
isDisposed(): boolean
|
|
124
|
+
}
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
import { expect, it } from '@effect/vitest'
|
|
2
|
-
import {
|
|
2
|
+
import { Effect, Layer, Schema as S } from 'effect'
|
|
3
3
|
import { Engine, Event, publish, Reducer, StateFamily } from '../index'
|
|
4
|
+
import { until } from '../testkit/flight.testkit'
|
|
4
5
|
|
|
5
|
-
|
|
6
|
+
// Eviction is queued with queueMicrotask; one turn of the queue provably drains it.
|
|
7
|
+
const drainMicrotasks = Effect.promise(
|
|
8
|
+
() => new Promise<void>((resolve) => queueMicrotask(resolve)),
|
|
9
|
+
)
|
|
6
10
|
|
|
7
11
|
it.live('same-shaped families resolve their own stores', () => {
|
|
8
12
|
class First extends StateFamily.make('family.nominal.first', S.String, S.Number) {}
|
|
@@ -36,7 +40,10 @@ it.live("writing one key does not notify another key's subscribers", () => {
|
|
|
36
40
|
})
|
|
37
41
|
|
|
38
42
|
a.set(1)
|
|
39
|
-
yield*
|
|
43
|
+
yield* until(
|
|
44
|
+
() => aWakes.n,
|
|
45
|
+
(n) => n > 0,
|
|
46
|
+
)
|
|
40
47
|
expect(aWakes.n).toBe(1)
|
|
41
48
|
expect(bWakes.n).toBe(0)
|
|
42
49
|
expect(b.get()).toBe(0)
|
|
@@ -81,12 +88,16 @@ it.live('evictWhenUnused drops a key once its last subscriber leaves', () => {
|
|
|
81
88
|
const offB = store.subscribe(() => {})
|
|
82
89
|
expect(family.size()).toBe(1)
|
|
83
90
|
|
|
91
|
+
// Releasing one of two subscribers queues no eviction at all — assert with no window.
|
|
84
92
|
offA()
|
|
85
|
-
yield*
|
|
93
|
+
yield* drainMicrotasks
|
|
86
94
|
expect(family.size()).toBe(1)
|
|
87
95
|
|
|
88
96
|
offB()
|
|
89
|
-
yield*
|
|
97
|
+
yield* until(
|
|
98
|
+
() => family.size(),
|
|
99
|
+
(size) => size === 0,
|
|
100
|
+
)
|
|
90
101
|
expect(family.size()).toBe(0)
|
|
91
102
|
expect(family.at('k').get()).toBe(0)
|
|
92
103
|
}).pipe(Effect.provide(TestLayer))
|
|
@@ -103,7 +114,7 @@ it.live('evictWhenUnused: a same-tick re-subscribe cancels eviction', () => {
|
|
|
103
114
|
const off = store.subscribe(() => {})
|
|
104
115
|
off()
|
|
105
116
|
store.subscribe(() => {})
|
|
106
|
-
yield*
|
|
117
|
+
yield* drainMicrotasks
|
|
107
118
|
expect(family.size()).toBe(1)
|
|
108
119
|
expect(family.at('k').get()).toBe(7)
|
|
109
120
|
}).pipe(Effect.provide(TestLayer))
|
|
@@ -130,12 +141,18 @@ it.live('a family reducer fold returning Tombstone evicts the key', () => {
|
|
|
130
141
|
const family = yield* Items.store
|
|
131
142
|
|
|
132
143
|
yield* publish('High', Event.construct(Bumped, { id: 'a' }))
|
|
133
|
-
yield*
|
|
144
|
+
yield* until(
|
|
145
|
+
() => family.at('a').get(),
|
|
146
|
+
(value) => value === 1,
|
|
147
|
+
)
|
|
134
148
|
const before = family.at('a')
|
|
135
149
|
expect(before.get()).toBe(1)
|
|
136
150
|
|
|
137
151
|
yield* publish('High', Event.construct(Removed, { id: 'a' }))
|
|
138
|
-
yield*
|
|
152
|
+
yield* until(
|
|
153
|
+
() => family.at('a'),
|
|
154
|
+
(store) => store !== before,
|
|
155
|
+
)
|
|
139
156
|
const after = family.at('a')
|
|
140
157
|
expect(after).not.toBe(before)
|
|
141
158
|
expect(after.get()).toBe(0)
|
|
@@ -10,6 +10,15 @@ import { Deferred, Duration, Effect, MutableRef, Option } from 'effect'
|
|
|
10
10
|
|
|
11
11
|
export const tick = (ms = 10): Effect.Effect<void> => Effect.sleep(Duration.millis(ms))
|
|
12
12
|
|
|
13
|
+
// The runtime notifies on the microtask queue (scheduler, stateFamily, calcFamily),
|
|
14
|
+
// so draining turns is exact where a duration is a guess: a microtask cannot be
|
|
15
|
+
// starved by host load the way a 10ms budget can. Use this to let the graph
|
|
16
|
+
// quiesce before asserting that something did NOT happen.
|
|
17
|
+
export const flush = (turns = 3): Effect.Effect<void> =>
|
|
18
|
+
Effect.promise(() => new Promise<void>((resolve) => queueMicrotask(resolve))).pipe(
|
|
19
|
+
Effect.repeatN(Math.max(turns - 1, 0)),
|
|
20
|
+
)
|
|
21
|
+
|
|
13
22
|
export const until = <A>(
|
|
14
23
|
read: () => A,
|
|
15
24
|
predicate: (value: A) => boolean,
|