{"_id":"@molecule/app-http","_rev":"3-87ed1e1fc8cb5b81e604ab79cf082070","name":"@molecule/app-http","dist-tags":{"latest":"1.0.2"},"versions":{"1.0.0":{"name":"@molecule/app-http","version":"1.0.0","keywords":["molecule","http","api-client","fetch"],"license":"Apache-2.0","_id":"@molecule/app-http@1.0.0","maintainers":[{"name":"vialoh","email":"npm@vialoh.me"}],"homepage":"https://github.com/molecule-dev/molecule/tree/main/packages/app/core/http","bugs":{"url":"https://github.com/molecule-dev/molecule/issues"},"dist":{"shasum":"f58eef420d97aa99039ca154837609f302ed5749","tarball":"https://registry.npmjs.org/@molecule/app-http/-/app-http-1.0.0.tgz","fileCount":30,"integrity":"sha512-qNSg0DfNA1qoTSYiluRu/UNBcSZB04asf3pkam1+4c6gOgB72x+0as2LS6WEGebspoBDbb4r8kINUFiKTKLKPw==","signatures":[{"sig":"MEUCIQCQthHXSg2gP2WuJ6vF1IUGjYuEe6/vxgFuHhfx00ascAIgdapr1K9YWVu5Rq2UagGftEuJn8UFzft/vrW8IzNAiHQ=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":73518},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"gitHead":"92623e72a527ca467963169420f4cf07533e4699","scripts":{"test":"vitest run","build":"tsc","test:watch":"vitest"},"_npmUser":{"name":"vialoh","email":"npm@vialoh.me"},"repository":{"url":"git+https://github.com/molecule-dev/molecule.git","type":"git","directory":"packages/app/core/http"},"_npmVersion":"11.12.1","description":"Client HTTP interface for molecule.dev","directories":{},"_nodeVersion":"25.9.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"4.1.10","typescript":"6.0.3","@types/node":"26.1.2","@molecule/app-bond":"1.0.0","@molecule/app-i18n":"1.0.0","@molecule/app-logger":"1.0.0"},"peerDependencies":{"@molecule/app-bond":"^1.0.0","@molecule/app-i18n":"^1.0.0","@molecule/app-logger":"^1.0.0"},"_npmOperationalInternal":{"tmp":"tmp/app-http_1.0.0_1785796139473_0.25782730487038785","host":"s3://npm-registry-packages-npm-production"}},"1.0.1":{"name":"@molecule/app-http","version":"1.0.1","keywords":["molecule","http","api-client","fetch"],"license":"Apache-2.0","_id":"@molecule/app-http@1.0.1","maintainers":[{"name":"vialoh","email":"npm@vialoh.me"}],"homepage":"https://github.com/molecule-dev/molecule/tree/main/packages/app/core/http","bugs":{"url":"https://github.com/molecule-dev/molecule/issues"},"dist":{"shasum":"03291c7b57bea4cfba8600c8d107b6f29134f790","tarball":"https://registry.npmjs.org/@molecule/app-http/-/app-http-1.0.1.tgz","fileCount":31,"integrity":"sha512-fC0oKpGOJ5fND9hLERQ70mfsCDlubzBVhC6PqZNVPr5h9ilNxSvJN8tc7/WQaBH91laKOx5b3XBeY5V9uPBq7w==","signatures":[{"sig":"MEYCIQDx3PDymO37yaEFzSsG7RGmA/LBy3ftljepNybSCYuROwIhAPPu8SQSQzHSMPJs1nh9kepwLMlra5Zm8td1Ykids4W4","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@molecule%2fapp-http@1.0.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":86818},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"gitHead":"8621216fd4c8c9abe863e4e4f41efd2bc866fb09","scripts":{"test":"vitest run","build":"tsc","test:watch":"vitest"},"_npmUser":{"name":"vialoh","email":"npm@vialoh.me"},"repository":{"url":"git+https://github.com/molecule-dev/molecule.git","type":"git","directory":"packages/app/core/http"},"_npmVersion":"12.0.2","description":"Client HTTP interface for molecule.dev","directories":{},"_nodeVersion":"22.23.1","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"4.1.10","typescript":"6.0.3","@types/node":"26.1.2","@molecule/app-bond":"1.0.1","@molecule/app-i18n":"1.0.1","@molecule/app-logger":"1.0.1"},"peerDependencies":{"@molecule/app-bond":"^1.0.1","@molecule/app-i18n":"^1.0.1","@molecule/app-logger":"^1.0.1"},"_npmOperationalInternal":{"tmp":"tmp/app-http_1.0.1_1785821132873_0.30475668379516585","host":"s3://npm-registry-packages-npm-production"}},"1.0.2":{"_id":"@molecule/app-http@1.0.2","bugs":{"url":"https://github.com/molecule-dev/molecule/issues"},"dist":{"shasum":"ac45547dd6765e4aa3c47e31c28415e6ba8fe676","tarball":"https://registry.npmjs.org/@molecule/app-http/-/app-http-1.0.2.tgz","fileCount":31,"integrity":"sha512-swI3Buwr+i6jSxTxHkUaBEOlcYSbFuL1LWbBee09GF4wJYpSUa/n7bOSyIqwNlDdW3JALSQWdlVhfvVcJrrtMw==","signatures":[{"sig":"MEQCIA/7M8TaPi8tBahHAXEY6kCwTk29TJqH7q4cxrb4UetjAiAOAtjIC0h6kOQvpDYsf0Yz8wSW9nXvYod5edK4obITNg==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQDZvF/IV3eY9xpSpevwC3ion8aYdUjgBpmNEhyPdf+EFQIhANDUG6VEoDnO7h0QYILsblTDanTTq3oxCcX7PeHbIFag"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@molecule%2fapp-http@1.0.2","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":86787},"main":"dist/index.js","name":"@molecule/app-http","type":"module","types":"dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"gitHead":"41bbb7d6c46b04d052a6b333e1b18e76ed007d23","license":"Apache-2.0","scripts":{"test":"vitest run","build":"tsc","test:watch":"vitest"},"version":"1.0.2","_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:f7265069-7935-4593-91b0-8452bcdc777b"}},"homepage":"https://www.molecule.dev/packages/app-http","keywords":["molecule","http","api-client","fetch"],"repository":{"url":"git+https://github.com/molecule-dev/molecule.git","type":"git","directory":"packages/app/core/http"},"_npmVersion":"12.0.2","description":"Client HTTP interface for molecule.dev","directories":{},"maintainers":[{"name":"vialoh","email":"npm@vialoh.me"}],"_nodeVersion":"22.23.2","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"4.1.11","typescript":"6.0.3","@types/node":"26.1.2","@molecule/app-bond":"1.0.2","@molecule/app-i18n":"1.0.2","@molecule/app-logger":"1.0.2"},"peerDependencies":{"@molecule/app-bond":"^1.0.1","@molecule/app-i18n":"^1.0.1","@molecule/app-logger":"^1.0.1"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/app-http_1.0.2_1789909589196_0.4863838117764552"}}},"time":{"created":"2026-08-03T22:28:59.340Z","modified":"2026-09-20T13:06:29.686Z","1.0.0":"2026-08-03T22:28:59.630Z","1.0.1":"2026-08-04T05:25:33.026Z","1.0.2":"2026-09-20T13:06:29.301Z"},"bugs":{"url":"https://github.com/molecule-dev/molecule/issues"},"license":"Apache-2.0","homepage":"https://www.molecule.dev/packages/app-http","keywords":["molecule","http","api-client","fetch"],"repository":{"url":"git+https://github.com/molecule-dev/molecule.git","type":"git","directory":"packages/app/core/http"},"description":"Client HTTP interface for molecule.dev","maintainers":[{"name":"vialoh","email":"npm@vialoh.me"}],"readme":"<!--\nAUTO-GENERATED — DO NOT EDIT THIS FILE.\nGenerated by `mlcl sync-docs` from the package's src/index.ts JSDoc + mlcl/registry.json.\nEdits here are overwritten on the next commit (molecule's pre-commit hook regenerates).\nTo change this document, edit the module-level JSDoc in src/index.ts.\nGenerated: 2026-08-04T01:50:58.464Z\n-->\n\n# @molecule/app-http\n\n> **Auto-generated, AI-first package reference** for the [molecule.dev](https://molecule.dev) ecosystem.\n> It is written to be read by coding agents as much as by people, and is generated from this\n> package's source — edit `src/index.ts` JSDoc, not this file.\n\nClient HTTP interface for molecule.dev.\n\nProvides a unified HTTP client API that works across different\nHTTP libraries (fetch, axios, ky, etc.).\n\n## Quick Start\n\n```tsx\n// In a React component, get the configured client from context and call it.\n// The hook is exported by the framework binding (@molecule/app-react), not\n// this core package — never construct your own fetch/axios client.\nimport { useHttpClient } from '@molecule/app-react'\n\nfunction Plants() {\n  const http = useHttpClient()\n  const load = async () => {\n    const res = await http.get<Plant[]>('/plants') // baseURL ('/api') is prepended\n    setPlants(res.data)\n  }\n  // http.post(url, body), http.put, http.delete are also available.\n}\n```\n\n## Type\n\n`core`\n\n## Installation\n\n```bash\nnpm install @molecule/app-http @molecule/app-bond @molecule/app-i18n @molecule/app-logger\n```\n\n## API\n\n### Interfaces\n\n#### `FullRequestConfig`\n\nFull request configuration including method and URL.\n\n```typescript\ninterface FullRequestConfig extends RequestConfig {\n  /**\n   * HTTP method.\n   */\n  method: HttpMethod\n\n  /**\n   * Request URL (can be relative or absolute).\n   */\n  url: string\n\n  /**\n   * Request body data.\n   */\n  data?: unknown\n}\n```\n\n#### `HttpClient`\n\nHTTP client interface.\n\nAll HTTP providers must implement this interface.\n\n```typescript\ninterface HttpClient {\n  /**\n   * Base URL for all requests.\n   */\n  baseURL: string\n\n  /**\n   * Default headers for all requests.\n   */\n  defaultHeaders: Record<string, string>\n\n  /**\n   * Makes a generic HTTP request.\n   */\n  request<T = unknown>(config: FullRequestConfig): Promise<HttpResponse<T>>\n\n  /**\n   * Makes a GET request.\n   */\n  get<T = unknown>(url: string, config?: RequestConfig): Promise<HttpResponse<T>>\n\n  /**\n   * Makes a POST request.\n   */\n  post<T = unknown>(url: string, data?: unknown, config?: RequestConfig): Promise<HttpResponse<T>>\n\n  /**\n   * Makes a PUT request.\n   */\n  put<T = unknown>(url: string, data?: unknown, config?: RequestConfig): Promise<HttpResponse<T>>\n\n  /**\n   * Makes a PATCH request.\n   */\n  patch<T = unknown>(url: string, data?: unknown, config?: RequestConfig): Promise<HttpResponse<T>>\n\n  /**\n   * Makes a DELETE request.\n   */\n  delete<T = unknown>(url: string, config?: RequestConfig): Promise<HttpResponse<T>>\n\n  /**\n   * Adds a request interceptor.\n   * Returns a function to remove the interceptor.\n   */\n  addRequestInterceptor(interceptor: RequestInterceptor): () => void\n\n  /**\n   * Adds a response interceptor.\n   * Returns a function to remove the interceptor.\n   */\n  addResponseInterceptor(interceptor: ResponseInterceptor): () => void\n\n  /**\n   * Adds an error interceptor.\n   * Returns a function to remove the interceptor.\n   */\n  addErrorInterceptor(interceptor: ErrorInterceptor): () => void\n\n  /**\n   * Sets the authorization token.\n   */\n  setAuthToken(token: string | null): void\n\n  /**\n   * Returns the current authorization token, or `null` if not set.\n   */\n  getAuthToken(): string | null\n\n  /**\n   * Registers a handler for authentication errors (401).\n   *\n   * @returns An unsubscribe function.\n   */\n  onAuthError(handler: () => void): () => void\n}\n```\n\n#### `HttpClientConfig`\n\nHTTP client configuration.\n\n```typescript\ninterface HttpClientConfig {\n  /**\n   * Base URL for all requests.\n   */\n  baseURL?: string\n\n  /**\n   * Default headers for all requests.\n   */\n  defaultHeaders?: Record<string, string>\n\n  /**\n   * Default timeout in milliseconds.\n   */\n  timeout?: number\n\n  /**\n   * Whether to include credentials by default.\n   */\n  withCredentials?: boolean\n}\n```\n\n#### `HttpResponse`\n\nParsed HTTP response with status code, headers, and typed body data.\n\n```typescript\ninterface HttpResponse<T = unknown> {\n  /**\n   * Response data.\n   */\n  data: T\n\n  /**\n   * HTTP status code.\n   */\n  status: number\n\n  /**\n   * HTTP status text.\n   */\n  statusText: string\n\n  /**\n   * Response headers.\n   */\n  headers: Record<string, string>\n\n  /**\n   * Original request config.\n   */\n  config: FullRequestConfig\n}\n```\n\n#### `RequestConfig`\n\nHTTP request options (headers, query params, timeout, credentials, response type, abort signal).\n\n```typescript\ninterface RequestConfig {\n  /**\n   * Request headers.\n   */\n  headers?: Record<string, string>\n\n  /**\n   * Query parameters.\n   */\n  params?: Record<string, string | number | boolean | undefined>\n\n  /**\n   * Request timeout in milliseconds.\n   */\n  timeout?: number\n\n  /**\n   * Whether to include credentials (cookies).\n   */\n  withCredentials?: boolean\n\n  /**\n   * Response type.\n   */\n  responseType?: 'json' | 'text' | 'blob' | 'arraybuffer'\n\n  /**\n   * Abort signal for cancellation.\n   */\n  signal?: AbortSignal\n\n  /**\n   * Request body data.\n   */\n  data?: unknown\n\n  /**\n   * Custom request options (implementation-specific).\n   */\n  options?: Record<string, unknown>\n}\n```\n\n### Types\n\n#### `ErrorInterceptor`\n\nIntercepts HTTP errors to transform, retry, or rethrow them.\nThrowing from this interceptor propagates the error to the caller.\n\n```typescript\ntype ErrorInterceptor = (error: HttpError) => HttpError | Promise<HttpError> | never\n```\n\n#### `HttpMethod`\n\nHTTP request method.\n\n```typescript\ntype HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'HEAD' | 'OPTIONS'\n```\n\n#### `RequestInterceptor`\n\nIntercepts outgoing requests to modify headers, URL, or body\nbefore the request is sent.\n\n```typescript\ntype RequestInterceptor = (\n  config: FullRequestConfig,\n) => FullRequestConfig | Promise<FullRequestConfig>\n```\n\n#### `ResponseInterceptor`\n\nIntercepts incoming responses to transform data, check status,\nor perform side effects before the response reaches the caller.\n\n```typescript\ntype ResponseInterceptor<T = unknown> = (\n  response: HttpResponse<T>,\n) => HttpResponse<T> | Promise<HttpResponse<T>>\n```\n\n### Classes\n\n#### `HttpError`\n\nHTTP error with response details.\n\n### Functions\n\n#### `createFetchClient(config)`\n\nCreates a fetch-based HTTP client using the native Fetch API.\n\nSupports request/response/error interceptors, automatic JSON\nserialization, auth token injection, timeout via AbortController,\nand 401 error handler hooks.\n\n```typescript\nfunction createFetchClient(config?: HttpClientConfig): HttpClient\n```\n\n- `config` — Client configuration including baseURL, default headers, timeout, and credentials.\n\n**Returns:** A fully configured `HttpClient` instance.\n\n#### `del(url, config)`\n\nMakes a DELETE request using the bonded HTTP client.\n\n```typescript\nfunction del(url: string, config?: RequestConfig): Promise<HttpResponse<T>>\n```\n\n- `url` — The request URL.\n- `config` — Optional request configuration.\n\n**Returns:** The HTTP response with typed data.\n\n#### `get(url, config)`\n\nMakes a GET request using the bonded HTTP client.\n\n```typescript\nfunction get(url: string, config?: RequestConfig): Promise<HttpResponse<T>>\n```\n\n- `url` — The request URL (relative to baseURL if configured).\n- `config` — Optional request configuration (headers, params, timeout).\n\n**Returns:** The HTTP response with typed data.\n\n#### `getClient()`\n\nRetrieves the bonded HTTP client. If none is bonded, automatically\ncreates a default fetch-based client.\n\n```typescript\nfunction getClient(): HttpClient\n```\n\n**Returns:** The active HTTP client instance.\n\n#### `patch(url, data, config)`\n\nMakes a PATCH request using the bonded HTTP client.\n\n```typescript\nfunction patch(url: string, data?: unknown, config?: RequestConfig): Promise<HttpResponse<T>>\n```\n\n- `url` — The request URL.\n- `data` — The request body (partial update).\n- `config` — Optional request configuration.\n\n**Returns:** The HTTP response with typed data.\n\n#### `post(url, data, config)`\n\nMakes a POST request using the bonded HTTP client.\n\n```typescript\nfunction post(url: string, data?: unknown, config?: RequestConfig): Promise<HttpResponse<T>>\n```\n\n- `url` — The request URL.\n- `data` — The request body.\n- `config` — Optional request configuration.\n\n**Returns:** The HTTP response with typed data.\n\n#### `put(url, data, config)`\n\nMakes a PUT request using the bonded HTTP client.\n\n```typescript\nfunction put(url: string, data?: unknown, config?: RequestConfig): Promise<HttpResponse<T>>\n```\n\n- `url` — The request URL.\n- `data` — The request body.\n- `config` — Optional request configuration.\n\n**Returns:** The HTTP response with typed data.\n\n#### `setClient(client)`\n\nRegisters an HTTP client as the active singleton.\n\n```typescript\nfunction setClient(client: HttpClient): void\n```\n\n- `client` — The HTTP client implementation to bond.\n\n#### `unwrapList(res)`\n\nNormalize an unknown response body into a typed array.\n\nAccepts:\n\n- a bare array → returned as-is (cast)\n- `{ data: T[] }` envelope → the inner array\n- `HttpResponse<T[]>` (i.e. `{ data: T[], status, ... }`) → the inner array\n- `HttpResponse<{ data: T[] }>` (the response of an envelope-returning\n  endpoint as it arrives from `@molecule/app-http`'s `HttpClient`) → the\n  doubly-nested inner array\n- `HttpResponse<{ data: T[], total, limit, offset }>` (a RICH pagination\n  envelope from a pre-built `@molecule/api-resource-*` list endpoint) → the\n  inner `data` array (the numeric `total`/`limit`/`offset` make the shape\n  unambiguous, so callers can pass the whole HttpResponse and still get the rows)\n- anything else → `[]`\n\nCallers commonly pass either the raw JSON body (e.g. from `fetch().then(r =>\nr.json())`) or the `HttpResponse` returned by `useHttpClient().get(...)`.\nBoth shapes are handled here so pages don't have to remember to call\n`unwrapList(res.data)` vs `unwrapList(res)`.\n\n```typescript\nfunction unwrapList(res: unknown): T[]\n```\n\n- `res` — Raw response body OR an `HttpResponse` envelope from `@molecule/app-http`.\n\n**Returns:** A typed array `T[]`; never `null`/`undefined`.\n\n#### `unwrapSingle(res)`\n\nNormalize an unknown response body into a single typed resource.\n\nAccepts:\n\n- a non-empty plain object → returned as-is (cast)\n- `{ data: T }` envelope → the inner value\n- `{ data: null }`, `{ data: undefined }`, `{ data: [] }`, or\n  `{ data: {} }` (mock-server's no-match shape) → `null`\n- an empty object `{}` → `null`\n- arrays, primitives, `null`, `undefined` → `null`\n\nThe \"envelope contains an array → null\" branch handles the case\nwhere the mock server returns `[]` for unmatched endpoints but the\ncaller expects a single resource.\n\n```typescript\nfunction unwrapSingle(res: unknown): T | null\n```\n\n- `res` — Raw response body (e.g. `HttpResponse.data` from `@molecule/app-http`).\n\n**Returns:** The typed resource `T`, or `null` when the response shape indicates \"no resource\" (including the various empty envelopes above).\n\n### Constants\n\n#### `fetchClient`\n\nPre-created fetch client for environments where `fetch` is available.\n`null` in environments without a global `fetch`.\n\n```typescript\nconst fetchClient: HttpClient | null\n```\n\n## Available Providers\n\n| Provider | Package                    |\n| -------- | -------------------------- |\n| Axios    | `@molecule/app-http-axios` |\n\n## Injection Notes\n\n### Requirements\n\nPeer dependencies:\n\n- `@molecule/app-bond` ^1.0.1\n- `@molecule/app-i18n` ^1.0.1\n- `@molecule/app-logger` ^1.0.1\n\n### Runtime Dependencies\n\n- `@molecule/app-bond`\n- `@molecule/app-i18n`\n- `@molecule/app-logger`\n\nMake ALL API calls through this client (via the framework hook `useHttpClient()` in\nReact / the Vue composable) — it carries the configured `baseURL`, auth headers, and\ninterceptors. Do NOT call `fetch()` / `axios` directly in components — that bypasses auth\n\n- base-URL config and breaks when the transport is swapped.\n\nTwo mistakes that break in preview/production (seen in real imported apps):\n\n- **Pass RELATIVE paths; never a hardcoded host.** Use `'/plants'` (the `baseURL` `'/api'`\n  is prepended), NOT `'/api/plants'`, and NEVER an absolute dev URL like\n  `'http://localhost:4000/api/…'`. A hardcoded `localhost`/host works on the author's\n  machine, then fails cross-origin (CORS) in the preview and points at the wrong server in\n  production. The base URL is configured ONCE (via `setClient`), not per call.\n- **The client is PUBLIC — never put a secret in it.** Anything the browser sends (an API\n  key, a service-role / `sk_…` key, a signing secret) is visible to every user. Secrets\n  stay in YOUR API; the browser calls your API and the API uses the secret server-side.\n  Only a publishable/public key may ever be client-side.\n\nAuth (the bearer token / session cookie) is attached by the client's interceptors — do not\nread a token from `localStorage` or hand-attach it (the token is memory-only; see the user\nresource).\n\n## Translations\n\nTranslation strings are provided by `@molecule/app-locales-http`.\n","readmeFilename":"README.md"}