@fgrzl/fetch 1.2.0 → 1.3.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/dist/index.d.ts CHANGED
@@ -361,6 +361,93 @@ declare class NetworkError extends FetchError {
361
361
  constructor(message: string, url: string, cause?: Error);
362
362
  }
363
363
 
364
+ /**
365
+ * @fileoverview Query parameter utilities for FetchClient
366
+ *
367
+ * Provides utilities for building URL query strings from JavaScript objects
368
+ * with proper handling of arrays, undefined values, and URL encoding.
369
+ */
370
+ /**
371
+ * Query parameter value types that can be converted to URL parameters.
372
+ * Arrays are handled specially with multiple parameter entries.
373
+ */
374
+ type QueryValue = string | number | boolean | null | undefined | QueryValue[];
375
+ /**
376
+ * Object representing query parameters for URL construction.
377
+ */
378
+ type QueryParams = Record<string, QueryValue>;
379
+ /**
380
+ * Builds a URL query string from a JavaScript object.
381
+ *
382
+ * Features:
383
+ * - ✅ Proper URL encoding via native URLSearchParams
384
+ * - ✅ Array handling with multiple parameter entries
385
+ * - ✅ Undefined value filtering (excluded from output)
386
+ * - ✅ Type coercion to strings for all values
387
+ *
388
+ * @param query - Object containing query parameters
389
+ * @returns URL-encoded query string (without leading '?')
390
+ *
391
+ * @example Basic usage:
392
+ * ```typescript
393
+ * buildQueryParams({ name: 'John', age: 30 })
394
+ * // → "name=John&age=30"
395
+ * ```
396
+ *
397
+ * @example Array handling:
398
+ * ```typescript
399
+ * buildQueryParams({ tags: ['typescript', 'javascript'], active: true })
400
+ * // → "tags=typescript&tags=javascript&active=true"
401
+ * ```
402
+ *
403
+ * @example Undefined filtering:
404
+ * ```typescript
405
+ * buildQueryParams({ name: 'John', email: undefined, age: null })
406
+ * // → "name=John&age=null" (undefined excluded, null converted to string)
407
+ * ```
408
+ *
409
+ * @example Real-world API usage:
410
+ * ```typescript
411
+ * const filters = {
412
+ * status: ['active', 'pending'],
413
+ * limit: 50,
414
+ * offset: 0,
415
+ * search: searchTerm || undefined // Conditionally included
416
+ * };
417
+ *
418
+ * const queryString = buildQueryParams(filters);
419
+ * // → "status=active&status=pending&limit=50&offset=0"
420
+ * // (search excluded if searchTerm is undefined)
421
+ * ```
422
+ */
423
+ declare function buildQueryParams(query: QueryParams): string;
424
+ /**
425
+ * Appends query parameters to a URL, handling existing query strings properly.
426
+ *
427
+ * @param baseUrl - The base URL to append parameters to
428
+ * @param query - Object containing query parameters
429
+ * @returns Complete URL with query parameters appended
430
+ *
431
+ * @example Basic URL building:
432
+ * ```typescript
433
+ * appendQueryParams('/api/users', { limit: 10, active: true })
434
+ * // → "/api/users?limit=10&active=true"
435
+ * ```
436
+ *
437
+ * @example Existing query parameters:
438
+ * ```typescript
439
+ * appendQueryParams('/api/users?sort=name', { limit: 10 })
440
+ * // → "/api/users?sort=name&limit=10"
441
+ * ```
442
+ *
443
+ * @example Empty query object:
444
+ * ```typescript
445
+ * appendQueryParams('/api/users', {})
446
+ * // → "/api/users" (no change)
447
+ * ```
448
+ */
449
+ declare function appendQueryParams(baseUrl: string, query: QueryParams): string;
450
+
364
451
  /**
365
452
  * @fileoverview Authentication middleware types and configuration.
366
453
  */
