feat: add generic type support for getRun/getRuns and useRuns#15
Conversation
Add multiple ways to get type-safe run access in useRuns: - `useRuns<TRun>(options)` - Pass type parameter for dashboards with multiple job types - `useRuns(jobDefinition, options?)` - Pass JobDefinition to infer types and auto-filter by jobName - `useRuns(options?)` - Untyped usage for simple cases New exports: - `TypedRun<TInput, TOutput>` type for browser hooks - `TypedClientRun<TInput, TOutput>` type for client hooks Examples updated to demonstrate typed dashboard pattern with union types. Closes #14 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
📝 WalkthroughWalkthroughAdded generic, strongly-typed run types (TypedRun / TypedClientRun), three overloads for useRuns (generic union, JobDefinition, untyped), updated hook implementations/exports and examples to use typed runs, made Durably storage/getters generic, bumped packages to 0.7.0, updated docs/changelog, and added Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant Component
participant Hook as "useRuns Hook"
participant ClientAPI as "durably.getRuns / getRun"
participant Storage as "Storage/DB"
Component->>Hook: call useRuns(jobDef | useRuns<DashboardRun>(opts) | useRuns(opts))
Note over Hook: isJobDefinition? → derive jobName & TInput/TOutput\nor use provided generic/options
Hook->>ClientAPI: request runs (jobName?, status?, pageSize?, offset?)
ClientAPI->>Storage: query runs
Storage-->>ClientAPI: raw run records
ClientAPI-->>Hook: raw runs array
Note over Hook: cast/parse → TypedRun<TInput,TOutput>[]\ncompute page/hasMore, expose controls
Hook-->>Component: { runs: TypedRun[], page, hasMore, nextPage, prevPage, goToPage, refresh }
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing touches
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
- Update version from 0.6.1 to 0.7.0 for new feature release - Move [Unreleased] changelog entry to [0.7.0] with today's date 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (4)
packages/durably-react/src/hooks/use-runs.ts (1)
39-42: Inconsistent default type forTOutputbetweenTypedRunandUseRunsResult.
TypedRun(line 10-12) defaultsTOutputtoRecord<string, unknown> | undefined, butUseRunsResult(line 41) defaults it toRecord<string, unknown>withoutundefined. This inconsistency may cause type mismatches when the result type is inferred.🔎 Proposed fix
export interface UseRunsResult< TInput extends Record<string, unknown> = Record<string, unknown>, - TOutput extends Record<string, unknown> | undefined = Record<string, unknown>, + TOutput extends Record<string, unknown> | undefined = + | Record<string, unknown> + | undefined, > {packages/durably-react/src/client/use-runs.ts (1)
96-99: SameTOutputdefault inconsistency as browser hook.
UseRunsClientResultdefaultsTOutputtoRecord<string, unknown>(line 98), butTypedClientRun(line 28-30) defaults toRecord<string, unknown> | undefined. Align these for consistency.🔎 Proposed fix
export interface UseRunsClientResult< TInput extends Record<string, unknown> = Record<string, unknown>, - TOutput extends Record<string, unknown> | undefined = Record<string, unknown>, + TOutput extends Record<string, unknown> | undefined = + | Record<string, unknown> + | undefined, > {examples/browser-react-router-spa/app/routes/_index/dashboard.tsx (1)
13-35: Consider aligningselectedRuntype withDashboardRunfor consistency.The
runsarray is typed asDashboardRun[]via the generic parameter on line 31, butselectedRunis typed asRunon line 35. For type consistency in this example demonstrating typed patterns, consider usingDashboardRunforselectedRunas well.🔎 Suggested type alignment
- const [selectedRun, setSelectedRun] = useState<Run | null>(null) + const [selectedRun, setSelectedRun] = useState<DashboardRun | null>(null)examples/browser-vite-react/src/components/dashboard.tsx (1)
13-32: Consider aligningselectedRuntype withDashboardRunfor consistency.The
runsarray is typed asDashboardRun[]via the generic parameter on line 28, butselectedRunis typed asRunon line 32. For type consistency in this example demonstrating typed patterns, consider usingDashboardRunforselectedRunas well.🔎 Suggested type alignment
- const [selectedRun, setSelectedRun] = useState<Run | null>(null) + const [selectedRun, setSelectedRun] = useState<DashboardRun | null>(null)
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (17)
.prettierignoreCHANGELOG.mdexamples/browser-react-router-spa/app/jobs/index.tsexamples/browser-react-router-spa/app/routes/_index/dashboard.tsxexamples/browser-vite-react/src/components/dashboard.tsxexamples/browser-vite-react/src/jobs/index.tsexamples/fullstack-react-router/app/jobs/index.tsexamples/fullstack-react-router/app/routes/_index/dashboard.tsxpackages/durably-react/docs/llms.mdpackages/durably-react/src/client.tspackages/durably-react/src/client/index.tspackages/durably-react/src/client/use-runs.tspackages/durably-react/src/hooks/use-runs.tspackages/durably-react/src/index.tswebsite/api/durably-react/browser.mdwebsite/api/durably-react/client.mdwebsite/public/llms.txt
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{js,ts,mjs,mts}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{js,ts,mjs,mts}: This library is ESM-only. CommonJS is not supported. Always use top-levelawaitfor async initialization (e.g.,await durably.migrate()). Do not wrap in async IIFE or Promise chains.
Jobs must be defined viadefineJob()and registered withdurably.register(), receiving a step context and payload
Steps are created viastep.run(), with each step's success state and return value persisted automatically
Files:
packages/durably-react/src/client/index.tspackages/durably-react/src/index.tsexamples/fullstack-react-router/app/jobs/index.tspackages/durably-react/src/client/use-runs.tsexamples/browser-react-router-spa/app/jobs/index.tspackages/durably-react/src/hooks/use-runs.tspackages/durably-react/src/client.tsexamples/browser-vite-react/src/jobs/index.ts
🧠 Learnings (8)
📚 Learning: 2026-01-03T06:23:01.913Z
Learnt from: CR
Repo: coji/durably PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-03T06:23:01.913Z
Learning: Applies to **/*.{js,ts,mjs,mts} : Jobs must be defined via `defineJob()` and registered with `durably.register()`, receiving a step context and payload
Applied to files:
CHANGELOG.mdpackages/durably-react/src/index.tsexamples/fullstack-react-router/app/jobs/index.tswebsite/api/durably-react/client.mdpackages/durably-react/src/client/use-runs.tsexamples/browser-react-router-spa/app/jobs/index.tspackages/durably-react/src/hooks/use-runs.tsexamples/browser-vite-react/src/jobs/index.ts
📚 Learning: 2026-01-03T06:23:01.913Z
Learnt from: CR
Repo: coji/durably PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-03T06:23:01.913Z
Learning: Applies to **/*.{js,ts,mjs,mts} : Steps are created via `step.run()`, with each step's success state and return value persisted automatically
Applied to files:
packages/durably-react/src/index.ts
📚 Learning: 2026-01-03T06:23:01.913Z
Learnt from: CR
Repo: coji/durably PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-03T06:23:01.913Z
Learning: Applies to packages/durably/docs/llms.md : Keep `packages/durably/docs/llms.md` in sync with API changes as it is bundled in the npm package and symlinked to `website/public/llms.txt` for web access
Applied to files:
website/public/llms.txt.prettierignorepackages/durably-react/docs/llms.md
📚 Learning: 2026-01-03T06:23:01.913Z
Learnt from: CR
Repo: coji/durably PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-03T06:23:01.913Z
Learning: Applies to **/*.{js,ts,mjs,mts} : This library is ESM-only. CommonJS is not supported. Always use top-level `await` for async initialization (e.g., `await durably.migrate()`). Do not wrap in async IIFE or Promise chains.
Applied to files:
examples/fullstack-react-router/app/jobs/index.tsexamples/browser-react-router-spa/app/jobs/index.tsexamples/browser-vite-react/src/jobs/index.ts
📚 Learning: 2026-01-02T14:03:25.739Z
Learnt from: coji
Repo: coji/durably PR: 11
File: examples/browser-react-router-spa/app/routes/_index/dashboard.tsx:24-34
Timestamp: 2026-01-02T14:03:25.739Z
Learning: In example code under the examples/ directory, error handling is intentionally omitted to keep the code simple and focused on demonstrating the Durably API. Treat these files as illustrative, not production-ready. For real applications, ensure proper error handling and user feedback; do not copy this pattern verbatim into production code.
Applied to files:
examples/browser-vite-react/src/components/dashboard.tsxexamples/fullstack-react-router/app/routes/_index/dashboard.tsxexamples/browser-react-router-spa/app/routes/_index/dashboard.tsx
📚 Learning: 2026-01-02T14:03:31.683Z
Learnt from: coji
Repo: coji/durably PR: 11
File: examples/browser-vite-react/src/components/dashboard.tsx:36-53
Timestamp: 2026-01-02T14:03:31.683Z
Learning: In all example files under the examples directory (e.g., examples/**/*.tsx), prioritize simplicity and clarity over comprehensive error handling. Focus on demonstrating Durably API usage patterns; omit production-grade error handling in examples to keep them readable and centered on core concepts. Where appropriate, include concise comments to explain the pattern being demonstrated.
Applied to files:
examples/browser-vite-react/src/components/dashboard.tsxexamples/fullstack-react-router/app/routes/_index/dashboard.tsxexamples/browser-react-router-spa/app/routes/_index/dashboard.tsx
📚 Learning: 2026-01-02T14:03:32.230Z
Learnt from: coji
Repo: coji/durably PR: 11
File: examples/fullstack-react-router/app/routes/_index.tsx:28-46
Timestamp: 2026-01-02T14:03:32.230Z
Learning: In the coji/durably repository's examples directory, code examples (e.g., any .tsx under examples) should prioritize simplicity and readability over comprehensive error handling to clearly demonstrate the Durably API usage patterns. Include minimal, demonstrative error handling only as necessary for clarity.
Applied to files:
examples/browser-vite-react/src/components/dashboard.tsxexamples/fullstack-react-router/app/routes/_index/dashboard.tsxexamples/browser-react-router-spa/app/routes/_index/dashboard.tsx
📚 Learning: 2026-01-02T14:03:31.517Z
Learnt from: coji
Repo: coji/durably PR: 11
File: examples/browser-vite-react/src/components/dashboard.tsx:24-34
Timestamp: 2026-01-02T14:03:31.517Z
Learning: In example code directories (e.g., examples/browser-vite-react, examples/fullstack-react-router) error handling may be omitted to keep code simple and readable and to focus on demonstrating the Durably API. This is intentional for examples, not a production best practice. When reviewing code in these example paths, verify that the lack of error handling is confined to examples and that the surrounding documentation clearly states this is for illustration. Do not apply this omission as a general guidance for production code.
Applied to files:
examples/browser-vite-react/src/components/dashboard.tsx
🧬 Code graph analysis (4)
examples/fullstack-react-router/app/jobs/index.ts (1)
examples/fullstack-react-router/app/jobs/import-csv.ts (1)
ImportCsvOutput(25-25)
examples/browser-vite-react/src/components/dashboard.tsx (1)
packages/durably-react/src/hooks/use-runs.ts (2)
TypedRun(8-16)useRuns(136-240)
examples/browser-react-router-spa/app/routes/_index/dashboard.tsx (3)
packages/durably-react/src/hooks/use-runs.ts (1)
TypedRun(8-16)examples/browser-react-router-spa/app/jobs/index.ts (6)
DataSyncInput(19-19)DataSyncOutput(20-20)ImportCsvInput(21-21)ImportCsvOutput(16-16)ProcessImageInput(22-22)ProcessImageOutput(23-23)examples/browser-vite-react/src/jobs/index.ts (4)
DataSyncInput(15-15)DataSyncOutput(16-16)ProcessImageInput(17-17)ProcessImageOutput(18-18)
packages/durably-react/src/client/use-runs.ts (4)
packages/durably-react/src/client.ts (4)
TypedClientRun(34-34)ClientRun(33-33)UseRunsClientResult(36-36)useRuns(31-31)packages/durably-react/src/client/index.ts (4)
TypedClientRun(34-34)ClientRun(33-33)UseRunsClientResult(36-36)useRuns(31-31)packages/durably-react/src/hooks/use-runs.ts (1)
useRuns(136-240)packages/durably/src/server.ts (1)
runs(242-261)
🔇 Additional comments (27)
packages/durably-react/src/hooks/use-runs.ts (2)
109-133: Well-designed overload structure for multiple usage patterns.The three overloads cleanly support the different use cases:
- Generic type parameter for multi-job dashboards
- JobDefinition for single-job with auto-filtering
- Untyped for backward compatibility
The conditional type inference in overload 1 (lines 117-119) correctly extracts input/output types from the provided
TRun.
148-161: JobDefinition detection heuristic is reasonable but could be more explicit.The check
'name' in jobDefinitionOrOptions && 'run' in jobDefinitionOrOptionsworks for the current API but could match other objects with these properties. Consider adding a type guard or checking for a discriminant property if JobDefinition gains one in the future.For now, this approach is acceptable given the controlled usage context.
packages/durably-react/src/client/use-runs.ts (2)
185-196: Client hook correctly requiresapioption with JobDefinition.Unlike the browser hook where options are optional (uses context), the client hook requires
optionswithapiin overload 2 (line 192). This is correct since the client mode needs an explicit API endpoint.
172-183: Overload signatures mirror browser hook correctly.The three overloads follow the same pattern as the browser version, maintaining API consistency across both modes. The conditional type inference for extracting input/output from
TRunis identical..prettierignore (1)
8-8: Appropriate addition to ignore symlinked documentation file.
website/public/llms.txtis symlinked frompackages/durably/docs/llms.mdand shouldn't be formatted by Prettier. Based on learnings, this file is bundled in the npm package for AI agent consumption.CHANGELOG.md (1)
8-28: Comprehensive changelog entry documenting the new feature.The entry clearly documents:
- All three usage patterns for
useRuns- New
TypedRunandTypedClientRuntypes- Practical code example showing union type for multi-job dashboards
packages/durably-react/src/client/index.ts (1)
31-37: Correctly exports newTypedClientRuntype.The new type is properly added to the public API exports, enabling consumers to use it for typed dashboard patterns.
examples/fullstack-react-router/app/routes/_index/dashboard.tsx (1)
26-37: Excellent demonstration of typed multi-job dashboard pattern.The
DashboardRununion type (lines 27-30) clearly shows how to combine multiple job types for a unified dashboard view, and the generic parameter usageuseRuns<DashboardRun>(line 34) demonstrates the intended API pattern.packages/durably-react/src/index.ts (1)
12-13: Correctly exports newTypedRuntype for browser mode.The new type is properly added to the browser-complete entry point, enabling typed dashboard usage in browser mode.
examples/browser-react-router-spa/app/routes/_index/dashboard.tsx (1)
22-26: LGTM! Clear demonstration of multi-job dashboard typing.The
DashboardRununion type effectively demonstrates how to useTypedRunfor multi-job dashboards with type-safe access to different job inputs and outputs.packages/durably-react/src/client.ts (1)
32-37: LGTM! TypedClientRun export correctly added.The new
TypedClientRuntype export is properly placed alongside the relatedClientRunexport, enabling type-safe client-side run handling.examples/browser-vite-react/src/components/dashboard.tsx (1)
20-23: LGTM! Clear demonstration of multi-job dashboard typing.The
DashboardRununion type effectively demonstrates how to composeTypedRunfor multi-job dashboards with type-safe access to job-specific inputs and outputs.examples/browser-vite-react/src/jobs/index.ts (1)
8-18: LGTM! Centralized job type exports pattern.The centralized export pattern using
JobInputandJobOutpututility types provides a clean way to expose typed interfaces for dashboard consumption. This aligns well with the broader PR pattern for typed dashboards.website/public/llms.txt (2)
699-735: LGTM! Clear documentation of typed useRuns patterns.The documentation effectively explains the three usage options (generic, JobDefinition, untyped) with clear examples. The progression from most flexible (generic) to most convenient (JobDefinition) to simplest (untyped) helps users choose the right approach.
1002-1018: TypedClientRun correctly uses theinputfield from ClientRun.The field names are intentionally different by design:
Run(server-side) usespayload, whileClientRun(client API) usesinput. TypedClientRun properly extends ClientRun with generic types and correctly omits and redefines theinputfield. No changes needed.Likely an incorrect or invalid review comment.
examples/browser-react-router-spa/app/jobs/index.ts (2)
8-13: LGTM! Clean consolidation of job exports.The barrel export pattern is well-structured with appropriate type imports and a single consolidated export statement. This provides a clean public API surface for the jobs.
15-23: LGTM! Well-structured type aliases for typed dashboard support.The type aliases provide a clean public API surface that enables type-safe
useRunsusage as described in the PR objectives. Backward compatibility is maintained via theImportCsvOutputalias, and the naming convention is consistent across all jobs.examples/fullstack-react-router/app/jobs/index.ts (1)
8-23: LGTM! Consistent pattern across examples.This file follows the exact same structure as
examples/browser-react-router-spa/app/jobs/index.ts, providing consistency across different example implementations. The barrel export pattern with typed aliases aligns perfectly with the PR's goal of enabling type-safe dashboard access.website/api/durably-react/browser.md (4)
249-276: LGTM! Clear documentation of generic type parameter usage.The documentation clearly demonstrates the generic type parameter pattern for multi-job dashboards. The
DashboardRununion type example and the jobName-based type narrowing pattern are well-explained and practical.
278-307: LGTM! Excellent documentation of the preferred JobDefinition pattern.This section clearly demonstrates the preferred API pattern from issue #14, where passing a
JobDefinitionprovides automatic type inference and jobName filtering. The inline comment highlighting the typed output is particularly helpful for users.
309-341: LGTM! Complete documentation of all usage patterns.The untyped fallback ensures backward compatibility, and the signatures section provides a clear quick reference for all three overload patterns. The documentation comprehensively covers the full API surface introduced in this PR.
343-363: LGTM! Accurate and complete API reference tables.The options and return type tables accurately document the updated API surface. The clarification that
jobNameis "only for untyped usage" is particularly helpful, as it guides users toward the preferred JobDefinition pattern for filtered, typed runs.packages/durably-react/docs/llms.md (2)
264-300: LGTM! Concise and complete useRuns documentation for LLM context.The documentation effectively presents all three usage patterns in a compact format suitable for LLM consumption. The structure parallels the user-facing docs while maintaining brevity, which aligns with the purpose of this file.
567-583: LGTM! Well-defined type exports for browser and client contexts.The
TypedRunandTypedClientRuntype definitions are clearly documented with appropriate generic constraints. The separation between browser hooks (usingpayload) and client hooks (usinginput) correctly reflects the different contexts. The nullableoutputtype accurately represents runtime behavior.website/api/durably-react/client.md (3)
217-244: LGTM! Correct client-side implementation of generic type pattern.The documentation correctly demonstrates the generic type parameter pattern for client hooks, using
TypedClientRuninstead ofTypedRunand including the requiredapiparameter. The pattern is consistent with the browser documentation while properly adapted for the client context.
246-296: LGTM! Complete coverage of all client-side usage patterns.The JobDefinition and untyped usage patterns are well-documented with clear examples. The required
apiparameter is consistently included, and the inline comments about typed/untyped output help users understand the benefits of each approach.
298-332: LGTM! Complete and accurate API reference for client useRuns.The signatures section and API reference tables comprehensively document the client-side useRuns API. The clarification that
jobNameis only for untyped usage guides users toward the type-safe patterns, and the return type documentation accurately reflects theTypedClientRungeneric structure.
…finition detection - Move TypedRun, TypedClientRun, and ClientRun to types.ts for single source of truth - Add isJobDefinition type guard function for robust JobDefinition detection - Re-export types from hooks for backward compatibility - Replace inline 'name' in obj && 'run' in obj checks with type guard 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add tests for TypedRun and TypedClientRun type inference - Add tests for UseRunsResult and UseRunsClientResult generic types - Add tests for union types in multi-job dashboard scenarios - Verify pagination controls and error handling types 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (1)
packages/durably-react/src/client/use-runs.ts (1)
186-197: Potential runtime error whenoptionsArgis undefined.In overload 2,
optionsis required (not optional), so this should be safe. However, the implementation signature allowsoptionsArg?: ...which means TypeScript won't catch if someone bypasses overloads. Line 194 casts without checking, and line 197 destructuresapiwhich would throw ifoptionsis undefined.Consider adding a runtime check or assertion for safety:
🔎 Proposed defensive check
const options = isJob - ? (optionsArg as Omit<UseRunsClientOptions, 'jobName'>) + ? (optionsArg ?? {}) as Omit<UseRunsClientOptions, 'jobName'> : (jobDefinitionOrOptions as UseRunsClientOptions) + + if (!options.api && !('api' in (jobDefinitionOrOptions as object))) { + throw new Error('useRuns: api option is required') + } const { api, status, pageSize = 10 } = optionsAlternatively, this is fine if you trust TypeScript consumers to use the overloads correctly.
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
packages/durably-react/src/client/use-runs.tspackages/durably-react/src/hooks/use-runs.tspackages/durably-react/src/types.ts
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{js,ts,mjs,mts}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{js,ts,mjs,mts}: This library is ESM-only. CommonJS is not supported. Always use top-levelawaitfor async initialization (e.g.,await durably.migrate()). Do not wrap in async IIFE or Promise chains.
Jobs must be defined viadefineJob()and registered withdurably.register(), receiving a step context and payload
Steps are created viastep.run(), with each step's success state and return value persisted automatically
Files:
packages/durably-react/src/types.tspackages/durably-react/src/hooks/use-runs.tspackages/durably-react/src/client/use-runs.ts
🧠 Learnings (2)
📓 Common learnings
Learnt from: CR
Repo: coji/durably PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-03T06:23:01.913Z
Learning: Applies to **/*.{js,ts,mjs,mts} : Jobs must be defined via `defineJob()` and registered with `durably.register()`, receiving a step context and payload
📚 Learning: 2026-01-03T06:23:01.913Z
Learnt from: CR
Repo: coji/durably PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-03T06:23:01.913Z
Learning: Applies to **/*.{js,ts,mjs,mts} : Jobs must be defined via `defineJob()` and registered with `durably.register()`, receiving a step context and payload
Applied to files:
packages/durably-react/src/types.tspackages/durably-react/src/hooks/use-runs.tspackages/durably-react/src/client/use-runs.ts
🧬 Code graph analysis (2)
packages/durably-react/src/hooks/use-runs.ts (1)
packages/durably-react/src/types.ts (2)
TypedRun(113-121)isJobDefinition(160-172)
packages/durably-react/src/client/use-runs.ts (1)
packages/durably-react/src/types.ts (2)
TypedClientRun(146-154)isJobDefinition(160-172)
🔇 Additional comments (12)
packages/durably-react/src/hooks/use-runs.ts (4)
1-7: LGTM!Clean imports and convenient re-export of
TypedRunfor consumers who import from this hook module.
100-124: LGTM!The three overloads are well-designed:
- Overload 1 handles union types for multi-job dashboards with proper conditional type inference.
- Overload 2 provides automatic
jobNamefiltering and type inference fromJobDefinition.- Overload 3 maintains backward compatibility.
139-148: LGTM!The implementation correctly uses the
isJobDefinitiontype guard to distinguish between call patterns, derivingjobNameandoptionsappropriately for each case.
159-175: LGTM!The
refreshcallback properly passesjobNameandstatusto the API, and the type cast on line 171 is a reasonable approach given the runtime data comes from an external source. The dependency array is complete.packages/durably-react/src/types.ts (4)
113-121: LGTM!The
TypedRuntype correctly extends the baseRuntype by omittingpayloadandoutput, then adding them back with generic types. Theoutput: TOutput | nullcorrectly handles the case where output hasn't been produced yet.
127-140: LGTM!The
ClientRuninterface provides a clean client-facing shape with string dates and appropriate nullable fields. This aligns well with typical JSON serialization from server responses.
146-154: LGTM!
TypedClientRunmirrorsTypedRunbut for client mode, usingOmit<ClientRun, 'input' | 'output'>to replace the untyped fields with generics.
160-172: LGTM!The
isJobDefinitiontype guard is robust:
- Checks for object type and non-null
- Verifies presence of
nameandrunproperties (distinguishing from options objects which usejobName)- Confirms
runis a functionThis reliably distinguishes
JobDefinitionfromUseRunsOptions/UseRunsClientOptionsat runtime.packages/durably-react/src/client/use-runs.ts (4)
1-11: LGTM!Clean imports and appropriate re-exports. The client hook correctly imports
TypedClientRunandisJobDefinitionfrom the shared types module.
149-173: LGTM!The overloads mirror the browser hook pattern correctly, using
TypedClientRuninstead ofTypedRun. The client-specificUseRunsClientOptionsrequiresapias mandatory, which is appropriate for HTTP-based fetching.
254-323: LGTM!The SSE subscription logic is well-implemented:
- Only subscribes on page 0 for efficiency
- Properly cleans up EventSource when navigating away
- Handles various event types appropriately (refresh on lifecycle events, in-place updates for progress/step completion)
- Correct cleanup on unmount
339-349: LGTM!The return object correctly includes all pagination controls and the
refreshfunction, matching theUseRunsClientResultinterface.
- Fix TOutput default type in UseRunsResult and UseRunsClientResult to match TypedRun/TypedClientRun (Record<string, unknown> | undefined) - Update example dashboards to use DashboardRun type for selectedRun instead of Run for better type consistency 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add generic type parameter to getRun<T>() and getRuns<T>() for type-safe run retrieval - Update examples to use typed getRun<DashboardRun>() instead of type cast - Update CHANGELOG with new feature 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add typed getRun<T>/getRuns<T> examples to llms.md - Update website API docs with generic type parameter documentation 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/durably/docs/llms.md (1)
1-173: Fix Prettier formatting violations before merge.The file has formatting violations in the type definitions (lines 152 and 171). Type
MyRundeclarations exceed the 80-character line width and must be reformatted. Runprettier --write packages/durably/docs/llms.mdfrom the packages/durably directory to fix.Formatting changes needed
-type MyRun = Run & { payload: { userId: string }; output: { count: number } | null } +type MyRun = Run & { + payload: { userId: string } + output: { count: number } | null +}
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
packages/durably/docs/llms.mdpackages/durably/package.jsonwebsite/api/create-durably.md
✅ Files skipped from review due to trivial changes (1)
- packages/durably/package.json
🧰 Additional context used
📓 Path-based instructions (1)
packages/durably/docs/llms.md
📄 CodeRabbit inference engine (CLAUDE.md)
Keep
packages/durably/docs/llms.mdin sync with API changes as it is bundled in the npm package and symlinked towebsite/public/llms.txtfor web access
Files:
packages/durably/docs/llms.md
🧠 Learnings (2)
📓 Common learnings
Learnt from: CR
Repo: coji/durably PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-03T06:23:01.913Z
Learning: Applies to packages/durably/docs/llms.md : Keep `packages/durably/docs/llms.md` in sync with API changes as it is bundled in the npm package and symlinked to `website/public/llms.txt` for web access
📚 Learning: 2026-01-03T06:23:01.913Z
Learnt from: CR
Repo: coji/durably PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-03T06:23:01.913Z
Learning: Applies to **/*.{js,ts,mjs,mts} : Jobs must be defined via `defineJob()` and registered with `durably.register()`, receiving a step context and payload
Applied to files:
website/api/create-durably.md
🪛 GitHub Actions: CI
packages/durably/docs/llms.md
[warning] 1-1: Prettier formatting issues found. Run 'prettier --write' to fix formatting in this file.
🔇 Additional comments (3)
packages/durably/docs/llms.md (1)
148-173: Excellent documentation of generic type support for getRun/getRuns.The added examples clearly demonstrate both untyped and typed usage patterns, with concrete type alias examples (MyRun) that help users understand how to define custom run types. The documentation is well-aligned with the PR's goal of enabling type-safe run access. Once formatting is fixed, this will integrate seamlessly with the bundled npm package documentation.
website/api/create-durably.md (2)
125-158: API documentation accurately reflects generic type support.The signature updates and examples correctly document the new generic type parameter support for
getRun<T extends Run = Run>andgetRuns<T extends Run = Run>. The examples clearly show both untyped (returnsRun) and typed (returns customT) usage patterns, making it easy for developers to adopt the new capability.
144-149: Field order in documentation differs from source code.The
RunFilterinterface definition is functionally correct, but the field order in the documentation (jobName, status, limit, offset) does not match the source code order (status, jobName, limit, offset) inpackages/durably/src/storage.tsandpackages/durably/src/job.ts. Additionally, the source code includes JSDoc comments for thelimitandoffsetfields that are not reflected in the documentation. Consider reordering the fields to match the source and adding the field descriptions for consistency.
🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Summary
@coji/durably
getRun<T>()andgetRuns<T>()for type-safe run retrieval@coji/durably-react
useRunshook for type-safe dashboard accessuseRuns<TRun>(options)- Generic type for multi-job dashboardsuseRuns(jobDefinition, options?)- JobDefinition for single job with auto-filteruseRuns(options?)- Untyped for simple casesTypedRun<TInput, TOutput>(browser) andTypedClientRun<TInput, TOutput>(client)isJobDefinitiontype guard functiontypes.tsExample Usage
Test plan
pnpm validatepassesCloses #14
🤖 Generated with Claude Code