@zayne-labs/callapi 1.11.33 → 1.11.35

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 CHANGED
@@ -10,8 +10,8 @@
10
10
  <a href="https://github.com/zayne-labs/callapi/blob/master/LICENSE"><img src="https://img.shields.io/npm/l/@zayne-labs/callapi?style=flat&color=EFBA5F" alt="license"></a>
11
11
  <a href="https://www.npmjs.com/package/@zayne-labs/callapi"><img src="https://img.shields.io/npm/dm/@zayne-labs/callapi?style=flat&color=EFBA5F" alt="downloads per month"></a>
12
12
  <a href="https://github.com/zayne-labs/callapi/graphs/commit-activity"><img src="https://img.shields.io/github/commit-activity/m/zayne-labs/callapi?style=flat&color=EFBA5F" alt="commit activity"></a>
13
- <a href="https://code2tutorial.com/tutorial/f77cfbd0-3c37-4c37-9608-b3c977e46f00/index.md"><img src="https://img.shields.io/badge/Code2Tutorial-blue?color=blue&logo=victoriametrics" alt="Code2Tutorial"></a>
14
13
  <a href="https://deepwiki.com/zayne-labs/callapi"><img src="https://deepwiki.com/badge.svg" alt="Ask DeepWiki"></a>
14
+ <a href="https://code2tutorial.com/tutorial/f77cfbd0-3c37-4c37-9608-b3c977e46f00/index.md"><img src="https://img.shields.io/badge/Code2Tutorial-blue?color=blue&logo=victoriametrics" alt="Code2Tutorial"></a>
15
15
  </p>
16
16
 
17
17
  <p align="center">
@@ -33,7 +33,7 @@ const isValidJsonString = (value) => {
33
33
  return false;
34
34
  }
35
35
  };
