@playfast/reform 1.0.1 → 1.1.1

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 (97) hide show
  1. package/README.md +18 -1
  2. package/package.json +1 -1
  3. package/src/boundary/boundary.test.ts +1 -33
  4. package/src/boundary/boundary.ts +4 -82
  5. package/src/calc/asyncCalc.invalidate.test.ts +2 -14
  6. package/src/calc/asyncCalc.test.ts +0 -38
  7. package/src/calc/asyncCalc.ts +11 -163
  8. package/src/calc/asyncCalcDefinitions.ts +48 -0
  9. package/src/calc/asyncData.ts +0 -37
  10. package/src/calc/calc.test.ts +4 -28
  11. package/src/calc/calc.ts +6 -46
  12. package/src/calc/calcFamily.test.ts +0 -12
  13. package/src/calc/calcFamily.ts +6 -48
  14. package/src/calc/compose.test.ts +1 -12
  15. package/src/calc/compose.ts +0 -22
  16. package/src/calc/queryState.ts +0 -23
  17. package/src/channel/channel.ts +72 -174
  18. package/src/channel/procedureRegistry.ts +90 -0
  19. package/src/compose/composition.ts +7 -67
  20. package/src/compose/host.ts +0 -6
  21. package/src/compose/props.ts +3 -9
  22. package/src/compose/provide.ts +19 -33
  23. package/src/compose/slot.ts +2 -30
  24. package/src/compose/structure.test.ts +0 -6
  25. package/src/compose/structure.ts +3 -67
  26. package/src/compose/ui.test.ts +0 -4
  27. package/src/compose/ui.ts +17 -111
  28. package/src/compose/ui.typecheck.ts +0 -14
  29. package/src/definition/definition.ts +0 -32
  30. package/src/event/event.test.ts +0 -3
  31. package/src/event/event.ts +5 -14
  32. package/src/event/eventGroup.ts +0 -1
  33. package/src/feature/feature.mount.test.ts +66 -54
  34. package/src/feature/feature.test.ts +67 -46
  35. package/src/feature/feature.ts +185 -214
  36. package/src/feature/feature.typecheck.ts +29 -16
  37. package/src/feature/featureBinding.ts +48 -0
  38. package/src/index.ts +245 -229
  39. package/src/internal/capture.ts +0 -20
  40. package/src/internal/ctx.ts +2 -9
  41. package/src/internal/env.ts +9 -0
  42. package/src/internal/errors.test.ts +0 -6
  43. package/src/internal/errors.ts +14 -48
  44. package/src/internal/inspect.test.ts +0 -6
  45. package/src/internal/inspect.ts +0 -12
  46. package/src/internal/queryDriver.ts +42 -243
  47. package/src/internal/queryDriverStore.ts +153 -0
  48. package/src/internal/queryDriverTypes.ts +57 -0
  49. package/src/internal/queryEvents.ts +0 -12
  50. package/src/internal/queryStore.ts +0 -14
  51. package/src/internal/reuse.test.ts +0 -11
  52. package/src/internal/reuse.ts +0 -28
  53. package/src/internal/scheduler.ts +0 -43
  54. package/src/internal/seeds.ts +0 -14
  55. package/src/internal/sources.ts +4 -41
  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 +0 -18
  60. package/src/internal/variance.ts +5 -0
  61. package/src/namespace/namespace.test.ts +46 -0
  62. package/src/namespace/namespace.ts +85 -0
  63. package/src/procedure/procedure.ts +2 -24
  64. package/src/reducer/reducer.ts +3 -21
  65. package/src/remote/remoteState.test.ts +92 -75
  66. package/src/remote/remoteState.ts +99 -486
  67. package/src/remote/remoteState.typecheck.ts +0 -33
  68. package/src/remote/remoteStateDefinition.ts +135 -0
  69. package/src/remote/remoteStateLayers.ts +118 -0
  70. package/src/remote/remoteStateLiveTypes.ts +59 -0
  71. package/src/remote/remoteStateSend.ts +64 -0
  72. package/src/runtime/appRuntime.test.ts +197 -0
  73. package/src/runtime/appRuntime.ts +40 -83
  74. package/src/runtime/bus.ts +2 -15
  75. package/src/runtime/eventBudget.test.ts +155 -0
  76. package/src/runtime/eventBudget.ts +119 -0
  77. package/src/runtime/hardening.test.ts +74 -9
  78. package/src/runtime/instrumentation.test.ts +213 -192
  79. package/src/runtime/instrumentation.ts +0 -49
  80. package/src/runtime/loop.test.ts +0 -17
  81. package/src/runtime/loop.ts +25 -55
  82. package/src/runtime/queries.ts +0 -17
  83. package/src/scene/featureScene.test.ts +59 -0
  84. package/src/scene/scene.ts +32 -59
  85. package/src/scene/seedScene.test.ts +96 -116
  86. package/src/state/state.ts +7 -26
  87. package/src/state/stateFamily.test.ts +2 -11
  88. package/src/state/stateFamily.ts +9 -52
  89. package/src/state/stateGroup.ts +2 -49
  90. package/src/state/token.ts +4 -17
  91. package/src/synced/syncedStore.ts +7 -42
  92. package/src/ui/node.ts +0 -6
  93. package/src/ui/trigger.ts +0 -5
  94. package/src/wire/tree.test.ts +0 -3
  95. package/src/wire/tree.ts +0 -39
  96. package/src/wire/triggers.test.ts +0 -4
  97. package/src/wire/triggers.ts +5 -27
