@next-feature/client 0.1.1 → 0.1.2-2

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.
@@ -0,0 +1,72 @@
1
+ import { AxiosInstance, AxiosRequestConfig } from 'axios';
2
+ import { ApiClientConfig } from './types/client';
3
+ /**
4
+ * Axios wrapper with JWT/Refresh token handling
5
+ */
6
+ export declare class ApiClient {
7
+ private readonly instance;
8
+ private isRefreshing;
9
+ private pendingRequests;
10
+ private config;
11
+ constructor(config: ApiClientConfig);
12
+ /**
13
+ * Setup request and response interceptors
14
+ */
15
+ private setupInterceptors;
16
+ /**
17
+ * Attach JWT token to request headers
18
+ */
19
+ private handleRequestFulfilled;
20
+ /**
21
+ * Handle request errors
22
+ */
23
+ private handleRequestRejected;
24
+ /**
25
+ * Pass through successful responses
26
+ */
27
+ private handleResponseFulfilled;
28
+ /**
29
+ * Check if a path should skip the refresh token logic
30
+ */
31
+ private shouldSkipRefresh;
32
+ /**
33
+ * Handle response errors with retry logic and token refresh
34
+ */
35
+ private handleResponseRejected;
36
+ /**
37
+ * Handle 401 errors with token refresh
38
+ */
39
+ private handleUnauthorizedError;
40
+ /**
41
+ * Refresh the JWT token using the refresh token
42
+ */
43
+ private refreshToken;
44
+ /**
45
+ * Process all pending requests after token refresh
46
+ */
47
+ private processPendingRequests;
48
+ /**
49
+ * Determine if request should be retried
50
+ */
51
+ private shouldRetry;
52
+ /**
53
+ * Retry failed request with exponential backoff
54
+ */
55
+ private retryRequest;
56
+ /**
57
+ * Sleep helper for retry delays
58
+ */
59
+ private sleep;
60
+ /**
61
+ * HTTP Methods with proper typing
62
+ */
63
+ get<T = any>(url: string, config?: AxiosRequestConfig): Promise<T>;
64
+ post<T = any>(url: string, data?: any, config?: AxiosRequestConfig): Promise<T>;
65
+ put<T = any>(url: string, data?: any, config?: AxiosRequestConfig): Promise<T>;
66
+ patch<T = any>(url: string, data?: any, config?: AxiosRequestConfig): Promise<T>;
67
+ delete<T = any>(url: string, config?: AxiosRequestConfig): Promise<T>;
68
+ /**
69
+ * Get the underlying Axios instance for advanced usage
70
+ */
71
+ getAxiosInstance(): AxiosInstance;
72
+ }
@@ -0,0 +1,20 @@
1
+ import { z } from 'zod';
2
+ declare const envSchema: z.ZodObject<{
3
+ NODE_ENV: z.ZodEnum<["development", "production"]>;
4
+ CLIENT_API_URL: z.ZodString;
5
+ }, "strip", z.ZodTypeAny, {
6
+ NODE_ENV?: "development" | "production";
7
+ CLIENT_API_URL?: string;
8
+ }, {
9
+ NODE_ENV?: "development" | "production";
10
+ CLIENT_API_URL?: string;
11
+ }>;
12
+ declare global {
13
+ namespace NodeJS {
14
+ interface ProcessEnv extends z.infer<typeof envSchema> {
15
+ }
16
+ }
17
+ }
18
+ export declare const NODE_ENV: "development" | "production" | "test";
19
+ export declare const BACKEND_API_URL: string;
20
+ export {};
package/lib/error.d.ts ADDED
@@ -0,0 +1,35 @@
1
+ import { HttpStatusCode } from 'axios';
2
+ import { ProblemDetail } from './types';
3
+ /**
4
+ * Custom error class for API errors
5
+ */
6
+ export declare class ApiError extends Error {
7
+ problemDetail: ProblemDetail;
8
+ originalError?: Error;
9
+ constructor(problemDetail: ProblemDetail, originalError?: Error);
10
+ get status(): HttpStatusCode;
11
+ get body(): ProblemDetail;
12
+ get isClientError(): boolean;
13
+ get isServerError(): boolean;
14
+ get isUnauthorized(): boolean;
15
+ get isForbidden(): boolean;
16
+ get isNotFound(): boolean;
17
+ static builder(): ApiErrorBuilder;
18
+ static of(error: Error | unknown): ApiError;
19
+ }
20
+ export declare class ApiErrorBuilder {
21
+ private _originalError;
22
+ private _problemDetail;
23
+ constructor(_originalError: Error | null);
24
+ /**
25
+ * Set standard ProblemDetail
26
+ */
27
+ problemDetail(problemDetail: ProblemDetail): ApiErrorBuilder;
28
+ originalError(error: Error): ApiErrorBuilder;
29
+ message(msg: string): ApiErrorBuilder;
30
+ status(status: HttpStatusCode): ApiErrorBuilder;
31
+ errors(errors: Record<string, string>): ApiErrorBuilder;
32
+ title(title: string): ApiErrorBuilder;
33
+ instance(instance: string): ApiErrorBuilder;
34
+ build(): ApiError;
35
+ }
@@ -0,0 +1,24 @@
1
+ import { InternalAxiosRequestConfig } from 'axios';
2
+ /**
3
+ * Configuration options for the API client
4
+ *
5
+ * [api-client-config]
6
+ * next-feature@0.1.1-beta.5
7
+ * January 11th 2026, 9:00:22 pm
8
+ */
9
+ export interface ApiClientConfig {
10
+ baseURL: string;
11
+ timeout?: number;
12
+ enableRefreshToken?: boolean;
13
+ maxRetries?: number;
14
+ retryDelay?: number;
15
+ /**
16
+ * Paths that should skip the refresh token logic on 401.
17
+ * Useful for login/register endpoints that expect 401 as a valid response.
18
+ */
19
+ skipRefreshPaths?: RegExp[];
20
+ onAuthenticated?: (config: InternalAxiosRequestConfig) => void | Promise<void>;
21
+ onUnauthorized?: () => void | Promise<void>;
22
+ onRefreshTokenExpired?: () => void | Promise<void>;
23
+ onRefreshToken?: () => string | Promise<string>;
24
+ }
@@ -0,0 +1,27 @@
1
+ import { HttpStatusCode } from 'axios';
2
+ /**
3
+ * [api-response]
4
+ * next-feature@0.0.11-beta
5
+ * November 4th 2025, 6:37:27 pm
6
+ */
7
+ export interface ApiResponse<Response> {
8
+ success?: boolean;
9
+ message?: string;
10
+ error?: ProblemDetail;
11
+ data: Response;
12
+ }
13
+ /**
14
+ * Spring Boot ProblemDetail structure
15
+ *
16
+ * [problem-detail]
17
+ * next-feature@0.1.1-beta.5
18
+ * January 11th 2026, 8:55:50 pm
19
+ */
20
+ export interface ProblemDetail {
21
+ type: string;
22
+ title: string;
23
+ status: HttpStatusCode;
24
+ detail: string;
25
+ instance?: string;
26
+ errors?: Record<string, string>;
27
+ }
@@ -0,0 +1,7 @@
1
+ import { AxiosError } from 'axios';
2
+ /**
3
+ * [handle-axios-error]
4
+ * next-feature@0.1.1-beta.5
5
+ * January 11th 2026, 9:37:55 pm
6
+ */
7
+ export declare function handleAxiosError(e: AxiosError): import('../error').ApiError;
@@ -0,0 +1,19 @@
1
+ import { ApiError } from '../error';
2
+ /**
3
+ * Extract user-friendly error message from ApiError
4
+ *
5
+ * [get-error-message]
6
+ * next-feature@0.0.11-beta
7
+ * November 4th 2025, 11:47:45 am
8
+ *
9
+ */
10
+ export declare function getErrorMessage(error: unknown): string;
11
+ /**
12
+ * Check if error is a specific HTTP status
13
+ */
14
+ export declare function isHttpStatus(error: unknown, status: number): boolean;
15
+ /**
16
+ * Handle common API errors
17
+ */
18
+ declare function handleApiError(error: unknown): ApiError;
19
+ export default handleApiError;
@@ -0,0 +1,10 @@
1
+ import { ZodError } from 'zod';
2
+ import { ApiError } from '../error';
3
+ /**
4
+ * Create ApiError from Zod validation error
5
+ *
6
+ * [handle-zod-error]
7
+ * next-feature@0.1.1-beta.5
8
+ * January 11th 2026, 9:40:46 pm
9
+ */
10
+ export declare function handleZodError(zodError: ZodError): ApiError;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@next-feature/client",
3
- "version": "0.1.1",
3
+ "version": "0.1.2-2",
4
4
  "main": "./index.js",
