@ipetsadmin/api-client 1.0.0 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -19,6 +19,34 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
19
19
 
20
20
  ### Security
21
21
 
22
+ ## [1.1.1] - 2026-04-25
23
+
24
+ ### Added
25
+
26
+ - **`HttpPatch`** and **`HttpClient.patch`** for JSON bodies (Axios-compatible), used by email verification.
27
+ - **`patchVerifyEmail`** — `PATCH /api/v1/auth/verify-email` (`VerifyEmailRequest` from `@ipetsadmin/contracts`). Exposed as **`api.auth.verifyEmail`** and as a named export.
28
+ - **`fetchHealthCheck`** (`src/endpoints/health-check.ts`) — `GET /api/v1/health-check` returning **`IApiResponse<HealthCheck>`**.
29
+ - **`createApiClient`** exposes **`healthCheck()`** delegating to **`fetchHealthCheck`**.
30
+
31
+ ### Changed
32
+
33
+ - **`HttpClient`** now requires **`patch`** in addition to **`get`** and **`post`** (breaking for custom HTTP adapters that only implemented **`get`** / **`post`**).
34
+ - **Dev dependency** on **`@ipetsadmin/contracts`** raised to **1.1.7** for typings (`VerifyEmailRequest`, etc.). **Peer dependency** remains **`>= 1.1.1`**; use a contracts version that includes **`VerifyEmailRequest`** if you call **`patchVerifyEmail`**.
35
+
36
+ ## [1.1.0] - 2026-04-04
37
+
38
+ ### Added
39
+
40
+ - **`HttpClient.post`** and **`HttpPost`** type for JSON bodies (Axios-compatible).
41
+ - **Auth endpoints** (`src/endpoints/auth.ts`): `postRegister`, `postLogin`, `getGoogleOAuthStart`, `postGoogleOAuthCallback`, `postRefresh`, `postLogout`, `getMe`.
42
+ - **`createApiClient`** exposes **`auth`** namespace delegating to the auth endpoints.
43
+ - **Peer dependency** on `@ipetsadmin/contracts` **≥ 1.1.1** (align with service ports and Auth0 helper types on the API).
44
+ - README updated with endpoint table and OAuth flow notes.
45
+
46
+ ### Changed
47
+
48
+ - **`createApiClient`** now requires an `http` implementation that provides both **`get`** and **`post`** (breaking for consumers that only implemented `get`).
49
+
22
50
  ## [1.0.0] - 2026-04-02
23
51
 
24
52
  ### Added
package/README.md CHANGED
@@ -1,11 +1,11 @@
1
1
  # `@ipetsadmin/api-client`
2
2
 
