@systemfsoftware/effect-cell-types 3.0.0 → 4.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 CHANGED
@@ -1,5 +1,28 @@
1
1
  # @systemfsoftware/effect-cell-types
2
2
 
3
+ ## 4.0.0
4
+
5
+ ### Major Changes
6
+
7
+ - `Workflow.make` now takes two arguments: the command's schema class first, the decider second.
8
+
9
+ To migrate, declare the command as a `Schema.Class` or `Schema.TaggedClass` and pass it first. The decider's parameter type is inferred from that class, so its annotation can be dropped. A plain interface, a type alias, an object literal, a `Schema.Struct` and a primitive are all refused at the command position — an interface produces no value to pass, and the others are not schema classes.
10
+
11
+ The decider's own contract is unchanged: it still returns a `Result`, and a decision channel of `never`, an error channel of `never`, or an error channel carrying no tag are refused as before.
12
+
13
+ - `Tagged` no longer ships in the `Workflow` namespace.
14
+
15
+ The requirement it expressed is unchanged: a decision error must still carry a tag the consumer can
16
+ dispatch on, and `UntaggedError` still names the failure when one does not. Replace an annotation
17
+ that referred to `Workflow.Tagged` with the concrete error type, or with a `Schema.TaggedError`
18
+ class, which satisfies the channel directly.
19
+
20
+ ### Patch Changes
21
+
22
+ - The peer requirements for `effect` and for the Effect test-runner integration now accept any compatible `4.0.0-rc` release, instead of demanding one exact release candidate.
23
+
24
+ Installing alongside a newer release candidate no longer reports an unmet peer dependency or resolves a second copy of `effect` into the dependency tree.
25
+
3
26
  ## 3.0.0
4
27
 
5
28
  ### Major Changes
