@playfast/reform 1.2.0 → 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 (50) hide show
  1. package/package.json +1 -1
  2. package/src/boundary/boundary.test.ts +26 -23
  3. package/src/calc/asyncCalc.invalidate.test.ts +258 -55
  4. package/src/calc/asyncCalc.test.ts +225 -85
  5. package/src/calc/asyncCalc.ts +27 -141
  6. package/src/calc/asyncCalcTypes.ts +147 -0
  7. package/src/calc/calc.test.ts +10 -11
  8. package/src/calc/calcFamily.test.ts +12 -12
  9. package/src/channel/channel.ts +8 -106
  10. package/src/channel/procedures.ts +108 -0
  11. package/src/compose/composition.ts +49 -246
  12. package/src/compose/compositionTypes.ts +249 -0
  13. package/src/event/event.fromSource.test.ts +12 -12
  14. package/src/feature/feature.ts +50 -1210
  15. package/src/feature/feature.typecheck.ts +2 -61
  16. package/src/feature/featureBinding.ts +158 -0
  17. package/src/feature/featureClass.ts +162 -0
  18. package/src/feature/featureConfig.ts +192 -0
  19. package/src/feature/featureFactory.ts +163 -0
  20. package/src/feature/featureModule.ts +136 -0
  21. package/src/feature/featureModule.typecheck.ts +63 -0
  22. package/src/feature/featureNativeTree.ts +152 -0
  23. package/src/feature/featureRequirement.ts +82 -0
  24. package/src/feature/featureVariants.ts +234 -0
  25. package/src/feature/mountFeature.ts +54 -0
  26. package/src/graph/closure.ts +45 -226
  27. package/src/graph/services.ts +95 -0
  28. package/src/graph/summary.ts +134 -0
  29. package/src/internal/queryDriver.ts +20 -89
  30. package/src/internal/queryDriverHydrate.ts +45 -0
  31. package/src/internal/queryDriverTypes.ts +8 -10
  32. package/src/internal/store.test.ts +27 -6
  33. package/src/remote/pendingQueue.ts +145 -0
  34. package/src/remote/remoteState.test.ts +28 -22
  35. package/src/remote/remoteState.ts +63 -489
  36. package/src/remote/remoteStateMake.ts +97 -0
  37. package/src/remote/remoteStateSend.ts +122 -0
  38. package/src/remote/remoteStateTypes.ts +155 -0
  39. package/src/runtime/appRuntime.activation.test.ts +9 -5
  40. package/src/runtime/appRuntime.test.ts +40 -16
  41. package/src/runtime/appRuntime.ts +17 -472
  42. package/src/runtime/capturedAppRuntime.ts +274 -0
  43. package/src/runtime/eventBudget.test.ts +38 -8
  44. package/src/runtime/featureMount.ts +94 -0
  45. package/src/runtime/hardening.test.ts +18 -4
  46. package/src/runtime/instrumentation.test.ts +49 -12
  47. package/src/runtime/loop.test.ts +51 -10
  48. package/src/runtime/runtimeHandle.ts +124 -0
  49. package/src/state/stateFamily.test.ts +25 -8
  50. package/src/testkit/flight.testkit.ts +9 -0
