@sudobility/svgr_client 1.0.11 → 1.0.13

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.
@@ -1 +1 @@
1
- {"version":3,"file":"query-keys.d.ts","sourceRoot":"","sources":["../../src/hooks/query-keys.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,QAAQ;;;CAGpB,CAAC"}
1
+ {"version":3,"file":"query-keys.d.ts","sourceRoot":"","sources":["../../src/hooks/query-keys.ts"],"names":[],"mappings":"AAoBA,eAAO,MAAM,QAAQ;;;CAKpB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"query-keys.js","sourceRoot":"","sources":["../../src/hooks/query-keys.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,QAAQ,GAAG;IACtB,GAAG,EAAE,CAAC,MAAM,CAAU;IACtB,OAAO,EAAE,GAAG,EAAE,CAAC,CAAC,GAAG,QAAQ,CAAC,GAAG,EAAE,SAAS,CAAU;CACrD,CAAC","sourcesContent":["export const svgrKeys = {\n all: [\"svgr\"] as const,\n convert: () => [...svgrKeys.all, \"convert\"] as const,\n};\n"]}
1
+ {"version":3,"file":"query-keys.js","sourceRoot":"","sources":["../../src/hooks/query-keys.ts"],"names":[],"mappings":"AAoBA,MAAM,CAAC,MAAM,QAAQ,GAAG;IAEtB,GAAG,EAAE,CAAC,MAAM,CAAU;IAEtB,OAAO,EAAE,GAAG,EAAE,CAAC,CAAC,GAAG,QAAQ,CAAC,GAAG,EAAE,SAAS,CAAU;CACrD,CAAC","sourcesContent":["/**\n * Query key factory for SVGR-related TanStack Query cache keys.\n *\n * Provides a hierarchical key structure for consistent cache invalidation.\n * All keys are `as const` tuples for type-safe query key matching.\n *\n * @example\n * ```typescript\n * import { svgrKeys } from '@sudobility/svgr_client';\n * import { useQueryClient } from '@tanstack/react-query';\n *\n * const queryClient = useQueryClient();\n *\n * // Invalidate all SVGR-related queries\n * queryClient.invalidateQueries({ queryKey: svgrKeys.all });\n *\n * // Invalidate only convert-related queries\n * queryClient.invalidateQueries({ queryKey: svgrKeys.convert() });\n * ```\n */\nexport const svgrKeys = {\n /** Base key for all SVGR queries: `[\"svgr\"]` */\n all: [\"svgr\"] as const,\n /** Key for conversion queries: `[\"svgr\", \"convert\"]` */\n convert: () => [...svgrKeys.all, \"convert\"] as const,\n};\n"]}
@@ -1,8 +1,9 @@
1
1
  import type { SvgrClient } from "../network/SvgrClient";
2
- export declare function useConvert(client: SvgrClient): import("@tanstack/react-query").UseMutationResult<import("@sudobility/types").BaseResponse<import("@sudobility/svgr_types").ConvertResult>, Error, {
2
+ export interface ConvertMutationParams {
3
3
  original: string;
4
4
  filename?: string;
5
5
  quality?: number;
6
6
  transparentBg?: boolean;
7
- }, unknown>;
7
+ }
8
+ export declare function useConvert(client: SvgrClient): import("@tanstack/react-query").UseMutationResult<import("@sudobility/types").BaseResponse<import("@sudobility/svgr_types").ConvertResult>, Error, ConvertMutationParams, unknown>;
8
9
  //# sourceMappingURL=useConvert.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"useConvert.d.ts","sourceRoot":"","sources":["../../src/hooks/useConvert.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAExD,wBAAgB,UAAU,CAAC,MAAM,EAAE,UAAU;cAQ7B,MAAM;eACL,MAAM;cACP,MAAM;oBACA,OAAO;YAG5B"}
1
+ {"version":3,"file":"useConvert.d.ts","sourceRoot":"","sources":["../../src/hooks/useConvert.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAWxD,MAAM,WAAW,qBAAqB;IAEpC,QAAQ,EAAE,MAAM,CAAC;IAEjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAkDD,wBAAgB,UAAU,CAAC,MAAM,EAAE,UAAU,sLAU5C"}
@@ -1 +1 @@
1
- {"version":3,"file":"useConvert.js","sourceRoot":"","sources":["../../src/hooks/useConvert.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAGpD,MAAM,UAAU,UAAU,CAAC,MAAkB;IAC3C,OAAO,WAAW,CAAC;QACjB,UAAU,EAAE,CAAC,EACX,QAAQ,EACR,QAAQ,EACR,OAAO,EACP,aAAa,GAMd,EAAE,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE,aAAa,CAAC;KACjE,CAAC,CAAC;AACL,CAAC","sourcesContent":["import { useMutation } from \"@tanstack/react-query\";\nimport type { SvgrClient } from \"../network/SvgrClient\";\n\nexport function useConvert(client: SvgrClient) {\n return useMutation({\n mutationFn: ({\n original,\n filename,\n quality,\n transparentBg,\n }: {\n original: string;\n filename?: string;\n quality?: number;\n transparentBg?: boolean;\n }) => client.convert(original, filename, quality, transparentBg),\n });\n}\n"]}
1
+ {"version":3,"file":"useConvert.js","sourceRoot":"","sources":["../../src/hooks/useConvert.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAuEpD,MAAM,UAAU,UAAU,CAAC,MAAkB;IAC3C,OAAO,WAAW,CAAC;QACjB,UAAU,EAAE,CAAC,EACX,QAAQ,EACR,QAAQ,EACR,OAAO,EACP,aAAa,GACS,EAAE,EAAE,CAC1B,MAAM,CAAC,OAAO,CAAC,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE,aAAa,CAAC;KAC7D,CAAC,CAAC;AACL,CAAC","sourcesContent":["import { useMutation } from \"@tanstack/react-query\";\nimport type { SvgrClient } from \"../network/SvgrClient\";\n\n/**\n * Parameters for the {@link useConvert} mutation hook.\n *\n * @interface ConvertMutationParams\n * @property {string} original - Base64-encoded raster image data (PNG, JPG, WEBP, BMP, or GIF)\n * @property {string} [filename] - Optional filename for metadata or audit purposes\n * @property {number} [quality] - Conversion quality level from 1 to 10. Default: 5\n * @property {boolean} [transparentBg] - If true, removes the background from the SVG. Default: false\n */\nexport interface ConvertMutationParams {\n /** Base64-encoded raster image data (PNG, JPG, WEBP, BMP, or GIF) */\n original: string;\n /** Optional filename for metadata or audit purposes */\n filename?: string;\n /** Conversion quality level from 1 to 10 (1 = smallest, 10 = highest fidelity). Default: 5 */\n quality?: number;\n /** If true, removes the background from the SVG. Default: false */\n transparentBg?: boolean;\n}\n\n/**\n * TanStack Query mutation hook for converting raster images to SVG.\n *\n * Wraps {@link SvgrClient.convert} in a `useMutation` hook, providing\n * loading state, error handling, and cache integration via TanStack Query.\n *\n * @param client - An initialized {@link SvgrClient} instance\n * @returns A TanStack Query mutation result object with `mutate`, `mutateAsync`,\n * `isPending`, `isError`, `data`, `error`, and other standard mutation properties\n *\n * @example\n * ```tsx\n * import { useConvert } from '@sudobility/svgr_client';\n *\n * function ConvertButton({ client }: { client: SvgrClient }) {\n * const convert = useConvert(client);\n *\n * const handleConvert = () => {\n * convert.mutate({\n * original: base64ImageData,\n * filename: 'logo.png',\n * quality: 7,\n * transparentBg: true,\n * });\n * };\n *\n * if (convert.isPending) return <p>Converting...</p>;\n * if (convert.isError) return <p>Error: {convert.error.message}</p>;\n * if (convert.data?.data) return <div>{convert.data.data.svg}</div>;\n *\n * return <button onClick={handleConvert}>Convert</button>;\n * }\n * ```\n *\n * @example\n * ```typescript\n * // Using mutateAsync for async/await flow\n * const convert = useConvert(client);\n * try {\n * const result = await convert.mutateAsync({ original: imageData });\n * console.log(result.data?.svg);\n * } catch (error) {\n * if (error instanceof SvgrApiError) {\n * console.error(`HTTP ${error.status}: ${error.message}`);\n * }\n * }\n * ```\n */\nexport function useConvert(client: SvgrClient) {\n return useMutation({\n mutationFn: ({\n original,\n filename,\n quality,\n transparentBg,\n }: ConvertMutationParams) =>\n client.convert(original, filename, quality, transparentBg),\n });\n}\n"]}
package/dist/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
- export { SvgrClient, SvgrApiError, type SvgrClientConfig, } from "./network/SvgrClient";
1
+ export { SvgrClient, SvgrApiError, type SvgrClientConfig, type RetryConfig, } from "./network/SvgrClient";
2
2
  export { useConvert, svgrKeys } from "./hooks";
3
+ export type { ConvertMutationParams } from "./hooks/useConvert";
3
4
  export type { ConvertRequest, ConvertResult, ConvertResponse, BaseResponse, } from "@sudobility/svgr_types";
4
5
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EACL,UAAU,EACV,YAAY,EACZ,KAAK,gBAAgB,GACtB,MAAM,sBAAsB,CAAC;AAG9B,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAG/C,YAAY,EACV,cAAc,EACd,aAAa,EACb,eAAe,EACf,YAAY,GACb,MAAM,wBAAwB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAgBA,OAAO,EACL,UAAU,EACV,YAAY,EACZ,KAAK,gBAAgB,EACrB,KAAK,WAAW,GACjB,MAAM,sBAAsB,CAAC;AAG9B,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAC/C,YAAY,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAGhE,YAAY,EACV,cAAc,EACd,aAAa,EACb,eAAe,EACf,YAAY,GACb,MAAM,wBAAwB,CAAC"}
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EACL,UAAU,EACV,YAAY,GAEb,MAAM,sBAAsB,CAAC;AAG9B,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC","sourcesContent":["// Network\nexport {\n SvgrClient,\n SvgrApiError,\n type SvgrClientConfig,\n} from \"./network/SvgrClient\";\n\n// Hooks\nexport { useConvert, svgrKeys } from \"./hooks\";\n\n// Re-export types for convenience\nexport type {\n ConvertRequest,\n ConvertResult,\n ConvertResponse,\n BaseResponse,\n} from \"@sudobility/svgr_types\";\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAgBA,OAAO,EACL,UAAU,EACV,YAAY,GAGb,MAAM,sBAAsB,CAAC;AAG9B,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC","sourcesContent":["/**\n * @module @sudobility/svgr_client\n *\n * API client SDK for SVGR with React Query hooks.\n *\n * Provides a typed HTTP client ({@link SvgrClient}) for calling the SVGR\n * image-to-SVG conversion API, along with TanStack Query hooks ({@link useConvert})\n * for seamless React integration.\n *\n * @example\n * ```typescript\n * import { SvgrClient, useConvert, svgrKeys } from '@sudobility/svgr_client';\n * ```\n */\n\n// Network\nexport {\n SvgrClient,\n SvgrApiError,\n type SvgrClientConfig,\n type RetryConfig,\n} from \"./network/SvgrClient\";\n\n// Hooks\nexport { useConvert, svgrKeys } from \"./hooks\";\nexport type { ConvertMutationParams } from \"./hooks/useConvert\";\n\n// Re-export types for convenience\nexport type {\n ConvertRequest,\n ConvertResult,\n ConvertResponse,\n BaseResponse,\n} from \"@sudobility/svgr_types\";\n"]}
@@ -3,6 +3,12 @@ import type { BaseResponse, ConvertResult } from "@sudobility/svgr_types";
3
3
  export interface SvgrClientConfig {
4
4
  baseUrl: string;
5
5
  networkClient: NetworkClient;
6
+ retry?: RetryConfig;
7
+ }
8
+ export interface RetryConfig {
9
+ maxRetries: number;
10
+ baseDelayMs: number;
11
+ retryableStatuses?: number[];
6
12
  }
7
13
  export declare class SvgrApiError extends Error {
8
14
  status: number;
@@ -11,7 +17,9 @@ export declare class SvgrApiError extends Error {
11
17
  export declare class SvgrClient {
12
18
  private readonly baseUrl;
13
19
  private readonly networkClient;
20
+ private readonly retryConfig;
14
21
  constructor(config: SvgrClientConfig);
15
22
  convert(original: string, filename?: string, quality?: number, transparentBg?: boolean): Promise<BaseResponse<ConvertResult>>;
23
+ private withRetry;
16
24
  }
17
25
  //# sourceMappingURL=SvgrClient.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"SvgrClient.d.ts","sourceRoot":"","sources":["../../src/network/SvgrClient.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAE1E,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,EAAE,aAAa,CAAC;CAC9B;AAED,qBAAa,YAAa,SAAQ,KAAK;IAE5B,MAAM,EAAE,MAAM;gBAAd,MAAM,EAAE,MAAM,EACrB,OAAO,EAAE,MAAM;CAKlB;AAED,qBAAa,UAAU;IACrB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAgB;gBAElC,MAAM,EAAE,gBAAgB;IAK9B,OAAO,CACX,QAAQ,EAAE,MAAM,EAChB,QAAQ,CAAC,EAAE,MAAM,EACjB,OAAO,CAAC,EAAE,MAAM,EAChB,aAAa,CAAC,EAAE,OAAO,GACtB,OAAO,CAAC,YAAY,CAAC,aAAa,CAAC,CAAC;CAqBxC"}
1
+ {"version":3,"file":"SvgrClient.d.ts","sourceRoot":"","sources":["../../src/network/SvgrClient.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAmB1E,MAAM,WAAW,gBAAgB;IAE/B,OAAO,EAAE,MAAM,CAAC;IAEhB,aAAa,EAAE,aAAa,CAAC;IAM7B,KAAK,CAAC,EAAE,WAAW,CAAC;CACrB;AAqBD,MAAM,WAAW,WAAW;IAE1B,UAAU,EAAE,MAAM,CAAC;IAEnB,WAAW,EAAE,MAAM,CAAC;IAEpB,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;CAC9B;AA8BD,qBAAa,YAAa,SAAQ,KAAK;IAG5B,MAAM,EAAE,MAAM;gBAAd,MAAM,EAAE,MAAM,EACrB,OAAO,EAAE,MAAM;CAKlB;AAgCD,qBAAa,UAAU;IACrB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAgB;IAC9C,OAAO,CAAC,QAAQ,CAAC,WAAW,CAA0B;gBAE1C,MAAM,EAAE,gBAAgB;IAmC9B,OAAO,CACX,QAAQ,EAAE,MAAM,EAChB,QAAQ,CAAC,EAAE,MAAM,EACjB,OAAO,CAAC,EAAE,MAAM,EAChB,aAAa,CAAC,EAAE,OAAO,GACtB,OAAO,CAAC,YAAY,CAAC,aAAa,CAAC,CAAC;YAmCzB,SAAS;CAsBxB"}
@@ -1,3 +1,4 @@
1
+ const DEFAULT_RETRYABLE_STATUSES = [408, 429, 500, 502, 503, 504];
1
2
  export class SvgrApiError extends Error {
2
3
  constructor(status, message) {
3
4
  super(message);
@@ -9,19 +10,36 @@ export class SvgrClient {
9
10
  constructor(config) {
10
11
  this.baseUrl = config.baseUrl;
11
12
  this.networkClient = config.networkClient;
13
+ this.retryConfig = config.retry;
12
14
  }
13
15
  async convert(original, filename, quality, transparentBg) {
14
- const response = await this.networkClient.post(`${this.baseUrl}/api/v1/convert`, {
16
+ const makeRequest = () => this.networkClient.post(`${this.baseUrl}/api/v1/convert`, {
15
17
  original,
16
18
  filename,
17
19
  quality,
18
20
  transparentBg,
19
21
  });
22
+ const response = this.retryConfig
23
+ ? await this.withRetry(makeRequest, this.retryConfig)
24
+ : await makeRequest();
20
25
  if (!response.ok || !response.data) {
21
26
  const errorData = response.data;
22
27
  throw new SvgrApiError(response.status, errorData?.error || "Conversion failed");
23
28
  }
24
29
  return response.data;
25
30
  }
31
+ async withRetry(fn, config) {
32
+ const retryableStatuses = config.retryableStatuses ?? DEFAULT_RETRYABLE_STATUSES;
33
+ let response = await fn();
34
+ for (let attempt = 0; attempt < config.maxRetries; attempt++) {
35
+ if (response.ok || !retryableStatuses.includes(response.status)) {
36
+ return response;
37
+ }
38
+ const delay = config.baseDelayMs * Math.pow(2, attempt);
39
+ await new Promise((resolve) => setTimeout(resolve, delay));
40
+ response = await fn();
41
+ }
42
+ return response;
43
+ }
26
44
  }
27
45
  //# sourceMappingURL=SvgrClient.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"SvgrClient.js","sourceRoot":"","sources":["../../src/network/SvgrClient.ts"],"names":[],"mappings":"AAQA,MAAM,OAAO,YAAa,SAAQ,KAAK;IACrC,YACS,MAAc,EACrB,OAAe;QAEf,KAAK,CAAC,OAAO,CAAC,CAAC;QAHR,WAAM,GAAN,MAAM,CAAQ;QAIrB,IAAI,CAAC,IAAI,GAAG,cAAc,CAAC;IAC7B,CAAC;CACF;AAED,MAAM,OAAO,UAAU;IAIrB,YAAY,MAAwB;QAClC,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC;QAC9B,IAAI,CAAC,aAAa,GAAG,MAAM,CAAC,aAAa,CAAC;IAC5C,CAAC;IAED,KAAK,CAAC,OAAO,CACX,QAAgB,EAChB,QAAiB,EACjB,OAAgB,EAChB,aAAuB;QAEvB,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,aAAa,CAAC,IAAI,CAC5C,GAAG,IAAI,CAAC,OAAO,iBAAiB,EAChC;YACE,QAAQ;YACR,QAAQ;YACR,OAAO;YACP,aAAa;SACd,CACF,CAAC;QAEF,IAAI,CAAC,QAAQ,CAAC,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC;YACnC,MAAM,SAAS,GAAG,QAAQ,CAAC,IAA6C,CAAC;YACzE,MAAM,IAAI,YAAY,CACpB,QAAQ,CAAC,MAAM,EACf,SAAS,EAAE,KAAK,IAAI,mBAAmB,CACxC,CAAC;QACJ,CAAC;QAED,OAAO,QAAQ,CAAC,IAAI,CAAC;IACvB,CAAC;CACF","sourcesContent":["import type { NetworkClient } from \"@sudobility/types\";\nimport type { BaseResponse, ConvertResult } from \"@sudobility/svgr_types\";\n\nexport interface SvgrClientConfig {\n baseUrl: string;\n networkClient: NetworkClient;\n}\n\nexport class SvgrApiError extends Error {\n constructor(\n public status: number,\n message: string,\n ) {\n super(message);\n this.name = \"SvgrApiError\";\n }\n}\n\nexport class SvgrClient {\n private readonly baseUrl: string;\n private readonly networkClient: NetworkClient;\n\n constructor(config: SvgrClientConfig) {\n this.baseUrl = config.baseUrl;\n this.networkClient = config.networkClient;\n }\n\n async convert(\n original: string,\n filename?: string,\n quality?: number,\n transparentBg?: boolean,\n ): Promise<BaseResponse<ConvertResult>> {\n const response = await this.networkClient.post<BaseResponse<ConvertResult>>(\n `${this.baseUrl}/api/v1/convert`,\n {\n original,\n filename,\n quality,\n transparentBg,\n },\n );\n\n if (!response.ok || !response.data) {\n const errorData = response.data as { error?: string } | null | undefined;\n throw new SvgrApiError(\n response.status,\n errorData?.error || \"Conversion failed\",\n );\n }\n\n return response.data;\n }\n}\n"]}
1
+ {"version":3,"file":"SvgrClient.js","sourceRoot":"","sources":["../../src/network/SvgrClient.ts"],"names":[],"mappings":"AA8DA,MAAM,0BAA0B,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;AA2BlE,MAAM,OAAO,YAAa,SAAQ,KAAK;IACrC,YAES,MAAc,EACrB,OAAe;QAEf,KAAK,CAAC,OAAO,CAAC,CAAC;QAHR,WAAM,GAAN,MAAM,CAAQ;QAIrB,IAAI,CAAC,IAAI,GAAG,cAAc,CAAC;IAC7B,CAAC;CACF;AAgCD,MAAM,OAAO,UAAU;IAKrB,YAAY,MAAwB;QAClC,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC;QAC9B,IAAI,CAAC,aAAa,GAAG,MAAM,CAAC,aAAa,CAAC;QAC1C,IAAI,CAAC,WAAW,GAAG,MAAM,CAAC,KAAK,CAAC;IAClC,CAAC;IA+BD,KAAK,CAAC,OAAO,CACX,QAAgB,EAChB,QAAiB,EACjB,OAAgB,EAChB,aAAuB;QAEvB,MAAM,WAAW,GAAG,GAAG,EAAE,CACvB,IAAI,CAAC,aAAa,CAAC,IAAI,CACrB,GAAG,IAAI,CAAC,OAAO,iBAAiB,EAChC;YACE,QAAQ;YACR,QAAQ;YACR,OAAO;YACP,aAAa;SACd,CACF,CAAC;QAEJ,MAAM,QAAQ,GAAG,IAAI,CAAC,WAAW;YAC/B,CAAC,CAAC,MAAM,IAAI,CAAC,SAAS,CAAC,WAAW,EAAE,IAAI,CAAC,WAAW,CAAC;YACrD,CAAC,CAAC,MAAM,WAAW,EAAE,CAAC;QAExB,IAAI,CAAC,QAAQ,CAAC,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC;YACnC,MAAM,SAAS,GAAG,QAAQ,CAAC,IAA6C,CAAC;YACzE,MAAM,IAAI,YAAY,CACpB,QAAQ,CAAC,MAAM,EACf,SAAS,EAAE,KAAK,IAAI,mBAAmB,CACxC,CAAC;QACJ,CAAC;QAED,OAAO,QAAQ,CAAC,IAAI,CAAC;IACvB,CAAC;IAUO,KAAK,CAAC,SAAS,CACrB,EAAsD,EACtD,MAAmB;QAEnB,MAAM,iBAAiB,GACrB,MAAM,CAAC,iBAAiB,IAAI,0BAA0B,CAAC;QAEzD,IAAI,QAAQ,GAAG,MAAM,EAAE,EAAE,CAAC;QAE1B,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC,UAAU,EAAE,OAAO,EAAE,EAAE,CAAC;YAC7D,IAAI,QAAQ,CAAC,EAAE,IAAI,CAAC,iBAAiB,CAAC,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;gBAChE,OAAO,QAAQ,CAAC;YAClB,CAAC;YAED,MAAM,KAAK,GAAG,MAAM,CAAC,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;YACxD,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC;YAE3D,QAAQ,GAAG,MAAM,EAAE,EAAE,CAAC;QACxB,CAAC;QAED,OAAO,QAAQ,CAAC;IAClB,CAAC;CACF","sourcesContent":["import type { NetworkClient } from \"@sudobility/types\";\nimport type { BaseResponse, ConvertResult } from \"@sudobility/svgr_types\";\n\n/**\n * Configuration for creating an {@link SvgrClient} instance.\n *\n * @interface SvgrClientConfig\n * @property {string} baseUrl - The base URL of the SVGR API server (e.g., `https://api.svgr.app`)\n * @property {NetworkClient} networkClient - A platform-agnostic HTTP client from `@sudobility/types`\n * that handles authentication headers and request serialization\n *\n * @example\n * ```typescript\n * const config: SvgrClientConfig = {\n * baseUrl: 'https://api.svgr.app',\n * networkClient: myNetworkClient,\n * };\n * const client = new SvgrClient(config);\n * ```\n */\nexport interface SvgrClientConfig {\n /** The base URL of the SVGR API server (e.g., `https://api.svgr.app`) */\n baseUrl: string;\n /** Platform-agnostic HTTP client that handles auth and serialization */\n networkClient: NetworkClient;\n /**\n * Optional retry configuration for failed requests.\n * When provided, the client will retry failed network requests\n * using exponential backoff.\n */\n retry?: RetryConfig;\n}\n\n/**\n * Configuration for automatic retry of failed network requests.\n *\n * @interface RetryConfig\n * @property {number} maxRetries - Maximum number of retry attempts (default: 3)\n * @property {number} baseDelayMs - Base delay in milliseconds before first retry (default: 1000).\n * Subsequent retries use exponential backoff: `baseDelayMs * 2^attempt`.\n * @property {number[]} [retryableStatuses] - HTTP status codes that should trigger a retry.\n * Defaults to [408, 429, 500, 502, 503, 504].\n *\n * @example\n * ```typescript\n * const retryConfig: RetryConfig = {\n * maxRetries: 3,\n * baseDelayMs: 1000,\n * retryableStatuses: [429, 500, 502, 503, 504],\n * };\n * ```\n */\nexport interface RetryConfig {\n /** Maximum number of retry attempts (default: 3) */\n maxRetries: number;\n /** Base delay in milliseconds before first retry (default: 1000) */\n baseDelayMs: number;\n /** HTTP status codes that should trigger a retry. Defaults to [408, 429, 500, 502, 503, 504] */\n retryableStatuses?: number[];\n}\n\n/** Default HTTP status codes considered retryable */\nconst DEFAULT_RETRYABLE_STATUSES = [408, 429, 500, 502, 503, 504];\n\n/**\n * Error thrown when the SVGR API returns a non-successful HTTP response.\n *\n * Extends the built-in `Error` class with an HTTP `status` code property,\n * allowing consumers to differentiate between different error types.\n *\n * @extends Error\n * @property {number} status - The HTTP status code from the API response\n * @property {string} message - Human-readable error description\n * @property {string} name - Always `\"SvgrApiError\"` for type identification\n *\n * @example\n * ```typescript\n * try {\n * await client.convert(imageData);\n * } catch (error) {\n * if (error instanceof SvgrApiError) {\n * console.error(`API error ${error.status}: ${error.message}`);\n * if (error.status === 429) {\n * // Handle rate limiting\n * }\n * }\n * }\n * ```\n */\nexport class SvgrApiError extends Error {\n constructor(\n /** The HTTP status code from the API response */\n public status: number,\n message: string,\n ) {\n super(message);\n this.name = \"SvgrApiError\";\n }\n}\n\n/**\n * HTTP client for the SVGR image-to-SVG conversion API.\n *\n * Provides a typed interface for calling the SVGR conversion endpoint.\n * Uses a platform-agnostic `NetworkClient` for HTTP communication,\n * making it usable in both web and React Native environments.\n *\n * @example\n * ```typescript\n * import { SvgrClient } from '@sudobility/svgr_client';\n *\n * const client = new SvgrClient({\n * baseUrl: 'https://api.svgr.app',\n * networkClient: myNetworkClient,\n * });\n *\n * const response = await client.convert(base64Image, 'logo.png', 7, true);\n * console.log(response.data?.svg);\n * ```\n *\n * @example\n * ```typescript\n * // With retry configuration\n * const client = new SvgrClient({\n * baseUrl: 'https://api.svgr.app',\n * networkClient: myNetworkClient,\n * retry: { maxRetries: 3, baseDelayMs: 1000 },\n * });\n * ```\n */\nexport class SvgrClient {\n private readonly baseUrl: string;\n private readonly networkClient: NetworkClient;\n private readonly retryConfig: RetryConfig | undefined;\n\n constructor(config: SvgrClientConfig) {\n this.baseUrl = config.baseUrl;\n this.networkClient = config.networkClient;\n this.retryConfig = config.retry;\n }\n\n /**\n * Convert a raster image to SVG format.\n *\n * Sends a POST request to `/api/v1/convert` with the image data and\n * optional conversion parameters. Returns a `BaseResponse<ConvertResult>`\n * containing the SVG string and original image dimensions on success.\n *\n * @param original - Base64-encoded raster image data (PNG, JPG, WEBP, BMP, or GIF)\n * @param filename - Optional filename for metadata or audit purposes\n * @param quality - Conversion quality level from 1 to 10 (1 = smallest, 10 = highest fidelity). Default: 5\n * @param transparentBg - If true, removes the background from the SVG. Default: false\n * @returns A promise resolving to a `BaseResponse<ConvertResult>` with the SVG output\n * @throws {SvgrApiError} When the API returns a non-successful response (includes HTTP status code)\n *\n * @example\n * ```typescript\n * const response = await client.convert(\n * 'data:image/png;base64,...',\n * 'logo.png',\n * 7,\n * true,\n * );\n * if (response.success && response.data) {\n * console.log(response.data.svg); // SVG string\n * console.log(response.data.width); // Original width in px\n * console.log(response.data.height); // Original height in px\n * }\n * ```\n */\n async convert(\n original: string,\n filename?: string,\n quality?: number,\n transparentBg?: boolean,\n ): Promise<BaseResponse<ConvertResult>> {\n const makeRequest = () =>\n this.networkClient.post<BaseResponse<ConvertResult>>(\n `${this.baseUrl}/api/v1/convert`,\n {\n original,\n filename,\n quality,\n transparentBg,\n },\n );\n\n const response = this.retryConfig\n ? await this.withRetry(makeRequest, this.retryConfig)\n : await makeRequest();\n\n if (!response.ok || !response.data) {\n const errorData = response.data as { error?: string } | null | undefined;\n throw new SvgrApiError(\n response.status,\n errorData?.error || \"Conversion failed\",\n );\n }\n\n return response.data;\n }\n\n /**\n * Execute a request function with exponential backoff retry.\n *\n * @param fn - The async function to retry\n * @param config - Retry configuration\n * @returns The result of the successful request\n * @throws The last error encountered after all retries are exhausted\n */\n private async withRetry<T>(\n fn: () => Promise<{ ok: boolean; status: number } & T>,\n config: RetryConfig,\n ): Promise<{ ok: boolean; status: number } & T> {\n const retryableStatuses =\n config.retryableStatuses ?? DEFAULT_RETRYABLE_STATUSES;\n\n let response = await fn();\n\n for (let attempt = 0; attempt < config.maxRetries; attempt++) {\n if (response.ok || !retryableStatuses.includes(response.status)) {\n return response;\n }\n\n const delay = config.baseDelayMs * Math.pow(2, attempt);\n await new Promise((resolve) => setTimeout(resolve, delay));\n\n response = await fn();\n }\n\n return response;\n }\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sudobility/svgr_client",
3
- "version": "1.0.11",
3
+ "version": "1.0.13",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -29,16 +29,16 @@
29
29
  "verify": "bun run typecheck && bun run lint && bun run test && bun run build"
30
30
  },
31
31
  "dependencies": {
32
- "@sudobility/svgr_types": "^1.0.6"
32
+ "@sudobility/svgr_types": "^1.0.7"
33
33
  },
34
34
  "peerDependencies": {
35
- "@sudobility/types": "^1.9.53",
35
+ "@sudobility/types": "^1.9.54",
36
36
  "react": "^18.0.0 || ^19.0.0",
37
37
  "@tanstack/react-query": "^5.0.0"
38
38
  },
39
39
  "devDependencies": {
40
- "@sudobility/di": "^1.5.38",
41
- "@sudobility/types": "^1.9.53",
40
+ "@sudobility/di": "^1.5.40",
41
+ "@sudobility/types": "^1.9.54",
42
42
  "@eslint/js": "^9.38.0",
43
43
  "@tanstack/react-query": "^5.0.0",
44
44
  "@types/react": "^19.0.0",