package/src/index.ts CHANGED
@@ -1,239 +1,255 @@
1
- // Reform core the renderer-neutral primitives and runtime.
2
- // Definitions (`X.make`) are separate from implementations (`.live`)
3
- // throughout, and nothing self-registers on import (principle #1, #6).
1
+ export * as State from "./state/state";
2
+ export * as StateGroup from "./state/stateGroup";
3
+ export * as StateFamily from "./state/stateFamily";
4
+ export { StateToken } from "./state/token";
5
+ export type { AnySource, Source } from "./state/token";
4
6
 
5
- export * as State from './state/state'
6
- export * as StateGroup from './state/stateGroup'
7
- export * as StateFamily from './state/stateFamily'
8
- export { StateToken } from './state/token'
9
- export type { AnySource, Source } from './state/token'
7
+ export * as Event from "./event/event";
8
+ // TS2742: plain type export so declaration emit can name EventOf
9
+ export type { EventOf } from "./event/event";
10
+ export * as EventGroup from "./event/eventGroup";
10
11
 
11
- export * as Event from './event/event'
12
- // Direct type re-export alongside the namespace (TS2742 nameability): inferred
13
- // types of layers built over intent/event unions reference `EventOf`, and the
14
- // declaration emitter can only name it through a plain export.
15
- export type { EventOf } from './event/event'
16
- export * as EventGroup from './event/eventGroup'
17
-
18
- export * as Reducer from './reducer/reducer'
19
- export * as Calc from './calc/calc'
20
- export * as CalcFamily from './calc/calcFamily'
21
- export * as AsyncCalc from './calc/asyncCalc'
22
- export { composeCalcs } from './calc/compose'
23
- // The inputs-object/key/store types appearing in `AsyncCalc.live` /
24
- // `RemoteState.live` signatures, exported so app code can annotate
25
- // query-swapping helpers and layer types without re-spelling them.
26
- export type { InputsObject, InputStores, InvalidateBy } from './internal/sources'
27
- export {
28
- AsyncData,
29
- type AsyncError,
30
- type AsyncIdle,
31
- type AsyncLoading,
32
- type AsyncSuccess,
33
- } from './calc/asyncData'
34
- // The flat, data-oriented query value (`data`/`error`/`isFetching`/`isStale`) an
35
- // `AsyncCalc` is driven by; `toAsyncData` projects it onto the matchable arms.
36
- export { isLoading, type QueryState, toAsyncData } from './calc/queryState'
37
- // Optional host seams an `AsyncCalc` consults — persistence and lifecycle signals.
38
- // Concrete implementations live in edge packages (e.g. `@playfast/reform-query-browser`).
39
- export {
40
- noopQueryStore,
41
- QueryStore,
42
- type QueryStoreApi,
43
- resolveQueryStore,
44
- } from './internal/queryStore'
45
- export {
46
- noopQueryEvents,
47
- QueryEvents,
48
- type QueryEventsApi,
49
- resolveQueryEvents,
50
- } from './internal/queryEvents'
51
- export * as RemoteState from './remote/remoteState'
52
- // Direct type re-exports alongside the namespace (TS2742 nameability): an app
53
- // layer's inferred type references these (`Store<ReadonlyArray<PendingIntent<…>>>`),
54
- // and the declaration emitter can only name them through a plain export.
55
- export type { FailedIntent, PendingIntent, RemoteStateClass } from './remote/remoteState'
56
- export * as SyncedStore from './synced/syncedStore'
57
- // Direct type re-export alongside the namespace (TS2742 nameability): adapters
58
- // (e.g. `@playfast/reform-db`) annotate the source handed to `SyncedStore.live`.
59
- export type { SyncedSource, SyncedStoreClass } from './synced/syncedStore'
60
- export * as Boundary from './boundary/boundary'
61
- // Direct type re-exports alongside the namespace (same TS2742 nameability
62
- // reason as the RemoteState types above): app code annotates props with these.
12
+ export * as Reducer from "./reducer/reducer";
13
+ export * as Calc from "./calc/calc";
14
+ export * as CalcFamily from "./calc/calcFamily";
15
+ export * as AsyncCalc from "./calc/asyncCalc";
16
+ export { composeCalcs } from "./calc/compose";
17
+ export type {
18
+ InputsObject,
19
+ InputStores,
20
+ InvalidateBy,
21
+ } from "./internal/sources";
22
+ export {
23
+ AsyncData,
24
+ type AsyncError,
25
+ type AsyncIdle,
26
+ type AsyncLoading,
27
+ type AsyncSuccess,
28
+ } from "./calc/asyncData";
29
+ export { isLoading, type QueryState, toAsyncData } from "./calc/queryState";
30
+ export {
31
+ noopQueryStore,
32
+ QueryStore,
33
+ type QueryStoreApi,
34
+ resolveQueryStore,
35
+ } from "./internal/queryStore";
36
+ export {
37
+ noopQueryEvents,
38
+ QueryEvents,
39
+ type QueryEventsApi,
40
+ resolveQueryEvents,
41
+ } from "./internal/queryEvents";
42
+ export * as RemoteState from "./remote/remoteState";
43
+ // TS2742: plain type exports so declaration emit can name pending/failed types
44
+ export type {
45
+ FailedIntent,
46
+ PendingIntent,
47
+ RemoteStateClass,
48
+ RemoteStateSettled,
49
+ } from "./remote/remoteState";
50
+ export * as SyncedStore from "./synced/syncedStore";
51
+ // TS2742: plain type export for SyncedSource nameability
52
+ export type { SyncedSource, SyncedStoreClass } from "./synced/syncedStore";
53
+ export * as Boundary from "./boundary/boundary";
54
+ // TS2742: plain type exports for Boundary* nameability
63
55
  export type {
64
- BoundaryErrored,
65
- BoundaryPending,
66
- BoundaryReady,
67
- BoundaryState,
68
- } from './boundary/boundary'
69
- export * as Procedure from './procedure/procedure'
70
- export * as Channel from './channel/channel'
56
+ BoundaryErrored,
57
+ BoundaryPending,
58
+ BoundaryReady,
59
+ BoundaryState,
60
+ } from "./boundary/boundary";
61
+ export * as Procedure from "./procedure/procedure";
62
+ export * as Channel from "./channel/channel";
63
+ export * as Namespace from "./namespace/namespace";
64
+ export type { FeatureNamespace } from "./namespace/namespace";
71
65
 
