zlient 1.0.10 → 2.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.cjs CHANGED
@@ -21,9 +21,83 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
21
21
  }) : target, mod));
22
22
 
23
23
  //#endregion
24
- let zod = require("zod");
25
- zod = __toESM(zod);
24
+ const zod = __toESM(require("zod"));
26
25
 
26
+ //#region lib/auth.ts
27
+ /**
28
+ * No-op authentication provider (no authentication applied).
29
+ * Use this when you don't need authentication.
30
+ */
31
+ var NoAuth = class {
32
+ async apply() {}
33
+ };
34
+ /**
35
+ * API Key authentication provider.
36
+ * Supports both header-based and query parameter-based authentication.
37
+ *
38
+ * @example
39
+ * ```ts
40
+ * // Header-based
41
+ * const auth = new ApiKeyAuth({ header: 'X-API-Key', value: 'secret' });
42
+ *
43
+ * // Query parameter-based
44
+ * const auth = new ApiKeyAuth({ query: 'apiKey', value: 'secret' });
45
+ * ```
46
+ */
47
+ var ApiKeyAuth = class {
48
+ constructor(opts) {
49
+ this.opts = opts;
50
+ if (!opts.header && !opts.query) throw new Error("ApiKeyAuth requires either \"header\" or \"query\" option");
51
+ if (opts.header && opts.query) throw new Error("ApiKeyAuth cannot use both \"header\" and \"query\" options");
52
+ }
53
+ apply({ url, init }) {
54
+ const value = this.opts.value;
55
+ if (this.opts.header) if (init.headers instanceof Headers) init.headers.set(this.opts.header, value);
56
+ else if (Array.isArray(init.headers)) init.headers.push([this.opts.header, value]);
57
+ else init.headers = {
58
+ ...init.headers,
59
+ [this.opts.header]: value
60
+ };
61
+ else if (this.opts.query) {
62
+ const u = new URL(url);
63
+ u.searchParams.set(this.opts.query, value);
64
+ init.__urlOverride = u.toString();
65
+ }
66
+ }
67
+ };
68
+ /**
69
+ * Bearer token authentication provider.
70
+ * Supports both static tokens and dynamic token fetching (e.g., for OAuth2 refresh).
71
+ *
72
+ * @example
73
+ * ```ts
74
+ * // Static token
75
+ * const auth = new BearerTokenAuth(() => 'my-token');
76
+ *
77
+ * // Dynamic token with refresh
78
+ * const auth = new BearerTokenAuth(async () => {
79
+ * return await refreshAccessToken();
80
+ * });
81
+ * ```
82
+ */
83
+ var BearerTokenAuth = class {
84
+ constructor(getToken) {
85
+ this.getToken = getToken;
86
+ }
87
+ async apply({ init }) {
88
+ const token = await this.getToken();
89
+ if (!token) throw new Error("BearerTokenAuth: token is empty or undefined");
90
+ const authHeader = `Bearer ${token}`;
91
+ if (init.headers instanceof Headers) init.headers.set("Authorization", authHeader);
92
+ else if (Array.isArray(init.headers)) init.headers.push(["Authorization", authHeader]);
93
+ else init.headers = {
94
+ ...init.headers,
95
+ Authorization: authHeader
96
+ };
97
+ }
98
+ };
99
+
100
+ //#endregion
27
101
  //#region lib/types.ts