@@ -0,0 +1,108 @@
1
+ import { Context, type Effect, Layer, Option } from 'effect'
2
+ import { makeBucketCache } from '../internal/bucketCache'
3
+ import type { Tagged } from '../runtime/bus'
4
+
5
+ export interface ProcedureEntry {
6
+ readonly name: string
7
+ readonly channelName: string
8
+ readonly handles: ReadonlySet<string>
9
+ readonly run: (event: Tagged) => Effect.Effect<unknown, unknown, never>
10
+ }
11
+
12
+ export interface ProcedureRegistry {
13
+ readonly entries: Array<ProcedureEntry>
14
+ readonly byName: Map<string, Set<ProcedureEntry>>
15
+ readonly byTag: Map<string, Set<ProcedureEntry>>
16
+ readonly channelsByTag: Map<string, Set<string>>
17
+ readonly channelsFor: (tag: string) => ReadonlyArray<string>
18
+ readonly byChannelTag: Map<string, Map<string, Set<ProcedureEntry>>>
19
+ readonly register: (entry: ProcedureEntry) => void
20
+ readonly unregister: (entry: ProcedureEntry) => void
21
+ }
22
+
23
+ const ProceduresBase: Context.TagClass<Procedures, 'reform/Procedures', ProcedureRegistry> =
24
+ Context.Tag('reform/Procedures')<Procedures, ProcedureRegistry>()
25
+ export class Procedures extends ProceduresBase {}
26
+
27
+ const getOrCreate = <K, V>(map: Map<K, V>, key: K, make: () => V): V => {
28
+ const existing = map.get(key)
29
+ if (existing !== undefined) {
30
+ return existing
31
+ }
32
+ const created = make()
33
+ map.set(key, created)
34
+ return created
35
+ }
36
+
37
+ const dropFrom = <K, T>(map: Map<K, Set<T>>, key: K, target: T): void => {
38
+ const bucket = map.get(key)
39
+ if (bucket === undefined) {
40
+ return
41
+ }
42
+ bucket.delete(target)
43
+ if (bucket.size === 0) {
44
+ map.delete(key)
45
+ }
46
+ }
47
+
48
+ const makeProcedureRegistry = (): ProcedureRegistry => {
49
+ const entries = new Set<ProcedureEntry>()
50
+ const byName = new Map<string, Set<ProcedureEntry>>()
51
+ const byTag = new Map<string, Set<ProcedureEntry>>()
52
+ const channelsByTag = new Map<string, Set<string>>()
53
+ const channelsFor = makeBucketCache(channelsByTag)
54
+ const byChannelTag = new Map<string, Map<string, Set<ProcedureEntry>>>()
55
+ const snapshot: { value: Option.Option<Array<ProcedureEntry>> } = {
56
+ value: Option.some([]),
57
+ }
58
+ return {
59
+ get entries() {
60
+ return Option.getOrElse(snapshot.value, () => {
61
+ const current = Array.from(entries)
62
+ snapshot.value = Option.some(current)
63
+ return current
64
+ })
65
+ },
66
+ byName,
67
+ byTag,
68
+ channelsByTag,
69
+ channelsFor: channelsFor.read,
70
+ byChannelTag,
71
+ register: (entry) => {
72
+ entries.add(entry)
73
+ snapshot.value = Option.none()
74
+ getOrCreate(byName, entry.name, () => new Set()).add(entry)
75
+ entry.handles.forEach((tag) => {
76
+ getOrCreate(byTag, tag, () => new Set()).add(entry)
77
+ getOrCreate(channelsByTag, tag, () => new Set()).add(entry.channelName)
78
+ channelsFor.invalidate(tag)
79
+ const channelMap = getOrCreate(byChannelTag, entry.channelName, () => new Map())
80
+ getOrCreate(channelMap, tag, () => new Set()).add(entry)
81
+ })
82
+ },
83
+ unregister: (entry) => {
84
+ entries.delete(entry)
85
+ snapshot.value = Option.none()
86
+ dropFrom(byName, entry.name, entry)
87
+ entry.handles.forEach((tag) => {
88
+ dropFrom(byTag, tag, entry)
89
+ const channelMap = byChannelTag.get(entry.channelName)
90
+ if (channelMap !== undefined) {
91
+ dropFrom(channelMap, tag, entry)
92
+ if (channelMap.get(tag) === undefined) {
93
+ dropFrom(channelsByTag, tag, entry.channelName)
94
+ channelsFor.invalidate(tag)
95
+ }
96
+ if (channelMap.size === 0) {
97
+ byChannelTag.delete(entry.channelName)
98
+ }
99
+ }
100
+ })
101
+ },
102
+ }
103
+ }
104
+
105
+ export const proceduresLayer: Layer.Layer<Procedures> = Layer.sync(
106
+ Procedures,
107
+ makeProcedureRegistry,
108
+ )
@@ -1,237 +1,55 @@
1
- import { Context, Effect, GlobalValue, Layer, MutableRef, Option, Schema, Scope } from 'effect'
2
- import type { AST } from 'effect/SchemaAST'
1
+ import { Context, Effect, GlobalValue, Layer, MutableRef, Option, Schema } from 'effect'
3
2
  import type { YieldWrap } from 'effect/Utils'