72
- export * as Composition from './compose/composition'
66
+ export * as Composition from "./compose/composition";
73
67
  export type {
74
- CompositionClass,
75
- CompositionService,
76
- Frame,
77
- RenderEnv,
78
- } from './compose/composition'
79
- export {
80
- absent,
81
- each,
82
- type EachFill,
83
- isStructure,
84
- mount,
85
- one,
86
- type OneFill,
87
- type AbsentFill,
88
- type SlotFill,
89
- type Structure,
90
- structureEquals,
91
- type StructureSlots,
92
- StructureTypeId,
93
- when,
94
- } from './compose/structure'
95
- export { Props } from './compose/props'
96
- export {
97
- isSlot,
98
- type Node,
99
- slot,
100
- type SlotClass,
101
- type SlotInstance,
102
- type SlotProps,
103
- SlotTypeId,
104
- } from './compose/slot'
105
- export { type SlotRenderer } from './compose/host'
106
- export * as Ui from './compose/ui'
107
- export {
108
- type DerivedContract,
109
- isUi,
110
- type MadeView,
111
- ui,
112
- type UiClass,
113
- type UiContract,
114
- type UiManifest,
115
- UiTypeId,
116
- UiViewContract,
117
- type ViewImpl,
118
- type WireSchemas,
119
- type WiredUi,
120
- type WiredUiManifest,
121
- } from './compose/ui'
122
- export { provide } from './compose/provide'
123
- export type { Trigger } from './ui/trigger'
68
+ CompositionClass,
69
+ CompositionService,
70
+ Frame,
71
+ RenderEnv,
72
+ } from "./compose/composition";
73
+ export {
74
+ absent,
75
+ each,
76
+ type EachFill,
77
+ isStructure,
78
+ mount,
79
+ one,
80
+ type OneFill,
81
+ type AbsentFill,
82
+ type SlotFill,
83
+ type Structure,
84
+ structureEquals,
85
+ type StructureSlots,
86
+ StructureTypeId,
87
+ when,
88
+ } from "./compose/structure";
89
+ export { Props } from "./compose/props";
90
+ export {
91
+ isSlot,
92
+ type Node,
93
+ slot,
94
+ type SlotClass,
95
+ type SlotInstance,
96
+ type SlotProps,
97
+ SlotTypeId,
98
+ } from "./compose/slot";
99
+ export { type SlotRenderer } from "./compose/host";
100
+ export * as Ui from "./compose/ui";
101
+ export {
102
+ type DerivedContract,
103
+ isUi,
104
+ type MadeView,
105
+ ui,
106
+ type UiClass,
107
+ type UiContract,
108
+ type UiManifest,
109
+ UiTypeId,
110
+ UiViewContract,
111
+ type ViewImpl,
112
+ type WireSchemas,
113
+ type WiredUi,
114
+ type WiredUiManifest,
115
+ } from "./compose/ui";
116
+ export { provide } from "./compose/provide";
117
+ export type { Trigger } from "./ui/trigger";
124
118
 
