@hardlydifficult/rest-client 1.0.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.
@@ -0,0 +1,18 @@
1
+ import type { AuthConfig, RestClientLogger } from "./types";
2
+ /** Manages token lifecycle for all supported auth strategies. */
3
+ export declare class AuthenticationManager {
4
+ private readonly authConfig;
5
+ private readonly logger?;
6
+ private bearerToken;
7
+ private tokenExpiry;
8
+ private tokenIssuedAt;
9
+ constructor(authConfig: AuthConfig, logger?: RestClientLogger | undefined);
10
+ authenticate(): Promise<string>;
11
+ clearToken(): void;
12
+ getTokenExpiryTime(): number | null;
13
+ getTokenIssuedAt(): number | null;
14
+ getTokenLifetimeMs(): number | null;
15
+ private authenticateOAuth2;
16
+ private isTokenValid;
17
+ }
18
+ //# sourceMappingURL=AuthenticationManager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AuthenticationManager.d.ts","sourceRoot":"","sources":["../src/AuthenticationManager.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAAc,gBAAgB,EAAE,MAAM,SAAS,CAAC;AASxE,iEAAiE;AACjE,qBAAa,qBAAqB;IAM9B,OAAO,CAAC,QAAQ,CAAC,UAAU;IAC3B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC;IAN1B,OAAO,CAAC,WAAW,CAAuB;IAC1C,OAAO,CAAC,WAAW,CAAuB;IAC1C,OAAO,CAAC,aAAa,CAAuB;gBAGzB,UAAU,EAAE,UAAU,EACtB,MAAM,CAAC,EAAE,gBAAgB,YAAA;IAGtC,YAAY,IAAI,OAAO,CAAC,MAAM,CAAC;IA+BrC,UAAU,IAAI,IAAI;IAMlB,kBAAkB,IAAI,MAAM,GAAG,IAAI;IAInC,gBAAgB,IAAI,MAAM,GAAG,IAAI;IAIjC,kBAAkB,IAAI,MAAM,GAAG,IAAI;YAOrB,kBAAkB;IA4FhC,OAAO,CAAC,YAAY;CAerB"}
@@ -0,0 +1,134 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.AuthenticationManager = void 0;
7
+ const axios_1 = __importDefault(require("axios"));
8
+ const errors_1 = require("./errors");
9
+ /** Manages token lifecycle for all supported auth strategies. */
10
+ class AuthenticationManager {
11
+ authConfig;
12
+ logger;
13
+ bearerToken = null;
14
+ tokenExpiry = null;
15
+ tokenIssuedAt = null;
16
+ constructor(authConfig, logger) {
17
+ this.authConfig = authConfig;
18
+ this.logger = logger;
19
+ }
20
+ async authenticate() {
21
+ if (this.isTokenValid() && this.bearerToken !== null) {
22
+ return this.bearerToken;
23
+ }
24
+ switch (this.authConfig.type) {
25
+ case "none":
26
+ return "";
27
+ case "bearer":
28
+ this.bearerToken = this.authConfig.token;
29
+ return this.bearerToken;
30
+ case "generator": {
31
+ this.bearerToken = await this.authConfig.generate();
32
+ this.tokenIssuedAt = Date.now();
33
+ this.tokenExpiry =
34
+ this.tokenIssuedAt + (this.authConfig.cacheDurationMs ?? 60_000);
35
+ return this.bearerToken;
36
+ }
37
+ case "oauth2":
38
+ return this.authenticateOAuth2(this.authConfig);
39
+ default:
40
+ throw new errors_1.AuthenticationError(`Unsupported auth type: ${this.authConfig.type}`);
41
+ }
42
+ }
43
+ clearToken() {
44
+ this.bearerToken = null;
45
+ this.tokenExpiry = null;
46
+ this.tokenIssuedAt = null;
47
+ }
48
+ getTokenExpiryTime() {
49
+ return this.tokenExpiry;
50
+ }
51
+ getTokenIssuedAt() {
52
+ return this.tokenIssuedAt;
53
+ }
54
+ getTokenLifetimeMs() {
55
+ if (this.tokenIssuedAt === null || this.tokenExpiry === null) {
56
+ return null;
57
+ }
58
+ return this.tokenExpiry - this.tokenIssuedAt;
59
+ }
60
+ async authenticateOAuth2(config) {
61
+ const grantType = config.grantType ?? "client_credentials";
62
+ const formData = new URLSearchParams();
63
+ formData.append("grant_type", grantType);
64
+ formData.append("client_id", config.clientId);
65
+ if (grantType === "client_credentials" &&
66
+ config.clientSecret !== undefined &&
67
+ config.clientSecret !== "") {
68
+ formData.append("client_secret", config.clientSecret);
69
+ }
70
+ if (config.audience !== undefined && config.audience !== "") {
71
+ formData.append("audience", config.audience);
72
+ }
73
+ if (config.scope !== undefined && config.scope !== "") {
74
+ formData.append("scope", config.scope);
75
+ }
76
+ if (grantType === "password") {
77
+ if (config.username === undefined ||
78
+ config.username === "" ||
79
+ config.password === undefined ||
80
+ config.password === "") {
81
+ throw new errors_1.AuthenticationError("Password grant requires username and password");
82
+ }
83
+ formData.append("username", config.username);
84
+ formData.append("password", config.password);
85
+ }
86
+ try {
87
+ const response = await axios_1.default.post(config.tokenUrl, formData.toString(), { headers: { "Content-Type": "application/x-www-form-urlencoded" } });
88
+ if (response.data.access_token === undefined ||
89
+ response.data.access_token === "") {
90
+ throw new errors_1.AuthenticationError(`Authentication response missing access_token from ${config.tokenUrl}`);
91
+ }
92
+ this.bearerToken = response.data.access_token;
93
+ this.tokenIssuedAt = Date.now();
94
+ if (response.data.expires_in !== undefined &&
95
+ response.data.expires_in !== 0) {
96
+ this.tokenExpiry = this.tokenIssuedAt + response.data.expires_in * 1000;
97
+ }
98
+ this.logger?.debug?.("OAuth2 token acquired", {
99
+ tokenUrl: config.tokenUrl,
100
+ expiresIn: response.data.expires_in,
101
+ });
102
+ return this.bearerToken;
103
+ }
104
+ catch (error) {
105
+ if (error instanceof errors_1.AuthenticationError) {
106
+ throw error;
107
+ }
108
+ if (axios_1.default.isAxiosError(error)) {
109
+ const status = error.response?.status;
110
+ const errorData = error.response?.data !== undefined
111
+ ? JSON.stringify(error.response.data, null, 2)
112
+ : error.message;
113
+ throw new errors_1.HttpError(`Authentication failed for ${config.tokenUrl}: ${String(status)} ${errorData}`, status, error.response?.statusText);
114
+ }
115
+ throw new errors_1.AuthenticationError(`Authentication failed for ${config.tokenUrl}: ${error instanceof Error ? error.message : String(error)}`);
116
+ }
117
+ }
118
+ isTokenValid() {
119
+ if (this.bearerToken === null) {
120
+ return false;
121
+ }
122
+ if (this.tokenExpiry === null) {
123
+ return true;
124
+ }
125
+ const lifetimeMs = this.getTokenLifetimeMs();
126
+ const defaultBufferMs = 5 * 60 * 1000;
127
+ const bufferMs = lifetimeMs !== null
128
+ ? Math.min(defaultBufferMs, Math.floor(lifetimeMs / 2))
129
+ : defaultBufferMs;
130
+ return Date.now() < this.tokenExpiry - bufferMs;
131
+ }
132
+ }
133
+ exports.AuthenticationManager = AuthenticationManager;
134
+ //# sourceMappingURL=AuthenticationManager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AuthenticationManager.js","sourceRoot":"","sources":["../src/AuthenticationManager.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ import type { RestClientLogger, RetryConfig } from "./types";
2
+ /** HTTP client with automatic retries and structured error formatting. */
3
+ export declare class HttpClient {
4
+ private readonly instance;
5
+ private readonly logger;
6
+ private readonly retry;
7
+ constructor(options?: {
8
+ logger?: RestClientLogger;
9
+ retry?: RetryConfig;
10
+ defaultHeaders?: Record<string, string>;
11
+ });
12
+ setBearerToken(token: string): void;
13
+ clearBearerToken(): void;
14
+ get<T>(url: string): Promise<T>;
15
+ post<T>(url: string, data?: unknown): Promise<T>;
16
+ delete<T>(url: string): Promise<T>;
17
+ patch<T>(url: string, data?: unknown): Promise<T>;
18
+ put<T>(url: string, data?: unknown): Promise<T>;
19
+ private withRetry;
20
+ private isRetryable;
21
+ private toTypedError;
22
+ }
23
+ //# sourceMappingURL=HttpClient.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"HttpClient.d.ts","sourceRoot":"","sources":["../src/HttpClient.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAW7D,0EAA0E;AAC1E,qBAAa,UAAU;IACrB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAgB;IACzC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA+B;IACtD,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAc;gBAExB,OAAO,CAAC,EAAE;QACpB,MAAM,CAAC,EAAE,gBAAgB,CAAC;QAC1B,KAAK,CAAC,EAAE,WAAW,CAAC;QACpB,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;KACzC;IAWD,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAInC,gBAAgB,IAAI,IAAI;IAIlB,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC;IAI/B,IAAI,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC;IAIhD,MAAM,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC;IAIlC,KAAK,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC;IAMjD,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC;YAIvC,SAAS;IA6BvB,OAAO,CAAC,WAAW;IAyBnB,OAAO,CAAC,YAAY;CAoDrB"}
@@ -0,0 +1,174 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.HttpClient = void 0;
7
+ const axios_1 = __importDefault(require("axios"));
8
+ const errors_1 = require("./errors");
9
+ const DEFAULT_RETRY = {
10
+ maxAttempts: 3,
11
+ delayMs: 6000,
12
+ };
13
+ const CAUSE_TRUNCATE = 200;
14
+ const CONTEXT_VALUE_TRUNCATE = 50;
15
+ const MAX_CONTEXT_KEYS = 3;
16
+ /** HTTP client with automatic retries and structured error formatting. */
17
+ class HttpClient {
18
+ instance;
19
+ logger;
20
+ retry;
21
+ constructor(options) {
22
+ this.instance = axios_1.default.create({
23
+ headers: {
24
+ "Content-Type": "application/json",
25
+ ...options?.defaultHeaders,
26
+ },
27
+ });
28
+ this.logger = options?.logger;
29
+ this.retry = { ...DEFAULT_RETRY, ...options?.retry };
30
+ }
31
+ setBearerToken(token) {
32
+ this.instance.defaults.headers.common.Authorization = `Bearer ${token}`;
33
+ }
34
+ clearBearerToken() {
35
+ delete this.instance.defaults.headers.common.Authorization;
36
+ }
37
+ async get(url) {
38
+ return this.withRetry("GET", url, () => this.instance.get(url));
39
+ }
40
+ async post(url, data) {
41
+ return this.withRetry("POST", url, () => this.instance.post(url, data));
42
+ }
43
+ async delete(url) {
44
+ return this.withRetry("DELETE", url, () => this.instance.delete(url));
45
+ }
46
+ async patch(url, data) {
47
+ return this.withRetry("PATCH", url, () => this.instance.patch(url, data));
48
+ }
49
+ async put(url, data) {
50
+ return this.withRetry("PUT", url, () => this.instance.put(url, data));
51
+ }
52
+ async withRetry(method, url, fn, attempt = 0) {
53
+ try {
54
+ const response = await fn();
55
+ this.logger?.debug?.(`${method} ${url}`, {
56
+ status: response.status ?? 200,
57
+ });
58
+ return response.data;
59
+ }
60
+ catch (error) {
61
+ if (attempt < this.retry.maxAttempts && this.isRetryable(error)) {
62
+ this.logger?.warn?.(`${method} ${url} failed, retrying (${String(attempt + 1)}/${String(this.retry.maxAttempts)})`, {
63
+ error: axios_1.default.isAxiosError(error)
64
+ ? (error.response?.status ?? "network")
65
+ : String(error),
66
+ });
67
+ await sleep(this.retry.delayMs);
68
+ return this.withRetry(method, url, fn, attempt + 1);
69
+ }
70
+ throw this.toTypedError(error);
71
+ }
72
+ }
73
+ isRetryable(error) {
74
+ if (!axios_1.default.isAxiosError(error)) {
75
+ return error instanceof errors_1.NetworkError;
76
+ }
77
+ const status = error.response?.status;
78
+ if (status === undefined) {
79
+ return true;
80
+ }
81
+ if (status >= 500 && status < 600) {
82
+ return true;
83
+ }
84
+ if (this.retry.retryableStatuses?.includes(status) === true) {
85
+ return true;
86
+ }
87
+ if (this.retry.isRetryable !== undefined) {
88
+ const body = (error.response?.data ?? {});
89
+ return this.retry.isRetryable(status, body);
90
+ }
91
+ return false;
92
+ }
93
+ toTypedError(error) {
94
+ if (axios_1.default.isAxiosError(error)) {
95
+ if (error.response === undefined) {
96
+ return new errors_1.NetworkError(`Request failed: ${error.message}`, error.code !== undefined ? { code: error.code } : undefined);
97
+ }
98
+ const { status } = error.response;
99
+ const { statusText } = error.response;
100
+ const data = error.response.data ?? {};
101
+ const code = typeof data.code === "string" ? data.code : undefined;
102
+ const message = typeof data.message === "string" ? data.message : undefined;
103
+ const cause = typeof data.cause === "string" ? data.cause : undefined;
104
+ const context = typeof data.context === "object" &&
105
+ data.context !== null &&
106
+ !Array.isArray(data.context)
107
+ ? data.context
108
+ : undefined;
109
+ let msg = `HTTP ${String(status)}`;
110
+ if (code !== undefined) {
111
+ msg += `: ${code}`;
112
+ }
113
+ if (message !== undefined) {
114
+ msg += ` - ${message}`;
115
+ }
116
+ if (cause !== undefined) {
117
+ const truncated = cause.length > CAUSE_TRUNCATE
118
+ ? `${cause.substring(0, CAUSE_TRUNCATE)}...`
119
+ : cause;
120
+ msg += ` (cause: ${truncated})`;
121
+ }
122
+ if (context !== undefined) {
123
+ const summary = formatContextSummary(context);
124
+ if (summary !== undefined) {
125
+ msg += ` [context: ${summary}]`;
126
+ }
127
+ }
128
+ return new errors_1.HttpError(msg, status, statusText, data);
129
+ }
130
+ return new errors_1.NetworkError(`Request failed: ${error instanceof Error ? error.message : String(error)}`);
131
+ }
132
+ }
133
+ exports.HttpClient = HttpClient;
134
+ function sleep(ms) {
135
+ return new Promise((resolve) => {
136
+ setTimeout(resolve, ms);
137
+ });
138
+ }
139
+ function formatContextSummary(obj) {
140
+ const keys = Object.keys(obj);
141
+ if (keys.length === 0) {
142
+ return undefined;
143
+ }
144
+ return keys
145
+ .slice(0, MAX_CONTEXT_KEYS)
146
+ .map((k) => {
147
+ const v = obj[k];
148
+ const str = stringifyValue(v);
149
+ return `${k}=${str.length > CONTEXT_VALUE_TRUNCATE ? `${str.substring(0, CONTEXT_VALUE_TRUNCATE)}...` : str}`;
150
+ })
151
+ .join(", ");
152
+ }
153
+ function stringifyValue(v) {
154
+ if (typeof v === "string") {
155
+ return v;
156
+ }
157
+ if (v === null) {
158
+ return "null";
159
+ }
160
+ if (v === undefined) {
161
+ return "undefined";
162
+ }
163
+ return safeStringify(v);
164
+ }
165
+ function safeStringify(v) {
166
+ try {
167
+ const result = JSON.stringify(v);
168
+ return typeof result === "string" ? result : "[Object]";
169
+ }
170
+ catch {
171
+ return "[Object]";
172
+ }
173
+ }
174
+ //# sourceMappingURL=HttpClient.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"HttpClient.js","sourceRoot":"","sources":["../src/HttpClient.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ import { z } from "zod";
2
+ import type { HttpMethod } from "./types";
3
+ /** Declarative configuration for a REST operation. */
4
+ export interface OperationConfig<Params, Response> {
5
+ readonly params: z.ZodType<Params>;
6
+ readonly method: HttpMethod;
7
+ readonly url: (params: Params, baseUrl: string) => string;
8
+ readonly body?: (params: Params) => Record<string, unknown> | string | undefined;
9
+ readonly transform?: (response: Response) => Response;
10
+ }
11
+ /**
12
+ * Define a REST operation declaratively.
13
+ *
14
+ * @example
15
+ * const GetUser = defineOperation<{ id: string }, User>({
16
+ * params: z.object({ id: z.string() }),
17
+ * method: "GET",
18
+ * url: (p, base) => `${base}/users/${p.id}`,
19
+ * });
20
+ */
21
+ export declare function defineOperation<Params, Response>(config: OperationConfig<Params, Response>): OperationConfig<Params, Response>;
22
+ /** Validate params against a Zod schema, throwing ValidationError on failure. */
23
+ export declare function validateParams<T>(params: T, schema: z.ZodType<T>): T;
24
+ //# sourceMappingURL=Operation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Operation.d.ts","sourceRoot":"","sources":["../src/Operation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAE1C,sDAAsD;AACtD,MAAM,WAAW,eAAe,CAAC,MAAM,EAAE,QAAQ;IAC/C,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IACnC,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;IAC5B,QAAQ,CAAC,GAAG,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,MAAM,CAAC;IAC1D,QAAQ,CAAC,IAAI,CAAC,EAAE,CACd,MAAM,EAAE,MAAM,KACX,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,GAAG,SAAS,CAAC;IAClD,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC,QAAQ,EAAE,QAAQ,KAAK,QAAQ,CAAC;CACvD;AAED;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,QAAQ,EAC9C,MAAM,EAAE,eAAe,CAAC,MAAM,EAAE,QAAQ,CAAC,GACxC,eAAe,CAAC,MAAM,EAAE,QAAQ,CAAC,CAEnC;AAED,iFAAiF;AACjF,wBAAgB,cAAc,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAWpE"}
@@ -0,0 +1,32 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.defineOperation = defineOperation;
4
+ exports.validateParams = validateParams;
5
+ const zod_1 = require("zod");
6
+ const errors_1 = require("./errors");
7
+ /**
8
+ * Define a REST operation declaratively.
9
+ *
10
+ * @example
11
+ * const GetUser = defineOperation<{ id: string }, User>({
12
+ * params: z.object({ id: z.string() }),
13
+ * method: "GET",
14
+ * url: (p, base) => `${base}/users/${p.id}`,
15
+ * });
16
+ */
17
+ function defineOperation(config) {
18
+ return config;
19
+ }
20
+ /** Validate params against a Zod schema, throwing ValidationError on failure. */
21
+ function validateParams(params, schema) {
22
+ try {
23
+ return schema.parse(params);
24
+ }
25
+ catch (error) {
26
+ if (error instanceof zod_1.z.ZodError) {
27
+ throw new errors_1.ValidationError(`Parameter validation failed: ${error.issues.map((e) => `${e.path.join(".")}: ${e.message}`).join(", ")}`);
28
+ }
29
+ throw error;
30
+ }
31
+ }
32
+ //# sourceMappingURL=Operation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Operation.js","sourceRoot":"","sources":["../src/Operation.ts"],"names":[],"mappings":";;AA0BA,0CAIC;AAGD,wCAWC;AA5CD,6BAAwB;AAExB,qCAA2C;AAc3C;;;;;;;;;GASG;AACH,SAAgB,eAAe,CAC7B,MAAyC;IAEzC,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,iFAAiF;AACjF,SAAgB,cAAc,CAAI,MAAS,EAAE,MAAoB;IAC/D,IAAI,CAAC;QACH,OAAO,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IAC9B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,KAAK,YAAY,OAAC,CAAC,QAAQ,EAAE,CAAC;YAChC,MAAM,IAAI,wBAAe,CACvB,gCAAgC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAC1G,CAAC;QACJ,CAAC;QACD,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC"}
@@ -0,0 +1,54 @@
1
+ import { type OperationConfig } from "./Operation";
2
+ import type { RestClientConfig, RestClientLogger } from "./types";
3
+ /**
4
+ * Opinionated REST client with OAuth2, retries, and declarative operations.
5
+ *
6
+ * @example
7
+ * class MyApi extends RestClient {
8
+ * getUser = this.bind(GetUser);
9
+ * createUser = this.bind(CreateUser);
10
+ * }
11
+ *
12
+ * const api = new MyApi({
13
+ * baseUrl: "https://api.example.com",
14
+ * auth: { type: "oauth2", tokenUrl: "...", clientId: "...", clientSecret: "..." },
15
+ * });
16
+ *
17
+ * const user = await api.getUser({ id: "123" });
18
+ */
19
+ export declare class RestClient {
20
+ private readonly httpClient;
21
+ private readonly authManager;
22
+ private readonly config;
23
+ constructor(config: RestClientConfig);
24
+ /** Base URL for all requests. */
25
+ getBaseUrl(): string;
26
+ /** Logger instance, if configured. */
27
+ getLogger(): RestClientLogger | undefined;
28
+ /**
29
+ * Bind a declarative operation to this client, returning a typed function.
30
+ *
31
+ * @example
32
+ * class MyApi extends RestClient {
33
+ * getUser = this.bind(GetUser);
34
+ * }
35
+ */
36
+ bind<Params, Response>(config: OperationConfig<Params, Response>): (params: Params) => Promise<Response>;
37
+ /** Authenticate and set the bearer token for subsequent requests. */
38
+ authenticate(): Promise<string>;
39
+ /** Clear the cached token, forcing re-authentication on next request. */
40
+ clearToken(): void;
41
+ /** Token expiry timestamp (ms since epoch), or null if unavailable. */
42
+ getTokenExpiryTime(): number | null;
43
+ /** Token issue timestamp (ms since epoch), or null if unavailable. */
44
+ getTokenIssuedAt(): number | null;
45
+ /** Token lifetime in milliseconds, or null if unavailable. */
46
+ getTokenLifetimeMs(): number | null;
47
+ get<T>(url: string): Promise<T>;
48
+ post<T>(url: string, data?: unknown): Promise<T>;
49
+ delete<T>(url: string): Promise<T>;
50
+ patch<T>(url: string, data?: unknown): Promise<T>;
51
+ put<T>(url: string, data?: unknown): Promise<T>;
52
+ private executeMethod;
53
+ }
54
+ //# sourceMappingURL=RestClient.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RestClient.d.ts","sourceRoot":"","sources":["../src/RestClient.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,eAAe,EAAkB,MAAM,aAAa,CAAC;AACnE,OAAO,KAAK,EAAc,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAE9E;;;;;;;;;;;;;;;GAeG;AACH,qBAAa,UAAU;IACrB,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAa;IACxC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAwB;IACpD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAmB;gBAE9B,MAAM,EAAE,gBAAgB;IAmBpC,iCAAiC;IACjC,UAAU,IAAI,MAAM;IAIpB,sCAAsC;IACtC,SAAS,IAAI,gBAAgB,GAAG,SAAS;IAIzC;;;;;;;OAOG;IACH,IAAI,CAAC,MAAM,EAAE,QAAQ,EACnB,MAAM,EAAE,eAAe,CAAC,MAAM,EAAE,QAAQ,CAAC,GACxC,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,QAAQ,CAAC;IAexC,qEAAqE;IAC/D,YAAY,IAAI,OAAO,CAAC,MAAM,CAAC;IAQrC,yEAAyE;IACzE,UAAU,IAAI,IAAI;IAKlB,uEAAuE;IACvE,kBAAkB,IAAI,MAAM,GAAG,IAAI;IAInC,sEAAsE;IACtE,gBAAgB,IAAI,MAAM,GAAG,IAAI;IAIjC,8DAA8D;IAC9D,kBAAkB,IAAI,MAAM,GAAG,IAAI;IAI7B,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC;IAK/B,IAAI,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC;IAKhD,MAAM,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC;IAKlC,KAAK,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC;IAKjD,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC;YAKvC,aAAa;CAuB5B"}
@@ -0,0 +1,132 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.RestClient = void 0;
4
+ const AuthenticationManager_1 = require("./AuthenticationManager");
5
+ const errors_1 = require("./errors");
6
+ const HttpClient_1 = require("./HttpClient");
7
+ const Operation_1 = require("./Operation");
8
+ /**
9
+ * Opinionated REST client with OAuth2, retries, and declarative operations.
10
+ *
11
+ * @example
12
+ * class MyApi extends RestClient {
13
+ * getUser = this.bind(GetUser);
14
+ * createUser = this.bind(CreateUser);
15
+ * }
16
+ *
17
+ * const api = new MyApi({
18
+ * baseUrl: "https://api.example.com",
19
+ * auth: { type: "oauth2", tokenUrl: "...", clientId: "...", clientSecret: "..." },
20
+ * });
21
+ *
22
+ * const user = await api.getUser({ id: "123" });
23
+ */
24
+ class RestClient {
25
+ httpClient;
26
+ authManager;
27
+ config;
28
+ constructor(config) {
29
+ if (config.baseUrl === "") {
30
+ throw new errors_1.ConfigurationError("baseUrl is required");
31
+ }
32
+ this.config = config;
33
+ this.authManager = new AuthenticationManager_1.AuthenticationManager(config.auth ?? { type: "none" }, config.logger);
34
+ this.httpClient = new HttpClient_1.HttpClient({
35
+ logger: config.logger,
36
+ retry: config.retry,
37
+ defaultHeaders: config.defaultHeaders
38
+ ? { ...config.defaultHeaders }
39
+ : undefined,
40
+ });
41
+ }
42
+ /** Base URL for all requests. */
43
+ getBaseUrl() {
44
+ return this.config.baseUrl;
45
+ }
46
+ /** Logger instance, if configured. */
47
+ getLogger() {
48
+ return this.config.logger;
49
+ }
50
+ /**
51
+ * Bind a declarative operation to this client, returning a typed function.
52
+ *
53
+ * @example
54
+ * class MyApi extends RestClient {
55
+ * getUser = this.bind(GetUser);
56
+ * }
57
+ */
58
+ bind(config) {
59
+ return async (params) => {
60
+ const validated = (0, Operation_1.validateParams)(params, config.params);
61
+ const url = config.url(validated, this.getBaseUrl());
62
+ const response = await this.executeMethod(config.method, url, config.body?.(validated));
63
+ return config.transform !== undefined
64
+ ? config.transform(response)
65
+ : response;
66
+ };
67
+ }
68
+ /** Authenticate and set the bearer token for subsequent requests. */
69
+ async authenticate() {
70
+ const token = await this.authManager.authenticate();
71
+ if (token !== "") {
72
+ this.httpClient.setBearerToken(token);
73
+ }
74
+ return token;
75
+ }
76
+ /** Clear the cached token, forcing re-authentication on next request. */
77
+ clearToken() {
78
+ this.authManager.clearToken();
79
+ this.httpClient.clearBearerToken();
80
+ }
81
+ /** Token expiry timestamp (ms since epoch), or null if unavailable. */
82
+ getTokenExpiryTime() {
83
+ return this.authManager.getTokenExpiryTime();
84
+ }
85
+ /** Token issue timestamp (ms since epoch), or null if unavailable. */
86
+ getTokenIssuedAt() {
87
+ return this.authManager.getTokenIssuedAt();
88
+ }
89
+ /** Token lifetime in milliseconds, or null if unavailable. */
90
+ getTokenLifetimeMs() {
91
+ return this.authManager.getTokenLifetimeMs();
92
+ }
93
+ async get(url) {
94
+ await this.authenticate();
95
+ return this.httpClient.get(url);
96
+ }
97
+ async post(url, data) {
98
+ await this.authenticate();
99
+ return this.httpClient.post(url, data);
100
+ }
101
+ async delete(url) {
102
+ await this.authenticate();
103
+ return this.httpClient.delete(url);
104
+ }
105
+ async patch(url, data) {
106
+ await this.authenticate();
107
+ return this.httpClient.patch(url, data);
108
+ }
109
+ async put(url, data) {
110
+ await this.authenticate();
111
+ return this.httpClient.put(url, data);
112
+ }
113
+ async executeMethod(method, url, data) {
114
+ await this.authenticate();
115
+ switch (method) {
116
+ case "GET":
117
+ return this.httpClient.get(url);
118
+ case "POST":
119
+ return this.httpClient.post(url, data);
120
+ case "DELETE":
121
+ return this.httpClient.delete(url);
122
+ case "PATCH":
123
+ return this.httpClient.patch(url, data);
124
+ case "PUT":
125
+ return this.httpClient.put(url, data);
126
+ default:
127
+ throw new errors_1.ConfigurationError(`Unsupported HTTP method: ${method}`);
128
+ }
129
+ }
130
+ }
131
+ exports.RestClient = RestClient;
132
+ //# sourceMappingURL=RestClient.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RestClient.js","sourceRoot":"","sources":["../src/RestClient.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,56 @@
1
+ /** JSON-serializable context for error details. */
2
+ export type ErrorContext = Readonly<Record<string, unknown>>;
3
+ export declare const ErrorCode: {
4
+ readonly REST_CLIENT_ERROR: "REST_CLIENT_ERROR";
5
+ readonly CONFIGURATION_ERROR: "CONFIGURATION_ERROR";
6
+ readonly AUTHENTICATION_ERROR: "AUTHENTICATION_ERROR";
7
+ readonly HTTP_ERROR: "HTTP_ERROR";
8
+ readonly VALIDATION_ERROR: "VALIDATION_ERROR";
9
+ readonly NETWORK_ERROR: "NETWORK_ERROR";
10
+ };
11
+ export type ErrorCodeType = (typeof ErrorCode)[keyof typeof ErrorCode];
12
+ /** Base error for all rest-client errors. */
13
+ export declare class RestClientError extends Error {
14
+ readonly code: string;
15
+ readonly context?: ErrorContext | undefined;
16
+ readonly name: string;
17
+ constructor(message: string, code: string, context?: ErrorContext | undefined);
18
+ }
19
+ /**
20
+ *
21
+ */
22
+ export declare class ConfigurationError extends RestClientError {
23
+ readonly name = "ConfigurationError";
24
+ constructor(message: string, context?: ErrorContext);
25
+ }
26
+ /**
27
+ *
28
+ */
29
+ export declare class AuthenticationError extends RestClientError {
30
+ readonly name = "AuthenticationError";
31
+ constructor(message: string, context?: ErrorContext);
32
+ }
33
+ /**
34
+ *
35
+ */
36
+ export declare class HttpError extends RestClientError {
37
+ readonly status?: number | undefined;
38
+ readonly statusText?: string | undefined;
39
+ readonly name = "HttpError";
40
+ constructor(message: string, status?: number | undefined, statusText?: string | undefined, response?: ErrorContext);
41
+ }
42
+ /**
43
+ *
44
+ */
45
+ export declare class ValidationError extends RestClientError {
46
+ readonly name = "ValidationError";
47
+ constructor(message: string, context?: ErrorContext);
48
+ }
49
+ /**
50
+ *
51
+ */
52
+ export declare class NetworkError extends RestClientError {
53
+ readonly name = "NetworkError";
54
+ constructor(message: string, context?: ErrorContext);
55
+ }
56
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,mDAAmD;AACnD,MAAM,MAAM,YAAY,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;AAE7D,eAAO,MAAM,SAAS;;;;;;;CAOZ,CAAC;AAEX,MAAM,MAAM,aAAa,GAAG,CAAC,OAAO,SAAS,CAAC,CAAC,MAAM,OAAO,SAAS,CAAC,CAAC;AAEvE,6CAA6C;AAC7C,qBAAa,eAAgB,SAAQ,KAAK;aAKtB,IAAI,EAAE,MAAM;aACZ,OAAO,CAAC,EAAE,YAAY;IALxC,SAAyB,IAAI,EAAE,MAAM,CAAC;gBAGpC,OAAO,EAAE,MAAM,EACC,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE,YAAY,YAAA;CAMzC;AAED;;GAEG;AACH,qBAAa,kBAAmB,SAAQ,eAAe;IACrD,SAAyB,IAAI,wBAAwB;gBAEzC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY;CAGpD;AAED;;GAEG;AACH,qBAAa,mBAAoB,SAAQ,eAAe;IACtD,SAAyB,IAAI,yBAAyB;gBAE1C,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY;CAGpD;AAED;;GAEG;AACH,qBAAa,SAAU,SAAQ,eAAe;aAK1B,MAAM,CAAC,EAAE,MAAM;aACf,UAAU,CAAC,EAAE,MAAM;IALrC,SAAyB,IAAI,eAAe;gBAG1C,OAAO,EAAE,MAAM,EACC,MAAM,CAAC,EAAE,MAAM,YAAA,EACf,UAAU,CAAC,EAAE,MAAM,YAAA,EACnC,QAAQ,CAAC,EAAE,YAAY;CAI1B;AAED;;GAEG;AACH,qBAAa,eAAgB,SAAQ,eAAe;IAClD,SAAyB,IAAI,qBAAqB;gBAEtC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY;CAGpD;AAED;;GAEG;AACH,qBAAa,YAAa,SAAQ,eAAe;IAC/C,SAAyB,IAAI,kBAAkB;gBAEnC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY;CAGpD"}
package/dist/errors.js ADDED
@@ -0,0 +1,80 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.NetworkError = exports.ValidationError = exports.HttpError = exports.AuthenticationError = exports.ConfigurationError = exports.RestClientError = exports.ErrorCode = void 0;
4
+ exports.ErrorCode = {
5
+ REST_CLIENT_ERROR: "REST_CLIENT_ERROR",
6
+ CONFIGURATION_ERROR: "CONFIGURATION_ERROR",
7
+ AUTHENTICATION_ERROR: "AUTHENTICATION_ERROR",
8
+ HTTP_ERROR: "HTTP_ERROR",
9
+ VALIDATION_ERROR: "VALIDATION_ERROR",
10
+ NETWORK_ERROR: "NETWORK_ERROR",
11
+ };
12
+ /** Base error for all rest-client errors. */
13
+ class RestClientError extends Error {
14
+ code;
15
+ context;
16
+ name;
17
+ constructor(message, code, context) {
18
+ super(message);
19
+ this.code = code;
20
+ this.context = context;
21
+ this.name = "RestClientError";
22
+ Object.setPrototypeOf(this, new.target.prototype);
23
+ }
24
+ }
25
+ exports.RestClientError = RestClientError;
26
+ /**
27
+ *
28
+ */
29
+ class ConfigurationError extends RestClientError {
30
+ name = "ConfigurationError";
31
+ constructor(message, context) {
32
+ super(message, exports.ErrorCode.CONFIGURATION_ERROR, context);
33
+ }
34
+ }
35
+ exports.ConfigurationError = ConfigurationError;
36
+ /**
37
+ *
38
+ */
39
+ class AuthenticationError extends RestClientError {
40
+ name = "AuthenticationError";
41
+ constructor(message, context) {
42
+ super(message, exports.ErrorCode.AUTHENTICATION_ERROR, context);
43
+ }
44
+ }
45
+ exports.AuthenticationError = AuthenticationError;
46
+ /**
47
+ *
48
+ */
49
+ class HttpError extends RestClientError {
50
+ status;
51
+ statusText;
52
+ name = "HttpError";
53
+ constructor(message, status, statusText, response) {
54
+ super(message, exports.ErrorCode.HTTP_ERROR, response);
55
+ this.status = status;
56
+ this.statusText = statusText;
57
+ }
58
+ }
59
+ exports.HttpError = HttpError;
60
+ /**
61
+ *
62
+ */
63
+ class ValidationError extends RestClientError {
64
+ name = "ValidationError";
65
+ constructor(message, context) {
66
+ super(message, exports.ErrorCode.VALIDATION_ERROR, context);
67
+ }
68
+ }
69
+ exports.ValidationError = ValidationError;
70
+ /**
71
+ *
72
+ */
73
+ class NetworkError extends RestClientError {
74
+ name = "NetworkError";
75
+ constructor(message, context) {
76
+ super(message, exports.ErrorCode.NETWORK_ERROR, context);
77
+ }
78
+ }
79
+ exports.NetworkError = NetworkError;
80
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":";;;AAGa,QAAA,SAAS,GAAG;IACvB,iBAAiB,EAAE,mBAAmB;IACtC,mBAAmB,EAAE,qBAAqB;IAC1C,oBAAoB,EAAE,sBAAsB;IAC5C,UAAU,EAAE,YAAY;IACxB,gBAAgB,EAAE,kBAAkB;IACpC,aAAa,EAAE,eAAe;CACtB,CAAC;AAIX,6CAA6C;AAC7C,MAAa,eAAgB,SAAQ,KAAK;IAKtB;IACA;IALO,IAAI,CAAS;IAEtC,YACE,OAAe,EACC,IAAY,EACZ,OAAsB;QAEtC,KAAK,CAAC,OAAO,CAAC,CAAC;QAHC,SAAI,GAAJ,IAAI,CAAQ;QACZ,YAAO,GAAP,OAAO,CAAe;QAGtC,IAAI,CAAC,IAAI,GAAG,iBAAiB,CAAC;QAC9B,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IACpD,CAAC;CACF;AAZD,0CAYC;AAED;;GAEG;AACH,MAAa,kBAAmB,SAAQ,eAAe;IAC5B,IAAI,GAAG,oBAAoB,CAAC;IAErD,YAAY,OAAe,EAAE,OAAsB;QACjD,KAAK,CAAC,OAAO,EAAE,iBAAS,CAAC,mBAAmB,EAAE,OAAO,CAAC,CAAC;IACzD,CAAC;CACF;AAND,gDAMC;AAED;;GAEG;AACH,MAAa,mBAAoB,SAAQ,eAAe;IAC7B,IAAI,GAAG,qBAAqB,CAAC;IAEtD,YAAY,OAAe,EAAE,OAAsB;QACjD,KAAK,CAAC,OAAO,EAAE,iBAAS,CAAC,oBAAoB,EAAE,OAAO,CAAC,CAAC;IAC1D,CAAC;CACF;AAND,kDAMC;AAED;;GAEG;AACH,MAAa,SAAU,SAAQ,eAAe;IAK1B;IACA;IALO,IAAI,GAAG,WAAW,CAAC;IAE5C,YACE,OAAe,EACC,MAAe,EACf,UAAmB,EACnC,QAAuB;QAEvB,KAAK,CAAC,OAAO,EAAE,iBAAS,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;QAJ/B,WAAM,GAAN,MAAM,CAAS;QACf,eAAU,GAAV,UAAU,CAAS;IAIrC,CAAC;CACF;AAXD,8BAWC;AAED;;GAEG;AACH,MAAa,eAAgB,SAAQ,eAAe;IACzB,IAAI,GAAG,iBAAiB,CAAC;IAElD,YAAY,OAAe,EAAE,OAAsB;QACjD,KAAK,CAAC,OAAO,EAAE,iBAAS,CAAC,gBAAgB,EAAE,OAAO,CAAC,CAAC;IACtD,CAAC;CACF;AAND,0CAMC;AAED;;GAEG;AACH,MAAa,YAAa,SAAQ,eAAe;IACtB,IAAI,GAAG,cAAc,CAAC;IAE/C,YAAY,OAAe,EAAE,OAAsB;QACjD,KAAK,CAAC,OAAO,EAAE,iBAAS,CAAC,aAAa,EAAE,OAAO,CAAC,CAAC;IACnD,CAAC;CACF;AAND,oCAMC"}
@@ -0,0 +1,9 @@
1
+ export { RestClient } from "./RestClient";
2
+ export { defineOperation, validateParams } from "./Operation";
3
+ export type { OperationConfig } from "./Operation";
4
+ export { AuthenticationManager } from "./AuthenticationManager";
5
+ export { HttpClient } from "./HttpClient";
6
+ export { RestClientError, ConfigurationError, AuthenticationError, HttpError, ValidationError, NetworkError, ErrorCode, } from "./errors";
7
+ export type { ErrorContext, ErrorCodeType } from "./errors";
8
+ export type { RestClientConfig, AuthConfig, OAuth2Auth, BearerAuth, TokenGeneratorAuth, NoAuth, RetryConfig, RestClientLogger, HttpMethod, } from "./types";
9
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC9D,YAAY,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAChE,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE1C,OAAO,EACL,eAAe,EACf,kBAAkB,EAClB,mBAAmB,EACnB,SAAS,EACT,eAAe,EACf,YAAY,EACZ,SAAS,GACV,MAAM,UAAU,CAAC;AAClB,YAAY,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAE5D,YAAY,EACV,gBAAgB,EAChB,UAAU,EACV,UAAU,EACV,UAAU,EACV,kBAAkB,EAClB,MAAM,EACN,WAAW,EACX,gBAAgB,EAChB,UAAU,GACX,MAAM,SAAS,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ErrorCode = exports.NetworkError = exports.ValidationError = exports.HttpError = exports.AuthenticationError = exports.ConfigurationError = exports.RestClientError = exports.HttpClient = exports.AuthenticationManager = exports.validateParams = exports.defineOperation = exports.RestClient = void 0;
4
+ var RestClient_1 = require("./RestClient");
5
+ Object.defineProperty(exports, "RestClient", { enumerable: true, get: function () { return RestClient_1.RestClient; } });
6
+ var Operation_1 = require("./Operation");
7
+ Object.defineProperty(exports, "defineOperation", { enumerable: true, get: function () { return Operation_1.defineOperation; } });
8
+ Object.defineProperty(exports, "validateParams", { enumerable: true, get: function () { return Operation_1.validateParams; } });
9
+ var AuthenticationManager_1 = require("./AuthenticationManager");
10
+ Object.defineProperty(exports, "AuthenticationManager", { enumerable: true, get: function () { return AuthenticationManager_1.AuthenticationManager; } });
11
+ var HttpClient_1 = require("./HttpClient");
12
+ Object.defineProperty(exports, "HttpClient", { enumerable: true, get: function () { return HttpClient_1.HttpClient; } });
13
+ var errors_1 = require("./errors");
14
+ Object.defineProperty(exports, "RestClientError", { enumerable: true, get: function () { return errors_1.RestClientError; } });
15
+ Object.defineProperty(exports, "ConfigurationError", { enumerable: true, get: function () { return errors_1.ConfigurationError; } });
16
+ Object.defineProperty(exports, "AuthenticationError", { enumerable: true, get: function () { return errors_1.AuthenticationError; } });
17
+ Object.defineProperty(exports, "HttpError", { enumerable: true, get: function () { return errors_1.HttpError; } });
18
+ Object.defineProperty(exports, "ValidationError", { enumerable: true, get: function () { return errors_1.ValidationError; } });
19
+ Object.defineProperty(exports, "NetworkError", { enumerable: true, get: function () { return errors_1.NetworkError; } });
20
+ Object.defineProperty(exports, "ErrorCode", { enumerable: true, get: function () { return errors_1.ErrorCode; } });
21
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,2CAA0C;AAAjC,wGAAA,UAAU,OAAA;AACnB,yCAA8D;AAArD,4GAAA,eAAe,OAAA;AAAE,2GAAA,cAAc,OAAA;AAExC,iEAAgE;AAAvD,8HAAA,qBAAqB,OAAA;AAC9B,2CAA0C;AAAjC,wGAAA,UAAU,OAAA;AAEnB,mCAQkB;AAPhB,yGAAA,eAAe,OAAA;AACf,4GAAA,kBAAkB,OAAA;AAClB,6GAAA,mBAAmB,OAAA;AACnB,mGAAA,SAAS,OAAA;AACT,yGAAA,eAAe,OAAA;AACf,sGAAA,YAAY,OAAA;AACZ,mGAAA,SAAS,OAAA"}
@@ -0,0 +1,57 @@
1
+ /** OAuth2 client credentials or password grant. */
2
+ export interface OAuth2Auth {
3
+ readonly type: "oauth2";
4
+ readonly tokenUrl: string;
5
+ readonly clientId: string;
6
+ readonly clientSecret?: string;
7
+ readonly audience?: string;
8
+ readonly scope?: string;
9
+ readonly grantType?: "client_credentials" | "password";
10
+ readonly username?: string;
11
+ readonly password?: string;
12
+ }
13
+ /** Static bearer token. */
14
+ export interface BearerAuth {
15
+ readonly type: "bearer";
16
+ readonly token: string;
17
+ }
18
+ /** Async token generator with optional cache duration. */
19
+ export interface TokenGeneratorAuth {
20
+ readonly type: "generator";
21
+ readonly generate: () => Promise<string>;
22
+ readonly cacheDurationMs?: number;
23
+ }
24
+ /** No authentication. */
25
+ export interface NoAuth {
26
+ readonly type: "none";
27
+ }
28
+ export type AuthConfig = OAuth2Auth | BearerAuth | TokenGeneratorAuth | NoAuth;
29
+ export interface RetryConfig {
30
+ /** Number of retries after the initial attempt. */
31
+ readonly maxAttempts: number;
32
+ /** Delay between retries in milliseconds. */
33
+ readonly delayMs: number;
34
+ /** Additional HTTP status codes to retry on (5xx and network errors are always retried). */
35
+ readonly retryableStatuses?: readonly number[];
36
+ /** Custom retryable check. Called with the status code and parsed response body. */
37
+ readonly isRetryable?: (status: number, body: Record<string, unknown>) => boolean;
38
+ }
39
+ /**
40
+ * Minimal logger interface. Structurally compatible with `@hardlydifficult/logger`'s Logger class.
41
+ * All methods are optional -- only implement what you need.
42
+ */
43
+ export interface RestClientLogger {
44
+ debug?(message: string, context?: Record<string, unknown>): void;
45
+ info?(message: string, context?: Record<string, unknown>): void;
46
+ warn?(message: string, context?: Record<string, unknown>): void;
47
+ error?(message: string, context?: Record<string, unknown>): void;
48
+ }
49
+ export interface RestClientConfig {
50
+ readonly baseUrl: string;
51
+ readonly auth?: AuthConfig;
52
+ readonly retry?: RetryConfig;
53
+ readonly defaultHeaders?: Readonly<Record<string, string>>;
54
+ readonly logger?: RestClientLogger;
55
+ }
56
+ export type HttpMethod = "GET" | "POST" | "DELETE" | "PATCH" | "PUT";
57
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,mDAAmD;AACnD,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IACxB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,SAAS,CAAC,EAAE,oBAAoB,GAAG,UAAU,CAAC;IACvD,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,2BAA2B;AAC3B,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAED,0DAA0D;AAC1D,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC;IAC3B,QAAQ,CAAC,QAAQ,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;IACzC,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;CACnC;AAED,yBAAyB;AACzB,MAAM,WAAW,MAAM;IACrB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,MAAM,UAAU,GAAG,UAAU,GAAG,UAAU,GAAG,kBAAkB,GAAG,MAAM,CAAC;AAE/E,MAAM,WAAW,WAAW;IAC1B,mDAAmD;IACnD,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,6CAA6C;IAC7C,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,4FAA4F;IAC5F,QAAQ,CAAC,iBAAiB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC/C,oFAAoF;IACpF,QAAQ,CAAC,WAAW,CAAC,EAAE,CACrB,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAC1B,OAAO,CAAC;CACd;AAED;;;GAGG;AACH,MAAM,WAAW,gBAAgB;IAC/B,KAAK,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IACjE,IAAI,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IAChE,IAAI,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IAChE,KAAK,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;CAClE;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,IAAI,CAAC,EAAE,UAAU,CAAC;IAC3B,QAAQ,CAAC,KAAK,CAAC,EAAE,WAAW,CAAC;IAC7B,QAAQ,CAAC,cAAc,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAC3D,QAAQ,CAAC,MAAM,CAAC,EAAE,gBAAgB,CAAC;CACpC;AAED,MAAM,MAAM,UAAU,GAAG,KAAK,GAAG,MAAM,GAAG,QAAQ,GAAG,OAAO,GAAG,KAAK,CAAC"}
package/dist/types.js ADDED
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
package/package.json ADDED
@@ -0,0 +1,32 @@
1
+ {
2
+ "name": "@hardlydifficult/rest-client",
3
+ "version": "1.0.0",
4
+ "main": "./dist/index.js",
5
+ "types": "./dist/index.d.ts",
6
+ "files": [
7
+ "dist"
8
+ ],
9
+ "scripts": {
10
+ "build": "tsc",
11
+ "test": "vitest run",
12
+ "test:watch": "vitest",
13
+ "test:coverage": "vitest run --coverage",
14
+ "lint": "tsc --noEmit",
15
+ "clean": "rm -rf dist"
16
+ },
17
+ "dependencies": {
18
+ "axios": "1.9.0"
19
+ },
20
+ "peerDependencies": {
21
+ "zod": ">=3.0.0"
22
+ },
23
+ "devDependencies": {
24
+ "@types/node": "25.2.3",
25
+ "typescript": "5.9.3",
26
+ "vitest": "4.0.18",
27
+ "zod": "3.25.67"
28
+ },
29
+ "engines": {
30
+ "node": ">=18.0.0"
31
+ }
32
+ }