@barnum/barnum 0.0.0-main-54c41bbb → 0.0.0-main-a589e69d

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.
Binary file
Binary file
Binary file
Binary file
Binary file
package/dist/ast.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import type { JSONSchema7 } from "json-schema";
2
+ import { type VarRef, type InferVarRefs } from "./bind.js";
2
3
  export type Action = InvokeAction | ChainAction | ForEachAction | AllAction | BranchAction | ResumeHandleAction | ResumePerformAction | RestartHandleAction | RestartPerformAction;
3
4
  export interface InvokeAction {
4
5
  kind: "Invoke";
@@ -82,12 +83,16 @@ export type BuiltinKind = {
82
83
  } | {
83
84
  kind: "Sleep";
84
85
  ms: number;
86
+ } | {
87
+ kind: "Panic";
88
+ message: string;
85
89
  };
86
90
  /**
87
- * When TIn is `never` (handler ignores input), produce `any` so the
88
- * combinator/pipe can sit in any pipeline position.
91
+ * When T is `never` or `void` (handler ignores input / recur doesn't
92
+ * thread state), produce `any` so the combinator can sit in any
93
+ * pipeline position.
89
94
  */
90
- export type PipeIn<T> = [T] extends [never] ? any : T;
95
+ export type PipeIn<T> = [T] extends [never] ? any : [T] extends [void] ? any : T;
91
96
  export interface Config {
92
97
  workflow: Action;
93
98
  }
@@ -95,22 +100,65 @@ type UnionToIntersection<TUnion> = (TUnion extends any ? (x: TUnion) => void : n
95
100
  /** Merge a tuple of objects into a single intersection type. */
96
101
  export type MergeTuple<TTuple> = TTuple extends unknown[] ? UnionToIntersection<TTuple[number]> : never;
97
102
  /**
98
- * An action with tracked input/output types. Phantom fields enforce invariance
103
+ * Runtime dispatch table for union postfix methods. Each method is optional —
104
+ * Option and Result provide different subsets. TypedAction type signatures
105
+ * gate availability at compile time; this table handles runtime dispatch.
106
+ */
107
+ export interface UnionMethods {
108
+ map?: (action: Action) => Action;
109
+ andThen?: (action: Action) => Action;
110
+ unwrap?: () => Action;
111
+ unwrapOr?: (action: Action) => Action;
112
+ flatten?: () => Action;
113
+ filter?: (predicate: Action) => Action;
114
+ collect?: () => Action;
115
+ isSome?: () => Action;
116
+ isNone?: () => Action;
117
+ mapErr?: (action: Action) => Action;
118
+ and?: (other: Action) => Action;
119
+ or?: (fallback: Action) => Action;
120
+ toOption?: () => Action;
121
+ toOptionErr?: () => Action;
122
+ transpose?: () => Action;
123
+ isOk?: () => Action;
124
+ isErr?: () => Action;
125
+ }
126
+ /**
127
+ * Runtime dispatch info attached to every TypedAction. Non-null when the
128
+ * output is a union type (Option, Result). `name` identifies the type for
129
+ * error messages; `methods` is the dispatch table.
130
+ */
131
+ export interface UnionDispatch {
132
+ name: string;
133
+ methods: UnionMethods;
134
+ }
135
+ /**
136
+ * Attach a union dispatch table to a TypedAction. Used by constructors
137
+ * and family-preserving combinators so postfix methods can dispatch
138
+ * to the correct implementation.
139
+ */
140
+ export declare function withUnion<In, Out>(action: TypedAction<In, Out>, name: string, methods: UnionMethods): TypedAction<In, Out>;
141
+ /**
142
+ * An action with tracked input/output types. Phantom fields enforce variance
99
143
  * and are never set at runtime — they exist only for the TypeScript compiler.
100
144
  *
101
- * Each type variable gets a contravariant + covariant field pair:
102
145
  * In: __in (contravariant) + __in_co (covariant) → invariant
103
- * Out: __out (covariant) + __out_contra (contravariant) → invariant
146
+ * Out: __out (covariant only)
104
147
  *
105
- * This ensures exact type matching at every pipeline connection point.
148
+ * Input invariance ensures exact type matching at pipeline connection points.
106
149
  * Data crosses serialization boundaries to handlers in arbitrary languages
107
150
  * (Rust, Python, etc.), so extra/missing fields are runtime errors.
151
+ *
152
+ * Output covariance is safe — a step producing Dog where Animal is expected
153
+ * downstream works. `never` (throwError, recur, done) is assignable to any
154
+ * output slot via standard subtyping.
108
155
  */
109
156
  export type TypedAction<In = unknown, Out = unknown> = Action & {
110
157
  __in?: (input: In) => void;
111
158
  __in_co?: In;
112
159
  __out?: () => Out;
113
- __out_contra?: (output: Out) => void;
160
+ /** Runtime dispatch info for union postfix methods (Option/Result). Null for non-union outputs. */
161
+ __union: UnionDispatch | null;
114
162
  /** Chain this action with another. `a.then(b)` ≡ `chain(a, b)`. */
115
163
  then<TNext>(next: Pipeable<Out, TNext>): TypedAction<In, TNext>;
116
164
  /** Apply an action to each element of an array output. `a.forEach(b)` ≡ `a.then(forEach(b))`. */
@@ -119,10 +167,13 @@ export type TypedAction<In = unknown, Out = unknown> = Action & {
119
167
  branch<TCases extends {
120
168
  [K in BranchKeys<Out>]: CaseHandler<BranchPayload<Out, K>, unknown>;
121
169
  }>(cases: [BranchKeys<Out>] extends [never] ? never : TCases): TypedAction<In, ExtractOutput<TCases[keyof TCases & string]>>;
122
- /** Flatten a nested array output. `a.flatten()` `pipe(a, flatten())`. */
123
- flatten(): TypedAction<In, Out extends (infer TElement)[][] ? TElement[] : Out>;
170
+ /** Flatten one level of nesting. Dispatches: Array, Option, Result. */
171
+ flatten<TIn, TElement>(this: TypedAction<TIn, TElement[][]>): TypedAction<TIn, TElement[]>;
172
+ flatten<TIn, TValue>(this: TypedAction<TIn, Option<Option<TValue>>>): TypedAction<TIn, Option<TValue>>;
173
+ flatten<TIn, TValue, TError>(this: TypedAction<TIn, Result<Result<TValue, TError>, TError>>): TypedAction<TIn, Result<TValue, TError>>;
174
+ flatten(): TypedAction<In, unknown>;
124
175
  /** Discard output. `a.drop()` ≡ `pipe(a, drop)`. */
125
- drop(): TypedAction<In, never>;
176
+ drop(): TypedAction<In, void>;
126
177
  /** Wrap output as a tagged union member. Requires full variant map TDef so __def is carried. */
127
178
  tag<TDef extends Record<string, unknown>, TKind extends keyof TDef & string>(kind: TKind): TypedAction<In, TaggedUnion<TDef>>;
128
179
  /** Extract a field from the output object. `a.getField("name")` ≡ `pipe(a, getField("name"))`. */
@@ -140,44 +191,77 @@ export type TypedAction<In = unknown, Out = unknown> = Action & {
140
191
  /** Init/last decomposition. Only callable when Out is TElement[]. */
141
192
  splitLast<TIn, TElement>(this: TypedAction<TIn, TElement[]>): TypedAction<TIn, Option<[TElement[], TElement]>>;
142
193
  /**
143
- * Transform the Some value inside an Option output. Only callable when
144
- * Out is Option<T>. Uses `this` parameter constraint to gate availability.
194
+ * Transform the inner value. Dispatches: Option.map, Result.map.
145
195
  */
146
- mapOption<TIn, T, U>(this: TypedAction<TIn, Option<T>>, action: Pipeable<T, U>): TypedAction<TIn, Option<U>>;
196
+ map<TIn, T, U>(this: TypedAction<TIn, Option<T>>, action: Pipeable<T, U>): TypedAction<TIn, Option<U>>;
197
+ map<TIn, TValue, TOut, TError>(this: TypedAction<TIn, Result<TValue, TError>>, action: Pipeable<TValue, TOut>): TypedAction<TIn, Result<TOut, TError>>;
147
198
  /**
148
199
  * Transform the Err value of a Result output.
149
200
  * `Result<TValue, TError> → Result<TValue, TErrorOut>`
150
- *
151
- * Only callable when Out is Result<TValue, TError>.
152
201
  */
153
202
  mapErr<TIn, TValue, TError, TErrorOut>(this: TypedAction<TIn, Result<TValue, TError>>, action: Pipeable<TError, TErrorOut>): TypedAction<TIn, Result<TValue, TErrorOut>>;
154
203
  /**
155
- * Unwrap a Result output. If Ok, pass through the value. If Err, apply
156
- * the default action. Only callable when Out is Result<TValue, TError>.
204
+ * Unwrap or panic. Dispatches: Option.unwrap, Result.unwrap.
157
205
  *
158
- * The `this` constraint provides TValue from context, so throw tokens
159
- * (Out=never) work without explicit type parameters:
160
- * `handler.unwrapOr(throwError)`
206
+ * Option: If Some, pass through value. If None, panic.
207
+ * Result: If Ok, pass through value. If Err, panic.
208
+ */
209
+ unwrap<TIn, TValue>(this: TypedAction<TIn, Option<TValue>>): TypedAction<TIn, TValue>;
210
+ unwrap<TIn, TValue, TError>(this: TypedAction<TIn, Result<TValue, TError>>): TypedAction<TIn, TValue>;
211
+ /**
212
+ * Unwrap a union output. Dispatches: Option.unwrapOr, Result.unwrapOr.
213
+ *
214
+ * Option: If Some, pass through value. If None, apply default.
215
+ * Result: If Ok, pass through value. If Err, apply default.
161
216
  *
162
- * Uses CaseHandler for defaultAction (covariant output only) so that
163
- * `TypedAction<TError, never>` is assignable to `CaseHandler<TError, TValue>`.
217
+ * Covariant output makes throw tokens (Out=never) work:
218
+ * `handler.unwrapOr(throwError)`
164
219
  */
165
- unwrapOr<TIn, TValue, TError>(this: TypedAction<TIn, Result<TValue, TError>>, defaultAction: CaseHandler<TError, TValue>): TypedAction<TIn, TValue>;
220
+ unwrapOr<TIn, TValue>(this: TypedAction<TIn, Option<TValue>>, defaultAction: Pipeable<void, TValue>): TypedAction<TIn, TValue>;
221
+ unwrapOr<TIn, TValue, TError>(this: TypedAction<TIn, Result<TValue, TError>>, defaultAction: Pipeable<TError, TValue>): TypedAction<TIn, TValue>;
222
+ /** Monadic bind. Option: `Option<T> → Option<U>`. Result: `Result<T,E> → Result<U,E>`. */
223
+ andThen<TIn, TValue, TOut>(this: TypedAction<TIn, Option<TValue>>, action: Pipeable<TValue, Option<TOut>>): TypedAction<TIn, Option<TOut>>;
224
+ andThen<TIn, TValue, TOut, TError>(this: TypedAction<TIn, Result<TValue, TError>>, action: Pipeable<TValue, Result<TOut, TError>>): TypedAction<TIn, Result<TOut, TError>>;
225
+ /** Conditional keep. If Some, apply predicate. If None, stay None. */
226
+ filter<TIn, TValue>(this: TypedAction<TIn, Option<TValue>>, predicate: Pipeable<TValue, Option<TValue>>): TypedAction<TIn, Option<TValue>>;
227
+ /** Test if the value is Some. `Option<T> → boolean` */
228
+ isSome<TIn, TValue>(this: TypedAction<TIn, Option<TValue>>): TypedAction<TIn, boolean>;
229
+ /** Test if the value is None. `Option<T> → boolean` */
230
+ isNone<TIn, TValue>(this: TypedAction<TIn, Option<TValue>>): TypedAction<TIn, boolean>;
231
+ /** Collect Some values from an array, discarding Nones. `Option<T>[] → T[]` */
232
+ collect<TIn, TValue>(this: TypedAction<TIn, Option<TValue>[]>): TypedAction<TIn, TValue[]>;
233
+ /** Fallback on Err. `Result<T,E> → Result<T,F>` */
234
+ or<TIn, TValue, TError, TErrorOut>(this: TypedAction<TIn, Result<TValue, TError>>, fallback: Pipeable<TError, Result<TValue, TErrorOut>>): TypedAction<TIn, Result<TValue, TErrorOut>>;
235
+ /** Replace Ok value with another Result. `Result<T,E> → Result<U,E>` */
236
+ and<TIn, TValue, TOut, TError>(this: TypedAction<TIn, Result<TValue, TError>>, other: Pipeable<void, Result<TOut, TError>>): TypedAction<TIn, Result<TOut, TError>>;
237
+ /** Convert Ok to Some, Err to None. `Result<T,E> → Option<T>` */
238
+ toOption<TIn, TValue, TError>(this: TypedAction<TIn, Result<TValue, TError>>): TypedAction<TIn, Option<TValue>>;
239
+ /** Convert Err to Some, Ok to None. `Result<T,E> → Option<E>` */
240
+ toOptionErr<TIn, TValue, TError>(this: TypedAction<TIn, Result<TValue, TError>>): TypedAction<TIn, Option<TError>>;
241
+ /** Test if the value is Ok. `Result<T,E> → boolean` */
242
+ isOk<TIn, TValue, TError>(this: TypedAction<TIn, Result<TValue, TError>>): TypedAction<TIn, boolean>;
243
+ /** Test if the value is Err. `Result<T,E> → boolean` */
244
+ isErr<TIn, TValue, TError>(this: TypedAction<TIn, Result<TValue, TError>>): TypedAction<TIn, boolean>;
245
+ /** Swap nesting. `Option<Result<T,E>> → Result<Option<T>,E>` or `Result<Option<T>,E> → Option<Result<T,E>>`. */
246
+ transpose<TIn, TValue, TError>(this: TypedAction<TIn, Option<Result<TValue, TError>>>): TypedAction<TIn, Result<Option<TValue>, TError>>;
247
+ transpose<TIn, TValue, TError>(this: TypedAction<TIn, Result<Option<TValue>, TError>>): TypedAction<TIn, Option<Result<TValue, TError>>>;
248
+ /** Bind concurrent values as VarRefs available throughout the body. */
249
+ bind<TBindings extends Action[], TOut>(bindings: [...TBindings], body: (vars: InferVarRefs<TBindings>) => Action & {
250
+ __out?: () => TOut;
251
+ }): TypedAction<In, TOut>;
252
+ /** Capture the pipeline input as a VarRef. */
253
+ bindInput<TOut>(body: (input: VarRef<Out>) => Action & {
254
+ __out?: () => TOut;
255
+ }): TypedAction<In, TOut>;
166
256
  };
167
257
  /**
168
- * Parameter type for pipe and combinators. Contains the same phantom fields
169
- * as TypedAction but without methods.
170
- *
171
- * Invariance: Both In and Out are invariant, matching TypedAction:
172
- * In: __in (contravariant) + __in_co (covariant) → invariant
173
- * Out: __out (covariant) + __out_contra (contravariant) → invariant
258
+ * Parameter type for pipe and combinators. Same phantom fields as TypedAction
259
+ * but without methods.
174
260
  *
175
261
  * Why no methods: TypedAction's methods (then, branch, etc.) participate in
176
262
  * TS assignability checks in complex, recursive ways that interfere with
177
263
  * generic inference in pipe overloads. Pipeable strips methods so that only
178
- * phantom fields drive inference — predictable covariant/contravariant
179
- * resolution, with invariance enforced when TS checks candidates from
180
- * both sides of a connection.
264
+ * phantom fields drive inference.
181
265
  *
182
266
  * TypedAction (with methods) is assignable to Pipeable because Pipeable
183
267
  * only requires a subset of properties.
@@ -186,24 +270,18 @@ export type Pipeable<In = unknown, Out = unknown> = Action & {
186
270
  __in?: (input: In) => void;
187
271
  __in_co?: In;
188
272
  __out?: () => Out;
189
- __out_contra?: (output: Out) => void;
190
273
  };
191
274
  /**
192
- * Contravariant-only input checking for branch case handler positions.
275
+ * Contravariant input + covariant output for branch case handler positions.
193
276
  *
194
- * Omits __in_co (covariant input) and __out_contra (contravariant output)
195
- * compared to TypedAction/Pipeable. This gives:
277
+ * Omits __in_co (covariant input) compared to Pipeable. This gives:
196
278
  * In: contravariant only (via __in)
197
279
  * Out: covariant only (via __out)
198
280
  *
199
281
  * Why contravariant input: a handler that accepts `unknown` (like drop)
200
282
  * can handle any variant. (input: unknown) => void is assignable to
201
283
  * (input: HasErrors) => void because HasErrors extends unknown.
202
- *
203
- * Why covariant output: the constraint doesn't restrict output types —
204
- * they're inferred from the actual case handlers via ExtractOutput.
205
- * TypedAction's invariant __out_contra with Out=unknown would
206
- * reject any handler with a specific output type, so we omit it.
284
+ * Pipeable's invariant input (__in_co) would reject this.
207
285
  *
208
286
  * TypedAction is assignable to CaseHandler because CaseHandler only
209
287
  * requires a subset of TypedAction's phantom fields.
@@ -333,7 +411,7 @@ export type LoopResult<TContinue, TBreak> = TaggedUnion<LoopResultDef<TContinue,
333
411
  *
334
412
  * Compiled form: `RestartHandle(id, GetIndex(0), body)`
335
413
  */
336
- export declare function recur<TIn = never, TOut = any>(bodyFn: (restart: TypedAction<TIn, never>) => Pipeable<TIn, TOut>): TypedAction<PipeIn<TIn>, TOut>;
414
+ export declare function recur<TIn = void, TOut = any>(bodyFn: (restart: TypedAction<TIn, never>) => Pipeable<TIn, TOut>): TypedAction<PipeIn<TIn>, TOut>;
337
415
  /**
338
416
  * Early return scope. The body callback receives `earlyReturn`, a TypedAction
339
417
  * that exits the scope immediately with the returned value.
@@ -346,7 +424,7 @@ export declare function recur<TIn = never, TOut = any>(bodyFn: (restart: TypedAc
346
424
  * a Branch. earlyReturn tags with Break and performs — the handler restarts
347
425
  * the body, Branch takes the Break path, and the value exits.
348
426
  */
349
- export declare function earlyReturn<TEarlyReturn = never, TIn = any, TOut = any>(bodyFn: (earlyReturn: TypedAction<TEarlyReturn, never>) => Pipeable<TIn, TOut>): TypedAction<TIn, TEarlyReturn | TOut>;
427
+ export declare function earlyReturn<TEarlyReturn = void, TIn = any, TOut = any>(bodyFn: (earlyReturn: TypedAction<TEarlyReturn, never>) => Pipeable<TIn, TOut>): TypedAction<TIn, TEarlyReturn | TOut>;
350
428
  /**
351
429
  * Build the restart+branch compiled form:
352
430
  * `Chain(Tag("Continue"), RestartHandle(id, GetIndex(0), Branch({ Continue: continueArm, Break: breakArm })))`
@@ -366,6 +444,6 @@ export declare function buildRestartBranchAction(restartHandlerId: RestartHandle
366
444
  *
367
445
  * Compiles to `RestartHandle`/`RestartPerform`/Branch — same effect substrate as tryCatch and earlyReturn.
368
446
  */
369
- export declare function loop<TBreak = never, TIn = never>(bodyFn: (recur: TypedAction<TIn, never>, done: TypedAction<VoidToNull<TBreak>, never>) => Pipeable<TIn, never>): TypedAction<PipeIn<TIn>, VoidToNull<TBreak>>;
447
+ export declare function loop<TBreak = void, TRecur = void>(bodyFn: (recur: TypedAction<TRecur, never>, done: TypedAction<VoidToNull<TBreak>, never>) => Pipeable<TRecur, never>): TypedAction<PipeIn<TRecur>, VoidToNull<TBreak>>;
370
448
  /** Simple config factory. */
371
449
  export declare function config(workflow: Action): Config;
package/dist/ast.js CHANGED
@@ -1,5 +1,23 @@
1
1
  import { chain } from "./chain.js";
2
- import { drop, flatten as flattenBuiltin, getField, getIndex, identity, merge, Option, pick, Result, splitFirst, splitLast, tag, wrapInField, } from "./builtins.js";
2
+ import { drop, flatten as flattenBuiltin, getField, getIndex, identity, merge, pick, splitFirst, splitLast, tag, wrapInField, } from "./builtins.js";
3
+ import { Option } from "./option.js";
4
+ // Lazy import — bind.ts imports from ast.ts, but these are only called inside
5
+ // methods (after all modules load), so the circular reference is safe at runtime.
6
+ import { bind as bindStandalone, bindInput as bindInputStandalone, } from "./bind.js";
7
+ /**
8
+ * Attach a union dispatch table to a TypedAction. Used by constructors
9
+ * and family-preserving combinators so postfix methods can dispatch
10
+ * to the correct implementation.
11
+ */
12
+ export function withUnion(action, name, methods) {
13
+ Object.defineProperty(action, "__union", {
14
+ value: { name, methods },
15
+ configurable: true,
16
+ enumerable: false,
17
+ writable: true,
18
+ });
19
+ return action;
20
+ }
3
21
  // ---------------------------------------------------------------------------
4
22
  // typedAction — attach .then() and .forEach() as non-enumerable methods
5
23
  // ---------------------------------------------------------------------------
@@ -14,6 +32,11 @@ function branchMethod(cases) {
14
32
  return chain(this, branch(cases));
15
33
  }
16
34
  function flattenMethod() {
35
+ const dispatch = this.__union;
36
+ if (dispatch?.methods.flatten) {
37
+ return chain(this, dispatch.methods.flatten());
38
+ }
39
+ // Fall back to array flatten
17
40
  return chain(this, flattenBuiltin());
18
41
  }
19
42
  function dropMethod() {
@@ -43,14 +66,89 @@ function splitFirstMethod() {
43
66
  function splitLastMethod() {
44
67
  return chain(this, splitLast());
45
68
  }
46
- function mapOptionMethod(action) {
47
- return chain(this, Option.map(action));
69
+ /**
70
+ * Require a dispatch method, throwing a descriptive error if missing.
71
+ * Uses the union's type name for context.
72
+ */
73
+ function requireDispatch(dispatch, methodName, accessor) {
74
+ if (!dispatch) {
75
+ throw new Error(`.${methodName}() requires a union type (Option or Result)`);
76
+ }
77
+ const method = accessor(dispatch.methods);
78
+ if (!method) {
79
+ throw new Error(`.${methodName}() is not available on ${dispatch.name}`);
80
+ }
81
+ return method;
82
+ }
83
+ function mapMethod(action) {
84
+ const map = requireDispatch(this.__union, "map", (m) => m.map);
85
+ return chain(this, map(action));
48
86
  }
49
87
  function mapErrMethod(action) {
50
- return chain(this, Result.mapErr(action));
88
+ const mapErr = requireDispatch(this.__union, "mapErr", (m) => m.mapErr);
89
+ return chain(this, mapErr(action));
90
+ }
91
+ function unwrapMethod() {
92
+ const unwrap = requireDispatch(this.__union, "unwrap", (m) => m.unwrap);
93
+ return chain(this, unwrap());
51
94
  }
52
95
  function unwrapOrMethod(defaultAction) {
53
- return chain(this, Result.unwrapOr(defaultAction));
96
+ const unwrapOr = requireDispatch(this.__union, "unwrapOr", (m) => m.unwrapOr);
97
+ return chain(this, unwrapOr(defaultAction));
98
+ }
99
+ // --- Dispatched postfix methods (via __union) ---
100
+ function andThenMethod(action) {
101
+ const andThen = requireDispatch(this.__union, "andThen", (m) => m.andThen);
102
+ return chain(this, andThen(action));
103
+ }
104
+ function filterMethod(predicate) {
105
+ const filter = requireDispatch(this.__union, "filter", (m) => m.filter);
106
+ return chain(this, filter(predicate));
107
+ }
108
+ function isSomeMethod() {
109
+ const isSome = requireDispatch(this.__union, "isSome", (m) => m.isSome);
110
+ return chain(this, isSome());
111
+ }
112
+ function isNoneMethod() {
113
+ const isNone = requireDispatch(this.__union, "isNone", (m) => m.isNone);
114
+ return chain(this, isNone());
115
+ }
116
+ function collectMethod() {
117
+ return chain(this, Option.collect());
118
+ }
119
+ function orMethod(fallback) {
120
+ const or = requireDispatch(this.__union, "or", (m) => m.or);
121
+ return chain(this, or(fallback));
122
+ }
123
+ function andPostfixMethod(other) {
124
+ const and = requireDispatch(this.__union, "and", (m) => m.and);
125
+ return chain(this, and(other));
126
+ }
127
+ function toOptionMethod() {
128
+ const toOption = requireDispatch(this.__union, "toOption", (m) => m.toOption);
129
+ return chain(this, toOption());
130
+ }
131
+ function toOptionErrMethod() {
132
+ const toOptionErr = requireDispatch(this.__union, "toOptionErr", (m) => m.toOptionErr);
133
+ return chain(this, toOptionErr());
134
+ }
135
+ function isOkMethod() {
136
+ const isOk = requireDispatch(this.__union, "isOk", (m) => m.isOk);
137
+ return chain(this, isOk());
138
+ }
139
+ function isErrMethod() {
140
+ const isErr = requireDispatch(this.__union, "isErr", (m) => m.isErr);
141
+ return chain(this, isErr());
142
+ }
143
+ function transposeMethod() {
144
+ const transpose = requireDispatch(this.__union, "transpose", (m) => m.transpose);
145
+ return chain(this, transpose());
146
+ }
147
+ function bindMethod(bindings, body) {
148
+ return chain(this, bindStandalone(bindings, body));
149
+ }
150
+ function bindInputMethod(body) {
151
+ return chain(this, bindInputStandalone(body));
54
152
  }
55
153
  /**
56
154
  * Attach `.then()` and `.forEach()` methods to a plain Action object.
@@ -59,6 +157,7 @@ function unwrapOrMethod(defaultAction) {
59
157
  export function typedAction(action) {
60
158
  if (!("then" in action)) {
61
159
  Object.defineProperties(action, {
160
+ __union: { value: null, configurable: true, enumerable: false, writable: true },
62
161
  then: { value: thenMethod, configurable: true },
63
162
  forEach: { value: forEachMethod, configurable: true },
64
163
  branch: { value: branchMethod, configurable: true },
@@ -72,9 +171,24 @@ export function typedAction(action) {
72
171
  pick: { value: pickMethod, configurable: true },
73
172
  splitFirst: { value: splitFirstMethod, configurable: true },
74
173
  splitLast: { value: splitLastMethod, configurable: true },
75
- mapOption: { value: mapOptionMethod, configurable: true },
174
+ map: { value: mapMethod, configurable: true },
76
175
  mapErr: { value: mapErrMethod, configurable: true },
176
+ unwrap: { value: unwrapMethod, configurable: true },
77
177
  unwrapOr: { value: unwrapOrMethod, configurable: true },
178
+ andThen: { value: andThenMethod, configurable: true },
179
+ filter: { value: filterMethod, configurable: true },
180
+ isSome: { value: isSomeMethod, configurable: true },
181
+ isNone: { value: isNoneMethod, configurable: true },
182
+ collect: { value: collectMethod, configurable: true },
183
+ or: { value: orMethod, configurable: true },
184
+ and: { value: andPostfixMethod, configurable: true },
185
+ toOption: { value: toOptionMethod, configurable: true },
186
+ toOptionErr: { value: toOptionErrMethod, configurable: true },
187
+ isOk: { value: isOkMethod, configurable: true },
188
+ isErr: { value: isErrMethod, configurable: true },
189
+ transpose: { value: transposeMethod, configurable: true },
190
+ bind: { value: bindMethod, configurable: true },
191
+ bindInput: { value: bindInputMethod, configurable: true },
78
192
  });
79
193
  }
80
194
  return action;
package/dist/bind.d.ts CHANGED
@@ -6,17 +6,14 @@ import { type Action, type ExtractInput, type ExtractOutput, type TypedAction }
6
6
  * action like `pick` or `getField` — pipe overloads can't infer
7
7
  * the generic's type parameter from the VarRef's output.
8
8
  */
9
- export type VarRef<TValue> = TypedAction<never, TValue>;
9
+ export type VarRef<TValue> = TypedAction<any, TValue>;
10
10
  /**
11
11
  * Maps each binding's output type to a VarRef. TypeScript resolves
12
12
  * ExtractOutput from each binding expression.
13
13
  *
14
- * Constraint is `Action[]` (not `Pipeable<any, any>[]`) because
15
- * `TypedAction<never, X>` (e.g. from `constant()`) fails the invariant
16
- * `__in` check against `Pipeable<any, any>` on the 9-variant
17
- * Action union. Using raw `Action[]` avoids the phantom field
18
- * assignability issue while `ExtractOutput` still extracts the correct
19
- * output type from the phantom fields on the concrete types.
14
+ * Constraint is `Action[]` (not `Pipeable<any, any>[]`) so that
15
+ * `ExtractOutput` extracts the correct output type from the phantom
16
+ * fields on the concrete types without fighting invariant `__in` checks.
20
17
  */
21
18
  export type InferVarRefs<TBindings extends Action[]> = {
22
19
  [K in keyof TBindings]: VarRef<ExtractOutput<TBindings[K]>>;
@@ -40,12 +37,11 @@ export type InferVarRefs<TBindings extends Action[]> = {
40
37
  */
41
38
  /**
42
39
  * Constraint for the body callback return type. Only requires the output
43
- * phantom fields — omits `__in` and `__in_co` so that body actions with
44
- * `In = never` (e.g. pipelines starting from a VarRef) are assignable.
40
+ * phantom field — omits `__in` and `__in_co` so that body actions with
41
+ * any input type (e.g. pipelines starting from a VarRef) are assignable.
45
42
  */
46
43
  type BodyResult<TOut> = Action & {
47
44
  __out?: () => TOut;
48
- __out_contra?: (output: TOut) => void;
49
45
  };
50
46
  export declare function bind<TBindings extends Action[], TOut>(bindings: [...TBindings], body: (vars: InferVarRefs<TBindings>) => BodyResult<TOut>): TypedAction<ExtractInput<TBindings[number]>, TOut>;
51
47
  /**
@@ -1,4 +1,4 @@
1
- import { type Action, type MergeTuple, type Option as OptionT, type Pipeable, type Result as ResultT, type TaggedUnion, type TypedAction } from "./ast.js";
1
+ import { type MergeTuple, type Option as OptionT, type Pipeable, type TaggedUnion, type TypedAction } from "./ast.js";
2
2
  import { z } from "zod";
3
3
  /**
4
4
  * Typed combinators for structural data transformations.
@@ -33,7 +33,14 @@ export declare function taggedUnionSchema<TDef extends Record<string, z.ZodTypeA
33
33
  }>>>;
34
34
  export declare function constant<TValue>(value: TValue): TypedAction<any, TValue>;
35
35
  export declare function identity<TValue = any>(): TypedAction<TValue, TValue>;
36
- export declare const drop: TypedAction<any, never>;
36
+ export declare const drop: TypedAction<any, void>;
37
+ /**
38
+ * Halt execution with a fatal error. Not caught by tryCatch.
39
+ * Analogous to Rust's `panic!`.
40
+ *
41
+ * Output type is `never` — a panic never produces a value.
42
+ */
43
+ export declare function panic(message: string): TypedAction<any, never>;
37
44
  /**
38
45
  * Wrap input as a tagged union member. Requires the full variant map TDef
39
46
  * so the output type carries __def for branch decomposition.
@@ -68,18 +75,6 @@ export declare function withResource<TIn extends Record<string, unknown>, TResou
68
75
  action: Pipeable<TResource & TIn, TOut>;
69
76
  dispose: Pipeable<TResource, TDisposeOut>;
70
77
  }): TypedAction<TIn, TOut>;
71
- /**
72
- * Run `action` on the input for its side effects, then discard the action's
73
- * output and return the original input unchanged. The action must accept
74
- * exactly `TInput`. Use `pick` inside the action's pipe if the inner
75
- * handler needs a subset.
76
- *
77
- * Constraint: input must be an object (uses all + merge internally).
78
- *
79
- * Example:
80
- * pipe(tap(pipe(pick("worktreePath", "description"), implement)), createPR)
81
- */
82
- export declare function tap<TInput extends Record<string, unknown>>(action: Pipeable<TInput, any>): TypedAction<TInput, TInput>;
83
78
  export declare function wrapInField<TField extends string, TValue>(field: TField): TypedAction<TValue, Record<TField, TValue>>;
84
79
  export declare function range(start: number, end: number): TypedAction<any, number[]>;
85
80
  /**
@@ -105,125 +100,4 @@ export declare function splitFirst<TElement>(): TypedAction<TElement[], OptionT<
105
100
  * that can't be composed from existing AST nodes.
106
101
  */
107
102
  export declare function splitLast<TElement>(): TypedAction<TElement[], OptionT<[TElement[], TElement]>>;
108
- /**
109
- * Extract the first element of an array.
110
- * `readonly TElement[] → Option<TElement>`
111
- *
112
- * Composes `splitFirst` (which returns `Option<[TElement, TElement[]]>`)
113
- * with `Option.map(getIndex(0))` to extract just the element.
114
- */
115
- export declare function first<TElement>(): TypedAction<readonly TElement[], OptionT<TElement>>;
116
- /**
117
- * Extract the last element of an array.
118
- * `readonly TElement[] → Option<TElement>`
119
- *
120
- * Composes `splitLast` (which returns `Option<[TElement[], TElement]>`)
121
- * with `Option.map(getIndex(1))` to extract just the element.
122
- */
123
- export declare function last<TElement>(): TypedAction<readonly TElement[], OptionT<TElement>>;
124
- /**
125
- * Option namespace. All combinators produce TypedAction AST nodes that
126
- * desugar to branch + existing builtins, except collect which uses the
127
- * CollectSome builtin.
128
- */
129
- export declare const Option: {
130
- /** Wrap a value as Some. `T → Option<T>` */
131
- readonly some: <T>() => TypedAction<T, OptionT<T>>;
132
- /** Produce a None. `never → Option<T>` */
133
- readonly none: <T>() => TypedAction<never, OptionT<T>>;
134
- /** Transform the Some value. `Option<T> → Option<U>` */
135
- readonly map: <T, U>(action: Pipeable<T, U>) => TypedAction<OptionT<T>, OptionT<U>>;
136
- /**
137
- * Monadic bind (flatMap). If Some, pass the value to action which
138
- * returns Option<U>. If None, stay None. `Option<T> → Option<U>`
139
- */
140
- readonly andThen: <T, U>(action: Pipeable<T, OptionT<U>>) => TypedAction<OptionT<T>, OptionT<U>>;
141
- /**
142
- * Extract the Some value or produce a default from an action.
143
- * `Option<T> → T`
144
- *
145
- * The None branch drops its void payload before calling defaultAction,
146
- * matching Rust's `unwrap_or_else(|| default)`.
147
- */
148
- readonly unwrapOr: <T>(defaultAction: Pipeable<never, T>) => TypedAction<OptionT<T>, T>;
149
- /** Unwrap a nested Option. `Option<Option<T>> → Option<T>` */
150
- readonly flatten: <T>() => TypedAction<OptionT<OptionT<T>>, OptionT<T>>;
151
- /**
152
- * Conditional keep. If Some, pass value to predicate which returns
153
- * Option<T>. If None, stay None. `Option<T> → Option<T>`
154
- */
155
- readonly filter: <T>(predicate: Pipeable<T, OptionT<T>>) => TypedAction<OptionT<T>, OptionT<T>>;
156
- /**
157
- * Collect Some values from an array, discarding Nones.
158
- * `Option<T>[] → T[]`
159
- */
160
- readonly collect: <T = any>() => TypedAction<OptionT<T>[], T[]>;
161
- /** Test if the value is Some. `Option<T> → boolean` */
162
- readonly isSome: <T>() => TypedAction<OptionT<T>, boolean>;
163
- /** Test if the value is None. `Option<T> → boolean` */
164
- readonly isNone: <T>() => TypedAction<OptionT<T>, boolean>;
165
- /**
166
- * Build a Zod schema for `Option<T>`.
167
- *
168
- * ```ts
169
- * const schema = Option.schema(z.string());
170
- * // validates: { kind: "Some", value: "hello" } or { kind: "None", value: null }
171
- * ```
172
- */
173
- readonly schema: <TValue>(valueSchema: z.ZodType<TValue>) => z.ZodType<OptionT<TValue>>;
174
- };
175
- export declare const Result: {
176
- /** Wrap a value as Ok. `TValue → Result<TValue, TError>` */
177
- readonly ok: <TValue, TError>() => TypedAction<TValue, ResultT<TValue, TError>>;
178
- /** Wrap a value as Err. `TError → Result<TValue, TError>` */
179
- readonly err: <TValue, TError>() => TypedAction<TError, ResultT<TValue, TError>>;
180
- /** Transform the Ok value. `Result<TValue, TError> → Result<TOut, TError>` */
181
- readonly map: <TValue, TOut, TError>(action: Pipeable<TValue, TOut>) => TypedAction<ResultT<TValue, TError>, ResultT<TOut, TError>>;
182
- /** Transform the Err value. `Result<TValue, TError> → Result<TValue, TErrorOut>` */
183
- readonly mapErr: <TValue, TError, TErrorOut>(action: Pipeable<TError, TErrorOut>) => TypedAction<ResultT<TValue, TError>, ResultT<TValue, TErrorOut>>;
184
- /**
185
- * Monadic bind (flatMap) for Ok. If Ok, pass value to action which
186
- * returns Result<TOut, TError>. If Err, propagate.
187
- */
188
- readonly andThen: <TValue, TOut, TError>(action: Pipeable<TValue, ResultT<TOut, TError>>) => TypedAction<ResultT<TValue, TError>, ResultT<TOut, TError>>;
189
- /** Fallback on Err. If Ok, keep it. If Err, pass error to fallback. */
190
- readonly or: <TValue, TError, TErrorOut>(fallback: Pipeable<TError, ResultT<TValue, TErrorOut>>) => TypedAction<ResultT<TValue, TError>, ResultT<TValue, TErrorOut>>;
191
- /** Replace Ok value with another Result. If Ok, discard value and return other. */
192
- readonly and: <TValue, TOut, TError>(other: Pipeable<never, ResultT<TOut, TError>>) => TypedAction<ResultT<TValue, TError>, ResultT<TOut, TError>>;
193
- /**
194
- * Extract Ok or compute default from Err. `Result<TValue, TError> → TValue`
195
- *
196
- * Uses covariant output checking so throw tokens (Out=never) are assignable.
197
- * For inference-free usage with throw tokens, prefer the postfix method:
198
- * `handler.unwrapOr(throwError)`.
199
- */
200
- readonly unwrapOr: <TValue, TError>(defaultAction: Action & {
201
- __in?: (input: TError) => void;
202
- __out?: () => TValue;
203
- }) => TypedAction<ResultT<TValue, TError>, TValue>;
204
- /** Unwrap nested Result. `Result<Result<TValue, TError>, TError> → Result<TValue, TError>` */
205
- readonly flatten: <TValue, TError>() => TypedAction<ResultT<ResultT<TValue, TError>, TError>, ResultT<TValue, TError>>;
206
- /** Convert Ok to Some, Err to None. `Result<TValue, TError> → Option<TValue>` */
207
- readonly toOption: <TValue, TError>() => TypedAction<ResultT<TValue, TError>, OptionT<TValue>>;
208
- /** Convert Err to Some, Ok to None. `Result<TValue, TError> → Option<TError>` */
209
- readonly toOptionErr: <TValue, TError>() => TypedAction<ResultT<TValue, TError>, OptionT<TError>>;
210
- /**
211
- * Swap Result/Option nesting.
212
- * `Result<Option<TValue>, TError> → Option<Result<TValue, TError>>`
213
- */
214
- readonly transpose: <TValue, TError>() => TypedAction<ResultT<OptionT<TValue>, TError>, OptionT<ResultT<TValue, TError>>>;
215
- /** Test if the value is Ok. `Result<TValue, TError> → boolean` */
216
- readonly isOk: <TValue, TError>() => TypedAction<ResultT<TValue, TError>, boolean>;
217
- /** Test if the value is Err. `Result<TValue, TError> → boolean` */
218
- readonly isErr: <TValue, TError>() => TypedAction<ResultT<TValue, TError>, boolean>;
219
- /**
220
- * Build a Zod schema for `Result<TValue, TError>`.
221
- *
222
- * ```ts
223
- * const schema = Result.schema(z.string(), z.number());
224
- * // validates: { kind: "Ok", value: "hello" } or { kind: "Err", value: 42 }
225
- * ```
226
- */
227
- readonly schema: <TValue, TError>(okSchema: z.ZodType<TValue>, errSchema: z.ZodType<TError>) => z.ZodType<ResultT<TValue, TError>>;
228
- };
229
103
  export {};