@barnum/barnum 0.3.0 → 0.4.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/artifacts/linux-arm64/barnum +0 -0
- package/artifacts/linux-x64/barnum +0 -0
- package/artifacts/macos-arm64/barnum +0 -0
- package/artifacts/macos-x64/barnum +0 -0
- package/artifacts/win-x64/barnum.exe +0 -0
- package/dist/all.d.ts +41 -10
- package/dist/all.d.ts.map +1 -0
- package/dist/all.js +1 -1
- package/dist/ast.d.ts +199 -98
- package/dist/ast.d.ts.map +1 -0
- package/dist/ast.js +271 -233
- package/dist/bind.d.ts +9 -12
- package/dist/bind.d.ts.map +1 -0
- package/dist/bind.js +14 -51
- package/dist/builtins/array.d.ts +36 -0
- package/dist/builtins/array.d.ts.map +1 -0
- package/dist/builtins/array.js +93 -0
- package/dist/builtins/index.d.ts +6 -0
- package/dist/builtins/index.d.ts.map +1 -0
- package/dist/builtins/index.js +5 -0
- package/dist/builtins/scalar.d.ts +12 -0
- package/dist/builtins/scalar.d.ts.map +1 -0
- package/dist/builtins/scalar.js +41 -0
- package/dist/builtins/struct.d.ts +25 -0
- package/dist/builtins/struct.d.ts.map +1 -0
- package/dist/builtins/struct.js +67 -0
- package/dist/builtins/tagged-union.d.ts +54 -0
- package/dist/builtins/tagged-union.d.ts.map +1 -0
- package/dist/builtins/tagged-union.js +81 -0
- package/dist/builtins/with-resource.d.ts +23 -0
- package/dist/builtins/with-resource.d.ts.map +1 -0
- package/dist/builtins/with-resource.js +35 -0
- package/dist/chain.d.ts +1 -0
- package/dist/chain.d.ts.map +1 -0
- package/dist/chain.js +3 -3
- package/dist/effect-id.d.ts +1 -0
- package/dist/effect-id.d.ts.map +1 -0
- package/dist/handler.d.ts +7 -6
- package/dist/handler.d.ts.map +1 -0
- package/dist/handler.js +5 -21
- package/dist/index.d.ts +10 -6
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4 -2
- package/dist/iterator.d.ts +32 -0
- package/dist/iterator.d.ts.map +1 -0
- package/dist/iterator.js +123 -0
- package/dist/option.d.ts +74 -0
- package/dist/option.d.ts.map +1 -0
- package/dist/option.js +141 -0
- package/dist/pipe.d.ts +11 -10
- package/dist/pipe.d.ts.map +1 -0
- package/dist/pipe.js +5 -4
- package/dist/race.d.ts +5 -4
- package/dist/race.d.ts.map +1 -0
- package/dist/race.js +17 -42
- package/dist/recursive.d.ts +9 -3
- package/dist/recursive.d.ts.map +1 -0
- package/dist/recursive.js +18 -13
- package/dist/result.d.ts +50 -0
- package/dist/result.d.ts.map +1 -0
- package/dist/result.js +117 -0
- package/dist/run.d.ts +9 -2
- package/dist/run.d.ts.map +1 -0
- package/dist/run.js +37 -20
- package/dist/runtime.d.ts +6 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/runtime.js +7 -0
- package/dist/schema.d.ts +1 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/schemas.d.ts +5 -0
- package/dist/schemas.d.ts.map +1 -0
- package/dist/schemas.js +13 -0
- package/dist/try-catch.d.ts +2 -1
- package/dist/try-catch.d.ts.map +1 -0
- package/dist/try-catch.js +10 -9
- package/dist/values.d.ts +6 -0
- package/dist/values.d.ts.map +1 -0
- package/dist/values.js +12 -0
- package/dist/worker.d.ts +5 -1
- package/dist/worker.d.ts.map +1 -0
- package/dist/worker.js +15 -3
- package/package.json +8 -6
- package/src/all.ts +118 -74
- package/src/ast.ts +773 -350
- package/src/bind.ts +32 -62
- package/src/builtins/array.ts +121 -0
- package/src/builtins/index.ts +17 -0
- package/src/builtins/scalar.ts +49 -0
- package/src/builtins/struct.ts +111 -0
- package/src/builtins/tagged-union.ts +142 -0
- package/src/builtins/with-resource.ts +69 -0
- package/src/chain.ts +4 -4
- package/src/handler.ts +12 -28
- package/src/index.ts +24 -17
- package/src/iterator.ts +243 -0
- package/src/option.ts +199 -0
- package/src/pipe.ts +123 -78
- package/src/race.ts +41 -51
- package/src/recursive.ts +44 -27
- package/src/result.ts +168 -0
- package/src/run.ts +53 -25
- package/src/runtime.ts +16 -0
- package/src/schemas.ts +21 -0
- package/src/try-catch.ts +14 -10
- package/src/values.ts +21 -0
- package/src/worker.ts +17 -2
- package/dist/builtins.d.ts +0 -257
- package/dist/builtins.js +0 -600
- package/src/builtins.ts +0 -804
package/src/ast.ts
CHANGED
|
@@ -1,4 +1,34 @@
|
|
|
1
1
|
import type { JSONSchema7 } from "json-schema";
|
|
2
|
+
import { chain } from "./chain.js";
|
|
3
|
+
import {
|
|
4
|
+
constant,
|
|
5
|
+
drop,
|
|
6
|
+
extractPrefix,
|
|
7
|
+
flatten as flattenBuiltin,
|
|
8
|
+
getField,
|
|
9
|
+
getIndex,
|
|
10
|
+
identity,
|
|
11
|
+
panic,
|
|
12
|
+
pick,
|
|
13
|
+
splitFirst,
|
|
14
|
+
splitLast,
|
|
15
|
+
tag,
|
|
16
|
+
wrapInField,
|
|
17
|
+
asOption as asOptionStandalone,
|
|
18
|
+
} from "./builtins/index.js";
|
|
19
|
+
import { Option } from "./option.js";
|
|
20
|
+
import { Result } from "./result.js";
|
|
21
|
+
// Lazy import — iterator.ts imports from ast.ts, but these are only called inside
|
|
22
|
+
// methods (after all modules load), so the circular reference is safe at runtime.
|
|
23
|
+
import { Iterator as IteratorNs } from "./iterator.js";
|
|
24
|
+
// Lazy import — bind.ts imports from ast.ts, but these are only called inside
|
|
25
|
+
// methods (after all modules load), so the circular reference is safe at runtime.
|
|
26
|
+
import {
|
|
27
|
+
bind as bindStandalone,
|
|
28
|
+
bindInput as bindInputStandalone,
|
|
29
|
+
type VarRef,
|
|
30
|
+
type InferVarRefs,
|
|
31
|
+
} from "./bind.js";
|
|
2
32
|
|
|
3
33
|
// ---------------------------------------------------------------------------
|
|
4
34
|
// Serializable Types — mirror the Rust AST in barnum_ast
|
|
@@ -88,20 +118,32 @@ export type BuiltinKind =
|
|
|
88
118
|
| { kind: "Constant"; value: unknown }
|
|
89
119
|
| { kind: "Identity" }
|
|
90
120
|
| { kind: "Drop" }
|
|
91
|
-
| { kind: "Tag"; value: string }
|
|
92
121
|
| { kind: "Merge" }
|
|
93
122
|
| { kind: "Flatten" }
|
|
94
|
-
| { kind: "
|
|
95
|
-
| { kind: "
|
|
96
|
-
| { kind: "Pick"; value: string[] }
|
|
123
|
+
| { kind: "GetField"; field: string }
|
|
124
|
+
| { kind: "GetIndex"; index: number }
|
|
97
125
|
| { kind: "CollectSome" }
|
|
98
|
-
|
|
126
|
+
// Future: Add WrapInArray builtin (T → [T]). Currently done via all(identity()) which
|
|
127
|
+
// works but routes through the All executor for a trivial operation.
|
|
128
|
+
| { kind: "AsOption" }
|
|
129
|
+
| { kind: "SplitFirst" }
|
|
130
|
+
| { kind: "SplitLast" }
|
|
131
|
+
| { kind: "WrapInField"; field: string }
|
|
132
|
+
| { kind: "Sleep"; ms: number }
|
|
133
|
+
| { kind: "Panic"; message: string }
|
|
134
|
+
| { kind: "ExtractPrefix" }
|
|
135
|
+
| { kind: "Slice"; start: number; end?: number };
|
|
99
136
|
|
|
100
137
|
/**
|
|
101
|
-
* When
|
|
102
|
-
* combinator
|
|
138
|
+
* When T is `never` or `void` (handler ignores input / recur doesn't
|
|
139
|
+
* thread state), produce `any` so the combinator can sit in any
|
|
140
|
+
* pipeline position.
|
|
103
141
|
*/
|
|
104
|
-
export type PipeIn<T> = [T] extends [never]
|
|
142
|
+
export type PipeIn<T> = [T] extends [never]
|
|
143
|
+
? any
|
|
144
|
+
: [T] extends [void]
|
|
145
|
+
? any
|
|
146
|
+
: T;
|
|
105
147
|
|
|
106
148
|
// ---------------------------------------------------------------------------
|
|
107
149
|
// Config
|
|
@@ -132,34 +174,26 @@ export type MergeTuple<TTuple> = TTuple extends unknown[]
|
|
|
132
174
|
// ---------------------------------------------------------------------------
|
|
133
175
|
|
|
134
176
|
/**
|
|
135
|
-
* An action with tracked input/output types. Phantom fields enforce
|
|
177
|
+
* An action with tracked input/output types. Phantom fields enforce variance
|
|
136
178
|
* and are never set at runtime — they exist only for the TypeScript compiler.
|
|
137
179
|
*
|
|
138
|
-
* Each type variable gets a contravariant + covariant field pair:
|
|
139
180
|
* In: __in (contravariant) + __in_co (covariant) → invariant
|
|
140
|
-
* Out: __out (covariant
|
|
181
|
+
* Out: __out (covariant only)
|
|
141
182
|
*
|
|
142
|
-
*
|
|
183
|
+
* Input invariance ensures exact type matching at pipeline connection points.
|
|
143
184
|
* Data crosses serialization boundaries to handlers in arbitrary languages
|
|
144
185
|
* (Rust, Python, etc.), so extra/missing fields are runtime errors.
|
|
186
|
+
*
|
|
187
|
+
* Output covariance is safe — a step producing Dog where Animal is expected
|
|
188
|
+
* downstream works. `never` (throwError, recur, done) is assignable to any
|
|
189
|
+
* output slot via standard subtyping.
|
|
145
190
|
*/
|
|
146
|
-
export type TypedAction<
|
|
147
|
-
In = unknown,
|
|
148
|
-
Out = unknown,
|
|
149
|
-
Refs extends string = never,
|
|
150
|
-
> = Action & {
|
|
191
|
+
export type TypedAction<In = unknown, Out = unknown> = Action & {
|
|
151
192
|
__in?: (input: In) => void;
|
|
152
193
|
__in_co?: In;
|
|
153
194
|
__out?: () => Out;
|
|
154
|
-
__out_contra?: (output: Out) => void;
|
|
155
|
-
__refs?: { _brand: Refs };
|
|
156
195
|
/** Chain this action with another. `a.then(b)` ≡ `chain(a, b)`. */
|
|
157
|
-
then<TNext>(next: Pipeable<Out, TNext>): TypedAction<In, TNext
|
|
158
|
-
/** Apply an action to each element of an array output. `a.forEach(b)` ≡ `a.then(forEach(b))`. */
|
|
159
|
-
forEach<TIn, TElement, TNext, TRefs extends string>(
|
|
160
|
-
this: TypedAction<TIn, TElement[], TRefs>,
|
|
161
|
-
action: Pipeable<TElement, TNext>,
|
|
162
|
-
): TypedAction<TIn, TNext[], TRefs>;
|
|
196
|
+
then<TNext>(next: Pipeable<Out, TNext>): TypedAction<In, TNext>;
|
|
163
197
|
/** Dispatch on a tagged union output. Auto-unwraps `value` before each case handler. */
|
|
164
198
|
branch<
|
|
165
199
|
TCases extends {
|
|
@@ -167,91 +201,281 @@ export type TypedAction<
|
|
|
167
201
|
},
|
|
168
202
|
>(
|
|
169
203
|
cases: [BranchKeys<Out>] extends [never] ? never : TCases,
|
|
170
|
-
): TypedAction<In, ExtractOutput<TCases[keyof TCases & string]
|
|
171
|
-
/** Flatten
|
|
172
|
-
flatten(
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
Refs
|
|
176
|
-
>;
|
|
204
|
+
): TypedAction<In, ExtractOutput<TCases[keyof TCases & string]>>;
|
|
205
|
+
/** Flatten one level of array nesting. `TElement[][] → TElement[]` */
|
|
206
|
+
flatten<TIn, TElement>(
|
|
207
|
+
this: TypedAction<TIn, TElement[][]>,
|
|
208
|
+
): TypedAction<TIn, TElement[]>;
|
|
177
209
|
/** Discard output. `a.drop()` ≡ `pipe(a, drop)`. */
|
|
178
|
-
drop(): TypedAction<In,
|
|
210
|
+
drop(): TypedAction<In, void>;
|
|
179
211
|
/** Wrap output as a tagged union member. Requires full variant map TDef so __def is carried. */
|
|
180
|
-
tag<
|
|
212
|
+
tag<
|
|
213
|
+
TEnumName extends string,
|
|
214
|
+
TDef extends Record<string, unknown>,
|
|
215
|
+
TKind extends keyof TDef & string,
|
|
216
|
+
>(
|
|
181
217
|
kind: TKind,
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
218
|
+
enumName: TEnumName,
|
|
219
|
+
): TypedAction<In, TaggedUnion<TEnumName, TDef>>;
|
|
220
|
+
/** Wrap output as `Option.Some`. `T → Option<T>` */
|
|
221
|
+
some(): TypedAction<In, Option<Out>>;
|
|
222
|
+
/** Wrap output as `Result.Ok`. `T → Result<T, never>` */
|
|
223
|
+
ok(): TypedAction<In, Result<Out, never>>;
|
|
224
|
+
/** Wrap output as `Result.Err`. `T → Result<never, T>` */
|
|
225
|
+
err(): TypedAction<In, Result<never, Out>>;
|
|
226
|
+
/** Extract a field from the output object. `a.getField("name")` ≡ `pipe(a, getField("name"))`. */
|
|
227
|
+
getField<TField extends keyof Out & string>(
|
|
185
228
|
field: TField,
|
|
186
|
-
): TypedAction<In, Out[TField]
|
|
187
|
-
/**
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
/** Merge a tuple of objects into a single object. `a.merge()` ≡ `pipe(a, merge())`. */
|
|
197
|
-
merge(): TypedAction<In, MergeTuple<Out>, Refs>;
|
|
229
|
+
): TypedAction<In, Out[TField]>;
|
|
230
|
+
/** Extract an element from the output array by index. Returns Option. */
|
|
231
|
+
getIndex<TIn, TTuple extends unknown[], TIndex extends number>(
|
|
232
|
+
this: TypedAction<TIn, TTuple>,
|
|
233
|
+
index: TIndex,
|
|
234
|
+
): TypedAction<TIn, Option<TTuple[TIndex]>>;
|
|
235
|
+
/** Wrap output in an object under a field name. `a.wrapInField("foo")` ≡ `pipe(a, wrapInField("foo"))`. */
|
|
236
|
+
wrapInField<TField extends string>(
|
|
237
|
+
field: TField,
|
|
238
|
+
): TypedAction<In, Record<TField, Out>>;
|
|
198
239
|
/** Select fields from the output. `a.pick("x", "y")` ≡ `pipe(a, pick("x", "y"))`. */
|
|
199
240
|
pick<TKeys extends (keyof Out & string)[]>(
|
|
200
241
|
...keys: TKeys
|
|
201
|
-
): TypedAction<In, Pick<Out, TKeys[number]
|
|
242
|
+
): TypedAction<In, Pick<Out, TKeys[number]>>;
|
|
243
|
+
/** Head/tail decomposition for Iterator. `Iterator<T> → Option<[T, Iterator<T>]>` */
|
|
244
|
+
splitFirst<TIn, TElement>(
|
|
245
|
+
this: TypedAction<TIn, Iterator<TElement>>,
|
|
246
|
+
): TypedAction<TIn, Option<[TElement, Iterator<TElement>]>>;
|
|
247
|
+
/** Head/tail decomposition. Only callable when Out is TElement[]. */
|
|
248
|
+
splitFirst<TIn, TElement>(
|
|
249
|
+
this: TypedAction<TIn, TElement[]>,
|
|
250
|
+
): TypedAction<TIn, Option<[TElement, TElement[]]>>;
|
|
251
|
+
/** Init/last decomposition for Iterator. `Iterator<T> → Option<[Iterator<T>, T]>` */
|
|
252
|
+
splitLast<TIn, TElement>(
|
|
253
|
+
this: TypedAction<TIn, Iterator<TElement>>,
|
|
254
|
+
): TypedAction<TIn, Option<[Iterator<TElement>, TElement]>>;
|
|
255
|
+
/** Init/last decomposition. Only callable when Out is TElement[]. */
|
|
256
|
+
splitLast<TIn, TElement>(
|
|
257
|
+
this: TypedAction<TIn, TElement[]>,
|
|
258
|
+
): TypedAction<TIn, Option<[TElement[], TElement]>>;
|
|
202
259
|
/**
|
|
203
|
-
* Transform the
|
|
204
|
-
* Out is Option<T>. Uses `this` parameter constraint to gate availability.
|
|
260
|
+
* Transform the inner value. Dispatches: Option.map, Result.map.
|
|
205
261
|
*/
|
|
206
|
-
|
|
207
|
-
this: TypedAction<TIn, Option<T
|
|
262
|
+
map<TIn, T, U>(
|
|
263
|
+
this: TypedAction<TIn, Option<T>>,
|
|
208
264
|
action: Pipeable<T, U>,
|
|
209
|
-
): TypedAction<TIn, Option<U
|
|
265
|
+
): TypedAction<TIn, Option<U>>;
|
|
266
|
+
map<TIn, TValue, TOut, TError>(
|
|
267
|
+
this: TypedAction<TIn, Result<TValue, TError>>,
|
|
268
|
+
action: Pipeable<TValue, TOut>,
|
|
269
|
+
): TypedAction<TIn, Result<TOut, TError>>;
|
|
270
|
+
/** Transform each element in Iterator. `Iterator<T> → Iterator<U>` */
|
|
271
|
+
map<TIn, TElement, TOut>(
|
|
272
|
+
this: TypedAction<TIn, Iterator<TElement>>,
|
|
273
|
+
action: Pipeable<TElement, TOut>,
|
|
274
|
+
): TypedAction<TIn, Iterator<TOut>>;
|
|
210
275
|
/**
|
|
211
276
|
* Transform the Err value of a Result output.
|
|
212
277
|
* `Result<TValue, TError> → Result<TValue, TErrorOut>`
|
|
213
|
-
*
|
|
214
|
-
* Only callable when Out is Result<TValue, TError>.
|
|
215
278
|
*/
|
|
216
279
|
mapErr<TIn, TValue, TError, TErrorOut>(
|
|
217
|
-
this: TypedAction<TIn, Result<TValue, TError
|
|
280
|
+
this: TypedAction<TIn, Result<TValue, TError>>,
|
|
218
281
|
action: Pipeable<TError, TErrorOut>,
|
|
219
|
-
): TypedAction<TIn, Result<TValue, TErrorOut
|
|
282
|
+
): TypedAction<TIn, Result<TValue, TErrorOut>>;
|
|
220
283
|
/**
|
|
221
|
-
* Unwrap
|
|
222
|
-
* the default action. Only callable when Out is Result<TValue, TError>.
|
|
284
|
+
* Unwrap or panic. Dispatches: Option.unwrap, Result.unwrap.
|
|
223
285
|
*
|
|
224
|
-
*
|
|
225
|
-
*
|
|
226
|
-
|
|
286
|
+
* Option: If Some, pass through value. If None, panic.
|
|
287
|
+
* Result: If Ok, pass through value. If Err, panic.
|
|
288
|
+
*/
|
|
289
|
+
unwrap<TIn, TValue>(
|
|
290
|
+
this: TypedAction<TIn, Option<TValue>>,
|
|
291
|
+
): TypedAction<TIn, TValue>;
|
|
292
|
+
unwrap<TIn, TValue, TError>(
|
|
293
|
+
this: TypedAction<TIn, Result<TValue, TError>>,
|
|
294
|
+
): TypedAction<TIn, TValue>;
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* Unwrap a union output. Dispatches: Option.unwrapOr, Result.unwrapOr.
|
|
227
298
|
*
|
|
228
|
-
*
|
|
229
|
-
*
|
|
299
|
+
* Option: If Some, pass through value. If None, apply default.
|
|
300
|
+
* Result: If Ok, pass through value. If Err, apply default.
|
|
230
301
|
*
|
|
231
|
-
*
|
|
232
|
-
*
|
|
233
|
-
* The return type uses the enclosing TypedAction's `Refs` directly.
|
|
302
|
+
* Covariant output makes throw tokens (Out=never) work:
|
|
303
|
+
* `handler.unwrapOr(throwError)`
|
|
234
304
|
*/
|
|
305
|
+
unwrapOr<TIn, TValue>(
|
|
306
|
+
this: TypedAction<TIn, Option<TValue>>,
|
|
307
|
+
defaultAction: Pipeable<void, TValue>,
|
|
308
|
+
): TypedAction<TIn, TValue>;
|
|
235
309
|
unwrapOr<TIn, TValue, TError>(
|
|
236
|
-
this: TypedAction<TIn, Result<TValue, TError
|
|
237
|
-
defaultAction:
|
|
238
|
-
): TypedAction<TIn, TValue
|
|
310
|
+
this: TypedAction<TIn, Result<TValue, TError>>,
|
|
311
|
+
defaultAction: Pipeable<TError, TValue>,
|
|
312
|
+
): TypedAction<TIn, TValue>;
|
|
313
|
+
|
|
314
|
+
/** Monadic bind. Option: `Option<T> → Option<U>`. Result: `Result<T,E> → Result<U,E>`. */
|
|
315
|
+
andThen<TIn, TValue, TOut>(
|
|
316
|
+
this: TypedAction<TIn, Option<TValue>>,
|
|
317
|
+
action: Pipeable<TValue, Option<TOut>>,
|
|
318
|
+
): TypedAction<TIn, Option<TOut>>;
|
|
319
|
+
andThen<TIn, TValue, TOut, TError>(
|
|
320
|
+
this: TypedAction<TIn, Result<TValue, TError>>,
|
|
321
|
+
action: Pipeable<TValue, Result<TOut, TError>>,
|
|
322
|
+
): TypedAction<TIn, Result<TOut, TError>>;
|
|
323
|
+
|
|
324
|
+
/** Conditional keep. If Some, apply predicate. If None, stay None. */
|
|
325
|
+
filter<TIn, TValue>(
|
|
326
|
+
this: TypedAction<TIn, Option<TValue>>,
|
|
327
|
+
predicate: Pipeable<TValue, Option<TValue>>,
|
|
328
|
+
): TypedAction<TIn, Option<TValue>>;
|
|
329
|
+
/** Keep elements where predicate returns true. `Iterator<T> → Iterator<T>` */
|
|
330
|
+
filter<TIn, TElement>(
|
|
331
|
+
this: TypedAction<TIn, Iterator<TElement>>,
|
|
332
|
+
predicate: Pipeable<TElement, boolean>,
|
|
333
|
+
): TypedAction<TIn, Iterator<TElement>>;
|
|
334
|
+
|
|
335
|
+
/** Test if the value is Some. `Option<T> → boolean` */
|
|
336
|
+
isSome<TIn, TValue>(
|
|
337
|
+
this: TypedAction<TIn, Option<TValue>>,
|
|
338
|
+
): TypedAction<TIn, boolean>;
|
|
339
|
+
|
|
340
|
+
/** Test if the value is None. `Option<T> → boolean` */
|
|
341
|
+
isNone<TIn, TValue>(
|
|
342
|
+
this: TypedAction<TIn, Option<TValue>>,
|
|
343
|
+
): TypedAction<TIn, boolean>;
|
|
344
|
+
|
|
345
|
+
/** Collect Some values from an array, discarding Nones. `Option<T>[] → T[]` */
|
|
346
|
+
collect<TIn, TValue>(
|
|
347
|
+
this: TypedAction<TIn, Option<TValue>[]>,
|
|
348
|
+
): TypedAction<TIn, TValue[]>;
|
|
349
|
+
/** Unwrap Iterator to array. `Iterator<T> → T[]` */
|
|
350
|
+
collect<TIn, TElement>(
|
|
351
|
+
this: TypedAction<TIn, Iterator<TElement>>,
|
|
352
|
+
): TypedAction<TIn, TElement[]>;
|
|
353
|
+
|
|
354
|
+
/** Fallback on Err. `Result<T,E> → Result<T,F>` */
|
|
355
|
+
or<TIn, TValue, TError, TErrorOut>(
|
|
356
|
+
this: TypedAction<TIn, Result<TValue, TError>>,
|
|
357
|
+
fallback: Pipeable<TError, Result<TValue, TErrorOut>>,
|
|
358
|
+
): TypedAction<TIn, Result<TValue, TErrorOut>>;
|
|
359
|
+
|
|
360
|
+
/** Convert Ok to Some, Err to None. `Result<T,E> → Option<T>` */
|
|
361
|
+
asOkOption<TIn, TValue, TError>(
|
|
362
|
+
this: TypedAction<TIn, Result<TValue, TError>>,
|
|
363
|
+
): TypedAction<TIn, Option<TValue>>;
|
|
364
|
+
|
|
365
|
+
/** Convert Err to Some, Ok to None. `Result<T,E> → Option<E>` */
|
|
366
|
+
asErrOption<TIn, TValue, TError>(
|
|
367
|
+
this: TypedAction<TIn, Result<TValue, TError>>,
|
|
368
|
+
): TypedAction<TIn, Option<TError>>;
|
|
369
|
+
|
|
370
|
+
/** Convert boolean to Option<void>. `boolean → Option<void>` */
|
|
371
|
+
asOption<TIn>(
|
|
372
|
+
this: TypedAction<TIn, boolean>,
|
|
373
|
+
): TypedAction<TIn, Option<void>>;
|
|
374
|
+
|
|
375
|
+
/** Test if the value is Ok. `Result<T,E> → boolean` */
|
|
376
|
+
isOk<TIn, TValue, TError>(
|
|
377
|
+
this: TypedAction<TIn, Result<TValue, TError>>,
|
|
378
|
+
): TypedAction<TIn, boolean>;
|
|
379
|
+
|
|
380
|
+
/** Test if the value is Err. `Result<T,E> → boolean` */
|
|
381
|
+
isErr<TIn, TValue, TError>(
|
|
382
|
+
this: TypedAction<TIn, Result<TValue, TError>>,
|
|
383
|
+
): TypedAction<TIn, boolean>;
|
|
384
|
+
|
|
385
|
+
/** Swap nesting. `Option<Result<T,E>> → Result<Option<T>,E>` or `Result<Option<T>,E> → Option<Result<T,E>>`. */
|
|
386
|
+
transpose<TIn, TValue, TError>(
|
|
387
|
+
this: TypedAction<TIn, Option<Result<TValue, TError>>>,
|
|
388
|
+
): TypedAction<TIn, Result<Option<TValue>, TError>>;
|
|
389
|
+
transpose<TIn, TValue, TError>(
|
|
390
|
+
this: TypedAction<TIn, Result<Option<TValue>, TError>>,
|
|
391
|
+
): TypedAction<TIn, Option<Result<TValue, TError>>>;
|
|
392
|
+
|
|
393
|
+
// --- Iterator methods ---
|
|
394
|
+
|
|
395
|
+
/** Enter Iterator from Option. `Option<T> → Iterator<T>` */
|
|
396
|
+
iterate<TIn, TElement>(
|
|
397
|
+
this: TypedAction<TIn, Option<TElement>>,
|
|
398
|
+
): TypedAction<TIn, Iterator<TElement>>;
|
|
399
|
+
/** Enter Iterator from Result. `Result<T,E> → Iterator<T>` */
|
|
400
|
+
iterate<TIn, TElement, TError>(
|
|
401
|
+
this: TypedAction<TIn, Result<TElement, TError>>,
|
|
402
|
+
): TypedAction<TIn, Iterator<TElement>>;
|
|
403
|
+
/** Enter Iterator from array. `T[] → Iterator<T>` */
|
|
404
|
+
iterate<TIn, TElement>(
|
|
405
|
+
this: TypedAction<TIn, TElement[]>,
|
|
406
|
+
): TypedAction<TIn, Iterator<TElement>>;
|
|
407
|
+
|
|
408
|
+
/** Flat-map each element. `f` returns Iterator. `Iterator<T> → Iterator<U>` */
|
|
409
|
+
flatMap<TIn, TElement, TOut>(
|
|
410
|
+
this: TypedAction<TIn, Iterator<TElement>>,
|
|
411
|
+
action: Pipeable<TElement, Iterator<TOut>>,
|
|
412
|
+
): TypedAction<TIn, Iterator<TOut>>;
|
|
413
|
+
/** Flat-map each element. `f` returns Option. `Iterator<T> → Iterator<U>` */
|
|
414
|
+
flatMap<TIn, TElement, TOut>(
|
|
415
|
+
this: TypedAction<TIn, Iterator<TElement>>,
|
|
416
|
+
action: Pipeable<TElement, Option<TOut>>,
|
|
417
|
+
): TypedAction<TIn, Iterator<TOut>>;
|
|
418
|
+
/** Flat-map each element. `f` returns Result. `Iterator<T> → Iterator<U>` */
|
|
419
|
+
flatMap<TIn, TElement, TOut, TError>(
|
|
420
|
+
this: TypedAction<TIn, Iterator<TElement>>,
|
|
421
|
+
action: Pipeable<TElement, Result<TOut, TError>>,
|
|
422
|
+
): TypedAction<TIn, Iterator<TOut>>;
|
|
423
|
+
/** Flat-map each element. `f` returns array. `Iterator<T> → Iterator<U>` */
|
|
424
|
+
flatMap<TIn, TElement, TOut>(
|
|
425
|
+
this: TypedAction<TIn, Iterator<TElement>>,
|
|
426
|
+
action: Pipeable<TElement, TOut[]>,
|
|
427
|
+
): TypedAction<TIn, Iterator<TOut>>;
|
|
428
|
+
|
|
429
|
+
/** Fold elements with accumulator. `Iterator<T> → TAcc` */
|
|
430
|
+
fold<TIn, TElement, TAcc>(
|
|
431
|
+
this: TypedAction<TIn, Iterator<TElement>>,
|
|
432
|
+
init: Pipeable<void, TAcc>,
|
|
433
|
+
body: Pipeable<[TAcc, TElement], TAcc>,
|
|
434
|
+
): TypedAction<TIn, TAcc>;
|
|
435
|
+
|
|
436
|
+
/** Check if iterator is empty. `Iterator<T> → boolean` */
|
|
437
|
+
isEmpty<TIn, TElement>(
|
|
438
|
+
this: TypedAction<TIn, Iterator<TElement>>,
|
|
439
|
+
): TypedAction<TIn, boolean>;
|
|
440
|
+
|
|
441
|
+
/** Slice elements from start to end. `Iterator<T> → Iterator<T>` */
|
|
442
|
+
slice<TIn, TElement>(
|
|
443
|
+
this: TypedAction<TIn, Iterator<TElement>>,
|
|
444
|
+
start: number,
|
|
445
|
+
end?: number,
|
|
446
|
+
): TypedAction<TIn, Iterator<TElement>>;
|
|
447
|
+
|
|
448
|
+
/** First n elements. `Iterator<T> → Iterator<T>` */
|
|
449
|
+
take<TIn, TElement>(
|
|
450
|
+
this: TypedAction<TIn, Iterator<TElement>>,
|
|
451
|
+
n: number,
|
|
452
|
+
): TypedAction<TIn, Iterator<TElement>>;
|
|
453
|
+
|
|
454
|
+
/** Drop first n elements. `Iterator<T> → Iterator<T>` */
|
|
455
|
+
skip<TIn, TElement>(
|
|
456
|
+
this: TypedAction<TIn, Iterator<TElement>>,
|
|
457
|
+
n: number,
|
|
458
|
+
): TypedAction<TIn, Iterator<TElement>>;
|
|
459
|
+
|
|
460
|
+
/** Bind concurrent values as VarRefs available throughout the body. */
|
|
461
|
+
bind<TBindings extends Action[], TOut>(
|
|
462
|
+
bindings: [...TBindings],
|
|
463
|
+
body: (vars: InferVarRefs<TBindings>) => Action & { __out?: () => TOut },
|
|
464
|
+
): TypedAction<In, TOut>;
|
|
465
|
+
/** Capture the pipeline input as a VarRef. */
|
|
466
|
+
bindInput<TOut>(
|
|
467
|
+
body: (input: VarRef<Out>) => Action & { __out?: () => TOut },
|
|
468
|
+
): TypedAction<In, TOut>;
|
|
239
469
|
};
|
|
240
470
|
|
|
241
471
|
/**
|
|
242
|
-
* Parameter type for pipe and combinators.
|
|
243
|
-
*
|
|
244
|
-
*
|
|
245
|
-
* Invariance: Both In and Out are invariant, matching TypedAction:
|
|
246
|
-
* In: __in (contravariant) + __in_co (covariant) → invariant
|
|
247
|
-
* Out: __out (covariant) + __out_contra (contravariant) → invariant
|
|
472
|
+
* Parameter type for pipe and combinators. Same phantom fields as TypedAction
|
|
473
|
+
* but without methods.
|
|
248
474
|
*
|
|
249
475
|
* Why no methods: TypedAction's methods (then, branch, etc.) participate in
|
|
250
476
|
* TS assignability checks in complex, recursive ways that interfere with
|
|
251
477
|
* generic inference in pipe overloads. Pipeable strips methods so that only
|
|
252
|
-
* phantom fields drive inference
|
|
253
|
-
* resolution, with invariance enforced when TS checks candidates from
|
|
254
|
-
* both sides of a connection.
|
|
478
|
+
* phantom fields drive inference.
|
|
255
479
|
*
|
|
256
480
|
* TypedAction (with methods) is assignable to Pipeable because Pipeable
|
|
257
481
|
* only requires a subset of properties.
|
|
@@ -260,25 +484,30 @@ export type Pipeable<In = unknown, Out = unknown> = Action & {
|
|
|
260
484
|
__in?: (input: In) => void;
|
|
261
485
|
__in_co?: In;
|
|
262
486
|
__out?: () => Out;
|
|
263
|
-
__out_contra?: (output: Out) => void;
|
|
264
487
|
};
|
|
265
488
|
|
|
266
489
|
/**
|
|
267
|
-
*
|
|
490
|
+
* Strip phantom types from a Pipeable, returning a plain Action.
|
|
491
|
+
*
|
|
492
|
+
* Replaces `x as Action` casts throughout the codebase. The constraint
|
|
493
|
+
* ensures the argument is structurally a Pipeable — unlike a bare cast,
|
|
494
|
+
* `toAction(123)` is a type error.
|
|
495
|
+
*/
|
|
496
|
+
export function toAction<TIn, TOut>(pipeable: Pipeable<TIn, TOut>): Action {
|
|
497
|
+
return pipeable;
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
/**
|
|
501
|
+
* Contravariant input + covariant output for branch case handler positions.
|
|
268
502
|
*
|
|
269
|
-
* Omits __in_co (covariant input)
|
|
270
|
-
* compared to TypedAction/Pipeable. This gives:
|
|
503
|
+
* Omits __in_co (covariant input) compared to Pipeable. This gives:
|
|
271
504
|
* In: contravariant only (via __in)
|
|
272
505
|
* Out: covariant only (via __out)
|
|
273
506
|
*
|
|
274
507
|
* Why contravariant input: a handler that accepts `unknown` (like drop)
|
|
275
508
|
* can handle any variant. (input: unknown) => void is assignable to
|
|
276
509
|
* (input: HasErrors) => void because HasErrors extends unknown.
|
|
277
|
-
*
|
|
278
|
-
* Why covariant output: the constraint doesn't restrict output types —
|
|
279
|
-
* they're inferred from the actual case handlers via ExtractOutput.
|
|
280
|
-
* TypedAction's invariant __out_contra with Out=unknown would
|
|
281
|
-
* reject any handler with a specific output type, so we omit it.
|
|
510
|
+
* Pipeable's invariant input (__in_co) would reject this.
|
|
282
511
|
*
|
|
283
512
|
* TypedAction is assignable to CaseHandler because CaseHandler only
|
|
284
513
|
* requires a subset of TypedAction's phantom fields.
|
|
@@ -298,6 +527,11 @@ type CaseHandler<TIn = unknown, TOut = unknown> = Action & {
|
|
|
298
527
|
* field enables `.branch()` to decompose the union via simple indexing
|
|
299
528
|
* (`keyof ExtractDef<Out>` and `ExtractDef<Out>[K]`) instead of
|
|
300
529
|
* conditional types (`KindOf<Out>` and `Extract<Out, { kind: K }>`).
|
|
530
|
+
*
|
|
531
|
+
* **Void → null mapping:** Variants with `void` payload (e.g. `{ None: void }`)
|
|
532
|
+
* become `{ kind: "None"; value: null }` at runtime. This is handled by
|
|
533
|
+
* `VoidToNull` below — `void` has no runtime representation in JSON, so it
|
|
534
|
+
* serializes as `null`. Use `z.null()` in Zod schemas for void variants.
|
|
301
535
|
*/
|
|
302
536
|
// 0 extends 1 & T detects `any` — preserve as-is to avoid collapsing.
|
|
303
537
|
type VoidToNull<T> = 0 extends 1 & T
|
|
@@ -308,9 +542,12 @@ type VoidToNull<T> = 0 extends 1 & T
|
|
|
308
542
|
? null
|
|
309
543
|
: T;
|
|
310
544
|
|
|
311
|
-
export type TaggedUnion<
|
|
545
|
+
export type TaggedUnion<
|
|
546
|
+
TEnumName extends string,
|
|
547
|
+
TDef extends Record<string, unknown>,
|
|
548
|
+
> = {
|
|
312
549
|
[K in keyof TDef & string]: {
|
|
313
|
-
kind: K
|
|
550
|
+
kind: `${TEnumName}.${K}`;
|
|
314
551
|
value: VoidToNull<TDef[K]>;
|
|
315
552
|
__def?: TDef;
|
|
316
553
|
};
|
|
@@ -324,36 +561,52 @@ export type ExtractDef<T> = T extends { __def?: infer D } ? D : never;
|
|
|
324
561
|
// ---------------------------------------------------------------------------
|
|
325
562
|
|
|
326
563
|
export type OptionDef<T> = { Some: T; None: void };
|
|
327
|
-
export type Option<T> = TaggedUnion<OptionDef<T>>;
|
|
564
|
+
export type Option<T> = TaggedUnion<"Option", OptionDef<T>>;
|
|
328
565
|
|
|
329
566
|
// ---------------------------------------------------------------------------
|
|
330
567
|
// Result<TValue, TError> — standard success/error type
|
|
331
568
|
// ---------------------------------------------------------------------------
|
|
332
569
|
|
|
333
570
|
export type ResultDef<TValue, TError> = { Ok: TValue; Err: TError };
|
|
334
|
-
export type Result<TValue, TError> = TaggedUnion<
|
|
571
|
+
export type Result<TValue, TError> = TaggedUnion<
|
|
572
|
+
"Result",
|
|
573
|
+
ResultDef<TValue, TError>
|
|
574
|
+
>;
|
|
575
|
+
|
|
576
|
+
// ---------------------------------------------------------------------------
|
|
577
|
+
// Iterator<T> — sequence wrapper (single-variant TaggedUnion)
|
|
578
|
+
// ---------------------------------------------------------------------------
|
|
579
|
+
|
|
580
|
+
export type IteratorDef<TElement> = { Iterator: TElement[] };
|
|
581
|
+
export type Iterator<TElement> = TaggedUnion<"Iterator", IteratorDef<TElement>>;
|
|
335
582
|
|
|
336
583
|
/** Extract all `kind` string literals from a discriminated union. */
|
|
337
584
|
type KindOf<T> = T extends { kind: infer K extends string } ? K : never;
|
|
338
585
|
|
|
586
|
+
/** Strip a `"Prefix."` namespace from a dotted kind string. `"Nat.Zero"` → `"Zero"`. */
|
|
587
|
+
type StripKindPrefix<K extends string> = K extends `${string}.${infer Bare}`
|
|
588
|
+
? Bare
|
|
589
|
+
: K;
|
|
590
|
+
|
|
339
591
|
/** Extract the `value` field from a `{ kind, value }` variant. Falls back to T if no `value` field. */
|
|
340
592
|
type UnwrapVariant<T> = T extends { value: infer V } ? V : T;
|
|
341
593
|
|
|
342
594
|
/**
|
|
343
595
|
* Branch case keys: prefer ExtractDef (simple keyof indexing) when the
|
|
344
|
-
* output carries __def. Falls back to KindOf
|
|
345
|
-
* outputs without __def.
|
|
596
|
+
* output carries __def. Falls back to KindOf with prefix stripping for
|
|
597
|
+
* outputs without __def (namespaced kinds like "Nat.Zero" → "Zero").
|
|
346
598
|
*/
|
|
347
599
|
type BranchKeys<Out> = [ExtractDef<Out>] extends [never]
|
|
348
|
-
? KindOf<Out
|
|
600
|
+
? StripKindPrefix<KindOf<Out>>
|
|
349
601
|
: keyof ExtractDef<Out> & string;
|
|
350
602
|
|
|
351
603
|
/**
|
|
352
604
|
* Branch case payload: prefer ExtractDef[K] (simple indexing) when available.
|
|
353
|
-
* Falls back to UnwrapVariant<Extract<Out, { kind:
|
|
605
|
+
* Falls back to UnwrapVariant<Extract<Out, { kind: ... }>> for outputs without __def.
|
|
606
|
+
* In the fallback, matches namespaced kinds (`"Prefix.K"`) against the bare key K.
|
|
354
607
|
*/
|
|
355
608
|
type BranchPayload<Out, K extends string> = [ExtractDef<Out>] extends [never]
|
|
356
|
-
? UnwrapVariant<Extract<Out, { kind: K }>>
|
|
609
|
+
? UnwrapVariant<Extract<Out, { kind: K } | { kind: `${string}.${K}` }>>
|
|
357
610
|
: K extends keyof ExtractDef<Out>
|
|
358
611
|
? VoidToNull<ExtractDef<Out>[K]>
|
|
359
612
|
: never;
|
|
@@ -363,228 +616,422 @@ type BranchPayload<Out, K extends string> = [ExtractDef<Out>] extends [never]
|
|
|
363
616
|
// ---------------------------------------------------------------------------
|
|
364
617
|
|
|
365
618
|
// Shared implementations (one closure, not per-instance)
|
|
366
|
-
function thenMethod<TIn, TOut,
|
|
367
|
-
this: TypedAction<TIn, TOut
|
|
619
|
+
function thenMethod<TIn, TOut, TNext>(
|
|
620
|
+
this: TypedAction<TIn, TOut>,
|
|
368
621
|
next: Pipeable<TOut, TNext>,
|
|
369
|
-
): TypedAction<TIn, TNext
|
|
370
|
-
return
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
function forEachMethod(this: TypedAction, action: Action): TypedAction {
|
|
374
|
-
return typedAction({
|
|
375
|
-
kind: "Chain",
|
|
376
|
-
first: this,
|
|
377
|
-
rest: { kind: "ForEach", action },
|
|
378
|
-
});
|
|
622
|
+
): TypedAction<TIn, TNext> {
|
|
623
|
+
return chain(this, next);
|
|
379
624
|
}
|
|
380
625
|
|
|
381
626
|
function branchMethod(
|
|
382
627
|
this: TypedAction,
|
|
383
628
|
cases: Record<string, Action>,
|
|
384
629
|
): TypedAction {
|
|
385
|
-
return
|
|
386
|
-
kind: "Chain",
|
|
387
|
-
first: this,
|
|
388
|
-
rest: { kind: "Branch", cases: unwrapBranchCases(cases) },
|
|
389
|
-
});
|
|
630
|
+
return chain(toAction(this), toAction(branch(cases)));
|
|
390
631
|
}
|
|
391
632
|
|
|
392
633
|
function flattenMethod(this: TypedAction): TypedAction {
|
|
393
|
-
return
|
|
394
|
-
kind: "Chain",
|
|
395
|
-
first: this,
|
|
396
|
-
rest: {
|
|
397
|
-
kind: "Invoke",
|
|
398
|
-
handler: { kind: "Builtin", builtin: { kind: "Flatten" } },
|
|
399
|
-
},
|
|
400
|
-
});
|
|
634
|
+
return chain(toAction(this), toAction(flattenBuiltin()));
|
|
401
635
|
}
|
|
402
636
|
|
|
403
637
|
function dropMethod(this: TypedAction): TypedAction {
|
|
404
|
-
return
|
|
405
|
-
kind: "Chain",
|
|
406
|
-
first: this,
|
|
407
|
-
rest: {
|
|
408
|
-
kind: "Invoke",
|
|
409
|
-
handler: { kind: "Builtin", builtin: { kind: "Drop" } },
|
|
410
|
-
},
|
|
411
|
-
});
|
|
638
|
+
return chain(toAction(this), toAction(drop));
|
|
412
639
|
}
|
|
413
640
|
|
|
414
|
-
function tagMethod(
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
handler: { kind: "Builtin", builtin: { kind: "Tag", value: kind } },
|
|
421
|
-
},
|
|
422
|
-
});
|
|
641
|
+
function tagMethod(
|
|
642
|
+
this: TypedAction,
|
|
643
|
+
kind: string,
|
|
644
|
+
enumName: string,
|
|
645
|
+
): TypedAction {
|
|
646
|
+
return chain(toAction(this), toAction(tag(kind, enumName)));
|
|
423
647
|
}
|
|
424
648
|
|
|
425
|
-
function
|
|
426
|
-
return
|
|
427
|
-
kind: "Chain",
|
|
428
|
-
first: this,
|
|
429
|
-
rest: {
|
|
430
|
-
kind: "Invoke",
|
|
431
|
-
handler: {
|
|
432
|
-
kind: "Builtin",
|
|
433
|
-
builtin: { kind: "ExtractField", value: field },
|
|
434
|
-
},
|
|
435
|
-
},
|
|
436
|
-
});
|
|
649
|
+
function someMethod(this: TypedAction): TypedAction {
|
|
650
|
+
return chain(toAction(this), toAction(Option.some()));
|
|
437
651
|
}
|
|
438
652
|
|
|
439
|
-
function
|
|
440
|
-
|
|
441
|
-
// "this" is the sub-pipeline. augment() wraps it so the original input
|
|
442
|
-
// flows through identity alongside the sub-pipeline, then merges the results.
|
|
443
|
-
return typedAction({
|
|
444
|
-
kind: "Chain",
|
|
445
|
-
first: {
|
|
446
|
-
kind: "All",
|
|
447
|
-
actions: [
|
|
448
|
-
this as Action,
|
|
449
|
-
{
|
|
450
|
-
kind: "Invoke",
|
|
451
|
-
handler: { kind: "Builtin", builtin: { kind: "Identity" } },
|
|
452
|
-
},
|
|
453
|
-
],
|
|
454
|
-
},
|
|
455
|
-
rest: {
|
|
456
|
-
kind: "Invoke",
|
|
457
|
-
handler: { kind: "Builtin", builtin: { kind: "Merge" } },
|
|
458
|
-
},
|
|
459
|
-
});
|
|
653
|
+
function okMethod(this: TypedAction): TypedAction {
|
|
654
|
+
return chain(toAction(this), toAction(Result.ok()));
|
|
460
655
|
}
|
|
461
656
|
|
|
462
|
-
function
|
|
463
|
-
return
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
657
|
+
function errMethod(this: TypedAction): TypedAction {
|
|
658
|
+
return chain(toAction(this), toAction(Result.err()));
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
function getFieldMethod(this: TypedAction, field: string): TypedAction {
|
|
662
|
+
return chain(toAction(this), toAction(getField(field)));
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
function getIndexMethod(this: TypedAction, index: number): TypedAction {
|
|
666
|
+
return chain(toAction(this), toAction(getIndex(index)));
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
function wrapInFieldMethod(this: TypedAction, field: string): TypedAction {
|
|
670
|
+
return chain(toAction(this), toAction(wrapInField(field)));
|
|
471
671
|
}
|
|
472
672
|
|
|
473
673
|
function pickMethod(this: TypedAction, ...keys: string[]): TypedAction {
|
|
474
|
-
return
|
|
475
|
-
kind: "Chain",
|
|
476
|
-
first: this,
|
|
477
|
-
rest: {
|
|
478
|
-
kind: "Invoke",
|
|
479
|
-
handler: { kind: "Builtin", builtin: { kind: "Pick", value: keys } },
|
|
480
|
-
},
|
|
481
|
-
});
|
|
674
|
+
return chain(toAction(this), toAction(pick(...keys)));
|
|
482
675
|
}
|
|
483
676
|
|
|
484
|
-
function
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
first: this,
|
|
492
|
-
rest: {
|
|
493
|
-
kind: "Branch",
|
|
494
|
-
cases: unwrapBranchCases({
|
|
495
|
-
Some: {
|
|
496
|
-
kind: "Chain",
|
|
497
|
-
first: action,
|
|
498
|
-
rest: {
|
|
499
|
-
kind: "Invoke",
|
|
500
|
-
handler: {
|
|
501
|
-
kind: "Builtin",
|
|
502
|
-
builtin: { kind: "Tag", value: "Some" },
|
|
503
|
-
},
|
|
504
|
-
},
|
|
505
|
-
},
|
|
506
|
-
None: {
|
|
507
|
-
kind: "Invoke",
|
|
508
|
-
handler: { kind: "Builtin", builtin: { kind: "Tag", value: "None" } },
|
|
509
|
-
},
|
|
677
|
+
function splitFirstMethod(this: TypedAction): TypedAction {
|
|
678
|
+
return chain(
|
|
679
|
+
toAction(this),
|
|
680
|
+
toAction(
|
|
681
|
+
branchFamily({
|
|
682
|
+
Iterator: IteratorNs.splitFirst(),
|
|
683
|
+
Array: splitFirst(),
|
|
510
684
|
}),
|
|
511
|
-
|
|
512
|
-
|
|
685
|
+
),
|
|
686
|
+
);
|
|
513
687
|
}
|
|
514
688
|
|
|
515
|
-
function
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
cases: unwrapBranchCases({
|
|
523
|
-
Ok: {
|
|
524
|
-
kind: "Invoke",
|
|
525
|
-
handler: { kind: "Builtin", builtin: { kind: "Tag", value: "Ok" } },
|
|
526
|
-
},
|
|
527
|
-
Err: {
|
|
528
|
-
kind: "Chain",
|
|
529
|
-
first: action,
|
|
530
|
-
rest: {
|
|
531
|
-
kind: "Invoke",
|
|
532
|
-
handler: {
|
|
533
|
-
kind: "Builtin",
|
|
534
|
-
builtin: { kind: "Tag", value: "Err" },
|
|
535
|
-
},
|
|
536
|
-
},
|
|
537
|
-
},
|
|
689
|
+
function splitLastMethod(this: TypedAction): TypedAction {
|
|
690
|
+
return chain(
|
|
691
|
+
toAction(this),
|
|
692
|
+
toAction(
|
|
693
|
+
branchFamily({
|
|
694
|
+
Iterator: IteratorNs.splitLast(),
|
|
695
|
+
Array: splitLast(),
|
|
538
696
|
}),
|
|
539
|
-
|
|
540
|
-
|
|
697
|
+
),
|
|
698
|
+
);
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
// --- Shared postfix methods (Option + Result) — dispatch via branchFamily ---
|
|
702
|
+
|
|
703
|
+
function mapMethod(this: TypedAction, action: Action): TypedAction {
|
|
704
|
+
return chain(
|
|
705
|
+
toAction(this),
|
|
706
|
+
toAction(
|
|
707
|
+
branchFamily({
|
|
708
|
+
Result: branch({
|
|
709
|
+
Ok: chain(toAction(action), toAction(Result.ok())),
|
|
710
|
+
Err: Result.err(),
|
|
711
|
+
}),
|
|
712
|
+
Option: branch({
|
|
713
|
+
Some: chain(toAction(action), toAction(Option.some())),
|
|
714
|
+
None: Option.none(),
|
|
715
|
+
}),
|
|
716
|
+
Iterator: IteratorNs.map(action),
|
|
717
|
+
}),
|
|
718
|
+
),
|
|
719
|
+
);
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
function unwrapMethod(this: TypedAction): TypedAction {
|
|
723
|
+
return chain(
|
|
724
|
+
toAction(this),
|
|
725
|
+
toAction(
|
|
726
|
+
branchFamily({
|
|
727
|
+
Result: branch({ Ok: identity(), Err: panic("called unwrap on Err") }),
|
|
728
|
+
Option: branch({
|
|
729
|
+
Some: identity(),
|
|
730
|
+
None: panic("called unwrap on None"),
|
|
731
|
+
}),
|
|
732
|
+
}),
|
|
733
|
+
),
|
|
734
|
+
);
|
|
541
735
|
}
|
|
542
736
|
|
|
543
737
|
function unwrapOrMethod(this: TypedAction, defaultAction: Action): TypedAction {
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
cases: unwrapBranchCases({
|
|
551
|
-
Ok: {
|
|
552
|
-
kind: "Invoke",
|
|
553
|
-
handler: { kind: "Builtin", builtin: { kind: "Identity" } },
|
|
554
|
-
},
|
|
555
|
-
Err: defaultAction,
|
|
738
|
+
return chain(
|
|
739
|
+
toAction(this),
|
|
740
|
+
toAction(
|
|
741
|
+
branchFamily({
|
|
742
|
+
Result: branch({ Ok: identity(), Err: defaultAction }),
|
|
743
|
+
Option: branch({ Some: identity(), None: defaultAction }),
|
|
556
744
|
}),
|
|
557
|
-
|
|
558
|
-
|
|
745
|
+
),
|
|
746
|
+
);
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
function andThenMethod(this: TypedAction, action: Action): TypedAction {
|
|
750
|
+
return chain(
|
|
751
|
+
toAction(this),
|
|
752
|
+
toAction(
|
|
753
|
+
branchFamily({
|
|
754
|
+
Result: branch({ Ok: action, Err: Result.err() }),
|
|
755
|
+
Option: branch({ Some: action, None: Option.none() }),
|
|
756
|
+
}),
|
|
757
|
+
),
|
|
758
|
+
);
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
function transposeMethod(this: TypedAction): TypedAction {
|
|
762
|
+
return chain(
|
|
763
|
+
toAction(this),
|
|
764
|
+
toAction(
|
|
765
|
+
branchFamily({
|
|
766
|
+
Option: branch({
|
|
767
|
+
Some: branch({
|
|
768
|
+
Ok: chain(toAction(Option.some()), toAction(Result.ok())),
|
|
769
|
+
Err: Result.err(),
|
|
770
|
+
}),
|
|
771
|
+
None: chain(
|
|
772
|
+
toAction(chain(toAction(drop), toAction(Option.none()))),
|
|
773
|
+
toAction(Result.ok()),
|
|
774
|
+
),
|
|
775
|
+
}),
|
|
776
|
+
Result: branch({
|
|
777
|
+
Ok: branch({
|
|
778
|
+
Some: chain(toAction(Result.ok()), toAction(Option.some())),
|
|
779
|
+
None: chain(toAction(drop), toAction(Option.none())),
|
|
780
|
+
}),
|
|
781
|
+
Err: chain(toAction(Result.err()), toAction(Option.some())),
|
|
782
|
+
}),
|
|
783
|
+
}),
|
|
784
|
+
),
|
|
785
|
+
);
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
// --- Result-only postfix methods ---
|
|
789
|
+
|
|
790
|
+
function mapErrMethod(this: TypedAction, action: Action): TypedAction {
|
|
791
|
+
return chain(
|
|
792
|
+
toAction(this),
|
|
793
|
+
toAction(
|
|
794
|
+
branch({
|
|
795
|
+
Ok: Result.ok(),
|
|
796
|
+
Err: chain(toAction(action), toAction(Result.err())),
|
|
797
|
+
}),
|
|
798
|
+
),
|
|
799
|
+
);
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
function orMethod(this: TypedAction, fallback: Action): TypedAction {
|
|
803
|
+
return chain(
|
|
804
|
+
toAction(this),
|
|
805
|
+
toAction(
|
|
806
|
+
branch({
|
|
807
|
+
Ok: Result.ok(),
|
|
808
|
+
Err: fallback,
|
|
809
|
+
}),
|
|
810
|
+
),
|
|
811
|
+
);
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
function asOkOptionMethod(this: TypedAction): TypedAction {
|
|
815
|
+
return chain(
|
|
816
|
+
toAction(this),
|
|
817
|
+
toAction(
|
|
818
|
+
branch({
|
|
819
|
+
Ok: Option.some(),
|
|
820
|
+
Err: chain(toAction(drop), toAction(Option.none())),
|
|
821
|
+
}),
|
|
822
|
+
),
|
|
823
|
+
);
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
function asErrOptionMethod(this: TypedAction): TypedAction {
|
|
827
|
+
return chain(
|
|
828
|
+
toAction(this),
|
|
829
|
+
toAction(
|
|
830
|
+
branch({
|
|
831
|
+
Ok: chain(toAction(drop), toAction(Option.none())),
|
|
832
|
+
Err: Option.some(),
|
|
833
|
+
}),
|
|
834
|
+
),
|
|
835
|
+
);
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
function isOkMethod(this: TypedAction): TypedAction {
|
|
839
|
+
return chain(
|
|
840
|
+
toAction(this),
|
|
841
|
+
toAction(
|
|
842
|
+
branch({
|
|
843
|
+
Ok: constant(true),
|
|
844
|
+
Err: constant(false),
|
|
845
|
+
}),
|
|
846
|
+
),
|
|
847
|
+
);
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
function isErrMethod(this: TypedAction): TypedAction {
|
|
851
|
+
return chain(
|
|
852
|
+
toAction(this),
|
|
853
|
+
toAction(
|
|
854
|
+
branch({
|
|
855
|
+
Ok: constant(false),
|
|
856
|
+
Err: constant(true),
|
|
857
|
+
}),
|
|
858
|
+
),
|
|
859
|
+
);
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
// --- Option-only postfix methods ---
|
|
863
|
+
|
|
864
|
+
function filterMethod(this: TypedAction, predicate: Action): TypedAction {
|
|
865
|
+
return chain(
|
|
866
|
+
toAction(this),
|
|
867
|
+
toAction(
|
|
868
|
+
branchFamily({
|
|
869
|
+
Option: branch({
|
|
870
|
+
Some: predicate,
|
|
871
|
+
None: Option.none(),
|
|
872
|
+
}),
|
|
873
|
+
Iterator: IteratorNs.filter(predicate),
|
|
874
|
+
}),
|
|
875
|
+
),
|
|
876
|
+
);
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
function isSomeMethod(this: TypedAction): TypedAction {
|
|
880
|
+
return chain(
|
|
881
|
+
toAction(this),
|
|
882
|
+
toAction(
|
|
883
|
+
branch({
|
|
884
|
+
Some: constant(true),
|
|
885
|
+
None: constant(false),
|
|
886
|
+
}),
|
|
887
|
+
),
|
|
888
|
+
);
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
function isNoneMethod(this: TypedAction): TypedAction {
|
|
892
|
+
return chain(
|
|
893
|
+
toAction(this),
|
|
894
|
+
toAction(
|
|
895
|
+
branch({
|
|
896
|
+
Some: constant(false),
|
|
897
|
+
None: constant(true),
|
|
898
|
+
}),
|
|
899
|
+
),
|
|
900
|
+
);
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
function asOptionMethod(this: TypedAction): TypedAction {
|
|
904
|
+
return chain(toAction(this), toAction(asOptionStandalone()));
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
// --- Iterator postfix methods ---
|
|
908
|
+
|
|
909
|
+
function iterateMethod(this: TypedAction): TypedAction {
|
|
910
|
+
return chain(
|
|
911
|
+
toAction(this),
|
|
912
|
+
toAction(
|
|
913
|
+
branchFamily({
|
|
914
|
+
Option: IteratorNs.fromOption(),
|
|
915
|
+
Result: IteratorNs.fromResult(),
|
|
916
|
+
Array: IteratorNs.fromArray(),
|
|
917
|
+
}),
|
|
918
|
+
),
|
|
919
|
+
);
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
function flatMapMethod(this: TypedAction, action: Action): TypedAction {
|
|
923
|
+
return chain(toAction(this), toAction(IteratorNs.flatMap(action)));
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
function collectMethod(this: TypedAction): TypedAction {
|
|
927
|
+
return chain(
|
|
928
|
+
toAction(this),
|
|
929
|
+
toAction(
|
|
930
|
+
branchFamily({
|
|
931
|
+
Array: Option.collect(),
|
|
932
|
+
Iterator: IteratorNs.collect(),
|
|
933
|
+
}),
|
|
934
|
+
),
|
|
935
|
+
);
|
|
936
|
+
}
|
|
937
|
+
|
|
938
|
+
function foldMethod(
|
|
939
|
+
this: TypedAction,
|
|
940
|
+
init: Action,
|
|
941
|
+
body: Action,
|
|
942
|
+
): TypedAction {
|
|
943
|
+
return chain(toAction(this), toAction(IteratorNs.fold(init, body)));
|
|
944
|
+
}
|
|
945
|
+
|
|
946
|
+
function isEmptyMethod(this: TypedAction): TypedAction {
|
|
947
|
+
return chain(toAction(this), toAction(IteratorNs.isEmpty()));
|
|
948
|
+
}
|
|
949
|
+
|
|
950
|
+
function sliceMethod(
|
|
951
|
+
this: TypedAction,
|
|
952
|
+
start: number,
|
|
953
|
+
end?: number,
|
|
954
|
+
): TypedAction {
|
|
955
|
+
return chain(toAction(this), toAction(IteratorNs.slice(start, end)));
|
|
956
|
+
}
|
|
957
|
+
|
|
958
|
+
function takeMethod(this: TypedAction, n: number): TypedAction {
|
|
959
|
+
return chain(toAction(this), toAction(IteratorNs.take(n)));
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
function skipMethod(this: TypedAction, n: number): TypedAction {
|
|
963
|
+
return chain(toAction(this), toAction(IteratorNs.skip(n)));
|
|
964
|
+
}
|
|
965
|
+
|
|
966
|
+
function bindMethod(
|
|
967
|
+
this: TypedAction,
|
|
968
|
+
bindings: Action[],
|
|
969
|
+
body: (vars: any) => Action,
|
|
970
|
+
): TypedAction {
|
|
971
|
+
return chain(toAction(this), toAction(bindStandalone(bindings, body)));
|
|
972
|
+
}
|
|
973
|
+
|
|
974
|
+
function bindInputMethod(
|
|
975
|
+
this: TypedAction,
|
|
976
|
+
body: (input: any) => Action,
|
|
977
|
+
): TypedAction {
|
|
978
|
+
return chain(toAction(this), toAction(bindInputStandalone(body)));
|
|
559
979
|
}
|
|
560
980
|
|
|
561
981
|
/**
|
|
562
982
|
* Attach `.then()` and `.forEach()` methods to a plain Action object.
|
|
563
983
|
* Methods are non-enumerable: invisible to JSON.stringify and toEqual.
|
|
564
984
|
*/
|
|
565
|
-
export function typedAction<
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
Refs extends string = never,
|
|
569
|
-
>(action: Action): TypedAction<In, Out, Refs> {
|
|
985
|
+
export function typedAction<In = unknown, Out = unknown>(
|
|
986
|
+
action: Action,
|
|
987
|
+
): TypedAction<In, Out> {
|
|
570
988
|
if (!("then" in action)) {
|
|
571
989
|
Object.defineProperties(action, {
|
|
572
990
|
then: { value: thenMethod, configurable: true },
|
|
573
|
-
|
|
991
|
+
|
|
574
992
|
branch: { value: branchMethod, configurable: true },
|
|
575
993
|
flatten: { value: flattenMethod, configurable: true },
|
|
576
994
|
drop: { value: dropMethod, configurable: true },
|
|
577
995
|
tag: { value: tagMethod, configurable: true },
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
996
|
+
some: { value: someMethod, configurable: true },
|
|
997
|
+
ok: { value: okMethod, configurable: true },
|
|
998
|
+
err: { value: errMethod, configurable: true },
|
|
999
|
+
getField: { value: getFieldMethod, configurable: true },
|
|
1000
|
+
getIndex: { value: getIndexMethod, configurable: true },
|
|
1001
|
+
wrapInField: { value: wrapInFieldMethod, configurable: true },
|
|
1002
|
+
|
|
581
1003
|
pick: { value: pickMethod, configurable: true },
|
|
582
|
-
|
|
1004
|
+
splitFirst: { value: splitFirstMethod, configurable: true },
|
|
1005
|
+
splitLast: { value: splitLastMethod, configurable: true },
|
|
1006
|
+
map: { value: mapMethod, configurable: true },
|
|
583
1007
|
mapErr: { value: mapErrMethod, configurable: true },
|
|
1008
|
+
unwrap: { value: unwrapMethod, configurable: true },
|
|
584
1009
|
unwrapOr: { value: unwrapOrMethod, configurable: true },
|
|
1010
|
+
andThen: { value: andThenMethod, configurable: true },
|
|
1011
|
+
filter: { value: filterMethod, configurable: true },
|
|
1012
|
+
isSome: { value: isSomeMethod, configurable: true },
|
|
1013
|
+
isNone: { value: isNoneMethod, configurable: true },
|
|
1014
|
+
asOption: { value: asOptionMethod, configurable: true },
|
|
1015
|
+
collect: { value: collectMethod, configurable: true },
|
|
1016
|
+
fold: { value: foldMethod, configurable: true },
|
|
1017
|
+
isEmpty: { value: isEmptyMethod, configurable: true },
|
|
1018
|
+
slice: { value: sliceMethod, configurable: true },
|
|
1019
|
+
take: { value: takeMethod, configurable: true },
|
|
1020
|
+
skip: { value: skipMethod, configurable: true },
|
|
1021
|
+
or: { value: orMethod, configurable: true },
|
|
1022
|
+
iterate: { value: iterateMethod, configurable: true },
|
|
1023
|
+
flatMap: { value: flatMapMethod, configurable: true },
|
|
1024
|
+
|
|
1025
|
+
asOkOption: { value: asOkOptionMethod, configurable: true },
|
|
1026
|
+
asErrOption: { value: asErrOptionMethod, configurable: true },
|
|
1027
|
+
isOk: { value: isOkMethod, configurable: true },
|
|
1028
|
+
isErr: { value: isErrMethod, configurable: true },
|
|
1029
|
+
transpose: { value: transposeMethod, configurable: true },
|
|
1030
|
+
bind: { value: bindMethod, configurable: true },
|
|
1031
|
+
bindInput: { value: bindInputMethod, configurable: true },
|
|
585
1032
|
});
|
|
586
1033
|
}
|
|
587
|
-
return action as TypedAction<In, Out
|
|
1034
|
+
return action as TypedAction<In, Out>;
|
|
588
1035
|
}
|
|
589
1036
|
|
|
590
1037
|
// ---------------------------------------------------------------------------
|
|
@@ -595,7 +1042,7 @@ export function typedAction<
|
|
|
595
1042
|
* Extract the input type from a TypedAction.
|
|
596
1043
|
*
|
|
597
1044
|
* Uses direct phantom field extraction (not full TypedAction matching) to
|
|
598
|
-
* avoid
|
|
1045
|
+
* avoid a full `TypedAction<any, any>` constraint which fails for In=never
|
|
599
1046
|
* due to __in contravariance.
|
|
600
1047
|
*/
|
|
601
1048
|
export type ExtractInput<T> = T extends {
|
|
@@ -634,11 +1081,11 @@ export { race, sleep, withTimeout } from "./race.js";
|
|
|
634
1081
|
export function forEach<In, Out>(
|
|
635
1082
|
action: Pipeable<In, Out>,
|
|
636
1083
|
): TypedAction<In[], Out[]> {
|
|
637
|
-
return typedAction({ kind: "ForEach", action: action
|
|
1084
|
+
return typedAction({ kind: "ForEach", action: toAction(action) });
|
|
638
1085
|
}
|
|
639
1086
|
|
|
640
1087
|
/**
|
|
641
|
-
* Insert
|
|
1088
|
+
* Insert GetField("value") before each case handler in a branch.
|
|
642
1089
|
* This implements auto-unwrapping: the engine dispatches on `kind`, then
|
|
643
1090
|
* extracts `value` before passing to the handler. Case handlers receive
|
|
644
1091
|
* the payload directly, not the full `{ kind, value }` variant.
|
|
@@ -648,17 +1095,9 @@ function unwrapBranchCases(
|
|
|
648
1095
|
): Record<string, Action> {
|
|
649
1096
|
const unwrapped: Record<string, Action> = {};
|
|
650
1097
|
for (const key of Object.keys(cases)) {
|
|
651
|
-
unwrapped[key] =
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
kind: "Invoke",
|
|
655
|
-
handler: {
|
|
656
|
-
kind: "Builtin",
|
|
657
|
-
builtin: { kind: "ExtractField", value: "value" },
|
|
658
|
-
},
|
|
659
|
-
},
|
|
660
|
-
rest: cases[key],
|
|
661
|
-
};
|
|
1098
|
+
unwrapped[key] = toAction(
|
|
1099
|
+
chain(toAction(getField("value")), toAction(cases[key])),
|
|
1100
|
+
);
|
|
662
1101
|
}
|
|
663
1102
|
return unwrapped;
|
|
664
1103
|
}
|
|
@@ -689,39 +1128,32 @@ export function branch<TCases extends Record<string, Action>>(
|
|
|
689
1128
|
return typedAction({ kind: "Branch", cases: unwrapBranchCases(cases) });
|
|
690
1129
|
}
|
|
691
1130
|
|
|
1131
|
+
/**
|
|
1132
|
+
* Two-level dispatch: extract the enum prefix from a tagged value's `kind`,
|
|
1133
|
+
* then branch on that prefix. Used by postfix methods (`.map()`, `.unwrapOr()`,
|
|
1134
|
+
* etc.) to dispatch across union families (Option, Result) without runtime
|
|
1135
|
+
* metadata.
|
|
1136
|
+
*
|
|
1137
|
+
* `branchFamily({ Result: ..., Option: ... })` ≡ `chain(extractPrefix(), branch(cases))`
|
|
1138
|
+
*/
|
|
1139
|
+
export function branchFamily(cases: Record<string, Action>): TypedAction {
|
|
1140
|
+
return typedAction({
|
|
1141
|
+
kind: "Chain",
|
|
1142
|
+
first: toAction(extractPrefix()),
|
|
1143
|
+
rest: toAction(branch(cases)),
|
|
1144
|
+
});
|
|
1145
|
+
}
|
|
1146
|
+
|
|
692
1147
|
type LoopResultDef<TContinue, TBreak> = {
|
|
693
1148
|
Continue: TContinue;
|
|
694
1149
|
Break: TBreak;
|
|
695
1150
|
};
|
|
696
1151
|
|
|
697
1152
|
export type LoopResult<TContinue, TBreak> = TaggedUnion<
|
|
1153
|
+
"LoopResult",
|
|
698
1154
|
LoopResultDef<TContinue, TBreak>
|
|
699
1155
|
>;
|
|
700
1156
|
|
|
701
|
-
// ---------------------------------------------------------------------------
|
|
702
|
-
// Shared AST constants for control flow compilation
|
|
703
|
-
// ---------------------------------------------------------------------------
|
|
704
|
-
|
|
705
|
-
const EXTRACT_PAYLOAD: Action = {
|
|
706
|
-
kind: "Invoke",
|
|
707
|
-
handler: { kind: "Builtin", builtin: { kind: "ExtractIndex", value: 0 } },
|
|
708
|
-
};
|
|
709
|
-
|
|
710
|
-
const TAG_CONTINUE: Action = {
|
|
711
|
-
kind: "Invoke",
|
|
712
|
-
handler: { kind: "Builtin", builtin: { kind: "Tag", value: "Continue" } },
|
|
713
|
-
};
|
|
714
|
-
|
|
715
|
-
export const TAG_BREAK: Action = {
|
|
716
|
-
kind: "Invoke",
|
|
717
|
-
handler: { kind: "Builtin", builtin: { kind: "Tag", value: "Break" } },
|
|
718
|
-
};
|
|
719
|
-
|
|
720
|
-
export const IDENTITY: Action = {
|
|
721
|
-
kind: "Invoke",
|
|
722
|
-
handler: { kind: "Builtin", builtin: { kind: "Identity" } },
|
|
723
|
-
};
|
|
724
|
-
|
|
725
1157
|
// ---------------------------------------------------------------------------
|
|
726
1158
|
// recur — restart body primitive
|
|
727
1159
|
// ---------------------------------------------------------------------------
|
|
@@ -733,9 +1165,9 @@ export const IDENTITY: Action = {
|
|
|
733
1165
|
* If the body completes normally → output is TOut.
|
|
734
1166
|
* If restart fires → body re-executes with the restarted value.
|
|
735
1167
|
*
|
|
736
|
-
* Compiled form: `RestartHandle(id,
|
|
1168
|
+
* Compiled form: `RestartHandle(id, GetIndex(0), body)`
|
|
737
1169
|
*/
|
|
738
|
-
export function recur<TIn =
|
|
1170
|
+
export function recur<TIn = void, TOut = any>(
|
|
739
1171
|
bodyFn: (restart: TypedAction<TIn, never>) => Pipeable<TIn, TOut>,
|
|
740
1172
|
): TypedAction<PipeIn<TIn>, TOut> {
|
|
741
1173
|
const restartHandlerId = allocateRestartHandlerId();
|
|
@@ -745,13 +1177,13 @@ export function recur<TIn = never, TOut = any>(
|
|
|
745
1177
|
restart_handler_id: restartHandlerId,
|
|
746
1178
|
});
|
|
747
1179
|
|
|
748
|
-
const body = bodyFn(restartAction)
|
|
1180
|
+
const body = toAction(bodyFn(restartAction));
|
|
749
1181
|
|
|
750
1182
|
return typedAction({
|
|
751
1183
|
kind: "RestartHandle",
|
|
752
1184
|
restart_handler_id: restartHandlerId,
|
|
753
1185
|
body,
|
|
754
|
-
handler:
|
|
1186
|
+
handler: toAction(getIndex(0).unwrap()),
|
|
755
1187
|
});
|
|
756
1188
|
}
|
|
757
1189
|
|
|
@@ -771,23 +1203,26 @@ export function recur<TIn = never, TOut = any>(
|
|
|
771
1203
|
* a Branch. earlyReturn tags with Break and performs — the handler restarts
|
|
772
1204
|
* the body, Branch takes the Break path, and the value exits.
|
|
773
1205
|
*/
|
|
774
|
-
export function earlyReturn<TEarlyReturn =
|
|
1206
|
+
export function earlyReturn<TEarlyReturn = void, TIn = any, TOut = any>(
|
|
775
1207
|
bodyFn: (
|
|
776
1208
|
earlyReturn: TypedAction<TEarlyReturn, never>,
|
|
777
1209
|
) => Pipeable<TIn, TOut>,
|
|
778
1210
|
): TypedAction<TIn, TEarlyReturn | TOut> {
|
|
779
1211
|
const restartHandlerId = allocateRestartHandlerId();
|
|
780
1212
|
|
|
781
|
-
const earlyReturnAction = typedAction<TEarlyReturn, never>(
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
1213
|
+
const earlyReturnAction = typedAction<TEarlyReturn, never>(
|
|
1214
|
+
toAction(
|
|
1215
|
+
chain(toAction(tag("Break", "LoopResult")), {
|
|
1216
|
+
kind: "RestartPerform",
|
|
1217
|
+
restart_handler_id: restartHandlerId,
|
|
1218
|
+
}),
|
|
1219
|
+
),
|
|
1220
|
+
);
|
|
786
1221
|
|
|
787
|
-
const body = bodyFn(earlyReturnAction)
|
|
1222
|
+
const body = toAction(bodyFn(earlyReturnAction));
|
|
788
1223
|
|
|
789
1224
|
return typedAction(
|
|
790
|
-
buildRestartBranchAction(restartHandlerId, body,
|
|
1225
|
+
buildRestartBranchAction(restartHandlerId, body, toAction(identity())),
|
|
791
1226
|
);
|
|
792
1227
|
}
|
|
793
1228
|
|
|
@@ -797,7 +1232,7 @@ export function earlyReturn<TEarlyReturn = never, TIn = any, TOut = any>(
|
|
|
797
1232
|
|
|
798
1233
|
/**
|
|
799
1234
|
* Build the restart+branch compiled form:
|
|
800
|
-
* `Chain(Tag("Continue"), RestartHandle(id,
|
|
1235
|
+
* `Chain(Tag("Continue"), RestartHandle(id, GetIndex(0), Branch({ Continue: continueArm, Break: breakArm })))`
|
|
801
1236
|
*
|
|
802
1237
|
* Input is tagged Continue so the Branch enters the continueArm on first execution.
|
|
803
1238
|
* Continue tag → restart → re-enters continueArm. Break tag → restart → runs breakArm, exits `RestartHandle`.
|
|
@@ -809,22 +1244,14 @@ export function buildRestartBranchAction(
|
|
|
809
1244
|
continueArm: Action,
|
|
810
1245
|
breakArm: Action,
|
|
811
1246
|
): Action {
|
|
812
|
-
return
|
|
813
|
-
|
|
814
|
-
first: TAG_CONTINUE,
|
|
815
|
-
rest: {
|
|
1247
|
+
return toAction(
|
|
1248
|
+
chain(toAction(tag("Continue", "LoopResult")), {
|
|
816
1249
|
kind: "RestartHandle",
|
|
817
1250
|
restart_handler_id: restartHandlerId,
|
|
818
|
-
body: {
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
Break: breakArm,
|
|
823
|
-
}),
|
|
824
|
-
},
|
|
825
|
-
handler: EXTRACT_PAYLOAD,
|
|
826
|
-
},
|
|
827
|
-
};
|
|
1251
|
+
body: toAction(branch({ Continue: continueArm, Break: breakArm })),
|
|
1252
|
+
handler: toAction(getIndex(0).unwrap()),
|
|
1253
|
+
}),
|
|
1254
|
+
);
|
|
828
1255
|
}
|
|
829
1256
|
|
|
830
1257
|
/**
|
|
@@ -836,12 +1263,12 @@ export function buildRestartBranchAction(
|
|
|
836
1263
|
*
|
|
837
1264
|
* Compiles to `RestartHandle`/`RestartPerform`/Branch — same effect substrate as tryCatch and earlyReturn.
|
|
838
1265
|
*/
|
|
839
|
-
export function loop<TBreak =
|
|
1266
|
+
export function loop<TBreak = void, TRecur = void>(
|
|
840
1267
|
bodyFn: (
|
|
841
|
-
recur: TypedAction<
|
|
1268
|
+
recur: TypedAction<TRecur, never>,
|
|
842
1269
|
done: TypedAction<VoidToNull<TBreak>, never>,
|
|
843
|
-
) => Pipeable<
|
|
844
|
-
): TypedAction<PipeIn<
|
|
1270
|
+
) => Pipeable<TRecur, never>,
|
|
1271
|
+
): TypedAction<PipeIn<TRecur>, VoidToNull<TBreak>> {
|
|
845
1272
|
const restartHandlerId = allocateRestartHandlerId();
|
|
846
1273
|
|
|
847
1274
|
const perform: Action = {
|
|
@@ -849,22 +1276,18 @@ export function loop<TBreak = never, TIn = never>(
|
|
|
849
1276
|
restart_handler_id: restartHandlerId,
|
|
850
1277
|
};
|
|
851
1278
|
|
|
852
|
-
const recurAction = typedAction<
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
rest: perform,
|
|
856
|
-
});
|
|
1279
|
+
const recurAction = typedAction<TRecur, never>(
|
|
1280
|
+
toAction(chain(toAction(tag("Continue", "LoopResult")), toAction(perform))),
|
|
1281
|
+
);
|
|
857
1282
|
|
|
858
|
-
const doneAction = typedAction<VoidToNull<TBreak>, never>(
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
rest: perform,
|
|
862
|
-
});
|
|
1283
|
+
const doneAction = typedAction<VoidToNull<TBreak>, never>(
|
|
1284
|
+
toAction(chain(toAction(tag("Break", "LoopResult")), toAction(perform))),
|
|
1285
|
+
);
|
|
863
1286
|
|
|
864
|
-
const body = bodyFn(recurAction, doneAction)
|
|
1287
|
+
const body = toAction(bodyFn(recurAction, doneAction));
|
|
865
1288
|
|
|
866
1289
|
return typedAction(
|
|
867
|
-
buildRestartBranchAction(restartHandlerId, body,
|
|
1290
|
+
buildRestartBranchAction(restartHandlerId, body, toAction(identity())),
|
|
868
1291
|
);
|
|
869
1292
|
}
|
|
870
1293
|
|