@fkws/klonk 0.0.23 → 0.0.25

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/README.md CHANGED
@@ -44,7 +44,7 @@ npm i @fkws/klonk
44
44
  | **Runtimes** | Node.js 18+, Bun 1.0+, Deno (via npm specifier, best-effort) |
45
45
  | **Module** | ESM (native) and CJS (via bundled `/dist`) |
46
46
  | **TypeScript** | 5.0+ (required for full type inference) |
47
- | **Dependencies** | Zero runtime dependencies |
47
+ | **Dependencies** | `@fkws/klonk-result` |
48
48
 
49
49
  **Status:** Pre-1.0, API may change between minor versions. Aiming for stability by 1.0.
50
50
 
@@ -53,22 +53,25 @@ npm i @fkws/klonk
53
53
  Copy-paste this to see Klonk in action. One trigger, two tasks, fully typed outputs:
54
54
 
55
55
  ```typescript
56
- import { Task, Trigger, Workflow, Railroad, isOk } from "@fkws/klonk";
56
+ import { Task, Trigger, Workflow } from "@fkws/klonk";
57
+ import { Result } from "@fkws/klonk-result";
57
58
 
58
59
  // 1. Define two simple tasks
59
60
  class FetchUser<I extends string> extends Task<{ userId: string }, { name: string; email: string }, I> {
60
61
  async validateInput(input: { userId: string }) { return !!input.userId; }
61
- async run(input: { userId: string }): Promise<Railroad<{ name: string; email: string }>> {
62
- if (input.userId !== "123") return { success: false, error: new Error("User not found") };
63
- return { success: true, data: { name: "Alice", email: "alice@example.com" } };
62
+ async run(input: { userId: string }): Promise<Result<{ name: string; email: string }>> {
63
+ if (input.userId !== "123") {
64
+ return new Result({ success: false, error: new Error("User not found") });
65
+ }
66
+ return new Result({ success: true, data: { name: "Alice", email: "alice@example.com" } });
64
67
  }
65
68
  }
66
69
 
67
70
  class SendEmail<I extends string> extends Task<{ to: string; subject: string }, { sent: boolean }, I> {
68
71
  async validateInput(input: { to: string; subject: string }) { return !!input.to; }
69
- async run(input: { to: string; subject: string }): Promise<Railroad<{ sent: boolean }>> {
72
+ async run(input: { to: string; subject: string }): Promise<Result<{ sent: boolean }>> {
70
73
  console.log(`📧 Sending "${input.subject}" to ${input.to}`);
71
- return { success: true, data: { sent: true } };
74
+ return new Result({ success: true, data: { sent: true } });
72
75
  }
73
76
  }
74
77
 
@@ -87,10 +90,10 @@ const workflow = Workflow.create()
87
90
 
88
91
  .addTask(new SendEmail("send-email"))
89
92
  .input((source, outputs) => {
90
- // outputs["fetch-user"] is typed as Railroad<{ name, email }> | null
93
+ // outputs["fetch-user"] is typed as Result<{ name, email }> | null
91
94
  const user = outputs["fetch-user"];
92
- if (!user || !isOk(user)) return null; // skip if failed
93
- return { to: user.data.email, subject: `Welcome, ${user.data.name}!` };
95
+ if (!user || user.isErr()) return null; // skip if failed
96
+ return { to: user.email, subject: `Welcome, ${user.name}!` };
94
97
  })
95
98
  );
96
99
 
@@ -100,7 +103,7 @@ workflow.start({ callback: (src, out) => console.log("✅ Done!", out) });
100
103
  **What you just saw:**
101
104
  - `source.data.userId` is typed from the trigger
102
105
  - `outputs["fetch-user"]` is typed by the task's ident string literal
103
- - `user.data.email` is narrowed after the `isOk()` check
106
+ - `user.email` is narrowed after the `isErr()` guard
104
107
 
105
108
  ## TypeScript Magic Moment
106
109
 
@@ -136,61 +139,54 @@ A `Task` is the smallest unit of work. It's an abstract class with two main meth
136
139
  - `validateInput(input)`: Runtime validation of the task's input (on top of strong typing).
137
140
  - `run(input)`: Executes the task's logic.
138
141
 
139
- Tasks use a `Railroad` return type - a discriminated union for handling success and error states without throwing exceptions. Inspired by Rust's `Result<T, E>` type, it comes with familiar helper functions like `unwrap()`, `unwrapOr()`, and more.
142
+ Tasks return `Result<T>` from `@fkws/klonk-result` for handling success and error states without throwing exceptions. It's inspired by Rust's `Result<T, E>` type and provides convenient methods like `isOk()`, `isErr()`, and `unwrap()`.
143
+
144
+ ### Result (Rust-inspired Result Type)
140
145
 
141
- ### Railroad (Rust-inspired Result Type)
146
+ See the `Result<T>` implementation at https://github.com/klar-web-services/klonk-result.
142
147
 
143
- `Railroad<T>` is Klonk's version of Rust's `Result<T, E>`. It's a discriminated union that forces you to handle both success and error cases:
148
+ `Result<T>` is Klonk's Rust-inspired result type. It forces you to handle both success and error cases:
144
149
 
145
150
  ```typescript
