@vaultgradient/pipequery-cli 0.10.3 → 0.10.5
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/dist/engine.d.ts +188 -0
- package/dist/engine.js +2098 -0
- package/dist/sources/manager.d.ts +296 -0
- package/dist/sources/manager.js +3854 -0
- package/package.json +12 -1
- package/tsconfig.json +3 -1
- package/tsup.config.ts +33 -10
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
type SourceConfig = RestSourceConfig | WebSocketSourceConfig | FileSourceConfig | StaticSourceConfig | PostgresSourceConfig | MysqlSourceConfig | SqliteSourceConfig | KafkaSourceConfig | SnowflakeSourceConfig | ClickhouseSourceConfig | MongoSourceConfig;
|
|
2
|
+
interface RestSourceConfig {
|
|
3
|
+
type: 'rest';
|
|
4
|
+
url: string;
|
|
5
|
+
params?: Record<string, string>;
|
|
6
|
+
headers?: Record<string, string>;
|
|
7
|
+
dataPath?: string;
|
|
8
|
+
interval?: string;
|
|
9
|
+
/** Optional auth helper. Currently supports `bearer`; more kinds (basic,
|
|
10
|
+
* apiKeyHeader, hmac-*) tracked in issue #37. The token is interpolated
|
|
11
|
+
* through ${ENV_VAR} like every other string field. */
|
|
12
|
+
auth?: BearerAuth;
|
|
13
|
+
}
|
|
14
|
+
interface BearerAuth {
|
|
15
|
+
kind: 'bearer';
|
|
16
|
+
token: string;
|
|
17
|
+
}
|
|
18
|
+
interface WebSocketSourceConfig {
|
|
19
|
+
type: 'websocket';
|
|
20
|
+
url: string;
|
|
21
|
+
maxBuffer?: number;
|
|
22
|
+
/** Payload(s) to send to the server immediately after the WebSocket
|
|
23
|
+
* opens. Most exchange feeds (Binance, Coinbase, Kraken, OKX, Bybit, …)
|
|
24
|
+
* require this — without it, no data is delivered. Each payload is
|
|
25
|
+
* JSON-stringified and sent in order. Re-sent on every reconnect. */
|
|
26
|
+
subscribe?: unknown | unknown[];
|
|
27
|
+
/** Optional keepalive: sends `payload` to the server every `interval`
|
|
28
|
+
* while the socket is open. Most exchanges close idle sockets within
|
|
29
|
+
* a few minutes; a periodic ping keeps the stream alive. */
|
|
30
|
+
heartbeat?: {
|
|
31
|
+
payload: unknown;
|
|
32
|
+
/** Duration like "30s", "1m". */
|
|
33
|
+
interval: string;
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
interface FileSourceConfig {
|
|
37
|
+
type: 'file';
|
|
38
|
+
path: string;
|
|
39
|
+
format?: 'json' | 'csv';
|
|
40
|
+
watch?: boolean;
|
|
41
|
+
}
|
|
42
|
+
interface StaticSourceConfig {
|
|
43
|
+
type: 'static';
|
|
44
|
+
data: unknown[];
|
|
45
|
+
}
|
|
46
|
+
interface PostgresSourceConfig {
|
|
47
|
+
type: 'postgres';
|
|
48
|
+
/**
|
|
49
|
+
* Postgres connection URL. Supports `${ENV_VAR}` interpolation so
|
|
50
|
+
* credentials can live in the environment instead of the yaml.
|
|
51
|
+
* Example: `postgres://${DB_USER}:${DB_PASS}@db.internal:5432/app`.
|
|
52
|
+
*/
|
|
53
|
+
url: string;
|
|
54
|
+
/**
|
|
55
|
+
* The SELECT query to run on each poll. Must be a single statement.
|
|
56
|
+
* It's the caller's responsibility to add LIMIT/WHERE clauses that
|
|
57
|
+
* bound the result set; `maxRows` below is a hard safety cap.
|
|
58
|
+
*/
|
|
59
|
+
query: string;
|
|
60
|
+
/** Poll interval, e.g. "30s", "5m". Defaults to "30s". */
|
|
61
|
+
interval?: string;
|
|
62
|
+
/**
|
|
63
|
+
* SSL policy. `"require"` verifies the cert (production default for
|
|
64
|
+
* most hosted Postgres). `"no-verify"` skips verification (useful for
|
|
65
|
+
* self-signed dev setups). `false` disables TLS entirely.
|
|
66
|
+
*/
|
|
67
|
+
ssl?: 'require' | 'no-verify' | false;
|
|
68
|
+
/** Safety cap. If the query returns more rows, the fetch errors. Default 10000. */
|
|
69
|
+
maxRows?: number;
|
|
70
|
+
}
|
|
71
|
+
interface MysqlSourceConfig {
|
|
72
|
+
type: 'mysql';
|
|
73
|
+
/**
|
|
74
|
+
* MySQL connection URL. Supports `${ENV_VAR}` interpolation so
|
|
75
|
+
* credentials can live in the environment instead of the yaml.
|
|
76
|
+
* Example: `mysql://${DB_USER}:${DB_PASS}@db.internal:3306/app`.
|
|
77
|
+
*/
|
|
78
|
+
url: string;
|
|
79
|
+
/** The SELECT query to run on each poll. */
|
|
80
|
+
query: string;
|
|
81
|
+
/** Poll interval, e.g. "30s", "5m". Defaults to "30s". */
|
|
82
|
+
interval?: string;
|
|
83
|
+
/**
|
|
84
|
+
* SSL policy. `"require"` verifies the cert (production default).
|
|
85
|
+
* `"no-verify"` skips verification. `false` disables TLS.
|
|
86
|
+
*/
|
|
87
|
+
ssl?: 'require' | 'no-verify' | false;
|
|
88
|
+
/** Safety cap. Default 10000. */
|
|
89
|
+
maxRows?: number;
|
|
90
|
+
}
|
|
91
|
+
interface KafkaSourceConfig {
|
|
92
|
+
type: 'kafka';
|
|
93
|
+
/**
|
|
94
|
+
* Comma-separated list of Kafka bootstrap brokers, or an array. Supports
|
|
95
|
+
* `${ENV_VAR}` interpolation. Example: `"broker1:9092,broker2:9092"`.
|
|
96
|
+
*/
|
|
97
|
+
brokers: string | string[];
|
|
98
|
+
/** Topic to subscribe to. Only one topic per source; use multiple sources for multiple topics. */
|
|
99
|
+
topic: string;
|
|
100
|
+
/**
|
|
101
|
+
* Consumer group ID. If omitted, an ephemeral per-process group is generated
|
|
102
|
+
* so this pipequery instance sees the full firehose. Set explicitly to share
|
|
103
|
+
* a partitioned subscription across pipequery replicas.
|
|
104
|
+
*/
|
|
105
|
+
groupId?: string;
|
|
106
|
+
/** Start reading from the topic's beginning on first connect. Default false (latest). */
|
|
107
|
+
fromBeginning?: boolean;
|
|
108
|
+
/** Ring buffer size. Oldest messages are evicted. Default 1000. */
|
|
109
|
+
maxBuffer?: number;
|
|
110
|
+
/**
|
|
111
|
+
* How to decode the message value.
|
|
112
|
+
* - `"json"` (default): `JSON.parse(buffer.toString())`; non-JSON messages are skipped.
|
|
113
|
+
* - `"string"`: UTF-8 string, stored as `{ value }`.
|
|
114
|
+
* - `"raw"`: base64-encoded bytes, stored as `{ value }`.
|
|
115
|
+
*/
|
|
116
|
+
valueFormat?: 'json' | 'string' | 'raw';
|
|
117
|
+
/** SSL / TLS toggle. Default false (plaintext). */
|
|
118
|
+
ssl?: boolean;
|
|
119
|
+
/**
|
|
120
|
+
* SASL authentication. Supports `${ENV_VAR}` interpolation in username/password.
|
|
121
|
+
*/
|
|
122
|
+
sasl?: {
|
|
123
|
+
mechanism: 'plain' | 'scram-sha-256' | 'scram-sha-512';
|
|
124
|
+
username: string;
|
|
125
|
+
password: string;
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
interface SqliteSourceConfig {
|
|
129
|
+
type: 'sqlite';
|
|
130
|
+
/**
|
|
131
|
+
* Path to the SQLite database file, relative to the directory
|
|
132
|
+
* containing pipequery.yaml. Use `:memory:` for an in-memory database
|
|
133
|
+
* (useful for tests; data will not persist across restarts).
|
|
134
|
+
*/
|
|
135
|
+
path: string;
|
|
136
|
+
/** The SELECT query to run on each poll. */
|
|
137
|
+
query: string;
|
|
138
|
+
/** Poll interval, e.g. "30s", "5m". Defaults to "30s". */
|
|
139
|
+
interval?: string;
|
|
140
|
+
/** Open the database read-only. Recommended for query-only sources. Default true. */
|
|
141
|
+
readonly?: boolean;
|
|
142
|
+
/** Safety cap. Default 10000. */
|
|
143
|
+
maxRows?: number;
|
|
144
|
+
}
|
|
145
|
+
interface SnowflakeSourceConfig {
|
|
146
|
+
type: 'snowflake';
|
|
147
|
+
/** Snowflake account locator, e.g. `myorg-myaccount`. Supports `${ENV_VAR}`. */
|
|
148
|
+
account: string;
|
|
149
|
+
/** Username. Supports `${ENV_VAR}`. */
|
|
150
|
+
username: string;
|
|
151
|
+
/** Password. Supports `${ENV_VAR}`. (Key-pair / OAuth out of scope for v1.) */
|
|
152
|
+
password: string;
|
|
153
|
+
/** Database to USE. Supports `${ENV_VAR}`. */
|
|
154
|
+
database?: string;
|
|
155
|
+
/** Schema to USE. Supports `${ENV_VAR}`. */
|
|
156
|
+
schema?: string;
|
|
157
|
+
/** Compute warehouse to USE. Supports `${ENV_VAR}`. */
|
|
158
|
+
warehouse?: string;
|
|
159
|
+
/** Optional role. Supports `${ENV_VAR}`. */
|
|
160
|
+
role?: string;
|
|
161
|
+
/** SELECT query polled on every interval. */
|
|
162
|
+
query: string;
|
|
163
|
+
/** Poll interval, e.g. "60s". Default: 60s. */
|
|
164
|
+
interval?: string;
|
|
165
|
+
/** Hard cap on rows per fetch (default 10000). */
|
|
166
|
+
maxRows?: number;
|
|
167
|
+
}
|
|
168
|
+
interface ClickhouseSourceConfig {
|
|
169
|
+
type: 'clickhouse';
|
|
170
|
+
/** ClickHouse HTTP/HTTPS endpoint, e.g. `https://host:8443`. Supports `${ENV_VAR}`. */
|
|
171
|
+
url: string;
|
|
172
|
+
/** Username. Supports `${ENV_VAR}`. */
|
|
173
|
+
username?: string;
|
|
174
|
+
/** Password. Supports `${ENV_VAR}`. */
|
|
175
|
+
password?: string;
|
|
176
|
+
/** Database to query. Defaults to `default`. */
|
|
177
|
+
database?: string;
|
|
178
|
+
/** SELECT query polled on every interval. */
|
|
179
|
+
query: string;
|
|
180
|
+
/** Poll interval, e.g. "30s". Default: 30s. */
|
|
181
|
+
interval?: string;
|
|
182
|
+
/** Hard cap on rows per fetch (default 10000). */
|
|
183
|
+
maxRows?: number;
|
|
184
|
+
}
|
|
185
|
+
interface MongoSourceConfig {
|
|
186
|
+
type: 'mongodb';
|
|
187
|
+
/** MongoDB connection URI. Supports `${ENV_VAR}`. */
|
|
188
|
+
url: string;
|
|
189
|
+
/** Database name. Supports `${ENV_VAR}`. */
|
|
190
|
+
database: string;
|
|
191
|
+
/** Collection to read from. */
|
|
192
|
+
collection: string;
|
|
193
|
+
/**
|
|
194
|
+
* Optional default filter applied on every poll (Mongo `$match` stage).
|
|
195
|
+
* Plain JSON object — `{ active: true }` etc. The filter is `AND`-merged
|
|
196
|
+
* with any push-down `where()` predicates the engine compiles in.
|
|
197
|
+
*/
|
|
198
|
+
filter?: Record<string, unknown>;
|
|
199
|
+
/** Poll interval, e.g. "30s". Default: 30s. */
|
|
200
|
+
interval?: string;
|
|
201
|
+
/** Hard cap on documents per fetch (default 10000). */
|
|
202
|
+
maxRows?: number;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
interface SourceStatus {
|
|
206
|
+
healthy: boolean;
|
|
207
|
+
rowCount: number;
|
|
208
|
+
lastFetch: Date | null;
|
|
209
|
+
error?: string;
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Result of an adapter-native push-down attempt.
|
|
213
|
+
*
|
|
214
|
+
* `ok: true` means the adapter executed the pipe expression natively (e.g.
|
|
215
|
+
* Postgres compiled it to SQL and ran it on the database) and returned rows
|
|
216
|
+
* without materializing the source query in memory. `sql` / `params` are
|
|
217
|
+
* optional telemetry — useful for audit logs and debugging, not required
|
|
218
|
+
* for callers to act on.
|
|
219
|
+
*
|
|
220
|
+
* `ok: false` is the explicit decline channel: the adapter recognised the
|
|
221
|
+
* shape but declined (unsupported op, multi-source pipeline, etc.). The
|
|
222
|
+
* caller should fall back to in-process execution against `getContext()`.
|
|
223
|
+
*
|
|
224
|
+
* Any thrown error means *neither* path succeeded — let it propagate; the
|
|
225
|
+
* caller has no useful fallback.
|
|
226
|
+
*/
|
|
227
|
+
type PushdownResult = {
|
|
228
|
+
ok: true;
|
|
229
|
+
rows: unknown[];
|
|
230
|
+
sql?: string;
|
|
231
|
+
params?: unknown[];
|
|
232
|
+
} | {
|
|
233
|
+
ok: false;
|
|
234
|
+
reason: string;
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
type DataContext = Record<string, unknown[]>;
|
|
238
|
+
type UpdateListener = (sourceName: string, data: unknown[]) => void;
|
|
239
|
+
declare class SourceManager {
|
|
240
|
+
private cwd;
|
|
241
|
+
private sources;
|
|
242
|
+
private globalListeners;
|
|
243
|
+
constructor(cwd: string);
|
|
244
|
+
addSource(name: string, config: SourceConfig): Promise<void>;
|
|
245
|
+
removeSource(name: string): boolean;
|
|
246
|
+
getContext(): DataContext;
|
|
247
|
+
getSourceData(name: string): unknown[] | undefined;
|
|
248
|
+
getSourceStatus(name: string): SourceStatus | undefined;
|
|
249
|
+
getAllStatuses(): Record<string, SourceStatus>;
|
|
250
|
+
getSourceNames(): string[];
|
|
251
|
+
/**
|
|
252
|
+
* Subscribe to data updates from any source.
|
|
253
|
+
* Used by SSE endpoint for real-time push to TUI clients.
|
|
254
|
+
* Returns unsubscribe function.
|
|
255
|
+
*/
|
|
256
|
+
onSourceUpdate(callback: UpdateListener): () => void;
|
|
257
|
+
refreshAll(): Promise<void>;
|
|
258
|
+
/**
|
|
259
|
+
* Run a pipe expression, preferring adapter-native push-down when the
|
|
260
|
+
* source supports it.
|
|
261
|
+
*
|
|
262
|
+
* Decision tree:
|
|
263
|
+
* 1. Parse the expression. If it can't parse, fall straight through to
|
|
264
|
+
* the in-process engine, which will throw the same parse error a
|
|
265
|
+
* caller would have seen before push-down existed (no behaviour
|
|
266
|
+
* change for malformed input).
|
|
267
|
+
* 2. Look up the source named in the pipeline. If it exists and its
|
|
268
|
+
* adapter exposes `runPushdown`, try it. On `{ ok: true }` we're
|
|
269
|
+
* done — return the rows. On `{ ok: false }` (decline) or any
|
|
270
|
+
* thrown error, fall back to in-process.
|
|
271
|
+
* 3. In-process: query(getContext(), expression) — the existing path.
|
|
272
|
+
*
|
|
273
|
+
* The push-down attempt is best-effort and *invisible to callers*: same
|
|
274
|
+
* return type as `query()`, no new failure modes. Telemetry (which
|
|
275
|
+
* branch ran, latency, source-engine SQL) belongs in the audit log when
|
|
276
|
+
* Phase 5 lands; not threaded through here.
|
|
277
|
+
*/
|
|
278
|
+
runQuery(expression: string): Promise<unknown>;
|
|
279
|
+
/**
|
|
280
|
+
* Inspect the pipeline AST and dispatch to an adapter's runPushdown when
|
|
281
|
+
* eligible. Returns `null` when push-down isn't even attempted (no parse,
|
|
282
|
+
* no source, no capability). Returns a PushdownResult when it was tried —
|
|
283
|
+
* `{ ok: true }` for success, `{ ok: false, reason }` for decline.
|
|
284
|
+
*
|
|
285
|
+
* Exposed on the manager (rather than tucked inside runQuery) so the
|
|
286
|
+
* future `event.audit` channel can record `pushed_down: bool` and the
|
|
287
|
+
* `source_engine_query` SQL string for governance. Today only Postgres
|
|
288
|
+
* implements runPushdown; MySQL / SQLite / ClickHouse will follow the
|
|
289
|
+
* same interface.
|
|
290
|
+
*/
|
|
291
|
+
tryPushdown(expression: string): Promise<PushdownResult | null>;
|
|
292
|
+
dispose(): Promise<void>;
|
|
293
|
+
private createAdapter;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
export { type DataContext, SourceManager };
|