@barnum/barnum 0.0.0-main-330fcf0a → 0.0.0-main-e8b82cff

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/src/builtins.ts CHANGED
@@ -1,4 +1,12 @@
1
- import { type Action, type Option as OptionT, type Pipeable, type Result as ResultT, type TaggedUnion, type TypedAction, typedAction } from "./ast.js";
1
+ import {
2
+ type Action,
3
+ type Option as OptionT,
4
+ type Pipeable,
5
+ type Result as ResultT,
6
+ type TaggedUnion,
7
+ type TypedAction,
8
+ typedAction,
9
+ } from "./ast.js";
2
10
  import { chain } from "./chain.js";
3
11
 
4
12
  /**
@@ -23,12 +31,10 @@ export function constant<TValue>(value: TValue): TypedAction<any, TValue> {
23
31
  // Identity — pass input through unchanged
24
32
  // ---------------------------------------------------------------------------
25
33
 
26
- export function identity<TValue>(): TypedAction<TValue, TValue> {
27
- return typedAction({
28
- kind: "Invoke",
29
- handler: { kind: "Builtin", builtin: { kind: "Identity" } },
30
- });
31
- }
34
+ export const identity: TypedAction<any, any> = typedAction({
35
+ kind: "Invoke",
36
+ handler: { kind: "Builtin", builtin: { kind: "Identity" } },
37
+ });
32
38
 
33
39
  // ---------------------------------------------------------------------------
34
40
  // Drop — discard pipeline value
@@ -53,9 +59,7 @@ export const drop: TypedAction<any, never> = typedAction({
53
59
  export function tag<
54
60
  TDef extends Record<string, unknown>,
55
61
  TKind extends keyof TDef & string,
56
- >(
57
- kind: TKind,
58
- ): TypedAction<TDef[TKind], TaggedUnion<TDef>> {
62
+ >(kind: TKind): TypedAction<TDef[TKind], TaggedUnion<TDef>> {
59
63
  return typedAction({
60
64
  kind: "Invoke",
61
65
  handler: { kind: "Builtin", builtin: { kind: "Tag", value: kind } },
@@ -114,10 +118,9 @@ export function extractField<
114
118
  // ExtractIndex — extract a single element from an array by index
115
119
  // ---------------------------------------------------------------------------
116
120
 
117
- export function extractIndex<
118
- TTuple extends unknown[],
119
- TIndex extends number,
120
- >(index: TIndex): TypedAction<TTuple, TTuple[TIndex]> {
121
+ export function extractIndex<TTuple extends unknown[], TIndex extends number>(
122
+ index: TIndex,
123
+ ): TypedAction<TTuple, TTuple[TIndex]> {
121
124
  return typedAction({
122
125
  kind: "Invoke",
123
126
  handler: {
@@ -153,7 +156,10 @@ export function dropResult<TInput, TOutput, TRefs extends string = never>(
153
156
  return typedAction({
154
157
  kind: "Chain",
155
158
  first: action as Action,
156
- rest: { kind: "Invoke", handler: { kind: "Builtin", builtin: { kind: "Drop" } } },
159
+ rest: {
160
+ kind: "Invoke",
161
+ handler: { kind: "Builtin", builtin: { kind: "Drop" } },
162
+ },
157
163
  });
158
164
  }
159
165
 
@@ -200,20 +206,26 @@ export function withResource<
200
206
  const acquireAndMerge = chain(
201
207
  typedAction<TIn, [TResource, TIn]>({
202
208
  kind: "All",
203
- actions: [create as Action, identity() as Action],
209
+ actions: [create as Action, identity as Action],
204
210
  }),
205
211
  typedAction<[TResource, TIn], TResource & TIn>(mergeBuiltin),
206
212
  );
207
213
 
208
214
  // Step 2: all(action, identity) → [TOut, TResource & TIn]
209
215
  // Keep merged object so dispose can access resource fields.
210
- const actionAndKeepMerged = typedAction<TResource & TIn, [TOut, TResource & TIn]>({
216
+ const actionAndKeepMerged = typedAction<
217
+ TResource & TIn,
218
+ [TOut, TResource & TIn]
219
+ >({
211
220
  kind: "All",
212
- actions: [action as Action, identity() as Action],
221
+ actions: [action as Action, identity as Action],
213
222
  });
214
223
 
215
224
  // Step 3: all(extractIndex(0), chain(extractIndex(1), dispose)) → [TOut, unknown]
216
- const disposeAndKeepResult = typedAction<[TOut, TResource & TIn], [TOut, unknown]>({
225
+ const disposeAndKeepResult = typedAction<
226
+ [TOut, TResource & TIn],
227
+ [TOut, unknown]
228
+ >({
217
229
  kind: "All",
218
230
  actions: [
219
231
  extractIndex<[TOut, TResource & TIn], 0>(0) as Action,
@@ -257,9 +269,12 @@ export function augment<
257
269
  kind: "Chain",
258
270
  first: {
259
271
  kind: "All",
260
- actions: [action as Action, identity() as Action],
272
+ actions: [action as Action, identity as Action],
273
+ },
274
+ rest: {
275
+ kind: "Invoke",
276
+ handler: { kind: "Builtin", builtin: { kind: "Merge" } },
261
277
  },
262
- rest: { kind: "Invoke", handler: { kind: "Builtin", builtin: { kind: "Merge" } } },
263
278
  });
264
279
  }
265
280
 
@@ -279,9 +294,10 @@ export function augment<
279
294
  * Example:
280
295
  * pipe(tap(pipe(pick("worktreePath", "description"), implement)), createPR)
281
296
  */