146
- type Railroad<T> =
147
- | { success: true, data: T }
148
- | { success: false, error: Error }
149
- ```
151
+ import { Result } from "@fkws/klonk-result";
150
152
 
151
- #### Helper Functions
153
+ const ok = new Result({ success: true, data: { value: 42 } });
154
+ const err = new Result({ success: false, error: new Error("oops") });
155
+ ```
152
156
 
153
- Klonk provides Rust-inspired helper functions for working with `Railroad`:
157
+ #### Result Methods
154
158
 
155
159
  ```typescript
156
- import { unwrap, unwrapOr, unwrapOrElse, isOk, isErr } from "@fkws/klonk";
160
+ // isErr: Type guard for error case
161
+ if (err.isErr()) {
162
+ console.log(err.error); // TypeScript knows it's error
163
+ }
157
164
 
158
165
  // unwrap: Get data or throw error (like Rust's .unwrap())
159
- const data = unwrap(result); // Returns T or throws
160
-
161
- // unwrapOr: Get data or return a default value
162
- const data = unwrapOr(result, defaultValue); // Returns T
163
-
164
- // unwrapOrElse: Get data or compute a fallback from the error
165
- const data = unwrapOrElse(result, (err) => computeFallback(err));
166
+ const data = ok.unwrap(); // Returns the data or throws
166
167
 
167
- // isOk / isErr: Type guards for narrowing
168
- if (isOk(result)) {
169
- console.log(result.data); // TypeScript knows it's success
170
- }
171
- if (isErr(result)) {
172
- console.log(result.error); // TypeScript knows it's error
168
+ // When isOk() is true, you can access T's members directly (proxy forwarding).
169
+ if (ok.isOk()) {
170
+ console.log(ok.value);
173
171
  }
174
172
  ```
175
173
 
176
- #### Why Railroad?
174
+ #### Why Result?
177
175
 
178
- The name "Railroad" comes from Railway Oriented Programming, where success travels the "happy path" and errors get shunted to the "error track". Combined with TypeScript's type narrowing, you get explicit error handling without exceptions. If you like Rust's `Result`, you'll feel at home.
176
+ The `Result` type keeps error handling explicit without forcing exceptions. Combined with TypeScript's narrowing, you get the ergonomics of Rust's `Result` with JS-friendly patterns.
179
177
 
180
178
  ### Playlist
181
179
 
182
180
  A `Playlist` is a sequence of `Tasks` executed in order. Each task has access to the outputs of all previous tasks, in a fully type-safe way. You build a `Playlist` by chaining `.addTask().input()` calls:
183
181
 
184
182
  ```typescript
185
- import { isOk } from "@fkws/klonk";
186
-
187
183
  playlist
188
184
  .addTask(new FetchTask("fetch"))
189
185
  .input((source) => ({ url: source.targetUrl }))
190
186
  .addTask(new ParseTask("parse"))
191
187
  .input((source, outputs) => ({
192
188
  // Use isOk for Rust-style type narrowing!
193
- html: outputs.fetch && isOk(outputs.fetch) ? outputs.fetch.data.body : ""
189
+ html: outputs.fetch && outputs.fetch.isOk() ? outputs.fetch.body : ""
194
190
  }))
195
191
  ```
