@coji/durably-react 0.10.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 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
- # Browser mode (with SQLocal)
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
- # Server-connected mode (client only)
16
- npm install @coji/durably-react
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
- ## Quick Start
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({ dialect: sqlocal.dialect }).register({
42
- myJob,
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
- <Suspense fallback={<div>Loading...</div>}>
52
- <DurablyProvider durably={durablyPromise}>
53
- <MyComponent />
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
- - [React Guide](https://coji.github.io/durably/guide/react) - Browser mode with hooks
106
- - [Full-Stack Guide](https://coji.github.io/durably/guide/full-stack) - Server-connected mode
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 * as react_jsx_runtime from 'react/jsx-runtime';
2
- import { Durably, JobDefinition } from '@coji/durably';
3
- import { ReactNode } from 'react';
4
- import { R as RunStatus, L as LogEntry, P as Progress, T as TypedRun } from './types-xrRs7jov.js';
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 DurablyContextValue {
8
- durably: Durably;
9
- }
10
- interface DurablyProviderProps {
11
- /**
12
- * Durably instance or Promise that resolves to one.
13
- * The instance should already be initialized via `await durably.init()`.
14
- *
15
- * When passing a Promise, wrap the provider with Suspense or use the fallback prop.
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 any pending or running job on initialization.
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 running job when a new run starts.
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 UseJobResult<TInput, TOutput> {
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
- declare function useJob<TName extends string, TInput extends Record<string, unknown>, TOutput extends Record<string, unknown> | void>(jobDefinition: JobDefinition<TName, TInput, TOutput>, options?: UseJobOptions): UseJobResult<TInput, TOutput>;
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 UseJobLogsOptions {
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 UseJobLogsResult {
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
- * Use this when you only need logs, not full run status.
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: UseJobLogsOptions): UseJobLogsResult;
134
+ declare function useJobLogs(options: UseJobLogsClientOptions): UseJobLogsClientResult;
151
135
 
152
- interface UseJobRunOptions {
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 UseJobRunResult<TOutput = unknown> {
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 by ID.
202
- * Use this when you have a runId and want to track its status.
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 useJobRun<TOutput = unknown>(options: UseJobRunOptions): UseJobRunResult<TOutput>;
341
+ declare function useRunActions(options: UseRunActionsClientOptions): UseRunActionsClientResult;
205
342
 
206
- interface UseRunsOptions {
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?: 'pending' | 'running' | 'completed' | 'failed' | 'cancelled';
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 UseRunsResult<TInput extends Record<string, unknown> = Record<string, unknown>, TOutput extends Record<string, unknown> | undefined = Record<string, unknown> | undefined> {
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: TypedRun<TInput, TOutput>[];
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 with pagination and real-time updates.
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 = TypedRun<ImportInput, ImportOutput> | TypedRun<SyncInput, SyncOutput>
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 Dashboard() {
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 Dashboard() {
291
- * const { runs } = useRuns({ pageSize: 20 })
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 TypedRun<Record<string, unknown>, Record<string, unknown> | undefined>>(options?: UseRunsOptions): UseRunsResult<TRun extends TypedRun<infer I, infer _O> ? I : Record<string, unknown>, TRun extends TypedRun<infer _I, infer O> ? O : Record<string, unknown>>;
297
- declare function useRuns<TName extends string, TInput extends Record<string, unknown>, TOutput extends Record<string, unknown> | undefined>(jobDefinition: JobDefinition<TName, TInput, TOutput>, options?: Omit<UseRunsOptions, 'jobName'>): UseRunsResult<TInput, TOutput>;
298
- declare function useRuns(options?: UseRunsOptions): UseRunsResult;
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 { DurablyProvider, type DurablyProviderProps, LogEntry, Progress, RunStatus, TypedRun, type UseJobLogsOptions, type UseJobLogsResult, type UseJobOptions, type UseJobResult, type UseJobRunOptions, type UseJobRunResult, type UseRunsOptions, type UseRunsResult, useDurably, useJob, useJobLogs, useJobRun, useRuns };
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 };