125
- // Wire: the serializable rendered-tree model + pure diff/apply the remote
126
- // transport sends over the wire and the client folds back. See REMOTE_UI.md §3.
127
- export * as Wire from './wire/tree'
128
- export type { WireNode, WirePatch, WireProp, WireTree } from './wire/tree'
119
+ export * as Wire from "./wire/tree";
120
+ export type { WireNode, WirePatch, WireProp, WireTree } from "./wire/tree";
129
121
 
130
- // Triggers: the server-side handle registry bridging client trigger invocations
131
- // back to live triggers, decoding payloads via the event schema. See REMOTE_UI.md §4.
132
- export * as Triggers from './wire/triggers'
133
- export {
134
- TriggerRegistry,
135
- type TriggerHandle,
136
- type TriggerRegistryApi,
137
- UnknownTrigger,
138
- } from './wire/triggers'
122
+ export * as Triggers from "./wire/triggers";
123
+ export {
124
+ TriggerRegistry,
125
+ type TriggerHandle,
126
+ type TriggerRegistryApi,
127
+ UnknownTrigger,
128
+ } from "./wire/triggers";
139
129
 
140
- // Feature: the lazy/eager code-split unit. Definitions stay eager and reflectable;
141
- // the heavy `.live` layer loads on demand (an Effect, never a Promise). See LAZY.md.
142
- export * as Feature from './feature/feature'
143
- export {
144
- type EngineServices,
145
- type FailedProps,
146
- type FeatureBinding,
147
- FeatureBindingTypeId,
148
- type FeatureClass,
149
- type FeatureManifest,
150
- featureModule,
151
- type FeatureModule,
152
- isFeature,
153
- isFeatureBinding,
154
- lazyImport,
155
- type LoadedModule,
156
- mountFeature,
157
- type Placeholders,
158
- type ProvidedBy,
159
- type RequireCarrier,
160
- } from './feature/feature'
161
- export { type SlotChild } from './compose/slot'
130
+ export * as Feature from "./feature/feature";
131
+ export {
132
+ type EngineServices,
133
+ type FailedProps,
134
+ type FeatureBinding,
135
+ FeatureBindingTypeId,
136
+ type FeatureClass,
137
+ type FeatureRequirement,
138
+ type FeatureManifest,
139
+ featureModule,
140
+ featureModuleFromInput,
141
+ FeatureInput,
142
+ type FeatureModule,
143
+ type EagerFeatureClass,
144
+ isFeature,
145
+ isFeatureBinding,
146
+ lazyImport,
147
+ type LazyFeatureClass,
148
+ type LoadedModule,
149
+ mountFeature,
150
+ type Placeholders,
151
+ type ProvidedBy,
152
+ type RequireCarrier,
153
+ } from "./feature/feature";
154
+ export { type SlotChild } from "./compose/slot";
162
155
 
