@barnum/barnum 0.2.3 → 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.
Files changed (115) hide show
  1. package/artifacts/linux-arm64/barnum +0 -0
  2. package/artifacts/linux-x64/barnum +0 -0
  3. package/artifacts/macos-arm64/barnum +0 -0
  4. package/artifacts/macos-x64/barnum +0 -0
  5. package/artifacts/win-x64/barnum.exe +0 -0
  6. package/cli.cjs +33 -0
  7. package/dist/all.d.ts +43 -0
  8. package/dist/all.d.ts.map +1 -0
  9. package/dist/all.js +8 -0
  10. package/dist/ast.d.ts +476 -0
  11. package/dist/ast.d.ts.map +1 -0
  12. package/dist/ast.js +419 -0
  13. package/dist/bind.d.ts +59 -0
  14. package/dist/bind.d.ts.map +1 -0
  15. package/dist/bind.js +69 -0
  16. package/dist/builtins/array.d.ts +36 -0
  17. package/dist/builtins/array.d.ts.map +1 -0
  18. package/dist/builtins/array.js +93 -0
  19. package/dist/builtins/index.d.ts +6 -0
  20. package/dist/builtins/index.d.ts.map +1 -0
  21. package/dist/builtins/index.js +5 -0
  22. package/dist/builtins/scalar.d.ts +12 -0
  23. package/dist/builtins/scalar.d.ts.map +1 -0
  24. package/dist/builtins/scalar.js +41 -0
  25. package/dist/builtins/struct.d.ts +25 -0
  26. package/dist/builtins/struct.d.ts.map +1 -0
  27. package/dist/builtins/struct.js +67 -0
  28. package/dist/builtins/tagged-union.d.ts +54 -0
  29. package/dist/builtins/tagged-union.d.ts.map +1 -0
  30. package/dist/builtins/tagged-union.js +81 -0
  31. package/dist/builtins/with-resource.d.ts +23 -0
  32. package/dist/builtins/with-resource.d.ts.map +1 -0
  33. package/dist/builtins/with-resource.js +35 -0
  34. package/dist/chain.d.ts +3 -0
  35. package/dist/chain.d.ts.map +1 -0
  36. package/dist/chain.js +8 -0
  37. package/dist/effect-id.d.ts +15 -0
  38. package/dist/effect-id.d.ts.map +1 -0
  39. package/dist/effect-id.js +16 -0
  40. package/dist/handler.d.ts +51 -0
  41. package/dist/handler.d.ts.map +1 -0
  42. package/dist/handler.js +130 -0
  43. package/dist/index.d.ts +12 -0
  44. package/dist/index.d.ts.map +1 -0
  45. package/dist/index.js +7 -0
  46. package/dist/iterator.d.ts +32 -0
  47. package/dist/iterator.d.ts.map +1 -0
  48. package/dist/iterator.js +123 -0
  49. package/dist/option.d.ts +74 -0
  50. package/dist/option.d.ts.map +1 -0
  51. package/dist/option.js +141 -0
  52. package/dist/pipe.d.ts +12 -0
  53. package/dist/pipe.d.ts.map +1 -0
  54. package/dist/pipe.js +12 -0
  55. package/dist/race.d.ts +54 -0
  56. package/dist/race.d.ts.map +1 -0
  57. package/dist/race.js +116 -0
  58. package/dist/recursive.d.ts +40 -0
  59. package/dist/recursive.d.ts.map +1 -0
  60. package/dist/recursive.js +58 -0
  61. package/dist/result.d.ts +50 -0
  62. package/dist/result.d.ts.map +1 -0
  63. package/dist/result.js +117 -0
  64. package/dist/run.d.ts +14 -0
  65. package/dist/run.d.ts.map +1 -0
  66. package/dist/run.js +160 -0
  67. package/dist/runtime.d.ts +6 -0
  68. package/dist/runtime.d.ts.map +1 -0
  69. package/dist/runtime.js +7 -0
  70. package/dist/schema.d.ts +9 -0
  71. package/dist/schema.d.ts.map +1 -0
  72. package/dist/schema.js +95 -0
  73. package/dist/schemas.d.ts +5 -0
  74. package/dist/schemas.d.ts.map +1 -0
  75. package/dist/schemas.js +13 -0
  76. package/dist/try-catch.d.ts +24 -0
  77. package/dist/try-catch.d.ts.map +1 -0
  78. package/dist/try-catch.js +37 -0
  79. package/dist/values.d.ts +6 -0
  80. package/dist/values.d.ts.map +1 -0
  81. package/dist/values.js +12 -0
  82. package/dist/worker.d.ts +15 -0
  83. package/dist/worker.d.ts.map +1 -0
  84. package/dist/worker.js +58 -0
  85. package/package.json +42 -16
  86. package/src/all.ts +133 -0
  87. package/src/ast.ts +1301 -0
  88. package/src/bind.ts +162 -0
  89. package/src/builtins/array.ts +121 -0
  90. package/src/builtins/index.ts +17 -0
  91. package/src/builtins/scalar.ts +49 -0
  92. package/src/builtins/struct.ts +111 -0
  93. package/src/builtins/tagged-union.ts +142 -0
  94. package/src/builtins/with-resource.ts +69 -0
  95. package/src/chain.ts +17 -0
  96. package/src/effect-id.ts +30 -0
  97. package/src/handler.ts +263 -0
  98. package/src/index.ts +37 -0
  99. package/src/iterator.ts +243 -0
  100. package/src/option.ts +199 -0
  101. package/src/pipe.ts +138 -0
  102. package/src/race.ts +173 -0
  103. package/src/recursive.ts +129 -0
  104. package/src/result.ts +168 -0
  105. package/src/run.ts +209 -0
  106. package/src/runtime.ts +16 -0
  107. package/src/schema.ts +118 -0
  108. package/src/schemas.ts +21 -0
  109. package/src/try-catch.ts +57 -0
  110. package/src/values.ts +21 -0
  111. package/src/worker.ts +71 -0
  112. package/README.md +0 -19
  113. package/barnum-config-schema.json +0 -408
  114. package/cli.js +0 -20
  115. package/index.js +0 -23
