diff --git a/brain/architecture/shared-utils.md b/brain/architecture/shared-utils.md index 4578843..7d2255a 100644 --- a/brain/architecture/shared-utils.md +++ b/brain/architecture/shared-utils.md @@ -10,8 +10,8 @@ - `pluginClient` (`src/kubb/plugin.ts`) with its generators in `src/kubb/client/` and the operation component in `src/kubb/components/`. kubb 5 stable dropped `@kubb/plugin-client`, so the plugin shell lives here (comment in `plugin.ts`). - Spec transforms (`src/kubb/spec-transforms.ts`): `fetchSpec` (throws on a timeout, a network failure, a non-OK status, a non-JSON body or a body without an `openapi`/`swagger` version and non-empty `paths`; every message names the URL, and the timeout, network and JSON errors keep the original as `cause`), `cleanOperationIds`, `sortArrays`, `addMissingPathParams`, `fixArrayItems`, `fixUnionConstraints`, `sanitizeEnumValues`, `camelCasePathParams`, `renameReservedWords`, `fixRegexPatterns`, `camelCaseProperties`. - `./effect` (`src/effect/index.ts`): the runtime side, shipped inside each client's `effect` entry. - - Tagged errors `ValidationError`, `ApiError`, `NetworkError`, the `ApiConfig` context tag and `makeApiLayer` (`src/effect/errors.ts`). `ApiError.message` is `API error : `, the message taken from the fetcher's `{ status, payload }` payload first and then the error itself (`message`, `error` as a string or `{ message }`, `errors[0].message`, or a text body), and falls back to `API Error: `. `NetworkError.message` reads `message` from an `Error` or a plain object cause. `errors.test.ts` covers both (#33). - - `createEffectApi` and `createEffectByPath` (`src/effect/proxy.ts`). + - Tagged errors `ValidationError`, `ApiError`, `NetworkError`, the `ApiConfig` context tag and `makeApiLayer` (`src/effect/errors.ts`). `ApiConfig` carries `token`, `baseUrl`, `fetchImpl` and `headers`, and `makeApiLayer` accepts all four (#46). `ApiError.message` is `API error : `, the message taken from the fetcher's `{ status, payload }` payload first and then the error itself (`message`, `error` as a string or `{ message }`, `errors[0].message`, or a text body), and falls back to `API Error: `. `NetworkError.message` reads `message` from an `Error` or a plain object cause. `errors.test.ts` covers both (#33). + - `createEffectApi` and `createEffectByPath` (`src/effect/proxy.ts`). Each operation runs in `Effect.tryPromise` and passes the `AbortSignal` it provides to the operation as `config.signal`, which the generated operation hands to the client's fetcher, so `Effect.timeout`, `Fiber.interrupt` or a lost race aborts the `fetch` instead of leaving it running. The proxies read only own keys, so `Object.prototype` names such as `constructor` return `undefined` (#46). ## Why it matters diff --git a/brain/architecture/website.md b/brain/architecture/website.md index c6f0038..8807da4 100644 --- a/brain/architecture/website.md +++ b/brain/architecture/website.md @@ -3,6 +3,8 @@ `apps/openapi` (`@sferadev/openapi-website`, private) is a Next.js app that lists the clients with install and usage snippets (`apps/openapi/src/app/page.tsx`). Changesets ignores it (`.changeset/config.json`). - The page is statically rendered and revalidates every 6 hours (`revalidate = 21600` in `apps/openapi/src/app/page.tsx`). Each version it shows comes from the npm registry, and each lookup is cached for an hour (`apps/openapi/src/lib/npm.ts`), so a new release can take up to 6 hours to appear. A failed lookup renders `unknown` and logs one JSON line naming the package: `npm package not published` with `status: 404`, or `npm registry unavailable` with the other status, or a `reason` for a network or JSON parse error. +- The provider tabs (`apps/openapi/src/components/provider-card.tsx`) render the first provider on the server and on the first client render, then restore the tab stored under the `localStorage` key `activeProviderTab` after mount, ignoring a stored id that matches no provider. Reading storage during the first render caused a hydration mismatch, and the tab list wraps so it fits any number of clients (#48). +- `apps/openapi/AGENTS.md` is written by `next dev` and points agents at the Next.js docs in `node_modules/next/dist/docs/`; it is committed so the tree stays clean (#48). - `GET /api/health` (`apps/openapi/src/app/api/health/route.ts`) runs `select 1` through the Drizzle `db` with a 3 second limit and returns `{ status, checks: { database } }`: 200 when it succeeds, 503 otherwise. A failure is logged server side with the driver error's name and message, never the connection string. The route is dynamic and sends `Cache-Control: no-store`, takes no auth and reads no new environment variables. - Auth is Better Auth with a Vercel OAuth provider (`apps/openapi/src/lib/auth.ts`, `apps/openapi/src/app/api/auth/[...all]/route.ts`), stored in Postgres through Drizzle (`apps/openapi/src/lib/db.ts`, migrations in `apps/openapi/drizzle/`). The Vercel userinfo fetch and profile mapping live in `apps/openapi/src/lib/vercel-user-info.ts`, apart from Better Auth and the database so they can be unit-tested ([[development/testing]]). - It imports no client package, so its build does not wait on any client build. The environment variables it reads, which `turbo.json` also lists for its build: diff --git a/brain/development/release.md b/brain/development/release.md index e563e3c..297ab6d 100644 --- a/brain/development/release.md +++ b/brain/development/release.md @@ -24,8 +24,8 @@ As of 2026-10-14 (`npm view version`, unchanged since the 2026-10-08 c ## Pending changesets -As of 2026-10-15, `.changeset/` holds 34 changesets besides `README.md` and `config.json`. The first release will be a major for all eight published clients: +As of 2026-10-16, `.changeset/` holds 57 changesets besides `README.md` and `config.json`. The first release will be a major for all eight published clients: -- Majors: `effect-v4.md` for the five Effect clients (#6, [[decisions/2026-10-08-effect-4-major]]), and `http-errors-as-api-error.md` for all eight published clients, the new `{ status, payload }` rejection shape (#8, made a major in #9). +- Majors: `effect-v4.md` for the five published Effect clients, which leaves out the unpublished `clickhouse-cloud` (#6, [[decisions/2026-10-08-effect-4-major]]), and `http-errors-as-api-error.md` for all eight published clients, the new `{ status, payload }` rejection shape (#8, made a major in #9). - Minors: `clickhouse-cloud-client.md` (#2); `path-encoding-clickhouse-cloud.md` and `path-encoding-vercel-api-js.md`, minors because their regenerated specs add operations (#17); `raw-request-bodies-netlify-api.md`, the `body` parameter for non-JSON request bodies (#25). -- Patches: `bundle-openapi-utils.md` (#1), `inline-effect-types.md` (#4), `fetcher-query-serialization.md` (#16), `path-encoding-*.md` for the other seven clients (#17), `path-params-keep-slashes-*.md` for `cloudflare-api-js`, `keycloak-api` and `netlify-api` (#23), `untagged-operations-*.md` for `cloudflare-api-js`, `keycloak-api`, `litellm-api` and `vercel-api-js` (#24), `request-via-operations-*.md` for all nine clients (#26), `fetcher-empty-body-header-case.md` for all nine clients (#28), and `zoom-refresh-basic-auth.md` (#30). +- Patches: `bundle-openapi-utils.md` (#1), `inline-effect-types.md` (#4), `fetcher-query-serialization.md` (#16), `path-encoding-*.md` for the other seven clients (#17), `path-params-keep-slashes-*.md` for `cloudflare-api-js`, `keycloak-api` and `netlify-api` (#23), `untagged-operations-*.md` for `cloudflare-api-js`, `keycloak-api`, `litellm-api` and `vercel-api-js` (#24), `request-via-operations-*.md` for all nine clients (#26), `fetcher-empty-body-header-case.md` for all nine clients (#28), `zoom-refresh-basic-auth.md` (#30), `effect-error-message-*.md` for the six Effect clients (#33), `fetcher-failure-detail-*.md` for all nine clients (#37), `dts-private-types-*.md` for `keycloak-api` and `v0-api` (#42), and `effect-abort-signal-*.md` for the six Effect clients (#46). diff --git a/brain/development/testing.md b/brain/development/testing.md index b34c693..55127f8 100644 --- a/brain/development/testing.md +++ b/brain/development/testing.md @@ -17,7 +17,7 @@ Run alone with `pnpm turbo run test --filter=@sferadev/openapi-utils`. - `src/kubb/components/client-operation.test.ts`: `buildUrlTemplate` percent-encodes single-segment path parameters (#17), keeps slashes in file path parameters and does not double-encode a path already passed through `encodeURI` (#23). - `src/kubb/client/operations.test.ts`: runs kubb with `createConfig` on the fixture `src/kubb/client/fixtures/params.json` into a temp directory, then imports the emitted `components.ts` against a stub fetcher and a fake `fetch`. A path parameter that is not a JS identifier (`team-id`) is read with bracket access and percent-encoded, a falsy required value (`0`) is kept, query and header params are forwarded, an operation without parameters works, and a missing or empty path parameter, or an omitted `pathParams`, rejects with `Missing required path parameter` before `fetch` is called. - `src/kubb/adapter.test.ts`: `rawRequestBodyFallback` keeps the first raw media type and leaves JSON-like and url-encoded bodies to the JSON parse (#25). -- `src/effect/proxy.test.ts`: the Effect proxies map a rejection with a `status` to `ApiError`, a missing path parameter to `ValidationError` naming the field and anything else to `NetworkError`, pass the `ApiConfig` service to the operation as `config`, and return `undefined` for an unknown tag, operation or endpoint (#33). +- `src/effect/proxy.test.ts`: the Effect proxies map a rejection with a `status` to `ApiError`, a missing path parameter to `ValidationError` naming the field and anything else to `NetworkError`, pass the `ApiConfig` service to the operation as `config`, and return `undefined` for an unknown tag, operation or endpoint (#33). They also abort the request signal when the effect times out, provide `fetchImpl` to the operation, and return `undefined` for `Object.prototype` keys (#46). - `src/effect/errors.test.ts`: how `ApiError.message` and `NetworkError.message` read each provider's error shape (#33). - `src/kubb/client/extra.test.ts`: reads every package's committed `src/**/generated/components.ts` and asserts that each exported operation appears in `operationsByTag` (#24). diff --git a/brain/plans/index.md b/brain/plans/index.md index ffae2a8..b76b62a 100644 --- a/brain/plans/index.md +++ b/brain/plans/index.md @@ -1,8 +1,8 @@ # Plans -The repository has no roadmap file. Its one open issue, #15, concerns `hq-stack.yml`, which is rendered from sferarc/hq and not edited here ([[development/ci]]) (checked 2026-10-14). Known open work, from the sources cited: +The repository has no roadmap file. Its one open issue, #15, concerns `hq-stack.yml`, which is rendered from sferarc/hq and not edited here ([[development/ci]]) (checked 2026-10-16). Known open work, from the sources cited: -1. First release from this repository, which waits on the owner setting `RELEASE_ENABLED` ([[development/release]]). It ships the openapi-utils bundling fix (#1), the inlined Effect types (#4), the first `clickhouse-cloud` version (#2), the path encoding, raw body, untagged operation and `request()` fixes (#17, #23 to #26), the fetcher's empty body and header case fixes (#28) and Zoom's basic auth token refresh (#30). +1. First release from this repository, which waits on the owner setting `RELEASE_ENABLED` ([[development/release]]). It ships the openapi-utils bundling fix (#1), the inlined Effect types (#4), the first `clickhouse-cloud` version (#2), the path encoding, raw body, untagged operation and `request()` fixes (#17, #23 to #26), the fetcher's empty body and header case fixes (#28), Zoom's basic auth token refresh (#30), the API's message in `ApiError` (#33), the failure cause and response body kept on fetcher errors (#37), the exported fetcher types in the `keycloak-api` and `v0-api` declarations (#42) and aborting the request when an Effect operation is interrupted (#46). 2. That first release is a major for every published client: the five Effect clients for Effect 4 ([[decisions/2026-10-08-effect-4-major]]), and all eight for the HTTP error shape change (#9, `.changeset/http-errors-as-api-error.md`). The full list of pending changesets is in [[development/release]]. The older clients' fetchers used to report an HTTP error status as `NetworkError` rather than `ApiError` (#6). #8 fixed that, and each Effect client's `src/effect.test.ts` asserts `ApiError` with the status (checked 2026-10-13).