@@ -1457,6 +1544,18 @@ declare function useBasicStack(client: FetchClient, config: {
1457
1544
  * const activeUsers = await api.get('/api/users', { status: 'active', limit: 10 });
1458
1545
  * ```
1459
1546
  *
1547
+ * @example Set base URL for API-specific clients:
1548
+ * ```typescript
1549
+ * import api from '@fgrzl/fetch';
1550
+ *
1551
+ * // Configure base URL dynamically
1552
+ * api.setBaseUrl('https://api.example.com');
1553
+ *
1554
+ * // Now all relative URLs are prefixed automatically
1555
+ * const users = await api.get('/users'); // → GET https://api.example.com/users
1556
+ * const posts = await api.get('/posts'); // → GET https://api.example.com/posts
1557
+ * ```
1558
+ *
1460
1559
  * @example Configure authentication:
1461
1560
  * ```typescript
1462
1561
  * import api from '@fgrzl/fetch';
@@ -1477,7 +1576,22 @@ declare function useBasicStack(client: FetchClient, config: {
1477
1576
  * tokenProvider: () => getJWTToken()
1478
1577
  * });
1479
1578
  * ```
1579
+ *
1580
+ * @example Production-ready API client with base URL:
1581
+ * ```typescript
1582
+ * import { FetchClient, useProductionStack } from '@fgrzl/fetch';
1583
+ *
1584
+ * // One-liner production setup with base URL
1585
+ * const apiClient = useProductionStack(new FetchClient(), {
1586
+ * auth: { tokenProvider: () => getAuthToken() },
1587
+ * retry: { maxRetries: 3 },
1588
+ * logging: { level: 'info' }
1589
+ * }).setBaseUrl(process.env.API_BASE_URL || 'https://api.example.com');
1590
+ *
1591
+ * // Ready to use with full production features
1592
+ * const users = await apiClient.get('/users');
1593
+ * ```
1480
1594
  */
1481
1595
  declare const api: FetchClient;
1482
1596
 
1483
- export { type AuthTokenProvider, type AuthenticationOptions, type AuthorizationOptions, type CacheEntry, type CacheKeyGenerator, type CacheOptions, type CacheStorage, FetchClient, type FetchClientOptions, FetchError, type FetchResponse, HttpError, type FetchMiddleware as InterceptMiddleware, type LogLevel, type Logger, type LoggingOptions, NetworkError, type RateLimitAlgorithm, type RateLimitOptions, type RetryOptions, type UnauthorizedHandler, createAuthenticationMiddleware, createAuthorizationMiddleware, createCacheMiddleware, createLoggingMiddleware, createRateLimitMiddleware, createRetryMiddleware, api as default, useAuthentication, useAuthorization, useBasicStack, useCSRF, useCache, useDevelopmentStack, useLogging, useProductionStack, useRateLimit, useRetry };
1597
+ export { type AuthTokenProvider, type AuthenticationOptions, type AuthorizationOptions, type CacheEntry, type CacheKeyGenerator, type CacheOptions, type CacheStorage, FetchClient, type FetchClientOptions, FetchError, type FetchResponse, HttpError, type FetchMiddleware as InterceptMiddleware, type LogLevel, type Logger, type LoggingOptions, NetworkError, type QueryParams, type QueryValue, type RateLimitAlgorithm, type RateLimitOptions, type RetryOptions, type UnauthorizedHandler, appendQueryParams, buildQueryParams, createAuthenticationMiddleware, createAuthorizationMiddleware, createCacheMiddleware, createLoggingMiddleware, createRateLimitMiddleware, createRetryMiddleware, api as default, useAuthentication, useAuthorization, useBasicStack, useCSRF, useCache, useDevelopmentStack, useLogging, useProductionStack, useRateLimit, useRetry };
package/dist/index.js CHANGED
@@ -1080,6 +1080,40 @@ var NetworkError = class extends FetchError {
1080
1080
  }
1081
1081
  };
1082
1082
 
1083
+ // src/client/query.ts
1084
+ function buildQueryParams(query) {
1085
+ const params = new URLSearchParams();
1086
+ for (const [key, value] of Object.entries(query)) {
1087
+ if (value !== void 0) {
1088
+ if (Array.isArray(value)) {
1089
+ value.forEach((item) => {
1090
+ if (item !== void 0) {
1091
+ params.append(key, String(item));
1092
+ }
1093
+ });
1094
+ } else {
1095
+ params.set(key, String(value));
1096
+ }
1097
+ }
1098
+ }
1099
+ return params.toString();
1100
+ }
1101
+ function appendQueryParams(baseUrl, query) {
1102
+ const queryString = buildQueryParams(query);
1103
+ if (!queryString) {
1104
+ return baseUrl;
1105
+ }
1106
+ const fragmentIndex = baseUrl.indexOf("#");
1107
+ if (fragmentIndex !== -1) {
1108
+ const urlPart = baseUrl.substring(0, fragmentIndex);
1109
+ const fragmentPart = baseUrl.substring(fragmentIndex);
1110
+ const separator2 = urlPart.includes("?") ? "&" : "?";
1111
+ return `${urlPart}${separator2}${queryString}${fragmentPart}`;
1112
+ }
1113
+ const separator = baseUrl.includes("?") ? "&" : "?";
1114
+ return `${baseUrl}${separator}${queryString}`;
1115
+ }
1116
+
1083
1117
  // src/index.ts
1084
1118
  var api = useProductionStack(
1085
1119
  new FetchClient({
@@ -1114,6 +1148,8 @@ export {
1114
1148
  FetchError,
1115
1149
  HttpError,
1116
1150
  NetworkError,
1151
+ appendQueryParams,
1152
+ buildQueryParams,
1117
1153
  createAuthenticationMiddleware,
1118
1154
  createAuthorizationMiddleware,
1119
1155
  createCacheMiddleware,