package/README.md CHANGED
@@ -23,22 +23,28 @@ can satisfy, so the mistake is a compile error with the remediation attached (be
23
23
 
24
24
  ## The constructor
25
25
 
26
- Executors build a workflow from a plain decider with `make` — runtime identity, one cast across the branded return:
26
+ Executors build a workflow from the command's schema class and a decider over that class — runtime identity, one assertion across the branded return:
27
27
 
28
28
  ```ts
29
29
  import { make } from '@systemfsoftware/effect-cell-types'
30
30
  import { Result } from 'effect'
31
+ import * as S from 'effect/Schema'
32
+
33
+ export class DecideInput extends S.Class<DecideInput>('DecideInput')({
34
+ exitSuccess: S.Boolean,
35
+ }) {}
31
36
 
32
- export const decide = make<DecideInput, RestartDecision, RestartDecisionExhausted>(
33
- (
34
- input,
35
- ) => (input.exitSuccess
37
+ export const decide = make(
38
+ DecideInput,
39
+ (input) => (input.exitSuccess
36
40
  ? Result.succeed(new RestartDecisionContinue())
37
41
  : Result.fail(new RestartDecisionExhausted())),
38
42
  )
39
43
  ```
40
44
 
41
- The parameter is the plain function type, not `Workflow<C, D, E>`: the `never`-channel conditional lives on the **return** type, so a total decision (`Result<Decision, never>`) resolves to `UninhabitedError` and the call site fails with "This expression is not callable", while a `Promise`- or bare-value-returning decider is rejected at the argument. `make` is a runtime value, so consumers need it as an ordinary import only where they construct workflows; everywhere else `import type` still erases at compile time.
45
+ The command is constrained on the **value**, not on a type parameter inferred from the decider's parameter, and that is the whole mechanism. A constraint on such a parameter is a structural predicate, and TypeScript cannot express "this type came from a class declaration" so a marker placed there is just a property, and `interface Fake extends Marker {}` satisfies it. A declared type produces no value, so it cannot reach an argument position at all: an interface at the command position is refused with "only refers to a type, but is being used as a value here". `Schema.Class` and `Schema.TaggedClass` are both accepted; a `Schema.Struct`, a plain class, an object literal and a primitive are each refused.
46
+
47
+ No type argument needs writing: the command type comes from the class, so the decider's parameter needs no annotation. The `never`-channel conditional still lives on the **return** type, so a total decision (`Result<Decision, never>`) resolves to `UninhabitedError` and the call site fails with "This expression is not callable", while a `Promise`- or bare-value-returning decider is rejected at the argument. `make` is a runtime value, so consumers need it as an ordinary import only where they construct workflows; everywhere else `import type` still erases at compile time.
42
48
 
43
49
  ## Worked example
44
50
 
@@ -92,7 +98,8 @@ const restartIndicesFor = (
92
98
  )
93
99
 
94
100
  export const decideRestart = Workflow.make(
95
- (input: DecideInput): Result.Result<
101
+ DecideInput,
102
+ (input): Result.Result<
96
103
  RestartDecisionContinue | RestartDecisionRestart,
97
104
  RestartDecisionExhausted
98
105
  > =>
@@ -113,25 +120,28 @@ export const decideRestart = Workflow.make(
113
120
  )
114
121
  ```
115
122
 
116
- The shape to copy: one exported decision built by `Workflow.make`, whose body returns
117
- `Result` values via `Result.succeed` and `Result.fail`. The constructor infers the channels
118
- from the annotated return and is the only door to the `WorkflowBrand` conjunct — annotating
119
- a function `Workflow<…>` directly is now refused wherever the brand is demanded, because a
120
- workflow that never passed through `make` is not a decision anything may run. The error
121
- channel is a real variant (`RestartDecisionExhausted`) giving up is a decision the caller
122
- must branch on, so declaring the error channel `never` is rejected, not allowed.
123
+ The shape to copy: one exported decision built by `Workflow.make`, taking the command's
124
+ schema class and a decider over that class, whose body returns `Result` values via
125
+ `Result.succeed` and `Result.fail`. The command channel comes from the class, the decision
126
+ and error channels are inferred from the annotated return, and `make` is the only door to
127
+ the `WorkflowBrand` conjunct annotating a function `Workflow<…>` directly is still refused
128
+ wherever the brand is demanded, because a workflow that never passed through `make` is not a
129
+ decision anything may run. The error channel is a real variant (`RestartDecisionExhausted`) —
130
+ giving up is a decision the caller must branch on, so declaring the error channel `never` is
131
+ rejected, not allowed.
123
132
 
124
133
  ## What it rejects at compile time
125
134
 
126
- All five violations fail `tsc`; the messages below are what `tsc` reports (verified against this package and `effect@4.0.0-rc.108`).
135
+ All six violations fail `tsc`; the messages below are what `tsc` reports (verified against this package and `effect@4.0.0-rc.108`).
127
136
 
128
- | Violation | `tsc` reports | Why it is rejected |
129
- | -------------------------------------- | ----------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- |
130
- | 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 |
131
- | 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 |
132
- | `never` decision channel | `Type '...' is not assignable to type 'UninhabitedDecision'` | a workflow that can never produce a decision can never succeed |
133
- | `never` error channel | `Type '...' is not assignable to type 'UninhabitedError'` | a workflow that cannot fail decides nothing; move it to a `*.kernel.ts` |
134
- | A bare decider handed to `Cell.decide` | `Type '(command: Cmd) => Result<Dec, Err>' is not assignable to type 'WorkflowBrand'` | only a `Workflow.make` value satisfies `DecidePhase`; a lambda that skipped `make` is not a decision a description may run |
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; move it to a `*.kernel.ts` |
143
+ | A bare decider handed to `Cell.decide` | `Type '(command: Cmd) => Result<Dec, Err>' is not assignable to type 'WorkflowBrand'` | only a `Workflow.make` value satisfies `DecidePhase`; a lambda that skipped `make` is not a decision a description may run |
144
+ | 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 |
135
145
 
136
146
  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:
137
147
 
@@ -159,17 +169,17 @@ the outcome once:
159
169
  import { Workflow } from '@systemfsoftware/effect-cell-types'
160
170
  import { Result } from 'effect'
161
171
  import * as Match from 'effect/Match'
172
+ import * as S from 'effect/Schema'
162
173
 
163
174
  class Decision {}
164
175
  class Err {
165
176
  constructor(readonly reason: string) {}
166
177
  }
167
- interface Input {
168
- readonly valid: boolean
169
- }
178
+ class Input extends S.Class<Input>('Input')({ valid: S.Boolean }) {}
170
179
 
171
180
  const decide = Workflow.make(
172
- (input: Input): Result.Result<Decision, Err> =>
181
+ Input,
182
+ (input): Result.Result<Decision, Err> =>
173
183
  Result.gen(function*() {
174
184
  const outcome = Match.value(input).pipe(
175
185
  Match.when({ valid: false }, () => Result.fail(new Err('invalid input'))),
@@ -192,7 +202,8 @@ type — so an unreachable error channel is rejected rather than silently allowe
192
202
 
193
203
  ```ts
194
204
  const decide = Workflow.make(
195
- (input: Input): Result.Result<Decision, Err> =>
205
+ Input,
206
+ (input): Result.Result<Decision, Err> =>
196
207
  Result.gen(function*() {
197
208
  return new Decision()
198
209
  }),
package/dist/index.d.ts CHANGED
@@ -2,9 +2,10 @@ 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";
5
6
  import { Schema, SchemaAST } from "effect";
6
7
  declare namespace Workflow_d_exports {
7
- export { Inhabited, Tagged, UninhabitedDecision, UninhabitedError, UntaggedError, Workflow, WorkflowBrand, make };
8
+ export { Inhabited, UninhabitedDecision, UninhabitedError, UntaggedError, Workflow, WorkflowBrand, make };
8
9
  }
9
10
  /**
10
11
  * The nominal brand a workflow carries. `Workflow.make` is the only door that applies it,
@@ -40,10 +41,6 @@ interface UninhabitedError {
40
41
  interface UntaggedError {
41
42
  readonly __WORKFLOW_ERROR_CHANNEL_CARRIES_NO_TAG__: 'this error carries no _tag the consumer can dispatch on; declare it as an S.TaggedError';
42
43
  }
43
- /** The shape an error channel must have: a tag the consumer dispatches on. */
44
- interface Tagged {
45
- readonly _tag: string;
46
- }
47
44
  /**
48
45
  * A decider whose channels are both inhabited, or the marker naming which channel is not.
49
46
  *
@@ -56,15 +53,48 @@ interface Tagged {
56
53
  */
57
54
  type Workflow<Command, Decision, DecisionError> = [Decision] extends [never] ? UninhabitedDecision : [DecisionError] extends [never] ? UninhabitedError : ((command: Command) => Result<Decision, DecisionError>) & WorkflowBrand;
58
55
  /**
59
- * `unknown` when both channels are inhabited and the error carries a tag, so the intersection in
60
- * {@link make} collapses to the plain `Result` and neither inference nor the authoring surface
61
- * changes. Otherwise the marker the author must satisfy, which they cannot, which is the point.
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
+ 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.
62
72
  */
63
- type Inhabited<Decision, DecisionError> = [Decision] extends [never] ? UninhabitedDecision : [DecisionError] extends [never] ? UninhabitedError : [DecisionError] extends [Tagged] ? unknown : UntaggedError;
73
+ type Inhabited<Decision, DecisionError> = [Decision] extends [never] ? UninhabitedDecision : [DecisionError] extends [never] ? UninhabitedError : DispatchableTag<DecisionError>;
64
74
  /**
65
- * Builds a workflow, refusing an uninhabited or untagged channel at this call rather than at
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
66
77
  * whoever first calls the result — which for a workflow nothing calls yet is never.
67
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
+ *
68
98
  * The markers ride the parameter function's return type, not the parameter as `Workflow<C, D, E>`:
69
99
  * a conditional type in parameter position resolves `D` and `E` to `unknown` and the markers become
70
100
  * unreachable. On the return type both still infer from the `Result` conjunct while the marker
@@ -78,13 +108,28 @@ type Inhabited<Decision, DecisionError> = [Decision] extends [never] ? Uninhabit
78
108
  * The narrowing goes through an assertion signature rather than an `as` cast: every narrowing
79
109
  * assertion trips `typescript(no-unsafe-type-assertion)`, and a suppression comment would hide the
80
110
  * one place this file could lie. It is sound rather than merely permitted — with both channels
81
- * inhabited `Workflow<C, D, E>` is `(command: C) => Result<D, E>` carrying the {@link WorkflowBrand}
82
- * conjunct, and otherwise the return type is a marker with no call signature, so the value handed
83
- * back is unobservable through it. The brand is applied here and nowhere else: the assertion adds
84
- * no runtime property, so `make` stays the identity function it always was, yet a value that did
85
- * not pass through this door fails the conjunct wherever a decision is run.
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.
86
116
  */
87
- declare const make: <C, D, E>(decide: (command: C) => Result<D, E> & Inhabited<D, E>) => Workflow<C, D, E>;
117
+ declare const make: <Self, S extends Schema$1.Constraint & {
118
+ readonly fields: Schema$1.Struct.Fields;
119
+ }, 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 {}
88
133
  declare namespace Cell_d_exports {
89
134
  export { Convention, DESCRIPTION_MODULE, DecideDone, DecideNode, DecidePhase, DecodeDone, DecodeNode, DecodePhase, Description, EncodeDone, EncodeNode, EncodePhase, IO_CELLS, IoCellClassification, Layer, Phase, PhaseFact, Phases, ReadDone, ReadNode, ReadPhase, Vocabulary, WriteDone, WriteNode, WritePhase, apply, canonical, decide, decode, encode, read, vocabulary, write };
90
135
  }
@@ -333,6 +378,16 @@ interface Vocabulary {
333
378
  */
334
379
  readonly applier: 'apply';
335
380
  }
381
+ /**
382
+ * The bag the canonical description is built with. It is `Phases` with one member
383
+ * pinned: `decoded` is the canonical command class, because `canonicalDecide` is a
384
+ * decider over that class and a decider's parameter is contravariant — a phase typed
385
+ * `(decoded: unknown) => …` would demand that `unknown` be assignable to the command,
386
+ * which it is not. Every other member stays `unknown`, so nothing else narrows.
387
+ */
388
+ interface CanonicalPhases extends Phases {
389
+ readonly decoded: CanonicalCommand;
390
+ }
336
391
  /**
337
392
  * A canonical description, built through the public constructors with phases that do
338
393
  * nothing. It is exported so a consumer — a generator, a lint rule, a documenter — can
@@ -345,7 +400,7 @@ interface Vocabulary {
345
400
  * any other sequence fails to typecheck here — which is what keeps the derived order
346
401
  * non-circular: it is read off a value, and the value's shape is enforced by the types.
347
402
  */
348
- declare const canonical: WriteDone<Phases>;
403
+ declare const canonical: WriteDone<CanonicalPhases>;
349
404
  declare const vocabulary: Vocabulary;
350
405
  declare namespace Policy_d_exports {
351
406
  export { Policy };
package/dist/index.mjs CHANGED
@@ -4,13 +4,36 @@ import * as Effect$1 from "effect/Effect";
4
4
  import { dual } from "effect/Function";
5
5
  import * as Option from "effect/Option";
6
6
  import * as Result$1 from "effect/Result";
7
+ import * as S from "effect/Schema";
7
8
  import { Schema } from "effect";
9
+ S.TaggedError()("CanonicalDecideError", {});
10
+ //#endregion
8
11
  //#region src/Workflow.ts
9
12
  var Workflow_exports = /* @__PURE__ */ __exportAll({ make: () => make });
10
13
  /**
11
- * Builds a workflow, refusing an uninhabited or untagged channel at this call rather than at
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
12
16
  * whoever first calls the result — which for a workflow nothing calls yet is never.
13
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
+ *
14
37
  * The markers ride the parameter function's return type, not the parameter as `Workflow<C, D, E>`:
15
38
  * a conditional type in parameter position resolves `D` and `E` to `unknown` and the markers become
16
39
  * unreachable. On the return type both still infer from the `Result` conjunct while the marker
@@ -24,24 +47,34 @@ var Workflow_exports = /* @__PURE__ */ __exportAll({ make: () => make });
24
47
  * The narrowing goes through an assertion signature rather than an `as` cast: every narrowing
25
48
  * assertion trips `typescript(no-unsafe-type-assertion)`, and a suppression comment would hide the
26
49
  * one place this file could lie. It is sound rather than merely permitted — with both channels
27
- * inhabited `Workflow<C, D, E>` is `(command: C) => Result<D, E>` carrying the {@link WorkflowBrand}
28
- * conjunct, and otherwise the return type is a marker with no call signature, so the value handed
29
- * back is unobservable through it. The brand is applied here and nowhere else: the assertion adds
30
- * no runtime property, so `make` stays the identity function it always was, yet a value that did
31
- * not pass through this door fails the conjunct wherever a decision is run.
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.
32
55
  */
33
- const make = (decide) => {
56
+ const make = (_command, decide) => {
34
57
  return decide;
35
58
  };
36
59
  //#endregion
37
60
  //#region src/CanonicalDecide.workflow.ts
38
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
+ /**
39
72
  * The canonical decider. Extracted so `make-file-location` only sees it inside a
40
73
  * single-segment `.workflow.ts` file, satisfying the restored taxonomy while
41
74
  * preserving the exact phantom-channel contract the `DecidePhase` brand and
42
75
  * the interpreter rely on.
43
76
  */
44
- const canonicalDecide = make((_decoded) => Result$1.succeed(void 0));
77
+ const canonicalDecide = make(CanonicalCommand, (_command) => Result$1.succeed(void 0));
45
78
  //#endregion
46
79
  //#region src/Cell.ts
47
80
  var Cell_exports = /* @__PURE__ */ __exportAll({
@@ -246,7 +279,7 @@ const apply = (description, command) => Effect$1.gen(function* () {
246
279
  * any other sequence fails to typecheck here — which is what keeps the derived order
247
280
  * non-circular: it is read off a value, and the value's shape is enforced by the types.
248
281
  */
249
- const canonical = write(encode(decide(decode(read(() => Effect$1.void), () => Result$1.succeed(void 0)), canonicalDecide), () => void 0), () => Effect$1.void);
282
+ const canonical = write(encode(decide(decode(read(() => Effect$1.void), () => Result$1.succeed(CanonicalCommand.make({}))), canonicalDecide), () => void 0), () => Effect$1.void);
250
283
  /**
251
284
  * The phase vocabulary, obtained by walking `canonical`. A consumer that needs the phase
252
285
  * names, their purity, or their order — a lint rule, a generator, a document — reads them
@@ -324,10 +357,8 @@ var Wire_exports = /* @__PURE__ */ __exportAll({
324
357
  * preserved, so a marked struct stays a struct and Effect's inference keeps working.
325
358
  */
326
359
  const mint = (field) => {
327
- assertMinted(field);
328
360
  return field;
329
361
  };
330
- function assertMinted(_field) {}
331
362
  const string = mint(Schema.String);
332
363
  const number = mint(Schema.Finite);
333
364
  const boolean = mint(Schema.Boolean);
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": "3.0.0",
4
+ "version": "4.0.0",
5
5
  "author": "Ryan Lee <drdgvhbh@gmail.com>",
6
6
  "repository": {
7
7
  "type": "git",
@@ -32,23 +32,23 @@
32
32
  "dist"
33
33
  ],
34
34
  "devDependencies": {
35
- "@effect/vitest": "4.0.0-rc.108",
35
+ "@effect/vitest": "4.0.0-rc.111",
36
36
  "@microsoft/api-extractor": "^7.58.7",
37
37
  "@systemfsoftware/arethetypeswrong-cli": "^1.1.1",
38
38
  "@types/node": "^24",
39
- "effect": "4.0.0-rc.108",
39
+ "effect": "4.0.0-rc.111",
40
40
  "oxlint": "^1.77.0",
41
41
  "rimraf": "^6.1.3",
42
42
  "tsdown": "^0.22.14",
43
43
  "tstyche": "^7.1.0",
44
44
  "vitest": "^4.1.10",
45
- "@systemfsoftware/effect-gherkin-spec": "2.0.0",
45
+ "@systemfsoftware/effect-gherkin-spec": "2.0.1",
46
+ "@systemfsoftware/oxlint-config": "^0.1.0",
46
47
  "@systemfsoftware/vitest-config": "^0.1.0",
47
- "@systemfsoftware/tsconfig": "^1.3.3",
48
- "@systemfsoftware/oxlint-config": "^0.1.0"
48
+ "@systemfsoftware/tsconfig": "^1.3.3"
49
49
  },
50
50
  "peerDependencies": {
51
- "effect": "4.0.0-rc.108"
51
+ "effect": "^4.0.0-rc.111"
52
52
  },
53
53
  "publishConfig": {
54
54
  "provenance": true