@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
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { Context, Effect, Schema } from 'effect'
|
|
2
|
+
import type { AsyncData } from '../calc/asyncData'
|
|
3
|
+
import { yieldableClass } from '../definition/definition'
|
|
4
|
+
import * as Event from '../event/event'
|
|
5
|
+
import { type GatedOf, gatedFlag, type QueryCodec } from '../internal/queryDriver'
|
|
6
|
+
import type { Store } from '../internal/store'
|
|
7
|
+
import { readTracked } from '../internal/track'
|
|
8
|
+
import { type AnySource, type SourceCapture, StateToken } from '../state/token'
|
|
9
|
+
import type {
|
|
10
|
+
FailedIntent,
|
|
11
|
+
PendingIntent,
|
|
12
|
+
RemoteStateClass,
|
|
13
|
+
RemoteStateConfigExternalApi,
|
|
14
|
+
RemoteStateManifestExternalApi,
|
|
15
|
+
RemoteStatePendingStore,
|
|
16
|
+
RemoteStateTruthStore,
|
|
17
|
+
RemoteStateVisibleStore,
|
|
18
|
+
} from './remoteStateTypes'
|
|
19
|
+
|
|
20
|
+
// Schema.Unknown is structural because Intents is open at make-time; the declaration preserves its authored payload type.
|
|
21
|
+
export const looseSchema = <P>(schema: Schema.Schema.AnyNoContext): Schema.Schema<P, P, never> =>
|
|
22
|
+
Schema.declare((input): input is P => Schema.is(schema)(input))
|
|
23
|
+
|
|
24
|
+
export const make = <
|
|
25
|
+
const N extends string,
|
|
26
|
+
const Inputs extends ReadonlyArray<AnySource>,
|
|
27
|
+
const Intents extends ReadonlyArray<Event.AnyEvent>,
|
|
28
|
+
OutputSchema extends Schema.Schema.AnyNoContext,
|
|
29
|
+
ErrorSchema extends Schema.Schema.AnyNoContext | undefined = undefined,
|
|
30
|
+
const AlwaysOn extends boolean = false,
|
|
31
|
+
>(
|
|
32
|
+
name: N,
|
|
33
|
+
config: RemoteStateConfigExternalApi<Inputs, Intents, OutputSchema, ErrorSchema, AlwaysOn>,
|
|
34
|
+
): RemoteStateClass<
|
|
35
|
+
N,
|
|
36
|
+
Inputs,
|
|
37
|
+
Intents,
|
|
38
|
+
Schema.Schema.Type<OutputSchema>,
|
|
39
|
+
ErrorSchema extends Schema.Schema.AnyNoContext ? Schema.Schema.Type<ErrorSchema> : never,
|
|
40
|
+
GatedOf<AlwaysOn>
|
|
41
|
+
> => {
|
|
42
|
+
type A = Schema.Schema.Type<OutputSchema>
|
|
43
|
+
type E = ErrorSchema extends Schema.Schema.AnyNoContext ? Schema.Schema.Type<ErrorSchema> : never
|
|
44
|
+
type I = Event.EventType<Intents[number]>
|
|
45
|
+
const store = Context.GenericTag<
|
|
46
|
+
RemoteStateVisibleStore<N>,
|
|
47
|
+
Store<AsyncData<A, E, GatedOf<AlwaysOn>>>
|
|
48
|
+
>(`reform/remoteState/${name}`)
|
|
49
|
+
const truthTag = Context.GenericTag<
|
|
50
|
+
RemoteStateTruthStore<N>,
|
|
51
|
+
Store<AsyncData<A, E, GatedOf<AlwaysOn>>>
|
|
52
|
+
>(`reform/remoteState/${name}/truth`)
|
|
53
|
+
const pendingTag = Context.GenericTag<
|
|
54
|
+
RemoteStatePendingStore<N>,
|
|
55
|
+
Store<ReadonlyArray<PendingIntent<I>>>
|
|
56
|
+
>(`reform/remoteState/${name}/pending`)
|
|
57
|
+
const gated = gatedFlag(config.alwaysOn)
|
|
58
|
+
const manifest: RemoteStateManifestExternalApi<N> = {
|
|
59
|
+
kind: 'RemoteState',
|
|
60
|
+
name,
|
|
61
|
+
output: config.output,
|
|
62
|
+
gated,
|
|
63
|
+
intents: config.intents.map((event) => event.tag),
|
|
64
|
+
...(config.error !== undefined ? { error: config.error } : {}),
|
|
65
|
+
}
|
|
66
|
+
const codec: QueryCodec<A> = {
|
|
67
|
+
encode: Schema.encode(config.output),
|
|
68
|
+
decode: Schema.decodeUnknown(config.output),
|
|
69
|
+
}
|
|
70
|
+
// Annotations preserve template-literal types for the public statics.
|
|
71
|
+
const truthName: `${N}/truth` = `${name}/truth`
|
|
72
|
+
const pendingName: `${N}/pending` = `${name}/pending`
|
|
73
|
+
const failedName: `${N}/Failed` = `${name}/Failed`
|
|
74
|
+
const read = Effect.flatMap(store, readTracked)
|
|
75
|
+
const remote: RemoteStateClass<N, Inputs, Intents, A, E, GatedOf<AlwaysOn>> = yieldableClass(
|
|
76
|
+
read,
|
|
77
|
+
{
|
|
78
|
+
manifest,
|
|
79
|
+
codec,
|
|
80
|
+
store,
|
|
81
|
+
name,
|
|
82
|
+
inputs: config.inputs,
|
|
83
|
+
intents: config.intents,
|
|
84
|
+
gated,
|
|
85
|
+
capture: <Result>(visit: SourceCapture<Result>): Result => visit(remote),
|
|
86
|
+
truth: new StateToken(truthName, truthTag),
|
|
87
|
+
pending: new StateToken(pendingName, pendingTag),
|
|
88
|
+
Failed: Event.make(
|
|
89
|
+
failedName,
|
|
90
|
+
looseSchema<FailedIntent<I>>(
|
|
91
|
+
Schema.Struct({ intent: Schema.Unknown, error: Schema.Unknown }),
|
|
92
|
+
),
|
|
93
|
+
),
|
|
94
|
+
},
|
|
95
|
+
)
|
|
96
|
+
return remote
|
|
97
|
+
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { Context, Effect, Either, Layer } from 'effect'
|
|
2
|
+
import * as Channel from '../channel/channel'
|
|
3
|
+
import * as Event from '../event/event'
|
|
4
|
+
import { Bus, narrowHandled } from '../runtime/bus'
|
|
5
|
+
import { type ReducerEntry, Reducers } from '../runtime/loop'
|
|
6
|
+
import type { PendingQueue } from './pendingQueue'
|
|
7
|
+
import type { QueueFoldEventExternalApi, SettleLink } from './remoteStateTypes'
|
|
8
|
+
|
|
9
|
+
export interface IntentEvents<I> {
|
|
10
|
+
readonly queued: Event.EventClass<string, { readonly opId: string; readonly intent: I }>
|
|
11
|
+
readonly acked: Event.EventClass<string, { readonly opId: string }>
|
|
12
|
+
readonly settled: Event.EventClass<string, { readonly opId: string }>
|
|
13
|
+
readonly invalidated: Event.EventClass<string, {}>
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
interface QueueReducerOptions<I> {
|
|
17
|
+
readonly name: string
|
|
18
|
+
readonly queueTag: Context.Tag<PendingQueue<I>, PendingQueue<I>>
|
|
19
|
+
readonly events: IntentEvents<I>
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Direct registry assembly avoids deferred conditional parameters while Intents is generic.
|
|
23
|
+
export const queueReducerLayer = <I>(
|
|
24
|
+
options: QueueReducerOptions<I>,
|
|
25
|
+
): Layer.Layer<never, never, Reducers | PendingQueue<I>> =>
|
|
26
|
+
Layer.scopedDiscard(
|
|
27
|
+
Effect.gen(function* () {
|
|
28
|
+
const reducers = yield* Reducers
|
|
29
|
+
if (reducers.entries.some((entry) => entry.name === options.name)) {
|
|
30
|
+
yield* Effect.logWarning(`reform: duplicate reducer name '${options.name}' registered`)
|
|
31
|
+
}
|
|
32
|
+
const queue = yield* options.queueTag
|
|
33
|
+
const entry: ReducerEntry = {
|
|
34
|
+
name: options.name,
|
|
35
|
+
handles: new Set([
|
|
36
|
+
options.events.queued.tag,
|
|
37
|
+
options.events.acked.tag,
|
|
38
|
+
options.events.settled.tag,
|
|
39
|
+
]),
|
|
40
|
+
apply: (event) => {
|
|
41
|
+
const handled = narrowHandled<QueueFoldEventExternalApi<I>>(event)
|
|
42
|
+
if (handled._tag === options.events.queued.tag) {
|
|
43
|
+
if (handled.intent !== undefined) {
|
|
44
|
+
queue.append({
|
|
45
|
+
opId: handled.opId,
|
|
46
|
+
intent: handled.intent,
|
|
47
|
+
status: 'sending',
|
|
48
|
+
})
|
|
49
|
+
}
|
|
50
|
+
return
|
|
51
|
+
}
|
|
52
|
+
if (handled._tag === options.events.acked.tag) {
|
|
53
|
+
queue.ack(handled.opId)
|
|
54
|
+
return
|
|
55
|
+
}
|
|
56
|
+
queue.settle(handled.opId)
|
|
57
|
+
},
|
|
58
|
+
}
|
|
59
|
+
yield* Effect.acquireRelease(
|
|
60
|
+
Effect.sync(() => reducers.register(entry)),
|
|
61
|
+
() => Effect.sync(() => reducers.unregister(entry)),
|
|
62
|
+
)
|
|
63
|
+
}),
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
interface SendProcedureOptions<I, R2> {
|
|
67
|
+
readonly name: string
|
|
68
|
+
readonly channel: Channel.ChannelClass
|
|
69
|
+
readonly handles: ReadonlySet<string>
|
|
70
|
+
readonly linkTag: Context.Tag<SettleLink, SettleLink>
|
|
71
|
+
readonly failed: Event.EventClass<string, { readonly intent: I; readonly error: unknown }>
|
|
72
|
+
readonly send: (intent: I) => Effect.Effect<unknown, unknown, R2>
|
|
73
|
+
readonly events: IntentEvents<I>
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export const sendProcedureLayer = <I, R2>(
|
|
77
|
+
options: SendProcedureOptions<I, R2>,
|
|
78
|
+
): Layer.Layer<never, never, Channel.Procedures | Bus | R2 | SettleLink> =>
|
|
79
|
+
Layer.scopedDiscard(
|
|
80
|
+
Effect.gen(function* () {
|
|
81
|
+
const procedures = yield* Channel.Procedures
|
|
82
|
+
if (procedures.entries.some((entry) => entry.name === options.name)) {
|
|
83
|
+
yield* Effect.logWarning(`reform: duplicate procedure name '${options.name}' registered`)
|
|
84
|
+
}
|
|
85
|
+
const link = yield* options.linkTag
|
|
86
|
+
const runtime = yield* Effect.runtime<Bus | R2>()
|
|
87
|
+
const deliver = Effect.fn('deliver')(function* (
|
|
88
|
+
intent: I,
|
|
89
|
+
): Effect.fn.Return<void, never, Bus | R2> {
|
|
90
|
+
const opId = yield* Effect.sync(() => crypto.randomUUID())
|
|
91
|
+
yield* Event.dispatch(options.events.queued, { opId, intent })
|
|
92
|
+
const outcome = yield* options.send(intent).pipe(
|
|
93
|
+
Effect.catchAllDefect((defect) => Effect.fail(defect)),
|
|
94
|
+
Effect.either,
|
|
95
|
+
Effect.onInterrupt(() => Event.dispatch(options.events.settled, { opId })),
|
|
96
|
+
)
|
|
97
|
+
yield* Either.match(outcome, {
|
|
98
|
+
onLeft: (error) =>
|
|
99
|
+
Effect.zipRight(
|
|
100
|
+
Event.dispatch(options.failed, { intent, error }),
|
|
101
|
+
Event.dispatch(options.events.settled, { opId }),
|
|
102
|
+
),
|
|
103
|
+
onRight: () =>
|
|
104
|
+
// Register before invalidation so a pre-ack refetch cannot settle this intent.
|
|
105
|
+
Effect.sync(() => link.register(opId, link.requested() + 1)).pipe(
|
|
106
|
+
Effect.zipRight(Event.dispatch(options.events.acked, { opId })),
|
|
107
|
+
Effect.zipRight(Event.dispatch(options.events.invalidated, {})),
|
|
108
|
+
),
|
|
109
|
+
})
|
|
110
|
+
})
|
|
111
|
+
const entry: Channel.ProcedureEntry = {
|
|
112
|
+
name: options.name,
|
|
113
|
+
channelName: options.channel.manifest.name,
|
|
114
|
+
handles: options.handles,
|
|
115
|
+
run: (event) => Effect.provide(deliver(narrowHandled<I>(event)), runtime),
|
|
116
|
+
}
|
|
117
|
+
yield* Effect.acquireRelease(
|
|
118
|
+
Effect.sync(() => procedures.register(entry)),
|
|
119
|
+
() => Effect.sync(() => procedures.unregister(entry)),
|
|
120
|
+
)
|
|
121
|
+
}),
|
|
122
|
+
)
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import type { Context, Effect, Schema } from 'effect'
|
|
2
|
+
import type { Effect as EffectType } from 'effect/Effect'
|
|
3
|
+
import type { AsyncData, AsyncError, AsyncSuccess } from '../calc/asyncData'
|
|
4
|
+
import type * as Channel from '../channel/channel'
|
|
5
|
+
import type { Manifest } from '../definition/definition'
|
|
6
|
+
import type * as Event from '../event/event'
|
|
7
|
+
import type { QueryCodec } from '../internal/queryDriver'
|
|
8
|
+
import type { InputsObject, InvalidateBy } from '../internal/sources'
|
|
9
|
+
import type { Store } from '../internal/store'
|
|
10
|
+
import type { AnySource, SourceCapture, StateToken } from '../state/token'
|
|
11
|
+
|
|
12
|
+
// An intent acked at generation g settles only on Success with generation > g.
|
|
13
|
+
// `apply` must be idempotent while refetched truth and the pending intent overlap.
|
|
14
|
+
|
|
15
|
+
export interface PendingIntent<I> {
|
|
16
|
+
readonly opId: string
|
|
17
|
+
readonly intent: I
|
|
18
|
+
readonly status: 'sending' | 'confirmed'
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface FailedIntent<I> {
|
|
22
|
+
readonly intent: I
|
|
23
|
+
readonly error: unknown
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export type RemoteStateSettled<A, E> = AsyncSuccess<A> | AsyncError<E>
|
|
27
|
+
|
|
28
|
+
export const RemoteStateVisibleStoreTypeId: unique symbol = Symbol.for(
|
|
29
|
+
'reform/RemoteStateVisibleStore',
|
|
30
|
+
)
|
|
31
|
+
export type RemoteStateVisibleStoreTypeId = typeof RemoteStateVisibleStoreTypeId
|
|
32
|
+
|
|
33
|
+
export interface RemoteStateVisibleStore<N extends string> {
|
|
34
|
+
readonly [RemoteStateVisibleStoreTypeId]: N
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export const RemoteStateTruthStoreTypeId: unique symbol = Symbol.for('reform/RemoteStateTruthStore')
|
|
38
|
+
export type RemoteStateTruthStoreTypeId = typeof RemoteStateTruthStoreTypeId
|
|
39
|
+
|
|
40
|
+
export interface RemoteStateTruthStore<N extends string> {
|
|
41
|
+
readonly [RemoteStateTruthStoreTypeId]: N
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export const RemoteStatePendingStoreTypeId: unique symbol = Symbol.for(
|
|
45
|
+
'reform/RemoteStatePendingStore',
|
|
46
|
+
)
|
|
47
|
+
export type RemoteStatePendingStoreTypeId = typeof RemoteStatePendingStoreTypeId
|
|
48
|
+
|
|
49
|
+
export interface RemoteStatePendingStore<N extends string> {
|
|
50
|
+
readonly [RemoteStatePendingStoreTypeId]: N
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export type EventPayload<Ev extends Event.AnyEvent> =
|
|
54
|
+
Ev extends Event.EventClass<string, infer P> ? P : never
|
|
55
|
+
|
|
56
|
+
// ExternalApi: optional `intent` only on Queued envelopes.
|
|
57
|
+
export interface QueueFoldEventExternalApi<I> {
|
|
58
|
+
readonly _tag: string
|
|
59
|
+
readonly opId: string
|
|
60
|
+
readonly intent?: I
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export interface RemoteStateSchemaReflection {
|
|
64
|
+
readonly ast: Schema.Schema<unknown, unknown, unknown>['ast']
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export interface RemoteStateManifestExternalApi<N extends string> extends Manifest {
|
|
68
|
+
readonly kind: 'RemoteState'
|
|
69
|
+
readonly name: N
|
|
70
|
+
readonly output: RemoteStateSchemaReflection
|
|
71
|
+
readonly error?: RemoteStateSchemaReflection
|
|
72
|
+
readonly gated: boolean
|
|
73
|
+
readonly intents: ReadonlyArray<string>
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export interface RemoteStateClass<
|
|
77
|
+
N extends string,
|
|
78
|
+
out Inputs extends ReadonlyArray<AnySource>,
|
|
79
|
+
in out Intents extends ReadonlyArray<Event.AnyEvent>,
|
|
80
|
+
in out A,
|
|
81
|
+
in out E,
|
|
82
|
+
in out Gated extends boolean,
|
|
83
|
+
> extends EffectType<AsyncData<A, E, Gated>, never, RemoteStateVisibleStore<N>> {
|
|
84
|
+
new (): {}
|
|
85
|
+
readonly manifest: RemoteStateManifestExternalApi<N>
|
|
86
|
+
readonly codec: QueryCodec<A>
|
|
87
|
+
readonly store: Context.Tag<RemoteStateVisibleStore<N>, Store<AsyncData<A, E, Gated>>>
|
|
88
|
+
readonly name: N
|
|
89
|
+
readonly inputs: Inputs
|
|
90
|
+
readonly intents: Intents
|
|
91
|
+
readonly gated: Gated
|
|
92
|
+
readonly capture: <Result>(visit: SourceCapture<Result>) => Result
|
|
93
|
+
readonly truth: StateToken<`${N}/truth`, AsyncData<A, E, Gated>, RemoteStateTruthStore<N>>
|
|
94
|
+
// pending is a Source, not a StateClass, so user reducers cannot target it.
|
|
95
|
+
readonly pending: StateToken<
|
|
96
|
+
`${N}/pending`,
|
|
97
|
+
ReadonlyArray<PendingIntent<Event.EventType<Intents[number]>>>,
|
|
98
|
+
RemoteStatePendingStore<N>
|
|
99
|
+
>
|
|
100
|
+
readonly Failed: Event.EventClass<`${N}/Failed`, FailedIntent<Event.EventType<Intents[number]>>>
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export interface RemoteStateConfigExternalApi<
|
|
104
|
+
Inputs extends ReadonlyArray<AnySource>,
|
|
105
|
+
Intents extends ReadonlyArray<Event.AnyEvent>,
|
|
106
|
+
OutputSchema extends Schema.Schema.AnyNoContext,
|
|
107
|
+
ErrorSchema extends Schema.Schema.AnyNoContext | undefined,
|
|
108
|
+
AlwaysOn extends boolean,
|
|
109
|
+
> {
|
|
110
|
+
readonly inputs: Inputs
|
|
111
|
+
readonly output: OutputSchema
|
|
112
|
+
readonly error?: ErrorSchema
|
|
113
|
+
readonly alwaysOn?: AlwaysOn
|
|
114
|
+
readonly intents: Intents
|
|
115
|
+
}
|
|
116
|
+
export type RemoteStateLiveExternalApi<
|
|
117
|
+
Inputs extends ReadonlyArray<AnySource>,
|
|
118
|
+
Intents extends ReadonlyArray<Event.AnyEvent>,
|
|
119
|
+
A,
|
|
120
|
+
E,
|
|
121
|
+
Gated extends boolean,
|
|
122
|
+
R,
|
|
123
|
+
R2,
|
|
124
|
+
SettledEvent extends Event.AnyEvent,
|
|
125
|
+
> = {
|
|
126
|
+
readonly query: (inputs: InputsObject<Inputs>) => Effect.Effect<A, E, R>
|
|
127
|
+
// Any failure settles and emits Failed; truth comes only from the invalidated query.
|
|
128
|
+
readonly send: (intent: Event.EventType<Intents[number]>) => Effect.Effect<unknown, unknown, R2>
|
|
129
|
+
readonly apply: (value: A, intent: Event.EventType<Intents[number]>) => A
|
|
130
|
+
readonly channel?: Channel.ChannelClass
|
|
131
|
+
readonly invalidateBy?: InvalidateBy<Inputs>
|
|
132
|
+
readonly invalidateOn?: ReadonlyArray<Event.AnyEvent>
|
|
133
|
+
readonly coalesce?: 'switch' | 'trailing'
|
|
134
|
+
readonly reuse?: boolean
|
|
135
|
+
readonly settled?: {
|
|
136
|
+
readonly event: SettledEvent
|
|
137
|
+
readonly payload: (result: RemoteStateSettled<A, E>) => EventPayload<SettledEvent>
|
|
138
|
+
}
|
|
139
|
+
// Only settled server truth is persisted; the optimistic overlay is a separate store.
|
|
140
|
+
readonly persist?:
|
|
141
|
+
| boolean
|
|
142
|
+
| { readonly key?: string | ((inputs: InputsObject<Inputs>) => string) }
|
|
143
|
+
} & (Gated extends true
|
|
144
|
+
? { readonly disabled?: (inputs: InputsObject<Inputs>) => boolean }
|
|
145
|
+
: { readonly disabled?: never })
|
|
146
|
+
|
|
147
|
+
export interface SettleLink {
|
|
148
|
+
readonly requested: () => number
|
|
149
|
+
readonly register: (opId: string, waitFor: number) => void
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export interface WaiterRegistration {
|
|
153
|
+
readonly opId: string
|
|
154
|
+
readonly waitFor: number
|
|
155
|
+
}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import { Context,
|
|
1
|
+
import { Context, Effect, Layer, Option, Schema } from 'effect'
|
|
2
2
|
import { expect, it } from '@effect/vitest'
|
|
3
3
|
import * as Composition from '../compose/composition'
|
|
4
4
|
import { mount } from '../compose/structure'
|
|
5
5
|
import { ui } from '../compose/ui'
|
|
6
6
|
import { scene } from '../scene/scene'
|
|
7
|
+
import { flush, until } from '../testkit/flight.testkit'
|
|
7
8
|
import { Engine } from './loop'
|
|
8
9
|
import { makeAppRuntime } from './appRuntime'
|
|
9
10
|
|
|
@@ -62,7 +63,10 @@ const makeScene = (probe: ActivationProbeService) =>
|
|
|
62
63
|
],
|
|
63
64
|
})
|
|
64
65
|
|
|
65
|
-
|
|
66
|
+
// Wait for the count, then quiesce: the exact `toEqual` is what proves no extra
|
|
67
|
+
// acquire/release slipped in behind it.
|
|
68
|
+
const settledAt = (read: () => ReadonlyArray<string>, count: number): Effect.Effect<void> =>
|
|
69
|
+
until(read, (entries) => entries.length >= count).pipe(Effect.zipRight(flush()))
|
|
66
70
|
|
|
67
71
|
it.scopedLive(
|
|
68
72
|
'keeps activation lazy, scopes sibling mounts independently, and closes them at teardown',
|
|
@@ -85,16 +89,16 @@ it.scopedLive(
|
|
|
85
89
|
Option.getOrThrow(Composition.activation(service, { id: 'first' })),
|
|
86
90
|
)
|
|
87
91
|
app.mountScoped(Option.getOrThrow(Composition.activation(service, { id: 'second' })))
|
|
88
|
-
yield*
|
|
92
|
+
yield* settledAt(() => probe.acquired, 2)
|
|
89
93
|
expect(probe.acquired).toEqual(['first', 'second'])
|
|
90
94
|
|
|
91
95
|
first()
|
|
92
96
|
first()
|
|
93
|
-
yield*
|
|
97
|
+
yield* settledAt(() => probe.released, 1)
|
|
94
98
|
expect(probe.released).toEqual(['first'])
|
|
95
99
|
|
|
96
100
|
app.dispose()
|
|
97
|
-
yield*
|
|
101
|
+
yield* settledAt(() => probe.released, 2)
|
|
98
102
|
expect(probe.released).toEqual(['first', 'second'])
|
|
99
103
|
}),
|
|
100
104
|
)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Context,
|
|
1
|
+
import { Context, Effect, Layer, Option, Schema as S } from 'effect'
|
|
2
2
|
import { expect, it } from '@effect/vitest'
|
|
3
3
|
import * as Composition from '../compose/composition'
|
|
4
4
|
import { provide } from '../compose/provide'
|
|
@@ -11,6 +11,7 @@ import { featureModule } from '../feature/feature'
|
|
|
11
11
|
import * as Reducer from '../reducer/reducer'
|
|
12
12
|
import { scene } from '../scene/scene'
|
|
13
13
|
import * as State from '../state/state'
|
|
14
|
+
import { until } from '../testkit/flight.testkit'
|
|
14
15
|
import { Engine, Reducers } from './loop'
|
|
15
16
|
import { makeAppRuntime } from './appRuntime'
|
|
16
17
|
|
|
@@ -72,7 +73,6 @@ const rootScene = () =>
|
|
|
72
73
|
],
|
|
73
74
|
})
|
|
74
75
|
|
|
75
|
-
const settle = Effect.sleep(Duration.millis(50))
|
|
76
76
|
const appRuntime = Effect.acquireRelease(
|
|
77
77
|
Effect.sync(() => makeAppRuntime(rootScene())),
|
|
78
78
|
(app) => Effect.sync(() => app.dispose()),
|
|
@@ -135,8 +135,29 @@ it.scopedLive(
|
|
|
135
135
|
})
|
|
136
136
|
// Same-tick dispose (the StrictMode cleanup shape): the fork has not run yet.
|
|
137
137
|
dispose()
|
|
138
|
-
|
|
138
|
+
// A probe mount forked behind the cancelled one: once it is live, the
|
|
139
|
+
// cancelled fork's turn has provably passed.
|
|
140
|
+
const probe = { live: 0, failed: 0 }
|
|
141
|
+
const disposeProbe = app.mountFeature(CounterNative.binding, {
|
|
142
|
+
onLive: () => {
|
|
143
|
+
probe.live += 1
|
|
144
|
+
},
|
|
145
|
+
onFailed: () => {
|
|
146
|
+
probe.failed += 1
|
|
147
|
+
},
|
|
148
|
+
})
|
|
149
|
+
yield* until(
|
|
150
|
+
() => probe.live + probe.failed,
|
|
151
|
+
(n) => n > 0,
|
|
152
|
+
)
|
|
139
153
|
expect(calls).toEqual({ live: 0, failed: 0 })
|
|
154
|
+
expect(app.read(Reducers).entries.length).toBe(baseline + 1)
|
|
155
|
+
|
|
156
|
+
disposeProbe()
|
|
157
|
+
yield* until(
|
|
158
|
+
() => app.read(Reducers).entries.length,
|
|
159
|
+
(n) => n === baseline,
|
|
160
|
+
)
|
|
140
161
|
expect(app.read(Reducers).entries.length).toBe(baseline)
|
|
141
162
|
expect(app.read(Reducers).byTag.get('appRuntime.Tick')).toBeUndefined()
|
|
142
163
|
}),
|
|
@@ -157,14 +178,17 @@ it.scopedLive(
|
|
|
157
178
|
calls.failed += 1
|
|
158
179
|
},
|
|
159
180
|
})
|
|
160
|
-
yield*
|
|
161
|
-
|
|
162
|
-
|
|
181
|
+
yield* until(
|
|
182
|
+
() => calls.live,
|
|
183
|
+
(n) => n === 1,
|
|
163
184
|
)
|
|
164
185
|
expect(calls).toEqual({ live: 1, failed: 0 })
|
|
165
186
|
expect(app.read(Reducers).entries.length).toBe(baseline + 1)
|
|
166
187
|
dispose()
|
|
167
|
-
yield*
|
|
188
|
+
yield* until(
|
|
189
|
+
() => app.read(Reducers).entries.length,
|
|
190
|
+
(n) => n === baseline,
|
|
191
|
+
)
|
|
168
192
|
expect(app.read(Reducers).entries.length).toBe(baseline)
|
|
169
193
|
}),
|
|
170
194
|
)
|
|
@@ -190,9 +214,9 @@ it.scopedLive('a nested Feature mounts against its immediate parent context', ()
|
|
|
190
214
|
},
|
|
191
215
|
}),
|
|
192
216
|
)
|
|
193
|
-
yield*
|
|
194
|
-
|
|
195
|
-
|
|
217
|
+
yield* until(
|
|
218
|
+
() => observed.length,
|
|
219
|
+
(n) => n > 0,
|
|
196
220
|
)
|
|
197
221
|
expect(observed).toEqual(['from-parent'])
|
|
198
222
|
}),
|
|
@@ -219,9 +243,9 @@ it.scopedLive(
|
|
|
219
243
|
},
|
|
220
244
|
})
|
|
221
245
|
yield* Effect.addFinalizer(() => Effect.sync(dispose))
|
|
222
|
-
yield*
|
|
223
|
-
|
|
224
|
-
|
|
246
|
+
yield* until(
|
|
247
|
+
() => observed.length,
|
|
248
|
+
(n) => n > 0,
|
|
225
249
|
)
|
|
226
250
|
expect(observed).toEqual(['from-parent'])
|
|
227
251
|
}),
|
|
@@ -240,9 +264,9 @@ it.scopedLive(
|
|
|
240
264
|
onLive: () => observed.push('live'),
|
|
241
265
|
})
|
|
242
266
|
yield* Effect.addFinalizer(() => Effect.sync(dispose))
|
|
243
|
-
yield*
|
|
244
|
-
|
|
245
|
-
|
|
267
|
+
yield* until(
|
|
268
|
+
() => observed.length,
|
|
269
|
+
(n) => n > 0,
|
|
246
270
|
)
|
|
247
271
|
expect(observed).toEqual(['failed'])
|
|
248
272
|
}),
|