Skip to content

Commit 5bf2016

Browse files
committed
chore: add sync-api skill for reconciling the SDK with the Mollie API
A Claude Code skill (.claude/skills/sync-api) that checks — and optionally fixes — the SDK's types, JSDoc, enums, endpoint coverage and helper wiring against Mollie's master OpenAPI spec. It is deliberately script-driven so the model spends its budget on judgment, not on parsing JSON: a bundled toolchain fetches/caches the master spec, emits compact per-field digests (descriptions preserved verbatim and self-checked for loss), reads the SDK's structure via the TypeScript AST, and produces a deterministic diff. The model then handles only what genuinely needs judgment — Pick-vs-inline, nullable-vs-optional, breaking changes, description coverage, and link/embed helper coverage. Includes the project .claude docs the skill references (CLAUDE.md, ARCHITECTURE.md), adds the `yaml` devDependency used by the spec extractor, and ignores the skill's .tmp cache and local worktrees.
1 parent 47fdb1b commit 5bf2016

12 files changed

Lines changed: 1117 additions & 5 deletions

File tree

.claude/ARCHITECTURE.md

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# Resource Architecture
2+
3+
Each API resource (payments, refunds, customers, etc.) is composed of up to four parts:
4+
5+
```
6+
src/
7+
├── data/<resource>/
8+
│ ├── data.ts # Response type + enums
9+
│ ├── <Resource>.ts # Sealed public type + transform()
10+
│ └── <Resource>Helper.ts # Instance methods (optional)
11+
12+
└── binders/<resource>/
13+
├── <Resource>Binder.ts # Endpoint methods (create, get, page, ...)
14+
└── parameters.ts # Request types per operation
15+
```
16+
17+
## `data.ts` — Response shape
18+
19+
- Defines `*Data` interface extending `Model<'resource-name'>` — the raw API response
20+
- Contains enums (statuses, embed/include options) and `*Links` interfaces
21+
- Source of truth for field names and types; `parameters.ts` reuses fields from here via `Pick`
22+
23+
## `<Resource>.ts` — Sealed type + transform
24+
25+
- Defines `type Resource = Seal<ResourceData, ResourceHelper>`
26+
- `Seal<M, H>` = `Readonly<M> & H` — frozen data properties merged with helper methods
27+
- `transform()` creates the sealed object and recursively transforms embedded sub-resources
28+
- This is what SDK consumers interact with
29+
30+
## `<Resource>Helper.ts` — Instance methods
31+
32+
- Extends `Helper<Data, Resource>` base class (provides `refresh()`, toString, inspect)
33+
- Adds convenience methods for navigating related resources (e.g. `getRefunds()`, `getPayment()`)
34+
- Not every resource needs a custom helper — simple resources use the base `Helper` directly
35+
- Wired to the sealed type via `Object.assign(Object.create(new Helper(...)), data)`
36+
37+
## `<Resource>Binder.ts` — Endpoint methods
38+
39+
- Extends `Binder<ResourceData, Resource>`
40+
- One public method per API operation: `create`, `get`, `page`, `iterate`, `update`, `cancel`/`delete`
41+
- Delegates to `TransformingNetworkClient`, which auto-applies `transform()` to responses
42+
43+
## `parameters.ts` — Request types
44+
45+
- One type per operation (`CreateParameters`, `GetParameters`, `PageParameters`, etc.)
46+
- Composed from the Data interface via `Pick` (preserves optionality) and `PickOptional` (forces optional)
47+
- Request-only fields (not in response) are defined inline here
48+
- Shared mixins (from `src/types/parameters.ts`): `IdempotencyParameter`, `PaginationParameters`, `SortParameter`, `ThrottlingParameter`, `TestModeParameter`
49+
- Nested resources include context params (e.g. `paymentId: string`)
50+
51+
## Wiring
52+
53+
New resources must be registered in `createMollieClient.ts`:
54+
1. Import and register the transformer: `.add('resource-name', transformResource)`
55+
2. Instantiate and expose the binder: `resourceName: new ResourceBinder(transformingNetworkClient)`
56+
57+
Public types are exported from `src/types.ts` (sealed types, parameter type aliases, enums).
58+
`*Data` interfaces and helpers are internal — not exported to consumers.

