Skip to content

feat: enforce JSON-compatible step outputs at construction time#615

Merged
jumski merged 1 commit intomainfrom
03-19-enforce_json-safe_step_outputs_without_narrowing_inference
Mar 20, 2026
Merged

feat: enforce JSON-compatible step outputs at construction time#615
jumski merged 1 commit intomainfrom
03-19-enforce_json-safe_step_outputs_without_narrowing_inference

Conversation

@jumski
Copy link
Contributor

@jumski jumski commented Mar 19, 2026

This pull request enforces JSON-compatible step outputs at .step() construction time by adding TypeScript constraints to the step handler functions.

The changes introduce a Json type constraint on step handler return values, preventing non-serializable types like undefined, Symbol, or functions from being returned. A new WidenJson<T> utility type is added to properly widen literal types (e.g., 123 becomes number) while maintaining JSON compatibility.

All six .step() method overloads are updated to use a constrained THandler type parameter instead of a generic TOutput, ensuring type safety at compile time. Comprehensive type tests verify that valid JSON types are accepted while invalid types like undefined and functions are properly rejected with TypeScript errors.

@changeset-bot
Copy link

changeset-bot bot commented Mar 19, 2026

🦋 Changeset detected

Latest commit: 97e8bc4

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 5 packages
Name Type
@pgflow/dsl Patch
@pgflow/edge-worker Patch
@pgflow/client Patch
@pgflow/core Patch
pgflow Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

Copy link
Contributor Author

jumski commented Mar 19, 2026

This stack of pull requests is managed by Graphite. Learn more about stacking.

@nx-cloud
Copy link

nx-cloud bot commented Mar 19, 2026

View your CI Pipeline Execution ↗ for commit 97e8bc4

Command Status Duration Result
nx run edge-worker:test:integration ✅ Succeeded 4m 17s View ↗
nx run core:pgtap ✅ Succeeded 1m 52s View ↗
nx run client:e2e ✅ Succeeded 1m 19s View ↗
nx run cli:e2e ✅ Succeeded 3s View ↗
nx affected -t verify-exports --base=origin/mai... ✅ Succeeded 4s View ↗
nx affected -t build --configuration=production... ✅ Succeeded 4s View ↗
nx affected -t lint typecheck test --parallel -... ✅ Succeeded 53s View ↗
nx run edge-worker:e2e ✅ Succeeded 43s View ↗

☁️ Nx Cloud last updated this comment at 2026-03-20 15:46:12 UTC

Comment on lines +51 to +52
: T extends readonly (infer U)[]
? WidenJson<U>[]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The WidenJson type loses the readonly modifier when widening arrays. If a handler returns a readonly array like readonly [1, 2, 3], this will be widened to a mutable number[], breaking type safety.

Fix: Preserve the readonly modifier:

: T extends readonly (infer U)[]
? readonly WidenJson<U>[]
: T extends object

This ensures readonly arrays remain readonly after widening.

Suggested change
: T extends readonly (infer U)[]
? WidenJson<U>[]
: T extends readonly (infer U)[]
? readonly WidenJson<U>[]

Spotted by Graphite

Fix in Graphite


Is this helpful? React 👍 or 👎 to let us know.

@jumski jumski force-pushed the 03-19-enforce_json-safe_step_outputs_without_narrowing_inference branch from 211672e to 86a8ccf Compare March 19, 2026 20:13
@jumski jumski force-pushed the 03-19-enforce_json-safe_step_outputs_without_narrowing_inference branch 2 times, most recently from 01544a2 to cffe9d0 Compare March 20, 2026 11:37
@jumski jumski force-pushed the 03-19-enforce_json-safe_step_outputs_without_narrowing_inference branch from cffe9d0 to aaf742e Compare March 20, 2026 13:57
Step handler return types were validated through EnforceJsonOutput but
stored as raw AwaitedReturn<THandler> in StepMeta. This mismatch caused
CompatibleFlow and EdgeWorker.start overloads to collapse to never when
step handlers returned interface types (e.g. DTOs), because the stored
metadata didn't match the JSON-normalized shape used by downstream type
inference.

Normalize all three flow method metadata paths to store JsonCompatible
variants of the handler output, matching the existing validation layer:

- .step(): store JsonCompatible<AwaitedReturn<THandler>> instead of raw
  AwaitedReturn<THandler> across all six overloads
- .array(): store JsonCompatible<Awaited<TOutput>> and enforce JSON
  compatibility at the handler return boundary
- .map(): store JsonCompatible<AwaitedReturn<THandler>>[] with EnforceJsonOutput
  handler validation

Also fix JsonCompatible to preserve tuple structure (previously widened
tuples to generic arrays), and add regression type tests for interface
DTO step outputs with dependent steps and EdgeWorker.start acceptance.
@jumski jumski force-pushed the 03-19-enforce_json-safe_step_outputs_without_narrowing_inference branch from aaf742e to 97e8bc4 Compare March 20, 2026 15:38
@github-actions
Copy link
Contributor

🔍 Preview Deployment: Website

Deployment successful!

🔗 Preview URL: https://pr-615.pgflow.pages.dev

📝 Details:

  • Branch: 03-19-enforce_json-safe_step_outputs_without_narrowing_inference
  • Commit: b05f22f77de3c3597eb107ed4b67c9dcb735bfd6
  • View Logs

_Last updated: _

Copy link
Contributor Author

jumski commented Mar 20, 2026

Merge activity

  • Mar 20, 3:48 PM UTC: A user started a stack merge that includes this pull request via Graphite.
  • Mar 20, 3:48 PM UTC: @jumski merged this pull request with Graphite.

@jumski jumski merged commit f41d0f1 into main Mar 20, 2026
14 checks passed
@github-actions
Copy link
Contributor

🚀 Production Deployment: Website

Successfully deployed to production!

🔗 Production URL: https://pgflow.dev

📝 Details:

  • Commit: f41d0f1db119d6e30e4132e758a7abdac5fea15d
  • View Logs

Deployed at: 2026-03-20T16:48:45+01:00

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant