lopata 0.19.2 → 0.20.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.
@@ -0,0 +1,107 @@
1
+ /**
2
+ * Local emulation of the Cloudflare Workers Analytics Engine **SQL API**.
3
+ *
4
+ * Cloudflare has no Worker binding for *reading* Analytics Engine data — you
5
+ * POST a ClickHouse-flavoured SQL string to
6
+ * https://api.cloudflare.com/client/v4/accounts/<acc>/analytics_engine/sql
7
+ * with `Authorization: Bearer <token>`. Lopata intercepts that fetch (see
8
+ * `plugin.ts`) and serves it from the local `analytics_engine` SQLite table so
9
+ * the *same* worker code runs locally and in production unchanged.
10
+ *
11
+ * Interception is gated on the token: a missing `Authorization` header or the
12
+ * sentinel `Bearer local` is served locally; any real bearer token falls
13
+ * through to the actual Cloudflare API. Drive the token from env (e.g. `local`
14
+ * in `.dev.vars`, the real secret in prod) to keep one code path. Note that
15
+ * `writeDataPoint` always writes locally — only the query fetch is gated.
16
+ *
17
+ * SPIKE SCOPE — this is a pragmatic subset, not a full ClickHouse engine:
18
+ * SELECT <items> FROM <dataset>
19
+ * [WHERE <cond>] [GROUP BY <exprs>] [ORDER BY <exprs>] [LIMIT n] [FORMAT f]
20
+ * with the functions/operators people actually use for dashboards (counts,
21
+ * sums, time filters, time bucketing). Unsupported constructs throw a clear
22
+ * error instead of silently returning wrong numbers.
23
+ *
24
+ * Key local simplifications (correct *because* there's no sampling locally):
25
+ * - `_sample_interval` is always 1, so `sum(_sample_interval)` == `count()`.
26
+ * - The `timestamp` column is stored as **ms epoch**; ClickHouse treats it as
27
+ * a `DateTime` (seconds). We canonicalise to **seconds** on read so
28
+ * `now()` / `INTERVAL` arithmetic lines up, then render `DateTime` *output*
29
+ * columns as ClickHouse `YYYY-MM-DD HH:MM:SS` (UTC) strings to match the real
30
+ * SQL API — so `new Date(row.bucket)` behaves the same locally as in prod.
31
+ */
32
+ import type { Database } from 'bun:sqlite';
33
+ /** True for the Cloudflare Analytics Engine SQL API endpoint. */
34
+ export declare function isAnalyticsEngineSqlUrl(url: string): boolean;
35
+ /**
36
+ * Whether an AE SQL API request should be served from the local store rather
37
+ * than forwarded to Cloudflare. A missing `Authorization` header or the
38
+ * `Bearer local` sentinel means local; any real bearer token passes through.
39
+ */
40
+ export declare function isLocalAnalyticsEngineToken(auth: string | null | undefined): boolean;
41
+ /** A quantile that's collected per-group via `group_concat` and computed in JS. */
42
+ interface QuantileSpec {
43
+ /** Internal SQLite column holding the comma-joined values for the group. */
44
+ tempCol: string;
45
+ /** Output column name. */
46
+ outputName: string;
47
+ /** Quantile level in [0, 1]. */
48
+ level: number;
49
+ /**
50
+ * `quantile`/`quantileWeighted` interpolate linearly (ClickHouse default);
51
+ * the `*Exact*` variants return an actual element (nearest-rank).
52
+ */
53
+ interpolate: boolean;
54
+ }
55
+ /**
56
+ * Post-processing applied in JS when the query uses quantile functions.
57
+ * `bun:sqlite` has no UDFs and no percentile aggregate, so we collect the
58
+ * per-group values with `group_concat` and finish the computation here. ORDER BY
59
+ * / LIMIT then also have to run in JS (they may reference a computed quantile).
60
+ */
61
+ interface PostProcess {
62
+ quantiles: QuantileSpec[];
63
+ orderBy: {
64
+ name: string;
65
+ desc: boolean;
66
+ }[];
67
+ limit?: number;
68
+ }
69
+ export interface TranslatedQuery {
70
+ sqlite: string;
71
+ columns: {
72
+ name: string;
73
+ type: string;
74
+ }[];
75
+ format: string;
76
+ /** `SELECT *` — column metadata is derived from the result rows at run time. */
77
+ hasStar?: boolean;
78
+ /** Present only when the query uses quantile functions. */
79
+ postProcess?: PostProcess;
80
+ /** The query uses LIKE — run it with `PRAGMA case_sensitive_like=ON` (ClickHouse semantics). */
81
+ caseSensitiveLike?: boolean;
82
+ }
83
+ /** Translate a Cloudflare Analytics Engine SQL string into a SQLite query. */
84
+ export declare function translateAnalyticsEngineSql(sql: string, nowSeconds: number): TranslatedQuery;
85
+ export interface AnalyticsEngineSqlResult {
86
+ meta: {
87
+ name: string;
88
+ type: string;
89
+ }[];
90
+ data: Record<string, unknown>[];
91
+ rows: number;
92
+ rows_before_limit_at_least: number;
93
+ }
94
+ /** Translate + execute a Cloudflare Analytics Engine SQL query against SQLite. */
95
+ export declare function runAnalyticsEngineSql(db: Database, sql: string, nowMs?: number): AnalyticsEngineSqlResult;
96
+ /**
97
+ * Run an Analytics Engine SQL string and build a Cloudflare-shaped `Response`
98
+ * (JSON by default, NDJSON for `FORMAT JSONEachRow`, or a 400 with the error
99
+ * message). Synchronous so callers can wrap it in their own tracing span.
100
+ */
101
+ export declare function buildAnalyticsEngineSqlResponse(db: Database, sql: string): Response;
102
+ /**
103
+ * Handle an intercepted POST to the Analytics Engine SQL API. Returns a
104
+ * Cloudflare-shaped JSON `Response` (or a 400 with the error message).
105
+ */
106
+ export declare function handleAnalyticsEngineSqlRequest(db: Database, request: Request): Promise<Response>;
107
+ export {};