@systemfsoftware/effect-cell-types 7.0.1 → 8.1.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/CHANGELOG.md +39 -0
- package/dist/index.d.ts +69 -27
- package/dist/index.mjs +67 -22
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,44 @@
|
|
|
1
1
|
# @systemfsoftware/effect-cell-types
|
|
2
2
|
|
|
3
|
+
## 8.1.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- `collectAll` now declares an error channel of `never`: the composed cell cannot fail, because every per-item refusal is delivered to the fold as a `Result`. Composed cells no longer carry the item error channel in their type.
|
|
8
|
+
|
|
9
|
+
`Workflow.andThen` now composes two total workflows (components whose error channel is `never`) into a total workflow. Error-carrying chains are unchanged.
|
|
10
|
+
|
|
11
|
+
`Workflow.make` and `Workflow.total` now refuse decision unions whose variants declare the family brand as a narrow unique-symbol type instead of the documented class-field idiom. Declare the brand as a `readonly [T] = T` field on each `S.TaggedClass` variant: the field initializer widens the slot to the general `symbol` type, and the predicate keys on that widened slot. A `typeof`-annotated slot stays narrow and is refused with `UnsharedTypeId` — including the idiom hand-written as an interface. An interface annotating the slot as plain `symbol` is structurally identical to the class idiom and is accepted.
|
|
12
|
+
|
|
13
|
+
## 8.0.0
|
|
14
|
+
|
|
15
|
+
### Major Changes
|
|
16
|
+
|
|
17
|
+
- The exported `Cell.run` helper is removed. Run a cell through its own arrow instead: `cell.run(input)` replaces `Cell.run(cell, input)`, in both the data-first and the curried form. The helper only forwarded to that arrow, so a migrated call site behaves identically.
|
|
18
|
+
|
|
19
|
+
The `Policy` type and `Cell.withPolicy` are removed. An execution strategy — retry, timeout, a restart cap — belongs to the interpreter: it is the only place that knows the write's idempotence and the deadline, and a strategy declared on the published value imposed re-execution of side effects on every consumer. Wrap the run at the edge with Effect's own combinators instead: `Effect.retry(cell.run(input), { times })`.
|
|
20
|
+
|
|
21
|
+
### Minor Changes
|
|
22
|
+
|
|
23
|
+
- `Cell.gate` runs one Cell only when another's response carries a value: the value admits it to the inner Cell and the composed response wraps the inner response, carrying nothing skips the inner Cell entirely, and both Cells' error and service channels union.
|
|
24
|
+
|
|
25
|
+
`Cell.collect` runs one Cell per item, in order, then folds the responses with a plain function. The first refusal fails the composed Cell with that item's own refusal. `Cell.collectAll` is the accumulate opt-in: an item's refusal travels to the fold as data, so the fold always runs over every result.
|
|
26
|
+
|
|
27
|
+
Both combinators are callable in the curried and data-first styles, and neither requires a workflow brand at its call site.
|
|
28
|
+
|
|
29
|
+
- Two constructors join `Workflow.make`.
|
|
30
|
+
|
|
31
|
+
`Workflow.total` brands a decision that cannot fail. A decider whose error channel is `never` no
|
|
32
|
+
longer becomes a workflow that cannot be called; it carries the workflow brand and is accepted
|
|
33
|
+
wherever a branded decision is, including `Cell.layer`'s `decide` slot. Like `make`, it takes the
|
|
34
|
+
command schema class first, and it still refuses a decision with a single variant or a variant
|
|
35
|
+
carrying no `_tag` to dispatch on.
|
|
36
|
+
|
|
37
|
+
`Workflow.andThen` chains two workflows. The first workflow's decision becomes the second workflow's
|
|
38
|
+
command, the composed workflow's error channel is the union of both components, and a refusal from
|
|
39
|
+
the first short-circuits — the second never runs. The second workflow's command class is passed as a
|
|
40
|
+
value, so the composed signature carries no adapter function.
|
|
41
|
+
|
|
3
42
|
## 7.0.1
|
|
4
43
|
|
|
5
44
|
### Patch Changes
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import * as Effect
|
|
2
|
-
import
|
|
1
|
+
import * as Effect from "effect/Effect";
|
|
2
|
+
import * as Option from "effect/Option";
|
|
3
3
|
import * as Result$1 from "effect/Result";
|
|
4
4
|
import { Result } from "effect/Result";
|
|
5
5
|
import { Kind, TypeLambda } from "effect/HKT";
|
|
@@ -13,12 +13,8 @@ declare const IO_CELLS: {
|
|
|
13
13
|
};
|
|
14
14
|
type IoCellClassification = typeof IO_CELLS;
|
|
15
15
|
type PhaseName = 'read' | 'decode' | 'decide' | 'encode' | 'write';
|
|
16
|
-
declare namespace Policy_d_exports {
|
|
17
|
-
export { Policy };
|
|
18
|
-
}
|
|
19
|
-
type Policy<A, E, R> = (self: Effect<A, E, R>) => Effect<A, E, R>;
|
|
20
16
|
declare namespace Workflow_d_exports {
|
|
21
|
-
export { Inhabited, SingleVariantDecision, UninhabitedDecision, UninhabitedError, UnsharedTypeId, UntaggedDecision, UntaggedError, Workflow, WorkflowBrand, make };
|
|
17
|
+
export { Inhabited, SingleVariantDecision, UninhabitedDecision, UninhabitedError, UnsharedTypeId, UntaggedDecision, UntaggedError, Workflow, WorkflowBrand, andThen$1 as andThen, make, total };
|
|
22
18
|
}
|
|
23
19
|
declare const WorkflowTypeId: unique symbol;
|
|
24
20
|
type WorkflowTypeId = typeof WorkflowTypeId;
|
|
@@ -43,10 +39,11 @@ interface UntaggedDecision {
|
|
|
43
39
|
interface UnsharedTypeId {
|
|
44
40
|
readonly __WORKFLOW_DECISION_VARIANTS_DO_NOT_SHARE_A_TYPE_ID__: 'the decision variants must share one TypeId — a Symbol.for family brand on each variant class';
|
|
45
41
|
}
|
|
46
|
-
type UnionToIntersection<U> = (U extends unknown ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never;
|
|
47
42
|
type AtLeastTwoDistinct<T, U = T> = U extends unknown ? [T] extends [U] ? false : true : never;
|
|
48
43
|
type TaggedMembers<D> = D extends unknown ? '_tag' extends keyof D ? [D['_tag']] extends [string] ? true : false : false : never;
|
|
49
|
-
type
|
|
44
|
+
type MutuallyAssignable<A, B> = [A] extends [B] ? ([B] extends [A] ? true : false) : false;
|
|
45
|
+
type BrandSlotIsTheGeneralSymbol<D, K extends PropertyKey> = D extends unknown ? K extends keyof D ? MutuallyAssignable<D[K], symbol> : false : never;
|
|
46
|
+
type SharedTypeId<D> = [{ [K in keyof D]: [K] extends [symbol] ? ([BrandSlotIsTheGeneralSymbol<D, K>] extends [true] ? K : never) : never; }[keyof D]] extends [never] ? UnsharedTypeId : unknown;
|
|
50
47
|
type DecisionShape<D> = [unknown] extends [D] ? unknown : AtLeastTwoDistinct<D> extends false ? SingleVariantDecision : boolean extends TaggedMembers<D> ? UntaggedDecision : SharedTypeId<D>;
|
|
51
48
|
type Workflow<Command, Decision, DecisionError> = [Decision] extends [never] ? UninhabitedDecision : [DecisionError] extends [never] ? UninhabitedError : ((command: Command) => Result<Decision, DecisionError>) & WorkflowBrand;
|
|
52
49
|
type DispatchableTag<E> = '_tag' extends keyof E ? [E['_tag']] extends [string] ? unknown : UntaggedError : UntaggedError;
|
|
@@ -54,8 +51,40 @@ type Inhabited<Decision, DecisionError> = [Decision] extends [never] ? Uninhabit
|
|
|
54
51
|
declare const make: <Self, S extends Schema.Constraint & {
|
|
55
52
|
readonly fields: Schema.Struct.Fields;
|
|
56
53
|
}, Inherited, D, E>(_command: Schema.Class<Self, S, Inherited>, decide: (command: Self) => Result<D, E> & Inhabited<D, E>) => Workflow<Self, D, E>;
|
|
54
|
+
/**
|
|
55
|
+
* Brands a decision that cannot fail. `make` refuses a `never` error channel outright
|
|
56
|
+
* (`UninhabitedError`); this is the door for the decider that genuinely decides everything.
|
|
57
|
+
* The decision must still choose between at least two tagged variants sharing one TypeId, so
|
|
58
|
+
* `SingleVariantDecision`, `UntaggedDecision`, and `UnsharedTypeId` still fire.
|
|
59
|
+
*
|
|
60
|
+
* The command schema class comes first, exactly as in {@link make}, so the command channel
|
|
61
|
+
* stays pinned to the class rather than an inferred annotation. The decider's return carries
|
|
62
|
+
* `DecisionShape` written out: the `Workflow` alias is a deferred conditional, and in
|
|
63
|
+
* parameter position it collapses the whole parameter to `unknown` while the decision
|
|
64
|
+
* channel is still generic.
|
|
65
|
+
*/
|
|
66
|
+
declare const total: <Self, S extends Schema.Constraint & {
|
|
67
|
+
readonly fields: Schema.Struct.Fields;
|
|
68
|
+
}, Inherited, D>(_command: Schema.Class<Self, S, Inherited>, decide: (command: Self) => Result<D, never> & DecisionShape<D>) => ((command: Self) => Result<D, never>) & WorkflowBrand;
|
|
69
|
+
/**
|
|
70
|
+
* Composes two workflows: what the upstream decides becomes the command the downstream decides
|
|
71
|
+
* on, and a refusal short-circuits the pair. The return type dispatches on the component error
|
|
72
|
+
* union — two components that cannot fail publish the total form, because the `Workflow` alias
|
|
73
|
+
* refuses a `never` channel; a carried error publishes the union as before.
|
|
74
|
+
*/
|
|
75
|
+
declare const andThen$1: <Ctx, SelfA, SA extends Schema.Constraint & {
|
|
76
|
+
readonly fields: Schema.Struct.Fields;
|
|
77
|
+
}, InheritedA, D1, E1, SelfB extends {
|
|
78
|
+
readonly decision: D1;
|
|
79
|
+
readonly ctx: Ctx;
|
|
80
|
+
}, D2, E2>(_commandA: Schema.Class<SelfA, SA, InheritedA>, upstream: ((command: SelfA) => Result<D1, E1>) & WorkflowBrand, commandB: {
|
|
81
|
+
new (props: {
|
|
82
|
+
readonly decision: D1;
|
|
83
|
+
readonly ctx: Ctx;
|
|
84
|
+
}): SelfB;
|
|
85
|
+
}, ctx: NoInfer<Ctx>, downstream: ((command: SelfB) => Result<D2, E2>) & WorkflowBrand) => [E1 | E2] extends [never] ? ((command: SelfA) => Result<D2, never>) & WorkflowBrand : Workflow<SelfA, D2, E1 | E2>;
|
|
57
86
|
declare namespace Cell_d_exports {
|
|
58
|
-
export { Cell, CellTypeId, DESCRIPTION_MODULE, IO_CELLS, IoCellClassification, Kind$1 as Kind, PhaseName, Run, TypeLambda$1 as TypeLambda, Vocabulary, andThen, layer, map, mapInput, provide,
|
|
87
|
+
export { Cell, CellTypeId, DESCRIPTION_MODULE, IO_CELLS, IoCellClassification, Kind$1 as Kind, PhaseName, Run, TypeLambda$1 as TypeLambda, Vocabulary, andThen, collect, collectAll, gate, layer, map, mapInput, provide, vocabulary, zip };
|
|
59
88
|
}
|
|
60
89
|
/**
|
|
61
90
|
* The nominal brand every `Cell` carries. `Cell.layer` is the only door that applies it.
|
|
@@ -73,7 +102,7 @@ type CellTypeId = typeof CellTypeId;
|
|
|
73
102
|
*/
|
|
74
103
|
interface Cell<in I, out A, out E = never, out R = never> {
|
|
75
104
|
readonly [CellTypeId]: CellTypeId;
|
|
76
|
-
readonly run: (input: I) => Effect
|
|
105
|
+
readonly run: (input: I) => Effect.Effect<A, E, R>;
|
|
77
106
|
}
|
|
78
107
|
/**
|
|
79
108
|
* The type lambda for {@link Cell}, admitting Cell to `Kind` positions.
|
|
@@ -92,9 +121,9 @@ type Kind$1<I, E, R, A> = Kind<TypeLambda$1, I, E, R, A>;
|
|
|
92
121
|
*/
|
|
93
122
|
type Run<I, A, E, R> = Cell<I, A, E, R>['run'];
|
|
94
123
|
interface LayerCore<I, Raw, RE, RR, Dec, DE, Resp, WE, WR> {
|
|
95
|
-
readonly read: (command: I) => Effect
|
|
124
|
+
readonly read: (command: I) => Effect.Effect<Raw, RE, RR>;
|
|
96
125
|
readonly decide: ((decoded: Raw) => Result$1.Result<Dec, DE>) & WorkflowBrand;
|
|
97
|
-
readonly write: (output: Result$1.Result<Dec, DE>, raw: Raw) => Effect
|
|
126
|
+
readonly write: (output: Result$1.Result<Dec, DE>, raw: Raw) => Effect.Effect<Resp, WE, WR>;
|
|
98
127
|
}
|
|
99
128
|
interface LayerShortSpec<I, Raw, RE, RR, Dec, DE, Resp, WE, WR> extends LayerCore<I, Raw, RE, RR, Dec, DE, Resp, WE, WR> {
|
|
100
129
|
readonly decode?: never;
|
|
@@ -104,7 +133,7 @@ interface LayerLongSpec<I, Raw, RE, RR, Dcd, DecE, Dec, DE, Out, Resp, WE, WR> e
|
|
|
104
133
|
readonly decode: (raw: Raw) => Result$1.Result<Dcd, DecE>;
|
|
105
134
|
readonly decide: ((decoded: Dcd) => Result$1.Result<Dec, DE>) & WorkflowBrand;
|
|
106
135
|
readonly encode: (outcome: Result$1.Result<Dec, DE>) => Out;
|
|
107
|
-
readonly write: (output: Out, raw: Raw) => Effect
|
|
136
|
+
readonly write: (output: Out, raw: Raw) => Effect.Effect<Resp, WE, WR>;
|
|
108
137
|
}
|
|
109
138
|
/**
|
|
110
139
|
* Builds a Cell from one sandwich.
|
|
@@ -133,10 +162,6 @@ interface LayerLongSpec<I, Raw, RE, RR, Dcd, DecE, Dec, DE, Out, Resp, WE, WR> e
|
|
|
133
162
|
*/
|
|
134
163
|
declare function layer<I, Raw, RE, RR, Dec, DE, Resp, WE, WR>(spec: LayerShortSpec<I, Raw, RE, RR, Dec, DE, Resp, WE, WR>): Cell<I, Resp, RE | WE, RR | WR>;
|
|
135
164
|
declare function layer<I, Raw, RE, RR, Dcd, DecE, Dec, DE, Out, Resp, WE, WR>(spec: LayerLongSpec<I, Raw, RE, RR, Dcd, DecE, Dec, DE, Out, Resp, WE, WR>): Cell<I, Resp, RE | DecE | WE, RR | WR>;
|
|
136
|
-
declare const run: {
|
|
137
|
-
<I>(input: I): <A, E, R>(self: Cell<I, A, E, R>) => Effect$1.Effect<A, E, R>;
|
|
138
|
-
<I, A, E, R>(self: Cell<I, A, E, R>, input: I): Effect$1.Effect<A, E, R>;
|
|
139
|
-
};
|
|
140
165
|
/**
|
|
141
166
|
* Transforms the Cell's response.
|
|
142
167
|
*/
|
|
@@ -167,6 +192,31 @@ declare const zip: {
|
|
|
167
192
|
<I, B, E2, R2>(that: Cell<I, B, E2, R2>): <A, E, R>(self: Cell<I, A, E, R>) => Cell<I, readonly [A, B], E | E2, R | R2>;
|
|
168
193
|
<I, A, E, R, B, E2, R2>(self: Cell<I, A, E, R>, that: Cell<I, B, E2, R2>): Cell<I, readonly [A, B], E | E2, R | R2>;
|
|
169
194
|
};
|
|
195
|
+
/**
|
|
196
|
+
* Runs the inner Cell on the value this Cell read, yielding `Option.none` when it read none.
|
|
197
|
+
* A skip is an absence, never a refusal; the error and service channels union.
|
|
198
|
+
*/
|
|
199
|
+
declare const gate: {
|
|
200
|
+
<Raw, A, E2, R2>(inner: Cell<Raw, A, E2, R2>): <I, E, R>(self: Cell<I, Option.Option<Raw>, E, R>) => Cell<I, Option.Option<A>, E | E2, R | R2>;
|
|
201
|
+
<I, Raw, E, R, A, E2, R2>(self: Cell<I, Option.Option<Raw>, E, R>, inner: Cell<Raw, A, E2, R2>): Cell<I, Option.Option<A>, E | E2, R | R2>;
|
|
202
|
+
};
|
|
203
|
+
/**
|
|
204
|
+
* Runs the Cell once per item, in order, and folds the responses into one value. The error
|
|
205
|
+
* and service channels are unchanged; the first refusal ends the run.
|
|
206
|
+
*/
|
|
207
|
+
declare const collect: {
|
|
208
|
+
<I, A, E, R, B>(fold: (responses: readonly A[]) => B): (self: Cell<I, A, E, R>) => Cell<readonly I[], B, E, R>;
|
|
209
|
+
<I, A, E, R, B>(self: Cell<I, A, E, R>, fold: (responses: readonly A[]) => B): Cell<readonly I[], B, E, R>;
|
|
210
|
+
};
|
|
211
|
+
/**
|
|
212
|
+
* Runs the Cell once per item, in order, and folds every outcome — each a `Result.Result` —
|
|
213
|
+
* into one value. Unlike {@link collect}, no refusal ends the run: every item is attempted
|
|
214
|
+
* and its failure travels to the fold.
|
|
215
|
+
*/
|
|
216
|
+
declare const collectAll: {
|
|
217
|
+
<I, A, E, R, B>(fold: (results: readonly Result$1.Result<A, E>[]) => B): (self: Cell<I, A, E, R>) => Cell<readonly I[], B, never, R>;
|
|
218
|
+
<I, A, E, R, B>(self: Cell<I, A, E, R>, fold: (results: readonly Result$1.Result<A, E>[]) => B): Cell<readonly I[], B, never, R>;
|
|
219
|
+
};
|
|
170
220
|
/**
|
|
171
221
|
* Provides a Layer to the Cell, eliminating the services the layer builds from `R`. This is
|
|
172
222
|
* the one composition-root elimination; the resulting Cell still demands the layer's input
|
|
@@ -176,14 +226,6 @@ declare const provide: {
|
|
|
176
226
|
<RIn, LE, ROut>(layer: Layer<ROut, LE, RIn>): <I, A, E, R>(self: Cell<I, A, E, R>) => Cell<I, A, E | LE, RIn | Exclude<R, ROut>>;
|
|
177
227
|
<I, A, E, R, RIn, LE, ROut>(self: Cell<I, A, E, R>, layer: Layer<ROut, LE, RIn>): Cell<I, A, E | LE, RIn | Exclude<R, ROut>>;
|
|
178
228
|
};
|
|
179
|
-
/**
|
|
180
|
-
* Wraps the Cell's run in a `Policy` — retry, timeout, and their kin — preserving every
|
|
181
|
-
* channel.
|
|
182
|
-
*/
|
|
183
|
-
declare const withPolicy: {
|
|
184
|
-
<A, E, R>(policy: Policy<A, E, R>): <I>(self: Cell<I, A, E, R>) => Cell<I, A, E, R>;
|
|
185
|
-
<I, A, E, R>(self: Cell<I, A, E, R>, policy: Policy<A, E, R>): Cell<I, A, E, R>;
|
|
186
|
-
};
|
|
187
229
|
/**
|
|
188
230
|
* The facts the lint plugin judges a spec body by, as a const table. The order the
|
|
189
231
|
* interpreter runs is the text of {@link layerRunner}; the table states only what a
|
|
@@ -199,4 +241,4 @@ interface Vocabulary {
|
|
|
199
241
|
}
|
|
200
242
|
declare const vocabulary: Vocabulary;
|
|
201
243
|
//#endregion
|
|
202
|
-
export { Cell_d_exports as Cell,
|
|
244
|
+
export { Cell_d_exports as Cell, Workflow_d_exports as Workflow };
|
package/dist/index.mjs
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
|
|
2
|
-
import * as Effect
|
|
2
|
+
import * as Effect from "effect/Effect";
|
|
3
3
|
import { dual } from "effect/Function";
|
|
4
|
+
import * as Option from "effect/Option";
|
|
4
5
|
import * as Result$1 from "effect/Result";
|
|
6
|
+
import { flatMap } from "effect/Result";
|
|
5
7
|
//#region src/Facts.ts
|
|
6
8
|
const DESCRIPTION_MODULE = "@systemfsoftware/effect-cell-types";
|
|
7
9
|
const IO_CELLS = {
|
|
@@ -10,10 +12,42 @@ const IO_CELLS = {
|
|
|
10
12
|
};
|
|
11
13
|
//#endregion
|
|
12
14
|
//#region src/Workflow.ts
|
|
13
|
-
var Workflow_exports = /* @__PURE__ */ __exportAll({
|
|
15
|
+
var Workflow_exports = /* @__PURE__ */ __exportAll({
|
|
16
|
+
andThen: () => andThen$1,
|
|
17
|
+
make: () => make$1,
|
|
18
|
+
total: () => total
|
|
19
|
+
});
|
|
14
20
|
const make$1 = (_command, decide) => {
|
|
15
21
|
return decide;
|
|
16
22
|
};
|
|
23
|
+
/**
|
|
24
|
+
* Brands a decision that cannot fail. `make` refuses a `never` error channel outright
|
|
25
|
+
* (`UninhabitedError`); this is the door for the decider that genuinely decides everything.
|
|
26
|
+
* The decision must still choose between at least two tagged variants sharing one TypeId, so
|
|
27
|
+
* `SingleVariantDecision`, `UntaggedDecision`, and `UnsharedTypeId` still fire.
|
|
28
|
+
*
|
|
29
|
+
* The command schema class comes first, exactly as in {@link make}, so the command channel
|
|
30
|
+
* stays pinned to the class rather than an inferred annotation. The decider's return carries
|
|
31
|
+
* `DecisionShape` written out: the `Workflow` alias is a deferred conditional, and in
|
|
32
|
+
* parameter position it collapses the whole parameter to `unknown` while the decision
|
|
33
|
+
* channel is still generic.
|
|
34
|
+
*/
|
|
35
|
+
const total = (_command, decide) => {
|
|
36
|
+
return decide;
|
|
37
|
+
};
|
|
38
|
+
/**
|
|
39
|
+
* Composes two workflows: what the upstream decides becomes the command the downstream decides
|
|
40
|
+
* on, and a refusal short-circuits the pair. The return type dispatches on the component error
|
|
41
|
+
* union — two components that cannot fail publish the total form, because the `Workflow` alias
|
|
42
|
+
* refuses a `never` channel; a carried error publishes the union as before.
|
|
43
|
+
*/
|
|
44
|
+
const andThen$1 = (_commandA, upstream, commandB, ctx, downstream) => {
|
|
45
|
+
const composed = (command) => flatMap(upstream(command), (decision) => downstream(new commandB({
|
|
46
|
+
decision,
|
|
47
|
+
ctx
|
|
48
|
+
})));
|
|
49
|
+
return composed;
|
|
50
|
+
};
|
|
17
51
|
//#endregion
|
|
18
52
|
//#region src/Cell.ts
|
|
19
53
|
var Cell_exports = /* @__PURE__ */ __exportAll({
|
|
@@ -21,13 +55,14 @@ var Cell_exports = /* @__PURE__ */ __exportAll({
|
|
|
21
55
|
DESCRIPTION_MODULE: () => DESCRIPTION_MODULE,
|
|
22
56
|
IO_CELLS: () => IO_CELLS,
|
|
23
57
|
andThen: () => andThen,
|
|
58
|
+
collect: () => collect,
|
|
59
|
+
collectAll: () => collectAll,
|
|
60
|
+
gate: () => gate,
|
|
24
61
|
layer: () => layer,
|
|
25
62
|
map: () => map,
|
|
26
63
|
mapInput: () => mapInput,
|
|
27
64
|
provide: () => provide,
|
|
28
|
-
run: () => run,
|
|
29
65
|
vocabulary: () => vocabulary,
|
|
30
|
-
withPolicy: () => withPolicy,
|
|
31
66
|
zip: () => zip
|
|
32
67
|
});
|
|
33
68
|
/**
|
|
@@ -44,16 +79,16 @@ const make = (run) => ({
|
|
|
44
79
|
* a decide refusal is the outcome the encode and write receive, not a failure.
|
|
45
80
|
*/
|
|
46
81
|
const layerRunner = (spec) => {
|
|
47
|
-
if ("decode" in spec && "encode" in spec) return (input) => Effect
|
|
82
|
+
if ("decode" in spec && "encode" in spec) return (input) => Effect.gen(function* () {
|
|
48
83
|
const raw = yield* spec.read(input);
|
|
49
84
|
const decoded = yield* Result$1.match(spec.decode(raw), {
|
|
50
|
-
onFailure: Effect
|
|
51
|
-
onSuccess: Effect
|
|
85
|
+
onFailure: Effect.fail,
|
|
86
|
+
onSuccess: Effect.succeed
|
|
52
87
|
});
|
|
53
88
|
const outcome = spec.decide(decoded);
|
|
54
89
|
return yield* spec.write(spec.encode(outcome), raw);
|
|
55
90
|
});
|
|
56
|
-
return (input) => Effect
|
|
91
|
+
return (input) => Effect.gen(function* () {
|
|
57
92
|
const raw = yield* spec.read(input);
|
|
58
93
|
const outcome = spec.decide(raw);
|
|
59
94
|
return yield* spec.write(outcome, raw);
|
|
@@ -62,11 +97,10 @@ const layerRunner = (spec) => {
|
|
|
62
97
|
function layer(spec) {
|
|
63
98
|
return make(layerRunner(spec));
|
|
64
99
|
}
|
|
65
|
-
const run = dual(2, (self, input) => self.run(input));
|
|
66
100
|
/**
|
|
67
101
|
* Transforms the Cell's response.
|
|
68
102
|
*/
|
|
69
|
-
const map = dual(2, (self, f) => make((input) => Effect
|
|
103
|
+
const map = dual(2, (self, f) => make((input) => Effect.map(self.run(input), f)));
|
|
70
104
|
/**
|
|
71
105
|
* Transforms the Cell's input.
|
|
72
106
|
*/
|
|
@@ -75,23 +109,37 @@ const mapInput = dual(2, (self, f) => make((input) => self.run(f(input))));
|
|
|
75
109
|
* Feeds this Cell's response to the next Cell as its input. The error and service channels
|
|
76
110
|
* union.
|
|
77
111
|
*/
|
|
78
|
-
const andThen = dual(2, (self, that) => make((input) => Effect
|
|
112
|
+
const andThen = dual(2, (self, that) => make((input) => Effect.flatMap(self.run(input), (response) => that.run(response))));
|
|
79
113
|
/**
|
|
80
114
|
* Runs both Cells against the same input and tuples the responses. Fails fast: when one
|
|
81
115
|
* side refuses, the other's write never runs.
|
|
82
116
|
*/
|
|
83
|
-
const zip = dual(2, (self, that) => make((input) => Effect
|
|
117
|
+
const zip = dual(2, (self, that) => make((input) => Effect.zipWith(self.run(input), that.run(input), (a, b) => [a, b])));
|
|
118
|
+
/**
|
|
119
|
+
* Runs the inner Cell on the value this Cell read, yielding `Option.none` when it read none.
|
|
120
|
+
* A skip is an absence, never a refusal; the error and service channels union.
|
|
121
|
+
*/
|
|
122
|
+
const gate = dual(2, (self, inner) => make((input) => Effect.flatMap(self.run(input), (read) => Option.match(read, {
|
|
123
|
+
onNone: () => Effect.succeed(Option.none()),
|
|
124
|
+
onSome: (raw) => Effect.map(inner.run(raw), Option.some)
|
|
125
|
+
}))));
|
|
126
|
+
/**
|
|
127
|
+
* Runs the Cell once per item, in order, and folds the responses into one value. The error
|
|
128
|
+
* and service channels are unchanged; the first refusal ends the run.
|
|
129
|
+
*/
|
|
130
|
+
const collect = dual(2, (self, fold) => make((items) => Effect.map(Effect.forEach(items, (item) => self.run(item)), (responses) => fold(responses))));
|
|
131
|
+
/**
|
|
132
|
+
* Runs the Cell once per item, in order, and folds every outcome — each a `Result.Result` —
|
|
133
|
+
* into one value. Unlike {@link collect}, no refusal ends the run: every item is attempted
|
|
134
|
+
* and its failure travels to the fold.
|
|
135
|
+
*/
|
|
136
|
+
const collectAll = dual(2, (self, fold) => make((items) => Effect.map(Effect.forEach(items, (item) => Effect.result(self.run(item))), (results) => fold(results))));
|
|
84
137
|
/**
|
|
85
138
|
* Provides a Layer to the Cell, eliminating the services the layer builds from `R`. This is
|
|
86
139
|
* the one composition-root elimination; the resulting Cell still demands the layer's input
|
|
87
140
|
* services. A missing provide is a compile error at the run site.
|
|
88
141
|
*/
|
|
89
|
-
const provide = dual(2, (self, layer) => make((input) => Effect
|
|
90
|
-
/**
|
|
91
|
-
* Wraps the Cell's run in a `Policy` — retry, timeout, and their kin — preserving every
|
|
92
|
-
* channel.
|
|
93
|
-
*/
|
|
94
|
-
const withPolicy = dual(2, (self, policy) => make((input) => policy(self.run(input))));
|
|
142
|
+
const provide = dual(2, (self, layer) => make((input) => Effect.provide(self.run(input), layer)));
|
|
95
143
|
const vocabulary = {
|
|
96
144
|
module: DESCRIPTION_MODULE,
|
|
97
145
|
ioCells: IO_CELLS,
|
|
@@ -103,7 +151,4 @@ const vocabulary = {
|
|
|
103
151
|
composer: "layer"
|
|
104
152
|
};
|
|
105
153
|
//#endregion
|
|
106
|
-
|
|
107
|
-
var Policy_exports = /* @__PURE__ */ __exportAll({});
|
|
108
|
-
//#endregion
|
|
109
|
-
export { Cell_exports as Cell, Policy_exports as Policy, Workflow_exports as Workflow };
|
|
154
|
+
export { Cell_exports as Cell, Workflow_exports as Workflow };
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@systemfsoftware/effect-cell-types",
|
|
3
3
|
"license": "Apache-2.0",
|
|
4
|
-
"version": "
|
|
4
|
+
"version": "8.1.0",
|
|
5
5
|
"author": "Ryan Lee <drdgvhbh@gmail.com>",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
@@ -43,9 +43,9 @@
|
|
|
43
43
|
"tstyche": "^7.1.0",
|
|
44
44
|
"vitest": "^4",
|
|
45
45
|
"@systemfsoftware/effect-gherkin-spec": "4.0.2",
|
|
46
|
+
"@systemfsoftware/oxlint-config": "^0.1.0",
|
|
46
47
|
"@systemfsoftware/tsconfig": "^1.3.4",
|
|
47
|
-
"@systemfsoftware/vitest-config": "^0.1.0"
|
|
48
|
-
"@systemfsoftware/oxlint-config": "^0.1.0"
|
|
48
|
+
"@systemfsoftware/vitest-config": "^0.1.0"
|
|
49
49
|
},
|
|
50
50
|
"peerDependencies": {
|
|
51
51
|
"effect": "4.0.0-rc.112"
|