196
192
 
@@ -201,13 +197,11 @@ playlist
201
197
  Need to conditionally skip a task? Just return `null` from the input builder:
202
198
 
203
199
  ```typescript
204
- import { isOk } from "@fkws/klonk";
205
-
206
200
  playlist
207
201
  .addTask(new NotifyTask("notify"))
208
202
  .input((source, outputs) => {
209
203
  // Skip notification if previous task failed - using isOk!
210
- if (!outputs.fetch || !isOk(outputs.fetch)) {
204
+ if (!outputs.fetch || outputs.fetch.isErr()) {
211
205
  return null; // Task will be skipped!
212
206
  }
213
207
  return { message: "Success!", level: "info" };
@@ -215,7 +209,7 @@ playlist
215
209
  ```
216
210
 
217
211
  When a task is skipped:
218
- - Its output in the `outputs` map is `null` (not a `Railroad`)
212
+ - Its output in the `outputs` map is `null` (not a `Result`)
219
213
  - The playlist continues to the next task
220
214
  - Subsequent tasks can check `if (outputs.notify === null)` to know it was skipped
221
215
 
@@ -223,7 +217,7 @@ This gives you Rust-like `Option` semantics using TypeScript's native `null` - n
223
217
 
224
218
  #### Task Retries
225
219
 
226
- When a task fails (`success: false`), it can be automatically retried. Retry behavior is configured on the `Machine` state or `Workflow`:
220
+ When a task fails (`Result.isErr()`), it can be automatically retried. Retry behavior is configured on the `Machine` state or `Workflow`:
227
221
 
228
222
  ```typescript
229
223
  // On a Machine state:
@@ -307,7 +301,8 @@ Coming soon(ish)! Klonkworks will be a collection of pre-built Tasks, Triggers,
307
301
  Here's how you create a custom `Task`. This task uses an AI client to perform text inference.
308
302
 
309
303
  ```typescript
310
- import { Railroad, Task } from "@fkws/klonk";
304
+ import { Task } from "@fkws/klonk";
305
+ import { Result } from "@fkws/klonk-result";
311
306
  import { OpenRouterClient } from "./common/OpenrouterClient"
312
307
  import { Model } from "./common/models";
313
308
 
@@ -342,8 +337,8 @@ export class TABasicTextInference<IdentType extends string> extends Task<
342
337
  return true;
343
338
  }
344
339
 
345
- // The core logic of your task. It must return a Railroad type.
346
- async run(input: TABasicTextInferenceInput): Promise<Railroad<TABasicTextInferenceOutput>> {
340
+ // The core logic of your task. It must return a Result type.
341
+ async run(input: TABasicTextInferenceInput): Promise<Result<TABasicTextInferenceOutput>> {
347
342
  try {
348
343
  const result = await this.client.basicTextInference({
349
344
  inputText: input.inputText,
@@ -351,16 +346,16 @@ export class TABasicTextInference<IdentType extends string> extends Task<
351
346
  model: input.model
352
347
  });
353
348
  // On success, return a success object with your data.
354
- return {
349
+ return new Result({
355
350
  success: true,
356
351
  data: { text: result }
357
- };
352
+ });
358
353
  } catch (error) {
359
354
  // On failure, return an error object.
360
- return {
355
+ return new Result({
361
356
  success: false,
362
357
  error: error instanceof Error ? error : new Error(String(error))
363
- };
358
+ });
364
359
  }
365
360
  }
366
361
  }
@@ -414,7 +409,7 @@ Notice the fluent `.addTask(task).input(builder)` syntax - each task's input bui
414
409
 
