@vaiftechnologies/vaif-client 0.1.2 → 0.2.1
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/LICENSE +21 -0
- package/README.md +554 -0
- package/dist/index.cjs +476 -113
- package/dist/index.d.cts +564 -62
- package/dist/index.d.ts +564 -62
- package/dist/index.js +459 -112
- package/package.json +3 -4
package/dist/index.cjs
CHANGED
|
@@ -25,21 +25,142 @@ __export(index_exports, {
|
|
|
25
25
|
VaifAdmin: () => VaifAdmin,
|
|
26
26
|
VaifApiKeys: () => VaifApiKeys,
|
|
27
27
|
VaifAuth: () => VaifAuth,
|
|
28
|
+
VaifAuthError: () => VaifAuthError,
|
|
28
29
|
VaifClient: () => VaifClient,
|
|
30
|
+
VaifConflictError: () => VaifConflictError,
|
|
29
31
|
VaifDatabase: () => VaifDatabase,
|
|
32
|
+
VaifError: () => VaifError,
|
|
30
33
|
VaifFunctions: () => VaifFunctions,
|
|
31
34
|
VaifIntegrations: () => VaifIntegrations,
|
|
35
|
+
VaifNetworkError: () => VaifNetworkError,
|
|
36
|
+
VaifNotFoundError: () => VaifNotFoundError,
|
|
32
37
|
VaifProjects: () => VaifProjects,
|
|
38
|
+
VaifRateLimitError: () => VaifRateLimitError,
|
|
33
39
|
VaifRealtime: () => VaifRealtime,
|
|
34
40
|
VaifSchema: () => VaifSchema,
|
|
35
41
|
VaifStorage: () => VaifStorage,
|
|
42
|
+
VaifTimeoutError: () => VaifTimeoutError,
|
|
36
43
|
VaifTypeGen: () => VaifTypeGen,
|
|
44
|
+
VaifValidationError: () => VaifValidationError,
|
|
37
45
|
createClient: () => createClient,
|
|
38
46
|
createTypeGen: () => createTypeGen,
|
|
39
|
-
createVaifClient: () => createClient
|
|
47
|
+
createVaifClient: () => createClient,
|
|
48
|
+
isVaifAuthError: () => isVaifAuthError,
|
|
49
|
+
isVaifConflictError: () => isVaifConflictError,
|
|
50
|
+
isVaifError: () => isVaifError,
|
|
51
|
+
isVaifNetworkError: () => isVaifNetworkError,
|
|
52
|
+
isVaifNotFoundError: () => isVaifNotFoundError,
|
|
53
|
+
isVaifRateLimitError: () => isVaifRateLimitError,
|
|
54
|
+
isVaifTimeoutError: () => isVaifTimeoutError,
|
|
55
|
+
isVaifValidationError: () => isVaifValidationError
|
|
40
56
|
});
|
|
41
57
|
module.exports = __toCommonJS(index_exports);
|
|
42
58
|
|
|
59
|
+
// src/errors.ts
|
|
60
|
+
var VaifError = class _VaifError extends Error {
|
|
61
|
+
constructor(message, options) {
|
|
62
|
+
super(message);
|
|
63
|
+
this.name = "VaifError";
|
|
64
|
+
this.code = options.code;
|
|
65
|
+
this.statusCode = options.statusCode;
|
|
66
|
+
this.requestId = options.requestId;
|
|
67
|
+
this.details = options.details;
|
|
68
|
+
const ErrorWithCapture = Error;
|
|
69
|
+
if (ErrorWithCapture.captureStackTrace) {
|
|
70
|
+
ErrorWithCapture.captureStackTrace(this, _VaifError);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
toJSON() {
|
|
74
|
+
return {
|
|
75
|
+
name: this.name,
|
|
76
|
+
message: this.message,
|
|
77
|
+
code: this.code,
|
|
78
|
+
statusCode: this.statusCode,
|
|
79
|
+
requestId: this.requestId,
|
|
80
|
+
details: this.details
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
var VaifAuthError = class extends VaifError {
|
|
85
|
+
constructor(message, options) {
|
|
86
|
+
super(message, {
|
|
87
|
+
code: options?.code ?? "AUTH_ERROR",
|
|
88
|
+
statusCode: options?.statusCode ?? 401,
|
|
89
|
+
requestId: options?.requestId
|
|
90
|
+
});
|
|
91
|
+
this.name = "VaifAuthError";
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
var VaifValidationError = class extends VaifError {
|
|
95
|
+
constructor(message, options) {
|
|
96
|
+
super(message, {
|
|
97
|
+
code: "VALIDATION_ERROR",
|
|
98
|
+
statusCode: 400,
|
|
99
|
+
requestId: options?.requestId,
|
|
100
|
+
details: options?.details ?? options?.fieldErrors
|
|
101
|
+
});
|
|
102
|
+
this.name = "VaifValidationError";
|
|
103
|
+
this.fieldErrors = options?.fieldErrors;
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
var VaifNetworkError = class extends VaifError {
|
|
107
|
+
constructor(message, cause) {
|
|
108
|
+
super(message, { code: "NETWORK_ERROR" });
|
|
109
|
+
this.name = "VaifNetworkError";
|
|
110
|
+
this.cause = cause;
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
var VaifRateLimitError = class extends VaifError {
|
|
114
|
+
constructor(message, retryAfter) {
|
|
115
|
+
super(message, { code: "RATE_LIMITED", statusCode: 429 });
|
|
116
|
+
this.name = "VaifRateLimitError";
|
|
117
|
+
this.retryAfter = retryAfter;
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
var VaifNotFoundError = class extends VaifError {
|
|
121
|
+
constructor(message, requestId) {
|
|
122
|
+
super(message, { code: "NOT_FOUND", statusCode: 404, requestId });
|
|
123
|
+
this.name = "VaifNotFoundError";
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
var VaifConflictError = class extends VaifError {
|
|
127
|
+
constructor(message, requestId) {
|
|
128
|
+
super(message, { code: "CONFLICT", statusCode: 409, requestId });
|
|
129
|
+
this.name = "VaifConflictError";
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
var VaifTimeoutError = class extends VaifNetworkError {
|
|
133
|
+
constructor(message, timeoutMs) {
|
|
134
|
+
super(message);
|
|
135
|
+
this.name = "VaifTimeoutError";
|
|
136
|
+
this.timeoutMs = timeoutMs;
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
function isVaifError(error) {
|
|
140
|
+
return error instanceof VaifError;
|
|
141
|
+
}
|
|
142
|
+
function isVaifAuthError(error) {
|
|
143
|
+
return error instanceof VaifAuthError;
|
|
144
|
+
}
|
|
145
|
+
function isVaifValidationError(error) {
|
|
146
|
+
return error instanceof VaifValidationError;
|
|
147
|
+
}
|
|
148
|
+
function isVaifNetworkError(error) {
|
|
149
|
+
return error instanceof VaifNetworkError;
|
|
150
|
+
}
|
|
151
|
+
function isVaifRateLimitError(error) {
|
|
152
|
+
return error instanceof VaifRateLimitError;
|
|
153
|
+
}
|
|
154
|
+
function isVaifNotFoundError(error) {
|
|
155
|
+
return error instanceof VaifNotFoundError;
|
|
156
|
+
}
|
|
157
|
+
function isVaifConflictError(error) {
|
|
158
|
+
return error instanceof VaifConflictError;
|
|
159
|
+
}
|
|
160
|
+
function isVaifTimeoutError(error) {
|
|
161
|
+
return error instanceof VaifTimeoutError;
|
|
162
|
+
}
|
|
163
|
+
|
|
43
164
|
// src/lib/database.ts
|
|
44
165
|
var VaifDatabase = class {
|
|
45
166
|
constructor(client) {
|
|
@@ -48,6 +169,9 @@ var VaifDatabase = class {
|
|
|
48
169
|
/**
|
|
49
170
|
* Start a query on a table
|
|
50
171
|
*
|
|
172
|
+
* @param table - Table name to query
|
|
173
|
+
* @returns A query builder for chaining operations
|
|
174
|
+
*
|
|
51
175
|
* @example
|
|
52
176
|
* ```typescript
|
|
53
177
|
* const users = await vaif.db.from('users').select('*').execute();
|
|
@@ -59,6 +183,10 @@ var VaifDatabase = class {
|
|
|
59
183
|
/**
|
|
60
184
|
* Execute raw SQL query (admin only)
|
|
61
185
|
*
|
|
186
|
+
* @param sql - SQL query string with $1, $2, etc. parameter placeholders
|
|
187
|
+
* @param params - Parameter values for the query
|
|
188
|
+
* @returns Query result with typed rows
|
|
189
|
+
*
|
|
62
190
|
* @example
|
|
63
191
|
* ```typescript
|
|
64
192
|
* const result = await vaif.db.raw('SELECT * FROM users WHERE id = $1', [userId]);
|
|
@@ -86,7 +214,16 @@ var VaifDatabase = class {
|
|
|
86
214
|
};
|
|
87
215
|
}
|
|
88
216
|
/**
|
|
89
|
-
* Execute a
|
|
217
|
+
* Execute a stored procedure (RPC call)
|
|
218
|
+
*
|
|
219
|
+
* @param functionName - Name of the database function to call
|
|
220
|
+
* @param params - Parameters to pass to the function
|
|
221
|
+
* @returns Query result with the function's return value
|
|
222
|
+
*
|
|
223
|
+
* @example
|
|
224
|
+
* ```typescript
|
|
225
|
+
* const result = await vaif.db.rpc('get_user_stats', { userId: '123' });
|
|
226
|
+
* ```
|
|
90
227
|
*/
|
|
91
228
|
async rpc(functionName, params) {
|
|
92
229
|
const response = await this.client.request(`/db/rpc/${functionName}`, {
|
|
@@ -175,72 +312,85 @@ var QueryBuilder = class {
|
|
|
175
312
|
this.onConflictUpdate = data;
|
|
176
313
|
return this;
|
|
177
314
|
}
|
|
178
|
-
|
|
315
|
+
/** Filter by equality (column = value) */
|
|
179
316
|
eq(column, value) {
|
|
180
317
|
this.addFilter(column, this.negateNext ? "neq" : "eq", value);
|
|
181
318
|
this.negateNext = false;
|
|
182
319
|
return this;
|
|
183
320
|
}
|
|
321
|
+
/** Filter by inequality (column != value) */
|
|
184
322
|
neq(column, value) {
|
|
185
323
|
this.addFilter(column, this.negateNext ? "eq" : "neq", value);
|
|
186
324
|
this.negateNext = false;
|
|
187
325
|
return this;
|
|
188
326
|
}
|
|
327
|
+
/** Filter by greater than (column > value) */
|
|
189
328
|
gt(column, value) {
|
|
190
329
|
this.addFilter(column, this.negateNext ? "lte" : "gt", value);
|
|
191
330
|
this.negateNext = false;
|
|
192
331
|
return this;
|
|
193
332
|
}
|
|
333
|
+
/** Filter by greater than or equal (column >= value) */
|
|
194
334
|
gte(column, value) {
|
|
195
335
|
this.addFilter(column, this.negateNext ? "lt" : "gte", value);
|
|
196
336
|
this.negateNext = false;
|
|
197
337
|
return this;
|
|
198
338
|
}
|
|
339
|
+
/** Filter by less than (column < value) */
|
|
199
340
|
lt(column, value) {
|
|
200
341
|
this.addFilter(column, this.negateNext ? "gte" : "lt", value);
|
|
201
342
|
this.negateNext = false;
|
|
202
343
|
return this;
|
|
203
344
|
}
|
|
345
|
+
/** Filter by less than or equal (column <= value) */
|
|
204
346
|
lte(column, value) {
|
|
205
347
|
this.addFilter(column, this.negateNext ? "gt" : "lte", value);
|
|
206
348
|
this.negateNext = false;
|
|
207
349
|
return this;
|
|
208
350
|
}
|
|
351
|
+
/** Filter by pattern matching (case-sensitive LIKE) */
|
|
209
352
|
like(column, pattern) {
|
|
210
353
|
this.addFilter(column, this.negateNext ? "nlike" : "like", pattern);
|
|
211
354
|
this.negateNext = false;
|
|
212
355
|
return this;
|
|
213
356
|
}
|
|
357
|
+
/** Filter by pattern matching (case-insensitive ILIKE) */
|
|
214
358
|
ilike(column, pattern) {
|
|
215
359
|
this.addFilter(column, this.negateNext ? "nilike" : "ilike", pattern);
|
|
216
360
|
this.negateNext = false;
|
|
217
361
|
return this;
|
|
218
362
|
}
|
|
363
|
+
/** Filter by IS (null/boolean checks) */
|
|
219
364
|
is(column, value) {
|
|
220
365
|
this.addFilter(column, this.negateNext ? "isnot" : "is", value);
|
|
221
366
|
this.negateNext = false;
|
|
222
367
|
return this;
|
|
223
368
|
}
|
|
369
|
+
/** Filter by inclusion in an array of values */
|
|
224
370
|
in(column, values) {
|
|
225
371
|
this.addFilter(column, this.negateNext ? "nin" : "in", values);
|
|
226
372
|
this.negateNext = false;
|
|
227
373
|
return this;
|
|
228
374
|
}
|
|
375
|
+
/** Filter by array containment (@>) */
|
|
229
376
|
contains(column, value) {
|
|
230
377
|
this.addFilter(column, "cs", value);
|
|
231
378
|
this.negateNext = false;
|
|
232
379
|
return this;
|
|
233
380
|
}
|
|
381
|
+
/** Filter by array being contained by (<@) */
|
|
234
382
|
containedBy(column, value) {
|
|
235
383
|
this.addFilter(column, "cd", value);
|
|
236
384
|
this.negateNext = false;
|
|
237
385
|
return this;
|
|
238
386
|
}
|
|
387
|
+
/** Filter by array overlap (&&) */
|
|
239
388
|
overlaps(column, value) {
|
|
240
389
|
this.addFilter(column, "ov", value);
|
|
241
390
|
this.negateNext = false;
|
|
242
391
|
return this;
|
|
243
392
|
}
|
|
393
|
+
/** Full-text search filter */
|
|
244
394
|
textSearch(column, query, options) {
|
|
245
395
|
this.addFilter(column, "fts", query, options?.config);
|
|
246
396
|
this.negateNext = false;
|
|
@@ -455,17 +605,19 @@ var VaifRealtime = class {
|
|
|
455
605
|
return;
|
|
456
606
|
}
|
|
457
607
|
const config = this.client.getConfig();
|
|
458
|
-
|
|
459
|
-
url.searchParams.set("apikey", config.apiKey);
|
|
460
|
-
url.searchParams.set("project", config.projectId);
|
|
461
|
-
const token = this.client.getAccessToken();
|
|
462
|
-
if (token) {
|
|
463
|
-
url.searchParams.set("token", token);
|
|
464
|
-
}
|
|
465
|
-
this.socket = new WebSocket(url.toString());
|
|
608
|
+
this.socket = new WebSocket(config.realtimeUrl);
|
|
466
609
|
this.socket.onopen = () => {
|
|
467
610
|
this.client.debug("Realtime connected");
|
|
468
611
|
this.reconnectAttempts = 0;
|
|
612
|
+
this.send({
|
|
613
|
+
topic: "phoenix",
|
|
614
|
+
event: "auth",
|
|
615
|
+
payload: {
|
|
616
|
+
apikey: config.apiKey,
|
|
617
|
+
project: config.projectId,
|
|
618
|
+
token: this.client.getAccessToken() ?? void 0
|
|
619
|
+
}
|
|
620
|
+
});
|
|
469
621
|
if (options.heartbeat !== false) {
|
|
470
622
|
this.startHeartbeat(options.heartbeatInterval ?? 3e4);
|
|
471
623
|
}
|
|
@@ -1916,6 +2068,63 @@ var VaifAI = class {
|
|
|
1916
2068
|
}
|
|
1917
2069
|
return { data: response.data, error: null };
|
|
1918
2070
|
}
|
|
2071
|
+
/**
|
|
2072
|
+
* Get Copilot observability metrics for the current project
|
|
2073
|
+
*
|
|
2074
|
+
* @example
|
|
2075
|
+
* ```typescript
|
|
2076
|
+
* const { data, error } = await vaif.ai.getCopilotMetrics({ range: '24h' });
|
|
2077
|
+
* console.log(`Success rate: ${data.summary.successRate}%`);
|
|
2078
|
+
* console.log(`Total tokens: ${data.summary.totalTokens}`);
|
|
2079
|
+
* ```
|
|
2080
|
+
*/
|
|
2081
|
+
async getCopilotMetrics(options = {}) {
|
|
2082
|
+
const config = this.client.getConfig();
|
|
2083
|
+
const range = options.range ?? "24h";
|
|
2084
|
+
const response = await this.client.request(
|
|
2085
|
+
`/copilot/metrics/${config.projectId}?range=${range}`,
|
|
2086
|
+
{ method: "GET" }
|
|
2087
|
+
);
|
|
2088
|
+
if (response.error) {
|
|
2089
|
+
return {
|
|
2090
|
+
data: null,
|
|
2091
|
+
error: {
|
|
2092
|
+
message: response.error.message,
|
|
2093
|
+
code: response.error.code,
|
|
2094
|
+
status: response.status
|
|
2095
|
+
}
|
|
2096
|
+
};
|
|
2097
|
+
}
|
|
2098
|
+
return { data: response.data, error: null };
|
|
2099
|
+
}
|
|
2100
|
+
/**
|
|
2101
|
+
* Get Copilot observability metrics for an organization
|
|
2102
|
+
*
|
|
2103
|
+
* @example
|
|
2104
|
+
* ```typescript
|
|
2105
|
+
* const { data, error } = await vaif.ai.getCopilotOrgMetrics(orgId, { range: '7d' });
|
|
2106
|
+
* console.log(`Total requests: ${data.summary.totalRequests}`);
|
|
2107
|
+
* console.log(`Projects: ${data.summary.uniqueProjects}`);
|
|
2108
|
+
* ```
|
|
2109
|
+
*/
|
|
2110
|
+
async getCopilotOrgMetrics(orgId, options = {}) {
|
|
2111
|
+
const range = options.range ?? "24h";
|
|
2112
|
+
const response = await this.client.request(
|
|
2113
|
+
`/copilot/metrics/org/${orgId}?range=${range}`,
|
|
2114
|
+
{ method: "GET" }
|
|
2115
|
+
);
|
|
2116
|
+
if (response.error) {
|
|
2117
|
+
return {
|
|
2118
|
+
data: null,
|
|
2119
|
+
error: {
|
|
2120
|
+
message: response.error.message,
|
|
2121
|
+
code: response.error.code,
|
|
2122
|
+
status: response.status
|
|
2123
|
+
}
|
|
2124
|
+
};
|
|
2125
|
+
}
|
|
2126
|
+
return { data: response.data, error: null };
|
|
2127
|
+
}
|
|
1919
2128
|
};
|
|
1920
2129
|
|
|
1921
2130
|
// src/lib/schema.ts
|
|
@@ -2482,7 +2691,7 @@ var VaifAdmin = class {
|
|
|
2482
2691
|
const response = await fetch(url, {
|
|
2483
2692
|
...options,
|
|
2484
2693
|
headers,
|
|
2485
|
-
credentials: "
|
|
2694
|
+
credentials: "same-origin"
|
|
2486
2695
|
});
|
|
2487
2696
|
if (!response.ok) {
|
|
2488
2697
|
const error = await response.json().catch(() => ({ message: "Request failed" }));
|
|
@@ -3271,6 +3480,14 @@ var VaifIntegrations = class {
|
|
|
3271
3480
|
var DEFAULT_API_URL = "https://api.vaif.studio";
|
|
3272
3481
|
var DEFAULT_REALTIME_URL = "wss://realtime.vaif.studio";
|
|
3273
3482
|
var DEFAULT_TIMEOUT = 3e4;
|
|
3483
|
+
var DEFAULT_RETRY = {
|
|
3484
|
+
maxRetries: 3,
|
|
3485
|
+
retryDelay: 1e3,
|
|
3486
|
+
maxRetryDelay: 3e4,
|
|
3487
|
+
backoffMultiplier: 2,
|
|
3488
|
+
retryOn: [429, 500, 502, 503, 504],
|
|
3489
|
+
retryOnNetworkError: true
|
|
3490
|
+
};
|
|
3274
3491
|
var memoryStorage = {
|
|
3275
3492
|
data: /* @__PURE__ */ new Map(),
|
|
3276
3493
|
getItem(key) {
|
|
@@ -3288,6 +3505,18 @@ var browserStorage = typeof window !== "undefined" && window.localStorage ? {
|
|
|
3288
3505
|
setItem: (key, value) => window.localStorage.setItem(key, value),
|
|
3289
3506
|
removeItem: (key) => window.localStorage.removeItem(key)
|
|
3290
3507
|
} : null;
|
|
3508
|
+
function calculateRetryDelay(attempt, config) {
|
|
3509
|
+
const exponentialDelay = config.retryDelay * Math.pow(config.backoffMultiplier, attempt);
|
|
3510
|
+
const cappedDelay = Math.min(exponentialDelay, config.maxRetryDelay);
|
|
3511
|
+
const jitter = 0.5 + Math.random();
|
|
3512
|
+
return Math.floor(cappedDelay * jitter);
|
|
3513
|
+
}
|
|
3514
|
+
function isRetryableStatus(status, config) {
|
|
3515
|
+
return config.retryOn.includes(status);
|
|
3516
|
+
}
|
|
3517
|
+
function sleep(ms) {
|
|
3518
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
3519
|
+
}
|
|
3291
3520
|
function createClient(options) {
|
|
3292
3521
|
return new VaifClient(options);
|
|
3293
3522
|
}
|
|
@@ -3297,10 +3526,10 @@ var VaifClient = class {
|
|
|
3297
3526
|
const isAdminClient = options.baseUrl || options.accessToken;
|
|
3298
3527
|
if (!isAdminClient) {
|
|
3299
3528
|
if (!options.projectId) {
|
|
3300
|
-
throw new
|
|
3529
|
+
throw new VaifError("projectId is required", { code: "INVALID_CONFIG" });
|
|
3301
3530
|
}
|
|
3302
3531
|
if (!options.apiKey) {
|
|
3303
|
-
throw new
|
|
3532
|
+
throw new VaifError("apiKey is required", { code: "INVALID_CONFIG" });
|
|
3304
3533
|
}
|
|
3305
3534
|
}
|
|
3306
3535
|
this.config = {
|
|
@@ -3314,7 +3543,13 @@ var VaifClient = class {
|
|
|
3314
3543
|
debug: options.debug ?? false,
|
|
3315
3544
|
autoRefreshToken: options.autoRefreshToken ?? true,
|
|
3316
3545
|
persistSession: options.persistSession ?? true,
|
|
3317
|
-
storage: options.storage ?? browserStorage ?? memoryStorage
|
|
3546
|
+
storage: options.storage ?? browserStorage ?? memoryStorage,
|
|
3547
|
+
retry: {
|
|
3548
|
+
...DEFAULT_RETRY,
|
|
3549
|
+
...options.retry,
|
|
3550
|
+
retryOn: options.retry?.retryOn ?? DEFAULT_RETRY.retryOn
|
|
3551
|
+
},
|
|
3552
|
+
interceptors: options.interceptors ?? {}
|
|
3318
3553
|
};
|
|
3319
3554
|
if (options.accessToken) {
|
|
3320
3555
|
this.accessToken = options.accessToken;
|
|
@@ -3333,120 +3568,228 @@ var VaifClient = class {
|
|
|
3333
3568
|
this.integrations = new VaifIntegrations(this);
|
|
3334
3569
|
this.restoreSession();
|
|
3335
3570
|
}
|
|
3336
|
-
/**
|
|
3337
|
-
* Get current configuration
|
|
3338
|
-
*/
|
|
3571
|
+
/** Get current configuration */
|
|
3339
3572
|
getConfig() {
|
|
3340
3573
|
return this.config;
|
|
3341
3574
|
}
|
|
3342
|
-
/**
|
|
3343
|
-
* Set access token for authenticated requests
|
|
3344
|
-
*/
|
|
3575
|
+
/** Set access token for authenticated requests */
|
|
3345
3576
|
setAccessToken(token) {
|
|
3346
3577
|
this.accessToken = token;
|
|
3347
3578
|
}
|
|
3348
|
-
/**
|
|
3349
|
-
* Get current access token
|
|
3350
|
-
*/
|
|
3579
|
+
/** Get current access token */
|
|
3351
3580
|
getAccessToken() {
|
|
3352
3581
|
return this.accessToken;
|
|
3353
3582
|
}
|
|
3354
3583
|
/**
|
|
3355
|
-
* Make an authenticated API request
|
|
3584
|
+
* Make an authenticated API request with automatic retry and interceptors
|
|
3585
|
+
*
|
|
3586
|
+
* @param path - API path (relative to base URL)
|
|
3587
|
+
* @param options - Request options (method, body, params, etc.)
|
|
3588
|
+
* @returns Typed API response with data, error, status, and headers
|
|
3356
3589
|
*/
|
|
3357
3590
|
async request(path, options = {}) {
|
|
3358
|
-
const
|
|
3359
|
-
|
|
3360
|
-
|
|
3361
|
-
|
|
3362
|
-
|
|
3591
|
+
const { retry, interceptors } = this.config;
|
|
3592
|
+
for (let attempt = 0; attempt <= retry.maxRetries; attempt++) {
|
|
3593
|
+
const startTime = Date.now();
|
|
3594
|
+
const url = new URL(path, this.config.apiUrl);
|
|
3595
|
+
if (options.params) {
|
|
3596
|
+
Object.entries(options.params).forEach(([key, value]) => {
|
|
3597
|
+
if (value !== void 0) {
|
|
3598
|
+
url.searchParams.set(key, String(value));
|
|
3599
|
+
}
|
|
3600
|
+
});
|
|
3601
|
+
}
|
|
3602
|
+
const headers = {
|
|
3603
|
+
"Content-Type": "application/json",
|
|
3604
|
+
...this.config.headers,
|
|
3605
|
+
...options.headers
|
|
3606
|
+
};
|
|
3607
|
+
if (this.config.projectId) {
|
|
3608
|
+
headers["X-Project-ID"] = this.config.projectId;
|
|
3609
|
+
}
|
|
3610
|
+
if (this.config.apiKey) {
|
|
3611
|
+
headers[this.config.apiKeyHeader] = this.config.apiKey;
|
|
3612
|
+
}
|
|
3613
|
+
if (this.accessToken) {
|
|
3614
|
+
headers.Authorization = `Bearer ${this.accessToken}`;
|
|
3615
|
+
}
|
|
3616
|
+
let requestContext = {
|
|
3617
|
+
url: url.toString(),
|
|
3618
|
+
path,
|
|
3619
|
+
method: options.method ?? "GET",
|
|
3620
|
+
headers: { ...headers },
|
|
3621
|
+
body: options.body && options.method !== "GET" ? JSON.stringify(options.body) : void 0,
|
|
3622
|
+
attempt
|
|
3623
|
+
};
|
|
3624
|
+
if (interceptors.onRequest) {
|
|
3625
|
+
try {
|
|
3626
|
+
const modified = await interceptors.onRequest(requestContext);
|
|
3627
|
+
if (modified) {
|
|
3628
|
+
requestContext = modified;
|
|
3629
|
+
}
|
|
3630
|
+
} catch {
|
|
3363
3631
|
}
|
|
3364
|
-
});
|
|
3365
|
-
}
|
|
3366
|
-
const headers = {
|
|
3367
|
-
"Content-Type": "application/json",
|
|
3368
|
-
...this.config.headers,
|
|
3369
|
-
...options.headers
|
|
3370
|
-
};
|
|
3371
|
-
if (this.config.projectId) {
|
|
3372
|
-
headers["X-Project-ID"] = this.config.projectId;
|
|
3373
|
-
}
|
|
3374
|
-
if (this.config.apiKey) {
|
|
3375
|
-
headers[this.config.apiKeyHeader] = this.config.apiKey;
|
|
3376
|
-
}
|
|
3377
|
-
if (this.accessToken) {
|
|
3378
|
-
headers.Authorization = `Bearer ${this.accessToken}`;
|
|
3379
|
-
}
|
|
3380
|
-
const fetchOptions = {
|
|
3381
|
-
method: options.method ?? "GET",
|
|
3382
|
-
headers,
|
|
3383
|
-
signal: options.signal
|
|
3384
|
-
};
|
|
3385
|
-
if (options.body && options.method !== "GET") {
|
|
3386
|
-
fetchOptions.body = JSON.stringify(options.body);
|
|
3387
|
-
}
|
|
3388
|
-
const timeoutMs = options.timeout ?? this.config.timeout;
|
|
3389
|
-
const timeoutController = new AbortController();
|
|
3390
|
-
const timeoutId = setTimeout(() => timeoutController.abort(), timeoutMs);
|
|
3391
|
-
if (options.signal) {
|
|
3392
|
-
options.signal.addEventListener("abort", () => timeoutController.abort());
|
|
3393
|
-
}
|
|
3394
|
-
fetchOptions.signal = timeoutController.signal;
|
|
3395
|
-
try {
|
|
3396
|
-
if (this.config.debug) {
|
|
3397
|
-
console.log(`[VAIF] ${options.method ?? "GET"} ${url.toString()}`);
|
|
3398
3632
|
}
|
|
3399
|
-
const
|
|
3400
|
-
|
|
3401
|
-
|
|
3402
|
-
|
|
3403
|
-
|
|
3404
|
-
if (
|
|
3405
|
-
|
|
3406
|
-
|
|
3407
|
-
|
|
3408
|
-
|
|
3633
|
+
const fetchOptions = {
|
|
3634
|
+
method: requestContext.method,
|
|
3635
|
+
headers: requestContext.headers,
|
|
3636
|
+
signal: options.signal
|
|
3637
|
+
};
|
|
3638
|
+
if (requestContext.body) {
|
|
3639
|
+
fetchOptions.body = requestContext.body;
|
|
3640
|
+
}
|
|
3641
|
+
const timeoutMs = options.timeout ?? this.config.timeout;
|
|
3642
|
+
const timeoutController = new AbortController();
|
|
3643
|
+
const timeoutId = setTimeout(() => timeoutController.abort(), timeoutMs);
|
|
3644
|
+
if (options.signal) {
|
|
3645
|
+
options.signal.addEventListener("abort", () => timeoutController.abort());
|
|
3646
|
+
}
|
|
3647
|
+
fetchOptions.signal = timeoutController.signal;
|
|
3648
|
+
try {
|
|
3649
|
+
if (this.config.debug) {
|
|
3650
|
+
console.log(`[VAIF] ${requestContext.method} ${requestContext.url} (attempt ${attempt + 1})`);
|
|
3651
|
+
}
|
|
3652
|
+
const response = await fetch(requestContext.url, fetchOptions);
|
|
3653
|
+
clearTimeout(timeoutId);
|
|
3654
|
+
const durationMs = Date.now() - startTime;
|
|
3655
|
+
const contentType = response.headers.get("content-type");
|
|
3656
|
+
let data = null;
|
|
3657
|
+
let error = null;
|
|
3658
|
+
if (contentType?.includes("application/json")) {
|
|
3659
|
+
const json = await response.json();
|
|
3660
|
+
if (response.ok) {
|
|
3661
|
+
data = json;
|
|
3662
|
+
} else {
|
|
3663
|
+
error = {
|
|
3664
|
+
message: json.message ?? json.error ?? "Request failed",
|
|
3665
|
+
code: json.code,
|
|
3666
|
+
status: response.status,
|
|
3667
|
+
details: json.details,
|
|
3668
|
+
requestId: response.headers.get("x-request-id") ?? void 0
|
|
3669
|
+
};
|
|
3670
|
+
}
|
|
3671
|
+
} else if (!response.ok) {
|
|
3409
3672
|
error = {
|
|
3410
|
-
message:
|
|
3411
|
-
|
|
3412
|
-
status: response.status,
|
|
3413
|
-
details: json.details,
|
|
3414
|
-
requestId: response.headers.get("x-request-id") ?? void 0
|
|
3673
|
+
message: `Request failed with status ${response.status}`,
|
|
3674
|
+
status: response.status
|
|
3415
3675
|
};
|
|
3416
3676
|
}
|
|
3417
|
-
|
|
3418
|
-
|
|
3419
|
-
|
|
3420
|
-
|
|
3677
|
+
const responseHeaders = {};
|
|
3678
|
+
response.headers.forEach((value, key) => {
|
|
3679
|
+
responseHeaders[key] = value;
|
|
3680
|
+
});
|
|
3681
|
+
if (error && attempt < retry.maxRetries && isRetryableStatus(response.status, retry)) {
|
|
3682
|
+
if (interceptors.onError) {
|
|
3683
|
+
try {
|
|
3684
|
+
await interceptors.onError({
|
|
3685
|
+
request: requestContext,
|
|
3686
|
+
error: new VaifError(error.message, {
|
|
3687
|
+
code: error.code ?? "REQUEST_ERROR",
|
|
3688
|
+
statusCode: response.status
|
|
3689
|
+
}),
|
|
3690
|
+
durationMs,
|
|
3691
|
+
willRetry: true
|
|
3692
|
+
});
|
|
3693
|
+
} catch {
|
|
3694
|
+
}
|
|
3695
|
+
}
|
|
3696
|
+
const delay = calculateRetryDelay(attempt, retry);
|
|
3697
|
+
if (this.config.debug) {
|
|
3698
|
+
console.log(`[VAIF] Retrying in ${delay}ms (attempt ${attempt + 2}/${retry.maxRetries + 1})`);
|
|
3699
|
+
}
|
|
3700
|
+
await sleep(delay);
|
|
3701
|
+
continue;
|
|
3702
|
+
}
|
|
3703
|
+
if (!error && interceptors.onResponse) {
|
|
3704
|
+
try {
|
|
3705
|
+
await interceptors.onResponse({
|
|
3706
|
+
request: requestContext,
|
|
3707
|
+
data,
|
|
3708
|
+
status: response.status,
|
|
3709
|
+
headers: responseHeaders,
|
|
3710
|
+
durationMs
|
|
3711
|
+
});
|
|
3712
|
+
} catch {
|
|
3713
|
+
}
|
|
3714
|
+
}
|
|
3715
|
+
if (error && interceptors.onError) {
|
|
3716
|
+
try {
|
|
3717
|
+
await interceptors.onError({
|
|
3718
|
+
request: requestContext,
|
|
3719
|
+
error: new VaifError(error.message, {
|
|
3720
|
+
code: error.code ?? "REQUEST_ERROR",
|
|
3721
|
+
statusCode: response.status
|
|
3722
|
+
}),
|
|
3723
|
+
durationMs,
|
|
3724
|
+
willRetry: false
|
|
3725
|
+
});
|
|
3726
|
+
} catch {
|
|
3727
|
+
}
|
|
3728
|
+
}
|
|
3729
|
+
return {
|
|
3730
|
+
data,
|
|
3731
|
+
error,
|
|
3732
|
+
status: response.status,
|
|
3733
|
+
headers: responseHeaders
|
|
3734
|
+
};
|
|
3735
|
+
} catch (err) {
|
|
3736
|
+
clearTimeout(timeoutId);
|
|
3737
|
+
const durationMs = Date.now() - startTime;
|
|
3738
|
+
const isTimeout = err instanceof Error && err.name === "AbortError";
|
|
3739
|
+
const isNetworkError = !isTimeout;
|
|
3740
|
+
if (isNetworkError && retry.retryOnNetworkError && attempt < retry.maxRetries) {
|
|
3741
|
+
if (interceptors.onError) {
|
|
3742
|
+
try {
|
|
3743
|
+
await interceptors.onError({
|
|
3744
|
+
request: requestContext,
|
|
3745
|
+
error: err instanceof Error ? err : new Error(String(err)),
|
|
3746
|
+
durationMs,
|
|
3747
|
+
willRetry: true
|
|
3748
|
+
});
|
|
3749
|
+
} catch {
|
|
3750
|
+
}
|
|
3751
|
+
}
|
|
3752
|
+
const delay = calculateRetryDelay(attempt, retry);
|
|
3753
|
+
if (this.config.debug) {
|
|
3754
|
+
console.log(`[VAIF] Network error, retrying in ${delay}ms`);
|
|
3755
|
+
}
|
|
3756
|
+
await sleep(delay);
|
|
3757
|
+
continue;
|
|
3758
|
+
}
|
|
3759
|
+
const error = {
|
|
3760
|
+
message: err instanceof Error ? isTimeout ? "Request timed out" : err.message : "Unknown error",
|
|
3761
|
+
code: isTimeout ? "TIMEOUT" : "NETWORK_ERROR"
|
|
3762
|
+
};
|
|
3763
|
+
if (interceptors.onError) {
|
|
3764
|
+
try {
|
|
3765
|
+
await interceptors.onError({
|
|
3766
|
+
request: requestContext,
|
|
3767
|
+
error: isTimeout ? new VaifTimeoutError("Request timed out", timeoutMs) : new VaifNetworkError(
|
|
3768
|
+
err instanceof Error ? err.message : "Network error",
|
|
3769
|
+
err instanceof Error ? err : void 0
|
|
3770
|
+
),
|
|
3771
|
+
durationMs,
|
|
3772
|
+
willRetry: false
|
|
3773
|
+
});
|
|
3774
|
+
} catch {
|
|
3775
|
+
}
|
|
3776
|
+
}
|
|
3777
|
+
return {
|
|
3778
|
+
data: null,
|
|
3779
|
+
error,
|
|
3780
|
+
status: 0,
|
|
3781
|
+
headers: {}
|
|
3421
3782
|
};
|
|
3422
3783
|
}
|
|
3423
|
-
const responseHeaders = {};
|
|
3424
|
-
response.headers.forEach((value, key) => {
|
|
3425
|
-
responseHeaders[key] = value;
|
|
3426
|
-
});
|
|
3427
|
-
return {
|
|
3428
|
-
data,
|
|
3429
|
-
error,
|
|
3430
|
-
status: response.status,
|
|
3431
|
-
headers: responseHeaders
|
|
3432
|
-
};
|
|
3433
|
-
} catch (err) {
|
|
3434
|
-
clearTimeout(timeoutId);
|
|
3435
|
-
const error = {
|
|
3436
|
-
message: err instanceof Error ? err.name === "AbortError" ? "Request timed out" : err.message : "Unknown error",
|
|
3437
|
-
code: err instanceof Error && err.name === "AbortError" ? "TIMEOUT" : "NETWORK_ERROR"
|
|
3438
|
-
};
|
|
3439
|
-
return {
|
|
3440
|
-
data: null,
|
|
3441
|
-
error,
|
|
3442
|
-
status: 0,
|
|
3443
|
-
headers: {}
|
|
3444
|
-
};
|
|
3445
3784
|
}
|
|
3785
|
+
return {
|
|
3786
|
+
data: null,
|
|
3787
|
+
error: { message: "Max retries exceeded", code: "MAX_RETRIES" },
|
|
3788
|
+
status: 0,
|
|
3789
|
+
headers: {}
|
|
3790
|
+
};
|
|
3446
3791
|
}
|
|
3447
|
-
/**
|
|
3448
|
-
* Restore session from storage
|
|
3449
|
-
*/
|
|
3792
|
+
/** Restore session from storage */
|
|
3450
3793
|
async restoreSession() {
|
|
3451
3794
|
if (!this.config.persistSession) return;
|
|
3452
3795
|
try {
|
|
@@ -3462,12 +3805,16 @@ var VaifClient = class {
|
|
|
3462
3805
|
} catch {
|
|
3463
3806
|
}
|
|
3464
3807
|
}
|
|
3465
|
-
/**
|
|
3466
|
-
* Log debug messages
|
|
3467
|
-
*/
|
|
3808
|
+
/** Log debug messages (sanitised to avoid leaking tokens) */
|
|
3468
3809
|
debug(...args) {
|
|
3469
3810
|
if (this.config.debug) {
|
|
3470
|
-
|
|
3811
|
+
const safe = args.map((arg) => {
|
|
3812
|
+
if (arg instanceof Error) return arg.message;
|
|
3813
|
+
if (typeof arg === "string") return arg;
|
|
3814
|
+
if (typeof arg === "number" || typeof arg === "boolean") return arg;
|
|
3815
|
+
return "[object]";
|
|
3816
|
+
});
|
|
3817
|
+
console.log("[VAIF]", ...safe);
|
|
3471
3818
|
}
|
|
3472
3819
|
}
|
|
3473
3820
|
};
|
|
@@ -3478,16 +3825,32 @@ var VaifClient = class {
|
|
|
3478
3825
|
VaifAdmin,
|
|
3479
3826
|
VaifApiKeys,
|
|
3480
3827
|
VaifAuth,
|
|
3828
|
+
VaifAuthError,
|
|
3481
3829
|
VaifClient,
|
|
3830
|
+
VaifConflictError,
|
|
3482
3831
|
VaifDatabase,
|
|
3832
|
+
VaifError,
|
|
3483
3833
|
VaifFunctions,
|
|
3484
3834
|
VaifIntegrations,
|
|
3835
|
+
VaifNetworkError,
|
|
3836
|
+
VaifNotFoundError,
|
|
3485
3837
|
VaifProjects,
|
|
3838
|
+
VaifRateLimitError,
|
|
3486
3839
|
VaifRealtime,
|
|
3487
3840
|
VaifSchema,
|
|
3488
3841
|
VaifStorage,
|
|
3842
|
+
VaifTimeoutError,
|
|
3489
3843
|
VaifTypeGen,
|
|
3844
|
+
VaifValidationError,
|
|
3490
3845
|
createClient,
|
|
3491
3846
|
createTypeGen,
|
|
3492
|
-
createVaifClient
|
|
3847
|
+
createVaifClient,
|
|
3848
|
+
isVaifAuthError,
|
|
3849
|
+
isVaifConflictError,
|
|
3850
|
+
isVaifError,
|
|
3851
|
+
isVaifNetworkError,
|
|
3852
|
+
isVaifNotFoundError,
|
|
3853
|
+
isVaifRateLimitError,
|
|
3854
|
+
isVaifTimeoutError,
|
|
3855
|
+
isVaifValidationError
|
|
3493
3856
|
});
|