@barnum/barnum 0.0.0-main-8d724777 → 0.0.0-main-50effdfd

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
@@ -78,6 +78,10 @@ export type BuiltinKind = {
78
78
  value: string[];
79
79
  } | {
80
80
  kind: "CollectSome";
81
+ } | {
82
+ kind: "SplitFirst";
83
+ } | {
84
+ kind: "SplitLast";
81
85
  } | {
82
86
  kind: "WrapInField";
83
87
  value: string;
@@ -132,10 +136,16 @@ export type TypedAction<In = unknown, Out = unknown, Refs extends string = never
132
136
  tag<TDef extends Record<string, unknown>, TKind extends keyof TDef & string>(kind: TKind): TypedAction<In, TaggedUnion<TDef>, Refs>;
133
137
  /** Extract a field from the output object. `a.get("name")` ≡ `pipe(a, extractField("name"))`. */
134
138
  get<TField extends keyof Out & string>(field: TField): TypedAction<In, Out[TField], Refs>;
139
+ /** Wrap output in an object under a field name. `a.wrapInField("foo")` ≡ `pipe(a, wrapInField("foo"))`. */
140
+ wrapInField<TField extends string>(field: TField): TypedAction<In, Record<TField, Out>, Refs>;
135
141
  /** Merge a tuple of objects into a single object. `a.merge()` ≡ `pipe(a, merge())`. */
136
142
  merge(): TypedAction<In, MergeTuple<Out>, Refs>;
137
143
  /** Select fields from the output. `a.pick("x", "y")` ≡ `pipe(a, pick("x", "y"))`. */
138
144
  pick<TKeys extends (keyof Out & string)[]>(...keys: TKeys): TypedAction<In, Pick<Out, TKeys[number]>, Refs>;
145
+ /** Head/tail decomposition. Only callable when Out is TElement[]. */
146
+ splitFirst<TIn, TElement, TRefs extends string>(this: TypedAction<TIn, TElement[], TRefs>): TypedAction<TIn, Option<[TElement, TElement[]]>, TRefs>;
147
+ /** Init/last decomposition. Only callable when Out is TElement[]. */
148
+ splitLast<TIn, TElement, TRefs extends string>(this: TypedAction<TIn, TElement[], TRefs>): TypedAction<TIn, Option<[TElement[], TElement]>, TRefs>;
139
149
  /**
140
150
  * Transform the Some value inside an Option output. Only callable when
141
151
  * Out is Option<T>. Uses `this` parameter constraint to gate availability.
package/dist/ast.js CHANGED
@@ -62,6 +62,19 @@ function getMethod(field) {
62
62
  },
63
63
  });
64
64
  }
65
+ function wrapInFieldMethod(field) {
66
+ return typedAction({
67
+ kind: "Chain",
68
+ first: this,
69
+ rest: {
70
+ kind: "Invoke",
71
+ handler: {
72
+ kind: "Builtin",
73
+ builtin: { kind: "WrapInField", value: field },
74
+ },
75
+ },
76
+ });
77
+ }
65
78
  function mergeMethod() {
66
79
  return typedAction({
67
80
  kind: "Chain",
@@ -82,6 +95,26 @@ function pickMethod(...keys) {
82
95
  },
83
96
  });
84
97
  }
98
+ function splitFirstMethod() {
99
+ return typedAction({
100
+ kind: "Chain",
101
+ first: this,
102
+ rest: {
103
+ kind: "Invoke",
104
+ handler: { kind: "Builtin", builtin: { kind: "SplitFirst" } },
105
+ },
106
+ });
107
+ }
108
+ function splitLastMethod() {
109
+ return typedAction({
110
+ kind: "Chain",
111
+ first: this,
112
+ rest: {
113
+ kind: "Invoke",
114
+ handler: { kind: "Builtin", builtin: { kind: "SplitLast" } },
115
+ },
116
+ });
117
+ }
85
118
  function mapOptionMethod(action) {
86
119
  // Desugars to: self.then(branch({ Some: pipe(action, tag("Some")), None: tag("None") }))
87
120
  // But branch auto-unwraps value, so:
@@ -170,8 +203,11 @@ export function typedAction(action) {
170
203
  drop: { value: dropMethod, configurable: true },
171
204
  tag: { value: tagMethod, configurable: true },
172
205
  get: { value: getMethod, configurable: true },
206
+ wrapInField: { value: wrapInFieldMethod, configurable: true },
173
207
  merge: { value: mergeMethod, configurable: true },
174
208
  pick: { value: pickMethod, configurable: true },
209
+ splitFirst: { value: splitFirstMethod, configurable: true },
210
+ splitLast: { value: splitLastMethod, configurable: true },
175
211
  mapOption: { value: mapOptionMethod, configurable: true },
176
212
  mapErr: { value: mapErrMethod, configurable: true },
177
213
  unwrapOr: { value: unwrapOrMethod, configurable: true },
@@ -1,4 +1,4 @@
1
- import { type Action, type Option as OptionT, type Pipeable, type Result as ResultT, type TaggedUnion, type TypedAction } from "./ast.js";
1
+ import { type Action, type MergeTuple, type Option as OptionT, type Pipeable, type Result as ResultT, type TaggedUnion, type TypedAction } from "./ast.js";
2
2
  /**
3
3
  * Typed combinators for structural data transformations.
4
4
  *
@@ -16,40 +16,7 @@ export declare const drop: TypedAction<any, never>;
16
16
  * input: string → output: TaggedUnion<{ Ok: string; Err: number }>
17
17
  */
18
18
  export declare function tag<TDef extends Record<string, unknown>, TKind extends keyof TDef & string>(kind: TKind): TypedAction<TDef[TKind], TaggedUnion<TDef>>;
19
- type Obj = Record<string, unknown>;
20
- export declare function merge<A extends Obj, B extends Obj>(): TypedAction<[
21
- A,
22
- B
23
- ], A & B>;
24
- export declare function merge<A extends Obj, B extends Obj, C extends Obj>(): TypedAction<[A, B, C], A & B & C>;
25
- export declare function merge<A extends Obj, B extends Obj, C extends Obj, D extends Obj>(): TypedAction<[A, B, C, D], A & B & C & D>;
26
- export declare function merge<A extends Obj, B extends Obj, C extends Obj, D extends Obj, E extends Obj>(): TypedAction<[A, B, C, D, E], A & B & C & D & E>;
27
- export declare function merge<A extends Obj, B extends Obj, C extends Obj, D extends Obj, E extends Obj, F extends Obj>(): TypedAction<[A, B, C, D, E, F], A & B & C & D & E & F>;
28
- export declare function merge<A extends Obj, B extends Obj, C extends Obj, D extends Obj, E extends Obj, F extends Obj, G extends Obj>(): TypedAction<[A, B, C, D, E, F, G], A & B & C & D & E & F & G>;
29
- export declare function merge<A extends Obj, B extends Obj, C extends Obj, D extends Obj, E extends Obj, F extends Obj, G extends Obj, H extends Obj>(): TypedAction<[A, B, C, D, E, F, G, H], A & B & C & D & E & F & G & H>;
30
- export declare function merge<A extends Obj, B extends Obj, C extends Obj, D extends Obj, E extends Obj, F extends Obj, G extends Obj, H extends Obj, I extends Obj>(): TypedAction<[
31
- A,
32
- B,
33
- C,
34
- D,
35
- E,
36
- F,
37
- G,
38
- H,
39
- I
40
- ], A & B & C & D & E & F & G & H & I>;
41
- export declare function merge<A extends Obj, B extends Obj, C extends Obj, D extends Obj, E extends Obj, F extends Obj, G extends Obj, H extends Obj, I extends Obj, J extends Obj>(): TypedAction<[
42
- A,
43
- B,
44
- C,
45
- D,
46
- E,
47
- F,
48
- G,
49
- H,
50
- I,
51
- J
52
- ], A & B & C & D & E & F & G & H & I & J>;
19
+ export declare function merge<TTuple extends Record<string, unknown>[]>(): TypedAction<TTuple, MergeTuple<TTuple>>;
53
20
  export declare function flatten<TElement>(): TypedAction<TElement[][], TElement[]>;
54
21
  export declare function extractField<TObj extends Record<string, unknown>, TField extends keyof TObj & string>(field: TField): TypedAction<TObj, TObj[TField]>;
55
22
  export declare function extractIndex<TTuple extends unknown[], TIndex extends number>(index: TIndex): TypedAction<TTuple, TTuple[TIndex]>;
@@ -90,6 +57,29 @@ export declare function withResource<TIn extends Record<string, unknown>, TResou
90
57
  export declare function tap<TInput extends Record<string, unknown>>(action: Pipeable<TInput, any>): TypedAction<TInput, TInput>;
91
58
  export declare function wrapInField<TField extends string, TValue>(field: TField): TypedAction<TValue, Record<TField, TValue>>;
92
59
  export declare function range(start: number, end: number): TypedAction<any, number[]>;
60
+ /**
61
+ * Deconstruct an array into its first element and the remaining elements.
62
+ * `TElement[] → Option<[TElement, TElement[]]>`
63
+ *
64
+ * Returns `Some([first, rest])` for non-empty arrays, `None` for empty arrays.
65
+ * This is the array equivalent of cons/uncons — enables recursive iteration
66
+ * patterns via `loop` + `splitFirst` + `branch`.
67
+ *
68
+ * This is a builtin (SplitFirst) because it requires array-length branching
69
+ * that can't be composed from existing AST nodes.
70
+ */
71
+ export declare function splitFirst<TElement>(): TypedAction<TElement[], OptionT<[TElement, TElement[]]>>;
72
+ /**
73
+ * Deconstruct an array into the leading elements and the last element.
74
+ * `TElement[] → Option<[TElement[], TElement]>`
75
+ *
76
+ * Returns `Some([init, last])` for non-empty arrays, `None` for empty arrays.
77
+ * Mirror of `splitFirst` — enables processing from the tail end.
78
+ *
79
+ * This is a builtin (SplitLast) because it requires array-length branching
80
+ * that can't be composed from existing AST nodes.
81
+ */
82
+ export declare function splitLast<TElement>(): TypedAction<TElement[], OptionT<[TElement[], TElement]>>;
93
83
  /**
94
84
  * Option namespace. All combinators produce TypedAction AST nodes that
95
85
  * desugar to branch + existing builtins, except collect which uses the
@@ -276,4 +266,3 @@ export declare const Result: {
276
266
  */
277
267
  readonly isErr: <TValue, TError>() => TypedAction<ResultT<TValue, TError>, boolean>;
278
268
  };
