@spfn/core 0.1.0-alpha.88 → 0.2.0-beta.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.
- package/README.md +1046 -384
- package/dist/boss-D-fGtVgM.d.ts +187 -0
- package/dist/cache/index.d.ts +13 -33
- package/dist/cache/index.js +14 -703
- package/dist/cache/index.js.map +1 -1
- package/dist/codegen/index.d.ts +167 -17
- package/dist/codegen/index.js +76 -1419
- package/dist/codegen/index.js.map +1 -1
- package/dist/config/index.d.ts +1191 -0
- package/dist/config/index.js +264 -0
- package/dist/config/index.js.map +1 -0
- package/dist/db/index.d.ts +728 -59
- package/dist/db/index.js +1028 -1225
- package/dist/db/index.js.map +1 -1
- package/dist/env/index.d.ts +579 -308
- package/dist/env/index.js +438 -930
- package/dist/env/index.js.map +1 -1
- package/dist/errors/index.d.ts +417 -29
- package/dist/errors/index.js +359 -98
- package/dist/errors/index.js.map +1 -1
- package/dist/event/index.d.ts +108 -0
- package/dist/event/index.js +122 -0
- package/dist/event/index.js.map +1 -0
- package/dist/job/index.d.ts +172 -0
- package/dist/job/index.js +361 -0
- package/dist/job/index.js.map +1 -0
- package/dist/logger/index.d.ts +20 -79
- package/dist/logger/index.js +82 -387
- package/dist/logger/index.js.map +1 -1
- package/dist/middleware/index.d.ts +2 -11
- package/dist/middleware/index.js +49 -703
- package/dist/middleware/index.js.map +1 -1
- package/dist/nextjs/index.d.ts +120 -0
- package/dist/nextjs/index.js +416 -0
- package/dist/nextjs/index.js.map +1 -0
- package/dist/{client/nextjs/index.d.ts → nextjs/server.d.ts} +288 -262
- package/dist/nextjs/server.js +568 -0
- package/dist/nextjs/server.js.map +1 -0
- package/dist/route/index.d.ts +686 -25
- package/dist/route/index.js +440 -1287
- package/dist/route/index.js.map +1 -1
- package/dist/route/types.d.ts +38 -0
- package/dist/route/types.js +3 -0
- package/dist/route/types.js.map +1 -0
- package/dist/server/index.d.ts +201 -67
- package/dist/server/index.js +921 -3182
- package/dist/server/index.js.map +1 -1
- package/dist/types-BGl4QL1w.d.ts +77 -0
- package/dist/types-DRG2XMTR.d.ts +157 -0
- package/package.json +52 -47
- package/dist/auto-loader-JFaZ9gON.d.ts +0 -80
- package/dist/client/index.d.ts +0 -358
- package/dist/client/index.js +0 -357
- package/dist/client/index.js.map +0 -1
- package/dist/client/nextjs/index.js +0 -371
- package/dist/client/nextjs/index.js.map +0 -1
- package/dist/codegen/generators/index.d.ts +0 -19
- package/dist/codegen/generators/index.js +0 -1404
- package/dist/codegen/generators/index.js.map +0 -1
- package/dist/database-errors-BNNmLTJE.d.ts +0 -86
- package/dist/events/index.d.ts +0 -183
- package/dist/events/index.js +0 -77
- package/dist/events/index.js.map +0 -1
- package/dist/index-DHiAqhKv.d.ts +0 -101
- package/dist/index.d.ts +0 -8
- package/dist/index.js +0 -3674
- package/dist/index.js.map +0 -1
- package/dist/types/index.d.ts +0 -121
- package/dist/types/index.js +0 -38
- package/dist/types/index.js.map +0 -1
- package/dist/types-BXibIEyj.d.ts +0 -60
package/dist/client/index.d.ts
DELETED
|
@@ -1,358 +0,0 @@
|
|
|
1
|
-
import '../auto-loader-JFaZ9gON.js';
|
|
2
|
-
import { a as RouteContract, I as InferContract } from '../types-BXibIEyj.js';
|
|
3
|
-
import 'hono';
|
|
4
|
-
import 'hono/utils/http-status';
|
|
5
|
-
import '@sinclair/typebox';
|
|
6
|
-
import '../types/index.js';
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Contract-Based API Client
|
|
10
|
-
*
|
|
11
|
-
* Type-safe HTTP client that works with RouteContract for full end-to-end type safety
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
type RequestInterceptor = (url: string, init: RequestInit) => Promise<RequestInit> | RequestInit;
|
|
15
|
-
interface ClientConfig {
|
|
16
|
-
/**
|
|
17
|
-
* API base URL (e.g., http://localhost:4000)
|
|
18
|
-
*/
|
|
19
|
-
baseUrl?: string;
|
|
20
|
-
/**
|
|
21
|
-
* Default headers to include in all requests
|
|
22
|
-
*/
|
|
23
|
-
headers?: Record<string, string>;
|
|
24
|
-
/**
|
|
25
|
-
* Request timeout in milliseconds
|
|
26
|
-
*/
|
|
27
|
-
timeout?: number;
|
|
28
|
-
/**
|
|
29
|
-
* Custom fetch implementation
|
|
30
|
-
*/
|
|
31
|
-
fetch?: typeof fetch;
|
|
32
|
-
}
|
|
33
|
-
interface CallOptions<TContract extends RouteContract> {
|
|
34
|
-
params?: InferContract<TContract>['params'];
|
|
35
|
-
query?: InferContract<TContract>['query'];
|
|
36
|
-
body?: InferContract<TContract>['body'];
|
|
37
|
-
headers?: Record<string, string>;
|
|
38
|
-
baseUrl?: string;
|
|
39
|
-
/**
|
|
40
|
-
* Additional fetch options (extends RequestInit)
|
|
41
|
-
*
|
|
42
|
-
* Can be used for environment-specific options like Next.js cache/next
|
|
43
|
-
*
|
|
44
|
-
* @example
|
|
45
|
-
* ```ts
|
|
46
|
-
* // Next.js time-based revalidation
|
|
47
|
-
* { fetchOptions: { next: { revalidate: 60 } } }
|
|
48
|
-
*
|
|
49
|
-
* // Next.js disable cache
|
|
50
|
-
* { fetchOptions: { cache: 'no-store' } }
|
|
51
|
-
*
|
|
52
|
-
* // Next.js on-demand revalidation
|
|
53
|
-
* { fetchOptions: { next: { tags: ['products'] } } }
|
|
54
|
-
* ```
|
|
55
|
-
*/
|
|
56
|
-
fetchOptions?: Record<string, any>;
|
|
57
|
-
}
|
|
58
|
-
/**
|
|
59
|
-
* API Client Error
|
|
60
|
-
*/
|
|
61
|
-
declare class ApiClientError extends Error {
|
|
62
|
-
readonly status: number;
|
|
63
|
-
readonly url: string;
|
|
64
|
-
readonly response?: unknown | undefined;
|
|
65
|
-
readonly errorType?: "timeout" | "network" | "http" | undefined;
|
|
66
|
-
constructor(message: string, status: number, url: string, response?: unknown | undefined, errorType?: "timeout" | "network" | "http" | undefined);
|
|
67
|
-
}
|
|
68
|
-
/**
|
|
69
|
-
* Contract-based API Client
|
|
70
|
-
*/
|
|
71
|
-
declare class ContractClient {
|
|
72
|
-
private readonly config;
|
|
73
|
-
private readonly interceptors;
|
|
74
|
-
constructor(config?: ClientConfig);
|
|
75
|
-
/**
|
|
76
|
-
* Add request interceptor
|
|
77
|
-
*/
|
|
78
|
-
use(interceptor: RequestInterceptor): void;
|
|
79
|
-
/**
|
|
80
|
-
* Make a type-safe API call using a contract
|
|
81
|
-
*
|
|
82
|
-
* @param contract - Route contract with absolute path
|
|
83
|
-
* @param options - Call options (params, query, body, headers)
|
|
84
|
-
*/
|
|
85
|
-
call<TContract extends RouteContract>(contract: TContract, options?: CallOptions<TContract>): Promise<InferContract<TContract>['response']>;
|
|
86
|
-
/**
|
|
87
|
-
* Create a new client with merged configuration
|
|
88
|
-
*/
|
|
89
|
-
withConfig(config: Partial<ClientConfig>): ContractClient;
|
|
90
|
-
private static buildUrl;
|
|
91
|
-
private static buildQuery;
|
|
92
|
-
private static getHttpMethod;
|
|
93
|
-
private static isFormData;
|
|
94
|
-
}
|
|
95
|
-
/**
|
|
96
|
-
* Type guard for timeout errors
|
|
97
|
-
*
|
|
98
|
-
* @example
|
|
99
|
-
* ```ts
|
|
100
|
-
* try {
|
|
101
|
-
* await api.users.getById({ params: { id: '123' } });
|
|
102
|
-
* } catch (error) {
|
|
103
|
-
* if (isTimeoutError(error)) {
|
|
104
|
-
* console.error('Request timed out, retrying...');
|
|
105
|
-
* // Implement retry logic
|
|
106
|
-
* }
|
|
107
|
-
* }
|
|
108
|
-
* ```
|
|
109
|
-
*/
|
|
110
|
-
declare function isTimeoutError(error: unknown): error is ApiClientError;
|
|
111
|
-
/**
|
|
112
|
-
* Type guard for network errors
|
|
113
|
-
*
|
|
114
|
-
* @example
|
|
115
|
-
* ```ts
|
|
116
|
-
* try {
|
|
117
|
-
* await api.users.list();
|
|
118
|
-
* } catch (error) {
|
|
119
|
-
* if (isNetworkError(error)) {
|
|
120
|
-
* showOfflineMessage();
|
|
121
|
-
* }
|
|
122
|
-
* }
|
|
123
|
-
* ```
|
|
124
|
-
*/
|
|
125
|
-
declare function isNetworkError(error: unknown): error is ApiClientError;
|
|
126
|
-
/**
|
|
127
|
-
* Type guard for HTTP errors (4xx, 5xx)
|
|
128
|
-
*
|
|
129
|
-
* @example
|
|
130
|
-
* ```ts
|
|
131
|
-
* try {
|
|
132
|
-
* await api.users.create({ body: userData });
|
|
133
|
-
* } catch (error) {
|
|
134
|
-
* if (isHttpError(error)) {
|
|
135
|
-
* if (error.status === 401) {
|
|
136
|
-
* redirectToLogin();
|
|
137
|
-
* } else if (error.status === 404) {
|
|
138
|
-
* showNotFoundMessage();
|
|
139
|
-
* }
|
|
140
|
-
* }
|
|
141
|
-
* }
|
|
142
|
-
* ```
|
|
143
|
-
*/
|
|
144
|
-
declare function isHttpError(error: unknown): error is ApiClientError;
|
|
145
|
-
/**
|
|
146
|
-
* Check if error is a specific server error type
|
|
147
|
-
*
|
|
148
|
-
* @example
|
|
149
|
-
* ```ts
|
|
150
|
-
* try {
|
|
151
|
-
* await api.workflows.getById({ params: { uuid: 'xxx' } });
|
|
152
|
-
* } catch (error) {
|
|
153
|
-
* if (isServerError(error, 'NotFoundError')) {
|
|
154
|
-
* showNotFoundMessage();
|
|
155
|
-
* } else if (isServerError(error, 'ValidationError')) {
|
|
156
|
-
* showValidationErrors(getServerErrorDetails(error));
|
|
157
|
-
* }
|
|
158
|
-
* }
|
|
159
|
-
* ```
|
|
160
|
-
*/
|
|
161
|
-
declare function isServerError(error: unknown, errorType: string): error is ApiClientError;
|
|
162
|
-
/**
|
|
163
|
-
* Get server error type from ApiClientError
|
|
164
|
-
*
|
|
165
|
-
* @example
|
|
166
|
-
* ```ts
|
|
167
|
-
* const errorType = getServerErrorType(error);
|
|
168
|
-
* // 'NotFoundError', 'ValidationError', 'PaymentFailedError', etc.
|
|
169
|
-
* ```
|
|
170
|
-
*/
|
|
171
|
-
declare function getServerErrorType(error: ApiClientError): string | undefined;
|
|
172
|
-
/**
|
|
173
|
-
* Get server error details from ApiClientError
|
|
174
|
-
*
|
|
175
|
-
* @example
|
|
176
|
-
* ```ts
|
|
177
|
-
* if (isServerError(error, 'PaymentFailedError')) {
|
|
178
|
-
* const details = getServerErrorDetails(error);
|
|
179
|
-
* console.log('Payment ID:', details.paymentId);
|
|
180
|
-
* }
|
|
181
|
-
* ```
|
|
182
|
-
*/
|
|
183
|
-
declare function getServerErrorDetails<T = any>(error: ApiClientError): T | undefined;
|
|
184
|
-
|
|
185
|
-
/**
|
|
186
|
-
* Universal API Client
|
|
187
|
-
*
|
|
188
|
-
* Automatically routes requests based on execution environment:
|
|
189
|
-
* - Server Environment: Direct call to SPFN API (internal network)
|
|
190
|
-
* - Browser Environment: Proxies through Next.js API Route (cookie forwarding)
|
|
191
|
-
*/
|
|
192
|
-
|
|
193
|
-
/**
|
|
194
|
-
* Universal Client Configuration
|
|
195
|
-
*/
|
|
196
|
-
interface UniversalClientConfig {
|
|
197
|
-
/**
|
|
198
|
-
* SPFN API server URL (for server-side direct calls)
|
|
199
|
-
*
|
|
200
|
-
* @default process.env.SERVER_API_URL || process.env.SPFN_API_URL || 'http://localhost:8790'
|
|
201
|
-
*/
|
|
202
|
-
apiUrl?: string;
|
|
203
|
-
/**
|
|
204
|
-
* Next.js API route base path (for client-side proxy calls)
|
|
205
|
-
*
|
|
206
|
-
* @default '/api/actions'
|
|
207
|
-
* @example '/api/proxy'
|
|
208
|
-
*/
|
|
209
|
-
proxyBasePath?: string;
|
|
210
|
-
/**
|
|
211
|
-
* Additional headers to include in all requests
|
|
212
|
-
*/
|
|
213
|
-
headers?: Record<string, string>;
|
|
214
|
-
/**
|
|
215
|
-
* Request timeout in milliseconds
|
|
216
|
-
*
|
|
217
|
-
* @default 30000
|
|
218
|
-
*/
|
|
219
|
-
timeout?: number;
|
|
220
|
-
/**
|
|
221
|
-
* Custom fetch implementation
|
|
222
|
-
*/
|
|
223
|
-
fetch?: typeof fetch;
|
|
224
|
-
}
|
|
225
|
-
/**
|
|
226
|
-
* Universal API Client
|
|
227
|
-
*
|
|
228
|
-
* Automatically detects execution environment and routes requests accordingly:
|
|
229
|
-
*
|
|
230
|
-
* **Server Environment** (Next.js Server Components, API Routes):
|
|
231
|
-
* - Direct HTTP call to SPFN API server
|
|
232
|
-
* - Uses internal network (e.g., http://localhost:8790)
|
|
233
|
-
* - No proxy overhead
|
|
234
|
-
*
|
|
235
|
-
* **Browser Environment** (Next.js Client Components):
|
|
236
|
-
* - Routes through Next.js API Route proxy (e.g., /api/proxy/*)
|
|
237
|
-
* - Enables HttpOnly cookie forwarding
|
|
238
|
-
* - Maintains CORS security
|
|
239
|
-
*
|
|
240
|
-
* @example
|
|
241
|
-
* ```typescript
|
|
242
|
-
* // Server Component - direct call
|
|
243
|
-
* import { createUniversalClient } from '@spfn/core/client';
|
|
244
|
-
* const client = createUniversalClient();
|
|
245
|
-
* const result = await client.call(loginContract, { body: {...} });
|
|
246
|
-
*
|
|
247
|
-
* // Client Component - proxied call (automatic)
|
|
248
|
-
* 'use client';
|
|
249
|
-
* const client = createUniversalClient();
|
|
250
|
-
* const result = await client.call(loginContract, { body: {...} }); // Goes through /api/proxy
|
|
251
|
-
* ```
|
|
252
|
-
*/
|
|
253
|
-
declare class UniversalClient {
|
|
254
|
-
private readonly directClient;
|
|
255
|
-
private readonly proxyBasePath;
|
|
256
|
-
private readonly isServer;
|
|
257
|
-
private readonly fetchImpl;
|
|
258
|
-
constructor(config?: UniversalClientConfig);
|
|
259
|
-
/**
|
|
260
|
-
* Make a type-safe API call using a contract
|
|
261
|
-
*
|
|
262
|
-
* Automatically routes based on environment:
|
|
263
|
-
* - Server: Direct SPFN API call
|
|
264
|
-
* - Browser: Next.js API Route proxy
|
|
265
|
-
*
|
|
266
|
-
* @param contract - Route contract with absolute path
|
|
267
|
-
* @param options - Call options (params, query, body, headers)
|
|
268
|
-
*/
|
|
269
|
-
call<TContract extends RouteContract>(contract: TContract, options?: CallOptions<TContract>): Promise<InferContract<TContract>['response']>;
|
|
270
|
-
/**
|
|
271
|
-
* Call via Next.js API Route proxy (client-side)
|
|
272
|
-
*
|
|
273
|
-
* Routes request through /api/proxy/[...path] to enable:
|
|
274
|
-
* - HttpOnly cookie forwarding
|
|
275
|
-
* - CORS security
|
|
276
|
-
* - Server-side session management
|
|
277
|
-
*
|
|
278
|
-
* @private
|
|
279
|
-
*/
|
|
280
|
-
private callViaProxy;
|
|
281
|
-
/**
|
|
282
|
-
* Build URL path with parameter substitution
|
|
283
|
-
*/
|
|
284
|
-
private buildUrlPath;
|
|
285
|
-
/**
|
|
286
|
-
* Build query string from query parameters
|
|
287
|
-
*/
|
|
288
|
-
private buildQueryString;
|
|
289
|
-
/**
|
|
290
|
-
* Get HTTP method from contract or infer from options
|
|
291
|
-
*/
|
|
292
|
-
private getHttpMethod;
|
|
293
|
-
/**
|
|
294
|
-
* Check if body is FormData
|
|
295
|
-
*/
|
|
296
|
-
private isFormData;
|
|
297
|
-
/**
|
|
298
|
-
* Check if currently running in server environment
|
|
299
|
-
*/
|
|
300
|
-
isServerEnv(): boolean;
|
|
301
|
-
/**
|
|
302
|
-
* Create a new client with merged configuration
|
|
303
|
-
*/
|
|
304
|
-
withConfig(config: Partial<UniversalClientConfig>): UniversalClient;
|
|
305
|
-
}
|
|
306
|
-
/**
|
|
307
|
-
* Create a new universal API client
|
|
308
|
-
*
|
|
309
|
-
* @example
|
|
310
|
-
* ```typescript
|
|
311
|
-
* // Default configuration
|
|
312
|
-
* const client = createUniversalClient();
|
|
313
|
-
*
|
|
314
|
-
* // Custom configuration
|
|
315
|
-
* const client = createUniversalClient({
|
|
316
|
-
* apiUrl: 'http://localhost:4000',
|
|
317
|
-
* proxyBasePath: '/api/spfn',
|
|
318
|
-
* headers: { 'X-App-Version': '1.0.0' },
|
|
319
|
-
* });
|
|
320
|
-
* ```
|
|
321
|
-
*/
|
|
322
|
-
declare function createUniversalClient(config?: UniversalClientConfig): UniversalClient;
|
|
323
|
-
/**
|
|
324
|
-
* Configure the global universal client instance
|
|
325
|
-
*
|
|
326
|
-
* Call this in your app initialization to set default configuration
|
|
327
|
-
* for all auto-generated API calls.
|
|
328
|
-
*
|
|
329
|
-
* @example
|
|
330
|
-
* ```typescript
|
|
331
|
-
* // In app initialization (layout.tsx, _app.tsx, etc)
|
|
332
|
-
* import { configureUniversalClient } from '@spfn/core/client';
|
|
333
|
-
*
|
|
334
|
-
* configureUniversalClient({
|
|
335
|
-
* apiUrl: process.env.NEXT_PUBLIC_API_URL || 'http://localhost:8790',
|
|
336
|
-
* proxyBasePath: '/api/proxy',
|
|
337
|
-
* headers: {
|
|
338
|
-
* 'X-App-Version': '1.0.0'
|
|
339
|
-
* }
|
|
340
|
-
* });
|
|
341
|
-
* ```
|
|
342
|
-
*/
|
|
343
|
-
declare function configureUniversalClient(config: UniversalClientConfig): void;
|
|
344
|
-
/**
|
|
345
|
-
* Get the global universal client instance
|
|
346
|
-
*
|
|
347
|
-
* Creates a default instance if not configured
|
|
348
|
-
*/
|
|
349
|
-
declare function getUniversalClient(): UniversalClient;
|
|
350
|
-
/**
|
|
351
|
-
* Global universal client singleton with Proxy
|
|
352
|
-
*
|
|
353
|
-
* This client can be configured using configureUniversalClient() before use.
|
|
354
|
-
* Used by auto-generated API client code.
|
|
355
|
-
*/
|
|
356
|
-
declare const universalClient: UniversalClient;
|
|
357
|
-
|
|
358
|
-
export { ApiClientError, type CallOptions, type UniversalClientConfig as ClientConfig, ContractClient, type RequestInterceptor, UniversalClient, type UniversalClientConfig, universalClient as client, configureUniversalClient as configureClient, configureUniversalClient, createUniversalClient as createClient, createUniversalClient, getServerErrorDetails, getServerErrorType, getUniversalClient, isHttpError, isNetworkError, isServerError, isTimeoutError, universalClient };
|
package/dist/client/index.js
DELETED
|
@@ -1,357 +0,0 @@
|
|
|
1
|
-
// src/client/contract-client.ts
|
|
2
|
-
var ApiClientError = class extends Error {
|
|
3
|
-
constructor(message, status, url, response, errorType) {
|
|
4
|
-
super(message);
|
|
5
|
-
this.status = status;
|
|
6
|
-
this.url = url;
|
|
7
|
-
this.response = response;
|
|
8
|
-
this.errorType = errorType;
|
|
9
|
-
this.name = "ApiClientError";
|
|
10
|
-
}
|
|
11
|
-
};
|
|
12
|
-
var ContractClient = class _ContractClient {
|
|
13
|
-
config;
|
|
14
|
-
interceptors = [];
|
|
15
|
-
constructor(config = {}) {
|
|
16
|
-
this.config = {
|
|
17
|
-
baseUrl: config.baseUrl || process.env.SERVER_API_URL || process.env.NEXT_PUBLIC_API_URL || "http://localhost:4000",
|
|
18
|
-
headers: config.headers || {},
|
|
19
|
-
timeout: config.timeout || 3e4,
|
|
20
|
-
fetch: config.fetch || globalThis.fetch.bind(globalThis)
|
|
21
|
-
};
|
|
22
|
-
}
|
|
23
|
-
/**
|
|
24
|
-
* Add request interceptor
|
|
25
|
-
*/
|
|
26
|
-
use(interceptor) {
|
|
27
|
-
this.interceptors.push(interceptor);
|
|
28
|
-
}
|
|
29
|
-
/**
|
|
30
|
-
* Make a type-safe API call using a contract
|
|
31
|
-
*
|
|
32
|
-
* @param contract - Route contract with absolute path
|
|
33
|
-
* @param options - Call options (params, query, body, headers)
|
|
34
|
-
*/
|
|
35
|
-
async call(contract, options) {
|
|
36
|
-
const baseUrl = options?.baseUrl || this.config.baseUrl;
|
|
37
|
-
const urlPath = _ContractClient.buildUrl(
|
|
38
|
-
contract.path,
|
|
39
|
-
options?.params
|
|
40
|
-
);
|
|
41
|
-
const queryString = _ContractClient.buildQuery(
|
|
42
|
-
options?.query
|
|
43
|
-
);
|
|
44
|
-
const url = `${baseUrl}${urlPath}${queryString}`;
|
|
45
|
-
const method = _ContractClient.getHttpMethod(contract, options);
|
|
46
|
-
const headers = {
|
|
47
|
-
...this.config.headers,
|
|
48
|
-
...options?.headers
|
|
49
|
-
};
|
|
50
|
-
const isFormData = _ContractClient.isFormData(options?.body);
|
|
51
|
-
if (options?.body !== void 0 && !isFormData && !headers["Content-Type"]) {
|
|
52
|
-
headers["Content-Type"] = "application/json";
|
|
53
|
-
}
|
|
54
|
-
let init = {
|
|
55
|
-
method,
|
|
56
|
-
headers,
|
|
57
|
-
...options?.fetchOptions
|
|
58
|
-
// Spread environment-specific options (e.g., Next.js cache/next)
|
|
59
|
-
};
|
|
60
|
-
if (options?.body !== void 0) {
|
|
61
|
-
init.body = isFormData ? options.body : JSON.stringify(options.body);
|
|
62
|
-
}
|
|
63
|
-
const controller = new AbortController();
|
|
64
|
-
const timeoutId = setTimeout(() => controller.abort(), this.config.timeout);
|
|
65
|
-
init.signal = controller.signal;
|
|
66
|
-
for (const interceptor of this.interceptors) {
|
|
67
|
-
init = await interceptor(url, init);
|
|
68
|
-
}
|
|
69
|
-
const response = await this.config.fetch(url, init).catch((error) => {
|
|
70
|
-
clearTimeout(timeoutId);
|
|
71
|
-
if (error instanceof Error && error.name === "AbortError") {
|
|
72
|
-
throw new ApiClientError(
|
|
73
|
-
`Request timed out after ${this.config.timeout}ms`,
|
|
74
|
-
0,
|
|
75
|
-
url,
|
|
76
|
-
void 0,
|
|
77
|
-
"timeout"
|
|
78
|
-
);
|
|
79
|
-
}
|
|
80
|
-
if (error instanceof Error) {
|
|
81
|
-
throw new ApiClientError(
|
|
82
|
-
`Network error: ${error.message}`,
|
|
83
|
-
0,
|
|
84
|
-
url,
|
|
85
|
-
void 0,
|
|
86
|
-
"network"
|
|
87
|
-
);
|
|
88
|
-
}
|
|
89
|
-
throw error;
|
|
90
|
-
});
|
|
91
|
-
clearTimeout(timeoutId);
|
|
92
|
-
if (!response.ok) {
|
|
93
|
-
const errorBody = await response.json().catch(() => null);
|
|
94
|
-
throw new ApiClientError(
|
|
95
|
-
`${method} ${urlPath} failed: ${response.status} ${response.statusText}`,
|
|
96
|
-
response.status,
|
|
97
|
-
url,
|
|
98
|
-
errorBody,
|
|
99
|
-
"http"
|
|
100
|
-
);
|
|
101
|
-
}
|
|
102
|
-
const data = await response.json();
|
|
103
|
-
return data;
|
|
104
|
-
}
|
|
105
|
-
/**
|
|
106
|
-
* Create a new client with merged configuration
|
|
107
|
-
*/
|
|
108
|
-
withConfig(config) {
|
|
109
|
-
return new _ContractClient({
|
|
110
|
-
baseUrl: config.baseUrl || this.config.baseUrl,
|
|
111
|
-
headers: { ...this.config.headers, ...config.headers },
|
|
112
|
-
timeout: config.timeout || this.config.timeout,
|
|
113
|
-
fetch: config.fetch || this.config.fetch
|
|
114
|
-
});
|
|
115
|
-
}
|
|
116
|
-
static buildUrl(path, params) {
|
|
117
|
-
if (!params) return path;
|
|
118
|
-
let url = path;
|
|
119
|
-
for (const [key, value] of Object.entries(params)) {
|
|
120
|
-
url = url.replace(`:${key}`, String(value));
|
|
121
|
-
}
|
|
122
|
-
return url;
|
|
123
|
-
}
|
|
124
|
-
static buildQuery(query) {
|
|
125
|
-
if (!query || Object.keys(query).length === 0) return "";
|
|
126
|
-
const params = new URLSearchParams();
|
|
127
|
-
for (const [key, value] of Object.entries(query)) {
|
|
128
|
-
if (Array.isArray(value)) {
|
|
129
|
-
value.forEach((v) => params.append(key, String(v)));
|
|
130
|
-
} else if (value !== void 0 && value !== null) {
|
|
131
|
-
params.append(key, String(value));
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
const queryString = params.toString();
|
|
135
|
-
return queryString ? `?${queryString}` : "";
|
|
136
|
-
}
|
|
137
|
-
static getHttpMethod(contract, options) {
|
|
138
|
-
if ("method" in contract && typeof contract.method === "string") {
|
|
139
|
-
return contract.method.toUpperCase();
|
|
140
|
-
}
|
|
141
|
-
if (options?.body !== void 0) {
|
|
142
|
-
return "POST";
|
|
143
|
-
}
|
|
144
|
-
return "GET";
|
|
145
|
-
}
|
|
146
|
-
static isFormData(body) {
|
|
147
|
-
return body instanceof FormData;
|
|
148
|
-
}
|
|
149
|
-
};
|
|
150
|
-
var _clientInstance = new ContractClient();
|
|
151
|
-
new Proxy({}, {
|
|
152
|
-
get(_target, prop) {
|
|
153
|
-
return _clientInstance[prop];
|
|
154
|
-
}
|
|
155
|
-
});
|
|
156
|
-
function isTimeoutError(error) {
|
|
157
|
-
return error instanceof ApiClientError && error.errorType === "timeout";
|
|
158
|
-
}
|
|
159
|
-
function isNetworkError(error) {
|
|
160
|
-
return error instanceof ApiClientError && error.errorType === "network";
|
|
161
|
-
}
|
|
162
|
-
function isHttpError(error) {
|
|
163
|
-
return error instanceof ApiClientError && error.errorType === "http";
|
|
164
|
-
}
|
|
165
|
-
function isServerError(error, errorType) {
|
|
166
|
-
if (!isHttpError(error)) return false;
|
|
167
|
-
const response = error.response;
|
|
168
|
-
return response?.error?.type === errorType;
|
|
169
|
-
}
|
|
170
|
-
function getServerErrorType(error) {
|
|
171
|
-
const response = error.response;
|
|
172
|
-
return response?.error?.type;
|
|
173
|
-
}
|
|
174
|
-
function getServerErrorDetails(error) {
|
|
175
|
-
const response = error.response;
|
|
176
|
-
return response?.error?.details;
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
// src/client/universal-client.ts
|
|
180
|
-
function isServerEnvironment() {
|
|
181
|
-
return typeof window === "undefined";
|
|
182
|
-
}
|
|
183
|
-
var UniversalClient = class _UniversalClient {
|
|
184
|
-
directClient;
|
|
185
|
-
proxyBasePath;
|
|
186
|
-
isServer;
|
|
187
|
-
fetchImpl;
|
|
188
|
-
constructor(config = {}) {
|
|
189
|
-
this.isServer = isServerEnvironment();
|
|
190
|
-
this.directClient = new ContractClient({
|
|
191
|
-
baseUrl: config.apiUrl,
|
|
192
|
-
headers: config.headers,
|
|
193
|
-
timeout: config.timeout,
|
|
194
|
-
fetch: config.fetch
|
|
195
|
-
});
|
|
196
|
-
this.proxyBasePath = config.proxyBasePath || "/api/actions";
|
|
197
|
-
this.fetchImpl = config.fetch || globalThis.fetch.bind(globalThis);
|
|
198
|
-
}
|
|
199
|
-
/**
|
|
200
|
-
* Make a type-safe API call using a contract
|
|
201
|
-
*
|
|
202
|
-
* Automatically routes based on environment:
|
|
203
|
-
* - Server: Direct SPFN API call
|
|
204
|
-
* - Browser: Next.js API Route proxy
|
|
205
|
-
*
|
|
206
|
-
* @param contract - Route contract with absolute path
|
|
207
|
-
* @param options - Call options (params, query, body, headers)
|
|
208
|
-
*/
|
|
209
|
-
async call(contract, options) {
|
|
210
|
-
if (this.isServer) {
|
|
211
|
-
return this.directClient.call(contract, options);
|
|
212
|
-
} else {
|
|
213
|
-
return this.callViaProxy(contract, options);
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
/**
|
|
217
|
-
* Call via Next.js API Route proxy (client-side)
|
|
218
|
-
*
|
|
219
|
-
* Routes request through /api/proxy/[...path] to enable:
|
|
220
|
-
* - HttpOnly cookie forwarding
|
|
221
|
-
* - CORS security
|
|
222
|
-
* - Server-side session management
|
|
223
|
-
*
|
|
224
|
-
* @private
|
|
225
|
-
*/
|
|
226
|
-
async callViaProxy(contract, options) {
|
|
227
|
-
const path = this.buildUrlPath(
|
|
228
|
-
contract.path,
|
|
229
|
-
options?.params
|
|
230
|
-
);
|
|
231
|
-
const queryString = this.buildQueryString(
|
|
232
|
-
options?.query
|
|
233
|
-
);
|
|
234
|
-
const proxyUrl = `${this.proxyBasePath}${path}${queryString}`;
|
|
235
|
-
const method = this.getHttpMethod(contract, options);
|
|
236
|
-
const headers = {
|
|
237
|
-
...options?.headers
|
|
238
|
-
};
|
|
239
|
-
const isFormData = this.isFormData(options?.body);
|
|
240
|
-
if (options?.body !== void 0 && !isFormData && !headers["Content-Type"]) {
|
|
241
|
-
headers["Content-Type"] = "application/json";
|
|
242
|
-
}
|
|
243
|
-
const init = {
|
|
244
|
-
method,
|
|
245
|
-
headers,
|
|
246
|
-
credentials: "include",
|
|
247
|
-
// Important: Include cookies for session
|
|
248
|
-
...options?.fetchOptions
|
|
249
|
-
// Spread environment-specific options (e.g., Next.js cache/next)
|
|
250
|
-
};
|
|
251
|
-
if (options?.body !== void 0) {
|
|
252
|
-
init.body = isFormData ? options.body : JSON.stringify(options.body);
|
|
253
|
-
}
|
|
254
|
-
const response = await this.fetchImpl(proxyUrl, init);
|
|
255
|
-
if (!response.ok) {
|
|
256
|
-
const errorBody = await response.json().catch(() => null);
|
|
257
|
-
throw new ApiClientError(
|
|
258
|
-
`${method} ${path} failed: ${response.status} ${response.statusText}`,
|
|
259
|
-
response.status,
|
|
260
|
-
proxyUrl,
|
|
261
|
-
errorBody,
|
|
262
|
-
"http"
|
|
263
|
-
);
|
|
264
|
-
}
|
|
265
|
-
const data = await response.json();
|
|
266
|
-
return data;
|
|
267
|
-
}
|
|
268
|
-
/**
|
|
269
|
-
* Build URL path with parameter substitution
|
|
270
|
-
*/
|
|
271
|
-
buildUrlPath(path, params) {
|
|
272
|
-
if (!params) return path;
|
|
273
|
-
let url = path;
|
|
274
|
-
for (const [key, value] of Object.entries(params)) {
|
|
275
|
-
url = url.replace(`:${key}`, String(value));
|
|
276
|
-
}
|
|
277
|
-
return url;
|
|
278
|
-
}
|
|
279
|
-
/**
|
|
280
|
-
* Build query string from query parameters
|
|
281
|
-
*/
|
|
282
|
-
buildQueryString(query) {
|
|
283
|
-
if (!query || Object.keys(query).length === 0) return "";
|
|
284
|
-
const params = new URLSearchParams();
|
|
285
|
-
for (const [key, value] of Object.entries(query)) {
|
|
286
|
-
if (Array.isArray(value)) {
|
|
287
|
-
value.forEach((v) => params.append(key, String(v)));
|
|
288
|
-
} else if (value !== void 0 && value !== null) {
|
|
289
|
-
params.append(key, String(value));
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
const queryString = params.toString();
|
|
293
|
-
return queryString ? `?${queryString}` : "";
|
|
294
|
-
}
|
|
295
|
-
/**
|
|
296
|
-
* Get HTTP method from contract or infer from options
|
|
297
|
-
*/
|
|
298
|
-
getHttpMethod(contract, options) {
|
|
299
|
-
if ("method" in contract && typeof contract.method === "string") {
|
|
300
|
-
return contract.method.toUpperCase();
|
|
301
|
-
}
|
|
302
|
-
if (options?.body !== void 0) {
|
|
303
|
-
return "POST";
|
|
304
|
-
}
|
|
305
|
-
return "GET";
|
|
306
|
-
}
|
|
307
|
-
/**
|
|
308
|
-
* Check if body is FormData
|
|
309
|
-
*/
|
|
310
|
-
isFormData(body) {
|
|
311
|
-
return typeof FormData !== "undefined" && body instanceof FormData;
|
|
312
|
-
}
|
|
313
|
-
/**
|
|
314
|
-
* Check if currently running in server environment
|
|
315
|
-
*/
|
|
316
|
-
isServerEnv() {
|
|
317
|
-
return this.isServer;
|
|
318
|
-
}
|
|
319
|
-
/**
|
|
320
|
-
* Create a new client with merged configuration
|
|
321
|
-
*/
|
|
322
|
-
withConfig(config) {
|
|
323
|
-
return new _UniversalClient({
|
|
324
|
-
apiUrl: config.apiUrl || this.directClient["config"].baseUrl,
|
|
325
|
-
proxyBasePath: config.proxyBasePath || this.proxyBasePath,
|
|
326
|
-
headers: {
|
|
327
|
-
...this.directClient["config"].headers,
|
|
328
|
-
...config.headers
|
|
329
|
-
},
|
|
330
|
-
timeout: config.timeout || this.directClient["config"].timeout,
|
|
331
|
-
fetch: config.fetch || this.fetchImpl
|
|
332
|
-
});
|
|
333
|
-
}
|
|
334
|
-
};
|
|
335
|
-
function createUniversalClient(config) {
|
|
336
|
-
return new UniversalClient(config);
|
|
337
|
-
}
|
|
338
|
-
var _universalClientInstance = null;
|
|
339
|
-
function configureUniversalClient(config) {
|
|
340
|
-
_universalClientInstance = new UniversalClient(config);
|
|
341
|
-
}
|
|
342
|
-
function getUniversalClient() {
|
|
343
|
-
if (!_universalClientInstance) {
|
|
344
|
-
_universalClientInstance = new UniversalClient();
|
|
345
|
-
}
|
|
346
|
-
return _universalClientInstance;
|
|
347
|
-
}
|
|
348
|
-
var universalClient = new Proxy({}, {
|
|
349
|
-
get(_target, prop) {
|
|
350
|
-
const instance = getUniversalClient();
|
|
351
|
-
return instance[prop];
|
|
352
|
-
}
|
|
353
|
-
});
|
|
354
|
-
|
|
355
|
-
export { ApiClientError, ContractClient, UniversalClient, universalClient as client, configureUniversalClient as configureClient, configureUniversalClient, createUniversalClient as createClient, createUniversalClient, getServerErrorDetails, getServerErrorType, getUniversalClient, isHttpError, isNetworkError, isServerError, isTimeoutError, universalClient };
|
|
356
|
-
//# sourceMappingURL=index.js.map
|
|
357
|
-
//# sourceMappingURL=index.js.map
|