415
410
  ```typescript
416
411
  import { z } from 'zod';
417
- import { Workflow, isOk } from '@fkws/klonk';
412
+ import { Workflow } from '@fkws/klonk';
418
413
 
419
414
  // The following example requires tasks, integrations and a trigger.
420
415
  // Soon, you will be able to import these from @fkws/klonkworks.
@@ -468,32 +463,32 @@ const workflow = Workflow.create()
468
463
  // Access outputs of previous tasks - fully typed!
469
464
  // Check for null (skipped) and success
470
465
  const downloadResult = outputs['download-invoice-pdf'];
471
- if (!downloadResult || !isOk(downloadResult)) {
466
+ if (!downloadResult || downloadResult.isErr()) {
472
467
  throw downloadResult?.error ?? new Error('Failed to download invoice PDF');
473
468
  }
474
469
 
475
470
  const payeesResult = outputs['get-payees'];
476
- if (!payeesResult || !isOk(payeesResult)) {
471
+ if (!payeesResult || payeesResult.isErr()) {
477
472
  throw payeesResult?.error ?? new Error('Failed to load payees');
478
473
  }
479
474
 
480
475
  const expenseTypesResult = outputs['get-expense-types'];
481
- if (!expenseTypesResult || !isOk(expenseTypesResult)) {
476
+ if (!expenseTypesResult || expenseTypesResult.isErr()) {
482
477
  throw expenseTypesResult?.error ?? new Error('Failed to load expense types');
483
478
  }
484
479
 
485
480
  return {
486
- pdf: downloadResult.data.file,
481
+ pdf: downloadResult.file,
487
482
  instructions: "Extract data from the invoice",
488
483
  schema: z.object({
489
- payee: z.enum(payeesResult.data.map(p => p.id) as [string, ...string[]])
484
+ payee: z.enum(payeesResult.map(p => p.id) as [string, ...string[]])
490
485
  .describe("The payee id"),
491
486
  total: z.number()
492
487
  .describe("The total amount"),
493
488
  invoice_date: z.string()
494
489
  .regex(/^\d{4}-\d{2}-\d{2}$/)
495
490
  .describe("Date as YYYY-MM-DD"),
496
- expense_type: z.enum(expenseTypesResult.data.map(e => e.id) as [string, ...string[]])
491
+ expense_type: z.enum(expenseTypesResult.map(e => e.id) as [string, ...string[]])
497
492
  .describe("The expense type id")
498
493
  })
499
494
  }
@@ -503,10 +498,10 @@ const workflow = Workflow.create()
503
498
  .addTask(new TACreateNotionDatabaseItem("create-notion-invoice", notionProvider))
504
499
  .input((source, outputs) => {
505
500
  const invoiceResult = outputs['parse-invoice'];
506
- if (!invoiceResult || !isOk(invoiceResult)) {
501
+ if (!invoiceResult || invoiceResult.isErr()) {
507
502
  throw invoiceResult?.error ?? new Error('Failed to parse invoice');
508
503
  }
509
- const invoiceData = invoiceResult.data;
504
+ const invoiceData = invoiceResult;
510
505
  return {
511
506
  database_id: process.env.NOTION_INVOICES_DATABASE_ID!,
512
507
  properties: {
@@ -539,7 +534,7 @@ workflow.start({
539
534
  The `Machine` manages a `StateData` object. Each `StateNode`'s `Playlist` can modify this state, and the `Transitions` between states use it to decide which state to move to next.
540
535
 
541
536
  ```typescript
542
- import { Machine, isOk } from "@fkws/klonk"
537
+ import { Machine } from "@fkws/klonk"
543
538
  import { OpenRouterClient } from "./tasks/common/OpenrouterClient"
544
539
  import { Model } from "./tasks/common/models"
545
540
  import { TABasicTextInference } from "./tasks/TABasicTextInference"
@@ -588,18 +583,18 @@ const webSearchAgent = Machine
588
583
  // Extract search terms from refined input
589
584
  .addTask(new TABasicTextInference("extract_search_terms", client))
590
585
  .input((state, outputs) => ({
591
- inputText: `Original: ${state.input}\n\nRefined: ${outputs.refine && isOk(outputs.refine) ? outputs.refine.data.text : state.input}`,
586
+ inputText: `Original: ${state.input}\n\nRefined: ${outputs.refine && outputs.refine.isOk() ? outputs.refine.text : state.input}`,
592
587
  model: state.model ?? "openai/gpt-5.2",
593
588
  instructions: `Extract one short web search query from the user request and refined prompt.`
594
589
  }))
595
590
 
596
591
  // Update state with results - using isOk for type narrowing