279
- export {};
package/dist/builtins.js CHANGED
@@ -45,6 +45,9 @@ export function tag(kind) {
45
45
  handler: { kind: "Builtin", builtin: { kind: "Tag", value: kind } },
46
46
  });
47
47
  }
48
+ // ---------------------------------------------------------------------------
49
+ // Merge — merge a tuple of objects into a single object
50
+ // ---------------------------------------------------------------------------
48
51
  export function merge() {
49
52
  return typedAction({
50
53
  kind: "Invoke",
@@ -224,6 +227,45 @@ export function range(start, end) {
224
227
  });
225
228
  }
226
229
  // ---------------------------------------------------------------------------
230
+ // SplitFirst — head/tail decomposition of an array
231
+ // ---------------------------------------------------------------------------
232
+ /**
233
+ * Deconstruct an array into its first element and the remaining elements.
234
+ * `TElement[] → Option<[TElement, TElement[]]>`
235
+ *
236
+ * Returns `Some([first, rest])` for non-empty arrays, `None` for empty arrays.
237
+ * This is the array equivalent of cons/uncons — enables recursive iteration
238
+ * patterns via `loop` + `splitFirst` + `branch`.
239
+ *
240
+ * This is a builtin (SplitFirst) because it requires array-length branching
241
+ * that can't be composed from existing AST nodes.
242
+ */
243
+ export function splitFirst() {
244
+ return typedAction({
245
+ kind: "Invoke",
246
+ handler: { kind: "Builtin", builtin: { kind: "SplitFirst" } },
247
+ });
248
+ }
249
+ // ---------------------------------------------------------------------------
250
+ // SplitLast — init/last decomposition of an array
251
+ // ---------------------------------------------------------------------------
252
+ /**
253
+ * Deconstruct an array into the leading elements and the last element.
254
+ * `TElement[] → Option<[TElement[], TElement]>`
255
+ *
256
+ * Returns `Some([init, last])` for non-empty arrays, `None` for empty arrays.
257
+ * Mirror of `splitFirst` — enables processing from the tail end.
258
+ *
259
+ * This is a builtin (SplitLast) because it requires array-length branching
260
+ * that can't be composed from existing AST nodes.
261
+ */
262
+ export function splitLast() {
263
+ return typedAction({
264
+ kind: "Invoke",
265
+ handler: { kind: "Builtin", builtin: { kind: "SplitLast" } },
266
+ });
267
+ }
268
+ // ---------------------------------------------------------------------------
227
269
  // Option namespace — combinators for Option<T> tagged unions
228
270
  // ---------------------------------------------------------------------------
229
271
  // Shared AST fragments for Option desugaring
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import type { TaggedUnion, OptionDef, ResultDef } from "./ast.js";
2
2
  export * from "./ast.js";
3
- export { constant, identity, drop, tag, merge, flatten, extractField, extractIndex, pick, dropResult, withResource, tap, range, wrapInField, Option, Result, } from "./builtins.js";
3
+ export { constant, identity, drop, tag, merge, flatten, extractField, extractIndex, pick, dropResult, withResource, tap, range, splitFirst, splitLast, wrapInField, Option, Result, } from "./builtins.js";
4
4
  export * from "./handler.js";
5
5
  export { runPipeline, type RunPipelineOptions, type LogLevel } from "./run.js";
6
6
  export { zodToCheckedJsonSchema } from "./schema.js";
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  export * from "./ast.js";
2
- export { constant, identity, drop, tag, merge, flatten, extractField, extractIndex, pick, dropResult, withResource, tap, range, wrapInField, Option, Result, } from "./builtins.js";
2
+ export { constant, identity, drop, tag, merge, flatten, extractField, extractIndex, pick, dropResult, withResource, tap, range, splitFirst, splitLast, wrapInField, Option, Result, } from "./builtins.js";
3
3
  export * from "./handler.js";
4
4
  export { runPipeline } from "./run.js";
5
5
  export { zodToCheckedJsonSchema } from "./schema.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@barnum/barnum",
3
- "version": "0.0.0-main-8d724777",
3
+ "version": "0.0.0-main-50effdfd",
4
4
  "description": "Barnum workflow engine",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
package/src/ast.ts CHANGED
@@ -95,6 +95,8 @@ export type BuiltinKind =
95
95
  | { kind: "ExtractIndex"; value: number }
96
96
  | { kind: "Pick"; value: string[] }
97
97
  | { kind: "CollectSome" }
98
+ | { kind: "SplitFirst" }
99
+ | { kind: "SplitLast" }
98
100
  | { kind: "WrapInField"; value: string }
99
101
  | { kind: "Sleep"; value: number };
100
102
 
@@ -185,12 +187,24 @@ export type TypedAction<
185
187
  get<TField extends keyof Out & string>(
186
188
  field: TField,
187
189
  ): TypedAction<In, Out[TField], Refs>;
190
+ /** Wrap output in an object under a field name. `a.wrapInField("foo")` ≡ `pipe(a, wrapInField("foo"))`. */
191
+ wrapInField<TField extends string>(
192
+ field: TField,
193
+ ): TypedAction<In, Record<TField, Out>, Refs>;
188
194
  /** Merge a tuple of objects into a single object. `a.merge()` ≡ `pipe(a, merge())`. */
189
195
  merge(): TypedAction<In, MergeTuple<Out>, Refs>;
190
196
  /** Select fields from the output. `a.pick("x", "y")` ≡ `pipe(a, pick("x", "y"))`. */
191
197
  pick<TKeys extends (keyof Out & string)[]>(
192
198
  ...keys: TKeys
193
199
  ): TypedAction<In, Pick<Out, TKeys[number]>, Refs>;
200
+ /** Head/tail decomposition. Only callable when Out is TElement[]. */
201
+ splitFirst<TIn, TElement, TRefs extends string>(
202
+ this: TypedAction<TIn, TElement[], TRefs>,
203
+ ): TypedAction<TIn, Option<[TElement, TElement[]]>, TRefs>;
204
+ /** Init/last decomposition. Only callable when Out is TElement[]. */
205
+ splitLast<TIn, TElement, TRefs extends string>(
206
+ this: TypedAction<TIn, TElement[], TRefs>,
207
+ ): TypedAction<TIn, Option<[TElement[], TElement]>, TRefs>;
194
208
  /**
195
209
  * Transform the Some value inside an Option output. Only callable when
196
210
  * Out is Option<T>. Uses `this` parameter constraint to gate availability.
@@ -428,6 +442,20 @@ function getMethod(this: TypedAction, field: string): TypedAction {
428
442
  });
429
443
  }
430
444
 
445
+ function wrapInFieldMethod(this: TypedAction, field: string): TypedAction {
446
+ return typedAction({
447
+ kind: "Chain",
448
+ first: this,
449
+ rest: {
450
+ kind: "Invoke",
451
+ handler: {
452
+ kind: "Builtin",
453
+ builtin: { kind: "WrapInField", value: field },
454
+ },
455
+ },
456
+ });
457
+ }
458
+
431
459
  function mergeMethod(this: TypedAction): TypedAction {
432
460
  return typedAction({
433
461
  kind: "Chain",
@@ -450,6 +478,28 @@ function pickMethod(this: TypedAction, ...keys: string[]): TypedAction {
450
478
  });
451
479
  }
452
480
 
481
+ function splitFirstMethod(this: TypedAction): TypedAction {
482
+ return typedAction({
483
+ kind: "Chain",
484
+ first: this,
485
+ rest: {
486
+ kind: "Invoke",
487
+ handler: { kind: "Builtin", builtin: { kind: "SplitFirst" } },
488
+ },
489
+ });
490
+ }
491
+
492
+ function splitLastMethod(this: TypedAction): TypedAction {
493
+ return typedAction({
494
+ kind: "Chain",
495
+ first: this,
496
+ rest: {
497
+ kind: "Invoke",
498
+ handler: { kind: "Builtin", builtin: { kind: "SplitLast" } },
499
+ },
500
+ });
501
+ }
502
+
453
503
  function mapOptionMethod(this: TypedAction, action: Action): TypedAction {
454
504
  // Desugars to: self.then(branch({ Some: pipe(action, tag("Some")), None: tag("None") }))
455
505
  // But branch auto-unwraps value, so:
@@ -545,8 +595,11 @@ export function typedAction<
545
595
  drop: { value: dropMethod, configurable: true },
546
596
  tag: { value: tagMethod, configurable: true },
547
597
  get: { value: getMethod, configurable: true },
598
+ wrapInField: { value: wrapInFieldMethod, configurable: true },
548
599
  merge: { value: mergeMethod, configurable: true },
549
600
  pick: { value: pickMethod, configurable: true },
601
+ splitFirst: { value: splitFirstMethod, configurable: true },
602
+ splitLast: { value: splitLastMethod, configurable: true },
550
603
  mapOption: { value: mapOptionMethod, configurable: true },
551
604
  mapErr: { value: mapErrMethod, configurable: true },
552
605
  unwrapOr: { value: unwrapOrMethod, configurable: true },
package/src/builtins.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import {
2
2
  type Action,
3
+ type MergeTuple,
3
4
  type Option as OptionT,
4
5
  type Pipeable,
5
6
  type Result as ResultT,
@@ -70,87 +71,10 @@ export function tag<
70
71
  // Merge — merge a tuple of objects into a single object
71
72
  // ---------------------------------------------------------------------------
72
73
 
73
- type Obj = Record<string, unknown>;
74
-
75
- export function merge<A extends Obj, B extends Obj>(): TypedAction<
76
- [A, B],
77
- A & B
78
- >;
79
- export function merge<
80
- A extends Obj,
81
- B extends Obj,
82
- C extends Obj,
83
- >(): TypedAction<[A, B, C], A & B & C>;
84
- export function merge<
85
- A extends Obj,
86
- B extends Obj,
87
- C extends Obj,
88
- D extends Obj,
89
- >(): TypedAction<[A, B, C, D], A & B & C & D>;
90
- export function merge<
91
- A extends Obj,
92
- B extends Obj,
93
- C extends Obj,
94
- D extends Obj,
95
- E extends Obj,
96
- >(): TypedAction<[A, B, C, D, E], A & B & C & D & E>;
97
- export function merge<
98
- A extends Obj,
99
- B extends Obj,
100
- C extends Obj,
101
- D extends Obj,
102
- E extends Obj,
103
- F extends Obj,
104
- >(): TypedAction<[A, B, C, D, E, F], A & B & C & D & E & F>;
105
- export function merge<
106
- A extends Obj,
107
- B extends Obj,
108
- C extends Obj,
109
- D extends Obj,
110
- E extends Obj,
111
- F extends Obj,
112
- G extends Obj,
113
- >(): TypedAction<[A, B, C, D, E, F, G], A & B & C & D & E & F & G>;
114
- export function merge<
115
- A extends Obj,
116
- B extends Obj,
117
- C extends Obj,
118
- D extends Obj,
119
- E extends Obj,
120
- F extends Obj,
121
- G extends Obj,
122
- H extends Obj,
123
- >(): TypedAction<[A, B, C, D, E, F, G, H], A & B & C & D & E & F & G & H>;
124
- export function merge<
125
- A extends Obj,
126
- B extends Obj,
127
- C extends Obj,
128
- D extends Obj,
129
- E extends Obj,
130
- F extends Obj,
131
- G extends Obj,
132
- H extends Obj,
133
- I extends Obj,
134
- >(): TypedAction<
135
- [A, B, C, D, E, F, G, H, I],
136
- A & B & C & D & E & F & G & H & I
137
- >;
138
- export function merge<
139
- A extends Obj,
140
- B extends Obj,
141
- C extends Obj,
142
- D extends Obj,
143
- E extends Obj,
144
- F extends Obj,
145
- G extends Obj,
146
- H extends Obj,
147
- I extends Obj,
148
- J extends Obj,
149
- >(): TypedAction<
150
- [A, B, C, D, E, F, G, H, I, J],
151
- A & B & C & D & E & F & G & H & I & J
152
- >;
153
- export function merge(): Action {
74
+ export function merge<TTuple extends Record<string, unknown>[]>(): TypedAction<
75
+ TTuple,
76
+ MergeTuple<TTuple>
77
+ > {
154
78
  return typedAction({
155
79
  kind: "Invoke",
156
80
  handler: { kind: "Builtin", builtin: { kind: "Merge" } },
@@ -393,6 +317,55 @@ export function range(start: number, end: number): TypedAction<any, number[]> {
393
317
  });
394
318
  }
395
319
 
320
+ // ---------------------------------------------------------------------------
321
+ // SplitFirst — head/tail decomposition of an array
322
+ // ---------------------------------------------------------------------------
323
+
324
+ /**
325
+ * Deconstruct an array into its first element and the remaining elements.
326
+ * `TElement[] → Option<[TElement, TElement[]]>`
327
+ *
328
+ * Returns `Some([first, rest])` for non-empty arrays, `None` for empty arrays.
329
+ * This is the array equivalent of cons/uncons — enables recursive iteration
330
+ * patterns via `loop` + `splitFirst` + `branch`.
331
+ *
332
+ * This is a builtin (SplitFirst) because it requires array-length branching
333
+ * that can't be composed from existing AST nodes.
334
+ */
335
+ export function splitFirst<TElement>(): TypedAction<
336
+ TElement[],
337
+ OptionT<[TElement, TElement[]]>
338
+ > {
339
+ return typedAction({
340
+ kind: "Invoke",
341
+ handler: { kind: "Builtin", builtin: { kind: "SplitFirst" } },
342
+ });
343
+ }
344
+
345
+ // ---------------------------------------------------------------------------
346
+ // SplitLast — init/last decomposition of an array
347
+ // ---------------------------------------------------------------------------
348
+
349
+ /**
350
+ * Deconstruct an array into the leading elements and the last element.
351
+ * `TElement[] → Option<[TElement[], TElement]>`
352
+ *
353
+ * Returns `Some([init, last])` for non-empty arrays, `None` for empty arrays.
354
+ * Mirror of `splitFirst` — enables processing from the tail end.
355
+ *
356
+ * This is a builtin (SplitLast) because it requires array-length branching
357
+ * that can't be composed from existing AST nodes.
358
+ */
359
+ export function splitLast<TElement>(): TypedAction<
360
+ TElement[],
361
+ OptionT<[TElement[], TElement]>
362
+ > {
363
+ return typedAction({
364
+ kind: "Invoke",
365
+ handler: { kind: "Builtin", builtin: { kind: "SplitLast" } },
366
+ });
367
+ }
368
+
396
369
  // ---------------------------------------------------------------------------
397
370
  // Option namespace — combinators for Option<T> tagged unions
398
371
  // ---------------------------------------------------------------------------
package/src/index.ts CHANGED
@@ -15,6 +15,8 @@ export {
15
15
  withResource,
16
16
  tap,
17
17
  range,
18
+ splitFirst,
19
+ splitLast,
18
20
  wrapInField,
19
21
  Option,
20
22
  Result,