@vahidkaargar/customized-api-client 0.2.0
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 +639 -0
- package/dist/index.cjs +1171 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +317 -0
- package/dist/index.d.ts +317 -0
- package/dist/index.js +1087 -0
- package/dist/index.js.map +1 -0
- package/package.json +73 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
import { AxiosRequestConfig, InternalAxiosRequestConfig, AxiosResponse, AxiosInstance } from 'axios';
|
|
2
|
+
|
|
3
|
+
/** Mode B (default): `baseURL` already includes `/api/v1`. Mode A: origin only; client prefixes `/api/v1`. */
|
|
4
|
+
type BaseUrlMode = 'modeB' | 'modeA';
|
|
5
|
+
type AuthConfig = {
|
|
6
|
+
readonly type: 'bearer';
|
|
7
|
+
readonly getToken: TokenProvider;
|
|
8
|
+
} | {
|
|
9
|
+
readonly type: 'partner-bearer';
|
|
10
|
+
readonly getSecret: TokenProvider;
|
|
11
|
+
};
|
|
12
|
+
type TokenProvider = () => string | null | undefined | Promise<string | null | undefined>;
|
|
13
|
+
type TransformResponseKeysMode = 'none' | 'camelCase-attributes-meta';
|
|
14
|
+
interface RetryOptions {
|
|
15
|
+
readonly maxAttempts?: number;
|
|
16
|
+
readonly baseDelayMs?: number;
|
|
17
|
+
readonly maxDelayMs?: number;
|
|
18
|
+
readonly jitterRatio?: number;
|
|
19
|
+
}
|
|
20
|
+
interface IdempotencyReplayContext {
|
|
21
|
+
readonly url?: string;
|
|
22
|
+
readonly method?: string;
|
|
23
|
+
}
|
|
24
|
+
interface ApiClientConfig {
|
|
25
|
+
readonly baseURL: string;
|
|
26
|
+
/** Default Mode B — see `BaseUrlMode`. */
|
|
27
|
+
readonly baseUrlMode?: BaseUrlMode;
|
|
28
|
+
readonly auth?: AuthConfig;
|
|
29
|
+
readonly getAcceptLanguage?: () => string | null | undefined | Promise<string | null | undefined>;
|
|
30
|
+
readonly defaultHeaders?: Readonly<Record<string, string>>;
|
|
31
|
+
readonly timeout?: number;
|
|
32
|
+
readonly retry?: RetryOptions;
|
|
33
|
+
readonly generateIdempotencyKey?: () => string;
|
|
34
|
+
readonly onIdempotencyReplay?: (ctx: Readonly<IdempotencyReplayContext>) => void;
|
|
35
|
+
readonly onUnauthorized?: (error: unknown) => void | Promise<void>;
|
|
36
|
+
readonly onDeprecated?: (info: Readonly<DeprecationInfo>) => void;
|
|
37
|
+
readonly transformResponseKeys?: TransformResponseKeysMode;
|
|
38
|
+
readonly maxBodyLogLength?: number;
|
|
39
|
+
}
|
|
40
|
+
interface DeprecationInfo {
|
|
41
|
+
readonly deprecation?: string;
|
|
42
|
+
readonly sunset?: string;
|
|
43
|
+
readonly rawHeaders: Readonly<Record<string, string>>;
|
|
44
|
+
}
|
|
45
|
+
declare const DEFAULT_TIMEOUT_MS = 30000;
|
|
46
|
+
declare const DEFAULT_PAGE_SIZE_CAP = 100;
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Minimal JSON:API wire shapes used by the client (snake_case on wire).
|
|
50
|
+
*/
|
|
51
|
+
interface JsonApiResourceLinkage {
|
|
52
|
+
readonly type: string;
|
|
53
|
+
readonly id: string;
|
|
54
|
+
}
|
|
55
|
+
interface JsonApiResourceObject {
|
|
56
|
+
readonly type: string;
|
|
57
|
+
readonly id: string;
|
|
58
|
+
readonly attributes?: Record<string, unknown>;
|
|
59
|
+
readonly relationships?: Record<string, unknown>;
|
|
60
|
+
readonly meta?: Record<string, unknown>;
|
|
61
|
+
readonly links?: Record<string, string | null | undefined>;
|
|
62
|
+
}
|
|
63
|
+
type JsonApiPrimaryData = JsonApiResourceObject | readonly JsonApiResourceObject[] | null;
|
|
64
|
+
interface JsonApiDocument<TData extends JsonApiPrimaryData = JsonApiPrimaryData> {
|
|
65
|
+
readonly data: TData;
|
|
66
|
+
readonly included?: readonly JsonApiResourceObject[];
|
|
67
|
+
readonly meta?: Readonly<Record<string, unknown>>;
|
|
68
|
+
readonly links?: Readonly<Record<string, string | null | undefined>>;
|
|
69
|
+
}
|
|
70
|
+
interface JsonApiErrorObject {
|
|
71
|
+
readonly id?: string;
|
|
72
|
+
readonly status?: string;
|
|
73
|
+
readonly code?: string;
|
|
74
|
+
readonly title?: string;
|
|
75
|
+
readonly detail?: string;
|
|
76
|
+
readonly source?: {
|
|
77
|
+
readonly pointer?: string;
|
|
78
|
+
readonly parameter?: string;
|
|
79
|
+
};
|
|
80
|
+
readonly meta?: Readonly<Record<string, unknown>>;
|
|
81
|
+
}
|
|
82
|
+
interface JsonApiErrorDocument {
|
|
83
|
+
readonly errors: readonly JsonApiErrorObject[];
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/** Normalized subset of interesting response headers */
|
|
87
|
+
interface NormalizedResponseHeaders {
|
|
88
|
+
readonly etag?: string;
|
|
89
|
+
readonly contentLanguage?: string;
|
|
90
|
+
readonly idempotentReplayed: boolean;
|
|
91
|
+
readonly retryAfterSeconds?: number;
|
|
92
|
+
}
|
|
93
|
+
interface JsonApiSuccessBody {
|
|
94
|
+
readonly kind: 'jsonapi-success';
|
|
95
|
+
readonly status: 200 | 201;
|
|
96
|
+
readonly headers: NormalizedResponseHeaders;
|
|
97
|
+
readonly document: JsonApiDocument;
|
|
98
|
+
}
|
|
99
|
+
interface NoContentBody {
|
|
100
|
+
readonly kind: 'no-content';
|
|
101
|
+
readonly status: 204;
|
|
102
|
+
readonly headers: NormalizedResponseHeaders;
|
|
103
|
+
}
|
|
104
|
+
interface AcceptedBody {
|
|
105
|
+
readonly kind: 'accepted';
|
|
106
|
+
readonly status: 202;
|
|
107
|
+
readonly location: string;
|
|
108
|
+
readonly rawBody?: unknown;
|
|
109
|
+
readonly headers: NormalizedResponseHeaders;
|
|
110
|
+
}
|
|
111
|
+
interface MultiStatusItem {
|
|
112
|
+
readonly httpStatus: number;
|
|
113
|
+
readonly body?: unknown;
|
|
114
|
+
}
|
|
115
|
+
interface MultiStatusBody {
|
|
116
|
+
readonly kind: 'multi-status';
|
|
117
|
+
readonly status: 207;
|
|
118
|
+
readonly items: readonly MultiStatusItem[];
|
|
119
|
+
readonly headers: NormalizedResponseHeaders;
|
|
120
|
+
}
|
|
121
|
+
type ClientSuccess = JsonApiSuccessBody | NoContentBody | AcceptedBody | MultiStatusBody;
|
|
122
|
+
interface OkResult<T extends ClientSuccess> {
|
|
123
|
+
readonly ok: true;
|
|
124
|
+
readonly value: T;
|
|
125
|
+
}
|
|
126
|
+
interface ErrResult<E> {
|
|
127
|
+
readonly ok: false;
|
|
128
|
+
readonly error: E;
|
|
129
|
+
}
|
|
130
|
+
type Result<T extends ClientSuccess, E> = OkResult<T> | ErrResult<E>;
|
|
131
|
+
|
|
132
|
+
declare class ApiClientError extends Error {
|
|
133
|
+
#private;
|
|
134
|
+
readonly status: number;
|
|
135
|
+
readonly errors: readonly JsonApiErrorObject[];
|
|
136
|
+
readonly primaryCode?: string | undefined;
|
|
137
|
+
readonly responseHeaders?: Readonly<Record<string, string>> | undefined;
|
|
138
|
+
readonly retryAfterSeconds?: number | undefined;
|
|
139
|
+
readonly requestMethod?: string | undefined;
|
|
140
|
+
readonly name = "ApiClientError";
|
|
141
|
+
constructor(status: number, errors: readonly JsonApiErrorObject[], primaryCode?: string | undefined, responseHeaders?: Readonly<Record<string, string>> | undefined, retryAfterSeconds?: number | undefined, requestMethod?: string | undefined, options?: ErrorOptions);
|
|
142
|
+
toJSON(): Record<string, unknown>;
|
|
143
|
+
}
|
|
144
|
+
declare function isApiClientError(value: unknown): value is ApiClientError;
|
|
145
|
+
|
|
146
|
+
interface RequestCallOptions {
|
|
147
|
+
readonly idempotencyKey?: string;
|
|
148
|
+
readonly ifMatchVersion?: number;
|
|
149
|
+
}
|
|
150
|
+
interface ApiClient {
|
|
151
|
+
readonly get: (path: string, opts?: RequestCallOptions) => Promise<ClientSuccess>;
|
|
152
|
+
readonly head: (path: string, opts?: RequestCallOptions) => Promise<ClientSuccess>;
|
|
153
|
+
readonly post: (path: string, data?: unknown, opts?: RequestCallOptions) => Promise<ClientSuccess>;
|
|
154
|
+
readonly patch: (path: string, data?: unknown, opts?: RequestCallOptions) => Promise<ClientSuccess>;
|
|
155
|
+
readonly put: (path: string, data?: unknown, opts?: RequestCallOptions) => Promise<ClientSuccess>;
|
|
156
|
+
readonly delete: (path: string, opts?: RequestCallOptions) => Promise<ClientSuccess>;
|
|
157
|
+
readonly request: (ax: AxiosRequestConfig, opts?: RequestCallOptions) => Promise<ClientSuccess>;
|
|
158
|
+
readonly getByUrl: (fullUrl: string, opts?: RequestCallOptions) => Promise<ClientSuccess>;
|
|
159
|
+
readonly patchWithVersion: (path: string, data: unknown, version: number, opts?: Omit<RequestCallOptions, 'ifMatchVersion'>) => Promise<ClientSuccess>;
|
|
160
|
+
readonly safeGet: (path: string, opts?: RequestCallOptions) => Promise<Result<ClientSuccess, ApiClientError>>;
|
|
161
|
+
readonly safePost: (path: string, data?: unknown, opts?: RequestCallOptions) => Promise<Result<ClientSuccess, ApiClientError>>;
|
|
162
|
+
readonly safePatch: (path: string, data?: unknown, opts?: RequestCallOptions) => Promise<Result<ClientSuccess, ApiClientError>>;
|
|
163
|
+
readonly safePut: (path: string, data?: unknown, opts?: RequestCallOptions) => Promise<Result<ClientSuccess, ApiClientError>>;
|
|
164
|
+
readonly safeDelete: (path: string, opts?: RequestCallOptions) => Promise<Result<ClientSuccess, ApiClientError>>;
|
|
165
|
+
readonly safeHead: (path: string, opts?: RequestCallOptions) => Promise<Result<ClientSuccess, ApiClientError>>;
|
|
166
|
+
readonly safeRequest: (ax: AxiosRequestConfig, opts?: RequestCallOptions) => Promise<Result<ClientSuccess, ApiClientError>>;
|
|
167
|
+
}
|
|
168
|
+
declare function createApiClient(config: ApiClientConfig): ApiClient;
|
|
169
|
+
|
|
170
|
+
declare function isAuthenticationError(e: unknown): boolean;
|
|
171
|
+
declare function isForbiddenError(e: unknown): boolean;
|
|
172
|
+
declare function isPreconditionRequiredError(e: unknown): boolean;
|
|
173
|
+
declare function isPreconditionFailedError(e: unknown): boolean;
|
|
174
|
+
declare function isValidationError(e: unknown): boolean;
|
|
175
|
+
declare function isConflictError(e: unknown): boolean;
|
|
176
|
+
declare function isPayloadTooLargeError(e: unknown): boolean;
|
|
177
|
+
declare function isRetryablePerPolicy(e: unknown): boolean;
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Truncate a value for safe logging (respects `maxBodyLogLength` from config when passed).
|
|
181
|
+
*/
|
|
182
|
+
declare function truncateForLog(body: unknown, maxLen?: number): string;
|
|
183
|
+
/**
|
|
184
|
+
* Redact sensitive header values for logging and error surfaces.
|
|
185
|
+
*/
|
|
186
|
+
declare function redactHeaderRecord(headers: Record<string, string>): Record<string, string>;
|
|
187
|
+
|
|
188
|
+
declare function applyJsonApiHeaders(config: InternalAxiosRequestConfig, method: string): InternalAxiosRequestConfig;
|
|
189
|
+
|
|
190
|
+
declare const IDEMPOTENCY_MAX_LENGTH = 64;
|
|
191
|
+
declare function defaultIdempotencyKey(): string;
|
|
192
|
+
declare function assertValidIdempotencyKey(key: string): void;
|
|
193
|
+
declare function isMutationMethod(method: string): boolean;
|
|
194
|
+
|
|
195
|
+
declare function formatIfMatch(version: number): string;
|
|
196
|
+
|
|
197
|
+
declare function resolveAcceptLanguage(provider?: () => string | null | undefined | Promise<string | null | undefined>): Promise<string | undefined>;
|
|
198
|
+
|
|
199
|
+
declare function resolveAuthorizationHeader(auth: AuthConfig | undefined): Promise<string | undefined>;
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Path for axios `url` (combined with `baseURL`). No duplicate `/api/v1`, no `//`.
|
|
203
|
+
*/
|
|
204
|
+
declare function resolveResourcePath(baseURL: string, path: string, mode: BaseUrlMode): string;
|
|
205
|
+
/**
|
|
206
|
+
* Prepare request URL for axios — if `fullUrl` is absolute http(s), return as-is for getByUrl.
|
|
207
|
+
*/
|
|
208
|
+
declare function normalizeHttpUrl(fullUrl: string): string;
|
|
209
|
+
|
|
210
|
+
declare function parseJsonApiDocument(payload: unknown): JsonApiDocument;
|
|
211
|
+
|
|
212
|
+
declare function parseJsonApiErrorBody(status: number, rawBody: unknown, headers: Readonly<Record<string, string>>, requestMethod?: string): ApiClientError;
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Best-effort normalization for 207 bodies — structure varies by backend.
|
|
216
|
+
*/
|
|
217
|
+
declare function parseMultiStatusBody(raw: unknown): readonly MultiStatusItem[];
|
|
218
|
+
|
|
219
|
+
type HeadersLike = Headers | Record<string, string | undefined>;
|
|
220
|
+
declare function flattenAxiosHeaders(headers: unknown): Record<string, string>;
|
|
221
|
+
declare function getHeader(headers: Record<string, string>, name: string): string | undefined;
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Resolve absolute Location for 202 per project-plan (relative to response URL when needed).
|
|
225
|
+
*/
|
|
226
|
+
declare function resolveAcceptedLocation(headers: HeadersLike, requestUrl: string): string;
|
|
227
|
+
|
|
228
|
+
interface OffsetPagination {
|
|
229
|
+
readonly kind: 'offset';
|
|
230
|
+
readonly page: number;
|
|
231
|
+
readonly totalPages?: number;
|
|
232
|
+
readonly total?: number;
|
|
233
|
+
}
|
|
234
|
+
interface CursorPagination {
|
|
235
|
+
readonly kind: 'cursor';
|
|
236
|
+
readonly hasMore: boolean;
|
|
237
|
+
readonly nextCursor?: string;
|
|
238
|
+
}
|
|
239
|
+
interface UnknownPagination {
|
|
240
|
+
readonly kind: 'unknown';
|
|
241
|
+
}
|
|
242
|
+
declare function parsePaginationKind(meta: Readonly<Record<string, unknown>> | undefined, links: Readonly<Record<string, string | null | undefined>> | undefined): OffsetPagination | CursorPagination | UnknownPagination;
|
|
243
|
+
declare function getNextPageUrl(links: Readonly<Record<string, string | null | undefined>> | undefined): string | undefined;
|
|
244
|
+
|
|
245
|
+
interface NormalizeOptions {
|
|
246
|
+
readonly transformResponseKeys?: TransformResponseKeysMode;
|
|
247
|
+
readonly requestUrl: string;
|
|
248
|
+
readonly requestMethod?: string;
|
|
249
|
+
}
|
|
250
|
+
declare function normalizeAxiosResponse(response: AxiosResponse<unknown>, options: NormalizeOptions): ClientSuccess;
|
|
251
|
+
|
|
252
|
+
interface RetryPolicyContext {
|
|
253
|
+
readonly method: string;
|
|
254
|
+
readonly status?: number;
|
|
255
|
+
/** First JSON:API error `code` when present */
|
|
256
|
+
readonly primaryErrorCode?: string;
|
|
257
|
+
readonly isNetworkError: boolean;
|
|
258
|
+
}
|
|
259
|
+
/** Pure policy per [.cursor/tasks/project-plan.md §7](../tasks/project-plan.md). */
|
|
260
|
+
declare function retryAllowed(ctx: RetryPolicyContext): boolean;
|
|
261
|
+
|
|
262
|
+
/** Parse Retry-After header: delta-seconds or HTTP-date. */
|
|
263
|
+
declare function parseRetryAfterSeconds(value: string | undefined): number | undefined;
|
|
264
|
+
|
|
265
|
+
interface DispatchOptions {
|
|
266
|
+
readonly retry?: RetryOptions;
|
|
267
|
+
readonly getPrimaryErrorCodeFromBody?: (body: unknown) => string | undefined;
|
|
268
|
+
}
|
|
269
|
+
declare function dispatchWithRetry<TData = unknown>(instance: AxiosInstance, config: AxiosRequestConfig, options: DispatchOptions): Promise<AxiosResponse<TData>>;
|
|
270
|
+
|
|
271
|
+
interface JsonApiQueryInput {
|
|
272
|
+
readonly filter?: Readonly<Record<string, string | number | boolean | undefined>>;
|
|
273
|
+
readonly sort?: readonly string[];
|
|
274
|
+
readonly fields?: Readonly<Record<string, readonly string[]>>;
|
|
275
|
+
readonly include?: readonly string[];
|
|
276
|
+
}
|
|
277
|
+
declare function buildJsonApiQuery(input: JsonApiQueryInput): Record<string, string>;
|
|
278
|
+
declare function buildOffsetPageParams(input: {
|
|
279
|
+
readonly number: number;
|
|
280
|
+
readonly size: number;
|
|
281
|
+
}): Record<string, string | number>;
|
|
282
|
+
declare function buildCursorPageParams(input: {
|
|
283
|
+
readonly cursor: string;
|
|
284
|
+
readonly size: number;
|
|
285
|
+
}): Record<string, string | number>;
|
|
286
|
+
|
|
287
|
+
type IncludedIndex = ReadonlyMap<string, JsonApiResourceObject>;
|
|
288
|
+
declare function indexIncluded(included: readonly JsonApiResourceObject[] | undefined): IncludedIndex;
|
|
289
|
+
declare function resolveIncluded(ref: JsonApiResourceLinkage, index: IncludedIndex): JsonApiResourceObject | undefined;
|
|
290
|
+
|
|
291
|
+
declare function readResourceVersion(resource: JsonApiResourceObject, etagHeader?: string): number | undefined;
|
|
292
|
+
declare function etagFromResponseHeaders(headerBag: unknown): string | undefined;
|
|
293
|
+
|
|
294
|
+
type ValidationGroups = Record<string, readonly string[]>;
|
|
295
|
+
declare function groupValidationErrorsByPointer(errors: readonly JsonApiErrorObject[]): ValidationGroups;
|
|
296
|
+
|
|
297
|
+
declare function createHealthCheck(target: ApiClientConfig | Pick<HealthGettable, 'get'>): () => Promise<boolean>;
|
|
298
|
+
interface HealthGettable {
|
|
299
|
+
get: (path: string) => Promise<unknown>;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
declare function parseDeprecationHeaders(headerBag: unknown): DeprecationInfo | null;
|
|
303
|
+
|
|
304
|
+
/** Shallow camelCase keys for `attributes` and `meta` only — copies document where needed. */
|
|
305
|
+
declare function applyTransformKeys(doc: JsonApiDocument, mode: TransformResponseKeysMode): JsonApiDocument;
|
|
306
|
+
|
|
307
|
+
interface PollOptions {
|
|
308
|
+
readonly maxAttempts?: number;
|
|
309
|
+
readonly delayMs?: number;
|
|
310
|
+
}
|
|
311
|
+
declare function pollAsyncResult(client: ApiClient, initial: Extract<ClientSuccess, {
|
|
312
|
+
kind: 'accepted';
|
|
313
|
+
}>, options?: PollOptions): Promise<ClientSuccess>;
|
|
314
|
+
|
|
315
|
+
declare const PACKAGE_VERSION: string;
|
|
316
|
+
|
|
317
|
+
export { type AcceptedBody, type ApiClient, type ApiClientConfig, ApiClientError, type AuthConfig, type BaseUrlMode, type ClientSuccess, type CursorPagination, DEFAULT_PAGE_SIZE_CAP, DEFAULT_TIMEOUT_MS, type DeprecationInfo, type ErrResult, IDEMPOTENCY_MAX_LENGTH, type IdempotencyReplayContext, type IncludedIndex, type JsonApiDocument, type JsonApiErrorDocument, type JsonApiErrorObject, type JsonApiPrimaryData, type JsonApiQueryInput, type JsonApiResourceLinkage, type JsonApiResourceObject, type JsonApiSuccessBody, type MultiStatusBody, type MultiStatusItem, type NoContentBody, type NormalizedResponseHeaders, type OffsetPagination, type OkResult, PACKAGE_VERSION, type PollOptions, type RequestCallOptions, type Result, type RetryOptions, type TokenProvider, type TransformResponseKeysMode, type UnknownPagination, type ValidationGroups, applyJsonApiHeaders, applyTransformKeys, assertValidIdempotencyKey, buildCursorPageParams, buildJsonApiQuery, buildOffsetPageParams, createApiClient, createHealthCheck, defaultIdempotencyKey, dispatchWithRetry, etagFromResponseHeaders, flattenAxiosHeaders, formatIfMatch, getHeader, getNextPageUrl, groupValidationErrorsByPointer, indexIncluded, isApiClientError, isAuthenticationError, isConflictError, isForbiddenError, isMutationMethod, isPayloadTooLargeError, isPreconditionFailedError, isPreconditionRequiredError, isRetryablePerPolicy, isValidationError, normalizeAxiosResponse, normalizeHttpUrl, parseDeprecationHeaders, parseJsonApiDocument, parseJsonApiErrorBody, parseMultiStatusBody, parsePaginationKind, parseRetryAfterSeconds, pollAsyncResult, readResourceVersion, redactHeaderRecord, resolveAcceptLanguage, resolveAcceptedLocation, resolveAuthorizationHeader, resolveIncluded, resolveResourcePath, retryAllowed, truncateForLog };
|