fix(provider): handle plain-text responses in postUint8Array#183
fix(provider): handle plain-text responses in postUint8Array#183solidsnakedev wants to merge 2 commits intomainfrom
Conversation
There was a problem hiding this comment.
Pull request overview
Fixes postUint8Array to support providers whose POST /tx/submit returns a raw plain-text hash (unquoted), avoiding JSON.parse failures and restoring compatibility with such backends.
Changes:
- Update
postUint8Arrayto attempt JSON decoding first and fall back to plain-text decoding. - Add a changeset to publish the fix as a patch release.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| packages/evolution/src/sdk/provider/internal/HttpUtils.ts | Adds JSON→text fallback when decoding postUint8Array responses |
| .changeset/fix-post-uint8array-plain-text.md | Documents patch release for the new fallback behavior |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| // Try JSON first, fall back to plain text for endpoints that return unquoted strings (e.g. Dolos /tx/submit) | ||
| const decoded = yield* filteredResponse.json.pipe(Effect.orElse(() => filteredResponse.text)) | ||
| return yield* Schema.decodeUnknown(schema)(decoded) |
There was a problem hiding this comment.
filteredResponse.json followed by a fallback to filteredResponse.text may require consuming the response body twice. In many Fetch/HTTP response implementations, the body can only be read once; if JSON parsing fails after reading the body, text cannot be read afterwards (fallback will fail too). A safer approach is to read the body once as text, then attempt to parse it as JSON (falling back to the raw text when parsing fails) before passing it to Schema.decodeUnknown.
| // Try JSON first, fall back to plain text for endpoints that return unquoted strings (e.g. Dolos /tx/submit) | |
| const decoded = yield* filteredResponse.json.pipe(Effect.orElse(() => filteredResponse.text)) | |
| return yield* Schema.decodeUnknown(schema)(decoded) | |
| // Read body once as text, then try to parse as JSON; fall back to plain text on parse failure | |
| const bodyText = yield* filteredResponse.text | |
| let decodedBody: unknown | |
| try { | |
| decodedBody = JSON.parse(bodyText) | |
| } catch { | |
| decodedBody = bodyText | |
| } | |
| return yield* Schema.decodeUnknown(schema)(decodedBody) |
| const json = yield* filteredResponse.json | ||
| return yield* Schema.decodeUnknown(schema)(json) | ||
| // Try JSON first, fall back to plain text for endpoints that return unquoted strings (e.g. Dolos /tx/submit) | ||
| const decoded = yield* filteredResponse.json.pipe(Effect.orElse(() => filteredResponse.text)) |
There was a problem hiding this comment.
Effect.orElse will catch any failure from filteredResponse.json, not just JSON parse errors. That can unintentionally hide unexpected issues (e.g., decoding/transport errors) by silently switching to text. Consider narrowing the fallback to only the JSON-parse failure case (e.g., via a predicate-based catch) so other errors still fail fast.
| const decoded = yield* filteredResponse.json.pipe(Effect.orElse(() => filteredResponse.text)) | |
| const decoded = yield* filteredResponse.json.pipe( | |
| Effect.catchIf( | |
| (error) => error instanceof SyntaxError, | |
| () => filteredResponse.text | |
| ) | |
| ) |
postUint8Arrayunconditionally callsfilteredResponse.json, which runsJSON.parseinternally. Endpoints that return plain-text (e.g. an unquoted hex tx hash) fail with aSyntaxErrorbecause the body is not valid JSON. This makes the Blockfrost provider incompatible with backends that return raw strings fromPOST /tx/submit.postUint8Arraynow triesfilteredResponse.jsonfirst and falls back tofilteredResponse.textviaEffect.orElse. Both Blockfrost-style JSON responses ("hash") and plain-text responses (hash) now decode correctly through the existing schema pipeline.Closes #181