@playfast/reform 1.1.1 → 1.2.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 (106) hide show
  1. package/README.md +85 -55
  2. package/package.json +17 -17
  3. package/src/boundary/boundary.test.ts +82 -33
  4. package/src/boundary/boundary.ts +141 -32
  5. package/src/calc/asyncCalc.invalidate.test.ts +733 -32
  6. package/src/calc/asyncCalc.test.ts +397 -225
  7. package/src/calc/asyncCalc.ts +115 -100
  8. package/src/calc/asyncCalcTypes.ts +147 -0
  9. package/src/calc/calc.test.ts +13 -16
  10. package/src/calc/calc.ts +44 -18
  11. package/src/calc/calcFamily.test.ts +90 -32
  12. package/src/calc/calcFamily.ts +56 -25
  13. package/src/calc/compose.ts +1 -14
  14. package/src/channel/channel.ts +25 -6
  15. package/src/channel/{procedureRegistry.ts → procedures.ts} +41 -23
  16. package/src/compose/composition.test.ts +19 -0
  17. package/src/compose/composition.ts +168 -81
  18. package/src/compose/compositionTypes.ts +249 -0
  19. package/src/compose/props.ts +13 -6
  20. package/src/compose/provide.ts +187 -46
  21. package/src/compose/slot.ts +156 -19
  22. package/src/compose/structure.test.ts +6 -3
  23. package/src/compose/structure.ts +106 -13
  24. package/src/compose/ui.test.ts +19 -3
  25. package/src/compose/ui.ts +147 -54
  26. package/src/compose/ui.typecheck.ts +40 -4
  27. package/src/definition/definition.ts +22 -9
  28. package/src/event/event.fromSource.test.ts +172 -0
  29. package/src/event/event.test.ts +10 -0
  30. package/src/event/event.ts +102 -18
  31. package/src/feature/feature.mount.test.ts +123 -56
  32. package/src/feature/feature.test.ts +67 -63
  33. package/src/feature/feature.ts +222 -262
  34. package/src/feature/feature.typecheck.ts +224 -91
  35. package/src/feature/featureBinding.ts +130 -20
  36. package/src/feature/featureClass.ts +162 -0
  37. package/src/feature/featureConfig.ts +192 -0
  38. package/src/feature/featureFactory.ts +163 -0
  39. package/src/feature/featureModule.ts +136 -0
  40. package/src/feature/featureModule.typecheck.ts +63 -0
  41. package/src/feature/featureNativeTree.ts +152 -0
  42. package/src/feature/featureRequirement.ts +82 -0
  43. package/src/feature/featureVariants.ts +234 -0
  44. package/src/feature/mountFeature.ts +54 -0
  45. package/src/graph/closure.ts +222 -0
  46. package/src/graph/services.ts +95 -0
  47. package/src/graph/summary.ts +134 -0
  48. package/src/index.ts +171 -245
  49. package/src/internal/bucketCache.ts +39 -0
  50. package/src/internal/capture.ts +9 -4
  51. package/src/internal/env.ts +1 -3
  52. package/src/internal/errors.ts +21 -10
  53. package/src/internal/queryDriver.ts +61 -25
  54. package/src/internal/queryDriverHydrate.ts +45 -0
  55. package/src/internal/queryDriverStore.ts +8 -5
  56. package/src/internal/queryDriverTypes.ts +11 -11
  57. package/src/internal/reuse.test.ts +10 -3
  58. package/src/internal/reuse.ts +5 -2
  59. package/src/internal/scheduler.ts +4 -1
  60. package/src/internal/sources.ts +25 -11
  61. package/src/internal/store.test.ts +27 -6
  62. package/src/internal/track.ts +3 -2
  63. package/src/internal.ts +222 -0
  64. package/src/namespace/namespace.ts +46 -25
  65. package/src/procedure/procedure.ts +38 -11
  66. package/src/reducer/reducer.ts +78 -34
  67. package/src/remote/pendingQueue.ts +145 -0
  68. package/src/remote/remoteState.test.ts +536 -354
  69. package/src/remote/remoteState.ts +145 -106
  70. package/src/remote/remoteState.typecheck.ts +47 -23
  71. package/src/remote/remoteStateMake.ts +97 -0
  72. package/src/remote/remoteStateSend.ts +95 -37
  73. package/src/remote/remoteStateTypes.ts +155 -0
  74. package/src/runtime/appRuntime.activation.test.ts +104 -0
  75. package/src/runtime/appRuntime.test.ts +187 -111
  76. package/src/runtime/appRuntime.ts +36 -130
  77. package/src/runtime/capturedAppRuntime.ts +274 -0
  78. package/src/runtime/eventBudget.test.ts +39 -12
  79. package/src/runtime/eventBudget.ts +9 -8
  80. package/src/runtime/featureMount.ts +94 -0
  81. package/src/runtime/hardening.test.ts +22 -13
  82. package/src/runtime/instrumentation.test.ts +213 -194
  83. package/src/runtime/instrumentation.ts +6 -11
  84. package/src/runtime/loop.test.ts +84 -7
  85. package/src/runtime/loop.ts +69 -40
  86. package/src/runtime/runtimeHandle.ts +124 -0
  87. package/src/scene/featureScene.test.ts +4 -2
  88. package/src/scene/scene.ts +234 -64
  89. package/src/scene/seedScene.test.ts +69 -86
  90. package/src/state/state.nominal.typecheck.ts +68 -0
  91. package/src/state/state.test.ts +17 -0
  92. package/src/state/state.ts +79 -26
  93. package/src/state/stateFamily.test.ts +39 -8
  94. package/src/state/stateFamily.ts +55 -22
  95. package/src/state/stateGroup.ts +53 -17
  96. package/src/state/token.ts +40 -10
  97. package/src/synced/syncedStore.ts +46 -23
  98. package/src/testkit/flight.testkit.ts +84 -0
  99. package/src/wire/tree.test.ts +8 -4
  100. package/src/wire/triggers.test.ts +6 -2
  101. package/src/wire/triggers.ts +14 -10
  102. package/src/calc/asyncCalcDefinitions.ts +0 -48
  103. package/src/internal/ctx.ts +0 -9
  104. package/src/remote/remoteStateDefinition.ts +0 -135
  105. package/src/remote/remoteStateLayers.ts +0 -118
  106. package/src/remote/remoteStateLiveTypes.ts +0 -59
