feat(repo): Added Python and TypeScript packages for RFC 9457 Problem Details#1
Conversation
frankkilcommins
commented
Apr 10, 2026
- Pydantic models + FastAPI exception handlers (Python)
- TypeScript interfaces + error utilities + type guards
- Schema conformance tests to prevent drift from schemas
- test coverage (30 Python tests, 47 TypeScript tests)
There was a problem hiding this comment.
Pull request overview
This PR introduces reusable RFC 9457 “Problem Details” libraries for both Python (Pydantic + FastAPI helpers) and TypeScript (types + utilities), alongside schema conformance tests and CI/release automation to keep the implementations aligned with the repo’s OpenAPI schemas.
Changes:
- Add a new Python package (
jentic-problem-details) with Pydantic models and FastAPI exception helpers, plus tests. - Add a new TypeScript package (
@jentic/problem-details) with interfaces, type guards, error utilities, plus Vitest-based tests. - Add CI and release workflows to build, test, and publish the packages.
Reviewed changes
Copilot reviewed 26 out of 28 changed files in this pull request and generated 9 comments.
Show a summary per file
| File | Description |
|---|---|
typescript/vitest.config.ts |
Adds Vitest config including coverage settings. |
typescript/tsconfig.json |
Adds TypeScript compiler configuration for the package build. |
typescript/tests/types.test.ts |
Adds TypeScript tests for types and type guards. |
typescript/tests/schema-conformance.test.ts |
Adds schema conformance tests against YAML schemas. |
typescript/tests/errors.test.ts |
Adds tests for ProblemDetailError and factory helpers. |
typescript/src/types.ts |
Defines ProblemDetail/ErrorItem and runtime type guards. |
typescript/src/index.ts |
Provides package public exports and documentation header. |
typescript/src/errors.ts |
Implements ProblemDetailError and factory helpers for common HTTP errors. |
typescript/README.md |
Adds TypeScript package documentation and usage examples. |
typescript/package.json |
Defines TypeScript package metadata, scripts, and dev dependencies. |
typescript/package-lock.json |
Locks dependency tree for the TypeScript package. |
typescript/.gitignore |
Adds TypeScript-package specific ignores. |
README.md |
Updates repository-level README with Python/TypeScript usage and structure. |
python/tests/test_schema_conformance.py |
Adds Python schema conformance tests (Pydantic vs OpenAPI schema). |
python/tests/test_responses.py |
Adds tests for FastAPI response helpers/exceptions. |
python/tests/test_models.py |
Adds tests for Pydantic model behavior and validation constraints. |
python/src/jentic/problem_details/responses.py |
Implements FastAPI HTTPException wrappers and handler for problem+json. |
python/src/jentic/problem_details/models.py |
Implements Pydantic ProblemDetail and ErrorItem models. |
python/src/jentic/problem_details/__init__.py |
Exposes public Python package API and sets __version__. |
python/src/jentic/__init__.py |
Adds jentic namespace package marker. |
python/README.md |
Adds Python package documentation and usage examples. |
python/pyproject.toml |
Adds Python package metadata, dependencies, and pytest config. |
python/.gitignore |
Adds Python-package specific ignores. |
NOTICE |
Adds attribution/notice content for the repository. |
LICENSE |
Adds Apache 2.0 license text at repository root. |
.gitignore |
Adds repository-wide ignore rules (Python + Node + tooling). |
.github/workflows/release.yaml |
Adds manual release workflow for tagging and publishing to PyPI/NPM. |
.github/workflows/ci.yaml |
Adds CI workflow to run Python + TypeScript build/tests. |
Files not reviewed (1)
- typescript/package-lock.json: Language not supported
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
char0n
left a comment
There was a problem hiding this comment.
Consolidated Review (manual + Copilot)
Good work overall — the architecture is clean, schema conformance tests are a strong design choice, and the ProblemDetailError.fromResponse() ergonomic is nice for frontend consumers. There are several issues to resolve before merge, organized by severity.
🔴 Critical (must fix)
C1. Python package crashes on import without FastAPI installed
python/src/jentic/problem_details/__init__.py unconditionally imports from responses.py, which does from fastapi import HTTPException, Request at the top level. But FastAPI is an optional dependency in pyproject.toml. Any user who installs pip install jentic-problem-details (without [fastapi]) will get ImportError on from jentic.problem_details import ProblemDetail.
Fix: Wrap the responses imports in try/except ImportError and conditionally extend __all__, as Copilot suggested.
(Flagged by both reviewers)
C2. License mismatch: Python says MIT, repo is Apache 2.0
python/pyproject.toml→license = {text = "MIT"}+ MIT classifierpython/README.md→ "MIT"- Root
LICENSE→ Apache 2.0 openapi-domain.yaml→ Apache 2.0typescript/package.json→ Apache-2.0
This is a legal inconsistency that must be resolved before publishing to PyPI.
(Flagged by both reviewers)
🟠 Major (should fix)
M1. __dirname used in ESM context (TypeScript schema conformance tests)
typescript/tests/schema-conformance.test.ts uses __dirname to locate schema files, but the package is ESM ("type": "module"). __dirname is not defined in ESM. This will throw at runtime.
Fix: Use import.meta.url with fileURLToPath/dirname from node:url/node:path.
(Flagged by Copilot)
M2. ProblemDetailError.fromResponse() doesn't handle malformed JSON
typescript/src/errors.ts — When the content-type is application/problem+json, response.json() is called without a try/catch. If the body is empty or malformed (common with proxies/CDNs), this throws an unhandled error instead of falling back to the text-based path.
Fix: Wrap the JSON parse in try/catch, fall back to text/status-based ProblemDetail.
(Flagged by Copilot)
M3. ValidationError title deviates from RFC 9457 convention
Both Python (responses.py → title="Validation Error") and TypeScript (errors.ts → title: 'Validation Error') use a non-standard title for 422. The OpenAPI spec in openapi-domain.yaml:214 uses title: Unprocessable Content, which is the standard HTTP phrase per RFC 9110. Per RFC 9457, when type is about:blank, title SHOULD be the standard phrase.
(Flagged by manual review)
M4. isProblemDetail and isErrorItem type guards are indistinguishable
typescript/src/types.ts — Both functions check for the exact same condition (typeof obj === 'object' && obj !== null && 'detail' in obj && typeof detail === 'string'). Since both types share detail as their only required field, isProblemDetail returns true for any ErrorItem and vice versa. Consider checking for a distinguishing property (e.g., status or title for ProblemDetail, pointer/parameter/header for ErrorItem), or document this limitation.
(Flagged by manual review)
M5. Unused HttpUrl import in Python models
python/src/jentic/problem_details/models.py:4 imports HttpUrl from Pydantic but never uses it.
(Flagged by both reviewers)
M6. ajv imported but unused in TypeScript schema conformance tests
typescript/tests/schema-conformance.test.ts imports and instantiates Ajv but never uses it. Either use it for actual JSON Schema validation or remove the import and the ajv devDependency.
(Flagged by both reviewers)
M7. Hand-rolled YAML parser is fragile
typescript/tests/schema-conformance.test.ts — The parseYamlSchema function is a regex-based YAML parser that handles a narrow subset. It will silently produce wrong results if the schema YAML format changes. Consider adding js-yaml as a devDependency (as the Python tests do with pyyaml).
(Flagged by manual review)
🟡 Minor (nice to fix)
m1. __version__ in __init__.py will drift after first release
.github/workflows/release.yaml updates pyproject.toml version but not __init__.py's __version__ = "1.0.0". Consider sourcing __version__ from importlib.metadata at runtime.
(Flagged by both reviewers)
m2. README FastAPI example missing Request import
The root README.md FastAPI example uses Request as a type annotation but only imports FastAPI. Should be from fastapi import FastAPI, Request.
(Flagged by Copilot)
m3. ValidationError name shadows Pydantic's ValidationError
Users who import both from pydantic import ValidationError and from jentic.problem_details import ValidationError will get a conflict. Consider renaming to UnprocessableContent to match the HTTP phrase, which also fixes M3.
(Flagged by manual review)
m4. Release workflow sed version update is fragile
sed -i 's/^version = .*/version = "..."/' pyproject.toml could match unintended lines. Consider a more targeted tool.
(Flagged by manual review)
m5. Release workflow pushes directly to main
git push origin main --tags bypasses branch protection. Consider whether this is intentional.
(Flagged by manual review)
m6. Duplicate .gitignore patterns
python/.gitignore duplicates most Python patterns already in the root .gitignore.
(Flagged by manual review)
m7. Missing py.typed marker
Add py.typed to python/src/jentic/problem_details/ so type checkers recognize the package as PEP 561 typed.
(Flagged by manual review)
The responses module imports FastAPI at the top level, but FastAPI is declared as an optional dependency in pyproject.toml. Wrap the import in try/except ImportError so the package can be imported without FastAPI installed. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Python pyproject.toml and README declared MIT license while the repo root LICENSE, openapi-domain.yaml, and TypeScript package all use Apache 2.0. Align Python metadata and classifiers to Apache-2.0. Also add verbose license section to both Python and TypeScript READMEs. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… ajv Replace __dirname (not available in ESM) with import.meta.url + fileURLToPath/dirname. Remove unused Ajv import and instantiation. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…onse Wrap response.json() in try/catch so that empty or malformed bodies with application/problem+json content-type fall back to the text/status-based ProblemDetail instead of throwing. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Per RFC 9457, when type is "about:blank" the title SHOULD be the standard HTTP status phrase. RFC 9110 defines 422 as "Unprocessable Content". Align both Python and TypeScript packages with the OpenAPI spec in this repo which already uses the correct phrase.
…uards Both type guards previously checked the same condition (has string detail field), making them indistinguishable at runtime. isProblemDetail now requires status or title. isErrorItem checks for pointer/parameter/header or absence of ProblemDetail-specific fields.
The regex-based YAML parser was fragile and could silently produce wrong results if the schema format changed. Replace with the yaml package for reliable parsing. Also remove unused ajv devDependency.
Use importlib.metadata instead of hardcoding version so it stays in sync with pyproject.toml across releases.
All patterns are already covered by the root .gitignore.
Allows type checkers (mypy, pyright) to recognize this package as providing inline type information.
|
🎉 This PR is included in version 1.0.0 🎉 The release is available on GitHub release Your semantic-release bot 📦🚀 |