597
592
  .finally((state, outputs) => {
598
- if (outputs.refine && isOk(outputs.refine)) {
599
- state.refinedInput = outputs.refine.data.text;
593
+ if (outputs.refine && outputs.refine.isOk()) {
594
+ state.refinedInput = outputs.refine.text;
600
595
  }
601
- if (outputs.extract_search_terms && isOk(outputs.extract_search_terms)) {
602
- state.searchTerm = outputs.extract_search_terms.data.text;
596
+ if (outputs.extract_search_terms && outputs.extract_search_terms.isOk()) {
597
+ state.searchTerm = outputs.extract_search_terms.text;
603
598
  }
604
599
  })
605
600
  )
@@ -623,8 +618,8 @@ const webSearchAgent = Machine
623
618
  query: state.searchTerm!
624
619
  }))
625
620
  .finally((state, outputs) => {
626
- if (outputs.search && isOk(outputs.search)) {
627
- state.searchResults = outputs.search.data;
621
+ if (outputs.search && outputs.search.isOk()) {
622
+ state.searchResults = outputs.search;
628
623
  }
629
624
  })
630
625
  )
@@ -646,8 +641,8 @@ const webSearchAgent = Machine
646
641
  Write a professional response.`
647
642
  }))
648
643
  .finally((state, outputs) => {
649
- state.finalResponse = outputs.generate_response && isOk(outputs.generate_response)
650
- ? outputs.generate_response.data.text
644
+ state.finalResponse = outputs.generate_response && outputs.generate_response.isOk()
645
+ ? outputs.generate_response.text
651
646
  : "Sorry, an error occurred: " + (outputs.generate_response?.error ?? "unknown");
652
647
  })
653
648
  )
@@ -680,22 +675,20 @@ Klonk's type system is minimal. Here's how it works:
680
675
  | Type | Parameters | Purpose |
681
676
  |------|------------|---------|
682
677
  | `Task<Input, Output, Ident>` | Input shape, output shape, string literal ident | Base class for all tasks |
683
- | `Railroad<Output>` | Success data type | Discriminated union for success/error results (like Rust's `Result`) |
678
+ | `Result<Output>` | Success data type | Rust-inspired result type (from `@fkws/klonk-result`) |
684
679
  | `Playlist<AllOutputs, Source>` | Accumulated output map, source data type | Ordered task sequence with typed chaining |
685
680
  | `Trigger<Ident, Data>` | String literal ident, event payload type | Event source for workflows |
686
681
  | `Workflow<Events>` | Union of trigger event types | Connects triggers to playlists |
687
682
  | `Machine<StateData, AllStateIdents>` | Mutable state shape, union of state idents | Finite state machine with typed transitions |
688
683
  | `StateNode<StateData, Ident, AllStateIdents>` | State shape, this node's ident, all valid transition targets | Individual state with playlist and transitions |
689
684
 
690
- ### Railroad Helper Functions
685
+ ### Result Methods
691
686
 
692
687
  | Function | Signature | Behavior |
693
688
  |----------|-----------|----------|
694
- | `unwrap(r)` | `Railroad<T> → T` | Returns data or throws error |
695
- | `unwrapOr(r, default)` | `Railroad<T>, T T` | Returns data or default value |
696
- | `unwrapOrElse(r, fn)` | `Railroad<T>, (E) T → T` | Returns data or result of fn(error) |
697
- | `isOk(r)` | `Railroad<T> → boolean` | Type guard for success case |
698
- | `isErr(r)` | `Railroad<T> → boolean` | Type guard for error case |
689
+ | `result.unwrap()` | `Result<T> → T` | Returns data or throws error |
690
+ | `result.isOk()` | `Result<T>boolean` | Type guard for success case |
691
+ | `result.isErr()` | `Result<T>boolean` | Type guard for error case |
699
692
 
700
693
  ### How Output Chaining Works
701
694
 
@@ -705,17 +698,17 @@ When you add a task to a playlist, Klonk extends the output type:
705
698
  // Start with empty outputs
706
699
  Playlist<{}, Source>
707
700
  .addTask(new FetchTask("fetch")).input(...)
708
- // Now outputs include: { fetch: Railroad<FetchOutput> | null }
709
- Playlist<{ fetch: Railroad<FetchOutput> | null }, Source>
701
+ // Now outputs include: { fetch: Result<FetchOutput> | null }
702
+ Playlist<{ fetch: Result<FetchOutput> | null }, Source>
710
703
  .addTask(new ParseTask("parse")).input(...)
711
- // Now outputs include both: { fetch: ..., parse: Railroad<ParseOutput> | null }
704
+ // Now outputs include both: { fetch: ..., parse: Result<ParseOutput> | null }
712
705
  ```