@@ -1,291 +1,251 @@
1
- import { Context, Effect, Layer, Option, type Scope } from 'effect'
2
- import { type Engine } from '../runtime/loop'
3
- import { type StoresOf } from '../internal/sources'
4
- import { FeatureLoadFailed } from '../internal/errors'
5
- import { definitionClass } from '../definition/definition'
6
- import { type CompositionClass } from '../compose/composition'
7
- import { type UiContract } from '../compose/ui'
8
- import { publish, type Tagged } from '../runtime/bus'
9
- import type { AnyValue } from '../internal/variance'
1
+ import { Effect, type Schema } from 'effect'
2
+ import type { AnyComposition, CompositionClass } from '../compose/composition'
3
+ import type { UiContract } from '../compose/ui'
4
+ import { FeatureGraphValidTypeId } from '../graph/closure'
5
+ import type { EagerFeatureClass, LazyFeatureClass } from './featureClass'
10
6
  import {
11
- type FeatureBinding,
12
- FeatureBindingTypeId,
13
- type FeatureManifest,
7
+ type EagerFeatureConfigExternalApi,
8
+ type ExactFeatureCompositionConfig,
9
+ type FeatureMountProps,
10
+ type FeatureUsesInput,
11
+ isMakeImplementationConfig,
12
+ type LazyFeatureConfigExternalApi,
13
+ } from './featureConfig'
14
+ import type { FeatureRequirement } from './featureRequirement'
15
+ import { makeEagerFeature, makeLazyFeature } from './featureVariants'
16
+
17
+ export type { EngineServices } from '../runtime/loop'
18
+ export { FeatureGraphValidTypeId }
19
+ export { FeatureInput } from './featureRequirement'
20
+ export type {
21
+ FeatureModuleInput,
22
+ FeatureRequirement,
23
+ ProvidedBy,
24
+ RequireCarrier,
25
+ } from './featureRequirement'
26
+ export { featureModule, featureModuleFromInput, lazyImport } from './featureModule'
27
+ export type { AnyLoadedModule, FeatureModule, LoadedModule, LoadedModuleCapture } from './featureModule'
28
+ export { FeatureBindingTypeId, isFeatureBinding, tree } from './featureBinding'
29
+ export type {
30
+ AnyFeatureBinding,
31
+ AnyFeatureBindingExternalApi,
32
+ CapturedFeatureBinding,
33
+ FailedProps,
34
+ FeatureBinding,
35
+ FeatureBindingCapture,
36
+ FeatureBindingType,
37
+ FeatureDefinition,
38
+ FeatureIdentityToken,
39
+ FeatureManifest,
40
+ FeatureOpenServices,
41
+ FeatureSupportOpenServices,
42
+ FeatureToken,
43
+ FeatureTreeCarrier,
44
+ ProvideableFeature,
14
45
  } from './featureBinding'
