@systemfsoftware/effect-cell-types 5.0.2 → 6.0.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 +24 -0
- package/README.md +41 -14
- package/dist/index.d.ts +136 -362
- package/dist/index.mjs +73 -275
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,29 @@
|
|
|
1
1
|
# @systemfsoftware/effect-cell-types
|
|
2
2
|
|
|
3
|
+
## 6.0.0
|
|
4
|
+
|
|
5
|
+
### Major Changes
|
|
6
|
+
|
|
7
|
+
- Cell is one sandwich: build it with `Cell.layer` and run it with `Cell.run`.
|
|
8
|
+
|
|
9
|
+
`Cell.apply` and the chained phase constructors (`Cell.read`, `Cell.decode`, `Cell.decide`, `Cell.encode`, `Cell.write`) are removed. `Cell.layer` takes one spec object — `{ read, decide, write }` rules on what the read produced, or add `decode` and `encode` together to adapt both sides of the decision.
|
|
10
|
+
|
|
11
|
+
Phase bodies that `yield*` a service publish it on the Cell's requirements channel; provide once with `Cell.provide` or `Effect.provide`. A missing provide is a compile error at the run site.
|
|
12
|
+
|
|
13
|
+
New arrows: `map`, `mapInput`, `andThen`, `zip`, `provide`, `withPolicy`. A refused decision reaches your write as the outcome value — only read, decode, and write failures fail the effect. The vocabulary drops `applier`.
|
|
14
|
+
|
|
15
|
+
`Cell.canonical`, `CanonicalCommand`, `canonicalDecide`, `PhaseFact`, and the vocabulary's `phases` list and `byKind.impure` partition are removed: the vocabulary is a const table (`module`, `ioCells`, `byKind.pure`, `composer`) and `PhaseName` is now exported. Nothing else read the walked surface; a rule that wants order reads the interpreter, not the table.
|
|
16
|
+
|
|
17
|
+
- A `Cell` description is now exactly one sandwich: read, decode, decide, encode, write. The `layers` member and the `Layer` type are deleted, `Cell.read` no longer accepts a `previous` argument, and `Cell.apply` runs that single fold; multi-layer replay no longer exists.
|
|
18
|
+
|
|
19
|
+
`Cell.layer(spec)` is new: the same description built from one object. `{ read, decide, write }` composes with identity decode and encode; `{ read, decode, decide, encode, write }` is the full form. A spec with only one of decode/encode, or a short-form write that cannot receive the decide outcome, is a compile error.
|
|
20
|
+
|
|
21
|
+
The walked vocabulary now names its composing constructor as `Cell.vocabulary.composer`, alongside `applier`.
|
|
22
|
+
|
|
23
|
+
Migrating: two layers become two descriptions applied in sequence in the calling `Effect.gen`; a write already receives the read's value as its second parameter.
|
|
24
|
+
|
|
25
|
+
- Workflow.make now requires the success channel to be a tagged union of at least two schema tagged classes sharing one family TypeId; single-variant, untagged, and unshared-brand decisions are compile errors naming the defect
|
|
26
|
+
|
|
3
27
|
## 5.0.2
|
|
4
28
|
|
|
5
29
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -15,11 +15,14 @@ When both channels are inhabited, `Workflow<Command, Decision, Error>` is the fu
|
|
|
15
15
|
`(command: Command) => Result<Decision, Error>` carrying the nominal `WorkflowBrand`
|
|
16
16
|
conjunct — a phantom readonly TypeId-keyed field that no runtime property backs. The brand
|
|
17
17
|
is what makes the workbook nominal: `Workflow.make` is the only constructor that applies
|
|
18
|
-
it, and every surface that runs a decision — `
|
|
19
|
-
demands — requires it, so a decision that skipped `make` is a compile error at the call
|
|
20
|
-
site that would have run it, with the brand named in the diagnostic. A `never` channel does
|
|
18
|
+
it, and every surface that runs a decision — the `decide` member a `Cell.layer` spec
|
|
19
|
+
demands — requires it, so a decision that skipped `make` is a compile error at the call site that would have run it, with the brand named in the diagnostic. A `never` channel does
|
|
21
20
|
not silently collapse to that function: it resolves to a marker interface that no function
|
|
22
|
-
can satisfy, so the mistake is a compile error with the remediation attached (below).
|
|
21
|
+
can satisfy, so the mistake is a compile error with the remediation attached (below). The
|
|
22
|
+
success channel is shaped the same way: `Workflow.make` refuses a decision channel that is
|
|
23
|
+
not a tagged union of at least two schema tagged classes sharing one TypeId — a single
|
|
24
|
+
outcome, an untagged variant, or variants with divergent family brands each resolve to a
|
|
25
|
+
marker interface whose property name is the remediation.
|
|
23
26
|
|
|
24
27
|
## The constructor
|
|
25
28
|
|
|
@@ -134,14 +137,18 @@ rejected, not allowed.
|
|
|
134
137
|
|
|
135
138
|
All six violations fail `tsc`; the messages below are what `tsc` reports (verified against this package and `effect@4.0.0-rc.108`).
|
|
136
139
|
|
|
137
|
-
| Violation | `tsc` reports | Why it is rejected
|
|
138
|
-
| ----------------------------------------- | ----------------------------------------------------------------------------------------- |
|
|
139
|
-
| A `Promise` return | `Type 'Promise<Decision>' is not assignable to type 'Result<Decision, Err>'` | a workflow is a synchronous pure decision; async work belongs in the executor shell around it
|
|
140
|
-
| An `Effect` return | `Type 'Effect<Decision, never, never>' is not assignable to type 'Result<Decision, Err>'` | the workflow returns a value, not an effect handle; the executor runs effects and hands the workflow its input
|
|
141
|
-
| `never` decision channel | `Type '...' is not assignable to type 'UninhabitedDecision'` | a workflow that can never produce a decision can never succeed
|
|
142
|
-
| `never` error channel | `Type '...' is not assignable to type 'UninhabitedError'` | a workflow that cannot fail decides nothing;
|
|
143
|
-
|
|
|
144
|
-
| A
|
|
140
|
+
| Violation | `tsc` reports | Why it is rejected |
|
|
141
|
+
| ----------------------------------------- | ----------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
|
|
142
|
+
| A `Promise` return | `Type 'Promise<Decision>' is not assignable to type 'Result<Decision, Err>'` | a workflow is a synchronous pure decision; async work belongs in the executor shell around it |
|
|
143
|
+
| An `Effect` return | `Type 'Effect<Decision, never, never>' is not assignable to type 'Result<Decision, Err>'` | the workflow returns a value, not an effect handle; the executor runs effects and hands the workflow its input |
|
|
144
|
+
| `never` decision channel | `Type '...' is not assignable to type 'UninhabitedDecision'` | a workflow that can never produce a decision can never succeed |
|
|
145
|
+
| `never` error channel | `Type '...' is not assignable to type 'UninhabitedError'` | a workflow that cannot fail decides nothing; fold the function into its owning module |
|
|
146
|
+
| An untagged error variant | `Type '...' is not assignable to type 'UntaggedError'` | an error variant needs a `_tag` a consumer can dispatch on; declare the errors as `S.TaggedError` instances |
|
|
147
|
+
| A single-variant decision channel | `Type '...' is not assignable to type 'SingleVariantDecision'` | a decision chooses between at least two distinguishable outcomes; one variant is a calculation wearing a decision's shape |
|
|
148
|
+
| An untagged decision variant | `Type '...' is not assignable to type 'UntaggedDecision'` | a decision variant needs a `_tag` a consumer can dispatch on; declare the variants as `S.TaggedClass` instances |
|
|
149
|
+
| Decision variants with no shared TypeId | `Type '...' is not assignable to type 'UnsharedTypeId'` | one decision family carries one TypeId — a `Symbol.for` brand on every variant class |
|
|
150
|
+
| A bare decider in a `Cell.layer` spec | `Type '(command: Cmd) => Result<Dec, Err>' is not assignable to type 'WorkflowBrand'` | only a `Workflow.make` value satisfies the `decide` member; a lambda that skipped `make` is not a decision a description may run |
|
|
151
|
+
| A plain interface at the command position | `'Cmd' only refers to a type, but is being used as a value here` | the command is constrained on the value, and a declared type produces none — so there is no marker to smuggle |
|
|
145
152
|
|
|
146
153
|
The two `never` cases are where the content-vs-filename distinction pays off. `Workflow<C, never, E>` resolves to `UninhabitedDecision` and `Workflow<C, D, never>` to `UninhabitedError` — interfaces whose only property is required and whose _type_ is the remediation, so the compile error points at the fix:
|
|
147
154
|
|
|
@@ -153,7 +160,27 @@ export interface UninhabitedDecision {
|
|
|
153
160
|
|
|
154
161
|
export interface UninhabitedError {
|
|
155
162
|
readonly __WORKFLOW_ERROR_CHANNEL_IS_NEVER__:
|
|
156
|
-
'this workflow cannot fail, so it decides nothing; give it an error variant or
|
|
163
|
+
'this workflow cannot fail, so it decides nothing; give it an error variant or fold the function into its owning module'
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
export interface UntaggedError {
|
|
167
|
+
readonly __WORKFLOW_ERROR_CHANNEL_CARRIES_NO_TAG__:
|
|
168
|
+
'this error carries no _tag the consumer can dispatch on; declare it as an S.TaggedError'
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
export interface SingleVariantDecision {
|
|
172
|
+
readonly __WORKFLOW_DECISION_CHANNEL_HAS_ONE_VARIANT__:
|
|
173
|
+
'this workflow decides one outcome, which is not a decision; add the variant it chooses between, or fold the function into its owning module'
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
export interface UntaggedDecision {
|
|
177
|
+
readonly __WORKFLOW_DECISION_CHANNEL_CARRIES_NO_TAG__:
|
|
178
|
+
'a decision variant carries no _tag the consumer can dispatch on; declare the variants as S.TaggedClass instances'
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
export interface UnsharedTypeId {
|
|
182
|
+
readonly __WORKFLOW_DECISION_VARIANTS_DO_NOT_SHARE_A_TYPE_ID__:
|
|
183
|
+
'the decision variants must share one TypeId — a Symbol.for family brand on each variant class'
|
|
157
184
|
}
|
|
158
185
|
```
|
|
159
186
|
|
|
@@ -211,7 +238,7 @@ const decide = Workflow.make(
|
|
|
211
238
|
// tsc: Type 'Result<Decision, unknown>' is not assignable to type 'Result<Decision, Err>'
|
|
212
239
|
```
|
|
213
240
|
|
|
214
|
-
If the workflow genuinely cannot fail, the error channel says so — and that is a
|
|
241
|
+
If the workflow genuinely cannot fail, the error channel says so — and that is a plain function inside its owning module, not a workflow.
|
|
215
242
|
|
|
216
243
|
## A wrong channel breaks the whole consumer cone
|
|
217
244
|
|
package/dist/index.d.ts
CHANGED
|
@@ -2,429 +2,203 @@ import * as Effect$1 from "effect/Effect";
|
|
|
2
2
|
import { Effect } from "effect/Effect";
|
|
3
3
|
import * as Result$1 from "effect/Result";
|
|
4
4
|
import { Result } from "effect/Result";
|
|
5
|
-
import * as Schema$1 from "effect/Schema";
|
|
6
5
|
import { Schema } from "effect";
|
|
6
|
+
import { Kind, TypeLambda } from "effect/HKT";
|
|
7
|
+
import { Layer } from "effect/Layer";
|
|
8
|
+
import * as Schema$1 from "effect/Schema";
|
|
9
|
+
//#region src/Facts.d.ts
|
|
10
|
+
declare const DESCRIPTION_MODULE: '@systemfsoftware/effect-cell-types';
|
|
11
|
+
declare const IO_CELLS: {
|
|
12
|
+
readonly cells: readonly ['store', 'adapter'];
|
|
13
|
+
readonly sources: readonly ['effect/Clock', 'effect/System'];
|
|
14
|
+
};
|
|
15
|
+
type IoCellClassification = typeof IO_CELLS;
|
|
16
|
+
type PhaseName = 'read' | 'decode' | 'decide' | 'encode' | 'write';
|
|
17
|
+
declare namespace Policy_d_exports {
|
|
18
|
+
export { Policy };
|
|
19
|
+
}
|
|
20
|
+
type Policy<A, E, R> = (self: Effect<A, E, R>) => Effect<A, E, R>;
|
|
7
21
|
declare namespace Workflow_d_exports {
|
|
8
|
-
export { Inhabited, UninhabitedDecision, UninhabitedError, UntaggedError, Workflow, WorkflowBrand, make };
|
|
22
|
+
export { Inhabited, SingleVariantDecision, UninhabitedDecision, UninhabitedError, UnsharedTypeId, UntaggedDecision, UntaggedError, Workflow, WorkflowBrand, make };
|
|
9
23
|
}
|
|
10
|
-
/**
|
|
11
|
-
* The nominal brand a workflow carries. `Workflow.make` is the only door that applies it,
|
|
12
|
-
* and every surface that runs a decision — `Cell.decide`, and through it `DecideNode.run`
|
|
13
|
-
* via the `DecidePhase` conjunct — demands it, so a bare decider that skipped `make` is
|
|
14
|
-
* refused by the compiler at the call site that would have run it.
|
|
15
|
-
*
|
|
16
|
-
* The brand is phantom — a readonly TypeId-keyed field that no runtime property ever
|
|
17
|
-
* backs: `assertWorkflow` narrows the same function value without touching it. The
|
|
18
|
-
* `Symbol.for` key follows the repo's branded-class idiom (the instance brands in
|
|
19
|
-
* `restart-decision.workflow.ts`, `hook-verdict.workflow.ts` and `survivors.workflow.ts`)
|
|
20
|
-
* so it is stable across realms; this is the type-level brand that subsumes those
|
|
21
|
-
* instance brands on the workflow shapes.
|
|
22
|
-
*/
|
|
23
24
|
declare const WorkflowTypeId: unique symbol;
|
|
24
25
|
type WorkflowTypeId = typeof WorkflowTypeId;
|
|
25
|
-
/** The phantom property `Workflow<C,D,E>` and `Cell.DecidePhase<P>` carry. */
|
|
26
26
|
interface WorkflowBrand {
|
|
27
27
|
readonly [WorkflowTypeId]: WorkflowTypeId;
|
|
28
28
|
}
|
|
29
|
-
/**
|
|
30
|
-
* Marker a workflow resolves to when its decision channel is `never`. The property type is the
|
|
31
|
-
* fix, so the compiler diagnostic names it.
|
|
32
|
-
*/
|
|
33
29
|
interface UninhabitedDecision {
|
|
34
30
|
readonly __WORKFLOW_DECISION_CHANNEL_IS_NEVER__: 'this workflow can never succeed; give it a decision variant it can return';
|
|
35
31
|
}
|
|
36
|
-
/** Marker a workflow resolves to when its error channel is `never`. */
|
|
37
32
|
interface UninhabitedError {
|
|
38
|
-
readonly __WORKFLOW_ERROR_CHANNEL_IS_NEVER__: 'this workflow cannot fail, so it decides nothing; give it an error variant or
|
|
33
|
+
readonly __WORKFLOW_ERROR_CHANNEL_IS_NEVER__: 'this workflow cannot fail, so it decides nothing; give it an error variant or fold the function into its owning module';
|
|
39
34
|
}
|
|
40
|
-
/** Marker a workflow resolves to when its error channel carries no tag to dispatch on. */
|
|
41
35
|
interface UntaggedError {
|
|
42
36
|
readonly __WORKFLOW_ERROR_CHANNEL_CARRIES_NO_TAG__: 'this error carries no _tag the consumer can dispatch on; declare it as an S.TaggedError';
|
|
43
37
|
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
38
|
+
interface SingleVariantDecision {
|
|
39
|
+
readonly __WORKFLOW_DECISION_CHANNEL_HAS_ONE_VARIANT__: 'this workflow decides one outcome, which is not a decision; add the variant it chooses between, or fold the function into its owning module';
|
|
40
|
+
}
|
|
41
|
+
interface UntaggedDecision {
|
|
42
|
+
readonly __WORKFLOW_DECISION_CHANNEL_CARRIES_NO_TAG__: 'a decision variant carries no _tag the consumer can dispatch on; declare the variants as S.TaggedClass instances';
|
|
43
|
+
}
|
|
44
|
+
interface UnsharedTypeId {
|
|
45
|
+
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';
|
|
46
|
+
}
|
|
47
|
+
type UnionToIntersection<U> = (U extends unknown ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never;
|
|
48
|
+
type AtLeastTwoDistinct<T, U = T> = U extends unknown ? [T] extends [U] ? false : true : never;
|
|
49
|
+
type TaggedMembers<D> = D extends unknown ? '_tag' extends keyof D ? [D['_tag']] extends [string] ? true : false : false : never;
|
|
50
|
+
type SharedTypeId<D, I = UnionToIntersection<D>> = [keyof { [K in keyof I as I[K] extends K ? (D extends { readonly [P in K]: K; } ? K : never) : never]: 0; }] extends [never] ? UnsharedTypeId : unknown;
|
|
51
|
+
type DecisionShape<D> = [unknown] extends [D] ? unknown : AtLeastTwoDistinct<D> extends false ? SingleVariantDecision : boolean extends TaggedMembers<D> ? UntaggedDecision : SharedTypeId<D>;
|
|
54
52
|
type Workflow<Command, Decision, DecisionError> = [Decision] extends [never] ? UninhabitedDecision : [DecisionError] extends [never] ? UninhabitedError : ((command: Command) => Result<Decision, DecisionError>) & WorkflowBrand;
|
|
55
|
-
/**
|
|
56
|
-
* `unknown` when the error channel carries a tag a consumer can dispatch on, the {@link UntaggedError}
|
|
57
|
-
* marker otherwise. Two steps, both load-bearing: `'_tag' extends keyof E` asks whether the key is
|
|
58
|
-
* there, and `[E['_tag']] extends [string]` asks whether what it holds is dispatchable. Key presence
|
|
59
|
-
* alone admits `_tag: number`, `_tag?: string` and a `_tag()` method — none of which `Match.tag` can
|
|
60
|
-
* dispatch on, which is exactly what the marker claims to refuse.
|
|
61
|
-
*
|
|
62
|
-
* Both steps read the tag through `keyof` and an indexed access rather than declaring a `_tag`
|
|
63
|
-
* member, which is why the erased `Tagged` interface cannot come back: stating the requirement as a
|
|
64
|
-
* shape would write the very member this repo forbids.
|
|
65
|
-
*/
|
|
66
53
|
type DispatchableTag<E> = '_tag' extends keyof E ? [E['_tag']] extends [string] ? unknown : UntaggedError : UntaggedError;
|
|
67
|
-
|
|
68
|
-
* `unknown` when both channels are inhabited and the error carries a dispatchable tag, so the
|
|
69
|
-
* intersection in {@link make} collapses to the plain `Result` and neither inference nor the
|
|
70
|
-
* authoring surface changes. Otherwise the marker the author must satisfy, which they cannot, which
|
|
71
|
-
* is the point.
|
|
72
|
-
*/
|
|
73
|
-
type Inhabited<Decision, DecisionError> = [Decision] extends [never] ? UninhabitedDecision : [DecisionError] extends [never] ? UninhabitedError : DispatchableTag<DecisionError>;
|
|
74
|
-
/**
|
|
75
|
-
* Builds a workflow from the command's schema class and a decider over that class's
|
|
76
|
-
* instance type, refusing an uninhabited or untagged channel at this call rather than at
|
|
77
|
-
* whoever first calls the result — which for a workflow nothing calls yet is never.
|
|
78
|
-
*
|
|
79
|
-
* The command is constrained on the **value**, not on a type parameter inferred from the
|
|
80
|
-
* decider's parameter. That is the whole mechanism. Any constraint on such a parameter is a
|
|
81
|
-
* structural predicate, and TypeScript cannot say "this type came from a class declaration"
|
|
82
|
-
* — so a marker placed there is a property, every property is declarable, and
|
|
83
|
-
* `interface Fake extends Marker {}` satisfies it. A declared type produces no value, so it
|
|
84
|
-
* cannot reach an argument position at all: there is no marker to smuggle because there is
|
|
85
|
-
* no marker.
|
|
86
|
-
*
|
|
87
|
-
* The three parameters mirror `Schema.Class`'s own bound exactly, and that is load-bearing.
|
|
88
|
-
* `Class<Self, S, Inherited>` places `S` in both covariant (`S["Type"]`) and contravariant
|
|
89
|
-
* (`S["fields"]`) positions, so it is invariant in `S`: every *fixed* spelling —
|
|
90
|
-
* `Class<unknown, Struct<Struct.Fields>, unknown>` and its variants — rejects real command
|
|
91
|
-
* classes. Generic over `S` accepts them and still refuses a `Struct`, which lacks
|
|
92
|
-
* `identifier` and `extend`. `Class<any, any, any>` also works and is banned here.
|
|
93
|
-
*
|
|
94
|
-
* `Schema.TaggedClass` returns this same `Class` interface, so one constraint covers both
|
|
95
|
-
* factories with no union. The import is type-only: this package gains no runtime dependency
|
|
96
|
-
* on Effect Schema, and `make` stays the identity function it always was.
|
|
97
|
-
*
|
|
98
|
-
* The markers ride the parameter function's return type, not the parameter as `Workflow<C, D, E>`:
|
|
99
|
-
* a conditional type in parameter position resolves `D` and `E` to `unknown` and the markers become
|
|
100
|
-
* unreachable. On the return type both still infer from the `Result` conjunct while the marker
|
|
101
|
-
* conjunct is what an uninhabited channel fails to satisfy.
|
|
102
|
-
*
|
|
103
|
-
* `E` carries no constraint on purpose. Constraining it gives inference a fallback: where `E` would
|
|
104
|
-
* infer as `never` TypeScript substitutes the constraint instead, the conditional takes its
|
|
105
|
-
* inhabited branch, and a `never` channel passes. The tagged requirement therefore lives in
|
|
106
|
-
* {@link Inhabited}, where nothing can stand in for `never`.
|
|
107
|
-
*
|
|
108
|
-
* The narrowing goes through an assertion signature rather than an `as` cast: every narrowing
|
|
109
|
-
* assertion trips `typescript(no-unsafe-type-assertion)`, and a suppression comment would hide the
|
|
110
|
-
* one place this file could lie. It is sound rather than merely permitted — with both channels
|
|
111
|
-
* inhabited `Workflow<Self, D, E>` is `(command: Self) => Result<D, E>` carrying the
|
|
112
|
-
* {@link WorkflowBrand} conjunct, and otherwise the return type is a marker with no call
|
|
113
|
-
* signature, so the value handed back is unobservable through it. The brand is applied here and
|
|
114
|
-
* nowhere else: the assertion adds no runtime property, yet a value that did not pass through this
|
|
115
|
-
* door fails the conjunct wherever a decision is run.
|
|
116
|
-
*/
|
|
54
|
+
type Inhabited<Decision, DecisionError> = [Decision] extends [never] ? UninhabitedDecision : [DecisionError] extends [never] ? UninhabitedError : DecisionShape<Decision> & DispatchableTag<DecisionError>;
|
|
117
55
|
declare const make: <Self, S extends Schema$1.Constraint & {
|
|
118
56
|
readonly fields: Schema$1.Struct.Fields;
|
|
119
57
|
}, Inherited, D, E>(_command: Schema$1.Class<Self, S, Inherited>, decide: (command: Self) => Result<D, E> & Inhabited<D, E>) => Workflow<Self, D, E>;
|
|
120
|
-
//#endregion
|
|
121
|
-
//#region src/CanonicalDecide.workflow.d.ts
|
|
122
|
-
declare const CanonicalCommand_base: Schema$1.Class<CanonicalCommand, Schema$1.TaggedStruct<"CanonicalCommand", {}>, {}>;
|
|
123
|
-
/**
|
|
124
|
-
* The canonical command. `Workflow.make` constrains its first argument to a real
|
|
125
|
-
* schema class, so the canonical description needs one too — it carries no fields
|
|
126
|
-
* because the canonical's phases do nothing, and its only job is to be a genuine
|
|
127
|
-
* command value rather than a shape asserted into place.
|
|
128
|
-
*
|
|
129
|
-
* It is declared here rather than in a `*.schema.ts` because this is the owning
|
|
130
|
-
* single-segment `<stem>.workflow.ts`, which `schema-declaration-location` admits.
|
|
131
|
-
*/
|
|
132
|
-
declare class CanonicalCommand extends CanonicalCommand_base {}
|
|
133
58
|
declare namespace Cell_d_exports {
|
|
134
|
-
export {
|
|
59
|
+
export { Cell, CellTypeId, DESCRIPTION_MODULE, IO_CELLS, IoCellClassification, Kind$1 as Kind, PhaseName, Run, TypeLambda$1 as TypeLambda, Vocabulary, andThen, layer, map, mapInput, provide, run, vocabulary, withPolicy, zip };
|
|
135
60
|
}
|
|
136
61
|
/**
|
|
137
|
-
* The
|
|
138
|
-
* stage's own type arguments are identical across stages, which leaves the sentence
|
|
139
|
-
* member as the only difference a diagnostic can report. Measured: with the payload
|
|
140
|
-
* carried per-stage instead, the compiler reports the mismatch at the argument (TS2345)
|
|
141
|
-
* and the sentence arrives only as a type argument; with the payload in one bag it
|
|
142
|
-
* reports the missing member (TS2741) and the sentence is that member's name.
|
|
62
|
+
* The nominal brand every `Cell` carries. `Cell.layer` is the only door that applies it.
|
|
143
63
|
*/
|
|
144
|
-
|
|
145
|
-
readonly command: unknown;
|
|
146
|
-
readonly raw: unknown;
|
|
147
|
-
readonly decoded: unknown;
|
|
148
|
-
readonly decision: unknown;
|
|
149
|
-
readonly decisionError: unknown;
|
|
150
|
-
readonly output: unknown;
|
|
151
|
-
readonly response: unknown;
|
|
152
|
-
readonly decodeError: unknown;
|
|
153
|
-
readonly readError: unknown;
|
|
154
|
-
readonly writeError: unknown;
|
|
155
|
-
}
|
|
64
|
+
declare const CellTypeId: unique symbol;
|
|
156
65
|
/**
|
|
157
|
-
*
|
|
158
|
-
* that interior is not type-visible, so no I/O count is claimed or enforced here. A step that
|
|
159
|
-
* mutates in order to report — bumping a counter and returning the resulting rate — is one
|
|
160
|
-
* such product, and belongs here rather than in a layer of its own.
|
|
161
|
-
*
|
|
162
|
-
* The context channel is pinned `never`: a phase requires nothing. Services are resolved by
|
|
163
|
-
* whoever builds the description and handed to the phase as ordinary parameters, which is
|
|
164
|
-
* the same edge that already gathers the read's inputs. The alternative — a `readContext`
|
|
165
|
-
* member on the bag — let an author write `never` for a body that reaches for a service, and
|
|
166
|
-
* nothing checked the claim: under a stage generic over `Phases` the compiler cannot see the
|
|
167
|
-
* lambda's requirement at all, so the description compiled and the missing service surfaced
|
|
168
|
-
* only where it was finally applied, or nowhere. Pinning it makes the lie unrepresentable
|
|
169
|
-
* instead of merely discouraged, and leaves `apply`'s derived `R` honestly `never`.
|
|
66
|
+
* The nominal brand type of {@link CellTypeId}.
|
|
170
67
|
*/
|
|
171
|
-
type
|
|
172
|
-
/** Validation. Its `Left` is fatal: it reaches the derived error channel and no write runs. */
|
|
173
|
-
type DecodePhase<P extends Phases> = (raw: P['raw']) => Result$1.Result<P['decoded'], P['decodeError']>;
|
|
68
|
+
type CellTypeId = typeof CellTypeId;
|
|
174
69
|
/**
|
|
175
|
-
*
|
|
176
|
-
*
|
|
177
|
-
*
|
|
178
|
-
*
|
|
179
|
-
* refused by the compiler with the brand conjunct named in the diagnostic. The `run` on
|
|
180
|
-
* a `DecideNode` inherits the conjunct through this type, which keeps the interpreter's
|
|
181
|
-
* fold sound — a description can only carry decisions that came through `make`.
|
|
70
|
+
* A Cell is one sandwich: a `read` that gathers, a `decide` that refuses or rules, and a
|
|
71
|
+
* `write` that acts — compiled into a single function from command to response. The `E`
|
|
72
|
+
* channel carries the infrastructure refusals; the `R` channel carries the services the
|
|
73
|
+
* phases `yield*`, provided once by the program's composition root.
|
|
182
74
|
*/
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
75
|
+
interface Cell<in I, out A, out E = never, out R = never> {
|
|
76
|
+
readonly [CellTypeId]: CellTypeId;
|
|
77
|
+
readonly run: (input: I) => Effect$1.Effect<A, E, R>;
|
|
78
|
+
}
|
|
186
79
|
/**
|
|
187
|
-
* The
|
|
188
|
-
* layer's read gathered.
|
|
189
|
-
*
|
|
190
|
-
* `raw` is there because a write is frequently the point that persists or reports what the
|
|
191
|
-
* read found, while the decision in between deliberately narrows to what it needed. Without
|
|
192
|
-
* this argument such a write has no channel for it and the layer smuggles the value through
|
|
193
|
-
* a closure — a `let` beside the description, assigned in the read and consulted in the
|
|
194
|
-
* write, which then needs a runtime guard for a value the fold has already produced. The
|
|
195
|
-
* argument is second, and a write that does not want it declares one parameter: a unary
|
|
196
|
-
* function satisfies this type, so every write written before it existed is unchanged.
|
|
80
|
+
* The type lambda for {@link Cell}, admitting Cell to `Kind` positions.
|
|
197
81
|
*/
|
|
198
|
-
|
|
82
|
+
interface TypeLambda$1 extends TypeLambda {
|
|
83
|
+
readonly type: Cell<this['In'], this['Target'], this['Out2'], this['Out1']>;
|
|
84
|
+
}
|
|
199
85
|
/**
|
|
200
|
-
*
|
|
201
|
-
* - `'effect'` — `run` returns an `Effect`; yield it. (read, write)
|
|
202
|
-
* - `'either-fail'` — `run` returns a `Result` whose `Failure` is fatal; fail on `Failure`. (decode)
|
|
203
|
-
* - `'either-pass'` — `run` returns a `Result` that travels forward whole. (decide)
|
|
204
|
-
* - `'total'` — `run` is a plain total function; call it directly. (encode)
|
|
205
|
-
*
|
|
206
|
-
* This is structural data on the record, not one of the five axes: it lets an executing
|
|
207
|
-
* consumer fold the description without knowing which phase it is looking at. The
|
|
208
|
-
* interpreter's switch over `convention` is exhaustively defaulted, so a phase with an
|
|
209
|
-
* invocation shape this module does not know fails at compile time at one named location.
|
|
86
|
+
* A fully-applied {@link Kind} for Cell: `Kind<I, E, R, A>` is `Cell<I, A, E, R>`.
|
|
210
87
|
*/
|
|
211
|
-
type
|
|
88
|
+
type Kind$1<I, E, R, A> = Kind<TypeLambda$1, I, E, R, A>;
|
|
212
89
|
/**
|
|
213
|
-
*
|
|
214
|
-
*
|
|
215
|
-
*
|
|
216
|
-
* the interpreter dispatches on `convention`, and other consumers read `name` as data.
|
|
217
|
-
* A hand-written `_tag` is deliberately absent — a manual `_tag` member is forbidden
|
|
218
|
-
* here (the schema rule prescribes `TaggedStruct`, which cannot describe a function
|
|
219
|
-
* member), and an ordered sequence of phase records is not a `Match`-style tagged union.
|
|
90
|
+
* The function shape a Cell publishes, read off the Cell type itself. Use it to type a
|
|
91
|
+
* capability parameter or a callback that hands a Cell's run to a shell:
|
|
92
|
+
* `Run<I, A, E, R>` is `Cell<I, A, E, R>['run']`.
|
|
220
93
|
*/
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
readonly
|
|
224
|
-
readonly
|
|
225
|
-
readonly
|
|
94
|
+
type Run<I, A, E, R> = Cell<I, A, E, R>['run'];
|
|
95
|
+
interface LayerCore<I, Raw, RE, RR, Dec, DE, Resp, WE, WR> {
|
|
96
|
+
readonly read: (command: I) => Effect$1.Effect<Raw, RE, RR>;
|
|
97
|
+
readonly decide: ((decoded: Raw) => Result$1.Result<Dec, DE>) & WorkflowBrand;
|
|
98
|
+
readonly write: (output: Result$1.Result<Dec, DE>, raw: Raw) => Effect$1.Effect<Resp, WE, WR>;
|
|
226
99
|
}
|
|
227
|
-
interface
|
|
228
|
-
readonly
|
|
229
|
-
readonly
|
|
230
|
-
readonly convention: 'either-fail';
|
|
231
|
-
readonly run: DecodePhase<P>;
|
|
100
|
+
interface LayerShortSpec<I, Raw, RE, RR, Dec, DE, Resp, WE, WR> extends LayerCore<I, Raw, RE, RR, Dec, DE, Resp, WE, WR> {
|
|
101
|
+
readonly decode?: never;
|
|
102
|
+
readonly encode?: never;
|
|
232
103
|
}
|
|
233
|
-
interface
|
|
234
|
-
readonly
|
|
235
|
-
readonly
|
|
236
|
-
readonly
|
|
237
|
-
readonly
|
|
104
|
+
interface LayerLongSpec<I, Raw, RE, RR, Dcd, DecE, Dec, DE, Out, Resp, WE, WR> extends Omit<LayerCore<I, Raw, RE, RR, Dec, DE, Resp, WE, WR>, 'decide' | 'write'> {
|
|
105
|
+
readonly decode: (raw: Raw) => Result$1.Result<Dcd, DecE>;
|
|
106
|
+
readonly decide: ((decoded: Dcd) => Result$1.Result<Dec, DE>) & WorkflowBrand;
|
|
107
|
+
readonly encode: (outcome: Result$1.Result<Dec, DE>) => Out;
|
|
108
|
+
readonly write: (output: Out, raw: Raw) => Effect$1.Effect<Resp, WE, WR>;
|
|
238
109
|
}
|
|
239
|
-
interface EncodeNode<P extends Phases> {
|
|
240
|
-
readonly name: 'encode';
|
|
241
|
-
readonly kind: 'pure';
|
|
242
|
-
readonly convention: 'total';
|
|
243
|
-
readonly run: EncodePhase<P>;
|
|
244
|
-
}
|
|
245
|
-
interface WriteNode<P extends Phases> {
|
|
246
|
-
readonly name: 'write';
|
|
247
|
-
readonly kind: 'impure';
|
|
248
|
-
readonly convention: 'effect';
|
|
249
|
-
readonly run: WritePhase<P>;
|
|
250
|
-
}
|
|
251
|
-
type Phase<P extends Phases> = ReadNode<P> | DecodeNode<P> | DecideNode<P> | EncodeNode<P> | WriteNode<P>;
|
|
252
|
-
/**
|
|
253
|
-
* One impure/pure layer: its phase records in intra-layer execution order. The order is
|
|
254
|
-
* data — the interpreter folds this array and runs each record as its `convention`
|
|
255
|
-
* says, holding no phase sequence of its own. The stage brands are what make a legal
|
|
256
|
-
* description well-ordered at build time; the value's declared order is its execution
|
|
257
|
-
* order.
|
|
258
|
-
*/
|
|
259
|
-
interface Layer<P extends Phases> {
|
|
260
|
-
readonly phases: readonly Phase<P>[];
|
|
261
|
-
}
|
|
262
|
-
/** The description package's own module name — what an import edge would match. */
|
|
263
|
-
declare const DESCRIPTION_MODULE: '@systemfsoftware/effect-cell-types';
|
|
264
110
|
/**
|
|
265
|
-
*
|
|
266
|
-
*
|
|
111
|
+
* Builds a Cell from one sandwich.
|
|
112
|
+
*
|
|
113
|
+
* Short form — `read` produces the value `decide` rules on, and the decide outcome is what
|
|
114
|
+
* `write` receives:
|
|
115
|
+
*
|
|
116
|
+
* ```ts
|
|
117
|
+
* import { Cell, Workflow } from '@systemfsoftware/effect-cell-types'
|
|
118
|
+
* import { Effect, Result } from 'effect'
|
|
119
|
+
*
|
|
120
|
+
* declare const decideAdmission: Workflow<CliArgs, Verdict, Refusal>
|
|
121
|
+
* declare class CliArgs { readonly target: string }
|
|
122
|
+
* declare class Verdict { readonly ok: boolean }
|
|
123
|
+
* declare class Refusal { readonly _tag: 'Refused' }
|
|
124
|
+
*
|
|
125
|
+
* const cell = Cell.layer({
|
|
126
|
+
* read: (args: CliArgs) => Effect.succeed(args),
|
|
127
|
+
* decide: decideAdmission,
|
|
128
|
+
* write: (outcome: Result.Result<Verdict, Refusal>, raw: CliArgs) => Effect.void,
|
|
129
|
+
* })
|
|
130
|
+
* ```
|
|
131
|
+
*
|
|
132
|
+
* Long form — `decode` and `encode` adapt each side of `decide`; both are required together,
|
|
133
|
+
* and a spec carrying one without the other fails inference.
|
|
267
134
|
*/
|
|
268
|
-
declare
|
|
269
|
-
|
|
270
|
-
|
|
135
|
+
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>;
|
|
136
|
+
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>;
|
|
137
|
+
declare const run: {
|
|
138
|
+
<I>(input: I): <A, E, R>(self: Cell<I, A, E, R>) => Effect$1.Effect<A, E, R>;
|
|
139
|
+
<I, A, E, R>(self: Cell<I, A, E, R>, input: I): Effect$1.Effect<A, E, R>;
|
|
271
140
|
};
|
|
272
141
|
/**
|
|
273
|
-
*
|
|
274
|
-
* declaration of axis 5, and the two drift the moment a cell is reclassified in only one.
|
|
142
|
+
* Transforms the Cell's response.
|
|
275
143
|
*/
|
|
276
|
-
|
|
144
|
+
declare const map: {
|
|
145
|
+
<A, B>(f: (a: A) => B): <I, E, R>(self: Cell<I, A, E, R>) => Cell<I, B, E, R>;
|
|
146
|
+
<I, A, E, R, B>(self: Cell<I, A, E, R>, f: (a: A) => B): Cell<I, B, E, R>;
|
|
147
|
+
};
|
|
277
148
|
/**
|
|
278
|
-
*
|
|
279
|
-
* recover the description's whole vocabulary: the phase names and kinds on each
|
|
280
|
-
* record, the intra-layer order in each `phases` array, the package's own module
|
|
281
|
-
* name, and the I/O-cell classification. Nothing about the shape of a legal
|
|
282
|
-
* description is written down anywhere else.
|
|
149
|
+
* Transforms the Cell's input.
|
|
283
150
|
*/
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
}
|
|
151
|
+
declare const mapInput: {
|
|
152
|
+
<I0, I>(f: (input: I0) => I): <A, E, R>(self: Cell<I, A, E, R>) => Cell<I0, A, E, R>;
|
|
153
|
+
<I0, I, A, E, R>(self: Cell<I, A, E, R>, f: (input: I0) => I): Cell<I0, A, E, R>;
|
|
154
|
+
};
|
|
289
155
|
/**
|
|
290
|
-
*
|
|
291
|
-
*
|
|
292
|
-
* stage is assignable to an earlier parameter, so an inversion — decoding what was already
|
|
293
|
-
* decided — compiles. As siblings both the forward skip and the backward inversion are
|
|
294
|
-
* rejected, each diagnostic naming the sentence it is missing.
|
|
295
|
-
*
|
|
296
|
-
* The carrier is deliberately the same type on every stage — `Description` — so the
|
|
297
|
-
* compiler reports the argument rather than the member only when the carrier itself is
|
|
298
|
-
* wrong, and the sentence stays the name a diagnostic reports.
|
|
156
|
+
* Feeds this Cell's response to the next Cell as its input. The error and service channels
|
|
157
|
+
* union.
|
|
299
158
|
*/
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
readonly 'call decode(raw) before decide(decoded)': true;
|
|
305
|
-
}
|
|
306
|
-
interface DecideDone<P extends Phases> extends Description<P> {
|
|
307
|
-
readonly 'call decide(decoded) before encode(decision)': true;
|
|
308
|
-
}
|
|
309
|
-
interface EncodeDone<P extends Phases> extends Description<P> {
|
|
310
|
-
readonly 'call encode(decision) before write(output)': true;
|
|
311
|
-
}
|
|
312
|
-
/** Terminal. A description is applied from here, and a further layer opens from here. */
|
|
313
|
-
interface WriteDone<P extends Phases> extends Description<P> {
|
|
314
|
-
readonly 'call write(output) before applying the description': true;
|
|
315
|
-
}
|
|
159
|
+
declare const andThen: {
|
|
160
|
+
<B, E2, R2>(that: Cell<never, B, E2, R2>): <I, A, E, R>(self: Cell<I, A, E, R>) => Cell<I, B, E | E2, R | R2>;
|
|
161
|
+
<I, A, E, R, B, E2, R2>(self: Cell<I, A, E, R>, that: Cell<A, B, E2, R2>): Cell<I, B, E | E2, R | R2>;
|
|
162
|
+
};
|
|
316
163
|
/**
|
|
317
|
-
*
|
|
318
|
-
*
|
|
319
|
-
* layers rather than two descriptions composed by hand.
|
|
320
|
-
*
|
|
321
|
-
* This one is not dual: it starts the chain, so on the opening layer it has no `self` to
|
|
322
|
-
* receive. Every phase after it is dual, which is what lets a description be written in the
|
|
323
|
-
* order it runs.
|
|
164
|
+
* Runs both Cells against the same input and tuples the responses. Fails fast: when one
|
|
165
|
+
* side refuses, the other's write never runs.
|
|
324
166
|
*/
|
|
325
|
-
declare const
|
|
167
|
+
declare const zip: {
|
|
168
|
+
<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>;
|
|
169
|
+
<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>;
|
|
170
|
+
};
|
|
326
171
|
/**
|
|
327
|
-
*
|
|
328
|
-
*
|
|
329
|
-
*
|
|
330
|
-
* and the sentence still arrives as a missing member through it.
|
|
331
|
-
*
|
|
332
|
-
* A `Do`-notation scope binding each phase's result for later phases to read was measured as
|
|
333
|
-
* an alternative: the sentence survives it, even with the scope varying per stage, because an
|
|
334
|
-
* absent member is reported before type arguments are compared. It was not adopted, because a
|
|
335
|
-
* scope the interpreter folds over is type-erased, and reading a phase back out of it needs an
|
|
336
|
-
* assertion this design does not.
|
|
172
|
+
* Provides a Layer to the Cell, eliminating the services the layer builds from `R`. This is
|
|
173
|
+
* the one composition-root elimination; the resulting Cell still demands the layer's input
|
|
174
|
+
* services. A missing provide is a compile error at the run site.
|
|
337
175
|
*/
|
|
338
|
-
declare const
|
|
339
|
-
<
|
|
340
|
-
<
|
|
341
|
-
};
|
|
342
|
-
declare const decide: {
|
|
343
|
-
<P extends Phases>(run: DecidePhase<P>): (previous: DecodeDone<P>) => DecideDone<P>;
|
|
344
|
-
<P extends Phases>(previous: DecodeDone<P>, run: DecidePhase<P>): DecideDone<P>;
|
|
345
|
-
};
|
|
346
|
-
declare const encode: {
|
|
347
|
-
<P extends Phases>(run: EncodePhase<P>): (previous: DecideDone<P>) => EncodeDone<P>;
|
|
348
|
-
<P extends Phases>(previous: DecideDone<P>, run: EncodePhase<P>): EncodeDone<P>;
|
|
349
|
-
};
|
|
350
|
-
declare const write: {
|
|
351
|
-
<P extends Phases>(run: WritePhase<P>): (previous: EncodeDone<P>) => WriteDone<P>;
|
|
352
|
-
<P extends Phases>(previous: EncodeDone<P>, run: WritePhase<P>): WriteDone<P>;
|
|
176
|
+
declare const provide: {
|
|
177
|
+
<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>>;
|
|
178
|
+
<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>>;
|
|
353
179
|
};
|
|
354
180
|
/**
|
|
355
|
-
*
|
|
356
|
-
*
|
|
357
|
-
* unrepresentable rather than merely discouraged. Annotating it here would let this module
|
|
358
|
-
* promise a failure that no phase can produce.
|
|
359
|
-
*
|
|
360
|
-
* The parameter keeps the terminal `WriteDone<P>` brand. It is not decoration: it is what
|
|
361
|
-
* makes applying a half-built chain — a `ReadDone`, say — a compile error rather than a
|
|
362
|
-
* runtime death, which `test-types/Cell.tst.ts` pins. The brand does not constrain the
|
|
363
|
-
* phases array's order (a literal satisfies it in any order, which is why the interpreter
|
|
364
|
-
* reads the order off the value), but it does constrain chain completion, and that is a
|
|
365
|
-
* guarantee worth the narrower parameter.
|
|
366
|
-
*
|
|
367
|
-
* `forEach` takes no concurrency option here, so the layers run in declared order and the
|
|
368
|
-
* sequence is structural rather than something a caller could pass differently; the
|
|
369
|
-
* description's response is the last layer's. No scope is opened and interruptibility is
|
|
370
|
-
* untouched, so a `Scope.Scope` a phase requires reaches the caller as part of the derived `R`.
|
|
181
|
+
* Wraps the Cell's run in a `Policy` — retry, timeout, and their kin — preserving every
|
|
182
|
+
* channel.
|
|
371
183
|
*/
|
|
372
|
-
declare const
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
readonly kind: Phase<Phases>['kind'];
|
|
377
|
-
readonly convention: Convention;
|
|
378
|
-
}
|
|
184
|
+
declare const withPolicy: {
|
|
185
|
+
<A, E, R>(policy: Policy<A, E, R>): <I>(self: Cell<I, A, E, R>) => Cell<I, A, E, R>;
|
|
186
|
+
<I, A, E, R>(self: Cell<I, A, E, R>, policy: Policy<A, E, R>): Cell<I, A, E, R>;
|
|
187
|
+
};
|
|
379
188
|
/**
|
|
380
|
-
* The
|
|
381
|
-
*
|
|
382
|
-
*
|
|
383
|
-
* each consumer because which phases are pure is this module's own fact, and a consumer
|
|
384
|
-
* that reconstructs it has to pick a proxy — inferring purity from the invocation shape,
|
|
385
|
-
* say — which is a different axis and silently disagrees the moment a pure phase is given
|
|
386
|
-
* an effectful shape or an impure one is not.
|
|
189
|
+
* The facts the lint plugin judges a spec body by, as a const table. The order the
|
|
190
|
+
* interpreter runs is the text of {@link layerRunner}; the table states only what a
|
|
191
|
+
* rule cannot read off a type: which phases are pure, and what counts as I/O.
|
|
387
192
|
*/
|
|
388
193
|
interface Vocabulary {
|
|
389
194
|
readonly module: typeof DESCRIPTION_MODULE;
|
|
390
195
|
readonly ioCells: IoCellClassification;
|
|
391
|
-
readonly
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
* belong to a description needs the phases *and* the applier; without it the applier is the one
|
|
396
|
-
* name it has to write down for itself, and one restated name is enough to drift.
|
|
397
|
-
*/
|
|
398
|
-
readonly applier: 'apply';
|
|
399
|
-
}
|
|
400
|
-
/**
|
|
401
|
-
* The bag the canonical description is built with. It is `Phases` with one member
|
|
402
|
-
* pinned: `decoded` is the canonical command class, because `canonicalDecide` is a
|
|
403
|
-
* decider over that class and a decider's parameter is contravariant — a phase typed
|
|
404
|
-
* `(decoded: unknown) => …` would demand that `unknown` be assignable to the command,
|
|
405
|
-
* which it is not. Every other member stays `unknown`, so nothing else narrows.
|
|
406
|
-
*/
|
|
407
|
-
interface CanonicalPhases extends Phases {
|
|
408
|
-
readonly decoded: CanonicalCommand;
|
|
196
|
+
readonly byKind: {
|
|
197
|
+
readonly pure: readonly PhaseName[];
|
|
198
|
+
};
|
|
199
|
+
readonly composer: 'layer';
|
|
409
200
|
}
|
|
410
|
-
/**
|
|
411
|
-
* A canonical description, built through the public constructors with phases that do
|
|
412
|
-
* nothing. It is exported so a consumer — a generator, a lint rule, a documenter — can
|
|
413
|
-
* obtain a real branded description without replaying the constructor chain: spread it
|
|
414
|
-
* and substitute its phase records' `run`s. The records it carries are the same literals
|
|
415
|
-
* the constructors write for real call sites, so the vocabulary below cannot drift from
|
|
416
|
-
* them.
|
|
417
|
-
*
|
|
418
|
-
* Its order is not a choice this module makes. The stage brands admit exactly one chain, so
|
|
419
|
-
* any other sequence fails to typecheck here — which is what keeps the derived order
|
|
420
|
-
* non-circular: it is read off a value, and the value's shape is enforced by the types.
|
|
421
|
-
*/
|
|
422
|
-
declare const canonical: WriteDone<CanonicalPhases>;
|
|
423
201
|
declare const vocabulary: Vocabulary;
|
|
424
|
-
declare namespace Policy_d_exports {
|
|
425
|
-
export { Policy };
|
|
426
|
-
}
|
|
427
|
-
type Policy<A, E, R> = (self: Effect<A, E, R>) => Effect<A, E, R>;
|
|
428
202
|
declare namespace Wire_d_exports {
|
|
429
203
|
export { AnyMinted, Fields, Mark, Minted, MintedField, mint, wire };
|
|
430
204
|
}
|
package/dist/index.mjs
CHANGED
|
@@ -1,311 +1,107 @@
|
|
|
1
1
|
import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
|
|
2
|
-
import * as Arr from "effect/Array";
|
|
3
2
|
import * as Effect$1 from "effect/Effect";
|
|
4
3
|
import { dual } from "effect/Function";
|
|
5
|
-
import * as Option from "effect/Option";
|
|
6
4
|
import * as Result$1 from "effect/Result";
|
|
7
|
-
import * as S from "effect/Schema";
|
|
8
5
|
import { Schema } from "effect";
|
|
9
|
-
|
|
6
|
+
//#region src/Facts.ts
|
|
7
|
+
const DESCRIPTION_MODULE = "@systemfsoftware/effect-cell-types";
|
|
8
|
+
const IO_CELLS = {
|
|
9
|
+
cells: ["store", "adapter"],
|
|
10
|
+
sources: ["effect/Clock", "effect/System"]
|
|
11
|
+
};
|
|
10
12
|
//#endregion
|
|
11
13
|
//#region src/Workflow.ts
|
|
12
|
-
var Workflow_exports = /* @__PURE__ */ __exportAll({ make: () => make });
|
|
13
|
-
|
|
14
|
-
* Builds a workflow from the command's schema class and a decider over that class's
|
|
15
|
-
* instance type, refusing an uninhabited or untagged channel at this call rather than at
|
|
16
|
-
* whoever first calls the result — which for a workflow nothing calls yet is never.
|
|
17
|
-
*
|
|
18
|
-
* The command is constrained on the **value**, not on a type parameter inferred from the
|
|
19
|
-
* decider's parameter. That is the whole mechanism. Any constraint on such a parameter is a
|
|
20
|
-
* structural predicate, and TypeScript cannot say "this type came from a class declaration"
|
|
21
|
-
* — so a marker placed there is a property, every property is declarable, and
|
|
22
|
-
* `interface Fake extends Marker {}` satisfies it. A declared type produces no value, so it
|
|
23
|
-
* cannot reach an argument position at all: there is no marker to smuggle because there is
|
|
24
|
-
* no marker.
|
|
25
|
-
*
|
|
26
|
-
* The three parameters mirror `Schema.Class`'s own bound exactly, and that is load-bearing.
|
|
27
|
-
* `Class<Self, S, Inherited>` places `S` in both covariant (`S["Type"]`) and contravariant
|
|
28
|
-
* (`S["fields"]`) positions, so it is invariant in `S`: every *fixed* spelling —
|
|
29
|
-
* `Class<unknown, Struct<Struct.Fields>, unknown>` and its variants — rejects real command
|
|
30
|
-
* classes. Generic over `S` accepts them and still refuses a `Struct`, which lacks
|
|
31
|
-
* `identifier` and `extend`. `Class<any, any, any>` also works and is banned here.
|
|
32
|
-
*
|
|
33
|
-
* `Schema.TaggedClass` returns this same `Class` interface, so one constraint covers both
|
|
34
|
-
* factories with no union. The import is type-only: this package gains no runtime dependency
|
|
35
|
-
* on Effect Schema, and `make` stays the identity function it always was.
|
|
36
|
-
*
|
|
37
|
-
* The markers ride the parameter function's return type, not the parameter as `Workflow<C, D, E>`:
|
|
38
|
-
* a conditional type in parameter position resolves `D` and `E` to `unknown` and the markers become
|
|
39
|
-
* unreachable. On the return type both still infer from the `Result` conjunct while the marker
|
|
40
|
-
* conjunct is what an uninhabited channel fails to satisfy.
|
|
41
|
-
*
|
|
42
|
-
* `E` carries no constraint on purpose. Constraining it gives inference a fallback: where `E` would
|
|
43
|
-
* infer as `never` TypeScript substitutes the constraint instead, the conditional takes its
|
|
44
|
-
* inhabited branch, and a `never` channel passes. The tagged requirement therefore lives in
|
|
45
|
-
* {@link Inhabited}, where nothing can stand in for `never`.
|
|
46
|
-
*
|
|
47
|
-
* The narrowing goes through an assertion signature rather than an `as` cast: every narrowing
|
|
48
|
-
* assertion trips `typescript(no-unsafe-type-assertion)`, and a suppression comment would hide the
|
|
49
|
-
* one place this file could lie. It is sound rather than merely permitted — with both channels
|
|
50
|
-
* inhabited `Workflow<Self, D, E>` is `(command: Self) => Result<D, E>` carrying the
|
|
51
|
-
* {@link WorkflowBrand} conjunct, and otherwise the return type is a marker with no call
|
|
52
|
-
* signature, so the value handed back is unobservable through it. The brand is applied here and
|
|
53
|
-
* nowhere else: the assertion adds no runtime property, yet a value that did not pass through this
|
|
54
|
-
* door fails the conjunct wherever a decision is run.
|
|
55
|
-
*/
|
|
56
|
-
const make = (_command, decide) => {
|
|
14
|
+
var Workflow_exports = /* @__PURE__ */ __exportAll({ make: () => make$1 });
|
|
15
|
+
const make$1 = (_command, decide) => {
|
|
57
16
|
return decide;
|
|
58
17
|
};
|
|
59
18
|
//#endregion
|
|
60
|
-
//#region src/CanonicalDecide.workflow.ts
|
|
61
|
-
/**
|
|
62
|
-
* The canonical command. `Workflow.make` constrains its first argument to a real
|
|
63
|
-
* schema class, so the canonical description needs one too — it carries no fields
|
|
64
|
-
* because the canonical's phases do nothing, and its only job is to be a genuine
|
|
65
|
-
* command value rather than a shape asserted into place.
|
|
66
|
-
*
|
|
67
|
-
* It is declared here rather than in a `*.schema.ts` because this is the owning
|
|
68
|
-
* single-segment `<stem>.workflow.ts`, which `schema-declaration-location` admits.
|
|
69
|
-
*/
|
|
70
|
-
var CanonicalCommand = class extends S.TaggedClass()("CanonicalCommand", {}) {};
|
|
71
|
-
/**
|
|
72
|
-
* The canonical decider. Extracted so `make-file-location` only sees it inside a
|
|
73
|
-
* single-segment `.workflow.ts` file, satisfying the restored taxonomy while
|
|
74
|
-
* preserving the exact phantom-channel contract the `DecidePhase` brand and
|
|
75
|
-
* the interpreter rely on.
|
|
76
|
-
*/
|
|
77
|
-
const canonicalDecide = make(CanonicalCommand, (_command) => Result$1.succeed(void 0));
|
|
78
|
-
//#endregion
|
|
79
19
|
//#region src/Cell.ts
|
|
80
20
|
var Cell_exports = /* @__PURE__ */ __exportAll({
|
|
21
|
+
CellTypeId: () => CellTypeId,
|
|
81
22
|
DESCRIPTION_MODULE: () => DESCRIPTION_MODULE,
|
|
82
23
|
IO_CELLS: () => IO_CELLS,
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
24
|
+
andThen: () => andThen,
|
|
25
|
+
layer: () => layer,
|
|
26
|
+
map: () => map,
|
|
27
|
+
mapInput: () => mapInput,
|
|
28
|
+
provide: () => provide,
|
|
29
|
+
run: () => run,
|
|
89
30
|
vocabulary: () => vocabulary,
|
|
90
|
-
|
|
31
|
+
withPolicy: () => withPolicy,
|
|
32
|
+
zip: () => zip
|
|
91
33
|
});
|
|
92
|
-
/** The description package's own module name — what an import edge would match. */
|
|
93
|
-
const DESCRIPTION_MODULE = "@systemfsoftware/effect-cell-types";
|
|
94
34
|
/**
|
|
95
|
-
* The
|
|
96
|
-
* sources whose calls are I/O. Written once here; a consumer folds it off the value.
|
|
35
|
+
* The nominal brand every `Cell` carries. `Cell.layer` is the only door that applies it.
|
|
97
36
|
*/
|
|
98
|
-
const
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
const DECODE_DONE = "call decode(raw) before decide(decoded)";
|
|
104
|
-
const DECIDE_DONE = "call decide(decoded) before encode(decision)";
|
|
105
|
-
const ENCODE_DONE = "call encode(decision) before write(output)";
|
|
106
|
-
const WRITE_DONE = "call write(output) before applying the description";
|
|
107
|
-
/** Replaces the open layer with itself plus one more phase record. */
|
|
108
|
-
const intoOpenLayer = (description, phase) => {
|
|
109
|
-
const layers = description.layers;
|
|
110
|
-
const last = layers[layers.length - 1];
|
|
111
|
-
return {
|
|
112
|
-
module: description.module,
|
|
113
|
-
ioCells: description.ioCells,
|
|
114
|
-
layers: [...layers.slice(0, -1), {
|
|
115
|
-
...last,
|
|
116
|
-
phases: [...last?.phases ?? [], phase]
|
|
117
|
-
}]
|
|
118
|
-
};
|
|
119
|
-
};
|
|
37
|
+
const CellTypeId = Symbol.for("@systemfsoftware/effect-cell-types/Cell");
|
|
38
|
+
const make = (run) => ({
|
|
39
|
+
[CellTypeId]: CellTypeId,
|
|
40
|
+
run
|
|
41
|
+
});
|
|
120
42
|
/**
|
|
121
|
-
*
|
|
122
|
-
*
|
|
123
|
-
*
|
|
124
|
-
*
|
|
125
|
-
* This one is not dual: it starts the chain, so on the opening layer it has no `self` to
|
|
126
|
-
* receive. Every phase after it is dual, which is what lets a description be written in the
|
|
127
|
-
* order it runs.
|
|
43
|
+
* The interpreter. Order is the text: read, then decode, then decide, then encode, then
|
|
44
|
+
* write. The `E` channel is the sandwich's truth — read, decode, and write failures;
|
|
45
|
+
* a decide refusal is the outcome the encode and write receive, not a failure.
|
|
128
46
|
*/
|
|
129
|
-
const
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
}
|
|
139
|
-
|
|
47
|
+
const layerRunner = (spec) => {
|
|
48
|
+
if ("decode" in spec && "encode" in spec) return (input) => Effect$1.gen(function* () {
|
|
49
|
+
const raw = yield* spec.read(input);
|
|
50
|
+
const decoded = yield* Result$1.match(spec.decode(raw), {
|
|
51
|
+
onFailure: Effect$1.fail,
|
|
52
|
+
onSuccess: Effect$1.succeed
|
|
53
|
+
});
|
|
54
|
+
const outcome = spec.decide(decoded);
|
|
55
|
+
return yield* spec.write(spec.encode(outcome), raw);
|
|
56
|
+
});
|
|
57
|
+
return (input) => Effect$1.gen(function* () {
|
|
58
|
+
const raw = yield* spec.read(input);
|
|
59
|
+
const outcome = spec.decide(raw);
|
|
60
|
+
return yield* spec.write(outcome, raw);
|
|
61
|
+
});
|
|
62
|
+
};
|
|
63
|
+
function layer(spec) {
|
|
64
|
+
return make(layerRunner(spec));
|
|
65
|
+
}
|
|
66
|
+
const run = dual(2, (self, input) => self.run(input));
|
|
140
67
|
/**
|
|
141
|
-
*
|
|
142
|
-
* reads innermost-first — backwards from the order the phases run — which defeats the point of
|
|
143
|
-
* a type that exists to make that order legible. In `pipe` the call site reads in phase order,
|
|
144
|
-
* and the sentence still arrives as a missing member through it.
|
|
145
|
-
*
|
|
146
|
-
* A `Do`-notation scope binding each phase's result for later phases to read was measured as
|
|
147
|
-
* an alternative: the sentence survives it, even with the scope varying per stage, because an
|
|
148
|
-
* absent member is reported before type arguments are compared. It was not adopted, because a
|
|
149
|
-
* scope the interpreter folds over is type-erased, and reading a phase back out of it needs an
|
|
150
|
-
* assertion this design does not.
|
|
68
|
+
* Transforms the Cell's response.
|
|
151
69
|
*/
|
|
152
|
-
const
|
|
153
|
-
[DECODE_DONE]: true,
|
|
154
|
-
...intoOpenLayer(previous, {
|
|
155
|
-
name: "decode",
|
|
156
|
-
kind: "pure",
|
|
157
|
-
convention: "either-fail",
|
|
158
|
-
run
|
|
159
|
-
})
|
|
160
|
-
}));
|
|
161
|
-
const decide = dual(2, (previous, run) => ({
|
|
162
|
-
[DECIDE_DONE]: true,
|
|
163
|
-
...intoOpenLayer(previous, {
|
|
164
|
-
name: "decide",
|
|
165
|
-
kind: "pure",
|
|
166
|
-
convention: "either-pass",
|
|
167
|
-
run
|
|
168
|
-
})
|
|
169
|
-
}));
|
|
170
|
-
const encode = dual(2, (previous, run) => ({
|
|
171
|
-
[ENCODE_DONE]: true,
|
|
172
|
-
...intoOpenLayer(previous, {
|
|
173
|
-
name: "encode",
|
|
174
|
-
kind: "pure",
|
|
175
|
-
convention: "total",
|
|
176
|
-
run
|
|
177
|
-
})
|
|
178
|
-
}));
|
|
179
|
-
const write = dual(2, (previous, run) => ({
|
|
180
|
-
[WRITE_DONE]: true,
|
|
181
|
-
...intoOpenLayer(previous, {
|
|
182
|
-
name: "write",
|
|
183
|
-
kind: "impure",
|
|
184
|
-
convention: "effect",
|
|
185
|
-
run
|
|
186
|
-
})
|
|
187
|
-
}));
|
|
70
|
+
const map = dual(2, (self, f) => make((input) => Effect$1.map(self.run(input), f)));
|
|
188
71
|
/**
|
|
189
|
-
*
|
|
190
|
-
* phase is chained only after a decide, so the value reaching it is the outcome `Result`
|
|
191
|
-
* and `Result.isResult` certifies exactly that. The specific `decision`/`decisionError`
|
|
192
|
-
* members are not observable at runtime, so the guard narrows to them by construction —
|
|
193
|
-
* the same trust the fold places in the chain's order.
|
|
72
|
+
* Transforms the Cell's input.
|
|
194
73
|
*/
|
|
195
|
-
const
|
|
74
|
+
const mapInput = dual(2, (self, f) => make((input) => self.run(f(input))));
|
|
196
75
|
/**
|
|
197
|
-
*
|
|
198
|
-
*
|
|
199
|
-
* the order is the `phases` array and the invocation shape is the `convention` field,
|
|
200
|
-
* so a description's declared order IS its execution order. The convention switch is
|
|
201
|
-
* exhaustive over the union via its `never` default: a future phase with an invocation
|
|
202
|
-
* shape this module does not know fails at compile time, at this one location.
|
|
203
|
-
*
|
|
204
|
-
* The two `Failure` rules are carried by the phase types rather than chosen here. A `decode`
|
|
205
|
-
* Failure has no downstream consumer — nothing accepts `decodeError` — so its only route is a
|
|
206
|
-
* failure, which is what puts it in the derived error channel. A `decide` Failure cannot be
|
|
207
|
-
* unwrapped, because `EncodePhase` takes the whole `Result`, so its only route is forward as
|
|
208
|
-
* a value. Neither is a decision the interpreter makes.
|
|
209
|
-
*
|
|
210
|
-
* Every layer reachable from a `WriteDone` was built by the five constructors in order, so
|
|
211
|
-
* its last phase is a write and every slot is filled. A layer that is nonetheless empty or
|
|
212
|
-
* not closed by a write is a defect in this module, never a domain outcome, so it dies —
|
|
213
|
-
* the same guard the name-keyed layer used for unfilled slots. `Effect.die` returns
|
|
214
|
-
* `Effect<never>`, which is why the guards cost the derived `E` and `R` nothing.
|
|
76
|
+
* Feeds this Cell's response to the next Cell as its input. The error and service channels
|
|
77
|
+
* union.
|
|
215
78
|
*/
|
|
216
|
-
const
|
|
217
|
-
const phases = layer.phases;
|
|
218
|
-
const last = phases[phases.length - 1];
|
|
219
|
-
if (!last || last.name !== "write") return yield* Effect$1.die(/* @__PURE__ */ new Error("effect-cell-types: a layer reached the interpreter without a write phase closing it"));
|
|
220
|
-
let value = command;
|
|
221
|
-
let raw = command;
|
|
222
|
-
for (const phase of phases.slice(0, -1)) switch (phase.convention) {
|
|
223
|
-
case "effect":
|
|
224
|
-
if (phase.name === "read") {
|
|
225
|
-
value = yield* phase.run(value);
|
|
226
|
-
raw = value;
|
|
227
|
-
break;
|
|
228
|
-
}
|
|
229
|
-
value = yield* phase.run(value, raw);
|
|
230
|
-
break;
|
|
231
|
-
case "either-fail":
|
|
232
|
-
value = yield* Result$1.match(phase.run(value), {
|
|
233
|
-
onFailure: Effect$1.fail,
|
|
234
|
-
onSuccess: Effect$1.succeed
|
|
235
|
-
});
|
|
236
|
-
break;
|
|
237
|
-
case "either-pass":
|
|
238
|
-
value = phase.run(value);
|
|
239
|
-
break;
|
|
240
|
-
case "total":
|
|
241
|
-
if (!isOutcome(value)) return yield* Effect$1.die(/* @__PURE__ */ new Error("effect-cell-types: an encode phase received a value that is not the decide outcome"));
|
|
242
|
-
value = phase.run(value);
|
|
243
|
-
break;
|
|
244
|
-
default: {
|
|
245
|
-
const unreachable = phase;
|
|
246
|
-
return yield* Effect$1.die(/* @__PURE__ */ new Error(`effect-cell-types: unknown phase convention ${String(unreachable)}`));
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
return yield* last.run(value, raw);
|
|
250
|
-
});
|
|
79
|
+
const andThen = dual(2, (self, that) => make((input) => Effect$1.flatMap(self.run(input), (response) => that.run(response))));
|
|
251
80
|
/**
|
|
252
|
-
*
|
|
253
|
-
*
|
|
254
|
-
* unrepresentable rather than merely discouraged. Annotating it here would let this module
|
|
255
|
-
* promise a failure that no phase can produce.
|
|
256
|
-
*
|
|
257
|
-
* The parameter keeps the terminal `WriteDone<P>` brand. It is not decoration: it is what
|
|
258
|
-
* makes applying a half-built chain — a `ReadDone`, say — a compile error rather than a
|
|
259
|
-
* runtime death, which `test-types/Cell.tst.ts` pins. The brand does not constrain the
|
|
260
|
-
* phases array's order (a literal satisfies it in any order, which is why the interpreter
|
|
261
|
-
* reads the order off the value), but it does constrain chain completion, and that is a
|
|
262
|
-
* guarantee worth the narrower parameter.
|
|
263
|
-
*
|
|
264
|
-
* `forEach` takes no concurrency option here, so the layers run in declared order and the
|
|
265
|
-
* sequence is structural rather than something a caller could pass differently; the
|
|
266
|
-
* description's response is the last layer's. No scope is opened and interruptibility is
|
|
267
|
-
* untouched, so a `Scope.Scope` a phase requires reaches the caller as part of the derived `R`.
|
|
81
|
+
* Runs both Cells against the same input and tuples the responses. Fails fast: when one
|
|
82
|
+
* side refuses, the other's write never runs.
|
|
268
83
|
*/
|
|
269
|
-
const
|
|
270
|
-
const responses = yield* Effect$1.forEach(description.layers, (layer) => runLayer(layer, command));
|
|
271
|
-
return yield* Option.match(Arr.last(responses), {
|
|
272
|
-
onNone: () => Effect$1.die(/* @__PURE__ */ new Error("effect-cell-types: a description reached the interpreter with no layers")),
|
|
273
|
-
onSome: Effect$1.succeed
|
|
274
|
-
});
|
|
275
|
-
});
|
|
84
|
+
const zip = dual(2, (self, that) => make((input) => Effect$1.zipWith(self.run(input), that.run(input), (a, b) => [a, b])));
|
|
276
85
|
/**
|
|
277
|
-
*
|
|
278
|
-
*
|
|
279
|
-
*
|
|
280
|
-
* and substitute its phase records' `run`s. The records it carries are the same literals
|
|
281
|
-
* the constructors write for real call sites, so the vocabulary below cannot drift from
|
|
282
|
-
* them.
|
|
283
|
-
*
|
|
284
|
-
* Its order is not a choice this module makes. The stage brands admit exactly one chain, so
|
|
285
|
-
* any other sequence fails to typecheck here — which is what keeps the derived order
|
|
286
|
-
* non-circular: it is read off a value, and the value's shape is enforced by the types.
|
|
86
|
+
* Provides a Layer to the Cell, eliminating the services the layer builds from `R`. This is
|
|
87
|
+
* the one composition-root elimination; the resulting Cell still demands the layer's input
|
|
88
|
+
* services. A missing provide is a compile error at the run site.
|
|
287
89
|
*/
|
|
288
|
-
const
|
|
90
|
+
const provide = dual(2, (self, layer) => make((input) => Effect$1.provide(self.run(input), layer)));
|
|
289
91
|
/**
|
|
290
|
-
*
|
|
291
|
-
*
|
|
292
|
-
* from here instead of restating them, so there is one place a phase is described and the
|
|
293
|
-
* description is the place.
|
|
92
|
+
* Wraps the Cell's run in a `Policy` — retry, timeout, and their kin — preserving every
|
|
93
|
+
* channel.
|
|
294
94
|
*/
|
|
295
|
-
const
|
|
296
|
-
convention,
|
|
297
|
-
kind,
|
|
298
|
-
name
|
|
299
|
-
})));
|
|
95
|
+
const withPolicy = dual(2, (self, policy) => make((input) => policy(self.run(input))));
|
|
300
96
|
const vocabulary = {
|
|
301
|
-
module:
|
|
302
|
-
ioCells:
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
},
|
|
308
|
-
|
|
97
|
+
module: DESCRIPTION_MODULE,
|
|
98
|
+
ioCells: IO_CELLS,
|
|
99
|
+
byKind: { pure: [
|
|
100
|
+
"decode",
|
|
101
|
+
"decide",
|
|
102
|
+
"encode"
|
|
103
|
+
] },
|
|
104
|
+
composer: "layer"
|
|
309
105
|
};
|
|
310
106
|
//#endregion
|
|
311
107
|
//#region src/Policy.ts
|
|
@@ -348,8 +144,10 @@ var Wire_exports = /* @__PURE__ */ __exportAll({
|
|
|
348
144
|
* the schema library can express, including a vendor's own schema — deliberately.
|
|
349
145
|
*/
|
|
350
146
|
const mint = (field) => {
|
|
147
|
+
assertMinted(field);
|
|
351
148
|
return field;
|
|
352
149
|
};
|
|
150
|
+
function assertMinted(_field) {}
|
|
353
151
|
const wire = (fields) => mint(Schema.Struct(fields));
|
|
354
152
|
//#endregion
|
|
355
153
|
export { Cell_exports as Cell, Policy_exports as Policy, Wire_exports as Wire, 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": "6.0.0",
|
|
5
5
|
"author": "Ryan Lee <drdgvhbh@gmail.com>",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
},
|
|
11
11
|
"homepage": "https://github.com/systemfsoftware/systemfsoftware/tree/main/packages/core/effect/cell/types#readme",
|
|
12
12
|
"bugs": "https://github.com/systemfsoftware/systemfsoftware/issues",
|
|
13
|
-
"description": "Type-level contracts for the repo's cell taxonomy, starting with the Workflow decision channel — branded Workflow<C, D, E> types plus a runtime make constructor
|
|
13
|
+
"description": "Type-level contracts for the repo's cell taxonomy, starting with the Workflow decision channel — branded Workflow<C, D, E> types plus a runtime make constructor shared across the repo's cells.",
|
|
14
14
|
"keywords": [
|
|
15
15
|
"effect",
|
|
16
16
|
"effect-ts",
|