36
- const isSerializable = (value) => {
36
+ const isSerializableObject = (value) => {
37
37
  return isPlainObject(value) || isArray(value) || typeof value?.toJSON === "function";
38
38
  };
39
39
  const isFunction = (value) => typeof value === "function";
@@ -46,12 +46,20 @@ const isReadableStream = (value) => {
46
46
 
47
47
  //#endregion
48
48
  //#region src/utils/external/body.ts
49
- const toQueryString = (query) => {
50
- if (!query) {
51
- console.error("toQueryString:", "No query params provided!");
52
- return null;
49
+ const toStringOrStringify = (value) => {
50
+ return isString(value) ? value : JSON.stringify(value);
51
+ };
52
+ const toQueryString = (data) => {
53
+ const queryString = new URLSearchParams();
54
+ for (const [key, value] of Object.entries(data)) {
55
+ if (value == null) continue;
56
+ if (isArray(value)) {
57
+ for (const innerValue of value) queryString.append(key, toStringOrStringify(innerValue));
58
+ continue;
59
+ }
60
+ queryString.set(key, toStringOrStringify(value));
53
61
  }
54
- return new URLSearchParams(query).toString();
62
+ return queryString.toString();
55
63
  };
56
64
  const toBlobOrString = (value) => {
57
65
  return isBlob(value) ? value : String(value);
@@ -96,7 +104,7 @@ const toFormData = (data) => {
96
104
  const formData = new FormData();
97
105
  for (const [key, value] of Object.entries(data)) {
98
106
  if (isArray(value)) {
99
- value.forEach((innerValue) => formData.append(key, toBlobOrString(innerValue)));
107
+ for (const innerValue of value) formData.append(key, toBlobOrString(innerValue));
100
108
  continue;
101
109
  }
102
110
  if (isObject(value) && !isBlob(value)) {
@@ -175,6 +183,30 @@ var ValidationError = class ValidationError extends Error {
175
183
  }
176
184
  };
177
185
 
186
+ //#endregion
187
+ //#region src/utils/external/define.ts
188
+ const defineSchema = (routes, config) => {
189
+ return {
190
+ config: defineSchemaConfig(config),
191
+ routes: defineSchemaRoutes(routes)
192
+ };
193
+ };
194
+ const defineSchemaRoutes = (routes) => {
195
+ return routes;
196
+ };
197
+ const defineMainSchema = (mainSchema) => {
198
+ return mainSchema;
199
+ };
200
+ const defineSchemaConfig = (config) => {
201
+ return config;
202
+ };
203
+ const definePlugin = (plugin) => {
204
+ return plugin;
205
+ };
206
+ const defineBaseConfig = (baseConfig) => {
207
+ return baseConfig;
208
+ };
209
+
178
210
  //#endregion
179
211
  //#region src/utils/external/guards.ts
180
212
  const isHTTPError = (error) => {
@@ -194,5 +226,5 @@ const isJavascriptError = (error) => {
194
226
  };
195
227
 
196
228
  //#endregion
197
- export { isReadableStream as _, isValidationErrorInstance as a, isValidJsonString as b, toFormData as c, isBoolean as d, isFunction as f, isQueryString as g, isPromise as h, isValidationError as i, toQueryString as l, isPlainObject as m, isHTTPErrorInstance as n, HTTPError as o, isObject as p, isJavascriptError as r, ValidationError as s, isHTTPError as t, isArray as u, isSerializable as v, isString as y };
198
- //# sourceMappingURL=guards-ZYV-Q_as.js.map
229
+ export { isReadableStream as C, isValidJsonString as E, isQueryString as S, isString as T, isBoolean as _, isValidationErrorInstance as a, isPlainObject as b, definePlugin as c, defineSchemaRoutes as d, HTTPError as f, isArray as g, toQueryString as h, isValidationError as i, defineSchema as l, toFormData as m, isHTTPErrorInstance as n, defineBaseConfig as o, ValidationError as p, isJavascriptError as r, defineMainSchema as s, isHTTPError as t, defineSchemaConfig as u, isFunction as v, isSerializableObject as w, isPromise as x, isObject as y };
230
+ //# sourceMappingURL=external-DXaCWLPN.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"external-DXaCWLPN.js","names":["defineBaseConfig: DefineBaseConfig"],"sources":["../src/utils/guards.ts","../src/utils/external/body.ts","../src/utils/external/error.ts","../src/utils/external/define.ts","../src/utils/external/guards.ts"],"sourcesContent":["import type { AnyFunction } from \"../types/type-helpers\";\n\nexport const isArray = <TArrayItem>(value: unknown): value is TArrayItem[] => Array.isArray(value);\n\nexport const isBoolean = (value: unknown): value is boolean => typeof value === \"boolean\";\n\nexport const isBlob = (value: unknown): value is Blob => value instanceof Blob;\n\nexport const isObject = <TObject extends object>(value: unknown): value is TObject => {\n\treturn typeof value === \"object\" && value !== null;\n};\n\nconst hasObjectPrototype = (value: unknown) => {\n\treturn Object.prototype.toString.call(value) === \"[object Object]\";\n};\n\n/**\n * @description Copied from TanStack Query's isPlainObject\n * @see https://github.com/TanStack/query/blob/main/packages/query-core/src/utils.ts#L321\n */\nexport const isPlainObject = <TPlainObject extends Record<string, unknown>>(\n\tvalue: unknown\n): value is TPlainObject => {\n\tif (!hasObjectPrototype(value)) {\n\t\treturn false;\n\t}\n\n\t// If has no constructor\n\tconst constructor = (value as object | undefined)?.constructor;\n\tif (constructor === undefined) {\n\t\treturn true;\n\t}\n\n\t// If has modified prototype\n\tconst prototype = constructor.prototype as object;\n\tif (!hasObjectPrototype(prototype)) {\n\t\treturn false;\n\t}\n\n\t// If constructor does not have an Object-specific method\n\tif (!Object.hasOwn(prototype, \"isPrototypeOf\")) {\n\t\treturn false;\n\t}\n\n\t// Handles Objects created by Object.create(<arbitrary prototype>)\n\tif (Object.getPrototypeOf(value) !== Object.prototype) {\n\t\treturn false;\n\t}\n\n\t// It's probably a plain object at this point\n\treturn true;\n};\n\nexport const isValidJsonString = (value: unknown): value is string => {\n\tif (!isString(value)) {\n\t\treturn false;\n\t}\n\n\ttry {\n\t\tJSON.parse(value);\n\t\treturn true;\n\t} catch {\n\t\treturn false;\n\t}\n};\n\nexport const isSerializableObject = (value: unknown) => {\n\treturn (\n\t\tisPlainObject(value)\n\t\t|| isArray(value)\n\t\t|| typeof (value as { toJSON: unknown } | undefined)?.toJSON === \"function\"\n\t);\n};\n\nexport const isFunction = <TFunction extends AnyFunction>(value: unknown): value is TFunction =>\n\ttypeof value === \"function\";\n\nexport const isQueryString = (value: unknown): value is string => isString(value) && value.includes(\"=\");\n\nexport const isString = (value: unknown) => typeof value === \"string\";\n\nexport const isPromise = (value: unknown) => value instanceof Promise;\n\nexport const isReadableStream = (value: unknown): value is ReadableStream<unknown> => {\n\treturn value instanceof ReadableStream;\n};\n\n// https://github.com/unjs/ofetch/blob/main/src/utils.ts\nexport const isJSONSerializable = (value: unknown) => {\n\tif (value === undefined) {\n\t\treturn false;\n\t}\n\tconst t = typeof value;\n\t// eslint-disable-next-line ts-eslint/no-unnecessary-condition -- No time to make this more type-safe\n\tif (t === \"string\" || t === \"number\" || t === \"boolean\" || t === null) {\n\t\treturn true;\n\t}\n\tif (t !== \"object\") {\n\t\treturn false;\n\t}\n\tif (isArray(value)) {\n\t\treturn true;\n\t}\n\tif ((value as Buffer | null)?.buffer) {\n\t\treturn false;\n\t}\n\n\treturn (\n\t\tvalue?.constructor.name === \"Object\"\n\t\t|| typeof (value as { toJSON: () => unknown } | null)?.toJSON === \"function\"\n\t);\n};\n","import type { CallApiRequestOptions } from \"../../types/common\";\nimport { isArray, isBlob, isObject, isString } from \"../guards\";\n\nconst toStringOrStringify = (value: unknown): string => {\n\treturn isString(value) ? value : JSON.stringify(value);\n};\n\nexport const toQueryString = (data: NonNullable<CallApiRequestOptions[\"body\"]>) => {\n\tconst queryString = new URLSearchParams();\n\n\tfor (const [key, value] of Object.entries(data)) {\n\t\tif (value == null) continue;\n\n\t\tif (isArray(value)) {\n\t\t\t// eslint-disable-next-line max-depth -- Allow\n\t\t\tfor (const innerValue of value) {\n\t\t\t\tqueryString.append(key, toStringOrStringify(innerValue));\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\n\t\tqueryString.set(key, toStringOrStringify(value));\n\t}\n\n\treturn queryString.toString();\n};\n\nconst toBlobOrString = (value: unknown): string | Blob => {\n\treturn isBlob(value) ? value : String(value);\n};\n\n/**\n * @description Converts a plain object to FormData.\n *\n * Handles various data types:\n * - **Primitives** (string, number, boolean): Converted to strings\n * - **Blobs/Files**: Added directly to FormData\n * - **Arrays**: Each item is appended (allows multiple values for same key)\n * - **Objects**: JSON stringified before adding to FormData\n *\n * @example\n * ```ts\n * // Basic usage\n * const formData = toFormData({\n * name: \"John\",\n * age: 30,\n * active: true\n * });\n *\n * // With arrays\n * const formData = toFormData({\n * tags: [\"javascript\", \"typescript\"],\n * name: \"John\"\n * });\n *\n * // With files\n * const formData = toFormData({\n * avatar: fileBlob,\n * name: \"John\"\n * });\n *\n * // With nested objects (one level only)\n * const formData = toFormData({\n * user: { name: \"John\", age: 30 },\n * settings: { theme: \"dark\" }\n * });\n */\nexport const toFormData = (data: NonNullable<CallApiRequestOptions[\"body\"]>) => {\n\tconst formData = new FormData();\n\n\tfor (const [key, value] of Object.entries(data)) {\n\t\tif (isArray(value)) {\n\t\t\t// eslint-disable-next-line max-depth -- Allow for now\n\t\t\tfor (const innerValue of value) {\n\t\t\t\tformData.append(key, toBlobOrString(innerValue));\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (isObject(value) && !isBlob(value)) {\n\t\t\tformData.set(key, JSON.stringify(value));\n\t\t\tcontinue;\n\t\t}\n\n\t\tformData.set(key, toBlobOrString(value));\n\t}\n\n\treturn formData;\n};\n","import { extraOptionDefaults } from \"../../constants/defaults\";\nimport type { CallApiExtraOptions } from \"../../types/common\";\nimport type { StandardSchemaV1 } from \"../../types/standard-schema\";\nimport type { CallApiSchema, CallApiSchemaConfig } from \"../../validation\";\nimport { isObject, isString } from \"../guards\";\n\ntype HTTPErrorDetails<TErrorData> = Pick<CallApiExtraOptions, \"defaultHTTPErrorMessage\"> & {\n\terrorData: TErrorData;\n\tresponse: Response;\n};\n\nconst httpErrorSymbol = Symbol(\"HTTPError\");\n\nexport class HTTPError<TErrorData = Record<string, unknown>> extends Error {\n\terrorData: HTTPErrorDetails<TErrorData>[\"errorData\"];\n\n\treadonly httpErrorSymbol = httpErrorSymbol;\n\n\toverride name = \"HTTPError\" as const;\n\n\tresponse: HTTPErrorDetails<TErrorData>[\"response\"];\n\n\tconstructor(errorDetails: HTTPErrorDetails<TErrorData>, errorOptions?: ErrorOptions) {\n\t\tconst { defaultHTTPErrorMessage, errorData, response } = errorDetails;\n\n\t\tconst resolvedDefaultHTTPErrorMessage =\n\t\t\tisString(defaultHTTPErrorMessage) ? defaultHTTPErrorMessage : (\n\t\t\t\tdefaultHTTPErrorMessage?.({ errorData, response })\n\t\t\t);\n\n\t\tconst selectedDefaultErrorMessage =\n\t\t\tresolvedDefaultHTTPErrorMessage\n\t\t\t?? (response.statusText || extraOptionDefaults.defaultHTTPErrorMessage);\n\n\t\tconst message =\n\t\t\t(errorData as { message?: string } | undefined)?.message ?? selectedDefaultErrorMessage;\n\n\t\tsuper(message, errorOptions);\n\n\t\tthis.errorData = errorData;\n\t\tthis.response = response;\n\t}\n\n\t/**\n\t * @description Checks if the given error is an instance of HTTPError\n\t * @param error - The error to check\n\t * @returns true if the error is an instance of HTTPError, false otherwise\n\t */\n\tstatic override isError<TErrorData>(error: unknown): error is HTTPError<TErrorData> {\n\t\tif (!isObject<HTTPError>(error)) {\n\t\t\treturn false;\n\t\t}\n\n\t\tif (error instanceof HTTPError) {\n\t\t\treturn true;\n\t\t}\n\n\t\tconst actualError = error as HTTPError;\n\n\t\treturn (\n\t\t\tactualError.httpErrorSymbol === httpErrorSymbol\n\t\t\t// eslint-disable-next-line ts-eslint/no-unnecessary-condition -- Allow\n\t\t\t&& actualError.name === \"HTTPError\"\n\t\t);\n\t}\n}\n\nconst prettifyPath = (path: ValidationError[\"errorData\"][number][\"path\"]) => {\n\tif (!path || path.length === 0) {\n\t\treturn \"\";\n\t}\n\n\tconst pathString = path.map((segment) => (isObject(segment) ? segment.key : segment)).join(\".\");\n\n\treturn ` → at ${pathString}`;\n};\n\nconst prettifyValidationIssues = (issues: ValidationError[\"errorData\"]) => {\n\tconst issuesString = issues.map((issue) => `✖ ${issue.message}${prettifyPath(issue.path)}`).join(\" | \");\n\n\treturn issuesString;\n};\n\ntype SafeExtract<TUnion, TKey extends TUnion> = Extract<TUnion, TKey>;\n\ntype ValidationErrorDetails = {\n\t/**\n\t * The cause of the validation error.\n\t *\n\t * It's either the name the schema for which validation failed, or the name of the schema config option that led to the validation error.\n\t */\n\tissueCause:\n\t\t| \"unknown\"\n\t\t| `schemaConfig-(${SafeExtract<keyof CallApiSchemaConfig, \"strict\">})`\n\t\t| keyof CallApiSchema;\n\n\t/**\n\t * The issues that caused the validation error.\n\t */\n\tissues: readonly StandardSchemaV1.Issue[];\n\n\t/**\n\t * The response from server, if any.\n\t */\n\tresponse: Response | null;\n};\n\nconst validationErrorSymbol = Symbol(\"ValidationErrorSymbol\");\n\nexport class ValidationError extends Error {\n\terrorData: ValidationErrorDetails[\"issues\"];\n\n\tissueCause: ValidationErrorDetails[\"issueCause\"];\n\n\toverride name = \"ValidationError\" as const;\n\n\tresponse: ValidationErrorDetails[\"response\"];\n\n\treadonly validationErrorSymbol = validationErrorSymbol;\n\n\tconstructor(details: ValidationErrorDetails, errorOptions?: ErrorOptions) {\n\t\tconst { issueCause, issues, response } = details;\n\n\t\tconst prettyMessage = prettifyValidationIssues(issues);\n\n\t\tconst message = `(${issueCause.toUpperCase()}) - ${prettyMessage}`;\n\n\t\tsuper(message, errorOptions);\n\n\t\tthis.errorData = issues;\n\t\tthis.response = response;\n\t\tthis.issueCause = issueCause;\n\t}\n\n\t/**\n\t * @description Checks if the given error is an instance of ValidationError\n\t * @param error - The error to check\n\t * @returns true if the error is an instance of ValidationError, false otherwise\n\t */\n\tstatic override isError(error: unknown): error is ValidationError {\n\t\tif (!isObject<ValidationError>(error)) {\n\t\t\treturn false;\n\t\t}\n\n\t\tif (error instanceof ValidationError) {\n\t\t\treturn true;\n\t\t}\n\n\t\tconst actualError = error as ValidationError;\n\n\t\treturn (\n\t\t\tactualError.validationErrorSymbol === validationErrorSymbol\n\t\t\t// eslint-disable-next-line ts-eslint/no-unnecessary-condition -- Allow\n\t\t\t&& actualError.name === \"ValidationError\"\n\t\t);\n\t}\n}\n","import type { CallApiPlugin } from \"../../plugins\";\nimport type { BaseCallApiConfig } from \"../../types/common\";\nimport type { AnyFunction, Satisfies, Writeable } from \"../../types/type-helpers\";\nimport type {\n\tBaseCallApiSchemaAndConfig,\n\tBaseCallApiSchemaRoutes,\n\tCallApiSchema,\n\tCallApiSchemaConfig,\n} from \"../../validation\";\n\nexport const defineSchema = <\n\tconst TBaseSchemaRoutes extends BaseCallApiSchemaRoutes,\n\tconst TSchemaConfig extends CallApiSchemaConfig,\n>(\n\troutes: TBaseSchemaRoutes,\n\tconfig?: Satisfies<TSchemaConfig, CallApiSchemaConfig>\n) => {\n\treturn {\n\t\tconfig: defineSchemaConfig(config as NonNullable<typeof config>),\n\t\troutes: defineSchemaRoutes(routes),\n\t} satisfies BaseCallApiSchemaAndConfig;\n};\n\nexport const defineSchemaRoutes = <const TSchemaRoutes extends BaseCallApiSchemaRoutes>(\n\troutes: TSchemaRoutes\n) => {\n\treturn routes as Writeable<typeof routes, \"deep\">;\n};\n\nexport const defineMainSchema = <const TSchema extends CallApiSchema>(\n\tmainSchema: Satisfies<TSchema, CallApiSchema>\n) => {\n\treturn mainSchema as Writeable<typeof mainSchema, \"deep\">;\n};\n\nexport const defineSchemaConfig = <const TSchemaConfig extends CallApiSchemaConfig>(\n\tconfig: Satisfies<TSchemaConfig, CallApiSchemaConfig>\n) => {\n\treturn config as Writeable<typeof config, \"deep\">;\n};\n\nexport const definePlugin = <const TPlugin extends CallApiPlugin>(plugin: TPlugin) => {\n\treturn plugin as Writeable<typeof plugin, \"deep\">;\n};\n\ntype BaseConfigObject = Exclude<BaseCallApiConfig, AnyFunction>;\n\ntype BaseConfigFn = Extract<BaseCallApiConfig, AnyFunction>;\n\ntype DefineBaseConfig = {\n\t<const TBaseConfig extends BaseConfigObject>(\n\t\tbaseConfig: Satisfies<TBaseConfig, BaseConfigObject>\n\t): Writeable<typeof baseConfig, \"deep\">;\n\t<TBaseConfigFn extends BaseConfigFn>(baseConfig: TBaseConfigFn): TBaseConfigFn;\n};\n\nexport const defineBaseConfig: DefineBaseConfig = <const TBaseConfig extends BaseCallApiConfig>(\n\tbaseConfig: TBaseConfig\n) => {\n\treturn baseConfig;\n};\n","import type {\n\tCallApiResultErrorVariant,\n\tPossibleHTTPError,\n\tPossibleJavaScriptError,\n\tPossibleValidationError,\n} from \"../../result\";\nimport { isObject } from \"../guards\";\nimport { HTTPError, ValidationError } from \"./error\";\n\nexport const isHTTPError = <TErrorData>(\n\terror: CallApiResultErrorVariant<TErrorData>[\"error\"] | null\n): error is PossibleHTTPError<TErrorData> => {\n\treturn isObject(error) && error.name === \"HTTPError\";\n};\n\nexport const isHTTPErrorInstance = <TErrorData>(error: unknown) => {\n\treturn HTTPError.isError<TErrorData>(error);\n};\n\nexport const isValidationError = (\n\terror: CallApiResultErrorVariant<unknown>[\"error\"] | null\n): error is PossibleValidationError => {\n\treturn isObject(error) && error.name === \"ValidationError\";\n};\n\nexport const isValidationErrorInstance = (error: unknown): error is ValidationError => {\n\treturn ValidationError.isError(error);\n};\n\nexport const isJavascriptError = (\n\terror: CallApiResultErrorVariant<unknown>[\"error\"] | null\n): error is PossibleJavaScriptError => {\n\treturn isObject(error) && !isHTTPError(error) && !isValidationError(error);\n};\n"],"mappings":"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"}
@@ -183,7 +183,7 @@ interface CallApiSchemaConfig {
183
183
  /**
184
184
  * The base url of the schema. By default it's the baseURL of the callApi instance.
185
185
  */
186
- baseURL?: string;
186
+ baseURL?: "" | AnyString;
187
187
  /**
188
188
  * Disables runtime validation for the schema.
189
189
  */
@@ -191,17 +191,18 @@ interface CallApiSchemaConfig {
191
191
  /**
192
192
  * If `true`, the original input value will be used instead of the transformed/validated output.
193
193
  *
194
- * This is useful when you want to validate the input but don't want any transformations
195
- * applied by the validation schema (e.g., type coercion, default values, etc).
194
+ * When true, the original input is returned unchanged after validation, ignoring any schema-level
195
+ * transformations such as type coercion, default values, or field mapping. Only the validation
196
+ * step is executed; the resulting value is discarded in favor of the raw input.
196
197
  */
197
- disableValidationOutputApplication?: boolean | BooleanObject;
198
+ disableRuntimeValidationTransform?: boolean | BooleanObject;
198
199
  /**
199
200
  * Optional url prefix that will be substituted for the `baseURL` of the schemaConfig at runtime.
200
201
  *
201
- * This allows you to reuse the same schema against different base URLs (for example,
202
- * swapping between `/api/v1` and `/api/v2`) without redefining the entire schema.
202
+ * Enables a short, stable prefix for routes while keeping the full `baseURL` centralized in config.
203
+ * Keeps route definitions concise and shields them from changes to the underlying base URL.
203
204
  */
204
- prefix?: string;
205
+ prefix?: "" | AnyString;
205
206
  /**
206
207
  * Controls the strictness of API route validation.
207
208
  *
@@ -385,7 +386,8 @@ interface URLOptions {
385
386
  * @description Makes a type partial if the output type of TSchema is not provided or has undefined in the union, otherwise makes it required
386
387
  */
387
388
  type MakeSchemaOptionRequiredIfDefined<TSchemaOption extends CallApiSchema[keyof CallApiSchema], TObject> = undefined extends InferSchemaOutput<TSchemaOption, undefined> ? TObject : Required<TObject>;
388
- type ApplyURLBasedConfig<TSchemaConfig extends CallApiSchemaConfig, TSchemaRouteKeys extends string> = TSchemaConfig["prefix"] extends string ? `${TSchemaConfig["prefix"]}${TSchemaRouteKeys}` : TSchemaConfig["baseURL"] extends string ? `${TSchemaConfig["baseURL"]}${TSchemaRouteKeys}` : TSchemaRouteKeys;
389
+ type MergePrefixWithRouteKey<TPrefix extends string, TRouteKey extends string> = TRouteKey extends `@${infer TMethod extends RouteKeyMethods}/${infer TRestOfRoutKey}` ? `@${TMethod}/${TPrefix extends `/${infer TPrefixWithoutSlash}` ? TPrefixWithoutSlash : TPrefix}${TRestOfRoutKey}` : `${TPrefix}${TRouteKey}`;
390
+ type ApplyURLBasedConfig<TSchemaConfig extends CallApiSchemaConfig, TSchemaRouteKeys extends string> = TSchemaConfig["prefix"] extends string ? MergePrefixWithRouteKey<TSchemaConfig["prefix"], TSchemaRouteKeys> : TSchemaConfig["baseURL"] extends string ? MergePrefixWithRouteKey<TSchemaConfig["baseURL"], TSchemaRouteKeys> : TSchemaRouteKeys;
389
391
  type ApplyStrictConfig<TSchemaConfig extends CallApiSchemaConfig, TSchemaRouteKeys extends string> = TSchemaConfig["strict"] extends true ? TSchemaRouteKeys :
390
392
  // eslint-disable-next-line perfectionist/sort-union-types -- Don't sort union types
391
393
  TSchemaRouteKeys | Exclude<InitURLOrURLObject, RouteKeyMethodsURLUnion>;
@@ -459,19 +461,40 @@ type InferAuthOption<TSchema extends CallApiSchema> = MakeSchemaOptionRequiredIf
459
461
  *
460
462
  * @example
461
463
  * ```ts
462
- * const callMainApi = callApi.create({
463
- * baseURL: "https://main-api.com",
464
- * onRequest: ({ options }) => {
465
- * if (options.auth) {
466
- * options.headers.Authorization = options.auth;
467
- * }
464
+ * // Bearer auth
465
+ * const response = await callMainApi({
466
+ * url: "https://example.com/api/data",
467
+ * auth: "123456",
468
+ * });
469
+ *
470
+ * // Bearer auth
471
+ * const response = await callMainApi({
472
+ * url: "https://example.com/api/data",
473
+ * auth: {
474
+ * type: "Bearer",
475
+ * value: "123456",
476
+ * },
477
+ })
478
+ *
479
+ * // Token auth
480
+ * const response = await callMainApi({
481
+ * url: "https://example.com/api/data",
482
+ * auth: {
483
+ * type: "Token",
484
+ * value: "123456",
468
485
  * },
469
486
  * });
470
487
  *
488
+ * // Basic auth
471
489
  * const response = await callMainApi({
472
490
  * url: "https://example.com/api/data",
473
- * auth: "Bearer 123456",
491
+ * auth: {
492
+ * type: "Basic",
493
+ * username: "username",
494
+ * password: "password",
495
+ * },
474
496
  * });
497
+ *
475
498
  * ```
476
499
  */
477
500
  auth?: InferSchemaOutput<TSchema["auth"], AuthOption>;
@@ -1855,4 +1878,4 @@ declare const callApi: <TData = unknown, TErrorData = unknown, TResultMode exten
1855
1878
  }, TSchema, CallApiSchemaConfig, TSchemaConfig, TInitURL, TCurrentRouteSchemaKey, DefaultPluginArray, TPluginArray>) => Promise<TComputedResult>;
1856
1879
  //#endregion
1857
1880
  export { Writeable as $, ErrorContext as A, CallApiPlugin as B, InferExtendSchemaContext as C, ValidationError as D, HTTPError as E, ResponseContext as F, BaseCallApiSchemaRoutes as G, PluginSetupContext as H, ResponseErrorContext as I, CallApiSchemaConfig as J, BaseSchemaRouteKeyPrefixes as K, ResponseStreamContext as L, HooksOrHooksArray as M, RequestContext as N, RetryOptions as O, RequestStreamContext as P, Satisfies as Q, SuccessContext as R, GetExtendSchemaConfigContext as S, Register as T, InferParamsFromRoute as U, PluginHooks as V, URLOptions as W, InferSchemaOutput as X, InferSchemaInput as Y, AnyFunction as Z, CallApiExtraOptionsForHooks as _, CallApiResultSuccessOrErrorVariant as a, CallApiRequestOptionsForHooks as b, PossibleJavaScriptError as c, ResponseTypeType as d, ResultModeType as f, CallApiExtraOptions as g, CallApiConfig as h, CallApiResultErrorVariant as i, Hooks as j, DedupeOptions as k, PossibleJavaScriptOrValidationError as l, BaseCallApiExtraOptions as m, createFetchClient as n, CallApiResultSuccessVariant as o, BaseCallApiConfig as p, CallApiSchema as q, createFetchClientWithContext as r, PossibleHTTPError as s, callApi as t, PossibleValidationError as u, CallApiParameters as v, InstanceContext as w, CallApiResultLoose as x, CallApiRequestOptions as y, DefaultCallApiContext as z };
1858
- //# sourceMappingURL=index-YnlEWnbR.d.ts.map
1881
+ //# sourceMappingURL=index-BEp4V3_c.d.ts.map
package/dist/index.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- import { A as ErrorContext, B as CallApiPlugin, C as InferExtendSchemaContext, F as ResponseContext, G as BaseCallApiSchemaRoutes, H as PluginSetupContext, I as ResponseErrorContext, J as CallApiSchemaConfig, K as BaseSchemaRouteKeyPrefixes, L as ResponseStreamContext, M as HooksOrHooksArray, N as RequestContext, O as RetryOptions, P as RequestStreamContext, R as SuccessContext, S as GetExtendSchemaConfigContext, T as Register, U as InferParamsFromRoute, V as PluginHooks, W as URLOptions, X as InferSchemaOutput, Y as InferSchemaInput, _ as CallApiExtraOptionsForHooks, a as CallApiResultSuccessOrErrorVariant, b as CallApiRequestOptionsForHooks, c as PossibleJavaScriptError, d as ResponseTypeType, f as ResultModeType, g as CallApiExtraOptions, h as CallApiConfig, i as CallApiResultErrorVariant, j as Hooks, k as DedupeOptions, l as PossibleJavaScriptOrValidationError, m as BaseCallApiExtraOptions, n as createFetchClient, o as CallApiResultSuccessVariant, p as BaseCallApiConfig, q as CallApiSchema, r as createFetchClientWithContext, s as PossibleHTTPError, t as callApi, u as PossibleValidationError, v as CallApiParameters, w as InstanceContext, x as CallApiResultLoose, y as CallApiRequestOptions, z as DefaultCallApiContext } from "./index-YnlEWnbR.js";
1
+ import { A as ErrorContext, B as CallApiPlugin, C as InferExtendSchemaContext, F as ResponseContext, G as BaseCallApiSchemaRoutes, H as PluginSetupContext, I as ResponseErrorContext, J as CallApiSchemaConfig, K as BaseSchemaRouteKeyPrefixes, L as ResponseStreamContext, M as HooksOrHooksArray, N as RequestContext, O as RetryOptions, P as RequestStreamContext, R as SuccessContext, S as GetExtendSchemaConfigContext, T as Register, U as InferParamsFromRoute, V as PluginHooks, W as URLOptions, X as InferSchemaOutput, Y as InferSchemaInput, _ as CallApiExtraOptionsForHooks, a as CallApiResultSuccessOrErrorVariant, b as CallApiRequestOptionsForHooks, c as PossibleJavaScriptError, d as ResponseTypeType, f as ResultModeType, g as CallApiExtraOptions, h as CallApiConfig, i as CallApiResultErrorVariant, j as Hooks, k as DedupeOptions, l as PossibleJavaScriptOrValidationError, m as BaseCallApiExtraOptions, n as createFetchClient, o as CallApiResultSuccessVariant, p as BaseCallApiConfig, q as CallApiSchema, r as createFetchClientWithContext, s as PossibleHTTPError, t as callApi, u as PossibleValidationError, v as CallApiParameters, w as InstanceContext, x as CallApiResultLoose, y as CallApiRequestOptions, z as DefaultCallApiContext } from "./index-BEp4V3_c.js";
2
2
  export { BaseCallApiConfig, BaseCallApiExtraOptions, BaseCallApiSchemaRoutes, BaseSchemaRouteKeyPrefixes, CallApiConfig, CallApiExtraOptions, CallApiExtraOptionsForHooks, CallApiParameters, CallApiPlugin, CallApiRequestOptions, CallApiRequestOptionsForHooks, CallApiResultLoose as CallApiResult, CallApiResultErrorVariant, CallApiResultSuccessOrErrorVariant, CallApiResultSuccessVariant, CallApiSchema, CallApiSchemaConfig, DedupeOptions, DefaultCallApiContext, ErrorContext, GetExtendSchemaConfigContext, Hooks, HooksOrHooksArray, InferExtendSchemaContext, InferParamsFromRoute, InferSchemaInput, InferSchemaOutput, InstanceContext, PluginHooks, PluginSetupContext, PossibleHTTPError, PossibleJavaScriptError, PossibleJavaScriptOrValidationError, PossibleValidationError, Register, RequestContext, RequestStreamContext, ResponseContext, ResponseErrorContext, ResponseStreamContext, ResponseTypeType, ResultModeType, RetryOptions, SuccessContext, URLOptions, callApi, createFetchClient, createFetchClientWithContext };
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { n as requestOptionDefaults, r as defineEnum, t as extraOptionDefaults } from "./defaults-B-dOt2Dd.js";
2
- import { _ as isReadableStream, a as isValidationErrorInstance, b as isValidJsonString, d as isBoolean, f as isFunction, g as isQueryString, h as isPromise, l as toQueryString, m as isPlainObject, n as isHTTPErrorInstance, o as HTTPError, p as isObject, s as ValidationError, u as isArray, v as isSerializable, y as isString } from "./guards-ZYV-Q_as.js";
2
+ import { C as isReadableStream, E as isValidJsonString, S as isQueryString, T as isString, _ as isBoolean, a as isValidationErrorInstance, b as isPlainObject, f as HTTPError, g as isArray, h as toQueryString, n as isHTTPErrorInstance, p as ValidationError, v as isFunction, w as isSerializableObject, x as isPromise, y as isObject } from "./external-DXaCWLPN.js";
3
3
  import { n as fetchSpecificKeys, t as fallBackRouteSchemaKey } from "./validation-DbbofkNi.js";
4
4
 
5
5
  //#region src/auth.ts
@@ -78,8 +78,8 @@ const handleSchemaValidation = async (fullSchema, schemaName, validationOptions)
78
78
  inputValue,
79
79
  response
80
80
  });
81
- const disableResultApplicationBooleanObject = isObject(schemaConfig?.disableValidationOutputApplication) ? schemaConfig.disableValidationOutputApplication : {};
82
- if (schemaConfig?.disableValidationOutputApplication === true || disableResultApplicationBooleanObject[schemaName] === true) return inputValue;
81
+ const disableResultApplicationBooleanObject = isObject(schemaConfig?.disableRuntimeValidationTransform) ? schemaConfig.disableRuntimeValidationTransform : {};
82
+ if (schemaConfig?.disableRuntimeValidationTransform === true || disableResultApplicationBooleanObject[schemaName] === true) return inputValue;
83
83
  return validResult;
84
84
  };
85
85
  const extraOptionsToBeValidated = [
@@ -225,7 +225,7 @@ const ampersand = "&";
225
225
  const mergeUrlWithQuery = (url, query) => {
226
226
  if (!query) return url;
227
227
  const queryString = toQueryString(query);
228
- if (queryString?.length === 0) return url;
228
+ if (queryString.length === 0) return url;
229
229
  if (url.endsWith(questionMark)) return `${url}${queryString}`;
230
230
  if (url.includes(questionMark)) return `${url}${ampersand}${queryString}`;
231
231
  return `${url}${questionMark}${queryString}`;
@@ -242,24 +242,8 @@ const mergeUrlWithQuery = (url, query) => {
242
242
  *
243
243
  * @example
244
244
  * ```typescript
245
- * // Method extraction from prefixed routes
246
245
  * extractMethodFromURL("@get/users"); // Returns: "get"
247
246
  * extractMethodFromURL("@post/users"); // Returns: "post"
248
- * extractMethodFromURL("@put/users/:id"); // Returns: "put"
249
- * extractMethodFromURL("@delete/users/:id"); // Returns: "delete"
250
- * extractMethodFromURL("@patch/users/:id"); // Returns: "patch"
251
- *
252
- * // No method modifier
253
- * extractMethodFromURL("/users"); // Returns: undefined
254
- * extractMethodFromURL("users"); // Returns: undefined
255
- *
256
- * // Invalid or unsupported methods
257
- * extractMethodFromURL("@invalid/users"); // Returns: undefined
258
- * extractMethodFromURL("@/users"); // Returns: undefined
259
- *
260
- * // Edge cases
261
- * extractMethodFromURL(undefined); // Returns: undefined
262
- * extractMethodFromURL(""); // Returns: undefined
263
247
  * ```
264
248
  */
265
249
  const extractMethodFromURL = (initURL) => {
@@ -271,14 +255,19 @@ const extractMethodFromURL = (initURL) => {
271
255
  const normalizeURL = (initURL) => {
272
256
  const methodFromURL = extractMethodFromURL(initURL);
273
257
  if (!methodFromURL) return initURL;
274
- return initURL.replace(`@${methodFromURL}/`, "/");
258
+ return initURL.includes("http") ? initURL.replace(`@${methodFromURL}/`, "") : initURL.replace(`@${methodFromURL}/`, "/");
259
+ };
260
+ const getFullURL = (initURL, baseURL) => {
261
+ if (!baseURL || initURL.startsWith("http")) return initURL;
262
+ return initURL.length > 0 && !initURL.startsWith(slash) && !baseURL.endsWith(slash) ? `${baseURL}${slash}${initURL}` : `${baseURL}${initURL}`;
275
263
  };
276
264
  const getFullAndNormalizedURL = (options) => {
277
265
  const { baseURL, initURL, params, query } = options;
278
266
  const normalizedInitURL = normalizeURL(initURL);
279
- const urlWithMergedQueryAndParams = mergeUrlWithQuery(mergeUrlWithParams(normalizedInitURL, params), query);
267
+ const fullURL = getFullURL(mergeUrlWithQuery(mergeUrlWithParams(normalizedInitURL, params), query), baseURL);
268
+ if (!URL.canParse(fullURL)) console.error(`Invalid URL '${normalizedInitURL}'. Are you passing a relative url to CallApi but not setting the 'baseURL' option?`);
280
269
  return {
281
- fullURL: !urlWithMergedQueryAndParams.startsWith("http") && baseURL ? `${baseURL}${urlWithMergedQueryAndParams}` : urlWithMergedQueryAndParams,
270
+ fullURL,
282
271
  normalizedInitURL
283
272
  };
284
273
  };
@@ -336,29 +325,36 @@ const getResolvedHeaders = (options) => {
336
325
  const { baseHeaders, headers } = options;
337
326
  return objectifyHeaders(isFunction(headers) ? headers({ baseHeaders: objectifyHeaders(baseHeaders) }) : headers ?? baseHeaders);
338
327
  };
328
+ const detectContentTypeHeader = (body) => {
329
+ if (isQueryString(body)) return { "Content-Type": "application/x-www-form-urlencoded" };
330
+ if (isSerializableObject(body) || isValidJsonString(body)) return {
331
+ Accept: "application/json",
332
+ "Content-Type": "application/json"
333
+ };
334
+ return null;
335
+ };
339
336
  const getHeaders = async (options) => {
340
337
  const { auth, body, resolvedHeaders } = options;
341
- const headersObject = {
342
- ...await getAuthHeader(auth),
343
- ...objectifyHeaders(resolvedHeaders)
344
- };
345
- if (isQueryString(body)) {
346
- headersObject["Content-Type"] = "application/x-www-form-urlencoded";
347
- return headersObject;
348
- }
349
- if (isSerializable(body) || isValidJsonString(body)) {
350
- headersObject["Content-Type"] = "application/json";
351
- headersObject.Accept = "application/json";
338
+ const authHeaderObject = await getAuthHeader(auth);
339
+ const resolvedHeadersObject = objectifyHeaders(resolvedHeaders);
340
+ if (!(Object.hasOwn(resolvedHeadersObject, "Content-Type") || Object.hasOwn(resolvedHeadersObject, "content-type"))) {
341
+ const contentTypeHeader = detectContentTypeHeader(body);
342
+ contentTypeHeader && Object.assign(resolvedHeadersObject, contentTypeHeader);
352
343
  }
353
- return headersObject;
344
+ return {
345
+ ...authHeaderObject,
346
+ ...resolvedHeadersObject
347
+ };
354
348
  };
355
349
  const getMethod = (ctx) => {
356
350
  const { initURL, method } = ctx;
357
351
  return method?.toUpperCase() ?? extractMethodFromURL(initURL)?.toUpperCase() ?? requestOptionDefaults.method;
358
352
  };
359
353
  const getBody = (options) => {
360
- const { body, bodySerializer } = options;
361
- if (isSerializable(body)) return (bodySerializer ?? extraOptionDefaults.bodySerializer)(body);
354
+ const { body, bodySerializer, resolvedHeaders } = options;
355
+ const existingContentType = new Headers(resolvedHeaders).get("content-type");
356
+ if (!existingContentType && isSerializableObject(body)) return (bodySerializer ?? extraOptionDefaults.bodySerializer)(body);
357
+ if (existingContentType === "application/x-www-form-urlencoded" && isSerializableObject(body)) return toQueryString(body);
362
358
  return body;
363
359
  };
364
360
  const getInitFetchImpl = (customFetchImpl) => {
@@ -1065,7 +1061,8 @@ const createFetchClientWithContext = () => {
1065
1061
  Object.assign(request, {
1066
1062
  body: getBody({
1067
1063
  body: requestOptionsValidationResult.body,
1068
- bodySerializer: options.bodySerializer
1064
+ bodySerializer: options.bodySerializer,
1065
+ resolvedHeaders: requestOptionsValidationResult.headers
1069
1066
  }),
1070
1067
  headers: await getHeaders({
1071
1068
  auth: options.auth,