foldkit 0.141.1 → 0.142.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/subscription/public.d.ts +1 -1
- package/dist/subscription/public.d.ts.map +1 -1
- package/dist/subscription/subscription.d.ts +97 -5
- package/dist/subscription/subscription.d.ts.map +1 -1
- package/dist/subscription/subscription.js +102 -16
- package/dist/update/public.d.ts +2 -2
- package/dist/update/public.d.ts.map +1 -1
- package/dist/update/public.js +1 -1
- package/dist/update/update.d.ts +113 -3
- package/dist/update/update.d.ts.map +1 -1
- package/dist/update/update.js +59 -6
- package/package.json +6 -6
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { aggregate, lift, make, persistent } from './subscription.js';
|
|
2
|
-
export type { EntryWithoutKeepAlive, Subscription, Subscriptions, } from './subscription.js';
|
|
2
|
+
export type { EntryWithoutKeepAlive, GatedDependencies, Subscription, Subscriptions, } from './subscription.js';
|
|
3
3
|
export { animationFrame } from './animationFrame.js';
|
|
4
4
|
export type { AnimationFrameConfig } from './animationFrame.js';
|
|
5
5
|
export { fromEvent, fromEventFilterMap } from './fromEvent.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"public.d.ts","sourceRoot":"","sources":["../../src/subscription/public.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAA;AAErE,YAAY,EACV,qBAAqB,EACrB,YAAY,EACZ,aAAa,GACd,MAAM,mBAAmB,CAAA;AAE1B,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAA;AAEpD,YAAY,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAA;AAE/D,OAAO,EAAE,SAAS,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAA;AAE9D,YAAY,EAAE,eAAe,EAAE,wBAAwB,EAAE,MAAM,gBAAgB,CAAA"}
|
|
1
|
+
{"version":3,"file":"public.d.ts","sourceRoot":"","sources":["../../src/subscription/public.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAA;AAErE,YAAY,EACV,qBAAqB,EACrB,iBAAiB,EACjB,YAAY,EACZ,aAAa,GACd,MAAM,mBAAmB,CAAA;AAE1B,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAA;AAEpD,YAAY,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAA;AAE/D,OAAO,EAAE,SAAS,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAA;AAE9D,YAAY,EAAE,eAAe,EAAE,wBAAwB,EAAE,MAAM,gBAAgB,CAAA"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type Equivalence, Schema, Stream } from 'effect';
|
|
1
|
+
import { type Equivalence, Option, Schema, Stream } from 'effect';
|
|
2
2
|
type SubscriptionBrand = {
|
|
3
3
|
readonly __subscription: never;
|
|
4
4
|
};
|
|
@@ -133,16 +133,108 @@ export declare const aggregate: <Model, Message, Services = never>() => (...reco
|
|
|
133
133
|
export declare const persistent: <Message, Services = never>(stream: Stream.Stream<Message, never, Services>) => EntryWithoutKeepAlive<unknown, Message, Record<string, never>, Services>;
|
|
134
134
|
type ChildModelOf<ChildSubscriptions> = ChildSubscriptions[keyof ChildSubscriptions] extends Subscription<infer ChildModel, any, any, any> ? ChildModel : never;
|
|
135
135
|
type ChildMessageOf<ChildSubscriptions> = ChildSubscriptions[keyof ChildSubscriptions] extends Subscription<any, infer ChildMessage, any, any> ? ChildMessage : never;
|
|
136
|
+
/**
|
|
137
|
+
* The dependencies of a Subscription lifted through a parent's `when` gate:
|
|
138
|
+
* the child entry's own dependencies under `maybeDependencies`, and `None`
|
|
139
|
+
* for as long as the parent holds the gate closed.
|
|
140
|
+
*
|
|
141
|
+
* A closed gate is a real teardown rather than a paused Stream. The entry's
|
|
142
|
+
* Stream is torn down, and the child's `modelToDependencies` does not run
|
|
143
|
+
* again until the parent reopens the gate, so child state that changes behind
|
|
144
|
+
* a closed gate causes no restarts.
|
|
145
|
+
*/
|
|
146
|
+
export type GatedDependencies<Dependencies> = Readonly<{
|
|
147
|
+
maybeDependencies: Option.Option<Dependencies>;
|
|
148
|
+
}>;
|
|
149
|
+
type WhenPredicate<ParentModel> = (parentModel: ParentModel) => boolean;
|
|
150
|
+
/**
|
|
151
|
+
* The per-entry form of a lift's `when`: a partial map from entry name to
|
|
152
|
+
* gate. Entries the map names are gated, and entries it omits are lifted
|
|
153
|
+
* ungated.
|
|
154
|
+
*/
|
|
155
|
+
export type EntryGates<ParentModel, Subscriptions> = Readonly<Partial<Record<keyof Subscriptions, WhenPredicate<ParentModel>>>>;
|
|
156
|
+
type LiftConfig<ParentModel, ParentMessage, Subscriptions> = Readonly<{
|
|
157
|
+
toChildModel: (parentModel: ParentModel) => ChildModelOf<Subscriptions>;
|
|
158
|
+
toParentMessage: (message: ChildMessageOf<Subscriptions>) => ParentMessage;
|
|
159
|
+
}>;
|
|
160
|
+
type GatedLiftConfig<ParentModel, ParentMessage, Subscriptions> = LiftConfig<ParentModel, ParentMessage, Subscriptions> & Readonly<{
|
|
161
|
+
when: WhenPredicate<ParentModel>;
|
|
162
|
+
}>;
|
|
163
|
+
type PerEntryGatedLiftConfig<ParentModel, ParentMessage, Subscriptions, Gates> = LiftConfig<ParentModel, ParentMessage, Subscriptions> & Readonly<{
|
|
164
|
+
when: Gates;
|
|
165
|
+
}>;
|
|
166
|
+
type LiftedSubscriptions<ParentModel, ParentMessage, Subscriptions> = {
|
|
167
|
+
readonly [K in keyof Subscriptions]: Subscriptions[K] extends Subscription<any, any, infer Dependencies, infer Services> ? Subscription<ParentModel, ParentMessage, Dependencies, Services> : never;
|
|
168
|
+
};
|
|
169
|
+
type GatedLiftedSubscriptions<ParentModel, ParentMessage, Subscriptions> = {
|
|
170
|
+
readonly [K in keyof Subscriptions]: Subscriptions[K] extends Subscription<any, any, infer Dependencies, infer Services> ? Subscription<ParentModel, ParentMessage, GatedDependencies<Dependencies>, Services> : never;
|
|
171
|
+
};
|
|
172
|
+
/**
|
|
173
|
+
* The lifted record for a per-entry `when`. An entry the gate map names
|
|
174
|
+
* carries {@link GatedDependencies}; an entry it omits keeps the child's own
|
|
175
|
+
* dependencies. When the gate map's own type is not precise enough to say
|
|
176
|
+
* which is which, because the call passed explicit `ParentModel` and
|
|
177
|
+
* `ParentMessage` type arguments and so suppressed inference on it, the entry
|
|
178
|
+
* carries both possibilities.
|
|
179
|
+
*/
|
|
180
|
+
type PerEntryGatedLiftedSubscriptions<ParentModel, ParentMessage, Subscriptions, Gates> = {
|
|
181
|
+
readonly [K in keyof Subscriptions]: Subscriptions[K] extends Subscription<any, any, infer Dependencies, infer Services> ? K extends keyof Gates ? undefined extends Gates[K] ? Subscription<ParentModel, ParentMessage, Dependencies | GatedDependencies<Dependencies>, Services> : Subscription<ParentModel, ParentMessage, GatedDependencies<Dependencies>, Services> : Subscription<ParentModel, ParentMessage, Dependencies, Services> : never;
|
|
182
|
+
};
|
|
136
183
|
/**
|
|
137
184
|
* Lifts a record of child Subscriptions into a parent's Model and Message
|
|
138
185
|
* context, applying a Model accessor and a Message wrapper uniformly to
|
|
139
186
|
* every entry. Per-entry dependency types, schemas, and `keepAliveEquivalence`
|
|
140
187
|
* settings are preserved; each lifted entry's variant (with or without
|
|
141
188
|
* `readDependencies`) matches its source entry's.
|
|
189
|
+
*
|
|
190
|
+
* The optional `when` is the parent's own gate. The parent writes it here on
|
|
191
|
+
* its `lift` call and answers it from the parent Model, which is what makes
|
|
192
|
+
* it useful: it carries the half of a condition the child cannot see, such as
|
|
193
|
+
* the route a page Submodel sits behind. The child neither declares nor sees
|
|
194
|
+
* the gate, and keeps holding its own half in `modelToDependencies`. A gated
|
|
195
|
+
* entry runs only while its gate returns `true`, and a closed gate tears it
|
|
196
|
+
* down.
|
|
197
|
+
*
|
|
198
|
+
* `when` takes either shape:
|
|
199
|
+
*
|
|
200
|
+
* - One predicate gates every entry in the record, for the common case where
|
|
201
|
+
* the whole child answers to one parent condition.
|
|
202
|
+
* - An {@link EntryGates} map gates entries by name, for a child whose
|
|
203
|
+
* Subscriptions answer to different parent conditions. Entries the map
|
|
204
|
+
* omits are lifted ungated. A child never has to organize its records
|
|
205
|
+
* around its parent's gating.
|
|
206
|
+
*
|
|
207
|
+
* Gating rewrites a gated entry's dependencies to {@link GatedDependencies},
|
|
208
|
+
* so its `readDependencies` returns the last dependencies seen through an
|
|
209
|
+
* open gate. Ungated entries keep the child's dependencies untouched. Passing
|
|
210
|
+
* `ParentModel` and `ParentMessage` explicitly suppresses inference on the
|
|
211
|
+
* gate map, which leaves each named entry's dependencies as either shape; let
|
|
212
|
+
* both infer from an annotated `toChildModel` when you want the exact per
|
|
213
|
+
* entry types.
|
|
214
|
+
*
|
|
215
|
+
* @example
|
|
216
|
+
* ```ts
|
|
217
|
+
* const homeSubscriptions = Subscription.lift(Home.subscriptions)<
|
|
218
|
+
* Model,
|
|
219
|
+
* Message
|
|
220
|
+
* >({
|
|
221
|
+
* toChildModel: model => model.home,
|
|
222
|
+
* toParentMessage: message => GotHomeMessage({ message }),
|
|
223
|
+
* when: ({ route }) => route._tag === 'Home',
|
|
224
|
+
* })
|
|
225
|
+
*
|
|
226
|
+
* const roomSubscriptions = Subscription.lift(Room.subscriptions)({
|
|
227
|
+
* toChildModel: (model: Model) => model.room,
|
|
228
|
+
* toParentMessage: (message: Room.Message.Message): Message =>
|
|
229
|
+
* GotRoomMessage({ message }),
|
|
230
|
+
* when: { roomKeyboard: ({ route }) => route._tag === 'Room' },
|
|
231
|
+
* })
|
|
232
|
+
* ```
|
|
142
233
|
*/
|
|
143
|
-
export declare const lift: <Subscriptions extends Readonly<Record<string, Subscription<any, any, any, any>>>>(subscriptions: Subscriptions) =>
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
234
|
+
export declare const lift: <Subscriptions extends Readonly<Record<string, Subscription<any, any, any, any>>>>(subscriptions: Subscriptions) => {
|
|
235
|
+
<ParentModel, ParentMessage>(config: GatedLiftConfig<ParentModel, ParentMessage, Subscriptions>): GatedLiftedSubscriptions<ParentModel, ParentMessage, Subscriptions>;
|
|
236
|
+
<ParentModel, ParentMessage_1, Gates extends EntryGates<ParentModel, Subscriptions> = EntryGates<ParentModel, Subscriptions>>(config: PerEntryGatedLiftConfig<ParentModel, ParentMessage_1, Subscriptions, Gates>): PerEntryGatedLiftedSubscriptions<ParentModel, ParentMessage_1, Subscriptions, Gates>;
|
|
237
|
+
<ParentModel, ParentMessage_2>(config: LiftConfig<ParentModel, ParentMessage_2, Subscriptions>): LiftedSubscriptions<ParentModel, ParentMessage_2, Subscriptions>;
|
|
238
|
+
};
|
|
147
239
|
export {};
|
|
148
240
|
//# sourceMappingURL=subscription.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"subscription.d.ts","sourceRoot":"","sources":["../../src/subscription/subscription.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,WAAW,EAAU,MAAM,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"subscription.d.ts","sourceRoot":"","sources":["../../src/subscription/subscription.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Record, Schema, Stream } from 'effect';
|
|
1
|
+
import { Option, Record, Schema, Stream, pipe } from 'effect';
|
|
2
2
|
/**
|
|
3
3
|
* Declares a Subscriptions record. The Model, Message, and optional Services
|
|
4
4
|
* generics are provided up front; the entries record follows, built from
|
|
@@ -63,30 +63,116 @@ export const persistent = (stream) => ({
|
|
|
63
63
|
modelToDependencies: () => ({}),
|
|
64
64
|
dependenciesToStream: () => stream,
|
|
65
65
|
});
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
66
|
+
const toEntryWhen = (when, key) => {
|
|
67
|
+
if (when === undefined) {
|
|
68
|
+
return Option.none();
|
|
69
|
+
}
|
|
70
|
+
else if (typeof when === 'function') {
|
|
71
|
+
return Option.some(when);
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
return pipe(Record.get(when, key), Option.flatMap(Option.fromNullishOr));
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
const toParentStream = (config, stream) => Stream.map(stream, config.toParentMessage);
|
|
78
|
+
const toLiftedEntry = (subscription, config) => {
|
|
76
79
|
const modelToDependencies = (parentModel) => subscription.modelToDependencies(config.toChildModel(parentModel));
|
|
77
|
-
const wrapStream = (stream) => Stream.map(stream, config.toParentMessage);
|
|
78
80
|
if (subscription.keepAliveEquivalence !== undefined) {
|
|
79
81
|
return {
|
|
80
82
|
dependenciesSchema: subscription.dependenciesSchema,
|
|
81
83
|
modelToDependencies,
|
|
82
84
|
keepAliveEquivalence: subscription.keepAliveEquivalence,
|
|
83
|
-
dependenciesToStream: (dependencies, readDependencies) =>
|
|
85
|
+
dependenciesToStream: (dependencies, readDependencies) => toParentStream(config, subscription.dependenciesToStream(dependencies, readDependencies)),
|
|
84
86
|
};
|
|
85
87
|
}
|
|
86
88
|
return {
|
|
87
89
|
dependenciesSchema: subscription.dependenciesSchema,
|
|
88
90
|
modelToDependencies,
|
|
89
|
-
dependenciesToStream: (dependencies) =>
|
|
91
|
+
dependenciesToStream: (dependencies) => toParentStream(config, subscription.dependenciesToStream(dependencies)),
|
|
90
92
|
};
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
+
};
|
|
94
|
+
const toGatedEntry = (subscription, config, when) => {
|
|
95
|
+
const dependenciesSchema = Schema.Struct({
|
|
96
|
+
maybeDependencies: Schema.Option(subscription.dependenciesSchema),
|
|
97
|
+
});
|
|
98
|
+
const modelToDependencies = (parentModel) => ({
|
|
99
|
+
maybeDependencies: pipe(parentModel, Option.liftPredicate(when), Option.map(config.toChildModel), Option.map(subscription.modelToDependencies)),
|
|
100
|
+
});
|
|
101
|
+
if (subscription.keepAliveEquivalence !== undefined) {
|
|
102
|
+
const maybeKeepAliveEquivalence = Option.makeEquivalence(subscription.keepAliveEquivalence);
|
|
103
|
+
return {
|
|
104
|
+
dependenciesSchema,
|
|
105
|
+
modelToDependencies,
|
|
106
|
+
keepAliveEquivalence: (left, right) => maybeKeepAliveEquivalence(left.maybeDependencies, right.maybeDependencies),
|
|
107
|
+
dependenciesToStream: (gatedDependencies, readGatedDependencies) => Option.match(gatedDependencies.maybeDependencies, {
|
|
108
|
+
onNone: () => Stream.empty,
|
|
109
|
+
onSome: dependencies => toParentStream(config, subscription.dependenciesToStream(dependencies, () => Option.getOrElse(readGatedDependencies().maybeDependencies, () => dependencies))),
|
|
110
|
+
}),
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
return {
|
|
114
|
+
dependenciesSchema,
|
|
115
|
+
modelToDependencies,
|
|
116
|
+
dependenciesToStream: (gatedDependencies) => Option.match(gatedDependencies.maybeDependencies, {
|
|
117
|
+
onNone: () => Stream.empty,
|
|
118
|
+
onSome: dependencies => toParentStream(config, subscription.dependenciesToStream(dependencies)),
|
|
119
|
+
}),
|
|
120
|
+
};
|
|
121
|
+
};
|
|
122
|
+
/**
|
|
123
|
+
* Lifts a record of child Subscriptions into a parent's Model and Message
|
|
124
|
+
* context, applying a Model accessor and a Message wrapper uniformly to
|
|
125
|
+
* every entry. Per-entry dependency types, schemas, and `keepAliveEquivalence`
|
|
126
|
+
* settings are preserved; each lifted entry's variant (with or without
|
|
127
|
+
* `readDependencies`) matches its source entry's.
|
|
128
|
+
*
|
|
129
|
+
* The optional `when` is the parent's own gate. The parent writes it here on
|
|
130
|
+
* its `lift` call and answers it from the parent Model, which is what makes
|
|
131
|
+
* it useful: it carries the half of a condition the child cannot see, such as
|
|
132
|
+
* the route a page Submodel sits behind. The child neither declares nor sees
|
|
133
|
+
* the gate, and keeps holding its own half in `modelToDependencies`. A gated
|
|
134
|
+
* entry runs only while its gate returns `true`, and a closed gate tears it
|
|
135
|
+
* down.
|
|
136
|
+
*
|
|
137
|
+
* `when` takes either shape:
|
|
138
|
+
*
|
|
139
|
+
* - One predicate gates every entry in the record, for the common case where
|
|
140
|
+
* the whole child answers to one parent condition.
|
|
141
|
+
* - An {@link EntryGates} map gates entries by name, for a child whose
|
|
142
|
+
* Subscriptions answer to different parent conditions. Entries the map
|
|
143
|
+
* omits are lifted ungated. A child never has to organize its records
|
|
144
|
+
* around its parent's gating.
|
|
145
|
+
*
|
|
146
|
+
* Gating rewrites a gated entry's dependencies to {@link GatedDependencies},
|
|
147
|
+
* so its `readDependencies` returns the last dependencies seen through an
|
|
148
|
+
* open gate. Ungated entries keep the child's dependencies untouched. Passing
|
|
149
|
+
* `ParentModel` and `ParentMessage` explicitly suppresses inference on the
|
|
150
|
+
* gate map, which leaves each named entry's dependencies as either shape; let
|
|
151
|
+
* both infer from an annotated `toChildModel` when you want the exact per
|
|
152
|
+
* entry types.
|
|
153
|
+
*
|
|
154
|
+
* @example
|
|
155
|
+
* ```ts
|
|
156
|
+
* const homeSubscriptions = Subscription.lift(Home.subscriptions)<
|
|
157
|
+
* Model,
|
|
158
|
+
* Message
|
|
159
|
+
* >({
|
|
160
|
+
* toChildModel: model => model.home,
|
|
161
|
+
* toParentMessage: message => GotHomeMessage({ message }),
|
|
162
|
+
* when: ({ route }) => route._tag === 'Home',
|
|
163
|
+
* })
|
|
164
|
+
*
|
|
165
|
+
* const roomSubscriptions = Subscription.lift(Room.subscriptions)({
|
|
166
|
+
* toChildModel: (model: Model) => model.room,
|
|
167
|
+
* toParentMessage: (message: Room.Message.Message): Message =>
|
|
168
|
+
* GotRoomMessage({ message }),
|
|
169
|
+
* when: { roomKeyboard: ({ route }) => route._tag === 'Room' },
|
|
170
|
+
* })
|
|
171
|
+
* ```
|
|
172
|
+
*/
|
|
173
|
+
export const lift = (subscriptions) => (config) =>
|
|
174
|
+
/* eslint-disable-next-line @typescript-eslint/consistent-type-assertions */
|
|
175
|
+
Record.map(subscriptions, (subscription, key) => Option.match(toEntryWhen(config.when, key), {
|
|
176
|
+
onNone: () => toLiftedEntry(subscription, config),
|
|
177
|
+
onSome: entryWhen => toGatedEntry(subscription, config, entryWhen),
|
|
178
|
+
}));
|
package/dist/update/public.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export { combine, foldChild, refresh } from './index.js';
|
|
2
|
-
export type { Commands, Return, ReturnWithOutMessage, Step, StepWithOutMessage, Refreshable, ChildFold, ChildFoldWithOutMessage, ChildFoldWithParentOutMessage, Fold, FoldWithOutMessage, } from './index.js';
|
|
1
|
+
export { combine, foldChild, foldChildStep, refresh } from './index.js';
|
|
2
|
+
export type { Commands, Return, ReturnWithOutMessage, Step, StepWithOutMessage, Refreshable, ChildFold, ChildFoldWithOutMessage, ChildFoldWithParentOutMessage, ChildStepFold, ChildStepFoldWithOutMessage, FoldContext, Fold, FoldWithOutMessage, } from './index.js';
|
|
3
3
|
//# sourceMappingURL=public.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"public.d.ts","sourceRoot":"","sources":["../../src/update/public.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,YAAY,CAAA;
|
|
1
|
+
{"version":3,"file":"public.d.ts","sourceRoot":"","sources":["../../src/update/public.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,YAAY,CAAA;AAEvE,YAAY,EACV,QAAQ,EACR,MAAM,EACN,oBAAoB,EACpB,IAAI,EACJ,kBAAkB,EAClB,WAAW,EACX,SAAS,EACT,uBAAuB,EACvB,6BAA6B,EAC7B,aAAa,EACb,2BAA2B,EAC3B,WAAW,EACX,IAAI,EACJ,kBAAkB,GACnB,MAAM,YAAY,CAAA"}
|
package/dist/update/public.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { combine, foldChild, refresh } from './index.js';
|
|
1
|
+
export { combine, foldChild, foldChildStep, refresh } from './index.js';
|
package/dist/update/update.d.ts
CHANGED
|
@@ -119,6 +119,47 @@ export type ChildFold<ParentModel, ParentMessage, ChildModel, Input, ChildMessag
|
|
|
119
119
|
write: (model: ParentModel, nextChildModel: ChildModel) => ParentModel;
|
|
120
120
|
toParentMessage: (message: ChildMessage) => ParentMessage;
|
|
121
121
|
}>;
|
|
122
|
+
/** The lifters a `foldOutMessage` receives as its second parameter,
|
|
123
|
+
* already bound to the fold config's `toParentMessage`.
|
|
124
|
+
*
|
|
125
|
+
* The fold lifts the Commands the child's `update` returns on its own.
|
|
126
|
+
* This covers the other case: a Command the parent returns on the
|
|
127
|
+
* child's behalf from the OutMessage Step, whose result Message is the
|
|
128
|
+
* child's and therefore still needs wrapping, such as a parent handling
|
|
129
|
+
* a child's `Requested*` fact by returning the child's Command that
|
|
130
|
+
* fulfills it, built with context only the parent holds.
|
|
131
|
+
*
|
|
132
|
+
* The lifters apply the same lift the fold gives the child's own
|
|
133
|
+
* Commands, so the Step writes no `Command.mapMessage` call and keeps
|
|
134
|
+
* no second copy of the wrapper, and the mapping stays recorded on the
|
|
135
|
+
* Command for `Story.Command.resolve` and `Scene.Command.resolve`.
|
|
136
|
+
*
|
|
137
|
+
* The annotated standalone const takes both parameters, so the match
|
|
138
|
+
* moves from `M.type` to `M.value` on the OutMessage:
|
|
139
|
+
*
|
|
140
|
+
* ```ts
|
|
141
|
+
* const foldLoginOutMessage: (
|
|
142
|
+
* outMessage: Login.OutMessage,
|
|
143
|
+
* context: Update.FoldContext<Login.Message, Message>,
|
|
144
|
+
* ) => Update.Step<Model, Message> = (outMessage, { liftCommand }) =>
|
|
145
|
+
* M.value(outMessage).pipe(
|
|
146
|
+
* M.withReturnType<Update.Step<Model, Message>>(),
|
|
147
|
+
* M.tagsExhaustive({
|
|
148
|
+
* RequestedMagicLink: ({ email }) => model => [
|
|
149
|
+
* model,
|
|
150
|
+
* [
|
|
151
|
+
* liftCommand(
|
|
152
|
+
* Login.SendMagicLink({ email, redirectRoute: model.route }),
|
|
153
|
+
* ),
|
|
154
|
+
* ],
|
|
155
|
+
* ],
|
|
156
|
+
* }),
|
|
157
|
+
* )
|
|
158
|
+
* ``` */
|
|
159
|
+
export type FoldContext<ChildMessage, ParentMessage> = Readonly<{
|
|
160
|
+
liftCommand: <E = never, R = never>(command: Command<ChildMessage, E, R>) => Command<ParentMessage, E, R>;
|
|
161
|
+
liftCommands: <E = never, R = never>(commands: ReadonlyArray<Command<ChildMessage, E, R>>) => ReadonlyArray<Command<ParentMessage, E, R>>;
|
|
162
|
+
}>;
|
|
122
163
|
/** {@link ChildFold} for a child whose update returns
|
|
123
164
|
* {@link ReturnWithOutMessage}, adding the fifth capability:
|
|
124
165
|
*
|
|
@@ -127,13 +168,15 @@ export type ChildFold<ParentModel, ParentMessage, ChildModel, Input, ChildMessag
|
|
|
127
168
|
* already written back, and its Commands follow the child's in the
|
|
128
169
|
* returned batch. Match on the OutMessage tag inside
|
|
129
170
|
* (`M.tagsExhaustive`), and build a multi-step fold with
|
|
130
|
-
* {@link combine}.
|
|
171
|
+
* {@link combine}. Takes an optional second parameter, a
|
|
172
|
+
* {@link FoldContext} of lifters bound to `toParentMessage`, for a
|
|
173
|
+
* Command the Step returns whose result is the child's Message. */
|
|
131
174
|
export type ChildFoldWithOutMessage<ParentModel, ParentMessage, ChildModel, Input, ChildMessage, ChildOutMessage, R = never> = Readonly<{
|
|
132
175
|
update: (childModel: ChildModel, input: Input) => ReturnWithOutMessage<ChildModel, ChildMessage, ChildOutMessage, R>;
|
|
133
176
|
read: (model: ParentModel) => Option.Option<ChildModel>;
|
|
134
177
|
write: (model: ParentModel, nextChildModel: ChildModel) => ParentModel;
|
|
135
178
|
toParentMessage: (message: ChildMessage) => ParentMessage;
|
|
136
|
-
foldOutMessage: (outMessage: ChildOutMessage) => Step<ParentModel, ParentMessage, R>;
|
|
179
|
+
foldOutMessage: (outMessage: ChildOutMessage, context: FoldContext<ChildMessage, ParentMessage>) => Step<ParentModel, ParentMessage, R>;
|
|
137
180
|
}>;
|
|
138
181
|
/** {@link ChildFoldWithOutMessage} for a parent that is itself a
|
|
139
182
|
* Submodel, so the fold's result carries the parent's own OutMessage
|
|
@@ -154,7 +197,7 @@ export type ChildFoldWithParentOutMessage<ParentModel, ParentMessage, ChildModel
|
|
|
154
197
|
write: (model: ParentModel, nextChildModel: ChildModel) => ParentModel;
|
|
155
198
|
toParentMessage: (message: ChildMessage) => ParentMessage;
|
|
156
199
|
toParentOutMessage: (outMessage: ChildOutMessage) => Option.Option<ParentOutMessage>;
|
|
157
|
-
foldOutMessage?: (outMessage: ChildOutMessage) => Step<ParentModel, ParentMessage, R>;
|
|
200
|
+
foldOutMessage?: (outMessage: ChildOutMessage, context: FoldContext<ChildMessage, ParentMessage>) => Step<ParentModel, ParentMessage, R>;
|
|
158
201
|
}>;
|
|
159
202
|
/** {@link Step} for an update that also surfaces an OutMessage to its
|
|
160
203
|
* parent: maps a Model to a {@link ReturnWithOutMessage} over the same
|
|
@@ -202,11 +245,21 @@ export type FoldWithOutMessage<ParentModel, ParentMessage, Input, ParentOutMessa
|
|
|
202
245
|
* the Model with the child already written back, and its Commands
|
|
203
246
|
* follow the child's in the returned batch.
|
|
204
247
|
*
|
|
248
|
+
* `foldOutMessage` takes an optional second parameter, a
|
|
249
|
+
* {@link FoldContext} carrying `liftCommand` and `liftCommands` bound to
|
|
250
|
+
* this config's `toParentMessage`. Reach for it when the Step returns a
|
|
251
|
+
* Command that produces the child's Message, such as an animating
|
|
252
|
+
* component's overridable leave Command.
|
|
253
|
+
*
|
|
205
254
|
* A parent that is itself a Submodel passes a
|
|
206
255
|
* {@link ChildFoldWithParentOutMessage} and receives a
|
|
207
256
|
* {@link FoldWithOutMessage}, whose results carry the parent's own
|
|
208
257
|
* OutMessage channel as a third element.
|
|
209
258
|
*
|
|
259
|
+
* An entry point that takes nothing but the child Model, such as
|
|
260
|
+
* `Dialog.close`, has no input to pass: fold it with
|
|
261
|
+
* {@link foldChildStep}, which returns the {@link Step} directly.
|
|
262
|
+
*
|
|
210
263
|
* `update` closes over per-dispatch context, and the data-last form
|
|
211
264
|
* composes with {@link combine}, here to put a navigation Command ahead
|
|
212
265
|
* of the child's:
|
|
@@ -229,4 +282,61 @@ export declare const foldChild: {
|
|
|
229
282
|
<ParentModel, ParentMessage, ChildModel, Input, ChildMessage, ChildOutMessage, R = never>(childFold: ChildFoldWithOutMessage<ParentModel, ParentMessage, ChildModel, Input, ChildMessage, ChildOutMessage, R>): Fold<ParentModel, ParentMessage, Input, R>;
|
|
230
283
|
<ParentModel, ParentMessage, ChildModel, Input, ChildMessage, R = never>(childFold: ChildFold<ParentModel, ParentMessage, ChildModel, Input, ChildMessage, R>): Fold<ParentModel, ParentMessage, Input, R>;
|
|
231
284
|
};
|
|
285
|
+
/** {@link ChildFold} for an entry point that takes nothing but the child
|
|
286
|
+
* Model, such as `Dialog.close` or a Submodel's `informRouteChanged` that
|
|
287
|
+
* derives everything it needs from its own state. There is no `input`, so
|
|
288
|
+
* {@link foldChildStep} returns the {@link Step} itself rather than a dual
|
|
289
|
+
* {@link Fold}. */
|
|
290
|
+
export type ChildStepFold<ParentModel, ParentMessage, ChildModel, ChildMessage, R = never> = Readonly<{
|
|
291
|
+
update: (childModel: ChildModel) => Return<ChildModel, ChildMessage, R>;
|
|
292
|
+
read: (model: ParentModel) => Option.Option<ChildModel>;
|
|
293
|
+
write: (model: ParentModel, nextChildModel: ChildModel) => ParentModel;
|
|
294
|
+
toParentMessage: (message: ChildMessage) => ParentMessage;
|
|
295
|
+
}>;
|
|
296
|
+
/** {@link ChildStepFold} for an entry point whose return carries the child's
|
|
297
|
+
* OutMessage channel, adding `foldOutMessage`. It behaves exactly as it does
|
|
298
|
+
* in {@link ChildFoldWithOutMessage}, down to the optional second parameter,
|
|
299
|
+
* a {@link FoldContext} of lifters bound to `toParentMessage`. */
|
|
300
|
+
export type ChildStepFoldWithOutMessage<ParentModel, ParentMessage, ChildModel, ChildMessage, ChildOutMessage, R = never> = Readonly<{
|
|
301
|
+
update: (childModel: ChildModel) => ReturnWithOutMessage<ChildModel, ChildMessage, ChildOutMessage, R>;
|
|
302
|
+
read: (model: ParentModel) => Option.Option<ChildModel>;
|
|
303
|
+
write: (model: ParentModel, nextChildModel: ChildModel) => ParentModel;
|
|
304
|
+
toParentMessage: (message: ChildMessage) => ParentMessage;
|
|
305
|
+
foldOutMessage: (outMessage: ChildOutMessage, context: FoldContext<ChildMessage, ParentMessage>) => Step<ParentModel, ParentMessage, R>;
|
|
306
|
+
}>;
|
|
307
|
+
/** Folds a child entry point that takes nothing but the child Model, and
|
|
308
|
+
* returns the {@link Step} directly. Everything else matches
|
|
309
|
+
* {@link foldChild}: the child is read, updated, and written back, its
|
|
310
|
+
* Commands are lifted through `toParentMessage`, a `None` from `read` makes
|
|
311
|
+
* the Step a no-op, and `foldOutMessage` runs against the Model with the
|
|
312
|
+
* child already written back.
|
|
313
|
+
*
|
|
314
|
+
* Reach for it wherever a Submodel exposes a no-argument entry point, so the
|
|
315
|
+
* call site composes with {@link combine} as a plain Step and never invents
|
|
316
|
+
* an input the child does not take:
|
|
317
|
+
*
|
|
318
|
+
* ```ts
|
|
319
|
+
* const foldMobileMenuDialogClose = Update.foldChildStep({
|
|
320
|
+
* update: Dialog.close,
|
|
321
|
+
* read: readMobileMenuDialog,
|
|
322
|
+
* write: writeMobileMenuDialog,
|
|
323
|
+
* toParentMessage: toGotMobileMenuDialogMessage,
|
|
324
|
+
* foldOutMessage: foldMobileMenuDialogOutMessage,
|
|
325
|
+
* })
|
|
326
|
+
*
|
|
327
|
+
* // in the parent update
|
|
328
|
+
* Update.combine(model, [writeRouteFields, foldMobileMenuDialogClose])
|
|
329
|
+
* ```
|
|
330
|
+
*
|
|
331
|
+
* `foldOutMessage` takes the same optional second parameter `foldChild`'s
|
|
332
|
+
* does, a {@link FoldContext} carrying `liftCommand` and `liftCommands` bound
|
|
333
|
+
* to this config's `toParentMessage`, for a Command the Step returns whose
|
|
334
|
+
* result is the child's Message.
|
|
335
|
+
*
|
|
336
|
+
* A parent that is itself a Submodel, and so needs its own OutMessage
|
|
337
|
+
* channel on the result, uses {@link foldChild}. */
|
|
338
|
+
export declare const foldChildStep: {
|
|
339
|
+
<ParentModel, ParentMessage, ChildModel, ChildMessage, ChildOutMessage, R = never>(childFold: ChildStepFoldWithOutMessage<ParentModel, ParentMessage, ChildModel, ChildMessage, ChildOutMessage, R>): Step<ParentModel, ParentMessage, R>;
|
|
340
|
+
<ParentModel, ParentMessage, ChildModel, ChildMessage, R = never>(childFold: ChildStepFold<ParentModel, ParentMessage, ChildModel, ChildMessage, R>): Step<ParentModel, ParentMessage, R>;
|
|
341
|
+
};
|
|
232
342
|
//# sourceMappingURL=update.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"update.d.ts","sourceRoot":"","sources":["../../src/update/update.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmB,MAAM,EAAQ,MAAM,QAAQ,CAAA;AAEtD,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,uBAAuB,CAAA;AACtD,OAAO,EAAE,KAAK,OAAO,
|
|
1
|
+
{"version":3,"file":"update.d.ts","sourceRoot":"","sources":["../../src/update/update.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmB,MAAM,EAAQ,MAAM,QAAQ,CAAA;AAEtD,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,uBAAuB,CAAA;AACtD,OAAO,EAAE,KAAK,OAAO,EAA2B,MAAM,qBAAqB,CAAA;AAE3E;;;;;;;;;UASU;AACV,MAAM,MAAM,QAAQ,CAAC,OAAO,EAAE,CAAC,GAAG,KAAK,IAAI,aAAa,CACtD,OAAO,CAAC,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC,CAC3B,CAAA;AAED;;;;;;;;;UASU;AACV,MAAM,MAAM,MAAM,CAAC,KAAK,EAAE,OAAO,EAAE,CAAC,GAAG,KAAK,IAAI,SAAS;IACvD,KAAK;IACL,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC;CACrB,CAAA;AAED;;;;yDAIyD;AACzD,MAAM,MAAM,oBAAoB,CAC9B,KAAK,EACL,OAAO,EACP,UAAU,EACV,CAAC,GAAG,KAAK,IACP,SAAS,CAAC,KAAK,EAAE,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC,EAAE,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAA;AAErE;;mEAEmE;AACnE,MAAM,MAAM,IAAI,CAAC,KAAK,EAAE,OAAO,EAAE,CAAC,GAAG,KAAK,IAAI,CAC5C,KAAK,EAAE,KAAK,KACT,MAAM,CAAC,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC,CAAA;AAE9B;;;;;;;;;;;;;;;;;;;;;;;;UAwBU;AACV,eAAO,MAAM,OAAO,EAAE;IACpB,CAAC,KAAK,EAAE,OAAO,EAAE,CAAC,GAAG,KAAK,EACxB,KAAK,EAAE,aAAa,CAAC,IAAI,CAAC,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,GAC5C,IAAI,CAAC,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC,CAAA;IAC1B,CAAC,KAAK,EAAE,OAAO,EAAE,CAAC,GAAG,KAAK,EACxB,KAAK,EAAE,KAAK,EACZ,KAAK,EAAE,aAAa,CAAC,IAAI,CAAC,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,GAC5C,MAAM,CAAC,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC,CAAA;CAa7B,CAAA;AAED;;;;;;;;;;qDAUqD;AACrD,MAAM,MAAM,WAAW,CAAC,KAAK,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,GAAG,KAAK,IAAI,QAAQ,CAAC;IAClE,IAAI,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAA;IACtD,UAAU,EAAE,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAA;IACxE,KAAK,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,KAAK,CAAA;IACrD,IAAI,EAAE,OAAO,CAAC,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC,CAAA;CACjC,CAAC,CAAA;AAEF;;;;;;;;;;;;;;;UAeU;AACV,eAAO,MAAM,OAAO,GACjB,KAAK,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,GAAG,KAAK,EAC9B,aAAa,WAAW,CAAC,KAAK,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,KAChD,IAAI,CAAC,KAAK,EAAE,OAAO,EAAE,CAAC,CAStB,CAAA;AAEL;;;;;;;;;;;;;4EAa4E;AAC5E,MAAM,MAAM,SAAS,CACnB,WAAW,EACX,aAAa,EACb,UAAU,EACV,KAAK,EACL,YAAY,EACZ,CAAC,GAAG,KAAK,IACP,QAAQ,CAAC;IACX,MAAM,EAAE,CACN,UAAU,EAAE,UAAU,EACtB,KAAK,EAAE,KAAK,KACT,MAAM,CAAC,UAAU,EAAE,YAAY,EAAE,CAAC,CAAC,CAAA;IACxC,IAAI,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAA;IACvD,KAAK,EAAE,CAAC,KAAK,EAAE,WAAW,EAAE,cAAc,EAAE,UAAU,KAAK,WAAW,CAAA;IACtE,eAAe,EAAE,CAAC,OAAO,EAAE,YAAY,KAAK,aAAa,CAAA;CAC1D,CAAC,CAAA;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;UAoCU;AACV,MAAM,MAAM,WAAW,CAAC,YAAY,EAAE,aAAa,IAAI,QAAQ,CAAC;IAC9D,WAAW,EAAE,CAAC,CAAC,GAAG,KAAK,EAAE,CAAC,GAAG,KAAK,EAChC,OAAO,EAAE,OAAO,CAAC,YAAY,EAAE,CAAC,EAAE,CAAC,CAAC,KACjC,OAAO,CAAC,aAAa,EAAE,CAAC,EAAE,CAAC,CAAC,CAAA;IACjC,YAAY,EAAE,CAAC,CAAC,GAAG,KAAK,EAAE,CAAC,GAAG,KAAK,EACjC,QAAQ,EAAE,aAAa,CAAC,OAAO,CAAC,YAAY,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,KACjD,aAAa,CAAC,OAAO,CAAC,aAAa,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAA;CACjD,CAAC,CAAA;AAEF;;;;;;;;;;sEAUsE;AACtE,MAAM,MAAM,uBAAuB,CACjC,WAAW,EACX,aAAa,EACb,UAAU,EACV,KAAK,EACL,YAAY,EACZ,eAAe,EACf,CAAC,GAAG,KAAK,IACP,QAAQ,CAAC;IACX,MAAM,EAAE,CACN,UAAU,EAAE,UAAU,EACtB,KAAK,EAAE,KAAK,KACT,oBAAoB,CAAC,UAAU,EAAE,YAAY,EAAE,eAAe,EAAE,CAAC,CAAC,CAAA;IACvE,IAAI,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAA;IACvD,KAAK,EAAE,CAAC,KAAK,EAAE,WAAW,EAAE,cAAc,EAAE,UAAU,KAAK,WAAW,CAAA;IACtE,eAAe,EAAE,CAAC,OAAO,EAAE,YAAY,KAAK,aAAa,CAAA;IACzD,cAAc,EAAE,CACd,UAAU,EAAE,eAAe,EAC3B,OAAO,EAAE,WAAW,CAAC,YAAY,EAAE,aAAa,CAAC,KAC9C,IAAI,CAAC,WAAW,EAAE,aAAa,EAAE,CAAC,CAAC,CAAA;CACzC,CAAC,CAAA;AAEF;;;;;;;;;;;;2DAY2D;AAC3D,MAAM,MAAM,6BAA6B,CACvC,WAAW,EACX,aAAa,EACb,UAAU,EACV,KAAK,EACL,YAAY,EACZ,eAAe,EACf,gBAAgB,EAChB,CAAC,GAAG,KAAK,IACP,QAAQ,CAAC;IACX,MAAM,EAAE,CACN,UAAU,EAAE,UAAU,EACtB,KAAK,EAAE,KAAK,KACT,oBAAoB,CAAC,UAAU,EAAE,YAAY,EAAE,eAAe,EAAE,CAAC,CAAC,CAAA;IACvE,IAAI,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAA;IACvD,KAAK,EAAE,CAAC,KAAK,EAAE,WAAW,EAAE,cAAc,EAAE,UAAU,KAAK,WAAW,CAAA;IACtE,eAAe,EAAE,CAAC,OAAO,EAAE,YAAY,KAAK,aAAa,CAAA;IACzD,kBAAkB,EAAE,CAClB,UAAU,EAAE,eAAe,KACxB,MAAM,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAA;IACpC,cAAc,CAAC,EAAE,CACf,UAAU,EAAE,eAAe,EAC3B,OAAO,EAAE,WAAW,CAAC,YAAY,EAAE,aAAa,CAAC,KAC9C,IAAI,CAAC,WAAW,EAAE,aAAa,EAAE,CAAC,CAAC,CAAA;CACzC,CAAC,CAAA;AAsBF;;aAEa;AACb,MAAM,MAAM,kBAAkB,CAAC,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE,CAAC,GAAG,KAAK,IAAI,CACtE,KAAK,EAAE,KAAK,KACT,oBAAoB,CAAC,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE,CAAC,CAAC,CAAA;AAExD;;;wBAGwB;AACxB,MAAM,MAAM,IAAI,CAAC,WAAW,EAAE,aAAa,EAAE,KAAK,EAAE,CAAC,GAAG,KAAK,IAAI;IAC/D,CAAC,KAAK,EAAE,WAAW,EAAE,KAAK,EAAE,KAAK,GAAG,MAAM,CAAC,WAAW,EAAE,aAAa,EAAE,CAAC,CAAC,CAAA;IACzE,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC,WAAW,EAAE,aAAa,EAAE,CAAC,CAAC,CAAA;CACpD,CAAA;AAED;;;wDAGwD;AACxD,MAAM,MAAM,kBAAkB,CAC5B,WAAW,EACX,aAAa,EACb,KAAK,EACL,gBAAgB,EAChB,CAAC,GAAG,KAAK,IACP;IACF,CACE,KAAK,EAAE,WAAW,EAClB,KAAK,EAAE,KAAK,GACX,oBAAoB,CAAC,WAAW,EAAE,aAAa,EAAE,gBAAgB,EAAE,CAAC,CAAC,CAAA;IACxE,CACE,KAAK,EAAE,KAAK,GACX,kBAAkB,CAAC,WAAW,EAAE,aAAa,EAAE,gBAAgB,EAAE,CAAC,CAAC,CAAA;CACvE,CAAA;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;UAyDU;AACV,eAAO,MAAM,SAAS,EAAE;IACtB,CACE,WAAW,EACX,aAAa,EACb,UAAU,EACV,KAAK,EACL,YAAY,EACZ,eAAe,EACf,gBAAgB,EAChB,CAAC,GAAG,KAAK,EAET,SAAS,EAAE,6BAA6B,CACtC,WAAW,EACX,aAAa,EACb,UAAU,EACV,KAAK,EACL,YAAY,EACZ,eAAe,EACf,gBAAgB,EAChB,CAAC,CACF,GACA,kBAAkB,CAAC,WAAW,EAAE,aAAa,EAAE,KAAK,EAAE,gBAAgB,EAAE,CAAC,CAAC,CAAA;IAC7E,CACE,WAAW,EACX,aAAa,EACb,UAAU,EACV,KAAK,EACL,YAAY,EACZ,eAAe,EACf,CAAC,GAAG,KAAK,EAET,SAAS,EAAE,uBAAuB,CAChC,WAAW,EACX,aAAa,EACb,UAAU,EACV,KAAK,EACL,YAAY,EACZ,eAAe,EACf,CAAC,CACF,GACA,IAAI,CAAC,WAAW,EAAE,aAAa,EAAE,KAAK,EAAE,CAAC,CAAC,CAAA;IAC7C,CAAC,WAAW,EAAE,aAAa,EAAE,UAAU,EAAE,KAAK,EAAE,YAAY,EAAE,CAAC,GAAG,KAAK,EACrE,SAAS,EAAE,SAAS,CAClB,WAAW,EACX,aAAa,EACb,UAAU,EACV,KAAK,EACL,YAAY,EACZ,CAAC,CACF,GACA,IAAI,CAAC,WAAW,EAAE,aAAa,EAAE,KAAK,EAAE,CAAC,CAAC,CAAA;CAO9C,CAAA;AA0DD;;;;oBAIoB;AACpB,MAAM,MAAM,aAAa,CACvB,WAAW,EACX,aAAa,EACb,UAAU,EACV,YAAY,EACZ,CAAC,GAAG,KAAK,IACP,QAAQ,CAAC;IACX,MAAM,EAAE,CAAC,UAAU,EAAE,UAAU,KAAK,MAAM,CAAC,UAAU,EAAE,YAAY,EAAE,CAAC,CAAC,CAAA;IACvE,IAAI,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAA;IACvD,KAAK,EAAE,CAAC,KAAK,EAAE,WAAW,EAAE,cAAc,EAAE,UAAU,KAAK,WAAW,CAAA;IACtE,eAAe,EAAE,CAAC,OAAO,EAAE,YAAY,KAAK,aAAa,CAAA;CAC1D,CAAC,CAAA;AAEF;;;mEAGmE;AACnE,MAAM,MAAM,2BAA2B,CACrC,WAAW,EACX,aAAa,EACb,UAAU,EACV,YAAY,EACZ,eAAe,EACf,CAAC,GAAG,KAAK,IACP,QAAQ,CAAC;IACX,MAAM,EAAE,CACN,UAAU,EAAE,UAAU,KACnB,oBAAoB,CAAC,UAAU,EAAE,YAAY,EAAE,eAAe,EAAE,CAAC,CAAC,CAAA;IACvE,IAAI,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAA;IACvD,KAAK,EAAE,CAAC,KAAK,EAAE,WAAW,EAAE,cAAc,EAAE,UAAU,KAAK,WAAW,CAAA;IACtE,eAAe,EAAE,CAAC,OAAO,EAAE,YAAY,KAAK,aAAa,CAAA;IACzD,cAAc,EAAE,CACd,UAAU,EAAE,eAAe,EAC3B,OAAO,EAAE,WAAW,CAAC,YAAY,EAAE,aAAa,CAAC,KAC9C,IAAI,CAAC,WAAW,EAAE,aAAa,EAAE,CAAC,CAAC,CAAA;CACzC,CAAC,CAAA;AAkBF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;qDA8BqD;AACrD,eAAO,MAAM,aAAa,EAAE;IAC1B,CACE,WAAW,EACX,aAAa,EACb,UAAU,EACV,YAAY,EACZ,eAAe,EACf,CAAC,GAAG,KAAK,EAET,SAAS,EAAE,2BAA2B,CACpC,WAAW,EACX,aAAa,EACb,UAAU,EACV,YAAY,EACZ,eAAe,EACf,CAAC,CACF,GACA,IAAI,CAAC,WAAW,EAAE,aAAa,EAAE,CAAC,CAAC,CAAA;IACtC,CAAC,WAAW,EAAE,aAAa,EAAE,UAAU,EAAE,YAAY,EAAE,CAAC,GAAG,KAAK,EAC9D,SAAS,EAAE,aAAa,CACtB,WAAW,EACX,aAAa,EACb,UAAU,EACV,YAAY,EACZ,CAAC,CACF,GACA,IAAI,CAAC,WAAW,EAAE,aAAa,EAAE,CAAC,CAAC,CAAA;CAKvC,CAAA"}
|
package/dist/update/update.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Array, Function, Option, pipe } from 'effect';
|
|
2
|
-
import { mapMessages } from '../command/index.js';
|
|
2
|
+
import { mapMessage, mapMessages } from '../command/index.js';
|
|
3
3
|
/** Composes a list of update steps into one. Each step runs against the
|
|
4
4
|
* Model the previous step produced, and every step's Commands are
|
|
5
5
|
* concatenated into a single batch, in step order.
|
|
@@ -78,11 +78,21 @@ export const refresh = (refreshable) => model => pipe(refreshable.read(model), O
|
|
|
78
78
|
* the Model with the child already written back, and its Commands
|
|
79
79
|
* follow the child's in the returned batch.
|
|
80
80
|
*
|
|
81
|
+
* `foldOutMessage` takes an optional second parameter, a
|
|
82
|
+
* {@link FoldContext} carrying `liftCommand` and `liftCommands` bound to
|
|
83
|
+
* this config's `toParentMessage`. Reach for it when the Step returns a
|
|
84
|
+
* Command that produces the child's Message, such as an animating
|
|
85
|
+
* component's overridable leave Command.
|
|
86
|
+
*
|
|
81
87
|
* A parent that is itself a Submodel passes a
|
|
82
88
|
* {@link ChildFoldWithParentOutMessage} and receives a
|
|
83
89
|
* {@link FoldWithOutMessage}, whose results carry the parent's own
|
|
84
90
|
* OutMessage channel as a third element.
|
|
85
91
|
*
|
|
92
|
+
* An entry point that takes nothing but the child Model, such as
|
|
93
|
+
* `Dialog.close`, has no input to pass: fold it with
|
|
94
|
+
* {@link foldChildStep}, which returns the {@link Step} directly.
|
|
95
|
+
*
|
|
86
96
|
* `update` closes over per-dispatch context, and the data-last form
|
|
87
97
|
* composes with {@link combine}, here to put a navigation Command ahead
|
|
88
98
|
* of the child's:
|
|
@@ -100,7 +110,15 @@ export const refresh = (refreshable) => model => pipe(refreshable.read(model), O
|
|
|
100
110
|
* })(player),
|
|
101
111
|
* ])
|
|
102
112
|
* ``` */
|
|
103
|
-
export const foldChild = (childFold) =>
|
|
113
|
+
export const foldChild = (childFold) => {
|
|
114
|
+
const context = makeFoldContext(childFold.toParentMessage);
|
|
115
|
+
return Function.dual(2, (model, input) => runChildFold(childFold, context, model, input));
|
|
116
|
+
};
|
|
117
|
+
const makeFoldContext = (toParentMessage) => ({
|
|
118
|
+
liftCommand: command => mapMessage(command, toParentMessage),
|
|
119
|
+
liftCommands: commands => mapMessages(commands, toParentMessage),
|
|
120
|
+
});
|
|
121
|
+
const runChildFold = (childFold, context, model, input) => pipe(model, childFold.read, Option.match({
|
|
104
122
|
onNone: () => childFold.toParentOutMessage === undefined
|
|
105
123
|
? [model, []]
|
|
106
124
|
: [model, [], Option.none()],
|
|
@@ -112,7 +130,7 @@ export const foldChild = (childFold) => Function.dual(2, (model, input) => pipe(
|
|
|
112
130
|
maybeOutMessage === undefined ||
|
|
113
131
|
Option.isNone(maybeOutMessage)
|
|
114
132
|
? [modelWithChild, mappedCommands]
|
|
115
|
-
: appendOutMessageStep(childFold.foldOutMessage, maybeOutMessage.value, modelWithChild, mappedCommands);
|
|
133
|
+
: appendOutMessageStep(childFold.foldOutMessage, maybeOutMessage.value, context, modelWithChild, mappedCommands);
|
|
116
134
|
if (childFold.toParentOutMessage === undefined) {
|
|
117
135
|
return [nextModel, commands];
|
|
118
136
|
}
|
|
@@ -121,8 +139,43 @@ export const foldChild = (childFold) => Function.dual(2, (model, input) => pipe(
|
|
|
121
139
|
: Option.flatMap(maybeOutMessage, childFold.toParentOutMessage);
|
|
122
140
|
return [nextModel, commands, maybeParentOutMessage];
|
|
123
141
|
},
|
|
124
|
-
}))
|
|
125
|
-
|
|
126
|
-
|
|
142
|
+
}));
|
|
143
|
+
/** Folds a child entry point that takes nothing but the child Model, and
|
|
144
|
+
* returns the {@link Step} directly. Everything else matches
|
|
145
|
+
* {@link foldChild}: the child is read, updated, and written back, its
|
|
146
|
+
* Commands are lifted through `toParentMessage`, a `None` from `read` makes
|
|
147
|
+
* the Step a no-op, and `foldOutMessage` runs against the Model with the
|
|
148
|
+
* child already written back.
|
|
149
|
+
*
|
|
150
|
+
* Reach for it wherever a Submodel exposes a no-argument entry point, so the
|
|
151
|
+
* call site composes with {@link combine} as a plain Step and never invents
|
|
152
|
+
* an input the child does not take:
|
|
153
|
+
*
|
|
154
|
+
* ```ts
|
|
155
|
+
* const foldMobileMenuDialogClose = Update.foldChildStep({
|
|
156
|
+
* update: Dialog.close,
|
|
157
|
+
* read: readMobileMenuDialog,
|
|
158
|
+
* write: writeMobileMenuDialog,
|
|
159
|
+
* toParentMessage: toGotMobileMenuDialogMessage,
|
|
160
|
+
* foldOutMessage: foldMobileMenuDialogOutMessage,
|
|
161
|
+
* })
|
|
162
|
+
*
|
|
163
|
+
* // in the parent update
|
|
164
|
+
* Update.combine(model, [writeRouteFields, foldMobileMenuDialogClose])
|
|
165
|
+
* ```
|
|
166
|
+
*
|
|
167
|
+
* `foldOutMessage` takes the same optional second parameter `foldChild`'s
|
|
168
|
+
* does, a {@link FoldContext} carrying `liftCommand` and `liftCommands` bound
|
|
169
|
+
* to this config's `toParentMessage`, for a Command the Step returns whose
|
|
170
|
+
* result is the child's Message.
|
|
171
|
+
*
|
|
172
|
+
* A parent that is itself a Submodel, and so needs its own OutMessage
|
|
173
|
+
* channel on the result, uses {@link foldChild}. */
|
|
174
|
+
export const foldChildStep = (childFold) => {
|
|
175
|
+
const context = makeFoldContext(childFold.toParentMessage);
|
|
176
|
+
return model => runChildFold(childFold, context, model, undefined);
|
|
177
|
+
};
|
|
178
|
+
const appendOutMessageStep = (foldOutMessage, outMessage, context, modelWithChild, mappedCommands) => {
|
|
179
|
+
const [nextModel, outCommands] = foldOutMessage(outMessage, context)(modelWithChild);
|
|
127
180
|
return [nextModel, [...mappedCommands, ...outCommands]];
|
|
128
181
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "foldkit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.142.0",
|
|
4
4
|
"description": "A TypeScript frontend framework, built on Effect and architected like Elm",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -154,14 +154,14 @@
|
|
|
154
154
|
"THIRD-PARTY-NOTICES.md"
|
|
155
155
|
],
|
|
156
156
|
"peerDependencies": {
|
|
157
|
-
"@effect/platform-browser": "4.0.0-beta.
|
|
158
|
-
"effect": "4.0.0-beta.
|
|
157
|
+
"@effect/platform-browser": "4.0.0-beta.106",
|
|
158
|
+
"effect": "4.0.0-beta.106"
|
|
159
159
|
},
|
|
160
160
|
"devDependencies": {
|
|
161
|
-
"@effect/platform-browser": "4.0.0-beta.
|
|
162
|
-
"@effect/vitest": "4.0.0-beta.
|
|
161
|
+
"@effect/platform-browser": "4.0.0-beta.106",
|
|
162
|
+
"@effect/vitest": "4.0.0-beta.106",
|
|
163
163
|
"@vitest/coverage-v8": "^4.1.9",
|
|
164
|
-
"effect": "4.0.0-beta.
|
|
164
|
+
"effect": "4.0.0-beta.106",
|
|
165
165
|
"happy-dom": "^20.10.4",
|
|
166
166
|
"rimraf": "^6.1.3",
|
|
167
167
|
"typedoc": "^0.28.19",
|