@tahanabavi/typefetch 1.1.1 → 1.2.1
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/README.md +149 -236
- package/dist/index.d.mts +107 -4
- package/dist/index.d.ts +107 -4
- package/dist/index.js +138 -3
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +138 -3
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
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
|
|
4130
|
+
url,
|
|
4081
4131
|
init: {
|
|
4082
4132
|
method: endpoint.method,
|
|
4083
4133
|
headers,
|
|
4084
|
-
body
|
|
4134
|
+
body
|
|
4085
4135
|
}
|
|
4086
4136
|
};
|
|
4087
4137
|
const runner = this.middlewares.reduceRight(
|
|
@@ -4126,6 +4176,81 @@ 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 missingTokens = Array.from(url.matchAll(/:([A-Za-z0-9_]+)/g)).map(
|
|
4212
|
+
(m) => m[1]
|
|
4213
|
+
);
|
|
4214
|
+
if (missingTokens.length > 0) {
|
|
4215
|
+
throw this.createError({
|
|
4216
|
+
message: `Missing path params for placeholders: ${missingTokens.join(
|
|
4217
|
+
", "
|
|
4218
|
+
)} in "${endpoint.path}"`,
|
|
4219
|
+
code: "MISSING_PATH_PARAMS"
|
|
4220
|
+
});
|
|
4221
|
+
}
|
|
4222
|
+
if (query) {
|
|
4223
|
+
const searchParams = new URLSearchParams();
|
|
4224
|
+
for (const [key, value] of Object.entries(query)) {
|
|
4225
|
+
if (value === void 0 || value === null) continue;
|
|
4226
|
+
if (Array.isArray(value)) {
|
|
4227
|
+
for (const v of value) {
|
|
4228
|
+
if (v === void 0 || v === null) continue;
|
|
4229
|
+
searchParams.append(key, String(v));
|
|
4230
|
+
}
|
|
4231
|
+
} else if (typeof value === "object") {
|
|
4232
|
+
searchParams.append(key, JSON.stringify(value));
|
|
4233
|
+
} else {
|
|
4234
|
+
searchParams.append(key, String(value));
|
|
4235
|
+
}
|
|
4236
|
+
}
|
|
4237
|
+
const qs = searchParams.toString();
|
|
4238
|
+
if (qs) {
|
|
4239
|
+
url += (url.includes("?") ? "&" : "?") + qs;
|
|
4240
|
+
}
|
|
4241
|
+
}
|
|
4242
|
+
let requestBody = void 0;
|
|
4243
|
+
if (endpoint.method !== "GET") {
|
|
4244
|
+
if (typeof body !== "undefined" && body !== null) {
|
|
4245
|
+
requestBody = JSON.stringify(body);
|
|
4246
|
+
}
|
|
4247
|
+
}
|
|
4248
|
+
return { url, body: requestBody };
|
|
4249
|
+
}
|
|
4250
|
+
/**
|
|
4251
|
+
* Returns a mocked response based on `endpoint.mockData`,
|
|
4252
|
+
* respecting the configured mock delay and response wrapper.
|
|
4253
|
+
*/
|
|
4129
4254
|
handleMockRequest(endpoint) {
|
|
4130
4255
|
return __async(this, null, function* () {
|
|
4131
4256
|
const delay = this.getRandomDelay();
|
|
@@ -4152,14 +4277,24 @@ var ApiClient = class {
|
|
|
4152
4277
|
return this.responseTransform(endpoint.response.parse(mockData));
|
|
4153
4278
|
});
|
|
4154
4279
|
}
|
|
4280
|
+
/**
|
|
4281
|
+
* Returns a random delay in milliseconds within the current mock delay range.
|
|
4282
|
+
*/
|
|
4155
4283
|
getRandomDelay() {
|
|
4156
4284
|
return Math.floor(
|
|
4157
4285
|
Math.random() * (this.mockDelay.max - this.mockDelay.min + 1)
|
|
4158
4286
|
) + this.mockDelay.min;
|
|
4159
4287
|
}
|
|
4288
|
+
/**
|
|
4289
|
+
* Creates a RichError instance from a partial error description.
|
|
4290
|
+
*/
|
|
4160
4291
|
createError(error) {
|
|
4161
4292
|
return new RichError(error);
|
|
4162
4293
|
}
|
|
4294
|
+
/**
|
|
4295
|
+
* Normalizes unknown errors into a RichError instance.
|
|
4296
|
+
* Zod validation errors are converted into a standardized validation error.
|
|
4297
|
+
*/
|
|
4163
4298
|
normalizeError(err) {
|
|
4164
4299
|
if (err instanceof RichError) return err;
|
|
4165
4300
|
if (err instanceof external_exports.ZodError) {
|