Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

openapi-fetch

Package Overview
Dependencies
Maintainers
1
Versions
69
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

openapi-fetch - npm Package Compare versions

Comparing version 0.9.0-rc.0 to 0.9.0

29

CHANGELOG.md
# openapi-fetch
## 0.9.0
### Minor Changes
- [#1521](https://github.com/drwpow/openapi-typescript/pull/1521) [`b174dd6`](https://github.com/drwpow/openapi-typescript/commit/b174dd6a7668e2f1f6bf6bd086ba2dabf7fb669e) Thanks [@drwpow](https://github.com/drwpow)! - Add middleware support
- [#1521](https://github.com/drwpow/openapi-typescript/pull/1521) [`fc3a468`](https://github.com/drwpow/openapi-typescript/commit/fc3a468c4342e17d203712be358b30a3fb82ab1e) Thanks [@drwpow](https://github.com/drwpow)! - ⚠️ Breaking change (internal): fetch() is now called with new Request() to support middleware (which may affect test mocking)
- [#1521](https://github.com/drwpow/openapi-typescript/pull/1521) [`2551e4b`](https://github.com/drwpow/openapi-typescript/commit/2551e4bde41d5437a76c13bb5ba25ede4f14db10) Thanks [@drwpow](https://github.com/drwpow)! - ⚠️ **Breaking change**: Responses are no longer automatically `.clone()`’d in certain instances. Be sure to `.clone()` yourself if you need to access the raw body!
- [#1534](https://github.com/drwpow/openapi-typescript/pull/1534) [`2bbeb92`](https://github.com/drwpow/openapi-typescript/commit/2bbeb92244cb82a534abb016ffb5fbd1255d9db5) Thanks [@drwpow](https://github.com/drwpow)! - ⚠️ Breaking change: no longer supports deeply-nested objects/arrays for query & path serialization.
### Patch Changes
- [#1484](https://github.com/drwpow/openapi-typescript/pull/1484) [`49bbd72`](https://github.com/drwpow/openapi-typescript/commit/49bbd72800f7bc6c460a741c50d11eb216746290) Thanks [@drwpow](https://github.com/drwpow)! - Remove prepare script
- [#1479](https://github.com/drwpow/openapi-typescript/pull/1479) [`c6d945b`](https://github.com/drwpow/openapi-typescript/commit/c6d945be717bb3999178fb3a77292e41e1b7ab80) Thanks [@darwish](https://github.com/darwish)! - Fixed build of openapi-typescript-helpers for CommonJS environments
- [#1534](https://github.com/drwpow/openapi-typescript/pull/1534) [`2bbeb92`](https://github.com/drwpow/openapi-typescript/commit/2bbeb92244cb82a534abb016ffb5fbd1255d9db5) Thanks [@drwpow](https://github.com/drwpow)! - Add support for automatic label & matrix path serialization.
- [#1521](https://github.com/drwpow/openapi-typescript/pull/1521) [`fd44bd2`](https://github.com/drwpow/openapi-typescript/commit/fd44bd28d881715e30f5a71435f05f6bae13859d) Thanks [@drwpow](https://github.com/drwpow)! - Support arrays in headers
- [#1534](https://github.com/drwpow/openapi-typescript/pull/1534) [`2bbeb92`](https://github.com/drwpow/openapi-typescript/commit/2bbeb92244cb82a534abb016ffb5fbd1255d9db5) Thanks [@drwpow](https://github.com/drwpow)! - Remove leading question marks from querySerializer
- [#1530](https://github.com/drwpow/openapi-typescript/pull/1530) [`4765658`](https://github.com/drwpow/openapi-typescript/commit/4765658460e0850d005e3f08cd63c4949326349b) Thanks [@wydengyre](https://github.com/wydengyre)! - Exports the ClientMethod utility type.
- Updated dependencies [[`c6d945b`](https://github.com/drwpow/openapi-typescript/commit/c6d945be717bb3999178fb3a77292e41e1b7ab80)]:
- openapi-typescript-helpers@0.0.7
## 0.8.2

@@ -4,0 +33,0 @@

155

dist/index.d.ts

@@ -23,3 +23,3 @@ import type {

/** global querySerializer */
querySerializer?: QuerySerializer<unknown>;
querySerializer?: QuerySerializer<unknown> | QuerySerializerOptions;
/** global bodySerializer */

@@ -48,2 +48,28 @@ bodySerializer?: BodySerializer<unknown>;

/** @see https://swagger.io/docs/specification/serialization/#query */
export type QuerySerializerOptions = {
/** Set serialization for arrays. @see https://swagger.io/docs/specification/serialization/#query */
array?: {
/** default: "form" */
style: "form" | "spaceDelimited" | "pipeDelimited";
/** default: true */
explode: boolean;
};
/** Set serialization for objects. @see https://swagger.io/docs/specification/serialization/#query */
object?: {
/** default: "deepObject" */
style: "form" | "deepObject";
/** default: true */
explode: boolean;
};
/**
* The `allowReserved` keyword specifies whether the reserved characters
* `:/?#[]@!$&'()*+,;=` in parameter values are allowed to be sent as they
* are, or should be percent-encoded. By default, allowReserved is `false`,
* and reserved characters are percent-encoded.
* @see https://swagger.io/docs/specification/serialization/#query
*/
allowReserved?: boolean;
};
export type BodySerializer<T> = (body: OperationRequestBodyContent<T>) => any;

@@ -112,3 +138,3 @@

RequestBodyOption<T> & {
querySerializer?: QuerySerializer<T>;
querySerializer?: QuerySerializer<T> | QuerySerializerOptions;
bodySerializer?: BodySerializer<T>;

@@ -154,2 +180,10 @@ parseAs?: ParseAs;

export type ClientMethod<Paths extends {}, M> = <
P extends PathsWithMethod<Paths, M>,
I extends MaybeOptionalInit<Paths[P], M>,
>(
url: P,
...init: I
) => Promise<FetchResponse<Paths[P][M], I[0]>>;
export default function createClient<Paths extends {}>(

@@ -159,65 +193,17 @@ clientOptions?: ClientOptions,

/** Call a GET endpoint */
GET<
P extends PathsWithMethod<Paths, "get">,
I extends MaybeOptionalInit<Paths[P], "get">,
>(
url: P,
...init: I
): Promise<FetchResponse<Paths[P]["get"], I[0]>>;
GET: ClientMethod<Paths, "get">;
/** Call a PUT endpoint */
PUT<
P extends PathsWithMethod<Paths, "put">,
I extends MaybeOptionalInit<Paths[P], "put">,
>(
url: P,
...init: I
): Promise<FetchResponse<Paths[P]["put"], I[0]>>;
PUT: ClientMethod<Paths, "put">;
/** Call a POST endpoint */
POST<
P extends PathsWithMethod<Paths, "post">,
I extends MaybeOptionalInit<Paths[P], "post">,
>(
url: P,
...init: I
): Promise<FetchResponse<Paths[P]["post"], I[0]>>;
POST: ClientMethod<Paths, "post">;
/** Call a DELETE endpoint */
DELETE<
P extends PathsWithMethod<Paths, "delete">,
I extends MaybeOptionalInit<Paths[P], "delete">,
>(
url: P,
...init: I
): Promise<FetchResponse<Paths[P]["delete"], I[0]>>;
DELETE: ClientMethod<Paths, "delete">;
/** Call a OPTIONS endpoint */
OPTIONS<
P extends PathsWithMethod<Paths, "options">,
I extends MaybeOptionalInit<Paths[P], "options">,
>(
url: P,
...init: I
): Promise<FetchResponse<Paths[P]["options"], I[0]>>;
OPTIONS: ClientMethod<Paths, "options">;
/** Call a HEAD endpoint */
HEAD<
P extends PathsWithMethod<Paths, "head">,
I extends MaybeOptionalInit<Paths[P], "head">,
>(
url: P,
...init: I
): Promise<FetchResponse<Paths[P]["head"], I[0]>>;
HEAD: ClientMethod<Paths, "head">;
/** Call a PATCH endpoint */
PATCH<
P extends PathsWithMethod<Paths, "patch">,
I extends MaybeOptionalInit<Paths[P], "patch">,
>(
url: P,
...init: I
): Promise<FetchResponse<Paths[P]["patch"], I[0]>>;
PATCH: ClientMethod<Paths, "patch">;
/** Call a TRACE endpoint */
TRACE<
P extends PathsWithMethod<Paths, "trace">,
I extends MaybeOptionalInit<Paths[P], "trace">,
>(
url: P,
...init: I
): Promise<FetchResponse<Paths[P]["trace"], I[0]>>;
TRACE: ClientMethod<Paths, "trace">;
/** Register middleware */

@@ -229,10 +215,51 @@ use(...middleware: Middleware[]): void;

/** Serialize primitive params to string */
export declare function serializePrimitiveParam(
name: string,
value: string,
options?: { allowReserved?: boolean },
): string;
/** Serialize object param to string */
export declare function serializeObjectParam(
name: string,
value: Record<string, unknown>,
options: {
style: "simple" | "label" | "matrix" | "form" | "deepObject";
explode: boolean;
allowReserved?: boolean;
},
): string;
/** Serialize array param to string */
export declare function serializeArrayParam(
name: string,
value: unknown[],
options: {
style:
| "simple"
| "label"
| "matrix"
| "form"
| "spaceDelimited"
| "pipeDelimited";
explode: boolean;
allowReserved?: boolean;
},
): string;
/** Serialize query params to string */
export declare function defaultQuerySerializer<T = unknown>(q: T): string;
export declare function createQuerySerializer<T = unknown>(
options?: QuerySerializerOptions,
): (queryParams: T) => string;
/** Serialize query param schema types according to expected default OpenAPI 3.x behavior */
export declare function defaultQueryParamSerializer<T = unknown>(
key: string[],
value: T,
): string | undefined;
/**
* Handle different OpenAPI 3.x serialization styles
* @type {import("./index.js").defaultPathSerializer}
* @see https://swagger.io/docs/specification/serialization/#path
*/
export declare function defaultPathSerializer(
pathname: string,
pathParams: Record<string, unknown>,
): string;

@@ -239,0 +266,0 @@ /** Serialize body object to string */

@@ -6,2 +6,4 @@ // settings & const

const PATH_PARAM_RE = /\{[^{}]+\}/g;
/**

@@ -15,4 +17,4 @@ * Create an openapi-fetch client.

fetch: baseFetch = globalThis.fetch,
querySerializer: globalQuerySerializer = defaultQuerySerializer,
bodySerializer: globalBodySerializer = defaultBodySerializer,
querySerializer: globalQuerySerializer,
bodySerializer: globalBodySerializer,
headers: baseHeaders,

@@ -22,3 +24,3 @@ ...baseOptions

if (baseUrl.endsWith("/")) {
baseUrl = baseUrl.slice(0, -1); // remove trailing slash
baseUrl = baseUrl.substring(0, baseUrl.length - 1);
}

@@ -39,7 +41,23 @@ baseHeaders = mergeHeaders(DEFAULT_HEADERS, baseHeaders);

parseAs = "json",
querySerializer = globalQuerySerializer,
bodySerializer = globalBodySerializer,
querySerializer: requestQuerySerializer,
bodySerializer = globalBodySerializer ?? defaultBodySerializer,
...init
} = fetchOptions || {};
let querySerializer =
typeof globalQuerySerializer === "function"
? globalQuerySerializer
: createQuerySerializer(globalQuerySerializer);
if (requestQuerySerializer) {
querySerializer =
typeof requestQuerySerializer === "function"
? requestQuerySerializer
: createQuerySerializer({
...(typeof globalQuerySerializer === "object"
? globalQuerySerializer
: {}),
...requestQuerySerializer,
});
}
const requestInit = {

@@ -55,7 +73,3 @@ redirect: "follow",

let request = new Request(
createFinalURL(url, {
baseUrl,
params,
querySerializer,
}),
createFinalURL(url, { baseUrl, params, querySerializer }),
requestInit,

@@ -201,73 +215,220 @@ );

/**
* Serialize query params to string
* @type {import("./index.js").defaultQuerySerializer}
* Serialize primitive param values
* @type {import("./index.js").serializePrimitiveParam}
*/
export function defaultQuerySerializer(q) {
const search = [];
if (q && typeof q === "object") {
for (const [k, v] of Object.entries(q)) {
const value = defaultQueryParamSerializer([k], v);
if (value) {
search.push(value);
export function serializePrimitiveParam(name, value, options) {
if (value === undefined || value === null) {
return "";
}
if (typeof value === "object") {
throw new Error(
`Deeply-nested arrays/objects aren’t supported. Provide your own \`querySerializer()\` to handle these.`,
);
}
return `${name}=${options?.allowReserved === true ? value : encodeURIComponent(value)}`;
}
/**
* Serialize object param (shallow only)
* @type {import("./index.js").serializeObjectParam}
*/
export function serializeObjectParam(name, value, options) {
if (!value || typeof value !== "object") {
return "";
}
const values = [];
const joiner =
{
simple: ",",
label: ".",
matrix: ";",
}[options.style] || "&";
// explode: false
if (options.style !== "deepObject" && options.explode === false) {
for (const k in value) {
values.push(
k,
options.allowReserved === true
? value[k]
: encodeURIComponent(value[k]),
);
}
const final = values.join(","); // note: values are always joined by comma in explode: false (but joiner can prefix)
switch (options.style) {
case "form": {
return `${name}=${final}`;
}
case "label": {
return `.${final}`;
}
case "matrix": {
return `;${name}=${final}`;
}
default: {
return final;
}
}
}
return search.join("&");
// explode: true
for (const k in value) {
const finalName = options.style === "deepObject" ? `${name}[${k}]` : k;
values.push(serializePrimitiveParam(finalName, value[k], options));
}
const final = values.join(joiner);
return options.style === "label" || options.style === "matrix"
? `${joiner}${final}`
: final;
}
/**
* Serialize query param schema types according to expected default OpenAPI 3.x behavior
* @type {import("./index.js").defaultQueryParamSerializer}
* Serialize array param (shallow only)
* @type {import("./index.js").serializeArrayParam}
*/
export function defaultQueryParamSerializer(key, value) {
if (value === null || value === undefined) {
return undefined;
export function serializeArrayParam(name, value, options) {
if (!Array.isArray(value)) {
return "";
}
if (typeof value === "string") {
return `${deepObjectPath(key)}=${encodeURIComponent(value)}`;
}
if (typeof value === "number" || typeof value === "boolean") {
return `${deepObjectPath(key)}=${String(value)}`;
}
if (Array.isArray(value)) {
if (!value.length) {
return undefined;
}
const nextValue = [];
for (const item of value) {
const next = defaultQueryParamSerializer(key, item);
if (next !== undefined) {
nextValue.push(next);
// explode: false
if (options.explode === false) {
const joiner =
{ form: ",", spaceDelimited: "%20", pipeDelimited: "|" }[options.style] ||
","; // note: for arrays, joiners vary wildly based on style + explode behavior
const final = (
options.allowReserved === true
? value
: value.map((v) => encodeURIComponent(v))
).join(joiner);
switch (options.style) {
case "simple": {
return final;
}
case "label": {
return `.${final}`;
}
case "matrix": {
return `;${name}=${final}`;
}
case "spaceDelimited":
case "pipeDelimited":
default: {
return `${name}=${final}`;
}
}
return nextValue.join(`&`);
}
if (typeof value === "object") {
if (!Object.keys(value).length) {
return undefined;
// explode: true
const joiner = { simple: ",", label: ".", matrix: ";" }[options.style] || "&";
const values = [];
for (const v of value) {
if (options.style === "simple" || options.style === "label") {
values.push(options.allowReserved === true ? v : encodeURIComponent(v));
} else {
values.push(serializePrimitiveParam(name, v, options));
}
const nextValue = [];
for (const [k, v] of Object.entries(value)) {
if (v !== undefined && v !== null) {
const next = defaultQueryParamSerializer([...key, k], v);
if (next !== undefined) {
nextValue.push(next);
}
return options.style === "label" || options.style === "matrix"
? `${joiner}${values.join(joiner)}`
: values.join(joiner);
}
/**
* Serialize query params to string
* @type {import("./index.js").createQuerySerializer}
*/
export function createQuerySerializer(options) {
return function querySerializer(queryParams) {
const search = [];
if (queryParams && typeof queryParams === "object") {
for (const name in queryParams) {
const value = queryParams[name];
if (value === undefined || value === null) {
continue;
}
if (Array.isArray(value)) {
search.push(
serializeArrayParam(name, value, {
style: "form",
explode: true,
...options?.array,
allowReserved: options?.allowReserved || false,
}),
);
continue;
}
if (typeof value === "object") {
search.push(
serializeObjectParam(name, value, {
style: "deepObject",
explode: true,
...options?.object,
allowReserved: options?.allowReserved || false,
}),
);
continue;
}
search.push(serializePrimitiveParam(name, value, options));
}
}
return nextValue.join("&");
}
return encodeURIComponent(`${deepObjectPath(key)}=${String(value)}`);
return search.join("&");
};
}
/**
* Flatten a node path into a deepObject string
* @type {import("./index.js").deepObjectPath}
* Handle different OpenAPI 3.x serialization styles
* @type {import("./index.js").defaultPathSerializer}
* @see https://swagger.io/docs/specification/serialization/#path
*/
function deepObjectPath(path) {
let output = path[0];
for (const k of path.slice(1)) {
output += `[${k}]`;
export function defaultPathSerializer(pathname, pathParams) {
let nextURL = pathname;
for (const match of pathname.match(PATH_PARAM_RE) ?? []) {
let name = match.substring(1, match.length - 1);
let explode = false;
let style = "simple";
if (name.endsWith("*")) {
explode = true;
name = name.substring(0, name.length - 1);
}
if (name.startsWith(".")) {
style = "label";
name = name.substring(1);
} else if (name.startsWith(";")) {
style = "matrix";
name = name.substring(1);
}
if (
!pathParams ||
pathParams[name] === undefined ||
pathParams[name] === null
) {
continue;
}
const value = pathParams[name];
if (Array.isArray(value)) {
nextURL = nextURL.replace(
match,
serializeArrayParam(name, value, { style, explode }),
);
continue;
}
if (typeof value === "object") {
nextURL = nextURL.replace(
match,
serializeObjectParam(name, value, { style, explode }),
);
continue;
}
if (style === "matrix") {
nextURL = nextURL.replace(
match,
`;${serializePrimitiveParam(name, value)}`,
);
continue;
}
nextURL = nextURL.replace(match, style === "label" ? `.${value}` : value);
continue;
}
return output;
return nextURL;
}

@@ -289,8 +450,9 @@

let finalURL = `${options.baseUrl}${pathname}`;
if (options.params.path) {
for (const [k, v] of Object.entries(options.params.path)) {
finalURL = finalURL.replace(`{${k}}`, encodeURIComponent(String(v)));
}
if (options.params?.path) {
finalURL = defaultPathSerializer(finalURL, options.params.path);
}
const search = options.querySerializer(options.params.query ?? {});
let search = options.querySerializer(options.params.query ?? {});
if (search.startsWith("?")) {
search = search.substring(1);
}
if (search) {

@@ -297,0 +459,0 @@ finalURL += `?${search}`;

@@ -23,3 +23,3 @@ import type {

/** global querySerializer */
querySerializer?: QuerySerializer<unknown>;
querySerializer?: QuerySerializer<unknown> | QuerySerializerOptions;
/** global bodySerializer */

@@ -48,2 +48,28 @@ bodySerializer?: BodySerializer<unknown>;

/** @see https://swagger.io/docs/specification/serialization/#query */
export type QuerySerializerOptions = {
/** Set serialization for arrays. @see https://swagger.io/docs/specification/serialization/#query */
array?: {
/** default: "form" */
style: "form" | "spaceDelimited" | "pipeDelimited";
/** default: true */
explode: boolean;
};
/** Set serialization for objects. @see https://swagger.io/docs/specification/serialization/#query */
object?: {
/** default: "deepObject" */
style: "form" | "deepObject";
/** default: true */
explode: boolean;
};
/**
* The `allowReserved` keyword specifies whether the reserved characters
* `:/?#[]@!$&'()*+,;=` in parameter values are allowed to be sent as they
* are, or should be percent-encoded. By default, allowReserved is `false`,
* and reserved characters are percent-encoded.
* @see https://swagger.io/docs/specification/serialization/#query
*/
allowReserved?: boolean;
};
export type BodySerializer<T> = (body: OperationRequestBodyContent<T>) => any;

@@ -112,3 +138,3 @@

RequestBodyOption<T> & {
querySerializer?: QuerySerializer<T>;
querySerializer?: QuerySerializer<T> | QuerySerializerOptions;
bodySerializer?: BodySerializer<T>;

@@ -154,2 +180,10 @@ parseAs?: ParseAs;

export type ClientMethod<Paths extends {}, M> = <
P extends PathsWithMethod<Paths, M>,
I extends MaybeOptionalInit<Paths[P], M>,
>(
url: P,
...init: I
) => Promise<FetchResponse<Paths[P][M], I[0]>>;
export default function createClient<Paths extends {}>(

@@ -159,65 +193,17 @@ clientOptions?: ClientOptions,

/** Call a GET endpoint */
GET<
P extends PathsWithMethod<Paths, "get">,
I extends MaybeOptionalInit<Paths[P], "get">,
>(
url: P,
...init: I
): Promise<FetchResponse<Paths[P]["get"], I[0]>>;
GET: ClientMethod<Paths, "get">;
/** Call a PUT endpoint */
PUT<
P extends PathsWithMethod<Paths, "put">,
I extends MaybeOptionalInit<Paths[P], "put">,
>(
url: P,
...init: I
): Promise<FetchResponse<Paths[P]["put"], I[0]>>;
PUT: ClientMethod<Paths, "put">;
/** Call a POST endpoint */
POST<
P extends PathsWithMethod<Paths, "post">,
I extends MaybeOptionalInit<Paths[P], "post">,
>(
url: P,
...init: I
): Promise<FetchResponse<Paths[P]["post"], I[0]>>;
POST: ClientMethod<Paths, "post">;
/** Call a DELETE endpoint */
DELETE<
P extends PathsWithMethod<Paths, "delete">,
I extends MaybeOptionalInit<Paths[P], "delete">,
>(
url: P,
...init: I
): Promise<FetchResponse<Paths[P]["delete"], I[0]>>;
DELETE: ClientMethod<Paths, "delete">;
/** Call a OPTIONS endpoint */
OPTIONS<
P extends PathsWithMethod<Paths, "options">,
I extends MaybeOptionalInit<Paths[P], "options">,
>(
url: P,
...init: I
): Promise<FetchResponse<Paths[P]["options"], I[0]>>;
OPTIONS: ClientMethod<Paths, "options">;
/** Call a HEAD endpoint */
HEAD<
P extends PathsWithMethod<Paths, "head">,
I extends MaybeOptionalInit<Paths[P], "head">,
>(
url: P,
...init: I
): Promise<FetchResponse<Paths[P]["head"], I[0]>>;
HEAD: ClientMethod<Paths, "head">;
/** Call a PATCH endpoint */
PATCH<
P extends PathsWithMethod<Paths, "patch">,
I extends MaybeOptionalInit<Paths[P], "patch">,
>(
url: P,
...init: I
): Promise<FetchResponse<Paths[P]["patch"], I[0]>>;
PATCH: ClientMethod<Paths, "patch">;
/** Call a TRACE endpoint */
TRACE<
P extends PathsWithMethod<Paths, "trace">,
I extends MaybeOptionalInit<Paths[P], "trace">,
>(
url: P,
...init: I
): Promise<FetchResponse<Paths[P]["trace"], I[0]>>;
TRACE: ClientMethod<Paths, "trace">;
/** Register middleware */

@@ -229,10 +215,51 @@ use(...middleware: Middleware[]): void;

/** Serialize primitive params to string */
export declare function serializePrimitiveParam(
name: string,
value: string,
options?: { allowReserved?: boolean },
): string;
/** Serialize object param to string */
export declare function serializeObjectParam(
name: string,
value: Record<string, unknown>,
options: {
style: "simple" | "label" | "matrix" | "form" | "deepObject";
explode: boolean;
allowReserved?: boolean;
},
): string;
/** Serialize array param to string */
export declare function serializeArrayParam(
name: string,
value: unknown[],
options: {
style:
| "simple"
| "label"
| "matrix"
| "form"
| "spaceDelimited"
| "pipeDelimited";
explode: boolean;
allowReserved?: boolean;
},
): string;
/** Serialize query params to string */
export declare function defaultQuerySerializer<T = unknown>(q: T): string;
export declare function createQuerySerializer<T = unknown>(
options?: QuerySerializerOptions,
): (queryParams: T) => string;
/** Serialize query param schema types according to expected default OpenAPI 3.x behavior */
export declare function defaultQueryParamSerializer<T = unknown>(
key: string[],
value: T,
): string | undefined;
/**
* Handle different OpenAPI 3.x serialization styles
* @type {import("./index.js").defaultPathSerializer}
* @see https://swagger.io/docs/specification/serialization/#path
*/
export declare function defaultPathSerializer(
pathname: string,
pathParams: Record<string, unknown>,
): string;

@@ -239,0 +266,0 @@ /** Serialize body object to string */

@@ -1,1 +0,1 @@

var C={"Content-Type":"application/json"};function P(o){let{baseUrl:e="",fetch:n=globalThis.fetch,querySerializer:a=$,bodySerializer:i=z,headers:f,...m}={...o};e.endsWith("/")&&(e=e.slice(0,-1)),f=A(C,f);let l=[];async function c(r,t){let{fetch:h=n,headers:q,params:b={},parseAs:w="json",querySerializer:T=a,bodySerializer:g=i,...x}=t||{},y={redirect:"follow",...m,...x,headers:A(f,q,b.header)};y.body&&(y.body=g(y.body));let p=new Request(H(r,{baseUrl:e,params:b,querySerializer:T}),y);y.body instanceof FormData&&p.headers.delete("Content-Type");let O={baseUrl:e,fetch:h,parseAs:w,querySerializer:T,bodySerializer:g};for(let u of l)if(u&&typeof u=="object"&&typeof u.onRequest=="function"){p.schemaPath=r,p.params=b;let d=await u.onRequest(p,O);if(d){if(!(d instanceof Request))throw new Error("Middleware must return new Request() when modifying the request");p=d}}let s=await h(p);for(let u=l.length-1;u>=0;u--){let d=l[u];if(d&&typeof d=="object"&&typeof d.onResponse=="function"){let j=await d.onResponse(s,O);if(j){if(!(j instanceof Response))throw new Error("Middleware must return new Response() when modifying the response");s=j}}}if(s.status===204||s.headers.get("Content-Length")==="0")return s.ok?{data:{},response:s}:{error:{},response:s};if(s.ok)return w==="stream"?{data:s.body,response:s}:{data:await s[w](),response:s};let R={};try{R=await s.json()}catch{R=await s.text()}return{error:R,response:s}}return{async GET(r,t){return c(r,{...t,method:"GET"})},async PUT(r,t){return c(r,{...t,method:"PUT"})},async POST(r,t){return c(r,{...t,method:"POST"})},async DELETE(r,t){return c(r,{...t,method:"DELETE"})},async OPTIONS(r,t){return c(r,{...t,method:"OPTIONS"})},async HEAD(r,t){return c(r,{...t,method:"HEAD"})},async PATCH(r,t){return c(r,{...t,method:"PATCH"})},async TRACE(r,t){return c(r,{...t,method:"TRACE"})},use(...r){for(let t of r)if(t){if(typeof t!="object"||!("onRequest"in t||"onResponse"in t))throw new Error("Middleware must be an object with one of `onRequest()` or `onResponse()`");l.push(t)}},eject(...r){for(let t of r){let h=l.indexOf(t);h!==-1&&l.splice(h,1)}}}}function $(o){let e=[];if(o&&typeof o=="object")for(let[n,a]of Object.entries(o)){let i=E([n],a);i&&e.push(i)}return e.join("&")}function E(o,e){if(e!=null){if(typeof e=="string")return`${S(o)}=${encodeURIComponent(e)}`;if(typeof e=="number"||typeof e=="boolean")return`${S(o)}=${String(e)}`;if(Array.isArray(e)){if(!e.length)return;let n=[];for(let a of e){let i=E(o,a);i!==void 0&&n.push(i)}return n.join("&")}if(typeof e=="object"){if(!Object.keys(e).length)return;let n=[];for(let[a,i]of Object.entries(e))if(i!=null){let f=E([...o,a],i);f!==void 0&&n.push(f)}return n.join("&")}return encodeURIComponent(`${S(o)}=${String(e)}`)}}function S(o){let e=o[0];for(let n of o.slice(1))e+=`[${n}]`;return e}function z(o){return JSON.stringify(o)}function H(o,e){let n=`${e.baseUrl}${o}`;if(e.params.path)for(let[i,f]of Object.entries(e.params.path))n=n.replace(`{${i}}`,encodeURIComponent(String(f)));let a=e.querySerializer(e.params.query??{});return a&&(n+=`?${a}`),n}function A(...o){let e=new Headers;for(let n of o){if(!n||typeof n!="object")continue;let a=n instanceof Headers?n.entries():Object.entries(n);for(let[i,f]of a)if(f===null)e.delete(i);else if(Array.isArray(f))for(let m of f)e.append(i,m);else f!==void 0&&e.set(i,f)}return e}export{H as createFinalURL,P as default,z as defaultBodySerializer,E as defaultQueryParamSerializer,$ as defaultQuerySerializer,A as mergeHeaders};
var D={"Content-Type":"application/json"},U=/\{[^{}]+\}/g;function H(s){let{baseUrl:t="",fetch:e=globalThis.fetch,querySerializer:n,bodySerializer:r,headers:i,...a}={...s};t.endsWith("/")&&(t=t.substring(0,t.length-1)),i=T(D,i);let l=[];async function u(c,o){let{fetch:p=e,headers:O,params:R={},parseAs:j="json",querySerializer:b,bodySerializer:A=r??I,...C}=o||{},x=typeof n=="function"?n:S(n);b&&(x=typeof b=="function"?b:S({...typeof n=="object"?n:{},...b}));let m={redirect:"follow",...a,...C,headers:T(i,O,R.header)};m.body&&(m.body=A(m.body));let h=new Request(L(c,{baseUrl:t,params:R,querySerializer:x}),m);m.body instanceof FormData&&h.headers.delete("Content-Type");let E={baseUrl:t,fetch:p,parseAs:j,querySerializer:x,bodySerializer:A};for(let d of l)if(d&&typeof d=="object"&&typeof d.onRequest=="function"){h.schemaPath=c,h.params=R;let y=await d.onRequest(h,E);if(y){if(!(y instanceof Request))throw new Error("Middleware must return new Request() when modifying the request");h=y}}let f=await p(h);for(let d=l.length-1;d>=0;d--){let y=l[d];if(y&&typeof y=="object"&&typeof y.onResponse=="function"){let g=await y.onResponse(f,E);if(g){if(!(g instanceof Response))throw new Error("Middleware must return new Response() when modifying the response");f=g}}}if(f.status===204||f.headers.get("Content-Length")==="0")return f.ok?{data:{},response:f}:{error:{},response:f};if(f.ok)return j==="stream"?{data:f.body,response:f}:{data:await f[j](),response:f};let $={};try{$=await f.json()}catch{$=await f.text()}return{error:$,response:f}}return{async GET(c,o){return u(c,{...o,method:"GET"})},async PUT(c,o){return u(c,{...o,method:"PUT"})},async POST(c,o){return u(c,{...o,method:"POST"})},async DELETE(c,o){return u(c,{...o,method:"DELETE"})},async OPTIONS(c,o){return u(c,{...o,method:"OPTIONS"})},async HEAD(c,o){return u(c,{...o,method:"HEAD"})},async PATCH(c,o){return u(c,{...o,method:"PATCH"})},async TRACE(c,o){return u(c,{...o,method:"TRACE"})},use(...c){for(let o of c)if(o){if(typeof o!="object"||!("onRequest"in o||"onResponse"in o))throw new Error("Middleware must be an object with one of `onRequest()` or `onResponse()`");l.push(o)}},eject(...c){for(let o of c){let p=l.indexOf(o);p!==-1&&l.splice(p,1)}}}}function w(s,t,e){if(t==null)return"";if(typeof t=="object")throw new Error("Deeply-nested arrays/objects aren\u2019t supported. Provide your own `querySerializer()` to handle these.");return`${s}=${e?.allowReserved===!0?t:encodeURIComponent(t)}`}function z(s,t,e){if(!t||typeof t!="object")return"";let n=[],r={simple:",",label:".",matrix:";"}[e.style]||"&";if(e.style!=="deepObject"&&e.explode===!1){for(let l in t)n.push(l,e.allowReserved===!0?t[l]:encodeURIComponent(t[l]));let a=n.join(",");switch(e.style){case"form":return`${s}=${a}`;case"label":return`.${a}`;case"matrix":return`;${s}=${a}`;default:return a}}for(let a in t){let l=e.style==="deepObject"?`${s}[${a}]`:a;n.push(w(l,t[a],e))}let i=n.join(r);return e.style==="label"||e.style==="matrix"?`${r}${i}`:i}function q(s,t,e){if(!Array.isArray(t))return"";if(e.explode===!1){let i={form:",",spaceDelimited:"%20",pipeDelimited:"|"}[e.style]||",",a=(e.allowReserved===!0?t:t.map(l=>encodeURIComponent(l))).join(i);switch(e.style){case"simple":return a;case"label":return`.${a}`;case"matrix":return`;${s}=${a}`;case"spaceDelimited":case"pipeDelimited":default:return`${s}=${a}`}}let n={simple:",",label:".",matrix:";"}[e.style]||"&",r=[];for(let i of t)e.style==="simple"||e.style==="label"?r.push(e.allowReserved===!0?i:encodeURIComponent(i)):r.push(w(s,i,e));return e.style==="label"||e.style==="matrix"?`${n}${r.join(n)}`:r.join(n)}function S(s){return function(e){let n=[];if(e&&typeof e=="object")for(let r in e){let i=e[r];if(i!=null){if(Array.isArray(i)){n.push(q(r,i,{style:"form",explode:!0,...s?.array,allowReserved:s?.allowReserved||!1}));continue}if(typeof i=="object"){n.push(z(r,i,{style:"deepObject",explode:!0,...s?.object,allowReserved:s?.allowReserved||!1}));continue}n.push(w(r,i,s))}}return n.join("&")}}function P(s,t){let e=s;for(let n of s.match(U)??[]){let r=n.substring(1,n.length-1),i=!1,a="simple";if(r.endsWith("*")&&(i=!0,r=r.substring(0,r.length-1)),r.startsWith(".")?(a="label",r=r.substring(1)):r.startsWith(";")&&(a="matrix",r=r.substring(1)),!t||t[r]===void 0||t[r]===null)continue;let l=t[r];if(Array.isArray(l)){e=e.replace(n,q(r,l,{style:a,explode:i}));continue}if(typeof l=="object"){e=e.replace(n,z(r,l,{style:a,explode:i}));continue}if(a==="matrix"){e=e.replace(n,`;${w(r,l)}`);continue}e=e.replace(n,a==="label"?`.${l}`:l)}return e}function I(s){return JSON.stringify(s)}function L(s,t){let e=`${t.baseUrl}${s}`;t.params?.path&&(e=P(e,t.params.path));let n=t.querySerializer(t.params.query??{});return n.startsWith("?")&&(n=n.substring(1)),n&&(e+=`?${n}`),e}function T(...s){let t=new Headers;for(let e of s){if(!e||typeof e!="object")continue;let n=e instanceof Headers?e.entries():Object.entries(e);for(let[r,i]of n)if(i===null)t.delete(r);else if(Array.isArray(i))for(let a of i)t.append(r,a);else i!==void 0&&t.set(r,i)}return t}export{L as createFinalURL,S as createQuerySerializer,H as default,I as defaultBodySerializer,P as defaultPathSerializer,T as mergeHeaders,q as serializeArrayParam,z as serializeObjectParam,w as serializePrimitiveParam};
{
"name": "openapi-fetch",
"description": "Fast, typesafe fetch client for your OpenAPI schema. Only 2kb (min). Works with React, Vue, Svelte, or vanilla JS.",
"version": "0.9.0-rc.0",
"description": "Fast, typesafe fetch client for your OpenAPI schema. Only 5 kb (min). Works with React, Vue, Svelte, or vanilla JS.",
"version": "0.9.0",
"author": {

@@ -66,3 +66,3 @@ "name": "Drew Powers",

"dependencies": {
"openapi-typescript-helpers": "^0.0.6"
"openapi-typescript-helpers": "^0.0.7"
},

@@ -69,0 +69,0 @@ "devDependencies": {

<img src="../../docs/public/assets/openapi-fetch.svg" alt="openapi-fetch" width="216" height="40" />
openapi-fetch is a typesafe fetch client that pulls in your OpenAPI schema. Weighs **2 kB** and has virtually zero runtime. Works with React, Vue, Svelte, or vanilla JS.
openapi-fetch is a typesafe fetch client that pulls in your OpenAPI schema. Weighs **4 kB** and has virtually zero runtime. Works with React, Vue, Svelte, or vanilla JS.
| Library | Size (min) | “GET” request |
| :------------------------- | ---------: | :------------------------ |
| openapi-fetch | `2 kB` | `151k` ops/s (fastest) |
| openapi-typescript-fetch | `4 kB` | `99k` ops/s (1.4× slower) |
| axios | `32 kB` | `90k` ops/s (1.6× slower) |
| superagent | `55 kB` | `42k` ops/s (3× slower) |
| openapi-typescript-codegen | `367 kB` | `71k` ops/s (2× slower) |
| Library | Size (min) | “GET” request |
| :------------------------- | ---------: | :------------------------- |
| openapi-fetch | `5 kB` | `278k` ops/s (fastest) |
| openapi-typescript-fetch | `4 kB` | `130k` ops/s (2.1× slower) |
| axios | `32 kB` | `217k` ops/s (1.3× slower) |
| superagent | `55 kB` | `63k` ops/s (4.4× slower) |
| openapi-typescript-codegen | `367 kB` | `106k` ops/s (2.6× slower) |

@@ -48,3 +48,3 @@ The syntax is inspired by popular libraries like react-query or Apollo client, but without all the bells and whistles and in a 2 kB package.

- ✅ Also eliminates `as` type overrides that can also hide bugs
- ✅ All of this in a **2 kB** client package 🎉
- ✅ All of this in a **5 kb** client package 🎉

@@ -51,0 +51,0 @@ ## 🔧 Setup

@@ -23,3 +23,3 @@ import type {

/** global querySerializer */
querySerializer?: QuerySerializer<unknown>;
querySerializer?: QuerySerializer<unknown> | QuerySerializerOptions;
/** global bodySerializer */

@@ -48,2 +48,28 @@ bodySerializer?: BodySerializer<unknown>;

/** @see https://swagger.io/docs/specification/serialization/#query */
export type QuerySerializerOptions = {
/** Set serialization for arrays. @see https://swagger.io/docs/specification/serialization/#query */
array?: {
/** default: "form" */
style: "form" | "spaceDelimited" | "pipeDelimited";
/** default: true */
explode: boolean;
};
/** Set serialization for objects. @see https://swagger.io/docs/specification/serialization/#query */
object?: {
/** default: "deepObject" */
style: "form" | "deepObject";
/** default: true */
explode: boolean;
};
/**
* The `allowReserved` keyword specifies whether the reserved characters
* `:/?#[]@!$&'()*+,;=` in parameter values are allowed to be sent as they
* are, or should be percent-encoded. By default, allowReserved is `false`,
* and reserved characters are percent-encoded.
* @see https://swagger.io/docs/specification/serialization/#query
*/
allowReserved?: boolean;
};
export type BodySerializer<T> = (body: OperationRequestBodyContent<T>) => any;

@@ -112,3 +138,3 @@

RequestBodyOption<T> & {
querySerializer?: QuerySerializer<T>;
querySerializer?: QuerySerializer<T> | QuerySerializerOptions;
bodySerializer?: BodySerializer<T>;

@@ -154,2 +180,10 @@ parseAs?: ParseAs;

export type ClientMethod<Paths extends {}, M> = <
P extends PathsWithMethod<Paths, M>,
I extends MaybeOptionalInit<Paths[P], M>,
>(
url: P,
...init: I
) => Promise<FetchResponse<Paths[P][M], I[0]>>;
export default function createClient<Paths extends {}>(

@@ -159,65 +193,17 @@ clientOptions?: ClientOptions,

/** Call a GET endpoint */
GET<
P extends PathsWithMethod<Paths, "get">,
I extends MaybeOptionalInit<Paths[P], "get">,
>(
url: P,
...init: I
): Promise<FetchResponse<Paths[P]["get"], I[0]>>;
GET: ClientMethod<Paths, "get">;
/** Call a PUT endpoint */
PUT<
P extends PathsWithMethod<Paths, "put">,
I extends MaybeOptionalInit<Paths[P], "put">,
>(
url: P,
...init: I
): Promise<FetchResponse<Paths[P]["put"], I[0]>>;
PUT: ClientMethod<Paths, "put">;
/** Call a POST endpoint */
POST<
P extends PathsWithMethod<Paths, "post">,
I extends MaybeOptionalInit<Paths[P], "post">,
>(
url: P,
...init: I
): Promise<FetchResponse<Paths[P]["post"], I[0]>>;
POST: ClientMethod<Paths, "post">;
/** Call a DELETE endpoint */
DELETE<
P extends PathsWithMethod<Paths, "delete">,
I extends MaybeOptionalInit<Paths[P], "delete">,
>(
url: P,
...init: I
): Promise<FetchResponse<Paths[P]["delete"], I[0]>>;
DELETE: ClientMethod<Paths, "delete">;
/** Call a OPTIONS endpoint */
OPTIONS<
P extends PathsWithMethod<Paths, "options">,
I extends MaybeOptionalInit<Paths[P], "options">,
>(
url: P,
...init: I
): Promise<FetchResponse<Paths[P]["options"], I[0]>>;
OPTIONS: ClientMethod<Paths, "options">;
/** Call a HEAD endpoint */
HEAD<
P extends PathsWithMethod<Paths, "head">,
I extends MaybeOptionalInit<Paths[P], "head">,
>(
url: P,
...init: I
): Promise<FetchResponse<Paths[P]["head"], I[0]>>;
HEAD: ClientMethod<Paths, "head">;
/** Call a PATCH endpoint */
PATCH<
P extends PathsWithMethod<Paths, "patch">,
I extends MaybeOptionalInit<Paths[P], "patch">,
>(
url: P,
...init: I
): Promise<FetchResponse<Paths[P]["patch"], I[0]>>;
PATCH: ClientMethod<Paths, "patch">;
/** Call a TRACE endpoint */
TRACE<
P extends PathsWithMethod<Paths, "trace">,
I extends MaybeOptionalInit<Paths[P], "trace">,
>(
url: P,
...init: I
): Promise<FetchResponse<Paths[P]["trace"], I[0]>>;
TRACE: ClientMethod<Paths, "trace">;
/** Register middleware */

@@ -229,10 +215,51 @@ use(...middleware: Middleware[]): void;

/** Serialize primitive params to string */
export declare function serializePrimitiveParam(
name: string,
value: string,
options?: { allowReserved?: boolean },
): string;
/** Serialize object param to string */
export declare function serializeObjectParam(
name: string,
value: Record<string, unknown>,
options: {
style: "simple" | "label" | "matrix" | "form" | "deepObject";
explode: boolean;
allowReserved?: boolean;
},
): string;
/** Serialize array param to string */
export declare function serializeArrayParam(
name: string,
value: unknown[],
options: {
style:
| "simple"
| "label"
| "matrix"
| "form"
| "spaceDelimited"
| "pipeDelimited";
explode: boolean;
allowReserved?: boolean;
},
): string;
/** Serialize query params to string */
export declare function defaultQuerySerializer<T = unknown>(q: T): string;
export declare function createQuerySerializer<T = unknown>(
options?: QuerySerializerOptions,
): (queryParams: T) => string;
/** Serialize query param schema types according to expected default OpenAPI 3.x behavior */
export declare function defaultQueryParamSerializer<T = unknown>(
key: string[],
value: T,
): string | undefined;
/**
* Handle different OpenAPI 3.x serialization styles
* @type {import("./index.js").defaultPathSerializer}
* @see https://swagger.io/docs/specification/serialization/#path
*/
export declare function defaultPathSerializer(
pathname: string,
pathParams: Record<string, unknown>,
): string;

@@ -239,0 +266,0 @@ /** Serialize body object to string */

@@ -6,2 +6,4 @@ // settings & const

const PATH_PARAM_RE = /\{[^{}]+\}/g;
/**

@@ -15,4 +17,4 @@ * Create an openapi-fetch client.

fetch: baseFetch = globalThis.fetch,
querySerializer: globalQuerySerializer = defaultQuerySerializer,
bodySerializer: globalBodySerializer = defaultBodySerializer,
querySerializer: globalQuerySerializer,
bodySerializer: globalBodySerializer,
headers: baseHeaders,

@@ -22,3 +24,3 @@ ...baseOptions

if (baseUrl.endsWith("/")) {
baseUrl = baseUrl.slice(0, -1); // remove trailing slash
baseUrl = baseUrl.substring(0, baseUrl.length - 1);
}

@@ -39,7 +41,23 @@ baseHeaders = mergeHeaders(DEFAULT_HEADERS, baseHeaders);

parseAs = "json",
querySerializer = globalQuerySerializer,
bodySerializer = globalBodySerializer,
querySerializer: requestQuerySerializer,
bodySerializer = globalBodySerializer ?? defaultBodySerializer,
...init
} = fetchOptions || {};
let querySerializer =
typeof globalQuerySerializer === "function"
? globalQuerySerializer
: createQuerySerializer(globalQuerySerializer);
if (requestQuerySerializer) {
querySerializer =
typeof requestQuerySerializer === "function"
? requestQuerySerializer
: createQuerySerializer({
...(typeof globalQuerySerializer === "object"
? globalQuerySerializer
: {}),
...requestQuerySerializer,
});
}
const requestInit = {

@@ -55,7 +73,3 @@ redirect: "follow",

let request = new Request(
createFinalURL(url, {
baseUrl,
params,
querySerializer,
}),
createFinalURL(url, { baseUrl, params, querySerializer }),
requestInit,

@@ -201,73 +215,220 @@ );

/**
* Serialize query params to string
* @type {import("./index.js").defaultQuerySerializer}
* Serialize primitive param values
* @type {import("./index.js").serializePrimitiveParam}
*/
export function defaultQuerySerializer(q) {
const search = [];
if (q && typeof q === "object") {
for (const [k, v] of Object.entries(q)) {
const value = defaultQueryParamSerializer([k], v);
if (value) {
search.push(value);
export function serializePrimitiveParam(name, value, options) {
if (value === undefined || value === null) {
return "";
}
if (typeof value === "object") {
throw new Error(
`Deeply-nested arrays/objects aren’t supported. Provide your own \`querySerializer()\` to handle these.`,
);
}
return `${name}=${options?.allowReserved === true ? value : encodeURIComponent(value)}`;
}
/**
* Serialize object param (shallow only)
* @type {import("./index.js").serializeObjectParam}
*/
export function serializeObjectParam(name, value, options) {
if (!value || typeof value !== "object") {
return "";
}
const values = [];
const joiner =
{
simple: ",",
label: ".",
matrix: ";",
}[options.style] || "&";
// explode: false
if (options.style !== "deepObject" && options.explode === false) {
for (const k in value) {
values.push(
k,
options.allowReserved === true
? value[k]
: encodeURIComponent(value[k]),
);
}
const final = values.join(","); // note: values are always joined by comma in explode: false (but joiner can prefix)
switch (options.style) {
case "form": {
return `${name}=${final}`;
}
case "label": {
return `.${final}`;
}
case "matrix": {
return `;${name}=${final}`;
}
default: {
return final;
}
}
}
return search.join("&");
// explode: true
for (const k in value) {
const finalName = options.style === "deepObject" ? `${name}[${k}]` : k;
values.push(serializePrimitiveParam(finalName, value[k], options));
}
const final = values.join(joiner);
return options.style === "label" || options.style === "matrix"
? `${joiner}${final}`
: final;
}
/**
* Serialize query param schema types according to expected default OpenAPI 3.x behavior
* @type {import("./index.js").defaultQueryParamSerializer}
* Serialize array param (shallow only)
* @type {import("./index.js").serializeArrayParam}
*/
export function defaultQueryParamSerializer(key, value) {
if (value === null || value === undefined) {
return undefined;
export function serializeArrayParam(name, value, options) {
if (!Array.isArray(value)) {
return "";
}
if (typeof value === "string") {
return `${deepObjectPath(key)}=${encodeURIComponent(value)}`;
}
if (typeof value === "number" || typeof value === "boolean") {
return `${deepObjectPath(key)}=${String(value)}`;
}
if (Array.isArray(value)) {
if (!value.length) {
return undefined;
}
const nextValue = [];
for (const item of value) {
const next = defaultQueryParamSerializer(key, item);
if (next !== undefined) {
nextValue.push(next);
// explode: false
if (options.explode === false) {
const joiner =
{ form: ",", spaceDelimited: "%20", pipeDelimited: "|" }[options.style] ||
","; // note: for arrays, joiners vary wildly based on style + explode behavior
const final = (
options.allowReserved === true
? value
: value.map((v) => encodeURIComponent(v))
).join(joiner);
switch (options.style) {
case "simple": {
return final;
}
case "label": {
return `.${final}`;
}
case "matrix": {
return `;${name}=${final}`;
}
case "spaceDelimited":
case "pipeDelimited":
default: {
return `${name}=${final}`;
}
}
return nextValue.join(`&`);
}
if (typeof value === "object") {
if (!Object.keys(value).length) {
return undefined;
// explode: true
const joiner = { simple: ",", label: ".", matrix: ";" }[options.style] || "&";
const values = [];
for (const v of value) {
if (options.style === "simple" || options.style === "label") {
values.push(options.allowReserved === true ? v : encodeURIComponent(v));
} else {
values.push(serializePrimitiveParam(name, v, options));
}
const nextValue = [];
for (const [k, v] of Object.entries(value)) {
if (v !== undefined && v !== null) {
const next = defaultQueryParamSerializer([...key, k], v);
if (next !== undefined) {
nextValue.push(next);
}
return options.style === "label" || options.style === "matrix"
? `${joiner}${values.join(joiner)}`
: values.join(joiner);
}
/**
* Serialize query params to string
* @type {import("./index.js").createQuerySerializer}
*/
export function createQuerySerializer(options) {
return function querySerializer(queryParams) {
const search = [];
if (queryParams && typeof queryParams === "object") {
for (const name in queryParams) {
const value = queryParams[name];
if (value === undefined || value === null) {
continue;
}
if (Array.isArray(value)) {
search.push(
serializeArrayParam(name, value, {
style: "form",
explode: true,
...options?.array,
allowReserved: options?.allowReserved || false,
}),
);
continue;
}
if (typeof value === "object") {
search.push(
serializeObjectParam(name, value, {
style: "deepObject",
explode: true,
...options?.object,
allowReserved: options?.allowReserved || false,
}),
);
continue;
}
search.push(serializePrimitiveParam(name, value, options));
}
}
return nextValue.join("&");
}
return encodeURIComponent(`${deepObjectPath(key)}=${String(value)}`);
return search.join("&");
};
}
/**
* Flatten a node path into a deepObject string
* @type {import("./index.js").deepObjectPath}
* Handle different OpenAPI 3.x serialization styles
* @type {import("./index.js").defaultPathSerializer}
* @see https://swagger.io/docs/specification/serialization/#path
*/
function deepObjectPath(path) {
let output = path[0];
for (const k of path.slice(1)) {
output += `[${k}]`;
export function defaultPathSerializer(pathname, pathParams) {
let nextURL = pathname;
for (const match of pathname.match(PATH_PARAM_RE) ?? []) {
let name = match.substring(1, match.length - 1);
let explode = false;
let style = "simple";
if (name.endsWith("*")) {
explode = true;
name = name.substring(0, name.length - 1);
}
if (name.startsWith(".")) {
style = "label";
name = name.substring(1);
} else if (name.startsWith(";")) {
style = "matrix";
name = name.substring(1);
}
if (
!pathParams ||
pathParams[name] === undefined ||
pathParams[name] === null
) {
continue;
}
const value = pathParams[name];
if (Array.isArray(value)) {
nextURL = nextURL.replace(
match,
serializeArrayParam(name, value, { style, explode }),
);
continue;
}
if (typeof value === "object") {
nextURL = nextURL.replace(
match,
serializeObjectParam(name, value, { style, explode }),
);
continue;
}
if (style === "matrix") {
nextURL = nextURL.replace(
match,
`;${serializePrimitiveParam(name, value)}`,
);
continue;
}
nextURL = nextURL.replace(match, style === "label" ? `.${value}` : value);
continue;
}
return output;
return nextURL;
}

@@ -289,8 +450,9 @@

let finalURL = `${options.baseUrl}${pathname}`;
if (options.params.path) {
for (const [k, v] of Object.entries(options.params.path)) {
finalURL = finalURL.replace(`{${k}}`, encodeURIComponent(String(v)));
}
if (options.params?.path) {
finalURL = defaultPathSerializer(finalURL, options.params.path);
}
const search = options.querySerializer(options.params.query ?? {});
let search = options.querySerializer(options.params.query ?? {});
if (search.startsWith("?")) {
search = search.substring(1);
}
if (search) {

@@ -297,0 +459,0 @@ finalURL += `?${search}`;

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc