@ereo/client-sdk 0.1.6

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 ADDED
@@ -0,0 +1,238 @@
1
+ # @ereo/client-sdk
2
+
3
+ Type-safe client SDK for EreoJS framework APIs. Provides end-to-end type safety for HTTP requests with a clean, ergonomic API.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ bun add @ereo/client-sdk
9
+ ```
10
+
11
+ ## Quick Start
12
+
13
+ ```typescript
14
+ import { api, createClient, configureClient } from '@ereo/client-sdk';
15
+
16
+ // Configure the global client
17
+ configureClient({
18
+ baseUrl: 'https://api.example.com',
19
+ headers: { 'Authorization': 'Bearer token' },
20
+ });
21
+
22
+ // Simple API calls
23
+ const { data: posts } = await api('/api/posts').get();
24
+ const { data: post } = await api('/api/posts').post({
25
+ body: { title: 'Hello World' },
26
+ });
27
+
28
+ // Or create a dedicated client instance
29
+ const client = createClient({
30
+ baseUrl: 'https://api.example.com',
31
+ timeout: 10000, // default is 30000ms
32
+ });
33
+
34
+ const { data } = await client.get('/api/users', {
35
+ query: { page: 1, limit: 10 },
36
+ });
37
+ ```
38
+
39
+ ## API
40
+
41
+ ### `api(path)`
42
+
43
+ Quick helper using the global client.
44
+
45
+ ```typescript
46
+ // GET request
47
+ const { data } = await api('/api/posts').get();
48
+
49
+ // POST with body
50
+ const { data } = await api('/api/posts').post({
51
+ body: { title: 'New Post' },
52
+ });
53
+
54
+ // With path parameters
55
+ const { data } = await api('/api/posts/[id]').get({
56
+ params: { id: '123' },
57
+ });
58
+
59
+ // With query parameters
60
+ const { data } = await api('/api/posts').get({
61
+ query: { page: 1, limit: 10 },
62
+ });
63
+ ```
64
+
65
+ ### `createClient(config?)`
66
+
67
+ Create a new API client instance.
68
+
69
+ ```typescript
70
+ const client = createClient({
71
+ baseUrl: 'https://api.example.com',
72
+ timeout: 30000,
73
+ headers: { 'X-API-Key': 'key' },
74
+ debug: true,
75
+ onRequest: async (config) => config,
76
+ onResponse: async (response) => response,
77
+ onError: async (error) => console.error(error),
78
+ });
79
+ ```
80
+
81
+ ### `configureClient(config)`
82
+
83
+ Configure the global API client instance. Call once at app startup.
84
+
85
+ ```typescript
86
+ import { configureClient } from '@ereo/client-sdk';
87
+
88
+ configureClient({
89
+ baseUrl: '/api',
90
+ headers: { 'X-API-Version': '1' },
91
+ onError: async (error) => {
92
+ if (error.status === 401) {
93
+ // Handle unauthorized
94
+ }
95
+ },
96
+ });
97
+ ```
98
+
99
+ ### `getGlobalClient()`
100
+
101
+ Get the global API client instance, creating one if it does not exist.
102
+
103
+ ```typescript
104
+ import { getGlobalClient } from '@ereo/client-sdk';
105
+
106
+ const client = getGlobalClient();
107
+ const { data } = await client.get('/api/users');
108
+ ```
109
+
110
+ ### `ApiClient` Class
111
+
112
+ The main client class returned by `createClient()`.
113
+
114
+ ```typescript
115
+ import { createClient, ApiClient } from '@ereo/client-sdk';
116
+
117
+ const client: ApiClient = createClient({ baseUrl: '/api' });
118
+
119
+ // Instance methods
120
+ client.configure({ debug: true }); // Update configuration
121
+ client.get(path, options?) // GET request
122
+ client.post(path, options?) // POST request
123
+ client.put(path, options?) // PUT request
124
+ client.patch(path, options?) // PATCH request
125
+ client.delete(path, options?) // DELETE request
126
+ client.request(config) // Generic request
127
+ ```
128
+
129
+ ### HTTP Methods
130
+
131
+ All standard HTTP methods are supported:
132
+
133
+ ```typescript
134
+ client.get(path, options?)
135
+ client.post(path, options?)
136
+ client.put(path, options?)
137
+ client.patch(path, options?)
138
+ client.delete(path, options?)
139
+ ```
140
+
141
+ ### Request Options
142
+
143
+ ```typescript
144
+ {
145
+ params?: Record<string, string>, // Path parameters
146
+ query?: Record<string, unknown>, // Query string parameters
147
+ body?: unknown, // Request body (POST/PUT/PATCH)
148
+ headers?: Record<string, string>, // Additional headers
149
+ signal?: AbortSignal, // Abort controller signal
150
+ }
151
+ ```
152
+
153
+ ## Body Types
154
+
155
+ The client automatically handles different body types:
156
+
157
+ ```typescript
158
+ // JSON body (default)
159
+ await api('/api/posts').post({
160
+ body: { title: 'Hello', content: 'World' },
161
+ });
162
+
163
+ // FormData (for file uploads)
164
+ const formData = new FormData();
165
+ formData.append('file', fileInput.files[0]);
166
+ formData.append('name', 'My File');
167
+
168
+ await api('/api/upload').post({ body: formData });
169
+
170
+ // URLSearchParams
171
+ const params = new URLSearchParams();
172
+ params.append('grant_type', 'client_credentials');
173
+
174
+ await api('/api/oauth/token').post({ body: params });
175
+ ```
176
+
177
+ ## Error Handling
178
+
179
+ ```typescript
180
+ import { api } from '@ereo/client-sdk';
181
+ import type { ApiError } from '@ereo/client-sdk';
182
+
183
+ try {
184
+ const { data } = await api('/api/posts').get();
185
+ } catch (error) {
186
+ const apiError = error as ApiError;
187
+ console.log(apiError.status); // HTTP status code
188
+ console.log(apiError.path); // Request path
189
+ console.log(apiError.method); // Request method
190
+ console.log(apiError.message); // Error message
191
+ }
192
+ ```
193
+
194
+ ## Type Exports
195
+
196
+ The package exports the following types for TypeScript users:
197
+
198
+ ```typescript
199
+ import type {
200
+ HttpMethod, // 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'HEAD' | 'OPTIONS'
201
+ ApiRoutes, // Module augmentation interface for route types
202
+ ApiPaths, // All registered API route paths
203
+ MethodsFor, // Get supported methods for a path
204
+ RequestBody, // Get request body type for path/method
205
+ ResponseType, // Get response type for path/method
206
+ QueryParams, // Get query params type for path/method
207
+ PathParams, // Extract path params from route string
208
+ ApiRequestConfig, // Full request configuration
209
+ ApiResponse, // Response wrapper { data, status, headers, ok }
210
+ ApiError, // Error with status, path, method
211
+ ClientConfig, // Client configuration options
212
+ DefineApiTypes, // Helper for defining route API types
213
+ } from '@ereo/client-sdk';
214
+ ```
215
+
216
+ ## Key Features
217
+
218
+ - End-to-end type safety with TypeScript
219
+ - Path parameter substitution (`/api/posts/[id]`)
220
+ - Query string building
221
+ - Automatic JSON serialization
222
+ - FormData and URLSearchParams support
223
+ - Request/response interceptors
224
+ - Error handling with custom callbacks
225
+ - Configurable timeout (default: 30000ms)
226
+ - Debug mode for logging
227
+
228
+ ## Documentation
229
+
230
+ For full documentation, visit [https://ereo.dev/docs/client-sdk](https://ereo.dev/docs/client-sdk)
231
+
232
+ ## Part of EreoJS
233
+
234
+ This package is part of the [EreoJS monorepo](https://github.com/anthropics/ereo-js).
235
+
236
+ ## License
237
+
238
+ MIT
@@ -0,0 +1,134 @@
1
+ /**
2
+ * @ereo/client-sdk - Type-safe API client
3
+ *
4
+ * Provides end-to-end type safety for API calls.
5
+ */
6
+ import type { ApiPaths, HttpMethod, ApiRequestConfig, ApiResponse, ClientConfig, ResponseType, RequestBody, QueryParams, PathParams } from './types';
7
+ /**
8
+ * Type-safe API client for making HTTP requests.
9
+ */
10
+ export declare class ApiClient {
11
+ private config;
12
+ constructor(config?: ClientConfig);
13
+ /**
14
+ * Update client configuration.
15
+ */
16
+ configure(config: Partial<ClientConfig>): void;
17
+ /**
18
+ * Make a type-safe API request.
19
+ */
20
+ request<Path extends ApiPaths, Method extends HttpMethod = 'GET'>(config: ApiRequestConfig<Path, Method>): Promise<ApiResponse<ResponseType<Path, Method>>>;
21
+ /**
22
+ * Make a GET request.
23
+ */
24
+ get<Path extends ApiPaths>(path: Path, options?: {
25
+ params?: PathParams<Path extends string ? Path : string>;
26
+ query?: QueryParams<Path, 'GET'>;
27
+ headers?: Record<string, string>;
28
+ signal?: AbortSignal;
29
+ }): Promise<ApiResponse<ResponseType<Path, 'GET'>>>;
30
+ /**
31
+ * Make a POST request.
32
+ */
33
+ post<Path extends ApiPaths>(path: Path, options?: {
34
+ params?: PathParams<Path extends string ? Path : string>;
35
+ body?: RequestBody<Path, 'POST'>;
36
+ headers?: Record<string, string>;
37
+ signal?: AbortSignal;
38
+ }): Promise<ApiResponse<ResponseType<Path, 'POST'>>>;
39
+ /**
40
+ * Make a PUT request.
41
+ */
42
+ put<Path extends ApiPaths>(path: Path, options?: {
43
+ params?: PathParams<Path extends string ? Path : string>;
44
+ body?: RequestBody<Path, 'PUT'>;
45
+ headers?: Record<string, string>;
46
+ signal?: AbortSignal;
47
+ }): Promise<ApiResponse<ResponseType<Path, 'PUT'>>>;
48
+ /**
49
+ * Make a PATCH request.
50
+ */
51
+ patch<Path extends ApiPaths>(path: Path, options?: {
52
+ params?: PathParams<Path extends string ? Path : string>;
53
+ body?: RequestBody<Path, 'PATCH'>;
54
+ headers?: Record<string, string>;
55
+ signal?: AbortSignal;
56
+ }): Promise<ApiResponse<ResponseType<Path, 'PATCH'>>>;
57
+ /**
58
+ * Make a DELETE request.
59
+ */
60
+ delete<Path extends ApiPaths>(path: Path, options?: {
61
+ params?: PathParams<Path extends string ? Path : string>;
62
+ headers?: Record<string, string>;
63
+ signal?: AbortSignal;
64
+ }): Promise<ApiResponse<ResponseType<Path, 'DELETE'>>>;
65
+ /**
66
+ * Build the full URL for a request.
67
+ */
68
+ private buildUrl;
69
+ /**
70
+ * Build fetch init options.
71
+ */
72
+ private buildInit;
73
+ /**
74
+ * Parse the response.
75
+ */
76
+ private parseResponse;
77
+ /**
78
+ * Create an API error.
79
+ */
80
+ private createError;
81
+ }
82
+ /**
83
+ * Create a new API client instance.
84
+ */
85
+ export declare function createClient(config?: ClientConfig): ApiClient;
86
+ /**
87
+ * Get the global API client instance.
88
+ * Creates one if it doesn't exist.
89
+ */
90
+ export declare function getGlobalClient(): ApiClient;
91
+ /**
92
+ * Configure the global API client.
93
+ */
94
+ export declare function configureClient(config: ClientConfig): void;
95
+ /**
96
+ * Type-safe API request helper.
97
+ * Uses the global client by default.
98
+ *
99
+ * @example
100
+ * const { data: posts } = await api('/api/posts').get();
101
+ * const { data: post } = await api('/api/posts').post({ body: { title: 'Hello' } });
102
+ */
103
+ export declare function api<Path extends ApiPaths>(path: Path): {
104
+ get: (options?: {
105
+ params?: Record<string, string>;
106
+ query?: Record<string, unknown>;
107
+ headers?: Record<string, string>;
108
+ signal?: AbortSignal;
109
+ }) => Promise<ApiResponse<ResponseType<Path, "GET">>>;
110
+ post: (options?: {
111
+ params?: Record<string, string>;
112
+ body?: unknown;
113
+ headers?: Record<string, string>;
114
+ signal?: AbortSignal;
115
+ }) => Promise<ApiResponse<ResponseType<Path, "POST">>>;
116
+ put: (options?: {
117
+ params?: Record<string, string>;
118
+ body?: unknown;
119
+ headers?: Record<string, string>;
120
+ signal?: AbortSignal;
121
+ }) => Promise<ApiResponse<ResponseType<Path, "PUT">>>;
122
+ patch: (options?: {
123
+ params?: Record<string, string>;
124
+ body?: unknown;
125
+ headers?: Record<string, string>;
126
+ signal?: AbortSignal;
127
+ }) => Promise<ApiResponse<ResponseType<Path, "PATCH">>>;
128
+ delete: (options?: {
129
+ params?: Record<string, string>;
130
+ headers?: Record<string, string>;
131
+ signal?: AbortSignal;
132
+ }) => Promise<ApiResponse<ResponseType<Path, "DELETE">>>;
133
+ };
134
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * @ereo/client-sdk - Main exports
3
+ */
4
+ export { ApiClient, createClient, getGlobalClient, configureClient, api, } from './client';
5
+ export type { HttpMethod, ApiRoutes, ApiPaths, MethodsFor, RequestBody, ResponseType, QueryParams, PathParams, ApiRequestConfig, ApiResponse, ApiError, ClientConfig, DefineApiTypes, } from './types';
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EACL,SAAS,EACT,YAAY,EACZ,eAAe,EACf,eAAe,EACf,GAAG,GACJ,MAAM,UAAU,CAAC;AAElB,YAAY,EACV,UAAU,EACV,SAAS,EACT,QAAQ,EACR,UAAU,EACV,WAAW,EACX,YAAY,EACZ,WAAW,EACX,UAAU,EACV,gBAAgB,EAChB,WAAW,EACX,QAAQ,EACR,YAAY,EACZ,cAAc,GACf,MAAM,SAAS,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,178 @@
1
+ // @bun
2
+ // src/client.ts
3
+ var defaultConfig = {
4
+ baseUrl: "",
5
+ timeout: 30000,
6
+ debug: false
7
+ };
8
+ var globalClient;
9
+
10
+ class ApiClient {
11
+ config;
12
+ constructor(config = {}) {
13
+ this.config = { ...defaultConfig, ...config };
14
+ }
15
+ configure(config) {
16
+ this.config = { ...this.config, ...config };
17
+ }
18
+ async request(config) {
19
+ const mergedConfig = this.config.onRequest ? await this.config.onRequest(config) : config;
20
+ const url = this.buildUrl(mergedConfig);
21
+ const init = this.buildInit(mergedConfig);
22
+ if (this.config.debug) {
23
+ console.log(`[API] ${init.method} ${url}`);
24
+ }
25
+ try {
26
+ const fetchFn = this.config.fetch || fetch;
27
+ const response = await fetchFn(url, init);
28
+ const apiResponse = await this.parseResponse(response);
29
+ if (this.config.onResponse) {
30
+ return this.config.onResponse(apiResponse);
31
+ }
32
+ return apiResponse;
33
+ } catch (error) {
34
+ const apiError = this.createError(error, config.path, config.method);
35
+ if (this.config.onError) {
36
+ await this.config.onError(apiError);
37
+ }
38
+ throw apiError;
39
+ }
40
+ }
41
+ async get(path, options) {
42
+ return this.request({
43
+ path,
44
+ method: "GET",
45
+ ...options
46
+ });
47
+ }
48
+ async post(path, options) {
49
+ return this.request({
50
+ path,
51
+ method: "POST",
52
+ ...options
53
+ });
54
+ }
55
+ async put(path, options) {
56
+ return this.request({
57
+ path,
58
+ method: "PUT",
59
+ ...options
60
+ });
61
+ }
62
+ async patch(path, options) {
63
+ return this.request({
64
+ path,
65
+ method: "PATCH",
66
+ ...options
67
+ });
68
+ }
69
+ async delete(path, options) {
70
+ return this.request({
71
+ path,
72
+ method: "DELETE",
73
+ ...options
74
+ });
75
+ }
76
+ buildUrl(config) {
77
+ let path = String(config.path);
78
+ if (config.params) {
79
+ for (const [key, value] of Object.entries(config.params)) {
80
+ path = path.replace(`[${key}]`, encodeURIComponent(String(value)));
81
+ }
82
+ }
83
+ if (config.query) {
84
+ const params = new URLSearchParams;
85
+ for (const [key, value] of Object.entries(config.query)) {
86
+ if (value !== undefined && value !== null) {
87
+ params.append(key, String(value));
88
+ }
89
+ }
90
+ const queryString = params.toString();
91
+ if (queryString) {
92
+ path += `?${queryString}`;
93
+ }
94
+ }
95
+ return `${this.config.baseUrl}${path}`;
96
+ }
97
+ buildInit(config) {
98
+ const headers = {
99
+ Accept: "application/json",
100
+ ...this.config.headers,
101
+ ...config.headers
102
+ };
103
+ const init = {
104
+ method: config.method,
105
+ headers,
106
+ signal: config.signal
107
+ };
108
+ if (config.body !== undefined) {
109
+ if (config.body instanceof FormData) {
110
+ delete headers["Content-Type"];
111
+ init.body = config.body;
112
+ } else if (config.body instanceof URLSearchParams) {
113
+ headers["Content-Type"] = "application/x-www-form-urlencoded";
114
+ init.body = config.body;
115
+ } else {
116
+ headers["Content-Type"] = "application/json";
117
+ init.body = JSON.stringify(config.body);
118
+ }
119
+ }
120
+ return init;
121
+ }
122
+ async parseResponse(response) {
123
+ const contentType = response.headers.get("content-type") || "";
124
+ let data;
125
+ if (contentType.includes("application/json")) {
126
+ data = await response.json();
127
+ } else {
128
+ data = await response.text();
129
+ }
130
+ return {
131
+ data,
132
+ status: response.status,
133
+ headers: response.headers,
134
+ ok: response.ok
135
+ };
136
+ }
137
+ createError(error, path, method) {
138
+ const apiError = new Error(error.message);
139
+ apiError.status = 0;
140
+ apiError.path = path;
141
+ apiError.method = method;
142
+ apiError.name = "ApiError";
143
+ return apiError;
144
+ }
145
+ }
146
+ function createClient(config) {
147
+ return new ApiClient(config);
148
+ }
149
+ function getGlobalClient() {
150
+ if (!globalClient) {
151
+ globalClient = new ApiClient;
152
+ }
153
+ return globalClient;
154
+ }
155
+ function configureClient(config) {
156
+ if (!globalClient) {
157
+ globalClient = new ApiClient(config);
158
+ } else {
159
+ globalClient.configure(config);
160
+ }
161
+ }
162
+ function api(path) {
163
+ const client = getGlobalClient();
164
+ return {
165
+ get: (options) => client.get(path, options),
166
+ post: (options) => client.post(path, options),
167
+ put: (options) => client.put(path, options),
168
+ patch: (options) => client.patch(path, options),
169
+ delete: (options) => client.delete(path, options)
170
+ };
171
+ }
172
+ export {
173
+ getGlobalClient,
174
+ createClient,
175
+ configureClient,
176
+ api,
177
+ ApiClient
178
+ };
@@ -0,0 +1,126 @@
1
+ /**
2
+ * @ereo/client-sdk - Type definitions for API routes
3
+ *
4
+ * These types support end-to-end type safety for API calls.
5
+ * Routes declare their API types, and the client SDK uses them.
6
+ */
7
+ export type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'HEAD' | 'OPTIONS';
8
+ /**
9
+ * Module augmentation target for API route types.
10
+ * Generated by the bundler during development.
11
+ *
12
+ * @example
13
+ * declare module '@ereo/client-sdk' {
14
+ * interface ApiRoutes {
15
+ * '/api/posts': {
16
+ * GET: {
17
+ * response: Post[];
18
+ * query?: { limit?: number; offset?: number };
19
+ * };
20
+ * POST: {
21
+ * body: CreatePostInput;
22
+ * response: Post;
23
+ * };
24
+ * };
25
+ * }
26
+ * }
27
+ */
28
+ export interface ApiRoutes {
29
+ }
30
+ /** All registered API route paths */
31
+ export type ApiPaths = keyof ApiRoutes extends never ? string : keyof ApiRoutes;
32
+ /** Get all supported methods for a path */
33
+ export type MethodsFor<Path extends ApiPaths> = Path extends keyof ApiRoutes ? keyof ApiRoutes[Path] extends HttpMethod ? keyof ApiRoutes[Path] : never : HttpMethod;
34
+ /** Get request body type for a path and method */
35
+ export type RequestBody<Path extends ApiPaths, Method extends HttpMethod = 'POST'> = Path extends keyof ApiRoutes ? Method extends keyof ApiRoutes[Path] ? ApiRoutes[Path][Method] extends {
36
+ body: infer B;
37
+ } ? B : never : never : unknown;
38
+ /** Get response type for a path and method */
39
+ export type ResponseType<Path extends ApiPaths, Method extends HttpMethod = 'GET'> = Path extends keyof ApiRoutes ? Method extends keyof ApiRoutes[Path] ? ApiRoutes[Path][Method] extends {
40
+ response: infer R;
41
+ } ? R : unknown : unknown : unknown;
42
+ /** Get query parameters type for a path and method */
43
+ export type QueryParams<Path extends ApiPaths, Method extends HttpMethod = 'GET'> = Path extends keyof ApiRoutes ? Method extends keyof ApiRoutes[Path] ? ApiRoutes[Path][Method] extends {
44
+ query: infer Q;
45
+ } ? Q : never : never : Record<string, string | number | boolean | undefined>;
46
+ /** Get path parameters type for a path */
47
+ export type PathParams<Path extends string> = Path extends `${infer _}/[${infer Param}]${infer Rest}` ? {
48
+ [K in Param]: string;
49
+ } & PathParams<Rest> : Path extends `[${infer Param}]${infer Rest}` ? {
50
+ [K in Param]: string;
51
+ } & PathParams<Rest> : Record<string, never>;
52
+ export interface ApiRequestConfig<Path extends ApiPaths = ApiPaths, Method extends HttpMethod = HttpMethod> {
53
+ /** Request path */
54
+ path: Path;
55
+ /** HTTP method */
56
+ method: Method;
57
+ /** Path parameters (for dynamic routes) */
58
+ params?: PathParams<Path extends string ? Path : string>;
59
+ /** Query parameters */
60
+ query?: QueryParams<Path, Method>;
61
+ /** Request body */
62
+ body?: RequestBody<Path, Method>;
63
+ /** Request headers */
64
+ headers?: Record<string, string>;
65
+ /** Abort signal */
66
+ signal?: AbortSignal;
67
+ }
68
+ export interface ApiResponse<T = unknown> {
69
+ /** Response data */
70
+ data: T;
71
+ /** HTTP status code */
72
+ status: number;
73
+ /** Response headers */
74
+ headers: Headers;
75
+ /** Whether response was successful */
76
+ ok: boolean;
77
+ }
78
+ export interface ApiError extends Error {
79
+ /** HTTP status code */
80
+ status: number;
81
+ /** Response data (if any) */
82
+ data?: unknown;
83
+ /** Request path */
84
+ path: string;
85
+ /** Request method */
86
+ method: string;
87
+ }
88
+ export interface ClientConfig {
89
+ /** Base URL for API requests */
90
+ baseUrl?: string;
91
+ /** Default headers */
92
+ headers?: Record<string, string>;
93
+ /** Request timeout (ms) */
94
+ timeout?: number;
95
+ /** Enable request/response logging */
96
+ debug?: boolean;
97
+ /** Custom fetch implementation */
98
+ fetch?: typeof fetch;
99
+ /** Request interceptor */
100
+ onRequest?: (config: ApiRequestConfig) => ApiRequestConfig | Promise<ApiRequestConfig>;
101
+ /** Response interceptor */
102
+ onResponse?: <T>(response: ApiResponse<T>) => ApiResponse<T> | Promise<ApiResponse<T>>;
103
+ /** Error handler */
104
+ onError?: (error: ApiError) => void | Promise<void>;
105
+ }
106
+ /**
107
+ * Helper to define API types for a route module.
108
+ * Use this to declare types that will be picked up by the client SDK.
109
+ *
110
+ * @example
111
+ * // In app/routes/api/posts.ts
112
+ * export interface ApiTypes {
113
+ * GET: {
114
+ * response: Post[];
115
+ * query: { limit?: number };
116
+ * };
117
+ * POST: {
118
+ * body: CreatePostInput;
119
+ * response: Post;
120
+ * };
121
+ * }
122
+ */
123
+ export type DefineApiTypes<T extends Record<HttpMethod, {
124
+ response: unknown;
125
+ }>> = T;
126
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,MAAM,MAAM,UAAU,GAAG,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,OAAO,GAAG,QAAQ,GAAG,MAAM,GAAG,SAAS,CAAC;AAM1F;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,WAAW,SAAS;CAAG;AAE7B,qCAAqC;AACrC,MAAM,MAAM,QAAQ,GAAG,MAAM,SAAS,SAAS,KAAK,GAAG,MAAM,GAAG,MAAM,SAAS,CAAC;AAMhF,2CAA2C;AAC3C,MAAM,MAAM,UAAU,CAAC,IAAI,SAAS,QAAQ,IAAI,IAAI,SAAS,MAAM,SAAS,GACxE,MAAM,SAAS,CAAC,IAAI,CAAC,SAAS,UAAU,GACtC,MAAM,SAAS,CAAC,IAAI,CAAC,GACrB,KAAK,GACP,UAAU,CAAC;AAEf,kDAAkD;AAClD,MAAM,MAAM,WAAW,CACrB,IAAI,SAAS,QAAQ,EACrB,MAAM,SAAS,UAAU,GAAG,MAAM,IAChC,IAAI,SAAS,MAAM,SAAS,GAC5B,MAAM,SAAS,MAAM,SAAS,CAAC,IAAI,CAAC,GAClC,SAAS,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,SAAS;IAAE,IAAI,EAAE,MAAM,CAAC,CAAA;CAAE,GAC/C,CAAC,GACD,KAAK,GACP,KAAK,GACP,OAAO,CAAC;AAEZ,8CAA8C;AAC9C,MAAM,MAAM,YAAY,CACtB,IAAI,SAAS,QAAQ,EACrB,MAAM,SAAS,UAAU,GAAG,KAAK,IAC/B,IAAI,SAAS,MAAM,SAAS,GAC5B,MAAM,SAAS,MAAM,SAAS,CAAC,IAAI,CAAC,GAClC,SAAS,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,SAAS;IAAE,QAAQ,EAAE,MAAM,CAAC,CAAA;CAAE,GACnD,CAAC,GACD,OAAO,GACT,OAAO,GACT,OAAO,CAAC;AAEZ,sDAAsD;AACtD,MAAM,MAAM,WAAW,CACrB,IAAI,SAAS,QAAQ,EACrB,MAAM,SAAS,UAAU,GAAG,KAAK,IAC/B,IAAI,SAAS,MAAM,SAAS,GAC5B,MAAM,SAAS,MAAM,SAAS,CAAC,IAAI,CAAC,GAClC,SAAS,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,SAAS;IAAE,KAAK,EAAE,MAAM,CAAC,CAAA;CAAE,GAChD,CAAC,GACD,KAAK,GACP,KAAK,GACP,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,SAAS,CAAC,CAAC;AAE1D,0CAA0C;AAC1C,MAAM,MAAM,UAAU,CAAC,IAAI,SAAS,MAAM,IAAI,IAAI,SAAS,GAAG,MAAM,CAAC,KAAK,MAAM,KAAK,IAAI,MAAM,IAAI,EAAE,GACjG;KAAG,CAAC,IAAI,KAAK,GAAG,MAAM;CAAE,GAAG,UAAU,CAAC,IAAI,CAAC,GAC3C,IAAI,SAAS,IAAI,MAAM,KAAK,IAAI,MAAM,IAAI,EAAE,GAC5C;KAAG,CAAC,IAAI,KAAK,GAAG,MAAM;CAAE,GAAG,UAAU,CAAC,IAAI,CAAC,GAC3C,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;AAM1B,MAAM,WAAW,gBAAgB,CAC/B,IAAI,SAAS,QAAQ,GAAG,QAAQ,EAChC,MAAM,SAAS,UAAU,GAAG,UAAU;IAEtC,mBAAmB;IACnB,IAAI,EAAE,IAAI,CAAC;IACX,kBAAkB;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,2CAA2C;IAC3C,MAAM,CAAC,EAAE,UAAU,CAAC,IAAI,SAAS,MAAM,GAAG,IAAI,GAAG,MAAM,CAAC,CAAC;IACzD,uBAAuB;IACvB,KAAK,CAAC,EAAE,WAAW,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IAClC,mBAAmB;IACnB,IAAI,CAAC,EAAE,WAAW,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IACjC,sBAAsB;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,mBAAmB;IACnB,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED,MAAM,WAAW,WAAW,CAAC,CAAC,GAAG,OAAO;IACtC,oBAAoB;IACpB,IAAI,EAAE,CAAC,CAAC;IACR,uBAAuB;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,uBAAuB;IACvB,OAAO,EAAE,OAAO,CAAC;IACjB,sCAAsC;IACtC,EAAE,EAAE,OAAO,CAAC;CACb;AAED,MAAM,WAAW,QAAS,SAAQ,KAAK;IACrC,uBAAuB;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,6BAA6B;IAC7B,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,mBAAmB;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,qBAAqB;IACrB,MAAM,EAAE,MAAM,CAAC;CAChB;AAMD,MAAM,WAAW,YAAY;IAC3B,gCAAgC;IAChC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,sBAAsB;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,2BAA2B;IAC3B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,sCAAsC;IACtC,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,kCAAkC;IAClC,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;IACrB,0BAA0B;IAC1B,SAAS,CAAC,EAAE,CAAC,MAAM,EAAE,gBAAgB,KAAK,gBAAgB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAC;IACvF,2BAA2B;IAC3B,UAAU,CAAC,EAAE,CAAC,CAAC,EAAE,QAAQ,EAAE,WAAW,CAAC,CAAC,CAAC,KAAK,WAAW,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC;IACvF,oBAAoB;IACpB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,QAAQ,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACrD;AAMD;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,MAAM,cAAc,CAAC,CAAC,SAAS,MAAM,CAAC,UAAU,EAAE;IAAE,QAAQ,EAAE,OAAO,CAAA;CAAE,CAAC,IAAI,CAAC,CAAC"}
package/dist/types.js ADDED
@@ -0,0 +1 @@
1
+ // @bun
package/package.json ADDED
@@ -0,0 +1,46 @@
1
+ {
2
+ "name": "@ereo/client-sdk",
3
+ "version": "0.1.6",
4
+ "license": "MIT",
5
+ "author": "Ereo Team",
6
+ "homepage": "https://ereo.dev",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/ereojs/ereo.git",
10
+ "directory": "packages/client-sdk"
11
+ },
12
+ "bugs": {
13
+ "url": "https://github.com/ereojs/ereo/issues"
14
+ },
15
+ "description": "Type-safe client SDK for EreoJS framework APIs",
16
+ "type": "module",
17
+ "main": "./dist/index.js",
18
+ "types": "./dist/index.d.ts",
19
+ "exports": {
20
+ ".": {
21
+ "types": "./dist/index.d.ts",
22
+ "import": "./dist/index.js"
23
+ },
24
+ "./types": {
25
+ "types": "./dist/types.d.ts",
26
+ "import": "./dist/types.js"
27
+ }
28
+ },
29
+ "files": [
30
+ "dist"
31
+ ],
32
+ "scripts": {
33
+ "build": "bun build ./src/index.ts ./src/types.ts --outdir ./dist --target bun --external @ereo/core && bun run build:types",
34
+ "build:types": "tsc --emitDeclarationOnly --outDir dist",
35
+ "dev": "bun build ./src/index.ts --outdir ./dist --target bun --watch",
36
+ "test": "bun test",
37
+ "typecheck": "tsc --noEmit"
38
+ },
39
+ "dependencies": {
40
+ "@ereo/core": "workspace:*"
41
+ },
42
+ "devDependencies": {
43
+ "@types/bun": "^1.1.0",
44
+ "typescript": "^5.4.0"
45
+ }
46
+ }