4
- import { type Manifest, definitionClass } from '../definition/definition'
5
- import type { Tracker } from '../internal/track'
3
+ import { definitionClass } from '../definition/definition'
6
4
  import { CurrentTracker } from '../internal/track'
7
- import { type PropsId, type PropsTag, propsTag } from './props'
8
- import type { AnySlot, AnySlotInstance, SlotClass, SlotInstance } from './slot'
9
- import type { Structure } from './structure'
10
- import type { UiClass, UiContract, UiReflection } from './ui'
5
+ import { propsTag } from './props'
6
+ import type { UiContract } from './ui'
7
+ import type {
8
+ CompositionActivation,
9
+ CompositionActivationLayerInput,
10
+ CompositionCapture,
11
+ CompositionClass,
12
+ CompositionConfig,
13
+ CompositionDefinitionStatics,
14
+ CompositionId,
15
+ CompositionLayerInput,
16
+ CompositionManifest,
17
+ CompositionService,
18
+ ContextFreeProps,
19
+ ContractSlots,
20
+ EncodedPropsOf,
21
+ Frame,
22
+ GeneratorContext,
23
+ PropsOf,
24
+ RenderEnv,
25
+ RequiredSlotIdentities,
26
+ ScopedCompositionLiveConfig,
27
+ StatesOf,
28
+ UiCarrier,
29
+ } from './compositionTypes'
30
+
31
+ export { CompositionIdTypeId } from './compositionTypes'
32
+ export type {
33
+ AnyComposition,
34
+ CompositionActivation,
35
+ CompositionCapture,
36
+ CompositionClass,
37
+ CompositionConfig,
38
+ CompositionId,
39
+ CompositionInstance,
40
+ CompositionManifest,
41
+ CompositionPropsReceiver,
42
+ CompositionReflection,
43
+ CompositionService,
44
+ CompositionTag,
45
+ Frame,
46
+ HasExactCompositionId,
47
+ RenderEnv,
48
+ RequiredSlotIdentities,
49
+ ScopedCompositionLiveConfig,
50
+ SlotClassesOf,
51
+ } from './compositionTypes'
11
52
 
