@coji/durably-react 0.9.0 → 0.11.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.
- package/README.md +37 -47
- package/dist/index.d.ts +296 -76
- package/dist/index.js +555 -397
- package/dist/index.js.map +1 -1
- package/dist/spa.d.ts +301 -0
- package/dist/spa.js +501 -0
- package/dist/spa.js.map +1 -0
- package/dist/{types-xrRs7jov.d.ts → types-JIBwGTm6.d.ts} +2 -2
- package/package.json +6 -6
- package/dist/client.d.ts +0 -515
- package/dist/client.js +0 -629
- package/dist/client.js.map +0 -1
package/README.md
CHANGED
|
@@ -9,19 +9,42 @@ React bindings for [Durably](https://github.com/coji/durably) - step-oriented re
|
|
|
9
9
|
## Installation
|
|
10
10
|
|
|
11
11
|
```bash
|
|
12
|
-
#
|
|
12
|
+
# Fullstack mode (connects to Durably server)
|
|
13
|
+
npm install @coji/durably-react
|
|
14
|
+
|
|
15
|
+
# SPA mode (runs Durably in the browser with SQLocal)
|
|
13
16
|
npm install @coji/durably-react @coji/durably kysely zod sqlocal
|
|
17
|
+
```
|
|
14
18
|
|
|
15
|
-
|
|
16
|
-
|
|
19
|
+
## Quick Start (Fullstack Mode)
|
|
20
|
+
|
|
21
|
+
```tsx
|
|
22
|
+
import { createDurably } from '@coji/durably-react'
|
|
23
|
+
import type { durably } from './durably.server'
|
|
24
|
+
|
|
25
|
+
// Create type-safe hooks from server's Durably type
|
|
26
|
+
export const durably = createDurably<typeof durably>({
|
|
27
|
+
api: '/api/durably',
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
function MyComponent() {
|
|
31
|
+
const { trigger, isRunning, isCompleted, output } = durably.myJob.useJob()
|
|
32
|
+
|
|
33
|
+
return (
|
|
34
|
+
<button onClick={() => trigger({ id: '123' })} disabled={isRunning}>
|
|
35
|
+
Run
|
|
36
|
+
</button>
|
|
37
|
+
)
|
|
38
|
+
}
|
|
17
39
|
```
|
|
18
40
|
|
|
19
|
-
##
|
|
41
|
+
## SPA Mode
|
|
42
|
+
|
|
43
|
+
For browser-only apps, import from `@coji/durably-react/spa`:
|
|
20
44
|
|
|
21
45
|
```tsx
|
|
22
|
-
import { Suspense } from 'react'
|
|
23
46
|
import { createDurably, defineJob } from '@coji/durably'
|
|
24
|
-
import { DurablyProvider, useJob } from '@coji/durably-react'
|
|
47
|
+
import { DurablyProvider, useJob } from '@coji/durably-react/spa'
|
|
25
48
|
import { SQLocalKysely } from 'sqlocal/kysely'
|
|
26
49
|
import { z } from 'zod'
|
|
27
50
|
|
|
@@ -38,8 +61,9 @@ const myJob = defineJob({
|
|
|
38
61
|
// Initialize Durably
|
|
39
62
|
async function initDurably() {
|
|
40
63
|
const sqlocal = new SQLocalKysely('app.sqlite3')
|
|
41
|
-
const durably = createDurably({
|
|
42
|
-
|
|
64
|
+
const durably = createDurably({
|
|
65
|
+
dialect: sqlocal.dialect,
|
|
66
|
+
jobs: { myJob },
|
|
43
67
|
})
|
|
44
68
|
await durably.init() // migrate + start
|
|
45
69
|
return durably
|
|
@@ -48,11 +72,9 @@ const durablyPromise = initDurably()
|
|
|
48
72
|
|
|
49
73
|
function App() {
|
|
50
74
|
return (
|
|
51
|
-
<
|
|
52
|
-
<
|
|
53
|
-
|
|
54
|
-
</DurablyProvider>
|
|
55
|
-
</Suspense>
|
|
75
|
+
<DurablyProvider durably={durablyPromise} fallback={<div>Loading...</div>}>
|
|
76
|
+
<MyComponent />
|
|
77
|
+
</DurablyProvider>
|
|
56
78
|
)
|
|
57
79
|
}
|
|
58
80
|
|
|
@@ -66,44 +88,12 @@ function MyComponent() {
|
|
|
66
88
|
}
|
|
67
89
|
```
|
|
68
90
|
|
|
69
|
-
## Server-Connected Mode
|
|
70
|
-
|
|
71
|
-
For full-stack apps, use hooks from `@coji/durably-react/client`:
|
|
72
|
-
|
|
73
|
-
```tsx
|
|
74
|
-
import { useJob } from '@coji/durably-react/client'
|
|
75
|
-
|
|
76
|
-
function MyComponent() {
|
|
77
|
-
const {
|
|
78
|
-
trigger,
|
|
79
|
-
status,
|
|
80
|
-
output,
|
|
81
|
-
isRunning,
|
|
82
|
-
isPending,
|
|
83
|
-
isCompleted,
|
|
84
|
-
isFailed,
|
|
85
|
-
isCancelled,
|
|
86
|
-
} = useJob<{ id: string }, { result: number }>({
|
|
87
|
-
api: '/api/durably',
|
|
88
|
-
jobName: 'my-job',
|
|
89
|
-
autoResume: true, // Auto-resume running/pending jobs on mount (default)
|
|
90
|
-
followLatest: true, // Switch to tracking new runs via SSE (default)
|
|
91
|
-
})
|
|
92
|
-
|
|
93
|
-
return (
|
|
94
|
-
<button onClick={() => trigger({ id: '123' })} disabled={isRunning}>
|
|
95
|
-
Run
|
|
96
|
-
</button>
|
|
97
|
-
)
|
|
98
|
-
}
|
|
99
|
-
```
|
|
100
|
-
|
|
101
91
|
## Documentation
|
|
102
92
|
|
|
103
93
|
For full documentation, visit [coji.github.io/durably](https://coji.github.io/durably/).
|
|
104
94
|
|
|
105
|
-
- [
|
|
106
|
-
- [
|
|
95
|
+
- [SPA Hooks](https://coji.github.io/durably/api/durably-react/spa) - SPA mode with OPFS
|
|
96
|
+
- [Fullstack Hooks](https://coji.github.io/durably/api/durably-react/fullstack) - Server-connected mode
|
|
107
97
|
|
|
108
98
|
## License
|
|
109
99
|
|
package/dist/index.d.ts
CHANGED
|
@@ -1,64 +1,37 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
export { D as DurablyEvent } from './types-xrRs7jov.js';
|
|
1
|
+
import { R as RunStatus, L as LogEntry, P as Progress, I as InferInput, a as InferOutput, T as TypedClientRun } from './types-JIBwGTm6.js';
|
|
2
|
+
export { D as DurablyEvent } from './types-JIBwGTm6.js';
|
|
3
|
+
import { JobDefinition, ClientRun } from '@coji/durably';
|
|
4
|
+
export { ClientRun } from '@coji/durably';
|
|
6
5
|
|
|
7
|
-
interface
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
*
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
*
|
|
17
|
-
* @example
|
|
18
|
-
* // With Suspense (recommended)
|
|
19
|
-
* <Suspense fallback={<Loading />}>
|
|
20
|
-
* <DurablyProvider durably={durablyPromise}>
|
|
21
|
-
* <App />
|
|
22
|
-
* </DurablyProvider>
|
|
23
|
-
* </Suspense>
|
|
24
|
-
*
|
|
25
|
-
* @example
|
|
26
|
-
* // With fallback prop
|
|
27
|
-
* <DurablyProvider durably={durablyPromise} fallback={<Loading />}>
|
|
28
|
-
* <App />
|
|
29
|
-
* </DurablyProvider>
|
|
30
|
-
*/
|
|
31
|
-
durably: Durably | Promise<Durably>;
|
|
32
|
-
/**
|
|
33
|
-
* Fallback to show while waiting for the Durably Promise to resolve.
|
|
34
|
-
* This wraps the provider content in a Suspense boundary automatically.
|
|
35
|
-
*/
|
|
36
|
-
fallback?: ReactNode;
|
|
37
|
-
children: ReactNode;
|
|
38
|
-
}
|
|
39
|
-
declare function DurablyProvider({ durably, fallback, children, }: DurablyProviderProps): react_jsx_runtime.JSX.Element;
|
|
40
|
-
declare function useDurably(): DurablyContextValue;
|
|
41
|
-
|
|
42
|
-
interface UseJobOptions {
|
|
6
|
+
interface UseJobClientOptions {
|
|
7
|
+
/**
|
|
8
|
+
* API endpoint URL (e.g., '/api/durably')
|
|
9
|
+
*/
|
|
10
|
+
api: string;
|
|
11
|
+
/**
|
|
12
|
+
* Job name to trigger
|
|
13
|
+
*/
|
|
14
|
+
jobName: string;
|
|
43
15
|
/**
|
|
44
16
|
* Initial Run ID to subscribe to (for reconnection scenarios)
|
|
17
|
+
* When provided, the hook will immediately start subscribing to this run
|
|
45
18
|
*/
|
|
46
19
|
initialRunId?: string;
|
|
47
20
|
/**
|
|
48
|
-
* Automatically resume tracking
|
|
49
|
-
* If a pending or running run exists for this job, the hook will subscribe to it.
|
|
21
|
+
* Automatically resume tracking a running/pending job on mount
|
|
50
22
|
* @default true
|
|
51
23
|
*/
|
|
52
24
|
autoResume?: boolean;
|
|
53
25
|
/**
|
|
54
|
-
* Automatically switch to tracking the latest
|
|
55
|
-
* When true, the hook will update to track any new run for this job as soon as it starts running.
|
|
56
|
-
* When false, the hook will only track the run that was triggered or explicitly set.
|
|
26
|
+
* Automatically switch to tracking the latest triggered job
|
|
57
27
|
* @default true
|
|
58
28
|
*/
|
|
59
29
|
followLatest?: boolean;
|
|
60
30
|
}
|
|
61
|
-
interface
|
|
31
|
+
interface UseJobClientResult<TInput, TOutput> {
|
|
32
|
+
/**
|
|
33
|
+
* Whether the hook is ready (always true for client mode)
|
|
34
|
+
*/
|
|
62
35
|
/**
|
|
63
36
|
* Trigger the job with the given input
|
|
64
37
|
*/
|
|
@@ -121,9 +94,17 @@ interface UseJobResult<TInput, TOutput> {
|
|
|
121
94
|
*/
|
|
122
95
|
reset: () => void;
|
|
123
96
|
}
|
|
124
|
-
|
|
97
|
+
/**
|
|
98
|
+
* Hook for triggering and subscribing to jobs via server API.
|
|
99
|
+
* Uses fetch for triggering and EventSource for SSE subscription.
|
|
100
|
+
*/
|
|
101
|
+
declare function useJob<TInput extends Record<string, unknown> = Record<string, unknown>, TOutput extends Record<string, unknown> = Record<string, unknown>>(options: UseJobClientOptions): UseJobClientResult<TInput, TOutput>;
|
|
125
102
|
|
|
126
|
-
interface
|
|
103
|
+
interface UseJobLogsClientOptions {
|
|
104
|
+
/**
|
|
105
|
+
* API endpoint URL (e.g., '/api/durably')
|
|
106
|
+
*/
|
|
107
|
+
api: string;
|
|
127
108
|
/**
|
|
128
109
|
* The run ID to subscribe to logs for
|
|
129
110
|
*/
|
|
@@ -133,7 +114,10 @@ interface UseJobLogsOptions {
|
|
|
133
114
|
*/
|
|
134
115
|
maxLogs?: number;
|
|
135
116
|
}
|
|
136
|
-
interface
|
|
117
|
+
interface UseJobLogsClientResult {
|
|
118
|
+
/**
|
|
119
|
+
* Whether the hook is ready (always true for client mode)
|
|
120
|
+
*/
|
|
137
121
|
/**
|
|
138
122
|
* Logs collected during execution
|
|
139
123
|
*/
|
|
@@ -144,18 +128,37 @@ interface UseJobLogsResult {
|
|
|
144
128
|
clearLogs: () => void;
|
|
145
129
|
}
|
|
146
130
|
/**
|
|
147
|
-
* Hook for subscribing to logs from a run.
|
|
148
|
-
*
|
|
131
|
+
* Hook for subscribing to logs from a run via server API.
|
|
132
|
+
* Uses EventSource for SSE subscription.
|
|
149
133
|
*/
|
|
150
|
-
declare function useJobLogs(options:
|
|
134
|
+
declare function useJobLogs(options: UseJobLogsClientOptions): UseJobLogsClientResult;
|
|
151
135
|
|
|
152
|
-
interface
|
|
136
|
+
interface UseJobRunClientOptions {
|
|
137
|
+
/**
|
|
138
|
+
* API endpoint URL (e.g., '/api/durably')
|
|
139
|
+
*/
|
|
140
|
+
api: string;
|
|
153
141
|
/**
|
|
154
142
|
* The run ID to subscribe to
|
|
155
143
|
*/
|
|
156
144
|
runId: string | null;
|
|
145
|
+
/**
|
|
146
|
+
* Callback when run starts (transitions to pending/running)
|
|
147
|
+
*/
|
|
148
|
+
onStart?: () => void;
|
|
149
|
+
/**
|
|
150
|
+
* Callback when run completes successfully
|
|
151
|
+
*/
|
|
152
|
+
onComplete?: () => void;
|
|
153
|
+
/**
|
|
154
|
+
* Callback when run fails
|
|
155
|
+
*/
|
|
156
|
+
onFail?: () => void;
|
|
157
157
|
}
|
|
158
|
-
interface
|
|
158
|
+
interface UseJobRunClientResult<TOutput = unknown> {
|
|
159
|
+
/**
|
|
160
|
+
* Whether the hook is ready (always true for client mode)
|
|
161
|
+
*/
|
|
159
162
|
/**
|
|
160
163
|
* Current run status
|
|
161
164
|
*/
|
|
@@ -198,20 +201,158 @@ interface UseJobRunResult<TOutput = unknown> {
|
|
|
198
201
|
isCancelled: boolean;
|
|
199
202
|
}
|
|
200
203
|
/**
|
|
201
|
-
* Hook for subscribing to an existing run
|
|
202
|
-
*
|
|
204
|
+
* Hook for subscribing to an existing run via server API.
|
|
205
|
+
* Uses EventSource for SSE subscription.
|
|
206
|
+
*/
|
|
207
|
+
declare function useJobRun<TOutput = unknown>(options: UseJobRunClientOptions): UseJobRunClientResult<TOutput>;
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Options for createJobHooks
|
|
211
|
+
*/
|
|
212
|
+
interface CreateJobHooksOptions {
|
|
213
|
+
/**
|
|
214
|
+
* API endpoint URL (e.g., '/api/durably')
|
|
215
|
+
*/
|
|
216
|
+
api: string;
|
|
217
|
+
/**
|
|
218
|
+
* Job name (must match the server-side job name)
|
|
219
|
+
*/
|
|
220
|
+
jobName: string;
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Type-safe hooks for a specific job
|
|
224
|
+
*/
|
|
225
|
+
interface JobHooks<TInput, TOutput> {
|
|
226
|
+
/**
|
|
227
|
+
* Hook for triggering and monitoring the job
|
|
228
|
+
*/
|
|
229
|
+
useJob: () => UseJobClientResult<TInput, TOutput>;
|
|
230
|
+
/**
|
|
231
|
+
* Hook for subscribing to an existing run by ID
|
|
232
|
+
*/
|
|
233
|
+
useRun: (runId: string | null) => UseJobRunClientResult<TOutput>;
|
|
234
|
+
/**
|
|
235
|
+
* Hook for subscribing to logs from a run
|
|
236
|
+
*/
|
|
237
|
+
useLogs: (runId: string | null, options?: {
|
|
238
|
+
maxLogs?: number;
|
|
239
|
+
}) => UseJobLogsClientResult;
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* Create type-safe hooks for a specific job.
|
|
243
|
+
*
|
|
244
|
+
* @example
|
|
245
|
+
* ```tsx
|
|
246
|
+
* // Import job type from server (type-only import is safe)
|
|
247
|
+
* import type { importCsvJob } from '~/lib/durably.server'
|
|
248
|
+
* import { createJobHooks } from '@coji/durably-react'
|
|
249
|
+
*
|
|
250
|
+
* const importCsv = createJobHooks<typeof importCsvJob>({
|
|
251
|
+
* api: '/api/durably',
|
|
252
|
+
* jobName: 'import-csv',
|
|
253
|
+
* })
|
|
254
|
+
*
|
|
255
|
+
* // In your component - fully type-safe
|
|
256
|
+
* function CsvImporter() {
|
|
257
|
+
* const { trigger, output, progress, isRunning } = importCsv.useJob()
|
|
258
|
+
*
|
|
259
|
+
* return (
|
|
260
|
+
* <button onClick={() => trigger({ rows: [...] })}>
|
|
261
|
+
* Import
|
|
262
|
+
* </button>
|
|
263
|
+
* )
|
|
264
|
+
* }
|
|
265
|
+
* ```
|
|
266
|
+
*/
|
|
267
|
+
declare function createJobHooks<TJob extends JobDefinition<string, any, any>>(options: CreateJobHooksOptions): JobHooks<InferInput<TJob>, InferOutput<TJob>>;
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Step record returned from the server API
|
|
271
|
+
*/
|
|
272
|
+
interface StepRecord {
|
|
273
|
+
name: string;
|
|
274
|
+
status: 'completed' | 'failed' | 'cancelled';
|
|
275
|
+
output: unknown;
|
|
276
|
+
}
|
|
277
|
+
interface UseRunActionsClientOptions {
|
|
278
|
+
/**
|
|
279
|
+
* API endpoint URL (e.g., '/api/durably')
|
|
280
|
+
*/
|
|
281
|
+
api: string;
|
|
282
|
+
}
|
|
283
|
+
interface UseRunActionsClientResult {
|
|
284
|
+
/**
|
|
285
|
+
* Retry a failed or cancelled run
|
|
286
|
+
*/
|
|
287
|
+
retry: (runId: string) => Promise<void>;
|
|
288
|
+
/**
|
|
289
|
+
* Cancel a pending or running run
|
|
290
|
+
*/
|
|
291
|
+
cancel: (runId: string) => Promise<void>;
|
|
292
|
+
/**
|
|
293
|
+
* Delete a run (only completed, failed, or cancelled runs)
|
|
294
|
+
*/
|
|
295
|
+
deleteRun: (runId: string) => Promise<void>;
|
|
296
|
+
/**
|
|
297
|
+
* Get a single run by ID
|
|
298
|
+
*/
|
|
299
|
+
getRun: (runId: string) => Promise<ClientRun | null>;
|
|
300
|
+
/**
|
|
301
|
+
* Get steps for a run
|
|
302
|
+
*/
|
|
303
|
+
getSteps: (runId: string) => Promise<StepRecord[]>;
|
|
304
|
+
/**
|
|
305
|
+
* Whether an action is in progress
|
|
306
|
+
*/
|
|
307
|
+
isLoading: boolean;
|
|
308
|
+
/**
|
|
309
|
+
* Error message from last action
|
|
310
|
+
*/
|
|
311
|
+
error: string | null;
|
|
312
|
+
}
|
|
313
|
+
/**
|
|
314
|
+
* Hook for run actions (retry, cancel) via server API.
|
|
315
|
+
*
|
|
316
|
+
* @example
|
|
317
|
+
* ```tsx
|
|
318
|
+
* function RunActions({ runId, status }: { runId: string; status: string }) {
|
|
319
|
+
* const { retry, cancel, isLoading, error } = useRunActions({
|
|
320
|
+
* api: '/api/durably',
|
|
321
|
+
* })
|
|
322
|
+
*
|
|
323
|
+
* return (
|
|
324
|
+
* <div>
|
|
325
|
+
* {status === 'failed' && (
|
|
326
|
+
* <button onClick={() => retry(runId)} disabled={isLoading}>
|
|
327
|
+
* Retry
|
|
328
|
+
* </button>
|
|
329
|
+
* )}
|
|
330
|
+
* {(status === 'pending' || status === 'running') && (
|
|
331
|
+
* <button onClick={() => cancel(runId)} disabled={isLoading}>
|
|
332
|
+
* Cancel
|
|
333
|
+
* </button>
|
|
334
|
+
* )}
|
|
335
|
+
* {error && <span className="error">{error}</span>}
|
|
336
|
+
* </div>
|
|
337
|
+
* )
|
|
338
|
+
* }
|
|
339
|
+
* ```
|
|
203
340
|
*/
|
|
204
|
-
declare function
|
|
341
|
+
declare function useRunActions(options: UseRunActionsClientOptions): UseRunActionsClientResult;
|
|
205
342
|
|
|
206
|
-
interface
|
|
343
|
+
interface UseRunsClientOptions {
|
|
344
|
+
/**
|
|
345
|
+
* API endpoint URL (e.g., '/api/durably')
|
|
346
|
+
*/
|
|
347
|
+
api: string;
|
|
207
348
|
/**
|
|
208
|
-
* Filter by job name
|
|
349
|
+
* Filter by job name(s). Pass a string for one, or an array for multiple.
|
|
209
350
|
*/
|
|
210
|
-
jobName?: string;
|
|
351
|
+
jobName?: string | string[];
|
|
211
352
|
/**
|
|
212
353
|
* Filter by status
|
|
213
354
|
*/
|
|
214
|
-
status?:
|
|
355
|
+
status?: RunStatus;
|
|
215
356
|
/**
|
|
216
357
|
* Filter by labels (all specified labels must match)
|
|
217
358
|
*/
|
|
@@ -222,16 +363,16 @@ interface UseRunsOptions {
|
|
|
222
363
|
*/
|
|
223
364
|
pageSize?: number;
|
|
224
365
|
/**
|
|
225
|
-
* Subscribe to real-time updates
|
|
366
|
+
* Subscribe to real-time updates via SSE (first page only)
|
|
226
367
|
* @default true
|
|
227
368
|
*/
|
|
228
369
|
realtime?: boolean;
|
|
229
370
|
}
|
|
230
|
-
interface
|
|
371
|
+
interface UseRunsClientResult<TInput extends Record<string, unknown> = Record<string, unknown>, TOutput extends Record<string, unknown> | undefined = Record<string, unknown> | undefined> {
|
|
231
372
|
/**
|
|
232
373
|
* List of runs for the current page
|
|
233
374
|
*/
|
|
234
|
-
runs:
|
|
375
|
+
runs: TypedClientRun<TInput, TOutput>[];
|
|
235
376
|
/**
|
|
236
377
|
* Current page (0-indexed)
|
|
237
378
|
*/
|
|
@@ -244,6 +385,10 @@ interface UseRunsResult<TInput extends Record<string, unknown> = Record<string,
|
|
|
244
385
|
* Whether data is being loaded
|
|
245
386
|
*/
|
|
246
387
|
isLoading: boolean;
|
|
388
|
+
/**
|
|
389
|
+
* Error message if fetch failed
|
|
390
|
+
*/
|
|
391
|
+
error: string | null;
|
|
247
392
|
/**
|
|
248
393
|
* Go to the next page
|
|
249
394
|
*/
|
|
@@ -262,14 +407,16 @@ interface UseRunsResult<TInput extends Record<string, unknown> = Record<string,
|
|
|
262
407
|
refresh: () => Promise<void>;
|
|
263
408
|
}
|
|
264
409
|
/**
|
|
265
|
-
* Hook for listing runs
|
|
410
|
+
* Hook for listing runs via server API with pagination.
|
|
411
|
+
* First page (page 0) automatically subscribes to SSE for real-time updates.
|
|
412
|
+
* Other pages are static and require manual refresh.
|
|
266
413
|
*
|
|
267
414
|
* @example With generic type parameter (dashboard with multiple job types)
|
|
268
415
|
* ```tsx
|
|
269
|
-
* type DashboardRun =
|
|
416
|
+
* type DashboardRun = TypedClientRun<ImportInput, ImportOutput> | TypedClientRun<SyncInput, SyncOutput>
|
|
270
417
|
*
|
|
271
418
|
* function Dashboard() {
|
|
272
|
-
* const { runs } = useRuns<DashboardRun>({ pageSize: 10 })
|
|
419
|
+
* const { runs } = useRuns<DashboardRun>({ api: '/api/durably', pageSize: 10 })
|
|
273
420
|
* // runs are typed as DashboardRun[]
|
|
274
421
|
* }
|
|
275
422
|
* ```
|
|
@@ -278,8 +425,8 @@ interface UseRunsResult<TInput extends Record<string, unknown> = Record<string,
|
|
|
278
425
|
* ```tsx
|
|
279
426
|
* const myJob = defineJob({ name: 'my-job', ... })
|
|
280
427
|
*
|
|
281
|
-
* function
|
|
282
|
-
* const { runs } = useRuns(myJob)
|
|
428
|
+
* function RunHistory() {
|
|
429
|
+
* const { runs } = useRuns(myJob, { api: '/api/durably' })
|
|
283
430
|
* // runs[0].output is typed!
|
|
284
431
|
* return <div>{runs[0]?.output?.someField}</div>
|
|
285
432
|
* }
|
|
@@ -287,14 +434,87 @@ interface UseRunsResult<TInput extends Record<string, unknown> = Record<string,
|
|
|
287
434
|
*
|
|
288
435
|
* @example With options only (untyped)
|
|
289
436
|
* ```tsx
|
|
290
|
-
* function
|
|
291
|
-
* const { runs } = useRuns({ pageSize:
|
|
437
|
+
* function RunHistory() {
|
|
438
|
+
* const { runs } = useRuns({ api: '/api/durably', pageSize: 10 })
|
|
292
439
|
* // runs[0].output is unknown
|
|
293
440
|
* }
|
|
294
441
|
* ```
|
|
295
442
|
*/
|
|
296
|
-
declare function useRuns<TRun extends
|
|
297
|
-
declare function useRuns<TName extends string, TInput extends Record<string, unknown>, TOutput extends Record<string, unknown> | undefined>(jobDefinition: JobDefinition<TName, TInput, TOutput>, options
|
|
298
|
-
declare function useRuns(options
|
|
443
|
+
declare function useRuns<TRun extends TypedClientRun<Record<string, unknown>, Record<string, unknown> | undefined>>(options: UseRunsClientOptions): UseRunsClientResult<TRun extends TypedClientRun<infer I, infer _O> ? I : Record<string, unknown>, TRun extends TypedClientRun<infer _I, infer O> ? O : Record<string, unknown>>;
|
|
444
|
+
declare function useRuns<TName extends string, TInput extends Record<string, unknown>, TOutput extends Record<string, unknown> | undefined>(jobDefinition: JobDefinition<TName, TInput, TOutput>, options: Omit<UseRunsClientOptions, 'jobName'>): UseRunsClientResult<TInput, TOutput>;
|
|
445
|
+
declare function useRuns(options: UseRunsClientOptions): UseRunsClientResult;
|
|
446
|
+
|
|
447
|
+
/**
|
|
448
|
+
* Options for createDurably
|
|
449
|
+
*/
|
|
450
|
+
interface CreateDurablyOptions {
|
|
451
|
+
/**
|
|
452
|
+
* API endpoint URL (e.g., '/api/durably')
|
|
453
|
+
*/
|
|
454
|
+
api: string;
|
|
455
|
+
}
|
|
456
|
+
/**
|
|
457
|
+
* Extract the jobs record from a Durably instance type.
|
|
458
|
+
* Allows `createDurably<typeof serverDurably>()` to infer job types.
|
|
459
|
+
*/
|
|
460
|
+
type ExtractJobs<T> = T extends {
|
|
461
|
+
readonly jobs: infer TJobs;
|
|
462
|
+
} ? TJobs : T;
|
|
463
|
+
/**
|
|
464
|
+
* A type-safe Durably client with per-job hooks and cross-job utilities.
|
|
465
|
+
*/
|
|
466
|
+
type DurablyClient<T> = {
|
|
467
|
+
[K in keyof ExtractJobs<T>]: JobHooks<InferInput<ExtractJobs<T>[K]>, InferOutput<ExtractJobs<T>[K]>>;
|
|
468
|
+
} & {
|
|
469
|
+
/**
|
|
470
|
+
* List runs with pagination and real-time updates (cross-job).
|
|
471
|
+
* The `api` option is pre-configured.
|
|
472
|
+
*/
|
|
473
|
+
useRuns: <TInput extends Record<string, unknown> = Record<string, unknown>, TOutput extends Record<string, unknown> | undefined = Record<string, unknown> | undefined>(options?: Omit<UseRunsClientOptions, 'api'>) => UseRunsClientResult<TInput, TOutput>;
|
|
474
|
+
/**
|
|
475
|
+
* Run actions: retry, cancel, delete, getRun, getSteps (cross-job).
|
|
476
|
+
* The `api` option is pre-configured.
|
|
477
|
+
*/
|
|
478
|
+
useRunActions: () => UseRunActionsClientResult;
|
|
479
|
+
};
|
|
480
|
+
/**
|
|
481
|
+
* Create a type-safe Durably client for React.
|
|
482
|
+
*
|
|
483
|
+
* Uses the same name as the server-side `createDurably` — the API endpoint
|
|
484
|
+
* option distinguishes it from the server constructor.
|
|
485
|
+
*
|
|
486
|
+
* @example
|
|
487
|
+
* ```tsx
|
|
488
|
+
* // Server: create Durably instance
|
|
489
|
+
* // app/lib/durably.server.ts
|
|
490
|
+
* import { createDurably } from '@coji/durably'
|
|
491
|
+
* export const durably = createDurably({
|
|
492
|
+
* dialect,
|
|
493
|
+
* jobs: { importCsv: importCsvJob, syncUsers: syncUsersJob },
|
|
494
|
+
* })
|
|
495
|
+
*
|
|
496
|
+
* // Client: create typed hooks
|
|
497
|
+
* // app/lib/durably.ts
|
|
498
|
+
* import type { durably as serverDurably } from '~/lib/durably.server'
|
|
499
|
+
* import { createDurably } from '@coji/durably-react'
|
|
500
|
+
*
|
|
501
|
+
* export const durably = createDurably<typeof serverDurably>({
|
|
502
|
+
* api: '/api/durably',
|
|
503
|
+
* })
|
|
504
|
+
*
|
|
505
|
+
* // In your component — fully type-safe with autocomplete
|
|
506
|
+
* function CsvImporter() {
|
|
507
|
+
* const { trigger, output, isRunning } = durably.importCsv.useJob()
|
|
508
|
+
* return <button onClick={() => trigger({ rows: [...] })}>Import</button>
|
|
509
|
+
* }
|
|
510
|
+
*
|
|
511
|
+
* // Cross-job hooks
|
|
512
|
+
* function Dashboard() {
|
|
513
|
+
* const { runs, nextPage } = durably.useRuns({ pageSize: 10 })
|
|
514
|
+
* const { retry, cancel } = durably.useRunActions()
|
|
515
|
+
* }
|
|
516
|
+
* ```
|
|
517
|
+
*/
|
|
518
|
+
declare function createDurably<T>(options: CreateDurablyOptions): DurablyClient<T>;
|
|
299
519
|
|
|
300
|
-
export {
|
|
520
|
+
export { type CreateDurablyOptions, type CreateJobHooksOptions, type DurablyClient, type JobHooks, LogEntry, Progress, RunStatus, type StepRecord, TypedClientRun, type UseJobClientOptions, type UseJobClientResult, type UseJobLogsClientOptions, type UseJobLogsClientResult, type UseJobRunClientOptions, type UseJobRunClientResult, type UseRunActionsClientOptions, type UseRunActionsClientResult, type UseRunsClientOptions, type UseRunsClientResult, createDurably, createJobHooks, useJob, useJobLogs, useJobRun, useRunActions, useRuns };
|