zlient 2.1.11 → 3.0.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.js CHANGED
@@ -1,5 +1,3 @@
1
- import { z } from "zod";
2
-
3
1
  //#region lib/auth.ts
4
2
  /**
5
3
  * No-op authentication provider (no authentication applied).
@@ -73,7 +71,6 @@ var BearerTokenAuth = class {
73
71
  };
74
72
  }
75
73
  };
76
-
77
74
  //#endregion
78
75
  //#region lib/types.ts
79
76
  const HTTPMethod = {
@@ -162,23 +159,20 @@ const HTTPStatusCode = {
162
159
  * ```
163
160
  */
164
161
  var ApiError = class ApiError extends Error {
165
- status;
166
- details;
167
- zodError;
168
162
  constructor(message, options) {
169
163
  super(message);
170
164
  this.name = "ApiError";
171
165
  this.status = options?.status;
172
166
  this.details = options?.details;
173
167
  this.cause = options?.cause;
174
- this.zodError = options?.zodError;
168
+ this.validationIssues = options?.validationIssues;
175
169
  if (Error.captureStackTrace) Error.captureStackTrace(this, ApiError);
176
170
  }
177
171
  /**
178
- * Check if this is a validation error (has zodError)
172
+ * Check if this is a validation error (has validationIssues)
179
173
  */
180
174
  isValidationError() {
181
- return !!this.zodError;
175
+ return !!this.validationIssues && this.validationIssues.length > 0;
182
176
  }
183
177
  /**
184
178
  * Check if this is a client error (4xx status)
@@ -201,7 +195,7 @@ var ApiError = class ApiError extends Error {
201
195
  message: this.message,
202
196
  status: this.status,
203
197
  details: this.details,
204
- zodError: this.zodError?.issues,
198
+ validationIssues: this.validationIssues,
205
199
  stack: this.stack
206
200
  };
207
201
  }
@@ -219,15 +213,6 @@ var SchemaDefinitionError = class SchemaDefinitionError extends Error {
219
213
  }
220
214
  };
221
215
  /**
222
- * Schema for paginated responses
223
- */
224
- const PaginationSchema = z.object({
225
- items: z.array(z.unknown()),
226
- total: z.number().int().nonnegative(),
227
- page: z.number().int().nonnegative(),
228
- pageSize: z.number().int().positive()
229
- });
230
- /**
231
216
  * Converts query parameters to a URL query string.
232
217
  * Filters out undefined values automatically.
233
218
  *
@@ -243,8 +228,8 @@ const PaginationSchema = z.object({
243
228
  function toQueryString(q) {
244
229
  if (!q) return "";
245
230
  if (q instanceof URLSearchParams) {
246
- const s$1 = q.toString();
247
- return s$1 ? `?${s$1}` : "";
231
+ const s = q.toString();
232
+ return s ? `?${s}` : "";
248
233
  }
249
234
  const params = new URLSearchParams();
250
235
  Object.entries(q).forEach(([k, v]) => {
@@ -253,36 +238,88 @@ function toQueryString(q) {
253
238
  const s = params.toString();
254
239
  return s ? `?${s}` : "";
255
240
  }
256
-
257
241
  //#endregion
258
242
  //#region lib/validation.ts
259
243
  /**
260
- * Parse data with a Zod schema, throwing an ApiError on validation failure.
244
+ * Safely parse/validate data with any Standard Schema-compatible library (Zod, Valibot, ArkType, etc.).
245
+ * Returns a result object with success status and data or issues.
246
+ *
247
+ * @param schema - A Standard Schema-compatible validator
248
+ * @param data - Data to validate
249
+ * @returns Result object with success flag and data/issues
250
+ *
251
+ * @example
252
+ * ```ts
253
+ * // Works with Zod
254
+ * import { z } from 'zod';
255
+ * const result = await safeParse(z.object({ name: z.string() }), userData);
256
+ *
257
+ * // Works with Valibot
258
+ * import * as v from 'valibot';
259
+ * const result = await safeParse(v.object({ name: v.string() }), userData);
260
+ *
261
+ * // Works with ArkType
262
+ * import { type } from 'arktype';
263
+ * const result = await safeParse(type({ name: 'string' }), userData);
264
+ *
265
+ * if (result.success) {
266
+ * console.log(result.data);
267
+ * } else {
268
+ * console.error(result.issues);
269
+ * }
270
+ * ```
271
+ */
272
+ async function safeParse(schema, data) {
273
+ const result = await schema["~standard"].validate(data);
274
+ if (result.issues) return {
275
+ success: false,
276
+ issues: result.issues
277
+ };
278
+ return {
279
+ success: true,
280
+ data: result.value
281
+ };
282
+ }
283
+ /**
284
+ * Parse/validate data with any Standard Schema-compatible library, throwing an ApiError on failure.
261
285
  * Use this when you want to fail fast on invalid data.
262
286
  *
263
- * @param schema - Zod schema to validate against
287
+ * @param schema - A Standard Schema-compatible validator
264
288
  * @param data - Data to validate
265
289
  * @returns Validated and typed data
266
- * @throws {ApiError} If validation fails
290
+ * @throws {ApiError} If validation fails (with validationIssues property)
267
291
  *
268
292
  * @example
269
293
  * ```ts
294
+ * // Works with any Standard Schema-compatible library
270
295
  * try {
271
- * const user = parseOrThrow(UserSchema, userData);
296
+ * const user = await parseOrThrow(UserSchema, userData);
272
297
  * console.log(user);
273
298
  * } catch (error) {
274
- * if (error instanceof ApiError && error.zodError) {
275
- * console.error('Validation failed:', error.zodError.issues);
299
+ * if (error instanceof ApiError && error.isValidationError()) {
300
+ * console.error('Validation failed:', error.validationIssues);
276
301
  * }
277
302
  * }
278
303
  * ```
279
304
  */
280
- function parseOrThrow(schema, data) {
281
- const res = schema.safeParse(data);
282
- if (!res.success) throw new ApiError("Response validation failed", { zodError: res.error });
283
- return res.data;
305
+ async function parseOrThrow(schema, data) {
306
+ const result = await schema["~standard"].validate(data);
307
+ if (result.issues) throw new ApiError(`Validation failed: ${result.issues.map((issue) => issue.message).join(", ")}`, { validationIssues: result.issues });
308
+ return result.value;
309
+ }
310
+ /**
311
+ * Type guard to check if a value is a Standard Schema-compatible validator.
312
+ * Handles both object-based schemas (Zod, Valibot) and function-based schemas (ArkType).
313
+ *
314
+ * @param value - Value to check
315
+ * @returns True if the value implements Standard Schema v1
316
+ */
317
+ function isStandardSchema(value) {
318
+ if (value === null || value === void 0) return false;
319
+ if (typeof value !== "object" && typeof value !== "function") return false;
320
+ const schema = value;
321
+ return "~standard" in schema && typeof schema["~standard"] === "object" && schema["~standard"] !== null && schema["~standard"].version === 1 && typeof schema["~standard"].validate === "function";
284
322
  }
285
-
286
323
  //#endregion
287
324
  //#region lib/endpoint/base-endpoint.ts
288
325
  var EndpointImpl = class {
@@ -299,18 +336,9 @@ var EndpointImpl = class {
299
336
  const missingHeaders = this.config.mustHeaderKeys.filter((key) => !headers || !(key in headers));
300
337
  if (missingHeaders.length > 0) throw new Error(`Missing required header(s): ${missingHeaders.join(", ")}`);
301
338
  }
302
- if (!skipRequestValidation && this.config.request && data !== void 0) {
303
- const parsed = this.config.request.safeParse(data);
304
- if (!parsed.success) throw parsed.error;
305
- }
306
- if (!skipRequestValidation && this.config.query && query !== void 0) {
307
- const parsed = this.config.query.safeParse(query);
308
- if (!parsed.success) throw parsed.error;
309
- }
310
- if (!skipRequestValidation && this.config.pathParams && pathParams !== void 0) {
311
- const parsed = this.config.pathParams.safeParse(pathParams);
312
- if (!parsed.success) throw parsed.error;
313
- }
339
+ if (!skipRequestValidation && this.config.request && data !== void 0) await parseOrThrow(this.config.request, data);
340
+ if (!skipRequestValidation && this.config.query && query !== void 0) await parseOrThrow(this.config.query, query);
341
+ if (!skipRequestValidation && this.config.pathParams && pathParams !== void 0) await parseOrThrow(this.config.pathParams, pathParams);
314
342
  if (this.config.request && data === void 0) throw new Error("Missing required request body (data)");
315
343
  if (this.config.pathParams && pathParams === void 0) throw new Error("Missing required path parameters (pathParams)");
316
344
  let pathStr;
@@ -328,24 +356,23 @@ var EndpointImpl = class {
328
356
  });
329
357
  const schema = this.config.response;
330
358
  if (skipResponseValidation) return responseData;
331
- if (schema instanceof z.ZodType) return parseOrThrow(schema, responseData);
359
+ if (isStandardSchema(schema)) return await parseOrThrow(schema, responseData);
332
360
  const specificSchema = schema[status];
333
361
  if (!specificSchema) throw new SchemaDefinitionError(status);
334
- return parseOrThrow(specificSchema, responseData);
362
+ return await parseOrThrow(specificSchema, responseData);
335
363
  }
336
364
  };