713
706
 
714
- The `| null` accounts for the possibility that a task was skipped (when its input builder returns `null`). This is why you'll check for null before using `isOk()` - for example: `outputs.fetch && isOk(outputs.fetch)`. TypeScript then narrows the type so you can safely access `.data`!
707
+ The `| null` accounts for the possibility that a task was skipped (when its input builder returns `null`). This is why you'll check for null before using `isOk()` - for example: `outputs.fetch && outputs.fetch.isOk()`. TypeScript then narrows the type so you can safely access fields!
715
708
 
716
709
  This maps cleanly to Rust's types:
717
710
  | Rust | Klonk (TypeScript) |
718
711
  |------|-------------------|
719
712
  | `Option<T>` | `T \| null` |
720
- | `Result<T, E>` | `Railroad<T>` |
721
- | `Option<Result<T, E>>` | `Railroad<T> \| null` |
713
+ | `Result<T, E>` | `Result<T>` |
714
+ | `Option<Result<T, E>>` | `Result<T> \| null` |
package/dist/index.cjs CHANGED
@@ -30,11 +30,6 @@ var __export = (target, all) => {
30
30
  // src/index.ts
31
31
  var exports_src = {};
32
32
  __export(exports_src, {
33
- unwrapOrElse: () => unwrapOrElse,
34
- unwrapOr: () => unwrapOr,
35
- unwrap: () => unwrap,
36
- isOk: () => isOk,
37
- isErr: () => isErr,
38
33
  Workflow: () => Workflow,
39
34
  Trigger: () => Trigger,
40
35
  Task: () => Task,
@@ -170,28 +165,6 @@ class Workflow {
170
165
  }
171
166
  }
172
167
  // src/prototypes/Task.ts
173
- function isOk(r) {
174
- return r.success === true;
175
- }
176
- function isErr(r) {
177
- return r.success === false;
178
- }
179
- function unwrap(r) {
180
- if (r.success)
181
- return r.data;
182
- throw r.error;
183
- }
184
- function unwrapOr(r, defaultValue) {
185
- if (r.success)
186
- return r.data;
187
- return defaultValue;
188
- }
189
- function unwrapOrElse(r, fn) {
190
- if (r.success)
191
- return r.data;
192
- return fn(r.error);
193
- }
194
-
195
168
  class Task {
196
169
  ident;
197
170
  constructor(ident) {
package/dist/index.d.ts CHANGED
@@ -1,55 +1,4 @@
1
- /**
2
- * A simple discriminated-union result type used by Tasks.
3
- * Prefer returning a `Railroad` from `Task.run` instead of throwing exceptions.
4
- *
5
- * Example:
6
- * - Success: `{ success: true, data: value }`
7
- * - Failure: `{ success: false, error }`
8
- *
9
- * @template OutputType - The success payload type.
10
- * @template ErrorType - Optional error payload type (default `Error`).
11
- */
12
- type Railroad<
13
- OutputType,
14
- ErrorType = Error
15
- > = {
16
- readonly success: true
17
- readonly data: OutputType
18
- } | {
19
- readonly success: false
20
- readonly error: ErrorType
21
- };
22
- /** Type guard: returns true if the Railroad is a success */
23
- declare function isOk<
24
- T,
25
- E
26
- >(r: Railroad<T, E>): r is {
27
- success: true
28
- data: T
29
- };
30
- /** Type guard: returns true if the Railroad is an error */
31
- declare function isErr<
32
- T,
33
- E
34
- >(r: Railroad<T, E>): r is {
35
- success: false
36
- error: E
37
- };
38
- /** Returns the data if success, throws the error if failure (like Rust's unwrap) */
39
- declare function unwrap<
40
- T,
41
- E
42
- >(r: Railroad<T, E>): T;
43
- /** Returns the data if success, or the default value if failure */
44
- declare function unwrapOr<
45
- T,
46
- E
47
- >(r: Railroad<T, E>, defaultValue: T): T;
48
- /** Returns the data if success, or calls the function with the error if failure */
49
- declare function unwrapOrElse<
50
- T,
51
- E
52
- >(r: Railroad<T, E>, fn: (error: E) => T): T;
1
+ import { Result } from "@fkws/klonk-result";
53
2
  /**
54
3
  * Base class for all executable units in Klonk.
55
4
  * Implement `validateInput` for runtime checks and `run` for the actual work.
@@ -82,8 +31,9 @@ declare abstract class Task<
82
31
  * @param input - The input object provided by the Playlist builder.
83
32
  * @returns A `Railroad` containing output data on success, or an error on failure.
84
33
  */
85
- abstract run(input: InputType): Promise<Railroad<OutputType>>;
34
+ abstract run(input: InputType): Promise<Result<OutputType>>;
86
35
  }
36
+ import { Result as Result2 } from "@fkws/klonk-result";
87
37
  /**
88
38
  * @internal Internal assembly type that couples a task with its input builder.
89
39
  */
@@ -118,7 +68,7 @@ interface TaskInputRequired<
118
68
  *
119
69
  * Return `null` to skip this task - its output will be `null` in the outputs map.
120
70
  */
121
- input(builder: (source: SourceType, outputs: AllOutputTypes) => TInput | null): Playlist<AllOutputTypes & { [K in TIdent] : Railroad<TOutput> | null }, SourceType>;
71
+ input(builder: (source: SourceType, outputs: AllOutputTypes) => TInput | null): Playlist<AllOutputTypes & { [K in TIdent] : Result2<TOutput> | null }, SourceType>;
122
72
  }
123
73
  /**
124
74
  * An ordered sequence of Tasks executed with strong type inference.
@@ -607,4 +557,4 @@ type RunOptions = {
607
557
  mode: "infinitely"
608
558
  interval?: number
609
559
  });
610
- export { unwrapOrElse, unwrapOr, unwrap, isOk, isErr, Workflow, TriggerEvent, Trigger, Task, StateNode, RunOptions, Railroad, PlaylistRunOptions, Playlist, Machine };
560
+ export { Workflow, TriggerEvent, Trigger, Task, StateNode, RunOptions, PlaylistRunOptions, Playlist, Machine };
package/dist/index.js CHANGED
@@ -124,28 +124,6 @@ class Workflow {
124
124
  }
125
125
  }
126
126
  // src/prototypes/Task.ts
127
- function isOk(r) {
128
- return r.success === true;
129
- }
130
- function isErr(r) {
131
- return r.success === false;
132
- }
133
- function unwrap(r) {
134
- if (r.success)
135
- return r.data;
136
- throw r.error;
137
- }
138
- function unwrapOr(r, defaultValue) {
139
- if (r.success)
140
- return r.data;
141
- return defaultValue;
142
- }
143
- function unwrapOrElse(r, fn) {
144
- if (r.success)
145
- return r.data;
146
- return fn(r.error);
147
- }
148
-
149
127
  class Task {
150
128
  ident;
151
129
  constructor(ident) {
@@ -487,11 +465,6 @@ class Machine {
487
465
  }
488
466
  }
489
467
  export {
490
- unwrapOrElse,
491
- unwrapOr,
492
- unwrap,
493
- isOk,
494
- isErr,
495
468
  Workflow,
496
469
  Trigger,
497
470
  Task,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fkws/klonk",
3
- "version": "0.0.23",
3
+ "version": "0.0.25",
4
4
  "description": "A lightweight, extensible workflow automation engine for Node.js and Bun",
5
5
  "repository": "https://github.com/klar-web-services/klonk",
6
6
  "homepage": "https://klonk.dev",
@@ -54,5 +54,7 @@
54
54
  "peerDependencies": {
55
55
  "typescript": "^5"
56
56
  },
57
- "dependencies": {}
57
+ "dependencies": {
58
+ "@fkws/klonk-result": "^0.0.5"
59
+ }
58
60
  }