15
- export {
16
- type FailedProps,
17
- type FeatureBinding,
18
- FeatureBindingTypeId,
19
- type FeatureManifest,
20
- isFeatureBinding,
21
- type LoadedModule,
22
- } from './featureBinding'
23
- export type { FeatureBindingTypeId as FeatureBindingType } from './featureBinding'
24
-
25
- // Features must only name EngineServices as open RIn — never provide them (shared engine)
26
- export type EngineServices = Layer.Layer.Success<typeof Engine>
27
-
28
- const FeatureInputBase: Context.TagClass<FeatureInput, 'reform/FeatureInput', unknown> = Context.Tag(
29
- 'reform/FeatureInput',
30
- )<FeatureInput, unknown>()
31
- export class FeatureInput extends FeatureInputBase {}
32
-
33
- export type RequireCarrier =
34
- | { readonly store: Context.Tag<AnyValue, AnyValue> }
35
- | { readonly members: ReadonlyArray<{ readonly store: Context.Tag<AnyValue, AnyValue> }> }
36
-
37
- export type FeatureRequirement = RequireCarrier | Context.Tag<AnyValue, AnyValue>
38
-
39
- // Covariant RIn: layer reading outside EngineServices | ProvidedBy is unassignable
40
- export type ProvidedBy<Requires extends ReadonlyArray<FeatureRequirement>> = {
41
- [I in keyof Requires]: Requires[I] extends Context.Tag<infer Identifier, AnyValue>
42
- ? Identifier
43
- : Requires[I] extends {
44
- readonly members: infer M extends ReadonlyArray<unknown>
45
- }
46
- ? StoresOf<M>
47
- : Requires[I] extends { readonly store: Context.Tag<infer Svc, AnyValue> }
48
- ? Svc
49
- : never
50
- }[number]
51
-
52
- export interface FeatureModule<
53
- ROut,
54
- Requires extends ReadonlyArray<FeatureRequirement>,
55
- UsesInput extends boolean = false,
56
- > {
57
- readonly requires: Requires
58
- readonly usesInput: UsesInput
59
- readonly layer: Layer.Layer<
60
- ROut,
61
- never,
62
- | EngineServices
63
- | ProvidedBy<Requires>
64
- | (UsesInput extends true ? FeatureInput : never)
65
- >
66
- }
67
-
68
- export const featureModule = <const Requires extends ReadonlyArray<FeatureRequirement>, ROut>(
69
- requires: Requires,
70
- layer: Layer.Layer<ROut, never, EngineServices | ProvidedBy<Requires>>,
71
- ): FeatureModule<ROut, Requires, false> => ({ requires, usesInput: false, layer })
72
-
73
- export const featureModuleFromInput = <
74
- const Requires extends ReadonlyArray<FeatureRequirement>,
75
- ROut,
76
- >(
77
- requires: Requires,
78
- layer: Layer.Layer<ROut, never, EngineServices | ProvidedBy<Requires> | FeatureInput>,
79
- ): FeatureModule<ROut, Requires, true> => ({ requires, usesInput: true, layer })
80
-
81
- // Seals Promise import as Effect; rejection → FeatureLoadFailed
82
- export const lazyImport = <M>(
83
- thunk: () => Promise<{ readonly default: M }>,
84
- ): Effect.Effect<M, FeatureLoadFailed> =>
85
- Effect.tryPromise({
86
- try: thunk,
87
- catch: (cause) => new FeatureLoadFailed({ cause }),
88
- }).pipe(Effect.map((module) => module.default))
89
-
90
- export interface FeatureClass<
91
- P,
92
- C extends UiContract,
93
- ROut,
94
- Requires extends ReadonlyArray<FeatureRequirement>,
95
- SupportRequires extends ReadonlyArray<FeatureRequirement> = readonly [],
96
- > {
97
- new (): {}
98
- readonly manifest: FeatureManifest
99
- readonly Props: P
100
- readonly Contract: C
101
- readonly composition: CompositionClass<P, C>
102
- readonly Requires: Requires
103
- readonly load: Effect.Effect<FeatureModule<ROut, Requires, boolean>, FeatureLoadFailed>
104
- readonly binding: FeatureBinding
105
- readonly eagerModule: FeatureModule<ROut, Requires, false> | undefined
106
- readonly supportModule: FeatureModule<never, SupportRequires, false> | undefined
107
- }
108
-
109
- export interface EagerFeatureClass<
110
- P,
111
- C extends UiContract,
112
- ROut,
113
- Requires extends ReadonlyArray<FeatureRequirement>,
114
- > extends FeatureClass<P, C, ROut, Requires> {
115
- readonly eagerModule: FeatureModule<ROut, Requires, false>
116
- readonly supportModule: undefined
117
- }
118
-
119
- export interface LazyFeatureClass<
120
- P,
121
- C extends UiContract,
122
- ROut,
123
- Requires extends ReadonlyArray<FeatureRequirement>,
124
- SupportRequires extends ReadonlyArray<FeatureRequirement> = readonly [],
125
- > extends FeatureClass<P, C, ROut, Requires, SupportRequires> {
126
- readonly eagerModule: undefined
127
- }
128
-
129
- export type AnyFeature = FeatureClass<
130
- AnyValue,
131
- AnyValue,
132
- AnyValue,
133
- ReadonlyArray<FeatureRequirement>,
134
- ReadonlyArray<FeatureRequirement>
135
- >
136
-
137
- export const isFeature = (candidate: unknown): candidate is AnyFeature =>
138
- (typeof candidate === 'function' || typeof candidate === 'object') &&
139
- candidate !== null &&
140
- 'manifest' in candidate &&
141
- typeof candidate.manifest === 'object' &&
142
- candidate.manifest !== null &&
143
- 'kind' in candidate.manifest &&
144
- candidate.manifest.kind === 'Feature'
145
-
146
- export interface Placeholders {
147
- readonly loading: CompositionClass<AnyValue, UiContract>
148
- readonly failed: CompositionClass<AnyValue, UiContract>
149
- }
150
-
151
- interface FeatureConfigBaseExternalApi<P, C extends UiContract> {
152
- readonly composition: CompositionClass<P, C>
153
- readonly boot?: ReadonlyArray<Tagged>
154
- }
155
-
156
- type EagerFeatureConfigExternalApi<
157
- P,
158
- C extends UiContract,
159
- ROut,
160
- Requires extends ReadonlyArray<FeatureRequirement>,
161
- > = FeatureConfigBaseExternalApi<P, C> & {
162
- readonly loadingStrategy?: 'default'
163
- readonly module: FeatureModule<ROut, Requires, false>
164
- }
165
-
166
- type LazyFeatureConfigExternalApi<
167
- P,
168
- C extends UiContract,
169
- ROut,
170
- Requires extends ReadonlyArray<FeatureRequirement>,
171
- SupportOut,
172
- SupportRequires extends ReadonlyArray<FeatureRequirement>,
173
- > = FeatureConfigBaseExternalApi<P, C> & {
174
- readonly loadingStrategy: 'lazy'
175
- readonly load: Effect.Effect<FeatureModule<ROut, Requires, boolean>, FeatureLoadFailed>
176
- readonly placeholder: Placeholders
177
- readonly eager?: FeatureModule<SupportOut, SupportRequires, false>
178
- }
46
+ export type {
47
+ EagerFeatureNativeTree,
48
+ FeatureNativeTree,
49
+ LazyFeatureNativeTree,
50
+ } from './featureNativeTree'
51
+ export type { EagerFeatureClass, FeatureClass, LazyFeatureClass } from './featureClass'
52
+ export { isFeature } from './featureConfig'
53
+ export type { FeatureMountProps, Placeholders } from './featureConfig'
54
+ export { mountFeature } from './mountFeature'
179
55
 
180
56
  export function make<
181
57
  P,
182
58
  C extends UiContract,
183
59
  ROut,
184
- Requires extends ReadonlyArray<FeatureRequirement>,
60
+ DirectRequires extends ReadonlyArray<FeatureRequirement>,
61
+ OpenServices,
62
+ S extends ReadonlyArray<unknown>,
63
+ N extends string,
64
+ Identity,
65
+ const Composition extends CompositionClass<P, C, S, N, Identity>,
185
66
  >(
186
67
  name: string,
187
- config: EagerFeatureConfigExternalApi<P, C, ROut, Requires>,
188
- ): EagerFeatureClass<P, C, ROut, Requires>
189
- export function make<
68
+ config: EagerFeatureConfigExternalApi<
69
+ P,
70
+ C,
71
+ ROut,
72
+ DirectRequires,
73
+ OpenServices,
74
+ S,
75
+ N,
76
+ Identity
77
+ > & { readonly composition: Composition },
78
+ ): EagerFeatureClass<
79
+ Identity,
190
80
  P,
81
+ C,
82
+ ROut,
83
+ DirectRequires,
84
+ OpenServices,
85
+ S,
86
+ N,
87
+ Identity,
88
+ Composition
89
+ >
90
+ export function make<
91
+ CompositionProps,
191
92
  C extends UiContract,
192
93
  ROut,
193
- Requires extends ReadonlyArray<FeatureRequirement>,
194
- SupportOut,
195
- SupportRequires extends ReadonlyArray<FeatureRequirement>,
94
+ DirectRequires extends ReadonlyArray<FeatureRequirement>,
95
+ OpenServices,
96
+ S extends ReadonlyArray<unknown>,
97
+ N extends string,
98
+ Identity,
99
+ const Composition extends CompositionClass<CompositionProps, C, S, N, Identity>,
100
+ const Loading extends AnyComposition,
101
+ const Failed extends AnyComposition,
102
+ const InputSchema extends Schema.Schema.AnyNoContext = never,
103
+ SupportOut = never,
104
+ SupportDirectRequires extends ReadonlyArray<FeatureRequirement> = readonly [],
105
+ SupportOpenServices = never,
196
106
  >(
197
107
  name: string,
198
108
  config: LazyFeatureConfigExternalApi<
199
- P,
109
+ CompositionProps,
200
110
  C,
201
111
  ROut,
202
- Requires,
112
+ DirectRequires,
113
+ OpenServices,
203
114
  SupportOut,
204
- SupportRequires
205
- > & { readonly eager: FeatureModule<SupportOut, SupportRequires, false> },
206
- ): LazyFeatureClass<P, C, ROut, Requires, SupportRequires>
115
+ SupportDirectRequires,
116
+ SupportOpenServices,
117
+ S,
118
+ N,
119
+ Identity,
120
+ InputSchema,
121
+ Loading,
122
+ Failed
123
+ > & { readonly composition: Composition },
124
+ ): LazyFeatureClass<
125
+ Identity,
126
+ FeatureMountProps<FeatureUsesInput<InputSchema>, CompositionProps, InputSchema>,
127
+ C,
128
+ ROut,
129
+ DirectRequires,
130
+ OpenServices,
131
+ SupportOut,
132
+ SupportDirectRequires,
133
+ SupportOpenServices,
134
+ FeatureUsesInput<InputSchema>,
135
+ S,
136
+ N,
137
+ CompositionProps,
138
+ Identity,
139
+ Composition
140
+ >
207
141
  export function make<