282
- export function tap<TInput extends Record<string, unknown>, TRefs extends string = never>(
283
- action: Pipeable<TInput, any, TRefs>,
284
- ): TypedAction<TInput, TInput, TRefs> {
297
+ export function tap<
298
+ TInput extends Record<string, unknown>,
299
+ TRefs extends string = never,
300
+ >(action: Pipeable<TInput, any, TRefs>): TypedAction<TInput, TInput, TRefs> {
285
301
  // Build AST directly — internal plumbing (action → constant → augment)
286
302
  // can't go through typed chain/augment with invariant phantom fields.
287
303
  // tap: all(chain(action, constant({})), identity()) → merge
@@ -293,12 +309,24 @@ export function tap<TInput extends Record<string, unknown>, TRefs extends string
293
309
  {
294
310
  kind: "Chain",
295
311
  first: action as Action,
296
- rest: { kind: "Invoke", handler: { kind: "Builtin", builtin: { kind: "Constant", value: {} } } },
312
+ rest: {
313
+ kind: "Invoke",
314
+ handler: {
315
+ kind: "Builtin",
316
+ builtin: { kind: "Constant", value: {} },
317
+ },
318
+ },
319
+ },
320
+ {
321
+ kind: "Invoke",
322
+ handler: { kind: "Builtin", builtin: { kind: "Identity" } },
297
323
  },
298
- { kind: "Invoke", handler: { kind: "Builtin", builtin: { kind: "Identity" } } },
299
324
  ],
300
325
  },
301
- rest: { kind: "Invoke", handler: { kind: "Builtin", builtin: { kind: "Merge" } } },
326
+ rest: {
327
+ kind: "Invoke",
328
+ handler: { kind: "Builtin", builtin: { kind: "Merge" } },
329
+ },
302
330
  });
303
331
  }
304
332
 
@@ -306,10 +334,7 @@ export function tap<TInput extends Record<string, unknown>, TRefs extends string
306
334
  // Range — produce an integer array [start, start+1, ..., end-1]
307
335
  // ---------------------------------------------------------------------------
308
336
 
