flowforge-client 0.1.2

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 ADDED
@@ -0,0 +1,343 @@
1
+ # FlowForge TypeScript Client
2
+
3
+ A type-safe, Supabase-style TypeScript client for interacting with FlowForge server from Node.js, Next.js, or any JavaScript runtime.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install flowforge-client
9
+ # or
10
+ pnpm add flowforge-client
11
+ ```
12
+
13
+ ## Quick Start
14
+
15
+ ```typescript
16
+ import { createClient } from 'flowforge-client';
17
+
18
+ const ff = createClient('http://localhost:8000');
19
+
20
+ // All methods return { data, error } - never throws
21
+ const { data, error } = await ff.events.send('order/created', {
22
+ order_id: '123',
23
+ customer: 'Alice',
24
+ total: 99.99
25
+ });
26
+
27
+ if (error) {
28
+ console.error('Failed to send event:', error.message);
29
+ } else {
30
+ console.log('Event ID:', data.id);
31
+ console.log('Triggered runs:', data.runs);
32
+ }
33
+
34
+ // Wait for the run to complete
35
+ const { data: run } = await ff.runs.waitFor(data.runs[0].id);
36
+ console.log('Run output:', run?.output);
37
+ ```
38
+
39
+ ## Usage in Next.js
40
+
41
+ ### Server Actions
42
+
43
+ ```typescript
44
+ // app/actions.ts
45
+ 'use server'
46
+
47
+ import { createClient } from 'flowforge-client';
48
+
49
+ const ff = createClient(process.env.FLOWFORGE_URL!);
50
+
51
+ export async function submitOrder(formData: FormData) {
52
+ const { data, error } = await ff.events.send('order/created', {
53
+ customer: formData.get('customer'),
54
+ items: JSON.parse(formData.get('items') as string),
55
+ });
56
+
57
+ if (error) throw new Error(error.message);
58
+ return { eventId: data.id, runId: data.runs[0]?.id };
59
+ }
60
+ ```
61
+
62
+ ### API Routes
63
+
64
+ ```typescript
65
+ // app/api/workflow/route.ts
66
+ import { createClient } from 'flowforge-client';
67
+ import { NextResponse } from 'next/server';
68
+
69
+ const ff = createClient(process.env.FLOWFORGE_URL!);
70
+
71
+ export async function POST(request: Request) {
72
+ const payload = await request.json();
73
+ const { data, error } = await ff.events.send('user/signup', payload);
74
+
75
+ if (error) {
76
+ return NextResponse.json({ error: error.message }, { status: error.status });
77
+ }
78
+
79
+ return NextResponse.json({
80
+ eventId: data.id,
81
+ runs: data.runs,
82
+ });
83
+ }
84
+ ```
85
+
86
+ ## API Reference
87
+
88
+ ### Creating a Client
89
+
90
+ ```typescript
91
+ import { createClient } from 'flowforge-client';
92
+
93
+ // Simple URL
94
+ const ff = createClient('http://localhost:8000');
95
+
96
+ // With options
97
+ const ff = createClient('http://localhost:8000', {
98
+ apiKey: 'your-api-key', // Optional
99
+ tenantId: 'tenant-123', // Optional (multi-tenant)
100
+ });
101
+ ```
102
+
103
+ ### Events
104
+
105
+ ```typescript
106
+ // Send an event
107
+ const { data, error } = await ff.events.send('order/created', { order_id: '123' });
108
+
109
+ // With options
110
+ const { data } = await ff.events.send('order/created', { order_id: '123' }, {
111
+ id: 'custom-event-id',
112
+ user_id: 'user-456',
113
+ });
114
+
115
+ // Get an event
116
+ const { data: event } = await ff.events.get('event-id');
117
+
118
+ // Query events with filters
119
+ const { data: events } = await ff.events
120
+ .select()
121
+ .eq('name', 'order/*')
122
+ .limit(10)
123
+ .execute();
124
+ ```
125
+
126
+ ### Runs
127
+
128
+ ```typescript
129
+ // Get a run with its steps
130
+ const { data: run } = await ff.runs.get('run-id');
131
+ console.log(run.status); // 'pending' | 'running' | 'completed' | 'failed'
132
+ console.log(run.steps); // Array of step details
133
+
134
+ // Query runs with type-safe filters
135
+ const { data: runs } = await ff.runs
136
+ .select()
137
+ .eq('status', 'completed')
138
+ .eq('function_id', 'process-order')
139
+ .order('created_at', 'desc')
140
+ .limit(10)
141
+ .execute();
142
+
143
+ // Wait for completion (polling)
144
+ const { data: completedRun, error } = await ff.runs.waitFor('run-id', {
145
+ timeout: 60000, // 60 seconds
146
+ interval: 1000, // poll every second
147
+ });
148
+
149
+ // Cancel a run
150
+ await ff.runs.cancel('run-id');
151
+
152
+ // Replay a failed run
153
+ const { data: newRun } = await ff.runs.replay('run-id');
154
+ ```
155
+
156
+ ### Functions
157
+
158
+ ```typescript
159
+ // Query functions with type-safe filters
160
+ const { data: fns } = await ff.functions
161
+ .select()
162
+ .eq('trigger_type', 'event')
163
+ .eq('is_active', true)
164
+ .execute();
165
+
166
+ // Get function details
167
+ const { data: fn } = await ff.functions.get('process-order');
168
+
169
+ // Create a new function
170
+ const { data: created } = await ff.functions.create({
171
+ id: 'my-workflow',
172
+ name: 'My Workflow',
173
+ trigger_type: 'event',
174
+ trigger_value: 'order/*',
175
+ endpoint_url: 'http://localhost:3000/api/workflows/order',
176
+ });
177
+
178
+ // Update a function
179
+ await ff.functions.update('my-workflow', { is_active: false });
180
+
181
+ // Delete a function
182
+ await ff.functions.delete('my-workflow');
183
+ ```
184
+
185
+ ### Tools
186
+
187
+ ```typescript
188
+ // Query tools
189
+ const { data: tools } = await ff.tools
190
+ .select()
191
+ .eq('requires_approval', true)
192
+ .eq('is_active', true)
193
+ .execute();
194
+
195
+ // Get a tool
196
+ const { data: tool } = await ff.tools.get('send-email');
197
+
198
+ // Create a tool
199
+ await ff.tools.create({
200
+ name: 'send-email',
201
+ description: 'Send an email to a recipient',
202
+ parameters: {
203
+ type: 'object',
204
+ properties: {
205
+ to: { type: 'string', description: 'Recipient email' },
206
+ subject: { type: 'string' },
207
+ body: { type: 'string' },
208
+ },
209
+ required: ['to', 'subject', 'body'],
210
+ },
211
+ requires_approval: true,
212
+ });
213
+
214
+ // Update a tool
215
+ await ff.tools.update('send-email', { requires_approval: false });
216
+
217
+ // Delete a tool
218
+ await ff.tools.delete('my-tool');
219
+ ```
220
+
221
+ ### Approvals (Human-in-the-Loop)
222
+
223
+ ```typescript
224
+ // Query pending approvals
225
+ const { data: pending } = await ff.approvals
226
+ .select()
227
+ .eq('status', 'pending')
228
+ .execute();
229
+
230
+ // Get approval details
231
+ const { data: approval } = await ff.approvals.get('approval-id');
232
+
233
+ // Approve a tool call
234
+ await ff.approvals.approve('approval-id');
235
+
236
+ // Approve with modified arguments
237
+ await ff.approvals.approve('approval-id', {
238
+ modifiedArguments: { amount: 100 },
239
+ });
240
+
241
+ // Reject a tool call
242
+ await ff.approvals.reject('approval-id', 'Amount too high');
243
+ ```
244
+
245
+ ### Health & Stats
246
+
247
+ ```typescript
248
+ // Check server health
249
+ const { data: healthy } = await ff.health.check();
250
+ if (healthy) console.log('Server is healthy');
251
+
252
+ // Get statistics
253
+ const { data: stats } = await ff.health.stats();
254
+ console.log(stats.runs.completed);
255
+ console.log(stats.queue.pending);
256
+ ```
257
+
258
+ ## Error Handling
259
+
260
+ All methods return `{ data, error }` and never throw. This makes error handling explicit and type-safe:
261
+
262
+ ```typescript
263
+ import { createClient, FlowForgeError } from 'flowforge-client';
264
+
265
+ const ff = createClient('http://localhost:8000');
266
+
267
+ const { data, error } = await ff.events.send('order/created', {});
268
+
269
+ if (error) {
270
+ console.error('API Error:', error.message);
271
+ console.error('Status:', error.status);
272
+ console.error('Code:', error.code);
273
+ console.error('Details:', error.detail);
274
+ }
275
+ ```
276
+
277
+ ## TypeScript Types
278
+
279
+ All types are exported for your convenience:
280
+
281
+ ```typescript
282
+ import type {
283
+ // Core types
284
+ Run,
285
+ RunWithSteps,
286
+ RunStatus,
287
+ Step,
288
+ StepType,
289
+ StepStatus,
290
+ Event,
291
+ FlowForgeFunction,
292
+ Tool,
293
+ Approval,
294
+ Stats,
295
+ HealthStatus,
296
+ TriggerType,
297
+ ApprovalStatus,
298
+
299
+ // Filter types (for type-safe queries)
300
+ RunFilters,
301
+ FunctionFilters,
302
+ EventFilters,
303
+ ToolFilters,
304
+ ApprovalFilters,
305
+
306
+ // Input types
307
+ CreateFunctionInput,
308
+ UpdateFunctionInput,
309
+ CreateToolInput,
310
+ UpdateToolInput,
311
+ SendEventInput,
312
+
313
+ // Result type
314
+ Result,
315
+ FlowForgeError,
316
+ ClientOptions,
317
+ } from 'flowforge-client';
318
+ ```
319
+
320
+ ## Query Builder
321
+
322
+ The client uses a chainable query builder for filtering and pagination:
323
+
324
+ ```typescript
325
+ // Available methods
326
+ ff.runs
327
+ .select() // Start a query
328
+ .eq('status', 'completed') // Exact match filter
329
+ .order('created_at', 'desc') // Sort results
330
+ .limit(10) // Limit results
331
+ .offset(20) // Skip results (pagination)
332
+ .execute(); // Execute and return array
333
+
334
+ // Get a single result
335
+ const { data: run } = await ff.runs
336
+ .select()
337
+ .eq('function_id', 'my-fn')
338
+ .single();
339
+ ```
340
+
341
+ ## License
342
+
343
+ MIT