@gscdump/cloudflare 1.4.0 → 1.4.2

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.
@@ -1,250 +1,5 @@
1
- import { ArchetypeQuery, ArchetypeResult, ArchetypeResultRow } from "@gscdump/contracts/archetypes";
2
- import { Result } from "gscdump/result";
3
- import { ServerTailDirective } from "@gscdump/contracts";
4
- declare const TABLE_PLACEHOLDER = "{{TABLE}}";
5
- type ArchetypeFactTable = 'pages' | 'queries' | 'countries' | 'page_queries' | 'dates';
6
- interface ArchetypeSqlPlan {
7
- sql: string;
8
- params: unknown[];
9
- table: ArchetypeFactTable;
10
- }
11
- type PartitionPredicateMode = 'bare' | 'r2-sql-concat';
12
- type PartitionKeyEncoding = 'int' | 'string';
13
- interface BuildArchetypeSqlOptions {
14
- /**
15
- * Set by the DuckDB file-list executor, which reads raw Iceberg parquet
16
- * directly via `read_parquet([...])`, bypassing the catalog metadata layer
17
- * that synthesizes the identity-partition columns. `site_id` / `search_type`
18
- * are partition identities NOT materialized in the data files (see engine
19
- * `iceberg/schema.ts`: "carried implicitly in the object-key prefix"), so a
20
- * `WHERE site_id = ?` predicate fails with `Referenced column "site_id" not
21
- * found in FROM clause`. Those files are already pruned to (site_id,
22
- * search_type) at resolution time, so the predicate is redundant — emit only
23
- * the row-level `date` range (a real stored column; `month(date)` is the
24
- * partition transform, not `date` itself).
25
- *
26
- * The R2 SQL catalog path (default `false`) exposes partition columns as
27
- * virtual columns and needs the full predicate.
28
- */
29
- partitionPruned?: boolean;
30
- /**
31
- * Int-partition catalogs are the default and use bare equality. Legacy
32
- * string-partition catalogs can request CONCAT-wrapped predicates directly
33
- * instead of post-processing generated SQL.
34
- */
35
- partitionPredicateMode?: PartitionPredicateMode;
36
- /** Preferred input for new callers. Defaults to `'int'`. */
37
- partitionKeyEncoding?: PartitionKeyEncoding;
38
- }
39
- declare function buildArchetypeSql(query: ArchetypeQuery, opts?: BuildArchetypeSqlOptions): ArchetypeSqlPlan;
40
- /** Row returned by the DuckDB sibling. */
41
- type DuckDbIcebergRow = Record<string, string | number | null>;
42
- /**
43
- * The minimal `DUCKDB_SVC` shape this executor needs — a structural subset of
44
- * the binding in `workers-duckdb.ts` / `env.ts`. Any binding with `runSQL`
45
- * satisfies it.
46
- */
47
- interface DuckDbSvc {
48
- runSQL: (args: {
49
- sql: string;
50
- deadlineAt?: number;
51
- }) => Promise<{
52
- rows: unknown[];
53
- sql: string;
54
- }>;
55
- }
56
- /** Configuration for the DuckDB-over-Iceberg executor. */
57
- interface DuckDbIcebergExecutorConfig {
58
- /** The DuckDB service binding (the sibling Worker RPC). */
59
- svc: DuckDbSvc;
60
- /**
61
- * R2 Data Catalog warehouse identifier. The sibling resolves Iceberg table
62
- * locations from `<warehouse>` + `<namespace>` + table name.
63
- */
64
- warehouse: string;
65
- /** Iceberg namespace the 5 fact tables live in. */
66
- namespace: string;
67
- /**
68
- * How the sibling addresses an Iceberg table in a `FROM` clause. Defaults to
69
- * DuckDB's `iceberg_scan('<warehouse>/<namespace>/<table>')`. Overridable so
70
- * a sibling configured with the Iceberg REST catalog can use
71
- * `iceberg_scan('<namespace>.<table>')` or an attached-catalog reference.
72
- */
73
- tableRefStyle?: 'path' | 'catalog';
74
- /** Per-query wall-clock deadline (ms). Default 25s. */
75
- timeoutMs?: number;
76
- }
77
- /** Result of a DuckDB-over-Iceberg query. */
78
- interface DuckDbIcebergResult {
79
- rows: DuckDbIcebergRow[];
80
- /** The exact SQL sent to the sibling. */
81
- sql: string;
82
- queryMs: number;
83
- }
84
- declare class DuckDbIcebergError extends Error {
85
- name: string;
86
- }
87
- declare class DuckDbIcebergTimeoutError extends Error {
88
- name: string;
89
- constructor(timeoutMs: number);
90
- }
91
- /**
92
- * The modelled, caller-actionable failure channel for a DuckDB-over-Iceberg
93
- * query. As with the R2 SQL client, callers branch on which class came back: a
94
- * `DuckDbIcebergTimeoutError` is the retry-able deadline overrun, a
95
- * `DuckDbIcebergError` is a hard sibling-RPC failure (or the `aux-cloud-only`
96
- * routing reject). The error variant IS the existing throwable class, so the
97
- * throwing wrappers preserve the identity/message tests assert
98
- * (`rejects.toThrow(/OOM in sibling/)`, `rejects.toThrow(DuckDbIcebergError)`).
99
- */
100
- type DuckDbIcebergQueryError = DuckDbIcebergError | DuckDbIcebergTimeoutError;
101
- /** A configured DuckDB-over-Iceberg executor. */
102
- interface DuckDbIcebergExecutor {
103
- /** Run a raw SQL string with `{{TABLE_<name>}}` placeholders resolved. */
104
- runSql: (sql: string, params?: readonly unknown[]) => Promise<DuckDbIcebergResult>;
105
- /** Run a dialect-neutral plan: resolve `{{TABLE}}`, bind params, send. */
106
- runPlan: (plan: ArchetypeSqlPlan) => Promise<DuckDbIcebergResult>;
107
- /** Translate + run an archetype query. Handles `arbitrary-sql` verbatim. */
108
- runArchetype: (query: ArchetypeQuery) => Promise<DuckDbIcebergResult>;
109
- /**
110
- * Errors-as-values core for {@link DuckDbIcebergExecutor.runArchetype}:
111
- * returns the modelled timeout-vs-hard-fail `DuckDbIcebergQueryError` instead
112
- * of throwing, so the dispatcher can branch on retry-ability (a timeout may be
113
- * worth a fallback) without `instanceof` over a `catch`. Optional so a
114
- * hand-rolled executor (e.g. a host app's own service-binding executor) can
115
- * implement only the throwing surface; {@link createDuckDbIcebergExecutor}
116
- * always provides it.
117
- */
118
- runArchetypeResult?: (query: ArchetypeQuery) => Promise<Result<DuckDbIcebergResult, DuckDbIcebergQueryError>>;
119
- }
120
- /**
121
- * Create a DuckDB-over-Iceberg-files executor.
122
- */
123
- declare function createDuckDbIcebergExecutor(config: DuckDbIcebergExecutorConfig): DuckDbIcebergExecutor;
124
- /** Configuration for an R2 SQL client. */
125
- interface R2SqlClientConfig {
126
- /** Cloudflare account id. */
127
- accountId: string;
128
- /** R2 bucket backing the Iceberg catalog — R2 SQL addresses the catalog by bucket. */
129
- bucket: string;
130
- /** Iceberg namespace the 5 fact tables live in. */
131
- namespace: string;
132
- /** Cloudflare API token with R2 Data Catalog read scope. */
133
- token: string;
134
- /**
135
- * Override the HTTP endpoint base. Defaults to the public CF API. Tests
136
- * point this at a local recorder.
137
- */
138
- apiBase?: string;
139
- /**
140
- * Injectable fetch. Defaults to global `fetch`. Tests pass a fake that
141
- * returns a recorded CF envelope without a network round-trip.
142
- */
143
- fetchImpl?: typeof fetch;
144
- /** Per-query wall-clock deadline (ms). Default 25s — under the Worker CPU budget. */
145
- timeoutMs?: number;
146
- /** Partition-key encoding of the target catalog. Defaults to `'int'`. */
147
- partitionKeyEncoding?: PartitionKeyEncoding;
148
- /**
149
- * Host-owned mapping from public site id to the INT `site_id` partition
150
- * value. Required for non-numeric public ids when `partitionKeyEncoding` is
151
- * `'int'`.
152
- */
153
- partitionSiteId?: (siteId: string) => string | number;
154
- }
155
- /** A row as returned by R2 SQL — flat dimension + metric values. */
156
- type R2SqlRow = Record<string, string | number | null>;
157
- /** Result of an R2 SQL query. */
158
- interface R2SqlResult {
159
- rows: R2SqlRow[];
160
- /** The exact SQL sent (params already inlined). For diagnostics. */
161
- sql: string;
162
- /** Wall-clock duration of the HTTP round-trip. */
163
- queryMs: number;
164
- }
165
- declare class R2SqlError extends Error {
166
- readonly status?: number | undefined;
167
- name: string;
168
- constructor(message: string, status?: number | undefined);
169
- }
170
- declare class R2SqlTimeoutError extends Error {
171
- name: string;
172
- constructor(timeoutMs: number);
173
- }
174
- /**
175
- * The modelled, caller-actionable failure channel for an R2 SQL query. Callers
176
- * branch on which class came back — a `R2SqlTimeoutError` is a transient retry
177
- * candidate (the query outran the per-query deadline), while a `R2SqlError`
178
- * (HTTP 4xx/5xx, a rejected envelope, a transport blow-up) is a hard failure.
179
- * The error variant IS the existing throwable class, so the throwing wrapper
180
- * preserves the exact identity/message tests assert (`rejects.toThrow(/HTTP 403/)`,
181
- * `rejects.toBeInstanceOf(R2SqlTimeoutError)`). Defects — a programmer handing
182
- * the client malformed params (`escapeSqlValue` / `inlineParams`) — are NOT
183
- * modelled here; they keep throwing `R2SqlError` synchronously.
184
- */
185
- type R2SqlQueryError = R2SqlError | R2SqlTimeoutError;
186
- /** A configured R2 SQL client. */
187
- interface R2SqlClient {
188
- /** Run a raw SQL string (table reference already resolved). */
189
- query: (sql: string) => Promise<R2SqlResult>;
190
- /**
191
- * Errors-as-values core for {@link R2SqlClient.query}: returns the modelled
192
- * timeout-vs-hard-fail `R2SqlQueryError` instead of throwing, so callers can
193
- * branch on retry-ability without `instanceof` over a `catch`. Optional so a
194
- * hand-rolled `R2SqlClient` (e.g. a host app's own endpoint-backed client) can
195
- * implement only the throwing surface; {@link createR2SqlClient} always
196
- * provides it.
197
- */
198
- queryResult?: (sql: string) => Promise<Result<R2SqlResult, R2SqlQueryError>>;
199
- /** Run a dialect-neutral plan: resolve `{{TABLE}}`, inline params, send. */
200
- runPlan: (plan: ArchetypeSqlPlan) => Promise<R2SqlResult>;
201
- /** Translate + run an archetype query end to end. */
202
- runArchetype: (query: ArchetypeQuery) => Promise<R2SqlResult>;
203
- }
204
- /**
205
- * Create an R2 SQL client. The endpoint requires a real CF token in
206
- * production; tests inject `fetchImpl` returning a recorded envelope.
207
- */
208
- declare function createR2SqlClient(config: R2SqlClientConfig): R2SqlClient;
209
- /** The two engines the server tail can route to. */
210
- type ServerTailEngine = 'r2-sql' | 'duckdb';
211
- /** Executors the dispatcher routes between. */
212
- interface ServerTailDispatcherConfig {
213
- r2Sql: R2SqlClient;
214
- duckdb: DuckDbIcebergExecutor;
215
- }
216
- declare class ServerTailRoutingError extends Error {
217
- name: string;
218
- }
219
- /**
220
- * Errors-as-values core for {@link resolveServerTailEngine}: returns a
221
- * `ServerTailRoutingError` instead of throwing when an archetype is `cloud-only`
222
- * (the one caller-actionable routing failure — the consumer must route that
223
- * query through the cloud endpoints, not the server tail). Pure — no I/O.
224
- */
225
- declare function resolveServerTailEngineResult(query: ArchetypeQuery): Result<ServerTailEngine, ServerTailRoutingError>;
226
- /**
227
- * Decide which engine answers an archetype query. Pure — no I/O. Exposed so
228
- * the file-resolution endpoint can compute the `ServerTailDirective.engine`
229
- * with the SAME logic the dispatcher uses at execution time. Throws
230
- * `ServerTailRoutingError` for a `cloud-only` archetype; see
231
- * {@link resolveServerTailEngineResult} for the errors-as-values core.
232
- */
233
- declare function resolveServerTailEngine(query: ArchetypeQuery): ServerTailEngine;
234
- /** A configured server-tail dispatcher. */
235
- interface ServerTailDispatcher {
236
- /** Decide the engine for a query without running it. */
237
- route: (query: ArchetypeQuery) => ServerTailEngine;
238
- /**
239
- * Execute a query, routing by execution class. If `directive` is supplied
240
- * its `engine` is honoured only when consistent with the archetype's class
241
- * (a `duckdb`-class archetype always runs on DuckDB regardless).
242
- */
243
- execute: <R extends ArchetypeResultRow = ArchetypeResultRow>(query: ArchetypeQuery, directive?: ServerTailDirective) => Promise<ArchetypeResult<R>>;
244
- }
245
- /**
246
- * Create the server-tail dispatcher. Holds an R2 SQL client and a DuckDB
247
- * executor and routes every `ArchetypeQuery` to one of them.
248
- */
249
- declare function createServerTailDispatcher(config: ServerTailDispatcherConfig): ServerTailDispatcher;
1
+ import { ArchetypeSqlPlan, BuildArchetypeSqlOptions, TABLE_PLACEHOLDER, buildArchetypeSql } from "./archetype-sql.mjs";
2
+ import { DuckDbIcebergError, DuckDbIcebergExecutor, DuckDbIcebergExecutorConfig, DuckDbIcebergQueryError, DuckDbIcebergResult, DuckDbIcebergRow, DuckDbIcebergTimeoutError, DuckDbSvc, createDuckDbIcebergExecutor } from "./duckdb-iceberg-executor.mjs";
3
+ import { R2SqlClient, R2SqlClientConfig, R2SqlError, R2SqlQueryError, R2SqlResult, R2SqlRow, R2SqlTimeoutError, createR2SqlClient } from "./r2-sql-client.mjs";
4
+ import { ServerTailDispatcher, ServerTailDispatcherConfig, ServerTailEngine, ServerTailRoutingError, createServerTailDispatcher, resolveServerTailEngine, resolveServerTailEngineResult } from "./dispatcher.mjs";
250
5
  export { type ArchetypeSqlPlan, type BuildArchetypeSqlOptions, DuckDbIcebergError, type DuckDbIcebergExecutor, type DuckDbIcebergExecutorConfig, type DuckDbIcebergQueryError, type DuckDbIcebergResult, type DuckDbIcebergRow, DuckDbIcebergTimeoutError, type DuckDbSvc, type R2SqlClient, type R2SqlClientConfig, R2SqlError, type R2SqlQueryError, type R2SqlResult, type R2SqlRow, R2SqlTimeoutError, type ServerTailDispatcher, type ServerTailDispatcherConfig, type ServerTailEngine, ServerTailRoutingError, TABLE_PLACEHOLDER, buildArchetypeSql, createDuckDbIcebergExecutor, createR2SqlClient, createServerTailDispatcher, resolveServerTailEngine, resolveServerTailEngineResult };