12
- export type Frame<C extends UiContract = UiContract> = Structure<C>
13
-
14
- export interface RenderEnv<out P = unknown> {
15
- readonly props: P
16
- readonly tracker: Tracker
17
- }
18
-
19
- export type CompositionActivation = Layer.Layer<never, never, never>
20
-
21
- export interface CompositionService<in out P = unknown, out C extends UiContract = UiContract> {
22
- readonly render: (env: RenderEnv<P>) => Effect.Effect<Frame<C>, never, never>
23
- readonly activation: (props: P) => Option.Option<CompositionActivation>
24
- }
25
-
26
- export const CompositionIdTypeId: unique symbol = Symbol.for('reform/CompositionId')
27
- export type CompositionIdTypeId = typeof CompositionIdTypeId
28
-
29
- export interface CompositionId<
30
- out N extends string,
31
- out Identity = unknown,
32
- out RequiredSlots = unknown,
33
- > {
34
- readonly [CompositionIdTypeId]: {
35
- readonly name: N
36
- readonly identity: Identity
37
- readonly requiredSlots: RequiredSlots
38
- }
39
- }
40
-
41
- type IsExact<Left, Right> = [Left] extends [Right] ? ([Right] extends [Left] ? true : false) : false
42
-
43
- type IsExactCompositionId<Service, N extends string, Identity> = Service extends {
44
- readonly [CompositionIdTypeId]: {
45
- readonly name: infer ServiceName extends string
46
- readonly identity: infer ServiceIdentity
47
- }
48
- }
49
- ? IsExact<ServiceName, N> extends true
50
- ? IsExact<ServiceIdentity, Identity>
51
- : false
52
- : false
53
-
54
- type IsAny<T> = 0 extends 1 & T ? true : false
55
-
56
- export type HasExactCompositionId<Services, N extends string, Identity> =
57
- IsAny<Services> extends true
58
- ? false
59
- : true extends IsExactCompositionId<Services, N, Identity>
60
- ? true
61
- : false
62
-
63
- export type CompositionTag<
64
- N extends string,
65
- P,
66
- C extends UiContract,
67
- Identity = unknown,
68
- > = Context.Tag<CompositionId<N, Identity, RequiredSlotIdentities<C>>, CompositionService<P, C>>
69
-
70
- export type SlotClassesOf<C extends UiContract> = C extends {
71
- readonly slots: infer Slots extends Record<string, AnySlotInstance>
72
- }
73
- ? {
74
- readonly [K in keyof Slots]: Slots[K] extends SlotInstance<
75
- infer Child,
76
- infer SlotName,
77
- infer SlotIdentity
78
- >
79
- ? SlotClass<Child, SlotName, SlotIdentity>
80
- : never
81
- }
82
- : Readonly<Record<PropertyKey, never>>
83
-
84
- type RequiredSlotIdentity<Slot> =
85
- Slot extends SlotInstance<infer _Target, infer _Name, infer Identity> ? Identity : never
86
-
87
- export type RequiredSlotIdentities<C extends UiContract> = C extends {
88
- readonly slots: infer Slots extends Record<string, AnySlotInstance>
89
- }
90
- ? RequiredSlotIdentity<Slots[keyof Slots]>
91
- : never
92
-
93
- type CompositionSlots<C extends UiContract> = C extends {
94
- readonly slots: infer Slots extends Record<string, AnySlotInstance>
95
- }
96
- ? [Slots[keyof Slots]] extends [never]
97
- ? {}
98
- : { readonly slots: SlotClassesOf<C> }
99
- : {}
100
-
101
- export type CompositionManifest<
102
- C extends UiContract = UiContract,
103
- N extends string = string,
104
- > = Manifest &
105
- Omit<CompositionConfig, 'slots' | 'ui'> &
106
- CompositionSlots<C> & {
107
- readonly kind: 'Composition'
108
- readonly name: N
109
- readonly ui: UiReflection<C>
110
- }
111
-
112
- export interface CompositionInstance<out Identity = unknown> {
113
- readonly _compositionIdentity: Identity
114
- }
115
-
116
- export interface CompositionReflection<
117
- out P = unknown,
118
- out C extends UiContract = UiContract,
119
- out S extends ReadonlyArray<unknown> = ReadonlyArray<unknown>,
120
- out N extends string = string,
121
- out Identity = unknown,
122
- > {
123
- new (): CompositionInstance<Identity>
124
- readonly manifest: CompositionManifest<C, N>
125
- readonly Props: P
126
- readonly Contract: C
127
- readonly States: S
128
- readonly parseProps: (input: unknown) => Option.Option<P>
129
- }
130
-
131
- export interface CompositionPropsReceiver<in P> {
132
- readonly acceptsProps: (props: P) => void
133
- }
134
-
135
- export type CompositionCapture<Result> = <
136
- P,
137
- C extends UiContract,
138
- S extends ReadonlyArray<unknown>,
139
- N extends string,
140
- Identity,
141
- >(
142
- composition: CompositionClass<P, C, S, N, Identity>,
143
- ) => Result
144
-
145
- export interface AnyComposition extends CompositionReflection {
146
- readonly capture: <Result>(visit: CompositionCapture<Result>) => Result
147
- }
148
-
149
- interface CompositionSchema {
150
- readonly ast: AST
151
- }
152
-
153
- type GeneratorContext<Eff> = [Eff] extends [never]
154
- ? never
155
- : [Eff] extends [YieldWrap<Effect.Effect<infer _Success, infer _Error, infer R>>]
156
- ? R
157
- : never
158
-
159
- // Property presence drives Config conditional inference; Option would erase it.
160
- interface CompositionConfigOptionalFields {
161
- readonly description: string
162
- readonly props: CompositionSchema
163
- readonly states: ReadonlyArray<unknown>
164
- readonly calcs: ReadonlyArray<unknown>
165
- readonly events: ReadonlyArray<unknown>
166
- readonly slots: Record<string, AnySlot>
167
- }
168
-
169
- export interface CompositionConfig extends Partial<CompositionConfigOptionalFields> {
170
- readonly title: string
171
- readonly ui: UiReflection
172
- }
173
-
174
- type PropsOf<Config> = Config extends { props: infer PropsSchema }
175
- ? PropsSchema extends Schema.Schema<infer P, infer _Encoded, infer _Context>
176
- ? P
177
- : unknown
178
- : unknown
179
-
180
- type EncodedPropsOf<Config> = Config extends { props: infer PropsSchema }
181
- ? PropsSchema extends Schema.Schema<infer _Props, infer Encoded, infer _Context>
182
- ? Encoded
183
- : unknown
184
- : unknown
185
-
186
- type StatesOf<Config> = Config extends {
187
- states: infer S extends ReadonlyArray<unknown>
188
- }
189
- ? S
190
- : readonly []
191
-
192
- export interface CompositionClass<
193
- in out P,
194
- in out C extends UiContract = UiContract,
195
- out S extends ReadonlyArray<unknown> = ReadonlyArray<unknown>,
196
- in out N extends string = string,
197
- in out Identity = unknown,
198
- >
199
- extends CompositionReflection<P, C, S, N, Identity>, CompositionPropsReceiver<P> {
200
- readonly Props: P
201
- readonly Contract: C
202
- readonly States: S
203
- readonly identity: symbol
204
- readonly props: PropsTag<P>
205
- readonly tag: CompositionTag<N, P, C, Identity>
206
- readonly capture: <Result>(visit: CompositionCapture<Result>) => Result
207
- }
208
-
209
- type CompositionDefinitionStatics<
210
- P,
211
- C extends UiContract,
212
- S extends ReadonlyArray<unknown>,
213
- N extends string,
214
- Identity,
215
- > = Pick<
216
- CompositionClass<P, C, S, N, Identity>,
217
- 'manifest' | 'identity' | 'props' | 'parseProps' | 'acceptsProps' | 'tag' | 'capture'
218
- >
219
-
220
- interface UiCarrier<C extends UiContract, UiName extends string> {
221
- readonly ui: UiClass<C, UiName>
222
- }
223
-
224
- type ContractSlots<C extends UiContract> = CompositionSlots<C>
225
-
226
- type ContextFreeProps<Config> = Config extends {
227
- readonly props: infer PropsSchema
228
- }
229
- ? PropsSchema extends Schema.Schema<infer _P, infer _Encoded, infer Context>
230
- ? [Context] extends [never]
231
- ? unknown
232
- : { readonly propsSchemaMustNotRequireServices: never }
233
- : unknown
234
- : unknown
235
53
 
