Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
177 changes: 177 additions & 0 deletions .claude/skills/update-cloudhub-node-client/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
---
name: update-cloudhub-node-client
description: >
Re-sync this Node client (cloudhubNodeClient, npm package cloudhub-client) with the CloudHub .NET
API: capture the latest OpenAPI spec from CloudHub, diff it, HAND-APPLY the changes to the
hand-written TypeScript sources (cloudhub-client.ts, rest-client.ts, types.ts), verify
(tsc --noEmit + npm test), and bump the package.json version. Use whenever the client must mirror
new/changed CloudHub endpoints or models, after CloudHub is updated, or when asked to
"update / regenerate / sync the cloudhub node client".
---

# Update the CloudHub Node client

This client is **hand-written TypeScript** — there is **no code generator** (unlike the sibling
`cloudhub-java-client`, which is OpenAPI-Generator output; it is the same *kind* of client as the
`cloudHubPhpClient`). "Syncing" means: capture the CloudHub OpenAPI spec, read its diff, and **edit
the `.ts` sources by hand** to match. The spec is the source of truth and the change detector; you
supply all the code.

## How the pieces fit (read first)

- `openapi/cloudhub.json` is the committed **source of truth**. `git diff` on it = "what changed upstream".
- All library code is **hand-maintained** and lives at the repo root:
- `types.ts` — interfaces + string enums (the data contracts).
- `rest-client.ts` — `RestClient`, an **axios** wrapper. Its axios instance is created with the
`x-api-key` header, so **every** call is authenticated. (An optional 3rd constructor arg injects
an axios adapter; used only by tests — null in production.)
- `cloudhub-client.ts` — `CloudHubClient`, the public API (one method per endpoint).
- `index.ts` — the barrel that re-exports all three.
There are no generated files and nothing to protect from a generator; instead, **preserve backward
compatibility** of the exported class/interface/enum names and method signatures — the
PkiSuiteSamples nodejs routes depend on them.
- Request/response shapes are **interfaces**, not classes, because consumers pass **plain object
literals** to the client (e.g. `createSessionAsync({ identifier, redirectUri, type })`). Enums are
real `enum`s because callers reference members by name (`TrustServiceSessionTypes.SingleSignature`).
- `scripts/Update-NodeClient.ps1` does the two mechanical halves (capture spec; typecheck + test).
The manual editing happens in between and is what this skill guides.
- The version is **package.json-driven** — set `"version"` and `npm publish`. (This is the opposite
of the PHP client, which is versioned by git tag.) `dist/` **is committed** (consumers install this
package as a folder, with no build step), so always `npm run build` after editing.

### Current surface (as of CloudHub 2.0.0)
`CloudHubClient` methods: `createSessionAsync`, `getCertificateAsync`, `signHashAsync` (the three the
PkiSuiteSamples node sample uses), plus 2.0.0 additions `createServiceSessionAsync`,
`getServiceAvailabilityAsync`, `getCustomStateAsync`, `getCertificateModelAsync`. Types/enums:
`SessionCreateRequest`, `ServiceSessionCreateRequest`, `ServiceSessionCreateResponse`, `SessionModel`,
`SignHashRequest`, `TrustServiceAuthParametersModel`, `TrustServiceInfoModel`,
`TrustServiceSessionTypes`, `IdentifierTypes`, `GetServiceAvailabilityResponse`, `CertificateModel`.
HTTP goes through `RestClient` (axios), which sends the `x-api-key` header on every call.

> `createServiceSessionAsync(name, ServiceSessionCreateRequest)` → `POST /api/sessions/services/{name}`
> starts a session against one named trust service (e.g. `"safeid"`) and needs **no CPF/CNPJ** — the
> provider identifies the signer during its own auth flow (e.g. a QR code). Contrast with
> `createSessionAsync` (`POST /api/sessions`), which either discovers services by identifier or, when
> given an empty identifier + no `discover` flag, returns every configured service.
>
> The client deliberately implements only the endpoints its consumers need — it does **not** cover
> the entire CloudHub API. The one not-yet-implemented 2.0 endpoint is `GET /api/sessions/services`
> (list `TrustServiceInfoModel[]` by identifier, no session/authUrl). Add it only when a consumer
> needs it (see step 4).

