{"_id":"@civitas-cerebrum/wasapi","_rev":"4-3313de88fdda9a0bbf61f995bafdbae0","name":"@civitas-cerebrum/wasapi","dist-tags":{"latest":"0.0.4"},"versions":{"0.0.1":{"name":"@civitas-cerebrum/wasapi","version":"0.0.1","author":{"name":"Umut Ay Bora"},"license":"MIT","_id":"@civitas-cerebrum/wasapi@0.0.1","maintainers":[{"name":"umutayb","email":"umutaybora@gmail.com"}],"homepage":"https://github.com/civitas-cerebrum/wasapi#readme","bugs":{"url":"https://github.com/civitas-cerebrum/wasapi/issues"},"dist":{"shasum":"b7b62b4c03e8d3acf836cca1864786a45946e646","tarball":"https://registry.npmjs.org/@civitas-cerebrum/wasapi/-/wasapi-0.0.1.tgz","fileCount":23,"integrity":"sha512-ZJTJ7PksVEvvcRHwbKUSSBcuvJplNlflOe9FhWYJtEckW0wdS/4XCA3Ng+9x23/GVeVljicapaZ7Tu9bOa6MvQ==","signatures":[{"sig":"MEYCIQCxTk/jH6obgdNWQSkcP6yheydZht7jlBgfBleXJE8nygIhAMwnXYXWgvmF1q9NKslmHgCuuNrIm8Q1pkZKxezHlpEr","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@civitas-cerebrum%2fwasapi@0.0.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":49982},"main":"dist/index.js","types":"dist/index.d.ts","gitHead":"40f759053039bbea91ac33ff902a61812d4016df","scripts":{"test":"npx tsx tests/book-hive.test.ts","build":"npm run clean && tsc","clean":"rm -rf dist","test:coverage":"npx test-coverage","prepublishOnly":"npm run build"},"_npmUser":{"name":"umutayb","email":"umutaybora@gmail.com"},"repository":{"url":"git+https://github.com/civitas-cerebrum/wasapi.git","type":"git"},"_npmVersion":"11.11.0","description":"A lightweight REST API client library with fluent builder, typed responses, and decorator-based API definitions.","directories":{},"_nodeVersion":"24.14.1","dependencies":{"debug":"^4.4.3","@civitas-cerebrum/context-store":"^0.0.2","@civitas-cerebrum/test-coverage":"^0.0.9"},"publishConfig":{"access":"public","provenance":true},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.21.0","typescript":"^5.0.0","@types/node":"^20.0.0","@types/debug":"^4.1.13"},"_npmOperationalInternal":{"tmp":"tmp/wasapi_0.0.1_1775475002732_0.9598457413894081","host":"s3://npm-registry-packages-npm-production"}},"0.0.2":{"name":"@civitas-cerebrum/wasapi","version":"0.0.2","author":{"name":"Umut Ay Bora"},"license":"MIT","_id":"@civitas-cerebrum/wasapi@0.0.2","maintainers":[{"name":"umutayb","email":"umutaybora@gmail.com"}],"homepage":"https://github.com/civitas-cerebrum/wasapi#readme","bugs":{"url":"https://github.com/civitas-cerebrum/wasapi/issues"},"dist":{"shasum":"aea108243f56a6c8165b07f4c0bd7d07f04b2000","tarball":"https://registry.npmjs.org/@civitas-cerebrum/wasapi/-/wasapi-0.0.2.tgz","fileCount":23,"integrity":"sha512-fZeeG3lctkPT+IJ0M6dg4rZ3X0XVq41datDrbWyHeNHOhmhAjt3zb0NvmgdOjQXPbq0Hrzw5NTB8X+MnPfDacQ==","signatures":[{"sig":"MEQCIDV8zEtHXkk73v0pxRlMKbeG6rAO254gp+qpWfugOpMAAiBnq0jZW/gaxSpE83wvq+U+LWJ6nnzYknQBMBts3l8JtQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@civitas-cerebrum%2fwasapi@0.0.2","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":49982},"main":"dist/index.js","types":"dist/index.d.ts","gitHead":"d0819b22c37c39050712aa67ca50fbc513e8884b","scripts":{"test":"npx tsx tests/book-hive.test.ts","build":"npm run clean && tsc","clean":"rm -rf dist","test:coverage":"npx test-coverage","prepublishOnly":"npm run build"},"_npmUser":{"name":"umutayb","email":"umutaybora@gmail.com"},"repository":{"url":"git+https://github.com/civitas-cerebrum/wasapi.git","type":"git"},"_npmVersion":"11.11.0","description":"A lightweight REST API client library with fluent builder, typed responses, and decorator-based API definitions.","directories":{},"_nodeVersion":"24.14.1","dependencies":{"debug":"^4.4.3","@civitas-cerebrum/context-store":"^0.0.2","@civitas-cerebrum/test-coverage":"^0.0.9"},"publishConfig":{"access":"public","provenance":true},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.21.0","typescript":"^5.0.0","@types/node":"^20.0.0","@types/debug":"^4.1.13"},"_npmOperationalInternal":{"tmp":"tmp/wasapi_0.0.2_1775479400439_0.26479344315868225","host":"s3://npm-registry-packages-npm-production"}},"0.0.3":{"name":"@civitas-cerebrum/wasapi","version":"0.0.3","author":{"name":"Umut Ay Bora"},"license":"MIT","_id":"@civitas-cerebrum/wasapi@0.0.3","maintainers":[{"name":"umutayb","email":"umutaybora@gmail.com"}],"homepage":"https://github.com/civitas-cerebrum/wasapi#readme","bugs":{"url":"https://github.com/civitas-cerebrum/wasapi/issues"},"dist":{"shasum":"ef67c1e25f8ed5cf13b8e44a6b48382f17e79a9a","tarball":"https://registry.npmjs.org/@civitas-cerebrum/wasapi/-/wasapi-0.0.3.tgz","fileCount":23,"integrity":"sha512-bYLpUNCVgGKlU92ZkRgPKt9nIqAWHeB0obyBoIPMD/r/6F+7iDGM5okr3iyoGN5/O890lQsRJ8n2u4GLCQhTLA==","signatures":[{"sig":"MEUCIQCD2EK/MknF4jIp3Mq2bJkUtPGkbTr3RD8Ajz/QCeduXgIgKVdm4bjuEECt68n5YYodR8nKdqpILwbn4sl/1OsB3kw=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@civitas-cerebrum%2fwasapi@0.0.3","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":50954},"main":"dist/index.js","types":"dist/index.d.ts","gitHead":"2e258937193f4765beef04c2eb4f873c37055cee","scripts":{"test":"npx tsx tests/book-hive.test.ts","build":"npm run clean && tsc","clean":"rm -rf dist","test:coverage":"npx test-coverage","prepublishOnly":"npm run build"},"_npmUser":{"name":"umutayb","email":"umutaybora@gmail.com"},"repository":{"url":"git+https://github.com/civitas-cerebrum/wasapi.git","type":"git"},"_npmVersion":"11.11.0","description":"A lightweight REST API client library with fluent builder, typed responses, and decorator-based API definitions.","directories":{},"_nodeVersion":"24.14.1","dependencies":{"debug":"^4.4.3","@civitas-cerebrum/context-store":"^0.0.2","@civitas-cerebrum/test-coverage":"^0.0.9"},"publishConfig":{"access":"public","provenance":true},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.21.0","typescript":"^5.0.0","@types/node":"^20.0.0","@types/debug":"^4.1.13"},"_npmOperationalInternal":{"tmp":"tmp/wasapi_0.0.3_1775480604983_0.916345017780055","host":"s3://npm-registry-packages-npm-production"}},"0.0.4":{"name":"@civitas-cerebrum/wasapi","version":"0.0.4","description":"A lightweight REST API client library with fluent builder, typed responses, and decorator-based API definitions.","main":"dist/index.js","types":"dist/index.d.ts","scripts":{"clean":"rm -rf dist","build":"npm run clean && tsc","test":"npx tsx tests/book-hive.test.ts","test:coverage":"npx test-coverage","postinstall":"node scripts/postinstall.js","prepublishOnly":"npm run build"},"dependencies":{"@civitas-cerebrum/context-store":"^0.0.2","@civitas-cerebrum/test-coverage":"^0.0.9","debug":"^4.4.3"},"devDependencies":{"@types/debug":"^4.1.13","@types/node":"^20.0.0","tsx":"^4.21.0","typescript":"^5.0.0"},"publishConfig":{"access":"public","provenance":true},"repository":{"type":"git","url":"git+https://github.com/civitas-cerebrum/wasapi.git"},"author":{"name":"Umut Ay Bora"},"license":"MIT","gitHead":"15e1cd3673c8e535ca9e304b8dae1d30435affdd","_id":"@civitas-cerebrum/wasapi@0.0.4","bugs":{"url":"https://github.com/civitas-cerebrum/wasapi/issues"},"homepage":"https://github.com/civitas-cerebrum/wasapi#readme","_nodeVersion":"24.14.1","_npmVersion":"11.11.0","dist":{"integrity":"sha512-BOoDaSrdrlsmsKNoukXWRlR/xkes/vtdG1C+oto6m58TYCRoe42eQGW4sfVUEtrGndDpqhz4BHcuDyqsvW0PnQ==","shasum":"ce409271b4f68c3a4f5062581912daf006240a9e","tarball":"https://registry.npmjs.org/@civitas-cerebrum/wasapi/-/wasapi-0.0.4.tgz","fileCount":25,"unpackedSize":73202,"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@civitas-cerebrum%2fwasapi@0.0.4","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQCMKDTkTkro3YEzcGb4BF1zJI0AhN070T5U8kP6eJYjcwIhAOfcfPV/CWuT8Qq1XnhDuv4VMvUs8+Gozhm57lutdHrt"}]},"_npmUser":{"name":"umutayb","email":"umutaybora@gmail.com"},"directories":{},"maintainers":[{"name":"umutayb","email":"umutaybora@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/wasapi_0.0.4_1778236865017_0.39512803383316353"},"_hasShrinkwrap":false}},"time":{"created":"2026-04-06T11:30:02.605Z","modified":"2026-05-08T10:41:05.605Z","0.0.1":"2026-04-06T11:30:02.882Z","0.0.2":"2026-04-06T12:43:20.606Z","0.0.3":"2026-04-06T13:03:25.156Z","0.0.4":"2026-05-08T10:41:05.202Z"},"bugs":{"url":"https://github.com/civitas-cerebrum/wasapi/issues"},"author":{"name":"Umut Ay Bora"},"license":"MIT","homepage":"https://github.com/civitas-cerebrum/wasapi#readme","repository":{"type":"git","url":"git+https://github.com/civitas-cerebrum/wasapi.git"},"description":"A lightweight REST API client library with fluent builder, typed responses, and decorator-based API definitions.","maintainers":[{"name":"umutayb","email":"umutaybora@gmail.com"}],"readme":"# Wasapi 🌶️\n\n[![npm](https://img.shields.io/npm/v/@civitas-cerebrum/wasapi?color=brightgreen&label=wasapi)](https://www.npmjs.com/package/@civitas-cerebrum/wasapi)\n\n**Wasapi** is a lightweight TypeScript REST API client library that simplifies HTTP service generation using decorator-based API definitions, a fluent builder, typed responses, and smart polling utilities.\n\nThe TypeScript counterpart of [wasapi for Java](https://github.com/Umutayb/wasapi) — same design philosophy, native TypeScript experience.\n\n## Features\n\n- **Decorator-based API definitions** — `@GET`, `@POST`, `@PUT`, `@DELETE`, `@PATCH`, `@HTTP`\n- **Fluent builder** — configure base URL, headers, timeouts, proxy, logging\n- **Typed responses** — `ApiCall<T>`, `ApiResponse<T>`, `ResponsePair<R, E>`\n- **Strict / lenient modes** — throw on failure or return null\n- **Response polling** — `monitorResponseCode()`, `monitorFieldValue()`\n- **Zero HTTP dependencies** — uses native `fetch` (Node 18+)\n- **TC39 Stage 3 decorators** — no `experimentalDecorators`, no `reflect-metadata`\n\n## Installation\n\n```bash\nnpm install @civitas-cerebrum/wasapi\n```\n\n## Quick Start\n\n### 1. Define your API with decorators\n\n```typescript\nimport { GET, POST, DELETE, ApiCall } from '@civitas-cerebrum/wasapi';\n\ninterface User {\n  id: string;\n  name: string;\n  email: string;\n}\n\nclass UserApi {\n  @GET('/users')\n  getUsers(): ApiCall<User[]> { return null!; }\n\n  @GET('/users/:id')\n  getUser(pathParams: { id: string }): ApiCall<User> { return null!; }\n\n  @POST('/users')\n  createUser(body: { name: string; email: string }): ApiCall<User> { return null!; }\n\n  @DELETE('/users/:id')\n  deleteUser(pathParams: { id: string }): ApiCall<void> { return null!; }\n}\n```\n\n### 2. Build the client\n\n```typescript\nimport { WasapiClient } from '@civitas-cerebrum/wasapi';\n\nconst api = new WasapiClient.Builder()\n  .setBaseUrl('https://api.example.com')\n  .setHeaders({ Authorization: 'Bearer token' })\n  .setLogHeaders(true)\n  .build(UserApi);\n```\n\n### 3. Execute requests\n\n```typescript\n// Lenient mode (default) — returns null on failure\nconst users = await api.getUsers().perform();\n\n// Strict mode — throws FailedCallException on non-2xx\nconst users = await api.getUsers().perform(true);\n\n// Strict + log response body\nconst users = await api.getUsers().perform(true, true);\n\n// Full response wrapper\nconst response = await api.getUser({ id: '5' }).getResponse();\nconsole.log(response.status);    // 200\nconsole.log(response.body);      // User object\nconsole.log(response.headers);   // Record<string, string>\n\n// Typed error handling\nconst pair = await api.getUser({ id: 'bad' }).getResponsePair(ErrorModel);\nif (pair.isError()) {\n  console.log(pair.errorBody);   // ErrorModel instance\n}\n```\n\n## API Reference\n\n### Decorators\n\n| Decorator | Description | Method args |\n|-----------|-------------|-------------|\n| `@GET(path)` | HTTP GET | `(pathParams?, queryParams?, options?)` |\n| `@POST(path)` | HTTP POST | `(body?, pathParams?, queryParams?, options?)` |\n| `@PUT(path)` | HTTP PUT | `(body?, pathParams?, queryParams?, options?)` |\n| `@PATCH(path)` | HTTP PATCH | `(body?, pathParams?, queryParams?, options?)` |\n| `@DELETE(path)` | HTTP DELETE | `(pathParams?, queryParams?, options?)` |\n| `@HTTP(method, path, hasBody?)` | Custom method | positional based on `hasBody` |\n\n**Path parameters** use `:param` syntax — e.g., `/users/:id` is substituted from `pathParams: { id: '5' }`.\n\n**Query parameters** are appended as `?key=value` from `queryParams: Record<string, string>`.\n\n### `@HTTP` — Custom HTTP Methods\n\nFor unconventional methods like `PURGE`, `COPY`, or `LOCK`:\n\n```typescript\nimport { HTTP, ApiCall } from '@civitas-cerebrum/wasapi';\n\nclass CacheApi {\n  @HTTP('PURGE', '/cache/:key')\n  purge(pathParams: { key: string }): ApiCall<void> { return null!; }\n\n  @HTTP('REPORT', '/analytics', true)  // hasBody = true\n  report(body: ReportRequest): ApiCall<ReportResult> { return null!; }\n}\n```\n\n### `WasapiClient.Builder`\n\n| Method | Description | Default |\n|--------|-------------|---------|\n| `setBaseUrl(url)` | Base URL for all requests | *required* |\n| `setHeaders(headers)` | Default headers (merged per-request) | `{}` |\n| `setTimeout(seconds)` | Request timeout in seconds | `60` |\n| `setLogHeaders(bool)` | Log request headers | `true` |\n| `setLogRequestBody(bool)` | Log request body | `false` |\n| `setDetailedLogging(bool)` | Log response body | `false` |\n| `setFollowRedirects(bool)` | Follow HTTP redirects | `false` |\n| `build(ApiClass)` | Build typed API proxy | — |\n\nPass a `ContextStore` instance to the constructor to read defaults from configuration:\n\n```typescript\nconst store = new ContextStore();\nstore.put('wasapi.baseUrl', 'https://api.example.com');\nstore.put('wasapi.timeout', 30);\n\nconst api = new WasapiClient.Builder(store).build(MyApi);\n```\n\n### `ApiCall<T>`\n\nEvery decorated method returns an `ApiCall<T>` — a lazy request descriptor that doesn't execute until you call one of its methods:\n\n| Method | Returns | Description |\n|--------|---------|-------------|\n| `perform(strict?, printBody?, ...errorModels)` | `Promise<T \\| null>` | Execute and return body. Strict throws on failure. |\n| `getResponse(strict?, printBody?)` | `Promise<ApiResponse<T>>` | Full response wrapper with status, headers, body. |\n| `getResponsePair(ErrorClass)` | `Promise<ResponsePair<ApiResponse<T>, E>>` | Response + typed error body. |\n| `monitorResponseCode(code, timeout, interval?)` | `Promise<ApiResponse<T>>` | Poll until HTTP status matches. |\n| `monitorFieldValue(field, value, timeout, interval?)` | `Promise<T>` | Poll until a response body field matches. |\n| `clone()` | `ApiCall<T>` | Independent copy for retry/polling. |\n\n### `ApiResponse<T>`\n\n| Property / Method | Type | Description |\n|-------------------|------|-------------|\n| `status` | `number` | HTTP status code |\n| `statusText` | `string` | HTTP status text |\n| `headers` | `Record<string, string>` | Response headers |\n| `ok` | `boolean` | True if status 200-299 |\n| `body` | `T \\| null` | Parsed JSON body |\n| `rawBody` | `string` | Raw response text |\n| `isSuccessful()` | `boolean` | Same as `ok` |\n| `errorBody(ErrorClass?)` | `E \\| null` | Deserialize error body |\n\n### `ResponsePair<R, E>`\n\n| Property / Method | Type | Description |\n|-------------------|------|-------------|\n| `response` | `R` | The API response |\n| `errorBody` | `E \\| null` | Typed error body (null on success) |\n| `isError()` | `boolean` | True if errorBody is not null |\n\n### Exceptions\n\n| Class | Description |\n|-------|-------------|\n| `FailedCallException` | Thrown in strict mode on non-2xx. Has `statusCode`, `responseBody`, `url`. |\n| `WasapiException` | General library error (timeout, missing config, etc.) |\n\n## Logging\n\nUses the `debug` package with `wasapi:*` namespace. Enabled by default.\n\n```bash\n# Suppress all wasapi logs\nWASAPI_DEBUG=false npx tsx tests/my-test.ts\n\n# Show only request logs\nDEBUG=wasapi:request npx tsx tests/my-test.ts\n```\n\n## Comparison with Java Wasapi\n\n| Java (Retrofit) | TypeScript (this package) |\n|-----------------|--------------------------|\n| `@GET` / `@POST` annotations on interface | `@GET` / `@POST` decorators on class methods |\n| `retrofit.create(Service.class)` | `builder.build(ServiceClass)` — returns Proxy |\n| `Call<T>` | `ApiCall<T>` |\n| `Response<T>` | `ApiResponse<T>` |\n| `ResponsePair<R, E>` | `ResponsePair<R, E>` |\n| `Caller.perform(call, strict, printBody)` | `apiCall.perform(strict, printBody)` |\n| `WasapiUtilities.monitorResponseCode()` | `apiCall.monitorResponseCode()` |\n| Extend `WasapiUtilities` | No inheritance needed — all on `ApiCall<T>` |\n\n## Important Notes\n\n**Argument order matters.** Body-bearing methods (`@POST`, `@PUT`, `@PATCH`) take `(body, pathParams?, queryParams?, options?)`. Non-body methods (`@GET`, `@DELETE`) take `(pathParams?, queryParams?, options?)`. TypeScript enforces this at compile time, but be careful when constructing calls dynamically.\n\n**`perform()` returns `null` in lenient mode** for both empty successful responses (e.g., 204) and failed requests. If you need to distinguish these cases, use `getResponse()` which gives you the full `ApiResponse<T>` with status code.\n\n**Response body is a plain JSON object**, not a class instance. `ApiCall<User>.perform()` returns a plain object shaped as `User`, not an instance of `User` with methods. This is standard TypeScript REST client behavior (same as axios, ky, etc.).\n\n**Timeout units:** Builder's `setTimeout()` is in **seconds**. Polling methods (`monitorResponseCode`, `monitorFieldValue`) take **milliseconds** for timeout and interval.\n\n**FormData via options:** To send multipart requests through the decorator path, pass `formData` in the options parameter:\n```typescript\nconst form = WasapiClient.getMultipartFromFile('./photo.jpg', 'avatar');\nawait api.uploadAvatar(undefined, undefined, { formData: form }).perform(true);\n```\n\n## License\n\nMIT\n","readmeFilename":"README.md"}