208
- P,
142
+ CompositionProps,
209
143
  C extends UiContract,
210
144
  ROut,
211
- Requires extends ReadonlyArray<FeatureRequirement>,
145
+ DirectRequires extends ReadonlyArray<FeatureRequirement>,
146
+ OpenServices,
147
+ S extends ReadonlyArray<unknown>,
148
+ N extends string,
149
+ Identity,
150
+ Composition extends CompositionClass<CompositionProps, C, S, N, Identity>,
151
+ Loading extends AnyComposition,
152
+ Failed extends AnyComposition,
153
+ InputSchema extends Schema.Schema.AnyNoContext = never,
154
+ SupportOut = never,
155
+ SupportDirectRequires extends ReadonlyArray<FeatureRequirement> = readonly [],
156
+ SupportOpenServices = never,
212
157
  >(
213
158
  name: string,
214
- config: LazyFeatureConfigExternalApi<P, C, ROut, Requires, never, readonly []>,
215
- ): LazyFeatureClass<P, C, ROut, Requires>
216
- export function make(
217
- name: string,
218
- config:
159
+ config: (
219
160
  | EagerFeatureConfigExternalApi<
220
- AnyValue,
221
- UiContract,
222
- never,
223
- ReadonlyArray<FeatureRequirement>
161
+ CompositionProps,
162
+ C,
163
+ ROut,
164
+ DirectRequires,
165
+ OpenServices,
166
+ S,
167
+ N,
168
+ Identity
224
169
  >
225
170
  | LazyFeatureConfigExternalApi<
226
- AnyValue,
227
- UiContract,
228
- never,
229
- ReadonlyArray<FeatureRequirement>,
230
- never,
231
- ReadonlyArray<FeatureRequirement>
232
- >,
233
- ): AnyFeature {
234
- const strategy: 'lazy' | 'default' = config.loadingStrategy ?? 'default'
235
- const load = 'load' in config ? config.load : Effect.succeed(config.module)
236
- const eagerModule = 'module' in config ? config.module : undefined
237
- const supportModule = 'eager' in config ? config.eager : undefined
238
- const placeholder = 'placeholder' in config ? config.placeholder : undefined
239
-
240
- const manifest: FeatureManifest = {
241
- kind: 'Feature',
242
- name,
243
- strategy,
244
- composition: config.composition.manifest,
245
- placeholder: Option.fromNullable(placeholder).pipe(
246
- Option.map((placeholders) => ({
247
- loading: placeholders.loading.manifest,
248
- failed: placeholders.failed.manifest,
249
- })),
250
- ),
171
+ CompositionProps,
172
+ C,
173
+ ROut,
174
+ DirectRequires,
175
+ OpenServices,
176
+ SupportOut,
177
+ SupportDirectRequires,
178
+ SupportOpenServices,
179
+ S,
180
+ N,
181
+ Identity,
182
+ InputSchema,
183
+ Loading,
184
+ Failed
185
+ >
186
+ ) &
187
+ ExactFeatureCompositionConfig<Composition>,
188
+ ):
189
+ | EagerFeatureClass<
190
+ Identity,
191
+ CompositionProps,
192
+ C,
193
+ ROut,
194
+ DirectRequires,
195
+ OpenServices,
196
+ S,
197
+ N,
198
+ Identity,
199
+ Composition
200
+ >
201
+ | LazyFeatureClass<
202
+ Identity,
203
+ FeatureMountProps<FeatureUsesInput<InputSchema>, CompositionProps, InputSchema>,
204
+ C,
205
+ ROut,
206
+ DirectRequires,
207
+ OpenServices,
208
+ SupportOut,
209
+ SupportDirectRequires,
210
+ SupportOpenServices,
211
+ FeatureUsesInput<InputSchema>,
212
+ S,
213
+ N,
214
+ CompositionProps,
215
+ Identity,
216
+ Composition
217
+ > {
218
+ if (!isMakeImplementationConfig(config)) {
219
+ return Effect.runSync(Effect.dieMessage(`Invalid Feature config for ${name}`))
251
220
  }
252
-
253
- const binding: FeatureBinding = {
254
- [FeatureBindingTypeId]: FeatureBindingTypeId,
255
- manifest,
256
- composition: config.composition,
257
- strategy,
258
- load,
259
- ...(placeholder ? { placeholder: { loading: placeholder.loading, failed: placeholder.failed } } : {}),
260
- boot: Option.getOrElse(Option.fromNullable(config.boot), () => []),
221
+ if (config.loadingStrategy === 'lazy') {
222
+ return makeLazyFeature<
223
+ CompositionProps,
224
+ C,
225
+ ROut,
226
+ DirectRequires,
227
+ OpenServices,
228
+ SupportOut,
229
+ SupportDirectRequires,
230
+ SupportOpenServices,
231
+ S,
232
+ N,
233
+ Identity,
234
+ Composition,
235
+ InputSchema,
236
+ Loading,
237
+ Failed
238
+ >(name, config)
261
239
  }
262
-
263
- return definitionClass<AnyFeature>({
264
- manifest,
265
- load,
266
- binding,
267
- eagerModule,
268
- supportModule,
269
- composition: config.composition,
270
- })
240
+ return makeEagerFeature<
241
+ CompositionProps,
242
+ C,
243
+ ROut,
244
+ DirectRequires,
245
+ OpenServices,
246
+ S,
247
+ N,
248
+ Identity,
249
+ Composition
250
+ >(name, config)
271
251
  }
272
-
273
- // oxlint-disable-next-line reform-rules/prefer-effect-fn -- generic export: Effect.fn's inferred type isn't portable under isolatedDeclarations
274
- export const mountFeature = <R>(
275
- binding: FeatureBinding,
276
- engineContext: Context.Context<R>,
277
- props?: unknown,
278
- ): Effect.Effect<Context.Context<unknown>, FeatureLoadFailed, Scope.Scope> =>
279
- Effect.gen(function* () {
280
- const module = yield* binding.load
281
- // oxlint-disable-next-line reform-rules/no-type-assertion -- documented erasure boundary: re-attach the concrete engine context to the type-erased `LoadedModule` layer (mirrors `buildRuntime`); RIn was verified cast-free at `provide`/scene wiring
282
- const featureLayer = module.layer as Layer.Layer<unknown, never, R | FeatureInput>
283
- const featureContext = Context.add(engineContext, FeatureInput, props)
284
- const context = yield* Layer.build(
285
- Layer.provideMerge(featureLayer, Layer.succeedContext(featureContext)),
286
- )
287
- yield* Effect.forEach(binding.boot, (event) => publish('High', event), { discard: true }).pipe(
288
- Effect.provide(context),
289
- )
290
- return context
291
- })