@simplix-react/contract 0.0.1 → 0.0.3
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/README.md +309 -0
- package/dist/index.d.ts +606 -11
- package/dist/index.js +99 -12
- package/package.json +19 -20
- package/dist/mcp-tools.d.ts +0 -14
- package/dist/mcp-tools.js +0 -62
- package/dist/openapi.d.ts +0 -21
- package/dist/openapi.js +0 -54
- package/dist/types-tFXBXgJP.d.ts +0 -41
package/dist/index.d.ts
CHANGED
|
@@ -1,62 +1,474 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Describes a single sort directive with field name and direction.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```ts
|
|
8
|
+
* import type { SortParam } from "@simplix-react/contract";
|
|
9
|
+
*
|
|
10
|
+
* const sort: SortParam = { field: "createdAt", direction: "desc" };
|
|
11
|
+
* ```
|
|
12
|
+
*/
|
|
13
|
+
interface SortParam {
|
|
14
|
+
/** The field name to sort by. */
|
|
15
|
+
field: string;
|
|
16
|
+
/** Sort direction: ascending or descending. */
|
|
17
|
+
direction: "asc" | "desc";
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Describes pagination strategy, supporting both offset-based and cursor-based patterns.
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```ts
|
|
24
|
+
* import type { PaginationParam } from "@simplix-react/contract";
|
|
25
|
+
*
|
|
26
|
+
* // Offset-based
|
|
27
|
+
* const offset: PaginationParam = { type: "offset", page: 1, limit: 20 };
|
|
28
|
+
*
|
|
29
|
+
* // Cursor-based
|
|
30
|
+
* const cursor: PaginationParam = { type: "cursor", cursor: "abc123", limit: 20 };
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
type PaginationParam = {
|
|
34
|
+
type: "offset";
|
|
35
|
+
page: number;
|
|
36
|
+
limit: number;
|
|
37
|
+
} | {
|
|
38
|
+
type: "cursor";
|
|
39
|
+
cursor: string;
|
|
40
|
+
limit: number;
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* Encapsulates all list query parameters: filters, sorting, and pagination.
|
|
44
|
+
*
|
|
45
|
+
* Passed to entity `list()` methods and serialized into URL search params
|
|
46
|
+
* by a {@link QueryBuilder}.
|
|
47
|
+
*
|
|
48
|
+
* @typeParam TFilters - Shape of the filter object, defaults to `Record<string, unknown>`.
|
|
49
|
+
*
|
|
50
|
+
* @example
|
|
51
|
+
* ```ts
|
|
52
|
+
* import type { ListParams } from "@simplix-react/contract";
|
|
53
|
+
*
|
|
54
|
+
* const params: ListParams = {
|
|
55
|
+
* filters: { status: "active" },
|
|
56
|
+
* sort: { field: "title", direction: "asc" },
|
|
57
|
+
* pagination: { type: "offset", page: 1, limit: 10 },
|
|
58
|
+
* };
|
|
59
|
+
*
|
|
60
|
+
* await api.client.task.list(params);
|
|
61
|
+
* ```
|
|
62
|
+
*/
|
|
63
|
+
interface ListParams<TFilters = Record<string, unknown>> {
|
|
64
|
+
/** Optional filter criteria applied to the list query. */
|
|
65
|
+
filters?: TFilters;
|
|
66
|
+
/** Single sort directive or array of sort directives. */
|
|
67
|
+
sort?: SortParam | SortParam[];
|
|
68
|
+
/** Pagination strategy and parameters. */
|
|
69
|
+
pagination?: PaginationParam;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Describes pagination metadata returned from the server.
|
|
73
|
+
*
|
|
74
|
+
* Used by {@link QueryBuilder.parsePageInfo} to extract pagination state
|
|
75
|
+
* from API responses, enabling infinite scroll and paginated UIs.
|
|
76
|
+
*
|
|
77
|
+
* @example
|
|
78
|
+
* ```ts
|
|
79
|
+
* import type { PageInfo } from "@simplix-react/contract";
|
|
80
|
+
*
|
|
81
|
+
* const pageInfo: PageInfo = {
|
|
82
|
+
* total: 42,
|
|
83
|
+
* hasNextPage: true,
|
|
84
|
+
* nextCursor: "cursor-xyz",
|
|
85
|
+
* };
|
|
86
|
+
* ```
|
|
87
|
+
*/
|
|
88
|
+
interface PageInfo {
|
|
89
|
+
/** Total number of items across all pages (if the server provides it). */
|
|
90
|
+
total?: number;
|
|
91
|
+
/** Whether more items exist beyond the current page. */
|
|
92
|
+
hasNextPage: boolean;
|
|
93
|
+
/** Cursor value to fetch the next page (cursor-based pagination only). */
|
|
94
|
+
nextCursor?: string;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Defines how list parameters are serialized to URL search params and how
|
|
98
|
+
* pagination metadata is extracted from API responses.
|
|
99
|
+
*
|
|
100
|
+
* Implement this interface to adapt the framework to your API's query string
|
|
101
|
+
* conventions. Use {@link simpleQueryBuilder} as a ready-made implementation
|
|
102
|
+
* for common REST patterns.
|
|
103
|
+
*
|
|
104
|
+
* @example
|
|
105
|
+
* ```ts
|
|
106
|
+
* import type { QueryBuilder } from "@simplix-react/contract";
|
|
107
|
+
*
|
|
108
|
+
* const customQueryBuilder: QueryBuilder = {
|
|
109
|
+
* buildSearchParams(params) {
|
|
110
|
+
* const sp = new URLSearchParams();
|
|
111
|
+
* if (params.pagination?.type === "offset") {
|
|
112
|
+
* sp.set("offset", String((params.pagination.page - 1) * params.pagination.limit));
|
|
113
|
+
* sp.set("limit", String(params.pagination.limit));
|
|
114
|
+
* }
|
|
115
|
+
* return sp;
|
|
116
|
+
* },
|
|
117
|
+
* parsePageInfo(response) {
|
|
118
|
+
* const { total, nextCursor } = response as any;
|
|
119
|
+
* return { total, hasNextPage: !!nextCursor, nextCursor };
|
|
120
|
+
* },
|
|
121
|
+
* };
|
|
122
|
+
* ```
|
|
123
|
+
*
|
|
124
|
+
* @see {@link simpleQueryBuilder} for the built-in implementation.
|
|
125
|
+
*/
|
|
126
|
+
interface QueryBuilder {
|
|
127
|
+
/** Converts structured list parameters into URL search params. */
|
|
128
|
+
buildSearchParams(params: ListParams): URLSearchParams;
|
|
129
|
+
/** Extracts pagination metadata from an API response (optional). */
|
|
130
|
+
parsePageInfo?(response: unknown): PageInfo;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Describes the parent resource in a nested entity relationship.
|
|
135
|
+
*
|
|
136
|
+
* Enables hierarchical URL construction such as `/projects/:projectId/tasks`.
|
|
137
|
+
*
|
|
138
|
+
* @example
|
|
139
|
+
* ```ts
|
|
140
|
+
* const parent: EntityParent = {
|
|
141
|
+
* param: "projectId",
|
|
142
|
+
* path: "/projects",
|
|
143
|
+
* };
|
|
144
|
+
* // Produces: /projects/:projectId/tasks
|
|
145
|
+
* ```
|
|
146
|
+
*
|
|
147
|
+
* @see {@link EntityDefinition} for the entity that references this parent.
|
|
148
|
+
*/
|
|
3
149
|
interface EntityParent {
|
|
150
|
+
/** Route parameter name used to identify the parent resource (e.g. `"projectId"`). */
|
|
4
151
|
param: string;
|
|
152
|
+
/** Base path segment for the parent resource (e.g. `"/projects"`). */
|
|
5
153
|
path: string;
|
|
6
154
|
}
|
|
155
|
+
/**
|
|
156
|
+
* Represents a named query scope that filters entities by a parent relationship.
|
|
157
|
+
*
|
|
158
|
+
* Allows defining reusable query patterns like "tasks by project" that can be
|
|
159
|
+
* referenced throughout the application.
|
|
160
|
+
*
|
|
161
|
+
* @see {@link EntityDefinition.queries} where these are declared.
|
|
162
|
+
*/
|
|
7
163
|
interface EntityQuery {
|
|
164
|
+
/** Name of the parent entity this query filters by (e.g. `"project"`). */
|
|
8
165
|
parent: string;
|
|
166
|
+
/** Route parameter name used to scope the query (e.g. `"projectId"`). */
|
|
9
167
|
param: string;
|
|
10
168
|
}
|
|
169
|
+
/**
|
|
170
|
+
* Defines a CRUD-capable API entity with Zod schemas for type-safe validation.
|
|
171
|
+
*
|
|
172
|
+
* Serves as the single source of truth for an entity's shape, creation payload,
|
|
173
|
+
* update payload, and URL structure. The framework derives API clients, React Query
|
|
174
|
+
* hooks, and MSW handlers from this definition.
|
|
175
|
+
*
|
|
176
|
+
* @typeParam TSchema - Zod schema for the entity's response shape.
|
|
177
|
+
* @typeParam TCreate - Zod schema for the creation payload.
|
|
178
|
+
* @typeParam TUpdate - Zod schema for the update (partial) payload.
|
|
179
|
+
*
|
|
180
|
+
* @example
|
|
181
|
+
* ```ts
|
|
182
|
+
* import { z } from "zod";
|
|
183
|
+
* import type { EntityDefinition } from "@simplix-react/contract";
|
|
184
|
+
*
|
|
185
|
+
* const taskEntity: EntityDefinition = {
|
|
186
|
+
* path: "/tasks",
|
|
187
|
+
* schema: z.object({ id: z.string(), title: z.string() }),
|
|
188
|
+
* createSchema: z.object({ title: z.string() }),
|
|
189
|
+
* updateSchema: z.object({ title: z.string().optional() }),
|
|
190
|
+
* parent: { param: "projectId", path: "/projects" },
|
|
191
|
+
* };
|
|
192
|
+
* ```
|
|
193
|
+
*
|
|
194
|
+
* @see {@link OperationDefinition} for non-CRUD custom operations.
|
|
195
|
+
* @see {@link @simplix-react/react!deriveHooks | deriveHooks} for deriving React Query hooks.
|
|
196
|
+
* @see {@link @simplix-react/mock!deriveMockHandlers | deriveMockHandlers} for deriving MSW handlers.
|
|
197
|
+
*/
|
|
11
198
|
interface EntityDefinition<TSchema extends z.ZodType = z.ZodType, TCreate extends z.ZodType = z.ZodType, TUpdate extends z.ZodType = z.ZodType> {
|
|
199
|
+
/** URL path segment for this entity (e.g. `"/tasks"`). */
|
|
12
200
|
path: string;
|
|
201
|
+
/** Zod schema describing the full entity shape returned by the API. */
|
|
13
202
|
schema: TSchema;
|
|
203
|
+
/** Zod schema describing the payload required to create a new entity. */
|
|
14
204
|
createSchema: TCreate;
|
|
205
|
+
/** Zod schema describing the payload for updating an existing entity. */
|
|
15
206
|
updateSchema: TUpdate;
|
|
207
|
+
/** Optional parent resource for nested URL construction. */
|
|
16
208
|
parent?: EntityParent;
|
|
209
|
+
/** Named query scopes for filtering entities by parent relationships. */
|
|
17
210
|
queries?: Record<string, EntityQuery>;
|
|
211
|
+
/** Optional Zod schema for validating list filter parameters. */
|
|
212
|
+
filterSchema?: z.ZodType;
|
|
18
213
|
}
|
|
214
|
+
/**
|
|
215
|
+
* Supported HTTP methods for API operations.
|
|
216
|
+
*/
|
|
19
217
|
type HttpMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
|
|
218
|
+
/**
|
|
219
|
+
* Defines a custom (non-CRUD) API operation with typed input and output.
|
|
220
|
+
*
|
|
221
|
+
* Covers endpoints that do not fit the standard entity CRUD pattern, such as
|
|
222
|
+
* file uploads, batch operations, or RPC-style calls. Path parameters use
|
|
223
|
+
* the `:paramName` syntax and are positionally mapped to function arguments.
|
|
224
|
+
*
|
|
225
|
+
* @typeParam TInput - Zod schema for the request payload.
|
|
226
|
+
* @typeParam TOutput - Zod schema for the response payload.
|
|
227
|
+
*
|
|
228
|
+
* @example
|
|
229
|
+
* ```ts
|
|
230
|
+
* import { z } from "zod";
|
|
231
|
+
* import type { OperationDefinition } from "@simplix-react/contract";
|
|
232
|
+
*
|
|
233
|
+
* const assignTask: OperationDefinition = {
|
|
234
|
+
* method: "POST",
|
|
235
|
+
* path: "/tasks/:taskId/assign",
|
|
236
|
+
* input: z.object({ userId: z.string() }),
|
|
237
|
+
* output: z.object({ id: z.string(), assigneeId: z.string() }),
|
|
238
|
+
* };
|
|
239
|
+
* ```
|
|
240
|
+
*
|
|
241
|
+
* @see {@link EntityDefinition} for standard CRUD entities.
|
|
242
|
+
*/
|
|
20
243
|
interface OperationDefinition<TInput extends z.ZodType = z.ZodType, TOutput extends z.ZodType = z.ZodType> {
|
|
244
|
+
/** HTTP method for this operation. */
|
|
21
245
|
method: HttpMethod;
|
|
246
|
+
/** URL path with optional `:paramName` placeholders (e.g. `"/tasks/:taskId/assign"`). */
|
|
22
247
|
path: string;
|
|
248
|
+
/** Zod schema validating the request payload. */
|
|
23
249
|
input: TInput;
|
|
250
|
+
/** Zod schema validating the response payload. */
|
|
24
251
|
output: TOutput;
|
|
252
|
+
/** Content type for the request body. Defaults to `"json"`. */
|
|
253
|
+
contentType?: "json" | "multipart";
|
|
254
|
+
/** Expected response format. Defaults to `"json"`. */
|
|
255
|
+
responseType?: "json" | "blob";
|
|
256
|
+
/**
|
|
257
|
+
* Returns query key arrays that should be invalidated after this operation succeeds.
|
|
258
|
+
* Enables automatic cache invalidation in `@simplix-react/react`.
|
|
259
|
+
*/
|
|
25
260
|
invalidates?: (queryKeys: Record<string, QueryKeyFactory>, params: Record<string, string>) => readonly unknown[][];
|
|
26
261
|
}
|
|
262
|
+
/**
|
|
263
|
+
* Configures a complete API contract with entities, operations, and shared settings.
|
|
264
|
+
*
|
|
265
|
+
* Serves as the input to {@link defineApi}, grouping all entity and operation
|
|
266
|
+
* definitions under a single domain namespace with a shared base path.
|
|
267
|
+
*
|
|
268
|
+
* @typeParam TEntities - Map of entity names to their definitions.
|
|
269
|
+
* @typeParam TOperations - Map of operation names to their definitions.
|
|
270
|
+
*
|
|
271
|
+
* @example
|
|
272
|
+
* ```ts
|
|
273
|
+
* import { z } from "zod";
|
|
274
|
+
* import { simpleQueryBuilder } from "@simplix-react/contract";
|
|
275
|
+
* import type { ApiContractConfig } from "@simplix-react/contract";
|
|
276
|
+
*
|
|
277
|
+
* const config: ApiContractConfig = {
|
|
278
|
+
* domain: "project",
|
|
279
|
+
* basePath: "/api/v1",
|
|
280
|
+
* entities: {
|
|
281
|
+
* task: {
|
|
282
|
+
* path: "/tasks",
|
|
283
|
+
* schema: z.object({ id: z.string(), title: z.string() }),
|
|
284
|
+
* createSchema: z.object({ title: z.string() }),
|
|
285
|
+
* updateSchema: z.object({ title: z.string().optional() }),
|
|
286
|
+
* },
|
|
287
|
+
* },
|
|
288
|
+
* queryBuilder: simpleQueryBuilder,
|
|
289
|
+
* };
|
|
290
|
+
* ```
|
|
291
|
+
*
|
|
292
|
+
* @see {@link defineApi} for constructing a contract from this config.
|
|
293
|
+
*/
|
|
27
294
|
interface ApiContractConfig<TEntities extends Record<string, EntityDefinition<any, any, any>> = Record<string, EntityDefinition>, TOperations extends Record<string, OperationDefinition<any, any>> = Record<string, OperationDefinition>> {
|
|
295
|
+
/** Logical domain name used as the root segment in query keys (e.g. `"project"`). */
|
|
28
296
|
domain: string;
|
|
297
|
+
/** Base URL path prepended to all entity and operation paths (e.g. `"/api/v1"`). */
|
|
29
298
|
basePath: string;
|
|
299
|
+
/** Map of entity names to their CRUD definitions. */
|
|
30
300
|
entities: TEntities;
|
|
301
|
+
/** Optional map of custom operation names to their definitions. */
|
|
31
302
|
operations?: TOperations;
|
|
303
|
+
/** Strategy for serializing list parameters (filters, sort, pagination) into URL search params. */
|
|
304
|
+
queryBuilder?: QueryBuilder;
|
|
32
305
|
}
|
|
306
|
+
/**
|
|
307
|
+
* Provides structured query key generators for a single entity, following the
|
|
308
|
+
* query key factory pattern recommended by TanStack Query.
|
|
309
|
+
*
|
|
310
|
+
* Generated automatically by {@link deriveQueryKeys} and used by `@simplix-react/react`
|
|
311
|
+
* to manage cache granularity and invalidation.
|
|
312
|
+
*
|
|
313
|
+
* @example
|
|
314
|
+
* ```ts
|
|
315
|
+
* import { defineApi } from "@simplix-react/contract";
|
|
316
|
+
*
|
|
317
|
+
* const api = defineApi(config);
|
|
318
|
+
*
|
|
319
|
+
* api.queryKeys.task.all; // ["project", "task"]
|
|
320
|
+
* api.queryKeys.task.lists(); // ["project", "task", "list"]
|
|
321
|
+
* api.queryKeys.task.list({ status: "open" }); // ["project", "task", "list", { status: "open" }]
|
|
322
|
+
* api.queryKeys.task.details(); // ["project", "task", "detail"]
|
|
323
|
+
* api.queryKeys.task.detail("abc"); // ["project", "task", "detail", "abc"]
|
|
324
|
+
* ```
|
|
325
|
+
*
|
|
326
|
+
* @see {@link deriveQueryKeys} for the factory function.
|
|
327
|
+
*/
|
|
33
328
|
interface QueryKeyFactory {
|
|
329
|
+
/** Root key matching all queries for this entity: `[domain, entity]`. */
|
|
34
330
|
all: readonly unknown[];
|
|
331
|
+
/** Returns key matching all list queries: `[domain, entity, "list"]`. */
|
|
35
332
|
lists: () => readonly unknown[];
|
|
333
|
+
/** Returns key matching a specific list query with parameters. */
|
|
36
334
|
list: (params: Record<string, unknown>) => readonly unknown[];
|
|
335
|
+
/** Returns key matching all detail queries: `[domain, entity, "detail"]`. */
|
|
37
336
|
details: () => readonly unknown[];
|
|
337
|
+
/** Returns key matching a specific detail query by ID. */
|
|
38
338
|
detail: (id: string) => readonly unknown[];
|
|
39
339
|
}
|
|
340
|
+
/**
|
|
341
|
+
* Provides a type-safe CRUD client for a single entity, derived from its
|
|
342
|
+
* {@link EntityDefinition} schemas.
|
|
343
|
+
*
|
|
344
|
+
* All methods infer request/response types directly from the Zod schemas,
|
|
345
|
+
* ensuring compile-time safety without manual type annotations.
|
|
346
|
+
*
|
|
347
|
+
* @typeParam TSchema - Zod schema for the entity's response shape.
|
|
348
|
+
* @typeParam TCreate - Zod schema for the creation payload.
|
|
349
|
+
* @typeParam TUpdate - Zod schema for the update payload.
|
|
350
|
+
*
|
|
351
|
+
* @example
|
|
352
|
+
* ```ts
|
|
353
|
+
* import { defineApi } from "@simplix-react/contract";
|
|
354
|
+
*
|
|
355
|
+
* const api = defineApi(config);
|
|
356
|
+
*
|
|
357
|
+
* // All methods are fully typed based on entity schemas
|
|
358
|
+
* const tasks = await api.client.task.list();
|
|
359
|
+
* const task = await api.client.task.get("task-1");
|
|
360
|
+
* const created = await api.client.task.create({ title: "New task" });
|
|
361
|
+
* const updated = await api.client.task.update("task-1", { title: "Updated" });
|
|
362
|
+
* await api.client.task.delete("task-1");
|
|
363
|
+
* ```
|
|
364
|
+
*
|
|
365
|
+
* @see {@link deriveClient} for the factory function.
|
|
366
|
+
*/
|
|
40
367
|
interface EntityClient<TSchema extends z.ZodType, TCreate extends z.ZodType, TUpdate extends z.ZodType> {
|
|
41
|
-
list
|
|
368
|
+
/** Fetches a list of entities, optionally scoped by parent ID and/or list parameters. */
|
|
369
|
+
list: (parentIdOrParams?: string | ListParams, params?: ListParams) => Promise<z.infer<TSchema>[]>;
|
|
370
|
+
/** Fetches a single entity by its ID. */
|
|
42
371
|
get: (id: string) => Promise<z.infer<TSchema>>;
|
|
372
|
+
/** Creates a new entity, optionally under a parent resource. */
|
|
43
373
|
create: (parentIdOrDto: string | z.infer<TCreate>, dto?: z.infer<TCreate>) => Promise<z.infer<TSchema>>;
|
|
374
|
+
/** Partially updates an existing entity by ID. */
|
|
44
375
|
update: (id: string, dto: z.infer<TUpdate>) => Promise<z.infer<TSchema>>;
|
|
376
|
+
/** Deletes an entity by its ID. */
|
|
45
377
|
delete: (id: string) => Promise<void>;
|
|
46
378
|
}
|
|
379
|
+
/**
|
|
380
|
+
* Represents a customizable fetch function signature.
|
|
381
|
+
*
|
|
382
|
+
* Allows replacing the default HTTP client with a custom implementation
|
|
383
|
+
* (e.g. for authentication headers, retry logic, or testing).
|
|
384
|
+
*
|
|
385
|
+
* @typeParam T - The expected response type after deserialization.
|
|
386
|
+
*
|
|
387
|
+
* @see {@link defaultFetch} for the built-in implementation.
|
|
388
|
+
* @see {@link defineApi} where this is provided via `options.fetchFn`.
|
|
389
|
+
*/
|
|
47
390
|
type FetchFn = <T>(path: string, options?: RequestInit) => Promise<T>;
|
|
391
|
+
/**
|
|
392
|
+
* Represents the fully constructed API contract returned by {@link defineApi}.
|
|
393
|
+
*
|
|
394
|
+
* Contains the original configuration, a type-safe HTTP client, and query key
|
|
395
|
+
* factories for all registered entities. This is the primary interface consumed
|
|
396
|
+
* by `@simplix-react/react` and `@simplix-react/mock`.
|
|
397
|
+
*
|
|
398
|
+
* @typeParam TEntities - Map of entity names to their definitions.
|
|
399
|
+
* @typeParam TOperations - Map of operation names to their definitions.
|
|
400
|
+
*
|
|
401
|
+
* @see {@link defineApi} for constructing this contract.
|
|
402
|
+
* @see {@link @simplix-react/react!deriveHooks | deriveHooks} for deriving React hooks.
|
|
403
|
+
* @see {@link @simplix-react/mock!deriveMockHandlers | deriveMockHandlers} for deriving mock handlers.
|
|
404
|
+
*/
|
|
48
405
|
interface ApiContract<TEntities extends Record<string, EntityDefinition<any, any, any>>, TOperations extends Record<string, OperationDefinition<any, any>>> {
|
|
406
|
+
/** The original contract configuration. */
|
|
49
407
|
config: ApiContractConfig<TEntities, TOperations>;
|
|
408
|
+
/** Type-safe HTTP client with methods for each entity and operation. */
|
|
50
409
|
client: {
|
|
51
410
|
[K in keyof TEntities]: EntityClient<TEntities[K]["schema"], TEntities[K]["createSchema"], TEntities[K]["updateSchema"]>;
|
|
52
411
|
} & {
|
|
53
412
|
[K in keyof TOperations]: TOperations[K] extends OperationDefinition<infer _TInput, infer TOutput> ? (...args: unknown[]) => Promise<z.infer<TOutput>> : never;
|
|
54
413
|
};
|
|
414
|
+
/** Query key factories for cache management, one per entity. */
|
|
55
415
|
queryKeys: {
|
|
56
416
|
[K in keyof TEntities]: QueryKeyFactory;
|
|
57
417
|
};
|
|
58
418
|
}
|
|
419
|
+
/** Shorthand for an entity definition with any Zod schema types. */
|
|
420
|
+
type AnyEntityDef = EntityDefinition<z.ZodTypeAny, z.ZodTypeAny, z.ZodTypeAny>;
|
|
421
|
+
/** Shorthand for an operation definition with any Zod schema types. */
|
|
422
|
+
type AnyOperationDef = OperationDefinition<z.ZodTypeAny, z.ZodTypeAny>;
|
|
59
423
|
|
|
424
|
+
/**
|
|
425
|
+
* Creates a fully-typed API contract from an {@link ApiContractConfig}.
|
|
426
|
+
*
|
|
427
|
+
* Serves as the main entry point for `@simplix-react/contract`. Takes a
|
|
428
|
+
* declarative config of entities and operations, then derives a type-safe
|
|
429
|
+
* HTTP client and query key factories. The returned contract is consumed
|
|
430
|
+
* by `@simplix-react/react` for hooks and `@simplix-react/mock` for MSW handlers.
|
|
431
|
+
*
|
|
432
|
+
* @typeParam TEntities - Map of entity names to their {@link EntityDefinition}s.
|
|
433
|
+
* @typeParam TOperations - Map of operation names to their {@link OperationDefinition}s.
|
|
434
|
+
*
|
|
435
|
+
* @param config - The API contract configuration defining entities, operations, and shared settings.
|
|
436
|
+
* @param options - Optional settings for customizing the contract.
|
|
437
|
+
* @param options.fetchFn - Custom fetch function replacing the built-in {@link defaultFetch}.
|
|
438
|
+
* @returns An {@link ApiContract} containing `config`, `client`, and `queryKeys`.
|
|
439
|
+
*
|
|
440
|
+
* @example
|
|
441
|
+
* ```ts
|
|
442
|
+
* import { z } from "zod";
|
|
443
|
+
* import { defineApi, simpleQueryBuilder } from "@simplix-react/contract";
|
|
444
|
+
*
|
|
445
|
+
* const projectApi = defineApi({
|
|
446
|
+
* domain: "project",
|
|
447
|
+
* basePath: "/api/v1",
|
|
448
|
+
* entities: {
|
|
449
|
+
* task: {
|
|
450
|
+
* path: "/tasks",
|
|
451
|
+
* schema: z.object({ id: z.string(), title: z.string() }),
|
|
452
|
+
* createSchema: z.object({ title: z.string() }),
|
|
453
|
+
* updateSchema: z.object({ title: z.string().optional() }),
|
|
454
|
+
* },
|
|
455
|
+
* },
|
|
456
|
+
* queryBuilder: simpleQueryBuilder,
|
|
457
|
+
* });
|
|
458
|
+
*
|
|
459
|
+
* // Type-safe client usage
|
|
460
|
+
* const tasks = await projectApi.client.task.list();
|
|
461
|
+
* const task = await projectApi.client.task.get("task-1");
|
|
462
|
+
*
|
|
463
|
+
* // Query keys for TanStack Query
|
|
464
|
+
* projectApi.queryKeys.task.all; // ["project", "task"]
|
|
465
|
+
* projectApi.queryKeys.task.detail("task-1"); // ["project", "task", "detail", "task-1"]
|
|
466
|
+
* ```
|
|
467
|
+
*
|
|
468
|
+
* @see {@link ApiContractConfig} for the full config shape.
|
|
469
|
+
* @see {@link @simplix-react/react!deriveHooks | deriveHooks} for deriving React Query hooks.
|
|
470
|
+
* @see {@link @simplix-react/mock!deriveMockHandlers | deriveMockHandlers} for deriving MSW handlers.
|
|
471
|
+
*/
|
|
60
472
|
declare function defineApi<TEntities extends Record<string, EntityDefinition<any, any, any>>, TOperations extends Record<string, OperationDefinition<any, any>> = Record<string, never>>(config: ApiContractConfig<TEntities, TOperations>, options?: {
|
|
61
473
|
fetchFn?: FetchFn;
|
|
62
474
|
}): {
|
|
@@ -65,38 +477,221 @@ declare function defineApi<TEntities extends Record<string, EntityDefinition<any
|
|
|
65
477
|
queryKeys: { [K in keyof TEntities]: QueryKeyFactory; };
|
|
66
478
|
};
|
|
67
479
|
|
|
480
|
+
/**
|
|
481
|
+
* Derives a type-safe HTTP client from an {@link ApiContractConfig}.
|
|
482
|
+
*
|
|
483
|
+
* Iterates over all entities and operations in the config and generates
|
|
484
|
+
* corresponding CRUD methods and operation functions. Each entity produces
|
|
485
|
+
* `list`, `get`, `create`, `update`, and `delete` methods. Each operation
|
|
486
|
+
* produces a callable function with positional path parameter arguments.
|
|
487
|
+
*
|
|
488
|
+
* Typically called internally by {@link defineApi} rather than used directly.
|
|
489
|
+
*
|
|
490
|
+
* @typeParam TEntities - Map of entity names to their definitions.
|
|
491
|
+
* @typeParam TOperations - Map of operation names to their definitions.
|
|
492
|
+
* @param config - The API contract configuration.
|
|
493
|
+
* @param fetchFn - Custom fetch function; defaults to {@link defaultFetch}.
|
|
494
|
+
* @returns A client object with typed methods for each entity and operation.
|
|
495
|
+
*
|
|
496
|
+
* @example
|
|
497
|
+
* ```ts
|
|
498
|
+
* import { deriveClient } from "@simplix-react/contract";
|
|
499
|
+
*
|
|
500
|
+
* const client = deriveClient(config);
|
|
501
|
+
* const tasks = await client.task.list();
|
|
502
|
+
* ```
|
|
503
|
+
*
|
|
504
|
+
* @see {@link defineApi} for the recommended high-level API.
|
|
505
|
+
*/
|
|
68
506
|
declare function deriveClient<TEntities extends Record<string, EntityDefinition<any, any, any>>, TOperations extends Record<string, OperationDefinition<any, any>>>(config: ApiContractConfig<TEntities, TOperations>, fetchFn?: FetchFn): Record<string, unknown>;
|
|
69
507
|
|
|
508
|
+
/**
|
|
509
|
+
* Derives a set of {@link QueryKeyFactory} instances for all entities in a contract.
|
|
510
|
+
*
|
|
511
|
+
* Generates structured query keys following the factory pattern recommended
|
|
512
|
+
* by TanStack Query. Each entity receives keys scoped by `[domain, entityName]`,
|
|
513
|
+
* enabling granular cache invalidation (e.g. invalidate all task lists without
|
|
514
|
+
* affecting task details).
|
|
515
|
+
*
|
|
516
|
+
* Typically called internally by {@link defineApi} rather than used directly.
|
|
517
|
+
*
|
|
518
|
+
* @typeParam TEntities - Map of entity names to their definitions.
|
|
519
|
+
* @param config - Subset of the API contract config containing `domain` and `entities`.
|
|
520
|
+
* @returns A map of entity names to their {@link QueryKeyFactory} instances.
|
|
521
|
+
*
|
|
522
|
+
* @example
|
|
523
|
+
* ```ts
|
|
524
|
+
* import { deriveQueryKeys } from "@simplix-react/contract";
|
|
525
|
+
*
|
|
526
|
+
* const queryKeys = deriveQueryKeys({ domain: "project", entities: { task: taskEntity } });
|
|
527
|
+
*
|
|
528
|
+
* queryKeys.task.all; // ["project", "task"]
|
|
529
|
+
* queryKeys.task.lists(); // ["project", "task", "list"]
|
|
530
|
+
* queryKeys.task.detail("abc"); // ["project", "task", "detail", "abc"]
|
|
531
|
+
* ```
|
|
532
|
+
*
|
|
533
|
+
* @see {@link defineApi} for the recommended high-level API.
|
|
534
|
+
* @see {@link QueryKeyFactory} for the generated key structure.
|
|
535
|
+
*/
|
|
70
536
|
declare function deriveQueryKeys<TEntities extends Record<string, EntityDefinition<any, any, any>>>(config: Pick<ApiContractConfig<TEntities>, "domain" | "entities">): {
|
|
71
537
|
[K in keyof TEntities]: QueryKeyFactory;
|
|
72
538
|
};
|
|
73
539
|
|
|
74
540
|
/**
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
*
|
|
541
|
+
* Substitutes `:paramName` placeholders in a URL path template with actual values.
|
|
542
|
+
*
|
|
543
|
+
* Values are URI-encoded to ensure safe inclusion in URLs. Used internally by
|
|
544
|
+
* {@link deriveClient} for building operation URLs, and available as a public
|
|
545
|
+
* utility for custom URL construction.
|
|
546
|
+
*
|
|
547
|
+
* @param template - URL path template with `:paramName` placeholders.
|
|
548
|
+
* @param params - Map of parameter names to their string values.
|
|
549
|
+
* @returns The resolved URL path with all placeholders replaced.
|
|
550
|
+
*
|
|
551
|
+
* @example
|
|
552
|
+
* ```ts
|
|
553
|
+
* import { buildPath } from "@simplix-react/contract";
|
|
554
|
+
*
|
|
555
|
+
* buildPath("/projects/:projectId/tasks", { projectId: "abc" });
|
|
556
|
+
* // "/projects/abc/tasks"
|
|
557
|
+
*
|
|
558
|
+
* buildPath("/tasks/:taskId/assign", { taskId: "task-1" });
|
|
559
|
+
* // "/tasks/task-1/assign"
|
|
560
|
+
* ```
|
|
78
561
|
*/
|
|
79
562
|
declare function buildPath(template: string, params?: Record<string, string>): string;
|
|
80
563
|
|
|
564
|
+
/**
|
|
565
|
+
* Represents an HTTP error response from the API.
|
|
566
|
+
*
|
|
567
|
+
* Thrown by {@link defaultFetch} and the internal multipart/blob fetchers when
|
|
568
|
+
* the server responds with a non-2xx status code. Captures both the HTTP status
|
|
569
|
+
* and the raw response body for debugging.
|
|
570
|
+
*
|
|
571
|
+
* @example
|
|
572
|
+
* ```ts
|
|
573
|
+
* import { ApiError } from "@simplix-react/contract";
|
|
574
|
+
*
|
|
575
|
+
* try {
|
|
576
|
+
* await api.client.task.get("nonexistent");
|
|
577
|
+
* } catch (error) {
|
|
578
|
+
* if (error instanceof ApiError) {
|
|
579
|
+
* console.log(error.status); // 404
|
|
580
|
+
* console.log(error.body); // "Not Found"
|
|
581
|
+
* }
|
|
582
|
+
* }
|
|
583
|
+
* ```
|
|
584
|
+
*/
|
|
81
585
|
declare class ApiError extends Error {
|
|
586
|
+
/** HTTP status code of the failed response. */
|
|
82
587
|
readonly status: number;
|
|
588
|
+
/** Raw response body text. */
|
|
83
589
|
readonly body: string;
|
|
84
|
-
constructor(
|
|
590
|
+
constructor(
|
|
591
|
+
/** HTTP status code of the failed response. */
|
|
592
|
+
status: number,
|
|
593
|
+
/** Raw response body text. */
|
|
594
|
+
body: string);
|
|
85
595
|
}
|
|
86
596
|
/**
|
|
87
|
-
*
|
|
597
|
+
* Performs an HTTP request with automatic JSON content-type headers and
|
|
598
|
+
* `{ data: T }` envelope unwrapping.
|
|
599
|
+
*
|
|
600
|
+
* Serves as the built-in fetch implementation used by {@link deriveClient}
|
|
601
|
+
* when no custom `fetchFn` is provided. Returns `undefined` for 204 No Content
|
|
602
|
+
* responses and throws {@link ApiError} for non-2xx status codes.
|
|
603
|
+
*
|
|
604
|
+
* @typeParam T - The expected deserialized response type.
|
|
605
|
+
* @param path - The full URL path to fetch.
|
|
606
|
+
* @param options - Standard `RequestInit` options forwarded to the native `fetch`.
|
|
607
|
+
* @returns The unwrapped response data.
|
|
608
|
+
* @throws {@link ApiError} When the response status is not OK.
|
|
609
|
+
*
|
|
610
|
+
* @example
|
|
611
|
+
* ```ts
|
|
612
|
+
* import { defineApi, defaultFetch } from "@simplix-react/contract";
|
|
613
|
+
*
|
|
614
|
+
* // Use as-is (default behavior)
|
|
615
|
+
* const api = defineApi(config);
|
|
616
|
+
*
|
|
617
|
+
* // Or provide a custom wrapper
|
|
618
|
+
* const api = defineApi(config, {
|
|
619
|
+
* fetchFn: async (path, options) => {
|
|
620
|
+
* // Add auth header, then delegate to defaultFetch
|
|
621
|
+
* return defaultFetch(path, {
|
|
622
|
+
* ...options,
|
|
623
|
+
* headers: { ...options?.headers, Authorization: `Bearer ${token}` },
|
|
624
|
+
* });
|
|
625
|
+
* },
|
|
626
|
+
* });
|
|
627
|
+
* ```
|
|
88
628
|
*/
|
|
89
629
|
declare function defaultFetch<T>(path: string, options?: RequestInit): Promise<T>;
|
|
90
630
|
|
|
91
631
|
/**
|
|
92
|
-
*
|
|
93
|
-
*
|
|
632
|
+
* Converts a camelCase string to kebab-case.
|
|
633
|
+
*
|
|
634
|
+
* Used internally for transforming entity names into URL-friendly path segments.
|
|
635
|
+
*
|
|
636
|
+
* @param str - The camelCase string to convert.
|
|
637
|
+
* @returns The kebab-case equivalent.
|
|
638
|
+
*
|
|
639
|
+
* @example
|
|
640
|
+
* ```ts
|
|
641
|
+
* import { camelToKebab } from "@simplix-react/contract";
|
|
642
|
+
*
|
|
643
|
+
* camelToKebab("doorReader"); // "door-reader"
|
|
644
|
+
* camelToKebab("myEntity"); // "my-entity"
|
|
645
|
+
* ```
|
|
94
646
|
*/
|
|
95
647
|
declare function camelToKebab(str: string): string;
|
|
96
648
|
/**
|
|
97
|
-
*
|
|
98
|
-
*
|
|
649
|
+
* Converts a camelCase string to snake_case.
|
|
650
|
+
*
|
|
651
|
+
* Also handles hyphenated and space-separated inputs by replacing them with
|
|
652
|
+
* underscores before lowercasing.
|
|
653
|
+
*
|
|
654
|
+
* @param str - The camelCase string to convert.
|
|
655
|
+
* @returns The snake_case equivalent.
|
|
656
|
+
*
|
|
657
|
+
* @example
|
|
658
|
+
* ```ts
|
|
659
|
+
* import { camelToSnake } from "@simplix-react/contract";
|
|
660
|
+
*
|
|
661
|
+
* camelToSnake("doorReader"); // "door_reader"
|
|
662
|
+
* camelToSnake("myEntity"); // "my_entity"
|
|
663
|
+
* camelToSnake("some-field"); // "some_field"
|
|
664
|
+
* ```
|
|
99
665
|
*/
|
|
100
666
|
declare function camelToSnake(str: string): string;
|
|
101
667
|
|
|
102
|
-
|
|
668
|
+
/**
|
|
669
|
+
* Provides a straightforward {@link QueryBuilder} implementation for common REST APIs.
|
|
670
|
+
*
|
|
671
|
+
* Serializes filters as flat key-value pairs, sort as `field:direction` comma-separated
|
|
672
|
+
* values, and pagination as `page`/`limit` (offset) or `cursor`/`limit` (cursor-based).
|
|
673
|
+
*
|
|
674
|
+
* @example
|
|
675
|
+
* ```ts
|
|
676
|
+
* import { defineApi, simpleQueryBuilder } from "@simplix-react/contract";
|
|
677
|
+
*
|
|
678
|
+
* const api = defineApi({
|
|
679
|
+
* domain: "project",
|
|
680
|
+
* basePath: "/api/v1",
|
|
681
|
+
* entities: { task: taskEntity },
|
|
682
|
+
* queryBuilder: simpleQueryBuilder,
|
|
683
|
+
* });
|
|
684
|
+
*
|
|
685
|
+
* // Produces: /api/v1/tasks?status=pending&sort=name:asc&page=1&limit=10
|
|
686
|
+
* await api.client.task.list({
|
|
687
|
+
* filters: { status: "pending" },
|
|
688
|
+
* sort: { field: "name", direction: "asc" },
|
|
689
|
+
* pagination: { type: "offset", page: 1, limit: 10 },
|
|
690
|
+
* });
|
|
691
|
+
* ```
|
|
692
|
+
*
|
|
693
|
+
* @see {@link QueryBuilder} for implementing custom serialization strategies.
|
|
694
|
+
*/
|
|
695
|
+
declare const simpleQueryBuilder: QueryBuilder;
|
|
696
|
+
|
|
697
|
+
export { type AnyEntityDef, type AnyOperationDef, type ApiContract, type ApiContractConfig, ApiError, type EntityClient, type EntityDefinition, type EntityParent, type EntityQuery, type FetchFn, type HttpMethod, type ListParams, type OperationDefinition, type PageInfo, type PaginationParam, type QueryBuilder, type QueryKeyFactory, type SortParam, buildPath, camelToKebab, camelToSnake, defaultFetch, defineApi, deriveClient, deriveQueryKeys, simpleQueryBuilder };
|