@ipetsadmin/api-client 1.0.0 → 1.1.0
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 +14 -0
- package/README.md +54 -312
- package/dist/index.d.mts +24 -3
- package/dist/index.d.ts +24 -3
- package/dist/index.js +66 -3
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +60 -4
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -19,6 +19,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
19
19
|
|
|
20
20
|
### Security
|
|
21
21
|
|
|
22
|
+
## [1.1.0] - 2026-04-04
|
|
23
|
+
|
|
24
|
+
### Added
|
|
25
|
+
|
|
26
|
+
- **`HttpClient.post`** and **`HttpPost`** type for JSON bodies (Axios-compatible).
|
|
27
|
+
- **Auth endpoints** (`src/endpoints/auth.ts`): `postRegister`, `postLogin`, `getGoogleOAuthStart`, `postGoogleOAuthCallback`, `postRefresh`, `postLogout`, `getMe`.
|
|
28
|
+
- **`createApiClient`** exposes **`auth`** namespace delegating to the auth endpoints.
|
|
29
|
+
- **Peer dependency** on `@ipetsadmin/contracts` **≥ 1.1.1** (align with service ports and Auth0 helper types on the API).
|
|
30
|
+
- README updated with endpoint table and OAuth flow notes.
|
|
31
|
+
|
|
32
|
+
### Changed
|
|
33
|
+
|
|
34
|
+
- **`createApiClient`** now requires an `http` implementation that provides both **`get`** and **`post`** (breaking for consumers that only implemented `get`).
|
|
35
|
+
|
|
22
36
|
## [1.0.0] - 2026-04-02
|
|
23
37
|
|
|
24
38
|
### Added
|
package/README.md
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
# `@ipetsadmin/api-client`
|
|
2
2
|
|
|
3
|
-
Typed HTTP client for the Truffa API.
|
|
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`
|
|
8
|
+
- **Peer dependency:** `@ipetsadmin/contracts` **≥ 1.1.0** (auth types and shared envelopes)
|
|
9
9
|
|
|
10
10
|
## Installation
|
|
11
11
|
|
|
@@ -15,338 +15,82 @@ pnpm add @ipetsadmin/api-client @ipetsadmin/contracts
|
|
|
15
15
|
npm install @ipetsadmin/api-client @ipetsadmin/contracts
|
|
16
16
|
```
|
|
17
17
|
|
|
18
|
-
##
|
|
18
|
+
## What ships today
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
|
23
|
-
|
|
|
24
|
-
| `
|
|
25
|
-
| `
|
|
26
|
-
| `
|
|
27
|
-
| `HttpClient` | Minimal interface your HTTP layer must satisfy (today: **`get` only**). |
|
|
28
|
-
| `HttpGet` | Type of the `get` method. |
|
|
20
|
+
| Export | Description |
|
|
21
|
+
| -------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
22
|
+
| `createApiClient(options)` | Factory returning `healthCheck` + **`auth`** namespace |
|
|
23
|
+
| `ApiClient` / `CreateApiClientOptions` | Typed client and `{ http: HttpClient }` options |
|
|
24
|
+
| `HttpClient` | **`get`** and **`post`** (minimal surface compatible with Axios) |
|
|
25
|
+
| `HttpGet` / `HttpPost` | Method signatures |
|
|
26
|
+
| Named auth helpers | `postRegister`, `postLogin`, `getGoogleOAuthStart`, `postGoogleOAuthCallback`, `postRefresh`, `postLogout`, `getMe` (see `src/endpoints/auth.ts`) |
|
|
29
27
|
|
|
30
28
|
### Endpoints implemented
|
|
31
29
|
|
|
32
|
-
|
|
|
33
|
-
| ------ |
|
|
34
|
-
| `GET` | `/api/v1/health-check`
|
|
30
|
+
| HTTP | Path | Client |
|
|
31
|
+
| ------ | ------------------------------------------------- | ------------------------------------------------------------------------------------------ |
|
|
32
|
+
| `GET` | `/api/v1/health-check` | `client.healthCheck()` |
|
|
33
|
+
| `POST` | `/api/v1/auth/register` | `client.auth.register(body)` or `postRegister(http, body)` |
|
|
34
|
+
| `POST` | `/api/v1/auth/login` | `client.auth.login(body)` or `postLogin(http, body)` |
|
|
35
|
+
| `GET` | `/api/v1/auth/oauth/google/start?redirectUri=...` | `client.auth.getGoogleOAuthStart(redirectUri)` or `getGoogleOAuthStart(http, redirectUri)` |
|
|
36
|
+
| `POST` | `/api/v1/auth/oauth/google/callback` | `client.auth.postGoogleOAuthCallback(body)` or `postGoogleOAuthCallback(http, body)` |
|
|
37
|
+
| `POST` | `/api/v1/auth/refresh` | `client.auth.refresh(body)` or `postRefresh(http, body)` |
|
|
38
|
+
| `POST` | `/api/v1/auth/logout` | `client.auth.logout(body)` or `postLogout(http, body)` |
|
|
39
|
+
| `GET` | `/api/v1/auth/me` | `client.auth.me(accessToken)` or `getMe(http, accessToken)` |
|
|
35
40
|
|
|
36
|
-
|
|
41
|
+
Responses follow **`IApiResponse<T>`** from contracts (`success`, `data`, etc.). Auth session payloads are **`AuthSessionResponse`** (`TokenPair` + `user`). **`logout`** returns **204** with no JSON body.
|
|
37
42
|
|
|
38
43
|
### Usage example (Axios)
|
|
39
44
|
|
|
40
|
-
|
|
45
|
+
Paths are **relative to `baseURL`** (e.g. `https://api.example.com`).
|
|
41
46
|
|
|
42
47
|
```typescript
|
|
43
48
|
import axios from 'axios';
|
|
44
49
|
import { createApiClient } from '@ipetsadmin/api-client';
|
|
45
50
|
|
|
46
51
|
const http = axios.create({
|
|
47
|
-
baseURL: process.env.API_BASE_URL,
|
|
48
|
-
timeout:
|
|
52
|
+
baseURL: process.env.API_BASE_URL,
|
|
53
|
+
timeout: 30_000,
|
|
49
54
|
headers: { 'Content-Type': 'application/json' },
|
|
50
55
|
});
|
|
51
56
|
|
|
52
57
|
const api = createApiClient({ http });
|
|
53
58
|
|
|
54
59
|
const health = await api.healthCheck();
|
|
55
|
-
|
|
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
|
-
}
|
|
268
|
-
```
|
|
269
|
-
|
|
270
|
-
### PUT — full replacement body
|
|
271
|
-
|
|
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
|
-
}
|
|
60
|
+
const session = await api.auth.login({ email: 'user@example.com', password: '********' });
|
|
61
|
+
// session.data — IApiResponse<AuthSessionResponse>; use session.data?.data for the inner payload when using Axios default response shape
|
|
286
62
|
```
|
|
287
63
|
|
|
288
|
-
|
|
64
|
+
Axios returns `{ data: body }` where `body` is the full JSON from the server. For `IApiResponse<AuthSessionResponse>`, the **envelope** is in `response.data`; the **`AuthSessionResponse`** is typically `response.data.data` (depending on your typings).
|
|
289
65
|
|
|
290
|
-
|
|
66
|
+
### OAuth (mobile / web)
|
|
291
67
|
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
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
|
-
```
|
|
68
|
+
1. Call **`getGoogleOAuthStart`** with the same **`redirectUri`** you registered in Auth0 and in the API’s `OAUTH_ALLOWED_REDIRECT_URIS`.
|
|
69
|
+
2. Open **`authorizationUrl`** in a browser / `ASWebAuthenticationSession`.
|
|
70
|
+
3. After redirect, **`POST`** the **`code`**, **`state`**, and **`redirectUri`** to **`postGoogleOAuthCallback`**.
|
|
303
71
|
|
|
304
|
-
|
|
72
|
+
### Why a factory?
|
|
305
73
|
|
|
306
|
-
|
|
307
|
-
await http.delete<IApiResponse<void>>(path, {
|
|
308
|
-
data: { reason: 'user_request' },
|
|
309
|
-
headers: { 'Content-Type': 'application/json' },
|
|
310
|
-
});
|
|
311
|
-
```
|
|
74
|
+
`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
75
|
|
|
313
76
|
---
|
|
314
77
|
|
|
315
|
-
##
|
|
78
|
+
## Architecture
|
|
316
79
|
|
|
317
|
-
1.
|
|
318
|
-
2.
|
|
319
|
-
3.
|
|
80
|
+
1. **`HttpClient`** — abstraction over GET/POST (`{ data: T }` shape, Axios-compatible).
|
|
81
|
+
2. **`src/endpoints/*.ts`** — thin functions: `(http, …args) => Promise<…>`.
|
|
82
|
+
3. **`createApiClient`** — wires `http` into endpoints.
|
|
83
|
+
4. **`@ipetsadmin/contracts`** — DTOs (`RegisterRequest`, `AuthSessionResponse`, …).
|
|
320
84
|
|
|
321
|
-
|
|
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
|
-
```
|
|
85
|
+
### Extending further
|
|
340
86
|
|
|
341
|
-
|
|
87
|
+
If you add PATCH/PUT/DELETE, extend **`HttpClient`** in `src/http-client.ts` and add endpoint modules. For request config (query params, headers), align types with your adapter (Axios uses a second `config` argument on `get`/`post`).
|
|
342
88
|
|
|
343
89
|
---
|
|
344
90
|
|
|
345
91
|
## Testing
|
|
346
92
|
|
|
347
|
-
Inject a fake
|
|
348
|
-
|
|
349
|
-
**With the current `HttpClient` (only `get`):**
|
|
93
|
+
Inject a fake **`HttpClient`** with **`get`** and **`post`** mocks and assert URLs and payloads.
|
|
350
94
|
|
|
351
95
|
```typescript
|
|
352
96
|
import type { HttpClient } from '@ipetsadmin/api-client';
|
|
@@ -354,34 +98,32 @@ import { createApiClient } from '@ipetsadmin/api-client';
|
|
|
354
98
|
|
|
355
99
|
const http: HttpClient = {
|
|
356
100
|
get: jest.fn().mockResolvedValue({ data: { success: true, data: {} } }),
|
|
101
|
+
post: jest.fn().mockResolvedValue({ data: { success: true, data: {} } }),
|
|
357
102
|
};
|
|
358
103
|
|
|
359
104
|
const api = createApiClient({ http });
|
|
360
105
|
await api.healthCheck();
|
|
361
|
-
|
|
362
106
|
expect(http.get).toHaveBeenCalledWith('/api/v1/health-check', undefined);
|
|
363
107
|
```
|
|
364
108
|
|
|
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
109
|
---
|
|
368
110
|
|
|
369
111
|
## Development (this repository)
|
|
370
112
|
|
|
371
|
-
| Script | Purpose
|
|
372
|
-
| ------------------------- |
|
|
373
|
-
| `pnpm run build` |
|
|
374
|
-
| `pnpm run typecheck` | `tsc --noEmit`
|
|
375
|
-
| `pnpm run lint` | ESLint
|
|
376
|
-
| `pnpm run validate` | typecheck + lint + Prettier check
|
|
377
|
-
| `pnpm run prepublishOnly` |
|
|
113
|
+
| Script | Purpose |
|
|
114
|
+
| ------------------------- | ------------------------------------ |
|
|
115
|
+
| `pnpm run build` | `tsup` → `dist/` (CJS, ESM, `.d.ts`) |
|
|
116
|
+
| `pnpm run typecheck` | `tsc --noEmit` |
|
|
117
|
+
| `pnpm run lint` | ESLint |
|
|
118
|
+
| `pnpm run validate` | typecheck + lint + Prettier check |
|
|
119
|
+
| `pnpm run prepublishOnly` | validate + build before publish |
|
|
378
120
|
|
|
379
121
|
---
|
|
380
122
|
|
|
381
|
-
##
|
|
123
|
+
## Changelog
|
|
124
|
+
|
|
125
|
+
See [CHANGELOG.md](./CHANGELOG.md).
|
|
382
126
|
|
|
383
|
-
|
|
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.
|
|
127
|
+
## License
|
|
386
128
|
|
|
387
|
-
|
|
129
|
+
MIT — see [LICENSE](./LICENSE).
|
package/dist/index.d.mts
CHANGED
|
@@ -1,20 +1,41 @@
|
|
|
1
|
-
import { IApiResponse } from '@ipetsadmin/contracts';
|
|
1
|
+
import { RegisterRequest, IApiResponse, AuthSessionResponse, LoginRequest, OAuthGoogleStartResponse, OAuthGoogleCallbackRequest, RefreshRequest, LogoutRequest, 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
|
+
}>;
|
|
6
9
|
interface HttpClient {
|
|
7
10
|
readonly get: HttpGet;
|
|
11
|
+
readonly post: HttpPost;
|
|
8
12
|
}
|
|
9
13
|
|
|
10
|
-
declare function
|
|
14
|
+
declare function postRegister(http: HttpClient, body: RegisterRequest): Promise<IApiResponse<AuthSessionResponse>>;
|
|
15
|
+
declare function postLogin(http: HttpClient, body: LoginRequest): Promise<IApiResponse<AuthSessionResponse>>;
|
|
16
|
+
declare function getGoogleOAuthStart(http: HttpClient, redirectUri: string): Promise<IApiResponse<OAuthGoogleStartResponse>>;
|
|
17
|
+
declare function postGoogleOAuthCallback(http: HttpClient, body: OAuthGoogleCallbackRequest): Promise<IApiResponse<AuthSessionResponse>>;
|
|
18
|
+
declare function postRefresh(http: HttpClient, body: RefreshRequest): Promise<IApiResponse<AuthSessionResponse>>;
|
|
19
|
+
declare function postLogout(http: HttpClient, body: LogoutRequest): Promise<void>;
|
|
20
|
+
declare function getMe(http: HttpClient, accessToken: string): Promise<IApiResponse<AuthUserResponse>>;
|
|
21
|
+
|
|
22
|
+
declare function fetchHealthCheck(http: HttpClient): Promise<IApiResponse<HealthCheck>>;
|
|
11
23
|
|
|
12
24
|
interface CreateApiClientOptions {
|
|
13
25
|
readonly http: HttpClient;
|
|
14
26
|
}
|
|
15
27
|
interface ApiClient {
|
|
16
28
|
readonly healthCheck: () => ReturnType<typeof fetchHealthCheck>;
|
|
29
|
+
readonly auth: {
|
|
30
|
+
readonly register: (body: Parameters<typeof postRegister>[1]) => ReturnType<typeof postRegister>;
|
|
31
|
+
readonly login: (body: Parameters<typeof postLogin>[1]) => ReturnType<typeof postLogin>;
|
|
32
|
+
readonly getGoogleOAuthStart: (redirectUri: string) => ReturnType<typeof getGoogleOAuthStart>;
|
|
33
|
+
readonly postGoogleOAuthCallback: (body: Parameters<typeof postGoogleOAuthCallback>[1]) => ReturnType<typeof postGoogleOAuthCallback>;
|
|
34
|
+
readonly refresh: (body: Parameters<typeof postRefresh>[1]) => ReturnType<typeof postRefresh>;
|
|
35
|
+
readonly logout: (body: Parameters<typeof postLogout>[1]) => ReturnType<typeof postLogout>;
|
|
36
|
+
readonly me: (accessToken: string) => ReturnType<typeof getMe>;
|
|
37
|
+
};
|
|
17
38
|
}
|
|
18
39
|
declare function createApiClient(options: CreateApiClientOptions): ApiClient;
|
|
19
40
|
|
|
20
|
-
export { type ApiClient, type CreateApiClientOptions, type HttpClient, type HttpGet, createApiClient };
|
|
41
|
+
export { type ApiClient, type CreateApiClientOptions, type HttpClient, type HttpGet, type HttpPost, createApiClient, getGoogleOAuthStart, getMe, postGoogleOAuthCallback, postLogin, postLogout, postRefresh, postRegister };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,20 +1,41 @@
|
|
|
1
|
-
import { IApiResponse } from '@ipetsadmin/contracts';
|
|
1
|
+
import { RegisterRequest, IApiResponse, AuthSessionResponse, LoginRequest, OAuthGoogleStartResponse, OAuthGoogleCallbackRequest, RefreshRequest, LogoutRequest, 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
|
+
}>;
|
|
6
9
|
interface HttpClient {
|
|
7
10
|
readonly get: HttpGet;
|
|
11
|
+
readonly post: HttpPost;
|
|
8
12
|
}
|
|
9
13
|
|
|
10
|
-
declare function
|
|
14
|
+
declare function postRegister(http: HttpClient, body: RegisterRequest): Promise<IApiResponse<AuthSessionResponse>>;
|
|
15
|
+
declare function postLogin(http: HttpClient, body: LoginRequest): Promise<IApiResponse<AuthSessionResponse>>;
|
|
16
|
+
declare function getGoogleOAuthStart(http: HttpClient, redirectUri: string): Promise<IApiResponse<OAuthGoogleStartResponse>>;
|
|
17
|
+
declare function postGoogleOAuthCallback(http: HttpClient, body: OAuthGoogleCallbackRequest): Promise<IApiResponse<AuthSessionResponse>>;
|
|
18
|
+
declare function postRefresh(http: HttpClient, body: RefreshRequest): Promise<IApiResponse<AuthSessionResponse>>;
|
|
19
|
+
declare function postLogout(http: HttpClient, body: LogoutRequest): Promise<void>;
|
|
20
|
+
declare function getMe(http: HttpClient, accessToken: string): Promise<IApiResponse<AuthUserResponse>>;
|
|
21
|
+
|
|
22
|
+
declare function fetchHealthCheck(http: HttpClient): Promise<IApiResponse<HealthCheck>>;
|
|
11
23
|
|
|
12
24
|
interface CreateApiClientOptions {
|
|
13
25
|
readonly http: HttpClient;
|
|
14
26
|
}
|
|
15
27
|
interface ApiClient {
|
|
16
28
|
readonly healthCheck: () => ReturnType<typeof fetchHealthCheck>;
|
|
29
|
+
readonly auth: {
|
|
30
|
+
readonly register: (body: Parameters<typeof postRegister>[1]) => ReturnType<typeof postRegister>;
|
|
31
|
+
readonly login: (body: Parameters<typeof postLogin>[1]) => ReturnType<typeof postLogin>;
|
|
32
|
+
readonly getGoogleOAuthStart: (redirectUri: string) => ReturnType<typeof getGoogleOAuthStart>;
|
|
33
|
+
readonly postGoogleOAuthCallback: (body: Parameters<typeof postGoogleOAuthCallback>[1]) => ReturnType<typeof postGoogleOAuthCallback>;
|
|
34
|
+
readonly refresh: (body: Parameters<typeof postRefresh>[1]) => ReturnType<typeof postRefresh>;
|
|
35
|
+
readonly logout: (body: Parameters<typeof postLogout>[1]) => ReturnType<typeof postLogout>;
|
|
36
|
+
readonly me: (accessToken: string) => ReturnType<typeof getMe>;
|
|
37
|
+
};
|
|
17
38
|
}
|
|
18
39
|
declare function createApiClient(options: CreateApiClientOptions): ApiClient;
|
|
19
40
|
|
|
20
|
-
export { type ApiClient, type CreateApiClientOptions, type HttpClient, type HttpGet, createApiClient };
|
|
41
|
+
export { type ApiClient, type CreateApiClientOptions, type HttpClient, type HttpGet, type HttpPost, createApiClient, getGoogleOAuthStart, getMe, postGoogleOAuthCallback, postLogin, postLogout, postRefresh, postRegister };
|
package/dist/index.js
CHANGED
|
@@ -1,20 +1,83 @@
|
|
|
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
|
+
|
|
3
50
|
// src/endpoints/health-check.ts
|
|
4
51
|
var HEALTH_CHECK_PATH = "/api/v1/health-check";
|
|
5
52
|
async function fetchHealthCheck(http) {
|
|
6
|
-
const { data } = await http.get(HEALTH_CHECK_PATH);
|
|
7
|
-
return
|
|
53
|
+
const { data: body } = await http.get(HEALTH_CHECK_PATH);
|
|
54
|
+
return body;
|
|
8
55
|
}
|
|
9
56
|
|
|
10
57
|
// src/create-api-client.ts
|
|
11
58
|
function createApiClient(options) {
|
|
12
59
|
const { http } = options;
|
|
13
60
|
return {
|
|
14
|
-
healthCheck: () => fetchHealthCheck(http)
|
|
61
|
+
healthCheck: () => fetchHealthCheck(http),
|
|
62
|
+
auth: {
|
|
63
|
+
register: (body) => postRegister(http, body),
|
|
64
|
+
login: (body) => postLogin(http, body),
|
|
65
|
+
getGoogleOAuthStart: (redirectUri) => getGoogleOAuthStart(http, redirectUri),
|
|
66
|
+
postGoogleOAuthCallback: (body) => postGoogleOAuthCallback(http, body),
|
|
67
|
+
refresh: (body) => postRefresh(http, body),
|
|
68
|
+
logout: (body) => postLogout(http, body),
|
|
69
|
+
me: (accessToken) => getMe(http, accessToken)
|
|
70
|
+
}
|
|
15
71
|
};
|
|
16
72
|
}
|
|
17
73
|
|
|
18
74
|
exports.createApiClient = createApiClient;
|
|
75
|
+
exports.getGoogleOAuthStart = getGoogleOAuthStart;
|
|
76
|
+
exports.getMe = getMe;
|
|
77
|
+
exports.postGoogleOAuthCallback = postGoogleOAuthCallback;
|
|
78
|
+
exports.postLogin = postLogin;
|
|
79
|
+
exports.postLogout = postLogout;
|
|
80
|
+
exports.postRefresh = postRefresh;
|
|
81
|
+
exports.postRegister = postRegister;
|
|
19
82
|
//# sourceMappingURL=index.js.map
|
|
20
83
|
//# 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":";;;
|
|
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} 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","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 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 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 me: (accessToken) => getMe(http, accessToken),\n },\n };\n}\n"]}
|
package/dist/index.mjs
CHANGED
|
@@ -1,18 +1,74 @@
|
|
|
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
|
+
|
|
1
48
|
// src/endpoints/health-check.ts
|
|
2
49
|
var HEALTH_CHECK_PATH = "/api/v1/health-check";
|
|
3
50
|
async function fetchHealthCheck(http) {
|
|
4
|
-
const { data } = await http.get(HEALTH_CHECK_PATH);
|
|
5
|
-
return
|
|
51
|
+
const { data: body } = await http.get(HEALTH_CHECK_PATH);
|
|
52
|
+
return body;
|
|
6
53
|
}
|
|
7
54
|
|
|
8
55
|
// src/create-api-client.ts
|
|
9
56
|
function createApiClient(options) {
|
|
10
57
|
const { http } = options;
|
|
11
58
|
return {
|
|
12
|
-
healthCheck: () => fetchHealthCheck(http)
|
|
59
|
+
healthCheck: () => fetchHealthCheck(http),
|
|
60
|
+
auth: {
|
|
61
|
+
register: (body) => postRegister(http, body),
|
|
62
|
+
login: (body) => postLogin(http, body),
|
|
63
|
+
getGoogleOAuthStart: (redirectUri) => getGoogleOAuthStart(http, redirectUri),
|
|
64
|
+
postGoogleOAuthCallback: (body) => postGoogleOAuthCallback(http, body),
|
|
65
|
+
refresh: (body) => postRefresh(http, body),
|
|
66
|
+
logout: (body) => postLogout(http, body),
|
|
67
|
+
me: (accessToken) => getMe(http, accessToken)
|
|
68
|
+
}
|
|
13
69
|
};
|
|
14
70
|
}
|
|
15
71
|
|
|
16
|
-
export { createApiClient };
|
|
72
|
+
export { createApiClient, getGoogleOAuthStart, getMe, postGoogleOAuthCallback, postLogin, postLogout, postRefresh, postRegister };
|
|
17
73
|
//# sourceMappingURL=index.mjs.map
|
|
18
74
|
//# sourceMappingURL=index.mjs.map
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/endpoints/health-check.ts","../src/create-api-client.ts"],"names":[],"mappings":";
|
|
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} 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","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 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 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 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.
|
|
3
|
+
"version": "1.1.0",
|
|
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": "
|
|
41
|
+
"@ipetsadmin/contracts": "1.1.2",
|
|
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": "
|
|
55
|
+
"@ipetsadmin/contracts": ">=1.1.1"
|
|
56
56
|
},
|
|
57
57
|
"publishConfig": {
|
|
58
58
|
"access": "public"
|