@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/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: (parentId?: string) => Promise<z.infer<TSchema>[]>;
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
- * Build URL path with parameter substitution.
76
- * buildPath("/topologies/:topologyId/controllers", { topologyId: "abc" })
77
- * -> "/topologies/abc/controllers"
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(status: number, body: string);
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
- * Default fetch function that unwraps { data: T } envelope.
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
- * Convert camelCase to kebab-case.
93
- * "doorReader" -> "door-reader"
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
- * Convert camelCase to snake_case.
98
- * "doorReader" -> "door_reader"
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
- export { type ApiContract, type ApiContractConfig, ApiError, type EntityClient, type EntityDefinition, type EntityParent, type EntityQuery, type FetchFn, type HttpMethod, type OperationDefinition, type QueryKeyFactory, buildPath, camelToKebab, camelToSnake, defaultFetch, defineApi, deriveClient, deriveQueryKeys };
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 };