foldkit 0.141.2 → 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.
@@ -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) => <ParentModel, ParentMessage>(config: {
144
- readonly toChildModel: (parentModel: ParentModel) => ChildModelOf<Subscriptions>;
145
- readonly toParentMessage: (message: ChildMessageOf<Subscriptions>) => ParentMessage;
146
- }) => { readonly [K in keyof Subscriptions]: Subscriptions[K] extends Subscription<any, any, infer Dependencies, infer Services> ? Subscription<ParentModel, ParentMessage, Dependencies, Services> : never; };
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":"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"}
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
- * Lifts a record of child Subscriptions into a parent's Model and Message
68
- * context, applying a Model accessor and a Message wrapper uniformly to
69
- * every entry. Per-entry dependency types, schemas, and `keepAliveEquivalence`
70
- * settings are preserved; each lifted entry's variant (with or without
71
- * `readDependencies`) matches its source entry's.
72
- */
73
- export const lift = (subscriptions) => (config) =>
74
- /* eslint-disable @typescript-eslint/consistent-type-assertions */
75
- Record.map(subscriptions, subscription => {
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) => wrapStream(subscription.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) => wrapStream(subscription.dependenciesToStream(dependencies)),
91
+ dependenciesToStream: (dependencies) => toParentStream(config, subscription.dependenciesToStream(dependencies)),
90
92
  };
91
- });
92
- /* eslint-enable @typescript-eslint/consistent-type-assertions */
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
+ }));
@@ -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;AAExD,YAAY,EACV,QAAQ,EACR,MAAM,EACN,oBAAoB,EACpB,IAAI,EACJ,kBAAkB,EAClB,WAAW,EACX,SAAS,EACT,uBAAuB,EACvB,6BAA6B,EAC7B,IAAI,EACJ,kBAAkB,GACnB,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"}
@@ -1 +1 @@
1
- export { combine, foldChild, refresh } from './index.js';
1
+ export { combine, foldChild, foldChildStep, refresh } from './index.js';
@@ -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":"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"}
1
+ {"version":3,"file":"update.d.ts","sourceRoot":"","sources":["../../src/update/update.ts"],"names":[],"mappings":"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"}
@@ -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) => Function.dual(2, (model, input) => pipe(model, childFold.read, Option.match({
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
- const appendOutMessageStep = (foldOutMessage, outMessage, modelWithChild, mappedCommands) => {
126
- const [nextModel, outCommands] = foldOutMessage(outMessage)(modelWithChild);
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.141.2",
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",