236
54
  const compositionDefinitionSequence: MutableRef.MutableRef<number> = GlobalValue.globalValue(
237
55
  Symbol.for('reform/CompositionDefinitionSequence'),
@@ -282,21 +100,6 @@ export const make =
282
100
  return composition
283
101
  }
284
102
 
285
- type CompositionLayerInput<Eff> = Exclude<Exclude<GeneratorContext<Eff>, PropsId>, CurrentTracker>
286
-
287
- type CompositionActivationLayerInput<Eff> = Exclude<
288
- Exclude<GeneratorContext<Eff>, PropsId>,
289
- Scope.Scope
290
- >
291
-
292
- export interface ScopedCompositionLiveConfig<
293
- C extends UiContract,
294
- RenderEff extends YieldWrap<Effect.Effect<unknown, never, unknown>>,
295
- ActivationEff extends YieldWrap<Effect.Effect<unknown, never, unknown>>,
296
- > {
297
- readonly render: () => Generator<RenderEff, Frame<C>, never>
298
- readonly activate: () => Generator<ActivationEff, void, never>
299
- }
300
103
 
301
104
  export const live = <
302
105
  P,
@@ -0,0 +1,249 @@
1
+ import type { Context, Effect, Layer, Option, Schema, Scope } from 'effect'
2
+ import type { AST } from 'effect/SchemaAST'
3
+ import type { YieldWrap } from 'effect/Utils'
4
+ import type { Manifest } from '../definition/definition'
5
+ import type { Tracker } from '../internal/track'
6
+ import type { CurrentTracker } from '../internal/track'
7
+ import type { PropsId, PropsTag } from './props'
8
+ import type { AnySlot, AnySlotInstance, SlotClass, SlotInstance } from './slot'
9
+ import type { Structure } from './structure'
10
+ import type { UiClass, UiContract, UiReflection } from './ui'
11
+
12
+ export type Frame<C extends UiContract = UiContract> = Structure<C>
13
+
14
+ export interface RenderEnv<out P = unknown> {
15
+ readonly props: P
16
+ readonly tracker: Tracker
17
+ }
18
+
19
+ export type CompositionActivation = Layer.Layer<never, never, never>
20
+
21
+ export interface CompositionService<in out P = unknown, out C extends UiContract = UiContract> {
22
+ readonly render: (env: RenderEnv<P>) => Effect.Effect<Frame<C>, never, never>
23
+ readonly activation: (props: P) => Option.Option<CompositionActivation>
24
+ }
25
+
26
+ export const CompositionIdTypeId: unique symbol = Symbol.for('reform/CompositionId')
27
+ export type CompositionIdTypeId = typeof CompositionIdTypeId
28
+
29
+ export interface CompositionId<
30
+ out N extends string,
31
+ out Identity = unknown,
32
+ out RequiredSlots = unknown,
33
+ > {
34
+ readonly [CompositionIdTypeId]: {
35
+ readonly name: N
36
+ readonly identity: Identity
37
+ readonly requiredSlots: RequiredSlots
38
+ }
39
+ }
40
+
41
+ export type IsExact<Left, Right> = [Left] extends [Right] ? ([Right] extends [Left] ? true : false) : false
42
+
43
+ export type IsExactCompositionId<Service, N extends string, Identity> = Service extends {
44
+ readonly [CompositionIdTypeId]: {
45
+ readonly name: infer ServiceName extends string
46
+ readonly identity: infer ServiceIdentity
47
+ }
48
+ }
49
+ ? IsExact<ServiceName, N> extends true
50
+ ? IsExact<ServiceIdentity, Identity>
51
+ : false
52
+ : false
53
+
54
+ export type IsAny<T> = 0 extends 1 & T ? true : false
55
+
56
+ export type HasExactCompositionId<Services, N extends string, Identity> =
57
+ IsAny<Services> extends true
58
+ ? false
59
+ : true extends IsExactCompositionId<Services, N, Identity>
60
+ ? true
61
+ : false
62
+
63
+ export type CompositionTag<
64
+ N extends string,
65
+ P,
66
+ C extends UiContract,
67
+ Identity = unknown,
68
+ > = Context.Tag<CompositionId<N, Identity, RequiredSlotIdentities<C>>, CompositionService<P, C>>
69
+
70
+ export type SlotClassesOf<C extends UiContract> = C extends {
71
+ readonly slots: infer Slots extends Record<string, AnySlotInstance>
72
+ }
73
+ ? {
74
+ readonly [K in keyof Slots]: Slots[K] extends SlotInstance<
75
+ infer Child,
76
+ infer SlotName,
77
+ infer SlotIdentity
78
+ >
79
+ ? SlotClass<Child, SlotName, SlotIdentity>
80
+ : never
81
+ }
82
+ : Readonly<Record<PropertyKey, never>>
83
+
84
+ export type RequiredSlotIdentity<Slot> =
85
+ Slot extends SlotInstance<infer _Target, infer _Name, infer Identity> ? Identity : never
86
+
87
+ export type RequiredSlotIdentities<C extends UiContract> = C extends {
88
+ readonly slots: infer Slots extends Record<string, AnySlotInstance>
89
+ }
90
+ ? RequiredSlotIdentity<Slots[keyof Slots]>
91
+ : never
92
+
93
+ export type CompositionSlots<C extends UiContract> = C extends {
94
+ readonly slots: infer Slots extends Record<string, AnySlotInstance>
95
+ }
96
+ ? [Slots[keyof Slots]] extends [never]
97
+ ? {}
98
+ : { readonly slots: SlotClassesOf<C> }
99
+ : {}
100
+
101
+ export type CompositionManifest<
102
+ C extends UiContract = UiContract,
103
+ N extends string = string,
104
+ > = Manifest &
105
+ Omit<CompositionConfig, 'slots' | 'ui'> &
106
+ CompositionSlots<C> & {
107
+ readonly kind: 'Composition'
108
+ readonly name: N
109
+ readonly ui: UiReflection<C>
110
+ }
111
+
112
+ export interface CompositionInstance<out Identity = unknown> {
113
+ readonly _compositionIdentity: Identity
114
+ }
115
+
116
+ export interface CompositionReflection<
117
+ out P = unknown,
118
+ out C extends UiContract = UiContract,
119
+ out S extends ReadonlyArray<unknown> = ReadonlyArray<unknown>,
120
+ out N extends string = string,
121
+ out Identity = unknown,
122
+ > {
123
+ new (): CompositionInstance<Identity>
124
+ readonly manifest: CompositionManifest<C, N>
125
+ readonly Props: P
126
+ readonly Contract: C
127
+ readonly States: S
128
+ readonly parseProps: (input: unknown) => Option.Option<P>
129
+ }
130
+
131
+ export interface CompositionPropsReceiver<in P> {
132
+ readonly acceptsProps: (props: P) => void
133
+ }
134
+
135
+ export type CompositionCapture<Result> = <
136
+ P,
137
+ C extends UiContract,
138
+ S extends ReadonlyArray<unknown>,
139
+ N extends string,
140
+ Identity,
141
+ >(
142
+ composition: CompositionClass<P, C, S, N, Identity>,
143
+ ) => Result
144
+
145
+ export interface AnyComposition extends CompositionReflection {
146
+ readonly capture: <Result>(visit: CompositionCapture<Result>) => Result
147
+ }
148
+
149
+ export interface CompositionSchema {
150
+ readonly ast: AST
151
+ }
152
+
153
+ export type GeneratorContext<Eff> = [Eff] extends [never]
154
+ ? never
155
+ : [Eff] extends [YieldWrap<Effect.Effect<infer _Success, infer _Error, infer R>>]
156
+ ? R
157
+ : never
158
+
159
+ // Property presence drives Config conditional inference; Option would erase it.
160
+ export interface CompositionConfigOptionalFields {
161
+ readonly description: string
162
+ readonly props: CompositionSchema
163
+ readonly states: ReadonlyArray<unknown>
164
+ readonly calcs: ReadonlyArray<unknown>
165
+ readonly events: ReadonlyArray<unknown>
166
+ readonly slots: Record<string, AnySlot>
167
+ }
168
+
169
+ export interface CompositionConfig extends Partial<CompositionConfigOptionalFields> {
170
+ readonly title: string
171
+ readonly ui: UiReflection
172
+ }
173
+
174
+ export type PropsOf<Config> = Config extends { props: infer PropsSchema }
175
+ ? PropsSchema extends Schema.Schema<infer P, infer _Encoded, infer _Context>
176
+ ? P
177
+ : unknown
178
+ : unknown
179
+
180
+ export type EncodedPropsOf<Config> = Config extends { props: infer PropsSchema }
181
+ ? PropsSchema extends Schema.Schema<infer _Props, infer Encoded, infer _Context>
182
+ ? Encoded
183
+ : unknown
184
+ : unknown
185
+
186
+ export type StatesOf<Config> = Config extends {
187
+ states: infer S extends ReadonlyArray<unknown>
188
+ }
189
+ ? S
190
+ : readonly []
191
+
192
+ export interface CompositionClass<
193
+ in out P,
194
+ in out C extends UiContract = UiContract,
195
+ out S extends ReadonlyArray<unknown> = ReadonlyArray<unknown>,
196
+ in out N extends string = string,
197
+ in out Identity = unknown,
198
+ >
199
+ extends CompositionReflection<P, C, S, N, Identity>, CompositionPropsReceiver<P> {
200
+ readonly Props: P
201
+ readonly Contract: C
202
+ readonly States: S
203
+ readonly identity: symbol
204
+ readonly props: PropsTag<P>
205
+ readonly tag: CompositionTag<N, P, C, Identity>
206
+ readonly capture: <Result>(visit: CompositionCapture<Result>) => Result
207
+ }
208
+
209
+ export type CompositionDefinitionStatics<
210
+ P,
211
+ C extends UiContract,
212
+ S extends ReadonlyArray<unknown>,
213
+ N extends string,
214
+ Identity,
215
+ > = Pick<
216
+ CompositionClass<P, C, S, N, Identity>,
217
+ 'manifest' | 'identity' | 'props' | 'parseProps' | 'acceptsProps' | 'tag' | 'capture'
218
+ >
219
+
220
+ export interface UiCarrier<C extends UiContract, UiName extends string> {
221
+ readonly ui: UiClass<C, UiName>
222
+ }
223
+
224
+ export type ContractSlots<C extends UiContract> = CompositionSlots<C>
225
+
226
+ export type ContextFreeProps<Config> = Config extends {
227
+ readonly props: infer PropsSchema
228
+ }
229
+ ? PropsSchema extends Schema.Schema<infer _P, infer _Encoded, infer Context>
230
+ ? [Context] extends [never]
231
+ ? unknown
232
+ : { readonly propsSchemaMustNotRequireServices: never }
233
+ : unknown
234
+ : unknown
235
+ export type CompositionLayerInput<Eff> = Exclude<Exclude<GeneratorContext<Eff>, PropsId>, CurrentTracker>
236
+
237
+ export type CompositionActivationLayerInput<Eff> = Exclude<
238
+ Exclude<GeneratorContext<Eff>, PropsId>,
239
+ Scope.Scope
240
+ >
241
+
242
+ export interface ScopedCompositionLiveConfig<
243
+ C extends UiContract,
244
+ RenderEff extends YieldWrap<Effect.Effect<unknown, never, unknown>>,
245
+ ActivationEff extends YieldWrap<Effect.Effect<unknown, never, unknown>>,
246
+ > {
247
+ readonly render: () => Generator<RenderEff, Frame<C>, never>
248
+ readonly activate: () => Generator<ActivationEff, void, never>
249
+ }