28
102
  const HTTPMethod = {
29
103
  GET: "GET",
@@ -34,6 +108,70 @@ const HTTPMethod = {
34
108
  HEAD: "HEAD",
35
109
  OPTIONS: "OPTIONS"
36
110
  };
111
+ const HTTPStatusCode = {
112
+ CONTINUE: 100,
113
+ SWITCHING_PROTOCOLS: 101,
114
+ PROCESSING: 102,
115
+ EARLY_HINTS: 103,
116
+ OK: 200,
117
+ CREATED: 201,
118
+ ACCEPTED: 202,
119
+ NON_AUTHORITATIVE_INFORMATION: 203,
120
+ NO_CONTENT: 204,
121
+ RESET_CONTENT: 205,
122
+ PARTIAL_CONTENT: 206,
123
+ MULTI_STATUS: 207,
124
+ ALREADY_REPORTED: 208,
125
+ IM_USED: 226,
126
+ MULTIPLE_CHOICES: 300,
127
+ MOVED_PERMANENTLY: 301,
128
+ FOUND: 302,
129
+ SEE_OTHER: 303,
130
+ NOT_MODIFIED: 304,
131
+ USE_PROXY: 305,
132
+ TEMPORARY_REDIRECT: 307,
133
+ PERMANENT_REDIRECT: 308,
134
+ BAD_REQUEST: 400,
135
+ UNAUTHORIZED: 401,
136
+ PAYMENT_REQUIRED: 402,
137
+ FORBIDDEN: 403,
138
+ NOT_FOUND: 404,
139
+ METHOD_NOT_ALLOWED: 405,
140
+ NOT_ACCEPTABLE: 406,
141
+ PROXY_AUTHENTICATION_REQUIRED: 407,
142
+ REQUEST_TIMEOUT: 408,
143
+ CONFLICT: 409,
144
+ GONE: 410,
145
+ LENGTH_REQUIRED: 411,
146
+ PRECONDITION_FAILED: 412,
147
+ PAYLOAD_TOO_LARGE: 413,
148
+ URI_TOO_LONG: 414,
149
+ UNSUPPORTED_MEDIA_TYPE: 415,
150
+ RANGE_NOT_SATISFIABLE: 416,
151
+ EXPECTATION_FAILED: 417,
152
+ IM_A_TEAPOT: 418,
153
+ MISDIRECTED_REQUEST: 421,
154
+ UNPROCESSABLE_ENTITY: 422,
155
+ LOCKED: 423,
156
+ FAILED_DEPENDENCY: 424,
157
+ TOO_EARLY: 425,
158
+ UPGRADE_REQUIRED: 426,
159
+ PRECONDITION_REQUIRED: 428,
160
+ TOO_MANY_REQUESTS: 429,
161
+ REQUEST_HEADER_FIELDS_TOO_LARGE: 431,
162
+ UNAVAILABLE_FOR_LEGAL_REASONS: 451,
163
+ INTERNAL_SERVER_ERROR: 500,
164
+ NOT_IMPLEMENTED: 501,
165
+ BAD_GATEWAY: 502,
166
+ SERVICE_UNAVAILABLE: 503,
167
+ GATEWAY_TIMEOUT: 504,
168
+ HTTP_VERSION_NOT_SUPPORTED: 505,
169
+ VARIANT_ALSO_NEGOTIATES: 506,
170
+ INSUFFICIENT_STORAGE: 507,
171
+ LOOP_DETECTED: 508,
172
+ NOT_EXTENDED: 510,
173
+ NETWORK_AUTHENTICATION_REQUIRED: 511
174
+ };
37
175
  /**
38
176
  * Custom error class for API-related errors.
39
177
  * Includes HTTP status codes, response details, and validation errors.
@@ -92,6 +230,18 @@ var ApiError = class ApiError extends Error {
92
230
  }
93
231
  };
94
232
  /**
233
+ * Error thrown when an endpoint receives a response with a status code
234
+ * that has no defined schema in the endpoint configuration.
235
+ */
236
+ var SchemaDefinitionError = class SchemaDefinitionError extends Error {
237
+ constructor(status) {
238
+ super(`No schema defined for status code ${status}`);
239
+ this.status = status;
240
+ this.name = "SchemaDefinitionError";
241
+ if (Error.captureStackTrace) Error.captureStackTrace(this, SchemaDefinitionError);
242
+ }
243
+ };
244
+ /**
95
245
  * Schema for paginated responses
96
246
  */
97
247
  const PaginationSchema = zod.z.object({
@@ -127,107 +277,9 @@ function toQueryString(q) {
127
277
  return s ? `?${s}` : "";
128
278
  }
129
279
 
130
- //#endregion
131
- //#region lib/auth.ts
132
- /**
133
- * No-op authentication provider (no authentication applied).
134
- * Use this when you don't need authentication.
135
- */
136
- var NoAuth = class {
137
- async apply() {}
138
- };
139
- /**
140
- * API Key authentication provider.
141
- * Supports both header-based and query parameter-based authentication.
142
- *
143
- * @example
144
- * ```ts
145
- * // Header-based
146
- * const auth = new ApiKeyAuth({ header: 'X-API-Key', value: 'secret' });
147
- *
148
- * // Query parameter-based
149
- * const auth = new ApiKeyAuth({ query: 'apiKey', value: 'secret' });
150
- * ```
151
- */
152
- var ApiKeyAuth = class {
153
- constructor(opts) {
154
- this.opts = opts;
155
- if (!opts.header && !opts.query) throw new Error("ApiKeyAuth requires either \"header\" or \"query\" option");
156
- if (opts.header && opts.query) throw new Error("ApiKeyAuth cannot use both \"header\" and \"query\" options");
157
- }
158
- apply({ url, init }) {
159
- if (this.opts.header) init.headers = {
160
- ...init.headers,
161
- [this.opts.header]: this.opts.value
162
- };
163
- else if (this.opts.query) {
164
- const u = new URL(url);
165
- u.searchParams.set(this.opts.query, this.opts.value);
166
- init.__urlOverride = u.toString();
167
- }
168
- }
169
- };
170
- /**
171
- * Bearer token authentication provider.
172
- * Supports both static tokens and dynamic token fetching (e.g., for OAuth2 refresh).
173
- *
174
- * @example
175
- * ```ts
176
- * // Static token
177
- * const auth = new BearerTokenAuth(() => 'my-token');
178
- *
179
- * // Dynamic token with refresh
180
- * const auth = new BearerTokenAuth(async () => {
181
- * return await refreshAccessToken();
182
- * });
183
- * ```
184
- */
185
- var BearerTokenAuth = class {
186
- constructor(getToken) {
187
- this.getToken = getToken;
188
- }
189
- async apply({ init }) {
190
- const token = await this.getToken();
191
- if (!token) throw new Error("BearerTokenAuth: token is empty or undefined");
192
- init.headers = {
193
- ...init.headers,
194
- Authorization: `Bearer ${token}`
195
- };
196
- }
197
- };
198
-
199
280
  //#endregion
200
281
  //#region lib/validation.ts
201
282
  /**
202
- * Safely parse data with a Zod schema without throwing.
203
- * Returns a result object with success status and data or error.
204
- *
205
- * @param schema - Zod schema to validate against
206
- * @param data - Data to validate
207
- * @returns Result object with success flag and data/error
208
- *
209
- * @example
210
- * ```ts
211
- * const result = safeParse(UserSchema, userData);
212
- * if (result.success) {
213
- * console.log(result.data);
214
- * } else {
215
- * console.error(result.error);
216
- * }
217
- * ```
218
- */
219
- function safeParse(schema, data) {
220
- const res = schema.safeParse(data);
221
- if (res.success) return {
222
- success: true,
223
- data: res.data
224
- };
225
- return {
226
- success: false,
227
- error: res.error
228
- };
229
- }
230
- /**
231
283
  * Parse data with a Zod schema, throwing an ApiError on validation failure.
232
284
  * Use this when you want to fail fast on invalid data.
233
285
  *
@@ -254,6 +306,48 @@ function parseOrThrow(schema, data) {
254
306
  return res.data;
255
307
  }
256
308
 
309
+ //#endregion
310
+ //#region lib/endpoint/base-endpoint.ts
311
+ var Endpoint = class {
312
+ constructor(client, config) {
313
+ this.client = client;
314
+ this.config = config;
315
+ }
316
+ async call(params) {
317
+ const { data, query, pathParams, headers, signal } = params;
318
+ if (this.config.request && data !== void 0) {
319
+ const parsed = this.config.request.safeParse(data);
320
+ if (!parsed.success) throw parsed.error;
321
+ }
322
+ if (this.config.query && query !== void 0) {
323
+ const parsed = this.config.query.safeParse(query);
324
+ if (!parsed.success) throw parsed.error;
325
+ }
326
+ if (this.config.pathParams && pathParams !== void 0) {
327
+ const parsed = this.config.pathParams.safeParse(pathParams);
328
+ if (!parsed.success) throw parsed.error;
329
+ }
330
+ if (this.config.request && data === void 0) throw new Error("Missing required request body (data)");
331
+ if (this.config.pathParams && pathParams === void 0) throw new Error("Missing required path parameters (pathParams)");
332
+ let pathStr;
333
+ if (typeof this.config.path === "function") {
334
+ if (!pathParams) throw new Error("Path function requires pathParams");
335
+ pathStr = this.config.path(pathParams);
336
+ } else pathStr = this.config.path;
337
+ const { data: responseData, status } = await this.client.request(this.config.method, pathStr, data, {
338
+ query,
339
+ headers,
340
+ baseUrlKey: this.config.baseUrlKey,
341
+ signal
342
+ });
343
+ const schema = this.config.response;
344
+ if (schema instanceof zod.z.ZodType) return parseOrThrow(schema, responseData);
345
+ const specificSchema = schema[status];
346
+ if (!specificSchema) throw new SchemaDefinitionError(status);
347
+ return parseOrThrow(specificSchema, responseData);
348
+ }
349
+ };
350
+
257
351
  //#endregion
258
352
  //#region lib/logger.ts
259
353
  /**
@@ -281,7 +375,9 @@ var ConsoleLogger = class {
281
375
  LogLevel.WARN,
282
376
  LogLevel.ERROR
283
377
  ];
284
- if (levels.indexOf(entry.level) < levels.indexOf(this.minLevel)) return;
378
+ const entryLevelIndex = levels.indexOf(entry.level);
379
+ const minLevelIndex = levels.indexOf(this.minLevel);
380
+ if (entryLevelIndex < minLevelIndex) return;
285
381
  const output = {
286
382
  ...entry,
287
383
  error: entry.error ? {
@@ -324,7 +420,7 @@ var LoggerUtil = class {
324
420
  this.logger.log({
325
421
  level: LogLevel.DEBUG,
326
422
  message,
327
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
423
+ timestamp: new Date().toISOString(),
328
424
  context
329
425
  });
330
426
  }
@@ -332,7 +428,7 @@ var LoggerUtil = class {
332
428
  this.logger.log({
333
429
  level: LogLevel.INFO,
334
430
  message,
335
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
431
+ timestamp: new Date().toISOString(),
336
432
  context
337
433
  });
338
434
  }
@@ -340,7 +436,7 @@ var LoggerUtil = class {
340
436
  this.logger.log({
341
437
  level: LogLevel.WARN,
342
438
  message,
343
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
439
+ timestamp: new Date().toISOString(),
344
440
  context
345
441
  });
346
442
  }
@@ -348,7 +444,7 @@ var LoggerUtil = class {
348
444
  this.logger.log({
349
445
  level: LogLevel.ERROR,
350
446
  message,
351
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
447
+ timestamp: new Date().toISOString(),
352
448
  context,
353
449
  error
354
450
  });
@@ -396,15 +492,22 @@ var InMemoryMetricsCollector = class {
396
492
  maxDurationMs: 0
397
493
  };
398
494
  const successful = this.metrics.filter((m) => m.success).length;
399
- const durations = this.metrics.map((m) => m.durationMs);
400
- const sum = durations.reduce((a, b) => a + b, 0);
495
+ let sum = 0;
496
+ let min = Infinity;
497
+ let max = -Infinity;
498
+ for (const m of this.metrics) {
499
+ const d = m.durationMs;
500
+ sum += d;
501
+ if (d < min) min = d;
502
+ if (d > max) max = d;
503
+ }
401
504
  return {
402
505
  total: this.metrics.length,
403
506
  successful,
404
507
  failed: this.metrics.length - successful,
405
508
  avgDurationMs: sum / this.metrics.length,
406
- minDurationMs: Math.min(...durations),
407
- maxDurationMs: Math.max(...durations)
509
+ minDurationMs: min === Infinity ? 0 : min,
510
+ maxDurationMs: max === -Infinity ? 0 : max
408
511
  };
409
512
  }
410
513
  /**
@@ -471,6 +574,10 @@ var HttpClient = class {
471
574
  jitter: .2,
472
575
  retryMethods: ["GET", "HEAD"]
473
576
  };
577
+ if (!this.retry.retryStatusCodes) this.retry.retryStatusCodes = Object.keys(HTTPStatusCode).filter((key) => {
578
+ const code = HTTPStatusCode[key];
579
+ return typeof code === "number" && code >= 500;
580
+ });
474
581
  if (this.retry.maxRetries < 0) throw new Error("retry.maxRetries must be non-negative");
475
582
  if (this.retry.baseDelayMs < 0) throw new Error("retry.baseDelayMs must be non-negative");
476
583
  if (this.retry.jitter !== void 0 && (this.retry.jitter < 0 || this.retry.jitter > 1)) throw new Error("retry.jitter must be between 0 and 1");
@@ -550,6 +657,23 @@ var HttpClient = class {
550
657
  });
551
658
  }
552
659
  /**
660
+ * Get all configured base URLs.
661
+ *
662
+ * @returns Object mapping base URL keys to their resolved URLs
663
+ */
664
+ getBaseUrls() {
665
+ return this.baseUrls;
666
+ }
667
+ /**
668
+ * Get the resolved base URL for a given key.
669
+ *
670
+ * @param key - Base URL key (defaults to 'default' if not provided)
671
+ * @returns Resolved base URL string
672
+ */
673
+ getBaseUrl(key) {
674
+ return this.resolveBaseUrl(key);
675
+ }
676
+ /**
553
677
  * Make an HTTP request with automatic retry, authentication, and validation.
554
678
  *
555
679
  * @param method - HTTP method (GET, POST, PUT, etc.)
@@ -569,7 +693,8 @@ var HttpClient = class {
569
693
  */
570
694
  async request(method, path, body, options) {
571
695
  const startTime = Date.now();
572
- let url = `${this.resolveBaseUrl(options?.baseUrlKey)}${path}${toQueryString(options?.query)}`;
696
+ const base = this.resolveBaseUrl(options?.baseUrlKey);
697
+ let url = `${base}${path}${toQueryString(options?.query)}`;
573
698
  this.logger.debug("HTTP request initiated", {
574
699
  method,
575
700
  path,
@@ -598,26 +723,16 @@ var HttpClient = class {
598
723
  const doFetch = async () => {
599
724
  let timeoutId;
600
725
  if (this.timeoutMs && !options?.signal) timeoutId = setTimeout(() => {
601
- const timeoutError = /* @__PURE__ */ new Error("Request timeout");
726
+ const timeoutError = new Error("Request timeout");
602
727
  timeoutError.name = "TimeoutError";
603
728
  controller.abort(timeoutError);
604
729
  }, this.timeoutMs);
605
730
  try {
606
731
  const req = new Request(url, init);
607
732
  const res = await this.fetchImpl(req);
608
- if (!res.ok) {
609
- let text = "";
610
- try {
611
- text = await res.text();
612
- } catch (readError) {
613
- text = `Failed to read response: ${readError instanceof Error ? readError.message : String(readError)}`;
614
- }
615
- throw new ApiError(`HTTP ${res.status}: ${res.statusText}`, {
616
- status: res.status,
617
- details: text
618
- });
619
- }
620
- const data = (res.headers.get("content-type") || "").includes("json") ? await res.json() : await res.text();
733
+ const status = res.status;
734
+ const contentType = res.headers.get("content-type") || "";
735
+ const data = contentType.includes("json") ? await res.json() : await res.text();
621
736
  await this.runAfterHooks(new Request(url, init), res, data);
622
737
  const duration = Date.now() - startTime;
623
738
  this.logger.info("HTTP request successful", {
@@ -631,12 +746,12 @@ var HttpClient = class {
631
746
  path,
632
747
  status: res.status,
633
748
  durationMs: duration,
634
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
749
+ timestamp: new Date().toISOString(),
635
750
  success: true
636
751
  });
637
752
  return {
638
753
  data,
639
- response: res
754
+ status
640
755
  };
641
756
  } catch (error) {
642
757
  const duration = Date.now() - startTime;
@@ -650,7 +765,7 @@ var HttpClient = class {
650
765
  path,
651
766
  status: error instanceof ApiError ? error.status : void 0,
652
767
  durationMs: duration,
653
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
768
+ timestamp: new Date().toISOString(),
654
769
  success: false,
655
770
  error: error instanceof Error ? error.message : String(error)
656
771
  });
@@ -659,13 +774,15 @@ var HttpClient = class {
659
774
  if (timeoutId) clearTimeout(timeoutId);
660
775
  }
661
776
  };
662
- const canRetry = ({ response, error }) => {
777
+ const canRetry = ({ error }) => {
663
778
  if (error && typeof error === "object" && "name" in error) {
664
779
  const errorName = error.name;
665
780
  if (errorName === "AbortError" || errorName === "TimeoutError") return false;
666
781
  }
667
- if (error instanceof ApiError && error.status && error.status >= 500) return true;
668
- if (error && !response) return true;
782
+ if (error instanceof ApiError && error.status) {
783
+ const retryCodes = this.retry.retryStatusCodes;
784
+ if (retryCodes?.some((codeKey) => HTTPStatusCode[codeKey] === error.status)) return true;
785
+ }
669
786
  return false;
670
787
  };
671
788
  if (!this.retry.retryMethods?.includes(method)) return doFetch();
@@ -726,109 +843,14 @@ var HttpClient = class {
726
843
  async delete(path, options) {
727
844
  return this.request("DELETE", path, void 0, options);
728
845
  }
729
- };
730
-
731
- //#endregion
732
- //#region lib/endpoint/base-endpoint.ts
733
- /**
734
- * Generic, strongly-typed endpoint with Zod schemas for request and response validation.
735
- * Extend this class to create type-safe API endpoints.
736
- *
737
- * @template ReqSchema - Zod schema for request validation
738
- * @template ResSchema - Zod schema for response validation
739
- * @template PathParams - Type for path parameters (optional)
740
- * @template QueryParams - Type for query parameters (optional)
741
- *
742
- * @example
743
- * ```ts
744
- * const UserSchema = z.object({ id: z.number(), name: z.string() });
745
- * const CreateUserSchema = z.object({ name: z.string() });
746
- *
747
- * type UserPathParams = { id: string };
748
- * type UserQueryParams = { include?: string; limit?: number };
749
- *
750
- * class GetUser extends BaseEndpoint<
751
- * typeof CreateUserSchema,
752
- * typeof UserSchema,
753
- * UserPathParams,
754
- * UserQueryParams
755
- * > {
756
- * protected method = 'GET' as const;
757
- * protected path = (params: UserPathParams) => `/users/${params.id}`;
758
- *
759
- * constructor(client: HttpClient) {
760
- * super(client, {
761
- * requestSchema: CreateUserSchema,
762
- * responseSchema: UserSchema
763
- * });
764
- * }
765
- * }
766
- *
767
- * // Usage:
768
- * const user = await endpoint.call({
769
- * pathParams: { id: '123' },
770
- * query: { include: 'posts', limit: 10 }
771
- * });
772
- * ```
773
- */
774
- var BaseEndpoint = class {
775
- /** Additional options for the request */
776
- options;
777
- /** Optional request schema for validation */
778
- requestSchema;
779
- /** Response schema for validation */
780
- responseSchema;
781
846
  /**
782
- * @param client - HttpClient instance
783
- * @param cfg - Configuration with request and response schemas
784
- */
785
- constructor(client, cfg) {
786
- this.client = client;
787
- this.requestSchema = cfg.requestSchema;
788
- this.responseSchema = cfg.responseSchema;
789
- }
790
- /**
791
- * Call the endpoint with strong typing derived from schemas.
792
- * Validates request data before sending and response data after receiving.
793
- *
794
- * @param config - Request configuration object containing all parameters
795
- * @returns Promise resolving to validated response data (typed by ResSchema)
796
- * @throws {ZodError} If request validation fails
797
- * @throws {ApiError} If response validation fails or request fails
847
+ * Create a strongly-typed endpoint builder.
798
848
  *
799
- * @example
800
- * ```ts
801
- * const endpoint = new GetUser(client);
802
- * const user = await endpoint.call({
803
- * pathParams: { id: '123' },
804
- * query: { include: 'posts' }
805
- * });
806
- * // With additional options:
807
- * const user = await endpoint.call({
808
- * data: { name: 'John' },
809
- * pathParams: { id: '123' },
810
- * headers: { 'X-Custom': 'value' },
811
- * query: { include: 'posts' }
812
- * });
813
- * ```
849
+ * @param config - Endpoint configuration
850
+ * @returns Endpoint instance
814
851
  */
815
- async call(config = {}) {
816
- const { data, query, headers, baseUrlKey, signal, pathParams } = config;
817
- if (this.requestSchema && data !== void 0) {
818
- const parsed = this.requestSchema.safeParse(data);
819
- if (!parsed.success) throw parsed.error;
820
- }
821
- const pathArgs = pathParams ?? data;
822
- const path = typeof this.path === "function" ? this.path(pathArgs) : this.path;
823
- const body = this.method !== "GET" && this.method !== "HEAD" ? data : void 0;
824
- const queryForRequest = query;
825
- const { data: responseData } = await this.client.request(this.method, path, body, {
826
- query: queryForRequest,
827
- headers,
828
- baseUrlKey: baseUrlKey ?? this.options?.baseUrlKey,
829
- signal
830
- });
831
- return parseOrThrow(this.responseSchema, responseData);
852
+ createEndpoint(config) {
853
+ return new Endpoint(this, config);
832
854
  }
833
855
  };
834
856
 
@@ -920,13 +942,14 @@ const Envelope = (inner) => zod.z.object({
920
942
  exports.ApiError = ApiError;
921
943
  exports.ApiErrorSchema = ApiErrorSchema;
922
944
  exports.ApiKeyAuth = ApiKeyAuth;
923
- exports.BaseEndpoint = BaseEndpoint;
924
945
  exports.BearerTokenAuth = BearerTokenAuth;
925
946
  exports.ConsoleLogger = ConsoleLogger;
926
947
  exports.ConsoleMetricsCollector = ConsoleMetricsCollector;
948
+ exports.Endpoint = Endpoint;
927
949
  exports.Envelope = Envelope;
928
950
  exports.ErrorDetail = ErrorDetail;
929
951
  exports.HTTPMethod = HTTPMethod;
952
+ exports.HTTPStatusCode = HTTPStatusCode;
930
953
  exports.HttpClient = HttpClient;
931
954
  exports.Id = Id;
932
955
  exports.InMemoryMetricsCollector = InMemoryMetricsCollector;
@@ -937,8 +960,7 @@ exports.NoAuth = NoAuth;
937
960
  exports.NoOpLogger = NoOpLogger;
938
961
  exports.NoOpMetricsCollector = NoOpMetricsCollector;
939
962
  exports.PaginationSchema = PaginationSchema;
963
+ exports.SchemaDefinitionError = SchemaDefinitionError;
940
964
  exports.Timestamps = Timestamps;
941
- exports.parseOrThrow = parseOrThrow;
942
- exports.safeParse = safeParse;
943
965
  exports.toQueryString = toQueryString;
944
966
  //# sourceMappingURL=index.cjs.map