.claude/CLAUDE.md

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# Mollie API Node.js SDK
2+
3+
Official Node.js client (`@mollie/api-client`) for the Mollie Payment API v2.
4+
5+
Resource layer structure (data, binders, helpers, parameters) is documented in `.claude/ARCHITECTURE.md`.
6+
7+
## Commands
8+
9+
```bash
10+
yarn build # Rollup bundle (CJS + ESM) + TypeScript declarations
11+
yarn test # Jest tests
12+
yarn test:watch # Jest watch mode
13+
yarn test:cov # Coverage report
14+
yarn lint # ESLint fix + Prettier
15+
```
16+
17+
## Type System — Request vs Response
18+
19+
IMPORTANT: `data.ts` and `parameters.ts` are **not** two views of the same type. They are separate concerns:
20+
21+
- `data/<resource>/data.ts`**response** shape (what the API returns). This is the source of truth for field names and types.
22+
- `binders/<resource>/parameters.ts`**request** shape (what the consumer sends). Built by `Pick`/`PickOptional` from the Data interface, plus request-only fields that don't appear in responses (e.g. `applePayPaymentToken`, `cardToken`).
23+
24+
`Pick<Data, ...>` preserves optionality from the response type. `PickOptional<Data, ...>` forces fields optional ("exists on response, but optional to send").
25+
26+
Do NOT add request-only fields to `data.ts` — they belong in `parameters.ts`.
27+
28+
## Helpers and underscore-prefixed fields
29+
30+
Consumers should never access underscore-prefixed properties directly (the `_` prefix denotes private/internal, e.g. `_links`, `_embedded`). Helper methods (e.g. `payment.getRefunds()`) abstract these away — they transparently return embedded data if available, or fetch via the API link if not. This is why helpers exist: a single entry point regardless of whether data was embedded or needs fetching.
31+
32+
## Conventions
33+
34+
- Conventional commits for all commit messages.
35+
- `prefer-rest-params` is intentionally off — the `arguments` object is used deliberately in the renege (promise↔callback) pattern. Don't refactor these to rest params.
36+
- New transformers must be registered in `createMollieClient.ts` alongside the binder wiring.
37+
- Enums and public types are exported from `src/createMollieClient.ts` and `src/types.ts`.
38+
39+
## Release Process
40+
41+
Publishing is automated via `.github/workflows/publish.yml` (npm Trusted Publishing / OIDC) — it runs when a GitHub Release is published. There is **no manual `npm publish`** and no npm token.
42+
43+
1. Update `CHANGELOG.md`
44+
2. `npm version <major|minor|patch>` (bumps `package.json`, commits, creates the `vX.Y.Z` tag)
45+
3. `git push --follow-tags`
46+
4. Publish a GitHub Release for the tag → the workflow verifies the tag matches `package.json`, builds, runs unit tests, and publishes with provenance (prereleases like `-rc.N` go to their own dist-tag, not `latest`).
47+
48+
See `CONTRIBUTING.md` for the maintainer-facing version of this.
49+
50+
## Gotchas
51+
52+
- **Mollie API docs are unreliable for optionality/nullability.** Fields marked `required` may work fine without sending them (e.g. `redirectUrl`). Fields typed as `object` (not nullable) may actually be optional (e.g. `billingAddress`). Fields typed as `string | null` are likely optional too (absent from responses despite not being marked optional). Never auto-apply optionality from the docs — flag discrepancies for manual review instead.
53+
- **Nullable vs optional convention**: `Nullable<T>` (`T | null`) = always present, sometimes null. `T?` = sometimes absent. `?: Nullable<T>` = both. Default to `?` (optional) when uncertain, upgrade to `Nullable<T>` only when confirmed via actual API behavior.
54+
- **Mollie docs .md trick** — append `.md` to any reference page URL (e.g. `https://docs.mollie.com/reference/create-payment.md`) to get plain-text markdown with fully resolved OpenAPI JSON. No browser automation needed. The GitHub repo `mollie/openapi` (`specs.yaml`) has the full spec but uses `$ref`s.
55+
- **node-fetch v2** (CommonJS) is the HTTP client, not native fetch.
56+
- **Node version: README says 14+, `engines` says `>=8` — this mismatch is intentional, do not "fix" it.** The SDK's *code* is expected to run on Node 8+, hence `engines.node: ">=8"`. But the *test environment* won't run on anything older than Node 14, so 14 is the lowest version we can actually verify. The policy: we guarantee it works on 14, and it *probably* works on older versions down to 8 — we just can't prove it. README states 14 (the supported/tested floor); `engines` states 8 (the believed-actual floor). Aligning the two numbers would either drop unverified-but-likely support or claim guarantees we can't back.

0 commit comments

Comments
 (0)