309
- export function range(
310
- start: number,
311
- end: number,
312
- ): TypedAction<any, number[]> {
337
+ export function range(start: number, end: number): TypedAction<any, number[]> {
313
338
  const result: number[] = [];
314
339
  for (let i = start; i < end; i++) {
315
340
  result.push(i);
@@ -325,11 +350,29 @@ export function range(
325
350
  // ---------------------------------------------------------------------------
326
351
 
327
352
  // Shared AST fragments for Option desugaring
328
- const TAG_SOME: Action = { kind: "Invoke", handler: { kind: "Builtin", builtin: { kind: "Tag", value: "Some" } } };
329
- const TAG_NONE: Action = { kind: "Invoke", handler: { kind: "Builtin", builtin: { kind: "Tag", value: "None" } } };
330
- const EXTRACT_VALUE: Action = { kind: "Invoke", handler: { kind: "Builtin", builtin: { kind: "ExtractField", value: "value" } } };
331
- const DROP: Action = { kind: "Invoke", handler: { kind: "Builtin", builtin: { kind: "Drop" } } };
332
- const IDENTITY: Action = { kind: "Invoke", handler: { kind: "Builtin", builtin: { kind: "Identity" } } };
353
+ const TAG_SOME: Action = {
354
+ kind: "Invoke",
355
+ handler: { kind: "Builtin", builtin: { kind: "Tag", value: "Some" } },
356
+ };
357
+ const TAG_NONE: Action = {
358
+ kind: "Invoke",
359
+ handler: { kind: "Builtin", builtin: { kind: "Tag", value: "None" } },
360
+ };
361
+ const EXTRACT_VALUE: Action = {
362
+ kind: "Invoke",
363
+ handler: {
364
+ kind: "Builtin",
365
+ builtin: { kind: "ExtractField", value: "value" },
366
+ },
367
+ };
368
+ const DROP: Action = {
369
+ kind: "Invoke",
370
+ handler: { kind: "Builtin", builtin: { kind: "Drop" } },
371
+ };
372
+ const IDENTITY: Action = {
373
+ kind: "Invoke",
374
+ handler: { kind: "Builtin", builtin: { kind: "Identity" } },
375
+ };
333
376
 
334
377
  /** Wrap branch cases with ExtractField("value") auto-unwrapping. */
335
378
  function optionBranch(someCaseBody: Action, noneCaseBody: Action): Action {
@@ -372,10 +415,12 @@ export const Option = {
372
415
  * Desugars to: `branch({ Some: pipe(action, tag("Some")), None: tag("None") })`
373
416
  */
374
417
  map<T, U>(action: Pipeable<T, U>): TypedAction<OptionT<T>, OptionT<U>> {
375
- return typedAction(optionBranch(
376
- { kind: "Chain", first: action as Action, rest: TAG_SOME },
377
- TAG_NONE,
378
- ));
418
+ return typedAction(
419
+ optionBranch(
420
+ { kind: "Chain", first: action as Action, rest: TAG_SOME },
421
+ TAG_NONE,
422
+ ),
423
+ );
379
424
  },
380
425
 
381
426
  /**
@@ -387,11 +432,10 @@ export const Option = {
387
432
  *
388
433
  * Desugars to: `branch({ Some: action, None: tag("None") })`
389
434
  */
390
- andThen<T, U>(action: Pipeable<T, OptionT<U>>): TypedAction<OptionT<T>, OptionT<U>> {
391
- return typedAction(optionBranch(
392
- action as Action,
393
- TAG_NONE,
394
- ));
435
+ andThen<T, U>(
436
+ action: Pipeable<T, OptionT<U>>,
437
+ ): TypedAction<OptionT<T>, OptionT<U>> {
438
+ return typedAction(optionBranch(action as Action, TAG_NONE));
395
439
  },
396
440
 
397
441
  /**
@@ -412,7 +456,11 @@ export const Option = {
412
456
  kind: "Branch",
413
457
  cases: {
414
458
  Some: { kind: "Chain", first: EXTRACT_VALUE, rest: IDENTITY },
415
- None: { kind: "Chain", first: EXTRACT_VALUE, rest: { kind: "Chain", first: DROP, rest: defaultAction as Action } },
459
+ None: {
460
+ kind: "Chain",
461
+ first: EXTRACT_VALUE,
462
+ rest: { kind: "Chain", first: DROP, rest: defaultAction as Action },
463
+ },
416
464
  },
417
465
  });
418
466
  },
@@ -423,10 +471,7 @@ export const Option = {
423
471
  * Desugars to: `branch({ Some: identity(), None: tag("None") })`
424
472
  */
425
473
  flatten<T>(): TypedAction<OptionT<OptionT<T>>, OptionT<T>> {
426
- return typedAction(optionBranch(
427
- IDENTITY,
428
- TAG_NONE,
429
- ));
474
+ return typedAction(optionBranch(IDENTITY, TAG_NONE));
430
475
  },
431
476
 
432
477
  /**
@@ -439,11 +484,10 @@ export const Option = {
439
484
  *
440
485
  * Desugars to: `branch({ Some: predicate, None: tag("None") })`
441
486
  */
442
- filter<T>(predicate: Pipeable<T, OptionT<T>>): TypedAction<OptionT<T>, OptionT<T>> {
443
- return typedAction(optionBranch(
444
- predicate as Action,
445
- TAG_NONE,
446
- ));
487
+ filter<T>(
488
+ predicate: Pipeable<T, OptionT<T>>,
489
+ ): TypedAction<OptionT<T>, OptionT<T>> {
490
+ return typedAction(optionBranch(predicate as Action, TAG_NONE));
447
491
  },
448
492
 
449
493
  /**
@@ -469,12 +513,20 @@ export const Option = {
469
513
  * Desugars to: `branch({ Some: pipe(drop(), constant(true)), None: pipe(drop(), constant(false)) })`
470
514
  */
471
515
  isSome<T>(): TypedAction<OptionT<T>, boolean> {
472
- const constTrue: Action = { kind: "Invoke", handler: { kind: "Builtin", builtin: { kind: "Constant", value: true } } };
473
- const constFalse: Action = { kind: "Invoke", handler: { kind: "Builtin", builtin: { kind: "Constant", value: false } } };
474
- return typedAction(optionBranch(
475
- { kind: "Chain", first: DROP, rest: constTrue },
476
- { kind: "Chain", first: DROP, rest: constFalse },
477
- ));
516
+ const constTrue: Action = {
517
+ kind: "Invoke",
518
+ handler: { kind: "Builtin", builtin: { kind: "Constant", value: true } },
519
+ };
520
+ const constFalse: Action = {
521
+ kind: "Invoke",
522
+ handler: { kind: "Builtin", builtin: { kind: "Constant", value: false } },
523
+ };
524
+ return typedAction(
525
+ optionBranch(
526
+ { kind: "Chain", first: DROP, rest: constTrue },
527
+ { kind: "Chain", first: DROP, rest: constFalse },
528
+ ),
529
+ );
478
530
  },
479
531
 
480
532
  /**
@@ -485,12 +537,20 @@ export const Option = {
485
537
  * Desugars to: `branch({ Some: pipe(drop(), constant(false)), None: pipe(drop(), constant(true)) })`
486
538
  */
487
539
  isNone<T>(): TypedAction<OptionT<T>, boolean> {
488
- const constTrue: Action = { kind: "Invoke", handler: { kind: "Builtin", builtin: { kind: "Constant", value: true } } };
489
- const constFalse: Action = { kind: "Invoke", handler: { kind: "Builtin", builtin: { kind: "Constant", value: false } } };
490
- return typedAction(optionBranch(
491
- { kind: "Chain", first: DROP, rest: constFalse },
492
- { kind: "Chain", first: DROP, rest: constTrue },
493
- ));
540
+ const constTrue: Action = {
541
+ kind: "Invoke",
542
+ handler: { kind: "Builtin", builtin: { kind: "Constant", value: true } },
543
+ };
544
+ const constFalse: Action = {
545
+ kind: "Invoke",
546
+ handler: { kind: "Builtin", builtin: { kind: "Constant", value: false } },
547
+ };
548
+ return typedAction(
549
+ optionBranch(
550
+ { kind: "Chain", first: DROP, rest: constFalse },
551
+ { kind: "Chain", first: DROP, rest: constTrue },
552
+ ),
553
+ );
494
554
  },
495
555
  } as const;
496
556
 
@@ -499,8 +559,14 @@ export const Option = {
499
559
  // ---------------------------------------------------------------------------
500
560
 
501
561
  // Shared AST fragments for Result desugaring
502
- const TAG_OK: Action = { kind: "Invoke", handler: { kind: "Builtin", builtin: { kind: "Tag", value: "Ok" } } };
503
- const TAG_ERR: Action = { kind: "Invoke", handler: { kind: "Builtin", builtin: { kind: "Tag", value: "Err" } } };
562
+ const TAG_OK: Action = {
563
+ kind: "Invoke",
564
+ handler: { kind: "Builtin", builtin: { kind: "Tag", value: "Ok" } },
565
+ };
566
+ const TAG_ERR: Action = {
567
+ kind: "Invoke",
568
+ handler: { kind: "Builtin", builtin: { kind: "Tag", value: "Err" } },
569
+ };
504
570
 
505
571
  /** Wrap branch cases with ExtractField("value") auto-unwrapping. */
506
572
  function resultBranch(okCaseBody: Action, errCaseBody: Action): Action {
@@ -540,10 +606,12 @@ export const Result = {
540
606
  map<TValue, TOut, TError>(
541
607
  action: Pipeable<TValue, TOut>,
542
608
  ): TypedAction<ResultT<TValue, TError>, ResultT<TOut, TError>> {
543
- return typedAction(resultBranch(
544
- { kind: "Chain", first: action as Action, rest: TAG_OK },
545
- TAG_ERR,
546
- ));
609
+ return typedAction(
610
+ resultBranch(
611
+ { kind: "Chain", first: action as Action, rest: TAG_OK },
612
+ TAG_ERR,
613
+ ),
614
+ );
547
615
  },
548
616
 
549
617
  /**
@@ -554,10 +622,13 @@ export const Result = {
554
622
  mapErr<TValue, TError, TErrorOut>(
555
623
  action: Pipeable<TError, TErrorOut>,
556
624
  ): TypedAction<ResultT<TValue, TError>, ResultT<TValue, TErrorOut>> {
557
- return typedAction(resultBranch(
558
- TAG_OK,
559
- { kind: "Chain", first: action as Action, rest: TAG_ERR },
560
- ));
625
+ return typedAction(
626
+ resultBranch(TAG_OK, {
627
+ kind: "Chain",
628
+ first: action as Action,
629
+ rest: TAG_ERR,
630
+ }),
631
+ );
561
632
  },
562
633
 
563
634
  /**
@@ -569,10 +640,7 @@ export const Result = {
569
640
  andThen<TValue, TOut, TError>(
570
641
  action: Pipeable<TValue, ResultT<TOut, TError>>,
571
642
  ): TypedAction<ResultT<TValue, TError>, ResultT<TOut, TError>> {
572
- return typedAction(resultBranch(
573
- action as Action,
574
- TAG_ERR,
575
- ));
643
+ return typedAction(resultBranch(action as Action, TAG_ERR));
576
644
  },
577
645
 
578
646
  /**
@@ -584,10 +652,7 @@ export const Result = {
584
652
  or<TValue, TError, TErrorOut>(
585
653
  fallback: Pipeable<TError, ResultT<TValue, TErrorOut>>,
586
654
  ): TypedAction<ResultT<TValue, TError>, ResultT<TValue, TErrorOut>> {
587
- return typedAction(resultBranch(
588
- TAG_OK,
589
- fallback as Action,
590
- ));
655
+ return typedAction(resultBranch(TAG_OK, fallback as Action));
591
656
  },
592
657
 
593
658
  /**
@@ -599,10 +664,12 @@ export const Result = {
599
664
  and<TValue, TOut, TError>(
600
665
  other: Pipeable<never, ResultT<TOut, TError>>,
601
666
  ): TypedAction<ResultT<TValue, TError>, ResultT<TOut, TError>> {
602
- return typedAction(resultBranch(
603
- { kind: "Chain", first: DROP, rest: other as Action },
604
- TAG_ERR,
605
- ));
667
+ return typedAction(
668
+ resultBranch(
669
+ { kind: "Chain", first: DROP, rest: other as Action },
670
+ TAG_ERR,
671
+ ),
672
+ );
606
673
  },
607
674
 
608
675
  /**
@@ -623,10 +690,7 @@ export const Result = {
623
690
  __phantom_out?: () => TValue;
624
691
  },
625
692
  ): TypedAction<ResultT<TValue, TError>, TValue> {
626
- return typedAction(resultBranch(
627
- IDENTITY,
628
- defaultAction as Action,
629
- ));
693
+ return typedAction(resultBranch(IDENTITY, defaultAction as Action));
630
694
  },
631
695
 
632
696
  /**
@@ -634,11 +698,11 @@ export const Result = {
634
698
  *
635
699
  * Desugars to: `branch({ Ok: identity(), Err: tag("Err") })`
636
700
  */
637
- flatten<TValue, TError>(): TypedAction<ResultT<ResultT<TValue, TError>, TError>, ResultT<TValue, TError>> {
638
- return typedAction(resultBranch(
639
- IDENTITY,
640
- TAG_ERR,
641
- ));
701
+ flatten<TValue, TError>(): TypedAction<
702
+ ResultT<ResultT<TValue, TError>, TError>,
703
+ ResultT<TValue, TError>
704
+ > {
705
+ return typedAction(resultBranch(IDENTITY, TAG_ERR));
642
706
  },
643
707
 
644
708
  /**
@@ -646,11 +710,13 @@ export const Result = {
646
710
  *
647
711
  * Desugars to: `branch({ Ok: tag("Some"), Err: pipe(drop(), tag("None")) })`
648
712
  */
649
- toOption<TValue, TError>(): TypedAction<ResultT<TValue, TError>, OptionT<TValue>> {
650
- return typedAction(resultBranch(
651
- TAG_SOME,
652
- { kind: "Chain", first: DROP, rest: TAG_NONE },
653
- ));
713
+ toOption<TValue, TError>(): TypedAction<
714
+ ResultT<TValue, TError>,
715
+ OptionT<TValue>
716
+ > {
717
+ return typedAction(
718
+ resultBranch(TAG_SOME, { kind: "Chain", first: DROP, rest: TAG_NONE }),
719
+ );
654
720
  },
655
721
 
656
722
  /**
@@ -658,53 +724,84 @@ export const Result = {
658
724
  *
659
725
  * Desugars to: `branch({ Ok: pipe(drop(), tag("None")), Err: tag("Some") })`
660
726
  */
661
- toOptionErr<TValue, TError>(): TypedAction<ResultT<TValue, TError>, OptionT<TError>> {
662
- return typedAction(resultBranch(
663
- { kind: "Chain", first: DROP, rest: TAG_NONE },
664
- TAG_SOME,
665
- ));
727
+ toOptionErr<TValue, TError>(): TypedAction<
728
+ ResultT<TValue, TError>,
729
+ OptionT<TError>
730
+ > {
731
+ return typedAction(
732
+ resultBranch({ kind: "Chain", first: DROP, rest: TAG_NONE }, TAG_SOME),
733
+ );
666
734
  },
667
735
 
668
736
  /**
669
737
  * Swap Result/Option nesting.
670
738
  * `Result<Option<TValue>, TError> → Option<Result<TValue, TError>>`
671
739
  */
672
- transpose<TValue, TError>(): TypedAction<ResultT<OptionT<TValue>, TError>, OptionT<ResultT<TValue, TError>>> {
673
- return typedAction(resultBranch(
674
- // Ok case: receives Option<TValue>, branch on Some/None
675
- {
676
- kind: "Branch",
677
- cases: {
678
- Some: { kind: "Chain", first: EXTRACT_VALUE, rest: { kind: "Chain", first: TAG_OK, rest: TAG_SOME } },
679
- None: { kind: "Chain", first: EXTRACT_VALUE, rest: { kind: "Chain", first: DROP, rest: TAG_NONE } },
740
+ transpose<TValue, TError>(): TypedAction<
741
+ ResultT<OptionT<TValue>, TError>,
742
+ OptionT<ResultT<TValue, TError>>
743
+ > {
744
+ return typedAction(
745
+ resultBranch(
746
+ // Ok case: receives Option<TValue>, branch on Some/None
747
+ {
748
+ kind: "Branch",
749
+ cases: {
750
+ Some: {
751
+ kind: "Chain",
752
+ first: EXTRACT_VALUE,
753
+ rest: { kind: "Chain", first: TAG_OK, rest: TAG_SOME },
754
+ },
755
+ None: {
756
+ kind: "Chain",
757
+ first: EXTRACT_VALUE,
758
+ rest: { kind: "Chain", first: DROP, rest: TAG_NONE },
759
+ },
760
+ },
680
761
  },
681
- },
682
- // Err case: receives TError, wrap as Result.err then Option.some
683
- { kind: "Chain", first: TAG_ERR, rest: TAG_SOME },
684
- ));
762
+ // Err case: receives TError, wrap as Result.err then Option.some
763
+ { kind: "Chain", first: TAG_ERR, rest: TAG_SOME },
764
+ ),
765
+ );
685
766
  },
686
767
 
687
768
  /**
688
769
  * Test if the value is Ok. `Result<TValue, TError> → boolean`
689
770
  */
690
771
  isOk<TValue, TError>(): TypedAction<ResultT<TValue, TError>, boolean> {
691
- const constTrue: Action = { kind: "Invoke", handler: { kind: "Builtin", builtin: { kind: "Constant", value: true } } };
692
- const constFalse: Action = { kind: "Invoke", handler: { kind: "Builtin", builtin: { kind: "Constant", value: false } } };
693
- return typedAction(resultBranch(
694
- { kind: "Chain", first: DROP, rest: constTrue },
695
- { kind: "Chain", first: DROP, rest: constFalse },
696
- ));
772
+ const constTrue: Action = {
773
+ kind: "Invoke",
774
+ handler: { kind: "Builtin", builtin: { kind: "Constant", value: true } },
775
+ };
776
+ const constFalse: Action = {
777
+ kind: "Invoke",
778
+ handler: { kind: "Builtin", builtin: { kind: "Constant", value: false } },
779
+ };
780
+ return typedAction(
781
+ resultBranch(
782
+ { kind: "Chain", first: DROP, rest: constTrue },
783
+ { kind: "Chain", first: DROP, rest: constFalse },
784
+ ),
785
+ );
697
786
  },
698
787
 
699
788
  /**
700
789
  * Test if the value is Err. `Result<TValue, TError> → boolean`
701
790
  */
702
791
  isErr<TValue, TError>(): TypedAction<ResultT<TValue, TError>, boolean> {
703
- const constTrue: Action = { kind: "Invoke", handler: { kind: "Builtin", builtin: { kind: "Constant", value: true } } };
704
- const constFalse: Action = { kind: "Invoke", handler: { kind: "Builtin", builtin: { kind: "Constant", value: false } } };
705
- return typedAction(resultBranch(
706
- { kind: "Chain", first: DROP, rest: constFalse },
707
- { kind: "Chain", first: DROP, rest: constTrue },
708
- ));
792
+ const constTrue: Action = {
793
+ kind: "Invoke",
794
+ handler: { kind: "Builtin", builtin: { kind: "Constant", value: true } },
795
+ };
796
+ const constFalse: Action = {
797
+ kind: "Invoke",
798
+ handler: { kind: "Builtin", builtin: { kind: "Constant", value: false } },
799
+ };
800
+ return typedAction(
801
+ resultBranch(
802
+ { kind: "Chain", first: DROP, rest: constFalse },
803
+ { kind: "Chain", first: DROP, rest: constTrue },
804
+ ),
805
+ );
709
806
  },
710
807
  } as const;
package/src/chain.ts CHANGED
@@ -1,8 +1,17 @@
1
- import { type Action, type Pipeable, type TypedAction, typedAction } from "./ast.js";
1
+ import {
2
+ type Action,
3
+ type Pipeable,
4
+ type TypedAction,
5
+ typedAction,
6
+ } from "./ast.js";
2
7
 
3
8
  export function chain<T1, T2, T3, R1 extends string, R2 extends string>(
4
9
  first: Pipeable<T1, T2, R1>,
5
10
  rest: Pipeable<T2, T3, R2>,
6
11
  ): TypedAction<T1, T3, R1 | R2> {
7
- return typedAction({ kind: "Chain", first: first as Action, rest: rest as Action });
12
+ return typedAction({
13
+ kind: "Chain",
14
+ first: first as Action,
15
+ rest: rest as Action,
16
+ });
8
17
  }
package/src/effect-id.ts CHANGED
@@ -2,14 +2,29 @@
2
2
  // Shared effect ID counter for gensym'd effect identifiers
3
3
  // ---------------------------------------------------------------------------
4
4
 
5
- let nextEffectId = 0;
5
+ /** Branded ID for resume-style effect handlers. */
6
+ export type ResumeHandlerId = number & {
7
+ readonly __resumeHandlerBrand: unique symbol;
8
+ };
6
9
 
7
- /** Allocate a fresh, unique effect ID. */
8
- export function allocateEffectId(): number {
9
- return nextEffectId++;
10
+ /** Branded ID for restart-style effect handlers. */
11
+ export type RestartHandlerId = number & {
12
+ readonly __restartHandlerBrand: unique symbol;
13
+ };
14
+
15
+ let nextId = 0;
16
+
17
+ /** Allocate a fresh, unique resume handler ID. */
18
+ export function allocateResumeHandlerId(): ResumeHandlerId {
19
+ return nextId++ as ResumeHandlerId;
20
+ }
21
+
22
+ /** Allocate a fresh, unique restart handler ID. */
23
+ export function allocateRestartHandlerId(): RestartHandlerId {
24
+ return nextId++ as RestartHandlerId;
10
25
  }
11
26
 
12
- /** Reset the effect ID counter. For test isolation only. */
27
+ /** Reset the ID counter. For test isolation only. */
13
28
  export function resetEffectIdCounter(): void {
14
- nextEffectId = 0;
29
+ nextId = 0;
15
30
  }