@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 +76 -83
- package/dist/index.cjs +0 -27
- package/dist/index.d.ts +5 -55
- package/dist/index.js +0 -27
- package/package.json +4 -2
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** |
|
|
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
|
|
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<
|
|
62
|
-
if (input.userId !== "123")
|
|
63
|
-
|
|
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<
|
|
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
|
|
93
|
+
// outputs["fetch-user"] is typed as Result<{ name, email }> | null
|
|
91
94
|
const user = outputs["fetch-user"];
|
|
92
|
-
if (!user ||
|
|
93
|
-
return { to: user.
|
|
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.
|
|
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
|
|
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
|
-
|
|
146
|
+
See the `Result<T>` implementation at https://github.com/klar-web-services/klonk-result.
|
|
142
147
|
|
|
143
|
-
`
|
|
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
|
-
|
|
147
|
-
| { success: true, data: T }
|
|
148
|
-
| { success: false, error: Error }
|
|
149
|
-
```
|
|
151
|
+
import { Result } from "@fkws/klonk-result";
|
|
150
152
|
|
|
151
|
-
|
|
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
|
-
|
|
157
|
+
#### Result Methods
|
|
154
158
|
|
|
155
159
|
```typescript
|
|
156
|
-
|
|
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(
|
|
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
|
|
168
|
-
if (isOk(
|
|
169
|
-
console.log(
|
|
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
|
|
174
|
+
#### Why Result?
|
|
177
175
|
|
|
178
|
-
The
|
|
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 &&
|
|
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 ||
|
|
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 `
|
|
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 (`
|
|
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 {
|
|
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
|
|
346
|
-
async run(input: TABasicTextInferenceInput): Promise<
|
|
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
|
|
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 ||
|
|
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 ||
|
|
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 ||
|
|
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.
|
|
481
|
+
pdf: downloadResult.file,
|
|
487
482
|
instructions: "Extract data from the invoice",
|
|
488
483
|
schema: z.object({
|
|
489
|
-
payee: z.enum(payeesResult.
|
|
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.
|
|
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 ||
|
|
501
|
+
if (!invoiceResult || invoiceResult.isErr()) {
|
|
507
502
|
throw invoiceResult?.error ?? new Error('Failed to parse invoice');
|
|
508
503
|
}
|
|
509
|
-
const invoiceData = invoiceResult
|
|
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
|
|
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 &&
|
|
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 &&
|
|
599
|
-
state.refinedInput = outputs.refine.
|
|
593
|
+
if (outputs.refine && outputs.refine.isOk()) {
|
|
594
|
+
state.refinedInput = outputs.refine.text;
|
|
600
595
|
}
|
|
601
|
-
if (outputs.extract_search_terms &&
|
|
602
|
-
state.searchTerm = outputs.extract_search_terms.
|
|
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 &&
|
|
627
|
-
state.searchResults = outputs.search
|
|
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 &&
|
|
650
|
-
? outputs.generate_response.
|
|
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
|
-
| `
|
|
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
|
-
###
|
|
685
|
+
### Result Methods
|
|
691
686
|
|
|
692
687
|
| Function | Signature | Behavior |
|
|
693
688
|
|----------|-----------|----------|
|
|
694
|
-
| `unwrap(
|
|
695
|
-
| `
|
|
696
|
-
| `
|
|
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:
|
|
709
|
-
Playlist<{ fetch:
|
|
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:
|
|
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 &&
|
|
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>` | `
|
|
721
|
-
| `Option<Result<T, E>>` | `
|
|
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<
|
|
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] :
|
|
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 {
|
|
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.
|
|
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
|
}
|