163
- // A scene bundles a composition with the closed wiring that runs it — the single
164
- // handle the react host, proofs, and the dev tool all consume.
165
- export {
166
- isScene,
167
- type MountedServices,
168
- profiledScene,
169
- type Scene,
170
- scene,
171
- sceneInstrumentation,
172
- seedScene,
173
- } from './scene/scene'
174
- export type { SeedsOf } from './state/stateGroup'
156
+ export {
157
+ isScene,
158
+ type MountedServices,
159
+ profiledScene,
160
+ type Scene,
161
+ type FeatureSceneConfig,
162
+ featureScene,
163
+ scene,
164
+ sceneInstrumentation,
165
+ seedScene,
166
+ } from "./scene/scene";
167
+ export type { SeedsOf } from "./state/stateGroup";
175
168
 
176
- // Runtime surface for hosts (`@reform/react`) and headless tests.
177
- export { Bus, busLayer, type Envelope, type Priority, publish, type Tagged } from './runtime/bus'
178
- // The renderer-neutral scene runtime: hosts layer their lifecycle on top;
179
- // headless tests/proofs/drive scripts consume it directly.
180
- export {
181
- type AppRuntime,
182
- type FeatureMountHandlers,
183
- makeAppRuntime,
184
- type RuntimeHandle,
185
- } from './runtime/appRuntime'
186
- export { Engine, type ReducerEntry, Reducers, reducersLayer } from './runtime/loop'
187
- // The profiler SPI: synchronous hooks the engine calls on its hot paths, plus
188
- // the FiberRef seam `@playfast/reform-profiler` writes through (`profiledScene`).
189
- export {
190
- CurrentInstrumentation,
191
- type Instrumentation,
192
- noopInstrumentation,
193
- resolveInstrumentation,
194
- type SpanEnd,
195
- } from './runtime/instrumentation'
196
- // The runtime-wide registry of live query handles. `AsyncCalc.invalidate` /
197
- // `AsyncCalc.refetch` and provider layers act on a query by name through it.
198
- export { Queries, queriesLayer, type QueryHandle, type QueryRegistry } from './runtime/queries'
199
- export {
200
- type ChannelClass,
201
- type ChannelPolicy,
202
- type ChannelRuntime,
203
- Channels,
204
- channelsLayer,
205
- type ProcedureEntry,
206
- Procedures,
207
- proceduresLayer,
208
- } from './channel/channel'
209
- export { CurrentTracker, type Subscribable, type Tracker } from './internal/track'
210
- export { CaptureSink, type CaptureSinkApi, type UiCapture } from './internal/capture'
211
- export {
212
- AsyncReducer,
213
- AsyncSceneLayer,
214
- DuplicateRegistration,
215
- FeatureLoadFailed,
216
- forceSync,
217
- InvalidProvideTarget,
218
- UnknownGroupState,
219
- } from './internal/errors'
220
- // Notification scheduler: hosts that need per-runtime isolation (concurrent SSR,
221
- // multiple mounted roots) provide `Notifications` upstream of their state layers;
222
- // everything else coalesces on the process-wide default.
223
- export {
224
- defaultScheduler,
225
- makeScheduler,
226
- Notifications,
227
- notificationsLayer,
228
- resolveScheduler,
229
- type Scheduler,
230
- } from './internal/scheduler'
231
- export { makeStore, type Store } from './internal/store'
232
- export type { FamilyStore } from './state/stateFamily'
233
- // Primitive-authoring toolkit — exported for first-party companion packages
234
- // (e.g. `@playfast/reform-db`, `@playfast/reform-resource`) that build their own
235
- // first-class `Source`s which must participate in dependency tracking and
236
- // reflection exactly like a `Calc`.
237
- export { type Kind, type Manifest, yieldableClass } from './definition/definition'
238
- export { readTracked } from './internal/track'
239
- export { sameKey, wireSources, type WiredSources } from './internal/sources'
169
+ export {
170
+ Bus,
171
+ busLayer,
172
+ type Envelope,
173
+ type Priority,
174
+ publish,
175
+ type Tagged,
176
+ } from "./runtime/bus";
177
+ export {
178
+ type AppRuntime,
179
+ type FeatureMountHandlers,
180
+ makeAppRuntime,
181
+ type RuntimeHandle,
182
+ } from "./runtime/appRuntime";
183
+ export {
184
+ Engine,
185
+ type ReducerEntry,
186
+ Reducers,
187
+ reducersLayer,
188
+ } from "./runtime/loop";
189
+ export {
190
+ CurrentEventBudget,
191
+ defaultEventBudget,
192
+ type EventBudget,
193
+ type ReformOptions,
194
+ resolveEventBudget,
195
+ } from "./runtime/eventBudget";
196
+ export {
197
+ CurrentInstrumentation,
198
+ type Instrumentation,
199
+ noopInstrumentation,
200
+ resolveInstrumentation,
201
+ type SpanEnd,
202
+ } from "./runtime/instrumentation";
203
+ export {
204
+ Queries,
205
+ queriesLayer,
206
+ type QueryHandle,
207
+ type QueryRegistry,
208
+ } from "./runtime/queries";
209
+ export {
210
+ type ChannelClass,
211
+ type ChannelPolicy,
212
+ type ChannelRuntime,
213
+ Channels,
214
+ channelsLayer,
215
+ type ProcedureEntry,
216
+ Procedures,
217
+ proceduresLayer,
218
+ } from "./channel/channel";
219
+ export {
220
+ CurrentTracker,
221
+ type Subscribable,
222
+ type Tracker,
223
+ } from "./internal/track";
224
+ export {
225
+ CaptureSink,
226
+ type CaptureSinkApi,
227
+ type UiCapture,
228
+ } from "./internal/capture";
229
+ export {
230
+ AsyncReducer,
231
+ AsyncSceneLayer,
232
+ DuplicateRegistration,
233
+ EventLoopOverflow,
234
+ FeatureLoadFailed,
235
+ forceSync,
236
+ InvalidProvideTarget,
237
+ UnknownGroupState,
238
+ } from "./internal/errors";
239
+ export {
240
+ defaultScheduler,
241
+ makeScheduler,
242
+ Notifications,
243
+ notificationsLayer,
244
+ resolveScheduler,
245
+ type Scheduler,
246
+ } from "./internal/scheduler";
247
+ export { makeStore, type Store } from "./internal/store";
248
+ export type { FamilyStore } from "./state/stateFamily";
249
+ export {
250
+ type Kind,
251
+ type Manifest,
252
+ yieldableClass,
253
+ } from "./definition/definition";
254
+ export { readTracked } from "./internal/track";
255
+ export { sameKey, wireSources, type WiredSources } from "./internal/sources";
@@ -1,38 +1,18 @@
1
1
  import { Context } from 'effect'
