@hideyukimori/nene2-client
v1.4.0
Published
TypeScript client ecosystem for NENE2 JSON APIs (OpenAPI-aligned)
Maintainers
Readme
nene2-js
TypeScript ecosystem for NENE2: OpenAPI-aligned types, HTTP client helpers, and Problem Details utilities for apps that consume NENE2 JSON APIs.
Documentation: https://hideyukimori.github.io/nene2-js/ (English, 日本語, Français, 中文, Português, Deutsch)
This repository is not a Node.js port of the PHP framework. The PHP runtime stays in NENE2. MCP stdio servers in PHP live in nene-mcp.
What this repo is for
- Typed fetch wrappers and shared client boundaries derived from NENE2 OpenAPI
- RFC 9457 Problem Details parsing and validation-error helpers for TypeScript consumers
- Optional codegen scripts and published npm packages (
@hideyukimori/nene2-client, scoped subpackages later) - Documentation and tooling that follow the same Issue-driven workflow as NENE2
What this repo is not for
- Replacing NENE2 PHP HTTP runtime, routing, middleware, or DI
- Duplicating nene-mcp (stdio MCP server in PHP)
- React/Vite starter UI (that remains in NENE2
frontend/unless explicitly extracted later) - Direct database access from AI tools or SDK code
- Application-specific business logic (belongs in consumer projects and NENE2-FT style trials)
See docs/scope.md for the full in-scope / out-of-scope matrix.
Install (consumers)
Use the published package from your app’s project root (React/Vite frontend, Node script, etc.). You do not clone nene2-js or lay it out next to NENE2 for normal usage.
cd your-app
npm install @hideyukimori/nene2-client@^1.0.0Requires Node 22+ (native fetch) or a browser with fetch. TypeScript consumers get .d.ts from the package.
import { createNene2Client } from '@hideyukimori/nene2-client';
const client = createNene2Client({
baseUrl: process.env.NENE2_JS_API_BASE_URL!,
});More examples: howto/consume-client.md · VitePress tutorial.
Develop this repository (contributors)
Optional sibling layout when working on nene2-js itself (OpenAPI sync, codegen, tests) next to NENE2 — not required to consume the npm package.
../docker/ # example parent directory
├── NENE2/ # PHP framework (OpenAPI source: docs/openapi/openapi.yaml)
├── nene2-js/ # this repository
├── nene-mcp/ # PHP MCP stdio library (separate concern)
└── NENE2-FT/ # field-trial reference apps (historical name; see nene2-js-FT)cd /path/to/parent-of-NENE2
git clone [email protected]:hideyukiMORI/nene2-js.git
cd nene2-js
npm install
npm run checkOpenAPI types: npm run codegen (see Phase 3).
Point live tests at a running NENE2 API when needed:
cp .env.example .env
# NENE2_JS_API_BASE_URL=http://localhost:8080Usage (typed client)
import { createNene2Client, Nene2ClientError } from '@hideyukimori/nene2-client';
const client = createNene2Client({
baseUrl: 'http://localhost:8080',
// apiKey: process.env.NENE2_MACHINE_API_KEY,
// bearer: process.env.NENE2_BEARER_TOKEN,
});
const { health, ping } = await client.smoke();
const root = await client.frameworkSmoke();
const notes = await client.listNotes({ limit: 20 });
// Load balancers may return 503 with status "degraded" — opt in:
const degraded = await client.health({ allowDegraded: true });
try {
await client.getNote(1);
} catch (err) {
if (err instanceof Nene2ClientError && err.problem) {
console.error(err.problem.title, err.problem.detail);
}
}Works in Node 22+ and browsers that provide fetch.
Verify the API before live smoke
Port 8080 is not always NENE2. Confirm the canonical health shape:
curl -sS http://localhost:8080/health | jq .
# expect: { "status": "ok", "service": "NENE2" }Or in TypeScript: await client.health({ strictService: true }) rejects wrong service values.
If you see a different JSON wrapper, point NENE2_JS_API_BASE_URL at a running NENE2 PHP instance (sibling ../NENE2).
Multi-backend live smoke (same client, OpenAPI contract — see ADR 0003):
export NENE2_JS_API_BASE_URL=http://localhost:18080 # NENE2 evac (see ft-evac-ports.md)
export NENE2_JS_PYTHON_BASE_URL=http://localhost:18000 # optional: nene2-python
# export NENE2_JS_NODE_BASE_URL=http://localhost:13000 # optional: nene2-node
npm run verify:backends # curl /health, /examples/ping, /examples/notes
npm test -- tests/client/live-smoke-matrix.test.tsUnset URLs are skipped; CI runs fixture tests only. Field-trial friction: ADR 0004.
Transport headers
By default, every authenticated request carries the bearer token on two headers: the standard Authorization and a non-standard X-Authorization mirror. Both the typed client (createNene2Client) and the fleet transport (createNene2Transport) apply the mirror on every path — JSON verbs, blob downloads, multipart uploads, raw byte POSTs. Auth headers are applied after static and per-request headers, so no per-request caller can drop or overwrite the mirror while it is enabled (src/transport/headers.ts, src/client/request.ts).
Why. Some shared-hosting front proxies and reverse proxies strip the standard Authorization header before it reaches the application. NENE2 backends fall back to the X-Authorization mirror when the standard header is missing, so the mirror keeps auth working behind such proxies.
Operational note — please read. Because the bearer is duplicated onto a non-standard header, add X-Authorization to the credential-masking rules of anything that records or inspects requests: application logs, access logs, WAF rules, and proxy log pipelines. An environment that masks only Authorization will otherwise record the bearer token in clear text.
Opting out (since 1.3.0). If you control the edge and know Authorization reaches the backend intact, disable the mirror at construction time with mirrorAuthorizationHeader: false — the client then sends Authorization only. This is a construction-time switch; there is no per-request override.
// Authorization only — no X-Authorization mirror
const client = createNene2Client({
baseUrl: process.env.NENE2_JS_API_BASE_URL!,
bearer,
mirrorAuthorizationHeader: false,
});
// Same switch on the fleet transport
const transport = createNene2Transport({ tokenStore, mirrorAuthorizationHeader: false });The default remains true (mirror on) to keep working behind Authorization-stripping proxies. Making the mirror off by default is planned for a future major release; until then, deployments that keep the default should continue to mask X-Authorization.
Transport contract for consumers (/testing)
The package's own unit tests prove that the package works. They cannot tell you whether your product's wiring — token store, onUnauthorized, recoverAuth, per-request headers — matches what the fleet assumes, and they are not distributed (they live in tests/, outside files).
The ./testing subpath ships a contract you run in your own repository:
// frontend/src/shared/api/transport.contract.test.ts
import { describe, it } from 'vitest';
import { createSessionTokenStore } from '@hideyukimori/nene2-client';
import { runTransportContract } from '@hideyukimori/nene2-client/testing';
runTransportContract({
product: 'nene-payout',
surface: 'adapter', // or 'transport' — required; see the how-to
runner: { describe, it },
createWiring: ({ storage }) => {
const tokenStore = createSessionTokenStore({ key: 'nene_payout_token', storage });
return {
config: buildTransportConfig(tokenStore), // your own config
seedToken: (token) => tokenStore.setToken(token),
};
},
});surface declares how your features reach the transport: pass createTransport from createWiring when they go through your own apiClient. Four required cases can only see a failure through that seam — measured, not assumed.
It registers 12 required cases in five groups — the X-Authorization mirror on every path, sessionStorage-only token handling, 401/403 policy, and single-flight recovery — plus an empty-run guard so a suite that shrinks to nothing cannot report success. Differences are declared as exemptions with a reason and a ref, never skipped silently. Individual expect* helpers are exported too, for products that need finer control.
Setup, exemptions, the jsdom requirement, and how to ask for the check to become required are in docs/howto/transport-contract.md.
Documentation site (local)
npm install
npm run docs:dev # http://localhost:5175
npm run docs:build # static output → .vitepress/distPublished on push to main via .github/workflows/docs.yml.
Contributing
Work is GitHub Issue driven. Read docs/CONTRIBUTING.md and docs/workflow.md before opening a PR.
AI agents: start at AGENTS.md.
Consumer DX evidence: field trials (FT30–529 marathon; see methodology, friction registry). Local app sandbox: sibling ../nene2-js-FT/. Quick start: howto/consume-client.md.
Release: releases.md · publish.md · GitHub Releases · Phase history: phase-2.md.
Related projects
| Project | Role |
| ----------------------------------------------------------------------- | -------------------------------------------------------- |
| NENE2 | PHP API framework, OpenAPI contract, MCP catalog in-repo |
| nene2-node | Node.js framework port (@hideyukimori/nene2-framework) |
| nene-mcp | Standalone PHP stdio MCP server |
| hideyukimori/nene2 | Composer package for PHP consumers |
License
MIT — see LICENSE.
