@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.
- package/package.json +1 -1
- 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.ts +147 -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,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
|
|
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 {
|
|
5
|
-
import type { Tracker } from '../internal/track'
|
|
3
|
+
import { definitionClass } from '../definition/definition'
|
|
6
4
|
import { CurrentTracker } from '../internal/track'
|
|
7
|
-
import {
|
|
8
|
-
import type {
|
|
9
|
-
import type {
|
|
10
|
-
|
|
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
|
+
}
|