2
2
  import type { Trigger } from '../ui/trigger'
3
3
 
4
- /**
5
- * A single rendered UI node's observable surface: which contract rendered, the
6
- * props it received, and the event triggers it was handed. This is exactly what
7
- * a headless inspector (the proof facade, the dev tool) needs — no DOM, no JSX.
8
- */
9
4
  export interface UiCapture {
10
5
  readonly name: string
11
6
  readonly props: unknown
12
7
  readonly events: Record<string, Trigger<unknown>>
13
- /**
14
- * The stable slot-fill key this render was mounted under (an `each` item's
15
- * `key`, or a synthesized singleton key for a `one`). Present only on the
16
- * structure path — the legacy Node path defers slot expansion to host
17
- * components and has no fill key, so it is omitted there. Lets the proof
18
- * facade select a child by key (`byKey`) instead of by render index.
19
- */
20
8
  // oxlint-disable-next-line reform-rules/no-optional-fields -- omitted (not Option-none) on the legacy Node path; built in compose/structure.ts and read by the proof package, both outside this batch
21
9
  readonly key?: string
22
10
  }
23
11
 
24
- /** Where captured renders are reported, when a capturing host is present. */
25
12
  export interface CaptureSinkApi {
26
13
  record(capture: UiCapture): void
27
14
  }
