ultrastiching-cli 0.0.0-stage → 0.2.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/LICENSE +186 -0
- package/README.md +75 -2
- package/bin/meridian.js +10 -0
- package/package.json +27 -4
- package/registry/endpoints.json +1 -0
- package/registry/marketdb-datasets.json +124 -0
- package/src/args.js +31 -0
- package/src/auth.js +62 -0
- package/src/config.js +33 -0
- package/src/credentials.js +117 -0
- package/src/db-cli.js +50 -0
- package/src/environment.js +127 -0
- package/src/http.js +129 -0
- package/src/main.js +201 -0
- package/src/marketdb.js +482 -0
- package/src/prompt.js +42 -0
- package/src/registry.js +91 -0
- package/src/websocket.js +72 -0
package/src/websocket.js
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { endpoint, buildPayload } from './registry.js';
|
|
2
|
+
import { normalizeOrigin, resolveKey } from './config.js';
|
|
3
|
+
import { redactKey } from './http.js';
|
|
4
|
+
|
|
5
|
+
export class MeridianWebSocketError extends Error {
|
|
6
|
+
constructor(message, { status, requestId, body, key, cause } = {}) {
|
|
7
|
+
super(message, { cause });
|
|
8
|
+
this.name = 'MeridianWebSocketError';
|
|
9
|
+
this.status = status;
|
|
10
|
+
this.requestId = requestId;
|
|
11
|
+
this.body = body;
|
|
12
|
+
this.key = key;
|
|
13
|
+
}
|
|
14
|
+
toDiagnostic() {
|
|
15
|
+
return {
|
|
16
|
+
type: this.name, message: this.message, status: this.status,
|
|
17
|
+
request_id: redactKey(this.requestId, this.key), raw_body: redactKey(this.body, this.key),
|
|
18
|
+
cause: this.cause ? redactKey(String(this.cause), this.key) : undefined,
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export async function streamNewsflash(values, {
|
|
24
|
+
key, websocketBaseUrl, count, idleTimeout,
|
|
25
|
+
onMessage = (line) => process.stdout.write(`${line}\n`),
|
|
26
|
+
socketFactory,
|
|
27
|
+
} = {}) {
|
|
28
|
+
const meta = endpoint('ws_newsflash', 'websocket');
|
|
29
|
+
if (!meta) throw new Error('ws_newsflash is absent from the registry');
|
|
30
|
+
const subscription = buildPayload(meta, values);
|
|
31
|
+
const apiKey = resolveKey(key);
|
|
32
|
+
const origin = websocketBaseUrl === undefined ? meta.origin : normalizeOrigin(websocketBaseUrl, ['ws:', 'wss:']);
|
|
33
|
+
const max = count === undefined ? Infinity : Number(count);
|
|
34
|
+
if (!(max > 0 && (max === Infinity || Number.isInteger(max)))) throw new Error('count must be a positive integer');
|
|
35
|
+
const idleMs = idleTimeout === undefined ? undefined : Number(idleTimeout);
|
|
36
|
+
if (idleMs !== undefined && (!Number.isFinite(idleMs) || idleMs <= 0)) throw new Error('idle-timeout must be positive milliseconds');
|
|
37
|
+
const factory = socketFactory ?? ((await import('ws')).default);
|
|
38
|
+
const socket = new factory(`${origin}${meta.path}`, { headers: { Authorization: `Bearer ${apiKey}` } });
|
|
39
|
+
return new Promise((resolve, reject) => {
|
|
40
|
+
let received = 0;
|
|
41
|
+
let timer;
|
|
42
|
+
let settled = false;
|
|
43
|
+
const finish = (error) => {
|
|
44
|
+
if (settled) return;
|
|
45
|
+
settled = true;
|
|
46
|
+
if (timer) clearTimeout(timer);
|
|
47
|
+
if (error) reject(error); else resolve({ received });
|
|
48
|
+
};
|
|
49
|
+
const resetTimer = () => {
|
|
50
|
+
if (timer) clearTimeout(timer);
|
|
51
|
+
if (idleMs) timer = setTimeout(() => socket.close(), idleMs);
|
|
52
|
+
};
|
|
53
|
+
socket.on('open', () => { socket.send(JSON.stringify(subscription)); resetTimer(); });
|
|
54
|
+
socket.on('message', (data) => {
|
|
55
|
+
resetTimer();
|
|
56
|
+
received++;
|
|
57
|
+
try { onMessage(String(data)); }
|
|
58
|
+
catch (cause) { finish(new MeridianWebSocketError('Could not handle WebSocket message', { cause, key: apiKey })); socket.close(); return; }
|
|
59
|
+
if (received >= max) socket.close();
|
|
60
|
+
});
|
|
61
|
+
socket.on('unexpected-response', (_request, response) => {
|
|
62
|
+
const chunks = [];
|
|
63
|
+
response.on('data', (chunk) => chunks.push(chunk));
|
|
64
|
+
response.on('end', () => finish(new MeridianWebSocketError('WebSocket handshake failed', {
|
|
65
|
+
status: response.statusCode, requestId: response.headers['x-meridian-request-id'],
|
|
66
|
+
body: Buffer.concat(chunks).toString('utf8'), key: apiKey,
|
|
67
|
+
})));
|
|
68
|
+
});
|
|
69
|
+
socket.on('error', (cause) => finish(new MeridianWebSocketError('WebSocket transport failed', { cause, key: apiKey })));
|
|
70
|
+
socket.on('close', () => finish());
|
|
71
|
+
});
|
|
72
|
+
}
|