@lokalise/frontend-http-client
Advanced tools
Comparing version
import type { DeleteRouteDefinition, GetRouteDefinition, InferSchemaInput, InferSchemaOutput, PayloadRouteDefinition } from '@lokalise/universal-ts-utils/api-contracts/apiContracts'; | ||
import { z } from 'zod'; | ||
import { type ZodSchema, z } from 'zod'; | ||
import type { DeleteParams, FreeDeleteParams, FreeHeadersParams, GetParamsWrapper, HeadersParams, PayloadRequestParamsWrapper, PayloadRouteRequestParams, RequestResultType, RouteRequestParams, WretchInstance } from './types.js'; | ||
@@ -7,2 +7,9 @@ export declare const UNKNOWN_SCHEMA: z.ZodUnknown; | ||
export declare function sendPost<T extends WretchInstance, ResponseBody, RequestBodySchema extends z.Schema | undefined = undefined, RequestQuerySchema extends z.Schema | undefined = undefined, RequestHeadersSchema extends z.Schema | undefined = undefined, IsNonJSONResponseExpected extends boolean = false, IsEmptyResponseExpected extends boolean = false>(wretch: T, params: PayloadRequestParamsWrapper<RequestBodySchema, ResponseBody, IsNonJSONResponseExpected, IsEmptyResponseExpected, RequestQuerySchema, RequestHeadersSchema>): Promise<RequestResultType<ResponseBody, IsNonJSONResponseExpected, IsEmptyResponseExpected>>; | ||
export declare function sendPostWithProgress<ResponseBody>({ path, responseBodySchema, headers, data, onProgress, }: { | ||
path: string; | ||
headers?: Record<string, string>; | ||
data: XMLHttpRequestBodyInit; | ||
responseBodySchema: ZodSchema<ResponseBody>; | ||
onProgress: (progressEvent: ProgressEvent) => void; | ||
}): Promise<ResponseBody>; | ||
export declare function sendPut<T extends WretchInstance, ResponseBody, RequestBodySchema extends z.Schema | undefined = undefined, RequestQuerySchema extends z.Schema | undefined = undefined, RequestHeadersSchema extends z.Schema | undefined = undefined, IsNonJSONResponseExpected extends boolean = false, IsEmptyResponseExpected extends boolean = false>(wretch: T, params: PayloadRequestParamsWrapper<RequestBodySchema, ResponseBody, IsNonJSONResponseExpected, IsEmptyResponseExpected, RequestQuerySchema, RequestHeadersSchema>): Promise<RequestResultType<ResponseBody, IsNonJSONResponseExpected, IsEmptyResponseExpected>>; | ||
@@ -9,0 +16,0 @@ export declare function sendPatch<T extends WretchInstance, ResponseBody, RequestBodySchema extends z.Schema | undefined = undefined, RequestQuerySchema extends z.Schema | undefined = undefined, RequestHeadersSchema extends z.Schema | undefined = undefined, IsNonJSONResponseExpected extends boolean = false, IsEmptyResponseExpected extends boolean = false>(wretch: T, params: PayloadRequestParamsWrapper<RequestBodySchema, ResponseBody, IsNonJSONResponseExpected, IsEmptyResponseExpected, RequestQuerySchema, RequestHeadersSchema>): Promise<RequestResultType<ResponseBody, IsNonJSONResponseExpected, IsEmptyResponseExpected>>; |
import { z } from 'zod'; | ||
import { parseRequestBody, tryToResolveJsonBody } from './utils/bodyUtils.js'; | ||
import { parseRequestBody, parseResponseBody, tryToResolveJsonBody, } from './utils/bodyUtils.js'; | ||
import { isFailure } from './utils/either.js'; | ||
import { buildWretchError } from './utils/errorUtils.js'; | ||
import { XmlHttpRequestError, buildWretchError } from './utils/errorUtils.js'; | ||
import { parseQueryParams } from './utils/queryUtils.js'; | ||
@@ -79,2 +79,32 @@ export const UNKNOWN_SCHEMA = z.unknown(); | ||
} | ||
export async function sendPostWithProgress({ path, responseBodySchema, headers = {}, data, onProgress, }) { | ||
const response = await new Promise((resolve, reject) => { | ||
/** | ||
* Usually we recommend Wretch for Network requests. | ||
* However, sometimes ( especially during files upload ) we require access to `progress` events | ||
* emitted by the request. Wretch does not expose this event to consumers, so we use XHR here instead. | ||
*/ | ||
const xhr = new XMLHttpRequest(); | ||
xhr.upload.onprogress = (progress) => onProgress(progress); | ||
xhr.responseType = 'json'; | ||
xhr.open('POST', path, true); | ||
for (const [headerName, headerValue] of Object.entries(headers)) { | ||
xhr.setRequestHeader(headerName, headerValue); | ||
} | ||
xhr.onload = () => { | ||
if (xhr.status >= 200 && xhr.status < 300) | ||
resolve(xhr.response); | ||
else | ||
reject(new XmlHttpRequestError('File upload failed', xhr.response)); | ||
}; | ||
xhr.onerror = () => { | ||
reject(new XmlHttpRequestError(`File upload failed: ${xhr.statusText}`)); | ||
}; | ||
xhr.send(data); | ||
}); | ||
const bodyParseResult = parseResponseBody({ response, responseBodySchema, path }); | ||
if (bodyParseResult.error) | ||
return Promise.reject(bodyParseResult.error); | ||
return bodyParseResult.result; | ||
} | ||
/* PUT */ | ||
@@ -81,0 +111,0 @@ export function sendPut(wretch, params) { |
@@ -1,1 +0,1 @@ | ||
export { sendPost, sendGet, sendPut, sendDelete, sendPatch, sendByGetRoute, sendByPayloadRoute, sendByDeleteRoute, UNKNOWN_SCHEMA, } from './client.js'; | ||
export { sendPost, sendPostWithProgress, sendGet, sendPut, sendDelete, sendPatch, sendByGetRoute, sendByPayloadRoute, sendByDeleteRoute, UNKNOWN_SCHEMA, } from './client.js'; |
@@ -1,2 +0,2 @@ | ||
export { sendPost, sendGet, sendPut, sendDelete, sendPatch, sendByGetRoute, sendByPayloadRoute, sendByDeleteRoute, UNKNOWN_SCHEMA, } from './client.js'; | ||
export { sendPost, sendPostWithProgress, sendGet, sendPut, sendDelete, sendPatch, sendByGetRoute, sendByPayloadRoute, sendByDeleteRoute, UNKNOWN_SCHEMA, } from './client.js'; | ||
//# sourceMappingURL=index.js.map |
@@ -6,2 +6,7 @@ import type { WretchResponse } from 'wretch'; | ||
export declare function tryToResolveJsonBody<RequestBodySchema extends z.ZodSchema>(response: WretchResponse, path: string, schema: RequestBodySchema, isEmptyResponseExpected?: boolean): Promise<BodyParseResult<RequestBodySchema>>; | ||
export declare function parseResponseBody<ResponseBody>({ response, responseBodySchema, path, }: { | ||
response: unknown; | ||
responseBodySchema: z.ZodSchema<ResponseBody>; | ||
path: string; | ||
}): Either<z.ZodError, ResponseBody>; | ||
export declare function parseRequestBody<RequestBodySchema extends z.Schema>({ body, requestBodySchema, path, }: { | ||
@@ -8,0 +13,0 @@ body: unknown; |
@@ -27,3 +27,3 @@ import { failure, success } from './either.js'; | ||
} | ||
function parseResponseBody({ response, responseBodySchema, path, }) { | ||
export function parseResponseBody({ response, responseBodySchema, path, }) { | ||
const result = responseBodySchema.safeParse(response); | ||
@@ -38,3 +38,3 @@ if (!result.success) { | ||
} | ||
return success(response); | ||
return success(result.data); | ||
} | ||
@@ -41,0 +41,0 @@ export function parseRequestBody({ body, requestBodySchema, path, }) { |
import type { WretchResponse } from 'wretch'; | ||
import { WretchError } from 'wretch/resolver'; | ||
export declare function buildWretchError(message: string, response: WretchResponse): WretchError; | ||
export declare class XmlHttpRequestError extends Error { | ||
readonly details?: Record<string, unknown>; | ||
constructor(message: string, details?: Record<string, unknown>); | ||
} |
@@ -9,2 +9,9 @@ import { WretchError } from 'wretch/resolver'; | ||
} | ||
export class XmlHttpRequestError extends Error { | ||
details; | ||
constructor(message, details) { | ||
super(message); | ||
this.details = details; | ||
} | ||
} | ||
//# sourceMappingURL=errorUtils.js.map |
{ | ||
"name": "@lokalise/frontend-http-client", | ||
"version": "4.0.0", | ||
"version": "4.1.0", | ||
"description": "Opinionated HTTP client for the frontend", | ||
@@ -50,2 +50,3 @@ "files": ["dist/**", "LICENSE", "README.md"], | ||
"jest-fail-on-console": "^3.1.2", | ||
"mock-xmlhttprequest": "^8.4.1", | ||
"mockttp": "^3.13.0", | ||
@@ -52,0 +53,0 @@ "rimraf": "^6.0.0", |
@@ -98,2 +98,21 @@ # Frontend HTTP client | ||
### Tracking request progress | ||
Tracking requests progress is especially useful while uploading files. | ||
> **Important note**: `wretch` does not support request progress tracking, so we rely on XMLHttpRequest. That's why the interface of the method below is slightly different from the others | ||
Usage example: | ||
```ts | ||
const response = await sendPostWithProgress({ | ||
path: '/', | ||
data: new FormData(), | ||
headers: { Authorization: 'Bearer ...' }, | ||
responseBodySchema: z.object(), | ||
onProgress: (progress) => { | ||
console.log(`Loaded ${progress.loaded} of ${progress.total}`) | ||
} | ||
}) | ||
``` | ||
## Credits | ||
@@ -100,0 +119,0 @@ |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
55336
8.77%466
12.83%127
17.59%0
-100%12
9.09%