28
15
 
29
- /**
30
- * An OPTIONAL service. Core's `ui` read consults it via `serviceOption`: when a
31
- * capturing host provides it (proofs, the dev tool), every resolved view reports
32
- * its `(props, events)` before rendering, giving a faithful tree without
33
- * replacing any presentation. Production never provides it, so nothing is
34
- * recorded and there is no cost beyond one `serviceOption` lookup per render.
35
- */
36
16
  const CaptureSinkBase: Context.TagClass<CaptureSink, 'reform/internal/CaptureSink', CaptureSinkApi> =
37
17
  Context.Tag('reform/internal/CaptureSink')<CaptureSink, CaptureSinkApi>()
38
18
 
@@ -1,16 +1,9 @@
1
1
  import type { Effect } from 'effect'
2
2
  import type { YieldWrap } from 'effect/Utils'
3
+ import type { AnyValue } from './variance'
3
4
 
4
- /**
5
- * The context (`R`) a generator body requires, recovered from the effects it
6
- * yields. Shared by `Composition.live` and `Procedure.live`, whose bodies are
7
- * both `Generator`s whose requirements must surface as their layer's `RIn`.
8
- *
9
- * A body that never yields (`Eff = never`) requires nothing — guard that case
10
- * first, or `infer R` resolves to `unknown` and leaks into the layer's `RIn`.
11
- */
12
5
  export type Ctx<Eff> = [Eff] extends [never]
13
6
  ? never
14
- : [Eff] extends [YieldWrap<Effect.Effect<any, any, infer R>>]
7
+ : [Eff] extends [YieldWrap<Effect.Effect<AnyValue, AnyValue, infer R>>]
15
8
  ? R
16
9
  : never
@@ -0,0 +1,9 @@
1
+ declare const process:
2
+ | { readonly env: { readonly NODE_ENV: string | undefined } }
3
+ | undefined
4
+
5
+ export const isDev: boolean =
6
+ typeof process === 'undefined' ||
7
+ typeof process.env === 'undefined' ||
8
+ // oxlint-disable-next-line reform-rules/no-process-env -- must stay a static member expression so bundlers substitute it and DCE dev-only branches; Config resolves at runtime and cannot
9
+ process.env.NODE_ENV !== 'production'
@@ -2,15 +2,9 @@ import { expect, it } from '@effect/vitest'
2
2
  import { Context, Effect, Layer, ManagedRuntime } from 'effect'
3
3
  import { AsyncSceneLayer, forceSync } from '../index'
4
4
 
5
- // `forceSync` is the sync-build boundary: reform materialises a scene's layer graph
6
- // with `runSync` so the first frame renders synchronously (D2). A layer whose acquire
7
- // suspends makes that `runSync` throw Effect's opaque `AsyncFiberException`; the
8
- // boundary translates it into `AsyncSceneLayer`, which names the cause and the fix.
9
-
10
5
  class Thing extends Context.Tag('test/Thing')<Thing, number>() {}
11
6
 
12
7
  it('translates an async layer build into AsyncSceneLayer', () => {
13
- // A layer whose acquire suspends — exactly what a consumer trips on.
14
8
  const asyncLayer = Layer.effect(Thing, Effect.as(Effect.sleep('1 millis'), 1))
15
9
  const runtime = ManagedRuntime.make(asyncLayer)
16
10
  expect(() => forceSync(() => runtime.runSync(Effect.context<Thing>()))).toThrow(AsyncSceneLayer)