## Prerequisites

- **.NET SDK** matching CloudHub's target framework (currently `net10.0`) to build + capture the spec.
The `swashbuckle.aspnetcore.cli` tool is pinned in `.config/dotnet-tools.json` (version 10.1.7).
- The **CloudHub repo** checked out, by default a sibling folder `../cloudhub`.
- For verification: **Node.js 18+** and **npm** (the test suite uses the built-in `node:test` runner —
no external framework). If absent, you can still capture the spec and hand-edit; verification
degrades to a manual type review + flags it.

## Procedure

### 1. Capture the new spec
Capture-only first, so you can inspect the diff before editing:
```
pwsh scripts/Update-NodeClient.ps1 -SpecOnly
```
This builds CloudHub, then writes `openapi/cloudhub.json` via the Swashbuckle CLI (`dotnet swagger
tofile ... v1`). If build/capture fails, see **Troubleshooting**.

### 2. Review what changed upstream
```
git diff -- openapi/cloudhub.json
```
Write a short human summary grouped as:
- **Added** paths / schemas / properties
- **Removed** paths / schemas / properties ← breaking
- **Changed** types, `required`, enum members, `format` ← often breaking

### 3. Classify the change → decide the version bump
Use [semver](https://semver.org) against the current `package.json` `"version"`:

| Change | Bump |
|---|---|
| Only additive (new endpoints/models/optional fields) | **minor** |
| Removed/renamed endpoint, field, or enum member; a property became `required`; a type or wire `format` changed; an enum's underlying type changed (e.g. integer→string) | **major** |
| Doc-only / cosmetic | **patch** |

State the recommended new version and why. (CloudHub 2.0.0 was a **major** bump because
`TrustServiceSessionTypes` went from an integer TS enum to a **string** enum on the wire.)

### 4. Map each spec change to a hand edit (this is the judgment core — FLAG, don't guess)
There is no generator, so **every** change is a manual edit. Walk the diff and apply:

- **New / changed schema property** → edit the matching interface in `types.ts`. Add new fields as
**optional** (`field?: T`) so existing object-literal callers keep compiling. Mark nullable spec
fields as `T | null` and be tolerant when reading responses (don't assume arrays/objects exist —
the responses are returned raw from axios, so guard on the consuming side / type them `?`).
- **Enum change**:
- *String enum* (CloudHub 2.0's style): `export enum X { NAME = "NAME" }`. Just add/rename members.
- *Integer enum*: `export enum X { NAME = 1 }` — switching an enum between integer and string is a
**wire-format** change and therefore **breaking** (major).
- **New endpoint** → add a method on `CloudHubClient` mirroring the existing ones: build a **relative**
path (`` `api/...` ``; RestClient's axios instance already has `baseURL`), call
`this.client.get(endpoint)` / `this.client.post(endpoint, body)`, and type the return as the model
interface. `encodeURIComponent(...)` any path segment or query value. **Decide whether the endpoint
belongs in this thin client at all**: if no consumer (PkiSuiteSamples node, or the requester) needs
it, **flag it and leave it out** rather than growing untested surface.
- **Binary payloads** (`type: string, format: byte`): base64. Mirror `getCertificateAsync`, which
resolves to the base64 **string** (`Promise<string>`) — do NOT type it `Uint32Array`/`Buffer`.
- **Removed endpoint/field** → confirm intentional; call it out as breaking; only delete if you're
sure no consumer depends on it (this is a published package).
- **New auth scheme** → `RestClient` hard-codes the `x-api-key` header on the axios instance; update
it if a new scheme is needed, but flag it for a human first.

### 5. Verify
```
pwsh scripts/Update-NodeClient.ps1 -SkipCloudHubBuild # re-capture (fast) + typecheck + test
# or, to only typecheck+test the current code:
pwsh scripts/Update-NodeClient.ps1 -VerifyOnly
```
This runs `npm install`, `npx tsc --noEmit` (typecheck), then `npm test` (which builds `dist/` and
runs the `node:test` suite in `test/`). The tests inject a capturing axios adapter and assert the
`x-api-key` header is sent and that `TrustServiceSessionTypes.SingleSignature` serializes as the
**string** `"SingleSignature"`. Extend `test/cloudhub.test.js` when you add endpoints/models.
If Node/npm are unavailable, review types by eye and **say so** in the report.

### 6. Finalize
- Bump `"version"` in `package.json` to the new semver, and update the install snippet in
`README.md`. (Do **not** rely on git tags — this package is versioned by `package.json`.)
- `npm run build` to regenerate the committed `dist/`. Keep the stray top-level `.js` gone (the
`/*.js` gitignore rule prevents stale duplicates from reappearing next to the `.ts` sources).
- Re-run `git status` / `git diff` and sanity-check that only intended files changed and the public
API stayed backward-compatible (exported names + `new CloudHubClient(baseURL, apiKey)` and the
existing method signatures unchanged).

### 7. Report
Tell the user: the new version, the upstream-change summary, each flagged judgment item and how you
resolved it, and the verification result. Do **not** commit/push/tag unless asked.

## Verifying the result
- `npx tsc --noEmit` clean; `npm run build` regenerates `dist/` without errors.
- `npm test` green (x-api-key header sent + string-enum wire format).
- Backward compatibility intact: `new CloudHubClient(baseURL, apiKey)` and the existing
`createSessionAsync` / `getCertificateAsync` / `signHashAsync` signatures unchanged; the
PkiSuiteSamples node cloudhub routes still work against the new build.
- Idempotence: re-running `-SpecOnly` with no upstream change leaves an empty `git diff` on
`openapi/cloudhub.json`.

## Troubleshooting
- **CloudHub build fails on restore**: the private Lacuna NuGet feed isn't configured. Add it
(`dotnet nuget add source ...`) or build on a machine with a warm package cache. If you only have a
pre-exported `swagger.json`, copy it to `openapi/cloudhub.json` and skip to step 2. (The sibling
`cloudHubPhpClient/openapi/cloudhub.json` is captured from the same server and is byte-identical.)
- **`dotnet swagger tofile` fails**: confirm the Swagger doc name is still `v1`
(`Site/Startup.cs` → `SwaggerDoc("v1", ...)`); pass the matching name as the last arg. Note the
spec's `info.version` is the literal string `"v1"` (the doc id), **not** the product version.
- **`node --test` finds no tests**: the script runs `node --test "test/**/*.test.js"`. Test files
must live under `test/` and end in `.test.js`, and require the COMPILED `../dist` (so a build must
run first — `npm test` does `npm run build` before the test run).
- **Types don't resolve in the test**: the tests import runtime values from `../dist` (interfaces
vanish at compile time — only enums, `RestClient`, and `CloudHubClient` exist at runtime). Assert
interface shapes with plain object literals, not imports.
- **Stray root `.js` reappear**: those were stale 1.x compiled output. The build emits only to
`dist/`; `/*.js` is git-ignored. Delete any that show up; don't commit them.
- **No Node on the machine**: capture + hand-edit still work; verification degrades to a manual type
review. Install Node.js 18+ to run `tsc --noEmit` + `npm test`.
13 changes: 13 additions & 0 deletions .config/dotnet-tools.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"version": 1,
"isRoot": true,
"tools": {
"swashbuckle.aspnetcore.cli": {
"version": "10.1.7",
"commands": [
"swagger"
],
"rollForward": false
}
}
}
3 changes: 3 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Enable automatic line ending normalization (keeps diffs clean across Windows/Unix, so re-capturing
# openapi/cloudhub.json produces a stable, reviewable diff).
* text=auto
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,7 @@
node_modules/*
package-lock.json

# Stale compiled output used to sit next to the .ts sources at the repo root; the real build now
# emits only to dist/ (which IS committed, since consumers install this package as a folder with no
# build step). Ignore any top-level .js so those duplicates never come back.
/*.js
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ Or informing on your project's `package.json` file:
{
...
"dependencies": {
"cloudhub-client": "1.0.1"
"cloudhub-client": "2.0.0"
}
}

Expand Down
74 changes: 0 additions & 74 deletions cloudhub-client.js

This file was deleted.

Loading