@attrove/sdk 0.1.5 → 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/cjs/{src/__mocks__ → __mocks__}/version.js +1 -2
- package/cjs/{src/admin-client.js → admin-client.js} +23 -22
- package/cjs/{src/client.js → client.js} +40 -39
- package/cjs/{src/constants.js → constants.js} +18 -19
- package/cjs/{src/errors → errors}/index.js +26 -27
- package/cjs/{src/index.js → index.js} +35 -36
- package/cjs/package.json +1 -56
- package/cjs/{src/resources → resources}/conversations.js +1 -2
- package/cjs/{src/resources → resources}/index.js +10 -11
- package/cjs/{src/resources → resources}/integrations.js +0 -1
- package/cjs/{src/resources → resources}/messages.js +3 -4
- package/cjs/{src/resources → resources}/query.js +2 -3
- package/cjs/{src/resources → resources}/users.js +0 -1
- package/cjs/{src/types → types}/index.js +88 -61
- package/cjs/{src/utils → utils}/fetch.js +67 -55
- package/cjs/{src/utils → utils}/index.js +5 -6
- package/cjs/{src/utils → utils}/streaming.js +59 -45
- package/cjs/{src/version.js → version.js} +2 -3
- package/esm/__mocks__/version.d.ts.map +1 -0
- package/esm/{src/__mocks__ → __mocks__}/version.js +1 -1
- package/esm/__mocks__/version.js.map +1 -0
- package/{types/src → esm}/admin-client.d.ts +2 -2
- package/esm/admin-client.d.ts.map +1 -0
- package/esm/{src/admin-client.js → admin-client.js} +18 -16
- package/esm/admin-client.js.map +1 -0
- package/{types/src → esm}/client.d.ts +6 -6
- package/esm/client.d.ts.map +1 -0
- package/esm/{src/client.js → client.js} +28 -26
- package/esm/client.js.map +1 -0
- package/esm/constants.d.ts.map +1 -0
- package/esm/{src/constants.js → constants.js} +18 -18
- package/esm/constants.js.map +1 -0
- package/{types/src → esm}/errors/index.d.ts +1 -1
- package/{types/src → esm}/errors/index.d.ts.map +1 -1
- package/esm/{src/errors → errors}/index.js +13 -13
- package/esm/errors/index.js.map +1 -0
- package/{types/src → esm}/index.d.ts +14 -14
- package/esm/index.d.ts.map +1 -0
- package/esm/{src/index.js → index.js} +8 -8
- package/esm/index.js.map +1 -0
- package/esm/package.json +1 -56
- package/{types/src → esm}/resources/conversations.d.ts +2 -2
- package/esm/resources/conversations.d.ts.map +1 -0
- package/esm/{src/resources → resources}/conversations.js +1 -1
- package/esm/resources/conversations.js.map +1 -0
- package/esm/resources/index.d.ts +12 -0
- package/esm/resources/index.d.ts.map +1 -0
- package/esm/resources/index.js +9 -0
- package/esm/resources/index.js.map +1 -0
- package/{types/src → esm}/resources/integrations.d.ts +2 -2
- package/esm/resources/integrations.d.ts.map +1 -0
- package/esm/resources/integrations.js.map +1 -0
- package/{types/src → esm}/resources/messages.d.ts +2 -2
- package/esm/resources/messages.d.ts.map +1 -0
- package/esm/{src/resources → resources}/messages.js +3 -3
- package/{cjs/src → esm}/resources/messages.js.map +1 -1
- package/{types/src → esm}/resources/query.d.ts +2 -2
- package/esm/resources/query.d.ts.map +1 -0
- package/esm/{src/resources → resources}/query.js +2 -2
- package/esm/resources/query.js.map +1 -0
- package/{types/src → esm}/resources/users.d.ts +2 -2
- package/esm/resources/users.d.ts.map +1 -0
- package/esm/resources/users.js.map +1 -0
- package/{types/src → esm}/types/index.d.ts +28 -28
- package/esm/types/index.d.ts.map +1 -0
- package/esm/{src/types → types}/index.js +88 -60
- package/esm/types/index.js.map +1 -0
- package/{types/src → esm}/utils/fetch.d.ts +10 -10
- package/esm/utils/fetch.d.ts.map +1 -0
- package/esm/{src/utils → utils}/fetch.js +57 -44
- package/esm/utils/fetch.js.map +1 -0
- package/esm/utils/index.d.ts +8 -0
- package/esm/utils/index.d.ts.map +1 -0
- package/esm/utils/index.js +6 -0
- package/esm/utils/index.js.map +1 -0
- package/{types/src → esm}/utils/streaming.d.ts +5 -5
- package/esm/utils/streaming.d.ts.map +1 -0
- package/esm/{src/utils → utils}/streaming.js +47 -32
- package/esm/utils/streaming.js.map +1 -0
- package/esm/version.d.ts.map +1 -0
- package/esm/{src/version.js → version.js} +1 -1
- package/esm/version.js.map +1 -0
- package/package.json +13 -10
- package/cjs/README.md +0 -247
- package/cjs/src/__mocks__/version.js.map +0 -1
- package/cjs/src/admin-client.js.map +0 -1
- package/cjs/src/client.js.map +0 -1
- package/cjs/src/constants.js.map +0 -1
- package/cjs/src/errors/index.js.map +0 -1
- package/cjs/src/index.js.map +0 -1
- package/cjs/src/resources/conversations.js.map +0 -1
- package/cjs/src/resources/index.js.map +0 -1
- package/cjs/src/resources/integrations.js.map +0 -1
- package/cjs/src/resources/query.js.map +0 -1
- package/cjs/src/resources/users.js.map +0 -1
- package/cjs/src/types/index.js.map +0 -1
- package/cjs/src/utils/fetch.js.map +0 -1
- package/cjs/src/utils/index.js.map +0 -1
- package/cjs/src/utils/streaming.js.map +0 -1
- package/cjs/src/version.js.map +0 -1
- package/esm/README.md +0 -247
- package/esm/src/__mocks__/version.js.map +0 -1
- package/esm/src/admin-client.js.map +0 -1
- package/esm/src/client.js.map +0 -1
- package/esm/src/constants.js.map +0 -1
- package/esm/src/errors/index.js.map +0 -1
- package/esm/src/index.js.map +0 -1
- package/esm/src/resources/conversations.js.map +0 -1
- package/esm/src/resources/index.js +0 -9
- package/esm/src/resources/index.js.map +0 -1
- package/esm/src/resources/integrations.js.map +0 -1
- package/esm/src/resources/messages.js.map +0 -1
- package/esm/src/resources/query.js.map +0 -1
- package/esm/src/resources/users.js.map +0 -1
- package/esm/src/types/index.js.map +0 -1
- package/esm/src/utils/fetch.js.map +0 -1
- package/esm/src/utils/index.js +0 -6
- package/esm/src/utils/index.js.map +0 -1
- package/esm/src/utils/streaming.js.map +0 -1
- package/esm/src/version.js.map +0 -1
- package/types/src/__mocks__/version.d.ts.map +0 -1
- package/types/src/admin-client.d.ts.map +0 -1
- package/types/src/client.d.ts.map +0 -1
- package/types/src/constants.d.ts.map +0 -1
- package/types/src/index.d.ts.map +0 -1
- package/types/src/resources/conversations.d.ts.map +0 -1
- package/types/src/resources/index.d.ts +0 -12
- package/types/src/resources/index.d.ts.map +0 -1
- package/types/src/resources/integrations.d.ts.map +0 -1
- package/types/src/resources/messages.d.ts.map +0 -1
- package/types/src/resources/query.d.ts.map +0 -1
- package/types/src/resources/users.d.ts.map +0 -1
- package/types/src/types/index.d.ts.map +0 -1
- package/types/src/utils/fetch.d.ts.map +0 -1
- package/types/src/utils/index.d.ts +0 -8
- package/types/src/utils/index.d.ts.map +0 -1
- package/types/src/utils/streaming.d.ts.map +0 -1
- package/types/src/version.d.ts.map +0 -1
- /package/{types/src → esm}/__mocks__/version.d.ts +0 -0
- /package/{types/src → esm}/constants.d.ts +0 -0
- /package/esm/{src/resources → resources}/integrations.js +0 -0
- /package/esm/{src/resources → resources}/users.js +0 -0
- /package/{types/src → esm}/version.d.ts +0 -0
|
@@ -7,9 +7,9 @@
|
|
|
7
7
|
*/
|
|
8
8
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
9
|
exports.HttpClient = void 0;
|
|
10
|
-
const
|
|
11
|
-
const
|
|
12
|
-
const
|
|
10
|
+
const index_js_1 = require("../errors/index.js");
|
|
11
|
+
const index_js_2 = require("../types/index.js");
|
|
12
|
+
const constants_js_1 = require("../constants.js");
|
|
13
13
|
/**
|
|
14
14
|
* Delay execution during retry backoff.
|
|
15
15
|
* Used between retry attempts to avoid overwhelming the server.
|
|
@@ -25,7 +25,7 @@ function calculateRetryDelay(attempt, retryAfter) {
|
|
|
25
25
|
return retryAfter * 1000;
|
|
26
26
|
}
|
|
27
27
|
// Exponential backoff with jitter
|
|
28
|
-
const exponentialDelay =
|
|
28
|
+
const exponentialDelay = constants_js_1.INITIAL_RETRY_DELAY * Math.pow(2, attempt);
|
|
29
29
|
const jitter = Math.random() * 0.3 * exponentialDelay;
|
|
30
30
|
return Math.floor(exponentialDelay + jitter);
|
|
31
31
|
}
|
|
@@ -57,32 +57,36 @@ function normalizeConfig(config) {
|
|
|
57
57
|
let auth;
|
|
58
58
|
// Check for partial API key credentials
|
|
59
59
|
if (config.apiKey && !config.userId) {
|
|
60
|
-
throw new
|
|
60
|
+
throw new index_js_1.ValidationError("userId is required when using apiKey authentication", index_js_2.ErrorCodes.VALIDATION_REQUIRED_FIELD, { field: "userId", providedFields: ["apiKey"] });
|
|
61
61
|
}
|
|
62
62
|
if (!config.apiKey && config.userId) {
|
|
63
|
-
throw new
|
|
63
|
+
throw new index_js_1.ValidationError("apiKey is required when using user authentication", index_js_2.ErrorCodes.VALIDATION_REQUIRED_FIELD, { field: "apiKey", providedFields: ["userId"] });
|
|
64
64
|
}
|
|
65
65
|
// Check for partial partner credentials
|
|
66
66
|
if (config.clientId && !config.clientSecret) {
|
|
67
|
-
throw new
|
|
67
|
+
throw new index_js_1.ValidationError("clientSecret is required when using partner authentication", index_js_2.ErrorCodes.VALIDATION_REQUIRED_FIELD, { field: "clientSecret", providedFields: ["clientId"] });
|
|
68
68
|
}
|
|
69
69
|
if (!config.clientId && config.clientSecret) {
|
|
70
|
-
throw new
|
|
70
|
+
throw new index_js_1.ValidationError("clientId is required when using partner authentication", index_js_2.ErrorCodes.VALIDATION_REQUIRED_FIELD, { field: "clientId", providedFields: ["clientSecret"] });
|
|
71
71
|
}
|
|
72
72
|
if (config.apiKey && config.userId) {
|
|
73
|
-
auth = { type:
|
|
73
|
+
auth = { type: "apiKey", apiKey: config.apiKey, userId: config.userId };
|
|
74
74
|
}
|
|
75
75
|
else if (config.clientId && config.clientSecret) {
|
|
76
|
-
auth = {
|
|
76
|
+
auth = {
|
|
77
|
+
type: "partner",
|
|
78
|
+
clientId: config.clientId,
|
|
79
|
+
clientSecret: config.clientSecret,
|
|
80
|
+
};
|
|
77
81
|
}
|
|
78
82
|
else {
|
|
79
|
-
auth = { type:
|
|
83
|
+
auth = { type: "none" };
|
|
80
84
|
}
|
|
81
85
|
return {
|
|
82
|
-
baseUrl: config.baseUrl.replace(/\/$/,
|
|
86
|
+
baseUrl: config.baseUrl.replace(/\/$/, ""),
|
|
83
87
|
auth,
|
|
84
|
-
timeout: config.timeout ??
|
|
85
|
-
maxRetries: config.maxRetries ??
|
|
88
|
+
timeout: config.timeout ?? constants_js_1.DEFAULT_TIMEOUT,
|
|
89
|
+
maxRetries: config.maxRetries ?? constants_js_1.DEFAULT_MAX_RETRIES,
|
|
86
90
|
onRetry: config.onRetry,
|
|
87
91
|
};
|
|
88
92
|
}
|
|
@@ -134,20 +138,20 @@ class HttpClient {
|
|
|
134
138
|
*/
|
|
135
139
|
buildHeaders(customHeaders) {
|
|
136
140
|
const headers = {
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
141
|
+
"Content-Type": "application/json",
|
|
142
|
+
Accept: "application/json",
|
|
143
|
+
"User-Agent": `@attrove/sdk/${constants_js_1.SDK_VERSION}`,
|
|
140
144
|
...customHeaders,
|
|
141
145
|
};
|
|
142
146
|
const { auth } = this.config;
|
|
143
|
-
if (auth.type ===
|
|
144
|
-
headers[
|
|
145
|
-
headers[
|
|
146
|
-
headers[
|
|
147
|
+
if (auth.type === "apiKey") {
|
|
148
|
+
headers["Authorization"] = `Bearer ${auth.apiKey}`;
|
|
149
|
+
headers["X-Auth-Type"] = "api";
|
|
150
|
+
headers["X-User-Id"] = auth.userId;
|
|
147
151
|
}
|
|
148
|
-
else if (auth.type ===
|
|
149
|
-
headers[
|
|
150
|
-
headers[
|
|
152
|
+
else if (auth.type === "partner") {
|
|
153
|
+
headers["Authorization"] = `Bearer ${auth.clientSecret}`;
|
|
154
|
+
headers["X-Auth-Type"] = "partner";
|
|
151
155
|
}
|
|
152
156
|
return headers;
|
|
153
157
|
}
|
|
@@ -157,18 +161,18 @@ class HttpClient {
|
|
|
157
161
|
*/
|
|
158
162
|
parseApiError(body, status) {
|
|
159
163
|
// Handle structured error response
|
|
160
|
-
if (body && typeof body ===
|
|
164
|
+
if (body && typeof body === "object" && "error" in body) {
|
|
161
165
|
const errorResponse = body;
|
|
162
166
|
const error = errorResponse.error;
|
|
163
|
-
if (typeof error ===
|
|
164
|
-
return (0,
|
|
167
|
+
if (typeof error === "string") {
|
|
168
|
+
return (0, index_js_1.createErrorFromStatus)(error, status);
|
|
165
169
|
}
|
|
166
|
-
return (0,
|
|
170
|
+
return (0, index_js_1.createErrorFromStatus)(error.message, status, error.code, error.details);
|
|
167
171
|
}
|
|
168
172
|
// Handle unknown error format - include response preview for debugging
|
|
169
173
|
let message;
|
|
170
|
-
if (typeof body ===
|
|
171
|
-
message = body ||
|
|
174
|
+
if (typeof body === "string") {
|
|
175
|
+
message = body || "Empty error response";
|
|
172
176
|
}
|
|
173
177
|
else {
|
|
174
178
|
try {
|
|
@@ -176,11 +180,13 @@ class HttpClient {
|
|
|
176
180
|
}
|
|
177
181
|
catch (serializeError) {
|
|
178
182
|
// Preserve serialization error context for debugging
|
|
179
|
-
const reason = serializeError instanceof Error
|
|
183
|
+
const reason = serializeError instanceof Error
|
|
184
|
+
? serializeError.message
|
|
185
|
+
: "Unknown reason";
|
|
180
186
|
message = `Unknown error occurred (response not serializable: ${reason})`;
|
|
181
187
|
}
|
|
182
188
|
}
|
|
183
|
-
return (0,
|
|
189
|
+
return (0, index_js_1.createErrorFromStatus)(message, status);
|
|
184
190
|
}
|
|
185
191
|
/**
|
|
186
192
|
* Make a request with automatic retries.
|
|
@@ -200,7 +206,7 @@ class HttpClient {
|
|
|
200
206
|
* @throws {ServerError} When server errors occur (5xx)
|
|
201
207
|
*/
|
|
202
208
|
async request(path, config = {}, queryParams) {
|
|
203
|
-
const method = config.method ??
|
|
209
|
+
const method = config.method ?? "GET";
|
|
204
210
|
const url = this.buildUrl(path, queryParams);
|
|
205
211
|
const headers = this.buildHeaders(config.headers);
|
|
206
212
|
const timeout = config.timeout ?? this.config.timeout;
|
|
@@ -214,22 +220,22 @@ class HttpClient {
|
|
|
214
220
|
headers,
|
|
215
221
|
signal: controller.signal,
|
|
216
222
|
};
|
|
217
|
-
if (config.body && method !==
|
|
223
|
+
if (config.body && method !== "GET") {
|
|
218
224
|
fetchOptions.body = JSON.stringify(config.body);
|
|
219
225
|
}
|
|
220
226
|
const response = await fetch(url, fetchOptions);
|
|
221
227
|
clearTimeout(timeoutId);
|
|
222
228
|
// Parse response body with explicit JSON error handling
|
|
223
229
|
let body;
|
|
224
|
-
const contentType = response.headers.get(
|
|
225
|
-
if (contentType?.includes(
|
|
230
|
+
const contentType = response.headers.get("content-type");
|
|
231
|
+
if (contentType?.includes("application/json")) {
|
|
226
232
|
const text = await response.text();
|
|
227
233
|
try {
|
|
228
234
|
body = JSON.parse(text);
|
|
229
235
|
}
|
|
230
236
|
catch (parseError) {
|
|
231
237
|
// JSON parse errors are not retryable
|
|
232
|
-
throw new
|
|
238
|
+
throw new index_js_1.ServerError(`Server returned invalid JSON: ${parseError instanceof Error ? parseError.message : "Parse failed"}`, response.status, { responsePreview: text.substring(0, 200) });
|
|
233
239
|
}
|
|
234
240
|
}
|
|
235
241
|
else {
|
|
@@ -238,11 +244,11 @@ class HttpClient {
|
|
|
238
244
|
// Handle successful response
|
|
239
245
|
if (response.ok) {
|
|
240
246
|
// Handle wrapped API responses
|
|
241
|
-
if (body && typeof body ===
|
|
247
|
+
if (body && typeof body === "object" && "success" in body) {
|
|
242
248
|
const apiResponse = body;
|
|
243
249
|
if (apiResponse.success) {
|
|
244
250
|
// Paginated responses have a `pagination` field - preserve the full structure
|
|
245
|
-
if (
|
|
251
|
+
if ("pagination" in body) {
|
|
246
252
|
return body;
|
|
247
253
|
}
|
|
248
254
|
return apiResponse.data;
|
|
@@ -255,8 +261,9 @@ class HttpClient {
|
|
|
255
261
|
// Handle error response
|
|
256
262
|
const error = this.parseApiError(body, response.status);
|
|
257
263
|
// Check if we should retry
|
|
258
|
-
if (attempt < this.config.maxRetries &&
|
|
259
|
-
|
|
264
|
+
if (attempt < this.config.maxRetries &&
|
|
265
|
+
constants_js_1.RETRYABLE_STATUS_SET.has(response.status)) {
|
|
266
|
+
const retryAfterSeconds = parseRetryAfter(response.headers.get("retry-after"));
|
|
260
267
|
const delay = calculateRetryDelay(attempt, retryAfterSeconds);
|
|
261
268
|
// Call retry callback if provided
|
|
262
269
|
if (this.config.onRetry) {
|
|
@@ -277,8 +284,8 @@ class HttpClient {
|
|
|
277
284
|
}
|
|
278
285
|
catch (err) {
|
|
279
286
|
// Handle abort/timeout
|
|
280
|
-
if (err instanceof Error && err.name ===
|
|
281
|
-
const timeoutError = new
|
|
287
|
+
if (err instanceof Error && err.name === "AbortError") {
|
|
288
|
+
const timeoutError = new index_js_1.TimeoutError(`Request timed out after ${timeout}ms`);
|
|
282
289
|
if (attempt < this.config.maxRetries) {
|
|
283
290
|
const delay = calculateRetryDelay(attempt);
|
|
284
291
|
if (this.config.onRetry) {
|
|
@@ -298,8 +305,8 @@ class HttpClient {
|
|
|
298
305
|
throw timeoutError;
|
|
299
306
|
}
|
|
300
307
|
// Handle network errors
|
|
301
|
-
if (err instanceof TypeError && err.message.includes(
|
|
302
|
-
const networkError = new
|
|
308
|
+
if (err instanceof TypeError && err.message.includes("fetch")) {
|
|
309
|
+
const networkError = new index_js_1.NetworkError(`Network error: ${err.message}. Check your connection and ensure the API endpoint is accessible.`);
|
|
303
310
|
if (attempt < this.config.maxRetries) {
|
|
304
311
|
const delay = calculateRetryDelay(attempt);
|
|
305
312
|
if (this.config.onRetry) {
|
|
@@ -319,7 +326,7 @@ class HttpClient {
|
|
|
319
326
|
throw networkError;
|
|
320
327
|
}
|
|
321
328
|
// Re-throw AttroveErrors (including ServerError from JSON parse)
|
|
322
|
-
if (err instanceof
|
|
329
|
+
if (err instanceof index_js_1.AttroveError) {
|
|
323
330
|
throw err;
|
|
324
331
|
}
|
|
325
332
|
// Handle unexpected errors - preserve as much context as possible
|
|
@@ -329,10 +336,13 @@ class HttpClient {
|
|
|
329
336
|
originalErrorMessage: originalError.message,
|
|
330
337
|
};
|
|
331
338
|
if (originalError.stack) {
|
|
332
|
-
errorDetails.originalStack = originalError.stack
|
|
339
|
+
errorDetails.originalStack = originalError.stack
|
|
340
|
+
.split("\n")
|
|
341
|
+
.slice(0, 5)
|
|
342
|
+
.join("\n");
|
|
333
343
|
}
|
|
334
344
|
// Wrap in ServerError to indicate this was an unexpected failure
|
|
335
|
-
const wrappedError = new
|
|
345
|
+
const wrappedError = new index_js_1.ServerError(`Unexpected error during request: ${originalError.message}`, undefined, // No status code for unexpected errors
|
|
336
346
|
errorDetails);
|
|
337
347
|
lastError = wrappedError;
|
|
338
348
|
if (attempt < this.config.maxRetries) {
|
|
@@ -354,7 +364,10 @@ class HttpClient {
|
|
|
354
364
|
}
|
|
355
365
|
}
|
|
356
366
|
// Should not reach here, but just in case - use ServerError for consistency
|
|
357
|
-
throw lastError ||
|
|
367
|
+
throw (lastError ||
|
|
368
|
+
new index_js_1.ServerError("Max retries exceeded", undefined, {
|
|
369
|
+
maxRetries: this.config.maxRetries,
|
|
370
|
+
}));
|
|
358
371
|
}
|
|
359
372
|
/**
|
|
360
373
|
* Make a GET request.
|
|
@@ -366,7 +379,7 @@ class HttpClient {
|
|
|
366
379
|
* @throws {NetworkError} When network errors occur
|
|
367
380
|
*/
|
|
368
381
|
get(path, params, config) {
|
|
369
|
-
return this.request(path, { ...config, method:
|
|
382
|
+
return this.request(path, { ...config, method: "GET" }, params);
|
|
370
383
|
}
|
|
371
384
|
/**
|
|
372
385
|
* Make a POST request.
|
|
@@ -377,7 +390,7 @@ class HttpClient {
|
|
|
377
390
|
* @throws {NetworkError} When network errors occur
|
|
378
391
|
*/
|
|
379
392
|
post(path, body, config) {
|
|
380
|
-
return this.request(path, { ...config, method:
|
|
393
|
+
return this.request(path, { ...config, method: "POST", body });
|
|
381
394
|
}
|
|
382
395
|
/**
|
|
383
396
|
* Make a PUT request.
|
|
@@ -388,7 +401,7 @@ class HttpClient {
|
|
|
388
401
|
* @throws {NetworkError} When network errors occur
|
|
389
402
|
*/
|
|
390
403
|
put(path, body, config) {
|
|
391
|
-
return this.request(path, { ...config, method:
|
|
404
|
+
return this.request(path, { ...config, method: "PUT", body });
|
|
392
405
|
}
|
|
393
406
|
/**
|
|
394
407
|
* Make a PATCH request.
|
|
@@ -399,7 +412,7 @@ class HttpClient {
|
|
|
399
412
|
* @throws {NetworkError} When network errors occur
|
|
400
413
|
*/
|
|
401
414
|
patch(path, body, config) {
|
|
402
|
-
return this.request(path, { ...config, method:
|
|
415
|
+
return this.request(path, { ...config, method: "PATCH", body });
|
|
403
416
|
}
|
|
404
417
|
/**
|
|
405
418
|
* Make a DELETE request.
|
|
@@ -410,8 +423,7 @@ class HttpClient {
|
|
|
410
423
|
* @throws {NetworkError} When network errors occur
|
|
411
424
|
*/
|
|
412
425
|
delete(path, config) {
|
|
413
|
-
return this.request(path, { ...config, method:
|
|
426
|
+
return this.request(path, { ...config, method: "DELETE" });
|
|
414
427
|
}
|
|
415
428
|
}
|
|
416
429
|
exports.HttpClient = HttpClient;
|
|
417
|
-
//# sourceMappingURL=fetch.js.map
|
|
@@ -4,9 +4,8 @@
|
|
|
4
4
|
*/
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.generateMessageId = exports.StreamingClient = exports.HttpClient = void 0;
|
|
7
|
-
var
|
|
8
|
-
Object.defineProperty(exports, "HttpClient", { enumerable: true, get: function () { return
|
|
9
|
-
var
|
|
10
|
-
Object.defineProperty(exports, "StreamingClient", { enumerable: true, get: function () { return
|
|
11
|
-
Object.defineProperty(exports, "generateMessageId", { enumerable: true, get: function () { return
|
|
12
|
-
//# sourceMappingURL=index.js.map
|
|
7
|
+
var fetch_js_1 = require("./fetch.js");
|
|
8
|
+
Object.defineProperty(exports, "HttpClient", { enumerable: true, get: function () { return fetch_js_1.HttpClient; } });
|
|
9
|
+
var streaming_js_1 = require("./streaming.js");
|
|
10
|
+
Object.defineProperty(exports, "StreamingClient", { enumerable: true, get: function () { return streaming_js_1.StreamingClient; } });
|
|
11
|
+
Object.defineProperty(exports, "generateMessageId", { enumerable: true, get: function () { return streaming_js_1.generateMessageId; } });
|
|
@@ -13,10 +13,10 @@
|
|
|
13
13
|
*/
|
|
14
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
15
|
exports.generateMessageId = exports.StreamingClient = void 0;
|
|
16
|
-
const
|
|
17
|
-
const
|
|
18
|
-
const
|
|
19
|
-
const
|
|
16
|
+
const index_js_1 = require("../types/index.js");
|
|
17
|
+
const index_js_2 = require("../errors/index.js");
|
|
18
|
+
const index_js_3 = require("../types/index.js");
|
|
19
|
+
const constants_js_1 = require("../constants.js");
|
|
20
20
|
/**
|
|
21
21
|
* Streaming query client for WebSocket-based LLM responses.
|
|
22
22
|
*
|
|
@@ -44,7 +44,7 @@ class StreamingClient {
|
|
|
44
44
|
this.ws = null;
|
|
45
45
|
this.currentMessageId = null;
|
|
46
46
|
// Convert HTTP URL to WebSocket URL
|
|
47
|
-
const wsBaseUrl = baseUrl.replace(/^http/,
|
|
47
|
+
const wsBaseUrl = baseUrl.replace(/^http/, "ws");
|
|
48
48
|
this.wsUrl = `${wsBaseUrl}/ws/llm-stream`;
|
|
49
49
|
this.token = token;
|
|
50
50
|
}
|
|
@@ -54,17 +54,17 @@ class StreamingClient {
|
|
|
54
54
|
* @throws {TimeoutError} If connection times out
|
|
55
55
|
* @throws {NetworkError} If connection fails
|
|
56
56
|
*/
|
|
57
|
-
async connect(timeout =
|
|
57
|
+
async connect(timeout = constants_js_1.DEFAULT_TIMEOUT) {
|
|
58
58
|
return new Promise((resolve, reject) => {
|
|
59
59
|
const timeoutId = setTimeout(() => {
|
|
60
|
-
reject(new
|
|
60
|
+
reject(new index_js_2.TimeoutError(`WebSocket connection timed out after ${timeout}ms`));
|
|
61
61
|
}, timeout);
|
|
62
62
|
try {
|
|
63
63
|
// Note: Token is passed as query parameter because WebSocket API
|
|
64
64
|
// doesn't support custom headers in browsers. This is a common pattern
|
|
65
65
|
// but means the token may appear in server logs.
|
|
66
66
|
const url = new URL(this.wsUrl);
|
|
67
|
-
url.searchParams.set(
|
|
67
|
+
url.searchParams.set("token", this.token);
|
|
68
68
|
const ws = new WebSocket(url.toString());
|
|
69
69
|
ws.onopen = () => {
|
|
70
70
|
clearTimeout(timeoutId);
|
|
@@ -72,19 +72,19 @@ class StreamingClient {
|
|
|
72
72
|
};
|
|
73
73
|
ws.onerror = () => {
|
|
74
74
|
clearTimeout(timeoutId);
|
|
75
|
-
reject(new
|
|
75
|
+
reject(new index_js_2.NetworkError(`WebSocket connection to ${this.wsUrl} failed. Check your network connection and ensure the API endpoint is accessible.`));
|
|
76
76
|
};
|
|
77
77
|
ws.onclose = (event) => {
|
|
78
78
|
if (event.code !== 1000) {
|
|
79
79
|
clearTimeout(timeoutId);
|
|
80
|
-
const reason = (0,
|
|
81
|
-
reject(new
|
|
80
|
+
const reason = (0, constants_js_1.getWsCloseReason)(event.code);
|
|
81
|
+
reject(new index_js_2.NetworkError(`WebSocket closed during connection (code ${event.code}: ${reason}). This may indicate a server issue or network problem.`));
|
|
82
82
|
}
|
|
83
83
|
};
|
|
84
84
|
}
|
|
85
85
|
catch (err) {
|
|
86
86
|
clearTimeout(timeoutId);
|
|
87
|
-
reject(new
|
|
87
|
+
reject(new index_js_2.NetworkError(`Failed to create WebSocket: ${err instanceof Error ? err.message : String(err)}`));
|
|
88
88
|
}
|
|
89
89
|
});
|
|
90
90
|
}
|
|
@@ -103,10 +103,10 @@ class StreamingClient {
|
|
|
103
103
|
* @throws {AttroveError} If the server returns an error
|
|
104
104
|
*/
|
|
105
105
|
async stream(query, messageId, history, queryOptions = {}, streamOptions = {}) {
|
|
106
|
-
const { onChunk, onState, onStart, onError, onEnd, onWarning, signal, timeout =
|
|
106
|
+
const { onChunk, onState, onStart, onError, onEnd, onWarning, signal, timeout = constants_js_1.DEFAULT_TIMEOUT, } = streamOptions;
|
|
107
107
|
// Store warning callback for use in cancel() and cleanup()
|
|
108
108
|
this.onWarning = onWarning;
|
|
109
|
-
let answer =
|
|
109
|
+
let answer = "";
|
|
110
110
|
let usedMessageIds = [];
|
|
111
111
|
let cancelled = false;
|
|
112
112
|
let completed = false;
|
|
@@ -115,7 +115,7 @@ class StreamingClient {
|
|
|
115
115
|
this.currentMessageId = messageId;
|
|
116
116
|
return new Promise((resolve, reject) => {
|
|
117
117
|
if (!this.ws) {
|
|
118
|
-
reject(new
|
|
118
|
+
reject(new index_js_2.NetworkError("WebSocket not connected"));
|
|
119
119
|
return;
|
|
120
120
|
}
|
|
121
121
|
const ws = this.ws;
|
|
@@ -125,7 +125,7 @@ class StreamingClient {
|
|
|
125
125
|
const cleanupAndFinish = () => {
|
|
126
126
|
completed = true;
|
|
127
127
|
if (signal && abortHandler) {
|
|
128
|
-
signal.removeEventListener(
|
|
128
|
+
signal.removeEventListener("abort", abortHandler);
|
|
129
129
|
}
|
|
130
130
|
this.cleanup();
|
|
131
131
|
};
|
|
@@ -137,7 +137,7 @@ class StreamingClient {
|
|
|
137
137
|
cancelled = true;
|
|
138
138
|
}
|
|
139
139
|
};
|
|
140
|
-
signal.addEventListener(
|
|
140
|
+
signal.addEventListener("abort", abortHandler);
|
|
141
141
|
// Check if already aborted
|
|
142
142
|
if (signal.aborted) {
|
|
143
143
|
this.cancel();
|
|
@@ -146,21 +146,22 @@ class StreamingClient {
|
|
|
146
146
|
}
|
|
147
147
|
// Send the query to the server after connection is established
|
|
148
148
|
const queryPayload = {
|
|
149
|
-
type:
|
|
149
|
+
type: "query",
|
|
150
150
|
message_id: messageId,
|
|
151
151
|
query,
|
|
152
|
-
client_timezone: queryOptions.timezone ||
|
|
152
|
+
client_timezone: queryOptions.timezone ||
|
|
153
|
+
Intl.DateTimeFormat().resolvedOptions().timeZone,
|
|
153
154
|
history: history.length > 0 ? history : undefined,
|
|
154
155
|
integration_ids: queryOptions.integrationIds,
|
|
155
156
|
conversation_ids: queryOptions.conversationIds,
|
|
156
157
|
allow_bot_messages: queryOptions.allowBotMessages,
|
|
157
|
-
expand: queryOptions.includeSources ?
|
|
158
|
+
expand: queryOptions.includeSources ? "sources" : undefined,
|
|
158
159
|
};
|
|
159
160
|
try {
|
|
160
161
|
ws.send(JSON.stringify(queryPayload));
|
|
161
162
|
}
|
|
162
163
|
catch (err) {
|
|
163
|
-
const error = new
|
|
164
|
+
const error = new index_js_2.NetworkError(`Failed to send query to server: ${err instanceof Error ? err.message : String(err)}`);
|
|
164
165
|
onError?.(error);
|
|
165
166
|
cleanupAndFinish();
|
|
166
167
|
reject(error);
|
|
@@ -173,15 +174,21 @@ class StreamingClient {
|
|
|
173
174
|
parsed = JSON.parse(event.data);
|
|
174
175
|
}
|
|
175
176
|
catch (parseErr) {
|
|
176
|
-
const error = new
|
|
177
|
+
const error = new index_js_2.AttroveError(`Failed to parse stream message: ${parseErr instanceof Error ? parseErr.message : String(parseErr)}`, index_js_3.ErrorCodes.INTERNAL_ERROR, undefined, {
|
|
178
|
+
category: "parse",
|
|
179
|
+
rawDataPreview: String(event.data).substring(0, 200),
|
|
180
|
+
});
|
|
177
181
|
onError?.(error);
|
|
178
182
|
cleanupAndFinish();
|
|
179
183
|
reject(error);
|
|
180
184
|
return;
|
|
181
185
|
}
|
|
182
186
|
// Validate the frame structure at runtime
|
|
183
|
-
if (!(0,
|
|
184
|
-
const error = new
|
|
187
|
+
if (!(0, index_js_1.isValidStreamFrame)(parsed)) {
|
|
188
|
+
const error = new index_js_2.AttroveError("Received invalid stream frame format from server", index_js_3.ErrorCodes.INTERNAL_ERROR, undefined, {
|
|
189
|
+
category: "parse",
|
|
190
|
+
rawDataPreview: JSON.stringify(parsed).substring(0, 200),
|
|
191
|
+
});
|
|
185
192
|
onError?.(error);
|
|
186
193
|
cleanupAndFinish();
|
|
187
194
|
reject(error);
|
|
@@ -196,7 +203,7 @@ class StreamingClient {
|
|
|
196
203
|
frameType: frame.type,
|
|
197
204
|
};
|
|
198
205
|
if (onWarning) {
|
|
199
|
-
onWarning(
|
|
206
|
+
onWarning("Received message with mismatched message_id, ignoring", warningContext);
|
|
200
207
|
}
|
|
201
208
|
else {
|
|
202
209
|
// prettier-ignore
|
|
@@ -206,28 +213,28 @@ class StreamingClient {
|
|
|
206
213
|
}
|
|
207
214
|
// Process the parsed frame - callback errors will propagate naturally
|
|
208
215
|
switch (frame.type) {
|
|
209
|
-
case
|
|
216
|
+
case "stream_start":
|
|
210
217
|
onStart?.();
|
|
211
218
|
break;
|
|
212
|
-
case
|
|
219
|
+
case "state":
|
|
213
220
|
onState?.(frame.state);
|
|
214
221
|
break;
|
|
215
|
-
case
|
|
222
|
+
case "chunk":
|
|
216
223
|
answer += frame.content;
|
|
217
224
|
onChunk?.(frame.content);
|
|
218
225
|
break;
|
|
219
|
-
case
|
|
226
|
+
case "message_ids":
|
|
220
227
|
usedMessageIds = frame.used_message_ids;
|
|
221
228
|
break;
|
|
222
|
-
case
|
|
223
|
-
const error = new
|
|
229
|
+
case "error": {
|
|
230
|
+
const error = new index_js_2.AttroveError(frame.error, index_js_3.ErrorCodes.INTERNAL_ERROR);
|
|
224
231
|
onError?.(error);
|
|
225
232
|
cleanupAndFinish();
|
|
226
233
|
reject(error);
|
|
227
234
|
break;
|
|
228
235
|
}
|
|
229
|
-
case
|
|
230
|
-
cancelled = frame.reason ===
|
|
236
|
+
case "end": {
|
|
237
|
+
cancelled = frame.reason === "cancelled";
|
|
231
238
|
if (frame.used_message_ids) {
|
|
232
239
|
usedMessageIds = frame.used_message_ids;
|
|
233
240
|
}
|
|
@@ -235,7 +242,7 @@ class StreamingClient {
|
|
|
235
242
|
// Build updated history
|
|
236
243
|
const updatedHistory = [
|
|
237
244
|
...history,
|
|
238
|
-
{ role:
|
|
245
|
+
{ role: "assistant", content: answer },
|
|
239
246
|
];
|
|
240
247
|
cleanupAndFinish();
|
|
241
248
|
resolve({
|
|
@@ -250,7 +257,7 @@ class StreamingClient {
|
|
|
250
257
|
};
|
|
251
258
|
ws.onerror = () => {
|
|
252
259
|
if (!completed) {
|
|
253
|
-
const error = new
|
|
260
|
+
const error = new index_js_2.NetworkError(`WebSocket error during streaming. The connection was interrupted. You may need to retry the query.`);
|
|
254
261
|
onError?.(error);
|
|
255
262
|
cleanupAndFinish();
|
|
256
263
|
reject(error);
|
|
@@ -258,8 +265,8 @@ class StreamingClient {
|
|
|
258
265
|
};
|
|
259
266
|
ws.onclose = (event) => {
|
|
260
267
|
if (!completed && event.code !== 1000) {
|
|
261
|
-
const reason = (0,
|
|
262
|
-
const error = new
|
|
268
|
+
const reason = (0, constants_js_1.getWsCloseReason)(event.code);
|
|
269
|
+
const error = new index_js_2.NetworkError(`WebSocket closed unexpectedly (code ${event.code}: ${reason}). The stream may need to be retried.`);
|
|
263
270
|
onError?.(error);
|
|
264
271
|
cleanupAndFinish();
|
|
265
272
|
reject(error);
|
|
@@ -291,27 +298,33 @@ class StreamingClient {
|
|
|
291
298
|
*/
|
|
292
299
|
cancel() {
|
|
293
300
|
if (!this.ws || !this.currentMessageId) {
|
|
294
|
-
return { success: false, reason:
|
|
301
|
+
return { success: false, reason: "no_active_stream" };
|
|
295
302
|
}
|
|
296
303
|
try {
|
|
297
304
|
this.ws.send(JSON.stringify({
|
|
298
|
-
type:
|
|
305
|
+
type: "stop",
|
|
299
306
|
message_id: this.currentMessageId,
|
|
300
307
|
}));
|
|
301
|
-
return { success: true, reason:
|
|
308
|
+
return { success: true, reason: "sent" };
|
|
302
309
|
}
|
|
303
310
|
catch (err) {
|
|
304
311
|
// Cancellation is best-effort - the WebSocket may already be closed
|
|
305
312
|
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
306
|
-
const context = {
|
|
313
|
+
const context = {
|
|
314
|
+
messageId: this.currentMessageId,
|
|
315
|
+
wsReadyState: this.ws?.readyState,
|
|
316
|
+
};
|
|
307
317
|
if (this.onWarning) {
|
|
308
|
-
this.onWarning(
|
|
318
|
+
this.onWarning("Failed to send cancel message", {
|
|
319
|
+
error: errorMessage,
|
|
320
|
+
...context,
|
|
321
|
+
});
|
|
309
322
|
}
|
|
310
323
|
else {
|
|
311
324
|
// prettier-ignore
|
|
312
325
|
console.warn('[AttroveSDK] Failed to send cancel message:', errorMessage, context);
|
|
313
326
|
}
|
|
314
|
-
return { success: false, reason:
|
|
327
|
+
return { success: false, reason: "send_failed", error: errorMessage };
|
|
315
328
|
}
|
|
316
329
|
}
|
|
317
330
|
/**
|
|
@@ -330,7 +343,9 @@ class StreamingClient {
|
|
|
330
343
|
// Cleanup is best-effort - the WebSocket may already be closed
|
|
331
344
|
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
332
345
|
if (this.onWarning) {
|
|
333
|
-
this.onWarning(
|
|
346
|
+
this.onWarning("Failed to close WebSocket cleanly", {
|
|
347
|
+
error: errorMessage,
|
|
348
|
+
});
|
|
334
349
|
}
|
|
335
350
|
else {
|
|
336
351
|
// prettier-ignore
|
|
@@ -365,4 +380,3 @@ function generateMessageId() {
|
|
|
365
380
|
return `msg_${Date.now()}_${Math.random().toString(36).substring(2, 11)}`;
|
|
366
381
|
}
|
|
367
382
|
exports.generateMessageId = generateMessageId;
|
|
368
|
-
//# sourceMappingURL=streaming.js.map
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
9
|
exports.resetVersionCache = exports.getVersion = void 0;
|
|
10
|
-
const
|
|
10
|
+
const constants_js_1 = require("./constants.js");
|
|
11
11
|
/**
|
|
12
12
|
* Get the SDK version.
|
|
13
13
|
*
|
|
@@ -15,7 +15,7 @@ const constants_1 = require("./constants");
|
|
|
15
15
|
* This value should be kept in sync with package.json during releases.
|
|
16
16
|
*/
|
|
17
17
|
function getVersion() {
|
|
18
|
-
return
|
|
18
|
+
return constants_js_1.SDK_VERSION;
|
|
19
19
|
}
|
|
20
20
|
exports.getVersion = getVersion;
|
|
21
21
|
/**
|
|
@@ -28,4 +28,3 @@ function resetVersionCache() {
|
|
|
28
28
|
// No-op - version is now a static constant
|
|
29
29
|
}
|
|
30
30
|
exports.resetVersionCache = resetVersionCache;
|
|
31
|
-
//# sourceMappingURL=version.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../../../../../packages/sdk/src/__mocks__/version.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,wBAAgB,UAAU,IAAI,MAAM,CAEnC;AAED,wBAAgB,iBAAiB,IAAI,IAAI,CAExC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"version.js","sourceRoot":"","sources":["../../../../../packages/sdk/src/__mocks__/version.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,MAAM,UAAU,UAAU;IACxB,OAAO,YAAY,CAAC;AACtB,CAAC;AAED,MAAM,UAAU,iBAAiB;IAC/B,gBAAgB;AAClB,CAAC"}
|