3
- Typed HTTP client for the Truffa API. This package is meant to be consumed by front-end and mobile apps. It does **not** own API URL configuration: you pass in a preconfigured HTTP implementation (typically an [Axios](https://axios-http.com/) instance with `baseURL` already set). Request and response **shapes** come from [`@ipetsadmin/contracts`](https://www.npmjs.com/package/@ipetsadmin/contracts) (or domain-specific types you align with that package).
3
+ Typed HTTP client for the Truffa API. Consumed by web and mobile apps. **No baked-in base URL:** pass a preconfigured HTTP layer (typically an [Axios](https://axios-http.com/) instance with `baseURL` set). Request and response shapes come from [`@ipetsadmin/contracts`](https://www.npmjs.com/package/@ipetsadmin/contracts).
4
4
 
5
5
  ## Requirements
6
6
 
7
7
  - **Node.js** ≥ 18.18
8
- - **Peer dependency:** `@ipetsadmin/contracts` (install it next to this package in your app)
8
+ - **Peer dependency:** `@ipetsadmin/contracts` **≥ 1.1.1** (auth types, `VerifyEmailRequest`, and `IApiResponse` envelopes). Use a contracts version that includes any DTO you call (e.g. `VerifyEmailRequest` for **`verifyEmail`**).
9
9
 
10
10
  ## Installation
11
11
 
@@ -15,338 +15,98 @@ pnpm add @ipetsadmin/api-client @ipetsadmin/contracts
15
15
  npm install @ipetsadmin/api-client @ipetsadmin/contracts
16
16
  ```
17
17
 
18
- ## Current implementation
18
+ ## What ships today
19
19
 
20
- ### What ships today
20
+ | Export | Description |
21
+ | -------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
22
+ | `createApiClient(options)` | Factory returning **`healthCheck`** and **`auth`** namespace |
23
+ | `ApiClient` / `CreateApiClientOptions` | Typed client and `{ http: HttpClient }` options |
24
+ | `HttpClient` | **`get`**, **`post`**, and **`patch`** (minimal surface compatible with Axios) |
25
+ | `HttpGet` / `HttpPost` / `HttpPatch` | Method signatures |
26
+ | Named helpers | Same calls as `client.*` — see [`src/endpoints/auth.ts`](./src/endpoints/auth.ts) and [`src/endpoints/health-check.ts`](./src/endpoints/health-check.ts) |
21
27
 
22
- | Export | Description |
23
- | -------------------------- | ----------------------------------------------------------------------- |
24
- | `createApiClient(options)` | Factory that returns an object of API methods. |
25
- | `ApiClient` | Type of the object returned by `createApiClient`. |
26
- | `CreateApiClientOptions` | `{ http: HttpClient }`. |
27
- | `HttpClient` | Minimal interface your HTTP layer must satisfy (today: **`get` only**). |
28
- | `HttpGet` | Type of the `get` method. |
28
+ ### Parity with `@ipetsadmin/api-main`
29
29
 
30
- ### Endpoints implemented
30
+ The API currently mounts **one public JSON surface** used by this client (see `api-main` `server.ts` / `routes/auth.routes.ts`):
31
31
 
32
- | Method | Path | Client method |
33
- | ------ | ---------------------- | ---------------------- |
34
- | `GET` | `/api/v1/health-check` | `client.healthCheck()` |
32
+ | Area | Server routes this client implements |
33
+ | ---------- | ------------------------------------ |
34
+ | **Health** | `GET /api/v1/health-check` |
35
+ | **Auth** | All routes under `/api/v1/auth/…` |
35
36
 
36
- The health check returns `Promise<IApiResponse<unknown>>` using the shared envelope from `@ipetsadmin/contracts`. When the canonical health payload exists in contracts, replace `unknown` with that type in `src/endpoints/health-check.ts`.
37
+ `api-main` also serves **OpenAPI** (`/api-docs`, `/api-docs.json`) when enabled; that is not wrapped here.
38
+
39
+ ### Endpoints
40
+
41
+ | HTTP | Path | `createApiClient` | Named helper |
42
+ | ------- | ------------------------------------------------- | ---------------------------------------------- | ---------------------------------------- |
43
+ | `GET` | `/api/v1/health-check` | `client.healthCheck()` | `fetchHealthCheck(http)` |
44
+ | `POST` | `/api/v1/auth/register` | `client.auth.register(body)` | `postRegister(http, body)` |
45
+ | `POST` | `/api/v1/auth/login` | `client.auth.login(body)` | `postLogin(http, body)` |
46
+ | `GET` | `/api/v1/auth/oauth/google/start?redirectUri=...` | `client.auth.getGoogleOAuthStart(redirectUri)` | `getGoogleOAuthStart(http, redirectUri)` |
47
+ | `POST` | `/api/v1/auth/oauth/google/callback` | `client.auth.postGoogleOAuthCallback(body)` | `postGoogleOAuthCallback(http, body)` |
48
+ | `POST` | `/api/v1/auth/refresh` | `client.auth.refresh(body)` | `postRefresh(http, body)` |
49
+ | `POST` | `/api/v1/auth/logout` | `client.auth.logout(body)` | `postLogout(http, body)` |
50
+ | `PATCH` | `/api/v1/auth/verify-email` | `client.auth.verifyEmail(body)` | `patchVerifyEmail(http, body)` |
51
+ | `GET` | `/api/v1/auth/me` | `client.auth.me(accessToken)` | `getMe(http, accessToken)` |
52
+
53
+ Responses use **`IApiResponse<T>`** from contracts (`success`, `data`, etc.). Auth session payloads are **`AuthSessionResponse`** (`TokenPair` + `user`). **`logout`** returns **204** with no JSON body. **`verifyEmail`** success envelope matches the server (typically `data` may be empty).
37
54
 
38
55
  ### Usage example (Axios)
39
56
 
40
- Configure Axios once in your app (base URL, timeouts, auth interceptors). Paths in this library are **relative to `baseURL`** (e.g. `/api/v1/...`).
57
+ Paths are **relative to `baseURL`** (e.g. `https://api.example.com`).
41
58
 
42
59
  ```typescript
43
60
  import axios from 'axios';
44
61
  import { createApiClient } from '@ipetsadmin/api-client';
45
62
 
46
63
  const http = axios.create({
47
- baseURL: process.env.API_BASE_URL, // e.g. https://api.example.com
48
- timeout: 15_000,
64
+ baseURL: process.env.API_BASE_URL,
65
+ timeout: 30_000,
49
66
  headers: { 'Content-Type': 'application/json' },
50
67
  });
51
68
 
52
69
  const api = createApiClient({ http });
53
70
 
54
71
  const health = await api.healthCheck();
55
- // health is IApiResponse<unknown>
56
- ```
57
-
58
- Axios’s `get` signature matches what this library expects: it returns a `Promise` that resolves to an object with a `data` property.
59
-
60
- ### Why a factory?
61
-
62
- `createApiClient({ http })` keeps this package free of environment-specific configuration, supports multiple backends or tenants in one app, and makes tests trivial by injecting a mock `http` object.
63
-
64
- ---
65
-
66
- ## Architecture overview
67
-
68
- 1. **`HttpClient`** — Abstraction over “how” HTTP is performed (Axios, `fetch` wrapper, etc.).
69
- 2. **`src/endpoints/*.ts`** — One module per route (or per resource): pure async functions that take `http` and return typed data.
70
- 3. **`createApiClient`** — Wires `http` into those functions and exposes a stable, discoverable API.
71
- 4. **`@ipetsadmin/contracts`** — Source of truth for DTOs shared with the server (e.g. `IApiResponse<T>`).
72
-
73
- When the API adds a route, add an endpoint module, extend `HttpClient` if you need new verbs, and add a method on the object returned by `createApiClient`.
74
-
75
- ---
76
-
77
- ## Extending `HttpClient` for all HTTP verbs
78
-
79
- Right now `HttpClient` only declares `get`. For **POST**, **PATCH**, **PUT**, and **DELETE**, extend the interface so adapters (Axios) stay type-safe and consistent.
80
-
81
- Axios maps HTTP concepts roughly as follows:
82
-
83
- | Concern | Axios request config field |
84
- | ------------ | -------------------------------------------------------------- |
85
- | URL path | First argument `url` (often built from path + **path params**) |
86
- | Query string | `params` |
87
- | JSON body | `data` |
88
- | Headers | `headers` |
89
-
90
- Recommended shape (mirror Axios so an `AxiosInstance` is assignable with minimal wrapping):
91
-
92
- ```typescript
93
- // src/http-client.ts — extend when you add non-GET endpoints
94
-
95
- /** Optional config aligned with Axios (params, data, headers, etc.). */
96
- export type HttpRequestConfig = {
97
- params?: Record<string, unknown>;
98
- data?: unknown;
99
- headers?: Record<string, string>;
100
- };
101
-
102
- export type HttpGet = <TResponse>(
103
- url: string,
104
- config?: HttpRequestConfig,
105
- ) => Promise<{ data: TResponse }>;
106
-
107
- export type HttpPost = <TResponse>(
108
- url: string,
109
- config?: HttpRequestConfig,
110
- ) => Promise<{ data: TResponse }>;
111
-
112
- export type HttpPut = HttpPost;
113
- export type HttpPatch = HttpPost;
114
-
115
- export type HttpDelete = <TResponse>(
116
- url: string,
117
- config?: HttpRequestConfig,
118
- ) => Promise<{ data: TResponse }>;
119
-
120
- export interface HttpClient {
121
- readonly get: HttpGet;
122
- readonly post: HttpPost;
123
- readonly put: HttpPut;
124
- readonly patch: HttpPatch;
125
- readonly delete: HttpDelete;
126
- }
127
- ```
128
-
129
- **Adapter example (Axios):** if TypeScript complains about `config` width, use a typed wrapper:
130
-
131
- ```typescript
132
- import type { AxiosInstance } from 'axios';
133
- import type { HttpClient, HttpRequestConfig } from '@ipetsadmin/api-client';
134
-
135
- export function createAxiosHttpClient(instance: AxiosInstance): HttpClient {
136
- return {
137
- get: (url, config) => instance.get(url, config),
138
- post: (url, config) => instance.post(url, config?.data, config),
139
- put: (url, config) => instance.put(url, config?.data, config),
140
- patch: (url, config) => instance.patch(url, config?.data, config),
141
- delete: (url, config) => instance.delete(url, config),
142
- };
143
- }
144
- ```
145
-
146
- Note: Axios’s `post`/`put`/`patch` take `data` as the second argument and `config` as the third; the wrapper above keeps your **endpoint** code calling `http.post(url, { data, params, headers })` in one place. Adjust the wrapper to match how you prefer to thread `config`.
147
-
148
- ---
149
-
150
- ## Implementing endpoints: patterns by verb
151
-
152
- Conventions used below:
153
-
154
- - **Path** — string constant; combine with **path parameters** via a small helper or template.
155
- - **Query** — `config.params` (Axios serializes to the query string).
156
- - **Body** — `config.data` for JSON bodies.
157
- - **Headers** — `config.headers` for per-request headers (auth, idempotency keys, etc.).
158
- - **Types** — import request/response types from `@ipetsadmin/contracts` when they exist.
159
-
160
- Assume:
161
-
162
- ```typescript
163
- import type { IApiResponse } from '@ipetsadmin/contracts';
164
- import type { HttpClient } from '../http-client';
165
- ```
166
-
167
- ### Path parameters
168
-
169
- Build the URL in the endpoint (keep templates next to the constant for clarity):
170
-
171
- ```typescript
172
- const USER_BY_ID_PATH = '/api/v1/users/:userId' as const;
173
-
174
- function buildUserPath(userId: string): string {
175
- return USER_BY_ID_PATH.replace(':userId', encodeURIComponent(userId));
176
- }
177
-
178
- // GET /api/v1/users/42
179
- await http.get<IApiResponse<UserDto>>(buildUserPath('42'));
180
- ```
181
-
182
- For many segments, a tiny helper avoids mistakes:
183
-
184
- ```typescript
185
- function buildPath(template: string, params: Record<string, string | number>): string {
186
- let path = template;
187
- for (const [key, value] of Object.entries(params)) {
188
- path = path.replace(`:${key}`, encodeURIComponent(String(value)));
189
- }
190
- return path;
191
- }
192
-
193
- // '/api/v1/orgs/:orgId/projects/:projectId'
194
- buildPath('/api/v1/orgs/:orgId/projects/:projectId', {
195
- orgId: 'acme',
196
- projectId: 'proj-1',
197
- });
198
- ```
199
-
200
- ### GET — query and headers
201
-
202
- ```typescript
203
- const SEARCH_PATH = '/api/v1/items' as const;
204
-
205
- export interface ListItemsParams {
206
- readonly query?: Record<string, string | number | boolean | undefined>;
207
- readonly headers?: Record<string, string>;
208
- }
209
-
210
- export async function fetchItems(
211
- http: HttpClient,
212
- args: ListItemsParams = {},
213
- ): Promise<IApiResponse<ItemSummary[]>> {
214
- const { data } = await http.get<IApiResponse<ItemSummary[]>>(SEARCH_PATH, {
215
- params: args.query,
216
- headers: args.headers,
217
- });
218
- return data;
219
- }
220
- ```
221
-
222
- ### POST — body, query, headers
223
-
224
- ```typescript
225
- const CREATE_ITEM_PATH = '/api/v1/items' as const;
226
-
227
- export interface CreateItemInput {
228
- readonly body: CreateItemRequestDto;
229
- readonly query?: Record<string, string | number | boolean | undefined>;
230
- readonly headers?: Record<string, string>;
231
- }
232
-
233
- export async function createItem(
234
- http: HttpClient,
235
- input: CreateItemInput,
236
- ): Promise<IApiResponse<ItemDto>> {
237
- const { data } = await http.post<IApiResponse<ItemDto>>(CREATE_ITEM_PATH, {
238
- data: input.body,
239
- params: input.query,
240
- headers: input.headers,
241
- });
242
- return data;
243
- }
244
- ```
245
-
246
- ### PATCH — partial body, path id
247
-
248
- ```typescript
249
- const ITEM_PATH = '/api/v1/items/:itemId' as const;
250
-
251
- export interface UpdateItemInput {
252
- readonly itemId: string;
253
- readonly body: Partial<UpdateItemRequestDto>;
254
- readonly headers?: Record<string, string>;
255
- }
256
-
257
- export async function updateItem(
258
- http: HttpClient,
259
- input: UpdateItemInput,
260
- ): Promise<IApiResponse<ItemDto>> {
261
- const path = buildPath(ITEM_PATH, { itemId: input.itemId });
262
- const { data } = await http.patch<IApiResponse<ItemDto>>(path, {
263
- data: input.body,
264
- headers: input.headers,
265
- });
266
- return data;
267
- }
72
+ const session = await api.auth.login({ email: 'user@example.com', password: '********' });
73
+ // session — IApiResponse<AuthSessionResponse>; with Axios, the HTTP body is axiosResponse.data
268
74
  ```
269
75
 
270
- ### PUT full replacement body
76
+ For **`IApiResponse<AuthSessionResponse>`**, the **envelope** is the JSON body; the **`AuthSessionResponse`** is usually **`response.data.data`** (outer `data` = Axios, inner `data` = API payload), depending on typings.
271
77
 
272
- Same pattern as PATCH; use when the API semantics are “replace resource”:
273
-
274
- ```typescript
275
- export async function replaceItem(
276
- http: HttpClient,
277
- input: { itemId: string; body: ReplaceItemRequestDto; headers?: Record<string, string> },
278
- ): Promise<IApiResponse<ItemDto>> {
279
- const path = buildPath('/api/v1/items/:itemId', { itemId: input.itemId });
280
- const { data } = await http.put<IApiResponse<ItemDto>>(path, {
281
- data: input.body,
282
- headers: input.headers,
283
- });
284
- return data;
285
- }
286
- ```
78
+ ### OAuth (mobile / web)
287
79
 
288
- ### DELETE path params; optional body for APIs that require one
80
+ 1. Call **`getGoogleOAuthStart`** with the same **`redirectUri`** you registered in Auth0 and in the API’s `OAUTH_ALLOWED_REDIRECT_URIS`.
81
+ 2. Open **`authorizationUrl`** in a browser / `ASWebAuthenticationSession`.
82
+ 3. After redirect, send **`code`**, **`state`**, and **`redirectUri`** with **`postGoogleOAuthCallback`**.
289
83
 
290
- Many DELETE endpoints have **no body**; omit `data`:
84
+ ### Email verification
291
85
 
292
- ```typescript
293
- export async function deleteItem(
294
- http: HttpClient,
295
- itemId: string,
296
- headers?: Record<string, string>,
297
- ): Promise<IApiResponse<void>> {
298
- const path = buildPath('/api/v1/items/:itemId', { itemId });
299
- const { data } = await http.delete<IApiResponse<void>>(path, { headers });
300
- return data;
301
- }
302
- ```
86
+ Call **`api.auth.verifyEmail({ token })`** (or **`patchVerifyEmail`**) with the one-time token from the verification link your backend emails. Shapes: **`VerifyEmailRequest`** from `@ipetsadmin/contracts`.
303
87
 
304
- If your API uses a JSON body on DELETE (uncommon but possible):
88
+ ### Why a factory?
305
89
 
306
- ```typescript
307
- await http.delete<IApiResponse<void>>(path, {
308
- data: { reason: 'user_request' },
309
- headers: { 'Content-Type': 'application/json' },
310
- });
311
- ```
90
+ `createApiClient({ http })` keeps env and transport out of this package, supports multiple backends in one app, and makes tests easy with a mock `http` object.
312
91
 
313
92
  ---
314
93
 
315
- ## Wiring new endpoints into `createApiClient`
94
+ ## Architecture
316
95
 
317
- 1. Add `src/endpoints/your-endpoint.ts` with a `fetch*` / `create*` / `update*` function that takes `http` first (and an `input` object for non-trivial inputs).
318
- 2. Extend `HttpClient` if the verb is not available yet.
319
- 3. Add a method on `ApiClient` and delegate to the endpoint function.
96
+ 1. **`HttpClient`** abstraction over **`get` / `post` / `patch`** (`{ data: T }` response shape, Axios-compatible).
97
+ 2. **`src/endpoints/*.ts`** thin functions: `(http, …args) => Promise<…>`.
98
+ 3. **`createApiClient`** wires `http` into those endpoints.
99
+ 4. **`@ipetsadmin/contracts`** — DTOs (`RegisterRequest`, `AuthSessionResponse`, `VerifyEmailRequest`, …).
320
100
 
321
- Example after adding `fetchItems` and `createItem`:
322
-
323
- ```typescript
324
- // src/create-api-client.ts
325
- import { createItem } from './endpoints/create-item';
326
- import { fetchItems } from './endpoints/list-items';
327
- import { fetchHealthCheck } from './endpoints/health-check';
328
- import type { HttpClient } from './http-client';
329
-
330
- export function createApiClient(options: { http: HttpClient }) {
331
- const { http } = options;
332
-
333
- return {
334
- healthCheck: () => fetchHealthCheck(http),
335
- listItems: (params?: ListItemsParams) => fetchItems(http, params),
336
- createItem: (input: CreateItemInput) => createItem(http, input),
337
- };
338
- }
339
- ```
101
+ ### Extending further
340
102
 
341
- Export new types (`ListItemsParams`, `CreateItemInput`, etc.) from `src/index.ts` if consumers need them.
103
+ Add methods on **`HttpClient`** in `src/http-client.ts`, then new endpoint modules. For query params, headers, and `Authorization`, align with your adapter (Axios passes a `config` object to **`get`**, **`post`**, and **`patch`**; **`getMe`** already sets `Authorization: Bearer` for the access token argument).
342
104
 
343
105
  ---
344
106
 
345
107
  ## Testing
346
108
 
347
- Inject a fake `HttpClient` and assert the right URL, method, and payload were used.
348
-
349
- **With the current `HttpClient` (only `get`):**
109
+ Inject a fake **`HttpClient`** with **`get`**, **`post`**, and **`patch`** mocks and assert URLs and payloads.
350
110
 
351
111
  ```typescript
352
112
  import type { HttpClient } from '@ipetsadmin/api-client';
@@ -354,34 +114,33 @@ import { createApiClient } from '@ipetsadmin/api-client';
354
114
 
355
115
  const http: HttpClient = {
356
116
  get: jest.fn().mockResolvedValue({ data: { success: true, data: {} } }),
117
+ post: jest.fn().mockResolvedValue({ data: { success: true, data: {} } }),
118
+ patch: jest.fn().mockResolvedValue({ data: { success: true } }),
357
119
  };
358
120
 
359
121
  const api = createApiClient({ http });
360
122
  await api.healthCheck();
361
-
362
123
  expect(http.get).toHaveBeenCalledWith('/api/v1/health-check', undefined);
363
124
  ```
364
125
 
365
- **After you extend `HttpClient` with `post` / `put` / `patch` / `delete`,** add matching mocks (e.g. `post: jest.fn()`) so the object satisfies the full interface.
366
-
367
126
  ---
368
127
 
369
128
  ## Development (this repository)
370
129
 
371
- | Script | Purpose |
372
- | ------------------------- | ------------------------------------------------------------------------- |
373
- | `pnpm run build` | Produce `dist/` (CJS, ESM, `.d.ts`) via [tsup](https://tsup.egoist.dev/). |
374
- | `pnpm run typecheck` | `tsc --noEmit` |
375
- | `pnpm run lint` | ESLint |
376
- | `pnpm run validate` | typecheck + lint + Prettier check |
377
- | `pnpm run prepublishOnly` | runs validate and build before publish |
130
+ | Script | Purpose |
131
+ | ------------------------- | ------------------------------------ |
132
+ | `pnpm run build` | `tsup` `dist/` (CJS, ESM, `.d.ts`) |
133
+ | `pnpm run typecheck` | `tsc --noEmit` |
134
+ | `pnpm run lint` | ESLint |
135
+ | `pnpm run validate` | typecheck + lint + Prettier check |
136
+ | `pnpm run prepublishOnly` | validate + build before publish |
378
137
 
379
138
  ---
380
139
 
381
- ## Summary
140
+ ## Changelog
141
+
142
+ See [CHANGELOG.md](./CHANGELOG.md).
382
143
 
383
- - **Today:** `createApiClient({ http })` + `healthCheck()` + `HttpClient` with **`get` only**.
384
- - **Next:** Extend `HttpClient` with `post` / `put` / `patch` / `delete` and optional `HttpRequestConfig`, then add endpoint modules that use **path helpers**, **`params`**, **`data`**, and **`headers`** as shown above.
385
- - **Types:** Prefer `@ipetsadmin/contracts` for DTOs and `IApiResponse<T>` for envelope responses.
144
+ ## License
386
145
 
387
- For changes to this package, see [CHANGELOG.md](./CHANGELOG.md).
146
+ MIT see [LICENSE](./LICENSE).
package/dist/index.d.mts CHANGED
@@ -1,20 +1,47 @@
1
- import { IApiResponse } from '@ipetsadmin/contracts';
1
+ import { RegisterRequest, IApiResponse, AuthSessionResponse, LoginRequest, OAuthGoogleStartResponse, OAuthGoogleCallbackRequest, RefreshRequest, LogoutRequest, VerifyEmailRequest, AuthUserResponse, HealthCheck } from '@ipetsadmin/contracts';
2
2
 
3
3
  type HttpGet = <TResponse>(url: string, config?: unknown) => Promise<{
4
4
  data: TResponse;
5
5
  }>;
6
+ type HttpPost = <TResponse, TBody = unknown>(url: string, body?: TBody, config?: unknown) => Promise<{
7
+ data: TResponse;
8
+ }>;
9
+ type HttpPatch = <TResponse, TBody = unknown>(url: string, body?: TBody, config?: unknown) => Promise<{
10
+ data: TResponse;
11
+ }>;
6
12
  interface HttpClient {
7
13
  readonly get: HttpGet;
14
+ readonly post: HttpPost;
15
+ readonly patch: HttpPatch;
8
16
  }
9
17
 
10
- declare function fetchHealthCheck(http: HttpClient): Promise<IApiResponse<unknown>>;
18
+ declare function postRegister(http: HttpClient, body: RegisterRequest): Promise<IApiResponse<AuthSessionResponse>>;
19
+ declare function postLogin(http: HttpClient, body: LoginRequest): Promise<IApiResponse<AuthSessionResponse>>;
20
+ declare function getGoogleOAuthStart(http: HttpClient, redirectUri: string): Promise<IApiResponse<OAuthGoogleStartResponse>>;
21
+ declare function postGoogleOAuthCallback(http: HttpClient, body: OAuthGoogleCallbackRequest): Promise<IApiResponse<AuthSessionResponse>>;
22
+ declare function postRefresh(http: HttpClient, body: RefreshRequest): Promise<IApiResponse<AuthSessionResponse>>;
23
+ declare function postLogout(http: HttpClient, body: LogoutRequest): Promise<void>;
24
+ declare function getMe(http: HttpClient, accessToken: string): Promise<IApiResponse<AuthUserResponse>>;
25
+ declare function patchVerifyEmail(http: HttpClient, body: VerifyEmailRequest): Promise<IApiResponse<void>>;
26
+
27
+ declare function fetchHealthCheck(http: HttpClient): Promise<IApiResponse<HealthCheck>>;
11
28
 
12
29
  interface CreateApiClientOptions {
13
30
  readonly http: HttpClient;
14
31
  }
15
32
  interface ApiClient {
16
33
  readonly healthCheck: () => ReturnType<typeof fetchHealthCheck>;
34
+ readonly auth: {
35
+ readonly register: (body: Parameters<typeof postRegister>[1]) => ReturnType<typeof postRegister>;
36
+ readonly login: (body: Parameters<typeof postLogin>[1]) => ReturnType<typeof postLogin>;
37
+ readonly getGoogleOAuthStart: (redirectUri: string) => ReturnType<typeof getGoogleOAuthStart>;
38
+ readonly postGoogleOAuthCallback: (body: Parameters<typeof postGoogleOAuthCallback>[1]) => ReturnType<typeof postGoogleOAuthCallback>;
39
+ readonly refresh: (body: Parameters<typeof postRefresh>[1]) => ReturnType<typeof postRefresh>;
40
+ readonly logout: (body: Parameters<typeof postLogout>[1]) => ReturnType<typeof postLogout>;
41
+ readonly verifyEmail: (body: Parameters<typeof patchVerifyEmail>[1]) => ReturnType<typeof patchVerifyEmail>;
42
+ readonly me: (accessToken: string) => ReturnType<typeof getMe>;
43
+ };
17
44
  }
18
45
  declare function createApiClient(options: CreateApiClientOptions): ApiClient;
19
46
 
20
- export { type ApiClient, type CreateApiClientOptions, type HttpClient, type HttpGet, createApiClient };
47
+ export { type ApiClient, type CreateApiClientOptions, type HttpClient, type HttpGet, type HttpPatch, type HttpPost, createApiClient, getGoogleOAuthStart, getMe, patchVerifyEmail, postGoogleOAuthCallback, postLogin, postLogout, postRefresh, postRegister };
package/dist/index.d.ts CHANGED
@@ -1,20 +1,47 @@
1
- import { IApiResponse } from '@ipetsadmin/contracts';
1
+ import { RegisterRequest, IApiResponse, AuthSessionResponse, LoginRequest, OAuthGoogleStartResponse, OAuthGoogleCallbackRequest, RefreshRequest, LogoutRequest, VerifyEmailRequest, AuthUserResponse, HealthCheck } from '@ipetsadmin/contracts';
2
2
 
3
3
  type HttpGet = <TResponse>(url: string, config?: unknown) => Promise<{
4
4
  data: TResponse;
5
5
  }>;
6
+ type HttpPost = <TResponse, TBody = unknown>(url: string, body?: TBody, config?: unknown) => Promise<{
7
+ data: TResponse;
8
+ }>;
9
+ type HttpPatch = <TResponse, TBody = unknown>(url: string, body?: TBody, config?: unknown) => Promise<{
10
+ data: TResponse;
11
+ }>;
6
12
  interface HttpClient {
7
13
  readonly get: HttpGet;
14
+ readonly post: HttpPost;
15
+ readonly patch: HttpPatch;
8
16
  }
9
17
 
10
- declare function fetchHealthCheck(http: HttpClient): Promise<IApiResponse<unknown>>;
18
+ declare function postRegister(http: HttpClient, body: RegisterRequest): Promise<IApiResponse<AuthSessionResponse>>;
19
+ declare function postLogin(http: HttpClient, body: LoginRequest): Promise<IApiResponse<AuthSessionResponse>>;
20
+ declare function getGoogleOAuthStart(http: HttpClient, redirectUri: string): Promise<IApiResponse<OAuthGoogleStartResponse>>;
21
+ declare function postGoogleOAuthCallback(http: HttpClient, body: OAuthGoogleCallbackRequest): Promise<IApiResponse<AuthSessionResponse>>;
22
+ declare function postRefresh(http: HttpClient, body: RefreshRequest): Promise<IApiResponse<AuthSessionResponse>>;
23
+ declare function postLogout(http: HttpClient, body: LogoutRequest): Promise<void>;
24
+ declare function getMe(http: HttpClient, accessToken: string): Promise<IApiResponse<AuthUserResponse>>;
25
+ declare function patchVerifyEmail(http: HttpClient, body: VerifyEmailRequest): Promise<IApiResponse<void>>;
26
+
27
+ declare function fetchHealthCheck(http: HttpClient): Promise<IApiResponse<HealthCheck>>;
11
28
 
12
29
  interface CreateApiClientOptions {
13
30
  readonly http: HttpClient;
14
31
  }
15
32
  interface ApiClient {
16
33
  readonly healthCheck: () => ReturnType<typeof fetchHealthCheck>;
34
+ readonly auth: {
35
+ readonly register: (body: Parameters<typeof postRegister>[1]) => ReturnType<typeof postRegister>;
36
+ readonly login: (body: Parameters<typeof postLogin>[1]) => ReturnType<typeof postLogin>;
37
+ readonly getGoogleOAuthStart: (redirectUri: string) => ReturnType<typeof getGoogleOAuthStart>;
38
+ readonly postGoogleOAuthCallback: (body: Parameters<typeof postGoogleOAuthCallback>[1]) => ReturnType<typeof postGoogleOAuthCallback>;
39
+ readonly refresh: (body: Parameters<typeof postRefresh>[1]) => ReturnType<typeof postRefresh>;
40
+ readonly logout: (body: Parameters<typeof postLogout>[1]) => ReturnType<typeof postLogout>;
41
+ readonly verifyEmail: (body: Parameters<typeof patchVerifyEmail>[1]) => ReturnType<typeof patchVerifyEmail>;
42
+ readonly me: (accessToken: string) => ReturnType<typeof getMe>;
43
+ };
17
44
  }
18
45
  declare function createApiClient(options: CreateApiClientOptions): ApiClient;
19
46
 
20
- export { type ApiClient, type CreateApiClientOptions, type HttpClient, type HttpGet, createApiClient };
47
+ export { type ApiClient, type CreateApiClientOptions, type HttpClient, type HttpGet, type HttpPatch, type HttpPost, createApiClient, getGoogleOAuthStart, getMe, patchVerifyEmail, postGoogleOAuthCallback, postLogin, postLogout, postRefresh, postRegister };
package/dist/index.js CHANGED
@@ -1,20 +1,92 @@
1
1
  'use strict';
2
2
 
3
+ // src/endpoints/auth.ts
4
+ var AUTH_BASE = "/api/v1/auth";
5
+ async function postRegister(http, body) {
6
+ const { data } = await http.post(
7
+ `${AUTH_BASE}/register`,
8
+ body
9
+ );
10
+ return data;
11
+ }
12
+ async function postLogin(http, body) {
13
+ const { data } = await http.post(
14
+ `${AUTH_BASE}/login`,
15
+ body
16
+ );
17
+ return data;
18
+ }
19
+ async function getGoogleOAuthStart(http, redirectUri) {
20
+ const q = new URLSearchParams({ redirectUri });
21
+ const { data } = await http.get(
22
+ `${AUTH_BASE}/oauth/google/start?${q.toString()}`
23
+ );
24
+ return data;
25
+ }
26
+ async function postGoogleOAuthCallback(http, body) {
27
+ const { data } = await http.post(
28
+ `${AUTH_BASE}/oauth/google/callback`,
29
+ body
30
+ );
31
+ return data;
32
+ }
33
+ async function postRefresh(http, body) {
34
+ const { data } = await http.post(
35
+ `${AUTH_BASE}/refresh`,
36
+ body
37
+ );
38
+ return data;
39
+ }
40
+ async function postLogout(http, body) {
41
+ await http.post(`${AUTH_BASE}/logout`, body);
42
+ }
43
+ async function getMe(http, accessToken) {
44
+ const { data } = await http.get(`${AUTH_BASE}/me`, {
45
+ headers: { Authorization: `Bearer ${accessToken}` }
46
+ });
47
+ return data;
48
+ }
49
+ async function patchVerifyEmail(http, body) {
50
+ const { data } = await http.patch(
51
+ `${AUTH_BASE}/verify-email`,
52
+ body
53
+ );
54
+ return data;
55
+ }
56
+
3
57
  // src/endpoints/health-check.ts
4
58
  var HEALTH_CHECK_PATH = "/api/v1/health-check";
5
59
  async function fetchHealthCheck(http) {
6
- const { data } = await http.get(HEALTH_CHECK_PATH);
7
- return data;
60
+ const { data: body } = await http.get(HEALTH_CHECK_PATH);
61
+ return body;
8
62
  }
9
63
 
10
64
  // src/create-api-client.ts
11
65
  function createApiClient(options) {
12
66
  const { http } = options;
13
67
  return {
14
- healthCheck: () => fetchHealthCheck(http)
68
+ healthCheck: () => fetchHealthCheck(http),
69
+ auth: {
70
+ register: (body) => postRegister(http, body),
71
+ login: (body) => postLogin(http, body),
72
+ getGoogleOAuthStart: (redirectUri) => getGoogleOAuthStart(http, redirectUri),
73
+ postGoogleOAuthCallback: (body) => postGoogleOAuthCallback(http, body),
74
+ refresh: (body) => postRefresh(http, body),
75
+ logout: (body) => postLogout(http, body),
76
+ verifyEmail: (body) => patchVerifyEmail(http, body),
77
+ me: (accessToken) => getMe(http, accessToken)
78
+ }
15
79
  };
16
80
  }
17
81
 
18
82
  exports.createApiClient = createApiClient;
83
+ exports.getGoogleOAuthStart = getGoogleOAuthStart;
84
+ exports.getMe = getMe;
85
+ exports.patchVerifyEmail = patchVerifyEmail;
86
+ exports.postGoogleOAuthCallback = postGoogleOAuthCallback;
87
+ exports.postLogin = postLogin;
88
+ exports.postLogout = postLogout;
89
+ exports.postRefresh = postRefresh;
90
+ exports.postRegister = postRegister;
19
91
  //# sourceMappingURL=index.js.map
20
92
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/endpoints/health-check.ts","../src/create-api-client.ts"],"names":[],"mappings":";;;AAGA,IAAM,iBAAA,GAAoB,sBAAA;AAO1B,eAAsB,iBAAiB,IAAA,EAAkD;AACvF,EAAA,MAAM,EAAE,IAAA,EAAK,GAAI,MAAM,IAAA,CAAK,IAA2B,iBAAiB,CAAA;AACxE,EAAA,OAAO,IAAA;AACT;;;ACKO,SAAS,gBAAgB,OAAA,EAA4C;AAC1E,EAAA,MAAM,EAAE,MAAK,GAAI,OAAA;AAEjB,EAAA,OAAO;AAAA,IACL,WAAA,EAAa,MAAM,gBAAA,CAAiB,IAAI;AAAA,GAC1C;AACF","file":"index.js","sourcesContent":["import type { IApiResponse } from '@ipetsadmin/contracts';\nimport type { HttpClient } from '../http-client';\n\nconst HEALTH_CHECK_PATH = '/api/v1/health-check' as const;\n\n/**\n * GET /api/v1/health-check — server liveness.\n * Response shape is the standard API envelope; payload typing can be refined\n * in `@ipetsadmin/contracts` when the health payload is finalized.\n */\nexport async function fetchHealthCheck(http: HttpClient): Promise<IApiResponse<unknown>> {\n const { data } = await http.get<IApiResponse<unknown>>(HEALTH_CHECK_PATH);\n return data;\n}\n","import { fetchHealthCheck } from './endpoints/health-check';\nimport type { HttpClient } from './http-client';\n\nexport interface CreateApiClientOptions {\n /**\n * Preconfigured HTTP client (e.g. Axios instance with `baseURL` set).\n * Paths in this library are relative to that base (e.g. `/api/v1/...`).\n */\n readonly http: HttpClient;\n}\n\nexport interface ApiClient {\n readonly healthCheck: () => ReturnType<typeof fetchHealthCheck>;\n}\n\n/**\n * Creates a typed API client backed by the provided HTTP implementation.\n */\nexport function createApiClient(options: CreateApiClientOptions): ApiClient {\n const { http } = options;\n\n return {\n healthCheck: () => fetchHealthCheck(http),\n };\n}\n"]}
1
+ {"version":3,"sources":["../src/endpoints/auth.ts","../src/endpoints/health-check.ts","../src/create-api-client.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import type {\n AuthSessionResponse,\n AuthUserResponse,\n IApiResponse,\n LoginRequest,\n LogoutRequest,\n OAuthGoogleCallbackRequest,\n OAuthGoogleStartResponse,\n RefreshRequest,\n RegisterRequest,\n VerifyEmailRequest,\n} from '@ipetsadmin/contracts';\n\nimport type { HttpClient } from '../http-client';\n\nconst AUTH_BASE = '/api/v1/auth' as const;\n\n/**\n * POST /api/v1/auth/register\n */\nexport async function postRegister(\n http: HttpClient,\n body: RegisterRequest,\n): Promise<IApiResponse<AuthSessionResponse>> {\n const { data } = await http.post<IApiResponse<AuthSessionResponse>, RegisterRequest>(\n `${AUTH_BASE}/register`,\n body,\n );\n return data;\n}\n\n/**\n * POST /api/v1/auth/login\n */\nexport async function postLogin(\n http: HttpClient,\n body: LoginRequest,\n): Promise<IApiResponse<AuthSessionResponse>> {\n const { data } = await http.post<IApiResponse<AuthSessionResponse>, LoginRequest>(\n `${AUTH_BASE}/login`,\n body,\n );\n return data;\n}\n\n/**\n * GET /api/v1/auth/oauth/google/start?redirectUri=...\n */\nexport async function getGoogleOAuthStart(\n http: HttpClient,\n redirectUri: string,\n): Promise<IApiResponse<OAuthGoogleStartResponse>> {\n const q = new URLSearchParams({ redirectUri });\n const { data } = await http.get<IApiResponse<OAuthGoogleStartResponse>>(\n `${AUTH_BASE}/oauth/google/start?${q.toString()}`,\n );\n return data;\n}\n\n/**\n * POST /api/v1/auth/oauth/google/callback\n */\nexport async function postGoogleOAuthCallback(\n http: HttpClient,\n body: OAuthGoogleCallbackRequest,\n): Promise<IApiResponse<AuthSessionResponse>> {\n const { data } = await http.post<IApiResponse<AuthSessionResponse>, OAuthGoogleCallbackRequest>(\n `${AUTH_BASE}/oauth/google/callback`,\n body,\n );\n return data;\n}\n\n/**\n * POST /api/v1/auth/refresh\n */\nexport async function postRefresh(\n http: HttpClient,\n body: RefreshRequest,\n): Promise<IApiResponse<AuthSessionResponse>> {\n const { data } = await http.post<IApiResponse<AuthSessionResponse>, RefreshRequest>(\n `${AUTH_BASE}/refresh`,\n body,\n );\n return data;\n}\n\n/**\n * POST /api/v1/auth/logout — 204 No Content (no JSON body).\n */\nexport async function postLogout(http: HttpClient, body: LogoutRequest): Promise<void> {\n await http.post<undefined, LogoutRequest>(`${AUTH_BASE}/logout`, body);\n}\n\n/**\n * GET /api/v1/auth/me — requires `Authorization: Bearer <accessToken>`.\n */\nexport async function getMe(\n http: HttpClient,\n accessToken: string,\n): Promise<IApiResponse<AuthUserResponse>> {\n const { data } = await http.get<IApiResponse<AuthUserResponse>>(`${AUTH_BASE}/me`, {\n headers: { Authorization: `Bearer ${accessToken}` },\n });\n return data;\n}\n\n/**\n * PATCH /api/v1/auth/verify-email\n */\nexport async function patchVerifyEmail(\n http: HttpClient,\n body: VerifyEmailRequest,\n): Promise<IApiResponse<void>> {\n const { data } = await http.patch<IApiResponse<void>, VerifyEmailRequest>(\n `${AUTH_BASE}/verify-email`,\n body,\n );\n return data;\n}\n","import type { IApiResponse, HealthCheck } from '@ipetsadmin/contracts';\nimport type { HttpClient } from '../http-client';\n\n/** Same path as `/api/${v1}/health-check` on the server when `v1` is the string `'v1'`. */\nconst HEALTH_CHECK_PATH = '/api/v1/health-check' as const;\n\n/**\n * GET /api/v1/health-check — server liveness.\n *\n * Returns the full JSON the server sends (`IApiResponse<HealthCheck>`).\n *\n * **Why not `data.data` here?** Adapters like Axios wrap the HTTP body in a property also\n * called `data`. That outer `data` is the entire `res.json(...)` object. The inner\n * `IApiResponse.data` is the `HealthCheck` payload — use `.data` on the **returned** value,\n * e.g. `(await fetchHealthCheck(http)).data`.\n */\nexport async function fetchHealthCheck(http: HttpClient): Promise<IApiResponse<HealthCheck>> {\n const { data: body } = await http.get<IApiResponse<HealthCheck>>(HEALTH_CHECK_PATH);\n\n return body;\n}\n","import {\n getGoogleOAuthStart,\n getMe,\n patchVerifyEmail,\n postGoogleOAuthCallback,\n postLogin,\n postLogout,\n postRefresh,\n postRegister,\n} from './endpoints/auth';\nimport { fetchHealthCheck } from './endpoints/health-check';\nimport type { HttpClient } from './http-client';\n\nexport interface CreateApiClientOptions {\n /**\n * Preconfigured HTTP client (e.g. Axios instance with `baseURL` set).\n * Paths in this library are relative to that base (e.g. `/api/v1/...`).\n * Both `get` and `post` are required for auth flows.\n */\n readonly http: HttpClient;\n}\n\nexport interface ApiClient {\n readonly healthCheck: () => ReturnType<typeof fetchHealthCheck>;\n readonly auth: {\n readonly register: (\n body: Parameters<typeof postRegister>[1],\n ) => ReturnType<typeof postRegister>;\n readonly login: (body: Parameters<typeof postLogin>[1]) => ReturnType<typeof postLogin>;\n readonly getGoogleOAuthStart: (redirectUri: string) => ReturnType<typeof getGoogleOAuthStart>;\n readonly postGoogleOAuthCallback: (\n body: Parameters<typeof postGoogleOAuthCallback>[1],\n ) => ReturnType<typeof postGoogleOAuthCallback>;\n readonly refresh: (body: Parameters<typeof postRefresh>[1]) => ReturnType<typeof postRefresh>;\n readonly logout: (body: Parameters<typeof postLogout>[1]) => ReturnType<typeof postLogout>;\n readonly verifyEmail: (\n body: Parameters<typeof patchVerifyEmail>[1],\n ) => ReturnType<typeof patchVerifyEmail>;\n readonly me: (accessToken: string) => ReturnType<typeof getMe>;\n };\n}\n\n/**\n * Creates a typed API client backed by the provided HTTP implementation.\n */\nexport function createApiClient(options: CreateApiClientOptions): ApiClient {\n const { http } = options;\n\n return {\n healthCheck: () => fetchHealthCheck(http),\n auth: {\n register: (body) => postRegister(http, body),\n login: (body) => postLogin(http, body),\n getGoogleOAuthStart: (redirectUri) => getGoogleOAuthStart(http, redirectUri),\n postGoogleOAuthCallback: (body) => postGoogleOAuthCallback(http, body),\n refresh: (body) => postRefresh(http, body),\n logout: (body) => postLogout(http, body),\n verifyEmail: (body) => patchVerifyEmail(http, body),\n me: (accessToken) => getMe(http, accessToken),\n },\n };\n}\n"]}
package/dist/index.mjs CHANGED
@@ -1,18 +1,82 @@
1
+ // src/endpoints/auth.ts
2
+ var AUTH_BASE = "/api/v1/auth";
3
+ async function postRegister(http, body) {
4
+ const { data } = await http.post(
5
+ `${AUTH_BASE}/register`,
6
+ body
7
+ );
8
+ return data;
9
+ }
10
+ async function postLogin(http, body) {
11
+ const { data } = await http.post(
12
+ `${AUTH_BASE}/login`,
13
+ body
14
+ );
15
+ return data;
16
+ }
17
+ async function getGoogleOAuthStart(http, redirectUri) {
18
+ const q = new URLSearchParams({ redirectUri });
19
+ const { data } = await http.get(
20
+ `${AUTH_BASE}/oauth/google/start?${q.toString()}`
21
+ );
22
+ return data;
23
+ }
24
+ async function postGoogleOAuthCallback(http, body) {
25
+ const { data } = await http.post(
26
+ `${AUTH_BASE}/oauth/google/callback`,
27
+ body
28
+ );
29
+ return data;
30
+ }
31
+ async function postRefresh(http, body) {
32
+ const { data } = await http.post(
33
+ `${AUTH_BASE}/refresh`,
34
+ body
35
+ );
36
+ return data;
37
+ }
38
+ async function postLogout(http, body) {
39
+ await http.post(`${AUTH_BASE}/logout`, body);
40
+ }
41
+ async function getMe(http, accessToken) {
42
+ const { data } = await http.get(`${AUTH_BASE}/me`, {
43
+ headers: { Authorization: `Bearer ${accessToken}` }
44
+ });
45
+ return data;
46
+ }
47
+ async function patchVerifyEmail(http, body) {
48
+ const { data } = await http.patch(
49
+ `${AUTH_BASE}/verify-email`,
50
+ body
51
+ );
52
+ return data;
53
+ }
54
+
1
55
  // src/endpoints/health-check.ts
2
56
  var HEALTH_CHECK_PATH = "/api/v1/health-check";
3
57
  async function fetchHealthCheck(http) {
4
- const { data } = await http.get(HEALTH_CHECK_PATH);
5
- return data;
58
+ const { data: body } = await http.get(HEALTH_CHECK_PATH);
59
+ return body;
6
60
  }
7
61
 
8
62
  // src/create-api-client.ts
9
63
  function createApiClient(options) {
10
64
  const { http } = options;
11
65
  return {
12
- healthCheck: () => fetchHealthCheck(http)
66
+ healthCheck: () => fetchHealthCheck(http),
67
+ auth: {
68
+ register: (body) => postRegister(http, body),
69
+ login: (body) => postLogin(http, body),
70
+ getGoogleOAuthStart: (redirectUri) => getGoogleOAuthStart(http, redirectUri),
71
+ postGoogleOAuthCallback: (body) => postGoogleOAuthCallback(http, body),
72
+ refresh: (body) => postRefresh(http, body),
73
+ logout: (body) => postLogout(http, body),
74
+ verifyEmail: (body) => patchVerifyEmail(http, body),
75
+ me: (accessToken) => getMe(http, accessToken)
76
+ }
13
77
  };
14
78
  }
15
79
 
16
- export { createApiClient };
80
+ export { createApiClient, getGoogleOAuthStart, getMe, patchVerifyEmail, postGoogleOAuthCallback, postLogin, postLogout, postRefresh, postRegister };
17
81
  //# sourceMappingURL=index.mjs.map
18
82
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/endpoints/health-check.ts","../src/create-api-client.ts"],"names":[],"mappings":";AAGA,IAAM,iBAAA,GAAoB,sBAAA;AAO1B,eAAsB,iBAAiB,IAAA,EAAkD;AACvF,EAAA,MAAM,EAAE,IAAA,EAAK,GAAI,MAAM,IAAA,CAAK,IAA2B,iBAAiB,CAAA;AACxE,EAAA,OAAO,IAAA;AACT;;;ACKO,SAAS,gBAAgB,OAAA,EAA4C;AAC1E,EAAA,MAAM,EAAE,MAAK,GAAI,OAAA;AAEjB,EAAA,OAAO;AAAA,IACL,WAAA,EAAa,MAAM,gBAAA,CAAiB,IAAI;AAAA,GAC1C;AACF","file":"index.mjs","sourcesContent":["import type { IApiResponse } from '@ipetsadmin/contracts';\nimport type { HttpClient } from '../http-client';\n\nconst HEALTH_CHECK_PATH = '/api/v1/health-check' as const;\n\n/**\n * GET /api/v1/health-check — server liveness.\n * Response shape is the standard API envelope; payload typing can be refined\n * in `@ipetsadmin/contracts` when the health payload is finalized.\n */\nexport async function fetchHealthCheck(http: HttpClient): Promise<IApiResponse<unknown>> {\n const { data } = await http.get<IApiResponse<unknown>>(HEALTH_CHECK_PATH);\n return data;\n}\n","import { fetchHealthCheck } from './endpoints/health-check';\nimport type { HttpClient } from './http-client';\n\nexport interface CreateApiClientOptions {\n /**\n * Preconfigured HTTP client (e.g. Axios instance with `baseURL` set).\n * Paths in this library are relative to that base (e.g. `/api/v1/...`).\n */\n readonly http: HttpClient;\n}\n\nexport interface ApiClient {\n readonly healthCheck: () => ReturnType<typeof fetchHealthCheck>;\n}\n\n/**\n * Creates a typed API client backed by the provided HTTP implementation.\n */\nexport function createApiClient(options: CreateApiClientOptions): ApiClient {\n const { http } = options;\n\n return {\n healthCheck: () => fetchHealthCheck(http),\n };\n}\n"]}
1
+ {"version":3,"sources":["../src/endpoints/auth.ts","../src/endpoints/health-check.ts","../src/create-api-client.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["import type {\n AuthSessionResponse,\n AuthUserResponse,\n IApiResponse,\n LoginRequest,\n LogoutRequest,\n OAuthGoogleCallbackRequest,\n OAuthGoogleStartResponse,\n RefreshRequest,\n RegisterRequest,\n VerifyEmailRequest,\n} from '@ipetsadmin/contracts';\n\nimport type { HttpClient } from '../http-client';\n\nconst AUTH_BASE = '/api/v1/auth' as const;\n\n/**\n * POST /api/v1/auth/register\n */\nexport async function postRegister(\n http: HttpClient,\n body: RegisterRequest,\n): Promise<IApiResponse<AuthSessionResponse>> {\n const { data } = await http.post<IApiResponse<AuthSessionResponse>, RegisterRequest>(\n `${AUTH_BASE}/register`,\n body,\n );\n return data;\n}\n\n/**\n * POST /api/v1/auth/login\n */\nexport async function postLogin(\n http: HttpClient,\n body: LoginRequest,\n): Promise<IApiResponse<AuthSessionResponse>> {\n const { data } = await http.post<IApiResponse<AuthSessionResponse>, LoginRequest>(\n `${AUTH_BASE}/login`,\n body,\n );\n return data;\n}\n\n/**\n * GET /api/v1/auth/oauth/google/start?redirectUri=...\n */\nexport async function getGoogleOAuthStart(\n http: HttpClient,\n redirectUri: string,\n): Promise<IApiResponse<OAuthGoogleStartResponse>> {\n const q = new URLSearchParams({ redirectUri });\n const { data } = await http.get<IApiResponse<OAuthGoogleStartResponse>>(\n `${AUTH_BASE}/oauth/google/start?${q.toString()}`,\n );\n return data;\n}\n\n/**\n * POST /api/v1/auth/oauth/google/callback\n */\nexport async function postGoogleOAuthCallback(\n http: HttpClient,\n body: OAuthGoogleCallbackRequest,\n): Promise<IApiResponse<AuthSessionResponse>> {\n const { data } = await http.post<IApiResponse<AuthSessionResponse>, OAuthGoogleCallbackRequest>(\n `${AUTH_BASE}/oauth/google/callback`,\n body,\n );\n return data;\n}\n\n/**\n * POST /api/v1/auth/refresh\n */\nexport async function postRefresh(\n http: HttpClient,\n body: RefreshRequest,\n): Promise<IApiResponse<AuthSessionResponse>> {\n const { data } = await http.post<IApiResponse<AuthSessionResponse>, RefreshRequest>(\n `${AUTH_BASE}/refresh`,\n body,\n );\n return data;\n}\n\n/**\n * POST /api/v1/auth/logout — 204 No Content (no JSON body).\n */\nexport async function postLogout(http: HttpClient, body: LogoutRequest): Promise<void> {\n await http.post<undefined, LogoutRequest>(`${AUTH_BASE}/logout`, body);\n}\n\n/**\n * GET /api/v1/auth/me — requires `Authorization: Bearer <accessToken>`.\n */\nexport async function getMe(\n http: HttpClient,\n accessToken: string,\n): Promise<IApiResponse<AuthUserResponse>> {\n const { data } = await http.get<IApiResponse<AuthUserResponse>>(`${AUTH_BASE}/me`, {\n headers: { Authorization: `Bearer ${accessToken}` },\n });\n return data;\n}\n\n/**\n * PATCH /api/v1/auth/verify-email\n */\nexport async function patchVerifyEmail(\n http: HttpClient,\n body: VerifyEmailRequest,\n): Promise<IApiResponse<void>> {\n const { data } = await http.patch<IApiResponse<void>, VerifyEmailRequest>(\n `${AUTH_BASE}/verify-email`,\n body,\n );\n return data;\n}\n","import type { IApiResponse, HealthCheck } from '@ipetsadmin/contracts';\nimport type { HttpClient } from '../http-client';\n\n/** Same path as `/api/${v1}/health-check` on the server when `v1` is the string `'v1'`. */\nconst HEALTH_CHECK_PATH = '/api/v1/health-check' as const;\n\n/**\n * GET /api/v1/health-check — server liveness.\n *\n * Returns the full JSON the server sends (`IApiResponse<HealthCheck>`).\n *\n * **Why not `data.data` here?** Adapters like Axios wrap the HTTP body in a property also\n * called `data`. That outer `data` is the entire `res.json(...)` object. The inner\n * `IApiResponse.data` is the `HealthCheck` payload — use `.data` on the **returned** value,\n * e.g. `(await fetchHealthCheck(http)).data`.\n */\nexport async function fetchHealthCheck(http: HttpClient): Promise<IApiResponse<HealthCheck>> {\n const { data: body } = await http.get<IApiResponse<HealthCheck>>(HEALTH_CHECK_PATH);\n\n return body;\n}\n","import {\n getGoogleOAuthStart,\n getMe,\n patchVerifyEmail,\n postGoogleOAuthCallback,\n postLogin,\n postLogout,\n postRefresh,\n postRegister,\n} from './endpoints/auth';\nimport { fetchHealthCheck } from './endpoints/health-check';\nimport type { HttpClient } from './http-client';\n\nexport interface CreateApiClientOptions {\n /**\n * Preconfigured HTTP client (e.g. Axios instance with `baseURL` set).\n * Paths in this library are relative to that base (e.g. `/api/v1/...`).\n * Both `get` and `post` are required for auth flows.\n */\n readonly http: HttpClient;\n}\n\nexport interface ApiClient {\n readonly healthCheck: () => ReturnType<typeof fetchHealthCheck>;\n readonly auth: {\n readonly register: (\n body: Parameters<typeof postRegister>[1],\n ) => ReturnType<typeof postRegister>;\n readonly login: (body: Parameters<typeof postLogin>[1]) => ReturnType<typeof postLogin>;\n readonly getGoogleOAuthStart: (redirectUri: string) => ReturnType<typeof getGoogleOAuthStart>;\n readonly postGoogleOAuthCallback: (\n body: Parameters<typeof postGoogleOAuthCallback>[1],\n ) => ReturnType<typeof postGoogleOAuthCallback>;\n readonly refresh: (body: Parameters<typeof postRefresh>[1]) => ReturnType<typeof postRefresh>;\n readonly logout: (body: Parameters<typeof postLogout>[1]) => ReturnType<typeof postLogout>;\n readonly verifyEmail: (\n body: Parameters<typeof patchVerifyEmail>[1],\n ) => ReturnType<typeof patchVerifyEmail>;\n readonly me: (accessToken: string) => ReturnType<typeof getMe>;\n };\n}\n\n/**\n * Creates a typed API client backed by the provided HTTP implementation.\n */\nexport function createApiClient(options: CreateApiClientOptions): ApiClient {\n const { http } = options;\n\n return {\n healthCheck: () => fetchHealthCheck(http),\n auth: {\n register: (body) => postRegister(http, body),\n login: (body) => postLogin(http, body),\n getGoogleOAuthStart: (redirectUri) => getGoogleOAuthStart(http, redirectUri),\n postGoogleOAuthCallback: (body) => postGoogleOAuthCallback(http, body),\n refresh: (body) => postRefresh(http, body),\n logout: (body) => postLogout(http, body),\n verifyEmail: (body) => patchVerifyEmail(http, body),\n me: (accessToken) => getMe(http, accessToken),\n },\n };\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ipetsadmin/api-client",
3
- "version": "1.0.0",
3
+ "version": "1.1.1",
4
4
  "description": "Client used to interact with Truffa project API",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
@@ -38,7 +38,7 @@
38
38
  "author": "",
39
39
  "license": "MIT",
40
40
  "devDependencies": {
41
- "@ipetsadmin/contracts": "^1.0.3",
41
+ "@ipetsadmin/contracts": "1.1.7",
42
42
  "@eslint/js": "^10.0.1",
43
43
  "@types/express": "^5.0.5",
44
44
  "@types/node": "^24.10.0",
@@ -52,7 +52,7 @@
52
52
  "typescript-eslint": "^8.58.0"
53
53
  },
54
54
  "peerDependencies": {
55
- "@ipetsadmin/contracts": "^1.0.0"
55
+ "@ipetsadmin/contracts": ">=1.1.1"
56
56
  },
57
57
  "publishConfig": {
58
58
  "access": "public"