@tahanabavi/typefetch 1.1.1 → 1.2.2

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.mjs CHANGED
@@ -4008,6 +4008,10 @@ var ApiClient = class {
4008
4008
  this.mockDelay = config.mockDelay || { min: 100, max: 1e3 };
4009
4009
  this.tokenProvider = config.tokenProvider;
4010
4010
  }
4011
+ /**
4012
+ * Builds the strongly-typed `modules` API from the provided contracts.
4013
+ * Must be called once after constructing the client.
4014
+ */
4011
4015
  init() {
4012
4016
  const modules = {};
4013
4017
  for (const moduleName in this.contracts) {
@@ -4020,30 +4024,62 @@ var ApiClient = class {
4020
4024
  }
4021
4025
  this._modules = modules;
4022
4026
  }
4027
+ /**
4028
+ * Type-safe entrypoint for calling API endpoints.
4029
+ * Populated by `init()` based on the `contracts` passed to the constructor.
4030
+ */
4023
4031
  get modules() {
4024
4032
  return this._modules;
4025
4033
  }
4034
+ /**
4035
+ * Registers a middleware in the pipeline.
4036
+ * Middlewares are executed in reverse order of registration.
4037
+ */
4026
4038
  use(middleware, options) {
4027
4039
  this.middlewares.push({ fn: middleware, options });
4028
4040
  }
4041
+ /**
4042
+ * Registers a global error handler.
4043
+ * The handler is invoked for normalized errors before they are re-thrown.
4044
+ */
4029
4045
  onError(handler) {
4030
4046
  this.errorHandler = handler;
4031
4047
  }
4048
+ /**
4049
+ * Registers a transformation function applied to all successful responses
4050
+ * after Zod parsing.
4051
+ */
4032
4052
  useResponseTransform(fn) {
4033
4053
  this.responseTransform = fn;
4034
4054
  }
4055
+ /**
4056
+ * Enables or disables mock mode. When enabled, endpoints with `mockData`
4057
+ * return mocked responses instead of performing network requests.
4058
+ */
4035
4059
  setMockMode(enabled, delay) {
4036
4060
  this.useMockData = enabled;
4037
4061
  if (delay) {
4038
4062
  this.mockDelay = delay;
4039
4063
  }
4040
4064
  }
4065
+ /**
4066
+ * Registers a schema wrapper for APIs that wrap data in an envelope.
4067
+ * Example: { success, data, message, code, ... }.
4068
+ */
4041
4069
  setResponseWrapper(wrapper) {
4042
4070
  this.responseWrapper = wrapper;
4043
4071
  }
4072
+ /**
4073
+ * Sets or updates the token provider used for authenticated endpoints.
4074
+ * Overrides any static token provided in the constructor.
4075
+ */
4044
4076
  setTokenProvider(provider) {
4045
4077
  this.tokenProvider = provider;
4046
4078
  }
4079
+ /**
4080
+ * Returns the current token, preferring the tokenProvider if present,
4081
+ * otherwise falling back to the static token from the constructor.
4082
+ */
4047
4083
  getCurrentToken() {
4048
4084
  return __async(this, null, function* () {
4049
4085
  if (this.tokenProvider) {
@@ -4052,10 +4088,23 @@ var ApiClient = class {
4052
4088
  return this.config.token;
4053
4089
  });
4054
4090
  }
4091
+ /**
4092
+ * Executes a single endpoint request.
4093
+ *
4094
+ * Expected request shape (new style):
4095
+ * z.object({
4096
+ * path: z.object({...}).optional(),
4097
+ * query: z.object({...}).optional(),
4098
+ * body: z.any().optional(),
4099
+ * })
4100
+ *
4101
+ * If the parsed request does not contain `path`, `query` or `body`,
4102
+ * the entire input is treated as the legacy flat request body.
4103
+ */
4055
4104
  request(endpoint, input) {
4056
4105
  return __async(this, null, function* () {
4057
4106
  var _a, _b, _c, _d;
4058
- endpoint.request.parse(input);
4107
+ const parsedInput = endpoint.request.parse(input);
4059
4108
  if (this.useMockData && endpoint.mockData) {
4060
4109
  return this.handleMockRequest(endpoint);
4061
4110
  }
@@ -4076,12 +4125,13 @@ var ApiClient = class {
4076
4125
  if (endpoint.auth && token) {
4077
4126
  headers["Authorization"] = `Bearer ${token}`;
4078
4127
  }
4128
+ const { url, body } = this.buildUrlAndBody(endpoint, parsedInput);
4079
4129
  const ctx = {
4080
- url: this.config.baseUrl + endpoint.path,
4130
+ url,
4081
4131
  init: {
4082
4132
  method: endpoint.method,
4083
4133
  headers,
4084
- body: endpoint.method !== "GET" ? JSON.stringify(input) : void 0
4134
+ body
4085
4135
  }
4086
4136
  };
4087
4137
  const runner = this.middlewares.reduceRight(
@@ -4126,6 +4176,85 @@ var ApiClient = class {
4126
4176
  }
4127
4177
  });
4128
4178
  }
4179
+ /**
4180
+ * Builds the effective URL and request body for an endpoint.
4181
+ *
4182
+ * - Legacy mode: if the input does not contain `path`, `query` or `body`,
4183
+ * the entire input is used as the JSON request body for non-GET methods.
4184
+ *
4185
+ * - New mode: uses `path` to interpolate `:param` segments, `query` to
4186
+ * construct the query string, and `body` as the JSON payload.
4187
+ */
4188
+ buildUrlAndBody(endpoint, parsedInput) {
4189
+ const isObject = typeof parsedInput === "object" && parsedInput !== null;
4190
+ const hasNewShape = isObject && ("path" in parsedInput || "query" in parsedInput || "body" in parsedInput);
4191
+ if (!hasNewShape) {
4192
+ const url2 = this.config.baseUrl + endpoint.path;
4193
+ let requestBody2 = void 0;
4194
+ if (endpoint.method !== "GET") {
4195
+ requestBody2 = JSON.stringify(parsedInput);
4196
+ }
4197
+ return { url: url2, body: requestBody2 };
4198
+ }
4199
+ const { path, query, body } = parsedInput;
4200
+ let url = this.config.baseUrl + endpoint.path;
4201
+ if (path) {
4202
+ for (const [key, value] of Object.entries(path)) {
4203
+ if (value === void 0 || value === null) continue;
4204
+ const token = `:${key}`;
4205
+ if (!url.includes(token)) {
4206
+ continue;
4207
+ }
4208
+ url = url.replace(token, encodeURIComponent(String(value)));
4209
+ }
4210
+ }
4211
+ const templatePlaceholders = Array.from(
4212
+ endpoint.path.matchAll(/:([A-Za-z0-9_]+)/g)
4213
+ ).map((m) => m[1]);
4214
+ const missingTokens = templatePlaceholders.filter((name) => {
4215
+ const v = path ? path[name] : void 0;
4216
+ return v === void 0 || v === null;
4217
+ });
4218
+ if (missingTokens.length > 0) {
4219
+ throw this.createError({
4220
+ message: `Missing path params for placeholders: ${missingTokens.join(
4221
+ ", "
4222
+ )} in "${endpoint.path}"`,
4223
+ code: "MISSING_PATH_PARAMS"
4224
+ });
4225
+ }
4226
+ if (query) {
4227
+ const searchParams = new URLSearchParams();
4228
+ for (const [key, value] of Object.entries(query)) {
4229
+ if (value === void 0 || value === null) continue;
4230
+ if (Array.isArray(value)) {
4231
+ for (const v of value) {
4232
+ if (v === void 0 || v === null) continue;
4233
+ searchParams.append(key, String(v));
4234
+ }
4235
+ } else if (typeof value === "object") {
4236
+ searchParams.append(key, JSON.stringify(value));
4237
+ } else {
4238
+ searchParams.append(key, String(value));
4239
+ }
4240
+ }
4241
+ const qs = searchParams.toString();
4242
+ if (qs) {
4243
+ url += (url.includes("?") ? "&" : "?") + qs;
4244
+ }
4245
+ }
4246
+ let requestBody = void 0;
4247
+ if (endpoint.method !== "GET") {
4248
+ if (typeof body !== "undefined" && body !== null) {
4249
+ requestBody = JSON.stringify(body);
4250
+ }
4251
+ }
4252
+ return { url, body: requestBody };
4253
+ }
4254
+ /**
4255
+ * Returns a mocked response based on `endpoint.mockData`,
4256
+ * respecting the configured mock delay and response wrapper.
4257
+ */
4129
4258
  handleMockRequest(endpoint) {
4130
4259
  return __async(this, null, function* () {
4131
4260
  const delay = this.getRandomDelay();
@@ -4152,14 +4281,24 @@ var ApiClient = class {
4152
4281
  return this.responseTransform(endpoint.response.parse(mockData));
4153
4282
  });
4154
4283
  }
4284
+ /**
4285
+ * Returns a random delay in milliseconds within the current mock delay range.
4286
+ */
4155
4287
  getRandomDelay() {
4156
4288
  return Math.floor(
4157
4289
  Math.random() * (this.mockDelay.max - this.mockDelay.min + 1)
4158
4290
  ) + this.mockDelay.min;
4159
4291
  }
4292
+ /**
4293
+ * Creates a RichError instance from a partial error description.
4294
+ */
4160
4295
  createError(error) {
4161
4296
  return new RichError(error);
4162
4297
  }
4298
+ /**
4299
+ * Normalizes unknown errors into a RichError instance.
4300
+ * Zod validation errors are converted into a standardized validation error.
4301
+ */
4163
4302
  normalizeError(err) {
4164
4303
  if (err instanceof RichError) return err;
4165
4304
  if (err instanceof external_exports.ZodError) {