337
-
338
365
  //#endregion
339
366
  //#region lib/logger.ts
340
367
  /**
341
368
  * Log levels for structured logging.
342
369
  */
343
- let LogLevel = /* @__PURE__ */ function(LogLevel$1) {
344
- LogLevel$1["DEBUG"] = "debug";
345
- LogLevel$1["INFO"] = "info";
346
- LogLevel$1["WARN"] = "warn";
347
- LogLevel$1["ERROR"] = "error";
348
- return LogLevel$1;
370
+ let LogLevel = /* @__PURE__ */ function(LogLevel) {
371
+ LogLevel["DEBUG"] = "debug";
372
+ LogLevel["INFO"] = "info";
373
+ LogLevel["WARN"] = "warn";
374
+ LogLevel["ERROR"] = "error";
375
+ return LogLevel;
349
376
  }({});
350
377
  /**
351
378
  * Default console logger implementation.
@@ -362,9 +389,7 @@ var ConsoleLogger = class {
362
389
  LogLevel.WARN,
363
390
  LogLevel.ERROR
364
391
  ];
365
- const entryLevelIndex = levels.indexOf(entry.level);
366
- const minLevelIndex = levels.indexOf(this.minLevel);
367
- if (entryLevelIndex < minLevelIndex) return;
392
+ if (levels.indexOf(entry.level) < levels.indexOf(this.minLevel)) return;
368
393
  const output = {
369
394
  ...entry,
370
395
  error: entry.error ? {
@@ -407,7 +432,7 @@ var LoggerUtil = class {
407
432
  this.logger.log({
408
433
  level: LogLevel.DEBUG,
409
434
  message,
410
- timestamp: new Date().toISOString(),
435
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
411
436
  context
412
437
  });
413
438
  }
@@ -415,7 +440,7 @@ var LoggerUtil = class {
415
440
  this.logger.log({
416
441
  level: LogLevel.INFO,
417
442
  message,
418
- timestamp: new Date().toISOString(),
443
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
419
444
  context
420
445
  });
421
446
  }
@@ -423,7 +448,7 @@ var LoggerUtil = class {
423
448
  this.logger.log({
424
449
  level: LogLevel.WARN,
425
450
  message,
426
- timestamp: new Date().toISOString(),
451
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
427
452
  context
428
453
  });
429
454
  }
@@ -431,13 +456,12 @@ var LoggerUtil = class {
431
456
  this.logger.log({
432
457
  level: LogLevel.ERROR,
433
458
  message,
434
- timestamp: new Date().toISOString(),
459
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
435
460
  context,
436
461
  error
437
462
  });
438
463
  }
439
464
  };
440
-
441
465
  //#endregion
442
466
  //#region lib/metrics.ts
443
467
  /**
@@ -451,9 +475,8 @@ var NoOpMetricsCollector = class {
451
475
  * Stores metrics in memory with configurable retention.
452
476
  */
453
477
  var InMemoryMetricsCollector = class {
454
- metrics = [];
455
- maxEntries;
456
478
  constructor(maxEntries = 1e3) {
479
+ this.metrics = [];
457
480
  this.maxEntries = maxEntries;
458
481
  }
459
482
  collect(metrics) {
@@ -512,7 +535,6 @@ var ConsoleMetricsCollector = class {
512
535
  console.log("[METRICS]", JSON.stringify(metrics));
513
536
  }
514
537
  };
515
-
516
538
  //#endregion
517
539
  //#region lib/http/http-client.ts
518
540
  /**
@@ -532,16 +554,6 @@ var ConsoleMetricsCollector = class {
532
554
  * ```
533
555
  */
534
556
  var HttpClient = class {
535
- fetchImpl;
536
- baseUrls;
537
- headers;
538
- interceptors;
539
- retry;
540
- timeoutMs;
541
- auth;
542
- logger;
543
- metrics;
544
- onUnauthenticated;
545
557
  /**
546
558
  * Creates a new HTTP client instance.
547
559
  *
@@ -682,8 +694,7 @@ var HttpClient = class {
682
694
  */
683
695
  async request(method, path, body, options) {
684
696
  const startTime = Date.now();
685
- const base = this.resolveBaseUrl(options?.baseUrlKey);
686
- let url = `${base}${path}${toQueryString(options?.query)}`;
697
+ let url = `${this.resolveBaseUrl(options?.baseUrlKey)}${path}${toQueryString(options?.query)}`;
687
698
  this.logger.debug("HTTP request initiated", {
688
699
  method,
689
700
  path,
@@ -721,7 +732,7 @@ var HttpClient = class {
721
732
  while (true) {
722
733
  let timeoutId;
723
734
  if (this.timeoutMs && !options?.signal) timeoutId = setTimeout(() => {
724
- const timeoutError = new Error("Request timeout");
735
+ const timeoutError = /* @__PURE__ */ new Error("Request timeout");
725
736
  timeoutError.name = "TimeoutError";
726
737
  controller.abort(timeoutError);
727
738
  }, this.timeoutMs);
@@ -738,8 +749,7 @@ var HttpClient = class {
738
749
  const req = new Request(url, init);
739
750
  const res = await this.fetchImpl(req);
740
751
  if (res.status === 401 && this.onUnauthenticated && !refreshAttempted) {
741
- const shouldRetry = await this.onUnauthenticated(res.clone());
742
- if (shouldRetry) {
752
+ if (await this.onUnauthenticated(res.clone())) {
743
753
  refreshAttempted = true;
744
754
  if (timeoutId) clearTimeout(timeoutId);
745
755
  continue;
@@ -764,7 +774,7 @@ var HttpClient = class {
764
774
  path,
765
775
  status: res.status,
766
776
  durationMs: duration,
767
- timestamp: new Date().toISOString(),
777
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
768
778
  success: true
769
779
  });
770
780
  return {
@@ -783,7 +793,7 @@ var HttpClient = class {
783
793
  path,
784
794
  status: error instanceof ApiError ? error.status : void 0,
785
795
  durationMs: duration,
786
- timestamp: new Date().toISOString(),
796
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
787
797
  success: false,
788
798
  error: error instanceof Error ? error.message : String(error)
789
799
  });
@@ -799,8 +809,7 @@ var HttpClient = class {
799
809
  if (errorName === "AbortError" || errorName === "TimeoutError") return false;
800
810
  }
801
811
  if (error instanceof ApiError && error.status) {
802
- const retryCodes = this.retry.retryStatusCodes;
803
- if (retryCodes?.some((codeKey) => HTTPStatusCode[codeKey] === error.status)) return true;
812
+ if (this.retry.retryStatusCodes?.some((codeKey) => HTTPStatusCode[codeKey] === error.status)) return true;
804
813
  }
805
814
  return false;
806
815
  };
@@ -864,98 +873,38 @@ var HttpClient = class {
864
873
  }
865
874
  /**
866
875
  * Create a strongly-typed endpoint builder.
876
+ * Works with any Standard Schema-compatible library (Zod, Valibot, ArkType, etc.)
877
+ *
878
+ * @param config - Endpoint configuration with schemas
879
+ * @returns Endpoint call function
880
+ *
881
+ * @example
882
+ * ```ts
883
+ * // With Zod
884
+ * import { z } from 'zod';
885
+ * const getUser = client.createEndpoint({
886
+ * method: 'GET',
887
+ * path: '/users/:id',
888
+ * response: z.object({ id: z.string(), name: z.string() }),
889
+ * pathParams: z.object({ id: z.string() }),
890
+ * });
867
891
  *
868
- * @param config - Endpoint configuration
869
- * @returns Endpoint instance
892
+ * // With Valibot
893
+ * import * as v from 'valibot';
894
+ * const getUser = client.createEndpoint({
895
+ * method: 'GET',
896
+ * path: '/users/:id',
897
+ * response: v.object({ id: v.string(), name: v.string() }),
898
+ * pathParams: v.object({ id: v.string() }),
899
+ * });
900
+ * ```
870
901
  */
871
902
  createEndpoint(config) {
872
903
  const endpoint = new EndpointImpl(this, config);
873
904
  return (params) => endpoint.call(params);
874
905
  }
875
906
  };
876
-
877
907
  //#endregion
878
- //#region lib/schemas/common.ts
879
- /**
880
- * Common ID type that supports strings, numbers, or UUIDs.
881
- * Use this for entity identifiers in your schemas.
882
- *
883
- * @example
884
- * ```ts
885
- * const UserSchema = z.object({ id: Id, name: z.string() });
886
- * ```
887
- */
888
- const Id = z.union([
889
- z.string().min(1),
890
- z.number(),
891
- z.uuid({ version: "v4" })
892
- ]);
893
- /**
894
- * Common timestamp fields for entities.
895
- * Use this for database models with creation/update tracking.
896
- *
897
- * @example
898
- * ```ts
899
- * const UserSchema = z.object({
900
- * id: Id,
901
- * name: z.string(),
902
- * ...Timestamps.shape
903
- * });
904
- * ```
905
- */
906
- const Timestamps = z.object({
907
- createdAt: z.iso.datetime(),
908
- updatedAt: z.iso.datetime()
909
- });
910
- /**
911
- * Metadata information typically included in API responses.
912
- * Contains request tracking and debugging information.
913
- */
914
- const Meta = z.object({ timestamp: z.iso.datetime().optional() });
915
- /**
916
- * Detailed error information for a specific field or path.
917
- */
918
- const ErrorDetail = z.object({
919
- path: z.string().optional(),
920
- message: z.string()
921
- });
922
- /**
923
- * Standard API error response schema.
924
- * Use this for consistent error handling across your API.
925
- */
926
- const ApiErrorSchema = z.object({
927
- code: z.string(),
928
- message: z.string(),
929
- details: z.array(ErrorDetail).optional()
930
- });
931
- /**
932
- * Generic envelope wrapper for API responses.
933
- * Provides consistent structure with success flag, data, error, and metadata.
934
- *
935
- * @param inner - Zod schema for the response data
936
- * @returns Envelope schema wrapping the inner schema
937
- *
938
- * @example
939
- * ```ts
940
- * const UserResponseSchema = Envelope(z.object({ id: Id, name: z.string() }));
941
- *
942
- * // Response structure:
943
- * // {
944
- * // success: true,
945
- * // data: { id: 1, name: 'John' },
946
- * // meta: { timestamp: '...' }
947
- * // }
948
- * ```
949
- */
950
- const Envelope = (inner, meta) => {
951
- return z.object({
952
- success: z.boolean(),
953
- data: inner.optional().nullable(),
954
- error: ApiErrorSchema.optional(),
955
- meta: meta !== void 0 ? meta : Meta.optional()
956
- });
957
- };
908
+ export { ApiError, ApiKeyAuth, BearerTokenAuth, ConsoleLogger, ConsoleMetricsCollector, EndpointImpl, HTTPMethod, HTTPStatusCode, HttpClient, InMemoryMetricsCollector, LogLevel, LoggerUtil, NoAuth, NoOpLogger, NoOpMetricsCollector, SchemaDefinitionError, isStandardSchema, parseOrThrow, safeParse, toQueryString };
958
909
 
959
- //#endregion
960
- export { ApiError, ApiErrorSchema, ApiKeyAuth, BearerTokenAuth, ConsoleLogger, ConsoleMetricsCollector, EndpointImpl, Envelope, ErrorDetail, HTTPMethod, HTTPStatusCode, HttpClient, Id, InMemoryMetricsCollector, LogLevel, LoggerUtil, Meta, NoAuth, NoOpLogger, NoOpMetricsCollector, PaginationSchema, SchemaDefinitionError, Timestamps, toQueryString };
961
910
  //# sourceMappingURL=index.js.map