{"_id":"@api-core/client","_rev":"3-c7b88224adb45974be8cf7dfa803e0a4","name":"@api-core/client","dist-tags":{"latest":"0.0.3"},"versions":{"0.0.1":{"name":"@api-core/client","version":"0.0.1","keywords":["api","client","contract","type-safe","typescript","testing"],"author":{"url":"https://github.com/shanmukaanem","name":"Shanmuka Chandra Teja Anem"},"license":"MIT","_id":"@api-core/client@0.0.1","maintainers":[{"name":"shanmukaanem","email":"shanmukaanem@gmail.com"}],"contributors":[{"name":"Shanmuka Chandra Teja Anem"}],"homepage":"https://github.com/QECore/api-core#readme","bugs":{"url":"https://github.com/QECore/api-core/issues"},"dist":{"shasum":"9610766872ca4f9a2d195d5cabcc1dd837e94cb8","tarball":"https://registry.npmjs.org/@api-core/client/-/client-0.0.1.tgz","fileCount":5,"integrity":"sha512-VeL1bAurdlJbuloFcfb8etsn7YCc89bVA7Bjz/mNbAvcPXd606N5MHUVQ9tmNwLWcNhRNBCqU2kjZrALrlhLDg==","signatures":[{"sig":"MEUCIE/ba4X9oo1vTjwlI9O2XLQmKpaIhnmvkNuzw9O5BiQKAiEA9nQ9tR2q3Sba/LM5rgQCDVlvPxKjnqO4L/jbqzb5nwU=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":87695},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","engines":{"node":">=18.0.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"gitHead":"b3afa2689b9fd48a2eb27955ec8ca91e2ebf241f","scripts":{"test":"tsx --test tests/config.test.ts tests/types.test.ts tests/contract.test.ts tests/graphql.test.ts","build":"tsup src/index.ts --format esm --dts --clean","test:types":"tsx --test tests/types.test.ts","test:config":"tsx --test tests/config.test.ts","test:graphql":"tsx --test tests/graphql.test.ts","api-extractor":"npx @microsoft/api-extractor run --local --verbose","test:contract":"tsx --test tests/contract.test.ts","api-extractor:ci":"npx @microsoft/api-extractor run --verbose"},"_npmUser":{"name":"shanmukaanem","email":"shanmukaanem@gmail.com"},"repository":{"url":"git+https://github.com/QECore/api-core.git","type":"git"},"_npmVersion":"10.8.2","description":"Transport-agnostic, type-safe API contract and execution engine core","directories":{},"sideEffects":false,"_nodeVersion":"20.19.0","dependencies":{},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{},"_npmOperationalInternal":{"tmp":"tmp/client_0.0.1_1784829510000_0.42100435657063207","host":"s3://npm-registry-packages-npm-production"}},"0.0.2":{"name":"@api-core/client","version":"0.0.2","keywords":["api","client","contract","type-safe","typescript","testing"],"author":{"url":"https://github.com/shanmukaanem","name":"Shanmuka Chandra Teja Anem"},"license":"MIT","_id":"@api-core/client@0.0.2","maintainers":[{"name":"shanmukaanem","email":"shanmukaanem@gmail.com"}],"contributors":[{"name":"Shanmuka Chandra Teja Anem"}],"homepage":"https://github.com/QECore/api-core#readme","bugs":{"url":"https://github.com/QECore/api-core/issues"},"dist":{"shasum":"7beba8c7c199d87d386065748376365dfe83dc89","tarball":"https://registry.npmjs.org/@api-core/client/-/client-0.0.2.tgz","fileCount":6,"integrity":"sha512-6IPJXqqiB7f7q6eqPbr8+6mAVP3PLw4EKzkjHUgdEu4azEIsy2IaLLQlJKK50DH1DH7hNTSPyuWbr64RWIOSrQ==","signatures":[{"sig":"MEYCIQC5hsDECeZvFTKANm3writ0M+9WUR5w3jdwx7OtnT75mgIhAOY/8OTNSFeIT6EssgM+s5hF6p8xw0Q0OtukIcIas+5R","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":132275},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=18.0.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","default":"./dist/index.js","require":"./dist/index.cjs"}},"gitHead":"03a78282d287a3199d80d9ec7e738663b64fa70e","scripts":{"test":"tsx --test tests/config.test.ts tests/types.test.ts tests/contract.test.ts tests/graphql.test.ts tests/k6-simulation.test.ts","build":"tsup && node ../../packages/build/cleanup.cjs","test:types":"tsx --test tests/types.test.ts","test:config":"tsx --test tests/config.test.ts","test:graphql":"tsx --test tests/graphql.test.ts","api-extractor":"npx @microsoft/api-extractor run --local --verbose","test:contract":"tsx --test tests/contract.test.ts","api-extractor:ci":"npx @microsoft/api-extractor run --verbose"},"_npmUser":{"name":"shanmukaanem","email":"shanmukaanem@gmail.com"},"repository":{"url":"git+https://github.com/QECore/api-core.git","type":"git"},"_npmVersion":"10.8.2","description":"Transport-agnostic, type-safe API contract and execution engine core","directories":{},"sideEffects":false,"_nodeVersion":"20.19.0","dependencies":{},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{},"_npmOperationalInternal":{"tmp":"tmp/client_0.0.2_1786575473356_0.48958119355844976","host":"s3://npm-registry-packages-npm-production"}},"0.0.3":{"name":"@api-core/client","version":"0.0.3","description":"Transport-agnostic, type-safe API contract and execution engine core","type":"module","main":"./dist/index.cjs","module":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs","default":"./dist/index.js"}},"sideEffects":false,"license":"MIT","author":{"name":"Shanmuka Chandra Teja Anem","url":"https://github.com/shanmukaanem"},"contributors":[{"name":"Shanmuka Chandra Teja Anem"}],"homepage":"https://github.com/QECore/api-core#readme","repository":{"type":"git","url":"git+https://github.com/QECore/api-core.git"},"bugs":{"url":"https://github.com/QECore/api-core/issues"},"publishConfig":{"access":"public"},"engines":{"node":">=18.0.0"},"keywords":["api","client","contract","type-safe","typescript","testing"],"scripts":{"build":"tsup && node ../../packages/build/cleanup.cjs","api-extractor":"npx @microsoft/api-extractor run --local --verbose","api-extractor:ci":"npx @microsoft/api-extractor run --verbose","test":"tsx --test tests/registry.test.ts tests/auth.test.ts tests/auth-users.test.ts tests/auth-cache.test.ts tests/config.test.ts tests/retry.test.ts tests/context.test.ts tests/types.test.ts tests/contract.test.ts tests/query.test.ts tests/graphql.test.ts tests/k6-simulation.test.ts tests/cache.test.ts tests/response-body.test.ts tests/lifecycle-retry.test.ts","test:registry":"tsx --test tests/registry.test.ts","test:auth":"tsx --test tests/auth.test.ts tests/auth-users.test.ts tests/auth-cache.test.ts","test:config":"tsx --test tests/config.test.ts","test:retry":"tsx --test tests/retry.test.ts","test:context":"tsx --test tests/context.test.ts","test:types":"tsx --test tests/types.test.ts","test:contract":"tsx --test tests/contract.test.ts","test:query":"tsx --test tests/query.test.ts","test:graphql":"tsx --test tests/graphql.test.ts","test:cache":"tsx --test tests/cache.test.ts"},"dependencies":{},"devDependencies":{},"_id":"@api-core/client@0.0.3","gitHead":"fd4996f8de62b6dbe753efc2a109dc62758adb23","_nodeVersion":"22.23.2","_npmVersion":"10.9.8","dist":{"integrity":"sha512-gvIW7cTy0XIkgjKiUGYY4cM5rOC6GDMTdeRNoGB0DYkhlLU2lgyGWqkgAJYBj2gS5f9Gbz8jFMhKWFY8kkk40g==","shasum":"bb80886b6518e32e32a97f70bf9c4cba0fc06641","tarball":"https://registry.npmjs.org/@api-core/client/-/client-0.0.3.tgz","fileCount":6,"unpackedSize":239031,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCICS9cKxEr63GaIQA3JFT36ByrxjoW5Fj0sdrpSdL96eDAiBU+6BrGB1ssSKUB0Jt5TwAHZ2iXjfg/AfOWqJQ8pHCeg=="}]},"_npmUser":{"name":"shanmukaanem","email":"shanmukaanem@gmail.com"},"directories":{},"maintainers":[{"name":"shanmukaanem","email":"shanmukaanem@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/client_0.0.3_1788465485309_0.7146812325611409"},"_hasShrinkwrap":false}},"time":{"created":"2026-07-23T17:58:29.799Z","modified":"2026-09-03T19:58:05.617Z","0.0.1":"2026-07-23T17:58:30.157Z","0.0.2":"2026-08-12T22:57:53.511Z","0.0.3":"2026-09-03T19:58:05.455Z"},"bugs":{"url":"https://github.com/QECore/api-core/issues"},"author":{"name":"Shanmuka Chandra Teja Anem","url":"https://github.com/shanmukaanem"},"license":"MIT","homepage":"https://github.com/QECore/api-core#readme","keywords":["api","client","contract","type-safe","typescript","testing"],"repository":{"type":"git","url":"git+https://github.com/QECore/api-core.git"},"description":"Transport-agnostic, type-safe API contract and execution engine core","contributors":[{"name":"Shanmuka Chandra Teja Anem"}],"maintainers":[{"name":"shanmukaanem","email":"shanmukaanem@gmail.com"}],"readme":"# @api-core\n\nTransport-agnostic, type-safe API contract and execution engine core for JavaScript/TypeScript.\n\nAPI-Core serves as the core engine to build type-safe HTTP clients for Axios, Playwright, k6, or custom executors. It provides a clean separation of concerns between **declarative configuration**, **behavioral hooks**, and **request-specific definitions**.\n\n## Installation\n\n```bash\nnpm install @api-core\n```\n\n## Quick Start\n\n```ts\nimport { createApi, createClient, createApiRegistry } from '@api-core/client';\n\n// 1. Define your API contract\nconst getUser = createApi({\n  method: 'GET',\n  endpoint: '/users/{id}',\n});\n\n// 2. Register your APIs\nconst registry = createApiRegistry({\n  apis: {\n    getUser,\n  },\n  config: {\n    baseUrl: 'https://api.example.com',\n  },\n});\n\n// 3. Perform type-safe requests\nconst response = await registry.call('getUser', {\n  path: { id: 42 },\n});\n```\n\n## Features\n\n- **Declarative API Contracts**: Define your API surfaces as type-safe schema contracts.\n- **Unified Request Pipeline**: Features a 4-level hierarchical configuration resolution (`Client` -> `Registry` -> `API` -> `Call`).\n- **Flexible Lifecycles**: Support for global and local request hooks (`beforeCall`, `afterCall`, `onError`).\n- **Extensible Architecture**: Core runtime accepts custom transport executors (e.g. Axios, Playwright, k6).\n- **TypeScript First**: Exceptional type inference for parameters, query variables, paths, and response bodies.\n\n---\n\n## API-CORE Architecture Documentation\n\n## Calling APIs\n\nUse the HTTP verb methods as the primary client interface. Each method accepts only\nendpoints declared with its matching HTTP method, while retaining the request and\nresponse types declared by the contract.\n\n```ts\nconst user = await client.get('users.getUser', {\n  path: { id: 42 },\n  query: { includeDetails: true },\n});\n\nconst created = await client.post('users.createUser', {\n  body: { name: 'Ada' },\n});\n```\n\nAvailable methods are `get`, `post`, `put`, `patch`, `delete`, `head`, and\n`options`. `client.call()` remains available for backward compatibility and is the\nlow-level API for dynamic contract execution.\n\nGraphQL operations also use the same client pipeline, including client plugins,\nhooks, authentication, retry, timeout, metadata, and inherited configuration:\n\n```ts\nconst result = await client.graphql<{ user: User }>({\n  query: 'query GetUser($id: ID!) { user(id: $id) { id } }',\n  variables: { id: 42 },\n  operationName: 'GetUser',\n});\n```\n\n---\n\n## Complete Resolution Model\n\nThe following centerpiece diagram illustrates how `config`, `hooks`, `defaults`, and call-time parameters resolve into a single `Resolved Request` consumed by the runtime:\n\n```text\nClient\n│\n├── config\n├── hooks\n│\n▼\nRegistry\n│\n├── config\n├── hooks\n│\n▼\nAPI\n│\n├── defaults\n├── config\n├── hooks\n│\n▼\nCall\n│\n├── config\n├── headers\n├── body\n│\n▼\nResolved Request\n```\n\n---\n\n## 1. Declarative Configuration (`config`)\n\n`config` contains declarative properties that participate in uniform 4-level hierarchical resolution (`Client` -> `Registry` -> `API` -> `Call`).\n\n### Inheritable Configuration Interface (`ApiConfig`)\n```ts\ninterface ApiConfig {\n  baseURL?: string;\n  basePath?: string;\n  headers?: Record<string, string>;\n  cookies?: Record<string, string>;\n  timeout?: number;\n  retry?: RetryConfig;\n  serializer?: string | unknown;\n  tags?: string[];\n  metadata?: Record<string, unknown>;\n  cacheMode?: 'sharedFile' | 'run' | 'none';\n  cachePath?: string;\n  successStatusCodes?: readonly number[];\n  environment?: string;\n}\n```\n\n### Deep Merge Rules\n- **Primitives**: Override\n- **Objects**: Recursive deep merge\n- **Arrays**: Replace (never concatenate)\n- **Special Objects**: Replace immediately without deep merging (`Date`, `Blob`, `File`, `FormData`, `URLSearchParams`, `ArrayBuffer`, `Buffer`, etc.)\n- **URL Resolution & Normalization**: Cleanly concatenates `baseURL` + `basePath` + `endpoint` while stripping duplicate slashes (`/v1/` + `/users` -> `/v1/users`).\n\n---\n\n## 2. Behavioral Hooks (`hooks`)\n\n`hooks` control lifecycle execution behavior:\n\n```ts\ninterface ApiHooks {\n  beforeCall?: BeforeCallCallback | BeforeCallCallback[];\n  afterCall?: AfterCallCallback | AfterCallCallback[];\n  onError?: OnErrorCallback | OnErrorCallback[];\n}\n```\n\n### Execution Order\n- `beforeCall`: Parent -> Child order (`Client.hooks` -> `Registry.hooks` -> `API.hooks` -> `Call.hooks`)\n- `afterCall`: Child -> Parent order (`Call.hooks` -> `API.hooks` -> `Registry.hooks` -> `Client.hooks`)\n- `onError`: Child -> Parent order (`Call.hooks` -> `API.hooks` -> `Registry.hooks` -> `Client.hooks`)\n\n---\n\n## 3. Strongly Typed Retry Configuration (`RetryConfig`)\n\n```ts\ninterface RetryConfig {\n  readonly strategy: 'constant' | 'linear' | 'exponential' | 'custom';\n  readonly attempts: number;\n  readonly delay: number; // in ms\n  readonly maxDelay?: number;\n  readonly backoffFactor?: number;\n  readonly when?: (error: Error, response?: unknown) => boolean;\n}\n```\n\n---\n\n## 4. Response Caching & Replay (`cacheMode`)\n\nAPI-Core supports in-memory and persistent file caching of successful API responses:\n\n- `cacheMode`: Controls caching behavior:\n  - `sharedFile`: Persistent JSON file cache on disk. Enables offline replay without network access.\n  - `run`: Process/run-scoped in-memory cache. Fast execution without touching the filesystem.\n  - `none` *(default)*: No caching. Always executes live network requests.\n- `cachePath`: Controls persistent cache location (defaults to `<process.cwd()>/.api-cache/`).\n- `successStatusCodes`: Controls which live HTTP status codes are eligible for cache capture.\n\n### Cache Identity\nCache identity is determined strictly by:\n```text\nenvironment + method + normalized URL + body\n```\n> Request headers and cookies are intentionally excluded from cache identity.\n\nDifferent request headers or cookies for the same environment, method, endpoint, and body map to the exact same cached response.\n\n### Authentication Credential Caching\n> Authentication credentials are stored only in memory for the lifetime of the authentication context. Independently created `AuthContext` instances maintain isolated credential caches. Cloned/configured contexts may share the existing in-memory credential cache while maintaining independent active-user state. Authentication credentials are never persisted to the shared-file response cache.\n\nNormal API responses can be cached in memory or in the shared-file cache according to `cacheMode`. Authentication credentials are stored only in the dedicated in-memory `AuthContext` cache and are never persisted to the shared-file response cache.\n\n### Normal Response Cache Security & Sanitization\nWhen normal API responses are persisted to the shared-file cache, sensitive request/response headers are sanitized:\n- Headers such as `authorization`, `cookie`, `set-cookie`, `api-key`, `x-api-key`, and `proxy-authorization` are stripped from response cache entries before writing to disk.\n- Authentication credentials themselves are never part of normal response cache entries.\n\n### Precedence\n```text\ncall.config > api.config > registry.config > default\n```\n\n### Cache Success Status Codes (`successStatusCodes`)\n- By default, responses with HTTP status codes `200–299` are captured.\n- Configure `successStatusCodes?: readonly number[]` to specify exact eligible status codes (e.g. `[200, 201, 204]` or `[304]`).\n- When defined, `successStatusCodes` must be a non-empty array of integer HTTP status codes between `0` and `999`.\n- Exact membership determines capture eligibility; unlisted status codes are never captured.\n\n### Examples\n\n#### Registry-Wide Persistent Cache\n```ts\nconst registry = createApiRegistry({\n  apis: { auth, users, tasks },\n  config: {\n    cacheMode: 'sharedFile',\n    cachePath: './.api-cache',\n    successStatusCodes: [200, 201, 204],\n  },\n});\n```\n\n#### API-Specific Cache Mode\n```ts\nconst register = createApi({\n  method: 'POST',\n  endpoint: '/users',\n  config: {\n    cacheMode: 'run',\n    successStatusCodes: [201],\n  },\n});\n```\n\n#### Per-Call Override\n```ts\nawait client.call('auth.register', {\n  body: registrationPayload,\n  config: {\n    cacheMode: 'none',\n  },\n});\n```\n\n### Cache Lifecycle & Invalidation\n`.api-cache` contains captured deterministic test data. Cached responses are not invalidated automatically. When underlying API responses, contracts, authentication policies, or test data change, delete the cache directory (`rm -rf .api-cache`) or run with `cacheMode: 'none'` to refresh captured files.\n\n---\n\n## Architecture Example\n\n```ts\n// 1. Registry Level\nconst registry = createApiRegistry({\n  apis: {\n    users: {\n      getUser,\n    },\n  },\n  config: {\n    basePath: '/users-service/',\n    headers: { region: 'EU' },\n  },\n});\n\n// 2. Client Level\nconst client = createClient({\n  runtime,\n  registry: registry.apis,\n  config: {\n    baseURL: 'https://api.company.com/',\n    basePath: '/v1/',\n    timeout: 30000,\n    retry: {\n      attempts: 3,\n      delay: 1000,\n      strategy: 'exponential',\n    },\n  },\n  hooks: {\n    beforeCall: [\n      (req) => console.log(`[Logger] Request to: ${req.url}`),\n      () => console.log(`[Metrics] Starting timer`),\n    ],\n  },\n});\n\n// 3. API Contract Level\nconst getUser = createApi({\n  method: 'GET',\n  endpoint: '//users/{id}',\n  config: {\n    timeout: 10000,\n  },\n});\n```\n\n## Example\n\nFor a complete and runnable example showing how to set up registries and execute requests with Playwright, please refer to [packages/test/examples/basic.ts](../test/examples/basic.ts).\n\n## API Overview\n\n- `createApi(config)`: Defines a type-safe HTTP contract endpoint.\n- `createApiRegistry(config)`: Groups multiple contracts under a structured namespace registry.\n- `createClient(options)`: Creates a unified client wrapper backed by an HTTP runtime executor.\n- `Runtime`: The internal core executor orchestrator.\n- `FetchExecutor`: Standard transport implementation using global fetch.\n\n## License\n\nMIT © Shanmuka Chandra Teja Anem\n\n## Contributing\n\nContributions are welcome! Please read the root contributing guidelines, open an issue, or submit a pull request.\n\n","readmeFilename":"README.md"}