{"_id":"@aipt/api-contract","_rev":"3-d562dbd95fd5421022e6be01ce5884a5","name":"@aipt/api-contract","dist-tags":{"latest":"0.2.4"},"versions":{"0.2.2":{"name":"@aipt/api-contract","version":"0.2.2","_id":"@aipt/api-contract@0.2.2","maintainers":[{"name":"dyytojerry","email":"zhangyunfeng.jerry@gmail.com"}],"dist":{"shasum":"3d8a81a80c2803560fa8d2c19f3a41265d10320e","tarball":"https://registry.npmjs.org/@aipt/api-contract/-/api-contract-0.2.2.tgz","fileCount":10,"integrity":"sha512-a5oebCDWeLLunJ/oO6wsxBuGeIwY4RH4qV9JgeuQwHBg5RYUddavIPE/Tldx8m8hVcce8AVrWCUJzbdelqx8IA==","signatures":[{"sig":"MEQCIQCqgTz3liEAts8FN5o27hsgtlbstjkB+ObH2mlFXNRxnAIfVH6RqejebeDiixniXTmG1zmSgCmCatGBUiYy+Pw/Mw==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":24863},"type":"module","engines":{"node":">=24.18.0 <25"},"exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"}},"scripts":{"test":"vitest run","build":"tsc -p tsconfig.build.json","typecheck":"tsc --noEmit -p tsconfig.json"},"_npmUser":{"name":"dyytojerry","email":"zhangyunfeng.jerry@gmail.com"},"description":"与传输无关的 API 请求、响应和错误合同。","directories":{},"_nodeVersion":"24.18.0","dependencies":{"@standard-schema/spec":"1.1.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"zod":"4.4.3","vitest":"4.1.10","typescript":"6.0.3","@types/node":"24.10.13","@aipt/testkit":"0.1.0"},"_npmOperationalInternal":{"tmp":"tmp/api-contract_0.2.2_1788778334946_0.9429698218378493","host":"s3://npm-registry-packages-npm-production"}},"0.2.3":{"name":"@aipt/api-contract","version":"0.2.3","_id":"@aipt/api-contract@0.2.3","maintainers":[{"name":"dyytojerry","email":"zhangyunfeng.jerry@gmail.com"}],"dist":{"shasum":"5feb1838577c486bf3f665eb9994649415f783a0","tarball":"https://registry.npmjs.org/@aipt/api-contract/-/api-contract-0.2.3.tgz","fileCount":10,"integrity":"sha512-QHUv4XmigH1oliO8Z+4Mgvd8vhM06p/kqYep9DL9s3G+t0HT7ymUwN8e7A3KgPNthPno76rYRxp1JzhwNgYYAQ==","signatures":[{"sig":"MEUCIQCdQ7OVaCLcvsh5i1RisFtbrGGHRf2zgZtiLDaqcqzELwIgH2J90WoMu5+KE3XfKQ+kuZfhNCcoWrvhzGx0jFTqhCA=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":24863},"type":"module","engines":{"node":">=24.18.0 <25"},"exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"}},"scripts":{"test":"vitest run","build":"tsc -p tsconfig.build.json","typecheck":"tsc --noEmit -p tsconfig.json"},"_npmUser":{"name":"dyytojerry","email":"zhangyunfeng.jerry@gmail.com"},"description":"与传输无关的 API 请求、响应和错误合同。","directories":{},"_nodeVersion":"24.18.0","dependencies":{"@standard-schema/spec":"1.1.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"zod":"4.4.3","vitest":"4.1.10","typescript":"6.0.3","@types/node":"24.10.13","@aipt/testkit":"0.1.0"},"_npmOperationalInternal":{"tmp":"tmp/api-contract_0.2.3_1788778552007_0.43485524435192113","host":"s3://npm-registry-packages-npm-production"}},"0.2.4":{"dependencies":{"@standard-schema/spec":"1.1.0"},"description":"与传输无关的 API 请求、响应和错误合同。","devDependencies":{"@aipt/testkit":"0.1.2","@types/node":"24.10.13","typescript":"6.0.3","vitest":"4.1.10","zod":"4.4.3"},"engines":{"node":">=24.18.0 <25"},"exports":{".":{"default":"./dist/index.js","types":"./dist/index.d.ts"}},"name":"@aipt/api-contract","publishConfig":{"access":"restricted"},"scripts":{"build":"tsc -p tsconfig.build.json","test":"vitest run","typecheck":"tsc --noEmit -p tsconfig.json"},"type":"module","version":"0.2.4","_nodeVersion":"24.18.0","_id":"@aipt/api-contract@0.2.4","dist":{"integrity":"sha512-UJHlkyJDad0EpV5Gin1yoRix3ajh6IDO+7wQpB8+3/lzMuKz/ZsTzhAU3lLHmQGsrqHQXlVdzTQXn3F76ekGVg==","shasum":"9c242c93254285459189a08ebda49ec7da8cba9d","tarball":"https://registry.npmjs.org/@aipt/api-contract/-/api-contract-0.2.4.tgz","fileCount":6,"unpackedSize":21750,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIBIikNFCgwI8F7ui+3UWj118/25aP4bCzBR30nEg5h6DAiEAohW18itVjaEAeNH/81OgCc+GzSn8nInhW1YdgAeCScw="}]},"_npmUser":{"name":"dyytojerry","email":"zhangyunfeng.jerry@gmail.com"},"directories":{},"maintainers":[{"name":"dyytojerry","email":"zhangyunfeng.jerry@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/api-contract_0.2.4_1788830056102_0.6217226549559924"},"_hasShrinkwrap":false}},"time":{"created":"2026-09-07T10:52:14.764Z","modified":"2026-09-08T01:14:16.427Z","0.2.2":"2026-09-07T10:52:15.090Z","0.2.3":"2026-09-07T10:55:52.144Z","0.2.4":"2026-09-08T01:14:16.247Z"},"description":"与传输无关的 API 请求、响应和错误合同。","maintainers":[{"name":"dyytojerry","email":"zhangyunfeng.jerry@gmail.com"}],"readme":"# API Contract\n\n`@aipt/api-contract@0.2.4` 让一份端点定义同时产出**类型、运行时校验和 mock**。契约声明一次，客户端调用、服务端处理和开发期 mock 共用同一个 schema，三者无法互相漂移。\n\n本包不依赖任何具体校验器。它消费 [Standard Schema](https://standardschema.dev) 规范，因此 zod、valibot、arktype 都可直接作为 schema 传入；平台不锁定其中任何一个。本包也不含 React、HTTP 框架、Provider SDK 或产品领域类型。\n\n## 安装与导入\n\n```bash\npnpm add @aipt/api-contract@0.2.4\n```\n\n```ts\nimport { createApiClient, createRouteHandler, defineEndpoint } from \"@aipt/api-contract\";\nimport { fetchTransport } from \"@aipt/api-contract/fetch\";\n```\n\n## 最小用法\n\n```ts\nimport { z } from \"zod\";\n\nconst readStation = defineEndpoint({\n  operationId: \"weather.readStation\",\n  method: \"GET\",\n  path: \"/stations/:stationId/latest\",\n  params: z.object({ stationId: z.string().min(1) }),\n  response: z.object({ reading: z.object({ celsius: z.number() }) }),\n});\n\n// 客户端：返回判别联合，不抛业务异常\nconst client = createApiClient({ baseUrl: \"https://api.example\", endpoints: { readStation }, transport: fetchTransport(), timeoutMs: 10_000 });\nconst result = await client.readStation({ params: { stationId: \"PEK001\" } });\nif (result.ok) console.log(result.data.reading.celsius); // number\nelse console.log(result.error.code);\n\n// 服务端：同一个定义，输入自动校验，输出必须符合契约\nconst handler = createRouteHandler(readStation, ({ params }) => ({ reading: { celsius: latestOf(params.stationId) } }));\n```\n\n开发期把 `transport` 换成 mock，其余代码一行不改：\n\n```ts\nimport { createMockTransport } from \"@aipt/api-contract\";\n\nconst transport = createMockTransport({\n  endpoints: { readStation },\n  handlers: { readStation: () => ({ reading: { celsius: 21.5 } }) },\n});\n```\n\nmock 返回值同样经过端点的 `response` schema 校验，**mock 数据无法偏离契约**。\n\n## 错误与边界\n\n- `API_ENDPOINT_INVALID`：端点定义非法（非法 operationId、路径穿越、GET 携带 body、路径参数与 `params` schema 不匹配）。装配期即失败。\n- `API_INPUT_INVALID`：`params`/`query`/`body` 不符合 schema。客户端不发出请求；服务端回落为 fail 信封。\n- `API_RESPONSE_INVALID`：响应载荷不符合 `response` schema。**服务端漂移在此 fail closed，业务拿不到半截数据。**\n- `API_ENVELOPE_INVALID`：响应不是合法信封。\n- `API_TRANSPORT_FAILED`：传输层失败、取消或超时。\n- `API_TIMEOUT_INVALID`：deadline 不是正数。\n- `API_MOCK_UNHANDLED`：mock 未登记该端点，不静默返回空数据。\n\n其他边界：\n\n- 线格式为 `{ ok: true, ...载荷 }` / `{ ok: false, error: { code, message } }`，与既有服务端一致。\n- 失败信封是**数据**（`result.ok === false`），只有契约违例和传输故障才抛异常。\n- 服务端输出非法时**抛出而非回落**——那是服务端缺陷，绝不把不符合契约的数据发出去。\n- 每次调用必须有正数 deadline，可与调用方 `AbortSignal` 组合。\n- 中间件为洋葱模型，失败用普通 `try/catch` 观察，不需要独立错误通道。\n- 不记录请求体、响应体、Header 或 Secret。\n\n## 与服务端状态层的关系\n\n本包只负责契约与传输，不做缓存、去重、重试或失效。客户端产出的 `(input) => Promise<ApiResult<T>>` 可直接作为 TanStack Query 的 `queryFn`，两者组合而非替代。\n\n## 退出方式\n\n端点定义是纯数据，schema 由调用方拥有。替换 `createApiClient` 与 `createRouteHandler` 后删除依赖即可；本包不持有持久状态，也不拥有任何已发出的请求。\n","readmeFilename":""}