5
5
  "types": "./index.d.ts",
6
6
  "exports": {
package/server.d.ts ADDED
File without changes
package/server.js ADDED
@@ -0,0 +1 @@
1
+
package/.babelrc DELETED
@@ -1,12 +0,0 @@
1
- {
2
- "presets": [
3
- [
4
- "@nx/react/babel",
5
- {
6
- "runtime": "automatic",
7
- "useBuiltIns": "usage"
8
- }
9
- ]
10
- ],
11
- "plugins": []
12
- }
package/eslint.config.mjs DELETED
@@ -1,12 +0,0 @@
1
- import nx from '@nx/eslint-plugin';
2
- import baseConfig from '../../eslint.config.mjs';
3
-
4
- export default [
5
- ...baseConfig,
6
- ...nx.configs['flat/react'],
7
- {
8
- files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'],
9
- // Override or add rules here
10
- rules: {},
11
- },
12
- ];
package/jest.config.cts DELETED
@@ -1,10 +0,0 @@
1
- module.exports = {
2
- displayName: 'client',
3
- preset: '../../jest.preset.js',
4
- transform: {
5
- '^(?!.*\\.(js|jsx|ts|tsx|css|json)$)': '@nx/react/plugins/jest',
6
- '^.+\\.[tj]sx?$': ['babel-jest', { presets: ['@nx/react/babel'] }],
7
- },
8
- moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],
9
- coverageDirectory: '../../coverage/clients/client',
10
- };
package/project.json DELETED
@@ -1,32 +0,0 @@
1
- {
2
- "name": "client",
3
- "$schema": "../../node_modules/nx/schemas/project-schema.json",
4
- "sourceRoot": "clients/client/src",
5
- "projectType": "library",
6
- "tags": [],
7
- "targets": {
8
- "build": {
9
- "executor": "@nx/vite:build",
10
- "outputs": ["{options.outputPath}"],
11
- "defaultConfiguration": "production",
12
- "options": {
13
- "outputPath": "dist/clients/client"
14
- },
15
- "configurations": {
16
- "development": {
17
- "mode": "development"
18
- },
19
- "production": {
20
- "mode": "production"
21
- }
22
- }
23
- },
24
- "test": {
25
- "executor": "@nx/jest:jest",
26
- "outputs": ["{workspaceRoot}/coverage/{projectRoot}"],
27
- "options": {
28
- "jestConfig": "clients/client/jest.config.cts"
29
- }
30
- }
31
- }
32
- }
@@ -1,58 +0,0 @@
1
- 'use client';
2
-
3
- import React, { Component, ErrorInfo, ReactNode } from 'react';
4
- import { ApiError } from '../lib/error';
5
- import { getErrorMessage } from '../lib/utils/error';
6
-
7
- interface Props {
8
- children: ReactNode;
9
- fallback?: (error: ApiError) => ReactNode;
10
- onError?: (error: Error, errorInfo: ErrorInfo) => void;
11
- }
12
-
13
- interface State {
14
- hasError: boolean;
15
- error: Error | null;
16
- }
17
-
18
- export class ApiErrorBoundary extends Component<Props, State> {
19
- constructor(props: Props) {
20
- super(props);
21
- this.state = { hasError: false, error: null };
22
- }
23
-
24
- static getDerivedStateFromError(error: Error): State {
25
- return { hasError: true, error };
26
- }
27
-
28
- componentDidCatch(error: Error, errorInfo: ErrorInfo) {
29
- console.error('ApiErrorBoundary caught error:', error, errorInfo);
30
-
31
- if (this.props.onError) {
32
- this.props.onError(error, errorInfo);
33
- }
34
- }
35
-
36
- render() {
37
- if (this.state.hasError && this.state.error) {
38
- if (this.state.error instanceof ApiError && this.props.fallback) {
39
- return this.props.fallback(this.state.error);
40
- }
41
-
42
- // Default error UI
43
- return (
44
- <div className="error-container">
45
- <h2>Something went wrong</h2>
46
- <p>{getErrorMessage(this.state.error)}</p>
47
- <button
48
- onClick={() => this.setState({ hasError: false, error: null })}
49
- >
50
- Try again
51
- </button>
52
- </div>
53
- );
54
- }
55
-
56
- return this.props.children;
57
- }
58
- }
@@ -1,39 +0,0 @@
1
- 'use client';
2
-
3
- import { useState, useCallback } from 'react';
4
- import { ApiError } from '../lib/error';
5
- import { getErrorMessage } from '../lib/utils/error';
6
-
7
- interface UseApiErrorResult {
8
- error: ApiError | null;
9
- setError: (error: ApiError | null) => void;
10
- clearError: () => void;
11
- handleError: (error: unknown) => void;
12
- errorMessage: string | null;
13
- }
14
-
15
- export function useApiError(): UseApiErrorResult {
16
- const [error, setError] = useState<ApiError | null>(null);
17
-
18
- const clearError = useCallback(() => {
19
- setError(null);
20
- }, []);
21
-
22
- const handleError = useCallback((err: unknown) => {
23
- if (err instanceof ApiError) {
24
- setError(err);
25
- } else {
26
- console.error('Non-API error:', err);
27
- }
28
- }, []);
29
-
30
- const errorMessage = error ? getErrorMessage(error) : null;
31
-
32
- return {
33
- error,
34
- setError,
35
- clearError,
36
- handleError,
37
- errorMessage,
38
- };
39
- }