@jammysunshine/astrology-api-client 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/API_CLIENT_DESIGN.md +1313 -0
- package/package.json +16 -0
- package/src/AstrologyApiClient.js +254 -0
- package/src/config.js +27 -0
- package/src/index.js +7 -0
- package/src/utils/ApiClientError.js +73 -0
- package/src/utils/CircuitBreaker.js +48 -0
- package/src/utils/ContextStore.js +107 -0
- package/src/utils/DiagnosticsTracker.js +52 -0
- package/src/utils/Interceptors.js +50 -0
- package/src/utils/NetworkMonitor.js +41 -0
- package/src/utils/RequestBatcher.js +28 -0
- package/src/utils/ResponseDetective.js +24 -0
- package/src/utils/formatter.js +43 -0
- package/src/utils/retry.js +34 -0
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
const { v4: uuid } = require('uuid');
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Request Batcher for aggregating independent calls (SDK Sec 9.B)
|
|
5
|
+
*/
|
|
6
|
+
class RequestBatcher {
|
|
7
|
+
constructor(apiClient) {
|
|
8
|
+
this.apiClient = apiClient;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
async batch(calls) {
|
|
12
|
+
const payload = {
|
|
13
|
+
requestId: uuid(),
|
|
14
|
+
timestamp: new Date().toISOString(),
|
|
15
|
+
isBatch: true,
|
|
16
|
+
requests: calls.map(c => ({
|
|
17
|
+
service: c.service,
|
|
18
|
+
method: c.method,
|
|
19
|
+
params: c.params
|
|
20
|
+
}))
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const result = await this.apiClient._executeRequest('gateway', 'batch', payload);
|
|
24
|
+
return result.results;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
module.exports = RequestBatcher;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Detects response types: JSON, Text (AI), or Binary (Charts/PDFs)
|
|
3
|
+
*/
|
|
4
|
+
class ResponseDetective {
|
|
5
|
+
static detect(response) {
|
|
6
|
+
const contentType = response.headers['content-type'] || '';
|
|
7
|
+
|
|
8
|
+
if (contentType.includes('application/json')) {
|
|
9
|
+
return 'JSON';
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
if (contentType.includes('image/') || contentType.includes('application/pdf')) {
|
|
13
|
+
return 'BINARY';
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
if (contentType.includes('text/plain')) {
|
|
17
|
+
return 'TEXT';
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return 'UNKNOWN';
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
module.exports = ResponseDetective;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared SDK Formatter for consistent cross-platform data structures (SDK Sec 11.C)
|
|
3
|
+
*/
|
|
4
|
+
class SDKFormatter {
|
|
5
|
+
/**
|
|
6
|
+
* Structures chat responses consistently across WhatsApp and Mobile UIs
|
|
7
|
+
*/
|
|
8
|
+
static formatChatResponse(data) {
|
|
9
|
+
return {
|
|
10
|
+
text: data.summary || data.text || data.interpretation || '',
|
|
11
|
+
buttons: (data.suggestions || []).map(s => ({
|
|
12
|
+
id: s.id || s.action,
|
|
13
|
+
title: s.label || s.title || s.text
|
|
14
|
+
})).slice(0, 3), // Standard Meta Button Limit
|
|
15
|
+
list: (data.options || []).map(o => ({
|
|
16
|
+
id: o.id || o.value,
|
|
17
|
+
title: o.label || o.title,
|
|
18
|
+
description: o.description || o.desc
|
|
19
|
+
})).slice(0, 10), // Standard Meta List Limit
|
|
20
|
+
metadata: data.metadata || {}
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
static formatMessage(data) {
|
|
25
|
+
// Shared structure for chat-like responses
|
|
26
|
+
return {
|
|
27
|
+
text: data.summary || data.text || '',
|
|
28
|
+
metadata: data.metadata || {},
|
|
29
|
+
actions: data.suggestions || []
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
static wrapBinary(buffer, type) {
|
|
34
|
+
return {
|
|
35
|
+
type: 'BINARY',
|
|
36
|
+
buffer,
|
|
37
|
+
contentType: type,
|
|
38
|
+
length: buffer.byteLength
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
module.exports = SDKFormatter;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Exponential Backoff Retry Utility
|
|
3
|
+
*/
|
|
4
|
+
async function retry(fn, options = {}) {
|
|
5
|
+
const {
|
|
6
|
+
maxAttempts = 3,
|
|
7
|
+
initialDelay = 500,
|
|
8
|
+
maxDelay = 2000,
|
|
9
|
+
shouldRetry = (err) => !err.code || err.code === 'TIMEOUT' || err.code === 'ECONNRESET'
|
|
10
|
+
} = options;
|
|
11
|
+
|
|
12
|
+
let attempt = 1;
|
|
13
|
+
let delay = initialDelay;
|
|
14
|
+
|
|
15
|
+
while (attempt <= maxAttempts) {
|
|
16
|
+
try {
|
|
17
|
+
return await fn();
|
|
18
|
+
} catch (error) {
|
|
19
|
+
if (attempt >= maxAttempts || !shouldRetry(error)) {
|
|
20
|
+
throw error;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
console.warn(`Attempt ${attempt} failed. Retrying in ${delay}ms...`);
|
|
24
|
+
await new Promise(resolve => setTimeout(resolve, delay));
|
|
25
|
+
|
|
26
|
+
attempt++;
|
|
27
|
+
// SDK Sec 7.B: Exponential backoff (500ms -> 1s -> 2s)
|
|
28
|
+
delay = Math.pow(2, attempt - 1) * initialDelay;
|
|
29
|
+
delay = Math.min(delay, maxDelay);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
module.exports = retry;
|