@@ -0,0 +1,58 @@
1
+ import { toAction, typedAction, branch, } from "./ast.js";
2
+ import { all } from "./all.js";
3
+ import { chain } from "./chain.js";
4
+ import { constant, identity, getField, getIndex, tag, } from "./builtins/index.js";
5
+ import { allocateResumeHandlerId } from "./effect-id.js";
6
+ const UNUSED_STATE = null;
7
+ // ---------------------------------------------------------------------------
8
+ // defineRecursiveFunctions
9
+ // ---------------------------------------------------------------------------
10
+ /**
11
+ * Define mutually recursive functions that can call each other.
12
+ *
13
+ * The type parameter is an array of [In, Out] tuples — one per function.
14
+ * TypeScript can't infer these from circular definitions, so they must be
15
+ * explicit.
16
+ *
17
+ * Returns a curried combinator: the first callback defines function bodies,
18
+ * the second receives the same call tokens and returns the workflow entry
19
+ * point.
20
+ *
21
+ * Desugars to a ResumeHandle with a Branch-based handler. Each call token
22
+ * is Chain(Tag("CallN"), ResumePerform(id)). The handler dispatches to the
23
+ * correct function body by tag. The caller's pipeline is preserved as a
24
+ * ResumePerformFrame across each call.
25
+ *
26
+ * **Known limitation:** concurrent calls to the same function do not work
27
+ * as expected. `all(f(x), f(x))` will NOT call `f` twice — both branches
28
+ * perform on the same ResumeHandle, and the second perform will not
29
+ * execute independently. Use sequential calls (chain/then) instead of
30
+ * concurrent calls (all) when calling recursive functions multiple times.
31
+ */
32
+ export function defineRecursiveFunctions(bodiesFn) {
33
+ const resumeHandlerId = allocateResumeHandlerId();
34
+ const resumePerform = {
35
+ kind: "ResumePerform",
36
+ resume_handler_id: resumeHandlerId,
37
+ };
38
+ // Call tokens: Chain(Tag("CallN"), ResumePerform(resumeHandlerId))
39
+ const fnCount = bodiesFn.length;
40
+ const callTokens = Array.from({ length: fnCount }, (_, i) => typedAction(toAction(chain(toAction(tag(`Call${i}`, "RecursiveDispatch")), toAction(resumePerform)))));
41
+ // Get function body ASTs
42
+ const bodyActions = bodiesFn(...callTokens).map(toAction);
43
+ // Branch cases: CallN → GetField("value") → bodyN
44
+ const cases = {};
45
+ for (let i = 0; i < bodyActions.length; i++) {
46
+ cases[`Call${i}`] = toAction(chain(toAction(getField("value")), toAction(bodyActions[i])));
47
+ }
48
+ // Return curried entry-point combinator
49
+ return (entryFn) => {
50
+ const userBody = toAction(entryFn(...callTokens));
51
+ return typedAction(toAction(chain(toAction(all(identity(), constant(UNUSED_STATE))), {
52
+ kind: "ResumeHandle",
53
+ resume_handler_id: resumeHandlerId,
54
+ body: toAction(chain(toAction(getIndex(0).unwrap()), toAction(userBody))),
55
+ handler: toAction(all(chain(toAction(getIndex(0).unwrap()), toAction(branch(cases))), constant(UNUSED_STATE))),
56
+ })));
57
+ };
58
+ }
@@ -0,0 +1,50 @@
1
+ import { type Option as OptionT, type Pipeable, type Result as ResultT, type TypedAction } from "./ast.js";
2
+ export declare const Result: {
3
+ /** Tag combinator: wrap value as `Result.Ok`. `TValue → Result<TValue, TError>` */
4
+ readonly ok: <TValue, TError = never>() => TypedAction<TValue, ResultT<TValue, TError>>;
5
+ /** Tag combinator: wrap value as `Result.Err`. `TError → Result<TValue, TError>` */
6
+ readonly err: <TValue = never, TError = unknown>() => TypedAction<TError, ResultT<TValue, TError>>;
7
+ /** Transform the Ok value. `Result<TValue, TError> → Result<TOut, TError>` */
8
+ readonly map: <TValue, TOut, TError>(action: Pipeable<TValue, TOut>) => TypedAction<ResultT<TValue, TError>, ResultT<TOut, TError>>;
9
+ /** Transform the Err value. `Result<TValue, TError> → Result<TValue, TErrorOut>` */
10
+ readonly mapErr: <TValue, TError, TErrorOut>(action: Pipeable<TError, TErrorOut>) => TypedAction<ResultT<TValue, TError>, ResultT<TValue, TErrorOut>>;
11
+ /**
12
+ * Monadic bind (flatMap) for Ok. If Ok, pass value to action which
13
+ * returns Result<TOut, TError>. If Err, propagate.
14
+ */
15
+ readonly andThen: <TValue, TOut, TError>(action: Pipeable<TValue, ResultT<TOut, TError>>) => TypedAction<ResultT<TValue, TError>, ResultT<TOut, TError>>;
16
+ /** Fallback on Err. If Ok, keep it. If Err, pass error to fallback. */
17
+ readonly or: <TValue, TError, TErrorOut>(fallback: Pipeable<TError, ResultT<TValue, TErrorOut>>) => TypedAction<ResultT<TValue, TError>, ResultT<TValue, TErrorOut>>;
18
+ /**
19
+ * Extract the Ok value or panic. `Result<TValue, TError> → TValue`
20
+ *
21
+ * Panics (fatal, not caught by tryCatch) if the value is Err.
22
+ */
23
+ readonly unwrap: <TValue, TError>() => TypedAction<ResultT<TValue, TError>, TValue>;
24
+ /**
25
+ * Extract Ok or compute default from Err. `Result<TValue, TError> → TValue`
26
+ */
27
+ readonly unwrapOr: <TValue, TError>(defaultAction: Pipeable<TError, TValue>) => TypedAction<ResultT<TValue, TError>, TValue>;
28
+ /**
29
+ * Convert Ok to Some, Err to None. `Result<TValue, TError> → Option<TValue>`
30
+ */
31
+ readonly asOkOption: <TValue, TError>() => TypedAction<ResultT<TValue, TError>, OptionT<TValue>>;
32
+ /**
33
+ * Convert Err to Some, Ok to None. `Result<TValue, TError> → Option<TError>`
34
+ */
35
+ readonly asErrOption: <TValue, TError>() => TypedAction<ResultT<TValue, TError>, OptionT<TError>>;
36
+ /**
37
+ * Swap Result/Option nesting.
38
+ * `Result<Option<TValue>, TError> → Option<Result<TValue, TError>>`
39
+ */
40
+ readonly transpose: <TValue, TError>() => TypedAction<ResultT<OptionT<TValue>, TError>, OptionT<ResultT<TValue, TError>>>;
41
+ /**
42
+ * Test if the value is Ok. `Result<TValue, TError> → boolean`
43
+ */
44
+ readonly isOk: <TValue, TError>() => TypedAction<ResultT<TValue, TError>, boolean>;
45
+ /**
46
+ * Test if the value is Err. `Result<TValue, TError> → boolean`
47
+ */
48
+ readonly isErr: <TValue, TError>() => TypedAction<ResultT<TValue, TError>, boolean>;
49
+ };
50
+ //# sourceMappingURL=result.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"result.d.ts","sourceRoot":"","sources":["../src/result.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,MAAM,IAAI,OAAO,EACtB,KAAK,QAAQ,EACb,KAAK,MAAM,IAAI,OAAO,EAEtB,KAAK,WAAW,EAEjB,MAAM,UAAU,CAAC;AASlB,eAAO,MAAM,MAAM;IACjB,mFAAmF;kBAChF,MAAM,EAAE,MAAM,eAAa,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAG1E,oFAAoF;mBAChF,MAAM,UAAU,MAAM,iBAAe,WAAW,CAClD,MAAM,EACN,OAAO,CAAC,MAAM,EAAE,MAAM,CAAC,CACxB;IAID,8EAA8E;mBAC1E,MAAM,EAAE,IAAI,EAAE,MAAM,UACd,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAC,KAC7B,WAAW,CAAC,OAAO,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IAO9D,oFAAoF;sBAC7E,MAAM,EAAE,MAAM,EAAE,SAAS,UACtB,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC,KAClC,WAAW,CAAC,OAAO,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IAOnE;;;OAGG;uBACK,MAAM,EAAE,IAAI,EAAE,MAAM,UAClB,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,KAC9C,WAAW,CAAC,OAAO,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IAO9D,uEAAuE;kBACpE,MAAM,EAAE,MAAM,EAAE,SAAS,YAChB,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC,KACrD,WAAW,CAAC,OAAO,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IAOnE;;;;OAIG;sBACI,MAAM,EAAE,MAAM,OAAK,WAAW,CAAC,OAAO,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC;IAOtE;;OAEG;wBACM,MAAM,EAAE,MAAM,iBACN,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,KACtC,WAAW,CAAC,OAAO,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC;IAO/C;;OAEG;0BACQ,MAAM,EAAE,MAAM,OAAK,WAAW,CACvC,OAAO,CAAC,MAAM,EAAE,MAAM,CAAC,EACvB,OAAO,CAAC,MAAM,CAAC,CAChB;IAOD;;OAEG;2BACS,MAAM,EAAE,MAAM,OAAK,WAAW,CACxC,OAAO,CAAC,MAAM,EAAE,MAAM,CAAC,EACvB,OAAO,CAAC,MAAM,CAAC,CAChB;IAOD;;;OAGG;yBACO,MAAM,EAAE,MAAM,OAAK,WAAW,CACtC,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,EAChC,OAAO,CAAC,OAAO,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CACjC;IAmBD;;OAEG;oBACE,MAAM,EAAE,MAAM,OAAK,WAAW,CAAC,OAAO,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,CAAC;IAOrE;;OAEG;qBACG,MAAM,EAAE,MAAM,OAAK,WAAW,CAAC,OAAO,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,CAAC;CAM9D,CAAC"}
package/dist/result.js ADDED
@@ -0,0 +1,117 @@
1
+ import { branch, } from "./ast.js";
2
+ import { chain } from "./chain.js";
3
+ import { constant, drop, identity, panic, tag } from "./builtins/index.js";
4
+ import { Option } from "./option.js";
5
+ // ---------------------------------------------------------------------------
6
+ // Result namespace — combinators for Result<TValue, TError> tagged unions
7
+ // ---------------------------------------------------------------------------
8
+ export const Result = {
9
+ /** Tag combinator: wrap value as `Result.Ok`. `TValue → Result<TValue, TError>` */
10
+ ok() {
11
+ return tag("Ok", "Result");
12
+ },
13
+ /** Tag combinator: wrap value as `Result.Err`. `TError → Result<TValue, TError>` */
14
+ err() {
15
+ return tag("Err", "Result");
16
+ },
17
+ /** Transform the Ok value. `Result<TValue, TError> → Result<TOut, TError>` */
18
+ map(action) {
19
+ return branch({
20
+ Ok: chain(action, Result.ok()),
21
+ Err: Result.err(),
22
+ });
23
+ },
24
+ /** Transform the Err value. `Result<TValue, TError> → Result<TValue, TErrorOut>` */
25
+ mapErr(action) {
26
+ return branch({
27
+ Ok: Result.ok(),
28
+ Err: chain(action, Result.err()),
29
+ });
30
+ },
31
+ /**
32
+ * Monadic bind (flatMap) for Ok. If Ok, pass value to action which
33
+ * returns Result<TOut, TError>. If Err, propagate.
34
+ */
35
+ andThen(action) {
36
+ return branch({
37
+ Ok: action,
38
+ Err: Result.err(),
39
+ });
40
+ },
41
+ /** Fallback on Err. If Ok, keep it. If Err, pass error to fallback. */
42
+ or(fallback) {
43
+ return branch({
44
+ Ok: Result.ok(),
45
+ Err: fallback,
46
+ });
47
+ },
48
+ /**
49
+ * Extract the Ok value or panic. `Result<TValue, TError> → TValue`
50
+ *
51
+ * Panics (fatal, not caught by tryCatch) if the value is Err.
52
+ */
53
+ unwrap() {
54
+ return branch({
55
+ Ok: identity(),
56
+ Err: panic("called unwrap on Err"),
57
+ });
58
+ },
59
+ /**
60
+ * Extract Ok or compute default from Err. `Result<TValue, TError> → TValue`
61
+ */
62
+ unwrapOr(defaultAction) {
63
+ return branch({
64
+ Ok: identity(),
65
+ Err: defaultAction,
66
+ });
67
+ },
68
+ /**
69
+ * Convert Ok to Some, Err to None. `Result<TValue, TError> → Option<TValue>`
70
+ */
71
+ asOkOption() {
72
+ return branch({
73
+ Ok: Option.some(),
74
+ Err: chain(drop, Option.none()),
75
+ });
76
+ },
77
+ /**
78
+ * Convert Err to Some, Ok to None. `Result<TValue, TError> → Option<TError>`
79
+ */
80
+ asErrOption() {
81
+ return branch({
82
+ Ok: chain(drop, Option.none()),
83
+ Err: Option.some(),
84
+ });
85
+ },
86
+ /**
87
+ * Swap Result/Option nesting.
88
+ * `Result<Option<TValue>, TError> → Option<Result<TValue, TError>>`
89
+ */
90
+ transpose() {
91
+ return branch({
92
+ Ok: branch({
93
+ Some: chain(Result.ok(), Option.some()),
94
+ None: chain(drop, Option.none()),
95
+ }),
96
+ Err: chain(Result.err(), Option.some()),
97
+ });
98
+ },
99
+ /**
100
+ * Test if the value is Ok. `Result<TValue, TError> → boolean`
101
+ */
102
+ isOk() {
103
+ return branch({
104
+ Ok: constant(true),
105
+ Err: constant(false),
106
+ });
107
+ },
108
+ /**
109
+ * Test if the value is Err. `Result<TValue, TError> → boolean`
110
+ */
111
+ isErr() {
112
+ return branch({
113
+ Ok: constant(false),
114
+ Err: constant(true),
115
+ });
116
+ },
117
+ };
package/dist/run.d.ts ADDED
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Workflow execution: resolves the barnum binary, tsx executor, and worker
3
+ * script, then spawns the workflow as a subprocess.
4
+ */
5
+ import { type Action, type ExtractOutput } from "./ast.js";
6
+ /** Log verbosity for the barnum engine runtime. Passed to the CLI's `--log-level`. */
7
+ export type LogLevel = "off" | "error" | "warn" | "info" | "debug" | "trace";
8
+ export interface RunPipelineOptions {
9
+ /** Engine log verbosity. Default: "off" (only handler stderr is visible). */
10
+ logLevel?: LogLevel;
11
+ }
12
+ /** Run a pipeline to completion. Returns the workflow's final output value. */
13
+ export declare function runPipeline<TPipeline extends Action>(pipeline: TPipeline, input?: unknown, options?: RunPipelineOptions): Promise<ExtractOutput<TPipeline>>;
14
+ //# sourceMappingURL=run.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../src/run.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAOH,OAAO,EACL,KAAK,MAAM,EAEX,KAAK,aAAa,EAEnB,MAAM,UAAU,CAAC;AAIlB,sFAAsF;AACtF,MAAM,MAAM,QAAQ,GAAG,KAAK,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,OAAO,CAAC;AAE7E,MAAM,WAAW,kBAAkB;IACjC,6EAA6E;IAC7E,QAAQ,CAAC,EAAE,QAAQ,CAAC;CACrB;AAyGD,+EAA+E;AAC/E,wBAAgB,WAAW,CAAC,SAAS,SAAS,MAAM,EAClD,QAAQ,EAAE,SAAS,EACnB,KAAK,CAAC,EAAE,OAAO,EACf,OAAO,CAAC,EAAE,kBAAkB,GAC3B,OAAO,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC,CAMnC"}
package/dist/run.js ADDED
@@ -0,0 +1,160 @@
1
+ /**
2
+ * Workflow execution: resolves the barnum binary, tsx executor, and worker
3
+ * script, then spawns the workflow as a subprocess.
4
+ */
5
+ import { execFileSync, spawn as nodeSpawn } from "node:child_process";
6
+ import { createRequire } from "node:module";
7
+ import { existsSync } from "node:fs";
8
+ import os from "node:os";
9
+ import path from "node:path";
10
+ import { toAction, } from "./ast.js";
11
+ import { chain } from "./chain.js";
12
+ import { constant } from "./builtins/index.js";
13
+ const __dirname = import.meta.dirname;
14
+ /** Resolve the TypeScript executor. Uses bun if the workflow was launched with bun, otherwise tsx. */
15
+ function resolveExecutor() {
16
+ if (process.versions.bun) {
17
+ return "bun";
18
+ }
19
+ const callerRequire = createRequire(process.argv[1] || import.meta.url);
20
+ const tsxPath = callerRequire.resolve("tsx/cli");
21
+ return `node ${tsxPath}`;
22
+ }
23
+ /** Resolve the platform-specific binary from the @barnum/barnum package artifacts. */
24
+ function resolveInstalledBinary() {
25
+ const platform = os.platform();
26
+ const arch = os.arch();
27
+ let artifactDir;
28
+ let binaryName = "barnum";
29
+ if (platform === "darwin" && arch === "arm64") {
30
+ artifactDir = "macos-arm64";
31
+ }
32
+ else if (platform === "darwin") {
33
+ artifactDir = "macos-x64";
34
+ }
35
+ else if (platform === "linux" && arch === "arm64") {
36
+ artifactDir = "linux-arm64";
37
+ }
38
+ else if (platform === "linux") {
39
+ artifactDir = "linux-x64";
40
+ }
41
+ else if (platform === "win32") {
42
+ artifactDir = "win-x64";
43
+ binaryName = "barnum.exe";
44
+ }
45
+ else {
46
+ return undefined;
47
+ }
48
+ const callerRequire = createRequire(process.argv[1] || import.meta.url);
49
+ try {
50
+ const packageDir = path.dirname(callerRequire.resolve("@barnum/barnum/package.json"));
51
+ const binaryPath = path.join(packageDir, "artifacts", artifactDir, binaryName);
52
+ if (existsSync(binaryPath)) {
53
+ return binaryPath;
54
+ }
55
+ }
56
+ catch {
57
+ // Package not installed
58
+ }
59
+ return undefined;
60
+ }
61
+ /** Resolve the barnum binary. Checks: BARNUM env var, local repo, node_modules. */
62
+ function resolveBinary() {
63
+ if (process.env.BARNUM) {
64
+ return { kind: "Env", path: process.env.BARNUM };
65
+ }
66
+ const repoRoot = path.resolve(__dirname, "../../..");
67
+ if (existsSync(path.join(repoRoot, "Cargo.toml"))) {
68
+ return {
69
+ kind: "Local",
70
+ path: path.join(repoRoot, "target/debug/barnum"),
71
+ };
72
+ }
73
+ const installedBinaryPath = resolveInstalledBinary();
74
+ if (installedBinaryPath) {
75
+ return { kind: "NodeModules", path: installedBinaryPath };
76
+ }
77
+ throw new Error("Could not find barnum binary. Set BARNUM env var or install @barnum/barnum.");
78
+ }
79
+ /** Resolve worker.ts relative to this package. */
80
+ function resolveWorker() {
81
+ return path.resolve(__dirname, "../src/worker.ts");
82
+ }
83
+ /** Build the barnum binary if using the local dev path. Skips if binary already exists. */
84
+ function buildBinaryIfNeeded(binaryPath) {
85
+ if (existsSync(binaryPath)) {
86
+ return;
87
+ }
88
+ const repoRoot = path.resolve(__dirname, "../../..");
89
+ // eslint-disable-next-line no-console
90
+ console.error("[barnum] building CLI binary (cargo build -p barnum_cli)...");
91
+ execFileSync("cargo", ["build", "-p", "barnum_cli"], {
92
+ cwd: repoRoot,
93
+ stdio: "inherit",
94
+ });
95
+ }
96
+ /** Run a pipeline to completion. Returns the workflow's final output value. */
97
+ export function runPipeline(pipeline, input, options) {
98
+ const workflow = input === undefined
99
+ ? pipeline
100
+ : toAction(chain(toAction(constant(input)), toAction(pipeline)));
101
+ return spawnBarnum({ workflow }, options?.logLevel);
102
+ }
103
+ /** Spawn the barnum CLI with the given config. Returns the parsed final value from stdout. */
104
+ function spawnBarnum(config, logLevel) {
105
+ const binaryResolution = resolveBinary();
106
+ if (binaryResolution.kind === "Local") {
107
+ buildBinaryIfNeeded(binaryResolution.path);
108
+ }
109
+ const executor = resolveExecutor();
110
+ const worker = resolveWorker();
111
+ const configJson = JSON.stringify(config);
112
+ const cliArgs = [
113
+ "run",
114
+ "--config",
115
+ configJson,
116
+ "--executor",
117
+ executor,
118
+ "--worker",
119
+ worker,
120
+ ];
121
+ if (logLevel) {
122
+ cliArgs.push("--log-level", logLevel);
123
+ }
124
+ return new Promise((resolve, reject) => {
125
+ const child = nodeSpawn(binaryResolution.path, cliArgs, {
126
+ stdio: ["inherit", "pipe", "pipe"],
127
+ });
128
+ const stdoutChunks = [];
129
+ const stderrChunks = [];
130
+ child.stdout?.on("data", (chunk) => {
131
+ stdoutChunks.push(chunk);
132
+ });
133
+ child.stderr?.on("data", (chunk) => {
134
+ stderrChunks.push(chunk);
135
+ process.stderr.write(chunk);
136
+ });
137
+ child.on("error", (error) => {
138
+ reject(new Error(`Failed to spawn barnum: ${error.message}`));
139
+ });
140
+ child.on("close", (code) => {
141
+ if (code !== 0) {
142
+ const stderr = Buffer.concat(stderrChunks).toString("utf8").trim();
143
+ const detail = stderr ? `\n${stderr}` : "";
144
+ reject(new Error(`barnum exited with code ${code}${detail}`));
145
+ return;
146
+ }
147
+ const stdout = Buffer.concat(stdoutChunks).toString("utf8").trim();
148
+ if (!stdout) {
149
+ resolve(undefined);
150
+ return;
151
+ }
152
+ try {
153
+ resolve(JSON.parse(stdout));
154
+ }
155
+ catch {
156
+ reject(new Error(`barnum produced non-JSON output on stdout: ${stdout}`));
157
+ }
158
+ });
159
+ });
160
+ }
@@ -0,0 +1,6 @@
1
+ export { ok, err, some, none } from "./values.js";
2
+ export { createHandler, createHandlerWithConfig, type Handler, } from "./handler.js";
3
+ export { resultSchema, optionSchema } from "./schemas.js";
4
+ export { taggedUnionSchema } from "./builtins/index.js";
5
+ export type { Result, Option, TaggedUnion } from "./ast.js";
6
+ //# sourceMappingURL=runtime.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,EAAE,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AAGlD,OAAO,EACL,aAAa,EACb,uBAAuB,EACvB,KAAK,OAAO,GACb,MAAM,cAAc,CAAC;AAGtB,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC1D,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAGxD,YAAY,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC"}
@@ -0,0 +1,7 @@
1
+ // Runtime value constructors
2
+ export { ok, err, some, none } from "./values.js";
3
+ // Handler creation
4
+ export { createHandler, createHandlerWithConfig, } from "./handler.js";
5
+ // Schema builders
6
+ export { resultSchema, optionSchema } from "./schemas.js";
7
+ export { taggedUnionSchema } from "./builtins/index.js";
@@ -0,0 +1,9 @@
1
+ import type { JSONSchema7 } from "json-schema";
2
+ import { type z } from "zod";
3
+ /**
4
+ * Convert a Zod schema to a JSON Schema document suitable for embedding
5
+ * in the serialized AST. Throws if the schema contains types that can't
6
+ * survive the TS → JSON → Rust boundary.
7
+ */
8
+ export declare function zodToCheckedJsonSchema(schema: z.ZodType, label: string): JSONSchema7;
9
+ //# sourceMappingURL=schema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,EAAE,KAAK,CAAC,EAAgB,MAAM,KAAK,CAAC;AAoF3C;;;;GAIG;AACH,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,CAAC,CAAC,OAAO,EACjB,KAAK,EAAE,MAAM,GACZ,WAAW,CAwBb"}
package/dist/schema.js ADDED
@@ -0,0 +1,95 @@
1
+ import { toJSONSchema } from "zod";
2
+ // Zod v4 schema def types that have child schemas.
3
+ // Verified against Zod 4.3.6 internals — every compound type's def
4
+ // shape and child property name is listed here.
5
+ const CHILD_ACCESSORS = {
6
+ object: (def) => Object.values(def.shape),
7
+ array: (def) => [def.element],
8
+ tuple: (def) => [...def.items, ...(def.rest ? [def.rest] : [])],
9
+ union: (def) => def.options,
10
+ intersection: (def) => [def.left, def.right],
11
+ record: (def) => [def.keyType, def.valueType],
12
+ // Wrappers with a single inner type
13
+ nullable: (def) => [def.innerType],
14
+ optional: (def) => [def.innerType],
15
+ nonoptional: (def) => [def.innerType],
16
+ default: (def) => [def.innerType],
17
+ catch: (def) => [def.innerType],
18
+ readonly: (def) => [def.innerType],
19
+ promise: (def) => [def.innerType],
20
+ // Pipe has two children
21
+ pipe: (def) => [def.in, def.out],
22
+ // Lazy resolves to inner schema
23
+ lazy: (def) => [def.getter()],
24
+ };
25
+ /**
26
+ * Walk the Zod schema tree and reject patterns that `toJSONSchema()`
27
+ * handles incorrectly or silently drops:
28
+ *
29
+ * - `z.intersection()` — produces `allOf` with `additionalProperties: false`
30
+ * on both sides (from `io: "output"`), making the intersection unmatchable
31
+ * on Draft 7.
32
+ *
33
+ * - `.refine()` / `.superRefine()` — silently stripped from JSON Schema
34
+ * output, so the Rust side would accept values that fail the refinement.
35
+ * Detected by checking for custom checks (`check._zod.def.check === "custom"`)
36
+ * in the schema's checks array.
37
+ */
38
+ function assertNoUnsupportedPatterns(schema, label, visited = new WeakSet()) {
39
+ if (visited.has(schema)) {
40
+ return;
41
+ }
42
+ visited.add(schema);
43
+ const def = schema._zod.def;
44
+ // Reject intersections
45
+ if (def.type === "intersection") {
46
+ throw new Error(`Handler "${label}": z.intersection() is not supported. ` +
47
+ `It produces broken JSON Schema on Draft 7 because both sides ` +
48
+ `get additionalProperties: false. Use z.object().extend() or ` +
49
+ `z.object().merge() instead.`);
50
+ }
51
+ // Reject custom checks (from .refine() and .superRefine())
52
+ const checks = def.checks;
53
+ if (checks) {
54
+ for (const check of checks) {
55
+ if (check._zod.def.check === "custom") {
56
+ throw new Error(`Handler "${label}": .refine() and .superRefine() are not ` +
57
+ `supported. Custom validations cannot be expressed in JSON ` +
58
+ `Schema and would be silently dropped.`);
59
+ }
60
+ }
61
+ }
62
+ // Recurse into children
63
+ const getChildren = CHILD_ACCESSORS[def.type];
64
+ if (getChildren) {
65
+ for (const child of getChildren(def)) {
66
+ assertNoUnsupportedPatterns(child, label, visited);
67
+ }
68
+ }
69
+ }
70
+ /**
71
+ * Convert a Zod schema to a JSON Schema document suitable for embedding
72
+ * in the serialized AST. Throws if the schema contains types that can't
73
+ * survive the TS → JSON → Rust boundary.
74
+ */
75
+ export function zodToCheckedJsonSchema(schema, label) {
76
+ // Pre-validate: catch patterns that toJSONSchema() handles incorrectly
77
+ assertNoUnsupportedPatterns(schema, label);
78
+ let raw;
79
+ try {
80
+ raw = toJSONSchema(schema, {
81
+ target: "draft-07",
82
+ unrepresentable: "throw",
83
+ io: "output",
84
+ cycles: "throw",
85
+ reused: "inline",
86
+ });
87
+ }
88
+ catch (error) {
89
+ const message = error instanceof Error ? error.message : String(error);
90
+ throw new Error(`Handler "${label}": Zod schema cannot be converted to JSON Schema: ${message}`, { cause: error });
91
+ }
92
+ // Strip $schema — embedded schemas don't need the draft URI.
93
+ const { $schema: _, ...rest } = raw;
94
+ return rest;
95
+ }
@@ -0,0 +1,5 @@
1
+ import { z } from "zod";
2
+ import type { Result, Option } from "./ast.js";
3
+ export declare function resultSchema<TValue, TError>(okSchema: z.ZodType<TValue>, errSchema: z.ZodType<TError>): z.ZodType<Result<TValue, TError>>;
4
+ export declare function optionSchema<TValue>(valueSchema: z.ZodType<TValue>): z.ZodType<Option<TValue>>;
5
+ //# sourceMappingURL=schemas.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schemas.d.ts","sourceRoot":"","sources":["../src/schemas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAE/C,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,EACzC,QAAQ,EAAE,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,EAC3B,SAAS,EAAE,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,GAC3B,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAKnC;AAED,wBAAgB,YAAY,CAAC,MAAM,EACjC,WAAW,EAAE,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,GAC7B,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAK3B"}
@@ -0,0 +1,13 @@
1
+ import { z } from "zod";
2
+ export function resultSchema(okSchema, errSchema) {
3
+ return z.discriminatedUnion("kind", [
4
+ z.object({ kind: z.literal("Result.Ok"), value: okSchema }),
5
+ z.object({ kind: z.literal("Result.Err"), value: errSchema }),
6
+ ]);
7
+ }
8
+ export function optionSchema(valueSchema) {
9
+ return z.discriminatedUnion("kind", [
10
+ z.object({ kind: z.literal("Option.Some"), value: valueSchema }),
11
+ z.object({ kind: z.literal("Option.None"), value: z.null() }),
12
+ ]);
13
+ }
@@ -0,0 +1,24 @@
1
+ import { type Pipeable, type TypedAction } from "./ast.js";
2
+ /**
3
+ * HOAS combinator for type-level error handling. The body callback receives
4
+ * a `throwError` token — a `TypedAction<TError, never>` that, when placed
5
+ * in the pipeline, tags the error as Break, performs to the handler, which
6
+ * restarts the body. The body-level Branch routes to the recovery arm.
7
+ *
8
+ * This handles **type-level errors only** — values returned by handlers via
9
+ * the `Result` type. If a handler panics, throws a JavaScript exception, or
10
+ * the runtime crashes, the existing error propagation path handles it.
11
+ * tryCatch does not catch those. Analogous to Rust's `Result` vs `panic!`.
12
+ *
13
+ * Compiled form (restart+Branch, same substrate as loop/earlyReturn):
14
+ * `Chain(Tag("Continue"),`
15
+ * `RestartHandle(id, GetIndex(0),`
16
+ * `Branch({ Continue: body, Break: recovery })))`
17
+ *
18
+ * throwError = `Chain(Tag("Break"), RestartPerform(id))`
19
+ *
20
+ * When throwError fires: error tagged Break → `RestartPerform` → handler extracts
21
+ * payload → body restarts → Branch takes Break arm → recovery receives error.
22
+ */
23
+ export declare function tryCatch<TIn, TOut, TError>(body: (throwError: TypedAction<TError, never>) => Pipeable<TIn, TOut>, recovery: Pipeable<TError, TOut>): TypedAction<TIn, TOut>;
24
+ //# sourceMappingURL=try-catch.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"try-catch.d.ts","sourceRoot":"","sources":["../src/try-catch.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,QAAQ,EACb,KAAK,WAAW,EAIjB,MAAM,UAAU,CAAC;AASlB;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,QAAQ,CAAC,GAAG,EAAE,IAAI,EAAE,MAAM,EACxC,IAAI,EAAE,CAAC,UAAU,EAAE,WAAW,CAAC,MAAM,EAAE,KAAK,CAAC,KAAK,QAAQ,CAAC,GAAG,EAAE,IAAI,CAAC,EACrE,QAAQ,EAAE,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAC,GAC/B,WAAW,CAAC,GAAG,EAAE,IAAI,CAAC,CAiBxB"}
@@ -0,0 +1,37 @@
1
+ import { toAction, typedAction, buildRestartBranchAction, } from "./ast.js";
2
+ import { chain } from "./chain.js";
3
+ import { tag } from "./builtins/index.js";
4
+ import { allocateRestartHandlerId } from "./effect-id.js";
5
+ // ---------------------------------------------------------------------------
6
+ // tryCatch — type-level error handling via restart+Branch
7
+ // ---------------------------------------------------------------------------
8
+ /**
9
+ * HOAS combinator for type-level error handling. The body callback receives
10
+ * a `throwError` token — a `TypedAction<TError, never>` that, when placed
11
+ * in the pipeline, tags the error as Break, performs to the handler, which
12
+ * restarts the body. The body-level Branch routes to the recovery arm.
13
+ *
14
+ * This handles **type-level errors only** — values returned by handlers via
15
+ * the `Result` type. If a handler panics, throws a JavaScript exception, or
16
+ * the runtime crashes, the existing error propagation path handles it.
17
+ * tryCatch does not catch those. Analogous to Rust's `Result` vs `panic!`.
18
+ *
19
+ * Compiled form (restart+Branch, same substrate as loop/earlyReturn):
20
+ * `Chain(Tag("Continue"),`
21
+ * `RestartHandle(id, GetIndex(0),`
22
+ * `Branch({ Continue: body, Break: recovery })))`
23
+ *
24
+ * throwError = `Chain(Tag("Break"), RestartPerform(id))`
25
+ *
26
+ * When throwError fires: error tagged Break → `RestartPerform` → handler extracts
27
+ * payload → body restarts → Branch takes Break arm → recovery receives error.
28
+ */
29
+ export function tryCatch(body, recovery) {
30
+ const restartHandlerId = allocateRestartHandlerId();
31
+ const throwError = typedAction(toAction(chain(toAction(tag("Break", "LoopResult")), {
32
+ kind: "RestartPerform",
33
+ restart_handler_id: restartHandlerId,
34
+ })));
35
+ const bodyAction = toAction(body(throwError));
36
+ return typedAction(buildRestartBranchAction(restartHandlerId, bodyAction, toAction(recovery)));
37
+ }
@@ -0,0 +1,6 @@
1
+ import type { Result, Option } from "./ast.js";
2
+ export declare function ok<TValue, TError = unknown>(value: TValue): Result<TValue, TError>;
3
+ export declare function err<TValue = unknown, TError = never>(error: TError): Result<TValue, TError>;
4
+ export declare function some<T>(value: T): Option<T>;
5
+ export declare function none<T = unknown>(): Option<T>;
6
+ //# sourceMappingURL=values.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"values.d.ts","sourceRoot":"","sources":["../src/values.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAE/C,wBAAgB,EAAE,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,EACzC,KAAK,EAAE,MAAM,GACZ,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAExB;AAED,wBAAgB,GAAG,CAAC,MAAM,GAAG,OAAO,EAAE,MAAM,GAAG,KAAK,EAClD,KAAK,EAAE,MAAM,GACZ,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAExB;AAED,wBAAgB,IAAI,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAE3C;AAED,wBAAgB,IAAI,CAAC,CAAC,GAAG,OAAO,KAAK,MAAM,CAAC,CAAC,CAAC,CAE7C"}
package/dist/values.js ADDED
@@ -0,0 +1,12 @@
1
+ export function ok(value) {
2
+ return { kind: "Result.Ok", value };
3
+ }
4
+ export function err(error) {
5
+ return { kind: "Result.Err", value: error };
6
+ }
7
+ export function some(value) {
8
+ return { kind: "Option.Some", value };
9
+ }
10
+ export function none() {
11
+ return { kind: "Option.None", value: null };
12
+ }